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