Georg Huettenegger's patch curl-7.8.1-pre5-patch-20010819
authorDaniel Stenberg <daniel@haxx.se>
Tue, 21 Aug 2001 13:18:07 +0000 (13:18 +0000)
committerDaniel Stenberg <daniel@haxx.se>
Tue, 21 Aug 2001 13:18:07 +0000 (13:18 +0000)
18 files changed:
docs/Makefile.am
docs/curl_easy_setopt.3
docs/curl_formadd.3 [new file with mode: 0644]
docs/curl_formfree.3
docs/curl_formparse.3
docs/curl_slist_append.3
docs/examples/Makefile.am
docs/libcurl.3
include/curl/curl.h
lib/escape.c
lib/escape.h
lib/formdata.c
lib/formdata.h
lib/http.c
lib/transfer.c
lib/urldata.h
src/version.h
tests/runtests.pl

index afd79abf8620ac4743316617ad5d2c81284cf80e..7520a5c7612bb077fce2c0f2c7944b86fdde8ccd 100644 (file)
@@ -13,6 +13,7 @@ man_MANS = \
        curl_easy_perform.3 \
        curl_easy_setopt.3 \
        curl_formparse.3 \
+       curl_formadd.3 \
        curl_formfree.3 \
        curl_getdate.3 \
        curl_getenv.3 \
@@ -38,6 +39,7 @@ HTMLPAGES = \
        curl_easy_init.html \
        curl_easy_perform.html \
        curl_easy_setopt.html \
+       curl_formadd.html \
        curl_formparse.html \
        curl_formfree.html \
        curl_getdate.html \
@@ -56,7 +58,7 @@ HTMLPAGES = \
 
 EXTRA_DIST = $(man_MANS) \
        MANUAL BUGS CONTRIBUTE FAQ FEATURES INTERNALS \
-       LIBCURL README.win32 RESOURCES TODO TheArtOfHttpScripting THANKS \
+       README.win32 RESOURCES TODO TheArtOfHttpScripting THANKS \
        $(HTMLPAGES)
 
 MAN2HTML= gnroff -man $< | man2html >$@
index 6665c9c0f867aa6dd1d2b4ca9da90c64d2c0816b..1a21a3cc45b26c6f1af3337a2f6cc3f5a1d4d37a 100644 (file)
@@ -275,7 +275,7 @@ instruct what data to pass on to the server.  Pass a pointer to a linked list
 of HTTP post structs as parameter.  The linked list should be a fully valid
 list of 'struct HttpPost' structs properly filled in. The best and most
 elegant way to do this, is to use
-.I curl_formparse(3)
+.I curl_formadd(3)
 as documented. The data in this list must remained intact until you close this
 curl handle again with curl_easy_cleanup().
 .TP
