76a5db1fc8fb3777f1340f190fb50b114f6b4995
[platform/upstream/curl.git] / src / tool_formparse.c
1 /***************************************************************************
2  *                                  _   _ ____  _
3  *  Project                     ___| | | |  _ \| |
4  *                             / __| | | | |_) | |
5  *                            | (__| |_| |  _ <| |___
6  *                             \___|\___/|_| \_\_____|
7  *
8  * Copyright (C) 1998 - 2012, 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 http://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 "curl_rawstr.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_mfiles.h"
32 #include "tool_msgs.h"
33 #include "tool_formparse.h"
34
35 #include "curl_memdebug.h" /* keep this as LAST include */
36
37 /***************************************************************************
38  *
39  * formparse()
40  *
41  * Reads a 'name=value' parameter and builds the appropriate linked list.
42  *
43  * Specify files to upload with 'name=@filename'. Supports specified
44  * given Content-Type of the files. Such as ';type=<content-type>'.
45  *
46  * If literal_value is set, any initial '@' or '<' in the value string
47  * loses its special meaning, as does any embedded ';type='.
48  *
49  * You may specify more than one file for a single name (field). Specify
50  * multiple files by writing it like:
51  *
52  * 'name=@filename,filename2,filename3'
53  *
54  * If you want content-types specified for each too, write them like:
55  *
56  * 'name=@filename;type=image/gif,filename2,filename3'
57  *
58  * If you want custom headers added for a single part, write them in a separate
59  * file and do like this:
60  *
61  * 'name=foo;headers=@headerfile' or why not
62  * 'name=@filemame;headers=@headerfile'
63  *
64  * To upload a file, but to fake the file name that will be included in the
65  * formpost, do like this:
66  *
67  * 'name=@filename;filename=/dev/null'
68  *
69  * This function uses curl_formadd to fulfill it's job. Is heavily based on
70  * the old curl_formparse code.
71  *
72  ***************************************************************************/
73
74 int formparse(struct Configurable *config,
75               const char *input,
76               struct curl_httppost **httppost,
77               struct curl_httppost **last_post,
78               bool literal_value)
79 {
80   /* nextarg MUST be a string in the format 'name=contents' and we'll
81      build a linked list with the info */
82   char name[256];
83   char *contents = NULL;
84   char type_major[128];
85   char type_minor[128];
86   char *contp;
87   const char *type = NULL;
88   char *sep;
89   char *sep2;
90
91   if((1 == sscanf(input, "%255[^=]=", name)) &&
92      ((contp = strchr(input, '=')) != NULL)) {
93     /* the input was using the correct format */
94
95     /* Allocate the contents */
96     contents = strdup(contp+1);
97     if(!contents) {
98       fprintf(config->errors, "out of memory\n");
99       return 1;
100     }
101     contp = contents;
102
103     if('@' == contp[0] && !literal_value) {
104
105       /* we use the @-letter to indicate file name(s) */
106
107       struct multi_files *multi_start = NULL;
108       struct multi_files *multi_current = NULL;
109
110       contp++;
111
112       do {
113         /* since this was a file, it may have a content-type specifier
114            at the end too, or a filename. Or both. */
115         char *ptr;
116         char *filename = NULL;
117
118         sep = strchr(contp, ';');
119         sep2 = strchr(contp, ',');
120
121         /* pick the closest */
122         if(sep2 && (sep2 < sep)) {
123           sep = sep2;
124
125           /* no type was specified! */
126         }
127
128         type = NULL;
129
130         if(sep) {
131           bool semicolon = (';' == *sep) ? TRUE : FALSE;
132
133           *sep = '\0'; /* terminate file name at separator */
134
135           ptr = sep+1; /* point to the text following the separator */
136
137           while(semicolon && ptr && (','!= *ptr)) {
138
139             /* pass all white spaces */
140             while(ISSPACE(*ptr))
141               ptr++;
142
143             if(checkprefix("type=", ptr)) {
144               /* set type pointer */
145               type = &ptr[5];
146
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! */
154               }
155
156               /* now point beyond the content-type specifier */
157               sep = (char *)type + strlen(type_major)+strlen(type_minor)+1;
158
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. */
163               if(*sep==';') {
164                 if(!checkprefix(";filename=", sep)) {
165                   sep2 = strchr(sep+1, ';');
166                   if(sep2)
167                     sep = sep2;
168                   else
169                     sep = sep + strlen(sep); /* point to end of string */
170                 }
171               }
172               else
173                 semicolon = FALSE;
174
175               if(*sep) {
176                 *sep = '\0'; /* zero terminate type string */
177
178                 ptr = sep+1;
179               }
180               else
181                 ptr = NULL; /* end */
182             }
183             else if(checkprefix("filename=", ptr)) {
184               filename = &ptr[9];
185               ptr = strchr(filename, ';');
186               if(!ptr) {
187                 ptr = strchr(filename, ',');
188               }
189               if(ptr) {
190                 *ptr = '\0'; /* zero terminate */
191                 ptr++;
192               }
193             }
194             else
195               /* confusion, bail out of loop */
196               break;
197           }
198
199           sep = ptr;
200         }
201
202         /* if type == NULL curl_formadd takes care of the problem */
203
204         if(!AddMultiFiles(contp, type, filename, &multi_start,
205                           &multi_current)) {
206           warnf(config, "Error building form post!\n");
207           Curl_safefree(contents);
208           FreeMultiInfo(&multi_start, &multi_current);
209           return 3;
210         }
211         contp = sep; /* move the contents pointer to after the separator */
212
213       } while(sep && *sep); /* loop if there's another file name */
214
215       /* now we add the multiple files section */
216       if(multi_start) {
217         struct curl_forms *forms = NULL;
218         struct multi_files *ptr = multi_start;
219         unsigned int i, count = 0;
220         while(ptr) {
221           ptr = ptr->next;
222           ++count;
223         }
224         forms = malloc((count+1)*sizeof(struct curl_forms));
225         if(!forms) {
226           fprintf(config->errors, "Error building form post!\n");
227           Curl_safefree(contents);
228           FreeMultiInfo(&multi_start, &multi_current);
229           return 4;
230         }
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;
234         }
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);
243           return 5;
244         }
245         Curl_safefree(forms);
246       }
247     }
248     else {
249       struct curl_forms info[4];
250       int i = 0;
251       char *ct = literal_value ? NULL : strstr(contp, ";type=");
252
253       info[i].option = CURLFORM_COPYNAME;
254       info[i].value = name;
255       i++;
256
257       if(ct) {
258         info[i].option = CURLFORM_CONTENTTYPE;
259         info[i].value = &ct[6];
260         i++;
261         ct[0] = '\0'; /* zero terminate here */
262       }
263
264       if(contp[0]=='<' && !literal_value) {
265         info[i].option = CURLFORM_FILECONTENT;
266         info[i].value = contp+1;
267         i++;
268         info[i].option = CURLFORM_END;
269
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",
273                 contp+1);
274           Curl_safefree(contents);
275           return 6;
276         }
277       }
278       else {
279 #ifdef CURL_DOES_CONVERSIONS
280         if(convert_to_network(contp, strlen(contp))) {
281           warnf(config, "curl_formadd failed!\n");
282           Curl_safefree(contents);
283           return 7;
284         }
285 #endif
286         info[i].option = CURLFORM_COPYCONTENTS;
287         info[i].value = contp;
288         i++;
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);
294           return 8;
295         }
296       }
297     }
298
299   }
300   else {
301     warnf(config, "Illegally formatted input field!\n");
302     return 1;
303   }
304   Curl_safefree(contents);
305   return 0;
306 }
307