diff --git a/CHANGES b/CHANGES index 2379be12a..374d103d1 100644 --- a/CHANGES +++ b/CHANGES @@ -6,6 +6,20 @@ Changelog +Daniel Stenberg (1 Jan 2010) +- Ingmar Runge enhanced libcurl's FTP engine to support the PRET command. This + command is a special "hack" used by the drftpd server, but even though it is + a custom extension I've deemed it fine to add to libcurl since this server + seems to survive and people keep using it and want libcurl to support + it. The new libcurl option is named CURLOPT_FTP_USE_PRET, and it is also + usable from the curl tool with --ftp-pret. Using this option on a server + that doesn't support this command will make libcurl fail. + + I added test cases 1107 and 1108 to verify the functionality. + + The PRET command is documented at + http://www.drftpd.org/index.php/Distributed_PASV + Yang Tse (30 Dec 2009) - Steven M. Schweda improved VMS build system, and Craig A. Berry helped with the patch and testing. diff --git a/RELEASE-NOTES b/RELEASE-NOTES index 63a0a5f73..2f26f2a94 100644 --- a/RELEASE-NOTES +++ b/RELEASE-NOTES @@ -1,8 +1,8 @@ Curl and libcurl 7.20.0 Public curl releases: 114 - Command line options: 134 - curl_easy_setopt() options: 165 + Command line options: 135 + curl_easy_setopt() options: 166 Public functions in libcurl: 58 Known libcurl bindings: 39 Contributors: 761 @@ -16,6 +16,7 @@ This release includes the following changes: o added support for IMAP, POP3 and SMTP transfers o added --mail-from and --mail-rcpt for SMTP o VMS build system enhancements + o added support for the PRET ftp command This release includes the following bugfixes: @@ -50,6 +51,6 @@ advice from friends like these: Marco Maggi, Camille Moncelier, Claes Jakobsson, Kevin Baughman, Marc Kleine-Budde, Jad Chamcham, Bjorn Augustsson, David Byron, Markus Koetter, Chad Monroe, Martin Storsjo, Siegfried Gyuricsko, - Jon Nelson, Julien Chaffraix, Renato Botelho, Peter Pentchev + Jon Nelson, Julien Chaffraix, Renato Botelho, Peter Pentchev, Ingmar Runge Thanks! (and sorry if I forgot to mention someone) diff --git a/docs/curl.1 b/docs/curl.1 index 991f2ee1f..27f81f276 100644 --- a/docs/curl.1 +++ b/docs/curl.1 @@ -461,6 +461,11 @@ will re-use the same IP address it already uses for the control connection. (Added in 7.14.2) This option has no effect if PORT, EPRT or EPSV is used instead of PASV. +.IP "--ftp-pret" +(FTP) Tell curl to send a PRET command before PASV (and EPSV). Certain +FTP servers, mainly drftpd, require this non-standard command for +directory listings as well as up and downloads in PASV mode. +(Added in 7.20.x) .IP "--ftp-ssl" (FTP) Try to use SSL/TLS for the FTP connection. Reverts to a non-secure connection if the server doesn't support SSL/TLS. See also diff --git a/docs/libcurl/curl_easy_setopt.3 b/docs/libcurl/curl_easy_setopt.3 index 697d49b0e..7377ef3ee 100644 --- a/docs/libcurl/curl_easy_setopt.3 +++ b/docs/libcurl/curl_easy_setopt.3 @@ -1160,6 +1160,12 @@ means that it will first attempt to use EPSV before using PASV, but if you pass zero to this option, it will not try using EPSV, only plain PASV. If the server is an IPv6 host, this option will have no effect as of 7.12.3. +.IP CURLOPT_FTP_USE_PRET +Pass a long. If the value is 1, it tells curl to send a PRET command +before PASV (and EPSV). Certain FTP servers, mainly drftpd, require this +non-standard command for directory listings as well as up and downloads in +PASV mode. Has no effect when using the active FTP transfers mode. +(Added in 7.20.x) .IP CURLOPT_FTP_CREATE_MISSING_DIRS Pass a long. If the value is 1, curl will attempt to create any remote directory that it fails to CWD into. CWD is the command that changes working diff --git a/docs/libcurl/libcurl-errors.3 b/docs/libcurl/libcurl-errors.3 index 6a3d74ac6..7447996f4 100644 --- a/docs/libcurl/libcurl-errors.3 +++ b/docs/libcurl/libcurl-errors.3 @@ -70,6 +70,10 @@ either a PASV or a EPSV command. The server is flawed. .IP "CURLE_FTP_WEIRD_227_FORMAT (14)" FTP servers return a 227-line as a response to a PASV command. If libcurl fails to parse that line, this return code is passed back. +.IP "CURLE_FTP_PRET_FAILED (84)" +The FTP server does not understand the PRET command at all or does not +support the given argument. Be careful when using \fICURLOPT_CUSTOMREQUEST\fP, +a custom LIST command will be sent with PRET CMD before PASV as well. .IP "CURLE_FTP_CANT_GET_HOST (15)" An internal failure to lookup the host used for the new connection. .IP "CURLE_FTP_COULDNT_SET_TYPE (17)" diff --git a/include/curl/curl.h b/include/curl/curl.h index e765a4d2c..f689598fa 100644 --- a/include/curl/curl.h +++ b/include/curl/curl.h @@ -413,6 +413,7 @@ typedef enum { wrong format (Added in 7.19.0) */ CURLE_SSL_ISSUER_ERROR, /* 83 - Issuer check failed. (Added in 7.19.0) */ + CURLE_FTP_PRET_FAILED, /* 84 - a PRET command failed */ CURL_LAST /* never use! */ } CURLcode; @@ -1291,6 +1292,9 @@ typedef enum { /* set the SMTP mail receiver(s) */ CINIT(MAIL_RCPT, OBJECTPOINT, 187), + /* FTP: send PRET before PASV */ + CINIT(FTP_USE_PRET, LONG, 188), + CURLOPT_LASTENTRY /* the last unused */ } CURLoption; diff --git a/lib/ftp.c b/lib/ftp.c index c5be2361f..64e29ceab 100644 --- a/lib/ftp.c +++ b/lib/ftp.c @@ -565,6 +565,7 @@ static void state(struct connectdata *conn, "REST", "RETR_REST", "PORT", + "PRET", "PASV", "LIST", "RETR", @@ -1090,7 +1091,25 @@ static CURLcode ftp_state_post_rest(struct connectdata *conn) } else { /* We have chosen (this is default) to use the PASV (or similar) command */ - result = ftp_state_use_pasv(conn); + if(data->set.ftp_use_pret) { + /* The user has requested that we send a PRET command + to prepare the server for the upcoming PASV */ + if(!conn->proto.ftpc.file) { + PPSENDF(&conn->proto.ftpc.pp, "PRET %s", data->set.str[STRING_CUSTOMREQUEST]? + data->set.str[STRING_CUSTOMREQUEST]: + (data->set.ftp_list_only?"NLST":"LIST")); + } + else if(data->set.upload) { + PPSENDF(&conn->proto.ftpc.pp, "PRET STOR %s", conn->proto.ftpc.file); + } + else { + PPSENDF(&conn->proto.ftpc.pp, "PRET RETR %s", conn->proto.ftpc.file); + } + state(conn, FTP_PRET); + } + else { + result = ftp_state_use_pasv(conn); + } } return result; } @@ -2710,6 +2729,15 @@ static CURLcode ftp_statemach_act(struct connectdata *conn) result = ftp_state_rest_resp(conn, ftpcode, ftpc->state); break; + case FTP_PRET: + if(ftpcode != 200) { + /* there only is this one standard OK return code. */ + failf(data, "PRET command not accepted: %03d", ftpcode); + return CURLE_FTP_PRET_FAILED; + } + result = ftp_state_use_pasv(conn); + break; + case FTP_PASV: result = ftp_state_pasv_resp(conn, ftpcode); break; diff --git a/lib/ftp.h b/lib/ftp.h index 93581bfc9..11dd65bae 100644 --- a/lib/ftp.h +++ b/lib/ftp.h @@ -79,6 +79,7 @@ typedef enum { FTP_REST, /* when used to check if the server supports it in head-like */ FTP_RETR_REST, /* when asking for "resume" in for RETR */ FTP_PORT, /* generic state for PORT, LPRT and EPRT, check count1 */ + FTP_PRET, /* generic state for PRET RETR, PRET STOR and PRET LIST/NLST */ FTP_PASV, /* generic state for PASV and EPSV, check count1 */ FTP_LIST, /* generic state for LIST, NLST or a custom list command */ FTP_RETR, diff --git a/lib/strerror.c b/lib/strerror.c index 75067a7d0..da223f58d 100644 --- a/lib/strerror.c +++ b/lib/strerror.c @@ -81,6 +81,9 @@ curl_easy_strerror(CURLcode error) case CURLE_REMOTE_ACCESS_DENIED: return "Access denied to remote resource"; + case CURLE_FTP_PRET_FAILED: + return "FTP: The server did not accept the PRET command."; + case CURLE_FTP_WEIRD_PASS_REPLY: return "FTP: unknown PASS reply"; diff --git a/lib/url.c b/lib/url.c index 3f2f1cd72..19283c290 100644 --- a/lib/url.c +++ b/lib/url.c @@ -701,6 +701,7 @@ CURLcode Curl_init_userdefined(struct UserDefined *set) set->httpreq = HTTPREQ_GET; /* Default HTTP request */ set->ftp_use_epsv = TRUE; /* FTP defaults to EPSV operations */ set->ftp_use_eprt = TRUE; /* FTP defaults to EPRT operations */ + set->ftp_use_pret = FALSE; /* mainly useful for drftpd servers */ set->ftp_filemethod = FTPFILE_MULTICWD; set->dns_cache_timeout = 60; /* Timeout every 60 seconds by default */ @@ -1563,6 +1564,10 @@ CURLcode Curl_setopt(struct SessionHandle *data, CURLoption option, data->set.ftp_use_epsv = (bool)(0 != va_arg(param, long)); break; + case CURLOPT_FTP_USE_PRET: + data->set.ftp_use_pret = (bool)(0 != va_arg(param, long)); + break; + case CURLOPT_FTP_SSL_CCC: data->set.ftp_ccc = (curl_ftpccc)va_arg(param, long); break; diff --git a/lib/urldata.h b/lib/urldata.h index 4bcb5bf6f..d7b8be6af 100644 --- a/lib/urldata.h +++ b/lib/urldata.h @@ -1308,6 +1308,7 @@ struct UserDefined { bool reuse_fresh; /* do not re-use an existing connection */ bool ftp_use_epsv; /* if EPSV is to be attempted or not */ bool ftp_use_eprt; /* if EPRT is to be attempted or not */ + bool ftp_use_pret; /* if PRET is to be used before PASV or not */ curl_usessl ftp_ssl; /* if AUTH TLS is to be attempted etc, for FTP or IMAP or POP3 or others! */ diff --git a/src/main.c b/src/main.c index 09d4c827b..cd875e723 100644 --- a/src/main.c +++ b/src/main.c @@ -477,6 +477,7 @@ struct Configurable { bool resume_from_current; bool disable_epsv; bool disable_eprt; + bool ftp_pret; curl_off_t resume_from; char *postfields; curl_off_t postfieldsize; @@ -794,6 +795,7 @@ static void help(void) " --ftp-pasv Use PASV/EPSV instead of PORT (F)", " -P/--ftp-port
Use PORT with address instead of PASV (F)", " --ftp-skip-pasv-ip Skip the IP address for PASV (F)\n" + " --ftp-pret Send PRET before PASV (for drftpd) (F)", " --ftp-ssl Try SSL/TLS for ftp transfer (F)", " --ftp-ssl-ccc Send CCC after authenticating (F)", " --ftp-ssl-ccc-mode [active/passive] Set CCC mode (F)", @@ -1746,6 +1748,7 @@ static ParameterError getparameter(char *flag, /* f or -long-flag */ {"$9", "tftp-blksize", TRUE}, {"$A", "mail-from", TRUE}, {"$B", "mail-rcpt", TRUE}, + {"$C", "ftp-pret", FALSE}, {"0", "http1.0", FALSE}, {"1", "tlsv1", FALSE}, {"2", "sslv2", FALSE}, @@ -2284,6 +2287,9 @@ static ParameterError getparameter(char *flag, /* f or -long-flag */ if(err) return err; break; + case 'C': /* --ftp-pret */ + config->ftp_pret = toggle; + break; } break; case '#': /* --progress-bar */ @@ -5032,6 +5038,10 @@ operate(struct Configurable *config, int argc, argv_item_t argv[]) if(config->mail_rcpt) my_setopt_str(curl, CURLOPT_MAIL_RCPT, config->mail_rcpt); + /* curl 7.20.x */ + if(config->ftp_pret) + my_setopt(curl, CURLOPT_FTP_USE_PRET, TRUE); + retry_numretries = config->req_retry; retrystart = cutil_tvnow(); diff --git a/tests/data/Makefile.am b/tests/data/Makefile.am index b2c4cc9b5..712e79c7d 100644 --- a/tests/data/Makefile.am +++ b/tests/data/Makefile.am @@ -63,7 +63,8 @@ EXTRA_DIST = test1 test108 test117 test127 test20 test27 test34 test46 \ test1089 test1090 test1091 test1092 test1093 test1094 test1095 test1096 \ test1097 test560 test561 test1098 test1099 test562 test563 test1100 \ test564 test1101 test1102 test1103 test1104 test299 test310 test311 \ - test312 test1105 test565 test800 test1106 test801 test566 test802 test803 + test312 test1105 test565 test800 test1106 test801 test566 test802 test803 \ + test1107 test1108 filecheck: @mkdir test-place; \ diff --git a/tests/data/test1107 b/tests/data/test1107 new file mode 100644 index 000000000..6adc6360c --- /dev/null +++ b/tests/data/test1107 @@ -0,0 +1,53 @@ + + + +FTP +PASV +RETR +PRET + + +# Server-side + + +data + to + see +that FTP +works + so does it? + + +REPLY PRET 200 fine + + + +# Client-side + + +ftp + + +FTP RETR PASV with PRET + + +ftp://%HOSTIP:%FTPPORT/1107 --ftp-pret + + + + +# Verify data after the test has been "shot" + + +USER anonymous +PASS ftp@example.com +PWD +PRET RETR 1107 +EPSV +TYPE I +SIZE 1107 +RETR 1107 +QUIT + + + diff --git a/tests/data/test1108 b/tests/data/test1108 new file mode 100644 index 000000000..7b779e11f --- /dev/null +++ b/tests/data/test1108 @@ -0,0 +1,45 @@ + + + +FTP +PASV +RETR +PRET + + +# Server-side + + + +REPLY PRET 550 unkown command + + + +# Client-side + + +ftp + + +FTP RETR PASV with PRET not supported + + +ftp://%HOSTIP:%FTPPORT/1108 --ftp-pret + + + + +# Verify data after the test has been "shot" + + +USER anonymous +PASS ftp@example.com +PWD +PRET RETR 1108 + +# we expect that the server doesn't understand PRET + +84 + + +