1 /***************************************************************************
3 * Project ___| | | | _ \| |
5 * | (__| |_| | _ <| |___
6 * \___|\___/|_| \_\_____|
8 * Copyright (C) 1998 - 2012, 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 http://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"
24 #include "curl_rawstr.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 "curl_memdebug.h" /* keep this as LAST include */
37 /***************************************************************************
41 * Reads a 'name=value' parameter and builds the appropriate linked list.
43 * Specify files to upload with 'name=@filename'. Supports specified
44 * given Content-Type of the files. Such as ';type=<content-type>'.
46 * If literal_value is set, any initial '@' or '<' in the value string
47 * loses its special meaning, as does any embedded ';type='.
49 * You may specify more than one file for a single name (field). Specify
50 * multiple files by writing it like:
52 * 'name=@filename,filename2,filename3'
54 * If you want content-types specified for each too, write them like:
56 * 'name=@filename;type=image/gif,filename2,filename3'
58 * If you want custom headers added for a single part, write them in a separate
59 * file and do like this:
61 * 'name=foo;headers=@headerfile' or why not
62 * 'name=@filemame;headers=@headerfile'
64 * To upload a file, but to fake the file name that will be included in the
65 * formpost, do like this:
67 * 'name=@filename;filename=/dev/null'
69 * This function uses curl_formadd to fulfill it's job. Is heavily based on
70 * the old curl_formparse code.
72 ***************************************************************************/
74 int formparse(struct Configurable *config,
76 struct curl_httppost **httppost,
77 struct curl_httppost **last_post,
80 /* nextarg MUST be a string in the format 'name=contents' and we'll
81 build a linked list with the info */
83 char *contents = NULL;
87 const char *type = NULL;
91 if((1 == sscanf(input, "%255[^=]=", name)) &&
92 ((contp = strchr(input, '=')) != NULL)) {
93 /* the input was using the correct format */
95 /* Allocate the contents */
96 contents = strdup(contp+1);
98 fprintf(config->errors, "out of memory\n");
103 if('@' == contp[0] && !literal_value) {
105 /* we use the @-letter to indicate file name(s) */
107 struct multi_files *multi_start = NULL;
108 struct multi_files *multi_current = NULL;
113 /* since this was a file, it may have a content-type specifier
114 at the end too, or a filename. Or both. */
116 char *filename = NULL;
118 sep = strchr(contp, ';');
119 sep2 = strchr(contp, ',');
121 /* pick the closest */
122 if(sep2 && (sep2 < sep)) {
125 /* no type was specified! */
131 bool semicolon = (';' == *sep) ? TRUE : FALSE;
133 *sep = '\0'; /* terminate file name at separator */
135 ptr = sep+1; /* point to the text following the separator */
137 while(semicolon && ptr && (','!= *ptr)) {
139 /* pass all white spaces */
143 if(checkprefix("type=", ptr)) {
144 /* set type pointer */
147 /* verify that this is a fine type specifier */
148 if(2 != sscanf(type, "%127[^/]/%127[^;,\n]",
149 type_major, type_minor)) {
150 warnf(config, "Illegally formatted content-type field!\n");
151 Curl_safefree(contents);
152 FreeMultiInfo(&multi_start, &multi_current);
153 return 2; /* illegal content-type syntax! */
156 /* now point beyond the content-type specifier */
157 sep = (char *)type + strlen(type_major)+strlen(type_minor)+1;
159 /* there's a semicolon following - we check if it is a filename
160 specified and if not we simply assume that it is text that
161 the user wants included in the type and include that too up
162 to the next zero or semicolon. */
164 if(!checkprefix(";filename=", sep)) {
165 sep2 = strchr(sep+1, ';');
169 sep = sep + strlen(sep); /* point to end of string */
176 *sep = '\0'; /* zero terminate type string */
181 ptr = NULL; /* end */
183 else if(checkprefix("filename=", ptr)) {
185 ptr = strchr(filename, ';');
187 ptr = strchr(filename, ',');
190 *ptr = '\0'; /* zero terminate */
195 /* confusion, bail out of loop */
202 /* if type == NULL curl_formadd takes care of the problem */
204 if(!AddMultiFiles(contp, type, filename, &multi_start,
206 warnf(config, "Error building form post!\n");
207 Curl_safefree(contents);
208 FreeMultiInfo(&multi_start, &multi_current);
211 contp = sep; /* move the contents pointer to after the separator */
213 } while(sep && *sep); /* loop if there's another file name */
215 /* now we add the multiple files section */
217 struct curl_forms *forms = NULL;
218 struct multi_files *ptr = multi_start;
219 unsigned int i, count = 0;
224 forms = malloc((count+1)*sizeof(struct curl_forms));
226 fprintf(config->errors, "Error building form post!\n");
227 Curl_safefree(contents);
228 FreeMultiInfo(&multi_start, &multi_current);
231 for(i = 0, ptr = multi_start; i < count; ++i, ptr = ptr->next) {
232 forms[i].option = ptr->form.option;
233 forms[i].value = ptr->form.value;
235 forms[count].option = CURLFORM_END;
236 FreeMultiInfo(&multi_start, &multi_current);
237 if(curl_formadd(httppost, last_post,
238 CURLFORM_COPYNAME, name,
239 CURLFORM_ARRAY, forms, CURLFORM_END) != 0) {
240 warnf(config, "curl_formadd failed!\n");
241 Curl_safefree(forms);
242 Curl_safefree(contents);
245 Curl_safefree(forms);
249 struct curl_forms info[4];
251 char *ct = literal_value ? NULL : strstr(contp, ";type=");
253 info[i].option = CURLFORM_COPYNAME;
254 info[i].value = name;
258 info[i].option = CURLFORM_CONTENTTYPE;
259 info[i].value = &ct[6];
261 ct[0] = '\0'; /* zero terminate here */
264 if(contp[0]=='<' && !literal_value) {
265 info[i].option = CURLFORM_FILECONTENT;
266 info[i].value = contp+1;
268 info[i].option = CURLFORM_END;
270 if(curl_formadd(httppost, last_post,
271 CURLFORM_ARRAY, info, CURLFORM_END ) != 0) {
272 warnf(config, "curl_formadd failed, possibly the file %s is bad!\n",
274 Curl_safefree(contents);
279 #ifdef CURL_DOES_CONVERSIONS
280 if(convert_to_network(contp, strlen(contp))) {
281 warnf(config, "curl_formadd failed!\n");
282 Curl_safefree(contents);
286 info[i].option = CURLFORM_COPYCONTENTS;
287 info[i].value = contp;
289 info[i].option = CURLFORM_END;
290 if(curl_formadd(httppost, last_post,
291 CURLFORM_ARRAY, info, CURLFORM_END) != 0) {
292 warnf(config, "curl_formadd failed!\n");
293 Curl_safefree(contents);
301 warnf(config, "Illegally formatted input field!\n");
304 Curl_safefree(contents);