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