Update docs for change to UTF-8 by default, and emphasise UTF-8 more generally.
[sgt/putty] / psftp.c
diff --git a/psftp.c b/psftp.c
index 53b6616..ab287df 100644 (file)
--- a/psftp.c
+++ b/psftp.c
@@ -16,6 +16,8 @@
 #include "sftp.h"
 #include "int64.h"
 
+const char *const appname = "PSFTP";
+
 /*
  * Since SFTP is a request-response oriented protocol, it requires
  * no buffer management: when we send data, we stop and wait for an
@@ -34,7 +36,8 @@ void do_sftp_cleanup();
 char *pwd, *homedir;
 static Backend *back;
 static void *backhandle;
-static Config cfg;
+static Conf *conf;
+int sent_eof = FALSE;
 
 /* ----------------------------------------------------------------------
  * Higher-level helper functions used in commands.
@@ -128,8 +131,12 @@ char *canonify(char *name)
        assert(rreq == req);
        canonname = fxp_realpath_recv(pktin, rreq);
 
-       if (!canonname)
-           return fullname;           /* even that failed; give up */
+       if (!canonname) {
+           /* Even that failed. Restore our best guess at the
+            * constructed filename and give up */
+           fullname[i] = '/';  /* restore slash and last component */
+           return fullname;
+       }
 
        /*
         * We have a canonical name for all but the last path
@@ -204,8 +211,9 @@ int sftp_get_file(char *fname, char *outfname, int recurse, int restart)
     struct sftp_request *req, *rreq;
     struct fxp_xfer *xfer;
     uint64 offset;
-    FILE *fp;
+    WFile *file;
     int ret, shown_err = FALSE;
+    struct fxp_attrs attrs;
 
     /*
      * In recursive mode, see if we're dealing with a directory.
@@ -213,7 +221,6 @@ int sftp_get_file(char *fname, char *outfname, int recurse, int restart)
      * subsequent FXP_OPEN will return a usable error message.)
      */
     if (recurse) {
-       struct fxp_attrs attrs;
        int result;
 
        sftp_register(req = fxp_stat_send(fname));
@@ -317,22 +324,24 @@ int sftp_get_file(char *fname, char *outfname, int recurse, int restart)
             * If none of them exists, of course, we start at 0.
             */
            i = 0;
-           while (i < nnames) {
-               char *nextoutfname;
-               int ret;
-               if (outfname)
-                   nextoutfname = dir_file_cat(outfname,
-                                               ournames[i]->filename);
-               else
-                   nextoutfname = dupstr(ournames[i]->filename);
-               ret = (file_type(nextoutfname) == FILE_TYPE_NONEXISTENT);
-               sfree(nextoutfname);
-               if (ret)
-                   break;
-               i++;
-           }
-           if (i > 0)
-               i--;
+            if (restart) {
+                while (i < nnames) {
+                    char *nextoutfname;
+                    int ret;
+                    if (outfname)
+                        nextoutfname = dir_file_cat(outfname,
+                                                    ournames[i]->filename);
+                    else
+                        nextoutfname = dupstr(ournames[i]->filename);
+                    ret = (file_type(nextoutfname) == FILE_TYPE_NONEXISTENT);
+                    sfree(nextoutfname);
+                    if (ret)
+                        break;
+                    i++;
+                }
+                if (i > 0)
+                    i--;
+            }
 
            /*
             * Now we're ready to recurse. Starting at ournames[i]
@@ -375,23 +384,29 @@ int sftp_get_file(char *fname, char *outfname, int recurse, int restart)
        }
     }
 
-    sftp_register(req = fxp_open_send(fname, SSH_FXF_READ));
+    sftp_register(req = fxp_stat_send(fname));
+    rreq = sftp_find_request(pktin = sftp_recv());
+    assert(rreq == req);
+    if (!fxp_stat_recv(pktin, rreq, &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);
 
     if (!fh) {
-       printf("%s: %s\n", fname, fxp_error());
+       printf("%s: open for read: %s\n", fname, fxp_error());
        return 0;
     }
 
     if (restart) {
-       fp = fopen(outfname, "rb+");
+       file = open_existing_wfile(outfname, NULL);
     } else {
-       fp = fopen(outfname, "wb");
+       file = open_new_file(outfname, GET_PERMISSIONS(attrs));
     }
 
-    if (!fp) {
+    if (!file) {
        printf("local: unable to open %s\n", outfname);
 
        sftp_register(req = fxp_close_send(fh));
@@ -403,11 +418,22 @@ int sftp_get_file(char *fname, char *outfname, int recurse, int restart)
     }
 
     if (restart) {
-       long posn;
-       fseek(fp, 0L, SEEK_END);
-       posn = ftell(fp);
-       printf("reget: restarting at file position %ld\n", posn);
-       offset = uint64_make(0, posn);
+       char decbuf[30];
+       if (seek_file(file, uint64_make(0,0) , FROM_END) == -1) {
+           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);
+               
+           return 0;
+       }
+           
+       offset = get_file_posn(file);
+       uint64_decimal(offset, decbuf);
+       printf("reget: restarting at file position %s\n", decbuf);
     } else {
        offset = uint64_make(0, 0);
     }
@@ -442,11 +468,12 @@ int sftp_get_file(char *fname, char *outfname, int recurse, int restart)
 
            wpos = 0;
            while (wpos < len) {
-               wlen = fwrite(buf + wpos, 1, len - wpos, fp);
+               wlen = write_to_file(file, buf + wpos, len - wpos);
                if (wlen <= 0) {
                    printf("error while writing local file\n");
                    ret = 0;
                    xfer_set_error(xfer);
+                   break;
                }
                wpos += wlen;
            }
@@ -461,7 +488,7 @@ int sftp_get_file(char *fname, char *outfname, int recurse, int restart)
 
     xfer_cleanup(xfer);
 
-    fclose(fp);
+    close_wfile(file);
 
     sftp_register(req = fxp_close_send(fh));
     rreq = sftp_find_request(pktin = sftp_recv());
@@ -478,8 +505,10 @@ int sftp_put_file(char *fname, char *outfname, int recurse, int restart)
     struct sftp_packet *pktin;
     struct sftp_request *req, *rreq;
     uint64 offset;
-    FILE *fp;
+    RFile *file;
     int ret, err, eof;
+    struct fxp_attrs attrs;
+    long permissions;
 
     /*
      * In recursive mode, see if we're dealing with a directory.
@@ -487,7 +516,6 @@ int sftp_put_file(char *fname, char *outfname, int recurse, int restart)
      * subsequent fopen will return an error message.)
      */
     if (recurse && file_type(fname) == FILE_TYPE_DIRECTORY) {
-       struct fxp_attrs attrs;
        int result;
        int nnames, namesize;
        char *name, **ournames;
@@ -554,23 +582,25 @@ int sftp_put_file(char *fname, char *outfname, int recurse, int restart)
         * If none of them exists, of course, we start at 0.
         */
        i = 0;
-       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);
-           sfree(nextoutfname);
-           if (!result)
-               break;
-           i++;
-       }
-       if (i > 0)
-           i--;
+        if (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);
+                sfree(nextoutfname);
+                if (!result)
+                    break;
+                i++;
+            }
+            if (i > 0)
+                i--;
+        }
 
