Revert "Update to 7.40.1"
[platform/upstream/curl.git] / lib / formdata.c
index cbef511..3260928 100644 (file)
@@ -5,7 +5,7 @@
  *                            | (__| |_| |  _ <| |___
  *                             \___|\___/|_| \_\_____|
  *
- * Copyright (C) 1998 - 2011, 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
  *
  ***************************************************************************/
 
-#include "setup.h"
+#include "curl_setup.h"
 
 #include <curl/curl.h>
 
-/* Length of the random boundary string. */
-#define BOUNDARY_LENGTH 40
-
 #if !defined(CURL_DISABLE_HTTP) || defined(USE_SSLEAY)
 
 #if defined(HAVE_LIBGEN_H) && defined(HAVE_BASENAME)
@@ -35,7 +32,7 @@
 
 #include "urldata.h" /* for struct SessionHandle */
 #include "formdata.h"
-#include "curl_rand.h"
+#include "vtls/vtls.h"
 #include "strequal.h"
 #include "curl_memory.h"
 #include "sendf.h"
@@ -56,6 +53,7 @@ static char *Curl_basename(char *path);
 #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. */
@@ -156,8 +154,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;
 }
@@ -172,8 +168,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;
@@ -182,7 +178,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[]={
@@ -430,7 +426,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?
@@ -458,9 +454,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;
@@ -535,10 +543,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;
@@ -596,6 +615,30 @@ CURLFORMcode FormAdd(struct curl_httppost **httppost,
     }
   }
 
+  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;
+      }
+    }
+  }
+
   if(CURL_FORMADD_OK == return_value) {
     /* go through the list, check for completeness and if everything is
      * alright add the HttpPost item otherwise set return_value accordingly */
@@ -624,9 +667,11 @@ CURLFORMcode FormAdd(struct curl_httppost **httppost,
         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;
@@ -675,32 +720,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;
@@ -724,6 +776,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.
  *
@@ -778,9 +894,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;
       }
     }
   }
@@ -972,6 +1089,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
@@ -996,17 +1156,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;
 
@@ -1018,7 +1178,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
@@ -1061,10 +1221,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)
@@ -1081,24 +1246,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;
       }
@@ -1108,15 +1262,7 @@ CURLcode Curl_getformdata(struct SessionHandle *data,
            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));
-          if(filebasename)
-            free(filebasename);
+          result = formdata_add_filename(post, &form, &size);
         }
 
         if(result)
@@ -1140,11 +1286,8 @@ CURLcode Curl_getformdata(struct SessionHandle *data,
           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)
@@ -1166,7 +1309,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);
@@ -1188,7 +1331,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;
         }
@@ -1210,11 +1353,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
@@ -1222,33 +1362,31 @@ 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(CURLE_OK == 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;
 }
@@ -1270,6 +1408,36 @@ 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()
  *
@@ -1292,7 +1460,7 @@ static size_t readfromfile(struct Form *form, char *buffer,
   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 */
     }
@@ -1387,6 +1555,18 @@ char *Curl_formpostheader(void *formp, size_t *len)
   return header;
 }
 
+/*
+ * formboundary() creates a suitable boundary string and returns an allocated
+ * one.
+ */
+static char *formboundary(struct SessionHandle *data)
+{
+  /* 24 dashes and 16 hexadecimal digits makes 64 bit (18446744073709551615)
+     combinations */
+  return aprintf("------------------------%08x%08x",
+                 Curl_rand(data), Curl_rand(data));
+}
+
 #else  /* CURL_DISABLE_HTTP */
 CURLFORMcode curl_formadd(struct curl_httppost **httppost,
                           struct curl_httppost **last_post,
@@ -1412,37 +1592,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) */