INSTALL: v14.9.4; and support `citron' a.k.a. "config build install"!
[s-mailx.git] / mime.c
blob5a4d5c0d4884954cb5bf8379e8ffeaf65d95e476
1 /*@ S-nail - a mail user agent derived from Berkeley Mail.
2 *@ MIME support functions.
3 *@ TODO Complete rewrite.
5 * Copyright (c) 2000-2004 Gunnar Ritter, Freiburg i. Br., Germany.
6 * Copyright (c) 2012 - 2017 Steffen (Daode) Nurpmeso <steffen@sdaoden.eu>.
7 */
8 /*
9 * Copyright (c) 2000
10 * Gunnar Ritter. All rights reserved.
12 * Redistribution and use in source and binary forms, with or without
13 * modification, are permitted provided that the following conditions
14 * are met:
15 * 1. Redistributions of source code must retain the above copyright
16 * notice, this list of conditions and the following disclaimer.
17 * 2. Redistributions in binary form must reproduce the above copyright
18 * notice, this list of conditions and the following disclaimer in the
19 * documentation and/or other materials provided with the distribution.
20 * 3. All advertising materials mentioning features or use of this software
21 * must display the following acknowledgement:
22 * This product includes software developed by Gunnar Ritter
23 * and his contributors.
24 * 4. Neither the name of Gunnar Ritter nor the names of his contributors
25 * may be used to endorse or promote products derived from this software
26 * without specific prior written permission.
28 * THIS SOFTWARE IS PROVIDED BY GUNNAR RITTER AND CONTRIBUTORS ``AS IS'' AND
29 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
30 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
31 * ARE DISCLAIMED. IN NO EVENT SHALL GUNNAR RITTER OR CONTRIBUTORS BE LIABLE
32 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
33 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
34 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
35 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
36 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
37 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
38 * SUCH DAMAGE.
40 #undef n_FILE
41 #define n_FILE mime
43 #ifndef HAVE_AMALGAMATION
44 # include "nail.h"
45 #endif
47 /* Don't ask, but it keeps body and soul together */
48 enum a_mime_structure_hack{
49 a_MIME_SH_NONE,
50 a_MIME_SH_COMMENT,
51 a_MIME_SH_QUOTE
54 static char *_cs_iter_base, *_cs_iter;
55 #ifdef HAVE_ICONV
56 # define _CS_ITER_GET() \
57 ((_cs_iter != NULL) ? _cs_iter : ok_vlook(CHARSET_8BIT_OKEY))
58 #else
59 # define _CS_ITER_GET() ((_cs_iter != NULL) ? _cs_iter : ok_vlook(ttycharset))
60 #endif
61 #define _CS_ITER_STEP() _cs_iter = n_strsep(&_cs_iter_base, ',', TRU1)
63 /* Is 7-bit enough? */
64 #ifdef HAVE_ICONV
65 static bool_t _has_highbit(char const *s);
66 static bool_t _name_highbit(struct name *np);
67 #endif
69 /* fwrite(3) while checking for displayability */
70 static ssize_t _fwrite_td(struct str const *input, enum tdflags flags,
71 struct str *outrest, struct quoteflt *qf);
73 /* Convert header fields to RFC 2047 format and write to the file fo */
74 static ssize_t mime_write_tohdr(struct str *in, FILE *fo,
75 size_t *colp, enum a_mime_structure_hack msh);
77 /* Write len characters of the passed string to the passed file, doing charset
78 * and header conversion */
80 /* Write an address to a header field */
81 static ssize_t mime_write_tohdr_a(struct str *in, FILE *f,
82 size_t *colp);
83 #ifdef HAVE_ICONV
84 static ssize_t a_mime__convhdra(struct str *inp, FILE *fp, size_t *colp,
85 enum a_mime_structure_hack msh);
86 #else
87 # define a_mime__convhdra(S,F,C,MSH) mime_write_tohdr(S, F, C, MSH)
88 #endif
90 /* Append to buf, handling resizing */
91 static void _append_str(char **buf, size_t *sz, size_t *pos,
92 char const *str, size_t len);
93 static void _append_conv(char **buf, size_t *sz, size_t *pos,
94 char const *str, size_t len);
96 #ifdef HAVE_ICONV
97 static bool_t
98 _has_highbit(char const *s)
100 bool_t rv = TRU1;
101 NYD_ENTER;
103 if (s) {
105 if ((ui8_t)*s & 0x80)
106 goto jleave;
107 while (*s++ != '\0');
109 rv = FAL0;
110 jleave:
111 NYD_LEAVE;
112 return rv;
115 static bool_t
116 _name_highbit(struct name *np)
118 bool_t rv = TRU1;
119 NYD_ENTER;
121 while (np) {
122 if (_has_highbit(np->n_name) || _has_highbit(np->n_fullname))
123 goto jleave;
124 np = np->n_flink;
126 rv = FAL0;
127 jleave:
128 NYD_LEAVE;
129 return rv;
131 #endif /* HAVE_ICONV */
133 static sigjmp_buf __mimefwtd_actjmp; /* TODO someday.. */
134 static int __mimefwtd_sig; /* TODO someday.. */
135 static sighandler_type __mimefwtd_opipe;
136 static void
137 __mimefwtd_onsig(int sig) /* TODO someday, we won't need it no more */
139 NYD_X; /* Signal handler */
140 __mimefwtd_sig = sig;
141 siglongjmp(__mimefwtd_actjmp, 1);
144 static ssize_t
145 _fwrite_td(struct str const *input, enum tdflags flags, struct str *outrest,
146 struct quoteflt *qf)
148 /* TODO note: after send/MIME layer rewrite we will have a string pool
149 * TODO so that memory allocation count drops down massively; for now,
150 * TODO v14.* that is, we pay a lot & heavily depend on the allocator */
151 /* TODO well if we get a broken pipe here, and it happens to
152 * TODO happen pretty easy when sleeping in a full pipe buffer,
153 * TODO then the current codebase performs longjump away;
154 * TODO this leaves memory leaks behind ('think up to 3 per,
155 * TODO dep. upon alloca availability). For this to be fixed
156 * TODO we either need to get rid of the longjmp()s (tm) or
157 * TODO the storage must come from the outside or be tracked
158 * TODO in a carrier struct. Best both. But storage reuse
159 * TODO would be a bigbig win besides */
160 /* *input* _may_ point to non-modifyable buffer; but even then it only
161 * needs to be dup'ed away if we have to transform the content */
162 struct str in, out;
163 ssize_t rv;
164 NYD_ENTER;
165 n_UNUSED(outrest);
167 in = *input;
168 out.s = NULL;
169 out.l = 0;
171 #ifdef HAVE_ICONV
172 if ((flags & TD_ICONV) && iconvd != (iconv_t)-1) {
173 int err;
174 char *buf;
176 buf = NULL;
178 if (outrest != NULL && outrest->l > 0) {
179 in.l = outrest->l + input->l;
180 in.s = buf = smalloc(in.l +1);
181 memcpy(in.s, outrest->s, outrest->l);
182 memcpy(&in.s[outrest->l], input->s, input->l);
183 outrest->l = 0;
186 if((err = n_iconv_str(iconvd, n_ICONV_UNIDEFAULT, &out, &in, &in)) != 0 &&
187 outrest != NULL && in.l > 0){
188 if(err != n_ERR_INVAL)
189 n_iconv_reset(iconvd);
191 /* Incomplete multibyte at EOF is special xxx _INVAL? */
192 if (flags & _TD_EOF) {
193 out.s = srealloc(out.s, out.l + sizeof(n_unirepl));
194 if(n_psonce & n_PSO_UNICODE){
195 memcpy(&out.s[out.l], n_unirepl, sizeof(n_unirepl) -1);
196 out.l += sizeof(n_unirepl) -1;
197 }else
198 out.s[out.l++] = '?';
199 } else
200 n_str_add(outrest, &in);
202 in = out;
203 out.l = 0;
204 out.s = NULL;
205 flags &= ~_TD_BUFCOPY;
207 if (buf != NULL)
208 free(buf);
209 }else
210 #endif /* HAVE_ICONV */
211 /* Else, if we will modify the data bytes and thus introduce the potential
212 * of messing up multibyte sequences which become splitted over buffer
213 * boundaries TODO and unless we don't have our filter chain which will
214 * TODO make these hacks go by, buffer data until we see a NL */
215 if((flags & (TD_ISPR | TD_DELCTRL)) && outrest != NULL &&
216 #ifdef HAVE_ICONV
217 iconvd == (iconv_t)-1 &&
218 #endif
219 (!(flags & _TD_EOF) || outrest->l > 0)
221 size_t i;
222 char *cp;
224 for (cp = &in.s[in.l]; cp > in.s && cp[-1] != '\n'; --cp)
226 i = PTR2SIZE(cp - in.s);
228 if (i != in.l) {
229 if (i > 0) {
230 n_str_assign_buf(outrest, cp, in.l - i);
231 cp = smalloc(i +1);
232 memcpy(cp, in.s, in.l = i);
233 (in.s = cp)[in.l = i] = '\0';
234 flags &= ~_TD_BUFCOPY;
235 } else {
236 n_str_add_buf(outrest, input->s, input->l);
237 rv = 0;
238 goto jleave;
243 if (flags & TD_ISPR)
244 makeprint(&in, &out);
245 else if (flags & _TD_BUFCOPY)
246 n_str_dup(&out, &in);
247 else
248 out = in;
249 if (flags & TD_DELCTRL)
250 out.l = delctrl(out.s, out.l);
252 __mimefwtd_sig = 0;
253 __mimefwtd_opipe = safe_signal(SIGPIPE, &__mimefwtd_onsig);
254 if (sigsetjmp(__mimefwtd_actjmp, 1)) {
255 rv = 0;
256 goto j__sig;
259 rv = quoteflt_push(qf, out.s, out.l);
261 j__sig:
262 if (out.s != in.s)
263 free(out.s);
264 if (in.s != input->s)
265 free(in.s);
266 safe_signal(SIGPIPE, __mimefwtd_opipe);
267 if (__mimefwtd_sig != 0)
268 n_raise(__mimefwtd_sig);
269 jleave:
270 NYD_LEAVE;
271 return rv;
274 static ssize_t
275 mime_write_tohdr(struct str *in, FILE *fo, size_t *colp,
276 enum a_mime_structure_hack msh)
278 /* TODO mime_write_tohdr(): we don't know the name of our header->maxcol..
279 * TODO MIME/send layer rewrite: more available state!!
280 * TODO Because of this we cannot make a difference in between structured
281 * TODO and unstructured headers (RFC 2047, 5. (2))
282 * TODO This means, e.g., that this gets called multiple times for a
283 * TODO structured header and always starts thinking it is at column 0.
284 * TODO I.e., it may get called for only the content of a comment etc.,
285 * TODO not knowing anything of its context.
286 * TODO Instead we should have a list of header body content tokens,
287 * TODO convert them, and then dump the converted tokens, breaking lines.
288 * TODO I.e., get rid of convhdra, mime_write_tohdr_a and such...
289 * TODO Somewhen, the following should produce smooth stuff:
290 * TODO ' "Hallo\"," Dr. Backe "Bl\"ö\"d" (Gell) <ha@llöch.en>
291 * TODO "Nochm\"a\"l"<ta@tu.da>(Dümm)'
292 * TODO NOT MULTIBYTE SAFE IF AN ENCODED WORD HAS TO BE SPLITTED!
293 * TODO To be better we had to mbtowc_l() (non-std! and no locale!!) and
294 * TODO work char-wise! -> S-CText..
295 * TODO The real problem for STD compatibility is however that "in" is
296 * TODO already iconv(3) encoded to the target character set! We could
297 * TODO also solve it (very expensively!) if we would narrow down to an
298 * TODO encoded word and then iconv(3)+MIME encode in one go, in which
299 * TODO case multibyte errors could be catched! */
300 enum {
301 /* Maximum line length */
302 a_MAXCOL_NENC = MIME_LINELEN,
303 a_MAXCOL = MIME_LINELEN_RFC2047
306 struct str cout, cin;
307 enum {
308 _FIRST = 1<<0, /* Nothing written yet, start of string */
309 _MSH_NOTHING = 1<<1, /* Now, really: nothing at all has been written */
310 a_ANYENC = 1<<2, /* We have RFC 2047 anything at least once */
311 _NO_QP = 1<<3, /* No quoted-printable allowed */
312 _NO_B64 = 1<<4, /* Ditto, base64 */
313 _ENC_LAST = 1<<5, /* Last round generated encoded word */
314 _SHOULD_BEE = 1<<6, /* Avoid lines longer than SHOULD via encoding */
315 _RND_SHIFT = 7,
316 _RND_MASK = (1<<_RND_SHIFT) - 1,
317 _SPACE = 1<<(_RND_SHIFT+1), /* Leading whitespace */
318 _8BIT = 1<<(_RND_SHIFT+2), /* High bit set */
319 _ENCODE = 1<<(_RND_SHIFT+3), /* Need encoding */
320 _ENC_B64 = 1<<(_RND_SHIFT+4), /* - let it be base64 */
321 _OVERLONG = 1<<(_RND_SHIFT+5) /* Temporarily rised limit */
322 } flags;
323 char const *cset7, *cset8, *wbot, *upper, *wend, *wcur;
324 ui32_t cset7_len, cset8_len;
325 size_t col, i, j;
326 ssize_t sz;
328 NYD_ENTER;
330 cout.s = NULL, cout.l = 0;
331 cset7 = ok_vlook(charset_7bit);
332 cset7_len = (ui32_t)strlen(cset7);
333 cset8 = _CS_ITER_GET(); /* TODO MIME/send layer: iter active? iter! else */
334 cset8_len = (ui32_t)strlen(cset8);
336 flags = _FIRST;
337 if(msh != a_MIME_SH_NONE)
338 flags |= _MSH_NOTHING;
340 /* RFC 1468, "MIME Considerations":
341 * ISO-2022-JP may also be used in MIME Part 2 headers. The "B"
342 * encoding should be used with ISO-2022-JP text. */
343 /* TODO of course, our current implementation won't deal properly with
344 * TODO any stateful encoding at all... (the standard says each encoded
345 * TODO word must include all necessary reset sequences..., i.e., each
346 * TODO encoded word must be a self-contained iconv(3) life cycle) */
347 if (!asccasecmp(cset8, "iso-2022-jp") || mime_enc_target() == MIMEE_B64)
348 flags |= _NO_QP;
350 wbot = in->s;
351 upper = wbot + in->l;
352 sz = 0;
354 if(colp == NULL || (col = *colp) == 0)
355 col = sizeof("Mail-Followup-To: ") -1; /* TODO dreadful thing */
357 /* The user may specify empy quoted-strings or comments, keep them! */
358 if(wbot == upper) {
359 if(flags & _MSH_NOTHING){
360 flags &= ~_MSH_NOTHING;
361 putc((msh == a_MIME_SH_COMMENT ? '(' : '"'), fo);
362 sz = 1;
363 ++col;
365 } else for (; wbot < upper; flags &= ~_FIRST, wbot = wend) {
366 flags &= _RND_MASK;
367 wcur = wbot;
368 while (wcur < upper && whitechar(*wcur)) {
369 flags |= _SPACE;
370 ++wcur;
373 /* Any occurrence of whitespace resets prevention of lines >SHOULD via
374 * enforced encoding (xxx SHOULD, but.. encoding is expensive!!) */
375 if (flags & _SPACE)
376 flags &= ~_SHOULD_BEE;
378 /* Data ends with WS - dump it and done.
379 * Also, if we have seen multiple successive whitespace characters, then
380 * if there was no encoded word last, i.e., if we can simply take them
381 * over to the output as-is, keep one WS for possible later separation
382 * purposes and simply print the others as-is, directly! */
383 if (wcur == upper) {
384 wend = wcur;
385 goto jnoenc_putws;
387 if ((flags & (_ENC_LAST | _SPACE)) == _SPACE && wcur - wbot > 1) {
388 wend = wcur - 1;
389 goto jnoenc_putws;
392 /* Skip over a word to next non-whitespace, keep track along the way
393 * whether our 7-bit charset suffices to represent the data */
394 for (wend = wcur; wend < upper; ++wend) {
395 if (whitechar(*wend))
396 break;
397 if ((uc_i)*wend & 0x80)
398 flags |= _8BIT;
401 /* Decide whether the range has to become encoded or not */
402 i = PTR2SIZE(wend - wcur);
403 j = mime_enc_mustquote(wcur, i, MIMEEF_ISHEAD);
404 /* If it just cannot fit on a SHOULD line length, force encode */
405 if (i > a_MAXCOL_NENC) {
406 flags |= _SHOULD_BEE; /* (Sigh: SHOULD only, not MUST..) */
407 goto j_beejump;
409 if ((flags & _SHOULD_BEE) || j > 0) {
410 j_beejump:
411 flags |= _ENCODE;
412 /* Use base64 if requested or more than 50% -37.5-% of the bytes of
413 * the string need to be encoded */
414 if ((flags & _NO_QP) || j >= i >> 1)/*(i >> 2) + (i >> 3))*/
415 flags |= _ENC_B64;
417 DBG( if (flags & _8BIT) assert(flags & _ENCODE); )
419 if (!(flags & _ENCODE)) {
420 /* Encoded word produced, but no linear whitespace for necessary RFC
421 * 2047 separation? Generate artificial data (bad standard!) */
422 if ((flags & (_ENC_LAST | _SPACE)) == _ENC_LAST) {
423 if (col >= a_MAXCOL) {
424 putc('\n', fo);
425 ++sz;
426 col = 0;
428 if(flags & _MSH_NOTHING){
429 flags &= ~_MSH_NOTHING;
430 putc((msh == a_MIME_SH_COMMENT ? '(' : '"'), fo);
431 ++sz;
432 ++col;
434 putc(' ', fo);
435 ++sz;
436 ++col;
439 jnoenc_putws:
440 flags &= ~_ENC_LAST;
442 /* todo No effort here: (1) v15.0 has to bring complete rewrite,
443 * todo (2) the standard is braindead and (3) usually this is one
444 * todo word only, and why be smarter than the standard? */
445 jnoenc_retry:
446 i = PTR2SIZE(wend - wbot);
447 if (i + col + ((flags & _MSH_NOTHING) != 0) <=
448 (flags & _OVERLONG ? MIME_LINELEN_MAX
449 : (flags & a_ANYENC ? a_MAXCOL : a_MAXCOL_NENC))) {
450 if(flags & _MSH_NOTHING){
451 flags &= ~_MSH_NOTHING;
452 putc((msh == a_MIME_SH_COMMENT ? '(' : '"'), fo);
453 ++sz;
454 ++col;
456 i = fwrite(wbot, sizeof *wbot, i, fo);
457 sz += i;
458 col += i;
459 continue;
462 /* Doesn't fit, try to break the line first; */
463 if (col > 1) {
464 putc('\n', fo);
465 if (whitechar(*wbot)) {
466 putc((uc_i)*wbot, fo);
467 ++wbot;
468 } else
469 putc(' ', fo); /* Bad standard: artificial data! */
470 sz += 2;
471 col = 1;
472 if(flags & _MSH_NOTHING){
473 flags &= ~_MSH_NOTHING;
474 putc((msh == a_MIME_SH_COMMENT ? '(' : '"'), fo);
475 ++sz;
476 ++col;
478 flags |= _OVERLONG;
479 goto jnoenc_retry;
482 /* It is so long that it needs to be broken, effectively causing
483 * artificial spaces to be inserted (bad standard), yuck */
484 /* todo This is not multibyte safe, as above; and completely stupid
485 * todo P.S.: our _SHOULD_BEE prevents these cases in the meanwhile */
486 /* FIXME n_PSO_UNICODE and parse using UTF-8 sync possibility! */
487 wcur = wbot + MIME_LINELEN_MAX - 8;
488 while (wend > wcur)
489 wend -= 4;
490 goto jnoenc_retry;
491 } else {
492 /* Encoding to encoded word(s); deal with leading whitespace, place
493 * a separator first as necessary: encoded words must always be
494 * separated from text and other encoded words with linear WS.
495 * And if an encoded word was last, intermediate whitespace must
496 * also be encoded, otherwise it would get stripped away! */
497 wcur = n_UNCONST(n_empty);
498 if ((flags & (_ENC_LAST | _SPACE)) != _SPACE) {
499 /* Reinclude whitespace */
500 flags &= ~_SPACE;
501 /* We don't need to place a separator at the very beginning */
502 if (!(flags & _FIRST))
503 wcur = n_UNCONST(" ");
504 } else
505 wcur = wbot++;
507 flags |= a_ANYENC | _ENC_LAST;
508 n_pstate |= n_PS_HEADER_NEEDED_MIME;
510 /* RFC 2047:
511 * An 'encoded-word' may not be more than 75 characters long,
512 * including 'charset', 'encoding', 'encoded-text', and
513 * delimiters. If it is desirable to encode more text than will
514 * fit in an 'encoded-word' of 75 characters, multiple
515 * 'encoded-word's (separated by CRLF SPACE) may be used.
517 * While there is no limit to the length of a multiple-line
518 * header field, each line of a header field that contains one
519 * or more 'encoded-word's is limited to 76 characters */
520 jenc_retry:
521 cin.s = n_UNCONST(wbot);
522 cin.l = PTR2SIZE(wend - wbot);
524 /* C99 */{
525 struct str *xout;
527 if(flags & _ENC_B64)
528 xout = b64_encode(&cout, &cin, B64_ISHEAD | B64_ISENCWORD);
529 else
530 xout = qp_encode(&cout, &cin, QP_ISHEAD | QP_ISENCWORD);
531 if(xout == NULL){
532 sz = -1;
533 break;
535 j = xout->l;
537 /* (Avoid trigraphs in the RFC 2047 placeholder..) */
538 i = j + (flags & _8BIT ? cset8_len : cset7_len) + sizeof("=!!B!!=") -1;
539 if (*wcur != '\0')
540 ++i;
542 jenc_retry_same:
543 /* Unfortunately RFC 2047 explicitly disallows encoded words to be
544 * longer (just like RFC 5322's "a line SHOULD fit in 78 but MAY be
545 * 998 characters long"), so we cannot use the _OVERLONG mechanism,
546 * even though all tested mailers seem to support it */
547 if (i + col <= (/*flags & _OVERLONG ? MIME_LINELEN_MAX :*/ a_MAXCOL)) {
548 if(flags & _MSH_NOTHING){
549 flags &= ~_MSH_NOTHING;
550 putc((msh == a_MIME_SH_COMMENT ? '(' : '"'), fo);
551 ++sz;
552 ++col;
554 fprintf(fo, "%.1s=?%s?%c?%.*s?=",
555 wcur, (flags & _8BIT ? cset8 : cset7),
556 (flags & _ENC_B64 ? 'B' : 'Q'),
557 (int)cout.l, cout.s);
558 sz += i;
559 col += i;
560 continue;
563 /* Doesn't fit, try to break the line first */
564 /* TODO I've commented out the _FIRST test since we (1) cannot do
565 * TODO _OVERLONG since (MUAs support but) the standard disallows,
566 * TODO and because of our iconv problem i prefer an empty first line
567 * TODO in favour of a possibly messed up multibytes character. :-( */
568 if (col > 1 /* TODO && !(flags & _FIRST)*/) {
569 putc('\n', fo);
570 sz += 2;
571 col = 1;
572 if (!(flags & _SPACE)) {
573 putc(' ', fo);
574 wcur = n_UNCONST(n_empty);
575 /*flags |= _OVERLONG;*/
576 goto jenc_retry_same;
577 } else {
578 putc((uc_i)*wcur, fo);
579 if (whitechar(*(wcur = wbot)))
580 ++wbot;
581 else {
582 flags &= ~_SPACE;
583 wcur = n_UNCONST(n_empty);
585 /*flags &= ~_OVERLONG;*/
586 goto jenc_retry;
590 /* It is so long that it needs to be broken, effectively causing
591 * artificial data to be inserted (bad standard), yuck */
592 /* todo This is not multibyte safe, as above */
593 /*if (!(flags & _OVERLONG)) { Mechanism explicitly forbidden by 2047
594 flags |= _OVERLONG;
595 goto jenc_retry;
598 /* FIXME n_PSO_UNICODE and parse using UTF-8 sync possibility! */
599 i = PTR2SIZE(wend - wbot) + !!(flags & _SPACE);
600 j = 3 + !(flags & _ENC_B64);
601 for (;;) {
602 wend -= j;
603 i -= j;
604 /* (Note the problem most likely is the transfer-encoding blow,
605 * which is why we test this *after* the decrements.. */
606 if (i <= a_MAXCOL)
607 break;
609 goto jenc_retry;
613 if(!(flags & _MSH_NOTHING) && msh != a_MIME_SH_NONE){
614 putc((msh == a_MIME_SH_COMMENT ? ')' : '"'), fo);
615 ++sz;
616 ++col;
619 if (cout.s != NULL)
620 free(cout.s);
622 if(colp != NULL)
623 *colp = col;
624 NYD_LEAVE;
625 return sz;
628 static ssize_t
629 mime_write_tohdr_a(struct str *in, FILE *f, size_t *colp)
631 struct str xin;
632 size_t i;
633 char const *cp, *lastcp;
634 ssize_t sz, x;
635 NYD_ENTER;
637 in->s[in->l] = '\0';
638 lastcp = in->s;
639 if((cp = routeaddr(in->s)) != NULL && cp > lastcp) {
640 xin.s = in->s;
641 xin.l = PTR2SIZE(cp - in->s);
642 if ((sz = mime_write_tohdr_a(&xin, f, colp)) < 0)
643 goto jleave;
644 xin.s[xin.l] = '<';
645 lastcp = cp;
646 } else {
647 cp = in->s;
648 sz = 0;
651 for( ; *cp != '\0'; ++cp){
652 switch(*cp){
653 case '(':
654 i = PTR2SIZE(cp - lastcp);
655 if(i > 0){
656 if(fwrite(lastcp, 1, i, f) != i)
657 goto jerr;
658 sz += i;
660 lastcp = ++cp;
661 cp = skip_comment(cp);
662 if(cp > lastcp)
663 --cp;
664 /* We want to keep empty comments, too! */
665 xin.s = n_UNCONST(lastcp);
666 xin.l = PTR2SIZE(cp - lastcp);
667 if ((x = a_mime__convhdra(&xin, f, colp, a_MIME_SH_COMMENT)) < 0)
668 goto jerr;
669 sz += x;
670 lastcp = &cp[1];
671 break;
672 case '"':
673 i = PTR2SIZE(cp - lastcp);
674 if(i > 0){
675 if(fwrite(lastcp, 1, i, f) != i)
676 goto jerr;
677 sz += i;
679 for(lastcp = ++cp; *cp != '\0'; ++cp){
680 if(*cp == '"')
681 break;
682 if(*cp == '\\' && cp[1] != '\0')
683 ++cp;
685 /* We want to keep empty quoted-strings, too! */
686 xin.s = n_UNCONST(lastcp);
687 xin.l = PTR2SIZE(cp - lastcp);
688 if((x = a_mime__convhdra(&xin, f, colp, a_MIME_SH_QUOTE)) < 0)
689 goto jerr;
690 sz += x;
691 ++sz;
692 lastcp = &cp[1];
693 break;
697 i = PTR2SIZE(cp - lastcp);
698 if(i > 0){
699 if(fwrite(lastcp, 1, i, f) != i)
700 goto jerr;
701 sz += i;
703 jleave:
704 NYD_LEAVE;
705 return sz;
706 jerr:
707 sz = -1;
708 goto jleave;
711 #ifdef HAVE_ICONV
712 static ssize_t
713 a_mime__convhdra(struct str *inp, FILE *fp, size_t *colp,
714 enum a_mime_structure_hack msh){
715 struct str ciconv;
716 ssize_t rv;
717 NYD_ENTER;
719 rv = 0;
720 ciconv.s = NULL;
722 if(inp->l > 0 && iconvd != (iconv_t)-1){
723 ciconv.l = 0;
724 if(n_iconv_str(iconvd, n_ICONV_IGN_NOREVERSE, &ciconv, inp, NULL) != 0){
725 n_iconv_reset(iconvd);
726 goto jleave;
728 *inp = ciconv;
731 rv = mime_write_tohdr(inp, fp, colp, msh);
732 jleave:
733 if(ciconv.s != NULL)
734 free(ciconv.s);
735 NYD_LEAVE;
736 return rv;
738 #endif /* HAVE_ICONV */
740 static void
741 _append_str(char **buf, size_t *sz, size_t *pos, char const *str, size_t len)
743 NYD_ENTER;
744 *buf = srealloc(*buf, *sz += len);
745 memcpy(&(*buf)[*pos], str, len);
746 *pos += len;
747 NYD_LEAVE;
750 static void
751 _append_conv(char **buf, size_t *sz, size_t *pos, char const *str, size_t len)
753 struct str in, out;
754 NYD_ENTER;
756 in.s = n_UNCONST(str);
757 in.l = len;
758 mime_fromhdr(&in, &out, TD_ISPR | TD_ICONV);
759 _append_str(buf, sz, pos, out.s, out.l);
760 free(out.s);
761 NYD_LEAVE;
764 FL bool_t
765 charset_iter_reset(char const *a_charset_to_try_first) /* TODO elim. dups! */
767 char const *sarr[3];
768 size_t sarrl[3], len;
769 char *cp;
770 NYD_ENTER;
771 n_UNUSED(a_charset_to_try_first);
773 #ifdef HAVE_ICONV
774 sarr[2] = ok_vlook(CHARSET_8BIT_OKEY);
776 if(a_charset_to_try_first != NULL && strcmp(a_charset_to_try_first, sarr[2]))
777 sarr[0] = a_charset_to_try_first;
778 else
779 sarr[0] = NULL;
781 if((sarr[1] = ok_vlook(sendcharsets)) == NULL &&
782 ok_blook(sendcharsets_else_ttycharset)){
783 cp = n_UNCONST(ok_vlook(ttycharset));
784 if(strcmp(cp, sarr[2]) && (sarr[0] == NULL || strcmp(cp, sarr[0])))
785 sarr[1] = cp;
787 #else
788 sarr[2] = ok_vlook(ttycharset);
789 #endif
791 sarrl[2] = len = strlen(sarr[2]);
792 #ifdef HAVE_ICONV
793 if ((cp = n_UNCONST(sarr[1])) != NULL)
794 len += (sarrl[1] = strlen(cp));
795 else
796 sarrl[1] = 0;
797 if ((cp = n_UNCONST(sarr[0])) != NULL)
798 len += (sarrl[0] = strlen(cp));
799 else
800 sarrl[0] = 0;
801 #endif
803 _cs_iter_base = cp = salloc(len + 1 + 1 +1);
805 #ifdef HAVE_ICONV
806 if ((len = sarrl[0]) != 0) {
807 memcpy(cp, sarr[0], len);
808 cp[len] = ',';
809 cp += ++len;
811 if ((len = sarrl[1]) != 0) {
812 memcpy(cp, sarr[1], len);
813 cp[len] = ',';
814 cp += ++len;
816 #endif
817 len = sarrl[2];
818 memcpy(cp, sarr[2], len);
819 cp[len] = '\0';
821 _CS_ITER_STEP();
822 NYD_LEAVE;
823 return (_cs_iter != NULL);
826 FL bool_t
827 charset_iter_next(void)
829 bool_t rv;
830 NYD_ENTER;
832 _CS_ITER_STEP();
833 rv = (_cs_iter != NULL);
834 NYD_LEAVE;
835 return rv;
838 FL bool_t
839 charset_iter_is_valid(void)
841 bool_t rv;
842 NYD_ENTER;
844 rv = (_cs_iter != NULL);
845 NYD_LEAVE;
846 return rv;
849 FL char const *
850 charset_iter(void)
852 char const *rv;
853 NYD_ENTER;
855 rv = _cs_iter;
856 NYD_LEAVE;
857 return rv;
860 FL char const *
861 charset_iter_or_fallback(void)
863 char const *rv;
864 NYD_ENTER;
866 rv = _CS_ITER_GET();
867 NYD_LEAVE;
868 return rv;
871 FL void
872 charset_iter_recurse(char *outer_storage[2]) /* TODO LEGACY FUN, REMOVE */
874 NYD_ENTER;
875 outer_storage[0] = _cs_iter_base;
876 outer_storage[1] = _cs_iter;
877 NYD_LEAVE;
880 FL void
881 charset_iter_restore(char *outer_storage[2]) /* TODO LEGACY FUN, REMOVE */
883 NYD_ENTER;
884 _cs_iter_base = outer_storage[0];
885 _cs_iter = outer_storage[1];
886 NYD_LEAVE;
889 #ifdef HAVE_ICONV
890 FL char const *
891 need_hdrconv(struct header *hp) /* TODO once only, then iter */
893 struct n_header_field *hfp;
894 char const *rv;
895 NYD_ENTER;
897 rv = NULL;
899 if((hfp = hp->h_user_headers) != NULL)
900 do if(_has_highbit(hfp->hf_dat + hfp->hf_nl +1))
901 goto jneeds;
902 while((hfp = hfp->hf_next) != NULL);
904 if((hfp = hp->h_custom_headers) != NULL ||
905 (hp->h_custom_headers = hfp = n_customhdr_query()) != NULL)
906 do if(_has_highbit(hfp->hf_dat + hfp->hf_nl +1))
907 goto jneeds;
908 while((hfp = hfp->hf_next) != NULL);
910 if (hp->h_mft != NULL) {
911 if (_name_highbit(hp->h_mft))
912 goto jneeds;
914 if (hp->h_from != NULL) {
915 if (_name_highbit(hp->h_from))
916 goto jneeds;
917 } else if (_has_highbit(myaddrs(NULL)))
918 goto jneeds;
919 if (hp->h_reply_to) {
920 if (_name_highbit(hp->h_reply_to))
921 goto jneeds;
922 } else {
923 char const *v15compat;
925 if((v15compat = ok_vlook(replyto)) != NULL)
926 n_OBSOLETE(_("please use *reply-to*, not *replyto*"));
927 if(_has_highbit(v15compat))
928 goto jneeds;
929 if(_has_highbit(ok_vlook(reply_to)))
930 goto jneeds;
932 if (hp->h_sender) {
933 if (_name_highbit(hp->h_sender))
934 goto jneeds;
935 } else if (_has_highbit(ok_vlook(sender)))
936 goto jneeds;
938 if (_name_highbit(hp->h_to))
939 goto jneeds;
940 if (_name_highbit(hp->h_cc))
941 goto jneeds;
942 if (_name_highbit(hp->h_bcc))
943 goto jneeds;
944 if (_has_highbit(hp->h_subject))
945 jneeds:
946 rv = _CS_ITER_GET(); /* TODO MIME/send: iter active? iter! else */
947 NYD_LEAVE;
948 return rv;
950 #endif /* HAVE_ICONV */
952 FL void
953 mime_fromhdr(struct str const *in, struct str *out, enum tdflags flags)
955 /* TODO mime_fromhdr(): is called with strings that contain newlines;
956 * TODO this is the usual newline problem all around the codebase;
957 * TODO i.e., if we strip it, then the display misses it ;>
958 * TODO this is why it is so messy and why S-nail v14.2 plus additional
959 * TODO patch for v14.5.2 (and maybe even v14.5.3 subminor) occurred, and
960 * TODO why our display reflects what is contained in the message: the 1:1
961 * TODO relationship of message content and display!
962 * TODO instead a header line should be decoded to what it is (a single
963 * TODO line that is) and it should be objective to the backend whether
964 * TODO it'll be folded to fit onto the display or not, e.g., for search
965 * TODO purposes etc. then the only condition we have to honour in here
966 * TODO is that whitespace in between multiple adjacent MIME encoded words
967 * TODO á la RFC 2047 is discarded; i.e.: this function should deal with
968 * TODO RFC 2047 and be renamed: mime_fromhdr() -> mime_rfc2047_decode() */
969 struct str cin, cout;
970 char *p, *op, *upper;
971 ui32_t convert, lastenc, lastoutl;
972 #ifdef HAVE_ICONV
973 char const *tcs;
974 char *cbeg;
975 iconv_t fhicd = (iconv_t)-1;
976 #endif
977 NYD_ENTER;
979 out->l = 0;
980 if (in->l == 0) {
981 *(out->s = smalloc(1)) = '\0';
982 goto jleave;
984 out->s = NULL;
986 #ifdef HAVE_ICONV
987 tcs = ok_vlook(ttycharset);
988 #endif
989 p = in->s;
990 upper = p + in->l;
991 lastenc = lastoutl = 0;
993 while (p < upper) {
994 op = p;
995 if (*p == '=' && *(p + 1) == '?') {
996 p += 2;
997 #ifdef HAVE_ICONV
998 cbeg = p;
999 #endif
1000 while (p < upper && *p != '?')
1001 ++p; /* strip charset */
1002 if (p >= upper)
1003 goto jnotmime;
1004 ++p;
1005 #ifdef HAVE_ICONV
1006 if (flags & TD_ICONV) {
1007 size_t i = PTR2SIZE(p - cbeg);
1008 char *ltag, *cs = ac_alloc(i);
1010 memcpy(cs, cbeg, --i);
1011 cs[i] = '\0';
1012 /* RFC 2231 extends the RFC 2047 character set definition in
1013 * encoded words by language tags - silently strip those off */
1014 if ((ltag = strchr(cs, '*')) != NULL)
1015 *ltag = '\0';
1017 if (fhicd != (iconv_t)-1)
1018 n_iconv_close(fhicd);
1019 fhicd = asccasecmp(cs, tcs) ? n_iconv_open(tcs, cs) : (iconv_t)-1;
1020 ac_free(cs);
1022 #endif
1023 switch (*p) {
1024 case 'B': case 'b':
1025 convert = CONV_FROMB64;
1026 break;
1027 case 'Q': case 'q':
1028 convert = CONV_FROMQP;
1029 break;
1030 default: /* invalid, ignore */
1031 goto jnotmime;
1033 if (*++p != '?')
1034 goto jnotmime;
1035 cin.s = ++p;
1036 cin.l = 1;
1037 for (;;) {
1038 if (PTRCMP(p + 1, >=, upper))
1039 goto jnotmime;
1040 if (*p++ == '?' && *p == '=')
1041 break;
1042 ++cin.l;
1044 ++p;
1045 --cin.l;
1047 cout.s = NULL;
1048 cout.l = 0;
1049 if (convert == CONV_FROMB64) {
1050 if(!b64_decode_header(&cout, &cin))
1051 n_str_assign_cp(&cout, _("[Invalid Base64 encoding]"));
1052 }else if(!qp_decode_header(&cout, &cin))
1053 n_str_assign_cp(&cout, _("[Invalid Quoted-Printable encoding]"));
1055 out->l = lastenc;
1056 #ifdef HAVE_ICONV
1057 if ((flags & TD_ICONV) && fhicd != (iconv_t)-1) {
1058 cin.s = NULL, cin.l = 0; /* XXX string pool ! */
1059 convert = n_iconv_str(fhicd, n_ICONV_UNIDEFAULT, &cin, &cout, NULL);
1060 out = n_str_add(out, &cin);
1061 if (convert) {/* n_ERR_INVAL at EOS */
1062 n_iconv_reset(fhicd);
1063 out = n_str_add_buf(out, n_qm, 1); /* TODO unicode replacement */
1065 free(cin.s);
1066 } else
1067 #endif
1068 out = n_str_add(out, &cout);
1069 lastenc = lastoutl = out->l;
1070 free(cout.s);
1071 } else
1072 jnotmime: {
1073 bool_t onlyws;
1075 p = op;
1076 onlyws = (lastenc > 0);
1077 for (;;) {
1078 if (++op == upper)
1079 break;
1080 if (op[0] == '=' && (PTRCMP(op + 1, ==, upper) || op[1] == '?'))
1081 break;
1082 if (onlyws && !blankchar(*op))
1083 onlyws = FAL0;
1086 out = n_str_add_buf(out, p, PTR2SIZE(op - p));
1087 p = op;
1088 if (!onlyws || lastoutl != lastenc)
1089 lastenc = out->l;
1090 lastoutl = out->l;
1093 out->s[out->l] = '\0';
1095 if (flags & TD_ISPR) {
1096 makeprint(out, &cout);
1097 free(out->s);
1098 *out = cout;
1100 if (flags & TD_DELCTRL)
1101 out->l = delctrl(out->s, out->l);
1102 #ifdef HAVE_ICONV
1103 if (fhicd != (iconv_t)-1)
1104 n_iconv_close(fhicd);
1105 #endif
1106 jleave:
1107 NYD_LEAVE;
1108 return;
1111 FL char *
1112 mime_fromaddr(char const *name)
1114 char const *cp, *lastcp;
1115 char *res = NULL;
1116 size_t ressz = 1, rescur = 0;
1117 NYD_ENTER;
1119 if (name == NULL)
1120 goto jleave;
1121 if (*name == '\0') {
1122 res = savestr(name);
1123 goto jleave;
1126 if ((cp = routeaddr(name)) != NULL && cp > name) {
1127 _append_conv(&res, &ressz, &rescur, name, PTR2SIZE(cp - name));
1128 lastcp = cp;
1129 } else
1130 cp = lastcp = name;
1132 for ( ; *cp; ++cp) {
1133 switch (*cp) {
1134 case '(':
1135 _append_str(&res, &ressz, &rescur, lastcp, PTR2SIZE(cp - lastcp + 1));
1136 lastcp = ++cp;
1137 cp = skip_comment(cp);
1138 if (--cp > lastcp)
1139 _append_conv(&res, &ressz, &rescur, lastcp, PTR2SIZE(cp - lastcp));
1140 lastcp = cp;
1141 break;
1142 case '"':
1143 while (*cp) {
1144 if (*++cp == '"')
1145 break;
1146 if (*cp == '\\' && cp[1] != '\0')
1147 ++cp;
1149 break;
1152 if (cp > lastcp)
1153 _append_str(&res, &ressz, &rescur, lastcp, PTR2SIZE(cp - lastcp));
1154 /* C99 */{
1155 char *x;
1157 x = res;
1158 res = savestrbuf(res, rescur);
1159 if(rescur > 0)
1160 free(x);
1162 jleave:
1163 NYD_LEAVE;
1164 return res;
1167 FL ssize_t
1168 xmime_write(char const *ptr, size_t size, FILE *f, enum conversion convert,
1169 enum tdflags dflags, struct str * volatile outrest,
1170 struct str * volatile inrest)
1172 ssize_t rv;
1173 struct quoteflt *qf;
1174 NYD_ENTER;
1176 quoteflt_reset(qf = quoteflt_dummy(), f);
1177 rv = mime_write(ptr, size, f, convert, dflags, qf, outrest, inrest);
1178 quoteflt_flush(qf);
1179 NYD_LEAVE;
1180 return rv;
1183 static sigjmp_buf __mimemw_actjmp; /* TODO someday.. */
1184 static int __mimemw_sig; /* TODO someday.. */
1185 static sighandler_type __mimemw_opipe;
1186 static void
1187 __mimemw_onsig(int sig) /* TODO someday, we won't need it no more */
1189 NYD_X; /* Signal handler */
1190 __mimemw_sig = sig;
1191 siglongjmp(__mimemw_actjmp, 1);
1194 FL ssize_t
1195 mime_write(char const *ptr, size_t size, FILE *f,
1196 enum conversion convert, enum tdflags volatile dflags,
1197 struct quoteflt *qf, struct str * volatile outrest,
1198 struct str * volatile inrest)
1200 /* TODO note: after send/MIME layer rewrite we will have a string pool
1201 * TODO so that memory allocation count drops down massively; for now,
1202 * TODO v14.0 that is, we pay a lot & heavily depend on the allocator.
1203 * TODO P.S.: furthermore all this encapsulated in filter objects instead */
1204 struct str in, out;
1205 ssize_t volatile sz;
1206 NYD_ENTER;
1208 dflags |= _TD_BUFCOPY;
1209 in.s = n_UNCONST(ptr);
1210 in.l = size;
1211 out.s = NULL;
1212 out.l = 0;
1214 if((sz = size) == 0){
1215 if(inrest != NULL && inrest->l != 0)
1216 goto jinrest;
1217 if(outrest != NULL && outrest->l != 0)
1218 goto jconvert;
1219 goto jleave;
1222 /* TODO This crap requires linewise input, then. We need a filter chain
1223 * TODO as in input->iconv->base64 where each filter can have its own
1224 * TODO buffer, with a filter->fflush() call to get rid of those! */
1225 #ifdef HAVE_ICONV
1226 if ((dflags & TD_ICONV) && iconvd != (iconv_t)-1 &&
1227 (convert == CONV_TOQP || convert == CONV_8BIT ||
1228 convert == CONV_TOB64 || convert == CONV_TOHDR)) {
1229 if (n_iconv_str(iconvd, n_ICONV_IGN_NOREVERSE, &out, &in, NULL) != 0) {
1230 n_iconv_reset(iconvd);
1231 /* TODO This causes hard-failure. We would need to have an action
1232 * TODO policy FAIL|IGNORE|SETERROR(but continue) */
1233 sz = -1;
1234 goto jleave;
1236 in = out;
1237 out.s = NULL;
1238 dflags &= ~_TD_BUFCOPY;
1240 #endif
1242 jinrest:
1243 if(inrest != NULL && inrest->l > 0){
1244 if(size == 0){
1245 in = *inrest;
1246 inrest->s = NULL;
1247 inrest->l = 0;
1248 }else{
1249 out.s = n_alloc(in.l + inrest->l + 1);
1250 memcpy(out.s, inrest->s, inrest->l);
1251 if(in.l > 0)
1252 memcpy(&out.s[inrest->l], in.s, in.l);
1253 if(in.s != ptr)
1254 n_free(in.s);
1255 (in.s = out.s)[in.l += inrest->l] = '\0';
1256 inrest->l = 0;
1257 out.s = NULL;
1259 dflags &= ~_TD_BUFCOPY;
1262 jconvert:
1263 __mimemw_sig = 0;
1264 __mimemw_opipe = safe_signal(SIGPIPE, &__mimemw_onsig);
1265 if (sigsetjmp(__mimemw_actjmp, 1))
1266 goto jleave;
1268 switch (convert) {
1269 case CONV_FROMQP:
1270 if(!qp_decode_part(&out, &in, outrest, inrest)){
1271 n_err(_("Invalid Quoted-Printable encoding ignored\n"));
1272 sz = 0; /* TODO sz = -1 stops outer levels! */
1273 break;
1275 goto jqpb64_dec;
1276 case CONV_TOQP:
1277 if(qp_encode(&out, &in, QP_NONE) == NULL){
1278 sz = 0; /* TODO sz = -1 stops outer levels! */
1279 break;
1281 goto jqpb64_enc;
1282 case CONV_8BIT:
1283 sz = quoteflt_push(qf, in.s, in.l);
1284 break;
1285 case CONV_FROMB64:
1286 if(!b64_decode_part(&out, &in, outrest, inrest))
1287 goto jeb64;
1288 outrest = NULL;
1289 if(0){
1290 /* FALLTHRU */
1291 case CONV_FROMB64_T:
1292 if(!b64_decode_part(&out, &in, outrest, inrest)){
1293 jeb64:
1294 n_err(_("Invalid Base64 encoding ignored\n"));
1295 sz = 0; /* TODO sz = -1 stops outer levels! */
1296 break;
1299 jqpb64_dec:
1300 if ((sz = out.l) != 0) {
1301 ui32_t opl = qf->qf_pfix_len;
1302 sz = _fwrite_td(&out, (dflags & ~_TD_BUFCOPY), outrest, qf);
1303 qf->qf_pfix_len = opl;
1305 break;
1306 case CONV_TOB64:
1307 /* TODO hack which is necessary unless this is a filter based approach
1308 * TODO and each filter has its own buffer (as necessary): we must not
1309 * TODO pass through a number of bytes which causes padding, otherwise we
1310 * TODO produce multiple adjacent base64 streams, and that is not treated
1311 * TODO in the same relaxed fashion like completely bogus bytes by at
1312 * TODO least mutt and OpenSSL. So we need an expensive workaround
1313 * TODO unless we have input->iconv->base64 filter chain as such!! :( */
1314 if(size != 0 && /* for Coverity, else assert() */ inrest != NULL){
1315 if(in.l > B64_ENCODE_INPUT_PER_LINE){
1316 size_t i;
1318 i = in.l % B64_ENCODE_INPUT_PER_LINE;
1319 in.l -= i;
1321 if(i != 0){
1322 assert(inrest->l == 0);
1323 inrest->s = n_realloc(inrest->s, i +1);
1324 memcpy(inrest->s, &in.s[in.l], i);
1325 inrest->s[inrest->l = i] = '\0';
1327 }else if(in.l < B64_ENCODE_INPUT_PER_LINE){
1328 inrest->s = n_realloc(inrest->s, in.l +1);
1329 memcpy(inrest->s, in.s, in.l);
1330 inrest->s[inrest->l = in.l] = '\0';
1331 in.l = 0;
1332 sz = 0;
1333 break;
1336 if(b64_encode(&out, &in, B64_LF | B64_MULTILINE) == NULL){
1337 sz = -1;
1338 break;
1340 jqpb64_enc:
1341 sz = fwrite(out.s, sizeof *out.s, out.l, f);
1342 if (sz != (ssize_t)out.l)
1343 sz = -1;
1344 break;
1345 case CONV_FROMHDR:
1346 mime_fromhdr(&in, &out, TD_ISPR | TD_ICONV | (dflags & TD_DELCTRL));
1347 sz = quoteflt_push(qf, out.s, out.l);
1348 break;
1349 case CONV_TOHDR:
1350 sz = mime_write_tohdr(&in, f, NULL, a_MIME_SH_NONE);
1351 break;
1352 case CONV_TOHDR_A:{
1353 size_t col;
1355 if(dflags & _TD_BUFCOPY){
1356 n_str_dup(&out, &in);
1357 in = out;
1358 out.s = NULL;
1359 dflags &= ~_TD_BUFCOPY;
1361 col = 0;
1362 sz = mime_write_tohdr_a(&in, f, &col);
1363 } break;
1364 default:
1365 sz = _fwrite_td(&in, dflags, NULL, qf);
1366 break;
1369 jleave:
1370 if (out.s != NULL)
1371 free(out.s);
1372 if (in.s != ptr)
1373 free(in.s);
1374 safe_signal(SIGPIPE, __mimemw_opipe);
1375 if (__mimemw_sig != 0)
1376 n_raise(__mimemw_sig);
1377 NYD_LEAVE;
1378 return sz;
1381 /* s-it-mode */