1 /* $NetBSD: mime_codecs.c,v 1.12 2019/10/24 18:18:00 kamil Exp $ */ 2 3 /*- 4 * Copyright (c) 2006 The NetBSD Foundation, Inc. 5 * All rights reserved. 6 * 7 * This code is derived from software contributed to The NetBSD Foundation 8 * by Anon Ymous. 9 * 10 * Redistribution and use in source and binary forms, with or without 11 * modification, are permitted provided that the following conditions 12 * are met: 13 * 1. Redistributions of source code must retain the above copyright 14 * notice, this list of conditions and the following disclaimer. 15 * 2. Redistributions in binary form must reproduce the above copyright 16 * notice, this list of conditions and the following disclaimer in the 17 * documentation and/or other materials provided with the distribution. 18 * 19 * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS 20 * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED 21 * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR 22 * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS 23 * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR 24 * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF 25 * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS 26 * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN 27 * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) 28 * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE 29 * POSSIBILITY OF SUCH DAMAGE. 30 */ 31 32 /* 33 * This module contains all mime related codecs. Typically there are 34 * two versions: one operating on buffers and one operating on files. 35 * All exported routines have a "mime_" prefix. The file oriented 36 * routines have a "mime_f" prefix replacing the "mime_" prefix of the 37 * equivalent buffer based version. 38 * 39 * The file based API should be: 40 * 41 * mime_f<name>_{encode,decode}(FILE *in, FILE *out, void *cookie) 42 * 43 * XXX - currently this naming convention has not been adheared to. 44 * 45 * where the cookie is a generic way to pass arguments to the routine. 46 * This way these routines can be run by run_function() in mime.c. 47 * 48 * The buffer based API is not as rigid. 49 */ 50 51 #ifdef MIME_SUPPORT 52 53 #include <sys/cdefs.h> 54 #ifndef __lint__ 55 __RCSID("$NetBSD: mime_codecs.c,v 1.12 2019/10/24 18:18:00 kamil Exp $"); 56 #endif /* not __lint__ */ 57 58 #include <assert.h> 59 #include <iconv.h> 60 #include <stdio.h> 61 #include <stdlib.h> 62 #include <util.h> 63 64 #include "def.h" 65 #include "extern.h" 66 #include "mime_codecs.h" 67 68 69 #ifdef CHARSET_SUPPORT 70 /************************************************************************ 71 * Core character set conversion routines. 72 * 73 */ 74 75 /* 76 * Fault-tolerant iconv() function. 77 * 78 * This routine was borrowed from nail-11.25/mime.c and modified. It 79 * tries to handle errno == EILSEQ by restarting at the next input 80 * byte (is this a good idea?). All other errors are handled by the 81 * caller. 82 */ 83 PUBLIC size_t 84 mime_iconv(iconv_t cd, const char **inb, size_t *inbleft, char **outb, size_t *outbleft) 85 { 86 size_t sz = 0; 87 88 while ((sz = iconv(cd, __UNCONST(inb), inbleft, outb, outbleft)) 89 == (size_t)-1 90 && errno == EILSEQ) { 91 if (*outbleft > 0) { 92 *(*outb)++ = '?'; 93 (*outbleft)--; 94 } else { 95 **outb = '\0'; 96 return E2BIG; 97 } 98 if (*inbleft > 0) { 99 (*inb)++; 100 (*inbleft)--; 101 } else { 102 **outb = '\0'; 103 break; 104 } 105 } 106 return sz; 107 } 108 109 /* 110 * This routine was mostly borrowed from src/usr.bin/iconv/iconv.c. 111 * We don't care about the invalid character count, so don't bother 112 * with __iconv(). We do care about robustness, so call iconv_ft() 113 * above to try to recover from errors. 114 */ 115 #define INBUFSIZE 1024 116 #define OUTBUFSIZE (INBUFSIZE * 2) 117 118 PUBLIC void 119 mime_ficonv(FILE *fi, FILE *fo, void *cookie) 120 { 121 char inbuf[INBUFSIZE], outbuf[OUTBUFSIZE], *out; 122 const char *in; 123 size_t inbytes, outbytes, ret; 124 iconv_t cd; 125 126 /* 127 * NOTE: iconv_t is actually a pointer typedef, so this 128 * conversion is not what it appears to be! 129 */ 130 cd = (iconv_t)cookie; 131 132 while ((inbytes = fread(inbuf, 1, INBUFSIZE, fi)) > 0) { 133 in = inbuf; 134 while (inbytes > 0) { 135 out = outbuf; 136 outbytes = OUTBUFSIZE; 137 ret = mime_iconv(cd, &in, &inbytes, &out, &outbytes); 138 if (ret == (size_t)-1 && errno != E2BIG) { 139 if (errno != EINVAL || in == inbuf) { 140 /* XXX - what is proper here? 141 * Just copy out the remains? */ 142 (void)fprintf(fo, 143 "\n\t[ iconv truncated message: %s ]\n\n", 144 strerror(errno)); 145 return; 146 } 147 /* 148 * If here: errno == EINVAL && in != inbuf 149 */ 150 /* incomplete input character */ 151 (void)memmove(inbuf, in, inbytes); 152 ret = fread(inbuf + inbytes, 1, 153 INBUFSIZE - inbytes, fi); 154 if (ret == 0) { 155 if (feof(fi)) { 156 (void)fprintf(fo, 157 "\n\t[ unexpected end of file; " 158 "the last character is " 159 "incomplete. ]\n\n"); 160 return; 161 } 162 (void)fprintf(fo, 163 "\n\t[ fread(): %s ]\n\n", 164 strerror(errno)); 165 return; 166 } 167 in = inbuf; 168 inbytes += ret; 169 170 } 171 if (outbytes < OUTBUFSIZE) 172 (void)fwrite(outbuf, 1, OUTBUFSIZE - outbytes, fo); 173 } 174 } 175 /* reset the shift state of the output buffer */ 176 outbytes = OUTBUFSIZE; 177 out = outbuf; 178 ret = iconv(cd, NULL, NULL, &out, &outbytes); 179 if (ret == (size_t)-1) { 180 (void)fprintf(fo, "\n\t[ iconv(): %s ]\n\n", 181 strerror(errno)); 182 return; 183 } 184 if (outbytes < OUTBUFSIZE) 185 (void)fwrite(outbuf, 1, OUTBUFSIZE - outbytes, fo); 186 } 187 188 #endif /* CHARSET_SUPPORT */ 189 190 191 192 /************************************************************************ 193 * Core base64 routines 194 * 195 * Defined in sec 6.8 of RFC 2045. 196 */ 197 198 /* 199 * Decode a base64 buffer. 200 * 201 * bin: buffer to hold the decoded (binary) result (see note 1). 202 * b64: buffer holding the encoded (base64) source. 203 * cnt: number of bytes in the b64 buffer to decode (see note 2). 204 * 205 * Return: the number of bytes written to the 'bin' buffer or -1 on 206 * error. 207 * NOTES: 208 * 1) It is the callers responsibility to ensure that bin is large 209 * enough to hold the result. 210 * 2) The b64 buffer should always contain a multiple of 4 bytes of 211 * data! 212 */ 213 PUBLIC ssize_t 214 mime_b64tobin(char *bin, const char *b64, size_t cnt) 215 { 216 static const signed char b64index[] = { 217 -1,-1,-1,-1, -1,-1,-1,-1, -1,-1,-1,-1, -1,-1,-1,-1, 218 -1,-1,-1,-1, -1,-1,-1,-1, -1,-1,-1,-1, -1,-1,-1,-1, 219 -1,-1,-1,-1, -1,-1,-1,-1, -1,-1,-1,62, -1,-1,-1,63, 220 52,53,54,55, 56,57,58,59, 60,61,-1,-1, -1,-2,-1,-1, 221 -1, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9,10, 11,12,13,14, 222 15,16,17,18, 19,20,21,22, 23,24,25,-1, -1,-1,-1,-1, 223 -1,26,27,28, 29,30,31,32, 33,34,35,36, 37,38,39,40, 224 41,42,43,44, 45,46,47,48, 49,50,51,-1, -1,-1,-1,-1 225 }; 226 unsigned char *p; 227 const unsigned char *q, *end; 228 229 #define EQU (unsigned)-2 230 #define BAD (unsigned)-1 231 #define uchar64(c) ((c) >= sizeof(b64index) ? BAD : (unsigned)b64index[(c)]) 232 233 p = (unsigned char *)bin; 234 q = (const unsigned char *)b64; 235 for (end = q + cnt; q < end; q += 4) { 236 unsigned a = uchar64(q[0]); 237 unsigned b = uchar64(q[1]); 238 unsigned c = uchar64(q[2]); 239 unsigned d = uchar64(q[3]); 240 241 if (a == BAD || a == EQU || b == BAD || b == EQU || 242 c == BAD || d == BAD) 243 return -1; 244 245 *p++ = ((a << 2) | ((b & 0x30) >> 4)); 246 if (c == EQU) { /* got '=' */ 247 if (d != EQU) 248 return -1; 249 break; 250 } 251 *p++ = (((b & 0x0f) << 4) | ((c & 0x3c) >> 2)); 252 if (d == EQU) { /* got '=' */ 253 break; 254 } 255 *p++ = (((c & 0x03) << 6) | d); 256 } 257 258 #undef uchar64 259 #undef EQU 260 #undef BAD 261 262 return p - (unsigned char*)bin; 263 } 264 265 /* 266 * Encode a buffer as a base64 result. 267 * 268 * b64: buffer to hold the encoded (base64) result (see note). 269 * bin: buffer holding the binary source. 270 * cnt: number of bytes in the bin buffer to encode. 271 * 272 * NOTE: it is the callers responsibility to ensure that 'b64' is 273 * large enough to hold the result. 274 */ 275 PUBLIC void 276 mime_bintob64(char *b64, const char *bin, size_t cnt) 277 { 278 static const char b64table[] = 279 "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/"; 280 const unsigned char *p = (const unsigned char*)bin; 281 ssize_t i; 282 283 for (i = cnt; i > 0; i -= 3) { 284 unsigned a = p[0]; 285 unsigned b = p[1]; 286 unsigned c = p[2]; 287 288 b64[0] = b64table[a >> 2]; 289 switch(i) { 290 case 1: 291 b64[1] = b64table[((a & 0x3) << 4)]; 292 b64[2] = '='; 293 b64[3] = '='; 294 break; 295 case 2: 296 b64[1] = b64table[((a & 0x3) << 4) | ((b & 0xf0) >> 4)]; 297 b64[2] = b64table[((b & 0xf) << 2)]; 298 b64[3] = '='; 299 break; 300 default: 301 b64[1] = b64table[((a & 0x3) << 4) | ((b & 0xf0) >> 4)]; 302 b64[2] = b64table[((b & 0xf) << 2) | ((c & 0xc0) >> 6)]; 303 b64[3] = b64table[c & 0x3f]; 304 break; 305 } 306 p += 3; 307 b64 += 4; 308 } 309 } 310 311 312 #define MIME_BASE64_LINE_MAX (4 * 19) /* max line length is 76: see RFC2045 sec 6.8 */ 313 314 static void 315 mime_fB64_encode(FILE *fi, FILE *fo, void *cookie __unused) 316 { 317 static char b64[MIME_BASE64_LINE_MAX]; 318 static char mem[3 * (MIME_BASE64_LINE_MAX / 4)]; 319 size_t cnt; 320 char *cp; 321 size_t limit; 322 #ifdef __lint__ 323 cookie = cookie; 324 #endif 325 limit = 0; 326 if ((cp = value(ENAME_MIME_B64_LINE_MAX)) != NULL) 327 limit = (size_t)atoi(cp); 328 if (limit == 0 || limit > sizeof(b64)) 329 limit = sizeof(b64); 330 331 limit = 3 * roundup(limit, 4) / 4; 332 if (limit < 3) 333 limit = 3; 334 335 while ((cnt = fread(mem, sizeof(*mem), limit, fi)) > 0) { 336 mime_bintob64(b64, mem, (size_t)cnt); 337 (void)fwrite(b64, sizeof(*b64), (size_t)4 * roundup(cnt, 3) / 3, fo); 338 (void)putc('\n', fo); 339 } 340 } 341 342 static void 343 mime_fB64_decode(FILE *fi, FILE *fo, void *add_lf) 344 { 345 char *line; 346 size_t len; 347 char *buf; 348 size_t buflen; 349 350 buflen = 3 * (MIME_BASE64_LINE_MAX / 4); 351 buf = emalloc(buflen); 352 353 while ((line = fgetln(fi, &len)) != NULL) { 354 ssize_t binlen; 355 if (line[len-1] == '\n') /* forget the trailing newline */ 356 len--; 357 358 /* trash trailing white space */ 359 for (/*EMPTY*/; len > 0 && is_WSP(line[len-1]); len--) 360 continue; 361 362 /* skip leading white space */ 363 for (/*EMPTY*/; len > 0 && is_WSP(line[0]); len--, line++) 364 continue; 365 366 if (len == 0) 367 break; 368 369 if (3 * len > 4 * buflen) { 370 buflen *= 2; 371 buf = erealloc(buf, buflen); 372 } 373 374 binlen = mime_b64tobin(buf, line, len); 375 376 if (binlen <= 0) { 377 (void)fprintf(fo, "WARN: invalid base64 encoding\n"); 378 break; 379 } 380 (void)fwrite(buf, 1, (size_t)binlen, fo); 381 } 382 383 free(buf); 384 385 if (add_lf) 386 (void)fputc('\n', fo); 387 } 388 389 390 /************************************************************************ 391 * Core quoted-printable routines. 392 * 393 * Defined in sec 6.7 of RFC 2045. 394 */ 395 396 /* 397 * strtol(3), but inline and with easy error indication. 398 */ 399 static inline int 400 _qp_cfromhex(char const *hex) 401 { 402 /* Be robust, allow lowercase hexadecimal letters, too */ 403 static unsigned char const atoi16[] = { 404 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, /* 0x30-0x37 */ 405 0x08, 0x09, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, /* 0x38-0x3F */ 406 0xFF, 0x0A, 0x0B, 0x0C, 0x0D, 0x0E, 0x0F, 0xFF, /* 0x40-0x47 */ 407 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, /* 0x48-0x4f */ 408 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, /* 0x50-0x57 */ 409 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, /* 0x58-0x5f */ 410 0xFF, 0x0A, 0x0B, 0x0C, 0x0D, 0x0E, 0x0F, 0xFF /* 0x60-0x67 */ 411 }; 412 unsigned char i1, i2; 413 int r; 414 415 if ((i1 = (unsigned char)hex[0] - '0') >= __arraycount(atoi16) || 416 (i2 = (unsigned char)hex[1] - '0') >= __arraycount(atoi16)) 417 goto jerr; 418 i1 = atoi16[i1]; 419 i2 = atoi16[i2]; 420 if ((i1 | i2) & 0xF0) 421 goto jerr; 422 r = i1; 423 r <<= 4; 424 r += i2; 425 jleave: 426 return r; 427 jerr: 428 r = -1; 429 goto jleave; 430 } 431 432 /* 433 * Header specific "quoted-printable" decode! 434 * Differences with body QP decoding (see rfc 2047, sec 4.2): 435 * 1) '=' occurs _only_ when followed by two hex digits (FWS is not allowed). 436 * 2) Spaces can be encoded as '_' in headers for readability. 437 */ 438 static ssize_t 439 mime_QPh_decode(char *outbuf, size_t outlen, const char *inbuf, size_t inlen) 440 { 441 const char *p, *inend; 442 char *outend; 443 char *q; 444 445 outend = outbuf + outlen; 446 inend = inbuf + inlen; 447 q = outbuf; 448 for (p = inbuf; p < inend; p++) { 449 if (q >= outend) 450 return -1; 451 if (*p == '=') { 452 p++; 453 if (p + 1 < inend) { 454 int c = _qp_cfromhex(p++); 455 if (c < 0) 456 return -1; 457 *q++ = (char)c; 458 } 459 else 460 return -1; 461 } 462 else if (*p == '_') /* header's may encode ' ' as '_' */ 463 *q++ = ' '; 464 else 465 *q++ = *p; 466 } 467 return q - outbuf; 468 } 469 470 471 static int 472 mustquote(unsigned char *p, unsigned char *end, size_t l) 473 { 474 #define N 0 /* do not quote */ 475 #define Q 1 /* must quote */ 476 #define SP 2 /* white space */ 477 #define XF 3 /* special character 'F' - maybe quoted */ 478 #define XD 4 /* special character '.' - maybe quoted */ 479 #define EQ Q /* '=' must be quoted */ 480 #define TB SP /* treat '\t' as a space */ 481 #define NL N /* don't quote '\n' (NL) - XXX - quoting here breaks the line length algorithm */ 482 #define CR Q /* always quote a '\r' (CR) - it occurs only in a CRLF combo */ 483 484 static const signed char quotetab[] = { 485 Q, Q, Q, Q, Q, Q, Q, Q, Q,TB,NL, Q, Q,CR, Q, Q, 486 Q, Q, Q, Q, Q, Q, Q, Q, Q, Q, Q, Q, Q, Q, Q, Q, 487 SP, N, N, N, N, N, N, N, N, N, N, N, N, N,XD, N, 488 N, N, N, N, N, N, N, N, N, N, N, N, N,EQ, N, N, 489 490 N, N, N, N, N, N,XF, N, N, N, N, N, N, N, N, N, 491 N, N, N, N, N, N, N, N, N, N, N, N, N, N, N, N, 492 N, N, N, N, N, N, N, N, N, N, N, N, N, N, N, N, 493 N, N, N, N, N, N, N, N, N, N, N, N, N, N, N, Q, 494 }; 495 int flag = *p > 0x7f ? Q : quotetab[*p]; 496 497 if (flag == N) 498 return 0; 499 if (flag == Q) 500 return 1; 501 if (flag == SP) 502 return p + 1 < end && p[1] == '\n'; /* trailing white space */ 503 504 /* The remainder are special start-of-line cases. */ 505 if (l != 0) 506 return 0; 507 508 if (flag == XF) /* line may start with "From" */ 509 return p + 4 < end && p[1] == 'r' && p[2] == 'o' && p[3] == 'm'; 510 511 if (flag == XD) /* line may consist of a single dot */ 512 return p + 1 < end && p[1] == '\n'; 513 514 errx(EXIT_FAILURE, 515 "mustquote: invalid logic: *p=0x%x (%d) flag=%d, l=%zu\n", 516 *p, *p, flag, l); 517 /* NOT REACHED */ 518 return 0; /* appease GCC */ 519 520 #undef N 521 #undef Q 522 #undef SP 523 #undef XX 524 #undef EQ 525 #undef TB 526 #undef NL 527 #undef CR 528 } 529 530 531 #define MIME_QUOTED_LINE_MAX 76 /* QP max length: see RFC2045 sec 6.7 */ 532 533 static void 534 fput_quoted_line(FILE *fo, char *line, size_t len, size_t limit) 535 { 536 size_t l; /* length of current output line */ 537 unsigned char *beg; 538 unsigned char *end; 539 unsigned char *p; 540 541 assert(limit <= MIME_QUOTED_LINE_MAX); 542 543 beg = (unsigned char*)line; 544 end = beg + len; 545 l = 0; 546 for (p = (unsigned char*)line; p < end; p++) { 547 if (mustquote(p, end, l)) { 548 if (l + 4 > limit) { 549 (void)fputs("=\n", fo); 550 l = 0; 551 } 552 (void)fprintf(fo, "=%02X", *p); 553 l += 3; 554 } 555 else { 556 if (*p == '\n') { 557 if (p > beg && p[-1] == '\r') { 558 if (l + 4 > limit) 559 (void)fputs("=\n", fo); 560 (void)fputs("=0A=", fo); 561 } 562 l = (size_t)-1; 563 } 564 else if (l + 2 > limit) { 565 (void)fputs("=\n", fo); 566 l = 0; 567 } 568 (void)putc(*p, fo); 569 l++; 570 } 571 } 572 /* 573 * Lines ending in a blank must escape the newline. 574 */ 575 if (len && is_WSP(p[-1])) 576 (void)fputs("=\n", fo); 577 } 578 579 static void 580 mime_fQP_encode(FILE *fi, FILE *fo, void *cookie __unused) 581 { 582 char *line; 583 size_t len; 584 char *cp; 585 size_t limit; 586 587 #ifdef __lint__ 588 cookie = cookie; 589 #endif 590 limit = 0; 591 if ((cp = value(ENAME_MIME_QP_LINE_MAX)) != NULL) 592 limit = (size_t)atoi(cp); 593 if (limit == 0 || limit > MIME_QUOTED_LINE_MAX) 594 limit = MIME_QUOTED_LINE_MAX; 595 if (limit < 4) 596 limit = 4; 597 598 while ((line = fgetln(fi, &len)) != NULL) 599 fput_quoted_line(fo, line, len, limit); 600 } 601 602 static void 603 mime_fQP_decode(FILE *fi, FILE *fo, void *cookie __unused) 604 { 605 char *line; 606 size_t len; 607 608 #ifdef __lint__ 609 cookie = cookie; 610 #endif 611 while ((line = fgetln(fi, &len)) != NULL) { 612 char *p; 613 char *end; 614 615 end = line + len; 616 for (p = line; p < end; p++) { 617 if (*p == '=') { 618 p++; 619 while (p < end && is_WSP(*p)) 620 p++; 621 if (*p != '\n' && p + 1 < end) { 622 int c = _qp_cfromhex(p++); 623 if (c >= 0) 624 (void)fputc(c, fo); 625 else 626 (void)fputs("[?]", fo); 627 } 628 } 629 else 630 (void)fputc(*p, fo); 631 } 632 } 633 } 634 635 636 /************************************************************************ 637 * Routines to select the codec by name. 638 */ 639 640 PUBLIC void 641 mime_fio_copy(FILE *fi, FILE *fo, void *cookie __unused) 642 { 643 int c; 644 645 #ifdef __lint__ 646 cookie = cookie; 647 #endif 648 while ((c = getc(fi)) != EOF) 649 (void)putc(c, fo); 650 651 (void)fflush(fo); 652 if (ferror(fi)) { 653 warn("read"); 654 rewind(fi); 655 return; 656 } 657 if (ferror(fo)) { 658 warn("write"); 659 (void)Fclose(fo); 660 rewind(fi); 661 return; 662 } 663 } 664 665 666 static const struct transfer_encoding_s { 667 const char *name; 668 mime_codec_t enc; 669 mime_codec_t dec; 670 } transfer_encoding_tbl[] = { 671 { MIME_TRANSFER_7BIT, mime_fio_copy, mime_fio_copy }, 672 { MIME_TRANSFER_8BIT, mime_fio_copy, mime_fio_copy }, 673 { MIME_TRANSFER_BINARY, mime_fio_copy, mime_fio_copy }, 674 { MIME_TRANSFER_QUOTED, mime_fQP_encode, mime_fQP_decode }, 675 { MIME_TRANSFER_BASE64, mime_fB64_encode, mime_fB64_decode }, 676 { NULL, NULL, NULL }, 677 }; 678 679 680 PUBLIC mime_codec_t 681 mime_fio_encoder(const char *ename) 682 { 683 const struct transfer_encoding_s *tep = NULL; 684 685 if (ename == NULL) 686 return NULL; 687 688 for (tep = transfer_encoding_tbl; tep->name; tep++) 689 if (strcasecmp(tep->name, ename) == 0) 690 break; 691 return tep->enc; 692 } 693 694 PUBLIC mime_codec_t 695 mime_fio_decoder(const char *ename) 696 { 697 const struct transfer_encoding_s *tep = NULL; 698 699 if (ename == NULL) 700 return NULL; 701 702 for (tep = transfer_encoding_tbl; tep->name; tep++) 703 if (strcasecmp(tep->name, ename) == 0) 704 break; 705 return tep->dec; 706 } 707 708 /* 709 * Decode a RFC 2047 extended message header *encoded-word*. 710 * *encoding* is the corresponding character of the *encoded-word*. 711 */ 712 PUBLIC ssize_t 713 mime_rfc2047_decode(char encoding, char *outbuf, size_t outlen, 714 const char *inbuf, size_t inlen) 715 { 716 ssize_t declen = -1; 717 718 if (encoding == 'B' || encoding == 'b') { 719 if (outlen >= 3 * roundup(inlen, 4) / 4) 720 declen = mime_b64tobin(outbuf, inbuf, inlen); 721 } else if (encoding == 'Q' || encoding == 'q') 722 declen = mime_QPh_decode(outbuf, outlen, inbuf, inlen); 723 return declen; 724 } 725 726 /* 727 * This is for use in complete.c and mime.c to get the list of 728 * encoding names without exposing the transfer_encoding_tbl[]. The 729 * first name is returned if called with a pointer to a NULL pointer. 730 * Subsequent calls with the same cookie give successive names. A 731 * NULL return indicates the end of the list. 732 */ 733 PUBLIC const char * 734 mime_next_encoding_name(const void **cookie) 735 { 736 const struct transfer_encoding_s *tep; 737 738 tep = *cookie; 739 if (tep == NULL) 740 tep = transfer_encoding_tbl; 741 742 *cookie = tep->name ? &tep[1] : NULL; 743 744 return tep->name; 745 } 746 747 #endif /* MIME_SUPPORT */ 748