Commit a2314225 authored by Daniel Stenberg's avatar Daniel Stenberg
Browse files

- 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.
parent 1e482fe6
Loading
Loading
Loading
Loading
+7 −0
Original line number Original line Diff line number Diff line
@@ -6,6 +6,13 @@


                                  Changelog
                                  Changelog


Daniel Stenberg (31 Mar 2008)
- 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.

Daniel Fandrich (30 Mar 2008)
Daniel Fandrich (30 Mar 2008)
- Changed the makefile so the doc/examples/ programs are never built in a
- Changed the makefile so the doc/examples/ programs are never built in a
  normal build/install (only with the 'make check' target), so that a
  normal build/install (only with the 'make check' target), so that a
+2 −2
Original line number Original line Diff line number Diff line
@@ -6,11 +6,11 @@ Curl and libcurl 7.18.2
 Public functions in libcurl:  56
 Public functions in libcurl:  56
 Public web site mirrors:      39
 Public web site mirrors:      39
 Known libcurl bindings:       36
 Known libcurl bindings:       36
 Contributors:                 621
 Contributors:                 636


This release includes the following changes:
This release includes the following changes:
 
 
 o 
 o CURLFORM_STREAM was added


This release includes the following bugfixes:
This release includes the following bugfixes:


+11 −18
Original line number Original line Diff line number Diff line
@@ -38,9 +38,7 @@ parts you want to add to your post.
The options listed first are for making normal parts. The options from
The options listed first are for making normal parts. The options from
\fICURLFORM_FILE\fP through \fICURLFORM_BUFFERLENGTH\fP are for file upload
\fICURLFORM_FILE\fP through \fICURLFORM_BUFFERLENGTH\fP are for file upload
parts.
parts.

.SH OPTIONS
.SH OPTIONS

.IP CURLFORM_COPYNAME
.IP CURLFORM_COPYNAME
followed by a string which provides the \fIname\fP of this part. libcurl
followed by a string which provides the \fIname\fP of this part. libcurl
copies the string so your application doesn't need to keep it around after
copies the string so your application doesn't need to keep it around after
@@ -48,14 +46,12 @@ this function call. If the name isn't NUL-terminated, or if you'd
like it to contain zero bytes, you must set its length with
like it to contain zero bytes, you must set its length with
\fBCURLFORM_NAMELENGTH\fP. The copied data will be freed by
\fBCURLFORM_NAMELENGTH\fP. The copied data will be freed by
\fIcurl_formfree(3)\fP.
\fIcurl_formfree(3)\fP.

.IP CURLFORM_PTRNAME
.IP CURLFORM_PTRNAME
followed by a string which provides the \fIname\fP of this part. libcurl
followed by a string which provides the \fIname\fP of this part. libcurl
will use the pointer and refer to the data in your application, so you
will use the pointer and refer to the data in your application, so you
must make sure it remains until curl no longer needs it. If the name
must make sure it remains until curl no longer needs it. If the name
isn't NUL-terminated, or if you'd like it to contain zero
isn't NUL-terminated, or if you'd like it to contain zero
bytes, you must set its length with \fBCURLFORM_NAMELENGTH\fP.
bytes, you must set its length with \fBCURLFORM_NAMELENGTH\fP.

.IP CURLFORM_COPYCONTENTS
.IP CURLFORM_COPYCONTENTS
followed by a pointer to the contents of this part, the actual data
followed by a pointer to the contents of this part, the actual data
to send away. libcurl copies the provided data, so your application doesn't
to send away. libcurl copies the provided data, so your application doesn't
@@ -63,57 +59,55 @@ need to keep it around after this function call. If the data isn't null
terminated, or if you'd like it to contain zero bytes, you must
terminated, or if you'd like it to contain zero bytes, you must
set the length of the name with \fBCURLFORM_CONTENTSLENGTH\fP. The copied
set the length of the name with \fBCURLFORM_CONTENTSLENGTH\fP. The copied
data will be freed by \fIcurl_formfree(3)\fP.
data will be freed by \fIcurl_formfree(3)\fP.

