static char rcsid[] = "@(#)$Id: mime_parse.c,v 1.52 2006/05/30 16:33:21 hurtta Exp $";
/******************************************************************************
* The Elm (ME+) Mail System - $Revision: 1.52 $ $State: Exp $
*
* Modified by: Kari Hurtta <hurtta+elm@posti.FMI.FI>
* (was hurtta+elm@ozone.FMI.FI)
*
* Initially written by: Michael Elkins <elkins@aero.org>, 1995
*****************************************************************************/
#include "def_melib.h"
#include "s_me.h"
DEBUG_VAR(Debug,__FILE__,"mime");
void mime_t_clear (mt)
mime_t *mt;
{
DPRINT(Debug,15,(&Debug,
"mime_t_clear(%p) --> BEGIN\n",mt));
if (mt->magic != MIME_magic)
mime_panic(__FILE__,__LINE__,"mime_t_clear",
"Bad magic number");
mt->offset = mt->begin_offset = 0;
mt->length = -1;
mt->encoding = ENCODING_7BIT;
mt->TYPE = give_media_type2(MIME_TYPE_TEXT,"plain",0);
if (!mt->TYPE)
mime_panic(__FILE__,__LINE__,"mime_t_clear",
"text/plain is not known");
mt->disposition = DISP_INLINE;
mt->mime_flags = 0;
if (mt->parser_data)
mime_parser_free(&(mt->parser_data));
if (mt->handler_data)
mime_selector_free(& (mt->handler_data));
if (mt->description)
free_string (&(mt->description));
if (mt->TYPE_opts)
free_mime_param (&(mt->TYPE_opts));
if (mt->DISPOSITION_opts)
free_mime_param (&(mt->DISPOSITION_opts));
if (mt->pathname0) {
if (mt->unlink) {
DPRINT(Debug,15,(&Debug,
"mime_t_clear: %s marked for unlink\n",
mt->pathname0));
if (0 == unlink(mt->pathname0)) {
DPRINT(Debug,14,(&Debug,
"mime_t_clear: %s unlinked\n",
mt->pathname0));
}
}
free (mt->pathname0);
} else {
if (mt->unlink) {
DPRINT(Debug,3,(&Debug,
"mime_t_clear: ERROR: Pathname marked for unlink, but no pathname\n"));
}
}
mt->pathname0 = NULL;
mt->unlink = 0;
if (mt->dispname)
free_string(& (mt->dispname));
DPRINT(Debug,15,(&Debug,
"mime_t_clear(%p) <-- END\n",mt));
return;
}
void mime_get_disposition (str, mt, def_charset, header_error)
char *str;
mime_t *mt;
charset_t def_charset;
struct header_errors **header_error;
{
char *c, tmp[VERY_LONG_STRING];
DPRINT(Debug,9,(&Debug,
"mime_get_disposition(): str=\"%s\"\n", str));
if (mt->magic != MIME_magic)
mime_panic(__FILE__,__LINE__,"mime_get_disposition",
"Bad magic number");
/* Don't harm "str" */
strfcpy (tmp, str, sizeof(tmp));
rfc822_reap_comments (tmp, NULL, 0);
if (mt->DISPOSITION_opts) {
free_mime_param(&(mt->DISPOSITION_opts));
}
/* Look for the options field */
if ((c = strchr (tmp, ';')) != NULL) {
char *d = c;
while (d > tmp && whitespace(*(d-1)))
d--;
*d = '\0';
c++;
while (*c && whitespace(*c))
c++;
mt->DISPOSITION_opts = parse_mime_param("Content-Disposition",
c, def_charset,header_error);
}
else {
char *d = tmp + strlen(tmp);
while (d > tmp && whitespace(*(d-1)))
d--;
*d = '\0';
}
/* All that's left now is the main disposition */
c = tmp;
while (*c && whitespace(*c))
c++;
/* No Content-Disposition -header -> DISP_INLINE (DISP_AUTOATTACH)
* Content-Disposition: inline -> DISP_INLINE
* Content-Disposition: attachment -> DISP_ATTACH
* Content-Disposition: {unknown} -> DISP_ATTACH
* See RFC 1806 (Experimental protocol) for details.
*/
if (istrcmp (c, "inline") != 0)
mt->disposition = DISP_ATTACH;
else
mt->disposition = DISP_INLINE;
DPRINT(Debug,9,(&Debug,
"mime_get_disposition(): disposition=\"%s\" (%d)\n",
DISPOSITION(mt->disposition),
mt->disposition));
}
void mime_get_content (str, mt, def_charset, header_error)
char *str;
mime_t *mt;
charset_t def_charset;
struct header_errors **header_error;
{
char *c, tmp[VERY_LONG_STRING];
char * subtype = "???";
DPRINT(Debug,9,(&Debug,
"mime_get_content(): str=\"%s\"\n", str));
if (mt->magic != MIME_magic)
mime_panic(__FILE__,__LINE__,"mime_get_content",
"Bad magic number");
/* Don't harm "str" */
strfcpy (tmp, str, sizeof(tmp));
rfc822_reap_comments (tmp, NULL, 0);
if (mt->TYPE_opts) {
free_mime_param(&(mt->TYPE_opts));
}
/* Look for the options field */
if ((c = strchr (tmp, ';')) != NULL) {
char *d = c;
while (d > tmp && whitespace(*(d-1)))
d--;
*d = '\0';
c++;
while (*c && whitespace(*c))
c++;
mt->TYPE_opts = parse_mime_param("Content-Type",
c, def_charset,
header_error);
}
else {
char *d = tmp + strlen(tmp);
while (d > tmp && whitespace(*(d-1)))
d--;
*d = '\0';
}
/* Get the subtype */
if ((c = strchr (tmp, '/')) != NULL) {
char *d = c;
while (d > tmp && whitespace(*(d-1)))
d--;
*d = '\0';
c++;
while (*c && whitespace(*c))
c++;
subtype = c;
}
/* All that's left now is the main type */
c = tmp;
while (*c && whitespace(*c))
c++;
mt->TYPE = give_media_type(c,subtype,1);
if (! mt->TYPE) {
DPRINT(Debug,1,(&Debug,
"mime_get_content(): str=\"%s\" FAILED!\n", str));
return;
}
DPRINT(Debug,9,(&Debug,
"mime_get_content(): %p -- type=\"%s\", subtype=\"%s\"\n",
mt->TYPE,
get_major_type_name(mt->TYPE),
get_subtype_name(mt->TYPE)));
return;
}
CONST char* mime_get_boundary (opts)
struct mime_param *opts;
{
CONST char * pv = get_mime_param_compat(opts,"boundary");
if (!pv)
lib_error(CATGETS(elm_msg_cat, MeSet, MeParseNoBoundary,
"'boundary' parameter is missing from Multipart -type!"));
return pv;
}
int mime_get_charset (charset_value, opts, display_charset,
default_content_charset)
charset_t *charset_value;
struct mime_param *opts;
charset_t * display_charset; /* vector */
charset_t default_content_charset;
{
/* Return > 0 (2) if charset is displayable with display_charset
* Return < 0 (-2) if charset needs translating to display_charset
-3 if charset can be translated to display_charset
without loss
*/
int ret = 0,j;
CONST char *pv;
pv = get_mime_param_compat(opts,"charset");
if (!pv) {
if (default_content_charset &&
default_content_charset->MIME_name) {
DPRINT(Debug,9,(&Debug,
"mime_get_charset: Using content default charset %s\n",
default_content_charset->MIME_name));
*charset_value = default_content_charset;
/* Charset name is also required on some confitions later */
pv = default_content_charset->MIME_name;
} else {
pv = "US-ASCII";
/* Default charset if nothing specified */
*charset_value = MIME_name_to_charset(pv,CHARSET_create);
}
} else
*charset_value = MIME_name_to_charset(pv,CHARSET_create);
DPRINT(Debug,9,(&Debug,
" : charset_value=%p '%s' (type=%p)\n",
*charset_value,
(*charset_value)->MIME_name,
(*charset_value)->charset_type));
if (display_charset) {
for (j = 0; display_charset[j]; j++) {
DPRINT(Debug,9,(&Debug,
" [%d] : display_charset=%p '%s' (type=%p)\n",
j,display_charset[j],
display_charset[j]->MIME_name ?
display_charset[j]->MIME_name :
"<no MIME name>",
display_charset[j]->charset_type));
if (*charset_value == display_charset[j] ||
/* Accept also charsets with same name although
charset is redefined
*/
display_charset[j]->MIME_name &&
0 == istrcmp(pv,display_charset[j]->MIME_name)) {
ret = 2; /* If display charset */
break;
} else if (charset_superset_of(display_charset[j],*charset_value)) {
ret = 2; /* if charset is subset of display */
break;
} else if (0 == istrcmp(pv,"US-ASCII") &&
charset_ok_p(display_charset[j])) {
ret = 2; /* If display_charset can show us-ascii? */
break;
}
}
if ( 0 == ret) {
int prop;
if (CS_printable & charset_properties(*charset_value)) {
ret = -2; /* convert (filter?) to display charset */
if (display_charset[0] &&
CS_universal_set & (prop =
charset_properties(display_charset[0]))
) {
DPRINT(Debug,9,(&Debug,
" display charset '%s' -- universal charset (flags=%d)\n",
display_charset[0]->MIME_name ?
display_charset[0]->MIME_name :
"<no MIME name>",
prop));
ret = -3; /* convert to display charset without loss */
}
} else
ret = 0;
}
} else {
DPRINT(Debug,9,(&Debug," --- display_charset vector not given ... \n"));
}
DPRINT(Debug,9,(&Debug,
"mime_get_charset=%d\n",ret));
return ret;
}
void
mime_t_zero (ptr)
mime_t *ptr;
{
/* This routine should be called whenever a new "mime_t" is created. It
* makes sure that the pointers inside are correctly initialized to NULL
* so that we don't end up calling free() on an uninitialized pointer.
*/
DPRINT(Debug,15,(&Debug,
"mime_t_zero(%p)\n",ptr));
ptr->parser_data = NULL;
ptr->handler_data = NULL;
ptr->TYPE_opts = ptr->DISPOSITION_opts = NULL;
ptr->pathname0 = NULL;
ptr->dispname = NULL;
ptr->description = NULL;
ptr->magic = MIME_magic;
ptr->offset = ptr->begin_offset = 0;
ptr->length = -1;
ptr->encoding = ENCODING_7BIT;
ptr->unlink = 0;
ptr->TYPE = give_media_type2(MIME_TYPE_TEXT,"plain",0);
if (!ptr->TYPE)
mime_panic(__FILE__,__LINE__,"mime_t_zero",
"text/plain is not known");
ptr->disposition = DISP_INLINE;
ptr->mime_flags = 0;
}
void mime_t_copy(trg, src)
mime_t *trg, *src;
{
/* This routines make copy of mime_t structure ... */
DPRINT(Debug,15,(&Debug,
"mime_t_copy(%p,%p) --> BEGIN\n",trg,src));
if (trg->magic != MIME_magic)
mime_panic(__FILE__,__LINE__,"mime_t_copy",
"Bad magic number (trg)");
if (src->magic != MIME_magic)
mime_panic(__FILE__,__LINE__,"mime_t_copy",
"Bad magic number (src)");
mime_t_clear(trg);
trg->offset = src->offset;
trg->begin_offset = src->begin_offset;
trg->length = src->length;
trg->encoding = src->encoding;
trg->unlink = 0; /* Don't unlink ! */
trg->TYPE = src->TYPE;
trg->disposition = src->disposition;
trg->mime_flags = src->mime_flags;
trg->parser_data = NULL;
trg->handler_data = NULL;
if (src->parser_data) {
copy_parser_data(& (trg->parser_data), src->parser_data);
} else {
DPRINT(Debug,3,(&Debug,
"mime_t_copy(src=%p,trg=%p): src->parser_data == NULL !\n",trg,src));
}
if (src->description) {
trg->description = dup_string(src->description);
}
if (src->TYPE_opts) {
trg->TYPE_opts = copy_mime_param(src->TYPE_opts);
}
if (src->DISPOSITION_opts) {
trg->DISPOSITION_opts = copy_mime_param(src->DISPOSITION_opts);
}
DPRINT(Debug,15,(&Debug,
"mime_t_copy(%p,%p) <-- END\n",trg,src));
}
mime_t *
mime_t_alloc ()
{
mime_t *ptr;
DPRINT(Debug,15,(&Debug,
"mime_t_alloc() --> BEGIN\n"));
ptr = (mime_t *) safe_malloc (sizeof (mime_t));
/* Make sure to clear the pointers initially so that later we know when
* to reclaim memory in mime_t_clear().
*/
mime_t_zero (ptr);
mime_t_clear (ptr);
DPRINT(Debug,15,(&Debug,
"mime_t_alloc() = %p <-- END\n",ptr));
return ptr;
}
void parse_mime_headers1 (ptr,headers,part_offset,body_offset,opts,
hdr_charset,header_error)
mime_t *ptr;
header_list_ptr headers;
long part_offset;
long body_offset;
int opts;
charset_t hdr_charset;
struct header_errors ** header_error;
{
header_list_ptr this_header;
int no_content_disposition = 0;
int x;
/* set some defaults */
ptr->encoding = ENCODING_7BIT;
if (opts & MIME_DIGEST) {
ptr->TYPE = give_media_type2(MIME_TYPE_MESSAGE,"rfc822",0);
if (!ptr->TYPE)
mime_panic(__FILE__,__LINE__,"parse_mime_headers1",
"message/rfc822 is not known");
} else {
ptr->TYPE = give_media_type2(MIME_TYPE_TEXT,"plain", 0);
if (!ptr->TYPE)
mime_panic(__FILE__,__LINE__,"parse_mime_headers1",
"text/plain is not known");
}
ptr->disposition = DISP_INLINE;
ptr->description = NULL;
ptr->begin_offset = part_offset;
ptr->offset = body_offset;
ptr->length = -1;
if (NULL != (this_header =
locate_header_by_name(headers,"Content-Type")) &&
NULL != this_header->body) {
mime_get_content (this_header->body, ptr, hdr_charset,
header_error);
if (this_header->next_this_header)
process_header_error(header_error,
CATGETS(elm_msg_cat, MeSet,
MeParseErrorContent,
"PARSE ERROR: Several Content-Type headers!"));
} else {
DPRINT(Debug,9,(&Debug,
"parse_mime_headers1: No Content-Type -header\n"));
}
if (NULL != (this_header =
locate_header_by_name(headers,"Content-Disposition")) &&
NULL != this_header->body) {
mime_get_disposition (this_header->body, ptr, hdr_charset,
header_error);
if (this_header->next_this_header)
process_header_error(header_error,
CATGETS(elm_msg_cat, MeSet, MeParseErrorDisposition,
"PARSE ERROR: Several Content-Disposition headers!"));
} else {
DPRINT(Debug,9,(&Debug,
"parse_mime_headers1: No Content-Disposition -header\n"));
no_content_disposition = 1;
}
if (NULL != (this_header =
locate_header_by_name(headers,"Content-Transfer-Encoding")) &&
NULL != this_header->body) {
char * value = this_header->body, *c;
/* This removes comments from buffer this_header->body */
rfc822_reap_comments (value, NULL, 0);
c = value;
while (*c && isspace((unsigned char) *c))
c++;
ptr->encoding = check_encoding (c);
if (this_header->next_this_header)
process_header_error(header_error,
CATGETS(elm_msg_cat, MeSet, MeParseErrorTransfer,
"PARSE ERROR: Several Content-Transfer-Encoding headers!"));
} else {
DPRINT(Debug,9,(&Debug,
"parse_mime_headers1: No Content-Transfer-Encoding -header\n"));
}
if (NULL != (this_header =
locate_header_by_name(headers,"Content-Description")) &&
NULL != this_header->body) {
char * c = this_header->body;
while (*c && whitespace(*c))
c++;
ptr->description = NULL;
ptr->description = hdr_to_string(HDR_TEXT,c,
hdr_charset,
1);
if (this_header->next_this_header)
process_header_error(header_error,
CATGETS(elm_msg_cat, MeSet,
MeParseErrorDescription,
"PARSE ERROR: Several Content-Description headers!"));
} else {
DPRINT(Debug,9,(&Debug,
"parse_mime_headers1: No Content-Description -header\n"));
}
x = mime_classify_media(ptr,
NULL /* ???? */);
if (x) {
ptr->mime_flags = x; /* TODO? -- should this check only NOTPLAIN_need_metamail ??*/
DPRINT(Debug,9,(&Debug,
"parse_mime_headers1: 'Not plain'\n"));
if (no_content_disposition) {
int aa = give_dt_enumerate_as_int(&auto_attachment);
DPRINT(Debug,9,(&Debug,
" : no content-disposition -- auto-attachment = %d\n",
aa));
if (get_major_type_code(ptr->TYPE) == MIME_TYPE_APPLICATION &&
aa >= 1 ||
get_major_type_code(ptr->TYPE) != MIME_TYPE_TEXT &&
!(get_type_flags(ptr->TYPE) & MIME_RFC822) &&
get_major_type_code(ptr->TYPE) != MIME_TYPE_MULTIPART &&
aa >= 2) {
DPRINT(Debug,9,(&Debug,
" : Set disposition = auto attachment\n"));
ptr->disposition = DISP_AUTOATTACH;
DPRINT(Debug,9,(&Debug,
" : was %s/%s\n",
get_major_type_name(ptr->TYPE),
get_subtype_name(ptr->TYPE)));
}
}
}
}
mime_t * parse_mime_headers(headers,part_offset,body_offset,opts,
hdr_charset,header_error)
header_list_ptr headers;
long part_offset;
long body_offset;
int opts;
charset_t hdr_charset;
struct header_errors **header_error;
{
mime_t *ptr;
DPRINT(Debug,9,(&Debug,
"parse_mime_headers(): part_offset=%ld, body_offset=%ld, opts=%d\n",
part_offset,body_offset,opts));
ptr = mime_t_alloc ();
parse_mime_headers1(ptr,headers,part_offset,body_offset,opts,hdr_charset,
header_error);
DPRINT(Debug,10,(&Debug,
"parse_mime_headers- type=%s/%s; flags=%d;\n",
get_major_type_name(ptr->TYPE),
get_subtype_name(ptr->TYPE),
get_type_flags(ptr->TYPE)));
DPRINT(Debug,10,(&Debug,
"parse_mime_headers- begin=%ld, offset=%ld, length=%ld\n",
ptr->begin_offset,ptr->offset,ptr->length));
DPRINT(Debug,9,(&Debug,
"parse_mime_headers=%p <-- END\n",(void *)ptr));
return ptr;
}
mime_t * mime_read_header (fp, opts, defcharset, header_error)
FILE *fp;
int opts;
charset_t defcharset;
struct header_errors **header_error;
{
mime_t *ptr;
header_list_ptr headers = NULL;
long part_offset;
long body_offset;
DPRINT(Debug,19,(&Debug,
"mime_read_header: opts=%d --> START\n",opts));
part_offset = ftell (fp);
headers = file_read_headers(fp,0);
body_offset = ftell(fp);
ptr = parse_mime_headers(headers,part_offset,body_offset,opts,
defcharset,header_error);
delete_headers(&headers);
return ptr;
}
void
mime_warnings(hdr)
struct header_rec *hdr;
{
if (hdr->status & PRE_MIME_CONTENT) {
lib_error(CATGETS(elm_msg_cat, MeSet, MeParsePreMime,
"Error: MIME-message has pre-MIME content-type!"));
}
if (hdr->status & MIME_UNSUPPORTED) {
lib_error(CATGETS(elm_msg_cat, MeSet, MeParseUnsupportedMime,
"Warning: Unsupported MIME-Version!"));
}
}
#if ANSI_C
parse_mime_callback mime_parse_routine;
#endif
int mime_parse_routine (folder,hdr, fp)
struct folder_info *folder;
struct header_rec *hdr;
FILE *fp;
{
/* This routine checks to see if the multipart messages specified by
* "hdr" has been parsed for its subparts, and if not, calls the routine
* to do so.
*/
if (hdr->mime_rec.magic != MIME_magic)
mime_panic(__FILE__,__LINE__,"mime_parse_routine",
"Bad magic number (mime_rec)");
if (hdr->mime_rec.parser_data || !(hdr->status & MIME_MESSAGE)) {
DPRINT(Debug,10,(&Debug,"mime_parse_routine=1 -- already parsed or no mime message\n"));
return 1;
}
/* read_mailcaps() parses mailcaps only once */
read_mailcaps();
/* Copy value */
if (hdr -> content_length >= 0)
hdr->mime_rec.length = hdr -> content_length;
if (hdr->mime_rec.begin_offset <= 0) {
fseek(fp,hdr->offset,SEEK_SET);
hdr->mime_rec.begin_offset = hdr->offset;
/* Skip mailbox's separator lines ... */
DPRINT(Debug,9,(&Debug,
"mime_parse_routine: scanning begin_offset: %ld\n",
(long) hdr->mime_rec.begin_offset));
hdr->mime_rec.begin_offset = skip_envelope(hdr,fp);
DPRINT(Debug,9,(&Debug,
"mime_parse_routine: begin_offset=%ld\n",
(long) hdr->mime_rec.begin_offset));
if (hdr->mime_rec.begin_offset < 0) {
lib_error(CATGETS(elm_msg_cat, MeSet, MeParseMailError,
"Can't parse mail..."));
return 0;
}
}
lib_transient(CATGETS(elm_msg_cat, MeSet, MeParsingMime,
"Parsing MIME structure..."));
if (mime_parser_parse(&(hdr->mime_rec),hdr->header_charset,fp,
&(hdr->header_error))) {
/* mime_classify_media sets hdr->mime_rec.mime_flags !!! */
int old = hdr->mime_rec.mime_flags;
int tmp = mime_classify_media(&(hdr->mime_rec),hdr);
if (tmp >= 0) {
if (tmp != old) {
/* WARNING:
Return value of mime_classify_media do not include
NOTPLAIN_is_fallback, that may be set on hdr->mime_rec.mime_flags
*/
if (! (tmp & NOTPLAIN_need_metamail) &&
! prompt_metamail) {
lib_transient(CATGETS(elm_msg_cat, MeSet, MeParsingMimeNoMetamail,
"Parsing MIME structure... metamail not needed"));
}
}
hdr->mime_parsed = 1;
}
}
return 1;
}
int is_pre_mime_content_type (ptr,content_type)
mime_t *ptr;
char *content_type;
{
int result;
char *cptr = strpbrk(content_type,"/;()");
DPRINT(Debug,10,(&Debug,
"is_pre_mime_content_type(): content_type=%s\n",
content_type));
if (!cptr || ';' == *cptr) {
char *ptr2;
char *tmp = content_type;
while (whitespace(*tmp))
tmp++;
if (cptr)
*cptr = '\0';
ptr2 = strpbrk(tmp," \t");
if (ptr2)
*ptr2 = '\0';
if (istrcmp(tmp,"text")!=0) {
char buf[STRING];
ptr -> mime_flags = NOTPLAIN_need_metamail; /* !!! */
/* Put some 'intelligent' value */
elm_sfprintf(buf,sizeof buf,
FRM("X-RFC1049-%.30s"),tmp);
ptr->TYPE = give_media_type2(MIME_TYPE_APPLICATION,
buf,1);
} else {
ptr -> mime_flags = 0;
ptr -> TYPE = give_media_type2(MIME_TYPE_TEXT,"plain",0);
if (!ptr->TYPE)
mime_panic(__FILE__,__LINE__,"is_pre_mime_content_type",
"text/plain is not known");
}
result = 1;
} else
result = 0;
DPRINT(Debug,10,(&Debug,
"is_pre_mime_content_type=%d\n",result));
return result;
}
/*
* Local Variables:
* mode:c
* c-basic-offset:4
* buffer-file-coding-system: iso-8859-1
* End:
*/
syntax highlighted by Code2HTML, v. 0.9.1