X-Git-Url: https://asedeno.scripts.mit.edu/gitweb/?a=blobdiff_plain;f=pscp.c;h=454ec084c5cf5d161d713ed2f643e5dc4429bee2;hb=7fd8915ce9dabd124ee10374c33449ad912b607c;hp=a0910c7a1ec9e100d8291f892429a8b79bf4c048;hpb=a1f3b7a358adaa7c2a98359cd0373aa823eeb14b;p=PuTTY.git diff --git a/pscp.c b/pscp.c index a0910c7a..454ec084 100644 --- a/pscp.c +++ b/pscp.c @@ -41,14 +41,16 @@ static int try_sftp = 1; static int main_cmd_is_sftp = 0; static int fallback_cmd_is_sftp = 0; static int using_sftp = 0; +static int uploading = 0; static Backend *back; static void *backhandle; static Conf *conf; +int sent_eof = FALSE; -static void source(char *src); -static void rsource(char *src); -static void sink(char *targ, char *src); +static void source(const char *src); +static void rsource(const char *src); +static void sink(const char *targ, const char *src); const char *const appname = "PSCP"; @@ -58,23 +60,14 @@ const char *const appname = "PSCP"; */ #define MAX_SCP_BUFSIZE 16384 -void ldisc_send(void *handle, char *buf, int len, int interactive) -{ - /* - * This is only here because of the calls to ldisc_send(NULL, - * 0) in ssh.c. Nothing in PSCP actually needs to use the ldisc - * as an ldisc. So if we get called with any real data, I want - * to know about it. - */ - assert(len == 0); -} +void ldisc_echoedit_update(void *handle) { } -static void tell_char(FILE * stream, char c) +static void tell_char(FILE *stream, char c) { fputc(c, stream); } -static void tell_str(FILE * stream, char *str) +static void tell_str(FILE *stream, const char *str) { unsigned int i; @@ -82,7 +75,7 @@ static void tell_str(FILE * stream, char *str) tell_char(stream, str[i]); } -static void tell_user(FILE * stream, char *fmt, ...) +static void tell_user(FILE *stream, const char *fmt, ...) { char *str, *str2; va_list ap; @@ -98,7 +91,7 @@ static void tell_user(FILE * stream, char *fmt, ...) /* * Print an error message and perform a fatal exit. */ -void fatalbox(char *fmt, ...) +void fatalbox(const char *fmt, ...) { char *str, *str2; va_list ap; @@ -113,7 +106,7 @@ void fatalbox(char *fmt, ...) cleanup_exit(1); } -void modalfatalbox(char *fmt, ...) +void modalfatalbox(const char *fmt, ...) { char *str, *str2; va_list ap; @@ -128,7 +121,20 @@ void modalfatalbox(char *fmt, ...) cleanup_exit(1); } -void connection_fatal(void *frontend, char *fmt, ...) +void nonfatal(const char *fmt, ...) +{ + char *str, *str2; + va_list ap; + va_start(ap, fmt); + str = dupvprintf(fmt, ap); + str2 = dupcat("Error: ", str, "\n", NULL); + sfree(str); + va_end(ap); + tell_str(stderr, str2); + sfree(str2); + errs++; +} +void connection_fatal(void *frontend, const char *fmt, ...) { char *str, *str2; va_list ap; @@ -214,6 +220,20 @@ int from_backend_untrusted(void *frontend_handle, const char *data, int len) assert(!"Unexpected call to from_backend_untrusted()"); return 0; /* not reached */ } +int from_backend_eof(void *frontend) +{ + /* + * We usually expect to be the party deciding when to close the + * connection, so if we see EOF before we sent it ourselves, we + * should panic. The exception is if we're using old-style scp and + * downloading rather than uploading. + */ + if ((using_sftp || uploading) && !sent_eof) { + connection_fatal(frontend, + "Received unexpected end-of-file from server"); + } + return FALSE; +} static int ssh_scp_recv(unsigned char *buf, int len) { outptr = buf; @@ -282,7 +302,7 @@ static void ssh_scp_init(void) /* * Print an error message and exit after closing the SSH link. */ -static void bump(char *fmt, ...) +static void bump(const char *fmt, ...) { char *str, *str2; va_list ap; @@ -298,12 +318,36 @@ static void bump(char *fmt, ...) if (back != NULL && back->connected(backhandle)) { char ch; back->special(backhandle, TS_EOF); + sent_eof = TRUE; ssh_scp_recv((unsigned char *) &ch, 1); } cleanup_exit(1); } +/* + * Wait for the reply to a single SFTP request. Parallels the same + * function in psftp.c (but isn't centralised into sftp.c because the + * latter module handles SFTP only and shouldn't assume that SFTP is + * the only thing going on by calling connection_fatal). + */ +struct sftp_packet *sftp_wait_for_reply(struct sftp_request *req) +{ + struct sftp_packet *pktin; + struct sftp_request *rreq; + + sftp_register(req); + pktin = sftp_recv(); + if (pktin == NULL) + connection_fatal(NULL, "did not receive SFTP response packet " + "from server"); + rreq = sftp_find_request(pktin); + if (rreq != req) + connection_fatal(NULL, "unable to understand SFTP response packet " + "from server: %s", fxp_error()); + return pktin; +} + /* * Open an SSH connection to user@host and execute cmd. */ @@ -317,15 +361,9 @@ static void do_cmd(char *host, char *user, char *cmd) bump("Empty host name"); /* - * Remove fiddly bits of address: remove a colon suffix, and - * the square brackets around an IPv6 literal address. + * Remove a colon suffix. */ - if (host[0] == '[') { - host++; - host[strcspn(host, "]")] = '\0'; - } else { - host[strcspn(host, ":")] = '\0'; - } + host[host_strcspn(host, ":")] = '\0'; /* * If we haven't loaded session details already (e.g., from -load), @@ -345,6 +383,7 @@ static void do_cmd(char *host, char *user, char *cmd) /* Use `host' as a bare hostname. */ conf_set_str(conf, CONF_host, host); } + conf_free(conf2); } else { /* Patch in hostname `host' to session details. */ conf_set_str(conf, CONF_host, host); @@ -450,7 +489,7 @@ static void do_cmd(char *host, char *user, char *cmd) if (try_scp) { /* Fallback is to use the provided scp command. */ fallback_cmd_is_sftp = 0; - conf_set_str(conf, CONF_remote_cmd2, "sftp"); + conf_set_str(conf, CONF_remote_cmd2, cmd); conf_set_int(conf, CONF_ssh_subsys2, FALSE); } else { /* Since we're not going to try SCP, we may as well try @@ -476,6 +515,11 @@ static void do_cmd(char *host, char *user, char *cmd) back = &ssh_backend; + logctx = log_init(NULL, conf); + console_provide_logctx(logctx); + + platform_psftp_pre_conn_setup(); + err = back->init(NULL, &backhandle, conf, conf_get_str(conf, CONF_host), conf_get_int(conf, CONF_port), @@ -483,19 +527,17 @@ static void do_cmd(char *host, char *user, char *cmd) conf_get_int(conf, CONF_tcp_keepalives)); if (err != NULL) bump("ssh_init: %s", err); - logctx = log_init(NULL, conf); back->provide_logctx(backhandle, logctx); - console_provide_logctx(logctx); ssh_scp_init(); if (verbose && realhost != NULL && errs == 0) - tell_user(stderr, "Connected to %s\n", realhost); + tell_user(stderr, "Connected to %s", realhost); sfree(realhost); } /* * Update statistic information about current file. */ -static void print_stats(char *name, uint64 size, uint64 done, +static void print_stats(const char *name, uint64 size, uint64 done, time_t start, time_t now) { float ratebs; @@ -560,47 +602,13 @@ static char *colon(char *str) if (str[0] == '\0' || str[0] == ':' || (str[0] != '[' && str[1] == ':')) return (NULL); - while (*str != '\0' && *str != ':' && *str != '/' && *str != '\\') { - if (*str == '[') { - /* Skip over IPv6 literal addresses - * (eg: 'jeroen@[2001:db8::1]:myfile.txt') */ - char *ipv6_end = strchr(str, ']'); - if (ipv6_end) { - str = ipv6_end; - } - } - str++; - } + str += host_strcspn(str, ":/\\"); if (*str == ':') return (str); else return (NULL); } -/* - * Return a pointer to the portion of str that comes after the last - * slash (or backslash or colon, if `local' is TRUE). - */ -static char *stripslashes(char *str, int local) -{ - char *p; - - if (local) { - p = strchr(str, ':'); - if (p) str = p+1; - } - - p = strrchr(str, '/'); - if (p) str = p+1; - - if (local) { - p = strrchr(str, '\\'); - if (p) str = p+1; - } - - return str; -} - /* * Determine whether a string is entirely composed of dots. */ @@ -637,7 +645,7 @@ static int response(void) } while (p < sizeof(rbuf) && ch != '\n'); rbuf[p - 1] = '\0'; if (resp == 1) - tell_user(stderr, "%s\n", rbuf); + tell_user(stderr, "%s", rbuf); else bump("%s", rbuf); errs++; @@ -654,6 +662,10 @@ int sftp_senddata(char *buf, int len) back->send(backhandle, buf, len); return 1; } +int sftp_sendbuffer(void) +{ + return back->sendbuffer(backhandle); +} /* ---------------------------------------------------------------------- * sftp-based replacement for the hacky `pscp -ls'. @@ -664,13 +676,13 @@ static int sftp_ls_compare(const void *av, const void *bv) const struct fxp_name *b = (const struct fxp_name *) bv; return strcmp(a->filename, b->filename); } -void scp_sftp_listdir(char *dirname) +void scp_sftp_listdir(const char *dirname) { struct fxp_handle *dirh; struct fxp_names *names; struct fxp_name *ournames; struct sftp_packet *pktin; - struct sftp_request *req, *rreq; + struct sftp_request *req; int nnames, namesize; int i; @@ -682,23 +694,22 @@ void scp_sftp_listdir(char *dirname) printf("Listing directory %s\n", dirname); - sftp_register(req = fxp_opendir_send(dirname)); - rreq = sftp_find_request(pktin = sftp_recv()); - assert(rreq == req); - dirh = fxp_opendir_recv(pktin, rreq); + req = fxp_opendir_send(dirname); + pktin = sftp_wait_for_reply(req); + dirh = fxp_opendir_recv(pktin, req); if (dirh == NULL) { - printf("Unable to open %s: %s\n", dirname, fxp_error()); + tell_user(stderr, "Unable to open %s: %s\n", dirname, fxp_error()); + errs++; } else { nnames = namesize = 0; ournames = NULL; while (1) { - sftp_register(req = fxp_readdir_send(dirh)); - rreq = sftp_find_request(pktin = sftp_recv()); - assert(rreq == req); - names = fxp_readdir_recv(pktin, rreq); + req = fxp_readdir_send(dirh); + pktin = sftp_wait_for_reply(req); + names = fxp_readdir_recv(pktin, req); if (names == NULL) { if (fxp_error_type() == SSH_FX_EOF) @@ -721,22 +732,24 @@ void scp_sftp_listdir(char *dirname) names->nnames = 0; /* prevent free_names */ fxp_free_names(names); } - sftp_register(req = fxp_close_send(dirh)); - rreq = sftp_find_request(pktin = sftp_recv()); - assert(rreq == req); - fxp_close_recv(pktin, rreq); + req = fxp_close_send(dirh); + pktin = sftp_wait_for_reply(req); + fxp_close_recv(pktin, req); /* * Now we have our filenames. Sort them by actual file * name, and then output the longname parts. */ - qsort(ournames, nnames, sizeof(*ournames), sftp_ls_compare); + if (nnames > 0) + qsort(ournames, nnames, sizeof(*ournames), sftp_ls_compare); /* * And print them. */ for (i = 0; i < nnames; i++) printf("%s\n", ournames[i].longname); + + sfree(ournames); } } @@ -763,7 +776,7 @@ static struct fxp_handle *scp_sftp_filehandle; static struct fxp_xfer *scp_sftp_xfer; static uint64 scp_sftp_fileoffset; -int scp_source_setup(char *target, int shouldbedir) +int scp_source_setup(const char *target, int shouldbedir) { if (using_sftp) { /* @@ -771,7 +784,7 @@ int scp_source_setup(char *target, int shouldbedir) * directory. */ struct sftp_packet *pktin; - struct sftp_request *req, *rreq; + struct sftp_request *req; struct fxp_attrs attrs; int ret; @@ -781,10 +794,9 @@ int scp_source_setup(char *target, int shouldbedir) return 1; } - sftp_register(req = fxp_stat_send(target)); - rreq = sftp_find_request(pktin = sftp_recv()); - assert(rreq == req); - ret = fxp_stat_recv(pktin, rreq, &attrs); + req = fxp_stat_send(target); + pktin = sftp_wait_for_reply(req); + ret = fxp_stat_recv(pktin, req, &attrs); if (!ret || !(attrs.flags & SSH_FILEXFER_ATTR_PERMISSIONS)) scp_sftp_targetisdir = 0; @@ -830,12 +842,13 @@ int scp_send_filetimes(unsigned long mtime, unsigned long atime) } } -int scp_send_filename(char *name, uint64 size, int modes) +int scp_send_filename(const char *name, uint64 size, int permissions) { if (using_sftp) { char *fullname; struct sftp_packet *pktin; - struct sftp_request *req, *rreq; + struct sftp_request *req; + struct fxp_attrs attrs; if (scp_sftp_targetisdir) { fullname = dupcat(scp_sftp_remotepath, "/", name, NULL); @@ -843,15 +856,19 @@ int scp_send_filename(char *name, uint64 size, int modes) fullname = dupstr(scp_sftp_remotepath); } - sftp_register(req = fxp_open_send(fullname, SSH_FXF_WRITE | - SSH_FXF_CREAT | SSH_FXF_TRUNC)); - rreq = sftp_find_request(pktin = sftp_recv()); - assert(rreq == req); - scp_sftp_filehandle = fxp_open_recv(pktin, rreq); + attrs.flags = 0; + PUT_PERMISSIONS(attrs, permissions); + + req = fxp_open_send(fullname, + SSH_FXF_WRITE | SSH_FXF_CREAT | SSH_FXF_TRUNC, + &attrs); + pktin = sftp_wait_for_reply(req); + scp_sftp_filehandle = fxp_open_recv(pktin, req); if (!scp_sftp_filehandle) { tell_user(stderr, "pscp: unable to open %s: %s", fullname, fxp_error()); + sfree(fullname); errs++; return 1; } @@ -864,7 +881,9 @@ int scp_send_filename(char *name, uint64 size, int modes) char buf[40]; char sizestr[40]; uint64_decimal(size, sizestr); - sprintf(buf, "C%04o %s ", modes, sizestr); + if (permissions < 0) + permissions = 0644; + sprintf(buf, "C%04o %s ", (int)(permissions & 07777), sizestr); back->send(backhandle, buf, strlen(buf)); back->send(backhandle, name, strlen(name)); back->send(backhandle, "\n", 1); @@ -885,8 +904,10 @@ int scp_send_filedata(char *data, int len) while (!xfer_upload_ready(scp_sftp_xfer)) { pktin = sftp_recv(); ret = xfer_upload_gotpkt(scp_sftp_xfer, pktin); - if (!ret) { - tell_user(stderr, "error while writing: %s\n", fxp_error()); + if (ret <= 0) { + tell_user(stderr, "error while writing: %s", fxp_error()); + if (ret == INT_MIN) /* pktin not even freed */ + sfree(pktin); errs++; return 1; } @@ -920,12 +941,19 @@ int scp_send_finish(void) if (using_sftp) { struct fxp_attrs attrs; struct sftp_packet *pktin; - struct sftp_request *req, *rreq; + struct sftp_request *req; int ret; while (!xfer_done(scp_sftp_xfer)) { pktin = sftp_recv(); - xfer_upload_gotpkt(scp_sftp_xfer, pktin); + ret = xfer_upload_gotpkt(scp_sftp_xfer, pktin); + if (ret <= 0) { + tell_user(stderr, "error while writing: %s", fxp_error()); + if (ret == INT_MIN) /* pktin not even freed */ + sfree(pktin); + errs++; + return 1; + } } xfer_cleanup(scp_sftp_xfer); @@ -936,19 +964,17 @@ int scp_send_finish(void) attrs.flags = SSH_FILEXFER_ATTR_ACMODTIME; attrs.atime = scp_sftp_atime; attrs.mtime = scp_sftp_mtime; - sftp_register(req = fxp_fsetstat_send(scp_sftp_filehandle, attrs)); - rreq = sftp_find_request(pktin = sftp_recv()); - assert(rreq == req); - ret = fxp_fsetstat_recv(pktin, rreq); + req = fxp_fsetstat_send(scp_sftp_filehandle, attrs); + pktin = sftp_wait_for_reply(req); + ret = fxp_fsetstat_recv(pktin, req); if (!ret) { - tell_user(stderr, "unable to set file times: %s\n", fxp_error()); + tell_user(stderr, "unable to set file times: %s", fxp_error()); errs++; } } - sftp_register(req = fxp_close_send(scp_sftp_filehandle)); - rreq = sftp_find_request(pktin = sftp_recv()); - assert(rreq == req); - fxp_close_recv(pktin, rreq); + req = fxp_close_send(scp_sftp_filehandle); + pktin = sftp_wait_for_reply(req); + fxp_close_recv(pktin, req); scp_has_times = 0; return 0; } else { @@ -971,14 +997,14 @@ void scp_restore_remotepath(char *data) scp_sftp_remotepath = data; } -int scp_send_dirname(char *name, int modes) +int scp_send_dirname(const char *name, int modes) { if (using_sftp) { char *fullname; char const *err; struct fxp_attrs attrs; struct sftp_packet *pktin; - struct sftp_request *req, *rreq; + struct sftp_request *req; int ret; if (scp_sftp_targetisdir) { @@ -994,25 +1020,24 @@ int scp_send_dirname(char *name, int modes) * exists and is a directory we will assume we were either * successful or it didn't matter. */ - sftp_register(req = fxp_mkdir_send(fullname)); - rreq = sftp_find_request(pktin = sftp_recv()); - assert(rreq == req); - ret = fxp_mkdir_recv(pktin, rreq); + req = fxp_mkdir_send(fullname); + pktin = sftp_wait_for_reply(req); + ret = fxp_mkdir_recv(pktin, req); if (!ret) err = fxp_error(); else err = "server reported no error"; - sftp_register(req = fxp_stat_send(fullname)); - rreq = sftp_find_request(pktin = sftp_recv()); - assert(rreq == req); - ret = fxp_stat_recv(pktin, rreq, &attrs); + req = fxp_stat_send(fullname); + pktin = sftp_wait_for_reply(req); + ret = fxp_stat_recv(pktin, req, &attrs); if (!ret || !(attrs.flags & SSH_FILEXFER_ATTR_PERMISSIONS) || !(attrs.permissions & 0040000)) { tell_user(stderr, "unable to create directory %s: %s", fullname, err); + sfree(fullname); errs++; return 1; } @@ -1047,7 +1072,7 @@ int scp_send_enddir(void) * right at the start, whereas scp_sink_init is called to * initialise every level of recursion in the protocol. */ -int scp_sink_setup(char *source, int preserve, int recursive) +int scp_sink_setup(const char *source, int preserve, int recursive) { if (using_sftp) { char *newsource; @@ -1068,6 +1093,9 @@ int scp_sink_setup(char *source, int preserve, int recursive) if (!wc_unescape(newsource, source)) { /* Yes, here we go; it's a wildcard. Bah. */ char *dupsource, *lastpart, *dirpart, *wildcard; + + sfree(newsource); + dupsource = dupstr(source); lastpart = stripslashes(dupsource, 0); wildcard = dupstr(lastpart); @@ -1144,7 +1172,7 @@ struct scp_sink_action { int action; /* FILE, DIR, ENDDIR */ char *buf; /* will need freeing after use */ char *name; /* filename or dirname (not ENDDIR) */ - int mode; /* access mode (not ENDDIR) */ + long permissions; /* access permissions (not ENDDIR) */ uint64 size; /* file size (not ENDDIR) */ int settime; /* 1 if atime and mtime are filled */ unsigned long atime, mtime; /* access times for the file */ @@ -1157,7 +1185,7 @@ int scp_get_sink_action(struct scp_sink_action *act) int must_free_fname; struct fxp_attrs attrs; struct sftp_packet *pktin; - struct sftp_request *req, *rreq; + struct sftp_request *req; int ret; if (!scp_sftp_dirstack_head) { @@ -1226,14 +1254,14 @@ int scp_get_sink_action(struct scp_sink_action *act) * Now we have a filename. Stat it, and see if it's a file * or a directory. */ - sftp_register(req = fxp_stat_send(fname)); - rreq = sftp_find_request(pktin = sftp_recv()); - assert(rreq == req); - ret = fxp_stat_recv(pktin, rreq, &attrs); + req = fxp_stat_send(fname); + pktin = sftp_wait_for_reply(req); + ret = fxp_stat_recv(pktin, req, &attrs); if (!ret || !(attrs.flags & SSH_FILEXFER_ATTR_PERMISSIONS)) { tell_user(stderr, "unable to identify %s: %s", fname, ret ? "file type not supplied" : fxp_error()); + if (must_free_fname) sfree(fname); errs++; return 1; } @@ -1282,13 +1310,12 @@ int scp_get_sink_action(struct scp_sink_action *act) * list), we must push the other (target,namelist) pair * on a stack. */ - sftp_register(req = fxp_opendir_send(fname)); - rreq = sftp_find_request(pktin = sftp_recv()); - assert(rreq == req); - dirhandle = fxp_opendir_recv(pktin, rreq); + req = fxp_opendir_send(fname); + pktin = sftp_wait_for_reply(req); + dirhandle = fxp_opendir_recv(pktin, req); if (!dirhandle) { - tell_user(stderr, "scp: unable to open directory %s: %s", + tell_user(stderr, "pscp: unable to open directory %s: %s", fname, fxp_error()); if (must_free_fname) sfree(fname); errs++; @@ -1299,16 +1326,20 @@ int scp_get_sink_action(struct scp_sink_action *act) while (1) { int i; - sftp_register(req = fxp_readdir_send(dirhandle)); - rreq = sftp_find_request(pktin = sftp_recv()); - assert(rreq == req); - names = fxp_readdir_recv(pktin, rreq); + req = fxp_readdir_send(dirhandle); + pktin = sftp_wait_for_reply(req); + names = fxp_readdir_recv(pktin, req); if (names == NULL) { if (fxp_error_type() == SSH_FX_EOF) break; - tell_user(stderr, "scp: reading directory %s: %s\n", + tell_user(stderr, "pscp: reading directory %s: %s", fname, fxp_error()); + + req = fxp_close_send(dirhandle); + pktin = sftp_wait_for_reply(req); + fxp_close_recv(pktin, req); + if (must_free_fname) sfree(fname); sfree(ournames); errs++; @@ -1332,7 +1363,7 @@ int scp_get_sink_action(struct scp_sink_action *act) */ } else if (!vet_filename(names->names[i].filename)) { tell_user(stderr, "ignoring potentially dangerous server-" - "supplied filename '%s'\n", + "supplied filename '%s'", names->names[i].filename); } else ournames[nnames++] = names->names[i]; @@ -1340,10 +1371,9 @@ int scp_get_sink_action(struct scp_sink_action *act) names->nnames = 0; /* prevent free_names */ fxp_free_names(names); } - sftp_register(req = fxp_close_send(dirhandle)); - rreq = sftp_find_request(pktin = sftp_recv()); - assert(rreq == req); - fxp_close_recv(pktin, rreq); + req = fxp_close_send(dirhandle); + pktin = sftp_wait_for_reply(req); + fxp_close_recv(pktin, req); newitem = snew(struct scp_sftp_dirstack); newitem->next = scp_sftp_dirstack_head; @@ -1370,7 +1400,7 @@ int scp_get_sink_action(struct scp_sink_action *act) act->buf = dupstr(stripslashes(fname, 0)); act->name = act->buf; act->size = uint64_make(0,0); /* duhh, it's a directory */ - act->mode = 07777 & attrs.permissions; + act->permissions = 07777 & attrs.permissions; if (scp_sftp_preserve && (attrs.flags & SSH_FILEXFER_ATTR_ACMODTIME)) { act->atime = attrs.atime; @@ -1392,7 +1422,7 @@ int scp_get_sink_action(struct scp_sink_action *act) act->size = attrs.size; } else act->size = uint64_make(ULONG_MAX,ULONG_MAX); /* no idea */ - act->mode = 07777 & attrs.permissions; + act->permissions = 07777 & attrs.permissions; if (scp_sftp_preserve && (attrs.flags & SSH_FILEXFER_ATTR_ACMODTIME)) { act->atime = attrs.atime; @@ -1436,7 +1466,7 @@ int scp_get_sink_action(struct scp_sink_action *act) act->buf[i - 1] = '\0'; switch (action) { case '\01': /* error */ - tell_user(stderr, "%s\n", act->buf); + tell_user(stderr, "%s", act->buf); errs++; continue; /* go round again */ case '\02': /* fatal error */ @@ -1474,7 +1504,8 @@ int scp_get_sink_action(struct scp_sink_action *act) { char sizestr[40]; - if (sscanf(act->buf, "%o %s %n", &act->mode, sizestr, &i) != 2) + if (sscanf(act->buf, "%lo %39s %n", &act->permissions, + sizestr, &i) != 2) bump("Protocol error: Illegal file descriptor format"); act->size = uint64_from_decimal(sizestr); act->name = act->buf + i; @@ -1487,12 +1518,11 @@ int scp_accept_filexfer(void) { if (using_sftp) { struct sftp_packet *pktin; - struct sftp_request *req, *rreq; + struct sftp_request *req; - sftp_register(req = fxp_open_send(scp_sftp_currentname, SSH_FXF_READ)); - rreq = sftp_find_request(pktin = sftp_recv()); - assert(rreq == req); - scp_sftp_filehandle = fxp_open_recv(pktin, rreq); + req = fxp_open_send(scp_sftp_currentname, SSH_FXF_READ, NULL); + pktin = sftp_wait_for_reply(req); + scp_sftp_filehandle = fxp_open_recv(pktin, req); if (!scp_sftp_filehandle) { tell_user(stderr, "pscp: unable to open %s: %s", @@ -1521,14 +1551,21 @@ int scp_recv_filedata(char *data, int len) xfer_download_queue(scp_sftp_xfer); pktin = sftp_recv(); ret = xfer_download_gotpkt(scp_sftp_xfer, pktin); - - if (ret < 0) { + if (ret <= 0) { tell_user(stderr, "pscp: error while reading: %s", fxp_error()); + if (ret == INT_MIN) /* pktin not even freed */ + sfree(pktin); errs++; return -1; } if (xfer_download_data(scp_sftp_xfer, &vbuf, &actuallen)) { + if (actuallen <= 0) { + tell_user(stderr, "pscp: end of file while reading"); + errs++; + sfree(vbuf); + return -1; + } /* * This assertion relies on the fact that the natural * block size used in the xfer manager is at most that @@ -1553,7 +1590,7 @@ int scp_finish_filerecv(void) { if (using_sftp) { struct sftp_packet *pktin; - struct sftp_request *req, *rreq; + struct sftp_request *req; /* * Ensure that xfer_done() will work correctly, so we can @@ -1563,19 +1600,25 @@ int scp_finish_filerecv(void) xfer_set_error(scp_sftp_xfer); while (!xfer_done(scp_sftp_xfer)) { void *vbuf; - int len; + int ret, len; pktin = sftp_recv(); - xfer_download_gotpkt(scp_sftp_xfer, pktin); + ret = xfer_download_gotpkt(scp_sftp_xfer, pktin); + if (ret <= 0) { + tell_user(stderr, "pscp: error while reading: %s", fxp_error()); + if (ret == INT_MIN) /* pktin not even freed */ + sfree(pktin); + errs++; + return -1; + } if (xfer_download_data(scp_sftp_xfer, &vbuf, &len)) sfree(vbuf); } xfer_cleanup(scp_sftp_xfer); - sftp_register(req = fxp_close_send(scp_sftp_filehandle)); - rreq = sftp_find_request(pktin = sftp_recv()); - assert(rreq == req); - fxp_close_recv(pktin, rreq); + req = fxp_close_send(scp_sftp_filehandle); + pktin = sftp_wait_for_reply(req); + fxp_close_recv(pktin, req); return 0; } else { back->send(backhandle, "", 1); @@ -1594,7 +1637,7 @@ static void run_err(const char *fmt, ...) va_start(ap, fmt); errs++; str = dupvprintf(fmt, ap); - str2 = dupcat("scp: ", str, "\n", NULL); + str2 = dupcat("pscp: ", str, "\n", NULL); sfree(str); scp_send_errmsg(str2); tell_user(stderr, "%s", str2); @@ -1605,11 +1648,12 @@ static void run_err(const char *fmt, ...) /* * Execute the source part of the SCP protocol. */ -static void source(char *src) +static void source(const char *src) { uint64 size; unsigned long mtime, atime; - char *last; + long permissions; + const char *last; RFile *f; int attr; uint64 i; @@ -1629,7 +1673,7 @@ static void source(char *src) /* * Avoid . and .. directories. */ - char *p; + const char *p; p = strrchr(src, '/'); if (!p) p = strrchr(src, '\\'); @@ -1656,14 +1700,16 @@ static void source(char *src) if (last == src && strchr(src, ':') != NULL) last = strchr(src, ':') + 1; - f = open_existing_file(src, &size, &mtime, &atime); + f = open_existing_file(src, &size, &mtime, &atime, &permissions); if (f == NULL) { run_err("%s: Cannot open file", src); return; } if (preserve) { - if (scp_send_filetimes(mtime, atime)) + if (scp_send_filetimes(mtime, atime)) { + close_rfile(f); return; + } } if (verbose) { @@ -1671,18 +1717,21 @@ static void source(char *src) uint64_decimal(size, sizestr); tell_user(stderr, "Sending file %s, size=%s", last, sizestr); } - if (scp_send_filename(last, size, 0644)) + if (scp_send_filename(last, size, permissions)) { + close_rfile(f); return; + } stat_bytes = uint64_make(0,0); stat_starttime = time(NULL); stat_lasttime = 0; +#define PSCP_SEND_BLOCK 4096 for (i = uint64_make(0,0); uint64_compare(i,size) < 0; - i = uint64_add32(i,4096)) { - char transbuf[4096]; - int j, k = 4096; + i = uint64_add32(i,PSCP_SEND_BLOCK)) { + char transbuf[PSCP_SEND_BLOCK]; + int j, k = PSCP_SEND_BLOCK; if (uint64_compare(uint64_add32(i, k),size) > 0) /* i + k > size */ k = (uint64_subtract(size, i)).lo; /* k = size - i; */ @@ -1713,9 +1762,9 @@ static void source(char *src) /* * Recursively send the contents of a directory. */ -static void rsource(char *src) +static void rsource(const char *src) { - char *last; + const char *last; char *save_target; DirHandle *dir; @@ -1757,7 +1806,7 @@ static void rsource(char *src) /* * Execute the sink part of the SCP protocol. */ -static void sink(char *targ, char *src) +static void sink(const char *targ, const char *src) { char *destfname; int targisdir = 0; @@ -1875,27 +1924,34 @@ static void sink(char *targ, char *src) if (act.action == SCP_SINK_DIR) { if (exists && attr != FILE_TYPE_DIRECTORY) { run_err("%s: Not a directory", destfname); + sfree(destfname); continue; } if (!exists) { if (!create_directory(destfname)) { run_err("%s: Cannot create directory", destfname); + sfree(destfname); continue; } } sink(destfname, NULL); /* can we set the timestamp for directories ? */ + sfree(destfname); continue; } - f = open_new_file(destfname); + f = open_new_file(destfname, act.permissions); if (f == NULL) { run_err("%s: Cannot create file", destfname); + sfree(destfname); continue; } - if (scp_accept_filexfer()) + if (scp_accept_filexfer()) { + sfree(destfname); + close_wfile(f); return; + } stat_bytes = uint64_make(0, 0); stat_starttime = time(NULL); @@ -1913,8 +1969,10 @@ static void sink(char *targ, char *src) read = scp_recv_filedata(transbuf, (int)blksize.lo); if (read <= 0) bump("Lost connection"); - if (wrerror) + if (wrerror) { + received = uint64_add32(received, read); continue; + } if (write_to_file(f, transbuf, read) != (int)read) { wrerror = 1; /* FIXME: in sftp we can actually abort the transfer */ @@ -1922,6 +1980,7 @@ static void sink(char *targ, char *src) printf("\r%-25.25s | %50s\n", stat_name, "Write error.. waiting for end of file"); + received = uint64_add32(received, read); continue; } if (statistics) { @@ -1942,6 +2001,7 @@ static void sink(char *targ, char *src) close_wfile(f); if (wrerror) { run_err("%s: Write error", destfname); + sfree(destfname); continue; } (void) scp_finish_filerecv(); @@ -1955,21 +2015,26 @@ static void sink(char *targ, char *src) */ static void toremote(int argc, char *argv[]) { - char *src, *targ, *host, *user; + char *src, *wtarg, *host, *user; + const char *targ; char *cmd; int i, wc_type; - targ = argv[argc - 1]; + uploading = 1; + + wtarg = argv[argc - 1]; /* Separate host from filename */ - host = targ; - targ = colon(targ); - if (targ == NULL) - bump("targ == NULL in toremote()"); - *targ++ = '\0'; - if (*targ == '\0') - targ = "."; + host = wtarg; + wtarg = colon(wtarg); + if (wtarg == NULL) + bump("wtarg == NULL in toremote()"); + *wtarg++ = '\0'; /* Substitute "." for empty target */ + if (*wtarg == '\0') + targ = "."; + else + targ = wtarg; /* Separate host and username */ user = host; @@ -2045,24 +2110,29 @@ static void toremote(int argc, char *argv[]) */ static void tolocal(int argc, char *argv[]) { - char *src, *targ, *host, *user; + char *wsrc, *host, *user; + const char *src, *targ; char *cmd; + uploading = 0; + if (argc != 2) bump("More than one remote source not supported"); - src = argv[0]; + wsrc = argv[0]; targ = argv[1]; /* Separate host from filename */ - host = src; - src = colon(src); - if (src == NULL) + host = wsrc; + wsrc = colon(wsrc); + if (wsrc == NULL) bump("Local to local copy not supported"); - *src++ = '\0'; - if (*src == '\0') - src = "."; + *wsrc++ = '\0'; /* Substitute "." for empty filename */ + if (*wsrc == '\0') + src = "."; + else + src = wsrc; /* Separate username and hostname */ user = host; @@ -2095,21 +2165,25 @@ static void tolocal(int argc, char *argv[]) */ static void get_dir_list(int argc, char *argv[]) { - char *src, *host, *user; - char *cmd, *p, *q; + char *wsrc, *host, *user; + const char *src; + char *cmd, *p; + const char *q; char c; - src = argv[0]; + wsrc = argv[0]; /* Separate host from filename */ - host = src; - src = colon(src); - if (src == NULL) + host = wsrc; + wsrc = colon(wsrc); + if (wsrc == NULL) bump("Local file listing not supported"); - *src++ = '\0'; - if (*src == '\0') - src = "."; + *wsrc++ = '\0'; /* Substitute "." for empty filename */ + if (*wsrc == '\0') + src = "."; + else + src = wsrc; /* Separate username and hostname */ user = host; @@ -2175,13 +2249,20 @@ static void usage(void) 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(" -i key private key file for user authentication\n"); printf(" -noagent disable use of Pageant\n"); printf(" -agent enable use of Pageant\n"); + printf(" -hostkey aa:bb:cc:...\n"); + printf(" manually specify a host key (may be repeated)\n"); printf(" -batch disable all interactive prompts\n"); + printf(" -proxycmd command\n"); + printf(" use 'command' as local proxy\n"); printf(" -unsafe allow server-side wildcards (DANGEROUS)\n"); printf(" -sftp force use of SFTP protocol\n"); printf(" -scp force use of SCP protocol\n"); + printf(" -sshlog file\n"); + printf(" -sshrawlog file\n"); + printf(" log protocol details to a file\n"); #if 0 /* * -gui is an internal option, used by GUI front ends to get @@ -2198,11 +2279,13 @@ static void usage(void) void version(void) { - printf("pscp: %s\n", ver); - cleanup_exit(1); + char *buildinfo_text = buildinfo("\n"); + printf("pscp: %s\n%s\n", ver, buildinfo_text); + sfree(buildinfo_text); + exit(0); } -void cmdline_error(char *p, ...) +void cmdline_error(const char *p, ...) { va_list ap; fprintf(stderr, "pscp: "); @@ -2213,6 +2296,9 @@ void cmdline_error(char *p, ...) exit(1); } +const int share_can_be_downstream = TRUE; +const int share_can_be_upstream = FALSE; + /* * Main program. (Called `psftp_main' because it gets called from * *sftp.c; bit silly, I know, but it had to be called _something_.) @@ -2258,9 +2344,12 @@ int psftp_main(int argc, char *argv[]) preserve = 1; } else if (strcmp(argv[i], "-q") == 0) { statistics = 0; - } else if (strcmp(argv[i], "-h") == 0 || strcmp(argv[i], "-?") == 0) { + } else if (strcmp(argv[i], "-h") == 0 || + strcmp(argv[i], "-?") == 0 || + strcmp(argv[i], "--help") == 0) { usage(); - } else if (strcmp(argv[i], "-V") == 0) { + } else if (strcmp(argv[i], "-V") == 0 || + strcmp(argv[i], "--version") == 0) { version(); } else if (strcmp(argv[i], "-ls") == 0) { list = 1; @@ -2304,6 +2393,7 @@ int psftp_main(int argc, char *argv[]) if (back != NULL && back->connected(backhandle)) { char ch; back->special(backhandle, TS_EOF); + sent_eof = TRUE; ssh_scp_recv((unsigned char *) &ch, 1); } random_save_seed();