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