1 /***************************************************************************
3 * Project ___| | | | _ \| |
5 * | (__| |_| | _ <| |___
6 * \___|\___/|_| \_\_____|
8 * Copyright (C) 1998 - 2004, Daniel Stenberg, <daniel@haxx.se>, et al.
10 * This software is licensed as described in the file COPYING, which
11 * you should have received as part of this distribution. The terms
12 * are also available at http://curl.haxx.se/docs/copyright.html.
14 * You may opt to use, copy, modify, merge, publish, distribute and/or sell
15 * copies of the Software, and permit persons to whom the Software is
16 * furnished to do so, under the terms of the COPYING file.
18 * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY
19 * KIND, either express or implied.
21 * $Id: formdata.c,v 1.56 2004/03/12 14:22:16 bagder Exp $
22 ***************************************************************************/
25 Debug the form generator stand-alone by compiling this source file with:
27 gcc -DHAVE_CONFIG_H -I../ -g -D_FORM_DEBUG -o formdata -I../include formdata.c strequal.c
29 run the 'formdata' executable the output should end with:
30 All Tests seem to have worked ...
31 and the following parts should be there:
33 Content-Disposition: form-data; name="simple_COPYCONTENTS"
34 value for simple COPYCONTENTS
36 Content-Disposition: form-data; name="COPYCONTENTS_+_CONTENTTYPE"
37 Content-Type: image/gif
38 value for COPYCONTENTS + CONTENTTYPE
40 Content-Disposition: form-data; name="PRNAME_+_NAMELENGTH_+_COPYNAME_+_CONTENTSLENGTH"
41 vlue for PTRNAME + NAMELENGTH + COPYNAME + CONTENTSLENGTH
42 (or you might see P^@RNAME and v^@lue at the start)
44 Content-Disposition: form-data; name="simple_PTRCONTENTS"
45 value for simple PTRCONTENTS
47 Content-Disposition: form-data; name="PTRCONTENTS_+_CONTENTSLENGTH"
48 vlue for PTRCONTENTS + CONTENTSLENGTH
49 (or you might see v^@lue at the start)
51 Content-Disposition: form-data; name="PTRCONTENTS_+_CONTENTSLENGTH_+_CONTENTTYPE"
52 Content-Type: text/plain
53 vlue for PTRCOTNENTS + CONTENTSLENGTH + CONTENTTYPE
54 (or you might see v^@lue at the start)
56 Content-Disposition: form-data; name="FILE1_+_CONTENTTYPE"; filename="inet_ntoa_r.h"
57 Content-Type: text/html
60 Content-Disposition: form-data; name="FILE1_+_FILE2"
61 Content-Type: multipart/mixed, boundary=curlz1s0dkticx49MV1KGcYP5cvfSsz
63 Content-Disposition: attachment; filename="inet_ntoa_r.h"
64 Content-Type: text/plain
66 Content-Disposition: attachment; filename="Makefile.b32.resp"
67 Content-Type: text/plain
70 Content-Disposition: form-data; name="FILE1_+_FILE2_+_FILE3"
71 Content-Type: multipart/mixed, boundary=curlirkYPmPwu6FrJ1vJ1u1BmtIufh1
73 Content-Disposition: attachment; filename="inet_ntoa_r.h"
74 Content-Type: text/plain
76 Content-Disposition: attachment; filename="Makefile.b32.resp"
77 Content-Type: text/plain
79 Content-Disposition: attachment; filename="inet_ntoa_r.h"
80 Content-Type: text/plain
84 Content-Disposition: form-data; name="ARRAY: FILE1_+_FILE2_+_FILE3"
85 Content-Type: multipart/mixed, boundary=curlirkYPmPwu6FrJ1vJ1u1BmtIufh1
87 Content-Disposition: attachment; filename="inet_ntoa_r.h"
88 Content-Type: text/plain
90 Content-Disposition: attachment; filename="Makefile.b32.resp"
91 Content-Type: text/plain
93 Content-Disposition: attachment; filename="inet_ntoa_r.h"
94 Content-Type: text/plain
97 Content-Disposition: form-data; name="FILECONTENT"
100 For the old FormParse used by curl_formparse use:
102 gcc -DHAVE_CONFIG_H -I../ -g -D_OLD_FORM_DEBUG -o formdata -I../include formdata.c strequal.c
104 run the 'formdata' executable and make sure the output is ok!
106 try './formdata "name=Daniel" "poo=noo" "foo=bar"' and similarly
112 #ifndef CURL_DISABLE_HTTP
121 #ifndef CURL_OLDSTYLE
122 #define CURL_OLDSTYLE 1 /* enable deprecated prototype for curl_formparse */
124 #include <curl/curl.h>
125 #include "formdata.h"
127 #include "strequal.h"
129 /* The last #include file should be: */
131 #include "memdebug.h"
134 /* Length of the random boundary string. */
135 #define BOUNDARY_LENGTH 40
137 /* What kind of Content-Type to use on un-specified files with unrecognized
139 #define HTTPPOST_CONTENTTYPE_DEFAULT "application/octet-stream"
141 /* This is a silly duplicate of the function in main.c to enable this source
142 to compile stand-alone for better debugging */
143 static void GetStr(char **string,
148 *string = strdup(value);
151 /***************************************************************************
155 * Reads a 'name=value' paramter and builds the appropriate linked list.
157 * Specify files to upload with 'name=@filename'. Supports specified
158 * given Content-Type of the files. Such as ';type=<content-type>'.
160 * You may specify more than one file for a single name (field). Specify
161 * multiple files by writing it like:
163 * 'name=@filename,filename2,filename3'
165 * If you want content-types specified for each too, write them like:
167 * 'name=@filename;type=image/gif,filename2,filename3'
169 ***************************************************************************/
171 #define FORM_FILE_SEPARATOR ','
172 #define FORM_TYPE_SEPARATOR ';'
175 int FormParse(char *input,
176 struct curl_httppost **httppost,
177 struct curl_httppost **last_post)
179 /* nextarg MUST be a string in the format 'name=contents' and we'll
180 build a linked list with the info */
187 const char *type = NULL;
188 char *prevtype = NULL;
191 struct curl_httppost *post;
192 struct curl_httppost *subpost; /* a sub-node */
195 /* Preallocate contents to the length of input to make sure we don't
196 overwrite anything. */
197 contents = malloc(strlen(input));
198 contents[0] = '\000';
200 if(1 <= sscanf(input, "%255[^=]=%[^\n]", name, contents)) {
201 /* the input was using the correct format */
204 if('@' == contp[0]) {
205 /* we use the @-letter to indicate file name(s) */
207 flags = HTTPPOST_FILENAME;
213 /* since this was a file, it may have a content-type specifier
216 sep=strchr(contp, FORM_TYPE_SEPARATOR);
217 sep2=strchr(contp, FORM_FILE_SEPARATOR);
219 /* pick the closest */
220 if(sep2 && (sep2 < sep)) {
223 /* no type was specified! */
227 /* if we got here on a comma, don't do much */
228 if(FORM_FILE_SEPARATOR != *sep)
229 type = strstr(sep+1, "type=");
233 *sep=0; /* terminate file name at separator */
236 type += strlen("type=");
238 if(2 != sscanf(type, "%127[^/]/%127[^,\n]",
241 return 2; /* illegal content-type syntax! */
243 /* now point beyond the content-type specifier */
244 sep = (char *)type + strlen(major)+strlen(minor)+1;
246 /* find the following comma */
247 sep=strchr(sep, FORM_FILE_SEPARATOR);
252 sep=strchr(contp, FORM_FILE_SEPARATOR);
255 /* the next file name starts here */
261 * No type was specified, we scan through a few well-known
262 * extensions and pick the first we match!
265 const char *extension;
268 static struct ContentType ctts[]={
269 {".gif", "image/gif"},
270 {".jpg", "image/jpeg"},
271 {".jpeg", "image/jpeg"},
272 {".txt", "text/plain"},
273 {".html", "text/html"}
277 /* default to the previously set/used! */
280 /* It seems RFC1867 defines no Content-Type to default to
281 text/plain so we don't actually need to set this: */
282 type = HTTPPOST_CONTENTTYPE_DEFAULT;
284 for(i=0; i<sizeof(ctts)/sizeof(ctts[0]); i++) {
285 if(strlen(contp) >= strlen(ctts[i].extension)) {
287 strlen(contp) - strlen(ctts[i].extension),
288 ctts[i].extension)) {
294 /* we have a type by now */
298 /* For the first file name, we allocate and initiate the main list
301 post = (struct curl_httppost *)malloc(sizeof(struct curl_httppost));
303 memset(post, 0, sizeof(struct curl_httppost));
304 GetStr(&post->name, name); /* get the name */
305 GetStr(&post->contents, contp); /* get the contents */
306 post->contentslength = 0;
309 GetStr(&post->contenttype, (char *)type); /* get type */
310 prevtype=post->contenttype; /* point to the allocated string! */
312 /* make the previous point to this */
314 (*last_post)->next = post;
323 /* we add a file name to the previously allocated node, known as
325 subpost =(struct curl_httppost *)
326 malloc(sizeof(struct curl_httppost));
328 memset(subpost, 0, sizeof(struct curl_httppost));
329 GetStr(&subpost->name, name); /* get the name */
330 GetStr(&subpost->contents, contp); /* get the contents */
331 subpost->contentslength = 0;
332 subpost->flags = flags;
334 GetStr(&subpost->contenttype, (char *)type); /* get type */
335 prevtype=subpost->contenttype; /* point to allocated string! */
337 /* now, point our 'more' to the original 'more' */
338 subpost->more = post->more;
340 /* then move the original 'more' to point to ourselves */
341 post->more = subpost;
344 contp = sep; /* move the contents pointer to after the separator */
345 } while(sep && *sep); /* loop if there's another file name */
348 post = (struct curl_httppost *)malloc(sizeof(struct curl_httppost));
350 memset(post, 0, sizeof(struct curl_httppost));
351 GetStr(&post->name, name); /* get the name */
352 if( contp[0]=='<' ) {
353 GetStr(&post->contents, contp+1); /* get the contents */
354 post->contentslength = 0;
355 post->flags = HTTPPOST_READFILE;
358 GetStr(&post->contents, contp); /* get the contents */
359 post->contentslength = 0;
363 /* make the previous point to this */
365 (*last_post)->next = post;
383 int curl_formparse(char *input,
384 struct curl_httppost **httppost,
385 struct curl_httppost **last_post)
387 return FormParse(input, httppost, last_post);
390 /***************************************************************************
394 * Adds a HttpPost structure to the list, if parent_post is given becomes
395 * a subpost of parent_post instead of a direct list element.
397 * Returns newly allocated HttpPost on success and NULL if malloc failed.
399 ***************************************************************************/
400 static struct curl_httppost *
401 AddHttpPost(char * name, size_t namelength,
402 char * value, size_t contentslength,
403 char * buffer, size_t bufferlength,
406 struct curl_slist* contentHeader,
408 struct curl_httppost *parent_post,
409 struct curl_httppost **httppost,
410 struct curl_httppost **last_post)
412 struct curl_httppost *post;
413 post = (struct curl_httppost *)malloc(sizeof(struct curl_httppost));
415 memset(post, 0, sizeof(struct curl_httppost));
417 post->namelength = (long)(name?(namelength?namelength:strlen(name)):0);
418 post->contents = value;
419 post->contentslength = (long)contentslength;
420 post->buffer = buffer;
421 post->bufferlength = (long)bufferlength;
422 post->contenttype = contenttype;
423 post->contentheader = contentHeader;
424 post->showfilename = showfilename;
431 /* now, point our 'more' to the original 'more' */
432 post->more = parent_post->more;
434 /* then move the original 'more' to point to ourselves */
435 parent_post->more = post;
438 /* make the previous point to this */
440 (*last_post)->next = post;
449 /***************************************************************************
453 * Adds a FormInfo structure to the list presented by parent_form_info.
455 * Returns newly allocated FormInfo on success and NULL if malloc failed/
456 * parent_form_info is NULL.
458 ***************************************************************************/
459 static FormInfo * AddFormInfo(char *value,
461 FormInfo *parent_form_info)
464 form_info = (FormInfo *)malloc(sizeof(FormInfo));
466 memset(form_info, 0, sizeof(FormInfo));
468 form_info->value = value;
470 form_info->contenttype = contenttype;
471 form_info->flags = HTTPPOST_FILENAME;
476 if (parent_form_info) {
477 /* now, point our 'more' to the original 'more' */
478 form_info->more = parent_form_info->more;
480 /* then move the original 'more' to point to ourselves */
481 parent_form_info->more = form_info;
489 /***************************************************************************
491 * ContentTypeForFilename()
493 * Provides content type for filename if one of the known types (else
494 * (either the prevtype or the default is returned).
496 * Returns some valid contenttype for filename.
498 ***************************************************************************/
499 static const char * ContentTypeForFilename (const char *filename,
500 const char *prevtype)
502 const char *contenttype = NULL;
505 * No type was specified, we scan through a few well-known
506 * extensions and pick the first we match!
509 const char *extension;
512 static struct ContentType ctts[]={
513 {".gif", "image/gif"},
514 {".jpg", "image/jpeg"},
515 {".jpeg", "image/jpeg"},
516 {".txt", "text/plain"},
517 {".html", "text/html"}
521 /* default to the previously set/used! */
522 contenttype = prevtype;
524 /* It seems RFC1867 defines no Content-Type to default to
525 text/plain so we don't actually need to set this: */
526 contenttype = HTTPPOST_CONTENTTYPE_DEFAULT;
528 for(i=0; i<sizeof(ctts)/sizeof(ctts[0]); i++) {
529 if(strlen(filename) >= strlen(ctts[i].extension)) {
530 if(strequal(filename +
531 strlen(filename) - strlen(ctts[i].extension),
532 ctts[i].extension)) {
533 contenttype = ctts[i].type;
538 /* we have a contenttype by now */
542 /***************************************************************************
546 * Copies the data currently available under *buffer using newly allocated
547 * buffer (that becomes *buffer). Uses buffer_length if not null, else
548 * uses strlen to determine the length of the buffer to be copied
550 * Returns 0 on success and 1 if the malloc failed.
552 ***************************************************************************/
553 static int AllocAndCopy(char **buffer, size_t buffer_length)
555 const char *src = *buffer;
559 length = buffer_length;
561 length = strlen(*buffer);
564 *buffer = (char*)malloc(length+add);
567 memcpy(*buffer, src, length);
568 /* if length unknown do null termination */
570 (*buffer)[length] = '\0';
574 /***************************************************************************
578 * Stores a 'name=value' formpost parameter and builds the appropriate
581 * Has two principal functionalities: using files and byte arrays as
582 * post parts. Byte arrays are either copied or just the pointer is stored
583 * (as the user requests) while for files only the filename and not the
586 * While you may have only one byte array for each name, multiple filenames
587 * are allowed (and because of this feature CURLFORM_END is needed after
588 * using CURLFORM_FILE).
592 * Simple name/value pair with copied contents:
593 * curl_formadd (&post, &last, CURLFORM_COPYNAME, "name",
594 * CURLFORM_COPYCONTENTS, "value", CURLFORM_END);
596 * name/value pair where only the content pointer is remembered:
597 * curl_formadd (&post, &last, CURLFORM_COPYNAME, "name",
598 * CURLFORM_PTRCONTENTS, ptr, CURLFORM_CONTENTSLENGTH, 10, CURLFORM_END);
599 * (if CURLFORM_CONTENTSLENGTH is missing strlen () is used)
601 * storing a filename (CONTENTTYPE is optional!):
602 * curl_formadd (&post, &last, CURLFORM_COPYNAME, "name",
603 * CURLFORM_FILE, "filename1", CURLFORM_CONTENTTYPE, "plain/text",
606 * storing multiple filenames:
607 * curl_formadd (&post, &last, CURLFORM_COPYNAME, "name",
608 * CURLFORM_FILE, "filename1", CURLFORM_FILE, "filename2", CURLFORM_END);
611 * CURL_FORMADD_OK on success
612 * CURL_FORMADD_MEMORY if the FormInfo allocation fails
613 * CURL_FORMADD_OPTION_TWICE if one option is given twice for one Form
614 * CURL_FORMADD_NULL if a null pointer was given for a char
615 * CURL_FORMADD_MEMORY if the allocation of a FormInfo struct failed
616 * CURL_FORMADD_UNKNOWN_OPTION if an unknown option was used
617 * CURL_FORMADD_INCOMPLETE if the some FormInfo is not complete (or an error)
618 * CURL_FORMADD_MEMORY if a HttpPost struct cannot be allocated
619 * CURL_FORMADD_MEMORY if some allocation for string copying failed.
620 * CURL_FORMADD_ILLEGAL_ARRAY if an illegal option is used in an array
622 ***************************************************************************/
625 CURLFORMcode FormAdd(struct curl_httppost **httppost,
626 struct curl_httppost **last_post,
629 FormInfo *first_form, *current_form, *form;
630 CURLFORMcode return_value = CURL_FORMADD_OK;
631 const char *prevtype = NULL;
632 struct curl_httppost *post = NULL;
633 CURLformoption option;
634 struct curl_forms *forms = NULL;
635 char *array_value=NULL; /* value read from an array */
637 /* This is a state variable, that if TRUE means that we're parsing an
638 array that we got passed to us. If FALSE we're parsing the input
639 va_list arguments. */
640 bool array_state = FALSE;
643 * We need to allocate the first struct to fill in.
645 first_form = (FormInfo *)malloc(sizeof(struct FormInfo));
647 memset(first_form, 0, sizeof(FormInfo));
648 current_form = first_form;
651 return CURL_FORMADD_MEMORY;
654 * Loop through all the options set.
658 /* break if we have an error to report */
659 if (return_value != CURL_FORMADD_OK)
662 /* first see if we have more parts of the array param */
664 /* get the upcoming option from the given array */
665 option = forms->option;
666 array_value = (char *)forms->value;
668 forms++; /* advance this to next entry */
669 if (CURLFORM_END == option) {
670 /* end of array state */
676 /* This is not array-state, get next option */
677 option = va_arg(params, CURLformoption);
678 if (CURLFORM_END == option)
685 /* we don't support an array from within an array */
686 return_value = CURL_FORMADD_ILLEGAL_ARRAY;
688 forms = va_arg(params, struct curl_forms *);
692 return_value = CURL_FORMADD_NULL;
697 * Set the Name property.
699 case CURLFORM_PTRNAME:
700 current_form->flags |= HTTPPOST_PTRNAME; /* fall through */
701 case CURLFORM_COPYNAME:
702 if (current_form->name)
703 return_value = CURL_FORMADD_OPTION_TWICE;
705 char *name = array_state?
706 array_value:va_arg(params, char *);
708 current_form->name = name; /* store for the moment */
710 return_value = CURL_FORMADD_NULL;
713 case CURLFORM_NAMELENGTH:
714 if (current_form->namelength)
715 return_value = CURL_FORMADD_OPTION_TWICE;
717 current_form->namelength =
718 array_state?(long)array_value:va_arg(params, long);
722 * Set the contents property.
724 case CURLFORM_PTRCONTENTS:
725 current_form->flags |= HTTPPOST_PTRCONTENTS; /* fall through */
726 case CURLFORM_COPYCONTENTS:
727 if (current_form->value)
728 return_value = CURL_FORMADD_OPTION_TWICE;
731 array_state?array_value:va_arg(params, char *);
733 current_form->value = value; /* store for the moment */
735 return_value = CURL_FORMADD_NULL;
738 case CURLFORM_CONTENTSLENGTH:
739 if (current_form->contentslength)
740 return_value = CURL_FORMADD_OPTION_TWICE;
742 current_form->contentslength =
743 array_state?(long)array_value:va_arg(params, long);
746 /* Get contents from a given file name */
747 case CURLFORM_FILECONTENT:
748 if (current_form->flags != 0)
749 return_value = CURL_FORMADD_OPTION_TWICE;
751 char *filename = array_state?
752 array_value:va_arg(params, char *);
754 current_form->value = strdup(filename);
755 current_form->flags |= HTTPPOST_READFILE;
758 return_value = CURL_FORMADD_NULL;
762 /* We upload a file */
765 char *filename = array_state?array_value:
766 va_arg(params, char *);
768 if (current_form->value) {
769 if (current_form->flags & HTTPPOST_FILENAME) {
771 if (!(current_form = AddFormInfo(strdup(filename),
772 NULL, current_form)))
773 return_value = CURL_FORMADD_MEMORY;
776 return_value = CURL_FORMADD_NULL;
779 return_value = CURL_FORMADD_OPTION_TWICE;
783 current_form->value = strdup(filename);
785 return_value = CURL_FORMADD_NULL;
786 current_form->flags |= HTTPPOST_FILENAME;
791 case CURLFORM_BUFFER:
793 char *filename = array_state?array_value:
794 va_arg(params, char *);
796 if (current_form->value) {
797 if (current_form->flags & HTTPPOST_BUFFER) {
799 if (!(current_form = AddFormInfo(strdup(filename),
800 NULL, current_form)))
801 return_value = CURL_FORMADD_MEMORY;
804 return_value = CURL_FORMADD_NULL;
807 return_value = CURL_FORMADD_OPTION_TWICE;
811 current_form->value = strdup(filename);
813 return_value = CURL_FORMADD_NULL;
814 current_form->flags |= HTTPPOST_BUFFER;
819 case CURLFORM_BUFFERPTR:
820 current_form->flags |= HTTPPOST_PTRBUFFER;
821 if (current_form->buffer)
822 return_value = CURL_FORMADD_OPTION_TWICE;
825 array_state?array_value:va_arg(params, char *);
827 current_form->buffer = buffer; /* store for the moment */
829 return_value = CURL_FORMADD_NULL;
833 case CURLFORM_BUFFERLENGTH:
834 if (current_form->bufferlength)
835 return_value = CURL_FORMADD_OPTION_TWICE;
837 current_form->bufferlength =
838 array_state?(long)array_value:va_arg(params, long);
841 case CURLFORM_CONTENTTYPE:
844 array_state?array_value:va_arg(params, char *);
845 if (current_form->contenttype) {
846 if (current_form->flags & HTTPPOST_FILENAME) {
848 if (!(current_form = AddFormInfo(NULL,
851 return_value = CURL_FORMADD_MEMORY;
854 return_value = CURL_FORMADD_NULL;
857 return_value = CURL_FORMADD_OPTION_TWICE;
861 current_form->contenttype = strdup(contenttype);
863 return_value = CURL_FORMADD_NULL;
867 case CURLFORM_CONTENTHEADER:
869 /* this "cast increases required alignment of target type" but
870 we consider it OK anyway */
871 struct curl_slist* list = array_state?
872 (struct curl_slist*)array_value:
873 va_arg(params, struct curl_slist*);
875 if( current_form->contentheader )
876 return_value = CURL_FORMADD_OPTION_TWICE;
878 current_form->contentheader = list;
882 case CURLFORM_FILENAME:
884 char *filename = array_state?array_value:
885 va_arg(params, char *);
886 if( current_form->showfilename )
887 return_value = CURL_FORMADD_OPTION_TWICE;
889 current_form->showfilename = strdup(filename);
893 return_value = CURL_FORMADD_UNKNOWN_OPTION;
897 if(CURL_FORMADD_OK == return_value) {
898 /* go through the list, check for copleteness and if everything is
899 * alright add the HttpPost item otherwise set return_value accordingly */
902 for(form = first_form;
905 if ( ((!form->name || !form->value) && !post) ||
906 ( (form->contentslength) &&
907 (form->flags & HTTPPOST_FILENAME) ) ||
908 ( (form->flags & HTTPPOST_FILENAME) &&
909 (form->flags & HTTPPOST_PTRCONTENTS) ) ||
912 (form->flags & HTTPPOST_BUFFER) &&
913 (form->flags & HTTPPOST_PTRBUFFER) ) ||
915 ( (form->flags & HTTPPOST_READFILE) &&
916 (form->flags & HTTPPOST_PTRCONTENTS) )
918 return_value = CURL_FORMADD_INCOMPLETE;
922 if ( ((form->flags & HTTPPOST_FILENAME) ||
923 (form->flags & HTTPPOST_BUFFER)) &&
924 !form->contenttype ) {
925 /* our contenttype is missing */
927 = strdup(ContentTypeForFilename(form->value, prevtype));
929 if ( !(form->flags & HTTPPOST_PTRNAME) &&
930 (form == first_form) ) {
931 /* copy name (without strdup; possibly contains null characters) */
932 if (AllocAndCopy(&form->name, form->namelength)) {
933 return_value = CURL_FORMADD_MEMORY;
937 if ( !(form->flags & HTTPPOST_FILENAME) &&
938 !(form->flags & HTTPPOST_READFILE) &&
939 !(form->flags & HTTPPOST_PTRCONTENTS) &&
940 !(form->flags & HTTPPOST_PTRBUFFER) ) {
942 /* copy value (without strdup; possibly contains null characters) */
943 if (AllocAndCopy(&form->value, form->contentslength)) {
944 return_value = CURL_FORMADD_MEMORY;
948 post = AddHttpPost(form->name, form->namelength,
949 form->value, form->contentslength,
950 form->buffer, form->bufferlength,
951 form->contenttype, form->flags,
952 form->contentheader, form->showfilename,
957 return_value = CURL_FORMADD_MEMORY;
959 if (form->contenttype)
960 prevtype = form->contenttype;
965 /* always delete the allocated memory before returning */
967 while (form != NULL) {
968 FormInfo *delete_form;
978 CURLFORMcode curl_formadd(struct curl_httppost **httppost,
979 struct curl_httppost **last_post,
984 va_start(arg, last_post);
985 result = FormAdd(httppost, last_post, arg);
990 static size_t AddFormData(struct FormData **formp,
994 struct FormData *newform = (struct FormData *)
995 malloc(sizeof(struct FormData));
996 newform->next = NULL;
998 /* we make it easier for plain strings: */
1000 length = strlen((char *)line);
1002 newform->line = (char *)malloc(length+1);
1003 memcpy(newform->line, line, length);
1004 newform->length = length;
1005 newform->line[length]=0; /* zero terminate for easier debugging */
1008 (*formp)->next = newform;
1018 static size_t AddFormDataf(struct FormData **formp,
1019 const char *fmt, ...)
1024 vsprintf(s, fmt, ap);
1027 return AddFormData(formp, s, 0);
1031 char *Curl_FormBoundary(void)
1034 static int randomizer=0; /* this is just so that two boundaries within
1035 the same form won't be identical */
1038 static char table16[]="abcdef0123456789";
1040 retstring = (char *)malloc(BOUNDARY_LENGTH+1);
1043 return NULL; /* failed */
1045 srand(time(NULL)+randomizer++); /* seed */
1047 strcpy(retstring, "----------------------------");
1049 for(i=strlen(retstring); i<BOUNDARY_LENGTH; i++)
1050 retstring[i] = table16[rand()%16];
1052 /* 28 dashes and 12 hexadecimal digits makes 12^16 (184884258895036416)
1054 retstring[BOUNDARY_LENGTH]=0; /* zero terminate */
1059 /* Used from http.c, this cleans a built FormData linked list */
1060 void Curl_formclean(struct FormData *form)
1062 struct FormData *next;
1065 next=form->next; /* the following form line */
1066 free(form->line); /* free the line */
1067 free(form); /* free the struct */
1069 } while((form=next)); /* continue */
1072 /* external function to free up a whole form post chain */
1073 void curl_formfree(struct curl_httppost *form)
1075 struct curl_httppost *next;
1078 /* no form to free, just get out of this */
1082 next=form->next; /* the following form line */
1084 /* recurse to sub-contents */
1086 curl_formfree(form->more);
1088 if( !(form->flags & HTTPPOST_PTRNAME) && form->name)
1089 free(form->name); /* free the name */
1090 if( !(form->flags & HTTPPOST_PTRCONTENTS) && form->contents)
1091 free(form->contents); /* free the contents */
1092 if(form->contenttype)
1093 free(form->contenttype); /* free the content type */
1094 if(form->showfilename)
1095 free(form->showfilename); /* free the faked file name */
1096 free(form); /* free the struct */
1098 } while((form=next)); /* continue */
1101 CURLcode Curl_getFormData(struct FormData **finalform,
1102 struct curl_httppost *post,
1105 struct FormData *form = NULL;
1106 struct FormData *firstform;
1107 struct curl_httppost *file;
1108 CURLcode result = CURLE_OK;
1112 char *fileboundary=NULL;
1113 struct curl_slist* curList;
1115 *finalform=NULL; /* default form is empty */
1118 return result; /* no input => no output! */
1120 boundary = Curl_FormBoundary();
1122 /* Make the first line of the output */
1124 "Content-Type: multipart/form-data;"
1127 /* we DO NOT count that line since that'll be part of the header! */
1134 size += AddFormDataf(&form, "\r\n");
1137 size += AddFormDataf(&form, "--%s\r\n", boundary);
1139 size += AddFormData(&form,
1140 "Content-Disposition: form-data; name=\"", 0);
1142 size += AddFormData(&form, post->name, post->namelength);
1144 size += AddFormData(&form, "\"", 0);
1147 /* If used, this is a link to more file names, we must then do
1148 the magic to include several files with the same field name */
1150 fileboundary = Curl_FormBoundary();
1152 size += AddFormDataf(&form,
1153 "\r\nContent-Type: multipart/mixed,"
1162 /* If 'showfilename' is set, that is a faked name passed on to us
1163 to use to in the formpost. If that is not set, the actually used
1164 local file name should be added. */
1167 /* if multiple-file */
1168 size += AddFormDataf(&form,
1169 "\r\n--%s\r\nContent-Disposition: "
1170 "attachment; filename=\"%s\"",
1172 (file->showfilename?file->showfilename:
1175 else if((post->flags & HTTPPOST_FILENAME) ||
1176 (post->flags & HTTPPOST_BUFFER)) {
1178 size += AddFormDataf(&form,
1179 "; filename=\"%s\"",
1180 (post->showfilename?post->showfilename:
1184 if(file->contenttype) {
1185 /* we have a specified type */
1186 size += AddFormDataf(&form,
1187 "\r\nContent-Type: %s",
1191 curList = file->contentheader;
1193 /* Process the additional headers specified for this form */
1194 size += AddFormDataf( &form, "\r\n%s", curList->data );
1195 curList = curList->next;
1199 /* The header Content-Transfer-Encoding: seems to confuse some receivers
1200 * (like the built-in PHP engine). While I can't see any reason why it
1201 * should, I can just as well skip this to the benefit of the users who
1202 * are using such confused receivers.
1205 if(file->contenttype &&
1206 !checkprefix("text/", file->contenttype)) {
1207 /* this is not a text content, mention our binary encoding */
1208 size += AddFormData(&form, "\r\nContent-Transfer-Encoding: binary", 0);
1212 size += AddFormData(&form, "\r\n\r\n", 0);
1214 if((post->flags & HTTPPOST_FILENAME) ||
1215 (post->flags & HTTPPOST_READFILE)) {
1216 /* we should include the contents from the specified file */
1221 fileread = strequal("-", file->contents)?stdin:
1222 /* binary read for win32 crap */
1223 /*VMS??*/ fopen(file->contents, "rb"); /* ONLY ALLOWS FOR STREAM FILES ON VMS */
1224 /*VMS?? Stream files are OK, as are FIXED & VAR files WITHOUT implied CC */
1225 /*VMS?? For implied CC, every record needs to have a \n appended & 1 added to SIZE */
1227 while((nread = fread(buffer, 1, 1024, fileread)))
1228 size += AddFormData(&form, buffer, nread);
1230 if(fileread != stdin)
1235 /* File wasn't found, add a nothing field! */
1236 size += AddFormData(&form, "", 0);
1238 Curl_formclean(firstform);
1241 return CURLE_READ_ERROR;
1245 else if (post->flags & HTTPPOST_BUFFER) {
1246 /* include contents of buffer */
1247 size += AddFormData(&form, post->buffer, post->bufferlength);
1251 /* include the contents we got */
1252 size += AddFormData(&form, post->contents, post->contentslength);
1254 } while((file = file->more)); /* for each specified file for this field */
1257 /* this was a multiple-file inclusion, make a termination file
1259 size += AddFormDataf(&form,
1265 } while((post=post->next)); /* for each field */
1267 /* end-boundary for everything */
1268 size += AddFormDataf(&form,
1276 *finalform=firstform;
1281 int Curl_FormInit(struct Form *form, struct FormData *formdata )
1284 return 1; /* error */
1286 form->data = formdata;
1292 /* fread() emulation */
1293 size_t Curl_FormReader(char *buffer,
1302 form=(struct Form *)mydata;
1304 wantedsize = size * nitems;
1307 return 0; /* nothing, error, empty */
1311 if( (form->data->length - form->sent ) > wantedsize - gotsize) {
1313 memcpy(buffer + gotsize , form->data->line + form->sent,
1314 wantedsize - gotsize);
1316 form->sent += wantedsize-gotsize;
1321 memcpy(buffer+gotsize,
1322 form->data->line + form->sent,
1323 (form->data->length - form->sent) );
1324 gotsize += form->data->length - form->sent;
1328 form->data = form->data->next; /* advance */
1330 } while(form->data);
1331 /* If we got an empty line and we have more data, we proceed to the next
1332 line immediately to avoid returning zero before we've reached the end.
1333 This is the bug reported November 22 1999 on curl 6.3. (Daniel) */
1338 /* possible (old) fread() emulation that copies at most one line */
1339 size_t Curl_FormReadOneLine(char *buffer,
1348 form=(struct Form *)mydata;
1350 wantedsize = size * nitems;
1353 return 0; /* nothing, error, empty */
1357 if( (form->data->length - form->sent ) > wantedsize ) {
1359 memcpy(buffer, form->data->line + form->sent, wantedsize);
1361 form->sent += wantedsize;
1367 form->data->line + form->sent,
1368 gotsize = (form->data->length - form->sent) );
1372 form->data = form->data->next; /* advance */
1374 } while(!gotsize && form->data);
1375 /* If we got an empty line and we have more data, we proceed to the next
1376 line immediately to avoid returning zero before we've reached the end.
1377 This is the bug reported November 22 1999 on curl 6.3. (Daniel) */
1384 int FormAddTest(const char * errormsg,
1385 struct curl_httppost **httppost,
1386 struct curl_httppost **last_post,
1391 va_start(arg, last_post);
1392 if ((result = FormAdd(httppost, last_post, arg)))
1393 fprintf (stderr, "ERROR doing FormAdd ret: %d action: %s\n", result,
1402 char name1[] = "simple_COPYCONTENTS";
1403 char name2[] = "COPYCONTENTS_+_CONTENTTYPE";
1404 char name3[] = "PTRNAME_+_NAMELENGTH_+_COPYNAME_+_CONTENTSLENGTH";
1405 char name4[] = "simple_PTRCONTENTS";
1406 char name5[] = "PTRCONTENTS_+_CONTENTSLENGTH";
1407 char name6[] = "PTRCONTENTS_+_CONTENTSLENGTH_+_CONTENTTYPE";
1408 char name7[] = "FILE1_+_CONTENTTYPE";
1409 char name8[] = "FILE1_+_FILE2";
1410 char name9[] = "FILE1_+_FILE2_+_FILE3";
1411 char name10[] = "ARRAY: FILE1_+_FILE2_+_FILE3";
1412 char name11[] = "FILECONTENT";
1413 char value1[] = "value for simple COPYCONTENTS";
1414 char value2[] = "value for COPYCONTENTS + CONTENTTYPE";
1415 char value3[] = "value for PTRNAME + NAMELENGTH + COPYNAME + CONTENTSLENGTH";
1416 char value4[] = "value for simple PTRCONTENTS";
1417 char value5[] = "value for PTRCONTENTS + CONTENTSLENGTH";
1418 char value6[] = "value for PTRCOTNENTS + CONTENTSLENGTH + CONTENTTYPE";
1419 char value7[] = "inet_ntoa_r.h";
1420 char value8[] = "Makefile.b32.resp";
1421 char type2[] = "image/gif";
1422 char type6[] = "text/plain";
1423 char type7[] = "text/html";
1424 int name3length = strlen(name3);
1425 int value3length = strlen(value3);
1426 int value5length = strlen(value4);
1427 int value6length = strlen(value5);
1432 struct curl_httppost *httppost=NULL;
1433 struct curl_httppost *last_post=NULL;
1434 struct curl_forms forms[4];
1436 struct FormData *form;
1437 struct Form formread;
1439 if (FormAddTest("simple COPYCONTENTS test", &httppost, &last_post,
1440 CURLFORM_COPYNAME, name1, CURLFORM_COPYCONTENTS, value1,
1443 if (FormAddTest("COPYCONTENTS + CONTENTTYPE test", &httppost, &last_post,
1444 CURLFORM_COPYNAME, name2, CURLFORM_COPYCONTENTS, value2,
1445 CURLFORM_CONTENTTYPE, type2, CURLFORM_END))
1447 /* make null character at start to check that contentslength works
1451 if (FormAddTest("PTRNAME + NAMELENGTH + COPYNAME + CONTENTSLENGTH test",
1452 &httppost, &last_post,
1453 CURLFORM_PTRNAME, name3, CURLFORM_COPYCONTENTS, value3,
1454 CURLFORM_CONTENTSLENGTH, value3length,
1455 CURLFORM_NAMELENGTH, name3length, CURLFORM_END))
1457 if (FormAddTest("simple PTRCONTENTS test", &httppost, &last_post,
1458 CURLFORM_COPYNAME, name4, CURLFORM_PTRCONTENTS, value4,
1461 /* make null character at start to check that contentslength works
1464 if (FormAddTest("PTRCONTENTS + CONTENTSLENGTH test", &httppost, &last_post,
1465 CURLFORM_COPYNAME, name5, CURLFORM_PTRCONTENTS, value5,
1466 CURLFORM_CONTENTSLENGTH, value5length, CURLFORM_END))
1468 /* make null character at start to check that contentslength works
1471 if (FormAddTest("PTRCONTENTS + CONTENTSLENGTH + CONTENTTYPE test",
1472 &httppost, &last_post,
1473 CURLFORM_COPYNAME, name6, CURLFORM_PTRCONTENTS, value6,
1474 CURLFORM_CONTENTSLENGTH, value6length,
1475 CURLFORM_CONTENTTYPE, type6, CURLFORM_END))
1477 if (FormAddTest("FILE + CONTENTTYPE test", &httppost, &last_post,
1478 CURLFORM_COPYNAME, name7, CURLFORM_FILE, value7,
1479 CURLFORM_CONTENTTYPE, type7, CURLFORM_END))
1481 if (FormAddTest("FILE1 + FILE2 test", &httppost, &last_post,
1482 CURLFORM_COPYNAME, name8, CURLFORM_FILE, value7,
1483 CURLFORM_FILE, value8, CURLFORM_END))
1485 if (FormAddTest("FILE1 + FILE2 + FILE3 test", &httppost, &last_post,
1486 CURLFORM_COPYNAME, name9, CURLFORM_FILE, value7,
1487 CURLFORM_FILE, value8, CURLFORM_FILE, value7, CURLFORM_END))
1489 forms[0].option = CURLFORM_FILE;
1490 forms[0].value = value7;
1491 forms[1].option = CURLFORM_FILE;
1492 forms[1].value = value8;
1493 forms[2].option = CURLFORM_FILE;
1494 forms[2].value = value7;
1495 forms[3].option = CURLFORM_END;
1496 if (FormAddTest("FILE1 + FILE2 + FILE3 ARRAY test", &httppost, &last_post,
1497 CURLFORM_COPYNAME, name10, CURLFORM_ARRAY, forms,
1500 if (FormAddTest("FILECONTENT test", &httppost, &last_post,
1501 CURLFORM_COPYNAME, name11, CURLFORM_FILECONTENT, value7,
1505 form=Curl_getFormData(httppost, &size);
1507 Curl_FormInit(&formread, form);
1510 nread = Curl_FormReader(buffer, 1, sizeof(buffer),
1515 fwrite(buffer, nread, 1, stdout);
1518 fprintf(stdout, "size: %d\n", size);
1520 fprintf(stdout, "\n==> %d Test(s) failed!\n", errors);
1522 fprintf(stdout, "\nAll Tests seem to have worked (please check output)\n");
1529 #ifdef _OLD_FORM_DEBUG
1531 int main(int argc, char **argv)
1535 "name1 = data in number one",
1536 "name2 = number two data",
1542 struct curl_httppost *httppost=NULL;
1543 struct curl_httppost *last_post=NULL;
1544 struct curl_httppost *post;
1549 struct FormData *form;
1550 struct Form formread;
1552 for(i=1; i<argc; i++) {
1554 if( FormParse( argv[i],
1557 fprintf(stderr, "Illegally formatted input field: '%s'!\n",
1563 form=Curl_getFormData(httppost, &size);
1565 Curl_FormInit(&formread, form);
1568 nread = Curl_FormReader(buffer, 1, sizeof(buffer),
1573 fwrite(buffer, nread, 1, stderr);
1576 fprintf(stderr, "size: %d\n", size);
1583 #endif /* CURL_DISABLE_HTTP */