diff options
Diffstat (limited to 'lib/ftp.c')
-rw-r--r-- | lib/ftp.c | 317 |
1 files changed, 285 insertions, 32 deletions
@@ -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 */ |