1 /***************************************************************************
3 * Project ___| | | | _ \| |
5 * | (__| |_| | _ <| |___
6 * \___|\___/|_| \_\_____|
8 * Copyright (C) 1998 - 2017, Daniel Stenberg, <daniel@haxx.se>, et al.
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.
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.
18 * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY
19 * KIND, either express or implied.
21 ***************************************************************************/
22 #include "tool_setup.h"
27 #define ENABLE_CURLX_PRINTF
28 /* use our own printf() functions */
31 #include "tool_cfgable.h"
32 #include "tool_convert.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"
39 #include "memdebug.h" /* keep this as LAST include */
41 /* Stdin parameters. */
43 char *data; /* Memory data. */
44 curl_off_t origin; /* File read origin offset. */
45 curl_off_t size; /* Data size. */
46 curl_off_t curpos; /* Current read position. */
51 * helper function to get a word from form param
52 * after call get_parm_word, str either point to string end
53 * or point to any of end chars.
55 static char *get_param_word(char **str, char **end_pos, char endchar)
58 char *word_begin = NULL;
62 /* the first non-space char is here */
68 if(ptr[1] == '\\' || ptr[1] == '"') {
69 /* remember the first escape position */
72 /* skip escape of back-slash or double-quote */
80 /* has escape, we restore the unescaped string here */
83 if(*ptr == '\\' && (ptr[1] == '\\' || ptr[1] == '"'))
87 while(ptr < *end_pos);
90 while(*ptr && *ptr != ';' && *ptr != endchar)
93 return word_begin + 1;
97 /* end quote is missing, treat it as non-quoted. */
101 while(*ptr && *ptr != ';' && *ptr != endchar)
103 *str = *end_pos = ptr;
107 /* Append slist item and return -1 if failed. */
108 static int slist_append(struct curl_slist **plist, const char *data)
110 struct curl_slist *s = curl_slist_append(*plist, data);
119 /* Read headers from a file and append to list. */
120 static int read_field_headers(struct OperationConfig *config,
121 const char *filename, FILE *fp,
122 struct curl_slist **pheaders)
127 bool incomment = FALSE;
129 char hdrbuf[999]; /* Max. header length + 1. */
133 if(c == EOF || (!pos && !ISSPACE(c))) {
134 /* Strip and flush the current header. */
135 while(hdrlen && ISSPACE(hdrbuf[hdrlen - 1]))
138 hdrbuf[hdrlen] = '\0';
139 if(slist_append(pheaders, hdrbuf)) {
140 fprintf(config->global->errors,
141 "Out of memory for field headers!\n");
151 fprintf(config->global->errors,
152 "Header file %s read error: %s\n", filename, strerror(errno));
155 return 0; /* Done. */
157 continue; /* Ignore. */
171 if(hdrlen == sizeof hdrbuf - 1) {
172 warnf(config->global, "File %s line %d: header too long (truncated)\n",
176 if(hdrlen <= sizeof hdrbuf - 1)
177 hdrbuf[hdrlen++] = (char) c;
183 static int get_param_part(struct OperationConfig *config, char endchar,
184 char **str, char **pdata, char **ptype,
185 char **pfilename, char **pencoder,
186 struct curl_slist **pheaders)
190 char *filename = NULL;
191 char *encoder = NULL;
195 char type_major[128] = "";
196 char type_minor[128] = "";
198 struct curl_slist *headers = NULL;
211 *pdata = get_param_word(&p, &endpos, endchar);
212 /* If not quoted, strip trailing spaces. */
214 while(endpos > *pdata && ISSPACE(endpos[-1]))
222 if(!endct && checkprefix("type=", p)) {
223 for(p += 5; ISSPACE(*p); p++)
225 /* set type pointer */
228 /* verify that this is a fine type specifier */
229 if(2 != sscanf(type, "%127[^/ ]/%127[^;, \n]", type_major, type_minor)) {
230 warnf(config->global, "Illegally formatted content-type field!\n");
231 curl_slist_free_all(headers);
232 return -1; /* illegal content-type syntax! */
235 /* now point beyond the content-type specifier */
236 p = type + strlen(type_major) + strlen(type_minor) + 1;
237 for(endct = p; *p && *p != ';' && *p != endchar; p++)
242 else if(checkprefix("filename=", p)) {
247 for(p += 9; ISSPACE(*p); p++)
250 filename = get_param_word(&p, &endpos, endchar);
251 /* If not quoted, strip trailing spaces. */
253 while(endpos > filename && ISSPACE(endpos[-1]))
258 else if(checkprefix("headers=", p)) {
264 if(*p == '@' || *p == '<') {
267 /* Read headers from a file. */
271 } while(ISSPACE(*p));
273 hdrfile = get_param_word(&p, &endpos, endchar);
274 /* If not quoted, strip trailing spaces. */
276 while(endpos > hdrfile && ISSPACE(endpos[-1]))
280 /* TODO: maybe special fopen for VMS? */
281 fp = fopen(hdrfile, FOPEN_READTEXT);
283 warnf(config->global, "Cannot read from %s: %s\n", hdrfile,
286 int i = read_field_headers(config, hdrfile, fp, &headers);
290 curl_slist_free_all(headers);
301 hdr = get_param_word(&p, &endpos, endchar);
302 /* If not quoted, strip trailing spaces. */
304 while(endpos > hdr && ISSPACE(endpos[-1]))
308 if(slist_append(&headers, hdr)) {
309 fprintf(config->global->errors, "Out of memory for field header!\n");
310 curl_slist_free_all(headers);
315 else if(checkprefix("encoder=", p)) {
320 for(p += 8; ISSPACE(*p); p++)
323 encoder = get_param_word(&p, &endpos, endchar);
324 /* If not quoted, strip trailing spaces. */
326 while(endpos > encoder && ISSPACE(endpos[-1]))
332 /* This is part of content type. */
333 for(endct = p; *p && *p != ';' && *p != endchar; p++)
339 /* unknown prefix, skip to next block */
340 char *unknown = get_param_word(&p, &endpos, endchar);
345 warnf(config->global, "skip unknown form field: %s\n", unknown);
349 /* Terminate content type. */
356 warnf(config->global, "Field content type not allowed here: %s\n", type);
359 *pfilename = filename;
361 warnf(config->global,
362 "Field file name not allowed here: %s\n", filename);
367 warnf(config->global,
368 "Field encoder not allowed here: %s\n", encoder);
373 warnf(config->global,
374 "Field headers not allowed here: %s\n", headers->data);
375 curl_slist_free_all(headers);
383 /* Mime part callbacks for stdin. */
384 static size_t stdin_read(char *buffer, size_t size, size_t nitems, void *arg)
386 standard_input *sip = (standard_input *) arg;
387 curl_off_t bytesleft;
388 (void) size; /* Always 1: ignored. */
390 if(sip->curpos >= sip->size)
391 return 0; /* At eof. */
392 bytesleft = sip->size - sip->curpos;
393 if((curl_off_t) nitems > bytesleft)
394 nitems = (size_t) bytesleft;
396 /* Return data from memory. */
397 memcpy(buffer, sip->data + (size_t) sip->curpos, nitems);
400 /* Read from stdin. */
401 nitems = fread(buffer, 1, nitems, stdin);
403 sip->curpos += nitems;
407 static int stdin_seek(void *instream, curl_off_t offset, int whence)
409 standard_input *sip = (standard_input *) instream;
413 offset += sip->curpos;
420 return CURL_SEEKFUNC_CANTSEEK;
422 if(fseek(stdin, (long) (offset + sip->origin), SEEK_SET))
423 return CURL_SEEKFUNC_CANTSEEK;
425 sip->curpos = offset;
426 return CURL_SEEKFUNC_OK;
429 static void stdin_free(void *ptr)
431 standard_input *sip = (standard_input *) ptr;
433 Curl_safefree(sip->data);
437 /* Set a part's data from a file, taking care about the pseudo filename "-" as
438 * a shortcut to read stdin: if so, use a callback to read OUR stdin (to
439 * workaround Windows DLL file handle caveat).
440 * If stdin is a regular file opened in binary mode, save current offset as
441 * origin for rewind and do not buffer data. Else read to EOF and keep in
442 * memory. In all cases, compute the stdin data size.
444 static CURLcode file_or_stdin(curl_mimepart *part, const char *file)
446 standard_input *sip = NULL;
448 CURLcode result = CURLE_OK;
451 if(strcmp(file, "-"))
452 return curl_mime_filedata(part, file);
454 sip = (standard_input *) malloc(sizeof *sip);
456 return CURLE_OUT_OF_MEMORY;
458 memset((char *) sip, 0, sizeof *sip);
461 /* If stdin is a regular file, do not buffer data but read it when needed. */
463 sip->origin = ftell(stdin);
464 if(fd >= 0 && sip->origin >= 0 && !fstat(fd, &sbuf) &&
466 sbuf.st_fab_rfm != FAB$C_VAR && sbuf.st_fab_rfm != FAB$C_VFC &&
468 S_ISREG(sbuf.st_mode)) {
469 sip->size = sbuf.st_size - sip->origin;
473 else { /* Not suitable for direct use, buffer stdin data. */
474 size_t stdinsize = 0;
477 if(file2memory(&sip->data, &stdinsize, stdin) != PARAM_OK)
478 result = CURLE_OUT_OF_MEMORY;
481 sip->data = NULL; /* Has been freed if no data. */
482 sip->size = stdinsize;
484 result = CURLE_READ_ERROR;
488 /* Set remote file name. */
490 result = curl_mime_filename(part, file);
492 /* Set part's data from callback. */
494 result = curl_mime_data_cb(part, sip->size,
495 stdin_read, stdin_seek, stdin_free, sip);
502 /***************************************************************************
506 * Reads a 'name=value' parameter and builds the appropriate linked list.
508 * Specify files to upload with 'name=@filename', or 'name=@"filename"'
509 * in case the filename contain ',' or ';'. Supports specified
510 * given Content-Type of the files. Such as ';type=<content-type>'.
512 * If literal_value is set, any initial '@' or '<' in the value string
513 * loses its special meaning, as does any embedded ';type='.
515 * You may specify more than one file for a single name (field). Specify
516 * multiple files by writing it like:
518 * 'name=@filename,filename2,filename3'
520 * or use double-quotes quote the filename:
522 * 'name=@"filename","filename2","filename3"'
524 * If you want content-types specified for each too, write them like:
526 * 'name=@filename;type=image/gif,filename2,filename3'
528 * If you want custom headers added for a single part, write them in a separate
529 * file and do like this:
531 * 'name=foo;headers=@headerfile' or why not
532 * 'name=@filemame;headers=@headerfile'
534 * To upload a file, but to fake the file name that will be included in the
535 * formpost, do like this:
537 * 'name=@filename;filename=/dev/null' or quote the faked filename like:
538 * 'name=@filename;filename="play, play, and play.txt"'
540 * If filename/path contains ',' or ';', it must be quoted by double-quotes,
541 * else curl will fail to figure out the correct filename. if the filename
542 * tobe quoted contains '"' or '\', '"' and '\' must be escaped by backslash.
544 * This function uses curl_formadd to fulfill it's job. Is heavily based on
545 * the old curl_formparse code.
547 ***************************************************************************/
549 int formparse(struct OperationConfig *config,
551 curl_mime **mimepost,
552 curl_mime **mimecurrent,
555 /* input MUST be a string in the format 'name=contents' and we'll
556 build a linked list with the info */
558 char *contents = NULL;
562 char *filename = NULL;
563 char *encoder = NULL;
564 struct curl_slist *headers = NULL;
565 curl_mimepart *part = NULL;
569 /* Allocate the main mime structure if needed. */
571 *mimepost = curl_mime_init(config->easy);
573 warnf(config->global, "curl_mime_init failed!\n");
576 *mimecurrent = *mimepost;
579 /* Make a copy we can overwrite. */
580 contents = strdup(input);
582 fprintf(config->global->errors, "out of memory\n");
586 /* Scan for the end of the name. */
587 contp = strchr(contents, '=');
593 if(*contp == '(' && !literal_value) {
596 /* Starting a multipart. */
597 sep = get_param_part(config, '\0',
598 &contp, &data, &type, NULL, NULL, &headers);
600 Curl_safefree(contents);
603 subparts = curl_mime_init(config->easy);
605 warnf(config->global, "curl_mime_init failed!\n");
606 curl_slist_free_all(headers);
607 Curl_safefree(contents);
610 part = curl_mime_addpart(*mimecurrent);
612 warnf(config->global, "curl_mime_addpart failed!\n");
613 curl_mime_free(subparts);
614 curl_slist_free_all(headers);
615 Curl_safefree(contents);
618 if(curl_mime_subparts(part, subparts)) {
619 warnf(config->global, "curl_mime_subparts failed!\n");
620 curl_mime_free(subparts);
621 curl_slist_free_all(headers);
622 Curl_safefree(contents);
625 *mimecurrent = subparts;
626 if(curl_mime_headers(part, headers, 1)) {
627 warnf(config->global, "curl_mime_headers failed!\n");
628 curl_slist_free_all(headers);
629 Curl_safefree(contents);
632 if(curl_mime_type(part, type)) {
633 warnf(config->global, "curl_mime_type failed!\n");
634 Curl_safefree(contents);
638 else if(!name && !strcmp(contp, ")") && !literal_value) {
639 /* Ending a mutipart. */
640 if(*mimecurrent == *mimepost) {
641 warnf(config->global, "no multipart to terminate!\n");
642 Curl_safefree(contents);
645 *mimecurrent = (*mimecurrent)->parent->parent;
647 else if('@' == contp[0] && !literal_value) {
649 /* we use the @-letter to indicate file name(s) */
651 curl_mime *subparts = NULL;
654 /* since this was a file, it may have a content-type specifier
655 at the end too, or a filename. Or both. */
657 sep = get_param_part(config, ',', &contp,
658 &data, &type, &filename, &encoder, &headers);
660 if(subparts != *mimecurrent)
661 curl_mime_free(subparts);
662 Curl_safefree(contents);
666 /* now contp point to comma or string end.
667 If more files to come, make sure we have multiparts. */
669 if(sep != ',') /* If there is a single file. */
670 subparts = *mimecurrent;
672 subparts = curl_mime_init(config->easy);
674 warnf(config->global, "curl_mime_init failed!\n");
675 curl_slist_free_all(headers);
676 Curl_safefree(contents);
682 /* Allocate a part for that file. */
683 part = curl_mime_addpart(subparts);
685 warnf(config->global, "curl_mime_addpart failed!\n");
686 if(subparts != *mimecurrent)
687 curl_mime_free(subparts);
688 curl_slist_free_all(headers);
689 Curl_safefree(contents);
693 /* Set part headers. */
694 if(curl_mime_headers(part, headers, 1)) {
695 warnf(config->global, "curl_mime_headers failed!\n");
696 if(subparts != *mimecurrent)
697 curl_mime_free(subparts);
698 curl_slist_free_all(headers);
699 Curl_safefree(contents);
703 /* Setup file in part. */
704 res = file_or_stdin(part, data);
706 warnf(config->global, "setting file %s failed!\n", data);
707 if(res != CURLE_READ_ERROR) {
708 if(subparts != *mimecurrent)
709 curl_mime_free(subparts);
710 Curl_safefree(contents);
714 if(filename && curl_mime_filename(part, filename)) {
715 warnf(config->global, "curl_mime_filename failed!\n");
716 if(subparts != *mimecurrent)
717 curl_mime_free(subparts);
718 Curl_safefree(contents);
721 if(curl_mime_type(part, type)) {
722 warnf(config->global, "curl_mime_type failed!\n");
723 if(subparts != *mimecurrent)
724 curl_mime_free(subparts);
725 Curl_safefree(contents);
728 if(curl_mime_encoder(part, encoder)) {
729 warnf(config->global, "curl_mime_encoder failed!\n");
730 if(subparts != *mimecurrent)
731 curl_mime_free(subparts);
732 Curl_safefree(contents);
736 /* *contp could be '\0', so we just check with the delimiter */
737 } while(sep); /* loop if there's another file name */
739 /* now we add the multiple files section */
740 if(subparts != *mimecurrent) {
741 part = curl_mime_addpart(*mimecurrent);
743 warnf(config->global, "curl_mime_addpart failed!\n");
744 curl_mime_free(subparts);
745 Curl_safefree(contents);
748 if(curl_mime_subparts(part, subparts)) {
749 warnf(config->global, "curl_mime_subparts failed!\n");
750 curl_mime_free(subparts);
751 Curl_safefree(contents);
757 /* Allocate a mime part. */
758 part = curl_mime_addpart(*mimecurrent);
760 warnf(config->global, "curl_mime_addpart failed!\n");
761 Curl_safefree(contents);
765 if(*contp == '<' && !literal_value) {
767 sep = get_param_part(config, '\0', &contp,
768 &data, &type, NULL, &encoder, &headers);
770 Curl_safefree(contents);
774 /* Set part headers. */
775 if(curl_mime_headers(part, headers, 1)) {
776 warnf(config->global, "curl_mime_headers failed!\n");
777 curl_slist_free_all(headers);
778 Curl_safefree(contents);
782 /* Setup file in part. */
783 res = file_or_stdin(part, data);
785 warnf(config->global, "setting file %s failed!\n", data);
786 if(res != CURLE_READ_ERROR) {
787 Curl_safefree(contents);
796 sep = get_param_part(config, '\0', &contp,
797 &data, &type, &filename, &encoder, &headers);
799 Curl_safefree(contents);
804 /* Set part headers. */
805 if(curl_mime_headers(part, headers, 1)) {
806 warnf(config->global, "curl_mime_headers failed!\n");
807 curl_slist_free_all(headers);
808 Curl_safefree(contents);
812 #ifdef CURL_DOES_CONVERSIONS
813 if(convert_to_network(data, strlen(data))) {
814 warnf(config->global, "curl_formadd failed!\n");
815 Curl_safefree(contents);
820 if(curl_mime_data(part, data, CURL_ZERO_TERMINATED)) {
821 warnf(config->global, "curl_mime_data failed!\n");
822 Curl_safefree(contents);
827 if(curl_mime_filename(part, filename)) {
828 warnf(config->global, "curl_mime_filename failed!\n");
829 Curl_safefree(contents);
832 if(curl_mime_type(part, type)) {
833 warnf(config->global, "curl_mime_type failed!\n");
834 Curl_safefree(contents);
837 if(curl_mime_encoder(part, encoder)) {
838 warnf(config->global, "curl_mime_encoder failed!\n");
839 Curl_safefree(contents);
845 warnf(config->global,
846 "garbage at end of field specification: %s\n", contp);
851 if(name && curl_mime_name(part, name)) {
852 warnf(config->global, "curl_mime_name failed!\n");
853 Curl_safefree(contents);
858 warnf(config->global, "Illegally formatted input field!\n");
859 Curl_safefree(contents);
862 Curl_safefree(contents);