diff options
Diffstat (limited to 'lib/formdata.c')
-rw-r--r-- | lib/formdata.c | 975 |
1 files changed, 383 insertions, 592 deletions
diff --git a/lib/formdata.c b/lib/formdata.c index c98246e..9e8ce4e 100644 --- a/lib/formdata.c +++ b/lib/formdata.c @@ -5,7 +5,7 @@ * | (__| |_| | _ <| |___ * \___|\___/|_| \_\_____| * - * Copyright (C) 1998 - 2010, Daniel Stenberg, <daniel@haxx.se>, et al. + * Copyright (C) 1998 - 2015, Daniel Stenberg, <daniel@haxx.se>, et al. * * This software is licensed as described in the file COPYING, which * you should have received as part of this distribution. The terms @@ -20,126 +20,35 @@ * ***************************************************************************/ -/* - Debug the form generator stand-alone by compiling this source file with: - - gcc -DHAVE_CONFIG_H -I../ -g -D_FORM_DEBUG -DCURLDEBUG -o formdata \ - -I../include formdata.c strequal.c memdebug.c mprintf.c strerror.c - - (depending on circumstances you may need further externals added) - - run the 'formdata' executable the output should end with: - All Tests seem to have worked ... - and the following parts should be there: - -Content-Disposition: form-data; name="simple_COPYCONTENTS" -value for simple COPYCONTENTS - -Content-Disposition: form-data; name="COPYCONTENTS_+_CONTENTTYPE" -Content-Type: image/gif -value for COPYCONTENTS + CONTENTTYPE - -Content-Disposition: form-data; name="PRNAME_+_NAMELENGTH_+_COPYNAME_+_CONTENTSLENGTH" -vlue for PTRNAME + NAMELENGTH + COPYNAME + CONTENTSLENGTH -(or you might see P^@RNAME and v^@lue at the start) - -Content-Disposition: form-data; name="simple_PTRCONTENTS" -value for simple PTRCONTENTS - -Content-Disposition: form-data; name="PTRCONTENTS_+_CONTENTSLENGTH" -vlue for PTRCONTENTS + CONTENTSLENGTH -(or you might see v^@lue at the start) - -Content-Disposition: form-data; name="PTRCONTENTS_+_CONTENTSLENGTH_+_CONTENTTYPE" -Content-Type: application/octet-stream -vlue for PTRCONTENTS + CONTENTSLENGTH + CONTENTTYPE -(or you might see v^@lue at the start) - -Content-Disposition: form-data; name="FILE1_+_CONTENTTYPE"; filename="formdata.h" -Content-Type: text/html -... - -Content-Disposition: form-data; name="FILE1_+_FILE2" -Content-Type: multipart/mixed, boundary=curlz1s0dkticx49MV1KGcYP5cvfSsz -... -Content-Disposition: attachment; filename="formdata.h" -Content-Type: application/octet-stream -... -Content-Disposition: attachment; filename="Makefile.b32" -Content-Type: application/octet-stream -... - -Content-Disposition: form-data; name="FILE1_+_FILE2_+_FILE3" -Content-Type: multipart/mixed, boundary=curlirkYPmPwu6FrJ1vJ1u1BmtIufh1 -... -Content-Disposition: attachment; filename="formdata.h" -Content-Type: application/octet-stream -... -Content-Disposition: attachment; filename="Makefile.b32" -Content-Type: application/octet-stream -... -Content-Disposition: attachment; filename="formdata.h" -Content-Type: application/octet-stream -... - - -Content-Disposition: form-data; name="ARRAY: FILE1_+_FILE2_+_FILE3" -Content-Type: multipart/mixed, boundary=curlirkYPmPwu6FrJ1vJ1u1BmtIufh1 -... -Content-Disposition: attachment; filename="formdata.h" -Content-Type: application/octet-stream -... -Content-Disposition: attachment; filename="Makefile.b32" -Content-Type: application/octet-stream -... -Content-Disposition: attachment; filename="formdata.h" -Content-Type: application/octet-stream -... - -Content-Disposition: form-data; name="FILECONTENT" -... - - */ +#include "curl_setup.h" -#include "setup.h" #include <curl/curl.h> -/* Length of the random boundary string. */ -#define BOUNDARY_LENGTH 40 - -#if !defined(CURL_DISABLE_HTTP) || defined(USE_SSLEAY) +#ifndef CURL_DISABLE_HTTP -#include <stdio.h> -#include <stdlib.h> -#include <string.h> -#include <stdarg.h> -#include <time.h> #if defined(HAVE_LIBGEN_H) && defined(HAVE_BASENAME) #include <libgen.h> #endif + #include "urldata.h" /* for struct SessionHandle */ -#include "easyif.h" /* for Curl_convert_... prototypes */ #include "formdata.h" -#include "curl_rand.h" +#include "vtls/vtls.h" #include "strequal.h" -#include "curl_memory.h" - -#define _MPRINTF_REPLACE /* use our functions only */ -#include <curl/mprintf.h> +#include "sendf.h" +#include "strdup.h" +#include "curl_printf.h" -/* The last #include file should be: */ +/* The last #include files should be: */ +#include "curl_memory.h" #include "memdebug.h" -#endif /* !defined(CURL_DISABLE_HTTP) || defined(USE_SSLEAY) */ - -#ifndef CURL_DISABLE_HTTP - #ifndef HAVE_BASENAME static char *Curl_basename(char *path); #define basename(x) Curl_basename((x)) #endif static size_t readfromfile(struct Form *form, char *buffer, size_t size); +static char *formboundary(struct SessionHandle *data); /* What kind of Content-Type to use on un-specified files with unrecognized extensions. */ @@ -240,8 +149,6 @@ static FormInfo * AddFormInfo(char *value, /* then move the original 'more' to point to ourselves */ parent_form_info->more = form_info; } - else - return NULL; return form_info; } @@ -256,8 +163,8 @@ static FormInfo * AddFormInfo(char *value, * Returns some valid contenttype for filename. * ***************************************************************************/ -static const char * ContentTypeForFilename (const char *filename, - const char *prevtype) +static const char *ContentTypeForFilename(const char *filename, + const char *prevtype) { const char *contenttype = NULL; unsigned int i; @@ -266,7 +173,7 @@ static const char * ContentTypeForFilename (const char *filename, * extensions and pick the first we match! */ struct ContentType { - char extension[6]; + const char *extension; const char *type; }; static const struct ContentType ctts[]={ @@ -302,46 +209,6 @@ static const char * ContentTypeForFilename (const char *filename, /*************************************************************************** * - * memdup() - * - * Copies the 'source' data to a newly allocated buffer buffer (that is - * returned). Uses buffer_length if not null, else uses strlen to determine - * the length of the buffer to be copied - * - * Returns the new pointer or NULL on failure. - * - ***************************************************************************/ -static char *memdup(const char *src, size_t buffer_length) -{ - size_t length; - bool add = FALSE; - char *buffer; - - if(buffer_length) - length = buffer_length; - else if(src) { - length = strlen(src); - add = TRUE; - } - else - /* no length and a NULL src pointer! */ - return strdup(""); - - buffer = malloc(length+add); - if(!buffer) - return NULL; /* fail */ - - memcpy(buffer, src, length); - - /* if length unknown do null termination */ - if(add) - buffer[length] = '\0'; - - return buffer; -} - -/*************************************************************************** - * * FormAdd() * * Stores a formpost parameter and builds the appropriate linked list. @@ -382,7 +249,7 @@ static char *memdup(const char *src, size_t buffer_length) * CURL_FORMADD_NULL if a null pointer was given for a char * CURL_FORMADD_MEMORY if the allocation of a FormInfo struct failed * CURL_FORMADD_UNKNOWN_OPTION if an unknown option was used - * CURL_FORMADD_INCOMPLETE if the some FormInfo is not complete (or an error) + * CURL_FORMADD_INCOMPLETE if the some FormInfo is not complete (or error) * CURL_FORMADD_MEMORY if a HttpPost struct cannot be allocated * CURL_FORMADD_MEMORY if some allocation for string copying failed. * CURL_FORMADD_ILLEGAL_ARRAY if an illegal option is used in an array @@ -422,7 +289,7 @@ CURLFORMcode FormAdd(struct curl_httppost **httppost, while(return_value == CURL_FORMADD_OK) { /* first see if we have more parts of the array param */ - if( array_state && forms ) { + if(array_state && forms) { /* get the upcoming option from the given array */ option = forms->option; array_value = (char *)forms->value; @@ -460,8 +327,10 @@ CURLFORMcode FormAdd(struct curl_httppost **httppost, */ case CURLFORM_PTRNAME: #ifdef CURL_DOES_CONVERSIONS - /* treat CURLFORM_PTR like CURLFORM_COPYNAME so we'll - have safe memory for the eventual conversion */ + /* Treat CURLFORM_PTR like CURLFORM_COPYNAME so that libcurl will copy + * the data in all cases so that we'll have safe memory for the eventual + * conversion. + */ #else current_form->flags |= HTTPPOST_PTRNAME; /* fall through */ #endif @@ -512,7 +381,7 @@ CURLFORMcode FormAdd(struct curl_httppost **httppost, /* Get contents from a given file name */ case CURLFORM_FILECONTENT: - if(current_form->flags != 0) + if(current_form->flags & (HTTPPOST_PTRCONTENTS|HTTPPOST_READFILE)) return_value = CURL_FORMADD_OPTION_TWICE; else { const char *filename = array_state? @@ -540,9 +409,21 @@ CURLFORMcode FormAdd(struct curl_httppost **httppost, if(current_form->value) { if(current_form->flags & HTTPPOST_FILENAME) { if(filename) { - if((current_form = AddFormInfo(strdup(filename), - NULL, current_form)) == NULL) + char *fname = strdup(filename); + if(!fname) return_value = CURL_FORMADD_MEMORY; + else { + form = AddFormInfo(fname, NULL, current_form); + if(!form) { + free(fname); + return_value = CURL_FORMADD_MEMORY; + } + else { + form->value_alloc = TRUE; + current_form = form; + form = NULL; + } + } } else return_value = CURL_FORMADD_NULL; @@ -566,46 +447,18 @@ CURLFORMcode FormAdd(struct curl_httppost **httppost, break; } - case CURLFORM_BUFFER: - { - const char *filename = array_state?array_value: - va_arg(params, char *); - - if(current_form->value) { - if(current_form->flags & HTTPPOST_BUFFER) { - if(filename) { - if((current_form = AddFormInfo(strdup(filename), - NULL, current_form)) == NULL) - return_value = CURL_FORMADD_MEMORY; - } - else - return_value = CURL_FORMADD_NULL; - } - else - return_value = CURL_FORMADD_OPTION_TWICE; - } - else { - if(filename) { - current_form->value = strdup(filename); - if(!current_form->value) - return_value = CURL_FORMADD_MEMORY; - } - else - return_value = CURL_FORMADD_NULL; - current_form->flags |= HTTPPOST_BUFFER; - } - break; - } - case CURLFORM_BUFFERPTR: - current_form->flags |= HTTPPOST_PTRBUFFER; + current_form->flags |= HTTPPOST_PTRBUFFER|HTTPPOST_BUFFER; if(current_form->buffer) return_value = CURL_FORMADD_OPTION_TWICE; else { char *buffer = array_state?array_value:va_arg(params, char *); - if(buffer) + if(buffer) { current_form->buffer = buffer; /* store for the moment */ + current_form->value = buffer; /* make it non-NULL to be accepted + as fine */ + } else return_value = CURL_FORMADD_NULL; } @@ -645,10 +498,21 @@ CURLFORMcode FormAdd(struct curl_httppost **httppost, if(current_form->contenttype) { if(current_form->flags & HTTPPOST_FILENAME) { if(contenttype) { - if((current_form = AddFormInfo(NULL, - strdup(contenttype), - current_form)) == NULL) + char *type = strdup(contenttype); + if(!type) return_value = CURL_FORMADD_MEMORY; + else { + form = AddFormInfo(NULL, type, current_form); + if(!form) { + free(type); + return_value = CURL_FORMADD_MEMORY; + } + else { + form->contenttype_alloc = TRUE; + current_form = form; + form = NULL; + } + } } else return_value = CURL_FORMADD_NULL; @@ -677,7 +541,7 @@ CURLFORMcode FormAdd(struct curl_httppost **httppost, (struct curl_slist*)array_value: va_arg(params, struct curl_slist*); - if( current_form->contentheader ) + if(current_form->contentheader) return_value = CURL_FORMADD_OPTION_TWICE; else current_form->contentheader = list; @@ -685,10 +549,11 @@ CURLFORMcode FormAdd(struct curl_httppost **httppost, break; } case CURLFORM_FILENAME: + case CURLFORM_BUFFER: { const char *filename = array_state?array_value: va_arg(params, char *); - if( current_form->showfilename ) + if(current_form->showfilename) return_value = CURL_FORMADD_OPTION_TWICE; else { current_form->showfilename = strdup(filename); @@ -701,6 +566,31 @@ CURLFORMcode FormAdd(struct curl_httppost **httppost, } default: return_value = CURL_FORMADD_UNKNOWN_OPTION; + break; + } + } + + if(CURL_FORMADD_OK != return_value) { + /* On error, free allocated fields for all nodes of the FormInfo linked + list without deallocating nodes. List nodes are deallocated later on */ + FormInfo *ptr; + for(ptr = first_form; ptr != NULL; ptr = ptr->more) { + if(ptr->name_alloc) { + Curl_safefree(ptr->name); + ptr->name_alloc = FALSE; + } + if(ptr->value_alloc) { + Curl_safefree(ptr->value); + ptr->value_alloc = FALSE; + } + if(ptr->contenttype_alloc) { + Curl_safefree(ptr->contenttype); + ptr->contenttype_alloc = FALSE; + } + if(ptr->showfilename_alloc) { + Curl_safefree(ptr->showfilename); + ptr->showfilename_alloc = FALSE; + } } } @@ -712,53 +602,60 @@ CURLFORMcode FormAdd(struct curl_httppost **httppost, for(form = first_form; form != NULL; form = form->more) { - if( ((!form->name || !form->value) && !post) || - ( (form->contentslength) && - (form->flags & HTTPPOST_FILENAME) ) || - ( (form->flags & HTTPPOST_FILENAME) && - (form->flags & HTTPPOST_PTRCONTENTS) ) || - - ( (!form->buffer) && - (form->flags & HTTPPOST_BUFFER) && - (form->flags & HTTPPOST_PTRBUFFER) ) || - - ( (form->flags & HTTPPOST_READFILE) && - (form->flags & HTTPPOST_PTRCONTENTS) ) + if(((!form->name || !form->value) && !post) || + ( (form->contentslength) && + (form->flags & HTTPPOST_FILENAME) ) || + ( (form->flags & HTTPPOST_FILENAME) && + (form->flags & HTTPPOST_PTRCONTENTS) ) || + + ( (!form->buffer) && + (form->flags & HTTPPOST_BUFFER) && + (form->flags & HTTPPOST_PTRBUFFER) ) || + + ( (form->flags & HTTPPOST_READFILE) && + (form->flags & HTTPPOST_PTRCONTENTS) ) ) { return_value = CURL_FORMADD_INCOMPLETE; break; } else { - if( ((form->flags & HTTPPOST_FILENAME) || - (form->flags & HTTPPOST_BUFFER)) && - !form->contenttype ) { + if(((form->flags & HTTPPOST_FILENAME) || + (form->flags & HTTPPOST_BUFFER)) && + !form->contenttype ) { + char *f = form->flags & HTTPPOST_BUFFER? + form->showfilename : form->value; + /* our contenttype is missing */ - form->contenttype - = strdup(ContentTypeForFilename(form->value, prevtype)); + form->contenttype = strdup(ContentTypeForFilename(f, prevtype)); if(!form->contenttype) { return_value = CURL_FORMADD_MEMORY; break; } form->contenttype_alloc = TRUE; } - if( !(form->flags & HTTPPOST_PTRNAME) && - (form == first_form) ) { + if(!(form->flags & HTTPPOST_PTRNAME) && + (form == first_form) ) { /* Note that there's small risk that form->name is NULL here if the app passed in a bad combo, so we better check for that first. */ - if(form->name) + if(form->name) { /* copy name (without strdup; possibly contains null characters) */ - form->name = memdup(form->name, form->namelength); + form->name = Curl_memdup(form->name, form->namelength? + form->namelength: + strlen(form->name)+1); + } if(!form->name) { return_value = CURL_FORMADD_MEMORY; break; } form->name_alloc = TRUE; } - if( !(form->flags & (HTTPPOST_FILENAME | HTTPPOST_READFILE | - HTTPPOST_PTRCONTENTS | HTTPPOST_PTRBUFFER | - HTTPPOST_CALLBACK)) ) { + if(!(form->flags & (HTTPPOST_FILENAME | HTTPPOST_READFILE | + HTTPPOST_PTRCONTENTS | HTTPPOST_PTRBUFFER | + HTTPPOST_CALLBACK)) && form->value) { /* copy value (without strdup; possibly contains null characters) */ - form->value = memdup(form->value, form->contentslength); + form->value = Curl_memdup(form->value, form->contentslength? + form->contentslength: + strlen(form->value)+1); if(!form->value) { return_value = CURL_FORMADD_MEMORY; break; @@ -783,32 +680,39 @@ CURLFORMcode FormAdd(struct curl_httppost **httppost, prevtype = form->contenttype; } } - } - - if(return_value) { - /* we return on error, free possibly allocated fields */ - if(!form) - form = current_form; - if(form) { - if(form->name_alloc) - free(form->name); - if(form->value_alloc) - free(form->value); - if(form->contenttype_alloc) - free(form->contenttype); - if(form->showfilename_alloc) - free(form->showfilename); + if(CURL_FORMADD_OK != return_value) { + /* On error, free allocated fields for nodes of the FormInfo linked + list which are not already owned by the httppost linked list + without deallocating nodes. List nodes are deallocated later on */ + FormInfo *ptr; + for(ptr = form; ptr != NULL; ptr = ptr->more) { + if(ptr->name_alloc) { + Curl_safefree(ptr->name); + ptr->name_alloc = FALSE; + } + if(ptr->value_alloc) { + Curl_safefree(ptr->value); + ptr->value_alloc = FALSE; + } + if(ptr->contenttype_alloc) { + Curl_safefree(ptr->contenttype); + ptr->contenttype_alloc = FALSE; + } + if(ptr->showfilename_alloc) { + Curl_safefree(ptr->showfilename); + ptr->showfilename_alloc = FALSE; + } + } } } - /* always delete the allocated memory before returning */ - form = first_form; - while(form != NULL) { - FormInfo *delete_form; - - delete_form = form; - form = form->more; - free (delete_form); + /* Always deallocate FormInfo linked list nodes without touching node + fields given that these have either been deallocated or are owned + now by the httppost linked list */ + while(first_form) { + FormInfo *ptr = first_form->more; + free(first_form); + first_form = ptr; } return return_value; @@ -816,6 +720,8 @@ CURLFORMcode FormAdd(struct curl_httppost **httppost, /* * curl_formadd() is a public API to add a section to the multipart formpost. + * + * @unittest: 1308 */ CURLFORMcode curl_formadd(struct curl_httppost **httppost, @@ -830,6 +736,70 @@ CURLFORMcode curl_formadd(struct curl_httppost **httppost, return result; } +#ifdef __VMS +#include <fabdef.h> +/* + * get_vms_file_size does what it takes to get the real size of the file + * + * For fixed files, find out the size of the EOF block and adjust. + * + * For all others, have to read the entire file in, discarding the contents. + * Most posted text files will be small, and binary files like zlib archives + * and CD/DVD images should be either a STREAM_LF format or a fixed format. + * + */ +curl_off_t VmsRealFileSize(const char * name, + const struct_stat * stat_buf) +{ + char buffer[8192]; + curl_off_t count; + int ret_stat; + FILE * file; + + file = fopen(name, "r"); /* VMS */ + if(file == NULL) + return 0; + + count = 0; + ret_stat = 1; + while(ret_stat > 0) { + ret_stat = fread(buffer, 1, sizeof(buffer), file); + if(ret_stat != 0) + count += ret_stat; + } + fclose(file); + + return count; +} + +/* + * + * VmsSpecialSize checks to see if the stat st_size can be trusted and + * if not to call a routine to get the correct size. + * + */ +static curl_off_t VmsSpecialSize(const char * name, + const struct_stat * stat_buf) +{ + switch(stat_buf->st_fab_rfm) { + case FAB$C_VAR: + case FAB$C_VFC: + return VmsRealFileSize(name, stat_buf); + break; + default: + return stat_buf->st_size; + } +} + +#endif + +#ifndef __VMS +#define filesize(name, stat_data) (stat_data.st_size) +#else + /* Getting the expected file size needs help on VMS */ +#define filesize(name, stat_data) VmsSpecialSize(name, &stat_data) +#endif + /* * AddFormData() adds a chunk of data to the FormData linked list. * @@ -884,9 +854,10 @@ static CURLcode AddFormData(struct FormData **formp, file */ if(!strequal("-", newform->line)) { struct_stat file; - if(!stat(newform->line, &file)) { - *size += file.st_size; - } + if(!stat(newform->line, &file) && !S_ISDIR(file.st_mode)) + *size += filesize(newform->line, file); + else + return CURLE_BAD_FUNCTION_ARGUMENT; } } } @@ -933,54 +904,26 @@ void Curl_formclean(struct FormData **form_ptr) *form_ptr = NULL; } -#ifdef CURL_DOES_CONVERSIONS -/* - * Curl_formcovert() is used from http.c, this converts any - form items that need to be sent in the network encoding. - Returns CURLE_OK on success. - */ -CURLcode Curl_formconvert(struct SessionHandle *data, struct FormData *form) -{ - struct FormData *next; - CURLcode rc; - - if(!form) - return CURLE_OK; - - if(!data) - return CURLE_BAD_FUNCTION_ARGUMENT; - - do { - next=form->next; /* the following form line */ - if(form->type == FORM_DATA) { - rc = Curl_convert_to_network(data, form->line, form->length); - /* Curl_convert_to_network calls failf if unsuccessful */ - if(rc != CURLE_OK) - return rc; - } - } while((form = next) != NULL); /* continue */ - return CURLE_OK; -} -#endif /* CURL_DOES_CONVERSIONS */ - /* * curl_formget() * Serialize a curl_httppost struct. * Returns 0 on success. + * + * @unittest: 1308 */ int curl_formget(struct curl_httppost *form, void *arg, curl_formget_callback append) { - CURLcode rc; + CURLcode result; curl_off_t size; struct FormData *data, *ptr; - rc = Curl_getFormData(&data, form, NULL, &size); - if(rc != CURLE_OK) - return (int)rc; + result = Curl_getformdata(NULL, &data, form, NULL, &size); + if(result) + return (int)result; - for (ptr = data; ptr; ptr = ptr->next) { - if(ptr->type == FORM_FILE) { + for(ptr = data; ptr; ptr = ptr->next) { + if((ptr->type == FORM_FILE) || (ptr->type == FORM_CALLBACK)) { char buffer[8192]; size_t nread; struct Form temp; @@ -989,14 +932,15 @@ int curl_formget(struct curl_httppost *form, void *arg, do { nread = readfromfile(&temp, buffer, sizeof(buffer)); - if((nread == (size_t) -1) || (nread != append(arg, buffer, nread))) { - if(temp.fp) { + if((nread == (size_t) -1) || + (nread > sizeof(buffer)) || + (nread != append(arg, buffer, nread))) { + if(temp.fp) fclose(temp.fp); - } Curl_formclean(&data); return -1; } - } while(nread == sizeof(buffer)); + } while(nread); } else { if(ptr->length != append(arg, ptr->line, ptr->length)) { @@ -1025,18 +969,16 @@ void curl_formfree(struct curl_httppost *form) next=form->next; /* the following form line */ /* recurse to sub-contents */ - if(form->more) - curl_formfree(form->more); + curl_formfree(form->more); - if( !(form->flags & HTTPPOST_PTRNAME) && form->name) + if(!(form->flags & HTTPPOST_PTRNAME)) free(form->name); /* free the name */ - if( !(form->flags & (HTTPPOST_PTRCONTENTS|HTTPPOST_CALLBACK)) && - form->contents) + if(!(form->flags & + (HTTPPOST_PTRCONTENTS|HTTPPOST_BUFFER|HTTPPOST_CALLBACK)) + ) free(form->contents); /* free the contents */ - if(form->contenttype) - free(form->contenttype); /* free the content type */ - if(form->showfilename) - free(form->showfilename); /* free the faked file name */ + free(form->contenttype); /* free the content type */ + free(form->showfilename); /* free the faked file name */ free(form); /* free the struct */ } while((form = next) != NULL); /* continue */ @@ -1104,16 +1046,64 @@ static char *strippath(const char *fullfile) return base; /* returns an allocated string or NULL ! */ } +static CURLcode formdata_add_filename(const struct curl_httppost *file, + struct FormData **form, + curl_off_t *size) +{ + CURLcode result = CURLE_OK; + char *filename = file->showfilename; + char *filebasename = NULL; + char *filename_escaped = NULL; + + if(!filename) { + filebasename = strippath(file->contents); + if(!filebasename) + return CURLE_OUT_OF_MEMORY; + filename = filebasename; + } + + if(strchr(filename, '\\') || strchr(filename, '"')) { + char *p0, *p1; + + /* filename need be escaped */ + filename_escaped = malloc(strlen(filename)*2+1); + if(!filename_escaped) { + free(filebasename); + return CURLE_OUT_OF_MEMORY; + } + p0 = filename_escaped; + p1 = filename; + while(*p1) { + if(*p1 == '\\' || *p1 == '"') + *p0++ = '\\'; + *p0++ = *p1++; + } + *p0 = '\0'; + filename = filename_escaped; + } + result = AddFormDataf(form, size, + "; filename=\"%s\"", + filename); + free(filename_escaped); + free(filebasename); + return result; +} + /* - * Curl_getFormData() converts a linked list of "meta data" into a complete + * Curl_getformdata() converts a linked list of "meta data" into a complete * (possibly huge) multipart formdata. The input list is in 'post', while the * output resulting linked lists gets stored in '*finalform'. *sizep will get * the total size of the whole POST. * A multipart/form_data content-type is built, unless a custom content-type * is passed in 'custom_content_type'. + * + * This function will not do a failf() for the potential memory failures but + * should for all other errors it spots. Just note that this function MAY get + * a NULL pointer in the 'data' argument. */ -CURLcode Curl_getFormData(struct FormData **finalform, +CURLcode Curl_getformdata(struct SessionHandle *data, + struct FormData **finalform, struct curl_httppost *post, const char *custom_content_type, curl_off_t *sizep) @@ -1123,17 +1113,17 @@ CURLcode Curl_getFormData(struct FormData **finalform, struct curl_httppost *file; CURLcode result = CURLE_OK; - curl_off_t size=0; /* support potentially ENORMOUS formposts */ + curl_off_t size = 0; /* support potentially ENORMOUS formposts */ char *boundary; - char *fileboundary=NULL; + char *fileboundary = NULL; struct curl_slist* curList; - *finalform=NULL; /* default form is empty */ + *finalform = NULL; /* default form is empty */ if(!post) return result; /* no input => no output! */ - boundary = Curl_FormBoundary(); + boundary = formboundary(data); if(!boundary) return CURLE_OUT_OF_MEMORY; @@ -1188,10 +1178,15 @@ CURLcode Curl_getFormData(struct FormData **finalform, /* If used, this is a link to more file names, we must then do the magic to include several files with the same field name */ - fileboundary = Curl_FormBoundary(); + free(fileboundary); + fileboundary = formboundary(data); + if(!fileboundary) { + result = CURLE_OUT_OF_MEMORY; + break; + } result = AddFormDataf(&form, &size, - "\r\nContent-Type: multipart/mixed," + "\r\nContent-Type: multipart/mixed;" " boundary=%s\r\n", fileboundary); if(result) @@ -1208,24 +1203,13 @@ CURLcode Curl_getFormData(struct FormData **finalform, if(post->more) { /* if multiple-file */ - char *filebasename= NULL; - if(!file->showfilename) { - filebasename = strippath(file->contents); - if(!filebasename) { - Curl_formclean(&firstform); - free(boundary); - return CURLE_OUT_OF_MEMORY; - } - } - result = AddFormDataf(&form, &size, "\r\n--%s\r\nContent-Disposition: " - "attachment; filename=\"%s\"", - fileboundary, - (file->showfilename?file->showfilename: - filebasename)); - if(filebasename) - free(filebasename); + "attachment", + fileboundary); + if(result) + break; + result = formdata_add_filename(file, &form, &size); if(result) break; } @@ -1234,15 +1218,9 @@ CURLcode Curl_getFormData(struct FormData **finalform, /* it should be noted that for the HTTPPOST_FILENAME and HTTPPOST_CALLBACK cases the ->showfilename struct member is always assigned at this point */ - char *filebasename= - (!post->showfilename)?strippath(post->contents):NULL; - - result = AddFormDataf(&form, &size, - "; filename=\"%s\"", - (post->showfilename?post->showfilename: - filebasename)); - if(filebasename) - free(filebasename); + if(post->showfilename || (post->flags & HTTPPOST_FILENAME)) { + result = formdata_add_filename(post, &form, &size); + } if(result) break; @@ -1258,35 +1236,15 @@ CURLcode Curl_getFormData(struct FormData **finalform, } curList = file->contentheader; - while( curList ) { + while(curList) { /* Process the additional headers specified for this form */ result = AddFormDataf( &form, &size, "\r\n%s", curList->data ); if(result) break; curList = curList->next; } - if(result) { - Curl_formclean(&firstform); - free(boundary); - return result; - } - -#if 0 - /* The header Content-Transfer-Encoding: seems to confuse some receivers - * (like the built-in PHP engine). While I can't see any reason why it - * should, I can just as well skip this to the benefit of the users who - * are using such confused receivers. - */ - - if(file->contenttype && - !checkprefix("text/", file->contenttype)) { - /* this is not a text content, mention our binary encoding */ - result = AddFormDataf(&form, &size, - "\r\nContent-Transfer-Encoding: binary"); - if(result) - break; - } -#endif + if(result) + break; result = AddFormDataf(&form, &size, "\r\n\r\n"); if(result) @@ -1308,7 +1266,7 @@ CURLcode Curl_getFormData(struct FormData **finalform, if(fileread) { if(fileread != stdin) { - /* close the file again */ + /* close the file */ fclose(fileread); /* add the file name only - for later reading from this */ result = AddFormData(&form, FORM_FILE, file->contents, 0, &size); @@ -1327,55 +1285,33 @@ CURLcode Curl_getFormData(struct FormData **finalform, break; } } - - if(result) { - Curl_formclean(&firstform); - free(boundary); - return result; - } - } else { -#ifdef _FORM_DEBUG - fprintf(stderr, - "\n==> Curl_getFormData couldn't open/read \"%s\"\n", - file->contents); -#endif - Curl_formclean(&firstform); - free(boundary); + if(data) + failf(data, "couldn't open file \"%s\"", file->contents); *finalform = NULL; - return CURLE_READ_ERROR; + result = CURLE_READ_ERROR; } - } - else if(post->flags & HTTPPOST_BUFFER) { + else if(post->flags & HTTPPOST_BUFFER) /* include contents of buffer */ result = AddFormData(&form, FORM_CONTENT, post->buffer, post->bufferlength, &size); - if(result) - break; - } - else if(post->flags & HTTPPOST_CALLBACK) { + else if(post->flags & HTTPPOST_CALLBACK) /* 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); - if(result) - break; - } - else { + else /* include the contents we got */ result = AddFormData(&form, FORM_CONTENT, post->contents, post->contentslength, &size); - if(result) - break; - } - } while((file = file->more) != NULL); /* for each specified file for this field */ - if(result) { - Curl_formclean(&firstform); - free(boundary); - return result; - } + + file = file->more; + } while(file && !result); /* for each specified file for this field */ + + if(result) + break; if(post->more) { /* this was a multiple-file inclusion, make a termination file @@ -1383,33 +1319,29 @@ CURLcode Curl_getFormData(struct FormData **finalform, result = AddFormDataf(&form, &size, "\r\n--%s--", fileboundary); - free(fileboundary); if(result) break; } } while((post = post->next) != NULL); /* for each field */ - if(result) { - Curl_formclean(&firstform); - free(boundary); - return result; - } /* end-boundary for everything */ - result = AddFormDataf(&form, &size, - "\r\n--%s--\r\n", - boundary); + if(!result) + result = AddFormDataf(&form, &size, "\r\n--%s--\r\n", boundary); + if(result) { Curl_formclean(&firstform); + free(fileboundary); free(boundary); return result; } *sizep = size; + free(fileboundary); free(boundary); - *finalform=firstform; + *finalform = firstform; return result; } @@ -1431,26 +1363,67 @@ int Curl_FormInit(struct Form *form, struct FormData *formdata ) return 0; } +#ifndef __VMS +# define fopen_read fopen +#else + /* + * vmsfopenread + * + * For upload to work as expected on VMS, different optional + * parameters must be added to the fopen command based on + * record format of the file. + * + */ +# define fopen_read vmsfopenread +static FILE * vmsfopenread(const char *file, const char *mode) { + struct_stat statbuf; + int result; + + result = stat(file, &statbuf); + + switch (statbuf.st_fab_rfm) { + case FAB$C_VAR: + case FAB$C_VFC: + case FAB$C_STMCR: + return fopen(file, "r"); /* VMS */ + break; + default: + return fopen(file, "r", "rfm=stmlf", "ctx=stm"); + } +} +#endif + +/* + * readfromfile() + * + * The read callback that this function may use can return a value larger than + * 'size' (which then this function returns) that indicates a problem and it + * must be properly dealt with + */ static size_t readfromfile(struct Form *form, char *buffer, size_t size) { size_t nread; - bool callback = (bool)(form->data->type == FORM_CALLBACK); + bool callback = (form->data->type == FORM_CALLBACK)?TRUE:FALSE; - if(callback) - nread = form->fread_func(buffer, 1, size, form->data->line); + if(callback) { + if(form->fread_func == ZERO_NULL) + return 0; + else + nread = form->fread_func(buffer, 1, size, form->data->line); + } else { if(!form->fp) { /* this file hasn't yet been opened */ - form->fp = fopen(form->data->line, "rb"); /* b is for binary */ + form->fp = fopen_read(form->data->line, "rb"); /* b is for binary */ if(!form->fp) return (size_t)-1; /* failure */ } nread = fread(buffer, 1, size, form->fp); } - if(!nread || nread > size) { + if(!nread) { /* this is the last chunk from the file, move on */ - if(!callback) { + if(form->fp) { fclose(form->fp); form->fp = NULL; } @@ -1490,7 +1463,7 @@ size_t Curl_FormReader(char *buffer, } do { - if( (form->data->length - form->sent ) > wantedsize - gotsize) { + if((form->data->length - form->sent ) > wantedsize - gotsize) { memcpy(buffer + gotsize , form->data->line + form->sent, wantedsize - gotsize); @@ -1537,168 +1510,18 @@ char *Curl_formpostheader(void *formp, size_t *len) return header; } - -#ifdef _FORM_DEBUG -int FormAddTest(const char * errormsg, - struct curl_httppost **httppost, - struct curl_httppost **last_post, - ...) -{ - int result; - va_list arg; - va_start(arg, last_post); - if((result = FormAdd(httppost, last_post, arg))) - fprintf (stderr, "ERROR doing FormAdd ret: %d action: %s\n", result, - errormsg); - va_end(arg); - return result; -} - - -int main(int argc, argv_item_t argv[]) +/* + * formboundary() creates a suitable boundary string and returns an allocated + * one. + */ +static char *formboundary(struct SessionHandle *data) { - char name1[] = "simple_COPYCONTENTS"; - char name2[] = "COPYCONTENTS_+_CONTENTTYPE"; - char name3[] = "PTRNAME_+_NAMELENGTH_+_COPYNAME_+_CONTENTSLENGTH"; - char name4[] = "simple_PTRCONTENTS"; - char name5[] = "PTRCONTENTS_+_CONTENTSLENGTH"; - char name6[] = "PTRCONTENTS_+_CONTENTSLENGTH_+_CONTENTTYPE"; - char name7[] = "FILE1_+_CONTENTTYPE"; - char name8[] = "FILE1_+_FILE2"; - char name9[] = "FILE1_+_FILE2_+_FILE3"; - char name10[] = "ARRAY: FILE1_+_FILE2_+_FILE3"; - char name11[] = "FILECONTENT"; - char value1[] = "value for simple COPYCONTENTS"; - char value2[] = "value for COPYCONTENTS + CONTENTTYPE"; - char value3[] = "value for PTRNAME + NAMELENGTH + COPYNAME + CONTENTSLENGTH"; - char value4[] = "value for simple PTRCONTENTS"; - char value5[] = "value for PTRCONTENTS + CONTENTSLENGTH"; - char value6[] = "value for PTRCONTENTS + CONTENTSLENGTH + CONTENTTYPE"; - char value7[] = "formdata.h"; - char value8[] = "Makefile.b32"; - char type2[] = "image/gif"; - char type6[] = "text/plain"; - char type7[] = "text/html"; - int name3length = strlen(name3); - int value3length = strlen(value3); - int value5length = strlen(value5); - int value6length = strlen(value6); - int errors = 0; - CURLcode rc; - curl_off_t size; - size_t nread; - char buffer[4096]; - struct curl_httppost *httppost=NULL; - struct curl_httppost *last_post=NULL; - struct curl_forms forms[4]; - - struct FormData *form; - struct Form formread; - - (void) argc; - (void) argv; - - Curl_srand(); /* Because we do not call curl_global_init() here. */ - - if(FormAddTest("simple COPYCONTENTS test", &httppost, &last_post, - CURLFORM_COPYNAME, name1, CURLFORM_COPYCONTENTS, value1, - CURLFORM_END)) - ++errors; - if(FormAddTest("COPYCONTENTS + CONTENTTYPE test", &httppost, &last_post, - CURLFORM_COPYNAME, name2, CURLFORM_COPYCONTENTS, value2, - CURLFORM_CONTENTTYPE, type2, CURLFORM_END)) - ++errors; - /* make null character at start to check that contentslength works - correctly */ - name3[1] = '\0'; - value3[1] = '\0'; - if(FormAddTest("PTRNAME + NAMELENGTH + COPYNAME + CONTENTSLENGTH test", - &httppost, &last_post, - CURLFORM_PTRNAME, name3, CURLFORM_COPYCONTENTS, value3, - CURLFORM_CONTENTSLENGTH, value3length, - CURLFORM_NAMELENGTH, name3length, CURLFORM_END)) - ++errors; - if(FormAddTest("simple PTRCONTENTS test", &httppost, &last_post, - CURLFORM_COPYNAME, name4, CURLFORM_PTRCONTENTS, value4, - CURLFORM_END)) - ++errors; - /* make null character at start to check that contentslength works - correctly */ - value5[1] = '\0'; - if(FormAddTest("PTRCONTENTS + CONTENTSLENGTH test", &httppost, &last_post, - CURLFORM_COPYNAME, name5, CURLFORM_PTRCONTENTS, value5, - CURLFORM_CONTENTSLENGTH, value5length, CURLFORM_END)) - ++errors; - /* make null character at start to check that contentslength works - correctly */ - value6[1] = '\0'; - if(FormAddTest("PTRCONTENTS + CONTENTSLENGTH + CONTENTTYPE test", - &httppost, &last_post, - CURLFORM_COPYNAME, name6, CURLFORM_PTRCONTENTS, value6, - CURLFORM_CONTENTSLENGTH, value6length, - CURLFORM_CONTENTTYPE, type6, CURLFORM_END)) - ++errors; - if(FormAddTest("FILE + CONTENTTYPE test", &httppost, &last_post, - CURLFORM_COPYNAME, name7, CURLFORM_FILE, value7, - CURLFORM_CONTENTTYPE, type7, CURLFORM_END)) - ++errors; - if(FormAddTest("FILE1 + FILE2 test", &httppost, &last_post, - CURLFORM_COPYNAME, name8, CURLFORM_FILE, value7, - CURLFORM_FILE, value8, CURLFORM_END)) - ++errors; - if(FormAddTest("FILE1 + FILE2 + FILE3 test", &httppost, &last_post, - CURLFORM_COPYNAME, name9, CURLFORM_FILE, value7, - CURLFORM_FILE, value8, CURLFORM_FILE, value7, CURLFORM_END)) - ++errors; - forms[0].option = CURLFORM_FILE; - forms[0].value = value7; - forms[1].option = CURLFORM_FILE; - forms[1].value = value8; - forms[2].option = CURLFORM_FILE; - forms[2].value = value7; - forms[3].option = CURLFORM_END; - if(FormAddTest("FILE1 + FILE2 + FILE3 ARRAY test", &httppost, &last_post, - CURLFORM_COPYNAME, name10, CURLFORM_ARRAY, forms, - CURLFORM_END)) - ++errors; - if(FormAddTest("FILECONTENT test", &httppost, &last_post, - CURLFORM_COPYNAME, name11, CURLFORM_FILECONTENT, value7, - CURLFORM_END)) - ++errors; - - rc = Curl_getFormData(&form, httppost, NULL, &size); - if(rc != CURLE_OK) { - if(rc != CURLE_READ_ERROR) { - const char *errortext = curl_easy_strerror(rc); - fprintf(stdout, "\n==> Curl_getFormData error: %s\n", errortext); - } - return 0; - } - - Curl_FormInit(&formread, form); - - do { - nread = Curl_FormReader(buffer, 1, sizeof(buffer), - (FILE *)&formread); - - if(nread < 1) - break; - fwrite(buffer, nread, 1, stdout); - } while(1); - - fprintf(stdout, "size: "); - fprintf(stdout, "%" FORMAT_OFF_T, size); - fprintf(stdout, "\n"); - if(errors) - fprintf(stdout, "\n==> %d Test(s) failed!\n", errors); - else - fprintf(stdout, "\nAll Tests seem to have worked (please check output)\n"); - - return 0; + /* 24 dashes and 16 hexadecimal digits makes 64 bit (18446744073709551615) + combinations */ + return aprintf("------------------------%08x%08x", + Curl_rand(data), Curl_rand(data)); } -#endif /* _FORM_DEBUG */ - #else /* CURL_DISABLE_HTTP */ CURLFORMcode curl_formadd(struct curl_httppost **httppost, struct curl_httppost **last_post, @@ -1724,37 +1547,5 @@ void curl_formfree(struct curl_httppost *form) /* does nothing HTTP is disabled */ } -#endif /* CURL_DISABLE_HTTP */ - -#if !defined(CURL_DISABLE_HTTP) || defined(USE_SSLEAY) - -/* - * Curl_FormBoundary() creates a suitable boundary string and returns an - * allocated one. This is also used by SSL-code so it must be present even - * if HTTP is disabled! - */ -char *Curl_FormBoundary(void) -{ - char *retstring; - size_t i; - - static const char table16[]="0123456789abcdef"; - - retstring = malloc(BOUNDARY_LENGTH+1); - - if(!retstring) - return NULL; /* failed */ - - strcpy(retstring, "----------------------------"); - - for(i=strlen(retstring); i<BOUNDARY_LENGTH; i++) - retstring[i] = table16[Curl_rand()%16]; - - /* 28 dashes and 12 hexadecimal digits makes 12^16 (184884258895036416) - combinations */ - retstring[BOUNDARY_LENGTH]=0; /* zero terminate */ - - return retstring; -} -#endif /* !defined(CURL_DISABLE_HTTP) || defined(USE_SSLEAY) */ +#endif /* !defined(CURL_DISABLE_HTTP) */ |