1 /* $NetBSD: mime_decode.c,v 1.15 2009/04/10 13:08:25 christos Exp $ */
4 * Copyright (c) 2006 The NetBSD Foundation, Inc.
7 * This code is derived from software contributed to The NetBSD Foundation
10 * Redistribution and use in source and binary forms, with or without
11 * modification, are permitted provided that the following conditions
13 * 1. Redistributions of source code must retain the above copyright
14 * notice, this list of conditions and the following disclaimer.
15 * 2. Redistributions in binary form must reproduce the above copyright
16 * notice, this list of conditions and the following disclaimer in the
17 * documentation and/or other materials provided with the distribution.
19 * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS
20 * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
21 * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
22 * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS
23 * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
24 * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
25 * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
26 * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
27 * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
28 * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
29 * POSSIBILITY OF SUCH DAMAGE.
35 #include <sys/cdefs.h>
37 __RCSID("$NetBSD: mime_decode.c,v 1.15 2009/04/10 13:08:25 christos Exp $");
38 #endif /* not __lint__ */
58 #include "mime_child.h"
59 #include "mime_codecs.h"
60 #include "mime_header.h"
61 #include "mime_detach.h"
69 * XXX - This block for debugging only and eventually should go away.
72 show_one_mime_info(FILE *fp
, struct mime_info
*mip
)
74 #define XX(a) (a) ? (a) : "<null>"
76 (void)fprintf(fp
, ">> --------\n");
77 (void)fprintf(fp
, "mip %d:\n", mip
->mi_partnum
);
78 (void)fprintf(fp
, "** Version: %s\n", XX(mip
->mi_version
));
79 (void)fprintf(fp
, "** type: %s\n", XX(mip
->mi_type
));
80 (void)fprintf(fp
, "** subtype: %s\n", XX(mip
->mi_subtype
));
81 (void)fprintf(fp
, "** boundary: %s\n", XX(mip
->mi_boundary
));
82 (void)fprintf(fp
, "** charset: %s\n", XX(mip
->mi_charset
));
83 (void)fprintf(fp
, "** encoding: %s\n", XX(mip
->mi_encoding
));
84 (void)fprintf(fp
, "** disposition: %s\n", XX(mip
->mi_disposition
));
85 (void)fprintf(fp
, "** filename: %s\n", XX(mip
->mi_filename
));
86 (void)fprintf(fp
, "** %p: flag: 0x%x, block: %ld, offset: %d, size: %lld, lines: %ld:%ld\n",
89 mip
->mp
->m_block
, mip
->mp
->m_offset
, mip
->mp
->m_size
,
90 mip
->mp
->m_lines
, mip
->mp
->m_blines
);
91 (void)fprintf(fp
, "** mip: %p\n", mip
);
92 (void)fprintf(fp
, "** mi_flink: %p\n", mip
->mi_flink
);
93 (void)fprintf(fp
, "** mi_blink: %p\n", mip
->mi_blink
);
94 (void)fprintf(fp
, "** mip %p, mp %p, parent_mip %p, parent_mp %p\n",
95 mip
, mip
->mp
, mip
->mi_parent
.mip
, mip
->mi_parent
.mp
);
97 (void)fprintf(fp
, "** mi_fo %p, mi_head_end %p, mi_pipe_end %p\n",
98 mip
->mi_fo
, mip
->mi_head_end
, mip
->mi_pipe_end
);
100 (void)fprintf(fp
, "** mi_ignore_body: %d\n", mip
->mi_ignore_body
);
101 (void)fprintf(fp
, "** mi_partnum: %d\n", mip
->mi_partnum
);
102 (void)fprintf(fp
, "** mi_partstr: %s\n", mip
->mi_partstr
);
103 (void)fprintf(fp
, "** mi_msgstr: %s\n", mip
->mi_msgstr
);
112 show_mime_info(FILE *fp
, struct mime_info
*mip
, struct mime_info
*end_mip
)
114 for (/* EMTPY */; mip
!= end_mip
; mip
= mip
->mi_flink
)
115 show_one_mime_info(fp
, mip
);
117 (void)fprintf(fp
, "++ =========\n");
120 #endif /* __lint__ */
125 * Our interface to the file registry in popen.c
128 pipe_end(struct mime_info
*mip
)
131 fp
= last_registered_file(0); /* get last registered file or pipe */
138 * Copy the first ';' delimited substring from 'src' (null terminated)
139 * into 'dst', expanding quotes and removing comments (as per RFC
140 * 822). Returns a pointer in src to the next non-white character
141 * following ';'. The caller is responsible for ensuring 'dst' is
142 * sufficiently large to hold the result.
145 get_param(char *dst
, char *src
)
154 for (cp
= src
; *cp
&& *cp
!= ';'; cp
++) {
156 case '"': /* start of quoted string */
157 for (cp
++; *cp
; cp
++) {
160 if (*cp
== '\\' && cp
[1] != '\0')
166 case '(': /* start of comment */
168 while (nesting
> 0 && *++cp
) {
169 if (*cp
== '\\' && cp
[1] != '\0')
182 /* remove trailing white space */
183 while (cp2
> lastq
&& is_WSP(cp2
[-1]))
194 * if field is NULL, return the content "specifier".
197 cparam(const char field
[], char *src
, int downcase
)
205 dst
= salloc(strlen(src
) + 1); /* large enough for any param in src */
207 cp
= get_param(dst
, cp
);
212 while (*cp
!= '\0') {
213 size_t len
= strlen(field
);
214 cp
= get_param(dst
, cp
);
215 if (strncasecmp(dst
, field
, len
) == 0 && dst
[len
] == '=') {
228 get_content(struct mime_info
*mip
)
230 char *mime_disposition_field
;
231 char *mime_type_field
;
237 mip
->mi_version
= cparam(NULL
, hfield(MIME_HDR_VERSION
, mp
), 0);
238 mip
->mi_encoding
= cparam(NULL
, hfield(MIME_HDR_ENCODING
, mp
), 1);
240 mime_type_field
= hfield(MIME_HDR_TYPE
, mp
);
241 mip
->mi_type
= cparam(NULL
, mime_type_field
, 1);
243 cp
= strchr(mip
->mi_type
, '/');
246 mip
->mi_subtype
= cp
;
248 mip
->mi_boundary
= cparam("boundary", mime_type_field
, 0);
249 mip
->mi_charset
= cparam("charset", mime_type_field
, 1);
251 mime_disposition_field
= hfield(MIME_HDR_DISPOSITION
, mp
);
252 mip
->mi_disposition
= cparam(NULL
, mime_disposition_field
, 1);
254 * The type field typically has a "name" parameter for "image"
255 * and "video" types, and I assume for other types as well.
256 * We grab it, but override it if the disposition field has a
257 * filename parameter as it often does for "attachments".
258 * More careful analysis could be done, but this seems to work
261 filename
= cparam("name", mime_type_field
, 0);
262 if ((cp
= cparam("filename", mime_disposition_field
, 0)) != NULL
)
265 filename
= basename(filename
); /* avoid absolute pathnames */
266 filename
= savestr(filename
); /* save it! */
268 mip
->mi_filename
= filename
;
271 * XXX: If we have a "Content-Type" in the header, then assume
272 * we also have a "MIME-Version: 1.0". This fixes some broken
273 * MIME headers that I have seen occasionally.
275 if (mip
->mi_version
== NULL
&& mip
->mi_type
!= NULL
)
276 mip
->mi_version
= MIME_VERSION
;
279 static struct message
*
280 salloc_message(int flag
, long block
, short offset
)
283 /* use csalloc in case someone adds a field someday! */
284 mp
= csalloc(1, sizeof(*mp
));
287 mp
->m_offset
= offset
;
296 static struct mime_info
*
297 insert_new_mip(struct mime_info
*this_mip
, struct mime_info
*top_mip
,
298 struct message
*top_mp
, off_t end_pos
, int partnum
)
300 struct mime_info
*new_mip
;
302 new_mip
= csalloc(1, sizeof(*new_mip
));
303 new_mip
->mi_blink
= this_mip
;
304 new_mip
->mi_flink
= this_mip
->mi_flink
;
305 this_mip
->mi_flink
= new_mip
;
307 new_mip
->mp
= salloc_message(this_mip
->mp
->m_flag
,
308 (long)blockof(end_pos
), blkoffsetof(end_pos
));
310 new_mip
->mi_parent
.mip
= top_mip
;
311 new_mip
->mi_parent
.mp
= top_mp
;
312 new_mip
->mi_partnum
= partnum
;
318 split_multipart(struct mime_info
*top_mip
)
321 struct message
*top_mp
;
322 struct message
*this_mp
;
323 struct mime_info
*this_mip
;
325 const char *boundary
;
327 long lines_left
; /* must be signed and same size as m_lines */
331 top_mp
= top_mip
->mp
;
332 this_mp
= salloc_message(top_mp
->m_flag
, top_mp
->m_block
, top_mp
->m_offset
);
334 this_mip
->mp
= this_mp
;
337 /* top_mip->mi_partnum = partnum++; */ /* Keep the number set by the caller */
339 boundary
= top_mip
->mi_boundary
;
340 boundary_len
= boundary
? strlen(boundary
) : 0;
342 fp
= setinput(top_mp
);
343 beg_pos
= ftello(fp
);
345 warnx("beg_pos: %lld, m_lines: %ld, m_blines: %ld",
346 beg_pos
, top_mp
->m_lines
, top_mp
->m_blines
);
348 for (lines_left
= top_mp
->m_lines
- 1; lines_left
>= 0; lines_left
--) {
352 line
= fgetln(fp
, &line_len
);
354 this_mp
->m_lines
++; /* count the message lines */
357 this_mp
->m_blines
++; /* count the body lines */
359 if (lines_left
== 0 || (
361 line_len
>= boundary_len
+ 2 &&
362 line
[0] == '-' && line
[1] == '-' &&
363 strncmp(line
+ 2, boundary
, boundary_len
) == 0)) {
367 cur_pos
= ftello(fp
);
369 /* the boundary belongs to the next part */
370 end_pos
= cur_pos
- line_len
;
371 this_mp
->m_lines
-= 1;
372 this_mp
->m_blines
-= 1;
374 this_mp
->m_size
= end_pos
- beg_pos
;
376 warnx("end_pos: %lld, m_lines: %ld, m_blines: %ld",
377 end_pos
, this_mp
->m_lines
, this_mp
->m_blines
);
379 if (line
[boundary_len
+ 2] == '-' &&
380 line
[boundary_len
+ 3] == '-') {/* end of multipart */
381 /* do a sanity check on the EOM */
382 if (lines_left
!= 1) {
384 * XXX - this can happen!
385 * Should we display the
386 * trailing garbage or check
387 * that it is blank or just
391 (void)printf("EOM: lines left: %ld\n", lines_left
);
394 break; /* XXX - stop at this point or grab the rest? */
396 this_mip
= insert_new_mip(this_mip
, top_mip
, top_mp
, end_pos
, partnum
++);
397 this_mp
= this_mip
->mp
;
398 this_mp
->m_lines
= 1; /* already read the first line in the header! */
409 split_message(struct mime_info
*top_mip
)
411 struct mime_info
*this_mip
;
412 struct message
*top_mp
;
413 struct message
*this_mp
;
416 long lines_left
; /* must be same size as m_lines */
419 top_mp
= top_mip
->mp
;
420 this_mp
= salloc_message(top_mp
->m_flag
, top_mp
->m_block
, top_mp
->m_offset
);
422 this_mip
->mp
= this_mp
;
426 fp
= setinput(top_mp
);
427 beg_pos
= ftello(fp
);
429 for (lines_left
= top_mp
->m_lines
; lines_left
> 0; lines_left
--) {
432 (void)fgetln(fp
, &line_len
);
434 this_mp
->m_lines
++; /* count the message lines */
436 this_mp
->m_blines
++; /* count the body lines */
438 if (in_header
&& line_len
== 1) { /* end of header */
440 end_pos
= ftello(fp
);
441 this_mp
->m_size
= end_pos
- beg_pos
;
442 this_mip
= insert_new_mip(this_mip
, top_mip
,top_mp
, end_pos
, 0);
443 this_mp
= this_mip
->mp
;
444 this_mp
->m_lines
= 1; /* we already counted one line in the header! */
446 in_header
= 0; /* never in header again */
450 /* close the last message */
451 this_mp
->m_size
= ftello(fp
) - beg_pos
;
456 get_command_hook(struct mime_info
*mip
, const char *domain
)
461 if (mip
->mi_type
== NULL
)
464 /* XXX - should we use easprintf() here? We are probably
465 * hosed elsewhere if this fails anyway. */
468 if (mip
->mi_subtype
) {
469 if (asprintf(&key
, "mime%s-%s-%s",
470 domain
, mip
->mi_type
, mip
->mi_subtype
) == -1) {
471 warn("get_command_hook: subtupe: asprintf");
478 if (asprintf(&key
, "mime%s-%s", domain
, mip
->mi_type
) == -1) {
479 warn("get_command_hook: type: asprintf");
490 is_basic_alternative(struct mime_info
*mip
)
493 strcasecmp(mip
->mi_type
, "text") == 0 &&
494 strcasecmp(mip
->mi_subtype
, "plain") == 0;
497 static struct mime_info
*
498 select_alternative(struct mime_info
*top_mip
, struct mime_info
*end_mip
)
500 struct mime_info
*the_mip
; /* the chosen alternate */
501 struct mime_info
*this_mip
;
503 * The alternates are supposed to occur in order of
504 * increasing "complexity". So: if there is at least
505 * one alternate of type "text/plain", use the last
506 * one, otherwise default to the first alternate.
508 the_mip
= top_mip
->mi_flink
;
509 for (this_mip
= top_mip
->mi_flink
;
511 this_mip
= this_mip
->mi_flink
) {
514 if (this_mip
->mi_type
== NULL
||
515 this_mip
->mi_subtype
== NULL
)
518 if (is_basic_alternative(this_mip
))
521 (cmd
= get_command_hook(this_mip
, "-hook")) ||
522 (cmd
= get_command_hook(this_mip
, "-head")) ||
523 (cmd
= get_command_hook(this_mip
, "-body"))) {
525 /* just get the flags. */
526 flags
= mime_run_command(cmd
, NULL
);
527 if ((flags
& CMD_FLAG_ALTERNATIVE
) != 0)
536 is_multipart(struct mime_info
*mip
)
538 return mip
->mi_type
&&
539 strcasecmp("multipart", mip
->mi_type
) == 0;
542 is_message(struct mime_info
*mip
)
544 return mip
->mi_type
&&
545 strcasecmp("message", mip
->mi_type
) == 0;
549 is_alternative(struct mime_info
*mip
)
551 return mip
->mi_subtype
&&
552 strcasecmp("alternative", mip
->mi_subtype
) == 0;
557 * Take a mime_info pointer and expand it recursively into all its
558 * mime parts. Only "multipart" and "message" types recursed into;
559 * they are handled separately.
561 static struct mime_info
*
562 expand_mip(struct mime_info
*top_mip
)
564 struct mime_info
*this_mip
;
565 struct mime_info
*next_mip
;
567 if (top_mip
->mi_partnum
== 0) {
568 if (top_mip
->mi_blink
)
569 top_mip
->mi_partstr
= top_mip
->mi_blink
->mi_partstr
;
571 else if (top_mip
->mi_parent
.mip
) {
574 prefix
= top_mip
->mi_parent
.mip
->mi_partstr
;
575 (void)sasprintf(&cp
, "%s%s%d", prefix
,
576 *prefix
? "." : "", top_mip
->mi_partnum
);
577 top_mip
->mi_partstr
= cp
;
580 next_mip
= top_mip
->mi_flink
;
582 if (is_multipart(top_mip
)) {
583 top_mip
->mi_ignore_body
= 1; /* the first body is ignored */
584 split_multipart(top_mip
);
586 for (this_mip
= top_mip
->mi_flink
;
587 this_mip
!= next_mip
;
588 this_mip
= this_mip
->mi_flink
) {
589 get_content(this_mip
);
591 if (is_alternative(top_mip
)) {
592 this_mip
= select_alternative(top_mip
, next_mip
);
593 this_mip
->mi_partnum
= 0; /* suppress partnum display */
594 this_mip
->mi_flink
= next_mip
;
595 this_mip
->mi_blink
= top_mip
;
596 top_mip
->mi_flink
= this_mip
;
599 * Recurse into each part.
601 for (this_mip
= top_mip
->mi_flink
;
602 this_mip
!= next_mip
;
603 this_mip
= expand_mip(this_mip
))
606 else if (is_message(top_mip
)) {
607 top_mip
->mi_ignore_body
= 1; /* the first body is ignored */
608 split_message(top_mip
);
610 this_mip
= top_mip
->mi_flink
;
612 get_content(this_mip
);
614 * If the one part is MIME encoded, recurse into it.
615 * XXX - Should this be conditional on subtype "rcs822"?
617 if (this_mip
->mi_type
&&
618 this_mip
->mi_version
&&
619 equal(this_mip
->mi_version
, MIME_VERSION
)) {
620 this_mip
->mi_partnum
= 0;
621 (void)expand_mip(this_mip
);
631 show_partnum(FILE *fp
, struct mime_info
*mip
)
635 if (mip
->mi_parent
.mip
&& mip
->mi_parent
.mip
->mi_parent
.mip
)
636 need_dot
= show_partnum(fp
, mip
->mi_parent
.mip
);
638 if (mip
->mi_partnum
) {
639 (void)fprintf(fp
, "%s%d", need_dot
? "." : "", mip
->mi_partnum
);
647 PUBLIC
struct mime_info
*
648 mime_decode_open(struct message
*mp
)
650 struct mime_info
*mip
;
653 mip
= csalloc(1, sizeof(*mip
));
654 mip
->mp
= salloc(sizeof(*mip
->mp
));
655 *mip
->mp
= *mp
; /* copy this so we don't trash the master mp */
659 /* RFC 2049 - sec 2 item 1 */
660 if (mip
->mi_version
== NULL
||
661 !equal(mip
->mi_version
, MIME_VERSION
))
664 mip
->mi_partstr
= "";
666 (void)expand_mip(mip
);
669 * Get the pipe_end and propagate it down the chain.
671 mip
->mi_pipe_end
= last_registered_file(0); /* for mime_decode_close() */
672 for (p
= mip
->mi_flink
; p
; p
= p
->mi_flink
)
673 p
->mi_pipe_end
= mip
->mi_pipe_end
;
675 /* show_mime_info(stderr, mip, NULL); */
682 mime_decode_close(struct mime_info
*mip
)
685 close_top_files(mip
->mi_pipe_end
);
689 struct prefix_line_args_s
{
695 prefix_line(FILE *fi
, FILE *fo
, void *cookie
)
697 struct prefix_line_args_s
*args
;
704 prefix
= args
->prefix
;
705 prefixlen
= args
->prefixlen
;
707 while ((line
= fgetln(fi
, &length
)) != NULL
) {
709 (void)fputs(prefix
, fo
);
711 (void)fwrite(prefix
, sizeof(*prefix
),
713 (void)fwrite(line
, sizeof(*line
), length
, fo
);
719 mime_sendmessage(struct message
*mp
, FILE *obuf
, struct ignoretab
*igntab
,
720 const char *prefix
, struct mime_info
*mip
)
724 const char *detachdir
;
728 return obuf
? /* were we trying to detach? */
729 sendmessage(mp
, obuf
, igntab
, prefix
, NULL
) : 0;
731 * The prefix has two meanigs which we handle here:
732 * 1) If obuf == NULL, then we are detaching to the 'prefix' directory.
733 * 2) If obuf != NULL, then the prefix is prepended to each line.
736 detachall_flag
= igntab
== detachall
;
738 assert(prefix
!= NULL
); /* coding error! */
739 if ((obuf
= last_registered_file(0)) == NULL
)
743 igntab
= ignoreall
; /* always ignore the headers */
746 * Set this early so pipe_end() will work!
750 (void)fflush(obuf
); /* Be safe and flush! XXX - necessary? */
753 * Handle the prefix as a pipe stage so it doesn't get seen by
754 * any decoding or hooks.
756 if (prefix
!= NULL
) {
757 static struct prefix_line_args_s prefix_line_args
;
758 const char *dp
, *dp2
= NULL
;
759 for (dp
= prefix
; *dp
; dp
++)
762 prefix_line_args
.prefixlen
= dp2
== 0 ? 0 : dp2
- prefix
+ 1;
763 prefix_line_args
.prefix
= prefix
;
764 mime_run_function(prefix_line
, pipe_end(mip
), (void*)&prefix_line_args
);
767 end_of_prefix
= last_registered_file(0);
769 for (/*EMPTY*/; mip
; mip
= mip
->mi_flink
) {
771 mip
->mi_head_end
= obuf
;
772 mip
->mi_detachdir
= detachdir
;
773 mip
->mi_detachall
= detachall_flag
;
774 error
|= sendmessage(mip
->mp
, pipe_end(mip
), igntab
, NULL
, mip
);
775 close_top_files(end_of_prefix
); /* don't close the prefixer! */
781 #ifdef CHARSET_SUPPORT
782 /**********************************************
783 * higher level interface to run mime_ficonv().
786 run_mime_ficonv(struct mime_info
*mip
, const char *charset
)
793 if (charset
== NULL
||
794 mip
->mi_charset
== NULL
||
795 strcasecmp(mip
->mi_charset
, charset
) == 0 ||
796 strcasecmp(mip
->mi_charset
, "unknown") == 0)
799 cd
= iconv_open(charset
, mip
->mi_charset
);
800 if (cd
== (iconv_t
)-1) {
801 (void)fprintf(fo
, "\t [ iconv_open failed: %s ]\n\n",
803 (void)fflush(fo
); /* flush here or see double! */
807 if (mip
->mi_detachdir
== NULL
&& /* don't contaminate the detach! */
808 value(ENAME_MIME_CHARSET_VERBOSE
))
809 (void)fprintf(fo
, "\t[ converting %s -> %s ]\n\n",
810 mip
->mi_charset
, charset
);
812 mime_run_function(mime_ficonv
, fo
, cd
);
814 (void)iconv_close(cd
);
816 #endif /* CHARSET_SUPPORT */
820 run_decoder(struct mime_info
*mip
, void(*fn
)(FILE*, FILE*, void *))
822 #ifdef CHARSET_SUPPORT
825 charset
= value(ENAME_MIME_CHARSET
);
826 if (charset
&& mip
->mi_type
&& strcasecmp(mip
->mi_type
, "text") == 0)
827 run_mime_ficonv(mip
, charset
);
828 #endif /* CHARSET_SUPPORT */
830 if (mip
->mi_detachdir
== NULL
&&
831 fn
== mime_fio_copy
)/* XXX - avoid an extra unnecessary pipe stage */
834 mime_run_function(fn
, pipe_end(mip
),
835 mip
->mi_detachdir
? NULL
: __UNCONST("add_lf"));
840 * Determine how to handle the display based on the type and subtype
844 DM_IGNORE
= 0x00, /* silently ignore part - must be zero! */
845 DM_DISPLAY
, /* decode and display the part */
846 DM_UNKNOWN
, /* unknown display */
847 DM_BINARY
, /* indicate binary data */
848 DM_PGPSIGN
, /* OpenPGP signed part */
849 DM_PGPENCR
, /* OpenPGP encrypted part */
850 DM_PGPKEYS
, /* OpenPGP keys part */
851 DM_SENTINEL
/* end marker; shouldn't be used */
853 #define APPLICATION_OCTET_STREAM DM_BINARY
855 static enum dispmode_e
856 get_display_mode(struct mime_info
*mip
, mime_codec_t dec
)
858 struct mime_subtype_s
{
860 enum dispmode_e st_dispmode
;
864 const struct mime_subtype_s
*mt_subtype
;
865 enum dispmode_e mt_dispmode
; /* default if NULL subtype */
867 static const struct mime_subtype_s text_subtype_tbl
[] = {
868 { "plain", DM_DISPLAY
},
869 { "html", DM_DISPLAY
}, /* rfc2854 */
870 { "rfc822-headers", DM_DISPLAY
},
871 { "css", DM_DISPLAY
}, /* rfc2318 */
872 { "enriched", DM_DISPLAY
}, /* rfc1523/rfc1563/rfc1896 */
873 { "graphics", DM_DISPLAY
}, /* rfc0553 */
874 { "nroff", DM_DISPLAY
}, /* rfc4263 */
875 { "red", DM_DISPLAY
}, /* rfc4102 */
876 { NULL
, DM_DISPLAY
} /* default */
878 static const struct mime_subtype_s image_subtype_tbl
[] = {
879 { "tiff", DM_BINARY
}, /* rfc2302/rfc3302 */
880 { "tiff-fx", DM_BINARY
}, /* rfc3250/rfc3950 */
881 { "t38", DM_BINARY
}, /* rfc3362 */
882 { NULL
, DM_BINARY
} /* default */
884 static const struct mime_subtype_s audio_subtype_tbl
[] = {
885 { "mpeg", DM_BINARY
}, /* rfc3003 */
886 { "t38", DM_BINARY
}, /* rfc4612 */
887 { NULL
, DM_BINARY
} /* default */
889 static const struct mime_subtype_s video_subtype_tbl
[] = {
890 { NULL
, DM_BINARY
} /* default */
892 static const struct mime_subtype_s application_subtype_tbl
[] = {
893 { "octet-stream", APPLICATION_OCTET_STREAM
},
894 { "pgp-encrypted", DM_PGPENCR
}, /* rfc3156 */
895 { "pgp-keys", DM_PGPKEYS
}, /* rfc3156 */
896 { "pgp-signature", DM_PGPSIGN
}, /* rfc3156 */
897 { "pdf", DM_BINARY
}, /* rfc3778 */
898 { "whoispp-query", DM_UNKNOWN
}, /* rfc2957 */
899 { "whoispp-response", DM_UNKNOWN
}, /* rfc2958 */
900 { "font-tdpfr", DM_UNKNOWN
}, /* rfc3073 */
901 { "xhtml+xml", DM_UNKNOWN
}, /* rfc3236 */
902 { "ogg", DM_UNKNOWN
}, /* rfc3534 */
903 { "rdf+xml", DM_UNKNOWN
}, /* rfc3870 */
904 { "soap+xml", DM_UNKNOWN
}, /* rfc3902 */
905 { "mbox", DM_UNKNOWN
}, /* rfc4155 */
906 { "xv+xml", DM_UNKNOWN
}, /* rfc4374 */
907 { "smil", DM_UNKNOWN
}, /* rfc4536 */
908 { "smil+xml", DM_UNKNOWN
}, /* rfc4536 */
909 { "json", DM_UNKNOWN
}, /* rfc4627 */
910 { "voicexml+xml", DM_UNKNOWN
}, /* rfc4267 */
911 { "ssml+xml", DM_UNKNOWN
}, /* rfc4267 */
912 { "srgs", DM_UNKNOWN
}, /* rfc4267 */
913 { "srgs+xml", DM_UNKNOWN
}, /* rfc4267 */
914 { "ccxml+xml", DM_UNKNOWN
}, /* rfc4267 */
915 { "pls+xml.", DM_UNKNOWN
}, /* rfc4267 */
916 { NULL
, APPLICATION_OCTET_STREAM
} /* default */
918 static const struct mime_type_s mime_type_tbl
[] = {
919 { "text", text_subtype_tbl
, DM_DISPLAY
},
920 { "image", image_subtype_tbl
, DM_IGNORE
},
921 { "audio", audio_subtype_tbl
, DM_IGNORE
},
922 { "video", video_subtype_tbl
, DM_IGNORE
},
923 { "application", application_subtype_tbl
, APPLICATION_OCTET_STREAM
},
924 { NULL
, NULL
, DM_UNKNOWN
}, /* default */
926 const struct mime_type_s
*mtp
;
927 const struct mime_subtype_s
*stp
;
929 const char *mi_subtype
;
932 * Silently ignore all multipart bodies.
933 * 1) In the case of "multipart" types, this typically
934 * contains a message for non-mime enabled mail readers.
935 * 2) In the case of "message" type, there should be no body.
937 if (mip
->mi_ignore_body
) /*is_multipart(mip) || is_message(mip))*/
941 * If the encoding type given but not recognized, treat block
942 * as "application/octet-stream". rfc 2049 sec 2 part 2.
944 if (mip
->mi_encoding
&& dec
== NULL
)
945 return APPLICATION_OCTET_STREAM
;
947 mi_type
= mip
->mi_type
;
948 mi_subtype
= mip
->mi_type
? mip
->mi_subtype
: NULL
;
951 * If there was no type specified, display anyway so we don't
952 * miss anything. (The encoding type is known.)
955 return DM_DISPLAY
; /* XXX - default to something safe! */
957 for (mtp
= mime_type_tbl
; mtp
->mt_type
; mtp
++) {
958 if (strcasecmp(mtp
->mt_type
, mi_type
) == 0) {
959 if (mi_subtype
== NULL
)
960 return mtp
->mt_dispmode
;
961 for (stp
= mtp
->mt_subtype
; stp
->st_name
; stp
++) {
962 if (strcasecmp(stp
->st_name
, mi_subtype
) == 0)
963 return stp
->st_dispmode
;
965 return stp
->st_dispmode
;
968 return mtp
->mt_dispmode
;
973 mime_decode_body(struct mime_info
*mip
)
975 static enum dispmode_e dispmode
;
979 /* close anything left over from mime_decode_head() */
980 close_top_files(mip
->mi_head_end
);
983 * Make sure we flush everything down the pipe so children
986 (void)fflush(pipe_end(mip
));
988 if (mip
->mi_detachdir
) /* We are detaching! Ignore the hooks. */
989 return mime_detach_parts(mip
);
992 if (mip
->mi_command_hook
== NULL
)
993 cmd
= get_command_hook(mip
, "-body");
995 dec
= mime_fio_decoder(mip
->mi_encoding
);
998 * If there is a filter running, we need to send the message
999 * to it. Otherwise, get the default display mode for this body.
1001 dispmode
= cmd
|| mip
->mi_command_hook
? DM_DISPLAY
: get_display_mode(mip
, dec
);
1003 if (dec
== NULL
) /* make sure we have a usable decoder */
1004 dec
= mime_fio_decoder(MIME_TRANSFER_7BIT
);
1006 if (dispmode
== DM_DISPLAY
) {
1009 /* just get the flags */
1010 flags
= mime_run_command(mip
->mi_command_hook
, NULL
);
1012 flags
= mime_run_command(cmd
, pipe_end(mip
));
1013 if ((flags
& CMD_FLAG_NO_DECODE
) == 0)
1014 run_decoder(mip
, dec
);
1015 return pipe_end(mip
);
1018 static const struct msg_tbl_s
{
1022 { DM_BINARY
, "binary content" },
1023 { DM_PGPSIGN
, "OpenPGP signature" },
1024 { DM_PGPENCR
, "OpenPGP encrypted" },
1025 { DM_PGPKEYS
, "OpenPGP keys" },
1026 { DM_UNKNOWN
, "unknown data" },
1027 { DM_IGNORE
, NULL
},
1028 { DM_SENTINEL
, NULL
},
1030 const struct msg_tbl_s
*mp
;
1032 for (mp
= msg_tbl
; mp
->dm
!= DM_SENTINEL
; mp
++)
1033 if (mp
->dm
== dispmode
)
1036 assert(mp
->dm
!= DM_SENTINEL
); /* msg_tbl is short if this happens! */
1039 (void)fprintf(pipe_end(mip
), " [%s]\n\n", mp
->msg
);
1046 /************************************************************************
1047 * Higher level header decoding interface.
1049 * The core routines are in mime_header.c.
1053 * Decode a portion of the header field.
1055 * linebuf buffer to decode into.
1056 * bufsize size of linebuf.
1057 * hdrline full header line including header name.
1058 * srcstr pointer to string to decode
1061 mime_decode_hfield(char *linebuf
, size_t bufsize
, const char *hdrline
, char *srcstr
)
1063 hfield_decoder_t decode
;
1064 decode
= mime_hfield_decoder(hdrline
);
1066 decode(linebuf
, bufsize
, srcstr
);
1073 * Return the next header field found in the input stream.
1074 * Return 0 if something found, -1 otherwise.
1075 * For a proper header, "*colon" is set to point to the colon
1076 * terminating the header name. Otherwise it is NULL.
1078 * NOTE: unlike gethfield() in support.c this:
1079 * 1) preserves folding (newlines),
1080 * 2) reads until fgetln() gets an EOF,
1081 * 3) only sets *colon if there is a "proper" one.
1084 get_folded_hfield(FILE *f
, char *linebuf
, size_t bufsize
, char **colon
)
1090 if ((cp
= fgetln(f
, &len
)) == NULL
)
1093 cp2
< cp
+ len
&& isprint((unsigned char)*cp2
) &&
1094 !is_WSP(*cp2
) && *cp2
!= ':';
1097 len
= MIN(bufsize
- 1, len
);
1099 (void)memcpy(linebuf
, cp
, len
);
1100 *colon
= *cp2
== ':' ? linebuf
+ (cp2
- cp
) : NULL
;
1101 line
= linebuf
+ len
;
1104 (void)ungetc(c
= getc(f
), f
);
1108 if ((cp
= fgetln(f
, &len
)) == NULL
)
1110 len
= MIN(bufsize
- 1, len
);
1114 (void)memcpy(line
, cp
, len
);
1122 decode_header(FILE *fi
, FILE *fo
, void *cookie __unused
)
1124 char linebuf
[LINESIZE
];
1129 while (get_folded_hfield(fi
, linebuf
, sizeof(linebuf
), &colon
) >= 0) {
1130 char decbuf
[LINESIZE
];
1134 hdrstr
= mime_decode_hfield(decbuf
, sizeof(decbuf
), hdrstr
, hdrstr
);
1135 (void)fprintf(fo
, hdrstr
);
1140 mime_decode_header(struct mime_info
*mip
)
1148 if (mip
->mi_detachdir
) { /* We are detaching. Don't run anything! */
1150 return pipe_end(mip
);
1153 if (mip
->mi_partnum
)
1154 (void)fprintf(fo
, "----- Part %s -----\n", mip
->mi_partstr
);
1156 (void)fflush(fo
); /* Flush so the childern don't see it. */
1159 * install the message hook before the head hook.
1161 cmd
= get_command_hook(mip
, "-hook");
1162 mip
->mi_command_hook
= cmd
;
1164 flags
= mime_run_command(cmd
, pipe_end(mip
));
1165 mip
->mi_head_end
= last_registered_file(0);
1168 cmd
= get_command_hook(mip
, "-head");
1169 mip
->mi_head_end
= last_registered_file(0);
1170 flags
= mime_run_command(cmd
, pipe_end(mip
));
1173 if (value(ENAME_MIME_DECODE_HDR
) && (flags
& CMD_FLAG_NO_DECODE
) == 0)
1174 mime_run_function(decode_header
, pipe_end(mip
), NULL
);
1176 return pipe_end(mip
);
1179 #endif /* MIME_SUPPORT */