Ahem. sftp.obj depends on sftp.c not on psftp.c!
[u/mdw/putty] / psftp.c
CommitLineData
4c7f0d61 1/*
2 * psftp.c: front end for PSFTP.
3 */
4
4a8fc3c4 5#include <windows.h>
6
4c7f0d61 7#include <stdio.h>
8#include <stdlib.h>
f9e162aa 9#include <stdarg.h>
4c7f0d61 10#include <assert.h>
4c7f0d61 11
4a8fc3c4 12#define PUTTY_DO_GLOBALS
13#include "putty.h"
14#include "storage.h"
15#include "ssh.h"
4c7f0d61 16#include "sftp.h"
17#include "int64.h"
18
4c7f0d61 19/* ----------------------------------------------------------------------
20 * String handling routines.
21 */
22
23char *dupstr(char *s) {
24 int len = strlen(s);
25 char *p = smalloc(len+1);
26 strcpy(p, s);
27 return p;
28}
29
f9e162aa 30/* Allocate the concatenation of N strings. Terminate arg list with NULL. */
31char *dupcat(char *s1, ...) {
32 int len;
33 char *p, *q, *sn;
34 va_list ap;
35
36 len = strlen(s1);
37 va_start(ap, s1);
38 while (1) {
39 sn = va_arg(ap, char *);
40 if (!sn)
41 break;
42 len += strlen(sn);
43 }
44 va_end(ap);
45
46 p = smalloc(len+1);
47 strcpy(p, s1);
48 q = p + strlen(p);
49
50 va_start(ap, s1);
51 while (1) {
52 sn = va_arg(ap, char *);
53 if (!sn)
54 break;
55 strcpy(q, sn);
56 q += strlen(q);
57 }
58 va_end(ap);
59
60 return p;
61}
62
4c7f0d61 63/* ----------------------------------------------------------------------
64 * sftp client state.
65 */
66
67char *pwd, *homedir;
68
69/* ----------------------------------------------------------------------
70 * Higher-level helper functions used in commands.
71 */
72
73/*
f9e162aa 74 * Attempt to canonify a pathname starting from the pwd. If
75 * canonification fails, at least fall back to returning a _valid_
76 * pathname (though it may be ugly, eg /home/simon/../foobar).
4c7f0d61 77 */
78char *canonify(char *name) {
f9e162aa 79 char *fullname, *canonname;
4a8fc3c4 80
f9e162aa 81 if (name[0] == '/') {
82 fullname = dupstr(name);
83 } else {
4a8fc3c4 84 char *slash;
85 if (pwd[strlen(pwd)-1] == '/')
86 slash = "";
87 else
88 slash = "/";
89 fullname = dupcat(pwd, slash, name, NULL);
f9e162aa 90 }
4a8fc3c4 91
92 canonname = fxp_realpath(fullname);
93
f9e162aa 94 if (canonname) {
95 sfree(fullname);
96 return canonname;
97 } else
98 return fullname;
4c7f0d61 99}
100
101/* ----------------------------------------------------------------------
102 * Actual sftp commands.
103 */
104struct sftp_command {
105 char **words;
106 int nwords, wordssize;
107 int (*obey)(struct sftp_command *);/* returns <0 to quit */
108};
109
110int sftp_cmd_null(struct sftp_command *cmd) {
111 return 0;
112}
113
114int sftp_cmd_unknown(struct sftp_command *cmd) {
115 printf("psftp: unknown command \"%s\"\n", cmd->words[0]);
116 return 0;
117}
118
119int sftp_cmd_quit(struct sftp_command *cmd) {
120 return -1;
121}
122
123/*
124 * List a directory. If no arguments are given, list pwd; otherwise
125 * list the directory given in words[1].
126 */
127static int sftp_ls_compare(const void *av, const void *bv) {
128 const struct fxp_name *a = (const struct fxp_name *)av;
129 const struct fxp_name *b = (const struct fxp_name *)bv;
130 return strcmp(a->filename, b->filename);
131}
132int sftp_cmd_ls(struct sftp_command *cmd) {
133 struct fxp_handle *dirh;
134 struct fxp_names *names;
135 struct fxp_name *ournames;
136 int nnames, namesize;
137 char *dir, *cdir;
138 int i;
139
140 if (cmd->nwords < 2)
141 dir = ".";
142 else
143 dir = cmd->words[1];
144
145 cdir = canonify(dir);
146 if (!cdir) {
147 printf("%s: %s\n", dir, fxp_error());
148 return 0;
149 }
150
151 printf("Listing directory %s\n", cdir);
152
153 dirh = fxp_opendir(cdir);
154 if (dirh == NULL) {
155 printf("Unable to open %s: %s\n", dir, fxp_error());
156 } else {
157 nnames = namesize = 0;
158 ournames = NULL;
159
160 while (1) {
161
162 names = fxp_readdir(dirh);
163 if (names == NULL) {
164 if (fxp_error_type() == SSH_FX_EOF)
165 break;
166 printf("Reading directory %s: %s\n", dir, fxp_error());
167 break;
168 }
169 if (names->nnames == 0) {
170 fxp_free_names(names);
171 break;
172 }
173
174 if (nnames + names->nnames >= namesize) {
175 namesize += names->nnames + 128;
176 ournames = srealloc(ournames, namesize * sizeof(*ournames));
177 }
178
179 for (i = 0; i < names->nnames; i++)
180 ournames[nnames++] = names->names[i];
181
182 names->nnames = 0; /* prevent free_names */
183 fxp_free_names(names);
184 }
185 fxp_close(dirh);
186
187 /*
188 * Now we have our filenames. Sort them by actual file
189 * name, and then output the longname parts.
190 */
191 qsort(ournames, nnames, sizeof(*ournames), sftp_ls_compare);
192
193 /*
194 * And print them.
195 */
196 for (i = 0; i < nnames; i++)
197 printf("%s\n", ournames[i].longname);
198 }
199
200 sfree(cdir);
201
202 return 0;
203}
204
205/*
206 * Change directories. We do this by canonifying the new name, then
207 * trying to OPENDIR it. Only if that succeeds do we set the new pwd.
208 */
209int sftp_cmd_cd(struct sftp_command *cmd) {
210 struct fxp_handle *dirh;
211 char *dir;
212
213 if (cmd->nwords < 2)
f9e162aa 214 dir = dupstr(homedir);
4c7f0d61 215 else
216 dir = canonify(cmd->words[1]);
217
218 if (!dir) {
219 printf("%s: %s\n", dir, fxp_error());
220 return 0;
221 }
222
223 dirh = fxp_opendir(dir);
224 if (!dirh) {
225 printf("Directory %s: %s\n", dir, fxp_error());
226 sfree(dir);
227 return 0;
228 }
229
230 fxp_close(dirh);
231
232 sfree(pwd);
233 pwd = dir;
234 printf("Remote directory is now %s\n", pwd);
235
236 return 0;
237}
238
239/*
240 * Get a file and save it at the local end.
241 */
242int sftp_cmd_get(struct sftp_command *cmd) {
243 struct fxp_handle *fh;
244 char *fname, *outfname;
245 uint64 offset;
246 FILE *fp;
247
248 if (cmd->nwords < 2) {
249 printf("get: expects a filename\n");
250 return 0;
251 }
252
253 fname = canonify(cmd->words[1]);
254 if (!fname) {
255 printf("%s: %s\n", cmd->words[1], fxp_error());
256 return 0;
257 }
258 outfname = (cmd->nwords == 2 ? cmd->words[1] : cmd->words[2]);
259
260 fh = fxp_open(fname, SSH_FXF_READ);
261 if (!fh) {
262 printf("%s: %s\n", fname, fxp_error());
263 sfree(fname);
264 return 0;
265 }
266 fp = fopen(outfname, "wb");
267 if (!fp) {
268 printf("local: unable to open %s\n", outfname);
269 fxp_close(fh);
270 sfree(fname);
271 return 0;
272 }
273
274 printf("remote:%s => local:%s\n", fname, outfname);
275
276 offset = uint64_make(0,0);
277
278 /*
279 * FIXME: we can use FXP_FSTAT here to get the file size, and
280 * thus put up a progress bar.
281 */
282 while (1) {
283 char buffer[4096];
284 int len;
285 int wpos, wlen;
286
287 len = fxp_read(fh, buffer, offset, sizeof(buffer));
288 if ((len == -1 && fxp_error_type() == SSH_FX_EOF) ||
289 len == 0)
290 break;
291 if (len == -1) {
292 printf("error while reading: %s\n", fxp_error());
293 break;
294 }
295
296 wpos = 0;
297 while (wpos < len) {
298 wlen = fwrite(buffer, 1, len-wpos, fp);
299 if (wlen <= 0) {
300 printf("error while writing local file\n");
301 break;
302 }
303 wpos += wlen;
304 }
305 if (wpos < len) /* we had an error */
306 break;
307 offset = uint64_add32(offset, len);
308 }
309
310 fclose(fp);
311 fxp_close(fh);
312 sfree(fname);
313
314 return 0;
315}
316
317/*
318 * Send a file and store it at the remote end.
319 */
320int sftp_cmd_put(struct sftp_command *cmd) {
321 struct fxp_handle *fh;
322 char *fname, *origoutfname, *outfname;
323 uint64 offset;
324 FILE *fp;
325
326 if (cmd->nwords < 2) {
327 printf("put: expects a filename\n");
328 return 0;
329 }
330
331 fname = cmd->words[1];
332 origoutfname = (cmd->nwords == 2 ? cmd->words[1] : cmd->words[2]);
333 outfname = canonify(origoutfname);
f9e162aa 334 if (!outfname) {
4c7f0d61 335 printf("%s: %s\n", origoutfname, fxp_error());
336 return 0;
337 }
338
339 fp = fopen(fname, "rb");
340 if (!fp) {
341 printf("local: unable to open %s\n", fname);
342 fxp_close(fh);
343 sfree(outfname);
344 return 0;
345 }
346 fh = fxp_open(outfname, SSH_FXF_WRITE | SSH_FXF_CREAT | SSH_FXF_TRUNC);
347 if (!fh) {
348 printf("%s: %s\n", outfname, fxp_error());
349 sfree(outfname);
350 return 0;
351 }
352
353 printf("local:%s => remote:%s\n", fname, outfname);
354
355 offset = uint64_make(0,0);
356
357 /*
358 * FIXME: we can use FXP_FSTAT here to get the file size, and
359 * thus put up a progress bar.
360 */
361 while (1) {
362 char buffer[4096];
363 int len;
364
f9e162aa 365 len = fread(buffer, 1, sizeof(buffer), fp);
4c7f0d61 366 if (len == -1) {
367 printf("error while reading local file\n");
368 break;
369 } else if (len == 0) {
370 break;
371 }
f9e162aa 372 if (!fxp_write(fh, buffer, offset, len)) {
4c7f0d61 373 printf("error while writing: %s\n", fxp_error());
374 break;
375 }
376 offset = uint64_add32(offset, len);
377 }
378
379 fxp_close(fh);
380 fclose(fp);
381 sfree(outfname);
382
383 return 0;
384}
385
386static struct sftp_cmd_lookup {
387 char *name;
388 int (*obey)(struct sftp_command *);
389} sftp_lookup[] = {
390 /*
391 * List of sftp commands. This is binary-searched so it MUST be
392 * in ASCII order.
393 */
394 {"bye", sftp_cmd_quit},
395 {"cd", sftp_cmd_cd},
4a8fc3c4 396 {"dir", sftp_cmd_ls},
4c7f0d61 397 {"exit", sftp_cmd_quit},
398 {"get", sftp_cmd_get},
399 {"ls", sftp_cmd_ls},
400 {"put", sftp_cmd_put},
401 {"quit", sftp_cmd_quit},
402};
403
404/* ----------------------------------------------------------------------
405 * Command line reading and parsing.
406 */
407struct sftp_command *sftp_getcmd(void) {
408 char *line;
409 int linelen, linesize;
410 struct sftp_command *cmd;
411 char *p, *q, *r;
412 int quoting;
413
414 printf("psftp> ");
415 fflush(stdout);
416
417 cmd = smalloc(sizeof(struct sftp_command));
418 cmd->words = NULL;
419 cmd->nwords = 0;
420 cmd->wordssize = 0;
421
422 line = NULL;
423 linesize = linelen = 0;
424 while (1) {
425 int len;
426 char *ret;
427
428 linesize += 512;
429 line = srealloc(line, linesize);
430 ret = fgets(line+linelen, linesize-linelen, stdin);
431
432 if (!ret || (linelen == 0 && line[0] == '\0')) {
433 cmd->obey = sftp_cmd_quit;
434 printf("quit\n");
435 return cmd; /* eof */
436 }
437 len = linelen + strlen(line+linelen);
438 linelen += len;
439 if (line[linelen-1] == '\n') {
440 linelen--;
441 line[linelen] = '\0';
442 break;
443 }
444 }
445
446 /*
447 * Parse the command line into words. The syntax is:
448 * - double quotes are removed, but cause spaces within to be
449 * treated as non-separating.
450 * - a double-doublequote pair is a literal double quote, inside
451 * _or_ outside quotes. Like this:
452 *
453 * firstword "second word" "this has ""quotes"" in" sodoes""this""
454 *
455 * becomes
456 *
457 * >firstword<
458 * >second word<
459 * >this has "quotes" in<
460 * >sodoes"this"<
461 */
462 p = line;
463 while (*p) {
464 /* skip whitespace */
465 while (*p && (*p == ' ' || *p == '\t')) p++;
466 /* mark start of word */
467 q = r = p; /* q sits at start, r writes word */
468 quoting = 0;
469 while (*p) {
470 if (!quoting && (*p == ' ' || *p == '\t'))
471 break; /* reached end of word */
472 else if (*p == '"' && p[1] == '"')
473 p+=2, *r++ = '"'; /* a literal quote */
474 else if (*p == '"')
475 p++, quoting = !quoting;
476 else
477 *r++ = *p++;
478 }
479 if (*p) p++; /* skip over the whitespace */
480 *r = '\0';
481 if (cmd->nwords >= cmd->wordssize) {
482 cmd->wordssize = cmd->nwords + 16;
483 cmd->words = srealloc(cmd->words, cmd->wordssize*sizeof(char *));
484 }
485 cmd->words[cmd->nwords++] = q;
486 }
487
488 /*
489 * Now parse the first word and assign a function.
490 */
491
492 if (cmd->nwords == 0)
493 cmd->obey = sftp_cmd_null;
494 else {
495 int i, j, k, cmp;
496
497 cmd->obey = sftp_cmd_unknown;
498
499 i = -1;
500 j = sizeof(sftp_lookup) / sizeof(*sftp_lookup);
501 while (j - i > 1) {
502 k = (j + i) / 2;
503 cmp = strcmp(cmd->words[0], sftp_lookup[k].name);
504 if (cmp < 0)
505 j = k;
506 else if (cmp > 0)
507 i = k;
508 else {
509 cmd->obey = sftp_lookup[k].obey;
510 break;
511 }
512 }
513 }
514
515 return cmd;
516}
517
518void do_sftp(void) {
519 /*
520 * Do protocol initialisation.
521 */
522 if (!fxp_init()) {
523 fprintf(stderr,
524 "Fatal: unable to initialise SFTP: %s\n",
525 fxp_error());
526 }
527
528 /*
529 * Find out where our home directory is.
530 */
f9e162aa 531 homedir = fxp_realpath(".");
4c7f0d61 532 if (!homedir) {
533 fprintf(stderr,
534 "Warning: failed to resolve home directory: %s\n",
535 fxp_error());
536 homedir = dupstr(".");
537 } else {
538 printf("Remote working directory is %s\n", homedir);
539 }
540 pwd = dupstr(homedir);
541
542 /* ------------------------------------------------------------------
543 * Now we're ready to do Real Stuff.
544 */
545 while (1) {
546 struct sftp_command *cmd;
547 cmd = sftp_getcmd();
548 if (!cmd)
549 break;
550 if (cmd->obey(cmd) < 0)
551 break;
552 }
4a8fc3c4 553}
4c7f0d61 554
4a8fc3c4 555/* ----------------------------------------------------------------------
556 * Dirty bits: integration with PuTTY.
557 */
558
559static int verbose = 0;
560
561void verify_ssh_host_key(char *host, int port, char *keytype,
562 char *keystr, char *fingerprint) {
563 int ret;
564
565 static const char absentmsg[] =
566 "The server's host key is not cached in the registry. You\n"
567 "have no guarantee that the server is the computer you\n"
568 "think it is.\n"
569 "The server's key fingerprint is:\n"
570 "%s\n"
571 "If you trust this host, enter \"y\" to add the key to\n"
572 "PuTTY's cache and carry on connecting.\n"
573 "If you do not trust this host, enter \"n\" to abandon the\n"
574 "connection.\n"
575 "Continue connecting? (y/n) ";
576
577 static const char wrongmsg[] =
578 "WARNING - POTENTIAL SECURITY BREACH!\n"
579 "The server's host key does not match the one PuTTY has\n"
580 "cached in the registry. This means that either the\n"
581 "server administrator has changed the host key, or you\n"
582 "have actually connected to another computer pretending\n"
583 "to be the server.\n"
584 "The new key fingerprint is:\n"
585 "%s\n"
586 "If you were expecting this change and trust the new key,\n"
587 "enter Yes to update PuTTY's cache and continue connecting.\n"
588 "If you want to carry on connecting but without updating\n"
589 "the cache, enter No.\n"
590 "If you want to abandon the connection completely, press\n"
591 "Return to cancel. Pressing Return is the ONLY guaranteed\n"
592 "safe choice.\n"
593 "Update cached key? (y/n, Return cancels connection) ";
594
595 static const char abandoned[] = "Connection abandoned.\n";
596
597 char line[32];
598
599 /*
600 * Verify the key against the registry.
601 */
602 ret = verify_host_key(host, port, keytype, keystr);
603
604 if (ret == 0) /* success - key matched OK */
605 return;
606 if (ret == 2) { /* key was different */
607 fprintf(stderr, wrongmsg, fingerprint);
608 if (fgets(line, sizeof(line), stdin) &&
609 line[0] != '\0' && line[0] != '\n') {
610 if (line[0] == 'y' || line[0] == 'Y')
611 store_host_key(host, port, keytype, keystr);
612 } else {
613 fprintf(stderr, abandoned);
614 exit(0);
615 }
616 }
617 if (ret == 1) { /* key was absent */
618 fprintf(stderr, absentmsg, fingerprint);
619 if (fgets(line, sizeof(line), stdin) &&
620 (line[0] == 'y' || line[0] == 'Y'))
621 store_host_key(host, port, keytype, keystr);
622 else {
623 fprintf(stderr, abandoned);
624 exit(0);
625 }
626 }
627}
628
629/*
630 * Print an error message and perform a fatal exit.
631 */
632void fatalbox(char *fmt, ...)
633{
634 char str[0x100]; /* Make the size big enough */
635 va_list ap;
636 va_start(ap, fmt);
637 strcpy(str, "Fatal:");
638 vsprintf(str+strlen(str), fmt, ap);
639 va_end(ap);
640 strcat(str, "\n");
641 fprintf(stderr, str);
642
643 exit(1);
644}
645void connection_fatal(char *fmt, ...)
646{
647 char str[0x100]; /* Make the size big enough */
648 va_list ap;
649 va_start(ap, fmt);
650 strcpy(str, "Fatal:");
651 vsprintf(str+strlen(str), fmt, ap);
652 va_end(ap);
653 strcat(str, "\n");
654 fprintf(stderr, str);
655
656 exit(1);
657}
658
659void logevent(char *string) { }
660
661void ldisc_send(char *buf, int len) {
662 /*
663 * This is only here because of the calls to ldisc_send(NULL,
664 * 0) in ssh.c. Nothing in PSFTP actually needs to use the
665 * ldisc as an ldisc. So if we get called with any real data, I
666 * want to know about it.
4c7f0d61 667 */
4a8fc3c4 668 assert(len == 0);
669}
670
671/*
672 * Be told what socket we're supposed to be using.
673 */
674static SOCKET sftp_ssh_socket;
675char *do_select(SOCKET skt, int startup) {
676 if (startup)
677 sftp_ssh_socket = skt;
678 else
679 sftp_ssh_socket = INVALID_SOCKET;
680 return NULL;
4c7f0d61 681}
4a8fc3c4 682extern int select_result(WPARAM, LPARAM);
683
684/*
685 * Receive a block of data from the SSH link. Block until all data
686 * is available.
687 *
688 * To do this, we repeatedly call the SSH protocol module, with our
689 * own trap in from_backend() to catch the data that comes back. We
690 * do this until we have enough data.
691 */
692
693static unsigned char *outptr; /* where to put the data */
694static unsigned outlen; /* how much data required */
695static unsigned char *pending = NULL; /* any spare data */
696static unsigned pendlen=0, pendsize=0; /* length and phys. size of buffer */
697void from_backend(int is_stderr, char *data, int datalen) {
698 unsigned char *p = (unsigned char *)data;
699 unsigned len = (unsigned)datalen;
700
701 /*
702 * stderr data is just spouted to local stderr and otherwise
703 * ignored.
704 */
705 if (is_stderr) {
706 fwrite(data, 1, len, stderr);
707 return;
708 }
709
710 /*
711 * If this is before the real session begins, just return.
712 */
713 if (!outptr)
714 return;
715
716 if (outlen > 0) {
717 unsigned used = outlen;
718 if (used > len) used = len;
719 memcpy(outptr, p, used);
720 outptr += used; outlen -= used;
721 p += used; len -= used;
722 }
723
724 if (len > 0) {
725 if (pendsize < pendlen + len) {
726 pendsize = pendlen + len + 4096;
727 pending = (pending ? srealloc(pending, pendsize) :
728 smalloc(pendsize));
729 if (!pending)
730 fatalbox("Out of memory");
731 }
732 memcpy(pending+pendlen, p, len);
733 pendlen += len;
734 }
735}
736int sftp_recvdata(char *buf, int len) {
737 outptr = (unsigned char *)buf;
738 outlen = len;
739
740 /*
741 * See if the pending-input block contains some of what we
742 * need.
743 */
744 if (pendlen > 0) {
745 unsigned pendused = pendlen;
746 if (pendused > outlen)
747 pendused = outlen;
748 memcpy(outptr, pending, pendused);
749 memmove(pending, pending+pendused, pendlen-pendused);
750 outptr += pendused;
751 outlen -= pendused;
752 pendlen -= pendused;
753 if (pendlen == 0) {
754 pendsize = 0;
755 sfree(pending);
756 pending = NULL;
757 }
758 if (outlen == 0)
759 return 1;
760 }
761
762 while (outlen > 0) {
763 fd_set readfds;
764
765 FD_ZERO(&readfds);
766 FD_SET(sftp_ssh_socket, &readfds);
767 if (select(1, &readfds, NULL, NULL, NULL) < 0)
768 return 0; /* doom */
769 select_result((WPARAM)sftp_ssh_socket, (LPARAM)FD_READ);
770 }
771
772 return 1;
773}
774int sftp_senddata(char *buf, int len) {
775 back->send((unsigned char *)buf, len);
776 return 1;
777}
778
779/*
780 * Loop through the ssh connection and authentication process.
781 */
782static void ssh_sftp_init(void) {
783 if (sftp_ssh_socket == INVALID_SOCKET)
784 return;
785 while (!back->sendok()) {
786 fd_set readfds;
787 FD_ZERO(&readfds);
788 FD_SET(sftp_ssh_socket, &readfds);
789 if (select(1, &readfds, NULL, NULL, NULL) < 0)
790 return; /* doom */
791 select_result((WPARAM)sftp_ssh_socket, (LPARAM)FD_READ);
792 }
793}
794
795static char *password = NULL;
796static int get_password(const char *prompt, char *str, int maxlen)
797{
798 HANDLE hin, hout;
799 DWORD savemode, i;
800
801 if (password) {
802 static int tried_once = 0;
803
804 if (tried_once) {
805 return 0;
806 } else {
807 strncpy(str, password, maxlen);
808 str[maxlen-1] = '\0';
809 tried_once = 1;
810 return 1;
811 }
812 }
813
814 hin = GetStdHandle(STD_INPUT_HANDLE);
815 hout = GetStdHandle(STD_OUTPUT_HANDLE);
816 if (hin == INVALID_HANDLE_VALUE || hout == INVALID_HANDLE_VALUE) {
817 fprintf(stderr, "Cannot get standard input/output handles\n");
818 exit(1);
819 }
820
821 GetConsoleMode(hin, &savemode);
822 SetConsoleMode(hin, (savemode & (~ENABLE_ECHO_INPUT)) |
823 ENABLE_PROCESSED_INPUT | ENABLE_LINE_INPUT);
824
825 WriteFile(hout, prompt, strlen(prompt), &i, NULL);
826 ReadFile(hin, str, maxlen-1, &i, NULL);
827
828 SetConsoleMode(hin, savemode);
829
830 if ((int)i > maxlen) i = maxlen-1; else i = i - 2;
831 str[i] = '\0';
832
833 WriteFile(hout, "\r\n", 2, &i, NULL);
834
835 return 1;
836}
837
838/*
839 * Initialize the Win$ock driver.
840 */
841static void init_winsock(void)
842{
843 WORD winsock_ver;
844 WSADATA wsadata;
845
846 winsock_ver = MAKEWORD(1, 1);
847 if (WSAStartup(winsock_ver, &wsadata)) {
848 fprintf(stderr, "Unable to initialise WinSock");
849 exit(1);
850 }
851 if (LOBYTE(wsadata.wVersion) != 1 ||
852 HIBYTE(wsadata.wVersion) != 1) {
853 fprintf(stderr, "WinSock version is incompatible with 1.1");
854 exit(1);
855 }
856}
857
858/*
859 * Short description of parameters.
860 */
861static void usage(void)
862{
863 printf("PuTTY Secure File Transfer (SFTP) client\n");
864 printf("%s\n", ver);
865 printf("Usage: psftp [options] user@host\n");
866 printf("Options:\n");
867 printf(" -v show verbose messages\n");
868 printf(" -P port connect to specified port\n");
869 printf(" -pw passw login with specified password\n");
870 exit(1);
871}
872
873/*
874 * Main program. Parse arguments etc.
875 */
876int main(int argc, char *argv[])
877{
878 int i;
879 int portnumber = 0;
880 char *user, *host, *userhost, *realhost;
881 char *err;
882
883 flags = FLAG_STDERR;
884 ssh_get_password = &get_password;
885 init_winsock();
886 sk_init();
887
888 userhost = user = NULL;
889
890 for (i = 1; i < argc; i++) {
891 if (argv[i][0] != '-') {
892 if (userhost)
893 usage();
894 else
895 userhost = dupstr(argv[i]);
896 } else if (strcmp(argv[i], "-v") == 0) {
897 verbose = 1, flags |= FLAG_VERBOSE;
898 } else if (strcmp(argv[i], "-h") == 0 ||
899 strcmp(argv[i], "-?") == 0) {
900 usage();
901 } else if (strcmp(argv[i], "-l") == 0 && i+1 < argc) {
902 user = argv[++i];
903 } else if (strcmp(argv[i], "-P") == 0 && i+1 < argc) {
904 portnumber = atoi(argv[++i]);
905 } else if (strcmp(argv[i], "-pw") == 0 && i+1 < argc) {
906 password = argv[++i];
907 } else if (strcmp(argv[i], "--") == 0) {
908 i++;
909 break;
910 } else {
911 usage();
912 }
913 }
914 argc -= i;
915 argv += i;
916 back = NULL;
917
918 if (argc > 0 || !userhost)
919 usage();
920
921 /* Separate host and username */
922 host = userhost;
923 host = strrchr(host, '@');
924 if (host == NULL) {
925 host = userhost;
926 } else {
927 *host++ = '\0';
928 if (user) {
929 printf("psftp: multiple usernames specified; using \"%s\"\n", user);
930 } else
931 user = userhost;
932 }
933
934 /* Try to load settings for this host */
935 do_defaults(host, &cfg);
936 if (cfg.host[0] == '\0') {
937 /* No settings for this host; use defaults */
938 do_defaults(NULL, &cfg);
939 strncpy(cfg.host, host, sizeof(cfg.host)-1);
940 cfg.host[sizeof(cfg.host)-1] = '\0';
941 cfg.port = 22;
942 }
943
944 /* Set username */
945 if (user != NULL && user[0] != '\0') {
946 strncpy(cfg.username, user, sizeof(cfg.username)-1);
947 cfg.username[sizeof(cfg.username)-1] = '\0';
948 }
949 if (!cfg.username[0]) {
950 printf("login as: ");
951 if (!fgets(cfg.username, sizeof(cfg.username), stdin)) {
952 fprintf(stderr, "psftp: aborting\n");
953 exit(1);
954 } else {
955 int len = strlen(cfg.username);
956 if (cfg.username[len-1] == '\n')
957 cfg.username[len-1] = '\0';
958 }
959 }
960
961 if (cfg.protocol != PROT_SSH)
962 cfg.port = 22;
963
964 if (portnumber)
965 cfg.port = portnumber;
966
967 /* SFTP uses SSH2 by default always */
968 cfg.sshprot = 2;
969
970 /* Set up subsystem name. FIXME: fudge for SSH1. */
971 strcpy(cfg.remote_cmd, "sftp");
972 cfg.ssh_subsys = TRUE;
973 cfg.nopty = TRUE;
974
975 back = &ssh_backend;
976
977 err = back->init(cfg.host, cfg.port, &realhost);
978 if (err != NULL) {
979 fprintf(stderr, "ssh_init: %s", err);
980 return 1;
981 }
982 ssh_sftp_init();
983 if (verbose && realhost != NULL)
984 printf("Connected to %s\n", realhost);
4c7f0d61 985
4c7f0d61 986 do_sftp();
4a8fc3c4 987
988 if (back != NULL && back->socket() != NULL) {
989 char ch;
990 back->special(TS_EOF);
991 sftp_recvdata(&ch, 1);
992 }
993 WSACleanup();
994 random_save_seed();
995
4c7f0d61 996 return 0;
997}