]> asedeno.scripts.mit.edu Git - PuTTY.git/blobdiff - psftp.c
Remove an unused variable.
[PuTTY.git] / psftp.c
diff --git a/psftp.c b/psftp.c
index d7b738129b4f0836f100fb824ff83e3d60d5f17d..793626197d3837e6927f8076aaf7f70faa2b6646 100644 (file)
--- a/psftp.c
+++ b/psftp.c
@@ -39,6 +39,26 @@ static void *backhandle;
 static Conf *conf;
 int sent_eof = FALSE;
 
+/* ----------------------------------------------------------------------
+ * Manage sending requests and waiting for replies.
+ */
+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;
+}
+
 /* ----------------------------------------------------------------------
  * Higher-level helper functions used in commands.
  */
@@ -48,16 +68,16 @@ int sent_eof = FALSE;
  * canonification fails, at least fall back to returning a _valid_
  * pathname (though it may be ugly, eg /home/simon/../foobar).
  */
-char *canonify(char *name)
+char *canonify(const char *name)
 {
     char *fullname, *canonname;
     struct sftp_packet *pktin;
-    struct sftp_request *req, *rreq;
+    struct sftp_request *req;
 
     if (name[0] == '/') {
        fullname = dupstr(name);
     } else {
-       char *slash;
+       const char *slash;
        if (pwd[strlen(pwd) - 1] == '/')
            slash = "";
        else
@@ -65,10 +85,9 @@ char *canonify(char *name)
        fullname = dupcat(pwd, slash, name, NULL);
     }
 
-    sftp_register(req = fxp_realpath_send(fullname));
-    rreq = sftp_find_request(pktin = sftp_recv());
-    assert(rreq == req);
-    canonname = fxp_realpath_recv(pktin, rreq);
+    req = fxp_realpath_send(fullname);
+    pktin = sftp_wait_for_reply(req);
+    canonname = fxp_realpath_recv(pktin, req);
 
     if (canonname) {
        sfree(fullname);
@@ -123,13 +142,12 @@ char *canonify(char *name)
         */
        fullname[i] = '\0';            /* separate the string */
        if (i == 0) {
-           sftp_register(req = fxp_realpath_send("/"));
+           req = fxp_realpath_send("/");
        } else {
-           sftp_register(req = fxp_realpath_send(fullname));
+           req = fxp_realpath_send(fullname);
        }
-       rreq = sftp_find_request(pktin = sftp_recv());
-       assert(rreq == req);
-       canonname = fxp_realpath_recv(pktin, rreq);
+       pktin = sftp_wait_for_reply(req);
+       canonname = fxp_realpath_recv(pktin, req);
 
        if (!canonname) {
            /* Even that failed. Restore our best guess at the
@@ -154,8 +172,13 @@ char *canonify(char *name)
 /*
  * Return a pointer to the portion of str that comes after the last
  * slash (or backslash or colon, if `local' is TRUE).
+ *
+ * This function has the annoying strstr() property of taking a const
+ * char * and returning a char *. You should treat it as if it was a
+ * pair of overloaded functions, one mapping mutable->mutable and the
+ * other const->const :-(
  */
-static char *stripslashes(char *str, int local)
+static char *stripslashes(const char *str, int local)
 {
     char *p;
 
@@ -172,7 +195,7 @@ static char *stripslashes(char *str, int local)
        if (p) str = p+1;
     }
 
-    return str;
+    return (char *)str;
 }
 
 /*
@@ -208,7 +231,7 @@ int sftp_get_file(char *fname, char *outfname, int recurse, int restart)
 {
     struct fxp_handle *fh;
     struct sftp_packet *pktin;
-    struct sftp_request *req, *rreq;
+    struct sftp_request *req;
     struct fxp_xfer *xfer;
     uint64 offset;
     WFile *file;
@@ -223,10 +246,9 @@ int sftp_get_file(char *fname, char *outfname, int recurse, int restart)
     if (recurse) {
        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);
+        req = fxp_stat_send(fname);
+        pktin = sftp_wait_for_reply(req);
+       result = fxp_stat_recv(pktin, req, &attrs);
 
        if (result &&
            (attrs.flags & SSH_FILEXFER_ATTR_PERMISSIONS) &&
@@ -252,10 +274,9 @@ int sftp_get_file(char *fname, char *outfname, int recurse, int restart)
             * Now get the list of filenames in the remote
             * directory.
             */
-           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) {
                printf("%s: unable to open directory: %s\n",
@@ -267,15 +288,19 @@ int sftp_get_file(char *fname, char *outfname, int recurse, int restart)
            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;
                    printf("%s: reading directory: %s\n", fname, fxp_error());
+
+                    req = fxp_close_send(dirhandle);
+                    pktin = sftp_wait_for_reply(req);
+                    fxp_close_recv(pktin, req);
+
                    sfree(ournames);
                    return 0;
                }
@@ -301,10 +326,9 @@ int sftp_get_file(char *fname, char *outfname, int recurse, int restart)
                    }
                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);
 
            /*
             * Sort the names into a clear order. This ought to
@@ -313,7 +337,8 @@ int sftp_get_file(char *fname, char *outfname, int recurse, int restart)
             * readdirs on the same remote directory return a
             * different order.
             */