diff --git a/docs/curl_formadd.3 b/docs/curl_formadd.3
new file mode 100644 (file)
index 0000000..d163288
--- /dev/null
@@ -0,0 +1,114 @@
+.\" You can view this file with:
+.\" nroff -man [file]
+.\" $Id$
+.\"
+.TH curl_formadd 3 "19 August 2001" "libcurl 7.9" "libcurl Manual"
+.SH NAME
+curl_formadd - add a section to a multipart/formdata HTTP POST
+.SH SYNOPSIS
+.B #include <curl/curl.h>
+.sp
+.BI "CURLcode curl_formadd(struct HttpPost ** " firstitem,
+.BI "struct HttpPost ** " lastitem, " ...);"
+.ad
+.SH DESCRIPTION
+curl_formadd() is used to append sections when building a multipart/formdata
+HTTP POST (sometimes refered to as rfc1867-style posts). Append one section at
+a time until you've added all the sections you want included and then you pass
+the \fIfirstitem\fP pointer as parameter to \fBCURLOPT_HTTPPOST\fP.
+\fIlastitem\fP is set after each call and on repeated invokes it should be
+left as set to allow repeated invokes to find the end of the list in a faster
+way.
+
+After \fIlastitem\fP follow the real arguments that constitute the
+new section (if the following description confuses you jump directly
+to the examples):
+
+The first is always CURLFORM_COPYNAME followed by a string used for
+the name of the section.
+
+Afterwards one may use one of three arguments: CURLFORM_COPYCONTENTS,
+CURLFORM_PTRCONTENTS, or CURLFORM_FILE. followed by a char or void
+pointer (allowed for PTRCONTENTS).
+
+The next argument may be CURLFORM_CONTENTTYPE if the
+user wishes to specify one (for FILE if no type is given the library
+tries to provide the correct one; for CONTENTS no Content-Type is sent
+in this case)
+
+For CURLFORM_PTRCONTENTS the user may also add CURLFORM_CONTENTSLENGTH
+followed by the length as a long (if not given the library will use
+strlen to determine the length; for COPYCONTENTS this is always done).
+
+For CURLFORM_FILE the user may send multiple files in one section by
+providing multiple CURLFORM_FILE arguments each followed by the filename
+(and each FILE is allowed to have a CONTENTTYPE).
+
+The last argument always is CURLFORM_END.
+
+The pointers \fI*firstitem\fP and \fI*lastitem\fP should both be pointing to
+NULL in the first call to this function. All list-data will be allocated by
+the function itself. You must call \fIcurl_formfree\fP after the form post has
+been done to free the resources again.
+
+This function will copy all input data except the data pointed to by
+the argument after CURLFORM_PTRCONTENTS and keep its own
+version of it allocated until you call \fIcurl_formfree\fP. When
+you've passed the pointer to \fIcurl_easy_setopt\fP, you must not free
+the list until after you've called \fIcurl_easy_cleanup\fP for the
+curl handle. If you provide a pointer as an argument after
+CURLFORM_PTRCONTENTS you must ensure that the pointer stays valid
+until you call \fIcurl_form_free\fP and \fIcurl_easy_cleanup\fP.
+
+See example below.
+.SH RETURN VALUE
+Returns non-zero if an error occurs.
+.SH EXAMPLE
+.nf
+
+ HttpPost* post = NULL;
+ HttpPost* last = NULL;
+ char buffer[] = "test buffer";
+ char htmlbuffer[] = "<HTML>test buffer</HTML>";
+ long htmlbufferlength = strlen(htmlbuffer);
+ /* add null character into htmlbuffer, to demonstrate that
+    transfers of buffers containing null characters actually work
+ */
+ htmlbuffer[8] = '\\0';
+
+ /* Add simple name/content section */
+ curl_formadd(&post, &last, CURLFORM_COPYNAME, "name",
+              CURLFORM_COPYCONTENTS, "content", CURLFORM_END); 
+ /* Add simple name/content/contenttype section */
+ curl_formadd(&post, &last, CURLFORM_COPYNAME, "htmlcode",
+              CURLFORM_COPYCONTENTS, "<HTML></HTML>",
+              CURLFORM_CONTENTTYPE, "text/html", CURLFORM_END);
+ /* Add name/ptrcontent section */
+ curl_formadd(&post, &past, CURLFORM_COPYNAME, "name_for_ptrcontent",
+              CURLFORM_PTRCONTENTS, buffer, CURLFORM_END);
+ /* Add name/ptrcontent/contenttype section */
+ curl_formadd(&post, &last, CURLFORM_COPYNAME, "html_code_with_hole",
+              CURLFORM_PTRCONTENTS, htmlbuffer,
+              CURLFORM_CONTENTSLENGTH, htmlbufferlength,
+              CURLFORM_CONTENTTYPE, "text/html", CURLFORM_END);
+ /* Add simple file section */
+ curl_formadd(&post, &last, CURLFORM_COPYNAME, "picture",
+              CURLFORM_FILE, "my-face.jpg", CURLFORM_END);
+ /* Add file/contenttype section */
+ curl_formadd(&post, &last, CURLFORM_COPYNAME, "picture",
+              CURLFORM_FILE, "my-face.jpg",
+              CURLFORM_CONTENTTYPE, "image/jpeg", CURLFORM_END);
+ /* Add two file section */
+ curl_formadd(&post, &last, CURLFORM_COPYNAME, "pictures",
+              CURLFORM_FILE, "my-face.jpg",
+              CURLFORM_FILE, "your-face.jpg", CURLFORM_END);
+ /* Set the form info */
+ curl_easy_setopt(curl, CURLOPT_HTTPPOST, post);
+
+.SH "SEE ALSO"
+.BR curl_easy_setopt "(3), "
+.BR curl_formparse "(3) [deprecated], "
+.BR curl_formfree "(3)
+.SH BUGS
+Surely there are some, you tell me!
+
index 38c53681e86f41328e775ad77af2dbef90a97b35..3bf3f999be701a9d19a236d2b5f517e37405d6ae 100644 (file)
@@ -12,12 +12,14 @@ curl_formfree - free a previously build multipart/formdata HTTP POST chain
 .ad
 .SH DESCRIPTION
 curl_formfree() is used to clean up data previously built/appended with
-curl_formparse(). This must be called when the data has been used, which
-typically means after the curl_easy_perform() has been called.
+curl_formadd()/curl_formparse(). This must be called when the data has
+been used, which typically means after the curl_easy_perform() has
+been called.
 .SH RETURN VALUE
 None
 .SH "SEE ALSO"
