Newer
Older
}
/* Set resume file transfer offset */
infof(data, "Instructs server to resume from offset %" FORMAT_OFF_T
"\n", data->state.resume_from);
PPSENDF(&ftpc->pp, "REST %" FORMAT_OFF_T, data->state.resume_from);
state(conn, FTP_RETR_REST);
}
else {
/* no resume */
PPSENDF(&ftpc->pp, "RETR %s", ftpc->file);
state(conn, FTP_RETR);
}
Daniel Stenberg
committed
return result;
static CURLcode ftp_state_size_resp(struct connectdata *conn,
int ftpcode,
ftpstate instate)
CURLcode result = CURLE_OK;
struct SessionHandle *data=conn->data;
curl_off_t filesize;
char *buf = data->state.buffer;
/* get the size from the ascii string: */
filesize = (ftpcode == 213)?curlx_strtoofft(buf+4, NULL, 0):-1;
if(instate == FTP_SIZE) {
#ifdef CURL_FTP_HTTPSTYLE_HEAD
if(-1 != filesize) {
snprintf(buf, sizeof(data->state.buffer),
"Content-Length: %" FORMAT_OFF_T "\r\n", filesize);
Daniel Stenberg
committed
result = Curl_client_write(conn, CLIENTWRITE_BOTH, buf, 0);
if(result)
Daniel Stenberg
committed
return result;
Curl_pgrsSetDownloadSize(data, filesize);
result = ftp_state_post_size(conn);
}
else if(instate == FTP_RETR_SIZE) {
Curl_pgrsSetDownloadSize(data, filesize);
result = ftp_state_post_retr_size(conn, filesize);
}
else if(instate == FTP_STOR_SIZE) {
Daniel Stenberg
committed
data->state.resume_from = filesize;
Daniel Stenberg
committed
result = ftp_state_ul_setup(conn, TRUE);
return result;
static CURLcode ftp_state_rest_resp(struct connectdata *conn,
int ftpcode,
ftpstate instate)
Daniel Stenberg
committed
{
CURLcode result = CURLE_OK;
struct ftp_conn *ftpc = &conn->proto.ftpc;
Daniel Stenberg
committed
switch(instate) {
case FTP_REST:
default:
#ifdef CURL_FTP_HTTPSTYLE_HEAD
Daniel Stenberg
committed
if(ftpcode == 350) {
char buffer[24]= { "Accept-ranges: bytes\r\n" };
result = Curl_client_write(conn, CLIENTWRITE_BOTH, buffer, 0);
if(result)
return result;
}
result = ftp_state_post_rest(conn);
break;
Daniel Stenberg
committed
case FTP_RETR_REST:
Daniel Stenberg
committed
if(ftpcode != 350) {
failf(conn->data, "Couldn't use REST");
result = CURLE_FTP_COULDNT_USE_REST;
}
else {
PPSENDF(&ftpc->pp, "RETR %s", ftpc->file);
state(conn, FTP_RETR);
Daniel Stenberg
committed
}
Daniel Stenberg
committed
}
return result;
Daniel Stenberg
committed
}
static CURLcode ftp_state_stor_resp(struct connectdata *conn,
int ftpcode)
Daniel Stenberg
committed
{
CURLcode result = CURLE_OK;
Daniel Stenberg
committed
struct SessionHandle *data = conn->data;
Daniel Stenberg
committed
struct FTP *ftp = data->state.proto.ftp;
Daniel Stenberg
committed
if(ftpcode>=400) {
failf(data, "Failed FTP upload: %0d", ftpcode);
/* oops, we never close the sockets! */
Daniel Stenberg
committed
return CURLE_UPLOAD_FAILED;
}
Daniel Stenberg
committed
if(data->set.ftp_use_port) {
/* BLOCKING */
/* PORT means we are now awaiting the server to connect to us. */
result = AllowServerConnect(conn);
if( result )
return result;
}
Daniel Stenberg
committed
if(conn->ssl[SECONDARYSOCKET].use) {
/* since we only have a plaintext TCP connection here, we must now
do the TLS stuff */
infof(data, "Doing the SSL/TLS handshake on the data stream\n");
/* BLOCKING */
result = Curl_ssl_connect(conn, SECONDARYSOCKET);
if(result)
return result;
}
*(ftp->bytecountp)=0;
Daniel Stenberg
committed
/* When we know we're uploading a specified file, we can get the file
size prior to the actual upload. */
Curl_pgrsSetUploadSize(data, data->set.infilesize);
Daniel Stenberg
committed
/* set the SO_SNDBUF for the secondary socket for those who need it */
Curl_sndbufset(conn->sock[SECONDARYSOCKET]);
Curl_setup_transfer(conn, -1, -1, FALSE, NULL, /* no download */
SECONDARYSOCKET, ftp->bytecountp);
state(conn, FTP_STOP);
conn->proto.ftpc.pp.pending_resp = TRUE; /* expect a server response */
Daniel Stenberg
committed
return result;
}
/* for LIST and RETR responses */
static CURLcode ftp_state_get_resp(struct connectdata *conn,
int ftpcode,
ftpstate instate)
Daniel Stenberg
committed
{
CURLcode result = CURLE_OK;
Daniel Stenberg
committed
struct SessionHandle *data = conn->data;
Daniel Stenberg
committed
struct FTP *ftp = data->state.proto.ftp;
char *buf = data->state.buffer;
Daniel Stenberg
committed
if((ftpcode == 150) || (ftpcode == 125)) {
Daniel Stenberg
committed
/*
A;
150 Opening BINARY mode data connection for /etc/passwd (2241
bytes). (ok, the file is being transfered)
Daniel Stenberg
committed
B:
150 Opening ASCII mode data connection for /bin/ls
Daniel Stenberg
committed
C:
150 ASCII data connection for /bin/ls (137.167.104.91,37445) (0 bytes).
D:
150 Opening ASCII mode data connection for /linux/fisk/kpanelrc (0.0.0.0,0) (545 bytes).
Daniel Stenberg
committed
E:
125 Data connection already open; Transfer starting. */
curl_off_t size=-1; /* default unknown size */
Daniel Stenberg
committed
/*
* It appears that there are FTP-servers that return size 0 for files when
* SIZE is used on the file while being in BINARY mode. To work around
* that (stupid) behavior, we attempt to parse the RETR response even if
* the SIZE returned size zero.
*
* Debugging help from Salvatore Sorrentino on February 26, 2003.
*/
Daniel Stenberg
committed
if((instate != FTP_LIST) &&
Daniel Stenberg
committed
!data->set.prefer_ascii &&
(ftp->downloadsize < 1)) {
/*
* It seems directory listings either don't show the size or very
* often uses size 0 anyway. ASCII transfers may very well turn out
* that the transfered amount of data is not the same as this line
* tells, why using this number in those cases only confuses us.
*
* Example D above makes this parsing a little tricky */
char *bytes;
bytes=strstr(buf, " bytes");
if(bytes--) {
long in=(long)(bytes-buf);
/* this is a hint there is size information in there! ;-) */
while(--in) {
/* scan for the left parenthesis and break there */
if('(' == *bytes)
break;
/* skip only digits */
Daniel Stenberg
committed
if(!ISDIGIT(*bytes)) {
bytes=NULL;
break;
}
/* one more estep backwards */
bytes--;
}
/* if we have nothing but digits: */
if(bytes++) {
/* get the number! */
size = curlx_strtoofft(bytes, NULL, 0);
}
}
}
else if(ftp->downloadsize > -1)
size = ftp->downloadsize;
Daniel Stenberg
committed
if(data->set.ftp_use_port) {
/* BLOCKING */
result = AllowServerConnect(conn);
if( result )
return result;
Daniel Stenberg
committed
}
Daniel Stenberg
committed
if(conn->ssl[SECONDARYSOCKET].use) {
/* since we only have a plaintext TCP connection here, we must now
do the TLS stuff */
infof(data, "Doing the SSL/TLS handshake on the data stream\n");
result = Curl_ssl_connect(conn, SECONDARYSOCKET);
if(result)
return result;
}
Daniel Stenberg
committed
if(size > data->req.maxdownload && data->req.maxdownload > 0)
size = data->req.size = data->req.maxdownload;
Daniel Stenberg
committed
else if((instate != FTP_LIST) && (data->set.prefer_ascii))
size = -1; /* kludge for servers that understate ASCII mode file size */
infof(data, "Maxdownload = %" FORMAT_OFF_T "\n", data->req.maxdownload);
if(instate != FTP_LIST)
infof(data, "Getting file with size: %" FORMAT_OFF_T "\n", size);
/* FTP download: */
Curl_setup_transfer(conn, SECONDARYSOCKET, size, FALSE,
ftp->bytecountp, -1, NULL); /* no upload here */
conn->proto.ftpc.pp.pending_resp = TRUE; /* expect server response */
state(conn, FTP_STOP);
}
else {
if((instate == FTP_LIST) && (ftpcode == 450)) {
/* simply no matching files in the dir listing */
ftp->transfer = FTPTRANSFER_NONE; /* don't download anything */
state(conn, FTP_STOP); /* this phase is over */
}
else {
failf(data, "RETR response: %03d", ftpcode);
return instate == FTP_RETR && ftpcode == 550?
CURLE_REMOTE_FILE_NOT_FOUND:
CURLE_FTP_COULDNT_RETR_FILE;
}
return result;
}
/* after USER, PASS and ACCT */
static CURLcode ftp_state_loggedin(struct connectdata *conn)
{
CURLcode result = CURLE_OK;
#ifdef HAVE_KRB4
if(conn->data->set.krb) {
/* We may need to issue a KAUTH here to have access to the files
* do it if user supplied a password
*/
if(conn->passwd && *conn->passwd) {
/* BLOCKING */
result = Curl_krb_kauth(conn);
if(result)
return result;
}
}
#endif
if(conn->ssl[FIRSTSOCKET].use) {
/* PBSZ = PROTECTION BUFFER SIZE.
The 'draft-murray-auth-ftp-ssl' (draft 12, page 7) says:
Specifically, the PROT command MUST be preceded by a PBSZ
command and a PBSZ command MUST be preceded by a successful
security data exchange (the TLS negotiation in this case)
... (and on page 8):
Thus the PBSZ command must still be issued, but must have a
parameter of '0' to indicate that no buffering is taking place
and the data connection should not be encapsulated.
*/
PPSENDF(&conn->proto.ftpc.pp, "PBSZ %d", 0);
state(conn, FTP_PBSZ);
}
else {
result = ftp_state_pwd(conn);
}
return result;
}
/* for USER and PASS responses */
static CURLcode ftp_state_user_resp(struct connectdata *conn,
int ftpcode,
ftpstate instate)
{
CURLcode result = CURLE_OK;
struct SessionHandle *data = conn->data;
Daniel Stenberg
committed
struct FTP *ftp = data->state.proto.ftp;
struct ftp_conn *ftpc = &conn->proto.ftpc;
(void)instate; /* no use for this yet */
/* some need password anyway, and others just return 2xx ignored */
if((ftpcode == 331) && (ftpc->state == FTP_USER)) {
/* 331 Password required for ...
(the server requires to send the user's password too) */
PPSENDF(&ftpc->pp, "PASS %s", ftp->passwd?ftp->passwd:"");
state(conn, FTP_PASS);
}
else if(ftpcode/100 == 2) {
/* 230 User ... logged in.
(the user logged in with or without password) */
result = ftp_state_loggedin(conn);
}
else if(ftpcode == 332) {
Daniel Stenberg
committed
if(data->set.str[STRING_FTP_ACCOUNT]) {
PPSENDF(&ftpc->pp, "ACCT %s", data->set.str[STRING_FTP_ACCOUNT]);
state(conn, FTP_ACCT);
}
else {
failf(data, "ACCT requested but none available");
result = CURLE_LOGIN_DENIED;
Daniel Stenberg
committed
}
else {
/* All other response codes, like:
Daniel Stenberg
committed
530 User ... access denied
(the server denies to log the specified user) */
Daniel Stenberg
committed
Daniel Stenberg
committed
if(conn->data->set.str[STRING_FTP_ALTERNATIVE_TO_USER] &&
Daniel Stenberg
committed
!conn->data->state.ftp_trying_alternative) {
/* Ok, USER failed. Let's try the supplied command. */
PPSENDF(&conn->proto.ftpc.pp, "%s",
conn->data->set.str[STRING_FTP_ALTERNATIVE_TO_USER]);
Daniel Stenberg
committed
conn->data->state.ftp_trying_alternative = TRUE;
state(conn, FTP_USER);
result = CURLE_OK;
}
else {
failf(data, "Access denied: %03d", ftpcode);
result = CURLE_LOGIN_DENIED;
}
Daniel Stenberg
committed
}
return result;
}
/* for ACCT response */
static CURLcode ftp_state_acct_resp(struct connectdata *conn,
int ftpcode)
{
CURLcode result = CURLE_OK;
struct SessionHandle *data = conn->data;
if(ftpcode != 230) {
failf(data, "ACCT rejected by server: %03d", ftpcode);
result = CURLE_FTP_WEIRD_PASS_REPLY; /* FIX */
Daniel Stenberg
committed
}
else
result = ftp_state_loggedin(conn);
return result;
}
static CURLcode ftp_statemach_act(struct connectdata *conn)
{
CURLcode result;
curl_socket_t sock = conn->sock[FIRSTSOCKET];
struct SessionHandle *data=conn->data;
int ftpcode;
struct ftp_conn *ftpc = &conn->proto.ftpc;
struct pingpong *pp = &ftpc->pp;
static const char ftpauth[][4] = { "SSL", "TLS" };
if(pp->sendleft)
return Curl_pp_flushsend(pp);
/* we read a piece of response */
result = ftp_readresp(sock, pp, &ftpcode, &nread);
if(result)
return result;
Sterling Hughes
committed
if(ftpcode) {
/* we have now received a full FTP server response */
switch(ftpc->state) {
case FTP_WAIT220:
if(ftpcode != 220) {
Daniel Stenberg
committed
failf(data, "Got a %03d ftp-server response when 220 was expected",
ftpcode);
return CURLE_FTP_WEIRD_SERVER_REPLY;
Daniel Stenberg
committed
}
Sterling Hughes
committed
/* We have received a 220 response fine, now we proceed. */
#if defined(HAVE_KRB4) || defined(HAVE_GSSAPI)
if(data->set.krb) {
/* If not anonymous login, try a secure login. Note that this
procedure is still BLOCKING. */
Curl_sec_request_prot(conn, "private");
/* We set private first as default, in case the line below fails to
set a valid level */
Curl_sec_request_prot(conn, data->set.str[STRING_KRB_LEVEL]);
Sterling Hughes
committed
if(Curl_sec_login(conn) != CURLE_OK)
infof(data, "Logging in with password in cleartext!\n");
Daniel Stenberg
committed
else
infof(data, "Authentication successful\n");
}
#endif
if(data->set.ftp_ssl && !conn->ssl[FIRSTSOCKET].use) {
/* We don't have a SSL/TLS connection yet, but FTPS is
requested. Try a FTPS connection now */
ftpc->count3=0;
switch(data->set.ftpsslauth) {
case CURLFTPAUTH_DEFAULT:
case CURLFTPAUTH_SSL:
ftpc->count2 = 1; /* add one to get next */
ftpc->count1 = 0;
break;
case CURLFTPAUTH_TLS:
ftpc->count2 = -1; /* subtract one to get next */
ftpc->count1 = 1;
break;
default:
Daniel Stenberg
committed
failf(data, "unsupported parameter to CURLOPT_FTPSSLAUTH: %d",
return CURLE_FAILED_INIT; /* we don't know what to do */
Sterling Hughes
committed
}
PPSENDF(&ftpc->pp, "AUTH %s", ftpauth[ftpc->count1]);
state(conn, FTP_AUTH);
Daniel Stenberg
committed
}
else {
result = ftp_state_user(conn);
if(result)
return result;
Daniel Stenberg
committed
}
Sterling Hughes
committed
break;
case FTP_AUTH:
/* we have gotten the response to a previous AUTH command */
/* RFC2228 (page 5) says:
*
* If the server is willing to accept the named security mechanism,
* and does not require any security data, it must respond with
* reply code 234/334.
*/
if((ftpcode == 234) || (ftpcode == 334)) {
Daniel Stenberg
committed
/* Curl_ssl_connect is BLOCKING */
result = Curl_ssl_connect(conn, FIRSTSOCKET);
Daniel Stenberg
committed
if(CURLE_OK == result) {
conn->protocol |= PROT_FTPS;
conn->ssl[SECONDARYSOCKET].use = FALSE; /* clear-text data */
result = ftp_state_user(conn);
}
else if(ftpc->count3 < 1) {
ftpc->count3++;
ftpc->count1 += ftpc->count2; /* get next attempt */
result = Curl_pp_sendf(&ftpc->pp, "AUTH %s", ftpauth[ftpc->count1]);
/* remain in this same state */
}
else {
if(data->set.ftp_ssl > CURLUSESSL_TRY)
/* we failed and CURLUSESSL_CONTROL or CURLUSESSL_ALL is set */
result = CURLE_USE_SSL_FAILED;
else
/* ignore the failure and continue */
result = ftp_state_user(conn);
}
Daniel Stenberg
committed
if(result)
return result;
Daniel Stenberg
committed
break;
case FTP_USER:
case FTP_PASS:
result = ftp_state_user_resp(conn, ftpcode, ftpc->state);
break;
Daniel Stenberg
committed
case FTP_ACCT:
result = ftp_state_acct_resp(conn, ftpcode);
break;
Daniel Stenberg
committed
case FTP_PBSZ:
PPSENDF(&ftpc->pp, "PROT %c",
data->set.ftp_ssl == CURLUSESSL_CONTROL ? 'C' : 'P');
state(conn, FTP_PROT);
Daniel Stenberg
committed
break;
Daniel Stenberg
committed
case FTP_PROT:
if(ftpcode/100 == 2)
/* We have enabled SSL for the data connection! */
Daniel Stenberg
committed
conn->ssl[SECONDARYSOCKET].use =
(bool)(data->set.ftp_ssl != CURLUSESSL_CONTROL);
/* FTP servers typically responds with 500 if they decide to reject
our 'P' request */
else if(data->set.ftp_ssl > CURLUSESSL_CONTROL)
/* we failed and bails out */
return CURLE_USE_SSL_FAILED;
Linus Nielsen
committed
if(data->set.ftp_ccc) {
Daniel Stenberg
committed
/* CCC - Clear Command Channel
*/
PPSENDF(&ftpc->pp, "CCC", NULL);
Daniel Stenberg
committed
state(conn, FTP_CCC);
}
else {
result = ftp_state_pwd(conn);
if(result)
return result;
}
break;
case FTP_CCC:
Daniel Stenberg
committed
if(ftpcode < 500) {
/* First shut down the SSL layer (note: this call will block) */
result = Curl_ssl_shutdown(conn, FIRSTSOCKET);
if(result) {
failf(conn->data, "Failed to clear the command channel (CCC)");
return result;
}
Daniel Stenberg
committed
/* Then continue as normal */
result = ftp_state_pwd(conn);
if(result)
return result;
break;
Daniel Stenberg
committed
case FTP_PWD:
if(ftpcode == 257) {
char *ptr=&data->state.buffer[4]; /* start on the first letter */
Patrick Monnerat
committed
char *dir;
char *store;
Daniel Stenberg
committed
Patrick Monnerat
committed
dir = malloc(nread + 1);
if(!dir)
return CURLE_OUT_OF_MEMORY;
/* Reply format is like
257<space>"<directory-name>"<space><commentary> and the RFC959
says
Daniel Stenberg
committed
The directory name can contain any character; embedded
double-quotes should be escaped by double-quotes (the
"quote-doubling" convention).
*/
if('\"' == *ptr) {
/* it started good */
ptr++;
Patrick Monnerat
committed
for (store = dir; *ptr;) {
if('\"' == *ptr) {
if('\"' == ptr[1]) {
/* "quote-doubling" */
*store = ptr[1];
ptr++;
}
else {
/* end of path */
*store = '\0'; /* zero terminate */
break; /* get out of this loop */
}
}
else
*store = *ptr;
store++;
ptr++;
}
Patrick Monnerat
committed
if(ftpc->entrypath)
free(ftpc->entrypath);
ftpc->entrypath =dir; /* remember this */
infof(data, "Entry path is '%s'\n", ftpc->entrypath);
/* also save it where getinfo can access it: */
data->state.most_recent_ftp_entrypath = ftpc->entrypath;
Patrick Monnerat
committed
/* If the path name does not look like an absolute path (i.e.: it
does not start with a '/'), we probably need some server-dependent
adjustments. For example, this is the case when connecting to
an OS400 FTP server: this server supports two name syntaxes,
the default one being incompatible with standard pathes. In
addition, this server switches automatically to the regular path
syntax when one is encountered in a command: this results in
having an entrypath in the wrong syntax when later used in CWD.
The method used here is to check the server OS: we do it only
if the path name looks strange to minimize overhead on other
systems. */
if(!ftpc->server_os && ftpc->entrypath[0] != '/') {
PPSENDF(&ftpc->pp, "SYST", NULL);
Patrick Monnerat
committed
state(conn, FTP_SYST);
break;
}
else {
/* couldn't get the path */
free(dir);
infof(data, "Failed to figure out path\n");
state(conn, FTP_STOP); /* we are done with the CONNECT phase! */
DEBUGF(infof(data, "protocol connect phase DONE\n"));
break;
Patrick Monnerat
committed
2634
2635
2636
2637
2638
2639
2640
2641
2642
2643
2644
2645
2646
2647
2648
2649
2650
2651
2652
2653
2654
2655
2656
2657
case FTP_SYST:
if(ftpcode == 215) {
char *ptr=&data->state.buffer[4]; /* start on the first letter */
char *os;
char *store;
os = malloc(nread + 1);
if(!os)
return CURLE_OUT_OF_MEMORY;
/* Reply format is like
215<space><OS-name><space><commentary>
*/
while (*ptr == ' ')
ptr++;
for (store = os; *ptr && *ptr != ' ';)
*store++ = *ptr++;
*store = '\0'; /* zero terminate */
ftpc->server_os = os;
/* Check for special servers here. */
if(strequal(ftpc->server_os, "OS/400")) {
/* Force OS400 name format 1. */
PPSENDF(&ftpc->pp, "SITE NAMEFMT 1", NULL);
Patrick Monnerat
committed
2659
2660
2661
2662
2663
2664
2665
2666
2667
2668
2669
2670
2671
2672
2673
2674
2675
2676
2677
2678
2679
2680
2681
2682
2683
2684
state(conn, FTP_NAMEFMT);
break;
}
else {
/* Nothing special for the target server. */
}
}
else {
/* Cannot identify server OS. Continue anyway and cross fingers. */
}
state(conn, FTP_STOP); /* we are done with the CONNECT phase! */
DEBUGF(infof(data, "protocol connect phase DONE\n"));
break;
case FTP_NAMEFMT:
if(ftpcode == 250) {
/* Name format change successful: reload initial path. */
ftp_state_pwd(conn);
break;
}
state(conn, FTP_STOP); /* we are done with the CONNECT phase! */
DEBUGF(infof(data, "protocol connect phase DONE\n"));
break;
case FTP_QUOTE:
case FTP_POSTQUOTE:
case FTP_RETR_PREQUOTE:
case FTP_STOR_PREQUOTE:
if((ftpcode >= 400) && !ftpc->count2) {
/* failure reponse code, and not allowed to fail */
failf(conn->data, "QUOT command failed with %03d", ftpcode);
return CURLE_QUOTE_ERROR;
result = ftp_state_quote(conn, FALSE, ftpc->state);
if(result)
return result;
Daniel Stenberg
committed
break;
Daniel Stenberg
committed
case FTP_CWD:
if(ftpcode/100 != 2) {
/* failure to CWD there */
if(conn->data->set.ftp_create_missing_dirs &&
ftpc->count1 && !ftpc->count2) {
/* try making it */
ftpc->count2++; /* counter to prevent CWD-MKD loops */
PPSENDF(&ftpc->pp, "MKD %s", ftpc->dirs[ftpc->count1 - 1]);
state(conn, FTP_MKD);
}
/* return failure */
failf(data, "Server denied you to change to the given directory");
ftpc->cwdfail = TRUE; /* don't remember this path as we failed
to enter it */
return CURLE_REMOTE_ACCESS_DENIED;
}
else {
/* success */
ftpc->count2=0;
if(++ftpc->count1 <= ftpc->dirdepth) {
/* send next CWD */
PPSENDF(&ftpc->pp, "CWD %s", ftpc->dirs[ftpc->count1 - 1]);
}
else {
result = ftp_state_post_cwd(conn);
if(result)
return result;
}
}
break;
Daniel Stenberg
committed
case FTP_MKD:
Daniel Stenberg
committed
if((ftpcode/100 != 2) && !ftpc->count3--) {
/* failure to MKD the dir */
failf(data, "Failed to MKD dir: %03d", ftpcode);
return CURLE_REMOTE_ACCESS_DENIED;
}
state(conn, FTP_CWD);
/* send CWD */
PPSENDF(&ftpc->pp, "CWD %s", ftpc->dirs[ftpc->count1 - 1]);
break;
case FTP_MDTM:
result = ftp_state_mdtm_resp(conn, ftpcode);
break;
Daniel Stenberg
committed
case FTP_TYPE:
case FTP_LIST_TYPE:
case FTP_RETR_TYPE:
case FTP_STOR_TYPE:
result = ftp_state_type_resp(conn, ftpcode, ftpc->state);
break;
Daniel Stenberg
committed
case FTP_SIZE:
case FTP_RETR_SIZE:
case FTP_STOR_SIZE:
result = ftp_state_size_resp(conn, ftpcode, ftpc->state);
break;
case FTP_REST:
case FTP_RETR_REST:
result = ftp_state_rest_resp(conn, ftpcode, ftpc->state);
break;
Daniel Stenberg
committed
case FTP_PRET:
if(ftpcode != 200) {
/* there only is this one standard OK return code. */
failf(data, "PRET command not accepted: %03d", ftpcode);
return CURLE_FTP_PRET_FAILED;
}
result = ftp_state_use_pasv(conn);
break;
case FTP_PASV:
result = ftp_state_pasv_resp(conn, ftpcode);
break;
Daniel Stenberg
committed
case FTP_PORT:
result = ftp_state_port_resp(conn, ftpcode);
break;
Daniel Stenberg
committed
case FTP_LIST:
case FTP_RETR:
result = ftp_state_get_resp(conn, ftpcode, ftpc->state);
break;
Daniel Stenberg
committed
case FTP_STOR:
result = ftp_state_stor_resp(conn, ftpcode);
break;
case FTP_QUIT:
/* fallthrough, just stop! */
default:
/* internal error */
state(conn, FTP_STOP);
break;
}
} /* if(ftpcode) */
return result;
}
Daniel Stenberg
committed
/* called repeatedly until done from multi.c */
static CURLcode ftp_multi_statemach(struct connectdata *conn,
bool *done)
{
struct ftp_conn *ftpc = &conn->proto.ftpc;
CURLcode result = Curl_pp_multi_statemach(&ftpc->pp);
Daniel Stenberg
committed
/* Check for the state outside of the Curl_socket_ready() return code checks
since at times we are in fact already in this state when this function
gets called. */
*done = (bool)(ftpc->state == FTP_STOP);
return result;
}
static CURLcode ftp_easy_statemach(struct connectdata *conn)
{
struct ftp_conn *ftpc = &conn->proto.ftpc;
struct pingpong *pp = &ftpc->pp;
CURLcode result = CURLE_OK;
Sterling Hughes
committed
while(ftpc->state != FTP_STOP) {
result = Curl_pp_easy_statemach(pp);
if(result)
break;
return result;
}
Daniel Stenberg
committed
/*
* Allocate and initialize the struct FTP for the current SessionHandle. If
* need be.
*/
#if defined(__INTEL_COMPILER) && (__INTEL_COMPILER == 910) && \
defined(__OPTIMIZE__) && defined(__unix__) && defined(__i386__)
/* workaround icc 9.1 optimizer issue */
static CURLcode ftp_init(struct connectdata *conn)
{
if(NULL == conn->data->state.proto.ftp) {
conn->data->state.proto.ftp = malloc(sizeof(struct FTP));
if(NULL == conn->data->state.proto.ftp)
return CURLE_OUT_OF_MEMORY;
}
ftp = conn->data->state.proto.ftp;
/* get some initial data into the ftp struct */
ftp->bytecountp = &conn->data->req.bytecount;
ftp->transfer = FTPTRANSFER_BODY;
ftp->downloadsize = 0;
/* No need to duplicate user+password, the connectdata struct won't change
during a session, but we re-init them here since on subsequent inits
since the conn struct may have changed or been replaced.
*/
ftp->user = conn->user;
ftp->passwd = conn->passwd;
if(TRUE == isBadFtpString(ftp->user))
return CURLE_URL_MALFORMAT;
if(TRUE == isBadFtpString(ftp->passwd))
return CURLE_URL_MALFORMAT;
conn->proto.ftpc.known_filesize = -1; /* unknown size for now */
return CURLE_OK;
}
#if defined(__INTEL_COMPILER) && (__INTEL_COMPILER == 910) && \
defined(__OPTIMIZE__) && defined(__unix__) && defined(__i386__)
/* workaround icc 9.1 optimizer issue */
#pragma optimize("", on)
#endif
/*
* ftp_connect() should do everything that is to be considered a part of
* the connection phase.
*
* The variable 'done' points to will be TRUE if the protocol-layer connect
* phase is done when this function returns, or FALSE is not. When called as
* a part of the easy interface, it will always be TRUE.
*/
static CURLcode ftp_connect(struct connectdata *conn,
Patrick Monnerat
committed
bool *done) /* see description above */
{
CURLcode result;
struct ftp_conn *ftpc = &conn->proto.ftpc;
struct SessionHandle *data=conn->data;
struct pingpong *pp = &ftpc->pp;
*done = FALSE; /* default to not done yet */
Daniel Stenberg
committed
/* If there already is a protocol-specific struct allocated for this
sessionhandle, deal with it */
Curl_reset_reqproto(conn);
result = ftp_init(conn);
if(CURLE_OK != result)
return result;
/* We always support persistant connections on ftp */
conn->bits.close = FALSE;
pp->response_time = RESP_TIMEOUT; /* set default response time-out */
pp->statemach_act = ftp_statemach_act;
pp->endofresp = ftp_endofresp;
pp->conn = conn;
Dan Fandrich
committed
#if !defined(CURL_DISABLE_HTTP) && !defined(CURL_DISABLE_PROXY)
Daniel Stenberg
committed
if(conn->bits.tunnel_proxy && conn->bits.httpproxy) {
/* for FTP over HTTP proxy */
struct HTTP http_proxy;
struct FTP *ftp_save;
/* BLOCKING */
/* We want "seamless" FTP operations through HTTP proxy tunnel */
/* Curl_proxyCONNECT is based on a pointer to a struct HTTP at the member
* conn->proto.http; we want FTP through HTTP and we have to change the
* member temporarily for connecting to the HTTP proxy. After
* Curl_proxyCONNECT we have to set back the member to the original struct
* FTP pointer
*/
Daniel Stenberg
committed
ftp_save = data->state.proto.ftp;
memset(&http_proxy, 0, sizeof(http_proxy));
Daniel Stenberg
committed
data->state.proto.http = &http_proxy;
Daniel Stenberg
committed
result = Curl_proxyCONNECT(conn, FIRSTSOCKET,
conn->host.name, conn->remote_port);
Daniel Stenberg
committed
data->state.proto.ftp = ftp_save;
Daniel Stenberg
committed
}
Dan Fandrich
committed
#endif /* !CURL_DISABLE_HTTP && !CURL_DISABLE_PROXY */
Daniel Stenberg
committed
if(conn->protocol & PROT_FTPS) {
/* BLOCKING */
/* FTPS is simply ftp with SSL for the control channel */
/* now, perform the SSL initialization for this socket */
Daniel Stenberg
committed
result = Curl_ssl_connect(conn, FIRSTSOCKET);
if(result)
return result;
}
Curl_pp_init(pp); /* init the generic pingpong data */
/* When we connect, we start in the state where we await the 220
response */
state(conn, FTP_WAIT220);
if(data->state.used_interface == Curl_if_multi)
result = ftp_multi_statemach(conn, done);
else {
result = ftp_easy_statemach(conn);
if(!result)
*done = TRUE;
}
return result;
Daniel Stenberg
committed
}
/***********************************************************************
*
* ftp_done()
*
* The DONE function. This does what needs to be done after a single DO has
* performed.
* Input argument is already checked for validity.
static CURLcode ftp_done(struct connectdata *conn, CURLcode status,
Patrick Monnerat
committed
bool premature)
Daniel Stenberg
committed
{
struct SessionHandle *data = conn->data;
Daniel Stenberg
committed
struct FTP *ftp = data->state.proto.ftp;
struct ftp_conn *ftpc = &conn->proto.ftpc;
struct pingpong *pp = &ftpc->pp;
ssize_t nread;
int ftpcode;
CURLcode result=CURLE_OK;
bool was_ctl_valid = ftpc->ctl_valid;
char *path;
const char *path_to_use = data->state.path;
Daniel Stenberg
committed
if(!ftp)
/* When the easy handle is removed from the multi while libcurl is still
* trying to resolve the host name, it seems that the ftp struct is not
* yet initialized, but the removal action calls Curl_done() which calls