tool_formparse.c 25 KB

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