-.BR curl_formparse "(3) "
+.BR curl_formparse "(3) [deprecated], "
+.BR curl_formadd "(3) "
 .SH BUGS
 libcurl 7.7.1 and earlier versions does not allow a NULL pointer to be used as
 argument.
index 6f0286adf236751ff3bca3be1b8c6a0fc6583199..46d48e1fcd50516af24434a66ca8d4bfe3d40bd9 100644 (file)
@@ -4,7 +4,8 @@
 .\"
 .TH curl_formparse 3 "21 May 2001" "libcurl 7.7.4" "libcurl Manual"
 .SH NAME
-curl_formparse - add a section to a multipart/formdata HTTP POST
+curl_formparse - add a section to a multipart/formdata HTTP POST:
+deprecated (use curl_formadd instead)
 .SH SYNOPSIS
 .B #include <curl/curl.h>
 .sp
@@ -79,6 +80,7 @@ Returns non-zero if an error occurs.
 
 .SH "SEE ALSO"
 .BR curl_easy_setopt "(3), "
+.BR curl_formadd "(3), "
 .BR curl_formfree "(3)
 .SH BUGS
 Surely there are some, you tell me!
index b8a02cfed1704d4b2a55873ecfc2935339d72a8f..4737b989b6195a4878d0b17c399a54b2489a7d0c 100644 (file)
@@ -8,7 +8,7 @@ curl_slist_append - add a string to an slist
 .SH SYNOPSIS
 .B #include <curl/curl.h>
 .sp
-.BI "struct curl_slist *curl_slist_append(struct curl_slit *" list,
+.BI "struct curl_slist *curl_slist_append(struct curl_slist *" list,
 .BI "const char * "string ");"
 .ad
 .SH DESCRIPTION
index eca6447a16dc9731cbf5526c613101d959020a77..909c7610009e26e685b0b4fe044b227412aa213c 100644 (file)
@@ -4,7 +4,7 @@
 
 AUTOMAKE_OPTIONS = foreign no-dependencies
 
-EXTRA_DIST = README curlgtk.c sepheaders.c simple.c postit.c \
+EXTRA_DIST = README curlgtk.c sepheaders.c simple.c postit.c postit2.c \
             win32sockets.c persistant.c ftpget.c Makefile.example \
             multithread.c getinmemory.c
 
index 445ee73eb20b200e770eceda151a5705c5138571..94de31d3e00379342f9ec55950c1184497b95d01 100644 (file)
@@ -53,11 +53,14 @@ portable environment variable reader
 .B curl_easy_getinfo()
 get information about a performed transfer
 .TP
-.B curl_formparse()
+.B curl_formadd()
 helps building a HTTP form POST
 .TP
+.B curl_formparse()
+helps building a HTTP form POST (deprecated since 7.9 use curl_formadd()!)
+.TP
 .B curl_formfree()
-free a list built with curl_formparse()
+free a list built with curl_formparse()/curl_formadd()
 .TP
 .B curl_slist_append()
 builds a linked list
