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