First phase of SFTP re-engineering. Each base-level fxp_* function
[u/mdw/putty] / scp.c
diff --git a/scp.c b/scp.c
index ec3575b..f146bd7 100644 (file)
--- a/scp.c
+++ b/scp.c
@@ -99,6 +99,10 @@ static int gui_mode = 0;
 static char *gui_hwnd = NULL;
 static int using_sftp = 0;
 
+static Backend *back;
+static void *backhandle;
+static Config cfg;
+
 static void source(char *src);
 static void rsource(char *src);
 static void sink(char *targ, char *src);
@@ -117,7 +121,7 @@ static void gui_update_stats(char *name, unsigned long size,
  */
 #define MAX_SCP_BUFSIZE 16384
 
-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,
@@ -157,13 +161,15 @@ static void tell_str(FILE * stream, char *str)
 
 static void tell_user(FILE * stream, char *fmt, ...)
 {
-    char str[0x100];                  /* Make the size big enough */
+    char *str, *str2;
     va_list ap;
     va_start(ap, fmt);
-    vsprintf(str, fmt, ap);
+    str = dupvprintf(fmt, ap);
     va_end(ap);
-    strcat(str, "\n");
-    tell_str(stream, str);
+    str2 = dupcat(str, "\n", NULL);
+    sfree(str);
+    tell_str(stream, str2);
+    sfree(str2);
 }
 
 static void gui_update_stats(char *name, unsigned long size,
@@ -213,14 +219,15 @@ static void gui_update_stats(char *name, unsigned long size,
  */
 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");
-    tell_str(stderr, str);
+    tell_str(stderr, str2);
+    sfree(str2);
     errs++;
 
     if (gui_mode) {
@@ -236,14 +243,15 @@ void fatalbox(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");
-    tell_str(stderr, str);
+    tell_str(stderr, str2);
+    sfree(str2);
     errs++;
 
     if (gui_mode) {
@@ -257,16 +265,17 @@ void modalfatalbox(char *fmt, ...)
 
     cleanup_exit(1);
 }
-void connection_fatal(char *fmt, ...)
+void connection_fatal(void *frontend, 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");
-    tell_str(stderr, str);
+    tell_str(stderr, str2);
+    sfree(str2);
     errs++;
 
     if (gui_mode) {
@@ -296,6 +305,16 @@ char *do_select(SOCKET skt, int startup)
 extern int select_result(WPARAM, LPARAM);
 
 /*
+ * In pscp, all agent requests should be synchronous, so this is a
+ * never-called stub.
+ */
+void agent_schedule_callback(void (*callback)(void *, void *, int),
+                            void *callback_ctx, void *data, int len)
+{
+    assert(!"We shouldn't be here");
+}
+
+/*
  * Receive a block of data from the SSH link. Block until all data
  * is available.
  *
@@ -308,7 +327,7 @@ 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(void *frontend, 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;
@@ -344,8 +363,7 @@ int from_backend(void *frontend, 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));
+           pending = sresize(pending, pendsize, unsigned char);
            if (!pending)
                fatalbox("Out of memory");
        }
@@ -408,7 +426,7 @@ static void ssh_scp_init(void)
 {
     if (scp_ssh_socket == INVALID_SOCKET)
        return;
-    while (!back->sendok()) {
+    while (!back->sendok(backhandle)) {
        fd_set readfds;
        FD_ZERO(&readfds);
        FD_SET(scp_ssh_socket, &readfds);
@@ -416,7 +434,13 @@ static void ssh_scp_init(void)
            return;                    /* doom */
        select_result((WPARAM) scp_ssh_socket, (LPARAM) FD_READ);
     }
-    using_sftp = !ssh_fallback_cmd;
+    using_sftp = !ssh_fallback_cmd(backhandle);
+    if (verbose) {
+       if (using_sftp)
+           tell_user(stderr, "Using SFTP");
+       else
+           tell_user(stderr, "Using SCP1");
+    }
 }
 
 /*
@@ -424,19 +448,20 @@ static void ssh_scp_init(void)
  */
 static void bump(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);
     va_end(ap);
-    strcat(str, "\n");
-    tell_str(stderr, str);
+    str2 = dupcat(str, "\n", NULL);
+    sfree(str);
+    tell_str(stderr, str2);
+    sfree(str2);
     errs++;
 
-    if (back != NULL && back->socket() != NULL) {
+    if (back != NULL && back->socket(backhandle) != NULL) {
        char ch;
-       back->special(TS_EOF);
+       back->special(backhandle, TS_EOF);
        ssh_scp_recv(&ch, 1);
     }
 
@@ -457,7 +482,8 @@ static void bump(char *fmt, ...)
  */
 static void do_cmd(char *host, char *user, char *cmd)
 {
-    char *err, *realhost;
+    const char *err;
+    char *realhost;
     DWORD namelen;
 
     if (host == NULL || host[0] == '\0')
@@ -484,7 +510,7 @@ static void do_cmd(char *host, char *user, char *cmd)
     /*
      * Enact command-line overrides.
      */
-    cmdline_run_saved();
+    cmdline_run_saved(&cfg);
 
     /*
      * Trim leading whitespace off the hostname if it's there.
@@ -535,7 +561,7 @@ static void do_cmd(char *host, char *user, char *cmd)
        namelen = 0;
        if (GetUserName(user, &namelen) == FALSE)
            bump("Empty user name");
-       user = smalloc(namelen * sizeof(char));
+       user = snewn(namelen, char);
        GetUserName(user, &namelen);
        if (verbose)
            tell_user(stderr, "Guessing user name: %s", user);
@@ -565,9 +591,12 @@ static void do_cmd(char *host, char *user, char *cmd)
 
     back = &ssh_backend;
 
-    err = back->init(NULL, cfg.host, cfg.port, &realhost, 0);
+    err = back->init(NULL, &backhandle, &cfg, cfg.host, cfg.port, &realhost,0);
     if (err != NULL)
        bump("ssh_init: %s", err);
+    logctx = log_init(NULL, &cfg);
+    back->provide_logctx(backhandle, logctx);
+    console_provide_logctx(logctx);
     ssh_scp_init();
     if (verbose && realhost != NULL)
        tell_user(stderr, "Connected to %s\n", realhost);
@@ -712,7 +741,7 @@ int sftp_recvdata(char *buf, int len)
 }
 int sftp_senddata(char *buf, int len)
 {
-    back->send((unsigned char *) buf, len);
+    back->send(backhandle, (unsigned char *) buf, len);
     return 1;
 }
 
@@ -730,6 +759,8 @@ void scp_sftp_listdir(char *dirname)
     struct fxp_handle *dirh;
     struct fxp_names *names;
     struct fxp_name *ournames;
+    struct sftp_packet *pktin;
+    struct sftp_request *req, *rreq;
     int nnames, namesize;
     int i;
 
@@ -741,7 +772,11 @@ void scp_sftp_listdir(char *dirname)
 
     printf("Listing directory %s\n", dirname);
 
-    dirh = fxp_opendir(dirname);
+    sftp_register(req = fxp_opendir_send(dirname));
+    rreq = sftp_find_request(pktin = sftp_recv());
+    assert(rreq == req);
+    dirh = fxp_opendir_recv(pktin);
+
     if (dirh == NULL) {
        printf("Unable to open %s: %s\n", dirname, fxp_error());
     } else {
@@ -750,7 +785,11 @@ void scp_sftp_listdir(char *dirname)
 
        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);
+
            if (names == NULL) {
                if (fxp_error_type() == SSH_FX_EOF)
                    break;
@@ -764,8 +803,7 @@ void scp_sftp_listdir(char *dirname)
 
            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++)
@@ -774,7 +812,10 @@ void scp_sftp_listdir(char *dirname)
            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);
 
        /*
         * Now we have our filenames. Sort them by actual file
@@ -819,16 +860,23 @@ void scp_source_setup(char *target, int shouldbedir)
         * Find out whether the target filespec is in fact a
         * directory.
         */
+       struct sftp_packet *pktin;
+       struct sftp_request *req, *rreq;
        struct fxp_attrs attrs;
+       int ret;
 
        if (!fxp_init()) {
            tell_user(stderr, "unable to initialise SFTP: %s", fxp_error());
            errs++;
-           return 1;
+           return;
        }
 
-       if (!fxp_stat(target, &attrs) ||
-           !(attrs.flags & SSH_FILEXFER_ATTR_PERMISSIONS))
+       sftp_register(req = fxp_stat_send(target));
+       rreq = sftp_find_request(pktin = sftp_recv());
+       assert(rreq == req);
+       ret = fxp_stat_recv(pktin, &attrs);
+
+       if (!ret || !(attrs.flags & SSH_FILEXFER_ATTR_PERMISSIONS))
            scp_sftp_targetisdir = 0;
        else
            scp_sftp_targetisdir = (attrs.permissions & 0040000) != 0;
@@ -850,8 +898,8 @@ int scp_send_errmsg(char *str)
     if (using_sftp) {
        /* do nothing; we never need to send our errors to the server */
     } else {
-       back->send("\001", 1);         /* scp protocol error prefix */
-       back->send(str, strlen(str));
+       back->send(backhandle, "\001", 1);/* scp protocol error prefix */
+       back->send(backhandle, str, strlen(str));
     }
     return 0;                         /* can't fail */
 }
@@ -866,7 +914,7 @@ int scp_send_filetimes(unsigned long mtime, unsigned long atime)
     } else {
        char buf[80];
        sprintf(buf, "T%lu 0 %lu 0\n", mtime, atime);
-       back->send(buf, strlen(buf));
+       back->send(backhandle, buf, strlen(buf));
        return response();
     }
 }
@@ -875,13 +923,21 @@ int scp_send_filename(char *name, unsigned long size, int modes)
 {
     if (using_sftp) {
        char *fullname;
+       struct sftp_packet *pktin;
+       struct sftp_request *req, *rreq;
+
        if (scp_sftp_targetisdir) {
            fullname = dupcat(scp_sftp_remotepath, "/", name, NULL);
        } else {
            fullname = dupstr(scp_sftp_remotepath);
        }
-       scp_sftp_filehandle =
-           fxp_open(fullname, SSH_FXF_WRITE | SSH_FXF_CREAT | SSH_FXF_TRUNC);
+
+       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);
+
        if (!scp_sftp_filehandle) {
            tell_user(stderr, "pscp: unable to open %s: %s",
                      fullname, fxp_error());
@@ -894,9 +950,9 @@ int scp_send_filename(char *name, unsigned long size, int modes)
     } else {
        char buf[40];
        sprintf(buf, "C%04o %lu ", modes, size);
-       back->send(buf, strlen(buf));
-       back->send(name, strlen(name));
-       back->send("\n", 1);
+       back->send(backhandle, buf, strlen(buf));
+       back->send(backhandle, name, strlen(name));
+       back->send(backhandle, "\n", 1);
        return response();
     }
 }
@@ -904,10 +960,21 @@ int scp_send_filename(char *name, unsigned long size, int modes)
 int scp_send_filedata(char *data, int len)
 {
     if (using_sftp) {
+       int ret;
+       struct sftp_packet *pktin;
+       struct sftp_request *req, *rreq;
+
        if (!scp_sftp_filehandle) {
            return 1;
        }
-       if (!fxp_write(scp_sftp_filehandle, data, scp_sftp_fileoffset, len)) {
+
+       sftp_register(req = fxp_write_send(scp_sftp_filehandle,
+                                          data, scp_sftp_fileoffset, len));
+       rreq = sftp_find_request(pktin = sftp_recv());
+       assert(rreq == req);
+       ret = fxp_write_recv(pktin);
+
+       if (!ret) {
            tell_user(stderr, "error while writing: %s\n", fxp_error());
            errs++;
            return 1;
@@ -915,7 +982,7 @@ int scp_send_filedata(char *data, int len)
        scp_sftp_fileoffset = uint64_add32(scp_sftp_fileoffset, len);
        return 0;
     } else {
-       int bufsize = back->send(data, len);
+       int bufsize = back->send(backhandle, data, len);
 
        /*
         * If the network transfer is backing up - that is, the
@@ -926,7 +993,7 @@ int scp_send_filedata(char *data, int len)
        while (bufsize > MAX_SCP_BUFSIZE) {
            if (!scp_process_network_event())
                return 1;
-           bufsize = back->sendbuffer();
+           bufsize = back->sendbuffer(backhandle);
        }
 
        return 0;
@@ -937,6 +1004,10 @@ int scp_send_finish(void)
 {
     if (using_sftp) {
        struct fxp_attrs attrs;
+       struct sftp_packet *pktin;
+       struct sftp_request *req, *rreq;
+       int ret;
+
        if (!scp_sftp_filehandle) {
            return 1;
        }
@@ -944,16 +1015,23 @@ int scp_send_finish(void)
            attrs.flags = SSH_FILEXFER_ATTR_ACMODTIME;
            attrs.atime = scp_sftp_atime;
            attrs.mtime = scp_sftp_mtime;
-           if (!fxp_fsetstat(scp_sftp_filehandle, attrs)) {
+           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);
+           if (!ret) {
                tell_user(stderr, "unable to set file times: %s\n", fxp_error());
                errs++;
            }
        }
-       fxp_close(scp_sftp_filehandle);
+       sftp_register(req = fxp_close_send(scp_sftp_filehandle));
+       rreq = sftp_find_request(pktin = sftp_recv());
+       assert(rreq == req);
+       fxp_close_recv(pktin);
        scp_has_times = 0;
        return 0;
     } else {
-       back->send("", 1);
+       back->send(backhandle, "", 1);
        return response();
     }
 }
@@ -978,6 +1056,10 @@ int scp_send_dirname(char *name, int modes)
        char *fullname;
        char const *err;
        struct fxp_attrs attrs;
+       struct sftp_packet *pktin;
+       struct sftp_request *req, *rreq;
+       int ret;
+
        if (scp_sftp_targetisdir) {
            fullname = dupcat(scp_sftp_remotepath, "/", name, NULL);
        } else {
@@ -991,12 +1073,22 @@ 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.
         */
-       if (!fxp_mkdir(fullname))
+       sftp_register(req = fxp_mkdir_send(fullname));
+       rreq = sftp_find_request(pktin = sftp_recv());
+       assert(rreq == req);
+       ret = fxp_mkdir_recv(pktin);
+
+       if (!ret)
            err = fxp_error();
        else
            err = "server reported no error";
-       if (!fxp_stat(fullname, &attrs) ||
-           !(attrs.flags & SSH_FILEXFER_ATTR_PERMISSIONS) ||
+
+       sftp_register(req = fxp_stat_send(fullname));
+       rreq = sftp_find_request(pktin = sftp_recv());
+       assert(rreq == req);
+       ret = fxp_stat_recv(pktin, &attrs);
+
+       if (!ret || !(attrs.flags & SSH_FILEXFER_ATTR_PERMISSIONS) ||
            !(attrs.permissions & 0040000)) {
            tell_user(stderr, "unable to create directory %s: %s",
                      fullname, err);
@@ -1010,9 +1102,9 @@ int scp_send_dirname(char *name, int modes)
     } else {
        char buf[40];
        sprintf(buf, "D%04o 0 ", modes);
-       back->send(buf, strlen(buf));
-       back->send(name, strlen(name));
-       back->send("\n", 1);
+       back->send(backhandle, buf, strlen(buf));
+       back->send(backhandle, name, strlen(name));
+       back->send(backhandle, "\n", 1);
        return response();
     }
 }
@@ -1023,7 +1115,7 @@ int scp_send_enddir(void)
        sfree(scp_sftp_remotepath);
        return 0;
     } else {
-       back->send("E\n", 2);
+       back->send(backhandle, "E\n", 2);
        return response();
     }
 }
@@ -1051,7 +1143,7 @@ int scp_sink_setup(char *source, int preserve, int recursive)
         * wildcardness comes before the final slash) and arrange
         * things so that a dirstack entry will be set up.
         */
-       newsource = smalloc(1+strlen(source));
+       newsource = snewn(1+strlen(source), char);
        if (!wc_unescape(newsource, source)) {
            /* Yes, here we go; it's a wildcard. Bah. */
            char *dupsource, *lastpart, *dirpart, *wildcard;
@@ -1084,7 +1176,7 @@ int scp_sink_setup(char *source, int preserve, int recursive)
             * wildcard escapes from the directory part, throwing
             * an error if it contains a real wildcard.
             */
-           dirpart = smalloc(1+strlen(dupsource));
+           dirpart = snewn(1+strlen(dupsource), char);
            if (!wc_unescape(dirpart, dupsource)) {
                tell_user(stderr, "%s: multiple-level wildcards unsupported",
                          source);
@@ -1118,7 +1210,7 @@ int scp_sink_setup(char *source, int preserve, int recursive)
 int scp_sink_init(void)
 {
     if (!using_sftp) {
-       back->send("", 1);
+       back->send(backhandle, "", 1);
     }
     return 0;
 }
@@ -1143,6 +1235,8 @@ int scp_get_sink_action(struct scp_sink_action *act)
        char *fname;
        int must_free_fname;
        struct fxp_attrs attrs;
+       struct sftp_packet *pktin;
+       struct sftp_request *req, *rreq;
        int ret;
 
        if (!scp_sftp_dirstack_head) {
@@ -1211,7 +1305,11 @@ 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.
         */
-       ret = fxp_stat(fname, &attrs);
+       sftp_register(req = fxp_stat_send(fname));
+       rreq = sftp_find_request(pktin = sftp_recv());
+       assert(rreq == req);
+       ret = fxp_stat_recv(pktin, &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());
@@ -1263,7 +1361,11 @@ int scp_get_sink_action(struct scp_sink_action *act)
             * list), we must push the other (target,namelist) pair
             * on a stack.
             */
-           dirhandle = fxp_opendir(fname);
+           sftp_register(req = fxp_opendir_send(fname));
+           rreq = sftp_find_request(pktin = sftp_recv());
+           assert(rreq == req);
+           dirhandle = fxp_opendir_recv(pktin);
+
            if (!dirhandle) {
                tell_user(stderr, "scp: unable to open directory %s: %s",
                          fname, fxp_error());
@@ -1276,7 +1378,11 @@ int scp_get_sink_action(struct scp_sink_action *act)
            while (1) {
                int i;
 
-               names = fxp_readdir(dirhandle);
+               sftp_register(req = fxp_readdir_send(dirhandle));
+               rreq = sftp_find_request(pktin = sftp_recv());
+               assert(rreq == req);
+               names = fxp_readdir_recv(pktin);
+
                if (names == NULL) {
                    if (fxp_error_type() == SSH_FX_EOF)
                        break;
@@ -1293,17 +1399,19 @@ int scp_get_sink_action(struct scp_sink_action *act)
                }
                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];
                names->nnames = 0;             /* prevent free_names */
                fxp_free_names(names);
            }
-           fxp_close(dirhandle);
+           sftp_register(req = fxp_close_send(dirhandle));
+           rreq = sftp_find_request(pktin = sftp_recv());
+           assert(rreq == req);
+           fxp_close_recv(pktin);
 
-           newitem = smalloc(sizeof(struct scp_sftp_dirstack));
+           newitem = snew(struct scp_sftp_dirstack);
            newitem->next = scp_sftp_dirstack_head;
            newitem->names = ournames;
            newitem->namepos = 0;
@@ -1391,7 +1499,7 @@ int scp_get_sink_action(struct scp_sink_action *act)
                    bump("Lost connection");
                if (i >= bufsize) {
                    bufsize = i + 128;
-                   act->buf = srealloc(act->buf, bufsize);
+                   act->buf = sresize(act->buf, bufsize, char);
                }
                act->buf[i++] = ch;
            } while (ch != '\n');
@@ -1404,14 +1512,14 @@ int scp_get_sink_action(struct scp_sink_action *act)
              case '\02':                      /* fatal error */
                bump("%s", act->buf);
              case 'E':
-               back->send("", 1);
+               back->send(backhandle, "", 1);
                act->action = SCP_SINK_ENDDIR;
                return 0;
              case 'T':
                if (sscanf(act->buf, "%ld %*d %ld %*d",
                           &act->mtime, &act->atime) == 2) {
                    act->settime = 1;
-                   back->send("", 1);
+                   back->send(backhandle, "", 1);
                    continue;          /* go round again */
                }
                bump("Protocol error: Illegal time format");
@@ -1443,8 +1551,14 @@ int scp_get_sink_action(struct scp_sink_action *act)
 int scp_accept_filexfer(void)
 {
     if (using_sftp) {
-       scp_sftp_filehandle =
-           fxp_open(scp_sftp_currentname, SSH_FXF_READ);
+       struct sftp_packet *pktin;
+       struct sftp_request *req, *rreq;
+
+       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);
+
        if (!scp_sftp_filehandle) {
            tell_user(stderr, "pscp: unable to open %s: %s",
                      scp_sftp_currentname, fxp_error());
@@ -1455,7 +1569,7 @@ int scp_accept_filexfer(void)
        sfree(scp_sftp_currentname);
        return 0;
     } else {
-       back->send("", 1);
+       back->send(backhandle, "", 1);
        return 0;                      /* can't fail */
     }
 }
@@ -1463,8 +1577,16 @@ int scp_accept_filexfer(void)
 int scp_recv_filedata(char *data, int len)
 {
     if (using_sftp) {
-       int actuallen = fxp_read(scp_sftp_filehandle, data,
-                                scp_sftp_fileoffset, len);
+       struct sftp_packet *pktin;
+       struct sftp_request *req, *rreq;
+       int actuallen;
+
+       sftp_register(req = fxp_read_send(scp_sftp_filehandle,
+                                         scp_sftp_fileoffset, len));
+       rreq = sftp_find_request(pktin = sftp_recv());
+       assert(rreq == req);
+       actuallen = fxp_read_recv(pktin, data, len);
+
        if (actuallen == -1 && fxp_error_type() != SSH_FX_EOF) {
            tell_user(stderr, "pscp: error while reading: %s", fxp_error());
            errs++;
@@ -1484,10 +1606,16 @@ int scp_recv_filedata(char *data, int len)
 int scp_finish_filerecv(void)
 {
     if (using_sftp) {
-       fxp_close(scp_sftp_filehandle);
+       struct sftp_packet *pktin;
+       struct sftp_request *req, *rreq;
+
+       sftp_register(req = fxp_close_send(scp_sftp_filehandle));
+       rreq = sftp_find_request(pktin = sftp_recv());
+       assert(rreq == req);
+       fxp_close_recv(pktin);
        return 0;
     } else {
-       back->send("", 1);
+       back->send(backhandle, "", 1);
        return response();
     }
 }
@@ -1498,16 +1626,17 @@ int scp_finish_filerecv(void)
  */
 static void run_err(const char *fmt, ...)
 {
-    char str[2048];
+    char *str, *str2;
     va_list ap;
     va_start(ap, fmt);
     errs++;
-    strcpy(str, "scp: ");
-    vsprintf(str + strlen(str), fmt, ap);
-    strcat(str, "\n");
-    scp_send_errmsg(str);
-    tell_user(stderr, "%s", str);
+    str = dupvprintf(fmt, ap);
+    str2 = dupcat("scp: ", str, "\n", NULL);
+    sfree(str);
+    scp_send_errmsg(str2);
+    tell_user(stderr, "%s", str2);
     va_end(ap);
+    sfree(str2);
 }
 
 /*
@@ -1916,12 +2045,11 @@ static void toremote(int argc, char *argv[])
        FindClose(fh);
     }
 
-    cmd = smalloc(strlen(targ) + 100);
-    sprintf(cmd, "scp%s%s%s%s -t %s",
-           verbose ? " -v" : "",
-           recursive ? " -r" : "",
-           preserve ? " -p" : "",
-           targetshouldbedirectory ? " -d" : "", targ);
+    cmd = dupprintf("scp%s%s%s%s -t %s",
+                   verbose ? " -v" : "",
+                   recursive ? " -r" : "",
+                   preserve ? " -p" : "",
+                   targetshouldbedirectory ? " -d" : "", targ);
     do_cmd(host, user, cmd);
     sfree(cmd);
 
@@ -2020,12 +2148,11 @@ static void tolocal(int argc, char *argv[])
            user = NULL;
     }
 
-    cmd = smalloc(strlen(src) + 100);
-    sprintf(cmd, "scp%s%s%s%s -f %s",
-           verbose ? " -v" : "",
-           recursive ? " -r" : "",
-           preserve ? " -p" : "",
-           targetshouldbedirectory ? " -d" : "", src);
+    cmd = dupprintf("scp%s%s%s%s -f %s",
+                   verbose ? " -v" : "",
+                   recursive ? " -r" : "",
+                   preserve ? " -p" : "",
+                   targetshouldbedirectory ? " -d" : "", src);
     do_cmd(host, user, cmd);
     sfree(cmd);
 
@@ -2068,7 +2195,7 @@ static void get_dir_list(int argc, char *argv[])
            user = NULL;
     }
 
-    cmd = smalloc(4 * strlen(src) + 100);
+    cmd = snewn(4 * strlen(src) + 100, char);
     strcpy(cmd, "ls -la '");
     p = cmd + strlen(cmd);
     for (q = src; *q; q++) {
@@ -2156,7 +2283,7 @@ void cmdline_error(char *p, ...)
     va_start(ap, p);
     vfprintf(stderr, p, ap);
     va_end(ap);
-    fputc('\n', stderr);
+    fprintf(stderr, "\n      try typing just \"pscp\" for help\n");
     exit(1);
 }
 
@@ -2169,7 +2296,7 @@ int main(int argc, char *argv[])
 
     default_protocol = PROT_TELNET;
 
-    flags = FLAG_STDERR;
+    flags = FLAG_STDERR | FLAG_SYNCAGENT;
     cmdline_tooltype = TOOLTYPE_FILETRANSFER;
     ssh_get_line = &console_get_line;
     init_winsock();
@@ -2179,7 +2306,7 @@ int main(int argc, char *argv[])
        int ret;
        if (argv[i][0] != '-')
            break;
-       ret = cmdline_process_param(argv[i], i+1<argc?argv[i+1]:NULL, 1);
+       ret = cmdline_process_param(argv[i], i+1<argc?argv[i+1]:NULL, 1, &cfg);
        if (ret == -2) {
            cmdline_error("option \"%s\" requires an argument", argv[i]);
        } else if (ret == 2) {
@@ -2209,8 +2336,9 @@ int main(int argc, char *argv[])
        } else if (strcmp(argv[i], "--") == 0) {
            i++;
            break;
-       } else
-           usage();
+       } else {
+           cmdline_error("unknown option \"%s\"", argv[i]);
+       }
     }
     argc -= i;
     argv += i;
@@ -2234,9 +2362,9 @@ int main(int argc, char *argv[])
            tolocal(argc, argv);
     }
 
-    if (back != NULL && back->socket() != NULL) {
+    if (back != NULL && back->socket(backhandle) != NULL) {
        char ch;
-       back->special(TS_EOF);
+       back->special(backhandle, TS_EOF);
        ssh_scp_recv(&ch, 1);
     }
     WSACleanup();