Skip to content
Snippets Groups Projects
formdata.c 40.9 KiB
Newer Older
  • Learn to ignore specific revisions
  • /***************************************************************************
    
    Daniel Stenberg's avatar
    Daniel Stenberg committed
     *                                  _   _ ____  _     
     *  Project                     ___| | | |  _ \| |    
     *                             / __| | | | |_) | |    
     *                            | (__| |_| |  _ <| |___ 
     *                             \___|\___/|_| \_\_____|
     *
    
     * Copyright (C) 1998 - 2004, Daniel Stenberg, <daniel@haxx.se>, et al.
    
    Daniel Stenberg's avatar
    Daniel Stenberg committed
     *
    
     * 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.
     * 
    
    Daniel Stenberg's avatar
    Daniel Stenberg committed
     * 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
    
     * furnished to do so, under the terms of the COPYING file.
    
    Daniel Stenberg's avatar
    Daniel Stenberg committed
     *
    
    Daniel Stenberg's avatar
    Daniel Stenberg committed
     * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY
     * KIND, either express or implied.
    
    Daniel Stenberg's avatar
    Daniel Stenberg committed
     *
    
    Daniel Stenberg's avatar
    Daniel Stenberg committed
     * $Id$
    
     ***************************************************************************/
    
    Daniel Stenberg's avatar
    Daniel Stenberg committed
    
    /*
      Debug the form generator stand-alone by compiling this source file with:
    
    
      gcc -DHAVE_CONFIG_H -I../ -g -D_FORM_DEBUG -o formdata -I../include formdata.c strequal.c
    
      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: text/plain
    vlue for PTRCOTNENTS + CONTENTSLENGTH + CONTENTTYPE
    
    (or you might see v^@lue at the start)
    
    
    Content-Disposition: form-data; name="FILE1_+_CONTENTTYPE"; filename="inet_ntoa_r.h"
    Content-Type: text/html
    ...
    
    Content-Disposition: form-data; name="FILE1_+_FILE2"
    Content-Type: multipart/mixed, boundary=curlz1s0dkticx49MV1KGcYP5cvfSsz
    
    Content-Disposition: attachment; filename="inet_ntoa_r.h"
    Content-Type: text/plain
    ...
    Content-Disposition: attachment; filename="Makefile.b32.resp"
    Content-Type: text/plain
    
    ...
    
    Content-Disposition: form-data; name="FILE1_+_FILE2_+_FILE3"
    Content-Type: multipart/mixed, boundary=curlirkYPmPwu6FrJ1vJ1u1BmtIufh1
    ...
    Content-Disposition: attachment; filename="inet_ntoa_r.h"
    Content-Type: text/plain
    ...
    Content-Disposition: attachment; filename="Makefile.b32.resp"
    Content-Type: text/plain
    ...
    Content-Disposition: attachment; filename="inet_ntoa_r.h"
    Content-Type: text/plain
    
    
    Content-Disposition: form-data; name="ARRAY: FILE1_+_FILE2_+_FILE3"
    Content-Type: multipart/mixed, boundary=curlirkYPmPwu6FrJ1vJ1u1BmtIufh1
    ...
    Content-Disposition: attachment; filename="inet_ntoa_r.h"
    Content-Type: text/plain
    ...
    Content-Disposition: attachment; filename="Makefile.b32.resp"
    Content-Type: text/plain
    ...
    Content-Disposition: attachment; filename="inet_ntoa_r.h"
    Content-Type: text/plain
    ...
    
    Content-Disposition: form-data; name="FILECONTENT"
    ...
    
    
    Daniel Stenberg's avatar
    Daniel Stenberg committed
    #include <stdio.h>
    #include <stdlib.h>
    #include <string.h>
    #include <stdarg.h>
    #include <time.h>
    #include <curl/curl.h>
    #include "formdata.h"
    
    #include "strequal.h"
    
    #include "memory.h"
    
    #define _MPRINTF_REPLACE /* use our functions only */
    #include <curl/mprintf.h>
    
    /* The last #include file should be: */
    #include "memdebug.h"
    
    
    /* Length of the random boundary string. */
    #define BOUNDARY_LENGTH 40
    
    Daniel Stenberg's avatar
    Daniel Stenberg committed
    
    /* What kind of Content-Type to use on un-specified files with unrecognized
       extensions. */
    
    #define HTTPPOST_CONTENTTYPE_DEFAULT "application/octet-stream"
    
    Daniel Stenberg's avatar
    Daniel Stenberg committed
    
    #define FORM_FILE_SEPARATOR ','
    #define FORM_TYPE_SEPARATOR ';'
    
    
    /***************************************************************************
     *
     * AddHttpPost()
     *	
     * Adds a HttpPost structure to the list, if parent_post is given becomes
     * a subpost of parent_post instead of a direct list element.
     *
    
     * Returns newly allocated HttpPost on success and NULL if malloc failed.
    
     *
     ***************************************************************************/
    
    AddHttpPost(char * name, size_t namelength,
                char * value, size_t contentslength,
                char * buffer, size_t bufferlength,
    
                char *contenttype,
                long flags,
                struct curl_slist* contentHeader,
                char *showfilename,
                struct curl_httppost *parent_post,
                struct curl_httppost **httppost,
                struct curl_httppost **last_post)
    
      post = (struct curl_httppost *)calloc(sizeof(struct curl_httppost), 1);
    
        post->namelength = (long)(name?(namelength?namelength:strlen(name)):0);
    
        post->contentslength = (long)contentslength;
    
        post->bufferlength = (long)bufferlength;
    
        post->contenttype = contenttype;
    
        post->contentheader = contentHeader;
    
        post->flags = flags;
      }
      else
        return NULL;
      
      if (parent_post) {
        /* now, point our 'more' to the original 'more' */
        post->more = parent_post->more;
        
        /* then move the original 'more' to point to ourselves */
        parent_post->more = post;	         
      }
      else {
        /* make the previous point to this */
        if(*last_post)
          (*last_post)->next = post;
        else
          (*httppost) = post;
        
        (*last_post) = post;  
      }
      return post;
    }
    
    
    /***************************************************************************
     *
     * AddFormInfo()
     *	
     * Adds a FormInfo structure to the list presented by parent_form_info.
     *
     * Returns newly allocated FormInfo on success and NULL if malloc failed/
     * parent_form_info is NULL.
     *
     ***************************************************************************/
    
    static FormInfo * AddFormInfo(char *value,
                                  char *contenttype,
                                  FormInfo *parent_form_info)
    
    {
      FormInfo *form_info;
      form_info = (FormInfo *)malloc(sizeof(FormInfo));
      if(form_info) {
        memset(form_info, 0, sizeof(FormInfo));
        if (value)
          form_info->value = value;
        if (contenttype)
          form_info->contenttype = contenttype;
        form_info->flags = HTTPPOST_FILENAME;
      }
      else
        return NULL;
      
      if (parent_form_info) {
        /* now, point our 'more' to the original 'more' */
        form_info->more = parent_form_info->more;
        
        /* then move the original 'more' to point to ourselves */
        parent_form_info->more = form_info;
      }
      else
        return NULL;
    
      return form_info;
    }
    
    /***************************************************************************
     *
     * 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 {
        const char *extension;
        const char *type;
      };
      static struct ContentType ctts[]={
        {".gif",  "image/gif"},
        {".jpg",  "image/jpeg"},
        {".jpeg", "image/jpeg"},
        {".txt",  "text/plain"},
    
        {".html", "text/html"}
    
      };
      
      if(prevtype)
        /* default to the previously set/used! */
        contenttype = prevtype;
      else
        /* It seems RFC1867 defines no Content-Type to default to
           text/plain so we don't actually need to set this: */
        contenttype = HTTPPOST_CONTENTTYPE_DEFAULT;
      
      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;
    }
    
    /***************************************************************************
     *
    
     * 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;
    
      if (buffer_length)
        length = buffer_length;
    
        length = strlen(src);
    
      buffer = (char*)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.
    
     *
     * Has two principal functionalities: using files and byte arrays as
     * post parts. Byte arrays are either copied or just the pointer is stored
     * (as the user requests) while for files only the filename and not the
     * content is stored.
     *
     * While you may have only one byte array for each name, multiple filenames
     * are allowed (and because of this feature CURLFORM_END is needed after
     * using CURLFORM_FILE).
     *
     * Examples:
     *
     * Simple name/value pair with copied contents:
     * curl_formadd (&post, &last, CURLFORM_COPYNAME, "name",
    
     * CURLFORM_COPYCONTENTS, "value", CURLFORM_END);
    
     *
     * name/value pair where only the content pointer is remembered:
     * curl_formadd (&post, &last, CURLFORM_COPYNAME, "name",
    
     * CURLFORM_PTRCONTENTS, ptr, CURLFORM_CONTENTSLENGTH, 10, CURLFORM_END);
    
     * (if CURLFORM_CONTENTSLENGTH is missing strlen () is used)
     *
     * storing a filename (CONTENTTYPE is optional!):
     * curl_formadd (&post, &last, CURLFORM_COPYNAME, "name",
     * CURLFORM_FILE, "filename1", CURLFORM_CONTENTTYPE, "plain/text",
     * CURLFORM_END);
     *
     * storing multiple filenames:
     * curl_formadd (&post, &last, CURLFORM_COPYNAME, "name",
     * CURLFORM_FILE, "filename1", CURLFORM_FILE, "filename2", CURLFORM_END);
     *
    
     * CURL_FORMADD_OK             on success
     * CURL_FORMADD_MEMORY         if the FormInfo allocation fails
     * CURL_FORMADD_OPTION_TWICE   if one option is given twice for one Form
     * 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_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
    
     *
     ***************************************************************************/
    
    static
    
    CURLFORMcode FormAdd(struct curl_httppost **httppost,
                         struct curl_httppost **last_post,
                         va_list params)
    
      FormInfo *first_form, *current_form, *form;
    
      CURLFORMcode return_value = CURL_FORMADD_OK;
    
      const char *prevtype = NULL;
    
      CURLformoption option;
      struct curl_forms *forms = NULL;
    
      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
         va_list arguments. */
      bool array_state = FALSE;
    
      /*
       * We need to allocate the first struct to fill in.
       */
    
      first_form = (FormInfo *)calloc(sizeof(struct FormInfo), 1);
      if(!first_form)
    
      current_form = first_form;
    
    
       * Loop through all the options set. Break if we have an error to report.
    
      while (return_value == CURL_FORMADD_OK) {
    
    
        /* first see if we have more parts of the array param */
        if ( array_state ) {
          /* get the upcoming option from the given array */
    
          array_value = (char *)forms->value;
    
    
          forms++; /* advance this to next entry */
    
          if (CURLFORM_END == option) {
            /* end of array state */
            array_state = FALSE;
            continue;
          }
        }
        else {
          /* This is not array-state, get next option */
          option = va_arg(params, CURLformoption);
          if (CURLFORM_END == option)
    
        }
    
        switch (option) {
        case CURLFORM_ARRAY:
    
          if(array_state)
            /* we don't support an array from within an array */
    
            return_value = CURL_FORMADD_ILLEGAL_ARRAY;
    
          else {
            forms = va_arg(params, struct curl_forms *);
            if (forms)
              array_state = TRUE;
            else
    
          break;
    
          /*
           * Set the Name property.
           */
        case CURLFORM_PTRNAME:
          current_form->flags |= HTTPPOST_PTRNAME; /* fall through */
        case CURLFORM_COPYNAME:
          if (current_form->name)
    
            return_value = CURL_FORMADD_OPTION_TWICE;
    
            char *name = array_state?
              array_value:va_arg(params, char *);
    
            if (name)
              current_form->name = name; /* store for the moment */
    
          }
          break;
        case CURLFORM_NAMELENGTH:
          if (current_form->namelength)
    
            return_value = CURL_FORMADD_OPTION_TWICE;
    
              array_state?(long)array_value:va_arg(params, long);
    
          break;
    
          /*
           * Set the contents property.
           */
        case CURLFORM_PTRCONTENTS:
          current_form->flags |= HTTPPOST_PTRCONTENTS; /* fall through */
        case CURLFORM_COPYCONTENTS:
          if (current_form->value)
    
            return_value = CURL_FORMADD_OPTION_TWICE;
    
            char *value =
              array_state?array_value:va_arg(params, char *);
    
            if (value)
              current_form->value = value; /* store for the moment */
            else
    
          }
          break;
        case CURLFORM_CONTENTSLENGTH:
          if (current_form->contentslength)
    
            return_value = CURL_FORMADD_OPTION_TWICE;
    
              array_state?(long)array_value:va_arg(params, long);
    
          break;
    
          /* Get contents from a given file name */
        case CURLFORM_FILECONTENT:
          if (current_form->flags != 0)
    
            return_value = CURL_FORMADD_OPTION_TWICE;
    
            char *filename = array_state?
              array_value:va_arg(params, char *);
    
            if (filename) {
              current_form->value = strdup(filename);
              current_form->flags |= HTTPPOST_READFILE;
    
          }
          break;
    
          /* We upload a file */
        case CURLFORM_FILE:
          {
    
            char *filename = array_state?array_value:
              va_arg(params, char *);
    
            if (current_form->value) {
              if (current_form->flags & HTTPPOST_FILENAME) {
                if (filename) {
                  if (!(current_form = AddFormInfo(strdup(filename),
                                                   NULL, current_form)))
    
                return_value = CURL_FORMADD_OPTION_TWICE;
    
              if (filename)
                current_form->value = strdup(filename);
              else
    
              current_form->flags |= HTTPPOST_FILENAME;
    
    
        case CURLFORM_BUFFER:
          {
            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)))
                    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);
              else
                return_value = CURL_FORMADD_NULL;
              current_form->flags |= HTTPPOST_BUFFER;
            }
            break;
          }
          
        case CURLFORM_BUFFERPTR:
            current_form->flags |= HTTPPOST_PTRBUFFER;
          if (current_form->buffer)
            return_value = CURL_FORMADD_OPTION_TWICE;
          else {
            char *buffer =
              array_state?array_value:va_arg(params, char *);
            if (buffer)
              current_form->buffer = buffer; /* store for the moment */
            else
              return_value = CURL_FORMADD_NULL;
          }
          break;
    
        case CURLFORM_BUFFERLENGTH:
          if (current_form->bufferlength)
            return_value = CURL_FORMADD_OPTION_TWICE;
          else
            current_form->bufferlength =
              array_state?(long)array_value:va_arg(params, long);
          break;
    
    
        case CURLFORM_CONTENTTYPE:
          {
    
              array_state?array_value:va_arg(params, char *);
    
            if (current_form->contenttype) {
              if (current_form->flags & HTTPPOST_FILENAME) {
    
                  if (!(current_form = AddFormInfo(NULL,
                                                   strdup(contenttype),
                                                   current_form)))
    
                return_value = CURL_FORMADD_OPTION_TWICE;
    
    	    current_form->contenttype = strdup(contenttype);
    
        case CURLFORM_CONTENTHEADER:
          {
    
            /* this "cast increases required alignment of target type" but
               we consider it OK anyway */
    
            struct curl_slist* list = array_state?
              (struct curl_slist*)array_value:
              va_arg(params, struct curl_slist*);
    
            
            if( current_form->contentheader )
    
              return_value = CURL_FORMADD_OPTION_TWICE;
    
            else
              current_form->contentheader = list;
            
            break;
          }
    
            char *filename = array_state?array_value:
    
              va_arg(params, char *);
            if( current_form->showfilename )
    
              return_value = CURL_FORMADD_OPTION_TWICE;
    
            else
              current_form->showfilename = strdup(filename);
            break;
          }
    
          return_value = CURL_FORMADD_UNKNOWN_OPTION;
    
      if(CURL_FORMADD_OK == return_value) {
    
        /* go through the list, check for copleteness and if everything is
         * alright add the HttpPost item otherwise set return_value accordingly */
    
        
        post = NULL;
        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) )
               ) {
    
            return_value = CURL_FORMADD_INCOMPLETE;
    
            if ( ((form->flags & HTTPPOST_FILENAME) ||
                  (form->flags & HTTPPOST_BUFFER)) &&
    
                 !form->contenttype ) {
              /* our contenttype is missing */
              form->contenttype
                = strdup(ContentTypeForFilename(form->value, prevtype));
            }
            if ( !(form->flags & HTTPPOST_PTRNAME) &&
                 (form == first_form) ) {
              /* copy name (without strdup; possibly contains null characters) */
    
              form->name = memdup(form->name, form->namelength);
              if (!form->name) {
    
              form->name_alloc = TRUE;
    
            }
            if ( !(form->flags & HTTPPOST_FILENAME) &&
                 !(form->flags & HTTPPOST_READFILE) && 
    
                 !(form->flags & HTTPPOST_PTRCONTENTS) &&
                 !(form->flags & HTTPPOST_PTRBUFFER) ) {
    
              /* copy value (without strdup; possibly contains null characters) */
    
              form->value = memdup(form->value, form->contentslength);
              if (!form->value) {
    
              form->value_alloc = TRUE;
    
            post = AddHttpPost(form->name, form->namelength,
                               form->value, form->contentslength,
    
                               form->contenttype, form->flags,
    
                               form->contentheader, form->showfilename,
    
            if (form->contenttype)
              prevtype = form->contenttype;
    
      if(return_value && form) {
        /* we return on error, free possibly allocated fields */
        if(form->name_alloc)
          free(form->name);
        if(form->value_alloc)
          free(form->value);
      }
    
    
      /* 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);
      }
    
    /*
     * curl_formadd() is a public API to add a section to the multipart formpost.
     */
    
    
    CURLFORMcode curl_formadd(struct curl_httppost **httppost,
    
      va_start(arg, last_post);
      result = FormAdd(httppost, last_post, arg);
      va_end(arg);
      return result;
    }
    
    
    /*
     * 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,
                                const void *line,
                                size_t length,
                                size_t *size)
    
    Daniel Stenberg's avatar
    Daniel Stenberg committed
    {
      struct FormData *newform = (struct FormData *)
        malloc(sizeof(struct FormData));
    
      if (!newform)
        return CURLE_OUT_OF_MEMORY;
    
    Daniel Stenberg's avatar
    Daniel Stenberg committed
      newform->next = NULL;
    
      /* we make it easier for plain strings: */
      if(!length)
        length = strlen((char *)line);
    
      newform->line = (char *)malloc(length+1);
    
      if (!newform->line) {
        free(newform);
        return CURLE_OUT_OF_MEMORY;
      }
    
      memcpy(newform->line, line, length);
    
    Daniel Stenberg's avatar
    Daniel Stenberg committed
      newform->length = length;
    
      newform->line[length]=0; /* zero terminate for easier debugging */
    
    Daniel Stenberg's avatar
    Daniel Stenberg committed
      
      if(*formp) {
        (*formp)->next = newform;
        *formp = newform;
      }
      else
        *formp = newform;
    
    
      if (size)
        *size += length;
      return CURLE_OK;
    
    /*
     * AddFormDataf() adds printf()-style formatted data to the formdata chain.
     */
    
    Daniel Stenberg's avatar
    Daniel Stenberg committed
    
    
    static CURLcode AddFormDataf(struct FormData **formp,
    
                                 size_t *size,
                                 const char *fmt, ...)
    
    Daniel Stenberg's avatar
    Daniel Stenberg committed
    {
    
      char s[4096];
    
    Daniel Stenberg's avatar
    Daniel Stenberg committed
      va_list ap;
      va_start(ap, fmt);
      vsprintf(s, fmt, ap);
      va_end(ap);
    
    
      return AddFormData(formp, s, 0, size);
    
    /*
     * Curl_FormBoundary() creates a suitable boundary string and returns an
     * allocated one.
     */
    
    Daniel Stenberg's avatar
    Daniel Stenberg committed
    {
      char *retstring;
      static int randomizer=0; /* this is just so that two boundaries within
    			      the same form won't be identical */
    
    Daniel Stenberg's avatar
    Daniel Stenberg committed
    
    
      static char table16[]="abcdef0123456789";
    
    Daniel Stenberg's avatar
    Daniel Stenberg committed
    
    
      retstring = (char *)malloc(BOUNDARY_LENGTH+1);
    
    Daniel Stenberg's avatar
    Daniel Stenberg committed
    
      if(!retstring)
        return NULL; /* failed */
    
      srand(time(NULL)+randomizer++); /* seed */
    
    
      strcpy(retstring, "----------------------------");
    
    Daniel Stenberg's avatar
    Daniel Stenberg committed
    
    
      for(i=strlen(retstring); i<BOUNDARY_LENGTH; i++)
        retstring[i] = table16[rand()%16];
    
      /* 28 dashes and 12 hexadecimal digits makes 12^16 (184884258895036416)
         combinations */
      retstring[BOUNDARY_LENGTH]=0; /* zero terminate */
    
    Daniel Stenberg's avatar
    Daniel Stenberg committed
    
      return retstring;
    }
    
    
    /*
     * Curl_formclean() is used from http.c, this cleans a built FormData linked
     * list
     */ 
    
    void Curl_formclean(struct FormData *form)
    
    Daniel Stenberg's avatar
    Daniel Stenberg committed
    {
      struct FormData *next;
    
    Daniel Stenberg's avatar
    Daniel Stenberg committed
      do {
        next=form->next;  /* the following form line */
        free(form->line); /* free the line */
        free(form);       /* free the struct */
    
    Daniel Stenberg's avatar
    Daniel Stenberg committed
      
      } while((form=next)); /* continue */
    }
    
    
    /*
     * curl_formfree() is an external function to free up a whole form post
     * chain
     */
    
    void curl_formfree(struct curl_httppost *form)
    
    
      if(!form)
        /* no form to free, just get out of this */
        return;
    
    
    Daniel Stenberg's avatar
    Daniel Stenberg committed
      do {
        next=form->next;  /* the following form line */
    
        /* recurse to sub-contents */
        if(form->more)
          curl_formfree(form->more);
    
    
        if( !(form->flags & HTTPPOST_PTRNAME) && form->name)
    
    Daniel Stenberg's avatar
    Daniel Stenberg committed
          free(form->name); /* free the name */
    
        if( !(form->flags & HTTPPOST_PTRCONTENTS) && form->contents)
    
    Daniel Stenberg's avatar
    Daniel Stenberg committed
          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 */
    
    Daniel Stenberg's avatar
    Daniel Stenberg committed
        free(form);       /* free the struct */
    
    Daniel Stenberg's avatar
    Daniel Stenberg committed
    
    
      } while((form=next)); /* continue */
    
    /*
     * 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.
     */
    
    
    CURLcode Curl_getFormData(struct FormData **finalform,
                              struct curl_httppost *post,
    
                              curl_off_t *sizep)
    
    Daniel Stenberg's avatar
    Daniel Stenberg committed
    {
      struct FormData *form = NULL;
      struct FormData *firstform;
    
    Daniel Stenberg's avatar
    Daniel Stenberg committed
    
    
    Daniel Stenberg's avatar
    Daniel Stenberg committed
      char *boundary;
      char *fileboundary=NULL;
    
      struct curl_slist* curList;
    
    
      *finalform=NULL; /* default form is empty */
    
    Daniel Stenberg's avatar
    Daniel Stenberg committed
    
      if(!post)
    
        return result; /* no input => no output! */
    
    Daniel Stenberg's avatar
    Daniel Stenberg committed
    
    
      boundary = Curl_FormBoundary();
    
    Daniel Stenberg's avatar
    Daniel Stenberg committed
      
      /* Make the first line of the output */
    
      result = AddFormDataf(&form, NULL,
                            "Content-Type: multipart/form-data;"
                            " boundary=%s\r\n",
                            boundary);
      if (result) {
    
      /* we DO NOT include that line in the total size of the POST, since it'll be
         part of the header! */
    
    Daniel Stenberg's avatar
    Daniel Stenberg committed
    
      firstform = form;
      
      do {
    
    
        if(size) {
          result = AddFormDataf(&form, &size, "\r\n");
    
    Daniel Stenberg's avatar
    Daniel Stenberg committed
        /* boundary */
    
        result = AddFormDataf(&form, &size, "--%s\r\n", boundary);
    
    Daniel Stenberg's avatar
    Daniel Stenberg committed
    
    
                             "Content-Disposition: form-data; name=\"", 0, &size);
        if (result)
    
        result = AddFormData(&form, post->name, post->namelength, &size);
    
        size += AddFormData(&form, "\"", 0, &size);
    
    Daniel Stenberg's avatar
    Daniel Stenberg committed
    
        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 */
    
    
          fileboundary = Curl_FormBoundary();
    
    Daniel Stenberg's avatar
    Daniel Stenberg committed
    
    
          result = AddFormDataf(&form, &size,
    
                                "\r\nContent-Type: multipart/mixed,"
                                " boundary=%s\r\n",
                                fileboundary);
          if (result)
    
    Daniel Stenberg's avatar
    Daniel Stenberg committed
        }
    
        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. */
    
    
    Daniel Stenberg's avatar
    Daniel Stenberg committed
          if(post->more) {
    
            result = AddFormDataf(&form, &size,
    
                                  "\r\n--%s\r\nContent-Disposition: "
                                  "attachment; filename=\"%s\"",
                                  fileboundary,
                                  (file->showfilename?file->showfilename:
                                   file->contents));
            if (result)
    
    Daniel Stenberg's avatar
    Daniel Stenberg committed
          }
    
          else if((post->flags & HTTPPOST_FILENAME) ||
                  (post->flags & HTTPPOST_BUFFER)) {
    
    
            result = AddFormDataf(&form, &size,
    
                                  "; filename=\"%s\"",
                                  (post->showfilename?post->showfilename:
                                   post->contents));
            if (result)
    
    Daniel Stenberg's avatar
    Daniel Stenberg committed
          }
          
          if(file->contenttype) {
    
            result = AddFormDataf(&form, &size,
    
                                  "\r\nContent-Type: %s",
                                  file->contenttype);
            if (result)