make-release.txt: get rid of redundancies (come in via cmdline)
[s-mailx.git] / mime.c
blob680e2ecffacae712cf48fe55cbe7f848713450c4
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,
71 bool_t failiconv, enum tdflags flags,
72 struct str *outrest, struct quoteflt *qf);
74 /* Convert header fields to RFC 2047 format and write to the file fo */
75 static ssize_t mime_write_tohdr(struct str *in, FILE *fo,
76 size_t *colp, enum a_mime_structure_hack msh);
78 /* Write len characters of the passed string to the passed file, doing charset
79 * and header conversion */
81 /* Write an address to a header field */
82 static ssize_t mime_write_tohdr_a(struct str *in, FILE *f,
83 size_t *colp);
84 #ifdef HAVE_ICONV
85 static ssize_t a_mime__convhdra(struct str *inp, FILE *fp, size_t *colp,
86 enum a_mime_structure_hack msh);
87 #else
88 # define a_mime__convhdra(S,F,C,MSH) mime_write_tohdr(S, F, C, MSH)
89 #endif
91 /* Append to buf, handling resizing */
92 static void _append_str(char **buf, size_t *sz, size_t *pos,
93 char const *str, size_t len);
94 static void _append_conv(char **buf, size_t *sz, size_t *pos,
95 char const *str, size_t len);
97 #ifdef HAVE_ICONV
98 static bool_t
99 _has_highbit(char const *s)
101 bool_t rv = TRU1;
102 NYD_ENTER;
104 if (s) {
106 if ((ui8_t)*s & 0x80)
107 goto jleave;
108 while (*s++ != '\0');
110 rv = FAL0;
111 jleave:
112 NYD_LEAVE;
113 return rv;
116 static bool_t
117 _name_highbit(struct name *np)
119 bool_t rv = TRU1;
120 NYD_ENTER;
122 while (np) {
123 if (_has_highbit(np->n_name) || _has_highbit(np->n_fullname))
124 goto jleave;
125 np = np->n_flink;
127 rv = FAL0;
128 jleave:
129 NYD_LEAVE;
130 return rv;
132 #endif /* HAVE_ICONV */
134 static sigjmp_buf __mimefwtd_actjmp; /* TODO someday.. */
135 static int __mimefwtd_sig; /* TODO someday.. */
136 static sighandler_type __mimefwtd_opipe;
137 static void
138 __mimefwtd_onsig(int sig) /* TODO someday, we won't need it no more */
140 NYD_X; /* Signal handler */
141 __mimefwtd_sig = sig;
142 siglongjmp(__mimefwtd_actjmp, 1);
145 static ssize_t
146 _fwrite_td(struct str const *input, bool_t failiconv, enum tdflags flags,
147 struct str *outrest, struct quoteflt *qf)
149 /* TODO note: after send/MIME layer rewrite we will have a string pool
150 * TODO so that memory allocation count drops down massively; for now,
151 * TODO v14.* that is, we pay a lot & heavily depend on the allocator */
152 /* TODO well if we get a broken pipe here, and it happens to
153 * TODO happen pretty easy when sleeping in a full pipe buffer,
154 * TODO then the current codebase performs longjump away;
155 * TODO this leaves memory leaks behind ('think up to 3 per,
156 * TODO dep. upon alloca availability). For this to be fixed
157 * TODO we either need to get rid of the longjmp()s (tm) or
158 * TODO the storage must come from the outside or be tracked
159 * TODO in a carrier struct. Best both. But storage reuse
160 * TODO would be a bigbig win besides */
161 /* *input* _may_ point to non-modifyable buffer; but even then it only
162 * needs to be dup'ed away if we have to transform the content */
163 struct str in, out;
164 ssize_t rv;
165 NYD_ENTER;
166 n_UNUSED(failiconv);
167 n_UNUSED(outrest);
169 in = *input;
170 out.s = NULL;
171 out.l = 0;
173 #ifdef HAVE_ICONV
174 if ((flags & TD_ICONV) && iconvd != (iconv_t)-1) {
175 int err;
176 char *buf;
178 buf = NULL;
180 if (outrest != NULL && outrest->l > 0) {
181 in.l = outrest->l + input->l;
182 in.s = buf = smalloc(in.l +1);
183 memcpy(in.s, outrest->s, outrest->l);
184 memcpy(&in.s[outrest->l], input->s, input->l);
185 outrest->l = 0;
188 rv = 0;
189 if((err = n_iconv_str(iconvd,
190 (failiconv ? n_ICONV_NONE : n_ICONV_UNIDEFAULT),
191 &out, &in, &in)) != 0){
192 if(err != n_ERR_INVAL)
193 n_iconv_reset(iconvd);
195 if(outrest != NULL && in.l > 0){
196 /* Incomplete multibyte at EOF is special xxx _INVAL? */
197 if (flags & _TD_EOF) {
198 out.s = srealloc(out.s, out.l + sizeof(n_unirepl));
199 if(n_psonce & n_PSO_UNICODE){
200 memcpy(&out.s[out.l], n_unirepl, sizeof(n_unirepl) -1);
201 out.l += sizeof(n_unirepl) -1;
202 }else
203 out.s[out.l++] = '?';
204 } else
205 n_str_add(outrest, &in);
206 }else
207 rv = -1;
209 in = out;
210 out.l = 0;
211 out.s = NULL;
212 flags &= ~_TD_BUFCOPY;
214 if(buf != NULL)
215 free(buf);
216 if(rv < 0)
217 goto jleave;
218 }else
219 #endif /* HAVE_ICONV */
220 /* Else, if we will modify the data bytes and thus introduce the potential
221 * of messing up multibyte sequences which become splitted over buffer
222 * boundaries TODO and unless we don't have our filter chain which will
223 * TODO make these hacks go by, buffer data until we see a NL */
224 if((flags & (TD_ISPR | TD_DELCTRL)) && outrest != NULL &&
225 #ifdef HAVE_ICONV
226 iconvd == (iconv_t)-1 &&
227 #endif
228 (!(flags & _TD_EOF) || outrest->l > 0)
230 size_t i;
231 char *cp;
233 for (cp = &in.s[in.l]; cp > in.s && cp[-1] != '\n'; --cp)
235 i = PTR2SIZE(cp - in.s);
237 if (i != in.l) {
238 if (i > 0) {
239 n_str_assign_buf(outrest, cp, in.l - i);
240 cp = smalloc(i +1);
241 memcpy(cp, in.s, in.l = i);
242 (in.s = cp)[in.l = i] = '\0';
243 flags &= ~_TD_BUFCOPY;
244 } else {
245 n_str_add_buf(outrest, input->s, input->l);
246 rv = 0;
247 goto jleave;
252 if (flags & TD_ISPR)
253 makeprint(&in, &out);
254 else if (flags & _TD_BUFCOPY)
255 n_str_dup(&out, &in);
256 else
257 out = in;
258 if (flags & TD_DELCTRL)
259 out.l = delctrl(out.s, out.l);
261 __mimefwtd_sig = 0;
262 __mimefwtd_opipe = safe_signal(SIGPIPE, &__mimefwtd_onsig);
263 if (sigsetjmp(__mimefwtd_actjmp, 1)) {
264 rv = 0;
265 goto j__sig;
268 rv = quoteflt_push(qf, out.s, out.l);
270 j__sig:
271 if (out.s != in.s)
272 free(out.s);
273 if (in.s != input->s)
274 free(in.s);
275 safe_signal(SIGPIPE, __mimefwtd_opipe);
276 if (__mimefwtd_sig != 0)
277 n_raise(__mimefwtd_sig);
278 jleave:
279 NYD_LEAVE;
280 return rv;
283 static ssize_t
284 mime_write_tohdr(struct str *in, FILE *fo, size_t *colp,
285 enum a_mime_structure_hack msh)
287 /* TODO mime_write_tohdr(): we don't know the name of our header->maxcol..
288 * TODO MIME/send layer rewrite: more available state!!
289 * TODO Because of this we cannot make a difference in between structured
290 * TODO and unstructured headers (RFC 2047, 5. (2))
291 * TODO This means, e.g., that this gets called multiple times for a
292 * TODO structured header and always starts thinking it is at column 0.
293 * TODO I.e., it may get called for only the content of a comment etc.,
294 * TODO not knowing anything of its context.
295 * TODO Instead we should have a list of header body content tokens,
296 * TODO convert them, and then dump the converted tokens, breaking lines.
297 * TODO I.e., get rid of convhdra, mime_write_tohdr_a and such...
298 * TODO Somewhen, the following should produce smooth stuff:
299 * TODO ' "Hallo\"," Dr. Backe "Bl\"ö\"d" (Gell) <ha@llöch.en>
300 * TODO "Nochm\"a\"l"<ta@tu.da>(Dümm)'
301 * TODO NOT MULTIBYTE SAFE IF AN ENCODED WORD HAS TO BE SPLITTED!
302 * TODO To be better we had to mbtowc_l() (non-std! and no locale!!) and
303 * TODO work char-wise! -> S-CText..
304 * TODO The real problem for STD compatibility is however that "in" is
305 * TODO already iconv(3) encoded to the target character set! We could
306 * TODO also solve it (very expensively!) if we would narrow down to an
307 * TODO encoded word and then iconv(3)+MIME encode in one go, in which
308 * TODO case multibyte errors could be catched! */
309 enum {
310 /* Maximum line length */
311 a_MAXCOL_NENC = MIME_LINELEN,
312 a_MAXCOL = MIME_LINELEN_RFC2047
315 struct str cout, cin;
316 enum {
317 _FIRST = 1<<0, /* Nothing written yet, start of string */
318 _MSH_NOTHING = 1<<1, /* Now, really: nothing at all has been written */
319 a_ANYENC = 1<<2, /* We have RFC 2047 anything at least once */
320 _NO_QP = 1<<3, /* No quoted-printable allowed */
321 _NO_B64 = 1<<4, /* Ditto, base64 */
322 _ENC_LAST = 1<<5, /* Last round generated encoded word */
323 _SHOULD_BEE = 1<<6, /* Avoid lines longer than SHOULD via encoding */
324 _RND_SHIFT = 7,
325 _RND_MASK = (1<<_RND_SHIFT) - 1,
326 _SPACE = 1<<(_RND_SHIFT+1), /* Leading whitespace */
327 _8BIT = 1<<(_RND_SHIFT+2), /* High bit set */
328 _ENCODE = 1<<(_RND_SHIFT+3), /* Need encoding */
329 _ENC_B64 = 1<<(_RND_SHIFT+4), /* - let it be base64 */
330 _OVERLONG = 1<<(_RND_SHIFT+5) /* Temporarily rised limit */
331 } flags;
332 char const *cset7, *cset8, *wbot, *upper, *wend, *wcur;
333 ui32_t cset7_len, cset8_len;
334 size_t col, i, j;
335 ssize_t sz;
337 NYD_ENTER;
339 cout.s = NULL, cout.l = 0;
340 cset7 = ok_vlook(charset_7bit);
341 cset7_len = (ui32_t)strlen(cset7);
342 cset8 = _CS_ITER_GET(); /* TODO MIME/send layer: iter active? iter! else */
343 cset8_len = (ui32_t)strlen(cset8);
345 flags = _FIRST;
346 if(msh != a_MIME_SH_NONE)
347 flags |= _MSH_NOTHING;
349 /* RFC 1468, "MIME Considerations":
350 * ISO-2022-JP may also be used in MIME Part 2 headers. The "B"
351 * encoding should be used with ISO-2022-JP text. */
352 /* TODO of course, our current implementation won't deal properly with
353 * TODO any stateful encoding at all... (the standard says each encoded
354 * TODO word must include all necessary reset sequences..., i.e., each
355 * TODO encoded word must be a self-contained iconv(3) life cycle) */
356 if (!asccasecmp(cset8, "iso-2022-jp") || mime_enc_target() == MIMEE_B64)
357 flags |= _NO_QP;
359 wbot = in->s;
360 upper = wbot + in->l;
361 sz = 0;
363 if(colp == NULL || (col = *colp) == 0)
364 col = sizeof("Mail-Followup-To: ") -1; /* TODO dreadful thing */
366 /* The user may specify empy quoted-strings or comments, keep them! */
367 if(wbot == upper) {
368 if(flags & _MSH_NOTHING){
369 flags &= ~_MSH_NOTHING;
370 putc((msh == a_MIME_SH_COMMENT ? '(' : '"'), fo);
371 sz = 1;
372 ++col;
374 } else for (; wbot < upper; flags &= ~_FIRST, wbot = wend) {
375 flags &= _RND_MASK;
376 wcur = wbot;
377 while (wcur < upper && whitechar(*wcur)) {
378 flags |= _SPACE;
379 ++wcur;
382 /* Any occurrence of whitespace resets prevention of lines >SHOULD via
383 * enforced encoding (xxx SHOULD, but.. encoding is expensive!!) */
384 if (flags & _SPACE)
385 flags &= ~_SHOULD_BEE;
387 /* Data ends with WS - dump it and done.
388 * Also, if we have seen multiple successive whitespace characters, then
389 * if there was no encoded word last, i.e., if we can simply take them
390 * over to the output as-is, keep one WS for possible later separation
391 * purposes and simply print the others as-is, directly! */
392 if (wcur == upper) {
393 wend = wcur;
394 goto jnoenc_putws;
396 if ((flags & (_ENC_LAST | _SPACE)) == _SPACE && wcur - wbot > 1) {
397 wend = wcur - 1;
398 goto jnoenc_putws;
401 /* Skip over a word to next non-whitespace, keep track along the way
402 * whether our 7-bit charset suffices to represent the data */
403 for (wend = wcur; wend < upper; ++wend) {
404 if (whitechar(*wend))
405 break;
406 if ((uc_i)*wend & 0x80)
407 flags |= _8BIT;
410 /* Decide whether the range has to become encoded or not */
411 i = PTR2SIZE(wend - wcur);
412 j = mime_enc_mustquote(wcur, i, MIMEEF_ISHEAD);
413 /* If it just cannot fit on a SHOULD line length, force encode */
414 if (i > a_MAXCOL_NENC) {
415 flags |= _SHOULD_BEE; /* (Sigh: SHOULD only, not MUST..) */
416 goto j_beejump;
418 if ((flags & _SHOULD_BEE) || j > 0) {
419 j_beejump:
420 flags |= _ENCODE;
421 /* Use base64 if requested or more than 50% -37.5-% of the bytes of
422 * the string need to be encoded */
423 if ((flags & _NO_QP) || j >= i >> 1)/*(i >> 2) + (i >> 3))*/
424 flags |= _ENC_B64;
426 DBG( if (flags & _8BIT) assert(flags & _ENCODE); )
428 if (!(flags & _ENCODE)) {
429 /* Encoded word produced, but no linear whitespace for necessary RFC
430 * 2047 separation? Generate artificial data (bad standard!) */
431 if ((flags & (_ENC_LAST | _SPACE)) == _ENC_LAST) {
432 if (col >= a_MAXCOL) {
433 putc('\n', fo);
434 ++sz;
435 col = 0;
437 if(flags & _MSH_NOTHING){
438 flags &= ~_MSH_NOTHING;
439 putc((msh == a_MIME_SH_COMMENT ? '(' : '"'), fo);
440 ++sz;
441 ++col;
443 putc(' ', fo);
444 ++sz;
445 ++col;
448 jnoenc_putws:
449 flags &= ~_ENC_LAST;
451 /* todo No effort here: (1) v15.0 has to bring complete rewrite,
452 * todo (2) the standard is braindead and (3) usually this is one
453 * todo word only, and why be smarter than the standard? */
454 jnoenc_retry:
455 i = PTR2SIZE(wend - wbot);
456 if (i + col + ((flags & _MSH_NOTHING) != 0) <=
457 (flags & _OVERLONG ? MIME_LINELEN_MAX
458 : (flags & a_ANYENC ? a_MAXCOL : a_MAXCOL_NENC))) {
459 if(flags & _MSH_NOTHING){
460 flags &= ~_MSH_NOTHING;
461 putc((msh == a_MIME_SH_COMMENT ? '(' : '"'), fo);
462 ++sz;
463 ++col;
465 i = fwrite(wbot, sizeof *wbot, i, fo);
466 sz += i;
467 col += i;
468 continue;
471 /* Doesn't fit, try to break the line first; */
472 if (col > 1) {
473 putc('\n', fo);
474 if (whitechar(*wbot)) {
475 putc((uc_i)*wbot, fo);
476 ++wbot;
477 } else
478 putc(' ', fo); /* Bad standard: artificial data! */
479 sz += 2;
480 col = 1;
481 if(flags & _MSH_NOTHING){
482 flags &= ~_MSH_NOTHING;
483 putc((msh == a_MIME_SH_COMMENT ? '(' : '"'), fo);
484 ++sz;
485 ++col;
487 flags |= _OVERLONG;
488 goto jnoenc_retry;
491 /* It is so long that it needs to be broken, effectively causing
492 * artificial spaces to be inserted (bad standard), yuck */
493 /* todo This is not multibyte safe, as above; and completely stupid
494 * todo P.S.: our _SHOULD_BEE prevents these cases in the meanwhile */
495 /* FIXME n_PSO_UNICODE and parse using UTF-8 sync possibility! */
496 wcur = wbot + MIME_LINELEN_MAX - 8;
497 while (wend > wcur)
498 wend -= 4;
499 goto jnoenc_retry;
500 } else {
501 /* Encoding to encoded word(s); deal with leading whitespace, place
502 * a separator first as necessary: encoded words must always be
503 * separated from text and other encoded words with linear WS.
504 * And if an encoded word was last, intermediate whitespace must
505 * also be encoded, otherwise it would get stripped away! */
506 wcur = n_UNCONST(n_empty);
507 if ((flags & (_ENC_LAST | _SPACE)) != _SPACE) {
508 /* Reinclude whitespace */
509 flags &= ~_SPACE;
510 /* We don't need to place a separator at the very beginning */
511 if (!(flags & _FIRST))
512 wcur = n_UNCONST(" ");
513 } else
514 wcur = wbot++;
516 flags |= a_ANYENC | _ENC_LAST;
517 n_pstate |= n_PS_HEADER_NEEDED_MIME;
519 /* RFC 2047:
520 * An 'encoded-word' may not be more than 75 characters long,
521 * including 'charset', 'encoding', 'encoded-text', and
522 * delimiters. If it is desirable to encode more text than will
523 * fit in an 'encoded-word' of 75 characters, multiple
524 * 'encoded-word's (separated by CRLF SPACE) may be used.
526 * While there is no limit to the length of a multiple-line
527 * header field, each line of a header field that contains one
528 * or more 'encoded-word's is limited to 76 characters */
529 jenc_retry:
530 cin.s = n_UNCONST(wbot);
531 cin.l = PTR2SIZE(wend - wbot);
533 /* C99 */{
534 struct str *xout;
536 if(flags & _ENC_B64)
537 xout = b64_encode(&cout, &cin, B64_ISHEAD | B64_ISENCWORD);
538 else
539 xout = qp_encode(&cout, &cin, QP_ISHEAD | QP_ISENCWORD);
540 if(xout == NULL){
541 sz = -1;
542 break;
544 j = xout->l;
546 /* (Avoid trigraphs in the RFC 2047 placeholder..) */
547 i = j + (flags & _8BIT ? cset8_len : cset7_len) + sizeof("=!!B!!=") -1;
548 if (*wcur != '\0')
549 ++i;
551 jenc_retry_same:
552 /* Unfortunately RFC 2047 explicitly disallows encoded words to be
553 * longer (just like RFC 5322's "a line SHOULD fit in 78 but MAY be
554 * 998 characters long"), so we cannot use the _OVERLONG mechanism,
555 * even though all tested mailers seem to support it */
556 if (i + col <= (/*flags & _OVERLONG ? MIME_LINELEN_MAX :*/ a_MAXCOL)) {
557 if(flags & _MSH_NOTHING){
558 flags &= ~_MSH_NOTHING;
559 putc((msh == a_MIME_SH_COMMENT ? '(' : '"'), fo);
560 ++sz;
561 ++col;
563 fprintf(fo, "%.1s=?%s?%c?%.*s?=",
564 wcur, (flags & _8BIT ? cset8 : cset7),
565 (flags & _ENC_B64 ? 'B' : 'Q'),
566 (int)cout.l, cout.s);
567 sz += i;
568 col += i;
569 continue;
572 /* Doesn't fit, try to break the line first */
573 /* TODO I've commented out the _FIRST test since we (1) cannot do
574 * TODO _OVERLONG since (MUAs support but) the standard disallows,
575 * TODO and because of our iconv problem i prefer an empty first line
576 * TODO in favour of a possibly messed up multibytes character. :-( */
577 if (col > 1 /* TODO && !(flags & _FIRST)*/) {
578 putc('\n', fo);
579 sz += 2;
580 col = 1;
581 if (!(flags & _SPACE)) {
582 putc(' ', fo);
583 wcur = n_UNCONST(n_empty);
584 /*flags |= _OVERLONG;*/
585 goto jenc_retry_same;
586 } else {
587 putc((uc_i)*wcur, fo);
588 if (whitechar(*(wcur = wbot)))
589 ++wbot;
590 else {
591 flags &= ~_SPACE;
592 wcur = n_UNCONST(n_empty);
594 /*flags &= ~_OVERLONG;*/
595 goto jenc_retry;
599 /* It is so long that it needs to be broken, effectively causing
600 * artificial data to be inserted (bad standard), yuck */
601 /* todo This is not multibyte safe, as above */
602 /*if (!(flags & _OVERLONG)) { Mechanism explicitly forbidden by 2047
603 flags |= _OVERLONG;
604 goto jenc_retry;
607 /* FIXME n_PSO_UNICODE and parse using UTF-8 sync possibility! */
608 i = PTR2SIZE(wend - wbot) + !!(flags & _SPACE);
609 j = 3 + !(flags & _ENC_B64);
610 for (;;) {
611 wend -= j;
612 i -= j;
613 /* (Note the problem most likely is the transfer-encoding blow,
614 * which is why we test this *after* the decrements.. */
615 if (i <= a_MAXCOL)
616 break;
618 goto jenc_retry;
622 if(!(flags & _MSH_NOTHING) && msh != a_MIME_SH_NONE){
623 putc((msh == a_MIME_SH_COMMENT ? ')' : '"'), fo);
624 ++sz;
625 ++col;
628 if (cout.s != NULL)
629 free(cout.s);
631 if(colp != NULL)
632 *colp = col;
633 NYD_LEAVE;
634 return sz;
637 static ssize_t
638 mime_write_tohdr_a(struct str *in, FILE *f, size_t *colp)
640 struct str xin;
641 size_t i;
642 char const *cp, *lastcp;
643 ssize_t sz, x;
644 NYD_ENTER;
646 in->s[in->l] = '\0';
647 lastcp = in->s;
648 if((cp = routeaddr(in->s)) != NULL && cp > lastcp) {
649 xin.s = in->s;
650 xin.l = PTR2SIZE(cp - in->s);
651 if ((sz = mime_write_tohdr_a(&xin, f, colp)) < 0)
652 goto jleave;
653 xin.s[xin.l] = '<';
654 lastcp = cp;
655 } else {
656 cp = in->s;
657 sz = 0;
660 for( ; *cp != '\0'; ++cp){
661 switch(*cp){
662 case '(':
663 i = PTR2SIZE(cp - lastcp);
664 if(i > 0){
665 if(fwrite(lastcp, 1, i, f) != i)
666 goto jerr;
667 sz += i;
669 lastcp = ++cp;
670 cp = skip_comment(cp);
671 if(cp > lastcp)
672 --cp;
673 /* We want to keep empty comments, too! */
674 xin.s = n_UNCONST(lastcp);
675 xin.l = PTR2SIZE(cp - lastcp);
676 if ((x = a_mime__convhdra(&xin, f, colp, a_MIME_SH_COMMENT)) < 0)
677 goto jerr;
678 sz += x;
679 lastcp = &cp[1];
680 break;
681 case '"':
682 i = PTR2SIZE(cp - lastcp);
683 if(i > 0){
684 if(fwrite(lastcp, 1, i, f) != i)
685 goto jerr;
686 sz += i;
688 for(lastcp = ++cp; *cp != '\0'; ++cp){
689 if(*cp == '"')
690 break;
691 if(*cp == '\\' && cp[1] != '\0')
692 ++cp;
694 /* We want to keep empty quoted-strings, too! */
695 xin.s = n_UNCONST(lastcp);
696 xin.l = PTR2SIZE(cp - lastcp);
697 if((x = a_mime__convhdra(&xin, f, colp, a_MIME_SH_QUOTE)) < 0)
698 goto jerr;
699 sz += x;
700 ++sz;
701 lastcp = &cp[1];
702 break;
706 i = PTR2SIZE(cp - lastcp);
707 if(i > 0){
708 if(fwrite(lastcp, 1, i, f) != i)
709 goto jerr;
710 sz += i;
712 jleave:
713 NYD_LEAVE;
714 return sz;
715 jerr:
716 sz = -1;
717 goto jleave;
720 #ifdef HAVE_ICONV
721 static ssize_t
722 a_mime__convhdra(struct str *inp, FILE *fp, size_t *colp,
723 enum a_mime_structure_hack msh){
724 struct str ciconv;
725 ssize_t rv;
726 NYD_ENTER;
728 rv = 0;
729 ciconv.s = NULL;
731 if(inp->l > 0 && iconvd != (iconv_t)-1){
732 ciconv.l = 0;
733 if(n_iconv_str(iconvd, n_ICONV_NONE, &ciconv, inp, NULL) != 0){
734 n_iconv_reset(iconvd);
735 goto jleave;
737 *inp = ciconv;
740 rv = mime_write_tohdr(inp, fp, colp, msh);
741 jleave:
742 if(ciconv.s != NULL)
743 free(ciconv.s);
744 NYD_LEAVE;
745 return rv;
747 #endif /* HAVE_ICONV */
749 static void
750 _append_str(char **buf, size_t *sz, size_t *pos, char const *str, size_t len)
752 NYD_ENTER;
753 *buf = srealloc(*buf, *sz += len);
754 memcpy(&(*buf)[*pos], str, len);
755 *pos += len;
756 NYD_LEAVE;
759 static void
760 _append_conv(char **buf, size_t *sz, size_t *pos, char const *str, size_t len)
762 struct str in, out;
763 NYD_ENTER;
765 in.s = n_UNCONST(str);
766 in.l = len;
767 mime_fromhdr(&in, &out, TD_ISPR | TD_ICONV);
768 _append_str(buf, sz, pos, out.s, out.l);
769 free(out.s);
770 NYD_LEAVE;
773 FL bool_t
774 charset_iter_reset(char const *a_charset_to_try_first) /* TODO elim. dups! */
776 char const *sarr[3];
777 size_t sarrl[3], len;
778 char *cp;
779 NYD_ENTER;
780 n_UNUSED(a_charset_to_try_first);
782 #ifdef HAVE_ICONV
783 sarr[2] = ok_vlook(CHARSET_8BIT_OKEY);
785 if(a_charset_to_try_first != NULL && strcmp(a_charset_to_try_first, sarr[2]))
786 sarr[0] = a_charset_to_try_first;
787 else
788 sarr[0] = NULL;
790 if((sarr[1] = ok_vlook(sendcharsets)) == NULL &&
791 ok_blook(sendcharsets_else_ttycharset)){
792 cp = n_UNCONST(ok_vlook(ttycharset));
793 if(strcmp(cp, sarr[2]) && (sarr[0] == NULL || strcmp(cp, sarr[0])))
794 sarr[1] = cp;
796 #else
797 sarr[2] = ok_vlook(ttycharset);
798 #endif
800 sarrl[2] = len = strlen(sarr[2]);
801 #ifdef HAVE_ICONV
802 if ((cp = n_UNCONST(sarr[1])) != NULL)
803 len += (sarrl[1] = strlen(cp));
804 else
805 sarrl[1] = 0;
806 if ((cp = n_UNCONST(sarr[0])) != NULL)
807 len += (sarrl[0] = strlen(cp));
808 else
809 sarrl[0] = 0;
810 #endif
812 _cs_iter_base = cp = salloc(len + 1 + 1 +1);
814 #ifdef HAVE_ICONV
815 if ((len = sarrl[0]) != 0) {
816 memcpy(cp, sarr[0], len);
817 cp[len] = ',';
818 cp += ++len;
820 if ((len = sarrl[1]) != 0) {
821 memcpy(cp, sarr[1], len);
822 cp[len] = ',';
823 cp += ++len;
825 #endif
826 len = sarrl[2];
827 memcpy(cp, sarr[2], len);
828 cp[len] = '\0';
830 _CS_ITER_STEP();
831 NYD_LEAVE;
832 return (_cs_iter != NULL);
835 FL bool_t
836 charset_iter_next(void)
838 bool_t rv;
839 NYD_ENTER;
841 _CS_ITER_STEP();
842 rv = (_cs_iter != NULL);
843 NYD_LEAVE;
844 return rv;
847 FL bool_t
848 charset_iter_is_valid(void)
850 bool_t rv;
851 NYD_ENTER;
853 rv = (_cs_iter != NULL);
854 NYD_LEAVE;
855 return rv;
858 FL char const *
859 charset_iter(void)
861 char const *rv;
862 NYD_ENTER;
864 rv = _cs_iter;
865 NYD_LEAVE;
866 return rv;
869 FL char const *
870 charset_iter_or_fallback(void)
872 char const *rv;
873 NYD_ENTER;
875 rv = _CS_ITER_GET();
876 NYD_LEAVE;
877 return rv;
880 FL void
881 charset_iter_recurse(char *outer_storage[2]) /* TODO LEGACY FUN, REMOVE */
883 NYD_ENTER;
884 outer_storage[0] = _cs_iter_base;
885 outer_storage[1] = _cs_iter;
886 NYD_LEAVE;
889 FL void
890 charset_iter_restore(char *outer_storage[2]) /* TODO LEGACY FUN, REMOVE */
892 NYD_ENTER;
893 _cs_iter_base = outer_storage[0];
894 _cs_iter = outer_storage[1];
895 NYD_LEAVE;
898 #ifdef HAVE_ICONV
899 FL char const *
900 need_hdrconv(struct header *hp) /* TODO once only, then iter */
902 struct n_header_field *hfp;
903 char const *rv;
904 NYD_ENTER;
906 rv = NULL;
908 if((hfp = hp->h_user_headers) != NULL)
909 do if(_has_highbit(hfp->hf_dat + hfp->hf_nl +1))
910 goto jneeds;
911 while((hfp = hfp->hf_next) != NULL);
913 if((hfp = hp->h_custom_headers) != NULL ||
914 (hp->h_custom_headers = hfp = n_customhdr_query()) != NULL)
915 do if(_has_highbit(hfp->hf_dat + hfp->hf_nl +1))
916 goto jneeds;
917 while((hfp = hfp->hf_next) != NULL);
919 if (hp->h_mft != NULL) {
920 if (_name_highbit(hp->h_mft))
921 goto jneeds;
923 if (hp->h_from != NULL) {
924 if (_name_highbit(hp->h_from))
925 goto jneeds;
926 } else if (_has_highbit(myaddrs(NULL)))
927 goto jneeds;
928 if (hp->h_reply_to) {
929 if (_name_highbit(hp->h_reply_to))
930 goto jneeds;
931 } else {
932 char const *v15compat;
934 if((v15compat = ok_vlook(replyto)) != NULL)
935 n_OBSOLETE(_("please use *reply-to*, not *replyto*"));
936 if(_has_highbit(v15compat))
937 goto jneeds;
938 if(_has_highbit(ok_vlook(reply_to)))
939 goto jneeds;
941 if (hp->h_sender) {
942 if (_name_highbit(hp->h_sender))
943 goto jneeds;
944 } else if (_has_highbit(ok_vlook(sender)))
945 goto jneeds;
947 if (_name_highbit(hp->h_to))
948 goto jneeds;
949 if (_name_highbit(hp->h_cc))
950 goto jneeds;
951 if (_name_highbit(hp->h_bcc))
952 goto jneeds;
953 if (_has_highbit(hp->h_subject))
954 jneeds:
955 rv = _CS_ITER_GET(); /* TODO MIME/send: iter active? iter! else */
956 NYD_LEAVE;
957 return rv;
959 #endif /* HAVE_ICONV */
961 FL void
962 mime_fromhdr(struct str const *in, struct str *out, enum tdflags flags)
964 /* TODO mime_fromhdr(): is called with strings that contain newlines;
965 * TODO this is the usual newline problem all around the codebase;
966 * TODO i.e., if we strip it, then the display misses it ;>
967 * TODO this is why it is so messy and why S-nail v14.2 plus additional
968 * TODO patch for v14.5.2 (and maybe even v14.5.3 subminor) occurred, and
969 * TODO why our display reflects what is contained in the message: the 1:1
970 * TODO relationship of message content and display!
971 * TODO instead a header line should be decoded to what it is (a single
972 * TODO line that is) and it should be objective to the backend whether
973 * TODO it'll be folded to fit onto the display or not, e.g., for search
974 * TODO purposes etc. then the only condition we have to honour in here
975 * TODO is that whitespace in between multiple adjacent MIME encoded words
976 * TODO á la RFC 2047 is discarded; i.e.: this function should deal with
977 * TODO RFC 2047 and be renamed: mime_fromhdr() -> mime_rfc2047_decode() */
978 struct str cin, cout;
979 char *p, *op, *upper;
980 ui32_t convert, lastenc, lastoutl;
981 #ifdef HAVE_ICONV
982 char const *tcs;
983 char *cbeg;
984 iconv_t fhicd = (iconv_t)-1;
985 #endif
986 NYD_ENTER;
988 out->l = 0;
989 if (in->l == 0) {
990 *(out->s = smalloc(1)) = '\0';
991 goto jleave;
993 out->s = NULL;
995 #ifdef HAVE_ICONV
996 tcs = ok_vlook(ttycharset);
997 #endif
998 p = in->s;
999 upper = p + in->l;
1000 lastenc = lastoutl = 0;
1002 while (p < upper) {
1003 op = p;
1004 if (*p == '=' && *(p + 1) == '?') {
1005 p += 2;
1006 #ifdef HAVE_ICONV
1007 cbeg = p;
1008 #endif
1009 while (p < upper && *p != '?')
1010 ++p; /* strip charset */
1011 if (p >= upper)
1012 goto jnotmime;
1013 ++p;
1014 #ifdef HAVE_ICONV
1015 if (flags & TD_ICONV) {
1016 size_t i = PTR2SIZE(p - cbeg);
1017 char *ltag, *cs = ac_alloc(i);
1019 memcpy(cs, cbeg, --i);
1020 cs[i] = '\0';
1021 /* RFC 2231 extends the RFC 2047 character set definition in
1022 * encoded words by language tags - silently strip those off */
1023 if ((ltag = strchr(cs, '*')) != NULL)
1024 *ltag = '\0';
1026 if (fhicd != (iconv_t)-1)
1027 n_iconv_close(fhicd);
1028 fhicd = asccasecmp(cs, tcs) ? n_iconv_open(tcs, cs) : (iconv_t)-1;
1029 ac_free(cs);
1031 #endif
1032 switch (*p) {
1033 case 'B': case 'b':
1034 convert = CONV_FROMB64;
1035 break;
1036 case 'Q': case 'q':
1037 convert = CONV_FROMQP;
1038 break;
1039 default: /* invalid, ignore */
1040 goto jnotmime;
1042 if (*++p != '?')
1043 goto jnotmime;
1044 cin.s = ++p;
1045 cin.l = 1;
1046 for (;;) {
1047 if (PTRCMP(p + 1, >=, upper))
1048 goto jnotmime;
1049 if (*p++ == '?' && *p == '=')
1050 break;
1051 ++cin.l;
1053 ++p;
1054 --cin.l;
1056 cout.s = NULL;
1057 cout.l = 0;
1058 if (convert == CONV_FROMB64) {
1059 if(!b64_decode_header(&cout, &cin))
1060 n_str_assign_cp(&cout, _("[Invalid Base64 encoding]"));
1061 }else if(!qp_decode_header(&cout, &cin))
1062 n_str_assign_cp(&cout, _("[Invalid Quoted-Printable encoding]"));
1064 out->l = lastenc;
1065 #ifdef HAVE_ICONV
1066 if ((flags & TD_ICONV) && fhicd != (iconv_t)-1) {
1067 cin.s = NULL, cin.l = 0; /* XXX string pool ! */
1068 convert = n_iconv_str(fhicd, n_ICONV_UNIDEFAULT, &cin, &cout, NULL);
1069 out = n_str_add(out, &cin);
1070 if (convert) {/* n_ERR_INVAL at EOS */
1071 n_iconv_reset(fhicd);
1072 out = n_str_add_buf(out, n_qm, 1); /* TODO unicode replacement */
1074 free(cin.s);
1075 } else
1076 #endif
1077 out = n_str_add(out, &cout);
1078 lastenc = lastoutl = out->l;
1079 free(cout.s);
1080 } else
1081 jnotmime: {
1082 bool_t onlyws;
1084 p = op;
1085 onlyws = (lastenc > 0);
1086 for (;;) {
1087 if (++op == upper)
1088 break;
1089 if (op[0] == '=' && (PTRCMP(op + 1, ==, upper) || op[1] == '?'))
1090 break;
1091 if (onlyws && !blankchar(*op))
1092 onlyws = FAL0;
1095 out = n_str_add_buf(out, p, PTR2SIZE(op - p));
1096 p = op;
1097 if (!onlyws || lastoutl != lastenc)
1098 lastenc = out->l;
1099 lastoutl = out->l;
1102 out->s[out->l] = '\0';
1104 if (flags & TD_ISPR) {
1105 makeprint(out, &cout);
1106 free(out->s);
1107 *out = cout;
1109 if (flags & TD_DELCTRL)
1110 out->l = delctrl(out->s, out->l);
1111 #ifdef HAVE_ICONV
1112 if (fhicd != (iconv_t)-1)
1113 n_iconv_close(fhicd);
1114 #endif
1115 jleave:
1116 NYD_LEAVE;
1117 return;
1120 FL char *
1121 mime_fromaddr(char const *name)
1123 char const *cp, *lastcp;
1124 char *res = NULL;
1125 size_t ressz = 1, rescur = 0;
1126 NYD_ENTER;
1128 if (name == NULL)
1129 goto jleave;
1130 if (*name == '\0') {
1131 res = savestr(name);
1132 goto jleave;
1135 if ((cp = routeaddr(name)) != NULL && cp > name) {
1136 _append_conv(&res, &ressz, &rescur, name, PTR2SIZE(cp - name));
1137 lastcp = cp;
1138 } else
1139 cp = lastcp = name;
1141 for ( ; *cp; ++cp) {
1142 switch (*cp) {
1143 case '(':
1144 _append_str(&res, &ressz, &rescur, lastcp, PTR2SIZE(cp - lastcp + 1));
1145 lastcp = ++cp;
1146 cp = skip_comment(cp);
1147 if (--cp > lastcp)
1148 _append_conv(&res, &ressz, &rescur, lastcp, PTR2SIZE(cp - lastcp));
1149 lastcp = cp;
1150 break;
1151 case '"':
1152 while (*cp) {
1153 if (*++cp == '"')
1154 break;
1155 if (*cp == '\\' && cp[1] != '\0')
1156 ++cp;
1158 break;
1161 if (cp > lastcp)
1162 _append_str(&res, &ressz, &rescur, lastcp, PTR2SIZE(cp - lastcp));
1163 /* C99 */{
1164 char *x;
1166 x = res;
1167 res = savestrbuf(res, rescur);
1168 if(rescur > 0)
1169 free(x);
1171 jleave:
1172 NYD_LEAVE;
1173 return res;
1176 FL ssize_t
1177 xmime_write(char const *ptr, size_t size, FILE *f, enum conversion convert,
1178 enum tdflags dflags, struct str * volatile outrest,
1179 struct str * volatile inrest)
1181 ssize_t rv;
1182 struct quoteflt *qf;
1183 NYD_ENTER;
1185 quoteflt_reset(qf = quoteflt_dummy(), f);
1186 rv = mime_write(ptr, size, f, convert, dflags, qf, outrest, inrest);
1187 quoteflt_flush(qf);
1188 NYD_LEAVE;
1189 return rv;
1192 static sigjmp_buf __mimemw_actjmp; /* TODO someday.. */
1193 static int __mimemw_sig; /* TODO someday.. */
1194 static sighandler_type __mimemw_opipe;
1195 static void
1196 __mimemw_onsig(int sig) /* TODO someday, we won't need it no more */
1198 NYD_X; /* Signal handler */
1199 __mimemw_sig = sig;
1200 siglongjmp(__mimemw_actjmp, 1);
1203 FL ssize_t
1204 mime_write(char const *ptr, size_t size, FILE *f,
1205 enum conversion convert, enum tdflags volatile dflags,
1206 struct quoteflt *qf, struct str * volatile outrest,
1207 struct str * volatile inrest)
1209 /* TODO note: after send/MIME layer rewrite we will have a string pool
1210 * TODO so that memory allocation count drops down massively; for now,
1211 * TODO v14.0 that is, we pay a lot & heavily depend on the allocator.
1212 * TODO P.S.: furthermore all this encapsulated in filter objects instead */
1213 struct str in, out;
1214 ssize_t volatile sz;
1215 NYD_ENTER;
1217 dflags |= _TD_BUFCOPY;
1218 in.s = n_UNCONST(ptr);
1219 in.l = size;
1220 out.s = NULL;
1221 out.l = 0;
1223 if((sz = size) == 0){
1224 if(inrest != NULL && inrest->l != 0)
1225 goto jinrest;
1226 if(outrest != NULL && outrest->l != 0)
1227 goto jconvert;
1228 goto jleave;
1231 /* TODO This crap requires linewise input, then. We need a filter chain
1232 * TODO as in input->iconv->base64 where each filter can have its own
1233 * TODO buffer, with a filter->fflush() call to get rid of those! */
1234 #ifdef HAVE_ICONV
1235 if ((dflags & TD_ICONV) && iconvd != (iconv_t)-1 &&
1236 (convert == CONV_TOQP || convert == CONV_8BIT ||
1237 convert == CONV_TOB64 || convert == CONV_TOHDR)) {
1238 if (n_iconv_str(iconvd, n_ICONV_NONE, &out, &in, NULL) != 0) {
1239 n_iconv_reset(iconvd);
1240 /* TODO This causes hard-failure. We would need to have an action
1241 * TODO policy FAIL|IGNORE|SETERROR(but continue) */
1242 sz = -1;
1243 goto jleave;
1245 in = out;
1246 out.s = NULL;
1247 dflags &= ~_TD_BUFCOPY;
1249 #endif
1251 jinrest:
1252 if(inrest != NULL && inrest->l > 0){
1253 if(size == 0){
1254 in = *inrest;
1255 inrest->s = NULL;
1256 inrest->l = 0;
1257 }else{
1258 out.s = n_alloc(in.l + inrest->l + 1);
1259 memcpy(out.s, inrest->s, inrest->l);
1260 if(in.l > 0)
1261 memcpy(&out.s[inrest->l], in.s, in.l);
1262 if(in.s != ptr)
1263 n_free(in.s);
1264 (in.s = out.s)[in.l += inrest->l] = '\0';
1265 inrest->l = 0;
1266 out.s = NULL;
1268 dflags &= ~_TD_BUFCOPY;
1271 jconvert:
1272 __mimemw_sig = 0;
1273 __mimemw_opipe = safe_signal(SIGPIPE, &__mimemw_onsig);
1274 if (sigsetjmp(__mimemw_actjmp, 1))
1275 goto jleave;
1277 switch (convert) {
1278 case CONV_FROMQP:
1279 if(!qp_decode_part(&out, &in, outrest, inrest)){
1280 n_err(_("Invalid Quoted-Printable encoding ignored\n"));
1281 sz = 0; /* TODO sz = -1 stops outer levels! */
1282 break;
1284 goto jqpb64_dec;
1285 case CONV_TOQP:
1286 if(qp_encode(&out, &in, QP_NONE) == NULL){
1287 sz = 0; /* TODO sz = -1 stops outer levels! */
1288 break;
1290 goto jqpb64_enc;
1291 case CONV_8BIT:
1292 sz = quoteflt_push(qf, in.s, in.l);
1293 break;
1294 case CONV_FROMB64:
1295 if(!b64_decode_part(&out, &in, outrest, inrest))
1296 goto jeb64;
1297 outrest = NULL;
1298 if(0){
1299 /* FALLTHRU */
1300 case CONV_FROMB64_T:
1301 if(!b64_decode_part(&out, &in, outrest, inrest)){
1302 jeb64:
1303 n_err(_("Invalid Base64 encoding ignored\n"));
1304 sz = 0; /* TODO sz = -1 stops outer levels! */
1305 break;
1308 jqpb64_dec:
1309 if ((sz = out.l) != 0) {
1310 ui32_t opl = qf->qf_pfix_len;
1311 sz = _fwrite_td(&out, FAL0, (dflags & ~_TD_BUFCOPY), outrest, qf);
1312 qf->qf_pfix_len = opl;
1314 break;
1315 case CONV_TOB64:
1316 /* TODO hack which is necessary unless this is a filter based approach
1317 * TODO and each filter has its own buffer (as necessary): we must not
1318 * TODO pass through a number of bytes which causes padding, otherwise we
1319 * TODO produce multiple adjacent base64 streams, and that is not treated
1320 * TODO in the same relaxed fashion like completely bogus bytes by at
1321 * TODO least mutt and OpenSSL. So we need an expensive workaround
1322 * TODO unless we have input->iconv->base64 filter chain as such!! :( */
1323 if(size != 0 && /* for Coverity, else assert() */ inrest != NULL){
1324 if(in.l > B64_ENCODE_INPUT_PER_LINE){
1325 size_t i;
1327 i = in.l % B64_ENCODE_INPUT_PER_LINE;
1328 in.l -= i;
1330 if(i != 0){
1331 assert(inrest->l == 0);
1332 inrest->s = n_realloc(inrest->s, i +1);
1333 memcpy(inrest->s, &in.s[in.l], i);
1334 inrest->s[inrest->l = i] = '\0';
1336 }else if(in.l < B64_ENCODE_INPUT_PER_LINE){
1337 inrest->s = n_realloc(inrest->s, in.l +1);
1338 memcpy(inrest->s, in.s, in.l);
1339 inrest->s[inrest->l = in.l] = '\0';
1340 in.l = 0;
1341 sz = 0;
1342 break;
1345 if(b64_encode(&out, &in, B64_LF | B64_MULTILINE) == NULL){
1346 sz = -1;
1347 break;
1349 jqpb64_enc:
1350 sz = fwrite(out.s, sizeof *out.s, out.l, f);
1351 if (sz != (ssize_t)out.l)
1352 sz = -1;
1353 break;
1354 case CONV_FROMHDR:
1355 mime_fromhdr(&in, &out, TD_ISPR | TD_ICONV | (dflags & TD_DELCTRL));
1356 sz = quoteflt_push(qf, out.s, out.l);
1357 break;
1358 case CONV_TOHDR:
1359 sz = mime_write_tohdr(&in, f, NULL, a_MIME_SH_NONE);
1360 break;
1361 case CONV_TOHDR_A:{
1362 size_t col;
1364 if(dflags & _TD_BUFCOPY){
1365 n_str_dup(&out, &in);
1366 in = out;
1367 out.s = NULL;
1368 dflags &= ~_TD_BUFCOPY;
1370 col = 0;
1371 sz = mime_write_tohdr_a(&in, f, &col);
1372 } break;
1373 default:
1374 sz = _fwrite_td(&in, TRU1, dflags, NULL, qf);
1375 break;
1378 jleave:
1379 if (out.s != NULL)
1380 free(out.s);
1381 if (in.s != ptr)
1382 free(in.s);
1383 safe_signal(SIGPIPE, __mimemw_opipe);
1384 if (__mimemw_sig != 0)
1385 n_raise(__mimemw_sig);
1386 NYD_LEAVE;
1387 return sz;
1390 /* s-it-mode */