tizen 2.3.1 release
[external/curl.git] / lib / formdata.c
index 5ec3e38..73d3b6d 100644 (file)
@@ -5,7 +5,7 @@
  *                            | (__| |_| |  _ <| |___
  *                             \___|\___/|_| \_\_____|
  *
- * Copyright (C) 1998 - 2010, Daniel Stenberg, <daniel@haxx.se>, et al.
+ * Copyright (C) 1998 - 2014, 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
  *
  ***************************************************************************/
 
-/*
-  Debug the form generator stand-alone by compiling this source file with:
-
-  gcc -DHAVE_CONFIG_H -I../ -g -D_FORM_DEBUG -DCURLDEBUG -o formdata \
-    -I../include formdata.c strequal.c memdebug.c mprintf.c strerror.c
-
-  (depending on circumstances you may need further externals added)
-
-  run the 'formdata' executable the output should end with:
-  All Tests seem to have worked ...
-  and the following parts should be there:
-
-Content-Disposition: form-data; name="simple_COPYCONTENTS"
-value for simple COPYCONTENTS
-
-Content-Disposition: form-data; name="COPYCONTENTS_+_CONTENTTYPE"
-Content-Type: image/gif
-value for COPYCONTENTS + CONTENTTYPE
-
-Content-Disposition: form-data; name="PRNAME_+_NAMELENGTH_+_COPYNAME_+_CONTENTSLENGTH"
-vlue for PTRNAME + NAMELENGTH + COPYNAME + CONTENTSLENGTH
-(or you might see P^@RNAME and v^@lue at the start)
-
-Content-Disposition: form-data; name="simple_PTRCONTENTS"
-value for simple PTRCONTENTS
-
-Content-Disposition: form-data; name="PTRCONTENTS_+_CONTENTSLENGTH"
-vlue for PTRCONTENTS + CONTENTSLENGTH
-(or you might see v^@lue at the start)
-
-Content-Disposition: form-data; name="PTRCONTENTS_+_CONTENTSLENGTH_+_CONTENTTYPE"
-Content-Type: application/octet-stream
-vlue for PTRCONTENTS + CONTENTSLENGTH + CONTENTTYPE
-(or you might see v^@lue at the start)
-
-Content-Disposition: form-data; name="FILE1_+_CONTENTTYPE"; filename="formdata.h"
-Content-Type: text/html
-...
-
-Content-Disposition: form-data; name="FILE1_+_FILE2"
-Content-Type: multipart/mixed, boundary=curlz1s0dkticx49MV1KGcYP5cvfSsz
-...
-Content-Disposition: attachment; filename="formdata.h"
-Content-Type: application/octet-stream
-...
-Content-Disposition: attachment; filename="Makefile.b32"
-Content-Type: application/octet-stream
-...
-
-Content-Disposition: form-data; name="FILE1_+_FILE2_+_FILE3"
-Content-Type: multipart/mixed, boundary=curlirkYPmPwu6FrJ1vJ1u1BmtIufh1
-...
-Content-Disposition: attachment; filename="formdata.h"
-Content-Type: application/octet-stream
-...
-Content-Disposition: attachment; filename="Makefile.b32"
-Content-Type: application/octet-stream
-...
-Content-Disposition: attachment; filename="formdata.h"
-Content-Type: application/octet-stream
-...
-
-
-Content-Disposition: form-data; name="ARRAY: FILE1_+_FILE2_+_FILE3"
-Content-Type: multipart/mixed, boundary=curlirkYPmPwu6FrJ1vJ1u1BmtIufh1
-...
-Content-Disposition: attachment; filename="formdata.h"
-Content-Type: application/octet-stream
-...
-Content-Disposition: attachment; filename="Makefile.b32"
-Content-Type: application/octet-stream
-...
-Content-Disposition: attachment; filename="formdata.h"
-Content-Type: application/octet-stream
-...
-
-Content-Disposition: form-data; name="FILECONTENT"
-...
-
- */
+#include "curl_setup.h"
 
-#include "setup.h"
 #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
 
-#include <stdio.h>
-#include <stdlib.h>
-#include <string.h>
-#include <stdarg.h>
-#include <time.h>
 #if defined(HAVE_LIBGEN_H) && defined(HAVE_BASENAME)
 #include <libgen.h>
 #endif
+
 #include "urldata.h" /* for struct SessionHandle */
-#include "easyif.h" /* for Curl_convert_... prototypes */
 #include "formdata.h"
-#include "curl_rand.h"
+#include "vtls/vtls.h"
 #include "strequal.h"
 #include "curl_memory.h"
 #include "sendf.h"
+#include "strdup.h"
 
 #define _MPRINTF_REPLACE /* use our functions only */
 #include <curl/mprintf.h>
@@ -131,16 +44,13 @@ Content-Disposition: form-data; name="FILECONTENT"
 /* The last #include file should be: */
 #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);
+static char *formboundary(struct SessionHandle *data);
 
 /* What kind of Content-Type to use on un-specified files with unrecognized
    extensions. */
@@ -241,8 +151,6 @@ static FormInfo * AddFormInfo(char *value,
     /* then move the original 'more' to point to ourselves */
     parent_form_info->more = form_info;
   }
-  else
-    return NULL;
 
   return form_info;
 }
