tool_formparse.c 24 KB

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