Skip to content
Snippets Groups Projects
formdata.c 51.4 KiB
Newer Older
  • Learn to ignore specific revisions
  • /***************************************************************************
    
     *                                  _   _ ____  _
     *  Project                     ___| | | |  _ \| |
     *                             / __| | | | |_) | |
     *                            | (__| |_| |  _ <| |___
    
    Daniel Stenberg's avatar
    Daniel Stenberg committed
     *                             \___|\___/|_| \_\_____|
     *
    
     * Copyright (C) 1998 - 2008, 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 -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-Disposition: attachment; filename="Makefile.b32"
    
    ...
    
    Content-Disposition: form-data; name="FILE1_+_FILE2_+_FILE3"
    Content-Type: multipart/mixed, boundary=curlirkYPmPwu6FrJ1vJ1u1BmtIufh1
    ...
    
    Content-Disposition: attachment; filename="formdata.h"
    
    Content-Disposition: attachment; filename="Makefile.b32"
    
    Content-Disposition: attachment; filename="formdata.h"
    
    
    Content-Disposition: form-data; name="ARRAY: FILE1_+_FILE2_+_FILE3"
    Content-Type: multipart/mixed, boundary=curlirkYPmPwu6FrJ1vJ1u1BmtIufh1
    ...
    
    Content-Disposition: attachment; filename="formdata.h"
    
    Content-Disposition: attachment; filename="Makefile.b32"
    
    Content-Disposition: attachment; filename="formdata.h"
    
    ...
    
    Content-Disposition: form-data; name="FILECONTENT"
    ...
    
    
    #include <curl/curl.h>
    
    /* Length of the random boundary string. */
    #define BOUNDARY_LENGTH 40
    
    
    #if !defined(CURL_DISABLE_HTTP) || defined(USE_SSLEAY)
    
    Daniel Stenberg's avatar
    Daniel Stenberg committed
    #include <stdio.h>
    #include <stdlib.h>
    #include <string.h>
    #include <stdarg.h>
    #include <time.h>
    
    #if defined(HAVE_LIBGEN_H) && defined(HAVE_BASENAME)
    
    #include "urldata.h" /* for struct SessionHandle */
    #include "easyif.h" /* for Curl_convert_... prototypes */
    
    Daniel Stenberg's avatar
    Daniel Stenberg committed
    #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"
    
    
    #endif  /* !defined(CURL_DISABLE_HTTP) || defined(USE_SSLEAY) */
    
    #ifndef CURL_DISABLE_HTTP
    
    
    #if defined(HAVE_BASENAME) && defined(NEED_BASENAME_PROTO)
    /* This system has a basename() but no prototype for it! */
    char *basename(char *path);
    #endif
    
    
    static size_t readfromfile(struct Form *form, char *buffer, size_t size);
    
    
    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,
    
                struct curl_httppost *parent_post,
                struct curl_httppost **httppost,
                struct curl_httppost **last_post)
    
      post = 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;
    
        /* 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;
    
    /***************************************************************************
     *
     * 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)
    
      form_info = malloc(sizeof(FormInfo));
    
      if(form_info) {
        memset(form_info, 0, sizeof(FormInfo));
    
          form_info->contenttype = contenttype;
        form_info->flags = HTTPPOST_FILENAME;
      }
      else
        return NULL;
    
        /* 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 const 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
        contenttype = HTTPPOST_CONTENTTYPE_DEFAULT;
    
      if(filename) { /* in case a NULL was passed in */
        for(i=0; i<sizeof(ctts)/sizeof(ctts[0]); i++) {
          if(strlen(filename) >= strlen(ctts[i].extension)) {
            if(strequal(filename +
                        strlen(filename) - strlen(ctts[i].extension),
                        ctts[i].extension)) {
              contenttype = ctts[i].type;
              break;
            }
    
        }
      }
      /* we have a contenttype by now */
      return contenttype;
    }
    
    /***************************************************************************
     *
    
     * 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;
    
      else if(src) {
    
        length = strlen(src);
    
      else
        /* no length and a NULL src pointer! */
    
      buffer = malloc(length+add);
    
        return NULL; /* fail */
    
      memcpy(buffer, src, length);
    
    
      /* if length unknown do null termination */
    
        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 = NULL;
    
      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 = calloc(sizeof(struct FormInfo), 1);
    
      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 */
    
          /* get the upcoming option from the given array */
    
          array_value = (char *)forms->value;
    
    
          forms++; /* advance this to next entry */
    
            /* end of array state */
            array_state = FALSE;
            continue;
          }
        }
        else {
          /* This is not array-state, get next option */
          option = va_arg(params, CURLformoption);
    
        }
    
        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 *);
    
          break;
    
          /*
           * Set the Name property.
           */
        case CURLFORM_PTRNAME:
    
    #ifdef CURL_DOES_CONVERSIONS
          /* treat CURLFORM_PTR like CURLFORM_COPYNAME so we'll
             have safe memory for the eventual conversion */
    #else
    
          current_form->flags |= HTTPPOST_PTRNAME; /* fall through */
    
        case CURLFORM_COPYNAME:
    
            return_value = CURL_FORMADD_OPTION_TWICE;
    
            char *name = array_state?
              array_value:va_arg(params, char *);
    
              current_form->name = name; /* store for the moment */
    
          }
          break;
        case CURLFORM_NAMELENGTH:
    
            return_value = CURL_FORMADD_OPTION_TWICE;
    
              array_state?(size_t)array_value:(size_t)va_arg(params, long);
    
          break;
    
          /*
           * Set the contents property.
           */
        case CURLFORM_PTRCONTENTS:
          current_form->flags |= HTTPPOST_PTRCONTENTS; /* fall through */
        case CURLFORM_COPYCONTENTS:
    
            return_value = CURL_FORMADD_OPTION_TWICE;
    
            char *value =
              array_state?array_value:va_arg(params, char *);
    
              current_form->value = value; /* store for the moment */
            else
    
          }
          break;
        case CURLFORM_CONTENTSLENGTH:
    
            return_value = CURL_FORMADD_OPTION_TWICE;
    
              array_state?(size_t)array_value:(size_t)va_arg(params, long);
    
          break;
    
          /* Get contents from a given file name */
        case CURLFORM_FILECONTENT:
    
            return_value = CURL_FORMADD_OPTION_TWICE;
    
              array_value:va_arg(params, char *);
    
              current_form->value = strdup(filename);
    
              if(!current_form->value)
                return_value = CURL_FORMADD_MEMORY;
    
                current_form->flags |= HTTPPOST_READFILE;
    
                current_form->value_alloc = TRUE;
              }
    
          }
          break;
    
          /* We upload a file */
        case CURLFORM_FILE:
          {
    
            const char *filename = array_state?array_value:
    
            if(current_form->value) {
              if(current_form->flags & HTTPPOST_FILENAME) {
                if(filename) {
                  if((current_form = AddFormInfo(strdup(filename),
    
                return_value = CURL_FORMADD_OPTION_TWICE;
    
                current_form->value = strdup(filename);
    
                if(!current_form->value)
                  return_value = CURL_FORMADD_MEMORY;
                else {
                  current_form->flags |= HTTPPOST_FILENAME;
                  current_form->value_alloc = TRUE;
                }
              }
    
            const char *filename = array_state?array_value:
    
            if(current_form->value) {
              if(current_form->flags & HTTPPOST_BUFFER) {
                if(filename) {
                  if((current_form = AddFormInfo(strdup(filename),
    
                    return_value = CURL_FORMADD_MEMORY;
                }
                else
                  return_value = CURL_FORMADD_NULL;
              }
              else
                return_value = CURL_FORMADD_OPTION_TWICE;
            }
            else {
    
                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;
          }
    
          current_form->flags |= HTTPPOST_PTRBUFFER;
    
            return_value = CURL_FORMADD_OPTION_TWICE;
          else {
            char *buffer =
              array_state?array_value:va_arg(params, char *);
    
              current_form->buffer = buffer; /* store for the moment */
            else
              return_value = CURL_FORMADD_NULL;
          }
          break;
    
        case CURLFORM_BUFFERLENGTH:
    
            return_value = CURL_FORMADD_OPTION_TWICE;
          else
            current_form->bufferlength =
    
              array_state?(size_t)array_value:(size_t)va_arg(params, long);
    
        case CURLFORM_STREAM:
          current_form->flags |= HTTPPOST_CALLBACK;
          if(current_form->userp)
            return_value = CURL_FORMADD_OPTION_TWICE;
          else {
            char *userp =
              array_state?array_value:va_arg(params, char *);
            if(userp) {
              current_form->userp = userp;
              current_form->value = userp; /* this isn't strictly true but we
                                              derive a value from this later on
                                              and we need this non-NULL to be
                                              accepted as a fine form part */
            }
            else
              return_value = CURL_FORMADD_NULL;
          }
          break;
    
    
        case CURLFORM_CONTENTTYPE:
          {
    
              array_state?array_value:va_arg(params, char *);
    
            if(current_form->contenttype) {
              if(current_form->flags & HTTPPOST_FILENAME) {
                if(contenttype) {
                  if((current_form = AddFormInfo(NULL,
    
                else
                  return_value = CURL_FORMADD_NULL;
    
                return_value = CURL_FORMADD_OPTION_TWICE;
    
                current_form->contenttype = strdup(contenttype);
    
                if(!current_form->contenttype)
                  return_value = CURL_FORMADD_MEMORY;
                else
                  current_form->contenttype_alloc = TRUE;
              }
    
              else
                return_value = CURL_FORMADD_NULL;
            }
    
        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;
    
            const char *filename = array_state?array_value:
    
              va_arg(params, char *);
            if( current_form->showfilename )
    
              return_value = CURL_FORMADD_OPTION_TWICE;
    
              current_form->showfilename = strdup(filename);
    
              if(!current_form->showfilename)
                return_value = CURL_FORMADD_MEMORY;
              else
                current_form->showfilename_alloc = TRUE;
            }
    
          return_value = CURL_FORMADD_UNKNOWN_OPTION;
    
      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 */
    
        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->contenttype ) {
              /* our contenttype is missing */
              form->contenttype
                = strdup(ContentTypeForFilename(form->value, prevtype));
    
              if(!form->contenttype) {
                return_value = CURL_FORMADD_MEMORY;
                break;
              }
              form->contenttype_alloc = TRUE;
    
            if( !(form->flags & HTTPPOST_PTRNAME) &&
    
                 (form == first_form) ) {
    
              /* Note that there's small risk that form->name is NULL here if the
                 app passed in a bad combo, so we better check for that first. */
              if(form->name)
                /* copy name (without strdup; possibly contains null characters) */
                form->name = memdup(form->name, form->namelength);
    
              form->name_alloc = TRUE;
    
            if( !(form->flags & (HTTPPOST_FILENAME | HTTPPOST_READFILE |
                                 HTTPPOST_PTRCONTENTS | HTTPPOST_PTRBUFFER |
                                 HTTPPOST_CALLBACK)) ) {
    
              /* copy value (without strdup; possibly contains null characters) */
    
              form->value = memdup(form->value, form->contentslength);
    
              form->value_alloc = TRUE;
    
            post = AddHttpPost(form->name, form->namelength,
                               form->value, form->contentslength,
    
                               form->contenttype, form->flags,
    
                               form->contentheader, form->showfilename,
    
                               post, httppost,
                               last_post);
    
              prevtype = form->contenttype;
    
        /* 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);
        }
    
      /* always delete the allocated memory before returning */
      form = first_form;
    
        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,
    
    Daniel Stenberg's avatar
    Daniel Stenberg committed
    {
    
      struct FormData *newform = malloc(sizeof(struct FormData));
    
    Daniel Stenberg's avatar
    Daniel Stenberg committed
      newform->next = NULL;
    
    
      if(type <= FORM_CONTENT) {
        /* we make it easier for plain strings: */
        if(!length)
          length = strlen((char *)line);
    
    Daniel Stenberg's avatar
    Daniel Stenberg committed
    
    
        newform->line = malloc(length+1);
    
        if(!newform->line) {
          free(newform);
          return CURLE_OUT_OF_MEMORY;
        }
        memcpy(newform->line, line, length);
        newform->length = length;
        newform->line[length]=0; /* zero terminate for easier debugging */
    
      else
        /* For callbacks and files we don't have any actual data so we just keep a
           pointer to whatever this points to */
        newform->line = (char *)line;
    
    
    Daniel Stenberg's avatar
    Daniel Stenberg committed
      if(*formp) {
        (*formp)->next = newform;
        *formp = newform;
      }
      else
        *formp = newform;
    
    
        if(type != FORM_FILE)
          /* for static content as well as callback data we add the size given
             as input argument */
    
          *size += length;
        else {
          /* Since this is a file to be uploaded here, add the size of the actual
             file */
          if(!strequal("-", newform->line)) {
    
            if(!stat(newform->line, &file)) {
              *size += file.st_size;
            }
          }
        }
      }
    
    /*
     * AddFormDataf() adds printf()-style formatted data to the formdata chain.
     */
    
    Daniel Stenberg's avatar
    Daniel Stenberg committed
    
    
    static CURLcode AddFormDataf(struct FormData **formp,
    
                                 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);
    
    Daniel Stenberg's avatar
    Daniel Stenberg committed
      va_end(ap);
    
    
      return AddFormData(formp, FORM_DATA, s, 0, size);
    
    /*
     * Curl_formclean() is used from http.c, this cleans a built FormData linked
     * list
    
    void Curl_formclean(struct FormData **form_ptr)
    
    Daniel Stenberg's avatar
    Daniel Stenberg committed
    {
    
    Daniel Stenberg's avatar
    Daniel Stenberg committed
      do {
        next=form->next;  /* the following form line */
    
        if(form->type <= FORM_CONTENT)
          free(form->line); /* free the line */
    
    Daniel Stenberg's avatar
    Daniel Stenberg committed
        free(form);       /* free the struct */
    
      } while((form = next) != NULL); /* continue */
    
    #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 */
    
          rc = Curl_convert_to_network(data, form->line, form->length);
          /* Curl_convert_to_network calls failf if unsuccessful */
    
      } while((form = next) != NULL); /* continue */
    
      return CURLE_OK;
    }
    #endif /* CURL_DOES_CONVERSIONS */
    
    
    /*
     * curl_formget()
     * Serialize a curl_httppost struct.
     * Returns 0 on success.
     */
    int curl_formget(struct curl_httppost *form, void *arg,
                     curl_formget_callback append)
    {
    
    Yang Tse's avatar
    Yang Tse committed
      CURLcode rc;
    
      rc = Curl_getFormData(&data, form, NULL, &size);
    
        return (int)rc;
    
      for (ptr = data; ptr; ptr = ptr->next) {
    
            nread = readfromfile(&temp, buffer, sizeof(buffer));
    
            if((nread == (size_t) -1) || (nread != append(arg, buffer, nread))) {
              if(temp.fp) {