The xfer mechanism wasn't gracefully terminating when an error was
[u/mdw/putty] / psftp.c
diff --git a/psftp.c b/psftp.c
index de1964e..0eedf9d 100644 (file)
--- a/psftp.c
+++ b/psftp.c
@@ -41,14 +41,6 @@ static Config cfg;
  */
 
 /*
- * Determine whether a string is entirely composed of dots.
- */
-static int is_dots(char *str)
-{
-    return str[strspn(str, ".")] == '\0';
-}
-
-/*
  * Attempt to canonify a pathname starting from the pwd. If
  * canonification fails, at least fall back to returning a _valid_
  * pathname (though it may be ugly, eg /home/simon/../foobar).
@@ -209,7 +201,7 @@ int sftp_get_file(char *fname, char *outfname, int recurse, int restart,
     struct fxp_xfer *xfer;
     uint64 offset;
     FILE *fp;
-    int ret;
+    int ret, shown_err = FALSE;
 
     /*
      * In recursive mode, see if we're dealing with a directory.
@@ -291,10 +283,19 @@ int sftp_get_file(char *fname, char *outfname, int recurse, int restart,
                    ournames = sresize(ournames, namesize, struct fxp_name *);
                }
                for (i = 0; i < names->nnames; i++)
-                   if (!is_dots(names->names[i].filename) &&
+                   if (strcmp(names->names[i].filename, ".") &&
+                       strcmp(names->names[i].filename, "..") &&
                        (!wildcard || wc_match(wildcard,
-                                              names->names[i].filename)))
-                       ournames[nnames++] = fxp_dup_name(&names->names[i]);
+                                              names->names[i].filename))) {
+                       if (!vet_filename(names->names[i].filename)) {
+                           printf("ignoring potentially dangerous server-"
+                                  "supplied filename '%s'\n",
+                                  names->names[i].filename);
+                       } else {
+                           ournames[nnames++] =
+                               fxp_dup_name(&names->names[i]);
+                       }
+                   }
                fxp_free_names(names);
            }
            sftp_register(req = fxp_close_send(dirhandle));
@@ -442,7 +443,10 @@ int sftp_get_file(char *fname, char *outfname, int recurse, int restart,
        ret = xfer_download_gotpkt(xfer, pktin);
 
        if (ret < 0) {
-            printf("error while reading: %s\n", fxp_error());
+           if (!shown_err) {
+               printf("error while reading: %s\n", fxp_error());
+               shown_err = TRUE;
+           }
             ret = 0;
        }
 
@@ -767,6 +771,23 @@ int sftp_cmd_quit(struct sftp_command *cmd)
     return -1;
 }
 
+int sftp_cmd_close(struct sftp_command *cmd)
+{
+    if (back == NULL) {
+       printf("psftp: not connected to a host; use \"open host.name\"\n");
+       return 0;
+    }
+
+    if (back != NULL && back->socket(backhandle) != NULL) {
+       char ch;
+       back->special(backhandle, TS_EOF);
+       sftp_recvdata(&ch, 1);
+    }
+    do_sftp_cleanup();
+
+    return 0;
+}
+
 /*
  * List a directory. If no arguments are given, list pwd; otherwise
  * list the directory given in words[1].
@@ -985,14 +1006,14 @@ int sftp_general_get(struct sftp_command *cmd, int restart, int multiple)
        } else if (!strcmp(cmd->words[i], "-r")) {
            recurse = TRUE;
        } else {
-           printf("get: unrecognised option '%s'\n", cmd->words[i]);
+           printf("%s: unrecognised option '%s'\n", cmd->words[0], cmd->words[i]);
            return 0;
        }
        i++;
     }
 
     if (i >= cmd->nwords) {
-       printf("get: expects a filename\n");
+       printf("%s: expects a filename\n", cmd->words[0]);
        return 0;
     }
 
@@ -1071,14 +1092,14 @@ int sftp_general_put(struct sftp_command *cmd, int restart, int multiple)
        } else if (!strcmp(cmd->words[i], "-r")) {
            recurse = TRUE;
        } else {
-           printf("put: unrecognised option '%s'\n", cmd->words[i]);
+           printf("%s: unrecognised option '%s'\n", cmd->words[0], cmd->words[i]);
            return 0;
        }
        i++;
     }
 
     if (i >= cmd->nwords) {
-       printf("put: expects a filename\n");
+       printf("%s: expects a filename\n", cmd->words[0]);
        return 0;
     }
 
@@ -1632,6 +1653,14 @@ static struct sftp_cmd_lookup {
            sftp_cmd_chmod
     },
     {
+       "close", TRUE, "finish your SFTP session but do not quit PSFTP",
+           "\n"
+           "  Terminates your SFTP session, but does not quit the PSFTP\n"
+           "  program. You can then use \"open\" to start another SFTP\n"
+           "  session, to the same server or to a different one.\n",
+           sftp_cmd_close
+    },
+    {
        "del", TRUE, "delete a file",
            " <filename>\n"
            "  Delete a file.\n",
@@ -1642,10 +1671,12 @@ static struct sftp_cmd_lookup {
     },
     {
        "dir", TRUE, "list contents of a remote directory",
-           " [ <directory-name> ]\n"
+           " [ <directory-name> ]/[ <wildcard> ]\n"
            "  List the contents of a specified directory on the server.\n"
            "  If <directory-name> is not given, the current working directory\n"
-           "  will be listed.\n",
+           "  is assumed.\n"
+           "  If <wildcard> is given, it is treated as a set of files to\n"
+           "  list; otherwise, all files are listed.\n",
            sftp_cmd_ls
     },
     {
@@ -1653,10 +1684,11 @@ static struct sftp_cmd_lookup {
     },
     {
        "get", TRUE, "download a file from the server to your local machine",
-           " <filename> [ <local-filename> ]\n"
+           " [ -r ] [ -- ] <filename> [ <local-filename> ]\n"
            "  Downloads a file on the server and stores it locally under\n"
            "  the same name, or under a different one if you supply the\n"
-           "  argument <local-filename>.\n",
+           "  argument <local-filename>.\n"
+           "  If -r specified, recursively fetch a directory.\n",
            sftp_cmd_get
     },
     {
@@ -1687,10 +1719,11 @@ static struct sftp_cmd_lookup {
     },
     {
        "mget", TRUE, "download multiple files at once",
-           " <filename-or-wildcard> [ <filename-or-wildcard>... ]\n"
+           " [ -r ] [ -- ] <filename-or-wildcard> [ <filename-or-wildcard>... ]\n"
            "  Downloads many files from the server, storing each one under\n"
            "  the same name it has on the server side. You can use wildcards\n"
-           "  such as \"*.c\" to specify lots of files at once.\n",
+           "  such as \"*.c\" to specify lots of files at once.\n"
+           "  If -r specified, recursively fetch files and directories.\n",
            sftp_cmd_mget
     },
     {
@@ -1704,7 +1737,8 @@ static struct sftp_cmd_lookup {
            " <filename-or-wildcard> [ <filename-or-wildcard>... ]\n"
            "  Uploads many files to the server, storing each one under the\n"
            "  same name it has on the client side. You can use wildcards\n"
-           "  such as \"*.c\" to specify lots of files at once.\n",
+           "  such as \"*.c\" to specify lots of files at once.\n"
+           "  If -r specified, recursively store files and directories.\n",
            sftp_cmd_mput
     },
     {
@@ -1724,10 +1758,11 @@ static struct sftp_cmd_lookup {
     },
     {
        "put", TRUE, "upload a file from your local machine to the server",
-           " <filename> [ <remote-filename> ]\n"
+           " [ -r ] [ -- ] <filename> [ <remote-filename> ]\n"
            "  Uploads a file to the server and stores it there under\n"
            "  the same name, or under a different one if you supply the\n"
-           "  argument <remote-filename>.\n",
+           "  argument <remote-filename>.\n"
+           "  If -r specified, recursively store a directory.\n",
            sftp_cmd_put
     },
     {
@@ -1742,10 +1777,11 @@ static struct sftp_cmd_lookup {
     },
     {
        "reget", TRUE, "continue downloading a file",
-           " <filename> [ <local-filename> ]\n"
+           " [ -r ] [ -- ] <filename> [ <local-filename> ]\n"
            "  Works exactly like the \"get\" command, but the local file\n"
            "  must already exist. The download will begin at the end of the\n"
-           "  file. This is for resuming a download that was interrupted.\n",
+           "  file. This is for resuming a download that was interrupted.\n"
+           "  If -r specified, resume interrupted \"get -r\".\n",
            sftp_cmd_reget
     },
     {
@@ -1758,10 +1794,11 @@ static struct sftp_cmd_lookup {
     },
     {
        "reput", TRUE, "continue uploading a file",
-           " <filename> [ <remote-filename> ]\n"
+           " [ -r ] [ -- ] <filename> [ <remote-filename> ]\n"
            "  Works exactly like the \"put\" command, but the remote file\n"
            "  must already exist. The upload will begin at the end of the\n"
-           "  file. This is for resuming an upload that was interrupted.\n",
+           "  file. This is for resuming an upload that was interrupted.\n"
+           "  If -r specified, resume interrupted \"put -r\".\n",
            sftp_cmd_reput
     },
     {
@@ -1866,7 +1903,7 @@ struct sftp_command *sftp_getcmd(FILE *fp, int mode, int modeflags)
            printf("psftp> ");
        line = fgetline(fp);
     } else {
-       line = ssh_sftp_get_cmdline("psftp> ");
+       line = ssh_sftp_get_cmdline("psftp> ", back == NULL);
     }
 
     if (!line || !*line) {
@@ -2004,6 +2041,8 @@ void do_sftp_cleanup()
        sftp_recvdata(&ch, 1);
        back->free(backhandle);
        sftp_cleanup_request();
+       back = NULL;
+       backhandle = NULL;
     }
     if (pwd) {
        sfree(pwd);
@@ -2577,12 +2616,10 @@ int psftp_main(int argc, char *argv[])
        back->special(backhandle, TS_EOF);
        sftp_recvdata(&ch, 1);
     }
+    do_sftp_cleanup();
     random_save_seed();
     cmdline_cleanup();
     console_provide_logctx(NULL);
-    do_sftp_cleanup();
-    backhandle = NULL;
-    back = NULL;
     sk_cleanup();
 
     return 0;