1 /***************************************************************************
2 * _ _ ____ _
3 * Project ___| | | | _ \| |
4 * / __| | | | |_) | |
5 * | (__| |_| | _ <| |___
6 * \___|\___/|_| \_\_____|
7 *
8 * Copyright (C) 1998 - 2020, 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(struct tool_mime * parent,toolmimekind kind)45 static struct tool_mime *tool_mime_new(struct tool_mime *parent,
46 toolmimekind kind)
47 {
48 struct tool_mime *m = (struct tool_mime *) calloc(1, sizeof(*m));
49
50 if(m) {
51 m->kind = kind;
52 m->parent = parent;
53 if(parent) {
54 m->prev = parent->subparts;
55 parent->subparts = m;
56 }
57 }
58 return m;
59 }
60
tool_mime_new_parts(struct tool_mime * parent)61 static struct tool_mime *tool_mime_new_parts(struct tool_mime *parent)
62 {
63 return tool_mime_new(parent, TOOLMIME_PARTS);
64 }
65
tool_mime_new_data(struct tool_mime * parent,const char * data)66 static struct tool_mime *tool_mime_new_data(struct tool_mime *parent,
67 const char *data)
68 {
69 struct tool_mime *m = NULL;
70
71 data = strdup(data);
72 if(data) {
73 m = tool_mime_new(parent, TOOLMIME_DATA);
74 if(!m)
75 CONST_FREE(data);
76 else
77 m->data = data;
78 }
79 return m;
80 }
81
tool_mime_new_filedata(struct tool_mime * parent,const char * filename,bool isremotefile,CURLcode * errcode)82 static struct tool_mime *tool_mime_new_filedata(struct tool_mime *parent,
83 const char *filename,
84 bool isremotefile,
85 CURLcode *errcode)
86 {
87 CURLcode result = CURLE_OK;
88 struct tool_mime *m = NULL;
89
90 *errcode = CURLE_OUT_OF_MEMORY;
91 if(strcmp(filename, "-")) {
92 /* This is a normal file. */
93 filename = strdup(filename);
94 if(filename) {
95 m = tool_mime_new(parent, TOOLMIME_FILE);
96 if(!m)
97 CONST_FREE(filename);
98 else {
99 m->data = filename;
100 if(!isremotefile)
101 m->kind = TOOLMIME_FILEDATA;
102 *errcode = CURLE_OK;
103 }
104 }
105 }
106 else { /* Standard input. */
107 int fd = fileno(stdin);
108 char *data = NULL;
109 curl_off_t size;
110 curl_off_t origin;
111 struct_stat sbuf;
112
113 set_binmode(stdin);
114 origin = ftell(stdin);
115 /* If stdin is a regular file, do not buffer data but read it
116 when needed. */
117 if(fd >= 0 && origin >= 0 && !fstat(fd, &sbuf) &&
118 #ifdef __VMS
119 sbuf.st_fab_rfm != FAB$C_VAR && sbuf.st_fab_rfm != FAB$C_VFC &&
120 #endif
121 S_ISREG(sbuf.st_mode)) {
122 size = sbuf.st_size - origin;
123 if(size < 0)
124 size = 0;
125 }
126 else { /* Not suitable for direct use, buffer stdin data. */
127 size_t stdinsize = 0;
128
129 if(file2memory(&data, &stdinsize, stdin) != PARAM_OK) {
130 /* Out of memory. */
131 return m;
132 }
133
134 if(ferror(stdin)) {
135 result = CURLE_READ_ERROR;
136 Curl_safefree(data);
137 data = NULL;
138 }
139 else if(!stdinsize) {
140 /* Zero-length data has been freed. Re-create it. */
141 data = strdup("");
142 if(!data)
143 return m;
144 }
145 size = curlx_uztoso(stdinsize);
146 origin = 0;
147 }
148 m = tool_mime_new(parent, TOOLMIME_STDIN);
149 if(!m)
150 Curl_safefree(data);
151 else {
152 m->data = data;
153 m->origin = origin;
154 m->size = size;
155 m->curpos = 0;
156 if(!isremotefile)
157 m->kind = TOOLMIME_STDINDATA;
158 *errcode = result;
159 }
160 }
161 return m;
162 }
163
tool_mime_free(struct tool_mime * mime)164 void tool_mime_free(struct tool_mime *mime)
165 {
166 if(mime) {
167 if(mime->subparts)
168 tool_mime_free(mime->subparts);
169 if(mime->prev)
170 tool_mime_free(mime->prev);
171 CONST_SAFEFREE(mime->name);
172 CONST_SAFEFREE(mime->filename);
173 CONST_SAFEFREE(mime->type);
174 CONST_SAFEFREE(mime->encoder);
175 CONST_SAFEFREE(mime->data);
176 curl_slist_free_all(mime->headers);
177 free(mime);
178 }
179 }
180
181
182 /* Mime part callbacks for stdin. */
tool_mime_stdin_read(char * buffer,size_t size,size_t nitems,void * arg)183 size_t tool_mime_stdin_read(char *buffer,
184 size_t size, size_t nitems, void *arg)
185 {
186 struct tool_mime *sip = (struct tool_mime *) arg;
187 curl_off_t bytesleft;
188 (void) size; /* Always 1: ignored. */
189
190 if(sip->size >= 0) {
191 if(sip->curpos >= sip->size)
192 return 0; /* At eof. */
193 bytesleft = sip->size - sip->curpos;
194 if(curlx_uztoso(nitems) > bytesleft)
195 nitems = curlx_sotouz(bytesleft);
196 }
197 if(nitems) {
198 if(sip->data) {
199 /* Return data from memory. */
200 memcpy(buffer, sip->data + curlx_sotouz(sip->curpos), nitems);
201 }
202 else {
203 /* Read from stdin. */
204 nitems = fread(buffer, 1, nitems, stdin);
205 if(ferror(stdin)) {
206 /* Show error only once. */
207 if(sip->config) {
208 warnf(sip->config, "stdin: %s\n", strerror(errno));
209 sip->config = NULL;
210 }
211 return CURL_READFUNC_ABORT;
212 }
213 }
214 sip->curpos += curlx_uztoso(nitems);
215 }
216 return nitems;
217 }
218
tool_mime_stdin_seek(void * instream,curl_off_t offset,int whence)219 int tool_mime_stdin_seek(void *instream, curl_off_t offset, int whence)
220 {
221 struct tool_mime *sip = (struct tool_mime *) instream;
222
223 switch(whence) {
224 case SEEK_CUR:
225 offset += sip->curpos;
226 break;
227 case SEEK_END:
228 offset += sip->size;
229 break;
230 }
231 if(offset < 0)
232 return CURL_SEEKFUNC_CANTSEEK;
233 if(!sip->data) {
234 if(fseek(stdin, (long) (offset + sip->origin), SEEK_SET))
235 return CURL_SEEKFUNC_CANTSEEK;
236 }
237 sip->curpos = offset;
238 return CURL_SEEKFUNC_OK;
239 }
240
241 /* Translate an internal mime tree into a libcurl mime tree. */
242
tool2curlparts(CURL * curl,struct tool_mime * m,curl_mime * mime)243 static CURLcode tool2curlparts(CURL *curl, struct tool_mime *m,
244 curl_mime *mime)
245 {
246 CURLcode ret = CURLE_OK;
247 curl_mimepart *part = NULL;
248 curl_mime *submime = NULL;
249 const char *filename = NULL;
250
251 if(m) {
252 ret = tool2curlparts(curl, m->prev, mime);
253 if(!ret) {
254 part = curl_mime_addpart(mime);
255 if(!part)
256 ret = CURLE_OUT_OF_MEMORY;
257 }
258 if(!ret) {
259 filename = m->filename;
260 switch(m->kind) {
261 case TOOLMIME_PARTS:
262 ret = tool2curlmime(curl, m, &submime);
263 if(!ret) {
264 ret = curl_mime_subparts(part, submime);
265 if(ret)
266 curl_mime_free(submime);
267 }
268 break;
269
270 case TOOLMIME_DATA:
271 #ifdef CURL_DOES_CONVERSIONS
272 /* Our data is always textual: convert it to ASCII. */
273 {
274 size_t size = strlen(m->data);
275 char *cp = malloc(size + 1);
276
277 if(!cp)
278 ret = CURLE_OUT_OF_MEMORY;
279 else {
280 memcpy(cp, m->data, size + 1);
281 ret = convert_to_network(cp, size);
282 if(!ret)
283 ret = curl_mime_data(part, cp, CURL_ZERO_TERMINATED);
284 free(cp);
285 }
286 }
287 #else
288 ret = curl_mime_data(part, m->data, CURL_ZERO_TERMINATED);
289 #endif
290 break;
291
292 case TOOLMIME_FILE:
293 case TOOLMIME_FILEDATA:
294 ret = curl_mime_filedata(part, m->data);
295 if(!ret && m->kind == TOOLMIME_FILEDATA && !filename)
296 ret = curl_mime_filename(part, NULL);
297 break;
298
299 case TOOLMIME_STDIN:
300 if(!filename)
301 filename = "-";
302 /* FALLTHROUGH */
303 case TOOLMIME_STDINDATA:
304 ret = curl_mime_data_cb(part, m->size,
305 (curl_read_callback) tool_mime_stdin_read,
306 (curl_seek_callback) tool_mime_stdin_seek,
307 NULL, m);
308 break;
309
310 default:
311 /* Other cases not possible in this context. */
312 break;
313 }
314 }
315 if(!ret && filename)
316 ret = curl_mime_filename(part, filename);
317 if(!ret)
318 ret = curl_mime_type(part, m->type);
319 if(!ret)
320 ret = curl_mime_headers(part, m->headers, 0);
321 if(!ret)
322 ret = curl_mime_encoder(part, m->encoder);
323 if(!ret)
324 ret = curl_mime_name(part, m->name);
325 }
326 return ret;
327 }
328
tool2curlmime(CURL * curl,struct tool_mime * m,curl_mime ** mime)329 CURLcode tool2curlmime(CURL *curl, struct tool_mime *m, curl_mime **mime)
330 {
331 CURLcode ret = CURLE_OK;
332
333 *mime = curl_mime_init(curl);
334 if(!*mime)
335 ret = CURLE_OUT_OF_MEMORY;
336 else
337 ret = tool2curlparts(curl, m->subparts, *mime);
338 if(ret) {
339 curl_mime_free(*mime);
340 *mime = NULL;
341 }
342 return ret;
343 }
344
345 /*
346 * helper function to get a word from form param
347 * after call get_parm_word, str either point to string end
348 * or point to any of end chars.
349 */
get_param_word(char ** str,char ** end_pos,char endchar)350 static char *get_param_word(char **str, char **end_pos, char endchar)
351 {
352 char *ptr = *str;
353 /* the first non-space char is here */
354 char *word_begin = ptr;
355 char *ptr2;
356 char *escape = NULL;
357
358 if(*ptr == '"') {
359 ++ptr;
360 while(*ptr) {
361 if(*ptr == '\\') {
362 if(ptr[1] == '\\' || ptr[1] == '"') {
363 /* remember the first escape position */
364 if(!escape)
365 escape = ptr;
366 /* skip escape of back-slash or double-quote */
367 ptr += 2;
368 continue;
369 }
370 }
371 if(*ptr == '"') {
372 *end_pos = ptr;
373 if(escape) {
374 /* has escape, we restore the unescaped string here */
375 ptr = ptr2 = escape;
376 do {
377 if(*ptr == '\\' && (ptr[1] == '\\' || ptr[1] == '"'))
378 ++ptr;
379 *ptr2++ = *ptr++;
380 }
381 while(ptr < *end_pos);
382 *end_pos = ptr2;
383 }
384 while(*ptr && *ptr != ';' && *ptr != endchar)
385 ++ptr;
386 *str = ptr;
387 return word_begin + 1;
388 }
389 ++ptr;
390 }
391 /* end quote is missing, treat it as non-quoted. */
392 ptr = word_begin;
393 }
394
395 while(*ptr && *ptr != ';' && *ptr != endchar)
396 ++ptr;
397 *str = *end_pos = ptr;
398 return word_begin;
399 }
400
401 /* Append slist item and return -1 if failed. */
slist_append(struct curl_slist ** plist,const char * data)402 static int slist_append(struct curl_slist **plist, const char *data)
403 {
404 struct curl_slist *s = curl_slist_append(*plist, data);
405
406 if(!s)
407 return -1;
408
409 *plist = s;
410 return 0;
411 }
412
413 /* Read headers from a file and append to list. */
read_field_headers(struct OperationConfig * config,const char * filename,FILE * fp,struct curl_slist ** pheaders)414 static int read_field_headers(struct OperationConfig *config,
415 const char *filename, FILE *fp,
416 struct curl_slist **pheaders)
417 {
418 size_t hdrlen = 0;
419 size_t pos = 0;
420 bool incomment = FALSE;
421 int lineno = 1;
422 char hdrbuf[999]; /* Max. header length + 1. */
423
424 for(;;) {
425 int c = getc(fp);
426 if(c == EOF || (!pos && !ISSPACE(c))) {
427 /* Strip and flush the current header. */
428 while(hdrlen && ISSPACE(hdrbuf[hdrlen - 1]))
429 hdrlen--;
430 if(hdrlen) {
431 hdrbuf[hdrlen] = '\0';
432 if(slist_append(pheaders, hdrbuf)) {
433 fprintf(config->global->errors,
434 "Out of memory for field headers!\n");
435 return -1;
436 }
437 hdrlen = 0;
438 }
439 }
440
441 switch(c) {
442 case EOF:
443 if(ferror(fp)) {
444 fprintf(config->global->errors,
445 "Header file %s read error: %s\n", filename, strerror(errno));
446 return -1;
447 }
448 return 0; /* Done. */
449 case '\r':
450 continue; /* Ignore. */
451 case '\n':
452 pos = 0;
453 incomment = FALSE;
454 lineno++;
455 continue;
456 case '#':
457 if(!pos)
458 incomment = TRUE;
459 break;
460 }
461
462 pos++;
463 if(!incomment) {
464 if(hdrlen == sizeof(hdrbuf) - 1) {
465 warnf(config->global, "File %s line %d: header too long (truncated)\n",
466 filename, lineno);
467 c = ' ';
468 }
469 if(hdrlen <= sizeof(hdrbuf) - 1)
470 hdrbuf[hdrlen++] = (char) c;
471 }
472 }
473 /* NOTREACHED */
474 }
475
get_param_part(struct OperationConfig * config,char endchar,char ** str,char ** pdata,char ** ptype,char ** pfilename,char ** pencoder,struct curl_slist ** pheaders)476 static int get_param_part(struct OperationConfig *config, char endchar,
477 char **str, char **pdata, char **ptype,
478 char **pfilename, char **pencoder,
479 struct curl_slist **pheaders)
480 {
481 char *p = *str;
482 char *type = NULL;
483 char *filename = NULL;
484 char *encoder = NULL;
485 char *endpos;
486 char *tp;
487 char sep;
488 char type_major[128] = "";
489 char type_minor[128] = "";
490 char *endct = NULL;
491 struct curl_slist *headers = NULL;
492
493 if(ptype)
494 *ptype = NULL;
495 if(pfilename)
496 *pfilename = NULL;
497 if(pheaders)
498 *pheaders = NULL;
499 if(pencoder)
500 *pencoder = NULL;
501 while(ISSPACE(*p))
502 p++;
503 tp = p;
504 *pdata = get_param_word(&p, &endpos, endchar);
505 /* If not quoted, strip trailing spaces. */
506 if(*pdata == tp)
507 while(endpos > *pdata && ISSPACE(endpos[-1]))
508 endpos--;
509 sep = *p;
510 *endpos = '\0';
511 while(sep == ';') {
512 while(ISSPACE(*++p))
513 ;
514
515 if(!endct && checkprefix("type=", p)) {
516 for(p += 5; ISSPACE(*p); p++)
517 ;
518 /* set type pointer */
519 type = p;
520
521 /* verify that this is a fine type specifier */
522 if(2 != sscanf(type, "%127[^/ ]/%127[^;, \n]", type_major, type_minor)) {
523 warnf(config->global, "Illegally formatted content-type field!\n");
524 curl_slist_free_all(headers);
525 return -1; /* illegal content-type syntax! */
526 }
527
528 /* now point beyond the content-type specifier */
529 p = type + strlen(type_major) + strlen(type_minor) + 1;
530 for(endct = p; *p && *p != ';' && *p != endchar; p++)
531 if(!ISSPACE(*p))
532 endct = p + 1;
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(&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(&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\n", 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(&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 fprintf(config->global->errors, "Out of memory for field header!\n");
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(&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(&p, &endpos, endchar);
633
634 sep = *p;
635 *endpos = '\0';
636 if(*unknown)
637 warnf(config->global, "skip unknown form field: %s\n", 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\n", type);
649
650 if(pfilename)
651 *pfilename = filename;
652 else if(filename)
653 warnf(config->global,
654 "Field file name not allowed here: %s\n", filename);
655
656 if(pencoder)
657 *pencoder = encoder;
658 else if(encoder)
659 warnf(config->global,
660 "Field encoder not allowed here: %s\n", encoder);
661
662 if(pheaders)
663 *pheaders = headers;
664 else if(headers) {
665 warnf(config->global,
666 "Field headers not allowed here: %s\n", 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 file name 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 /* Convenience macros for null pointer check. */
723 #define NULL_CHECK(ptr, init, retcode) { \
724 (ptr) = (init); \
725 if(!(ptr)) { \
726 warnf(config->global, "out of memory!\n"); \
727 curl_slist_free_all(headers); \
728 Curl_safefree(contents); \
729 return retcode; \
730 } \
731 }
732 #define SET_TOOL_MIME_PTR(m, field, retcode) { \
733 if(field) \
734 NULL_CHECK((m)->field, strdup(field), retcode); \
735 }
736
formparse(struct OperationConfig * config,const char * input,struct tool_mime ** mimeroot,struct tool_mime ** mimecurrent,bool literal_value)737 int formparse(struct OperationConfig *config,
738 const char *input,
739 struct tool_mime **mimeroot,
740 struct tool_mime **mimecurrent,
741 bool literal_value)
742 {
743 /* input MUST be a string in the format 'name=contents' and we'll
744 build a linked list with the info */
745 char *name = NULL;
746 char *contents = NULL;
747 char *contp;
748 char *data;
749 char *type = NULL;
750 char *filename = NULL;
751 char *encoder = NULL;
752 struct curl_slist *headers = NULL;
753 struct tool_mime *part = NULL;
754 CURLcode res;
755
756 /* Allocate the main mime structure if needed. */
757 if(!*mimecurrent) {
758 NULL_CHECK(*mimeroot, tool_mime_new_parts(NULL), 1);
759 *mimecurrent = *mimeroot;
760 }
761
762 /* Make a copy we can overwrite. */
763 NULL_CHECK(contents, strdup(input), 2);
764
765 /* Scan for the end of the name. */
766 contp = strchr(contents, '=');
767 if(contp) {
768 int sep = '\0';
769 if(contp > contents)
770 name = contents;
771 *contp++ = '\0';
772
773 if(*contp == '(' && !literal_value) {
774 /* Starting a multipart. */
775 sep = get_param_part(config, '\0',
776 &contp, &data, &type, NULL, NULL, &headers);
777 if(sep < 0) {
778 Curl_safefree(contents);
779 return 3;
780 }
781 NULL_CHECK(part, tool_mime_new_parts(*mimecurrent), 4);
782 *mimecurrent = part;
783 part->headers = headers;
784 headers = NULL;
785 SET_TOOL_MIME_PTR(part, type, 5);
786 }
787 else if(!name && !strcmp(contp, ")") && !literal_value) {
788 /* Ending a multipart. */
789 if(*mimecurrent == *mimeroot) {
790 warnf(config->global, "no multipart to terminate!\n");
791 Curl_safefree(contents);
792 return 6;
793 }
794 *mimecurrent = (*mimecurrent)->parent;
795 }
796 else if('@' == contp[0] && !literal_value) {
797
798 /* we use the @-letter to indicate file name(s) */
799
800 struct tool_mime *subparts = NULL;
801
802 do {
803 /* since this was a file, it may have a content-type specifier
804 at the end too, or a filename. Or both. */
805 ++contp;
806 sep = get_param_part(config, ',', &contp,
807 &data, &type, &filename, &encoder, &headers);
808 if(sep < 0) {
809 Curl_safefree(contents);
810 return 7;
811 }
812
813 /* now contp point to comma or string end.
814 If more files to come, make sure we have multiparts. */
815 if(!subparts) {
816 if(sep != ',') /* If there is a single file. */
817 subparts = *mimecurrent;
818 else
819 NULL_CHECK(subparts, tool_mime_new_parts(*mimecurrent), 8);
820 }
821
822 /* Store that file in a part. */
823 NULL_CHECK(part,
824 tool_mime_new_filedata(subparts, data, TRUE, &res), 9);
825 part->headers = headers;
826 headers = NULL;
827 part->config = config->global;
828 if(res == CURLE_READ_ERROR) {
829 /* An error occurred while reading stdin: if read has started,
830 issue the error now. Else, delay it until processed by
831 libcurl. */
832 if(part->size > 0) {
833 warnf(config->global,
834 "error while reading standard input\n");
835 Curl_safefree(contents);
836 return 10;
837 }
838 CONST_SAFEFREE(part->data);
839 part->data = NULL;
840 part->size = -1;
841 res = CURLE_OK;
842 }
843 SET_TOOL_MIME_PTR(part, filename, 11);
844 SET_TOOL_MIME_PTR(part, type, 12);
845 SET_TOOL_MIME_PTR(part, encoder, 13);
846
847 /* *contp could be '\0', so we just check with the delimiter */
848 } while(sep); /* loop if there's another file name */
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 Curl_safefree(contents);
858 return 14;
859 }
860
861 NULL_CHECK(part, tool_mime_new_filedata(*mimecurrent, data, FALSE,
862 &res), 15);
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\n");
873 Curl_safefree(contents);
874 return 16;
875 }
876 CONST_SAFEFREE(part->data);
877 part->data = NULL;
878 part->size = -1;
879 res = CURLE_OK;
880 }
881 }
882 else {
883 if(literal_value)
884 data = contp;
885 else {
886 sep = get_param_part(config, '\0', &contp,
887 &data, &type, &filename, &encoder, &headers);
888 if(sep < 0) {
889 Curl_safefree(contents);
890 return 17;
891 }
892 }
893
894 NULL_CHECK(part, tool_mime_new_data(*mimecurrent, data), 18);
895 part->headers = headers;
896 headers = NULL;
897 }
898
899 SET_TOOL_MIME_PTR(part, filename, 19);
900 SET_TOOL_MIME_PTR(part, type, 20);
901 SET_TOOL_MIME_PTR(part, encoder, 21);
902
903 if(sep) {
904 *contp = (char) sep;
905 warnf(config->global,
906 "garbage at end of field specification: %s\n", contp);
907 }
908 }
909
910 /* Set part name. */
911 SET_TOOL_MIME_PTR(part, name, 22);
912 }
913 else {
914 warnf(config->global, "Illegally formatted input field!\n");
915 Curl_safefree(contents);
916 return 23;
917 }
918 Curl_safefree(contents);
919 return 0;
920 }
921