Run entire source base through GNU indent to tidy up the varying
[u/mdw/putty] / 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 }