Skip to content
Snippets Groups Projects
formdata.c 37.2 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
    
    ...
    
      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)) {
    	      fprintf(stderr, "Illegally formatted content-type field!\n");
    
    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 {
        fprintf(stderr, "Illegally formatted input field!\n");
    
    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,
    
                                          char * value,
                                          long contentslength,
    
                                          long flags,
                                          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 = namelength;
    
        post->contents = value;
        post->contentslength = contentslength;
    
        post->contenttype = contenttype;
    
        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 (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)
    {
      char *src = *buffer;
      int length;
      if (buffer_length)
        length = buffer_length;
      else
        length = strlen(*buffer);
      *buffer = (char*)malloc(length);
      if (!*buffer)
        return 1;
      memcpy(*buffer, src, length);
      return 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");
     *
     * name/value pair where only the content pointer is remembered:
     * curl_formadd (&post, &last, CURLFORM_COPYNAME, "name",
     * CURLFORM_PTRCONTENTS, ptr, CURLFORM_CONTENTSLENGTH, 10);
     * (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 0 on success, 1 if the FormInfo allocation fails, 2 if one
     * option is given twice for one Form, 3 if a null pointer was given for
     * a char *, 4 if the allocation of a FormInfo struct failed, 5 if an
     * unknown option was used, 6 if the some FormInfo is not complete (or
     * has an error), 7 if a HttpPost struct cannot be allocated, and 8
     * if some allocation for string copying failed.
    
     *
     ***************************************************************************/
    
    static
    int FormAdd(struct HttpPost **httppost,
                struct HttpPost **last_post,
                va_list params)
    {
    
      FormInfo *first_form_info, *current_form_info, *form_info;
      int return_value = 0;
    
      const char *prevtype = NULL;
      struct HttpPost *post = NULL;
      CURLformoption next_option;
    
    
      first_form_info = (FormInfo *)malloc(sizeof(struct FormInfo));
      if(first_form_info) {
        memset(first_form_info, 0, sizeof(FormInfo));
        current_form_info = first_form_info;
      }
      else
    
      /** TODO: first check whether char * is not NULL
          TODO: transfer.c
      */
      while ( ((next_option = va_arg(params, CURLformoption)) != CURLFORM_END) &&
              (return_value == 0) )
    
          case CURLFORM_PTRNAME:
            current_form_info->flags |= HTTPPOST_PTRNAME; /* fall through */
          case CURLFORM_COPYNAME:
            if (current_form_info->name)
              return_value = 2;
            else {
              if (next_option == CURLFORM_PTRNAME)
                current_form_info->name = va_arg(params, char *);
              else {
    	    char *name = va_arg(params, char *);
    	    if (name)
    	      current_form_info->name = name; /* store for the moment */
    	    else
    	      return_value = 3;
    	  }
    
          case CURLFORM_NAMELENGTH:
            if (current_form_info->namelength)
              return_value = 2;
            else
              current_form_info->namelength = va_arg(params, long);
            break;
    
          case CURLFORM_PTRCONTENTS:
    
            current_form_info->flags |= HTTPPOST_PTRCONTENTS; /* fall through */
          case CURLFORM_COPYCONTENTS:
            if (current_form_info->value)
              return_value = 2;
            else {
              if (next_option == CURLFORM_PTRCONTENTS)
                current_form_info->value = va_arg(params, char *);
              else {
    	    char *value = va_arg(params, char *);
    	    if (value)
    	      current_form_info->value = value; /* store for the moment */
    	    else
    	      return_value = 3;
    	  }
    
          case CURLFORM_CONTENTSLENGTH:
            if (current_form_info->contentslength)
              return_value = 2;
            else
              current_form_info->contentslength = va_arg(params, long);
            break;
          case CURLFORM_FILE: {
    	char *filename = va_arg(params, char *);
    	if (current_form_info->value) {
              if (current_form_info->flags & HTTPPOST_FILENAME) {
    	    if (filename) {
    	      if (!(current_form_info = AddFormInfo (strdup(filename),
    						     NULL, current_form_info)))
    		return_value = 4;
    	    }
    	    else
    	      return_value = 3;
              }
              else
                return_value = 2;
    
    	  if (filename)
    	    current_form_info->value = strdup(filename);
    
    	    return_value = 3;
              current_form_info->flags |= HTTPPOST_FILENAME;
    
          case CURLFORM_CONTENTTYPE: {
    	char *contenttype = va_arg(params, char *);
            if (current_form_info->contenttype) {
              if (current_form_info->flags & HTTPPOST_FILENAME) {
                if (contenttype) {
                  if (!(current_form_info = AddFormInfo (NULL,
                                                         strdup(contenttype),
                                                         current_form_info)))
                    return_value = 4;
                }
    	    else
    	      return_value = 3;
              }
              else
                return_value = 2;
            }
            else {
    	  if (contenttype)
    	    current_form_info->contenttype = strdup(contenttype);
    	  else
    	    return_value = 3;
    	}
    
            fprintf (stderr, "got unknown CURLFORM_OPTION: %d\n", next_option);
            return_value = 5;
    
      /* go through the list, check for copleteness and if everything is
       * alright add the HttpPost item otherwise set return_value accordingly */
      form_info = first_form_info;
      while (form_info != NULL)
      {
        if ( (!first_form_info->name) ||
             (!form_info->value) ||
             ( (!form_info->namelength) &&
               (form_info->flags & HTTPPOST_PTRNAME) ) ||
    	 ( (form_info->contentslength) &&
    	   (form_info->flags & HTTPPOST_FILENAME) ) ||
    	 ( (form_info->flags & HTTPPOST_FILENAME) &&
    	   (form_info->flags & HTTPPOST_PTRCONTENTS) )
             ) {
          return_value = 6;
          break;
        }
        else {
          if ( (form_info->flags & HTTPPOST_FILENAME) &&
    	   (!form_info->contenttype) ) {
    	/* our contenttype is missing */
    	form_info->contenttype
    	  = strdup(ContentTypeForFilename(form_info->value, prevtype));
          }
          if ( !(form_info->flags & HTTPPOST_PTRNAME) &&
    	   (form_info == first_form_info) ) {
    	/* copy name (without strdup; possibly contains null characters) */
    	if (AllocAndCopy(&form_info->name, form_info->namelength)) {
    	  return_value = 8;
    	  break;
    	}
          }
          if ( !(form_info->flags & HTTPPOST_FILENAME) &&
    	   !(form_info->flags & HTTPPOST_PTRCONTENTS) ) {
    	/* copy value (without strdup; possibly contains null characters) */
    	if (AllocAndCopy(&form_info->value, form_info->contentslength)) {
    	  return_value = 8;
    	  break;
    	}
          }
          if ( (post = AddHttpPost (form_info->name, form_info->namelength,
                                    form_info->value, form_info->contentslength,
                                    form_info->contenttype, form_info->flags,
    				post, httppost,
                                    last_post)) == NULL) {
            return_value = 7;
          }
          if (form_info->contenttype)
    	prevtype = form_info->contenttype;
        }
        form_info = form_info->more;
      };
    
      /* and finally delete the allocated memory */
      form_info = first_form_info;
      while (form_info != NULL) {
        FormInfo *delete_form_info;
        
        delete_form_info = form_info;
        form_info = form_info->more;
        free (delete_form_info);
      };
    
      return return_value;
    
    }
    
    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+1);
      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
    {
      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 */
    }
    
    /* external function to free up a whole form post chain */
    void curl_formfree(struct HttpPost *form)
    {
      struct HttpPost *next;
    
    
      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 */
        free(form);       /* free the struct */
    
    Daniel Stenberg's avatar
    Daniel Stenberg committed
    
    
      } while((form=next)); /* continue */
    
    struct FormData *Curl_getFormData(struct HttpPost *post,
                                      int *sizep)
    
    Daniel Stenberg's avatar
    Daniel Stenberg committed
    {
      struct FormData *form = NULL;
      struct FormData *firstform;
    
      struct HttpPost *file;
    
      int size =0;
      char *boundary;
      char *fileboundary=NULL;
    
      if(!post)
        return NULL; /* no input => no output! */
    
    
      boundary = Curl_FormBoundary();
    
    Daniel Stenberg's avatar
    Daniel Stenberg committed
      
      /* Make the first line of the output */
      AddFormDataf(&form,
                   "Content-Type: multipart/form-data;"
                   " boundary=%s\r\n",
                   boundary);
      /* we DO NOT count that line since that'll be part of the header! */
    
      firstform = form;
      
      do {
    
        /* boundary */
        size += AddFormDataf(&form, "\r\n--%s\r\n", boundary);
    
    
        size += AddFormData(&form,
                            "Content-Disposition: form-data; name=\"", 0);
    
        size += AddFormData(&form, post->name, post->namelength);
    
        size += AddFormData(&form, "\"", 0);
    
    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
    
          size += AddFormDataf(&form,
    			   "\r\nContent-Type: multipart/mixed,"
    			   " boundary=%s\r\n",
    			   fileboundary);
        }
    
        file = post;
    
        do {
          if(post->more) {
    	/* if multiple-file */
    	size += AddFormDataf(&form,
    			     "\r\n--%s\r\nContent-Disposition: attachment; filename=\"%s\"",
    			     fileboundary, file->contents);
          }
          else if(post->flags & HTTPPOST_FILENAME) {
    	size += AddFormDataf(&form,
    			     "; filename=\"%s\"",
    			     post->contents);
          }
          
          if(file->contenttype) {
    	/* we have a specified type */
    	size += AddFormDataf(&form,
    			     "\r\nContent-Type: %s",
    			     file->contenttype);
          }
    
    
    #if 0
          /* The header Content-Transfer-Encoding: seems to confuse some receivers
           * (like the built-in PHP engine). While I can't see any reason why it
           * should, I can just as well skip this to the benefit of the users who
           * are using such confused receivers.
           */
          
    
    Daniel Stenberg's avatar
    Daniel Stenberg committed
          if(file->contenttype &&
    	 !strnequal("text/", file->contenttype, 5)) {
    	/* this is not a text content, mention our binary encoding */
    
    	size += AddFormData(&form, "\r\nContent-Transfer-Encoding: binary", 0);
    
    Daniel Stenberg's avatar
    Daniel Stenberg committed
          }
    
    Daniel Stenberg's avatar
    Daniel Stenberg committed
    
    
          size += AddFormData(&form, "\r\n\r\n", 0);
    
    Daniel Stenberg's avatar
    Daniel Stenberg committed
    
    
          if((post->flags & HTTPPOST_FILENAME) ||
    	 (post->flags & HTTPPOST_READFILE)) {
    
    Daniel Stenberg's avatar
    Daniel Stenberg committed
    	/* we should include the contents from the specified file */
    	FILE *fileread;
    	char buffer[1024];
    	int nread;
    
    	fileread = strequal("-", file->contents)?stdin:
              /* binary read for win32 crap */
    
    /*VMS??*/ fopen(file->contents, "rb");  /* ONLY ALLOWS FOR STREAM FILES ON VMS */
    /*VMS?? Stream files are OK, as are FIXED & VAR files WITHOUT implied CC */