X-Git-Url: https://asedeno.scripts.mit.edu/gitweb/?a=blobdiff_plain;f=psftp.c;h=9637e2e015a361c3378ec46ed0be2620a9e24408;hb=693bbf0ec6007120b91dafb4eaed1ae833eb59f7;hp=ac8a938d8eb11554ae6b1d2c510f76ec0a54fdcc;hpb=bee5812a496b6d24b6ac5bb13e8aa4de1d2f61b7;p=PuTTY.git diff --git a/psftp.c b/psftp.c index ac8a938d..9637e2e0 100644 --- a/psftp.c +++ b/psftp.c @@ -40,14 +40,6 @@ static Config cfg; * Higher-level helper functions used in commands. */ -/* - * Determine whether a string is entirely composed of dots. - */ -static int is_dots(char *str) -{ - return str[strspn(str, ".")] == '\0'; -} - /* * Attempt to canonify a pathname starting from the pwd. If * canonification fails, at least fall back to returning a _valid_ @@ -200,7 +192,8 @@ static int bare_name_compare(const void *av, const void *bv) /* ---------------------------------------------------------------------- * The meat of the `get' and `put' commands. */ -int sftp_get_file(char *fname, char *outfname, int recurse, int restart) +int sftp_get_file(char *fname, char *outfname, int recurse, int restart, + char *wildcard) { struct fxp_handle *fh; struct sftp_packet *pktin; @@ -208,24 +201,29 @@ int sftp_get_file(char *fname, char *outfname, int recurse, int restart) struct fxp_xfer *xfer; uint64 offset; FILE *fp; - int ret; + int ret, shown_err = FALSE; /* * In recursive mode, see if we're dealing with a directory. * (If we're not in recursive mode, we need not even check: the * subsequent FXP_OPEN will return a usable error message.) */ - if (recurse) { + if (wildcard || recurse) { struct fxp_attrs attrs; int result; - 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) && - (attrs.permissions & 0040000)) { + if (!wildcard) { + sftp_register(req = fxp_stat_send(fname)); + rreq = sftp_find_request(pktin = sftp_recv()); + assert(rreq == req); + result = fxp_stat_recv(pktin, rreq, &attrs); + } else + result = 0; /* placate optimisers */ + + if (wildcard || + (result && + (attrs.flags & SSH_FILEXFER_ATTR_PERMISSIONS) && + (attrs.permissions & 0040000))) { struct fxp_handle *dirhandle; int nnames, namesize; @@ -235,9 +233,11 @@ int sftp_get_file(char *fname, char *outfname, int recurse, int restart) /* * First, attempt to create the destination directory, - * unless it already exists. + * unless it already exists (or this is a wildcard + * run). */ - if (file_type(outfname) != FILE_TYPE_DIRECTORY && + if (!wildcard && + file_type(outfname) != FILE_TYPE_DIRECTORY && !create_directory(outfname)) { printf("%s: Cannot create directory\n", outfname); return 0; @@ -283,8 +283,19 @@ int sftp_get_file(char *fname, char *outfname, int recurse, int restart) ournames = sresize(ournames, namesize, struct fxp_name *); } for (i = 0; i < names->nnames; i++) - if (!is_dots(names->names[i].filename)) - ournames[nnames++] = fxp_dup_name(&names->names[i]); + if (strcmp(names->names[i].filename, ".") && + strcmp(names->names[i].filename, "..") && + (!wildcard || wc_match(wildcard, + names->names[i].filename))) { + if (!vet_filename(names->names[i].filename)) { + printf("ignoring potentially dangerous server-" + "supplied filename '%s'\n", + names->names[i].filename); + } else { + ournames[nnames++] = + fxp_dup_name(&names->names[i]); + } + } fxp_free_names(names); } sftp_register(req = fxp_close_send(dirhandle)); @@ -292,6 +303,14 @@ int sftp_get_file(char *fname, char *outfname, int recurse, int restart) assert(rreq == req); fxp_close_recv(pktin, rreq); + /* + * A polite warning if nothing at all matched the + * wildcard. + */ + if (wildcard && !nnames) { + printf("%s: nothing matched\n", wildcard); + } + /* * Sort the names into a clear order. This ought to * make things more predictable when we're doing a @@ -313,7 +332,11 @@ int sftp_get_file(char *fname, char *outfname, int recurse, int restart) while (i < nnames) { char *nextoutfname; int ret; - nextoutfname = dir_file_cat(outfname, ournames[i]->filename); + if (outfname) + nextoutfname = dir_file_cat(outfname, + ournames[i]->filename); + else + nextoutfname = dupstr(ournames[i]->filename); ret = (file_type(nextoutfname) == FILE_TYPE_NONEXISTENT); sfree(nextoutfname); if (ret) @@ -334,8 +357,13 @@ int sftp_get_file(char *fname, char *outfname, int recurse, int restart) int ret; nextfname = dupcat(fname, "/", ournames[i]->filename, NULL); - nextoutfname = dir_file_cat(outfname, ournames[i]->filename); - ret = sftp_get_file(nextfname, nextoutfname, recurse, restart); + if (outfname) + nextoutfname = dir_file_cat(outfname, + ournames[i]->filename); + else + nextoutfname = dupstr(ournames[i]->filename); + ret = sftp_get_file(nextfname, nextoutfname, + recurse, restart, NULL); restart = FALSE; /* after first partial file, do full */ sfree(nextoutfname); sfree(nextfname); @@ -415,7 +443,10 @@ int sftp_get_file(char *fname, char *outfname, int recurse, int restart) ret = xfer_download_gotpkt(xfer, pktin); if (ret < 0) { - printf("error while reading: %s\n", fxp_error()); + if (!shown_err) { + printf("error while reading: %s\n", fxp_error()); + shown_err = TRUE; + } ret = 0; } @@ -453,7 +484,8 @@ int sftp_get_file(char *fname, char *outfname, int recurse, int restart) return ret; } -int sftp_put_file(char *fname, char *outfname, int recurse, int restart) +int sftp_put_file(char *fname, char *outfname, int recurse, int restart, + char *wildcard) { struct fxp_handle *fh; struct fxp_xfer *xfer; @@ -468,7 +500,7 @@ int sftp_put_file(char *fname, char *outfname, int recurse, int restart) * (If we're not in recursive mode, we need not even check: the * subsequent fopen will return an error message.) */ - if (recurse && file_type(fname) == FILE_TYPE_DIRECTORY) { + if (wildcard || (recurse && file_type(fname) == FILE_TYPE_DIRECTORY)) { struct fxp_attrs attrs; int result; int nnames, namesize; @@ -476,46 +508,73 @@ int sftp_put_file(char *fname, char *outfname, int recurse, int restart) DirHandle *dh; int i; - /* - * First, attempt to create the destination directory, - * unless it already exists. - */ - sftp_register(req = fxp_stat_send(outfname)); - 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)) { - sftp_register(req = fxp_mkdir_send(outfname)); + if (!wildcard) { + /* + * First, attempt to create the destination directory, + * unless it already exists. + */ + sftp_register(req = fxp_stat_send(outfname)); rreq = sftp_find_request(pktin = sftp_recv()); assert(rreq == req); - result = fxp_mkdir_recv(pktin, rreq); + result = fxp_stat_recv(pktin, rreq, &attrs); + if (!result || + !(attrs.flags & SSH_FILEXFER_ATTR_PERMISSIONS) || + !(attrs.permissions & 0040000)) { + sftp_register(req = fxp_mkdir_send(outfname)); + rreq = sftp_find_request(pktin = sftp_recv()); + assert(rreq == req); + result = fxp_mkdir_recv(pktin, rreq); - if (!result) { - printf("%s: create directory: %s\n", outfname, fxp_error()); - return 0; + if (!result) { + printf("%s: create directory: %s\n", + outfname, fxp_error()); + return 0; + } } } /* * Now get the list of filenames in the local directory. */ - dh = open_directory(fname); - if (!dh) { - printf("%s: unable to open directory\n", fname); - return 0; - } nnames = namesize = 0; ournames = NULL; - while ((name = read_filename(dh)) != NULL) { - if (nnames >= namesize) { - namesize += 128; - ournames = sresize(ournames, namesize, char *); + if (wildcard) { + WildcardMatcher *wcm; + + wcm = begin_wildcard_matching(wildcard); + if (wcm) { + while ((name = wildcard_get_filename(wcm)) != NULL) { + if (nnames >= namesize) { + namesize += 128; + ournames = sresize(ournames, namesize, char *); + } + ournames[nnames++] = name; + } + finish_wildcard_matching(wcm); } - ournames[nnames++] = name; + } else { + dh = open_directory(fname); + if (!dh) { + printf("%s: unable to open directory\n", fname); + return 0; + } + while ((name = read_filename(dh)) != NULL) { + if (nnames >= namesize) { + namesize += 128; + ournames = sresize(ournames, namesize, char *); + } + ournames[nnames++] = name; + } + close_directory(dh); + } + + /* + * A polite warning if nothing at all matched the + * wildcard. + */ + if (wildcard && !nnames) { + printf("%s: nothing matched\n", wildcard); } - close_directory(dh); /* * Sort the names into a clear order. This ought to make @@ -559,9 +618,13 @@ int sftp_put_file(char *fname, char *outfname, int recurse, int restart) char *nextfname, *nextoutfname; int ret; - nextfname = dir_file_cat(fname, ournames[i]); + if (fname) + nextfname = dir_file_cat(fname, ournames[i]); + else + nextfname = dupstr(ournames[i]); nextoutfname = dupcat(outfname, "/", ournames[i], NULL); - ret = sftp_put_file(nextfname, nextoutfname, recurse, restart); + ret = sftp_put_file(nextfname, nextoutfname, + recurse, restart, NULL); restart = FALSE; /* after first partial file, do full */ sfree(nextoutfname); sfree(nextfname); @@ -708,6 +771,23 @@ int sftp_cmd_quit(struct sftp_command *cmd) return -1; } +int sftp_cmd_close(struct sftp_command *cmd) +{ + if (back == NULL) { + printf("psftp: not connected to a host; use \"open host.name\"\n"); + return 0; + } + + if (back != NULL && back->socket(backhandle) != NULL) { + char ch; + back->special(backhandle, TS_EOF); + sftp_recvdata(&ch, 1); + } + do_sftp_cleanup(); + + return 0; +} + /* * List a directory. If no arguments are given, list pwd; otherwise * list the directory given in words[1]. @@ -718,7 +798,7 @@ int sftp_cmd_ls(struct sftp_command *cmd) struct fxp_names *names; struct fxp_name **ournames; int nnames, namesize; - char *dir, *cdir; + char *dir, *cdir, *unwcdir, *wildcard; struct sftp_packet *pktin; struct sftp_request *req, *rreq; int i; @@ -733,9 +813,35 @@ int sftp_cmd_ls(struct sftp_command *cmd) else dir = cmd->words[1]; + unwcdir = snewn(1 + strlen(dir), char); + if (wc_unescape(unwcdir, dir)) { + dir = unwcdir; + wildcard = NULL; + } else { + char *tmpdir; + int len, check; + + wildcard = stripslashes(dir, 0); + unwcdir = dupstr(dir); + len = wildcard - dir; + unwcdir[len] = '\0'; + if (len > 0 && unwcdir[len-1] == '/') + unwcdir[len-1] = '\0'; + tmpdir = snewn(1 + len, char); + check = wc_unescape(tmpdir, unwcdir); + sfree(tmpdir); + if (!check) { + printf("Multiple-level wildcards are not supported\n"); + sfree(unwcdir); + return 0; + } + dir = unwcdir; + } + cdir = canonify(dir); if (!cdir) { printf("%s: %s\n", dir, fxp_error()); + sfree(unwcdir); return 0; } @@ -776,7 +882,8 @@ int sftp_cmd_ls(struct sftp_command *cmd) } for (i = 0; i < names->nnames; i++) - ournames[nnames++] = fxp_dup_name(&names->names[i]); + if (!wildcard || wc_match(wildcard, names->names[i].filename)) + ournames[nnames++] = fxp_dup_name(&names->names[i]); fxp_free_names(names); } @@ -802,6 +909,7 @@ int sftp_cmd_ls(struct sftp_command *cmd) } sfree(cdir); + sfree(unwcdir); return 1; } @@ -870,14 +978,17 @@ int sftp_cmd_pwd(struct sftp_command *cmd) } /* - * Get a file and save it at the local end. We have two very - * similar commands here: `get' and `reget', which differ in that - * `reget' checks for the existence of the destination file and - * starts from where a previous aborted transfer left off. + * Get a file and save it at the local end. We have three very + * similar commands here. The basic one is `get'; `reget' differs + * in that it checks for the existence of the destination file and + * starts from where a previous aborted transfer left off; `mget' + * differs in that it interprets all its arguments as files to + * transfer (never as a different local name for a remote file) and + * can handle wildcards. */ -int sftp_general_get(struct sftp_command *cmd, int restart) +int sftp_general_get(struct sftp_command *cmd, int restart, int multiple) { - char *fname, *origfname, *outfname; + char *fname, *unwcfname, *origfname, *outfname; int i, ret; int recurse = FALSE; @@ -895,49 +1006,73 @@ int sftp_general_get(struct sftp_command *cmd, int restart) } else if (!strcmp(cmd->words[i], "-r")) { recurse = TRUE; } else { - printf("get: unrecognised option '%s'\n", cmd->words[i]); + printf("%s: unrecognised option '%s'\n", cmd->words[0], cmd->words[i]); return 0; } i++; } if (i >= cmd->nwords) { - printf("get: expects a filename\n"); + printf("%s: expects a filename\n", cmd->words[0]); return 0; } - origfname = cmd->words[i++]; - fname = canonify(origfname); - if (!fname) { - printf("%s: %s\n", origfname, fxp_error()); - return 0; - } + do { + unwcfname = NULL; + origfname = cmd->words[i++]; + + if (multiple && + !wc_unescape(unwcfname = snewn(strlen(origfname)+1, char), + origfname)) { + ret = sftp_get_file(pwd, NULL, recurse, restart, origfname); + } else { + fname = canonify(origfname); + if (!fname) { + printf("%s: %s\n", origfname, fxp_error()); + sfree(unwcfname); + return 0; + } - outfname = (i >= cmd->nwords ? - stripslashes(origfname, 0) : cmd->words[i++]); + if (!multiple && i < cmd->nwords) + outfname = cmd->words[i++]; + else + outfname = stripslashes(origfname, 0); - ret = sftp_get_file(fname, outfname, recurse, restart); + ret = sftp_get_file(fname, outfname, recurse, restart, NULL); - sfree(fname); + sfree(fname); + } + sfree(unwcfname); + if (!ret) + return ret; + + } while (multiple && i < cmd->nwords); return ret; } int sftp_cmd_get(struct sftp_command *cmd) { - return sftp_general_get(cmd, 0); + return sftp_general_get(cmd, 0, 0); +} +int sftp_cmd_mget(struct sftp_command *cmd) +{ + return sftp_general_get(cmd, 0, 1); } int sftp_cmd_reget(struct sftp_command *cmd) { - return sftp_general_get(cmd, 1); + return sftp_general_get(cmd, 1, 0); } /* - * Send a file and store it at the remote end. We have two very - * similar commands here: `put' and `reput', which differ in that - * `reput' checks for the existence of the destination file and - * starts from where a previous aborted transfer left off. + * Send a file and store it at the remote end. We have three very + * similar commands here. The basic one is `put'; `reput' differs + * in that it checks for the existence of the destination file and + * starts from where a previous aborted transfer left off; `mput' + * differs in that it interprets all its arguments as files to + * transfer (never as a different remote name for a local file) and + * can handle wildcards. */ -int sftp_general_put(struct sftp_command *cmd, int restart) +int sftp_general_put(struct sftp_command *cmd, int restart, int multiple) { char *fname, *origoutfname, *outfname; int i, ret; @@ -957,39 +1092,54 @@ int sftp_general_put(struct sftp_command *cmd, int restart) } else if (!strcmp(cmd->words[i], "-r")) { recurse = TRUE; } else { - printf("put: unrecognised option '%s'\n", cmd->words[i]); + printf("%s: unrecognised option '%s'\n", cmd->words[0], cmd->words[i]); return 0; } i++; } if (i >= cmd->nwords) { - printf("put: expects a filename\n"); + printf("%s: expects a filename\n", cmd->words[0]); return 0; } - fname = cmd->words[i++]; - origoutfname = (i >= cmd->nwords ? - stripslashes(fname, 1) : cmd->words[i++]); - outfname = canonify(origoutfname); - if (!outfname) { - printf("%s: %s\n", origoutfname, fxp_error()); - return 0; - } + do { + fname = cmd->words[i++]; - ret = sftp_put_file(fname, outfname, recurse, restart); + if (multiple && test_wildcard(fname, FALSE) == WCTYPE_WILDCARD) { + ret = sftp_put_file(NULL, pwd, recurse, restart, fname); + } else { + if (!multiple && i < cmd->nwords) + origoutfname = cmd->words[i++]; + else + origoutfname = stripslashes(fname, 1); + + outfname = canonify(origoutfname); + if (!outfname) { + printf("%s: %s\n", origoutfname, fxp_error()); + return 0; + } + ret = sftp_put_file(fname, outfname, recurse, restart, NULL); + sfree(outfname); + } + if (!ret) + return ret; - sfree(outfname); + } while (multiple && i < cmd->nwords); return ret; } int sftp_cmd_put(struct sftp_command *cmd) { - return sftp_general_put(cmd, 0); + return sftp_general_put(cmd, 0, 0); +} +int sftp_cmd_mput(struct sftp_command *cmd) +{ + return sftp_general_put(cmd, 0, 1); } int sftp_cmd_reput(struct sftp_command *cmd) { - return sftp_general_put(cmd, 1); + return sftp_general_put(cmd, 1, 0); } int sftp_cmd_mkdir(struct sftp_command *cmd) @@ -1502,6 +1652,14 @@ static struct sftp_cmd_lookup { " use commas to separate different modifiers (\"u+rwx,g+s\").\n", sftp_cmd_chmod }, + { + "close", TRUE, "finish your SFTP session but do not quit PSFTP", + "\n" + " Terminates your SFTP session, but does not quit the PSFTP\n" + " program. You can then use \"open\" to start another SFTP\n" + " session, to the same server or to a different one.\n", + sftp_cmd_close + }, { "del", TRUE, "delete a file", " \n" @@ -1513,10 +1671,12 @@ static struct sftp_cmd_lookup { }, { "dir", TRUE, "list contents of a remote directory", - " [ ]\n" + " [ ]/[ ]\n" " List the contents of a specified directory on the server.\n" " If is not given, the current working directory\n" - " will be listed.\n", + " is assumed.\n" + " If is given, it is treated as a set of files to\n" + " list; otherwise, all files are listed.\n", sftp_cmd_ls }, { @@ -1524,10 +1684,11 @@ static struct sftp_cmd_lookup { }, { "get", TRUE, "download a file from the server to your local machine", - " [ ]\n" + " [ -r ] [ -- ] [ ]\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" - " argument .\n", + " argument .\n" + " If -r specified, recursively fetch a directory.\n", sftp_cmd_get }, { @@ -1556,12 +1717,30 @@ static struct sftp_cmd_lookup { "ls", TRUE, "dir", NULL, sftp_cmd_ls }, + { + "mget", TRUE, "download multiple files at once", + " [ -r ] [ -- ] [ ... ]\n" + " Downloads many files from the server, storing each one under\n" + " the same name it has on the server side. You can use wildcards\n" + " such as \"*.c\" to specify lots of files at once.\n" + " If -r specified, recursively fetch files and directories.\n", + sftp_cmd_mget + }, { "mkdir", TRUE, "create a directory on the remote server", " \n" " Creates a directory with the given name on the server.\n", sftp_cmd_mkdir }, + { + "mput", TRUE, "upload multiple files at once", + " [ -r ] [ -- ] [ ... ]\n" + " Uploads many files to the server, storing each one under the\n" + " same name it has on the client side. You can use wildcards\n" + " such as \"*.c\" to specify lots of files at once.\n" + " If -r specified, recursively store files and directories.\n", + sftp_cmd_mput + }, { "mv", TRUE, "move or rename a file on the remote server", " \n" @@ -1579,10 +1758,11 @@ static struct sftp_cmd_lookup { }, { "put", TRUE, "upload a file from your local machine to the server", - " [ ]\n" + " [ -r ] [ -- ] [ ]\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" - " argument .\n", + " argument .\n" + " If -r specified, recursively store a directory.\n", sftp_cmd_put }, { @@ -1597,10 +1777,11 @@ static struct sftp_cmd_lookup { }, { "reget", TRUE, "continue downloading a file", - " [ ]\n" + " [ -r ] [ -- ] [ ]\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" - " file. This is for resuming a download that was interrupted.\n", + " file. This is for resuming a download that was interrupted.\n" + " If -r specified, resume interrupted \"get -r\".\n", sftp_cmd_reget }, { @@ -1613,10 +1794,11 @@ static struct sftp_cmd_lookup { }, { "reput", TRUE, "continue uploading a file", - " [ ]\n" + " [ -r ] [ -- ] [ ]\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" - " file. This is for resuming an upload that was interrupted.\n", + " file. This is for resuming an upload that was interrupted.\n" + " If -r specified, resume interrupted \"put -r\".\n", sftp_cmd_reput }, { @@ -1721,7 +1903,7 @@ struct sftp_command *sftp_getcmd(FILE *fp, int mode, int modeflags) printf("psftp> "); line = fgetline(fp); } else { - line = ssh_sftp_get_cmdline("psftp> "); + line = ssh_sftp_get_cmdline("psftp> ", back == NULL); } if (!line || !*line) { @@ -1859,6 +2041,8 @@ void do_sftp_cleanup() sftp_recvdata(&ch, 1); back->free(backhandle); sftp_cleanup_request(); + back = NULL; + backhandle = NULL; } if (pwd) { sfree(pwd); @@ -2432,12 +2616,10 @@ int psftp_main(int argc, char *argv[]) back->special(backhandle, TS_EOF); sftp_recvdata(&ch, 1); } + do_sftp_cleanup(); random_save_seed(); cmdline_cleanup(); console_provide_logctx(NULL); - do_sftp_cleanup(); - backhandle = NULL; - back = NULL; sk_cleanup(); return 0;