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