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