-       /*
-        * Now we're ready to recurse. Starting at ournames[i]
+        /*
+         * Now we're ready to recurse. Starting at ournames[i]
         * and continuing on to the end of the list, we
         * construct a new source and target file name, and
         * call sftp_put_file again.
@@ -608,23 +638,27 @@ int sftp_put_file(char *fname, char *outfname, int recurse, int restart)
        return 1;
     }
 
-    fp = fopen(fname, "rb");
-    if (!fp) {
+    file = open_existing_file(fname, NULL, NULL, NULL, &permissions);
+    if (!file) {
        printf("local: unable to open %s\n", fname);
        return 0;
     }
+    attrs.flags = 0;
+    PUT_PERMISSIONS(attrs, permissions);
     if (restart) {
-       sftp_register(req = fxp_open_send(outfname, SSH_FXF_WRITE));
+       sftp_register(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));
+                                         SSH_FXF_CREAT | SSH_FXF_TRUNC,
+                                          &attrs));
     }
     rreq = sftp_find_request(pktin = sftp_recv());
     assert(rreq == req);
     fh = fxp_open_recv(pktin, rreq);
 
     if (!fh) {
-       printf("%s: %s\n", outfname, fxp_error());
+       close_rfile(file);
+       printf("%s: open for write: %s\n", outfname, fxp_error());
        return 0;
     }
 
@@ -639,22 +673,21 @@ int sftp_put_file(char *fname, char *outfname, int recurse, int restart)
        ret = fxp_fstat_recv(pktin, rreq, &attrs);
 
        if (!ret) {
+           close_rfile(file);
            printf("read size of %s: %s\n", outfname, fxp_error());
            return 0;
        }
        if (!(attrs.flags & SSH_FILEXFER_ATTR_SIZE)) {
+           close_rfile(file);
            printf("read size of %s: size was not given\n", outfname);
            return 0;
        }
        offset = attrs.size;
        uint64_decimal(offset, decbuf);
        printf("reput: restarting at file position %s\n", decbuf);
-       if (uint64_compare(offset, uint64_make(0, LONG_MAX)) > 0) {
-           printf("reput: remote file is larger than we can deal with\n");
-           return 0;
-       }
-       if (fseek(fp, offset.lo, SEEK_SET) != 0)
-           fseek(fp, 0, SEEK_END);    /* *shrug* */
+
+       if (seek_file((WFile *)file, offset, FROM_START) != 0)
+           seek_file((WFile *)file, uint64_make(0,0), FROM_END);    /* *shrug* */
     } else {
        offset = uint64_make(0, 0);
     }
