]> asedeno.scripts.mit.edu Git - PuTTY.git/blob - psftp.c
Run entire source base through GNU indent to tidy up the varying
[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
648     static const char absentmsg[] =
649         "The server's host key is not cached in the registry. You\n"
650         "have no guarantee that the server is the computer you\n"
651         "think it is.\n"
652         "The server's key fingerprint is:\n"
653         "%s\n"
654         "If you trust this host, enter \"y\" to add the key to\n"
655         "PuTTY's cache and carry on connecting.\n"
656         "If you do not trust this host, enter \"n\" to abandon the\n"
657         "connection.\n" "Continue connecting? (y/n) ";
658
659     static const char wrongmsg[] =
660         "WARNING - POTENTIAL SECURITY BREACH!\n"
661         "The server's host key does not match the one PuTTY has\n"
662         "cached in the registry. This means that either the\n"
663         "server administrator has changed the host key, or you\n"
664         "have actually connected to another computer pretending\n"
665         "to be the server.\n"
666         "The new key fingerprint is:\n"
667         "%s\n"
668         "If you were expecting this change and trust the new key,\n"
669         "enter Yes to update PuTTY's cache and continue connecting.\n"
670         "If you want to carry on connecting but without updating\n"
671         "the cache, enter No.\n"
672         "If you want to abandon the connection completely, press\n"
673         "Return to cancel. Pressing Return is the ONLY guaranteed\n"
674         "safe choice.\n"
675         "Update cached key? (y/n, Return cancels connection) ";
676
677     static const char abandoned[] = "Connection abandoned.\n";
678
679     char line[32];
680
681     /*
682      * Verify the key against the registry.
683      */
684     ret = verify_host_key(host, port, keytype, keystr);
685
686     if (ret == 0)                      /* success - key matched OK */
687         return;
688     if (ret == 2) {                    /* key was different */
689         fprintf(stderr, wrongmsg, fingerprint);
690         if (fgets(line, sizeof(line), stdin) &&
691             line[0] != '\0' && line[0] != '\n') {
692             if (line[0] == 'y' || line[0] == 'Y')
693                 store_host_key(host, port, keytype, keystr);
694         } else {
695             fprintf(stderr, abandoned);
696             exit(0);
697         }
698     }
699     if (ret == 1) {                    /* key was absent */
700         fprintf(stderr, absentmsg, fingerprint);
701         if (fgets(line, sizeof(line), stdin) &&
702             (line[0] == 'y' || line[0] == 'Y'))
703             store_host_key(host, port, keytype, keystr);
704         else {
705             fprintf(stderr, abandoned);
706             exit(0);
707         }
708     }
709 }
710
711 /*
712  *  Print an error message and perform a fatal exit.
713  */
714 void fatalbox(char *fmt, ...)
715 {
716     char str[0x100];                   /* Make the size big enough */
717     va_list ap;
718     va_start(ap, fmt);
719     strcpy(str, "Fatal:");
720     vsprintf(str + strlen(str), fmt, ap);
721     va_end(ap);
722     strcat(str, "\n");
723     fprintf(stderr, str);
724
725     exit(1);
726 }
727 void connection_fatal(char *fmt, ...)
728 {
729     char str[0x100];                   /* Make the size big enough */
730     va_list ap;
731     va_start(ap, fmt);
732     strcpy(str, "Fatal:");
733     vsprintf(str + strlen(str), fmt, ap);
734     va_end(ap);
735     strcat(str, "\n");
736     fprintf(stderr, str);
737
738     exit(1);
739 }
740
741 void logevent(char *string)
742 {
743 }
744
745 void ldisc_send(char *buf, int len)
746 {
747     /*
748      * This is only here because of the calls to ldisc_send(NULL,
749      * 0) in ssh.c. Nothing in PSFTP actually needs to use the
750      * ldisc as an ldisc. So if we get called with any real data, I
751      * want to know about it.
752      */
753     assert(len == 0);
754 }
755
756 /*
757  * Be told what socket we're supposed to be using.
758  */
759 static SOCKET sftp_ssh_socket;
760 char *do_select(SOCKET skt, int startup)
761 {
762     if (startup)
763         sftp_ssh_socket = skt;
764     else
765         sftp_ssh_socket = INVALID_SOCKET;
766     return NULL;
767 }
768 extern int select_result(WPARAM, LPARAM);
769
770 /*
771  * Receive a block of data from the SSH link. Block until all data
772  * is available.
773  *
774  * To do this, we repeatedly call the SSH protocol module, with our
775  * own trap in from_backend() to catch the data that comes back. We
776  * do this until we have enough data.
777  */
778
779 static unsigned char *outptr;          /* where to put the data */
780 static unsigned outlen;                /* how much data required */
781 static unsigned char *pending = NULL;  /* any spare data */
782 static unsigned pendlen = 0, pendsize = 0;      /* length and phys. size of buffer */
783 void from_backend(int is_stderr, char *data, int datalen)
784 {
785     unsigned char *p = (unsigned char *) data;
786     unsigned len = (unsigned) datalen;
787
788     /*
789      * stderr data is just spouted to local stderr and otherwise
790      * ignored.
791      */
792     if (is_stderr) {
793         fwrite(data, 1, len, stderr);
794         return;
795     }
796
797     /*
798      * If this is before the real session begins, just return.
799      */
800     if (!outptr)
801         return;
802
803     if (outlen > 0) {
804         unsigned used = outlen;
805         if (used > len)
806             used = len;
807         memcpy(outptr, p, used);
808         outptr += used;
809         outlen -= used;
810         p += used;
811         len -= used;
812     }
813
814     if (len > 0) {
815         if (pendsize < pendlen + len) {
816             pendsize = pendlen + len + 4096;
817             pending = (pending ? srealloc(pending, pendsize) :
818                        smalloc(pendsize));
819             if (!pending)
820                 fatalbox("Out of memory");
821         }
822         memcpy(pending + pendlen, p, len);
823         pendlen += len;
824     }
825 }
826 int sftp_recvdata(char *buf, int len)
827 {
828     outptr = (unsigned char *) buf;
829     outlen = len;
830
831     /*
832      * See if the pending-input block contains some of what we
833      * need.
834      */
835     if (pendlen > 0) {
836         unsigned pendused = pendlen;
837         if (pendused > outlen)
838             pendused = outlen;
839         memcpy(outptr, pending, pendused);
840         memmove(pending, pending + pendused, pendlen - pendused);
841         outptr += pendused;
842         outlen -= pendused;
843         pendlen -= pendused;
844         if (pendlen == 0) {
845             pendsize = 0;
846             sfree(pending);
847             pending = NULL;
848         }
849         if (outlen == 0)
850             return 1;
851     }
852
853     while (outlen > 0) {
854         fd_set readfds;
855
856         FD_ZERO(&readfds);
857         FD_SET(sftp_ssh_socket, &readfds);
858         if (select(1, &readfds, NULL, NULL, NULL) < 0)
859             return 0;                  /* doom */
860         select_result((WPARAM) sftp_ssh_socket, (LPARAM) FD_READ);
861     }
862
863     return 1;
864 }
865 int sftp_senddata(char *buf, int len)
866 {
867     back->send((unsigned char *) buf, len);
868     return 1;
869 }
870
871 /*
872  * Loop through the ssh connection and authentication process.
873  */
874 static void ssh_sftp_init(void)
875 {
876     if (sftp_ssh_socket == INVALID_SOCKET)
877         return;
878     while (!back->sendok()) {
879         fd_set readfds;
880         FD_ZERO(&readfds);
881         FD_SET(sftp_ssh_socket, &readfds);
882         if (select(1, &readfds, NULL, NULL, NULL) < 0)
883             return;                    /* doom */
884         select_result((WPARAM) sftp_ssh_socket, (LPARAM) FD_READ);
885     }
886 }
887
888 static char *password = NULL;
889 static int get_line(const char *prompt, char *str, int maxlen, int is_pw)
890 {
891     HANDLE hin, hout;
892     DWORD savemode, newmode, i;
893
894     if (password) {
895         static int tried_once = 0;
896
897         if (tried_once) {
898             return 0;
899         } else {
900             strncpy(str, password, maxlen);
901             str[maxlen - 1] = '\0';
902             tried_once = 1;
903             return 1;
904         }
905     }
906
907     hin = GetStdHandle(STD_INPUT_HANDLE);
908     hout = GetStdHandle(STD_OUTPUT_HANDLE);
909     if (hin == INVALID_HANDLE_VALUE || hout == INVALID_HANDLE_VALUE) {
910         fprintf(stderr, "Cannot get standard input/output handles\n");
911         exit(1);
912     }
913
914     GetConsoleMode(hin, &savemode);
915     newmode = savemode | ENABLE_PROCESSED_INPUT | ENABLE_LINE_INPUT;
916     if (is_pw)
917         newmode &= ~ENABLE_ECHO_INPUT;
918     else
919         newmode |= ENABLE_ECHO_INPUT;
920     SetConsoleMode(hin, newmode);
921
922     WriteFile(hout, prompt, strlen(prompt), &i, NULL);
923     ReadFile(hin, str, maxlen - 1, &i, NULL);
924
925     SetConsoleMode(hin, savemode);
926
927     if ((int) i > maxlen)
928         i = maxlen - 1;
929     else
930         i = i - 2;
931     str[i] = '\0';
932
933     if (is_pw)
934         WriteFile(hout, "\r\n", 2, &i, NULL);
935
936     return 1;
937 }
938
939 /*
940  *  Initialize the Win$ock driver.
941  */
942 static void init_winsock(void)
943 {
944     WORD winsock_ver;
945     WSADATA wsadata;
946
947     winsock_ver = MAKEWORD(1, 1);
948     if (WSAStartup(winsock_ver, &wsadata)) {
949         fprintf(stderr, "Unable to initialise WinSock");
950         exit(1);
951     }
952     if (LOBYTE(wsadata.wVersion) != 1 || HIBYTE(wsadata.wVersion) != 1) {
953         fprintf(stderr, "WinSock version is incompatible with 1.1");
954         exit(1);
955     }
956 }
957
958 /*
959  *  Short description of parameters.
960  */
961 static void usage(void)
962 {
963     printf("PuTTY Secure File Transfer (SFTP) client\n");
964     printf("%s\n", ver);
965     printf("Usage: psftp [options] user@host\n");
966     printf("Options:\n");
967     printf("  -v        show verbose messages\n");
968     printf("  -P port   connect to specified port\n");
969     printf("  -pw passw login with specified password\n");
970     exit(1);
971 }
972
973 /*
974  * Main program. Parse arguments etc.
975  */
976 int main(int argc, char *argv[])
977 {
978     int i;
979     int portnumber = 0;
980     char *user, *host, *userhost, *realhost;
981     char *err;
982
983     flags = FLAG_STDERR;
984     ssh_get_line = &get_line;
985     init_winsock();
986     sk_init();
987
988     userhost = user = NULL;
989
990     for (i = 1; i < argc; i++) {
991         if (argv[i][0] != '-') {
992             if (userhost)
993                 usage();
994             else
995                 userhost = dupstr(argv[i]);
996         } else if (strcmp(argv[i], "-v") == 0) {
997             verbose = 1, flags |= FLAG_VERBOSE;
998         } else if (strcmp(argv[i], "-h") == 0 ||
999                    strcmp(argv[i], "-?") == 0) {
1000             usage();
1001         } else if (strcmp(argv[i], "-l") == 0 && i + 1 < argc) {
1002             user = argv[++i];
1003         } else if (strcmp(argv[i], "-P") == 0 && i + 1 < argc) {
1004             portnumber = atoi(argv[++i]);
1005         } else if (strcmp(argv[i], "-pw") == 0 && i + 1 < argc) {
1006             password = argv[++i];
1007         } else if (strcmp(argv[i], "--") == 0) {
1008             i++;
1009             break;
1010         } else {
1011             usage();
1012         }
1013     }
1014     argc -= i;
1015     argv += i;
1016     back = NULL;
1017
1018     if (argc > 0 || !userhost)
1019         usage();
1020
1021     /* Separate host and username */
1022     host = userhost;
1023     host = strrchr(host, '@');
1024     if (host == NULL) {
1025         host = userhost;
1026     } else {
1027         *host++ = '\0';
1028         if (user) {
1029             printf("psftp: multiple usernames specified; using \"%s\"\n",
1030                    user);
1031         } else
1032             user = userhost;
1033     }
1034
1035     /* Try to load settings for this host */
1036     do_defaults(host, &cfg);
1037     if (cfg.host[0] == '\0') {
1038         /* No settings for this host; use defaults */
1039         do_defaults(NULL, &cfg);
1040         strncpy(cfg.host, host, sizeof(cfg.host) - 1);
1041         cfg.host[sizeof(cfg.host) - 1] = '\0';
1042         cfg.port = 22;
1043     }
1044
1045     /* Set username */
1046     if (user != NULL && user[0] != '\0') {
1047         strncpy(cfg.username, user, sizeof(cfg.username) - 1);
1048         cfg.username[sizeof(cfg.username) - 1] = '\0';
1049     }
1050     if (!cfg.username[0]) {
1051         printf("login as: ");
1052         if (!fgets(cfg.username, sizeof(cfg.username), stdin)) {
1053             fprintf(stderr, "psftp: aborting\n");
1054             exit(1);
1055         } else {
1056             int len = strlen(cfg.username);
1057             if (cfg.username[len - 1] == '\n')
1058                 cfg.username[len - 1] = '\0';
1059         }
1060     }
1061
1062     if (cfg.protocol != PROT_SSH)
1063         cfg.port = 22;
1064
1065     if (portnumber)
1066         cfg.port = portnumber;
1067
1068     /* SFTP uses SSH2 by default always */
1069     cfg.sshprot = 2;
1070
1071     /* Set up subsystem name. FIXME: fudge for SSH1. */
1072     strcpy(cfg.remote_cmd, "sftp");
1073     cfg.ssh_subsys = TRUE;
1074     cfg.nopty = TRUE;
1075
1076     back = &ssh_backend;
1077
1078     err = back->init(cfg.host, cfg.port, &realhost);
1079     if (err != NULL) {
1080         fprintf(stderr, "ssh_init: %s", err);
1081         return 1;
1082     }
1083     ssh_sftp_init();
1084     if (verbose && realhost != NULL)
1085         printf("Connected to %s\n", realhost);
1086
1087     do_sftp();
1088
1089     if (back != NULL && back->socket() != NULL) {
1090         char ch;
1091         back->special(TS_EOF);
1092         sftp_recvdata(&ch, 1);
1093     }
1094     WSACleanup();
1095     random_save_seed();
1096
1097     return 0;
1098 }