Sebastian Kuschel reports that pfd_closing can be called for a socket
[u/mdw/putty] / pscp.c
diff --git a/pscp.c b/pscp.c
index 575025f..3209932 100644 (file)
--- a/pscp.c
+++ b/pscp.c
@@ -25,6 +25,7 @@
 #include "ssh.h"
 #include "sftp.h"
 #include "storage.h"
+#include "int64.h"
 
 static int list = 0;
 static int verbose = 0;
@@ -35,21 +36,24 @@ static int statistics = 1;
 static int prev_stats_len = 0;
 static int scp_unsafe_mode = 0;
 static int errs = 0;
-static int gui_mode = 0;
 static int try_scp = 1;
 static int try_sftp = 1;
 static int main_cmd_is_sftp = 0;
 static int fallback_cmd_is_sftp = 0;
 static int using_sftp = 0;
+static int uploading = 0;
 
 static Backend *back;
 static void *backhandle;
-static Config cfg;
+static Conf *conf;
+int sent_eof = FALSE;
 
 static void source(char *src);
 static void rsource(char *src);
 static void sink(char *targ, char *src);
 
+const char *const appname = "PSCP";
+
 /*
  * The maximum amount of queued data we accept before we stop and
  * wait for the server to process some.
@@ -69,10 +73,7 @@ void ldisc_send(void *handle, char *buf, int len, int interactive)
 
 static void tell_char(FILE * stream, char c)
 {
-    if (!gui_mode)
-       fputc(c, stream);
-    else
-       gui_send_char(stream == stderr, c);
+    fputc(c, stream);
 }
 
 static void tell_str(FILE * stream, char *str)
@@ -112,9 +113,6 @@ void fatalbox(char *fmt, ...)
     sfree(str2);
     errs++;
 
-    if (gui_mode)
-       gui_send_errcount(list, errs);
-
     cleanup_exit(1);
 }
 void modalfatalbox(char *fmt, ...)
@@ -130,11 +128,21 @@ void modalfatalbox(char *fmt, ...)
     sfree(str2);
     errs++;
 
-    if (gui_mode)
-       gui_send_errcount(list, errs);
-
     cleanup_exit(1);
 }
+void nonfatal(char *fmt, ...)
+{
+    char *str, *str2;
+    va_list ap;
+    va_start(ap, fmt);
+    str = dupvprintf(fmt, ap);
+    str2 = dupcat("Error: ", str, "\n", NULL);
+    sfree(str);
+    va_end(ap);
+    tell_str(stderr, str2);
+    sfree(str2);
+    errs++;
+}
 void connection_fatal(void *frontend, char *fmt, ...)
 {
     char *str, *str2;
@@ -148,9 +156,6 @@ void connection_fatal(void *frontend, char *fmt, ...)
     sfree(str2);
     errs++;
 
-    if (gui_mode)
-       gui_send_errcount(list, errs);
-
     cleanup_exit(1);
 }
 
@@ -188,16 +193,11 @@ 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;
     }
 
-    /*
-     * If this is before the real session begins, just return.
-     */
-    if (!outptr)
-       return 0;
-
     if ((outlen > 0) && (len > 0)) {
        unsigned used = outlen;
        if (used > len)
@@ -229,6 +229,20 @@ int from_backend_untrusted(void *frontend_handle, const char *data, int len)
     assert(!"Unexpected call to from_backend_untrusted()");
     return 0; /* not reached */
 }
+int from_backend_eof(void *frontend)
+{
+    /*
+     * We usually expect to be the party deciding when to close the
+     * connection, so if we see EOF before we sent it ourselves, we
+     * should panic. The exception is if we're using old-style scp and
+     * downloading rather than uploading.
+     */
+    if ((using_sftp || uploading) && !sent_eof) {
+        connection_fatal(frontend,
+                         "Received unexpected end-of-file from server");
+    }
+    return FALSE;
+}
 static int ssh_scp_recv(unsigned char *buf, int len)
 {
     outptr = buf;
@@ -257,7 +271,7 @@ static int ssh_scp_recv(unsigned 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 */
     }
 
@@ -270,8 +284,14 @@ static int ssh_scp_recv(unsigned char *buf, int len)
 static void ssh_scp_init(void)
 {
     while (!back->sendok(backhandle)) {
-       if (ssh_sftp_loop_iteration() < 0)
+        if (back->exitcode(backhandle) >= 0) {
+            errs++;
+            return;
+        }
+       if (ssh_sftp_loop_iteration() < 0) {
+            errs++;
            return;                    /* doom */
+        }
     }
 
     /* Work out which backend we ended up using. */
@@ -304,19 +324,40 @@ static void bump(char *fmt, ...)
     sfree(str2);
     errs++;
 
-    if (back != NULL && back->socket(backhandle) != NULL) {
+    if (back != NULL && back->connected(backhandle)) {
        char ch;
        back->special(backhandle, TS_EOF);
+        sent_eof = TRUE;
        ssh_scp_recv((unsigned char *) &ch, 1);
     }
 
-    if (gui_mode)
-       gui_send_errcount(list, errs);
-
     cleanup_exit(1);
 }
 
 /*
+ * Wait for the reply to a single SFTP request. Parallels the same
+ * function in psftp.c (but isn't centralised into sftp.c because the
+ * latter module handles SFTP only and shouldn't assume that SFTP is
+ * the only thing going on by calling connection_fatal).
+ */
+struct sftp_packet *sftp_wait_for_reply(struct sftp_request *req)
+{
+    struct sftp_packet *pktin;
+    struct sftp_request *rreq;
+
+    sftp_register(req);
+    pktin = sftp_recv();
+    if (pktin == NULL)
+        connection_fatal(NULL, "did not receive SFTP response packet "
+                         "from server");
+    rreq = sftp_find_request(pktin);
+    if (rreq != req)
+        connection_fatal(NULL, "unable to understand SFTP response packet "
+                         "from server: %s", fxp_error());
+    return pktin;
+}
+
+/*
  *  Open an SSH connection to user@host and execute cmd.
  */
 static void do_cmd(char *host, char *user, char *cmd)
@@ -345,88 +386,90 @@ static void do_cmd(char *host, char *user, char *cmd)
      */
     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);
     }
 
     /*
      * 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);
-    }
+       char *hostbuf = dupstr(conf_get_str(conf, CONF_host));
+       char *host = hostbuf;
+       char *p, *q;
 
-    /* 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';
+       /*
+        * Trim leading whitespace.
+        */
+       host += strspn(host, " \t");
+
+       /*
+        * 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;
            }
-           memmove(cfg.host, atsign + 1, 1 + strlen(atsign + 1));
        }
-    }
 
-    /*
-     * 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++;
-           }
-           p2++;
+       /*
+        * Remove any remaining whitespace.
+        */
+       p = hostbuf;
+       q = host;
+       while (*q) {
+           if (*q != ' ' && *q != '\t')
+               *p++ = *q;
+           q++;
        }
