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