1 /***************************************************************************
3 * Project ___| | | | _ \| |
5 * | (__| |_| | _ <| |___
6 * \___|\___/|_| \_\_____|
8 * Copyright (C) 1998 - 2018, Daniel Stenberg, <daniel@haxx.se>, et al.
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.
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.
18 * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY
19 * KIND, either express or implied.
21 ***************************************************************************/
23 #include "curl_setup.h"
25 #include <curl/curl.h>
28 #include "non-ascii.h"
32 #if !defined(CURL_DISABLE_HTTP) || !defined(CURL_DISABLE_SMTP) || \
33 !defined(CURL_DISABLE_IMAP)
35 #if defined(HAVE_LIBGEN_H) && defined(HAVE_BASENAME)
42 /* The last 3 #include files should be in this order */
43 #include "curl_printf.h"
44 #include "curl_memory.h"
54 #define READ_ERROR ((size_t) -1)
57 static size_t encoder_nop_read(char *buffer, size_t size, bool ateof,
59 static curl_off_t encoder_nop_size(curl_mimepart *part);
60 static size_t encoder_7bit_read(char *buffer, size_t size, bool ateof,
62 static size_t encoder_base64_read(char *buffer, size_t size, bool ateof,
64 static curl_off_t encoder_base64_size(curl_mimepart *part);
65 static size_t encoder_qp_read(char *buffer, size_t size, bool ateof,
67 static curl_off_t encoder_qp_size(curl_mimepart *part);
69 static const mime_encoder encoders[] = {
70 {"binary", encoder_nop_read, encoder_nop_size},
71 {"8bit", encoder_nop_read, encoder_nop_size},
72 {"7bit", encoder_7bit_read, encoder_nop_size},
73 {"base64", encoder_base64_read, encoder_base64_size},
74 {"quoted-printable", encoder_qp_read, encoder_qp_size},
75 {ZERO_NULL, ZERO_NULL, ZERO_NULL}
78 /* Base64 encoding table */
79 static const char base64[] =
80 "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/";
82 /* Quoted-printable character class table.
84 * We cannot rely on ctype functions since quoted-printable input data
85 * is assumed to be ascii-compatible, even on non-ascii platforms. */
86 #define QP_OK 1 /* Can be represented by itself. */
87 #define QP_SP 2 /* Space or tab. */
88 #define QP_CR 3 /* Carriage return. */
89 #define QP_LF 4 /* Line-feed. */
90 static const unsigned char qp_class[] = {
91 0, 0, 0, 0, 0, 0, 0, 0, /* 00 - 07 */
92 0, QP_SP, QP_LF, 0, 0, QP_CR, 0, 0, /* 08 - 0F */
93 0, 0, 0, 0, 0, 0, 0, 0, /* 10 - 17 */
94 0, 0, 0, 0, 0, 0, 0, 0, /* 18 - 1F */
95 QP_SP, QP_OK, QP_OK, QP_OK, QP_OK, QP_OK, QP_OK, QP_OK, /* 20 - 27 */
96 QP_OK, QP_OK, QP_OK, QP_OK, QP_OK, QP_OK, QP_OK, QP_OK, /* 28 - 2F */
97 QP_OK, QP_OK, QP_OK, QP_OK, QP_OK, QP_OK, QP_OK, QP_OK, /* 30 - 37 */
98 QP_OK, QP_OK, QP_OK, QP_OK, QP_OK, 0 , QP_OK, QP_OK, /* 38 - 3F */
99 QP_OK, QP_OK, QP_OK, QP_OK, QP_OK, QP_OK, QP_OK, QP_OK, /* 40 - 47 */
100 QP_OK, QP_OK, QP_OK, QP_OK, QP_OK, QP_OK, QP_OK, QP_OK, /* 48 - 4F */
101 QP_OK, QP_OK, QP_OK, QP_OK, QP_OK, QP_OK, QP_OK, QP_OK, /* 50 - 57 */
102 QP_OK, QP_OK, QP_OK, QP_OK, QP_OK, QP_OK, QP_OK, QP_OK, /* 58 - 5F */
103 QP_OK, QP_OK, QP_OK, QP_OK, QP_OK, QP_OK, QP_OK, QP_OK, /* 60 - 67 */
104 QP_OK, QP_OK, QP_OK, QP_OK, QP_OK, QP_OK, QP_OK, QP_OK, /* 68 - 6F */
105 QP_OK, QP_OK, QP_OK, QP_OK, QP_OK, QP_OK, QP_OK, QP_OK, /* 70 - 77 */
106 QP_OK, QP_OK, QP_OK, QP_OK, QP_OK, QP_OK, QP_OK, 0, /* 78 - 7F */
107 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, /* 80 - 8F */
108 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, /* 90 - 9F */
109 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, /* A0 - AF */
110 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, /* B0 - BF */
111 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, /* C0 - CF */
112 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, /* D0 - DF */
113 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, /* E0 - EF */
114 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 /* F0 - FF */
118 /* Binary --> hexadecimal ASCII table. */
119 static const char aschex[] =
120 "\x30\x31\x32\x33\x34\x35\x36\x37\x38\x39\x41\x42\x43\x44\x45\x46";
125 #define filesize(name, stat_data) (stat_data.st_size)
126 #define fopen_read fopen
132 * get_vms_file_size does what it takes to get the real size of the file
134 * For fixed files, find out the size of the EOF block and adjust.
136 * For all others, have to read the entire file in, discarding the contents.
137 * Most posted text files will be small, and binary files like zlib archives
138 * and CD/DVD images should be either a STREAM_LF format or a fixed format.
141 curl_off_t VmsRealFileSize(const char *name,
142 const struct_stat *stat_buf)
149 file = fopen(name, FOPEN_READTEXT); /* VMS */
155 while(ret_stat > 0) {
156 ret_stat = fread(buffer, 1, sizeof(buffer), file);
167 * VmsSpecialSize checks to see if the stat st_size can be trusted and
168 * if not to call a routine to get the correct size.
171 static curl_off_t VmsSpecialSize(const char *name,
172 const struct_stat *stat_buf)
174 switch(stat_buf->st_fab_rfm) {
177 return VmsRealFileSize(name, stat_buf);
180 return stat_buf->st_size;
184 #define filesize(name, stat_data) VmsSpecialSize(name, &stat_data)
189 * For upload to work as expected on VMS, different optional
190 * parameters must be added to the fopen command based on
191 * record format of the file.
194 static FILE * vmsfopenread(const char *file, const char *mode)
199 result = stat(file, &statbuf);
201 switch(statbuf.st_fab_rfm) {
205 return fopen(file, FOPEN_READTEXT); /* VMS */
208 return fopen(file, FOPEN_READTEXT, "rfm=stmlf", "ctx=stm");
212 #define fopen_read vmsfopenread
216 #ifndef HAVE_BASENAME
218 (Quote from The Open Group Base Specifications Issue 6 IEEE Std 1003.1, 2004
221 The basename() function shall take the pathname pointed to by path and
222 return a pointer to the final component of the pathname, deleting any
223 trailing '/' characters.
225 If the string pointed to by path consists entirely of the '/' character,
226 basename() shall return a pointer to the string "/". If the string pointed
227 to by path is exactly "//", it is implementation-defined whether '/' or "//"
230 If path is a null pointer or points to an empty string, basename() shall
231 return a pointer to the string ".".
233 The basename() function may modify the string pointed to by path, and may
234 return a pointer to static storage that may then be overwritten by a
235 subsequent call to basename().
237 The basename() function need not be reentrant. A function that is not
238 required to be reentrant is not required to be thread-safe.
241 static char *Curl_basename(char *path)
243 /* Ignore all the details above for now and make a quick and simple
244 implementaion here */
248 s1 = strrchr(path, '/');
249 s2 = strrchr(path, '\\');
252 path = (s1 > s2? s1 : s2) + 1;
262 #define basename(x) Curl_basename((x))
266 /* Set readback state. */
267 static void mimesetstate(mime_state *state, enum mimestate tok, void *ptr)
275 /* Escape header string into allocated memory. */
276 static char *escape_string(const char *src)
278 size_t bytecount = 0;
282 for(i = 0; src[i]; i++)
283 if(src[i] == '"' || src[i] == '\\')
287 dst = malloc(bytecount + 1);
291 for(i = 0; *src; src++) {
292 if(*src == '"' || *src == '\\')
301 /* Check if header matches. */
302 static char *match_header(struct curl_slist *hdr, const char *lbl, size_t len)
306 if(strncasecompare(hdr->data, lbl, len) && hdr->data[len] == ':')
307 for(value = hdr->data + len + 1; *value == ' '; value++)
312 /* Get a header from an slist. */
313 static char *search_header(struct curl_slist *hdrlist, const char *hdr)
315 size_t len = strlen(hdr);
318 for(; !value && hdrlist; hdrlist = hdrlist->next)
319 value = match_header(hdrlist, hdr, len);
324 static char *strippath(const char *fullfile)
328 filename = strdup(fullfile); /* duplicate since basename() may ruin the
329 buffer it works on */
332 base = strdup(basename(filename));
334 free(filename); /* free temporary buffer */
336 return base; /* returns an allocated string or NULL ! */
339 /* Initialize data encoder state. */
340 static void cleanup_encoder_state(mime_encoder_state *p)
348 /* Dummy encoder. This is used for 8bit and binary content encodings. */
349 static size_t encoder_nop_read(char *buffer, size_t size, bool ateof,
352 mime_encoder_state *st = &part->encstate;
353 size_t insize = st->bufend - st->bufbeg;
360 memcpy(buffer, st->buf, size);
365 static curl_off_t encoder_nop_size(curl_mimepart *part)
367 return part->datasize;
371 /* 7bit encoder: the encoder is just a data validity check. */
372 static size_t encoder_7bit_read(char *buffer, size_t size, bool ateof,
375 mime_encoder_state *st = &part->encstate;
376 size_t cursize = st->bufend - st->bufbeg;
383 for(cursize = 0; cursize < size; cursize++) {
384 *buffer = st->buf[st->bufbeg];
386 return cursize? cursize: READ_ERROR;
394 /* Base64 content encoder. */
395 static size_t encoder_base64_read(char *buffer, size_t size, bool ateof,
398 mime_encoder_state *st = &part->encstate;
403 while(st->bufbeg < st->bufend) {
405 if(st->pos > MAX_ENCODED_LINE_LENGTH - 4) {
406 /* Yes, we need 2 characters for CRLF. */
416 /* Be sure there is enough space and input data for a base64 group. */
417 if(size < 4 || st->bufend - st->bufbeg < 3)
420 /* Encode three bytes as four characters. */
421 i = st->buf[st->bufbeg++] & 0xFF;
422 i = (i << 8) | (st->buf[st->bufbeg++] & 0xFF);
423 i = (i << 8) | (st->buf[st->bufbeg++] & 0xFF);
424 *ptr++ = base64[(i >> 18) & 0x3F];
425 *ptr++ = base64[(i >> 12) & 0x3F];
426 *ptr++ = base64[(i >> 6) & 0x3F];
427 *ptr++ = base64[i & 0x3F];
433 /* If at eof, we have to flush the buffered data. */
434 if(ateof && size >= 4) {
435 /* Buffered data size can only be 0, 1 or 2. */
436 ptr[2] = ptr[3] = '=';
438 switch(st->bufend - st->bufbeg) {
440 i = (st->buf[st->bufbeg + 1] & 0xFF) << 8;
443 i |= (st->buf[st->bufbeg] & 0xFF) << 16;
444 ptr[0] = base64[(i >> 18) & 0x3F];
445 ptr[1] = base64[(i >> 12) & 0x3F];
446 if(++st->bufbeg != st->bufend) {
447 ptr[2] = base64[(i >> 6) & 0x3F];
456 #ifdef CURL_DOES_CONVERSIONS
457 /* This is now textual data, Convert character codes. */
458 if(part->easy && cursize) {
459 CURLcode result = Curl_convert_to_network(part->easy, buffer, cursize);
468 static curl_off_t encoder_base64_size(curl_mimepart *part)
470 curl_off_t size = part->datasize;
473 return size; /* Unknown size or no data. */
475 /* Compute base64 character count. */
476 size = 4 * (1 + (size - 1) / 3);
478 /* Effective character count must include CRLFs. */
479 return size + 2 * ((size - 1) / MAX_ENCODED_LINE_LENGTH);
483 /* Quoted-printable lookahead.
485 * Check if a CRLF or end of data is in input buffer at current position + n.
486 * Return -1 if more data needed, 1 if CRLF or end of data, else 0.
488 static int qp_lookahead_eol(mime_encoder_state *st, int ateof, size_t n)
491 if(n >= st->bufend && ateof)
493 if(n + 2 > st->bufend)
495 if(qp_class[st->buf[n] & 0xFF] == QP_CR &&
496 qp_class[st->buf[n + 1] & 0xFF] == QP_LF)
501 /* Quoted-printable encoder. */
502 static size_t encoder_qp_read(char *buffer, size_t size, bool ateof,
505 mime_encoder_state *st = &part->encstate;
514 /* On all platforms, input is supposed to be ASCII compatible: for this
515 reason, we use hexadecimal ASCII codes in this function rather than
516 character constants that can be interpreted as non-ascii on some
517 platforms. Preserve ASCII encoding on output too. */
518 while(st->bufbeg < st->bufend) {
521 i = st->buf[st->bufbeg];
523 buf[1] = aschex[(i >> 4) & 0xF];
524 buf[2] = aschex[i & 0xF];
526 switch(qp_class[st->buf[st->bufbeg] & 0xFF]) {
527 case QP_OK: /* Not a special character. */
529 case QP_SP: /* Space or tab. */
530 /* Spacing must be escaped if followed by CRLF. */
531 switch(qp_lookahead_eol(st, ateof, 1)) {
532 case -1: /* More input data needed. */
534 case 0: /* No encoding needed. */
536 default: /* CRLF after space or tab. */
537 buf[0] = '\x3D'; /* '=' */
542 case QP_CR: /* Carriage return. */
543 /* If followed by a line-feed, output the CRLF pair.
545 switch(qp_lookahead_eol(st, ateof, 0)) {
546 case -1: /* Need more data. */
548 case 1: /* CRLF found. */
549 buf[len++] = '\x0A'; /* Append '\n'. */
552 default: /* Not followed by LF: escape. */
553 buf[0] = '\x3D'; /* '=' */
558 default: /* Character must be escaped. */
559 buf[0] = '\x3D'; /* '=' */
564 /* Be sure the encoded character fits within maximum line length. */
565 if(buf[len - 1] != '\x0A') { /* '\n' */
566 softlinebreak = st->pos + len > MAX_ENCODED_LINE_LENGTH;
567 if(!softlinebreak && st->pos + len == MAX_ENCODED_LINE_LENGTH) {
568 /* We may use the current line only if end of data or followed by
570 switch(qp_lookahead_eol(st, ateof, consumed)) {
571 case -1: /* Need more data. */
574 case 0: /* Not followed by a CRLF. */
580 strcpy(buf, "\x3D\x0D\x0A"); /* "=\r\n" */
586 /* If the output buffer would overflow, do not store. */
590 /* Append to output buffer. */
591 memcpy(ptr, buf, len);
596 if(buf[len - 1] == '\x0A') /* '\n' */
598 st->bufbeg += consumed;
604 static curl_off_t encoder_qp_size(curl_mimepart *part)
606 /* Determining the size can only be done by reading the data: unless the
607 data size is 0, we return it as unknown (-1). */
608 return part->datasize? -1: 0;
612 /* In-memory data callbacks. */
613 /* Argument is a pointer to the mime part. */
614 static size_t mime_mem_read(char *buffer, size_t size, size_t nitems,
617 curl_mimepart *part = (curl_mimepart *) instream;
618 size_t sz = (size_t) part->datasize - part->state.offset;
619 (void) size; /* Always 1.*/
625 memcpy(buffer, (char *) &part->data[part->state.offset], sz);
627 part->state.offset += sz;
631 static int mime_mem_seek(void *instream, curl_off_t offset, int whence)
633 curl_mimepart *part = (curl_mimepart *) instream;
637 offset += part->state.offset;
640 offset += part->datasize;
644 if(offset < 0 || offset > part->datasize)
645 return CURL_SEEKFUNC_FAIL;
647 part->state.offset = (size_t) offset;
648 return CURL_SEEKFUNC_OK;
651 static void mime_mem_free(void *ptr)
653 Curl_safefree(((curl_mimepart *) ptr)->data);
657 /* Named file callbacks. */
658 /* Argument is a pointer to the mime part. */
659 static int mime_open_file(curl_mimepart * part)
661 /* Open a MIMEKIND_FILE part. */
665 part->fp = fopen_read(part->data, "rb");
666 return part->fp? 0: -1;
669 static size_t mime_file_read(char *buffer, size_t size, size_t nitems,
672 curl_mimepart *part = (curl_mimepart *) instream;
674 if(mime_open_file(part))
677 return fread(buffer, size, nitems, part->fp);
680 static int mime_file_seek(void *instream, curl_off_t offset, int whence)
682 curl_mimepart *part = (curl_mimepart *) instream;
684 if(whence == SEEK_SET && !offset && !part->fp)
685 return CURL_SEEKFUNC_OK; /* Not open: implicitly already at BOF. */
687 if(mime_open_file(part))
688 return CURL_SEEKFUNC_FAIL;
690 return fseek(part->fp, (long) offset, whence)?
691 CURL_SEEKFUNC_CANTSEEK: CURL_SEEKFUNC_OK;
694 static void mime_file_free(void *ptr)
696 curl_mimepart *part = (curl_mimepart *) ptr;
702 Curl_safefree(part->data);
707 /* Subparts callbacks. */
708 /* Argument is a pointer to the mime structure. */
710 /* Readback a byte string segment. */
711 static size_t readback_bytes(mime_state *state,
712 char *buffer, size_t bufsize,
713 const char *bytes, size_t numbytes,
718 if(numbytes > state->offset) {
719 sz = numbytes - state->offset;
720 bytes += state->offset;
723 size_t tsz = strlen(trail);
725 sz = state->offset - numbytes;
735 memcpy(buffer, bytes, sz);
740 /* Read a non-encoded part content. */
741 static size_t read_part_content(curl_mimepart *part,
742 char *buffer, size_t bufsize)
747 sz = part->readfunc(buffer, 1, bufsize, part->arg);
751 /* Read and encode part content. */
752 static size_t read_encoded_part_content(curl_mimepart *part,
753 char *buffer, size_t bufsize)
755 mime_encoder_state *st = &part->encstate;
761 if(st->bufbeg < st->bufend || ateof) {
762 /* Encode buffered data. */
763 sz = part->encoder->encodefunc(buffer, bufsize, ateof, part);
769 case CURL_READFUNC_ABORT:
770 case CURL_READFUNC_PAUSE:
772 return cursize? cursize: sz;
781 /* We need more data in input buffer. */
783 size_t len = st->bufend - st->bufbeg;
786 memmove(st->buf, st->buf + st->bufbeg, len);
790 if(st->bufend >= sizeof st->buf)
791 return cursize? cursize: READ_ERROR; /* Buffer full. */
792 sz = read_part_content(part, st->buf + st->bufend,
793 sizeof st->buf - st->bufend);
798 case CURL_READFUNC_ABORT:
799 case CURL_READFUNC_PAUSE:
801 return cursize? cursize: sz;
811 /* Readback a mime part. */
812 static size_t readback_part(curl_mimepart *part,
813 char *buffer, size_t bufsize)
817 struct curl_slist *hdr;
818 #ifdef CURL_DOES_CONVERSIONS
819 char *convbuf = buffer;
822 /* Readback from part. */
826 hdr = (struct curl_slist *) part->state.ptr;
827 switch(part->state.state) {
828 case MIMESTATE_BEGIN:
829 mimesetstate(&part->state, part->flags & MIME_BODY_ONLY? MIMESTATE_BODY:
830 MIMESTATE_CURLHEADERS, part->curlheaders);
832 case MIMESTATE_USERHEADERS:
834 mimesetstate(&part->state, MIMESTATE_EOH, NULL);
837 if(match_header(hdr, "Content-Type", 12)) {
838 mimesetstate(&part->state, MIMESTATE_USERHEADERS, hdr->next);
842 case MIMESTATE_CURLHEADERS:
844 mimesetstate(&part->state, MIMESTATE_USERHEADERS, part->userheaders);
846 sz = readback_bytes(&part->state, buffer, bufsize,
847 hdr->data, strlen(hdr->data), "\r\n");
849 mimesetstate(&part->state, part->state.state, hdr->next);
853 sz = readback_bytes(&part->state, buffer, bufsize, "\r\n", 2, "");
855 mimesetstate(&part->state, MIMESTATE_BODY, NULL);
858 #ifdef CURL_DOES_CONVERSIONS
859 if(part->easy && convbuf < buffer) {
860 CURLcode result = Curl_convert_to_network(part->easy, convbuf,
867 cleanup_encoder_state(&part->encstate);
868 mimesetstate(&part->state, MIMESTATE_CONTENT, NULL);
870 case MIMESTATE_CONTENT:
872 sz = read_encoded_part_content(part, buffer, bufsize);
874 sz = read_part_content(part, buffer, bufsize);
877 mimesetstate(&part->state, MIMESTATE_END, NULL);
878 /* Try sparing open file descriptors. */
879 if(part->kind == MIMEKIND_FILE && part->fp) {
884 case CURL_READFUNC_ABORT:
885 case CURL_READFUNC_PAUSE:
887 return cursize? cursize: sz;
893 break; /* Other values not in part state. */
896 /* Bump buffer and counters according to read size. */
902 #ifdef CURL_DOES_CONVERSIONS
903 if(part->easy && convbuf < buffer &&
904 part->state.state < MIMESTATE_BODY) {
905 CURLcode result = Curl_convert_to_network(part->easy, convbuf,
915 /* Readback from mime. */
916 static size_t mime_subparts_read(char *buffer, size_t size, size_t nitems,
919 curl_mime *mime = (curl_mime *) instream;
923 #ifdef CURL_DOES_CONVERSIONS
924 char *convbuf = buffer;
927 (void) size; /* Always 1. */
931 part = mime->state.ptr;
932 switch(mime->state.state) {
933 case MIMESTATE_BEGIN:
935 #ifdef CURL_DOES_CONVERSIONS
938 mimesetstate(&mime->state, MIMESTATE_BOUNDARY1, mime->firstpart);
939 /* The first boundary always follows the header termination empty line,
940 so is always preceded by a CRLK. We can then spare 2 characters
941 by skipping the leading CRLF in boundary. */
942 mime->state.offset += 2;
944 case MIMESTATE_BOUNDARY1:
945 sz = readback_bytes(&mime->state, buffer, nitems, "\r\n--", 4, "");
947 mimesetstate(&mime->state, MIMESTATE_BOUNDARY2, part);
949 case MIMESTATE_BOUNDARY2:
950 sz = readback_bytes(&mime->state, buffer, nitems, mime->boundary,
951 strlen(mime->boundary), part? "\r\n": "--\r\n");
953 #ifdef CURL_DOES_CONVERSIONS
954 if(mime->easy && convbuf < buffer) {
955 CURLcode result = Curl_convert_to_network(mime->easy, convbuf,
962 mimesetstate(&mime->state, MIMESTATE_CONTENT, part);
965 case MIMESTATE_CONTENT:
967 mimesetstate(&mime->state, MIMESTATE_END, NULL);
970 sz = readback_part(part, buffer, nitems);
972 case CURL_READFUNC_ABORT:
973 case CURL_READFUNC_PAUSE:
975 return cursize? cursize: sz;
977 #ifdef CURL_DOES_CONVERSIONS
980 mimesetstate(&mime->state, MIMESTATE_BOUNDARY1, part->nextpart);
987 break; /* other values not used in mime state. */
990 /* Bump buffer and counters according to read size. */
996 #ifdef CURL_DOES_CONVERSIONS
997 if(mime->easy && convbuf < buffer &&
998 mime->state.state <= MIMESTATE_CONTENT) {
999 CURLcode result = Curl_convert_to_network(mime->easy, convbuf,
1009 static int mime_part_rewind(curl_mimepart *part)
1011 int res = CURL_SEEKFUNC_OK;
1012 enum mimestate targetstate = MIMESTATE_BEGIN;
1014 if(part->flags & MIME_BODY_ONLY)
1015 targetstate = MIMESTATE_BODY;
1016 cleanup_encoder_state(&part->encstate);
1017 if(part->state.state > targetstate) {
1018 res = CURL_SEEKFUNC_CANTSEEK;
1019 if(part->seekfunc) {
1020 res = part->seekfunc(part->arg, (curl_off_t) 0, SEEK_SET);
1022 case CURL_SEEKFUNC_OK:
1023 case CURL_SEEKFUNC_FAIL:
1024 case CURL_SEEKFUNC_CANTSEEK:
1026 case -1: /* For fseek() error. */
1027 res = CURL_SEEKFUNC_CANTSEEK;
1030 res = CURL_SEEKFUNC_FAIL;
1036 if(res == CURL_SEEKFUNC_OK)
1037 mimesetstate(&part->state, targetstate, NULL);
1042 static int mime_subparts_seek(void *instream, curl_off_t offset, int whence)
1044 curl_mime *mime = (curl_mime *) instream;
1045 curl_mimepart *part;
1046 int result = CURL_SEEKFUNC_OK;
1049 if(whence != SEEK_SET || offset)
1050 return CURL_SEEKFUNC_CANTSEEK; /* Only support full rewind. */
1052 if(mime->state.state == MIMESTATE_BEGIN)
1053 return CURL_SEEKFUNC_OK; /* Already rewound. */
1055 for(part = mime->firstpart; part; part = part->nextpart) {
1056 res = mime_part_rewind(part);
1057 if(res != CURL_SEEKFUNC_OK)
1061 if(result == CURL_SEEKFUNC_OK)
1062 mimesetstate(&mime->state, MIMESTATE_BEGIN, NULL);
1067 /* Release part content. */
1068 static void cleanup_part_content(curl_mimepart *part)
1071 part->freefunc(part->arg);
1073 part->readfunc = NULL;
1074 part->seekfunc = NULL;
1075 part->freefunc = NULL;
1076 part->arg = (void *) part; /* Defaults to part itself. */
1079 part->datasize = (curl_off_t) 0; /* No size yet. */
1080 cleanup_encoder_state(&part->encstate);
1081 part->kind = MIMEKIND_NONE;
1084 static void mime_subparts_free(void *ptr)
1086 curl_mime *mime = (curl_mime *) ptr;
1088 if(mime && mime->parent) {
1089 mime->parent->freefunc = NULL; /* Be sure we won't be called again. */
1090 cleanup_part_content(mime->parent); /* Avoid dangling pointer in part. */
1092 curl_mime_free(mime);
1095 /* Do not free subparts: unbind them. This is used for the top level only. */
1096 static void mime_subparts_unbind(void *ptr)
1098 curl_mime *mime = (curl_mime *) ptr;
1100 if(mime && mime->parent) {
1101 mime->parent->freefunc = NULL; /* Be sure we won't be called again. */
1102 cleanup_part_content(mime->parent); /* Avoid dangling pointer in part. */
1103 mime->parent = NULL;
1108 void Curl_mime_cleanpart(curl_mimepart *part)
1110 cleanup_part_content(part);
1111 curl_slist_free_all(part->curlheaders);
1112 if(part->flags & MIME_USERHEADERS_OWNER)
1113 curl_slist_free_all(part->userheaders);
1114 Curl_safefree(part->mimetype);
1115 Curl_safefree(part->name);
1116 Curl_safefree(part->filename);
1117 Curl_mime_initpart(part, part->easy);
1120 /* Recursively delete a mime handle and its parts. */
1121 void curl_mime_free(curl_mime *mime)
1123 curl_mimepart *part;
1126 mime_subparts_unbind(mime); /* Be sure it's not referenced anymore. */
1127 while(mime->firstpart) {
1128 part = mime->firstpart;
1129 mime->firstpart = part->nextpart;
1130 Curl_mime_cleanpart(part);
1134 free(mime->boundary);
1139 CURLcode Curl_mime_duppart(curl_mimepart *dst, const curl_mimepart *src)
1143 const curl_mimepart *s;
1144 CURLcode res = CURLE_OK;
1146 /* Duplicate content. */
1151 res = curl_mime_data(dst, src->data, (size_t) src->datasize);
1154 res = curl_mime_filedata(dst, src->data);
1155 /* Do not abort duplication if file is not readable. */
1156 if(res == CURLE_READ_ERROR)
1159 case MIMEKIND_CALLBACK:
1160 res = curl_mime_data_cb(dst, src->datasize, src->readfunc,
1161 src->seekfunc, src->freefunc, src->arg);
1163 case MIMEKIND_MULTIPART:
1164 /* No one knows about the cloned subparts, thus always attach ownership
1166 mime = curl_mime_init(dst->easy);
1167 res = mime? curl_mime_subparts(dst, mime): CURLE_OUT_OF_MEMORY;
1169 /* Duplicate subparts. */
1170 for(s = ((curl_mime *) src->arg)->firstpart; !res && s; s = s->nextpart) {
1171 d = curl_mime_addpart(mime);
1172 res = d? Curl_mime_duppart(d, s): CURLE_OUT_OF_MEMORY;
1175 default: /* Invalid kind: should not occur. */
1176 res = CURLE_BAD_FUNCTION_ARGUMENT; /* Internal error? */
1180 /* Duplicate headers. */
1181 if(!res && src->userheaders) {
1182 struct curl_slist *hdrs = Curl_slist_duplicate(src->userheaders);
1185 res = CURLE_OUT_OF_MEMORY;
1187 /* No one but this procedure knows about the new header list,
1188 so always take ownership. */
1189 res = curl_mime_headers(dst, hdrs, TRUE);
1191 curl_slist_free_all(hdrs);
1195 /* Duplicate other fields. */
1196 dst->encoder = src->encoder;
1198 res = curl_mime_type(dst, src->mimetype);
1200 res = curl_mime_name(dst, src->name);
1202 res = curl_mime_filename(dst, src->filename);
1204 /* If an error occurred, rollback. */
1206 Curl_mime_cleanpart(dst);
1212 * Mime build functions.
1215 /* Create a mime handle. */
1216 curl_mime *curl_mime_init(struct Curl_easy *easy)
1220 mime = (curl_mime *) malloc(sizeof *mime);
1224 mime->parent = NULL;
1225 mime->firstpart = NULL;
1226 mime->lastpart = NULL;
1228 /* Get a part boundary. */
1229 mime->boundary = malloc(24 + MIME_RAND_BOUNDARY_CHARS + 1);
1230 if(!mime->boundary) {
1235 memset(mime->boundary, '-', 24);
1236 Curl_rand_hex(easy, (unsigned char *) mime->boundary + 24,
1237 MIME_RAND_BOUNDARY_CHARS + 1);
1238 mimesetstate(&mime->state, MIMESTATE_BEGIN, NULL);
1244 /* Initialize a mime part. */
1245 void Curl_mime_initpart(curl_mimepart *part, struct Curl_easy *easy)
1247 memset((char *) part, 0, sizeof *part);
1249 mimesetstate(&part->state, MIMESTATE_BEGIN, NULL);
1252 /* Create a mime part and append it to a mime handle's part list. */
1253 curl_mimepart *curl_mime_addpart(curl_mime *mime)
1255 curl_mimepart *part;
1260 part = (curl_mimepart *) malloc(sizeof *part);
1263 Curl_mime_initpart(part, mime->easy);
1264 part->parent = mime;
1267 mime->lastpart->nextpart = part;
1269 mime->firstpart = part;
1271 mime->lastpart = part;
1277 /* Set mime part name. */
1278 CURLcode curl_mime_name(curl_mimepart *part, const char *name)
1281 return CURLE_BAD_FUNCTION_ARGUMENT;
1283 Curl_safefree(part->name);
1287 part->name = strdup(name);
1289 return CURLE_OUT_OF_MEMORY;
1295 /* Set mime part remote file name. */
1296 CURLcode curl_mime_filename(curl_mimepart *part, const char *filename)
1299 return CURLE_BAD_FUNCTION_ARGUMENT;
1301 Curl_safefree(part->filename);
1302 part->filename = NULL;
1305 part->filename = strdup(filename);
1307 return CURLE_OUT_OF_MEMORY;
1313 /* Set mime part content from memory data. */
1314 CURLcode curl_mime_data(curl_mimepart *part,
1315 const char *data, size_t datasize)
1318 return CURLE_BAD_FUNCTION_ARGUMENT;
1320 cleanup_part_content(part);
1323 if(datasize == CURL_ZERO_TERMINATED)
1324 datasize = strlen(data);
1326 part->data = malloc(datasize + 1);
1328 return CURLE_OUT_OF_MEMORY;
1330 part->datasize = datasize;
1333 memcpy(part->data, data, datasize);
1334 part->data[datasize] = '\0'; /* Set a nul terminator as sentinel. */
1336 part->readfunc = mime_mem_read;
1337 part->seekfunc = mime_mem_seek;
1338 part->freefunc = mime_mem_free;
1339 part->kind = MIMEKIND_DATA;
1345 /* Set mime part content from named local file. */
1346 CURLcode curl_mime_filedata(curl_mimepart *part, const char *filename)
1348 CURLcode result = CURLE_OK;
1352 return CURLE_BAD_FUNCTION_ARGUMENT;
1354 cleanup_part_content(part);
1359 if(stat(filename, &sbuf) || access(filename, R_OK))
1360 result = CURLE_READ_ERROR;
1362 part->data = strdup(filename);
1364 result = CURLE_OUT_OF_MEMORY;
1366 part->datasize = -1;
1367 if(!result && S_ISREG(sbuf.st_mode)) {
1368 part->datasize = filesize(filename, sbuf);
1369 part->seekfunc = mime_file_seek;
1372 part->readfunc = mime_file_read;
1373 part->freefunc = mime_file_free;
1374 part->kind = MIMEKIND_FILE;
1376 /* As a side effect, set the filename to the current file's base name.
1377 It is possible to withdraw this by explicitly calling
1378 curl_mime_filename() with a NULL filename argument after the current
1380 base = strippath(filename);
1382 result = CURLE_OUT_OF_MEMORY;
1384 CURLcode res = curl_mime_filename(part, base);
1394 /* Set mime part type. */
1395 CURLcode curl_mime_type(curl_mimepart *part, const char *mimetype)
1398 return CURLE_BAD_FUNCTION_ARGUMENT;
1400 Curl_safefree(part->mimetype);
1401 part->mimetype = NULL;
1404 part->mimetype = strdup(mimetype);
1406 return CURLE_OUT_OF_MEMORY;
1412 /* Set mime data transfer encoder. */
1413 CURLcode curl_mime_encoder(curl_mimepart *part, const char *encoding)
1415 CURLcode result = CURLE_BAD_FUNCTION_ARGUMENT;
1416 const mime_encoder *mep;
1421 part->encoder = NULL;
1424 return CURLE_OK; /* Removing current encoder. */
1426 for(mep = encoders; mep->name; mep++)
1427 if(strcasecompare(encoding, mep->name)) {
1428 part->encoder = mep;
1435 /* Set mime part headers. */
1436 CURLcode curl_mime_headers(curl_mimepart *part,
1437 struct curl_slist *headers, int take_ownership)
1440 return CURLE_BAD_FUNCTION_ARGUMENT;
1442 if(part->flags & MIME_USERHEADERS_OWNER) {
1443 if(part->userheaders != headers) /* Allow setting twice the same list. */
1444 curl_slist_free_all(part->userheaders);
1445 part->flags &= ~MIME_USERHEADERS_OWNER;
1447 part->userheaders = headers;
1448 if(headers && take_ownership)
1449 part->flags |= MIME_USERHEADERS_OWNER;
1453 /* Set mime part content from callback. */
1454 CURLcode curl_mime_data_cb(curl_mimepart *part, curl_off_t datasize,
1455 curl_read_callback readfunc,
1456 curl_seek_callback seekfunc,
1457 curl_free_callback freefunc, void *arg)
1460 return CURLE_BAD_FUNCTION_ARGUMENT;
1462 cleanup_part_content(part);
1465 part->readfunc = readfunc;
1466 part->seekfunc = seekfunc;
1467 part->freefunc = freefunc;
1469 part->datasize = datasize;
1470 part->kind = MIMEKIND_CALLBACK;
1476 /* Set mime part content from subparts. */
1477 CURLcode Curl_mime_set_subparts(curl_mimepart *part,
1478 curl_mime *subparts, int take_ownership)
1483 return CURLE_BAD_FUNCTION_ARGUMENT;
1485 /* Accept setting twice the same subparts. */
1486 if(part->kind == MIMEKIND_MULTIPART && part->arg == subparts)
1489 cleanup_part_content(part);
1492 /* Must belong to the same data handle. */
1493 if(part->easy && subparts->easy && part->easy != subparts->easy)
1494 return CURLE_BAD_FUNCTION_ARGUMENT;
1496 /* Should not have been attached already. */
1497 if(subparts->parent)
1498 return CURLE_BAD_FUNCTION_ARGUMENT;
1500 /* Should not be the part's root. */
1501 root = part->parent;
1503 while(root->parent && root->parent->parent)
1504 root = root->parent->parent;
1505 if(subparts == root) {
1507 failf(part->easy, "Can't add itself as a subpart!");
1508 return CURLE_BAD_FUNCTION_ARGUMENT;
1512 subparts->parent = part;
1513 part->readfunc = mime_subparts_read;
1514 part->seekfunc = mime_subparts_seek;
1515 part->freefunc = take_ownership? mime_subparts_free: mime_subparts_unbind;
1516 part->arg = subparts;
1517 part->datasize = -1;
1518 part->kind = MIMEKIND_MULTIPART;
1524 CURLcode curl_mime_subparts(curl_mimepart *part, curl_mime *subparts)
1526 return Curl_mime_set_subparts(part, subparts, TRUE);
1530 /* Readback from top mime. */
1531 /* Argument is the dummy top part. */
1532 size_t Curl_mime_read(char *buffer, size_t size, size_t nitems, void *instream)
1534 curl_mimepart *part = (curl_mimepart *) instream;
1536 (void) size; /* Always 1. */
1537 return readback_part(part, buffer, nitems);
1540 /* Rewind mime stream. */
1541 CURLcode Curl_mime_rewind(curl_mimepart *part)
1543 return mime_part_rewind(part) == CURL_SEEKFUNC_OK?
1544 CURLE_OK: CURLE_SEND_FAIL_REWIND;
1547 /* Compute header list size. */
1548 static size_t slist_size(struct curl_slist *s,
1549 size_t overhead, const char *skip)
1552 size_t skiplen = skip? strlen(skip): 0;
1554 for(; s; s = s->next)
1555 if(!skip || !match_header(s, skip, skiplen))
1556 size += strlen(s->data) + overhead;
1560 /* Get/compute multipart size. */
1561 static curl_off_t multipart_size(curl_mime *mime)
1565 size_t boundarysize;
1566 curl_mimepart *part;
1569 return 0; /* Not present -> empty. */
1571 boundarysize = 4 + strlen(mime->boundary) + 2;
1572 size = boundarysize; /* Final boundary - CRLF after headers. */
1574 for(part = mime->firstpart; part; part = part->nextpart) {
1575 sz = Curl_mime_size(part);
1581 size += boundarysize + sz;
1587 /* Get/compute mime size. */
1588 curl_off_t Curl_mime_size(curl_mimepart *part)
1592 if(part->kind == MIMEKIND_MULTIPART)
1593 part->datasize = multipart_size(part->arg);
1595 size = part->datasize;
1598 size = part->encoder->sizefunc(part);
1600 if(size >= 0 && !(part->flags & MIME_BODY_ONLY)) {
1601 /* Compute total part size. */
1602 size += slist_size(part->curlheaders, 2, NULL);
1603 size += slist_size(part->userheaders, 2, "Content-Type");
1604 size += 2; /* CRLF after headers. */
1611 CURLcode Curl_mime_add_header(struct curl_slist **slp, const char *fmt, ...)
1613 struct curl_slist *hdr = NULL;
1618 s = curl_mvaprintf(fmt, ap);
1622 hdr = Curl_slist_append_nodup(*slp, s);
1629 return hdr? CURLE_OK: CURLE_OUT_OF_MEMORY;
1632 /* Add a content type header. */
1633 static CURLcode add_content_type(struct curl_slist **slp,
1634 const char *type, const char *boundary)
1636 return Curl_mime_add_header(slp, "Content-Type: %s%s%s", type,
1637 boundary? "; boundary=": "",
1638 boundary? boundary: "");
1641 const char *Curl_mime_contenttype(const char *filename)
1646 * If no content type was specified, we scan through a few well-known
1647 * extensions and pick the first we match!
1649 struct ContentType {
1650 const char *extension;
1653 static const struct ContentType ctts[] = {
1654 {".gif", "image/gif"},
1655 {".jpg", "image/jpeg"},
1656 {".jpeg", "image/jpeg"},
1657 {".png", "image/png"},
1658 {".svg", "image/svg+xml"},
1659 {".txt", "text/plain"},
1660 {".htm", "text/html"},
1661 {".html", "text/html"},
1662 {".pdf", "application/pdf"},
1663 {".xml", "application/xml"}
1667 size_t len1 = strlen(filename);
1668 const char *nameend = filename + len1;
1670 for(i = 0; i < sizeof ctts / sizeof ctts[0]; i++) {
1671 size_t len2 = strlen(ctts[i].extension);
1673 if(len1 >= len2 && strcasecompare(nameend - len2, ctts[i].extension))
1674 return ctts[i].type;
1680 CURLcode Curl_mime_prepare_headers(curl_mimepart *part,
1681 const char *contenttype,
1682 const char *disposition,
1683 enum mimestrategy strategy)
1685 curl_mime *mime = NULL;
1686 const char *boundary = NULL;
1688 const char *cte = NULL;
1689 CURLcode ret = CURLE_OK;
1691 /* Get rid of previously prepared headers. */
1692 curl_slist_free_all(part->curlheaders);
1693 part->curlheaders = NULL;
1695 /* Be sure we won't access old headers later. */
1696 if(part->state.state == MIMESTATE_CURLHEADERS)
1697 mimesetstate(&part->state, MIMESTATE_CURLHEADERS, NULL);
1699 /* Check if content type is specified. */
1700 customct = part->mimetype;
1702 customct = search_header(part->userheaders, "Content-Type");
1704 contenttype = customct;
1706 /* If content type is not specified, try to determine it. */
1708 switch(part->kind) {
1709 case MIMEKIND_MULTIPART:
1710 contenttype = MULTIPART_CONTENTTYPE_DEFAULT;
1713 contenttype = Curl_mime_contenttype(part->filename);
1715 contenttype = Curl_mime_contenttype(part->data);
1716 if(!contenttype && part->filename)
1717 contenttype = FILE_CONTENTTYPE_DEFAULT;
1720 contenttype = Curl_mime_contenttype(part->filename);
1725 if(part->kind == MIMEKIND_MULTIPART) {
1726 mime = (curl_mime *) part->arg;
1728 boundary = mime->boundary;
1730 else if(contenttype && !customct &&
1731 strcasecompare(contenttype, "text/plain"))
1732 if(strategy == MIMESTRATEGY_MAIL || !part->filename)
1735 /* Issue content-disposition header only if not already set by caller. */
1736 if(!search_header(part->userheaders, "Content-Disposition")) {
1738 if(part->filename || part->name ||
1739 (contenttype && !strncasecompare(contenttype, "multipart/", 10)))
1740 disposition = DISPOSITION_DEFAULT;
1741 if(disposition && curl_strequal(disposition, "attachment") &&
1742 !part->name && !part->filename)
1746 char *filename = NULL;
1749 name = escape_string(part->name);
1751 ret = CURLE_OUT_OF_MEMORY;
1753 if(!ret && part->filename) {
1754 filename = escape_string(part->filename);
1756 ret = CURLE_OUT_OF_MEMORY;
1759 ret = Curl_mime_add_header(&part->curlheaders,
1760 "Content-Disposition: %s%s%s%s%s%s%s",
1762 name? "; name=\"": "",
1765 filename? "; filename=\"": "",
1766 filename? filename: "",
1767 filename? "\"": "");
1768 Curl_safefree(name);
1769 Curl_safefree(filename);
1775 /* Issue Content-Type header. */
1777 ret = add_content_type(&part->curlheaders, contenttype, boundary);
1782 /* Content-Transfer-Encoding header. */
1783 if(!search_header(part->userheaders, "Content-Transfer-Encoding")) {
1785 cte = part->encoder->name;
1786 else if(contenttype && strategy == MIMESTRATEGY_MAIL &&
1787 part->kind != MIMEKIND_MULTIPART)
1790 ret = Curl_mime_add_header(&part->curlheaders,
1791 "Content-Transfer-Encoding: %s", cte);
1797 /* If we were reading curl-generated headers, restart with new ones (this
1798 should not occur). */
1799 if(part->state.state == MIMESTATE_CURLHEADERS)
1800 mimesetstate(&part->state, MIMESTATE_CURLHEADERS, part->curlheaders);
1802 /* Process subparts. */
1803 if(part->kind == MIMEKIND_MULTIPART && mime) {
1804 curl_mimepart *subpart;
1807 if(strcasecompare(contenttype, "multipart/form-data"))
1808 disposition = "form-data";
1809 for(subpart = mime->firstpart; subpart; subpart = subpart->nextpart) {
1810 ret = Curl_mime_prepare_headers(subpart, NULL, disposition, strategy);
1818 #else /* !CURL_DISABLE_HTTP || !CURL_DISABLE_SMTP || !CURL_DISABLE_IMAP */
1820 /* Mime not compiled in: define stubs for externally-referenced functions. */
1821 curl_mime *curl_mime_init(CURL *easy)
1827 void curl_mime_free(curl_mime *mime)
1832 curl_mimepart *curl_mime_addpart(curl_mime *mime)
1838 CURLcode curl_mime_name(curl_mimepart *part, const char *name)
1842 return CURLE_NOT_BUILT_IN;
1845 CURLcode curl_mime_filename(curl_mimepart *part, const char *filename)
1849 return CURLE_NOT_BUILT_IN;
1852 CURLcode curl_mime_type(curl_mimepart *part, const char *mimetype)
1856 return CURLE_NOT_BUILT_IN;
1859 CURLcode curl_mime_encoder(curl_mimepart *part, const char *encoding)
1863 return CURLE_NOT_BUILT_IN;
1866 CURLcode curl_mime_data(curl_mimepart *part,
1867 const char *data, size_t datasize)
1872 return CURLE_NOT_BUILT_IN;
1875 CURLcode curl_mime_filedata(curl_mimepart *part, const char *filename)
1879 return CURLE_NOT_BUILT_IN;
1882 CURLcode curl_mime_data_cb(curl_mimepart *part,
1883 curl_off_t datasize,
1884 curl_read_callback readfunc,
1885 curl_seek_callback seekfunc,
1886 curl_free_callback freefunc,
1895 return CURLE_NOT_BUILT_IN;
1898 CURLcode curl_mime_subparts(curl_mimepart *part, curl_mime *subparts)
1902 return CURLE_NOT_BUILT_IN;
1905 CURLcode curl_mime_headers(curl_mimepart *part,
1906 struct curl_slist *headers, int take_ownership)
1910 (void) take_ownership;
1911 return CURLE_NOT_BUILT_IN;
1914 void Curl_mime_initpart(curl_mimepart *part, struct Curl_easy *easy)
1920 void Curl_mime_cleanpart(curl_mimepart *part)
1925 CURLcode Curl_mime_duppart(curl_mimepart *dst, const curl_mimepart *src)
1929 return CURLE_OK; /* Nothing to duplicate: always succeed. */
1932 CURLcode Curl_mime_set_subparts(curl_mimepart *part,
1933 curl_mime *subparts, int take_ownership)
1937 (void) take_ownership;
1938 return CURLE_NOT_BUILT_IN;
1941 CURLcode Curl_mime_prepare_headers(curl_mimepart *part,
1942 const char *contenttype,
1943 const char *disposition,
1944 enum mimestrategy strategy)
1950 return CURLE_NOT_BUILT_IN;
1953 curl_off_t Curl_mime_size(curl_mimepart *part)
1956 return (curl_off_t) -1;
1959 size_t Curl_mime_read(char *buffer, size_t size, size_t nitems, void *instream)
1968 CURLcode Curl_mime_rewind(curl_mimepart *part)
1971 return CURLE_NOT_BUILT_IN;
1975 CURLcode Curl_mime_add_header(struct curl_slist **slp, const char *fmt, ...)
1979 return CURLE_NOT_BUILT_IN;
1982 #endif /* !CURL_DISABLE_HTTP || !CURL_DISABLE_SMTP || !CURL_DISABLE_IMAP */