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