X-Git-Url: https://git.distorted.org.uk/u/mdw/putty/blobdiff_plain/2184a5d91ffbcf2de2f730c83dda2d9443035f50..b51259f6a879f69bec5348bddb604d9b3d499941:/psftp.c diff --git a/psftp.c b/psftp.c index 250a5fcc..53896598 100644 --- a/psftp.c +++ b/psftp.c @@ -2,8 +2,6 @@ * psftp.c: front end for PSFTP. */ -#include - #include #include #include @@ -12,6 +10,7 @@ #define PUTTY_DO_GLOBALS #include "putty.h" +#include "psftp.h" #include "storage.h" #include "ssh.h" #include "sftp.h" @@ -24,11 +23,17 @@ * send buffer. */ +static int psftp_connect(char *userhost, char *user, int portnumber); +static int do_sftp_init(void); + /* ---------------------------------------------------------------------- * sftp client state. */ char *pwd, *homedir; +static Backend *back; +static void *backhandle; +static Config cfg; /* ---------------------------------------------------------------------- * Higher-level helper functions used in commands. @@ -42,6 +47,8 @@ char *pwd, *homedir; char *canonify(char *name) { char *fullname, *canonname; + struct sftp_packet *pktin; + struct sftp_request *req, *rreq; if (name[0] == '/') { fullname = dupstr(name); @@ -54,7 +61,10 @@ char *canonify(char *name) fullname = dupcat(pwd, slash, name, NULL); } - canonname = fxp_realpath(fullname); + sftp_register(req = fxp_realpath_send(fullname)); + rreq = sftp_find_request(pktin = sftp_recv()); + assert(rreq == req); + canonname = fxp_realpath_recv(pktin, rreq); if (canonname) { sfree(fullname); @@ -109,10 +119,13 @@ char *canonify(char *name) */ fullname[i] = '\0'; /* separate the string */ if (i == 0) { - canonname = fxp_realpath("/"); + sftp_register(req = fxp_realpath_send("/")); } else { - canonname = fxp_realpath(fullname); + sftp_register(req = fxp_realpath_send(fullname)); } + rreq = sftp_find_request(pktin = sftp_recv()); + assert(rreq == req); + canonname = fxp_realpath_recv(pktin, rreq); if (!canonname) return fullname; /* even that failed; give up */ @@ -165,13 +178,13 @@ struct sftp_command { int sftp_cmd_null(struct sftp_command *cmd) { - return 0; + return 1; /* success */ } int sftp_cmd_unknown(struct sftp_command *cmd) { printf("psftp: unknown command \"%s\"\n", cmd->words[0]); - return 0; + return 0; /* failure */ } int sftp_cmd_quit(struct sftp_command *cmd) @@ -185,19 +198,26 @@ int sftp_cmd_quit(struct sftp_command *cmd) */ static int sftp_ls_compare(const void *av, const void *bv) { - const struct fxp_name *a = (const struct fxp_name *) av; - const struct fxp_name *b = (const struct fxp_name *) bv; - return strcmp(a->filename, b->filename); + const struct fxp_name *const *a = (const struct fxp_name *const *) av; + const struct fxp_name *const *b = (const struct fxp_name *const *) bv; + return strcmp((*a)->filename, (*b)->filename); } int sftp_cmd_ls(struct sftp_command *cmd) { struct fxp_handle *dirh; struct fxp_names *names; - struct fxp_name *ournames; + struct fxp_name **ournames; int nnames, namesize; char *dir, *cdir; + struct sftp_packet *pktin; + struct sftp_request *req, *rreq; int i; + if (back == NULL) { + printf("psftp: not connected to a host; use \"open host.name\"\n"); + return 0; + } + if (cmd->nwords < 2) dir = "."; else @@ -211,7 +231,11 @@ int sftp_cmd_ls(struct sftp_command *cmd) printf("Listing directory %s\n", cdir); - dirh = fxp_opendir(cdir); + sftp_register(req = fxp_opendir_send(cdir)); + rreq = sftp_find_request(pktin = sftp_recv()); + assert(rreq == req); + dirh = fxp_opendir_recv(pktin, rreq); + if (dirh == NULL) { printf("Unable to open %s: %s\n", dir, fxp_error()); } else { @@ -220,7 +244,11 @@ int sftp_cmd_ls(struct sftp_command *cmd) while (1) { - names = fxp_readdir(dirh); + sftp_register(req = fxp_readdir_send(dirh)); + rreq = sftp_find_request(pktin = sftp_recv()); + assert(rreq == req); + names = fxp_readdir_recv(pktin, rreq); + if (names == NULL) { if (fxp_error_type() == SSH_FX_EOF) break; @@ -234,17 +262,18 @@ int sftp_cmd_ls(struct sftp_command *cmd) if (nnames + names->nnames >= namesize) { namesize += names->nnames + 128; - ournames = - srealloc(ournames, namesize * sizeof(*ournames)); + ournames = sresize(ournames, namesize, struct fxp_name *); } for (i = 0; i < names->nnames; i++) - ournames[nnames++] = names->names[i]; + ournames[nnames++] = fxp_dup_name(&names->names[i]); - names->nnames = 0; /* prevent free_names */ fxp_free_names(names); } - fxp_close(dirh); + sftp_register(req = fxp_close_send(dirh)); + rreq = sftp_find_request(pktin = sftp_recv()); + assert(rreq == req); + fxp_close_recv(pktin, rreq); /* * Now we have our filenames. Sort them by actual file @@ -255,13 +284,16 @@ int sftp_cmd_ls(struct sftp_command *cmd) /* * And print them. */ - for (i = 0; i < nnames; i++) - printf("%s\n", ournames[i].longname); + for (i = 0; i < nnames; i++) { + printf("%s\n", ournames[i]->longname); + fxp_free_name(ournames[i]); + } + sfree(ournames); } sfree(cdir); - return 0; + return 1; } /* @@ -271,8 +303,15 @@ int sftp_cmd_ls(struct sftp_command *cmd) int sftp_cmd_cd(struct sftp_command *cmd) { struct fxp_handle *dirh; + struct sftp_packet *pktin; + struct sftp_request *req, *rreq; char *dir; + if (back == NULL) { + printf("psftp: not connected to a host; use \"open host.name\"\n"); + return 0; + } + if (cmd->nwords < 2) dir = dupstr(homedir); else @@ -283,20 +322,27 @@ int sftp_cmd_cd(struct sftp_command *cmd) return 0; } - dirh = fxp_opendir(dir); + sftp_register(req = fxp_opendir_send(dir)); + rreq = sftp_find_request(pktin = sftp_recv()); + assert(rreq == req); + dirh = fxp_opendir_recv(pktin, rreq); + if (!dirh) { printf("Directory %s: %s\n", dir, fxp_error()); sfree(dir); return 0; } - fxp_close(dirh); + sftp_register(req = fxp_close_send(dirh)); + rreq = sftp_find_request(pktin = sftp_recv()); + assert(rreq == req); + fxp_close_recv(pktin, rreq); sfree(pwd); pwd = dir; printf("Remote directory is now %s\n", pwd); - return 0; + return 1; } /* @@ -304,8 +350,13 @@ int sftp_cmd_cd(struct sftp_command *cmd) */ int sftp_cmd_pwd(struct sftp_command *cmd) { + if (back == NULL) { + printf("psftp: not connected to a host; use \"open host.name\"\n"); + return 0; + } + printf("Remote directory is %s\n", pwd); - return 0; + return 1; } /* @@ -317,9 +368,17 @@ int sftp_cmd_pwd(struct sftp_command *cmd) int sftp_general_get(struct sftp_command *cmd, int restart) { struct fxp_handle *fh; + struct sftp_packet *pktin; + struct sftp_request *req, *rreq; char *fname, *outfname; uint64 offset; FILE *fp; + int ret; + + if (back == NULL) { + printf("psftp: not connected to a host; use \"open host.name\"\n"); + return 0; + } if (cmd->nwords < 2) { printf("get: expects a filename\n"); @@ -334,7 +393,11 @@ int sftp_general_get(struct sftp_command *cmd, int restart) outfname = (cmd->nwords == 2 ? stripslashes(cmd->words[1], 0) : cmd->words[2]); - fh = fxp_open(fname, SSH_FXF_READ); + sftp_register(req = fxp_open_send(fname, SSH_FXF_READ)); + rreq = sftp_find_request(pktin = sftp_recv()); + assert(rreq == req); + fh = fxp_open_recv(pktin, rreq); + if (!fh) { printf("%s: %s\n", fname, fxp_error()); sfree(fname); @@ -349,7 +412,12 @@ int sftp_general_get(struct sftp_command *cmd, int restart) if (!fp) { printf("local: unable to open %s\n", outfname); - fxp_close(fh); + + sftp_register(req = fxp_close_send(fh)); + rreq = sftp_find_request(pktin = sftp_recv()); + assert(rreq == req); + fxp_close_recv(pktin, rreq); + sfree(fname); return 0; } @@ -370,16 +438,22 @@ int sftp_general_get(struct sftp_command *cmd, int restart) * FIXME: we can use FXP_FSTAT here to get the file size, and * thus put up a progress bar. */ + ret = 1; while (1) { char buffer[4096]; int len; int wpos, wlen; - len = fxp_read(fh, buffer, offset, sizeof(buffer)); + sftp_register(req = fxp_read_send(fh, offset, sizeof(buffer))); + rreq = sftp_find_request(pktin = sftp_recv()); + assert(rreq == req); + len = fxp_read_recv(pktin, rreq, buffer, sizeof(buffer)); + if ((len == -1 && fxp_error_type() == SSH_FX_EOF) || len == 0) break; if (len == -1) { printf("error while reading: %s\n", fxp_error()); + ret = 0; break; } @@ -388,20 +462,28 @@ int sftp_general_get(struct sftp_command *cmd, int restart) wlen = fwrite(buffer, 1, len - wpos, fp); if (wlen <= 0) { printf("error while writing local file\n"); + ret = 0; break; } wpos += wlen; } - if (wpos < len) /* we had an error */ + if (wpos < len) { /* we had an error */ + ret = 0; break; + } offset = uint64_add32(offset, len); } fclose(fp); - fxp_close(fh); + + sftp_register(req = fxp_close_send(fh)); + rreq = sftp_find_request(pktin = sftp_recv()); + assert(rreq == req); + fxp_close_recv(pktin, rreq); + sfree(fname); - return 0; + return ret; } int sftp_cmd_get(struct sftp_command *cmd) { @@ -422,8 +504,16 @@ int sftp_general_put(struct sftp_command *cmd, int restart) { struct fxp_handle *fh; char *fname, *origoutfname, *outfname; + struct sftp_packet *pktin; + struct sftp_request *req, *rreq; uint64 offset; FILE *fp; + int ret; + + if (back == NULL) { + printf("psftp: not connected to a host; use \"open host.name\"\n"); + return 0; + } if (cmd->nwords < 2) { printf("put: expects a filename\n"); @@ -446,12 +536,15 @@ int sftp_general_put(struct sftp_command *cmd, int restart) return 0; } if (restart) { - fh = fxp_open(outfname, - SSH_FXF_WRITE); + sftp_register(req = fxp_open_send(outfname, SSH_FXF_WRITE)); } else { - fh = fxp_open(outfname, - SSH_FXF_WRITE | SSH_FXF_CREAT | SSH_FXF_TRUNC); + sftp_register(req = fxp_open_send(outfname, SSH_FXF_WRITE | + SSH_FXF_CREAT | SSH_FXF_TRUNC)); } + rreq = sftp_find_request(pktin = sftp_recv()); + assert(rreq == req); + fh = fxp_open_recv(pktin, rreq); + if (!fh) { printf("%s: %s\n", outfname, fxp_error()); sfree(outfname); @@ -461,7 +554,14 @@ int sftp_general_put(struct sftp_command *cmd, int restart) if (restart) { char decbuf[30]; struct fxp_attrs attrs; - if (!fxp_fstat(fh, &attrs)) { + int ret; + + sftp_register(req = fxp_fstat_send(fh)); + rreq = sftp_find_request(pktin = sftp_recv()); + assert(rreq == req); + ret = fxp_fstat_recv(pktin, rreq, &attrs); + + if (!ret) { printf("read size of %s: %s\n", outfname, fxp_error()); sfree(outfname); return 0; @@ -491,29 +591,42 @@ int sftp_general_put(struct sftp_command *cmd, int restart) * FIXME: we can use FXP_FSTAT here to get the file size, and * thus put up a progress bar. */ + ret = 1; while (1) { char buffer[4096]; - int len; + int len, ret; len = fread(buffer, 1, sizeof(buffer), fp); if (len == -1) { printf("error while reading local file\n"); + ret = 0; break; } else if (len == 0) { break; } - if (!fxp_write(fh, buffer, offset, len)) { + + sftp_register(req = fxp_write_send(fh, buffer, offset, len)); + rreq = sftp_find_request(pktin = sftp_recv()); + assert(rreq == req); + ret = fxp_write_recv(pktin, rreq); + + if (!ret) { printf("error while writing: %s\n", fxp_error()); + ret = 0; break; } offset = uint64_add32(offset, len); } - fxp_close(fh); + sftp_register(req = fxp_close_send(fh)); + rreq = sftp_find_request(pktin = sftp_recv()); + assert(rreq == req); + fxp_close_recv(pktin, rreq); + fclose(fp); sfree(outfname); - return 0; + return ret; } int sftp_cmd_put(struct sftp_command *cmd) { @@ -527,8 +640,14 @@ int sftp_cmd_reput(struct sftp_command *cmd) int sftp_cmd_mkdir(struct sftp_command *cmd) { char *dir; + struct sftp_packet *pktin; + struct sftp_request *req, *rreq; int result; + if (back == NULL) { + printf("psftp: not connected to a host; use \"open host.name\"\n"); + return 0; + } if (cmd->nwords < 2) { printf("mkdir: expects a directory\n"); @@ -541,7 +660,11 @@ int sftp_cmd_mkdir(struct sftp_command *cmd) return 0; } - result = fxp_mkdir(dir); + sftp_register(req = fxp_mkdir_send(dir)); + rreq = sftp_find_request(pktin = sftp_recv()); + assert(rreq == req); + result = fxp_mkdir_recv(pktin, rreq); + if (!result) { printf("mkdir %s: %s\n", dir, fxp_error()); sfree(dir); @@ -549,14 +672,20 @@ int sftp_cmd_mkdir(struct sftp_command *cmd) } sfree(dir); - return 0; + return 1; } int sftp_cmd_rmdir(struct sftp_command *cmd) { char *dir; + struct sftp_packet *pktin; + struct sftp_request *req, *rreq; int result; + if (back == NULL) { + printf("psftp: not connected to a host; use \"open host.name\"\n"); + return 0; + } if (cmd->nwords < 2) { printf("rmdir: expects a directory\n"); @@ -569,7 +698,11 @@ int sftp_cmd_rmdir(struct sftp_command *cmd) return 0; } - result = fxp_rmdir(dir); + sftp_register(req = fxp_rmdir_send(dir)); + rreq = sftp_find_request(pktin = sftp_recv()); + assert(rreq == req); + result = fxp_rmdir_recv(pktin, rreq); + if (!result) { printf("rmdir %s: %s\n", dir, fxp_error()); sfree(dir); @@ -577,14 +710,21 @@ int sftp_cmd_rmdir(struct sftp_command *cmd) } sfree(dir); - return 0; + return 1; } int sftp_cmd_rm(struct sftp_command *cmd) { char *fname; + struct sftp_packet *pktin; + struct sftp_request *req, *rreq; int result; + if (back == NULL) { + printf("psftp: not connected to a host; use \"open host.name\"\n"); + return 0; + } + if (cmd->nwords < 2) { printf("rm: expects a filename\n"); return 0; @@ -596,7 +736,11 @@ int sftp_cmd_rm(struct sftp_command *cmd) return 0; } - result = fxp_remove(fname); + sftp_register(req = fxp_remove_send(fname)); + rreq = sftp_find_request(pktin = sftp_recv()); + assert(rreq == req); + result = fxp_remove_recv(pktin, rreq); + if (!result) { printf("rm %s: %s\n", fname, fxp_error()); sfree(fname); @@ -604,15 +748,21 @@ int sftp_cmd_rm(struct sftp_command *cmd) } sfree(fname); - return 0; - + return 1; } int sftp_cmd_mv(struct sftp_command *cmd) { char *srcfname, *dstfname; + struct sftp_packet *pktin; + struct sftp_request *req, *rreq; int result; + if (back == NULL) { + printf("psftp: not connected to a host; use \"open host.name\"\n"); + return 0; + } + if (cmd->nwords < 3) { printf("mv: expects two filenames\n"); return 0; @@ -629,7 +779,11 @@ int sftp_cmd_mv(struct sftp_command *cmd) return 0; } - result = fxp_rename(srcfname, dstfname); + sftp_register(req = fxp_rename_send(srcfname, dstfname)); + rreq = sftp_find_request(pktin = sftp_recv()); + assert(rreq == req); + result = fxp_rename_recv(pktin, rreq); + if (!result) { char const *error = fxp_error(); struct fxp_attrs attrs; @@ -640,7 +794,11 @@ int sftp_cmd_mv(struct sftp_command *cmd) * _is_ a directory, we re-attempt the move by appending * the basename of srcfname to dstfname. */ - result = fxp_stat(dstfname, &attrs); + sftp_register(req = fxp_stat_send(dstfname)); + rreq = sftp_find_request(pktin = sftp_recv()); + assert(rreq == req); + result = fxp_stat_recv(pktin, rreq, &attrs); + if (result && (attrs.flags & SSH_FILEXFER_ATTR_PERMISSIONS) && (attrs.permissions & 0040000)) { @@ -655,7 +813,12 @@ int sftp_cmd_mv(struct sftp_command *cmd) if (newcanon) { sfree(dstfname); dstfname = newcanon; - result = fxp_rename(srcfname, dstfname); + + sftp_register(req = fxp_rename_send(srcfname, dstfname)); + rreq = sftp_find_request(pktin = sftp_recv()); + assert(rreq == req); + result = fxp_rename_recv(pktin, rreq); + error = result ? NULL : fxp_error(); } } @@ -670,7 +833,7 @@ int sftp_cmd_mv(struct sftp_command *cmd) sfree(srcfname); sfree(dstfname); - return 0; + return 1; } int sftp_cmd_chmod(struct sftp_command *cmd) @@ -679,6 +842,13 @@ int sftp_cmd_chmod(struct sftp_command *cmd) int result; struct fxp_attrs attrs; unsigned attrs_clr, attrs_xor, oldperms, newperms; + struct sftp_packet *pktin; + struct sftp_request *req, *rreq; + + if (back == NULL) { + printf("psftp: not connected to a host; use \"open host.name\"\n"); + return 0; + } if (cmd->nwords < 3) { printf("chmod: expects a mode specifier and a filename\n"); @@ -724,20 +894,20 @@ int sftp_cmd_chmod(struct sftp_command *cmd) default: printf("chmod: file mode '%.*s' contains unrecognised" " user/group/other specifier '%c'\n", - strcspn(modebegin, ","), modebegin, *mode); + (int)strcspn(modebegin, ","), modebegin, *mode); return 0; } mode++; } if (!*mode || *mode == ',') { printf("chmod: file mode '%.*s' is incomplete\n", - strcspn(modebegin, ","), modebegin); + (int)strcspn(modebegin, ","), modebegin); return 0; } action = *mode++; if (!*mode || *mode == ',') { printf("chmod: file mode '%.*s' is incomplete\n", - strcspn(modebegin, ","), modebegin); + (int)strcspn(modebegin, ","), modebegin); return 0; } perms = 0; @@ -752,7 +922,7 @@ int sftp_cmd_chmod(struct sftp_command *cmd) (subset & 06777) != 02070) { printf("chmod: file mode '%.*s': set[ug]id bit should" " be used with exactly one of u or g only\n", - strcspn(modebegin, ","), modebegin); + (int)strcspn(modebegin, ","), modebegin); return 0; } perms |= 06000; @@ -760,7 +930,7 @@ int sftp_cmd_chmod(struct sftp_command *cmd) default: printf("chmod: file mode '%.*s' contains unrecognised" " permission specifier '%c'\n", - strcspn(modebegin, ","), modebegin, *mode); + (int)strcspn(modebegin, ","), modebegin, *mode); return 0; } mode++; @@ -768,7 +938,7 @@ int sftp_cmd_chmod(struct sftp_command *cmd) if (!(subset & 06777) && (perms &~ subset)) { printf("chmod: file mode '%.*s' contains no user/group/other" " specifier and permissions other than 't' \n", - strcspn(modebegin, ","), modebegin, *mode); + (int)strcspn(modebegin, ","), modebegin); return 0; } perms &= subset; @@ -796,7 +966,11 @@ int sftp_cmd_chmod(struct sftp_command *cmd) return 0; } - result = fxp_stat(fname, &attrs); + sftp_register(req = fxp_stat_send(fname)); + rreq = sftp_find_request(pktin = sftp_recv()); + assert(rreq == req); + result = fxp_stat_recv(pktin, rreq, &attrs); + if (!result || !(attrs.flags & SSH_FILEXFER_ATTR_PERMISSIONS)) { printf("get attrs for %s: %s\n", fname, result ? "file permissions not provided" : fxp_error()); @@ -810,7 +984,10 @@ int sftp_cmd_chmod(struct sftp_command *cmd) attrs.permissions ^= attrs_xor; newperms = attrs.permissions & 07777; - result = fxp_setstat(fname, attrs); + sftp_register(req = fxp_setstat_send(fname, attrs)); + rreq = sftp_find_request(pktin = sftp_recv()); + assert(rreq == req); + result = fxp_setstat_recv(pktin, rreq); if (!result) { printf("set attrs for %s: %s\n", fname, fxp_error()); @@ -821,7 +998,69 @@ int sftp_cmd_chmod(struct sftp_command *cmd) printf("%s: %04o -> %04o\n", fname, oldperms, newperms); sfree(fname); - return 0; + return 1; +} + +static int sftp_cmd_open(struct sftp_command *cmd) +{ + if (back != NULL) { + printf("psftp: already connected\n"); + return 0; + } + + if (cmd->nwords < 2) { + printf("open: expects a host name\n"); + return 0; + } + + if (psftp_connect(cmd->words[1], NULL, 0)) { + back = NULL; /* connection is already closed */ + return -1; /* this is fatal */ + } + do_sftp_init(); + return 1; +} + +static int sftp_cmd_lcd(struct sftp_command *cmd) +{ + char *currdir, *errmsg; + + if (cmd->nwords < 2) { + printf("lcd: expects a local directory name\n"); + return 0; + } + + errmsg = psftp_lcd(cmd->words[1]); + if (errmsg) { + printf("lcd: unable to change directory: %s\n", errmsg); + sfree(errmsg); + return 0; + } + + currdir = psftp_getcwd(); + printf("New local directory is %s\n", currdir); + sfree(currdir); + + return 1; +} + +static int sftp_cmd_lpwd(struct sftp_command *cmd) +{ + char *currdir; + + currdir = psftp_getcwd(); + printf("Current local directory is %s\n", currdir); + sfree(currdir); + + return 1; +} + +static int sftp_cmd_pling(struct sftp_command *cmd) +{ + int exitcode; + + exitcode = system(cmd->words[1]); + return (exitcode == 0); } static int sftp_cmd_help(struct sftp_command *cmd); @@ -840,6 +1079,7 @@ static struct sftp_cmd_lookup { * `shorthelp' is the name of a primary command, which * contains the help that should double up for this command. */ + int listed; /* do we list this in primary help? */ char *shorthelp; char *longhelp; int (*obey) (struct sftp_command *); @@ -849,13 +1089,20 @@ static struct sftp_cmd_lookup { * in ASCII order. */ { - "bye", "finish your SFTP session", + "!", TRUE, "run a local command", + "\n" + /* FIXME: this example is crap for non-Windows. */ + " Runs a local command. For example, \"!del myfile\".\n", + sftp_cmd_pling + }, + { + "bye", TRUE, "finish your SFTP session", "\n" " Terminates your SFTP session and quits the PSFTP program.\n", sftp_cmd_quit }, { - "cd", "change your remote working directory", + "cd", TRUE, "change your remote working directory", " [ ]\n" " Change the remote working directory for your SFTP session.\n" " If a new working directory is not supplied, you will be\n" @@ -863,7 +1110,7 @@ static struct sftp_cmd_lookup { sftp_cmd_cd }, { - "chmod", "change file permissions and modes", + "chmod", TRUE, "change file permissions and modes", " ( | ) \n" " Change the file permissions on a file or directory.\n" " can be any octal Unix permission specifier.\n" @@ -890,19 +1137,16 @@ static struct sftp_cmd_lookup { sftp_cmd_chmod }, { - "del", "delete a file", + "del", TRUE, "delete a file", " \n" " Delete a file.\n", sftp_cmd_rm }, { - "delete", "delete a file", - "\n" - " Delete a file.\n", - sftp_cmd_rm + "delete", FALSE, "del", NULL, sftp_cmd_rm }, { - "dir", "list contents of a remote directory", + "dir", TRUE, "list contents of a remote directory", " [ ]\n" " List the contents of a specified directory on the server.\n" " If is not given, the current working directory\n" @@ -910,10 +1154,10 @@ static struct sftp_cmd_lookup { sftp_cmd_ls }, { - "exit", "bye", NULL, sftp_cmd_quit + "exit", TRUE, "bye", NULL, sftp_cmd_quit }, { - "get", "download a file from the server to your local machine", + "get", TRUE, "download a file from the server to your local machine", " [ ]\n" " Downloads a file on the server and stores it locally under\n" " the same name, or under a different one if you supply the\n" @@ -921,7 +1165,7 @@ static struct sftp_cmd_lookup { sftp_cmd_get }, { - "help", "give help", + "help", TRUE, "give help", " [ [ ... ] ]\n" " Give general help if no commands are specified.\n" " If one or more commands are specified, give specific help on\n" @@ -929,24 +1173,46 @@ static struct sftp_cmd_lookup { sftp_cmd_help }, { - "ls", "dir", NULL, + "lcd", TRUE, "change local working directory", + " \n" + " Change the local working directory of the PSFTP program (the\n" + " default location where the \"get\" command will save files).\n", + sftp_cmd_lcd + }, + { + "lpwd", TRUE, "print local working directory", + "\n" + " Print the local working directory of the PSFTP program (the\n" + " default location where the \"get\" command will save files).\n", + sftp_cmd_lpwd + }, + { + "ls", TRUE, "dir", NULL, sftp_cmd_ls }, { - "mkdir", "create a directory on the remote server", + "mkdir", TRUE, "create a directory on the remote server", " \n" " Creates a directory with the given name on the server.\n", sftp_cmd_mkdir }, { - "mv", "move or rename a file on the remote server", + "mv", TRUE, "move or rename a file on the remote server", " \n" " Moves or renames the file on the server,\n" " so that it is accessible under the name .\n", sftp_cmd_mv }, { - "put", "upload a file from your local machine to the server", + "open", TRUE, "connect to a host", + " [@]\n" + " Establishes an SFTP connection to a given host. Only usable\n" + " when you did not already specify a host name on the command\n" + " line.\n", + sftp_cmd_open + }, + { + "put", TRUE, "upload a file from your local machine to the server", " [ ]\n" " Uploads a file to the server and stores it there under\n" " the same name, or under a different one if you supply the\n" @@ -954,17 +1220,17 @@ static struct sftp_cmd_lookup { sftp_cmd_put }, { - "pwd", "print your remote working directory", + "pwd", TRUE, "print your remote working directory", "\n" " Print the current remote working directory for your SFTP session.\n", sftp_cmd_pwd }, { - "quit", "bye", NULL, + "quit", TRUE, "bye", NULL, sftp_cmd_quit }, { - "reget", "continue downloading a file", + "reget", TRUE, "continue downloading a file", " [ ]\n" " Works exactly like the \"get\" command, but the local file\n" " must already exist. The download will begin at the end of the\n" @@ -972,15 +1238,15 @@ static struct sftp_cmd_lookup { sftp_cmd_reget }, { - "ren", "mv", NULL, + "ren", TRUE, "mv", NULL, sftp_cmd_mv }, { - "rename", "mv", NULL, + "rename", FALSE, "mv", NULL, sftp_cmd_mv }, { - "reput", "continue uploading a file", + "reput", TRUE, "continue uploading a file", " [ ]\n" " Works exactly like the \"put\" command, but the remote file\n" " must already exist. The upload will begin at the end of the\n" @@ -988,11 +1254,11 @@ static struct sftp_cmd_lookup { sftp_cmd_reput }, { - "rm", "del", NULL, + "rm", TRUE, "del", NULL, sftp_cmd_rm }, { - "rmdir", "remove a directory on the remote server", + "rmdir", TRUE, "remove a directory on the remote server", " \n" " Removes the directory with the given name on the server.\n" " The directory will not be removed unless it is empty.\n", @@ -1030,12 +1296,17 @@ static int sftp_cmd_help(struct sftp_command *cmd) int maxlen; maxlen = 0; for (i = 0; i < sizeof(sftp_lookup) / sizeof(*sftp_lookup); i++) { - int len = strlen(sftp_lookup[i].name); + int len; + if (!sftp_lookup[i].listed) + continue; + len = strlen(sftp_lookup[i].name); if (maxlen < len) maxlen = len; } for (i = 0; i < sizeof(sftp_lookup) / sizeof(*sftp_lookup); i++) { const struct sftp_cmd_lookup *lookup; + if (!sftp_lookup[i].listed) + continue; lookup = &sftp_lookup[i]; printf("%-*s", maxlen+2, lookup->name); if (lookup->longhelp == NULL) @@ -1059,7 +1330,7 @@ static int sftp_cmd_help(struct sftp_command *cmd) } } } - return 0; + return 1; } /* ---------------------------------------------------------------------- @@ -1073,12 +1344,12 @@ struct sftp_command *sftp_getcmd(FILE *fp, int mode, int modeflags) char *p, *q, *r; int quoting; - if ((mode == 0) || (modeflags & 1)) { - printf("psftp> "); - } + if ((mode == 0) || (modeflags & 1)) { + printf("psftp> "); + } fflush(stdout); - cmd = smalloc(sizeof(struct sftp_command)); + cmd = snew(struct sftp_command); cmd->words = NULL; cmd->nwords = 0; cmd->wordssize = 0; @@ -1090,15 +1361,13 @@ struct sftp_command *sftp_getcmd(FILE *fp, int mode, int modeflags) char *ret; linesize += 512; - line = srealloc(line, linesize); + line = sresize(line, linesize, char); ret = fgets(line + linelen, linesize - linelen, fp); - if (modeflags & 1) { - printf("%s", ret); - } if (!ret || (linelen == 0 && line[0] == '\0')) { cmd->obey = sftp_cmd_quit; - printf("quit\n"); + if ((mode == 0) || (modeflags & 1)) + printf("quit\n"); return cmd; /* eof */ } len = linelen + strlen(line + linelen); @@ -1109,50 +1378,68 @@ struct sftp_command *sftp_getcmd(FILE *fp, int mode, int modeflags) break; } } + if (modeflags & 1) { + printf("%s\n", line); + } - /* - * Parse the command line into words. The syntax is: - * - double quotes are removed, but cause spaces within to be - * treated as non-separating. - * - a double-doublequote pair is a literal double quote, inside - * _or_ outside quotes. Like this: - * - * firstword "second word" "this has ""quotes"" in" sodoes""this"" - * - * becomes - * - * >firstword< - * >second word< - * >this has "quotes" in< - * >sodoes"this"< - */ p = line; - while (*p) { - /* skip whitespace */ - while (*p && (*p == ' ' || *p == '\t')) - p++; - /* mark start of word */ - q = r = p; /* q sits at start, r writes word */ - quoting = 0; + while (*p && (*p == ' ' || *p == '\t')) + p++; + + if (*p == '!') { + /* + * Special case: the ! command. This is always parsed as + * exactly two words: one containing the !, and the second + * containing everything else on the line. + */ + cmd->nwords = cmd->wordssize = 2; + cmd->words = sresize(cmd->words, cmd->wordssize, char *); + cmd->words[0] = "!"; + cmd->words[1] = p+1; + } else { + + /* + * Parse the command line into words. The syntax is: + * - double quotes are removed, but cause spaces within to be + * treated as non-separating. + * - a double-doublequote pair is a literal double quote, inside + * _or_ outside quotes. Like this: + * + * firstword "second word" "this has ""quotes"" in" and""this"" + * + * becomes + * + * >firstword< + * >second word< + * >this has "quotes" in< + * >and"this"< + */ while (*p) { - if (!quoting && (*p == ' ' || *p == '\t')) - break; /* reached end of word */ - else if (*p == '"' && p[1] == '"') - p += 2, *r++ = '"'; /* a literal quote */ - else if (*p == '"') - p++, quoting = !quoting; - else - *r++ = *p++; - } - if (*p) - p++; /* skip over the whitespace */ - *r = '\0'; - if (cmd->nwords >= cmd->wordssize) { - cmd->wordssize = cmd->nwords + 16; - cmd->words = - srealloc(cmd->words, cmd->wordssize * sizeof(char *)); + /* skip whitespace */ + while (*p && (*p == ' ' || *p == '\t')) + p++; + /* mark start of word */ + q = r = p; /* q sits at start, r writes word */ + quoting = 0; + while (*p) { + if (!quoting && (*p == ' ' || *p == '\t')) + break; /* reached end of word */ + else if (*p == '"' && p[1] == '"') + p += 2, *r++ = '"'; /* a literal quote */ + else if (*p == '"') + p++, quoting = !quoting; + else + *r++ = *p++; + } + if (*p) + p++; /* skip over the whitespace */ + *r = '\0'; + if (cmd->nwords >= cmd->wordssize) { + cmd->wordssize = cmd->nwords + 16; + cmd->words = sresize(cmd->words, cmd->wordssize, char *); + } + cmd->words[cmd->nwords++] = q; } - cmd->words[cmd->nwords++] = q; } /* @@ -1173,9 +1460,10 @@ struct sftp_command *sftp_getcmd(FILE *fp, int mode, int modeflags) return cmd; } -void do_sftp(int mode, int modeflags, char *batchfile) +static int do_sftp_init(void) { - FILE *fp; + struct sftp_packet *pktin; + struct sftp_request *req, *rreq; /* * Do protocol initialisation. @@ -1183,13 +1471,17 @@ void do_sftp(int mode, int modeflags, char *batchfile) if (!fxp_init()) { fprintf(stderr, "Fatal: unable to initialise SFTP: %s\n", fxp_error()); - return; + return 1; /* failure */ } /* * Find out where our home directory is. */ - homedir = fxp_realpath("."); + sftp_register(req = fxp_realpath_send(".")); + rreq = sftp_find_request(pktin = sftp_recv()); + assert(rreq == req); + homedir = fxp_realpath_recv(pktin, rreq); + if (!homedir) { fprintf(stderr, "Warning: failed to resolve home directory: %s\n", @@ -1199,6 +1491,13 @@ void do_sftp(int mode, int modeflags, char *batchfile) printf("Remote working directory is %s\n", homedir); } pwd = dupstr(homedir); + return 0; +} + +void do_sftp(int mode, int modeflags, char *batchfile) +{ + FILE *fp; + int ret; /* * Batch mode? @@ -1209,12 +1508,12 @@ void do_sftp(int mode, int modeflags, char *batchfile) * Now we're ready to do Real Stuff. */ while (1) { - struct sftp_command *cmd; - cmd = sftp_getcmd(stdin, 0, 0); - if (!cmd) - break; - if (cmd->obey(cmd) < 0) - break; + struct sftp_command *cmd; + cmd = sftp_getcmd(stdin, 0, 0); + if (!cmd) + break; + if (cmd->obey(cmd) < 0) + break; } } else { fp = fopen(batchfile, "r"); @@ -1227,9 +1526,10 @@ void do_sftp(int mode, int modeflags, char *batchfile) cmd = sftp_getcmd(fp, mode, modeflags); if (!cmd) break; - if (cmd->obey(cmd) < 0) + ret = cmd->obey(cmd); + if (ret < 0) break; - if (fxp_error() != NULL) { + if (ret == 0) { if (!(modeflags & 2)) break; } @@ -1245,176 +1545,53 @@ void do_sftp(int mode, int modeflags, char *batchfile) static int verbose = 0; -void verify_ssh_host_key(char *host, int port, char *keytype, - char *keystr, char *fingerprint) -{ - int ret; - HANDLE hin; - DWORD savemode, i; - - static const char absentmsg[] = - "The server's host key is not cached in the registry. You\n" - "have no guarantee that the server is the computer you\n" - "think it is.\n" - "The server's key fingerprint is:\n" - "%s\n" - "If you trust this host, enter \"y\" to add the key to\n" - "PuTTY's cache and carry on connecting.\n" - "If you want to carry on connecting just once, without\n" - "adding the key to the cache, enter \"n\".\n" - "If you do not trust this host, press Return to abandon the\n" - "connection.\n" - "Store key in cache? (y/n) "; - - static const char wrongmsg[] = - "WARNING - POTENTIAL SECURITY BREACH!\n" - "The server's host key does not match the one PuTTY has\n" - "cached in the registry. This means that either the\n" - "server administrator has changed the host key, or you\n" - "have actually connected to another computer pretending\n" - "to be the server.\n" - "The new key fingerprint is:\n" - "%s\n" - "If you were expecting this change and trust the new key,\n" - "enter \"y\" to update PuTTY's cache and continue connecting.\n" - "If you want to carry on connecting but without updating\n" - "the cache, enter \"n\".\n" - "If you want to abandon the connection completely, press\n" - "Return to cancel. Pressing Return is the ONLY guaranteed\n" - "safe choice.\n" - "Update cached key? (y/n, Return cancels connection) "; - - static const char abandoned[] = "Connection abandoned.\n"; - - char line[32]; - - /* - * Verify the key against the registry. - */ - ret = verify_host_key(host, port, keytype, keystr); - - if (ret == 0) /* success - key matched OK */ - return; - - if (ret == 2) { /* key was different */ - fprintf(stderr, wrongmsg, fingerprint); - fflush(stderr); - } - if (ret == 1) { /* key was absent */ - fprintf(stderr, absentmsg, fingerprint); - fflush(stderr); - } - - hin = GetStdHandle(STD_INPUT_HANDLE); - GetConsoleMode(hin, &savemode); - SetConsoleMode(hin, (savemode | ENABLE_ECHO_INPUT | - ENABLE_PROCESSED_INPUT | ENABLE_LINE_INPUT)); - ReadFile(hin, line, sizeof(line) - 1, &i, NULL); - SetConsoleMode(hin, savemode); - - if (line[0] != '\0' && line[0] != '\r' && line[0] != '\n') { - if (line[0] == 'y' || line[0] == 'Y') - store_host_key(host, port, keytype, keystr); - } else { - fprintf(stderr, abandoned); - exit(0); - } -} - -/* - * Ask whether the selected cipher is acceptable (since it was - * below the configured 'warn' threshold). - * cs: 0 = both ways, 1 = client->server, 2 = server->client - */ -void askcipher(char *ciphername, int cs) -{ - HANDLE hin; - DWORD savemode, i; - - static const char msg[] = - "The first %scipher supported by the server is\n" - "%s, which is below the configured warning threshold.\n" - "Continue with connection? (y/n) "; - static const char abandoned[] = "Connection abandoned.\n"; - - char line[32]; - - fprintf(stderr, msg, - (cs == 0) ? "" : - (cs == 1) ? "client-to-server " : - "server-to-client ", - ciphername); - fflush(stderr); - - hin = GetStdHandle(STD_INPUT_HANDLE); - GetConsoleMode(hin, &savemode); - SetConsoleMode(hin, (savemode | ENABLE_ECHO_INPUT | - ENABLE_PROCESSED_INPUT | ENABLE_LINE_INPUT)); - ReadFile(hin, line, sizeof(line) - 1, &i, NULL); - SetConsoleMode(hin, savemode); - - if (line[0] == 'y' || line[0] == 'Y') { - return; - } else { - fprintf(stderr, abandoned); - exit(0); - } -} - -/* - * Warn about the obsolescent key file format. - */ -void old_keyfile_warning(void) -{ - static const char message[] = - "You are loading an SSH 2 private key which has an\n" - "old version of the file format. This means your key\n" - "file is not fully tamperproof. Future versions of\n" - "PuTTY may stop supporting this private key format,\n" - "so we recommend you convert your key to the new\n" - "format.\n" - "\n" - "Once the key is loaded into PuTTYgen, you can perform\n" - "this conversion simply by saving it again.\n"; - - fputs(message, stderr); -} - /* * Print an error message and perform a fatal exit. */ void fatalbox(char *fmt, ...) { - char str[0x100]; /* Make the size big enough */ + char *str, *str2; va_list ap; va_start(ap, fmt); - strcpy(str, "Fatal:"); - vsprintf(str + strlen(str), fmt, ap); + str = dupvprintf(fmt, ap); + str2 = dupcat("Fatal: ", str, "\n", NULL); + sfree(str); va_end(ap); - strcat(str, "\n"); - fprintf(stderr, str); + fputs(str2, stderr); + sfree(str2); - exit(1); + cleanup_exit(1); } -void connection_fatal(char *fmt, ...) +void modalfatalbox(char *fmt, ...) { - char str[0x100]; /* Make the size big enough */ + char *str, *str2; va_list ap; va_start(ap, fmt); - strcpy(str, "Fatal:"); - vsprintf(str + strlen(str), fmt, ap); + str = dupvprintf(fmt, ap); + str2 = dupcat("Fatal: ", str, "\n", NULL); + sfree(str); va_end(ap); - strcat(str, "\n"); - fprintf(stderr, str); + fputs(str2, stderr); + sfree(str2); - exit(1); + cleanup_exit(1); } - -void logevent(char *string) +void connection_fatal(void *frontend, char *fmt, ...) { + char *str, *str2; + va_list ap; + va_start(ap, fmt); + str = dupvprintf(fmt, ap); + str2 = dupcat("Fatal: ", str, "\n", NULL); + sfree(str); + va_end(ap); + fputs(str2, stderr); + sfree(str2); + + cleanup_exit(1); } -void ldisc_send(char *buf, int len, int interactive) +void ldisc_send(void *handle, char *buf, int len, int interactive) { /* * This is only here because of the calls to ldisc_send(NULL, @@ -1426,18 +1603,14 @@ void ldisc_send(char *buf, int len, int interactive) } /* - * Be told what socket we're supposed to be using. + * In psftp, all agent requests should be synchronous, so this is a + * never-called stub. */ -static SOCKET sftp_ssh_socket; -char *do_select(SOCKET skt, int startup) +void agent_schedule_callback(void (*callback)(void *, void *, int), + void *callback_ctx, void *data, int len) { - if (startup) - sftp_ssh_socket = skt; - else - sftp_ssh_socket = INVALID_SOCKET; - return NULL; + assert(!"We shouldn't be here"); } -extern int select_result(WPARAM, LPARAM); /* * Receive a block of data from the SSH link. Block until all data @@ -1452,11 +1625,13 @@ static unsigned char *outptr; /* where to put the data */ static unsigned outlen; /* how much data required */ static unsigned char *pending = NULL; /* any spare data */ static unsigned pendlen = 0, pendsize = 0; /* length and phys. size of buffer */ -int from_backend(int is_stderr, char *data, int datalen) +int from_backend(void *frontend, int is_stderr, const char *data, int datalen) { unsigned char *p = (unsigned char *) data; unsigned len = (unsigned) datalen; + assert(len > 0); + /* * stderr data is just spouted to local stderr and otherwise * ignored. @@ -1486,10 +1661,7 @@ int from_backend(int is_stderr, char *data, int datalen) if (len > 0) { if (pendsize < pendlen + len) { pendsize = pendlen + len + 4096; - pending = (pending ? srealloc(pending, pendsize) : - smalloc(pendsize)); - if (!pending) - fatalbox("Out of memory"); + pending = sresize(pending, pendsize, unsigned char); } memcpy(pending + pendlen, p, len); pendlen += len; @@ -1525,111 +1697,19 @@ int sftp_recvdata(char *buf, int len) } while (outlen > 0) { - fd_set readfds; - - FD_ZERO(&readfds); - FD_SET(sftp_ssh_socket, &readfds); - if (select(1, &readfds, NULL, NULL, NULL) < 0) + if (ssh_sftp_loop_iteration() < 0) return 0; /* doom */ - select_result((WPARAM) sftp_ssh_socket, (LPARAM) FD_READ); } return 1; } int sftp_senddata(char *buf, int len) { - back->send((unsigned char *) buf, len); + back->send(backhandle, (unsigned char *) buf, len); return 1; } /* - * Loop through the ssh connection and authentication process. - */ -static void ssh_sftp_init(void) -{ - if (sftp_ssh_socket == INVALID_SOCKET) - return; - while (!back->sendok()) { - fd_set readfds; - FD_ZERO(&readfds); - FD_SET(sftp_ssh_socket, &readfds); - if (select(1, &readfds, NULL, NULL, NULL) < 0) - return; /* doom */ - select_result((WPARAM) sftp_ssh_socket, (LPARAM) FD_READ); - } -} - -static char *password = NULL; -static int get_line(const char *prompt, char *str, int maxlen, int is_pw) -{ - HANDLE hin, hout; - DWORD savemode, newmode, i; - - if (password) { - static int tried_once = 0; - - if (tried_once) { - return 0; - } else { - strncpy(str, password, maxlen); - str[maxlen - 1] = '\0'; - tried_once = 1; - return 1; - } - } - - hin = GetStdHandle(STD_INPUT_HANDLE); - hout = GetStdHandle(STD_OUTPUT_HANDLE); - if (hin == INVALID_HANDLE_VALUE || hout == INVALID_HANDLE_VALUE) { - fprintf(stderr, "Cannot get standard input/output handles\n"); - exit(1); - } - - GetConsoleMode(hin, &savemode); - newmode = savemode | ENABLE_PROCESSED_INPUT | ENABLE_LINE_INPUT; - if (is_pw) - newmode &= ~ENABLE_ECHO_INPUT; - else - newmode |= ENABLE_ECHO_INPUT; - SetConsoleMode(hin, newmode); - - WriteFile(hout, prompt, strlen(prompt), &i, NULL); - ReadFile(hin, str, maxlen - 1, &i, NULL); - - SetConsoleMode(hin, savemode); - - if ((int) i > maxlen) - i = maxlen - 1; - else - i = i - 2; - str[i] = '\0'; - - if (is_pw) - WriteFile(hout, "\r\n", 2, &i, NULL); - - return 1; -} - -/* - * Initialize the Win$ock driver. - */ -static void init_winsock(void) -{ - WORD winsock_ver; - WSADATA wsadata; - - winsock_ver = MAKEWORD(1, 1); - if (WSAStartup(winsock_ver, &wsadata)) { - fprintf(stderr, "Unable to initialise WinSock"); - exit(1); - } - if (LOBYTE(wsadata.wVersion) != 1 || HIBYTE(wsadata.wVersion) != 1) { - fprintf(stderr, "WinSock version is incompatible with 1.1"); - exit(1); - } -} - -/* * Short description of parameters. */ static void usage(void) @@ -1642,68 +1722,25 @@ static void usage(void) printf(" -bc output batchfile commands\n"); printf(" -be don't stop batchfile processing if errors\n"); printf(" -v show verbose messages\n"); + printf(" -load sessname Load settings from saved session\n"); + printf(" -l user connect with specified username\n"); printf(" -P port connect to specified port\n"); printf(" -pw passw login with specified password\n"); - exit(1); + printf(" -1 -2 force use of particular SSH protocol version\n"); + printf(" -C enable compression\n"); + printf(" -i key private key file for authentication\n"); + printf(" -batch disable all interactive prompts\n"); + cleanup_exit(1); } /* - * Main program. Parse arguments etc. + * Connect to a host. */ -int main(int argc, char *argv[]) +static int psftp_connect(char *userhost, char *user, int portnumber) { - int i; - int portnumber = 0; - char *user, *host, *userhost, *realhost; - char *err; - int mode = 0; - int modeflags = 0; - char *batchfile = NULL; - - flags = FLAG_STDERR | FLAG_INTERACTIVE; - ssh_get_line = &get_line; - init_winsock(); - sk_init(); - - userhost = user = NULL; - - for (i = 1; i < argc; i++) { - if (argv[i][0] != '-') { - if (userhost) - usage(); - else - userhost = dupstr(argv[i]); - } else if (strcmp(argv[i], "-v") == 0) { - verbose = 1, flags |= FLAG_VERBOSE; - } else if (strcmp(argv[i], "-h") == 0 || - strcmp(argv[i], "-?") == 0) { - usage(); - } else if (strcmp(argv[i], "-l") == 0 && i + 1 < argc) { - user = argv[++i]; - } else if (strcmp(argv[i], "-P") == 0 && i + 1 < argc) { - portnumber = atoi(argv[++i]); - } else if (strcmp(argv[i], "-pw") == 0 && i + 1 < argc) { - password = argv[++i]; - } else if (strcmp(argv[i], "-b") == 0 && i + 1 < argc) { - mode = 1; - batchfile = argv[++i]; - } else if (strcmp(argv[i], "-bc") == 0 && i + 1 < argc) { - modeflags = modeflags | 1; - } else if (strcmp(argv[i], "-be") == 0 && i + 1 < argc) { - modeflags = modeflags | 2; - } else if (strcmp(argv[i], "--") == 0) { - i++; - break; - } else { - usage(); - } - } - argc -= i; - argv += i; - back = NULL; - - if (argc > 0 || !userhost) - usage(); + char *host, *realhost; + const char *err; + void *logctx; /* Separate host and username */ host = userhost; @@ -1726,10 +1763,23 @@ int main(int argc, char *argv[]) do_defaults(NULL, &cfg); strncpy(cfg.host, host, sizeof(cfg.host) - 1); cfg.host[sizeof(cfg.host) - 1] = '\0'; - cfg.port = 22; } /* + * Force use of SSH. (If they got the protocol wrong we assume the + * port is useless too.) + */ + if (cfg.protocol != PROT_SSH) { + cfg.protocol = PROT_SSH; + cfg.port = 22; + } + + /* + * Enact command-line overrides. + */ + cmdline_run_saved(&cfg); + + /* * Trim leading whitespace off the hostname if it's there. */ { @@ -1755,6 +1805,21 @@ int main(int argc, char *argv[]) */ cfg.host[strcspn(cfg.host, ":")] = '\0'; + /* + * Remove any remaining whitespace from the hostname. + */ + { + int p1 = 0, p2 = 0; + while (cfg.host[p2] != '\0') { + if (cfg.host[p2] != ' ' && cfg.host[p2] != '\t') { + cfg.host[p1] = cfg.host[p2]; + p1++; + } + p2++; + } + cfg.host[p1] = '\0'; + } + /* Set username */ if (user != NULL && user[0] != '\0') { strncpy(cfg.username, user, sizeof(cfg.username) - 1); @@ -1762,9 +1827,10 @@ int main(int argc, char *argv[]) } if (!cfg.username[0]) { printf("login as: "); + fflush(stdout); if (!fgets(cfg.username, sizeof(cfg.username), stdin)) { fprintf(stderr, "psftp: aborting\n"); - exit(1); + cleanup_exit(1); } else { int len = strlen(cfg.username); if (cfg.username[len - 1] == '\n') @@ -1772,9 +1838,6 @@ int main(int argc, char *argv[]) } } - if (cfg.protocol != PROT_SSH) - cfg.port = 22; - if (portnumber) cfg.port = portnumber; @@ -1820,23 +1883,123 @@ int main(int argc, char *argv[]) back = &ssh_backend; - err = back->init(cfg.host, cfg.port, &realhost, 0); + err = back->init(NULL, &backhandle, &cfg, cfg.host, cfg.port, &realhost,0); if (err != NULL) { - fprintf(stderr, "ssh_init: %s", err); + fprintf(stderr, "ssh_init: %s\n", err); return 1; } - ssh_sftp_init(); + logctx = log_init(NULL, &cfg); + back->provide_logctx(backhandle, logctx); + console_provide_logctx(logctx); + while (!back->sendok(backhandle)) { + if (ssh_sftp_loop_iteration() < 0) { + fprintf(stderr, "ssh_init: error during SSH connection setup\n"); + return 1; + } + } if (verbose && realhost != NULL) printf("Connected to %s\n", realhost); + return 0; +} + +void cmdline_error(char *p, ...) +{ + va_list ap; + fprintf(stderr, "psftp: "); + va_start(ap, p); + vfprintf(stderr, p, ap); + va_end(ap); + fprintf(stderr, "\n try typing \"psftp -h\" for help\n"); + exit(1); +} + +/* + * Main program. Parse arguments etc. + */ +int psftp_main(int argc, char *argv[]) +{ + int i; + int portnumber = 0; + char *userhost, *user; + int mode = 0; + int modeflags = 0; + char *batchfile = NULL; + int errors = 0; + + flags = FLAG_STDERR | FLAG_INTERACTIVE +#ifdef FLAG_SYNCAGENT + | FLAG_SYNCAGENT +#endif + ; + cmdline_tooltype = TOOLTYPE_FILETRANSFER; + ssh_get_line = &console_get_line; + sk_init(); + + userhost = user = NULL; + + errors = 0; + for (i = 1; i < argc; i++) { + int ret; + if (argv[i][0] != '-') { + if (userhost) + usage(); + else + userhost = dupstr(argv[i]); + continue; + } + ret = cmdline_process_param(argv[i], i+1socket() != NULL) { + if (back != NULL && back->socket(backhandle) != NULL) { char ch; - back->special(TS_EOF); + back->special(backhandle, TS_EOF); sftp_recvdata(&ch, 1); } - WSACleanup(); random_save_seed(); return 0;