Newer
Older
}
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;
}
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 */
Daniel Stenberg
committed
result = Curl_ssl_connect(conn, SECONDARYSOCKET);
if(result)
return result;
Daniel Stenberg
committed
}
*(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);
result = Curl_Transfer(conn, -1, -1, FALSE, NULL, /* no download */
SECONDARYSOCKET, ftp->bytecountp);
state(conn, FTP_STOP);
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;
struct FTP *ftp = conn->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) &&
!data->set.ftp_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);
2090
2091
2092
2093
2094
2095
2096
2097
2098
2099
2100
2101
2102
2103
2104
2105
2106
2107
2108
2109
2110
2111
/* 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 */
if(!isdigit((int)*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
}
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");
Daniel Stenberg
committed
result = Curl_ssl_connect(conn, SECONDARYSOCKET);
if(result)
return result;
Daniel Stenberg
committed
}
if(size > conn->maxdownload && conn->maxdownload > 0)
size = conn->size = conn->maxdownload;
if(instate != FTP_LIST)
infof(data, "Getting file with size: %" FORMAT_OFF_T "\n", size);
/* FTP download: */
result=Curl_Transfer(conn, SECONDARYSOCKET, size, FALSE,
ftp->bytecountp,
-1, NULL); /* no upload here */
if(result)
return result;
state(conn, FTP_STOP);
}
else {
if((instate == FTP_LIST) && (ftpcode == 450)) {
/* simply no matching files in the dir listing */
ftp->no_transfer = TRUE; /* don't download anything */
state(conn, FTP_STOP); /* this phase is over */
}
else {
failf(data, "%s", buf+4);
return CURLE_FTP_COULDNT_RETR_FILE;
}
return result;
}
/* after USER, PASS and ACCT */
static CURLcode ftp_state_loggedin(struct connectdata *conn)
{
CURLcode result = CURLE_OK;
struct SessionHandle *data = conn->data;
infof(data, "We have successfully logged in\n");
#ifdef HAVE_KRB4
if(data->set.krb4) {
/* We are logged in, asked to use Kerberos. Set the requested
* protection level
*/
if(conn->sec_complete)
/* BLOCKING */
Curl_sec_set_protection_level(conn);
Daniel Stenberg
committed
/* 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;
}
2184
2185
2186
2187
2188
2189
2190
2191
2192
2193
2194
2195
2196
2197
2198
2199
2200
2201
2202
2203
2204
2205
2206
2207
2208
2209
2210
2211
2212
2213
2214
2215
2216
2217
2218
2219
2220
2221
2222
2223
2224
2225
2226
2227
2228
2229
2230
2231
2232
2233
2234
2235
2236
}
#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.
*/
NBFTPSENDF(conn, "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 = conn->proto.ftp;
(void)instate; /* no use for this yet */
if((ftpcode == 331) && (ftp->state == FTP_USER)) {
/* 331 Password required for ...
(the server requires to send the user's password too) */
NBFTPSENDF(conn, "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) {
if(data->set.ftp_account) {
NBFTPSENDF(conn, "ACCT %s", data->set.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) */
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 *ftp = conn->proto.ftp;
static const char * const ftpauth[] = {
"SSL", "TLS"
};
size_t nread;
if(ftp->sendleft) {
/* we have a piece of a command still left to send */
ssize_t written;
result = Curl_write(conn, sock, ftp->sendthis + ftp->sendsize -
ftp->sendleft, ftp->sendleft, &written);
if(result)
return result;
if(written != (ssize_t)ftp->sendleft) {
/* only a fraction was sent */
ftp->sendleft -= written;
Daniel Stenberg
committed
}
else {
free(ftp->sendthis);
ftp->sendthis=NULL;
ftp->sendleft = ftp->sendsize = 0;
ftp->response = Curl_tvnow();
}
return CURLE_OK;
}
/* we read a piece of response */
result = ftp_readresp(sock, conn, &ftpcode, &nread);
if(result)
return result;
Sterling Hughes
committed
if(ftpcode) {
/* we have now received a full FTP server response */
switch(ftp->state) {
case FTP_WAIT220:
if(ftpcode != 220) {
failf(data, "This doesn't seem like a nice ftp-server response");
return CURLE_FTP_WEIRD_SERVER_REPLY;
Daniel Stenberg
committed
}
Sterling Hughes
committed
/* We have received a 220 response fine, now we proceed. */
#ifdef HAVE_KRB4
if(data->set.krb4) {
/* 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.krb4_level);
Sterling Hughes
committed
if(Curl_sec_login(conn) != 0)
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 */
ftp->count3=0;
switch(data->set.ftpsslauth) {
case CURLFTPAUTH_DEFAULT:
case CURLFTPAUTH_SSL:
ftp->count2 = 1; /* add one to get next */
ftp->count1 = 0;
break;
case CURLFTPAUTH_TLS:
ftp->count2 = -1; /* subtract one to get next */
ftp->count1 = 1;
break;
default:
failf(data, "unsupported parameter to CURLOPT_FTPSSLAUTH: %d\n",
data->set.ftpsslauth);
return CURLE_FAILED_INIT; /* we don't know what to do */
Sterling Hughes
committed
}
NBFTPSENDF(conn, "AUTH %s", ftpauth[ftp->count1]);
state(conn, FTP_AUTH);
Daniel Stenberg
committed
}
else {
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(ftp->count3 < 1) {
ftp->count3++;
ftp->count1 += ftp->count2; /* get next attempt */
Daniel Stenberg
committed
result = Curl_nbftpsendf(conn, "AUTH %s", ftpauth[ftp->count1]);
/* remain in this same state */
}
Daniel Stenberg
committed
else
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, ftp->state);
break;
Daniel Stenberg
committed
case FTP_ACCT:
result = ftp_state_acct_resp(conn, ftpcode);
break;
Daniel Stenberg
committed
case FTP_PBSZ:
/* FIX: check response code */
Daniel Stenberg
committed
/* For TLS, the data connection can have one of two security levels.
Daniel Stenberg
committed
1) Clear (requested by 'PROT C')
2)Private (requested by 'PROT P')
*/
if(!conn->ssl[SECONDARYSOCKET].use) {
NBFTPSENDF(conn, "PROT %c", 'P');
state(conn, FTP_PROT);
}
else {
result = ftp_state_pwd(conn);
if(result)
return result;
}
Daniel Stenberg
committed
break;
Daniel Stenberg
committed
case FTP_PROT:
if(ftpcode/100 == 2)
/* We have enabled SSL for the data connection! */
conn->ssl[SECONDARYSOCKET].use = TRUE;
/* FTP servers typically responds with 500 if they decide to reject
our 'P' request */
else if(data->set.ftp_ssl> CURLFTPSSL_CONTROL)
/* we failed and bails out */
return CURLE_FTP_SSL_FAILED;
result = ftp_state_pwd(conn);
if(result)
return result;
break;
Daniel Stenberg
committed
case FTP_PWD:
if(ftpcode == 257) {
char *dir = (char *)malloc(nread+1);
char *store=dir;
char *ptr=&data->state.buffer[4]; /* start on the first letter */
Daniel Stenberg
committed
if(!dir)
return CURLE_OUT_OF_MEMORY;
/* Reply format is like
257<space>"<directory-name>"<space><commentary> and the RFC959
says
Daniel Stenberg
committed
2455
2456
2457
2458
2459
2460
2461
2462
2463
2464
2465
2466
2467
2468
2469
2470
2471
2472
2473
2474
2475
2476
2477
2478
2479
2480
2481
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++;
while(ptr && *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++;
}
ftp->entrypath =dir; /* remember this */
infof(data, "Entry path is '%s'\n", ftp->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! */
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) {
failf(conn->data, "QUOT command failed with %03d", ftpcode);
return CURLE_FTP_QUOTE_ERROR;
result = ftp_state_quote(conn, FALSE, ftp->state);
if(result)
return result;
Daniel Stenberg
committed
break;
Daniel Stenberg
committed
2507
2508
2509
2510
2511
2512
2513
2514
2515
2516
2517
2518
2519
2520
2521
2522
2523
2524
2525
2526
2527
2528
2529
2530
2531
2532
2533
2534
case FTP_CWD:
if(ftpcode/100 != 2) {
/* failure to CWD there */
if(conn->data->set.ftp_create_missing_dirs &&
ftp->count1 && !ftp->count2) {
/* try making it */
ftp->count2++; /* counter to prevent CWD-MKD loops */
NBFTPSENDF(conn, "MKD %s", ftp->dirs[ftp->count1 - 1]);
state(conn, FTP_MKD);
}
else
/* return failure */
return CURLE_FTP_ACCESS_DENIED;
}
else {
/* success */
ftp->count2=0;
if(++ftp->count1 <= ftp->dirdepth) {
/* send next CWD */
NBFTPSENDF(conn, "CWD %s", ftp->dirs[ftp->count1 - 1]);
}
else {
result = ftp_state_post_cwd(conn);
if(result)
return result;
}
}
break;
Daniel Stenberg
committed
case FTP_MKD:
if(ftpcode/100 != 2) {
/* failure to MKD the dir */
failf(data, "Failed to MKD dir: %03d", ftpcode);
return CURLE_FTP_ACCESS_DENIED;
}
state(conn, FTP_CWD);
/* send CWD */
NBFTPSENDF(conn, "CWD %s", ftp->dirs[ftp->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, ftp->state);
break;
Daniel Stenberg
committed
case FTP_SIZE:
case FTP_RETR_SIZE:
case FTP_STOR_SIZE:
result = ftp_state_size_resp(conn, ftpcode, ftp->state);
break;
case FTP_REST:
case FTP_RETR_REST:
result = ftp_state_rest_resp(conn, ftpcode, ftp->state);
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, ftp->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;
}
2598
2599
2600
2601
2602
2603
2604
2605
2606
2607
2608
2609
2610
2611
2612
2613
2614
2615
2616
2617
2618
2619
2620
2621
/* Returns timeout in ms. 0 or negative number means the timeout has already
triggered */
static long ftp_state_timeout(struct connectdata *conn)
{
struct SessionHandle *data=conn->data;
struct FTP *ftp = conn->proto.ftp;
long timeout_ms=360000; /* in milliseconds */
if(data->set.ftp_response_timeout )
/* if CURLOPT_FTP_RESPONSE_TIMEOUT is set, use that to determine remaining
time. Also, use ftp->response because FTP_RESPONSE_TIMEOUT is supposed
to govern the response for any given ftp response, not for the time
from connect to the given ftp response. */
timeout_ms = data->set.ftp_response_timeout*1000 - /* timeout time */
Curl_tvdiff(Curl_tvnow(), ftp->response); /* spent time */
else if(data->set.timeout)
/* if timeout is requested, find out how much remaining time we have */
timeout_ms = data->set.timeout*1000 - /* timeout time */
Curl_tvdiff(Curl_tvnow(), conn->now); /* spent time */
else
/* Without a requested timeout, we only wait 'response_time' seconds for
the full response to arrive before we bail out */
timeout_ms = ftp->response_time*1000 -
Curl_tvdiff(Curl_tvnow(), ftp->response); /* spent time */
return timeout_ms;
}
Daniel Stenberg
committed
/* called repeatedly until done from multi.c */
CURLcode Curl_ftp_multi_statemach(struct connectdata *conn,
bool *done)
{
curl_socket_t sock = conn->sock[FIRSTSOCKET];
int rc;
struct SessionHandle *data=conn->data;
struct FTP *ftp = conn->proto.ftp;
CURLcode result = CURLE_OK;
long timeout_ms = ftp_state_timeout(conn);
*done = FALSE; /* default to not done yet */
if(timeout_ms <= 0) {
failf(data, "FTP response timeout");
return CURLE_OPERATION_TIMEDOUT;
}
rc = Curl_select(ftp->sendleft?CURL_SOCKET_BAD:sock, /* reading */
ftp->sendleft?sock:CURL_SOCKET_BAD, /* writing */
0);
if(rc == -1) {
failf(data, "select error");
return CURLE_OUT_OF_MEMORY;
Daniel Stenberg
committed
}
else if(rc != 0) {
result = ftp_statemach_act(conn);
*done = (ftp->state == FTP_STOP);
Daniel Stenberg
committed
}
/* if rc == 0, then select() timed out */
return result;
}
static CURLcode ftp_easy_statemach(struct connectdata *conn)
{
curl_socket_t sock = conn->sock[FIRSTSOCKET];
int rc;
struct SessionHandle *data=conn->data;
struct FTP *ftp = conn->proto.ftp;
CURLcode result = CURLE_OK;
Sterling Hughes
committed
while(ftp->state != FTP_STOP) {
long timeout_ms = ftp_state_timeout(conn);
if(timeout_ms <=0 ) {
failf(data, "FTP response timeout");
return CURLE_OPERATION_TIMEDOUT; /* already too little time */
}
rc = Curl_select(ftp->sendleft?CURL_SOCKET_BAD:sock, /* reading */
ftp->sendleft?sock:CURL_SOCKET_BAD, /* writing */
timeout_ms);
Daniel Stenberg
committed
if(rc == -1) {
failf(data, "select error");
return CURLE_OUT_OF_MEMORY;
Daniel Stenberg
committed
}
else if(rc == 0) {
result = CURLE_OPERATION_TIMEDOUT;
break;
}
else {
result = ftp_statemach_act(conn);
if(result)
break;
Daniel Stenberg
committed
}
return result;
}
Daniel Stenberg
committed
/*
* Curl_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.
*/
CURLcode Curl_ftp_connect(struct connectdata *conn,
bool *done) /* see description above */
{
struct FTP *ftp;
CURLcode result;
*done = FALSE; /* default to not done yet */
Daniel Stenberg
committed
ftp = (struct FTP *)calloc(sizeof(struct FTP), 1);
if(!ftp)
return CURLE_OUT_OF_MEMORY;
conn->proto.ftp = ftp;
/* We always support persistant connections on ftp */
conn->bits.close = FALSE;
/* get some initial data into the ftp struct */
ftp->bytecountp = &conn->bytecount;
/* no need to duplicate them, this connectdata struct won't change */
ftp->user = conn->user;
ftp->passwd = conn->passwd;
if (isBadFtpString(ftp->user) || isBadFtpString(ftp->passwd))
return CURLE_URL_MALFORMAT;
ftp->response_time = 3600; /* set default response time-out */
#ifndef CURL_DISABLE_HTTP
if (conn->bits.tunnel_proxy) {
/* BLOCKING */
/* We want "seamless" FTP operations through HTTP proxy tunnel */
result = Curl_ConnectHTTPProxyTunnel(conn, FIRSTSOCKET,
conn->host.name, conn->remote_port);
Daniel Stenberg
committed
}
#endif /* CURL_DISABLE_HTTP */
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;
}
/* When we connect, we start in the state where we await the 220
response */
Daniel Stenberg
committed
ftp_respinit(conn); /* init the response reader stuff */
state(conn, FTP_WAIT220);
ftp->response = Curl_tvnow(); /* start response time-out now! */
if(conn->data->state.used_interface == Curl_if_multi)
result = Curl_ftp_multi_statemach(conn, done);
else {
result = ftp_easy_statemach(conn);
if(!result)
*done = TRUE;
}
return result;
Daniel Stenberg
committed
}
/***********************************************************************
*
* Curl_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.
CURLcode Curl_ftp_done(struct connectdata *conn, CURLcode status)
Daniel Stenberg
committed
{
struct SessionHandle *data = conn->data;
Daniel Stenberg
committed
struct FTP *ftp = conn->proto.ftp;
ssize_t nread;
int ftpcode;
CURLcode result=CURLE_OK;
bool was_ctl_valid = ftp->ctl_valid;
size_t flen;
size_t dlen;
char *path;
/* now store a copy of the directory we are in */
if(ftp->prevpath)
free(ftp->prevpath);
path = curl_unescape(conn->path, 0); /* get the "raw" path */
if(!path)
return CURLE_OUT_OF_MEMORY;
flen = ftp->file?strlen(ftp->file):0; /* file is "raw" already */
dlen = strlen(path)-flen;
if(dlen) {
ftp->prevpath = path;
if(flen)
/* if 'path' is not the whole string */
ftp->prevpath[dlen]=0; /* terminate */
infof(data, "Remembering we are in dir %s\n", ftp->prevpath);
}
else {
ftp->prevpath = NULL; /* no path */
free(path);
}
/* free the dir tree and file parts */
freedirs(ftp);
ftp->ctl_valid = FALSE;
if(data->set.upload) {
if((-1 != data->set.infilesize) &&
(data->set.infilesize != *ftp->bytecountp) &&
!data->set.crlf &&
2825
2826
2827
2828
2829
2830
2831
2832
2833
2834
2835
2836
2837
2838
2839
2840
2841
2842
2843
2844
2845
2846
2847
2848
2849
2850
2851
2852
failf(data, "Uploaded unaligned file size (%" FORMAT_OFF_T
" out of %" FORMAT_OFF_T " bytes)",
*ftp->bytecountp, data->set.infilesize);
conn->bits.close = TRUE; /* close this connection since we don't
know what state this error leaves us in */
return CURLE_PARTIAL_FILE;
}
}
else {
if((-1 != conn->size) && (conn->size != *ftp->bytecountp) &&
(conn->maxdownload != *ftp->bytecountp)) {
failf(data, "Received only partial file: %" FORMAT_OFF_T " bytes",
*ftp->bytecountp);
conn->bits.close = TRUE; /* close this connection since we don't
know what state this error leaves us in */
return CURLE_PARTIAL_FILE;
}
else if(!ftp->dont_check &&
!*ftp->bytecountp &&
(conn->size>0)) {
/* We consider this an error, but there's no true FTP error received
why we need to continue to "read out" the server response too.
We don't want to leave a "waiting" server reply if we'll get told
to make a second request on this same connection! */
failf(data, "No data was received!");
result = CURLE_FTP_COULDNT_RETR_FILE;
}
}
switch(status) {
case CURLE_BAD_DOWNLOAD_RESUME:
case CURLE_FTP_WEIRD_PASV_REPLY:
case CURLE_FTP_PORT_FAILED:
case CURLE_FTP_COULDNT_SET_BINARY:
case CURLE_FTP_COULDNT_RETR_FILE:
case CURLE_FTP_ACCESS_DENIED:
/* the connection stays alive fine even though this happened */
/* fall-through */
case CURLE_OK: /* doesn't affect the control connection's status */
ftp->ctl_valid = was_ctl_valid;
break;
default: /* by default, an error means the control connection is
wedged and should not be used anymore */
ftp->ctl_valid = FALSE;
break;
}
#ifdef HAVE_KRB4
Curl_sec_fflush_fd(conn, conn->sock[SECONDARYSOCKET]);
#endif
/* shut down the socket to inform the server we're done */
#ifdef _WIN32_WCE
shutdown(conn->sock[SECONDARYSOCKET],2); /* SD_BOTH */
#endif
sclose(conn->sock[SECONDARYSOCKET]);
conn->sock[SECONDARYSOCKET] = CURL_SOCKET_BAD;
if(!ftp->no_transfer && !status) {
/* Let's see what the server says about the transfer we just performed,
* but lower the timeout as sometimes this connection has died while the
* data has been transfered. This happens when doing through NATs etc that
* abandon old silent connections.
*/
ftp->response_time = 60; /* give it only a minute for now */
Daniel Stenberg
committed
result = Curl_GetFTPResponse(&nread, conn, &ftpcode);
ftp->response_time = 3600; /* set this back to one hour waits */
if(!nread && (CURLE_OPERATION_TIMEDOUT == result)) {
failf(data, "control connection looks dead");
return result;
if(!ftp->dont_check) {
/* 226 Transfer complete, 250 Requested file action okay, completed. */
if((ftpcode != 226) && (ftpcode != 250)) {
failf(data, "server did not report OK, got %d", ftpcode);
return CURLE_FTP_WRITE_ERROR;
}
/* clear these for next connection */
ftp->no_transfer = FALSE;
ftp->dont_check = FALSE;
if (!result && conn->sec_conn) { /* 3rd party transfer */
/* "done" with the secondary connection */
result = Curl_ftp_done(conn->sec_conn, status);
}
/* Send any post-transfer QUOTE strings? */
if(!status && !result && data->set.postquote)
result = ftp_sendquote(conn, data->set.postquote);
return result;
}
/***********************************************************************
*
* ftp_sendquote()
*
* Where a 'quote' means a list of custom commands to send to the server.
* The quote list is passed as an argument.
*/
static
CURLcode ftp_sendquote(struct connectdata *conn, struct curl_slist *quote)
{
struct curl_slist *item;
ssize_t nread;
int ftpcode;
CURLcode result;
item = quote;
while (item) {
if (item->data) {
FTPSENDF(conn, "%s", item->data);
result = Curl_GetFTPResponse(&nread, conn, &ftpcode);
if (result)
return result;
if (ftpcode >= 400) {
failf(conn->data, "QUOT string not accepted: %s", item->data);
return CURLE_FTP_QUOTE_ERROR;
item = item->next;
}
return CURLE_OK;
}
/***********************************************************************
*
* ftp_transfertype()
*
* Set transfer type. We only deal with ASCII or BINARY so this function
* sets one of them.
*/
static CURLcode ftp_transfertype(struct connectdata *conn,
bool ascii)
{
struct SessionHandle *data = conn->data;
int ftpcode;
ssize_t nread;
CURLcode result;
FTPSENDF(conn, "TYPE %s", ascii?"A":"I");
result = Curl_GetFTPResponse(&nread, conn, &ftpcode);
if(result)
return result;
if(ftpcode != 200) {
failf(data, "Couldn't set %s mode",
ascii?"ASCII":"binary");
return ascii? CURLE_FTP_COULDNT_SET_ASCII:CURLE_FTP_COULDNT_SET_BINARY;
}
return CURLE_OK;
}
/***************************************************************************
*
* ftp_pasv_verbose()