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 /* ----------------------------------------------------------------------
28 * String handling routines.
34 char *p = smalloc(len + 1);
39 /* Allocate the concatenation of N strings. Terminate arg list with NULL. */
40 char *dupcat(char *s1, ...)
49 sn = va_arg(ap, char *);
62 sn = va_arg(ap, char *);
73 /* ----------------------------------------------------------------------
79 /* ----------------------------------------------------------------------
80 * Higher-level helper functions used in commands.
84 * Attempt to canonify a pathname starting from the pwd. If
85 * canonification fails, at least fall back to returning a _valid_
86 * pathname (though it may be ugly, eg /home/simon/../foobar).
88 char *canonify(char *name)
90 char *fullname, *canonname;
93 fullname = dupstr(name);
96 if (pwd[strlen(pwd) - 1] == '/')
100 fullname = dupcat(pwd, slash, name, NULL);
103 canonname = fxp_realpath(fullname);
110 * Attempt number 2. Some FXP_REALPATH implementations
111 * (glibc-based ones, in particular) require the _whole_
112 * path to point to something that exists, whereas others
113 * (BSD-based) only require all but the last component to
114 * exist. So if the first call failed, we should strip off
115 * everything from the last slash onwards and try again,
116 * then put the final component back on.
120 * - if the last component is "/." or "/..", then we don't
121 * bother trying this because there's no way it can work.
123 * - if the thing actually ends with a "/", we remove it
124 * before we start. Except if the string is "/" itself
125 * (although I can't see why we'd have got here if so,
126 * because surely "/" would have worked the first
127 * time?), in which case we don't bother.
129 * - if there's no slash in the string at all, give up in
130 * confusion (we expect at least one because of the way
131 * we constructed the string).
137 i = strlen(fullname);
138 if (i > 2 && fullname[i - 1] == '/')
139 fullname[--i] = '\0'; /* strip trailing / unless at pos 0 */
140 while (i > 0 && fullname[--i] != '/');
143 * Give up on special cases.
145 if (fullname[i] != '/' || /* no slash at all */
146 !strcmp(fullname + i, "/.") || /* ends in /. */
147 !strcmp(fullname + i, "/..") || /* ends in /.. */
148 !strcmp(fullname, "/")) {
153 * Now i points at the slash. Deal with the final special
154 * case i==0 (ie the whole path was "/nonexistentfile").
156 fullname[i] = '\0'; /* separate the string */
158 canonname = fxp_realpath("/");
160 canonname = fxp_realpath(fullname);
164 return fullname; /* even that failed; give up */
167 * We have a canonical name for all but the last path
168 * component. Concatenate the last component and return.
170 returnname = dupcat(canonname,
171 canonname[strlen(canonname) - 1] ==
172 '/' ? "" : "/", fullname + i + 1, NULL);
179 /* ----------------------------------------------------------------------
180 * Actual sftp commands.
182 struct sftp_command {
184 int nwords, wordssize;
185 int (*obey) (struct sftp_command *); /* returns <0 to quit */
188 int sftp_cmd_null(struct sftp_command *cmd)
193 int sftp_cmd_unknown(struct sftp_command *cmd)
195 printf("psftp: unknown command \"%s\"\n", cmd->words[0]);
199 int sftp_cmd_quit(struct sftp_command *cmd)
205 * List a directory. If no arguments are given, list pwd; otherwise
206 * list the directory given in words[1].
208 static int sftp_ls_compare(const void *av, const void *bv)
210 const struct fxp_name *a = (const struct fxp_name *) av;
211 const struct fxp_name *b = (const struct fxp_name *) bv;
212 return strcmp(a->filename, b->filename);
214 int sftp_cmd_ls(struct sftp_command *cmd)
216 struct fxp_handle *dirh;
217 struct fxp_names *names;
218 struct fxp_name *ournames;
219 int nnames, namesize;
228 cdir = canonify(dir);
230 printf("%s: %s\n", dir, fxp_error());
234 printf("Listing directory %s\n", cdir);
236 dirh = fxp_opendir(cdir);
238 printf("Unable to open %s: %s\n", dir, fxp_error());
240 nnames = namesize = 0;
245 names = fxp_readdir(dirh);
247 if (fxp_error_type() == SSH_FX_EOF)
249 printf("Reading directory %s: %s\n", dir, fxp_error());
252 if (names->nnames == 0) {
253 fxp_free_names(names);
257 if (nnames + names->nnames >= namesize) {
258 namesize += names->nnames + 128;
260 srealloc(ournames, namesize * sizeof(*ournames));
263 for (i = 0; i < names->nnames; i++)
264 ournames[nnames++] = names->names[i];
266 names->nnames = 0; /* prevent free_names */
267 fxp_free_names(names);
272 * Now we have our filenames. Sort them by actual file
273 * name, and then output the longname parts.
275 qsort(ournames, nnames, sizeof(*ournames), sftp_ls_compare);
280 for (i = 0; i < nnames; i++)
281 printf("%s\n", ournames[i].longname);
290 * Change directories. We do this by canonifying the new name, then
291 * trying to OPENDIR it. Only if that succeeds do we set the new pwd.
293 int sftp_cmd_cd(struct sftp_command *cmd)
295 struct fxp_handle *dirh;
299 dir = dupstr(homedir);
301 dir = canonify(cmd->words[1]);
304 printf("%s: %s\n", dir, fxp_error());
308 dirh = fxp_opendir(dir);
310 printf("Directory %s: %s\n", dir, fxp_error());
319 printf("Remote directory is now %s\n", pwd);
325 * Get a file and save it at the local end. We have two very
326 * similar commands here: `get' and `reget', which differ in that
327 * `reget' checks for the existence of the destination file and
328 * starts from where a previous aborted transfer left off.
330 int sftp_general_get(struct sftp_command *cmd, int restart)
332 struct fxp_handle *fh;
333 char *fname, *outfname;
337 if (cmd->nwords < 2) {
338 printf("get: expects a filename\n");
342 fname = canonify(cmd->words[1]);
344 printf("%s: %s\n", cmd->words[1], fxp_error());
347 outfname = (cmd->nwords == 2 ? cmd->words[1] : cmd->words[2]);
349 fh = fxp_open(fname, SSH_FXF_READ);
351 printf("%s: %s\n", fname, fxp_error());
357 fp = fopen(outfname, "rb+");
359 fp = fopen(outfname, "wb");
363 printf("local: unable to open %s\n", outfname);
371 fseek(fp, 0L, SEEK_END);
373 printf("reget: restarting at file position %ld\n", posn);
374 offset = uint64_make(0, posn);
376 offset = uint64_make(0, 0);
379 printf("remote:%s => local:%s\n", fname, outfname);
382 * FIXME: we can use FXP_FSTAT here to get the file size, and
383 * thus put up a progress bar.
390 len = fxp_read(fh, buffer, offset, sizeof(buffer));
391 if ((len == -1 && fxp_error_type() == SSH_FX_EOF) || len == 0)
394 printf("error while reading: %s\n", fxp_error());
400 wlen = fwrite(buffer, 1, len - wpos, fp);
402 printf("error while writing local file\n");
407 if (wpos < len) /* we had an error */
409 offset = uint64_add32(offset, len);
418 int sftp_cmd_get(struct sftp_command *cmd)
420 return sftp_general_get(cmd, 0);
422 int sftp_cmd_reget(struct sftp_command *cmd)
424 return sftp_general_get(cmd, 1);
428 * Send a file and store it at the remote end. We have two very
429 * similar commands here: `put' and `reput', which differ in that
430 * `reput' checks for the existence of the destination file and
431 * starts from where a previous aborted transfer left off.
433 int sftp_general_put(struct sftp_command *cmd, int restart)
435 struct fxp_handle *fh;
436 char *fname, *origoutfname, *outfname;
440 if (cmd->nwords < 2) {
441 printf("put: expects a filename\n");
445 fname = cmd->words[1];
446 origoutfname = (cmd->nwords == 2 ? cmd->words[1] : cmd->words[2]);
447 outfname = canonify(origoutfname);
449 printf("%s: %s\n", origoutfname, fxp_error());
453 fp = fopen(fname, "rb");
455 printf("local: unable to open %s\n", fname);
460 fh = fxp_open(outfname,
463 fh = fxp_open(outfname,
464 SSH_FXF_WRITE | SSH_FXF_CREAT | SSH_FXF_TRUNC);
467 printf("%s: %s\n", outfname, fxp_error());
474 struct fxp_attrs attrs;
475 if (!fxp_fstat(fh, &attrs)) {
476 printf("read size of %s: %s\n", outfname, fxp_error());
480 if (!(attrs.flags & SSH_FILEXFER_ATTR_SIZE)) {
481 printf("read size of %s: size was not given\n", outfname);
486 uint64_decimal(offset, decbuf);
487 printf("reput: restarting at file position %s\n", decbuf);
488 if (uint64_compare(offset, uint64_make(0, LONG_MAX)) > 0) {
489 printf("reput: remote file is larger than we can deal with\n");
493 if (fseek(fp, offset.lo, SEEK_SET) != 0)
494 fseek(fp, 0, SEEK_END); /* *shrug* */
496 offset = uint64_make(0, 0);
499 printf("local:%s => remote:%s\n", fname, outfname);
502 * FIXME: we can use FXP_FSTAT here to get the file size, and
503 * thus put up a progress bar.
509 len = fread(buffer, 1, sizeof(buffer), fp);
511 printf("error while reading local file\n");
513 } else if (len == 0) {
516 if (!fxp_write(fh, buffer, offset, len)) {
517 printf("error while writing: %s\n", fxp_error());
520 offset = uint64_add32(offset, len);
529 int sftp_cmd_put(struct sftp_command *cmd)
531 return sftp_general_put(cmd, 0);
533 int sftp_cmd_reput(struct sftp_command *cmd)
535 return sftp_general_put(cmd, 1);
538 int sftp_cmd_mkdir(struct sftp_command *cmd)
544 if (cmd->nwords < 2) {
545 printf("mkdir: expects a directory\n");
549 dir = canonify(cmd->words[1]);
551 printf("%s: %s\n", dir, fxp_error());
555 result = fxp_mkdir(dir);
557 printf("mkdir %s: %s\n", dir, fxp_error());
566 int sftp_cmd_rmdir(struct sftp_command *cmd)
572 if (cmd->nwords < 2) {
573 printf("rmdir: expects a directory\n");
577 dir = canonify(cmd->words[1]);
579 printf("%s: %s\n", dir, fxp_error());
583 result = fxp_rmdir(dir);
585 printf("rmdir %s: %s\n", dir, fxp_error());
594 int sftp_cmd_rm(struct sftp_command *cmd)
599 if (cmd->nwords < 2) {
600 printf("rm: expects a filename\n");
604 fname = canonify(cmd->words[1]);
606 printf("%s: %s\n", fname, fxp_error());
610 result = fxp_remove(fname);
612 printf("rm %s: %s\n", fname, fxp_error());
622 int sftp_cmd_mv(struct sftp_command *cmd)
624 char *srcfname, *dstfname;
627 if (cmd->nwords < 3) {
628 printf("mv: expects two filenames\n");
631 srcfname = canonify(cmd->words[1]);
633 printf("%s: %s\n", srcfname, fxp_error());
637 dstfname = canonify(cmd->words[2]);
639 printf("%s: %s\n", dstfname, fxp_error());
643 result = fxp_rename(srcfname, dstfname);
645 char const *error = fxp_error();
646 struct fxp_attrs attrs;
649 * The move might have failed because dstfname pointed at a
650 * directory. We check this possibility now: if dstfname
651 * _is_ a directory, we re-attempt the move by appending
652 * the basename of srcfname to dstfname.
654 result = fxp_stat(dstfname, &attrs);
656 (attrs.flags & SSH_FILEXFER_ATTR_PERMISSIONS) &&
657 (attrs.permissions & 0040000)) {
659 char *newname, *newcanon;
660 printf("(destination %s is a directory)\n", dstfname);
661 p = srcfname + strlen(srcfname);
662 while (p > srcfname && p[-1] != '/') p--;
663 newname = dupcat(dstfname, "/", p, NULL);
664 newcanon = canonify(newname);
669 result = fxp_rename(srcfname, dstfname);
670 error = result ? NULL : fxp_error();
674 printf("mv %s %s: %s\n", srcfname, dstfname, error);
680 printf("%s -> %s\n", srcfname, dstfname);
687 int sftp_cmd_chmod(struct sftp_command *cmd)
691 struct fxp_attrs attrs;
692 unsigned attrs_clr, attrs_xor, oldperms, newperms;
694 if (cmd->nwords < 3) {
695 printf("chmod: expects a mode specifier and a filename\n");
700 * Attempt to parse the mode specifier in cmd->words[1]. We
701 * don't support the full horror of Unix chmod; instead we
702 * support a much simpler syntax in which the user can either
703 * specify an octal number, or a comma-separated sequence of
704 * [ugoa]*[-+=][rwxst]+. (The initial [ugoa] sequence may
705 * _only_ be omitted if the only attribute mentioned is t,
706 * since all others require a user/group/other specification.
707 * Additionally, the s attribute may not be specified for any
708 * [ugoa] specifications other than exactly u or exactly g.
710 attrs_clr = attrs_xor = 0;
711 mode = cmd->words[1];
712 if (mode[0] >= '0' && mode[0] <= '9') {
713 if (mode[strspn(mode, "01234567")]) {
714 printf("chmod: numeric file modes should"
715 " contain digits 0-7 only\n");
719 sscanf(mode, "%o", &attrs_xor);
720 attrs_xor &= attrs_clr;
723 char *modebegin = mode;
724 unsigned subset, perms;
728 while (*mode && *mode != ',' &&
729 *mode != '+' && *mode != '-' && *mode != '=') {
731 case 'u': subset |= 04700; break; /* setuid, user perms */
732 case 'g': subset |= 02070; break; /* setgid, group perms */
733 case 'o': subset |= 00007; break; /* just other perms */
734 case 'a': subset |= 06777; break; /* all of the above */
736 printf("chmod: file mode '%.*s' contains unrecognised"
737 " user/group/other specifier '%c'\n",
738 strcspn(modebegin, ","), modebegin, *mode);
743 if (!*mode || *mode == ',') {
744 printf("chmod: file mode '%.*s' is incomplete\n",
745 strcspn(modebegin, ","), modebegin);
749 if (!*mode || *mode == ',') {
750 printf("chmod: file mode '%.*s' is incomplete\n",
751 strcspn(modebegin, ","), modebegin);
755 while (*mode && *mode != ',') {
757 case 'r': perms |= 00444; break;
758 case 'w': perms |= 00222; break;
759 case 'x': perms |= 00111; break;
760 case 't': perms |= 01000; subset |= 01000; break;
762 if ((subset & 06777) != 04700 &&
763 (subset & 06777) != 02070) {
764 printf("chmod: file mode '%.*s': set[ug]id bit should"
765 " be used with exactly one of u or g only\n",
766 strcspn(modebegin, ","), modebegin);
772 printf("chmod: file mode '%.*s' contains unrecognised"
773 " permission specifier '%c'\n",
774 strcspn(modebegin, ","), modebegin, *mode);
779 if (!(subset & 06777) && (perms &~ subset)) {
780 printf("chmod: file mode '%.*s' contains no user/group/other"
781 " specifier and permissions other than 't' \n",
782 strcspn(modebegin, ","), modebegin, *mode);
800 if (*mode) mode++; /* eat comma */
804 fname = canonify(cmd->words[2]);
806 printf("%s: %s\n", fname, fxp_error());
810 result = fxp_stat(fname, &attrs);
811 if (!result || !(attrs.flags & SSH_FILEXFER_ATTR_PERMISSIONS)) {
812 printf("get attrs for %s: %s\n", fname,
813 result ? "file permissions not provided" : fxp_error());
818 attrs.flags = SSH_FILEXFER_ATTR_PERMISSIONS; /* perms _only_ */
819 oldperms = attrs.permissions & 07777;
820 attrs.permissions &= ~attrs_clr;
821 attrs.permissions ^= attrs_xor;
822 newperms = attrs.permissions & 07777;
824 result = fxp_setstat(fname, attrs);
827 printf("set attrs for %s: %s\n", fname, fxp_error());
832 printf("%s: %04o -> %04o\n", fname, oldperms, newperms);
838 static struct sftp_cmd_lookup {
840 int (*obey) (struct sftp_command *);
843 * List of sftp commands. This is binary-searched so it MUST be
847 "bye", sftp_cmd_quit}, {
848 "cd", sftp_cmd_cd}, {
849 "chmod", sftp_cmd_chmod}, {
850 "del", sftp_cmd_rm}, {
851 "delete", sftp_cmd_rm}, {
852 "dir", sftp_cmd_ls}, {
853 "exit", sftp_cmd_quit}, {
854 "get", sftp_cmd_get}, {
855 "ls", sftp_cmd_ls}, {
856 "mkdir", sftp_cmd_mkdir}, {
857 "mv", sftp_cmd_mv}, {
858 "put", sftp_cmd_put}, {
859 "quit", sftp_cmd_quit}, {
860 "reget", sftp_cmd_reget}, {
861 "ren", sftp_cmd_mv}, {
862 "rename", sftp_cmd_mv}, {
863 "reput", sftp_cmd_reput}, {
864 "rm", sftp_cmd_rm}, {
865 "rmdir", sftp_cmd_rmdir},};
867 /* ----------------------------------------------------------------------
868 * Command line reading and parsing.
870 struct sftp_command *sftp_getcmd(FILE *fp, int mode, int modeflags)
873 int linelen, linesize;
874 struct sftp_command *cmd;
878 if ((mode == 0) || (modeflags & 1)) {
883 cmd = smalloc(sizeof(struct sftp_command));
889 linesize = linelen = 0;
895 line = srealloc(line, linesize);
896 ret = fgets(line + linelen, linesize - linelen, fp);
901 if (!ret || (linelen == 0 && line[0] == '\0')) {
902 cmd->obey = sftp_cmd_quit;
904 return cmd; /* eof */
906 len = linelen + strlen(line + linelen);
908 if (line[linelen - 1] == '\n') {
910 line[linelen] = '\0';
916 * Parse the command line into words. The syntax is:
917 * - double quotes are removed, but cause spaces within to be
918 * treated as non-separating.
919 * - a double-doublequote pair is a literal double quote, inside
920 * _or_ outside quotes. Like this:
922 * firstword "second word" "this has ""quotes"" in" sodoes""this""
928 * >this has "quotes" in<
933 /* skip whitespace */
934 while (*p && (*p == ' ' || *p == '\t'))
936 /* mark start of word */
937 q = r = p; /* q sits at start, r writes word */
940 if (!quoting && (*p == ' ' || *p == '\t'))
941 break; /* reached end of word */
942 else if (*p == '"' && p[1] == '"')
943 p += 2, *r++ = '"'; /* a literal quote */
945 p++, quoting = !quoting;
950 p++; /* skip over the whitespace */
952 if (cmd->nwords >= cmd->wordssize) {
953 cmd->wordssize = cmd->nwords + 16;
955 srealloc(cmd->words, cmd->wordssize * sizeof(char *));
957 cmd->words[cmd->nwords++] = q;
961 * Now parse the first word and assign a function.
964 if (cmd->nwords == 0)
965 cmd->obey = sftp_cmd_null;
969 cmd->obey = sftp_cmd_unknown;
972 j = sizeof(sftp_lookup) / sizeof(*sftp_lookup);
975 cmp = strcmp(cmd->words[0], sftp_lookup[k].name);
981 cmd->obey = sftp_lookup[k].obey;
990 void do_sftp(int mode, int modeflags, char *batchfile)
995 * Do protocol initialisation.
999 "Fatal: unable to initialise SFTP: %s\n", fxp_error());
1004 * Find out where our home directory is.
1006 homedir = fxp_realpath(".");
1009 "Warning: failed to resolve home directory: %s\n",
1011 homedir = dupstr(".");
1013 printf("Remote working directory is %s\n", homedir);
1015 pwd = dupstr(homedir);
1022 /* ------------------------------------------------------------------
1023 * Now we're ready to do Real Stuff.
1026 struct sftp_command *cmd;
1027 cmd = sftp_getcmd(stdin, 0, 0);
1030 if (cmd->obey(cmd) < 0)
1034 fp = fopen(batchfile, "r");
1036 printf("Fatal: unable to open %s\n", batchfile);
1040 struct sftp_command *cmd;
1041 cmd = sftp_getcmd(fp, mode, modeflags);
1044 if (cmd->obey(cmd) < 0)
1046 if (fxp_error() != NULL) {
1047 if (!(modeflags & 2))
1056 /* ----------------------------------------------------------------------
1057 * Dirty bits: integration with PuTTY.
1060 static int verbose = 0;
1062 void verify_ssh_host_key(char *host, int port, char *keytype,
1063 char *keystr, char *fingerprint)
1069 static const char absentmsg[] =
1070 "The server's host key is not cached in the registry. You\n"
1071 "have no guarantee that the server is the computer you\n"
1073 "The server's key fingerprint is:\n"
1075 "If you trust this host, enter \"y\" to add the key to\n"
1076 "PuTTY's cache and carry on connecting.\n"
1077 "If you want to carry on connecting just once, without\n"
1078 "adding the key to the cache, enter \"n\".\n"
1079 "If you do not trust this host, press Return to abandon the\n"
1081 "Store key in cache? (y/n) ";
1083 static const char wrongmsg[] =
1084 "WARNING - POTENTIAL SECURITY BREACH!\n"
1085 "The server's host key does not match the one PuTTY has\n"
1086 "cached in the registry. This means that either the\n"
1087 "server administrator has changed the host key, or you\n"
1088 "have actually connected to another computer pretending\n"
1089 "to be the server.\n"
1090 "The new key fingerprint is:\n"
1092 "If you were expecting this change and trust the new key,\n"
1093 "enter \"y\" to update PuTTY's cache and continue connecting.\n"
1094 "If you want to carry on connecting but without updating\n"
1095 "the cache, enter \"n\".\n"
1096 "If you want to abandon the connection completely, press\n"
1097 "Return to cancel. Pressing Return is the ONLY guaranteed\n"
1099 "Update cached key? (y/n, Return cancels connection) ";
1101 static const char abandoned[] = "Connection abandoned.\n";
1106 * Verify the key against the registry.
1108 ret = verify_host_key(host, port, keytype, keystr);
1110 if (ret == 0) /* success - key matched OK */
1113 if (ret == 2) { /* key was different */
1114 fprintf(stderr, wrongmsg, fingerprint);
1117 if (ret == 1) { /* key was absent */
1118 fprintf(stderr, absentmsg, fingerprint);
1122 hin = GetStdHandle(STD_INPUT_HANDLE);
1123 GetConsoleMode(hin, &savemode);
1124 SetConsoleMode(hin, (savemode | ENABLE_ECHO_INPUT |
1125 ENABLE_PROCESSED_INPUT | ENABLE_LINE_INPUT));
1126 ReadFile(hin, line, sizeof(line) - 1, &i, NULL);
1127 SetConsoleMode(hin, savemode);
1129 if (line[0] != '\0' && line[0] != '\r' && line[0] != '\n') {
1130 if (line[0] == 'y' || line[0] == 'Y')
1131 store_host_key(host, port, keytype, keystr);
1133 fprintf(stderr, abandoned);
1139 * Ask whether the selected cipher is acceptable (since it was
1140 * below the configured 'warn' threshold).
1141 * cs: 0 = both ways, 1 = client->server, 2 = server->client
1143 void askcipher(char *ciphername, int cs)
1148 static const char msg[] =
1149 "The first %scipher supported by the server is\n"
1150 "%s, which is below the configured warning threshold.\n"
1151 "Continue with connection? (y/n) ";
1152 static const char abandoned[] = "Connection abandoned.\n";
1156 fprintf(stderr, msg,
1158 (cs == 1) ? "client-to-server " :
1159 "server-to-client ",
1163 hin = GetStdHandle(STD_INPUT_HANDLE);
1164 GetConsoleMode(hin, &savemode);
1165 SetConsoleMode(hin, (savemode | ENABLE_ECHO_INPUT |
1166 ENABLE_PROCESSED_INPUT | ENABLE_LINE_INPUT));
1167 ReadFile(hin, line, sizeof(line) - 1, &i, NULL);
1168 SetConsoleMode(hin, savemode);
1170 if (line[0] == 'y' || line[0] == 'Y') {
1173 fprintf(stderr, abandoned);
1179 * Print an error message and perform a fatal exit.
1181 void fatalbox(char *fmt, ...)
1183 char str[0x100]; /* Make the size big enough */
1186 strcpy(str, "Fatal:");
1187 vsprintf(str + strlen(str), fmt, ap);
1190 fprintf(stderr, str);
1194 void connection_fatal(char *fmt, ...)
1196 char str[0x100]; /* Make the size big enough */
1199 strcpy(str, "Fatal:");
1200 vsprintf(str + strlen(str), fmt, ap);
1203 fprintf(stderr, str);
1208 void logevent(char *string)
1212 void ldisc_send(char *buf, int len)
1215 * This is only here because of the calls to ldisc_send(NULL,
1216 * 0) in ssh.c. Nothing in PSFTP actually needs to use the
1217 * ldisc as an ldisc. So if we get called with any real data, I
1218 * want to know about it.
1224 * Be told what socket we're supposed to be using.
1226 static SOCKET sftp_ssh_socket;
1227 char *do_select(SOCKET skt, int startup)
1230 sftp_ssh_socket = skt;
1232 sftp_ssh_socket = INVALID_SOCKET;
1235 extern int select_result(WPARAM, LPARAM);
1238 * Receive a block of data from the SSH link. Block until all data
1241 * To do this, we repeatedly call the SSH protocol module, with our
1242 * own trap in from_backend() to catch the data that comes back. We
1243 * do this until we have enough data.
1246 static unsigned char *outptr; /* where to put the data */
1247 static unsigned outlen; /* how much data required */
1248 static unsigned char *pending = NULL; /* any spare data */
1249 static unsigned pendlen = 0, pendsize = 0; /* length and phys. size of buffer */
1250 int from_backend(int is_stderr, char *data, int datalen)
1252 unsigned char *p = (unsigned char *) data;
1253 unsigned len = (unsigned) datalen;
1256 * stderr data is just spouted to local stderr and otherwise
1260 fwrite(data, 1, len, stderr);
1265 * If this is before the real session begins, just return.
1271 unsigned used = outlen;
1274 memcpy(outptr, p, used);
1282 if (pendsize < pendlen + len) {
1283 pendsize = pendlen + len + 4096;
1284 pending = (pending ? srealloc(pending, pendsize) :
1287 fatalbox("Out of memory");
1289 memcpy(pending + pendlen, p, len);
1295 int sftp_recvdata(char *buf, int len)
1297 outptr = (unsigned char *) buf;
1301 * See if the pending-input block contains some of what we
1305 unsigned pendused = pendlen;
1306 if (pendused > outlen)
1308 memcpy(outptr, pending, pendused);
1309 memmove(pending, pending + pendused, pendlen - pendused);
1312 pendlen -= pendused;
1322 while (outlen > 0) {
1326 FD_SET(sftp_ssh_socket, &readfds);
1327 if (select(1, &readfds, NULL, NULL, NULL) < 0)
1328 return 0; /* doom */
1329 select_result((WPARAM) sftp_ssh_socket, (LPARAM) FD_READ);
1334 int sftp_senddata(char *buf, int len)
1336 back->send((unsigned char *) buf, len);
1341 * Loop through the ssh connection and authentication process.
1343 static void ssh_sftp_init(void)
1345 if (sftp_ssh_socket == INVALID_SOCKET)
1347 while (!back->sendok()) {
1350 FD_SET(sftp_ssh_socket, &readfds);
1351 if (select(1, &readfds, NULL, NULL, NULL) < 0)
1353 select_result((WPARAM) sftp_ssh_socket, (LPARAM) FD_READ);
1357 static char *password = NULL;
1358 static int get_line(const char *prompt, char *str, int maxlen, int is_pw)
1361 DWORD savemode, newmode, i;
1364 static int tried_once = 0;
1369 strncpy(str, password, maxlen);
1370 str[maxlen - 1] = '\0';
1376 hin = GetStdHandle(STD_INPUT_HANDLE);
1377 hout = GetStdHandle(STD_OUTPUT_HANDLE);
1378 if (hin == INVALID_HANDLE_VALUE || hout == INVALID_HANDLE_VALUE) {
1379 fprintf(stderr, "Cannot get standard input/output handles\n");
1383 GetConsoleMode(hin, &savemode);
1384 newmode = savemode | ENABLE_PROCESSED_INPUT | ENABLE_LINE_INPUT;
1386 newmode &= ~ENABLE_ECHO_INPUT;
1388 newmode |= ENABLE_ECHO_INPUT;
1389 SetConsoleMode(hin, newmode);
1391 WriteFile(hout, prompt, strlen(prompt), &i, NULL);
1392 ReadFile(hin, str, maxlen - 1, &i, NULL);
1394 SetConsoleMode(hin, savemode);
1396 if ((int) i > maxlen)
1403 WriteFile(hout, "\r\n", 2, &i, NULL);
1409 * Initialize the Win$ock driver.
1411 static void init_winsock(void)
1416 winsock_ver = MAKEWORD(1, 1);
1417 if (WSAStartup(winsock_ver, &wsadata)) {
1418 fprintf(stderr, "Unable to initialise WinSock");
1421 if (LOBYTE(wsadata.wVersion) != 1 || HIBYTE(wsadata.wVersion) != 1) {
1422 fprintf(stderr, "WinSock version is incompatible with 1.1");
1428 * Short description of parameters.
1430 static void usage(void)
1432 printf("PuTTY Secure File Transfer (SFTP) client\n");
1433 printf("%s\n", ver);
1434 printf("Usage: psftp [options] user@host\n");
1435 printf("Options:\n");
1436 printf(" -b file use specified batchfile\n");
1437 printf(" -bc output batchfile commands\n");
1438 printf(" -be don't stop batchfile processing if errors\n");
1439 printf(" -v show verbose messages\n");
1440 printf(" -P port connect to specified port\n");
1441 printf(" -pw passw login with specified password\n");
1446 * Main program. Parse arguments etc.
1448 int main(int argc, char *argv[])
1452 char *user, *host, *userhost, *realhost;
1456 char *batchfile = NULL;
1458 flags = FLAG_STDERR;
1459 ssh_get_line = &get_line;
1463 userhost = user = NULL;
1465 for (i = 1; i < argc; i++) {
1466 if (argv[i][0] != '-') {
1470 userhost = dupstr(argv[i]);
1471 } else if (strcmp(argv[i], "-v") == 0) {
1472 verbose = 1, flags |= FLAG_VERBOSE;
1473 } else if (strcmp(argv[i], "-h") == 0 ||
1474 strcmp(argv[i], "-?") == 0) {
1476 } else if (strcmp(argv[i], "-l") == 0 && i + 1 < argc) {
1478 } else if (strcmp(argv[i], "-P") == 0 && i + 1 < argc) {
1479 portnumber = atoi(argv[++i]);
1480 } else if (strcmp(argv[i], "-pw") == 0 && i + 1 < argc) {
1481 password = argv[++i];
1482 } else if (strcmp(argv[i], "-b") == 0 && i + 1 < argc) {
1484 batchfile = argv[++i];
1485 } else if (strcmp(argv[i], "-bc") == 0 && i + 1 < argc) {
1486 modeflags = modeflags | 1;
1487 } else if (strcmp(argv[i], "-be") == 0 && i + 1 < argc) {
1488 modeflags = modeflags | 2;
1489 } else if (strcmp(argv[i], "--") == 0) {
1500 if (argc > 0 || !userhost)
1503 /* Separate host and username */
1505 host = strrchr(host, '@');
1511 printf("psftp: multiple usernames specified; using \"%s\"\n",
1517 /* Try to load settings for this host */
1518 do_defaults(host, &cfg);
1519 if (cfg.host[0] == '\0') {
1520 /* No settings for this host; use defaults */
1521 do_defaults(NULL, &cfg);
1522 strncpy(cfg.host, host, sizeof(cfg.host) - 1);
1523 cfg.host[sizeof(cfg.host) - 1] = '\0';
1528 if (user != NULL && user[0] != '\0') {
1529 strncpy(cfg.username, user, sizeof(cfg.username) - 1);
1530 cfg.username[sizeof(cfg.username) - 1] = '\0';
1532 if (!cfg.username[0]) {
1533 printf("login as: ");
1534 if (!fgets(cfg.username, sizeof(cfg.username), stdin)) {
1535 fprintf(stderr, "psftp: aborting\n");
1538 int len = strlen(cfg.username);
1539 if (cfg.username[len - 1] == '\n')
1540 cfg.username[len - 1] = '\0';
1544 if (cfg.protocol != PROT_SSH)
1548 cfg.port = portnumber;
1550 /* SFTP uses SSH2 by default always */
1553 /* Set up subsystem name. FIXME: fudge for SSH1. */
1554 strcpy(cfg.remote_cmd, "sftp");
1555 cfg.ssh_subsys = TRUE;
1558 back = &ssh_backend;
1560 err = back->init(cfg.host, cfg.port, &realhost);
1562 fprintf(stderr, "ssh_init: %s", err);
1566 if (verbose && realhost != NULL)
1567 printf("Connected to %s\n", realhost);
1569 do_sftp(mode, modeflags, batchfile);
1571 if (back != NULL && back->socket() != NULL) {
1573 back->special(TS_EOF);
1574 sftp_recvdata(&ch, 1);