]> asedeno.scripts.mit.edu Git - PuTTY.git/blob - psftp.c
Patches to prevent a couple of silly crashes
[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         fxp_close(fh);
410         sfree(outfname);
411         return 0;
412     }
413     fh = fxp_open(outfname, SSH_FXF_WRITE | SSH_FXF_CREAT | SSH_FXF_TRUNC);
414     if (!fh) {
415         printf("%s: %s\n", outfname, fxp_error());
416         sfree(outfname);
417         return 0;
418     }
419
420     printf("local:%s => remote:%s\n", fname, outfname);
421
422     offset = uint64_make(0,0);
423
424     /*
425      * FIXME: we can use FXP_FSTAT here to get the file size, and
426      * thus put up a progress bar.
427      */
428     while (1) {
429         char buffer[4096];
430         int len;
431
432         len = fread(buffer, 1, sizeof(buffer), fp);
433         if (len == -1) {
434             printf("error while reading local file\n");
435             break;
436         } else if (len == 0) {
437             break;
438         }
439         if (!fxp_write(fh, buffer, offset, len)) {
440             printf("error while writing: %s\n", fxp_error());
441             break;
442         }
443         offset = uint64_add32(offset, len);
444     }
445
446     fxp_close(fh);
447     fclose(fp);
448     sfree(outfname);
449
450     return 0;
451 }
452
453 static struct sftp_cmd_lookup {
454     char *name;
455     int (*obey)(struct sftp_command *);
456 } sftp_lookup[] = {
457     /*
458      * List of sftp commands. This is binary-searched so it MUST be
459      * in ASCII order.
460      */
461     {"bye", sftp_cmd_quit},
462     {"cd", sftp_cmd_cd},
463     {"dir", sftp_cmd_ls},
464     {"exit", sftp_cmd_quit},
465     {"get", sftp_cmd_get},
466     {"ls", sftp_cmd_ls},
467     {"put", sftp_cmd_put},
468     {"quit", sftp_cmd_quit},
469 };
470
471 /* ----------------------------------------------------------------------
472  * Command line reading and parsing.
473  */
474 struct sftp_command *sftp_getcmd(void) {
475     char *line;
476     int linelen, linesize;
477     struct sftp_command *cmd;
478     char *p, *q, *r;
479     int quoting;
480
481     printf("psftp> ");
482     fflush(stdout);
483
484     cmd = smalloc(sizeof(struct sftp_command));
485     cmd->words = NULL;
486     cmd->nwords = 0;
487     cmd->wordssize = 0;
488
489     line = NULL;
490     linesize = linelen = 0;
491     while (1) {
492         int len;
493         char *ret;
494
495         linesize += 512;
496         line = srealloc(line, linesize);
497         ret = fgets(line+linelen, linesize-linelen, stdin);
498
499         if (!ret || (linelen == 0 && line[0] == '\0')) {
500             cmd->obey = sftp_cmd_quit;
501             printf("quit\n");
502             return cmd;                /* eof */
503         }
504         len = linelen + strlen(line+linelen);
505         linelen += len;
506         if (line[linelen-1] == '\n') {
507             linelen--;
508             line[linelen] = '\0';
509             break;
510         }
511     }
512
513     /*
514      * Parse the command line into words. The syntax is:
515      *  - double quotes are removed, but cause spaces within to be
516      *    treated as non-separating.
517      *  - a double-doublequote pair is a literal double quote, inside
518      *    _or_ outside quotes. Like this:
519      * 
520      *      firstword "second word" "this has ""quotes"" in" sodoes""this""
521      * 
522      * becomes
523      * 
524      *      >firstword<
525      *      >second word<
526      *      >this has "quotes" in<
527      *      >sodoes"this"<
528      */
529     p = line;
530     while (*p) {
531         /* skip whitespace */
532         while (*p && (*p == ' ' || *p == '\t')) p++;
533         /* mark start of word */
534         q = r = p;                     /* q sits at start, r writes word */
535         quoting = 0;
536         while (*p) {
537             if (!quoting && (*p == ' ' || *p == '\t'))
538                 break;                 /* reached end of word */
539             else if (*p == '"' && p[1] == '"')
540                 p+=2, *r++ = '"';      /* a literal quote */
541             else if (*p == '"')
542                 p++, quoting = !quoting;
543             else
544                 *r++ = *p++;
545         }
546         if (*p) p++;                           /* skip over the whitespace */
547         *r = '\0';
548         if (cmd->nwords >= cmd->wordssize) {
549             cmd->wordssize = cmd->nwords + 16;
550             cmd->words = srealloc(cmd->words, cmd->wordssize*sizeof(char *));
551         }
552         cmd->words[cmd->nwords++] = q;
553     }
554
555     /*
556      * Now parse the first word and assign a function.
557      */
558
559     if (cmd->nwords == 0)
560         cmd->obey = sftp_cmd_null;
561     else {
562         int i, j, k, cmp;
563
564         cmd->obey = sftp_cmd_unknown;
565
566         i = -1;
567         j = sizeof(sftp_lookup) / sizeof(*sftp_lookup);
568         while (j - i > 1) {
569             k = (j + i) / 2;
570             cmp = strcmp(cmd->words[0], sftp_lookup[k].name);
571             if (cmp < 0)
572                 j = k;
573             else if (cmp > 0)
574                 i = k;
575             else {
576                 cmd->obey = sftp_lookup[k].obey;
577                 break;
578             }
579         }
580     }
581
582     return cmd;
583 }
584
585 void do_sftp(void) {
586     /*
587      * Do protocol initialisation. 
588      */
589     if (!fxp_init()) {
590         fprintf(stderr,
591                 "Fatal: unable to initialise SFTP: %s\n",
592                 fxp_error());
593         return;
594     }
595
596     /*
597      * Find out where our home directory is.
598      */
599     homedir = fxp_realpath(".");
600     if (!homedir) {
601         fprintf(stderr,
602                 "Warning: failed to resolve home directory: %s\n",
603                 fxp_error());
604         homedir = dupstr(".");
605     } else {
606         printf("Remote working directory is %s\n", homedir);
607     }
608     pwd = dupstr(homedir);
609
610     /* ------------------------------------------------------------------
611      * Now we're ready to do Real Stuff.
612      */
613     while (1) {
614         struct sftp_command *cmd;
615         cmd = sftp_getcmd();
616         if (!cmd)
617             break;
618         if (cmd->obey(cmd) < 0)
619             break;
620     }
621 }
622
623 /* ----------------------------------------------------------------------
624  * Dirty bits: integration with PuTTY.
625  */
626
627 static int verbose = 0;
628
629 void verify_ssh_host_key(char *host, int port, char *keytype,
630                          char *keystr, char *fingerprint) {
631     int ret;
632
633     static const char absentmsg[] =
634         "The server's host key is not cached in the registry. You\n"
635         "have no guarantee that the server is the computer you\n"
636         "think it is.\n"
637         "The server's key fingerprint is:\n"
638         "%s\n"
639         "If you trust this host, enter \"y\" to add the key to\n"
640         "PuTTY's cache and carry on connecting.\n"
641         "If you do not trust this host, enter \"n\" to abandon the\n"
642         "connection.\n"
643         "Continue connecting? (y/n) ";
644
645     static const char wrongmsg[] =
646         "WARNING - POTENTIAL SECURITY BREACH!\n"
647         "The server's host key does not match the one PuTTY has\n"
648         "cached in the registry. This means that either the\n"
649         "server administrator has changed the host key, or you\n"
650         "have actually connected to another computer pretending\n"
651         "to be the server.\n"
652         "The new key fingerprint is:\n"
653         "%s\n"
654         "If you were expecting this change and trust the new key,\n"
655         "enter Yes to update PuTTY's cache and continue connecting.\n"
656         "If you want to carry on connecting but without updating\n"
657         "the cache, enter No.\n"
658         "If you want to abandon the connection completely, press\n"
659         "Return to cancel. Pressing Return is the ONLY guaranteed\n"
660         "safe choice.\n"
661         "Update cached key? (y/n, Return cancels connection) ";
662
663     static const char abandoned[] = "Connection abandoned.\n";
664
665     char line[32];
666
667     /*
668      * Verify the key against the registry.
669      */
670     ret = verify_host_key(host, port, keytype, keystr);
671
672     if (ret == 0)                      /* success - key matched OK */
673         return;
674     if (ret == 2) {                    /* key was different */
675         fprintf(stderr, wrongmsg, fingerprint);
676         if (fgets(line, sizeof(line), stdin) &&
677             line[0] != '\0' && line[0] != '\n') {
678             if (line[0] == 'y' || line[0] == 'Y')
679                 store_host_key(host, port, keytype, keystr);
680         } else {
681             fprintf(stderr, abandoned);
682             exit(0);
683         }
684     }
685     if (ret == 1) {                    /* key was absent */
686         fprintf(stderr, absentmsg, fingerprint);
687         if (fgets(line, sizeof(line), stdin) &&
688             (line[0] == 'y' || line[0] == 'Y'))
689             store_host_key(host, port, keytype, keystr);
690         else {
691             fprintf(stderr, abandoned);
692             exit(0);
693         }
694     }
695 }
696
697 /*
698  *  Print an error message and perform a fatal exit.
699  */
700 void fatalbox(char *fmt, ...)
701 {
702     char str[0x100]; /* Make the size big enough */
703     va_list ap;
704     va_start(ap, fmt);
705     strcpy(str, "Fatal:");
706     vsprintf(str+strlen(str), fmt, ap);
707     va_end(ap);
708     strcat(str, "\n");
709     fprintf(stderr, str);
710
711     exit(1);
712 }
713 void connection_fatal(char *fmt, ...)
714 {
715     char str[0x100]; /* Make the size big enough */
716     va_list ap;
717     va_start(ap, fmt);
718     strcpy(str, "Fatal:");
719     vsprintf(str+strlen(str), fmt, ap);
720     va_end(ap);
721     strcat(str, "\n");
722     fprintf(stderr, str);
723
724     exit(1);
725 }
726
727 void logevent(char *string) { }
728
729 void ldisc_send(char *buf, int len) {
730     /*
731      * This is only here because of the calls to ldisc_send(NULL,
732      * 0) in ssh.c. Nothing in PSFTP actually needs to use the
733      * ldisc as an ldisc. So if we get called with any real data, I
734      * want to know about it.
735      */
736     assert(len == 0);
737 }
738
739 /*
740  * Be told what socket we're supposed to be using.
741  */
742 static SOCKET sftp_ssh_socket;
743 char *do_select(SOCKET skt, int startup) {
744     if (startup)
745         sftp_ssh_socket = skt;
746     else
747         sftp_ssh_socket = INVALID_SOCKET;
748     return NULL;
749 }
750 extern int select_result(WPARAM, LPARAM);
751
752 /*
753  * Receive a block of data from the SSH link. Block until all data
754  * is available.
755  *
756  * To do this, we repeatedly call the SSH protocol module, with our
757  * own trap in from_backend() to catch the data that comes back. We
758  * do this until we have enough data.
759  */
760
761 static unsigned char *outptr;          /* where to put the data */
762 static unsigned outlen;                /* how much data required */
763 static unsigned char *pending = NULL;  /* any spare data */
764 static unsigned pendlen=0, pendsize=0; /* length and phys. size of buffer */
765 void from_backend(int is_stderr, char *data, int datalen) {
766     unsigned char *p = (unsigned char *)data;
767     unsigned len = (unsigned)datalen;
768
769     /*
770      * stderr data is just spouted to local stderr and otherwise
771      * ignored.
772      */
773     if (is_stderr) {
774         fwrite(data, 1, len, stderr);
775         return;
776     }
777
778     /*
779      * If this is before the real session begins, just return.
780      */
781     if (!outptr)
782         return;
783
784     if (outlen > 0) {
785         unsigned used = outlen;
786         if (used > len) used = len;
787         memcpy(outptr, p, used);
788         outptr += used; outlen -= used;
789         p += used; len -= used;
790     }
791
792     if (len > 0) {
793         if (pendsize < pendlen + len) {
794             pendsize = pendlen + len + 4096;
795             pending = (pending ? srealloc(pending, pendsize) :
796                        smalloc(pendsize));
797             if (!pending)
798                 fatalbox("Out of memory");
799         }
800         memcpy(pending+pendlen, p, len);
801         pendlen += len;
802     }
803 }
804 int sftp_recvdata(char *buf, int len) {
805     outptr = (unsigned char *)buf;
806     outlen = len;
807
808     /*
809      * See if the pending-input block contains some of what we
810      * need.
811      */
812     if (pendlen > 0) {
813         unsigned pendused = pendlen;
814         if (pendused > outlen)
815             pendused = outlen;
816         memcpy(outptr, pending, pendused);
817         memmove(pending, pending+pendused, pendlen-pendused);
818         outptr += pendused;
819         outlen -= pendused;
820         pendlen -= pendused;
821         if (pendlen == 0) {
822             pendsize = 0;
823             sfree(pending);
824             pending = NULL;
825         }
826         if (outlen == 0)
827             return 1;
828     }
829
830     while (outlen > 0) {
831         fd_set readfds;
832
833         FD_ZERO(&readfds);
834         FD_SET(sftp_ssh_socket, &readfds);
835         if (select(1, &readfds, NULL, NULL, NULL) < 0)
836             return 0;                  /* doom */
837         select_result((WPARAM)sftp_ssh_socket, (LPARAM)FD_READ);
838     }
839
840     return 1;
841 }
842 int sftp_senddata(char *buf, int len) {
843     back->send((unsigned char *)buf, len);
844     return 1;
845 }
846
847 /*
848  * Loop through the ssh connection and authentication process.
849  */
850 static void ssh_sftp_init(void) {
851     if (sftp_ssh_socket == INVALID_SOCKET)
852         return;
853     while (!back->sendok()) {
854         fd_set readfds;
855         FD_ZERO(&readfds);
856         FD_SET(sftp_ssh_socket, &readfds);
857         if (select(1, &readfds, NULL, NULL, NULL) < 0)
858             return;                    /* doom */
859         select_result((WPARAM)sftp_ssh_socket, (LPARAM)FD_READ);
860     }
861 }
862
863 static char *password = NULL;
864 static int get_password(const char *prompt, char *str, int maxlen)
865 {
866     HANDLE hin, hout;
867     DWORD savemode, i;
868
869     if (password) {
870         static int tried_once = 0;
871
872         if (tried_once) {
873             return 0;
874         } else {
875             strncpy(str, password, maxlen);
876             str[maxlen-1] = '\0';
877             tried_once = 1;
878             return 1;
879         }
880     }
881
882     hin = GetStdHandle(STD_INPUT_HANDLE);
883     hout = GetStdHandle(STD_OUTPUT_HANDLE);
884     if (hin == INVALID_HANDLE_VALUE || hout == INVALID_HANDLE_VALUE) {
885         fprintf(stderr, "Cannot get standard input/output handles\n");
886         exit(1);
887     }
888
889     GetConsoleMode(hin, &savemode);
890     SetConsoleMode(hin, (savemode & (~ENABLE_ECHO_INPUT)) |
891                    ENABLE_PROCESSED_INPUT | ENABLE_LINE_INPUT);
892
893     WriteFile(hout, prompt, strlen(prompt), &i, NULL);
894     ReadFile(hin, str, maxlen-1, &i, NULL);
895
896     SetConsoleMode(hin, savemode);
897
898     if ((int)i > maxlen) i = maxlen-1; else i = i - 2;
899     str[i] = '\0';
900
901     WriteFile(hout, "\r\n", 2, &i, NULL);
902
903     return 1;
904 }
905
906 /*
907  *  Initialize the Win$ock driver.
908  */
909 static void init_winsock(void)
910 {
911     WORD winsock_ver;
912     WSADATA wsadata;
913
914     winsock_ver = MAKEWORD(1, 1);
915     if (WSAStartup(winsock_ver, &wsadata)) {
916         fprintf(stderr, "Unable to initialise WinSock");
917         exit(1);
918     }
919     if (LOBYTE(wsadata.wVersion) != 1 ||
920         HIBYTE(wsadata.wVersion) != 1) {
921         fprintf(stderr, "WinSock version is incompatible with 1.1");
922         exit(1);
923     }
924 }
925
926 /*
927  *  Short description of parameters.
928  */
929 static void usage(void)
930 {
931     printf("PuTTY Secure File Transfer (SFTP) client\n");
932     printf("%s\n", ver);
933     printf("Usage: psftp [options] user@host\n");
934     printf("Options:\n");
935     printf("  -v        show verbose messages\n");
936     printf("  -P port   connect to specified port\n");
937     printf("  -pw passw login with specified password\n");
938     exit(1);
939 }
940
941 /*
942  * Main program. Parse arguments etc.
943  */
944 int main(int argc, char *argv[])
945 {
946     int i;
947     int portnumber = 0;
948     char *user, *host, *userhost, *realhost;
949     char *err;
950
951     flags = FLAG_STDERR;
952     ssh_get_password = &get_password;
953     init_winsock();
954     sk_init();
955
956     userhost = user = NULL;
957
958     for (i = 1; i < argc; i++) {
959         if (argv[i][0] != '-') {
960             if (userhost)
961                 usage();
962             else
963                 userhost = dupstr(argv[i]);
964         } else if (strcmp(argv[i], "-v") == 0) {
965             verbose = 1, flags |= FLAG_VERBOSE;
966         } else if (strcmp(argv[i], "-h") == 0 ||
967                    strcmp(argv[i], "-?") == 0) {
968             usage();
969         } else if (strcmp(argv[i], "-l") == 0 && i+1 < argc) {
970             user = argv[++i];
971         } else if (strcmp(argv[i], "-P") == 0 && i+1 < argc) {
972             portnumber = atoi(argv[++i]);
973         } else if (strcmp(argv[i], "-pw") == 0 && i+1 < argc) {
974             password = argv[++i];
975         } else if (strcmp(argv[i], "--") == 0) {
976             i++;
977             break;
978         } else {
979             usage();
980         }
981     }
982     argc -= i;
983     argv += i;
984     back = NULL;
985
986     if (argc > 0 || !userhost)
987         usage();
988
989     /* Separate host and username */
990     host = userhost;
991     host = strrchr(host, '@');
992     if (host == NULL) {
993         host = userhost;
994     } else {
995         *host++ = '\0';
996         if (user) {
997             printf("psftp: multiple usernames specified; using \"%s\"\n", user);
998         } else
999             user = userhost;
1000     }
1001
1002     /* Try to load settings for this host */
1003     do_defaults(host, &cfg);
1004     if (cfg.host[0] == '\0') {
1005         /* No settings for this host; use defaults */
1006         do_defaults(NULL, &cfg);
1007         strncpy(cfg.host, host, sizeof(cfg.host)-1);
1008         cfg.host[sizeof(cfg.host)-1] = '\0';
1009         cfg.port = 22;
1010     }
1011
1012     /* Set username */
1013     if (user != NULL && user[0] != '\0') {
1014         strncpy(cfg.username, user, sizeof(cfg.username)-1);
1015         cfg.username[sizeof(cfg.username)-1] = '\0';
1016     }
1017     if (!cfg.username[0]) {
1018         printf("login as: ");
1019         if (!fgets(cfg.username, sizeof(cfg.username), stdin)) {
1020             fprintf(stderr, "psftp: aborting\n");
1021             exit(1);
1022         } else {
1023             int len = strlen(cfg.username);
1024             if (cfg.username[len-1] == '\n')
1025                 cfg.username[len-1] = '\0';
1026         }
1027     }
1028
1029     if (cfg.protocol != PROT_SSH)
1030         cfg.port = 22;
1031
1032     if (portnumber)
1033         cfg.port = portnumber;
1034
1035     /* SFTP uses SSH2 by default always */
1036     cfg.sshprot = 2;
1037
1038     /* Set up subsystem name. FIXME: fudge for SSH1. */
1039     strcpy(cfg.remote_cmd, "sftp");
1040     cfg.ssh_subsys = TRUE;
1041     cfg.nopty = TRUE;
1042
1043     back = &ssh_backend;
1044
1045     err = back->init(cfg.host, cfg.port, &realhost);
1046     if (err != NULL) {
1047         fprintf(stderr, "ssh_init: %s", err);
1048         return 1;
1049     }
1050     ssh_sftp_init();
1051     if (verbose && realhost != NULL)
1052         printf("Connected to %s\n", realhost);
1053
1054     do_sftp();
1055
1056     if (back != NULL && back->socket() != NULL) {
1057         char ch;
1058         back->special(TS_EOF);
1059         sftp_recvdata(&ch, 1);
1060     }
1061     WSACleanup();
1062     random_save_seed();
1063
1064     return 0;
1065 }