Newer
Older
/*
* 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
*/
if (libssh2_sftp_realpath(ssh->sftp_session, ".", tempHome, PATH_MAX-1)
> 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 requries 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,
LIBSSH2_SFTP_S_IRUSR|
LIBSSH2_SFTP_S_IWUSR|
LIBSSH2_SFTP_S_IRGRP|
LIBSSH2_SFTP_S_IROTH,
conn->data->set.infilesize, 0, 0);
if (!scp->ssh_channel &&
(libssh2_session_last_errno(scp->ssh_session) !=
LIBSSH2_ERROR_EAGAIN)) {
return CURLE_FAILED_INIT;
}
} while (!scp->ssh_channel);
#else /* !(LIBSSH2_APINO >= 200706012030) */
scp->ssh_channel = libssh2_scp_send_ex(scp->ssh_session, scp->path,
LIBSSH2_SFTP_S_IRUSR|
LIBSSH2_SFTP_S_IWUSR|
LIBSSH2_SFTP_S_IRGRP|
LIBSSH2_SFTP_S_IROTH,
conn->data->set.infilesize, 0, 0);
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);
(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,
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);
#else /* !(LIBSSH2_APINO >= 200706012030) */
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) */
/* 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) {
if ((attrs.flags & LIBSSH2_SFTP_ATTR_PERMISSIONS) &&
((attrs.permissions & LIBSSH2_SFTP_S_IFMT) ==
LIBSSH2_SFTP_S_IFDIR)) {
infof(data, "%s\n", filename);
}
else {
totalLen = 80 + len;
line = (char *)malloc(totalLen);
if (!line)
return CURLE_OUT_OF_MEMORY;
1442
1443
1444
1445
1446
1447
1448
1449
1450
1451
1452
1453
1454
1455
1456
1457
1458
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
1484
1485
1486
1487
1488
1489
1490
1491
1492
1493
1494
1495
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);
1501
1502
1503
1504
1505
1506
1507
1508
1509
1510
1511
1512
1513
1514
1515
1516
1517
1518
1519
1520
1521
1522
1523
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);
len = libssh2_sftp_readlink(sftp->sftp_session, linkPath,
filename, PATH_MAX);
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,
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 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,
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 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) {
1601
1602
1603
1604
1605
1606
1607
1608
1609
1610
1611
1612
1613
1614
1615
1616
1617
1618
1619
1620
1621
1622
1623
1624
1625
/*
* 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) */
1651
1652
1653
1654
1655
1656
1657
1658
1659
1660
1661
1662
1663
1664
1665
1666
1667
1668
1669
1670
1671
1672
1673
1674
1675
1676
1677
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_sftp_shutdown(sftp->sftp_session) < 0) {
infof(conn->data, "Failed to stop libssh2 sftp subsystem\n");
}
}
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);
Daniel Stenberg
committed
#endif
(void)sockindex;
return nwrite;
}
1772
1773
1774
1775
1776
1777
1778
1779
1780
1781
1782
1783
1784
1785
1786
1787
1788
1789
1790
1791
1792
1793
1794
/* 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;
1802
1803
1804
1805
1806
1807
1808
1809
1810
1811
1812
1813
1814
1815
1816
1817
1818
1819
1820
1821
1822
1823
1824
1825
1826
1827
1828
1829
1830
1831
1832
1833
1834
1835
1836
1837
1838
1839
1840
1841
1842
1843
1844
1845
1846
1847
1848
1849
1850
1851
1852
1853
1854
1855
1856
1857
1858
1859
1860
1861
1862
1863
1864
1865
1866
1867
1868
1869
1870
1871
1872
1873
1874
1875
1876
1877
1878
1879
1880
1881
1882
1883
1884
1885
1886
1887
1888
1889
1890
1891
1892
1893
1894
1895
1896
1897
1898
1899
1900
1901
1902
1903
1904
1905
1906
1907
1908
1909
1910
1911
1912
1913
1914
1915
}
*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:
return "Link points to itself";
}
return "Unknown error in libssh2";
}
/* BLOCKING */
static CURLcode sftp_sendquote(struct connectdata *conn,
struct curl_slist *quote)
{
struct curl_slist *item=quote;
const char *cp;
long err;
struct SessionHandle *data = conn->data;
LIBSSH2_SFTP *sftp_session = data->reqdata.proto.ssh->sftp_session;
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
while (item) {
if (item->data) {
char *path1 = NULL;
char *path2 = NULL;
/* the arguments following the command must be separated from the
command with a space so we can check for it unconditionally */
cp = strchr(item->data, ' ');
if (cp == NULL) {
failf(data, "Syntax error in SFTP command. Supply parameter(s)!");
return CURLE_FTP_QUOTE_ERROR;
}
/* also, every command takes at least one argument so we get that first
argument right now */
err = get_pathname(&cp, &path1);
if (err) {
if (err == CURLE_OUT_OF_MEMORY)
failf(data, "Out of memory");
else
failf(data, "Syntax error: Bad first parameter");
return err;
}
/* 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(item->data, "chgrp ", 6) ||
curl_strnequal(item->data, "chmod ", 6) ||
curl_strnequal(item->data, "chown ", 6) ) { /* attribute change */
LIBSSH2_SFTP_ATTRIBUTES attrs;
/* path1 contains the mode to set */
err = get_pathname(&cp, &path2); /* get the destination */
if (err) {
if (err == CURLE_OUT_OF_MEMORY)
failf(data, "Out of memory");
else
failf(data,
"Syntax error in chgrp/chmod/chown: Bad second parameter");
free(path1);
return err;
}
memset(&attrs, 0, sizeof(LIBSSH2_SFTP_ATTRIBUTES));
if (libssh2_sftp_stat(sftp_session,
path2, &attrs) != 0) { /* get those attributes */
err = libssh2_sftp_last_error(sftp_session);
free(path1);
free(path2);
failf(data, "Attempt to get SFTP stats failed: %s",
sftp_libssh2_strerror(err));
return CURLE_FTP_QUOTE_ERROR;
}
1971
1972
1973
1974
1975
1976
1977
1978
1979
1980
1981
1982
1983
1984
1985
1986
1987
1988
1989
1990
1991
1992
1993
#if (LIBSSH2_APINO >= 200706012030)
while ((ret = libssh2_sftp_stat(sftp_session,
path2, &attrs)) ==
LIBSSH2_ERROR_EAGAIN);
if (ret != 0) { /* get those attributes */
err = libssh2_sftp_last_error(sftp_session);
free(path1);
free(path2);
failf(data, "Attempt to get SFTP stats failed: %s",
sftp_libssh2_strerror(err));
return CURLE_FTP_QUOTE_ERROR;
}
#else /* !(LIBSSH2_APINO >= 200706012030) */
if (libssh2_sftp_stat(sftp_session,
path2, &attrs) != 0) { /* get those attributes */
err = libssh2_sftp_last_error(sftp_session);
free(path1);
free(path2);
failf(data, "Attempt to get SFTP stats failed: %s",
sftp_libssh2_strerror(err));
return CURLE_FTP_QUOTE_ERROR;
}
#endif /* !(LIBSSH2_APINO >= 200706012030) */
/* Now set the new attributes... */
if (curl_strnequal(item->data, "chgrp", 5)) {
attrs.gid = strtol(path1, NULL, 10);
if (attrs.gid == 0 && !ISDIGIT(path1[0])) {
free(path1);
free(path2);