aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorDaniel Stenberg <daniel@haxx.se>2001-08-21 13:18:07 +0000
committerDaniel Stenberg <daniel@haxx.se>2001-08-21 13:18:07 +0000
commit08655d8d5d0ea980227096366c231693198e61d6 (patch)
tree580291c13caa05196681b328a70f4658a91eecfc
parent3e5dbac7a2f13de110a5c9244d89901e0a755652 (diff)
Georg Huettenegger's patch curl-7.8.1-pre5-patch-20010819
-rw-r--r--docs/Makefile.am4
-rw-r--r--docs/curl_easy_setopt.32
-rw-r--r--docs/curl_formadd.3114
-rw-r--r--docs/curl_formfree.38
-rw-r--r--docs/curl_formparse.34
-rw-r--r--docs/curl_slist_append.32
-rw-r--r--docs/examples/Makefile.am2
-rw-r--r--docs/libcurl.37
-rw-r--r--include/curl/curl.h37
-rw-r--r--lib/escape.c4
-rw-r--r--lib/escape.h4
-rw-r--r--lib/formdata.c467
-rw-r--r--lib/formdata.h7
-rw-r--r--lib/http.c33
-rw-r--r--lib/transfer.c25
-rw-r--r--lib/urldata.h2
-rw-r--r--src/version.h2
-rwxr-xr-xtests/runtests.pl5
18 files changed, 701 insertions, 28 deletions
diff --git a/docs/Makefile.am b/docs/Makefile.am
index afd79abf8..7520a5c76 100644
--- a/docs/Makefile.am
+++ b/docs/Makefile.am
@@ -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 >$@
diff --git a/docs/curl_easy_setopt.3 b/docs/curl_easy_setopt.3
index 6665c9c0f..1a21a3cc4 100644
--- a/docs/curl_easy_setopt.3
+++ b/docs/curl_easy_setopt.3
@@ -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
index 000000000..d16328817
--- /dev/null
+++ b/docs/curl_formadd.3
@@ -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!
+
diff --git a/docs/curl_formfree.3 b/docs/curl_formfree.3
index 38c53681e..3bf3f999b 100644
--- a/docs/curl_formfree.3
+++ b/docs/curl_formfree.3
@@ -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.
diff --git a/docs/curl_formparse.3 b/docs/curl_formparse.3
index 6f0286adf..46d48e1fc 100644
--- a/docs/curl_formparse.3
+++ b/docs/curl_formparse.3
@@ -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!
diff --git a/docs/curl_slist_append.3 b/docs/curl_slist_append.3
index b8a02cfed..4737b989b 100644
--- a/docs/curl_slist_append.3
+++ b/docs/curl_slist_append.3
@@ -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
diff --git a/docs/examples/Makefile.am b/docs/examples/Makefile.am
index eca6447a1..909c76100 100644
--- a/docs/examples/Makefile.am
+++ b/docs/examples/Makefile.am
@@ -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
diff --git a/docs/libcurl.3 b/docs/libcurl.3
index 445ee73eb..94de31d3e 100644
--- a/docs/libcurl.3
+++ b/docs/libcurl.3
@@ -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
diff --git a/include/curl/curl.h b/include/curl/curl.h
index 2f64a79df..0c92d7395 100644
--- a/include/curl/curl.h
+++ b/include/curl/curl.h
@@ -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 {
diff --git a/lib/escape.c b/lib/escape.c
index 167129df6..ab355bf0f 100644
--- a/lib/escape.c
+++ b/lib/escape.c
@@ -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);
diff --git a/lib/escape.h b/lib/escape.h
index 1ec5c7ddd..cda6a65a2 100644
--- a/lib/escape.h
+++ b/lib/escape.h
@@ -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
diff --git a/lib/formdata.c b/lib/formdata.c
index e195e0ba2..9733e3d0f 100644
--- a/lib/formdata.c
+++ b/lib/formdata.c
@@ -24,7 +24,46 @@
/*
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!
@@ -64,7 +103,7 @@
/* 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 */
@@ -571,6 +872,52 @@ int Curl_FormReader(char *buffer,
{
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;
int gotsize;
form=(struct Form *)mydata;
@@ -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)
{
diff --git a/lib/formdata.h b/lib/formdata.h
index 35a5ca7a8..8623c89e5 100644
--- a/lib/formdata.h
+++ b/lib/formdata.h
@@ -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
+
diff --git a/lib/http.c b/lib/http.c
index 5dd1e49d8..df5e58557 100644
--- a/lib/http.c
+++ b/lib/http.c
@@ -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;
diff --git a/lib/transfer.c b/lib/transfer.c
index bef206436..b4ea0b23f 100644
--- a/lib/transfer.c
+++ b/lib/transfer.c
@@ -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! */
diff --git a/lib/urldata.h b/lib/urldata.h
index a050d020f..342e143df 100644
--- a/lib/urldata.h
+++ b/lib/urldata.h
@@ -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 */
};
/*
diff --git a/src/version.h b/src/version.h
index 54e9a2856..6e8594a98 100644
--- a/src/version.h
+++ b/src/version.h
@@ -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 ") "
diff --git a/tests/runtests.pl b/tests/runtests.pl
index 2c9a4bfc7..cc10dd670 100755
--- a/tests/runtests.pl
+++ b/tests/runtests.pl
@@ -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;