• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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 
25 #include "curl_setup.h"
26 
27 #include <curl/curl.h>
28 
29 #include "mime.h"
30 #include "warnless.h"
31 #include "urldata.h"
32 #include "sendf.h"
33 #include "strdup.h"
34 
35 #if !defined(CURL_DISABLE_MIME) && (!defined(CURL_DISABLE_HTTP) ||      \
36                                     !defined(CURL_DISABLE_SMTP) ||      \
37                                     !defined(CURL_DISABLE_IMAP))
38 
39 #if defined(HAVE_LIBGEN_H) && defined(HAVE_BASENAME)
40 #include <libgen.h>
41 #endif
42 
43 #include "rand.h"
44 #include "slist.h"
45 #include "strcase.h"
46 #include "dynbuf.h"
47 /* The last 3 #include files should be in this order */
48 #include "curl_printf.h"
49 #include "curl_memory.h"
50 #include "memdebug.h"
51 
52 #ifdef _WIN32
53 # ifndef R_OK
54 #  define R_OK 4
55 # endif
56 #endif
57 
58 
59 #define READ_ERROR                      ((size_t) -1)
60 #define STOP_FILLING                    ((size_t) -2)
61 
62 static size_t mime_subparts_read(char *buffer, size_t size, size_t nitems,
63                                  void *instream, bool *hasread);
64 
65 /* Encoders. */
66 static size_t encoder_nop_read(char *buffer, size_t size, bool ateof,
67                                 curl_mimepart *part);
68 static curl_off_t encoder_nop_size(curl_mimepart *part);
69 static size_t encoder_7bit_read(char *buffer, size_t size, bool ateof,
70                                 curl_mimepart *part);
71 static size_t encoder_base64_read(char *buffer, size_t size, bool ateof,
72                                 curl_mimepart *part);
73 static curl_off_t encoder_base64_size(curl_mimepart *part);
74 static size_t encoder_qp_read(char *buffer, size_t size, bool ateof,
75                               curl_mimepart *part);
76 static curl_off_t encoder_qp_size(curl_mimepart *part);
77 
78 static const struct mime_encoder encoders[] = {
79   {"binary", encoder_nop_read, encoder_nop_size},
80   {"8bit", encoder_nop_read, encoder_nop_size},
81   {"7bit", encoder_7bit_read, encoder_nop_size},
82   {"base64", encoder_base64_read, encoder_base64_size},
83   {"quoted-printable", encoder_qp_read, encoder_qp_size},
84   {ZERO_NULL, ZERO_NULL, ZERO_NULL}
85 };
86 
87 /* Base64 encoding table */
88 static const char base64enc[] =
89   "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/";
90 
91 /* Quoted-printable character class table.
92  *
93  * We cannot rely on ctype functions since quoted-printable input data
94  * is assumed to be ascii-compatible, even on non-ascii platforms. */
95 #define QP_OK           1       /* Can be represented by itself. */
96 #define QP_SP           2       /* Space or tab. */
97 #define QP_CR           3       /* Carriage return. */
98 #define QP_LF           4       /* Line-feed. */
99 static const unsigned char qp_class[] = {
100  0,     0,     0,     0,     0,     0,     0,     0,            /* 00 - 07 */
101  0,     QP_SP, QP_LF, 0,     0,     QP_CR, 0,     0,            /* 08 - 0F */
102  0,     0,     0,     0,     0,     0,     0,     0,            /* 10 - 17 */
103  0,     0,     0,     0,     0,     0,     0,     0,            /* 18 - 1F */
104  QP_SP, QP_OK, QP_OK, QP_OK, QP_OK, QP_OK, QP_OK, QP_OK,        /* 20 - 27 */
105  QP_OK, QP_OK, QP_OK, QP_OK, QP_OK, QP_OK, QP_OK, QP_OK,        /* 28 - 2F */
106  QP_OK, QP_OK, QP_OK, QP_OK, QP_OK, QP_OK, QP_OK, QP_OK,        /* 30 - 37 */
107  QP_OK, QP_OK, QP_OK, QP_OK, QP_OK, 0    , QP_OK, QP_OK,        /* 38 - 3F */
108  QP_OK, QP_OK, QP_OK, QP_OK, QP_OK, QP_OK, QP_OK, QP_OK,        /* 40 - 47 */
109  QP_OK, QP_OK, QP_OK, QP_OK, QP_OK, QP_OK, QP_OK, QP_OK,        /* 48 - 4F */
110  QP_OK, QP_OK, QP_OK, QP_OK, QP_OK, QP_OK, QP_OK, QP_OK,        /* 50 - 57 */
111  QP_OK, QP_OK, QP_OK, QP_OK, QP_OK, QP_OK, QP_OK, QP_OK,        /* 58 - 5F */
112  QP_OK, QP_OK, QP_OK, QP_OK, QP_OK, QP_OK, QP_OK, QP_OK,        /* 60 - 67 */
113  QP_OK, QP_OK, QP_OK, QP_OK, QP_OK, QP_OK, QP_OK, QP_OK,        /* 68 - 6F */
114  QP_OK, QP_OK, QP_OK, QP_OK, QP_OK, QP_OK, QP_OK, QP_OK,        /* 70 - 77 */
115  QP_OK, QP_OK, QP_OK, QP_OK, QP_OK, QP_OK, QP_OK, 0,            /* 78 - 7F */
116  0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,                /* 80 - 8F */
117  0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,                /* 90 - 9F */
118  0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,                /* A0 - AF */
119  0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,                /* B0 - BF */
120  0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,                /* C0 - CF */
121  0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,                /* D0 - DF */
122  0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,                /* E0 - EF */
123  0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0                 /* F0 - FF */
124 };
125 
126 
127 /* Binary --> hexadecimal ASCII table. */
128 static const char aschex[] =
129   "\x30\x31\x32\x33\x34\x35\x36\x37\x38\x39\x41\x42\x43\x44\x45\x46";
130 
131 
132 
133 #ifndef __VMS
134 #define filesize(name, stat_data) (stat_data.st_size)
135 #define fopen_read fopen
136 
137 #else
138 
139 #include <fabdef.h>
140 /*
141  * get_vms_file_size does what it takes to get the real size of the file
142  *
143  * For fixed files, find out the size of the EOF block and adjust.
144  *
145  * For all others, have to read the entire file in, discarding the contents.
146  * Most posted text files will be small, and binary files like zlib archives
147  * and CD/DVD images should be either a STREAM_LF format or a fixed format.
148  *
149  */
VmsRealFileSize(const char * name,const struct_stat * stat_buf)150 curl_off_t VmsRealFileSize(const char *name,
151                            const struct_stat *stat_buf)
152 {
153   char buffer[8192];
154   curl_off_t count;
155   int ret_stat;
156   FILE * file;
157 
158   file = fopen(name, FOPEN_READTEXT); /* VMS */
159   if(!file)
160     return 0;
161 
162   count = 0;
163   ret_stat = 1;
164   while(ret_stat > 0) {
165     ret_stat = fread(buffer, 1, sizeof(buffer), file);
166     if(ret_stat)
167       count += ret_stat;
168   }
169   fclose(file);
170 
171   return count;
172 }
173 
174 /*
175  *
176  *  VmsSpecialSize checks to see if the stat st_size can be trusted and
177  *  if not to call a routine to get the correct size.
178  *
179  */
VmsSpecialSize(const char * name,const struct_stat * stat_buf)180 static curl_off_t VmsSpecialSize(const char *name,
181                                  const struct_stat *stat_buf)
182 {
183   switch(stat_buf->st_fab_rfm) {
184   case FAB$C_VAR:
185   case FAB$C_VFC:
186     return VmsRealFileSize(name, stat_buf);
187     break;
188   default:
189     return stat_buf->st_size;
190   }
191 }
192 
193 #define filesize(name, stat_data) VmsSpecialSize(name, &stat_data)
194 
195 /*
196  * vmsfopenread
197  *
198  * For upload to work as expected on VMS, different optional
199  * parameters must be added to the fopen command based on
200  * record format of the file.
201  *
202  */
vmsfopenread(const char * file,const char * mode)203 static FILE * vmsfopenread(const char *file, const char *mode)
204 {
205   struct_stat statbuf;
206   int result;
207 
208   result = stat(file, &statbuf);
209 
210   switch(statbuf.st_fab_rfm) {
211   case FAB$C_VAR:
212   case FAB$C_VFC:
213   case FAB$C_STMCR:
214     return fopen(file, FOPEN_READTEXT); /* VMS */
215     break;
216   default:
217     return fopen(file, FOPEN_READTEXT, "rfm=stmlf", "ctx=stm");
218   }
219 }
220 
221 #define fopen_read vmsfopenread
222 #endif
223 
224 
225 #ifndef HAVE_BASENAME
226 /*
227   (Quote from The Open Group Base Specifications Issue 6 IEEE Std 1003.1, 2004
228   Edition)
229 
230   The basename() function shall take the pathname pointed to by path and
231   return a pointer to the final component of the pathname, deleting any
232   trailing '/' characters.
233 
234   If the string pointed to by path consists entirely of the '/' character,
235   basename() shall return a pointer to the string "/". If the string pointed
236   to by path is exactly "//", it is implementation-defined whether '/' or "//"
237   is returned.
238 
239   If path is a null pointer or points to an empty string, basename() shall
240   return a pointer to the string ".".
241 
242   The basename() function may modify the string pointed to by path, and may
243   return a pointer to static storage that may then be overwritten by a
244   subsequent call to basename().
245 
246   The basename() function need not be reentrant. A function that is not
247   required to be reentrant is not required to be thread-safe.
248 
249 */
Curl_basename(char * path)250 static char *Curl_basename(char *path)
251 {
252   /* Ignore all the details above for now and make a quick and simple
253      implementation here */
254   char *s1;
255   char *s2;
256 
257   s1 = strrchr(path, '/');
258   s2 = strrchr(path, '\\');
259 
260   if(s1 && s2) {
261     path = (s1 > s2? s1 : s2) + 1;
262   }
263   else if(s1)
264     path = s1 + 1;
265   else if(s2)
266     path = s2 + 1;
267 
268   return path;
269 }
270 
271 #define basename(x)  Curl_basename((x))
272 #endif
273 
274 
275 /* Set readback state. */
mimesetstate(struct mime_state * state,enum mimestate tok,void * ptr)276 static void mimesetstate(struct mime_state *state,
277                          enum mimestate tok, void *ptr)
278 {
279   state->state = tok;
280   state->ptr = ptr;
281   state->offset = 0;
282 }
283 
284 
285 /* Escape header string into allocated memory. */
escape_string(struct Curl_easy * data,const char * src,enum mimestrategy strategy)286 static char *escape_string(struct Curl_easy *data,
287                            const char *src, enum mimestrategy strategy)
288 {
289   CURLcode result;
290   struct dynbuf db;
291   const char * const *table;
292   const char * const *p;
293   /* replace first character by rest of string. */
294   static const char * const mimetable[] = {
295     "\\\\\\",
296     "\"\\\"",
297     NULL
298   };
299   /* WHATWG HTML living standard 4.10.21.8 2 specifies:
300      For field names and filenames for file fields, the result of the
301      encoding in the previous bullet point must be escaped by replacing
302      any 0x0A (LF) bytes with the byte sequence `%0A`, 0x0D (CR) with `%0D`
303      and 0x22 (") with `%22`.
304      The user agent must not perform any other escapes. */
305   static const char * const formtable[] = {
306     "\"%22",
307     "\r%0D",
308     "\n%0A",
309     NULL
310   };
311 
312   table = formtable;
313   /* data can be NULL when this function is called indirectly from
314      curl_formget(). */
315   if(strategy == MIMESTRATEGY_MAIL || (data && (data->set.mime_formescape)))
316     table = mimetable;
317 
318   Curl_dyn_init(&db, CURL_MAX_INPUT_LENGTH);
319 
320   for(result = Curl_dyn_addn(&db, STRCONST("")); !result && *src; src++) {
321     for(p = table; *p && **p != *src; p++)
322       ;
323 
324     if(*p)
325       result = Curl_dyn_add(&db, *p + 1);
326     else
327       result = Curl_dyn_addn(&db, src, 1);
328   }
329 
330   return Curl_dyn_ptr(&db);
331 }
332 
333 /* Check if header matches. */
match_header(struct curl_slist * hdr,const char * lbl,size_t len)334 static char *match_header(struct curl_slist *hdr, const char *lbl, size_t len)
335 {
336   char *value = NULL;
337 
338   if(strncasecompare(hdr->data, lbl, len) && hdr->data[len] == ':')
339     for(value = hdr->data + len + 1; *value == ' '; value++)
340       ;
341   return value;
342 }
343 
344 /* Get a header from an slist. */
search_header(struct curl_slist * hdrlist,const char * hdr,size_t len)345 static char *search_header(struct curl_slist *hdrlist,
346                            const char *hdr, size_t len)
347 {
348   char *value = NULL;
349 
350   for(; !value && hdrlist; hdrlist = hdrlist->next)
351     value = match_header(hdrlist, hdr, len);
352 
353   return value;
354 }
355 
strippath(const char * fullfile)356 static char *strippath(const char *fullfile)
357 {
358   char *filename;
359   char *base;
360   filename = strdup(fullfile); /* duplicate since basename() may ruin the
361                                   buffer it works on */
362   if(!filename)
363     return NULL;
364   base = strdup(basename(filename));
365 
366   free(filename); /* free temporary buffer */
367 
368   return base; /* returns an allocated string or NULL ! */
369 }
370 
371 /* Initialize data encoder state. */
cleanup_encoder_state(struct mime_encoder_state * p)372 static void cleanup_encoder_state(struct mime_encoder_state *p)
373 {
374   p->pos = 0;
375   p->bufbeg = 0;
376   p->bufend = 0;
377 }
378 
379 
380 /* Dummy encoder. This is used for 8bit and binary content encodings. */
encoder_nop_read(char * buffer,size_t size,bool ateof,struct curl_mimepart * part)381 static size_t encoder_nop_read(char *buffer, size_t size, bool ateof,
382                                struct curl_mimepart *part)
383 {
384   struct mime_encoder_state *st = &part->encstate;
385   size_t insize = st->bufend - st->bufbeg;
386 
387   (void) ateof;
388 
389   if(!size)
390     return STOP_FILLING;
391 
392   if(size > insize)
393     size = insize;
394 
395   if(size)
396     memcpy(buffer, st->buf + st->bufbeg, size);
397 
398   st->bufbeg += size;
399   return size;
400 }
401 
encoder_nop_size(curl_mimepart * part)402 static curl_off_t encoder_nop_size(curl_mimepart *part)
403 {
404   return part->datasize;
405 }
406 
407 
408 /* 7bit encoder: the encoder is just a data validity check. */
encoder_7bit_read(char * buffer,size_t size,bool ateof,curl_mimepart * part)409 static size_t encoder_7bit_read(char *buffer, size_t size, bool ateof,
410                                 curl_mimepart *part)
411 {
412   struct mime_encoder_state *st = &part->encstate;
413   size_t cursize = st->bufend - st->bufbeg;
414 
415   (void) ateof;
416 
417   if(!size)
418     return STOP_FILLING;
419 
420   if(size > cursize)
421     size = cursize;
422 
423   for(cursize = 0; cursize < size; cursize++) {
424     *buffer = st->buf[st->bufbeg];
425     if(*buffer++ & 0x80)
426       return cursize? cursize: READ_ERROR;
427     st->bufbeg++;
428   }
429 
430   return cursize;
431 }
432 
433 
434 /* Base64 content encoder. */
encoder_base64_read(char * buffer,size_t size,bool ateof,curl_mimepart * part)435 static size_t encoder_base64_read(char *buffer, size_t size, bool ateof,
436                                 curl_mimepart *part)
437 {
438   struct mime_encoder_state *st = &part->encstate;
439   size_t cursize = 0;
440   int i;
441   char *ptr = buffer;
442 
443   while(st->bufbeg < st->bufend) {
444     /* Line full ? */
445     if(st->pos > MAX_ENCODED_LINE_LENGTH - 4) {
446       /* Yes, we need 2 characters for CRLF. */
447       if(size < 2) {
448         if(!cursize)
449           return STOP_FILLING;
450         break;
451       }
452       *ptr++ = '\r';
453       *ptr++ = '\n';
454       st->pos = 0;
455       cursize += 2;
456       size -= 2;
457     }
458 
459     /* Be sure there is enough space and input data for a base64 group. */
460     if(size < 4) {
461       if(!cursize)
462         return STOP_FILLING;
463       break;
464     }
465     if(st->bufend - st->bufbeg < 3)
466       break;
467 
468     /* Encode three bytes as four characters. */
469     i = st->buf[st->bufbeg++] & 0xFF;
470     i = (i << 8) | (st->buf[st->bufbeg++] & 0xFF);
471     i = (i << 8) | (st->buf[st->bufbeg++] & 0xFF);
472     *ptr++ = base64enc[(i >> 18) & 0x3F];
473     *ptr++ = base64enc[(i >> 12) & 0x3F];
474     *ptr++ = base64enc[(i >> 6) & 0x3F];
475     *ptr++ = base64enc[i & 0x3F];
476     cursize += 4;
477     st->pos += 4;
478     size -= 4;
479   }
480 
481   /* If at eof, we have to flush the buffered data. */
482   if(ateof) {
483     if(size < 4) {
484       if(!cursize)
485         return STOP_FILLING;
486     }
487     else {
488       /* Buffered data size can only be 0, 1 or 2. */
489       ptr[2] = ptr[3] = '=';
490       i = 0;
491 
492       /* If there is buffered data */
493       if(st->bufend != st->bufbeg) {
494 
495         if(st->bufend - st->bufbeg == 2)
496           i = (st->buf[st->bufbeg + 1] & 0xFF) << 8;
497 
498         i |= (st->buf[st->bufbeg] & 0xFF) << 16;
499         ptr[0] = base64enc[(i >> 18) & 0x3F];
500         ptr[1] = base64enc[(i >> 12) & 0x3F];
501         if(++st->bufbeg != st->bufend) {
502           ptr[2] = base64enc[(i >> 6) & 0x3F];
503           st->bufbeg++;
504         }
505         cursize += 4;
506         st->pos += 4;
507       }
508     }
509   }
510 
511   return cursize;
512 }
513 
encoder_base64_size(curl_mimepart * part)514 static curl_off_t encoder_base64_size(curl_mimepart *part)
515 {
516   curl_off_t size = part->datasize;
517 
518   if(size <= 0)
519     return size;    /* Unknown size or no data. */
520 
521   /* Compute base64 character count. */
522   size = 4 * (1 + (size - 1) / 3);
523 
524   /* Effective character count must include CRLFs. */
525   return size + 2 * ((size - 1) / MAX_ENCODED_LINE_LENGTH);
526 }
527 
528 
529 /* Quoted-printable lookahead.
530  *
531  * Check if a CRLF or end of data is in input buffer at current position + n.
532  * Return -1 if more data needed, 1 if CRLF or end of data, else 0.
533  */
qp_lookahead_eol(struct mime_encoder_state * st,int ateof,size_t n)534 static int qp_lookahead_eol(struct mime_encoder_state *st, int ateof, size_t n)
535 {
536   n += st->bufbeg;
537   if(n >= st->bufend && ateof)
538     return 1;
539   if(n + 2 > st->bufend)
540     return ateof? 0: -1;
541   if(qp_class[st->buf[n] & 0xFF] == QP_CR &&
542      qp_class[st->buf[n + 1] & 0xFF] == QP_LF)
543     return 1;
544   return 0;
545 }
546 
547 /* Quoted-printable encoder. */
encoder_qp_read(char * buffer,size_t size,bool ateof,curl_mimepart * part)548 static size_t encoder_qp_read(char *buffer, size_t size, bool ateof,
549                               curl_mimepart *part)
550 {
551   struct mime_encoder_state *st = &part->encstate;
552   char *ptr = buffer;
553   size_t cursize = 0;
554   int softlinebreak;
555   char buf[4];
556 
557   /* On all platforms, input is supposed to be ASCII compatible: for this
558      reason, we use hexadecimal ASCII codes in this function rather than
559      character constants that can be interpreted as non-ascii on some
560      platforms. Preserve ASCII encoding on output too. */
561   while(st->bufbeg < st->bufend) {
562     size_t len = 1;
563     size_t consumed = 1;
564     int i = st->buf[st->bufbeg];
565     buf[0] = (char) i;
566     buf[1] = aschex[(i >> 4) & 0xF];
567     buf[2] = aschex[i & 0xF];
568 
569     switch(qp_class[st->buf[st->bufbeg] & 0xFF]) {
570     case QP_OK:          /* Not a special character. */
571       break;
572     case QP_SP:          /* Space or tab. */
573       /* Spacing must be escaped if followed by CRLF. */
574       switch(qp_lookahead_eol(st, ateof, 1)) {
575       case -1:          /* More input data needed. */
576         return cursize;
577       case 0:           /* No encoding needed. */
578         break;
579       default:          /* CRLF after space or tab. */
580         buf[0] = '\x3D';    /* '=' */
581         len = 3;
582         break;
583       }
584       break;
585     case QP_CR:         /* Carriage return. */
586       /* If followed by a line-feed, output the CRLF pair.
587          Else escape it. */
588       switch(qp_lookahead_eol(st, ateof, 0)) {
589       case -1:          /* Need more data. */
590         return cursize;
591       case 1:           /* CRLF found. */
592         buf[len++] = '\x0A';    /* Append '\n'. */
593         consumed = 2;
594         break;
595       default:          /* Not followed by LF: escape. */
596         buf[0] = '\x3D';    /* '=' */
597         len = 3;
598         break;
599       }
600       break;
601     default:            /* Character must be escaped. */
602       buf[0] = '\x3D';    /* '=' */
603       len = 3;
604       break;
605     }
606 
607     /* Be sure the encoded character fits within maximum line length. */
608     if(buf[len - 1] != '\x0A') {    /* '\n' */
609       softlinebreak = st->pos + len > MAX_ENCODED_LINE_LENGTH;
610       if(!softlinebreak && st->pos + len == MAX_ENCODED_LINE_LENGTH) {
611         /* We may use the current line only if end of data or followed by
612            a CRLF. */
613         switch(qp_lookahead_eol(st, ateof, consumed)) {
614         case -1:        /* Need more data. */
615           return cursize;
616         case 0:         /* Not followed by a CRLF. */
617           softlinebreak = 1;
618           break;
619         }
620       }
621       if(softlinebreak) {
622         strcpy(buf, "\x3D\x0D\x0A");    /* "=\r\n" */
623         len = 3;
624         consumed = 0;
625       }
626     }
627 
628     /* If the output buffer would overflow, do not store. */
629     if(len > size) {
630       if(!cursize)
631         return STOP_FILLING;
632       break;
633     }
634 
635     /* Append to output buffer. */
636     memcpy(ptr, buf, len);
637     cursize += len;
638     ptr += len;
639     size -= len;
640     st->pos += len;
641     if(buf[len - 1] == '\x0A')    /* '\n' */
642       st->pos = 0;
643     st->bufbeg += consumed;
644   }
645 
646   return cursize;
647 }
648 
encoder_qp_size(curl_mimepart * part)649 static curl_off_t encoder_qp_size(curl_mimepart *part)
650 {
651   /* Determining the size can only be done by reading the data: unless the
652      data size is 0, we return it as unknown (-1). */
653   return part->datasize? -1: 0;
654 }
655 
656 
657 /* In-memory data callbacks. */
658 /* Argument is a pointer to the mime part. */
mime_mem_read(char * buffer,size_t size,size_t nitems,void * instream)659 static size_t mime_mem_read(char *buffer, size_t size, size_t nitems,
660                             void *instream)
661 {
662   curl_mimepart *part = (curl_mimepart *) instream;
663   size_t sz = curlx_sotouz(part->datasize - part->state.offset);
664   (void) size;   /* Always 1.*/
665 
666   if(!nitems)
667     return STOP_FILLING;
668 
669   if(sz > nitems)
670     sz = nitems;
671 
672   if(sz)
673     memcpy(buffer, part->data + curlx_sotouz(part->state.offset), sz);
674 
675   return sz;
676 }
677 
mime_mem_seek(void * instream,curl_off_t offset,int whence)678 static int mime_mem_seek(void *instream, curl_off_t offset, int whence)
679 {
680   curl_mimepart *part = (curl_mimepart *) instream;
681 
682   switch(whence) {
683   case SEEK_CUR:
684     offset += part->state.offset;
685     break;
686   case SEEK_END:
687     offset += part->datasize;
688     break;
689   }
690 
691   if(offset < 0 || offset > part->datasize)
692     return CURL_SEEKFUNC_FAIL;
693 
694   part->state.offset = offset;
695   return CURL_SEEKFUNC_OK;
696 }
697 
mime_mem_free(void * ptr)698 static void mime_mem_free(void *ptr)
699 {
700   Curl_safefree(((curl_mimepart *) ptr)->data);
701 }
702 
703 
704 /* Named file callbacks. */
705 /* Argument is a pointer to the mime part. */
mime_open_file(curl_mimepart * part)706 static int mime_open_file(curl_mimepart *part)
707 {
708   /* Open a MIMEKIND_FILE part. */
709 
710   if(part->fp)
711     return 0;
712   part->fp = fopen_read(part->data, "rb");
713   return part->fp? 0: -1;
714 }
715 
mime_file_read(char * buffer,size_t size,size_t nitems,void * instream)716 static size_t mime_file_read(char *buffer, size_t size, size_t nitems,
717                              void *instream)
718 {
719   curl_mimepart *part = (curl_mimepart *) instream;
720 
721   if(!nitems)
722     return STOP_FILLING;
723 
724   if(mime_open_file(part))
725     return READ_ERROR;
726 
727   return fread(buffer, size, nitems, part->fp);
728 }
729 
mime_file_seek(void * instream,curl_off_t offset,int whence)730 static int mime_file_seek(void *instream, curl_off_t offset, int whence)
731 {
732   curl_mimepart *part = (curl_mimepart *) instream;
733 
734   if(whence == SEEK_SET && !offset && !part->fp)
735     return CURL_SEEKFUNC_OK;   /* Not open: implicitly already at BOF. */
736 
737   if(mime_open_file(part))
738     return CURL_SEEKFUNC_FAIL;
739 
740   return fseek(part->fp, (long) offset, whence)?
741                CURL_SEEKFUNC_CANTSEEK: CURL_SEEKFUNC_OK;
742 }
743 
mime_file_free(void * ptr)744 static void mime_file_free(void *ptr)
745 {
746   curl_mimepart *part = (curl_mimepart *) ptr;
747 
748   if(part->fp) {
749     fclose(part->fp);
750     part->fp = NULL;
751   }
752   Curl_safefree(part->data);
753 }
754 
755 
756 /* Subparts callbacks. */
757 /* Argument is a pointer to the mime structure. */
758 
759 /* Readback a byte string segment. */
readback_bytes(struct mime_state * state,char * buffer,size_t bufsize,const char * bytes,size_t numbytes,const char * trail,size_t traillen)760 static size_t readback_bytes(struct mime_state *state,
761                              char *buffer, size_t bufsize,
762                              const char *bytes, size_t numbytes,
763                              const char *trail, size_t traillen)
764 {
765   size_t sz;
766   size_t offset = curlx_sotouz(state->offset);
767 
768   if(numbytes > offset) {
769     sz = numbytes - offset;
770     bytes += offset;
771   }
772   else {
773     sz = offset - numbytes;
774     if(sz >= traillen)
775       return 0;
776     bytes = trail + sz;
777     sz = traillen - sz;
778   }
779 
780   if(sz > bufsize)
781     sz = bufsize;
782 
783   memcpy(buffer, bytes, sz);
784   state->offset += sz;
785   return sz;
786 }
787 
788 /* Read a non-encoded part content. */
read_part_content(curl_mimepart * part,char * buffer,size_t bufsize,bool * hasread)789 static size_t read_part_content(curl_mimepart *part,
790                                 char *buffer, size_t bufsize, bool *hasread)
791 {
792   size_t sz = 0;
793 
794   switch(part->lastreadstatus) {
795   case 0:
796   case CURL_READFUNC_ABORT:
797   case CURL_READFUNC_PAUSE:
798   case READ_ERROR:
799     return part->lastreadstatus;
800   default:
801     break;
802   }
803 
804   /* If we can determine we are at end of part data, spare a read. */
805   if(part->datasize != (curl_off_t) -1 &&
806      part->state.offset >= part->datasize) {
807     /* sz is already zero. */
808   }
809   else {
810     switch(part->kind) {
811     case MIMEKIND_MULTIPART:
812       /*
813        * Cannot be processed as other kinds since read function requires
814        * an additional parameter and is highly recursive.
815        */
816        sz = mime_subparts_read(buffer, 1, bufsize, part->arg, hasread);
817        break;
818     case MIMEKIND_FILE:
819       if(part->fp && feof(part->fp))
820         break;  /* At EOF. */
821       FALLTHROUGH();
822     default:
823       if(part->readfunc) {
824         if(!(part->flags & MIME_FAST_READ)) {
825           if(*hasread)
826             return STOP_FILLING;
827           *hasread = TRUE;
828         }
829         sz = part->readfunc(buffer, 1, bufsize, part->arg);
830       }
831       break;
832     }
833   }
834 
835   switch(sz) {
836   case STOP_FILLING:
837     break;
838   case 0:
839   case CURL_READFUNC_ABORT:
840   case CURL_READFUNC_PAUSE:
841   case READ_ERROR:
842     part->lastreadstatus = sz;
843     break;
844   default:
845     part->state.offset += sz;
846     part->lastreadstatus = sz;
847     break;
848   }
849 
850   return sz;
851 }
852 
853 /* Read and encode part content. */
read_encoded_part_content(curl_mimepart * part,char * buffer,size_t bufsize,bool * hasread)854 static size_t read_encoded_part_content(curl_mimepart *part, char *buffer,
855                                         size_t bufsize, bool *hasread)
856 {
857   struct mime_encoder_state *st = &part->encstate;
858   size_t cursize = 0;
859   size_t sz;
860   bool ateof = FALSE;
861 
862   for(;;) {
863     if(st->bufbeg < st->bufend || ateof) {
864       /* Encode buffered data. */
865       sz = part->encoder->encodefunc(buffer, bufsize, ateof, part);
866       switch(sz) {
867       case 0:
868         if(ateof)
869           return cursize;
870         break;
871       case READ_ERROR:
872       case STOP_FILLING:
873         return cursize? cursize: sz;
874       default:
875         cursize += sz;
876         buffer += sz;
877         bufsize -= sz;
878         continue;
879       }
880     }
881 
882     /* We need more data in input buffer. */
883     if(st->bufbeg) {
884       size_t len = st->bufend - st->bufbeg;
885 
886       if(len)
887         memmove(st->buf, st->buf + st->bufbeg, len);
888       st->bufbeg = 0;
889       st->bufend = len;
890     }
891     if(st->bufend >= sizeof(st->buf))
892       return cursize? cursize: READ_ERROR;    /* Buffer full. */
893     sz = read_part_content(part, st->buf + st->bufend,
894                            sizeof(st->buf) - st->bufend, hasread);
895     switch(sz) {
896     case 0:
897       ateof = TRUE;
898       break;
899     case CURL_READFUNC_ABORT:
900     case CURL_READFUNC_PAUSE:
901     case READ_ERROR:
902     case STOP_FILLING:
903       return cursize? cursize: sz;
904     default:
905       st->bufend += sz;
906       break;
907     }
908   }
909 
910   /* NOTREACHED */
911 }
912 
913 /* Readback a mime part. */
readback_part(curl_mimepart * part,char * buffer,size_t bufsize,bool * hasread)914 static size_t readback_part(curl_mimepart *part,
915                             char *buffer, size_t bufsize, bool *hasread)
916 {
917   size_t cursize = 0;
918 
919   /* Readback from part. */
920 
921   while(bufsize) {
922     size_t sz = 0;
923     struct curl_slist *hdr = (struct curl_slist *) part->state.ptr;
924     switch(part->state.state) {
925     case MIMESTATE_BEGIN:
926       mimesetstate(&part->state,
927                    (part->flags & MIME_BODY_ONLY)?
928                      MIMESTATE_BODY: MIMESTATE_CURLHEADERS,
929                    part->curlheaders);
930       break;
931     case MIMESTATE_USERHEADERS:
932       if(!hdr) {
933         mimesetstate(&part->state, MIMESTATE_EOH, NULL);
934         break;
935       }
936       if(match_header(hdr, "Content-Type", 12)) {
937         mimesetstate(&part->state, MIMESTATE_USERHEADERS, hdr->next);
938         break;
939       }
940       FALLTHROUGH();
941     case MIMESTATE_CURLHEADERS:
942       if(!hdr)
943         mimesetstate(&part->state, MIMESTATE_USERHEADERS, part->userheaders);
944       else {
945         sz = readback_bytes(&part->state, buffer, bufsize,
946                             hdr->data, strlen(hdr->data), STRCONST("\r\n"));
947         if(!sz)
948           mimesetstate(&part->state, part->state.state, hdr->next);
949       }
950       break;
951     case MIMESTATE_EOH:
952       sz = readback_bytes(&part->state, buffer, bufsize, STRCONST("\r\n"),
953                           STRCONST(""));
954       if(!sz)
955         mimesetstate(&part->state, MIMESTATE_BODY, NULL);
956       break;
957     case MIMESTATE_BODY:
958       cleanup_encoder_state(&part->encstate);
959       mimesetstate(&part->state, MIMESTATE_CONTENT, NULL);
960       break;
961     case MIMESTATE_CONTENT:
962       if(part->encoder)
963         sz = read_encoded_part_content(part, buffer, bufsize, hasread);
964       else
965         sz = read_part_content(part, buffer, bufsize, hasread);
966       switch(sz) {
967       case 0:
968         mimesetstate(&part->state, MIMESTATE_END, NULL);
969         /* Try sparing open file descriptors. */
970         if(part->kind == MIMEKIND_FILE && part->fp) {
971           fclose(part->fp);
972           part->fp = NULL;
973         }
974         FALLTHROUGH();
975       case CURL_READFUNC_ABORT:
976       case CURL_READFUNC_PAUSE:
977       case READ_ERROR:
978       case STOP_FILLING:
979         return cursize? cursize: sz;
980       }
981       break;
982     case MIMESTATE_END:
983       return cursize;
984     default:
985       break;    /* Other values not in part state. */
986     }
987 
988     /* Bump buffer and counters according to read size. */
989     cursize += sz;
990     buffer += sz;
991     bufsize -= sz;
992   }
993 
994   return cursize;
995 }
996 
997 /* Readback from mime. Warning: not a read callback function. */
mime_subparts_read(char * buffer,size_t size,size_t nitems,void * instream,bool * hasread)998 static size_t mime_subparts_read(char *buffer, size_t size, size_t nitems,
999                                  void *instream, bool *hasread)
1000 {
1001   curl_mime *mime = (curl_mime *) instream;
1002   size_t cursize = 0;
1003   (void) size;   /* Always 1. */
1004 
1005   while(nitems) {
1006     size_t sz = 0;
1007     curl_mimepart *part = mime->state.ptr;
1008     switch(mime->state.state) {
1009     case MIMESTATE_BEGIN:
1010     case MIMESTATE_BODY:
1011       mimesetstate(&mime->state, MIMESTATE_BOUNDARY1, mime->firstpart);
1012       /* The first boundary always follows the header termination empty line,
1013          so is always preceded by a CRLF. We can then spare 2 characters
1014          by skipping the leading CRLF in boundary. */
1015       mime->state.offset += 2;
1016       break;
1017     case MIMESTATE_BOUNDARY1:
1018       sz = readback_bytes(&mime->state, buffer, nitems, STRCONST("\r\n--"),
1019                           STRCONST(""));
1020       if(!sz)
1021         mimesetstate(&mime->state, MIMESTATE_BOUNDARY2, part);
1022       break;
1023     case MIMESTATE_BOUNDARY2:
1024       if(part)
1025         sz = readback_bytes(&mime->state, buffer, nitems, mime->boundary,
1026                             MIME_BOUNDARY_LEN, STRCONST("\r\n"));
1027       else
1028         sz = readback_bytes(&mime->state, buffer, nitems, mime->boundary,
1029                             MIME_BOUNDARY_LEN, STRCONST("--\r\n"));
1030       if(!sz) {
1031         mimesetstate(&mime->state, MIMESTATE_CONTENT, part);
1032       }
1033       break;
1034     case MIMESTATE_CONTENT:
1035       if(!part) {
1036         mimesetstate(&mime->state, MIMESTATE_END, NULL);
1037         break;
1038       }
1039       sz = readback_part(part, buffer, nitems, hasread);
1040       switch(sz) {
1041       case CURL_READFUNC_ABORT:
1042       case CURL_READFUNC_PAUSE:
1043       case READ_ERROR:
1044       case STOP_FILLING:
1045         return cursize? cursize: sz;
1046       case 0:
1047         mimesetstate(&mime->state, MIMESTATE_BOUNDARY1, part->nextpart);
1048         break;
1049       }
1050       break;
1051     case MIMESTATE_END:
1052       return cursize;
1053     default:
1054       break;    /* other values not used in mime state. */
1055     }
1056 
1057     /* Bump buffer and counters according to read size. */
1058     cursize += sz;
1059     buffer += sz;
1060     nitems -= sz;
1061   }
1062 
1063   return cursize;
1064 }
1065 
mime_part_rewind(curl_mimepart * part)1066 static int mime_part_rewind(curl_mimepart *part)
1067 {
1068   int res = CURL_SEEKFUNC_OK;
1069   enum mimestate targetstate = MIMESTATE_BEGIN;
1070 
1071   if(part->flags & MIME_BODY_ONLY)
1072     targetstate = MIMESTATE_BODY;
1073   cleanup_encoder_state(&part->encstate);
1074   if(part->state.state > targetstate) {
1075     res = CURL_SEEKFUNC_CANTSEEK;
1076     if(part->seekfunc) {
1077       res = part->seekfunc(part->arg, (curl_off_t) 0, SEEK_SET);
1078       switch(res) {
1079       case CURL_SEEKFUNC_OK:
1080       case CURL_SEEKFUNC_FAIL:
1081       case CURL_SEEKFUNC_CANTSEEK:
1082         break;
1083       case -1:    /* For fseek() error. */
1084         res = CURL_SEEKFUNC_CANTSEEK;
1085         break;
1086       default:
1087         res = CURL_SEEKFUNC_FAIL;
1088         break;
1089       }
1090     }
1091   }
1092 
1093   if(res == CURL_SEEKFUNC_OK)
1094     mimesetstate(&part->state, targetstate, NULL);
1095 
1096   part->lastreadstatus = 1; /* Successful read status. */
1097   return res;
1098 }
1099 
mime_subparts_seek(void * instream,curl_off_t offset,int whence)1100 static int mime_subparts_seek(void *instream, curl_off_t offset, int whence)
1101 {
1102   curl_mime *mime = (curl_mime *) instream;
1103   curl_mimepart *part;
1104   int result = CURL_SEEKFUNC_OK;
1105 
1106   if(whence != SEEK_SET || offset)
1107     return CURL_SEEKFUNC_CANTSEEK;    /* Only support full rewind. */
1108 
1109   if(mime->state.state == MIMESTATE_BEGIN)
1110     return CURL_SEEKFUNC_OK;           /* Already rewound. */
1111 
1112   for(part = mime->firstpart; part; part = part->nextpart) {
1113     int res = mime_part_rewind(part);
1114     if(res != CURL_SEEKFUNC_OK)
1115       result = res;
1116   }
1117 
1118   if(result == CURL_SEEKFUNC_OK)
1119     mimesetstate(&mime->state, MIMESTATE_BEGIN, NULL);
1120 
1121   return result;
1122 }
1123 
1124 /* Release part content. */
cleanup_part_content(curl_mimepart * part)1125 static void cleanup_part_content(curl_mimepart *part)
1126 {
1127   if(part->freefunc)
1128     part->freefunc(part->arg);
1129 
1130   part->readfunc = NULL;
1131   part->seekfunc = NULL;
1132   part->freefunc = NULL;
1133   part->arg = (void *) part;          /* Defaults to part itself. */
1134   part->data = NULL;
1135   part->fp = NULL;
1136   part->datasize = (curl_off_t) 0;    /* No size yet. */
1137   cleanup_encoder_state(&part->encstate);
1138   part->kind = MIMEKIND_NONE;
1139   part->flags &= ~MIME_FAST_READ;
1140   part->lastreadstatus = 1; /* Successful read status. */
1141   part->state.state = MIMESTATE_BEGIN;
1142 }
1143 
mime_subparts_free(void * ptr)1144 static void mime_subparts_free(void *ptr)
1145 {
1146   curl_mime *mime = (curl_mime *) ptr;
1147 
1148   if(mime && mime->parent) {
1149     mime->parent->freefunc = NULL;  /* Be sure we won't be called again. */
1150     cleanup_part_content(mime->parent);  /* Avoid dangling pointer in part. */
1151   }
1152   curl_mime_free(mime);
1153 }
1154 
1155 /* Do not free subparts: unbind them. This is used for the top level only. */
mime_subparts_unbind(void * ptr)1156 static void mime_subparts_unbind(void *ptr)
1157 {
1158   curl_mime *mime = (curl_mime *) ptr;
1159 
1160   if(mime && mime->parent) {
1161     mime->parent->freefunc = NULL;  /* Be sure we won't be called again. */
1162     cleanup_part_content(mime->parent);  /* Avoid dangling pointer in part. */
1163     mime->parent = NULL;
1164   }
1165 }
1166 
1167 
Curl_mime_cleanpart(curl_mimepart * part)1168 void Curl_mime_cleanpart(curl_mimepart *part)
1169 {
1170   if(part) {
1171     cleanup_part_content(part);
1172     curl_slist_free_all(part->curlheaders);
1173     if(part->flags & MIME_USERHEADERS_OWNER)
1174       curl_slist_free_all(part->userheaders);
1175     Curl_safefree(part->mimetype);
1176     Curl_safefree(part->name);
1177     Curl_safefree(part->filename);
1178     Curl_mime_initpart(part);
1179   }
1180 }
1181 
1182 /* Recursively delete a mime handle and its parts. */
curl_mime_free(curl_mime * mime)1183 void curl_mime_free(curl_mime *mime)
1184 {
1185   curl_mimepart *part;
1186 
1187   if(mime) {
1188     mime_subparts_unbind(mime);  /* Be sure it's not referenced anymore. */
1189     while(mime->firstpart) {
1190       part = mime->firstpart;
1191       mime->firstpart = part->nextpart;
1192       Curl_mime_cleanpart(part);
1193       free(part);
1194     }
1195     free(mime);
1196   }
1197 }
1198 
Curl_mime_duppart(struct Curl_easy * data,curl_mimepart * dst,const curl_mimepart * src)1199 CURLcode Curl_mime_duppart(struct Curl_easy *data,
1200                            curl_mimepart *dst, const curl_mimepart *src)
1201 {
1202   curl_mime *mime;
1203   curl_mimepart *d;
1204   const curl_mimepart *s;
1205   CURLcode res = CURLE_OK;
1206 
1207   DEBUGASSERT(dst);
1208 
1209   /* Duplicate content. */
1210   switch(src->kind) {
1211   case MIMEKIND_NONE:
1212     break;
1213   case MIMEKIND_DATA:
1214     res = curl_mime_data(dst, src->data, (size_t) src->datasize);
1215     break;
1216   case MIMEKIND_FILE:
1217     res = curl_mime_filedata(dst, src->data);
1218     /* Do not abort duplication if file is not readable. */
1219     if(res == CURLE_READ_ERROR)
1220       res = CURLE_OK;
1221     break;
1222   case MIMEKIND_CALLBACK:
1223     res = curl_mime_data_cb(dst, src->datasize, src->readfunc,
1224                             src->seekfunc, src->freefunc, src->arg);
1225     break;
1226   case MIMEKIND_MULTIPART:
1227     /* No one knows about the cloned subparts, thus always attach ownership
1228        to the part. */
1229     mime = curl_mime_init(data);
1230     res = mime? curl_mime_subparts(dst, mime): CURLE_OUT_OF_MEMORY;
1231 
1232     /* Duplicate subparts. */
1233     for(s = ((curl_mime *) src->arg)->firstpart; !res && s; s = s->nextpart) {
1234       d = curl_mime_addpart(mime);
1235       res = d? Curl_mime_duppart(data, d, s): CURLE_OUT_OF_MEMORY;
1236     }
1237     break;
1238   default:  /* Invalid kind: should not occur. */
1239     DEBUGF(infof(data, "invalid MIMEKIND* attempt"));
1240     res = CURLE_BAD_FUNCTION_ARGUMENT;  /* Internal error? */
1241     break;
1242   }
1243 
1244   /* Duplicate headers. */
1245   if(!res && src->userheaders) {
1246     struct curl_slist *hdrs = Curl_slist_duplicate(src->userheaders);
1247 
1248     if(!hdrs)
1249       res = CURLE_OUT_OF_MEMORY;
1250     else {
1251       /* No one but this procedure knows about the new header list,
1252          so always take ownership. */
1253       res = curl_mime_headers(dst, hdrs, TRUE);
1254       if(res)
1255         curl_slist_free_all(hdrs);
1256     }
1257   }
1258 
1259   if(!res) {
1260     /* Duplicate other fields. */
1261     dst->encoder = src->encoder;
1262     res = curl_mime_type(dst, src->mimetype);
1263   }
1264   if(!res)
1265     res = curl_mime_name(dst, src->name);
1266   if(!res)
1267     res = curl_mime_filename(dst, src->filename);
1268 
1269   /* If an error occurred, rollback. */
1270   if(res)
1271     Curl_mime_cleanpart(dst);
1272 
1273   return res;
1274 }
1275 
1276 /*
1277  * Mime build functions.
1278  */
1279 
1280 /* Create a mime handle. */
curl_mime_init(struct Curl_easy * easy)1281 curl_mime *curl_mime_init(struct Curl_easy *easy)
1282 {
1283   curl_mime *mime;
1284 
1285   mime = (curl_mime *) malloc(sizeof(*mime));
1286 
1287   if(mime) {
1288     mime->parent = NULL;
1289     mime->firstpart = NULL;
1290     mime->lastpart = NULL;
1291 
1292     memset(mime->boundary, '-', MIME_BOUNDARY_DASHES);
1293     if(Curl_rand_alnum(easy,
1294                        (unsigned char *) &mime->boundary[MIME_BOUNDARY_DASHES],
1295                        MIME_RAND_BOUNDARY_CHARS + 1)) {
1296       /* failed to get random separator, bail out */
1297       free(mime);
1298       return NULL;
1299     }
1300     mimesetstate(&mime->state, MIMESTATE_BEGIN, NULL);
1301   }
1302 
1303   return mime;
1304 }
1305 
1306 /* Initialize a mime part. */
Curl_mime_initpart(curl_mimepart * part)1307 void Curl_mime_initpart(curl_mimepart *part)
1308 {
1309   memset((char *) part, 0, sizeof(*part));
1310   part->lastreadstatus = 1; /* Successful read status. */
1311   mimesetstate(&part->state, MIMESTATE_BEGIN, NULL);
1312 }
1313 
1314 /* Create a mime part and append it to a mime handle's part list. */
curl_mime_addpart(curl_mime * mime)1315 curl_mimepart *curl_mime_addpart(curl_mime *mime)
1316 {
1317   curl_mimepart *part;
1318 
1319   if(!mime)
1320     return NULL;
1321 
1322   part = (curl_mimepart *) malloc(sizeof(*part));
1323 
1324   if(part) {
1325     Curl_mime_initpart(part);
1326     part->parent = mime;
1327 
1328     if(mime->lastpart)
1329       mime->lastpart->nextpart = part;
1330     else
1331       mime->firstpart = part;
1332 
1333     mime->lastpart = part;
1334   }
1335 
1336   return part;
1337 }
1338 
1339 /* Set mime part name. */
curl_mime_name(curl_mimepart * part,const char * name)1340 CURLcode curl_mime_name(curl_mimepart *part, const char *name)
1341 {
1342   if(!part)
1343     return CURLE_BAD_FUNCTION_ARGUMENT;
1344 
1345   Curl_safefree(part->name);
1346 
1347   if(name) {
1348     part->name = strdup(name);
1349     if(!part->name)
1350       return CURLE_OUT_OF_MEMORY;
1351   }
1352 
1353   return CURLE_OK;
1354 }
1355 
1356 /* Set mime part remote file name. */
curl_mime_filename(curl_mimepart * part,const char * filename)1357 CURLcode curl_mime_filename(curl_mimepart *part, const char *filename)
1358 {
1359   if(!part)
1360     return CURLE_BAD_FUNCTION_ARGUMENT;
1361 
1362   Curl_safefree(part->filename);
1363 
1364   if(filename) {
1365     part->filename = strdup(filename);
1366     if(!part->filename)
1367       return CURLE_OUT_OF_MEMORY;
1368   }
1369 
1370   return CURLE_OK;
1371 }
1372 
1373 /* Set mime part content from memory data. */
curl_mime_data(curl_mimepart * part,const char * ptr,size_t datasize)1374 CURLcode curl_mime_data(curl_mimepart *part,
1375                         const char *ptr, size_t datasize)
1376 {
1377   if(!part)
1378     return CURLE_BAD_FUNCTION_ARGUMENT;
1379 
1380   cleanup_part_content(part);
1381 
1382   if(ptr) {
1383     if(datasize == CURL_ZERO_TERMINATED)
1384       datasize = strlen(ptr);
1385 
1386     part->data = Curl_memdup0(ptr, datasize);
1387     if(!part->data)
1388       return CURLE_OUT_OF_MEMORY;
1389 
1390     part->datasize = datasize;
1391     part->readfunc = mime_mem_read;
1392     part->seekfunc = mime_mem_seek;
1393     part->freefunc = mime_mem_free;
1394     part->flags |= MIME_FAST_READ;
1395     part->kind = MIMEKIND_DATA;
1396   }
1397 
1398   return CURLE_OK;
1399 }
1400 
1401 /* Set mime part content from named local file. */
curl_mime_filedata(curl_mimepart * part,const char * filename)1402 CURLcode curl_mime_filedata(curl_mimepart *part, const char *filename)
1403 {
1404   CURLcode result = CURLE_OK;
1405 
1406   if(!part)
1407     return CURLE_BAD_FUNCTION_ARGUMENT;
1408 
1409   cleanup_part_content(part);
1410 
1411   if(filename) {
1412     char *base;
1413     struct_stat sbuf;
1414 
1415     if(stat(filename, &sbuf) || access(filename, R_OK))
1416       result = CURLE_READ_ERROR;
1417 
1418     part->data = strdup(filename);
1419     if(!part->data)
1420       result = CURLE_OUT_OF_MEMORY;
1421 
1422     part->datasize = -1;
1423     if(!result && S_ISREG(sbuf.st_mode)) {
1424       part->datasize = filesize(filename, sbuf);
1425       part->seekfunc = mime_file_seek;
1426     }
1427 
1428     part->readfunc = mime_file_read;
1429     part->freefunc = mime_file_free;
1430     part->kind = MIMEKIND_FILE;
1431 
1432     /* As a side effect, set the filename to the current file's base name.
1433        It is possible to withdraw this by explicitly calling
1434        curl_mime_filename() with a NULL filename argument after the current
1435        call. */
1436     base = strippath(filename);
1437     if(!base)
1438       result = CURLE_OUT_OF_MEMORY;
1439     else {
1440       CURLcode res = curl_mime_filename(part, base);
1441 
1442       if(res)
1443         result = res;
1444       free(base);
1445     }
1446   }
1447   return result;
1448 }
1449 
1450 /* Set mime part type. */
curl_mime_type(curl_mimepart * part,const char * mimetype)1451 CURLcode curl_mime_type(curl_mimepart *part, const char *mimetype)
1452 {
1453   if(!part)
1454     return CURLE_BAD_FUNCTION_ARGUMENT;
1455 
1456   Curl_safefree(part->mimetype);
1457 
1458   if(mimetype) {
1459     part->mimetype = strdup(mimetype);
1460     if(!part->mimetype)
1461       return CURLE_OUT_OF_MEMORY;
1462   }
1463 
1464   return CURLE_OK;
1465 }
1466 
1467 /* Set mime data transfer encoder. */
curl_mime_encoder(curl_mimepart * part,const char * encoding)1468 CURLcode curl_mime_encoder(curl_mimepart *part, const char *encoding)
1469 {
1470   CURLcode result = CURLE_BAD_FUNCTION_ARGUMENT;
1471   const struct mime_encoder *mep;
1472 
1473   if(!part)
1474     return result;
1475 
1476   part->encoder = NULL;
1477 
1478   if(!encoding)
1479     return CURLE_OK;    /* Removing current encoder. */
1480 
1481   for(mep = encoders; mep->name; mep++)
1482     if(strcasecompare(encoding, mep->name)) {
1483       part->encoder = mep;
1484       result = CURLE_OK;
1485     }
1486 
1487   return result;
1488 }
1489 
1490 /* Set mime part headers. */
curl_mime_headers(curl_mimepart * part,struct curl_slist * headers,int take_ownership)1491 CURLcode curl_mime_headers(curl_mimepart *part,
1492                            struct curl_slist *headers, int take_ownership)
1493 {
1494   if(!part)
1495     return CURLE_BAD_FUNCTION_ARGUMENT;
1496 
1497   if(part->flags & MIME_USERHEADERS_OWNER) {
1498     if(part->userheaders != headers)  /* Allow setting twice the same list. */
1499       curl_slist_free_all(part->userheaders);
1500     part->flags &= ~MIME_USERHEADERS_OWNER;
1501   }
1502   part->userheaders = headers;
1503   if(headers && take_ownership)
1504     part->flags |= MIME_USERHEADERS_OWNER;
1505   return CURLE_OK;
1506 }
1507 
1508 /* Set mime part content from callback. */
curl_mime_data_cb(curl_mimepart * part,curl_off_t datasize,curl_read_callback readfunc,curl_seek_callback seekfunc,curl_free_callback freefunc,void * arg)1509 CURLcode curl_mime_data_cb(curl_mimepart *part, curl_off_t datasize,
1510                            curl_read_callback readfunc,
1511                            curl_seek_callback seekfunc,
1512                            curl_free_callback freefunc, void *arg)
1513 {
1514   if(!part)
1515     return CURLE_BAD_FUNCTION_ARGUMENT;
1516 
1517   cleanup_part_content(part);
1518 
1519   if(readfunc) {
1520     part->readfunc = readfunc;
1521     part->seekfunc = seekfunc;
1522     part->freefunc = freefunc;
1523     part->arg = arg;
1524     part->datasize = datasize;
1525     part->kind = MIMEKIND_CALLBACK;
1526   }
1527 
1528   return CURLE_OK;
1529 }
1530 
1531 /* Set mime part content from subparts. */
Curl_mime_set_subparts(curl_mimepart * part,curl_mime * subparts,int take_ownership)1532 CURLcode Curl_mime_set_subparts(curl_mimepart *part,
1533                                 curl_mime *subparts, int take_ownership)
1534 {
1535   curl_mime *root;
1536 
1537   if(!part)
1538     return CURLE_BAD_FUNCTION_ARGUMENT;
1539 
1540   /* Accept setting twice the same subparts. */
1541   if(part->kind == MIMEKIND_MULTIPART && part->arg == subparts)
1542     return CURLE_OK;
1543 
1544   cleanup_part_content(part);
1545 
1546   if(subparts) {
1547     /* Should not have been attached already. */
1548     if(subparts->parent)
1549       return CURLE_BAD_FUNCTION_ARGUMENT;
1550 
1551     /* Should not be the part's root. */
1552     root = part->parent;
1553     if(root) {
1554       while(root->parent && root->parent->parent)
1555         root = root->parent->parent;
1556       if(subparts == root) {
1557         /* Can't add as a subpart of itself. */
1558         return CURLE_BAD_FUNCTION_ARGUMENT;
1559       }
1560     }
1561 
1562     subparts->parent = part;
1563     /* Subparts are processed internally: no read callback. */
1564     part->seekfunc = mime_subparts_seek;
1565     part->freefunc = take_ownership? mime_subparts_free: mime_subparts_unbind;
1566     part->arg = subparts;
1567     part->datasize = -1;
1568     part->kind = MIMEKIND_MULTIPART;
1569   }
1570 
1571   return CURLE_OK;
1572 }
1573 
curl_mime_subparts(curl_mimepart * part,curl_mime * subparts)1574 CURLcode curl_mime_subparts(curl_mimepart *part, curl_mime *subparts)
1575 {
1576   return Curl_mime_set_subparts(part, subparts, TRUE);
1577 }
1578 
1579 
1580 /* Readback from top mime. */
1581 /* Argument is the dummy top part. */
Curl_mime_read(char * buffer,size_t size,size_t nitems,void * instream)1582 size_t Curl_mime_read(char *buffer, size_t size, size_t nitems, void *instream)
1583 {
1584   curl_mimepart *part = (curl_mimepart *) instream;
1585   size_t ret;
1586   bool hasread;
1587 
1588   (void) size;   /* Always 1. */
1589 
1590   do {
1591     hasread = FALSE;
1592     ret = readback_part(part, buffer, nitems, &hasread);
1593     /*
1594      * If this is not possible to get some data without calling more than
1595      * one read callback (probably because a content encoder is not able to
1596      * deliver a new bunch for the few data accumulated so far), force another
1597      * read until we get enough data or a special exit code.
1598      */
1599   } while(ret == STOP_FILLING);
1600 
1601   return ret;
1602 }
1603 
1604 /* Rewind mime stream. */
Curl_mime_rewind(curl_mimepart * part)1605 CURLcode Curl_mime_rewind(curl_mimepart *part)
1606 {
1607   return mime_part_rewind(part) == CURL_SEEKFUNC_OK?
1608          CURLE_OK: CURLE_SEND_FAIL_REWIND;
1609 }
1610 
1611 /* Compute header list size. */
slist_size(struct curl_slist * s,size_t overhead,const char * skip,size_t skiplen)1612 static size_t slist_size(struct curl_slist *s,
1613                          size_t overhead, const char *skip, size_t skiplen)
1614 {
1615   size_t size = 0;
1616 
1617   for(; s; s = s->next)
1618     if(!skip || !match_header(s, skip, skiplen))
1619       size += strlen(s->data) + overhead;
1620   return size;
1621 }
1622 
1623 /* Get/compute multipart size. */
multipart_size(curl_mime * mime)1624 static curl_off_t multipart_size(curl_mime *mime)
1625 {
1626   curl_off_t size;
1627   curl_off_t boundarysize;
1628   curl_mimepart *part;
1629 
1630   if(!mime)
1631     return 0;           /* Not present -> empty. */
1632 
1633   boundarysize = 4 + MIME_BOUNDARY_LEN + 2;
1634   size = boundarysize;  /* Final boundary - CRLF after headers. */
1635 
1636   for(part = mime->firstpart; part; part = part->nextpart) {
1637     curl_off_t sz = Curl_mime_size(part);
1638 
1639     if(sz < 0)
1640       size = sz;
1641 
1642     if(size >= 0)
1643       size += boundarysize + sz;
1644   }
1645 
1646   return size;
1647 }
1648 
1649 /* Get/compute mime size. */
Curl_mime_size(curl_mimepart * part)1650 curl_off_t Curl_mime_size(curl_mimepart *part)
1651 {
1652   curl_off_t size;
1653 
1654   if(part->kind == MIMEKIND_MULTIPART)
1655     part->datasize = multipart_size(part->arg);
1656 
1657   size = part->datasize;
1658 
1659   if(part->encoder)
1660     size = part->encoder->sizefunc(part);
1661 
1662   if(size >= 0 && !(part->flags & MIME_BODY_ONLY)) {
1663     /* Compute total part size. */
1664     size += slist_size(part->curlheaders, 2, NULL, 0);
1665     size += slist_size(part->userheaders, 2, STRCONST("Content-Type"));
1666     size += 2;    /* CRLF after headers. */
1667   }
1668   return size;
1669 }
1670 
1671 /* Add a header. */
1672 /* VARARGS2 */
Curl_mime_add_header(struct curl_slist ** slp,const char * fmt,...)1673 CURLcode Curl_mime_add_header(struct curl_slist **slp, const char *fmt, ...)
1674 {
1675   struct curl_slist *hdr = NULL;
1676   char *s = NULL;
1677   va_list ap;
1678 
1679   va_start(ap, fmt);
1680   s = curl_mvaprintf(fmt, ap);
1681   va_end(ap);
1682 
1683   if(s) {
1684     hdr = Curl_slist_append_nodup(*slp, s);
1685     if(hdr)
1686       *slp = hdr;
1687     else
1688       free(s);
1689   }
1690 
1691   return hdr? CURLE_OK: CURLE_OUT_OF_MEMORY;
1692 }
1693 
1694 /* Add a content type header. */
add_content_type(struct curl_slist ** slp,const char * type,const char * boundary)1695 static CURLcode add_content_type(struct curl_slist **slp,
1696                                  const char *type, const char *boundary)
1697 {
1698   return Curl_mime_add_header(slp, "Content-Type: %s%s%s", type,
1699                               boundary? "; boundary=": "",
1700                               boundary? boundary: "");
1701 }
1702 
Curl_mime_contenttype(const char * filename)1703 const char *Curl_mime_contenttype(const char *filename)
1704 {
1705   /*
1706    * If no content type was specified, we scan through a few well-known
1707    * extensions and pick the first we match!
1708    */
1709   struct ContentType {
1710     const char *extension;
1711     const char *type;
1712   };
1713   static const struct ContentType ctts[] = {
1714     {".gif",  "image/gif"},
1715     {".jpg",  "image/jpeg"},
1716     {".jpeg", "image/jpeg"},
1717     {".png",  "image/png"},
1718     {".svg",  "image/svg+xml"},
1719     {".txt",  "text/plain"},
1720     {".htm",  "text/html"},
1721     {".html", "text/html"},
1722     {".pdf",  "application/pdf"},
1723     {".xml",  "application/xml"}
1724   };
1725 
1726   if(filename) {
1727     size_t len1 = strlen(filename);
1728     const char *nameend = filename + len1;
1729     unsigned int i;
1730 
1731     for(i = 0; i < sizeof(ctts) / sizeof(ctts[0]); i++) {
1732       size_t len2 = strlen(ctts[i].extension);
1733 
1734       if(len1 >= len2 && strcasecompare(nameend - len2, ctts[i].extension))
1735         return ctts[i].type;
1736     }
1737   }
1738   return NULL;
1739 }
1740 
content_type_match(const char * contenttype,const char * target,size_t len)1741 static bool content_type_match(const char *contenttype,
1742                                const char *target, size_t len)
1743 {
1744   if(contenttype && strncasecompare(contenttype, target, len))
1745     switch(contenttype[len]) {
1746     case '\0':
1747     case '\t':
1748     case '\r':
1749     case '\n':
1750     case ' ':
1751     case ';':
1752       return TRUE;
1753     }
1754   return FALSE;
1755 }
1756 
Curl_mime_prepare_headers(struct Curl_easy * data,curl_mimepart * part,const char * contenttype,const char * disposition,enum mimestrategy strategy)1757 CURLcode Curl_mime_prepare_headers(struct Curl_easy *data,
1758                                    curl_mimepart *part,
1759                                    const char *contenttype,
1760                                    const char *disposition,
1761                                    enum mimestrategy strategy)
1762 {
1763   curl_mime *mime = NULL;
1764   const char *boundary = NULL;
1765   char *customct;
1766   const char *cte = NULL;
1767   CURLcode ret = CURLE_OK;
1768 
1769   /* Get rid of previously prepared headers. */
1770   curl_slist_free_all(part->curlheaders);
1771   part->curlheaders = NULL;
1772 
1773   /* Be sure we won't access old headers later. */
1774   if(part->state.state == MIMESTATE_CURLHEADERS)
1775     mimesetstate(&part->state, MIMESTATE_CURLHEADERS, NULL);
1776 
1777   /* Check if content type is specified. */
1778   customct = part->mimetype;
1779   if(!customct)
1780     customct = search_header(part->userheaders, STRCONST("Content-Type"));
1781   if(customct)
1782     contenttype = customct;
1783 
1784   /* If content type is not specified, try to determine it. */
1785   if(!contenttype) {
1786     switch(part->kind) {
1787     case MIMEKIND_MULTIPART:
1788       contenttype = MULTIPART_CONTENTTYPE_DEFAULT;
1789       break;
1790     case MIMEKIND_FILE:
1791       contenttype = Curl_mime_contenttype(part->filename);
1792       if(!contenttype)
1793         contenttype = Curl_mime_contenttype(part->data);
1794       if(!contenttype && part->filename)
1795         contenttype = FILE_CONTENTTYPE_DEFAULT;
1796       break;
1797     default:
1798       contenttype = Curl_mime_contenttype(part->filename);
1799       break;
1800     }
1801   }
1802 
1803   if(part->kind == MIMEKIND_MULTIPART) {
1804     mime = (curl_mime *) part->arg;
1805     if(mime)
1806       boundary = mime->boundary;
1807   }
1808   else if(contenttype && !customct &&
1809           content_type_match(contenttype, STRCONST("text/plain")))
1810     if(strategy == MIMESTRATEGY_MAIL || !part->filename)
1811       contenttype = NULL;
1812 
1813   /* Issue content-disposition header only if not already set by caller. */
1814   if(!search_header(part->userheaders, STRCONST("Content-Disposition"))) {
1815     if(!disposition)
1816       if(part->filename || part->name ||
1817         (contenttype && !strncasecompare(contenttype, "multipart/", 10)))
1818           disposition = DISPOSITION_DEFAULT;
1819     if(disposition && curl_strequal(disposition, "attachment") &&
1820      !part->name && !part->filename)
1821       disposition = NULL;
1822     if(disposition) {
1823       char *name = NULL;
1824       char *filename = NULL;
1825 
1826       if(part->name) {
1827         name = escape_string(data, part->name, strategy);
1828         if(!name)
1829           ret = CURLE_OUT_OF_MEMORY;
1830       }
1831       if(!ret && part->filename) {
1832         filename = escape_string(data, part->filename, strategy);
1833         if(!filename)
1834           ret = CURLE_OUT_OF_MEMORY;
1835       }
1836       if(!ret)
1837         ret = Curl_mime_add_header(&part->curlheaders,
1838                                    "Content-Disposition: %s%s%s%s%s%s%s",
1839                                    disposition,
1840                                    name? "; name=\"": "",
1841                                    name? name: "",
1842                                    name? "\"": "",
1843                                    filename? "; filename=\"": "",
1844                                    filename? filename: "",
1845                                    filename? "\"": "");
1846       Curl_safefree(name);
1847       Curl_safefree(filename);
1848       if(ret)
1849         return ret;
1850       }
1851     }
1852 
1853   /* Issue Content-Type header. */
1854   if(contenttype) {
1855     ret = add_content_type(&part->curlheaders, contenttype, boundary);
1856     if(ret)
1857       return ret;
1858   }
1859 
1860   /* Content-Transfer-Encoding header. */
1861   if(!search_header(part->userheaders,
1862                     STRCONST("Content-Transfer-Encoding"))) {
1863     if(part->encoder)
1864       cte = part->encoder->name;
1865     else if(contenttype && strategy == MIMESTRATEGY_MAIL &&
1866      part->kind != MIMEKIND_MULTIPART)
1867       cte = "8bit";
1868     if(cte) {
1869       ret = Curl_mime_add_header(&part->curlheaders,
1870                                  "Content-Transfer-Encoding: %s", cte);
1871       if(ret)
1872         return ret;
1873     }
1874   }
1875 
1876   /* If we were reading curl-generated headers, restart with new ones (this
1877      should not occur). */
1878   if(part->state.state == MIMESTATE_CURLHEADERS)
1879     mimesetstate(&part->state, MIMESTATE_CURLHEADERS, part->curlheaders);
1880 
1881   /* Process subparts. */
1882   if(part->kind == MIMEKIND_MULTIPART && mime) {
1883     curl_mimepart *subpart;
1884 
1885     disposition = NULL;
1886     if(content_type_match(contenttype, STRCONST("multipart/form-data")))
1887       disposition = "form-data";
1888     for(subpart = mime->firstpart; subpart; subpart = subpart->nextpart) {
1889       ret = Curl_mime_prepare_headers(data, subpart, NULL,
1890                                       disposition, strategy);
1891       if(ret)
1892         return ret;
1893     }
1894   }
1895   return ret;
1896 }
1897 
1898 /* Recursively reset paused status in the given part. */
Curl_mime_unpause(curl_mimepart * part)1899 void Curl_mime_unpause(curl_mimepart *part)
1900 {
1901   if(part) {
1902     if(part->lastreadstatus == CURL_READFUNC_PAUSE)
1903       part->lastreadstatus = 1; /* Successful read status. */
1904     if(part->kind == MIMEKIND_MULTIPART) {
1905       curl_mime *mime = (curl_mime *) part->arg;
1906 
1907       if(mime) {
1908         curl_mimepart *subpart;
1909 
1910         for(subpart = mime->firstpart; subpart; subpart = subpart->nextpart)
1911           Curl_mime_unpause(subpart);
1912       }
1913     }
1914   }
1915 }
1916 
1917 
1918 #else /* !CURL_DISABLE_MIME && (!CURL_DISABLE_HTTP ||
1919                                 !CURL_DISABLE_SMTP || !CURL_DISABLE_IMAP) */
1920 
1921 /* Mime not compiled in: define stubs for externally-referenced functions. */
curl_mime_init(CURL * easy)1922 curl_mime *curl_mime_init(CURL *easy)
1923 {
1924   (void) easy;
1925   return NULL;
1926 }
1927 
curl_mime_free(curl_mime * mime)1928 void curl_mime_free(curl_mime *mime)
1929 {
1930   (void) mime;
1931 }
1932 
curl_mime_addpart(curl_mime * mime)1933 curl_mimepart *curl_mime_addpart(curl_mime *mime)
1934 {
1935   (void) mime;
1936   return NULL;
1937 }
1938 
curl_mime_name(curl_mimepart * part,const char * name)1939 CURLcode curl_mime_name(curl_mimepart *part, const char *name)
1940 {
1941   (void) part;
1942   (void) name;
1943   return CURLE_NOT_BUILT_IN;
1944 }
1945 
curl_mime_filename(curl_mimepart * part,const char * filename)1946 CURLcode curl_mime_filename(curl_mimepart *part, const char *filename)
1947 {
1948   (void) part;
1949   (void) filename;
1950   return CURLE_NOT_BUILT_IN;
1951 }
1952 
curl_mime_type(curl_mimepart * part,const char * mimetype)1953 CURLcode curl_mime_type(curl_mimepart *part, const char *mimetype)
1954 {
1955   (void) part;
1956   (void) mimetype;
1957   return CURLE_NOT_BUILT_IN;
1958 }
1959 
curl_mime_encoder(curl_mimepart * part,const char * encoding)1960 CURLcode curl_mime_encoder(curl_mimepart *part, const char *encoding)
1961 {
1962   (void) part;
1963   (void) encoding;
1964   return CURLE_NOT_BUILT_IN;
1965 }
1966 
curl_mime_data(curl_mimepart * part,const char * data,size_t datasize)1967 CURLcode curl_mime_data(curl_mimepart *part,
1968                         const char *data, size_t datasize)
1969 {
1970   (void) part;
1971   (void) data;
1972   (void) datasize;
1973   return CURLE_NOT_BUILT_IN;
1974 }
1975 
curl_mime_filedata(curl_mimepart * part,const char * filename)1976 CURLcode curl_mime_filedata(curl_mimepart *part, const char *filename)
1977 {
1978   (void) part;
1979   (void) filename;
1980   return CURLE_NOT_BUILT_IN;
1981 }
1982 
curl_mime_data_cb(curl_mimepart * part,curl_off_t datasize,curl_read_callback readfunc,curl_seek_callback seekfunc,curl_free_callback freefunc,void * arg)1983 CURLcode curl_mime_data_cb(curl_mimepart *part,
1984                            curl_off_t datasize,
1985                            curl_read_callback readfunc,
1986                            curl_seek_callback seekfunc,
1987                            curl_free_callback freefunc,
1988                            void *arg)
1989 {
1990   (void) part;
1991   (void) datasize;
1992   (void) readfunc;
1993   (void) seekfunc;
1994   (void) freefunc;
1995   (void) arg;
1996   return CURLE_NOT_BUILT_IN;
1997 }
1998 
curl_mime_subparts(curl_mimepart * part,curl_mime * subparts)1999 CURLcode curl_mime_subparts(curl_mimepart *part, curl_mime *subparts)
2000 {
2001   (void) part;
2002   (void) subparts;
2003   return CURLE_NOT_BUILT_IN;
2004 }
2005 
curl_mime_headers(curl_mimepart * part,struct curl_slist * headers,int take_ownership)2006 CURLcode curl_mime_headers(curl_mimepart *part,
2007                            struct curl_slist *headers, int take_ownership)
2008 {
2009   (void) part;
2010   (void) headers;
2011   (void) take_ownership;
2012   return CURLE_NOT_BUILT_IN;
2013 }
2014 
Curl_mime_add_header(struct curl_slist ** slp,const char * fmt,...)2015 CURLcode Curl_mime_add_header(struct curl_slist **slp, const char *fmt, ...)
2016 {
2017   (void)slp;
2018   (void)fmt;
2019   return CURLE_NOT_BUILT_IN;
2020 }
2021 
2022 #endif /* if disabled */
2023