]> icculus.org git repositories - icculus/iodoom3.git/blob - neo/curl/lib/formdata.c
Various Mac OS X tweaks to get this to build. Probably breaking things.
[icculus/iodoom3.git] / neo / curl / lib / formdata.c
1 /***************************************************************************
2  *                                  _   _ ____  _     
3  *  Project                     ___| | | |  _ \| |    
4  *                             / __| | | | |_) | |    
5  *                            | (__| |_| |  _ <| |___ 
6  *                             \___|\___/|_| \_\_____|
7  *
8  * Copyright (C) 1998 - 2004, Daniel Stenberg, <daniel@haxx.se>, et al.
9  *
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.
13  * 
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.
17  *
18  * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY
19  * KIND, either express or implied.
20  *
21  * $Id: formdata.c,v 1.56 2004/03/12 14:22:16 bagder Exp $
22  ***************************************************************************/
23
24 /*
25   Debug the form generator stand-alone by compiling this source file with:
26
27   gcc -DHAVE_CONFIG_H -I../ -g -D_FORM_DEBUG -o formdata -I../include formdata.c strequal.c
28
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:
32
33 Content-Disposition: form-data; name="simple_COPYCONTENTS"
34 value for simple COPYCONTENTS
35
36 Content-Disposition: form-data; name="COPYCONTENTS_+_CONTENTTYPE"
37 Content-Type: image/gif
38 value for COPYCONTENTS + CONTENTTYPE
39
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)
43
44 Content-Disposition: form-data; name="simple_PTRCONTENTS"
45 value for simple PTRCONTENTS
46
47 Content-Disposition: form-data; name="PTRCONTENTS_+_CONTENTSLENGTH"
48 vlue for PTRCONTENTS + CONTENTSLENGTH
49 (or you might see v^@lue at the start)
50
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)
55
56 Content-Disposition: form-data; name="FILE1_+_CONTENTTYPE"; filename="inet_ntoa_r.h"
57 Content-Type: text/html
58 ...
59
60 Content-Disposition: form-data; name="FILE1_+_FILE2"
61 Content-Type: multipart/mixed, boundary=curlz1s0dkticx49MV1KGcYP5cvfSsz
62 ...
63 Content-Disposition: attachment; filename="inet_ntoa_r.h"
64 Content-Type: text/plain
65 ...
66 Content-Disposition: attachment; filename="Makefile.b32.resp"
67 Content-Type: text/plain
68 ...
69
70 Content-Disposition: form-data; name="FILE1_+_FILE2_+_FILE3"
71 Content-Type: multipart/mixed, boundary=curlirkYPmPwu6FrJ1vJ1u1BmtIufh1
72 ...
73 Content-Disposition: attachment; filename="inet_ntoa_r.h"
74 Content-Type: text/plain
75 ...
76 Content-Disposition: attachment; filename="Makefile.b32.resp"
77 Content-Type: text/plain
78 ...
79 Content-Disposition: attachment; filename="inet_ntoa_r.h"
80 Content-Type: text/plain
81 ...
82
83
84 Content-Disposition: form-data; name="ARRAY: FILE1_+_FILE2_+_FILE3"
85 Content-Type: multipart/mixed, boundary=curlirkYPmPwu6FrJ1vJ1u1BmtIufh1
86 ...
87 Content-Disposition: attachment; filename="inet_ntoa_r.h"
88 Content-Type: text/plain
89 ...
90 Content-Disposition: attachment; filename="Makefile.b32.resp"
91 Content-Type: text/plain
92 ...
93 Content-Disposition: attachment; filename="inet_ntoa_r.h"
94 Content-Type: text/plain
95 ...
96
97 Content-Disposition: form-data; name="FILECONTENT"
98 ...
99
100   For the old FormParse used by curl_formparse use:
101
102   gcc -DHAVE_CONFIG_H -I../ -g -D_OLD_FORM_DEBUG -o formdata -I../include formdata.c strequal.c
103
104   run the 'formdata' executable and make sure the output is ok!
105
106   try './formdata "name=Daniel" "poo=noo" "foo=bar"' and similarly
107
108  */
109
110 #include "setup.h"
111
112 #ifndef CURL_DISABLE_HTTP
113
114 #include <stdio.h>
115 #include <stdlib.h>
116 #include <string.h>
117 #include <stdarg.h>
118
119 #include <time.h>
120
121 #ifndef CURL_OLDSTYLE 
122 #define CURL_OLDSTYLE 1 /* enable deprecated prototype for curl_formparse */
123 #endif
124 #include <curl/curl.h>
125 #include "formdata.h"
126
127 #include "strequal.h"
128
129 /* The last #include file should be: */
130 #ifdef CURLDEBUG
131 #include "memdebug.h"
132 #endif
133
134 /* Length of the random boundary string. */
135 #define BOUNDARY_LENGTH 40
136
137 /* What kind of Content-Type to use on un-specified files with unrecognized
138    extensions. */
139 #define HTTPPOST_CONTENTTYPE_DEFAULT "application/octet-stream"
140
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,
144                    const char *value)
145 {
146   if(*string)
147     free(*string);
148   *string = strdup(value);
149 }
150
151 /***************************************************************************
152  *
153  * FormParse()
154  *      
155  * Reads a 'name=value' paramter and builds the appropriate linked list.
156  *
157  * Specify files to upload with 'name=@filename'. Supports specified
158  * given Content-Type of the files. Such as ';type=<content-type>'.
159  *
160  * You may specify more than one file for a single name (field). Specify
161  * multiple files by writing it like:
162  *
163  * 'name=@filename,filename2,filename3'
164  *
165  * If you want content-types specified for each too, write them like:
166  *
167  * 'name=@filename;type=image/gif,filename2,filename3'
168  *
169  ***************************************************************************/
170
171 #define FORM_FILE_SEPARATOR ','
172 #define FORM_TYPE_SEPARATOR ';'
173
174 static
175 int FormParse(char *input,
176               struct curl_httppost **httppost,
177               struct curl_httppost **last_post)
178 {
179   /* nextarg MUST be a string in the format 'name=contents' and we'll
180      build a linked list with the info */
181   char name[256];
182   char *contents;
183   char major[128];
184   char minor[128];
185   long flags = 0;
186   char *contp;
187   const char *type = NULL;
188   char *prevtype = NULL;
189   char *sep;
190   char *sep2;
191   struct curl_httppost *post;
192   struct curl_httppost *subpost; /* a sub-node */
193   unsigned int i;
194
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';
199  
200   if(1 <= sscanf(input, "%255[^=]=%[^\n]", name, contents)) {
201     /* the input was using the correct format */
202     contp = contents;
203
204     if('@' == contp[0]) {
205       /* we use the @-letter to indicate file name(s) */
206       
207       flags = HTTPPOST_FILENAME;
208       contp++;
209
210       post=NULL;
211
212       do {
213         /* since this was a file, it may have a content-type specifier
214            at the end too */
215
216         sep=strchr(contp, FORM_TYPE_SEPARATOR);
217         sep2=strchr(contp, FORM_FILE_SEPARATOR);
218
219         /* pick the closest */
220         if(sep2 && (sep2 < sep)) {
221           sep = sep2;
222
223           /* no type was specified! */
224         }
225         if(sep) {
226
227           /* if we got here on a comma, don't do much */
228           if(FORM_FILE_SEPARATOR != *sep)
229             type = strstr(sep+1, "type=");
230           else
231             type=NULL;
232
233           *sep=0; /* terminate file name at separator */
234
235           if(type) {
236             type += strlen("type=");
237             
238             if(2 != sscanf(type, "%127[^/]/%127[^,\n]",
239                            major, minor)) {
240               free(contents);
241               return 2; /* illegal content-type syntax! */
242             }
243             /* now point beyond the content-type specifier */
244             sep = (char *)type + strlen(major)+strlen(minor)+1;
245
246             /* find the following comma */
247             sep=strchr(sep, FORM_FILE_SEPARATOR);
248           }
249         }
250         else {
251           type=NULL;
252           sep=strchr(contp, FORM_FILE_SEPARATOR);
253         }
254         if(sep) {
255           /* the next file name starts here */
256           *sep =0;
257           sep++;
258         }
259         if(!type) {
260           /*
261            * No type was specified, we scan through a few well-known
262            * extensions and pick the first we match!
263            */
264           struct ContentType {
265             const char *extension;
266             const char *type;
267           };
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"}
274           };
275
276           if(prevtype)
277             /* default to the previously set/used! */
278             type = prevtype;
279           else
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;
283
284           for(i=0; i<sizeof(ctts)/sizeof(ctts[0]); i++) {
285             if(strlen(contp) >= strlen(ctts[i].extension)) {
286               if(strequal(contp +
287                           strlen(contp) - strlen(ctts[i].extension),
288                           ctts[i].extension)) {
289                 type = ctts[i].type;
290                 break;
291               }       
292             }
293           }
294           /* we have a type by now */
295         }
296
297         if(NULL == post) {
298           /* For the first file name, we allocate and initiate the main list
299              node */
300
301           post = (struct curl_httppost *)malloc(sizeof(struct curl_httppost));
302           if(post) {
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;
307             post->flags = flags;
308             if(type) {
309               GetStr(&post->contenttype, (char *)type); /* get type */
310               prevtype=post->contenttype; /* point to the allocated string! */
311             }
312             /* make the previous point to this */
313             if(*last_post)
314               (*last_post)->next = post;
315             else
316               (*httppost) = post;
317
318             (*last_post) = post;          
319           }
320
321         }
322         else {
323           /* we add a file name to the previously allocated node, known as
324              'post' now */
325           subpost =(struct curl_httppost *)
326             malloc(sizeof(struct curl_httppost));
327           if(subpost) {
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;
333              if(type) {
334                GetStr(&subpost->contenttype, (char *)type); /* get type */
335                prevtype=subpost->contenttype; /* point to allocated string! */
336              }
337              /* now, point our 'more' to the original 'more' */
338              subpost->more = post->more;
339
340              /* then move the original 'more' to point to ourselves */
341              post->more = subpost;           
342           }
343         }
344         contp = sep; /* move the contents pointer to after the separator */
345       } while(sep && *sep); /* loop if there's another file name */
346     }
347     else {
348       post = (struct curl_httppost *)malloc(sizeof(struct curl_httppost));
349       if(post) {
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;
356         }
357         else {
358           GetStr(&post->contents, contp); /* get the contents */
359           post->contentslength = 0;
360           post->flags = 0;
361         }
362
363         /* make the previous point to this */
364         if(*last_post)
365           (*last_post)->next = post;
366         else
367           (*httppost) = post;
368
369         (*last_post) = post;      
370       }
371
372     }
373
374   }
375   else {
376     free(contents);
377     return 1;
378   }
379   free(contents);
380   return 0;
381 }
382
383 int curl_formparse(char *input,
384                    struct curl_httppost **httppost,
385                    struct curl_httppost **last_post)
386 {
387   return FormParse(input, httppost, last_post);
388 }
389
390 /***************************************************************************
391  *
392  * AddHttpPost()
393  *      
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.
396  *
397  * Returns newly allocated HttpPost on success and NULL if malloc failed.
398  *
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,
404             char *contenttype,
405             long flags,
406             struct curl_slist* contentHeader,
407             char *showfilename,
408             struct curl_httppost *parent_post,
409             struct curl_httppost **httppost,
410             struct curl_httppost **last_post)
411 {
412   struct curl_httppost *post;
413   post = (struct curl_httppost *)malloc(sizeof(struct curl_httppost));
414   if(post) {
415     memset(post, 0, sizeof(struct curl_httppost));
416     post->name = name;
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;
425     post->flags = flags;
426   }
427   else
428     return NULL;
429   
430   if (parent_post) {
431     /* now, point our 'more' to the original 'more' */
432     post->more = parent_post->more;
433     
434     /* then move the original 'more' to point to ourselves */
435     parent_post->more = post;            
436   }
437   else {
438     /* make the previous point to this */
439     if(*last_post)
440       (*last_post)->next = post;
441     else
442       (*httppost) = post;
443     
444     (*last_post) = post;  
445   }
446   return post;
447 }
448
449 /***************************************************************************
450  *
451  * AddFormInfo()
452  *      
453  * Adds a FormInfo structure to the list presented by parent_form_info.
454  *
455  * Returns newly allocated FormInfo on success and NULL if malloc failed/
456  * parent_form_info is NULL.
457  *
458  ***************************************************************************/
459 static FormInfo * AddFormInfo(char *value,
460                               char *contenttype,
461                               FormInfo *parent_form_info)
462 {
463   FormInfo *form_info;
464   form_info = (FormInfo *)malloc(sizeof(FormInfo));
465   if(form_info) {
466     memset(form_info, 0, sizeof(FormInfo));
467     if (value)
468       form_info->value = value;
469     if (contenttype)
470       form_info->contenttype = contenttype;
471     form_info->flags = HTTPPOST_FILENAME;
472   }
473   else
474     return NULL;
475   
476   if (parent_form_info) {
477     /* now, point our 'more' to the original 'more' */
478     form_info->more = parent_form_info->more;
479     
480     /* then move the original 'more' to point to ourselves */
481     parent_form_info->more = form_info;
482   }
483   else
484     return NULL;
485
486   return form_info;
487 }
488
489 /***************************************************************************
490  *
491  * ContentTypeForFilename()
492  *      
493  * Provides content type for filename if one of the known types (else
494  * (either the prevtype or the default is returned).
495  *
496  * Returns some valid contenttype for filename.
497  *
498  ***************************************************************************/
499 static const char * ContentTypeForFilename (const char *filename,
500                                             const char *prevtype)
501 {
502   const char *contenttype = NULL;
503   unsigned int i;
504   /*
505    * No type was specified, we scan through a few well-known
506    * extensions and pick the first we match!
507    */
508   struct ContentType {
509     const char *extension;
510     const char *type;
511   };
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"}
518   };
519   
520   if(prevtype)
521     /* default to the previously set/used! */
522     contenttype = prevtype;
523   else
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;
527   
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;
534         break;
535       }       
536     }
537   }
538   /* we have a contenttype by now */
539   return contenttype;
540 }
541
542 /***************************************************************************
543  *
544  * AllocAndCopy()
545  *      
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
549  *
550  * Returns 0 on success and 1 if the malloc failed.
551  *
552  ***************************************************************************/
553 static int AllocAndCopy(char **buffer, size_t buffer_length)
554 {
555   const char *src = *buffer;
556   size_t length;
557   bool add = FALSE;
558   if (buffer_length)
559     length = buffer_length;
560   else {
561     length = strlen(*buffer);
562     add = TRUE;
563   }
564   *buffer = (char*)malloc(length+add);
565   if (!*buffer)
566     return 1;
567   memcpy(*buffer, src, length);
568   /* if length unknown do null termination */
569   if (add)
570     (*buffer)[length] = '\0';
571   return 0;
572 }
573
574 /***************************************************************************
575  *
576  * FormAdd()
577  *      
578  * Stores a 'name=value' formpost parameter and builds the appropriate
579  * linked list.
580  *
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
584  * content is stored.
585  *
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).
589  *
590  * Examples:
591  *
592  * Simple name/value pair with copied contents:
593  * curl_formadd (&post, &last, CURLFORM_COPYNAME, "name",
594  * CURLFORM_COPYCONTENTS, "value", CURLFORM_END);
595  *
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)
600  *
601  * storing a filename (CONTENTTYPE is optional!):
602  * curl_formadd (&post, &last, CURLFORM_COPYNAME, "name",
603  * CURLFORM_FILE, "filename1", CURLFORM_CONTENTTYPE, "plain/text",
604  * CURLFORM_END);
605  *
606  * storing multiple filenames:
607  * curl_formadd (&post, &last, CURLFORM_COPYNAME, "name",
608  * CURLFORM_FILE, "filename1", CURLFORM_FILE, "filename2", CURLFORM_END);
609  *
610  * Returns:
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
621  *
622  ***************************************************************************/
623
624 static
625 CURLFORMcode FormAdd(struct curl_httppost **httppost,
626                      struct curl_httppost **last_post,
627                      va_list params)
628 {
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 */
636
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;
641
642   /*
643    * We need to allocate the first struct to fill in.
644    */
645   first_form = (FormInfo *)malloc(sizeof(struct FormInfo));
646   if(first_form) {
647     memset(first_form, 0, sizeof(FormInfo));
648     current_form = first_form;
649   }
650   else
651     return CURL_FORMADD_MEMORY;
652
653   /*
654    * Loop through all the options set.
655    */
656   while (1) {
657
658     /* break if we have an error to report */
659     if (return_value != CURL_FORMADD_OK)
660       break;
661
662     /* first see if we have more parts of the array param */
663     if ( array_state ) {
664       /* get the upcoming option from the given array */
665       option = forms->option;
666       array_value = (char *)forms->value;
667
668       forms++; /* advance this to next entry */
669       if (CURLFORM_END == option) {
670         /* end of array state */
671         array_state = FALSE;
672         continue;
673       }
674     }
675     else {
676       /* This is not array-state, get next option */
677       option = va_arg(params, CURLformoption);
678       if (CURLFORM_END == option)
679         break;
680     }
681
682     switch (option) {
683     case CURLFORM_ARRAY:
684       if(array_state)
685         /* we don't support an array from within an array */
686         return_value = CURL_FORMADD_ILLEGAL_ARRAY;
687       else {
688         forms = va_arg(params, struct curl_forms *);
689         if (forms)
690           array_state = TRUE;
691         else
692           return_value = CURL_FORMADD_NULL;
693       }
694       break;
695
696       /*
697        * Set the Name property.
698        */
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;
704       else {
705         char *name = array_state?
706           array_value:va_arg(params, char *);
707         if (name)
708           current_form->name = name; /* store for the moment */
709         else
710           return_value = CURL_FORMADD_NULL;
711       }
712       break;
713     case CURLFORM_NAMELENGTH:
714       if (current_form->namelength)
715         return_value = CURL_FORMADD_OPTION_TWICE;
716       else
717         current_form->namelength =
718           array_state?(long)array_value:va_arg(params, long);
719       break;
720
721       /*
722        * Set the contents property.
723        */
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;
729       else {
730         char *value =
731           array_state?array_value:va_arg(params, char *);
732         if (value)
733           current_form->value = value; /* store for the moment */
734         else
735           return_value = CURL_FORMADD_NULL;
736       }
737       break;
738     case CURLFORM_CONTENTSLENGTH:
739       if (current_form->contentslength)
740         return_value = CURL_FORMADD_OPTION_TWICE;
741       else
742         current_form->contentslength =
743           array_state?(long)array_value:va_arg(params, long);
744       break;
745
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;
750       else {
751         char *filename = array_state?
752           array_value:va_arg(params, char *);
753         if (filename) {
754           current_form->value = strdup(filename);
755           current_form->flags |= HTTPPOST_READFILE;
756         }
757         else
758           return_value = CURL_FORMADD_NULL;
759       }
760       break;
761
762       /* We upload a file */
763     case CURLFORM_FILE:
764       {
765         char *filename = array_state?array_value:
766           va_arg(params, char *);
767
768         if (current_form->value) {
769           if (current_form->flags & HTTPPOST_FILENAME) {
770             if (filename) {
771               if (!(current_form = AddFormInfo(strdup(filename),
772                                                NULL, current_form)))
773                 return_value = CURL_FORMADD_MEMORY;
774             }
775             else
776               return_value = CURL_FORMADD_NULL;
777           }
778           else
779             return_value = CURL_FORMADD_OPTION_TWICE;
780         }
781         else {
782           if (filename)
783             current_form->value = strdup(filename);
784           else
785             return_value = CURL_FORMADD_NULL;
786           current_form->flags |= HTTPPOST_FILENAME;
787         }
788         break;
789       }
790
791     case CURLFORM_BUFFER:
792       {
793         char *filename = array_state?array_value:
794           va_arg(params, char *);
795
796         if (current_form->value) {
797           if (current_form->flags & HTTPPOST_BUFFER) {
798             if (filename) {
799               if (!(current_form = AddFormInfo(strdup(filename),
800                                                NULL, current_form)))
801                 return_value = CURL_FORMADD_MEMORY;
802             }
803             else
804               return_value = CURL_FORMADD_NULL;
805           }
806           else
807             return_value = CURL_FORMADD_OPTION_TWICE;
808         }
809         else {
810           if (filename)
811             current_form->value = strdup(filename);
812           else
813             return_value = CURL_FORMADD_NULL;
814           current_form->flags |= HTTPPOST_BUFFER;
815         }
816         break;
817       }
818       
819     case CURLFORM_BUFFERPTR:
820         current_form->flags |= HTTPPOST_PTRBUFFER;
821       if (current_form->buffer)
822         return_value = CURL_FORMADD_OPTION_TWICE;
823       else {
824         char *buffer =
825           array_state?array_value:va_arg(params, char *);
826         if (buffer)
827           current_form->buffer = buffer; /* store for the moment */
828         else
829           return_value = CURL_FORMADD_NULL;
830       }
831       break;
832
833     case CURLFORM_BUFFERLENGTH:
834       if (current_form->bufferlength)
835         return_value = CURL_FORMADD_OPTION_TWICE;
836       else
837         current_form->bufferlength =
838           array_state?(long)array_value:va_arg(params, long);
839       break;
840
841     case CURLFORM_CONTENTTYPE:
842       {
843         char *contenttype =
844           array_state?array_value:va_arg(params, char *);
845         if (current_form->contenttype) {
846           if (current_form->flags & HTTPPOST_FILENAME) {
847             if (contenttype) {
848               if (!(current_form = AddFormInfo(NULL,
849                                                strdup(contenttype),
850                                                current_form)))
851                 return_value = CURL_FORMADD_MEMORY;
852             }
853             else
854               return_value = CURL_FORMADD_NULL;
855           }
856           else
857             return_value = CURL_FORMADD_OPTION_TWICE;
858         }
859         else {
860           if (contenttype)
861             current_form->contenttype = strdup(contenttype);
862           else
863             return_value = CURL_FORMADD_NULL;
864         }
865         break;
866       }
867     case CURLFORM_CONTENTHEADER:
868       {
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*);
874         
875         if( current_form->contentheader )
876           return_value = CURL_FORMADD_OPTION_TWICE;
877         else
878           current_form->contentheader = list;
879         
880         break;
881       }
882     case CURLFORM_FILENAME:
883       {
884         char *filename = array_state?array_value:
885           va_arg(params, char *);
886         if( current_form->showfilename )
887           return_value = CURL_FORMADD_OPTION_TWICE;
888         else
889           current_form->showfilename = strdup(filename);
890         break;
891       }
892     default:
893       return_value = CURL_FORMADD_UNKNOWN_OPTION;
894     }
895   }
896
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 */
900     
901     post = NULL;
902     for(form = first_form;
903         form != NULL;
904         form = form->more) {
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) ) ||
910
911            ( (!form->buffer) &&
912              (form->flags & HTTPPOST_BUFFER) &&
913              (form->flags & HTTPPOST_PTRBUFFER) ) ||
914
915            ( (form->flags & HTTPPOST_READFILE) &&
916              (form->flags & HTTPPOST_PTRCONTENTS) )
917            ) {
918         return_value = CURL_FORMADD_INCOMPLETE;
919         break;
920       }
921       else {
922         if ( ((form->flags & HTTPPOST_FILENAME) ||
923               (form->flags & HTTPPOST_BUFFER)) &&
924              !form->contenttype ) {
925           /* our contenttype is missing */
926           form->contenttype
927             = strdup(ContentTypeForFilename(form->value, prevtype));
928         }
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;
934             break;
935           }
936         }
937         if ( !(form->flags & HTTPPOST_FILENAME) &&
938              !(form->flags & HTTPPOST_READFILE) && 
939              !(form->flags & HTTPPOST_PTRCONTENTS) &&
940              !(form->flags & HTTPPOST_PTRBUFFER) ) {
941
942           /* copy value (without strdup; possibly contains null characters) */
943           if (AllocAndCopy(&form->value, form->contentslength)) {
944             return_value = CURL_FORMADD_MEMORY;
945             break;
946           }
947         }
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,
953                            post, httppost,
954                            last_post);
955         
956         if(!post)
957           return_value = CURL_FORMADD_MEMORY;
958
959         if (form->contenttype)
960           prevtype = form->contenttype;
961       }
962     }
963   }
964
965   /* always delete the allocated memory before returning */
966   form = first_form;
967   while (form != NULL) {
968     FormInfo *delete_form;
969     
970     delete_form = form;
971     form = form->more;
972     free (delete_form);
973   }
974
975   return return_value;
976 }
977
978 CURLFORMcode curl_formadd(struct curl_httppost **httppost,
979                  struct curl_httppost **last_post,
980                  ...)
981 {
982   va_list arg;
983   CURLFORMcode result;
984   va_start(arg, last_post);
985   result = FormAdd(httppost, last_post, arg);
986   va_end(arg);
987   return result;
988 }
989
990 static size_t AddFormData(struct FormData **formp,
991                           const void *line,
992                           size_t length)
993 {
994   struct FormData *newform = (struct FormData *)
995     malloc(sizeof(struct FormData));
996   newform->next = NULL;
997
998   /* we make it easier for plain strings: */
999   if(!length)
1000     length = strlen((char *)line);
1001
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 */
1006   
1007   if(*formp) {
1008     (*formp)->next = newform;
1009     *formp = newform;
1010   }
1011   else
1012     *formp = newform;
1013
1014   return length;
1015 }
1016
1017
1018 static size_t AddFormDataf(struct FormData **formp,
1019                         const char *fmt, ...)
1020 {
1021   char s[4096];
1022   va_list ap;
1023   va_start(ap, fmt);
1024   vsprintf(s, fmt, ap);
1025   va_end(ap);
1026
1027   return AddFormData(formp, s, 0);
1028 }
1029
1030
1031 char *Curl_FormBoundary(void)
1032 {
1033   char *retstring;
1034   static int randomizer=0; /* this is just so that two boundaries within
1035                               the same form won't be identical */
1036   size_t i;
1037
1038   static char table16[]="abcdef0123456789";
1039
1040   retstring = (char *)malloc(BOUNDARY_LENGTH+1);
1041
1042   if(!retstring)
1043     return NULL; /* failed */
1044
1045   srand(time(NULL)+randomizer++); /* seed */
1046
1047   strcpy(retstring, "----------------------------");
1048
1049   for(i=strlen(retstring); i<BOUNDARY_LENGTH; i++)
1050     retstring[i] = table16[rand()%16];
1051
1052   /* 28 dashes and 12 hexadecimal digits makes 12^16 (184884258895036416)
1053      combinations */
1054   retstring[BOUNDARY_LENGTH]=0; /* zero terminate */
1055
1056   return retstring;
1057 }
1058
1059 /* Used from http.c, this cleans a built FormData linked list */ 
1060 void Curl_formclean(struct FormData *form)
1061 {
1062   struct FormData *next;
1063
1064   do {
1065     next=form->next;  /* the following form line */
1066     free(form->line); /* free the line */
1067     free(form);       /* free the struct */
1068   
1069   } while((form=next)); /* continue */
1070 }
1071
1072 /* external function to free up a whole form post chain */
1073 void curl_formfree(struct curl_httppost *form)
1074 {
1075   struct curl_httppost *next;
1076
1077   if(!form)
1078     /* no form to free, just get out of this */
1079     return;
1080
1081   do {
1082     next=form->next;  /* the following form line */
1083
1084     /* recurse to sub-contents */
1085     if(form->more)
1086       curl_formfree(form->more);
1087
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 */
1097
1098   } while((form=next)); /* continue */
1099 }
1100
1101 CURLcode Curl_getFormData(struct FormData **finalform,
1102                           struct curl_httppost *post,
1103                           curl_off_t *sizep)
1104 {
1105   struct FormData *form = NULL;
1106   struct FormData *firstform;
1107   struct curl_httppost *file;
1108   CURLcode result = CURLE_OK;
1109
1110   size_t size =0;
1111   char *boundary;
1112   char *fileboundary=NULL;
1113   struct curl_slist* curList;
1114
1115   *finalform=NULL; /* default form is empty */
1116
1117   if(!post)
1118     return result; /* no input => no output! */
1119
1120   boundary = Curl_FormBoundary();
1121   
1122   /* Make the first line of the output */
1123   AddFormDataf(&form,
1124                "Content-Type: multipart/form-data;"
1125                " boundary=%s\r\n",
1126                boundary);
1127   /* we DO NOT count that line since that'll be part of the header! */
1128
1129   firstform = form;
1130   
1131   do {
1132
1133     if(size)
1134       size += AddFormDataf(&form, "\r\n");
1135
1136     /* boundary */
1137     size += AddFormDataf(&form, "--%s\r\n", boundary);
1138
1139     size += AddFormData(&form,
1140                         "Content-Disposition: form-data; name=\"", 0);
1141
1142     size += AddFormData(&form, post->name, post->namelength);
1143
1144     size += AddFormData(&form, "\"", 0);
1145
1146     if(post->more) {
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 */
1149
1150       fileboundary = Curl_FormBoundary();
1151
1152       size += AddFormDataf(&form,
1153                            "\r\nContent-Type: multipart/mixed,"
1154                            " boundary=%s\r\n",
1155                            fileboundary);
1156     }
1157
1158     file = post;
1159
1160     do {
1161
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. */
1165
1166       if(post->more) {
1167         /* if multiple-file */
1168         size += AddFormDataf(&form,
1169                              "\r\n--%s\r\nContent-Disposition: "
1170                              "attachment; filename=\"%s\"",
1171                              fileboundary,
1172                              (file->showfilename?file->showfilename:
1173                               file->contents));
1174       }
1175       else if((post->flags & HTTPPOST_FILENAME) ||
1176               (post->flags & HTTPPOST_BUFFER)) {
1177
1178         size += AddFormDataf(&form,
1179                              "; filename=\"%s\"",
1180                              (post->showfilename?post->showfilename:
1181                               post->contents));
1182       }
1183       
1184       if(file->contenttype) {
1185         /* we have a specified type */
1186         size += AddFormDataf(&form,
1187                              "\r\nContent-Type: %s",
1188                              file->contenttype);
1189       }
1190
1191       curList = file->contentheader;
1192       while( curList ) {
1193         /* Process the additional headers specified for this form */
1194         size += AddFormDataf( &form, "\r\n%s", curList->data );
1195         curList = curList->next;
1196       }
1197
1198 #if 0
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.
1203        */
1204       
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);
1209       }
1210 #endif
1211
1212       size += AddFormData(&form, "\r\n\r\n", 0);
1213
1214       if((post->flags & HTTPPOST_FILENAME) ||
1215          (post->flags & HTTPPOST_READFILE)) {
1216         /* we should include the contents from the specified file */
1217         FILE *fileread;
1218         char buffer[1024];
1219         size_t nread;
1220
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 */
1226         if(fileread) {
1227           while((nread = fread(buffer, 1, 1024, fileread)))
1228             size += AddFormData(&form, buffer, nread);
1229
1230           if(fileread != stdin)
1231             fclose(fileread);
1232         }
1233         else {
1234 #if 0
1235           /* File wasn't found, add a nothing field! */
1236           size += AddFormData(&form, "", 0);
1237 #endif
1238           Curl_formclean(firstform);
1239           free(boundary);
1240           *finalform = NULL;
1241           return CURLE_READ_ERROR;
1242         }
1243
1244       } 
1245       else if (post->flags & HTTPPOST_BUFFER) {
1246           /* include contents of buffer */
1247           size += AddFormData(&form, post->buffer, post->bufferlength);
1248       }
1249
1250       else {
1251         /* include the contents we got */
1252         size += AddFormData(&form, post->contents, post->contentslength);
1253       }
1254     } while((file = file->more)); /* for each specified file for this field */
1255
1256     if(post->more) {
1257       /* this was a multiple-file inclusion, make a termination file
1258          boundary: */
1259       size += AddFormDataf(&form,
1260                            "\r\n--%s--",
1261                            fileboundary);     
1262       free(fileboundary);
1263     }
1264
1265   } while((post=post->next)); /* for each field */
1266
1267   /* end-boundary for everything */
1268   size += AddFormDataf(&form,
1269                        "\r\n--%s--\r\n",
1270                        boundary);
1271
1272   *sizep = size;
1273
1274   free(boundary);
1275
1276   *finalform=firstform;
1277
1278   return result;
1279 }
1280
1281 int Curl_FormInit(struct Form *form, struct FormData *formdata )
1282 {
1283   if(!formdata)
1284     return 1; /* error */
1285
1286   form->data = formdata;
1287   form->sent = 0;
1288
1289   return 0;
1290 }
1291
1292 /* fread() emulation */
1293 size_t Curl_FormReader(char *buffer,
1294                        size_t size,
1295                        size_t nitems,
1296                        FILE *mydata)
1297 {
1298   struct Form *form;
1299   size_t wantedsize;
1300   size_t gotsize = 0;
1301
1302   form=(struct Form *)mydata;
1303
1304   wantedsize = size * nitems;
1305
1306   if(!form->data)
1307     return 0; /* nothing, error, empty */
1308
1309   do {
1310   
1311     if( (form->data->length - form->sent ) > wantedsize - gotsize) {
1312
1313       memcpy(buffer + gotsize , form->data->line + form->sent,
1314              wantedsize - gotsize);
1315
1316       form->sent += wantedsize-gotsize;
1317
1318       return wantedsize;
1319     }
1320
1321     memcpy(buffer+gotsize,
1322            form->data->line + form->sent,
1323            (form->data->length - form->sent) );
1324     gotsize += form->data->length - form->sent;
1325     
1326     form->sent = 0;
1327
1328     form->data = form->data->next; /* advance */
1329
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) */
1334
1335   return gotsize;
1336 }
1337
1338 /* possible (old) fread() emulation that copies at most one line */
1339 size_t Curl_FormReadOneLine(char *buffer,
1340                             size_t size,
1341                             size_t nitems,
1342                             FILE *mydata)
1343 {
1344   struct Form *form;
1345   size_t wantedsize;
1346   size_t gotsize;
1347
1348   form=(struct Form *)mydata;
1349
1350   wantedsize = size * nitems;
1351
1352   if(!form->data)
1353     return 0; /* nothing, error, empty */
1354
1355   do {
1356   
1357     if( (form->data->length - form->sent ) > wantedsize ) {
1358
1359       memcpy(buffer, form->data->line + form->sent, wantedsize);
1360
1361       form->sent += wantedsize;
1362
1363       return wantedsize;
1364     }
1365
1366     memcpy(buffer,
1367            form->data->line + form->sent,
1368            gotsize = (form->data->length - form->sent) );
1369
1370     form->sent = 0;
1371
1372     form->data = form->data->next; /* advance */
1373
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) */
1378
1379   return gotsize;
1380 }
1381
1382
1383 #ifdef _FORM_DEBUG
1384 int FormAddTest(const char * errormsg,
1385                  struct curl_httppost **httppost,
1386                  struct curl_httppost **last_post,
1387                  ...)
1388 {
1389   int result;
1390   va_list arg;
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,
1394              errormsg);
1395   va_end(arg);
1396   return result;
1397 }
1398
1399
1400 int main()
1401 {
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);
1428   int errors = 0;
1429   int size;
1430   size_t nread;
1431   char buffer[4096];
1432   struct curl_httppost *httppost=NULL;
1433   struct curl_httppost *last_post=NULL;
1434   struct curl_forms forms[4];
1435
1436   struct FormData *form;
1437   struct Form formread;
1438
1439   if (FormAddTest("simple COPYCONTENTS test", &httppost, &last_post,
1440                   CURLFORM_COPYNAME, name1, CURLFORM_COPYCONTENTS, value1,
1441                   CURLFORM_END))
1442     ++errors;
1443   if (FormAddTest("COPYCONTENTS  + CONTENTTYPE test", &httppost, &last_post,
1444                   CURLFORM_COPYNAME, name2, CURLFORM_COPYCONTENTS, value2,
1445                   CURLFORM_CONTENTTYPE, type2, CURLFORM_END))
1446     ++errors;
1447   /* make null character at start to check that contentslength works
1448      correctly */
1449   name3[1] = '\0';
1450   value3[1] = '\0';
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))
1456     ++errors;
1457   if (FormAddTest("simple PTRCONTENTS test", &httppost, &last_post,
1458                   CURLFORM_COPYNAME, name4, CURLFORM_PTRCONTENTS, value4,
1459                   CURLFORM_END))
1460     ++errors;
1461   /* make null character at start to check that contentslength works
1462      correctly */
1463   value5[1] = '\0';
1464   if (FormAddTest("PTRCONTENTS + CONTENTSLENGTH test", &httppost, &last_post,
1465                   CURLFORM_COPYNAME, name5, CURLFORM_PTRCONTENTS, value5,
1466                   CURLFORM_CONTENTSLENGTH, value5length, CURLFORM_END))
1467     ++errors;
1468   /* make null character at start to check that contentslength works
1469      correctly */
1470   value6[1] = '\0';
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))
1476     ++errors;
1477   if (FormAddTest("FILE + CONTENTTYPE test", &httppost, &last_post,
1478                   CURLFORM_COPYNAME, name7, CURLFORM_FILE, value7,
1479                   CURLFORM_CONTENTTYPE, type7, CURLFORM_END))
1480     ++errors;
1481   if (FormAddTest("FILE1 + FILE2 test", &httppost, &last_post,
1482                   CURLFORM_COPYNAME, name8, CURLFORM_FILE, value7,
1483                   CURLFORM_FILE, value8, CURLFORM_END))
1484     ++errors;
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))
1488     ++errors;
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,
1498                   CURLFORM_END))
1499     ++errors;
1500   if (FormAddTest("FILECONTENT test", &httppost, &last_post,
1501                   CURLFORM_COPYNAME, name11, CURLFORM_FILECONTENT, value7,
1502                   CURLFORM_END))
1503     ++errors;
1504
1505   form=Curl_getFormData(httppost, &size);
1506
1507   Curl_FormInit(&formread, form);
1508
1509   do {
1510     nread = Curl_FormReader(buffer, 1, sizeof(buffer),
1511                             (FILE *)&formread);
1512
1513     if(-1 == nread)
1514       break;
1515     fwrite(buffer, nread, 1, stdout);
1516   } while(1);
1517
1518   fprintf(stdout, "size: %d\n", size);
1519   if (errors)
1520     fprintf(stdout, "\n==> %d Test(s) failed!\n", errors);
1521   else
1522     fprintf(stdout, "\nAll Tests seem to have worked (please check output)\n");
1523
1524   return 0;
1525 }
1526
1527 #endif
1528
1529 #ifdef _OLD_FORM_DEBUG
1530
1531 int main(int argc, char **argv)
1532 {
1533 #if 0
1534   char *testargs[]={
1535     "name1 = data in number one",
1536     "name2 = number two data",
1537     "test = @upload"
1538   };
1539 #endif
1540   int i;
1541   char *nextarg;
1542   struct curl_httppost *httppost=NULL;
1543   struct curl_httppost *last_post=NULL;
1544   struct curl_httppost *post;
1545   int size;
1546   int nread;
1547   char buffer[4096];
1548
1549   struct FormData *form;
1550   struct Form formread;
1551
1552   for(i=1; i<argc; i++) {
1553
1554     if( FormParse( argv[i],
1555                    &httppost,
1556                    &last_post)) {
1557       fprintf(stderr, "Illegally formatted input field: '%s'!\n",
1558               argv[i]);
1559       return 1;
1560     }
1561   }
1562
1563   form=Curl_getFormData(httppost, &size);
1564
1565   Curl_FormInit(&formread, form);
1566
1567   do {
1568     nread = Curl_FormReader(buffer, 1, sizeof(buffer),
1569                             (FILE *)&formread);
1570
1571     if(-1 == nread)
1572       break;
1573     fwrite(buffer, nread, 1, stderr);
1574   } while(1);
1575
1576   fprintf(stderr, "size: %d\n", size);
1577
1578   return 0;
1579 }
1580
1581 #endif
1582
1583 #endif /* CURL_DISABLE_HTTP */