Commit f82f952d authored by Patrick Monnerat's avatar Patrick Monnerat
Browse files

cli tool: in -F option arg, comma is a delimiter for files only

Also upgrade test 1133 to cover this case and clarify man page about
form data quoting.

Bug: https://github.com/curl/curl/issues/2022
Reported-By: omau on github
parent 7ee59512
Loading
Loading
Loading
Loading
+5 −0
Original line number Diff line number Diff line
@@ -57,6 +57,11 @@ or
Note that if a filename/path is quoted by double-quotes, any double-quote
or backslash within the filename must be escaped by backslash.

Quoting must also be applied to non-file data if it contains semicolons,
leading/trailing spaces or leading double quotes:

 curl -F 'colors="red; green; blue";type=text/x-myapp' example.com

You can add custom headers to the field by setting headers=, like

  curl -F "submit=OK;headers=\\"X-submit-type: OK\\"" example.com
+18 −17
Original line number Diff line number Diff line
@@ -52,13 +52,12 @@ typedef struct {
 * after call get_parm_word, str either point to string end
 * or point to any of end chars.
 */
static char *get_param_word(char **str, char **end_pos)
static char *get_param_word(char **str, char **end_pos, char endchar)
{
  char *ptr = *str;
  char *word_begin = NULL;
  char *ptr2;
  char *escape = NULL;
  const char *end_chars = ";,";

  /* the first non-space char is here */
  word_begin = ptr;
@@ -88,7 +87,7 @@ static char *get_param_word(char **str, char **end_pos)
          while(ptr < *end_pos);
          *end_pos = ptr2;
        }
        while(*ptr && NULL == strchr(end_chars, *ptr))
        while(*ptr && *ptr != ';' && *ptr != endchar)
          ++ptr;
        *str = ptr;
        return word_begin + 1;
@@ -99,7 +98,7 @@ static char *get_param_word(char **str, char **end_pos)
    ptr = word_begin;
  }

  while(*ptr && NULL == strchr(end_chars, *ptr))
  while(*ptr && *ptr != ';' && *ptr != endchar)
    ++ptr;
  *str = *end_pos = ptr;
  return word_begin;
@@ -181,9 +180,10 @@ static int read_field_headers(struct OperationConfig *config,
  /* NOTREACHED */
}

static int get_param_part(struct OperationConfig *config, char **str,
                          char **pdata, char **ptype, char **pfilename,
                          char **pencoder, struct curl_slist **pheaders)
