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