1 /*@ S-nail - a mail user agent derived from Berkeley Mail.
2 *@ MIME parameter handling.
4 * Copyright (c) 2016 Steffen (Daode) Nurpmeso <steffen@sdaoden.eu>.
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
;
307 switch (_mime_param_value_trim(&xval
, hbp
, &cp
)) {
309 emsg
= (c
== '*') ? N_("invalid value encoding")/* XXX fake */
310 : N_("faulty value - missing closing quotation mark \"?");
313 /* XXX if (np->is_enc && memchr(np->dat, '\'', i) != NULL) {
314 * XXX emsg = N_("character set info not allowed here");
316 * XXX } */np
->rj_is_enc
= FAL0
; /* Silently ignore */
319 if (xval
.l
>= UI32_MAX
) {
320 emsg
= N_("parameter value too long");
323 np
->rj_len
= (ui32_t
)xval
.l
;
328 /* Watch out for character set and language info */
329 if (np
->rj_is_enc
&& (eptr
= memchr(xval
.s
, '\'', xval
.l
)) != NULL
) {
330 np
->rj_cs_len
= PTR2SIZE(eptr
- xval
.s
);
331 if ((eptr
= memchr(eptr
+ 1, '\'', xval
.l
- np
->rj_cs_len
- 1))
333 emsg
= N_("faulty RFC 2231 parameter extension");
336 np
->rj_val_off
= PTR2SIZE(++eptr
- xval
.s
);
341 hbp
= _mime_param_skip(hbp
);
343 assert(head
!= NULL
); /* (always true due to jumpin:, but..) */
345 errors
|= __rfc2231_join(head
, &rv
, &emsg
);
346 if (errors
&& (options
& OPT_D_V_VV
)) {
347 /* TODO should set global flags so that at the end of an operation
348 * TODO (for a message) a summary can be printed: faulty MIME, xy */
350 emsg
= N_("multiple causes");
351 n_err(_("Message had MIME errors: %s\n"), V_(emsg
));
358 while ((np
= head
) != NULL
) {
362 if (options
& OPT_D_V
) {
364 emsg
= N_("expected asterisk *");
365 n_err(_("Faulty RFC 2231 MIME parameter value: %s: %s\n"
366 "Near: %s\n"), param
, V_(emsg
), hbp_base
);
373 __rfc2231_join(struct rfc2231_joiner
*head
, char **result
, char const **emsg
)
376 struct rfc2231_joiner
*np
;
393 UNINIT(fhicd
, (iconv_t
)-1);
395 if (head
->rj_is_enc
) {
399 if (head
->rj_cs_len
== 0) {
400 /* It is an error if the character set is not set, the language alone
401 * cannot convert characters, let aside that we don't use it at all */
402 *emsg
= N_("MIME RFC 2231 invalidity: missing character set\n");
404 } else if (ascncasecmp(tcs
= charset_get_lc(),
405 head
->rj_dat
, head
->rj_cs_len
)) {
406 char *cs
= ac_alloc(head
->rj_cs_len
+1);
408 memcpy(cs
, head
->rj_dat
, head
->rj_cs_len
);
409 cs
[head
->rj_cs_len
] = '\0';
410 if ((fhicd
= n_iconv_open(tcs
, cs
)) != (iconv_t
)-1)
413 *emsg
= N_("necessary character set conversion missing");
421 if (head
->rj_no
!= 0) {
423 *emsg
= N_("First RFC 2231 parameter value chunk number is not 0");
427 for (sou
.s
= NULL
, sou
.l
= 0, no
= 0; (np
= head
) != NULL
; free(np
)) {
430 if (np
->rj_no
!= no
++) {
432 *emsg
= N_("RFC 2231 parameter value chunks are not contiguous");
436 /* RFC 2231 allows such info only in the first continuation, and
437 * furthermore MUSTs the first to be encoded, then */
438 if (/*np->rj_is_enc &&*/ np
->rj_val_off
> 0 &&
439 (f
& (_HAVE_ENC
| _SEEN_ANY
)) != _HAVE_ENC
) {
441 *emsg
= N_("invalid redundant RFC 2231 charset/language ignored");
446 i
= np
->rj_len
- np
->rj_val_off
;
448 n_str_add_buf(&sou
, np
->rj_dat
+ np
->rj_val_off
, i
);
450 /* Perform percent decoding */
451 sin
.s
= smalloc(i
+1);
454 for (cp
= np
->rj_dat
+ np
->rj_val_off
; i
> 0;) {
457 if ((c
= *cp
++) == '%') {
460 if (i
< 3 || (cc
= n_c_from_hex_base16(cp
)) < 0) {
462 *emsg
= N_("invalid RFC 2231 percent encoded sequence");
466 sin
.s
[sin
.l
++] = (char)cc
;
477 n_str_add_buf(&sou
, sin
.s
, sin
.l
);
482 /* And add character set conversion on top as necessary.
483 * RFC 2231 is pragmatic: encode only mentions percent encoding and the
484 * character set for the entire string ("[no] facility for using more
485 * than one character set or language"), therefore "continuations may
486 * contain a mixture of encoded and unencoded segments" applies to
487 * a contiguous string of a single character set that has been torn in
488 * pieces due to space restrictions, and it happened that some pieces
489 * didn't need to be percent encoded.
491 * _In particular_ it therefore doesn't repeat the RFC 2047 paradigm
492 * that encoded-words-are-atomic, meaning that a single character-set
493 * conversion run over the final, joined, partially percent-decoded value
494 * should be sufficient */
496 if (f
& _HAVE_ICONV
) {
499 if (n_iconv_str(fhicd
, n_ICONV_DEFAULT
, &sin
, &sou
, NULL
) != 0) {
501 *emsg
= N_("character set conversion failed on value");
503 n_str_add_buf(&sin
, "?", 1); /* XXX necessary? Unicode replacement */
508 n_iconv_close(fhicd
);
512 memcpy(*result
= salloc(sou
.l
+1), sou
.s
, sou
.l
+1);
515 return ((f
& _ERRORS
) != 0);
519 _mime_param_create(struct mime_param_builder
*self
)
521 struct mime_param_builder next
;
522 /* Don't use MIME_LINELEN_(MAX|LIMIT) stack buffer sizes: normally we won't
523 * exceed plain MIME_LINELEN, so that this would be a factor 10 wastage.
524 * On the other hand we may excess _LINELEN to avoid breaking up possible
525 * multibyte sequences until sizeof(buf) is reached, but since we (a) don't
526 * support stateful encodings and (b) will try to synchronize on UTF-8 this
527 * problem is scarce, possibly even artificial */
528 char buf
[MIN(MIME_LINELEN_MAX
>> 1, MIME_LINELEN
* 2)],
529 *bp
, *bp_max
, *bp_xmax
, *bp_lanoenc
;
530 char const *vb
, *vb_lanoenc
;
539 LCTA(sizeof(buf
) >= MIME_LINELEN
* 2);
542 self
->mpb_buf
= bp
= bp_lanoenc
= buf
;
543 self
->mpb_buf_len
= 0;
544 self
->mpb_is_enc
= ((f
& _ISENC
) != 0);
545 vb_lanoenc
= vb
= self
->mpb_value
;
546 vl
= self
->mpb_value_len
;
548 /* Configure bp_max to fit in SHOULD, bp_xmax to extent */
549 bp_max
= (buf
+ MIME_LINELEN
) -
550 (1 + self
->mpb_name_len
+ sizeof("*999*='';") -1 + 2);
551 bp_xmax
= (buf
+ sizeof(buf
)) -
552 (1 + self
->mpb_name_len
+ sizeof("*999*='';") -1 + 2);
553 if ((f
& _ISENC
) && self
->mpb_level
== 0) {
554 bp_max
-= self
->mpb_charset_len
;
555 bp_xmax
-= self
->mpb_charset_len
;
557 if (PTRCMP(bp_max
, <=, buf
+ sizeof("Hunky Dory"))) {
558 DBG( n_alert("_mime_param_create(): Hunky Dory!"); )
559 bp_max
= buf
+ (MIME_LINELEN
>> 1); /* And then it is SHOULD, anyway */
561 assert(PTRCMP(bp_max
+ (4 * 3), <=, bp_xmax
)); /* UTF-8 extra pad, below */
565 union {char c
; ui8_t uc
;} u
; u
.c
= *vb
;
569 if (u
.uc
> 0x7F || cntrlchar(u
.c
)) { /* XXX reject cntrlchar? */
570 /* We need to percent encode this character, possibly changing
571 * overall strategy, but anyway the one of this level, possibly
572 * rendering invalid any output byte we yet have produced here.
573 * Instead of throwing away that work just recurse if some fancy
574 * magic condition is true */
575 /* *However*, many tested MUAs fail to deal with parameters that
576 * are splitted across "too many" fields, including ones that
577 * misread RFC 2231 to allow only one digit, i.e., a maximum of
578 * ten. This is plain wrong, but that won't help their users */
579 if (PTR2SIZE(bp
- buf
) > /*10 (strawberry) COMPAT*/MIME_LINELEN
>>1)
585 if (u
.uc
== '"' || u
.uc
== '\\') {
591 } else if (u
.uc
> 0x7F || _rfc2231_etab
[u
.uc
]) {
594 n_c_to_hex_base16(bp
+ 1, u
.c
);
607 /* If all available space has been consumed we must split.
608 * Due to compatibility reasons we must take care not to break up
609 * multibyte sequences -- even though RFC 2231 rather implies that the
610 * splitted value should be joined (after percent encoded fields have
611 * been percent decoded) and the resulting string be treated in the
612 * specified character set / language, MUAs have been seen which apply
613 * the RFC 2047 encoded-words-are-atomic even to RFC 2231 values, even
614 * if stateful encodings cannot truly be supported like that?!..
616 * So split at 7-bit character if we have seen any and the wastage isn't
617 * too large; recall that we need to keep the overall number of P=V
618 * values as low as possible due to compatibility reasons.
619 * If we haven't seen any plain bytes be laxe and realize that bp_max
620 * reflects SHOULD lines, and try to extend this as long as possible.
621 * However, with UTF-8, try to backward synchronize on sequence start */
625 if ((f
& _HADRAW
) && (PTRCMP(bp
- bp_lanoenc
, <=, bp_lanoenc
- buf
) ||
626 (!self
->mpb_is_utf8
&&
627 PTR2SIZE(bp_lanoenc
- buf
) >= (MIME_LINELEN
>> 2)))) {
629 vl
+= PTR2SIZE(vb
- vb_lanoenc
);
634 if (self
->mpb_is_utf8
&& ((ui8_t
)(vb
[-1]) & 0xC0) != 0x80) {
647 /* That level made the great and completed encoding. Build result */
648 self
->mpb_is_enc
= ((f
& _ISENC
) != 0);
649 self
->mpb_buf_len
= PTR2SIZE(bp
- buf
);
650 __mime_param_join(self
);
655 /* Need to recurse, take care not to excess magical limit of 999 levels */
657 if (self
->mpb_level
== 999) {
658 if (options
& OPT_D_V_VV
)
659 n_err(_("Message RFC 2231 parameters nested too deeply!\n"));
663 self
->mpb_is_enc
= ((f
& _ISENC
) != 0);
664 self
->mpb_buf_len
= PTR2SIZE(bp
- buf
);
666 memset(&next
, 0, sizeof next
);
667 next
.mpb_next
= self
;
668 next
.mpb_level
= self
->mpb_level
+ 1;
669 next
.mpb_name_len
= self
->mpb_name_len
;
670 next
.mpb_value_len
= vl
;
671 next
.mpb_is_utf8
= self
->mpb_is_utf8
;
672 next
.mpb_name
= self
->mpb_name
;
674 _mime_param_create(&next
);
679 __mime_param_join(struct mime_param_builder
*head
)
682 struct mime_param_builder
*np
;
683 size_t i
, ll
; DBG( size_t len_max
; )
694 /* Traverse the stack upwards to find out result length (worst case).
695 * Reverse the list while doing so */
696 for (i
= 0, np
= head
, head
= NULL
; np
!= NULL
;) {
697 struct mime_param_builder
*tmp
;
699 i
+= np
->mpb_buf_len
+ np
->mpb_name_len
+ sizeof(" *999*=\"\";\n") -1;
709 i
+= head
->mpb_charset_len
; /* sizeof("''") -1 covered by \"\" above */
713 result
= head
->mpb_result
;
714 if (head
->mpb_next
!= NULL
)
716 cp
= result
->s
= salloc(i
+1);
718 for (ll
= 0, np
= head
;;) {
720 memcpy(cp
, np
->mpb_name
, i
= np
->mpb_name_len
);
725 char *cpo
= cp
, *nop
= nobuf
+ sizeof(nobuf
);
726 ui32_t noi
= np
->mpb_level
;
730 *--nop
= "0123456789"[noi
% 10];
731 while ((noi
/= 10) != 0);
737 ll
+= PTR2SIZE(cp
- cpo
);
740 if ((f
& _ISENC
) || np
->mpb_is_enc
) {
750 memcpy(cp
, np
->mpb_charset
, i
= np
->mpb_charset_len
);
756 } else if (!np
->mpb_is_enc
) {
762 memcpy(cp
, np
->mpb_buf
, i
= np
->mpb_buf_len
);
772 if ((np
= np
->mpb_next
) == NULL
)
778 i
+= np
->mpb_name_len
+ np
->mpb_buf_len
+ sizeof(" *999*=\"\";\n") -1;
779 if (i
>= MIME_LINELEN
) {
780 head
->mpb_rv
= -TRU1
;
789 result
->l
= PTR2SIZE(cp
- result
->s
);
790 assert(result
->l
< len_max
);
795 mime_param_get(char const *param
, char const *headerbody
) /* TODO rewr. */
803 plen
= strlen(param
);
806 /* At the beginning of headerbody there is no parameter=value pair xxx */
811 while (whitechar(*p
))
814 if (!ascncasecmp(p
, param
, plen
)) {
816 while (whitechar(*p
)) /* XXX? */
820 rv
= _rfc2231_param_parse(param
, plen
, p
);
823 if (!_mime_param_value_trim(&xval
, p
, NULL
)) {
829 /* We do have a result, but some (elder) software (S-nail <v14.8)
830 * will use RFC 2047 encoded words in parameter values, too */
831 /* TODO Automatically check whether the value seems to be RFC 2047
832 * TODO encwd. -- instead use *rfc2047_parameters* like mutt(1)? */
833 if ((p
= strstr(rv
, "=?")) != NULL
&& strstr(p
, "?=") != NULL
) {
836 ti
.l
= strlen(ti
.s
= rv
);
837 mime_fromhdr(&ti
, &to
, TD_ISPR
| TD_ICONV
| TD_DELCTRL
);
838 rv
= savestrbuf(to
.s
, to
.l
);
843 /* Not our desired parameter, skip and continue */
849 if (*(p
= _mime_param_skip(p
)) == '\0')
859 mime_param_create(struct str
*result
, char const *name
, char const *value
)
861 /* TODO All this needs rework when we have (1) a real string and even more
862 * TODO (2) use objects instead of stupid string concat; it's temporary
863 * TODO I.e., this function should return a HeaderBodyParam */
864 struct mime_param_builder top
;
868 memset(result
, 0, sizeof *result
);
870 memset(&top
, 0, sizeof top
);
871 top
.mpb_result
= result
;
872 if ((i
= strlen(top
.mpb_name
= name
)) > UI32_MAX
)
874 top
.mpb_name_len
= (ui32_t
)i
;
875 if ((i
= strlen(top
.mpb_value
= value
)) > UI32_MAX
)
877 top
.mpb_value_len
= (ui32_t
)i
;
878 if ((i
= strlen(name
= charset_get_lc())) > UI32_MAX
)
880 top
.mpb_charset
= salloc((top
.mpb_charset_len
= (ui32_t
)i
) +1);
881 for (i
= 0; *name
!= '\0'; ++i
, ++name
)
882 ((char*)UNCONST(top
.mpb_charset
))[i
] = lowerconv(*name
);
883 ((char*)UNCONST(top
.mpb_charset
))[i
] = '\0';
884 top
.mpb_is_utf8
= !ascncasecmp(top
.mpb_charset
, "utf-8",
885 top
.mpb_charset_len
);
887 _mime_param_create(&top
);
894 mime_param_boundary_get(char const *headerbody
, size_t *len
)
899 if ((p
= mime_param_get("boundary", headerbody
)) != NULL
) {
900 size_t sz
= strlen(p
);
904 q
= salloc(sz
+ 2 +1);
906 memcpy(q
+ 2, p
, sz
);
907 *(q
+ sz
+ 2) = '\0';
914 mime_param_boundary_create(void)
919 bp
= salloc(36 + 6 +1);
920 bp
[0] = bp
[2] = bp
[39] = bp
[41] = '=';
921 bp
[1] = bp
[40] = '-';
922 memcpy(bp
+ 3, getrandstring(36), 36);