1 /*@ S-nail - a mail user agent derived from Berkeley Mail.
2 *@ MIME support functions.
4 * Copyright (c) 2000-2004 Gunnar Ritter, Freiburg i. Br., Germany.
5 * Copyright (c) 2012 - 2016 Steffen (Daode) Nurpmeso <steffen@sdaoden.eu>.
9 * Gunnar Ritter. All rights reserved.
11 * Redistribution and use in source and binary forms, with or without
12 * modification, are permitted provided that the following conditions
14 * 1. Redistributions of source code must retain the above copyright
15 * notice, this list of conditions and the following disclaimer.
16 * 2. Redistributions in binary form must reproduce the above copyright
17 * notice, this list of conditions and the following disclaimer in the
18 * documentation and/or other materials provided with the distribution.
19 * 3. All advertising materials mentioning features or use of this software
20 * must display the following acknowledgement:
21 * This product includes software developed by Gunnar Ritter
22 * and his contributors.
23 * 4. Neither the name of Gunnar Ritter nor the names of his contributors
24 * may be used to endorse or promote products derived from this software
25 * without specific prior written permission.
27 * THIS SOFTWARE IS PROVIDED BY GUNNAR RITTER AND CONTRIBUTORS ``AS IS'' AND
28 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
29 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
30 * ARE DISCLAIMED. IN NO EVENT SHALL GUNNAR RITTER OR CONTRIBUTORS BE LIABLE
31 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
32 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
33 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
34 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
35 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
36 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
42 #ifndef HAVE_AMALGAMATION
46 static char *_cs_iter_base
, *_cs_iter
;
48 # define _CS_ITER_GET() ((_cs_iter != NULL) ? _cs_iter : charset_get_8bit())
50 # define _CS_ITER_GET() ((_cs_iter != NULL) ? _cs_iter : charset_get_lc())
52 #define _CS_ITER_STEP() _cs_iter = n_strsep(&_cs_iter_base, ',', TRU1)
54 /* Is 7-bit enough? */
56 static bool_t
_has_highbit(char const *s
);
57 static bool_t
_name_highbit(struct name
*np
);
60 /* fwrite(3) while checking for displayability */
61 static ssize_t
_fwrite_td(struct str
const *input
, enum tdflags flags
,
62 struct str
*rest
, struct quoteflt
*qf
);
64 /* Convert header fields to RFC 1522 format and write to the file fo */
65 static ssize_t
mime_write_tohdr(struct str
*in
, FILE *fo
);
67 /* Write len characters of the passed string to the passed file, doing charset
68 * and header conversion */
69 static ssize_t
convhdra(char const *str
, size_t len
, FILE *fp
);
71 /* Write an address to a header field */
72 static ssize_t
mime_write_tohdr_a(struct str
*in
, FILE *f
);
74 /* Append to buf, handling resizing */
75 static void _append_str(char **buf
, size_t *sz
, size_t *pos
,
76 char const *str
, size_t len
);
77 static void _append_conv(char **buf
, size_t *sz
, size_t *pos
,
78 char const *str
, size_t len
);
82 _has_highbit(char const *s
)
100 _name_highbit(struct name
*np
)
106 if (_has_highbit(np
->n_name
) || _has_highbit(np
->n_fullname
))
115 #endif /* HAVE_ICONV */
117 static sigjmp_buf __mimefwtd_actjmp
; /* TODO someday.. */
118 static int __mimefwtd_sig
; /* TODO someday.. */
119 static sighandler_type __mimefwtd_opipe
;
121 __mimefwtd_onsig(int sig
) /* TODO someday, we won't need it no more */
123 NYD_X
; /* Signal handler */
124 __mimefwtd_sig
= sig
;
125 siglongjmp(__mimefwtd_actjmp
, 1);
129 _fwrite_td(struct str
const *input
, enum tdflags flags
, struct str
*rest
,
132 /* TODO note: after send/MIME layer rewrite we will have a string pool
133 * TODO so that memory allocation count drops down massively; for now,
134 * TODO v14.* that is, we pay a lot & heavily depend on the allocator */
135 /* TODO well if we get a broken pipe here, and it happens to
136 * TODO happen pretty easy when sleeping in a full pipe buffer,
137 * TODO then the current codebase performs longjump away;
138 * TODO this leaves memory leaks behind ('think up to 3 per,
139 * TODO dep. upon alloca availability). For this to be fixed
140 * TODO we either need to get rid of the longjmp()s (tm) or
141 * TODO the storage must come from the outside or be tracked
142 * TODO in a carrier struct. Best both. But storage reuse
143 * TODO would be a bigbig win besides */
144 /* *input* _may_ point to non-modifyable buffer; but even then it only
145 * needs to be dup'ed away if we have to transform the content */
156 if ((flags
& TD_ICONV
) && iconvd
!= (iconv_t
)-1) {
159 if (rest
!= NULL
&& rest
->l
> 0) {
160 in
.l
= rest
->l
+ input
->l
;
161 in
.s
= buf
= smalloc(in
.l
+1);
162 memcpy(in
.s
, rest
->s
, rest
->l
);
163 memcpy(in
.s
+ rest
->l
, input
->s
, input
->l
);
167 if (n_iconv_str(iconvd
, n_ICONV_DEFAULT
, &out
, &in
, &in
) != 0 &&
168 rest
!= NULL
&& in
.l
> 0) {
169 n_iconv_reset(iconvd
);
170 /* Incomplete multibyte at EOF is special */
171 if (flags
& _TD_EOF
) {
172 out
.s
= srealloc(out
.s
, out
.l
+ 4);
173 /* TODO 0xFFFD out.s[out.l++] = '[';*/
174 out
.s
[out
.l
++] = '?'; /* TODO unicode replacement 0xFFFD !!! */
175 /* TODO 0xFFFD out.s[out.l++] = ']';*/
177 n_str_add(rest
, &in
);
182 flags
&= ~_TD_BUFCOPY
;
190 makeprint(&in
, &out
);
191 else if (flags
& _TD_BUFCOPY
)
192 n_str_dup(&out
, &in
);
195 if (flags
& TD_DELCTRL
)
196 out
.l
= delctrl(out
.s
, out
.l
);
199 __mimefwtd_opipe
= safe_signal(SIGPIPE
, &__mimefwtd_onsig
);
200 if (sigsetjmp(__mimefwtd_actjmp
, 1)) {
205 rv
= quoteflt_push(qf
, out
.s
, out
.l
);
210 if (in
.s
!= input
->s
)
212 safe_signal(SIGPIPE
, __mimefwtd_opipe
);
213 if (__mimefwtd_sig
!= 0)
214 n_raise(__mimefwtd_sig
);
220 mime_write_tohdr(struct str
*in
, FILE *fo
)
222 /* TODO mime_write_tohdr(): we don't know the name of our header->maxcol..
223 * TODO MIME/send layer rewrite: more available state!!
224 * TODO Because of this we cannot make a difference in between structured
225 * TODO and unstructured headers (RFC 2047, 5. (2))
226 * TODO NOT MULTIBYTE SAFE IF AN ENCODED WORD HAS TO BE SPLITTED!
227 * TODO To be better we had to mbtowc_l() (non-std! and no locale!!) and
228 * TODO work char-wise! -> S-CText..
229 * TODO The real problem for STD compatibility is however that "in" is
230 * TODO already iconv(3) encoded to the target character set! We could
231 * TODO also solve it (very expensively!) if we would narrow down to an
232 * TODO encoded word and then iconv(3)+MIME encode in one go, in which
233 * TODO case multibyte errors could be catched! */
235 /* Maximum line length *//* XXX we are too inflexible and could use
236 * XXX MIME_LINELEN unless an RFC 2047 encoding was actually used */
237 _MAXCOL
= MIME_LINELEN_RFC2047
240 _FIRST
= 1<<0, /* Nothing written yet, start of string */
241 _NO_QP
= 1<<1, /* No quoted-printable allowed */
242 _NO_B64
= 1<<2, /* Ditto, base64 */
243 _ENC_LAST
= 1<<3, /* Last round generated encoded word */
244 _SHOULD_BEE
= 1<<4, /* Avoid lines longer than SHOULD via encoding */
246 _RND_MASK
= (1<<_RND_SHIFT
) - 1,
247 _SPACE
= 1<<(_RND_SHIFT
+1), /* Leading whitespace */
248 _8BIT
= 1<<(_RND_SHIFT
+2), /* High bit set */
249 _ENCODE
= 1<<(_RND_SHIFT
+3), /* Need encoding */
250 _ENC_B64
= 1<<(_RND_SHIFT
+4), /* - let it be base64 */
251 _OVERLONG
= 1<<(_RND_SHIFT
+5) /* Temporarily rised limit */
254 struct str cout
, cin
;
255 char const *cset7
, *cset8
, *wbot
, *upper
, *wend
, *wcur
;
256 ui32_t cset7_len
, cset8_len
;
261 cout
.s
= NULL
, cout
.l
= 0;
262 cset7
= charset_get_7bit();
263 cset7_len
= (ui32_t
)strlen(cset7
);
264 cset8
= _CS_ITER_GET(); /* TODO MIME/send layer: iter active? iter! else */
265 cset8_len
= (ui32_t
)strlen(cset8
);
267 /* RFC 1468, "MIME Considerations":
268 * ISO-2022-JP may also be used in MIME Part 2 headers. The "B"
269 * encoding should be used with ISO-2022-JP text. */
270 /* TODO of course, our current implementation won't deal properly with
271 * TODO any stateful encoding at all... (the standard says each encoded
272 * TODO word must include all necessary reset sequences..., i.e., each
273 * TODO encoded word must be a self-contained iconv(3) life cycle) */
274 if (!asccasecmp(cset8
, "iso-2022-jp"))
278 upper
= wbot
+ in
->l
;
279 col
= sizeof("Mail-Followup-To: ") -1; /* dreadful thing */
281 for (sz
= 0; wbot
< upper
; flags
&= ~_FIRST
, wbot
= wend
) {
284 while (wcur
< upper
&& whitechar(*wcur
)) {
289 /* Any occurrence of whitespace resets prevention of lines >SHOULD via
290 * enforced encoding (xxx SHOULD, but.. encoding is expensive!!) */
292 flags
&= ~_SHOULD_BEE
;
294 /* Data ends with WS - dump it and done.
295 * Also, if we have seen multiple successive whitespace characters, then
296 * if there was no encoded word last, i.e., if we can simply take them
297 * over to the output as-is, keep one WS for possible later separation
298 * purposes and simply print the others as-is, directly! */
303 if ((flags
& (_ENC_LAST
| _SPACE
)) == _SPACE
&& wcur
- wbot
> 1) {
308 /* Skip over a word to next non-whitespace, keep track along the way
309 * whether our 7-bit charset suffices to represent the data */
310 for (wend
= wcur
; wend
< upper
; ++wend
) {
311 if (whitechar(*wend
))
313 if ((uc_i
)*wend
& 0x80)
317 /* Decide whether the range has to become encoded or not */
318 i
= PTR2SIZE(wend
- wcur
);
319 j
= mime_enc_mustquote(wcur
, i
, MIMEEF_ISHEAD
);
320 /* If it just cannot fit on a SHOULD line length, force encode */
322 flags
|= _SHOULD_BEE
; /* (Sigh: SHOULD only, not MUST..) */
325 if ((flags
& _SHOULD_BEE
) || j
> 0) {
328 /* Use base64 if requested or more than 50% -37.5-% of the bytes of
329 * the string need to be encoded */
330 if ((flags
& _NO_QP
) || j
>= i
>> 1)/*(i >> 2) + (i >> 3))*/
333 DBG( if (flags
& _8BIT
) assert(flags
& _ENCODE
); )
335 if (!(flags
& _ENCODE
)) {
336 /* Encoded word produced, but no linear whitespace for necessary RFC
337 * 2047 separation? Generate artificial data (bad standard!) */
338 if ((flags
& (_ENC_LAST
| _SPACE
)) == _ENC_LAST
) {
339 if (col
>= _MAXCOL
) {
352 /* todo No effort here: (1) v15.0 has to bring complete rewrite,
353 * todo (2) the standard is braindead and (3) usually this is one
354 * todo word only, and why be smarter than the standard? */
356 i
= PTR2SIZE(wend
- wbot
);
357 if (i
+ col
<= (flags
& _OVERLONG
? MIME_LINELEN_MAX
: _MAXCOL
)) {
358 i
= fwrite(wbot
, sizeof *wbot
, i
, fo
);
364 /* Doesn't fit, try to break the line first; */
367 if (whitechar(*wbot
)) {
368 putc((uc_i
)*wbot
, fo
);
371 putc(' ', fo
); /* Bad standard: artificial data! */
378 /* It is so long that it needs to be broken, effectively causing
379 * artificial spaces to be inserted (bad standard), yuck */
380 /* todo This is not multibyte safe, as above; and completely stupid
381 * todo P.S.: our _SHOULD_BEE prevents these cases in the meanwhile */
382 /* FIXME OPT_UNICODE and parse using UTF-8 sync possibility! */
383 wcur
= wbot
+ MIME_LINELEN_MAX
- 8;
388 /* Encoding to encoded word(s); deal with leading whitespace, place
389 * a separator first as necessary: encoded words must always be
390 * separated from text and other encoded words with linear WS.
391 * And if an encoded word was last, intermediate whitespace must
392 * also be encoded, otherwise it would get stripped away! */
394 if ((flags
& (_ENC_LAST
| _SPACE
)) != _SPACE
) {
395 /* Reinclude whitespace */
397 /* We don't need to place a separator at the very beginning */
398 if (!(flags
& _FIRST
))
404 pstate
|= PS_HEADER_NEEDED_MIME
;
407 * An 'encoded-word' may not be more than 75 characters long,
408 * including 'charset', 'encoding', 'encoded-text', and
409 * delimiters. If it is desirable to encode more text than will
410 * fit in an 'encoded-word' of 75 characters, multiple
411 * 'encoded-word's (separated by CRLF SPACE) may be used.
413 * While there is no limit to the length of a multiple-line
414 * header field, each line of a header field that contains one
415 * or more 'encoded-word's is limited to 76 characters */
417 cin
.s
= UNCONST(wbot
);
418 cin
.l
= PTR2SIZE(wend
- wbot
);
420 if (flags
& _ENC_B64
)
421 j
= b64_encode(&cout
, &cin
, B64_ISHEAD
| B64_ISENCWORD
)->l
;
423 j
= qp_encode(&cout
, &cin
, QP_ISHEAD
| QP_ISENCWORD
)->l
;
424 /* (Avoid trigraphs in the RFC 2047 placeholder..) */
425 i
= j
+ (flags
& _8BIT
? cset8_len
: cset7_len
) + sizeof("=!!B!!=") -1;
430 /* Unfortunately RFC 2047 explicitly disallows encoded words to be
431 * longer (just like RFC 5322's "a line SHOULD fit in 78 but MAY be
432 * 998 characters long"), so we cannot use the _OVERLONG mechanism,
433 * even though all tested mailers seem to support it */
434 if (i
+ col
<= (/*flags & _OVERLONG ? MIME_LINELEN_MAX :*/ _MAXCOL
)) {
435 fprintf(fo
, "%.1s=?%s?%c?%.*s?=",
436 wcur
, (flags
& _8BIT
? cset8
: cset7
),
437 (flags
& _ENC_B64
? 'B' : 'Q'),
438 (int)cout
.l
, cout
.s
);
444 /* Doesn't fit, try to break the line first */
445 /* TODO I've commented out the _FIRST test since we (1) cannot do
446 * TODO _OVERLONG since (MUAs support but) the standard disallows,
447 * TODO and because of our iconv problem i prefer an empty first line
448 * TODO in favour of a possibly messed up multibytes character. :-( */
449 if (col
> 1 /* TODO && !(flags & _FIRST)*/) {
453 if (!(flags
& _SPACE
)) {
456 /*flags |= _OVERLONG;*/
457 goto jenc_retry_same
;
459 putc((uc_i
)*wcur
, fo
);
460 if (whitechar(*(wcur
= wbot
)))
466 /*flags &= ~_OVERLONG;*/
471 /* It is so long that it needs to be broken, effectively causing
472 * artificial data to be inserted (bad standard), yuck */
473 /* todo This is not multibyte safe, as above */
474 /*if (!(flags & _OVERLONG)) { Mechanism explicitly forbidden by 2047
479 /* FIXME OPT_UNICODE and parse using UTF-8 sync possibility! */
480 i
= PTR2SIZE(wend
- wbot
) + !!(flags
& _SPACE
);
481 j
= 3 + !(flags
& _ENC_B64
);
485 /* (Note the problem most likely is the transfer-encoding blow,
486 * which is why we test this *after* the decrements.. */
501 convhdra(char const *str
, size_t len
, FILE *fp
)
510 cin
.s
= UNCONST(str
);
514 if (iconvd
!= (iconv_t
)-1) {
516 if(n_iconv_str(iconvd
, n_ICONV_IGN_NOREVERSE
, &ciconv
, &cin
, NULL
) != 0){
517 n_iconv_reset(iconvd
);
523 ret
= mime_write_tohdr(&cin
, fp
);
526 if (ciconv
.s
!= NULL
)
534 mime_write_tohdr_a(struct str
*in
, FILE *f
) /* TODO error handling */
536 char const *cp
, *lastcp
;
542 if ((cp
= routeaddr(in
->s
)) != NULL
&& cp
> lastcp
) {
543 if ((sz
= convhdra(lastcp
, PTR2SIZE(cp
- lastcp
), f
)) < 0)
551 for ( ; *cp
!= '\0'; ++cp
) {
554 sz
+= fwrite(lastcp
, 1, PTR2SIZE(cp
- lastcp
+ 1), f
);
556 cp
= skip_comment(cp
);
558 if ((x
= convhdra(lastcp
, PTR2SIZE(cp
- lastcp
), f
)) < 0) {
570 if (*cp
== '\\' && cp
[1] != '\0')
577 sz
+= fwrite(lastcp
, 1, PTR2SIZE(cp
- lastcp
), f
);
584 _append_str(char **buf
, size_t *sz
, size_t *pos
, char const *str
, size_t len
)
587 *buf
= srealloc(*buf
, *sz
+= len
);
588 memcpy(&(*buf
)[*pos
], str
, len
);
594 _append_conv(char **buf
, size_t *sz
, size_t *pos
, char const *str
, size_t len
)
601 mime_fromhdr(&in
, &out
, TD_ISPR
| TD_ICONV
);
602 _append_str(buf
, sz
, pos
, out
.s
, out
.l
);
608 charset_get_7bit(void)
613 if ((t
= ok_vlook(charset_7bit
)) == NULL
)
621 charset_get_8bit(void)
626 if ((t
= ok_vlook(CHARSET_8BIT_OKEY
)) == NULL
)
639 if ((t
= ok_vlook(ttycharset
)) == NULL
)
646 charset_iter_reset(char const *a_charset_to_try_first
)
649 size_t sarrl
[3], len
;
652 UNUSED(a_charset_to_try_first
);
655 sarr
[0] = a_charset_to_try_first
;
656 if ((sarr
[1] = ok_vlook(sendcharsets
)) == NULL
&&
657 ok_blook(sendcharsets_else_ttycharset
))
658 sarr
[1] = charset_get_lc();
659 sarr
[2] = charset_get_8bit();
661 sarr
[2] = charset_get_lc();
664 sarrl
[2] = len
= strlen(sarr
[2]);
666 if ((cp
= UNCONST(sarr
[1])) != NULL
)
667 len
+= (sarrl
[1] = strlen(cp
));
670 if ((cp
= UNCONST(sarr
[0])) != NULL
)
671 len
+= (sarrl
[0] = strlen(cp
));
676 _cs_iter_base
= cp
= salloc(len
+ 1 + 1 +1);
679 if ((len
= sarrl
[0]) != 0) {
680 memcpy(cp
, sarr
[0], len
);
684 if ((len
= sarrl
[1]) != 0) {
685 memcpy(cp
, sarr
[1], len
);
691 memcpy(cp
, sarr
[2], len
);
696 return (_cs_iter
!= NULL
);
700 charset_iter_next(void)
706 rv
= (_cs_iter
!= NULL
);
712 charset_iter_is_valid(void)
717 rv
= (_cs_iter
!= NULL
);
734 charset_iter_or_fallback(void)
745 charset_iter_recurse(char *outer_storage
[2]) /* TODO LEGACY FUN, REMOVE */
748 outer_storage
[0] = _cs_iter_base
;
749 outer_storage
[1] = _cs_iter
;
754 charset_iter_restore(char *outer_storage
[2]) /* TODO LEGACY FUN, REMOVE */
757 _cs_iter_base
= outer_storage
[0];
758 _cs_iter
= outer_storage
[1];
764 need_hdrconv(struct header
*hp
) /* TODO once only, then iter */
766 struct n_header_field
*hfp
;
772 if((hfp
= hp
->h_user_headers
) != NULL
)
773 do if(_has_highbit(hfp
->hf_dat
+ hfp
->hf_nl
+1))
775 while((hfp
= hfp
->hf_next
) != NULL
);
777 if((hfp
= hp
->h_custom_headers
) != NULL
||
778 (hp
->h_custom_headers
= hfp
= n_customhdr_query()) != NULL
)
779 do if(_has_highbit(hfp
->hf_dat
+ hfp
->hf_nl
+1))
781 while((hfp
= hfp
->hf_next
) != NULL
);
783 if (hp
->h_mft
!= NULL
) {
784 if (_name_highbit(hp
->h_mft
))
787 if (hp
->h_from
!= NULL
) {
788 if (_name_highbit(hp
->h_from
))
790 } else if (_has_highbit(myaddrs(NULL
)))
793 if (_name_highbit(hp
->h_replyto
))
795 } else if (_has_highbit(ok_vlook(replyto
)))
798 if (_name_highbit(hp
->h_sender
))
800 } else if (_has_highbit(ok_vlook(sender
)))
803 if (_name_highbit(hp
->h_to
))
805 if (_name_highbit(hp
->h_cc
))
807 if (_name_highbit(hp
->h_bcc
))
809 if (_has_highbit(hp
->h_subject
))
811 rv
= _CS_ITER_GET(); /* TODO MIME/send: iter active? iter! else */
815 #endif /* HAVE_ICONV */
818 mime_fromhdr(struct str
const *in
, struct str
*out
, enum tdflags flags
)
820 /* TODO mime_fromhdr(): is called with strings that contain newlines;
821 * TODO this is the usual newline problem all around the codebase;
822 * TODO i.e., if we strip it, then the display misses it ;>
823 * TODO this is why it is so messy and why S-nail v14.2 plus additional
824 * TODO patch for v14.5.2 (and maybe even v14.5.3 subminor) occurred, and
825 * TODO why our display reflects what is contained in the message: the 1:1
826 * TODO relationship of message content and display!
827 * TODO instead a header line should be decoded to what it is (a single
828 * TODO line that is) and it should be objective to the backend whether
829 * TODO it'll be folded to fit onto the display or not, e.g., for search
830 * TODO purposes etc. then the only condition we have to honour in here
831 * TODO is that whitespace in between multiple adjacent MIME encoded words
832 * TODO á la RFC 2047 is discarded; i.e.: this function should deal with
833 * TODO RFC 2047 and be renamed: mime_fromhdr() -> mime_rfc2047_decode() */
834 struct str cin
, cout
;
835 char *p
, *op
, *upper
;
836 ui32_t convert
, lastenc
, lastoutl
;
840 iconv_t fhicd
= (iconv_t
)-1;
846 *(out
->s
= smalloc(1)) = '\0';
852 tcs
= charset_get_lc();
856 lastenc
= lastoutl
= 0;
860 if (*p
== '=' && *(p
+ 1) == '?') {
865 while (p
< upper
&& *p
!= '?')
866 ++p
; /* strip charset */
871 if (flags
& TD_ICONV
) {
872 size_t i
= PTR2SIZE(p
- cbeg
);
873 char *ltag
, *cs
= ac_alloc(i
);
875 memcpy(cs
, cbeg
, --i
);
877 /* RFC 2231 extends the RFC 2047 character set definition in
878 * encoded words by language tags - silently strip those off */
879 if ((ltag
= strchr(cs
, '*')) != NULL
)
882 if (fhicd
!= (iconv_t
)-1)
883 n_iconv_close(fhicd
);
884 fhicd
= asccasecmp(cs
, tcs
) ? n_iconv_open(tcs
, cs
) : (iconv_t
)-1;
890 convert
= CONV_FROMB64
;
893 convert
= CONV_FROMQP
;
895 default: /* invalid, ignore */
903 if (PTRCMP(p
+ 1, >=, upper
))
905 if (*p
++ == '?' && *p
== '=')
914 if (convert
== CONV_FROMB64
) {
915 /* XXX Take care for, and strip LF from
916 * XXX [Invalid Base64 encoding ignored] */
917 if (b64_decode(&cout
, &cin
, NULL
) == STOP
&&
918 cout
.s
[cout
.l
- 1] == '\n')
921 qp_decode(&cout
, &cin
, NULL
);
925 if ((flags
& TD_ICONV
) && fhicd
!= (iconv_t
)-1) {
926 cin
.s
= NULL
, cin
.l
= 0; /* XXX string pool ! */
927 convert
= n_iconv_str(fhicd
, n_ICONV_DEFAULT
, &cin
, &cout
, NULL
);
928 out
= n_str_add(out
, &cin
);
929 if (convert
) {/* EINVAL at EOS */
930 n_iconv_reset(fhicd
);
931 out
= n_str_add_buf(out
, "?", 1); /* TODO unicode replacement */
936 out
= n_str_add(out
, &cout
);
937 lastenc
= lastoutl
= out
->l
;
944 onlyws
= (lastenc
> 0);
948 if (op
[0] == '=' && (PTRCMP(op
+ 1, ==, upper
) || op
[1] == '?'))
950 if (onlyws
&& !blankchar(*op
))
954 out
= n_str_add_buf(out
, p
, PTR2SIZE(op
- p
));
956 if (!onlyws
|| lastoutl
!= lastenc
)
961 out
->s
[out
->l
] = '\0';
963 if (flags
& TD_ISPR
) {
964 makeprint(out
, &cout
);
968 if (flags
& TD_DELCTRL
)
969 out
->l
= delctrl(out
->s
, out
->l
);
971 if (fhicd
!= (iconv_t
)-1)
972 n_iconv_close(fhicd
);
980 mime_fromaddr(char const *name
)
982 char const *cp
, *lastcp
;
984 size_t ressz
= 1, rescur
= 0;
994 if ((cp
= routeaddr(name
)) != NULL
&& cp
> name
) {
995 _append_conv(&res
, &ressz
, &rescur
, name
, PTR2SIZE(cp
- name
));
1000 for ( ; *cp
; ++cp
) {
1003 _append_str(&res
, &ressz
, &rescur
, lastcp
, PTR2SIZE(cp
- lastcp
+ 1));
1005 cp
= skip_comment(cp
);
1007 _append_conv(&res
, &ressz
, &rescur
, lastcp
, PTR2SIZE(cp
- lastcp
));
1014 if (*cp
== '\\' && cp
[1] != '\0')
1021 _append_str(&res
, &ressz
, &rescur
, lastcp
, PTR2SIZE(cp
- lastcp
));
1022 /* TODO rescur==0: inserted to silence Coverity ...; check that */
1037 xmime_write(char const *ptr
, size_t size
, FILE *f
, enum conversion convert
,
1038 enum tdflags dflags
)
1041 struct quoteflt
*qf
;
1044 quoteflt_reset(qf
= quoteflt_dummy(), f
);
1045 rv
= mime_write(ptr
, size
, f
, convert
, dflags
, qf
, NULL
);
1051 static sigjmp_buf __mimemw_actjmp
; /* TODO someday.. */
1052 static int __mimemw_sig
; /* TODO someday.. */
1053 static sighandler_type __mimemw_opipe
;
1055 __mimemw_onsig(int sig
) /* TODO someday, we won't need it no more */
1057 NYD_X
; /* Signal handler */
1059 siglongjmp(__mimemw_actjmp
, 1);
1063 mime_write(char const *ptr
, size_t size
, FILE *f
,
1064 enum conversion convert
, enum tdflags
volatile dflags
,
1065 struct quoteflt
*qf
, struct str
* volatile rest
)
1067 /* TODO note: after send/MIME layer rewrite we will have a string pool
1068 * TODO so that memory allocation count drops down massively; for now,
1069 * TODO v14.0 that is, we pay a lot & heavily depend on the allocator */
1071 ssize_t
volatile sz
;
1075 in
.s
= UNCONST(ptr
);
1080 dflags
|= _TD_BUFCOPY
;
1081 if ((sz
= size
) == 0) {
1082 if (rest
!= NULL
&& rest
->l
!= 0)
1088 if ((dflags
& TD_ICONV
) && iconvd
!= (iconv_t
)-1 &&
1089 (convert
== CONV_TOQP
|| convert
== CONV_8BIT
||
1090 convert
== CONV_TOB64
|| convert
== CONV_TOHDR
)) {
1091 if (n_iconv_str(iconvd
, n_ICONV_IGN_NOREVERSE
, &out
, &in
, NULL
) != 0) {
1092 /* XXX report conversion error? */;
1093 n_iconv_reset(iconvd
);
1099 dflags
&= ~_TD_BUFCOPY
;
1105 __mimemw_opipe
= safe_signal(SIGPIPE
, &__mimemw_onsig
);
1106 if (sigsetjmp(__mimemw_actjmp
, 1))
1111 state
= qp_decode(&out
, &in
, rest
);
1114 qp_encode(&out
, &in
, QP_NONE
);
1117 sz
= quoteflt_push(qf
, in
.s
, in
.l
);
1122 case CONV_FROMB64_T
:
1123 state
= b64_decode(&out
, &in
, rest
);
1125 if ((sz
= out
.l
) != 0) {
1126 ui32_t opl
= qf
->qf_pfix_len
;
1128 qf
->qf_pfix_len
= 0;
1129 sz
= _fwrite_td(&out
, (dflags
& ~_TD_BUFCOPY
), rest
, qf
);
1130 qf
->qf_pfix_len
= opl
;
1136 b64_encode(&out
, &in
, B64_LF
| B64_MULTILINE
);
1138 sz
= fwrite(out
.s
, sizeof *out
.s
, out
.l
, f
);
1139 if (sz
!= (ssize_t
)out
.l
)
1143 mime_fromhdr(&in
, &out
, TD_ISPR
| TD_ICONV
| (dflags
& TD_DELCTRL
));
1144 sz
= quoteflt_push(qf
, out
.s
, out
.l
);
1147 sz
= mime_write_tohdr(&in
, f
);
1150 sz
= mime_write_tohdr_a(&in
, f
);
1153 sz
= _fwrite_td(&in
, dflags
, NULL
, qf
);
1161 safe_signal(SIGPIPE
, __mimemw_opipe
);
1162 if (__mimemw_sig
!= 0)
1163 n_raise(__mimemw_sig
);