Newer
Older
/* FIX: this MUST wait for a proper connect first if 'connected' is
* FALSE */
case CURLPROXY_SOCKS5:
case CURLPROXY_SOCKS5_HOSTNAME:
result = Curl_SOCKS5(conn->proxyuser, conn->proxypasswd, newhost, newport,
SECONDARYSOCKET, conn);
connected = TRUE;
break;
case CURLPROXY_SOCKS4:
result = Curl_SOCKS4(conn->proxyuser, newhost, newport,
SECONDARYSOCKET, conn, FALSE);
connected = TRUE;
break;
case CURLPROXY_SOCKS4A:
result = Curl_SOCKS4(conn->proxyuser, newhost, newport,
SECONDARYSOCKET, conn, TRUE);
connected = TRUE;
break;
case CURLPROXY_HTTP:
case CURLPROXY_HTTP_1_0:
/* do nothing here. handled later. */
break;
default:
failf(data, "unknown proxytype option given");
result = CURLE_COULDNT_CONNECT;
break;
}
if(result) {
if(ftpc->count1 == 0 && ftpcode == 229)
return ftp_epsv_disable(conn);
Daniel Stenberg
committed
if(conn->bits.tunnel_proxy && conn->bits.httpproxy) {
/* FIX: this MUST wait for a proper connect first if 'connected' is
* FALSE */
/* 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
*/
struct HTTP http_proxy;
struct FTP *ftp_save = data->req.protop;
memset(&http_proxy, 0, sizeof(http_proxy));
data->req.protop = &http_proxy;
Daniel Stenberg
committed
result = Curl_proxyCONNECT(conn, SECONDARYSOCKET, newhost, newport);
data->req.protop = ftp_save;
return result;
if(conn->tunnel_state[SECONDARYSOCKET] != TUNNEL_COMPLETE) {
/* the CONNECT procedure is not complete, the tunnel is not yet up */
state(conn, FTP_STOP); /* this phase is completed */
conn->bits.tcpconnect[SECONDARYSOCKET] = FALSE;
return result;
}
}
conn->bits.tcpconnect[SECONDARYSOCKET] = connected;
conn->bits.do_more = TRUE;
state(conn, FTP_STOP); /* this phase is completed */
return result;
}
static CURLcode ftp_state_port_resp(struct connectdata *conn,
int ftpcode)
{
struct SessionHandle *data = conn->data;
struct ftp_conn *ftpc = &conn->proto.ftpc;
ftpport fcmd = (ftpport)ftpc->count1;
CURLcode result = CURLE_OK;
if(ftpcode != 200) {
/* the command failed */
Daniel Stenberg
committed
if(EPRT == fcmd) {
infof(data, "disabling EPRT usage\n");
conn->bits.ftp_use_eprt = FALSE;
fcmd++;
if(fcmd == DONE) {
failf(data, "Failed to do PORT");
result = CURLE_FTP_PORT_FAILED;
}
else
/* try next */
result = ftp_state_use_port(conn, fcmd);
infof(data, "Connect data stream actively\n");
state(conn, FTP_STOP); /* end of DO phase */
result = ftp_dophase_done(conn, FALSE);
return result;
static CURLcode ftp_state_mdtm_resp(struct connectdata *conn,
int ftpcode)
CURLcode result = CURLE_OK;
struct SessionHandle *data=conn->data;
struct FTP *ftp = data->req.protop;
struct ftp_conn *ftpc = &conn->proto.ftpc;
Daniel Stenberg
committed
switch(ftpcode) {
case 213:
{
/* we got a time. Format should be: "YYYYMMDDHHMMSS[.sss]" where the
last .sss part is optional and means fractions of a second */
int year, month, day, hour, minute, second;
char *buf = data->state.buffer;
if(6 == sscanf(buf+4, "%04d%02d%02d%02d%02d%02d",
&year, &month, &day, &hour, &minute, &second)) {
/* we have a time, reformat it */
time_t secs=time(NULL);
/* using the good old yacc/bison yuck */
snprintf(buf, sizeof(conn->data->state.buffer),
"%04d%02d%02d %02d:%02d:%02d GMT",
year, month, day, hour, minute, second);
/* now, convert this into a time() value: */
data->info.filetime = (long)curl_getdate(buf, &secs);
}
#ifdef CURL_FTP_HTTPSTYLE_HEAD
/* If we asked for a time of the file and we actually got one as well,
we "emulate" a HTTP-style header in our output. */
Daniel Stenberg
committed
Daniel Stenberg
committed
if(data->set.opt_no_body &&
Daniel Stenberg
committed
data->set.get_filetime &&
(data->info.filetime>=0) ) {
time_t filetime = (time_t)data->info.filetime;
struct tm buffer;
const struct tm *tm = &buffer;
result = Curl_gmtime(filetime, &buffer);
if(result)
return result;
/* format: "Tue, 15 Nov 1994 12:45:26" */
snprintf(buf, BUFSIZE-1,
"Last-Modified: %s, %02d %s %4d %02d:%02d:%02d GMT\r\n",
Curl_wkday[tm->tm_wday?tm->tm_wday-1:6],
tm->tm_mday,
Curl_month[tm->tm_mon],
tm->tm_year + 1900,
tm->tm_hour,
tm->tm_min,
tm->tm_sec);
Daniel Stenberg
committed
result = Curl_client_write(conn, CLIENTWRITE_BOTH, buf, 0);
if(result)
return result;
Daniel Stenberg
committed
} /* end of a ridiculous amount of conditionals */
break;
default:
infof(data, "unsupported MDTM reply format\n");
break;
case 550: /* "No such file or directory" */
failf(data, "Given file does not exist");
result = CURLE_FTP_COULDNT_RETR_FILE;
break;
if(data->set.timecondition) {
if((data->info.filetime > 0) && (data->set.timevalue > 0)) {
switch(data->set.timecondition) {
case CURL_TIMECOND_IFMODSINCE:
default:
Daniel Stenberg
committed
if(data->info.filetime <= data->set.timevalue) {
infof(data, "The requested document is not new enough\n");
ftp->transfer = FTPTRANSFER_NONE; /* mark to not transfer data */
Daniel Stenberg
committed
data->info.timecond = TRUE;
state(conn, FTP_STOP);
return CURLE_OK;
}
break;
case CURL_TIMECOND_IFUNMODSINCE:
if(data->info.filetime > data->set.timevalue) {
infof(data, "The requested document is not old enough\n");
ftp->transfer = FTPTRANSFER_NONE; /* mark to not transfer data */
Daniel Stenberg
committed
data->info.timecond = TRUE;
state(conn, FTP_STOP);
return CURLE_OK;
}
break;
} /* switch */
else {
infof(data, "Skipping time comparison\n");
if(!result)
return result;
}
static CURLcode ftp_state_type_resp(struct connectdata *conn,
int ftpcode,
ftpstate instate)
{
CURLcode result = CURLE_OK;
struct SessionHandle *data=conn->data;
if(ftpcode/100 != 2) {
/* "sasserftpd" and "(u)r(x)bot ftpd" both responds with 226 after a
successful 'TYPE I'. While that is not as RFC959 says, it is still a
positive response code and we allow that. */
failf(data, "Couldn't set desired mode");
return CURLE_FTP_COULDNT_SET_TYPE;
}
if(ftpcode != 200)
infof(data, "Got a %03d response code instead of the assumed 200\n",
ftpcode);
if(instate == FTP_TYPE)
else if(instate == FTP_LIST_TYPE)
else if(instate == FTP_RETR_TYPE)
result = ftp_state_retr_prequote(conn);
else if(instate == FTP_STOR_TYPE)
result = ftp_state_stor_prequote(conn);
return result;
}
static CURLcode ftp_state_retr(struct connectdata *conn,
curl_off_t filesize)
{
CURLcode result = CURLE_OK;
struct SessionHandle *data=conn->data;
struct FTP *ftp = data->req.protop;
struct ftp_conn *ftpc = &conn->proto.ftpc;
Daniel Stenberg
committed
if(data->set.max_filesize && (filesize > data->set.max_filesize)) {
failf(data, "Maximum file size exceeded");
return CURLE_FILESIZE_EXCEEDED;
}
ftp->downloadsize = filesize;
Daniel Stenberg
committed
if(data->state.resume_from) {
/* We always (attempt to) get the size of downloads, so it is done before
this even when not doing resumes. */
if(filesize == -1) {
infof(data, "ftp server doesn't support SIZE\n");
/* We couldn't get the size and therefore we can't know if there really
is a part of the file left to get, although the server will just
close the connection when we start the connection so it won't cause
us any harm, just not make us exit as nicely. */
}
else {
/* We got a file size report, so we check that there actually is a
part of the file left to get, or else we go home. */
Daniel Stenberg
committed
if(data->state.resume_from< 0) {
/* We're supposed to download the last abs(from) bytes */
Daniel Stenberg
committed
if(filesize < -data->state.resume_from) {
failf(data, "Offset (%" FORMAT_OFF_T
") was beyond file size (%" FORMAT_OFF_T ")",
Daniel Stenberg
committed
data->state.resume_from, filesize);
return CURLE_BAD_DOWNLOAD_RESUME;
}
/* convert to size to download */
Daniel Stenberg
committed
ftp->downloadsize = -data->state.resume_from;
/* download from where? */
Daniel Stenberg
committed
data->state.resume_from = filesize - ftp->downloadsize;
}
else {
Daniel Stenberg
committed
if(filesize < data->state.resume_from) {
failf(data, "Offset (%" FORMAT_OFF_T
") was beyond file size (%" FORMAT_OFF_T ")",
Daniel Stenberg
committed
data->state.resume_from, filesize);
return CURLE_BAD_DOWNLOAD_RESUME;
}
/* Now store the number of bytes we are expected to download */
Daniel Stenberg
committed
ftp->downloadsize = filesize-data->state.resume_from;
}
}
if(ftp->downloadsize == 0) {
/* no data to transfer */
Curl_setup_transfer(conn, -1, -1, FALSE, NULL, -1, NULL);
infof(data, "File already completely downloaded\n");
/* Set ->transfer so that we won't get any error in ftp_done()
* because we didn't transfer the any file */
ftp->transfer = FTPTRANSFER_NONE;
state(conn, FTP_STOP);
return CURLE_OK;
}
/* 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);
}
else if(instate == FTP_RETR_SIZE) {
Curl_pgrsSetDownloadSize(data, filesize);
result = ftp_state_retr(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_prepare_transfer(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, ftpstate instate)
Daniel Stenberg
committed
{
CURLcode result = CURLE_OK;
Daniel Stenberg
committed
struct SessionHandle *data = conn->data;
Daniel Stenberg
committed
if(ftpcode>=400) {
failf(data, "Failed FTP upload: %0d", ftpcode);
state(conn, FTP_STOP);
/* oops, we never close the sockets! */
Daniel Stenberg
committed
return CURLE_UPLOAD_FAILED;
}
Daniel Stenberg
committed
conn->proto.ftpc.state_saved = instate;
/* PORT means we are now awaiting the server to connect to us. */
if(data->set.ftp_use_port) {
state(conn, FTP_STOP); /* no longer in STOR state */
result = AllowServerConnect(conn, &connected);
Daniel Stenberg
committed
if(result)
return result;
struct ftp_conn *ftpc = &conn->proto.ftpc;
infof(data, "Data conn was not available immediately\n");
ftpc->wait_data_conn = TRUE;
Daniel Stenberg
committed
return CURLE_OK;
}
else
return InitiateTransfer(conn);
}
/* 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;
struct FTP *ftp = data->req.protop;
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 transferred)
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 [file] (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 transferred 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
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: */
conn->proto.ftpc.state_saved = instate;
conn->proto.ftpc.retr_size_saved = size;
if(data->set.ftp_use_port) {
bool connected;
result = AllowServerConnect(conn, &connected);
if(result)
return result;
struct ftp_conn *ftpc = &conn->proto.ftpc;
infof(data, "Data conn was not available immediately\n");
state(conn, FTP_STOP);
ftpc->wait_data_conn = TRUE;
}
}
else
return InitiateTransfer(conn);
}
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;
}
2559
2560
2561
2562
2563
2564
2565
2566
2567
2568
2569
2570
2571
2572
2573
2574
2575
2576
2577
2578
2579
2580
2581
return result;
}
/* after USER, PASS and ACCT */
static CURLcode ftp_state_loggedin(struct connectdata *conn)
{
CURLcode result = CURLE_OK;
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;
struct FTP *ftp = data->req.protop;
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);
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 == 230)
/* 230 User logged in - already! */
return ftp_state_user_resp(conn, ftpcode, ftpc->state);
else 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(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.use_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_UNKNOWN_OPTION; /* 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->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.use_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.use_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 =
(data->set.use_ssl != CURLUSESSL_CONTROL) ? TRUE : FALSE;
/* FTP servers typically responds with 500 if they decide to reject
our 'P' request */
else if(data->set.use_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, "%s", "CCC");
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>[rubbish]"<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).
*/
/* scan for the first double-quote for non-standard responses */
while(ptr < &data->state.buffer[sizeof(data->state.buffer)]
&& *ptr != '\n' && *ptr != '\0' && *ptr != '"')
ptr++;
if('\"' == *ptr) {
/* it started good */
ptr++;
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 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 && dir[0] != '/') {
result = Curl_pp_sendf(&ftpc->pp, "%s", "SYST");
free(dir);
Curl_safefree(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
state(conn, FTP_SYST);
break;
}
Curl_safefree(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;
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
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 == ' ')
Patrick Monnerat
committed
ptr++;
for(store = os; *ptr && *ptr != ' ';)
Patrick Monnerat
committed
*store++ = *ptr++;
*store = '\0'; /* zero terminate */
/* Check for special servers here. */
if(strequal(os, "OS/400")) {
Patrick Monnerat
committed
/* Force OS400 name format 1. */
result = Curl_pp_sendf(&ftpc->pp, "%s", "SITE NAMEFMT 1");
free(os);
/* remember target server OS */
Curl_safefree(ftpc->server_os);
ftpc->server_os = os;
Patrick Monnerat
committed
state(conn, FTP_NAMEFMT);
break;
}
else {
/* Nothing special for the target server. */
/* remember target server OS */
Curl_safefree(ftpc->server_os);
ftpc->server_os = os;
}
Patrick Monnerat
committed
}
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 response 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;