1 /* $NetBSD: mime_codecs.c,v 1.13 2025/07/09 16:59:54 rillig 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.13 2025/07/09 16:59:54 rillig 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 #undef N 518 #undef Q 519 #undef SP 520 #undef XX 521 #undef EQ 522 #undef TB 523 #undef NL 524 #undef CR 525 } 526 527 528 #define MIME_QUOTED_LINE_MAX 76 /* QP max length: see RFC2045 sec 6.7 */ 529 530 static void 531 fput_quoted_line(FILE *fo, char *line, size_t len, size_t limit) 532 { 533 size_t l; /* length of current output line */ 534 unsigned char *beg; 535 unsigned char *end; 536 unsigned char *p; 537 538 assert(limit <= MIME_QUOTED_LINE_MAX); 539 540 beg = (unsigned char*)line; 541 end = beg + len; 542 l = 0; 543 for (p = (unsigned char*)line; p < end; p++) { 544 if (mustquote(p, end, l)) { 545 if (l + 4 > limit) { 546 (void)fputs("=\n", fo); 547 l = 0; 548 } 549 (void)fprintf(fo, "=%02X", *p); 550 l += 3; 551 } 552 else { 553 if (*p == '\n') { 554 if (p > beg && p[-1] == '\r') { 555 if (l + 4 > limit) 556 (void)fputs("=\n", fo); 557 (void)fputs("=0A=", fo); 558 } 559 l = (size_t)-1; 560 } 561 else if (l + 2 > limit) { 562 (void)fputs("=\n", fo); 563 l = 0; 564 } 565 (void)putc(*p, fo); 566 l++; 567 } 568 } 569 /* 570 * Lines ending in a blank must escape the newline. 571 */ 572 if (len && is_WSP(p[-1])) 573 (void)fputs("=\n", fo); 574 } 575 576 static void 577 mime_fQP_encode(FILE *fi, FILE *fo, void *cookie __unused) 578 { 579 char *line; 580 size_t len; 581 char *cp; 582 size_t limit; 583 584 #ifdef __lint__ 585 cookie = cookie; 586 #endif 587 limit = 0; 588 if ((cp = value(ENAME_MIME_QP_LINE_MAX)) != NULL) 589 limit = (size_t)atoi(cp); 590 if (limit == 0 || limit > MIME_QUOTED_LINE_MAX) 591 limit = MIME_QUOTED_LINE_MAX; 592 if (limit < 4) 593 limit = 4; 594 595 while ((line = fgetln(fi, &len)) != NULL) 596 fput_quoted_line(fo, line, len, limit); 597 } 598 599 static void 600 mime_fQP_decode(FILE *fi, FILE *fo, void *cookie __unused) 601 { 602 char *line; 603 size_t len; 604 605 #ifdef __lint__ 606 cookie = cookie; 607 #endif 608 while ((line = fgetln(fi, &len)) != NULL) { 609 char *p; 610 char *end; 611 612 end = line + len; 613 for (p = line; p < end; p++) { 614 if (*p == '=') { 615 p++; 616 while (p < end && is_WSP(*p)) 617 p++; 618 if (*p != '\n' && p + 1 < end) { 619 int c = _qp_cfromhex(p++); 620 if (c >= 0) 621 (void)fputc(c, fo); 622 else 623 (void)fputs("[?]", fo); 624 } 625 } 626 else 627 (void)fputc(*p, fo); 628 } 629 } 630 } 631 632 633 /************************************************************************ 634 * Routines to select the codec by name. 635 */ 636 637 PUBLIC void 638 mime_fio_copy(FILE *fi, FILE *fo, void *cookie __unused) 639 { 640 int c; 641 642 #ifdef __lint__ 643 cookie = cookie; 644 #endif 645 while ((c = getc(fi)) != EOF) 646 (void)putc(c, fo); 647 648 (void)fflush(fo); 649 if (ferror(fi)) { 650 warn("read"); 651 rewind(fi); 652 return; 653 } 654 if (ferror(fo)) { 655 warn("write"); 656 (void)Fclose(fo); 657 rewind(fi); 658 return; 659 } 660 } 661 662 663 static const struct transfer_encoding_s { 664 const char *name; 665 mime_codec_t enc; 666 mime_codec_t dec; 667 } transfer_encoding_tbl[] = { 668 { MIME_TRANSFER_7BIT, mime_fio_copy, mime_fio_copy }, 669 { MIME_TRANSFER_8BIT, mime_fio_copy, mime_fio_copy }, 670 { MIME_TRANSFER_BINARY, mime_fio_copy, mime_fio_copy }, 671 { MIME_TRANSFER_QUOTED, mime_fQP_encode, mime_fQP_decode }, 672 { MIME_TRANSFER_BASE64, mime_fB64_encode, mime_fB64_decode }, 673 { NULL, NULL, NULL }, 674 }; 675 676 677 PUBLIC mime_codec_t 678 mime_fio_encoder(const char *ename) 679 { 680 const struct transfer_encoding_s *tep = NULL; 681 682 if (ename == NULL) 683 return NULL; 684 685 for (tep = transfer_encoding_tbl; tep->name; tep++) 686 if (strcasecmp(tep->name, ename) == 0) 687 break; 688 return tep->enc; 689 } 690 691 PUBLIC mime_codec_t 692 mime_fio_decoder(const char *ename) 693 { 694 const struct transfer_encoding_s *tep = NULL; 695 696 if (ename == NULL) 697 return NULL; 698 699 for (tep = transfer_encoding_tbl; tep->name; tep++) 700 if (strcasecmp(tep->name, ename) == 0) 701 break; 702 return tep->dec; 703 } 704 705 /* 706 * Decode a RFC 2047 extended message header *encoded-word*. 707 * *encoding* is the corresponding character of the *encoded-word*. 708 */ 709 PUBLIC ssize_t 710 mime_rfc2047_decode(char encoding, char *outbuf, size_t outlen, 711 const char *inbuf, size_t inlen) 712 { 713 ssize_t declen = -1; 714 715 if (encoding == 'B' || encoding == 'b') { 716 if (outlen >= 3 * roundup(inlen, 4) / 4) 717 declen = mime_b64tobin(outbuf, inbuf, inlen); 718 } else if (encoding == 'Q' || encoding == 'q') 719 declen = mime_QPh_decode(outbuf, outlen, inbuf, inlen); 720 return declen; 721 } 722 723 /* 724 * This is for use in complete.c and mime.c to get the list of 725 * encoding names without exposing the transfer_encoding_tbl[]. The 726 * first name is returned if called with a pointer to a NULL pointer. 727 * Subsequent calls with the same cookie give successive names. A 728 * NULL return indicates the end of the list. 729 */ 730 PUBLIC const char * 731 mime_next_encoding_name(const void **cookie) 732 { 733 const struct transfer_encoding_s *tep; 734 735 tep = *cookie; 736 if (tep == NULL) 737 tep = transfer_encoding_tbl; 738 739 *cookie = tep->name ? &tep[1] : NULL; 740 741 return tep->name; 742 } 743 744 #endif /* MIME_SUPPORT */ 745