From d17416190bcde9f59b1bc282682bc2b52548b829 Mon Sep 17 00:00:00 2001 From: Daniel Stenberg Date: Mon, 11 Jan 2010 23:10:53 +0000 Subject: ssh_statemach_act() is now modified to loop over the switch() to perform as much as possible in one go, as long as it doesn't block and hasn't reached the end of the state machine. This avoids spurious -1 returns from curl_multi_fdset() simply because previously it would return from this function without anything in EWOUDLBLOCK and thus basically it wasn't actually waiting for anything!! --- lib/ssh.c | 2937 +++++++++++++++++++++++++++++++------------------------------ 1 file changed, 1471 insertions(+), 1466 deletions(-) (limited to 'lib/ssh.c') diff --git a/lib/ssh.c b/lib/ssh.c index a93e440bc..71a8de386 100644 --- a/lib/ssh.c +++ b/lib/ssh.c @@ -5,7 +5,7 @@ * | (__| |_| | _ <| |___ * \___|\___/|_| \_\_____| * - * Copyright (C) 1998 - 2009, Daniel Stenberg, , et al. + * Copyright (C) 1998 - 2010, Daniel Stenberg, , et al. * * This software is licensed as described in the file COPYING, which * you should have received as part of this distribution. The terms @@ -459,10 +459,10 @@ static int sshkeycallback(CURL *easy, #endif /* - * ssh_statemach_act() runs the SSH statemachine "one round" and returns. The - * data the pointer 'block' points to will be set to TRUE if the libssh2 - * function returns LIBSSH2_ERROR_EAGAIN meaning it wants to be called again - * when the socket is ready + * ssh_statemach_act() runs the SSH state machine as far as it can without + * blocking and without reaching the end. The data the pointer 'block' points + * to will be set to TRUE if the libssh2 function returns LIBSSH2_ERROR_EAGAIN + * meaning it wants to be called again when the socket is ready */ static CURLcode ssh_statemach_act(struct connectdata *conn, bool *block) @@ -481,1194 +481,1181 @@ static CURLcode ssh_statemach_act(struct connectdata *conn, bool *block) int seekerr = CURL_SEEKFUNC_OK; *block = 0; /* we're not blocking by default */ - switch(sshc->state) { - case SSH_S_STARTUP: - sshc->secondCreateDirs = 0; - sshc->nextstate = SSH_NO_STATE; - sshc->actualcode = CURLE_OK; + do { - 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; - } + switch(sshc->state) { + 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; + } - /* Set libssh2 to non-blocking, since everything internally is - non-blocking */ - libssh2_session_set_blocking(sshc->ssh_session, 0); + /* Set libssh2 to non-blocking, since everything internally is + non-blocking */ + libssh2_session_set_blocking(sshc->ssh_session, 0); - state(conn, SSH_HOSTKEY); + state(conn, SSH_HOSTKEY); - /* fall-through */ - case SSH_HOSTKEY: + /* fall-through */ + case SSH_HOSTKEY: #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"); -#endif /* 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); - /* 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; + /* 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 */ -#ifdef HAVE_LIBSSH2_KNOWNHOST_API - if(data->set.str[STRING_SSH_KNOWNHOSTS]) { - /* we're asked to verify the host against a file */ - int keytype; - size_t keylen; - const char *remotekey = libssh2_session_hostkey(sshc->ssh_session, - &keylen, &keytype); - int keycheck; - int keybit; - - if(remotekey) { - /* - * A subject to figure out is what host name we need to pass in here. - * What host name does OpenSSH store in its file if an IDN name is - * used? - */ - struct libssh2_knownhost *host; - enum curl_khmatch keymatch; - curl_sshkeycallback func = - data->set.ssh_keyfunc?data->set.ssh_keyfunc:sshkeycallback; - struct curl_khkey knownkey; - struct curl_khkey *knownkeyp = NULL; - struct curl_khkey foundkey; - - keybit = (keytype == LIBSSH2_HOSTKEY_TYPE_RSA)? - LIBSSH2_KNOWNHOST_KEY_SSHRSA:LIBSSH2_KNOWNHOST_KEY_SSHDSS; - - keycheck = libssh2_knownhost_check(sshc->kh, - conn->host.name, - remotekey, keylen, - LIBSSH2_KNOWNHOST_TYPE_PLAIN| - LIBSSH2_KNOWNHOST_KEYENC_RAW| - keybit, - &host); - - infof(data, "SSH host check: %d, key: %s\n", keycheck, - (keycheck <= LIBSSH2_KNOWNHOST_CHECK_MISMATCH)? - host->key:""); - - /* setup 'knownkey' */ - if(keycheck <= LIBSSH2_KNOWNHOST_CHECK_MISMATCH) { - knownkey.key = host->key; - knownkey.len = 0; - knownkey.keytype = (keytype == LIBSSH2_HOSTKEY_TYPE_RSA)? - CURLKHTYPE_RSA : CURLKHTYPE_DSS; - knownkeyp = &knownkey; + /* 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; } + } - /* setup 'foundkey' */ - foundkey.key = remotekey; - foundkey.len = keylen; - foundkey.keytype = (keytype == LIBSSH2_HOSTKEY_TYPE_RSA)? - CURLKHTYPE_RSA : CURLKHTYPE_DSS; - - /* - * if any of the LIBSSH2_KNOWNHOST_CHECK_* defines and the - * curl_khmatch enum are ever modified, we need to introduce a - * translation table here! - */ - keymatch = (enum curl_khmatch)keycheck; +#ifdef HAVE_LIBSSH2_KNOWNHOST_API + if(data->set.str[STRING_SSH_KNOWNHOSTS]) { + /* we're asked to verify the host against a file */ + int keytype; + size_t keylen; + const char *remotekey = libssh2_session_hostkey(sshc->ssh_session, + &keylen, &keytype); + int keycheck; + int keybit; + + if(remotekey) { + /* + * A subject to figure out is what host name we need to pass in here. + * What host name does OpenSSH store in its file if an IDN name is + * used? + */ + struct libssh2_knownhost *host; + enum curl_khmatch keymatch; + curl_sshkeycallback func = + data->set.ssh_keyfunc?data->set.ssh_keyfunc:sshkeycallback; + struct curl_khkey knownkey; + struct curl_khkey *knownkeyp = NULL; + struct curl_khkey foundkey; + + keybit = (keytype == LIBSSH2_HOSTKEY_TYPE_RSA)? + LIBSSH2_KNOWNHOST_KEY_SSHRSA:LIBSSH2_KNOWNHOST_KEY_SSHDSS; + + keycheck = libssh2_knownhost_check(sshc->kh, + conn->host.name, + remotekey, keylen, + LIBSSH2_KNOWNHOST_TYPE_PLAIN| + LIBSSH2_KNOWNHOST_KEYENC_RAW| + keybit, + &host); + + infof(data, "SSH host check: %d, key: %s\n", keycheck, + (keycheck <= LIBSSH2_KNOWNHOST_CHECK_MISMATCH)? + host->key:""); + + /* setup 'knownkey' */ + if(keycheck <= LIBSSH2_KNOWNHOST_CHECK_MISMATCH) { + knownkey.key = host->key; + knownkey.len = 0; + knownkey.keytype = (keytype == LIBSSH2_HOSTKEY_TYPE_RSA)? + CURLKHTYPE_RSA : CURLKHTYPE_DSS; + knownkeyp = &knownkey; + } - /* Ask the callback how to behave */ - rc = func(data, knownkeyp, /* from the knownhosts file */ - &foundkey, /* from the remote host */ - keymatch, data->set.ssh_keyfunc_userp); - } - else - /* no remotekey means failure! */ - rc = CURLKHSTAT_REJECT; + /* setup 'foundkey' */ + foundkey.key = remotekey; + foundkey.len = keylen; + foundkey.keytype = (keytype == LIBSSH2_HOSTKEY_TYPE_RSA)? + CURLKHTYPE_RSA : CURLKHTYPE_DSS; - switch(rc) { - default: /* unknown return codes will equal reject */ - case CURLKHSTAT_REJECT: - state(conn, SSH_SESSION_FREE); - case CURLKHSTAT_DEFER: - /* DEFER means bail out but keep the SSH_HOSTKEY state */ - result = sshc->actualcode = CURLE_PEER_FAILED_VERIFICATION; - break; - case CURLKHSTAT_FINE: - case CURLKHSTAT_FINE_ADD_TO_FILE: - /* proceed */ - if(keycheck != LIBSSH2_KNOWNHOST_CHECK_MATCH) { - /* the found host+key didn't match but has been told to be fine - anyway so we add it in memory */ - int addrc = libssh2_knownhost_add(sshc->kh, - conn->host.name, NULL, - remotekey, keylen, - LIBSSH2_KNOWNHOST_TYPE_PLAIN| - LIBSSH2_KNOWNHOST_KEYENC_RAW| - keybit, NULL); - if(addrc) - infof(data, "Warning adding the known host %s failed!\n", - conn->host.name); - else if(rc == CURLKHSTAT_FINE_ADD_TO_FILE) { - /* now we write the entire in-memory list of known hosts to the - known_hosts file */ - int wrc = - libssh2_knownhost_writefile(sshc->kh, - data->set.str[STRING_SSH_KNOWNHOSTS], - LIBSSH2_KNOWNHOST_FILE_OPENSSH); - if(wrc) { - infof(data, "Warning, writing %s failed!\n", - data->set.str[STRING_SSH_KNOWNHOSTS]); + /* + * if any of the LIBSSH2_KNOWNHOST_CHECK_* defines and the + * curl_khmatch enum are ever modified, we need to introduce a + * translation table here! + */ + keymatch = (enum curl_khmatch)keycheck; + + /* Ask the callback how to behave */ + rc = func(data, knownkeyp, /* from the knownhosts file */ + &foundkey, /* from the remote host */ + keymatch, data->set.ssh_keyfunc_userp); + } + else + /* no remotekey means failure! */ + rc = CURLKHSTAT_REJECT; + + switch(rc) { + default: /* unknown return codes will equal reject */ + case CURLKHSTAT_REJECT: + state(conn, SSH_SESSION_FREE); + case CURLKHSTAT_DEFER: + /* DEFER means bail out but keep the SSH_HOSTKEY state */ + result = sshc->actualcode = CURLE_PEER_FAILED_VERIFICATION; + break; + case CURLKHSTAT_FINE: + case CURLKHSTAT_FINE_ADD_TO_FILE: + /* proceed */ + if(keycheck != LIBSSH2_KNOWNHOST_CHECK_MATCH) { + /* the found host+key didn't match but has been told to be fine + anyway so we add it in memory */ + int addrc = libssh2_knownhost_add(sshc->kh, + conn->host.name, NULL, + remotekey, keylen, + LIBSSH2_KNOWNHOST_TYPE_PLAIN| + LIBSSH2_KNOWNHOST_KEYENC_RAW| + keybit, NULL); + if(addrc) + infof(data, "Warning adding the known host %s failed!\n", + conn->host.name); + else if(rc == CURLKHSTAT_FINE_ADD_TO_FILE) { + /* now we write the entire in-memory list of known hosts to the + known_hosts file */ + int wrc = + libssh2_knownhost_writefile(sshc->kh, + data->set.str[STRING_SSH_KNOWNHOSTS], + LIBSSH2_KNOWNHOST_FILE_OPENSSH); + if(wrc) { + infof(data, "Warning, writing %s failed!\n", + data->set.str[STRING_SSH_KNOWNHOSTS]); + } } } + break; } - break; } - } #endif /* HAVE_LIBSSH2_KNOWNHOST_API */ - state(conn, SSH_AUTHLIST); - break; + state(conn, SSH_AUTHLIST); + break; - case SSH_AUTHLIST: - /* - * 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) { - rc = LIBSSH2_ERROR_EAGAIN; - break; - } - else { - state(conn, SSH_SESSION_FREE); - sshc->actualcode = libssh2_session_error_to_CURLE(err); - break; + case SSH_AUTHLIST: + /* + * 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) { + rc = LIBSSH2_ERROR_EAGAIN; + break; + } + 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); + infof(data, "SSH authentication methods available: %s\n", sshc->authlist); - state(conn, SSH_AUTH_PKEY_INIT); - break; + 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; + 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; - if((data->set.ssh_auth_types & CURLSSH_AUTH_PUBLICKEY) && - (strstr(sshc->authlist, "publickey") != NULL)) { - char *home; + if((data->set.ssh_auth_types & CURLSSH_AUTH_PUBLICKEY) && + (strstr(sshc->authlist, "publickey") != NULL)) { + char *home; - sshc->rsa_pub = sshc->rsa = NULL; + sshc->rsa_pub = sshc->rsa = NULL; - /* To ponder about: should really the lib be messing about with the - HOME environment variable etc? */ - home = curl_getenv("HOME"); + /* To ponder about: should really the lib be messing about with the + HOME environment variable etc? */ + home = curl_getenv("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(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; - } + if(sshc->rsa_pub == NULL) { + Curl_safefree(home); + home = NULL; + state(conn, SSH_SESSION_FREE); + sshc->actualcode = CURLE_OUT_OF_MEMORY; + 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"); + 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; + } + + sshc->passphrase = data->set.str[STRING_KEY_PASSWD]; + if(!sshc->passphrase) + sshc->passphrase = ""; - 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; - } - - sshc->passphrase = data->set.str[STRING_KEY_PASSWD]; - if(!sshc->passphrase) - sshc->passphrase = ""; - 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); - 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, - conn->user, sshc->rsa_pub, - sshc->rsa, sshc->passphrase); - if(rc == LIBSSH2_ERROR_EAGAIN) { + state(conn, SSH_AUTH_PKEY); + } + else { + state(conn, SSH_AUTH_PASS_INIT); + } 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 { - char *err_msg; - (void)libssh2_session_last_error(sshc->ssh_session, - &err_msg, NULL, 0); - infof(data, "SSH public key authentication failed: %s\n", err_msg); - 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_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; + Curl_safefree(sshc->rsa_pub); + sshc->rsa_pub = NULL; + Curl_safefree(sshc->rsa); + sshc->rsa = NULL; - case SSH_AUTH_PASS: - rc = libssh2_userauth_password(sshc->ssh_session, conn->user, - conn->passwd); - if(rc == LIBSSH2_ERROR_EAGAIN) { + if(rc == 0) { + sshc->authed = TRUE; + infof(data, "Initialized SSH public key authentication\n"); + state(conn, SSH_AUTH_DONE); + } + else { + char *err_msg; + (void)libssh2_session_last_error(sshc->ssh_session, + &err_msg, NULL, 0); + infof(data, "SSH public key authentication failed: %s\n", err_msg); + state(conn, SSH_AUTH_PASS_INIT); + } 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; - - case SSH_AUTH_HOST: - state(conn, SSH_AUTH_KEY_INIT); - break; - - 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) { + 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; - } - else if(rc == 0) { - sshc->authed = TRUE; - infof(data, "Initialized keyboard interactive authentication\n"); - } - state(conn, SSH_AUTH_DONE); - break; - case SSH_AUTH_DONE: - if(!sshc->authed) { - failf(data, "Authentication failure"); - state(conn, SSH_SESSION_FREE); - sshc->actualcode = CURLE_LOGIN_DENIED; + 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; - } - /* - * At this point we have an authenticated ssh session. - */ - infof(data, "Authentication complete\n"); - - Curl_pgrsTime(conn->data, TIMER_APPCONNECT); /* SSH is connected */ + 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; - conn->sockfd = sock; - conn->writesockfd = CURL_SOCKET_BAD; + case SSH_AUTH_HOST: + state(conn, SSH_AUTH_KEY_INIT); + break; - if(conn->protocol == PROT_SFTP) { - state(conn, SSH_SFTP_INIT); + 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, "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) { - rc = LIBSSH2_ERROR_EAGAIN; + 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 { - char *err_msg; + else if(rc == 0) { + sshc->authed = TRUE; + infof(data, "Initialized keyboard interactive authentication\n"); + } + state(conn, SSH_AUTH_DONE); + break; - (void)libssh2_session_last_error(sshc->ssh_session, - &err_msg, NULL, 0); - failf(data, "Failure initializing sftp session: %s", err_msg); + case SSH_AUTH_DONE: + if(!sshc->authed) { + failf(data, "Authentication failure"); state(conn, SSH_SESSION_FREE); - sshc->actualcode = CURLE_FAILED_INIT; + sshc->actualcode = CURLE_LOGIN_DENIED; break; } - } - state(conn, SSH_SFTP_REALPATH); - break; - case SSH_SFTP_REALPATH: - { - char tempHome[PATH_MAX]; + /* + * At this point we have an authenticated ssh session. + */ + infof(data, "Authentication complete\n"); - /* - * 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'; - sshc->homedir = strdup(tempHome); - if(!sshc->homedir) { - state(conn, SSH_SFTP_CLOSE); - sshc->actualcode = CURLE_OUT_OF_MEMORY; + Curl_pgrsTime(conn->data, TIMER_APPCONNECT); /* SSH is connected */ + + conn->sockfd = sock; + conn->writesockfd = CURL_SOCKET_BAD; + + if(conn->protocol == PROT_SFTP) { + state(conn, SSH_SFTP_INIT); break; } - } - else { - /* Return the error type */ - err = libssh2_sftp_last_error(sshc->sftp_session); - result = sftp_libssh2_error_to_CURLE(err); - sshc->actualcode = result?result:CURLE_SSH; - DEBUGF(infof(data, "error = %d makes libcurl = %d\n", err, result)); + infof(data, "SSH CONNECT phase done\n"); 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_INIT: + 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) { + rc = LIBSSH2_ERROR_EAGAIN; + break; + } + else { + char *err_msg; - result = ssh_getworkingpath(conn, sshc->homedir, &sftp_scp->path); - if(result) { - sshc->actualcode = result; - state(conn, SSH_STOP); + (void)libssh2_session_last_error(sshc->ssh_session, + &err_msg, NULL, 0); + failf(data, "Failure initializing sftp session: %s", err_msg); + state(conn, SSH_SESSION_FREE); + sshc->actualcode = CURLE_FAILED_INIT; + break; + } + } + state(conn, SSH_SFTP_REALPATH); 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; - 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_QUOTE: - /* Send any quote commands */ - { - const char *cp; + case SSH_SFTP_REALPATH: + { + char tempHome[PATH_MAX]; - /* - * Support some of the "FTP" commands - */ - if(curl_strequal("pwd", sshc->quote_item->data)) { - /* 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) { /* - * the arguments following the command must be separated from the - * command with a space so we can check for it unconditionally + * Get the "home" directory */ - 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; + 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'; + sshc->homedir = 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); + sshc->actualcode = result?result:CURLE_SSH; + 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; - /* - * also, every command takes at least one argument so we get that - * first argument right now - */ - result = get_pathname(&cp, &sshc->quote_path1); + case SSH_SFTP_QUOTE_INIT: + + result = ssh_getworkingpath(conn, sshc->homedir, &sftp_scp->path); if(result) { - if(result == CURLE_OUT_OF_MEMORY) - failf(data, "Out of memory"); - else - failf(data, "Syntax error: Bad first parameter"); - state(conn, SSH_SFTP_CLOSE); sshc->actualcode = result; + state(conn, SSH_STOP); 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; + + 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_QUOTE: + /* Send any quote commands */ + { + const char *cp; + /* - * 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. + * Support some of the "FTP" commands */ - 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 */ - result = get_pathname(&cp, &sshc->quote_path2); + if(curl_strequal("pwd", sshc->quote_item->data)) { + /* 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) { + /* + * 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; + } + + /* + * also, every command takes at least one argument so we get that + * first argument right now + */ + result = get_pathname(&cp, &sshc->quote_path1); if(result) { if(result == 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; + failf(data, "Syntax error: Bad first parameter"); state(conn, SSH_SFTP_CLOSE); sshc->actualcode = result; break; } - memset(&sshc->quote_attrs, 0, sizeof(LIBSSH2_SFTP_ATTRIBUTES)); - state(conn, SSH_SFTP_QUOTE_STAT); + + /* + * 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 */ + result = get_pathname(&cp, &sshc->quote_path2); + if(result) { + if(result == 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 = result; + 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 */ + result = get_pathname(&cp, &sshc->quote_path2); + if(result) { + if(result == 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 = result; + 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 */ + result = get_pathname(&cp, &sshc->quote_path2); + if(result) { + if(result == 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 = result; + 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; + } + + failf(data, "Unknown SFTP command"); + Curl_safefree(sshc->quote_path1); + sshc->quote_path1 = NULL; + Curl_safefree(sshc->quote_path2); + sshc->quote_path2 = NULL; + state(conn, SSH_SFTP_CLOSE); + sshc->actualcode = CURLE_QUOTE_ERROR; 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 */ - result = get_pathname(&cp, &sshc->quote_path2); - if(result) { - if(result == CURLE_OUT_OF_MEMORY) - failf(data, "Out of memory"); - else - failf(data, - "Syntax error in ln/symlink: Bad second parameter"); + } + if(!sshc->quote_item) { + state(conn, SSH_SFTP_TRANS_INIT); + } + 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 { + state(conn, SSH_SFTP_TRANS_INIT); + } + } + break; + + case SSH_SFTP_QUOTE_STAT: + if(!curl_strnequal(sshc->quote_item->data, "chmod", 5)) { + /* Since chown and chgrp only set owner OR group but libssh2 wants to + * set them both at once, we need to obtain the current ownership first. + * This takes an extra protocol round trip. + */ + 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 = result; + sshc->actualcode = CURLE_QUOTE_ERROR; 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 */ - result = get_pathname(&cp, &sshc->quote_path2); - if(result) { - if(result == CURLE_OUT_OF_MEMORY) - failf(data, "Out of memory"); - else - failf(data, "Syntax error in rename: Bad second parameter"); + + /* Now set the new attributes... */ + if(curl_strnequal(sshc->quote_item->data, "chgrp", 5)) { + sshc->quote_attrs.gid = strtol(sshc->quote_path1, NULL, 10); + sshc->quote_attrs.flags = LIBSSH2_SFTP_ATTR_UIDGID; + 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, "Syntax error: chgrp gid not a number"); state(conn, SSH_SFTP_CLOSE); - sshc->actualcode = result; + sshc->actualcode = CURLE_QUOTE_ERROR; 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, "chmod", 5)) { + sshc->quote_attrs.permissions = strtol(sshc->quote_path1, NULL, 8); + sshc->quote_attrs.flags = LIBSSH2_SFTP_ATTR_PERMISSIONS; + /* permissions are octal */ + if(sshc->quote_attrs.permissions == 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, "Syntax error: chmod permissions not a number"); + state(conn, SSH_SFTP_CLOSE); + sshc->actualcode = CURLE_QUOTE_ERROR; + break; + } } - else if(curl_strnequal(sshc->quote_item->data, "rm ", 3)) { - state(conn, SSH_SFTP_QUOTE_UNLINK); - break; + else if(curl_strnequal(sshc->quote_item->data, "chown", 5)) { + sshc->quote_attrs.uid = strtol(sshc->quote_path1, NULL, 10); + sshc->quote_attrs.flags = LIBSSH2_SFTP_ATTR_UIDGID; + if(sshc->quote_attrs.uid == 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, "Syntax error: chown uid not a number"); + state(conn, SSH_SFTP_CLOSE); + sshc->actualcode = CURLE_QUOTE_ERROR; + break; + } } - failf(data, "Unknown SFTP command"); - Curl_safefree(sshc->quote_path1); - sshc->quote_path1 = NULL; - Curl_safefree(sshc->quote_path2); - sshc->quote_path2 = NULL; - state(conn, SSH_SFTP_CLOSE); - sshc->actualcode = CURLE_QUOTE_ERROR; + /* Now send the completed structure... */ + state(conn, SSH_SFTP_QUOTE_SETSTAT); break; - } - } - if(!sshc->quote_item) { - state(conn, SSH_SFTP_TRANS_INIT); - } - 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 { - state(conn, SSH_SFTP_TRANS_INIT); - } - } - break; - case SSH_SFTP_QUOTE_STAT: - if(!curl_strnequal(sshc->quote_item->data, "chmod", 5)) { - /* Since chown and chgrp only set owner OR group but libssh2 wants to - * set them both at once, we need to obtain the current ownership first. - * This takes an extra protocol round trip. - */ - rc = libssh2_sftp_stat(sshc->sftp_session, sshc->quote_path2, - &sshc->quote_attrs); + 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) { /* get those attributes */ + 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 get SFTP stats failed: %s", + 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; - /* Now set the new attributes... */ - if(curl_strnequal(sshc->quote_item->data, "chgrp", 5)) { - sshc->quote_attrs.gid = strtol(sshc->quote_path1, NULL, 10); - sshc->quote_attrs.flags = LIBSSH2_SFTP_ATTR_UIDGID; - if(sshc->quote_attrs.gid == 0 && !ISDIGIT(sshc->quote_path1[0])) { + 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, "Syntax error: chgrp gid not a number"); + failf(data, "symlink command failed: %s", + sftp_libssh2_strerror(err)); state(conn, SSH_SFTP_CLOSE); sshc->actualcode = CURLE_QUOTE_ERROR; break; } - } - else if(curl_strnequal(sshc->quote_item->data, "chmod", 5)) { - sshc->quote_attrs.permissions = strtol(sshc->quote_path1, NULL, 8); - sshc->quote_attrs.flags = LIBSSH2_SFTP_ATTR_PERMISSIONS; - /* permissions are octal */ - if(sshc->quote_attrs.permissions == 0 && - !ISDIGIT(sshc->quote_path1[0])) { + state(conn, SSH_SFTP_NEXT_QUOTE); + break; + + 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; - Curl_safefree(sshc->quote_path2); - sshc->quote_path2 = NULL; - failf(data, "Syntax error: chmod permissions not a number"); + failf(data, "mkdir command failed: %s", sftp_libssh2_strerror(err)); state(conn, SSH_SFTP_CLOSE); sshc->actualcode = CURLE_QUOTE_ERROR; break; } - } - else if(curl_strnequal(sshc->quote_item->data, "chown", 5)) { - sshc->quote_attrs.uid = strtol(sshc->quote_path1, NULL, 10); - sshc->quote_attrs.flags = LIBSSH2_SFTP_ATTR_UIDGID; - if(sshc->quote_attrs.uid == 0 && !ISDIGIT(sshc->quote_path1[0])) { + state(conn, SSH_SFTP_NEXT_QUOTE); + 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); 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"); + failf(data, "rename command failed: %s", sftp_libssh2_strerror(err)); state(conn, SSH_SFTP_CLOSE); sshc->actualcode = CURLE_QUOTE_ERROR; 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) { - 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; - - 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; - - 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; + state(conn, SSH_SFTP_NEXT_QUOTE); break; - } - state(conn, SSH_SFTP_NEXT_QUOTE); - 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); - 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; + 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; - } - 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); - 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; + 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; - } - 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) { - 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; + case SSH_SFTP_TRANS_INIT: + if(data->set.upload) + state(conn, SSH_SFTP_UPLOAD_INIT); + else { + if(data->set.opt_no_body) + state(conn, SSH_STOP); + else if(sftp_scp->path[strlen(sftp_scp->path)-1] == '/') + state(conn, SSH_SFTP_READDIR_INIT); + else + state(conn, SSH_SFTP_DOWNLOAD_INIT); + } 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(data->set.opt_no_body) - state(conn, SSH_STOP); - 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: + { + unsigned long flags; + /* + * 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. + */ - case SSH_SFTP_UPLOAD_INIT: - { - unsigned long flags; - /* - * 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. - */ - - if(data->state.resume_from != 0) { - LIBSSH2_SFTP_ATTRIBUTES attrs; - if(data->state.resume_from< 0) { - rc = libssh2_sftp_stat(sshc->sftp_session, sftp_scp->path, &attrs); - if(rc == LIBSSH2_ERROR_EAGAIN) { - break; - } - else if(rc) { - data->state.resume_from = 0; - } - else { - data->state.resume_from = attrs.filesize; + if(data->state.resume_from != 0) { + LIBSSH2_SFTP_ATTRIBUTES attrs; + if(data->state.resume_from< 0) { + rc = libssh2_sftp_stat(sshc->sftp_session, sftp_scp->path, &attrs); + if(rc == LIBSSH2_ERROR_EAGAIN) { + break; + } + else if(rc) { + data->state.resume_from = 0; + } + else { + data->state.resume_from = attrs.filesize; + } } } - } - if(data->set.ftp_append) - /* Try to open for append, but create if nonexisting */ - flags = LIBSSH2_FXF_WRITE|LIBSSH2_FXF_CREAT|LIBSSH2_FXF_APPEND; - else if (data->state.resume_from > 0) - /* If we have restart position then open for append */ - flags = LIBSSH2_FXF_WRITE|LIBSSH2_FXF_APPEND; - else - /* Clear file before writing (normal behaviour) */ - flags = LIBSSH2_FXF_WRITE|LIBSSH2_FXF_CREAT|LIBSSH2_FXF_TRUNC; + if(data->set.ftp_append) + /* Try to open for append, but create if nonexisting */ + flags = LIBSSH2_FXF_WRITE|LIBSSH2_FXF_CREAT|LIBSSH2_FXF_APPEND; + else if (data->state.resume_from > 0) + /* If we have restart position then open for append */ + flags = LIBSSH2_FXF_WRITE|LIBSSH2_FXF_APPEND; + else + /* Clear file before writing (normal behaviour) */ + flags = LIBSSH2_FXF_WRITE|LIBSSH2_FXF_CREAT|LIBSSH2_FXF_TRUNC; - sshc->sftp_handle = - libssh2_sftp_open(sshc->sftp_session, sftp_scp->path, - flags, data->set.new_file_perms); + sshc->sftp_handle = + libssh2_sftp_open(sshc->sftp_session, sftp_scp->path, + flags, data->set.new_file_perms); - if(!sshc->sftp_handle) { - rc = libssh2_session_last_errno(sshc->ssh_session); + if(!sshc->sftp_handle) { + rc = libssh2_session_last_errno(sshc->ssh_session); - if(LIBSSH2_ERROR_EAGAIN == rc) - break; - else { - if(LIBSSH2_ERROR_SFTP_PROTOCOL == rc) - /* only when there was an SFTP protocol error can we extract - the sftp error! */ - err = libssh2_sftp_last_error(sshc->sftp_session); - else - err = -1; /* not an sftp error at all */ + if(LIBSSH2_ERROR_EAGAIN == rc) + break; + else { + if(LIBSSH2_ERROR_SFTP_PROTOCOL == rc) + /* only when there was an SFTP protocol error can we extract + the sftp error! */ + err = libssh2_sftp_last_error(sshc->sftp_session); + else + err = -1; /* not an sftp error at all */ - if(sshc->secondCreateDirs) { + if(sshc->secondCreateDirs) { + state(conn, SSH_SFTP_CLOSE); + sshc->actualcode = err>= LIBSSH2_FX_OK? + sftp_libssh2_error_to_CURLE(err):CURLE_SSH; + failf(data, "Creating the dir/file failed: %s", + sftp_libssh2_strerror(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 = err>= LIBSSH2_FX_OK? sftp_libssh2_error_to_CURLE(err):CURLE_SSH; - failf(data, "Creating the dir/file failed: %s", - sftp_libssh2_strerror(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); + if(!sshc->actualcode) { + /* Sometimes, for some reason libssh2_sftp_last_error() returns + zero even though libssh2_sftp_open() failed previously! We need + to work around that! */ + sshc->actualcode = CURLE_SSH; + err=-1; + } + failf(data, "Upload failed: %s (%d/%d)", + err>= LIBSSH2_FX_OK?sftp_libssh2_strerror(err):"ssh error", + err, rc); break; } - state(conn, SSH_SFTP_CLOSE); - sshc->actualcode = err>= LIBSSH2_FX_OK? - sftp_libssh2_error_to_CURLE(err):CURLE_SSH; - if(!sshc->actualcode) { - /* Sometimes, for some reason libssh2_sftp_last_error() returns zero - even though libssh2_sftp_open() failed previously! We need to - work around that! */ - sshc->actualcode = CURLE_SSH; - err=-1; - } - failf(data, "Upload failed: %s (%d/%d)", - err>= LIBSSH2_FX_OK?sftp_libssh2_strerror(err):"ssh error", - err, rc); - break; } - } - /* If we have restart point then we need to seek to the correct position. */ - if(data->state.resume_from > 0) { - /* Let's read off the proper amount of bytes from the input. */ - if(conn->seek_func) { - seekerr = conn->seek_func(conn->seek_client, data->state.resume_from, - SEEK_SET); - } + /* If we have restart point then we need to seek to the correct + position. */ + if(data->state.resume_from > 0) { + /* Let's read off the proper amount of bytes from the input. */ + if(conn->seek_func) { + seekerr = conn->seek_func(conn->seek_client, data->state.resume_from, + SEEK_SET); + } - if(seekerr != CURL_SEEKFUNC_OK){ + if(seekerr != CURL_SEEKFUNC_OK){ - if(seekerr != CURL_SEEKFUNC_CANTSEEK) { - failf(data, "Could not seek stream"); - return CURLE_FTP_COULDNT_USE_REST; + if(seekerr != CURL_SEEKFUNC_CANTSEEK) { + failf(data, "Could not seek stream"); + return CURLE_FTP_COULDNT_USE_REST; + } + /* seekerr == CURL_SEEKFUNC_CANTSEEK (can't seek to offset) */ + else { + curl_off_t passed=0; + curl_off_t readthisamountnow; + curl_off_t actuallyread; + do { + readthisamountnow = (data->state.resume_from - passed); + + if(readthisamountnow > BUFSIZE) + readthisamountnow = BUFSIZE; + + actuallyread = + (curl_off_t) conn->fread_func(data->state.buffer, 1, + (size_t)readthisamountnow, + conn->fread_in); + + passed += actuallyread; + if((actuallyread <= 0) || (actuallyread > readthisamountnow)) { + /* this checks for greater-than only to make sure that the + CURL_READFUNC_ABORT return code still aborts */ + failf(data, "Failed to read data"); + return CURLE_FTP_COULDNT_USE_REST; + } + } while(passed < data->state.resume_from); + } } - /* seekerr == CURL_SEEKFUNC_CANTSEEK (can't seek to offset) */ - else { - curl_off_t passed=0; - curl_off_t readthisamountnow; - curl_off_t actuallyread; - do { - readthisamountnow = (data->state.resume_from - passed); - - if(readthisamountnow > BUFSIZE) - readthisamountnow = BUFSIZE; - - actuallyread = - (curl_off_t) conn->fread_func(data->state.buffer, 1, - (size_t)readthisamountnow, - conn->fread_in); - - passed += actuallyread; - if((actuallyread <= 0) || (actuallyread > readthisamountnow)) { - /* this checks for greater-than only to make sure that the - CURL_READFUNC_ABORT return code still aborts */ - failf(data, "Failed to read data"); - return CURLE_FTP_COULDNT_USE_REST; - } - } while(passed < data->state.resume_from); + + /* now, decrease the size of the read */ + if(data->set.infilesize>0) { + data->set.infilesize -= data->state.resume_from; + data->req.size = data->set.infilesize; + Curl_pgrsSetUploadSize(data, data->set.infilesize); } - } - /* now, decrease the size of the read */ + SFTP_SEEK(sshc->sftp_handle, data->state.resume_from); + } if(data->set.infilesize>0) { - data->set.infilesize -= data->state.resume_from; data->req.size = data->set.infilesize; Curl_pgrsSetUploadSize(data, data->set.infilesize); } + /* upload data */ + result = Curl_setup_transfer(conn, -1, -1, FALSE, NULL, + FIRSTSOCKET, NULL); - SFTP_SEEK(sshc->sftp_handle, data->state.resume_from); - } - if(data->set.infilesize>0) { - data->req.size = data->set.infilesize; - Curl_pgrsSetUploadSize(data, data->set.infilesize); - } - /* upload data */ - result = Curl_setup_transfer(conn, -1, -1, FALSE, NULL, - FIRSTSOCKET, NULL); - - /* not set by Curl_setup_transfer to preserve keepon bits */ - conn->sockfd = conn->writesockfd; + /* not set by Curl_setup_transfer to preserve keepon bits */ + conn->sockfd = conn->writesockfd; - if(result) { - state(conn, SSH_SFTP_CLOSE); - sshc->actualcode = result; - } - else { - /* store this original bitmask setup to use later on if we can't figure - out a "real" bitmask */ - sshc->orig_waitfor = data->req.keepon; - - 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; + if(result) { + state(conn, SSH_SFTP_CLOSE); + sshc->actualcode = result; + } + else { + /* store this original bitmask setup to use later on if we can't + figure out a "real" bitmask */ + sshc->orig_waitfor = data->req.keepon; - infof(data, "Creating directory '%s'\n", sftp_scp->path); - state(conn, SSH_SFTP_CREATE_DIRS_MKDIR); + state(conn, SSH_STOP); + } break; } - else { - state(conn, SSH_SFTP_UPLOAD_INIT); - } - 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) { - 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); - state(conn, SSH_SFTP_CLOSE); - sshc->actualcode = result?result:CURLE_SSH; - 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); } - } - state(conn, SSH_SFTP_CREATE_DIRS); - break; + else { + state(conn, SSH_SFTP_UPLOAD_INIT); + } + 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) { - rc = LIBSSH2_ERROR_EAGAIN; + 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 { - 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); - result = sftp_libssh2_error_to_CURLE(err); - sshc->actualcode = result?result:CURLE_SSH; - break; + state(conn, SSH_SFTP_UPLOAD_INIT); } - } - if((sshc->readdir_filename = malloc(PATH_MAX+1)) == NULL) { - state(conn, SSH_SFTP_CLOSE); - sshc->actualcode = CURLE_OUT_OF_MEMORY; break; - } - if((sshc->readdir_longentry = 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) { - rc = LIBSSH2_ERROR_EAGAIN; - break; - } - if(sshc->readdir_len > 0) { - sshc->readdir_filename[sshc->readdir_len] = '\0'; - - if(data->set.ftp_list_only) { - char *tmpLine; - tmpLine = aprintf("%s\n", sshc->readdir_filename); - if(tmpLine == NULL) { + 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; + /* + * 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); state(conn, SSH_SFTP_CLOSE); - sshc->actualcode = CURLE_OUT_OF_MEMORY; + sshc->actualcode = result?result:CURLE_SSH; break; } - result = Curl_client_write(conn, CLIENTWRITE_BODY, - tmpLine, sshc->readdir_len+1); - Curl_safefree(tmpLine); + } + state(conn, SSH_SFTP_CREATE_DIRS); + break; - if(result) { - state(conn, SSH_STOP); + 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) { + rc = LIBSSH2_ERROR_EAGAIN; break; } - /* since this counts what we send to the client, we include the newline - in this counter */ - data->req.bytecount += sshc->readdir_len+1; - - /* 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 = 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; + 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; + result = sftp_libssh2_error_to_CURLE(err); + sshc->actualcode = result?result:CURLE_SSH; break; } + } + if((sshc->readdir_filename = malloc(PATH_MAX+1)) == NULL) { + state(conn, SSH_SFTP_CLOSE); + sshc->actualcode = CURLE_OUT_OF_MEMORY; + break; + } + if((sshc->readdir_longentry = 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) { + rc = LIBSSH2_ERROR_EAGAIN; + break; + } + if(sshc->readdir_len > 0) { + sshc->readdir_filename[sshc->readdir_len] = '\0'; - 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 = malloc(PATH_MAX + 1); - if(sshc->readdir_linkPath == NULL) { + 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, sshc->readdir_len+1); + Curl_safefree(tmpLine); + + if(result) { + state(conn, SSH_STOP); + break; + } + /* since this counts what we send to the client, we include the + newline in this counter */ + data->req.bytecount += sshc->readdir_len+1; + + /* 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 = calloc(sshc->readdir_totalLen, 1); + if(!sshc->readdir_line) { Curl_safefree(sshc->readdir_filename); sshc->readdir_filename = NULL; Curl_safefree(sshc->readdir_longentry); @@ -1678,563 +1665,581 @@ static CURLcode ssh_statemach_act(struct connectdata *conn, bool *block) break; } - snprintf(sshc->readdir_linkPath, PATH_MAX, "%s%s", sftp_scp->path, - sshc->readdir_filename); - state(conn, SSH_SFTP_READDIR_LINK); + 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 = 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); break; } - state(conn, SSH_SFTP_READDIR_BOTTOM); + } + 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); + result = sftp_libssh2_error_to_CURLE(err); + sshc->actualcode = result?result:CURLE_SSH; + 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; } - } - 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); - result = sftp_libssh2_error_to_CURLE(err); - sshc->actualcode = result?result:CURLE_SSH; - 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) { - rc = 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) { + rc = 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; + } - sshc->readdir_currLen += snprintf(sshc->readdir_line + - sshc->readdir_currLen, - sshc->readdir_totalLen - - sshc->readdir_currLen, - " -> %s", - sshc->readdir_filename); + 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; + 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, - sshc->readdir_currLen); + 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, + sshc->readdir_currLen); - if(result == CURLE_OK) { + if(result == CURLE_OK) { - /* output debug output if that is requested */ - if(data->set.verbose) { - Curl_debug(data, CURLINFO_DATA_OUT, sshc->readdir_line, - sshc->readdir_currLen, conn); + /* output debug output if that is requested */ + if(data->set.verbose) { + Curl_debug(data, CURLINFO_DATA_OUT, sshc->readdir_line, + sshc->readdir_currLen, conn); + } + data->req.bytecount += sshc->readdir_currLen; } - data->req.bytecount += sshc->readdir_currLen; - } - Curl_safefree(sshc->readdir_line); - sshc->readdir_line = NULL; - if(result) { - state(conn, SSH_STOP); - } - else - state(conn, SSH_SFTP_READDIR); - break; - - case SSH_SFTP_READDIR_DONE: - if(libssh2_sftp_closedir(sshc->sftp_handle) == - LIBSSH2_ERROR_EAGAIN) { - rc = LIBSSH2_ERROR_EAGAIN; + Curl_safefree(sshc->readdir_line); + sshc->readdir_line = NULL; + if(result) { + state(conn, SSH_STOP); + } + else + state(conn, SSH_SFTP_READDIR); 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) == + case SSH_SFTP_READDIR_DONE: + if(libssh2_sftp_closedir(sshc->sftp_handle) == LIBSSH2_ERROR_EAGAIN) { rc = 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); - result = sftp_libssh2_error_to_CURLE(err); - sshc->actualcode = result?result:CURLE_SSH; - break; - } - } - state(conn, SSH_SFTP_DOWNLOAD_STAT); - break; - - case SSH_SFTP_DOWNLOAD_STAT: - { - LIBSSH2_SFTP_ATTRIBUTES attrs; + sshc->sftp_handle = NULL; + Curl_safefree(sshc->readdir_filename); + sshc->readdir_filename = NULL; + Curl_safefree(sshc->readdir_longentry); + sshc->readdir_longentry = NULL; - rc = libssh2_sftp_stat(sshc->sftp_session, sftp_scp->path, &attrs); - if(rc == LIBSSH2_ERROR_EAGAIN) { + /* no data to transfer */ + result = Curl_setup_transfer(conn, -1, -1, FALSE, NULL, -1, NULL); + state(conn, SSH_STOP); break; - } - else if(rc) { + + case SSH_SFTP_DOWNLOAD_INIT: /* - * libssh2_sftp_open() didn't return an error, so maybe the server - * just doesn't support stat() + * Work on getting the specified file */ - data->req.size = -1; - data->req.maxdownload = -1; - } - else { - curl_off_t size; - - size = attrs.filesize; - if(conn->data->state.use_range) { - curl_off_t from, to; - char *ptr; - char *ptr2; - - from=curlx_strtoofft(conn->data->state.range, &ptr, 0); - while(ptr && *ptr && (isspace((int)*ptr) || (*ptr=='-'))) - ptr++; - to=curlx_strtoofft(ptr, &ptr2, 0); - if((ptr == ptr2) /* no "to" value given */ - || (to >= size)) { - to = size - 1; - } - if(from < 0) { - /* from is relative to end of file */ - from += size; - } - if(from >= size) { - failf(data, "Offset (%" - FORMAT_OFF_T ") was beyond file size (%" FORMAT_OFF_T ")", - from, attrs.filesize); - return CURLE_BAD_DOWNLOAD_RESUME; - } - if(from > to) { - from = to; - size = 0; + 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) { + rc = LIBSSH2_ERROR_EAGAIN; + break; } else { - size = to - from + 1; + 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); + result = sftp_libssh2_error_to_CURLE(err); + sshc->actualcode = result?result:CURLE_SSH; + break; } - - SFTP_SEEK(conn->proto.sshc.sftp_handle, from); } - data->req.size = size; - data->req.maxdownload = size; - Curl_pgrsSetDownloadSize(data, size); - } + state(conn, SSH_SFTP_DOWNLOAD_STAT); + break; - /* We can resume if we can seek to the resume position */ - if(data->state.resume_from) { - if(data->state.resume_from< 0) { - /* We're supposed to download the last abs(from) bytes */ - if((curl_off_t)attrs.filesize < -data->state.resume_from) { - failf(data, "Offset (%" - FORMAT_OFF_T ") was beyond file size (%" FORMAT_OFF_T ")", - data->state.resume_from, attrs.filesize); - return CURLE_BAD_DOWNLOAD_RESUME; - } - /* download from where? */ - data->state.resume_from = attrs.filesize - data->state.resume_from; + case SSH_SFTP_DOWNLOAD_STAT: + { + LIBSSH2_SFTP_ATTRIBUTES attrs; + + 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->req.size = -1; + data->req.maxdownload = -1; } else { - if((curl_off_t)attrs.filesize < data->state.resume_from) { - failf(data, "Offset (%" FORMAT_OFF_T - ") was beyond file size (%" FORMAT_OFF_T ")", - data->state.resume_from, attrs.filesize); - return CURLE_BAD_DOWNLOAD_RESUME; - } - } - /* Does a completed file need to be seeked and started or closed ? */ - /* Now store the number of bytes we are expected to download */ - data->req.size = attrs.filesize - data->state.resume_from; - data->req.maxdownload = attrs.filesize - data->state.resume_from; - Curl_pgrsSetDownloadSize(data, - attrs.filesize - data->state.resume_from); - SFTP_SEEK(sshc->sftp_handle, data->state.resume_from); + curl_off_t size; + + size = attrs.filesize; + if(conn->data->state.use_range) { + curl_off_t from, to; + char *ptr; + char *ptr2; + + from=curlx_strtoofft(conn->data->state.range, &ptr, 0); + while(ptr && *ptr && (isspace((int)*ptr) || (*ptr=='-'))) + ptr++; + to=curlx_strtoofft(ptr, &ptr2, 0); + if((ptr == ptr2) /* no "to" value given */ + || (to >= size)) { + to = size - 1; + } + if(from < 0) { + /* from is relative to end of file */ + from += size; + } + if(from >= size) { + failf(data, "Offset (%" + FORMAT_OFF_T ") was beyond file size (%" FORMAT_OFF_T ")", + from, attrs.filesize); + return CURLE_BAD_DOWNLOAD_RESUME; + } + if(from > to) { + from = to; + size = 0; + } + else { + size = to - from + 1; + } + + SFTP_SEEK(conn->proto.sshc.sftp_handle, from); + } + data->req.size = size; + data->req.maxdownload = size; + Curl_pgrsSetDownloadSize(data, size); + } + + /* We can resume if we can seek to the resume position */ + if(data->state.resume_from) { + if(data->state.resume_from< 0) { + /* We're supposed to download the last abs(from) bytes */ + if((curl_off_t)attrs.filesize < -data->state.resume_from) { + failf(data, "Offset (%" + FORMAT_OFF_T ") was beyond file size (%" FORMAT_OFF_T ")", + data->state.resume_from, attrs.filesize); + return CURLE_BAD_DOWNLOAD_RESUME; + } + /* download from where? */ + data->state.resume_from = attrs.filesize - data->state.resume_from; + } + else { + if((curl_off_t)attrs.filesize < data->state.resume_from) { + failf(data, "Offset (%" FORMAT_OFF_T + ") was beyond file size (%" FORMAT_OFF_T ")", + data->state.resume_from, attrs.filesize); + return CURLE_BAD_DOWNLOAD_RESUME; + } + } + /* Does a completed file need to be seeked and started or closed ? */ + /* Now store the number of bytes we are expected to download */ + data->req.size = attrs.filesize - data->state.resume_from; + data->req.maxdownload = attrs.filesize - data->state.resume_from; + Curl_pgrsSetDownloadSize(data, + attrs.filesize - data->state.resume_from); + SFTP_SEEK(sshc->sftp_handle, data->state.resume_from); + } + } + /* Setup the actual download */ + if(data->req.size == 0) { + /* no data to transfer */ + result = Curl_setup_transfer(conn, -1, -1, FALSE, NULL, -1, NULL); + infof(data, "File already completely downloaded\n"); + state(conn, SSH_STOP); + break; } - } - /* Setup the actual download */ - if(data->req.size == 0) { - /* no data to transfer */ - result = Curl_setup_transfer(conn, -1, -1, FALSE, NULL, -1, NULL); - infof(data, "File already completely downloaded\n"); - state(conn, SSH_STOP); - break; - } - else { - result = Curl_setup_transfer(conn, FIRSTSOCKET, data->req.size, - FALSE, NULL, -1, NULL); + else { + result = Curl_setup_transfer(conn, FIRSTSOCKET, data->req.size, + FALSE, NULL, -1, NULL); - /* not set by Curl_setup_transfer to preserve keepon bits */ - conn->writesockfd = conn->sockfd; + /* not set by Curl_setup_transfer to preserve keepon bits */ + conn->writesockfd = conn->sockfd; - /* FIXME: here should be explained why we need it to start the download */ - conn->cselect_bits = CURL_CSELECT_IN; - } - if(result) { - state(conn, SSH_SFTP_CLOSE); - sshc->actualcode = result; - } - else { - state(conn, SSH_STOP); - } - break; + /* FIXME: here should be explained why we need it to start the download */ + conn->cselect_bits = CURL_CSELECT_IN; + } + if(result) { + state(conn, SSH_SFTP_CLOSE); + sshc->actualcode = result; + } + else { + 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"); + 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; } - sshc->sftp_handle = NULL; - } - Curl_safefree(sftp_scp->path); - sftp_scp->path = NULL; + Curl_safefree(sftp_scp->path); + sftp_scp->path = NULL; - DEBUGF(infof(data, "SFTP DONE done\n")); + DEBUGF(infof(data, "SFTP DONE done\n")); #if 0 /* PREV */ - state(conn, SSH_SFTP_SHUTDOWN); + state(conn, SSH_SFTP_SHUTDOWN); #endif - state(conn, SSH_STOP); - result = sshc->actualcode; - break; + state(conn, SSH_STOP); + result = sshc->actualcode; + break; - case SSH_SFTP_SHUTDOWN: - /* during times we get here due to a broken transfer and then the - sftp_handle might not have been taken down so make sure that is done - before we proceed */ + case SSH_SFTP_SHUTDOWN: + /* during times we get here due to a broken transfer and then the + sftp_handle might not have been taken down so make sure that is done + before we proceed */ - 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(sshc->sftp_session) { - rc = libssh2_sftp_shutdown(sshc->sftp_session); - if(rc == LIBSSH2_ERROR_EAGAIN) { - break; + 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; } - else if(rc < 0) { - infof(data, "Failed to stop libssh2 sftp subsystem\n"); + 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; } - sshc->sftp_session = NULL; - } - Curl_safefree(sshc->homedir); - sshc->homedir = NULL; + Curl_safefree(sshc->homedir); + sshc->homedir = NULL; - state(conn, SSH_SESSION_DISCONNECT); - break; - - case SSH_SCP_TRANS_INIT: - result = ssh_getworkingpath(conn, sshc->homedir, &sftp_scp->path); - if(result) { - sshc->actualcode = result; - state(conn, SSH_STOP); + state(conn, SSH_SESSION_DISCONNECT); break; - } - 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); + case SSH_SCP_TRANS_INIT: + result = ssh_getworkingpath(conn, sshc->homedir, &sftp_scp->path); + if(result) { + sshc->actualcode = result; + state(conn, SSH_STOP); break; } - 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, - (size_t)data->set.infilesize, 0, 0); - if(!sshc->ssh_channel) { - if(libssh2_session_last_errno(sshc->ssh_session) == - LIBSSH2_ERROR_EAGAIN) { - rc = LIBSSH2_ERROR_EAGAIN; - break; + 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 { - int ssh_err; - char *err_msg; - - ssh_err = libssh2_session_last_error(sshc->ssh_session, - &err_msg, NULL, 0); - failf(conn->data, "%s", err_msg); - state(conn, SSH_SCP_CHANNEL_FREE); - sshc->actualcode = libssh2_session_error_to_CURLE(ssh_err); - break; + state(conn, SSH_SCP_DOWNLOAD_INIT); } - } + break; - /* upload data */ - result = Curl_setup_transfer(conn, -1, data->req.size, FALSE, NULL, - FIRSTSOCKET, NULL); + 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, + (size_t)data->set.infilesize, 0, 0); + if(!sshc->ssh_channel) { + if(libssh2_session_last_errno(sshc->ssh_session) == + LIBSSH2_ERROR_EAGAIN) { + rc = LIBSSH2_ERROR_EAGAIN; + break; + } + else { + int ssh_err; + char *err_msg; + + ssh_err = libssh2_session_last_error(sshc->ssh_session, + &err_msg, NULL, 0); + failf(conn->data, "%s", err_msg); + state(conn, SSH_SCP_CHANNEL_FREE); + sshc->actualcode = libssh2_session_error_to_CURLE(ssh_err); + break; + } + } - /* not set by Curl_setup_transfer to preserve keepon bits */ - conn->sockfd = conn->writesockfd; + /* upload data */ + result = Curl_setup_transfer(conn, -1, data->req.size, FALSE, NULL, + FIRSTSOCKET, NULL); - if(result) { - state(conn, SSH_SCP_CHANNEL_FREE); - sshc->actualcode = result; - } - else { - state(conn, SSH_STOP); - } - break; + /* not set by Curl_setup_transfer to preserve keepon bits */ + conn->sockfd = conn->writesockfd; - 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) { - rc = LIBSSH2_ERROR_EAGAIN; - break; + if(result) { + state(conn, SSH_SCP_CHANNEL_FREE); + sshc->actualcode = result; } else { - int ssh_err; - char *err_msg; - - ssh_err = libssh2_session_last_error(sshc->ssh_session, - &err_msg, NULL, 0); - failf(conn->data, "%s", err_msg); - state(conn, SSH_SCP_CHANNEL_FREE); - sshc->actualcode = libssh2_session_error_to_CURLE(ssh_err); - break; + state(conn, SSH_STOP); } - } - - /* download data */ - bytecount = (curl_off_t)sb.st_size; - data->req.maxdownload = (curl_off_t)sb.st_size; - result = Curl_setup_transfer(conn, FIRSTSOCKET, - bytecount, FALSE, NULL, -1, NULL); + break; - /* not set by Curl_setup_transfer to preserve keepon bits */ - conn->writesockfd = conn->sockfd; + 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) { + rc = LIBSSH2_ERROR_EAGAIN; + break; + } + else { + int ssh_err; + char *err_msg; + + ssh_err = libssh2_session_last_error(sshc->ssh_session, + &err_msg, NULL, 0); + failf(conn->data, "%s", err_msg); + state(conn, SSH_SCP_CHANNEL_FREE); + sshc->actualcode = libssh2_session_error_to_CURLE(ssh_err); + break; + } + } - /* FIXME: here should be explained why we need it to start the download */ - conn->cselect_bits = CURL_CSELECT_IN; + /* download data */ + bytecount = (curl_off_t)sb.st_size; + data->req.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; + /* not set by Curl_setup_transfer to preserve keepon bits */ + conn->writesockfd = conn->sockfd; - case SSH_SCP_DONE: - if(data->set.upload) - state(conn, SSH_SCP_SEND_EOF); - else - state(conn, SSH_SCP_CHANNEL_FREE); - break; + /* FIXME: here should be explained why we need it to start the download */ + conn->cselect_bits = CURL_CSELECT_IN; - 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"); + if(result) { + state(conn, SSH_SCP_CHANNEL_FREE); + sshc->actualcode = result; } + else + state(conn, SSH_STOP); } - 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: %d\n", rc); - } - } - state(conn, SSH_SCP_WAIT_CLOSE); - break; + case SSH_SCP_DONE: + if(data->set.upload) + state(conn, SSH_SCP_SEND_EOF); + else + state(conn, SSH_SCP_CHANNEL_FREE); + break; - case SSH_SCP_WAIT_CLOSE: - if(sshc->ssh_channel) { - rc = libssh2_channel_wait_closed(sshc->ssh_channel); - if(rc == LIBSSH2_ERROR_EAGAIN) { - 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"); + } } - else if(rc) { - infof(data, "Channel failed to close: %d\n", rc); + 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: %d\n", rc); + } } - } - state(conn, SSH_SCP_CHANNEL_FREE); - break; + state(conn, SSH_SCP_WAIT_CLOSE); + break; - case SSH_SCP_CHANNEL_FREE: - if(sshc->ssh_channel) { - rc = libssh2_channel_free(sshc->ssh_channel); - if(rc == LIBSSH2_ERROR_EAGAIN) { - 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: %d\n", rc); + } } - else if(rc < 0) { - infof(data, "Failed to free libssh2 scp subsystem\n"); + 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; } - sshc->ssh_channel = NULL; - } - DEBUGF(infof(data, "SCP DONE phase complete\n")); + DEBUGF(infof(data, "SCP DONE phase complete\n")); #if 0 /* PREV */ - state(conn, SSH_SESSION_DISCONNECT); + state(conn, SSH_SESSION_DISCONNECT); #endif - state(conn, SSH_STOP); - result = sshc->actualcode; - break; + state(conn, SSH_STOP); + result = sshc->actualcode; + break; - case SSH_SESSION_DISCONNECT: - /* during weird times when we've been prematurely aborted, the channel - is still alive when we reach this state and we MUST kill the channel - properly first */ - 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"); + case SSH_SESSION_DISCONNECT: + /* during weird times when we've been prematurely aborted, the channel + is still alive when we reach this state and we MUST kill the channel + properly first */ + 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; } - sshc->ssh_channel = NULL; - } - 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"); + 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(sshc->homedir); - sshc->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: + case SSH_SESSION_FREE: #ifdef HAVE_LIBSSH2_KNOWNHOST_API - if(sshc->kh) { - libssh2_knownhost_free(sshc->kh); - sshc->kh = NULL; - } + if(sshc->kh) { + libssh2_knownhost_free(sshc->kh); + sshc->kh = NULL; + } #endif - 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"); + 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; } - sshc->ssh_session = NULL; + conn->bits.close = TRUE; + 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; } - conn->bits.close = TRUE; - 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; - } + } while(!rc && (sshc->state != SSH_STOP)); if(rc == LIBSSH2_ERROR_EAGAIN) { /* we would block, we need to wait for the socket to be ready (in the -- cgit v1.2.3