static char rcsid[] = "@(#)$Id: mime_selector.c,v 1.10 2006/04/13 16:35:40 hurtta Exp $"; /****************************************************************************** * The Elm (ME+) Mail System - $Revision: 1.10 $ $State: Exp $ * * Author: Kari Hurtta (was hurtta+elm@ozone.FMI.FI) *****************************************************************************/ #include "def_melib.h" #include "s_me.h" DEBUG_VAR(Debug,__FILE__,"mime"); void mime_selector_free (p) struct mime_selected_handler **p; { if (MIME_selector_magic != (*p)->magic) panic("MAILCAP PANIC",__FILE__,__LINE__,"mime_selector_free", "Bad magic number",0); (*p)->handler = NULL; (*p)->entry = NULL; (*p)->use_entry = 0; (*p)->EC_decoder = null_EC_decoder; (*p)->SG_decoder = null_SG_decoder; (*p)->selected_alternative = NULL; (*p)->magic = 0; /* Invalidate */ free(*p); *p = NULL; } /* old mime_notplain() reinvented here returns mask: NOTPLAIN_need_metamail 0x01 NOTPLAIN_need_mailcap 0x02 NOTPLAIN_canuse_mailcap 0x04 */ #if DEBUG static char * debug_f P_((int c)); static char * debug_f(c) int c; { static char buffer[80]; if (!c) return "none"; buffer[0] = '\0'; buffer[2] = '\0'; /* In case there is unknown bits set */ if (c & NOTPLAIN_need_metamail) strfcat(buffer,", NOTPLAIN_need_metamail",sizeof buffer); if (c & NOTPLAIN_need_mailcap) strfcat(buffer,", NOTPLAIN_need_mailcap",sizeof buffer); if (c & NOTPLAIN_canuse_mailcap) strfcat(buffer,", NOTPLAIN_canuse_mailcap",sizeof buffer); if (c & NOTPLAIN_is_fallback) strfcat(buffer,", NOTPLAIN_is_fallback",sizeof buffer); return buffer +2; } #endif int mime_classify_media(p,hdr) mime_t *p; struct header_rec * hdr; { int Flags; struct media_type_handle * H = NULL; int is_default = 0; int is_fallback = 0; int submask = 0; if (p->magic != MIME_magic) panic("MIME PANIC",__FILE__,__LINE__,"mime_classify_media", "Bad magic number",0); if (! p->TYPE) { DPRINT(Debug,1,(&Debug, "mime_classify_media(%p) NOT PARSED\n", p)); submask = NOTPLAIN_need_metamail; goto bad_encoding; } melib_register_decoders(); DPRINT(Debug,11,(&Debug, "mime_classify_media(%p) << type: %p=%s/%s\n", p, p->TYPE, get_major_type_name(p->TYPE), get_subtype_name(p->TYPE))); Flags = get_type_flags(p->TYPE); if (Flags) { DPRINT(Debug,11,(&Debug, " Flags: %s%s%s%s%s%s\n", (Flags & MIME_RFC822) ? " MIME_RFC822" : "", (Flags & MIME_MIXED) ? " MIME_MIXED" : "", (Flags & MIME_DIGEST) ? " MIME_DIGEST" : "", (Flags & MIME_ALTERNATIVE) ? " MIME_ALTERNATIVE" : "", (Flags & MIME_SIGNED) ? " MIME_SIGNED" : "", (Flags & MIME_ENCRYPTED) ? " MIME_ENCRYPTED" : "" )); } if (p->encoding < ENCODING_NONE || p->encoding >= ENCODING_EXPERIMENTAL) { DPRINT(Debug,11,(&Debug, "mime_classify_media(%p) -- type: %p=%s/%s; encoding=%s (%d)\n", p, p->TYPE, get_major_type_name(p->TYPE), get_subtype_name(p->TYPE), ENCODING(p->encoding),p->encoding)); submask |= NOTPLAIN_need_metamail; goto bad_encoding; } if (p->handler_data) { if (MIME_selector_magic != p->handler_data->magic) panic("MAILCAP PANIC",__FILE__,__LINE__,"mime_classify_media", "Bad magic number",0); } else { p->handler_data = safe_malloc(sizeof (* p->handler_data)); p->handler_data->magic = MIME_selector_magic; } p->handler_data->handler = NULL; /* Not selected yet */ p->handler_data->entry = NULL; p->handler_data->use_entry = 0; p->handler_data->EC_decoder = null_EC_decoder; p->handler_data->SG_decoder = null_SG_decoder; p->handler_data->selected_alternative = NULL; /* RESET loop variables */ H = NULL; is_default = 0; while(walk_mt_handler(p->TYPE,&H,&is_default,handle_pager)) { int r; if (H->type != handle_pager) { mime_panic(__FILE__,__LINE__,"mime_classify_media", "Unexpected handler type"); continue; /* not reached */ } DPRINT(Debug,11,(&Debug, "mime_classify_media [%s/%s] func = %p, selector(pager) = %p (media_type_handle %p) default:%d\n", get_major_type_name(p->TYPE), get_subtype_name(p->TYPE), H->p.pager->func, H->p.pager, H, is_default)); r = H->p.pager->selector(p,hdr); if (r >= 0) { p->handler_data->handler = H->p.pager; is_fallback = r & NOTPLAIN_is_fallback; submask |= (r & ~NOTPLAIN_is_fallback); DPRINT(Debug,11,(&Debug, "mime_classify_media: internal pager found for %s/%s, r=%d (%s)\n", get_major_type_name(p->TYPE), get_subtype_name(p->TYPE), r, debug_f(r))); if (is_fallback) { DPRINT(Debug,11,(&Debug, "mime_classify_media: ... but continuing search\n", get_major_type_name(p->TYPE), get_subtype_name(p->TYPE))); } else goto found; } } /* Check mailcap */ /* RFC 1343 is not completely clear, but it is assumed that "test" entry does _not_ need mail data ,,, therefore we do NOT run arrange_decoded() on here .... */ /* RESET loop variables */ H = NULL; is_default = 0; while (walk_mt_handler(p->TYPE,&H,&is_default,handle_mailcap_entry)) { struct mailcap_entry *f; if (H->type != handle_mailcap_entry) panic("MIME PARSER PANIC",__FILE__,__LINE__, "mailcap_add_entries", "Bad handler type",0); DPRINT(Debug,11,(&Debug, "mime_classify_media [%s/%s] mailcap = %p (media_type_handle %p) default:%d\n", get_major_type_name(p->TYPE), get_subtype_name(p->TYPE), H->p.mailcap, H, is_default)); f = H->p.mailcap; if (mailcap_is_valid_view(f,p)) { submask |= NOTPLAIN_need_mailcap; p->handler_data->entry = f; DPRINT(Debug,11,(&Debug, "mime_classify_media: mailcap pager found for %s/%s\n", get_major_type_name(p->TYPE), get_subtype_name(p->TYPE))); goto found; } } /* Not found */ submask |= NOTPLAIN_need_metamail; found: if ((submask | is_fallback) != p->mime_flags && submask >= 0) { int a = (submask | is_fallback); DPRINT(Debug,11,(&Debug, "mime_classify_media: changing mime_flags from %d (%s) ", p->mime_flags,debug_f(p->mime_flags))); /* Do not call debug_f() twise on same output, return is statically alloced! */ DPRINT(Debug,11,(&Debug, "to %d (%s) for %s/%s\n", a,debug_f(a), get_major_type_name(p->TYPE), get_subtype_name(p->TYPE))); p->mime_flags = a; if (p->mime_flags != a) mime_panic(__FILE__,__LINE__,"mime_classify_media", "mime_flags too narrow?"); } bad_encoding: DPRINT(Debug,11,(&Debug, "mime_classify_media(%p) = %d (%s)\n", p, submask, debug_f(submask))); return submask; } /* mime_classify_subparts replaces can_handle() returns mask: NOTPLAIN_need_metamail 0x01 NOTPLAIN_need_mailcap 0x02 NOTPLAIN_canuse_mailcap 0x04 */ int mime_classify_subparts(ptr,hdr) mime_t *ptr; struct header_rec * hdr; { int i; int count; int submask = 0; if (!ptr->parser_data) { submask = NOTPLAIN_need_metamail; goto out; } count = mime_parser_subparts(ptr->parser_data); for (i = 0; i < count; i++) { mime_t *att = mime_parser_index(ptr->parser_data,i); if (att->magic != MIME_magic) mime_panic(__FILE__,__LINE__,"mime_classify_subparts", "Bad magic number (subpart)"); if (att->disposition == DISP_INLINE) { int f = mime_classify_media(att, hdr); if (f < 0) { DPRINT(Debug,9,(&Debug, "mime_classify_subparts -- Failed: %p=%s/%s (f=%d)\n", att->TYPE, get_major_type_name(att->TYPE), get_subtype_name(att->TYPE), f)); submask = -1; goto out; } DPRINT(Debug,9,(&Debug, "mime_classify_subparts: f=%d (%s): %p=%s/%s\n", f, debug_f(f), att->TYPE, get_major_type_name(att->TYPE), get_subtype_name(att->TYPE))); submask |= f; } else { DPRINT(Debug,9,(&Debug, "mime_classify_subparts: Attachment: %p=%s/%s\n", att->TYPE, get_major_type_name(att->TYPE), get_subtype_name(att->TYPE))); } } out: DPRINT(Debug,11,(&Debug, "mime_classify_subparts(%p) = %d (%s)\n", ptr, submask,debug_f(submask))); return submask; } /* mime_classify_best_alternative replaces best_alternative() returns mask: NOTPLAIN_need_metamail 0x01 NOTPLAIN_need_mailcap 0x02 NOTPLAIN_canuse_mailcap 0x04 */ static int mask_score P_((int x)); static int mask_score(x) int x; { int r = 0; if (0 == x) r = 10; else { r = 4; if (x & NOTPLAIN_need_metamail) r -=4; else if (x & NOTPLAIN_is_fallback) r -=3; else if (x & NOTPLAIN_need_mailcap) r -=2; else if (x & NOTPLAIN_canuse_mailcap) r -=1; } DPRINT(Debug,11,(&Debug," Mask %d (%s) score = %d\n", x,debug_f(x),r)); return r; } int mime_classify_best_alternative(ptr, ret,hdr) mime_t *ptr; mime_t **ret; struct header_rec * hdr; { mime_t * r = NULL; int i; int count; int submask = 0; int score = 0; if (!ptr->parser_data) { submask = NOTPLAIN_need_metamail; goto out; } count = mime_parser_subparts(ptr->parser_data); if (count < 1) { submask = NOTPLAIN_need_metamail; goto out; } r = mime_parser_index(ptr->parser_data,0); submask = NOTPLAIN_need_metamail; /* DEFAULT -- recalculated later */ for (i = 0; i < count; i++) { mime_t * att = mime_parser_index(ptr->parser_data,i); int f; int s; if (att->magic != MIME_magic) mime_panic(__FILE__,__LINE__,"mime_classify_best_alternative", "Bad magic number"); /* We will ignore here Content-Disposition: -header, because it does not make sense inside on multipart/alternative */ f = mime_classify_media(att,hdr); if (f < 0) { DPRINT(Debug,11,(&Debug, "mime_classify_best_alternative -- Failed: %p=%s/%s (f=%d)\n", att->TYPE, get_major_type_name(att->TYPE), get_subtype_name(att->TYPE), f)); continue; } s = mask_score(f); if ( s > score) { r = att; submask = f; score = s; DPRINT(Debug,11,(&Debug, "-- mime_classify_best_alternative, found: %p=%s/%s [%d] flags=%d (%s), score=%d\n", att->TYPE, get_major_type_name(att->TYPE), get_subtype_name(att->TYPE), i,submask,debug_f(submask), score )); } } out: DPRINT(Debug,11,(&Debug, "mime_classify_best_alternative(%p) = %d (%s)", ptr, submask, debug_f(submask))); if (r) { DPRINT(Debug,11,(&Debug, ", found=%s/%s", get_major_type_name(r->TYPE), get_subtype_name(r->TYPE))); } DPRINT(Debug,11,(&Debug, "\n")); if (ret) *ret = r; return submask; } void mime_decode (ptr, state_in, state_out, defcharset, mss, badtype) mime_t *ptr; in_state_t *state_in; out_state_t *state_out; charset_t defcharset; struct header_rec *mss; type_mismatch_prompt *badtype; { int j; /* This routine calls the appropriate routine to decode the data * described in "ptr". */ DPRINT(Debug,11,(&Debug, "mime_decode -> state: offset=%ld, length=%ld, type=%s/%s, mime_flags=%d (%s) -- ", (long) ptr -> offset, (long) ptr -> length, get_major_type_name(ptr->TYPE), get_subtype_name(ptr->TYPE), ptr->mime_flags, debug_f(ptr->mime_flags))); for (j = 0; state_out->display_charset[j]; j++) { DPRINT(Debug,11,(&Debug, "'display charset[%d]'=%s ",j, state_out->display_charset[j]->MIME_name ? state_out->display_charset[j]->MIME_name : "")); } DPRINT(Debug,11,(&Debug, ", header charset='%s'\n", defcharset->MIME_name ? defcharset->MIME_name : "")); if (ptr->magic != MIME_magic) mime_panic(__FILE__,__LINE__,"mime_decode", "Bad magic number"); if (!in_state_seekable(state_in)) { mime_panic(__FILE__,__LINE__,"mime_decode", "mime_decode: unsupported input state"); } if (ptr->disposition == DISP_INLINE) { if (!ptr->handler_data) { state_puts("[ ",state_out); state_printf(state_out, CATGETS(elm_msg_cat, MeSet, MeDecodeNoHandler, "mime_decode: no handler selected")); state_nlputs(" ]\n",state_out); lib_error(CATGETS(elm_msg_cat, MeSet, MeDecodeNoHandler, "mime_decode: no handler selected")); return; } if (MIME_selector_magic != ptr->handler_data->magic) mime_panic(__FILE__,__LINE__,"mime_decode", "Bad magic number (handler_data)"); if (in_state_fseek(state_in,ptr->offset) != 0) { /* state_nlputs or state_printf is needed for EOLN_is_CRLF conversions */ state_puts("[ ",state_out); state_printf(state_out, CATGETS(elm_msg_cat, MeSet, MeDecodeMimeSeekFailed, "mime_decode: seek failed")); state_nlputs(" ]\n",state_out); lib_error(CATGETS(elm_msg_cat, MeSet, MeDecodeMimeSeekFailed, "mime_decode: seek failed")); return; } /* Use handler if it is not just fallback routine */ if (ptr->handler_data->handler && 0 == (ptr->mime_flags & NOTPLAIN_is_fallback)) { DPRINT(Debug,11,(&Debug, "mime_decode: using builtin handler \n")); ptr->handler_data->handler->func (ptr, state_in, state_out, defcharset, mss, badtype); } else if (ptr->handler_data->entry) { if (ptr->handler_data->use_entry) { char * file_name = NULL; FILE * file_handle = NULL; in_state_t newstate2; in_state_clear(&newstate2,STATE_in_file); DPRINT(Debug,11,(&Debug, "mime_decode: using mailcap handler \n")); if (NULL != (file_handle = arrange_decoded(ptr,state_in,state_out,&newstate2, &file_name))) { int x; if (check_type_pattern) { if (!check_type_magic(ptr,&newstate2, state_out, badtype)) { DPRINT(Debug,11,(&Debug, "mime_decode: mime_type_rejected\n")); goto FAILTYPE; } } x = run_mailcap_view(file_name,file_handle,state_out,ptr->handler_data->entry,ptr); if (!x) { DPRINT(Debug,11,(&Debug, "mime_decode: ... mailcap handler failed\n")); } FAILTYPE: unlink(file_name); free(file_name); } else { DPRINT(Debug,11,(&Debug, "mime_decode: ... decondig failed for mailcap handler\n")); } in_state_destroy(&newstate2); } else { DPRINT(Debug,11,(&Debug, "mime_decode: ... mailcap handler skipped\n")); goto is_null; } } else { is_null: if (ptr->handler_data->handler) { DPRINT(Debug,11,(&Debug, "mime_decode: using builtin (fallback) handler \n")); ptr->handler_data->handler->func (ptr, state_in, state_out, defcharset, mss, badtype); } else { DPRINT(Debug,11,(&Debug, "mime_decode: using builtin null handler \n")); null_decode(ptr, state_in, state_out, defcharset, mss); } } } else { if (ptr->disposition == DISP_AUTOATTACH) { state_printf(state_out, CATGETS(elm_msg_cat, MeSet, MeDecodeUnsupportedAttach, "[ Type %.15s/%.30s treated as attachment, skipping... ]\n"), get_major_type_name(ptr->TYPE), get_subtype_name(ptr->TYPE)); if (state_out->displaying) state_printf(state_out, CATGETS(elm_msg_cat, MeSet, MeDecodeUseVtosave, "[ Use 'v' to view or save this part. ]\n")); } else { if (state_out->displaying) state_printf(state_out, CATGETS(elm_msg_cat, MeSet, MeDecodeAttachUseV, "[ Attachment, skipping... Use 'v' to view this part. ]\n")); else state_printf(state_out, CATGETS(elm_msg_cat, MeSet, MeDecodeAttachSkipping, "[ Attachment, skipping... ]\n")); } } DPRINT(Debug,11,(&Debug, "mime_decode <- END\n")); } /* * Local Variables: * mode:c * c-basic-offset:4 * buffer-file-coding-system: iso-8859-1 * End: */