aboutsummaryrefslogtreecommitdiffstats
path: root/lib/ftp.c
diff options
context:
space:
mode:
Diffstat (limited to 'lib/ftp.c')
-rw-r--r--lib/ftp.c317
1 files changed, 285 insertions, 32 deletions
diff --git a/lib/ftp.c b/lib/ftp.c
index f821d124..60d95172 100644
--- a/lib/ftp.c
+++ b/lib/ftp.c
@@ -71,6 +71,8 @@
#include "http.h" /* for HTTP proxy tunnel stuff */
#include "socks.h"
#include "ftp.h"
+#include "fileinfo.h"
+#include "ftplistparser.h"
#if defined(HAVE_KRB4) || defined(HAVE_GSSAPI)
#include "krb4.h"
@@ -144,6 +146,14 @@ static CURLcode ftp_doing(struct connectdata *conn,
bool *dophase_done);
static CURLcode ftp_setup_connection(struct connectdata * conn);
+static CURLcode init_wc_data(struct connectdata *conn);
+static CURLcode wc_statemach(struct connectdata *conn);
+
+static void wc_data_dtor(void *ptr);
+
+static CURLcode ftp_state_post_retr_size(struct connectdata *conn,
+ curl_off_t filesize);
+
/* easy-to-use macro: */
#define FTPSENDF(x,y,z) if((result = Curl_ftpsendf(x,y,z)) != CURLE_OK) \
return result
@@ -314,7 +324,7 @@ static CURLcode AllowServerConnect(struct connectdata *conn)
for(;;) {
timeout_ms = Curl_timeleft(conn, NULL, TRUE);
- if(timeout_ms <= 0) {
+ if(timeout_ms < 0) {
/* if a timeout was already reached, bail out */
failf(data, "Timeout while waiting for server connect");
return CURLE_OPERATION_TIMEDOUT;
@@ -820,7 +830,7 @@ static CURLcode ftp_state_use_port(struct connectdata *conn,
/* resolv ip/host to ip */
rc = Curl_resolv(conn, host, 0, &h);
if(rc == CURLRESOLV_PENDING)
- rc = Curl_wait_for_resolv(conn, &h);
+ (void)Curl_wait_for_resolv(conn, &h);
if(h) {
res = h->addr;
/* when we return from this function, we can forget about this entry
@@ -1378,7 +1388,7 @@ static CURLcode ftp_state_ul_setup(struct connectdata *conn,
infof(data, "File already completely uploaded\n");
/* no data to transfer */
- result=Curl_setup_transfer(conn, -1, -1, FALSE, NULL, -1, NULL);
+ Curl_setup_transfer(conn, -1, -1, FALSE, NULL, -1, NULL);
/* Set ->transfer so that we won't get any error in
* ftp_done() because we didn't transfer anything! */
@@ -1469,8 +1479,14 @@ static CURLcode ftp_state_quote(struct connectdata *conn,
if(ftp->transfer != FTPTRANSFER_BODY)
state(conn, FTP_STOP);
else {
- PPSENDF(&ftpc->pp, "SIZE %s", ftpc->file);
- state(conn, FTP_RETR_SIZE);
+ if(ftpc->known_filesize != -1) {
+ Curl_pgrsSetDownloadSize(data, ftpc->known_filesize);
+ result = ftp_state_post_retr_size(conn, ftpc->known_filesize);
+ }
+ else {
+ PPSENDF(&ftpc->pp, "SIZE %s", ftpc->file);
+ state(conn, FTP_RETR_SIZE);
+ }
}
break;
case FTP_STOR_PREQUOTE:
@@ -1630,8 +1646,9 @@ static CURLcode ftp_state_pasv_resp(struct connectdata *conn,
*/
rc = Curl_resolv(conn, conn->proxy.name, (int)conn->port, &addr);
if(rc == CURLRESOLV_PENDING)
- /* BLOCKING */
- rc = Curl_wait_for_resolv(conn, &addr);
+ /* BLOCKING, ignores the return code but 'addr' will be NULL in
+ case of failure */
+ (void)Curl_wait_for_resolv(conn, &addr);
connectport =
(unsigned short)conn->port; /* we connect to the proxy's port */
@@ -1647,7 +1664,7 @@ static CURLcode ftp_state_pasv_resp(struct connectdata *conn,
rc = Curl_resolv(conn, newhost, newport, &addr);
if(rc == CURLRESOLV_PENDING)
/* BLOCKING */
- rc = Curl_wait_for_resolv(conn, &addr);
+ (void)Curl_wait_for_resolv(conn, &addr);
connectport = newport; /* we connect to the remote port */
@@ -1973,7 +1990,7 @@ static CURLcode ftp_state_post_retr_size(struct connectdata *conn,
if(ftp->downloadsize == 0) {
/* no data to transfer */
- result = Curl_setup_transfer(conn, -1, -1, FALSE, NULL, -1, NULL);
+ 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()
@@ -2115,8 +2132,8 @@ static CURLcode ftp_state_stor_resp(struct connectdata *conn,
/* set the SO_SNDBUF for the secondary socket for those who need it */
Curl_sndbufset(conn->sock[SECONDARYSOCKET]);
- result = Curl_setup_transfer(conn, -1, -1, FALSE, NULL, /* no download */
- SECONDARYSOCKET, ftp->bytecountp);
+ 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 */
@@ -2229,11 +2246,8 @@ static CURLcode ftp_state_get_resp(struct connectdata *conn,
infof(data, "Getting file with size: %" FORMAT_OFF_T "\n", size);
/* FTP download: */
- result=Curl_setup_transfer(conn, SECONDARYSOCKET, size, FALSE,
- ftp->bytecountp,
- -1, NULL); /* no upload here */
- if(result)
- return result;
+ 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);
@@ -2410,7 +2424,7 @@ static CURLcode ftp_statemach_act(struct connectdata *conn)
set a valid level */
Curl_sec_request_prot(conn, data->set.str[STRING_KRB_LEVEL]);
- if(Curl_sec_login(conn) != 0)
+ if(Curl_sec_login(conn) != CURLE_OK)
infof(data, "Logging in with password in cleartext!\n");
else
infof(data, "Authentication successful\n");
@@ -2857,6 +2871,8 @@ static CURLcode ftp_init(struct connectdata *conn)
if(TRUE == isBadFtpString(ftp->passwd))
return CURLE_URL_MALFORMAT;
+ conn->proto.ftpc.known_filesize = -1; /* unknown size for now */
+
return CURLE_OK;
}
@@ -3020,6 +3036,13 @@ static CURLcode ftp_done(struct connectdata *conn, CURLcode status,
if(ftpc->prevpath)
free(ftpc->prevpath);
+ if(data->set.wildcardmatch) {
+ if(data->set.chunk_end && ftpc->file) {
+ data->set.chunk_end(data->wildcard.customptr);
+ }
+ ftpc->known_filesize = -1;
+ }
+
/* get the "raw" path */
path = curl_easy_unescape(data, path_to_use, 0, NULL);
if(!path) {
@@ -3053,10 +3076,6 @@ static CURLcode ftp_done(struct connectdata *conn, CURLcode status,
/* free the dir tree and file parts */
freedirs(ftpc);
-#if defined(HAVE_KRB4) || defined(HAVE_GSSAPI)
- Curl_sec_fflush_fd(conn, conn->sock[SECONDARYSOCKET]);
-#endif
-
/* shut down the socket to inform the server we're done */
#ifdef _WIN32_WCE
@@ -3180,6 +3199,8 @@ CURLcode ftp_sendquote(struct connectdata *conn, struct curl_slist *quote)
ssize_t nread;
int ftpcode;
CURLcode result;
+ struct ftp_conn *ftpc = &conn->proto.ftpc;
+ struct pingpong *pp = &ftpc->pp;
item = quote;
while(item) {
@@ -3199,6 +3220,8 @@ CURLcode ftp_sendquote(struct connectdata *conn, struct curl_slist *quote)
FTPSENDF(conn, "%s", cmd);
+ pp->response = Curl_tvnow(); /* timeout relative now */
+
result = Curl_GetFTPResponse(&nread, conn, &ftpcode);
if(result)
return result;
@@ -3285,7 +3308,6 @@ ftp_pasv_verbose(struct connectdata *conn,
static CURLcode ftp_range(struct connectdata *conn)
{
curl_off_t from, to;
- curl_off_t totalsize=-1;
char *ptr;
char *ptr2;
struct SessionHandle *data = conn->data;
@@ -3293,7 +3315,7 @@ static CURLcode ftp_range(struct connectdata *conn)
if(data->state.use_range && data->state.range) {
from=curlx_strtoofft(data->state.range, &ptr, 0);
- while(ptr && *ptr && (ISSPACE(*ptr) || (*ptr=='-')))
+ while(*ptr && (ISSPACE(*ptr) || (*ptr=='-')))
ptr++;
to=curlx_strtoofft(ptr, &ptr2, 0);
if(ptr == ptr2) {
@@ -3308,16 +3330,14 @@ static CURLcode ftp_range(struct connectdata *conn)
}
else if(from < 0) {
/* -Y */
- totalsize = -from;
data->req.maxdownload = -from;
data->state.resume_from = from;
DEBUGF(infof(conn->data, "FTP RANGE the last %" FORMAT_OFF_T " bytes\n",
- totalsize));
+ -from));
}
else {
/* X-Y */
- totalsize = to-from;
- data->req.maxdownload = totalsize+1; /* include last byte */
+ data->req.maxdownload = (to-from)+1; /* include last byte */
data->state.resume_from = from;
DEBUGF(infof(conn->data, "FTP RANGE from %" FORMAT_OFF_T
" getting %" FORMAT_OFF_T " bytes\n",
@@ -3393,7 +3413,7 @@ static CURLcode ftp_nextconnect(struct connectdata *conn)
if((result == CURLE_OK) && (ftp->transfer != FTPTRANSFER_BODY))
/* no data to transfer. FIX: it feels like a kludge to have this here
too! */
- result = Curl_setup_transfer(conn, -1, -1, FALSE, NULL, -1, NULL);
+ Curl_setup_transfer(conn, -1, -1, FALSE, NULL, -1, NULL);
/* end of transfer */
DEBUGF(infof(data, "DO-MORE phase ends with %d\n", (int)result));
@@ -3450,6 +3470,221 @@ CURLcode ftp_perform(struct connectdata *conn,
return result;
}
+static void wc_data_dtor(void *ptr)
+{
+ struct ftp_wc_tmpdata *tmp = ptr;
+ if(tmp)
+ Curl_ftp_parselist_data_free(&tmp->parser);
+ Curl_safefree(tmp);
+}
+
+static CURLcode init_wc_data(struct connectdata *conn)
+{
+ char *last_slash;
+ char *path = conn->data->state.path;
+ struct WildcardData *wildcard = &(conn->data->wildcard);
+ CURLcode ret = CURLE_OK;
+ struct ftp_wc_tmpdata *ftp_tmp;
+
+ last_slash = strrchr(conn->data->state.path, '/');
+ if(last_slash) {
+ last_slash++;
+ if(last_slash[0] == '\0') {
+ wildcard->state = CURLWC_CLEAN;
+ ret = ftp_parse_url_path(conn);
+ return ret;
+ }
+ else {
+ wildcard->pattern = strdup(last_slash);
+ if (!wildcard->pattern)
+ return CURLE_OUT_OF_MEMORY;
+ last_slash[0] = '\0'; /* cut file from path */
+ }
+ }
+ else { /* there is only 'wildcard pattern' or nothing */
+ if(path[0]) {
+ wildcard->pattern = strdup(path);
+ if (!wildcard->pattern)
+ return CURLE_OUT_OF_MEMORY;
+ path[0] = '\0';
+ }
+ else { /* only list */
+ wildcard->state = CURLWC_CLEAN;
+ ret = ftp_parse_url_path(conn);
+ return ret;
+ }
+ }
+
+ /* program continues only if URL is not ending with slash, allocate needed
+ resources for wildcard transfer */
+
+ /* allocate ftp protocol specific temporary wildcard data */
+ ftp_tmp = malloc(sizeof(struct ftp_wc_tmpdata));
+ if(!ftp_tmp) {
+ return CURLE_OUT_OF_MEMORY;
+ }
+
+ /* INITIALIZE parselist structure */
+ ftp_tmp->parser = Curl_ftp_parselist_data_alloc();
+ if(!ftp_tmp->parser) {
+ free(ftp_tmp);
+ return CURLE_OUT_OF_MEMORY;
+ }
+
+ wildcard->tmp = ftp_tmp; /* put it to the WildcardData tmp pointer */
+ wildcard->tmp_dtor = wc_data_dtor;
+
+ /* wildcard does not support NOCWD option (assert it?) */
+ if(conn->data->set.ftp_filemethod == FTPFILE_NOCWD)
+ conn->data->set.ftp_filemethod = FTPFILE_MULTICWD;
+
+ /* try to parse ftp url */
+ ret = ftp_parse_url_path(conn);
+ if(ret) {
+ return ret;
+ }
+
+ /* backup old write_function */
+ ftp_tmp->backup.write_function = conn->data->set.fwrite_func;
+ /* parsing write function (callback included directly from ftplistparser.c) */
+ conn->data->set.fwrite_func = Curl_ftp_parselist;
+ /* backup old file descriptor */
+ ftp_tmp->backup.file_descriptor = conn->data->set.out;
+ /* let the writefunc callback know what curl pointer is working with */
+ conn->data->set.out = conn;
+
+ wildcard->path = strdup(conn->data->state.path);
+ if(!wildcard->path) {
+ return CURLE_OUT_OF_MEMORY;
+ }
+
+ infof(conn->data, "Wildcard - Parsing started\n");
+ return CURLE_OK;
+}
+
+static CURLcode wc_statemach(struct connectdata *conn)
+{
+ struct ftp_conn *ftpc = &conn->proto.ftpc;
+ struct WildcardData *wildcard = &(conn->data->wildcard);
+ struct ftp_wc_tmpdata *ftp_tmp = wildcard->tmp;
+ char *tmp_path;
+ CURLcode ret = CURLE_OK;
+ long userresponse = 0;
+ switch (wildcard->state) {
+ case CURLWC_INIT:
+ ret = init_wc_data(conn);
+ if(wildcard->state == CURLWC_CLEAN)
+ /* only listing! */
+ break;
+ else
+ wildcard->state = ret ? CURLWC_ERROR : CURLWC_MATCHING;
+ break;
+
+ case CURLWC_MATCHING:
+ /* In this state is LIST response successfully parsed, so lets restore
+ previous WRITEFUNCTION callback and WRITEDATA pointer */
+ ftp_tmp = wildcard->tmp;
+ conn->data->set.fwrite_func = ftp_tmp->backup.write_function;
+ conn->data->set.out = ftp_tmp->backup.file_descriptor;
+ wildcard->state = CURLWC_DOWNLOADING;
+
+ if(Curl_ftp_parselist_geterror(ftp_tmp->parser)) {
+ /* error found in LIST parsing */
+ wildcard->state = CURLWC_CLEAN;
+ return wc_statemach(conn);
+ }
+ else if(wildcard->filelist->size == 0) {
+ /* no corresponding file */
+ wildcard->state = CURLWC_CLEAN;
+ return CURLE_REMOTE_FILE_NOT_FOUND;
+ }
+ ret = wc_statemach(conn);
+ break;
+
+ case CURLWC_DOWNLOADING: {
+ /* filelist has at least one file, lets get first one */
+ struct curl_fileinfo *finfo = wildcard->filelist->head->ptr;
+ tmp_path = malloc(strlen(conn->data->state.path) +
+ strlen(finfo->filename) + 1);
+ if(!tmp_path) {
+ return CURLE_OUT_OF_MEMORY;
+ }
+
+ tmp_path[0] = 0;
+ /* make full path to matched file */
+ strcat(tmp_path, wildcard->path);
+ strcat(tmp_path, finfo->filename);
+ /* switch default "state.pathbuffer" and tmp_path, good to see
+ ftp_parse_url_path function to understand this trick */
+ if(conn->data->state.pathbuffer)
+ free(conn->data->state.pathbuffer);
+ conn->data->state.pathbuffer = tmp_path;
+ conn->data->state.path = tmp_path;
+
+ infof(conn->data, "Wildcard - START of \"%s\"\n", finfo->filename);
+ if(conn->data->set.chunk_bgn) {
+ userresponse = conn->data->set.chunk_bgn(
+ finfo, wildcard->customptr, (int)wildcard->filelist->size);
+ switch(userresponse) {
+ case CURL_CHUNK_BGN_FUNC_SKIP:
+ infof(conn->data, "Wildcard - \"%s\" skipped by user\n",
+ finfo->filename);
+ wildcard->state = CURLWC_SKIP;
+ return wc_statemach(conn);
+ case CURL_CHUNK_BGN_FUNC_FAIL:
+ return CURLE_CHUNK_FAILED;
+ }
+ }
+
+ if(finfo->filetype != CURLFILETYPE_FILE) {
+ wildcard->state = CURLWC_SKIP;
+ return wc_statemach(conn);
+ }
+
+ if(finfo->flags & CURLFINFOFLAG_KNOWN_SIZE)
+ ftpc->known_filesize = finfo->size;
+
+ ret = ftp_parse_url_path(conn);
+ if(ret) {
+ return ret;
+ }
+
+ /* we don't need the Curl_fileinfo of first file anymore */
+ Curl_llist_remove(wildcard->filelist, wildcard->filelist->head, NULL);
+
+ if(wildcard->filelist->size == 0) { /* remains only one file to down. */
+ wildcard->state = CURLWC_CLEAN;
+ /* after that will be ftp_do called once again and no transfer
+ will be done because of CURLWC_CLEAN state */
+ return CURLE_OK;
+ }
+ } break;
+
+ case CURLWC_SKIP: {
+ if(conn->data->set.chunk_end)
+ conn->data->set.chunk_end(conn->data->wildcard.customptr);
+ Curl_llist_remove(wildcard->filelist, wildcard->filelist->head, NULL);
+ wildcard->state = (wildcard->filelist->size == 0) ?
+ CURLWC_CLEAN : CURLWC_DOWNLOADING;
+ ret = wc_statemach(conn);
+ } break;
+
+ case CURLWC_CLEAN:
+ ret = CURLE_OK;
+ if(ftp_tmp) {
+ ret = Curl_ftp_parselist_geterror(ftp_tmp->parser);
+ }
+ wildcard->state = ret ? CURLWC_ERROR : CURLWC_DONE;
+ break;
+
+ case CURLWC_DONE:
+ case CURLWC_ERROR:
+ break;
+ }
+
+ return ret;
+}
+
/***********************************************************************
*
* ftp_do()
@@ -3476,9 +3711,21 @@ static CURLcode ftp_do(struct connectdata *conn, bool *done)
if(retcode)
return retcode;
- retcode = ftp_parse_url_path(conn);
- if(retcode)
- return retcode;
+ if(conn->data->set.wildcardmatch) {
+ retcode = wc_statemach(conn);
+ if(conn->data->wildcard.state == CURLWC_SKIP ||
+ conn->data->wildcard.state == CURLWC_DONE) {
+ /* do not call ftp_regular_transfer */
+ return CURLE_OK;
+ }
+ if(retcode) /* error, loop or skipping the file */
+ return retcode;
+ }
+ else { /* no wildcard FSM needed */
+ retcode = ftp_parse_url_path(conn);
+ if(retcode)
+ return retcode;
+ }
retcode = ftp_regular_transfer(conn, done);
@@ -3613,6 +3860,10 @@ static CURLcode ftp_disconnect(struct connectdata *conn)
Curl_pp_disconnect(pp);
+#if defined(HAVE_KRB4) || defined(HAVE_GSSAPI)
+ Curl_sec_end(conn);
+#endif
+
return CURLE_OK;
}
@@ -3820,7 +4071,7 @@ static CURLcode ftp_dophase_done(struct connectdata *conn,
if(ftp->transfer != FTPTRANSFER_BODY)
/* no data to transfer */
- result=Curl_setup_transfer(conn, -1, -1, FALSE, NULL, -1, NULL);
+ Curl_setup_transfer(conn, -1, -1, FALSE, NULL, -1, NULL);
else if(!connected)
/* since we didn't connect now, we want do_more to get called */
conn->bits.do_more = TRUE;
@@ -3927,6 +4178,7 @@ static CURLcode ftp_setup_connection(struct connectdata * conn)
}
data->state.path++; /* don't include the initial slash */
+ data->state.slash_removed = TRUE; /* we've skipped the slash */
/* FTP URLs support an extension like ";type=<typecode>" that
* we'll try to get now! */
@@ -3938,6 +4190,7 @@ static CURLcode ftp_setup_connection(struct connectdata * conn)
if(type) {
*type = 0; /* it was in the middle of the hostname */
command = Curl_raw_toupper(type[6]);
+ conn->bits.type_set = TRUE;
switch (command) {
case 'A': /* ASCII mode */