Review: imap_search.c
[s-mailx.git] / mime.c
blob91ef12754e8d29f063fc19eea8baba37bf202ffd
1 /*@ S-nail - a mail user agent derived from Berkeley Mail.
2 *@ MIME support functions.
4 * Copyright (c) 2000-2004 Gunnar Ritter, Freiburg i. Br., Germany.
5 * Copyright (c) 2012 - 2014 Steffen (Daode) Nurpmeso <sdaoden@users.sf.net>.
6 */
7 /*
8 * Copyright (c) 2000
9 * Gunnar Ritter. All rights reserved.
11 * Redistribution and use in source and binary forms, with or without
12 * modification, are permitted provided that the following conditions
13 * are met:
14 * 1. Redistributions of source code must retain the above copyright
15 * notice, this list of conditions and the following disclaimer.
16 * 2. Redistributions in binary form must reproduce the above copyright
17 * notice, this list of conditions and the following disclaimer in the
18 * documentation and/or other materials provided with the distribution.
19 * 3. All advertising materials mentioning features or use of this software
20 * must display the following acknowledgement:
21 * This product includes software developed by Gunnar Ritter
22 * and his contributors.
23 * 4. Neither the name of Gunnar Ritter nor the names of his contributors
24 * may be used to endorse or promote products derived from this software
25 * without specific prior written permission.
27 * THIS SOFTWARE IS PROVIDED BY GUNNAR RITTER AND CONTRIBUTORS ``AS IS'' AND
28 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
29 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
30 * ARE DISCLAIMED. IN NO EVENT SHALL GUNNAR RITTER OR CONTRIBUTORS BE LIABLE
31 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
32 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
33 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
34 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
35 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
36 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
37 * SUCH DAMAGE.
40 #ifndef HAVE_AMALGAMATION
41 # include "nail.h"
42 #endif
44 #define _CHARSET() ((_cs_iter != NULL) ? _cs_iter : charset_get_8bit())
46 struct mtnode {
47 struct mtnode *mt_next;
48 size_t mt_mtlen; /* Length of MIME type string */
49 char mt_line[VFIELD_SIZE(8)];
52 static char const * const _mt_sources[] = {
53 /* XXX Order fixed due to *mimetypes-load-control* handling! */
54 MIME_TYPES_USR, MIME_TYPES_SYS, NULL
56 * const _mt_bltin[] = {
57 #include "mime_types.h"
58 NULL
61 struct mtnode *_mt_list;
62 char *_cs_iter_base, *_cs_iter;
64 /* Initialize MIME type list */
65 static void _mt_init(void);
66 static void __mt_add_line(char const *line, struct mtnode **tail);
68 /* Is 7-bit enough? */
69 #ifdef HAVE_ICONV
70 static bool_t _has_highbit(char const *s);
71 static bool_t _name_highbit(struct name *np);
72 #endif
74 /* Get the conversion that matches *encoding* */
75 static enum conversion _conversion_by_encoding(void);
77 /* fwrite(3) while checking for displayability */
78 static ssize_t _fwrite_td(struct str const *input, enum tdflags flags,
79 struct str *rest, struct quoteflt *qf);
81 static size_t delctrl(char *cp, size_t sz);
83 static int is_this_enc(char const *line, char const *encoding);
85 /* Convert header fields to RFC 1522 format and write to the file fo */
86 static size_t mime_write_tohdr(struct str *in, FILE *fo);
88 /* Write len characters of the passed string to the passed file, doing charset
89 * and header conversion */
90 static size_t convhdra(char const *str, size_t len, FILE *fp);
92 /* Write an address to a header field */
93 static size_t mime_write_tohdr_a(struct str *in, FILE *f);
95 /* Append to buf, handling resizing */
96 static void addstr(char **buf, size_t *sz, size_t *pos,
97 char const *str, size_t len);
99 static void addconv(char **buf, size_t *sz, size_t *pos,
100 char const *str, size_t len);
102 static void
103 _mt_init(void)
105 struct mtnode *tail = NULL;
106 char *line = NULL;
107 size_t linesize = 0;
108 ui32_t idx, idx_ok;
109 char const *ccp, * const *srcs;
110 FILE *fp;
111 NYD_ENTER;
113 if ((ccp = ok_vlook(mimetypes_load_control)) == NULL)
114 idx_ok = (ui32_t)-1;
115 else for (idx_ok = 0; *ccp != '\0'; ++ccp)
116 switch (*ccp) {
117 case 'S':
118 case 's':
119 idx_ok |= 1 << 1;
120 break;
121 case 'U':
122 case 'u':
123 idx_ok |= 1 << 0;
124 break;
125 default:
126 /* XXX bad *mimetypes-load-control*; log error? */
127 break;
130 for (idx = 1, srcs = _mt_sources; *srcs != NULL; idx <<= 1, ++srcs) {
131 if ((idx & idx_ok) == 0 || (ccp = file_expand(*srcs)) == NULL)
132 continue;
133 if ((fp = Fopen(ccp, "r")) == NULL) {
134 /*fprintf(stderr, tr(176, "Cannot open %s\n"), fn);*/
135 continue;
137 while (fgetline(&line, &linesize, NULL, NULL, fp, 0))
138 __mt_add_line(line, &tail);
139 Fclose(fp);
141 if (line != NULL)
142 free(line);
144 for (srcs = _mt_bltin; *srcs != NULL; ++srcs)
145 __mt_add_line(*srcs, &tail);
146 NYD_LEAVE;
149 static void
150 __mt_add_line(char const *line, struct mtnode **tail) /* XXX diag? dups!*/
152 char const *typ;
153 size_t tlen, elen;
154 struct mtnode *mtn;
155 NYD_ENTER;
157 if (!alphachar(*line))
158 goto jleave;
160 typ = line;
161 while (blankchar(*line) == 0 && *line != '\0')
162 ++line;
163 if (*line == '\0')
164 goto jleave;
165 tlen = PTR2SIZE(line - typ);
167 while (blankchar(*line) != 0 && *line != '\0')
168 ++line;
169 if (*line == '\0')
170 goto jleave;
172 elen = strlen(line);
173 if (line[elen - 1] == '\n' && line[--elen] == '\0')
174 goto jleave;
176 mtn = smalloc(sizeof(struct mtnode) -
177 VFIELD_SIZEOF(struct mtnode, mt_line) + tlen + 1 + elen +1);
178 if (*tail != NULL)
179 (*tail)->mt_next = mtn;
180 else
181 _mt_list = mtn;
182 *tail = mtn;
183 mtn->mt_next = NULL;
184 mtn->mt_mtlen = tlen;
185 memcpy(mtn->mt_line, typ, tlen);
186 mtn->mt_line[tlen] = '\0';
187 ++tlen;
188 memcpy(mtn->mt_line + tlen, line, elen);
189 tlen += elen;
190 mtn->mt_line[tlen] = '\0';
191 jleave:
192 NYD_LEAVE;
195 #ifdef HAVE_ICONV
196 static bool_t
197 _has_highbit(char const *s)
199 bool_t rv = TRU1;
200 NYD_ENTER;
202 if (s) {
204 if (*s & 0200)
205 goto jleave;
206 while (*s++ != '\0');
208 rv = FAL0;
209 jleave:
210 NYD_LEAVE;
211 return rv;
214 static bool_t
215 _name_highbit(struct name *np)
217 bool_t rv = TRU1;
218 NYD_ENTER;
220 while (np) {
221 if (_has_highbit(np->n_name) || _has_highbit(np->n_fullname))
222 goto jleave;
223 np = np->n_flink;
225 rv = FAL0;
226 jleave:
227 NYD_LEAVE;
228 return rv;
230 #endif
232 static enum conversion
233 _conversion_by_encoding(void)
235 char const *cp;
236 enum conversion ret;
237 NYD_ENTER;
239 if ((cp = ok_vlook(encoding)) == NULL)
240 ret = MIME_DEFAULT_ENCODING;
241 else if (!strcmp(cp, "quoted-printable"))
242 ret = CONV_TOQP;
243 else if (!strcmp(cp, "8bit"))
244 ret = CONV_8BIT;
245 else if (!strcmp(cp, "base64"))
246 ret = CONV_TOB64;
247 else {
248 fprintf(stderr, tr(177, "Warning: invalid encoding %s, using base64\n"),
249 cp);
250 ret = CONV_TOB64;
252 NYD_LEAVE;
253 return ret;
256 static ssize_t
257 _fwrite_td(struct str const *input, enum tdflags flags, struct str *rest,
258 struct quoteflt *qf)
260 /* TODO note: after send/MIME layer rewrite we will have a string pool
261 * TODO so that memory allocation count drops down massively; for now,
262 * TODO v14.* that is, we pay a lot & heavily depend on the allocator */
263 /* TODO well if we get a broken pipe here, and it happens to
264 * TODO happen pretty easy when sleeping in a full pipe buffer,
265 * TODO then the current codebase performs longjump away;
266 * TODO this leaves memory leaks behind ('think up to 3 per,
267 * TODO dep. upon alloca availability). For this to be fixed
268 * TODO we either need to get rid of the longjmp()s (tm) or
269 * TODO the storage must come from the outside or be tracked
270 * TODO in a carrier struct. Best both. But storage reuse
271 * TODO would be a bigbig win besides */
272 /* *input* _may_ point to non-modifyable buffer; but even then it only
273 * needs to be dup'ed away if we have to transform the content */
274 struct str in, out;
275 ssize_t rv;
276 NYD_ENTER;
277 UNUSED(rest);
279 in = *input;
280 out.s = NULL;
281 out.l = 0;
283 #ifdef HAVE_ICONV
284 if ((flags & TD_ICONV) && iconvd != (iconv_t)-1) {
285 char *buf = NULL;
287 if (rest != NULL && rest->l > 0) {
288 in.l = rest->l + input->l;
289 in.s = buf = smalloc(in.l + 1);
290 memcpy(in.s, rest->s, rest->l);
291 memcpy(in.s + rest->l, input->s, input->l);
292 rest->l = 0;
295 if (n_iconv_str(iconvd, &out, &in, &in, TRU1) != 0 && rest != NULL &&
296 in.l > 0) {
297 /* Incomplete multibyte at EOF is special */
298 if (flags & _TD_EOF) {
299 out.s = srealloc(out.s, out.l + 4);
300 /* TODO 0xFFFD out.s[out.l++] = '[';*/
301 out.s[out.l++] = '?'; /* TODO 0xFFFD !!! */
302 /* TODO 0xFFFD out.s[out.l++] = ']';*/
303 } else
304 n_str_add(rest, &in);
306 in = out;
307 out.s = NULL;
308 flags &= ~_TD_BUFCOPY;
310 if (buf != NULL)
311 free(buf);
313 #endif
315 if (flags & TD_ISPR)
316 makeprint(&in, &out);
317 else if (flags & _TD_BUFCOPY)
318 n_str_dup(&out, &in);
319 else
320 out = in;
321 if (flags & TD_DELCTRL)
322 out.l = delctrl(out.s, out.l);
324 rv = quoteflt_push(qf, out.s, out.l);
326 if (out.s != in.s)
327 free(out.s);
328 if (in.s != input->s)
329 free(in.s);
330 NYD_LEAVE;
331 return rv;
334 static size_t
335 delctrl(char *cp, size_t sz)
337 size_t x = 0, y = 0;
338 NYD_ENTER;
340 while (x < sz) {
341 if (!cntrlchar(cp[x]))
342 cp[y++] = cp[x];
343 ++x;
345 NYD_LEAVE;
346 return y;
349 static int
350 is_this_enc(char const *line, char const *encoding)
352 int rv, c, quoted = 0;
353 NYD_ENTER;
355 if (*line == '"')
356 quoted = 1, line++;
357 rv = 0;
358 while (*line && *encoding)
359 if ((c = *line++, lowerconv(c) != *encoding++))
360 goto jleave;
361 rv = 1;
362 if (quoted && *line == '"')
363 goto jleave;
364 if (*line == '\0' || whitechar(*line))
365 goto jleave;
366 rv = 0;
367 jleave:
368 NYD_LEAVE;
369 return rv;
372 static size_t
373 mime_write_tohdr(struct str *in, FILE *fo) /* TODO rewrite - FAST! */
375 struct str cin, cout;
376 char buf[B64_LINESIZE +1]; /* (No CR/LF used) */
377 char const *charset7, *charset, *upper, *wbeg, *wend, *lastspc,
378 *lastwordend = NULL;
379 size_t sz = 0, col = 0, quoteany, wr, charsetlen,
380 maxcol = 65 /* there is the header field's name, too */;
381 bool_t highbit, mustquote, broken;
382 NYD_ENTER;
384 charset7 = charset_get_7bit();
385 charset = _CHARSET();
386 wr = strlen(charset7);
387 charsetlen = strlen(charset);
388 charsetlen = MAX(charsetlen, wr);
389 upper = in->s + in->l;
391 /* xxx note this results in too much hits since =/? force quoting even
392 * xxx if they don't form =? etc. */
393 quoteany = mime_cte_mustquote(in->s, in->l, TRU1);
395 highbit = FAL0;
396 if (quoteany != 0)
397 for (wbeg = in->s; wbeg < upper; ++wbeg)
398 if ((ui8_t)*wbeg & 0x80)
399 highbit = TRU1;
401 if (quoteany << 1 > in->l) {
402 /* Print the entire field in base64 */
403 for (wbeg = in->s; wbeg < upper; wbeg = wend) {
404 wend = upper;
405 cin.s = UNCONST(wbeg);
406 for (;;) {
407 cin.l = wend - wbeg;
408 if (cin.l * 4/3 + 7 + charsetlen < maxcol - col) {
409 cout.s = buf;
410 cout.l = sizeof buf;
411 wr = fprintf(fo, "=?%s?B?%s?=", (highbit ? charset : charset7),
412 b64_encode(&cout, &cin, B64_BUF)->s);
413 sz += wr;
414 col += wr;
415 if (wend < upper) {
416 fwrite("\n ", sizeof(char), 2, fo);
417 sz += 2;
418 col = 0;
419 maxcol = 76;
421 break;
422 } else {
423 if (col) {
424 fprintf(fo, "\n ");
425 sz += 2;
426 col = 0;
427 maxcol = 76;
428 } else
429 wend -= 4;
433 } else {
434 /* Print the field word-wise in quoted-printable */
435 broken = FAL0;
436 for (wbeg = in->s; wbeg < upper; wbeg = wend) {
437 lastspc = NULL;
438 while (wbeg < upper && whitechar(*wbeg)) {
439 lastspc = lastspc ? lastspc : wbeg;
440 ++wbeg;
441 ++col;
442 broken = FAL0;
444 if (wbeg == upper) {
445 if (lastspc)
446 while (lastspc < wbeg) {
447 putc(*lastspc&0377, fo);
448 ++lastspc;
449 ++sz;
451 break;
454 if (lastspc != NULL)
455 broken = FAL0;
456 highbit = FAL0;
457 for (wend = wbeg; wend < upper && !whitechar(*wend); ++wend)
458 if ((ui8_t)*wend & 0x80)
459 highbit = TRU1;
460 mustquote = (mime_cte_mustquote(wbeg, PTR2SIZE(wend - wbeg), TRU1)
461 != 0);
463 if (mustquote || broken || ((wend - wbeg) >= 76-5 && quoteany)) {
464 for (cout.s = NULL;;) {
465 cin.s = UNCONST(lastwordend ? lastwordend : wbeg);
466 cin.l = wend - cin.s;
467 qp_encode(&cout, &cin, QP_ISHEAD);
468 wr = cout.l + charsetlen + 7;
469 jqp_retest:
470 if (col <= maxcol && wr <= maxcol - col) {
471 if (lastspc) {
472 /* TODO because we included the WS in the encoded str,
473 * TODO put SP only??
474 * TODO RFC: "any 'linear-white-space' that separates
475 * TODO a pair of adjacent 'encoded-word's is ignored" */
476 putc(' ', fo);
477 ++sz;
478 ++col;
480 fprintf(fo, "=?%s?Q?%.*s?=",
481 (highbit ? charset : charset7), (int)cout.l, cout.s);
482 sz += wr;
483 col += wr;
484 break;
485 } else if (col > 1) {
486 /* TODO assuming SP separator, ignore *lastspc* !?? */
487 broken = TRU1;
488 if (lastspc != NULL) {
489 putc('\n', fo);
490 ++sz;
491 col = 0;
492 } else {
493 fputs("\n ", fo);
494 sz += 2;
495 col = 1;
497 maxcol = 76;
498 goto jqp_retest;
499 } else {
500 for (;;) { /* XXX */
501 wend -= 4;
502 assert(wend > wbeg);
503 if (wr - 4 < maxcol)
504 break;
505 wr -= 4;
509 if (cout.s != NULL)
510 free(cout.s);
511 lastwordend = wend;
512 } else {
513 if (col && PTR2SIZE(wend - wbeg) > maxcol - col) {
514 putc('\n', fo);
515 ++sz;
516 col = 0;
517 maxcol = 76;
518 if (lastspc == NULL) {
519 putc(' ', fo);
520 ++sz;
521 --maxcol;
522 } else
523 maxcol -= wbeg - lastspc;
525 if (lastspc)
526 while (lastspc < wbeg) {
527 putc(*lastspc&0377, fo);
528 ++lastspc;
529 ++sz;
531 wr = fwrite(wbeg, sizeof *wbeg, PTR2SIZE(wend - wbeg), fo);
532 sz += wr;
533 col += wr;
534 lastwordend = NULL;
538 NYD_LEAVE;
539 return sz;
542 static size_t
543 convhdra(char const *str, size_t len, FILE *fp)
545 #ifdef HAVE_ICONV
546 struct str ciconv;
547 #endif
548 struct str cin;
549 size_t ret = 0;
550 NYD_ENTER;
552 cin.s = UNCONST(str);
553 cin.l = len;
554 #ifdef HAVE_ICONV
555 ciconv.s = NULL;
556 if (iconvd != (iconv_t)-1) {
557 ciconv.l = 0;
558 if (n_iconv_str(iconvd, &ciconv, &cin, NULL, FAL0) != 0)
559 goto jleave;
560 cin = ciconv;
562 #endif
563 ret = mime_write_tohdr(&cin, fp);
564 #ifdef HAVE_ICONV
565 jleave:
566 if (ciconv.s != NULL)
567 free(ciconv.s);
568 #endif
569 NYD_LEAVE;
570 return ret;
573 static size_t
574 mime_write_tohdr_a(struct str *in, FILE *f)
576 char const *cp, *lastcp;
577 size_t sz = 0;
578 NYD_ENTER;
580 in->s[in->l] = '\0';
581 lastcp = in->s;
582 if ((cp = routeaddr(in->s)) != NULL && cp > lastcp) {
583 sz += convhdra(lastcp, cp - lastcp, f);
584 lastcp = cp;
585 } else
586 cp = in->s;
587 for ( ; *cp != '\0'; ++cp) {
588 switch (*cp) {
589 case '(':
590 sz += fwrite(lastcp, 1, PTR2SIZE(cp - lastcp + 1), f);
591 lastcp = ++cp;
592 cp = skip_comment(cp);
593 if (--cp > lastcp)
594 sz += convhdra(lastcp, PTR2SIZE(cp - lastcp), f);
595 lastcp = cp;
596 break;
597 case '"':
598 while (*cp) {
599 if (*++cp == '"')
600 break;
601 if (*cp == '\\' && cp[1] != '\0')
602 cp++;
604 break;
607 if (cp > lastcp)
608 sz += fwrite(lastcp, 1, PTR2SIZE(cp - lastcp), f);
609 NYD_LEAVE;
610 return sz;
613 static void
614 addstr(char **buf, size_t *sz, size_t *pos, char const *str, size_t len)
616 NYD_ENTER;
617 *buf = srealloc(*buf, *sz += len);
618 memcpy(&(*buf)[*pos], str, len);
619 *pos += len;
620 NYD_LEAVE;
623 static void
624 addconv(char **buf, size_t *sz, size_t *pos, char const *str, size_t len)
626 struct str in, out;
627 NYD_ENTER;
629 in.s = UNCONST(str);
630 in.l = len;
631 mime_fromhdr(&in, &out, TD_ISPR | TD_ICONV);
632 addstr(buf, sz, pos, out.s, out.l);
633 free(out.s);
634 NYD_LEAVE;
637 FL char const *
638 charset_get_7bit(void)
640 char const *t;
641 NYD_ENTER;
643 if ((t = ok_vlook(charset_7bit)) == NULL)
644 t = CHARSET_7BIT;
645 NYD_LEAVE;
646 return t;
649 FL char const *
650 charset_get_8bit(void)
652 char const *t;
653 NYD_ENTER;
655 if ((t = ok_vlook(CHARSET_8BIT_OKEY)) == NULL)
656 t = CHARSET_8BIT;
657 NYD_LEAVE;
658 return t;
661 FL char const *
662 charset_get_lc(void)
664 char const *t;
665 NYD_ENTER;
667 if ((t = ok_vlook(ttycharset)) == NULL)
668 t = CHARSET_8BIT;
669 NYD_LEAVE;
670 return t;
673 FL void
674 charset_iter_reset(char const *a_charset_to_try_first)
676 char const *sarr[3];
677 size_t sarrl[3], len;
678 char *cp;
679 NYD_ENTER;
681 sarr[0] = a_charset_to_try_first;
682 #ifdef HAVE_ICONV
683 if ((sarr[1] = ok_vlook(sendcharsets)) == NULL &&
684 ok_blook(sendcharsets_else_ttycharset))
685 sarr[1] = charset_get_lc();
686 #endif
687 sarr[2] = charset_get_8bit();
689 sarrl[2] = len = strlen(sarr[2]);
690 #ifdef HAVE_ICONV
691 if ((cp = UNCONST(sarr[1])) != NULL)
692 len += (sarrl[1] = strlen(cp));
693 else
694 sarrl[1] = 0;
695 if ((cp = UNCONST(sarr[0])) != NULL)
696 len += (sarrl[0] = strlen(cp));
697 else
698 sarrl[0] = 0;
699 #endif
701 _cs_iter_base = cp = salloc(len + 1 + 1 +1);
703 #ifdef HAVE_ICONV
704 if ((len = sarrl[0]) != 0) {
705 memcpy(cp, sarr[0], len);
706 cp[len] = ',';
707 cp += ++len;
709 if ((len = sarrl[1]) != 0) {
710 memcpy(cp, sarr[1], len);
711 cp[len] = ',';
712 cp += ++len;
714 #endif
715 len = sarrl[2];
716 memcpy(cp, sarr[2], len);
717 cp[len] = '\0';
718 _cs_iter = NULL;
719 NYD_LEAVE;
722 FL char const *
723 charset_iter_next(void)
725 char const *rv;
726 NYD_ENTER;
728 rv = _cs_iter = n_strsep(&_cs_iter_base, ',', TRU1);
729 NYD_LEAVE;
730 return rv;
733 FL char const *
734 charset_iter_current(void)
736 char const *rv;
737 NYD_ENTER;
739 rv = _cs_iter;
740 NYD_LEAVE;
741 return rv;
744 FL void
745 charset_iter_recurse(char *outer_storage[2]) /* TODO LEGACY FUN, REMOVE */
747 NYD_ENTER;
748 outer_storage[0] = _cs_iter_base;
749 outer_storage[1] = _cs_iter;
750 NYD_LEAVE;
753 FL void
754 charset_iter_restore(char *outer_storage[2]) /* TODO LEGACY FUN, REMOVE */
756 NYD_ENTER;
757 _cs_iter_base = outer_storage[0];
758 _cs_iter = outer_storage[1];
759 NYD_LEAVE;
762 #ifdef HAVE_ICONV
763 FL char const *
764 need_hdrconv(struct header *hp, enum gfield w)
766 char const *ret = NULL;
767 NYD_ENTER;
769 if (w & GIDENT) {
770 if (hp->h_from != NULL) {
771 if (_name_highbit(hp->h_from))
772 goto jneeds;
773 } else if (_has_highbit(myaddrs(NULL)))
774 goto jneeds;
775 if (hp->h_organization) {
776 if (_has_highbit(hp->h_organization))
777 goto jneeds;
778 } else if (_has_highbit(ok_vlook(ORGANIZATION)))
779 goto jneeds;
780 if (hp->h_replyto) {
781 if (_name_highbit(hp->h_replyto))
782 goto jneeds;
783 } else if (_has_highbit(ok_vlook(replyto)))
784 goto jneeds;
785 if (hp->h_sender) {
786 if (_name_highbit(hp->h_sender))
787 goto jneeds;
788 } else if (_has_highbit(ok_vlook(sender)))
789 goto jneeds;
791 if (w & GTO && _name_highbit(hp->h_to))
792 goto jneeds;
793 if (w & GCC && _name_highbit(hp->h_cc))
794 goto jneeds;
795 if (w & GBCC && _name_highbit(hp->h_bcc))
796 goto jneeds;
797 if (w & GSUBJECT && _has_highbit(hp->h_subject))
798 jneeds:
799 ret = _CHARSET();
800 NYD_LEAVE;
801 return ret;
803 #endif /* HAVE_ICONV */
805 FL enum mimeenc
806 mime_getenc(char *p)
808 enum mimeenc rv;
809 NYD_ENTER;
811 if (is_this_enc(p, "7bit"))
812 rv = MIME_7B;
813 else if (is_this_enc(p, "8bit"))
814 rv = MIME_8B;
815 else if (is_this_enc(p, "base64"))
816 rv = MIME_B64;
817 else if (is_this_enc(p, "binary"))
818 rv = MIME_BIN;
819 else if (is_this_enc(p, "quoted-printable"))
820 rv = MIME_QP;
821 else
822 rv = MIME_NONE;
823 NYD_LEAVE;
824 return rv;
827 FL char *
828 mime_getparam(char const *param, char *h)
830 char *p = h, *q, *rv = NULL;
831 int c;
832 size_t sz;
833 NYD_ENTER;
835 sz = strlen(param);
836 if (!whitechar(*p)) {
837 c = '\0';
838 while (*p && (*p != ';' || c == '\\')) {
839 c = (c == '\\') ? '\0' : *p;
840 ++p;
842 if (*p++ == '\0')
843 goto jleave;
846 for (;;) {
847 while (whitechar(*p))
848 ++p;
849 if (!ascncasecmp(p, param, sz)) {
850 p += sz;
851 while (whitechar(*p))
852 ++p;
853 if (*p++ == '=')
854 break;
856 c = '\0';
857 while (*p && (*p != ';' || c == '\\')) {
858 if (*p == '"' && c != '\\') {
859 ++p;
860 while (*p && (*p != '"' || c == '\\')) {
861 c = (c == '\\') ? '\0' : *p;
862 ++p;
864 ++p;
865 } else {
866 c = (c == '\\') ? '\0' : *p;
867 ++p;
870 if (*p++ == '\0')
871 goto jleave;
873 while (whitechar(*p))
874 ++p;
876 q = p;
877 if (*p == '"') {
878 p++;
879 if ((q = strchr(p, '"')) == NULL)
880 goto jleave;
881 } else {
882 while (*q && !whitechar(*q) && *q != ';')
883 ++q;
885 sz = q - p;
886 rv = salloc(q - p +1);
887 memcpy(rv, p, sz);
888 rv[sz] = '\0';
889 jleave:
890 NYD_LEAVE;
891 return rv;
894 FL char *
895 mime_get_boundary(char *h, size_t *len)
897 char *q = NULL, *p;
898 size_t sz;
899 NYD_ENTER;
901 if ((p = mime_getparam("boundary", h)) != NULL) {
902 sz = strlen(p);
903 if (len != NULL)
904 *len = sz + 2;
905 q = salloc(sz + 2 +1);
906 q[0] = q[1] = '-';
907 memcpy(q + 2, p, sz);
908 *(q + sz + 2) = '\0';
910 NYD_LEAVE;
911 return q;
914 FL char *
915 mime_create_boundary(void)
917 char *bp;
918 NYD_ENTER;
920 bp = salloc(48);
921 snprintf(bp, 48, "=_%011lu=-%s=_", (ul_it)time_current.tc_time,
922 getrandstring(47 - (11 + 6)));
923 NYD_LEAVE;
924 return bp;
927 FL int
928 mime_classify_file(FILE *fp, char const **contenttype, char const **charset,
929 int *do_iconv)
931 /* TODO classify once only PLEASE PLEASE PLEASE */
932 /* TODO BTW., after the MIME/send layer rewrite we could use a MIME
933 * TODO boundary of "=-=-=" if we would add a B_ in EQ spirit to F_,
934 * TODO and report that state to the outer world */
935 #define F_ "From "
936 #define F_SIZEOF (sizeof(F_) - 1)
938 char f_buf[F_SIZEOF], *f_p = f_buf;
939 enum {
940 _CLEAN = 0, /* Plain RFC 2822 message */
941 _NCTT = 1<<0, /* *contenttype == NULL */
942 _ISTXT = 1<<1, /* *contenttype =~ text/ */
943 _ISTXTCOK = 1<<2, /* _ISTXT+*mime-allow-text-controls* */
944 _HIGHBIT = 1<<3, /* Not 7bit clean */
945 _LONGLINES = 1<<4, /* MIME_LINELEN_LIMIT exceed. */
946 _CTRLCHAR = 1<<5, /* Control characters seen */
947 _HASNUL = 1<<6, /* Contains \0 characters */
948 _NOTERMNL = 1<<7, /* Lacks a final newline */
949 _TRAILWS = 1<<8, /* Blanks before NL */
950 _FROM_ = 1<<9 /* ^From_ seen */
951 } ctt = _CLEAN;
952 enum conversion convert;
953 sl_it curlen;
954 int c, lastc;
955 NYD_ENTER;
957 assert(ftell(fp) == 0x0l);
959 *do_iconv = 0;
961 if (*contenttype == NULL)
962 ctt = _NCTT;
963 else if (!ascncasecmp(*contenttype, "text/", 5))
964 ctt = ok_blook(mime_allow_text_controls) ? _ISTXT | _ISTXTCOK : _ISTXT;
965 convert = _conversion_by_encoding();
967 if (fsize(fp) == 0)
968 goto j7bit;
970 /* We have to inspect the file content */
971 for (curlen = 0, c = EOF;; ++curlen) {
972 lastc = c;
973 c = getc(fp);
975 if (c == '\0') {
976 ctt |= _HASNUL;
977 if ((ctt & _ISTXTCOK) == 0)
978 break;
979 continue;
981 if (c == '\n' || c == EOF) {
982 if (curlen >= MIME_LINELEN_LIMIT)
983 ctt |= _LONGLINES;
984 if (c == EOF)
985 break;
986 if (blankchar(lastc))
987 ctt |= _TRAILWS;
988 f_p = f_buf;
989 curlen = -1;
990 continue;
992 /* A bit hairy is handling of \r=\x0D=CR.
993 * RFC 2045, 6.7:
994 * Control characters other than TAB, or CR and LF as parts of CRLF
995 * pairs, must not appear. \r alone does not force _CTRLCHAR below since
996 * we cannot peek the next character. Thus right here, inspect the last
997 * seen character for if its \r and set _CTRLCHAR in a delayed fashion */
998 /*else*/ if (lastc == '\r')
999 ctt |= _CTRLCHAR;
1001 /* Control character? */
1002 if (c < 0x20 || c == 0x7F) {
1003 /* RFC 2045, 6.7, as above ... */
1004 if (c != '\t' && c != '\r')
1005 ctt |= _CTRLCHAR;
1006 /* If there is a escape sequence in backslash notation defined for
1007 * this in ANSI X3.159-1989 (ANSI C89), don't treat it as a control
1008 * for real. I.e., \a=\x07=BEL, \b=\x08=BS, \t=\x09=HT. Don't follow
1009 * libmagic(1) in respect to \v=\x0B=VT. \f=\x0C=NP; do ignore
1010 * \e=\x1B=ESC */
1011 if ((c >= '\x07' && c <= '\x0D') || c == '\x1B')
1012 continue;
1013 ctt |= _HASNUL; /* Force base64 */
1014 if ((ctt & _ISTXTCOK) == 0)
1015 break;
1016 } else if (c & 0x80) {
1017 ctt |= _HIGHBIT;
1018 /* TODO count chars with HIGHBIT? libmagic?
1019 * TODO try encode part - base64 if bails? */
1020 if ((ctt & (_NCTT|_ISTXT)) == 0) { /* TODO _NCTT?? */
1021 ctt |= _HASNUL; /* Force base64 */
1022 break;
1024 } else if ((ctt & _FROM_) == 0 && curlen < (sl_it)F_SIZEOF) {
1025 *f_p++ = (char)c;
1026 if (curlen == (sl_it)(F_SIZEOF - 1) &&
1027 PTR2SIZE(f_p - f_buf) == F_SIZEOF &&
1028 !memcmp(f_buf, F_, F_SIZEOF))
1029 ctt |= _FROM_;
1032 if (lastc != '\n')
1033 ctt |= _NOTERMNL;
1034 rewind(fp);
1036 if (ctt & _HASNUL) {
1037 convert = CONV_TOB64;
1038 /* Don't overwrite a text content-type to allow UTF-16 and such, but only
1039 * on request; else enforce what file(1)/libmagic(3) would suggest */
1040 if (ctt & _ISTXTCOK)
1041 goto jcharset;
1042 if (ctt & (_NCTT | _ISTXT))
1043 *contenttype = "application/octet-stream";
1044 if (*charset == NULL)
1045 *charset = "binary";
1046 goto jleave;
1049 if (ctt & (_LONGLINES | _CTRLCHAR | _NOTERMNL | _TRAILWS | _FROM_)) {
1050 convert = CONV_TOQP;
1051 goto jstepi;
1053 if (ctt & _HIGHBIT) {
1054 jstepi:
1055 if (ctt & (_NCTT|_ISTXT))
1056 *do_iconv = (ctt & _HIGHBIT) != 0;
1057 } else
1058 j7bit:
1059 convert = CONV_7BIT;
1060 if (ctt & _NCTT)
1061 *contenttype = "text/plain";
1063 /* Not an attachment with specified charset? */
1064 jcharset:
1065 if (*charset == NULL)
1066 *charset = (ctt & _HIGHBIT) ? _CHARSET() : charset_get_7bit();
1067 jleave:
1068 NYD_LEAVE;
1069 return convert;
1070 #undef F_
1071 #undef F_SIZEOF
1074 FL enum mimecontent
1075 mime_classify_content_of_part(struct mimepart const *mip)
1077 enum mimecontent mc;
1078 char const *ct;
1079 NYD_ENTER;
1081 mc = MIME_UNKNOWN;
1082 ct = mip->m_ct_type_plain;
1084 if (!asccasecmp(ct, "application/octet-stream") &&
1085 mip->m_filename != NULL && ok_blook(mime_counter_evidence)) {
1086 ct = mime_classify_content_type_by_fileext(mip->m_filename);
1087 if (ct == NULL)
1088 /* TODO how about let *mime-counter-evidence* have
1089 * TODO a value, and if set, saving the attachment in
1090 * TODO a temporary file that mime_classify_file() can
1091 * TODO examine, and using MIME_TEXT if that gives us
1092 * TODO something that seems to be human readable?! */
1093 goto jleave;
1095 if (strchr(ct, '/') == NULL) /* For compatibility with non-MIME */
1096 mc = MIME_TEXT;
1097 else if (!asccasecmp(ct, "text/plain"))
1098 mc = MIME_TEXT_PLAIN;
1099 else if (!asccasecmp(ct, "text/html"))
1100 mc = MIME_TEXT_HTML;
1101 else if (!ascncasecmp(ct, "text/", 5))
1102 mc = MIME_TEXT;
1103 else if (!asccasecmp(ct, "message/rfc822"))
1104 mc = MIME_822;
1105 else if (!ascncasecmp(ct, "message/", 8))
1106 mc = MIME_MESSAGE;
1107 else if (!asccasecmp(ct, "multipart/alternative"))
1108 mc = MIME_ALTERNATIVE;
1109 else if (!asccasecmp(ct, "multipart/digest"))
1110 mc = MIME_DIGEST;
1111 else if (!ascncasecmp(ct, "multipart/", 10))
1112 mc = MIME_MULTI;
1113 else if (!asccasecmp(ct, "application/x-pkcs7-mime") ||
1114 !asccasecmp(ct, "application/pkcs7-mime"))
1115 mc = MIME_PKCS7;
1116 jleave:
1117 NYD_LEAVE;
1118 return mc;
1121 FL char *
1122 mime_classify_content_type_by_fileext(char const *name)
1124 char *content = NULL;
1125 struct mtnode *mtn;
1126 size_t nlen;
1127 NYD_ENTER;
1129 if ((name = strrchr(name, '.')) == NULL || *++name == '\0')
1130 goto jleave;
1132 if (_mt_list == NULL)
1133 _mt_init();
1135 nlen = strlen(name);
1136 for (mtn = _mt_list; mtn != NULL; mtn = mtn->mt_next) {
1137 char const *ext = mtn->mt_line + mtn->mt_mtlen + 1,
1138 *cp = ext;
1139 do {
1140 while (!whitechar(*cp) && *cp != '\0')
1141 ++cp;
1142 /* Better to do case-insensitive comparison on extension, since the
1143 * RFC doesn't specify case of attribute values? */
1144 if (nlen == PTR2SIZE(cp - ext) && !ascncasecmp(name, ext, nlen)) {
1145 content = savestrbuf(mtn->mt_line, mtn->mt_mtlen);
1146 goto jleave;
1148 while (whitechar(*cp) && *cp != '\0')
1149 ++cp;
1150 ext = cp;
1151 } while (*ext != '\0');
1153 jleave:
1154 NYD_LEAVE;
1155 return content;
1158 FL int
1159 c_mimetypes(void *v)
1161 char **argv = v;
1162 struct mtnode *mtn;
1163 NYD_ENTER;
1165 if (*argv == NULL)
1166 goto jlist;
1167 if (argv[1] != NULL)
1168 goto jerr;
1169 if (!asccasecmp(*argv, "show"))
1170 goto jlist;
1171 if (!asccasecmp(*argv, "clear"))
1172 goto jclear;
1173 jerr:
1174 fprintf(stderr, "Synopsis: mimetypes: %s\n",
1175 tr(418, "Either <show> (default) or <clear> the mime.types cache"));
1176 v = NULL;
1177 jleave:
1178 NYD_LEAVE;
1179 return (v == NULL ? !STOP : !OKAY); /* xxx 1:bad 0:good -- do some */
1181 jlist: {
1182 FILE *fp;
1183 size_t l;
1185 if (_mt_list == NULL)
1186 _mt_init();
1188 if ((fp = Ftmp(NULL, "mimelist", OF_RDWR | OF_UNLINK | OF_REGISTER, 0600)) ==
1189 NULL) {
1190 perror("tmpfile");
1191 v = NULL;
1192 goto jleave;
1195 for (l = 0, mtn = _mt_list; mtn != NULL; ++l, mtn = mtn->mt_next)
1196 fprintf(fp, "%s\t%s\n", mtn->mt_line, mtn->mt_line + mtn->mt_mtlen + 1);
1198 page_or_print(fp, l);
1199 Fclose(fp);
1201 goto jleave;
1203 jclear:
1204 while ((mtn = _mt_list) != NULL) {
1205 _mt_list = mtn->mt_next;
1206 free(mtn);
1208 goto jleave;
1211 FL void
1212 mime_fromhdr(struct str const *in, struct str *out, enum tdflags flags)
1214 /* TODO mime_fromhdr(): is called with strings that contain newlines;
1215 * TODO this is the usual newline problem all around the codebase;
1216 * TODO i.e., if we strip it, then the display misses it ;>
1217 * TODO this is why it is so messy and why S-nail v14.2 plus additional
1218 * TODO patch for v14.5.2 (and maybe even v14.5.3 subminor) occurred, and
1219 * TODO why our display reflects what is contained in the message: the 1:1
1220 * TODO relationship of message content and display!
1221 * TODO instead a header line should be decoded to what it is (a single
1222 * TODO line that is) and it should be objective to the backend wether
1223 * TODO it'll be folded to fit onto the display or not, e.g., for search
1224 * TODO purposes etc. then the only condition we have to honour in here
1225 * TODO is that whitespace in between multiple adjacent MIME encoded words
1226 * TODO á la RFC 2047 is discarded; i.e.: this function should deal with
1227 * TODO RFC 2047 and be renamed: mime_fromhdr() -> mime_rfc2047_decode() */
1228 struct str cin, cout;
1229 char *p, *op, *upper, *cs, *cbeg;
1230 ui32_t convert, lastenc, lastoutl;
1231 #ifdef HAVE_ICONV
1232 char const *tcs;
1233 iconv_t fhicd = (iconv_t)-1;
1234 #endif
1235 NYD_ENTER;
1237 out->l = 0;
1238 if (in->l == 0) {
1239 *(out->s = smalloc(1)) = '\0';
1240 goto jleave;
1242 out->s = NULL;
1244 #ifdef HAVE_ICONV
1245 tcs = charset_get_lc();
1246 #endif
1247 p = in->s;
1248 upper = p + in->l;
1249 lastenc = lastoutl = 0;
1251 while (p < upper) {
1252 op = p;
1253 if (*p == '=' && *(p + 1) == '?') {
1254 p += 2;
1255 cbeg = p;
1256 while (p < upper && *p != '?')
1257 ++p; /* strip charset */
1258 if (p >= upper)
1259 goto jnotmime;
1260 cs = salloc(PTR2SIZE(++p - cbeg));
1261 memcpy(cs, cbeg, PTR2SIZE(p - cbeg - 1));
1262 cs[p - cbeg - 1] = '\0';
1263 #ifdef HAVE_ICONV
1264 if (fhicd != (iconv_t)-1)
1265 n_iconv_close(fhicd);
1266 fhicd = asccasecmp(cs, tcs) ? n_iconv_open(tcs, cs) : (iconv_t)-1;
1267 #endif
1268 switch (*p) {
1269 case 'B': case 'b':
1270 convert = CONV_FROMB64;
1271 break;
1272 case 'Q': case 'q':
1273 convert = CONV_FROMQP;
1274 break;
1275 default: /* invalid, ignore */
1276 goto jnotmime;
1278 if (*++p != '?')
1279 goto jnotmime;
1280 cin.s = ++p;
1281 cin.l = 1;
1282 for (;;) {
1283 if (PTRCMP(p + 1, >=, upper))
1284 goto jnotmime;
1285 if (*p++ == '?' && *p == '=')
1286 break;
1287 ++cin.l;
1289 ++p;
1290 --cin.l;
1292 cout.s = NULL;
1293 cout.l = 0;
1294 if (convert == CONV_FROMB64) {
1295 /* XXX Take care for, and strip LF from
1296 * XXX [Invalid Base64 encoding ignored] */
1297 if (b64_decode(&cout, &cin, NULL) == STOP &&
1298 cout.s[cout.l - 1] == '\n')
1299 --cout.l;
1300 } else
1301 qp_decode(&cout, &cin, NULL);
1303 out->l = lastenc;
1304 #ifdef HAVE_ICONV
1305 if ((flags & TD_ICONV) && fhicd != (iconv_t)-1) {
1306 cin.s = NULL, cin.l = 0; /* XXX string pool ! */
1307 convert = n_iconv_str(fhicd, &cin, &cout, NULL, TRU1);
1308 out = n_str_add(out, &cin);
1309 if (convert) /* EINVAL at EOS */
1310 out = n_str_add_buf(out, "?", 1);
1311 free(cin.s);
1312 } else
1313 #endif
1314 out = n_str_add(out, &cout);
1315 lastenc = lastoutl = out->l;
1316 free(cout.s);
1317 } else
1318 jnotmime: {
1319 bool_t onlyws;
1321 p = op;
1322 onlyws = (lastenc > 0);
1323 for (;;) {
1324 if (++op == upper)
1325 break;
1326 if (op[0] == '=' && (PTRCMP(op + 1, ==, upper) || op[1] == '?'))
1327 break;
1328 if (onlyws && !blankchar(*op))
1329 onlyws = FAL0;
1332 out = n_str_add_buf(out, p, PTR2SIZE(op - p));
1333 p = op;
1334 if (!onlyws || lastoutl != lastenc)
1335 lastenc = out->l;
1336 lastoutl = out->l;
1339 out->s[out->l] = '\0';
1341 if (flags & TD_ISPR) {
1342 makeprint(out, &cout);
1343 free(out->s);
1344 *out = cout;
1346 if (flags & TD_DELCTRL)
1347 out->l = delctrl(out->s, out->l);
1348 #ifdef HAVE_ICONV
1349 if (fhicd != (iconv_t)-1)
1350 n_iconv_close(fhicd);
1351 #endif
1352 jleave:
1353 NYD_LEAVE;
1354 return;
1357 FL char *
1358 mime_fromaddr(char const *name)
1360 char const *cp, *lastcp;
1361 char *res = NULL;
1362 size_t ressz = 1, rescur = 0;
1363 NYD_ENTER;
1365 if (name == NULL)
1366 goto jleave;
1367 if (*name == '\0') {
1368 res = savestr(name);
1369 goto jleave;
1372 if ((cp = routeaddr(name)) != NULL && cp > name) {
1373 addconv(&res, &ressz, &rescur, name, PTR2SIZE(cp - name));
1374 lastcp = cp;
1375 } else
1376 cp = lastcp = name;
1378 for ( ; *cp; ++cp) {
1379 switch (*cp) {
1380 case '(':
1381 addstr(&res, &ressz, &rescur, lastcp, PTR2SIZE(cp - lastcp + 1));
1382 lastcp = ++cp;
1383 cp = skip_comment(cp);
1384 if (--cp > lastcp)
1385 addconv(&res, &ressz, &rescur, lastcp, PTR2SIZE(cp - lastcp));
1386 lastcp = cp;
1387 break;
1388 case '"':
1389 while (*cp) {
1390 if (*++cp == '"')
1391 break;
1392 if (*cp == '\\' && cp[1] != '\0')
1393 ++cp;
1395 break;
1398 if (cp > lastcp)
1399 addstr(&res, &ressz, &rescur, lastcp, PTR2SIZE(cp - lastcp));
1400 /* TODO rescur==0: inserted to silence Coverity ...; check that */
1401 if (rescur == 0)
1402 res = UNCONST("");
1403 else
1404 res[rescur] = '\0';
1405 { char *x = res;
1406 res = savestr(res);
1407 free(x);
1409 jleave:
1410 NYD_LEAVE;
1411 return res;
1414 FL ssize_t
1415 xmime_write(char const *ptr, size_t size, FILE *f, enum conversion convert,
1416 enum tdflags dflags, struct str *rest)
1418 ssize_t rv;
1419 struct quoteflt *qf;
1420 NYD_ENTER;
1422 quoteflt_reset(qf = quoteflt_dummy(), f);
1423 rv = mime_write(ptr, size, f, convert, dflags, qf, rest);
1424 assert(quoteflt_flush(qf) == 0);
1425 NYD_LEAVE;
1426 return rv;
1429 FL ssize_t
1430 mime_write(char const *ptr, size_t size, FILE *f,
1431 enum conversion convert, enum tdflags dflags,
1432 struct quoteflt *qf, struct str *rest)
1434 /* TODO note: after send/MIME layer rewrite we will have a string pool
1435 * TODO so that memory allocation count drops down massively; for now,
1436 * TODO v14.0 that is, we pay a lot & heavily depend on the allocator */
1437 struct str in, out;
1438 ssize_t sz;
1439 int state;
1440 NYD_ENTER;
1442 in.s = UNCONST(ptr);
1443 in.l = size;
1444 out.s = NULL;
1445 out.l = 0;
1447 dflags |= _TD_BUFCOPY;
1448 if ((sz = size) == 0) {
1449 if (rest != NULL && rest->l != 0)
1450 goto jconvert;
1451 goto jleave;
1454 #ifdef HAVE_ICONV
1455 if ((dflags & TD_ICONV) && iconvd != (iconv_t)-1 &&
1456 (convert == CONV_TOQP || convert == CONV_8BIT ||
1457 convert == CONV_TOB64 || convert == CONV_TOHDR)) {
1458 if (n_iconv_str(iconvd, &out, &in, NULL, FAL0) != 0) {
1459 /* XXX report conversion error? */;
1460 sz = -1;
1461 goto jleave;
1463 in = out;
1464 out.s = NULL;
1465 dflags &= ~_TD_BUFCOPY;
1467 #endif
1469 jconvert:
1470 switch (convert) {
1471 case CONV_FROMQP:
1472 state = qp_decode(&out, &in, rest);
1473 goto jqpb64_dec;
1474 case CONV_TOQP:
1475 qp_encode(&out, &in, QP_NONE);
1476 goto jqpb64_enc;
1477 case CONV_8BIT:
1478 sz = quoteflt_push(qf, in.s, in.l);
1479 break;
1480 case CONV_FROMB64:
1481 rest = NULL;
1482 /* FALLTHRU */
1483 case CONV_FROMB64_T:
1484 state = b64_decode(&out, &in, rest);
1485 jqpb64_dec:
1486 if ((sz = out.l) != 0) {
1487 ui32_t opl = qf->qf_pfix_len;
1488 if (state != OKAY)
1489 qf->qf_pfix_len = 0;
1490 sz = _fwrite_td(&out, (dflags & ~_TD_BUFCOPY), rest,qf);
1491 qf->qf_pfix_len = opl;
1493 if (state != OKAY)
1494 sz = -1;
1495 break;
1496 case CONV_TOB64:
1497 b64_encode(&out, &in, B64_LF | B64_MULTILINE);
1498 jqpb64_enc:
1499 sz = fwrite(out.s, sizeof *out.s, out.l, f);
1500 if (sz != (ssize_t)out.l)
1501 sz = -1;
1502 break;
1503 case CONV_FROMHDR:
1504 mime_fromhdr(&in, &out, TD_ISPR | TD_ICONV | (dflags & TD_DELCTRL));
1505 sz = quoteflt_push(qf, out.s, out.l);
1506 break;
1507 case CONV_TOHDR:
1508 sz = mime_write_tohdr(&in, f);
1509 break;
1510 case CONV_TOHDR_A:
1511 sz = mime_write_tohdr_a(&in, f);
1512 break;
1513 default:
1514 sz = _fwrite_td(&in, dflags, NULL, qf);
1515 break;
1517 jleave:
1518 if (out.s != NULL)
1519 free(out.s);
1520 if (in.s != ptr)
1521 free(in.s);
1522 NYD_LEAVE;
1523 return sz;
1526 /* vim:set fenc=utf-8:s-it-mode */