-       cfg.host[p1] = '\0';
+       *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';
-    } else if (cfg.username[0] == '\0') {
+       conf_set_str(conf, CONF_username, user);
+    } else if (conf_get_str(conf, CONF_username)[0] == '\0') {
        user = get_username();
        if (!user)
            bump("Empty user name");
        else {
            if (verbose)
                tell_user(stderr, "Guessing user name: %s", user);
-           strncpy(cfg.username, user, sizeof(cfg.username) - 1);
-           cfg.username[sizeof(cfg.username) - 1] = '\0';
+           conf_set_str(conf, CONF_username, user);
            sfree(user);
        }
     }
@@ -436,9 +479,14 @@ static void do_cmd(char *host, char *user, char *cmd)
      * 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 main and possibly fallback command depending on
@@ -446,56 +494,61 @@ static void do_cmd(char *host, char *user, char *cmd)
      * Attempt to start the SFTP subsystem as a first choice,
      * falling back to the provided scp command if that fails.
      */
-    cfg.remote_cmd_ptr2 = NULL;
+    conf_set_str(conf, CONF_remote_cmd2, "");
     if (try_sftp) {
        /* First choice is SFTP subsystem. */
        main_cmd_is_sftp = 1;
-       strcpy(cfg.remote_cmd, "sftp");
-       cfg.ssh_subsys = TRUE;
+       conf_set_str(conf, CONF_remote_cmd, "sftp");
+       conf_set_int(conf, CONF_ssh_subsys, TRUE);
        if (try_scp) {
            /* Fallback is to use the provided scp command. */
            fallback_cmd_is_sftp = 0;
-           cfg.remote_cmd_ptr2 = cmd;
-           cfg.ssh_subsys2 = FALSE;
+           conf_set_str(conf, CONF_remote_cmd2, cmd);
+           conf_set_int(conf, CONF_ssh_subsys2, FALSE);
        } else {
            /* Since we're not going to try SCP, we may as well try
             * harder to find an SFTP server, since in the current
             * implementation we have a spare slot. */
            fallback_cmd_is_sftp = 1;
            /* see psftp.c for full explanation of this kludge */
-           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);
        }
     } else {
        /* Don't try SFTP at all; just try the scp command. */
        main_cmd_is_sftp = 0;
-       cfg.remote_cmd_ptr = cmd;
-       cfg.ssh_subsys = FALSE;
+       conf_set_str(conf, CONF_remote_cmd, cmd);
+       conf_set_int(conf, CONF_ssh_subsys, FALSE);
     }
-    cfg.nopty = TRUE;
+    conf_set_int(conf, CONF_nopty, TRUE);
 
     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)
        bump("ssh_init: %s", err);
-    logctx = log_init(NULL, &cfg);
+    logctx = log_init(NULL, conf);
     back->provide_logctx(backhandle, logctx);
     console_provide_logctx(logctx);
     ssh_scp_init();
-    if (verbose && realhost != NULL)
-       tell_user(stderr, "Connected to %s\n", realhost);
+    if (verbose && realhost != NULL && errs == 0)
+       tell_user(stderr, "Connected to %s", realhost);
     sfree(realhost);
 }
 
 /*
  *  Update statistic information about current file.
  */
