Coverage Report

Created: 2026-06-15 07:03

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