xref: /third_party/curl/src/tool_formparse.c (revision 13498266)
1/***************************************************************************
2 *                                  _   _ ____  _
3 *  Project                     ___| | | |  _ \| |
4 *                             / __| | | | |_) | |
5 *                            | (__| |_| |  _ <| |___
6 *                             \___|\___/|_| \_\_____|
7 *
8 * Copyright (C) Daniel Stenberg, <daniel@haxx.se>, et al.
9 *
10 * This software is licensed as described in the file COPYING, which
11 * you should have received as part of this distribution. The terms
12 * are also available at https://curl.se/docs/copyright.html.
13 *
14 * You may opt to use, copy, modify, merge, publish, distribute and/or sell
15 * copies of the Software, and permit persons to whom the Software is
16 * furnished to do so, under the terms of the COPYING file.
17 *
18 * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY
19 * KIND, either express or implied.
20 *
21 * SPDX-License-Identifier: curl
22 *
23 ***************************************************************************/
24#include "tool_setup.h"
25
26#include "strcase.h"
27
28#define ENABLE_CURLX_PRINTF
29/* use our own printf() functions */
30#include "curlx.h"
31
32#include "tool_cfgable.h"
33#include "tool_msgs.h"
34#include "tool_binmode.h"
35#include "tool_getparam.h"
36#include "tool_paramhlp.h"
37#include "tool_formparse.h"
38
39#include "memdebug.h" /* keep this as LAST include */
40
41/* tool_mime functions. */
42static struct tool_mime *tool_mime_new(struct tool_mime *parent,
43                                       toolmimekind kind)
44{
45  struct tool_mime *m = (struct tool_mime *) calloc(1, sizeof(*m));
46
47  if(m) {
48    m->kind = kind;
49    m->parent = parent;
50    if(parent) {
51      m->prev = parent->subparts;
52      parent->subparts = m;
53    }
54  }
55  return m;
56}
57
58static struct tool_mime *tool_mime_new_parts(struct tool_mime *parent)
59{
60  return tool_mime_new(parent, TOOLMIME_PARTS);
61}
62
63static struct tool_mime *tool_mime_new_data(struct tool_mime *parent,
64                                            char *mime_data)
65{
66  char *mime_data_copy;
67  struct tool_mime *m = NULL;
68
69  mime_data_copy = strdup(mime_data);
70  if(mime_data_copy) {
71    m = tool_mime_new(parent, TOOLMIME_DATA);
72    if(!m)
73      free(mime_data_copy);
74    else
75      m->data = mime_data_copy;
76  }
77  return m;
78}
79
80static struct tool_mime *tool_mime_new_filedata(struct tool_mime *parent,
81                                                const char *filename,
82                                                bool isremotefile,
83                                                CURLcode *errcode)
84{
85  CURLcode result = CURLE_OK;
86  struct tool_mime *m = NULL;
87
88  *errcode = CURLE_OUT_OF_MEMORY;
89  if(strcmp(filename, "-")) {
90    /* This is a normal file. */
91    char *filedup = strdup(filename);
92    if(filedup) {
93      m = tool_mime_new(parent, TOOLMIME_FILE);
94      if(!m)
95        free(filedup);
96      else {
97        m->data = filedup;
98        if(!isremotefile)
99          m->kind = TOOLMIME_FILEDATA;
100       *errcode = CURLE_OK;
101      }
102    }
103  }
104  else {        /* Standard input. */
105    int fd = fileno(stdin);
106    char *data = NULL;
107    curl_off_t size;
108    curl_off_t origin;
109    struct_stat sbuf;
110
111    set_binmode(stdin);
112    origin = ftell(stdin);
113    /* If stdin is a regular file, do not buffer data but read it
114       when needed. */
115    if(fd >= 0 && origin >= 0 && !fstat(fd, &sbuf) &&
116#ifdef __VMS
117       sbuf.st_fab_rfm != FAB$C_VAR && sbuf.st_fab_rfm != FAB$C_VFC &&
118#endif
119       S_ISREG(sbuf.st_mode)) {
120      size = sbuf.st_size - origin;
121      if(size < 0)
122        size = 0;
123    }
124    else {  /* Not suitable for direct use, buffer stdin data. */
125      size_t stdinsize = 0;
126
127      switch(file2memory(&data, &stdinsize, stdin)) {
128      case PARAM_NO_MEM:
129        return m;
130      case PARAM_READ_ERROR:
131        result = CURLE_READ_ERROR;
132        break;
133      default:
134        if(!stdinsize) {
135          /* Zero-length data has been freed. Re-create it. */
136          data = strdup("");
137          if(!data)
138            return m;
139        }
140        break;
141      }
142      size = curlx_uztoso(stdinsize);
143      origin = 0;
144    }
145    m = tool_mime_new(parent, TOOLMIME_STDIN);
146    if(!m)
147      Curl_safefree(data);
148    else {
149      m->data = data;
150      m->origin = origin;
151      m->size = size;
152      m->curpos = 0;
153      if(!isremotefile)
154        m->kind = TOOLMIME_STDINDATA;
155      *errcode = result;
156    }
157  }
158  return m;
159}
160
161void tool_mime_free(struct tool_mime *mime)
162{
163  if(mime) {
164    if(mime->subparts)
165      tool_mime_free(mime->subparts);
166    if(mime->prev)
167      tool_mime_free(mime->prev);
168    Curl_safefree(mime->name);
169    Curl_safefree(mime->filename);
170    Curl_safefree(mime->type);
171    Curl_safefree(mime->encoder);
172    Curl_safefree(mime->data);
173    curl_slist_free_all(mime->headers);
174    free(mime);
175  }
176}
177
178
179/* Mime part callbacks for stdin. */
180size_t tool_mime_stdin_read(char *buffer,
181                            size_t size, size_t nitems, void *arg)
182{
183  struct tool_mime *sip = (struct tool_mime *) arg;
184  curl_off_t bytesleft;
185  (void) size;  /* Always 1: ignored. */
186
187  if(sip->size >= 0) {
188    if(sip->curpos >= sip->size)
189      return 0;  /* At eof. */
190    bytesleft = sip->size - sip->curpos;
191    if(curlx_uztoso(nitems) > bytesleft)
192      nitems = curlx_sotouz(bytesleft);
193  }
194  if(nitems) {
195    if(sip->data) {
196      /* Return data from memory. */
197      memcpy(buffer, sip->data + curlx_sotouz(sip->curpos), nitems);
198    }
199    else {
200      /* Read from stdin. */
201      nitems = fread(buffer, 1, nitems, stdin);
202      if(ferror(stdin)) {
203        /* Show error only once. */
204        if(sip->config) {
205          warnf(sip->config, "stdin: %s", strerror(errno));
206          sip->config = NULL;
207        }
208        return CURL_READFUNC_ABORT;
209      }
210    }
211    sip->curpos += curlx_uztoso(nitems);
212  }
213  return nitems;
214}
215
216int tool_mime_stdin_seek(void *instream, curl_off_t offset, int whence)
217{
218  struct tool_mime *sip = (struct tool_mime *) instream;
219
220  switch(whence) {
221  case SEEK_CUR:
222    offset += sip->curpos;
223    break;
224  case SEEK_END:
225    offset += sip->size;
226    break;
227  }
228  if(offset < 0)
229    return CURL_SEEKFUNC_CANTSEEK;
230  if(!sip->data) {
231    if(fseek(stdin, (long) (offset + sip->origin), SEEK_SET))
232      return CURL_SEEKFUNC_CANTSEEK;
233  }
234  sip->curpos = offset;
235  return CURL_SEEKFUNC_OK;
236}
237
238/* Translate an internal mime tree into a libcurl mime tree. */
239
240static CURLcode tool2curlparts(CURL *curl, struct tool_mime *m,
241                               curl_mime *mime)
242{
243  CURLcode ret = CURLE_OK;
244  curl_mimepart *part = NULL;
245  curl_mime *submime = NULL;
246  const char *filename = NULL;
247
248  if(m) {
249    ret = tool2curlparts(curl, m->prev, mime);
250    if(!ret) {
251      part = curl_mime_addpart(mime);
252      if(!part)
253        ret = CURLE_OUT_OF_MEMORY;
254    }
255    if(!ret) {
256      filename = m->filename;
257      switch(m->kind) {
258      case TOOLMIME_PARTS:
259        ret = tool2curlmime(curl, m, &submime);
260        if(!ret) {
261          ret = curl_mime_subparts(part, submime);
262          if(ret)
263            curl_mime_free(submime);
264        }
265        break;
266
267      case TOOLMIME_DATA:
268        ret = curl_mime_data(part, m->data, CURL_ZERO_TERMINATED);
269        break;
270
271      case TOOLMIME_FILE:
272      case TOOLMIME_FILEDATA:
273        ret = curl_mime_filedata(part, m->data);
274        if(!ret && m->kind == TOOLMIME_FILEDATA && !filename)
275          ret = curl_mime_filename(part, NULL);
276        break;
277
278      case TOOLMIME_STDIN:
279        if(!filename)
280          filename = "-";
281        FALLTHROUGH();
282      case TOOLMIME_STDINDATA:
283        ret = curl_mime_data_cb(part, m->size,
284                                (curl_read_callback) tool_mime_stdin_read,
285                                (curl_seek_callback) tool_mime_stdin_seek,
286                                NULL, m);
287        break;
288
289      default:
290        /* Other cases not possible in this context. */
291        break;
292      }
293    }
294    if(!ret && filename)
295      ret = curl_mime_filename(part, filename);
296    if(!ret)
297      ret = curl_mime_type(part, m->type);
298    if(!ret)
299      ret = curl_mime_headers(part, m->headers, 0);
300    if(!ret)
301      ret = curl_mime_encoder(part, m->encoder);
302    if(!ret)
303      ret = curl_mime_name(part, m->name);
304  }
305  return ret;
306}
307
308CURLcode tool2curlmime(CURL *curl, struct tool_mime *m, curl_mime **mime)
309{
310  CURLcode ret = CURLE_OK;
311
312  *mime = curl_mime_init(curl);
313  if(!*mime)
314    ret = CURLE_OUT_OF_MEMORY;
315  else
316    ret = tool2curlparts(curl, m->subparts, *mime);
317  if(ret) {
318    curl_mime_free(*mime);
319    *mime = NULL;
320  }
321  return ret;
322}
323
324/*
325 * helper function to get a word from form param
326 * after call get_parm_word, str either point to string end
327 * or point to any of end chars.
328 */
329static char *get_param_word(struct OperationConfig *config, char **str,
330                            char **end_pos, char endchar)
331{
332  char *ptr = *str;
333  /* the first non-space char is here */
334  char *word_begin = ptr;
335  char *ptr2;
336  char *escape = NULL;
337
338  if(*ptr == '"') {
339    ++ptr;
340    while(*ptr) {
341      if(*ptr == '\\') {
342        if(ptr[1] == '\\' || ptr[1] == '"') {
343          /* remember the first escape position */
344          if(!escape)
345            escape = ptr;
346          /* skip escape of back-slash or double-quote */
347          ptr += 2;
348          continue;
349        }
350      }
351      if(*ptr == '"') {
352        bool trailing_data = FALSE;
353        *end_pos = ptr;
354        if(escape) {
355          /* has escape, we restore the unescaped string here */
356          ptr = ptr2 = escape;
357          do {
358            if(*ptr == '\\' && (ptr[1] == '\\' || ptr[1] == '"'))
359              ++ptr;
360            *ptr2++ = *ptr++;
361          }
362          while(ptr < *end_pos);
363          *end_pos = ptr2;
364        }
365        ++ptr;
366        while(*ptr && *ptr != ';' && *ptr != endchar) {
367          if(!ISSPACE(*ptr))
368            trailing_data = TRUE;
369          ++ptr;
370        }
371        if(trailing_data)
372          warnf(config->global, "Trailing data after quoted form parameter");
373        *str = ptr;
374        return word_begin + 1;
375      }
376      ++ptr;
377    }
378    /* end quote is missing, treat it as non-quoted. */
379    ptr = word_begin;
380  }
381
382  while(*ptr && *ptr != ';' && *ptr != endchar)
383    ++ptr;
384  *str = *end_pos = ptr;
385  return word_begin;
386}
387
388/* Append slist item and return -1 if failed. */
389static int slist_append(struct curl_slist **plist, const char *data)
390{
391  struct curl_slist *s = curl_slist_append(*plist, data);
392
393  if(!s)
394    return -1;
395
396  *plist = s;
397  return 0;
398}
399
400/* Read headers from a file and append to list. */
401static int read_field_headers(struct OperationConfig *config,
402                              const char *filename, FILE *fp,
403                              struct curl_slist **pheaders)
404{
405  size_t hdrlen = 0;
406  size_t pos = 0;
407  bool incomment = FALSE;
408  int lineno = 1;
409  char hdrbuf[999] = ""; /* Max. header length + 1. */
410
411  for(;;) {
412    int c = getc(fp);
413    if(c == EOF || (!pos && !ISSPACE(c))) {
414      /* Strip and flush the current header. */
415      while(hdrlen && ISSPACE(hdrbuf[hdrlen - 1]))
416        hdrlen--;
417      if(hdrlen) {
418        hdrbuf[hdrlen] = '\0';
419        if(slist_append(pheaders, hdrbuf)) {
420          errorf(config->global, "Out of memory for field headers");
421          return -1;
422        }
423        hdrlen = 0;
424      }
425    }
426
427    switch(c) {
428    case EOF:
429      if(ferror(fp)) {
430        errorf(config->global, "Header file %s read error: %s", filename,
431               strerror(errno));
432        return -1;
433      }
434      return 0;    /* Done. */
435    case '\r':
436      continue;    /* Ignore. */
437    case '\n':
438      pos = 0;
439      incomment = FALSE;
440      lineno++;
441      continue;
442    case '#':
443      if(!pos)
444        incomment = TRUE;
445      break;
446    }
447
448    pos++;
449    if(!incomment) {
450      if(hdrlen == sizeof(hdrbuf) - 1) {
451        warnf(config->global, "File %s line %d: header too long (truncated)",
452              filename, lineno);
453        c = ' ';
454      }
455      if(hdrlen <= sizeof(hdrbuf) - 1)
456        hdrbuf[hdrlen++] = (char) c;
457    }
458  }
459  /* NOTREACHED */
460}
461
462static int get_param_part(struct OperationConfig *config, char endchar,
463                          char **str, char **pdata, char **ptype,
464                          char **pfilename, char **pencoder,
465                          struct curl_slist **pheaders)
466{
467  char *p = *str;
468  char *type = NULL;
469  char *filename = NULL;
470  char *encoder = NULL;
471  char *endpos;
472  char *tp;
473  char sep;
474  char type_major[128] = "";
475  char type_minor[128] = "";
476  char *endct = NULL;
477  struct curl_slist *headers = NULL;
478
479  if(ptype)
480    *ptype = NULL;
481  if(pfilename)
482    *pfilename = NULL;
483  if(pheaders)
484    *pheaders = NULL;
485  if(pencoder)
486    *pencoder = NULL;
487  while(ISSPACE(*p))
488    p++;
489  tp = p;
490  *pdata = get_param_word(config, &p, &endpos, endchar);
491  /* If not quoted, strip trailing spaces. */
492  if(*pdata == tp)
493    while(endpos > *pdata && ISSPACE(endpos[-1]))
494      endpos--;
495  sep = *p;
496  *endpos = '\0';
497  while(sep == ';') {
498    while(p++ && ISSPACE(*p))
499      ;
500
501    if(!endct && checkprefix("type=", p)) {
502      for(p += 5; ISSPACE(*p); p++)
503        ;
504      /* set type pointer */
505      type = p;
506
507      /* verify that this is a fine type specifier */
508      if(2 != sscanf(type, "%127[^/ ]/%127[^;, \n]", type_major, type_minor)) {
509        warnf(config->global, "Illegally formatted content-type field");
510        curl_slist_free_all(headers);
511        return -1; /* illegal content-type syntax! */
512      }
513
514      /* now point beyond the content-type specifier */
515      p = type + strlen(type_major) + strlen(type_minor) + 1;
516      for(endct = p; *p && *p != ';' && *p != endchar; p++)
517        if(!ISSPACE(*p))
518          endct = p + 1;
519      sep = *p;
520    }
521    else if(checkprefix("filename=", p)) {
522      if(endct) {
523        *endct = '\0';
524        endct = NULL;
525      }
526      for(p += 9; ISSPACE(*p); p++)
527        ;
528      tp = p;
529      filename = get_param_word(config, &p, &endpos, endchar);
530      /* If not quoted, strip trailing spaces. */
531      if(filename == tp)
532        while(endpos > filename && ISSPACE(endpos[-1]))
533          endpos--;
534      sep = *p;
535      *endpos = '\0';
536    }
537    else if(checkprefix("headers=", p)) {
538      if(endct) {
539        *endct = '\0';
540        endct = NULL;
541      }
542      p += 8;
543      if(*p == '@' || *p == '<') {
544        char *hdrfile;
545        FILE *fp;
546        /* Read headers from a file. */
547
548        do {
549          p++;
550        } while(ISSPACE(*p));
551        tp = p;
552        hdrfile = get_param_word(config, &p, &endpos, endchar);
553        /* If not quoted, strip trailing spaces. */
554        if(hdrfile == tp)
555          while(endpos > hdrfile && ISSPACE(endpos[-1]))
556            endpos--;
557        sep = *p;
558        *endpos = '\0';
559        fp = fopen(hdrfile, FOPEN_READTEXT);
560        if(!fp)
561          warnf(config->global, "Cannot read from %s: %s", hdrfile,
562                strerror(errno));
563        else {
564          int i = read_field_headers(config, hdrfile, fp, &headers);
565
566          fclose(fp);
567          if(i) {
568            curl_slist_free_all(headers);
569            return -1;
570          }
571        }
572      }
573      else {
574        char *hdr;
575
576        while(ISSPACE(*p))
577          p++;
578        tp = p;
579        hdr = get_param_word(config, &p, &endpos, endchar);
580        /* If not quoted, strip trailing spaces. */
581        if(hdr == tp)
582          while(endpos > hdr && ISSPACE(endpos[-1]))
583            endpos--;
584        sep = *p;
585        *endpos = '\0';
586        if(slist_append(&headers, hdr)) {
587          errorf(config->global, "Out of memory for field header");
588          curl_slist_free_all(headers);
589          return -1;
590        }
591      }
592    }
593    else if(checkprefix("encoder=", p)) {
594      if(endct) {
595        *endct = '\0';
596        endct = NULL;
597      }
598      for(p += 8; ISSPACE(*p); p++)
599        ;
600      tp = p;
601      encoder = get_param_word(config, &p, &endpos, endchar);
602      /* If not quoted, strip trailing spaces. */
603      if(encoder == tp)
604        while(endpos > encoder && ISSPACE(endpos[-1]))
605          endpos--;
606      sep = *p;
607      *endpos = '\0';
608    }
609    else if(endct) {
610      /* This is part of content type. */
611      for(endct = p; *p && *p != ';' && *p != endchar; p++)
612        if(!ISSPACE(*p))
613          endct = p + 1;
614      sep = *p;
615    }
616    else {
617      /* unknown prefix, skip to next block */
618      char *unknown = get_param_word(config, &p, &endpos, endchar);
619
620      sep = *p;
621      *endpos = '\0';
622      if(*unknown)
623        warnf(config->global, "skip unknown form field: %s", unknown);
624    }
625  }
626
627  /* Terminate content type. */
628  if(endct)
629    *endct = '\0';
630
631  if(ptype)
632    *ptype = type;
633  else if(type)
634    warnf(config->global, "Field content type not allowed here: %s", type);
635
636  if(pfilename)
637    *pfilename = filename;
638  else if(filename)
639    warnf(config->global,
640          "Field file name not allowed here: %s", filename);
641
642  if(pencoder)
643    *pencoder = encoder;
644  else if(encoder)
645    warnf(config->global,
646          "Field encoder not allowed here: %s", encoder);
647
648  if(pheaders)
649    *pheaders = headers;
650  else if(headers) {
651    warnf(config->global,
652          "Field headers not allowed here: %s", headers->data);
653    curl_slist_free_all(headers);
654  }
655
656  *str = p;
657  return sep & 0xFF;
658}
659
660
661/***************************************************************************
662 *
663 * formparse()
664 *
665 * Reads a 'name=value' parameter and builds the appropriate linked list.
666 *
667 * If the value is of the form '<filename', field data is read from the
668 * given file.
669
670 * Specify files to upload with 'name=@filename', or 'name=@"filename"'
671 * in case the filename contain ',' or ';'. Supports specified
672 * given Content-Type of the files. Such as ';type=<content-type>'.
673 *
674 * If literal_value is set, any initial '@' or '<' in the value string
675 * loses its special meaning, as does any embedded ';type='.
676 *
677 * You may specify more than one file for a single name (field). Specify
678 * multiple files by writing it like:
679 *
680 * 'name=@filename,filename2,filename3'
681 *
682 * or use double-quotes quote the filename:
683 *
684 * 'name=@"filename","filename2","filename3"'
685 *
686 * If you want content-types specified for each too, write them like:
687 *
688 * 'name=@filename;type=image/gif,filename2,filename3'
689 *
690 * If you want custom headers added for a single part, write them in a separate
691 * file and do like this:
692 *
693 * 'name=foo;headers=@headerfile' or why not
694 * 'name=@filemame;headers=@headerfile'
695 *
696 * To upload a file, but to fake the file name that will be included in the
697 * formpost, do like this:
698 *
699 * 'name=@filename;filename=/dev/null' or quote the faked filename like:
700 * 'name=@filename;filename="play, play, and play.txt"'
701 *
702 * If filename/path contains ',' or ';', it must be quoted by double-quotes,
703 * else curl will fail to figure out the correct filename. if the filename
704 * tobe quoted contains '"' or '\', '"' and '\' must be escaped by backslash.
705 *
706 ***************************************************************************/
707
708#define SET_TOOL_MIME_PTR(m, field)                                     \
709  do {                                                                  \
710    if(field) {                                                         \
711      (m)->field = strdup(field);                                       \
712      if(!(m)->field)                                                   \
713        goto fail;                                                      \
714    }                                                                   \
715  } while(0)
716
717int formparse(struct OperationConfig *config,
718              const char *input,
719              struct tool_mime **mimeroot,
720              struct tool_mime **mimecurrent,
721              bool literal_value)
722{
723  /* input MUST be a string in the format 'name=contents' and we'll
724     build a linked list with the info */
725  char *name = NULL;
726  char *contents = NULL;
727  char *contp;
728  char *data;
729  char *type = NULL;
730  char *filename = NULL;
731  char *encoder = NULL;
732  struct curl_slist *headers = NULL;
733  struct tool_mime *part = NULL;
734  CURLcode res;
735  int err = 1;
736
737  /* Allocate the main mime structure if needed. */
738  if(!*mimecurrent) {
739    *mimeroot = tool_mime_new_parts(NULL);
740    if(!*mimeroot)
741      goto fail;
742    *mimecurrent = *mimeroot;
743  }
744
745  /* Make a copy we can overwrite. */
746  contents = strdup(input);
747  if(!contents)
748    goto fail;
749
750  /* Scan for the end of the name. */
751  contp = strchr(contents, '=');
752  if(contp) {
753    int sep = '\0';
754    if(contp > contents)
755      name = contents;
756    *contp++ = '\0';
757
758    if(*contp == '(' && !literal_value) {
759      /* Starting a multipart. */
760      sep = get_param_part(config, '\0',
761                           &contp, &data, &type, NULL, NULL, &headers);
762      if(sep < 0)
763        goto fail;
764      part = tool_mime_new_parts(*mimecurrent);
765      if(!part)
766        goto fail;
767      *mimecurrent = part;
768      part->headers = headers;
769      headers = NULL;
770      SET_TOOL_MIME_PTR(part, type);
771    }
772    else if(!name && !strcmp(contp, ")") && !literal_value) {
773      /* Ending a multipart. */
774      if(*mimecurrent == *mimeroot) {
775        warnf(config->global, "no multipart to terminate");
776        goto fail;
777      }
778      *mimecurrent = (*mimecurrent)->parent;
779    }
780    else if('@' == contp[0] && !literal_value) {
781
782      /* we use the @-letter to indicate file name(s) */
783
784      struct tool_mime *subparts = NULL;
785
786      do {
787        /* since this was a file, it may have a content-type specifier
788           at the end too, or a filename. Or both. */
789        ++contp;
790        sep = get_param_part(config, ',', &contp,
791                             &data, &type, &filename, &encoder, &headers);
792        if(sep < 0) {
793          goto fail;
794        }
795
796        /* now contp point to comma or string end.
797           If more files to come, make sure we have multiparts. */
798        if(!subparts) {
799          if(sep != ',')    /* If there is a single file. */
800            subparts = *mimecurrent;
801          else {
802            subparts = tool_mime_new_parts(*mimecurrent);
803            if(!subparts)
804              goto fail;
805          }
806        }
807
808        /* Store that file in a part. */
809        part = tool_mime_new_filedata(subparts, data, TRUE, &res);
810        if(!part)
811          goto fail;
812        part->headers = headers;
813        headers = NULL;
814        part->config = config->global;
815        if(res == CURLE_READ_ERROR) {
816            /* An error occurred while reading stdin: if read has started,
817               issue the error now. Else, delay it until processed by
818               libcurl. */
819          if(part->size > 0) {
820            warnf(config->global,
821                  "error while reading standard input");
822            goto fail;
823          }
824          Curl_safefree(part->data);
825          part->data = NULL;
826          part->size = -1;
827          res = CURLE_OK;
828        }
829        SET_TOOL_MIME_PTR(part, filename);
830        SET_TOOL_MIME_PTR(part, type);
831        SET_TOOL_MIME_PTR(part, encoder);
832
833        /* *contp could be '\0', so we just check with the delimiter */
834      } while(sep); /* loop if there's another file name */
835      part = (*mimecurrent)->subparts;  /* Set name on group. */
836    }
837    else {
838      if(*contp == '<' && !literal_value) {
839        ++contp;
840        sep = get_param_part(config, '\0', &contp,
841                             &data, &type, NULL, &encoder, &headers);
842        if(sep < 0)
843          goto fail;
844
845        part = tool_mime_new_filedata(*mimecurrent, data, FALSE,
846                                      &res);
847        if(!part)
848          goto fail;
849        part->headers = headers;
850        headers = NULL;
851        part->config = config->global;
852        if(res == CURLE_READ_ERROR) {
853            /* An error occurred while reading stdin: if read has started,
854               issue the error now. Else, delay it until processed by
855               libcurl. */
856          if(part->size > 0) {
857            warnf(config->global,
858                  "error while reading standard input");
859            goto fail;
860          }
861          Curl_safefree(part->data);
862          part->data = NULL;
863          part->size = -1;
864          res = CURLE_OK;
865        }
866      }
867      else {
868        if(literal_value)
869          data = contp;
870        else {
871          sep = get_param_part(config, '\0', &contp,
872                               &data, &type, &filename, &encoder, &headers);
873          if(sep < 0)
874            goto fail;
875        }
876
877        part = tool_mime_new_data(*mimecurrent, data);
878        if(!part)
879          goto fail;
880        part->headers = headers;
881        headers = NULL;
882      }
883
884      SET_TOOL_MIME_PTR(part, filename);
885      SET_TOOL_MIME_PTR(part, type);
886      SET_TOOL_MIME_PTR(part, encoder);
887
888      if(sep) {
889        *contp = (char) sep;
890        warnf(config->global,
891              "garbage at end of field specification: %s", contp);
892      }
893    }
894
895    /* Set part name. */
896    SET_TOOL_MIME_PTR(part, name);
897  }
898  else {
899    warnf(config->global, "Illegally formatted input field");
900    goto fail;
901  }
902  err = 0;
903fail:
904  Curl_safefree(contents);
905  curl_slist_free_all(headers);
906  return err;
907}
908