1
0
mirror of https://github.com/moparisthebest/curl synced 2024-12-21 23:58:49 -05:00

Commit Tom Regner's code for SFTP create missing directories. This patch

uses the --ftp-create-dirs flag to control if cURL will try and create
directories that are specified in an upload path, but don't exist.
This commit is contained in:
James Housley 2007-06-13 12:15:23 +00:00
parent 3ec7f8a25a
commit 96f4af4db9

227
lib/ssh.c
View File

@ -110,12 +110,6 @@
#define _MPRINTF_REPLACE /* use our functions only */
#include <curl/mprintf.h>
#if defined(WIN32) || defined(MSDOS) || defined(__EMX__)
#define DIRSEP '\\'
#else
#define DIRSEP '/'
#endif
#define _MPRINTF_REPLACE /* use our functions only */
#include <curl/mprintf.h>
@ -136,31 +130,37 @@
#define S_IROTH 0
#endif
#define LIBSSH2_SFTP_S_IRUSR S_IRUSR
#define LIBSSH2_SFTP_S_IWUSR S_IWUSR
#define LIBSSH2_SFTP_S_IRGRP S_IRGRP
#define LIBSSH2_SFTP_S_IROTH S_IROTH
#define LIBSSH2_SFTP_S_IRUSR S_IRUSR
#define LIBSSH2_SFTP_S_IWUSR S_IWUSR
#define LIBSSH2_SFTP_S_IRGRP S_IRGRP
#define LIBSSH2_SFTP_S_IROTH S_IROTH
#define LIBSSH2_SFTP_S_IFMT S_IFMT
#define LIBSSH2_SFTP_S_IFDIR S_IFDIR
#define LIBSSH2_SFTP_S_IFLNK S_IFLNK
#define LIBSSH2_SFTP_S_IFSOCK S_IFSOCK
#define LIBSSH2_SFTP_S_IFCHR S_IFCHR
#define LIBSSH2_SFTP_S_IFBLK S_IFBLK
#define LIBSSH2_SFTP_S_IXUSR S_IXUSR
#define LIBSSH2_SFTP_S_IWGRP S_IWGRP
#define LIBSSH2_SFTP_S_IXGRP S_IXGRP
#define LIBSSH2_SFTP_S_IWOTH S_IWOTH
#define LIBSSH2_SFTP_S_IXOTH S_IXOTH
/* File mode */
/* Read, write, execute/search by owner */
#define LIBSSH2_SFTP_S_IRWXU S_IRWXU /* RWX mask for owner */
#define LIBSSH2_SFTP_S_IRUSR S_IRUSR /* R for owner */
#define LIBSSH2_SFTP_S_IWUSR S_IWUSR /* W for owner */
#define LIBSSH2_SFTP_S_IXUSR S_IXUSR /* X for owner */
/* Read, write, execute/search by group */
#define LIBSSH2_SFTP_S_IRWXG S_IRWXG /* RWX mask for group */
#define LIBSSH2_SFTP_S_IRGRP S_IRGRP /* R for group */
#define LIBSSH2_SFTP_S_IWGRP S_IWGRP /* W for group */
#define LIBSSH2_SFTP_S_IXGRP S_IXGRP /* X for group */
/* Read, write, execute/search by others */
#define LIBSSH2_SFTP_S_IRWXO S_IRWXO /* RWX mask for other */
#define LIBSSH2_SFTP_S_IROTH S_IROTH /* R for other */
#define LIBSSH2_SFTP_S_IWOTH S_IWOTH /* W for other */
#define LIBSSH2_SFTP_S_IXOTH S_IXOTH /* X for other */
/* File type */
#define LIBSSH2_SFTP_S_IFMT S_IFMT /* type of file mask */
#define LIBSSH2_SFTP_S_IFDIR S_IFDIR /* directory */
#define LIBSSH2_SFTP_S_IFLNK S_IFLNK /* symbolic link */
#define LIBSSH2_SFTP_S_IFSOCK S_IFSOCK /* socket */
#define LIBSSH2_SFTP_S_IFCHR S_IFCHR /* character special */
#define LIBSSH2_SFTP_S_IFBLK S_IFBLK /* block special */
#endif
/* Local functions: */
static const char *sftp_libssh2_strerror(unsigned long err);
static CURLcode sftp_sendquote(struct connectdata *conn,
struct curl_slist *quote);
static CURLcode sftp_create_dirs(struct connectdata *conn);
static LIBSSH2_ALLOC_FUNC(libssh2_malloc);
static LIBSSH2_REALLOC_FUNC(libssh2_realloc);
@ -236,7 +236,7 @@ static LIBSSH2_FREE_FUNC(libssh2_free)
#if (LIBSSH2_APINO >= 200706012030)
/*
* SSH State machine related code
* SSH State machine related code
*/
/* This is the ONLY way to change SSH state! */
static void state(struct connectdata *conn, ftpstate state)
@ -265,14 +265,14 @@ static void state(struct connectdata *conn, ftpstate state)
};
#endif
struct ssh_conn *sshc = &conn->proto.sshc;
#if defined(CURLDEBUG) && !defined(CURL_DISABLE_VERBOSE_STRINGS)
if (sshc->state != state) {
infof(conn->data, "FTP %p state change from %s to %s\n",
sshc, names[sshc->state], names[state]);
}
#endif
sshc->state = state;
}
@ -287,9 +287,9 @@ static CURLcode ssh_statemach_act(struct connectdata *conn)
const char *fingerprint;
#endif /* CURL_LIBSSH2_DEBUG */
int rc;
ssh = data->reqdata.proto.ssh;
switch(sshc->state) {
case SSH_S_STARTUP:
rc = libssh2_session_startup(ssh->ssh_session, sock);
@ -302,10 +302,10 @@ static CURLcode ssh_statemach_act(struct connectdata *conn)
sshc->actualCode = CURLE_FAILED_INIT;
break;
}
/* Set libssh2 to non-blocking, since cURL is all non-blocking */
libssh2_session_set_blocking(ssh->ssh_session, 0);
#ifdef CURL_LIBSSH2_DEBUG
/*
* Before we authenticate we should check the hostkey's fingerprint
@ -315,7 +315,7 @@ static CURLcode ssh_statemach_act(struct connectdata *conn)
*/
fingerprint = libssh2_hostkey_hash(ssh->ssh_session,
LIBSSH2_HOSTKEY_HASH_MD5);
/* The fingerprint points to static storage (!), don't free() it. */
infof(data, "Fingerprint: ");
for (i = 0; i < 16; i++) {
@ -323,13 +323,13 @@ static CURLcode ssh_statemach_act(struct connectdata *conn)
}
infof(data, "\n");
#endif /* CURL_LIBSSH2_DEBUG */
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
@ -342,7 +342,7 @@ static CURLcode ssh_statemach_act(struct connectdata *conn)
*/
sshc->authlist = libssh2_userauth_list(ssh->ssh_session, ssh->user,
strlen(ssh->user));
if (!sshc->authlist) {
if (libssh2_session_last_errno(ssh->ssh_session) ==
LIBSSH2_ERROR_EAGAIN) {
@ -357,46 +357,46 @@ static CURLcode ssh_statemach_act(struct connectdata *conn)
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;
if ((data->set.ssh_auth_types & CURLSSH_AUTH_PUBLICKEY) &&
(strstr(sshc->authlist, "publickey") != NULL)) {
char *home;
sshc->rsa_pub[0] = sshc->rsa[0] = '\0';
/* To ponder about: should really the lib be messing about with the
HOME environment variable etc? */
home = curl_getenv("HOME");
if (data->set.ssh_public_key)
snprintf(sshc->rsa_pub, sizeof(sshc->rsa_pub), "%s",
data->set.ssh_public_key);
else if (home)
snprintf(sshc->rsa_pub, sizeof(sshc->rsa_pub), "%s/.ssh/id_dsa.pub",
home);
if (data->set.ssh_private_key)
snprintf(sshc->rsa, sizeof(sshc->rsa), "%s",
data->set.ssh_private_key);
else if (home)
snprintf(sshc->rsa, sizeof(sshc->rsa), "%s/.ssh/id_dsa", home);
sshc->passphrase = data->set.key_passwd;
if (!sshc->passphrase)
sshc->passphrase = "";
curl_free(home);
infof(conn->data, "Using ssh public key file %s\n", sshc->rsa_pub);
infof(conn->data, "Using ssh private key file %s\n", sshc->rsa);
if (sshc->rsa_pub[0]) {
state(conn, SSH_AUTH_PKEY);
} else {
@ -406,7 +406,7 @@ static CURLcode ssh_statemach_act(struct connectdata *conn)
state(conn, SSH_AUTH_PASS_INIT);
}
break;
case SSH_AUTH_PKEY:
/* The function below checks if the files exists, no need to stat() here.
*/
@ -433,7 +433,7 @@ static CURLcode ssh_statemach_act(struct connectdata *conn)
state(conn, SSH_AUTH_HOST_INIT);
}
break;
case SSH_AUTH_PASS:
rc = libssh2_userauth_password(ssh->ssh_session, ssh->user,
ssh->passwd);
@ -448,7 +448,7 @@ static CURLcode ssh_statemach_act(struct connectdata *conn)
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)) {
@ -457,20 +457,20 @@ static CURLcode ssh_statemach_act(struct connectdata *conn)
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(ssh->ssh_session,
@ -486,7 +486,7 @@ static CURLcode ssh_statemach_act(struct connectdata *conn)
}
state(conn, SSH_AUTH_DONE);
break;
case SSH_AUTH_DONE:
if (!sshc->authed) {
failf(data, "Authentication failure");
@ -494,12 +494,12 @@ static CURLcode ssh_statemach_act(struct connectdata *conn)
sshc->actualCode = CURLE_LOGIN_DENIED;
break;
}
/*
* At this point we have an authenticated ssh session.
*/
infof(conn->data, "Authentication complete\n");
conn->sockfd = sock;
conn->writesockfd = CURL_SOCKET_BAD;
@ -509,7 +509,7 @@ static CURLcode ssh_statemach_act(struct connectdata *conn)
}
state(conn, SSH_GET_WORKINGPATH);
break;
case SSH_SFTP_INIT:
/*
* Start the libssh2 sftp session
@ -528,11 +528,11 @@ static CURLcode ssh_statemach_act(struct connectdata *conn)
}
state(conn, SSH_SFTP_REALPATH);
break;
case SSH_SFTP_REALPATH:
{
char tempHome[PATH_MAX];
/*
* Get the "home" directory
*/
@ -560,13 +560,13 @@ static CURLcode ssh_statemach_act(struct connectdata *conn)
state(conn, SSH_GET_WORKINGPATH);
}
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) {
@ -574,7 +574,7 @@ static CURLcode ssh_statemach_act(struct connectdata *conn)
result = CURLE_OUT_OF_MEMORY;
break;
}
/* Check for /~/ , indicating relative to the user's home directory */
if (conn->protocol == PROT_SCP) {
real_path = (char *)malloc(working_path_len+1);
@ -632,7 +632,7 @@ static CURLcode ssh_statemach_act(struct connectdata *conn)
sshc->actualCode = CURLE_FAILED_INIT;
break;
}
Curl_safefree(working_path);
ssh->path = real_path;
@ -649,7 +649,7 @@ static CURLcode ssh_statemach_act(struct connectdata *conn)
ssh->sftp_session = NULL;
state(conn, SSH_SESSION_FREE);
break;
case SSH_SESSION_FREE:
rc = libssh2_session_free(ssh->ssh_session);
if (rc == LIBSSH2_ERROR_EAGAIN) {
@ -659,7 +659,7 @@ static CURLcode ssh_statemach_act(struct connectdata *conn)
state(conn, SSH_STOP);
result = sshc->actualCode;
break;
case SSH_QUIT:
/* fallthrough, just stop! */
default:
@ -685,9 +685,9 @@ CURLcode Curl_ssh_multi_statemach(struct connectdata *conn,
#if 0
long timeout_ms = ssh_state_timeout(conn);
#endif
*done = FALSE; /* default to not done yet */
#if 0
if (timeout_ms <= 0) {
failf(data, "SSH response timeout");
@ -721,11 +721,11 @@ static CURLcode ssh_easy_statemach(struct connectdata *conn)
struct SessionHandle *data=conn->data;
struct ssh_conn *sshc = &conn->proto.sshc;
CURLcode result = CURLE_OK;
while(sshc->state != SSH_STOP) {
#if 0
long timeout_ms = ssh_state_timeout(conn);
if (timeout_ms <=0 ) {
failf(data, "SSH response timeout");
return CURLE_OPERATION_TIMEDOUT; /* already too little time */
@ -839,7 +839,7 @@ CURLcode Curl_ssh_connect(struct connectdata *conn, bool *done)
#if (LIBSSH2_APINO >= 200706012030)
state(conn, SSH_S_STARTUP);
if (data->state.used_interface == Curl_if_multi)
result = Curl_ssh_multi_statemach(conn, done);
else {
@ -857,9 +857,9 @@ CURLcode Curl_ssh_connect(struct connectdata *conn, bool *done)
(void)authlist; /* not used */
(void)fingerprint; /* not used */
(void)i; /* not used */
#else /* !(LIBSSH2_APINO >= 200706012030) */
if (libssh2_session_startup(ssh->ssh_session, sock)) {
failf(data, "Failure establishing ssh session");
libssh2_session_free(ssh->ssh_session);
@ -1029,7 +1029,7 @@ CURLcode Curl_ssh_connect(struct connectdata *conn, bool *done)
&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);
@ -1352,9 +1352,37 @@ CURLcode Curl_sftp_do(struct connectdata *conn, bool *done)
(libssh2_session_last_errno(sftp->ssh_session) !=
LIBSSH2_ERROR_EAGAIN)) {
err = libssh2_sftp_last_error(sftp->sftp_session);
failf(conn->data, "Could not open remote file for writing: %s",
sftp_libssh2_strerror(err));
return sftp_libssh2_error_to_CURLE(err);
if (((err == LIBSSH2_FX_NO_SUCH_FILE) ||
(err == LIBSSH2_FX_FAILURE) ||
(err == LIBSSH2_FX_NO_SUCH_PATH)) &&
(conn->data->set.ftp_create_missing_dirs &&
(strlen(sftp->path) > 1))) {
/* try to create the path remotely */
res = sftp_create_dirs(conn);
if (res == 0) {
do {
sftp->sftp_handle = libssh2_sftp_open(sftp->sftp_session,
sftp->path,
LIBSSH2_FXF_WRITE|LIBSSH2_FXF_CREAT|LIBSSH2_FXF_TRUNC,
LIBSSH2_SFTP_S_IRUSR|LIBSSH2_SFTP_S_IWUSR|
LIBSSH2_SFTP_S_IRGRP|LIBSSH2_SFTP_S_IROTH);
if (!sftp->sftp_handle &&
(libssh2_session_last_errno(sftp->ssh_session) !=
LIBSSH2_ERROR_EAGAIN)) {
err = libssh2_sftp_last_error(sftp->sftp_session);
failf(conn->data, "Could not open remote file for writing: %s",
sftp_libssh2_strerror(err));
return sftp_libssh2_error_to_CURLE(err);
}
} while (!sftp->sftp_handle);
}
}
if (!sftp->sftp_handle) {
err = libssh2_sftp_last_error(sftp->sftp_session);
failf(conn->data, "Could not open remote file for writing: %s",
sftp_libssh2_strerror(err));
return sftp_libssh2_error_to_CURLE(err);
}
}
} while (!sftp->sftp_handle);
#else /* !(LIBSSH2_APINO >= 200706012030) */
@ -1365,9 +1393,26 @@ CURLcode Curl_sftp_do(struct connectdata *conn, bool *done)
LIBSSH2_SFTP_S_IRGRP|LIBSSH2_SFTP_S_IROTH);
if (!sftp->sftp_handle) {
err = libssh2_sftp_last_error(sftp->sftp_session);
failf(conn->data, "Could not open remote file for writing: %s",
sftp_libssh2_strerror(err));
return sftp_libssh2_error_to_CURLE(err);
if (((err == LIBSSH2_FX_NO_SUCH_FILE) ||
(err == LIBSSH2_FX_FAILURE) ||
(err == LIBSSH2_FX_NO_SUCH_PATH)) &&
(conn->data->set.ftp_create_missing_dirs &&
(strlen(sftp->path) > 1))) {
/* try to create the path remotely */
res = sftp_create_dirs(conn);
if (res == 0) {
sftp->sftp_handle = libssh2_sftp_open(sftp->sftp_session, sftp->path,
LIBSSH2_FXF_WRITE|LIBSSH2_FXF_CREAT|LIBSSH2_FXF_TRUNC,
LIBSSH2_SFTP_S_IRUSR|LIBSSH2_SFTP_S_IWUSR|
LIBSSH2_SFTP_S_IRGRP|LIBSSH2_SFTP_S_IROTH);
}
}
if (!sftp->sftp_handle) {
err = libssh2_sftp_last_error(sftp->sftp_session);
failf(conn->data, "Could not open remote file for writing: %s",
sftp_libssh2_strerror(err));
return sftp_libssh2_error_to_CURLE(err);
}
}
#endif /* !(LIBSSH2_APINO >= 200706012030) */
@ -1789,9 +1834,9 @@ ssize_t Curl_sftp_recv(struct connectdata *conn, int sockindex,
{
ssize_t nread;
(void)sockindex;
/* libssh2_sftp_read() returns size_t !*/
#if defined(LIBSSH2SFTP_EAGAIN) && (LIBSSH2_APINO < 200706012030)
/* we prefer the non-blocking API but that didn't exist previously */
nread = (ssize_t)
@ -2231,37 +2276,37 @@ static CURLcode sftp_create_dirs(struct connectdata *conn) {
unsigned int sftp_err = 0;
int rc;
struct SSHPROTO *sftp = conn->data->reqdata.proto.ssh;
if (strlen(sftp->path) > 1) {
char *slash_pos = sftp->path + 1; /* ignore the leading '/' */
while ((slash_pos = strchr(slash_pos, '/')) != NULL) {
*slash_pos = 0;
infof(conn->data, "Creating directory '%s'\n", sftp->path);
/* 'mode' - parameter is preliminary - default to 0644 */
#if (LIBSSH2_APINO >= 200706012030)
while ((rc = libssh2_sftp_mkdir(sftp->sftp_session, sftp->path,
while ((rc = libssh2_sftp_mkdir(sftp->sftp_session, sftp->path,
LIBSSH2_SFTP_S_IRWXU |
LIBSSH2_SFTP_S_IRGRP | LIBSSH2_SFTP_S_IXGRP |
LIBSSH2_SFTP_S_IROTH | LIBSSH2_SFTP_S_IXOTH)) ==
LIBSSH2_ERROR_EAGAIN);
#else /* !(LIBSSH2_APINO >= 200706012030) */
rc = libssh2_sftp_mkdir(sftp->sftp_session, sftp->path,
rc = libssh2_sftp_mkdir(sftp->sftp_session, sftp->path,
LIBSSH2_SFTP_S_IRWXU |
LIBSSH2_SFTP_S_IRGRP | LIBSSH2_SFTP_S_IXGRP |
LIBSSH2_SFTP_S_IROTH | LIBSSH2_SFTP_S_IXOTH);
#endif /* !(LIBSSH2_APINO >= 200706012030) */
*slash_pos = '/';
++slash_pos;
if (rc == -1) {
if (rc == -1) {
/* abort if failure wasn't that the dir already exists or the
* permission was denied (creation might succeed further
* permission was denied (creation might succeed further
* down the path) - retry on unspecific FAILURE also
*/
sftp_err = libssh2_sftp_last_error(sftp->sftp_session);
if ((sftp_err != LIBSSH2_FX_FILE_ALREADY_EXISTS) &&
(sftp_err != LIBSSH2_FX_FAILURE) &&
if ((sftp_err != LIBSSH2_FX_FILE_ALREADY_EXISTS) &&
(sftp_err != LIBSSH2_FX_FAILURE) &&
(sftp_err != LIBSSH2_FX_PERMISSION_DENIED)) {
result = -1;
break;