Skip to content
Snippets Groups Projects
formdata.c 42 KiB
Newer Older
  • Learn to ignore specific revisions
  • Daniel Stenberg's avatar
    Daniel Stenberg committed
    /*****************************************************************************
     *                                  _   _ ____  _     
     *  Project                     ___| | | |  _ \| |    
     *                             / __| | | | |_) | |    
     *                            | (__| |_| |  _ <| |___ 
     *                             \___|\___/|_| \_\_____|
     *
    
     * Copyright (C) 2001, Daniel Stenberg, <daniel@haxx.se>, et al.
    
    Daniel Stenberg's avatar
    Daniel Stenberg committed
     *
    
    Daniel Stenberg's avatar
    Daniel Stenberg committed
     * In order to be useful for every potential user, curl and libcurl are
     * dual-licensed under the MPL and the MIT/X-derivate licenses.
    
    Daniel Stenberg's avatar
    Daniel Stenberg committed
     *
    
    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 MPL or the MIT/X-derivate
     * licenses. You may pick one of these licenses.
    
    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"
    ...
    
    
      For the old FormParse used by curl_formparse use:
    
      gcc -DHAVE_CONFIG_H -I../ -g -D_OLD_FORM_DEBUG -o formdata -I../include formdata.c strequal.c
    
    Daniel Stenberg's avatar
    Daniel Stenberg committed
    
      run the 'formdata' executable and make sure the output is ok!
    
      try './formdata "name=Daniel" "poo=noo" "foo=bar"' and similarly
    
     */
    
    
    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"
    
    
    /* The last #include file should be: */
    #ifdef MALLOCDEBUG
    #include "memdebug.h"
    #endif
    
    
    Daniel Stenberg's avatar
    Daniel Stenberg committed
    /* Length of the random boundary string. The risk of this being used
       in binary data is very close to zero, 64^32 makes
       6277101735386680763835789423207666416102355444464034512896
       combinations... */
    #define BOUNDARY_LENGTH 32
    
    /* What kind of Content-Type to use on un-specified files with unrecognized
       extensions. */
    #define HTTPPOST_CONTENTTYPE_DEFAULT "text/plain"
    
    /* This is a silly duplicate of the function in main.c to enable this source
       to compile stand-alone for better debugging */
    static void GetStr(char **string,
    
    Daniel Stenberg's avatar
    Daniel Stenberg committed
    {
      if(*string)
        free(*string);
      *string = strdup(value);
    }
    
    /***************************************************************************
     *
     * FormParse()
     *	
     * Reads a 'name=value' paramter and builds the appropriate linked list.
     *
     * Specify files to upload with 'name=@filename'. Supports specified
     * given Content-Type of the files. Such as ';type=<content-type>'.
     *
     * You may specify more than one file for a single name (field). Specify
     * multiple files by writing it like:
     *
     * 'name=@filename,filename2,filename3'
     *
     * If you want content-types specified for each too, write them like:
     *
     * 'name=@filename;type=image/gif,filename2,filename3'
     *
     ***************************************************************************/
    
    #define FORM_FILE_SEPARATOR ','
    #define FORM_TYPE_SEPARATOR ';'
    
    
    Daniel Stenberg's avatar
    Daniel Stenberg committed
    int FormParse(char *input,
    	      struct HttpPost **httppost,
    	      struct HttpPost **last_post)
    {
      /* nextarg MUST be a string in the format 'name=contents' and we'll
         build a linked list with the info */
      char name[256];
    
    Daniel Stenberg's avatar
    Daniel Stenberg committed
      char major[128];
      char minor[128];
      long flags = 0;
      char *contp;
    
    Daniel Stenberg's avatar
    Daniel Stenberg committed
      char *prevtype = NULL;
      char *sep;
      char *sep2;
      struct HttpPost *post;
      struct HttpPost *subpost; /* a sub-node */
      unsigned int i;
    
    
      /* Preallocate contents to the length of input to make sure we don't
         overwrite anything. */
      contents = malloc(strlen(input));
      contents[0] = '\000';
     
      if(1 <= sscanf(input, "%255[^=]=%[^\n]", name, contents)) {
    
    Daniel Stenberg's avatar
    Daniel Stenberg committed
        /* the input was using the correct format */
        contp = contents;
    
        if('@' == contp[0]) {
          /* we use the @-letter to indicate file name(s) */
          
          flags = HTTPPOST_FILENAME;
          contp++;
    
          post=NULL;
    
          do {
    	/* since this was a file, it may have a content-type specifier
    	   at the end too */
    
    	sep=strchr(contp, FORM_TYPE_SEPARATOR);
    	sep2=strchr(contp, FORM_FILE_SEPARATOR);
    
    	/* pick the closest */
    	if(sep2 && (sep2 < sep)) {
    	  sep = sep2;
    
    	  /* no type was specified! */
    	}
    	if(sep) {
    
    	  /* if we got here on a comma, don't do much */
    	  if(FORM_FILE_SEPARATOR != *sep)
    	    type = strstr(sep+1, "type=");
    	  else
    	    type=NULL;
    
    	  *sep=0; /* terminate file name at separator */
    
    	  if(type) {
    	    type += strlen("type=");
    	    
    	    if(2 != sscanf(type, "%127[^/]/%127[^,\n]",
    			   major, minor)) {
    
    Daniel Stenberg's avatar
    Daniel Stenberg committed
    	      return 2; /* illegal content-type syntax! */
    	    }
    	    /* now point beyond the content-type specifier */
    
    	    sep = (char *)type + strlen(major)+strlen(minor)+1;
    
    Daniel Stenberg's avatar
    Daniel Stenberg committed
    
    	    /* find the following comma */
    	    sep=strchr(sep, FORM_FILE_SEPARATOR);
    	  }
    	}
    	else {
    	  type=NULL;
    	  sep=strchr(contp, FORM_FILE_SEPARATOR);
    	}
    	if(sep) {
    	  /* the next file name starts here */
    	  *sep =0;
    	  sep++;
    	}
    	if(!type) {
    	  /*
    	   * 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;
    
    Daniel Stenberg's avatar
    Daniel Stenberg committed
    	  };
    
    Daniel Stenberg's avatar
    Daniel Stenberg committed
    	    {".gif",  "image/gif"},
    	    {".jpg",  "image/jpeg"},
    	    {".jpeg", "image/jpeg"},
    	    {".txt",  "text/plain"},
    	    {".html", "text/plain"}
    	  };
    
    	  if(prevtype)
    	    /* default to the previously set/used! */
    	    type = prevtype;
    	  else
    	    /* It seems RFC1867 defines no Content-Type to default to
    	       text/plain so we don't actually need to set this: */
    	    type = HTTPPOST_CONTENTTYPE_DEFAULT;
    
    	  for(i=0; i<sizeof(ctts)/sizeof(ctts[0]); i++) {
    	    if(strlen(contp) >= strlen(ctts[i].extension)) {
    	      if(strequal(contp +
    			  strlen(contp) - strlen(ctts[i].extension),
    			  ctts[i].extension)) {
    		type = ctts[i].type;
    		break;
    	      }	      
    	    }
    	  }
    	  /* we have a type by now */
    	}
    
    	if(NULL == post) {
    	  /* For the first file name, we allocate and initiate the main list
    	     node */
    
    	  post = (struct HttpPost *)malloc(sizeof(struct HttpPost));
    	  if(post) {
    	    memset(post, 0, sizeof(struct HttpPost));
    	    GetStr(&post->name, name);      /* get the name */
    	    GetStr(&post->contents, contp); /* get the contents */
    
    Daniel Stenberg's avatar
    Daniel Stenberg committed
    	    post->flags = flags;
    	    if(type) {
    
    	      GetStr(&post->contenttype, (char *)type); /* get type */
    
    Daniel Stenberg's avatar
    Daniel Stenberg committed
    	      prevtype=post->contenttype; /* point to the allocated string! */
    	    }
    	    /* make the previous point to this */
    	    if(*last_post)
    	      (*last_post)->next = post;
    	    else
    	      (*httppost) = post;
    
    	    (*last_post) = post;	  
    	  }
    
    	}
    	else {
    	  /* we add a file name to the previously allocated node, known as
                 'post' now */
    	  subpost =(struct HttpPost *)malloc(sizeof(struct HttpPost));
    	  if(subpost) {
    	     memset(subpost, 0, sizeof(struct HttpPost));
    	     GetStr(&subpost->name, name);      /* get the name */
    	     GetStr(&subpost->contents, contp); /* get the contents */
    
                 subpost->contentslength = 0;
    
    Daniel Stenberg's avatar
    Daniel Stenberg committed
    	     subpost->flags = flags;
    	     if(type) {
    
    	       GetStr(&subpost->contenttype, (char *)type); /* get type */
    	       prevtype=subpost->contenttype; /* point to allocated string! */
    
    Daniel Stenberg's avatar
    Daniel Stenberg committed
    	     }
    	     /* now, point our 'more' to the original 'more' */
    	     subpost->more = post->more;
    
    	     /* then move the original 'more' to point to ourselves */
    	     post->more = subpost;	     
    	  }
    	}
    	contp = sep; /* move the contents pointer to after the separator */
          } while(sep && *sep); /* loop if there's another file name */
        }
        else {
          post = (struct HttpPost *)malloc(sizeof(struct HttpPost));
          if(post) {
    	memset(post, 0, sizeof(struct HttpPost));
    	GetStr(&post->name, name);      /* get the name */
    
    	if( contp[0]=='<' ) {
    	  GetStr(&post->contents, contp+1); /* get the contents */
    
    	  post->flags = HTTPPOST_READFILE;
    	}
    	else {
    	  GetStr(&post->contents, contp); /* get the contents */
    
    	  post->flags = 0;
    	}
    
    Daniel Stenberg's avatar
    Daniel Stenberg committed
    
    	/* make the previous point to this */
    	if(*last_post)
    	  (*last_post)->next = post;
    	else
    	  (*httppost) = post;
    
    	(*last_post) = post;	  
          }
    
        }
    
      }
      else {
    
    Daniel Stenberg's avatar
    Daniel Stenberg committed
        return 1;
      }
    
    Daniel Stenberg's avatar
    Daniel Stenberg committed
      return 0;
    }
    
    
    int curl_formparse(char *input,
                       struct HttpPost **httppost,
                       struct HttpPost **last_post)
    {
      return FormParse(input, httppost, last_post);
    }
    
    
    /***************************************************************************
     *
     * 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.
    
     *
     ***************************************************************************/
    
    static struct HttpPost * AddHttpPost(char * name,
                                         long namelength,
                                         char * value,
                                         long contentslength,
                                         char *contenttype,
                                         long flags,
                                         struct curl_slist* contentHeader,
                                         struct HttpPost *parent_post,
                                         struct HttpPost **httppost,
                                         struct HttpPost **last_post)
    
    {
      struct HttpPost *post;
      post = (struct HttpPost *)malloc(sizeof(struct HttpPost));
      if(post) {
        memset(post, 0, sizeof(struct HttpPost));
        post->name = name;
    
        post->namelength = name?(namelength?namelength:(long)strlen(name)):0;
    
        post->contents = value;
        post->contentslength = contentslength;
    
        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/plain"}
      };
      
      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;
    }
    
    /***************************************************************************
     *
     * AllocAndCopy()
     *	
     * Copies the data currently available under *buffer using newly allocated
     * buffer (that becomes *buffer). Uses buffer_length if not null, else
     * uses strlen to determine the length of the buffer to be copied
     *
     * Returns 0 on success and 1 if the malloc failed.
     *
     ***************************************************************************/
    static int AllocAndCopy (char **buffer, int buffer_length)
    {
    
      const char *src = *buffer;
      int length, add = 0;
    
      if (buffer_length)
        length = buffer_length;
    
        add = 1;
      }
      *buffer = (char*)malloc(length+add);
    
      if (!*buffer)
        return 1;
      memcpy(*buffer, src, length);
    
      /* if length unknown do null termination */
      if (add)
        (*buffer)[length] = '\0';
    
    /***************************************************************************
     *
     * FormAdd()
     *	
     * Stores a 'name=value' 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);
     *
    
     * Returns:
     * FORMADD_OK             on success
     * FORMADD_MEMORY         if the FormInfo allocation fails
     * FORMADD_OPTION_TWICE   if one option is given twice for one Form
     * FORMADD_NULL           if a null pointer was given for a char
     * FORMADD_MEMORY         if the allocation of a FormInfo struct failed
     * FORMADD_UNKNOWN_OPTION if an unknown option was used
     * FORMADD_INCOMPLETE     if the some FormInfo is not complete (or an error)
     * FORMADD_MEMORY         if a HttpPost struct cannot be allocated
     * FORMADD_MEMORY         if some allocation for string copying failed.
     * FORMADD_ILLEGAL_ARRAY  if an illegal option is used in an array
    
     *
     ***************************************************************************/
    
    
    typedef enum {
      FORMADD_OK, /* first, no error */
    
      FORMADD_MEMORY,
      FORMADD_OPTION_TWICE,
      FORMADD_NULL,
      FORMADD_UNKNOWN_OPTION,
      FORMADD_INCOMPLETE,
      FORMADD_ILLEGAL_ARRAY,
    
      FORMADD_LAST /* last */
    } FORMcode;
    
    
    FORMcode FormAdd(struct HttpPost **httppost,
                     struct HttpPost **last_post,
                     va_list params)
    
      FormInfo *first_form, *current_form, *form;
      FORMcode return_value = FORMADD_OK;
    
      const char *prevtype = NULL;
      struct HttpPost *post = NULL;
    
      CURLformoption option;
      struct curl_forms *forms = NULL;
    
      const char *array_value; /* 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 *)malloc(sizeof(struct FormInfo));
      if(first_form) {
        memset(first_form, 0, sizeof(FormInfo));
        current_form = first_form;
    
        return FORMADD_MEMORY;
    
      /*
       * Loop through all the options set.
       */
      while (1) {
    
        /* break if we have an error to report */
        if (return_value != FORMADD_OK)
          break;
    
        /* first see if we have more parts of the array param */
        if ( array_state ) {
          /* get the upcoming option from the given array */
    
          option = forms->option;
          array_value = forms->value;
    
          forms++; /* advance this to next entry */
    
          if (CURLFORM_END == option) {
            /* end of array state */
            array_state = FALSE;
            continue;
          }
          else {
            /* check that the option is OK in an array */
    
            /* Daniel's note: do we really need to do this? */
            if ( (option <= CURLFORM_ARRAY_START) ||
                 (option >= CURLFORM_ARRAY_END) ) {
              return_value = FORMADD_ILLEGAL_ARRAY;
              break;
    
          }
        }
        else {
          /* This is not array-state, get next option */
          option = va_arg(params, CURLformoption);
          if (CURLFORM_END == option)
    
        }
    
        switch (option) {
        case CURLFORM_ARRAY:
          forms = va_arg(params, struct curl_forms *);
    
            array_state = TRUE;
          else
            return_value = FORMADD_NULL;
          break;
    
          /*
           * Set the Name property.
           */
        case CURLFORM_PTRNAME:
          current_form->flags |= HTTPPOST_PTRNAME; /* fall through */
        case CURLFORM_COPYNAME:
          if (current_form->name)
            return_value = FORMADD_OPTION_TWICE;
          else {
            char *name = va_arg(params, char *);
            if (name)
              current_form->name = name; /* store for the moment */
    
              return_value = FORMADD_NULL;
          }
          break;
        case CURLFORM_NAMELENGTH:
          if (current_form->namelength)
            return_value = FORMADD_OPTION_TWICE;
          else
            current_form->namelength = 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 = FORMADD_OPTION_TWICE;
          else {
            char *value = va_arg(params, char *);
            if (value)
              current_form->value = value; /* store for the moment */
            else
              return_value = FORMADD_NULL;
          }
          break;
        case CURLFORM_CONTENTSLENGTH:
          if (current_form->contentslength)
            return_value = FORMADD_OPTION_TWICE;
          else
            current_form->contentslength = va_arg(params, long);
          break;
    
          /* Get contents from a given file name */
        case CURLFORM_FILECONTENT:
          if (current_form->flags != 0)
            return_value = FORMADD_OPTION_TWICE;
          else {
            char *filename = va_arg(params, char *);
            if (filename) {
              current_form->value = strdup(filename);
              current_form->flags |= HTTPPOST_READFILE;
    
              return_value = FORMADD_NULL;
          }
          break;
    
          /* We upload a file */
        case CURLFORM_FILE:
          {
            const char *filename = NULL;
            if (array_state)
    
            else
              filename = va_arg(params, const char *);
            if (current_form->value) {
              if (current_form->flags & HTTPPOST_FILENAME) {
                if (filename) {
                  if (!(current_form = AddFormInfo(strdup(filename),
                                                   NULL, current_form)))
                    return_value = FORMADD_MEMORY;
                }
                else
                  return_value = FORMADD_NULL;
    
                return_value = FORMADD_OPTION_TWICE;
    
              if (filename)
                current_form->value = strdup(filename);
              else
                return_value = FORMADD_NULL;
              current_form->flags |= HTTPPOST_FILENAME;
    
        case CURLFORM_CONTENTTYPE:
          {
    	const char *contenttype = NULL;
            if (array_state)
    
            else
              contenttype = va_arg(params, const char *);
            if (current_form->contenttype) {
              if (current_form->flags & HTTPPOST_FILENAME) {
    
                  if (!(current_form = AddFormInfo(NULL,
                                                   strdup(contenttype),
                                                   current_form)))
                    return_value = FORMADD_MEMORY;
    
    	      return_value = FORMADD_NULL;
    
                return_value = FORMADD_OPTION_TWICE;
    
    	    current_form->contenttype = strdup(contenttype);
    
    	    return_value = FORMADD_NULL;
    
        case CURLFORM_CONTENTHEADER:
          {
            struct curl_slist* list = NULL;
            if( array_state )
              list = (struct curl_slist*)array_value;
            else
              list = va_arg(params,struct curl_slist*);
            
            if( current_form->contentheader )
              return_value = FORMADD_OPTION_TWICE;
            else
              current_form->contentheader = list;
            
            break;
          }
    
        default:
          return_value = FORMADD_UNKNOWN_OPTION;
    
      }
    
      if(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->flags & HTTPPOST_READFILE) &&
                 (form->flags & HTTPPOST_PTRCONTENTS) )
               ) {
            return_value = FORMADD_INCOMPLETE;
            break;
    
          else {
            if ( (form->flags & HTTPPOST_FILENAME) &&
                 !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) */
              if (AllocAndCopy(&form->name, form->namelength)) {
                return_value = FORMADD_MEMORY;
                break;
              }
            }
            if ( !(form->flags & HTTPPOST_FILENAME) &&
                 !(form->flags & HTTPPOST_READFILE) && 
                 !(form->flags & HTTPPOST_PTRCONTENTS) ) {
              /* copy value (without strdup; possibly contains null characters) */
              if (AllocAndCopy(&form->value, form->contentslength)) {
                return_value = FORMADD_MEMORY;
                break;
              }
            }
    
            post = AddHttpPost(form->name, form->namelength,
                               form->value, form->contentslength,
                               form->contenttype, form->flags,
                               form->contentheader,
                               post, httppost,
                               last_post);
            
            if(!post)
    
              return_value = FORMADD_MEMORY;
    
            if (form->contenttype)
              prevtype = form->contenttype;
    
      /* 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);
      }
    
    }
    
    int curl_formadd(struct HttpPost **httppost,
                     struct HttpPost **last_post,
                     ...)
    {
      va_list arg;
      int result;
      va_start(arg, last_post);
      result = FormAdd(httppost, last_post, arg);
      va_end(arg);
      return result;
    }
    
    
    Daniel Stenberg's avatar
    Daniel Stenberg committed
    static int AddFormData(struct FormData **formp,
    
    Daniel Stenberg's avatar
    Daniel Stenberg committed
    {
      struct FormData *newform = (struct FormData *)
        malloc(sizeof(struct FormData));
      newform->next = NULL;
    
      /* we make it easier for plain strings: */
      if(!length)
        length = strlen((char *)line);
    
      newform->line = (char *)malloc(length+1);
    
      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;
    
      return length;
    }
    
    
    static int AddFormDataf(struct FormData **formp,
    
    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);
    }
    
    
    
    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 */
      int i;
    
      static char table64[]=
        "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/";
    
      retstring = (char *)malloc(BOUNDARY_LENGTH);
    
      if(!retstring)
        return NULL; /* failed */
    
      srand(time(NULL)+randomizer++); /* seed */
    
      strcpy(retstring, "curl"); /* bonus commercials 8*) */
    
      for(i=4; i<(BOUNDARY_LENGTH-1); i++) {
        retstring[i] = table64[rand()%64];
      }
      retstring[BOUNDARY_LENGTH-1]=0; /* zero terminate */
    
      return retstring;
    }
    
    
    /* Used from http.c, this cleans a built FormData linked list */ 
    void Curl_formclean(struct FormData *form)
    
    Daniel Stenberg's avatar
    Daniel Stenberg committed
    {