.IP CURLFORM_PTRCONTENTS
.IP CURLFORM_PTRCONTENTS
followed by a pointer to the contents of this part, the actual data
followed by a pointer to the contents of this part, the actual data
to send away. libcurl will use the pointer and refer to the data in your
to send away. libcurl will use the pointer and refer to the data in your
application, so you must make sure it remains until curl no longer needs it.
application, so you must make sure it remains until curl no longer needs it.
If the data isn't NUL-terminated, or if you'd like it to contain zero bytes,
If the data isn't NUL-terminated, or if you'd like it to contain zero bytes,
you must set its length  with \fBCURLFORM_CONTENTSLENGTH\fP.
you must set its length  with \fBCURLFORM_CONTENTSLENGTH\fP.

.IP CURLFORM_CONTENTSLENGTH
.IP CURLFORM_CONTENTSLENGTH
followed by a long giving the length of the contents.
followed by a long giving the length of the contents. Note that for

\fICURLFORM_STREAM\fP contents, this option is mandatory.
.IP CURLFORM_FILECONTENT
.IP CURLFORM_FILECONTENT
followed by a filename, causes that file to be read and its contents used
followed by a filename, causes that file to be read and its contents used
as data in this part. This part does \fInot\fP automatically become a file
as data in this part. This part does \fInot\fP automatically become a file
upload part simply because its data was read from a file.
upload part simply because its data was read from a file.

.IP CURLFORM_FILE
.IP CURLFORM_FILE
followed by a filename, makes this part a file upload part. It sets the
followed by a filename, makes this part a file upload part. It sets the
\fIfilename\fP field to the basename of the provided filename, it reads the
\fIfilename\fP field to the basename of the provided filename, it reads the
contents of the file and passes them as data and sets the content-type if the
contents of the file and passes them as data and sets the content-type if the
given file match one of the internally known file extensions.  For
given file match one of the internally known file extensions.  For
\fBCURLFORM_FILE\fP the user may send one or more files in one part by
\fBCURLFORM_FILE\fP the user may send one or more files in one part by
providing multiple \fBCURLFORM_FILE\fP arguments each followed by the
providing multiple \fBCURLFORM_FILE\fP arguments each followed by the filename
filename (and each CURLFORM_FILE is allowed to have a CURLFORM_CONTENTTYPE).
(and each \fICURLFORM_FILE\fP is allowed to have a

\fICURLFORM_CONTENTTYPE\fP).
.IP CURLFORM_CONTENTTYPE
.IP CURLFORM_CONTENTTYPE
is used in combination with \fICURLFORM_FILE\fP. Followed by a pointer to a
is used in combination with \fICURLFORM_FILE\fP. Followed by a pointer to a
string which provides the content-type for this part, possibly instead of an
string which provides the content-type for this part, possibly instead of an
internally chosen one.
internally chosen one.

.IP CURLFORM_FILENAME
.IP CURLFORM_FILENAME
is used in combination with \fICURLFORM_FILE\fP. Followed by a pointer to a
is used in combination with \fICURLFORM_FILE\fP. Followed by a pointer to a
string, it tells libcurl to use the given string as the \fIfilename\fP in the
string, it tells libcurl to use the given string as the \fIfilename\fP in the
file upload part instead of the actual file name.
file upload part instead of the actual file name.

.IP CURLFORM_BUFFER
.IP CURLFORM_BUFFER
is used for custom file upload parts without use of \fICURLFORM_FILE\fP.  It
is used for custom file upload parts without use of \fICURLFORM_FILE\fP.  It
tells libcurl that the file contents are already present in a buffer.  The
tells libcurl that the file contents are already present in a buffer.  The
parameter is a string which provides the \fIfilename\fP field in the content
parameter is a string which provides the \fIfilename\fP field in the content
header.
header.

