1 /*@ S-nail - a mail user agent derived from Berkeley Mail.
2 *@ Parse a message into a tree of struct mimepart objects.
4 * Copyright (c) 2000-2004 Gunnar Ritter, Freiburg i. Br., Germany.
5 * Copyright (c) 2012 - 2018 Steffen (Daode) Nurpmeso <steffen@sdaoden.eu>.
6 * SPDX-License-Identifier: BSD-3-Clause
9 * Copyright (c) 1980, 1993
10 * The Regents of the University of California. 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. Neither the name of the University nor the names of its contributors
21 * may be used to endorse or promote products derived from this software
22 * without specific prior written permission.
24 * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
25 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
26 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
27 * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
28 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
29 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
30 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
31 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
32 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
33 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
37 #define n_FILE mime_parse
39 #ifndef HAVE_AMALGAMATION
44 static char * _mime_parse_ct_plain_from_ct(char const *cth
);
46 static bool_t
_mime_parse_part(struct message
*zmp
, struct mimepart
*ip
,
47 enum mime_parse_flags mpf
, int level
);
49 static void _mime_parse_rfc822(struct message
*zmp
, struct mimepart
*ip
,
50 enum mime_parse_flags mpf
, int level
);
53 static void _mime_parse_pkcs7(struct message
*zmp
, struct mimepart
*ip
,
54 enum mime_parse_flags mpf
, int level
);
57 static bool_t
_mime_parse_multipart(struct message
*zmp
,
58 struct mimepart
*ip
, enum mime_parse_flags mpf
, int level
);
59 static void __mime_parse_new(struct mimepart
*ip
, struct mimepart
**np
,
60 off_t offs
, int *part
);
61 static void __mime_parse_end(struct mimepart
**np
, off_t xoffs
,
65 _mime_parse_ct_plain_from_ct(char const *cth
)
72 if ((rv
= strchr(rv_b
, ';')) != NULL
)
75 rv
= rv_b
+ strlen(rv_b
);
76 while (rv
> rv_b
&& blankchar(rv
[-1]))
84 _mime_parse_part(struct message
*zmp
, struct mimepart
*ip
,
85 enum mime_parse_flags mpf
, int level
)
91 ip
->m_ct_type
= hfield1("content-type", (struct message
*)ip
);
92 if (ip
->m_ct_type
!= NULL
)
93 ip
->m_ct_type_plain
= _mime_parse_ct_plain_from_ct(ip
->m_ct_type
);
94 else if (ip
->m_parent
!= NULL
&& ip
->m_parent
->m_mimecontent
== MIME_DIGEST
)
95 ip
->m_ct_type_plain
= "message/rfc822";
97 ip
->m_ct_type_plain
= "text/plain";
98 ip
->m_ct_type_usr_ovwr
= NULL
;
100 if(ip
->m_ct_type
!= NULL
&&
101 (ip
->m_charset
= cp
= mime_param_get("charset", ip
->m_ct_type
)
103 ip
->m_charset
= n_iconv_normalize_name(cp
);
105 if (ip
->m_charset
== NULL
)
106 ip
->m_charset
= ok_vlook(charset_7bit
);
108 ip
->m_charset
= n_charsetalias_expand(ip
->m_charset
);
110 if ((ip
->m_ct_enc
= hfield1("content-transfer-encoding",
111 (struct message
*)ip
)) == NULL
)
112 ip
->m_ct_enc
= mime_enc_from_conversion(CONV_7BIT
);
113 ip
->m_mime_enc
= mime_enc_from_ctehead(ip
->m_ct_enc
);
115 if (((cp
= hfield1("content-disposition", (struct message
*)ip
)) == NULL
||
116 (ip
->m_filename
= mime_param_get("filename", cp
)) == NULL
) &&
117 ip
->m_ct_type
!= NULL
)
118 ip
->m_filename
= mime_param_get("name", ip
->m_ct_type
);
120 if ((cp
= hfield1("content-description", (struct message
*)ip
)) != NULL
)
121 ip
->m_content_description
= cp
;
123 if ((ip
->m_mimecontent
= n_mimetype_classify_part(ip
,
124 ((mpf
& MIME_PARSE_FOR_USER_CONTEXT
) != 0))) == MIME_822
) {
125 /* TODO (v15) HACK: message/rfc822 is treated special, that this one is
126 * TODO too stupid to apply content-decoding when (falsely) applied */
127 if (ip
->m_mime_enc
!= MIMEE_8B
&& ip
->m_mime_enc
!= MIMEE_7B
) {
128 n_err(_("Pre-v15 %s cannot handle (falsely) encoded message/rfc822\n"
129 " (not 7bit or 8bit)! Interpreting as text/plain!\n"),
131 ip
->m_mimecontent
= MIME_TEXT_PLAIN
;
135 assert(ip
->m_external_body_url
== NULL
);
136 if(!asccasecmp(ip
->m_ct_type_plain
, "message/external-body") &&
137 (cp
= mime_param_get("access-type", ip
->m_ct_type
)) != NULL
&&
138 !asccasecmp(cp
, "URL"))
139 ip
->m_external_body_url
= mime_param_get("url", ip
->m_ct_type
);
141 if (mpf
& MIME_PARSE_PARTS
) {
142 if (level
> 9999) { /* TODO MAGIC */
143 n_err(_("MIME content too deeply nested\n"));
146 switch (ip
->m_mimecontent
) {
148 if (mpf
& MIME_PARSE_DECRYPT
) {
150 _mime_parse_pkcs7(zmp
, ip
, mpf
, level
);
151 if (ip
->m_content_info
& CI_ENCRYPTED_OK
)
152 ip
->m_content_info
|= CI_EXPANDED
;
155 n_err(_("No SSL / S/MIME support compiled in\n"));
162 case MIME_ALTERNATIVE
:
163 case MIME_RELATED
: /* TODO /related yet handled like /alternative */
168 if (!_mime_parse_multipart(zmp
, ip
, mpf
, level
))
172 _mime_parse_rfc822(zmp
, ip
, mpf
, level
);
184 _mime_parse_rfc822(struct message
*zmp
, struct mimepart
*ip
,
185 enum mime_parse_flags mpf
, int level
)
195 if ((ibuf
= setinput(&mb
, (struct message
*)ip
, NEED_BODY
)) == NULL
)
200 while (cnt
&& ((c
= getc(ibuf
)) != EOF
)) {
211 np
= n_autorec_calloc(1, sizeof *np
);
212 np
->m_flag
= MNOFROM
;
213 np
->m_content_info
= CI_HAVE_HEADER
| CI_HAVE_BODY
;
214 np
->m_block
= mailx_blockof(offs
);
215 np
->m_offset
= mailx_offsetof(offs
);
216 np
->m_size
= np
->m_xsize
= cnt
;
217 np
->m_lines
= np
->m_xlines
= lines
;
218 np
->m_partstring
= ip
->m_partstring
;
220 ip
->m_multipart
= np
;
222 if (!(mpf
& MIME_PARSE_SHALLOW
) && ok_blook(rfc822_body_from_
)) {
223 substdate((struct message
*)np
);
224 np
->m_from
= fakefrom((struct message
*)np
);/* TODO strip MNOFROM flag? */
227 _mime_parse_part(zmp
, np
, mpf
, level
+ 1);
234 _mime_parse_pkcs7(struct message
*zmp
, struct mimepart
*ip
,
235 enum mime_parse_flags mpf
, int level
)
237 struct message m
, *xmp
;
242 memcpy(&m
, ip
, sizeof m
);
243 to
= hfield1("to", zmp
);
244 cc
= hfield1("cc", zmp
);
246 if ((xmp
= smime_decrypt(&m
, to
, cc
, 0)) != NULL
) {
247 np
= n_autorec_calloc(1, sizeof *np
);
248 np
->m_flag
= xmp
->m_flag
;
249 np
->m_content_info
= xmp
->m_content_info
| CI_ENCRYPTED
| CI_ENCRYPTED_OK
;
250 np
->m_block
= xmp
->m_block
;
251 np
->m_offset
= xmp
->m_offset
;
252 np
->m_size
= xmp
->m_size
;
253 np
->m_xsize
= xmp
->m_xsize
;
254 np
->m_lines
= xmp
->m_lines
;
255 np
->m_xlines
= xmp
->m_xlines
;
257 /* TODO using part "1" for decrypted content is a hack */
258 if ((np
->m_partstring
= ip
->m_partstring
) == NULL
)
259 ip
->m_partstring
= np
->m_partstring
= n_UNCONST(n_1
);
261 if (_mime_parse_part(zmp
, np
, mpf
, level
+ 1) == OKAY
) {
262 ip
->m_content_info
|= CI_ENCRYPTED
| CI_ENCRYPTED_OK
;
264 ip
->m_multipart
= np
;
267 ip
->m_content_info
|= CI_ENCRYPTED
| CI_ENCRYPTED_BAD
;
270 #endif /* HAVE_TLS */
273 _mime_parse_multipart(struct message
*zmp
, struct mimepart
*ip
,
274 enum mime_parse_flags mpf
, int level
)
276 struct mimepart
*np
= NULL
;
277 char *boundary
, *line
= NULL
;
278 size_t linesize
= 0, linelen
, cnt
, boundlen
;
285 if ((boundary
= mime_param_boundary_get(ip
->m_ct_type
, &linelen
)) == NULL
)
289 if ((ibuf
= setinput(&mb
, (struct message
*)ip
, NEED_BODY
)) == NULL
)
293 while (fgetline(&line
, &linesize
, &cnt
, &linelen
, ibuf
, 0))
298 /* TODO using part "1" for decrypted content is a hack */
299 if (ip
->m_partstring
== NULL
)
300 ip
->m_partstring
= n_UNCONST("1");
301 __mime_parse_new(ip
, &np
, offs
, NULL
);
303 while (fgetline(&line
, &linesize
, &cnt
, &linelen
, ibuf
, 0)) {
304 /* XXX linelen includes LF */
305 if (!((lines
> 0 || part
== 0) && linelen
> boundlen
&&
306 !strncmp(line
, boundary
, boundlen
))) {
311 /* Subpart boundary? */
312 if (line
[boundlen
] == '\n') {
315 __mime_parse_end(&np
, offs
- boundlen
- 2, lines
);
316 __mime_parse_new(ip
, &np
, offs
- boundlen
- 2, NULL
);
318 __mime_parse_end(&np
, offs
, 2);
319 __mime_parse_new(ip
, &np
, offs
, &part
);
324 /* Final boundary? Be aware of cases where there is no separating
325 * newline in between boundaries, as has been seen in a message with
326 * "Content-Type: multipart/appledouble;" */
327 if (linelen
< boundlen
+ 2)
329 linelen
-= boundlen
+ 2;
330 if (line
[boundlen
] != '-' || line
[boundlen
+ 1] != '-' ||
331 (linelen
> 0 && line
[boundlen
+ 2] != '\n'))
335 __mime_parse_end(&np
, offs
- boundlen
- 4, lines
);
336 __mime_parse_new(ip
, &np
, offs
- boundlen
- 4, NULL
);
338 __mime_parse_end(&np
, offs
+ cnt
, 2);
343 __mime_parse_end(&np
, offs
, lines
);
346 for (np
= ip
->m_multipart
; np
!= NULL
; np
= np
->m_nextpart
)
347 if (np
->m_mimecontent
!= MIME_DISCARD
)
348 _mime_parse_part(zmp
, np
, mpf
, level
+ 1);
358 __mime_parse_new(struct mimepart
*ip
, struct mimepart
**np
, off_t offs
,
365 *np
= n_autorec_calloc(1, sizeof **np
);
366 (*np
)->m_flag
= MNOFROM
;
367 (*np
)->m_content_info
= CI_HAVE_HEADER
| CI_HAVE_BODY
;
368 (*np
)->m_block
= mailx_blockof(offs
);
369 (*np
)->m_offset
= mailx_offsetof(offs
);
373 sz
= (ip
->m_partstring
!= NULL
) ? strlen(ip
->m_partstring
) : 0;
375 (*np
)->m_partstring
= n_autorec_alloc(sz
);
376 if (ip
->m_partstring
)
377 snprintf((*np
)->m_partstring
, sz
, "%s.%u", ip
->m_partstring
, *part
);
379 snprintf((*np
)->m_partstring
, sz
, "%u", *part
);
381 (*np
)->m_mimecontent
= MIME_DISCARD
;
382 (*np
)->m_parent
= ip
;
384 if (ip
->m_multipart
) {
385 for (pp
= ip
->m_multipart
; pp
->m_nextpart
!= NULL
; pp
= pp
->m_nextpart
)
387 pp
->m_nextpart
= *np
;
389 ip
->m_multipart
= *np
;
394 __mime_parse_end(struct mimepart
**np
, off_t xoffs
, long lines
)
399 offs
= mailx_positionof((*np
)->m_block
, (*np
)->m_offset
);
400 (*np
)->m_size
= (*np
)->m_xsize
= xoffs
- offs
;
401 (*np
)->m_lines
= (*np
)->m_xlines
= lines
;
407 mime_parse_msg(struct message
*mp
, enum mime_parse_flags mpf
)
412 ip
= n_autorec_calloc(1, sizeof *ip
);
413 ip
->m_flag
= mp
->m_flag
;
414 ip
->m_content_info
= mp
->m_content_info
;
415 ip
->m_block
= mp
->m_block
;
416 ip
->m_offset
= mp
->m_offset
;
417 ip
->m_size
= mp
->m_size
;
418 ip
->m_xsize
= mp
->m_xsize
;
419 ip
->m_lines
= mp
->m_lines
;
420 ip
->m_xlines
= mp
->m_lines
;
421 if (!_mime_parse_part(mp
, ip
, mpf
, 0))