@@ -673,7 +706,7 @@ int sftp_put_file(char *fname, char *outfname, int recurse, int restart)
        int len, ret;
 
        while (xfer_upload_ready(xfer) && !err && !eof) {
-           len = fread(buffer, 1, sizeof(buffer), fp);
+           len = read_from_file(file, buffer, sizeof(buffer));
            if (len == -1) {
                printf("error while reading local file\n");
                err = 1;
@@ -687,7 +720,7 @@ 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) {
+           if (ret <= 0 && !err) {
                printf("error while writing: %s\n", fxp_error());
                err = 1;
            }
@@ -701,7 +734,7 @@ int sftp_put_file(char *fname, char *outfname, int recurse, int restart)
     assert(rreq == req);
     fxp_close_recv(pktin, rreq);
 
-    fclose(fp);
+    close_rfile(file);
 
     return ret;
 }
@@ -797,7 +830,17 @@ char *sftp_wildcard_get_filename(SftpWildcardMatcher *swcm)
                    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;
        }
@@ -874,7 +917,7 @@ int wildcard_iterate(char *filename, int (*func)(void *, char *), void *ctx)
        while ( (newname = sftp_wildcard_get_filename(swcm)) != NULL ) {
            cname = canonify(newname);
            if (!cname) {
-               printf("%s: %s\n", newname, fxp_error());
+               printf("%s: canonify: %s\n", newname, fxp_error());
                ret = 0;
            }
            matched = TRUE;
@@ -891,7 +934,7 @@ int wildcard_iterate(char *filename, int (*func)(void *, char *), void *ctx)
     } else {
        cname = canonify(unwcfname);
        if (!cname) {
-           printf("%s: %s\n", filename, fxp_error());
+           printf("%s: canonify: %s\n", filename, fxp_error());
            ret = 0;
        }
        ret = func(ctx, cname);
@@ -945,9 +988,10 @@ int sftp_cmd_close(struct sftp_command *cmd)
        return 0;
     }
 
