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