Only include libgen.h if we have a basename as well.
[platform/upstream/curl.git] / 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$
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  */
101
102 #include "setup.h"
103 #include <curl/curl.h>
104
105 /* Length of the random boundary string. */
106 #define BOUNDARY_LENGTH 40
107
108 #ifndef CURL_DISABLE_HTTP
109
110 #include <stdio.h>
111 #include <stdlib.h>
112 #include <string.h>
113 #include <stdarg.h>
114 #include <time.h>
115 #include <sys/stat.h>
116 #if defined(HAVE_LIBGEN_H) && defined(HAVE_BASENAME)
117 #include <libgen.h>
118 #endif
119 #include "formdata.h"
120 #include "strequal.h"
121 #include "memory.h"
122
123 #define _MPRINTF_REPLACE /* use our functions only */
124 #include <curl/mprintf.h>
125
126 /* The last #include file should be: */
127 #include "memdebug.h"
128
129 /* What kind of Content-Type to use on un-specified files with unrecognized
130    extensions. */
131 #define HTTPPOST_CONTENTTYPE_DEFAULT "application/octet-stream"
132
133 #define FORM_FILE_SEPARATOR ','
134 #define FORM_TYPE_SEPARATOR ';'
135
136 /***************************************************************************
137  *
138  * AddHttpPost()
139  *
140  * Adds a HttpPost structure to the list, if parent_post is given becomes
141  * a subpost of parent_post instead of a direct list element.
142  *
143  * Returns newly allocated HttpPost on success and NULL if malloc failed.
144  *
145  ***************************************************************************/
146 static struct curl_httppost *
147 AddHttpPost(char * name, size_t namelength,
148             char * value, size_t contentslength,
149             char * buffer, size_t bufferlength,
150             char *contenttype,
151             long flags,
152             struct curl_slist* contentHeader,
153             char *showfilename,
154             struct curl_httppost *parent_post,
155             struct curl_httppost **httppost,
156             struct curl_httppost **last_post)
157 {
158   struct curl_httppost *post;
159   post = (struct curl_httppost *)calloc(sizeof(struct curl_httppost), 1);
160   if(post) {
161     post->name = name;
162     post->namelength = (long)(name?(namelength?namelength:strlen(name)):0);
163     post->contents = value;
164     post->contentslength = (long)contentslength;
165     post->buffer = buffer;
166     post->bufferlength = (long)bufferlength;
167     post->contenttype = contenttype;
168     post->contentheader = contentHeader;
169     post->showfilename = showfilename;
170     post->flags = flags;
171   }
172   else
173     return NULL;
174
175   if (parent_post) {
176     /* now, point our 'more' to the original 'more' */
177     post->more = parent_post->more;
178
179     /* then move the original 'more' to point to ourselves */
180     parent_post->more = post;
181   }
182   else {
183     /* make the previous point to this */
184     if(*last_post)
185       (*last_post)->next = post;
186     else
187       (*httppost) = post;
188
189     (*last_post) = post;
190   }
191   return post;
192 }
193
194 /***************************************************************************
195  *
196  * AddFormInfo()
197  *
198  * Adds a FormInfo structure to the list presented by parent_form_info.
199  *
200  * Returns newly allocated FormInfo on success and NULL if malloc failed/
201  * parent_form_info is NULL.
202  *
203  ***************************************************************************/
204 static FormInfo * AddFormInfo(char *value,
205                               char *contenttype,
206                               FormInfo *parent_form_info)
207 {
208   FormInfo *form_info;
209   form_info = (FormInfo *)malloc(sizeof(FormInfo));
210   if(form_info) {
211     memset(form_info, 0, sizeof(FormInfo));
212     if (value)
213       form_info->value = value;
214     if (contenttype)
215       form_info->contenttype = contenttype;
216     form_info->flags = HTTPPOST_FILENAME;
217   }
218   else
219     return NULL;
220
221   if (parent_form_info) {
222     /* now, point our 'more' to the original 'more' */
223     form_info->more = parent_form_info->more;
224
225     /* then move the original 'more' to point to ourselves */
226     parent_form_info->more = form_info;
227   }
228   else
229     return NULL;
230
231   return form_info;
232 }
233
234 /***************************************************************************
235  *
236  * ContentTypeForFilename()
237  *
238  * Provides content type for filename if one of the known types (else
239  * (either the prevtype or the default is returned).
240  *
241  * Returns some valid contenttype for filename.
242  *
243  ***************************************************************************/
244 static const char * ContentTypeForFilename (const char *filename,
245                                             const char *prevtype)
246 {
247   const char *contenttype = NULL;
248   unsigned int i;
249   /*
250    * No type was specified, we scan through a few well-known
251    * extensions and pick the first we match!
252    */
253   struct ContentType {
254     const char *extension;
255     const char *type;
256   };
257   static struct ContentType ctts[]={
258     {".gif",  "image/gif"},
259     {".jpg",  "image/jpeg"},
260     {".jpeg", "image/jpeg"},
261     {".txt",  "text/plain"},
262     {".html", "text/html"}
263   };
264
265   if(prevtype)
266     /* default to the previously set/used! */
267     contenttype = prevtype;
268   else
269     /* It seems RFC1867 defines no Content-Type to default to
270        text/plain so we don't actually need to set this: */
271     contenttype = HTTPPOST_CONTENTTYPE_DEFAULT;
272
273   for(i=0; i<sizeof(ctts)/sizeof(ctts[0]); i++) {
274     if(strlen(filename) >= strlen(ctts[i].extension)) {
275       if(strequal(filename +
276                   strlen(filename) - strlen(ctts[i].extension),
277                   ctts[i].extension)) {
278         contenttype = ctts[i].type;
279         break;
280       }
281     }
282   }
283   /* we have a contenttype by now */
284   return contenttype;
285 }
286
287 /***************************************************************************
288  *
289  * memdup()
290  *
291  * Copies the 'source' data to a newly allocated buffer buffer (that is
292  * returned). Uses buffer_length if not null, else uses strlen to determine
293  * the length of the buffer to be copied
294  *
295  * Returns the new pointer or NULL on failure.
296  *
297  ***************************************************************************/
298 static char *memdup(const char *src, size_t buffer_length)
299 {
300   size_t length;
301   bool add = FALSE;
302   char *buffer;
303
304   if (buffer_length)
305     length = buffer_length;
306   else {
307     length = strlen(src);
308     add = TRUE;
309   }
310   buffer = (char*)malloc(length+add);
311   if (!buffer)
312     return NULL; /* fail */
313
314   memcpy(buffer, src, length);
315
316   /* if length unknown do null termination */
317   if (add)
318     buffer[length] = '\0';
319
320   return buffer;
321 }
322
323 /***************************************************************************
324  *
325  * FormAdd()
326  *
327  * Stores a formpost parameter and builds the appropriate linked list.
328  *
329  * Has two principal functionalities: using files and byte arrays as
330  * post parts. Byte arrays are either copied or just the pointer is stored
331  * (as the user requests) while for files only the filename and not the
332  * content is stored.
333  *
334  * While you may have only one byte array for each name, multiple filenames
335  * are allowed (and because of this feature CURLFORM_END is needed after
336  * using CURLFORM_FILE).
337  *
338  * Examples:
339  *
340  * Simple name/value pair with copied contents:
341  * curl_formadd (&post, &last, CURLFORM_COPYNAME, "name",
342  * CURLFORM_COPYCONTENTS, "value", CURLFORM_END);
343  *
344  * name/value pair where only the content pointer is remembered:
345  * curl_formadd (&post, &last, CURLFORM_COPYNAME, "name",
346  * CURLFORM_PTRCONTENTS, ptr, CURLFORM_CONTENTSLENGTH, 10, CURLFORM_END);
347  * (if CURLFORM_CONTENTSLENGTH is missing strlen () is used)
348  *
349  * storing a filename (CONTENTTYPE is optional!):
350  * curl_formadd (&post, &last, CURLFORM_COPYNAME, "name",
351  * CURLFORM_FILE, "filename1", CURLFORM_CONTENTTYPE, "plain/text",
352  * CURLFORM_END);
353  *
354  * storing multiple filenames:
355  * curl_formadd (&post, &last, CURLFORM_COPYNAME, "name",
356  * CURLFORM_FILE, "filename1", CURLFORM_FILE, "filename2", CURLFORM_END);
357  *
358  * Returns:
359  * CURL_FORMADD_OK             on success
360  * CURL_FORMADD_MEMORY         if the FormInfo allocation fails
361  * CURL_FORMADD_OPTION_TWICE   if one option is given twice for one Form
362  * CURL_FORMADD_NULL           if a null pointer was given for a char
363  * CURL_FORMADD_MEMORY         if the allocation of a FormInfo struct failed
364  * CURL_FORMADD_UNKNOWN_OPTION if an unknown option was used
365  * CURL_FORMADD_INCOMPLETE     if the some FormInfo is not complete (or an error)
366  * CURL_FORMADD_MEMORY         if a HttpPost struct cannot be allocated
367  * CURL_FORMADD_MEMORY         if some allocation for string copying failed.
368  * CURL_FORMADD_ILLEGAL_ARRAY  if an illegal option is used in an array
369  *
370  ***************************************************************************/
371
372 static
373 CURLFORMcode FormAdd(struct curl_httppost **httppost,
374                      struct curl_httppost **last_post,
375                      va_list params)
376 {
377   FormInfo *first_form, *current_form, *form = NULL;
378   CURLFORMcode return_value = CURL_FORMADD_OK;
379   const char *prevtype = NULL;
380   struct curl_httppost *post = NULL;
381   CURLformoption option;
382   struct curl_forms *forms = NULL;
383   char *array_value=NULL; /* value read from an array */
384
385   /* This is a state variable, that if TRUE means that we're parsing an
386      array that we got passed to us. If FALSE we're parsing the input
387      va_list arguments. */
388   bool array_state = FALSE;
389
390   /*
391    * We need to allocate the first struct to fill in.
392    */
393   first_form = (FormInfo *)calloc(sizeof(struct FormInfo), 1);
394   if(!first_form)
395     return CURL_FORMADD_MEMORY;
396
397   current_form = first_form;
398
399   /*
400    * Loop through all the options set. Break if we have an error to report.
401    */
402   while (return_value == CURL_FORMADD_OK) {
403
404     /* first see if we have more parts of the array param */
405     if ( array_state ) {
406       /* get the upcoming option from the given array */
407       option = forms->option;
408       array_value = (char *)forms->value;
409
410       forms++; /* advance this to next entry */
411       if (CURLFORM_END == option) {
412         /* end of array state */
413         array_state = FALSE;
414         continue;
415       }
416     }
417     else {
418       /* This is not array-state, get next option */
419       option = va_arg(params, CURLformoption);
420       if (CURLFORM_END == option)
421         break;
422     }
423
424     switch (option) {
425     case CURLFORM_ARRAY:
426       if(array_state)
427         /* we don't support an array from within an array */
428         return_value = CURL_FORMADD_ILLEGAL_ARRAY;
429       else {
430         forms = va_arg(params, struct curl_forms *);
431         if (forms)
432           array_state = TRUE;
433         else
434           return_value = CURL_FORMADD_NULL;
435       }
436       break;
437
438       /*
439        * Set the Name property.
440        */
441     case CURLFORM_PTRNAME:
442       current_form->flags |= HTTPPOST_PTRNAME; /* fall through */
443     case CURLFORM_COPYNAME:
444       if (current_form->name)
445         return_value = CURL_FORMADD_OPTION_TWICE;
446       else {
447         char *name = array_state?
448           array_value:va_arg(params, char *);
449         if (name)
450           current_form->name = name; /* store for the moment */
451         else
452           return_value = CURL_FORMADD_NULL;
453       }
454       break;
455     case CURLFORM_NAMELENGTH:
456       if (current_form->namelength)
457         return_value = CURL_FORMADD_OPTION_TWICE;
458       else
459         current_form->namelength =
460           array_state?(long)array_value:va_arg(params, long);
461       break;
462
463       /*
464        * Set the contents property.
465        */
466     case CURLFORM_PTRCONTENTS:
467       current_form->flags |= HTTPPOST_PTRCONTENTS; /* fall through */
468     case CURLFORM_COPYCONTENTS:
469       if (current_form->value)
470         return_value = CURL_FORMADD_OPTION_TWICE;
471       else {
472         char *value =
473           array_state?array_value:va_arg(params, char *);
474         if (value)
475           current_form->value = value; /* store for the moment */
476         else
477           return_value = CURL_FORMADD_NULL;
478       }
479       break;
480     case CURLFORM_CONTENTSLENGTH:
481       if (current_form->contentslength)
482         return_value = CURL_FORMADD_OPTION_TWICE;
483       else
484         current_form->contentslength =
485           array_state?(long)array_value:va_arg(params, long);
486       break;
487
488       /* Get contents from a given file name */
489     case CURLFORM_FILECONTENT:
490       if (current_form->flags != 0)
491         return_value = CURL_FORMADD_OPTION_TWICE;
492       else {
493         char *filename = array_state?
494           array_value:va_arg(params, char *);
495         if (filename) {
496           current_form->value = strdup(filename);
497           if(!current_form->value)
498             return_value = CURL_FORMADD_MEMORY;
499           else {
500             current_form->flags |= HTTPPOST_READFILE;
501             current_form->value_alloc = TRUE;
502           }
503         }
504         else
505           return_value = CURL_FORMADD_NULL;
506       }
507       break;
508
509       /* We upload a file */
510     case CURLFORM_FILE:
511       {
512         char *filename = array_state?array_value:
513           va_arg(params, char *);
514
515         if (current_form->value) {
516           if (current_form->flags & HTTPPOST_FILENAME) {
517             if (filename) {
518               if (!(current_form = AddFormInfo(strdup(filename),
519                                                NULL, current_form)))
520                 return_value = CURL_FORMADD_MEMORY;
521             }
522             else
523               return_value = CURL_FORMADD_NULL;
524           }
525           else
526             return_value = CURL_FORMADD_OPTION_TWICE;
527         }
528         else {
529           if (filename) {
530             current_form->value = strdup(filename);
531             if(!current_form->value)
532               return_value = CURL_FORMADD_MEMORY;
533             else {
534               current_form->flags |= HTTPPOST_FILENAME;
535               current_form->value_alloc = TRUE;
536             }
537           }
538           else
539             return_value = CURL_FORMADD_NULL;
540         }
541         break;
542       }
543
544     case CURLFORM_BUFFER:
545       {
546         char *filename = array_state?array_value:
547           va_arg(params, char *);
548
549         if (current_form->value) {
550           if (current_form->flags & HTTPPOST_BUFFER) {
551             if (filename) {
552               if (!(current_form = AddFormInfo(strdup(filename),
553                                                NULL, current_form)))
554                 return_value = CURL_FORMADD_MEMORY;
555             }
556             else
557               return_value = CURL_FORMADD_NULL;
558           }
559           else
560             return_value = CURL_FORMADD_OPTION_TWICE;
561         }
562         else {
563           if (filename) {
564             current_form->value = strdup(filename);
565             if(!current_form->value)
566               return_value = CURL_FORMADD_MEMORY;
567           }
568           else
569             return_value = CURL_FORMADD_NULL;
570           current_form->flags |= HTTPPOST_BUFFER;
571         }
572         break;
573       }
574
575     case CURLFORM_BUFFERPTR:
576         current_form->flags |= HTTPPOST_PTRBUFFER;
577       if (current_form->buffer)
578         return_value = CURL_FORMADD_OPTION_TWICE;
579       else {
580         char *buffer =
581           array_state?array_value:va_arg(params, char *);
582         if (buffer)
583           current_form->buffer = buffer; /* store for the moment */
584         else
585           return_value = CURL_FORMADD_NULL;
586       }
587       break;
588
589     case CURLFORM_BUFFERLENGTH:
590       if (current_form->bufferlength)
591         return_value = CURL_FORMADD_OPTION_TWICE;
592       else
593         current_form->bufferlength =
594           array_state?(long)array_value:va_arg(params, long);
595       break;
596
597     case CURLFORM_CONTENTTYPE:
598       {
599         char *contenttype =
600           array_state?array_value:va_arg(params, char *);
601         if (current_form->contenttype) {
602           if (current_form->flags & HTTPPOST_FILENAME) {
603             if (contenttype) {
604               if (!(current_form = AddFormInfo(NULL,
605                                                strdup(contenttype),
606                                                current_form)))
607                 return_value = CURL_FORMADD_MEMORY;
608             }
609             else
610               return_value = CURL_FORMADD_NULL;
611           }
612           else
613             return_value = CURL_FORMADD_OPTION_TWICE;
614         }
615         else {
616           if (contenttype) {
617             current_form->contenttype = strdup(contenttype);
618             if(!current_form->contenttype)
619               return_value = CURL_FORMADD_MEMORY;
620             else
621               current_form->contenttype_alloc = TRUE;
622           }
623           else
624             return_value = CURL_FORMADD_NULL;
625         }
626         break;
627       }
628     case CURLFORM_CONTENTHEADER:
629       {
630         /* this "cast increases required alignment of target type" but
631            we consider it OK anyway */
632         struct curl_slist* list = array_state?
633           (struct curl_slist*)array_value:
634           va_arg(params, struct curl_slist*);
635
636         if( current_form->contentheader )
637           return_value = CURL_FORMADD_OPTION_TWICE;
638         else
639           current_form->contentheader = list;
640
641         break;
642       }
643     case CURLFORM_FILENAME:
644       {
645         char *filename = array_state?array_value:
646           va_arg(params, char *);
647         if( current_form->showfilename )
648           return_value = CURL_FORMADD_OPTION_TWICE;
649         else {
650           current_form->showfilename = strdup(filename);
651           if(!current_form->showfilename)
652             return_value = CURL_FORMADD_MEMORY;
653           else
654             current_form->showfilename_alloc = TRUE;
655         }
656         break;
657       }
658     default:
659       return_value = CURL_FORMADD_UNKNOWN_OPTION;
660     }
661   }
662
663   if(CURL_FORMADD_OK == return_value) {
664     /* go through the list, check for copleteness and if everything is
665      * alright add the HttpPost item otherwise set return_value accordingly */
666
667     post = NULL;
668     for(form = first_form;
669         form != NULL;
670         form = form->more) {
671       if ( ((!form->name || !form->value) && !post) ||
672            ( (form->contentslength) &&
673              (form->flags & HTTPPOST_FILENAME) ) ||
674            ( (form->flags & HTTPPOST_FILENAME) &&
675              (form->flags & HTTPPOST_PTRCONTENTS) ) ||
676
677            ( (!form->buffer) &&
678              (form->flags & HTTPPOST_BUFFER) &&
679              (form->flags & HTTPPOST_PTRBUFFER) ) ||
680
681            ( (form->flags & HTTPPOST_READFILE) &&
682              (form->flags & HTTPPOST_PTRCONTENTS) )
683            ) {
684         return_value = CURL_FORMADD_INCOMPLETE;
685         break;
686       }
687       else {
688         if ( ((form->flags & HTTPPOST_FILENAME) ||
689               (form->flags & HTTPPOST_BUFFER)) &&
690              !form->contenttype ) {
691           /* our contenttype is missing */
692           form->contenttype
693             = strdup(ContentTypeForFilename(form->value, prevtype));
694           if(!form->contenttype) {
695             return_value = CURL_FORMADD_MEMORY;
696             break;
697           }
698           form->contenttype_alloc = TRUE;
699         }
700         if ( !(form->flags & HTTPPOST_PTRNAME) &&
701              (form == first_form) ) {
702           /* copy name (without strdup; possibly contains null characters) */
703           form->name = memdup(form->name, form->namelength);
704           if (!form->name) {
705             return_value = CURL_FORMADD_MEMORY;
706             break;
707           }
708           form->name_alloc = TRUE;
709         }
710         if ( !(form->flags & HTTPPOST_FILENAME) &&
711              !(form->flags & HTTPPOST_READFILE) &&
712              !(form->flags & HTTPPOST_PTRCONTENTS) &&
713              !(form->flags & HTTPPOST_PTRBUFFER) ) {
714           /* copy value (without strdup; possibly contains null characters) */
715           form->value = memdup(form->value, form->contentslength);
716           if (!form->value) {
717             return_value = CURL_FORMADD_MEMORY;
718             break;
719           }
720           form->value_alloc = TRUE;
721         }
722         post = AddHttpPost(form->name, form->namelength,
723                            form->value, form->contentslength,
724                            form->buffer, form->bufferlength,
725                            form->contenttype, form->flags,
726                            form->contentheader, form->showfilename,
727                            post, httppost,
728                            last_post);
729
730         if(!post) {
731           return_value = CURL_FORMADD_MEMORY;
732           break;
733         }
734
735         if (form->contenttype)
736           prevtype = form->contenttype;
737       }
738     }
739   }
740
741   if(return_value) {
742     /* we return on error, free possibly allocated fields */
743     if(!form)
744       form = current_form;
745     if(form) {
746       if(form->name_alloc)
747         free(form->name);
748       if(form->value_alloc)
749         free(form->value);
750       if(form->contenttype_alloc)
751         free(form->contenttype);
752       if(form->showfilename_alloc)
753         free(form->showfilename);
754     }
755   }
756
757   /* always delete the allocated memory before returning */
758   form = first_form;
759   while (form != NULL) {
760     FormInfo *delete_form;
761
762     delete_form = form;
763     form = form->more;
764     free (delete_form);
765   }
766
767   return return_value;
768 }
769
770 /*
771  * curl_formadd() is a public API to add a section to the multipart formpost.
772  */
773
774 CURLFORMcode curl_formadd(struct curl_httppost **httppost,
775                           struct curl_httppost **last_post,
776                           ...)
777 {
778   va_list arg;
779   CURLFORMcode result;
780   va_start(arg, last_post);
781   result = FormAdd(httppost, last_post, arg);
782   va_end(arg);
783   return result;
784 }
785
786 /*
787  * AddFormData() adds a chunk of data to the FormData linked list.
788  *
789  * size is incremented by the chunk length, unless it is NULL
790  */
791 static CURLcode AddFormData(struct FormData **formp,
792                             enum formtype type,
793                             const void *line,
794                             size_t length,
795                             curl_off_t *size)
796 {
797   struct FormData *newform = (struct FormData *)
798     malloc(sizeof(struct FormData));
799   if (!newform)
800     return CURLE_OUT_OF_MEMORY;
801   newform->next = NULL;
802
803   /* we make it easier for plain strings: */
804   if(!length)
805     length = strlen((char *)line);
806
807   newform->line = (char *)malloc(length+1);
808   if (!newform->line) {
809     free(newform);
810     return CURLE_OUT_OF_MEMORY;
811   }
812   memcpy(newform->line, line, length);
813   newform->length = length;
814   newform->line[length]=0; /* zero terminate for easier debugging */
815   newform->type = type;
816
817   if(*formp) {
818     (*formp)->next = newform;
819     *formp = newform;
820   }
821   else
822     *formp = newform;
823
824   if (size) {
825     if(type == FORM_DATA)
826       *size += length;
827     else {
828       /* Since this is a file to be uploaded here, add the size of the actual
829          file */
830       if(!strequal("-", newform->line)) {
831         struct stat file;
832         if(!stat(newform->line, &file)) {
833           *size += file.st_size;
834         }
835       }
836     }
837   }
838   return CURLE_OK;
839 }
840
841 /*
842  * AddFormDataf() adds printf()-style formatted data to the formdata chain.
843  */
844
845 static CURLcode AddFormDataf(struct FormData **formp,
846                              curl_off_t *size,
847                              const char *fmt, ...)
848 {
849   char s[4096];
850   va_list ap;
851   va_start(ap, fmt);
852   vsnprintf(s, sizeof(s), fmt, ap);
853   va_end(ap);
854
855   return AddFormData(formp, FORM_DATA, s, 0, size);
856 }
857
858 /*
859  * Curl_formclean() is used from http.c, this cleans a built FormData linked
860  * list
861  */
862 void Curl_formclean(struct FormData *form)
863 {
864   struct FormData *next;
865
866   if(!form)
867     return;
868
869   do {
870     next=form->next;  /* the following form line */
871     free(form->line); /* free the line */
872     free(form);       /* free the struct */
873
874   } while((form=next)); /* continue */
875 }
876
877 /*
878  * curl_formfree() is an external function to free up a whole form post
879  * chain
880  */
881 void curl_formfree(struct curl_httppost *form)
882 {
883   struct curl_httppost *next;
884
885   if(!form)
886     /* no form to free, just get out of this */
887     return;
888
889   do {
890     next=form->next;  /* the following form line */
891
892     /* recurse to sub-contents */
893     if(form->more)
894       curl_formfree(form->more);
895
896     if( !(form->flags & HTTPPOST_PTRNAME) && form->name)
897       free(form->name); /* free the name */
898     if( !(form->flags & HTTPPOST_PTRCONTENTS) && form->contents)
899       free(form->contents); /* free the contents */
900     if(form->contenttype)
901       free(form->contenttype); /* free the content type */
902     if(form->showfilename)
903       free(form->showfilename); /* free the faked file name */
904     free(form);       /* free the struct */
905
906   } while((form=next)); /* continue */
907 }
908
909 #ifndef HAVE_BASENAME
910 /*
911   (Quote from The Open Group Base Specifications Issue 6 IEEE Std 1003.1, 2004
912   Edition)
913
914   The basename() function shall take the pathname pointed to by path and
915   return a pointer to the final component of the pathname, deleting any
916   trailing '/' characters.
917
918   If the string pointed to by path consists entirely of the '/' character,
919   basename() shall return a pointer to the string "/". If the string pointed
920   to by path is exactly "//", it is implementation-defined whether '/' or "//"
921   is returned.
922
923   If path is a null pointer or points to an empty string, basename() shall
924   return a pointer to the string ".".
925
926   The basename() function may modify the string pointed to by path, and may
927   return a pointer to static storage that may then be overwritten by a
928   subsequent call to basename().
929
930   The basename() function need not be reentrant. A function that is not
931   required to be reentrant is not required to be thread-safe.
932
933 */
934 static char *basename(char *path)
935 {
936   /* Ignore all the details above for now and make a quick and simple
937      implementaion here */
938   char *s1;
939   char *s2;
940
941   s1=strrchr(path, '/');
942   s2=strrchr(path, '\\');
943
944   if(s1 && s2) {
945     path = (s1 > s2? s1 : s2)+1;
946   }
947   else if(s1)
948     path = s1 + 1;
949   else if(s2)
950     path = s2 + 1;
951
952   return path;
953 }
954 #endif
955
956 static char *strippath(char *fullfile)
957 {
958   char *filename;
959   char *base;
960   filename = strdup(fullfile); /* duplicate since basename() may ruin the
961                                   buffer it works on */
962   if(!filename)
963     return NULL;
964   base = strdup(basename(filename));
965
966   free(filename); /* free temporary buffer */
967
968   return base; /* returns an allocated string! */
969 }
970
971 /*
972  * Curl_getFormData() converts a linked list of "meta data" into a complete
973  * (possibly huge) multipart formdata. The input list is in 'post', while the
974  * output resulting linked lists gets stored in '*finalform'. *sizep will get
975  * the total size of the whole POST.
976  */
977
978 CURLcode Curl_getFormData(struct FormData **finalform,
979                           struct curl_httppost *post,
980                           curl_off_t *sizep)
981 {
982   struct FormData *form = NULL;
983   struct FormData *firstform;
984   struct curl_httppost *file;
985   CURLcode result = CURLE_OK;
986
987   curl_off_t size=0; /* support potentially ENORMOUS formposts */
988   char *boundary;
989   char *fileboundary=NULL;
990   struct curl_slist* curList;
991
992   *finalform=NULL; /* default form is empty */
993
994   if(!post)
995     return result; /* no input => no output! */
996
997   boundary = Curl_FormBoundary();
998   if(!boundary)
999     return CURLE_OUT_OF_MEMORY;
1000
1001   /* Make the first line of the output */
1002   result = AddFormDataf(&form, NULL,
1003                         "Content-Type: multipart/form-data;"
1004                         " boundary=%s\r\n",
1005                         boundary);
1006   if (result) {
1007     free(boundary);
1008     return result;
1009   }
1010   /* we DO NOT include that line in the total size of the POST, since it'll be
1011      part of the header! */
1012
1013   firstform = form;
1014
1015   do {
1016
1017     if(size) {
1018       result = AddFormDataf(&form, &size, "\r\n");
1019       if (result)
1020         break;
1021     }
1022
1023     /* boundary */
1024     result = AddFormDataf(&form, &size, "--%s\r\n", boundary);
1025     if (result)
1026       break;
1027
1028     result = AddFormDataf(&form, &size,
1029                           "Content-Disposition: form-data; name=\"");
1030     if (result)
1031       break;
1032
1033     result = AddFormData(&form, FORM_DATA, post->name, post->namelength,
1034                          &size);
1035     if (result)
1036       break;
1037
1038     result = AddFormDataf(&form, &size, "\"");
1039     if (result)
1040       break;
1041
1042     if(post->more) {
1043       /* If used, this is a link to more file names, we must then do
1044          the magic to include several files with the same field name */
1045
1046       fileboundary = Curl_FormBoundary();
1047
1048       result = AddFormDataf(&form, &size,
1049                             "\r\nContent-Type: multipart/mixed,"
1050                             " boundary=%s\r\n",
1051                             fileboundary);
1052       if (result)
1053         break;
1054     }
1055
1056     file = post;
1057
1058     do {
1059
1060       /* If 'showfilename' is set, that is a faked name passed on to us
1061          to use to in the formpost. If that is not set, the actually used
1062          local file name should be added. */
1063
1064       if(post->more) {
1065         /* if multiple-file */
1066         char *filebasename=
1067           (!file->showfilename)?strippath(file->contents):NULL;
1068
1069         result = AddFormDataf(&form, &size,
1070                               "\r\n--%s\r\nContent-Disposition: "
1071                               "attachment; filename=\"%s\"",
1072                               fileboundary,
1073                               (file->showfilename?file->showfilename:
1074                                filebasename));
1075         if (filebasename)
1076           free(filebasename);
1077         if (result)
1078           break;
1079       }
1080       else if((post->flags & HTTPPOST_FILENAME) ||
1081               (post->flags & HTTPPOST_BUFFER)) {
1082
1083         char *filebasename=
1084           (!post->showfilename)?strippath(post->contents):NULL;
1085
1086         result = AddFormDataf(&form, &size,
1087                               "; filename=\"%s\"",
1088                               (post->showfilename?post->showfilename:
1089                                filebasename));
1090         if (filebasename)
1091           free(filebasename);
1092
1093         if (result)
1094           break;
1095       }
1096
1097       if(file->contenttype) {
1098         /* we have a specified type */
1099         result = AddFormDataf(&form, &size,
1100                               "\r\nContent-Type: %s",
1101                               file->contenttype);
1102         if (result)
1103           break;
1104       }
1105
1106       curList = file->contentheader;
1107       while( curList ) {
1108         /* Process the additional headers specified for this form */
1109         result = AddFormDataf( &form, &size, "\r\n%s", curList->data );
1110         if (result)
1111           break;
1112         curList = curList->next;
1113       }
1114       if (result) {
1115         Curl_formclean(firstform);
1116         free(boundary);
1117         return result;
1118       }
1119
1120 #if 0
1121       /* The header Content-Transfer-Encoding: seems to confuse some receivers
1122        * (like the built-in PHP engine). While I can't see any reason why it
1123        * should, I can just as well skip this to the benefit of the users who
1124        * are using such confused receivers.
1125        */
1126
1127       if(file->contenttype &&
1128          !checkprefix("text/", file->contenttype)) {
1129         /* this is not a text content, mention our binary encoding */
1130         size += AddFormData(&form, "\r\nContent-Transfer-Encoding: binary", 0);
1131       }
1132 #endif
1133
1134       result = AddFormDataf(&form, &size, "\r\n\r\n");
1135       if (result)
1136         break;
1137
1138       if((post->flags & HTTPPOST_FILENAME) ||
1139          (post->flags & HTTPPOST_READFILE)) {
1140         /* we should include the contents from the specified file */
1141         FILE *fileread;
1142
1143         fileread = strequal("-", file->contents)?
1144           stdin:fopen(file->contents, "rb"); /* binary read for win32  */
1145
1146         /*
1147          * VMS: This only allows for stream files on VMS.  Stream files are
1148          * OK, as are FIXED & VAR files WITHOUT implied CC For implied CC,
1149          * every record needs to have a \n appended & 1 added to SIZE
1150          */
1151
1152         if(fileread) {
1153           if(fileread != stdin) {
1154             /* close the file again */
1155             fclose(fileread);
1156             /* add the file name only - for later reading from this */
1157             result = AddFormData(&form, FORM_FILE, file->contents, 0, &size);
1158           }
1159           else {
1160             /* When uploading from stdin, we can't know the size of the file,
1161              * thus must read the full file as before. We *could* use chunked
1162              * transfer-encoding, but that only works for HTTP 1.1 and we
1163              * can't be sure we work with such a server.
1164              */
1165             size_t nread;
1166             char buffer[512];
1167             while((nread = fread(buffer, 1, sizeof(buffer), fileread))) {
1168               result = AddFormData(&form, FORM_DATA, buffer, nread, &size);
1169               if (result)
1170                 break;
1171             }
1172           }
1173
1174           if (result) {
1175             Curl_formclean(firstform);
1176             free(boundary);
1177             return result;
1178           }
1179
1180         }
1181         else {
1182           Curl_formclean(firstform);
1183           free(boundary);
1184           *finalform = NULL;
1185           return CURLE_READ_ERROR;
1186         }
1187
1188       }
1189       else if (post->flags & HTTPPOST_BUFFER) {
1190         /* include contents of buffer */
1191         result = AddFormData(&form, FORM_DATA, post->buffer,
1192                              post->bufferlength, &size);
1193           if (result)
1194             break;
1195       }
1196
1197       else {
1198         /* include the contents we got */
1199         result = AddFormData(&form, FORM_DATA, post->contents,
1200                              post->contentslength, &size);
1201         if (result)
1202           break;
1203       }
1204     } while((file = file->more)); /* for each specified file for this field */
1205     if (result) {
1206       Curl_formclean(firstform);
1207       free(boundary);
1208       return result;
1209     }
1210
1211     if(post->more) {
1212       /* this was a multiple-file inclusion, make a termination file
1213          boundary: */
1214       result = AddFormDataf(&form, &size,
1215                            "\r\n--%s--",
1216                            fileboundary);
1217       free(fileboundary);
1218       if (result)
1219         break;
1220     }
1221
1222   } while((post=post->next)); /* for each field */
1223   if (result) {
1224     Curl_formclean(firstform);
1225     free(boundary);
1226     return result;
1227   }
1228
1229   /* end-boundary for everything */
1230   result = AddFormDataf(&form, &size,
1231                        "\r\n--%s--\r\n",
1232                        boundary);
1233   if (result) {
1234     Curl_formclean(firstform);
1235     free(boundary);
1236     return result;
1237   }
1238
1239   *sizep = size;
1240
1241   free(boundary);
1242
1243   *finalform=firstform;
1244
1245   return result;
1246 }
1247
1248 /*
1249  * Curl_FormInit() inits the struct 'form' points to with the 'formdata'
1250  * and resets the 'sent' counter.
1251  */
1252 int Curl_FormInit(struct Form *form, struct FormData *formdata )
1253 {
1254   if(!formdata)
1255     return 1; /* error */
1256
1257   form->data = formdata;
1258   form->sent = 0;
1259   form->fp = NULL;
1260
1261   return 0;
1262 }
1263
1264 static size_t readfromfile(struct Form *form, char *buffer, size_t size)
1265 {
1266   size_t nread;
1267   if(!form->fp) {
1268     /* this file hasn't yet been opened */
1269     form->fp = fopen(form->data->line, "rb"); /* b is for binary */
1270     if(!form->fp)
1271       return -1; /* failure */
1272   }
1273   nread = fread(buffer, 1, size, form->fp);
1274
1275   if(nread != size) {
1276     /* this is the last chunk form the file, move on */
1277     fclose(form->fp);
1278     form->fp = NULL;
1279     form->data = form->data->next;
1280   }
1281
1282   return nread;
1283 }
1284
1285 /*
1286  * Curl_FormReader() is the fread() emulation function that will be used to
1287  * deliver the formdata to the transfer loop and then sent away to the peer.
1288  */
1289 size_t Curl_FormReader(char *buffer,
1290                        size_t size,
1291                        size_t nitems,
1292                        FILE *mydata)
1293 {
1294   struct Form *form;
1295   size_t wantedsize;
1296   size_t gotsize = 0;
1297
1298   form=(struct Form *)mydata;
1299
1300   wantedsize = size * nitems;
1301
1302   if(!form->data)
1303     return 0; /* nothing, error, empty */
1304
1305   if(form->data->type == FORM_FILE)
1306     return readfromfile(form, buffer, wantedsize);
1307
1308   do {
1309
1310     if( (form->data->length - form->sent ) > wantedsize - gotsize) {
1311
1312       memcpy(buffer + gotsize , form->data->line + form->sent,
1313              wantedsize - gotsize);
1314
1315       form->sent += wantedsize-gotsize;
1316
1317       return wantedsize;
1318     }
1319
1320     memcpy(buffer+gotsize,
1321            form->data->line + form->sent,
1322            (form->data->length - form->sent) );
1323     gotsize += form->data->length - form->sent;
1324
1325     form->sent = 0;
1326
1327     form->data = form->data->next; /* advance */
1328
1329   } while(form->data && (form->data->type == FORM_DATA));
1330   /* If we got an empty line and we have more data, we proceed to the next
1331      line immediately to avoid returning zero before we've reached the end.
1332      This is the bug reported November 22 1999 on curl 6.3. (Daniel) */
1333
1334   return gotsize;
1335 }
1336
1337 /*
1338  * Curl_formpostheader() returns the first line of the formpost, the
1339  * request-header part (which is not part of the request-body like the rest of
1340  * the post).
1341  */
1342 char *Curl_formpostheader(void *formp, size_t *len)
1343 {
1344   char *header;
1345   struct Form *form=(struct Form *)formp;
1346
1347   if(!form->data)
1348     return 0; /* nothing, ERROR! */
1349
1350   header = form->data->line;
1351   *len = form->data->length;
1352
1353   form->data = form->data->next; /* advance */
1354
1355   return header;
1356 }
1357
1358
1359 #ifdef _FORM_DEBUG
1360 int FormAddTest(const char * errormsg,
1361                  struct curl_httppost **httppost,
1362                  struct curl_httppost **last_post,
1363                  ...)
1364 {
1365   int result;
1366   va_list arg;
1367   va_start(arg, last_post);
1368   if ((result = FormAdd(httppost, last_post, arg)))
1369     fprintf (stderr, "ERROR doing FormAdd ret: %d action: %s\n", result,
1370              errormsg);
1371   va_end(arg);
1372   return result;
1373 }
1374
1375
1376 int main()
1377 {
1378   char name1[] = "simple_COPYCONTENTS";
1379   char name2[] = "COPYCONTENTS_+_CONTENTTYPE";
1380   char name3[] = "PTRNAME_+_NAMELENGTH_+_COPYNAME_+_CONTENTSLENGTH";
1381   char name4[] = "simple_PTRCONTENTS";
1382   char name5[] = "PTRCONTENTS_+_CONTENTSLENGTH";
1383   char name6[] = "PTRCONTENTS_+_CONTENTSLENGTH_+_CONTENTTYPE";
1384   char name7[] = "FILE1_+_CONTENTTYPE";
1385   char name8[] = "FILE1_+_FILE2";
1386   char name9[] = "FILE1_+_FILE2_+_FILE3";
1387   char name10[] = "ARRAY: FILE1_+_FILE2_+_FILE3";
1388   char name11[] = "FILECONTENT";
1389   char value1[] = "value for simple COPYCONTENTS";
1390   char value2[] = "value for COPYCONTENTS + CONTENTTYPE";
1391   char value3[] = "value for PTRNAME + NAMELENGTH + COPYNAME + CONTENTSLENGTH";
1392   char value4[] = "value for simple PTRCONTENTS";
1393   char value5[] = "value for PTRCONTENTS + CONTENTSLENGTH";
1394   char value6[] = "value for PTRCOTNENTS + CONTENTSLENGTH + CONTENTTYPE";
1395   char value7[] = "inet_ntoa_r.h";
1396   char value8[] = "Makefile.b32.resp";
1397   char type2[] = "image/gif";
1398   char type6[] = "text/plain";
1399   char type7[] = "text/html";
1400   int name3length = strlen(name3);
1401   int value3length = strlen(value3);
1402   int value5length = strlen(value4);
1403   int value6length = strlen(value5);
1404   int errors = 0;
1405   int size;
1406   size_t nread;
1407   char buffer[4096];
1408   struct curl_httppost *httppost=NULL;
1409   struct curl_httppost *last_post=NULL;
1410   struct curl_forms forms[4];
1411
1412   struct FormData *form;
1413   struct Form formread;
1414
1415   if (FormAddTest("simple COPYCONTENTS test", &httppost, &last_post,
1416                   CURLFORM_COPYNAME, name1, CURLFORM_COPYCONTENTS, value1,
1417                   CURLFORM_END))
1418     ++errors;
1419   if (FormAddTest("COPYCONTENTS  + CONTENTTYPE test", &httppost, &last_post,
1420                   CURLFORM_COPYNAME, name2, CURLFORM_COPYCONTENTS, value2,
1421                   CURLFORM_CONTENTTYPE, type2, CURLFORM_END))
1422     ++errors;
1423   /* make null character at start to check that contentslength works
1424      correctly */
1425   name3[1] = '\0';
1426   value3[1] = '\0';
1427   if (FormAddTest("PTRNAME + NAMELENGTH + COPYNAME + CONTENTSLENGTH test",
1428                   &httppost, &last_post,
1429                   CURLFORM_PTRNAME, name3, CURLFORM_COPYCONTENTS, value3,
1430                   CURLFORM_CONTENTSLENGTH, value3length,
1431                   CURLFORM_NAMELENGTH, name3length, CURLFORM_END))
1432     ++errors;
1433   if (FormAddTest("simple PTRCONTENTS test", &httppost, &last_post,
1434                   CURLFORM_COPYNAME, name4, CURLFORM_PTRCONTENTS, value4,
1435                   CURLFORM_END))
1436     ++errors;
1437   /* make null character at start to check that contentslength works
1438      correctly */
1439   value5[1] = '\0';
1440   if (FormAddTest("PTRCONTENTS + CONTENTSLENGTH test", &httppost, &last_post,
1441                   CURLFORM_COPYNAME, name5, CURLFORM_PTRCONTENTS, value5,
1442                   CURLFORM_CONTENTSLENGTH, value5length, CURLFORM_END))
1443     ++errors;
1444   /* make null character at start to check that contentslength works
1445      correctly */
1446   value6[1] = '\0';
1447   if (FormAddTest("PTRCONTENTS + CONTENTSLENGTH + CONTENTTYPE test",
1448                   &httppost, &last_post,
1449                   CURLFORM_COPYNAME, name6, CURLFORM_PTRCONTENTS, value6,
1450                   CURLFORM_CONTENTSLENGTH, value6length,
1451                   CURLFORM_CONTENTTYPE, type6, CURLFORM_END))
1452     ++errors;
1453   if (FormAddTest("FILE + CONTENTTYPE test", &httppost, &last_post,
1454                   CURLFORM_COPYNAME, name7, CURLFORM_FILE, value7,
1455                   CURLFORM_CONTENTTYPE, type7, CURLFORM_END))
1456     ++errors;
1457   if (FormAddTest("FILE1 + FILE2 test", &httppost, &last_post,
1458                   CURLFORM_COPYNAME, name8, CURLFORM_FILE, value7,
1459                   CURLFORM_FILE, value8, CURLFORM_END))
1460     ++errors;
1461   if (FormAddTest("FILE1 + FILE2 + FILE3 test", &httppost, &last_post,
1462                   CURLFORM_COPYNAME, name9, CURLFORM_FILE, value7,
1463                   CURLFORM_FILE, value8, CURLFORM_FILE, value7, CURLFORM_END))
1464     ++errors;
1465   forms[0].option = CURLFORM_FILE;
1466   forms[0].value  = value7;
1467   forms[1].option = CURLFORM_FILE;
1468   forms[1].value  = value8;
1469   forms[2].option = CURLFORM_FILE;
1470   forms[2].value  = value7;
1471   forms[3].option  = CURLFORM_END;
1472   if (FormAddTest("FILE1 + FILE2 + FILE3 ARRAY test", &httppost, &last_post,
1473                   CURLFORM_COPYNAME, name10, CURLFORM_ARRAY, forms,
1474                   CURLFORM_END))
1475     ++errors;
1476   if (FormAddTest("FILECONTENT test", &httppost, &last_post,
1477                   CURLFORM_COPYNAME, name11, CURLFORM_FILECONTENT, value7,
1478                   CURLFORM_END))
1479     ++errors;
1480
1481   form=Curl_getFormData(httppost, &size);
1482
1483   Curl_FormInit(&formread, form);
1484
1485   do {
1486     nread = Curl_FormReader(buffer, 1, sizeof(buffer),
1487                             (FILE *)&formread);
1488
1489     if(-1 == nread)
1490       break;
1491     fwrite(buffer, nread, 1, stdout);
1492   } while(1);
1493
1494   fprintf(stdout, "size: %d\n", size);
1495   if (errors)
1496     fprintf(stdout, "\n==> %d Test(s) failed!\n", errors);
1497   else
1498     fprintf(stdout, "\nAll Tests seem to have worked (please check output)\n");
1499
1500   return 0;
1501 }
1502
1503 #endif
1504
1505 #else  /* CURL_DISABLE_HTTP */
1506 CURLFORMcode curl_formadd(struct curl_httppost **httppost,
1507                           struct curl_httppost **last_post,
1508                           ...)
1509 {
1510   (void)httppost;
1511   (void)last_post;
1512   return CURL_FORMADD_DISABLED;
1513 }
1514
1515 void curl_formfree(struct curl_httppost *form)
1516 {
1517   (void)form;
1518   /* does nothing HTTP is disabled */
1519 }
1520
1521 #endif  /* CURL_DISABLE_HTTP */
1522
1523 /*
1524  * Curl_FormBoundary() creates a suitable boundary string and returns an
1525  * allocated one. This is also used by SSL-code so it must be present even
1526  * if HTTP is disabled!
1527  */
1528 char *Curl_FormBoundary(void)
1529 {
1530   char *retstring;
1531   static int randomizer=0; /* this is just so that two boundaries within
1532                               the same form won't be identical */
1533   size_t i;
1534
1535   static char table16[]="abcdef0123456789";
1536
1537   retstring = (char *)malloc(BOUNDARY_LENGTH+1);
1538
1539   if(!retstring)
1540     return NULL; /* failed */
1541
1542   srand(time(NULL)+randomizer++); /* seed */
1543
1544   strcpy(retstring, "----------------------------");
1545
1546   for(i=strlen(retstring); i<BOUNDARY_LENGTH; i++)
1547     retstring[i] = table16[rand()%16];
1548
1549   /* 28 dashes and 12 hexadecimal digits makes 12^16 (184884258895036416)
1550      combinations */
1551   retstring[BOUNDARY_LENGTH]=0; /* zero terminate */
1552
1553   return retstring;
1554 }