From a2314225e02ea2f3bd49dc8557f2452846e49b19 Mon Sep 17 00:00:00 2001 From: Daniel Stenberg Date: Mon, 31 Mar 2008 10:02:23 +0000 Subject: - Added CURLFORM_STREAM as a supported option to curl_formadd() to allow an application to provide data for a multipart with the read callback. Note that the size needs to be provided with CURLFORM_CONTENTSLENGTH when the stream option is used. This feature is verified by the new test case 554. This feature was sponsored by Xponaut. --- lib/formdata.c | 156 +++++++++++++++++++++++++++++++++++++-------------------- lib/formdata.h | 10 ++-- lib/http.c | 2 + 3 files changed, 111 insertions(+), 57 deletions(-) (limited to 'lib') diff --git a/lib/formdata.c b/lib/formdata.c index d80dc3d14..420f85f17 100644 --- a/lib/formdata.c +++ b/lib/formdata.c @@ -5,7 +5,7 @@ * | (__| |_| | _ <| |___ * \___|\___/|_| \_\_____| * - * Copyright (C) 1998 - 2007, Daniel Stenberg, , et al. + * Copyright (C) 1998 - 2008, Daniel Stenberg, , et al. * * This software is licensed as described in the file COPYING, which * you should have received as part of this distribution. The terms @@ -159,13 +159,13 @@ static size_t readfromfile(struct Form *form, char *buffer, size_t size); * ***************************************************************************/ static struct curl_httppost * -AddHttpPost(char * name, size_t namelength, - char * value, size_t contentslength, - char * buffer, size_t bufferlength, +AddHttpPost(char *name, size_t namelength, + char *value, size_t contentslength, + char *buffer, size_t bufferlength, char *contenttype, long flags, struct curl_slist* contentHeader, - char *showfilename, + char *showfilename, char *userp, struct curl_httppost *parent_post, struct curl_httppost **httppost, struct curl_httppost **last_post) @@ -182,6 +182,7 @@ AddHttpPost(char * name, size_t namelength, post->contenttype = contenttype; post->contentheader = contentHeader; post->showfilename = showfilename; + post->userp = userp, post->flags = flags; } else @@ -597,7 +598,7 @@ CURLFORMcode FormAdd(struct curl_httppost **httppost, } case CURLFORM_BUFFERPTR: - current_form->flags |= HTTPPOST_PTRBUFFER; + current_form->flags |= HTTPPOST_PTRBUFFER; if(current_form->buffer) return_value = CURL_FORMADD_OPTION_TWICE; else { @@ -618,6 +619,25 @@ CURLFORMcode FormAdd(struct curl_httppost **httppost, array_state?(size_t)array_value:(size_t)va_arg(params, long); break; + case CURLFORM_STREAM: + current_form->flags |= HTTPPOST_CALLBACK; + if(current_form->userp) + return_value = CURL_FORMADD_OPTION_TWICE; + else { + char *userp = + array_state?array_value:va_arg(params, char *); + if(userp) { + current_form->userp = userp; + current_form->value = userp; /* this isn't strictly true but we + derive a value from this later on + and we need this non-NULL to be + accepted as a fine form part */ + } + else + return_value = CURL_FORMADD_NULL; + } + break; + case CURLFORM_CONTENTTYPE: { const char *contenttype = @@ -693,18 +713,18 @@ CURLFORMcode FormAdd(struct curl_httppost **httppost, form != NULL; form = form->more) { if( ((!form->name || !form->value) && !post) || - ( (form->contentslength) && - (form->flags & HTTPPOST_FILENAME) ) || - ( (form->flags & HTTPPOST_FILENAME) && - (form->flags & HTTPPOST_PTRCONTENTS) ) || - - ( (!form->buffer) && - (form->flags & HTTPPOST_BUFFER) && - (form->flags & HTTPPOST_PTRBUFFER) ) || - - ( (form->flags & HTTPPOST_READFILE) && - (form->flags & HTTPPOST_PTRCONTENTS) ) - ) { + ( (form->contentslength) && + (form->flags & HTTPPOST_FILENAME) ) || + ( (form->flags & HTTPPOST_FILENAME) && + (form->flags & HTTPPOST_PTRCONTENTS) ) || + + ( (!form->buffer) && + (form->flags & HTTPPOST_BUFFER) && + (form->flags & HTTPPOST_PTRBUFFER) ) || + + ( (form->flags & HTTPPOST_READFILE) && + (form->flags & HTTPPOST_PTRCONTENTS) ) + ) { return_value = CURL_FORMADD_INCOMPLETE; break; } @@ -731,10 +751,9 @@ CURLFORMcode FormAdd(struct curl_httppost **httppost, } form->name_alloc = TRUE; } - if( !(form->flags & HTTPPOST_FILENAME) && - !(form->flags & HTTPPOST_READFILE) && - !(form->flags & HTTPPOST_PTRCONTENTS) && - !(form->flags & HTTPPOST_PTRBUFFER) ) { + if( !(form->flags & (HTTPPOST_FILENAME | HTTPPOST_READFILE | + HTTPPOST_PTRCONTENTS | HTTPPOST_PTRBUFFER | + HTTPPOST_CALLBACK)) ) { /* copy value (without strdup; possibly contains null characters) */ form->value = memdup(form->value, form->contentslength); if(!form->value) { @@ -748,6 +767,7 @@ CURLFORMcode FormAdd(struct curl_httppost **httppost, form->buffer, form->bufferlength, form->contenttype, form->flags, form->contentheader, form->showfilename, + form->userp, post, httppost, last_post); @@ -824,18 +844,25 @@ static CURLcode AddFormData(struct FormData **formp, return CURLE_OUT_OF_MEMORY; newform->next = NULL; - /* we make it easier for plain strings: */ - if(!length) - length = strlen((char *)line); + if(type <= FORM_CONTENT) { + /* we make it easier for plain strings: */ + if(!length) + length = strlen((char *)line); - newform->line = (char *)malloc(length+1); - if(!newform->line) { - free(newform); - return CURLE_OUT_OF_MEMORY; + newform->line = (char *)malloc(length+1); + if(!newform->line) { + free(newform); + return CURLE_OUT_OF_MEMORY; + } + memcpy(newform->line, line, length); + newform->length = length; + newform->line[length]=0; /* zero terminate for easier debugging */ } - memcpy(newform->line, line, length); - newform->length = length; - newform->line[length]=0; /* zero terminate for easier debugging */ + else + /* For callbacks and files we don't have any actual data so we just keep a + pointer to whatever this points to */ + newform->line = (char *)line; + newform->type = type; if(*formp) { @@ -846,7 +873,9 @@ static CURLcode AddFormData(struct FormData **formp, *formp = newform; if(size) { - if((type == FORM_DATA) || (type == FORM_CONTENT)) + if(type != FORM_FILE) + /* for static content as well as callback data we add the size given + as input argument */ *size += length; else { /* Since this is a file to be uploaded here, add the size of the actual @@ -893,7 +922,8 @@ void Curl_formclean(struct FormData **form_ptr) do { next=form->next; /* the following form line */ - free(form->line); /* free the line */ + if(form->type <= FORM_CONTENT) + free(form->line); /* free the line */ free(form); /* free the struct */ } while((form = next) != NULL); /* continue */ @@ -997,7 +1027,8 @@ void curl_formfree(struct curl_httppost *form) if( !(form->flags & HTTPPOST_PTRNAME) && form->name) free(form->name); /* free the name */ - if( !(form->flags & HTTPPOST_PTRCONTENTS) && form->contents) + if( !(form->flags & (HTTPPOST_PTRCONTENTS|HTTPPOST_CALLBACK)) && + form->contents) free(form->contents); /* free the contents */ if(form->contenttype) free(form->contenttype); /* free the content type */ @@ -1188,9 +1219,11 @@ CURLcode Curl_getFormData(struct FormData **finalform, if(result) break; } - else if((post->flags & HTTPPOST_FILENAME) || - (post->flags & HTTPPOST_BUFFER)) { - + else if(post->flags & (HTTPPOST_FILENAME|HTTPPOST_BUFFER| + HTTPPOST_CALLBACK)) { + /* it should be noted that for the HTTPPOST_FILENAME and + HTTPPOST_CALLBACK cases the ->showfilename struct member is always + assigned at this point */ char *filebasename= (!post->showfilename)?strippath(post->contents):NULL; @@ -1312,7 +1345,14 @@ CURLcode Curl_getFormData(struct FormData **finalform, if(result) break; } - + else if(post->flags & HTTPPOST_CALLBACK) { + /* the contents should be read with the callback and the size + is set with the contentslength */ + result = AddFormData(&form, FORM_CALLBACK, post->userp, + post->contentslength, &size); + if(result) + break; + } else { /* include the contents we got */ result = AddFormData(&form, FORM_CONTENT, post->contents, @@ -1380,21 +1420,29 @@ int Curl_FormInit(struct Form *form, struct FormData *formdata ) return 0; } -static size_t readfromfile(struct Form *form, char *buffer, size_t size) +static size_t readfromfile(struct Form *form, char *buffer, + size_t size) { size_t nread; - if(!form->fp) { - /* this file hasn't yet been opened */ - form->fp = fopen(form->data->line, "rb"); /* b is for binary */ - if(!form->fp) - return (size_t)-1; /* failure */ - } - nread = fread(buffer, 1, size, form->fp); + bool callback = (bool)(form->data->type == FORM_CALLBACK); - if(nread != size) { + if(callback) + nread = form->fread_func(buffer, 1, size, form->data->line); + else { + if(!form->fp) { + /* this file hasn't yet been opened */ + form->fp = fopen(form->data->line, "rb"); /* b is for binary */ + if(!form->fp) + return (size_t)-1; /* failure */ + } + nread = fread(buffer, 1, size, form->fp); + } + if(!nread || nread > size) { /* this is the last chunk from the file, move on */ - fclose(form->fp); - form->fp = NULL; + if(!callback) { + fclose(form->fp); + form->fp = NULL; + } form->data = form->data->next; } @@ -1421,7 +1469,8 @@ size_t Curl_FormReader(char *buffer, if(!form->data) return 0; /* nothing, error, empty */ - if(form->data->type == FORM_FILE) { + if((form->data->type == FORM_FILE) || + (form->data->type == FORM_CALLBACK)) { gotsize = readfromfile(form, buffer, wantedsize); if(gotsize) @@ -1449,10 +1498,9 @@ size_t Curl_FormReader(char *buffer, form->data = form->data->next; /* advance */ - } while(form->data && (form->data->type != FORM_FILE)); + } while(form->data && (form->data->type < FORM_CALLBACK)); /* 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) */ + line immediately to avoid returning zero before we've reached the end. */ return gotsize; } diff --git a/lib/formdata.h b/lib/formdata.h index 4ca0f3c5c..04f139322 100644 --- a/lib/formdata.h +++ b/lib/formdata.h @@ -8,7 +8,7 @@ * | (__| |_| | _ <| |___ * \___|\___/|_| \_\_____| * - * Copyright (C) 1998 - 2007, Daniel Stenberg, , et al. + * Copyright (C) 1998 - 2008, Daniel Stenberg, , et al. * * This software is licensed as described in the file COPYING, which * you should have received as part of this distribution. The terms @@ -27,8 +27,10 @@ enum formtype { FORM_DATA, /* form metadata (convert to network encoding if necessary) */ FORM_CONTENT, /* form content (never convert) */ - FORM_FILE /* 'line' points to a file name we should read from - to create the form data (never convert) */ + FORM_CALLBACK, /* 'line' points to the custom pointer we pass to the callback + */ + FORM_FILE /* 'line' points to a file name we should read from + to create the form data (never convert) */ }; /* plain and simple linked list with lines to send */ @@ -44,6 +46,7 @@ struct Form { size_t sent; /* number of bytes of the current line that has already been sent in a previous invoke */ FILE *fp; /* file to read from */ + curl_read_callback fread_func; /* fread callback pointer */ }; /* used by FormAdd for temporary storage */ @@ -62,6 +65,7 @@ typedef struct FormInfo { char *showfilename; /* The file name to show. If not set, the actual file name will be used */ bool showfilename_alloc; + char *userp; /* pointer for the read callback */ struct curl_slist* contentheader; struct FormInfo *more; } FormInfo; diff --git a/lib/http.c b/lib/http.c index a76c16053..356741fe8 100644 --- a/lib/http.c +++ b/lib/http.c @@ -2507,6 +2507,8 @@ CURLcode Curl_http(struct connectdata *conn, bool *done) } /* set the read function to read from the generated form data */ + http->form.fread_func = conn->fread_func; /* get the previously set callback + function pointer */ conn->fread_func = (curl_read_callback)Curl_FormReader; conn->fread_in = &http->form; -- cgit v1.2.3