xref: /curl/src/tool_formparse.c (revision 3829759b)
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. */
tool_mime_new(struct tool_mime * parent,toolmimekind kind)42 static 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 
tool_mime_new_parts(struct tool_mime * parent)58 static struct tool_mime *tool_mime_new_parts(struct tool_mime *parent)
59 {
60   return tool_mime_new(parent, TOOLMIME_PARTS);
61 }
62 
tool_mime_new_data(struct tool_mime * parent,char * mime_data)63 static 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 
tool_mime_new_filedata(struct tool_mime * parent,const char * filename,bool isremotefile,CURLcode * errcode)80 static 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 
tool_mime_free(struct tool_mime * mime)161 void 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. */
tool_mime_stdin_read(char * buffer,size_t size,size_t nitems,void * arg)180 size_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 
tool_mime_stdin_seek(void * instream,curl_off_t offset,int whence)216 int 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 
tool2curlparts(CURL * curl,struct tool_mime * m,curl_mime * mime)240 static 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 
tool2curlmime(CURL * curl,struct tool_mime * m,curl_mime ** mime)308 CURLcode 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  */
get_param_word(struct OperationConfig * config,char ** str,char ** end_pos,char endchar)329 static 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. */
slist_append(struct curl_slist ** plist,const char * data)389 static 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. */
read_field_headers(struct OperationConfig * config,const char * filename,FILE * fp,struct curl_slist ** pheaders)401 static 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 
get_param_part(struct OperationConfig * config,char endchar,char ** str,char ** pdata,char ** ptype,char ** pfilename,char ** pencoder,struct curl_slist ** pheaders)462 static 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 
formparse(struct OperationConfig * config,const char * input,struct tool_mime ** mimeroot,struct tool_mime ** mimecurrent,bool literal_value)717 int 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;
903 fail:
904   Curl_safefree(contents);
905   curl_slist_free_all(headers);
906   return err;
907 }
908