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