-           qsort(ournames, nnames, sizeof(*ournames), sftp_name_compare);
+            if (nnames > 0)
+                qsort(ournames, nnames, sizeof(*ournames), sftp_name_compare);
 
            /*
             * If we're in restart mode, find the last filename on
@@ -328,11 +353,8 @@ int sftp_get_file(char *fname, char *outfname, int recurse, int restart)
                 while (i < nnames) {
                     char *nextoutfname;
                     int ret;
-                    if (outfname)
-                        nextoutfname = dir_file_cat(outfname,
-                                                    ournames[i]->filename);
-                    else
-                        nextoutfname = dupstr(ournames[i]->filename);
+                    nextoutfname = dir_file_cat(outfname,
+                                                ournames[i]->filename);
                     ret = (file_type(nextoutfname) == FILE_TYPE_NONEXISTENT);
                     sfree(nextoutfname);
                     if (ret)
@@ -354,11 +376,7 @@ int sftp_get_file(char *fname, char *outfname, int recurse, int restart)
                int ret;
                
                nextfname = dupcat(fname, "/", ournames[i]->filename, NULL);
-               if (outfname)
-                   nextoutfname = dir_file_cat(outfname,
-                                               ournames[i]->filename);
-               else
-                   nextoutfname = dupstr(ournames[i]->filename);
+                nextoutfname = dir_file_cat(outfname, ournames[i]->filename);
                ret = sftp_get_file(nextfname, nextoutfname, recurse, restart);
                restart = FALSE;       /* after first partial file, do full */
                sfree(nextoutfname);
@@ -384,16 +402,14 @@ int sftp_get_file(char *fname, char *outfname, int recurse, int restart)
        }
     }
 
-    sftp_register(req = fxp_stat_send(fname));
-    rreq = sftp_find_request(pktin = sftp_recv());
-    assert(rreq == req);
-    if (!fxp_stat_recv(pktin, rreq, &attrs))
+    req = fxp_stat_send(fname);
+    pktin = sftp_wait_for_reply(req);
+    if (!fxp_stat_recv(pktin, req, &attrs))
         attrs.flags = 0;
 
-    sftp_register(req = fxp_open_send(fname, SSH_FXF_READ, NULL));
-    rreq = sftp_find_request(pktin = sftp_recv());
-    assert(rreq == req);
-    fh = fxp_open_recv(pktin, rreq);
+    req = fxp_open_send(fname, SSH_FXF_READ, NULL);
+    pktin = sftp_wait_for_reply(req);
+    fh = fxp_open_recv(pktin, req);
 
     if (!fh) {
        printf("%s: open for read: %s\n", fname, fxp_error());
@@ -409,10 +425,9 @@ int sftp_get_file(char *fname, char *outfname, int recurse, int restart)
     if (!file) {
        printf("local: unable to open %s\n", outfname);
 
-       sftp_register(req = fxp_close_send(fh));
-       rreq = sftp_find_request(pktin = sftp_recv());
-       assert(rreq == req);
-       fxp_close_recv(pktin, rreq);
+        req = fxp_close_send(fh);
+        pktin = sftp_wait_for_reply(req);
+       fxp_close_recv(pktin, req);
 
        return 0;
     }
@@ -423,10 +438,9 @@ int sftp_get_file(char *fname, char *outfname, int recurse, int restart)
            close_wfile(file);
            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);
+           req = fxp_close_send(fh);
+            pktin = sftp_wait_for_reply(req);
+           fxp_close_recv(pktin, req);
                
            return 0;
        }
