1 /*@ S-nail - a mail user agent derived from Berkeley Mail.
2 *@ MIME parameter handling.
4 * Copyright (c) 2015 Steffen (Daode) Nurpmeso <sdaoden@users.sf.net>.
6 * Permission to use, copy, modify, and/or distribute this software for any
7 * purpose with or without fee is hereby granted, provided that the above
8 * copyright notice and this permission notice appear in all copies.
10 * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
11 * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
12 * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
13 * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
14 * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
15 * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
16 * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
19 #define n_FILE mime_param
21 #ifndef HAVE_AMALGAMATION
25 struct rfc2231_joiner
{
26 struct rfc2231_joiner
*rj_next
;
27 ui32_t rj_no
; /* Continuation number */
28 ui32_t rj_len
; /* of useful data in .rj_dat */
29 ui32_t rj_val_off
; /* Start of value data therein */
30 ui32_t rj_cs_len
; /* Length of charset part */
31 bool_t rj_is_enc
; /* Is percent encoded */
36 struct mime_param_builder
{
37 struct mime_param_builder
*mpb_next
;
38 struct str
*mpb_result
;
39 ui32_t mpb_level
; /* of recursion (<-> continuation number) */
40 ui32_t mpb_name_len
; /* of the parameter .mpb_name */
41 ui32_t mpb_value_len
; /* of remaining value */
42 ui32_t mpb_charset_len
; /* of .mpb_charset (only in outermost level) */
43 ui32_t mpb_buf_len
; /* Usable result of this level in .mpb_buf */
44 bool_t mpb_is_enc
; /* Level requires encoding */
46 bool_t mpb_is_utf8
; /* Encoding is UTF-8 */
49 char const *mpb_value
; /* Remains of, once the level was entered */
50 char const *mpb_charset
; /* charset_get_lc() */
51 char *mpb_buf
; /* Pointer to on-stack buffer */
54 /* All ASCII characters which cause RFC 2231 to be applied XXX check -1 slots*/
55 static bool_t
const _rfc2231_etab
[] = {
56 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,-1,-1, 1,-1, 1, 1, /* NUL..SI */
57 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, /* DLE..US */
58 1, 0, 1, 0, 0, 1, 0, 1, 1, 1, 1, 0, 1, 0, 0, 1, /* CAN.. / */
59 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 1, 1, /* 0.. ? */
61 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, /* @.. O */
62 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 0, 0, /* P.. _ */
63 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, /* `.. o */
64 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, /* p..DEL */
67 /* In a headerbody, at a "param=XY" that we're not interested in, skip over the
68 * entire construct, return pointer to the first byte thereafter or to NUL */
69 static char const * _mime_param_skip(char const *hbp
);
71 /* Trim value, which points to after the "name[RFC 2231 stuff]=".
72 * On successful return (1,-1; -1 is returned if the value was quoted via
73 * double quotation marks) a set end_or_null points to after the value and any
74 * possible separator and result->s is the salloc()d normalized value */
75 static si8_t
_mime_param_value_trim(struct str
*result
, char const *start
,
76 char const **end_or_null
);
78 /* mime_param_get() found the desired parameter but it seems to use RFC 2231
79 * extended syntax: perform full RFC 2231 parsing starting at this point.
80 * Note that _join() returns is-error */
81 static char * _rfc2231_param_parse(char const *param
, size_t plen
,
83 static bool_t
__rfc2231_join(struct rfc2231_joiner
*head
, char **result
,
86 /* Recursive parameter builder. Note we have a magic limit of 999 levels.
87 * Prepares a portion of output in self->mpb_buf;
88 * once >mpb_value is worked completely the deepmost level joins the result
89 * into >mpb_result and unrolls the stack. */
90 static void _mime_param_create(struct mime_param_builder
*self
);
91 static void __mime_param_join(struct mime_param_builder
*head
);
94 _mime_param_skip(char const *hbp
)
99 /* Skip over parameter name - note we may have skipped over an entire
100 * parameter name and thus point to a "="; i haven't yet truly checked
101 * against MIME RFCs, just test for ";" in the meanwhile XXX */
102 while ((cn
= *hbp
) != '\0' && cn
!= '=' && cn
!= ';')
110 while (whitechar((cn
= *hbp
))) /* XXX */
117 while ((cn
= *++hbp
) != '\0' && (cn
!= '"' || co
== '\\'))
118 co
= (co
== '\\') ? '\0' : cn
;
119 if (cn
!= '\0' && (cn
= *++hbp
) == ';')
123 if (cn
== '\0' || cn
== ';' || whitechar(cn
))
134 _mime_param_value_trim(struct str
*result
, char const *start
,
135 char const **end_or_null
)
143 while (whitechar(*start
)) /* XXX? */
147 for (co
= '\0', e
= ++start
;; ++e
)
148 if ((cn
= *e
) == '\0')
150 else if (cn
== '"' && co
!= '\\')
152 else if (cn
== '\\' && co
== '\\')
156 i
= PTR2SIZE(e
++ - start
);
159 for (e
= start
; (cn
= *e
) != '\0' && !whitechar(cn
) && cn
!= ';'; ++e
)
161 i
= PTR2SIZE(e
- start
);
165 result
->s
= salloc(i
+1);
167 memcpy(result
->s
, start
, result
->l
= i
);
173 for (j
= 0, cp
= result
->s
, co
= '\0'; i
-- > 0; co
= cn
) {
175 if (cn
!= '\\' || co
== '\\') {
187 if (end_or_null
!= NULL
) {
188 while (*e
!= '\0' && *e
== ';')
201 _rfc2231_param_parse(char const *param
, size_t plen
, char const *hbp
)
203 /* TODO Do it for real and unite with mime_param_get() */
205 char nobuf
[32], *eptr
, *rv
= NULL
, c
;
206 char const *hbp_base
, *cp
, *emsg
= NULL
;
207 struct rfc2231_joiner
*head
= NULL
, *np
;
208 bool_t errors
= FAL0
;
212 /* We were called by mime_param_get() after a param name match that
213 * involved "*", so jump to the matching code */
217 for (; *hbp
!= '\0'; hbp_base
= hbp
) {
218 while (whitechar(*hbp
))
221 if (!ascncasecmp(hbp
, param
, plen
)) {
223 while (whitechar(*hbp
))
228 /* RFC 2231 extensions: "NAME[*DIGITS][*]=", where "*DIGITS" indicates
229 * parameter continuation and the lone asterisk "*" percent encoded
230 * values -- if encoding is used the "*0" or lone parameter value
231 * MUST be encoded and start with a "CHARSET'LANGUAGE'" construct,
232 * where both of CHARSET and LANGUAGE are optional (we do effectively
233 * generate error if CHARSET is missing though).
234 * Continuations may not use that "C'L'" construct, but be tolerant
235 * and ignore those. Also encoded and non-encoded continuations may
236 * occur, i.e., perform percent en-/decoding only as necessary.
237 * Continuations may occur in any order */
238 /* xxx RFC 2231 parsing ignores language tags */
240 for (cp
= hbp
; digitchar(*cp
); ++cp
)
242 i
= PTR2SIZE(cp
- hbp
);
244 if (i
>= sizeof(nobuf
)) {
245 emsg
= N_("too many digits to form a valid number");
247 } else if ((c
= *cp
) != '=' && c
!= '*') {
248 emsg
= N_("expected \"=\" or \"*\" after leading digits");
251 memcpy(nobuf
, hbp
, i
);
253 i
= (size_t)strtol(nobuf
, UNCONST(&eptr
), 10);
254 if (i
>= 999 || *eptr
!= '\0') {
255 emsg
= N_("invalid continuation sequence number");
264 } else if (c
!= '=') {
266 emsg
= N_("expected \"=\" after asterisk \"*\"");
270 /* In continuation mode that is an error, however */
272 emsg
= N_("missing continuation sequence number");
275 /* Parameter value is encoded, may define encoding */
283 /* Create new node and insert it sorted; should be faster than
284 * creating an unsorted list and sorting it after parsing */
285 np
= smalloc(sizeof *np
);
287 np
->rj_no
= (ui32_t
)i
;
288 np
->rj_is_enc
= (c
== '*');
289 np
->rj_val_off
= np
->rj_cs_len
= 0;
293 else if (i
< head
->rj_no
) {
297 struct rfc2231_joiner
*l
= NULL
, *x
= head
;
299 while (x
!= NULL
&& i
> x
->rj_no
)
300 l
= x
, x
= x
->rj_next
;
306 switch (_mime_param_value_trim(&xval
, hbp
, &cp
)) {
308 emsg
= (c
== '*') ? N_("invalid value encoding")/* XXX fake */
309 : N_("faulty value - missing closing quotation mark \"\"\"?");
312 /* XXX if (np->is_enc && memchr(np->dat, '\'', i) != NULL) {
313 * XXX emsg = N_("character set info not allowed here");
315 * XXX } */np
->rj_is_enc
= FAL0
; /* Silently ignore */
318 if (xval
.l
>= UI32_MAX
) {
319 emsg
= N_("parameter value too long");
322 np
->rj_len
= (ui32_t
)xval
.l
;
327 /* Watch out for character set and language info */
328 if (np
->rj_is_enc
&& (eptr
= memchr(xval
.s
, '\'', xval
.l
)) != NULL
) {
329 np
->rj_cs_len
= PTR2SIZE(eptr
- xval
.s
);
330 if ((eptr
= memchr(eptr
+ 1, '\'', xval
.l
- np
->rj_cs_len
- 1))
332 emsg
= N_("faulty RFC 2231 parameter extension");
335 np
->rj_val_off
= PTR2SIZE(++eptr
- xval
.s
);
340 hbp
= _mime_param_skip(hbp
);
342 assert(head
!= NULL
); /* (always true due to jumpin:, but..) */
344 errors
|= __rfc2231_join(head
, &rv
, &emsg
);
345 if (errors
&& (options
& OPT_D_V_VV
)) {
346 /* TODO should set global flags so that at the end of an operation
347 * TODO (for a message) a summary can be printed: faulty MIME, xy */
349 emsg
= N_("multiple causes");
350 n_err(_("Message had MIME errors: %s\n"), V_(emsg
));
357 while ((np
= head
) != NULL
) {
361 if (options
& OPT_D_V
) {
363 emsg
= N_("expected asterisk \"*\"");
364 n_err(_("Faulty \"%s\" RFC 2231 MIME parameter value: %s\n"
365 "Near: %s\n"), param
, V_(emsg
), hbp_base
);
372 __rfc2231_join(struct rfc2231_joiner
*head
, char **result
, char const **emsg
)
375 struct rfc2231_joiner
*np
;
392 UNINIT(fhicd
, (iconv_t
)-1);
394 if (head
->rj_is_enc
) {
398 if (head
->rj_cs_len
== 0) {
399 /* It is an error if the character set is not set, the language alone
400 * cannot convert characters, let aside that we don't use it at all */
401 *emsg
= N_("MIME RFC 2231 invalidity: missing character set\n");
403 } else if (ascncasecmp(tcs
= charset_get_lc(),
404 head
->rj_dat
, head
->rj_cs_len
)) {
405 char *cs
= ac_alloc(head
->rj_cs_len
+1);
407 memcpy(cs
, head
->rj_dat
, head
->rj_cs_len
);
408 cs
[head
->rj_cs_len
] = '\0';
409 if ((fhicd
= n_iconv_open(tcs
, cs
)) != (iconv_t
)-1)
412 *emsg
= N_("necessary character set conversion missing");
420 if (head
->rj_no
!= 0) {
422 *emsg
= N_("First RFC 2231 parameter value chunk number is not 0");
426 for (sou
.s
= NULL
, sou
.l
= 0, no
= 0; (np
= head
) != NULL
; free(np
)) {
429 if (np
->rj_no
!= no
++) {
431 *emsg
= N_("RFC 2231 parameter value chunks are not contiguous");
435 /* RFC 2231 allows such info only in the first continuation, and
436 * furthermore MUSTs the first to be encoded, then */
437 if (/*np->rj_is_enc &&*/ np
->rj_val_off
> 0 &&
438 (f
& (_HAVE_ENC
| _SEEN_ANY
)) != _HAVE_ENC
) {
440 *emsg
= N_("invalid redundant RFC 2231 charset/language ignored");
445 i
= np
->rj_len
- np
->rj_val_off
;
447 n_str_add_buf(&sou
, np
->rj_dat
+ np
->rj_val_off
, i
);
449 /* Perform percent decoding */
450 sin
.s
= smalloc(i
+1);
453 for (cp
= np
->rj_dat
+ np
->rj_val_off
; i
> 0;) {
456 if ((c
= *cp
++) == '%') {
459 if (i
< 3 || (cc
= mime_hexseq_to_char(cp
)) < 0) {
461 *emsg
= N_("invalid RFC 2231 percent encoded sequence");
465 sin
.s
[sin
.l
++] = (char)cc
;
476 n_str_add_buf(&sou
, sin
.s
, sin
.l
);
481 /* And add character set conversion on top as necessary.
482 * RFC 2231 is pragmatic: encode only mentions percent encoding and the
483 * character set for the entire string ("[no] facility for using more
484 * than one character set or language"), therefore "continuations may
485 * contain a mixture of encoded and unencoded segments" applies to
486 * a contiguous string of a single character set that has been torn in
487 * pieces due to space restrictions, and it happened that some pieces
488 * didn't need to be percent encoded.
490 * _In particular_ it therefore doesn't repeat the RFC 2047 paradigm
491 * that encoded-words-are-atomic, meaning that a single character-set
492 * conversion run over the final, joined, partially percent-decoded value
493 * should be sufficient */
495 if (f
& _HAVE_ICONV
) {
498 if (n_iconv_str(fhicd
, &sin
, &sou
, NULL
, TRU1
) != 0) {
500 *emsg
= N_("character set conversion failed on value");
502 n_str_add_buf(&sin
, "?", 1);
507 n_iconv_close(fhicd
);
511 memcpy(*result
= salloc(sou
.l
+1), sou
.s
, sou
.l
+1);
514 return ((f
& _ERRORS
) != 0);
518 _mime_param_create(struct mime_param_builder
*self
)
520 struct mime_param_builder next
;
521 /* Don't use MIME_LINELEN_(MAX|LIMIT) stack buffer sizes: normally we won't
522 * exceed plain MIME_LINELEN, so that this would be a factor 10 wastage.
523 * On the other hand we may excess _LINELEN to avoid breaking up possible
524 * multibyte sequences until sizeof(buf) is reached, but since we (a) don't
525 * support stateful encodings and (b) will try to synchronize on UTF-8 this
526 * problem is scarce, possibly even artificial */
527 char buf
[MIN(MIME_LINELEN_MAX
>> 1, MIME_LINELEN
* 2)],
528 *bp
, *bp_max
, *bp_xmax
, *bp_lanoenc
;
529 char const *vb
, *vb_lanoenc
;
538 LCTA(sizeof(buf
) >= MIME_LINELEN
* 2);
541 self
->mpb_buf
= bp
= bp_lanoenc
= buf
;
542 self
->mpb_buf_len
= 0;
543 self
->mpb_is_enc
= ((f
& _ISENC
) != 0);
544 vb_lanoenc
= vb
= self
->mpb_value
;
545 vl
= self
->mpb_value_len
;
547 /* Configure bp_max to fit in SHOULD, bp_xmax to extent */
548 bp_max
= (buf
+ MIME_LINELEN
) -
549 (1 + self
->mpb_name_len
+ sizeof("*999*='';") -1 + 2);
550 bp_xmax
= (buf
+ sizeof(buf
)) -
551 (1 + self
->mpb_name_len
+ sizeof("*999*='';") -1 + 2);
552 if ((f
& _ISENC
) && self
->mpb_level
== 0) {
553 bp_max
-= self
->mpb_charset_len
;
554 bp_xmax
-= self
->mpb_charset_len
;
556 if (PTRCMP(bp_max
, <=, buf
+ sizeof("Hunky Dory"))) {
557 DBG( n_alert("_mime_param_create(): Hunky Dory!"); )
558 bp_max
= buf
+ (MIME_LINELEN
>> 1); /* And then it is SHOULD, anyway */
560 assert(PTRCMP(bp_max
+ (4 * 3), <=, bp_xmax
)); /* UTF-8 extra pad, below */
564 union {char c
; ui8_t uc
;} u
; u
.c
= *vb
;
568 if (u
.uc
> 0x7F || cntrlchar(u
.c
)) { /* XXX reject cntrlchar? */
569 /* We need to percent encode this character, possibly changing
570 * overall strategy, but anyway the one of this level, possibly
571 * rendering invalid any output byte we yet have produced here.
572 * Instead of throwing away that work just recurse if some fancy
573 * magic condition is true */
574 /* *However*, many tested MUAs fail to deal with parameters that
575 * are splitted across "too many" fields, including ones that
576 * misread RFC 2231 to allow only one digit, i.e., a maximum of
577 * ten. This is plain wrong, but that won't help their users */
578 if (PTR2SIZE(bp
- buf
) > /*10 (strawberry) COMPAT*/MIME_LINELEN
>>1)
584 if (u
.uc
== '"' || u
.uc
== '\\') {
590 } else if (u
.uc
> 0x7F || _rfc2231_etab
[u
.uc
]) {
593 mime_char_to_hexseq(bp
+ 1, u
.c
);
606 /* If all available space has been consumed we must split.
607 * Due to compatibility reasons we must take care not to break up
608 * multibyte sequences -- even though RFC 2231 rather implies that the
609 * splitted value should be joined (after percent encoded fields have
610 * been percent decoded) and the resulting string be treated in the
611 * specified character set / language, MUAs have been seen which apply
612 * the RFC 2047 encoded-words-are-atomic even to RFC 2231 values, even
613 * if stateful encodings cannot truly be supported like that?!..
615 * So split at 7-bit character if we have seen any and the wastage isn't
616 * too large; recall that we need to keep the overall number of P=V
617 * values as low as possible due to compatibility reasons.
618 * If we haven't seen any plain bytes be laxe and realize that bp_max
619 * reflects SHOULD lines, and try to extend this as long as possible.
620 * However, with UTF-8, try to backward synchronize on sequence start */
624 if ((f
& _HADRAW
) && (PTRCMP(bp
- bp_lanoenc
, <=, bp_lanoenc
- buf
) ||
625 (!self
->mpb_is_utf8
&&
626 PTR2SIZE(bp_lanoenc
- buf
) >= (MIME_LINELEN
>> 2)))) {
628 vl
+= PTR2SIZE(vb
- vb_lanoenc
);
633 if (self
->mpb_is_utf8
&& ((ui8_t
)(vb
[-1]) & 0xC0) != 0x80) {
646 /* That level made the great and completed encoding. Build result */
647 self
->mpb_is_enc
= ((f
& _ISENC
) != 0);
648 self
->mpb_buf_len
= PTR2SIZE(bp
- buf
);
649 __mime_param_join(self
);
654 /* Need to recurse, take care not to excess magical limit of 999 levels */
656 if (self
->mpb_level
== 999) {
657 if (options
& OPT_D_V_VV
)
658 n_err(_("Message RFC 2231 parameters nested too deeply!\n"));
662 self
->mpb_is_enc
= ((f
& _ISENC
) != 0);
663 self
->mpb_buf_len
= PTR2SIZE(bp
- buf
);
665 memset(&next
, 0, sizeof next
);
666 next
.mpb_next
= self
;
667 next
.mpb_level
= self
->mpb_level
+ 1;
668 next
.mpb_name_len
= self
->mpb_name_len
;
669 next
.mpb_value_len
= vl
;
670 next
.mpb_is_utf8
= self
->mpb_is_utf8
;
671 next
.mpb_name
= self
->mpb_name
;
673 _mime_param_create(&next
);
678 __mime_param_join(struct mime_param_builder
*head
)
681 struct mime_param_builder
*np
;
682 size_t i
, ll
; DBG( size_t len_max
; )
693 /* Traverse the stack upwards to find out result length (worst case).
694 * Reverse the list while doing so */
695 for (i
= 0, np
= head
, head
= NULL
; np
!= NULL
;) {
696 struct mime_param_builder
*tmp
;
698 i
+= np
->mpb_buf_len
+ np
->mpb_name_len
+ sizeof(" *999*=\"\";\n") -1;
708 i
+= head
->mpb_charset_len
; /* sizeof("''") -1 covered by \"\" above */
712 result
= head
->mpb_result
;
713 if (head
->mpb_next
!= NULL
)
715 cp
= result
->s
= salloc(i
+1);
717 for (ll
= 0, np
= head
;;) {
719 memcpy(cp
, np
->mpb_name
, i
= np
->mpb_name_len
);
724 char *cpo
= cp
, *nop
= nobuf
+ sizeof(nobuf
);
725 ui32_t noi
= np
->mpb_level
;
729 *--nop
= "0123456789"[noi
% 10];
730 while ((noi
/= 10) != 0);
736 ll
+= PTR2SIZE(cp
- cpo
);
739 if ((f
& _ISENC
) || np
->mpb_is_enc
) {
749 memcpy(cp
, np
->mpb_charset
, i
= np
->mpb_charset_len
);
755 } else if (!np
->mpb_is_enc
) {
761 memcpy(cp
, np
->mpb_buf
, i
= np
->mpb_buf_len
);
771 if ((np
= np
->mpb_next
) == NULL
)
777 i
+= np
->mpb_name_len
+ np
->mpb_buf_len
+ sizeof(" *999*=\"\";\n") -1;
778 if (i
>= MIME_LINELEN
) {
779 head
->mpb_rv
= -TRU1
;
788 result
->l
= PTR2SIZE(cp
- result
->s
);
789 assert(result
->l
< len_max
);
794 mime_param_get(char const *param
, char const *headerbody
) /* TODO rewr. */
802 plen
= strlen(param
);
805 /* At the beginning of headerbody there is no parameter=value pair xxx */
810 while (whitechar(*p
))
813 if (!ascncasecmp(p
, param
, plen
)) {
815 while (whitechar(*p
)) /* XXX? */
819 rv
= _rfc2231_param_parse(param
, plen
, p
);
822 if (!_mime_param_value_trim(&xval
, p
, NULL
)) {
828 /* We do have a result, but some (elder) software (S-nail <v14.8)
829 * will use RFC 2047 encoded words in parameter values, too */
830 /* TODO Automatically check wether the value seems to be RFC 2047
831 * TODO encwd. -- instead use *rfc2047_parameters* like mutt(1)? */
832 if ((p
= strstr(rv
, "=?")) != NULL
&& strstr(p
, "?=") != NULL
) {
835 ti
.l
= strlen(ti
.s
= rv
);
836 mime_fromhdr(&ti
, &to
, TD_ISPR
| TD_ICONV
| TD_DELCTRL
);
837 rv
= savestrbuf(to
.s
, to
.l
);
842 /* Not our desired parameter, skip and continue */
848 if (*(p
= _mime_param_skip(p
)) == '\0')
858 mime_param_create(struct str
*result
, char const *name
, char const *value
)
860 /* TODO All this needs rework when we have (1) a real string and even more
861 * TODO (2) use objects instead of stupid string concat; it's temporary
862 * TODO I.e., this function should return a HeaderBodyParam */
863 struct mime_param_builder top
;
867 memset(result
, 0, sizeof *result
);
869 memset(&top
, 0, sizeof top
);
870 top
.mpb_result
= result
;
871 if ((i
= strlen(top
.mpb_name
= name
)) > UI32_MAX
)
873 top
.mpb_name_len
= (ui32_t
)i
;
874 if ((i
= strlen(top
.mpb_value
= value
)) > UI32_MAX
)
876 top
.mpb_value_len
= (ui32_t
)i
;
877 top
.mpb_charset_len
= (ui32_t
)strlen(top
.mpb_charset
= charset_get_lc());
878 top
.mpb_is_utf8
= !ascncasecmp(top
.mpb_charset
, "utf-8",
879 top
.mpb_charset_len
);
881 _mime_param_create(&top
);
888 mime_param_boundary_get(char const *headerbody
, size_t *len
)
893 if ((p
= mime_param_get("boundary", headerbody
)) != NULL
) {
894 size_t sz
= strlen(p
);
898 q
= salloc(sz
+ 2 +1);
900 memcpy(q
+ 2, p
, sz
);
901 *(q
+ sz
+ 2) = '\0';
908 mime_param_boundary_create(void)
913 bp
= salloc(36 + 6 +1);
914 bp
[0] = bp
[2] = bp
[39] = bp
[41] = '=';
915 bp
[1] = bp
[40] = '-';
916 memcpy(bp
+ 3, getrandstring(36), 36);