-    if (back != NULL && back->socket(backhandle) != NULL) {
+    if (back != NULL && back->connected(backhandle)) {
        char ch;
        back->special(backhandle, TS_EOF);
+        sent_eof = TRUE;
        sftp_recvdata(&ch, 1);
     }
     do_sftp_cleanup();
@@ -1007,7 +1051,7 @@ int sftp_cmd_ls(struct sftp_command *cmd)
 
     cdir = canonify(dir);
     if (!cdir) {
-       printf("%s: %s\n", dir, fxp_error());
+       printf("%s: canonify: %s\n", dir, fxp_error());
        sfree(unwcdir);
        return 0;
     }
@@ -1103,7 +1147,7 @@ int sftp_cmd_cd(struct sftp_command *cmd)
        dir = canonify(cmd->words[1]);
 
     if (!dir) {
-       printf("%s: %s\n", dir, fxp_error());
+       printf("%s: canonify: %s\n", dir, fxp_error());
        return 0;
     }
 
@@ -1214,7 +1258,7 @@ int sftp_general_get(struct sftp_command *cmd, int restart, int multiple)
            fname = canonify(origwfname);
 
            if (!fname) {
-               printf("%s: %s\n", origwfname, fxp_error());
+               printf("%s: canonify: %s\n", origwfname, fxp_error());
                sfree(unwcfname);
                return 0;
            }
@@ -1325,7 +1369,7 @@ int sftp_general_put(struct sftp_command *cmd, int restart, int multiple)
 
            outfname = canonify(origoutfname);
            if (!outfname) {
-               printf("%s: %s\n", origoutfname, fxp_error());
+               printf("%s: canonify: %s\n", origoutfname, fxp_error());
                if (wcm) {
                    sfree(wfname);
                    finish_wildcard_matching(wcm);
@@ -1388,7 +1432,7 @@ int sftp_cmd_mkdir(struct sftp_command *cmd)
     for (i = 1; i < cmd->nwords; i++) {
        dir = canonify(cmd->words[i]);
        if (!dir) {
-           printf("%s: %s\n", dir, fxp_error());
+           printf("%s: canonify: %s\n", dir, fxp_error());
            return 0;
        }
 
@@ -1536,7 +1580,7 @@ static int sftp_action_mv(void *vctx, char *srcfname)
        newname = dupcat(ctx->dstfname, "/", p, NULL);
        newcanon = canonify(newname);
        if (!newcanon) {
-           printf("%s: %s\n", newname, fxp_error());
+           printf("%s: canonify: %s\n", newname, fxp_error());
            sfree(newname);
            return 0;
        }
@@ -1583,7 +1627,7 @@ int sftp_cmd_mv(struct sftp_command *cmd)
 
     ctx->dstfname = canonify(cmd->words[cmd->nwords-1]);
     if (!ctx->dstfname) {
-       printf("%s: %s\n", ctx->dstfname, fxp_error());
+       printf("%s: canonify: %s\n", ctx->dstfname, fxp_error());
        return 0;
     }
 
@@ -2220,6 +2264,11 @@ struct sftp_command *sftp_getcmd(FILE *fp, int mode, int modeflags)
        cmd->words = sresize(cmd->words, cmd->wordssize, char *);
        cmd->words[0] = dupstr("!");
        cmd->words[1] = dupstr(p+1);
+    } else if (*p == '#') {
+       /*
+        * Special case: comment. Entire line is ignored.
+        */
+       cmd->nwords = cmd->wordssize = 0;
     } else {
 
        /*
@@ -2238,10 +2287,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;
@@ -2325,6 +2377,7 @@ void do_sftp_cleanup()
     char ch;
     if (back) {
        back->special(backhandle, TS_EOF);
+        sent_eof = TRUE;
        sftp_recvdata(&ch, 1);
        back->free(backhandle);
        sftp_cleanup_request();
@@ -2491,7 +2544,8 @@ int from_backend(void *frontend, int is_stderr, const char *data, int datalen)
      */
     if (is_stderr) {
        if (len > 0)
-           fwrite(data, 1, len, stderr);
+           if (fwrite(data, 1, len, stderr) < len)
+               /* oh well */;
        return 0;
     }
 
@@ -2523,6 +2577,28 @@ int from_backend(void *frontend, int is_stderr, const char *data, int datalen)
 
     return 0;
 }
+int from_backend_untrusted(void *frontend_handle, const char *data, int len)
+{
+    /*
+     * No "untrusted" output should get here (the way the code is
+     * currently, it's all diverted by FLAG_STDERR).
+     */
+    assert(!"Unexpected call to from_backend_untrusted()");
+    return 0; /* not reached */
+}
+int from_backend_eof(void *frontend)
+{
+    /*
+     * We expect to be the party deciding when to close the
+     * connection, so if we see EOF before we sent it ourselves, we
+     * should panic.
+     */
+    if (!sent_eof) {
+        connection_fatal(frontend,
+                         "Received unexpected end-of-file from SFTP server");
+    }
+    return FALSE;
+}
 int sftp_recvdata(char *buf, int len)
 {
     outptr = (unsigned char *) buf;
@@ -2551,7 +2627,7 @@ int sftp_recvdata(char *buf, int len)
     }
 
     while (outlen > 0) {
-       if (ssh_sftp_loop_iteration() < 0)
+       if (back->exitcode(backhandle) >= 0 || ssh_sftp_loop_iteration() < 0)
            return 0;                  /* doom */
     }
 
@@ -2586,6 +2662,8 @@ static void usage(void)
     printf("  -4 -6     force use of IPv4 or IPv6\n");
     printf("  -C        enable compression\n");
     printf("  -i key    private key file for authentication\n");
+    printf("  -noagent  disable use of Pageant\n");
+    printf("  -agent    enable use of Pageant\n");
     printf("  -batch    disable all interactive prompts\n");
     cleanup_exit(1);
 }
@@ -2625,32 +2703,30 @@ static int psftp_connect(char *userhost, char *user, int portnumber)
      */
     if (!loaded_session) {
        /* Try to load settings for `host' into a temporary config */
-       Config cfg2;
-       cfg2.host[0] = '\0';
-       do_defaults(host, &cfg2);
-       if (cfg2.host[0] != '\0') {
+       Conf *conf2 = conf_new();
+       conf_set_str(conf2, CONF_host, "");
+       do_defaults(host, conf2);
+       if (conf_get_str(conf2, CONF_host)[0] != '\0') {
            /* Settings present and include hostname */
            /* Re-load data into the real config. */
-           do_defaults(host, &cfg);
+           do_defaults(host, conf);
        } else {
            /* Session doesn't exist or mention a hostname. */
            /* Use `host' as a bare hostname. */
-           strncpy(cfg.host, host, sizeof(cfg.host) - 1);
-           cfg.host[sizeof(cfg.host) - 1] = '\0';
+           conf_set_str(conf, CONF_host, host);
        }
     } else {
        /* Patch in hostname `host' to session details. */
-       strncpy(cfg.host, host, sizeof(cfg.host) - 1);
-       cfg.host[sizeof(cfg.host) - 1] = '\0';
+       conf_set_str(conf, CONF_host, host);
     }
 
     /*
      * Force use of SSH. (If they got the protocol wrong we assume the
      * port is useless too.)
      */
-    if (cfg.protocol != PROT_SSH) {
-        cfg.protocol = PROT_SSH;
-        cfg.port = 22;
+    if (conf_get_int(conf, CONF_protocol) != PROT_SSH) {
+        conf_set_int(conf, CONF_protocol, PROT_SSH);
+        conf_set_int(conf, CONF_port, 22);
     }
 
     /*
@@ -2659,88 +2735,82 @@ static int psftp_connect(char *userhost, char *user, int portnumber)
      * work for SFTP. (Can be overridden with `-1' option.)
      * But if it says `2 only' or `2', respect which.
      */
-    if (cfg.sshprot != 2 && cfg.sshprot != 3)
-       cfg.sshprot = 2;
+    if ((conf_get_int(conf, CONF_sshprot) & ~1) != 2)   /* is it 2 or 3? */
+       conf_set_int(conf, CONF_sshprot, 2);
 
     /*
      * Enact command-line overrides.
      */
-    cmdline_run_saved(&cfg);
+    cmdline_run_saved(conf);
 
     /*
-     * Trim leading whitespace off the hostname if it's there.
+     * Muck about with the hostname in various ways.
      */
     {
-       int space = strspn(cfg.host, " \t");
-       memmove(cfg.host, cfg.host+space, 1+strlen(cfg.host)-space);
-    }
-
-    /* See if host is of the form user@host */
-    if (cfg.host[0] != '\0') {
-       char *atsign = strrchr(cfg.host, '@');
-       /* Make sure we're not overflowing the user field */
-       if (atsign) {
-           if (atsign - cfg.host < sizeof cfg.username) {
-               strncpy(cfg.username, cfg.host, atsign - cfg.host);
-               cfg.username[atsign - cfg.host] = '\0';
-           }
-           memmove(cfg.host, atsign + 1, 1 + strlen(atsign + 1));
-       }
-    }
+       char *hostbuf = dupstr(conf_get_str(conf, CONF_host));
+       char *host = hostbuf;
+       char *p, *q;
 
-    /*
-     * Trim a colon suffix off the hostname if it's there.
-     */
-    cfg.host[strcspn(cfg.host, ":")] = '\0';
+       /*
+        * Trim leading whitespace.
+        */
+       host += strspn(host, " \t");
 
-    /*
-     * Remove any remaining whitespace from the hostname.
-     */
-    {
-       int p1 = 0, p2 = 0;
-       while (cfg.host[p2] != '\0') {
-           if (cfg.host[p2] != ' ' && cfg.host[p2] != '\t') {
-               cfg.host[p1] = cfg.host[p2];
-               p1++;
+       /*
+        * See if host is of the form user@host, and separate out
+        * the username if so.
+        */
+       if (host[0] != '\0') {
+           char *atsign = strrchr(host, '@');
+           if (atsign) {
+               *atsign = '\0';
+               conf_set_str(conf, CONF_username, host);
+               host = atsign + 1;
            }
-           p2++;
        }
-       cfg.host[p1] = '\0';
+
+       /*
+        * Remove any remaining whitespace.
+        */
+       p = hostbuf;
+       q = host;
+       while (*q) {
+           if (*q != ' ' && *q != '\t')
+               *p++ = *q;
+           q++;
+       }
+       *p = '\0';
+
+       conf_set_str(conf, CONF_host, hostbuf);
+       sfree(hostbuf);
     }
 
     /* Set username */
     if (user != NULL && user[0] != '\0') {
-       strncpy(cfg.username, user, sizeof(cfg.username) - 1);
-       cfg.username[sizeof(cfg.username) - 1] = '\0';
-    }
-    if (!cfg.username[0]) {
-       if (!console_get_line("login as: ",
-                             cfg.username, sizeof(cfg.username), FALSE)) {
-           fprintf(stderr, "psftp: no username, aborting\n");
-           cleanup_exit(1);
-       } else {
-           int len = strlen(cfg.username);
-           if (cfg.username[len - 1] == '\n')
-               cfg.username[len - 1] = '\0';
-       }
+       conf_set_str(conf, CONF_username, user);
     }
 
     if (portnumber)
-       cfg.port = portnumber;
+       conf_set_int(conf, CONF_port, portnumber);
 
     /*
      * Disable scary things which shouldn't be enabled for simple
      * things like SCP and SFTP: agent forwarding, port forwarding,
      * X forwarding.
      */
-    cfg.x11_forward = 0;
-    cfg.agentfwd = 0;
-    cfg.portfwd[0] = cfg.portfwd[1] = '\0';
+    conf_set_int(conf, CONF_x11_forward, 0);
+    conf_set_int(conf, CONF_agentfwd, 0);
+    conf_set_int(conf, CONF_ssh_simple, TRUE);
+    {
+       char *key;
+       while ((key = conf_get_str_nthstrkey(conf, CONF_portfwd, 0)) != NULL)
+           conf_del_str_str(conf, CONF_portfwd, key);
+    }
 
     /* Set up subsystem name. */
-    strcpy(cfg.remote_cmd, "sftp");
-    cfg.ssh_subsys = TRUE;
-    cfg.nopty = TRUE;
+    conf_set_str(conf, CONF_remote_cmd, "sftp");
+    conf_set_int(conf, CONF_ssh_subsys, TRUE);
+    conf_set_int(conf, CONF_nopty, TRUE);
 
     /*
      * Set up fallback option, for SSH-1 servers or servers with the
@@ -2759,24 +2829,31 @@ static int psftp_connect(char *userhost, char *user, int portnumber)
      * obvious pathnames and then give up, and when it does give up
      * it will print the preferred pathname in the error messages.
      */
-    cfg.remote_cmd_ptr2 =
-       "test -x /usr/lib/sftp-server && exec /usr/lib/sftp-server\n"
-       "test -x /usr/local/lib/sftp-server && exec /usr/local/lib/sftp-server\n"
-       "exec sftp-server";
-    cfg.ssh_subsys2 = FALSE;
+    conf_set_str(conf, CONF_remote_cmd2,
+                "test -x /usr/lib/sftp-server &&"
+                " exec /usr/lib/sftp-server\n"
+                "test -x /usr/local/lib/sftp-server &&"
+                " exec /usr/local/lib/sftp-server\n"
+                "exec sftp-server");
+    conf_set_int(conf, CONF_ssh_subsys2, FALSE);
 
     back = &ssh_backend;
 
-    err = back->init(NULL, &backhandle, &cfg, cfg.host, cfg.port, &realhost,
-                    0, cfg.tcp_keepalives);
+    err = back->init(NULL, &backhandle, conf,
+                    conf_get_str(conf, CONF_host),
+                    conf_get_int(conf, CONF_port),
+                    &realhost, 0,
+                    conf_get_int(conf, CONF_tcp_keepalives));
     if (err != NULL) {
        fprintf(stderr, "ssh_init: %s\n", err);
        return 1;
     }
-    logctx = log_init(NULL, &cfg);
+    logctx = log_init(NULL, conf);
     back->provide_logctx(backhandle, logctx);
     console_provide_logctx(logctx);
     while (!back->sendok(backhandle)) {
+       if (back->exitcode(backhandle) >= 0)
+           return 1;
        if (ssh_sftp_loop_iteration() < 0) {
            fprintf(stderr, "ssh_init: error during SSH connection setup\n");
            return 1;
@@ -2811,7 +2888,6 @@ int psftp_main(int argc, char *argv[])
     int mode = 0;
     int modeflags = 0;
     char *batchfile = NULL;
-    int errors = 0;
 
     flags = FLAG_STDERR | FLAG_INTERACTIVE
 #ifdef FLAG_SYNCAGENT
@@ -2819,16 +2895,15 @@ int psftp_main(int argc, char *argv[])
 #endif
        ;
     cmdline_tooltype = TOOLTYPE_FILETRANSFER;
-    ssh_get_line = &console_get_line;
     sk_init();
 
     userhost = user = NULL;
 
     /* Load Default Settings before doing anything else. */
-    do_defaults(NULL, &cfg);
+    conf = conf_new();
+    do_defaults(NULL, conf);
     loaded_session = FALSE;
 
-    errors = 0;
     for (i = 1; i < argc; i++) {
        int ret;
        if (argv[i][0] != '-') {
@@ -2838,7 +2913,7 @@ int psftp_main(int argc, char *argv[])
                 userhost = dupstr(argv[i]);
            continue;
        }
-       ret = cmdline_process_param(argv[i], i+1<argc?argv[i+1]:NULL, 1, &cfg);
+       ret = cmdline_process_param(argv[i], i+1<argc?argv[i+1]:NULL, 1, conf);
        if (ret == -2) {
            cmdline_error("option \"%s\" requires an argument", argv[i]);
        } else if (ret == 2) {
@@ -2848,12 +2923,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;
@@ -2880,8 +2957,8 @@ int psftp_main(int argc, char *argv[])
      * otherwise been specified, pop it in `userhost' so that
      * `psftp -load sessname' is sufficient to start a session.
      */
-    if (!userhost && cfg.host[0] != '\0') {
-       userhost = dupstr(cfg.host);
+    if (!userhost && conf_get_str(conf, CONF_host)[0] != '\0') {
+       userhost = dupstr(conf_get_str(conf, CONF_host));
     }
 
     /*
@@ -2903,9 +2980,10 @@ int psftp_main(int argc, char *argv[])
 
     do_sftp(mode, modeflags, batchfile);
 
-    if (back != NULL && back->socket(backhandle) != NULL) {
+    if (back != NULL && back->connected(backhandle)) {
        char ch;
        back->special(backhandle, TS_EOF);
+        sent_eof = TRUE;
        sftp_recvdata(&ch, 1);
     }
     do_sftp_cleanup();