2 * psftp.c: front end for PSFTP.
13 #define PUTTY_DO_GLOBALS
21 * Since SFTP is a request-response oriented protocol, it requires
22 * no buffer management: when we send data, we stop and wait for an
23 * acknowledgement _anyway_, and so we can't possibly overfill our
27 /* ----------------------------------------------------------------------
33 /* ----------------------------------------------------------------------
34 * Higher-level helper functions used in commands.
38 * Attempt to canonify a pathname starting from the pwd. If
39 * canonification fails, at least fall back to returning a _valid_
40 * pathname (though it may be ugly, eg /home/simon/../foobar).
42 char *canonify(char *name)
44 char *fullname, *canonname;
47 fullname = dupstr(name);
50 if (pwd[strlen(pwd) - 1] == '/')
54 fullname = dupcat(pwd, slash, name, NULL);
57 canonname = fxp_realpath(fullname);
64 * Attempt number 2. Some FXP_REALPATH implementations
65 * (glibc-based ones, in particular) require the _whole_
66 * path to point to something that exists, whereas others
67 * (BSD-based) only require all but the last component to
68 * exist. So if the first call failed, we should strip off
69 * everything from the last slash onwards and try again,
70 * then put the final component back on.
74 * - if the last component is "/." or "/..", then we don't
75 * bother trying this because there's no way it can work.
77 * - if the thing actually ends with a "/", we remove it
78 * before we start. Except if the string is "/" itself
79 * (although I can't see why we'd have got here if so,
80 * because surely "/" would have worked the first
81 * time?), in which case we don't bother.
83 * - if there's no slash in the string at all, give up in
84 * confusion (we expect at least one because of the way
85 * we constructed the string).
92 if (i > 2 && fullname[i - 1] == '/')
93 fullname[--i] = '\0'; /* strip trailing / unless at pos 0 */
94 while (i > 0 && fullname[--i] != '/');
97 * Give up on special cases.
99 if (fullname[i] != '/' || /* no slash at all */
100 !strcmp(fullname + i, "/.") || /* ends in /. */
101 !strcmp(fullname + i, "/..") || /* ends in /.. */
102 !strcmp(fullname, "/")) {
107 * Now i points at the slash. Deal with the final special
108 * case i==0 (ie the whole path was "/nonexistentfile").
110 fullname[i] = '\0'; /* separate the string */
112 canonname = fxp_realpath("/");
114 canonname = fxp_realpath(fullname);
118 return fullname; /* even that failed; give up */
121 * We have a canonical name for all but the last path
122 * component. Concatenate the last component and return.
124 returnname = dupcat(canonname,
125 canonname[strlen(canonname) - 1] ==
126 '/' ? "" : "/", fullname + i + 1, NULL);
133 /* ----------------------------------------------------------------------
134 * Actual sftp commands.
136 struct sftp_command {
138 int nwords, wordssize;
139 int (*obey) (struct sftp_command *); /* returns <0 to quit */
142 int sftp_cmd_null(struct sftp_command *cmd)
147 int sftp_cmd_unknown(struct sftp_command *cmd)
149 printf("psftp: unknown command \"%s\"\n", cmd->words[0]);
153 int sftp_cmd_quit(struct sftp_command *cmd)
159 * List a directory. If no arguments are given, list pwd; otherwise
160 * list the directory given in words[1].
162 static int sftp_ls_compare(const void *av, const void *bv)
164 const struct fxp_name *a = (const struct fxp_name *) av;
165 const struct fxp_name *b = (const struct fxp_name *) bv;
166 return strcmp(a->filename, b->filename);
168 int sftp_cmd_ls(struct sftp_command *cmd)
170 struct fxp_handle *dirh;
171 struct fxp_names *names;
172 struct fxp_name *ournames;
173 int nnames, namesize;
182 cdir = canonify(dir);
184 printf("%s: %s\n", dir, fxp_error());
188 printf("Listing directory %s\n", cdir);
190 dirh = fxp_opendir(cdir);
192 printf("Unable to open %s: %s\n", dir, fxp_error());
194 nnames = namesize = 0;
199 names = fxp_readdir(dirh);
201 if (fxp_error_type() == SSH_FX_EOF)
203 printf("Reading directory %s: %s\n", dir, fxp_error());
206 if (names->nnames == 0) {
207 fxp_free_names(names);
211 if (nnames + names->nnames >= namesize) {
212 namesize += names->nnames + 128;
214 srealloc(ournames, namesize * sizeof(*ournames));
217 for (i = 0; i < names->nnames; i++)
218 ournames[nnames++] = names->names[i];
220 names->nnames = 0; /* prevent free_names */
221 fxp_free_names(names);
226 * Now we have our filenames. Sort them by actual file
227 * name, and then output the longname parts.
229 qsort(ournames, nnames, sizeof(*ournames), sftp_ls_compare);
234 for (i = 0; i < nnames; i++)
235 printf("%s\n", ournames[i].longname);
244 * Change directories. We do this by canonifying the new name, then
245 * trying to OPENDIR it. Only if that succeeds do we set the new pwd.
247 int sftp_cmd_cd(struct sftp_command *cmd)
249 struct fxp_handle *dirh;
253 dir = dupstr(homedir);
255 dir = canonify(cmd->words[1]);
258 printf("%s: %s\n", dir, fxp_error());
262 dirh = fxp_opendir(dir);
264 printf("Directory %s: %s\n", dir, fxp_error());
273 printf("Remote directory is now %s\n", pwd);
279 * Get a file and save it at the local end. We have two very
280 * similar commands here: `get' and `reget', which differ in that
281 * `reget' checks for the existence of the destination file and
282 * starts from where a previous aborted transfer left off.
284 int sftp_general_get(struct sftp_command *cmd, int restart)
286 struct fxp_handle *fh;
287 char *fname, *outfname;
291 if (cmd->nwords < 2) {
292 printf("get: expects a filename\n");
296 fname = canonify(cmd->words[1]);
298 printf("%s: %s\n", cmd->words[1], fxp_error());
301 outfname = (cmd->nwords == 2 ? cmd->words[1] : cmd->words[2]);
303 fh = fxp_open(fname, SSH_FXF_READ);
305 printf("%s: %s\n", fname, fxp_error());
311 fp = fopen(outfname, "rb+");
313 fp = fopen(outfname, "wb");
317 printf("local: unable to open %s\n", outfname);
325 fseek(fp, 0L, SEEK_END);
327 printf("reget: restarting at file position %ld\n", posn);
328 offset = uint64_make(0, posn);
330 offset = uint64_make(0, 0);
333 printf("remote:%s => local:%s\n", fname, outfname);
336 * FIXME: we can use FXP_FSTAT here to get the file size, and
337 * thus put up a progress bar.
344 len = fxp_read(fh, buffer, offset, sizeof(buffer));
345 if ((len == -1 && fxp_error_type() == SSH_FX_EOF) || len == 0)
348 printf("error while reading: %s\n", fxp_error());
354 wlen = fwrite(buffer, 1, len - wpos, fp);
356 printf("error while writing local file\n");
361 if (wpos < len) /* we had an error */
363 offset = uint64_add32(offset, len);
372 int sftp_cmd_get(struct sftp_command *cmd)
374 return sftp_general_get(cmd, 0);
376 int sftp_cmd_reget(struct sftp_command *cmd)
378 return sftp_general_get(cmd, 1);
382 * Send a file and store it at the remote end. We have two very
383 * similar commands here: `put' and `reput', which differ in that
384 * `reput' checks for the existence of the destination file and
385 * starts from where a previous aborted transfer left off.
387 int sftp_general_put(struct sftp_command *cmd, int restart)
389 struct fxp_handle *fh;
390 char *fname, *origoutfname, *outfname;
394 if (cmd->nwords < 2) {
395 printf("put: expects a filename\n");
399 fname = cmd->words[1];
400 origoutfname = (cmd->nwords == 2 ? cmd->words[1] : cmd->words[2]);
401 outfname = canonify(origoutfname);
403 printf("%s: %s\n", origoutfname, fxp_error());
407 fp = fopen(fname, "rb");
409 printf("local: unable to open %s\n", fname);
414 fh = fxp_open(outfname,
417 fh = fxp_open(outfname,
418 SSH_FXF_WRITE | SSH_FXF_CREAT | SSH_FXF_TRUNC);
421 printf("%s: %s\n", outfname, fxp_error());
428 struct fxp_attrs attrs;
429 if (!fxp_fstat(fh, &attrs)) {
430 printf("read size of %s: %s\n", outfname, fxp_error());
434 if (!(attrs.flags & SSH_FILEXFER_ATTR_SIZE)) {
435 printf("read size of %s: size was not given\n", outfname);
440 uint64_decimal(offset, decbuf);
441 printf("reput: restarting at file position %s\n", decbuf);
442 if (uint64_compare(offset, uint64_make(0, LONG_MAX)) > 0) {
443 printf("reput: remote file is larger than we can deal with\n");
447 if (fseek(fp, offset.lo, SEEK_SET) != 0)
448 fseek(fp, 0, SEEK_END); /* *shrug* */
450 offset = uint64_make(0, 0);
453 printf("local:%s => remote:%s\n", fname, outfname);
456 * FIXME: we can use FXP_FSTAT here to get the file size, and
457 * thus put up a progress bar.
463 len = fread(buffer, 1, sizeof(buffer), fp);
465 printf("error while reading local file\n");
467 } else if (len == 0) {
470 if (!fxp_write(fh, buffer, offset, len)) {
471 printf("error while writing: %s\n", fxp_error());
474 offset = uint64_add32(offset, len);
483 int sftp_cmd_put(struct sftp_command *cmd)
485 return sftp_general_put(cmd, 0);
487 int sftp_cmd_reput(struct sftp_command *cmd)
489 return sftp_general_put(cmd, 1);
492 int sftp_cmd_mkdir(struct sftp_command *cmd)
498 if (cmd->nwords < 2) {
499 printf("mkdir: expects a directory\n");
503 dir = canonify(cmd->words[1]);
505 printf("%s: %s\n", dir, fxp_error());
509 result = fxp_mkdir(dir);
511 printf("mkdir %s: %s\n", dir, fxp_error());
520 int sftp_cmd_rmdir(struct sftp_command *cmd)
526 if (cmd->nwords < 2) {
527 printf("rmdir: expects a directory\n");
531 dir = canonify(cmd->words[1]);
533 printf("%s: %s\n", dir, fxp_error());
537 result = fxp_rmdir(dir);
539 printf("rmdir %s: %s\n", dir, fxp_error());
548 int sftp_cmd_rm(struct sftp_command *cmd)
553 if (cmd->nwords < 2) {
554 printf("rm: expects a filename\n");
558 fname = canonify(cmd->words[1]);
560 printf("%s: %s\n", fname, fxp_error());
564 result = fxp_remove(fname);
566 printf("rm %s: %s\n", fname, fxp_error());
576 int sftp_cmd_mv(struct sftp_command *cmd)
578 char *srcfname, *dstfname;
581 if (cmd->nwords < 3) {
582 printf("mv: expects two filenames\n");
585 srcfname = canonify(cmd->words[1]);
587 printf("%s: %s\n", srcfname, fxp_error());
591 dstfname = canonify(cmd->words[2]);
593 printf("%s: %s\n", dstfname, fxp_error());
597 result = fxp_rename(srcfname, dstfname);
599 char const *error = fxp_error();
600 struct fxp_attrs attrs;
603 * The move might have failed because dstfname pointed at a
604 * directory. We check this possibility now: if dstfname
605 * _is_ a directory, we re-attempt the move by appending
606 * the basename of srcfname to dstfname.
608 result = fxp_stat(dstfname, &attrs);
610 (attrs.flags & SSH_FILEXFER_ATTR_PERMISSIONS) &&
611 (attrs.permissions & 0040000)) {
613 char *newname, *newcanon;
614 printf("(destination %s is a directory)\n", dstfname);
615 p = srcfname + strlen(srcfname);
616 while (p > srcfname && p[-1] != '/') p--;
617 newname = dupcat(dstfname, "/", p, NULL);
618 newcanon = canonify(newname);
623 result = fxp_rename(srcfname, dstfname);
624 error = result ? NULL : fxp_error();
628 printf("mv %s %s: %s\n", srcfname, dstfname, error);
634 printf("%s -> %s\n", srcfname, dstfname);
641 int sftp_cmd_chmod(struct sftp_command *cmd)
645 struct fxp_attrs attrs;
646 unsigned attrs_clr, attrs_xor, oldperms, newperms;
648 if (cmd->nwords < 3) {
649 printf("chmod: expects a mode specifier and a filename\n");
654 * Attempt to parse the mode specifier in cmd->words[1]. We
655 * don't support the full horror of Unix chmod; instead we
656 * support a much simpler syntax in which the user can either
657 * specify an octal number, or a comma-separated sequence of
658 * [ugoa]*[-+=][rwxst]+. (The initial [ugoa] sequence may
659 * _only_ be omitted if the only attribute mentioned is t,
660 * since all others require a user/group/other specification.
661 * Additionally, the s attribute may not be specified for any
662 * [ugoa] specifications other than exactly u or exactly g.
664 attrs_clr = attrs_xor = 0;
665 mode = cmd->words[1];
666 if (mode[0] >= '0' && mode[0] <= '9') {
667 if (mode[strspn(mode, "01234567")]) {
668 printf("chmod: numeric file modes should"
669 " contain digits 0-7 only\n");
673 sscanf(mode, "%o", &attrs_xor);
674 attrs_xor &= attrs_clr;
677 char *modebegin = mode;
678 unsigned subset, perms;
682 while (*mode && *mode != ',' &&
683 *mode != '+' && *mode != '-' && *mode != '=') {
685 case 'u': subset |= 04700; break; /* setuid, user perms */
686 case 'g': subset |= 02070; break; /* setgid, group perms */
687 case 'o': subset |= 00007; break; /* just other perms */
688 case 'a': subset |= 06777; break; /* all of the above */
690 printf("chmod: file mode '%.*s' contains unrecognised"
691 " user/group/other specifier '%c'\n",
692 strcspn(modebegin, ","), modebegin, *mode);
697 if (!*mode || *mode == ',') {
698 printf("chmod: file mode '%.*s' is incomplete\n",
699 strcspn(modebegin, ","), modebegin);
703 if (!*mode || *mode == ',') {
704 printf("chmod: file mode '%.*s' is incomplete\n",
705 strcspn(modebegin, ","), modebegin);
709 while (*mode && *mode != ',') {
711 case 'r': perms |= 00444; break;
712 case 'w': perms |= 00222; break;
713 case 'x': perms |= 00111; break;
714 case 't': perms |= 01000; subset |= 01000; break;
716 if ((subset & 06777) != 04700 &&
717 (subset & 06777) != 02070) {
718 printf("chmod: file mode '%.*s': set[ug]id bit should"
719 " be used with exactly one of u or g only\n",
720 strcspn(modebegin, ","), modebegin);
726 printf("chmod: file mode '%.*s' contains unrecognised"
727 " permission specifier '%c'\n",
728 strcspn(modebegin, ","), modebegin, *mode);
733 if (!(subset & 06777) && (perms &~ subset)) {
734 printf("chmod: file mode '%.*s' contains no user/group/other"
735 " specifier and permissions other than 't' \n",
736 strcspn(modebegin, ","), modebegin, *mode);
754 if (*mode) mode++; /* eat comma */
758 fname = canonify(cmd->words[2]);
760 printf("%s: %s\n", fname, fxp_error());
764 result = fxp_stat(fname, &attrs);
765 if (!result || !(attrs.flags & SSH_FILEXFER_ATTR_PERMISSIONS)) {
766 printf("get attrs for %s: %s\n", fname,
767 result ? "file permissions not provided" : fxp_error());
772 attrs.flags = SSH_FILEXFER_ATTR_PERMISSIONS; /* perms _only_ */
773 oldperms = attrs.permissions & 07777;
774 attrs.permissions &= ~attrs_clr;
775 attrs.permissions ^= attrs_xor;
776 newperms = attrs.permissions & 07777;
778 result = fxp_setstat(fname, attrs);
781 printf("set attrs for %s: %s\n", fname, fxp_error());
786 printf("%s: %04o -> %04o\n", fname, oldperms, newperms);
792 static struct sftp_cmd_lookup {
794 int (*obey) (struct sftp_command *);
797 * List of sftp commands. This is binary-searched so it MUST be
801 "bye", sftp_cmd_quit}, {
802 "cd", sftp_cmd_cd}, {
803 "chmod", sftp_cmd_chmod}, {
804 "del", sftp_cmd_rm}, {
805 "delete", sftp_cmd_rm}, {
806 "dir", sftp_cmd_ls}, {
807 "exit", sftp_cmd_quit}, {
808 "get", sftp_cmd_get}, {
809 "ls", sftp_cmd_ls}, {
810 "mkdir", sftp_cmd_mkdir}, {
811 "mv", sftp_cmd_mv}, {
812 "put", sftp_cmd_put}, {
813 "quit", sftp_cmd_quit}, {
814 "reget", sftp_cmd_reget}, {
815 "ren", sftp_cmd_mv}, {
816 "rename", sftp_cmd_mv}, {
817 "reput", sftp_cmd_reput}, {
818 "rm", sftp_cmd_rm}, {
819 "rmdir", sftp_cmd_rmdir},};
821 /* ----------------------------------------------------------------------
822 * Command line reading and parsing.
824 struct sftp_command *sftp_getcmd(FILE *fp, int mode, int modeflags)
827 int linelen, linesize;
828 struct sftp_command *cmd;
832 if ((mode == 0) || (modeflags & 1)) {
837 cmd = smalloc(sizeof(struct sftp_command));
843 linesize = linelen = 0;
849 line = srealloc(line, linesize);
850 ret = fgets(line + linelen, linesize - linelen, fp);
855 if (!ret || (linelen == 0 && line[0] == '\0')) {
856 cmd->obey = sftp_cmd_quit;
858 return cmd; /* eof */
860 len = linelen + strlen(line + linelen);
862 if (line[linelen - 1] == '\n') {
864 line[linelen] = '\0';
870 * Parse the command line into words. The syntax is:
871 * - double quotes are removed, but cause spaces within to be
872 * treated as non-separating.
873 * - a double-doublequote pair is a literal double quote, inside
874 * _or_ outside quotes. Like this:
876 * firstword "second word" "this has ""quotes"" in" sodoes""this""
882 * >this has "quotes" in<
887 /* skip whitespace */
888 while (*p && (*p == ' ' || *p == '\t'))
890 /* mark start of word */
891 q = r = p; /* q sits at start, r writes word */
894 if (!quoting && (*p == ' ' || *p == '\t'))
895 break; /* reached end of word */
896 else if (*p == '"' && p[1] == '"')
897 p += 2, *r++ = '"'; /* a literal quote */
899 p++, quoting = !quoting;
904 p++; /* skip over the whitespace */
906 if (cmd->nwords >= cmd->wordssize) {
907 cmd->wordssize = cmd->nwords + 16;
909 srealloc(cmd->words, cmd->wordssize * sizeof(char *));
911 cmd->words[cmd->nwords++] = q;
915 * Now parse the first word and assign a function.
918 if (cmd->nwords == 0)
919 cmd->obey = sftp_cmd_null;
923 cmd->obey = sftp_cmd_unknown;
926 j = sizeof(sftp_lookup) / sizeof(*sftp_lookup);
929 cmp = strcmp(cmd->words[0], sftp_lookup[k].name);
935 cmd->obey = sftp_lookup[k].obey;
944 void do_sftp(int mode, int modeflags, char *batchfile)
949 * Do protocol initialisation.
953 "Fatal: unable to initialise SFTP: %s\n", fxp_error());
958 * Find out where our home directory is.
960 homedir = fxp_realpath(".");
963 "Warning: failed to resolve home directory: %s\n",
965 homedir = dupstr(".");
967 printf("Remote working directory is %s\n", homedir);
969 pwd = dupstr(homedir);
976 /* ------------------------------------------------------------------
977 * Now we're ready to do Real Stuff.
980 struct sftp_command *cmd;
981 cmd = sftp_getcmd(stdin, 0, 0);
984 if (cmd->obey(cmd) < 0)
988 fp = fopen(batchfile, "r");
990 printf("Fatal: unable to open %s\n", batchfile);
994 struct sftp_command *cmd;
995 cmd = sftp_getcmd(fp, mode, modeflags);
998 if (cmd->obey(cmd) < 0)
1000 if (fxp_error() != NULL) {
1001 if (!(modeflags & 2))
1010 /* ----------------------------------------------------------------------
1011 * Dirty bits: integration with PuTTY.
1014 static int verbose = 0;
1016 void verify_ssh_host_key(char *host, int port, char *keytype,
1017 char *keystr, char *fingerprint)
1023 static const char absentmsg[] =
1024 "The server's host key is not cached in the registry. You\n"
1025 "have no guarantee that the server is the computer you\n"
1027 "The server's key fingerprint is:\n"
1029 "If you trust this host, enter \"y\" to add the key to\n"
1030 "PuTTY's cache and carry on connecting.\n"
1031 "If you want to carry on connecting just once, without\n"
1032 "adding the key to the cache, enter \"n\".\n"
1033 "If you do not trust this host, press Return to abandon the\n"
1035 "Store key in cache? (y/n) ";
1037 static const char wrongmsg[] =
1038 "WARNING - POTENTIAL SECURITY BREACH!\n"
1039 "The server's host key does not match the one PuTTY has\n"
1040 "cached in the registry. This means that either the\n"
1041 "server administrator has changed the host key, or you\n"
1042 "have actually connected to another computer pretending\n"
1043 "to be the server.\n"
1044 "The new key fingerprint is:\n"
1046 "If you were expecting this change and trust the new key,\n"
1047 "enter \"y\" to update PuTTY's cache and continue connecting.\n"
1048 "If you want to carry on connecting but without updating\n"
1049 "the cache, enter \"n\".\n"
1050 "If you want to abandon the connection completely, press\n"
1051 "Return to cancel. Pressing Return is the ONLY guaranteed\n"
1053 "Update cached key? (y/n, Return cancels connection) ";
1055 static const char abandoned[] = "Connection abandoned.\n";
1060 * Verify the key against the registry.
1062 ret = verify_host_key(host, port, keytype, keystr);
1064 if (ret == 0) /* success - key matched OK */
1067 if (ret == 2) { /* key was different */
1068 fprintf(stderr, wrongmsg, fingerprint);
1071 if (ret == 1) { /* key was absent */
1072 fprintf(stderr, absentmsg, fingerprint);
1076 hin = GetStdHandle(STD_INPUT_HANDLE);
1077 GetConsoleMode(hin, &savemode);
1078 SetConsoleMode(hin, (savemode | ENABLE_ECHO_INPUT |
1079 ENABLE_PROCESSED_INPUT | ENABLE_LINE_INPUT));
1080 ReadFile(hin, line, sizeof(line) - 1, &i, NULL);
1081 SetConsoleMode(hin, savemode);
1083 if (line[0] != '\0' && line[0] != '\r' && line[0] != '\n') {
1084 if (line[0] == 'y' || line[0] == 'Y')
1085 store_host_key(host, port, keytype, keystr);
1087 fprintf(stderr, abandoned);
1093 * Ask whether the selected cipher is acceptable (since it was
1094 * below the configured 'warn' threshold).
1095 * cs: 0 = both ways, 1 = client->server, 2 = server->client
1097 void askcipher(char *ciphername, int cs)
1102 static const char msg[] =
1103 "The first %scipher supported by the server is\n"
1104 "%s, which is below the configured warning threshold.\n"
1105 "Continue with connection? (y/n) ";
1106 static const char abandoned[] = "Connection abandoned.\n";
1110 fprintf(stderr, msg,
1112 (cs == 1) ? "client-to-server " :
1113 "server-to-client ",
1117 hin = GetStdHandle(STD_INPUT_HANDLE);
1118 GetConsoleMode(hin, &savemode);
1119 SetConsoleMode(hin, (savemode | ENABLE_ECHO_INPUT |
1120 ENABLE_PROCESSED_INPUT | ENABLE_LINE_INPUT));
1121 ReadFile(hin, line, sizeof(line) - 1, &i, NULL);
1122 SetConsoleMode(hin, savemode);
1124 if (line[0] == 'y' || line[0] == 'Y') {
1127 fprintf(stderr, abandoned);
1133 * Print an error message and perform a fatal exit.
1135 void fatalbox(char *fmt, ...)
1137 char str[0x100]; /* Make the size big enough */
1140 strcpy(str, "Fatal:");
1141 vsprintf(str + strlen(str), fmt, ap);
1144 fprintf(stderr, str);
1148 void connection_fatal(char *fmt, ...)
1150 char str[0x100]; /* Make the size big enough */
1153 strcpy(str, "Fatal:");
1154 vsprintf(str + strlen(str), fmt, ap);
1157 fprintf(stderr, str);
1162 void logevent(char *string)
1166 void ldisc_send(char *buf, int len)
1169 * This is only here because of the calls to ldisc_send(NULL,
1170 * 0) in ssh.c. Nothing in PSFTP actually needs to use the
1171 * ldisc as an ldisc. So if we get called with any real data, I
1172 * want to know about it.
1178 * Be told what socket we're supposed to be using.
1180 static SOCKET sftp_ssh_socket;
1181 char *do_select(SOCKET skt, int startup)
1184 sftp_ssh_socket = skt;
1186 sftp_ssh_socket = INVALID_SOCKET;
1189 extern int select_result(WPARAM, LPARAM);
1192 * Receive a block of data from the SSH link. Block until all data
1195 * To do this, we repeatedly call the SSH protocol module, with our
1196 * own trap in from_backend() to catch the data that comes back. We
1197 * do this until we have enough data.
1200 static unsigned char *outptr; /* where to put the data */
1201 static unsigned outlen; /* how much data required */
1202 static unsigned char *pending = NULL; /* any spare data */
1203 static unsigned pendlen = 0, pendsize = 0; /* length and phys. size of buffer */
1204 int from_backend(int is_stderr, char *data, int datalen)
1206 unsigned char *p = (unsigned char *) data;
1207 unsigned len = (unsigned) datalen;
1210 * stderr data is just spouted to local stderr and otherwise
1214 fwrite(data, 1, len, stderr);
1219 * If this is before the real session begins, just return.
1225 unsigned used = outlen;
1228 memcpy(outptr, p, used);
1236 if (pendsize < pendlen + len) {
1237 pendsize = pendlen + len + 4096;
1238 pending = (pending ? srealloc(pending, pendsize) :
1241 fatalbox("Out of memory");
1243 memcpy(pending + pendlen, p, len);
1249 int sftp_recvdata(char *buf, int len)
1251 outptr = (unsigned char *) buf;
1255 * See if the pending-input block contains some of what we
1259 unsigned pendused = pendlen;
1260 if (pendused > outlen)
1262 memcpy(outptr, pending, pendused);
1263 memmove(pending, pending + pendused, pendlen - pendused);
1266 pendlen -= pendused;
1276 while (outlen > 0) {
1280 FD_SET(sftp_ssh_socket, &readfds);
1281 if (select(1, &readfds, NULL, NULL, NULL) < 0)
1282 return 0; /* doom */
1283 select_result((WPARAM) sftp_ssh_socket, (LPARAM) FD_READ);
1288 int sftp_senddata(char *buf, int len)
1290 back->send((unsigned char *) buf, len);
1295 * Loop through the ssh connection and authentication process.
1297 static void ssh_sftp_init(void)
1299 if (sftp_ssh_socket == INVALID_SOCKET)
1301 while (!back->sendok()) {
1304 FD_SET(sftp_ssh_socket, &readfds);
1305 if (select(1, &readfds, NULL, NULL, NULL) < 0)
1307 select_result((WPARAM) sftp_ssh_socket, (LPARAM) FD_READ);
1311 static char *password = NULL;
1312 static int get_line(const char *prompt, char *str, int maxlen, int is_pw)
1315 DWORD savemode, newmode, i;
1318 static int tried_once = 0;
1323 strncpy(str, password, maxlen);
1324 str[maxlen - 1] = '\0';
1330 hin = GetStdHandle(STD_INPUT_HANDLE);
1331 hout = GetStdHandle(STD_OUTPUT_HANDLE);
1332 if (hin == INVALID_HANDLE_VALUE || hout == INVALID_HANDLE_VALUE) {
1333 fprintf(stderr, "Cannot get standard input/output handles\n");
1337 GetConsoleMode(hin, &savemode);
1338 newmode = savemode | ENABLE_PROCESSED_INPUT | ENABLE_LINE_INPUT;
1340 newmode &= ~ENABLE_ECHO_INPUT;
1342 newmode |= ENABLE_ECHO_INPUT;
1343 SetConsoleMode(hin, newmode);
1345 WriteFile(hout, prompt, strlen(prompt), &i, NULL);
1346 ReadFile(hin, str, maxlen - 1, &i, NULL);
1348 SetConsoleMode(hin, savemode);
1350 if ((int) i > maxlen)
1357 WriteFile(hout, "\r\n", 2, &i, NULL);
1363 * Initialize the Win$ock driver.
1365 static void init_winsock(void)
1370 winsock_ver = MAKEWORD(1, 1);
1371 if (WSAStartup(winsock_ver, &wsadata)) {
1372 fprintf(stderr, "Unable to initialise WinSock");
1375 if (LOBYTE(wsadata.wVersion) != 1 || HIBYTE(wsadata.wVersion) != 1) {
1376 fprintf(stderr, "WinSock version is incompatible with 1.1");
1382 * Short description of parameters.
1384 static void usage(void)
1386 printf("PuTTY Secure File Transfer (SFTP) client\n");
1387 printf("%s\n", ver);
1388 printf("Usage: psftp [options] user@host\n");
1389 printf("Options:\n");
1390 printf(" -b file use specified batchfile\n");
1391 printf(" -bc output batchfile commands\n");
1392 printf(" -be don't stop batchfile processing if errors\n");
1393 printf(" -v show verbose messages\n");
1394 printf(" -P port connect to specified port\n");
1395 printf(" -pw passw login with specified password\n");
1400 * Main program. Parse arguments etc.
1402 int main(int argc, char *argv[])
1406 char *user, *host, *userhost, *realhost;
1410 char *batchfile = NULL;
1412 flags = FLAG_STDERR;
1413 ssh_get_line = &get_line;
1417 userhost = user = NULL;
1419 for (i = 1; i < argc; i++) {
1420 if (argv[i][0] != '-') {
1424 userhost = dupstr(argv[i]);
1425 } else if (strcmp(argv[i], "-v") == 0) {
1426 verbose = 1, flags |= FLAG_VERBOSE;
1427 } else if (strcmp(argv[i], "-h") == 0 ||
1428 strcmp(argv[i], "-?") == 0) {
1430 } else if (strcmp(argv[i], "-l") == 0 && i + 1 < argc) {
1432 } else if (strcmp(argv[i], "-P") == 0 && i + 1 < argc) {
1433 portnumber = atoi(argv[++i]);
1434 } else if (strcmp(argv[i], "-pw") == 0 && i + 1 < argc) {
1435 password = argv[++i];
1436 } else if (strcmp(argv[i], "-b") == 0 && i + 1 < argc) {
1438 batchfile = argv[++i];
1439 } else if (strcmp(argv[i], "-bc") == 0 && i + 1 < argc) {
1440 modeflags = modeflags | 1;
1441 } else if (strcmp(argv[i], "-be") == 0 && i + 1 < argc) {
1442 modeflags = modeflags | 2;
1443 } else if (strcmp(argv[i], "--") == 0) {
1454 if (argc > 0 || !userhost)
1457 /* Separate host and username */
1459 host = strrchr(host, '@');
1465 printf("psftp: multiple usernames specified; using \"%s\"\n",
1471 /* Try to load settings for this host */
1472 do_defaults(host, &cfg);
1473 if (cfg.host[0] == '\0') {
1474 /* No settings for this host; use defaults */
1475 do_defaults(NULL, &cfg);
1476 strncpy(cfg.host, host, sizeof(cfg.host) - 1);
1477 cfg.host[sizeof(cfg.host) - 1] = '\0';
1482 if (user != NULL && user[0] != '\0') {
1483 strncpy(cfg.username, user, sizeof(cfg.username) - 1);
1484 cfg.username[sizeof(cfg.username) - 1] = '\0';
1486 if (!cfg.username[0]) {
1487 printf("login as: ");
1488 if (!fgets(cfg.username, sizeof(cfg.username), stdin)) {
1489 fprintf(stderr, "psftp: aborting\n");
1492 int len = strlen(cfg.username);
1493 if (cfg.username[len - 1] == '\n')
1494 cfg.username[len - 1] = '\0';
1498 if (cfg.protocol != PROT_SSH)
1502 cfg.port = portnumber;
1504 /* SFTP uses SSH2 by default always */
1507 /* Set up subsystem name. FIXME: fudge for SSH1. */
1508 strcpy(cfg.remote_cmd, "sftp");
1509 cfg.ssh_subsys = TRUE;
1512 back = &ssh_backend;
1514 err = back->init(cfg.host, cfg.port, &realhost);
1516 fprintf(stderr, "ssh_init: %s", err);
1520 if (verbose && realhost != NULL)
1521 printf("Connected to %s\n", realhost);
1523 do_sftp(mode, modeflags, batchfile);
1525 if (back != NULL && back->socket() != NULL) {
1527 back->special(TS_EOF);
1528 sftp_recvdata(&ch, 1);