smtp: use the upload buffer size for scratch buffer malloc
[platform/upstream/curl.git] / lib / formdata.c
index 480de98..f912410 100644 (file)
@@ -5,11 +5,11 @@
  *                            | (__| |_| |  _ <| |___
  *                             \___|\___/|_| \_\_____|
  *
- * Copyright (C) 1998 - 2013, Daniel Stenberg, <daniel@haxx.se>, et al.
+ * Copyright (C) 1998 - 2018, Daniel Stenberg, <daniel@haxx.se>, et al.
  *
  * This software is licensed as described in the file COPYING, which
  * you should have received as part of this distribution. The terms
- * are also available at http://curl.haxx.se/docs/copyright.html.
+ * are also available at https://curl.haxx.se/docs/copyright.html.
  *
  * You may opt to use, copy, modify, merge, publish, distribute and/or sell
  * copies of the Software, and permit persons to whom the Software is
 
 #include <curl/curl.h>
 
-/* Length of the random boundary string. */
-#define BOUNDARY_LENGTH 40
-
-#if !defined(CURL_DISABLE_HTTP) || defined(USE_SSLEAY)
+#ifndef CURL_DISABLE_HTTP
 
 #if defined(HAVE_LIBGEN_H) && defined(HAVE_BASENAME)
 #include <libgen.h>
 #endif
 
-#include "urldata.h" /* for struct SessionHandle */
+#include "urldata.h" /* for struct Curl_easy */
 #include "formdata.h"
-#include "curl_rand.h"
-#include "strequal.h"
-#include "curl_memory.h"
+#include "mime.h"
+#include "non-ascii.h"
+#include "vtls/vtls.h"
+#include "strcase.h"
 #include "sendf.h"
-
-#define _MPRINTF_REPLACE /* use our functions only */
-#include <curl/mprintf.h>
-
-/* The last #include file should be: */
+#include "strdup.h"
+#include "rand.h"
+/* The last 3 #include files should be in this order */
+#include "curl_printf.h"
+#include "curl_memory.h"
 #include "memdebug.h"
 
-#endif  /* !defined(CURL_DISABLE_HTTP) || defined(USE_SSLEAY) */
-
-#ifndef CURL_DISABLE_HTTP
-
-#ifndef HAVE_BASENAME
-static char *Curl_basename(char *path);
-#define basename(x)  Curl_basename((x))
-#endif
-
-static size_t readfromfile(struct Form *form, char *buffer, size_t size);
 
 /* What kind of Content-Type to use on un-specified files with unrecognized
    extensions. */
 #define HTTPPOST_CONTENTTYPE_DEFAULT "application/octet-stream"
 
-#define FORM_FILE_SEPARATOR ','
-#define FORM_TYPE_SEPARATOR ';'
+#define HTTPPOST_PTRNAME CURL_HTTPPOST_PTRNAME
+#define HTTPPOST_FILENAME CURL_HTTPPOST_FILENAME
+#define HTTPPOST_PTRCONTENTS CURL_HTTPPOST_PTRCONTENTS
+#define HTTPPOST_READFILE CURL_HTTPPOST_READFILE
+#define HTTPPOST_PTRBUFFER CURL_HTTPPOST_PTRBUFFER
+#define HTTPPOST_CALLBACK CURL_HTTPPOST_CALLBACK
+#define HTTPPOST_BUFFER CURL_HTTPPOST_BUFFER
 
 /***************************************************************************
  *
@@ -76,11 +69,11 @@ static size_t readfromfile(struct Form *form, char *buffer, size_t size);
  ***************************************************************************/
 static struct curl_httppost *
 AddHttpPost(char *name, size_t namelength,
-            char *value, size_t contentslength,
+            char *value, curl_off_t contentslength,
             char *buffer, size_t bufferlength,
             char *contenttype,
             long flags,
-            struct curl_slistcontentHeader,
+            struct curl_slist *contentHeader,
             char *showfilename, char *userp,
             struct curl_httppost *parent_post,
             struct curl_httppost **httppost,
@@ -92,14 +85,14 @@ AddHttpPost(char *name, size_t namelength,
     post->name = name;
     post->namelength = (long)(name?(namelength?namelength:strlen(name)):0);
     post->contents = value;
-    post->contentslength = (long)contentslength;
+    post->contentlen = contentslength;
     post->buffer = buffer;
     post->bufferlength = (long)bufferlength;
     post->contenttype = contenttype;
     post->contentheader = contentHeader;
     post->showfilename = showfilename;
-    post->userp = userp,
-    post->flags = flags;
+    post->userp = userp;
+    post->flags = flags | CURL_HTTPPOST_LARGE;
   }
   else
     return NULL;
@@ -162,100 +155,6 @@ static FormInfo * AddFormInfo(char *value,
 
 /***************************************************************************
  *
- * ContentTypeForFilename()
- *
- * Provides content type for filename if one of the known types (else
- * (either the prevtype or the default is returned).
- *
- * Returns some valid contenttype for filename.
- *
- ***************************************************************************/
-static const char * ContentTypeForFilename (const char *filename,
-                                            const char *prevtype)
-{
-  const char *contenttype = NULL;
-  unsigned int i;
-  /*
-   * No type was specified, we scan through a few well-known
-   * extensions and pick the first we match!
-   */
-  struct ContentType {
-    char extension[6];
-    const char *type;
-  };
-  static const struct ContentType ctts[]={
-    {".gif",  "image/gif"},
-    {".jpg",  "image/jpeg"},
-    {".jpeg", "image/jpeg"},
-    {".txt",  "text/plain"},
-    {".html", "text/html"},
-    {".xml", "application/xml"}
-  };
-
-  if(prevtype)
-    /* default to the previously set/used! */
-    contenttype = prevtype;
-  else
-    contenttype = HTTPPOST_CONTENTTYPE_DEFAULT;
-
-  if(filename) { /* in case a NULL was passed in */
-    for(i=0; i<sizeof(ctts)/sizeof(ctts[0]); i++) {
-      if(strlen(filename) >= strlen(ctts[i].extension)) {
-        if(strequal(filename +
-                    strlen(filename) - strlen(ctts[i].extension),
-                    ctts[i].extension)) {
-          contenttype = ctts[i].type;
-          break;
-        }
-      }
-    }
-  }
-  /* we have a contenttype by now */
-  return contenttype;
-}
-
-/***************************************************************************
- *
- * memdup()
- *
- * Copies the 'source' data to a newly allocated buffer buffer (that is
- * returned). Uses buffer_length if not null, else uses strlen to determine
- * the length of the buffer to be copied
- *
- * Returns the new pointer or NULL on failure.
- *
- ***************************************************************************/
-static char *memdup(const char *src, size_t buffer_length)
-{
-  size_t length;
-  bool add = FALSE;
-  char *buffer;
-
-  if(buffer_length)
-    length = buffer_length;
-  else if(src) {
-    length = strlen(src);
-    add = TRUE;
-  }
-  else
-    /* no length and a NULL src pointer! */
-    return strdup("");
-
-  buffer = malloc(length+add);
-  if(!buffer)
-    return NULL; /* fail */
-
-  memcpy(buffer, src, length);
-
-  /* if length unknown do null termination */
-  if(add)
-    buffer[length] = '\0';
-
-  return buffer;
-}
-
-/***************************************************************************
- *
  * FormAdd()
  *
  * Stores a formpost parameter and builds the appropriate linked list.
@@ -314,7 +213,7 @@ CURLFORMcode FormAdd(struct curl_httppost **httppost,
   struct curl_httppost *post = NULL;
   CURLformoption option;
   struct curl_forms *forms = NULL;
-  char *array_value=NULL; /* value read from an array */
+  char *array_value = NULL; /* value read from an array */
 
   /* This is a state variable, that if TRUE means that we're parsing an
      array that we got passed to us. If FALSE we're parsing the input
@@ -355,7 +254,7 @@ CURLFORMcode FormAdd(struct curl_httppost **httppost,
         break;
     }
 
-    switch (option) {
+    switch(option) {
     case CURLFORM_ARRAY:
       if(array_state)
         /* we don't support an array from within an array */
@@ -381,6 +280,7 @@ CURLFORMcode FormAdd(struct curl_httppost **httppost,
 #else
       current_form->flags |= HTTPPOST_PTRNAME; /* fall through */
 #endif
+      /* FALLTHROUGH */
     case CURLFORM_COPYNAME:
       if(current_form->name)
         return_value = CURL_FORMADD_OPTION_TWICE;
@@ -419,16 +319,19 @@ CURLFORMcode FormAdd(struct curl_httppost **httppost,
       }
       break;
     case CURLFORM_CONTENTSLENGTH:
-      if(current_form->contentslength)
-        return_value = CURL_FORMADD_OPTION_TWICE;
-      else
-        current_form->contentslength =
-          array_state?(size_t)array_value:(size_t)va_arg(params, long);
+      current_form->contentslength =
+        array_state?(size_t)array_value:(size_t)va_arg(params, long);
+      break;
+
+    case CURLFORM_CONTENTLEN:
+      current_form->flags |= CURL_HTTPPOST_LARGE;
+      current_form->contentslength =
+        array_state?(curl_off_t)(size_t)array_value:va_arg(params, curl_off_t);
       break;
 
       /* Get contents from a given file name */
     case CURLFORM_FILECONTENT:
-      if(current_form->flags != 0)
+      if(current_form->flags & (HTTPPOST_PTRCONTENTS|HTTPPOST_READFILE))
         return_value = CURL_FORMADD_OPTION_TWICE;
       else {
         const char *filename = array_state?
@@ -462,7 +365,7 @@ CURLFORMcode FormAdd(struct curl_httppost **httppost,
               else {
                 form = AddFormInfo(fname, NULL, current_form);
                 if(!form) {
-                  Curl_safefree(fname);
+                  free(fname);
                   return_value = CURL_FORMADD_MEMORY;
                 }
                 else {
@@ -551,7 +454,7 @@ CURLFORMcode FormAdd(struct curl_httppost **httppost,
               else {
                 form = AddFormInfo(NULL, type, current_form);
                 if(!form) {
-                  Curl_safefree(type);
+                  free(type);
                   return_value = CURL_FORMADD_MEMORY;
                 }
                 else {
@@ -584,9 +487,9 @@ CURLFORMcode FormAdd(struct curl_httppost **httppost,
       {
         /* this "cast increases required alignment of target type" but
            we consider it OK anyway */
-        struct curl_slistlist = array_state?
-          (struct curl_slist*)array_value:
-          va_arg(params, struct curl_slist*);
+        struct curl_slist *list = array_state?
+          (struct curl_slist *)(void *)array_value:
+          va_arg(params, struct curl_slist *);
 
         if(current_form->contentheader)
           return_value = CURL_FORMADD_OPTION_TWICE;
@@ -665,60 +568,85 @@ CURLFORMcode FormAdd(struct curl_httppost **httppost,
         return_value = CURL_FORMADD_INCOMPLETE;
         break;
       }
-      else {
-        if(((form->flags & HTTPPOST_FILENAME) ||
-            (form->flags & HTTPPOST_BUFFER)) &&
-           !form->contenttype ) {
-          /* our contenttype is missing */
-          form->contenttype
-            = strdup(ContentTypeForFilename(form->value, prevtype));
-          if(!form->contenttype) {
-            return_value = CURL_FORMADD_MEMORY;
-            break;
-          }
-          form->contenttype_alloc = TRUE;
-        }
-        if(!(form->flags & HTTPPOST_PTRNAME) &&
-           (form == first_form) ) {
-          /* Note that there's small risk that form->name is NULL here if the
-             app passed in a bad combo, so we better check for that first. */
-          if(form->name)
-            /* copy name (without strdup; possibly contains null characters) */
-            form->name = memdup(form->name, form->namelength);
-          if(!form->name) {
-            return_value = CURL_FORMADD_MEMORY;
-            break;
-          }
-          form->name_alloc = TRUE;
+      if(((form->flags & HTTPPOST_FILENAME) ||
+          (form->flags & HTTPPOST_BUFFER)) &&
+         !form->contenttype) {
+        char *f = form->flags & HTTPPOST_BUFFER?
+          form->showfilename : form->value;
+        char const *type;
+        type = Curl_mime_contenttype(f);
+        if(!type)
+          type = prevtype;
+        if(!type)
+          type = FILE_CONTENTTYPE_DEFAULT;
+
+        /* our contenttype is missing */
+        form->contenttype = strdup(type);
+        if(!form->contenttype) {
+          return_value = CURL_FORMADD_MEMORY;
+          break;
         }
-        if(!(form->flags & (HTTPPOST_FILENAME | HTTPPOST_READFILE |
-                            HTTPPOST_PTRCONTENTS | HTTPPOST_PTRBUFFER |
-                            HTTPPOST_CALLBACK)) ) {
-          /* copy value (without strdup; possibly contains null characters) */
-          form->value = memdup(form->value, form->contentslength);
-          if(!form->value) {
-            return_value = CURL_FORMADD_MEMORY;
+        form->contenttype_alloc = TRUE;
+      }
+      if(form->name && form->namelength) {
+        /* Name should not contain nul bytes. */
+        size_t i;
+        for(i = 0; i < form->namelength; i++)
+          if(!form->name[i]) {
+            return_value = CURL_FORMADD_NULL;
             break;
           }
-          form->value_alloc = TRUE;
+        if(return_value != CURL_FORMADD_OK)
+          break;
+      }
+      if(!(form->flags & HTTPPOST_PTRNAME) &&
+         (form == first_form) ) {
+        /* Note that there's small risk that form->name is NULL here if the
+           app passed in a bad combo, so we better check for that first. */
+        if(form->name) {
+          /* copy name (without strdup; possibly not nul-terminated) */
+          form->name = Curl_memdup(form->name, form->namelength?
+                                   form->namelength:
+                                   strlen(form->name) + 1);
         }
-        post = AddHttpPost(form->name, form->namelength,
-                           form->value, form->contentslength,
-                           form->buffer, form->bufferlength,
-                           form->contenttype, form->flags,
-                           form->contentheader, form->showfilename,
-                           form->userp,
-                           post, httppost,
-                           last_post);
-
-        if(!post) {
+        if(!form->name) {
           return_value = CURL_FORMADD_MEMORY;
           break;
         }
+        form->name_alloc = TRUE;
+      }
+      if(!(form->flags & (HTTPPOST_FILENAME | HTTPPOST_READFILE |
+                          HTTPPOST_PTRCONTENTS | HTTPPOST_PTRBUFFER |
+                          HTTPPOST_CALLBACK)) && form->value) {
+        /* copy value (without strdup; possibly contains null characters) */
+        size_t clen  = (size_t) form->contentslength;
+        if(!clen)
+          clen = strlen(form->value) + 1;
+
+        form->value = Curl_memdup(form->value, clen);
 
-        if(form->contenttype)
-          prevtype = form->contenttype;
+        if(!form->value) {
+          return_value = CURL_FORMADD_MEMORY;
+          break;
+        }
+        form->value_alloc = TRUE;
+      }
+      post = AddHttpPost(form->name, form->namelength,
+                         form->value, form->contentslength,
+                         form->buffer, form->bufferlength,
+                         form->contenttype, form->flags,
+                         form->contentheader, form->showfilename,
+                         form->userp,
+                         post, httppost,
+                         last_post);
+
+      if(!post) {
+        return_value = CURL_FORMADD_MEMORY;
+        break;
       }
+
+      if(form->contenttype)
+        prevtype = form->contenttype;
     }
     if(CURL_FORMADD_OK != return_value) {
       /* On error, free allocated fields for nodes of the FormInfo linked
@@ -751,7 +679,7 @@ CURLFORMcode FormAdd(struct curl_httppost **httppost,
      now by the httppost linked list */
   while(first_form) {
     FormInfo *ptr = first_form->more;
-    Curl_safefree(first_form);
+    free(first_form);
     first_form = ptr;
   }
 
@@ -777,110 +705,6 @@ CURLFORMcode curl_formadd(struct curl_httppost **httppost,
 }
 
 /*
- * AddFormData() adds a chunk of data to the FormData linked list.
- *
- * size is incremented by the chunk length, unless it is NULL
- */
-static CURLcode AddFormData(struct FormData **formp,
-                            enum formtype type,
-                            const void *line,
-                            size_t length,
-                            curl_off_t *size)
-{
-  struct FormData *newform = malloc(sizeof(struct FormData));
-  if(!newform)
-    return CURLE_OUT_OF_MEMORY;
-  newform->next = NULL;
-
-  if(type <= FORM_CONTENT) {
-    /* we make it easier for plain strings: */
-    if(!length)
-      length = strlen((char *)line);
-
-    newform->line = malloc(length+1);
-    if(!newform->line) {
-      free(newform);
-      return CURLE_OUT_OF_MEMORY;
-    }
-    memcpy(newform->line, line, length);
-    newform->length = length;
-    newform->line[length]=0; /* zero terminate for easier debugging */
-  }
-  else
-    /* For callbacks and files we don't have any actual data so we just keep a
-       pointer to whatever this points to */
-    newform->line = (char *)line;
-
-  newform->type = type;
-
-  if(*formp) {
-    (*formp)->next = newform;
-    *formp = newform;
-  }
-  else
-    *formp = newform;
-
-  if(size) {
-    if(type != FORM_FILE)
-      /* for static content as well as callback data we add the size given
-         as input argument */
-      *size += length;
-    else {
-      /* Since this is a file to be uploaded here, add the size of the actual
-         file */
-      if(!strequal("-", newform->line)) {
-        struct_stat file;
-        if(!stat(newform->line, &file) && S_ISREG(file.st_mode))
-          *size += file.st_size;
-        else
-          return CURLE_BAD_FUNCTION_ARGUMENT;
-      }
-    }
-  }
-  return CURLE_OK;
-}
-
-/*
- * AddFormDataf() adds printf()-style formatted data to the formdata chain.
- */
-
-static CURLcode AddFormDataf(struct FormData **formp,
-                             curl_off_t *size,
-                             const char *fmt, ...)
-{
-  char s[4096];
-  va_list ap;
-  va_start(ap, fmt);
-  vsnprintf(s, sizeof(s), fmt, ap);
-  va_end(ap);
-
-  return AddFormData(formp, FORM_DATA, s, 0, size);
-}
-
-/*
- * Curl_formclean() is used from http.c, this cleans a built FormData linked
- * list
- */
-void Curl_formclean(struct FormData **form_ptr)
-{
-  struct FormData *next, *form;
-
-  form = *form_ptr;
-  if(!form)
-    return;
-
-  do {
-    next=form->next;  /* the following form line */
-    if(form->type <= FORM_CONTENT)
-      free(form->line); /* free the line */
-    free(form);       /* free the struct */
-
-  } while((form = next) != NULL); /* continue */
-
-  *form_ptr = NULL;
-}
-
-/*
  * curl_formget()
  * Serialize a curl_httppost struct.
  * Returns 0 on success.
@@ -890,43 +714,35 @@ void Curl_formclean(struct FormData **form_ptr)
 int curl_formget(struct curl_httppost *form, void *arg,
                  curl_formget_callback append)
 {
-  CURLcode rc;
-  curl_off_t size;
-  struct FormData *data, *ptr;
-
-  rc = Curl_getformdata(NULL, &data, form, NULL, &size);
-  if(rc != CURLE_OK)
-    return (int)rc;
-
-  for(ptr = data; ptr; ptr = ptr->next) {
-    if((ptr->type == FORM_FILE) || (ptr->type == FORM_CALLBACK)) {
-      char buffer[8192];
-      size_t nread;
-      struct Form temp;
-
-      Curl_FormInit(&temp, ptr);
-
-      do {
-        nread = readfromfile(&temp, buffer, sizeof(buffer));
-        if((nread == (size_t) -1) ||
-           (nread > sizeof(buffer)) ||
-           (nread != append(arg, buffer, nread))) {
-          if(temp.fp)
-            fclose(temp.fp);
-          Curl_formclean(&data);
-          return -1;
-        }
-      } while(nread);
-    }
-    else {
-      if(ptr->length != append(arg, ptr->line, ptr->length)) {
-        Curl_formclean(&data);
-        return -1;
-      }
+  CURLcode result;
+  curl_mimepart toppart;
+
+  Curl_mime_initpart(&toppart, NULL); /* default form is empty */
+  result = Curl_getformdata(NULL, &toppart, form, NULL);
+  if(!result)
+    result = Curl_mime_prepare_headers(&toppart, "multipart/form-data",
+                                       NULL, MIMESTRATEGY_FORM);
+
+  while(!result) {
+    char buffer[8192];
+    size_t nread = Curl_mime_read(buffer, 1, sizeof buffer, &toppart);
+
+    if(!nread)
+      break;
+
+    switch(nread) {
+    default:
+      if(append(arg, buffer, nread) != nread)
+        result = CURLE_READ_ERROR;
+      break;
+    case CURL_READFUNC_ABORT:
+    case CURL_READFUNC_PAUSE:
+      break;
     }
   }
-  Curl_formclean(&data);
-  return 0;
+
+  Curl_mime_cleanpart(&toppart);
+  return (int) result;
 }
 
 /*
@@ -942,498 +758,171 @@ void curl_formfree(struct curl_httppost *form)
     return;
 
   do {
-    next=form->next;  /* the following form line */
+    next = form->next;  /* the following form line */
 
     /* recurse to sub-contents */
-    if(form->more)
-      curl_formfree(form->more);
+    curl_formfree(form->more);
 
-    if(!(form->flags & HTTPPOST_PTRNAME) && form->name)
+    if(!(form->flags & HTTPPOST_PTRNAME))
       free(form->name); /* free the name */
     if(!(form->flags &
-         (HTTPPOST_PTRCONTENTS|HTTPPOST_BUFFER|HTTPPOST_CALLBACK)) &&
-       form->contents)
+         (HTTPPOST_PTRCONTENTS|HTTPPOST_BUFFER|HTTPPOST_CALLBACK))
+      )
       free(form->contents); /* free the contents */
-    if(form->contenttype)
-      free(form->contenttype); /* free the content type */
-    if(form->showfilename)
-      free(form->showfilename); /* free the faked file name */
+    free(form->contenttype); /* free the content type */
+    free(form->showfilename); /* free the faked file name */
     free(form);       /* free the struct */
-
-  } while((form = next) != NULL); /* continue */
+    form = next;
+  } while(form); /* continue */
 }
 
-#ifndef HAVE_BASENAME
-/*
-  (Quote from The Open Group Base Specifications Issue 6 IEEE Std 1003.1, 2004
-  Edition)
-
-  The basename() function shall take the pathname pointed to by path and
-  return a pointer to the final component of the pathname, deleting any
-  trailing '/' characters.
-
-  If the string pointed to by path consists entirely of the '/' character,
-  basename() shall return a pointer to the string "/". If the string pointed
-  to by path is exactly "//", it is implementation-defined whether '/' or "//"
-  is returned.
 
-  If path is a null pointer or points to an empty string, basename() shall
-  return a pointer to the string ".".
-
-  The basename() function may modify the string pointed to by path, and may
-  return a pointer to static storage that may then be overwritten by a
-  subsequent call to basename().
-
-  The basename() function need not be reentrant. A function that is not
-  required to be reentrant is not required to be thread-safe.
-
-*/
-static char *Curl_basename(char *path)
+/* Set mime part name, taking care of non nul-terminated name string. */
+static CURLcode setname(curl_mimepart *part, const char *name, size_t len)
 {
-  /* Ignore all the details above for now and make a quick and simple
-     implementaion here */
-  char *s1;
-  char *s2;
-
-  s1=strrchr(path, '/');
-  s2=strrchr(path, '\\');
+  char *zname;
+  CURLcode res;
 
-  if(s1 && s2) {
-    path = (s1 > s2? s1 : s2)+1;
-  }
-  else if(s1)
-    path = s1 + 1;
-  else if(s2)
-    path = s2 + 1;
-
-  return path;
-}
-#endif
-
-static char *strippath(const char *fullfile)
-{
-  char *filename;
-  char *base;
-  filename = strdup(fullfile); /* duplicate since basename() may ruin the
-                                  buffer it works on */
-  if(!filename)
-    return NULL;
-  base = strdup(basename(filename));
-
-  free(filename); /* free temporary buffer */
-
-  return base; /* returns an allocated string or NULL ! */
+  if(!name || !len)
+    return curl_mime_name(part, name);
+  zname = malloc(len + 1);
+  if(!zname)
+    return CURLE_OUT_OF_MEMORY;
+  memcpy(zname, name, len);
+  zname[len] = '\0';
+  res = curl_mime_name(part, zname);
+  free(zname);
+  return res;
 }
 
 /*
- * Curl_getformdata() converts a linked list of "meta data" into a complete
- * (possibly huge) multipart formdata. The input list is in 'post', while the
- * output resulting linked lists gets stored in '*finalform'. *sizep will get
- * the total size of the whole POST.
- * A multipart/form_data content-type is built, unless a custom content-type
- * is passed in 'custom_content_type'.
+ * Curl_getformdata() converts a linked list of "meta data" into a mime
+ * structure. The input list is in 'post', while the output is stored in
+ * mime part at '*finalform'.
  *
  * This function will not do a failf() for the potential memory failures but
  * should for all other errors it spots. Just note that this function MAY get
  * a NULL pointer in the 'data' argument.
  */
 
-CURLcode Curl_getformdata(struct SessionHandle *data,
-                          struct FormData **finalform,
+CURLcode Curl_getformdata(struct Curl_easy *data,
+                          curl_mimepart *finalform,
                           struct curl_httppost *post,
-                          const char *custom_content_type,
-                          curl_off_t *sizep)
+                          curl_read_callback fread_func)
 {
-  struct FormData *form = NULL;
-  struct FormData *firstform;
-  struct curl_httppost *file;
   CURLcode result = CURLE_OK;
+  curl_mime *form = NULL;
+  curl_mime *multipart;
+  curl_mimepart *part;
+  struct curl_httppost *file;
 
-  curl_off_t size = 0; /* support potentially ENORMOUS formposts */
-  char *boundary;
-  char *fileboundary = NULL;
-  struct curl_slist* curList;
-
-  *finalform = NULL; /* default form is empty */
+  Curl_mime_cleanpart(finalform); /* default form is empty */
 
   if(!post)
     return result; /* no input => no output! */
 
-  boundary = Curl_FormBoundary();
-  if(!boundary)
-    return CURLE_OUT_OF_MEMORY;
-
-  /* Make the first line of the output */
-  result = AddFormDataf(&form, NULL,
-                        "%s; boundary=%s\r\n",
-                        custom_content_type?custom_content_type:
-                        "Content-Type: multipart/form-data",
-                        boundary);
-
-  if(result) {
-    Curl_safefree(boundary);
-    return result;
-  }
-  /* we DO NOT include that line in the total size of the POST, since it'll be
-     part of the header! */
-
-  firstform = form;
-
-  do {
-
-    if(size) {
-      result = AddFormDataf(&form, &size, "\r\n");
-      if(result)
-        break;
-    }
-
-    /* boundary */
-    result = AddFormDataf(&form, &size, "--%s\r\n", boundary);
-    if(result)
-      break;
-
-    /* Maybe later this should be disabled when a custom_content_type is
-       passed, since Content-Disposition is not meaningful for all multipart
-       types.
-    */
-    result = AddFormDataf(&form, &size,
-                          "Content-Disposition: form-data; name=\"");
-    if(result)
-      break;
-
-    result = AddFormData(&form, FORM_DATA, post->name, post->namelength,
-                         &size);
-    if(result)
-      break;
+  form = curl_mime_init(data);
+  if(!form)
+    result = CURLE_OUT_OF_MEMORY;
 
-    result = AddFormDataf(&form, &size, "\"");
-    if(result)
-      break;
+  if(!result)
+    result = curl_mime_subparts(finalform, form);
 
+  /* Process each top part. */
+  for(; !result && post; post = post->next) {
+    /* If we have more than a file here, create a mime subpart and fill it. */
+    multipart = form;
     if(post->more) {
-      /* If used, this is a link to more file names, we must then do
-         the magic to include several files with the same field name */
-
-      Curl_safefree(fileboundary);
-      fileboundary = Curl_FormBoundary();
-      if(!fileboundary) {
+      part = curl_mime_addpart(form);
+      if(!part)
         result = CURLE_OUT_OF_MEMORY;
-        break;
+      if(!result)
+        result = setname(part, post->name, post->namelength);
+      if(!result) {
+        multipart = curl_mime_init(data);
+        if(!multipart)
+          result = CURLE_OUT_OF_MEMORY;
       }
-
-      result = AddFormDataf(&form, &size,
-                            "\r\nContent-Type: multipart/mixed,"
-                            " boundary=%s\r\n",
-                            fileboundary);
-      if(result)
-        break;
+      if(!result)
+        result = curl_mime_subparts(part, multipart);
     }
 
-    file = post;
-
-    do {
-
-      /* If 'showfilename' is set, that is a faked name passed on to us
-         to use to in the formpost. If that is not set, the actually used
-         local file name should be added. */
-
-      if(post->more) {
-        /* if multiple-file */
-        char *filebasename = NULL;
-        if(!file->showfilename) {
-          filebasename = strippath(file->contents);
-          if(!filebasename) {
-            result = CURLE_OUT_OF_MEMORY;
-            break;
-          }
-        }
-
-        result = AddFormDataf(&form, &size,
-                              "\r\n--%s\r\nContent-Disposition: "
-                              "attachment; filename=\"%s\"",
-                              fileboundary,
-                              (file->showfilename?file->showfilename:
-                               filebasename));
-        Curl_safefree(filebasename);
-        if(result)
-          break;
-      }
-      else if(post->flags & (HTTPPOST_FILENAME|HTTPPOST_BUFFER|
-                             HTTPPOST_CALLBACK)) {
-        /* it should be noted that for the HTTPPOST_FILENAME and
-           HTTPPOST_CALLBACK cases the ->showfilename struct member is always
-           assigned at this point */
-        if(post->showfilename || (post->flags & HTTPPOST_FILENAME)) {
-          char *filebasename=
-            (!post->showfilename)?strippath(post->contents):NULL;
-
-          result = AddFormDataf(&form, &size,
-                                "; filename=\"%s\"",
-                                (post->showfilename?post->showfilename:
-                                 filebasename));
-          Curl_safefree(filebasename);
-        }
-
-        if(result)
-          break;
-      }
-
-      if(file->contenttype) {
-        /* we have a specified type */
-        result = AddFormDataf(&form, &size,
-                              "\r\nContent-Type: %s",
-                              file->contenttype);
-        if(result)
-          break;
-      }
-
-      curList = file->contentheader;
-      while(curList) {
-        /* Process the additional headers specified for this form */
-        result = AddFormDataf( &form, &size, "\r\n%s", curList->data );
-        if(result)
-          break;
-        curList = curList->next;
-      }
-      if(result)
-        break;
-
-      result = AddFormDataf(&form, &size, "\r\n\r\n");
-      if(result)
-        break;
+    /* Generate all the part contents. */
+    for(file = post; !result && file; file = file->more) {
+      /* Create the part. */
+      part = curl_mime_addpart(multipart);
+      if(!part)
+        result = CURLE_OUT_OF_MEMORY;
 
-      if((post->flags & HTTPPOST_FILENAME) ||
-         (post->flags & HTTPPOST_READFILE)) {
-        /* we should include the contents from the specified file */
-        FILE *fileread;
-
-        fileread = strequal("-", file->contents)?
-          stdin:fopen(file->contents, "rb"); /* binary read for win32  */
-
-        /*
-         * VMS: This only allows for stream files on VMS.  Stream files are
-         * OK, as are FIXED & VAR files WITHOUT implied CC For implied CC,
-         * every record needs to have a \n appended & 1 added to SIZE
-         */
-
-        if(fileread) {
-          if(fileread != stdin) {
-            /* close the file */
-            fclose(fileread);
-            /* add the file name only - for later reading from this */
-            result = AddFormData(&form, FORM_FILE, file->contents, 0, &size);
-          }
-          else {
-            /* When uploading from stdin, we can't know the size of the file,
-             * thus must read the full file as before. We *could* use chunked
-             * transfer-encoding, but that only works for HTTP 1.1 and we
-             * can't be sure we work with such a server.
-             */
-            size_t nread;
-            char buffer[512];
-            while((nread = fread(buffer, 1, sizeof(buffer), fileread)) != 0) {
-              result = AddFormData(&form, FORM_CONTENT, buffer, nread, &size);
-              if(result)
-                break;
-            }
+      /* Set the headers. */
+      if(!result)
+        result = curl_mime_headers(part, file->contentheader, 0);
+
+      /* Set the content type. */
+      if(!result && file->contenttype)
+        result = curl_mime_type(part, file->contenttype);
+
+      /* Set field name. */
+      if(!result && !post->more)
+        result = setname(part, post->name, post->namelength);
+
+      /* Process contents. */
+      if(!result) {
+        curl_off_t clen = post->contentslength;
+
+        if(post->flags & CURL_HTTPPOST_LARGE)
+          clen = post->contentlen;
+        if(!clen)
+          clen = -1;
+
+        if(post->flags & (HTTPPOST_FILENAME | HTTPPOST_READFILE)) {
+          if(!strcmp(file->contents, "-")) {
+            /* There are a few cases where the code below won't work; in
+               particular, freopen(stdin) by the caller is not guaranteed
+               to result as expected. This feature has been kept for backward
+               compatibility: use of "-" pseudo file name should be avoided. */
+            result = curl_mime_data_cb(part, (curl_off_t) -1,
+                                       (curl_read_callback) fread,
+                                       (curl_seek_callback) fseek,
+                                       NULL, (void *) stdin);
           }
+          else
+            result = curl_mime_filedata(part, file->contents);
+          if(!result && (post->flags & HTTPPOST_READFILE))
+            result = curl_mime_filename(part, NULL);
         }
+        else if(post->flags & HTTPPOST_BUFFER)
+          result = curl_mime_data(part, post->buffer,
+                                  post->bufferlength? post->bufferlength: -1);
+        else if(post->flags & HTTPPOST_CALLBACK)
+          /* the contents should be read with the callback and the size is set
+             with the contentslength */
+          result = curl_mime_data_cb(part, clen,
+                                     fread_func, NULL, NULL, post->userp);
         else {
-          if(data)
-            failf(data, "couldn't open file \"%s\"", file->contents);
-          *finalform = NULL;
-          result = CURLE_READ_ERROR;
+          result = curl_mime_data(part, post->contents, (ssize_t) clen);
+#ifdef CURL_DOES_CONVERSIONS
+          /* Convert textual contents now. */
+          if(!result && data && part->datasize)
+            result = Curl_convert_to_network(data, part->data, part->datasize);
+#endif
         }
       }
-      else if(post->flags & HTTPPOST_BUFFER)
-        /* include contents of buffer */
-        result = AddFormData(&form, FORM_CONTENT, post->buffer,
-                             post->bufferlength, &size);
-      else if(post->flags & HTTPPOST_CALLBACK)
-        /* the contents should be read with the callback and the size
-           is set with the contentslength */
-        result = AddFormData(&form, FORM_CALLBACK, post->userp,
-                             post->contentslength, &size);
-      else
-        /* include the contents we got */
-        result = AddFormData(&form, FORM_CONTENT, post->contents,
-                             post->contentslength, &size);
 
-      file = file->more;
-    } while(file && !result); /* for each specified file for this field */
-
-    if(result)
-      break;
-
-    if(post->more) {
-      /* this was a multiple-file inclusion, make a termination file
-         boundary: */
-      result = AddFormDataf(&form, &size,
-                           "\r\n--%s--",
-                           fileboundary);
-      if(result)
-        break;
+      /* Set fake file name. */
+      if(!result && post->showfilename)
+        if(post->more || (post->flags & (HTTPPOST_FILENAME | HTTPPOST_BUFFER |
+                                        HTTPPOST_CALLBACK)))
+          result = curl_mime_filename(part, post->showfilename);
     }
-
-  } while((post = post->next) != NULL); /* for each field */
-
-  /* end-boundary for everything */
-  if(CURLE_OK == result)
-    result = AddFormDataf(&form, &size,
-                          "\r\n--%s--\r\n",
-                          boundary);
-
-  if(result) {
-    Curl_formclean(&firstform);
-    Curl_safefree(fileboundary);
-    Curl_safefree(boundary);
-    return result;
   }
 
-  *sizep = size;
-
-  Curl_safefree(fileboundary);
-  Curl_safefree(boundary);
-
-  *finalform = firstform;
+  if(result)
+    Curl_mime_cleanpart(finalform);
 
   return result;
 }
 
-/*
- * Curl_FormInit() inits the struct 'form' points to with the 'formdata'
- * and resets the 'sent' counter.
- */
-int Curl_FormInit(struct Form *form, struct FormData *formdata )
-{
-  if(!formdata)
-    return 1; /* error */
-
-  form->data = formdata;
-  form->sent = 0;
-  form->fp = NULL;
-  form->fread_func = ZERO_NULL;
-
-  return 0;
-}
-
-/*
- * readfromfile()
- *
- * The read callback that this function may use can return a value larger than
- * 'size' (which then this function returns) that indicates a problem and it
- * must be properly dealt with
- */
-static size_t readfromfile(struct Form *form, char *buffer,
-                           size_t size)
-{
-  size_t nread;
-  bool callback = (form->data->type == FORM_CALLBACK)?TRUE:FALSE;
-
-  if(callback) {
-    if(form->fread_func == ZERO_NULL)
-      return 0;
-    else
-      nread = form->fread_func(buffer, 1, size, form->data->line);
-  }
-  else {
-    if(!form->fp) {
-      /* this file hasn't yet been opened */
-      form->fp = fopen(form->data->line, "rb"); /* b is for binary */
-      if(!form->fp)
-        return (size_t)-1; /* failure */
-    }
-    nread = fread(buffer, 1, size, form->fp);
-  }
-  if(!nread) {
-    /* this is the last chunk from the file, move on */
-    if(form->fp) {
-      fclose(form->fp);
-      form->fp = NULL;
-    }
-    form->data = form->data->next;
-  }
-
-  return nread;
-}
-
-/*
- * Curl_FormReader() is the fread() emulation function that will be used to
- * deliver the formdata to the transfer loop and then sent away to the peer.
- */
-size_t Curl_FormReader(char *buffer,
-                       size_t size,
-                       size_t nitems,
-                       FILE *mydata)
-{
-  struct Form *form;
-  size_t wantedsize;
-  size_t gotsize = 0;
-
-  form=(struct Form *)mydata;
-
-  wantedsize = size * nitems;
-
-  if(!form->data)
-    return 0; /* nothing, error, empty */
-
-  if((form->data->type == FORM_FILE) ||
-     (form->data->type == FORM_CALLBACK)) {
-    gotsize = readfromfile(form, buffer, wantedsize);
-
-    if(gotsize)
-      /* If positive or -1, return. If zero, continue! */
-      return gotsize;
-  }
-  do {
-
-    if((form->data->length - form->sent ) > wantedsize - gotsize) {
-
-      memcpy(buffer + gotsize , form->data->line + form->sent,
-             wantedsize - gotsize);
-
-      form->sent += wantedsize-gotsize;
-
-      return wantedsize;
-    }
-
-    memcpy(buffer+gotsize,
-           form->data->line + form->sent,
-           (form->data->length - form->sent) );
-    gotsize += form->data->length - form->sent;
-
-    form->sent = 0;
-
-    form->data = form->data->next; /* advance */
-
-  } while(form->data && (form->data->type < FORM_CALLBACK));
-  /* If we got an empty line and we have more data, we proceed to the next
-     line immediately to avoid returning zero before we've reached the end. */
-
-  return gotsize;
-}
-
-/*
- * Curl_formpostheader() returns the first line of the formpost, the
- * request-header part (which is not part of the request-body like the rest of
- * the post).
- */
-char *Curl_formpostheader(void *formp, size_t *len)
-{
-  char *header;
-  struct Form *form=(struct Form *)formp;
-
-  if(!form->data)
-    return 0; /* nothing, ERROR! */
-
-  header = form->data->line;
-  *len = form->data->length;
-
-  form->data = form->data->next; /* advance */
-
-  return header;
-}
-
 #else  /* CURL_DISABLE_HTTP */
 CURLFORMcode curl_formadd(struct curl_httppost **httppost,
                           struct curl_httppost **last_post,
@@ -1459,37 +948,5 @@ void curl_formfree(struct curl_httppost *form)
   /* does nothing HTTP is disabled */
 }
 
-#endif  /* CURL_DISABLE_HTTP */
-
-#if !defined(CURL_DISABLE_HTTP) || defined(USE_SSLEAY)
-
-/*
- * Curl_FormBoundary() creates a suitable boundary string and returns an
- * allocated one. This is also used by SSL-code so it must be present even
- * if HTTP is disabled!
- */
-char *Curl_FormBoundary(void)
-{
-  char *retstring;
-  size_t i;
-
-  static const char table16[]="0123456789abcdef";
-
-  retstring = malloc(BOUNDARY_LENGTH+1);
-
-  if(!retstring)
-    return NULL; /* failed */
-
-  strcpy(retstring, "----------------------------");
-
-  for(i=strlen(retstring); i<BOUNDARY_LENGTH; i++)
-    retstring[i] = table16[Curl_rand()%16];
-
-  /* 28 dashes and 12 hexadecimal digits makes 12^16 (184884258895036416)
-     combinations */
-  retstring[BOUNDARY_LENGTH]=0; /* zero terminate */
-
-  return retstring;
-}
 
-#endif  /* !defined(CURL_DISABLE_HTTP) || defined(USE_SSLEAY) */
+#endif  /* !defined(CURL_DISABLE_HTTP) */