mk-conf.sh, uninstall: avoid (faulty) shell error message
[s-mailx.git] / mime.c
blob1880bf0662422a1237a49dc31e1ed1e57c9f762c
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 struct mtnode {
45 struct mtnode *mt_next;
46 size_t mt_mtlen; /* Length of MIME type string */
47 char mt_line[VFIELD_SIZE(8)];
50 static char const * const _mt_sources[] = {
51 /* XXX Order fixed due to *mimetypes-load-control* handling! */
52 MIME_TYPES_USR, MIME_TYPES_SYS, NULL
54 * const _mt_bltin[] = {
55 #include "mime_types.h"
56 NULL
59 struct mtnode *_mt_list;
60 char *_cs_iter_base, *_cs_iter;
62 #ifdef HAVE_ICONV
63 # define _CS_ITER_GET() ((_cs_iter != NULL) ? _cs_iter : charset_get_8bit())
64 #else
65 # define _CS_ITER_GET() ((_cs_iter != NULL) ? _cs_iter : charset_get_lc())
66 #endif
67 #define _CS_ITER_STEP() _cs_iter = n_strsep(&_cs_iter_base, ',', TRU1)
69 /* Initialize MIME type list */
70 static void _mt_init(void);
71 static void __mt_add_line(char const *line, struct mtnode **tail);
73 /* Is 7-bit enough? */
74 #ifdef HAVE_ICONV
75 static bool_t _has_highbit(char const *s);
76 static bool_t _name_highbit(struct name *np);
77 #endif
79 /* Get the conversion that matches *encoding* */
80 static enum conversion _conversion_by_encoding(void);
82 /* fwrite(3) while checking for displayability */
83 static ssize_t _fwrite_td(struct str const *input, enum tdflags flags,
84 struct str *rest, struct quoteflt *qf);
86 static size_t delctrl(char *cp, size_t sz);
88 static int is_this_enc(char const *line, char const *encoding);
90 /* Convert header fields to RFC 1522 format and write to the file fo */
91 static ssize_t mime_write_tohdr(struct str *in, FILE *fo);
93 /* Write len characters of the passed string to the passed file, doing charset
94 * and header conversion */
95 static ssize_t convhdra(char const *str, size_t len, FILE *fp);
97 /* Write an address to a header field */
98 static ssize_t mime_write_tohdr_a(struct str *in, FILE *f);
100 /* Append to buf, handling resizing */
101 static void addstr(char **buf, size_t *sz, size_t *pos,
102 char const *str, size_t len);
104 static void addconv(char **buf, size_t *sz, size_t *pos,
105 char const *str, size_t len);
107 static void
108 _mt_init(void)
110 struct mtnode *tail = NULL;
111 char *line = NULL; /* TODO line pool */
112 size_t linesize = 0;
113 ui32_t idx, idx_ok;
114 char const *ccp, * const *srcs;
115 FILE *fp;
116 NYD_ENTER;
118 if ((ccp = ok_vlook(mimetypes_load_control)) == NULL)
119 idx_ok = (ui32_t)-1;
120 else for (idx_ok = 0; *ccp != '\0'; ++ccp)
121 switch (*ccp) {
122 case 'S':
123 case 's':
124 idx_ok |= 1 << 1;
125 break;
126 case 'U':
127 case 'u':
128 idx_ok |= 1 << 0;
129 break;
130 default:
131 /* XXX bad *mimetypes-load-control*; log error? */
132 break;
135 for (idx = 1, srcs = _mt_sources; *srcs != NULL; idx <<= 1, ++srcs) {
136 if (!(idx & idx_ok) || (ccp = file_expand(*srcs)) == NULL)
137 continue;
138 if ((fp = Fopen(ccp, "r")) == NULL) {
139 /*fprintf(stderr, tr(176, "Cannot open %s\n"), fn);*/
140 continue;
142 while (fgetline(&line, &linesize, NULL, NULL, fp, 0))
143 __mt_add_line(line, &tail);
144 Fclose(fp);
146 if (line != NULL)
147 free(line);
149 for (srcs = _mt_bltin; *srcs != NULL; ++srcs)
150 __mt_add_line(*srcs, &tail);
151 NYD_LEAVE;
154 static void
155 __mt_add_line(char const *line, struct mtnode **tail) /* XXX diag? dups!*/
157 char const *typ;
158 size_t tlen, elen;
159 struct mtnode *mtn;
160 NYD_ENTER;
162 if (!alphachar(*line))
163 goto jleave;
165 typ = line;
166 while (!blankchar(*line) && *line != '\0')
167 ++line;
168 if (*line == '\0')
169 goto jleave;
170 tlen = PTR2SIZE(line - typ);
172 while (blankchar(*line) && *line != '\0')
173 ++line;
175 if (*line == '\0')
176 goto jleave;
177 elen = strlen(line);
178 if (line[elen - 1] == '\n') {
179 if (--elen > 0 && line[elen - 1] == '\r')
180 --elen;
181 if (elen == 0)
182 goto jleave;
185 mtn = smalloc(sizeof(struct mtnode) -
186 VFIELD_SIZEOF(struct mtnode, mt_line) + tlen + 1 + elen +1);
187 if (*tail != NULL)
188 (*tail)->mt_next = mtn;
189 else
190 _mt_list = mtn;
191 *tail = mtn;
192 mtn->mt_next = NULL;
193 mtn->mt_mtlen = tlen;
194 memcpy(mtn->mt_line, typ, tlen);
195 mtn->mt_line[tlen] = '\0';
196 ++tlen;
197 memcpy(mtn->mt_line + tlen, line, elen);
198 tlen += elen;
199 mtn->mt_line[tlen] = '\0';
200 jleave:
201 NYD_LEAVE;
204 #ifdef HAVE_ICONV
205 static bool_t
206 _has_highbit(char const *s)
208 bool_t rv = TRU1;
209 NYD_ENTER;
211 if (s) {
213 if ((ui8_t)*s & 0x80)
214 goto jleave;
215 while (*s++ != '\0');
217 rv = FAL0;
218 jleave:
219 NYD_LEAVE;
220 return rv;
223 static bool_t
224 _name_highbit(struct name *np)
226 bool_t rv = TRU1;
227 NYD_ENTER;
229 while (np) {
230 if (_has_highbit(np->n_name) || _has_highbit(np->n_fullname))
231 goto jleave;
232 np = np->n_flink;
234 rv = FAL0;
235 jleave:
236 NYD_LEAVE;
237 return rv;
239 #endif /* HAVE_ICONV */
241 static enum conversion
242 _conversion_by_encoding(void)
244 char const *cp;
245 enum conversion ret;
246 NYD_ENTER;
248 if ((cp = ok_vlook(encoding)) == NULL)
249 ret = MIME_DEFAULT_ENCODING;
250 else if (!strcmp(cp, "quoted-printable"))
251 ret = CONV_TOQP;
252 else if (!strcmp(cp, "8bit"))
253 ret = CONV_8BIT;
254 else if (!strcmp(cp, "base64"))
255 ret = CONV_TOB64;
256 else {
257 fprintf(stderr, tr(177, "Warning: invalid encoding %s, using base64\n"),
258 cp);
259 ret = CONV_TOB64;
261 NYD_LEAVE;
262 return ret;
265 static ssize_t
266 _fwrite_td(struct str const *input, enum tdflags flags, struct str *rest,
267 struct quoteflt *qf)
269 /* TODO note: after send/MIME layer rewrite we will have a string pool
270 * TODO so that memory allocation count drops down massively; for now,
271 * TODO v14.* that is, we pay a lot & heavily depend on the allocator */
272 /* TODO well if we get a broken pipe here, and it happens to
273 * TODO happen pretty easy when sleeping in a full pipe buffer,
274 * TODO then the current codebase performs longjump away;
275 * TODO this leaves memory leaks behind ('think up to 3 per,
276 * TODO dep. upon alloca availability). For this to be fixed
277 * TODO we either need to get rid of the longjmp()s (tm) or
278 * TODO the storage must come from the outside or be tracked
279 * TODO in a carrier struct. Best both. But storage reuse
280 * TODO would be a bigbig win besides */
281 /* *input* _may_ point to non-modifyable buffer; but even then it only
282 * needs to be dup'ed away if we have to transform the content */
283 struct str in, out;
284 ssize_t rv;
285 NYD_ENTER;
286 UNUSED(rest);
288 in = *input;
289 out.s = NULL;
290 out.l = 0;
292 #ifdef HAVE_ICONV
293 if ((flags & TD_ICONV) && iconvd != (iconv_t)-1) {
294 char *buf = NULL;
296 if (rest != NULL && rest->l > 0) {
297 in.l = rest->l + input->l;
298 in.s = buf = smalloc(in.l +1);
299 memcpy(in.s, rest->s, rest->l);
300 memcpy(in.s + rest->l, input->s, input->l);
301 rest->l = 0;
304 if (n_iconv_str(iconvd, &out, &in, &in, TRU1) != 0 && rest != NULL &&
305 in.l > 0) {
306 /* Incomplete multibyte at EOF is special */
307 if (flags & _TD_EOF) {
308 out.s = srealloc(out.s, out.l + 4);
309 /* TODO 0xFFFD out.s[out.l++] = '[';*/
310 out.s[out.l++] = '?'; /* TODO 0xFFFD !!! */
311 /* TODO 0xFFFD out.s[out.l++] = ']';*/
312 } else
313 n_str_add(rest, &in);
315 in = out;
316 out.s = NULL;
317 flags &= ~_TD_BUFCOPY;
319 if (buf != NULL)
320 free(buf);
322 #endif
324 if (flags & TD_ISPR)
325 makeprint(&in, &out);
326 else if (flags & _TD_BUFCOPY)
327 n_str_dup(&out, &in);
328 else
329 out = in;
330 if (flags & TD_DELCTRL)
331 out.l = delctrl(out.s, out.l);
333 rv = quoteflt_push(qf, out.s, out.l);
335 if (out.s != in.s)
336 free(out.s);
337 if (in.s != input->s)
338 free(in.s);
339 NYD_LEAVE;
340 return rv;
343 static size_t
344 delctrl(char *cp, size_t sz)
346 size_t x = 0, y = 0;
347 NYD_ENTER;
349 while (x < sz) {
350 if (!cntrlchar(cp[x]))
351 cp[y++] = cp[x];
352 ++x;
354 NYD_LEAVE;
355 return y;
358 static int
359 is_this_enc(char const *line, char const *encoding)
361 int rv, c, quoted = 0;
362 NYD_ENTER;
364 if (*line == '"')
365 quoted = 1, ++line;
366 rv = 0;
367 while (*line != '\0' && *encoding)
368 if ((c = *line++, lowerconv(c) != *encoding++))
369 goto jleave;
370 rv = 1;
371 if (quoted && *line == '"')
372 goto jleave;
373 if (*line == '\0' || whitechar(*line))
374 goto jleave;
375 rv = 0;
376 jleave:
377 NYD_LEAVE;
378 return rv;
381 static ssize_t
382 mime_write_tohdr(struct str *in, FILE *fo) /* TODO rewrite - FAST! */
384 struct str cin, cout;
385 char buf[B64_LINESIZE +1]; /* (No CR/LF used) */
386 char const *charset7, *charset, *upper, *wbeg, *wend, *lastspc,
387 *lastwordend = NULL;
388 size_t col = 0, quoteany, wr, charsetlen,
389 maxcol = 65 /* there is the header field's name, too */;
390 ssize_t sz = 0;
391 bool_t highbit, mustquote, broken;
392 NYD_ENTER;
394 charset7 = charset_get_7bit();
395 charset = _CS_ITER_GET(); /* TODO MIME/send layer: iter active? iter! else */
396 wr = strlen(charset7);
397 charsetlen = strlen(charset);
398 charsetlen = MAX(charsetlen, wr);
399 upper = in->s + in->l;
401 /* xxx note this results in too much hits since =/? force quoting even
402 * xxx if they don't form =? etc. */
403 quoteany = mime_cte_mustquote(in->s, in->l, TRU1);
405 highbit = FAL0;
406 if (quoteany != 0)
407 for (wbeg = in->s; wbeg < upper; ++wbeg)
408 if ((ui8_t)*wbeg & 0x80) {
409 highbit = TRU1;
410 if (charset == NULL) {
411 sz = -1;
412 goto jleave;
414 break;
417 if (quoteany << 1 > in->l) {
418 /* Print the entire field in base64 */
419 for (wbeg = in->s; wbeg < upper; wbeg = wend) {
420 wend = upper;
421 cin.s = UNCONST(wbeg);
422 for (;;) {
423 cin.l = PTR2SIZE(wend - wbeg);
424 if (cin.l * 4/3 + 7 + charsetlen < maxcol - col) {
425 cout.s = buf;
426 cout.l = sizeof buf;
427 wr = fprintf(fo, "=?%s?B?%s?=", (highbit ? charset : charset7),
428 b64_encode(&cout, &cin, B64_BUF)->s);
429 sz += wr;
430 col += wr;
431 if (wend < upper) {
432 fwrite("\n ", sizeof(char), 2, fo);
433 sz += 2;
434 col = 0;
435 maxcol = 76;
437 break;
438 } else {
439 if (col) {
440 fprintf(fo, "\n ");
441 sz += 2;
442 col = 0;
443 maxcol = 76;
444 } else
445 wend -= 4;
449 } else {
450 /* Print the field word-wise in quoted-printable */
451 broken = FAL0;
452 for (wbeg = in->s; wbeg < upper; wbeg = wend) {
453 lastspc = NULL;
454 while (wbeg < upper && whitechar(*wbeg)) {
455 lastspc = lastspc ? lastspc : wbeg;
456 ++wbeg;
457 ++col;
458 broken = FAL0;
460 if (wbeg == upper) {
461 if (lastspc)
462 while (lastspc < wbeg) {
463 putc(*lastspc&0377, fo);
464 ++lastspc;
465 ++sz;
467 break;
470 if (lastspc != NULL)
471 broken = FAL0;
472 highbit = FAL0;
473 for (wend = wbeg; wend < upper && !whitechar(*wend); ++wend)
474 if ((ui8_t)*wend & 0x80)
475 highbit = TRU1;
476 mustquote = (mime_cte_mustquote(wbeg, PTR2SIZE(wend - wbeg), TRU1)
477 != 0);
479 if (mustquote || broken ||
480 (PTR2SIZE(wend - wbeg) >= 76-5 && quoteany)) {
481 for (cout.s = NULL;;) {
482 cin.s = UNCONST(lastwordend ? lastwordend : wbeg);
483 cin.l = PTR2SIZE(wend - cin.s);
484 qp_encode(&cout, &cin, QP_ISHEAD);
485 wr = cout.l + charsetlen + 7;
486 jqp_retest:
487 if (col <= maxcol && wr <= maxcol - col) {
488 if (lastspc) {
489 /* TODO because we included the WS in the encoded str,
490 * TODO put SP only??
491 * TODO RFC: "any 'linear-white-space' that separates
492 * TODO a pair of adjacent 'encoded-word's is ignored" */
493 putc(' ', fo);
494 ++sz;
495 ++col;
497 fprintf(fo, "=?%s?Q?%.*s?=",
498 (highbit ? charset : charset7), (int)cout.l, cout.s);
499 sz += wr;
500 col += wr;
501 break;
502 } else if (col > 1) {
503 /* TODO assuming SP separator, ignore *lastspc* !?? */
504 broken = TRU1;
505 if (lastspc != NULL) {
506 putc('\n', fo);
507 ++sz;
508 col = 0;
509 } else {
510 fputs("\n ", fo);
511 sz += 2;
512 col = 1;
514 maxcol = 76;
515 goto jqp_retest;
516 } else {
517 for (;;) { /* XXX */
518 wend -= 4;
519 assert(wend > wbeg);
520 if (wr - 4 < maxcol)
521 break;
522 wr -= 4;
526 if (cout.s != NULL)
527 free(cout.s);
528 lastwordend = wend;
529 } else {
530 if (col && PTR2SIZE(wend - wbeg) > maxcol - col) {
531 putc('\n', fo);
532 ++sz;
533 col = 0;
534 maxcol = 76;
535 if (lastspc == NULL) {
536 putc(' ', fo);
537 ++sz;
538 --maxcol;
539 } else
540 maxcol -= PTR2SIZE(wbeg - lastspc);
542 if (lastspc)
543 while (lastspc < wbeg) {
544 putc(*lastspc&0377, fo);
545 ++lastspc;
546 ++sz;
548 wr = fwrite(wbeg, sizeof *wbeg, PTR2SIZE(wend - wbeg), fo);
549 sz += wr;
550 col += wr;
551 lastwordend = NULL;
555 jleave:
556 NYD_LEAVE;
557 return sz;
560 static ssize_t
561 convhdra(char const *str, size_t len, FILE *fp)
563 #ifdef HAVE_ICONV
564 struct str ciconv;
565 #endif
566 struct str cin;
567 ssize_t ret = 0;
568 NYD_ENTER;
570 cin.s = UNCONST(str);
571 cin.l = len;
572 #ifdef HAVE_ICONV
573 ciconv.s = NULL;
574 if (iconvd != (iconv_t)-1) {
575 ciconv.l = 0;
576 if (n_iconv_str(iconvd, &ciconv, &cin, NULL, FAL0) != 0)
577 goto jleave;
578 cin = ciconv;
580 #endif
581 ret = mime_write_tohdr(&cin, fp);
582 #ifdef HAVE_ICONV
583 jleave:
584 if (ciconv.s != NULL)
585 free(ciconv.s);
586 #endif
587 NYD_LEAVE;
588 return ret;
591 static ssize_t
592 mime_write_tohdr_a(struct str *in, FILE *f) /* TODO error handling */
594 char const *cp, *lastcp;
595 ssize_t sz, x;
596 NYD_ENTER;
598 in->s[in->l] = '\0';
599 lastcp = in->s;
600 if ((cp = routeaddr(in->s)) != NULL && cp > lastcp) {
601 if ((sz = convhdra(lastcp, PTR2SIZE(cp - lastcp), f)) < 0)
602 goto jleave;
603 lastcp = cp;
604 } else {
605 cp = in->s;
606 sz = 0;
609 for ( ; *cp != '\0'; ++cp) {
610 switch (*cp) {
611 case '(':
612 sz += fwrite(lastcp, 1, PTR2SIZE(cp - lastcp + 1), f);
613 lastcp = ++cp;
614 cp = skip_comment(cp);
615 if (--cp > lastcp) {
616 if ((x = convhdra(lastcp, PTR2SIZE(cp - lastcp), f)) < 0) {
617 sz = x;
618 goto jleave;
620 sz += x;
622 lastcp = cp;
623 break;
624 case '"':
625 while (*cp) {
626 if (*++cp == '"')
627 break;
628 if (*cp == '\\' && cp[1] != '\0')
629 ++cp;
631 break;
634 if (cp > lastcp)
635 sz += fwrite(lastcp, 1, PTR2SIZE(cp - lastcp), f);
636 jleave:
637 NYD_LEAVE;
638 return sz;
641 static void
642 addstr(char **buf, size_t *sz, size_t *pos, char const *str, size_t len)
644 NYD_ENTER;
645 *buf = srealloc(*buf, *sz += len);
646 memcpy(&(*buf)[*pos], str, len);
647 *pos += len;
648 NYD_LEAVE;
651 static void
652 addconv(char **buf, size_t *sz, size_t *pos, char const *str, size_t len)
654 struct str in, out;
655 NYD_ENTER;
657 in.s = UNCONST(str);
658 in.l = len;
659 mime_fromhdr(&in, &out, TD_ISPR | TD_ICONV);
660 addstr(buf, sz, pos, out.s, out.l);
661 free(out.s);
662 NYD_LEAVE;
665 FL char const *
666 charset_get_7bit(void)
668 char const *t;
669 NYD_ENTER;
671 if ((t = ok_vlook(charset_7bit)) == NULL)
672 t = CHARSET_7BIT;
673 NYD_LEAVE;
674 return t;
677 #ifdef HAVE_ICONV
678 FL char const *
679 charset_get_8bit(void)
681 char const *t;
682 NYD_ENTER;
684 if ((t = ok_vlook(CHARSET_8BIT_OKEY)) == NULL)
685 t = CHARSET_8BIT;
686 NYD_LEAVE;
687 return t;
689 #endif
691 FL char const *
692 charset_get_lc(void)
694 char const *t;
695 NYD_ENTER;
697 if ((t = ok_vlook(ttycharset)) == NULL)
698 t = CHARSET_8BIT;
699 NYD_LEAVE;
700 return t;
703 FL bool_t
704 charset_iter_reset(char const *a_charset_to_try_first)
706 char const *sarr[3];
707 size_t sarrl[3], len;
708 char *cp;
709 NYD_ENTER;
710 UNUSED(a_charset_to_try_first);
712 #ifdef HAVE_ICONV
713 sarr[0] = a_charset_to_try_first;
714 if ((sarr[1] = ok_vlook(sendcharsets)) == NULL &&
715 ok_blook(sendcharsets_else_ttycharset))
716 sarr[1] = charset_get_lc();
717 sarr[2] = charset_get_8bit();
718 #else
719 sarr[2] = charset_get_lc();
720 #endif
722 sarrl[2] = len = strlen(sarr[2]);
723 #ifdef HAVE_ICONV
724 if ((cp = UNCONST(sarr[1])) != NULL)
725 len += (sarrl[1] = strlen(cp));
726 else
727 sarrl[1] = 0;
728 if ((cp = UNCONST(sarr[0])) != NULL)
729 len += (sarrl[0] = strlen(cp));
730 else
731 sarrl[0] = 0;
732 #endif
734 _cs_iter_base = cp = salloc(len + 1 + 1 +1);
736 #ifdef HAVE_ICONV
737 if ((len = sarrl[0]) != 0) {
738 memcpy(cp, sarr[0], len);
739 cp[len] = ',';
740 cp += ++len;
742 if ((len = sarrl[1]) != 0) {
743 memcpy(cp, sarr[1], len);
744 cp[len] = ',';
745 cp += ++len;
747 #endif
748 len = sarrl[2];
749 memcpy(cp, sarr[2], len);
750 cp[len] = '\0';
752 _CS_ITER_STEP();
753 NYD_LEAVE;
754 return (_cs_iter != NULL);
757 FL bool_t
758 charset_iter_next(void)
760 bool_t rv;
761 NYD_ENTER;
763 _CS_ITER_STEP();
764 rv = (_cs_iter != NULL);
765 NYD_LEAVE;
766 return rv;
769 FL bool_t
770 charset_iter_is_valid(void)
772 bool_t rv;
773 NYD_ENTER;
775 rv = (_cs_iter != NULL);
776 NYD_LEAVE;
777 return rv;
780 FL char const *
781 charset_iter(void)
783 char const *rv;
784 NYD_ENTER;
786 rv = _cs_iter;
787 NYD_LEAVE;
788 return rv;
791 FL void
792 charset_iter_recurse(char *outer_storage[2]) /* TODO LEGACY FUN, REMOVE */
794 NYD_ENTER;
795 outer_storage[0] = _cs_iter_base;
796 outer_storage[1] = _cs_iter;
797 NYD_LEAVE;
800 FL void
801 charset_iter_restore(char *outer_storage[2]) /* TODO LEGACY FUN, REMOVE */
803 NYD_ENTER;
804 _cs_iter_base = outer_storage[0];
805 _cs_iter = outer_storage[1];
806 NYD_LEAVE;
809 #ifdef HAVE_ICONV
810 FL char const *
811 need_hdrconv(struct header *hp, enum gfield w) /* TODO once only, then iter */
813 char const *ret = NULL;
814 NYD_ENTER;
816 if (w & GIDENT) {
817 if (hp->h_from != NULL) {
818 if (_name_highbit(hp->h_from))
819 goto jneeds;
820 } else if (_has_highbit(myaddrs(NULL)))
821 goto jneeds;
822 if (hp->h_organization) {
823 if (_has_highbit(hp->h_organization))
824 goto jneeds;
825 } else if (_has_highbit(ok_vlook(ORGANIZATION)))
826 goto jneeds;
827 if (hp->h_replyto) {
828 if (_name_highbit(hp->h_replyto))
829 goto jneeds;
830 } else if (_has_highbit(ok_vlook(replyto)))
831 goto jneeds;
832 if (hp->h_sender) {
833 if (_name_highbit(hp->h_sender))
834 goto jneeds;
835 } else if (_has_highbit(ok_vlook(sender)))
836 goto jneeds;
838 if ((w & GTO) && _name_highbit(hp->h_to))
839 goto jneeds;
840 if ((w & GCC) && _name_highbit(hp->h_cc))
841 goto jneeds;
842 if ((w & GBCC) && _name_highbit(hp->h_bcc))
843 goto jneeds;
844 if ((w & GSUBJECT) && _has_highbit(hp->h_subject))
845 jneeds:
846 ret = _CS_ITER_GET(); /* TODO MIME/send: iter active? iter! else */
847 NYD_LEAVE;
848 return ret;
850 #endif /* HAVE_ICONV */
852 FL enum mimeenc
853 mime_getenc(char *p)
855 enum mimeenc rv;
856 NYD_ENTER;
858 if (is_this_enc(p, "7bit"))
859 rv = MIME_7B;
860 else if (is_this_enc(p, "8bit"))
861 rv = MIME_8B;
862 else if (is_this_enc(p, "base64"))
863 rv = MIME_B64;
864 else if (is_this_enc(p, "binary"))
865 rv = MIME_BIN;
866 else if (is_this_enc(p, "quoted-printable"))
867 rv = MIME_QP;
868 else
869 rv = MIME_NONE;
870 NYD_LEAVE;
871 return rv;
874 FL char *
875 mime_getparam(char const *param, char *h)
877 char *p = h, *q, *rv = NULL;
878 int c;
879 size_t sz;
880 NYD_ENTER;
882 sz = strlen(param);
883 if (!whitechar(*p)) {
884 c = '\0';
885 while (*p && (*p != ';' || c == '\\')) {
886 c = (c == '\\') ? '\0' : *p;
887 ++p;
889 if (*p++ == '\0')
890 goto jleave;
893 for (;;) {
894 while (whitechar(*p))
895 ++p;
896 if (!ascncasecmp(p, param, sz)) {
897 p += sz;
898 while (whitechar(*p))
899 ++p;
900 if (*p++ == '=')
901 break;
903 c = '\0';
904 while (*p != '\0' && (*p != ';' || c == '\\')) {
905 if (*p == '"' && c != '\\') {
906 ++p;
907 while (*p != '\0' && (*p != '"' || c == '\\')) {
908 c = (c == '\\') ? '\0' : *p;
909 ++p;
911 ++p;
912 } else {
913 c = (c == '\\') ? '\0' : *p;
914 ++p;
917 if (*p++ == '\0')
918 goto jleave;
920 while (whitechar(*p))
921 ++p;
923 q = p;
924 if (*p == '"') {
925 p++;
926 if ((q = strchr(p, '"')) == NULL)
927 goto jleave;
928 } else {
929 while (*q != '\0' && !whitechar(*q) && *q != ';')
930 ++q;
932 sz = PTR2SIZE(q - p);
933 rv = salloc(q - p +1);
934 memcpy(rv, p, sz);
935 rv[sz] = '\0';
936 jleave:
937 NYD_LEAVE;
938 return rv;
941 FL char *
942 mime_get_boundary(char *h, size_t *len)
944 char *q = NULL, *p;
945 size_t sz;
946 NYD_ENTER;
948 if ((p = mime_getparam("boundary", h)) != NULL) {
949 sz = strlen(p);
950 if (len != NULL)
951 *len = sz + 2;
952 q = salloc(sz + 2 +1);
953 q[0] = q[1] = '-';
954 memcpy(q + 2, p, sz);
955 *(q + sz + 2) = '\0';
957 NYD_LEAVE;
958 return q;
961 FL char *
962 mime_create_boundary(void)
964 char *bp;
965 NYD_ENTER;
967 bp = salloc(48);
968 snprintf(bp, 48, "=_%011lu=-%s=_", (ul_it)time_current.tc_time,
969 getrandstring(47 - (11 + 6)));
970 NYD_LEAVE;
971 return bp;
974 FL int
975 mime_classify_file(FILE *fp, char const **contenttype, char const **charset,
976 int *do_iconv)
978 /* TODO classify once only PLEASE PLEASE PLEASE */
979 /* TODO BTW., after the MIME/send layer rewrite we could use a MIME
980 * TODO boundary of "=-=-=" if we would add a B_ in EQ spirit to F_,
981 * TODO and report that state to the outer world */
982 #define F_ "From "
983 #define F_SIZEOF (sizeof(F_) -1)
985 char f_buf[F_SIZEOF], *f_p = f_buf;
986 enum {
987 _CLEAN = 0, /* Plain RFC 2822 message */
988 _NCTT = 1<<0, /* *contenttype == NULL */
989 _ISTXT = 1<<1, /* *contenttype =~ text/ */
990 _ISTXTCOK = 1<<2, /* _ISTXT + *mime-allow-text-controls* */
991 _HIGHBIT = 1<<3, /* Not 7bit clean */
992 _LONGLINES = 1<<4, /* MIME_LINELEN_LIMIT exceed. */
993 _CTRLCHAR = 1<<5, /* Control characters seen */
994 _HASNUL = 1<<6, /* Contains \0 characters */
995 _NOTERMNL = 1<<7, /* Lacks a final newline */
996 _TRAILWS = 1<<8, /* Blanks before NL */
997 _FROM_ = 1<<9 /* ^From_ seen */
998 } ctt = _CLEAN;
999 enum conversion convert;
1000 ssize_t curlen;
1001 int c, lastc;
1002 NYD_ENTER;
1004 assert(ftell(fp) == 0x0l);
1006 *do_iconv = 0;
1008 if (*contenttype == NULL)
1009 ctt = _NCTT;
1010 else if (!ascncasecmp(*contenttype, "text/", 5))
1011 ctt = ok_blook(mime_allow_text_controls) ? _ISTXT | _ISTXTCOK : _ISTXT;
1012 convert = _conversion_by_encoding();
1014 if (fsize(fp) == 0)
1015 goto j7bit;
1017 /* We have to inspect the file content */
1018 for (curlen = 0, c = EOF;; ++curlen) {
1019 lastc = c;
1020 c = getc(fp);
1022 if (c == '\0') {
1023 ctt |= _HASNUL;
1024 if (!(ctt & _ISTXTCOK))
1025 break;
1026 continue;
1028 if (c == '\n' || c == EOF) {
1029 if (curlen >= MIME_LINELEN_LIMIT)
1030 ctt |= _LONGLINES;
1031 if (c == EOF)
1032 break;
1033 if (blankchar(lastc))
1034 ctt |= _TRAILWS;
1035 f_p = f_buf;
1036 curlen = -1;
1037 continue;
1039 /* A bit hairy is handling of \r=\x0D=CR.
1040 * RFC 2045, 6.7:
1041 * Control characters other than TAB, or CR and LF as parts of CRLF
1042 * pairs, must not appear. \r alone does not force _CTRLCHAR below since
1043 * we cannot peek the next character. Thus right here, inspect the last
1044 * seen character for if its \r and set _CTRLCHAR in a delayed fashion */
1045 /*else*/ if (lastc == '\r')
1046 ctt |= _CTRLCHAR;
1048 /* Control character? XXX this is all ASCII here */
1049 if (c < 0x20 || c == 0x7F) {
1050 /* RFC 2045, 6.7, as above ... */
1051 if (c != '\t' && c != '\r')
1052 ctt |= _CTRLCHAR;
1053 /* If there is a escape sequence in backslash notation defined for
1054 * this in ANSI X3.159-1989 (ANSI C89), don't treat it as a control
1055 * for real. I.e., \a=\x07=BEL, \b=\x08=BS, \t=\x09=HT. Don't follow
1056 * libmagic(1) in respect to \v=\x0B=VT. \f=\x0C=NP; do ignore
1057 * \e=\x1B=ESC */
1058 if ((c >= '\x07' && c <= '\x0D') || c == '\x1B')
1059 continue;
1060 ctt |= _HASNUL; /* Force base64 */
1061 if (!(ctt & _ISTXTCOK))
1062 break;
1063 } else if ((ui8_t)c & 0x80) {
1064 ctt |= _HIGHBIT;
1065 /* TODO count chars with HIGHBIT? libmagic?
1066 * TODO try encode part - base64 if bails? */
1067 if (!(ctt & (_NCTT | _ISTXT))) { /* TODO _NCTT?? */
1068 ctt |= _HASNUL; /* Force base64 */
1069 break;
1071 } else if (!(ctt & _FROM_) && UICMP(z, curlen, <, F_SIZEOF)) {
1072 *f_p++ = (char)c;
1073 if (UICMP(z, curlen, ==, F_SIZEOF - 1) &&
1074 PTR2SIZE(f_p - f_buf) == F_SIZEOF &&
1075 !memcmp(f_buf, F_, F_SIZEOF))
1076 ctt |= _FROM_;
1079 if (lastc != '\n')
1080 ctt |= _NOTERMNL;
1081 rewind(fp);
1083 if (ctt & _HASNUL) {
1084 convert = CONV_TOB64;
1085 /* Don't overwrite a text content-type to allow UTF-16 and such, but only
1086 * on request; else enforce what file(1)/libmagic(3) would suggest */
1087 if (ctt & _ISTXTCOK)
1088 goto jcharset;
1089 if (ctt & (_NCTT | _ISTXT))
1090 *contenttype = "application/octet-stream";
1091 if (*charset == NULL)
1092 *charset = "binary";
1093 goto jleave;
1096 if (ctt & (_LONGLINES | _CTRLCHAR | _NOTERMNL | _TRAILWS | _FROM_)) {
1097 if (convert != CONV_TOB64)
1098 convert = CONV_TOQP;
1099 goto jstepi;
1101 if (ctt & _HIGHBIT) {
1102 jstepi:
1103 if (ctt & (_NCTT | _ISTXT))
1104 *do_iconv = ((ctt & _HIGHBIT) != 0);
1105 } else
1106 j7bit:
1107 convert = CONV_7BIT;
1108 if (ctt & _NCTT)
1109 *contenttype = "text/plain";
1111 /* Not an attachment with specified charset? */
1112 jcharset:
1113 if (*charset == NULL) /* TODO MIME/send: iter active? iter! else */
1114 *charset = (ctt & _HIGHBIT) ? _CS_ITER_GET() : charset_get_7bit();
1115 jleave:
1116 NYD_LEAVE;
1117 return convert;
1118 #undef F_
1119 #undef F_SIZEOF
1122 FL enum mimecontent
1123 mime_classify_content_of_part(struct mimepart const *mip)
1125 enum mimecontent mc;
1126 char const *ct;
1127 NYD_ENTER;
1129 mc = MIME_UNKNOWN;
1130 ct = mip->m_ct_type_plain;
1132 if (!asccasecmp(ct, "application/octet-stream") &&
1133 mip->m_filename != NULL && ok_blook(mime_counter_evidence)) {
1134 ct = mime_classify_content_type_by_fileext(mip->m_filename);
1135 if (ct == NULL)
1136 /* TODO how about let *mime-counter-evidence* have
1137 * TODO a value, and if set, saving the attachment in
1138 * TODO a temporary file that mime_classify_file() can
1139 * TODO examine, and using MIME_TEXT if that gives us
1140 * TODO something that seems to be human readable?! */
1141 goto jleave;
1143 if (strchr(ct, '/') == NULL) /* For compatibility with non-MIME */
1144 mc = MIME_TEXT;
1145 else if (!asccasecmp(ct, "text/plain"))
1146 mc = MIME_TEXT_PLAIN;
1147 else if (!asccasecmp(ct, "text/html"))
1148 mc = MIME_TEXT_HTML;
1149 else if (!ascncasecmp(ct, "text/", 5))
1150 mc = MIME_TEXT;
1151 else if (!asccasecmp(ct, "message/rfc822"))
1152 mc = MIME_822;
1153 else if (!ascncasecmp(ct, "message/", 8))
1154 mc = MIME_MESSAGE;
1155 else if (!asccasecmp(ct, "multipart/alternative"))
1156 mc = MIME_ALTERNATIVE;
1157 else if (!asccasecmp(ct, "multipart/digest"))
1158 mc = MIME_DIGEST;
1159 else if (!ascncasecmp(ct, "multipart/", 10))
1160 mc = MIME_MULTI;
1161 else if (!asccasecmp(ct, "application/x-pkcs7-mime") ||
1162 !asccasecmp(ct, "application/pkcs7-mime"))
1163 mc = MIME_PKCS7;
1164 jleave:
1165 NYD_LEAVE;
1166 return mc;
1169 FL char *
1170 mime_classify_content_type_by_fileext(char const *name)
1172 char *content = NULL;
1173 struct mtnode *mtn;
1174 size_t nlen;
1175 NYD_ENTER;
1177 if ((name = strrchr(name, '.')) == NULL || *++name == '\0')
1178 goto jleave;
1180 if (_mt_list == NULL)
1181 _mt_init();
1183 nlen = strlen(name);
1184 for (mtn = _mt_list; mtn != NULL; mtn = mtn->mt_next) {
1185 char const *ext = mtn->mt_line + mtn->mt_mtlen + 1,
1186 *cp = ext;
1187 do {
1188 while (!whitechar(*cp) && *cp != '\0')
1189 ++cp;
1190 /* Better to do case-insensitive comparison on extension, since the
1191 * RFC doesn't specify case of attribute values? */
1192 if (nlen == PTR2SIZE(cp - ext) && !ascncasecmp(name, ext, nlen)) {
1193 content = savestrbuf(mtn->mt_line, mtn->mt_mtlen);
1194 goto jleave;
1196 while (whitechar(*cp) && *cp != '\0')
1197 ++cp;
1198 ext = cp;
1199 } while (*ext != '\0');
1201 jleave:
1202 NYD_LEAVE;
1203 return content;
1206 FL int
1207 c_mimetypes(void *v)
1209 char **argv = v;
1210 struct mtnode *mtn;
1211 NYD_ENTER;
1213 if (*argv == NULL)
1214 goto jlist;
1215 if (argv[1] != NULL)
1216 goto jerr;
1217 if (!asccasecmp(*argv, "show"))
1218 goto jlist;
1219 if (!asccasecmp(*argv, "clear"))
1220 goto jclear;
1221 jerr:
1222 fprintf(stderr, "Synopsis: mimetypes: %s\n",
1223 tr(418, "Either <show> (default) or <clear> the mime.types cache"));
1224 v = NULL;
1225 jleave:
1226 NYD_LEAVE;
1227 return (v == NULL ? !STOP : !OKAY); /* xxx 1:bad 0:good -- do some */
1229 jlist: {
1230 FILE *fp;
1231 size_t l;
1233 if (_mt_list == NULL)
1234 _mt_init();
1236 if ((fp = Ftmp(NULL, "mimelist", OF_RDWR | OF_UNLINK | OF_REGISTER, 0600)) ==
1237 NULL) {
1238 perror("tmpfile");
1239 v = NULL;
1240 goto jleave;
1243 for (l = 0, mtn = _mt_list; mtn != NULL; ++l, mtn = mtn->mt_next)
1244 fprintf(fp, "%s\t%s\n", mtn->mt_line, mtn->mt_line + mtn->mt_mtlen + 1);
1246 page_or_print(fp, l);
1247 Fclose(fp);
1249 goto jleave;
1251 jclear:
1252 while ((mtn = _mt_list) != NULL) {
1253 _mt_list = mtn->mt_next;
1254 free(mtn);
1256 goto jleave;
1259 FL void
1260 mime_fromhdr(struct str const *in, struct str *out, enum tdflags flags)
1262 /* TODO mime_fromhdr(): is called with strings that contain newlines;
1263 * TODO this is the usual newline problem all around the codebase;
1264 * TODO i.e., if we strip it, then the display misses it ;>
1265 * TODO this is why it is so messy and why S-nail v14.2 plus additional
1266 * TODO patch for v14.5.2 (and maybe even v14.5.3 subminor) occurred, and
1267 * TODO why our display reflects what is contained in the message: the 1:1
1268 * TODO relationship of message content and display!
1269 * TODO instead a header line should be decoded to what it is (a single
1270 * TODO line that is) and it should be objective to the backend wether
1271 * TODO it'll be folded to fit onto the display or not, e.g., for search
1272 * TODO purposes etc. then the only condition we have to honour in here
1273 * TODO is that whitespace in between multiple adjacent MIME encoded words
1274 * TODO á la RFC 2047 is discarded; i.e.: this function should deal with
1275 * TODO RFC 2047 and be renamed: mime_fromhdr() -> mime_rfc2047_decode() */
1276 struct str cin, cout;
1277 char *p, *op, *upper, *cs, *cbeg;
1278 ui32_t convert, lastenc, lastoutl;
1279 #ifdef HAVE_ICONV
1280 char const *tcs;
1281 iconv_t fhicd = (iconv_t)-1;
1282 #endif
1283 NYD_ENTER;
1285 out->l = 0;
1286 if (in->l == 0) {
1287 *(out->s = smalloc(1)) = '\0';
1288 goto jleave;
1290 out->s = NULL;
1292 #ifdef HAVE_ICONV
1293 tcs = charset_get_lc();
1294 #endif
1295 p = in->s;
1296 upper = p + in->l;
1297 lastenc = lastoutl = 0;
1299 while (p < upper) {
1300 op = p;
1301 if (*p == '=' && *(p + 1) == '?') {
1302 p += 2;
1303 cbeg = p;
1304 while (p < upper && *p != '?')
1305 ++p; /* strip charset */
1306 if (p >= upper)
1307 goto jnotmime;
1308 cs = salloc(PTR2SIZE(++p - cbeg));
1309 memcpy(cs, cbeg, PTR2SIZE(p - cbeg - 1));
1310 cs[p - cbeg - 1] = '\0';
1311 #ifdef HAVE_ICONV
1312 if (fhicd != (iconv_t)-1)
1313 n_iconv_close(fhicd);
1314 fhicd = asccasecmp(cs, tcs) ? n_iconv_open(tcs, cs) : (iconv_t)-1;
1315 #endif
1316 switch (*p) {
1317 case 'B': case 'b':
1318 convert = CONV_FROMB64;
1319 break;
1320 case 'Q': case 'q':
1321 convert = CONV_FROMQP;
1322 break;
1323 default: /* invalid, ignore */
1324 goto jnotmime;
1326 if (*++p != '?')
1327 goto jnotmime;
1328 cin.s = ++p;
1329 cin.l = 1;
1330 for (;;) {
1331 if (PTRCMP(p + 1, >=, upper))
1332 goto jnotmime;
1333 if (*p++ == '?' && *p == '=')
1334 break;
1335 ++cin.l;
1337 ++p;
1338 --cin.l;
1340 cout.s = NULL;
1341 cout.l = 0;
1342 if (convert == CONV_FROMB64) {
1343 /* XXX Take care for, and strip LF from
1344 * XXX [Invalid Base64 encoding ignored] */
1345 if (b64_decode(&cout, &cin, NULL) == STOP &&
1346 cout.s[cout.l - 1] == '\n')
1347 --cout.l;
1348 } else
1349 qp_decode(&cout, &cin, NULL);
1351 out->l = lastenc;
1352 #ifdef HAVE_ICONV
1353 if ((flags & TD_ICONV) && fhicd != (iconv_t)-1) {
1354 cin.s = NULL, cin.l = 0; /* XXX string pool ! */
1355 convert = n_iconv_str(fhicd, &cin, &cout, NULL, TRU1);
1356 out = n_str_add(out, &cin);
1357 if (convert) /* EINVAL at EOS */
1358 out = n_str_add_buf(out, "?", 1);
1359 free(cin.s);
1360 } else
1361 #endif
1362 out = n_str_add(out, &cout);
1363 lastenc = lastoutl = out->l;
1364 free(cout.s);
1365 } else
1366 jnotmime: {
1367 bool_t onlyws;
1369 p = op;
1370 onlyws = (lastenc > 0);
1371 for (;;) {
1372 if (++op == upper)
1373 break;
1374 if (op[0] == '=' && (PTRCMP(op + 1, ==, upper) || op[1] == '?'))
1375 break;
1376 if (onlyws && !blankchar(*op))
1377 onlyws = FAL0;
1380 out = n_str_add_buf(out, p, PTR2SIZE(op - p));
1381 p = op;
1382 if (!onlyws || lastoutl != lastenc)
1383 lastenc = out->l;
1384 lastoutl = out->l;
1387 out->s[out->l] = '\0';
1389 if (flags & TD_ISPR) {
1390 makeprint(out, &cout);
1391 free(out->s);
1392 *out = cout;
1394 if (flags & TD_DELCTRL)
1395 out->l = delctrl(out->s, out->l);
1396 #ifdef HAVE_ICONV
1397 if (fhicd != (iconv_t)-1)
1398 n_iconv_close(fhicd);
1399 #endif
1400 jleave:
1401 NYD_LEAVE;
1402 return;
1405 FL char *
1406 mime_fromaddr(char const *name)
1408 char const *cp, *lastcp;
1409 char *res = NULL;
1410 size_t ressz = 1, rescur = 0;
1411 NYD_ENTER;
1413 if (name == NULL)
1414 goto jleave;
1415 if (*name == '\0') {
1416 res = savestr(name);
1417 goto jleave;
1420 if ((cp = routeaddr(name)) != NULL && cp > name) {
1421 addconv(&res, &ressz, &rescur, name, PTR2SIZE(cp - name));
1422 lastcp = cp;
1423 } else
1424 cp = lastcp = name;
1426 for ( ; *cp; ++cp) {
1427 switch (*cp) {
1428 case '(':
1429 addstr(&res, &ressz, &rescur, lastcp, PTR2SIZE(cp - lastcp + 1));
1430 lastcp = ++cp;
1431 cp = skip_comment(cp);
1432 if (--cp > lastcp)
1433 addconv(&res, &ressz, &rescur, lastcp, PTR2SIZE(cp - lastcp));
1434 lastcp = cp;
1435 break;
1436 case '"':
1437 while (*cp) {
1438 if (*++cp == '"')
1439 break;
1440 if (*cp == '\\' && cp[1] != '\0')
1441 ++cp;
1443 break;
1446 if (cp > lastcp)
1447 addstr(&res, &ressz, &rescur, lastcp, PTR2SIZE(cp - lastcp));
1448 /* TODO rescur==0: inserted to silence Coverity ...; check that */
1449 if (rescur == 0)
1450 res = UNCONST("");
1451 else
1452 res[rescur] = '\0';
1453 { char *x = res;
1454 res = savestr(res);
1455 free(x);
1457 jleave:
1458 NYD_LEAVE;
1459 return res;
1462 FL ssize_t
1463 xmime_write(char const *ptr, size_t size, FILE *f, enum conversion convert,
1464 enum tdflags dflags, struct str *rest)
1466 ssize_t rv;
1467 struct quoteflt *qf;
1468 NYD_ENTER;
1470 quoteflt_reset(qf = quoteflt_dummy(), f);
1471 rv = mime_write(ptr, size, f, convert, dflags, qf, rest);
1472 assert(quoteflt_flush(qf) == 0);
1473 NYD_LEAVE;
1474 return rv;
1477 FL ssize_t
1478 mime_write(char const *ptr, size_t size, FILE *f,
1479 enum conversion convert, enum tdflags dflags,
1480 struct quoteflt *qf, struct str *rest)
1482 /* TODO note: after send/MIME layer rewrite we will have a string pool
1483 * TODO so that memory allocation count drops down massively; for now,
1484 * TODO v14.0 that is, we pay a lot & heavily depend on the allocator */
1485 struct str in, out;
1486 ssize_t sz;
1487 int state;
1488 NYD_ENTER;
1490 in.s = UNCONST(ptr);
1491 in.l = size;
1492 out.s = NULL;
1493 out.l = 0;
1495 dflags |= _TD_BUFCOPY;
1496 if ((sz = size) == 0) {
1497 if (rest != NULL && rest->l != 0)
1498 goto jconvert;
1499 goto jleave;
1502 #ifdef HAVE_ICONV
1503 if ((dflags & TD_ICONV) && iconvd != (iconv_t)-1 &&
1504 (convert == CONV_TOQP || convert == CONV_8BIT ||
1505 convert == CONV_TOB64 || convert == CONV_TOHDR)) {
1506 if (n_iconv_str(iconvd, &out, &in, NULL, FAL0) != 0) {
1507 /* XXX report conversion error? */;
1508 sz = -1;
1509 goto jleave;
1511 in = out;
1512 out.s = NULL;
1513 dflags &= ~_TD_BUFCOPY;
1515 #endif
1517 jconvert:
1518 switch (convert) {
1519 case CONV_FROMQP:
1520 state = qp_decode(&out, &in, rest);
1521 goto jqpb64_dec;
1522 case CONV_TOQP:
1523 qp_encode(&out, &in, QP_NONE);
1524 goto jqpb64_enc;
1525 case CONV_8BIT:
1526 sz = quoteflt_push(qf, in.s, in.l);
1527 break;
1528 case CONV_FROMB64:
1529 rest = NULL;
1530 /* FALLTHRU */
1531 case CONV_FROMB64_T:
1532 state = b64_decode(&out, &in, rest);
1533 jqpb64_dec:
1534 if ((sz = out.l) != 0) {
1535 ui32_t opl = qf->qf_pfix_len;
1536 if (state != OKAY)
1537 qf->qf_pfix_len = 0;
1538 sz = _fwrite_td(&out, (dflags & ~_TD_BUFCOPY), rest, qf);
1539 qf->qf_pfix_len = opl;
1541 if (state != OKAY)
1542 sz = -1;
1543 break;
1544 case CONV_TOB64:
1545 b64_encode(&out, &in, B64_LF | B64_MULTILINE);
1546 jqpb64_enc:
1547 sz = fwrite(out.s, sizeof *out.s, out.l, f);
1548 if (sz != (ssize_t)out.l)
1549 sz = -1;
1550 break;
1551 case CONV_FROMHDR:
1552 mime_fromhdr(&in, &out, TD_ISPR | TD_ICONV | (dflags & TD_DELCTRL));
1553 sz = quoteflt_push(qf, out.s, out.l);
1554 break;
1555 case CONV_TOHDR:
1556 sz = mime_write_tohdr(&in, f);
1557 break;
1558 case CONV_TOHDR_A:
1559 sz = mime_write_tohdr_a(&in, f);
1560 break;
1561 default:
1562 sz = _fwrite_td(&in, dflags, NULL, qf);
1563 break;
1565 jleave:
1566 if (out.s != NULL)
1567 free(out.s);
1568 if (in.s != ptr)
1569 free(in.s);
1570 NYD_LEAVE;
1571 return sz;
1574 /* vim:set fenc=utf-8:s-it-mode */