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
mime_iconv(iconv_t cd,const char ** inb,size_t * inbleft,char ** outb,size_t * outbleft)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
mime_ficonv(FILE * fi,FILE * fo,void * cookie)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
mime_b64tobin(char * bin,const char * b64,size_t cnt)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
mime_bintob64(char * b64,const char * bin,size_t cnt)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
mime_fB64_encode(FILE * fi,FILE * fo,void * cookie __unused)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
mime_fB64_decode(FILE * fi,FILE * fo,void * add_lf)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
_qp_cfromhex(char const * hex)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
mime_QPh_decode(char * outbuf,size_t outlen,const char * inbuf,size_t inlen)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
mustquote(unsigned char * p,unsigned char * end,size_t l)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
fput_quoted_line(FILE * fo,char * line,size_t len,size_t limit)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
mime_fQP_encode(FILE * fi,FILE * fo,void * cookie __unused)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
mime_fQP_decode(FILE * fi,FILE * fo,void * cookie __unused)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
mime_fio_copy(FILE * fi,FILE * fo,void * cookie __unused)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
mime_fio_encoder(const char * ename)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
mime_fio_decoder(const char * ename)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
mime_rfc2047_decode(char encoding,char * outbuf,size_t outlen,const char * inbuf,size_t inlen)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 *
mime_next_encoding_name(const void ** cookie)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