Mercurial > hgrepos > Python2 > PyMuPDF
comparison mupdf-source/thirdparty/curl/src/tool_formparse.c @ 2:b50eed0cc0ef upstream
ADD: MuPDF v1.26.7: the MuPDF source as downloaded by a default build of PyMuPDF 1.26.4.
The directory name has changed: no version number in the expanded directory now.
| author | Franz Glasner <fzglas.hg@dom66.de> |
|---|---|
| date | Mon, 15 Sep 2025 11:43:07 +0200 |
| parents | |
| children |
comparison
equal
deleted
inserted
replaced
| 1:1d09e1dec1d9 | 2:b50eed0cc0ef |
|---|---|
| 1 /*************************************************************************** | |
| 2 * _ _ ____ _ | |
| 3 * Project ___| | | | _ \| | | |
| 4 * / __| | | | |_) | | | |
| 5 * | (__| |_| | _ <| |___ | |
| 6 * \___|\___/|_| \_\_____| | |
| 7 * | |
| 8 * Copyright (C) 1998 - 2019, 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 https://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 "strcase.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_convert.h" | |
| 32 #include "tool_msgs.h" | |
| 33 #include "tool_binmode.h" | |
| 34 #include "tool_getparam.h" | |
| 35 #include "tool_paramhlp.h" | |
| 36 #include "tool_formparse.h" | |
| 37 | |
| 38 #include "memdebug.h" /* keep this as LAST include */ | |
| 39 | |
| 40 /* Macros to free const pointers. */ | |
| 41 #define CONST_FREE(x) free((void *) (x)) | |
| 42 #define CONST_SAFEFREE(x) Curl_safefree(*((void **) &(x))) | |
| 43 | |
| 44 /* tool_mime functions. */ | |
| 45 static tool_mime *tool_mime_new(tool_mime *parent, toolmimekind kind) | |
| 46 { | |
| 47 tool_mime *m = (tool_mime *) calloc(1, sizeof(*m)); | |
| 48 | |
| 49 if(m) { | |
| 50 m->kind = kind; | |
| 51 m->parent = parent; | |
| 52 if(parent) { | |
| 53 m->prev = parent->subparts; | |
| 54 parent->subparts = m; | |
| 55 } | |
| 56 } | |
| 57 return m; | |
| 58 } | |
| 59 | |
| 60 static tool_mime *tool_mime_new_parts(tool_mime *parent) | |
| 61 { | |
| 62 return tool_mime_new(parent, TOOLMIME_PARTS); | |
| 63 } | |
| 64 | |
| 65 static tool_mime *tool_mime_new_data(tool_mime *parent, const char *data) | |
| 66 { | |
| 67 tool_mime *m = NULL; | |
| 68 | |
| 69 data = strdup(data); | |
| 70 if(data) { | |
| 71 m = tool_mime_new(parent, TOOLMIME_DATA); | |
| 72 if(!m) | |
| 73 CONST_FREE(data); | |
| 74 else | |
| 75 m->data = data; | |
| 76 } | |
| 77 return m; | |
| 78 } | |
| 79 | |
| 80 static tool_mime *tool_mime_new_filedata(tool_mime *parent, | |
| 81 const char *filename, | |
| 82 bool isremotefile, | |
| 83 CURLcode *errcode) | |
| 84 { | |
| 85 CURLcode result = CURLE_OK; | |
| 86 tool_mime *m = NULL; | |
| 87 | |
| 88 *errcode = CURLE_OUT_OF_MEMORY; | |
| 89 if(strcmp(filename, "-")) { | |
| 90 /* This is a normal file. */ | |
| 91 filename = strdup(filename); | |
| 92 if(filename) { | |
| 93 m = tool_mime_new(parent, TOOLMIME_FILE); | |
| 94 if(!m) | |
| 95 CONST_FREE(filename); | |
| 96 else { | |
| 97 m->data = filename; | |
| 98 if(!isremotefile) | |
| 99 m->kind = TOOLMIME_FILEDATA; | |
| 100 *errcode = CURLE_OK; | |
| 101 } | |
| 102 } | |
| 103 } | |
| 104 else { /* Standard input. */ | |
| 105 int fd = fileno(stdin); | |
| 106 char *data = NULL; | |
| 107 curl_off_t size; | |
| 108 curl_off_t origin; | |
| 109 struct_stat sbuf; | |
| 110 | |
| 111 set_binmode(stdin); | |
| 112 origin = ftell(stdin); | |
| 113 /* If stdin is a regular file, do not buffer data but read it | |
| 114 when needed. */ | |
| 115 if(fd >= 0 && origin >= 0 && !fstat(fd, &sbuf) && | |
| 116 #ifdef __VMS | |
| 117 sbuf.st_fab_rfm != FAB$C_VAR && sbuf.st_fab_rfm != FAB$C_VFC && | |
| 118 #endif | |
| 119 S_ISREG(sbuf.st_mode)) { | |
| 120 size = sbuf.st_size - origin; | |
| 121 if(size < 0) | |
| 122 size = 0; | |
| 123 } | |
| 124 else { /* Not suitable for direct use, buffer stdin data. */ | |
| 125 size_t stdinsize = 0; | |
| 126 | |
| 127 if(file2memory(&data, &stdinsize, stdin) != PARAM_OK) { | |
| 128 /* Out of memory. */ | |
| 129 return m; | |
| 130 } | |
| 131 | |
| 132 if(ferror(stdin)) { | |
| 133 result = CURLE_READ_ERROR; | |
| 134 Curl_safefree(data); | |
| 135 data = NULL; | |
| 136 } | |
| 137 else if(!stdinsize) { | |
| 138 /* Zero-length data has been freed. Re-create it. */ | |
| 139 data = strdup(""); | |
| 140 if(!data) | |
| 141 return m; | |
| 142 } | |
| 143 size = curlx_uztoso(stdinsize); | |
| 144 origin = 0; | |
| 145 } | |
| 146 m = tool_mime_new(parent, TOOLMIME_STDIN); | |
| 147 if(!m) | |
| 148 Curl_safefree(data); | |
| 149 else { | |
| 150 m->data = data; | |
| 151 m->origin = origin; | |
| 152 m->size = size; | |
| 153 m->curpos = 0; | |
| 154 if(!isremotefile) | |
| 155 m->kind = TOOLMIME_STDINDATA; | |
| 156 *errcode = result; | |
| 157 } | |
| 158 } | |
| 159 return m; | |
| 160 } | |
| 161 | |
| 162 void tool_mime_free(tool_mime *mime) | |
| 163 { | |
| 164 if(mime) { | |
| 165 if(mime->subparts) | |
| 166 tool_mime_free(mime->subparts); | |
| 167 if(mime->prev) | |
| 168 tool_mime_free(mime->prev); | |
| 169 CONST_SAFEFREE(mime->name); | |
| 170 CONST_SAFEFREE(mime->filename); | |
| 171 CONST_SAFEFREE(mime->type); | |
| 172 CONST_SAFEFREE(mime->encoder); | |
| 173 CONST_SAFEFREE(mime->data); | |
| 174 curl_slist_free_all(mime->headers); | |
| 175 free(mime); | |
| 176 } | |
| 177 } | |
| 178 | |
| 179 | |
| 180 /* Mime part callbacks for stdin. */ | |
| 181 size_t tool_mime_stdin_read(char *buffer, | |
| 182 size_t size, size_t nitems, void *arg) | |
| 183 { | |
| 184 tool_mime *sip = (tool_mime *) arg; | |
| 185 curl_off_t bytesleft; | |
| 186 (void) size; /* Always 1: ignored. */ | |
| 187 | |
| 188 if(sip->size >= 0) { | |
| 189 if(sip->curpos >= sip->size) | |
| 190 return 0; /* At eof. */ | |
| 191 bytesleft = sip->size - sip->curpos; | |
| 192 if(curlx_uztoso(nitems) > bytesleft) | |
| 193 nitems = curlx_sotouz(bytesleft); | |
| 194 } | |
| 195 if(nitems) { | |
| 196 if(sip->data) { | |
| 197 /* Return data from memory. */ | |
| 198 memcpy(buffer, sip->data + curlx_sotouz(sip->curpos), nitems); | |
| 199 } | |
| 200 else { | |
| 201 /* Read from stdin. */ | |
| 202 nitems = fread(buffer, 1, nitems, stdin); | |
| 203 if(ferror(stdin)) { | |
| 204 /* Show error only once. */ | |
| 205 if(sip->config) { | |
| 206 warnf(sip->config, "stdin: %s\n", strerror(errno)); | |
| 207 sip->config = NULL; | |
| 208 } | |
| 209 return CURL_READFUNC_ABORT; | |
| 210 } | |
| 211 } | |
| 212 sip->curpos += curlx_uztoso(nitems); | |
| 213 } | |
| 214 return nitems; | |
| 215 } | |
| 216 | |
| 217 int tool_mime_stdin_seek(void *instream, curl_off_t offset, int whence) | |
| 218 { | |
| 219 tool_mime *sip = (tool_mime *) instream; | |
| 220 | |
| 221 switch(whence) { | |
| 222 case SEEK_CUR: | |
| 223 offset += sip->curpos; | |
| 224 break; | |
| 225 case SEEK_END: | |
| 226 offset += sip->size; | |
| 227 break; | |
| 228 } | |
| 229 if(offset < 0) | |
| 230 return CURL_SEEKFUNC_CANTSEEK; | |
| 231 if(!sip->data) { | |
| 232 if(fseek(stdin, (long) (offset + sip->origin), SEEK_SET)) | |
| 233 return CURL_SEEKFUNC_CANTSEEK; | |
| 234 } | |
| 235 sip->curpos = offset; | |
| 236 return CURL_SEEKFUNC_OK; | |
| 237 } | |
| 238 | |
| 239 /* Translate an internal mime tree into a libcurl mime tree. */ | |
| 240 | |
| 241 static CURLcode tool2curlparts(CURL *curl, tool_mime *m, curl_mime *mime) | |
| 242 { | |
| 243 CURLcode ret = CURLE_OK; | |
| 244 curl_mimepart *part = NULL; | |
| 245 curl_mime *submime = NULL; | |
| 246 const char *filename = NULL; | |
| 247 | |
| 248 if(m) { | |
| 249 ret = tool2curlparts(curl, m->prev, mime); | |
| 250 if(!ret) { | |
| 251 part = curl_mime_addpart(mime); | |
| 252 if(!part) | |
| 253 ret = CURLE_OUT_OF_MEMORY; | |
| 254 } | |
| 255 if(!ret) { | |
| 256 filename = m->filename; | |
| 257 switch(m->kind) { | |
| 258 case TOOLMIME_PARTS: | |
| 259 ret = tool2curlmime(curl, m, &submime); | |
| 260 if(!ret) { | |
| 261 ret = curl_mime_subparts(part, submime); | |
| 262 if(ret) | |
| 263 curl_mime_free(submime); | |
| 264 } | |
| 265 break; | |
| 266 | |
| 267 case TOOLMIME_DATA: | |
| 268 #ifdef CURL_DOES_CONVERSIONS | |
| 269 /* Our data is always textual: convert it to ASCII. */ | |
| 270 { | |
| 271 size_t size = strlen(m->data); | |
| 272 char *cp = malloc(size + 1); | |
| 273 | |
| 274 if(!cp) | |
| 275 ret = CURLE_OUT_OF_MEMORY; | |
| 276 else { | |
| 277 memcpy(cp, m->data, size + 1); | |
| 278 ret = convert_to_network(cp, size); | |
| 279 if(!ret) | |
| 280 ret = curl_mime_data(part, cp, CURL_ZERO_TERMINATED); | |
| 281 free(cp); | |
| 282 } | |
| 283 } | |
| 284 #else | |
| 285 ret = curl_mime_data(part, m->data, CURL_ZERO_TERMINATED); | |
| 286 #endif | |
| 287 break; | |
| 288 | |
| 289 case TOOLMIME_FILE: | |
| 290 case TOOLMIME_FILEDATA: | |
| 291 ret = curl_mime_filedata(part, m->data); | |
| 292 if(!ret && m->kind == TOOLMIME_FILEDATA && !filename) | |
| 293 ret = curl_mime_filename(part, NULL); | |
| 294 break; | |
| 295 | |
| 296 case TOOLMIME_STDIN: | |
| 297 if(!filename) | |
| 298 filename = "-"; | |
| 299 /* FALLTHROUGH */ | |
| 300 case TOOLMIME_STDINDATA: | |
| 301 ret = curl_mime_data_cb(part, m->size, | |
| 302 (curl_read_callback) tool_mime_stdin_read, | |
| 303 (curl_seek_callback) tool_mime_stdin_seek, | |
| 304 NULL, m); | |
| 305 break; | |
| 306 | |
| 307 default: | |
| 308 /* Other cases not possible in this context. */ | |
| 309 break; | |
| 310 } | |
| 311 } | |
| 312 if(!ret && filename) | |
| 313 ret = curl_mime_filename(part, filename); | |
| 314 if(!ret) | |
| 315 ret = curl_mime_type(part, m->type); | |
| 316 if(!ret) | |
| 317 ret = curl_mime_headers(part, m->headers, 0); | |
| 318 if(!ret) | |
| 319 ret = curl_mime_encoder(part, m->encoder); | |
| 320 if(!ret) | |
| 321 ret = curl_mime_name(part, m->name); | |
| 322 } | |
| 323 return ret; | |
| 324 } | |
| 325 | |
| 326 CURLcode tool2curlmime(CURL *curl, tool_mime *m, curl_mime **mime) | |
| 327 { | |
| 328 CURLcode ret = CURLE_OK; | |
| 329 | |
| 330 *mime = curl_mime_init(curl); | |
| 331 if(!*mime) | |
| 332 ret = CURLE_OUT_OF_MEMORY; | |
| 333 else | |
| 334 ret = tool2curlparts(curl, m->subparts, *mime); | |
| 335 if(ret) { | |
| 336 curl_mime_free(*mime); | |
| 337 *mime = NULL; | |
| 338 } | |
| 339 return ret; | |
| 340 } | |
| 341 | |
| 342 /* | |
| 343 * helper function to get a word from form param | |
| 344 * after call get_parm_word, str either point to string end | |
| 345 * or point to any of end chars. | |
| 346 */ | |
| 347 static char *get_param_word(char **str, char **end_pos, char endchar) | |
| 348 { | |
| 349 char *ptr = *str; | |
| 350 /* the first non-space char is here */ | |
| 351 char *word_begin = ptr; | |
| 352 char *ptr2; | |
| 353 char *escape = NULL; | |
| 354 | |
| 355 if(*ptr == '"') { | |
| 356 ++ptr; | |
| 357 while(*ptr) { | |
| 358 if(*ptr == '\\') { | |
| 359 if(ptr[1] == '\\' || ptr[1] == '"') { | |
| 360 /* remember the first escape position */ | |
| 361 if(!escape) | |
| 362 escape = ptr; | |
| 363 /* skip escape of back-slash or double-quote */ | |
| 364 ptr += 2; | |
| 365 continue; | |
| 366 } | |
| 367 } | |
| 368 if(*ptr == '"') { | |
| 369 *end_pos = ptr; | |
| 370 if(escape) { | |
| 371 /* has escape, we restore the unescaped string here */ | |
| 372 ptr = ptr2 = escape; | |
| 373 do { | |
| 374 if(*ptr == '\\' && (ptr[1] == '\\' || ptr[1] == '"')) | |
| 375 ++ptr; | |
| 376 *ptr2++ = *ptr++; | |
| 377 } | |
| 378 while(ptr < *end_pos); | |
| 379 *end_pos = ptr2; | |
| 380 } | |
| 381 while(*ptr && *ptr != ';' && *ptr != endchar) | |
| 382 ++ptr; | |
| 383 *str = ptr; | |
| 384 return word_begin + 1; | |
| 385 } | |
| 386 ++ptr; | |
| 387 } | |
| 388 /* end quote is missing, treat it as non-quoted. */ | |
| 389 ptr = word_begin; | |
| 390 } | |
| 391 | |
| 392 while(*ptr && *ptr != ';' && *ptr != endchar) | |
| 393 ++ptr; | |
| 394 *str = *end_pos = ptr; | |
| 395 return word_begin; | |
| 396 } | |
| 397 | |
| 398 /* Append slist item and return -1 if failed. */ | |
| 399 static int slist_append(struct curl_slist **plist, const char *data) | |
| 400 { | |
| 401 struct curl_slist *s = curl_slist_append(*plist, data); | |
| 402 | |
| 403 if(!s) | |
| 404 return -1; | |
| 405 | |
| 406 *plist = s; | |
| 407 return 0; | |
| 408 } | |
| 409 | |
| 410 /* Read headers from a file and append to list. */ | |
| 411 static int read_field_headers(struct OperationConfig *config, | |
| 412 const char *filename, FILE *fp, | |
| 413 struct curl_slist **pheaders) | |
| 414 { | |
| 415 size_t hdrlen = 0; | |
| 416 size_t pos = 0; | |
| 417 bool incomment = FALSE; | |
| 418 int lineno = 1; | |
| 419 char hdrbuf[999]; /* Max. header length + 1. */ | |
| 420 | |
| 421 for(;;) { | |
| 422 int c = getc(fp); | |
| 423 if(c == EOF || (!pos && !ISSPACE(c))) { | |
| 424 /* Strip and flush the current header. */ | |
| 425 while(hdrlen && ISSPACE(hdrbuf[hdrlen - 1])) | |
| 426 hdrlen--; | |
| 427 if(hdrlen) { | |
| 428 hdrbuf[hdrlen] = '\0'; | |
| 429 if(slist_append(pheaders, hdrbuf)) { | |
| 430 fprintf(config->global->errors, | |
| 431 "Out of memory for field headers!\n"); | |
| 432 return -1; | |
| 433 } | |
| 434 hdrlen = 0; | |
| 435 } | |
| 436 } | |
| 437 | |
| 438 switch(c) { | |
| 439 case EOF: | |
| 440 if(ferror(fp)) { | |
| 441 fprintf(config->global->errors, | |
| 442 "Header file %s read error: %s\n", filename, strerror(errno)); | |
| 443 return -1; | |
| 444 } | |
| 445 return 0; /* Done. */ | |
| 446 case '\r': | |
| 447 continue; /* Ignore. */ | |
| 448 case '\n': | |
| 449 pos = 0; | |
| 450 incomment = FALSE; | |
| 451 lineno++; | |
| 452 continue; | |
| 453 case '#': | |
| 454 if(!pos) | |
| 455 incomment = TRUE; | |
| 456 break; | |
| 457 } | |
| 458 | |
| 459 pos++; | |
| 460 if(!incomment) { | |
| 461 if(hdrlen == sizeof(hdrbuf) - 1) { | |
| 462 warnf(config->global, "File %s line %d: header too long (truncated)\n", | |
| 463 filename, lineno); | |
| 464 c = ' '; | |
| 465 } | |
| 466 if(hdrlen <= sizeof(hdrbuf) - 1) | |
| 467 hdrbuf[hdrlen++] = (char) c; | |
| 468 } | |
| 469 } | |
| 470 /* NOTREACHED */ | |
| 471 } | |
| 472 | |
| 473 static int get_param_part(struct OperationConfig *config, char endchar, | |
| 474 char **str, char **pdata, char **ptype, | |
| 475 char **pfilename, char **pencoder, | |
| 476 struct curl_slist **pheaders) | |
| 477 { | |
| 478 char *p = *str; | |
| 479 char *type = NULL; | |
| 480 char *filename = NULL; | |
| 481 char *encoder = NULL; | |
| 482 char *endpos; | |
| 483 char *tp; | |
| 484 char sep; | |
| 485 char type_major[128] = ""; | |
| 486 char type_minor[128] = ""; | |
| 487 char *endct = NULL; | |
| 488 struct curl_slist *headers = NULL; | |
| 489 | |
| 490 if(ptype) | |
| 491 *ptype = NULL; | |
| 492 if(pfilename) | |
| 493 *pfilename = NULL; | |
| 494 if(pheaders) | |
| 495 *pheaders = NULL; | |
| 496 if(pencoder) | |
| 497 *pencoder = NULL; | |
| 498 while(ISSPACE(*p)) | |
| 499 p++; | |
| 500 tp = p; | |
| 501 *pdata = get_param_word(&p, &endpos, endchar); | |
| 502 /* If not quoted, strip trailing spaces. */ | |
| 503 if(*pdata == tp) | |
| 504 while(endpos > *pdata && ISSPACE(endpos[-1])) | |
| 505 endpos--; | |
| 506 sep = *p; | |
| 507 *endpos = '\0'; | |
| 508 while(sep == ';') { | |
| 509 while(ISSPACE(*++p)) | |
| 510 ; | |
| 511 | |
| 512 if(!endct && checkprefix("type=", p)) { | |
| 513 for(p += 5; ISSPACE(*p); p++) | |
| 514 ; | |
| 515 /* set type pointer */ | |
| 516 type = p; | |
| 517 | |
| 518 /* verify that this is a fine type specifier */ | |
| 519 if(2 != sscanf(type, "%127[^/ ]/%127[^;, \n]", type_major, type_minor)) { | |
| 520 warnf(config->global, "Illegally formatted content-type field!\n"); | |
| 521 curl_slist_free_all(headers); | |
| 522 return -1; /* illegal content-type syntax! */ | |
| 523 } | |
| 524 | |
| 525 /* now point beyond the content-type specifier */ | |
| 526 p = type + strlen(type_major) + strlen(type_minor) + 1; | |
| 527 for(endct = p; *p && *p != ';' && *p != endchar; p++) | |
| 528 if(!ISSPACE(*p)) | |
| 529 endct = p + 1; | |
| 530 sep = *p; | |
| 531 } | |
| 532 else if(checkprefix("filename=", p)) { | |
| 533 if(endct) { | |
| 534 *endct = '\0'; | |
| 535 endct = NULL; | |
| 536 } | |
| 537 for(p += 9; ISSPACE(*p); p++) | |
| 538 ; | |
| 539 tp = p; | |
| 540 filename = get_param_word(&p, &endpos, endchar); | |
| 541 /* If not quoted, strip trailing spaces. */ | |
| 542 if(filename == tp) | |
| 543 while(endpos > filename && ISSPACE(endpos[-1])) | |
| 544 endpos--; | |
| 545 sep = *p; | |
| 546 *endpos = '\0'; | |
| 547 } | |
| 548 else if(checkprefix("headers=", p)) { | |
| 549 if(endct) { | |
| 550 *endct = '\0'; | |
| 551 endct = NULL; | |
| 552 } | |
| 553 p += 8; | |
| 554 if(*p == '@' || *p == '<') { | |
| 555 char *hdrfile; | |
| 556 FILE *fp; | |
| 557 /* Read headers from a file. */ | |
| 558 | |
| 559 do { | |
| 560 p++; | |
| 561 } while(ISSPACE(*p)); | |
| 562 tp = p; | |
| 563 hdrfile = get_param_word(&p, &endpos, endchar); | |
| 564 /* If not quoted, strip trailing spaces. */ | |
| 565 if(hdrfile == tp) | |
| 566 while(endpos > hdrfile && ISSPACE(endpos[-1])) | |
| 567 endpos--; | |
| 568 sep = *p; | |
| 569 *endpos = '\0'; | |
| 570 fp = fopen(hdrfile, FOPEN_READTEXT); | |
| 571 if(!fp) | |
| 572 warnf(config->global, "Cannot read from %s: %s\n", hdrfile, | |
| 573 strerror(errno)); | |
| 574 else { | |
| 575 int i = read_field_headers(config, hdrfile, fp, &headers); | |
| 576 | |
| 577 fclose(fp); | |
| 578 if(i) { | |
| 579 curl_slist_free_all(headers); | |
| 580 return -1; | |
| 581 } | |
| 582 } | |
| 583 } | |
| 584 else { | |
| 585 char *hdr; | |
| 586 | |
| 587 while(ISSPACE(*p)) | |
| 588 p++; | |
| 589 tp = p; | |
| 590 hdr = get_param_word(&p, &endpos, endchar); | |
| 591 /* If not quoted, strip trailing spaces. */ | |
| 592 if(hdr == tp) | |
| 593 while(endpos > hdr && ISSPACE(endpos[-1])) | |
| 594 endpos--; | |
| 595 sep = *p; | |
| 596 *endpos = '\0'; | |
| 597 if(slist_append(&headers, hdr)) { | |
| 598 fprintf(config->global->errors, "Out of memory for field header!\n"); | |
| 599 curl_slist_free_all(headers); | |
| 600 return -1; | |
| 601 } | |
| 602 } | |
| 603 } | |
| 604 else if(checkprefix("encoder=", p)) { | |
| 605 if(endct) { | |
| 606 *endct = '\0'; | |
| 607 endct = NULL; | |
| 608 } | |
| 609 for(p += 8; ISSPACE(*p); p++) | |
| 610 ; | |
| 611 tp = p; | |
| 612 encoder = get_param_word(&p, &endpos, endchar); | |
| 613 /* If not quoted, strip trailing spaces. */ | |
| 614 if(encoder == tp) | |
| 615 while(endpos > encoder && ISSPACE(endpos[-1])) | |
| 616 endpos--; | |
| 617 sep = *p; | |
| 618 *endpos = '\0'; | |
| 619 } | |
| 620 else if(endct) { | |
| 621 /* This is part of content type. */ | |
| 622 for(endct = p; *p && *p != ';' && *p != endchar; p++) | |
| 623 if(!ISSPACE(*p)) | |
| 624 endct = p + 1; | |
| 625 sep = *p; | |
| 626 } | |
| 627 else { | |
| 628 /* unknown prefix, skip to next block */ | |
| 629 char *unknown = get_param_word(&p, &endpos, endchar); | |
| 630 | |
| 631 sep = *p; | |
| 632 *endpos = '\0'; | |
| 633 if(*unknown) | |
| 634 warnf(config->global, "skip unknown form field: %s\n", unknown); | |
| 635 } | |
| 636 } | |
| 637 | |
| 638 /* Terminate content type. */ | |
| 639 if(endct) | |
| 640 *endct = '\0'; | |
| 641 | |
| 642 if(ptype) | |
| 643 *ptype = type; | |
| 644 else if(type) | |
| 645 warnf(config->global, "Field content type not allowed here: %s\n", type); | |
| 646 | |
| 647 if(pfilename) | |
| 648 *pfilename = filename; | |
| 649 else if(filename) | |
| 650 warnf(config->global, | |
| 651 "Field file name not allowed here: %s\n", filename); | |
| 652 | |
| 653 if(pencoder) | |
| 654 *pencoder = encoder; | |
| 655 else if(encoder) | |
| 656 warnf(config->global, | |
| 657 "Field encoder not allowed here: %s\n", encoder); | |
| 658 | |
| 659 if(pheaders) | |
| 660 *pheaders = headers; | |
| 661 else if(headers) { | |
| 662 warnf(config->global, | |
| 663 "Field headers not allowed here: %s\n", headers->data); | |
| 664 curl_slist_free_all(headers); | |
| 665 } | |
| 666 | |
| 667 *str = p; | |
| 668 return sep & 0xFF; | |
| 669 } | |
| 670 | |
| 671 | |
| 672 /*************************************************************************** | |
| 673 * | |
| 674 * formparse() | |
| 675 * | |
| 676 * Reads a 'name=value' parameter and builds the appropriate linked list. | |
| 677 * | |
| 678 * If the value is of the form '<filename', field data is read from the | |
| 679 * given file. | |
| 680 | |
| 681 * Specify files to upload with 'name=@filename', or 'name=@"filename"' | |
| 682 * in case the filename contain ',' or ';'. Supports specified | |
| 683 * given Content-Type of the files. Such as ';type=<content-type>'. | |
| 684 * | |
| 685 * If literal_value is set, any initial '@' or '<' in the value string | |
| 686 * loses its special meaning, as does any embedded ';type='. | |
| 687 * | |
| 688 * You may specify more than one file for a single name (field). Specify | |
| 689 * multiple files by writing it like: | |
| 690 * | |
| 691 * 'name=@filename,filename2,filename3' | |
| 692 * | |
| 693 * or use double-quotes quote the filename: | |
| 694 * | |
| 695 * 'name=@"filename","filename2","filename3"' | |
| 696 * | |
| 697 * If you want content-types specified for each too, write them like: | |
| 698 * | |
| 699 * 'name=@filename;type=image/gif,filename2,filename3' | |
| 700 * | |
| 701 * If you want custom headers added for a single part, write them in a separate | |
| 702 * file and do like this: | |
| 703 * | |
| 704 * 'name=foo;headers=@headerfile' or why not | |
| 705 * 'name=@filemame;headers=@headerfile' | |
| 706 * | |
| 707 * To upload a file, but to fake the file name that will be included in the | |
| 708 * formpost, do like this: | |
| 709 * | |
| 710 * 'name=@filename;filename=/dev/null' or quote the faked filename like: | |
| 711 * 'name=@filename;filename="play, play, and play.txt"' | |
| 712 * | |
| 713 * If filename/path contains ',' or ';', it must be quoted by double-quotes, | |
| 714 * else curl will fail to figure out the correct filename. if the filename | |
| 715 * tobe quoted contains '"' or '\', '"' and '\' must be escaped by backslash. | |
| 716 * | |
| 717 ***************************************************************************/ | |
| 718 | |
| 719 /* Convenience macros for null pointer check. */ | |
| 720 #define NULL_CHECK(ptr, init, retcode) { \ | |
| 721 (ptr) = (init); \ | |
| 722 if(!(ptr)) { \ | |
| 723 warnf(config->global, "out of memory!\n"); \ | |
| 724 curl_slist_free_all(headers); \ | |
| 725 Curl_safefree(contents); \ | |
| 726 return retcode; \ | |
| 727 } \ | |
| 728 } | |
| 729 #define SET_TOOL_MIME_PTR(m, field, retcode) { \ | |
| 730 if(field) \ | |
| 731 NULL_CHECK((m)->field, strdup(field), retcode); \ | |
| 732 } | |
| 733 | |
| 734 int formparse(struct OperationConfig *config, | |
| 735 const char *input, | |
| 736 tool_mime **mimeroot, | |
| 737 tool_mime **mimecurrent, | |
| 738 bool literal_value) | |
| 739 { | |
| 740 /* input MUST be a string in the format 'name=contents' and we'll | |
| 741 build a linked list with the info */ | |
| 742 char *name = NULL; | |
| 743 char *contents = NULL; | |
| 744 char *contp; | |
| 745 char *data; | |
| 746 char *type = NULL; | |
| 747 char *filename = NULL; | |
| 748 char *encoder = NULL; | |
| 749 struct curl_slist *headers = NULL; | |
| 750 tool_mime *part = NULL; | |
| 751 CURLcode res; | |
| 752 | |
| 753 /* Allocate the main mime structure if needed. */ | |
| 754 if(!*mimecurrent) { | |
| 755 NULL_CHECK(*mimeroot, tool_mime_new_parts(NULL), 1); | |
| 756 *mimecurrent = *mimeroot; | |
| 757 } | |
| 758 | |
| 759 /* Make a copy we can overwrite. */ | |
| 760 NULL_CHECK(contents, strdup(input), 2); | |
| 761 | |
| 762 /* Scan for the end of the name. */ | |
| 763 contp = strchr(contents, '='); | |
| 764 if(contp) { | |
| 765 int sep = '\0'; | |
| 766 if(contp > contents) | |
| 767 name = contents; | |
| 768 *contp++ = '\0'; | |
| 769 | |
| 770 if(*contp == '(' && !literal_value) { | |
| 771 /* Starting a multipart. */ | |
| 772 sep = get_param_part(config, '\0', | |
| 773 &contp, &data, &type, NULL, NULL, &headers); | |
| 774 if(sep < 0) { | |
| 775 Curl_safefree(contents); | |
| 776 return 3; | |
| 777 } | |
| 778 NULL_CHECK(part, tool_mime_new_parts(*mimecurrent), 4); | |
| 779 *mimecurrent = part; | |
| 780 part->headers = headers; | |
| 781 headers = NULL; | |
| 782 SET_TOOL_MIME_PTR(part, type, 5); | |
| 783 } | |
| 784 else if(!name && !strcmp(contp, ")") && !literal_value) { | |
| 785 /* Ending a multipart. */ | |
| 786 if(*mimecurrent == *mimeroot) { | |
| 787 warnf(config->global, "no multipart to terminate!\n"); | |
| 788 Curl_safefree(contents); | |
| 789 return 6; | |
| 790 } | |
| 791 *mimecurrent = (*mimecurrent)->parent; | |
| 792 } | |
| 793 else if('@' == contp[0] && !literal_value) { | |
| 794 | |
| 795 /* we use the @-letter to indicate file name(s) */ | |
| 796 | |
| 797 tool_mime *subparts = NULL; | |
| 798 | |
| 799 do { | |
| 800 /* since this was a file, it may have a content-type specifier | |
| 801 at the end too, or a filename. Or both. */ | |
| 802 ++contp; | |
| 803 sep = get_param_part(config, ',', &contp, | |
| 804 &data, &type, &filename, &encoder, &headers); | |
| 805 if(sep < 0) { | |
| 806 Curl_safefree(contents); | |
| 807 return 7; | |
| 808 } | |
| 809 | |
| 810 /* now contp point to comma or string end. | |
| 811 If more files to come, make sure we have multiparts. */ | |
| 812 if(!subparts) { | |
| 813 if(sep != ',') /* If there is a single file. */ | |
| 814 subparts = *mimecurrent; | |
| 815 else | |
| 816 NULL_CHECK(subparts, tool_mime_new_parts(*mimecurrent), 8); | |
| 817 } | |
| 818 | |
| 819 /* Store that file in a part. */ | |
| 820 NULL_CHECK(part, | |
| 821 tool_mime_new_filedata(subparts, data, TRUE, &res), 9); | |
| 822 part->headers = headers; | |
| 823 headers = NULL; | |
| 824 part->config = config->global; | |
| 825 if(res == CURLE_READ_ERROR) { | |
| 826 /* An error occurred while reading stdin: if read has started, | |
| 827 issue the error now. Else, delay it until processed by | |
| 828 libcurl. */ | |
| 829 if(part->size > 0) { | |
| 830 warnf(config->global, | |
| 831 "error while reading standard input\n"); | |
| 832 Curl_safefree(contents); | |
| 833 return 10; | |
| 834 } | |
| 835 CONST_SAFEFREE(part->data); | |
| 836 part->data = NULL; | |
| 837 part->size = -1; | |
| 838 res = CURLE_OK; | |
| 839 } | |
| 840 SET_TOOL_MIME_PTR(part, filename, 11); | |
| 841 SET_TOOL_MIME_PTR(part, type, 12); | |
| 842 SET_TOOL_MIME_PTR(part, encoder, 13); | |
| 843 | |
| 844 /* *contp could be '\0', so we just check with the delimiter */ | |
| 845 } while(sep); /* loop if there's another file name */ | |
| 846 part = (*mimecurrent)->subparts; /* Set name on group. */ | |
| 847 } | |
| 848 else { | |
| 849 if(*contp == '<' && !literal_value) { | |
| 850 ++contp; | |
| 851 sep = get_param_part(config, '\0', &contp, | |
| 852 &data, &type, NULL, &encoder, &headers); | |
| 853 if(sep < 0) { | |
| 854 Curl_safefree(contents); | |
| 855 return 14; | |
| 856 } | |
| 857 | |
| 858 NULL_CHECK(part, tool_mime_new_filedata(*mimecurrent, data, FALSE, | |
| 859 &res), 15); | |
| 860 part->headers = headers; | |
| 861 headers = NULL; | |
| 862 part->config = config->global; | |
| 863 if(res == CURLE_READ_ERROR) { | |
| 864 /* An error occurred while reading stdin: if read has started, | |
| 865 issue the error now. Else, delay it until processed by | |
| 866 libcurl. */ | |
| 867 if(part->size > 0) { | |
| 868 warnf(config->global, | |
| 869 "error while reading standard input\n"); | |
| 870 Curl_safefree(contents); | |
| 871 return 16; | |
| 872 } | |
| 873 CONST_SAFEFREE(part->data); | |
| 874 part->data = NULL; | |
| 875 part->size = -1; | |
| 876 res = CURLE_OK; | |
| 877 } | |
| 878 } | |
| 879 else { | |
| 880 if(literal_value) | |
| 881 data = contp; | |
| 882 else { | |
| 883 sep = get_param_part(config, '\0', &contp, | |
| 884 &data, &type, &filename, &encoder, &headers); | |
| 885 if(sep < 0) { | |
| 886 Curl_safefree(contents); | |
| 887 return 17; | |
| 888 } | |
| 889 } | |
| 890 | |
| 891 NULL_CHECK(part, tool_mime_new_data(*mimecurrent, data), 18); | |
| 892 part->headers = headers; | |
| 893 headers = NULL; | |
| 894 } | |
| 895 | |
| 896 SET_TOOL_MIME_PTR(part, filename, 19); | |
| 897 SET_TOOL_MIME_PTR(part, type, 20); | |
| 898 SET_TOOL_MIME_PTR(part, encoder, 21); | |
| 899 | |
| 900 if(sep) { | |
| 901 *contp = (char) sep; | |
| 902 warnf(config->global, | |
| 903 "garbage at end of field specification: %s\n", contp); | |
| 904 } | |
| 905 } | |
| 906 | |
| 907 /* Set part name. */ | |
| 908 SET_TOOL_MIME_PTR(part, name, 22); | |
| 909 } | |
| 910 else { | |
| 911 warnf(config->global, "Illegally formatted input field!\n"); | |
| 912 Curl_safefree(contents); | |
| 913 return 23; | |
| 914 } | |
| 915 Curl_safefree(contents); | |
| 916 return 0; | |
| 917 } |
