Newer
Older
}
else if(sshc->quote_item->data) {
/*
* the arguments following the command must be separated from the
* command with a space so we can check for it unconditionally
*/
cp = strchr(sshc->quote_item->data, ' ');
if(cp == NULL) {
failf(data, "Syntax error in SFTP command. Supply parameter(s)!");
state(conn, SSH_SFTP_CLOSE);
sshc->actualcode = CURLE_QUOTE_ERROR;
break;
}
/*
* also, every command takes at least one argument so we get that
* first argument right now
*/
result = get_pathname(&cp, &sshc->quote_path1);
if(result) {
if(result == CURLE_OUT_OF_MEMORY)
failf(data, "Out of memory");
else
failf(data, "Syntax error: Bad first parameter");
James Housley
committed
state(conn, SSH_SFTP_CLOSE);
James Housley
committed
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 */
result = get_pathname(&cp, &sshc->quote_path2);
if(result) {
if(result == CURLE_OUT_OF_MEMORY)
failf(data, "Out of memory");
else
failf(data, "Syntax error in chgrp/chmod/chown: "
"Bad second parameter");
James Housley
committed
Curl_safefree(sshc->quote_path1);
sshc->quote_path1 = NULL;
state(conn, SSH_SFTP_CLOSE);
James Housley
committed
break;
}
memset(&sshc->quote_attrs, 0, sizeof(LIBSSH2_SFTP_ATTRIBUTES));
state(conn, SSH_SFTP_QUOTE_STAT);
break;
James Housley
committed
}
else if(curl_strnequal(sshc->quote_item->data, "ln ", 3) ||
curl_strnequal(sshc->quote_item->data, "symlink ", 8)) {
/* symbolic linking */
/* sshc->quote_path1 is the source */
/* get the destination */
result = get_pathname(&cp, &sshc->quote_path2);
if(result) {
if(result == CURLE_OUT_OF_MEMORY)
failf(data, "Out of memory");
else
failf(data,
"Syntax error in ln/symlink: Bad second parameter");
James Housley
committed
Curl_safefree(sshc->quote_path1);
sshc->quote_path1 = NULL;
state(conn, SSH_SFTP_CLOSE);
James Housley
committed
break;
}
state(conn, SSH_SFTP_QUOTE_SYMLINK);
break;
James Housley
committed
}
else if(curl_strnequal(sshc->quote_item->data, "mkdir ", 6)) {
/* create dir */
state(conn, SSH_SFTP_QUOTE_MKDIR);
break;
}
else if(curl_strnequal(sshc->quote_item->data, "rename ", 7)) {
/* rename file */
/* first param is the source path */
/* second param is the dest. path */
result = get_pathname(&cp, &sshc->quote_path2);
if(result) {
if(result == CURLE_OUT_OF_MEMORY)
failf(data, "Out of memory");
else
failf(data, "Syntax error in rename: Bad second parameter");
James Housley
committed
Curl_safefree(sshc->quote_path1);
sshc->quote_path1 = NULL;
state(conn, SSH_SFTP_CLOSE);
James Housley
committed
break;
}
state(conn, SSH_SFTP_QUOTE_RENAME);
}
else if(curl_strnequal(sshc->quote_item->data, "rmdir ", 6)) {
/* delete dir */
state(conn, SSH_SFTP_QUOTE_RMDIR);
James Housley
committed
break;
else if(curl_strnequal(sshc->quote_item->data, "rm ", 3)) {
state(conn, SSH_SFTP_QUOTE_UNLINK);
James Housley
committed
break;
}
failf(data, "Unknown SFTP command");
Curl_safefree(sshc->quote_path1);
sshc->quote_path1 = NULL;
Curl_safefree(sshc->quote_path2);
sshc->quote_path2 = NULL;
state(conn, SSH_SFTP_CLOSE);
sshc->actualcode = CURLE_QUOTE_ERROR;
break;
}
}
if(!sshc->quote_item) {
state(conn, SSH_SFTP_TRANS_INIT);
}
break;
James Housley
committed
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;
James Housley
committed
}
else {
state(conn, SSH_SFTP_TRANS_INIT);
James Housley
committed
}
}
break;
case SSH_SFTP_QUOTE_STAT:
if(!curl_strnequal(sshc->quote_item->data, "chmod", 5)) {
/* Since chown and chgrp only set owner OR group but libssh2 wants to
* set them both at once, we need to obtain the current ownership first.
* This takes an extra protocol round trip.
*/
rc = libssh2_sftp_stat(sshc->sftp_session, sshc->quote_path2,
&sshc->quote_attrs);
if(rc == LIBSSH2_ERROR_EAGAIN) {
break;
}
else if(rc != 0) { /* get those attributes */
err = libssh2_sftp_last_error(sshc->sftp_session);
Curl_safefree(sshc->quote_path1);
sshc->quote_path1 = NULL;
Curl_safefree(sshc->quote_path2);
sshc->quote_path2 = NULL;
failf(data, "Attempt to get SFTP stats failed: %s",
sftp_libssh2_strerror(err));
state(conn, SSH_SFTP_CLOSE);
sshc->actualcode = CURLE_QUOTE_ERROR;
break;
}
}
James Housley
committed
/* Now set the new attributes... */
if(curl_strnequal(sshc->quote_item->data, "chgrp", 5)) {
sshc->quote_attrs.gid = strtol(sshc->quote_path1, NULL, 10);
sshc->quote_attrs.flags = LIBSSH2_SFTP_ATTR_UIDGID;
if(sshc->quote_attrs.gid == 0 && !ISDIGIT(sshc->quote_path1[0])) {
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");
James Housley
committed
state(conn, SSH_SFTP_CLOSE);
sshc->actualcode = CURLE_QUOTE_ERROR;
James Housley
committed
break;
}
}
else if(curl_strnequal(sshc->quote_item->data, "chmod", 5)) {
sshc->quote_attrs.permissions = strtol(sshc->quote_path1, NULL, 8);
sshc->quote_attrs.flags = LIBSSH2_SFTP_ATTR_PERMISSIONS;
/* permissions are octal */
if(sshc->quote_attrs.permissions == 0 &&
!ISDIGIT(sshc->quote_path1[0])) {
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: chmod permissions not a number");
James Housley
committed
state(conn, SSH_SFTP_CLOSE);
sshc->actualcode = CURLE_QUOTE_ERROR;
James Housley
committed
break;
}
}
else if(curl_strnequal(sshc->quote_item->data, "chown", 5)) {
sshc->quote_attrs.uid = strtol(sshc->quote_path1, NULL, 10);
sshc->quote_attrs.flags = LIBSSH2_SFTP_ATTR_UIDGID;
if(sshc->quote_attrs.uid == 0 && !ISDIGIT(sshc->quote_path1[0])) {
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");
James Housley
committed
state(conn, SSH_SFTP_CLOSE);
sshc->actualcode = CURLE_QUOTE_ERROR;
James Housley
committed
break;
}
}
James Housley
committed
/* Now send the completed structure... */
state(conn, SSH_SFTP_QUOTE_SETSTAT);
break;
James Housley
committed
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;
James Housley
committed
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;
James Housley
committed
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;
James Housley
committed
break;
}
state(conn, SSH_SFTP_NEXT_QUOTE);
break;
James Housley
committed
case SSH_SFTP_QUOTE_RENAME:
rc = libssh2_sftp_rename(sshc->sftp_session, sshc->quote_path1,
sshc->quote_path2);
if(rc == LIBSSH2_ERROR_EAGAIN) {
James Housley
committed
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;
James Housley
committed
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;
James Housley
committed
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);
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;
James Housley
committed
case SSH_SFTP_TRANS_INIT:
if(data->set.upload)
state(conn, SSH_SFTP_UPLOAD_INIT);
else {
if(data->set.opt_no_body)
state(conn, SSH_STOP);
else if(sftp_scp->path[strlen(sftp_scp->path)-1] == '/')
state(conn, SSH_SFTP_READDIR_INIT);
else
state(conn, SSH_SFTP_DOWNLOAD_INIT);
}
break;
case SSH_SFTP_UPLOAD_INIT:
{
unsigned long flags;
/*
* NOTE!!! libssh2 requires that the destination path is a full path
* that includes the destination file and name OR ends in a "/"
* If this is not done the destination file will be named the
* same name as the last directory in the path.
*/
if(data->state.resume_from != 0) {
LIBSSH2_SFTP_ATTRIBUTES attrs;
if(data->state.resume_from< 0) {
rc = libssh2_sftp_stat(sshc->sftp_session, sftp_scp->path, &attrs);
if(rc == LIBSSH2_ERROR_EAGAIN) {
break;
}
else if(rc) {
data->state.resume_from = 0;
}
else {
data->state.resume_from = attrs.filesize;
}
}
}
if(data->set.ftp_append)
/* Try to open for append, but create if nonexisting */
flags = LIBSSH2_FXF_WRITE|LIBSSH2_FXF_CREAT|LIBSSH2_FXF_APPEND;
else if (data->state.resume_from > 0)
/* If we have restart position then open for append */
flags = LIBSSH2_FXF_WRITE|LIBSSH2_FXF_APPEND;
else
/* Clear file before writing (normal behaviour) */
flags = LIBSSH2_FXF_WRITE|LIBSSH2_FXF_CREAT|LIBSSH2_FXF_TRUNC;
sshc->sftp_handle =
libssh2_sftp_open(sshc->sftp_session, sftp_scp->path,
flags, data->set.new_file_perms);
if(!sshc->sftp_handle) {
Daniel Stenberg
committed
rc = libssh2_session_last_errno(sshc->ssh_session);
if(LIBSSH2_ERROR_EAGAIN == rc)
James Housley
committed
break;
else {
Daniel Stenberg
committed
if(LIBSSH2_ERROR_SFTP_PROTOCOL == rc)
/* only when there was an SFTP protocol error can we extract
the sftp error! */
err = libssh2_sftp_last_error(sshc->sftp_session);
else
err = -1; /* not an sftp error at all */
if(sshc->secondCreateDirs) {
James Housley
committed
state(conn, SSH_SFTP_CLOSE);
Daniel Stenberg
committed
sshc->actualcode = err>= LIBSSH2_FX_OK?
sftp_libssh2_error_to_CURLE(err):CURLE_SSH;
failf(data, "Creating the dir/file failed: %s",
sftp_libssh2_strerror(err));
James Housley
committed
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);
Daniel Stenberg
committed
sshc->actualcode = err>= LIBSSH2_FX_OK?
sftp_libssh2_error_to_CURLE(err):CURLE_SSH;
if(!sshc->actualcode) {
/* Sometimes, for some reason libssh2_sftp_last_error() returns zero
even though libssh2_sftp_open() failed previously! We need to
work around that! */
sshc->actualcode = CURLE_SSH;
err=-1;
}
failf(data, "Upload failed: %s (%d/%d)",
err>= LIBSSH2_FX_OK?sftp_libssh2_strerror(err):"ssh error",
err, rc);
break;
James Housley
committed
}
}
/* If we have restart point then we need to seek to the correct position. */
if(data->state.resume_from > 0) {
/* Let's read off the proper amount of bytes from the input. */
if(conn->seek_func) {
Daniel Stenberg
committed
seekerr = conn->seek_func(conn->seek_client, data->state.resume_from,
SEEK_SET);
}
if(seekerr != CURL_SEEKFUNC_OK){
Daniel Stenberg
committed
if(seekerr != CURL_SEEKFUNC_CANTSEEK) {
failf(data, "Could not seek stream");
return CURLE_FTP_COULDNT_USE_REST;
}
Daniel Stenberg
committed
1459
1460
1461
1462
1463
1464
1465
1466
1467
1468
1469
1470
1471
1472
1473
1474
1475
1476
1477
1478
1479
1480
1481
1482
1483
/* seekerr == CURL_SEEKFUNC_CANTSEEK (can't seek to offset) */
else {
curl_off_t passed=0;
curl_off_t readthisamountnow;
curl_off_t actuallyread;
do {
readthisamountnow = (data->state.resume_from - passed);
if(readthisamountnow > BUFSIZE)
readthisamountnow = BUFSIZE;
actuallyread =
(curl_off_t) conn->fread_func(data->state.buffer, 1,
(size_t)readthisamountnow,
conn->fread_in);
passed += actuallyread;
if((actuallyread <= 0) || (actuallyread > readthisamountnow)) {
/* this checks for greater-than only to make sure that the
CURL_READFUNC_ABORT return code still aborts */
failf(data, "Failed to read data");
return CURLE_FTP_COULDNT_USE_REST;
}
} while(passed < data->state.resume_from);
}
}
/* now, decrease the size of the read */
if(data->set.infilesize>0) {
data->set.infilesize -= data->state.resume_from;
data->req.size = data->set.infilesize;
Curl_pgrsSetUploadSize(data, data->set.infilesize);
}
Daniel Stenberg
committed
SFTP_SEEK(sshc->sftp_handle, data->state.resume_from);
}
if(data->set.infilesize>0) {
data->req.size = data->set.infilesize;
Curl_pgrsSetUploadSize(data, data->set.infilesize);
}
/* upload data */
result = Curl_setup_transfer(conn, -1, -1, FALSE, NULL,
FIRSTSOCKET, NULL);
Kamil Dudka
committed
/* not set by Curl_setup_transfer to preserve keepon bits */
conn->sockfd = conn->writesockfd;
if(result) {
state(conn, SSH_SFTP_CLOSE);
sshc->actualcode = result;
}
else {
Daniel Stenberg
committed
/* store this original bitmask setup to use later on if we can't figure
out a "real" bitmask */
sshc->orig_waitfor = data->req.keepon;
state(conn, SSH_STOP);
}
break;
}
case SSH_SFTP_CREATE_DIRS_INIT:
if(strlen(sftp_scp->path) > 1) {
sshc->slash_pos = sftp_scp->path + 1; /* ignore the leading '/' */
James Housley
committed
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);
James Housley
committed
break;
}
else {
state(conn, SSH_SFTP_UPLOAD_INIT);
}
break;
James Housley
committed
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;
James Housley
committed
/*
* 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
James Housley
committed
*/
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);
James Housley
committed
state(conn, SSH_SFTP_CLOSE);
Daniel Stenberg
committed
sshc->actualcode = result?result:CURLE_SSH;
break;
}
}
state(conn, SSH_SFTP_CREATE_DIRS);
break;
case SSH_SFTP_READDIR_INIT:
/*
* This is a directory that we are trying to get, so produce a
* directory listing
*/
sshc->sftp_handle = libssh2_sftp_opendir(sshc->sftp_session,
sftp_scp->path);
if(!sshc->sftp_handle) {
if(libssh2_session_last_errno(sshc->ssh_session) ==
LIBSSH2_ERROR_EAGAIN) {
rc = LIBSSH2_ERROR_EAGAIN;
James Housley
committed
break;
}
else {
err = libssh2_sftp_last_error(sshc->sftp_session);
failf(data, "Could not open directory for reading: %s",
sftp_libssh2_strerror(err));
James Housley
committed
state(conn, SSH_SFTP_CLOSE);
Daniel Stenberg
committed
result = sftp_libssh2_error_to_CURLE(err);
sshc->actualcode = result?result:CURLE_SSH;
James Housley
committed
break;
}
}
if((sshc->readdir_filename = malloc(PATH_MAX+1)) == NULL) {
state(conn, SSH_SFTP_CLOSE);
sshc->actualcode = CURLE_OUT_OF_MEMORY;
James Housley
committed
break;
}
if((sshc->readdir_longentry = malloc(PATH_MAX+1)) == NULL) {
Curl_safefree(sshc->readdir_filename);
sshc->readdir_filename = NULL;
state(conn, SSH_SFTP_CLOSE);
sshc->actualcode = CURLE_OUT_OF_MEMORY;
break;
}
state(conn, SSH_SFTP_READDIR);
break;
James Housley
committed
case SSH_SFTP_READDIR:
sshc->readdir_len = libssh2_sftp_readdir_ex(sshc->sftp_handle,
sshc->readdir_filename,
PATH_MAX,
sshc->readdir_longentry,
PATH_MAX,
&sshc->readdir_attrs);
if(sshc->readdir_len == LIBSSH2_ERROR_EAGAIN) {
rc = LIBSSH2_ERROR_EAGAIN;
break;
}
if(sshc->readdir_len > 0) {
sshc->readdir_filename[sshc->readdir_len] = '\0';
James Housley
committed
if(data->set.ftp_list_only) {
char *tmpLine;
James Housley
committed
tmpLine = aprintf("%s\n", sshc->readdir_filename);
if(tmpLine == NULL) {
state(conn, SSH_SFTP_CLOSE);
sshc->actualcode = CURLE_OUT_OF_MEMORY;
break;
}
result = Curl_client_write(conn, CLIENTWRITE_BODY,
tmpLine, sshc->readdir_len+1);
Curl_safefree(tmpLine);
if(result) {
state(conn, SSH_STOP);
break;
}
/* since this counts what we send to the client, we include the newline
in this counter */
Daniel Stenberg
committed
data->req.bytecount += sshc->readdir_len+1;
/* output debug output if that is requested */
if(data->set.verbose) {
Curl_debug(data, CURLINFO_DATA_OUT, sshc->readdir_filename,
sshc->readdir_len, conn);
}
}
else {
sshc->readdir_currLen = strlen(sshc->readdir_longentry);
sshc->readdir_totalLen = 80 + sshc->readdir_currLen;
sshc->readdir_line = calloc(sshc->readdir_totalLen, 1);
if(!sshc->readdir_line) {
Curl_safefree(sshc->readdir_filename);
sshc->readdir_filename = NULL;
Curl_safefree(sshc->readdir_longentry);
sshc->readdir_longentry = NULL;
state(conn, SSH_SFTP_CLOSE);
sshc->actualcode = CURLE_OUT_OF_MEMORY;
break;
}
James Housley
committed
memcpy(sshc->readdir_line, sshc->readdir_longentry,
sshc->readdir_currLen);
if((sshc->readdir_attrs.flags & LIBSSH2_SFTP_ATTR_PERMISSIONS) &&
((sshc->readdir_attrs.permissions & LIBSSH2_SFTP_S_IFMT) ==
LIBSSH2_SFTP_S_IFLNK)) {
sshc->readdir_linkPath = malloc(PATH_MAX + 1);
if(sshc->readdir_linkPath == NULL) {
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;
James Housley
committed
break;
}
snprintf(sshc->readdir_linkPath, PATH_MAX, "%s%s", sftp_scp->path,
sshc->readdir_filename);
state(conn, SSH_SFTP_READDIR_LINK);
James Housley
committed
break;
}
state(conn, SSH_SFTP_READDIR_BOTTOM);
James Housley
committed
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);
Daniel Stenberg
committed
result = sftp_libssh2_error_to_CURLE(err);
sshc->actualcode = result?result:CURLE_SSH;
failf(data, "Could not open remote file for reading: %s :: %d",
sftp_libssh2_strerror(err),
libssh2_session_last_errno(sshc->ssh_session));
Curl_safefree(sshc->readdir_filename);
sshc->readdir_filename = NULL;
Curl_safefree(sshc->readdir_longentry);
sshc->readdir_longentry = NULL;
state(conn, SSH_SFTP_CLOSE);
break;
}
break;
case SSH_SFTP_READDIR_LINK:
sshc->readdir_len = libssh2_sftp_readlink(sshc->sftp_session,
sshc->readdir_linkPath,
sshc->readdir_filename,
PATH_MAX);
if(sshc->readdir_len == LIBSSH2_ERROR_EAGAIN) {
rc = LIBSSH2_ERROR_EAGAIN;
break;
}
Curl_safefree(sshc->readdir_linkPath);
sshc->readdir_linkPath = NULL;
sshc->readdir_line = realloc(sshc->readdir_line,
sshc->readdir_totalLen + 4 +
sshc->readdir_len);
if(!sshc->readdir_line) {
Curl_safefree(sshc->readdir_filename);
sshc->readdir_filename = NULL;
Curl_safefree(sshc->readdir_longentry);
sshc->readdir_longentry = NULL;
state(conn, SSH_SFTP_CLOSE);
sshc->actualcode = CURLE_OUT_OF_MEMORY;
James Housley
committed
break;
}
James Housley
committed
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,
sshc->readdir_currLen);
if(result == CURLE_OK) {
/* output debug output if that is requested */
if(data->set.verbose) {
Curl_debug(data, CURLINFO_DATA_OUT, sshc->readdir_line,
sshc->readdir_currLen, conn);
}
Daniel Stenberg
committed
data->req.bytecount += sshc->readdir_currLen;
}
Curl_safefree(sshc->readdir_line);
sshc->readdir_line = NULL;
if(result) {
state(conn, SSH_STOP);
}
else
state(conn, SSH_SFTP_READDIR);
break;
case SSH_SFTP_READDIR_DONE:
if(libssh2_sftp_closedir(sshc->sftp_handle) ==
LIBSSH2_ERROR_EAGAIN) {
rc = LIBSSH2_ERROR_EAGAIN;
1779
1780
1781
1782
1783
1784
1785
1786
1787
1788
1789
1790
1791
1792
1793
1794
1795
1796
1797
1798
1799
1800
1801
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) {
rc = LIBSSH2_ERROR_EAGAIN;
James Housley
committed
break;
}
else {
err = libssh2_sftp_last_error(sshc->sftp_session);
failf(data, "Could not open remote file for reading: %s",
sftp_libssh2_strerror(err));
James Housley
committed
state(conn, SSH_SFTP_CLOSE);
Daniel Stenberg
committed
result = sftp_libssh2_error_to_CURLE(err);
sshc->actualcode = result?result:CURLE_SSH;
James Housley
committed
break;
}
}
state(conn, SSH_SFTP_DOWNLOAD_STAT);
break;
James Housley
committed
case SSH_SFTP_DOWNLOAD_STAT:
{
LIBSSH2_SFTP_ATTRIBUTES attrs;
James Housley
committed
rc = libssh2_sftp_stat(sshc->sftp_session, sftp_scp->path, &attrs);
if(rc == LIBSSH2_ERROR_EAGAIN) {
James Housley
committed
break;
}
else if(rc) {
/*
* libssh2_sftp_open() didn't return an error, so maybe the server
* just doesn't support stat()
*/
Daniel Stenberg
committed
data->req.size = -1;
data->req.maxdownload = -1;
}
else {
Daniel Stenberg
committed
curl_off_t size;
size = attrs.filesize;
if(conn->data->state.use_range) {
curl_off_t from, to;
char *ptr;
char *ptr2;
from=curlx_strtoofft(conn->data->state.range, &ptr, 0);
while(ptr && *ptr && (isspace((int)*ptr) || (*ptr=='-')))
ptr++;
to=curlx_strtoofft(ptr, &ptr2, 0);
Dan Fandrich
committed
if((ptr == ptr2) /* no "to" value given */
|| (to >= size)) {
to = size - 1;
Daniel Stenberg
committed
}
Dan Fandrich
committed
if(from < 0) {
/* from is relative to end of file */
from += size;
}
if(from >= size) {
failf(data, "Offset (%"
FORMAT_OFF_T ") was beyond file size (%" FORMAT_OFF_T ")",
from, attrs.filesize);
return CURLE_BAD_DOWNLOAD_RESUME;
}
if(from > to) {
Daniel Stenberg
committed
from = to;
size = 0;
}
else {
size = to - from + 1;
}
Daniel Stenberg
committed
SFTP_SEEK(conn->proto.sshc.sftp_handle, from);
Daniel Stenberg
committed
}
data->req.size = size;
data->req.maxdownload = size;
Curl_pgrsSetDownloadSize(data, size);
}
James Housley
committed
/* We can resume if we can seek to the resume position */
if(data->state.resume_from) {
if(data->state.resume_from< 0) {
/* We're supposed to download the last abs(from) bytes */
if((curl_off_t)attrs.filesize < -data->state.resume_from) {
failf(data, "Offset (%"
FORMAT_OFF_T ") was beyond file size (%" FORMAT_OFF_T ")",
data->state.resume_from, attrs.filesize);
return CURLE_BAD_DOWNLOAD_RESUME;
}
/* download from where? */
data->state.resume_from = attrs.filesize - data->state.resume_from;
}
else {
if((curl_off_t)attrs.filesize < data->state.resume_from) {
failf(data, "Offset (%" FORMAT_OFF_T
") was beyond file size (%" FORMAT_OFF_T ")",
data->state.resume_from, attrs.filesize);
return CURLE_BAD_DOWNLOAD_RESUME;
}
}
/* Does a completed file need to be seeked and started or closed ? */
/* Now store the number of bytes we are expected to download */
data->req.size = attrs.filesize - data->state.resume_from;
data->req.maxdownload = attrs.filesize - data->state.resume_from;
Curl_pgrsSetDownloadSize(data,
attrs.filesize - data->state.resume_from);
Daniel Stenberg
committed
SFTP_SEEK(sshc->sftp_handle, data->state.resume_from);
}
}
/* Setup the actual download */
if(data->req.size == 0) {
/* no data to transfer */
result = Curl_setup_transfer(conn, -1, -1, FALSE, NULL, -1, NULL);
infof(data, "File already completely downloaded\n");
state(conn, SSH_STOP);
break;
}
else {
result = Curl_setup_transfer(conn, FIRSTSOCKET, data->req.size,
Daniel Stenberg
committed
FALSE, NULL, -1, NULL);
Kamil Dudka
committed
/* not set by Curl_setup_transfer to preserve keepon bits */
conn->writesockfd = conn->sockfd;
/* FIXME: here should be explained why we need it to start the download */
conn->cselect_bits = CURL_CSELECT_IN;
}
if(result) {
state(conn, SSH_SFTP_CLOSE);
sshc->actualcode = result;
}
else {
state(conn, SSH_STOP);
}
break;
James Housley
committed
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) {
infof(data, "Failed to close libssh2 file\n");
}
sshc->sftp_handle = NULL;
}
Curl_safefree(sftp_scp->path);
sftp_scp->path = NULL;
James Housley
committed
DEBUGF(infof(data, "SFTP DONE done\n"));
#if 0 /* PREV */
state(conn, SSH_SFTP_SHUTDOWN);
#endif
state(conn, SSH_STOP);
result = sshc->actualcode;
break;
James Housley
committed
case SSH_SFTP_SHUTDOWN:
Daniel Stenberg
committed
/* during times we get here due to a broken transfer and then the
sftp_handle might not have been taken down so make sure that is done
before we proceed */
if(sshc->sftp_handle) {
rc = libssh2_sftp_close(sshc->sftp_handle);
if(rc == LIBSSH2_ERROR_EAGAIN) {
break;
}
else if(rc < 0) {
infof(data, "Failed to close libssh2 file\n");
}
sshc->sftp_handle = NULL;
}
if(sshc->sftp_session) {
rc = libssh2_sftp_shutdown(sshc->sftp_session);
if(rc == LIBSSH2_ERROR_EAGAIN) {
break;
James Housley
committed
}
else if(rc < 0) {
infof(data, "Failed to stop libssh2 sftp subsystem\n");
}
sshc->sftp_session = NULL;
}
James Housley
committed
Curl_safefree(sshc->homedir);
sshc->homedir = NULL;
James Housley
committed
state(conn, SSH_SESSION_DISCONNECT);
break;
James Housley
committed
case SSH_SCP_TRANS_INIT:
result = ssh_getworkingpath(conn, sshc->homedir, &sftp_scp->path);
if(result) {
sshc->actualcode = result;
state(conn, SSH_STOP);
James Housley
committed
break;
}
James Housley
committed
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;