@@ -257,8 +165,8 @@ static FormInfo * AddFormInfo(char *value,
  * Returns some valid contenttype for filename.
  *
  ***************************************************************************/
-static const char * ContentTypeForFilename (const char *filename,
-                                            const char *prevtype)
+static const char *ContentTypeForFilename(const char *filename,
+                                          const char *prevtype)
 {
   const char *contenttype = NULL;
   unsigned int i;
@@ -267,7 +175,7 @@ static const char * ContentTypeForFilename (const char *filename,
    * extensions and pick the first we match!
    */
   struct ContentType {
-    char extension[6];
+    const char *extension;
     const char *type;
   };
   static const struct ContentType ctts[]={
@@ -303,46 +211,6 @@ static const char * ContentTypeForFilename (const char *filename,
 
 /***************************************************************************
  *
- * 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.
@@ -383,7 +251,7 @@ static char *memdup(const char *src, size_t buffer_length)
  * CURL_FORMADD_NULL           if a null pointer was given for a char
  * CURL_FORMADD_MEMORY         if the allocation of a FormInfo struct failed
  * CURL_FORMADD_UNKNOWN_OPTION if an unknown option was used
- * CURL_FORMADD_INCOMPLETE     if the some FormInfo is not complete (or an error)
+ * CURL_FORMADD_INCOMPLETE     if the some FormInfo is not complete (or error)
  * CURL_FORMADD_MEMORY         if a HttpPost struct cannot be allocated
  * CURL_FORMADD_MEMORY         if some allocation for string copying failed.
  * CURL_FORMADD_ILLEGAL_ARRAY  if an illegal option is used in an array
@@ -423,7 +291,7 @@ CURLFORMcode FormAdd(struct curl_httppost **httppost,
   while(return_value == CURL_FORMADD_OK) {
 
     /* first see if we have more parts of the array param */
-    if( array_state && forms ) {
+    if(array_state && forms) {
       /* get the upcoming option from the given array */
       option = forms->option;
       array_value = (char *)forms->value;
@@ -461,8 +329,10 @@ CURLFORMcode FormAdd(struct curl_httppost **httppost,
        */
     case CURLFORM_PTRNAME:
 #ifdef CURL_DOES_CONVERSIONS
-      /* treat CURLFORM_PTR like CURLFORM_COPYNAME so we'll
-         have safe memory for the eventual conversion */
+      /* Treat CURLFORM_PTR like CURLFORM_COPYNAME so that libcurl will copy
+       * the data in all cases so that we'll have safe memory for the eventual
+       * conversion.
+       */
 #else
       current_form->flags |= HTTPPOST_PTRNAME; /* fall through */
 #endif
@@ -513,7 +383,7 @@ CURLFORMcode FormAdd(struct curl_httppost **httppost,
 
       /* 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?
@@ -541,9 +411,21 @@ CURLFORMcode FormAdd(struct curl_httppost **httppost,
         if(current_form->value) {
           if(current_form->flags & HTTPPOST_FILENAME) {
             if(filename) {
-              if((current_form = AddFormInfo(strdup(filename),
-                                              NULL, current_form)) == NULL)
+              char *fname = strdup(filename);
+              if(!fname)
                 return_value = CURL_FORMADD_MEMORY;
+              else {
+                form = AddFormInfo(fname, NULL, current_form);
+                if(!form) {
+                  Curl_safefree(fname);
+                  return_value = CURL_FORMADD_MEMORY;
+                }
+                else {
+                  form->value_alloc = TRUE;
+                  current_form = form;
+                  form = NULL;
+                }
+              }
             }
             else
               return_value = CURL_FORMADD_NULL;
@@ -567,46 +449,18 @@ CURLFORMcode FormAdd(struct curl_httppost **httppost,
         break;
       }
 
-    case CURLFORM_BUFFER:
-      {
-        const char *filename = array_state?array_value:
-          va_arg(params, char *);
-
-        if(current_form->value) {
-          if(current_form->flags & HTTPPOST_BUFFER) {
-            if(filename) {
-              if((current_form = AddFormInfo(strdup(filename),
-                                              NULL, current_form)) == NULL)
-                return_value = CURL_FORMADD_MEMORY;
-            }
-            else
-              return_value = CURL_FORMADD_NULL;
-          }
-          else
-            return_value = CURL_FORMADD_OPTION_TWICE;
-        }
-        else {
-          if(filename) {
-            current_form->value = strdup(filename);
-            if(!current_form->value)
-              return_value = CURL_FORMADD_MEMORY;
-          }
-          else
-            return_value = CURL_FORMADD_NULL;
-          current_form->flags |= HTTPPOST_BUFFER;
-        }
-        break;
-      }
-
     case CURLFORM_BUFFERPTR:
-      current_form->flags |= HTTPPOST_PTRBUFFER;
+      current_form->flags |= HTTPPOST_PTRBUFFER|HTTPPOST_BUFFER;
       if(current_form->buffer)
         return_value = CURL_FORMADD_OPTION_TWICE;
       else {
         char *buffer =
           array_state?array_value:va_arg(params, char *);
-        if(buffer)
+        if(buffer) {
           current_form->buffer = buffer; /* store for the moment */
+          current_form->value = buffer; /* make it non-NULL to be accepted
+                                           as fine */
+        }
         else
           return_value = CURL_FORMADD_NULL;
       }
@@ -646,10 +500,21 @@ CURLFORMcode FormAdd(struct curl_httppost **httppost,
         if(current_form->contenttype) {
           if(current_form->flags & HTTPPOST_FILENAME) {
             if(contenttype) {
-              if((current_form = AddFormInfo(NULL,
-                                              strdup(contenttype),
-                                              current_form)) == NULL)
+              char *type = strdup(contenttype);
+              if(!type)
                 return_value = CURL_FORMADD_MEMORY;
+              else {
+                form = AddFormInfo(NULL, type, current_form);
+                if(!form) {
+                  Curl_safefree(type);
+                  return_value = CURL_FORMADD_MEMORY;
+                }
+                else {
+                  form->contenttype_alloc = TRUE;
+                  current_form = form;
+                  form = NULL;
+                }
+              }
             }
             else
               return_value = CURL_FORMADD_NULL;
@@ -678,7 +543,7 @@ CURLFORMcode FormAdd(struct curl_httppost **httppost,
           (struct curl_slist*)array_value:
           va_arg(params, struct curl_slist*);
 
-        if( current_form->contentheader )
+        if(current_form->contentheader)
           return_value = CURL_FORMADD_OPTION_TWICE;
         else
           current_form->contentheader = list;
@@ -686,10 +551,11 @@ CURLFORMcode FormAdd(struct curl_httppost **httppost,
         break;
       }
     case CURLFORM_FILENAME:
+    case CURLFORM_BUFFER:
       {
         const char *filename = array_state?array_value:
           va_arg(params, char *);
-        if( current_form->showfilename )
+        if(current_form->showfilename)
           return_value = CURL_FORMADD_OPTION_TWICE;
         else {
           current_form->showfilename = strdup(filename);
@@ -702,6 +568,31 @@ CURLFORMcode FormAdd(struct curl_httppost **httppost,
       }
     default:
       return_value = CURL_FORMADD_UNKNOWN_OPTION;
+      break;
+    }
+  }
+
+  if(CURL_FORMADD_OK != return_value) {
+    /* On error, free allocated fields for all nodes of the FormInfo linked
+       list without deallocating nodes. List nodes are deallocated later on */
+    FormInfo *ptr;
+    for(ptr = first_form; ptr != NULL; ptr = ptr->more) {
+      if(ptr->name_alloc) {
+        Curl_safefree(ptr->name);
+        ptr->name_alloc = FALSE;
+      }
+      if(ptr->value_alloc) {
+        Curl_safefree(ptr->value);
+        ptr->value_alloc = FALSE;
+      }
+      if(ptr->contenttype_alloc) {
+        Curl_safefree(ptr->contenttype);
+        ptr->contenttype_alloc = FALSE;
+      }
+      if(ptr->showfilename_alloc) {
+        Curl_safefree(ptr->showfilename);
+        ptr->showfilename_alloc = FALSE;
+      }
     }
   }
 
@@ -713,53 +604,60 @@ CURLFORMcode FormAdd(struct curl_httppost **httppost,
     for(form = first_form;
         form != NULL;
         form = form->more) {
-      if( ((!form->name || !form->value) && !post) ||
-          ( (form->contentslength) &&
-            (form->flags & HTTPPOST_FILENAME) ) ||
-          ( (form->flags & HTTPPOST_FILENAME) &&
-            (form->flags & HTTPPOST_PTRCONTENTS) ) ||
-
-          ( (!form->buffer) &&
-            (form->flags & HTTPPOST_BUFFER) &&
-            (form->flags & HTTPPOST_PTRBUFFER) ) ||
-
-          ( (form->flags & HTTPPOST_READFILE) &&
-            (form->flags & HTTPPOST_PTRCONTENTS) )
+      if(((!form->name || !form->value) && !post) ||
+         ( (form->contentslength) &&
+           (form->flags & HTTPPOST_FILENAME) ) ||
+         ( (form->flags & HTTPPOST_FILENAME) &&
+           (form->flags & HTTPPOST_PTRCONTENTS) ) ||
+
+         ( (!form->buffer) &&
+           (form->flags & HTTPPOST_BUFFER) &&
+           (form->flags & HTTPPOST_PTRBUFFER) ) ||
+
+         ( (form->flags & HTTPPOST_READFILE) &&
+           (form->flags & HTTPPOST_PTRCONTENTS) )
         ) {
         return_value = CURL_FORMADD_INCOMPLETE;
         break;
       }
       else {
-        if( ((form->flags & HTTPPOST_FILENAME) ||
-              (form->flags & HTTPPOST_BUFFER)) &&
-             !form->contenttype ) {
+        if(((form->flags & HTTPPOST_FILENAME) ||
+            (form->flags & HTTPPOST_BUFFER)) &&
+           !form->contenttype ) {
+          char *f = form->flags & HTTPPOST_BUFFER?
+            form->showfilename : form->value;
+
           /* our contenttype is missing */
-          form->contenttype
-            = strdup(ContentTypeForFilename(form->value, prevtype));
+          form->contenttype = strdup(ContentTypeForFilename(f, prevtype));
           if(!form->contenttype) {
             return_value = CURL_FORMADD_MEMORY;
             break;
           }
           form->contenttype_alloc = TRUE;
         }
-        if( !(form->flags & HTTPPOST_PTRNAME) &&
-             (form == first_form) ) {
+        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)
+          if(form->name) {
             /* copy name (without strdup; possibly contains null characters) */
-            form->name = memdup(form->name, form->namelength);
+            form->name = Curl_memdup(form->name, form->namelength?
+                                     form->namelength:
+                                     strlen(form->name)+1);
+          }
           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)) ) {
+        if(!(form->flags & (HTTPPOST_FILENAME | HTTPPOST_READFILE |
+                            HTTPPOST_PTRCONTENTS | HTTPPOST_PTRBUFFER |
+                            HTTPPOST_CALLBACK)) && form->value) {
           /* copy value (without strdup; possibly contains null characters) */
-          form->value = memdup(form->value, form->contentslength);
+          form->value = Curl_memdup(form->value, form->contentslength?
+                                    form->contentslength:
+                                    strlen(form->value)+1);
           if(!form->value) {
             return_value = CURL_FORMADD_MEMORY;
             break;
@@ -784,32 +682,39 @@ CURLFORMcode FormAdd(struct curl_httppost **httppost,
           prevtype = form->contenttype;
       }
     }
-  }
-
-  if(return_value) {
-    /* we return on error, free possibly allocated fields */
-    if(!form)
-      form = current_form;
-    if(form) {
-      if(form->name_alloc)
-        free(form->name);
-      if(form->value_alloc)
-        free(form->value);
-      if(form->contenttype_alloc)
-        free(form->contenttype);
-      if(form->showfilename_alloc)
-        free(form->showfilename);
+    if(CURL_FORMADD_OK != return_value) {
+      /* On error, free allocated fields for nodes of the FormInfo linked
+         list which are not already owned by the httppost linked list
+         without deallocating nodes. List nodes are deallocated later on */
+      FormInfo *ptr;
+      for(ptr = form; ptr != NULL; ptr = ptr->more) {
+        if(ptr->name_alloc) {
+          Curl_safefree(ptr->name);
+          ptr->name_alloc = FALSE;
+        }
+        if(ptr->value_alloc) {
+          Curl_safefree(ptr->value);
+          ptr->value_alloc = FALSE;
+        }
+        if(ptr->contenttype_alloc) {
+          Curl_safefree(ptr->contenttype);
+          ptr->contenttype_alloc = FALSE;
+        }
+        if(ptr->showfilename_alloc) {
+          Curl_safefree(ptr->showfilename);
+          ptr->showfilename_alloc = FALSE;
+        }
+      }
     }
   }
 
-  /* always delete the allocated memory before returning */
-  form = first_form;
-  while(form != NULL) {
-    FormInfo *delete_form;
-
-    delete_form = form;
-    form = form->more;
-    free (delete_form);
+  /* Always deallocate FormInfo linked list nodes without touching node
+     fields given that these have either been deallocated or are owned
+     now by the httppost linked list */
+  while(first_form) {
+    FormInfo *ptr = first_form->more;
+    Curl_safefree(first_form);
+    first_form = ptr;
   }
 
   return return_value;
@@ -817,6 +722,8 @@ CURLFORMcode FormAdd(struct curl_httppost **httppost,
 
 /*
  * curl_formadd() is a public API to add a section to the multipart formpost.
+ *
+ * @unittest: 1308
  */
 
 CURLFORMcode curl_formadd(struct curl_httppost **httppost,
@@ -831,6 +738,70 @@ CURLFORMcode curl_formadd(struct curl_httppost **httppost,
   return result;
 }
 
+#ifdef __VMS
+#include <fabdef.h>
+/*
+ * get_vms_file_size does what it takes to get the real size of the file
+ *
+ * For fixed files, find out the size of the EOF block and adjust.
+ *
+ * For all others, have to read the entire file in, discarding the contents.
+ * Most posted text files will be small, and binary files like zlib archives
+ * and CD/DVD images should be either a STREAM_LF format or a fixed format.
+ *
+ */
+curl_off_t VmsRealFileSize(const char * name,
+                           const struct_stat * stat_buf)
+{
+  char buffer[8192];
+  curl_off_t count;
+  int ret_stat;
+  FILE * file;
+
+  file = fopen(name, "r");
+  if(file == NULL)
+    return 0;
+
+  count = 0;
+  ret_stat = 1;
+  while(ret_stat > 0) {
+    ret_stat = fread(buffer, 1, sizeof(buffer), file);
+    if(ret_stat != 0)
+      count += ret_stat;
+  }
+  fclose(file);
+
+  return count;
+}
+
+/*
+ *
+ *  VmsSpecialSize checks to see if the stat st_size can be trusted and
+ *  if not to call a routine to get the correct size.
+ *
+ */
+static curl_off_t VmsSpecialSize(const char * name,
+                                 const struct_stat * stat_buf)
+{
+  switch(stat_buf->st_fab_rfm) {
+  case FAB$C_VAR:
+  case FAB$C_VFC:
+    return VmsRealFileSize(name, stat_buf);
+    break;
+  default:
+    return stat_buf->st_size;
+  }
+}
+
+#endif
+
+#ifndef __VMS
+#define filesize(name, stat_data) (stat_data.st_size)
+#else
+    /* Getting the expected file size needs help on VMS */
+#define filesize(name, stat_data) VmsSpecialSize(name, &stat_data)
+#endif
+
 /*
  * AddFormData() adds a chunk of data to the FormData linked list.
  *
@@ -885,9 +856,10 @@ static CURLcode AddFormData(struct FormData **formp,
          file */
       if(!strequal("-", newform->line)) {
         struct_stat file;
-        if(!stat(newform->line, &file)) {
-          *size += file.st_size;
-        }
+        if(!stat(newform->line, &file) && !S_ISDIR(file.st_mode))
+          *size += filesize(newform->line, file);
+        else
+          return CURLE_BAD_FUNCTION_ARGUMENT;
       }
     }
   }
@@ -934,54 +906,26 @@ void Curl_formclean(struct FormData **form_ptr)
   *form_ptr = NULL;
 }
 
-#ifdef CURL_DOES_CONVERSIONS
-/*
- * Curl_formcovert() is used from http.c, this converts any
-   form items that need to be sent in the network encoding.
-   Returns CURLE_OK on success.
- */
-CURLcode Curl_formconvert(struct SessionHandle *data, struct FormData *form)
-{
-  struct FormData *next;
-  CURLcode rc;
-
-  if(!form)
-    return CURLE_OK;
-
-  if(!data)
-    return CURLE_BAD_FUNCTION_ARGUMENT;
-
-  do {
-    next=form->next;  /* the following form line */
-    if(form->type == FORM_DATA) {
-      rc = Curl_convert_to_network(data, form->line, form->length);
-      /* Curl_convert_to_network calls failf if unsuccessful */
-      if(rc != CURLE_OK)
-        return rc;
-    }
-  } while((form = next) != NULL); /* continue */
-  return CURLE_OK;
-}
-#endif /* CURL_DOES_CONVERSIONS */
-
 /*
  * curl_formget()
  * Serialize a curl_httppost struct.
  * Returns 0 on success.
+ *
+ * @unittest: 1308
  */
 int curl_formget(struct curl_httppost *form, void *arg,
                  curl_formget_callback append)
 {
-  CURLcode rc;
+  CURLcode result;
   curl_off_t size;
   struct FormData *data, *ptr;
 
-  rc = Curl_getformdata(NULL, &data, form, NULL, &size);
-  if(rc != CURLE_OK)
-    return (int)rc;
+  result = Curl_getformdata(NULL, &data, form, NULL, &size);
+  if(result)
+    return (int)result;
 
-  for (ptr = data; ptr; ptr = ptr->next) {
-    if(ptr->type == FORM_FILE) {
+  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;
@@ -990,14 +934,15 @@ int curl_formget(struct curl_httppost *form, void *arg,
 
       do {
         nread = readfromfile(&temp, buffer, sizeof(buffer));
-        if((nread == (size_t) -1) || (nread != append(arg, buffer, nread))) {
-          if(temp.fp) {
+        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 == sizeof(buffer));
+      } while(nread);
     }
     else {
       if(ptr->length != append(arg, ptr->line, ptr->length)) {
@@ -1029,10 +974,11 @@ void curl_formfree(struct curl_httppost *form)
     if(form->more)
       curl_formfree(form->more);
 
-    if( !(form->flags & HTTPPOST_PTRNAME) && form->name)
+    if(!(form->flags & HTTPPOST_PTRNAME) && form->name)
       free(form->name); /* free the name */
-    if( !(form->flags & (HTTPPOST_PTRCONTENTS|HTTPPOST_CALLBACK)) &&
-        form->contents)
+    if(!(form->flags &
+         (HTTPPOST_PTRCONTENTS|HTTPPOST_BUFFER|HTTPPOST_CALLBACK)) &&
+       form->contents)
       free(form->contents); /* free the contents */
     if(form->contenttype)
       free(form->contenttype); /* free the content type */
@@ -1105,6 +1051,49 @@ static char *strippath(const char *fullfile)
   return base; /* returns an allocated string or NULL ! */
 }
 
+static CURLcode formdata_add_filename(const struct curl_httppost *file,
+                                      struct FormData **form,
+                                      curl_off_t *size)
+{
+  CURLcode result = CURLE_OK;
+  char *filename = file->showfilename;
+  char *filebasename = NULL;
+  char *filename_escaped = NULL;
+
+  if(!filename) {
+    filebasename = strippath(file->contents);
+    if(!filebasename)
+      return CURLE_OUT_OF_MEMORY;
+    filename = filebasename;
+  }
+
+  if(strchr(filename, '\\') || strchr(filename, '"')) {
+    char *p0, *p1;
+
+    /* filename need be escaped */
+    filename_escaped = malloc(strlen(filename)*2+1);
+    if(!filename_escaped) {
+      Curl_safefree(filebasename);
+      return CURLE_OUT_OF_MEMORY;
+    }
+    p0 = filename_escaped;
+    p1 = filename;
+    while(*p1) {
+      if(*p1 == '\\' || *p1 == '"')
+        *p0++ = '\\';
+      *p0++ = *p1++;
+    }
+    *p0 = '\0';
+    filename = filename_escaped;
+  }
+  result = AddFormDataf(form, size,
+                        "; filename=\"%s\"",
+                        filename);
+  Curl_safefree(filename_escaped);
+  Curl_safefree(filebasename);
+  return result;
+}
+
 /*
  * Curl_getformdata() converts a linked list of "meta data" into a complete
  * (possibly huge) multipart formdata. The input list is in 'post', while the
@@ -1129,17 +1118,17 @@ CURLcode Curl_getformdata(struct SessionHandle *data,
   struct curl_httppost *file;
   CURLcode result = CURLE_OK;
 
-  curl_off_t size=0; /* support potentially ENORMOUS formposts */
+  curl_off_t size = 0; /* support potentially ENORMOUS formposts */
   char *boundary;
-  char *fileboundary=NULL;
+  char *fileboundary = NULL;
   struct curl_slist* curList;
 
-  *finalform=NULL; /* default form is empty */
+  *finalform = NULL; /* default form is empty */
 
   if(!post)
     return result; /* no input => no output! */
 
-  boundary = Curl_FormBoundary();
+  boundary = formboundary(data);
   if(!boundary)
     return CURLE_OUT_OF_MEMORY;
 
@@ -1151,7 +1140,7 @@ CURLcode Curl_getformdata(struct SessionHandle *data,
                         boundary);
 
   if(result) {
-    free(boundary);
+    Curl_safefree(boundary);
     return result;
   }
   /* we DO NOT include that line in the total size of the POST, since it'll be
@@ -1194,10 +1183,15 @@ CURLcode Curl_getformdata(struct SessionHandle *data,
       /* 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 */
 
-      fileboundary = Curl_FormBoundary();
+      Curl_safefree(fileboundary);
+      fileboundary = formboundary(data);
+      if(!fileboundary) {
+        result = CURLE_OUT_OF_MEMORY;
+        break;
+      }
 
       result = AddFormDataf(&form, &size,
-                            "\r\nContent-Type: multipart/mixed,"
+                            "\r\nContent-Type: multipart/mixed;"
                             " boundary=%s\r\n",
                             fileboundary);
       if(result)
@@ -1214,24 +1208,13 @@ CURLcode Curl_getformdata(struct SessionHandle *data,
 
       if(post->more) {
         /* if multiple-file */
-        char *filebasename= NULL;
-        if(!file->showfilename) {
-          filebasename = strippath(file->contents);
-          if(!filebasename) {
-            Curl_formclean(&firstform);
-            free(boundary);
-            return CURLE_OUT_OF_MEMORY;
-          }
-        }
-
         result = AddFormDataf(&form, &size,
                               "\r\n--%s\r\nContent-Disposition: "
-                              "attachment; filename=\"%s\"",
-                              fileboundary,
-                              (file->showfilename?file->showfilename:
-                               filebasename));
-        if(filebasename)
-          free(filebasename);
+                              "attachment",
+                              fileboundary);
+        if(result)
+          break;
+        result = formdata_add_filename(file, &form, &size);
         if(result)
           break;
       }
@@ -1240,15 +1223,9 @@ CURLcode Curl_getformdata(struct SessionHandle *data,
         /* it should be noted that for the HTTPPOST_FILENAME and
            HTTPPOST_CALLBACK cases the ->showfilename struct member is always
            assigned at this point */
-        char *filebasename=
-          (!post->showfilename)?strippath(post->contents):NULL;
-
-        result = AddFormDataf(&form, &size,
-                              "; filename=\"%s\"",
-                              (post->showfilename?post->showfilename:
-                               filebasename));
-        if(filebasename)
-          free(filebasename);
+        if(post->showfilename || (post->flags & HTTPPOST_FILENAME)) {
+          result = formdata_add_filename(post, &form, &size);
+        }
 
         if(result)
           break;
@@ -1264,18 +1241,15 @@ CURLcode Curl_getformdata(struct SessionHandle *data,
       }
 
       curList = file->contentheader;
-      while( curList ) {
+      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) {
-        Curl_formclean(&firstform);
-        free(boundary);
-        return result;
-      }
+      if(result)
+        break;
 
       result = AddFormDataf(&form, &size, "\r\n\r\n");
       if(result)
@@ -1297,7 +1271,7 @@ CURLcode Curl_getformdata(struct SessionHandle *data,
 
         if(fileread) {
           if(fileread != stdin) {
-            /* close the file again */
+            /* close the file */
             fclose(fileread);
             /* add the file name only - for later reading from this */
             result = AddFormData(&form, FORM_FILE, file->contents, 0, &size);
@@ -1319,7 +1293,7 @@ CURLcode Curl_getformdata(struct SessionHandle *data,
         }
         else {
           if(data)
-            failf(data, "couldn't open file \"%s\"\n", file->contents);
+            failf(data, "couldn't open file \"%s\"", file->contents);
           *finalform = NULL;
           result = CURLE_READ_ERROR;
         }
@@ -1341,11 +1315,8 @@ CURLcode Curl_getformdata(struct SessionHandle *data,
       file = file->more;
     } while(file && !result); /* for each specified file for this field */
 
-    if(result) {
-      Curl_formclean(&firstform);
-      free(boundary);
-      return result;
-    }
+    if(result)
+      break;
 
     if(post->more) {
       /* this was a multiple-file inclusion, make a termination file
@@ -1353,33 +1324,29 @@ CURLcode Curl_getformdata(struct SessionHandle *data,
       result = AddFormDataf(&form, &size,
                            "\r\n--%s--",
                            fileboundary);
-      free(fileboundary);
       if(result)
         break;
     }
 
   } while((post = post->next) != NULL); /* for each field */
-  if(result) {
-    Curl_formclean(&firstform);
-    free(boundary);
-    return result;
-  }
 
   /* end-boundary for everything */
-  result = AddFormDataf(&form, &size,
-                       "\r\n--%s--\r\n",
-                       boundary);
+  if(!result)
+    result = AddFormDataf(&form, &size, "\r\n--%s--\r\n", boundary);
+
   if(result) {
     Curl_formclean(&firstform);
-    free(boundary);
+    Curl_safefree(fileboundary);
+    Curl_safefree(boundary);
     return result;
   }
 
   *sizep = size;
 
-  free(boundary);
+  Curl_safefree(fileboundary);
+  Curl_safefree(boundary);
 
-  *finalform=firstform;
+  *finalform = firstform;
 
   return result;
 }
@@ -1401,26 +1368,67 @@ int Curl_FormInit(struct Form *form, struct FormData *formdata )
   return 0;
 }
 
+#ifndef __VMS
+# define fopen_read fopen
+#else
+  /*
+   * vmsfopenread
+   *
+   * For upload to work as expected on VMS, different optional
+   * parameters must be added to the fopen command based on
+   * record format of the file.
+   *
+   */
+# define fopen_read vmsfopenread
+static FILE * vmsfopenread(const char *file, const char *mode) {
+  struct_stat statbuf;
+  int result;
+
+  result = stat(file, &statbuf);
+
+  switch (statbuf.st_fab_rfm) {
+  case FAB$C_VAR:
+  case FAB$C_VFC:
+  case FAB$C_STMCR:
+    return fopen(file, "r");
+    break;
+  default:
+    return fopen(file, "r", "rfm=stmlf", "ctx=stm");
+  }
+}
+#endif
+
+/*
+ * 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 = (bool)(form->data->type == FORM_CALLBACK);
+  bool callback = (form->data->type == FORM_CALLBACK)?TRUE:FALSE;
 
-  if(callback)
-    nread = form->fread_func(buffer, 1, size, form->data->line);
+  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 */
+      form->fp = fopen_read(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 || nread > size) {
+  if(!nread) {
     /* this is the last chunk from the file, move on */
-    if(!callback) {
+    if(form->fp) {
       fclose(form->fp);
       form->fp = NULL;
     }
@@ -1460,7 +1468,7 @@ size_t Curl_FormReader(char *buffer,
   }
   do {
 
-    if( (form->data->length - form->sent ) > wantedsize - gotsize) {
+    if((form->data->length - form->sent ) > wantedsize - gotsize) {
 
       memcpy(buffer + gotsize , form->data->line + form->sent,
              wantedsize - gotsize);
@@ -1507,168 +1515,18 @@ char *Curl_formpostheader(void *formp, size_t *len)
   return header;
 }
 
-
-#ifdef _FORM_DEBUG
-int FormAddTest(const char * errormsg,
-                 struct curl_httppost **httppost,
-                 struct curl_httppost **last_post,
-                 ...)
-{
-  int result;
-  va_list arg;
-  va_start(arg, last_post);
-  if((result = FormAdd(httppost, last_post, arg)))
-    fprintf (stderr, "ERROR doing FormAdd ret: %d action: %s\n", result,
-             errormsg);
-  va_end(arg);
-  return result;
-}
-
-
-int main(int argc, argv_item_t argv[])
+/*
+ * formboundary() creates a suitable boundary string and returns an allocated
+ * one.
+ */
+static char *formboundary(struct SessionHandle *data)
 {
-  char name1[] = "simple_COPYCONTENTS";
-  char name2[] = "COPYCONTENTS_+_CONTENTTYPE";
-  char name3[] = "PTRNAME_+_NAMELENGTH_+_COPYNAME_+_CONTENTSLENGTH";
-  char name4[] = "simple_PTRCONTENTS";
-  char name5[] = "PTRCONTENTS_+_CONTENTSLENGTH";
-  char name6[] = "PTRCONTENTS_+_CONTENTSLENGTH_+_CONTENTTYPE";
-  char name7[] = "FILE1_+_CONTENTTYPE";
-  char name8[] = "FILE1_+_FILE2";
-  char name9[] = "FILE1_+_FILE2_+_FILE3";
-  char name10[] = "ARRAY: FILE1_+_FILE2_+_FILE3";
-  char name11[] = "FILECONTENT";
-  char value1[] = "value for simple COPYCONTENTS";
-  char value2[] = "value for COPYCONTENTS + CONTENTTYPE";
-  char value3[] = "value for PTRNAME + NAMELENGTH + COPYNAME + CONTENTSLENGTH";
-  char value4[] = "value for simple PTRCONTENTS";
-  char value5[] = "value for PTRCONTENTS + CONTENTSLENGTH";
-  char value6[] = "value for PTRCONTENTS + CONTENTSLENGTH + CONTENTTYPE";
-  char value7[] = "formdata.h";
-  char value8[] = "Makefile.b32";
-  char type2[] = "image/gif";
-  char type6[] = "text/plain";
-  char type7[] = "text/html";
-  int name3length = strlen(name3);
-  int value3length = strlen(value3);
-  int value5length = strlen(value5);
-  int value6length = strlen(value6);
-  int errors = 0;
-  CURLcode rc;
-  curl_off_t size;
-  size_t nread;
-  char buffer[4096];
-  struct curl_httppost *httppost=NULL;
-  struct curl_httppost *last_post=NULL;
-  struct curl_forms forms[4];
-
-  struct FormData *form;
-  struct Form formread;
-
-  (void) argc;
-  (void) argv;
-
-  Curl_srand();         /* Because we do not call curl_global_init() here. */
-
-  if(FormAddTest("simple COPYCONTENTS test", &httppost, &last_post,
-                  CURLFORM_COPYNAME, name1, CURLFORM_COPYCONTENTS, value1,
-                  CURLFORM_END))
-    ++errors;
-  if(FormAddTest("COPYCONTENTS  + CONTENTTYPE test", &httppost, &last_post,
-                  CURLFORM_COPYNAME, name2, CURLFORM_COPYCONTENTS, value2,
-                  CURLFORM_CONTENTTYPE, type2, CURLFORM_END))
-    ++errors;
-  /* make null character at start to check that contentslength works
-     correctly */
-  name3[1] = '\0';
-  value3[1] = '\0';
-  if(FormAddTest("PTRNAME + NAMELENGTH + COPYNAME + CONTENTSLENGTH test",
-                  &httppost, &last_post,
-                  CURLFORM_PTRNAME, name3, CURLFORM_COPYCONTENTS, value3,
-                  CURLFORM_CONTENTSLENGTH, value3length,
-                  CURLFORM_NAMELENGTH, name3length, CURLFORM_END))
-    ++errors;
-  if(FormAddTest("simple PTRCONTENTS test", &httppost, &last_post,
-                  CURLFORM_COPYNAME, name4, CURLFORM_PTRCONTENTS, value4,
-                  CURLFORM_END))
-    ++errors;
-  /* make null character at start to check that contentslength works
-     correctly */
-  value5[1] = '\0';
-  if(FormAddTest("PTRCONTENTS + CONTENTSLENGTH test", &httppost, &last_post,
-                  CURLFORM_COPYNAME, name5, CURLFORM_PTRCONTENTS, value5,
-                  CURLFORM_CONTENTSLENGTH, value5length, CURLFORM_END))
-    ++errors;
-  /* make null character at start to check that contentslength works
-     correctly */
-  value6[1] = '\0';
-  if(FormAddTest("PTRCONTENTS + CONTENTSLENGTH + CONTENTTYPE test",
-                  &httppost, &last_post,
-                  CURLFORM_COPYNAME, name6, CURLFORM_PTRCONTENTS, value6,
-                  CURLFORM_CONTENTSLENGTH, value6length,
-                  CURLFORM_CONTENTTYPE, type6, CURLFORM_END))
-    ++errors;
-  if(FormAddTest("FILE + CONTENTTYPE test", &httppost, &last_post,
-                  CURLFORM_COPYNAME, name7, CURLFORM_FILE, value7,
-                  CURLFORM_CONTENTTYPE, type7, CURLFORM_END))
-    ++errors;
-  if(FormAddTest("FILE1 + FILE2 test", &httppost, &last_post,
-                  CURLFORM_COPYNAME, name8, CURLFORM_FILE, value7,
-                  CURLFORM_FILE, value8, CURLFORM_END))
-    ++errors;
-  if(FormAddTest("FILE1 + FILE2 + FILE3 test", &httppost, &last_post,
-                  CURLFORM_COPYNAME, name9, CURLFORM_FILE, value7,
-                  CURLFORM_FILE, value8, CURLFORM_FILE, value7, CURLFORM_END))
-    ++errors;
-  forms[0].option = CURLFORM_FILE;
-  forms[0].value  = value7;
-  forms[1].option = CURLFORM_FILE;
-  forms[1].value  = value8;
-  forms[2].option = CURLFORM_FILE;
-  forms[2].value  = value7;
-  forms[3].option  = CURLFORM_END;
-  if(FormAddTest("FILE1 + FILE2 + FILE3 ARRAY test", &httppost, &last_post,
-                  CURLFORM_COPYNAME, name10, CURLFORM_ARRAY, forms,
-                  CURLFORM_END))
-    ++errors;
-  if(FormAddTest("FILECONTENT test", &httppost, &last_post,
-                  CURLFORM_COPYNAME, name11, CURLFORM_FILECONTENT, value7,
-                  CURLFORM_END))
-    ++errors;
-
-  rc = Curl_getformdata(NULL, &form, httppost, NULL, &size);
-  if(rc != CURLE_OK) {
-    if(rc != CURLE_READ_ERROR) {
-      const char *errortext = curl_easy_strerror(rc);
-      fprintf(stdout, "\n==> Curl_getformdata error: %s\n", errortext);
-    }
-    return 0;
-  }
-
-  Curl_FormInit(&formread, form);
-
-  for(;;) {
-    nread = Curl_FormReader(buffer, 1, sizeof(buffer),
-                            (FILE *)&formread);
-
-    if(nread < 1)
-      break;
-    fwrite(buffer, nread, 1, stdout);
-  }
-
-  fprintf(stdout, "size: ");
-  fprintf(stdout, "%" FORMAT_OFF_T, size);
-  fprintf(stdout, "\n");
-  if(errors)
-    fprintf(stdout, "\n==> %d Test(s) failed!\n", errors);
-  else
-    fprintf(stdout, "\nAll Tests seem to have worked (please check output)\n");
-
-  return 0;
+  /* 24 dashes and 16 hexadecimal digits makes 64 bit (18446744073709551615)
+     combinations */
+  return aprintf("------------------------%08x%08x",
+                 Curl_rand(data), Curl_rand(data));
 }
 
-#endif  /* _FORM_DEBUG */
-
 #else  /* CURL_DISABLE_HTTP */
 CURLFORMcode curl_formadd(struct curl_httppost **httppost,
                           struct curl_httppost **last_post,
@@ -1694,37 +1552,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) */