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 - 2016 Steffen (Daode) Nurpmeso <steffen@sdaoden.eu>.
8 * Copyright (c) 1980, 1993
9 * The Regents of the University of California. 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. Neither the name of the University nor the names of its contributors
20 * may be used to endorse or promote products derived from this software
21 * without specific prior written permission.
23 * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
24 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
25 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
26 * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
27 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
28 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
29 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
30 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
31 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
32 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
36 #define n_FILE mime_parse
38 #ifndef HAVE_AMALGAMATION
42 static bool_t
_mime_parse_part(struct message
*zmp
, struct mimepart
*ip
,
43 enum mime_parse_flags mpf
, int level
);
45 static void _mime_parse_rfc822(struct message
*zmp
, struct mimepart
*ip
,
46 enum mime_parse_flags mpf
, int level
);
49 static void _mime_parse_pkcs7(struct message
*zmp
, struct mimepart
*ip
,
50 enum mime_parse_flags mpf
, int level
);
53 static void _mime_parse_multipart(struct message
*zmp
,
54 struct mimepart
*ip
, enum mime_parse_flags mpf
, int level
);
55 static void __mime_parse_new(struct mimepart
*ip
, struct mimepart
**np
,
56 off_t offs
, int *part
);
57 static void __mime_parse_end(struct mimepart
**np
, off_t xoffs
,
61 _mime_parse_part(struct message
*zmp
, struct mimepart
*ip
,
62 enum mime_parse_flags mpf
, int level
)
68 ip
->m_ct_type
= hfield1("content-type", (struct message
*)ip
);
69 if (ip
->m_ct_type
!= NULL
) {
70 ip
->m_ct_type_plain
= cp_b
= savestr(ip
->m_ct_type
);
71 if ((cp
= strchr(cp_b
, ';')) != NULL
)
73 cp
= cp_b
+ strlen(cp_b
);
74 while (cp
> cp_b
&& blankchar(cp
[-1]))
77 } else if (ip
->m_parent
!= NULL
&&
78 ip
->m_parent
->m_mimecontent
== MIME_DIGEST
)
79 ip
->m_ct_type_plain
= "message/rfc822";
81 ip
->m_ct_type_plain
= "text/plain";
82 ip
->m_ct_type_usr_ovwr
= NULL
;
84 if (ip
->m_ct_type
!= NULL
)
85 ip
->m_charset
= mime_param_get("charset", ip
->m_ct_type
);
86 if (ip
->m_charset
== NULL
)
87 ip
->m_charset
= charset_get_7bit();
89 if ((ip
->m_ct_enc
= hfield1("content-transfer-encoding",
90 (struct message
*)ip
)) == NULL
)
91 ip
->m_ct_enc
= mime_enc_from_conversion(CONV_7BIT
);
92 ip
->m_mime_enc
= mime_enc_from_ctehead(ip
->m_ct_enc
);
94 if (((cp
= hfield1("content-disposition", (struct message
*)ip
)) == NULL
||
95 (ip
->m_filename
= mime_param_get("filename", cp
)) == NULL
) &&
96 ip
->m_ct_type
!= NULL
)
97 ip
->m_filename
= mime_param_get("name", ip
->m_ct_type
);
99 ip
->m_mimecontent
= mime_type_classify_part(ip
);
101 if (mpf
& MIME_PARSE_PARTS
) {
102 if (level
> 9999) { /* TODO MAGIC */
103 n_err(_("MIME content too deeply nested\n"));
106 switch (ip
->m_mimecontent
) {
108 if (mpf
& MIME_PARSE_DECRYPT
) {
110 _mime_parse_pkcs7(zmp
, ip
, mpf
, level
);
113 n_err(_("No SSL support compiled in\n"));
121 case MIME_ALTERNATIVE
:
122 case MIME_RELATED
: /* TODO /related yet handled like /alternative */
124 _mime_parse_multipart(zmp
, ip
, mpf
, level
);
127 _mime_parse_rfc822(zmp
, ip
, mpf
, level
);
139 _mime_parse_rfc822(struct message
*zmp
, struct mimepart
*ip
,
140 enum mime_parse_flags mpf
, int level
)
150 if ((ibuf
= setinput(&mb
, (struct message
*)ip
, NEED_BODY
)) == NULL
)
155 while (cnt
&& ((c
= getc(ibuf
)) != EOF
)) {
166 np
= csalloc(1, sizeof *np
);
167 np
->m_flag
= MNOFROM
;
168 np
->m_have
= HAVE_HEADER
| HAVE_BODY
;
169 np
->m_block
= mailx_blockof(offs
);
170 np
->m_offset
= mailx_offsetof(offs
);
171 np
->m_size
= np
->m_xsize
= cnt
;
172 np
->m_lines
= np
->m_xlines
= lines
;
173 np
->m_partstring
= ip
->m_partstring
;
175 ip
->m_multipart
= np
;
177 if (ok_blook(rfc822_body_from_
)) {
178 substdate((struct message
*)np
);
179 np
->m_from
= fakefrom((struct message
*)np
);/* TODO strip MNOFROM flag? */
182 _mime_parse_part(zmp
, np
, mpf
, level
+ 1);
189 _mime_parse_pkcs7(struct message
*zmp
, struct mimepart
*ip
,
190 enum mime_parse_flags mpf
, int level
)
192 struct message m
, *xmp
;
197 memcpy(&m
, ip
, sizeof m
);
198 to
= hfield1("to", zmp
);
199 cc
= hfield1("cc", zmp
);
201 if ((xmp
= smime_decrypt(&m
, to
, cc
, 0)) != NULL
) {
202 np
= csalloc(1, sizeof *np
);
203 np
->m_flag
= xmp
->m_flag
;
204 np
->m_have
= xmp
->m_have
;
205 np
->m_block
= xmp
->m_block
;
206 np
->m_offset
= xmp
->m_offset
;
207 np
->m_size
= xmp
->m_size
;
208 np
->m_xsize
= xmp
->m_xsize
;
209 np
->m_lines
= xmp
->m_lines
;
210 np
->m_xlines
= xmp
->m_xlines
;
211 np
->m_partstring
= ip
->m_partstring
;
213 if (_mime_parse_part(zmp
, np
, mpf
, level
+ 1) == OKAY
) {
215 ip
->m_multipart
= np
;
220 #endif /* HAVE_SSL */
223 _mime_parse_multipart(struct message
*zmp
, struct mimepart
*ip
,
224 enum mime_parse_flags mpf
, int level
)
226 /* TODO Instead of the recursive multiple run parse we have today,
227 * TODO the send/MIME layer rewrite must create a "tree" of parts with
228 * TODO a single-pass parse, then address each part directly as
229 * TODO necessary; since boundaries start with -- and the content
230 * TODO rather forms a stack this is pretty cheap indeed! */
231 struct mimepart
*np
= NULL
;
232 char *boundary
, *line
= NULL
;
233 size_t linesize
= 0, linelen
, cnt
, boundlen
;
240 if ((boundary
= mime_param_boundary_get(ip
->m_ct_type
, &linelen
)) == NULL
)
244 if ((ibuf
= setinput(&mb
, (struct message
*)ip
, NEED_BODY
)) == NULL
)
248 while (fgetline(&line
, &linesize
, &cnt
, &linelen
, ibuf
, 0))
253 __mime_parse_new(ip
, &np
, offs
, NULL
);
254 while (fgetline(&line
, &linesize
, &cnt
, &linelen
, ibuf
, 0)) {
255 /* XXX linelen includes LF */
256 if (!((lines
> 0 || part
== 0) && linelen
> boundlen
&&
257 !strncmp(line
, boundary
, boundlen
))) {
262 /* Subpart boundary? */
263 if (line
[boundlen
] == '\n') {
266 __mime_parse_end(&np
, offs
- boundlen
- 2, lines
);
267 __mime_parse_new(ip
, &np
, offs
- boundlen
- 2, NULL
);
269 __mime_parse_end(&np
, offs
, 2);
270 __mime_parse_new(ip
, &np
, offs
, &part
);
275 /* Final boundary? Be aware of cases where there is no separating
276 * newline in between boundaries, as has been seen in a message with
277 * "Content-Type: multipart/appledouble;" */
278 if (linelen
< boundlen
+ 2)
280 linelen
-= boundlen
+ 2;
281 if (line
[boundlen
] != '-' || line
[boundlen
+ 1] != '-' ||
282 (linelen
> 0 && line
[boundlen
+ 2] != '\n'))
286 __mime_parse_end(&np
, offs
- boundlen
- 4, lines
);
287 __mime_parse_new(ip
, &np
, offs
- boundlen
- 4, NULL
);
289 __mime_parse_end(&np
, offs
+ cnt
, 2);
294 __mime_parse_end(&np
, offs
, lines
);
297 for (np
= ip
->m_multipart
; np
!= NULL
; np
= np
->m_nextpart
)
298 if (np
->m_mimecontent
!= MIME_DISCARD
)
299 _mime_parse_part(zmp
, np
, mpf
, level
+ 1);
306 __mime_parse_new(struct mimepart
*ip
, struct mimepart
**np
, off_t offs
,
313 *np
= csalloc(1, sizeof **np
);
314 (*np
)->m_flag
= MNOFROM
;
315 (*np
)->m_have
= HAVE_HEADER
| HAVE_BODY
;
316 (*np
)->m_block
= mailx_blockof(offs
);
317 (*np
)->m_offset
= mailx_offsetof(offs
);
321 sz
= (ip
->m_partstring
!= NULL
) ? strlen(ip
->m_partstring
) : 0;
323 (*np
)->m_partstring
= salloc(sz
);
324 if (ip
->m_partstring
)
325 snprintf((*np
)->m_partstring
, sz
, "%s.%u", ip
->m_partstring
, *part
);
327 snprintf((*np
)->m_partstring
, sz
, "%u", *part
);
329 (*np
)->m_mimecontent
= MIME_DISCARD
;
330 (*np
)->m_parent
= ip
;
332 if (ip
->m_multipart
) {
333 for (pp
= ip
->m_multipart
; pp
->m_nextpart
!= NULL
; pp
= pp
->m_nextpart
)
335 pp
->m_nextpart
= *np
;
337 ip
->m_multipart
= *np
;
342 __mime_parse_end(struct mimepart
**np
, off_t xoffs
, long lines
)
347 offs
= mailx_positionof((*np
)->m_block
, (*np
)->m_offset
);
348 (*np
)->m_size
= (*np
)->m_xsize
= xoffs
- offs
;
349 (*np
)->m_lines
= (*np
)->m_xlines
= lines
;
355 mime_parse_msg(struct message
*mp
, enum mime_parse_flags mpf
)
360 ip
= csalloc(1, sizeof *ip
);
361 ip
->m_flag
= mp
->m_flag
;
362 ip
->m_have
= mp
->m_have
;
363 ip
->m_block
= mp
->m_block
;
364 ip
->m_offset
= mp
->m_offset
;
365 ip
->m_size
= mp
->m_size
;
366 ip
->m_xsize
= mp
->m_xsize
;
367 ip
->m_lines
= mp
->m_lines
;
368 ip
->m_xlines
= mp
->m_lines
;
369 if (!_mime_parse_part(mp
, ip
, mpf
, 0))