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