-static void print_stats(char *name, unsigned long size, unsigned long done,
+static void print_stats(char *name, uint64 size, uint64 done,
                        time_t start, time_t now)
 {
     float ratebs;
@@ -504,34 +557,42 @@ static void print_stats(char *name, unsigned long size, unsigned long done,
     int pct;
     int len;
     int elap;
+    double donedbl;
+    double sizedbl;
 
     elap = (unsigned long) difftime(now, start);
 
     if (now > start)
-       ratebs = (float) done / elap;
+       ratebs = (float) (uint64_to_double(done) / elap);
     else
-       ratebs = (float) done;
+       ratebs = (float) uint64_to_double(done);
 
     if (ratebs < 1.0)
-       eta = size - done;
-    else
-       eta = (unsigned long) ((size - done) / ratebs);
+       eta = (unsigned long) (uint64_to_double(uint64_subtract(size, done)));
+    else {
+        eta = (unsigned long)
+           ((uint64_to_double(uint64_subtract(size, done)) / ratebs));
+    }
+
     etastr = dupprintf("%02ld:%02ld:%02ld",
                       eta / 3600, (eta % 3600) / 60, eta % 60);
 
-    pct = (int) (100 * (done * 1.0 / size));
+    donedbl = uint64_to_double(done);
+    sizedbl = uint64_to_double(size);
+    pct = (int) (100 * (donedbl * 1.0 / sizedbl));
 
-    if (gui_mode) {
-       gui_update_stats(name, size, pct, elap, done, eta,
-                        (unsigned long) ratebs);
-    } else {
-       len = printf("\r%-25.25s | %10ld kB | %5.1f kB/s | ETA: %8s | %3d%%",
-                    name, done / 1024, ratebs / 1024.0, etastr, pct);
+    {
+       char donekb[40];
+       /* divide by 1024 to provide kB */
+       uint64_decimal(uint64_shift_right(done, 10), donekb);
+       len = printf("\r%-25.25s | %s kB | %5.1f kB/s | ETA: %8s | %3d%%",
+                    name,
+                    donekb, ratebs / 1024.0, etastr, pct);
        if (len < prev_stats_len)
            printf("%*s", prev_stats_len - len, "");
        prev_stats_len = len;
 
-       if (done == size)
+       if (uint64_compare(done, size) == 0)
            printf("\n");
 
        fflush(stdout);
@@ -546,26 +607,23 @@ static void print_stats(char *name, unsigned long size, unsigned long done,
  */
 static char *colon(char *str)
 {
-    /* Check and process IPv6 literal addresses
-     * (eg: 'jeroen@[2001:db8::1]:myfile.txt') */
-    char *ipv6 = strchr(str, '[');
-    if (ipv6) {
-       str = strchr(str, ']');
-       if (str) {
-           /* Terminate on the closing bracket */
-           *str++ = '\0';
-           return (str);
-       }
-       return (NULL);
-    }
-
     /* We ignore a leading colon, since the hostname cannot be
        empty. We also ignore a colon as second character because
        of filenames like f:myfile.txt. */
-    if (str[0] == '\0' || str[0] == ':' || str[1] == ':')
+    if (str[0] == '\0' || str[0] == ':' ||
+        (str[0] != '[' && str[1] == ':'))
        return (NULL);
-    while (*str != '\0' && *str != ':' && *str != '/' && *str != '\\')
+    while (*str != '\0' && *str != ':' && *str != '/' && *str != '\\') {
+       if (*str == '[') {
+           /* Skip over IPv6 literal addresses
+            * (eg: 'jeroen@[2001:db8::1]:myfile.txt') */
+           char *ipv6_end = strchr(str, ']');
+           if (ipv6_end) {
+               str = ipv6_end;
+           }
+       }
        str++;
+    }
     if (*str == ':')
        return (str);
     else
@@ -632,7 +690,7 @@ static int response(void)
        } while (p < sizeof(rbuf) && ch != '\n');
        rbuf[p - 1] = '\0';
        if (resp == 1)
-           tell_user(stderr, "%s\n", rbuf);
+           tell_user(stderr, "%s", rbuf);
        else
            bump("%s", rbuf);
        errs++;
@@ -665,7 +723,7 @@ void scp_sftp_listdir(char *dirname)
     struct fxp_names *names;
     struct fxp_name *ournames;
     struct sftp_packet *pktin;
-    struct sftp_request *req, *rreq;
+    struct sftp_request *req;
     int nnames, namesize;
     int i;
 
@@ -677,10 +735,9 @@ void scp_sftp_listdir(char *dirname)
 
     printf("Listing directory %s\n", dirname);
 
-    sftp_register(req = fxp_opendir_send(dirname));
-    rreq = sftp_find_request(pktin = sftp_recv());
-    assert(rreq == req);
-    dirh = fxp_opendir_recv(pktin, rreq);
+    req = fxp_opendir_send(dirname);
+    pktin = sftp_wait_for_reply(req);
+    dirh = fxp_opendir_recv(pktin, req);
 
     if (dirh == NULL) {
        printf("Unable to open %s: %s\n", dirname, fxp_error());
@@ -690,10 +747,9 @@ void scp_sftp_listdir(char *dirname)
 
        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)
@@ -716,22 +772,24 @@ void scp_sftp_listdir(char *dirname)
            names->nnames = 0;         /* prevent free_names */
            fxp_free_names(names);
        }
-       sftp_register(req = fxp_close_send(dirh));
-       rreq = sftp_find_request(pktin = sftp_recv());
-       assert(rreq == req);
-       fxp_close_recv(pktin, rreq);
+       req = fxp_close_send(dirh);
+        pktin = sftp_wait_for_reply(req);
+       fxp_close_recv(pktin, req);
 
        /*
         * Now we have our filenames. Sort them by actual file
         * name, and then output the longname parts.
         */
-       qsort(ournames, nnames, sizeof(*ournames), sftp_ls_compare);
+        if (nnames > 0)
+            qsort(ournames, nnames, sizeof(*ournames), sftp_ls_compare);
 
        /*
         * And print them.
         */
        for (i = 0; i < nnames; i++)
            printf("%s\n", ournames[i].longname);
+
+        sfree(ournames);
     }
 }
 
@@ -766,7 +824,7 @@ int scp_source_setup(char *target, int shouldbedir)
         * directory.
         */
        struct sftp_packet *pktin;
-       struct sftp_request *req, *rreq;
+       struct sftp_request *req;
        struct fxp_attrs attrs;
        int ret;
 
@@ -776,10 +834,9 @@ int scp_source_setup(char *target, int shouldbedir)
            return 1;
        }
 
-       sftp_register(req = fxp_stat_send(target));
-       rreq = sftp_find_request(pktin = sftp_recv());
-       assert(rreq == req);
-       ret = fxp_stat_recv(pktin, rreq, &attrs);
+       req = fxp_stat_send(target);
+        pktin = sftp_wait_for_reply(req);
+       ret = fxp_stat_recv(pktin, req, &attrs);
 
        if (!ret || !(attrs.flags & SSH_FILEXFER_ATTR_PERMISSIONS))
            scp_sftp_targetisdir = 0;
@@ -825,12 +882,13 @@ int scp_send_filetimes(unsigned long mtime, unsigned long atime)
     }
 }
 
