X-Git-Url: https://git.distorted.org.uk/u/mdw/putty/blobdiff_plain/5079ee6d7c8f3fa6bafb42e0c56edfff2ddff2b9..6226c9390d23d6868edde63b9568891cd88631cc:/psftp.c diff --git a/psftp.c b/psftp.c index dc5073ef..d07ee8b0 100644 --- a/psftp.c +++ b/psftp.c @@ -189,6 +189,11 @@ static int bare_name_compare(const void *av, const void *bv) return strcmp(*a, *b); } +static void not_connected(void) +{ + printf("psftp: not connected to a host; use \"open host.name\"\n"); +} + /* ---------------------------------------------------------------------- * The meat of the `get' and `put' commands. */ @@ -199,7 +204,7 @@ int sftp_get_file(char *fname, char *outfname, int recurse, int restart) struct sftp_request *req, *rreq; struct fxp_xfer *xfer; uint64 offset; - FILE *fp; + WFile *file; int ret, shown_err = FALSE; /* @@ -376,17 +381,17 @@ int sftp_get_file(char *fname, char *outfname, int recurse, int restart) fh = fxp_open_recv(pktin, rreq); if (!fh) { - printf("%s: %s\n", fname, fxp_error()); + printf("%s: open for read: %s\n", fname, fxp_error()); return 0; } if (restart) { - fp = fopen(outfname, "rb+"); + file = open_existing_wfile(outfname, NULL); } else { - fp = fopen(outfname, "wb"); + file = open_new_file(outfname); } - if (!fp) { + if (!file) { printf("local: unable to open %s\n", outfname); sftp_register(req = fxp_close_send(fh)); @@ -398,11 +403,21 @@ int sftp_get_file(char *fname, char *outfname, int recurse, int restart) } if (restart) { - long posn; - fseek(fp, 0L, SEEK_END); - posn = ftell(fp); - printf("reget: restarting at file position %ld\n", posn); - offset = uint64_make(0, posn); + char decbuf[30]; + if (seek_file(file, uint64_make(0,0) , FROM_END) == -1) { + printf("reget: cannot restart %s - file too large\n", + outfname); + sftp_register(req = fxp_close_send(fh)); + rreq = sftp_find_request(pktin = sftp_recv()); + assert(rreq == req); + fxp_close_recv(pktin, rreq); + + return 0; + } + + offset = get_file_posn(file); + uint64_decimal(offset, decbuf); + printf("reget: restarting at file position %s\n", decbuf); } else { offset = uint64_make(0, 0); } @@ -437,7 +452,7 @@ int sftp_get_file(char *fname, char *outfname, int recurse, int restart) wpos = 0; while (wpos < len) { - wlen = fwrite(buf + wpos, 1, len - wpos, fp); + wlen = write_to_file(file, buf + wpos, len - wpos); if (wlen <= 0) { printf("error while writing local file\n"); ret = 0; @@ -456,7 +471,7 @@ int sftp_get_file(char *fname, char *outfname, int recurse, int restart) xfer_cleanup(xfer); - fclose(fp); + close_wfile(file); sftp_register(req = fxp_close_send(fh)); rreq = sftp_find_request(pktin = sftp_recv()); @@ -473,7 +488,7 @@ int sftp_put_file(char *fname, char *outfname, int recurse, int restart) struct sftp_packet *pktin; struct sftp_request *req, *rreq; uint64 offset; - FILE *fp; + RFile *file; int ret, err, eof; /* @@ -603,8 +618,8 @@ int sftp_put_file(char *fname, char *outfname, int recurse, int restart) return 1; } - fp = fopen(fname, "rb"); - if (!fp) { + file = open_existing_file(fname, NULL, NULL, NULL); + if (!file) { printf("local: unable to open %s\n", fname); return 0; } @@ -619,7 +634,7 @@ int sftp_put_file(char *fname, char *outfname, int recurse, int restart) fh = fxp_open_recv(pktin, rreq); if (!fh) { - printf("%s: %s\n", outfname, fxp_error()); + printf("%s: open for write: %s\n", outfname, fxp_error()); return 0; } @@ -644,12 +659,9 @@ int sftp_put_file(char *fname, char *outfname, int recurse, int restart) offset = attrs.size; uint64_decimal(offset, decbuf); printf("reput: restarting at file position %s\n", decbuf); - if (uint64_compare(offset, uint64_make(0, LONG_MAX)) > 0) { - printf("reput: remote file is larger than we can deal with\n"); - return 0; - } - if (fseek(fp, offset.lo, SEEK_SET) != 0) - fseek(fp, 0, SEEK_END); /* *shrug* */ + + if (seek_file((WFile *)file, offset, FROM_START) != 0) + seek_file((WFile *)file, uint64_make(0,0), FROM_END); /* *shrug* */ } else { offset = uint64_make(0, 0); } @@ -668,7 +680,7 @@ int sftp_put_file(char *fname, char *outfname, int recurse, int restart) int len, ret; while (xfer_upload_ready(xfer) && !err && !eof) { - len = fread(buffer, 1, sizeof(buffer), fp); + len = read_from_file(file, buffer, sizeof(buffer)); if (len == -1) { printf("error while reading local file\n"); err = 1; @@ -696,7 +708,7 @@ int sftp_put_file(char *fname, char *outfname, int recurse, int restart) assert(rreq == req); fxp_close_recv(pktin, rreq); - fclose(fp); + close_rfile(file); return ret; } @@ -817,7 +829,9 @@ char *sftp_wildcard_get_filename(SftpWildcardMatcher *swcm) * We have a working filename. Return it. */ return dupprintf("%s%s%s", swcm->prefix, - swcm->prefix[strlen(swcm->prefix)-1]=='/' ? "" : "/", + (!swcm->prefix[0] || + swcm->prefix[strlen(swcm->prefix)-1]=='/' ? + "" : "/"), name->filename); } } @@ -841,6 +855,71 @@ void sftp_finish_wildcard_matching(SftpWildcardMatcher *swcm) sfree(swcm); } +/* + * General function to match a potential wildcard in a filename + * argument and iterate over every matching file. Used in several + * PSFTP commands (rmdir, rm, chmod, mv). + */ +int wildcard_iterate(char *filename, int (*func)(void *, char *), void *ctx) +{ + char *unwcfname, *newname, *cname; + int is_wc, ret; + + unwcfname = snewn(strlen(filename)+1, char); + is_wc = !wc_unescape(unwcfname, filename); + + if (is_wc) { + SftpWildcardMatcher *swcm = sftp_begin_wildcard_matching(filename); + int matched = FALSE; + sfree(unwcfname); + + if (!swcm) + return 0; + + ret = 1; + + while ( (newname = sftp_wildcard_get_filename(swcm)) != NULL ) { + cname = canonify(newname); + if (!cname) { + printf("%s: canonify: %s\n", newname, fxp_error()); + ret = 0; + } + matched = TRUE; + ret &= func(ctx, cname); + sfree(cname); + } + + if (!matched) { + /* Politely warn the user that nothing matched. */ + printf("%s: nothing matched\n", filename); + } + + sftp_finish_wildcard_matching(swcm); + } else { + cname = canonify(unwcfname); + if (!cname) { + printf("%s: canonify: %s\n", filename, fxp_error()); + ret = 0; + } + ret = func(ctx, cname); + sfree(cname); + sfree(unwcfname); + } + + return ret; +} + +/* + * Handy helper function. + */ +int is_wildcard(char *name) +{ + char *unwcfname = snewn(strlen(name)+1, char); + int is_wc = !wc_unescape(unwcfname, name); + sfree(unwcfname); + return is_wc; +} + /* ---------------------------------------------------------------------- * Actual sftp commands. */ @@ -869,11 +948,11 @@ int sftp_cmd_quit(struct sftp_command *cmd) int sftp_cmd_close(struct sftp_command *cmd) { if (back == NULL) { - printf("psftp: not connected to a host; use \"open host.name\"\n"); + not_connected(); return 0; } - if (back != NULL && back->socket(backhandle) != NULL) { + if (back != NULL && back->connected(backhandle)) { char ch; back->special(backhandle, TS_EOF); sftp_recvdata(&ch, 1); @@ -899,7 +978,7 @@ int sftp_cmd_ls(struct sftp_command *cmd) int i; if (back == NULL) { - printf("psftp: not connected to a host; use \"open host.name\"\n"); + not_connected(); return 0; } @@ -935,7 +1014,7 @@ int sftp_cmd_ls(struct sftp_command *cmd) cdir = canonify(dir); if (!cdir) { - printf("%s: %s\n", dir, fxp_error()); + printf("%s: canonify: %s\n", dir, fxp_error()); sfree(unwcdir); return 0; } @@ -1021,7 +1100,7 @@ int sftp_cmd_cd(struct sftp_command *cmd) char *dir; if (back == NULL) { - printf("psftp: not connected to a host; use \"open host.name\"\n"); + not_connected(); return 0; } @@ -1031,7 +1110,7 @@ int sftp_cmd_cd(struct sftp_command *cmd) dir = canonify(cmd->words[1]); if (!dir) { - printf("%s: %s\n", dir, fxp_error()); + printf("%s: canonify: %s\n", dir, fxp_error()); return 0; } @@ -1064,7 +1143,7 @@ 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"); + not_connected(); return 0; } @@ -1088,7 +1167,7 @@ int sftp_general_get(struct sftp_command *cmd, int restart, int multiple) int recurse = FALSE; if (back == NULL) { - printf("psftp: not connected to a host; use \"open host.name\"\n"); + not_connected(); return 0; } @@ -1142,7 +1221,7 @@ int sftp_general_get(struct sftp_command *cmd, int restart, int multiple) fname = canonify(origwfname); if (!fname) { - printf("%s: %s\n", origwfname, fxp_error()); + printf("%s: canonify: %s\n", origwfname, fxp_error()); sfree(unwcfname); return 0; } @@ -1202,7 +1281,7 @@ int sftp_general_put(struct sftp_command *cmd, int restart, int multiple) int recurse = FALSE; if (back == NULL) { - printf("psftp: not connected to a host; use \"open host.name\"\n"); + not_connected(); return 0; } @@ -1253,7 +1332,7 @@ int sftp_general_put(struct sftp_command *cmd, int restart, int multiple) outfname = canonify(origoutfname); if (!outfname) { - printf("%s: %s\n", origoutfname, fxp_error()); + printf("%s: canonify: %s\n", origoutfname, fxp_error()); if (wcm) { sfree(wfname); finish_wildcard_matching(wcm); @@ -1300,9 +1379,10 @@ int sftp_cmd_mkdir(struct sftp_command *cmd) struct sftp_packet *pktin; struct sftp_request *req, *rreq; int result; + int i, ret; if (back == NULL) { - printf("psftp: not connected to a host; use \"open host.name\"\n"); + not_connected(); return 0; } @@ -1311,36 +1391,58 @@ int sftp_cmd_mkdir(struct sftp_command *cmd) return 0; } - dir = canonify(cmd->words[1]); - if (!dir) { - printf("%s: %s\n", dir, fxp_error()); - return 0; + ret = 1; + for (i = 1; i < cmd->nwords; i++) { + dir = canonify(cmd->words[i]); + if (!dir) { + printf("%s: canonify: %s\n", dir, fxp_error()); + return 0; + } + + 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()); + ret = 0; + } else + printf("mkdir %s: OK\n", dir); + + sfree(dir); } - sftp_register(req = fxp_mkdir_send(dir)); + return ret; +} + +static int sftp_action_rmdir(void *vctx, char *dir) +{ + struct sftp_packet *pktin; + struct sftp_request *req, *rreq; + int result; + + sftp_register(req = fxp_rmdir_send(dir)); rreq = sftp_find_request(pktin = sftp_recv()); assert(rreq == req); - result = fxp_mkdir_recv(pktin, rreq); + result = fxp_rmdir_recv(pktin, rreq); if (!result) { - printf("mkdir %s: %s\n", dir, fxp_error()); - sfree(dir); + printf("rmdir %s: %s\n", dir, fxp_error()); return 0; } - sfree(dir); + printf("rmdir %s: OK\n", dir); + return 1; } int sftp_cmd_rmdir(struct sftp_command *cmd) { - char *dir; - struct sftp_packet *pktin; - struct sftp_request *req, *rreq; - int result; + int i, ret; if (back == NULL) { - printf("psftp: not connected to a host; use \"open host.name\"\n"); + not_connected(); return 0; } @@ -1349,36 +1451,40 @@ int sftp_cmd_rmdir(struct sftp_command *cmd) return 0; } - dir = canonify(cmd->words[1]); - if (!dir) { - printf("%s: %s\n", dir, fxp_error()); - return 0; - } + ret = 1; + for (i = 1; i < cmd->nwords; i++) + ret &= wildcard_iterate(cmd->words[i], sftp_action_rmdir, NULL); - sftp_register(req = fxp_rmdir_send(dir)); + return ret; +} + +static int sftp_action_rm(void *vctx, char *fname) +{ + struct sftp_packet *pktin; + struct sftp_request *req, *rreq; + int result; + + sftp_register(req = fxp_remove_send(fname)); rreq = sftp_find_request(pktin = sftp_recv()); assert(rreq == req); - result = fxp_rmdir_recv(pktin, rreq); + result = fxp_remove_recv(pktin, rreq); if (!result) { - printf("rmdir %s: %s\n", dir, fxp_error()); - sfree(dir); + printf("rm %s: %s\n", fname, fxp_error()); return 0; } - sfree(dir); + printf("rm %s: OK\n", fname); + return 1; } int sftp_cmd_rm(struct sftp_command *cmd) { - char *fname; - struct sftp_packet *pktin; - struct sftp_request *req, *rreq; - int result; + int i, ret; if (back == NULL) { - printf("psftp: not connected to a host; use \"open host.name\"\n"); + not_connected(); return 0; } @@ -1387,36 +1493,93 @@ int sftp_cmd_rm(struct sftp_command *cmd) return 0; } - fname = canonify(cmd->words[1]); - if (!fname) { - printf("%s: %s\n", fname, fxp_error()); - return 0; + ret = 1; + for (i = 1; i < cmd->nwords; i++) + ret &= wildcard_iterate(cmd->words[i], sftp_action_rm, NULL); + + return ret; +} + +static int check_is_dir(char *dstfname) +{ + struct sftp_packet *pktin; + struct sftp_request *req, *rreq; + struct fxp_attrs attrs; + int result; + + 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)) + return TRUE; + else + return FALSE; +} + +struct sftp_context_mv { + char *dstfname; + int dest_is_dir; +}; + +static int sftp_action_mv(void *vctx, char *srcfname) +{ + struct sftp_context_mv *ctx = (struct sftp_context_mv *)vctx; + struct sftp_packet *pktin; + struct sftp_request *req, *rreq; + const char *error; + char *finalfname, *newcanon = NULL; + int ret, result; + + if (ctx->dest_is_dir) { + char *p; + char *newname; + + p = srcfname + strlen(srcfname); + while (p > srcfname && p[-1] != '/') p--; + newname = dupcat(ctx->dstfname, "/", p, NULL); + newcanon = canonify(newname); + if (!newcanon) { + printf("%s: canonify: %s\n", newname, fxp_error()); + sfree(newname); + return 0; + } + sfree(newname); + + finalfname = newcanon; + } else { + finalfname = ctx->dstfname; } - sftp_register(req = fxp_remove_send(fname)); + sftp_register(req = fxp_rename_send(srcfname, finalfname)); rreq = sftp_find_request(pktin = sftp_recv()); assert(rreq == req); - result = fxp_remove_recv(pktin, rreq); + result = fxp_rename_recv(pktin, rreq); - if (!result) { - printf("rm %s: %s\n", fname, fxp_error()); - sfree(fname); - return 0; + error = result ? NULL : fxp_error(); + + if (error) { + printf("mv %s %s: %s\n", srcfname, finalfname, error); + ret = 0; + } else { + printf("%s -> %s\n", srcfname, finalfname); + ret = 1; } - sfree(fname); - return 1; + sfree(newcanon); + return ret; } int sftp_cmd_mv(struct sftp_command *cmd) { - char *srcfname, *dstfname; - struct sftp_packet *pktin; - struct sftp_request *req, *rreq; - int result; + struct sftp_context_mv actx, *ctx = &actx; + int i, ret; if (back == NULL) { - printf("psftp: not connected to a host; use \"open host.name\"\n"); + not_connected(); return 0; } @@ -1424,86 +1587,93 @@ int sftp_cmd_mv(struct sftp_command *cmd) printf("mv: expects two filenames\n"); return 0; } - srcfname = canonify(cmd->words[1]); - if (!srcfname) { - printf("%s: %s\n", srcfname, fxp_error()); + + ctx->dstfname = canonify(cmd->words[cmd->nwords-1]); + if (!ctx->dstfname) { + printf("%s: canonify: %s\n", ctx->dstfname, fxp_error()); return 0; } - dstfname = canonify(cmd->words[2]); - if (!dstfname) { - printf("%s: %s\n", dstfname, fxp_error()); + /* + * If there's more than one source argument, or one source + * argument which is a wildcard, we _require_ that the + * destination is a directory. + */ + ctx->dest_is_dir = check_is_dir(ctx->dstfname); + if ((cmd->nwords > 3 || is_wildcard(cmd->words[1])) && !ctx->dest_is_dir) { + printf("mv: multiple or wildcard arguments require the destination" + " to be a directory\n"); + sfree(ctx->dstfname); return 0; } - sftp_register(req = fxp_rename_send(srcfname, dstfname)); + /* + * Now iterate over the source arguments. + */ + ret = 1; + for (i = 1; i < cmd->nwords-1; i++) + ret &= wildcard_iterate(cmd->words[i], sftp_action_mv, ctx); + + sfree(ctx->dstfname); + return ret; +} + +struct sftp_context_chmod { + unsigned attrs_clr, attrs_xor; +}; + +static int sftp_action_chmod(void *vctx, char *fname) +{ + struct fxp_attrs attrs; + struct sftp_packet *pktin; + struct sftp_request *req, *rreq; + int result; + unsigned oldperms, newperms; + struct sftp_context_chmod *ctx = (struct sftp_context_chmod *)vctx; + + sftp_register(req = fxp_stat_send(fname)); rreq = sftp_find_request(pktin = sftp_recv()); assert(rreq == req); - result = fxp_rename_recv(pktin, rreq); + result = fxp_stat_recv(pktin, rreq, &attrs); - if (!result) { - char const *error = fxp_error(); - struct fxp_attrs attrs; + if (!result || !(attrs.flags & SSH_FILEXFER_ATTR_PERMISSIONS)) { + printf("get attrs for %s: %s\n", fname, + result ? "file permissions not provided" : fxp_error()); + return 0; + } - /* - * The move might have failed because dstfname pointed at a - * directory. We check this possibility now: if dstfname - * _is_ a directory, we re-attempt the move by appending - * the basename of srcfname to dstfname. - */ - sftp_register(req = fxp_stat_send(dstfname)); - rreq = sftp_find_request(pktin = sftp_recv()); - assert(rreq == req); - result = fxp_stat_recv(pktin, rreq, &attrs); + attrs.flags = SSH_FILEXFER_ATTR_PERMISSIONS; /* perms _only_ */ + oldperms = attrs.permissions & 07777; + attrs.permissions &= ~ctx->attrs_clr; + attrs.permissions ^= ctx->attrs_xor; + newperms = attrs.permissions & 07777; - if (result && - (attrs.flags & SSH_FILEXFER_ATTR_PERMISSIONS) && - (attrs.permissions & 0040000)) { - char *p; - char *newname, *newcanon; - printf("(destination %s is a directory)\n", dstfname); - p = srcfname + strlen(srcfname); - while (p > srcfname && p[-1] != '/') p--; - newname = dupcat(dstfname, "/", p, NULL); - newcanon = canonify(newname); - sfree(newname); - if (newcanon) { - sfree(dstfname); - dstfname = newcanon; + if (oldperms == newperms) + return 1; /* no need to do anything! */ - sftp_register(req = fxp_rename_send(srcfname, dstfname)); - rreq = sftp_find_request(pktin = sftp_recv()); - assert(rreq == req); - result = fxp_rename_recv(pktin, rreq); + sftp_register(req = fxp_setstat_send(fname, attrs)); + rreq = sftp_find_request(pktin = sftp_recv()); + assert(rreq == req); + result = fxp_setstat_recv(pktin, rreq); - error = result ? NULL : fxp_error(); - } - } - if (error) { - printf("mv %s %s: %s\n", srcfname, dstfname, error); - sfree(srcfname); - sfree(dstfname); - return 0; - } + if (!result) { + printf("set attrs for %s: %s\n", fname, fxp_error()); + return 0; } - printf("%s -> %s\n", srcfname, dstfname); - sfree(srcfname); - sfree(dstfname); + printf("%s: %04o -> %04o\n", fname, oldperms, newperms); + return 1; } int sftp_cmd_chmod(struct sftp_command *cmd) { - char *fname, *mode; - int result; - struct fxp_attrs attrs; - unsigned attrs_clr, attrs_xor, oldperms, newperms; - struct sftp_packet *pktin; - struct sftp_request *req, *rreq; + char *mode; + int i, ret; + struct sftp_context_chmod actx, *ctx = &actx; if (back == NULL) { - printf("psftp: not connected to a host; use \"open host.name\"\n"); + not_connected(); return 0; } @@ -1523,7 +1693,7 @@ int sftp_cmd_chmod(struct sftp_command *cmd) * Additionally, the s attribute may not be specified for any * [ugoa] specifications other than exactly u or exactly g. */ - attrs_clr = attrs_xor = 0; + ctx->attrs_clr = ctx->attrs_xor = 0; mode = cmd->words[1]; if (mode[0] >= '0' && mode[0] <= '9') { if (mode[strspn(mode, "01234567")]) { @@ -1531,9 +1701,9 @@ int sftp_cmd_chmod(struct sftp_command *cmd) " contain digits 0-7 only\n"); return 0; } - attrs_clr = 07777; - sscanf(mode, "%o", &attrs_xor); - attrs_xor &= attrs_clr; + ctx->attrs_clr = 07777; + sscanf(mode, "%o", &ctx->attrs_xor); + ctx->attrs_xor &= ctx->attrs_clr; } else { while (*mode) { char *modebegin = mode; @@ -1601,61 +1771,27 @@ int sftp_cmd_chmod(struct sftp_command *cmd) perms &= subset; switch (action) { case '+': - attrs_clr |= perms; - attrs_xor |= perms; + ctx->attrs_clr |= perms; + ctx->attrs_xor |= perms; break; case '-': - attrs_clr |= perms; - attrs_xor &= ~perms; + ctx->attrs_clr |= perms; + ctx->attrs_xor &= ~perms; break; case '=': - attrs_clr |= subset; - attrs_xor |= perms; + ctx->attrs_clr |= subset; + ctx->attrs_xor |= perms; break; } if (*mode) mode++; /* eat comma */ } } - fname = canonify(cmd->words[2]); - if (!fname) { - printf("%s: %s\n", fname, fxp_error()); - return 0; - } - - 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()); - sfree(fname); - return 0; - } - - attrs.flags = SSH_FILEXFER_ATTR_PERMISSIONS; /* perms _only_ */ - oldperms = attrs.permissions & 07777; - attrs.permissions &= ~attrs_clr; - attrs.permissions ^= attrs_xor; - newperms = attrs.permissions & 07777; - - 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()); - sfree(fname); - return 0; - } - - printf("%s: %04o -> %04o\n", fname, oldperms, newperms); + ret = 1; + for (i = 2; i < cmd->nwords; i++) + ret &= wildcard_iterate(cmd->words[i], sftp_action_chmod, ctx); - sfree(fname); - return 1; + return ret; } static int sftp_cmd_open(struct sftp_command *cmd) @@ -1771,7 +1907,7 @@ static struct sftp_cmd_lookup { }, { "cd", TRUE, "change your remote working directory", - " [ ]\n" + " [ ]\n" " Change the remote working directory for your SFTP session.\n" " If a new working directory is not supplied, you will be\n" " returned to your home directory.\n", @@ -1779,10 +1915,11 @@ static struct sftp_cmd_lookup { }, { "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" - " Alternatively, can include:\n" + " [ ... ]\n" + " Change the file permissions on one or more remote files or\n" + " directories.\n" + " can be any octal Unix permission specifier.\n" + " Alternatively, can include the following modifiers:\n" " u+r make file readable by owning user\n" " u+w make file writable by owning user\n" " u+x make file executable by owning user\n" @@ -1813,16 +1950,16 @@ static struct sftp_cmd_lookup { sftp_cmd_close }, { - "del", TRUE, "delete a file", - " \n" - " Delete a file.\n", + "del", TRUE, "delete files on the remote server", + " [ ... ]\n" + " Delete a file or files from the server.\n", sftp_cmd_rm }, { "delete", FALSE, "del", NULL, sftp_cmd_rm }, { - "dir", TRUE, "list contents of a remote directory", + "dir", TRUE, "list remote files", " [ ]/[ ]\n" " List the contents of a specified directory on the server.\n" " If is not given, the current working directory\n" @@ -1879,9 +2016,9 @@ static struct sftp_cmd_lookup { sftp_cmd_mget }, { - "mkdir", TRUE, "create a directory on the remote server", - " \n" - " Creates a directory with the given name on the server.\n", + "mkdir", TRUE, "create directories on the remote server", + " [ ... ]\n" + " Creates directories with the given names on the server.\n", sftp_cmd_mkdir }, { @@ -1894,18 +2031,22 @@ static struct sftp_cmd_lookup { sftp_cmd_mput }, { - "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", + "mv", TRUE, "move or rename file(s) on the remote server", + " [ ... ] \n" + " Moves or renames (s) on the server to ,\n" + " also on the server.\n" + " If specifies an existing directory, then \n" + " may be a wildcard, and multiple s may be given; all\n" + " source files are moved into .\n" + " Otherwise, must specify a single file, which is moved\n" + " or renamed so that it is accessible under the name .\n", sftp_cmd_mv }, { "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", + " when you are not already connected to a server.\n", sftp_cmd_open }, { @@ -1928,7 +2069,7 @@ static struct sftp_cmd_lookup { sftp_cmd_quit }, { - "reget", TRUE, "continue downloading a file", + "reget", TRUE, "continue downloading files", " [ -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" @@ -1945,7 +2086,7 @@ static struct sftp_cmd_lookup { sftp_cmd_mv }, { - "reput", TRUE, "continue uploading a file", + "reput", TRUE, "continue uploading files", " [ -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" @@ -1958,10 +2099,11 @@ static struct sftp_cmd_lookup { sftp_cmd_rm }, { - "rmdir", TRUE, "remove a directory on the remote server", - " \n" + "rmdir", TRUE, "remove directories 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", + " The directory will not be removed unless it is empty.\n" + " Wildcards may be used to specify multiple directories.\n", sftp_cmd_rmdir } }; @@ -2388,6 +2530,15 @@ int from_backend(void *frontend, int is_stderr, const char *data, int datalen) return 0; } +int from_backend_untrusted(void *frontend_handle, const char *data, int len) +{ + /* + * No "untrusted" output should get here (the way the code is + * currently, it's all diverted by FLAG_STDERR). + */ + assert(!"Unexpected call to from_backend_untrusted()"); + return 0; /* not reached */ +} int sftp_recvdata(char *buf, int len) { outptr = (unsigned char *) buf; @@ -2416,7 +2567,7 @@ int sftp_recvdata(char *buf, int len) } while (outlen > 0) { - if (ssh_sftp_loop_iteration() < 0) + if (back->exitcode(backhandle) >= 0 || ssh_sftp_loop_iteration() < 0) return 0; /* doom */ } @@ -2437,6 +2588,8 @@ static void usage(void) printf("%s\n", ver); printf("Usage: psftp [options] [user@]host\n"); printf("Options:\n"); + printf(" -V print version information and exit\n"); + printf(" -pgpfp print PGP key fingerprints and exit\n"); printf(" -b file use specified batchfile\n"); printf(" -bc output batchfile commands\n"); printf(" -be don't stop batchfile processing if errors\n"); @@ -2446,10 +2599,12 @@ static void usage(void) printf(" -P port connect to specified port\n"); printf(" -pw passw login with specified password\n"); printf(" -1 -2 force use of particular SSH protocol version\n"); + printf(" -4 -6 force use of IPv4 or IPv6\n"); printf(" -C enable compression\n"); printf(" -i key private key file for authentication\n"); + printf(" -noagent disable use of Pageant\n"); + printf(" -agent enable use of Pageant\n"); printf(" -batch disable all interactive prompts\n"); - printf(" -V print version information\n"); cleanup_exit(1); } @@ -2576,17 +2731,6 @@ static int psftp_connect(char *userhost, char *user, int portnumber) strncpy(cfg.username, user, sizeof(cfg.username) - 1); cfg.username[sizeof(cfg.username) - 1] = '\0'; } - if (!cfg.username[0]) { - if (!console_get_line("login as: ", - cfg.username, sizeof(cfg.username), FALSE)) { - fprintf(stderr, "psftp: no username, aborting\n"); - cleanup_exit(1); - } else { - int len = strlen(cfg.username); - if (cfg.username[len - 1] == '\n') - cfg.username[len - 1] = '\0'; - } - } if (portnumber) cfg.port = portnumber; @@ -2606,7 +2750,7 @@ static int psftp_connect(char *userhost, char *user, int portnumber) cfg.nopty = TRUE; /* - * Set up fallback option, for SSH1 servers or servers with the + * Set up fallback option, for SSH-1 servers or servers with the * sftp subsystem not enabled but the server binary installed * in the usual place. We only support fallback on Unix * systems, and we use a kludgy piece of shellery which should @@ -2682,7 +2826,6 @@ int psftp_main(int argc, char *argv[]) #endif ; cmdline_tooltype = TOOLTYPE_FILETRANSFER; - ssh_get_line = &console_get_line; sk_init(); userhost = user = NULL; @@ -2713,6 +2856,9 @@ int psftp_main(int argc, char *argv[]) } else if (strcmp(argv[i], "-h") == 0 || strcmp(argv[i], "-?") == 0) { usage(); + } else if (strcmp(argv[i], "-pgpfp") == 0) { + pgp_fingerprints(); + return 1; } else if (strcmp(argv[i], "-V") == 0) { version(); } else if (strcmp(argv[i], "-batch") == 0) { @@ -2763,7 +2909,7 @@ int psftp_main(int argc, char *argv[]) do_sftp(mode, modeflags, batchfile); - if (back != NULL && back->socket(backhandle) != NULL) { + if (back != NULL && back->connected(backhandle)) { char ch; back->special(backhandle, TS_EOF); sftp_recvdata(&ch, 1);