| /*************************************************************************** |
| * _ _ ____ _ |
| * Project ___| | | | _ \| | |
| * / __| | | | |_) | | |
| * | (__| |_| | _ <| |___ |
| * \___|\___/|_| \_\_____| |
| * |
| * Copyright (C) 1998 - 2004, Daniel Stenberg, <[email protected]>, et al. |
| * |
| * This software is licensed as described in the file COPYING, which |
| * you should have received as part of this distribution. The terms |
| * are also available at http://curl.haxx.se/docs/copyright.html. |
| * |
| * You may opt to use, copy, modify, merge, publish, distribute and/or sell |
| * copies of the Software, and permit persons to whom the Software is |
| * furnished to do so, under the terms of the COPYING file. |
| * |
| * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY |
| * KIND, either express or implied. |
| * |
| * $Id$ |
| ***************************************************************************/ |
| |
| /* |
| Debug the form generator stand-alone by compiling this source file with: |
| |
| gcc -DHAVE_CONFIG_H -I../ -g -D_FORM_DEBUG -o formdata -I../include formdata.c strequal.c |
| |
| 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: text/plain |
| vlue for PTRCOTNENTS + CONTENTSLENGTH + CONTENTTYPE |
| (or you might see v^@lue at the start) |
| |
| Content-Disposition: form-data; name="FILE1_+_CONTENTTYPE"; filename="inet_ntoa_r.h" |
| Content-Type: text/html |
| ... |
| |
| Content-Disposition: form-data; name="FILE1_+_FILE2" |
| Content-Type: multipart/mixed, boundary=curlz1s0dkticx49MV1KGcYP5cvfSsz |
| ... |
| Content-Disposition: attachment; filename="inet_ntoa_r.h" |
| Content-Type: text/plain |
| ... |
| Content-Disposition: attachment; filename="Makefile.b32.resp" |
| Content-Type: text/plain |
| ... |
| |
| Content-Disposition: form-data; name="FILE1_+_FILE2_+_FILE3" |
| Content-Type: multipart/mixed, boundary=curlirkYPmPwu6FrJ1vJ1u1BmtIufh1 |
| ... |
| Content-Disposition: attachment; filename="inet_ntoa_r.h" |
| Content-Type: text/plain |
| ... |
| Content-Disposition: attachment; filename="Makefile.b32.resp" |
| Content-Type: text/plain |
| ... |
| Content-Disposition: attachment; filename="inet_ntoa_r.h" |
| Content-Type: text/plain |
| ... |
| |
| |
| Content-Disposition: form-data; name="ARRAY: FILE1_+_FILE2_+_FILE3" |
| Content-Type: multipart/mixed, boundary=curlirkYPmPwu6FrJ1vJ1u1BmtIufh1 |
| ... |
| Content-Disposition: attachment; filename="inet_ntoa_r.h" |
| Content-Type: text/plain |
| ... |
| Content-Disposition: attachment; filename="Makefile.b32.resp" |
| Content-Type: text/plain |
| ... |
| Content-Disposition: attachment; filename="inet_ntoa_r.h" |
| Content-Type: text/plain |
| ... |
| |
| Content-Disposition: form-data; name="FILECONTENT" |
| ... |
| |
| For the old FormParse used by curl_formparse use: |
| |
| gcc -DHAVE_CONFIG_H -I../ -g -D_OLD_FORM_DEBUG -o formdata -I../include formdata.c strequal.c |
| |
| run the 'formdata' executable and make sure the output is ok! |
| |
| try './formdata "name=Daniel" "poo=noo" "foo=bar"' and similarly |
| |
| */ |
| |
| #include "setup.h" |
| |
| #ifndef CURL_DISABLE_HTTP |
| |
| #include <stdio.h> |
| #include <stdlib.h> |
| #include <string.h> |
| #include <stdarg.h> |
| |
| #include <time.h> |
| |
| #include <curl/curl.h> |
| #include "formdata.h" |
| |
| #include "strequal.h" |
| |
| /* The last #include file should be: */ |
| #ifdef CURLDEBUG |
| #include "memdebug.h" |
| #endif |
| |
| /* Length of the random boundary string. */ |
| #define BOUNDARY_LENGTH 40 |
| |
| /* What kind of Content-Type to use on un-specified files with unrecognized |
| extensions. */ |
| #define HTTPPOST_CONTENTTYPE_DEFAULT "application/octet-stream" |
| |
| /* This is a silly duplicate of the function in main.c to enable this source |
| to compile stand-alone for better debugging */ |
| static void GetStr(char **string, |
| const char *value) |
| { |
| if(*string) |
| free(*string); |
| *string = strdup(value); |
| } |
| |
| /*************************************************************************** |
| * |
| * FormParse() |
| * |
| * Reads a 'name=value' paramter and builds the appropriate linked list. |
| * |
| * Specify files to upload with 'name=@filename'. Supports specified |
| * given Content-Type of the files. Such as ';type=<content-type>'. |
| * |
| * You may specify more than one file for a single name (field). Specify |
| * multiple files by writing it like: |
| * |
| * 'name=@filename,filename2,filename3' |
| * |
| * If you want content-types specified for each too, write them like: |
| * |
| * 'name=@filename;type=image/gif,filename2,filename3' |
| * |
| ***************************************************************************/ |
| |
| #define FORM_FILE_SEPARATOR ',' |
| #define FORM_TYPE_SEPARATOR ';' |
| |
| static |
| int FormParse(char *input, |
| struct curl_httppost **httppost, |
| struct curl_httppost **last_post) |
| { |
| /* nextarg MUST be a string in the format 'name=contents' and we'll |
| build a linked list with the info */ |
| char name[256]; |
| char *contents; |
| char major[128]; |
| char minor[128]; |
| long flags = 0; |
| char *contp; |
| const char *type = NULL; |
| char *prevtype = NULL; |
| char *sep; |
| char *sep2; |
| struct curl_httppost *post; |
| struct curl_httppost *subpost; /* a sub-node */ |
| unsigned int i; |
| |
| /* Preallocate contents to the length of input to make sure we don't |
| overwrite anything. */ |
| contents = malloc(strlen(input)); |
| contents[0] = '\000'; |
| |
| if(1 <= sscanf(input, "%255[^=]=%[^\n]", name, contents)) { |
| /* the input was using the correct format */ |
| contp = contents; |
| |
| if('@' == contp[0]) { |
| /* we use the @-letter to indicate file name(s) */ |
| |
| flags = HTTPPOST_FILENAME; |
| contp++; |
| |
| post=NULL; |
| |
| do { |
| /* since this was a file, it may have a content-type specifier |
| at the end too */ |
| |
| sep=strchr(contp, FORM_TYPE_SEPARATOR); |
| sep2=strchr(contp, FORM_FILE_SEPARATOR); |
| |
| /* pick the closest */ |
| if(sep2 && (sep2 < sep)) { |
| sep = sep2; |
| |
| /* no type was specified! */ |
| } |
| if(sep) { |
| |
| /* if we got here on a comma, don't do much */ |
| if(FORM_FILE_SEPARATOR != *sep) |
| type = strstr(sep+1, "type="); |
| else |
| type=NULL; |
| |
| *sep=0; /* terminate file name at separator */ |
| |
| if(type) { |
| type += strlen("type="); |
| |
| if(2 != sscanf(type, "%127[^/]/%127[^,\n]", |
| major, minor)) { |
| free(contents); |
| return 2; /* illegal content-type syntax! */ |
| } |
| /* now point beyond the content-type specifier */ |
| sep = (char *)type + strlen(major)+strlen(minor)+1; |
| |
| /* find the following comma */ |
| sep=strchr(sep, FORM_FILE_SEPARATOR); |
| } |
| } |
| else { |
| type=NULL; |
| sep=strchr(contp, FORM_FILE_SEPARATOR); |
| } |
| if(sep) { |
| /* the next file name starts here */ |
| *sep =0; |
| sep++; |
| } |
| if(!type) { |
| /* |
| * No type was specified, we scan through a few well-known |
| * extensions and pick the first we match! |
| */ |
| struct ContentType { |
| const char *extension; |
| const char *type; |
| }; |
| static struct ContentType ctts[]={ |
| {".gif", "image/gif"}, |
| {".jpg", "image/jpeg"}, |
| {".jpeg", "image/jpeg"}, |
| {".txt", "text/plain"}, |
| {".html", "text/html"} |
| }; |
| |
| if(prevtype) |
| /* default to the previously set/used! */ |
| type = prevtype; |
| else |
| /* It seems RFC1867 defines no Content-Type to default to |
| text/plain so we don't actually need to set this: */ |
| type = HTTPPOST_CONTENTTYPE_DEFAULT; |
| |
| for(i=0; i<sizeof(ctts)/sizeof(ctts[0]); i++) { |
| if(strlen(contp) >= strlen(ctts[i].extension)) { |
| if(strequal(contp + |
| strlen(contp) - strlen(ctts[i].extension), |
| ctts[i].extension)) { |
| type = ctts[i].type; |
| break; |
| } |
| } |
| } |
| /* we have a type by now */ |
| } |
| |
| if(NULL == post) { |
| /* For the first file name, we allocate and initiate the main list |
| node */ |
| |
| post = (struct curl_httppost *)malloc(sizeof(struct curl_httppost)); |
| if(post) { |
| memset(post, 0, sizeof(struct curl_httppost)); |
| GetStr(&post->name, name); /* get the name */ |
| GetStr(&post->contents, contp); /* get the contents */ |
| post->contentslength = 0; |
| post->flags = flags; |
| if(type) { |
| GetStr(&post->contenttype, (char *)type); /* get type */ |
| prevtype=post->contenttype; /* point to the allocated string! */ |
| } |
| /* make the previous point to this */ |
| if(*last_post) |
| (*last_post)->next = post; |
| else |
| (*httppost) = post; |
| |
| (*last_post) = post; |
| } |
| |
| } |
| else { |
| /* we add a file name to the previously allocated node, known as |
| 'post' now */ |
| subpost =(struct curl_httppost *) |
| malloc(sizeof(struct curl_httppost)); |
| if(subpost) { |
| memset(subpost, 0, sizeof(struct curl_httppost)); |
| GetStr(&subpost->name, name); /* get the name */ |
| GetStr(&subpost->contents, contp); /* get the contents */ |
| subpost->contentslength = 0; |
| subpost->flags = flags; |
| if(type) { |
| GetStr(&subpost->contenttype, (char *)type); /* get type */ |
| prevtype=subpost->contenttype; /* point to allocated string! */ |
| } |
| /* now, point our 'more' to the original 'more' */ |
| subpost->more = post->more; |
| |
| /* then move the original 'more' to point to ourselves */ |
| post->more = subpost; |
| } |
| } |
| contp = sep; /* move the contents pointer to after the separator */ |
| } while(sep && *sep); /* loop if there's another file name */ |
| } |
| else { |
| post = (struct curl_httppost *)malloc(sizeof(struct curl_httppost)); |
| if(post) { |
| memset(post, 0, sizeof(struct curl_httppost)); |
| GetStr(&post->name, name); /* get the name */ |
| if( contp[0]=='<' ) { |
| GetStr(&post->contents, contp+1); /* get the contents */ |
| post->contentslength = 0; |
| post->flags = HTTPPOST_READFILE; |
| } |
| else { |
| GetStr(&post->contents, contp); /* get the contents */ |
| post->contentslength = 0; |
| post->flags = 0; |
| } |
| |
| /* make the previous point to this */ |
| if(*last_post) |
| (*last_post)->next = post; |
| else |
| (*httppost) = post; |
| |
| (*last_post) = post; |
| } |
| |
| } |
| |
| } |
| else { |
| free(contents); |
| return 1; |
| } |
| free(contents); |
| return 0; |
| } |
| |
| int curl_formparse(char *input, |
| struct curl_httppost **httppost, |
| struct curl_httppost **last_post) |
| { |
| return FormParse(input, httppost, last_post); |
| } |
| |
| /*************************************************************************** |
| * |
| * AddHttpPost() |
| * |
| * Adds a HttpPost structure to the list, if parent_post is given becomes |
| * a subpost of parent_post instead of a direct list element. |
| * |
| * Returns newly allocated HttpPost on success and NULL if malloc failed. |
| * |
| ***************************************************************************/ |
| static struct curl_httppost * |
| AddHttpPost(char * name, long namelength, |
| char * value, long contentslength, |
| |
| /* CMC: Added support for buffer uploads */ |
| char * buffer, long bufferlength, |
| |
| char *contenttype, |
| long flags, |
| struct curl_slist* contentHeader, |
| char *showfilename, |
| struct curl_httppost *parent_post, |
| struct curl_httppost **httppost, |
| struct curl_httppost **last_post) |
| { |
| struct curl_httppost *post; |
| post = (struct curl_httppost *)malloc(sizeof(struct curl_httppost)); |
| if(post) { |
| memset(post, 0, sizeof(struct curl_httppost)); |
| post->name = name; |
| post->namelength = name?(namelength?namelength:(long)strlen(name)):0; |
| post->contents = value; |
| post->contentslength = contentslength; |
| |
| /* CMC: Added support for buffer uploads */ |
| post->buffer = buffer; |
| post->bufferlength = bufferlength; |
| |
| post->contenttype = contenttype; |
| post->contentheader = contentHeader; |
| post->showfilename = showfilename; |
| post->flags = flags; |
| } |
| else |
| return NULL; |
| |
| if (parent_post) { |
| /* now, point our 'more' to the original 'more' */ |
| post->more = parent_post->more; |
| |
| /* then move the original 'more' to point to ourselves */ |
| parent_post->more = post; |
| } |
| else { |
| /* make the previous point to this */ |
| if(*last_post) |
| (*last_post)->next = post; |
| else |
| (*httppost) = post; |
| |
| (*last_post) = post; |
| } |
| return post; |
| } |
| |
| /*************************************************************************** |
| * |
| * AddFormInfo() |
| * |
| * Adds a FormInfo structure to the list presented by parent_form_info. |
| * |
| * Returns newly allocated FormInfo on success and NULL if malloc failed/ |
| * parent_form_info is NULL. |
| * |
| ***************************************************************************/ |
| static FormInfo * AddFormInfo(char *value, |
| char *contenttype, |
| FormInfo *parent_form_info) |
| { |
| FormInfo *form_info; |
| form_info = (FormInfo *)malloc(sizeof(FormInfo)); |
| if(form_info) { |
| memset(form_info, 0, sizeof(FormInfo)); |
| if (value) |
| form_info->value = value; |
| if (contenttype) |
| form_info->contenttype = contenttype; |
| form_info->flags = HTTPPOST_FILENAME; |
| } |
| else |
| return NULL; |
| |
| if (parent_form_info) { |
| /* now, point our 'more' to the original 'more' */ |
| form_info->more = parent_form_info->more; |
| |
| /* then move the original 'more' to point to ourselves */ |
| parent_form_info->more = form_info; |
| } |
| else |
| return NULL; |
| |
| return form_info; |
| } |
| |
| /*************************************************************************** |
| * |
| * ContentTypeForFilename() |
| * |
| * Provides content type for filename if one of the known types (else |
| * (either the prevtype or the default is returned). |
| * |
| * Returns some valid contenttype for filename. |
| * |
| ***************************************************************************/ |
| static const char * ContentTypeForFilename (const char *filename, |
| const char *prevtype) |
| { |
| const char *contenttype = NULL; |
| unsigned int i; |
| /* |
| * No type was specified, we scan through a few well-known |
| * extensions and pick the first we match! |
| */ |
| struct ContentType { |
| const char *extension; |
| const char *type; |
| }; |
| static struct ContentType ctts[]={ |
| {".gif", "image/gif"}, |
| {".jpg", "image/jpeg"}, |
| {".jpeg", "image/jpeg"}, |
| {".txt", "text/plain"}, |
| {".html", "text/html"} |
| }; |
| |
| if(prevtype) |
| /* default to the previously set/used! */ |
| contenttype = prevtype; |
| else |
| /* It seems RFC1867 defines no Content-Type to default to |
| text/plain so we don't actually need to set this: */ |
| contenttype = HTTPPOST_CONTENTTYPE_DEFAULT; |
| |
| for(i=0; i<sizeof(ctts)/sizeof(ctts[0]); i++) { |
| if(strlen(filename) >= strlen(ctts[i].extension)) { |
| if(strequal(filename + |
| strlen(filename) - strlen(ctts[i].extension), |
| ctts[i].extension)) { |
| contenttype = ctts[i].type; |
| break; |
| } |
| } |
| } |
| /* we have a contenttype by now */ |
| return contenttype; |
| } |
| |
| /*************************************************************************** |
| * |
| * AllocAndCopy() |
| * |
| * Copies the data currently available under *buffer using newly allocated |
| * buffer (that becomes *buffer). Uses buffer_length if not null, else |
| * uses strlen to determine the length of the buffer to be copied |
| * |
| * Returns 0 on success and 1 if the malloc failed. |
| * |
| ***************************************************************************/ |
| static int AllocAndCopy (char **buffer, int buffer_length) |
| { |
| const char *src = *buffer; |
| int length, add = 0; |
| if (buffer_length) |
| length = buffer_length; |
| else { |
| length = strlen(*buffer); |
| add = 1; |
| } |
| *buffer = (char*)malloc(length+add); |
| if (!*buffer) |
| return 1; |
| memcpy(*buffer, src, length); |
| /* if length unknown do null termination */ |
| if (add) |
| (*buffer)[length] = '\0'; |
| return 0; |
| } |
| |
| /*************************************************************************** |
| * |
| * FormAdd() |
| * |
| * Stores a 'name=value' formpost parameter and builds the appropriate |
| * linked list. |
| * |
| * Has two principal functionalities: using files and byte arrays as |
| * post parts. Byte arrays are either copied or just the pointer is stored |
| * (as the user requests) while for files only the filename and not the |
| * content is stored. |
| * |
| * While you may have only one byte array for each name, multiple filenames |
| * are allowed (and because of this feature CURLFORM_END is needed after |
| * using CURLFORM_FILE). |
| * |
| * Examples: |
| * |
| * Simple name/value pair with copied contents: |
| * curl_formadd (&post, &last, CURLFORM_COPYNAME, "name", |
| * CURLFORM_COPYCONTENTS, "value", CURLFORM_END); |
| * |
| * name/value pair where only the content pointer is remembered: |
| * curl_formadd (&post, &last, CURLFORM_COPYNAME, "name", |
| * CURLFORM_PTRCONTENTS, ptr, CURLFORM_CONTENTSLENGTH, 10, CURLFORM_END); |
| * (if CURLFORM_CONTENTSLENGTH is missing strlen () is used) |
| * |
| * storing a filename (CONTENTTYPE is optional!): |
| * curl_formadd (&post, &last, CURLFORM_COPYNAME, "name", |
| * CURLFORM_FILE, "filename1", CURLFORM_CONTENTTYPE, "plain/text", |
| * CURLFORM_END); |
| * |
| * storing multiple filenames: |
| * curl_formadd (&post, &last, CURLFORM_COPYNAME, "name", |
| * CURLFORM_FILE, "filename1", CURLFORM_FILE, "filename2", CURLFORM_END); |
| * |
| * Returns: |
| * CURL_FORMADD_OK on success |
| * CURL_FORMADD_MEMORY if the FormInfo allocation fails |
| * CURL_FORMADD_OPTION_TWICE if one option is given twice for one Form |
| * 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_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 |
| * |
| ***************************************************************************/ |
| |
| static |
| CURLFORMcode FormAdd(struct curl_httppost **httppost, |
| struct curl_httppost **last_post, |
| va_list params) |
| { |
| FormInfo *first_form, *current_form, *form; |
| CURLFORMcode return_value = CURL_FORMADD_OK; |
| const char *prevtype = NULL; |
| struct curl_httppost *post = NULL; |
| CURLformoption option; |
| struct curl_forms *forms = NULL; |
| char *array_value=NULL; /* value read from an array */ |
| |
| /* This is a state variable, that if TRUE means that we're parsing an |
| array that we got passed to us. If FALSE we're parsing the input |
| va_list arguments. */ |
| bool array_state = FALSE; |
| |
| /* |
| * We need to allocate the first struct to fill in. |
| */ |
| first_form = (FormInfo *)malloc(sizeof(struct FormInfo)); |
| if(first_form) { |
| memset(first_form, 0, sizeof(FormInfo)); |
| current_form = first_form; |
| } |
| else |
| return CURL_FORMADD_MEMORY; |
| |
| /* |
| * Loop through all the options set. |
| */ |
| while (1) { |
| |
| /* break if we have an error to report */ |
| if (return_value != CURL_FORMADD_OK) |
| break; |
| |
| /* first see if we have more parts of the array param */ |
| if ( array_state ) { |
| /* get the upcoming option from the given array */ |
| option = forms->option; |
| array_value = (char *)forms->value; |
| |
| forms++; /* advance this to next entry */ |
| if (CURLFORM_END == option) { |
| /* end of array state */ |
| array_state = FALSE; |
| continue; |
| } |
| } |
| else { |
| /* This is not array-state, get next option */ |
| option = va_arg(params, CURLformoption); |
| if (CURLFORM_END == option) |
| break; |
| } |
| |
| switch (option) { |
| case CURLFORM_ARRAY: |
| if(array_state) |
| /* we don't support an array from within an array */ |
| return_value = CURL_FORMADD_ILLEGAL_ARRAY; |
| else { |
| forms = va_arg(params, struct curl_forms *); |
| if (forms) |
| array_state = TRUE; |
| else |
| return_value = CURL_FORMADD_NULL; |
| } |
| break; |
| |
| /* |
| * Set the Name property. |
| */ |
| case CURLFORM_PTRNAME: |
| current_form->flags |= HTTPPOST_PTRNAME; /* fall through */ |
| case CURLFORM_COPYNAME: |
| if (current_form->name) |
| return_value = CURL_FORMADD_OPTION_TWICE; |
| else { |
| char *name = array_state? |
| array_value:va_arg(params, char *); |
| if (name) |
| current_form->name = name; /* store for the moment */ |
| else |
| return_value = CURL_FORMADD_NULL; |
| } |
| break; |
| case CURLFORM_NAMELENGTH: |
| if (current_form->namelength) |
| return_value = CURL_FORMADD_OPTION_TWICE; |
| else |
| current_form->namelength = |
| array_state?(long)array_value:va_arg(params, long); |
| break; |
| |
| /* |
| * Set the contents property. |
| */ |
| case CURLFORM_PTRCONTENTS: |
| current_form->flags |= HTTPPOST_PTRCONTENTS; /* fall through */ |
| case CURLFORM_COPYCONTENTS: |
| if (current_form->value) |
| return_value = CURL_FORMADD_OPTION_TWICE; |
| else { |
| char *value = |
| array_state?array_value:va_arg(params, char *); |
| if (value) |
| current_form->value = value; /* store for the moment */ |
| else |
| return_value = CURL_FORMADD_NULL; |
| } |
| break; |
| case CURLFORM_CONTENTSLENGTH: |
| if (current_form->contentslength) |
| return_value = CURL_FORMADD_OPTION_TWICE; |
| else |
| current_form->contentslength = |
| array_state?(long)array_value:va_arg(params, long); |
| break; |
| |
| /* Get contents from a given file name */ |
| case CURLFORM_FILECONTENT: |
| if (current_form->flags != 0) |
| return_value = CURL_FORMADD_OPTION_TWICE; |
| else { |
| char *filename = array_state? |
| array_value:va_arg(params, char *); |
| if (filename) { |
| current_form->value = strdup(filename); |
| current_form->flags |= HTTPPOST_READFILE; |
| } |
| else |
| return_value = CURL_FORMADD_NULL; |
| } |
| break; |
| |
| /* We upload a file */ |
| case CURLFORM_FILE: |
| { |
| char *filename = array_state?array_value: |
| va_arg(params, char *); |
| |
| if (current_form->value) { |
| if (current_form->flags & HTTPPOST_FILENAME) { |
| if (filename) { |
| if (!(current_form = AddFormInfo(strdup(filename), |
| NULL, current_form))) |
| 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); |
| else |
| return_value = CURL_FORMADD_NULL; |
| current_form->flags |= HTTPPOST_FILENAME; |
| } |
| break; |
| } |
| |
| /* CMC: Added support for buffer uploads */ |
| case CURLFORM_BUFFER: |
| { |
| 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))) |
| 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); |
| else |
| return_value = CURL_FORMADD_NULL; |
| current_form->flags |= HTTPPOST_BUFFER; |
| } |
| break; |
| } |
| |
| /* CMC: Added support for buffer uploads */ |
| case CURLFORM_BUFFERPTR: |
| current_form->flags |= HTTPPOST_PTRBUFFER; |
| if (current_form->buffer) |
| return_value = CURL_FORMADD_OPTION_TWICE; |
| else { |
| char *buffer = |
| array_state?array_value:va_arg(params, char *); |
| if (buffer) |
| current_form->buffer = buffer; /* store for the moment */ |
| else |
| return_value = CURL_FORMADD_NULL; |
| } |
| break; |
| |
| /* CMC: Added support for buffer uploads */ |
| case CURLFORM_BUFFERLENGTH: |
| if (current_form->bufferlength) |
| return_value = CURL_FORMADD_OPTION_TWICE; |
| else |
| current_form->bufferlength = |
| array_state?(long)array_value:va_arg(params, long); |
| break; |
| |
| case CURLFORM_CONTENTTYPE: |
| { |
| char *contenttype = |
| array_state?array_value:va_arg(params, char *); |
| if (current_form->contenttype) { |
| if (current_form->flags & HTTPPOST_FILENAME) { |
| if (contenttype) { |
| if (!(current_form = AddFormInfo(NULL, |
| strdup(contenttype), |
| current_form))) |
| return_value = CURL_FORMADD_MEMORY; |
| } |
| else |
| return_value = CURL_FORMADD_NULL; |
| } |
| else |
| return_value = CURL_FORMADD_OPTION_TWICE; |
| } |
| else { |
| if (contenttype) |
| current_form->contenttype = strdup(contenttype); |
| else |
| return_value = CURL_FORMADD_NULL; |
| } |
| break; |
| } |
| case CURLFORM_CONTENTHEADER: |
| { |
| /* this "cast increases required alignment of target type" but |
| we consider it OK anyway */ |
| struct curl_slist* list = array_state? |
| (struct curl_slist*)array_value: |
| va_arg(params, struct curl_slist*); |
| |
| if( current_form->contentheader ) |
| return_value = CURL_FORMADD_OPTION_TWICE; |
| else |
| current_form->contentheader = list; |
| |
| break; |
| } |
| case CURLFORM_FILENAME: |
| { |
| char *filename = array_state?array_value: |
| va_arg(params, char *); |
| if( current_form->showfilename ) |
| return_value = CURL_FORMADD_OPTION_TWICE; |
| else |
| current_form->showfilename = strdup(filename); |
| break; |
| } |
| default: |
| return_value = CURL_FORMADD_UNKNOWN_OPTION; |
| } |
| } |
| |
| if(CURL_FORMADD_OK == return_value) { |
| /* go through the list, check for copleteness and if everything is |
| * alright add the HttpPost item otherwise set return_value accordingly */ |
| |
| post = NULL; |
| 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) ) || |
| |
| /* CMC: Added support for buffer uploads */ |
| ( (!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 ) { |
| /* our contenttype is missing */ |
| form->contenttype |
| = strdup(ContentTypeForFilename(form->value, prevtype)); |
| } |
| if ( !(form->flags & HTTPPOST_PTRNAME) && |
| (form == first_form) ) { |
| /* copy name (without strdup; possibly contains null characters) */ |
| if (AllocAndCopy(&form->name, form->namelength)) { |
| return_value = CURL_FORMADD_MEMORY; |
| break; |
| } |
| } |
| if ( !(form->flags & HTTPPOST_FILENAME) && |
| !(form->flags & HTTPPOST_READFILE) && |
| !(form->flags & HTTPPOST_PTRCONTENTS) && |
| |
| /* CMC: Added support for buffer uploads */ |
| !(form->flags & HTTPPOST_PTRBUFFER) ) { |
| |
| /* copy value (without strdup; possibly contains null characters) */ |
| if (AllocAndCopy(&form->value, form->contentslength)) { |
| return_value = CURL_FORMADD_MEMORY; |
| break; |
| } |
| } |
| post = AddHttpPost(form->name, form->namelength, |
| form->value, form->contentslength, |
| |
| /* CMC: Added support for buffer uploads */ |
| form->buffer, form->bufferlength, |
| |
| form->contenttype, form->flags, |
| form->contentheader, form->showfilename, |
| post, httppost, |
| last_post); |
| |
| if(!post) |
| return_value = CURL_FORMADD_MEMORY; |
| |
| if (form->contenttype) |
| prevtype = form->contenttype; |
| } |
| } |
| } |
| |
| /* 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); |
| } |
| |
| return return_value; |
| } |
| |
| CURLFORMcode curl_formadd(struct curl_httppost **httppost, |
| struct curl_httppost **last_post, |
| ...) |
| { |
| va_list arg; |
| CURLFORMcode result; |
| va_start(arg, last_post); |
| result = FormAdd(httppost, last_post, arg); |
| va_end(arg); |
| return result; |
| } |
| |
| static int AddFormData(struct FormData **formp, |
| const void *line, |
| long length) |
| { |
| struct FormData *newform = (struct FormData *) |
| malloc(sizeof(struct FormData)); |
| newform->next = NULL; |
| |
| /* we make it easier for plain strings: */ |
| if(!length) |
| length = strlen((char *)line); |
| |
| newform->line = (char *)malloc(length+1); |
| memcpy(newform->line, line, length); |
| newform->length = length; |
| newform->line[length]=0; /* zero terminate for easier debugging */ |
| |
| if(*formp) { |
| (*formp)->next = newform; |
| *formp = newform; |
| } |
| else |
| *formp = newform; |
| |
| return length; |
| } |
| |
| |
| static int AddFormDataf(struct FormData **formp, |
| const char *fmt, ...) |
| { |
| char s[4096]; |
| va_list ap; |
| va_start(ap, fmt); |
| vsprintf(s, fmt, ap); |
| va_end(ap); |
| |
| return AddFormData(formp, s, 0); |
| } |
| |
| |
| char *Curl_FormBoundary(void) |
| { |
| char *retstring; |
| static int randomizer=0; /* this is just so that two boundaries within |
| the same form won't be identical */ |
| int i; |
| |
| static char table16[]="abcdef0123456789"; |
| |
| retstring = (char *)malloc(BOUNDARY_LENGTH+1); |
| |
| if(!retstring) |
| return NULL; /* failed */ |
| |
| srand(time(NULL)+randomizer++); /* seed */ |
| |
| strcpy(retstring, "----------------------------"); |
| |
| for(i=strlen(retstring); i<BOUNDARY_LENGTH; i++) |
| retstring[i] = table16[rand()%16]; |
| |
| /* 28 dashes and 12 hexadecimal digits makes 12^16 (184884258895036416) |
| combinations */ |
| retstring[BOUNDARY_LENGTH]=0; /* zero terminate */ |
| |
| return retstring; |
| } |
| |
| /* Used from http.c, this cleans a built FormData linked list */ |
| void Curl_formclean(struct FormData *form) |
| { |
| struct FormData *next; |
| |
| do { |
| next=form->next; /* the following form line */ |
| free(form->line); /* free the line */ |
| free(form); /* free the struct */ |
| |
| } while((form=next)); /* continue */ |
| } |
| |
| /* external function to free up a whole form post chain */ |
| void curl_formfree(struct curl_httppost *form) |
| { |
| struct curl_httppost *next; |
| |
| if(!form) |
| /* no form to free, just get out of this */ |
| return; |
| |
| do { |
| next=form->next; /* the following form line */ |
| |
| /* recurse to sub-contents */ |
| if(form->more) |
| curl_formfree(form->more); |
| |
| if( !(form->flags & HTTPPOST_PTRNAME) && form->name) |
| free(form->name); /* free the name */ |
| if( !(form->flags & HTTPPOST_PTRCONTENTS) && form->contents) |
| 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); /* free the struct */ |
| |
| } while((form=next)); /* continue */ |
| } |
| |
| CURLcode Curl_getFormData(struct FormData **finalform, |
| struct curl_httppost *post, |
| int *sizep) |
| { |
| struct FormData *form = NULL; |
| struct FormData *firstform; |
| struct curl_httppost *file; |
| CURLcode result = CURLE_OK; |
| |
| int size =0; |
| char *boundary; |
| char *fileboundary=NULL; |
| struct curl_slist* curList; |
| |
| *finalform=NULL; /* default form is empty */ |
| |
| if(!post) |
| return result; /* no input => no output! */ |
| |
| boundary = Curl_FormBoundary(); |
| |
| /* Make the first line of the output */ |
| AddFormDataf(&form, |
| "Content-Type: multipart/form-data;" |
| " boundary=%s\r\n", |
| boundary); |
| /* we DO NOT count that line since that'll be part of the header! */ |
| |
| firstform = form; |
| |
| do { |
| |
| if(size) |
| size += AddFormDataf(&form, "\r\n"); |
| |
| /* boundary */ |
| size += AddFormDataf(&form, "--%s\r\n", boundary); |
| |
| size += AddFormData(&form, |
| "Content-Disposition: form-data; name=\"", 0); |
| |
| size += AddFormData(&form, post->name, post->namelength); |
| |
| size += AddFormData(&form, "\"", 0); |
| |
| if(post->more) { |
| /* 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(); |
| |
| size += AddFormDataf(&form, |
| "\r\nContent-Type: multipart/mixed," |
| " boundary=%s\r\n", |
| fileboundary); |
| } |
| |
| file = post; |
| |
| do { |
| |
| /* If 'showfilename' is set, that is a faked name passed on to us |
| to use to in the formpost. If that is not set, the actually used |
| local file name should be added. */ |
| |
| if(post->more) { |
| /* if multiple-file */ |
| size += AddFormDataf(&form, |
| "\r\n--%s\r\nContent-Disposition: " |
| "attachment; filename=\"%s\"", |
| fileboundary, |
| (file->showfilename?file->showfilename: |
| file->contents)); |
| } |
| else if((post->flags & HTTPPOST_FILENAME) || |
| |
| /* CMC: Added support for buffer uploads */ |
| (post->flags & HTTPPOST_BUFFER)) { |
| |
| size += AddFormDataf(&form, |
| "; filename=\"%s\"", |
| (post->showfilename?post->showfilename: |
| post->contents)); |
| } |
| |
| if(file->contenttype) { |
| /* we have a specified type */ |
| size += AddFormDataf(&form, |
| "\r\nContent-Type: %s", |
| file->contenttype); |
| } |
| |
| curList = file->contentheader; |
| while( curList ) { |
| /* Process the additional headers specified for this form */ |
| size += AddFormDataf( &form, "\r\n%s", curList->data ); |
| curList = curList->next; |
| } |
| |
| #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 */ |
| size += AddFormData(&form, "\r\nContent-Transfer-Encoding: binary", 0); |
| } |
| #endif |
| |
| size += AddFormData(&form, "\r\n\r\n", 0); |
| |
| if((post->flags & HTTPPOST_FILENAME) || |
| (post->flags & HTTPPOST_READFILE)) { |
| /* we should include the contents from the specified file */ |
| FILE *fileread; |
| char buffer[1024]; |
| int nread; |
| |
| fileread = strequal("-", file->contents)?stdin: |
| /* binary read for win32 crap */ |
| /*VMS??*/ fopen(file->contents, "rb"); /* ONLY ALLOWS FOR STREAM FILES ON VMS */ |
| /*VMS?? Stream files are OK, as are FIXED & VAR files WITHOUT implied CC */ |
| /*VMS?? For implied CC, every record needs to have a \n appended & 1 added to SIZE */ |
| if(fileread) { |
| while((nread = fread(buffer, 1, 1024, fileread))) |
| size += AddFormData(&form, buffer, nread); |
| |
| if(fileread != stdin) |
| fclose(fileread); |
| } |
| else { |
| #if 0 |
| /* File wasn't found, add a nothing field! */ |
| size += AddFormData(&form, "", 0); |
| #endif |
| Curl_formclean(firstform); |
| free(boundary); |
| *finalform = NULL; |
| return CURLE_READ_ERROR; |
| } |
| |
| /* CMC: Added support for buffer uploads */ |
| } else if (post->flags & HTTPPOST_BUFFER) { |
| /* include contents of buffer */ |
| size += AddFormData(&form, post->buffer, post->bufferlength); |
| } |
| |
| else { |
| /* include the contents we got */ |
| size += AddFormData(&form, post->contents, post->contentslength); |
| } |
| } while((file = file->more)); /* for each specified file for this field */ |
| |
| if(post->more) { |
| /* this was a multiple-file inclusion, make a termination file |
| boundary: */ |
| size += AddFormDataf(&form, |
| "\r\n--%s--", |
| fileboundary); |
| free(fileboundary); |
| } |
| |
| } while((post=post->next)); /* for each field */ |
| |
| /* end-boundary for everything */ |
| size += AddFormDataf(&form, |
| "\r\n--%s--\r\n", |
| boundary); |
| |
| *sizep = size; |
| |
| free(boundary); |
| |
| *finalform=firstform; |
| |
| return result; |
| } |
| |
| int Curl_FormInit(struct Form *form, struct FormData *formdata ) |
| { |
| if(!formdata) |
| return 1; /* error */ |
| |
| form->data = formdata; |
| form->sent = 0; |
| |
| return 0; |
| } |
| |
| /* fread() emulation */ |
| int Curl_FormReader(char *buffer, |
| size_t size, |
| size_t nitems, |
| FILE *mydata) |
| { |
| struct Form *form; |
| int wantedsize; |
| int gotsize = 0; |
| |
| form=(struct Form *)mydata; |
| |
| wantedsize = size * nitems; |
| |
| if(!form->data) |
| return 0; /* nothing, error, empty */ |
| |
| do { |
| |
| if( (form->data->length - form->sent ) > wantedsize - gotsize) { |
| |
| memcpy(buffer + gotsize , form->data->line + form->sent, |
| wantedsize - gotsize); |
| |
| form->sent += wantedsize-gotsize; |
| |
| return wantedsize; |
| } |
| |
| memcpy(buffer+gotsize, |
| form->data->line + form->sent, |
| (form->data->length - form->sent) ); |
| gotsize += form->data->length - form->sent; |
| |
| form->sent = 0; |
| |
| form->data = form->data->next; /* advance */ |
| |
| } while(form->data); |
| /* If we got an empty line and we have more data, we proceed to the next |
| line immediately to avoid returning zero before we've reached the end. |
| This is the bug reported November 22 1999 on curl 6.3. (Daniel) */ |
| |
| return gotsize; |
| } |
| |
| /* possible (old) fread() emulation that copies at most one line */ |
| int Curl_FormReadOneLine(char *buffer, |
| size_t size, |
| size_t nitems, |
| FILE *mydata) |
| { |
| struct Form *form; |
| int wantedsize; |
| int gotsize; |
| |
| form=(struct Form *)mydata; |
| |
| wantedsize = size * nitems; |
| |
| if(!form->data) |
| return -1; /* nothing, error, empty */ |
| |
| do { |
| |
| if( (form->data->length - form->sent ) > wantedsize ) { |
| |
| memcpy(buffer, form->data->line + form->sent, wantedsize); |
| |
| form->sent += wantedsize; |
| |
| return wantedsize; |
| } |
| |
| memcpy(buffer, |
| form->data->line + form->sent, |
| gotsize = (form->data->length - form->sent) ); |
| |
| form->sent = 0; |
| |
| form->data = form->data->next; /* advance */ |
| |
| } while(!gotsize && form->data); |
| /* If we got an empty line and we have more data, we proceed to the next |
| line immediately to avoid returning zero before we've reached the end. |
| This is the bug reported November 22 1999 on curl 6.3. (Daniel) */ |
| |
| return gotsize; |
| } |
| |
| |
| #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() |
| { |
| 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 PTRCOTNENTS + CONTENTSLENGTH + CONTENTTYPE"; |
| char value7[] = "inet_ntoa_r.h"; |
| char value8[] = "Makefile.b32.resp"; |
| char type2[] = "image/gif"; |
| char type6[] = "text/plain"; |
| char type7[] = "text/html"; |
| int name3length = strlen(name3); |
| int value3length = strlen(value3); |
| int value5length = strlen(value4); |
| int value6length = strlen(value5); |
| int errors = 0; |
| int size; |
| int 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; |
| |
| 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; |
| |
| form=Curl_getFormData(httppost, &size); |
| |
| Curl_FormInit(&formread, form); |
| |
| do { |
| nread = Curl_FormReader(buffer, 1, sizeof(buffer), |
| (FILE *)&formread); |
| |
| if(-1 == nread) |
| break; |
| fwrite(buffer, nread, 1, stdout); |
| } while(1); |
| |
| fprintf(stdout, "size: %d\n", size); |
| 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; |
| } |
| |
| #endif |
| |
| #ifdef _OLD_FORM_DEBUG |
| |
| int main(int argc, char **argv) |
| { |
| #if 0 |
| char *testargs[]={ |
| "name1 = data in number one", |
| "name2 = number two data", |
| "test = @upload" |
| }; |
| #endif |
| int i; |
| char *nextarg; |
| struct curl_httppost *httppost=NULL; |
| struct curl_httppost *last_post=NULL; |
| struct curl_httppost *post; |
| int size; |
| int nread; |
| char buffer[4096]; |
| |
| struct FormData *form; |
| struct Form formread; |
| |
| for(i=1; i<argc; i++) { |
| |
| if( FormParse( argv[i], |
| &httppost, |
| &last_post)) { |
| fprintf(stderr, "Illegally formatted input field: '%s'!\n", |
| argv[i]); |
| return 1; |
| } |
| } |
| |
| form=Curl_getFormData(httppost, &size); |
| |
| Curl_FormInit(&formread, form); |
| |
| do { |
| nread = Curl_FormReader(buffer, 1, sizeof(buffer), |
| (FILE *)&formread); |
| |
| if(-1 == nread) |
| break; |
| fwrite(buffer, nread, 1, stderr); |
| } while(1); |
| |
| fprintf(stderr, "size: %d\n", size); |
| |
| return 0; |
| } |
| |
| #endif |
| |
| #endif /* CURL_DISABLE_HTTP */ |