-int scp_send_filename(char *name, unsigned long size, int modes)
+int scp_send_filename(char *name, uint64 size, int permissions)
 {
     if (using_sftp) {
        char *fullname;
        struct sftp_packet *pktin;
-       struct sftp_request *req, *rreq;
+       struct sftp_request *req;
+        struct fxp_attrs attrs;
 
        if (scp_sftp_targetisdir) {
            fullname = dupcat(scp_sftp_remotepath, "/", name, NULL);
@@ -838,15 +896,19 @@ int scp_send_filename(char *name, unsigned long size, int modes)
            fullname = dupstr(scp_sftp_remotepath);
        }
 
-       sftp_register(req = fxp_open_send(fullname, SSH_FXF_WRITE |
-                                         SSH_FXF_CREAT | SSH_FXF_TRUNC));
-       rreq = sftp_find_request(pktin = sftp_recv());
-       assert(rreq == req);
-       scp_sftp_filehandle = fxp_open_recv(pktin, rreq);
+        attrs.flags = 0;
+        PUT_PERMISSIONS(attrs, permissions);
+
+       req = fxp_open_send(fullname,
+                            SSH_FXF_WRITE | SSH_FXF_CREAT | SSH_FXF_TRUNC,
+                            &attrs);
+        pktin = sftp_wait_for_reply(req);
+       scp_sftp_filehandle = fxp_open_recv(pktin, req);
 
        if (!scp_sftp_filehandle) {
            tell_user(stderr, "pscp: unable to open %s: %s",
                      fullname, fxp_error());
+            sfree(fullname);
            errs++;
            return 1;
        }
@@ -857,7 +919,11 @@ int scp_send_filename(char *name, unsigned long size, int modes)
        return 0;
     } else {
        char buf[40];
-       sprintf(buf, "C%04o %lu ", modes, size);
+       char sizestr[40];
+       uint64_decimal(size, sizestr);
+        if (permissions < 0)
+            permissions = 0644;
+       sprintf(buf, "C%04o %s ", (int)(permissions & 07777), sizestr);
        back->send(backhandle, buf, strlen(buf));
        back->send(backhandle, name, strlen(name));
        back->send(backhandle, "\n", 1);
@@ -878,8 +944,10 @@ int scp_send_filedata(char *data, int len)
        while (!xfer_upload_ready(scp_sftp_xfer)) {
            pktin = sftp_recv();
            ret = xfer_upload_gotpkt(scp_sftp_xfer, pktin);
-           if (!ret) {
-               tell_user(stderr, "error while writing: %s\n", fxp_error());
+           if (ret <= 0) {
+               tell_user(stderr, "error while writing: %s", fxp_error());
+                if (ret == INT_MIN)        /* pktin not even freed */
+                    sfree(pktin);
                errs++;
                return 1;
            }
@@ -913,12 +981,19 @@ int scp_send_finish(void)
     if (using_sftp) {
        struct fxp_attrs attrs;
        struct sftp_packet *pktin;
-       struct sftp_request *req, *rreq;
+       struct sftp_request *req;
        int ret;
 
        while (!xfer_done(scp_sftp_xfer)) {
            pktin = sftp_recv();
-           xfer_upload_gotpkt(scp_sftp_xfer, pktin);
+           ret = xfer_upload_gotpkt(scp_sftp_xfer, pktin);
+           if (ret <= 0) {
+               tell_user(stderr, "error while writing: %s", fxp_error());
+                if (ret == INT_MIN)        /* pktin not even freed */
+                    sfree(pktin);
+               errs++;
+               return 1;
+           }
        }
        xfer_cleanup(scp_sftp_xfer);
 
@@ -929,19 +1004,17 @@ int scp_send_finish(void)
            attrs.flags = SSH_FILEXFER_ATTR_ACMODTIME;
            attrs.atime = scp_sftp_atime;
            attrs.mtime = scp_sftp_mtime;
-           sftp_register(req = fxp_fsetstat_send(scp_sftp_filehandle, attrs));
-           rreq = sftp_find_request(pktin = sftp_recv());
-           assert(rreq == req);
-           ret = fxp_fsetstat_recv(pktin, rreq);
+           req = fxp_fsetstat_send(scp_sftp_filehandle, attrs);
+            pktin = sftp_wait_for_reply(req);
+           ret = fxp_fsetstat_recv(pktin, req);
            if (!ret) {
-               tell_user(stderr, "unable to set file times: %s\n", fxp_error());
+               tell_user(stderr, "unable to set file times: %s", fxp_error());
                errs++;
            }
        }
-       sftp_register(req = fxp_close_send(scp_sftp_filehandle));
-       rreq = sftp_find_request(pktin = sftp_recv());
-       assert(rreq == req);
-       fxp_close_recv(pktin, rreq);
+       req = fxp_close_send(scp_sftp_filehandle);
+        pktin = sftp_wait_for_reply(req);
+       fxp_close_recv(pktin, req);
        scp_has_times = 0;
        return 0;
     } else {
@@ -971,7 +1044,7 @@ int scp_send_dirname(char *name, int modes)
        char const *err;
        struct fxp_attrs attrs;
        struct sftp_packet *pktin;
-       struct sftp_request *req, *rreq;
+       struct sftp_request *req;
        int ret;
 
        if (scp_sftp_targetisdir) {
@@ -987,25 +1060,24 @@ int scp_send_dirname(char *name, int modes)
         * exists and is a directory we will assume we were either
         * successful or it didn't matter.
         */
-       sftp_register(req = fxp_mkdir_send(fullname));
-       rreq = sftp_find_request(pktin = sftp_recv());
-       assert(rreq == req);
-       ret = fxp_mkdir_recv(pktin, rreq);
+       req = fxp_mkdir_send(fullname);
+        pktin = sftp_wait_for_reply(req);
+       ret = fxp_mkdir_recv(pktin, req);
 
        if (!ret)
            err = fxp_error();
        else
            err = "server reported no error";
 
-       sftp_register(req = fxp_stat_send(fullname));
-       rreq = sftp_find_request(pktin = sftp_recv());
-       assert(rreq == req);
-       ret = fxp_stat_recv(pktin, rreq, &attrs);
+       req = fxp_stat_send(fullname);
+        pktin = sftp_wait_for_reply(req);
+       ret = fxp_stat_recv(pktin, req, &attrs);
 
        if (!ret || !(attrs.flags & SSH_FILEXFER_ATTR_PERMISSIONS) ||
            !(attrs.permissions & 0040000)) {
            tell_user(stderr, "unable to create directory %s: %s",
                      fullname, err);
+            sfree(fullname);
            errs++;
            return 1;
        }
@@ -1061,6 +1133,9 @@ int scp_sink_setup(char *source, int preserve, int recursive)
        if (!wc_unescape(newsource, source)) {
            /* Yes, here we go; it's a wildcard. Bah. */
            char *dupsource, *lastpart, *dirpart, *wildcard;
+
+           sfree(newsource);
+
            dupsource = dupstr(source);
            lastpart = stripslashes(dupsource, 0);
            wildcard = dupstr(lastpart);
@@ -1137,8 +1212,8 @@ struct scp_sink_action {
     int action;                               /* FILE, DIR, ENDDIR */
     char *buf;                        /* will need freeing after use */
     char *name;                               /* filename or dirname (not ENDDIR) */
-    int mode;                         /* access mode (not ENDDIR) */
-    unsigned long size;                       /* file size (not ENDDIR) */
+    long permissions;                 /* access permissions (not ENDDIR) */
+    uint64 size;                      /* file size (not ENDDIR) */
     int settime;                      /* 1 if atime and mtime are filled */
     unsigned long atime, mtime;               /* access times for the file */
 };
@@ -1150,7 +1225,7 @@ int scp_get_sink_action(struct scp_sink_action *act)
        int must_free_fname;
        struct fxp_attrs attrs;
        struct sftp_packet *pktin;
-       struct sftp_request *req, *rreq;
+       struct sftp_request *req;
        int ret;
 
        if (!scp_sftp_dirstack_head) {
@@ -1219,14 +1294,14 @@ int scp_get_sink_action(struct scp_sink_action *act)
         * Now we have a filename. Stat it, and see if it's a file
         * or a directory.
         */
-       sftp_register(req = fxp_stat_send(fname));
-       rreq = sftp_find_request(pktin = sftp_recv());
-       assert(rreq == req);
-       ret = fxp_stat_recv(pktin, rreq, &attrs);
+       req = fxp_stat_send(fname);
+        pktin = sftp_wait_for_reply(req);
+       ret = fxp_stat_recv(pktin, req, &attrs);
 
        if (!ret || !(attrs.flags & SSH_FILEXFER_ATTR_PERMISSIONS)) {
            tell_user(stderr, "unable to identify %s: %s", fname,
                      ret ? "file type not supplied" : fxp_error());
+            if (must_free_fname) sfree(fname);
            errs++;
            return 1;
        }
@@ -1275,13 +1350,12 @@ int scp_get_sink_action(struct scp_sink_action *act)
             * list), we must push the other (target,namelist) pair
             * on a stack.
             */
-           sftp_register(req = fxp_opendir_send(fname));
-           rreq = sftp_find_request(pktin = sftp_recv());
-           assert(rreq == req);
-           dirhandle = fxp_opendir_recv(pktin, rreq);
+           req = fxp_opendir_send(fname);
+            pktin = sftp_wait_for_reply(req);
+           dirhandle = fxp_opendir_recv(pktin, req);
 
            if (!dirhandle) {
-               tell_user(stderr, "scp: unable to open directory %s: %s",
+               tell_user(stderr, "pscp: unable to open directory %s: %s",
                          fname, fxp_error());
                if (must_free_fname) sfree(fname);
                errs++;
@@ -1292,16 +1366,20 @@ int scp_get_sink_action(struct scp_sink_action *act)
            while (1) {
                int i;
 
-               sftp_register(req = fxp_readdir_send(dirhandle));
-               rreq = sftp_find_request(pktin = sftp_recv());
-               assert(rreq == req);
-               names = fxp_readdir_recv(pktin, rreq);
+               req = fxp_readdir_send(dirhandle);
+                pktin = sftp_wait_for_reply(req);
+               names = fxp_readdir_recv(pktin, req);
 
                if (names == NULL) {
                    if (fxp_error_type() == SSH_FX_EOF)
                        break;
-                   tell_user(stderr, "scp: reading directory %s: %s\n",
+                   tell_user(stderr, "pscp: reading directory %s: %s",
                              fname, fxp_error());
+
+                    req = fxp_close_send(dirhandle);
+                    pktin = sftp_wait_for_reply(req);
+                    fxp_close_recv(pktin, req);
+
                    if (must_free_fname) sfree(fname);
                    sfree(ournames);
                    errs++;
@@ -1325,7 +1403,7 @@ int scp_get_sink_action(struct scp_sink_action *act)
                         */
                    } else if (!vet_filename(names->names[i].filename)) {
                        tell_user(stderr, "ignoring potentially dangerous server-"
-                                 "supplied filename '%s'\n",
+                                 "supplied filename '%s'",
                                  names->names[i].filename);
                    } else
                        ournames[nnames++] = names->names[i];
@@ -1333,10 +1411,9 @@ int scp_get_sink_action(struct scp_sink_action *act)
                names->nnames = 0;             /* prevent free_names */
                fxp_free_names(names);
            }
-           sftp_register(req = fxp_close_send(dirhandle));
-           rreq = sftp_find_request(pktin = sftp_recv());
-           assert(rreq == req);
-           fxp_close_recv(pktin, rreq);
+           req = fxp_close_send(dirhandle);
+            pktin = sftp_wait_for_reply(req);
+           fxp_close_recv(pktin, req);
 
            newitem = snew(struct scp_sftp_dirstack);
            newitem->next = scp_sftp_dirstack_head;
@@ -1362,8 +1439,8 @@ int scp_get_sink_action(struct scp_sink_action *act)
                act->action = SCP_SINK_DIR;
                act->buf = dupstr(stripslashes(fname, 0));
                act->name = act->buf;
-               act->size = 0;         /* duhh, it's a directory */
-               act->mode = 07777 & attrs.permissions;
+               act->size = uint64_make(0,0);     /* duhh, it's a directory */
+               act->permissions = 07777 & attrs.permissions;
                if (scp_sftp_preserve &&
                    (attrs.flags & SSH_FILEXFER_ATTR_ACMODTIME)) {
                    act->atime = attrs.atime;
@@ -1382,14 +1459,10 @@ int scp_get_sink_action(struct scp_sink_action *act)
            act->buf = dupstr(stripslashes(fname, 0));
            act->name = act->buf;
            if (attrs.flags & SSH_FILEXFER_ATTR_SIZE) {
-               if (uint64_compare(attrs.size,
-                                  uint64_make(0, ULONG_MAX)) > 0) {
-                   act->size = ULONG_MAX;   /* *boggle* */
-               } else
-                   act->size = attrs.size.lo;
+               act->size = attrs.size;
            } else
-               act->size = ULONG_MAX;   /* no idea */
-           act->mode = 07777 & attrs.permissions;
+               act->size = uint64_make(ULONG_MAX,ULONG_MAX);   /* no idea */
+           act->permissions = 07777 & attrs.permissions;
            if (scp_sftp_preserve &&
                (attrs.flags & SSH_FILEXFER_ATTR_ACMODTIME)) {
                act->atime = attrs.atime;
@@ -1433,7 +1506,7 @@ int scp_get_sink_action(struct scp_sink_action *act)
            act->buf[i - 1] = '\0';
            switch (action) {
              case '\01':                      /* error */
-               tell_user(stderr, "%s\n", act->buf);
+               tell_user(stderr, "%s", act->buf);
                errs++;
                continue;                      /* go round again */
              case '\02':                      /* fatal error */
@@ -1468,10 +1541,16 @@ int scp_get_sink_action(struct scp_sink_action *act)
         * If we get here, we must have seen SCP_SINK_FILE or
         * SCP_SINK_DIR.
         */
-       if (sscanf(act->buf, "%o %lu %n", &act->mode, &act->size, &i) != 2)
-           bump("Protocol error: Illegal file descriptor format");
-       act->name = act->buf + i;
-       return 0;
+       {
+           char sizestr[40];
+       
+           if (sscanf(act->buf, "%lo %s %n", &act->permissions,
+                       sizestr, &i) != 2)
+               bump("Protocol error: Illegal file descriptor format");
+           act->size = uint64_from_decimal(sizestr);
+           act->name = act->buf + i;
+           return 0;
+       }
     }
 }
 
@@ -1479,12 +1558,11 @@ int scp_accept_filexfer(void)
 {
     if (using_sftp) {
        struct sftp_packet *pktin;
-       struct sftp_request *req, *rreq;
+       struct sftp_request *req;
 
-       sftp_register(req = fxp_open_send(scp_sftp_currentname, SSH_FXF_READ));
-       rreq = sftp_find_request(pktin = sftp_recv());
-       assert(rreq == req);
-       scp_sftp_filehandle = fxp_open_recv(pktin, rreq);
+       req = fxp_open_send(scp_sftp_currentname, SSH_FXF_READ, NULL);
+        pktin = sftp_wait_for_reply(req);
+       scp_sftp_filehandle = fxp_open_recv(pktin, req);
 
        if (!scp_sftp_filehandle) {
            tell_user(stderr, "pscp: unable to open %s: %s",
@@ -1513,9 +1591,10 @@ int scp_recv_filedata(char *data, int len)
        xfer_download_queue(scp_sftp_xfer);
        pktin = sftp_recv();
        ret = xfer_download_gotpkt(scp_sftp_xfer, pktin);
-
-       if (ret < 0) {
+       if (ret <= 0) {
            tell_user(stderr, "pscp: error while reading: %s", fxp_error());
+            if (ret == INT_MIN)        /* pktin not even freed */
+                sfree(pktin);
            errs++;
            return -1;
        }
@@ -1545,7 +1624,7 @@ int scp_finish_filerecv(void)
 {
     if (using_sftp) {
        struct sftp_packet *pktin;
-       struct sftp_request *req, *rreq;
+       struct sftp_request *req;
 
        /*
         * Ensure that xfer_done() will work correctly, so we can
@@ -1555,19 +1634,25 @@ int scp_finish_filerecv(void)
        xfer_set_error(scp_sftp_xfer);
        while (!xfer_done(scp_sftp_xfer)) {
            void *vbuf;
-           int len;
+           int ret, len;
 
            pktin = sftp_recv();
-           xfer_download_gotpkt(scp_sftp_xfer, pktin);
+           ret = xfer_download_gotpkt(scp_sftp_xfer, pktin);
+            if (ret <= 0) {
+                tell_user(stderr, "pscp: error while reading: %s", fxp_error());
+                if (ret == INT_MIN)        /* pktin not even freed */
+                    sfree(pktin);
+                errs++;
+                return -1;
+            }
            if (xfer_download_data(scp_sftp_xfer, &vbuf, &len))
                sfree(vbuf);
        }
        xfer_cleanup(scp_sftp_xfer);
 
-       sftp_register(req = fxp_close_send(scp_sftp_filehandle));
-       rreq = sftp_find_request(pktin = sftp_recv());
-       assert(rreq == req);
-       fxp_close_recv(pktin, rreq);
+       req = fxp_close_send(scp_sftp_filehandle);
+        pktin = sftp_wait_for_reply(req);
+       fxp_close_recv(pktin, req);
        return 0;
     } else {
        back->send(backhandle, "", 1);
@@ -1586,7 +1671,7 @@ static void run_err(const char *fmt, ...)
     va_start(ap, fmt);
     errs++;
     str = dupvprintf(fmt, ap);
-    str2 = dupcat("scp: ", str, "\n", NULL);
+    str2 = dupcat("pscp: ", str, "\n", NULL);
     sfree(str);
     scp_send_errmsg(str2);
     tell_user(stderr, "%s", str2);
@@ -1599,13 +1684,14 @@ static void run_err(const char *fmt, ...)
  */
 static void source(char *src)
 {
-    unsigned long size;
+    uint64 size;
     unsigned long mtime, atime;
+    long permissions;
     char *last;
     RFile *f;
     int attr;
-    unsigned long i;
-    unsigned long stat_bytes;
+    uint64 i;
+    uint64 stat_bytes;
     time_t stat_starttime, stat_lasttime;
 
     attr = file_type(src);
@@ -1648,31 +1734,40 @@ static void source(char *src)
     if (last == src && strchr(src, ':') != NULL)
        last = strchr(src, ':') + 1;
 
-    f = open_existing_file(src, &size, &mtime, &atime);
+    f = open_existing_file(src, &size, &mtime, &atime, &permissions);
     if (f == NULL) {
        run_err("%s: Cannot open file", src);
        return;
     }
     if (preserve) {
-       if (scp_send_filetimes(mtime, atime))
+       if (scp_send_filetimes(mtime, atime)) {
+            close_rfile(f);
            return;
+        }
     }
 
-    if (verbose)
-       tell_user(stderr, "Sending file %s, size=%lu", last, size);
-    if (scp_send_filename(last, size, 0644))
+    if (verbose) {
+       char sizestr[40];
+       uint64_decimal(size, sizestr);
+       tell_user(stderr, "Sending file %s, size=%s", last, sizestr);
+    }
+    if (scp_send_filename(last, size, permissions)) {
+        close_rfile(f);
        return;
+    }
 
-    stat_bytes = 0;
+    stat_bytes = uint64_make(0,0);
     stat_starttime = time(NULL);
     stat_lasttime = 0;
 
-    for (i = 0; i < size; i += 4096) {
+    for (i = uint64_make(0,0);
+        uint64_compare(i,size) < 0;
+        i = uint64_add32(i,4096)) {
        char transbuf[4096];
        int j, k = 4096;
 
-       if (i + k > size)
-           k = size - i;
+       if (uint64_compare(uint64_add32(i, k),size) > 0) /* i + k > size */ 
+           k = (uint64_subtract(size, i)).lo;  /* k = size - i; */
        if ((j = read_from_file(f, transbuf, k)) != k) {
            if (statistics)
                printf("\n");
@@ -1682,8 +1777,9 @@ static void source(char *src)
            bump("%s: Network error occurred", src);
 
        if (statistics) {
-           stat_bytes += k;
-           if (time(NULL) != stat_lasttime || i + k == size) {
+           stat_bytes = uint64_add32(stat_bytes, k);
+           if (time(NULL) != stat_lasttime ||
+               (uint64_compare(uint64_add32(i, k), size) == 0)) {
                stat_lasttime = time(NULL);
                print_stats(last, size, stat_bytes,
                            stat_starttime, stat_lasttime);
@@ -1750,9 +1846,9 @@ static void sink(char *targ, char *src)
     int exists;
     int attr;
     WFile *f;
-    unsigned long received;
+    uint64 received;
     int wrerror = 0;
-    unsigned long stat_bytes;
+    uint64 stat_bytes;
     time_t stat_starttime, stat_lasttime;
     char *stat_name;
 
@@ -1861,42 +1957,49 @@ static void sink(char *targ, char *src)
        if (act.action == SCP_SINK_DIR) {
            if (exists && attr != FILE_TYPE_DIRECTORY) {
                run_err("%s: Not a directory", destfname);
+                sfree(destfname);
                continue;
            }
            if (!exists) {
                if (!create_directory(destfname)) {
                    run_err("%s: Cannot create directory", destfname);
+                    sfree(destfname);
                    continue;
                }
            }
            sink(destfname, NULL);
            /* can we set the timestamp for directories ? */
+            sfree(destfname);
            continue;
        }
 
-       f = open_new_file(destfname);
+       f = open_new_file(destfname, act.permissions);
        if (f == NULL) {
            run_err("%s: Cannot create file", destfname);
+            sfree(destfname);
            continue;
        }
 
-       if (scp_accept_filexfer())
+       if (scp_accept_filexfer()) {
+            sfree(destfname);
+            close_wfile(f);
            return;
+        }
 
-       stat_bytes = 0;
+       stat_bytes = uint64_make(0, 0);
        stat_starttime = time(NULL);
        stat_lasttime = 0;
        stat_name = stripslashes(destfname, 1);
 
-       received = 0;
-       while (received < act.size) {
-           char transbuf[4096];
-           unsigned long blksize;
+       received = uint64_make(0, 0);
+       while (uint64_compare(received,act.size) < 0) {
+           char transbuf[32768];
+           uint64 blksize;
            int read;
-           blksize = 4096;
-           if (blksize > (act.size - received))
-               blksize = act.size - received;
-           read = scp_recv_filedata(transbuf, (int)blksize);
+           blksize = uint64_make(0, 32768);
+           if (uint64_compare(blksize,uint64_subtract(act.size,received)) > 0)
+             blksize = uint64_subtract(act.size,received);
+           read = scp_recv_filedata(transbuf, (int)blksize.lo);
            if (read <= 0)
                bump("Lost connection");
            if (wrerror)
@@ -1911,15 +2014,15 @@ static void sink(char *targ, char *src)
                continue;
            }
            if (statistics) {
-               stat_bytes += read;
+               stat_bytes = uint64_add32(stat_bytes,read);
                if (time(NULL) > stat_lasttime ||
-                   received + read == act.size) {
+                   uint64_compare(uint64_add32(received, read), act.size) == 0) {
                    stat_lasttime = time(NULL);
                    print_stats(stat_name, act.size, stat_bytes,
                                stat_starttime, stat_lasttime);
                }
            }
-           received += read;
+           received = uint64_add32(received, read);
        }
        if (act.settime) {
            set_file_times(f, act.mtime, act.atime);
@@ -1928,6 +2031,7 @@ static void sink(char *targ, char *src)
        close_wfile(f);
        if (wrerror) {
            run_err("%s: Write error", destfname);
+            sfree(destfname);
            continue;
        }
        (void) scp_finish_filerecv();
@@ -1945,6 +2049,8 @@ static void toremote(int argc, char *argv[])
     char *cmd;
     int i, wc_type;
 
+    uploading = 1;
+
     targ = argv[argc - 1];
 
     /* Separate host from filename */
@@ -2034,6 +2140,8 @@ static void tolocal(int argc, char *argv[])
     char *src, *targ, *host, *user;
     char *cmd;
 
+    uploading = 0;
+
     if (argc != 2)
        bump("More than one remote source not supported");
 
@@ -2091,7 +2199,7 @@ static void get_dir_list(int argc, char *argv[])
     host = src;
     src = colon(src);
     if (src == NULL)
-       bump("Local to local copy not supported");
+       bump("Local file listing not supported");
     *src++ = '\0';
     if (*src == '\0')
        src = ".";
@@ -2162,6 +2270,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");
     printf("  -unsafe   allow server-side wildcards (DANGEROUS)\n");
     printf("  -sftp     force use of SFTP protocol\n");
@@ -2216,14 +2326,15 @@ int psftp_main(int argc, char *argv[])
     sk_init();
 
     /* Load Default Settings before doing anything else. */
-    do_defaults(NULL, &cfg);
+    conf = conf_new();
+    do_defaults(NULL, conf);
     loaded_session = FALSE;
 
     for (i = 1; i < argc; i++) {
        int ret;
        if (argv[i][0] != '-')
            break;
-       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) {
@@ -2241,14 +2352,13 @@ int psftp_main(int argc, char *argv[])
            preserve = 1;
        } else if (strcmp(argv[i], "-q") == 0) {
            statistics = 0;
-       } else if (strcmp(argv[i], "-h") == 0 || strcmp(argv[i], "-?") == 0) {
+       } else if (strcmp(argv[i], "-h") == 0 ||
+                   strcmp(argv[i], "-?") == 0 ||
+                   strcmp(argv[i], "--help") == 0) {
            usage();
-       } else if (strcmp(argv[i], "-V") == 0) {
+       } else if (strcmp(argv[i], "-V") == 0 ||
+                   strcmp(argv[i], "--version") == 0) {
             version();
-       } else if (strcmp(argv[i], "-gui") == 0 && i + 1 < argc) {
-           gui_enable(argv[++i]);
-           gui_mode = 1;
-           console_batch_mode = TRUE;
         } else if (strcmp(argv[i], "-ls") == 0) {
            list = 1;
        } else if (strcmp(argv[i], "-batch") == 0) {
@@ -2288,16 +2398,14 @@ int psftp_main(int argc, char *argv[])
            tolocal(argc, argv);
     }
 
-    if (back != NULL && back->socket(backhandle) != NULL) {
+    if (back != NULL && back->connected(backhandle)) {
        char ch;
        back->special(backhandle, TS_EOF);
+        sent_eof = TRUE;
        ssh_scp_recv((unsigned char *) &ch, 1);
     }
     random_save_seed();
 
-    if (gui_mode)
-       gui_send_errcount(list, errs);
-
     cmdline_cleanup();
     console_provide_logctx(NULL);
     back->free(backhandle);