@@ -454,12 +468,13 @@ int sftp_get_file(char *fname, char *outfname, int recurse, int restart)
        xfer_download_queue(xfer);
        pktin = sftp_recv();
        ret = xfer_download_gotpkt(xfer, pktin);
-
-       if (ret < 0) {
+       if (ret <= 0) {
            if (!shown_err) {
                printf("error while reading: %s\n", fxp_error());
                shown_err = TRUE;
            }
+            if (ret == INT_MIN)        /* pktin not even freed */
+                sfree(pktin);
             ret = 0;
        }
 
@@ -490,10 +505,9 @@ int sftp_get_file(char *fname, char *outfname, int recurse, int restart)
 
     close_wfile(file);
 
-    sftp_register(req = fxp_close_send(fh));
-    rreq = sftp_find_request(pktin = sftp_recv());
-    assert(rreq == req);
-    fxp_close_recv(pktin, rreq);
+    req = fxp_close_send(fh);
+    pktin = sftp_wait_for_reply(req);
+    fxp_close_recv(pktin, req);
 
     return ret;
 }
@@ -503,7 +517,7 @@ int sftp_put_file(char *fname, char *outfname, int recurse, int restart)
     struct fxp_handle *fh;
     struct fxp_xfer *xfer;
     struct sftp_packet *pktin;
-    struct sftp_request *req, *rreq;
+    struct sftp_request *req;
     uint64 offset;
     RFile *file;
     int ret, err, eof;
@@ -526,17 +540,15 @@ int sftp_put_file(char *fname, char *outfname, int recurse, int restart)
         * 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);