.IP CURLFORM_BUFFERPTR
.IP CURLFORM_BUFFERPTR
is used in combination with \fICURLFORM_BUFFER\fP. The parameter is a pointer
is used in combination with \fICURLFORM_BUFFER\fP. The parameter is a pointer
to the buffer to be uploaded. This buffer must not be freed until after
to the buffer to be uploaded. This buffer must not be freed until after
\fIcurl_easy_cleanup(3)\fP is called. You must also use
\fIcurl_easy_cleanup(3)\fP is called. You must also use
\fICURLFORM_BUFFERLENGTH\fP to set the number of bytes in the buffer.
\fICURLFORM_BUFFERLENGTH\fP to set the number of bytes in the buffer.

.IP CURLFORM_BUFFERLENGTH
.IP CURLFORM_BUFFERLENGTH
is used in combination with \fICURLFORM_BUFFER\fP. The parameter is a
is used in combination with \fICURLFORM_BUFFER\fP. The parameter is a
long which gives the length of the buffer.
long which gives the length of the buffer.

.IP CURLFORM_STREAM
Tells libcurl to use the \fICURLOPT_READFUNCTION\fP callback to get data. The
parameter you pass to \fICURLFORM_STREAM\fP is the pointer passed on to the
read callback's fourth argument. If you want the part to look like a file
upload one, set the \fICURLFORM_FILENAME\fP parameter as well. (Option added
in libcurl 7.18.2)
.IP CURLFORM_ARRAY
.IP CURLFORM_ARRAY
Another possibility to send options to curl_formadd() is the
Another possibility to send options to curl_formadd() is the
\fBCURLFORM_ARRAY\fP option, that passes a struct curl_forms array pointer as
\fBCURLFORM_ARRAY\fP option, that passes a struct curl_forms array pointer as
@@ -121,7 +115,6 @@ its value. Each curl_forms structure element has a CURLformoption and a char
pointer. The final element in the array must be a CURLFORM_END. All available
pointer. The final element in the array must be a CURLFORM_END. All available
options can be used in an array, except the CURLFORM_ARRAY option itself!  The
options can be used in an array, except the CURLFORM_ARRAY option itself!  The
last argument in such an array must always be \fBCURLFORM_END\fP.
last argument in such an array must always be \fBCURLFORM_END\fP.

.IP CURLFORM_CONTENTHEADER
.IP CURLFORM_CONTENTHEADER
specifies extra headers for the form POST section.  This takes a curl_slist
specifies extra headers for the form POST section.  This takes a curl_slist
prepared in the usual way using \fBcurl_slist_append\fP and appends the list
prepared in the usual way using \fBcurl_slist_append\fP and appends the list
+10 −2
Original line number Original line Diff line number Diff line
@@ -211,10 +211,16 @@ struct curl_httppost {
                                       do not free in formfree */
                                       do not free in formfree */
#define HTTPPOST_BUFFER (1<<4)      /* upload file from buffer */
#define HTTPPOST_BUFFER (1<<4)      /* upload file from buffer */
#define HTTPPOST_PTRBUFFER (1<<5)   /* upload file from pointer contents */
#define HTTPPOST_PTRBUFFER (1<<5)   /* upload file from pointer contents */
#define HTTPPOST_CALLBACK (1<<6)    /* upload fiel contents by using the
                                       regular read callback to get the data
                                       and pass the given pointer as custom
                                       pointer */


  char *showfilename;               /* The file name to show. If not set, the
  char *showfilename;               /* The file name to show. If not set, the
                                       actual file name will be used (if this
                                       actual file name will be used (if this
                                       is a file part) */
                                       is a file part) */
  void *userp;                      /* custom pointer used for
                                       HTTPPOST_CALLBACK posts */
};
};


