Newer
Older
/*
* At this point we have an authenticated ssh session.
*/
infof(conn->data, "Authentication complete\n");
conn->sockfd = sock;
conn->writesockfd = CURL_SOCKET_BAD;
if (conn->protocol == PROT_SFTP) {
/*
* Start the libssh2 sftp session
*/
ssh->sftp_session = libssh2_sftp_init(ssh->ssh_session);
if (ssh->sftp_session == NULL) {
failf(data, "Failure initialising sftp session\n");
libssh2_session_free(ssh->ssh_session);
ssh->ssh_session = NULL;
return CURLE_FAILED_INIT;
}
/*
* Get the "home" directory
*/
i = libssh2_sftp_realpath(ssh->sftp_session, ".", tempHome, PATH_MAX-1);
if (i > 0) {
/* It seems that this string is not always NULL terminated */
tempHome[i] = '\0';
ssh->homedir = (char *)strdup(tempHome);
if (!ssh->homedir) {
libssh2_sftp_shutdown(ssh->sftp_session);
ssh->sftp_session = NULL;
libssh2_session_free(ssh->ssh_session);
ssh->ssh_session = NULL;
return CURLE_OUT_OF_MEMORY;
}
}
else {
/* Return the error type */
i = libssh2_sftp_last_error(ssh->sftp_session);
DEBUGF(infof(data, "error = %d\n", i));
}
}
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) {
libssh2_session_free(ssh->ssh_session);
ssh->ssh_session = NULL;
Curl_safefree(working_path);
return CURLE_OUT_OF_MEMORY;
}
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(ssh->homedir) +
working_path_len + 1);
if (real_path == NULL) {
libssh2_sftp_shutdown(ssh->sftp_session);
ssh->sftp_session = NULL;
libssh2_session_free(ssh->ssh_session);
ssh->ssh_session = NULL;
Curl_safefree(ssh->homedir);
ssh->homedir = NULL;
Curl_safefree(working_path);
return CURLE_OUT_OF_MEMORY;
}
/* It is referenced to the home directory, so strip the leading '/' */
memcpy(real_path, ssh->homedir, strlen(ssh->homedir));
real_path[strlen(ssh->homedir)] = '/';
real_path[strlen(ssh->homedir)+1] = '\0';
if (working_path_len > 3) {
memcpy(real_path+strlen(ssh->homedir)+1, working_path + 3,
1 + working_path_len -3);
}
}
else {
real_path = (char *)malloc(working_path_len+1);
if (real_path == NULL) {
libssh2_sftp_shutdown(ssh->sftp_session);
ssh->sftp_session = NULL;
libssh2_session_free(ssh->ssh_session);
ssh->ssh_session = NULL;
Curl_safefree(ssh->homedir);
ssh->homedir = NULL;
Curl_safefree(working_path);
return CURLE_OUT_OF_MEMORY;
}
memcpy(real_path, working_path, 1+working_path_len);
}
}
else {
libssh2_session_free(ssh->ssh_session);
ssh->ssh_session = NULL;
Curl_safefree(working_path);
return CURLE_FAILED_INIT;
Curl_safefree(working_path);
ssh->path = real_path;
*done = TRUE;
return CURLE_OK;
#endif /* !(LIBSSH2_APINO >= 200706012030) */
}
CURLcode Curl_scp_do(struct connectdata *conn, bool *done)
{
struct stat sb;
struct SSHPROTO *scp = conn->data->reqdata.proto.ssh;
CURLcode res = CURLE_OK;
*done = TRUE; /* unconditionally */
if (conn->data->set.upload) {
if(conn->data->set.infilesize < 0) {
failf(conn->data, "SCP requires a known file size for upload");
return CURLE_UPLOAD_FAILED;
}
* 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 (LIBSSH2_APINO >= 200706012030)
do {
scp->ssh_channel = libssh2_scp_send_ex(scp->ssh_session, scp->path,
conn->data->set.new_file_perms,
conn->data->set.infilesize, 0, 0);
if (!scp->ssh_channel &&
(libssh2_session_last_errno(scp->ssh_session) !=
LIBSSH2_ERROR_EAGAIN)) {
int err;
char *err_msg;
err = libssh2_session_error_to_CURLE(
libssh2_session_last_error(scp->ssh_session, &err_msg, NULL, 0));
failf(conn->data, "%s", err_msg);
return err;
}
} while (!scp->ssh_channel);
#else /* !(LIBSSH2_APINO >= 200706012030) */
scp->ssh_channel = libssh2_scp_send_ex(scp->ssh_session, scp->path,
conn->data->set.new_file_perms,
if (!scp->ssh_channel)
return CURLE_FAILED_INIT;
#endif /* !(LIBSSH2_APINO >= 200706012030) */
/* upload data */
res = Curl_setup_transfer(conn, -1, -1, FALSE, NULL, FIRSTSOCKET, NULL);
}
else {
/*
* We must check the remote file; if it is a directory no values will
* be set in sb
curl_off_t bytecount;
memset(&sb, 0, sizeof(struct stat));
#if (LIBSSH2_APINO >= 200706012030)
do {
scp->ssh_channel = libssh2_scp_recv(scp->ssh_session, scp->path, &sb);
if (!scp->ssh_channel &&
(libssh2_session_last_errno(scp->ssh_session) !=
LIBSSH2_ERROR_EAGAIN)) {
if ((sb.st_mode == 0) && (sb.st_atime == 0) && (sb.st_mtime == 0) &&
(sb.st_size == 0)) {
/* Since sb is still empty, it is likely the file was not found */
return CURLE_REMOTE_FILE_NOT_FOUND;
}
return libssh2_session_error_to_CURLE(
libssh2_session_last_error(scp->ssh_session, NULL, NULL, 0));
}
} while (!scp->ssh_channel);
#else /* !(LIBSSH2_APINO >= 200706012030) */
scp->ssh_channel = libssh2_scp_recv(scp->ssh_session, scp->path, &sb);
if (!scp->ssh_channel) {
if ((sb.st_mode == 0) && (sb.st_atime == 0) && (sb.st_mtime == 0) &&
(sb.st_size == 0)) {
/* Since sb is still empty, it is likely the file was not found */
return CURLE_REMOTE_FILE_NOT_FOUND;
}
return libssh2_session_error_to_CURLE(
libssh2_session_last_error(scp->ssh_session, NULL, NULL, 0));
#endif /* !(LIBSSH2_APINO >= 200706012030) */
/* download data */
bytecount = (curl_off_t) sb.st_size;
conn->data->reqdata.maxdownload = (curl_off_t) sb.st_size;
res = Curl_setup_transfer(conn, FIRSTSOCKET,
bytecount, FALSE, NULL, -1, NULL);
}
return res;
}
Daniel Stenberg
committed
CURLcode Curl_scp_done(struct connectdata *conn, CURLcode status,
bool premature)
struct SSHPROTO *scp = conn->data->reqdata.proto.ssh;
Daniel Stenberg
committed
(void)premature; /* not used */
Curl_safefree(scp->path);
scp->path = NULL;
if (scp->ssh_channel) {
#if (LIBSSH2_APINO >= 200706012030)
if (conn->data->set.upload) {
while ((rc = libssh2_channel_send_eof(scp->ssh_channel)) ==
LIBSSH2_ERROR_EAGAIN);
if (rc) {
infof(conn->data, "Failed to send libssh2 channel EOF\n");
}
while ((rc = libssh2_channel_wait_eof(scp->ssh_channel)) ==
LIBSSH2_ERROR_EAGAIN);
if (rc) {
infof(conn->data, "Failed to get channel EOF\n");
}
while ((rc = libssh2_channel_wait_closed(scp->ssh_channel)) ==
LIBSSH2_ERROR_EAGAIN);
if (rc) {
infof(conn->data, "Channel failed to close\n");
}
}
#else /* !(LIBSSH2_APINO >= 200706012030) */
if (conn->data->set.upload &&
libssh2_channel_send_eof(scp->ssh_channel) < 0) {
infof(conn->data, "Failed to send libssh2 channel EOF\n");
}
if (libssh2_channel_close(scp->ssh_channel) < 0) {
infof(conn->data, "Failed to stop libssh2 channel subsystem\n");
#endif /* !(LIBSSH2_APINO >= 200706012030) */
libssh2_channel_free(scp->ssh_channel);
if (scp->ssh_session) {
#if (LIBSSH2_APINO >= 200706012030)
while (libssh2_session_disconnect(scp->ssh_session, "Shutdown") ==
LIBSSH2_ERROR_EAGAIN);
#else /* !(LIBSSH2_APINO >= 200706012030) */
libssh2_session_disconnect(scp->ssh_session, "Shutdown");
#endif /* !(LIBSSH2_APINO >= 200706012030) */
libssh2_session_free(scp->ssh_session);
scp->ssh_session = NULL;
free(conn->data->reqdata.proto.ssh);
conn->data->reqdata.proto.ssh = NULL;
Curl_pgrsDone(conn);
(void)status; /* unused */
(void) rc; /* possiby unused */
return CURLE_OK;
}
/* return number of received (decrypted) bytes */
ssize_t Curl_scp_send(struct connectdata *conn, int sockindex,
void *mem, size_t len)
{
ssize_t nwrite;
/* libssh2_channel_write() returns int
*
* NOTE: we should not store nor rely on connection-related data to be
* in the SessionHandle struct
*/
#if defined(LIBSSH2CHANNEL_EAGAIN) && (LIBSSH2_APINO < 200706012030)
nwrite = (ssize_t)
libssh2_channel_writenb(conn->data->reqdata.proto.ssh->ssh_channel,
mem, len);
#else
nwrite = (ssize_t)
libssh2_channel_write(conn->data->reqdata.proto.ssh->ssh_channel,
mem, len);
#if (LIBSSH2_APINO >= 200706012030)
if (nwrite == LIBSSH2_ERROR_EAGAIN) {
return 0;
}
#endif
(void)sockindex;
return nwrite;
}
/*
* If the read would block (EWOULDBLOCK) we return -1. Otherwise we return
* a regular CURLcode value.
*/
ssize_t Curl_scp_recv(struct connectdata *conn, int sockindex,
char *mem, size_t len)
{
ssize_t nread;
(void)sockindex; /* we only support SCP on the fixed known primary socket */
/* libssh2_channel_read() returns int
*
* NOTE: we should not store nor rely on connection-related data to be
* in the SessionHandle struct
*/
#if defined(LIBSSH2CHANNEL_EAGAIN) && (LIBSSH2_APINO < 200706012030)
/* we prefer the non-blocking API but that didn't exist previously */
nread = (ssize_t)
libssh2_channel_readnb(conn->data->reqdata.proto.ssh->ssh_channel,
mem, len);
#else
nread = (ssize_t)
libssh2_channel_read(conn->data->reqdata.proto.ssh->ssh_channel,
mem, len);
return nread;
}
/*
* =============== SFTP ===============
*/
CURLcode Curl_sftp_do(struct connectdata *conn, bool *done)
{
LIBSSH2_SFTP_ATTRIBUTES attrs;
struct SSHPROTO *sftp = conn->data->reqdata.proto.ssh;
CURLcode res = CURLE_OK;
struct SessionHandle *data = conn->data;
curl_off_t bytecount = 0;
char *buf = data->state.buffer;
*done = TRUE; /* unconditionally */
/* Send any quote commands */
if(conn->data->set.quote) {
infof(conn->data, "Sending quote commands\n");
res = sftp_sendquote(conn, conn->data->set.quote);
if (res != CURLE_OK)
return res;
}
if (data->set.upload) {
/*
* 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 (LIBSSH2_APINO >= 200706012030)
do {
sftp->sftp_handle =
libssh2_sftp_open(sftp->sftp_session, sftp->path,
LIBSSH2_FXF_WRITE|LIBSSH2_FXF_CREAT|LIBSSH2_FXF_TRUNC,
data->set.new_file_perms);
if (!sftp->sftp_handle &&
(libssh2_session_last_errno(sftp->ssh_session) !=
LIBSSH2_ERROR_EAGAIN)) {
err = libssh2_sftp_last_error(sftp->sftp_session);
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,
data->set.new_file_perms);
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) */
sftp->sftp_handle =
libssh2_sftp_open(sftp->sftp_session, sftp->path,
LIBSSH2_FXF_WRITE|LIBSSH2_FXF_CREAT|LIBSSH2_FXF_TRUNC,
data->set.new_file_perms);
if (!sftp->sftp_handle) {
err = libssh2_sftp_last_error(sftp->sftp_session);
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,
data->set.new_file_perms);
}
}
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) */
/* upload data */
res = Curl_setup_transfer(conn, -1, -1, FALSE, NULL, FIRSTSOCKET, NULL);
}
else {
if (sftp->path[strlen(sftp->path)-1] == '/') {
/*
* This is a directory that we are trying to get, so produce a
* directory listing
*
* **BLOCKING behaviour** This should be made into a state machine and
* get a separate function called from Curl_sftp_recv() when there is
* data to read from the network, instead of "hanging" here.
*/
char filename[PATH_MAX+1];
int len, totalLen, currLen;
char *line;
#if (LIBSSH2_APINO >= 200706012030)
do {
sftp->sftp_handle =
libssh2_sftp_opendir(sftp->sftp_session, sftp->path);
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 directory for reading: %s",
sftp_libssh2_strerror(err));
return sftp_libssh2_error_to_CURLE(err);
}
} while (!sftp->sftp_handle);
#else /* !(LIBSSH2_APINO >= 200706012030) */
sftp->sftp_handle =
libssh2_sftp_opendir(sftp->sftp_session, sftp->path);
if (!sftp->sftp_handle) {
err = libssh2_sftp_last_error(sftp->sftp_session);
failf(conn->data, "Could not open directory for reading: %s",
sftp_libssh2_strerror(err));
return sftp_libssh2_error_to_CURLE(err);
}
#endif /* !(LIBSSH2_APINO >= 200706012030) */
do {
#if (LIBSSH2_APINO >= 200706012030)
while ((len = libssh2_sftp_readdir(sftp->sftp_handle, filename,
PATH_MAX, &attrs)) ==
LIBSSH2_ERROR_EAGAIN);
#else /* !(LIBSSH2_APINO >= 200706012030) */
len = libssh2_sftp_readdir(sftp->sftp_handle, filename,
PATH_MAX, &attrs);
#endif /* !(LIBSSH2_APINO >= 200706012030) */
if (len > 0) {
filename[len] = '\0';
if (data->set.ftp_list_only) {
char *tmpLine;
tmpLine = aprintf("%s\n", filename);
if (tmpLine == NULL) {
return CURLE_OUT_OF_MEMORY;
}
res = Curl_client_write(conn, CLIENTWRITE_BODY, tmpLine, 0);
Curl_safefree(tmpLine);
else {
totalLen = 80 + len;
line = (char *)malloc(totalLen);
if (!line)
return CURLE_OUT_OF_MEMORY;
1501
1502
1503
1504
1505
1506
1507
1508
1509
1510
1511
1512
1513
1514
1515
1516
1517
1518
1519
1520
1521
1522
1523
1524
1525
1526
1527
1528
1529
1530
1531
1532
1533
1534
1535
1536
1537
1538
1539
1540
1541
1542
1543
1544
1545
1546
1547
1548
1549
1550
1551
1552
1553
1554
if (!(attrs.flags & LIBSSH2_SFTP_ATTR_UIDGID))
attrs.uid = attrs.gid =0;
currLen = snprintf(line, totalLen, "---------- 1 %5d %5d",
attrs.uid, attrs.gid);
if (attrs.flags & LIBSSH2_SFTP_ATTR_PERMISSIONS) {
if ((attrs.permissions & LIBSSH2_SFTP_S_IFMT) ==
LIBSSH2_SFTP_S_IFDIR) {
line[0] = 'd';
}
else if ((attrs.permissions & LIBSSH2_SFTP_S_IFMT) ==
LIBSSH2_SFTP_S_IFLNK) {
line[0] = 'l';
}
else if ((attrs.permissions & LIBSSH2_SFTP_S_IFMT) ==
LIBSSH2_SFTP_S_IFSOCK) {
line[0] = 's';
}
else if ((attrs.permissions & LIBSSH2_SFTP_S_IFMT) ==
LIBSSH2_SFTP_S_IFCHR) {
line[0] = 'c';
}
else if ((attrs.permissions & LIBSSH2_SFTP_S_IFMT) ==
LIBSSH2_SFTP_S_IFBLK) {
line[0] = 'b';
}
if (attrs.permissions & LIBSSH2_SFTP_S_IRUSR) {
line[1] = 'r';
}
if (attrs.permissions & LIBSSH2_SFTP_S_IWUSR) {
line[2] = 'w';
}
if (attrs.permissions & LIBSSH2_SFTP_S_IXUSR) {
line[3] = 'x';
}
if (attrs.permissions & LIBSSH2_SFTP_S_IRGRP) {
line[4] = 'r';
}
if (attrs.permissions & LIBSSH2_SFTP_S_IWGRP) {
line[5] = 'w';
}
if (attrs.permissions & LIBSSH2_SFTP_S_IXGRP) {
line[6] = 'x';
}
if (attrs.permissions & LIBSSH2_SFTP_S_IROTH) {
line[7] = 'r';
}
if (attrs.permissions & LIBSSH2_SFTP_S_IWOTH) {
line[8] = 'w';
}
if (attrs.permissions & LIBSSH2_SFTP_S_IXOTH) {
line[9] = 'x';
}
if (attrs.flags & LIBSSH2_SFTP_ATTR_SIZE) {
currLen += snprintf(line+currLen, totalLen-currLen, "%11lld",
attrs.filesize);
1560
1561
1562
1563
1564
1565
1566
1567
1568
1569
1570
1571
1572
1573
1574
1575
1576
1577
1578
1579
1580
1581
1582
if (attrs.flags & LIBSSH2_SFTP_ATTR_ACMODTIME) {
static const char * const months[12] = {
"Jan", "Feb", "Mar", "Apr", "May", "Jun",
"Jul", "Aug", "Sep", "Oct", "Nov", "Dec"};
struct tm *nowParts;
time_t now, remoteTime;
now = time(NULL);
remoteTime = (time_t)attrs.mtime;
nowParts = localtime(&remoteTime);
if ((time_t)attrs.mtime > (now - (3600 * 24 * 180))) {
currLen += snprintf(line+currLen, totalLen-currLen,
" %s %2d %2d:%02d",
months[nowParts->tm_mon],
nowParts->tm_mday, nowParts->tm_hour,
nowParts->tm_min);
}
else {
currLen += snprintf(line+currLen, totalLen-currLen,
" %s %2d %5d", months[nowParts->tm_mon],
nowParts->tm_mday, 1900+nowParts->tm_year);
}
currLen += snprintf(line+currLen, totalLen-currLen, " %s",
filename);
if ((attrs.flags & LIBSSH2_SFTP_ATTR_PERMISSIONS) &&
((attrs.permissions & LIBSSH2_SFTP_S_IFMT) ==
LIBSSH2_SFTP_S_IFLNK)) {
char linkPath[PATH_MAX + 1];
snprintf(linkPath, PATH_MAX, "%s%s", sftp->path, filename);
#if (LIBSSH2_APINO >= 200706012030)
while ((len = libssh2_sftp_readlink(sftp->sftp_session, linkPath,
filename, PATH_MAX)) ==
LIBSSH2_ERROR_EAGAIN);
#else /* !(LIBSSH2_APINO >= 200706012030) */
len = libssh2_sftp_readlink(sftp->sftp_session, linkPath,
filename, PATH_MAX);
#endif /* !(LIBSSH2_APINO >= 200706012030) */
line = realloc(line, totalLen + 4 + len);
if (!line)
return CURLE_OUT_OF_MEMORY;
currLen += snprintf(line+currLen, totalLen-currLen, " -> %s",
filename);
currLen += snprintf(line+currLen, totalLen-currLen, "\n");
res = Curl_client_write(conn, CLIENTWRITE_BODY, line, 0);
free(line);
}
}
else if (len <= 0) {
break;
}
} while (1);
#if (LIBSSH2_APINO >= 200706012030)
while (libssh2_sftp_closedir(sftp->sftp_handle) == LIBSSH2_ERROR_EAGAIN);
#else /* !(LIBSSH2_APINO >= 200706012030) */
libssh2_sftp_closedir(sftp->sftp_handle);
#endif /* !(LIBSSH2_APINO >= 200706012030) */
sftp->sftp_handle = NULL;
/* no data to transfer */
res = Curl_setup_transfer(conn, -1, -1, FALSE, NULL, -1, NULL);
}
else {
/*
* Work on getting the specified file
*/
#if (LIBSSH2_APINO >= 200706012030)
do {
sftp->sftp_handle =
libssh2_sftp_open(sftp->sftp_session, sftp->path, LIBSSH2_FXF_READ,
data->set.new_file_perms);
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 reading: %s",
sftp_libssh2_strerror(err));
return sftp_libssh2_error_to_CURLE(err);
}
} while (!sftp->sftp_handle);
#else /* !(LIBSSH2_APINO >= 200706012030) */
sftp->sftp_handle =
libssh2_sftp_open(sftp->sftp_session, sftp->path, LIBSSH2_FXF_READ,
data->set.new_file_perms);
if (!sftp->sftp_handle) {
err = libssh2_sftp_last_error(sftp->sftp_session);
failf(conn->data, "Could not open remote file for reading: %s",
sftp_libssh2_strerror(err));
return sftp_libssh2_error_to_CURLE(err);
}
#endif /* !(LIBSSH2_APINO >= 200706012030) */
#if (LIBSSH2_APINO >= 200706012030)
while ((rc = libssh2_sftp_stat(sftp->sftp_session, sftp->path, &attrs))
== LIBSSH2_ERROR_EAGAIN);
#else /* !(LIBSSH2_APINO >= 200706012030) */
rc = libssh2_sftp_stat(sftp->sftp_session, sftp->path, &attrs);
#endif /* !(LIBSSH2_APINO >= 200706012030) */
if (rc) {
1664
1665
1666
1667
1668
1669
1670
1671
1672
1673
1674
1675
1676
1677
1678
1679
1680
1681
1682
1683
1684
1685
1686
1687
1688
/*
* 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);
}
Curl_pgrsTime(data, TIMER_STARTTRANSFER);
/* Now download data. The libssh2 0.14 doesn't offer any way to do this
without using this BLOCKING approach, so here's room for improvement
once libssh2 can return EWOULDBLOCK to us. */
#if 0
/* code left here just because this is what this function will use the
day libssh2 is improved */
res = Curl_setup_transfer(conn, FIRSTSOCKET,
bytecount, FALSE, NULL, -1, NULL);
#endif
while (res == CURLE_OK) {
#if (LIBSSH2_APINO >= 200706012030)
ssize_t nread;
while ((nread = libssh2_sftp_read(data->reqdata.proto.ssh->sftp_handle,
buf, BUFSIZE-1)) == LIBSSH2_ERROR_EAGAIN);
#else /* !(LIBSSH2_APINO >= 200706012030) */
size_t nread;
/* NOTE: most *read() functions return ssize_t but this returns size_t
which normally is unsigned! */
nread = libssh2_sftp_read(data->reqdata.proto.ssh->sftp_handle,
buf, BUFSIZE-1);
#endif /* !(LIBSSH2_APINO >= 200706012030) */
if (nread > 0)
buf[nread] = 0;
#if (LIBSSH2_APINO >= 200706012030)
if (nread <= 0)
break;
#else /* !(LIBSSH2_APINO >= 200706012030) */
/* this check can be changed to a <= 0 when nread is changed to a
signed variable type */
if ((nread == 0) || (nread == (size_t)~0))
break;
#endif /* !(LIBSSH2_APINO >= 200706012030) */
1714
1715
1716
1717
1718
1719
1720
1721
1722
1723
1724
1725
1726
1727
1728
1729
1730
1731
1732
1733
1734
1735
1736
1737
1738
1739
1740
bytecount += nread;
res = Curl_client_write(conn, CLIENTWRITE_BODY, buf, nread);
if(res)
return res;
Curl_pgrsSetDownloadCounter(data, bytecount);
if(Curl_pgrsUpdate(conn))
res = CURLE_ABORTED_BY_CALLBACK;
else {
struct timeval now = Curl_tvnow();
res = Curl_speedcheck(data, now);
}
}
if(Curl_pgrsUpdate(conn))
res = CURLE_ABORTED_BY_CALLBACK;
/* no (more) data to transfer */
res = Curl_setup_transfer(conn, -1, -1, FALSE, NULL, -1, NULL);
}
}
return res;
}
Daniel Stenberg
committed
CURLcode Curl_sftp_done(struct connectdata *conn, CURLcode status,
bool premature)
{
struct SSHPROTO *sftp = conn->data->reqdata.proto.ssh;
Dan Fandrich
committed
CURLcode rc = CURLE_OK;
Daniel Stenberg
committed
(void)premature; /* not used */
Curl_safefree(sftp->path);
sftp->path = NULL;
Curl_safefree(sftp->homedir);
sftp->homedir = NULL;
if (sftp->sftp_handle) {
#if (LIBSSH2_APINO >= 200706012030)
while ((ret = libssh2_sftp_close(sftp->sftp_handle)) ==
LIBSSH2_ERROR_EAGAIN);
if (ret < 0) {
infof(conn->data, "Failed to close libssh2 file\n");
}
#else /* !(LIBSSH2_APINO >= 200706012030) */
if (libssh2_sftp_close(sftp->sftp_handle) < 0) {
infof(conn->data, "Failed to close libssh2 file\n");
}
#endif /* !(LIBSSH2_APINO >= 200706012030) */
/* Before we shut down, see if there are any post-quote commands to send: */
if(!status && !premature && conn->data->set.postquote) {
infof(conn->data, "Sending postquote commands\n");
rc = sftp_sendquote(conn, conn->data->set.postquote);
}
if (sftp->sftp_session) {
#if (LIBSSH2_APINO >= 200706012030)
while ((ret = libssh2_sftp_shutdown(sftp->sftp_session)) ==
LIBSSH2_ERROR_EAGAIN);
if (ret < 0) {
infof(conn->data, "Failed to stop libssh2 sftp subsystem\n");
}
#else /* !(LIBSSH2_APINO >= 200706012030) */
if (libssh2_sftp_shutdown(sftp->sftp_session) < 0) {
infof(conn->data, "Failed to stop libssh2 sftp subsystem\n");
}
#endif /* !(LIBSSH2_APINO >= 200706012030) */
}
if (sftp->ssh_channel) {
#if (LIBSSH2_APINO >= 200706012030)
while ((ret = libssh2_channel_close(sftp->ssh_channel)) ==
LIBSSH2_ERROR_EAGAIN);
if (ret < 0) {
infof(conn->data, "Failed to stop libssh2 channel subsystem\n");
}
#else /* !(LIBSSH2_APINO >= 200706012030) */
if (libssh2_channel_close(sftp->ssh_channel) < 0) {
infof(conn->data, "Failed to stop libssh2 channel subsystem\n");
}
#endif /* !(LIBSSH2_APINO >= 200706012030) */
}
if (sftp->ssh_session) {
#if (LIBSSH2_APINO >= 200706012030)
while (libssh2_session_disconnect(sftp->ssh_session, "Shutdown") ==
LIBSSH2_ERROR_EAGAIN);
#else /* !(LIBSSH2_APINO >= 200706012030) */
libssh2_session_disconnect(sftp->ssh_session, "Shutdown");
#endif /* !(LIBSSH2_APINO >= 200706012030) */
libssh2_session_free(sftp->ssh_session);
sftp->ssh_session = NULL;
}
free(conn->data->reqdata.proto.ssh);
conn->data->reqdata.proto.ssh = NULL;
Curl_pgrsDone(conn);
(void)status; /* unused */
(void)ret; /* possibly unused */
Dan Fandrich
committed
return rc;
}
/* return number of received (decrypted) bytes */
ssize_t Curl_sftp_send(struct connectdata *conn, int sockindex,
void *mem, size_t len)
{
ssize_t nwrite; /* libssh2_sftp_write() used to return size_t in 0.14
but is changed to ssize_t in 0.15! */
#if defined(LIBSSH2SFTP_EAGAIN) && (LIBSSH2_APINO < 200706012030)
Daniel Stenberg
committed
/* we prefer the non-blocking API but that didn't exist previously */
nwrite = (ssize_t)
libssh2_sftp_writenb(conn->data->reqdata.proto.ssh->sftp_handle, mem, len);
#else
nwrite = (ssize_t)
libssh2_sftp_write(conn->data->reqdata.proto.ssh->sftp_handle, mem, len);
#if (LIBSSH2_APINO >= 200706012030)
if (nwrite == LIBSSH2_ERROR_EAGAIN) {
return 0;
}
#endif
Daniel Stenberg
committed
#endif
(void)sockindex;
return nwrite;
}
/*
* If the read would block (EWOULDBLOCK) we return -1. Otherwise we return
* a regular CURLcode value.
*/
ssize_t Curl_sftp_recv(struct connectdata *conn, int sockindex,
char *mem, size_t len)
{
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)
libssh2_sftp_readnb(conn->data->reqdata.proto.ssh->sftp_handle, mem, len);
#else
nread = (ssize_t)
libssh2_sftp_read(conn->data->reqdata.proto.ssh->sftp_handle, mem, len);
#endif
return nread;
}
1871
1872
1873
1874
1875
1876
1877
1878
1879
1880
1881
1882
1883
1884
1885
1886
1887
1888
1889
1890
1891
1892
1893
/* The get_pathname() function is being borrowed from OpenSSH sftp.c
version 4.6p1. */
/*
* Copyright (c) 2001-2004 Damien Miller <djm@openbsd.org>
*
* Permission to use, copy, modify, and distribute this software for any
* purpose with or without fee is hereby granted, provided that the above
* copyright notice and this permission notice appear in all copies.
*
* THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
* WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
* MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
* ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
* WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
* ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
* OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
*/
static int
get_pathname(const char **cpp, char **path)
{
const char *cp = *cpp, *end;
char quot;
u_int i, j;
static const char * const WHITESPACE = " \t\r\n";
cp += strspn(cp, WHITESPACE);
if (!*cp) {
*cpp = cp;
*path = NULL;
1901
1902
1903
1904
1905
1906
1907
1908
1909
1910
1911
1912
1913
1914
1915
1916
1917
1918
1919
1920
1921
1922
1923
1924
1925
1926
1927
1928
1929
1930
1931
1932
1933
1934
1935
1936
1937
1938
1939
1940
1941
1942
1943
1944
1945
1946
1947
1948
1949
1950
1951
1952
1953
1954
1955
1956
1957
1958
1959
1960
1961
1962
1963
1964
1965
1966
1967
1968
1969
1970
1971
1972
1973
1974
1975
1976
1977
1978
1979
1980
1981
1982
1983
1984
1985
1986
1987
1988
1989
1990
1991
1992
1993
1994
1995
1996
1997
1998
1999
2000
}
*path = malloc(strlen(cp) + 1);
if (*path == NULL)
return CURLE_OUT_OF_MEMORY;
/* Check for quoted filenames */
if (*cp == '\"' || *cp == '\'') {
quot = *cp++;
/* Search for terminating quote, unescape some chars */
for (i = j = 0; i <= strlen(cp); i++) {
if (cp[i] == quot) { /* Found quote */
i++;
(*path)[j] = '\0';
break;
}
if (cp[i] == '\0') { /* End of string */
/*error("Unterminated quote");*/
goto fail;
}
if (cp[i] == '\\') { /* Escaped characters */
i++;
if (cp[i] != '\'' && cp[i] != '\"' &&
cp[i] != '\\') {
/*error("Bad escaped character '\\%c'",
cp[i]);*/
goto fail;
}
}
(*path)[j++] = cp[i];
}
if (j == 0) {
/*error("Empty quotes");*/
goto fail;
}
*cpp = cp + i + strspn(cp + i, WHITESPACE);
}
else {
/* Read to end of filename */
end = strpbrk(cp, WHITESPACE);
if (end == NULL)
end = strchr(cp, '\0');
*cpp = end + strspn(end, WHITESPACE);
memcpy(*path, cp, end - cp);
(*path)[end - cp] = '\0';
}
return (0);
fail:
free(*path);
*path = NULL;
return CURLE_FTP_QUOTE_ERROR;
}
static const char *sftp_libssh2_strerror(unsigned long err)
{
switch (err) {
case LIBSSH2_FX_NO_SUCH_FILE:
return "No such file or directory";
case LIBSSH2_FX_PERMISSION_DENIED:
return "Permission denied";
case LIBSSH2_FX_FAILURE:
return "Operation failed";
case LIBSSH2_FX_BAD_MESSAGE:
return "Bad message from SFTP server";
case LIBSSH2_FX_NO_CONNECTION:
return "Not connected to SFTP server";
case LIBSSH2_FX_CONNECTION_LOST:
return "Connection to SFTP server lost";
case LIBSSH2_FX_OP_UNSUPPORTED:
return "Operation not supported by SFTP server";
case LIBSSH2_FX_INVALID_HANDLE:
return "Invalid handle";
case LIBSSH2_FX_NO_SUCH_PATH:
return "No such file or directory";
case LIBSSH2_FX_FILE_ALREADY_EXISTS:
return "File already exists";
case LIBSSH2_FX_WRITE_PROTECT:
return "File is write protected";
case LIBSSH2_FX_NO_MEDIA:
return "No media";
case LIBSSH2_FX_NO_SPACE_ON_FILESYSTEM:
return "Disk full";
case LIBSSH2_FX_QUOTA_EXCEEDED:
return "User quota exceeded";
case LIBSSH2_FX_UNKNOWN_PRINCIPLE:
return "Unknown principle";
case LIBSSH2_FX_LOCK_CONFlICT:
return "File lock conflict";
case LIBSSH2_FX_DIR_NOT_EMPTY:
return "Directory not empty";
case LIBSSH2_FX_NOT_A_DIRECTORY:
return "Not a directory";
case LIBSSH2_FX_INVALID_FILENAME:
return "Invalid filename";
case LIBSSH2_FX_LINK_LOOP: