in libs/curl/lib/vssh/wolfssh.c [441:898]
static CURLcode wssh_statemach_act(struct Curl_easy *data, bool *block)
{
CURLcode result = CURLE_OK;
struct connectdata *conn = data->conn;
struct ssh_conn *sshc = &conn->proto.sshc;
struct SSHPROTO *sftp_scp = data->req.p.ssh;
WS_SFTPNAME *name;
int rc = 0;
*block = FALSE; /* we are not blocking by default */
do {
switch(sshc->state) {
case SSH_INIT:
state(data, SSH_S_STARTUP);
break;
case SSH_S_STARTUP:
rc = wolfSSH_connect(sshc->ssh_session);
if(rc != WS_SUCCESS)
rc = wolfSSH_get_error(sshc->ssh_session);
if(rc == WS_WANT_READ) {
*block = TRUE;
conn->waitfor = KEEP_RECV;
return CURLE_OK;
}
else if(rc == WS_WANT_WRITE) {
*block = TRUE;
conn->waitfor = KEEP_SEND;
return CURLE_OK;
}
else if(rc != WS_SUCCESS) {
state(data, SSH_STOP);
return CURLE_SSH;
}
infof(data, "wolfssh connected");
state(data, SSH_STOP);
break;
case SSH_STOP:
break;
case SSH_SFTP_INIT:
rc = wolfSSH_SFTP_connect(sshc->ssh_session);
if(rc != WS_SUCCESS)
rc = wolfSSH_get_error(sshc->ssh_session);
if(rc == WS_WANT_READ) {
*block = TRUE;
conn->waitfor = KEEP_RECV;
return CURLE_OK;
}
else if(rc == WS_WANT_WRITE) {
*block = TRUE;
conn->waitfor = KEEP_SEND;
return CURLE_OK;
}
else if(rc == WS_SUCCESS) {
infof(data, "wolfssh SFTP connected");
state(data, SSH_SFTP_REALPATH);
}
else {
failf(data, "wolfssh SFTP connect error %d", rc);
return CURLE_SSH;
}
break;
case SSH_SFTP_REALPATH:
name = wolfSSH_SFTP_RealPath(sshc->ssh_session, (char *)".");
rc = wolfSSH_get_error(sshc->ssh_session);
if(rc == WS_WANT_READ) {
*block = TRUE;
conn->waitfor = KEEP_RECV;
return CURLE_OK;
}
else if(rc == WS_WANT_WRITE) {
*block = TRUE;
conn->waitfor = KEEP_SEND;
return CURLE_OK;
}
else if(name && (rc == WS_SUCCESS)) {
sshc->homedir = Curl_memdup0(name->fName, name->fSz);
if(!sshc->homedir)
sshc->actualcode = CURLE_OUT_OF_MEMORY;
wolfSSH_SFTPNAME_list_free(name);
state(data, SSH_STOP);
return CURLE_OK;
}
failf(data, "wolfssh SFTP realpath %d", rc);
return CURLE_SSH;
case SSH_SFTP_QUOTE_INIT:
result = Curl_getworkingpath(data, sshc->homedir, &sftp_scp->path);
if(result) {
sshc->actualcode = result;
state(data, SSH_STOP);
break;
}
if(data->set.quote) {
infof(data, "Sending quote commands");
sshc->quote_item = data->set.quote;
state(data, SSH_SFTP_QUOTE);
}
else {
state(data, SSH_SFTP_GETINFO);
}
break;
case SSH_SFTP_GETINFO:
if(data->set.get_filetime) {
state(data, SSH_SFTP_FILETIME);
}
else {
state(data, SSH_SFTP_TRANS_INIT);
}
break;
case SSH_SFTP_TRANS_INIT:
if(data->state.upload)
state(data, SSH_SFTP_UPLOAD_INIT);
else {
if(sftp_scp->path[strlen(sftp_scp->path)-1] == '/')
state(data, SSH_SFTP_READDIR_INIT);
else
state(data, SSH_SFTP_DOWNLOAD_INIT);
}
break;
case SSH_SFTP_UPLOAD_INIT: {
word32 flags;
WS_SFTP_FILEATRB createattrs;
if(data->state.resume_from) {
WS_SFTP_FILEATRB attrs;
if(data->state.resume_from < 0) {
rc = wolfSSH_SFTP_STAT(sshc->ssh_session, sftp_scp->path,
&attrs);
if(rc != WS_SUCCESS)
break;
if(rc) {
data->state.resume_from = 0;
}
else {
curl_off_t size = ((curl_off_t)attrs.sz[1] << 32) | attrs.sz[0];
if(size < 0) {
failf(data, "Bad file size (%" CURL_FORMAT_CURL_OFF_T ")", size);
return CURLE_BAD_DOWNLOAD_RESUME;
}
data->state.resume_from = size;
}
}
}
if(data->set.remote_append)
/* Try to open for append, but create if nonexisting */
flags = WOLFSSH_FXF_WRITE|WOLFSSH_FXF_CREAT|WOLFSSH_FXF_APPEND;
else if(data->state.resume_from > 0)
/* If we have restart position then open for append */
flags = WOLFSSH_FXF_WRITE|WOLFSSH_FXF_APPEND;
else
/* Clear file before writing (normal behavior) */
flags = WOLFSSH_FXF_WRITE|WOLFSSH_FXF_CREAT|WOLFSSH_FXF_TRUNC;
memset(&createattrs, 0, sizeof(createattrs));
createattrs.per = (word32)data->set.new_file_perms;
sshc->handleSz = sizeof(sshc->handle);
rc = wolfSSH_SFTP_Open(sshc->ssh_session, sftp_scp->path,
flags, &createattrs,
sshc->handle, &sshc->handleSz);
if(rc == WS_FATAL_ERROR)
rc = wolfSSH_get_error(sshc->ssh_session);
if(rc == WS_WANT_READ) {
*block = TRUE;
conn->waitfor = KEEP_RECV;
return CURLE_OK;
}
else if(rc == WS_WANT_WRITE) {
*block = TRUE;
conn->waitfor = KEEP_SEND;
return CURLE_OK;
}
else if(rc == WS_SUCCESS) {
infof(data, "wolfssh SFTP open succeeded");
}
else {
failf(data, "wolfssh SFTP upload open failed: %d", rc);
return CURLE_SSH;
}
state(data, SSH_SFTP_DOWNLOAD_STAT);
/* If we have a 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. */
int seekerr = CURL_SEEKFUNC_OK;
if(data->set.seek_func) {
Curl_set_in_callback(data, true);
seekerr = data->set.seek_func(data->set.seek_client,
data->state.resume_from, SEEK_SET);
Curl_set_in_callback(data, false);
}
if(seekerr != CURL_SEEKFUNC_OK) {
curl_off_t passed = 0;
if(seekerr != CURL_SEEKFUNC_CANTSEEK) {
failf(data, "Could not seek stream");
return CURLE_FTP_COULDNT_USE_REST;
}
/* seekerr == CURL_SEEKFUNC_CANTSEEK (cannot seek to offset) */
do {
char scratch[4*1024];
size_t readthisamountnow =
(data->state.resume_from - passed >
(curl_off_t)sizeof(scratch)) ?
sizeof(scratch) : curlx_sotouz(data->state.resume_from - passed);
size_t actuallyread;
Curl_set_in_callback(data, true);
actuallyread = data->state.fread_func(scratch, 1,
readthisamountnow,
data->state.in);
Curl_set_in_callback(data, false);
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->state.infilesize > 0) {
data->state.infilesize -= data->state.resume_from;
data->req.size = data->state.infilesize;
Curl_pgrsSetUploadSize(data, data->state.infilesize);
}
sshc->offset += data->state.resume_from;
}
if(data->state.infilesize > 0) {
data->req.size = data->state.infilesize;
Curl_pgrsSetUploadSize(data, data->state.infilesize);
}
/* upload data */
Curl_xfer_setup1(data, CURL_XFER_SEND, -1, FALSE);
/* not set by Curl_xfer_setup to preserve keepon bits */
conn->sockfd = conn->writesockfd;
if(result) {
state(data, SSH_SFTP_CLOSE);
sshc->actualcode = result;
}
else {
/* store this original bitmask setup to use later on if we cannot
figure out a "real" bitmask */
sshc->orig_waitfor = data->req.keepon;
/* we want to use the _sending_ function even when the socket turns
out readable as the underlying libssh2 sftp send function will deal
with both accordingly */
data->state.select_bits = CURL_CSELECT_OUT;
/* since we do not really wait for anything at this point, we want the
state machine to move on as soon as possible so we set a very short
timeout here */
Curl_expire(data, 0, EXPIRE_RUN_NOW);
state(data, SSH_STOP);
}
break;
}
case SSH_SFTP_DOWNLOAD_INIT:
sshc->handleSz = sizeof(sshc->handle);
rc = wolfSSH_SFTP_Open(sshc->ssh_session, sftp_scp->path,
WOLFSSH_FXF_READ, NULL,
sshc->handle, &sshc->handleSz);
if(rc == WS_FATAL_ERROR)
rc = wolfSSH_get_error(sshc->ssh_session);
if(rc == WS_WANT_READ) {
*block = TRUE;
conn->waitfor = KEEP_RECV;
return CURLE_OK;
}
else if(rc == WS_WANT_WRITE) {
*block = TRUE;
conn->waitfor = KEEP_SEND;
return CURLE_OK;
}
else if(rc == WS_SUCCESS) {
infof(data, "wolfssh SFTP open succeeded");
state(data, SSH_SFTP_DOWNLOAD_STAT);
return CURLE_OK;
}
failf(data, "wolfssh SFTP open failed: %d", rc);
return CURLE_SSH;
case SSH_SFTP_DOWNLOAD_STAT: {
WS_SFTP_FILEATRB attrs;
curl_off_t size;
rc = wolfSSH_SFTP_STAT(sshc->ssh_session, sftp_scp->path, &attrs);
if(rc == WS_FATAL_ERROR)
rc = wolfSSH_get_error(sshc->ssh_session);
if(rc == WS_WANT_READ) {
*block = TRUE;
conn->waitfor = KEEP_RECV;
return CURLE_OK;
}
else if(rc == WS_WANT_WRITE) {
*block = TRUE;
conn->waitfor = KEEP_SEND;
return CURLE_OK;
}
else if(rc == WS_SUCCESS) {
infof(data, "wolfssh STAT succeeded");
}
else {
failf(data, "wolfssh SFTP open failed: %d", rc);
data->req.size = -1;
data->req.maxdownload = -1;
Curl_pgrsSetDownloadSize(data, -1);
return CURLE_SSH;
}
size = ((curl_off_t)attrs.sz[1] <<32) | attrs.sz[0];
data->req.size = size;
data->req.maxdownload = size;
Curl_pgrsSetDownloadSize(data, size);
infof(data, "SFTP download %" CURL_FORMAT_CURL_OFF_T " bytes", size);
/* We cannot seek with wolfSSH so resuming and range requests are not
possible */
if(data->state.use_range || data->state.resume_from) {
infof(data, "wolfSSH cannot do range/seek on SFTP");
return CURLE_BAD_DOWNLOAD_RESUME;
}
/* Setup the actual download */
if(data->req.size == 0) {
/* no data to transfer */
Curl_xfer_setup_nop(data);
infof(data, "File already completely downloaded");
state(data, SSH_STOP);
break;
}
Curl_xfer_setup1(data, CURL_XFER_RECV, data->req.size, FALSE);
/* not set by Curl_xfer_setup to preserve keepon bits */
conn->writesockfd = conn->sockfd;
/* we want to use the _receiving_ function even when the socket turns
out writableable as the underlying libssh2 recv function will deal
with both accordingly */
data->state.select_bits = CURL_CSELECT_IN;
if(result) {
/* this should never occur; the close state should be entered
at the time the error occurs */
state(data, SSH_SFTP_CLOSE);
sshc->actualcode = result;
}
else {
state(data, SSH_STOP);
}
break;
}
case SSH_SFTP_CLOSE:
if(sshc->handleSz)
rc = wolfSSH_SFTP_Close(sshc->ssh_session, sshc->handle,
sshc->handleSz);
else
rc = WS_SUCCESS; /* directory listing */
if(rc == WS_WANT_READ) {
*block = TRUE;
conn->waitfor = KEEP_RECV;
return CURLE_OK;
}
else if(rc == WS_WANT_WRITE) {
*block = TRUE;
conn->waitfor = KEEP_SEND;
return CURLE_OK;
}
else if(rc == WS_SUCCESS) {
state(data, SSH_STOP);
return CURLE_OK;
}
failf(data, "wolfssh SFTP CLOSE failed: %d", rc);
return CURLE_SSH;
case SSH_SFTP_READDIR_INIT:
Curl_pgrsSetDownloadSize(data, -1);
if(data->req.no_body) {
state(data, SSH_STOP);
break;
}
state(data, SSH_SFTP_READDIR);
break;
case SSH_SFTP_READDIR:
name = wolfSSH_SFTP_LS(sshc->ssh_session, sftp_scp->path);
if(!name)
rc = wolfSSH_get_error(sshc->ssh_session);
else
rc = WS_SUCCESS;
if(rc == WS_WANT_READ) {
*block = TRUE;
conn->waitfor = KEEP_RECV;
return CURLE_OK;
}
else if(rc == WS_WANT_WRITE) {
*block = TRUE;
conn->waitfor = KEEP_SEND;
return CURLE_OK;
}
else if(name && (rc == WS_SUCCESS)) {
WS_SFTPNAME *origname = name;
result = CURLE_OK;
while(name) {
char *line = aprintf("%s\n",
data->set.list_only ?
name->fName : name->lName);
if(!line) {
state(data, SSH_SFTP_CLOSE);
sshc->actualcode = CURLE_OUT_OF_MEMORY;
break;
}
result = Curl_client_write(data, CLIENTWRITE_BODY,
line, strlen(line));
free(line);
if(result) {
sshc->actualcode = result;
break;
}
name = name->next;
}
wolfSSH_SFTPNAME_list_free(origname);
state(data, SSH_STOP);
return result;
}
failf(data, "wolfssh SFTP ls failed: %d", rc);
return CURLE_SSH;
case SSH_SFTP_SHUTDOWN:
Curl_safefree(sshc->homedir);
wolfSSH_free(sshc->ssh_session);
wolfSSH_CTX_free(sshc->ctx);
state(data, SSH_STOP);
return CURLE_OK;
default:
break;
}
} while(!rc && (sshc->state != SSH_STOP));
return result;
}