static int get_param_part(struct OperationConfig *config, char endchar,
                          char **str, char **pdata, char **ptype,
                          char **pfilename, char **pencoder,
                          struct curl_slist **pheaders)
{
  char *p = *str;
  char *type = NULL;
@@ -208,7 +208,7 @@ static int get_param_part(struct OperationConfig *config, char **str,
  while(ISSPACE(*p))
    p++;
  tp = p;
  *pdata = get_param_word(&p, &endpos);
  *pdata = get_param_word(&p, &endpos, endchar);
  /* If not quoted, strip trailing spaces. */
  if(*pdata == tp)
    while(endpos > *pdata && ISSPACE(endpos[-1]))
@@ -249,7 +249,7 @@ static int get_param_part(struct OperationConfig *config, char **str,
      for(p += 9; ISSPACE(*p); p++)
        ;
      tp = p;
      filename = get_param_word(&p, &endpos);
      filename = get_param_word(&p, &endpos, endchar);
      /* If not quoted, strip trailing spaces. */
      if(filename == tp)
        while(endpos > filename && ISSPACE(endpos[-1]))
@@ -272,7 +272,7 @@ static int get_param_part(struct OperationConfig *config, char **str,
          p++;
        } while(ISSPACE(*p));
        tp = p;
        hdrfile = get_param_word(&p, &endpos);
        hdrfile = get_param_word(&p, &endpos, endchar);
        /* If not quoted, strip trailing spaces. */
        if(hdrfile == tp)
          while(endpos > hdrfile && ISSPACE(endpos[-1]))
@@ -300,7 +300,7 @@ static int get_param_part(struct OperationConfig *config, char **str,
        while(ISSPACE(*p))
          p++;
        tp = p;
        hdr = get_param_word(&p, &endpos);
        hdr = get_param_word(&p, &endpos, endchar);
        /* If not quoted, strip trailing spaces. */
        if(hdr == tp)
          while(endpos > hdr && ISSPACE(endpos[-1]))
@@ -322,7 +322,7 @@ static int get_param_part(struct OperationConfig *config, char **str,
      for(p += 8; ISSPACE(*p); p++)
        ;
      tp = p;
      encoder = get_param_word(&p, &endpos);
      encoder = get_param_word(&p, &endpos, endchar);
      /* If not quoted, strip trailing spaces. */
      if(encoder == tp)
        while(endpos > encoder && ISSPACE(endpos[-1]))
@@ -332,7 +332,7 @@ static int get_param_part(struct OperationConfig *config, char **str,
    }
    else {
      /* unknown prefix, skip to next block */
      char *unknown = get_param_word(&p, &endpos);
      char *unknown = get_param_word(&p, &endpos, endchar);

      sep = *p;
      if(endct)
@@ -598,7 +598,8 @@ int formparse(struct OperationConfig *config,
      curl_mime *subparts;

      /* Starting a multipart. */
      sep = get_param_part(config, &contp, &data, &type, NULL, NULL, &headers);
      sep = get_param_part(config, '\0',
                           &contp, &data, &type, NULL, NULL, &headers);
      if(sep < 0) {
        Curl_safefree(contents);
        return 3;
@@ -657,7 +658,7 @@ int formparse(struct OperationConfig *config,
        /* since this was a file, it may have a content-type specifier
           at the end too, or a filename. Or both. */
        ++contp;
        sep = get_param_part(config, &contp,
        sep = get_param_part(config, ',', &contp,
                             &data, &type, &filename, &encoder, &headers);
        if(sep < 0) {
          if(subparts != *mimecurrent)
@@ -767,7 +768,7 @@ int formparse(struct OperationConfig *config,

      if(*contp == '<' && !literal_value) {
        ++contp;
        sep = get_param_part(config, &contp,
        sep = get_param_part(config, '\0', &contp,
                             &data, &type, NULL, &encoder, &headers);
        if(sep < 0) {
          Curl_safefree(contents);
@@ -796,7 +797,7 @@ int formparse(struct OperationConfig *config,
        if(literal_value)
          data = contp;
        else {
          sep = get_param_part(config, &contp,
          sep = get_param_part(config, '\0', &contp,
                               &data, &type, &filename, &encoder, &headers);
          if(sep < 0) {
            Curl_safefree(contents);
+11 −3
Original line number Diff line number Diff line
@@ -23,10 +23,10 @@ blablabla
http
</server>
 <name>
HTTP RFC1867-type formposting with filename contains ',', ';', '"'
HTTP RFC1867-type formposting with filename/data contains ',', ';', '"'
 </name>
 <command>
http://%HOSTIP:%HTTPPORT/we/want/1133 -F "file=@\"log/test1133,a\\\"nd;.txt\";type=mo/foo;filename=\"faker,and;.txt\"" -F 'file2=@"log/test1133,a\"nd;.txt"' -F 'file3=@"log/test1133,a\"nd;.txt";type=m/f,"log/test1133,a\"nd;.txt"'
http://%HOSTIP:%HTTPPORT/we/want/1133 -F "file=@\"log/test1133,a\\\"nd;.txt\";type=mo/foo;filename=\"faker,and;.txt\"" -F 'file2=@"log/test1133,a\"nd;.txt"' -F 'file3=@"log/test1133,a\"nd;.txt";type=m/f,"log/test1133,a\"nd;.txt"' -F a="{\"field1\":\"value1\",\"field2\":\"value2\"}" -F 'b=" \\value1;type=\"whatever\" "; type=text/foo; charset=utf-8 ; filename=param_b'
</command>
# We create this file before the command is invoked!
<file name=log/test1133,a"nd;.txt>
@@ -47,7 +47,8 @@ POST /we/want/1133 HTTP/1.1
User-Agent: curl/7.10.4 (i686-pc-linux-gnu) libcurl/7.10.4 OpenSSL/0.9.7a ipv6 zlib/1.1.3
Host: %HOSTIP:%HTTPPORT
Accept: */*
Content-Length: 969
Content-Length: 1270
Expect: 100-continue
Content-Type: multipart/form-data; boundary=----------------------------24e78000bd32

------------------------------24e78000bd32
@@ -89,6 +90,13 @@ bar
foo


Content-Disposition: form-data; name="a"

{"field1":"value1","field2":"value2"}
Content-Disposition: form-data; name="b"; filename="param_b"
Content-Type: text/foo; charset=utf-8

 \value1;type="whatever" 
------------------------------24e78000bd32--
</protocol>
</verify>