Newer
Older
sshc->actualCode = CURLE_QUOTE_ERROR;
James Housley
committed
break;
}
/* Now set the new attributes... */
if(curl_strnequal(sshc->quote_item->data, "chgrp", 5)) {
James Housley
committed
sshc->quote_attrs.gid = strtol(sshc->quote_path1, NULL, 10);
if(sshc->quote_attrs.gid == 0 && !ISDIGIT(sshc->quote_path1[0])) {
James Housley
committed
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;
James Housley
committed
break;
}
}
else if(curl_strnequal(sshc->quote_item->data, "chmod", 5)) {
James Housley
committed
sshc->quote_attrs.permissions = strtol(sshc->quote_path1, NULL, 8);
/* permissions are octal */
if(sshc->quote_attrs.permissions == 0 &&
James Housley
committed
!ISDIGIT(sshc->quote_path1[0])) {
Curl_safefree(sshc->quote_path1);
sshc->quote_path1 = NULL;
Curl_safefree(sshc->quote_path2);
sshc->quote_path2 = NULL;
failf(data, "Syntax error: chmod permissions not a number");
state(conn, SSH_SFTP_CLOSE);
sshc->actualCode = CURLE_QUOTE_ERROR;
James Housley
committed
break;
}
}
else if(curl_strnequal(sshc->quote_item->data, "chown", 5)) {
James Housley
committed
sshc->quote_attrs.uid = strtol(sshc->quote_path1, NULL, 10);
if(sshc->quote_attrs.uid == 0 && !ISDIGIT(sshc->quote_path1[0])) {
James Housley
committed
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;
James Housley
committed
break;
}
}
/* Now send the completed structure... */
state(conn, SSH_SFTP_QUOTE_SETSTAT);
James Housley
committed
case SSH_SFTP_QUOTE_SETSTAT:
rc = libssh2_sftp_setstat(sshc->sftp_session, sshc->quote_path2,
James Housley
committed
&sshc->quote_attrs);
if(rc == LIBSSH2_ERROR_EAGAIN) {
} else if(rc != 0) {
err = libssh2_sftp_last_error(sshc->sftp_session);
James Housley
committed
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;
James Housley
committed
break;
James Housley
committed
state(conn, SSH_SFTP_NEXT_QUOTE);
break;
case SSH_SFTP_QUOTE_SYMLINK:
rc = libssh2_sftp_symlink(sshc->sftp_session, sshc->quote_path1,
James Housley
committed
sshc->quote_path2);
if(rc == LIBSSH2_ERROR_EAGAIN) {
James Housley
committed
break;
}
else if(rc != 0) {
err = libssh2_sftp_last_error(sshc->sftp_session);
James Housley
committed
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;
James Housley
committed
break;
}
state(conn, SSH_SFTP_NEXT_QUOTE);
break;
case SSH_SFTP_QUOTE_MKDIR:
rc = libssh2_sftp_mkdir(sshc->sftp_session, sshc->quote_path1, 0755);
if(rc == LIBSSH2_ERROR_EAGAIN) {
James Housley
committed
break;
}
else if(rc != 0) {
err = libssh2_sftp_last_error(sshc->sftp_session);
James Housley
committed
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;
James Housley
committed
break;
}
state(conn, SSH_SFTP_NEXT_QUOTE);
break;
case SSH_SFTP_QUOTE_RENAME:
rc = libssh2_sftp_rename(sshc->sftp_session, sshc->quote_path1,
James Housley
committed
sshc->quote_path2);
if(rc == LIBSSH2_ERROR_EAGAIN) {
James Housley
committed
break;
}
else if(rc != 0) {
err = libssh2_sftp_last_error(sshc->sftp_session);
James Housley
committed
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;
James Housley
committed
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) {
James Housley
committed
break;
}
else if(rc != 0) {
err = libssh2_sftp_last_error(sshc->sftp_session);
James Housley
committed
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;
James Housley
committed
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) {
James Housley
committed
break;
}
else if(rc != 0) {
err = libssh2_sftp_last_error(sshc->sftp_session);
James Housley
committed
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;
James Housley
committed
break;
}
state(conn, SSH_SFTP_NEXT_QUOTE);
break;
case SSH_SFTP_TRANS_INIT:
if(data->set.upload)
James Housley
committed
state(conn, SSH_SFTP_UPLOAD_INIT);
else {
if(sftp_scp->path[strlen(sftp_scp->path)-1] == '/')
James Housley
committed
state(conn, SSH_SFTP_READDIR_INIT);
else
James Housley
committed
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) ==
James Housley
committed
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) {
James Housley
committed
state(conn, SSH_SFTP_CLOSE);
sshc->actualCode = err;
break;
}
else if(((err == LIBSSH2_FX_NO_SUCH_FILE) ||
James Housley
committed
(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) {
James Housley
committed
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) {
James Housley
committed
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) {
James Housley
committed
*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_CREATE_DIRS_MKDIR:
/* 'mode' - parameter is preliminary - default to 0644 */
rc = libssh2_sftp_mkdir(sshc->sftp_session, sftp_scp->path,
James Housley
committed
data->set.new_directory_perms);
if(rc == LIBSSH2_ERROR_EAGAIN) {
James Housley
committed
break;
}
*sshc->slash_pos = '/';
++sshc->slash_pos;
if(rc == -1) {
James Housley
committed
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) &&
James Housley
committed
(sftp_err != LIBSSH2_FX_FAILURE) &&
(sftp_err != LIBSSH2_FX_PERMISSION_DENIED)) {
result = sftp_libssh2_error_to_CURLE(sftp_err);
state(conn, SSH_SFTP_CLOSE);
sshc->actualCode = result;
break;
}
}
state(conn, SSH_SFTP_CREATE_DIRS);
break;
case SSH_SFTP_READDIR_INIT:
/*
* This is a directory that we are trying to get, so produce a
* directory listing
*/
sshc->sftp_handle = libssh2_sftp_opendir(sshc->sftp_session,
James Housley
committed
sftp_scp->path);
if(!sshc->sftp_handle) {
if(libssh2_session_last_errno(sshc->ssh_session) ==
James Housley
committed
LIBSSH2_ERROR_EAGAIN) {
break;
}
else {
err = libssh2_sftp_last_error(sshc->sftp_session);
James Housley
committed
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) {
James Housley
committed
state(conn, SSH_SFTP_CLOSE);
sshc->actualCode = CURLE_OUT_OF_MEMORY;
break;
}
if((sshc->readdir_longentry = (char *)malloc(PATH_MAX+1)) == NULL) {
James Housley
committed
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,
James Housley
committed
sshc->readdir_filename,
PATH_MAX,
sshc->readdir_longentry,
PATH_MAX,
&sshc->readdir_attrs);
if(sshc->readdir_len == LIBSSH2_ERROR_EAGAIN) {
James Housley
committed
break;
}
if(sshc->readdir_len > 0) {
James Housley
committed
sshc->readdir_filename[sshc->readdir_len] = '\0';
if(data->set.ftp_list_only) {
James Housley
committed
char *tmpLine;
tmpLine = aprintf("%s\n", sshc->readdir_filename);
if(tmpLine == NULL) {
James Housley
committed
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) {
James Housley
committed
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) {
James Housley
committed
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) &&
James Housley
committed
((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) {
James Housley
committed
Curl_safefree(sshc->readdir_filename);
sshc->readdir_filename = NULL;
Curl_safefree(sshc->readdir_longentry);
sshc->readdir_longentry = NULL;
state(conn, SSH_SFTP_CLOSE);
sshc->actualCode = CURLE_OUT_OF_MEMORY;
break;
}
snprintf(sshc->readdir_linkPath, PATH_MAX, "%s%s", sftp_scp->path,
sshc->readdir_filename);
state(conn, SSH_SFTP_READDIR_LINK);
break;
}
state(conn, SSH_SFTP_READDIR_BOTTOM);
break;
}
}
else if(sshc->readdir_len == 0) {
James Housley
committed
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);
James Housley
committed
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));
James Housley
committed
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,
James Housley
committed
sshc->readdir_linkPath,
sshc->readdir_filename,
PATH_MAX);
if(sshc->readdir_len == LIBSSH2_ERROR_EAGAIN) {
James Housley
committed
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) {
James Housley
committed
1419
1420
1421
1422
1423
1424
1425
1426
1427
1428
1429
1430
1431
1432
1433
1434
1435
1436
1437
1438
1439
1440
1441
1442
1443
1444
1445
1446
Curl_safefree(sshc->readdir_filename);
sshc->readdir_filename = NULL;
Curl_safefree(sshc->readdir_longentry);
sshc->readdir_longentry = NULL;
state(conn, SSH_SFTP_CLOSE);
sshc->actualCode = CURLE_OUT_OF_MEMORY;
break;
}
sshc->readdir_currLen += snprintf(sshc->readdir_line +
sshc->readdir_currLen,
sshc->readdir_totalLen -
sshc->readdir_currLen,
" -> %s",
sshc->readdir_filename);
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) {
James Housley
committed
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) ==
James Housley
committed
LIBSSH2_ERROR_EAGAIN) {
break;
}
sshc->sftp_handle = NULL;
James Housley
committed
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,
James Housley
committed
LIBSSH2_FXF_READ, data->set.new_file_perms);
if(!sshc->sftp_handle) {
if(libssh2_session_last_errno(sshc->ssh_session) ==
LIBSSH2_ERROR_EAGAIN) {
James Housley
committed
break;
}
else {
err = libssh2_sftp_last_error(sshc->sftp_session);
James Housley
committed
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;
}
}
state(conn, SSH_SFTP_DOWNLOAD_STAT);
break;
case SSH_SFTP_DOWNLOAD_STAT:
{
LIBSSH2_SFTP_ATTRIBUTES attrs;
rc = libssh2_sftp_stat(sshc->sftp_session, sftp_scp->path, &attrs);
if(rc == LIBSSH2_ERROR_EAGAIN) {
James Housley
committed
break;
}
else if(rc) {
James Housley
committed
/*
* 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);
}
}
/* Setup the actual download */
result = Curl_setup_transfer(conn, FIRSTSOCKET, data->reqdata.size,
FALSE, NULL, -1, NULL);
if(result) {
James Housley
committed
state(conn, SSH_SFTP_CLOSE);
sshc->actualCode = result;
} else {
state(conn, SSH_STOP);
}
break;
case SSH_SFTP_CLOSE:
if(sshc->sftp_handle) {
rc = libssh2_sftp_close(sshc->sftp_handle);
if(rc == LIBSSH2_ERROR_EAGAIN) {
James Housley
committed
break;
}
else if(rc < 0) {
James Housley
committed
infof(data, "Failed to close libssh2 file\n");
}
sshc->sftp_handle = NULL;
James Housley
committed
}
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) {
James Housley
committed
break;
}
else if(rc < 0) {
James Housley
committed
infof(data, "Failed to stop libssh2 sftp subsystem\n");
}
sshc->sftp_session = NULL;
James Housley
committed
}
Curl_safefree(sftp_scp->path);
sftp_scp->path = NULL;
Curl_safefree(sftp_scp->homedir);
sftp_scp->homedir = NULL;
state(conn, SSH_CHANNEL_CLOSE);
break;
case SSH_SCP_TRANS_INIT:
if(data->set.upload) {
if(data->set.infilesize < 0) {
James Housley
committed
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);
}
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,
James Housley
committed
data->set.new_file_perms,
data->set.infilesize, 0, 0);
if(!sshc->ssh_channel) {
if(libssh2_session_last_errno(sshc->ssh_session) ==
James Housley
committed
LIBSSH2_ERROR_EAGAIN) {
break;
} else {
int ssh_err;
char *err_msg;
ssh_err = libssh2_session_last_error(sshc->ssh_session,
James Housley
committed
&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);
if(result) {
James Housley
committed
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,
James Housley
committed
sftp_scp->path, &sb);
if(!sshc->ssh_channel) {
if(libssh2_session_last_errno(sshc->ssh_session) ==
James Housley
committed
LIBSSH2_ERROR_EAGAIN) {
break;
} else {
int ssh_err;
char *err_msg;
ssh_err = libssh2_session_last_error(sshc->ssh_session,
James Housley
committed
&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;
}
}
/* 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) {
James Housley
committed
state(conn, SSH_SCP_CHANNEL_FREE);
sshc->actualCode = result;
} else {
state(conn, SSH_STOP);
}
}
break;
case SSH_SCP_DONE:
if(data->set.upload) {
James Housley
committed
state(conn, SSH_SCP_SEND_EOF);
} else {
state(conn, SSH_SCP_CHANNEL_FREE);
}
break;
case SSH_SCP_SEND_EOF:
if(sshc->ssh_channel) {
rc = libssh2_channel_send_eof(sshc->ssh_channel);
if(rc == LIBSSH2_ERROR_EAGAIN) {
James Housley
committed
break;
}
else if(rc) {
James Housley
committed
infof(data, "Failed to send libssh2 channel EOF\n");
}
}
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) {
James Housley
committed
break;
}
else if(rc) {
James Housley
committed
infof(data, "Failed to get channel EOF\n");
}
}
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) {
James Housley
committed
break;
}
else if(rc) {
James Housley
committed
infof(data, "Channel failed to close\n");
}
}
state(conn, SSH_SCP_CHANNEL_FREE);
break;
case SSH_SCP_CHANNEL_FREE:
if(sshc->ssh_channel) {
rc = libssh2_channel_free(sshc->ssh_channel);
if(rc == LIBSSH2_ERROR_EAGAIN) {
James Housley
committed
break;
}
else if(rc < 0) {
James Housley
committed
infof(data, "Failed to free libssh2 scp subsystem\n");
}
sshc->ssh_channel = NULL;
James Housley
committed
}
state(conn, SSH_SESSION_DISCONECT);
break;
case SSH_CHANNEL_CLOSE:
if(sshc->ssh_channel) {
rc = libssh2_channel_close(sshc->ssh_channel);
if(rc == LIBSSH2_ERROR_EAGAIN) {
James Housley
committed
break;
}
else if(rc < 0) {
James Housley
committed
infof(data, "Failed to stop libssh2 channel subsystem\n");
}
sshc->ssh_channel = NULL;
James Housley
committed
}
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) {
James Housley
committed
break;
}
else if(rc < 0) {
James Housley
committed
infof(data, "Failed to disconnect libssh2 session\n");
}
}
Curl_safefree(sftp_scp->path);
sftp_scp->path = NULL;
Curl_safefree(sftp_scp->homedir);
sftp_scp->homedir = NULL;
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) {
James Housley
committed
break;
}
else if(rc < 0) {
James Housley
committed
infof(data, "Failed to free libssh2 session\n");
}
sshc->ssh_session = NULL;
James Housley
committed
}
sshc->nextState = SSH_NO_STATE;
state(conn, SSH_STOP);
result = sshc->actualCode;
break;
case SSH_QUIT:
/* fallthrough, just stop! */
default:
/* internal error */
James Housley
committed
sshc->nextState = SSH_NO_STATE;
state(conn, SSH_STOP);
break;
}
return result;
}
/* called repeatedly until done from multi.c */
Patrick Monnerat
committed
static CURLcode Curl_ssh_multi_statemach(struct connectdata *conn, bool *done)
{
struct ssh_conn *sshc = &conn->proto.sshc;
CURLcode result = CURLE_OK;
James Housley
committed
result = ssh_statemach_act(conn);
*done = (bool)(sshc->state == SSH_STOP);
return result;
}
static CURLcode ssh_easy_statemach(struct connectdata *conn)
{
struct ssh_conn *sshc = &conn->proto.sshc;
CURLcode result = CURLE_OK;
James Housley
committed
while (sshc->state != SSH_STOP) {
result = ssh_statemach_act(conn);
if(result) {
break;
}
}
James Housley
committed
return result;
}
/*
* SSH setup and connection
*/
static CURLcode ssh_init(struct connectdata *conn)
{
struct SessionHandle *data = conn->data;
struct SSHPROTO *ssh;
if(data->reqdata.proto.ssh)
return CURLE_OK;
ssh = (struct SSHPROTO *)calloc(sizeof(struct SSHPROTO), 1);
if(!ssh)
return CURLE_OUT_OF_MEMORY;
data->reqdata.proto.ssh = ssh;
/* 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;
}
/*
* Curl_ssh_connect() gets called from Curl_protocol_connect() to allow us to
* do protocol-specific actions at connect-time.
*/
Patrick Monnerat
committed
static CURLcode Curl_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 */
Curl_reset_reqproto(conn);
Daniel Stenberg
committed
result = ssh_init(conn);
if(result)
ssh = &conn->proto.sshc;
#ifdef CURL_LIBSSH2_DEBUG
if(ssh->user) {
infof(data, "User: %s\n", ssh->user);
if(ssh->passwd) {
infof(data, "Password: %s\n", ssh->passwd);
}
#endif /* CURL_LIBSSH2_DEBUG */
sock = conn->sock[FIRSTSOCKET];
ssh->ssh_session = libssh2_session_init_ex(libssh2_malloc, libssh2_free,
James Housley
committed
libssh2_realloc, ssh);
if(ssh->ssh_session == NULL) {
failf(data, "Failure initialising ssh session");
return CURLE_FAILED_INIT;
}
#ifdef CURL_LIBSSH2_DEBUG
libssh2_trace(ssh->ssh_session, LIBSSH2_TRACE_CONN|LIBSSH2_TRACE_TRANS|
LIBSSH2_TRACE_KEX|LIBSSH2_TRACE_AUTH|LIBSSH2_TRACE_SCP|
LIBSSH2_TRACE_SFTP|LIBSSH2_TRACE_ERROR|
LIBSSH2_TRACE_PUBLICKEY);
infof(data, "SSH socket: %d\n", sock);
#endif /* CURL_LIBSSH2_DEBUG */
state(conn, SSH_S_STARTUP);
if(data->state.used_interface == Curl_if_multi)
result = Curl_ssh_multi_statemach(conn, done);
else {
result = ssh_easy_statemach(conn);
if(!result)
*done = TRUE;
}
return result;
James Housley
committed
1909
1910
1911
1912
1913
1914
1915
1916
1917
1918
1919
1920
1921
1922
1923
1924
1925
1926
1927
1928
1929
1930
1931
1932
/*
***********************************************************************
*
* scp_perform()
*
* This is the actual DO function for SCP. Get a file according to
* the options previously setup.
*/
static
CURLcode scp_perform(struct connectdata *conn,
bool *connected,
bool *dophase_done)
{
CURLcode result = CURLE_OK;
DEBUGF(infof(conn->data, "DO phase starts\n"));
*dophase_done = FALSE; /* not done yet */
/* start the first command in the DO phase */
state(conn, SSH_SCP_TRANS_INIT);
/* run the state-machine */
if(conn->data->state.used_interface == Curl_if_multi) {
James Housley
committed
result = Curl_ssh_multi_statemach(conn, dophase_done);
} else {
result = ssh_easy_statemach(conn);
*dophase_done = TRUE; /* with the easy interface we are done here */
}
*connected = conn->bits.tcpconnect;
if(*dophase_done) {
James Housley
committed
DEBUGF(infof(conn->data, "DO phase is complete\n"));
}
return result;
}
/* called from multi.c while DOing */
Patrick Monnerat
committed
static CURLcode Curl_scp_doing(struct connectdata *conn,
bool *dophase_done)
James Housley
committed
{
CURLcode result;
result = Curl_ssh_multi_statemach(conn, dophase_done);
if(*dophase_done) {
James Housley
committed
DEBUGF(infof(conn->data, "DO phase is complete\n"));
}
return result;
}
Patrick Monnerat
committed
static CURLcode Curl_scp_do(struct connectdata *conn, bool *done)
James Housley
committed
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) {
James Housley
committed
return res;
}
data->reqdata.size = -1; /* make sure this is unknown at this point */
Curl_pgrsSetUploadCounter(data, 0);
Curl_pgrsSetDownloadCounter(data, 0);
Curl_pgrsSetUploadSize(data, 0);
Curl_pgrsSetDownloadSize(data, 0);
res = scp_perform(conn, &connected, done);
return res;
Patrick Monnerat
committed
static CURLcode Curl_scp_done(struct connectdata *conn, CURLcode status,
bool premature)
James Housley
committed
CURLcode result = CURLE_OK;
bool done = FALSE;
(void)premature; /* not used */
(void)status; /* unused */