Newer
Older
Daniel Stenberg
committed
break;
James Housley
committed
Daniel Stenberg
committed
case SSH_SFTP_CLOSE:
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;
}
Daniel Stenberg
committed
Curl_safefree(sftp_scp->path);
sftp_scp->path = NULL;
James Housley
committed
Daniel Stenberg
committed
DEBUGF(infof(data, "SFTP DONE done\n"));
#if 0 /* PREV */
Daniel Stenberg
committed
state(conn, SSH_SFTP_SHUTDOWN);
#endif
Daniel Stenberg
committed
state(conn, SSH_STOP);
result = sshc->actualcode;
break;
James Housley
committed
Daniel Stenberg
committed
case SSH_SFTP_SHUTDOWN:
/* 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 */
Daniel Stenberg
committed
Daniel Stenberg
committed
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;
James Housley
committed
}
Daniel Stenberg
committed
if(sshc->sftp_session) {
rc = libssh2_sftp_shutdown(sshc->sftp_session);
if(rc == LIBSSH2_ERROR_EAGAIN) {
break;
}
else if(rc < 0) {
infof(data, "Failed to stop libssh2 sftp subsystem\n");
}
sshc->sftp_session = NULL;
}
James Housley
committed
Daniel Stenberg
committed
Curl_safefree(sshc->homedir);
sshc->homedir = NULL;
James Housley
committed
Daniel Stenberg
committed
state(conn, SSH_SESSION_DISCONNECT);
James Housley
committed
break;
Daniel Stenberg
committed
case SSH_SCP_TRANS_INIT:
result = ssh_getworkingpath(conn, sshc->homedir, &sftp_scp->path);
if(result) {
sshc->actualcode = result;
state(conn, SSH_STOP);
break;
James Housley
committed
}
Daniel Stenberg
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;
}
state(conn, SSH_SCP_UPLOAD_INIT);
James Housley
committed
}
else {
Daniel Stenberg
committed
state(conn, SSH_SCP_DOWNLOAD_INIT);
}
Daniel Stenberg
committed
break;
James Housley
committed
Daniel Stenberg
committed
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,
Daniel Stenberg
committed
(size_t)data->set.infilesize, 0, 0);
if(!sshc->ssh_channel) {
if(libssh2_session_last_errno(sshc->ssh_session) ==
LIBSSH2_ERROR_EAGAIN) {
rc = LIBSSH2_ERROR_EAGAIN;
break;
}
else {
int ssh_err;
char *err_msg;
ssh_err = (int)(libssh2_session_last_error(sshc->ssh_session,
&err_msg, NULL, 0));
Daniel Stenberg
committed
failf(conn->data, "%s", err_msg);
state(conn, SSH_SCP_CHANNEL_FREE);
sshc->actualcode = libssh2_session_error_to_CURLE(ssh_err);
break;
}
}
James Housley
committed
Daniel Stenberg
committed
/* upload data */
result = Curl_setup_transfer(conn, -1, data->req.size, FALSE, NULL,
FIRSTSOCKET, NULL);
Kamil Dudka
committed
Daniel Stenberg
committed
/* not set by Curl_setup_transfer to preserve keepon bits */
conn->sockfd = conn->writesockfd;
Daniel Stenberg
committed
if(result) {
state(conn, SSH_SCP_CHANNEL_FREE);
sshc->actualcode = result;
James Housley
committed
}
else {
Daniel Stenberg
committed
state(conn, SSH_STOP);
James Housley
committed
}
Daniel Stenberg
committed
break;
James Housley
committed
Daniel Stenberg
committed
2125
2126
2127
2128
2129
2130
2131
2132
2133
2134
2135
2136
2137
2138
2139
2140
2141
2142
2143
2144
2145
2146
2147
2148
2149
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;
/* clear the struct scp recv will fill in */
memset(&sb, 0, sizeof(struct stat));
/* get a fresh new channel from the ssh layer */
sshc->ssh_channel = libssh2_scp_recv(sshc->ssh_session,
sftp_scp->path, &sb);
if(!sshc->ssh_channel) {
if(libssh2_session_last_errno(sshc->ssh_session) ==
LIBSSH2_ERROR_EAGAIN) {
rc = LIBSSH2_ERROR_EAGAIN;
break;
}
else {
int ssh_err;
char *err_msg;
ssh_err = (int)(libssh2_session_last_error(sshc->ssh_session,
&err_msg, NULL, 0));
Daniel Stenberg
committed
failf(conn->data, "%s", err_msg);
state(conn, SSH_SCP_CHANNEL_FREE);
sshc->actualcode = libssh2_session_error_to_CURLE(ssh_err);
break;
}
}
Kamil Dudka
committed
Daniel Stenberg
committed
/* download data */
bytecount = (curl_off_t)sb.st_size;
data->req.maxdownload = (curl_off_t)sb.st_size;
result = Curl_setup_transfer(conn, FIRSTSOCKET,
bytecount, FALSE, NULL, -1, NULL);
Kamil Dudka
committed
Daniel Stenberg
committed
/* not set by Curl_setup_transfer to preserve keepon bits */
conn->writesockfd = conn->sockfd;
James Housley
committed
/* FIXME: here should be explained why we need it to start the
* download */
Daniel Stenberg
committed
conn->cselect_bits = CURL_CSELECT_IN;
James Housley
committed
Daniel Stenberg
committed
if(result) {
state(conn, SSH_SCP_CHANNEL_FREE);
sshc->actualcode = result;
James Housley
committed
}
Daniel Stenberg
committed
else
state(conn, SSH_STOP);
}
break;
James Housley
committed
Daniel Stenberg
committed
case SSH_SCP_DONE:
if(data->set.upload)
state(conn, SSH_SCP_SEND_EOF);
else
state(conn, SSH_SCP_CHANNEL_FREE);
break;
James Housley
committed
Daniel Stenberg
committed
case SSH_SCP_SEND_EOF:
if(sshc->ssh_channel) {
rc = libssh2_channel_send_eof(sshc->ssh_channel);
if(rc == LIBSSH2_ERROR_EAGAIN) {
break;
}
else if(rc) {
infof(data, "Failed to send libssh2 channel EOF\n");
}
James Housley
committed
}
Daniel Stenberg
committed
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) {
break;
}
else if(rc) {
infof(data, "Failed to get channel EOF: %d\n", rc);
}
James Housley
committed
}
Daniel Stenberg
committed
state(conn, SSH_SCP_WAIT_CLOSE);
break;
James Housley
committed
Daniel Stenberg
committed
case SSH_SCP_WAIT_CLOSE:
if(sshc->ssh_channel) {
rc = libssh2_channel_wait_closed(sshc->ssh_channel);
if(rc == LIBSSH2_ERROR_EAGAIN) {
break;
}
else if(rc) {
infof(data, "Channel failed to close: %d\n", rc);
}
James Housley
committed
}
Daniel Stenberg
committed
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) {
break;
}
else if(rc < 0) {
infof(data, "Failed to free libssh2 scp subsystem\n");
}
sshc->ssh_channel = NULL;
James Housley
committed
}
Daniel Stenberg
committed
DEBUGF(infof(data, "SCP DONE phase complete\n"));
#if 0 /* PREV */
Daniel Stenberg
committed
state(conn, SSH_SESSION_DISCONNECT);
#endif
Daniel Stenberg
committed
state(conn, SSH_STOP);
result = sshc->actualcode;
break;
James Housley
committed
Daniel Stenberg
committed
case SSH_SESSION_DISCONNECT:
/* during weird times when we've been prematurely aborted, the channel
is still alive when we reach this state and we MUST kill the channel
properly first */
if(sshc->ssh_channel) {
rc = libssh2_channel_free(sshc->ssh_channel);
if(rc == LIBSSH2_ERROR_EAGAIN) {
break;
}
else if(rc < 0) {
infof(data, "Failed to free libssh2 scp subsystem\n");
}
sshc->ssh_channel = NULL;
Daniel Stenberg
committed
}
Daniel Stenberg
committed
if(sshc->ssh_session) {
rc = libssh2_session_disconnect(sshc->ssh_session, "Shutdown");
if(rc == LIBSSH2_ERROR_EAGAIN) {
break;
}
else if(rc < 0) {
infof(data, "Failed to disconnect libssh2 session\n");
}
}
James Housley
committed
Daniel Stenberg
committed
Curl_safefree(sshc->homedir);
sshc->homedir = NULL;
James Housley
committed
Daniel Stenberg
committed
state(conn, SSH_SESSION_FREE);
break;
James Housley
committed
Daniel Stenberg
committed
case SSH_SESSION_FREE:
#ifdef HAVE_LIBSSH2_KNOWNHOST_API
Daniel Stenberg
committed
if(sshc->kh) {
libssh2_knownhost_free(sshc->kh);
sshc->kh = NULL;
}
#endif
Daniel Stenberg
committed
if(sshc->ssh_session) {
rc = libssh2_session_free(sshc->ssh_session);
if(rc == LIBSSH2_ERROR_EAGAIN) {
break;
}
else if(rc < 0) {
infof(data, "Failed to free libssh2 session\n");
}
sshc->ssh_session = NULL;
}
Daniel Stenberg
committed
conn->bits.close = TRUE;
sshc->nextstate = SSH_NO_STATE;
state(conn, SSH_STOP);
result = sshc->actualcode;
break;
case SSH_QUIT:
/* fallthrough, just stop! */
default:
/* internal error */
sshc->nextstate = SSH_NO_STATE;
state(conn, SSH_STOP);
break;
}
Daniel Stenberg
committed
} while(!rc && (sshc->state != SSH_STOP));
if(rc == LIBSSH2_ERROR_EAGAIN) {
/* we would block, we need to wait for the socket to be ready (in the
right direction too)! */
*block = TRUE;
}
return result;
}
Daniel Stenberg
committed
/* called by the multi interface to figure out what socket(s) to wait for and
for what actions in the DO_DONE, PERFORM and WAITPERFORM states */
static int ssh_perform_getsock(const struct connectdata *conn,
curl_socket_t *sock, /* points to numsocks
number of sockets */
int numsocks)
{
Guenter Knauf
committed
#ifdef HAVE_LIBSSH2_SESSION_BLOCK_DIRECTION
Daniel Stenberg
committed
int bitmap = GETSOCK_BLANK;
(void)numsocks;
sock[0] = conn->sock[FIRSTSOCKET];
Kamil Dudka
committed
if(conn->waitfor & KEEP_RECV)
Daniel Stenberg
committed
bitmap |= GETSOCK_READSOCK(FIRSTSOCKET);
Kamil Dudka
committed
if(conn->waitfor & KEEP_SEND)
Daniel Stenberg
committed
bitmap |= GETSOCK_WRITESOCK(FIRSTSOCKET);
return bitmap;
#else
/* if we don't know the direction we can use the generic *_getsock()
function even for the protocol_connect and doing states */
return Curl_single_getsock(conn, sock, numsocks);
#endif
}
/* Generic function called by the multi interface to figure out what socket(s)
to wait for and for what actions during the DOING and PROTOCONNECT states*/
static int ssh_getsock(struct connectdata *conn,
curl_socket_t *sock, /* points to numsocks number
of sockets */
int numsocks)
{
Guenter Knauf
committed
#ifndef HAVE_LIBSSH2_SESSION_BLOCK_DIRECTION
(void)conn;
(void)sock;
(void)numsocks;
Daniel Stenberg
committed
/* if we don't know any direction we can just play along as we used to and
not provide any sensible info */
return GETSOCK_BLANK;
#else
/* if we know the direction we can use the generic *_getsock() function even
for the protocol_connect and doing states */
return ssh_perform_getsock(conn, sock, numsocks);
#endif
}
Guenter Knauf
committed
#ifdef HAVE_LIBSSH2_SESSION_BLOCK_DIRECTION
Daniel Stenberg
committed
/*
* When one of the libssh2 functions has returned LIBSSH2_ERROR_EAGAIN this
* function is used to figure out in what direction and stores this info so
* that the multi interface can take advantage of it. Make sure to call this
* function in all cases so that when it _doesn't_ return EAGAIN we can
* restore the default wait bits.
*/
static void ssh_block2waitfor(struct connectdata *conn, bool block)
{
struct ssh_conn *sshc = &conn->proto.sshc;
int dir;
Kamil Dudka
committed
if(!block)
conn->waitfor = 0;
else if((dir = libssh2_session_block_directions(sshc->ssh_session))) {
Daniel Stenberg
committed
/* translate the libssh2 define bits into our own bit defines */
Kamil Dudka
committed
conn->waitfor = ((dir&LIBSSH2_SESSION_BLOCK_INBOUND)?KEEP_RECV:0) |
((dir&LIBSSH2_SESSION_BLOCK_OUTBOUND)?KEEP_SEND:0);
Daniel Stenberg
committed
}
else
/* It didn't block or libssh2 didn't reveal in which direction, put back
the original set */
Kamil Dudka
committed
conn->waitfor = sshc->orig_waitfor;
Daniel Stenberg
committed
}
#else
/* no libssh2 directional support so we simply don't know */
#define ssh_block2waitfor(x,y)
#endif
/* called repeatedly until done from multi.c */
static CURLcode ssh_multi_statemach(struct connectdata *conn, bool *done)
{
struct ssh_conn *sshc = &conn->proto.sshc;
CURLcode result = CURLE_OK;
Daniel Stenberg
committed
bool block; /* we store the status and use that to provide a ssh_getsock()
implementation */
Daniel Stenberg
committed
result = ssh_statemach_act(conn, &block);
James Housley
committed
*done = (bool)(sshc->state == SSH_STOP);
Daniel Stenberg
committed
ssh_block2waitfor(conn, block);
return result;
}
static CURLcode ssh_easy_statemach(struct connectdata *conn)
{
struct ssh_conn *sshc = &conn->proto.sshc;
CURLcode result = CURLE_OK;
while((sshc->state != SSH_STOP) && !result) {
bool block;
result = ssh_statemach_act(conn, &block);
Guenter Knauf
committed
#ifdef HAVE_LIBSSH2_SESSION_BLOCK_DIRECTION
if((CURLE_OK == result) && block) {
int dir = libssh2_session_block_directions(sshc->ssh_session);
curl_socket_t sock = conn->sock[FIRSTSOCKET];
curl_socket_t fd_read = CURL_SOCKET_BAD;
curl_socket_t fd_write = CURL_SOCKET_BAD;
if (LIBSSH2_SESSION_BLOCK_INBOUND & dir) {
fd_read = sock;
}
if (LIBSSH2_SESSION_BLOCK_OUTBOUND & dir) {
fd_write = sock;
}
/* wait for the socket to become ready */
Curl_socket_ready(fd_read, fd_write, 1000); /* ignore result */
}
#endif
}
James Housley
committed
return result;
}
/*
* SSH setup and connection
*/
static CURLcode ssh_init(struct connectdata *conn)
{
struct SessionHandle *data = conn->data;
struct SSHPROTO *ssh;
struct ssh_conn *sshc = &conn->proto.sshc;
Daniel Stenberg
committed
sshc->actualcode = CURLE_OK; /* reset error code */
sshc->secondCreateDirs =0; /* reset the create dir attempt state
variable */
Daniel Stenberg
committed
Daniel Stenberg
committed
if(data->state.proto.ssh)
return CURLE_OK;
ssh = calloc(1, sizeof(struct SSHPROTO));
if(!ssh)
return CURLE_OUT_OF_MEMORY;
Daniel Stenberg
committed
data->state.proto.ssh = ssh;
return CURLE_OK;
}
/*
* Curl_ssh_connect() gets called from Curl_protocol_connect() to allow us to
* do protocol-specific actions at connect-time.
*/
static CURLcode ssh_connect(struct connectdata *conn, bool *done)
CURLcode result;
struct SessionHandle *data = conn->data;
/* 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;
/* 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(conn->user) {
infof(data, "User: %s\n", conn->user);
if(conn->passwd) {
infof(data, "Password: %s\n", conn->passwd);
}
sock = conn->sock[FIRSTSOCKET];
ssh->ssh_session = libssh2_session_init_ex(libssh2_malloc, libssh2_free,
libssh2_realloc, conn);
if(ssh->ssh_session == NULL) {
failf(data, "Failure initialising ssh session");
return CURLE_FAILED_INIT;
}
Daniel Stenberg
committed
#ifdef HAVE_LIBSSH2_KNOWNHOST_API
if(data->set.str[STRING_SSH_KNOWNHOSTS]) {
int rc;
ssh->kh = libssh2_knownhost_init(ssh->ssh_session);
if(!ssh->kh) {
/* eeek. TODO: free the ssh_session! */
return CURLE_FAILED_INIT;
}
/* read all known hosts from there */
rc = libssh2_knownhost_readfile(ssh->kh,
data->set.str[STRING_SSH_KNOWNHOSTS],
LIBSSH2_KNOWNHOST_FILE_OPENSSH);
if(rc) {
infof(data, "Failed to read known hosts from %s\n",
data->set.str[STRING_SSH_KNOWNHOSTS]);
}
}
#endif /* HAVE_LIBSSH2_KNOWNHOST_API */
#ifdef CURL_LIBSSH2_DEBUG
Daniel Stenberg
committed
libssh2_trace(ssh->ssh_session, ~0);
#endif /* CURL_LIBSSH2_DEBUG */
state(conn, SSH_S_STARTUP);
if(data->state.used_interface == Curl_if_multi)
result = ssh_multi_statemach(conn, done);
else {
result = ssh_easy_statemach(conn);
if(!result)
*done = TRUE;
}
return result;
James Housley
committed
2551
2552
2553
2554
2555
2556
2557
2558
2559
2560
2561
2562
2563
2564
2565
2566
2567
2568
2569
2570
2571
2572
2573
2574
/*
***********************************************************************
*
* 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) {
result = ssh_multi_statemach(conn, dophase_done);
}
else {
James Housley
committed
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 */
static CURLcode scp_doing(struct connectdata *conn,
Patrick Monnerat
committed
bool *dophase_done)
James Housley
committed
{
CURLcode result;
result = ssh_multi_statemach(conn, dophase_done);
James Housley
committed
if(*dophase_done) {
James Housley
committed
DEBUGF(infof(conn->data, "DO phase is complete\n"));
}
return result;
}
/*
* The DO function is generic for both protocols. There was previously two
* separate ones but this way means less duplicated code.
*/
James Housley
committed
static CURLcode ssh_do(struct connectdata *conn, bool *done)
James Housley
committed
CURLcode res;
bool connected = 0;
struct SessionHandle *data = conn->data;
*done = FALSE; /* default to false */
Daniel Stenberg
committed
/*
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
ssh_connect() function.
*/
Curl_reset_reqproto(conn);
res = ssh_init(conn);
if(res)
return res;
Daniel Stenberg
committed
data->req.size = -1; /* make sure this is unknown at this point */
James Housley
committed
Curl_pgrsSetUploadCounter(data, 0);
Curl_pgrsSetDownloadCounter(data, 0);
Curl_pgrsSetUploadSize(data, 0);
Curl_pgrsSetDownloadSize(data, 0);
if(conn->protocol & PROT_SCP)
res = scp_perform(conn, &connected, done);
else
res = sftp_perform(conn, &connected, done);
James Housley
committed
return res;
/* BLOCKING, but the function is using the state machine so the only reason
this is still blocking is that the multi interface code has no support for
disconnecting operations that takes a while */
static CURLcode scp_disconnect(struct connectdata *conn)
{
CURLcode result = CURLE_OK;
struct ssh_conn *ssh = &conn->proto.sshc;
Daniel Stenberg
committed
Curl_safefree(conn->data->state.proto.ssh);
conn->data->state.proto.ssh = NULL;
if(ssh->ssh_session) {
Daniel Stenberg
committed
/* only if there's a session still around to use! */
state(conn, SSH_SESSION_DISCONNECT);
Daniel Stenberg
committed
result = ssh_easy_statemach(conn);
}
return result;
}
Daniel Stenberg
committed
/* generic done function for both SCP and SFTP called from their specific
done functions */
static CURLcode ssh_done(struct connectdata *conn, CURLcode status)
James Housley
committed
CURLcode result = CURLE_OK;
struct SSHPROTO *sftp_scp = conn->data->state.proto.ssh;
James Housley
committed
if(status == CURLE_OK) {
/* run the state-machine
TODO: when the multi interface is used, this _really_ should be using
the ssh_multi_statemach function but we have no general support for
non-blocking DONE operations, not in the multi state machine and with
Curl_done() invokes on several places in the code!
*/
result = ssh_easy_statemach(conn);
}
else
James Housley
committed
result = status;
Daniel Stenberg
committed
Curl_safefree(sftp_scp->path);
sftp_scp->path = NULL;
Curl_pgrsDone(conn);
James Housley
committed
Daniel Stenberg
committed
conn->data->req.keepon = 0; /* clear all bits */
James Housley
committed
return result;
Daniel Stenberg
committed
}
static CURLcode scp_done(struct connectdata *conn, CURLcode status,
bool premature)
{
(void)premature; /* not used */
if(status == CURLE_OK)
state(conn, SSH_SCP_DONE);
return ssh_done(conn, status);
}
/* return number of received (decrypted) bytes */
ssize_t Curl_scp_send(struct connectdata *conn, int sockindex,
const void *mem, size_t len)
{
ssize_t nwrite;
(void)sockindex; /* we only support SCP on the fixed known primary socket */
/* libssh2_channel_write() returns int! */
nwrite = (ssize_t)
libssh2_channel_write(conn->proto.sshc.ssh_channel, mem, len);
Daniel Stenberg
committed
ssh_block2waitfor(conn, (nwrite == LIBSSH2_ERROR_EAGAIN)?TRUE:FALSE);
if(nwrite == LIBSSH2_ERROR_EAGAIN)
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 */
nread = (ssize_t)
libssh2_channel_read(conn->proto.sshc.ssh_channel, mem, len);
Daniel Stenberg
committed
ssh_block2waitfor(conn, (nread == LIBSSH2_ERROR_EAGAIN)?TRUE:FALSE);
return nread;
}
/*
* =============== SFTP ===============
*/
James Housley
committed
2749
2750
2751
2752
2753
2754
2755
2756
2757
2758
2759
2760
2761
2762
2763
2764
2765
2766
2767
2768
2769
2770
2771
2772
/*
***********************************************************************
*
* sftp_perform()
*
* This is the actual DO function for SFTP. Get a file/directory according to
* the options previously setup.
*/
static
CURLcode sftp_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_SFTP_QUOTE_INIT);
/* run the state-machine */
if(conn->data->state.used_interface == Curl_if_multi) {
result = ssh_multi_statemach(conn, dophase_done);
}
else {
James Housley
committed
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 */
static CURLcode sftp_doing(struct connectdata *conn,
bool *dophase_done)
James Housley
committed
{
CURLcode result;
result = ssh_multi_statemach(conn, dophase_done);
James Housley
committed
if(*dophase_done) {
James Housley
committed
DEBUGF(infof(conn->data, "DO phase is complete\n"));
}
return result;
}
/* BLOCKING, but the function is using the state machine so the only reason
this is still blocking is that the multi interface code has no support for
disconnecting operations that takes a while */
static CURLcode sftp_disconnect(struct connectdata *conn)
CURLcode result = CURLE_OK;
James Housley
committed
DEBUGF(infof(conn->data, "SSH DISCONNECT starts now\n"));
James Housley
committed
Daniel Stenberg
committed
Curl_safefree(conn->data->state.proto.ssh);
conn->data->state.proto.ssh = NULL;
Daniel Stenberg
committed
if(conn->proto.sshc.ssh_session) {
/* only if there's a session still around to use! */
state(conn, SSH_SFTP_SHUTDOWN);
result = ssh_easy_statemach(conn);
}
James Housley
committed
DEBUGF(infof(conn->data, "SSH DISCONNECT is done\n"));
James Housley
committed
return result;
James Housley
committed
static CURLcode sftp_done(struct connectdata *conn, CURLcode status,
Patrick Monnerat
committed
bool premature)
James Housley
committed
struct ssh_conn *sshc = &conn->proto.sshc;
if(status == CURLE_OK) {
/* Before we shut down, see if there are any post-quote commands to
send: */
if(!status && !premature && conn->data->set.postquote) {
sshc->nextstate = SSH_SFTP_CLOSE;
James Housley
committed
state(conn, SSH_SFTP_POSTQUOTE_INIT);
}
else
state(conn, SSH_SFTP_CLOSE);
James Housley
committed
}
Daniel Stenberg
committed
return ssh_done(conn, status);
/* return number of sent bytes */
ssize_t Curl_sftp_send(struct connectdata *conn, int sockindex,
const 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. These days we don't
support libssh2 0.15*/
(void)sockindex;
nwrite = libssh2_sftp_write(conn->proto.sshc.sftp_handle, mem, len);
Daniel Stenberg
committed
ssh_block2waitfor(conn, (nwrite == LIBSSH2_ERROR_EAGAIN)?TRUE:FALSE);
if(nwrite == LIBSSH2_ERROR_EAGAIN)
return nwrite;
}
* Return number of received (decrypted) bytes
*/
ssize_t Curl_sftp_recv(struct connectdata *conn, int sockindex,
char *mem, size_t len)
{
ssize_t nread;
(void)sockindex;
nread = libssh2_sftp_read(conn->proto.sshc.sftp_handle, mem, len);
Daniel Stenberg
committed
ssh_block2waitfor(conn, (nread == LIBSSH2_ERROR_EAGAIN)?TRUE:FALSE);
return nread;
}
/* 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.
*/
get_pathname(const char **cpp, char **path)
{
const char *cp = *cpp, *end;
char quot;
static const char WHITESPACE[] = " \t\r\n";
cp += strspn(cp, WHITESPACE);
if(!*cp) {
*cpp = cp;
*path = NULL;
return CURLE_QUOTE_ERROR;
}
*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 */
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';
}
fail:
James Housley
committed
Curl_safefree(*path);
*path = NULL;
return CURLE_QUOTE_ERROR;
}
static const char *sftp_libssh2_strerror(unsigned long err)
{
switch (err) {
James Housley
committed
2970
2971
2972
2973
2974
2975
2976
2977
2978
2979
2980
2981
2982
2983
2984
2985
2986
2987
2988
2989
2990
2991
2992
2993
2994
2995
2996
2997
2998
2999
3000
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: