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