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