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