index 2f64a79df8249d13264a9324bd3bcb4c49a59560..0c92d73958aaa47f53c34c31003d96c9257e0220 100644 (file)
@@ -59,12 +59,15 @@ struct HttpPost {
   struct HttpPost *next; /* next entry in the list */
   char *name;     /* pointer to allocated name */
   char *contents; /* pointer to allocated data contents */
+  long contentslength; /* length of contents field */
   char *contenttype; /* Content-Type */
   struct HttpPost *more; /* if one field name has more than one file, this
                            link should link to following files */
   long flags;     /* as defined below */
 #define HTTPPOST_FILENAME (1<<0) /* specified content is a file name */
 #define HTTPPOST_READFILE (1<<1) /* specified content is a file name */
+#define HTTPPOST_PTRCONTENTS (1<<2) /* contents is only stored pointer
+                                        do not free in formfree */
 };
 
 typedef int (*curl_progress_callback)(void *clientp,
@@ -483,6 +486,32 @@ int curl_formparse(char *string,
                    struct HttpPost **httppost,
                    struct HttpPost **last_post);
 
+/* name is uppercase CURLFORM_<name> */
+#ifdef CFINIT
+#undef CFINIT
+#endif
+#define CFINIT(name) CURLFORM_ ## name
+
+typedef enum {
+  CFINIT(NOTHING),        /********* the first one is unused ************/
+  
+  /*  */
+  CFINIT(COPYNAME),
+  CFINIT(COPYCONTENTS),
+  CFINIT(PTRCONTENTS),
+  CFINIT(CONTENTSLENGTH),
+  CFINIT(FILE),
+  CFINIT(CONTENTTYPE),
+  CFINIT(END),
+
+  CURLFORM_LASTENTRY /* the last unusued */
+} CURLformoption;
+
+/* new external form function */
+int curl_formadd(struct HttpPost **httppost,
+                 struct HttpPost **last_post,
+                 ...);
+
 /* cleanup a form: */
 void curl_formfree(struct HttpPost *form);
 
@@ -495,8 +524,8 @@ char *curl_version(void);
 
 /* Escape and unescape URL encoding in strings. The functions return a new
  * allocated string or NULL if an error occurred.  */
-char *curl_escape(char *string, int length);
-char *curl_unescape(char *string, int length);
+char *curl_escape(const char *string, int length);
+char *curl_unescape(const char *string, int length);
 
 /* curl_global_init() should be invoked exactly once for each application that
    uses libcurl */
@@ -507,8 +536,8 @@ CURLcode curl_global_init(long flags);
 void curl_global_cleanup(void);
 
 /* This is the version number */
-#define LIBCURL_VERSION "7.8.1"
-#define LIBCURL_VERSION_NUM 0x070801
+#define LIBCURL_VERSION "7.8.2-pre1"
+#define LIBCURL_VERSION_NUM 0x070802
 
 /* linked-list structure for the CURLOPT_QUOTE option (and other) */
 struct curl_slist {
index 167129df6a4d7ab80bbb151287c617fb1dde0e79..ab355bf0f540d9e04a5424afb2041c2b7d1d5039 100644 (file)
@@ -37,7 +37,7 @@
 #include "memdebug.h"
 #endif
 
-char *curl_escape(char *string, int length)
+char *curl_escape(const char *string, int length)
 {
   int alloc = (length?length:(int)strlen(string))+1;  
   char *ns = malloc(alloc);
@@ -75,7 +75,7 @@ char *curl_escape(char *string, int length)
   return ns;
 }
 
-char *curl_unescape(char *string, int length)
+char *curl_unescape(const char *string, int length)
 {
   int alloc = (length?length:(int)strlen(string))+1;
   char *ns = malloc(alloc);
index 1ec5c7ddd1938a37b8a95bd10fb4ad4566ce35a1..cda6a65a21a8474ec3066305bf222c55686bf5a8 100644 (file)
@@ -26,7 +26,7 @@
 /* Escape and unescape URL encoding in strings. The functions return a new
  * allocated string or NULL if an error occurred.  */
 
-char *curl_escape(char *string, int length);
-char *curl_unescape(char *string, int length);
+char *curl_escape(const char *string, int length);
+char *curl_unescape(const char *string, int length);
 
 #endif
index e195e0ba2a57294c51fcc6e39eb792916914a9dc..9733e3d0fe3ce3f318d01788a3a4983fac653a26 100644 (file)
 /*
   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
+  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="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
+
+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
+...
+
+  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
 
   run the 'formdata' executable and make sure the output is ok!
 
 /* 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,
-                  char *value)
+                  const char *value)
 {
   if(*string)
     free(*string);
@@ -227,6 +266,7 @@ int FormParse(char *input,
            memset(post, 0, sizeof(struct HttpPost));
            GetStr(&post->name, name);      /* get the name */
            GetStr(&post->contents, contp); /* get the contents */
+            post->contentslength = 0;
            post->flags = flags;
            if(type) {
              GetStr(&post->contenttype, (char *)type); /* get type */
@@ -250,6 +290,7 @@ int FormParse(char *input,
             memset(subpost, 0, sizeof(struct HttpPost));
             GetStr(&subpost->name, name);      /* get the name */
             GetStr(&subpost->contents, contp); /* get the contents */
+             subpost->contentslength = 0;
             subpost->flags = flags;
             if(type) {
               GetStr(&subpost->contenttype, (char *)type); /* get type */
@@ -272,10 +313,12 @@ int FormParse(char *input,
        GetStr(&post->name, name);      /* get the name */
        if( contp[0]=='<' ) {
          GetStr(&post->contents, contp+1); /* get the contents */
+          post->contentslength = 0;
          post->flags = HTTPPOST_READFILE;
        }
        else {
          GetStr(&post->contents, contp); /* get the contents */
+          post->contentslength = 0;
          post->flags = 0;
        }
 
@@ -307,6 +350,264 @@ int curl_formparse(char *input,
   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 0 on success and 1 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->contents = value;
+    post->contentslength = contentslength;
+    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;
+}
+
+/***************************************************************************
+ *
+ * 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 first option is not CURLFORM_COPYNAME,
+ * 2 if AddHttpPost failes, and 3 if an unknown option is encountered
+ *
+ ***************************************************************************/
+
+static
+int FormAdd(struct HttpPost **httppost,
+            struct HttpPost **last_post,
+            va_list params)
+{
+  int go_on = TRUE;
+  int read_argument = TRUE;
+  unsigned int i;
+  char *name;
+  char *value;
+  const char *prevtype = NULL;
+  struct HttpPost *post = NULL;
+  CURLformoption next_option;
+
+  /* We always expect CURLFORM_COPYNAME first for the moment. */
+  next_option = va_arg(params, CURLformoption);
+  if (next_option != CURLFORM_COPYNAME)
+    return 1;
+
+  name = va_arg(params, char *);
+  do
+  {
+    /* if not already read read next argument */
+    if (read_argument)
+      next_option = va_arg(params, CURLformoption);
+    else
+      read_argument = TRUE;
+      
+    switch (next_option)
+    {
+      case CURLFORM_COPYCONTENTS:
+      { /* simple name/value storage of duplicated data */
+        const char * contenttype = NULL;
+        value = va_arg(params, char *);
+        next_option = va_arg(params, CURLformoption);
+        if (next_option == CURLFORM_CONTENTTYPE)
+          contenttype = va_arg(params, char *);
+        else
+          read_argument = FALSE;
+        if ((post = AddHttpPost(strdup(name), strdup(value), 0, 0, NULL,
+                                httppost, last_post)) == NULL) {
+          return 2;
+        }
+        if (contenttype)
+          post->contenttype = strdup(contenttype);
+        /* at the moment no more options are allowd in this case */
+        go_on = FALSE;
+        break;
+      }
+      case CURLFORM_PTRCONTENTS:
+      { /* name/value storage with value stored as a pointer */
+        const char * contenttype = NULL;
+        void * ptr_contents = va_arg(params, void *);
+        long contentslength;
+        int got_contentslength = FALSE;
+        /* either use provided length or use strlen () to get it */
+        next_option = va_arg(params, CURLformoption);
+        while ( (next_option == CURLFORM_CONTENTSLENGTH) ||
+                (next_option == CURLFORM_CONTENTTYPE) ) {
+          if (next_option == CURLFORM_CONTENTSLENGTH) {
+            contentslength = va_arg(params, long);
+            got_contentslength = TRUE;
+          }
+          else { /* CURLFORM_CONTENTTYPE */
+            contenttype = va_arg(params, char *);
+          }
+          next_option = va_arg(params, CURLformoption);
+        };
+        /* we already read the next CURLformoption */
+        read_argument = FALSE;
+        if (!got_contentslength)
+          /* no length given, use strlen to find out */
+          contentslength = strlen (ptr_contents);
+        if ((post = AddHttpPost(strdup(name), ptr_contents, contentslength,
+                                HTTPPOST_PTRCONTENTS, NULL, httppost,
+                                last_post))
+            == NULL) {
+          return 2;
+        }
+        if (contenttype)
+          post->contenttype = strdup(contenttype);
+        /* at the moment no more options are allowd in this case */
+        go_on = FALSE;
+        break;
+      }
+      case CURLFORM_FILE:
+      {
+        const char * contenttype = NULL;
+        value = va_arg(params, char *);
+        next_option = va_arg(params, CURLformoption);
+        /* if contenttype was provided retrieve it */
+        if (next_option == CURLFORM_CONTENTTYPE) {
+          contenttype = va_arg(params, char *);
+        }
+        else {
+         /*
+          * 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(value) >= strlen(ctts[i].extension)) {
+             if(strequal(value +
+                         strlen(value) - strlen(ctts[i].extension),
+                         ctts[i].extension)) {
+               contenttype = ctts[i].type;
+               break;
+             }       
+           }
+         }
+         /* we have a contenttype by now */
+          /* do not try to read the next option we already did that */
+          read_argument = FALSE;
+        }
+        if ( (post = AddHttpPost (strdup(name), strdup(value), 0,
+                                  HTTPPOST_FILENAME, post, httppost,
+                                  last_post)) == NULL) {
+          return 2;
+        }
+        post->contenttype = strdup (contenttype);
+        prevtype = post->contenttype;
+        /* we do not set go_on to false as multiple files are allowed */
+        break;
+      }
+      case CURLFORM_END:
+        /* this ends our loop */
+        break;
+      default:
+        fprintf (stderr, "got: %d\n", next_option);
+        return 3;
+    };
+
+  } while (go_on && next_option != CURLFORM_END);
+
+  return 0;
+}
+
+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;
+}
+
 static int AddFormData(struct FormData **formp,
                        const void *line,
                        long length)
@@ -406,7 +707,7 @@ void curl_formfree(struct HttpPost *form)
 
     if(form->name)
       free(form->name); /* free the name */
-    if(form->contents)
+    if( !(form->flags & HTTPPOST_PTRCONTENTS) && form->contents)
       free(form->contents); /* free the contents */
     if(form->contenttype)
       free(form->contenttype); /* free the content type */
@@ -525,7 +826,7 @@ struct FormData *Curl_getFormData(struct HttpPost *post,
        }
       } else {
        /* include the contents we got */
-       size += AddFormData(&form, post->contents, 0);
+       size += AddFormData(&form, post->contents, post->contentslength);
       }
     } while((file = file->more)); /* for each specified file for this field */
 
@@ -568,6 +869,52 @@ int Curl_FormReader(char *buffer,
                     size_t size,
                     size_t nitems,
                     FILE *mydata)
+{
+  struct Form *form;
+  int wantedsize;
+  int gotsize = 0;
+
+  form=(struct Form *)mydata;
+
+  wantedsize = size * nitems;
+
+  if(!form->data)
+    return -1; /* nothing, error, empty */
+
+  do {
+  
+    if( (form->data->length - form->sent ) > wantedsize - gotsize) {
+
+      memcpy(buffer + gotsize , form->data->line + form->sent,
+             wantedsize - gotsize);
+
+      form->sent += wantedsize-gotsize;
+
+      return wantedsize;
+    }
+
+    memcpy(buffer+gotsize,
+           form->data->line + form->sent,
+           (form->data->length - form->sent) );
+    gotsize += form->data->length - form->sent;
+    
+    form->sent = 0;
+
+    form->data = form->data->next; /* advance */
+
+  } while(form->data);
+  /* If we got an empty line and we have more data, we proceed to the next
+     line immediately to avoid returning zero before we've reached the end.
+     This is the bug reported November 22 1999 on curl 6.3. (Daniel) */
+
+  return gotsize;
+}
+
+/* possible (old) fread() emulation that copies at most one line */
+int Curl_FormReadOneLine(char *buffer,
+                         size_t size,
+                         size_t nitems,
+                         FILE *mydata)
 {
   struct Form *form;
   int wantedsize;
@@ -609,6 +956,118 @@ int Curl_FormReader(char *buffer,
 
 
 #ifdef _FORM_DEBUG
+int FormAddTest(const char * errormsg,
+                 struct HttpPost **httppost,
+                 struct HttpPost **last_post,
+                 ...)
+{
+  int result;
+  va_list arg;
+  CURLformoption next_option;
+  char * value;
+  va_start(arg, last_post);
+  if ((result = FormAdd(httppost, last_post, arg)))
+    fprintf (stderr, "ERROR doing FormAdd ret: %d action: %s\n", result,
+             errormsg);
+  va_end(arg);
+  return result;
+}
+
+
+int main()
+{
+  char name1[] = "simple_COPYCONTENTS";
+  char name2[] = "COPYCONTENTS_+_CONTENTTYPE";
+  char name3[] = "simple_PTRCONTENTS";
+  char name4[] = "PTRCONTENTS_+_CONTENTSLENGTH";
+  char name5[] = "PTRCONTENTS_+_CONTENTSLENGTH_+_CONTENTTYPE";
+  char name6[] = "FILE1_+_CONTENTTYPE";
+  char name7[] = "FILE1_+_FILE2";
+  char value1[] = "value for simple COPYCONTENTS";
+  char value2[] = "value for COPYCONTENTS + CONTENTTYPE";
+  char value3[] = "value for simple PTRCONTENTS";
+  char value4[] = "value for PTRCONTENTS + CONTENTSLENGTH";
+  char value5[] = "value for PTRCOTNENTS + CONTENTSLENGTH + CONTENTTYPE";
+  char value6[] = "inet_ntoa_r.h";
+  char value7[] = "Makefile.b32.resp";
+  char type2[] = "image/gif";
+  char type5[] = "text/plain";
+  char type6[] = "text/html";
+  int value4length = strlen(value4);
+  int value5length = strlen(value5);
+  int errors = 0;
+  int size;
+  int nread;
+  char buffer[4096];
+  struct HttpPost *httppost=NULL;
+  struct HttpPost *last_post=NULL;
+  struct HttpPost *post;
+
+  struct FormData *form;
+  struct Form formread;
+
+  if (FormAddTest("simple COPYCONTENTS test", &httppost, &last_post,
+                  CURLFORM_COPYNAME, name1, CURLFORM_COPYCONTENTS, value1,
+                  CURLFORM_END))
+    ++errors;
+  if (FormAddTest("COPYCONTENTS  + CONTENTTYPE test", &httppost, &last_post,
+                  CURLFORM_COPYNAME, name2, CURLFORM_COPYCONTENTS, value2,
+                  CURLFORM_CONTENTTYPE, type2, CURLFORM_END))
+    ++errors;
+  if (FormAddTest("simple PTRCONTENTS test", &httppost, &last_post,
+                  CURLFORM_COPYNAME, name3, CURLFORM_PTRCONTENTS, value3,
+                  CURLFORM_END))
+    ++errors;
+  /* make null character at start to check that contentslength works
+     correctly */
+  value4[1] = '\0';
+  if (FormAddTest("PTRCONTENTS + CONTENTSLENGTH test", &httppost, &last_post,
+                  CURLFORM_COPYNAME, name4, CURLFORM_PTRCONTENTS, value4,
+                  CURLFORM_CONTENTSLENGTH, value4length, CURLFORM_END))
+    ++errors;
+  /* make null character at start to check that contentslength works
+     correctly */
+  value5[1] = '\0';
+  if (FormAddTest("PTRCONTENTS + CONTENTSLENGTH + CONTENTTYPE test",
+                  &httppost, &last_post,
+                  CURLFORM_COPYNAME, name5, CURLFORM_PTRCONTENTS, value5,
+                  CURLFORM_CONTENTSLENGTH, value5length,
+                  CURLFORM_CONTENTTYPE, type5, CURLFORM_END))
+    ++errors;
+  if (FormAddTest("FILE + CONTENTTYPE test", &httppost, &last_post,
+                  CURLFORM_COPYNAME, name6, CURLFORM_FILE, value6,
+                  CURLFORM_CONTENTTYPE, type6, CURLFORM_END))
+    ++errors;
+  if (FormAddTest("FILE1 + FILE2 test", &httppost, &last_post,
+                  CURLFORM_COPYNAME, name7, CURLFORM_FILE, value6,
+                  CURLFORM_FILE, value7, CURLFORM_END))
+    ++errors;
+
+  form=Curl_getFormData(httppost, &size);
+
+  Curl_FormInit(&formread, form);
+
+  do {
+    nread = Curl_FormReader(buffer, 1, sizeof(buffer),
+                            (FILE *)&formread);
+
+    if(-1 == nread)
+      break;
+    fwrite(buffer, nread, 1, stderr);
+  } while(1);
+
+  fprintf(stderr, "size: %d\n", size);
+  if (errors)
+    fprintf(stderr, "\n==> %d Test(s) failed!\n", errors);
+  else
+    fprintf(stdout, "\nAll Tests seem to have worked (please check output)\n");
+
+  return 0;
+}
+
+#endif
+
+#ifdef _OLD_FORM_DEBUG
 
 int main(int argc, char **argv)
 {
index 35a5ca7a89a33132de006f9c6cb818bd0f8530c3..8623c89e5ba73227d5f812bfbef08ba46a4a2c44 100644 (file)
@@ -47,8 +47,15 @@ int Curl_FormReader(char *buffer,
                     size_t nitems,
                     FILE *mydata);
 
+/* possible (old) fread() emulation that copies at most one line */
+int Curl_FormReadOneLine(char *buffer,
+                         size_t size,
+                         size_t nitems,
+                         FILE *mydata);
+
 char *Curl_FormBoundary(void);
 
 void Curl_formclean(struct FormData *);
 
 #endif
+
index 5dd1e49d8899f1bd69d2a45e802cf294ad1e617a..df5e58557a03aaad5796b78d79441fa24c292a6f 100644 (file)
@@ -703,6 +703,8 @@ CURLcode Curl_http(struct connectdata *conn)
     }
 
     if(HTTPREQ_POST_FORM == data->httpreq) {
+      char contentType[256];
+      int linelength=0;
       if(Curl_FormInit(&http->form, http->sendit)) {
         failf(data, "Internal HTTP POST error!\n");
         return CURLE_HTTP_POST_ERROR;
@@ -719,15 +721,40 @@ CURLcode Curl_http(struct connectdata *conn)
       add_bufferf(req_buffer,
                   "Content-Length: %d\r\n", http->postsize-2);
 
+      if(!checkheaders(data, "Expect:")) {
+        /* if not disabled explicitly we add a Expect: 100-continue
+           to the headers which actually speeds up post operations (as
+           there is one packet coming back from the web server) */
+        add_bufferf(req_buffer,
+                    "Expect: 100-continue\r\n");
+        data->bits.expect100header = TRUE;
+
+        /* Get Content-Type: line from Curl_FormReadOneLine, which happens
+           to always be the first line. We can know this for sure since
+           we always build the formpost linked list the same way! */
+        linelength = Curl_FormReadOneLine (contentType,
+                                           sizeof(contentType),
+                                           1,
+                                           (FILE *)&http->form);
+        if(linelength == -1) {
+          failf(data, "Could not get Content-Type header line!\n");
+          return CURLE_HTTP_POST_ERROR;
+        }
+        add_buffer(req_buffer, contentType, linelength);
+      }
+
       /* set upload size to the progress meter */
       Curl_pgrsSetUploadSize(data, http->postsize);
 
+      /* fire away the whole request to the server */
       data->request_size = 
         add_buffer_send(conn->firstsocket, conn, req_buffer);
+
+      /* setup variables for the upcoming transfer */
       result = Curl_Transfer(conn, conn->firstsocket, -1, TRUE,
-                        &http->readbytecount,
-                          conn->firstsocket,
-                        &http->writebytecount);
+                             &http->readbytecount,
+                             conn->firstsocket,
+                             &http->writebytecount);
       if(result) {
         Curl_formclean(http->sendit); /* free that whole lot */
         return result;
index bef20643619d61e9f30df5ca3388243f279900be..b4ea0b23fe1810cbf687ab46cb253b8c7e5d03d2 100644 (file)
@@ -197,6 +197,9 @@ Transfer(struct connectdata *c_conn)
                                    Content-Range: header */
   int httpcode = 0;            /* error code from the 'HTTP/1.? XXX' line */
   int httpversion = -1;                        /* the HTTP version*10 */
+  bool write_after_100_header = FALSE;  /* should we enable the write after
+                                           we received a 100-continue/timeout
+                                           or directly */
 
   /* for the low speed checks: */
   CURLcode urg;
@@ -263,8 +266,13 @@ Transfer(struct connectdata *c_conn)
 
     FD_ZERO (&writefd);                /* clear it */
     if(conn->writesockfd != -1) {
-      FD_SET (conn->writesockfd, &writefd); /* write socket */
-      keepon |= KEEP_WRITE;
+      if (data->bits.expect100header)
+        /* wait with write until we either got 100-continue or a timeout */
+        write_after_100_header = TRUE;
+      else {
+        FD_SET (conn->writesockfd, &writefd); /* write socket */
+        keepon |= KEEP_WRITE;
+      }
     }
 
     /* get these in backup variables to be able to restore them on each lap in
@@ -290,6 +298,12 @@ Transfer(struct connectdata *c_conn)
           keepon = 0; /* no more read or write */
        continue;
       case 0:                  /* timeout */
+        if (write_after_100_header) {
+          write_after_100_header = FALSE;
+          FD_SET (conn->writesockfd, &writefd); /* write socket */
+          keepon |= KEEP_WRITE;
+          wkeepfd = writefd;
+        }
        break;
       default:
         if((keepon & KEEP_READ) && FD_ISSET(conn->sockfd, &readfd)) {
@@ -408,6 +422,13 @@ Transfer(struct connectdata *c_conn)
                    */
                   header = TRUE;
                   headerline = 0; /* we restart the header line counter */
+                  /* if we did wait for this do enable write now! */
+                  if (write_after_100_header) {
+                    write_after_100_header = FALSE;
+                    FD_SET (conn->writesockfd, &writefd); /* write socket */
+                    keepon |= KEEP_WRITE;
+                    wkeepfd = writefd;
+                  }
                 }
                 else
                   header = FALSE;      /* no more header to parse! */
index a050d020f3331808cd7cf463ef8294eb00e71afa..342e143df016c5661ef87c1bc7c3452fc86e1fcf 100644 (file)
@@ -413,6 +413,8 @@ struct Configbits {
                             after use */
   bool reuse_fresh;      /* do not re-use an existing connection for this
                             transfer */
+  bool expect100header;  /* TRUE if we added Expect: 100-continue to the
+                            HTTP header */
 };
 
 /*
index 54e9a285636bf8b4914e81e684465e83936fe87d..6e8594a98eb666c34530c82d521dc58040ea8623 100644 (file)
@@ -1,3 +1,3 @@
 #define CURL_NAME "curl"
-#define CURL_VERSION "7.8.1"
+#define CURL_VERSION "7.8.2-pre1"
 #define CURL_ID CURL_NAME " " CURL_VERSION " (" OS ") "
index 2c9a4bfc74afefda0f0d59f291183dc411d48d97..cc10dd6702b97a262e7b33bd9226ecd99bbce6ca 100755 (executable)
@@ -741,6 +741,11 @@ do {
         # verbose output
         $verbose=1;
     }
+    elsif ($ARGV[0] eq "-c") {
+        # use this path to curl instead of default        
+        $CURL=$ARGV[1];
+        shift @ARGV;
+    }
     elsif ($ARGV[0] eq "-d") {
         # have the servers display protocol output 
         $debugprotocol=1;