Bug report #1823487 (http://curl.haxx.se/bug/view.cgi?id=1823487) pointed
authorDaniel Stenberg <daniel@haxx.se>
Thu, 8 Nov 2007 10:22:49 +0000 (10:22 +0000)
committerDaniel Stenberg <daniel@haxx.se>
Thu, 8 Nov 2007 10:22:49 +0000 (10:22 +0000)
out that SFTP requests didn't use persistent connections. Neither did SCP
ones.  I gave the SSH code a good beating and now both SCP and SFTP should
use persistent connections fine. I also did a bunch for indent changes as
well as a bug fix for the "keyboard interactive" auth.

CHANGES
RELEASE-NOTES
lib/ssh.c
lib/urldata.h

diff --git a/CHANGES b/CHANGES
index 497bf43..ebf6ebd 100644 (file)
--- a/CHANGES
+++ b/CHANGES
@@ -6,6 +6,13 @@
 
                                   Changelog
 
+Daniel S (8 Nov 2007)
+- Bug report #1823487 (http://curl.haxx.se/bug/view.cgi?id=1823487) pointed
+  out that SFTP requests didn't use persistent connections. Neither did SCP
+  ones.  I gave the SSH code a good beating and now both SCP and SFTP should
+  use persistent connections fine. I also did a bunch for indent changes as
+  well as a bug fix for the "keyboard interactive" auth.
+
 Dan F (6 Nov 2007)
 - Improved telnet support by drastically reducing the number of write
   callbacks needed to pass a buffer to the user.  Instead one per byte it
index 633c6f8..fe3b550 100644 (file)
@@ -4,7 +4,7 @@ Curl and libcurl 7.17.2
  Command line options:         121
  curl_easy_setopt() options:   147
  Public functions in libcurl:  55
- Public web site mirrors:      41
+ Public web site mirrors:      42
  Known libcurl bindings:       36
  Contributors:                 597
 
@@ -18,6 +18,7 @@ This release includes the following bugfixes:
    with NSS, and also when SCP, SFTP and libz are not available
  o free problem in the curl tool for users with empty home dir
  o curl.h version 7.17.1 problem when building C++ apps with MSVC
+ o SFTP and SCP use persistent connections
 
 This release includes the following known bugs:
 
@@ -34,6 +35,6 @@ New curl mirrors:
 This release would not have looked like this without help, code, reports and
 advice from friends like these:
 
- Dan Fandrich, Gisle Vanem, Toby Peterson
+ Dan Fandrich, Gisle Vanem, Toby Peterson, Yang Tse
  
         Thanks! (and sorry if I forgot to mention someone)
index 96d9367..ab9ddcc 100644 (file)
--- a/lib/ssh.c
+++ b/lib/ssh.c
@@ -134,21 +134,29 @@ static LIBSSH2_FREE_FUNC(libssh2_free);
 
 static int get_pathname(const char **cpp, char **path);
 
-static CURLcode Curl_ssh_connect(struct connectdata *conn, bool *done);
-static CURLcode Curl_ssh_multi_statemach(struct connectdata *conn, bool *done);
-
-static CURLcode Curl_scp_do(struct connectdata *conn, bool *done);
-static CURLcode Curl_scp_done(struct connectdata *conn,
-                              CURLcode, bool premature);
-static CURLcode Curl_scp_doing(struct connectdata *conn,
-                               bool *dophase_done);
-
-static CURLcode Curl_sftp_do(struct connectdata *conn, bool *done);
-static CURLcode Curl_sftp_done(struct connectdata *conn,
-                               CURLcode, bool premature);
-static CURLcode Curl_sftp_doing(struct connectdata *conn,
-                                bool *dophase_done);
-
+static CURLcode ssh_connect(struct connectdata *conn, bool *done);
+static CURLcode ssh_multi_statemach(struct connectdata *conn, bool *done);
+static CURLcode ssh_do(struct connectdata *conn, bool *done);
+
+static CURLcode ssh_getworkingpath(struct connectdata *conn,
+                                   char *homedir, /* when SFTP is used */
+                                   char **path);
+
+static CURLcode scp_done(struct connectdata *conn,
+                         CURLcode, bool premature);
+static CURLcode scp_doing(struct connectdata *conn,
+                          bool *dophase_done);
+static CURLcode scp_disconnect(struct connectdata *conn);
+
+static CURLcode sftp_done(struct connectdata *conn,
+                          CURLcode, bool premature);
+static CURLcode sftp_doing(struct connectdata *conn,
+                           bool *dophase_done);
+static CURLcode sftp_disconnect(struct connectdata *conn);
+static
+CURLcode sftp_perform(struct connectdata *conn,
+                      bool *connected,
+                      bool *dophase_done);
 /*
  * SCP protocol handler.
  */
