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 <hurtta+elm@posti.FMI.FI> (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 :
			 "<no MIME name>"));
    }
    DPRINT(Debug,11,(&Debug, 
		     ", header charset='%s'\n",
		     defcharset->MIME_name ?
		     defcharset->MIME_name :
		     "<no 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:
 */


syntax highlighted by Code2HTML, v. 0.9.1