]> granicus.if.org Git - curl/commitdiff
cli tool: do not use mime.h private structures.
authorPatrick Monnerat <patrick@monnerat.net>
Mon, 11 Feb 2019 18:10:41 +0000 (19:10 +0100)
committerPatrick Monnerat <patrick@monnerat.net>
Mon, 11 Feb 2019 18:10:41 +0000 (19:10 +0100)
Option -F generates an intermediate representation of the mime structure
that is used later to create the libcurl mime structure and generate
the --libcurl statements.

Reported-by: Daniel Stenberg
Fixes #3532
Closes #3546

src/tool_cfgable.c
src/tool_cfgable.h
src/tool_formparse.c
src/tool_formparse.h
src/tool_setopt.c
src/tool_setopt.h
tests/data/test1404

index 0eb941ef6eb51711686491422e757c4c6199e43b..6d4a0ed9b7657101953e72962220d685c980d7aa 100644 (file)
@@ -5,7 +5,7 @@
  *                            | (__| |_| |  _ <| |___
  *                             \___|\___/|_| \_\_____|
  *
- * Copyright (C) 1998 - 2018, Daniel Stenberg, <daniel@haxx.se>, et al.
+ * Copyright (C) 1998 - 2019, Daniel Stenberg, <daniel@haxx.se>, et al.
  *
  * This software is licensed as described in the file COPYING, which
  * you should have received as part of this distribution. The terms
@@ -145,7 +145,7 @@ static void free_config_fields(struct OperationConfig *config)
   curl_slist_free_all(config->proxyheaders);
 
   if(config->mimepost) {
-    curl_mime_free(config->mimepost);
+    tool_mime_free(config->mimepost);
     config->mimepost = NULL;
   }
   config->mimecurrent = NULL;
index 81680dbbbd278c2f20ad769dee91733c8e790031..0ebe947ae92fb7ab377707b1b5164e21539d9c75 100644 (file)
@@ -7,7 +7,7 @@
  *                            | (__| |_| |  _ <| |___
  *                             \___|\___/|_| \_\_____|
  *
- * Copyright (C) 1998 - 2018, Daniel Stenberg, <daniel@haxx.se>, et al.
+ * Copyright (C) 1998 - 2019, Daniel Stenberg, <daniel@haxx.se>, 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,6 +27,8 @@
 
 #include "tool_metalink.h"
 
+#include "tool_formparse.h"
+
 typedef enum {
   ERR_NONE,
   ERR_BINARY_TERMINAL = 1, /* binary to terminal detected */
@@ -176,8 +178,8 @@ struct OperationConfig {
   curl_off_t condtime;
   struct curl_slist *headers;
   struct curl_slist *proxyheaders;
-  curl_mime *mimepost;
-  curl_mime *mimecurrent;
+  tool_mime *mimepost;
+  tool_mime *mimecurrent;
   struct curl_slist *telnet_options;
   struct curl_slist *resolve;
   struct curl_slist *connect_to;
index 5d1ea9c53de1f208e8b38a81b61e07516f3e0677..537c6ca93b4b93b8163c3f3b7821e026fc39e7db 100644 (file)
@@ -5,7 +5,7 @@
  *                            | (__| |_| |  _ <| |___
  *                             \___|\___/|_| \_\_____|
  *
- * Copyright (C) 1998 - 2018, Daniel Stenberg, <daniel@haxx.se>, et al.
+ * Copyright (C) 1998 - 2019, Daniel Stenberg, <daniel@haxx.se>, et al.
  *
  * This software is licensed as described in the file COPYING, which
  * you should have received as part of this distribution. The terms
@@ -21,7 +21,6 @@
  ***************************************************************************/
 #include "tool_setup.h"
 
-#include "mime.h"
 #include "strcase.h"
 
 #define ENABLE_CURLX_PRINTF
 
 #include "memdebug.h" /* keep this as LAST include */
 
-/* Stdin parameters. */
-typedef struct {
-  char *data;  /* Memory data. */
-  curl_off_t origin;  /* File read origin offset. */
-  curl_off_t size; /* Data size. */
-  curl_off_t curpos; /* Current read position. */
-}  standard_input;
+/* Macros to free const pointers. */
+#define CONST_FREE(x)           free((void *) (x))
+#define CONST_SAFEFREE(x)       Curl_safefree(*((void **) &(x)))
 
+/* tool_mime functions. */
+static tool_mime *tool_mime_new(tool_mime *parent, toolmimekind kind)
+{
+  tool_mime *m = (tool_mime *) calloc(1, sizeof(*m));
+
+  if(m) {
+    m->kind = kind;
+    m->parent = parent;
+    if(parent) {
+      m->prev = parent->subparts;
+      parent->subparts = m;
+    }
+  }
+  return m;
+}
+
+static tool_mime *tool_mime_new_parts(tool_mime *parent)
+{
+  return tool_mime_new(parent, TOOLMIME_PARTS);
+}
+
+static tool_mime *tool_mime_new_data(tool_mime *parent, const char *data)
+{
+  tool_mime *m = NULL;
+
+  data = strdup(data);
+  if(data) {
+    m = tool_mime_new(parent, TOOLMIME_DATA);
+    if(!m)
+      CONST_FREE(data);
+    else
+      m->data = data;
+  }
+  return m;
+}
+
+static tool_mime *tool_mime_new_filedata(tool_mime *parent,
+                                         const char *filename,
+                                         bool isremotefile,
+                                         CURLcode *errcode)
+{
+  CURLcode result = CURLE_OK;
+  tool_mime *m = NULL;
+
+  *errcode = CURLE_OUT_OF_MEMORY;
+  if(strcmp(filename, "-")) {
+    /* This is a normal file. */
+    filename = strdup(filename);
+    if(filename) {
+      m = tool_mime_new(parent, TOOLMIME_FILE);
+      if(!m)
+        CONST_FREE(filename);
+      else {
+        m->data = filename;
+        if(!isremotefile)
+          m->kind = TOOLMIME_FILEDATA;
+       *errcode = CURLE_OK;
+      }
+    }
+  }
+  else {        /* Standard input. */
+    int fd = fileno(stdin);
+    char *data = NULL;
+    curl_off_t size;
+    curl_off_t origin;
+    struct_stat sbuf;
+
+    set_binmode(stdin);
+    origin = ftell(stdin);
+    /* If stdin is a regular file, do not buffer data but read it
+       when needed. */
+    if(fd >= 0 && origin >= 0 && !fstat(fd, &sbuf) &&
+#ifdef __VMS
+       sbuf.st_fab_rfm != FAB$C_VAR && sbuf.st_fab_rfm != FAB$C_VFC &&
+#endif
+       S_ISREG(sbuf.st_mode)) {
+      size = sbuf.st_size - origin;
+      if(size < 0)
+        size = 0;
+    }
+    else {  /* Not suitable for direct use, buffer stdin data. */
+      size_t stdinsize = 0;
+
+      if(file2memory(&data, &stdinsize, stdin) != PARAM_OK) {
+        /* Out of memory. */
+        return m;
+      }
+
+      if(ferror(stdin)) {
+        result = CURLE_READ_ERROR;
+        Curl_safefree(data);
+        data = NULL;
+      }
+      else if(!stdinsize) {
+        /* Zero-length data has been freed. Re-create it. */
+        data = strdup("");
+        if(!data)
+          return m;
+      }
+      size = curlx_uztoso(stdinsize);
+      origin = 0;
+    }
+    m = tool_mime_new(parent, TOOLMIME_STDIN);
+    if(!m)
+      Curl_safefree(data);
+    else {
+      m->data = data;
+      m->origin = origin;
+      m->size = size;
+      m->curpos = 0;
+      if(!isremotefile)
+        m->kind = TOOLMIME_STDINDATA;
+      *errcode = result;
+    }
+  }
+  return m;
+}
+
+void tool_mime_free(tool_mime *mime)
+{
+  if(mime) {
+    if(mime->subparts)
+      tool_mime_free(mime->subparts);
+    if(mime->prev)
+      tool_mime_free(mime->prev);
+    curl_mime_free(mime->handle);
+    CONST_SAFEFREE(mime->name);
+    CONST_SAFEFREE(mime->filename);
+    CONST_SAFEFREE(mime->type);
+    CONST_SAFEFREE(mime->encoder);
+    CONST_SAFEFREE(mime->data);
+    curl_slist_free_all(mime->headers);
+    free(mime);
+  }
+}
+
+
+/* Mime part callbacks for stdin. */
+size_t tool_mime_stdin_read(char *buffer,
+                            size_t size, size_t nitems, void *arg)
+{
+  tool_mime *sip = (tool_mime *) arg;
+  curl_off_t bytesleft;
+  (void) size;  /* Always 1: ignored. */
+
+  if(sip->size >= 0) {
+    if(sip->curpos >= sip->size)
+      return 0;  /* At eof. */
+    bytesleft = sip->size - sip->curpos;
+    if(curlx_uztoso(nitems) > bytesleft)
+      nitems = curlx_sotouz(bytesleft);
+  }
+  if(nitems) {
+    if(sip->data) {
+      /* Return data from memory. */
+      memcpy(buffer, sip->data + curlx_sotouz(sip->curpos), nitems);
+    }
+    else {
+      /* Read from stdin. */
+      nitems = fread(buffer, 1, nitems, stdin);
+      if(ferror(stdin)) {
+        /* Show error only once. */
+        if(sip->config) {
+          warnf(sip->config, "stdin: %s\n", strerror(errno));
+          sip->config = NULL;
+        }
+        return CURL_READFUNC_ABORT;
+      }
+    }
+    sip->curpos += curlx_uztoso(nitems);
+  }
+  return nitems;
+}
+
+int tool_mime_stdin_seek(void *instream, curl_off_t offset, int whence)
+{
+  tool_mime *sip = (tool_mime *) instream;
+
+  switch(whence) {
+  case SEEK_CUR:
+    offset += sip->curpos;
+    break;
+  case SEEK_END:
+    offset += sip->size;
+    break;
+  }
+  if(offset < 0)
+    return CURL_SEEKFUNC_CANTSEEK;
+  if(!sip->data) {
+    if(fseek(stdin, (long) (offset + sip->origin), SEEK_SET))
+      return CURL_SEEKFUNC_CANTSEEK;
+  }
+  sip->curpos = offset;
+  return CURL_SEEKFUNC_OK;
+}
 
 /*
  * helper function to get a word from form param
@@ -379,130 +569,15 @@ static int get_param_part(struct OperationConfig *config, char endchar,
 }
 
 
-/* Mime part callbacks for stdin. */
-static size_t stdin_read(char *buffer, size_t size, size_t nitems, void *arg)
-{
-  standard_input *sip = (standard_input *) arg;
-  curl_off_t bytesleft;
-  (void) size;  /* Always 1: ignored. */
-
-  if(sip->curpos >= sip->size)
-    return 0;  /* At eof. */
-  bytesleft = sip->size - sip->curpos;
-  if((curl_off_t) nitems > bytesleft)
-    nitems = (size_t) bytesleft;
-  if(sip->data) {
-    /* Return data from memory. */
-    memcpy(buffer, sip->data + (size_t) sip->curpos, nitems);
-  }
-  else {
-    /* Read from stdin. */
-    nitems = fread(buffer, 1, nitems, stdin);
-  }
-  sip->curpos += nitems;
-  return nitems;
-}
-
-static int stdin_seek(void *instream, curl_off_t offset, int whence)
-{
-  standard_input *sip = (standard_input *) instream;
-
-  switch(whence) {
-  case SEEK_CUR:
-    offset += sip->curpos;
-    break;
-  case SEEK_END:
-    offset += sip->size;
-    break;
-  }
-  if(offset < 0)
-    return CURL_SEEKFUNC_CANTSEEK;
-  if(!sip->data) {
-    if(fseek(stdin, (long) (offset + sip->origin), SEEK_SET))
-      return CURL_SEEKFUNC_CANTSEEK;
-  }
-  sip->curpos = offset;
-  return CURL_SEEKFUNC_OK;
-}
-
-static void stdin_free(void *ptr)
-{
-  standard_input *sip = (standard_input *) ptr;
-
-  Curl_safefree(sip->data);
-  free(sip);
-}
-
-/* Set a part's data from a file, taking care about the pseudo filename "-" as
- * a shortcut to read stdin: if so, use a callback to read OUR stdin (to
- * workaround Windows DLL file handle caveat).
- * If stdin is a regular file opened in binary mode, save current offset as
- * origin for rewind and do not buffer data. Else read to EOF and keep in
- * memory. In all cases, compute the stdin data size.
- */
-static CURLcode file_or_stdin(curl_mimepart *part, const char *file)
-{
-  standard_input *sip = NULL;
-  int fd = -1;
-  CURLcode result = CURLE_OK;
-  struct_stat sbuf;
-
-  if(strcmp(file, "-"))
-    return curl_mime_filedata(part, file);
-
-  sip = (standard_input *) calloc(1, sizeof(*sip));
-  if(!sip)
-    return CURLE_OUT_OF_MEMORY;
-
-  set_binmode(stdin);
-
-  /* If stdin is a regular file, do not buffer data but read it when needed. */
-  fd = fileno(stdin);
-  sip->origin = ftell(stdin);
-  if(fd >= 0 && sip->origin >= 0 && !fstat(fd, &sbuf) &&
-#ifdef __VMS
-     sbuf.st_fab_rfm != FAB$C_VAR && sbuf.st_fab_rfm != FAB$C_VFC &&
-#endif
-     S_ISREG(sbuf.st_mode)) {
-    sip->size = sbuf.st_size - sip->origin;
-    if(sip->size < 0)
-      sip->size = 0;
-  }
-  else {  /* Not suitable for direct use, buffer stdin data. */
-    size_t stdinsize = 0;
-
-    sip->origin = 0;
-    if(file2memory(&sip->data, &stdinsize, stdin) != PARAM_OK)
-      result = CURLE_OUT_OF_MEMORY;
-    else {
-      if(!stdinsize)
-        sip->data = NULL;  /* Has been freed if no data. */
-      sip->size = stdinsize;
-      if(ferror(stdin))
-        result = CURLE_READ_ERROR;
-    }
-  }
-
-  /* Set remote file name. */
-  if(!result)
-    result = curl_mime_filename(part, file);
-
-  /* Set part's data from callback. */
-  if(!result)
-    result = curl_mime_data_cb(part, sip->size,
-                               stdin_read, stdin_seek, stdin_free, sip);
-  if(result)
-    stdin_free(sip);
-  return result;
-}
-
-
 /***************************************************************************
  *
  * formparse()
  *
  * Reads a 'name=value' parameter and builds the appropriate linked list.
  *
+ * If the value is of the form '<filename', field data is read from the
+ * given file.
+
  * Specify files to upload with 'name=@filename', or 'name=@"filename"'
  * in case the filename contain ',' or ';'. Supports specified
  * given Content-Type of the files. Such as ';type=<content-type>'.
@@ -539,15 +614,27 @@ static CURLcode file_or_stdin(curl_mimepart *part, const char *file)
  * else curl will fail to figure out the correct filename. if the filename
  * tobe quoted contains '"' or '\', '"' and '\' must be escaped by backslash.
  *
- * This function uses curl_formadd to fulfill it's job. Is heavily based on
- * the old curl_formparse code.
- *
  ***************************************************************************/
 
+/* Convenience macros for null pointer check. */
+#define NULL_CHECK(ptr, init, retcode) {                                \
+  (ptr) = (init);                                                       \
+  if(!(ptr)) {                                                          \
+    warnf(config->global, "out of memory!\n");                          \
+    curl_slist_free_all(headers);                                       \
+    Curl_safefree(contents);                                            \
+    return retcode;                                                     \
+  }                                                                     \
+}
+#define SET_TOOL_MIME_PTR(m, field, retcode) {                          \
+  if(field)                                                             \
+    NULL_CHECK(m->field, strdup(field), retcode);                       \
+}
+
 int formparse(struct OperationConfig *config,
               const char *input,
-              curl_mime **mimepost,
-              curl_mime **mimecurrent,
+              tool_mime **mimepost,
+              tool_mime **mimecurrent,
               bool literal_value)
 {
   /* input MUST be a string in the format 'name=contents' and we'll
@@ -560,25 +647,17 @@ int formparse(struct OperationConfig *config,
   char *filename = NULL;
   char *encoder = NULL;
   struct curl_slist *headers = NULL;
-  curl_mimepart *part = NULL;
+  tool_mime *part = NULL;
   CURLcode res;
 
   /* Allocate the main mime structure if needed. */
   if(!*mimepost) {
-    *mimepost = curl_mime_init(config->easy);
-    if(!*mimepost) {
-      warnf(config->global, "curl_mime_init failed!\n");
-      return 1;
-    }
+    NULL_CHECK(*mimepost, tool_mime_new_parts(NULL), 1);
     *mimecurrent = *mimepost;
   }
 
   /* Make a copy we can overwrite. */
-  contents = strdup(input);
-  if(!contents) {
-    fprintf(config->global->errors, "out of memory\n");
-    return 2;
-  }
+  NULL_CHECK(contents, strdup(input), 2);
 
   /* Scan for the end of the name. */
   contp = strchr(contents, '=');
@@ -589,8 +668,6 @@ int formparse(struct OperationConfig *config,
     *contp++ = '\0';
 
     if(*contp == '(' && !literal_value) {
-      curl_mime *subparts;
-
       /* Starting a multipart. */
       sep = get_param_part(config, '\0',
                            &contp, &data, &type, NULL, NULL, &headers);
@@ -598,55 +675,26 @@ int formparse(struct OperationConfig *config,
         Curl_safefree(contents);
         return 3;
       }
-      subparts = curl_mime_init(config->easy);
-      if(!subparts) {
-        warnf(config->global, "curl_mime_init failed!\n");
-        curl_slist_free_all(headers);
-        Curl_safefree(contents);
-        return 4;
-      }
-      part = curl_mime_addpart(*mimecurrent);
-      if(!part) {
-        warnf(config->global, "curl_mime_addpart failed!\n");
-        curl_mime_free(subparts);
-        curl_slist_free_all(headers);
-        Curl_safefree(contents);
-        return 5;
-      }
-      if(curl_mime_subparts(part, subparts)) {
-        warnf(config->global, "curl_mime_subparts failed!\n");
-        curl_mime_free(subparts);
-        curl_slist_free_all(headers);
-        Curl_safefree(contents);
-        return 6;
-      }
-      *mimecurrent = subparts;
-      if(curl_mime_headers(part, headers, 1)) {
-        warnf(config->global, "curl_mime_headers failed!\n");
-        curl_slist_free_all(headers);
-        Curl_safefree(contents);
-        return 7;
-      }
-      if(curl_mime_type(part, type)) {
-        warnf(config->global, "curl_mime_type failed!\n");
-        Curl_safefree(contents);
-        return 8;
-      }
+      NULL_CHECK(part, tool_mime_new_parts(*mimecurrent), 4);
+      *mimecurrent = part;
+      part->headers = headers;
+      headers = NULL;
+      SET_TOOL_MIME_PTR(part, type, 5);
     }
     else if(!name && !strcmp(contp, ")") && !literal_value) {
-      /* Ending a mutipart. */
+      /* Ending a multipart. */
       if(*mimecurrent == *mimepost) {
         warnf(config->global, "no multipart to terminate!\n");
         Curl_safefree(contents);
-        return 9;
+        return 6;
         }
-      *mimecurrent = (*mimecurrent)->parent->parent;
+      *mimecurrent = (*mimecurrent)->parent;
     }
     else if('@' == contp[0] && !literal_value) {
 
       /* we use the @-letter to indicate file name(s) */
 
-      curl_mime *subparts = NULL;
+      tool_mime *subparts = NULL;
 
       do {
         /* since this was a file, it may have a content-type specifier
@@ -655,10 +703,8 @@ int formparse(struct OperationConfig *config,
         sep = get_param_part(config, ',', &contp,
                              &data, &type, &filename, &encoder, &headers);
         if(sep < 0) {
-          if(subparts != *mimecurrent)
-            curl_mime_free(subparts);
           Curl_safefree(contents);
-          return 10;
+          return 7;
         }
 
         /* now contp point to comma or string end.
@@ -666,125 +712,66 @@ int formparse(struct OperationConfig *config,
         if(!subparts) {
           if(sep != ',')    /* If there is a single file. */
             subparts = *mimecurrent;
-          else {
-            subparts = curl_mime_init(config->easy);
-            if(!subparts) {
-              warnf(config->global, "curl_mime_init failed!\n");
-              curl_slist_free_all(headers);
-              Curl_safefree(contents);
-              return 11;
-            }
-          }
-        }
-
-        /* Allocate a part for that file. */
-        part = curl_mime_addpart(subparts);
-        if(!part) {
-          warnf(config->global, "curl_mime_addpart failed!\n");
-          if(subparts != *mimecurrent)
-            curl_mime_free(subparts);
-          curl_slist_free_all(headers);
-          Curl_safefree(contents);
-          return 12;
-        }
-
-        /* Set part headers. */
-        if(curl_mime_headers(part, headers, 1)) {
-          warnf(config->global, "curl_mime_headers failed!\n");
-          if(subparts != *mimecurrent)
-            curl_mime_free(subparts);
-          curl_slist_free_all(headers);
-          Curl_safefree(contents);
-          return 13;
+          else
+            NULL_CHECK(subparts, tool_mime_new_parts(*mimecurrent), 8);
         }
 
-        /* Setup file in part. */
-        res = file_or_stdin(part, data);
-        if(res) {
-          warnf(config->global, "setting file %s  failed!\n", data);
-          if(res != CURLE_READ_ERROR) {
-            if(subparts != *mimecurrent)
-              curl_mime_free(subparts);
+        /* Store that file in a part. */
+        NULL_CHECK(part,
+                   tool_mime_new_filedata(subparts, data, TRUE, &res), 9);
+        part->headers = headers;
+        headers = NULL;
+        if(res == CURLE_READ_ERROR) {
+            /* An error occurred while reading stdin: if read has started,
+               issue the error now. Else, delay it until processed by
+               libcurl. */
+          if(part->size > 0) {
+            warnf(config->global,
+                  "error while reading standard input\n");
             Curl_safefree(contents);
-            return 14;
+            return 10;
           }
+          CONST_SAFEFREE(part->data);
+          part->data = NULL;
+          part->size = -1;
+          res = CURLE_OK;
         }
-        if(filename && curl_mime_filename(part, filename)) {
-          warnf(config->global, "curl_mime_filename failed!\n");
-          if(subparts != *mimecurrent)
-            curl_mime_free(subparts);
-          Curl_safefree(contents);
-          return 15;
-        }
-        if(curl_mime_type(part, type)) {
-          warnf(config->global, "curl_mime_type failed!\n");
-          if(subparts != *mimecurrent)
-            curl_mime_free(subparts);
-          Curl_safefree(contents);
-          return 16;
-        }
-        if(curl_mime_encoder(part, encoder)) {
-          warnf(config->global, "curl_mime_encoder failed!\n");
-          if(subparts != *mimecurrent)
-            curl_mime_free(subparts);
-          Curl_safefree(contents);
-          return 17;
-        }
+        SET_TOOL_MIME_PTR(part, filename, 11);
+        SET_TOOL_MIME_PTR(part, type, 12);
+        SET_TOOL_MIME_PTR(part, encoder, 13);
 
         /* *contp could be '\0', so we just check with the delimiter */
       } while(sep); /* loop if there's another file name */
-
-      /* now we add the multiple files section */
-      if(subparts != *mimecurrent) {
-        part = curl_mime_addpart(*mimecurrent);
-        if(!part) {
-          warnf(config->global, "curl_mime_addpart failed!\n");
-          curl_mime_free(subparts);
-          Curl_safefree(contents);
-          return 18;
-        }
-        if(curl_mime_subparts(part, subparts)) {
-          warnf(config->global, "curl_mime_subparts failed!\n");
-          curl_mime_free(subparts);
-          Curl_safefree(contents);
-          return 19;
-        }
-      }
+      part = (*mimecurrent)->subparts;  /* Set name on group. */
     }
     else {
-        /* Allocate a mime part. */
-        part = curl_mime_addpart(*mimecurrent);
-        if(!part) {
-          warnf(config->global, "curl_mime_addpart failed!\n");
-          Curl_safefree(contents);
-          return 20;
-        }
-
       if(*contp == '<' && !literal_value) {
         ++contp;
         sep = get_param_part(config, '\0', &contp,
                              &data, &type, NULL, &encoder, &headers);
         if(sep < 0) {
           Curl_safefree(contents);
-          return 21;
-        }
-
-        /* Set part headers. */
-        if(curl_mime_headers(part, headers, 1)) {
-          warnf(config->global, "curl_mime_headers failed!\n");
-          curl_slist_free_all(headers);
-          Curl_safefree(contents);
-          return 22;
+          return 14;
         }
 
-        /* Setup file in part. */
-        res = file_or_stdin(part, data);
-        if(res) {
-          warnf(config->global, "setting file %s failed!\n", data);
-          if(res != CURLE_READ_ERROR) {
+        NULL_CHECK(part, tool_mime_new_filedata(*mimecurrent, data, FALSE,
+                                                &res), 15);
+        part->headers = headers;
+        headers = NULL;
+        if(res == CURLE_READ_ERROR) {
+            /* An error occurred while reading stdin: if read has started,
+               issue the error now. Else, delay it until processed by
+               libcurl. */
+          if(part->size > 0) {
+            warnf(config->global,
+                  "error while reading standard input\n");
             Curl_safefree(contents);
-            return 23;
+            return 16;
           }
+          CONST_SAFEFREE(part->data);
+          part->data = NULL;
+          part->size = -1;
+          res = CURLE_OK;
         }
       }
       else {
@@ -795,48 +782,18 @@ int formparse(struct OperationConfig *config,
                                &data, &type, &filename, &encoder, &headers);
           if(sep < 0) {
             Curl_safefree(contents);
-            return 24;
+            return 17;
           }
         }
 
-        /* Set part headers. */
-        if(curl_mime_headers(part, headers, 1)) {
-          warnf(config->global, "curl_mime_headers failed!\n");
-          curl_slist_free_all(headers);
-          Curl_safefree(contents);
-          return 25;
-        }
-
-#ifdef CURL_DOES_CONVERSIONS
-        if(convert_to_network(data, strlen(data))) {
-          warnf(config->global, "curl_formadd failed!\n");
-          Curl_safefree(contents);
-          return 26;
-        }
-#endif
-
-        if(curl_mime_data(part, data, CURL_ZERO_TERMINATED)) {
-          warnf(config->global, "curl_mime_data failed!\n");
-          Curl_safefree(contents);
-          return 27;
-        }
+        NULL_CHECK(part, tool_mime_new_data(*mimecurrent, data), 18);
+        part->headers = headers;
+        headers = NULL;
       }
 
-      if(curl_mime_filename(part, filename)) {
-        warnf(config->global, "curl_mime_filename failed!\n");
-        Curl_safefree(contents);
-        return 28;
-      }
-      if(curl_mime_type(part, type)) {
-        warnf(config->global, "curl_mime_type failed!\n");
-        Curl_safefree(contents);
-        return 29;
-      }
-      if(curl_mime_encoder(part, encoder)) {
-        warnf(config->global, "curl_mime_encoder failed!\n");
-        Curl_safefree(contents);
-        return 30;
-      }
+      SET_TOOL_MIME_PTR(part, filename, 19);
+      SET_TOOL_MIME_PTR(part, type, 20);
+      SET_TOOL_MIME_PTR(part, encoder, 21);
 
       if(sep) {
         *contp = (char) sep;
@@ -846,16 +803,12 @@ int formparse(struct OperationConfig *config,
     }
 
     /* Set part name. */
-    if(name && curl_mime_name(part, name)) {
-      warnf(config->global, "curl_mime_name failed!\n");
-      Curl_safefree(contents);
-      return 31;
-    }
+    SET_TOOL_MIME_PTR(part, name, 22);
   }
   else {
     warnf(config->global, "Illegally formatted input field!\n");
     Curl_safefree(contents);
-    return 32;
+    return 23;
   }
   Curl_safefree(contents);
   return 0;
index cdf02d028b152078448fc5f770ba7f27a3fb256f..203f5ab3028d8ff5f5284d030164a5077a42485b 100644 (file)
@@ -7,7 +7,7 @@
  *                            | (__| |_| |  _ <| |___
  *                             \___|\___/|_| \_\_____|
  *
- * Copyright (C) 1998 - 2017, Daniel Stenberg, <daniel@haxx.se>, et al.
+ * Copyright (C) 1998 - 2019, Daniel Stenberg, <daniel@haxx.se>, et al.
  *
  * This software is licensed as described in the file COPYING, which
  * you should have received as part of this distribution. The terms
  ***************************************************************************/
 #include "tool_setup.h"
 
+/* Private structure for mime/parts. */
+
+typedef enum {
+  TOOLMIME_NONE = 0,
+  TOOLMIME_PARTS,
+  TOOLMIME_DATA,
+  TOOLMIME_FILE,
+  TOOLMIME_FILEDATA,
+  TOOLMIME_STDIN,
+  TOOLMIME_STDINDATA
+} toolmimekind;
+
+typedef struct tool_mime        tool_mime;
+struct tool_mime {
+  /* Structural fields. */
+  toolmimekind kind;            /* Part kind. */
+  tool_mime *parent;            /* Parent item. */
+  tool_mime *prev;              /* Previous sibling (reverse order link). */
+  /* Common fields. */
+  const char *data;             /* Actual data or data filename. */
+  const char *name;             /* Part name. */
+  const char *filename;         /* Part's filename. */
+  const char *type;             /* Part's mime type. */
+  const char *encoder;          /* Part's requested encoding. */
+  struct curl_slist *headers;   /* User-defined headers. */
+  /* TOOLMIME_PARTS fields. */
+  tool_mime *subparts;          /* Part's subparts. */
+  curl_mime *handle;            /* Libcurl mime handle. */
+  /* TOOLMIME_STDIN/TOOLMIME_STDINDATA fields. */
+  curl_off_t origin;            /* Stdin read origin offset. */
+  curl_off_t size;              /* Stdin data size. */
+  curl_off_t curpos;            /* Stdin current read position. */
+  struct GlobalConfig *config;  /* For access from callback. */
+};
+
+size_t tool_mime_stdin_read(char *buffer,
+                            size_t size, size_t nitems, void *arg);
+int tool_mime_stdin_seek(void *instream, curl_off_t offset, int whence);
+
 int formparse(struct OperationConfig *config,
               const char *input,
-              curl_mime **mimepost,
-              curl_mime **mimecurrent,
+              tool_mime **mimepost,
+              tool_mime **mimecurrent,
               bool literal_value);
+void tool_mime_free(tool_mime *mime);
 
 #endif /* HEADER_CURL_TOOL_FORMPARSE_H */
index cd28ad8291e48c16595163b6e20cfd29f730f5ab..878bea2b4703905816227a3be18a32716a5c2311 100644 (file)
@@ -5,7 +5,7 @@
  *                            | (__| |_| |  _ <| |___
  *                             \___|\___/|_| \_\_____|
  *
- * Copyright (C) 1998 - 2018, Daniel Stenberg, <daniel@haxx.se>, et al.
+ * Copyright (C) 1998 - 2019, Daniel Stenberg, <daniel@haxx.se>, et al.
  *
  * This software is licensed as described in the file COPYING, which
  * you should have received as part of this distribution. The terms
@@ -32,7 +32,6 @@
 #include "tool_setopt.h"
 #include "tool_convert.h"
 
-#include "mime.h"
 #include "memdebug.h" /* keep this as LAST include */
 
 /* Lookup tables for converting setopt values back to symbols */
@@ -187,6 +186,12 @@ static const NameValue setopt_nv_CURLNONZERODEFAULTS[] = {
   if(ret) \
     goto nomem; \
 } WHILE_FALSE
+#define NULL_CHECK(p) do { \
+  if(!p) { \
+    ret = CURLE_OUT_OF_MEMORY; \
+    goto nomem; \
+  } \
+} WHILE_FALSE
 
 #define DECL0(s) ADD((&easysrc_decl, s))
 #define DECL1(f,a) ADDF((&easysrc_decl, f,a))
@@ -406,174 +411,238 @@ static CURLcode libcurl_generate_slist(struct curl_slist *slist, int *slistno)
   return ret;
 }
 
-/* Generate source code for a mime structure. */
-static CURLcode libcurl_generate_mime(curl_mime *mime, int *mimeno)
+static CURLcode libcurl_generate_mime(CURL *curl,
+                                      struct GlobalConfig *config,
+                                      tool_mime *toolmime,
+                                      curl_mime **mime,
+                                      int *mimeno);     /* Forward. */
+
+/* Wrapper to build and generate source code for a mime part. */
+static CURLcode libcurl_generate_mime_part(CURL *curl,
+                                      struct GlobalConfig *config,
+                                      tool_mime *part,
+                                      curl_mime *mime,
+                                      int mimeno)
 {
   CURLcode ret = CURLE_OK;
-  int i;
-  curl_off_t size;
-  curl_mimepart *part;
-  char *filename;
+  curl_mimepart *mimepart;
+  int submimeno;
+  curl_mime *submime = NULL;
   char *escaped = NULL;
-  char *cp;
-  char *data;
-
-  /* May need several mime variables, so invent name */
-  *mimeno = ++easysrc_mime_count;
-
-  DECL1("curl_mime *mime%d;", *mimeno);
-  DATA1("mime%d = NULL;", *mimeno);
-  CODE1("mime%d = curl_mime_init(hnd);", *mimeno);
-  CLEAN1("curl_mime_free(mime%d);", *mimeno);
-  CLEAN1("mime%d = NULL;", *mimeno);
-  if(mime->firstpart) {
-    DECL1("curl_mimepart *part%d;", *mimeno);
-    for(part = mime->firstpart; part; part = part->nextpart) {
-      CODE2("part%d = curl_mime_addpart(mime%d);", *mimeno, *mimeno);
-      filename = part->filename;
-      switch(part->kind) {
-      case MIMEKIND_FILE:
-        Curl_safefree(escaped);
-        escaped = c_escape(part->data, CURL_ZERO_TERMINATED);
-        if(!escaped)
-          return CURLE_OUT_OF_MEMORY;
-        CODE2("curl_mime_filedata(part%d, \"%s\");", *mimeno, escaped);
-        if(!filename)
-          CODE1("curl_mime_filename(part%d, NULL);", *mimeno);
-        else {
-          /* Fast check to see if remote file name is base name. */
-          filename = part->data;
-          for(cp = filename; *cp; cp++)
-            if(*cp == '/' || *cp == '\\')
-              filename = cp + 1;
-          if(!part->filename || !strcmp(filename, part->filename))
-            filename = NULL;
-          else
-            filename = part->filename;
+  const char *data = NULL;
+  const char *filename = part->filename;
+
+  /* Parts are linked in reverse order. */
+  if(part->prev) {
+    ret = libcurl_generate_mime_part(curl, config, part->prev, mime, mimeno);
+    if(ret)
+      return ret;
+  }
+
+  /* Create the part. */
+  mimepart = curl_mime_addpart(mime);
+  NULL_CHECK(mimepart);
+  if(config->libcurl)
+    CODE2("part%d = curl_mime_addpart(mime%d);", mimeno, mimeno);
+
+  switch(part->kind) {
+  case TOOLMIME_PARTS:
+    ret = libcurl_generate_mime(curl, config, part, &submime, &submimeno);
+    if(!ret) {
+      ret = curl_mime_subparts(mimepart, submime);
+      if(!ret) {
+        submime = NULL;
+        if(config->libcurl) {
+          CODE2("curl_mime_subparts(part%d, mime%d);", mimeno, submimeno);
+          CODE1("mime%d = NULL;", submimeno);   /* Avoid freeing in CLEAN. */
         }
-        break;
-      case MIMEKIND_CALLBACK:
-        /* Can only be reading stdin in the current context. */
-        CODE1("curl_mime_data_cb(part%d, -1, (curl_read_callback) fread, \\",
-              *mimeno);
-        CODE0("                  (curl_seek_callback) fseek, NULL, stdin);");
-        break;
-      case MIMEKIND_DATA:
-#ifdef CURL_DOES_CONVERSIONS
-          /* Data is stored in ASCII and we want in in the host character
-             code. Convert it back for output. */
-          data = malloc(part->datasize + 1);
-          if(!data) {
-            ret = CURLE_OUT_OF_MEMORY;
-            goto nomem;
-          }
-          memcpy(data, part->data, part->datasize + 1);
-          ret = convert_from_network(data, strlen(data));
-          if(ret) {
-            Curl_safefree(data);
-            goto nomem;
-          }
-#else
-        data = part->data;
-#endif
+      }
+    }
+    break;
 
-        /* Are there any nul byte in data? */
-        for(cp = data; *cp; cp++)
-          ;
-        size = (cp == data + part->datasize)? (curl_off_t) -1: part->datasize;
-        Curl_safefree(escaped);
-        escaped = c_escape(data, (size_t) part->datasize);
+  case TOOLMIME_DATA:
 #ifdef CURL_DOES_CONVERSIONS
-        Curl_safefree(data);
+    if(config->libcurl) {
+      /* Data will be set in ASCII, thus issue a comment with clear text. */
+      escaped = c_escape(part->data, CURL_ZERO_TERMINATED);
+      NULL_CHECK(escaped);
+      CODE1("/* \"%s\" */", escaped);
+    }
+
+    /* Our data is always textual: convert it to ASCII. */
+    {
+      size_t size = strlen(part->data);
+      char *cp = malloc(size + 1);
+
+      NULL_CHECK(cp);
+      memcpy(cp, part->data, size + 1);
+      ret = convert_to_network(cp, size);
+      data = cp;
+    }
+#else
+    data = part->data;
 #endif
-        if(!escaped)
-          return CURLE_OUT_OF_MEMORY;
-        if(size >= 0)
-          CODE3("curl_mime_data(part%d, \"%s\", %" CURL_FORMAT_CURL_OFF_T ");",
-                                *mimeno, escaped, size);
-        else
-          CODE2("curl_mime_data(part%d, \"%s\", CURL_ZERO_TERMINATED);",
-                                *mimeno, escaped);
-        break;
-      case MIMEKIND_MULTIPART:
-        ret = libcurl_generate_mime(part->arg, &i);
-        if(ret)
-          goto nomem;
-        CODE2("curl_mime_subparts(part%d, mime%d);", *mimeno, i);
-        CODE1("mime%d = NULL;", i);   /* Avoid freeing in CLEAN sequence. */
-        break;
-      default:
-        /* Other cases not possible in this context. */
-        break;
+    if(!ret)
+      ret = curl_mime_data(mimepart, data, CURL_ZERO_TERMINATED);
+    if(!ret && config->libcurl) {
+      Curl_safefree(escaped);
+      escaped = c_escape(data, CURL_ZERO_TERMINATED);
+      NULL_CHECK(escaped);
+      CODE2("curl_mime_data(part%d, \"%s\", CURL_ZERO_TERMINATED);",
+                            mimeno, escaped);
+    }
+    break;
+
+  case TOOLMIME_FILE:
+  case TOOLMIME_FILEDATA:
+    ret = curl_mime_filedata(mimepart, part->data);
+    if(!ret && config->libcurl) {
+      escaped = c_escape(part->data, CURL_ZERO_TERMINATED);
+      NULL_CHECK(escaped);
+      CODE2("curl_mime_filedata(part%d, \"%s\");", mimeno, escaped);
+    }
+    if(!ret && part->kind == TOOLMIME_FILEDATA && !filename) {
+      ret = curl_mime_filename(mimepart, NULL);
+      if(!ret && config->libcurl)
+        CODE1("curl_mime_filename(part%d, NULL);", mimeno);
+    }
+    break;
+
+  case TOOLMIME_STDIN:
+    if(!filename)
+      filename = "-";
+    /* FALLTHROUGH */
+  case TOOLMIME_STDINDATA:
+    part->config = config;
+    ret = curl_mime_data_cb(mimepart, part->size,
+                            (curl_read_callback) tool_mime_stdin_read,
+                            (curl_seek_callback) tool_mime_stdin_seek,
+                            NULL, part);
+    if(!ret && config->libcurl) {
+      /* Can only be reading stdin in the current context. */
+      CODE1("curl_mime_data_cb(part%d, -1, (curl_read_callback) fread, \\",
+            mimeno);
+      CODE0("                  (curl_seek_callback) fseek, NULL, stdin);");
       }
+    break;
+  default:
+    /* Other cases not possible in this context. */
+    break;
+  }
 
-      if(part->encoder) {
-        Curl_safefree(escaped);
-        escaped = c_escape(part->encoder->name, CURL_ZERO_TERMINATED);
-        if(!escaped)
-          return CURLE_OUT_OF_MEMORY;
-        CODE2("curl_mime_encoder(part%d, \"%s\");", *mimeno, escaped);
-      }
+  if(!ret && part->encoder) {
+    ret = curl_mime_encoder(mimepart, part->encoder);
+    if(!ret && config->libcurl) {
+      Curl_safefree(escaped);
+      escaped = c_escape(part->encoder, CURL_ZERO_TERMINATED);
+      NULL_CHECK(escaped);
+      CODE2("curl_mime_encoder(part%d, \"%s\");", mimeno, escaped);
+    }
+  }
 
-      if(filename) {
-        Curl_safefree(escaped);
-        escaped = c_escape(filename, CURL_ZERO_TERMINATED);
-        if(!escaped)
-          return CURLE_OUT_OF_MEMORY;
-        CODE2("curl_mime_filename(part%d, \"%s\");", *mimeno, escaped);
-      }
+  if(!ret && filename) {
+    ret = curl_mime_filename(mimepart, filename);
+    if(!ret && config->libcurl) {
+      Curl_safefree(escaped);
+      escaped = c_escape(filename, CURL_ZERO_TERMINATED);
+      NULL_CHECK(escaped);
+      CODE2("curl_mime_filename(part%d, \"%s\");", mimeno, escaped);
+    }
+  }
 
-      if(part->name) {
-        Curl_safefree(escaped);
-        escaped = c_escape(part->name, CURL_ZERO_TERMINATED);
-        if(!escaped)
-          return CURLE_OUT_OF_MEMORY;
-        CODE2("curl_mime_name(part%d, \"%s\");", *mimeno, escaped);
-      }
+  if(!ret && part->name) {
+    ret = curl_mime_name(mimepart, part->name);
+    if(!ret && config->libcurl) {
+      Curl_safefree(escaped);
+      escaped = c_escape(part->name, CURL_ZERO_TERMINATED);
+      NULL_CHECK(escaped);
+      CODE2("curl_mime_name(part%d, \"%s\");", mimeno, escaped);
+    }
+  }
 
-      if(part->mimetype) {
-        Curl_safefree(escaped);
-        escaped = c_escape(part->mimetype, CURL_ZERO_TERMINATED);
-        if(!escaped)
-          return CURLE_OUT_OF_MEMORY;
-        CODE2("curl_mime_type(part%d, \"%s\");", *mimeno, escaped);
-      }
+  if(!ret && part->type) {
+    ret = curl_mime_type(mimepart, part->type);
+    if(!ret && config->libcurl) {
+      Curl_safefree(escaped);
+      escaped = c_escape(part->type, CURL_ZERO_TERMINATED);
+      NULL_CHECK(escaped);
+      CODE2("curl_mime_type(part%d, \"%s\");", mimeno, escaped);
+    }
+  }
 
-      if(part->userheaders) {
-        int ownership = part->flags & MIME_USERHEADERS_OWNER? 1: 0;
+  if(!ret && part->headers) {
+    ret = curl_mime_headers(mimepart, part->headers, 0);
+    if(!ret && config->libcurl) {
+      int slistno;
 
-        ret = libcurl_generate_slist(part->userheaders, &i);
-        if(ret)
-          goto nomem;
-        CODE3("curl_mime_headers(part%d, slist%d, %d);",
-              *mimeno, i, ownership);
-        if(ownership)
-          CODE1("slist%d = NULL;", i); /* Prevent freeing in CLEAN sequence. */
+      ret = libcurl_generate_slist(part->headers, &slistno);
+      if(!ret) {
+        CODE2("curl_mime_headers(part%d, slist%d, 1);", mimeno, slistno);
+        CODE1("slist%d = NULL;", slistno); /* Prevent CLEANing. */
       }
     }
   }
 
 nomem:
+#ifdef CURL_DOES_CONVERSIONS
+  if(data)
+    free((char *) data);
+#endif
+
+  curl_mime_free(submime);
   Curl_safefree(escaped);
   return ret;
 }
 
+/* Wrapper to build and generate source code for a mime structure. */
+static CURLcode libcurl_generate_mime(CURL *curl,
+                                      struct GlobalConfig *config,
+                                      tool_mime *toolmime,
+                                      curl_mime **mime,
+                                      int *mimeno)
+{
+  CURLcode ret = CURLE_OK;
+
+  *mime = curl_mime_init(curl);
+  NULL_CHECK(*mime);
+
+  if(config->libcurl) {
+    /* May need several mime variables, so invent name. */
+    *mimeno = ++easysrc_mime_count;
+    DECL1("curl_mime *mime%d;", *mimeno);
+    DATA1("mime%d = NULL;", *mimeno);
+    CODE1("mime%d = curl_mime_init(hnd);", *mimeno);
+    CLEAN1("curl_mime_free(mime%d);", *mimeno);
+    CLEAN1("mime%d = NULL;", *mimeno);
+  }
+
+  if(toolmime->subparts) {
+    if(config->libcurl)
+      DECL1("curl_mimepart *part%d;", *mimeno);
+    ret = libcurl_generate_mime_part(curl, config,
+                                     toolmime->subparts, *mime, *mimeno);
+  }
+
+nomem:
+  return ret;
+}
+
 /* setopt wrapper for CURLOPT_MIMEPOST */
 CURLcode tool_setopt_mimepost(CURL *curl, struct GlobalConfig *config,
                               const char *name, CURLoption tag,
-                              curl_mime *mimepost)
+                              tool_mime *mimepost)
 {
   CURLcode ret = CURLE_OK;
+  int mimeno = 0;
 
-  ret = curl_easy_setopt(curl, tag, mimepost);
-
-  if(config->libcurl && mimepost && !ret) {
-    int i;
+  ret = libcurl_generate_mime(curl, config, mimepost,
+                              &mimepost->handle, &mimeno);
 
-    ret = libcurl_generate_mime(mimepost, &i);
+  if(!ret) {
+    ret = curl_easy_setopt(curl, tag, mimepost->handle);
 
-    if(!ret)
-      CODE2("curl_easy_setopt(hnd, %s, mime%d);", name, i);
+    if(config->libcurl && !ret)
+      CODE2("curl_easy_setopt(hnd, %s, mime%d);", name, mimeno);
   }
 
 nomem:
@@ -685,10 +754,7 @@ CURLcode tool_setopt(CURL *curl, bool str, struct GlobalConfig *config,
     else {
       if(escape) {
         escaped = c_escape(value, CURL_ZERO_TERMINATED);
-        if(!escaped) {
-          ret = CURLE_OUT_OF_MEMORY;
-          goto nomem;
-        }
+        NULL_CHECK(escaped);
         CODE2("curl_easy_setopt(hnd, %s, \"%s\");", name, escaped);
       }
       else
index f8a52cd758674cffe9fe3a32b2e37c1cee0449f8..4a03a06fce71a8a74d9c1edf508071dac8f5a681 100644 (file)
@@ -7,7 +7,7 @@
  *                            | (__| |_| |  _ <| |___
  *                             \___|\___/|_| \_\_____|
  *
- * Copyright (C) 1998 - 2017, Daniel Stenberg, <daniel@haxx.se>, et al.
+ * Copyright (C) 1998 - 2019, Daniel Stenberg, <daniel@haxx.se>, et al.
  *
  * This software is licensed as described in the file COPYING, which
  * you should have received as part of this distribution. The terms
@@ -23,6 +23,8 @@
  ***************************************************************************/
 #include "tool_setup.h"
 
+#include "tool_formparse.h"
+
 /*
  * Macros used in operate()
  */
@@ -87,7 +89,7 @@ CURLcode tool_setopt_bitmask(CURL *curl, struct GlobalConfig *config,
                              const NameValueUnsigned *nv, long lval);
 CURLcode tool_setopt_mimepost(CURL *curl, struct GlobalConfig *config,
                               const char *name, CURLoption tag,
-                              curl_mime *mimepost);
+                              tool_mime *mimepost);
 CURLcode tool_setopt_slist(CURL *curl, struct GlobalConfig *config,
                            const char *name, CURLoption tag,
                            struct curl_slist *list);
index a00bf10a341f722087dcbec272e715b13d686f90..a7b8cc823257cf0be7306d0239a53882b89a4616 100644 (file)
@@ -94,6 +94,8 @@ s/(USERAGENT, \")[^\"]+/${1}stripped/
 $_ = '' if /CURLOPT_SSL_VERIFYPEER/
 $_ = '' if /CURLOPT_SSH_KNOWNHOSTS/
 $_ = '' if /CURLOPT_HTTP_VERSION/
+# CURL_DOES_CONVERSION generates an extra comment.
+$_ = '' if /\/\* "value" \*\//
 </stripfile>
 <file name="log/test1404.c" mode="text">
 /********* Sample code generated by the curl command line tool **********