@@ -156,15 +164,15 @@ static CURLcode Curl_sftp_doing(struct connectdata *conn,
 const struct Curl_handler Curl_handler_scp = {
   "SCP",                                /* scheme */
   ZERO_NULL,                            /* setup_connection */
-  Curl_scp_do,                          /* do_it */
-  Curl_scp_done,                        /* done */
+  ssh_do,                               /* do_it */
+  scp_done,                             /* done */
   ZERO_NULL,                            /* do_more */
-  Curl_ssh_connect,                     /* connect_it */
-  Curl_ssh_multi_statemach,             /* connecting */
-  Curl_scp_doing,                       /* doing */
+  ssh_connect,                          /* connect_it */
+  ssh_multi_statemach,                  /* connecting */
+  scp_doing,                            /* doing */
   ZERO_NULL,                            /* proto_getsock */
   ZERO_NULL,                            /* doing_getsock */
-  ZERO_NULL,                            /* disconnect */
+  scp_disconnect,                       /* disconnect */
   PORT_SSH,                             /* defport */
   PROT_SCP                              /* protocol */
 };
@@ -177,15 +185,15 @@ const struct Curl_handler Curl_handler_scp = {
 const struct Curl_handler Curl_handler_sftp = {
   "SFTP",                               /* scheme */
   ZERO_NULL,                            /* setup_connection */
-  Curl_sftp_do,                         /* do_it */
-  Curl_sftp_done,                       /* done */
+  ssh_do,                               /* do_it */
+  sftp_done,                            /* done */
   ZERO_NULL,                            /* do_more */
-  Curl_ssh_connect,                     /* connect_it */
-  Curl_ssh_multi_statemach,             /* connecting */
-  Curl_sftp_doing,                      /* doing */
+  ssh_connect,                          /* connect_it */
+  ssh_multi_statemach,                  /* connecting */
+  sftp_doing,                           /* doing */
   ZERO_NULL,                            /* proto_getsock */
   ZERO_NULL,                            /* doing_getsock */
-  ZERO_NULL,                            /* disconnect */
+  sftp_disconnect,                      /* disconnect */
   PORT_SSH,                             /* defport */
   PROT_SFTP                             /* protocol */
 };
@@ -198,7 +206,7 @@ kbd_callback(const char *name, int name_len, const char *instruction,
              LIBSSH2_USERAUTH_KBDINT_RESPONSE *responses,
              void **abstract)
 {
-  struct SSHPROTO *ssh = (struct SSHPROTO *)*abstract;
+  struct connectdata *conn = (struct connectdata *)*abstract;
 
 #ifdef CURL_LIBSSH2_DEBUG
   fprintf(stderr, "name=%s\n", name);
@@ -213,8 +221,8 @@ kbd_callback(const char *name, int name_len, const char *instruction,
   (void)instruction_len;
 #endif  /* CURL_LIBSSH2_DEBUG */
   if(num_prompts == 1) {
-    responses[0].text = strdup(ssh->passwd);
-    responses[0].length = strlen(ssh->passwd);
+    responses[0].text = strdup(conn->passwd);
+    responses[0].length = strlen(conn->passwd);
   }
   (void)prompts;
   (void)abstract;
@@ -305,7 +313,6 @@ static void state(struct connectdata *conn, sshstate nowstate)
     "SSH_AUTH_DONE",
     "SSH_SFTP_INIT",
     "SSH_SFTP_REALPATH",
-    "SSH_GET_WORKINGPATH",
     "SSH_SFTP_QUOTE_INIT",
     "SSH_SFTP_POSTQUOTE_INIT",
     "SSH_SFTP_QUOTE",
@@ -339,8 +346,7 @@ static void state(struct connectdata *conn, sshstate nowstate)
     "SSH_SCP_WAIT_EOF",
     "SSH_SCP_WAIT_CLOSE",
     "SSH_SCP_CHANNEL_FREE",
-    "SSH_CHANNEL_CLOSE",
-    "SSH_SESSION_DISCONECT",
+    "SSH_SESSION_DISCONNECT",
     "SSH_SESSION_FREE",
     "QUIT"
   };
@@ -357,6 +363,71 @@ static void state(struct connectdata *conn, sshstate nowstate)
   sshc->state = nowstate;
 }
 
+/* figure out the path to work with in this particular request */
+static CURLcode ssh_getworkingpath(struct connectdata *conn,
+                                   char *homedir,  /* when SFTP is used */
+                                   char **path) /* returns the  allocated
+                                                   real path to work with */
+{
+  struct SessionHandle *data = conn->data;
+  char *real_path;
+  char *working_path;
+  int working_path_len;
+
+  working_path = curl_easy_unescape(data, data->reqdata.path, 0,
+                                    &working_path_len);
+  if(!working_path)
+    return CURLE_OUT_OF_MEMORY;
+
+  /* Check for /~/ , indicating relative to the user's home directory */
+  if(conn->protocol & PROT_SCP) {
+    real_path = (char *)malloc(working_path_len+1);
+    if(real_path == NULL) {
+      free(working_path);
+      return CURLE_OUT_OF_MEMORY;
+    }
+    if((working_path_len > 1) && (working_path[1] == '~'))
+      /* It is referenced to the home directory, so strip the leading '/' */
+      memcpy(real_path, working_path+1, 1 + working_path_len-1);
+    else
+      memcpy(real_path, working_path, 1 + working_path_len);
+  }
+  else if(conn->protocol & PROT_SFTP) {
+    if((working_path_len > 1) && (working_path[1] == '~')) {
+      size_t homelen = strlen(homedir);
+      real_path = (char *)malloc(homelen + working_path_len + 1);
+      if(real_path == NULL) {
+        free(working_path);
+        return CURLE_OUT_OF_MEMORY;
+      }
+      /* It is referenced to the home directory, so strip the
+         leading '/' */
+      memcpy(real_path, homedir, homelen);
+      real_path[homelen] = '/';
+      real_path[homelen+1] = '\0';
+      if(working_path_len > 3) {
+        memcpy(real_path+homelen+1, working_path + 3,
+               1 + working_path_len -3);
+      }
+    }
+    else {
+      real_path = (char *)malloc(working_path_len+1);
+      if(real_path == NULL) {
+        free(working_path);
+        return CURLE_OUT_OF_MEMORY;
+      }
+      memcpy(real_path, working_path, 1+working_path_len);
+    }
+  }
+
+  free(working_path);
+
+  /* store the pointer for the caller to receive */
+  *path = real_path;
+
+  return CURLE_OK;
+}
+
 static CURLcode ssh_statemach_act(struct connectdata *conn)
 {
   CURLcode result = CURLE_OK;
@@ -369,1423 +440,1369 @@ static CURLcode ssh_statemach_act(struct connectdata *conn)
 #endif /* CURL_LIBSSH2_DEBUG */
   const char *host_public_key_md5;
   int rc,i;
-  long err;
+  int err;
 
   switch(sshc->state) {
-    case SSH_S_STARTUP:
-      sshc->secondCreateDirs = 0;
-      sshc->nextState = SSH_NO_STATE;
-      sshc->actualCode = CURLE_OK;
+  case SSH_S_STARTUP:
+    sshc->secondCreateDirs = 0;
+    sshc->nextstate = SSH_NO_STATE;
+    sshc->actualcode = CURLE_OK;
 
-      rc = libssh2_session_startup(sshc->ssh_session, sock);
-      if(rc == LIBSSH2_ERROR_EAGAIN) {
-        break;
-      }
-      else if(rc) {
-        failf(data, "Failure establishing ssh session");
-        state(conn, SSH_SESSION_FREE);
-        sshc->actualCode = CURLE_FAILED_INIT;
-        break;
-      }
+    rc = libssh2_session_startup(sshc->ssh_session, sock);
+    if(rc == LIBSSH2_ERROR_EAGAIN) {
+      break;
+    }
+    else if(rc) {
+      failf(data, "Failure establishing ssh session");
+      state(conn, SSH_SESSION_FREE);
+      sshc->actualcode = CURLE_FAILED_INIT;
+      break;
+    }
 
-      /* Set libssh2 to non-blocking, since cURL is all non-blocking */
-      libssh2_session_set_blocking(sshc->ssh_session, 0);
+    /* Set libssh2 to non-blocking, since cURL is all non-blocking */
+    libssh2_session_set_blocking(sshc->ssh_session, 0);
 
 #ifdef CURL_LIBSSH2_DEBUG
-      /*
-       * Before we authenticate we should check the hostkey's fingerprint
-       * against our known hosts. How that is handled (reading from file,
-       * whatever) is up to us. As for know not much is implemented, besides
-       * showing how to get the fingerprint.
-       */
-      fingerprint = libssh2_hostkey_hash(sshc->ssh_session,
-                                         LIBSSH2_HOSTKEY_HASH_MD5);
-
-      /* The fingerprint points to static storage (!), don't free() it. */
-      infof(data, "Fingerprint: ");
-      for (rc = 0; rc < 16; rc++) {
-        infof(data, "%02X ", (unsigned char) fingerprint[rc]);
-      }
-      infof(data, "\n");
+    /*
+     * Before we authenticate we should check the hostkey's fingerprint
+     * against our known hosts. How that is handled (reading from file,
+     * whatever) is up to us. As for know not much is implemented, besides
+     * showing how to get the fingerprint.
+     */
+    fingerprint = libssh2_hostkey_hash(sshc->ssh_session,
+                                       LIBSSH2_HOSTKEY_HASH_MD5);
+
+    /* The fingerprint points to static storage (!), don't free() it. */
+    infof(data, "Fingerprint: ");
+    for (rc = 0; rc < 16; rc++) {
+      infof(data, "%02X ", (unsigned char) fingerprint[rc]);
+    }
+    infof(data, "\n");
 #endif /* CURL_LIBSSH2_DEBUG */
 
-      /* Before we authenticate we check the hostkey's MD5 fingerprint
-       * against a known fingerprint, if available.  This implementation pulls
-       * it from the curl option.
-       */
-      if(data->set.str[STRING_SSH_HOST_PUBLIC_KEY_MD5] &&
-          strlen(data->set.str[STRING_SSH_HOST_PUBLIC_KEY_MD5]) == 32)
-      {
-        char buf[33];
-        host_public_key_md5 = libssh2_hostkey_hash(sshc->ssh_session,
-                                                   LIBSSH2_HOSTKEY_HASH_MD5);
-        for (i = 0; i < 16; i++)
-          snprintf(&buf[i*2], 3, "%02x",
-                   (unsigned char) host_public_key_md5[i]);
-        if(!strequal(buf, data->set.str[STRING_SSH_HOST_PUBLIC_KEY_MD5])) {
-          failf(data,
-                "Denied establishing ssh session: mismatch md5 fingerprint. "
-                "Remote %s is not equal to %s",
-                buf, data->set.str[STRING_SSH_HOST_PUBLIC_KEY_MD5]);
-          state(conn, SSH_SESSION_FREE);
-          sshc->actualCode = CURLE_PEER_FAILED_VERIFICATION;
-          break;
-        }
+    /* Before we authenticate we check the hostkey's MD5 fingerprint
+     * against a known fingerprint, if available.  This implementation pulls
+     * it from the curl option.
+     */
+    if(data->set.str[STRING_SSH_HOST_PUBLIC_KEY_MD5] &&
+       strlen(data->set.str[STRING_SSH_HOST_PUBLIC_KEY_MD5]) == 32) {
+      char buf[33];
+      host_public_key_md5 = libssh2_hostkey_hash(sshc->ssh_session,
+                                                 LIBSSH2_HOSTKEY_HASH_MD5);
+      for (i = 0; i < 16; i++)
+        snprintf(&buf[i*2], 3, "%02x",
+                 (unsigned char) host_public_key_md5[i]);
+      if(!strequal(buf, data->set.str[STRING_SSH_HOST_PUBLIC_KEY_MD5])) {
+        failf(data,
+              "Denied establishing ssh session: mismatch md5 fingerprint. "
+              "Remote %s is not equal to %s",
+              buf, data->set.str[STRING_SSH_HOST_PUBLIC_KEY_MD5]);
+        state(conn, SSH_SESSION_FREE);
+        sshc->actualcode = CURLE_PEER_FAILED_VERIFICATION;
+        break;
       }
+    }
 
-      state(conn, SSH_AUTHLIST);
-      break;
-
-    case SSH_AUTHLIST:
-      /* TBD - methods to check the host keys need to be done */
-
-      /*
-       * Figure out authentication methods
-       * NB: As soon as we have provided a username to an openssh server we
-       * must never change it later. Thus, always specify the correct username
-       * here, even though the libssh2 docs kind of indicate that it should be
-       * possible to get a 'generic' list (not user-specific) of authentication
-       * methods, presumably with a blank username. That won't work in my
-       * experience.
-       * So always specify it here.
-       */
-      sshc->authlist = libssh2_userauth_list(sshc->ssh_session,
-                                             sftp_scp->user,
-                                             strlen(sftp_scp->user));
-
-      if(!sshc->authlist) {
-        if((err = libssh2_session_last_errno(sshc->ssh_session)) ==
-            LIBSSH2_ERROR_EAGAIN) {
-          break;
-        }
-        else {
-          state(conn, SSH_SESSION_FREE);
-          sshc->actualCode = libssh2_session_error_to_CURLE(err);
-          break;
-        }
+    state(conn, SSH_AUTHLIST);
+    break;
+
+  case SSH_AUTHLIST:
+    /* TBD - methods to check the host keys need to be done */
+
+    /*
+     * Figure out authentication methods
+     * NB: As soon as we have provided a username to an openssh server we
+     * must never change it later. Thus, always specify the correct username
+     * here, even though the libssh2 docs kind of indicate that it should be
+     * possible to get a 'generic' list (not user-specific) of authentication
+     * methods, presumably with a blank username. That won't work in my
+     * experience.
+     * So always specify it here.
+     */
+    sshc->authlist = libssh2_userauth_list(sshc->ssh_session,
+                                           conn->user,
+                                           strlen(conn->user));
+
+    if(!sshc->authlist) {
+      if((err = libssh2_session_last_errno(sshc->ssh_session)) ==
+         LIBSSH2_ERROR_EAGAIN) {
+        break;
       }
-      infof(data, "SSH authentication methods available: %s\n", sshc->authlist);
-
-      state(conn, SSH_AUTH_PKEY_INIT);
-      break;
-
-    case SSH_AUTH_PKEY_INIT:
-      /*
-       * Check the supported auth types in the order I feel is most secure
-       * with the requested type of authentication
-       */
-      sshc->authed = FALSE;
+      else {
+        state(conn, SSH_SESSION_FREE);
+        sshc->actualcode = libssh2_session_error_to_CURLE(err);
+        break;
+      }
+    }
+    infof(data, "SSH authentication methods available: %s\n", sshc->authlist);
 
-      if((data->set.ssh_auth_types & CURLSSH_AUTH_PUBLICKEY) &&
-          (strstr(sshc->authlist, "publickey") != NULL)) {
-        char *home;
+    state(conn, SSH_AUTH_PKEY_INIT);
+    break;
 
-        sshc->rsa_pub = sshc->rsa = NULL;
+  case SSH_AUTH_PKEY_INIT:
+    /*
+     * Check the supported auth types in the order I feel is most secure
+     * with the requested type of authentication
+     */
+    sshc->authed = FALSE;
 
-        /* To ponder about: should really the lib be messing about with the
-           HOME environment variable etc? */
-        home = curl_getenv("HOME");
+    if((data->set.ssh_auth_types & CURLSSH_AUTH_PUBLICKEY) &&
+       (strstr(sshc->authlist, "publickey") != NULL)) {
+      char *home;
 
-        if(data->set.str[STRING_SSH_PUBLIC_KEY])
-          sshc->rsa_pub = aprintf("%s", data->set.str[STRING_SSH_PUBLIC_KEY]);
-        else if(home)
-          sshc->rsa_pub = aprintf("%s/.ssh/id_dsa.pub", home);
-        else
-          /* as a final resort, try current dir! */
-          sshc->rsa_pub = strdup("id_dsa.pub");
-
-        if(sshc->rsa_pub == NULL) {
-          Curl_safefree(home);
-          home = NULL;
-          state(conn, SSH_SESSION_FREE);
-          sshc->actualCode = CURLE_OUT_OF_MEMORY;
-          break;
-        }
+      sshc->rsa_pub = sshc->rsa = NULL;
 
-        if(data->set.str[STRING_SSH_PRIVATE_KEY])
-          sshc->rsa = aprintf("%s", data->set.str[STRING_SSH_PRIVATE_KEY]);
-        else if(home)
-          sshc->rsa = aprintf("%s/.ssh/id_dsa", home);
-        else
-          /* as a final resort, try current dir! */
-          sshc->rsa = strdup("id_dsa");
-
-        if(sshc->rsa == NULL) {
-          Curl_safefree(home);
-          home = NULL;
-          Curl_safefree(sshc->rsa_pub);
-          sshc->rsa_pub = NULL;
-          state(conn, SSH_SESSION_FREE);
-          sshc->actualCode = CURLE_OUT_OF_MEMORY;
-          break;
-        }
+      /* To ponder about: should really the lib be messing about with the
+         HOME environment variable etc? */
+      home = curl_getenv("HOME");
 
-        sshc->passphrase = data->set.str[STRING_KEY_PASSWD];
-        if(!sshc->passphrase)
-          sshc->passphrase = "";
+      if(data->set.str[STRING_SSH_PUBLIC_KEY])
+        sshc->rsa_pub = aprintf("%s", data->set.str[STRING_SSH_PUBLIC_KEY]);
+      else if(home)
+        sshc->rsa_pub = aprintf("%s/.ssh/id_dsa.pub", home);
+      else
+        /* as a final resort, try current dir! */
+        sshc->rsa_pub = strdup("id_dsa.pub");
 
+      if(sshc->rsa_pub == NULL) {
         Curl_safefree(home);
         home = NULL;
-
-        infof(data, "Using ssh public key file %s\n", sshc->rsa_pub);
-        infof(data, "Using ssh private key file %s\n", sshc->rsa);
-
-        state(conn, SSH_AUTH_PKEY);
-      } else {
-        state(conn, SSH_AUTH_PASS_INIT);
-      }
-      break;
-
-    case SSH_AUTH_PKEY:
-      /* The function below checks if the files exists, no need to stat() here.
-       */
-      rc = libssh2_userauth_publickey_fromfile(sshc->ssh_session,
-                                               sftp_scp->user, sshc->rsa_pub,
-                                               sshc->rsa, sshc->passphrase);
-      if(rc == LIBSSH2_ERROR_EAGAIN) {
+        state(conn, SSH_SESSION_FREE);
+        sshc->actualcode = CURLE_OUT_OF_MEMORY;
         break;
       }
 
-      Curl_safefree(sshc->rsa_pub);
-      sshc->rsa_pub = NULL;
-      Curl_safefree(sshc->rsa);
-      sshc->rsa = NULL;
-
-      if(rc == 0) {
-        sshc->authed = TRUE;
-        infof(data, "Initialized SSH public key authentication\n");
-        state(conn, SSH_AUTH_DONE);
-      }
-      else {
-        state(conn, SSH_AUTH_PASS_INIT);
-      }
-      break;
+      if(data->set.str[STRING_SSH_PRIVATE_KEY])
+        sshc->rsa = aprintf("%s", data->set.str[STRING_SSH_PRIVATE_KEY]);
+      else if(home)
+        sshc->rsa = aprintf("%s/.ssh/id_dsa", home);
+      else
+        /* as a final resort, try current dir! */
+        sshc->rsa = strdup("id_dsa");
 
-    case SSH_AUTH_PASS_INIT:
-      if((data->set.ssh_auth_types & CURLSSH_AUTH_PASSWORD) &&
-          (strstr(sshc->authlist, "password") != NULL)) {
-        state(conn, SSH_AUTH_PASS);
-      } else {
-        state(conn, SSH_AUTH_HOST_INIT);
-      }
-      break;
-
-    case SSH_AUTH_PASS:
-      rc = libssh2_userauth_password(sshc->ssh_session, sftp_scp->user,
-                                     sftp_scp->passwd);
-      if(rc == LIBSSH2_ERROR_EAGAIN) {
+      if(sshc->rsa == NULL) {
+        Curl_safefree(home);
+        home = NULL;
+        Curl_safefree(sshc->rsa_pub);
+        sshc->rsa_pub = NULL;
+        state(conn, SSH_SESSION_FREE);
+        sshc->actualcode = CURLE_OUT_OF_MEMORY;
         break;
       }
-      else if(rc == 0) {
-        sshc->authed = TRUE;
-        infof(data, "Initialized password authentication\n");
-        state(conn, SSH_AUTH_DONE);
-      }
-      else {
-        state(conn, SSH_AUTH_HOST_INIT);
-      }
-      break;
 
-    case SSH_AUTH_HOST_INIT:
-      if((data->set.ssh_auth_types & CURLSSH_AUTH_HOST) &&
-          (strstr(sshc->authlist, "hostbased") != NULL)) {
-        state(conn, SSH_AUTH_HOST);
-      }
-      else {
-        state(conn, SSH_AUTH_KEY_INIT);
-      }
-      break;
+      sshc->passphrase = data->set.str[STRING_KEY_PASSWD];
+      if(!sshc->passphrase)
+        sshc->passphrase = "";
 
-    case SSH_AUTH_HOST:
-      state(conn, SSH_AUTH_KEY_INIT);
-      break;
+      Curl_safefree(home);
+      home = NULL;
 
-    case SSH_AUTH_KEY_INIT:
-      if((data->set.ssh_auth_types & CURLSSH_AUTH_KEYBOARD)
-          && (strstr(sshc->authlist, "keyboard-interactive") != NULL)) {
-        state(conn, SSH_AUTH_KEY);
-      }
-      else {
-        state(conn, SSH_AUTH_DONE);
-      }
-      break;
+      infof(data, "Using ssh public key file %s\n", sshc->rsa_pub);
+      infof(data, "Using ssh private key file %s\n", sshc->rsa);
 
-    case SSH_AUTH_KEY:
-      /* Authentication failed. Continue with keyboard-interactive now. */
-      rc = libssh2_userauth_keyboard_interactive_ex(sshc->ssh_session,
-                                                    sftp_scp->user,
-                                                    strlen(sftp_scp->user),
-                                                    &kbd_callback);
-      if(rc == LIBSSH2_ERROR_EAGAIN) {
-        break;
-      }
-      else if(rc == 0) {
-        sshc->authed = TRUE;
-        infof(data, "Initialized keyboard interactive authentication\n");
-      }
-      state(conn, SSH_AUTH_DONE);
+      state(conn, SSH_AUTH_PKEY);
+    }
+    else {
+      state(conn, SSH_AUTH_PASS_INIT);
+    }
+    break;
+
+  case SSH_AUTH_PKEY:
+    /* The function below checks if the files exists, no need to stat() here.
+     */
+    rc = libssh2_userauth_publickey_fromfile(sshc->ssh_session,
+                                             conn->user, sshc->rsa_pub,
+                                             sshc->rsa, sshc->passphrase);
+    if(rc == LIBSSH2_ERROR_EAGAIN) {
       break;
+    }
 
-    case SSH_AUTH_DONE:
-      if(!sshc->authed) {
-        failf(data, "Authentication failure");
-        state(conn, SSH_SESSION_FREE);
-        sshc->actualCode = CURLE_LOGIN_DENIED;
-        break;
-      }
+    Curl_safefree(sshc->rsa_pub);
+    sshc->rsa_pub = NULL;
+    Curl_safefree(sshc->rsa);
+    sshc->rsa = NULL;
 
-      /*
-       * At this point we have an authenticated ssh session.
-       */
-      infof(data, "Authentication complete\n");
+    if(rc == 0) {
+      sshc->authed = TRUE;
+      infof(data, "Initialized SSH public key authentication\n");
+      state(conn, SSH_AUTH_DONE);
+    }
+    else {
+      state(conn, SSH_AUTH_PASS_INIT);
+    }
+    break;
 
-      conn->sockfd = sock;
-      conn->writesockfd = CURL_SOCKET_BAD;
+  case SSH_AUTH_PASS_INIT:
+    if((data->set.ssh_auth_types & CURLSSH_AUTH_PASSWORD) &&
+       (strstr(sshc->authlist, "password") != NULL)) {
+      state(conn, SSH_AUTH_PASS);
+    }
+    else {
+      state(conn, SSH_AUTH_HOST_INIT);
+    }
+    break;
 
-      if(conn->protocol == PROT_SFTP) {
-        state(conn, SSH_SFTP_INIT);
-        break;
-      }
-      state(conn, SSH_GET_WORKINGPATH);
+  case SSH_AUTH_PASS:
+    rc = libssh2_userauth_password(sshc->ssh_session, conn->user,
+                                   conn->passwd);
+    if(rc == LIBSSH2_ERROR_EAGAIN) {
       break;
+    }
+    else if(rc == 0) {
+      sshc->authed = TRUE;
+      infof(data, "Initialized password authentication\n");
+      state(conn, SSH_AUTH_DONE);
+    }
+    else {
+      state(conn, SSH_AUTH_HOST_INIT);
+    }
+    break;
 
-    case SSH_SFTP_INIT:
-      /*
-       * Start the libssh2 sftp session
-       */
-      sshc->sftp_session = libssh2_sftp_init(sshc->ssh_session);
-      if(!sshc->sftp_session) {
-        if(libssh2_session_last_errno(sshc->ssh_session) ==
-            LIBSSH2_ERROR_EAGAIN) {
-          break;
-        }
-        else {
-          failf(data, "Failure initialising sftp session\n");
-          state(conn, SSH_SESSION_FREE);
-          sshc->actualCode = CURLE_FAILED_INIT;
-          break;
-        }
-      }
-      state(conn, SSH_SFTP_REALPATH);
-      break;
+  case SSH_AUTH_HOST_INIT:
+    if((data->set.ssh_auth_types & CURLSSH_AUTH_HOST) &&
+       (strstr(sshc->authlist, "hostbased") != NULL)) {
+      state(conn, SSH_AUTH_HOST);
+    }
+    else {
+      state(conn, SSH_AUTH_KEY_INIT);
+    }
+    break;
 
-    case SSH_SFTP_REALPATH:
-      {
-        char tempHome[PATH_MAX];
+  case SSH_AUTH_HOST:
+    state(conn, SSH_AUTH_KEY_INIT);
+    break;
 
-        /*
-         * Get the "home" directory
-         */
-        rc = libssh2_sftp_realpath(sshc->sftp_session, ".",
-                                   tempHome, PATH_MAX-1);
-        if(rc == LIBSSH2_ERROR_EAGAIN) {
-          break;
-        }
-        else if(rc > 0) {
-          /* It seems that this string is not always NULL terminated */
-          tempHome[rc] = '\0';
-          sftp_scp->homedir = (char *)strdup(tempHome);
-          if(!sftp_scp->homedir) {
-            state(conn, SSH_SFTP_CLOSE);
-            sshc->actualCode = CURLE_OUT_OF_MEMORY;
-            break;
-          }
-        }
-        else {
-          /* Return the error type */
-          result = libssh2_sftp_last_error(sshc->sftp_session);
-          DEBUGF(infof(data, "error = %d\n", result));
-          state(conn, SSH_STOP);
-          break;
-        }
-        state(conn, SSH_GET_WORKINGPATH);
-      }
+  case SSH_AUTH_KEY_INIT:
+    if((data->set.ssh_auth_types & CURLSSH_AUTH_KEYBOARD)
+       && (strstr(sshc->authlist, "keyboard-interactive") != NULL)) {
+      state(conn, SSH_AUTH_KEY);
+    }
+    else {
+      state(conn, SSH_AUTH_DONE);
+    }
+    break;
+
+  case SSH_AUTH_KEY:
+    /* Authentication failed. Continue with keyboard-interactive now. */
+    rc = libssh2_userauth_keyboard_interactive_ex(sshc->ssh_session,
+                                                  conn->user,
+                                                  strlen(conn->user),
+                                                  &kbd_callback);
+    if(rc == LIBSSH2_ERROR_EAGAIN) {
       break;
+    }
+    else if(rc == 0) {
+      sshc->authed = TRUE;
+      infof(data, "Initialized keyboard interactive authentication\n");
+    }
+    state(conn, SSH_AUTH_DONE);
+    break;
 
-    case SSH_GET_WORKINGPATH:
-      {
-        char *real_path;
-        char *working_path;
-        int working_path_len;
-
-        working_path = curl_easy_unescape(data, data->reqdata.path, 0,
-                                          &working_path_len);
-        if(!working_path) {
-          result = CURLE_OUT_OF_MEMORY;
-          state(conn, SSH_STOP);
-          break;
-        }
+  case SSH_AUTH_DONE:
+    if(!sshc->authed) {
+      failf(data, "Authentication failure");
+      state(conn, SSH_SESSION_FREE);
+      sshc->actualcode = CURLE_LOGIN_DENIED;
+      break;
+    }
 
-        /* Check for /~/ , indicating relative to the user's home directory */
-        if(conn->protocol == PROT_SCP) {
-          real_path = (char *)malloc(working_path_len+1);
-          if(real_path == NULL) {
-            Curl_safefree(working_path);
-            working_path = NULL;
-            state(conn, SSH_SESSION_FREE);
-            sshc->actualCode = CURLE_OUT_OF_MEMORY;
-            break;
-          }
-          if(working_path[1] == '~')
-            /* It is referenced to the home directory, so strip the
-               leading '/' */
-            memcpy(real_path, working_path+1, 1 + working_path_len-1);
-          else
-            memcpy(real_path, working_path, 1 + working_path_len);
-        }
-        else if(conn->protocol == PROT_SFTP) {
-          if(working_path[1] == '~') {
-            real_path = (char *)malloc(strlen(sftp_scp->homedir) +
-                                       working_path_len + 1);
-            if(real_path == NULL) {
-              Curl_safefree(sftp_scp->homedir);
-              sftp_scp->homedir = NULL;
-              Curl_safefree(working_path);
-              working_path = NULL;
-              state(conn, SSH_SFTP_CLOSE);
-              sshc->actualCode = CURLE_OUT_OF_MEMORY;
-              break;
-            }
-            /* It is referenced to the home directory, so strip the
-               leading '/' */
-            memcpy(real_path, sftp_scp->homedir, strlen(sftp_scp->homedir));
-            real_path[strlen(sftp_scp->homedir)] = '/';
-            real_path[strlen(sftp_scp->homedir)+1] = '\0';
-            if(working_path_len > 3) {
-              memcpy(real_path+strlen(sftp_scp->homedir)+1, working_path + 3,
-                     1 + working_path_len -3);
-            }
-          }
-          else {
-            real_path = (char *)malloc(working_path_len+1);
-            if(real_path == NULL) {
-              Curl_safefree(sftp_scp->homedir);
-              sftp_scp->homedir = NULL;
-              Curl_safefree(working_path);
-              working_path = NULL;
-              state(conn, SSH_SFTP_CLOSE);
-              sshc->actualCode = CURLE_OUT_OF_MEMORY;
-              break;
-            }
-            memcpy(real_path, working_path, 1+working_path_len);
-          }
-        }
-        else {
-          Curl_safefree(working_path);
-          working_path = NULL;
-          state(conn, SSH_SESSION_FREE);
-          sshc->actualCode = CURLE_FAILED_INIT;
-          break;
-        }
+    /*
+     * At this point we have an authenticated ssh session.
+     */
+    infof(data, "Authentication complete\n");
 
-        Curl_safefree(working_path);
-        working_path = NULL;
-        sftp_scp->path = real_path;
+    conn->sockfd = sock;
+    conn->writesockfd = CURL_SOCKET_BAD;
 
-        /* Connect is all done */
-        state(conn, SSH_STOP);
-      }
+    if(conn->protocol == PROT_SFTP) {
+      state(conn, SSH_SFTP_INIT);
       break;
-
-    case SSH_SFTP_QUOTE_INIT:
-      if(data->set.quote) {
-        infof(data, "Sending quote commands\n");
-        sshc->quote_item = data->set.quote;
-        state(conn, SSH_SFTP_QUOTE);
+    }
+    infof(data, "SSH CONNECT phase done\n");
+    state(conn, SSH_STOP);
+    break;
+
+  case SSH_SFTP_INIT:
+    /*
+     * Start the libssh2 sftp session
+     */
+    sshc->sftp_session = libssh2_sftp_init(sshc->ssh_session);
+    if(!sshc->sftp_session) {
+      if(libssh2_session_last_errno(sshc->ssh_session) ==
+         LIBSSH2_ERROR_EAGAIN) {
+        break;
       }
       else {
-        state(conn, SSH_SFTP_TRANS_INIT);
+        failf(data, "Failure initialising sftp session\n");
+        state(conn, SSH_SESSION_FREE);
+        sshc->actualcode = CURLE_FAILED_INIT;
+        break;
       }
+    }
+    state(conn, SSH_SFTP_REALPATH);
+    break;
+
+  case SSH_SFTP_REALPATH:
+  {
+    char tempHome[PATH_MAX];
+
+    /*
+     * Get the "home" directory
+     */
+    rc = libssh2_sftp_realpath(sshc->sftp_session, ".",
+                               tempHome, PATH_MAX-1);
+    if(rc == LIBSSH2_ERROR_EAGAIN) {
       break;
-
-    case SSH_SFTP_POSTQUOTE_INIT:
-      if(data->set.postquote) {
-        infof(data, "Sending quote commands\n");
-        sshc->quote_item = data->set.postquote;
-        state(conn, SSH_SFTP_QUOTE);
-      }
-      else {
-        state(conn, SSH_STOP);
+    }
+    else if(rc > 0) {
+      /* It seems that this string is not always NULL terminated */
+      tempHome[rc] = '\0';
+      sshc->homedir = (char *)strdup(tempHome);
+      if(!sshc->homedir) {
+        state(conn, SSH_SFTP_CLOSE);
+        sshc->actualcode = CURLE_OUT_OF_MEMORY;
+        break;
       }
+    }
+    else {
+      /* Return the error type */
+      err = libssh2_sftp_last_error(sshc->sftp_session);
+      result = sftp_libssh2_error_to_CURLE(err);
+      DEBUGF(infof(data, "error = %d makes libcurl = %d\n", err, result));
+      state(conn, SSH_STOP);
       break;
+    }
+  }
+  /* This is the last step in the SFTP connect phase. Do note that while
+     we get the homedir here, we get the "workingpath" in the DO action
+     since the homedir will remain the same between request but the
+     working path will not. */
+  DEBUGF(infof(data, "SSH CONNECT phase done\n"));
+  state(conn, SSH_STOP);
+  break;
 
-    case SSH_SFTP_QUOTE:
-      /* Send any quote commands */
-      {
-        const char *cp;
-
-        /*
-         * Support some of the "FTP" commands
-         */
-        if(curl_strnequal(sshc->quote_item->data, "PWD", 3)) {
-          /* output debug output if that is requested */
-          if(data->set.verbose) {
-            char tmp[PATH_MAX+1];
-
-            Curl_debug(data, CURLINFO_HEADER_OUT, (char *)"PWD\n", 4, conn);
-            snprintf(tmp, PATH_MAX, "257 \"%s\" is current directory.\n",
-                     sftp_scp->path);
-            Curl_debug(data, CURLINFO_HEADER_IN, tmp, strlen(tmp), conn);
-          }
-          state(conn, SSH_SFTP_NEXT_QUOTE);
-          break;
-        }
-        else if(sshc->quote_item->data) {
-          fprintf(stderr, "data: %s\n", sshc->quote_item->data);
-          /*
-           * the arguments following the command must be separated from the
-           * command with a space so we can check for it unconditionally
-           */
-          cp = strchr(sshc->quote_item->data, ' ');
-          if(cp == NULL) {
-            failf(data, "Syntax error in SFTP command. Supply parameter(s)!");
-            state(conn, SSH_SFTP_CLOSE);
-            sshc->actualCode = CURLE_QUOTE_ERROR;
-            break;
-          }
+  case SSH_SFTP_QUOTE_INIT:
 
-          /*
-           * also, every command takes at least one argument so we get that
-           * first argument right now
-           */
-          err = get_pathname(&cp, &sshc->quote_path1);
-          if(err) {
-            if(err == CURLE_OUT_OF_MEMORY)
-              failf(data, "Out of memory");
-            else
-              failf(data, "Syntax error: Bad first parameter");
-            state(conn, SSH_SFTP_CLOSE);
-            sshc->actualCode = err;
-            break;
-          }
+    result = ssh_getworkingpath(conn, sshc->homedir, &sftp_scp->path);
+    if(result) {
+      sshc->actualcode = result;
+      state(conn, SSH_STOP);
+      break;
+    }
 
-          /*
-           * SFTP is a binary protocol, so we don't send text commands to
-           * the server. Instead, we scan for commands for commands used by
-           * OpenSSH's sftp program and call the appropriate libssh2
-           * functions.
-           */
-          if(curl_strnequal(sshc->quote_item->data, "chgrp ", 6) ||
-              curl_strnequal(sshc->quote_item->data, "chmod ", 6) ||
-              curl_strnequal(sshc->quote_item->data, "chown ", 6) ) {
-            /* attribute change */
-
-            /* sshc->quote_path1 contains the mode to set */
-            /* get the destination */
-            err = get_pathname(&cp, &sshc->quote_path2);
-            if(err) {
-              if(err == CURLE_OUT_OF_MEMORY)
-                failf(data, "Out of memory");
-              else
-                failf(data, "Syntax error in chgrp/chmod/chown: "
-                      "Bad second parameter");
-              Curl_safefree(sshc->quote_path1);
-              sshc->quote_path1 = NULL;
-              state(conn, SSH_SFTP_CLOSE);
-              sshc->actualCode = err;
-              break;
-            }
-            memset(&sshc->quote_attrs, 0, sizeof(LIBSSH2_SFTP_ATTRIBUTES));
-            state(conn, SSH_SFTP_QUOTE_STAT);
-            break;
-          }
-          else if(curl_strnequal(sshc->quote_item->data, "ln ", 3) ||
-                   curl_strnequal(sshc->quote_item->data, "symlink ", 8)) {
-            /* symbolic linking */
-            /* sshc->quote_path1 is the source */
-            /* get the destination */
-            err = get_pathname(&cp, &sshc->quote_path2);
-            if(err) {
-              if(err == CURLE_OUT_OF_MEMORY)
-                failf(data, "Out of memory");
-              else
-                failf(data,
-                      "Syntax error in ln/symlink: Bad second parameter");
-              Curl_safefree(sshc->quote_path1);
-              sshc->quote_path1 = NULL;
-              state(conn, SSH_SFTP_CLOSE);
-              sshc->actualCode = err;
-              break;
-            }
-            state(conn, SSH_SFTP_QUOTE_SYMLINK);
-            break;
-          }
-          else if(curl_strnequal(sshc->quote_item->data, "mkdir ", 6)) {
-            /* create dir */
-            state(conn, SSH_SFTP_QUOTE_MKDIR);
-            break;
-          }
-          else if(curl_strnequal(sshc->quote_item->data, "rename ", 7)) {
-            /* rename file */
-            /* first param is the source path */
-            /* second param is the dest. path */
-            err = get_pathname(&cp, &sshc->quote_path2);
-            if(err) {
-              if(err == CURLE_OUT_OF_MEMORY)
-                failf(data, "Out of memory");
-              else
-                failf(data, "Syntax error in rename: Bad second parameter");
-              Curl_safefree(sshc->quote_path1);
-              sshc->quote_path1 = NULL;
-              state(conn, SSH_SFTP_CLOSE);
-              sshc->actualCode = err;
-              break;
-            }
-            state(conn, SSH_SFTP_QUOTE_RENAME);
-            break;
-          }
-          else if(curl_strnequal(sshc->quote_item->data, "rmdir ", 6)) {
-            /* delete dir */
-            state(conn, SSH_SFTP_QUOTE_RMDIR);
-            break;
-          }
-          else if(curl_strnequal(sshc->quote_item->data, "rm ", 3)) {
-            state(conn, SSH_SFTP_QUOTE_UNLINK);
-            break;
-          }
+    if(data->set.quote) {
+      infof(data, "Sending quote commands\n");
+      sshc->quote_item = data->set.quote;
+      state(conn, SSH_SFTP_QUOTE);
+    }
+    else {
+      state(conn, SSH_SFTP_TRANS_INIT);
+    }
+    break;
 
-          if(sshc->quote_path1) {
-            Curl_safefree(sshc->quote_path1);
-            sshc->quote_path1 = NULL;
-          }
-          if(sshc->quote_path2) {
-            Curl_safefree(sshc->quote_path2);
-            sshc->quote_path2 = NULL;
-          }
-        }
-      }
-      if(!sshc->quote_item) {
-        state(conn, SSH_SFTP_TRANS_INIT);
-      }
-      break;
+  case SSH_SFTP_POSTQUOTE_INIT:
+    if(data->set.postquote) {
+      infof(data, "Sending quote commands\n");
+      sshc->quote_item = data->set.postquote;
+      state(conn, SSH_SFTP_QUOTE);
+    }
+    else {
+      state(conn, SSH_STOP);
+    }
+    break;
 
-    case SSH_SFTP_NEXT_QUOTE:
-      if(sshc->quote_path1) {
-        Curl_safefree(sshc->quote_path1);
-        sshc->quote_path1 = NULL;
-      }
-      if(sshc->quote_path2) {
-        Curl_safefree(sshc->quote_path2);
-        sshc->quote_path2 = NULL;
-      }
+  case SSH_SFTP_QUOTE:
+    /* Send any quote commands */
+  {
+    const char *cp;
 
-      sshc->quote_item = sshc->quote_item->next;
+    /*
+     * Support some of the "FTP" commands
+     */
+    if(curl_strnequal(sshc->quote_item->data, "PWD", 3)) {
+      /* output debug output if that is requested */
+      if(data->set.verbose) {
+        char tmp[PATH_MAX+1];
 
-      if(sshc->quote_item) {
-        state(conn, SSH_SFTP_QUOTE);
-      } else {
-        if(sshc->nextState != SSH_NO_STATE) {
-          state(conn, sshc->nextState);
-          sshc->nextState = SSH_NO_STATE;
-        } else {
-          state(conn, SSH_SFTP_TRANS_INIT);
-        }
+        Curl_debug(data, CURLINFO_HEADER_OUT, (char *)"PWD\n", 4, conn);
+        snprintf(tmp, PATH_MAX, "257 \"%s\" is current directory.\n",
+                 sftp_scp->path);
+        Curl_debug(data, CURLINFO_HEADER_IN, tmp, strlen(tmp), conn);
       }
+      state(conn, SSH_SFTP_NEXT_QUOTE);
       break;
-
-    case SSH_SFTP_QUOTE_STAT:
-      rc = libssh2_sftp_stat(sshc->sftp_session, sshc->quote_path2,
-                             &sshc->quote_attrs);
-      if(rc == LIBSSH2_ERROR_EAGAIN) {
+    }
+    else if(sshc->quote_item->data) {
+      fprintf(stderr, "data: %s\n", sshc->quote_item->data);
+      /*
+       * the arguments following the command must be separated from the
+       * command with a space so we can check for it unconditionally
+       */
+      cp = strchr(sshc->quote_item->data, ' ');
+      if(cp == NULL) {
+        failf(data, "Syntax error in SFTP command. Supply parameter(s)!");
+        state(conn, SSH_SFTP_CLOSE);
+        sshc->actualcode = CURLE_QUOTE_ERROR;
         break;
       }
-      else if(rc != 0) { /* get those attributes */
-        err = libssh2_sftp_last_error(sshc->sftp_session);
-        Curl_safefree(sshc->quote_path1);
-        sshc->quote_path1 = NULL;
-        Curl_safefree(sshc->quote_path2);
-        sshc->quote_path2 = NULL;
-        failf(data, "Attempt to get SFTP stats failed: %s",
-              sftp_libssh2_strerror(err));
+
+      /*
+       * also, every command takes at least one argument so we get that
+       * first argument right now
+       */
+      err = get_pathname(&cp, &sshc->quote_path1);
+      if(err) {
+        if(err == CURLE_OUT_OF_MEMORY)
+          failf(data, "Out of memory");
+        else
+          failf(data, "Syntax error: Bad first parameter");
         state(conn, SSH_SFTP_CLOSE);
-        sshc->actualCode = CURLE_QUOTE_ERROR;
+        sshc->actualcode = err;
         break;
       }
 
-      /* Now set the new attributes... */
-      if(curl_strnequal(sshc->quote_item->data, "chgrp", 5)) {
-        sshc->quote_attrs.gid = strtol(sshc->quote_path1, NULL, 10);
-        if(sshc->quote_attrs.gid == 0 && !ISDIGIT(sshc->quote_path1[0])) {
+      /*
+       * SFTP is a binary protocol, so we don't send text commands to
+       * the server. Instead, we scan for commands for commands used by
+       * OpenSSH's sftp program and call the appropriate libssh2
+       * functions.
+       */
+      if(curl_strnequal(sshc->quote_item->data, "chgrp ", 6) ||
+         curl_strnequal(sshc->quote_item->data, "chmod ", 6) ||
+         curl_strnequal(sshc->quote_item->data, "chown ", 6) ) {
+        /* attribute change */
+
+        /* sshc->quote_path1 contains the mode to set */
+        /* get the destination */
+        err = get_pathname(&cp, &sshc->quote_path2);
+        if(err) {
+          if(err == CURLE_OUT_OF_MEMORY)
+            failf(data, "Out of memory");
+          else
+            failf(data, "Syntax error in chgrp/chmod/chown: "
+                  "Bad second parameter");
           Curl_safefree(sshc->quote_path1);
           sshc->quote_path1 = NULL;
-          Curl_safefree(sshc->quote_path2);
-          sshc->quote_path2 = NULL;
-          failf(data, "Syntax error: chgrp gid not a number");
           state(conn, SSH_SFTP_CLOSE);
-          sshc->actualCode = CURLE_QUOTE_ERROR;
+          sshc->actualcode = err;
           break;
         }
+        memset(&sshc->quote_attrs, 0, sizeof(LIBSSH2_SFTP_ATTRIBUTES));
+        state(conn, SSH_SFTP_QUOTE_STAT);
+        break;
       }
-      else if(curl_strnequal(sshc->quote_item->data, "chmod", 5)) {
-        sshc->quote_attrs.permissions = strtol(sshc->quote_path1, NULL, 8);
-        /* permissions are octal */
-        if(sshc->quote_attrs.permissions == 0 &&
-            !ISDIGIT(sshc->quote_path1[0])) {
+      else if(curl_strnequal(sshc->quote_item->data, "ln ", 3) ||
+              curl_strnequal(sshc->quote_item->data, "symlink ", 8)) {
+        /* symbolic linking */
+        /* sshc->quote_path1 is the source */
+        /* get the destination */
+        err = get_pathname(&cp, &sshc->quote_path2);
+        if(err) {
+          if(err == CURLE_OUT_OF_MEMORY)
+            failf(data, "Out of memory");
+          else
+            failf(data,
+                  "Syntax error in ln/symlink: Bad second parameter");
           Curl_safefree(sshc->quote_path1);
           sshc->quote_path1 = NULL;
-          Curl_safefree(sshc->quote_path2);
-          sshc->quote_path2 = NULL;
-          failf(data, "Syntax error: chmod permissions not a number");
           state(conn, SSH_SFTP_CLOSE);
-          sshc->actualCode = CURLE_QUOTE_ERROR;
+          sshc->actualcode = err;
           break;
         }
+        state(conn, SSH_SFTP_QUOTE_SYMLINK);
+        break;
+      }
+      else if(curl_strnequal(sshc->quote_item->data, "mkdir ", 6)) {
+        /* create dir */
+        state(conn, SSH_SFTP_QUOTE_MKDIR);
+        break;
       }
-      else if(curl_strnequal(sshc->quote_item->data, "chown", 5)) {
-        sshc->quote_attrs.uid = strtol(sshc->quote_path1, NULL, 10);
-        if(sshc->quote_attrs.uid == 0 && !ISDIGIT(sshc->quote_path1[0])) {
+      else if(curl_strnequal(sshc->quote_item->data, "rename ", 7)) {
+        /* rename file */
+        /* first param is the source path */
+        /* second param is the dest. path */
+        err = get_pathname(&cp, &sshc->quote_path2);
+        if(err) {
+          if(err == CURLE_OUT_OF_MEMORY)
+            failf(data, "Out of memory");
+          else
+            failf(data, "Syntax error in rename: Bad second parameter");
           Curl_safefree(sshc->quote_path1);
           sshc->quote_path1 = NULL;
-          Curl_safefree(sshc->quote_path2);
-          sshc->quote_path2 = NULL;
-          failf(data, "Syntax error: chown uid not a number");
           state(conn, SSH_SFTP_CLOSE);
-          sshc->actualCode = CURLE_QUOTE_ERROR;
+          sshc->actualcode = err;
           break;
         }
-      }
-
-      /* Now send the completed structure... */
-      state(conn, SSH_SFTP_QUOTE_SETSTAT);
-      break;
-
-    case SSH_SFTP_QUOTE_SETSTAT:
-      rc = libssh2_sftp_setstat(sshc->sftp_session, sshc->quote_path2,
-                                &sshc->quote_attrs);
-      if(rc == LIBSSH2_ERROR_EAGAIN) {
+        state(conn, SSH_SFTP_QUOTE_RENAME);
         break;
-      } else if(rc != 0) {
-        err = libssh2_sftp_last_error(sshc->sftp_session);
-        Curl_safefree(sshc->quote_path1);
-        sshc->quote_path1 = NULL;
-        Curl_safefree(sshc->quote_path2);
-        sshc->quote_path2 = NULL;
-        failf(data, "Attempt to set SFTP stats failed: %s",
-              sftp_libssh2_strerror(err));
-        state(conn, SSH_SFTP_CLOSE);
-        sshc->actualCode = CURLE_QUOTE_ERROR;
+      }
+      else if(curl_strnequal(sshc->quote_item->data, "rmdir ", 6)) {
+        /* delete dir */
+        state(conn, SSH_SFTP_QUOTE_RMDIR);
         break;
       }
-      state(conn, SSH_SFTP_NEXT_QUOTE);
-      break;
-
-    case SSH_SFTP_QUOTE_SYMLINK:
-      rc = libssh2_sftp_symlink(sshc->sftp_session, sshc->quote_path1,
-                                sshc->quote_path2);
-      if(rc == LIBSSH2_ERROR_EAGAIN) {
+      else if(curl_strnequal(sshc->quote_item->data, "rm ", 3)) {
+        state(conn, SSH_SFTP_QUOTE_UNLINK);
         break;
       }
-      else if(rc != 0) {
-        err = libssh2_sftp_last_error(sshc->sftp_session);
+
+      if(sshc->quote_path1) {
         Curl_safefree(sshc->quote_path1);
         sshc->quote_path1 = NULL;
+      }
+      if(sshc->quote_path2) {
         Curl_safefree(sshc->quote_path2);
         sshc->quote_path2 = NULL;
-        failf(data, "symlink command failed: %s",
-              sftp_libssh2_strerror(err));
-        state(conn, SSH_SFTP_CLOSE);
-        sshc->actualCode = CURLE_QUOTE_ERROR;
-        break;
       }
-      state(conn, SSH_SFTP_NEXT_QUOTE);
-      break;
+    }
+  }
+  if(!sshc->quote_item) {
+    state(conn, SSH_SFTP_TRANS_INIT);
+  }
+  break;
 
-    case SSH_SFTP_QUOTE_MKDIR:
-      rc = libssh2_sftp_mkdir(sshc->sftp_session, sshc->quote_path1, 0755);
-      if(rc == LIBSSH2_ERROR_EAGAIN) {
-        break;
+  case SSH_SFTP_NEXT_QUOTE:
+    if(sshc->quote_path1) {
+      Curl_safefree(sshc->quote_path1);
+      sshc->quote_path1 = NULL;
+    }
+    if(sshc->quote_path2) {
+      Curl_safefree(sshc->quote_path2);
+      sshc->quote_path2 = NULL;
+    }
+
+    sshc->quote_item = sshc->quote_item->next;
+
+    if(sshc->quote_item) {
+      state(conn, SSH_SFTP_QUOTE);
+    }
+    else {
+      if(sshc->nextstate != SSH_NO_STATE) {
+        state(conn, sshc->nextstate);
+        sshc->nextstate = SSH_NO_STATE;
       }
-      else if(rc != 0) {
-        err = libssh2_sftp_last_error(sshc->sftp_session);
-        Curl_safefree(sshc->quote_path1);
-        sshc->quote_path1 = NULL;
-        failf(data, "mkdir command failed: %s", sftp_libssh2_strerror(err));
-        state(conn, SSH_SFTP_CLOSE);
-        sshc->actualCode = CURLE_QUOTE_ERROR;
-        break;
+      else {
+        state(conn, SSH_SFTP_TRANS_INIT);
       }
-      state(conn, SSH_SFTP_NEXT_QUOTE);
+    }
+    break;
+
+  case SSH_SFTP_QUOTE_STAT:
+    rc = libssh2_sftp_stat(sshc->sftp_session, sshc->quote_path2,
+                           &sshc->quote_attrs);
+    if(rc == LIBSSH2_ERROR_EAGAIN) {
+      break;
+    }
+    else if(rc != 0) { /* get those attributes */
+      err = libssh2_sftp_last_error(sshc->sftp_session);
+      Curl_safefree(sshc->quote_path1);
+      sshc->quote_path1 = NULL;
+      Curl_safefree(sshc->quote_path2);
+      sshc->quote_path2 = NULL;
+      failf(data, "Attempt to get SFTP stats failed: %s",
+            sftp_libssh2_strerror(err));
+      state(conn, SSH_SFTP_CLOSE);
+      sshc->actualcode = CURLE_QUOTE_ERROR;
       break;
+    }
 
-    case SSH_SFTP_QUOTE_RENAME:
-      rc = libssh2_sftp_rename(sshc->sftp_session, sshc->quote_path1,
-                               sshc->quote_path2);
-      if(rc == LIBSSH2_ERROR_EAGAIN) {
-        break;
-      }
-      else if(rc != 0) {
-        err = libssh2_sftp_last_error(sshc->sftp_session);
+    /* Now set the new attributes... */
+    if(curl_strnequal(sshc->quote_item->data, "chgrp", 5)) {
+      sshc->quote_attrs.gid = strtol(sshc->quote_path1, NULL, 10);
+      if(sshc->quote_attrs.gid == 0 && !ISDIGIT(sshc->quote_path1[0])) {
         Curl_safefree(sshc->quote_path1);
         sshc->quote_path1 = NULL;
         Curl_safefree(sshc->quote_path2);
         sshc->quote_path2 = NULL;
-        failf(data, "rename command failed: %s", sftp_libssh2_strerror(err));
+        failf(data, "Syntax error: chgrp gid not a number");
         state(conn, SSH_SFTP_CLOSE);
-        sshc->actualCode = CURLE_QUOTE_ERROR;
+        sshc->actualcode = CURLE_QUOTE_ERROR;
         break;
       }
-      state(conn, SSH_SFTP_NEXT_QUOTE);
-      break;
-
-    case SSH_SFTP_QUOTE_RMDIR:
-      rc = libssh2_sftp_rmdir(sshc->sftp_session, sshc->quote_path1);
-      if(rc == LIBSSH2_ERROR_EAGAIN) {
-        break;
-      }
-      else if(rc != 0) {
-        err = libssh2_sftp_last_error(sshc->sftp_session);
+    }
+    else if(curl_strnequal(sshc->quote_item->data, "chmod", 5)) {
+      sshc->quote_attrs.permissions = strtol(sshc->quote_path1, NULL, 8);
+      /* permissions are octal */
+      if(sshc->quote_attrs.permissions == 0 &&
+         !ISDIGIT(sshc->quote_path1[0])) {
         Curl_safefree(sshc->quote_path1);
         sshc->quote_path1 = NULL;
-        failf(data, "rmdir command failed: %s", sftp_libssh2_strerror(err));
+        Curl_safefree(sshc->quote_path2);
+        sshc->quote_path2 = NULL;
+        failf(data, "Syntax error: chmod permissions not a number");
         state(conn, SSH_SFTP_CLOSE);
-        sshc->actualCode = CURLE_QUOTE_ERROR;
-        break;
-      }
-      state(conn, SSH_SFTP_NEXT_QUOTE);
-      break;
-
-    case SSH_SFTP_QUOTE_UNLINK:
-      rc = libssh2_sftp_unlink(sshc->sftp_session, sshc->quote_path1);
-      if(rc == LIBSSH2_ERROR_EAGAIN) {
+        sshc->actualcode = CURLE_QUOTE_ERROR;
         break;
       }
-      else if(rc != 0) {
-        err = libssh2_sftp_last_error(sshc->sftp_session);
+    }
+    else if(curl_strnequal(sshc->quote_item->data, "chown", 5)) {
+      sshc->quote_attrs.uid = strtol(sshc->quote_path1, NULL, 10);
+      if(sshc->quote_attrs.uid == 0 && !ISDIGIT(sshc->quote_path1[0])) {
         Curl_safefree(sshc->quote_path1);
         sshc->quote_path1 = NULL;
-        failf(data, "rm command failed: %s", sftp_libssh2_strerror(err));
+        Curl_safefree(sshc->quote_path2);
+        sshc->quote_path2 = NULL;
+        failf(data, "Syntax error: chown uid not a number");
         state(conn, SSH_SFTP_CLOSE);
-        sshc->actualCode = CURLE_QUOTE_ERROR;
+        sshc->actualcode = CURLE_QUOTE_ERROR;
         break;
       }
-      state(conn, SSH_SFTP_NEXT_QUOTE);
-      break;
+    }
 
-    case SSH_SFTP_TRANS_INIT:
-      if(data->set.upload)
-        state(conn, SSH_SFTP_UPLOAD_INIT);
-      else {
-        if(sftp_scp->path[strlen(sftp_scp->path)-1] == '/')
-          state(conn, SSH_SFTP_READDIR_INIT);
-        else
-          state(conn, SSH_SFTP_DOWNLOAD_INIT);
-      }
-      break;
+    /* Now send the completed structure... */
+    state(conn, SSH_SFTP_QUOTE_SETSTAT);
+    break;
 
-    case SSH_SFTP_UPLOAD_INIT:
-      /*
-       * NOTE!!!  libssh2 requires that the destination path is a full path
-       *          that includes the destination file and name OR ends in a "/"
-       *          If this is not done the destination file will be named the
-       *          same name as the last directory in the path.
-       */
-      sshc->sftp_handle =
-        libssh2_sftp_open(sshc->sftp_session, sftp_scp->path,
-                          LIBSSH2_FXF_WRITE|LIBSSH2_FXF_CREAT|LIBSSH2_FXF_TRUNC,
-                          data->set.new_file_perms);
-      if(!sshc->sftp_handle) {
-        if(libssh2_session_last_errno(sshc->ssh_session) ==
-            LIBSSH2_ERROR_EAGAIN) {
-          break;
-        }
-        else {
-          err = libssh2_sftp_last_error(sshc->sftp_session);
-          failf(data, "Upload failed: %s", sftp_libssh2_strerror(err));
-          if(sshc->secondCreateDirs) {
-            state(conn, SSH_SFTP_CLOSE);
-            sshc->actualCode = err;
-            break;
-          }
-          else if(((err == LIBSSH2_FX_NO_SUCH_FILE) ||
-                    (err == LIBSSH2_FX_FAILURE) ||
-                    (err == LIBSSH2_FX_NO_SUCH_PATH)) &&
-                   (data->set.ftp_create_missing_dirs &&
-                    (strlen(sftp_scp->path) > 1))) {
-            /* try to create the path remotely */
-            sshc->secondCreateDirs = 1;
-            state(conn, SSH_SFTP_CREATE_DIRS_INIT);
-            break;
-          }
-          state(conn, SSH_SFTP_CLOSE);
-          sshc->actualCode = sftp_libssh2_error_to_CURLE(err);
-          break;
-        }
-      }
+  case SSH_SFTP_QUOTE_SETSTAT:
+    rc = libssh2_sftp_setstat(sshc->sftp_session, sshc->quote_path2,
+                              &sshc->quote_attrs);
+    if(rc == LIBSSH2_ERROR_EAGAIN) {
+      break;
+    }
+    else if(rc != 0) {
+      err = libssh2_sftp_last_error(sshc->sftp_session);
+      Curl_safefree(sshc->quote_path1);
+      sshc->quote_path1 = NULL;
+      Curl_safefree(sshc->quote_path2);
+      sshc->quote_path2 = NULL;
+      failf(data, "Attempt to set SFTP stats failed: %s",
+            sftp_libssh2_strerror(err));
+      state(conn, SSH_SFTP_CLOSE);
+      sshc->actualcode = CURLE_QUOTE_ERROR;
+      break;
+    }
+    state(conn, SSH_SFTP_NEXT_QUOTE);
+    break;
 
-      /* upload data */
-      result = Curl_setup_transfer(conn, -1, -1, FALSE, NULL,
-                                   FIRSTSOCKET, NULL);
+  case SSH_SFTP_QUOTE_SYMLINK:
+    rc = libssh2_sftp_symlink(sshc->sftp_session, sshc->quote_path1,
+                              sshc->quote_path2);
+    if(rc == LIBSSH2_ERROR_EAGAIN) {
+      break;
+    }
+    else if(rc != 0) {
+      err = libssh2_sftp_last_error(sshc->sftp_session);
+      Curl_safefree(sshc->quote_path1);
+      sshc->quote_path1 = NULL;
+      Curl_safefree(sshc->quote_path2);
+      sshc->quote_path2 = NULL;
+      failf(data, "symlink command failed: %s",
+            sftp_libssh2_strerror(err));
+      state(conn, SSH_SFTP_CLOSE);
+      sshc->actualcode = CURLE_QUOTE_ERROR;
+      break;
+    }
+    state(conn, SSH_SFTP_NEXT_QUOTE);
+    break;
 
-      if(result) {
-        state(conn, SSH_SFTP_CLOSE);
-        sshc->actualCode = result;
-      } else {
-        state(conn, SSH_STOP);
-      }
+  case SSH_SFTP_QUOTE_MKDIR:
+    rc = libssh2_sftp_mkdir(sshc->sftp_session, sshc->quote_path1, 0755);
+    if(rc == LIBSSH2_ERROR_EAGAIN) {
       break;
+    }
+    else if(rc != 0) {
+      err = libssh2_sftp_last_error(sshc->sftp_session);
+      Curl_safefree(sshc->quote_path1);
+      sshc->quote_path1 = NULL;
+      failf(data, "mkdir command failed: %s", sftp_libssh2_strerror(err));
+      state(conn, SSH_SFTP_CLOSE);
+      sshc->actualcode = CURLE_QUOTE_ERROR;
+      break;
+    }
+    state(conn, SSH_SFTP_NEXT_QUOTE);
+    break;
 
-    case SSH_SFTP_CREATE_DIRS_INIT:
-      if(strlen(sftp_scp->path) > 1) {
-        sshc->slash_pos = sftp_scp->path + 1; /* ignore the leading '/' */
-        state(conn, SSH_SFTP_CREATE_DIRS);
-      } else {
-        state(conn, SSH_SFTP_UPLOAD_INIT);
-      }
+  case SSH_SFTP_QUOTE_RENAME:
+    rc = libssh2_sftp_rename(sshc->sftp_session, sshc->quote_path1,
+                             sshc->quote_path2);
+    if(rc == LIBSSH2_ERROR_EAGAIN) {
+      break;
+    }
+    else if(rc != 0) {
+      err = libssh2_sftp_last_error(sshc->sftp_session);
+      Curl_safefree(sshc->quote_path1);
+      sshc->quote_path1 = NULL;
+      Curl_safefree(sshc->quote_path2);
+      sshc->quote_path2 = NULL;
+      failf(data, "rename command failed: %s", sftp_libssh2_strerror(err));
+      state(conn, SSH_SFTP_CLOSE);
+      sshc->actualcode = CURLE_QUOTE_ERROR;
       break;
+    }
+    state(conn, SSH_SFTP_NEXT_QUOTE);
+    break;
 
-    case SSH_SFTP_CREATE_DIRS:
-      if((sshc->slash_pos = strchr(sshc->slash_pos, '/')) != NULL) {
-        *sshc->slash_pos = 0;
+  case SSH_SFTP_QUOTE_RMDIR:
+    rc = libssh2_sftp_rmdir(sshc->sftp_session, sshc->quote_path1);
+    if(rc == LIBSSH2_ERROR_EAGAIN) {
+      break;
+    }
+    else if(rc != 0) {
+      err = libssh2_sftp_last_error(sshc->sftp_session);
+      Curl_safefree(sshc->quote_path1);
+      sshc->quote_path1 = NULL;
+      failf(data, "rmdir command failed: %s", sftp_libssh2_strerror(err));
+      state(conn, SSH_SFTP_CLOSE);
+      sshc->actualcode = CURLE_QUOTE_ERROR;
+      break;
+    }
+    state(conn, SSH_SFTP_NEXT_QUOTE);
+    break;
 
-        infof(data, "Creating directory '%s'\n", sftp_scp->path);
-        state(conn, SSH_SFTP_CREATE_DIRS_MKDIR);
-        break;
-      } else {
-        state(conn, SSH_SFTP_UPLOAD_INIT);
-      }
+  case SSH_SFTP_QUOTE_UNLINK:
+    rc = libssh2_sftp_unlink(sshc->sftp_session, sshc->quote_path1);
+    if(rc == LIBSSH2_ERROR_EAGAIN) {
+      break;
+    }
+    else if(rc != 0) {
+      err = libssh2_sftp_last_error(sshc->sftp_session);
+      Curl_safefree(sshc->quote_path1);
+      sshc->quote_path1 = NULL;
+      failf(data, "rm command failed: %s", sftp_libssh2_strerror(err));
+      state(conn, SSH_SFTP_CLOSE);
+      sshc->actualcode = CURLE_QUOTE_ERROR;
       break;
+    }
+    state(conn, SSH_SFTP_NEXT_QUOTE);
+    break;
 
-    case SSH_SFTP_CREATE_DIRS_MKDIR:
-      /* 'mode' - parameter is preliminary - default to 0644 */
-      rc = libssh2_sftp_mkdir(sshc->sftp_session, sftp_scp->path,
-                              data->set.new_directory_perms);
-      if(rc == LIBSSH2_ERROR_EAGAIN) {
+  case SSH_SFTP_TRANS_INIT:
+    if(data->set.upload)
+      state(conn, SSH_SFTP_UPLOAD_INIT);
+    else {
+      if(sftp_scp->path[strlen(sftp_scp->path)-1] == '/')
+        state(conn, SSH_SFTP_READDIR_INIT);
+      else
+        state(conn, SSH_SFTP_DOWNLOAD_INIT);
+    }
+    break;
+
+  case SSH_SFTP_UPLOAD_INIT:
+    /*
+     * NOTE!!!  libssh2 requires that the destination path is a full path
+     *          that includes the destination file and name OR ends in a "/"
+     *          If this is not done the destination file will be named the
+     *          same name as the last directory in the path.
+     */
+    sshc->sftp_handle =
+      libssh2_sftp_open(sshc->sftp_session, sftp_scp->path,
+                        LIBSSH2_FXF_WRITE|LIBSSH2_FXF_CREAT|LIBSSH2_FXF_TRUNC,
+                        data->set.new_file_perms);
+    if(!sshc->sftp_handle) {
+      if(libssh2_session_last_errno(sshc->ssh_session) ==
+         LIBSSH2_ERROR_EAGAIN) {
         break;
       }
-      *sshc->slash_pos = '/';
-      ++sshc->slash_pos;
-      if(rc == -1) {
-        unsigned int sftp_err = 0;
-        /*
-         * abort if failure wasn't that the dir already exists or the
-         * permission was denied (creation might succeed further
-         * down the path) - retry on unspecific FAILURE also
-         */
-        sftp_err = libssh2_sftp_last_error(sshc->sftp_session);
-        if((sftp_err != LIBSSH2_FX_FILE_ALREADY_EXISTS) &&
-            (sftp_err != LIBSSH2_FX_FAILURE) &&
-            (sftp_err != LIBSSH2_FX_PERMISSION_DENIED)) {
-          result = sftp_libssh2_error_to_CURLE(sftp_err);
+      else {
+        err = libssh2_sftp_last_error(sshc->sftp_session);
+        failf(data, "Upload failed: %s", sftp_libssh2_strerror(err));
+        if(sshc->secondCreateDirs) {
           state(conn, SSH_SFTP_CLOSE);
-          sshc->actualCode = result;
+          sshc->actualcode = err;
           break;
         }
+        else if(((err == LIBSSH2_FX_NO_SUCH_FILE) ||
+                 (err == LIBSSH2_FX_FAILURE) ||
+                 (err == LIBSSH2_FX_NO_SUCH_PATH)) &&
+                (data->set.ftp_create_missing_dirs &&
+                 (strlen(sftp_scp->path) > 1))) {
+          /* try to create the path remotely */
+          sshc->secondCreateDirs = 1;
+          state(conn, SSH_SFTP_CREATE_DIRS_INIT);
+          break;
+        }
+        state(conn, SSH_SFTP_CLOSE);
+        sshc->actualcode = sftp_libssh2_error_to_CURLE(err);
+        break;
       }
+    }
+
+    /* upload data */
+    result = Curl_setup_transfer(conn, -1, -1, FALSE, NULL,
+                                 FIRSTSOCKET, NULL);
+
+    if(result) {
+      state(conn, SSH_SFTP_CLOSE);
+      sshc->actualcode = result;
+    }
+    else {
+      state(conn, SSH_STOP);
+    }
+    break;
+
+  case SSH_SFTP_CREATE_DIRS_INIT:
+    if(strlen(sftp_scp->path) > 1) {
+      sshc->slash_pos = sftp_scp->path + 1; /* ignore the leading '/' */
       state(conn, SSH_SFTP_CREATE_DIRS);
+    }
+    else {
+      state(conn, SSH_SFTP_UPLOAD_INIT);
+    }
+    break;
+
+  case SSH_SFTP_CREATE_DIRS:
+    if((sshc->slash_pos = strchr(sshc->slash_pos, '/')) != NULL) {
+      *sshc->slash_pos = 0;
+
+      infof(data, "Creating directory '%s'\n", sftp_scp->path);
+      state(conn, SSH_SFTP_CREATE_DIRS_MKDIR);
       break;
+    }
+    else {
+      state(conn, SSH_SFTP_UPLOAD_INIT);
+    }
+    break;
 
-    case SSH_SFTP_READDIR_INIT:
+  case SSH_SFTP_CREATE_DIRS_MKDIR:
+    /* 'mode' - parameter is preliminary - default to 0644 */
+    rc = libssh2_sftp_mkdir(sshc->sftp_session, sftp_scp->path,
+                            data->set.new_directory_perms);
+    if(rc == LIBSSH2_ERROR_EAGAIN) {
+      break;
+    }
+    *sshc->slash_pos = '/';
+    ++sshc->slash_pos;
+    if(rc == -1) {
+      unsigned int sftp_err = 0;
       /*
-       * This is a directory that we are trying to get, so produce a
-       * directory listing
+       * abort if failure wasn't that the dir already exists or the
+       * permission was denied (creation might succeed further
+       * down the path) - retry on unspecific FAILURE also
        */
-      sshc->sftp_handle = libssh2_sftp_opendir(sshc->sftp_session,
-                                                   sftp_scp->path);
-      if(!sshc->sftp_handle) {
-        if(libssh2_session_last_errno(sshc->ssh_session) ==
-            LIBSSH2_ERROR_EAGAIN) {
-          break;
-        }
-        else {
-          err = libssh2_sftp_last_error(sshc->sftp_session);
-          failf(data, "Could not open directory for reading: %s",
-                sftp_libssh2_strerror(err));
-          state(conn, SSH_SFTP_CLOSE);
-          sshc->actualCode = sftp_libssh2_error_to_CURLE(err);
-          break;
-        }
-      }
-      if((sshc->readdir_filename = (char *)malloc(PATH_MAX+1)) == NULL) {
+      sftp_err = libssh2_sftp_last_error(sshc->sftp_session);
+      if((sftp_err != LIBSSH2_FX_FILE_ALREADY_EXISTS) &&
+         (sftp_err != LIBSSH2_FX_FAILURE) &&
+         (sftp_err != LIBSSH2_FX_PERMISSION_DENIED)) {
+        result = sftp_libssh2_error_to_CURLE(sftp_err);
         state(conn, SSH_SFTP_CLOSE);
-        sshc->actualCode = CURLE_OUT_OF_MEMORY;
+        sshc->actualcode = result;
+        break;
+      }
+    }
+    state(conn, SSH_SFTP_CREATE_DIRS);
+    break;
+
+  case SSH_SFTP_READDIR_INIT:
+    /*
+     * This is a directory that we are trying to get, so produce a
+     * directory listing
+     */
+    sshc->sftp_handle = libssh2_sftp_opendir(sshc->sftp_session,
+                                             sftp_scp->path);
+    if(!sshc->sftp_handle) {
+      if(libssh2_session_last_errno(sshc->ssh_session) ==
+         LIBSSH2_ERROR_EAGAIN) {
         break;
       }
-      if((sshc->readdir_longentry = (char *)malloc(PATH_MAX+1)) == NULL) {
-        Curl_safefree(sshc->readdir_filename);
-        sshc->readdir_filename = NULL;
+      else {
+        err = libssh2_sftp_last_error(sshc->sftp_session);
+        failf(data, "Could not open directory for reading: %s",
+              sftp_libssh2_strerror(err));
         state(conn, SSH_SFTP_CLOSE);
-        sshc->actualCode = CURLE_OUT_OF_MEMORY;
+        sshc->actualcode = sftp_libssh2_error_to_CURLE(err);
         break;
       }
-      state(conn, SSH_SFTP_READDIR);
+    }
+    if((sshc->readdir_filename = (char *)malloc(PATH_MAX+1)) == NULL) {
+      state(conn, SSH_SFTP_CLOSE);
+      sshc->actualcode = CURLE_OUT_OF_MEMORY;
       break;
+    }
+    if((sshc->readdir_longentry = (char *)malloc(PATH_MAX+1)) == NULL) {
+      Curl_safefree(sshc->readdir_filename);
+      sshc->readdir_filename = NULL;
+      state(conn, SSH_SFTP_CLOSE);
+      sshc->actualcode = CURLE_OUT_OF_MEMORY;
+      break;
+    }
+    state(conn, SSH_SFTP_READDIR);
+    break;
 
-    case SSH_SFTP_READDIR:
-      sshc->readdir_len = libssh2_sftp_readdir_ex(sshc->sftp_handle,
-                                                  sshc->readdir_filename,
-                                                  PATH_MAX,
-                                                  sshc->readdir_longentry,
-                                                  PATH_MAX,
-                                                  &sshc->readdir_attrs);
-      if(sshc->readdir_len == LIBSSH2_ERROR_EAGAIN) {
-        break;
-      }
-      if(sshc->readdir_len > 0) {
-        sshc->readdir_filename[sshc->readdir_len] = '\0';
+  case SSH_SFTP_READDIR:
+    sshc->readdir_len = libssh2_sftp_readdir_ex(sshc->sftp_handle,
+                                                sshc->readdir_filename,
+                                                PATH_MAX,
+                                                sshc->readdir_longentry,
+                                                PATH_MAX,
+                                                &sshc->readdir_attrs);
+    if(sshc->readdir_len == LIBSSH2_ERROR_EAGAIN) {
+      break;
+    }
+    if(sshc->readdir_len > 0) {
+      sshc->readdir_filename[sshc->readdir_len] = '\0';
 
-        if(data->set.ftp_list_only) {
-          char *tmpLine;
+      if(data->set.ftp_list_only) {
+        char *tmpLine;
 
-          tmpLine = aprintf("%s\n", sshc->readdir_filename);
-          if(tmpLine == NULL) {
-            state(conn, SSH_SFTP_CLOSE);
-            sshc->actualCode = CURLE_OUT_OF_MEMORY;
-            break;
-          }
-          result = Curl_client_write(conn, CLIENTWRITE_BODY, tmpLine, 0);
-          Curl_safefree(tmpLine);
+        tmpLine = aprintf("%s\n", sshc->readdir_filename);
+        if(tmpLine == NULL) {
+          state(conn, SSH_SFTP_CLOSE);
+          sshc->actualcode = CURLE_OUT_OF_MEMORY;
+          break;
+        }
+        result = Curl_client_write(conn, CLIENTWRITE_BODY, tmpLine, 0);
+        Curl_safefree(tmpLine);
+
+        /* output debug output if that is requested */
+        if(data->set.verbose) {
+          Curl_debug(data, CURLINFO_DATA_OUT, sshc->readdir_filename,
+                     sshc->readdir_len, conn);
+        }
+      }
+      else {
+        sshc->readdir_currLen = strlen(sshc->readdir_longentry);
+        sshc->readdir_totalLen = 80 + sshc->readdir_currLen;
+        sshc->readdir_line = (char *)calloc(sshc->readdir_totalLen, 1);
+        if(!sshc->readdir_line) {
+          Curl_safefree(sshc->readdir_filename);
+          sshc->readdir_filename = NULL;
+          Curl_safefree(sshc->readdir_longentry);
+          sshc->readdir_longentry = NULL;
+          state(conn, SSH_SFTP_CLOSE);
+          sshc->actualcode = CURLE_OUT_OF_MEMORY;
+          break;
+        }
 
-          /* output debug output if that is requested */
-          if(data->set.verbose) {
-            Curl_debug(data, CURLINFO_DATA_OUT, sshc->readdir_filename,
-                       sshc->readdir_len, conn);
-          }
-        } else {
-          sshc->readdir_currLen = strlen(sshc->readdir_longentry);
-          sshc->readdir_totalLen = 80 + sshc->readdir_currLen;
-          sshc->readdir_line = (char *)calloc(sshc->readdir_totalLen, 1);
-          if(!sshc->readdir_line) {
+        memcpy(sshc->readdir_line, sshc->readdir_longentry,
+               sshc->readdir_currLen);
+        if((sshc->readdir_attrs.flags & LIBSSH2_SFTP_ATTR_PERMISSIONS) &&
+           ((sshc->readdir_attrs.permissions & LIBSSH2_SFTP_S_IFMT) ==
+            LIBSSH2_SFTP_S_IFLNK)) {
+          sshc->readdir_linkPath = (char *)malloc(PATH_MAX + 1);
+          if(sshc->readdir_linkPath == NULL) {
             Curl_safefree(sshc->readdir_filename);
             sshc->readdir_filename = NULL;
             Curl_safefree(sshc->readdir_longentry);
             sshc->readdir_longentry = NULL;
             state(conn, SSH_SFTP_CLOSE);
-            sshc->actualCode = CURLE_OUT_OF_MEMORY;
+            sshc->actualcode = CURLE_OUT_OF_MEMORY;
             break;
           }
 
-          memcpy(sshc->readdir_line, sshc->readdir_longentry,
-                 sshc->readdir_currLen);
-          if((sshc->readdir_attrs.flags & LIBSSH2_SFTP_ATTR_PERMISSIONS) &&
-              ((sshc->readdir_attrs.permissions & LIBSSH2_SFTP_S_IFMT) ==
-               LIBSSH2_SFTP_S_IFLNK)) {
-            sshc->readdir_linkPath = (char *)malloc(PATH_MAX + 1);
-            if(sshc->readdir_linkPath == NULL) {
-              Curl_safefree(sshc->readdir_filename);
-              sshc->readdir_filename = NULL;
-              Curl_safefree(sshc->readdir_longentry);
-              sshc->readdir_longentry = NULL;
-              state(conn, SSH_SFTP_CLOSE);
-              sshc->actualCode = CURLE_OUT_OF_MEMORY;
-              break;
-            }
-
-            snprintf(sshc->readdir_linkPath, PATH_MAX, "%s%s", sftp_scp->path,
-                     sshc->readdir_filename);
-            state(conn, SSH_SFTP_READDIR_LINK);
-            break;
-          }
-          state(conn, SSH_SFTP_READDIR_BOTTOM);
+          snprintf(sshc->readdir_linkPath, PATH_MAX, "%s%s", sftp_scp->path,
+                   sshc->readdir_filename);
+          state(conn, SSH_SFTP_READDIR_LINK);
           break;
         }
-      }
-      else if(sshc->readdir_len == 0) {
-        Curl_safefree(sshc->readdir_filename);
-        sshc->readdir_filename = NULL;
-        Curl_safefree(sshc->readdir_longentry);
-        sshc->readdir_longentry = NULL;
-        state(conn, SSH_SFTP_READDIR_DONE);
-        break;
-      }
-      else if(sshc->readdir_len <= 0) {
-        err = libssh2_sftp_last_error(sshc->sftp_session);
-        sshc->actualCode = err;
-        failf(data, "Could not open remote file for reading: %s :: %d",
-              sftp_libssh2_strerror(err),
-              libssh2_session_last_errno(sshc->ssh_session));
-        Curl_safefree(sshc->readdir_filename);
-        sshc->readdir_filename = NULL;
-        Curl_safefree(sshc->readdir_longentry);
-        sshc->readdir_longentry = NULL;
-        state(conn, SSH_SFTP_CLOSE);
+        state(conn, SSH_SFTP_READDIR_BOTTOM);
         break;
       }
+    }
+    else if(sshc->readdir_len == 0) {
+      Curl_safefree(sshc->readdir_filename);
+      sshc->readdir_filename = NULL;
+      Curl_safefree(sshc->readdir_longentry);
+      sshc->readdir_longentry = NULL;
+      state(conn, SSH_SFTP_READDIR_DONE);
+      break;
+    }
+    else if(sshc->readdir_len <= 0) {
+      err = libssh2_sftp_last_error(sshc->sftp_session);
+      sshc->actualcode = err;
+      failf(data, "Could not open remote file for reading: %s :: %d",
+            sftp_libssh2_strerror(err),
+            libssh2_session_last_errno(sshc->ssh_session));
+      Curl_safefree(sshc->readdir_filename);
+      sshc->readdir_filename = NULL;
+      Curl_safefree(sshc->readdir_longentry);
+      sshc->readdir_longentry = NULL;
+      state(conn, SSH_SFTP_CLOSE);
+      break;
+    }
+    break;
+
+  case SSH_SFTP_READDIR_LINK:
+    sshc->readdir_len = libssh2_sftp_readlink(sshc->sftp_session,
+                                              sshc->readdir_linkPath,
+                                              sshc->readdir_filename,
+                                              PATH_MAX);
+    if(sshc->readdir_len == LIBSSH2_ERROR_EAGAIN) {
+      break;
+    }
+    Curl_safefree(sshc->readdir_linkPath);
+    sshc->readdir_linkPath = NULL;
+    sshc->readdir_line = realloc(sshc->readdir_line,
+                                 sshc->readdir_totalLen + 4 +
+                                 sshc->readdir_len);
+    if(!sshc->readdir_line) {
+      Curl_safefree(sshc->readdir_filename);
+      sshc->readdir_filename = NULL;
+      Curl_safefree(sshc->readdir_longentry);
+      sshc->readdir_longentry = NULL;
+      state(conn, SSH_SFTP_CLOSE);
+      sshc->actualcode = CURLE_OUT_OF_MEMORY;
       break;
+    }
 
-    case SSH_SFTP_READDIR_LINK:
-      sshc->readdir_len = libssh2_sftp_readlink(sshc->sftp_session,
-                                                sshc->readdir_linkPath,
-                                                sshc->readdir_filename,
-                                                PATH_MAX);
-      if(sshc->readdir_len == LIBSSH2_ERROR_EAGAIN) {
+    sshc->readdir_currLen += snprintf(sshc->readdir_line +
+                                      sshc->readdir_currLen,
+                                      sshc->readdir_totalLen -
+                                      sshc->readdir_currLen,
+                                      " -> %s",
+                                      sshc->readdir_filename);
+
+    state(conn, SSH_SFTP_READDIR_BOTTOM);
+    break;
+
+  case SSH_SFTP_READDIR_BOTTOM:
+    sshc->readdir_currLen += snprintf(sshc->readdir_line +
+                                      sshc->readdir_currLen,
+                                      sshc->readdir_totalLen -
+                                      sshc->readdir_currLen, "\n");
+    result = Curl_client_write(conn, CLIENTWRITE_BODY,
+                               sshc->readdir_line, 0);
+
+    /* output debug output if that is requested */
+    if(data->set.verbose) {
+      Curl_debug(data, CURLINFO_DATA_OUT, sshc->readdir_line,
+                 sshc->readdir_currLen, conn);
+    }
+    Curl_safefree(sshc->readdir_line);
+    sshc->readdir_line = NULL;
+    state(conn, SSH_SFTP_READDIR);
+    break;
+
+  case SSH_SFTP_READDIR_DONE:
+    if(libssh2_sftp_closedir(sshc->sftp_handle) ==
+       LIBSSH2_ERROR_EAGAIN) {
+      break;
+    }
+    sshc->sftp_handle = NULL;
+    Curl_safefree(sshc->readdir_filename);
+    sshc->readdir_filename = NULL;
+    Curl_safefree(sshc->readdir_longentry);
+    sshc->readdir_longentry = NULL;
+
+    /* no data to transfer */
+    result = Curl_setup_transfer(conn, -1, -1, FALSE, NULL, -1, NULL);
+    state(conn, SSH_STOP);
+    break;
+
+  case SSH_SFTP_DOWNLOAD_INIT:
+    /*
+     * Work on getting the specified file
+     */
+    sshc->sftp_handle =
+      libssh2_sftp_open(sshc->sftp_session, sftp_scp->path,
+                        LIBSSH2_FXF_READ, data->set.new_file_perms);
+    if(!sshc->sftp_handle) {
+      if(libssh2_session_last_errno(sshc->ssh_session) ==
+         LIBSSH2_ERROR_EAGAIN) {
         break;
       }
-      Curl_safefree(sshc->readdir_linkPath);
-      sshc->readdir_linkPath = NULL;
-      sshc->readdir_line = realloc(sshc->readdir_line,
-                                   sshc->readdir_totalLen + 4 +
-                                   sshc->readdir_len);
-      if(!sshc->readdir_line) {
-        Curl_safefree(sshc->readdir_filename);
-        sshc->readdir_filename = NULL;
-        Curl_safefree(sshc->readdir_longentry);
-        sshc->readdir_longentry = NULL;
+      else {
+        err = libssh2_sftp_last_error(sshc->sftp_session);
+        failf(data, "Could not open remote file for reading: %s",
+              sftp_libssh2_strerror(err));
         state(conn, SSH_SFTP_CLOSE);
-        sshc->actualCode = CURLE_OUT_OF_MEMORY;
+        sshc->actualcode = sftp_libssh2_error_to_CURLE(err);
         break;
       }
+    }
+    state(conn, SSH_SFTP_DOWNLOAD_STAT);
+    break;
 
-      sshc->readdir_currLen += snprintf(sshc->readdir_line +
-                                        sshc->readdir_currLen,
-                                        sshc->readdir_totalLen -
-                                        sshc->readdir_currLen,
-                                        " -> %s",
-                                        sshc->readdir_filename);
+  case SSH_SFTP_DOWNLOAD_STAT:
+  {
+    LIBSSH2_SFTP_ATTRIBUTES attrs;
 
-      state(conn, SSH_SFTP_READDIR_BOTTOM);
+    rc = libssh2_sftp_stat(sshc->sftp_session, sftp_scp->path, &attrs);
+    if(rc == LIBSSH2_ERROR_EAGAIN) {
       break;
+    }
+    else if(rc) {
+      /*
+       * libssh2_sftp_open() didn't return an error, so maybe the server
+       * just doesn't support stat()
+       */
+      data->reqdata.size = -1;
+      data->reqdata.maxdownload = -1;
+    }
+    else {
+      data->reqdata.size = attrs.filesize;
+      data->reqdata.maxdownload = attrs.filesize;
+      Curl_pgrsSetDownloadSize(data, attrs.filesize);
+    }
+  }
 
-    case SSH_SFTP_READDIR_BOTTOM:
-      sshc->readdir_currLen += snprintf(sshc->readdir_line +
-                                        sshc->readdir_currLen,
-                                        sshc->readdir_totalLen -
-                                        sshc->readdir_currLen, "\n");
-      result = Curl_client_write(conn, CLIENTWRITE_BODY,
-                                 sshc->readdir_line, 0);
-
-      /* output debug output if that is requested */
-      if(data->set.verbose) {
-        Curl_debug(data, CURLINFO_DATA_OUT, sshc->readdir_line,
-                   sshc->readdir_currLen, conn);
-      }
-      Curl_safefree(sshc->readdir_line);
-      sshc->readdir_line = NULL;
-      state(conn, SSH_SFTP_READDIR);
-      break;
+  /* Setup the actual download */
+  result = Curl_setup_transfer(conn, FIRSTSOCKET, data->reqdata.size,
+                               FALSE, NULL, -1, NULL);
+  if(result) {
+    state(conn, SSH_SFTP_CLOSE);
+    sshc->actualcode = result;
+  }
+  else {
+    state(conn, SSH_STOP);
+  }
+  break;
 
-    case SSH_SFTP_READDIR_DONE:
-      if(libssh2_sftp_closedir(sshc->sftp_handle) ==
-          LIBSSH2_ERROR_EAGAIN) {
+  case SSH_SFTP_CLOSE:
+    if(sshc->sftp_handle) {
+      rc = libssh2_sftp_close(sshc->sftp_handle);
+      if(rc == LIBSSH2_ERROR_EAGAIN) {
         break;
       }
+      else if(rc < 0) {
+        infof(data, "Failed to close libssh2 file\n");
+      }
       sshc->sftp_handle = NULL;
-      Curl_safefree(sshc->readdir_filename);
-      sshc->readdir_filename = NULL;
-      Curl_safefree(sshc->readdir_longentry);
-      sshc->readdir_longentry = NULL;
+    }
+    Curl_safefree(sftp_scp->path);
+    sftp_scp->path = NULL;
 
-      /* no data to transfer */
-      result = Curl_setup_transfer(conn, -1, -1, FALSE, NULL, -1, NULL);
-      state(conn, SSH_STOP);
-      break;
+    DEBUGF(infof(data, "SFTP DONE done\n"));
+#if 0 /* PREV */
+    state(conn, SSH_SFTP_SHUTDOWN);
+#endif
+    state(conn, SSH_STOP);
+    result = sshc->actualcode;
+    break;
 
-    case SSH_SFTP_DOWNLOAD_INIT:
-      /*
-       * Work on getting the specified file
-       */
-      sshc->sftp_handle =
-        libssh2_sftp_open(sshc->sftp_session, sftp_scp->path,
-                          LIBSSH2_FXF_READ, data->set.new_file_perms);
-      if(!sshc->sftp_handle) {
-        if(libssh2_session_last_errno(sshc->ssh_session) ==
-            LIBSSH2_ERROR_EAGAIN) {
-          break;
-        }
-        else {
-          err = libssh2_sftp_last_error(sshc->sftp_session);
-          failf(data, "Could not open remote file for reading: %s",
-                sftp_libssh2_strerror(err));
-          state(conn, SSH_SFTP_CLOSE);
-          sshc->actualCode = sftp_libssh2_error_to_CURLE(err);
-          break;
-        }
+  case SSH_SFTP_SHUTDOWN:
+    if(sshc->sftp_session) {
+      rc = libssh2_sftp_shutdown(sshc->sftp_session);
+      if(rc == LIBSSH2_ERROR_EAGAIN) {
+        break;
       }
-      state(conn, SSH_SFTP_DOWNLOAD_STAT);
-      break;
+      else if(rc < 0) {
+        infof(data, "Failed to stop libssh2 sftp subsystem\n");
+      }
+      sshc->sftp_session = NULL;
+    }
 
-    case SSH_SFTP_DOWNLOAD_STAT:
-      {
-        LIBSSH2_SFTP_ATTRIBUTES attrs;
+    Curl_safefree(sshc->homedir);
+    sshc->homedir = NULL;
 
-        rc = libssh2_sftp_stat(sshc->sftp_session, sftp_scp->path, &attrs);
-        if(rc == LIBSSH2_ERROR_EAGAIN) {
-          break;
-        }
-        else if(rc) {
-          /*
-           * libssh2_sftp_open() didn't return an error, so maybe the server
-           * just doesn't support stat()
-           */
-          data->reqdata.size = -1;
-          data->reqdata.maxdownload = -1;
-        } else {
-          data->reqdata.size = attrs.filesize;
-          data->reqdata.maxdownload = attrs.filesize;
-          Curl_pgrsSetDownloadSize(data, attrs.filesize);
-        }
-      }
+    state(conn, SSH_SESSION_DISCONNECT);
+    break;
 
-      /* Setup the actual download */
-      result = Curl_setup_transfer(conn, FIRSTSOCKET, data->reqdata.size,
-                                   FALSE, NULL, -1, NULL);
-      if(result) {
-        state(conn, SSH_SFTP_CLOSE);
-        sshc->actualCode = result;
-      } else {
-        state(conn, SSH_STOP);
-      }
+  case SSH_SCP_TRANS_INIT:
+    result = ssh_getworkingpath(conn, sshc->homedir, &sftp_scp->path);
+    if(result) {
+      sshc->actualcode = result;
+      state(conn, SSH_STOP);
       break;
+    }
 
-    case SSH_SFTP_CLOSE:
-      if(sshc->sftp_handle) {
-        rc = libssh2_sftp_close(sshc->sftp_handle);
-        if(rc == LIBSSH2_ERROR_EAGAIN) {
-          break;
-        }
-        else if(rc < 0) {
-          infof(data, "Failed to close libssh2 file\n");
-        }
-        sshc->sftp_handle = NULL;
+    if(data->set.upload) {
+      if(data->set.infilesize < 0) {
+        failf(data, "SCP requires a known file size for upload");
+        sshc->actualcode = CURLE_UPLOAD_FAILED;
+        state(conn, SSH_SCP_CHANNEL_FREE);
+        break;
       }
-      state(conn, SSH_SFTP_SHUTDOWN);
-      break;
-
-    case SSH_SFTP_SHUTDOWN:
-      if(sshc->sftp_session) {
-        rc = libssh2_sftp_shutdown(sshc->sftp_session);
-        if(rc == LIBSSH2_ERROR_EAGAIN) {
-          break;
-        }
-        else if(rc < 0) {
-          infof(data, "Failed to stop libssh2 sftp subsystem\n");
-        }
-        sshc->sftp_session = NULL;
+      state(conn, SSH_SCP_UPLOAD_INIT);
+    }
+    else {
+      state(conn, SSH_SCP_DOWNLOAD_INIT);
+    }
+    break;
+
+  case SSH_SCP_UPLOAD_INIT:
+    /*
+     * libssh2 requires that the destination path is a full path that
+     * includes the destination file and name OR ends in a "/" .  If this is
+     * not done the destination file will be named the same name as the last
+     * directory in the path.
+     */
+    sshc->ssh_channel =
+      libssh2_scp_send_ex(sshc->ssh_session, sftp_scp->path,
+                          data->set.new_file_perms,
+                          data->set.infilesize, 0, 0);
+    if(!sshc->ssh_channel) {
+      if(libssh2_session_last_errno(sshc->ssh_session) ==
+         LIBSSH2_ERROR_EAGAIN) {
+        break;
       }
+      else {
+        int ssh_err;
+        char *err_msg;
 
-      Curl_safefree(sftp_scp->path);
-      sftp_scp->path = NULL;
-
-      Curl_safefree(sftp_scp->homedir);
-      sftp_scp->homedir = NULL;
+        ssh_err = libssh2_session_last_error(sshc->ssh_session,
+                                             &err_msg, NULL, 0);
+        err = libssh2_session_error_to_CURLE(ssh_err);
+        failf(conn->data, "%s", err_msg);
+        state(conn, SSH_SCP_CHANNEL_FREE);
+        sshc->actualcode = err;
+        break;
+      }
+    }
 
-      state(conn, SSH_CHANNEL_CLOSE);
-      break;
+    /* upload data */
+    result = Curl_setup_transfer(conn, -1, data->reqdata.size, FALSE, NULL,
+                                 FIRSTSOCKET, NULL);
 
-    case SSH_SCP_TRANS_INIT:
-      if(data->set.upload) {
-        if(data->set.infilesize < 0) {
-          failf(data, "SCP requires a known file size for upload");
-          sshc->actualCode = CURLE_UPLOAD_FAILED;
-          state(conn, SSH_SCP_CHANNEL_FREE);
-          break;
-        }
-        state(conn, SSH_SCP_UPLOAD_INIT);
-      } else {
-        state(conn, SSH_SCP_DOWNLOAD_INIT);
+    if(result) {
+      state(conn, SSH_SCP_CHANNEL_FREE);
+      sshc->actualcode = result;
+    }
+    else {
+      state(conn, SSH_STOP);
+    }
+    break;
+
+  case SSH_SCP_DOWNLOAD_INIT:
+  {
+    /*
+     * We must check the remote file; if it is a directory no values will
+     * be set in sb
+     */
+    struct stat sb;
+    curl_off_t bytecount;
+
+    /* clear the struct scp recv will fill in */
+    memset(&sb, 0, sizeof(struct stat));
+
+    /* get a fresh new channel from the ssh layer */
+    sshc->ssh_channel = libssh2_scp_recv(sshc->ssh_session,
+                                         sftp_scp->path, &sb);
+    if(!sshc->ssh_channel) {
+      if(libssh2_session_last_errno(sshc->ssh_session) ==
+         LIBSSH2_ERROR_EAGAIN) {
+        break;
       }
-      break;
+      else {
+        int ssh_err;
+        char *err_msg;
 
-    case SSH_SCP_UPLOAD_INIT:
-      /*
-       * libssh2 requires that the destination path is a full path that
-       * includes the destination file and name OR ends in a "/" .  If this is
-       * not done the destination file will be named the same name as the last
-       * directory in the path.
-       */
-      sshc->ssh_channel =
-                  libssh2_scp_send_ex(sshc->ssh_session, sftp_scp->path,
-                                      data->set.new_file_perms,
-                                      data->set.infilesize, 0, 0);
-      if(!sshc->ssh_channel) {
-        if(libssh2_session_last_errno(sshc->ssh_session) ==
-            LIBSSH2_ERROR_EAGAIN) {
-          break;
-        } else {
-          int ssh_err;
-          char *err_msg;
-
-          ssh_err = libssh2_session_last_error(sshc->ssh_session,
-                                               &err_msg, NULL, 0);
-          err = libssh2_session_error_to_CURLE(ssh_err);
-          failf(conn->data, "%s", err_msg);
-          state(conn, SSH_SCP_CHANNEL_FREE);
-          sshc->actualCode = err;
-          break;
-        }
+        ssh_err = libssh2_session_last_error(sshc->ssh_session,
+                                             &err_msg, NULL, 0);
+        err = libssh2_session_error_to_CURLE(ssh_err);
+        failf(conn->data, "%s", err_msg);
+        state(conn, SSH_SCP_CHANNEL_FREE);
+        sshc->actualcode = err;
+        break;
       }
+    }
 
-      /* upload data */
-      result = Curl_setup_transfer(conn, -1, data->reqdata.size, FALSE, NULL,
-                                   FIRSTSOCKET, NULL);
+    /* download data */
+    bytecount = (curl_off_t)sb.st_size;
+    data->reqdata.maxdownload =  (curl_off_t)sb.st_size;
+    result = Curl_setup_transfer(conn, FIRSTSOCKET,
+                                 bytecount, FALSE, NULL, -1, NULL);
 
-      if(result) {
-        state(conn, SSH_SCP_CHANNEL_FREE);
-        sshc->actualCode = result;
-      } else {
-        state(conn, SSH_STOP);
-      }
-      break;
+    if(result) {
+      state(conn, SSH_SCP_CHANNEL_FREE);
+      sshc->actualcode = result;
+    }
+    else
+      state(conn, SSH_STOP);
+  }
+  break;
 
-    case SSH_SCP_DOWNLOAD_INIT:
-      {
-        /*
-         * We must check the remote file; if it is a directory no values will
-         * be set in sb
-         */
-        struct stat sb;
-        curl_off_t bytecount;
-
-        memset(&sb, 0, sizeof(struct stat));
-        sshc->ssh_channel = libssh2_scp_recv(sshc->ssh_session,
-                                                 sftp_scp->path, &sb);
-        if(!sshc->ssh_channel) {
-          if(libssh2_session_last_errno(sshc->ssh_session) ==
-             LIBSSH2_ERROR_EAGAIN) {
-            break;
-          } else {
-            int ssh_err;
-            char *err_msg;
-
-            ssh_err = libssh2_session_last_error(sshc->ssh_session,
-                                                 &err_msg, NULL, 0);
-            err = libssh2_session_error_to_CURLE(ssh_err);
-            failf(conn->data, "%s", err_msg);
-            state(conn, SSH_SCP_CHANNEL_FREE);
-            sshc->actualCode = err;
-            break;
-          }
-        }
+  case SSH_SCP_DONE:
+    if(data->set.upload)
+      state(conn, SSH_SCP_SEND_EOF);
+    else
+      state(conn, SSH_SCP_CHANNEL_FREE);
+    break;
 
-        /* download data */
-        bytecount = (curl_off_t)sb.st_size;
-        data->reqdata.maxdownload =  (curl_off_t)sb.st_size;
-        result = Curl_setup_transfer(conn, FIRSTSOCKET,
-                                     bytecount, FALSE, NULL, -1, NULL);
-
-        if(result) {
-          state(conn, SSH_SCP_CHANNEL_FREE);
-          sshc->actualCode = result;
-        } else {
-          state(conn, SSH_STOP);
-        }
+  case SSH_SCP_SEND_EOF:
+    if(sshc->ssh_channel) {
+      rc = libssh2_channel_send_eof(sshc->ssh_channel);
+      if(rc == LIBSSH2_ERROR_EAGAIN) {
+        break;
       }
-      break;
-
-    case SSH_SCP_DONE:
-      if(data->set.upload) {
-        state(conn, SSH_SCP_SEND_EOF);
-      } else {
-        state(conn, SSH_SCP_CHANNEL_FREE);
+      else if(rc) {
+        infof(data, "Failed to send libssh2 channel EOF\n");
       }
-      break;
+    }
+    state(conn, SSH_SCP_WAIT_EOF);
+    break;
 
-    case SSH_SCP_SEND_EOF:
-      if(sshc->ssh_channel) {
-        rc = libssh2_channel_send_eof(sshc->ssh_channel);
-        if(rc == LIBSSH2_ERROR_EAGAIN) {
-          break;
-        }
-        else if(rc) {
-          infof(data, "Failed to send libssh2 channel EOF\n");
-        }
+  case SSH_SCP_WAIT_EOF:
+    if(sshc->ssh_channel) {
+      rc = libssh2_channel_wait_eof(sshc->ssh_channel);
+      if(rc == LIBSSH2_ERROR_EAGAIN) {
+        break;
       }
-      state(conn, SSH_SCP_WAIT_EOF);
-      break;
-
-    case SSH_SCP_WAIT_EOF:
-      if(sshc->ssh_channel) {
-        rc = libssh2_channel_wait_eof(sshc->ssh_channel);
-        if(rc == LIBSSH2_ERROR_EAGAIN) {
-          break;
-        }
-        else if(rc) {
-          infof(data, "Failed to get channel EOF\n");
-        }
+      else if(rc) {
+        infof(data, "Failed to get channel EOF\n");
       }
-      state(conn, SSH_SCP_WAIT_CLOSE);
-      break;
+    }
+    state(conn, SSH_SCP_WAIT_CLOSE);
+    break;
 
-    case SSH_SCP_WAIT_CLOSE:
-      if(sshc->ssh_channel) {
-        rc = libssh2_channel_wait_closed(sshc->ssh_channel);
-        if(rc == LIBSSH2_ERROR_EAGAIN) {
-          break;
-        }
-        else if(rc) {
-          infof(data, "Channel failed to close\n");
-        }
+  case SSH_SCP_WAIT_CLOSE:
+    if(sshc->ssh_channel) {
+      rc = libssh2_channel_wait_closed(sshc->ssh_channel);
+      if(rc == LIBSSH2_ERROR_EAGAIN) {
+        break;
       }
-      state(conn, SSH_SCP_CHANNEL_FREE);
-      break;
-
-    case SSH_SCP_CHANNEL_FREE:
-      if(sshc->ssh_channel) {
-        rc = libssh2_channel_free(sshc->ssh_channel);
-        if(rc == LIBSSH2_ERROR_EAGAIN) {
-          break;
-        }
-        else if(rc < 0) {
-          infof(data, "Failed to free libssh2 scp subsystem\n");
-        }
-        sshc->ssh_channel = NULL;
+      else if(rc) {
+        infof(data, "Channel failed to close\n");
       }
-      state(conn, SSH_SESSION_DISCONECT);
-      break;
+    }
+    state(conn, SSH_SCP_CHANNEL_FREE);
+    break;
 
-    case SSH_CHANNEL_CLOSE:
-      if(sshc->ssh_channel) {
-        rc = libssh2_channel_close(sshc->ssh_channel);
-        if(rc == LIBSSH2_ERROR_EAGAIN) {
-          break;
-        }
-        else if(rc < 0) {
-          infof(data, "Failed to stop libssh2 channel subsystem\n");
-        }
-        sshc->ssh_channel = NULL;
+  case SSH_SCP_CHANNEL_FREE:
+    if(sshc->ssh_channel) {
+      rc = libssh2_channel_free(sshc->ssh_channel);
+      if(rc == LIBSSH2_ERROR_EAGAIN) {
+        break;
       }
-      state(conn, SSH_SESSION_DISCONECT);
-      break;
-
-    case SSH_SESSION_DISCONECT:
-      if(sshc->ssh_session) {
-        rc = libssh2_session_disconnect(sshc->ssh_session, "Shutdown");
-        if(rc == LIBSSH2_ERROR_EAGAIN) {
-          break;
-        }
-        else if(rc < 0) {
-          infof(data, "Failed to disconnect libssh2 session\n");
-        }
+      else if(rc < 0) {
+        infof(data, "Failed to free libssh2 scp subsystem\n");
       }
+      sshc->ssh_channel = NULL;
+    }
+    DEBUGF(infof(data, "SCP DONE phase complete\n"));
+#if 0 /* PREV */
+    state(conn, SSH_SESSION_DISCONNECT);
+#endif
+    state(conn, SSH_STOP);
+    result = sshc->actualcode;
+    break;
 
-      Curl_safefree(sftp_scp->path);
-      sftp_scp->path = NULL;
+  case SSH_SESSION_DISCONNECT:
+    if(sshc->ssh_session) {
+      rc = libssh2_session_disconnect(sshc->ssh_session, "Shutdown");
+      if(rc == LIBSSH2_ERROR_EAGAIN) {
+        break;
+      }
+      else if(rc < 0) {
+        infof(data, "Failed to disconnect libssh2 session\n");
+      }
+    }
 
-      Curl_safefree(sftp_scp->homedir);
-      sftp_scp->homedir = NULL;
+    Curl_safefree(sshc->homedir);
+    sshc->homedir = NULL;
 
-      state(conn, SSH_SESSION_FREE);
-      break;
+    state(conn, SSH_SESSION_FREE);
+    break;
 
-    case SSH_SESSION_FREE:
-      if(sshc->ssh_session) {
-        rc = libssh2_session_free(sshc->ssh_session);
-        if(rc == LIBSSH2_ERROR_EAGAIN) {
-          break;
-        }
-        else if(rc < 0) {
-          infof(data, "Failed to free libssh2 session\n");
-        }
-        sshc->ssh_session = NULL;
+  case SSH_SESSION_FREE:
+    if(sshc->ssh_session) {
+      rc = libssh2_session_free(sshc->ssh_session);
+      if(rc == LIBSSH2_ERROR_EAGAIN) {
+        break;
       }
-      sshc->nextState = SSH_NO_STATE;
-      state(conn, SSH_STOP);
-      result = sshc->actualCode;
-      break;
-
-    case SSH_QUIT:
-      /* fallthrough, just stop! */
-    default:
-      /* internal error */
-      sshc->nextState = SSH_NO_STATE;
-      state(conn, SSH_STOP);
-      break;
+      else if(rc < 0) {
+        infof(data, "Failed to free libssh2 session\n");
+      }
+      sshc->ssh_session = NULL;
+    }
+    sshc->nextstate = SSH_NO_STATE;
+    state(conn, SSH_STOP);
+    result = sshc->actualcode;
+    break;
+
+  case SSH_QUIT:
+    /* fallthrough, just stop! */
+  default:
+    /* internal error */
+    sshc->nextstate = SSH_NO_STATE;
+    state(conn, SSH_STOP);
+    break;
   }
 
   return result;
 }
 
 /* called repeatedly until done from multi.c */
-static CURLcode Curl_ssh_multi_statemach(struct connectdata *conn, bool *done)
+static CURLcode ssh_multi_statemach(struct connectdata *conn, bool *done)
 {
   struct ssh_conn *sshc = &conn->proto.sshc;
   CURLcode result = CURLE_OK;
@@ -1830,12 +1847,6 @@ static CURLcode ssh_init(struct connectdata *conn)
   /* get some initial data into the ssh struct */
   ssh->bytecountp = &data->reqdata.keep.bytecount;
 
-  /* no need to duplicate them, this connectdata struct won't change */
-  ssh->user = conn->user;
-  ssh->passwd = conn->passwd;
-
-  ssh->errorstr = NULL;
-
   return CURLE_OK;
 }
 
@@ -1843,21 +1854,16 @@ static CURLcode ssh_init(struct connectdata *conn)
  * Curl_ssh_connect() gets called from Curl_protocol_connect() to allow us to
  * do protocol-specific actions at connect-time.
  */
-static CURLcode Curl_ssh_connect(struct connectdata *conn, bool *done)
+static CURLcode ssh_connect(struct connectdata *conn, bool *done)
 {
   struct ssh_conn *ssh;
   curl_socket_t sock;
   CURLcode result;
   struct SessionHandle *data = conn->data;
 
-#if 0
-  /* Due to the fact that the state machine always cleans up and kills all
-     handles, we cannot currently re-use SCP or SFTP connections... */
-
   /* We default to persistent connections. We set this already in this connect
      function to make the re-use checks properly be able to check this bit. */
   conn->bits.close = FALSE;
-#endif
 
   /* If there already is a protocol-specific struct allocated for this
      sessionhandle, deal with it */
@@ -1879,7 +1885,7 @@ static CURLcode Curl_ssh_connect(struct connectdata *conn, bool *done)
 #endif /* CURL_LIBSSH2_DEBUG */
   sock = conn->sock[FIRSTSOCKET];
   ssh->ssh_session = libssh2_session_init_ex(libssh2_malloc, libssh2_free,
-                                             libssh2_realloc, ssh);
+                                             libssh2_realloc, conn);
   if(ssh->ssh_session == NULL) {
     failf(data, "Failure initialising ssh session");
     return CURLE_FAILED_INIT;
@@ -1896,7 +1902,7 @@ static CURLcode Curl_ssh_connect(struct connectdata *conn, bool *done)
   state(conn, SSH_S_STARTUP);
 
   if(data->state.used_interface == Curl_if_multi)
-    result = Curl_ssh_multi_statemach(conn, done);
+    result = ssh_multi_statemach(conn, done);
   else {
     result = ssh_easy_statemach(conn);
     if(!result)
@@ -1931,8 +1937,9 @@ CURLcode scp_perform(struct connectdata *conn,
 
   /* run the state-machine */
   if(conn->data->state.used_interface == Curl_if_multi) {
-    result = Curl_ssh_multi_statemach(conn, dophase_done);
-  } else {
+    result = ssh_multi_statemach(conn, dophase_done);
+  }
+  else {
     result = ssh_easy_statemach(conn);
     *dophase_done = TRUE; /* with the easy interface we are done here */
   }
@@ -1946,11 +1953,11 @@ CURLcode scp_perform(struct connectdata *conn,
 }
 
 /* called from multi.c while DOing */
-static CURLcode Curl_scp_doing(struct connectdata *conn,
+static CURLcode scp_doing(struct connectdata *conn,
                                bool *dophase_done)
 {
   CURLcode result;
-  result = Curl_ssh_multi_statemach(conn, dophase_done);
+  result = ssh_multi_statemach(conn, dophase_done);
 
   if(*dophase_done) {
     DEBUGF(infof(conn->data, "DO phase is complete\n"));
@@ -1958,8 +1965,12 @@ static CURLcode Curl_scp_doing(struct connectdata *conn,
   return result;
 }
 
+/*
+ * The DO function is generic for both protocols. There was previously two
+ * separate ones but this way means less duplicated code.
+ */
 
-static CURLcode Curl_scp_do(struct connectdata *conn, bool *done)
+static CURLcode ssh_do(struct connectdata *conn, bool *done)
 {
   CURLcode res;
   bool connected = 0;
@@ -1975,9 +1986,8 @@ static CURLcode Curl_scp_do(struct connectdata *conn, bool *done)
    * Curl_ssh_connect() function.
    */
   res = ssh_init(conn);
-  if(res) {
+  if(res)
     return res;
-  }
 
   data->reqdata.size = -1; /* make sure this is unknown at this point */
 
@@ -1986,13 +1996,30 @@ static CURLcode Curl_scp_do(struct connectdata *conn, bool *done)
   Curl_pgrsSetUploadSize(data, 0);
   Curl_pgrsSetDownloadSize(data, 0);
 
-  res = scp_perform(conn, &connected,  done);
+  if(conn->protocol & PROT_SCP)
+    res = scp_perform(conn, &connected,  done);
+  else
+    res = sftp_perform(conn, &connected,  done);
 
   return res;
 }
 
-static CURLcode Curl_scp_done(struct connectdata *conn, CURLcode status,
-                              bool premature)
+/* BLOCKING, but the function is using the state machine so the only reason this
+   is still blocking is that the multi interface code has no support for
+   disconnecting operations that takes a while */
+static CURLcode scp_disconnect(struct connectdata *conn)
+{
+  CURLcode result;
+
+  state(conn, SSH_SESSION_DISCONNECT);
+
+  result = ssh_easy_statemach(conn);
+
+  return result;
+}
+
+static CURLcode scp_done(struct connectdata *conn, CURLcode status,
+                         bool premature)
 {
   CURLcode result = CURLE_OK;
   bool done = FALSE;
@@ -2003,7 +2030,7 @@ static CURLcode Curl_scp_done(struct connectdata *conn, CURLcode status,
     state(conn, SSH_SCP_DONE);
     /* run the state-machine */
     if(conn->data->state.used_interface == Curl_if_multi) {
-      result = Curl_ssh_multi_statemach(conn, &done);
+      result = ssh_multi_statemach(conn, &done);
     }
     else {
       result = ssh_easy_statemach(conn);
@@ -2016,7 +2043,11 @@ static CURLcode Curl_scp_done(struct connectdata *conn, CURLcode status,
   }
 
   if(done) {
-    Curl_safefree(conn->data->reqdata.proto.ssh);
+    struct SSHPROTO *sftp_scp = conn->data->reqdata.proto.ssh;
+
+    Curl_safefree(sftp_scp->path);
+
+    Curl_safefree(sftp_scp);
     conn->data->reqdata.proto.ssh = NULL;
     Curl_pgrsDone(conn);
   }
@@ -2086,7 +2117,7 @@ CURLcode sftp_perform(struct connectdata *conn,
 
   /* run the state-machine */
   if(conn->data->state.used_interface == Curl_if_multi) {
-    result = Curl_ssh_multi_statemach(conn, dophase_done);
+    result = ssh_multi_statemach(conn, dophase_done);
   }
   else {
     result = ssh_easy_statemach(conn);
@@ -2102,11 +2133,11 @@ CURLcode sftp_perform(struct connectdata *conn,
 }
 
 /* called from multi.c while DOing */
-static CURLcode Curl_sftp_doing(struct connectdata *conn,
-                                bool *dophase_done)
+static CURLcode sftp_doing(struct connectdata *conn,
+                           bool *dophase_done)
 {
   CURLcode result;
-  result = Curl_ssh_multi_statemach(conn, dophase_done);
+  result = ssh_multi_statemach(conn, dophase_done);
 
   if(*dophase_done) {
     DEBUGF(infof(conn->data, "DO phase is complete\n"));
@@ -2114,46 +2145,25 @@ static CURLcode Curl_sftp_doing(struct connectdata *conn,
   return result;
 }
 
-static CURLcode Curl_sftp_do(struct connectdata *conn, bool *done)
+/* BLOCKING, but the function is using the state machine so the only reason this
+   is still blocking is that the multi interface code has no support for
+   disconnecting operations that takes a while */
+static CURLcode sftp_disconnect(struct connectdata *conn)
 {
-  CURLcode res;
-  bool connected = 0;
-  struct SessionHandle *data = conn->data;
-
-  *done = FALSE; /* default to false */
-
-  /*
-   * Since connections can be re-used between SessionHandles, this might be a
-   * connection already existing but on a fresh SessionHandle struct so we must
-   * make sure we have a good 'struct SSHPROTO' to play with. For new
-   * connections, the struct SSHPROTO is allocated and setup in the
-   * Curl_ssh_connect() function.
-   */
-  res = ssh_init(conn);
-  if(res)
-    return res;
-
-  data->reqdata.size = -1; /* make sure this is unknown at this point */
+  CURLcode result;
 
-  Curl_pgrsSetUploadCounter(data, 0);
-  Curl_pgrsSetDownloadCounter(data, 0);
-  Curl_pgrsSetUploadSize(data, 0);
-  Curl_pgrsSetDownloadSize(data, 0);
+  DEBUGF(infof(conn->data, "SSH DISCONNECT starts now\n"));
 
-  res = sftp_perform(conn, &connected,  done);
+  state(conn, SSH_SFTP_SHUTDOWN);
+  result = ssh_easy_statemach(conn);
 
-  if(CURLE_OK == res) {
+  DEBUGF(infof(conn->data, "SSH DISCONNECT is done\n"));
 
-    if(!done) {
-      /* the DO phase has not completed yet */
-      return CURLE_OK;
-    }
-  }
+  return result;
 
-  return res;
 }
 
-static CURLcode Curl_sftp_done(struct connectdata *conn, CURLcode status,
+static CURLcode sftp_done(struct connectdata *conn, CURLcode status,
                                bool premature)
 {
   CURLcode result = CURLE_OK;
@@ -2165,7 +2175,7 @@ static CURLcode Curl_sftp_done(struct connectdata *conn, CURLcode status,
   if(status == CURLE_OK) {
     /* Before we shut down, see if there are any post-quote commands to send: */
     if(!status && !premature && conn->data->set.postquote) {
-      sshc->nextState = SSH_SFTP_CLOSE;
+      sshc->nextstate = SSH_SFTP_CLOSE;
       state(conn, SSH_SFTP_POSTQUOTE_INIT);
     }
     else
@@ -2173,7 +2183,7 @@ static CURLcode Curl_sftp_done(struct connectdata *conn, CURLcode status,
 
     /* run the state-machine */
     if(conn->data->state.used_interface == Curl_if_multi)
-      result = Curl_ssh_multi_statemach(conn, &done);
+      result = ssh_multi_statemach(conn, &done);
     else {
       result = ssh_easy_statemach(conn);
       done = TRUE;
index 228afd8..8deeded 100644 (file)
@@ -431,7 +431,8 @@ struct ftp_conn {
 typedef enum {
   SSH_NO_STATE = -1,  /* Used for "nextState" so say there is none */
   SSH_STOP = 0,       /* do nothing state, stops the state machine */
-  SSH_S_STARTUP,      /* Session startup */
+
+  SSH_S_STARTUP,      /* Session startup, First rate in SSH-CONNECT */
   SSH_AUTHLIST,
   SSH_AUTH_PKEY_INIT,
   SSH_AUTH_PKEY,
@@ -443,10 +444,10 @@ typedef enum {
   SSH_AUTH_KEY,
   SSH_AUTH_DONE,
   SSH_SFTP_INIT,
-  SSH_SFTP_REALPATH,
-  SSH_GET_WORKINGPATH,
-  SSH_SFTP_QUOTE_INIT,
-  SSH_SFTP_POSTQUOTE_INIT,
+  SSH_SFTP_REALPATH,   /* Last state in SSH-CONNECT */
+
+  SSH_SFTP_QUOTE_INIT, /* First state in SFTP-DO */
+  SSH_SFTP_POSTQUOTE_INIT, /* (Possibly) First state in SFTP-DONE */
   SSH_SFTP_QUOTE,
   SSH_SFTP_NEXT_QUOTE,
   SSH_SFTP_QUOTE_STAT,
@@ -467,20 +468,19 @@ typedef enum {
   SSH_SFTP_READDIR_BOTTOM,
   SSH_SFTP_READDIR_DONE,
   SSH_SFTP_DOWNLOAD_INIT,
-  SSH_SFTP_DOWNLOAD_STAT,
-  SSH_SFTP_CLOSE,
-  SSH_SFTP_SHUTDOWN,
-  SSH_SCP_TRANS_INIT,
+  SSH_SFTP_DOWNLOAD_STAT, /* Last state in SFTP-DO */
+  SSH_SFTP_CLOSE,    /* Last state in SFTP-DONE */
+  SSH_SFTP_SHUTDOWN, /* First state in SFTP-DISCONNECT */
+  SSH_SCP_TRANS_INIT, /* First state in SCP-DO */
   SSH_SCP_UPLOAD_INIT,
   SSH_SCP_DOWNLOAD_INIT,
   SSH_SCP_DONE,
   SSH_SCP_SEND_EOF,
   SSH_SCP_WAIT_EOF,
   SSH_SCP_WAIT_CLOSE,
-  SSH_SCP_CHANNEL_FREE,
-  SSH_CHANNEL_CLOSE,
-  SSH_SESSION_DISCONECT,
-  SSH_SESSION_FREE,
+  SSH_SCP_CHANNEL_FREE,   /* Last state in SCP-DONE */
+  SSH_SESSION_DISCONNECT, /* First state in SCP-DISCONNECT */
+  SSH_SESSION_FREE,       /* Last state in SCP/SFTP-DISCONNECT */
   SSH_QUIT,
   SSH_LAST  /* never used */
 } sshstate;
@@ -491,11 +491,7 @@ typedef enum {
    struct. */
 struct SSHPROTO {
   curl_off_t *bytecountp;
-  char *user;
-  char *passwd;
   char *path;                   /* the path we operate on */
-  char *homedir;
-  char *errorstr;
 };
 
 /* ssh_conn is used for struct connection-oriented data in the connectdata
@@ -508,12 +504,14 @@ struct ssh_conn {
   char *rsa;                  /* path name */
   bool authed;                /* the connection has been authenticated fine */
   sshstate state;             /* always use ssh.c:state() to change state! */
-  sshstate nextState;         /* the state to goto after stopping */
-  CURLcode actualCode;        /* the actual error code */
+  sshstate nextstate;         /* the state to goto after stopping */
+  CURLcode actualcode;        /* the actual error code */
   struct curl_slist *quote_item; /* for the quote option */
   char *quote_path1;          /* two generic pointers for the QUOTE stuff */
   char *quote_path2;
   LIBSSH2_SFTP_ATTRIBUTES quote_attrs; /* used by the SFTP_QUOTE state */
+  char *homedir;              /* when doing SFTP we figure out home dir in the
+                                 connect phase */
 
   /* Here's a set of struct members used by the SFTP_READDIR state */
   LIBSSH2_SFTP_ATTRIBUTES readdir_attrs;