typedef int (*curl_progress_callback)(void *clientp,
typedef int (*curl_progress_callback)(void *clientp,
@@ -1313,6 +1319,8 @@ typedef enum {
  CFINIT(END),
  CFINIT(END),
  CFINIT(OBSOLETE2),
  CFINIT(OBSOLETE2),


  CFINIT(STREAM),

  CURLFORM_LASTENTRY /* the last unusued */
  CURLFORM_LASTENTRY /* the last unusued */
} CURLformoption;
} CURLformoption;


+102 −54
Original line number Original line Diff line number Diff line
@@ -5,7 +5,7 @@
 *                            | (__| |_| |  _ <| |___
 *                            | (__| |_| |  _ <| |___
 *                             \___|\___/|_| \_\_____|
 *                             \___|\___/|_| \_\_____|
 *
 *
 * Copyright (C) 1998 - 2007, Daniel Stenberg, <daniel@haxx.se>, et al.
 * Copyright (C) 1998 - 2008, Daniel Stenberg, <daniel@haxx.se>, et al.
 *
 *
 * This software is licensed as described in the file COPYING, which
 * This software is licensed as described in the file COPYING, which
 * you should have received as part of this distribution. The terms
 * you should have received as part of this distribution. The terms
@@ -165,7 +165,7 @@ AddHttpPost(char * name, size_t namelength,
            char *contenttype,
            char *contenttype,
            long flags,
            long flags,
            struct curl_slist* contentHeader,
            struct curl_slist* contentHeader,
            char *showfilename,
            char *showfilename, char *userp,
            struct curl_httppost *parent_post,
            struct curl_httppost *parent_post,
            struct curl_httppost **httppost,
            struct curl_httppost **httppost,
            struct curl_httppost **last_post)
            struct curl_httppost **last_post)
@@ -182,6 +182,7 @@ AddHttpPost(char * name, size_t namelength,
    post->contenttype = contenttype;
    post->contenttype = contenttype;
    post->contentheader = contentHeader;
    post->contentheader = contentHeader;
    post->showfilename = showfilename;
    post->showfilename = showfilename;
    post->userp = userp,
    post->flags = flags;
    post->flags = flags;
  }
  }
  else
  else
@@ -618,6 +619,25 @@ CURLFORMcode FormAdd(struct curl_httppost **httppost,
          array_state?(size_t)array_value:(size_t)va_arg(params, long);
          array_state?(size_t)array_value:(size_t)va_arg(params, long);
      break;
      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:
    case CURLFORM_CONTENTTYPE:
      {
      {
        const char *contenttype =
        const char *contenttype =
@@ -731,10 +751,9 @@ CURLFORMcode FormAdd(struct curl_httppost **httppost,
          }
          }
          form->name_alloc = TRUE;
          form->name_alloc = TRUE;
        }
        }
        if( !(form->flags & HTTPPOST_FILENAME) &&
        if( !(form->flags & (HTTPPOST_FILENAME | HTTPPOST_READFILE |
             !(form->flags & HTTPPOST_READFILE) &&
                             HTTPPOST_PTRCONTENTS | HTTPPOST_PTRBUFFER |
             !(form->flags & HTTPPOST_PTRCONTENTS) &&
                             HTTPPOST_CALLBACK)) ) {
             !(form->flags & HTTPPOST_PTRBUFFER) ) {
          /* copy value (without strdup; possibly contains null characters) */
          /* copy value (without strdup; possibly contains null characters) */
          form->value = memdup(form->value, form->contentslength);
          form->value = memdup(form->value, form->contentslength);
          if(!form->value) {
          if(!form->value) {
@@ -748,6 +767,7 @@ CURLFORMcode FormAdd(struct curl_httppost **httppost,
                           form->buffer, form->bufferlength,
                           form->buffer, form->bufferlength,
                           form->contenttype, form->flags,
                           form->contenttype, form->flags,
                           form->contentheader, form->showfilename,
                           form->contentheader, form->showfilename,
                           form->userp,
                           post, httppost,
                           post, httppost,
                           last_post);
                           last_post);


@@ -824,6 +844,7 @@ static CURLcode AddFormData(struct FormData **formp,
    return CURLE_OUT_OF_MEMORY;
    return CURLE_OUT_OF_MEMORY;
  newform->next = NULL;
  newform->next = NULL;


  if(type <= FORM_CONTENT) {
    /* we make it easier for plain strings: */
    /* we make it easier for plain strings: */
    if(!length)
    if(!length)
      length = strlen((char *)line);
      length = strlen((char *)line);
@@ -836,6 +857,12 @@ static CURLcode AddFormData(struct FormData **formp,
    memcpy(newform->line, line, length);
    memcpy(newform->line, line, length);
    newform->length = length;
    newform->length = length;
    newform->line[length]=0; /* zero terminate for easier debugging */
    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;
  newform->type = type;


  if(*formp) {
  if(*formp) {
@@ -846,7 +873,9 @@ static CURLcode AddFormData(struct FormData **formp,
    *formp = newform;
    *formp = newform;


  if(size) {
  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;
      *size += length;
    else {
    else {
      /* Since this is a file to be uploaded here, add the size of the actual
      /* Since this is a file to be uploaded here, add the size of the actual
@@ -893,6 +922,7 @@ void Curl_formclean(struct FormData **form_ptr)


  do {
  do {
    next=form->next;  /* the following form line */
    next=form->next;  /* the following form line */
    if(form->type <= FORM_CONTENT)
      free(form->line); /* free the line */
      free(form->line); /* free the line */
    free(form);       /* free the struct */
    free(form);       /* free the struct */


@@ -997,7 +1027,8 @@ void curl_formfree(struct curl_httppost *form)


    if( !(form->flags & HTTPPOST_PTRNAME) && form->name)
    if( !(form->flags & HTTPPOST_PTRNAME) && form->name)
      free(form->name); /* free the 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 */
      free(form->contents); /* free the contents */
    if(form->contenttype)
    if(form->contenttype)
      free(form->contenttype); /* free the content type */
      free(form->contenttype); /* free the content type */
@@ -1188,9 +1219,11 @@ CURLcode Curl_getFormData(struct FormData **finalform,
        if(result)
        if(result)
          break;
          break;
      }
      }
      else if((post->flags & HTTPPOST_FILENAME) ||
      else if(post->flags & (HTTPPOST_FILENAME|HTTPPOST_BUFFER|
              (post->flags & 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=
        char *filebasename=
          (!post->showfilename)?strippath(post->contents):NULL;
          (!post->showfilename)?strippath(post->contents):NULL;


@@ -1312,7 +1345,14 @@ CURLcode Curl_getFormData(struct FormData **finalform,
          if(result)
          if(result)
            break;
            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 {
      else {
        /* include the contents we got */
        /* include the contents we got */
        result = AddFormData(&form, FORM_CONTENT, post->contents,
        result = AddFormData(&form, FORM_CONTENT, post->contents,
@@ -1380,9 +1420,15 @@ int Curl_FormInit(struct Form *form, struct FormData *formdata )
  return 0;
  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;
  size_t nread;
  bool callback = (bool)(form->data->type == FORM_CALLBACK);

  if(callback)
    nread = form->fread_func(buffer, 1, size, form->data->line);
  else {
    if(!form->fp) {
    if(!form->fp) {
      /* this file hasn't yet been opened */
      /* this file hasn't yet been opened */
      form->fp = fopen(form->data->line, "rb"); /* b is for binary */
      form->fp = fopen(form->data->line, "rb"); /* b is for binary */
@@ -1390,11 +1436,13 @@ static size_t readfromfile(struct Form *form, char *buffer, size_t size)
        return (size_t)-1; /* failure */
        return (size_t)-1; /* failure */
    }
    }
    nread = fread(buffer, 1, size, form->fp);
    nread = fread(buffer, 1, size, form->fp);

  }
  if(nread != size) {
  if(!nread || nread > size) {
    /* this is the last chunk from the file, move on */
    /* this is the last chunk from the file, move on */
    if(!callback) {
      fclose(form->fp);
      fclose(form->fp);
      form->fp = NULL;
      form->fp = NULL;
    }
    form->data = form->data->next;
    form->data = form->data->next;
  }
  }


@@ -1421,7 +1469,8 @@ size_t Curl_FormReader(char *buffer,
  if(!form->data)
  if(!form->data)
    return 0; /* nothing, error, empty */
    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);
    gotsize = readfromfile(form, buffer, wantedsize);


    if(gotsize)
    if(gotsize)
@@ -1449,10 +1498,9 @@ size_t Curl_FormReader(char *buffer,


    form->data = form->data->next; /* advance */
    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
  /* 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.
     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;
  return gotsize;
}
}
Loading