+       req = fxp_stat_send(outfname);
+        pktin = sftp_wait_for_reply(req);
+       result = fxp_stat_recv(pktin, req, &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);
+           req = fxp_mkdir_send(outfname);
+            pktin = sftp_wait_for_reply(req);
+           result = fxp_mkdir_recv(pktin, req);
 
            if (!result) {
                printf("%s: create directory: %s\n",
@@ -571,7 +583,8 @@ int sftp_put_file(char *fname, char *outfname, int recurse, int restart)
         * same directory, just in case two readdirs on the same
         * local directory return a different order.
         */
-       qsort(ournames, nnames, sizeof(*ournames), bare_name_compare);
+        if (nnames > 0)
+            qsort(ournames, nnames, sizeof(*ournames), bare_name_compare);
 
        /*
         * If we're in restart mode, find the last filename on this
@@ -586,10 +599,9 @@ int sftp_put_file(char *fname, char *outfname, int recurse, int restart)
             while (i < nnames) {
                 char *nextoutfname;
                 nextoutfname = dupcat(outfname, "/", ournames[i], NULL);
-                sftp_register(req = fxp_stat_send(nextoutfname));
-                rreq = sftp_find_request(pktin = sftp_recv());
-                assert(rreq == req);
-                result = fxp_stat_recv(pktin, rreq, &attrs);
+                req = fxp_stat_send(nextoutfname);
+                pktin = sftp_wait_for_reply(req);
+                result = fxp_stat_recv(pktin, req, &attrs);
                 sfree(nextoutfname);
                 if (!result)
                     break;
@@ -609,10 +621,7 @@ int sftp_put_file(char *fname, char *outfname, int recurse, int restart)
            char *nextfname, *nextoutfname;
            int ret;
 
-           if (fname)
-               nextfname = dir_file_cat(fname, ournames[i]);
-           else
-               nextfname = dupstr(ournames[i]);
+            nextfname = dir_file_cat(fname, ournames[i]);
            nextoutfname = dupcat(outfname, "/", ournames[i], NULL);
            ret = sftp_put_file(nextfname, nextoutfname, recurse, restart);
            restart = FALSE;           /* after first partial file, do full */
@@ -646,15 +655,14 @@ int sftp_put_file(char *fname, char *outfname, int recurse, int restart)
     attrs.flags = 0;
     PUT_PERMISSIONS(attrs, permissions);
     if (restart) {
-       sftp_register(req = fxp_open_send(outfname, SSH_FXF_WRITE, &attrs));
+       req = fxp_open_send(outfname, SSH_FXF_WRITE, &attrs);
     } else {
-       sftp_register(req = fxp_open_send(outfname, SSH_FXF_WRITE |
-                                         SSH_FXF_CREAT | SSH_FXF_TRUNC,
-                                          &attrs));
+       req = fxp_open_send(outfname,
+                            SSH_FXF_WRITE | SSH_FXF_CREAT | SSH_FXF_TRUNC,
+                            &attrs);
     }
-    rreq = sftp_find_request(pktin = sftp_recv());
-    assert(rreq == req);
-    fh = fxp_open_recv(pktin, rreq);
+    pktin = sftp_wait_for_reply(req);
+    fh = fxp_open_recv(pktin, req);
 
     if (!fh) {
        close_rfile(file);
@@ -665,22 +673,19 @@ int sftp_put_file(char *fname, char *outfname, int recurse, int restart)
     if (restart) {
        char decbuf[30];
        struct fxp_attrs attrs;
-       int ret;
 
-       sftp_register(req = fxp_fstat_send(fh));
-       rreq = sftp_find_request(pktin = sftp_recv());
-       assert(rreq == req);
-       ret = fxp_fstat_recv(pktin, rreq, &attrs);
+       req = fxp_fstat_send(fh);
+        pktin = sftp_wait_for_reply(req);
+       ret = fxp_fstat_recv(pktin, req, &attrs);
 
        if (!ret) {
-           close_rfile(file);
            printf("read size of %s: %s\n", outfname, fxp_error());
-           return 0;
+            goto cleanup;
        }
        if (!(attrs.flags & SSH_FILEXFER_ATTR_SIZE)) {
-           close_rfile(file);
            printf("read size of %s: size was not given\n", outfname);
-           return 0;
+            ret = 0;
+            goto cleanup;
        }
        offset = attrs.size;
        uint64_decimal(offset, decbuf);
@@ -720,19 +725,23 @@ int sftp_put_file(char *fname, char *outfname, int recurse, int restart)
        if (!xfer_done(xfer)) {
            pktin = sftp_recv();
            ret = xfer_upload_gotpkt(xfer, pktin);
-           if (ret <= 0 && !err) {
-               printf("error while writing: %s\n", fxp_error());
-               err = 1;
+           if (ret <= 0) {
+                if (ret == INT_MIN)        /* pktin not even freed */
+                    sfree(pktin);
+                if (!err) {
+                    printf("error while writing: %s\n", fxp_error());
+                    err = 1;
+                }
            }
        }
     }
 
     xfer_cleanup(xfer);
 
-    sftp_register(req = fxp_close_send(fh));
-    rreq = sftp_find_request(pktin = sftp_recv());
-    assert(rreq == req);
-    fxp_close_recv(pktin, rreq);
+  cleanup:
+    req = fxp_close_send(fh);
+    pktin = sftp_wait_for_reply(req);
+    fxp_close_recv(pktin, req);
 
     close_rfile(file);
 
@@ -754,7 +763,7 @@ typedef struct SftpWildcardMatcher {
 SftpWildcardMatcher *sftp_begin_wildcard_matching(char *name)
 {
     struct sftp_packet *pktin;
-    struct sftp_request *req, *rreq;
+    struct sftp_request *req;
     char *wildcard;
     char *unwcdir, *tmpdir, *cdir;
     int len, check;
@@ -785,10 +794,9 @@ SftpWildcardMatcher *sftp_begin_wildcard_matching(char *name)
 
     cdir = canonify(unwcdir);
 
-    sftp_register(req = fxp_opendir_send(cdir));
-    rreq = sftp_find_request(pktin = sftp_recv());
-    assert(rreq == req);
-    dirh = fxp_opendir_recv(pktin, rreq);
+    req = fxp_opendir_send(cdir);
+    pktin = sftp_wait_for_reply(req);
+    dirh = fxp_opendir_recv(pktin, req);
 
     if (dirh) {
        swcm = snew(SftpWildcardMatcher);
@@ -811,7 +819,7 @@ char *sftp_wildcard_get_filename(SftpWildcardMatcher *swcm)
 {
     struct fxp_name *name;
     struct sftp_packet *pktin;
-    struct sftp_request *req, *rreq;
+    struct sftp_request *req;
 
     while (1) {
        if (swcm->names && swcm->namepos >= swcm->names->nnames) {
@@ -820,17 +828,26 @@ char *sftp_wildcard_get_filename(SftpWildcardMatcher *swcm)
        }
 
        if (!swcm->names) {
-           sftp_register(req = fxp_readdir_send(swcm->dirh));
-           rreq = sftp_find_request(pktin = sftp_recv());
-           assert(rreq == req);
-           swcm->names = fxp_readdir_recv(pktin, rreq);
+           req = fxp_readdir_send(swcm->dirh);
+            pktin = sftp_wait_for_reply(req);
+           swcm->names = fxp_readdir_recv(pktin, req);
 
            if (!swcm->names) {
                if (fxp_error_type() != SSH_FX_EOF)
                    printf("%s: reading directory: %s\n", swcm->prefix,
                           fxp_error());
                return NULL;
-           }
+           } else if (swcm->names->nnames == 0) {
+                /*
+                 * Another failure mode which we treat as EOF is if
+                 * the server reports success from FXP_READDIR but
+                 * returns no actual names. This is unusual, since
+                 * from most servers you'd expect at least "." and
+                 * "..", but there's nothing forbidding a server from
+                 * omitting those if it wants to.
+                 */
+                return NULL;
+            }
 
            swcm->namepos = 0;
        }
@@ -865,12 +882,11 @@ char *sftp_wildcard_get_filename(SftpWildcardMatcher *swcm)
 void sftp_finish_wildcard_matching(SftpWildcardMatcher *swcm)
 {
     struct sftp_packet *pktin;
-    struct sftp_request *req, *rreq;
+    struct sftp_request *req;
 
-    sftp_register(req = fxp_close_send(swcm->dirh));
-    rreq = sftp_find_request(pktin = sftp_recv());
-    assert(rreq == req);
-    fxp_close_recv(pktin, rreq);
+    req = fxp_close_send(swcm->dirh);
+    pktin = sftp_wait_for_reply(req);
+    fxp_close_recv(pktin, req);
 
     if (swcm->names)
        fxp_free_names(swcm->names);
@@ -910,6 +926,7 @@ int wildcard_iterate(char *filename, int (*func)(void *, char *), void *ctx)
                printf("%s: canonify: %s\n", newname, fxp_error());
                ret = 0;
            }
+            sfree(newname);
            matched = TRUE;
            ret &= func(ctx, cname);
            sfree(cname);
@@ -999,9 +1016,10 @@ int sftp_cmd_ls(struct sftp_command *cmd)
     struct fxp_names *names;
     struct fxp_name **ournames;
     int nnames, namesize;
-    char *dir, *cdir, *unwcdir, *wildcard;
+    const char *dir;
+    char *cdir, *unwcdir, *wildcard;
     struct sftp_packet *pktin;
-    struct sftp_request *req, *rreq;
+    struct sftp_request *req;
     int i;
 
     if (back == NULL) {
@@ -1022,6 +1040,7 @@ int sftp_cmd_ls(struct sftp_command *cmd)
        char *tmpdir;
        int len, check;
 
+        sfree(unwcdir);
        wildcard = stripslashes(dir, 0);
        unwcdir = dupstr(dir);
        len = wildcard - dir;
@@ -1048,10 +1067,9 @@ int sftp_cmd_ls(struct sftp_command *cmd)
 
     printf("Listing directory %s\n", cdir);
 
-    sftp_register(req = fxp_opendir_send(cdir));
-    rreq = sftp_find_request(pktin = sftp_recv());
-    assert(rreq == req);
-    dirh = fxp_opendir_recv(pktin, rreq);
+    req = fxp_opendir_send(cdir);
+    pktin = sftp_wait_for_reply(req);
+    dirh = fxp_opendir_recv(pktin, req);
 
     if (dirh == NULL) {
        printf("Unable to open %s: %s\n", dir, fxp_error());
@@ -1061,10 +1079,9 @@ int sftp_cmd_ls(struct sftp_command *cmd)
 
        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)
@@ -1088,16 +1105,16 @@ int sftp_cmd_ls(struct sftp_command *cmd)
 
            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_name_compare);
+        if (nnames > 0)
+            qsort(ournames, nnames, sizeof(*ournames), sftp_name_compare);
 
        /*
         * And print them.
@@ -1123,7 +1140,7 @@ int sftp_cmd_cd(struct sftp_command *cmd)
 {
     struct fxp_handle *dirh;
     struct sftp_packet *pktin;
-    struct sftp_request *req, *rreq;
+    struct sftp_request *req;
     char *dir;
 
     if (back == NULL) {
@@ -1141,10 +1158,9 @@ int sftp_cmd_cd(struct sftp_command *cmd)
        return 0;
     }
 
-    sftp_register(req = fxp_opendir_send(dir));
-    rreq = sftp_find_request(pktin = sftp_recv());
-    assert(rreq == req);
-    dirh = fxp_opendir_recv(pktin, rreq);
+    req = fxp_opendir_send(dir);
+    pktin = sftp_wait_for_reply(req);
+    dirh = fxp_opendir_recv(pktin, req);
 
     if (!dirh) {
        printf("Directory %s: %s\n", dir, fxp_error());
@@ -1152,10 +1168,9 @@ int sftp_cmd_cd(struct sftp_command *cmd)
        return 0;
     }
 
-    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);
 
     sfree(pwd);
     pwd = dir;
@@ -1248,7 +1263,9 @@ int sftp_general_get(struct sftp_command *cmd, int restart, int multiple)
            fname = canonify(origwfname);
 
            if (!fname) {
+                sftp_finish_wildcard_matching(swcm);
                printf("%s: canonify: %s\n", origwfname, fxp_error());
+               sfree(origwfname);
                sfree(unwcfname);
                return 0;
            }
@@ -1404,7 +1421,7 @@ int sftp_cmd_mkdir(struct sftp_command *cmd)
 {
     char *dir;
     struct sftp_packet *pktin;
-    struct sftp_request *req, *rreq;
+    struct sftp_request *req;
     int result;
     int i, ret;
 
@@ -1426,10 +1443,9 @@ int sftp_cmd_mkdir(struct sftp_command *cmd)
            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);
+       req = fxp_mkdir_send(dir);
+        pktin = sftp_wait_for_reply(req);
+       result = fxp_mkdir_recv(pktin, req);
 
        if (!result) {
            printf("mkdir %s: %s\n", dir, fxp_error());
@@ -1446,13 +1462,12 @@ int sftp_cmd_mkdir(struct sftp_command *cmd)
 static int sftp_action_rmdir(void *vctx, char *dir)
 {
     struct sftp_packet *pktin;
-    struct sftp_request *req, *rreq;
+    struct sftp_request *req;
     int result;
 
-    sftp_register(req = fxp_rmdir_send(dir));
-    rreq = sftp_find_request(pktin = sftp_recv());
-    assert(rreq == req);
-    result = fxp_rmdir_recv(pktin, rreq);
+    req = fxp_rmdir_send(dir);
+    pktin = sftp_wait_for_reply(req);
+    result = fxp_rmdir_recv(pktin, req);
 
     if (!result) {
        printf("rmdir %s: %s\n", dir, fxp_error());
@@ -1488,13 +1503,12 @@ int sftp_cmd_rmdir(struct sftp_command *cmd)
 static int sftp_action_rm(void *vctx, char *fname)
 {
     struct sftp_packet *pktin;
-    struct sftp_request *req, *rreq;
+    struct sftp_request *req;
     int result;
 
-    sftp_register(req = fxp_remove_send(fname));
-    rreq = sftp_find_request(pktin = sftp_recv());
-    assert(rreq == req);
-    result = fxp_remove_recv(pktin, rreq);
+    req = fxp_remove_send(fname);
+    pktin = sftp_wait_for_reply(req);
+    result = fxp_remove_recv(pktin, req);
 
     if (!result) {
        printf("rm %s: %s\n", fname, fxp_error());
@@ -1530,14 +1544,13 @@ int sftp_cmd_rm(struct sftp_command *cmd)
 static int check_is_dir(char *dstfname)
 {
     struct sftp_packet *pktin;
-    struct sftp_request *req, *rreq;
+    struct sftp_request *req;
     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);
+    req = fxp_stat_send(dstfname);
+    pktin = sftp_wait_for_reply(req);
+    result = fxp_stat_recv(pktin, req, &attrs);
 
     if (result &&
        (attrs.flags & SSH_FILEXFER_ATTR_PERMISSIONS) &&
@@ -1556,7 +1569,7 @@ 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;
+    struct sftp_request *req;
     const char *error;
     char *finalfname, *newcanon = NULL;
     int ret, result;
@@ -1581,10 +1594,9 @@ static int sftp_action_mv(void *vctx, char *srcfname)
        finalfname = ctx->dstfname;
     }
 
-    sftp_register(req = fxp_rename_send(srcfname, finalfname));
-    rreq = sftp_find_request(pktin = sftp_recv());
-    assert(rreq == req);
-    result = fxp_rename_recv(pktin, rreq);
+    req = fxp_rename_send(srcfname, finalfname);
+    pktin = sftp_wait_for_reply(req);
+    result = fxp_rename_recv(pktin, req);
 
     error = result ? NULL : fxp_error();
 
@@ -1653,15 +1665,14 @@ static int sftp_action_chmod(void *vctx, char *fname)
 {
     struct fxp_attrs attrs;
     struct sftp_packet *pktin;
-    struct sftp_request *req, *rreq;
+    struct sftp_request *req;
     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_stat_recv(pktin, rreq, &attrs);
+    req = fxp_stat_send(fname);
+    pktin = sftp_wait_for_reply(req);
+    result = fxp_stat_recv(pktin, req, &attrs);
 
     if (!result || !(attrs.flags & SSH_FILEXFER_ATTR_PERMISSIONS)) {
        printf("get attrs for %s: %s\n", fname,
@@ -1678,10 +1689,9 @@ static int sftp_action_chmod(void *vctx, char *fname)
     if (oldperms == newperms)
        return 1;                      /* no need to do anything! */
 
-    sftp_register(req = fxp_setstat_send(fname, attrs));
-    rreq = sftp_find_request(pktin = sftp_recv());
-    assert(rreq == req);
-    result = fxp_setstat_recv(pktin, rreq);
+    req = fxp_setstat_send(fname, attrs);
+    pktin = sftp_wait_for_reply(req);
+    result = fxp_setstat_recv(pktin, req);
 
     if (!result) {
        printf("set attrs for %s: %s\n", fname, fxp_error());
@@ -1897,7 +1907,7 @@ static int sftp_cmd_pling(struct sftp_command *cmd)
 static int sftp_cmd_help(struct sftp_command *cmd);
 
 static struct sftp_cmd_lookup {
-    char *name;
+    const char *name;
     /*
      * For help purposes, there are two kinds of command:
      * 
@@ -1911,8 +1921,8 @@ static struct sftp_cmd_lookup {
      *    contains the help that should double up for this command.
      */
     int listed;                               /* do we list this in primary help? */
-    char *shorthelp;
-    char *longhelp;
+    const char *shorthelp;
+    const char *longhelp;
     int (*obey) (struct sftp_command *);
 } sftp_lookup[] = {
     /*
@@ -2135,7 +2145,7 @@ static struct sftp_cmd_lookup {
     }
 };
 
-const struct sftp_cmd_lookup *lookup_command(char *name)
+const struct sftp_cmd_lookup *lookup_command(const char *name)
 {
     int i, j, k, cmp;
 
@@ -2231,6 +2241,7 @@ struct sftp_command *sftp_getcmd(FILE *fp, int mode, int modeflags)
        cmd->obey = sftp_cmd_quit;
        if ((mode == 0) || (modeflags & 1))
            printf("quit\n");
+        sfree(line);
        return cmd;                    /* eof */
     }
 
@@ -2277,10 +2288,13 @@ struct sftp_command *sftp_getcmd(FILE *fp, int mode, int modeflags)
         *      >this has "quotes" in<
         *      >and"this"<
         */
-       while (*p) {
+       while (1) {
            /* skip whitespace */
            while (*p && (*p == ' ' || *p == '\t'))
                p++;
+            /* terminate loop */
+            if (!*p)
+                break;
            /* mark start of word */
            q = r = p;                 /* q sits at start, r writes word */
            quoting = 0;
@@ -2328,7 +2342,7 @@ struct sftp_command *sftp_getcmd(FILE *fp, int mode, int modeflags)
 static int do_sftp_init(void)
 {
     struct sftp_packet *pktin;
-    struct sftp_request *req, *rreq;
+    struct sftp_request *req;
 
     /*
      * Do protocol initialisation. 
@@ -2342,10 +2356,9 @@ static int do_sftp_init(void)
     /*
      * Find out where our home directory is.
      */
-    sftp_register(req = fxp_realpath_send("."));
-    rreq = sftp_find_request(pktin = sftp_recv());
-    assert(rreq == req);
-    homedir = fxp_realpath_recv(pktin, rreq);
+    req = fxp_realpath_send(".");
+    pktin = sftp_wait_for_reply(req);
+    homedir = fxp_realpath_recv(pktin, req);
 
     if (!homedir) {
        fprintf(stderr,
@@ -2381,7 +2394,7 @@ void do_sftp_cleanup()
     }
 }
 
-void do_sftp(int mode, int modeflags, char *batchfile)
+int do_sftp(int mode, int modeflags, char *batchfile)
 {
     FILE *fp;
     int ret;
@@ -2414,8 +2427,9 @@ void do_sftp(int mode, int modeflags, char *batchfile)
         fp = fopen(batchfile, "r");
         if (!fp) {
            printf("Fatal: unable to open %s\n", batchfile);
-           return;
+           return 1;
         }
+       ret = 0;
         while (1) {
            struct sftp_command *cmd;
            cmd = sftp_getcmd(fp, mode, modeflags);
@@ -2430,8 +2444,13 @@ void do_sftp(int mode, int modeflags, char *batchfile)
            }
         }
        fclose(fp);
-
+       /*
+        * In batch mode, and if exit on command failure is enabled,
+        * any command failure causes the whole of PSFTP to fail.
+        */
+       if (ret == 0 && !(modeflags & 2)) return 2;
     }
+    return 0;
 }
 
 /* ----------------------------------------------------------------------
@@ -2443,7 +2462,7 @@ static int verbose = 0;
 /*
  *  Print an error message and perform a fatal exit.
  */
-void fatalbox(char *fmt, ...)
+void fatalbox(const char *fmt, ...)
 {
     char *str, *str2;
     va_list ap;
@@ -2457,7 +2476,7 @@ void fatalbox(char *fmt, ...)
 
     cleanup_exit(1);
 }
-void modalfatalbox(char *fmt, ...)
+void modalfatalbox(const char *fmt, ...)
 {
     char *str, *str2;
     va_list ap;
@@ -2471,7 +2490,19 @@ 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);
+    fputs(str2, stderr);
+    sfree(str2);
+}
+void connection_fatal(void *frontend, const char *fmt, ...)
 {
     char *str, *str2;
     va_list ap;
@@ -2486,16 +2517,7 @@ void connection_fatal(void *frontend, char *fmt, ...)
     cleanup_exit(1);
 }
 
-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 PSFTP 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) { }
 
 /*
  * In psftp, all agent requests should be synchronous, so this is a
@@ -2648,9 +2670,11 @@ 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");
     cleanup_exit(1);
 }
@@ -2702,6 +2726,7 @@ static int psftp_connect(char *userhost, char *user, int portnumber)
            /* 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);
@@ -2853,7 +2878,7 @@ static int psftp_connect(char *userhost, char *user, int portnumber)
     return 0;
 }
 
-void cmdline_error(char *p, ...)
+void cmdline_error(const char *p, ...)
 {
     va_list ap;
     fprintf(stderr, "psftp: ");
@@ -2864,12 +2889,15 @@ void cmdline_error(char *p, ...)
     exit(1);
 }
 
+const int share_can_be_downstream = TRUE;
+const int share_can_be_upstream = FALSE;
+
 /*
  * Main program. Parse arguments etc.
  */
 int psftp_main(int argc, char *argv[])
 {
-    int i;
+    int i, ret;
     int portnumber = 0;
     char *userhost, *user;
     int mode = 0;
@@ -2910,12 +2938,14 @@ int psftp_main(int argc, char *argv[])
            if (flags & FLAG_VERBOSE)
                verbose = 1;
        } else if (strcmp(argv[i], "-h") == 0 ||
-                  strcmp(argv[i], "-?") == 0) {
+                  strcmp(argv[i], "-?") == 0 ||
+                   strcmp(argv[i], "--help") == 0) {
            usage();
         } else if (strcmp(argv[i], "-pgpfp") == 0) {
             pgp_fingerprints();
             return 1;
-       } 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], "-batch") == 0) {
            console_batch_mode = 1;
@@ -2963,7 +2993,7 @@ int psftp_main(int argc, char *argv[])
               " to connect\n");
     }
 
-    do_sftp(mode, modeflags, batchfile);
+    ret = do_sftp(mode, modeflags, batchfile);
 
     if (back != NULL && back->connected(backhandle)) {
        char ch;
@@ -2977,5 +3007,5 @@ int psftp_main(int argc, char *argv[])
     console_provide_logctx(NULL);
     sk_cleanup();
 
-    return 0;
+    return ret;
 }