diff options
Diffstat (limited to 'lib/ftp.c')
-rw-r--r-- | lib/ftp.c | 282 |
1 files changed, 159 insertions, 123 deletions
@@ -387,8 +387,10 @@ int Curl_GetFTPResponse(char *buf, return nread; /* total amount of bytes read */ } -/* ftp_connect() should do everything that is to be considered a part - of the connection phase. */ +/* + * Curl_ftp_connect() should do everything that is to be considered a part of + * the connection phase. + */ CURLcode Curl_ftp_connect(struct connectdata *conn) { /* this is FTP and no proxy */ @@ -1321,7 +1323,8 @@ CURLcode ftp_use_port(struct connectdata *conn) */ static -CURLcode ftp_use_pasv(struct connectdata *conn) +CURLcode ftp_use_pasv(struct connectdata *conn, + bool *connected) { struct SessionHandle *data = conn->data; ssize_t nread; @@ -1473,7 +1476,14 @@ CURLcode ftp_use_pasv(struct connectdata *conn) addr, connectport, &conn->secondarysocket, - &conninfo); + &conninfo, + connected); + + /* + * When this is used from the multi interface, this might've returned with + * the 'connected' set to FALSE and thus we are now awaiting a non-blocking + * connect to connect and we should not be "hanging" here waiting. + */ if((CURLE_OK == result) && data->set.verbose) @@ -1494,127 +1504,24 @@ CURLcode ftp_use_pasv(struct connectdata *conn) return CURLE_OK; } -/*********************************************************************** - * - * ftp_perform() +/* + * Curl_ftp_nextconnect() * - * This is the actual DO function for FTP. Get a file/directory according to - * the options previously setup. + * This function shall be called when the second FTP connection has been + * established and is confirmed connected. */ -static -CURLcode ftp_perform(struct connectdata *conn) +CURLcode Curl_ftp_nextconnect(struct connectdata *conn) { - /* this is FTP and no proxy */ - ssize_t nread; - CURLcode result=CURLE_OK; struct SessionHandle *data=conn->data; char *buf = data->state.buffer; /* this is our buffer */ + CURLcode result; + ssize_t nread; + int ftpcode; /* for ftp status */ /* the ftp struct is already inited in ftp_connect() */ struct FTP *ftp = conn->proto.ftp; - long *bytecountp = ftp->bytecountp; - int ftpcode; /* for ftp status */ - - /* Send any QUOTE strings? */ - if(data->set.quote) { - if ((result = ftp_sendquote(conn, data->set.quote)) != CURLE_OK) - return result; - } - - /* This is a re-used connection. Since we change directory to where the - transfer is taking place, we must now get back to the original dir - where we ended up after login: */ - if (conn->bits.reuse) { - if ((result = ftp_cwd(conn, ftp->entrypath)) != CURLE_OK) - return result; - } - - /* change directory first! */ - if(ftp->dir && ftp->dir[0]) { - if ((result = ftp_cwd(conn, ftp->dir)) != CURLE_OK) - return result; - } - - /* Requested time of file? */ - if(data->set.get_filetime && ftp->file) { - result = ftp_getfiletime(conn, ftp->file); - if(result) - return result; - } - - /* If we have selected NOBODY and HEADER, it means that we only want file - information. Which in FTP can't be much more than the file size and - date. */ - if(data->set.no_body && data->set.include_header && ftp->file) { - /* The SIZE command is _not_ RFC 959 specified, and therefor many servers - may not support it! It is however the only way we have to get a file's - size! */ - ssize_t filesize; - - ftp->no_transfer = TRUE; /* this means no actual transfer is made */ - - /* Some servers return different sizes for different modes, and thus we - must set the proper type before we check the size */ - result = ftp_transfertype(conn, data->set.ftp_ascii); - if(result) - return result; - - /* failing to get size is not a serious error */ - result = ftp_getsize(conn, ftp->file, &filesize); - - if(CURLE_OK == result) { - sprintf(buf, "Content-Length: %d\r\n", filesize); - result = Curl_client_write(data, CLIENTWRITE_BOTH, buf, 0); - if(result) - return result; - } - - /* 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. */ - -#ifdef HAVE_STRFTIME - if(data->set.get_filetime && data->info.filetime) { - struct tm *tm; -#ifdef HAVE_LOCALTIME_R - struct tm buffer; - tm = (struct tm *)localtime_r(&data->info.filetime, &buffer); -#else - tm = localtime((unsigned long *)&data->info.filetime); -#endif - /* format: "Tue, 15 Nov 1994 12:45:26 GMT" */ - strftime(buf, BUFSIZE-1, "Last-Modified: %a, %d %b %Y %H:%M:%S %Z\r\n", - tm); - result = Curl_client_write(data, CLIENTWRITE_BOTH, buf, 0); - if(result) - return result; - } -#endif - - return CURLE_OK; - } - - if(data->set.no_body) - /* doesn't really transfer any data */ - ftp->no_transfer = TRUE; - /* Get us a second connection up and connected */ - else if(data->set.ftp_use_port) { - /* We have chosen to use the PORT command */ - result = ftp_use_port(conn); - if(CURLE_OK == result) - /* we have the data connection ready */ - infof(data, "Connected the data stream with PORT!\n"); - } - else { - /* We have chosen (this is default) to use the PASV command */ - result = ftp_use_pasv(conn); - if(CURLE_OK == result) - infof(data, "Connected the data stream with PASV!\n"); - } - - if(result) - return result; if(data->set.upload) { @@ -1993,6 +1900,128 @@ CURLcode ftp_perform(struct connectdata *conn) /*********************************************************************** * + * ftp_perform() + * + * This is the actual DO function for FTP. Get a file/directory according to + * the options previously setup. + */ + +static +CURLcode ftp_perform(struct connectdata *conn, + bool *connected) /* for the TCP connect status after + PASV / PORT */ +{ + /* this is FTP and no proxy */ + CURLcode result=CURLE_OK; + struct SessionHandle *data=conn->data; + char *buf = data->state.buffer; /* this is our buffer */ + + /* the ftp struct is already inited in ftp_connect() */ + struct FTP *ftp = conn->proto.ftp; + + /* Send any QUOTE strings? */ + if(data->set.quote) { + if ((result = ftp_sendquote(conn, data->set.quote)) != CURLE_OK) + return result; + } + + /* This is a re-used connection. Since we change directory to where the + transfer is taking place, we must now get back to the original dir + where we ended up after login: */ + if (conn->bits.reuse) { + if ((result = ftp_cwd(conn, ftp->entrypath)) != CURLE_OK) + return result; + } + + /* change directory first! */ + if(ftp->dir && ftp->dir[0]) { + if ((result = ftp_cwd(conn, ftp->dir)) != CURLE_OK) + return result; + } + + /* Requested time of file? */ + if(data->set.get_filetime && ftp->file) { + result = ftp_getfiletime(conn, ftp->file); + if(result) + return result; + } + + /* If we have selected NOBODY and HEADER, it means that we only want file + information. Which in FTP can't be much more than the file size and + date. */ + if(data->set.no_body && data->set.include_header && ftp->file) { + /* The SIZE command is _not_ RFC 959 specified, and therefor many servers + may not support it! It is however the only way we have to get a file's + size! */ + ssize_t filesize; + + ftp->no_transfer = TRUE; /* this means no actual transfer is made */ + + /* Some servers return different sizes for different modes, and thus we + must set the proper type before we check the size */ + result = ftp_transfertype(conn, data->set.ftp_ascii); + if(result) + return result; + + /* failing to get size is not a serious error */ + result = ftp_getsize(conn, ftp->file, &filesize); + + if(CURLE_OK == result) { + sprintf(buf, "Content-Length: %d\r\n", filesize); + result = Curl_client_write(data, CLIENTWRITE_BOTH, buf, 0); + if(result) + return result; + } + + /* 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. */ + +#ifdef HAVE_STRFTIME + if(data->set.get_filetime && data->info.filetime) { + struct tm *tm; +#ifdef HAVE_LOCALTIME_R + struct tm buffer; + tm = (struct tm *)localtime_r(&data->info.filetime, &buffer); +#else + tm = localtime((unsigned long *)&data->info.filetime); +#endif + /* format: "Tue, 15 Nov 1994 12:45:26 GMT" */ + strftime(buf, BUFSIZE-1, "Last-Modified: %a, %d %b %Y %H:%M:%S %Z\r\n", + tm); + result = Curl_client_write(data, CLIENTWRITE_BOTH, buf, 0); + if(result) + return result; + } +#endif + + return CURLE_OK; + } + + if(data->set.no_body) + /* doesn't really transfer any data */ + ftp->no_transfer = TRUE; + /* Get us a second connection up and connected */ + else if(data->set.ftp_use_port) { + /* We have chosen to use the PORT command */ + result = ftp_use_port(conn); + if(CURLE_OK == result) { + /* we have the data connection ready */ + infof(data, "Ordered connect of the data stream with PORT!\n"); + *connected = TRUE; /* mark us "still connected" */ + } + } + else { + /* We have chosen (this is default) to use the PASV command */ + result = ftp_use_pasv(conn, connected); + if(connected) + infof(data, "Connected the data stream with PASV!\n"); + } + + return result; +} + +/*********************************************************************** + * * Curl_ftp() * * This function is registered as 'curl_do' function. It decodes the path @@ -2003,6 +2032,7 @@ CURLcode ftp_perform(struct connectdata *conn) CURLcode Curl_ftp(struct connectdata *conn) { CURLcode retcode; + bool connected; struct SessionHandle *data = conn->data; struct FTP *ftp; @@ -2049,15 +2079,15 @@ CURLcode Curl_ftp(struct connectdata *conn) else ftp->dir = NULL; - retcode = ftp_perform(conn); - - /* clean up here, success or error doesn't matter */ - if(ftp->file) - free(ftp->file); - if(ftp->dir) - free(ftp->dir); + retcode = ftp_perform(conn, &connected); - ftp->file = ftp->dir = NULL; /* zero */ + if(CURLE_OK == retcode) { + if(connected) + retcode = Curl_ftp_nextconnect(conn); + else + /* since we didn't connect now, we want do_more to get called */ + conn->do_more = TRUE; + } return retcode; } @@ -2128,6 +2158,12 @@ CURLcode Curl_ftp_disconnect(struct connectdata *conn) free(ftp->entrypath); if(ftp->cache) free(ftp->cache); + if(ftp->file) + free(ftp->file); + if(ftp->dir) + free(ftp->dir); + + ftp->file = ftp->dir = NULL; /* zero */ } return CURLE_OK; } |