From ca5f9341ef0a941ed478bc5c7e79397b68c2a616 Mon Sep 17 00:00:00 2001 From: Daniel Stenberg Date: Sat, 24 Oct 2015 00:52:25 +0200 Subject: [PATCH] formadd: support >2GB files on windows Closes #425 --- docs/libcurl/curl_formadd.3 | 11 +++++++ docs/libcurl/symbols-in-versions | 2 ++ include/curl/curl.h | 9 +++++- lib/formdata.c | 41 ++++++++++++++---------- lib/formdata.h | 4 +-- tests/data/test554 | 47 +++++++++++++++++++++++++++ tests/libtest/lib554.c | 54 +++++++++++++++++++++++--------- 7 files changed, 133 insertions(+), 35 deletions(-) diff --git a/docs/libcurl/curl_formadd.3 b/docs/libcurl/curl_formadd.3 index cdae51be2..fa9829216 100644 --- a/docs/libcurl/curl_formadd.3 +++ b/docs/libcurl/curl_formadd.3 @@ -83,7 +83,18 @@ to send away. libcurl will use the pointer and refer to the data in your application, so you must make sure it remains until curl no longer needs it. If the data isn't NUL-terminated, or if you'd like it to contain zero bytes, you must set its length with \fBCURLFORM_CONTENTSLENGTH\fP. +.IP CURLFORM_CONTENTLEN +followed by a curl_off_t value giving the length of the contents. Note that +for \fICURLFORM_STREAM\fP contents, this option is mandatory. + +If you pass a 0 (zero) for this option, libcurl will instead do a strlen() on +the contents to figure out the size. If you really want to send a zero byte +content then you must make sure strlen() on the data pointer returns zero. + +(Option added in 7.46.0) .IP CURLFORM_CONTENTSLENGTH +(This option is deprecated. Use \fICURLFORM_CONTENTLEN\fP instead!) + followed by a long giving the length of the contents. Note that for \fICURLFORM_STREAM\fP contents, this option is mandatory. diff --git a/docs/libcurl/symbols-in-versions b/docs/libcurl/symbols-in-versions index 732ad19ed..37250918a 100644 --- a/docs/libcurl/symbols-in-versions +++ b/docs/libcurl/symbols-in-versions @@ -164,6 +164,7 @@ CURLFORM_BUFFER 7.9.8 CURLFORM_BUFFERLENGTH 7.9.8 CURLFORM_BUFFERPTR 7.9.8 CURLFORM_CONTENTHEADER 7.9.3 +CURLFORM_CONTENTLEN 7.46.0 CURLFORM_CONTENTSLENGTH 7.9 CURLFORM_CONTENTTYPE 7.9 CURLFORM_COPYCONTENTS 7.9 @@ -678,6 +679,7 @@ CURL_GLOBAL_DEFAULT 7.8 CURL_GLOBAL_NOTHING 7.8 CURL_GLOBAL_SSL 7.8 CURL_GLOBAL_WIN32 7.8.1 +CURL_HTTPPOST_LARGE 7.46.0 CURL_HTTP_VERSION_1_0 7.9.1 CURL_HTTP_VERSION_1_1 7.9.1 CURL_HTTP_VERSION_2 7.43.0 diff --git a/include/curl/curl.h b/include/curl/curl.h index 24b9195de..6297ae6ee 100644 --- a/include/curl/curl.h +++ b/include/curl/curl.h @@ -127,7 +127,8 @@ struct curl_httppost { char *name; /* pointer to allocated name */ long namelength; /* length of name length */ char *contents; /* pointer to allocated data contents */ - long contentslength; /* length of contents field */ + long contentslength; /* length of contents field, see also + CURL_HTTPPOST_LARGE */ char *buffer; /* pointer to allocated buffer contents */ long bufferlength; /* length of buffer field */ char *contenttype; /* Content-Type */ @@ -152,12 +153,17 @@ struct curl_httppost { /* upload file contents by using the regular read callback to get the data and pass the given pointer as custom pointer */ #define CURL_HTTPPOST_CALLBACK (1<<6) +/* use size in 'contentlen', added in 7.46.0 */ +#define CURL_HTTPPOST_LARGE (1<<7) char *showfilename; /* The file name to show. If not set, the actual file name will be used (if this is a file part) */ void *userp; /* custom pointer used for HTTPPOST_CALLBACK posts */ + curl_off_t contentlen; /* alternative length of contents + field. Used if CURL_HTTPPOST_LARGE is + set. Added in 7.46.0 */ }; /* This is the CURLOPT_PROGRESSFUNCTION callback proto. It is now considered @@ -1835,6 +1841,7 @@ typedef enum { CFINIT(OBSOLETE2), CFINIT(STREAM), + CFINIT(CONTENTLEN), /* added in 7.46.0, provide a curl_off_t length */ CURLFORM_LASTENTRY /* the last unused */ } CURLformoption; diff --git a/lib/formdata.c b/lib/formdata.c index a4e6ab35d..cb061acb7 100644 --- a/lib/formdata.c +++ b/lib/formdata.c @@ -77,7 +77,7 @@ static char *formboundary(struct SessionHandle *data); ***************************************************************************/ static struct curl_httppost * AddHttpPost(char *name, size_t namelength, - char *value, size_t contentslength, + char *value, curl_off_t contentslength, char *buffer, size_t bufferlength, char *contenttype, long flags, @@ -93,14 +93,14 @@ AddHttpPost(char *name, size_t namelength, post->name = name; post->namelength = (long)(name?(namelength?namelength:strlen(name)):0); post->contents = value; - post->contentslength = (long)contentslength; + post->contentlen = contentslength; post->buffer = buffer; post->bufferlength = (long)bufferlength; post->contenttype = contenttype; post->contentheader = contentHeader; post->showfilename = showfilename; post->userp = userp, - post->flags = flags; + post->flags = flags | CURL_HTTPPOST_LARGE; } else return NULL; @@ -380,11 +380,14 @@ CURLFORMcode FormAdd(struct curl_httppost **httppost, } break; case CURLFORM_CONTENTSLENGTH: - if(current_form->contentslength) - return_value = CURL_FORMADD_OPTION_TWICE; - else - current_form->contentslength = - array_state?(size_t)array_value:(size_t)va_arg(params, long); + current_form->contentslength = + array_state?(size_t)array_value:(size_t)va_arg(params, long); + break; + + case CURLFORM_CONTENTLEN: + current_form->flags |= CURL_HTTPPOST_LARGE; + current_form->contentslength = + array_state?(curl_off_t)array_value:va_arg(params, curl_off_t); break; /* Get contents from a given file name */ @@ -661,9 +664,12 @@ CURLFORMcode FormAdd(struct curl_httppost **httppost, HTTPPOST_PTRCONTENTS | HTTPPOST_PTRBUFFER | HTTPPOST_CALLBACK)) && form->value) { /* copy value (without strdup; possibly contains null characters) */ - form->value = Curl_memdup(form->value, form->contentslength? - form->contentslength: - strlen(form->value)+1); + size_t clen = (size_t) form->contentslength; + if(!clen) + clen = strlen(form->value)+1; + + form->value = Curl_memdup(form->value, clen); + if(!form->value) { return_value = CURL_FORMADD_MEMORY; break; @@ -816,7 +822,7 @@ static curl_off_t VmsSpecialSize(const char * name, static CURLcode AddFormData(struct FormData **formp, enum formtype type, const void *line, - size_t length, + curl_off_t length, curl_off_t *size) { struct FormData *newform = malloc(sizeof(struct FormData)); @@ -1306,15 +1312,16 @@ CURLcode Curl_getformdata(struct SessionHandle *data, result = AddFormData(&form, FORM_CONTENT, post->buffer, post->bufferlength, &size); else if(post->flags & HTTPPOST_CALLBACK) - /* the contents should be read with the callback and the size - is set with the contentslength */ + /* the contents should be read with the callback and the size is set + with the contentslength */ result = AddFormData(&form, FORM_CALLBACK, post->userp, - post->contentslength, &size); + post->flags&CURL_HTTPPOST_LARGE? + post->contentlen:post->contentslength, &size); else /* include the contents we got */ result = AddFormData(&form, FORM_CONTENT, post->contents, - post->contentslength, &size); - + post->flags&CURL_HTTPPOST_LARGE? + post->contentlen:post->contentslength, &size); file = file->more; } while(file && !result); /* for each specified file for this field */ diff --git a/lib/formdata.h b/lib/formdata.h index 22f504bb3..05621e3e8 100644 --- a/lib/formdata.h +++ b/lib/formdata.h @@ -7,7 +7,7 @@ * | (__| |_| | _ <| |___ * \___|\___/|_| \_\_____| * - * Copyright (C) 1998 - 2010, Daniel Stenberg, , et al. + * Copyright (C) 1998 - 2015, Daniel Stenberg, , et al. * * This software is licensed as described in the file COPYING, which * you should have received as part of this distribution. The terms @@ -54,7 +54,7 @@ typedef struct FormInfo { size_t namelength; char *value; bool value_alloc; - size_t contentslength; + curl_off_t contentslength; char *contenttype; bool contenttype_alloc; long flags; diff --git a/tests/data/test554 b/tests/data/test554 index b55fa3d10..fdbd1868b 100644 --- a/tests/data/test554 +++ b/tests/data/test554 @@ -18,6 +18,22 @@ Content-Type: text/html hello + +HTTP/1.1 200 OK +Date: Thu, 09 Nov 2010 14:49:00 GMT +Server: test-server/fake swsclose +Connection: close +Content-Type: text/html + +hello +HTTP/1.1 200 OK +Date: Thu, 09 Nov 2010 14:49:00 GMT +Server: test-server/fake swsclose +Connection: close +Content-Type: text/html + +hello + # Client-side @@ -78,6 +94,37 @@ send Content-Disposition: form-data; name="somename"; filename="somefile.txt" Content-Type: text/plain +blah blah +-------------------------------- +POST /554 HTTP/1.1 +Host: %HOSTIP:%HTTPPORT +Accept: */* +Content-Length: 732 +Expect: 100-continue +Content-Type: multipart/form-data; boundary=---------------------------- + +------------------------------ +Content-Disposition: form-data; name="sendfile alternative"; filename="file name 2" + +this is what we post to the silly web server + +------------------------------ +Content-Disposition: form-data; name="callbackdata" + +this is what we post to the silly web server + +------------------------------ +Content-Disposition: form-data; name="filename" + +postit2.c +------------------------------ +Content-Disposition: form-data; name="submit" + +send +------------------------------ +Content-Disposition: form-data; name="somename"; filename="somefile.txt" +Content-Type: text/plain + blah blah -------------------------------- diff --git a/tests/libtest/lib554.c b/tests/libtest/lib554.c index 0596f3ef1..c54d99e86 100644 --- a/tests/libtest/lib554.c +++ b/tests/libtest/lib554.c @@ -5,7 +5,7 @@ * | (__| |_| | _ <| |___ * \___|\___/|_| \_\_____| * - * Copyright (C) 1998 - 2012, Daniel Stenberg, , et al. + * Copyright (C) 1998 - 2015, Daniel Stenberg, , et al. * * This software is licensed as described in the file COPYING, which * you should have received as part of this distribution. The terms @@ -64,7 +64,7 @@ static size_t read_callback(void *ptr, size_t size, size_t nmemb, void *userp) #endif } -int test(char *URL) +static int once(char *URL, bool oldstyle) { CURL *curl; CURLcode res=CURLE_OK; @@ -75,22 +75,29 @@ int test(char *URL) struct WriteThis pooh; struct WriteThis pooh2; - if (curl_global_init(CURL_GLOBAL_ALL) != CURLE_OK) { - fprintf(stderr, "curl_global_init() failed\n"); - return TEST_ERR_MAJOR_BAD; - } - pooh.readptr = data; pooh.sizeleft = strlen(data); /* Fill in the file upload field */ - formrc = curl_formadd(&formpost, - &lastptr, - CURLFORM_COPYNAME, "sendfile", - CURLFORM_STREAM, &pooh, - CURLFORM_CONTENTSLENGTH, (long)pooh.sizeleft, - CURLFORM_FILENAME, "postit2.c", - CURLFORM_END); + if(oldstyle) { + formrc = curl_formadd(&formpost, + &lastptr, + CURLFORM_COPYNAME, "sendfile", + CURLFORM_STREAM, &pooh, + CURLFORM_CONTENTSLENGTH, (long)pooh.sizeleft, + CURLFORM_FILENAME, "postit2.c", + CURLFORM_END); + } + else { + /* new style */ + formrc = curl_formadd(&formpost, + &lastptr, + CURLFORM_COPYNAME, "sendfile alternative", + CURLFORM_STREAM, &pooh, + CURLFORM_CONTENTLEN, (curl_off_t)pooh.sizeleft, + CURLFORM_FILENAME, "file name 2", + CURLFORM_END); + } if(formrc) printf("curl_formadd(1) = %d\n", (int)formrc); @@ -190,10 +197,27 @@ test_cleanup: /* always cleanup */ curl_easy_cleanup(curl); - curl_global_cleanup(); /* now cleanup the formpost chain */ curl_formfree(formpost); return res; } + +int test(char *URL) +{ + int res; + + if (curl_global_init(CURL_GLOBAL_ALL) != CURLE_OK) { + fprintf(stderr, "curl_global_init() failed\n"); + return TEST_ERR_MAJOR_BAD; + } + + res = once(URL, TRUE); /* old */ + if(!res) + res = once(URL, FALSE); /* new */ + + curl_global_cleanup(); + + return res; +} -- 2.40.0