--- docs/libcurl/curl_easy_setopt.3 | 7 ++++ include/curl/curl.h | 3 + lib/http.c | 34 +++++++++++---------- lib/url.c | 17 ++++++++++ lib/urldata.h | 2 + tests/data/test208 | 2 - tests/data/test549 | 62 ++++++++++++++++++++++++++++++++++++++++ tests/data/test550 | 62 ++++++++++++++++++++++++++++++++++++++++ tests/data/test79 | 2 - tests/libtest/Makefile.am | 5 ++- tests/libtest/lib549.c | 48 ++++++++++++++++++++++++++++++ 11 files changed, 225 insertions(+), 19 deletions(-) --- docs/libcurl/curl_easy_setopt.3.orig +++ docs/libcurl/curl_easy_setopt.3 @@ -1064,6 +1064,13 @@ or similar. libcurl does not do a complete ASCII conversion when doing ASCII transfers over FTP. This is a known limitation/flaw that nobody has rectified. libcurl simply sets the mode to ascii and performs a standard transfer. +.IP CURLOPT_PROXY_TRANSFER_MODE +Pass a long. If the value is set to 1 (one), it tells libcurl to set the +transfer mode (binary or ASCII) for FTP transfers done via an HTTP proxy, by +appending ;type=a or ;type=i to the URL. Without this setting, or it being +set to 0 (zero, the default), \fICURLOPT_TRANSFERTEXT\fP has no effect when +doing FTP via a proxy. Beware that not all proxies support this feature. +(Added in 7.17.2) .IP CURLOPT_CRLF Convert Unix newlines to CRLF newlines on transfers. .IP CURLOPT_RANGE --- include/curl/curl.h.orig +++ include/curl/curl.h @@ -1159,6 +1159,9 @@ typedef enum { /* POST volatile input fields. */ CINIT(COPYPOSTFIELDS, OBJECTPOINT, 165), + /* set transfer mode (;type=) when doing FTP via an HTTP proxy */ + CINIT(PROXY_TRANSFER_MODE, LONG, 166), + CURLOPT_LASTENTRY /* the last unused */ } CURLoption; --- lib/http.c.orig +++ lib/http.c @@ -2094,22 +2094,24 @@ CURLcode Curl_http(struct connectdata *c } } ppath = data->change.url; - /* when doing ftp, append ;type= if not present */ - if (checkprefix("ftp://", ppath) || checkprefix("ftps://", ppath)) { - char *p = strstr(ppath, ";type="); - if (p && p[6] && p[7] == 0) { - switch (toupper((int)((unsigned char)p[6]))) { - case 'A': - case 'D': - case 'I': - break; - default: - p = NULL; - } - } - if (!p) - snprintf(ftp_typecode, sizeof(ftp_typecode), ";type=%c", - data->set.prefer_ascii ? 'a' : 'i'); + if(data->set.proxy_transfer_mode) { + /* when doing ftp, append ;type= if not present */ + if(checkprefix("ftp://", ppath) || checkprefix("ftps://", ppath)) { + char *p = strstr(ppath, ";type="); + if(p && p[6] && p[7] == 0) { + switch (toupper((int)((unsigned char)p[6]))) { + case 'A': + case 'D': + case 'I': + break; + default: + p = NULL; + } + } + if(!p) + snprintf(ftp_typecode, sizeof(ftp_typecode), ";type=%c", + data->set.prefer_ascii ? 'a' : 'i'); + } } } if(HTTPREQ_POST_FORM == httpreq) { --- lib/url.c.orig +++ lib/url.c @@ -2037,6 +2037,23 @@ CURLcode Curl_setopt(struct SessionHandl */ data->set.new_directory_perms = va_arg(param, long); break; + case CURLOPT_PROXY_TRANSFER_MODE: + /* + * set transfer mode (;type=) when doing FTP via an HTTP proxy + */ + switch (va_arg(param, long)) { + case 0: + data->set.proxy_transfer_mode = FALSE; + break; + case 1: + data->set.proxy_transfer_mode = TRUE; + break; + default: + /* reserve other values for future use */ + result = CURLE_FAILED_INIT; + break; + } + break; default: /* unknown tag and its companion, just ignore: */ --- lib/urldata.h.orig +++ lib/urldata.h @@ -1459,6 +1459,8 @@ struct UserDefined { content-encoded (chunked, compressed) */ long new_file_perms; /* Permissions to use when creating remote files */ long new_directory_perms; /* Permissions to use when creating remote dirs */ + bool proxy_transfer_mode; /* set transfer mode (;type=) when doing FTP + via an HTTP proxy */ char *str[STRING_LAST]; /* array of strings, pointing to allocated memory */ }; --- tests/data/test208.orig +++ tests/data/test208 @@ -44,7 +44,7 @@ the ^User-Agent:.* -PUT ftp://daniel:mysecret@host.com/we/want/208;type=i HTTP/1.1 +PUT ftp://daniel:mysecret@host.com/we/want/208 HTTP/1.1 Authorization: Basic ZGFuaWVsOm15c2VjcmV0 Host: host.com:21 Pragma: no-cache --- tests/data/test79.orig +++ tests/data/test79 @@ -45,7 +45,7 @@ ftp://%HOSTIP:%HTTPPORT/we/want/that/pag ^User-Agent:.* -GET ftp://%HOSTIP:%HTTPPORT/we/want/that/page/79;type=i HTTP/1.1 +GET ftp://%HOSTIP:%HTTPPORT/we/want/that/page/79 HTTP/1.1 Host: %HOSTIP:%HTTPPORT Pragma: no-cache Accept: */* --- /dev/null +++ tests/data/test549 @@ -0,0 +1,62 @@ + + + +FTP +CURLOPT_PROXY_TRANSFER_MODE +CURLOPT_PROXY + + + +# +# Server-side + + +HTTP/1.1 200 OK swsclose +Date: Thu, 09 Nov 2010 14:49:00 GMT +Server: test-server/fake +Last-Modified: Tue, 13 Jun 2000 12:10:00 GMT +ETag: "21025-dc7-39462498" +Accept-Ranges: bytes +Content-Length: 6 + +hello + + + +# +# Client-side + + +http + + +lib549 + + +FPT RETR over proxy with CURLOPT_PROXY_TRANSFER_MODE + +# first URL then proxy + +ftp://www.haxx.se/moo/549 http://%HOSTIP:%HTTPPORT + + + +# +# Verify data after the test has been "shot" + + +^User-Agent:.* + + +GET ftp://www.haxx.se/moo/549;type=i HTTP/1.1 +Host: www.haxx.se:21 +Pragma: no-cache +Accept: */* +Proxy-Connection: Keep-Alive + + + +hello + + + --- /dev/null +++ tests/data/test550 @@ -0,0 +1,62 @@ + + + +FTP +CURLOPT_PROXY_TRANSFER_MODE +CURLOPT_PROXY + + + +# +# Server-side + + +HTTP/1.1 200 OK swsclose +Date: Thu, 09 Nov 2010 14:49:00 GMT +Server: test-server/fake +Last-Modified: Tue, 13 Jun 2000 12:10:00 GMT +ETag: "21025-dc7-39462498" +Accept-Ranges: bytes +Content-Length: 6 + +hello + + + +# +# Client-side + + +http + + +lib549 + + +FPT RETR over proxy with CURLOPT_PROXY_TRANSFER_MODE and ascii transfer + +# first URL then proxy + +ftp://www.haxx.se/moo/550 http://%HOSTIP:%HTTPPORT ascii + + + +# +# Verify data after the test has been "shot" + + +^User-Agent:.* + + +GET ftp://www.haxx.se/moo/550;type=a HTTP/1.1 +Host: www.haxx.se:21 +Pragma: no-cache +Accept: */* +Proxy-Connection: Keep-Alive + + + +hello + + + --- tests/libtest/Makefile.am.orig +++ tests/libtest/Makefile.am @@ -48,7 +48,7 @@ noinst_PROGRAMS = lib500 lib501 lib502 l lib507 lib508 lib509 lib510 lib511 lib512 lib513 lib514 lib515 lib516 \ lib517 lib518 lib519 lib520 lib521 lib523 lib524 lib525 lib526 lib527 \ lib529 lib530 lib532 lib533 lib536 lib537 lib540 lib541 lib542 lib543 \ - lib544 lib545 + lib544 lib545 lib549 # Dependencies (may need to be overriden) LDADD = $(LIBDIR)/libcurl.la @@ -138,3 +138,6 @@ lib544_SOURCES = lib544.c $(SUPPORTFILES lib545_SOURCES = lib544.c $(SUPPORTFILES) lib545_CFLAGS = -DLIB545 + +lib549_SOURCES = lib549.c $(SUPPORTFILES) + --- /dev/null +++ tests/libtest/lib549.c @@ -0,0 +1,48 @@ +/***************************************************************************** + * _ _ ____ _ + * Project ___| | | | _ \| | + * / __| | | | |_) | | + * | (__| |_| | _ <| |___ + * \___|\___/|_| \_\_____| + * + * $Id: lib549.c,v 1.1 2007-12-08 22:53:32 bagder Exp $ + * + * argv1 = URL + * argv2 = proxy + * argv3 = non-zero means ASCII transfer + */ + +#include "test.h" + +int test(char *URL) +{ + CURLcode res; + CURL *curl; + + if (curl_global_init(CURL_GLOBAL_ALL) != CURLE_OK) { + fprintf(stderr, "curl_global_init() failed\n"); + return TEST_ERR_MAJOR_BAD; + } + + if ((curl = curl_easy_init()) == NULL) { + fprintf(stderr, "curl_easy_init() failed\n"); + curl_global_cleanup(); + return TEST_ERR_MAJOR_BAD; + } + + curl_easy_setopt(curl, CURLOPT_PROXY, libtest_arg2); + curl_easy_setopt(curl, CURLOPT_URL, URL); + curl_easy_setopt(curl, CURLOPT_PROXY_TRANSFER_MODE, 1); + curl_easy_setopt(curl, CURLOPT_VERBOSE, TRUE); + if(libtest_arg3) + /* enable ascii/text mode */ + curl_easy_setopt(curl, CURLOPT_TRANSFERTEXT, TRUE); + + res = curl_easy_perform(curl); + + curl_easy_cleanup(curl); + curl_global_cleanup(); + + return (int)res; +} +