1 /***************************************************************************
3 * Project ___| | | | _ \| |
5 * | (__| |_| | _ <| |___
6 * \___|\___/|_| \_\_____|
8 * Copyright (C) 1998 - 2016, 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"
26 #define ENABLE_CURLX_PRINTF
27 /* use our own printf() functions */
30 #include "tool_cfgable.h"
31 #include "tool_mfiles.h"
32 #include "tool_msgs.h"
33 #include "tool_formparse.h"
35 #include "memdebug.h" /* keep this as LAST include */
39 * helper function to get a word from form param
40 * after call get_parm_word, str either point to string end
41 * or point to any of end chars.
43 static char *get_param_word(char **str, char **end_pos)
46 char *word_begin = NULL;
49 const char *end_chars = ";,";
51 /* the first non-space char is here */
57 if(ptr[1] == '\\' || ptr[1] == '"') {
58 /* remember the first escape position */
61 /* skip escape of back-slash or double-quote */
69 /* has escape, we restore the unescaped string here */
72 if(*ptr == '\\' && (ptr[1] == '\\' || ptr[1] == '"'))
76 while(ptr < *end_pos);
79 while(*ptr && NULL==strchr(end_chars, *ptr))
86 /* end quote is missing, treat it as non-quoted. */
90 while(*ptr && NULL==strchr(end_chars, *ptr))
92 *str = *end_pos = ptr;
96 /***************************************************************************
100 * Reads a 'name=value' parameter and builds the appropriate linked list.
102 * Specify files to upload with 'name=@filename', or 'name=@"filename"'
103 * in case the filename contain ',' or ';'. Supports specified
104 * given Content-Type of the files. Such as ';type=<content-type>'.
106 * If literal_value is set, any initial '@' or '<' in the value string
107 * loses its special meaning, as does any embedded ';type='.
109 * You may specify more than one file for a single name (field). Specify
110 * multiple files by writing it like:
112 * 'name=@filename,filename2,filename3'
114 * or use double-quotes quote the filename:
116 * 'name=@"filename","filename2","filename3"'
118 * If you want content-types specified for each too, write them like:
120 * 'name=@filename;type=image/gif,filename2,filename3'
122 * If you want custom headers added for a single part, write them in a separate
123 * file and do like this:
125 * 'name=foo;headers=@headerfile' or why not
126 * 'name=@filemame;headers=@headerfile'
128 * To upload a file, but to fake the file name that will be included in the
129 * formpost, do like this:
131 * 'name=@filename;filename=/dev/null' or quote the faked filename like:
132 * 'name=@filename;filename="play, play, and play.txt"'
134 * If filename/path contains ',' or ';', it must be quoted by double-quotes,
135 * else curl will fail to figure out the correct filename. if the filename
136 * tobe quoted contains '"' or '\', '"' and '\' must be escaped by backslash.
138 * This function uses curl_formadd to fulfill it's job. Is heavily based on
139 * the old curl_formparse code.
141 ***************************************************************************/
143 int formparse(struct OperationConfig *config,
145 struct curl_httppost **httppost,
146 struct curl_httppost **last_post,
149 /* nextarg MUST be a string in the format 'name=contents' and we'll
150 build a linked list with the info */
152 char *contents = NULL;
153 char type_major[128] = "";
154 char type_minor[128] = "";
156 const char *type = NULL;
159 if((1 == sscanf(input, "%255[^=]=", name)) &&
160 ((contp = strchr(input, '=')) != NULL)) {
161 /* the input was using the correct format */
163 /* Allocate the contents */
164 contents = strdup(contp+1);
166 fprintf(config->global->errors, "out of memory\n");
171 if('@' == contp[0] && !literal_value) {
173 /* we use the @-letter to indicate file name(s) */
175 struct multi_files *multi_start = NULL;
176 struct multi_files *multi_current = NULL;
179 char *end = ptr + strlen(ptr);
182 /* since this was a file, it may have a content-type specifier
183 at the end too, or a filename. Or both. */
184 char *filename = NULL;
191 contp = get_param_word(&ptr, &word_end);
192 semicolon = (';' == *ptr) ? TRUE : FALSE;
193 *word_end = '\0'; /* terminate the contp */
195 /* have other content, continue parse */
197 /* have type or filename field */
199 while(*ptr && (ISSPACE(*ptr)))
202 if(checkprefix("type=", ptr)) {
203 /* set type pointer */
206 /* verify that this is a fine type specifier */
207 if(2 != sscanf(type, "%127[^/]/%127[^;,\n]",
208 type_major, type_minor)) {
209 warnf(config->global,
210 "Illegally formatted content-type field!\n");
211 Curl_safefree(contents);
212 FreeMultiInfo(&multi_start, &multi_current);
213 return 2; /* illegal content-type syntax! */
216 /* now point beyond the content-type specifier */
217 sep = (char *)type + strlen(type_major)+strlen(type_minor)+1;
219 /* there's a semicolon following - we check if it is a filename
220 specified and if not we simply assume that it is text that
221 the user wants included in the type and include that too up
225 if(!checkprefix(";filename=", sep)) {
227 (void)get_param_word(&ptr, &sep);
228 semicolon = (';' == *ptr) ? TRUE : FALSE;
235 *sep = '\0'; /* zero terminate type string */
237 else if(checkprefix("filename=", ptr)) {
239 filename = get_param_word(&ptr, &word_end);
240 semicolon = (';' == *ptr) ? TRUE : FALSE;
244 /* unknown prefix, skip to next block */
245 char *unknown = NULL;
246 unknown = get_param_word(&ptr, &word_end);
247 semicolon = (';' == *ptr) ? TRUE : FALSE;
250 warnf(config->global, "skip unknown form field: %s\n", unknown);
254 /* now ptr point to comma or string end */
257 /* if type == NULL curl_formadd takes care of the problem */
259 if(*contp && !AddMultiFiles(contp, type, filename, &multi_start,
261 warnf(config->global, "Error building form post!\n");
262 Curl_safefree(contents);
263 FreeMultiInfo(&multi_start, &multi_current);
267 /* *ptr could be '\0', so we just check with the string end */
268 } while(ptr < end); /* loop if there's another file name */
270 /* now we add the multiple files section */
272 struct curl_forms *forms = NULL;
273 struct multi_files *start = multi_start;
274 unsigned int i, count = 0;
279 forms = malloc((count+1)*sizeof(struct curl_forms));
281 fprintf(config->global->errors, "Error building form post!\n");
282 Curl_safefree(contents);
283 FreeMultiInfo(&multi_start, &multi_current);
286 for(i = 0, start = multi_start; i < count; ++i, start = start->next) {
287 forms[i].option = start->form.option;
288 forms[i].value = start->form.value;
290 forms[count].option = CURLFORM_END;
291 FreeMultiInfo(&multi_start, &multi_current);
292 if(curl_formadd(httppost, last_post,
293 CURLFORM_COPYNAME, name,
294 CURLFORM_ARRAY, forms, CURLFORM_END) != 0) {
295 warnf(config->global, "curl_formadd failed!\n");
296 Curl_safefree(forms);
297 Curl_safefree(contents);
300 Curl_safefree(forms);
304 struct curl_forms info[4];
306 char *ct = literal_value ? NULL : strstr(contp, ";type=");
308 info[i].option = CURLFORM_COPYNAME;
309 info[i].value = name;
313 info[i].option = CURLFORM_CONTENTTYPE;
314 info[i].value = &ct[6];
316 ct[0] = '\0'; /* zero terminate here */
319 if(contp[0]=='<' && !literal_value) {
320 info[i].option = CURLFORM_FILECONTENT;
321 info[i].value = contp+1;
323 info[i].option = CURLFORM_END;
325 if(curl_formadd(httppost, last_post,
326 CURLFORM_ARRAY, info, CURLFORM_END) != 0) {
327 warnf(config->global, "curl_formadd failed, possibly the file %s is "
328 "bad!\n", contp + 1);
329 Curl_safefree(contents);
334 #ifdef CURL_DOES_CONVERSIONS
335 if(convert_to_network(contp, strlen(contp))) {
336 warnf(config->global, "curl_formadd failed!\n");
337 Curl_safefree(contents);
341 info[i].option = CURLFORM_COPYCONTENTS;
342 info[i].value = contp;
344 info[i].option = CURLFORM_END;
345 if(curl_formadd(httppost, last_post,
346 CURLFORM_ARRAY, info, CURLFORM_END) != 0) {
347 warnf(config->global, "curl_formadd failed!\n");
348 Curl_safefree(contents);
356 warnf(config->global, "Illegally formatted input field!\n");
359 Curl_safefree(contents);