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