1 /* $NetBSD: mime_attach.c,v 1.12 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.
34 #include <sys/cdefs.h>
36 __RCSID("$NetBSD: mime_attach.c,v 1.12 2009/04/10 13:08:25 christos Exp $");
37 #endif /* not __lint__ */
58 #include "mime_codecs.h"
59 #include "mime_child.h"
66 * XXX - This block is for debugging only and eventually should go away.
68 # define SHOW_ALIST(a,b) show_alist(a,b)
70 show_alist(struct attachment
*alist
, struct attachment
*ap
)
72 (void)printf("alist=%p ap=%p\n", alist
, ap
);
73 for (ap
= alist
; ap
; ap
= ap
->a_flink
) {
74 (void)printf("ap=%p ap->a_flink=%p ap->a_blink=%p ap->a_name=%s\n",
75 ap
, ap
->a_flink
, ap
->a_blink
, ap
->a_name
? ap
->a_name
: "<null>");
79 # define SHOW_ALIST(a,b)
83 #ifndef __lint__ /* Don't lint: the public routines may not be used. */
85 * XXX - This block for is debugging only and eventually should go away.
88 show_name(const char *prefix
, struct name
*np
)
93 for (/*EMPTY*/; np
; np
= np
->n_flink
) {
94 (void)printf("%s[%d]: %s\n", prefix
, i
, np
->n_name
);
99 static void fput_mime_content(FILE *fp
, struct Content
*Cp
);
102 show_attach(const char *prefix
, struct attachment
*ap
)
106 for (/*EMPTY*/; ap
; ap
= ap
->a_flink
) {
107 (void)printf("%s[%d]:\n", prefix
, i
);
108 fput_mime_content(stdout
, &ap
->a_Content
);
114 show_header(struct header
*hp
)
116 show_name("TO", hp
->h_to
);
117 (void)printf("SUBJECT: %s\n", hp
->h_subject
);
118 show_name("CC", hp
->h_cc
);
119 show_name("BCC", hp
->h_bcc
);
120 show_name("SMOPTS", hp
->h_smopts
);
121 show_attach("ATTACH", hp
->h_attach
);
123 #endif /* __lint__ */
126 /***************************
127 * boundary string routines
130 getrandstring(size_t length
)
138 /* XXX - check this stuff again!!! */
140 binlen
= 3 * roundup(length
, 4) / 4; /* bytes of binary to encode base64 */
141 bin
= vbin
= salloc(roundup(binlen
, 4));
142 for (i
= 0; i
< roundup(binlen
, 4) / 4; i
++)
143 bin
[i
] = arc4random();
145 b64
= salloc(roundup(length
, 4));
146 mime_bintob64(b64
, vbin
, binlen
);
153 * Generate a boundary for MIME multipart messages.
158 #define BOUND_LEN 70 /* maximum length is 70 characters: RFC2046 sec 5.1.1 */
164 bound
= salloc(BOUND_LEN
);
165 (void)snprintf(bound
, BOUND_LEN
, "=_%08lx.%s",
166 (long)now
, getrandstring(BOUND_LEN
- 12));
172 /***************************
173 * Transfer coding routines
176 * We determine the recommended transfer encoding type for a file as
179 * 1) If there is a NULL byte or a stray CR (not in a CRLF
180 * combination) in the file, play it safe and use base64.
182 * 2) If any high bit is set, use quoted-printable if the content type
183 * is "text" and base64 otherwise.
186 * a) use quoted-printable if there are any long lines, control
187 * chars (including CR), end-of-line blank space, or a missing
189 * b) use 7bit in all remaining case, including an empty file.
191 * NOTE: This means that CRLF text (MSDOS) files will be encoded
195 * RFC 821 imposes the following line length limit:
196 * The maximum total length of a text line including the
197 * <CRLF> is 1000 characters (but not counting the leading
198 * dot duplicated for transparency).
200 #define MIME_UNENCODED_LINE_MAX (1000 - 2)
208 if ((cp
= value(ENAME_MIME_UNENC_LINE_MAX
)) != NULL
)
211 if (limit
< 0 || limit
> MIME_UNENCODED_LINE_MAX
)
212 limit
= MIME_UNENCODED_LINE_MAX
;
214 return (size_t)limit
;
218 is_text(const char *ctype
)
221 strncasecmp(ctype
, "text/", sizeof("text/") - 1) == 0;
225 content_encoding_core(void *fh
, const char *ctype
)
228 int ctrlchar
, endwhite
;
229 size_t curlen
, maxlen
;
236 while ((c
= fgetc(fh
)) != EOF
) {
239 if (c
== '\0' || (lastc
== '\r' && c
!= '\n'))
240 return MIME_TRANSFER_BASE64
;
244 return MIME_TRANSFER_QUOTED
;
246 return MIME_TRANSFER_BASE64
;
255 else if ((c
< 0x20 && c
!= '\t') || c
== 0x7f)
260 if (lastc
== EOF
) /* no characters read */
261 return MIME_TRANSFER_7BIT
;
263 if (lastc
!= '\n' || ctrlchar
|| endwhite
|| maxlen
> line_limit())
264 return MIME_TRANSFER_QUOTED
;
266 return MIME_TRANSFER_7BIT
;
270 content_encoding_by_name(const char *filename
, const char *ctype
)
274 fp
= Fopen(filename
, "r");
276 warn("content_encoding_by_name: %s", filename
);
277 return MIME_TRANSFER_BASE64
; /* safe */
279 enc
= content_encoding_core(fp
, ctype
);
285 content_encoding_by_fileno(int fd
, const char *ctype
)
289 const char *encoding
;
292 cur_pos
= lseek(fd
, (off_t
)0, SEEK_CUR
);
293 if ((fd2
= dup(fd
)) == -1 ||
294 (fp
= Fdopen(fd2
, "r")) == NULL
) {
295 warn("content_encoding_by_fileno");
298 return MIME_TRANSFER_BASE64
;
300 encoding
= content_encoding_core(fp
, ctype
);
302 (void)lseek(fd
, cur_pos
, SEEK_SET
);
307 content_encoding(struct attachment
*ap
, const char *ctype
)
309 switch (ap
->a_type
) {
311 return content_encoding_by_name(ap
->a_name
, ctype
);
315 return content_encoding_by_fileno(ap
->a_fileno
, ctype
);
318 /* This is a coding error! */
319 assert(/* CONSTCOND */ 0);
320 errx(EXIT_FAILURE
, "invalid attachment type: %d", ap
->a_type
);
325 /************************
326 * Content type routines
329 * We use libmagic(3) to get the content type, except in the case of a
330 * 0 or 1 byte file where libmagic gives rather useless results.
333 content_type_by_name(char *filename
)
342 * libmagic(3) produces annoying results on very short files.
343 * The common case is MIME encoding an empty message body.
344 * XXX - it would be better to fix libmagic(3)!
346 * Note: a 1-byte message body always consists of a newline,
347 * so size determines all there. However, 1-byte attachments
348 * (filename != NULL) could be anything, so check those.
350 if ((filename
!= NULL
&& stat(filename
, &sb
) == 0) ||
351 (filename
== NULL
&& fstat(0, &sb
) == 0)) {
352 if (sb
.st_size
< 2 && S_ISREG(sb
.st_mode
)) {
356 if (sb
.st_size
== 0 || filename
== NULL
||
357 (fp
= Fopen(filename
, "r")) == NULL
)
363 return isprint(ch
) || isspace(ch
) ?
364 "text/plain" : "application/octet-stream";
368 magic
= magic_open(MAGIC_MIME
);
370 warnx("magic_open: %s", magic_error(magic
));
373 if (magic_load(magic
, NULL
) != 0) {
374 warnx("magic_load: %s", magic_error(magic
));
377 cp
= magic_file(magic
, filename
);
379 warnx("magic_load: %s", magic_error(magic
));
383 sasprintf(&cp2
, "%s; name=\"%s\"", cp
, basename(filename
)) != -1)
392 content_type_by_fileno(int fd
)
398 cur_pos
= lseek(fd
, (off_t
)0, SEEK_CUR
);
400 ofd
= dup(0); /* save stdin */
401 if (dup2(fd
, 0) == -1) /* become stdin */
404 cp
= content_type_by_name(NULL
);
406 if (dup2(ofd
, 0) == -1) /* restore stdin */
408 (void)close(ofd
); /* close the copy */
410 (void)lseek(fd
, cur_pos
, SEEK_SET
);
415 content_type(struct attachment
*ap
)
417 switch (ap
->a_type
) {
419 return content_type_by_name(ap
->a_name
);
422 * Note: the encapusulated message header must include
423 * at least one of the "Date:", "From:", or "Subject:"
424 * fields. See rfc2046 Sec 5.2.1.
425 * XXX - Should we really test for this?
427 return "message/rfc822";
429 return content_type_by_fileno(ap
->a_fileno
);
432 /* This is a coding error! */
433 assert(/* CONSTCOND */ 0);
434 errx(EXIT_FAILURE
, "invalid attachment type: %d", ap
->a_type
);
439 /*************************
440 * Other content routines
444 content_disposition(struct attachment
*ap
)
446 switch (ap
->a_type
) {
449 (void)sasprintf(&disp
, "attachment; filename=\"%s\"",
450 basename(ap
->a_name
));
460 /* This is a coding error! */
461 assert(/* CONSTCOND */ 0);
462 errx(EXIT_FAILURE
, "invalid attachment type: %d", ap
->a_type
);
469 content_id(struct attachment
*ap __unused
)
471 /* XXX - to be written. */
477 content_description(struct attachment
*attach
, int attach_num
)
481 (void)sasprintf(&description
, "attachment %d", attach_num
);
485 return attach
->a_Content
.C_description
;
488 /*******************************************
489 * Routines to get the MIME content strings.
491 PUBLIC
struct Content
492 get_mime_content(struct attachment
*ap
, int i
)
496 Cp
.C_type
= content_type(ap
);
497 Cp
.C_encoding
= content_encoding(ap
, Cp
.C_type
);
498 Cp
.C_disposition
= content_disposition(ap
);
499 Cp
.C_id
= content_id(ap
);
500 Cp
.C_description
= content_description(ap
, i
);
509 fput_mime_content(FILE *fp
, struct Content
*Cp
)
511 (void)fprintf(fp
, MIME_HDR_TYPE
": %s\n", Cp
->C_type
);
512 (void)fprintf(fp
, MIME_HDR_ENCODING
": %s\n", Cp
->C_encoding
);
513 if (Cp
->C_disposition
)
514 (void)fprintf(fp
, MIME_HDR_DISPOSITION
": %s\n",
517 (void)fprintf(fp
, MIME_HDR_ID
": %s\n", Cp
->C_id
);
518 if (Cp
->C_description
)
519 (void)fprintf(fp
, MIME_HDR_DESCRIPTION
": %s\n",
524 fput_body(FILE *fi
, FILE *fo
, struct Content
*Cp
)
528 enc
= mime_fio_encoder(Cp
->C_encoding
);
530 warnx("unknown transfer encoding type: %s\n", Cp
->C_encoding
);
536 fput_attachment(FILE *fo
, struct attachment
*ap
)
539 struct Content
*Cp
= &ap
->a_Content
;
541 fput_mime_content(fo
, &ap
->a_Content
);
542 (void)putc('\n', fo
);
544 switch (ap
->a_type
) {
546 fi
= Fopen(ap
->a_name
, "r");
548 err(EXIT_FAILURE
, "Fopen: %s", ap
->a_name
);
553 * XXX - we should really dup(2) here, however we are
554 * finished with the attachment, so the Fclose() below
555 * is OK for now. This will be changed in the future.
557 fi
= Fdopen(ap
->a_fileno
, "r");
559 err(EXIT_FAILURE
, "Fdopen: %d", ap
->a_fileno
);
563 char mailtempname
[PATHSIZE
];
566 fi
= NULL
; /* appease gcc */
567 (void)snprintf(mailtempname
, sizeof(mailtempname
),
568 "%s/mail.RsXXXXXXXXXX", tmpdir
);
569 if ((fd
= mkstemp(mailtempname
)) == -1 ||
570 (fi
= Fdopen(fd
, "w+")) == NULL
) {
573 err(EXIT_FAILURE
, "%s", mailtempname
);
575 (void)rm(mailtempname
);
578 * This is only used for forwarding, so use the forwardtab[].
580 * XXX - sendmessage really needs a 'flags' argument
581 * so we don't have to play games.
583 ap
->a_msg
->m_size
--; /* XXX - remove trailing newline */
584 (void)fputc('>', fi
); /* XXX - hide the headerline */
585 if (sendmessage(ap
->a_msg
, fi
, forwardtab
, NULL
, NULL
))
586 (void)fprintf(stderr
, ". . . forward failed, sorry.\n");
594 /* This is a coding error! */
595 assert(/* CONSTCOND */ 0);
596 errx(EXIT_FAILURE
, "invalid attachment type: %d", ap
->a_type
);
599 fput_body(fi
, fo
, Cp
);
603 /***********************************
604 * Higher level attachment routines.
608 mktemp_file(FILE **nfo
, FILE **nfi
, const char *hint
)
610 char tempname
[PATHSIZE
];
612 (void)snprintf(tempname
, sizeof(tempname
), "%s/%sXXXXXXXXXX",
614 if ((fd
= mkstemp(tempname
)) == -1 ||
615 (*nfo
= Fdopen(fd
, "w")) == NULL
) {
618 warn("%s", tempname
);
622 if ((fd2
= dup(fd
)) == -1 ||
623 (*nfi
= Fdopen(fd2
, "r")) == NULL
) {
624 warn("%s", tempname
);
632 * Repackage the mail as a multipart MIME message. This should always
633 * be called whenever there are attachments, but might be called even
634 * if there are none if we want to wrap the message in a MIME package.
637 mime_encode(FILE *fi
, struct header
*header
)
639 struct attachment map
; /* fake structure for the message body */
640 struct attachment
*attach
;
641 struct attachment
*ap
;
644 attach
= header
->h_attach
;
647 * Make new phantom temporary file with read and write file
648 * handles: nfi and nfo, resp.
650 if (mktemp_file(&nfo
, &nfi
, "mail.Rs") != 0)
653 (void)memset(&map
, 0, sizeof(map
));
654 map
.a_type
= ATTACH_FILENO
;
655 map
.a_fileno
= fileno(fi
);
657 map
.a_Content
= get_mime_content(&map
, 0);
660 /* Multi-part message:
661 * Make an attachment structure for the body message
662 * and make that the first element in the attach list.
665 map
.a_flink
= attach
;
666 attach
->a_blink
= &map
;
670 /* Construct our MIME boundary string - used by mime_putheader() */
671 header
->h_mime_boundary
= make_boundary();
673 (void)fprintf(nfo
, "This is a multi-part message in MIME format.\n");
675 for (ap
= attach
; ap
; ap
= ap
->a_flink
) {
676 (void)fprintf(nfo
, "\n--%s\n", header
->h_mime_boundary
);
677 fput_attachment(nfo
, ap
);
680 /* the final boundary with two attached dashes */
681 (void)fprintf(nfo
, "\n--%s--\n", header
->h_mime_boundary
);
684 /* Single-part message (no attachments):
685 * Update header->h_Content (used by mime_putheader()).
686 * Output the body contents.
690 header
->h_Content
= map
.a_Content
;
692 /* check for an encoding override */
693 if ((encoding
= value(ENAME_MIME_ENCODE_MSG
)) && *encoding
)
694 header
->h_Content
.C_encoding
= encoding
;
696 fput_body(fi
, nfo
, &header
->h_Content
);
705 check_filename(char *filename
, char *canon_name
)
709 char *fname
= filename
;
711 /* We need to expand '~' if we got here from '~@'. The shell
712 * does this otherwise.
714 if (fname
[0] == '~' && fname
[1] == '/') {
715 if (homedir
&& homedir
[0] != '~')
716 (void)easprintf(&fname
, "%s/%s",
719 if (realpath(fname
, canon_name
) == NULL
) {
720 warn("realpath: %s", filename
);
724 fd
= open(canon_name
, O_RDONLY
, 0);
726 warnx("open: cannot read %s", filename
);
730 if (fstat(fd
, &sb
) == -1) {
731 warn("stat: %s", canon_name
);
735 if (!S_ISREG(sb
.st_mode
)) {
736 warnx("stat: %s is not a file", filename
);
743 if (fname
!= filename
)
749 static struct attachment
*
750 attach_one_file(struct attachment
*ap
, char *filename
, int attach_num
)
752 char canon_name
[MAXPATHLEN
];
753 struct attachment
*nap
;
756 * 1) check that filename is really a readable file; return NULL if not.
757 * 2) allocate an attachment structure.
758 * 3) save cananonical name for filename, so cd won't screw things later.
759 * 4) add the structure to the end of the chain.
760 * 5) return the new attachment structure.
762 if (check_filename(filename
, canon_name
) == NULL
)
765 nap
= csalloc(1, sizeof(*nap
));
766 nap
->a_type
= ATTACH_FNAME
;
767 nap
->a_name
= savestr(canon_name
);
770 for (/*EMPTY*/; ap
->a_flink
!= NULL
; ap
= ap
->a_flink
)
777 nap
->a_Content
= get_mime_content(nap
, attach_num
);
783 get_line(el_mode_t
*em
, const char *pr
, const char *str
, int i
)
789 * Don't use a '\t' in the format string here as completion
790 * seems to handle it badly.
792 (void)easprintf(&prompt
, "#%-7d %s: ", i
, pr
);
793 line
= my_gets(em
, prompt
, __UNCONST(str
));
795 (void)strip_WSP(line
); /* strip trailing whitespace */
796 line
= skip_WSP(line
); /* skip leading white space */
797 line
= savestr(line
); /* XXX - do we need this? */
800 line
= __UNCONST("");
808 sget_line(el_mode_t
*em
, const char *pr
, const char **str
, int i
)
811 line
= get_line(em
, pr
, *str
, i
);
812 if (line
!= NULL
&& strcmp(line
, *str
) != 0)
817 sget_encoding(const char **str
, const char *filename
, const char *ctype
, int num
)
820 const char *defename
;
825 ename
= get_line(&elm
.mime_enc
, "encoding", ename
, num
);
827 if (*ename
== '\0') {
828 if (defename
== NULL
)
829 defename
= content_encoding_by_name(filename
, ctype
);
832 else if (mime_fio_encoder(ename
) == NULL
) {
834 (void)printf("Sorry: valid encoding modes are: ");
836 ename
= mime_next_encoding_name(&cookie
);
838 (void)printf("%s", ename
);
839 ename
= mime_next_encoding_name(&cookie
);
842 (void)fputc(',', stdout
);
848 if (strcmp(ename
, *str
) != 0)
849 *str
= savestr(ename
);
856 * Edit an attachment list.
857 * Return the new attachment list.
859 static struct attachment
*
860 edit_attachlist(struct attachment
*alist
)
862 struct attachment
*ap
;
866 (void)printf("Attachments:\n");
871 SHOW_ALIST(alist
, ap
);
875 (void)printf("#%-7d message: <not changeable>\n",
880 line
= get_line(&elm
.filec
, "filename", ap
->a_name
, attach_num
);
881 if (*line
== '\0') { /* omit this attachment */
883 struct attachment
*next_ap
;
884 next_ap
= ap
->a_flink
;
886 ap
->a_flink
= next_ap
;
888 next_ap
->a_blink
= ap
;
895 alist
->a_blink
= NULL
;
899 char canon_name
[MAXPATHLEN
];
900 if (strcmp(line
, ap
->a_name
) != 0) { /* new filename */
901 if (check_filename(line
, canon_name
) == NULL
)
903 ap
->a_name
= savestr(canon_name
);
904 ap
->a_Content
= get_mime_content(ap
, 0);
906 sget_line(&elm
.string
, "description",
907 &ap
->a_Content
.C_description
, attach_num
);
908 sget_encoding(&ap
->a_Content
.C_encoding
, ap
->a_name
,
909 ap
->a_Content
.C_type
, attach_num
);
914 /* This is a coding error! */
915 assert(/* CONSTCOND */ 0);
916 errx(EXIT_FAILURE
, "invalid attachment type: %d",
921 if (alist
== NULL
|| ap
->a_flink
== NULL
)
929 struct attachment
*nap
;
931 SHOW_ALIST(alist
, ap
);
933 line
= get_line(&elm
.filec
, "filename", "", attach_num
);
937 nap
= attach_one_file(ap
, line
, attach_num
);
945 sget_line(&elm
.string
, "description",
946 &ap
->a_Content
.C_description
, attach_num
);
947 sget_encoding(&ap
->a_Content
.C_encoding
, ap
->a_name
,
948 ap
->a_Content
.C_type
, attach_num
);
952 SHOW_ALIST(alist
, ap
);
958 * Hook used by the '~@' escape to attach files.
960 PUBLIC
struct attachment
*
961 mime_attach_files(struct attachment
* volatile attach
, char *linebuf
)
963 struct attachment
*ap
;
968 argc
= getrawlist(linebuf
, argv
, (int)__arraycount(argv
));
970 for (ap
= attach
; ap
&& ap
->a_flink
; ap
= ap
->a_flink
)
975 for (i
= 0; i
< argc
; i
++) {
976 struct attachment
*ap2
;
977 ap2
= attach_one_file(ap
, argv
[i
], attach_num
);
987 attach
= edit_attachlist(attach
);
988 (void)printf("--- end attachments ---\n");
995 * Hook called in main() to attach files registered by the '-a' flag.
997 PUBLIC
struct attachment
*
998 mime_attach_optargs(struct name
*optargs
)
1000 struct attachment
*attach
;
1001 struct attachment
*ap
;
1003 char *expand_optargs
;
1006 expand_optargs
= value(ENAME_MIME_ATTACH_LIST
);
1010 for (np
= optargs
; np
; np
= np
->n_flink
) {
1011 char *argv
[MAXARGC
];
1015 if (expand_optargs
!= NULL
)
1016 argc
= getrawlist(np
->n_name
,
1017 argv
, (int)__arraycount(argv
));
1019 if (np
->n_name
== '\0')
1023 argv
[0] = np
->n_name
;
1025 argv
[argc
] = NULL
;/* be consistent with getrawlist() */
1027 for (i
= 0; i
< argc
; i
++) {
1028 struct attachment
*ap2
;
1031 if (argv
[i
][0] == '/') /* an absolute path */
1032 (void)easprintf(&filename
, "%s", argv
[i
]);
1034 (void)easprintf(&filename
, "%s/%s",
1037 ap2
= attach_one_file(ap
, filename
, attach_num
);
1051 * Output MIME header strings as specified in the header structure.
1054 mime_putheader(FILE *fp
, struct header
*header
)
1056 (void)fprintf(fp
, MIME_HDR_VERSION
": " MIME_VERSION
"\n");
1057 if (header
->h_attach
) {
1058 (void)fprintf(fp
, MIME_HDR_TYPE
": multipart/mixed;\n");
1059 (void)fprintf(fp
, "\tboundary=\"%s\"\n", header
->h_mime_boundary
);
1062 fput_mime_content(fp
, &header
->h_Content
);
1066 #endif /* MIME_SUPPORT */