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>.
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
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
43 #ifndef HAVE_AMALGAMATION
47 /* Don't ask, but it keeps body and soul together */
48 enum a_mime_structure_hack
{
54 static char *_cs_iter_base
, *_cs_iter
;
56 # define _CS_ITER_GET() \
57 ((_cs_iter != NULL) ? _cs_iter : ok_vlook(CHARSET_8BIT_OKEY))
59 # define _CS_ITER_GET() ((_cs_iter != NULL) ? _cs_iter : ok_vlook(ttycharset))
61 #define _CS_ITER_STEP() _cs_iter = n_strsep(&_cs_iter_base, ',', TRU1)
63 /* Is 7-bit enough? */
65 static bool_t
_has_highbit(char const *s
);
66 static bool_t
_name_highbit(struct name
*np
);
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
,
84 static ssize_t
a_mime__convhdra(struct str
*inp
, FILE *fp
, size_t *colp
,
85 enum a_mime_structure_hack msh
);
87 # define a_mime__convhdra(S,F,C,MSH) mime_write_tohdr(S, F, C, MSH)
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
);
98 _has_highbit(char const *s
)
105 if ((ui8_t
)*s
& 0x80)
107 while (*s
++ != '\0');
116 _name_highbit(struct name
*np
)
122 if (_has_highbit(np
->n_name
) || _has_highbit(np
->n_fullname
))
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
;
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);
145 _fwrite_td(struct str
const *input
, enum tdflags flags
, struct str
*outrest
,
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 */
172 if ((flags
& TD_ICONV
) && iconvd
!= (iconv_t
)-1) {
175 if (outrest
!= NULL
&& outrest
->l
> 0) {
176 in
.l
= outrest
->l
+ input
->l
;
177 in
.s
= buf
= smalloc(in
.l
+1);
178 memcpy(in
.s
, outrest
->s
, outrest
->l
);
179 memcpy(&in
.s
[outrest
->l
], input
->s
, input
->l
);
183 if (n_iconv_str(iconvd
, n_ICONV_UNIDEFAULT
, &out
, &in
, &in
) != 0 &&
184 outrest
!= NULL
&& in
.l
> 0) {
185 n_iconv_reset(iconvd
);
186 /* Incomplete multibyte at EOF is special */
187 if (flags
& _TD_EOF
) {
188 out
.s
= srealloc(out
.s
, out
.l
+ sizeof(n_unirepl
));
189 if(n_psonce
& n_PSO_UNICODE
){
190 memcpy(&out
.s
[out
.l
], n_unirepl
, sizeof(n_unirepl
) -1);
191 out
.l
+= sizeof(n_unirepl
) -1;
193 out
.s
[out
.l
++] = '?';
195 n_str_add(outrest
, &in
);
200 flags
&= ~_TD_BUFCOPY
;
206 /* Else, if we will modify the data bytes and thus introduce the potential
207 * of messing up multibyte sequences which become splitted over buffer
208 * boundaries TODO and unless we don't have our filter chain which will
209 * TODO make these hacks go by, buffer data until we see a NL */
210 if((flags
& (TD_ISPR
| TD_DELCTRL
)) && outrest
!= NULL
&&
212 iconvd
== (iconv_t
)-1 &&
214 (!(flags
& _TD_EOF
) || outrest
->l
> 0)
219 for (cp
= &in
.s
[in
.l
]; cp
> in
.s
&& cp
[-1] != '\n'; --cp
)
221 i
= PTR2SIZE(cp
- in
.s
);
225 n_str_assign_buf(outrest
, cp
, in
.l
- i
);
227 memcpy(cp
, in
.s
, in
.l
= i
);
228 (in
.s
= cp
)[in
.l
= i
] = '\0';
229 flags
&= ~_TD_BUFCOPY
;
231 n_str_add_buf(outrest
, input
->s
, input
->l
);
239 makeprint(&in
, &out
);
240 else if (flags
& _TD_BUFCOPY
)
241 n_str_dup(&out
, &in
);
244 if (flags
& TD_DELCTRL
)
245 out
.l
= delctrl(out
.s
, out
.l
);
248 __mimefwtd_opipe
= safe_signal(SIGPIPE
, &__mimefwtd_onsig
);
249 if (sigsetjmp(__mimefwtd_actjmp
, 1)) {
254 rv
= quoteflt_push(qf
, out
.s
, out
.l
);
259 if (in
.s
!= input
->s
)
261 safe_signal(SIGPIPE
, __mimefwtd_opipe
);
262 if (__mimefwtd_sig
!= 0)
263 n_raise(__mimefwtd_sig
);
270 mime_write_tohdr(struct str
*in
, FILE *fo
, size_t *colp
,
271 enum a_mime_structure_hack msh
)
273 /* TODO mime_write_tohdr(): we don't know the name of our header->maxcol..
274 * TODO MIME/send layer rewrite: more available state!!
275 * TODO Because of this we cannot make a difference in between structured
276 * TODO and unstructured headers (RFC 2047, 5. (2))
277 * TODO This means, e.g., that this gets called multiple times for a
278 * TODO structured header and always starts thinking it is at column 0.
279 * TODO I.e., it may get called for only the content of a comment etc.,
280 * TODO not knowing anything of its context.
281 * TODO Instead we should have a list of header body content tokens,
282 * TODO convert them, and then dump the converted tokens, breaking lines.
283 * TODO I.e., get rid of convhdra, mime_write_tohdr_a and such...
284 * TODO Somewhen, the following should produce smooth stuff:
285 * TODO ' "Hallo\"," Dr. Backe "Bl\"ö\"d" (Gell) <ha@llöch.en>
286 * TODO "Nochm\"a\"l"<ta@tu.da>(Dümm)'
287 * TODO NOT MULTIBYTE SAFE IF AN ENCODED WORD HAS TO BE SPLITTED!
288 * TODO To be better we had to mbtowc_l() (non-std! and no locale!!) and
289 * TODO work char-wise! -> S-CText..
290 * TODO The real problem for STD compatibility is however that "in" is
291 * TODO already iconv(3) encoded to the target character set! We could
292 * TODO also solve it (very expensively!) if we would narrow down to an
293 * TODO encoded word and then iconv(3)+MIME encode in one go, in which
294 * TODO case multibyte errors could be catched! */
296 /* Maximum line length */
297 a_MAXCOL_NENC
= MIME_LINELEN
,
298 a_MAXCOL
= MIME_LINELEN_RFC2047
301 struct str cout
, cin
;
303 _FIRST
= 1<<0, /* Nothing written yet, start of string */
304 _MSH_NOTHING
= 1<<1, /* Now, really: nothing at all has been written */
305 a_ANYENC
= 1<<2, /* We have RFC 2047 anything at least once */
306 _NO_QP
= 1<<3, /* No quoted-printable allowed */
307 _NO_B64
= 1<<4, /* Ditto, base64 */
308 _ENC_LAST
= 1<<5, /* Last round generated encoded word */
309 _SHOULD_BEE
= 1<<6, /* Avoid lines longer than SHOULD via encoding */
311 _RND_MASK
= (1<<_RND_SHIFT
) - 1,
312 _SPACE
= 1<<(_RND_SHIFT
+1), /* Leading whitespace */
313 _8BIT
= 1<<(_RND_SHIFT
+2), /* High bit set */
314 _ENCODE
= 1<<(_RND_SHIFT
+3), /* Need encoding */
315 _ENC_B64
= 1<<(_RND_SHIFT
+4), /* - let it be base64 */
316 _OVERLONG
= 1<<(_RND_SHIFT
+5) /* Temporarily rised limit */
318 char const *cset7
, *cset8
, *wbot
, *upper
, *wend
, *wcur
;
319 ui32_t cset7_len
, cset8_len
;
325 cout
.s
= NULL
, cout
.l
= 0;
326 cset7
= ok_vlook(charset_7bit
);
327 cset7_len
= (ui32_t
)strlen(cset7
);
328 cset8
= _CS_ITER_GET(); /* TODO MIME/send layer: iter active? iter! else */
329 cset8_len
= (ui32_t
)strlen(cset8
);
332 if(msh
!= a_MIME_SH_NONE
)
333 flags
|= _MSH_NOTHING
;
335 /* RFC 1468, "MIME Considerations":
336 * ISO-2022-JP may also be used in MIME Part 2 headers. The "B"
337 * encoding should be used with ISO-2022-JP text. */
338 /* TODO of course, our current implementation won't deal properly with
339 * TODO any stateful encoding at all... (the standard says each encoded
340 * TODO word must include all necessary reset sequences..., i.e., each
341 * TODO encoded word must be a self-contained iconv(3) life cycle) */
342 if (!asccasecmp(cset8
, "iso-2022-jp") || mime_enc_target() == MIMEE_B64
)
346 upper
= wbot
+ in
->l
;
349 if(colp
== NULL
|| (col
= *colp
) == 0)
350 col
= sizeof("Mail-Followup-To: ") -1; /* TODO dreadful thing */
352 /* The user may specify empy quoted-strings or comments, keep them! */
354 if(flags
& _MSH_NOTHING
){
355 flags
&= ~_MSH_NOTHING
;
356 putc((msh
== a_MIME_SH_COMMENT
? '(' : '"'), fo
);
360 } else for (; wbot
< upper
; flags
&= ~_FIRST
, wbot
= wend
) {
363 while (wcur
< upper
&& whitechar(*wcur
)) {
368 /* Any occurrence of whitespace resets prevention of lines >SHOULD via
369 * enforced encoding (xxx SHOULD, but.. encoding is expensive!!) */
371 flags
&= ~_SHOULD_BEE
;
373 /* Data ends with WS - dump it and done.
374 * Also, if we have seen multiple successive whitespace characters, then
375 * if there was no encoded word last, i.e., if we can simply take them
376 * over to the output as-is, keep one WS for possible later separation
377 * purposes and simply print the others as-is, directly! */
382 if ((flags
& (_ENC_LAST
| _SPACE
)) == _SPACE
&& wcur
- wbot
> 1) {
387 /* Skip over a word to next non-whitespace, keep track along the way
388 * whether our 7-bit charset suffices to represent the data */
389 for (wend
= wcur
; wend
< upper
; ++wend
) {
390 if (whitechar(*wend
))
392 if ((uc_i
)*wend
& 0x80)
396 /* Decide whether the range has to become encoded or not */
397 i
= PTR2SIZE(wend
- wcur
);
398 j
= mime_enc_mustquote(wcur
, i
, MIMEEF_ISHEAD
);
399 /* If it just cannot fit on a SHOULD line length, force encode */
400 if (i
> a_MAXCOL_NENC
) {
401 flags
|= _SHOULD_BEE
; /* (Sigh: SHOULD only, not MUST..) */
404 if ((flags
& _SHOULD_BEE
) || j
> 0) {
407 /* Use base64 if requested or more than 50% -37.5-% of the bytes of
408 * the string need to be encoded */
409 if ((flags
& _NO_QP
) || j
>= i
>> 1)/*(i >> 2) + (i >> 3))*/
412 DBG( if (flags
& _8BIT
) assert(flags
& _ENCODE
); )
414 if (!(flags
& _ENCODE
)) {
415 /* Encoded word produced, but no linear whitespace for necessary RFC
416 * 2047 separation? Generate artificial data (bad standard!) */
417 if ((flags
& (_ENC_LAST
| _SPACE
)) == _ENC_LAST
) {
418 if (col
>= a_MAXCOL
) {
423 if(flags
& _MSH_NOTHING
){
424 flags
&= ~_MSH_NOTHING
;
425 putc((msh
== a_MIME_SH_COMMENT
? '(' : '"'), fo
);
437 /* todo No effort here: (1) v15.0 has to bring complete rewrite,
438 * todo (2) the standard is braindead and (3) usually this is one
439 * todo word only, and why be smarter than the standard? */
441 i
= PTR2SIZE(wend
- wbot
);
442 if (i
+ col
+ ((flags
& _MSH_NOTHING
) != 0) <=
443 (flags
& _OVERLONG
? MIME_LINELEN_MAX
444 : (flags
& a_ANYENC
? a_MAXCOL
: a_MAXCOL_NENC
))) {
445 if(flags
& _MSH_NOTHING
){
446 flags
&= ~_MSH_NOTHING
;
447 putc((msh
== a_MIME_SH_COMMENT
? '(' : '"'), fo
);
451 i
= fwrite(wbot
, sizeof *wbot
, i
, fo
);
457 /* Doesn't fit, try to break the line first; */
460 if (whitechar(*wbot
)) {
461 putc((uc_i
)*wbot
, fo
);
464 putc(' ', fo
); /* Bad standard: artificial data! */
467 if(flags
& _MSH_NOTHING
){
468 flags
&= ~_MSH_NOTHING
;
469 putc((msh
== a_MIME_SH_COMMENT
? '(' : '"'), fo
);
477 /* It is so long that it needs to be broken, effectively causing
478 * artificial spaces to be inserted (bad standard), yuck */
479 /* todo This is not multibyte safe, as above; and completely stupid
480 * todo P.S.: our _SHOULD_BEE prevents these cases in the meanwhile */
481 /* FIXME n_PSO_UNICODE and parse using UTF-8 sync possibility! */
482 wcur
= wbot
+ MIME_LINELEN_MAX
- 8;
487 /* Encoding to encoded word(s); deal with leading whitespace, place
488 * a separator first as necessary: encoded words must always be
489 * separated from text and other encoded words with linear WS.
490 * And if an encoded word was last, intermediate whitespace must
491 * also be encoded, otherwise it would get stripped away! */
492 wcur
= n_UNCONST(n_empty
);
493 if ((flags
& (_ENC_LAST
| _SPACE
)) != _SPACE
) {
494 /* Reinclude whitespace */
496 /* We don't need to place a separator at the very beginning */
497 if (!(flags
& _FIRST
))
498 wcur
= n_UNCONST(" ");
502 flags
|= a_ANYENC
| _ENC_LAST
;
503 n_pstate
|= n_PS_HEADER_NEEDED_MIME
;
506 * An 'encoded-word' may not be more than 75 characters long,
507 * including 'charset', 'encoding', 'encoded-text', and
508 * delimiters. If it is desirable to encode more text than will
509 * fit in an 'encoded-word' of 75 characters, multiple
510 * 'encoded-word's (separated by CRLF SPACE) may be used.
512 * While there is no limit to the length of a multiple-line
513 * header field, each line of a header field that contains one
514 * or more 'encoded-word's is limited to 76 characters */
516 cin
.s
= n_UNCONST(wbot
);
517 cin
.l
= PTR2SIZE(wend
- wbot
);
523 xout
= b64_encode(&cout
, &cin
, B64_ISHEAD
| B64_ISENCWORD
);
525 xout
= qp_encode(&cout
, &cin
, QP_ISHEAD
| QP_ISENCWORD
);
532 /* (Avoid trigraphs in the RFC 2047 placeholder..) */
533 i
= j
+ (flags
& _8BIT
? cset8_len
: cset7_len
) + sizeof("=!!B!!=") -1;
538 /* Unfortunately RFC 2047 explicitly disallows encoded words to be
539 * longer (just like RFC 5322's "a line SHOULD fit in 78 but MAY be
540 * 998 characters long"), so we cannot use the _OVERLONG mechanism,
541 * even though all tested mailers seem to support it */
542 if (i
+ col
<= (/*flags & _OVERLONG ? MIME_LINELEN_MAX :*/ a_MAXCOL
)) {
543 if(flags
& _MSH_NOTHING
){
544 flags
&= ~_MSH_NOTHING
;
545 putc((msh
== a_MIME_SH_COMMENT
? '(' : '"'), fo
);
549 fprintf(fo
, "%.1s=?%s?%c?%.*s?=",
550 wcur
, (flags
& _8BIT
? cset8
: cset7
),
551 (flags
& _ENC_B64
? 'B' : 'Q'),
552 (int)cout
.l
, cout
.s
);
558 /* Doesn't fit, try to break the line first */
559 /* TODO I've commented out the _FIRST test since we (1) cannot do
560 * TODO _OVERLONG since (MUAs support but) the standard disallows,
561 * TODO and because of our iconv problem i prefer an empty first line
562 * TODO in favour of a possibly messed up multibytes character. :-( */
563 if (col
> 1 /* TODO && !(flags & _FIRST)*/) {
567 if (!(flags
& _SPACE
)) {
569 wcur
= n_UNCONST(n_empty
);
570 /*flags |= _OVERLONG;*/
571 goto jenc_retry_same
;
573 putc((uc_i
)*wcur
, fo
);
574 if (whitechar(*(wcur
= wbot
)))
578 wcur
= n_UNCONST(n_empty
);
580 /*flags &= ~_OVERLONG;*/
585 /* It is so long that it needs to be broken, effectively causing
586 * artificial data to be inserted (bad standard), yuck */
587 /* todo This is not multibyte safe, as above */
588 /*if (!(flags & _OVERLONG)) { Mechanism explicitly forbidden by 2047
593 /* FIXME n_PSO_UNICODE and parse using UTF-8 sync possibility! */
594 i
= PTR2SIZE(wend
- wbot
) + !!(flags
& _SPACE
);
595 j
= 3 + !(flags
& _ENC_B64
);
599 /* (Note the problem most likely is the transfer-encoding blow,
600 * which is why we test this *after* the decrements.. */
608 if(!(flags
& _MSH_NOTHING
) && msh
!= a_MIME_SH_NONE
){
609 putc((msh
== a_MIME_SH_COMMENT
? ')' : '"'), fo
);
624 mime_write_tohdr_a(struct str
*in
, FILE *f
, size_t *colp
)
628 char const *cp
, *lastcp
;
634 if((cp
= routeaddr(in
->s
)) != NULL
&& cp
> lastcp
) {
636 xin
.l
= PTR2SIZE(cp
- in
->s
);
637 if ((sz
= mime_write_tohdr_a(&xin
, f
, colp
)) < 0)
646 for( ; *cp
!= '\0'; ++cp
){
649 i
= PTR2SIZE(cp
- lastcp
);
651 if(fwrite(lastcp
, 1, i
, f
) != i
)
656 cp
= skip_comment(cp
);
659 /* We want to keep empty comments, too! */
660 xin
.s
= n_UNCONST(lastcp
);
661 xin
.l
= PTR2SIZE(cp
- lastcp
);
662 if ((x
= a_mime__convhdra(&xin
, f
, colp
, a_MIME_SH_COMMENT
)) < 0)
668 i
= PTR2SIZE(cp
- lastcp
);
670 if(fwrite(lastcp
, 1, i
, f
) != i
)
674 for(lastcp
= ++cp
; *cp
!= '\0'; ++cp
){
677 if(*cp
== '\\' && cp
[1] != '\0')
680 /* We want to keep empty quoted-strings, too! */
681 xin
.s
= n_UNCONST(lastcp
);
682 xin
.l
= PTR2SIZE(cp
- lastcp
);
683 if((x
= a_mime__convhdra(&xin
, f
, colp
, a_MIME_SH_QUOTE
)) < 0)
692 i
= PTR2SIZE(cp
- lastcp
);
694 if(fwrite(lastcp
, 1, i
, f
) != i
)
708 a_mime__convhdra(struct str
*inp
, FILE *fp
, size_t *colp
,
709 enum a_mime_structure_hack msh
){
717 if(inp
->l
> 0 && iconvd
!= (iconv_t
)-1){
719 if(n_iconv_str(iconvd
, n_ICONV_IGN_NOREVERSE
, &ciconv
, inp
, NULL
) != 0){
720 n_iconv_reset(iconvd
);
726 rv
= mime_write_tohdr(inp
, fp
, colp
, msh
);
733 #endif /* HAVE_ICONV */
736 _append_str(char **buf
, size_t *sz
, size_t *pos
, char const *str
, size_t len
)
739 *buf
= srealloc(*buf
, *sz
+= len
);
740 memcpy(&(*buf
)[*pos
], str
, len
);
746 _append_conv(char **buf
, size_t *sz
, size_t *pos
, char const *str
, size_t len
)
751 in
.s
= n_UNCONST(str
);
753 mime_fromhdr(&in
, &out
, TD_ISPR
| TD_ICONV
);
754 _append_str(buf
, sz
, pos
, out
.s
, out
.l
);
760 charset_iter_reset(char const *a_charset_to_try_first
) /* TODO elim. dups! */
763 size_t sarrl
[3], len
;
766 n_UNUSED(a_charset_to_try_first
);
769 sarr
[2] = ok_vlook(CHARSET_8BIT_OKEY
);
771 if(a_charset_to_try_first
!= NULL
&& strcmp(a_charset_to_try_first
, sarr
[2]))
772 sarr
[0] = a_charset_to_try_first
;
776 if((sarr
[1] = ok_vlook(sendcharsets
)) == NULL
&&
777 ok_blook(sendcharsets_else_ttycharset
)){
778 cp
= n_UNCONST(ok_vlook(ttycharset
));
779 if(strcmp(cp
, sarr
[2]) && (sarr
[0] == NULL
|| strcmp(cp
, sarr
[0])))
783 sarr
[2] = ok_vlook(ttycharset
);
786 sarrl
[2] = len
= strlen(sarr
[2]);
788 if ((cp
= n_UNCONST(sarr
[1])) != NULL
)
789 len
+= (sarrl
[1] = strlen(cp
));
792 if ((cp
= n_UNCONST(sarr
[0])) != NULL
)
793 len
+= (sarrl
[0] = strlen(cp
));
798 _cs_iter_base
= cp
= salloc(len
+ 1 + 1 +1);
801 if ((len
= sarrl
[0]) != 0) {
802 memcpy(cp
, sarr
[0], len
);
806 if ((len
= sarrl
[1]) != 0) {
807 memcpy(cp
, sarr
[1], len
);
813 memcpy(cp
, sarr
[2], len
);
818 return (_cs_iter
!= NULL
);
822 charset_iter_next(void)
828 rv
= (_cs_iter
!= NULL
);
834 charset_iter_is_valid(void)
839 rv
= (_cs_iter
!= NULL
);
856 charset_iter_or_fallback(void)
867 charset_iter_recurse(char *outer_storage
[2]) /* TODO LEGACY FUN, REMOVE */
870 outer_storage
[0] = _cs_iter_base
;
871 outer_storage
[1] = _cs_iter
;
876 charset_iter_restore(char *outer_storage
[2]) /* TODO LEGACY FUN, REMOVE */
879 _cs_iter_base
= outer_storage
[0];
880 _cs_iter
= outer_storage
[1];
886 need_hdrconv(struct header
*hp
) /* TODO once only, then iter */
888 struct n_header_field
*hfp
;
894 if((hfp
= hp
->h_user_headers
) != NULL
)
895 do if(_has_highbit(hfp
->hf_dat
+ hfp
->hf_nl
+1))
897 while((hfp
= hfp
->hf_next
) != NULL
);
899 if((hfp
= hp
->h_custom_headers
) != NULL
||
900 (hp
->h_custom_headers
= hfp
= n_customhdr_query()) != NULL
)
901 do if(_has_highbit(hfp
->hf_dat
+ hfp
->hf_nl
+1))
903 while((hfp
= hfp
->hf_next
) != NULL
);
905 if (hp
->h_mft
!= NULL
) {
906 if (_name_highbit(hp
->h_mft
))
909 if (hp
->h_from
!= NULL
) {
910 if (_name_highbit(hp
->h_from
))
912 } else if (_has_highbit(myaddrs(NULL
)))
915 if (_name_highbit(hp
->h_replyto
))
917 } else if (_has_highbit(ok_vlook(replyto
)))
920 if (_name_highbit(hp
->h_sender
))
922 } else if (_has_highbit(ok_vlook(sender
)))
925 if (_name_highbit(hp
->h_to
))
927 if (_name_highbit(hp
->h_cc
))
929 if (_name_highbit(hp
->h_bcc
))
931 if (_has_highbit(hp
->h_subject
))
933 rv
= _CS_ITER_GET(); /* TODO MIME/send: iter active? iter! else */
937 #endif /* HAVE_ICONV */
940 mime_fromhdr(struct str
const *in
, struct str
*out
, enum tdflags flags
)
942 /* TODO mime_fromhdr(): is called with strings that contain newlines;
943 * TODO this is the usual newline problem all around the codebase;
944 * TODO i.e., if we strip it, then the display misses it ;>
945 * TODO this is why it is so messy and why S-nail v14.2 plus additional
946 * TODO patch for v14.5.2 (and maybe even v14.5.3 subminor) occurred, and
947 * TODO why our display reflects what is contained in the message: the 1:1
948 * TODO relationship of message content and display!
949 * TODO instead a header line should be decoded to what it is (a single
950 * TODO line that is) and it should be objective to the backend whether
951 * TODO it'll be folded to fit onto the display or not, e.g., for search
952 * TODO purposes etc. then the only condition we have to honour in here
953 * TODO is that whitespace in between multiple adjacent MIME encoded words
954 * TODO á la RFC 2047 is discarded; i.e.: this function should deal with
955 * TODO RFC 2047 and be renamed: mime_fromhdr() -> mime_rfc2047_decode() */
956 struct str cin
, cout
;
957 char *p
, *op
, *upper
;
958 ui32_t convert
, lastenc
, lastoutl
;
962 iconv_t fhicd
= (iconv_t
)-1;
968 *(out
->s
= smalloc(1)) = '\0';
974 tcs
= ok_vlook(ttycharset
);
978 lastenc
= lastoutl
= 0;
982 if (*p
== '=' && *(p
+ 1) == '?') {
987 while (p
< upper
&& *p
!= '?')
988 ++p
; /* strip charset */
993 if (flags
& TD_ICONV
) {
994 size_t i
= PTR2SIZE(p
- cbeg
);
995 char *ltag
, *cs
= ac_alloc(i
);
997 memcpy(cs
, cbeg
, --i
);
999 /* RFC 2231 extends the RFC 2047 character set definition in
1000 * encoded words by language tags - silently strip those off */
1001 if ((ltag
= strchr(cs
, '*')) != NULL
)
1004 if (fhicd
!= (iconv_t
)-1)
1005 n_iconv_close(fhicd
);
1006 fhicd
= asccasecmp(cs
, tcs
) ? n_iconv_open(tcs
, cs
) : (iconv_t
)-1;
1012 convert
= CONV_FROMB64
;
1015 convert
= CONV_FROMQP
;
1017 default: /* invalid, ignore */
1025 if (PTRCMP(p
+ 1, >=, upper
))
1027 if (*p
++ == '?' && *p
== '=')
1036 if (convert
== CONV_FROMB64
) {
1037 if(!b64_decode_header(&cout
, &cin
))
1038 n_str_assign_cp(&cout
, _("[Invalid Base64 encoding]"));
1039 }else if(!qp_decode_header(&cout
, &cin
))
1040 n_str_assign_cp(&cout
, _("[Invalid Quoted-Printable encoding]"));
1044 if ((flags
& TD_ICONV
) && fhicd
!= (iconv_t
)-1) {
1045 cin
.s
= NULL
, cin
.l
= 0; /* XXX string pool ! */
1046 convert
= n_iconv_str(fhicd
, n_ICONV_UNIDEFAULT
, &cin
, &cout
, NULL
);
1047 out
= n_str_add(out
, &cin
);
1048 if (convert
) {/* n_ERR_INVAL at EOS */
1049 n_iconv_reset(fhicd
);
1050 out
= n_str_add_buf(out
, n_qm
, 1); /* TODO unicode replacement */
1055 out
= n_str_add(out
, &cout
);
1056 lastenc
= lastoutl
= out
->l
;
1063 onlyws
= (lastenc
> 0);
1067 if (op
[0] == '=' && (PTRCMP(op
+ 1, ==, upper
) || op
[1] == '?'))
1069 if (onlyws
&& !blankchar(*op
))
1073 out
= n_str_add_buf(out
, p
, PTR2SIZE(op
- p
));
1075 if (!onlyws
|| lastoutl
!= lastenc
)
1080 out
->s
[out
->l
] = '\0';
1082 if (flags
& TD_ISPR
) {
1083 makeprint(out
, &cout
);
1087 if (flags
& TD_DELCTRL
)
1088 out
->l
= delctrl(out
->s
, out
->l
);
1090 if (fhicd
!= (iconv_t
)-1)
1091 n_iconv_close(fhicd
);
1099 mime_fromaddr(char const *name
)
1101 char const *cp
, *lastcp
;
1103 size_t ressz
= 1, rescur
= 0;
1108 if (*name
== '\0') {
1109 res
= savestr(name
);
1113 if ((cp
= routeaddr(name
)) != NULL
&& cp
> name
) {
1114 _append_conv(&res
, &ressz
, &rescur
, name
, PTR2SIZE(cp
- name
));
1119 for ( ; *cp
; ++cp
) {
1122 _append_str(&res
, &ressz
, &rescur
, lastcp
, PTR2SIZE(cp
- lastcp
+ 1));
1124 cp
= skip_comment(cp
);
1126 _append_conv(&res
, &ressz
, &rescur
, lastcp
, PTR2SIZE(cp
- lastcp
));
1133 if (*cp
== '\\' && cp
[1] != '\0')
1140 _append_str(&res
, &ressz
, &rescur
, lastcp
, PTR2SIZE(cp
- lastcp
));
1141 /* TODO rescur==0: inserted to silence Coverity ...; check that */
1143 res
= n_UNCONST(n_empty
);
1156 xmime_write(char const *ptr
, size_t size
, FILE *f
, enum conversion convert
,
1157 enum tdflags dflags
)
1160 struct quoteflt
*qf
;
1163 quoteflt_reset(qf
= quoteflt_dummy(), f
);
1164 rv
= mime_write(ptr
, size
, f
, convert
, dflags
, qf
, NULL
, NULL
);
1170 static sigjmp_buf __mimemw_actjmp
; /* TODO someday.. */
1171 static int __mimemw_sig
; /* TODO someday.. */
1172 static sighandler_type __mimemw_opipe
;
1174 __mimemw_onsig(int sig
) /* TODO someday, we won't need it no more */
1176 NYD_X
; /* Signal handler */
1178 siglongjmp(__mimemw_actjmp
, 1);
1182 mime_write(char const *ptr
, size_t size
, FILE *f
,
1183 enum conversion convert
, enum tdflags
volatile dflags
,
1184 struct quoteflt
*qf
, struct str
* volatile outrest
,
1185 struct str
* volatile inrest
)
1187 /* TODO note: after send/MIME layer rewrite we will have a string pool
1188 * TODO so that memory allocation count drops down massively; for now,
1189 * TODO v14.0 that is, we pay a lot & heavily depend on the allocator */
1191 ssize_t
volatile sz
;
1194 dflags
|= _TD_BUFCOPY
;
1195 in
.s
= n_UNCONST(ptr
);
1198 if(inrest
!= NULL
&& inrest
->l
> 0){
1199 out
.s
= smalloc(inrest
->l
+ size
+ 1);
1200 memcpy(out
.s
, inrest
->s
, inrest
->l
);
1202 memcpy(&out
.s
[inrest
->l
], in
.s
, size
);
1205 (in
.s
= out
.s
)[in
.l
= size
] = '\0';
1206 dflags
&= ~_TD_BUFCOPY
;
1212 if ((sz
= size
) == 0) {
1213 if (outrest
!= NULL
&& outrest
->l
!= 0)
1219 if ((dflags
& TD_ICONV
) && iconvd
!= (iconv_t
)-1 &&
1220 (convert
== CONV_TOQP
|| convert
== CONV_8BIT
||
1221 convert
== CONV_TOB64
|| convert
== CONV_TOHDR
)) {
1222 if (n_iconv_str(iconvd
, n_ICONV_IGN_NOREVERSE
, &out
, &in
, NULL
) != 0) {
1223 n_iconv_reset(iconvd
);
1224 /* TODO This causes hard-failure. We would need to have an action
1225 * TODO policy FAIL|IGNORE|SETERROR(but continue). Better huh? */
1231 dflags
&= ~_TD_BUFCOPY
;
1237 __mimemw_opipe
= safe_signal(SIGPIPE
, &__mimemw_onsig
);
1238 if (sigsetjmp(__mimemw_actjmp
, 1))
1243 if(!qp_decode_part(&out
, &in
, outrest
, inrest
)){
1244 n_err(_("Invalid Quoted-Printable encoding ignored\n"));
1245 sz
= 0; /* TODO sz = -1 stops outer levels! */
1250 if(qp_encode(&out
, &in
, QP_NONE
) == NULL
){
1251 sz
= 0; /* TODO sz = -1 stops outer levels! */
1256 sz
= quoteflt_push(qf
, in
.s
, in
.l
);
1259 if(!b64_decode_part(&out
, &in
, outrest
, inrest
))
1264 case CONV_FROMB64_T
:
1265 if(!b64_decode_part(&out
, &in
, outrest
, inrest
)){
1267 n_err(_("Invalid Base64 encoding ignored\n"));
1268 sz
= 0; /* TODO sz = -1 stops outer levels! */
1273 if ((sz
= out
.l
) != 0) {
1274 ui32_t opl
= qf
->qf_pfix_len
;
1275 sz
= _fwrite_td(&out
, (dflags
& ~_TD_BUFCOPY
), outrest
, qf
);
1276 qf
->qf_pfix_len
= opl
;
1280 if(b64_encode(&out
, &in
, B64_LF
| B64_MULTILINE
) == NULL
){
1285 sz
= fwrite(out
.s
, sizeof *out
.s
, out
.l
, f
);
1286 if (sz
!= (ssize_t
)out
.l
)
1290 mime_fromhdr(&in
, &out
, TD_ISPR
| TD_ICONV
| (dflags
& TD_DELCTRL
));
1291 sz
= quoteflt_push(qf
, out
.s
, out
.l
);
1294 sz
= mime_write_tohdr(&in
, f
, NULL
, a_MIME_SH_NONE
);
1299 if(dflags
& _TD_BUFCOPY
){
1300 n_str_dup(&out
, &in
);
1303 dflags
&= ~_TD_BUFCOPY
;
1306 sz
= mime_write_tohdr_a(&in
, f
, &col
);
1309 sz
= _fwrite_td(&in
, dflags
, NULL
, qf
);
1317 safe_signal(SIGPIPE
, __mimemw_opipe
);
1318 if (__mimemw_sig
!= 0)
1319 n_raise(__mimemw_sig
);