static char rcsid[] = "@(#)$Id: mime_decode.c,v 1.77.36.1 2007/10/28 08:15:04 hurtta Exp $";

/******************************************************************************
 *  The Elm (ME+) Mail System  -  $Revision: 1.77.36.1 $   $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");

#if ANSI_C
#define S_(x) static x;
#else
#define S_(x)
#endif

/* Prototype */
static void Xbit_decode	P_((in_state_t *, out_state_t *, int, int)); 

static void Xbit_decode (s_in, s_out, length, is_text)
     in_state_t *s_in;
     out_state_t *s_out;
     int length;
     int is_text;
{
    /* state_getl adds \n to end of file so do not use it .... */


    int	bytes = 0; /* the total number of bytes read so far */
    int store_ch = 0; /* CRLF conversion */
    int addprefix = 1;

    DPRINT(Debug,10,(&Debug, 
		     "Xbit_decode: length=%d, prefix=%s\n",
		     length,
		     s_out->prefix ? s_out->prefix : "<NULL>"));
   
    while (1) {
	int ch;
    
	if (bytes >= length)
	    break;
       
	ch = state_getc(s_in);

	if (EOF == ch) {
	    DPRINT(Debug,10,(&Debug, 
			     "Xbit_decode: EOF  bytes=%d\n",
			     bytes));
	    break;
	}
	bytes++;

	if (addprefix) {
	    state_add_prefix(s_out);
	    addprefix = 0;
	}


	if (store_ch && ch != '\n') 
	    state_putc(store_ch,s_out);

	store_ch = 0;
	
	if (ch == '\r')
	    store_ch = ch;
	else {
	    if (ch == '\n' && s_out->EOLN_is_CRLF)
		state_putc('\r',s_out);

	    state_putc(ch,s_out);
	    if (ch == '\n')
		addprefix = 1;
	}      
    }

    /* Make sure to flush anything left in the internal buffer. */
    if (store_ch)  /* for astext */
	state_putc(store_ch,s_out);


}

void base64_decode (s_in, s_out, length, astext)
     in_state_t  *s_in;
     out_state_t *s_out;
     int length;
     int astext;
{
    /* Partially based on base64 decode on kehlist
     * (first occured on kehlist 1.1.2   9 Jun 1999
     *  Author: Kari E. Hurtta <Kari.Hurtta@Fmi.FI>)
     *
     * Well, basically same decoding routine is on
     * cs_add_streambyte_to_s_utf7() on lib/cs_utf.c
     *
     * And also found from kehpager (UTF7_convert()
     * on charset.c,  kehpager 1.2   1994
     * Author: Kari Hurtta <Kari.hurtta@helsinki.fi> )
     *
     */
  
    int	bytes = 0; /* the total number of bytes read so far */

    unsigned char ch,
	store_ch = 0; /* for astext -- CRLF conversion */
    int corrupted = FALSE;

    int res = 0;    /* Result of decoding    */
    int bit = 0;    /* Number of bits on res */

    int addprefix = 1;  

    DPRINT(Debug,10,(&Debug, 
		     "base64_decode: length=%d, prefix=%s, astext=%d\n",
		     length,
		     s_out->prefix ? s_out->prefix : "<NULL>",
		     astext));


    while (1) {
	int c;
	int code;

	if (bytes >= length)
	    break;

	c = state_getc(s_in);

	if (EOF == c) {
	    DPRINT(Debug,10,(&Debug, 
			     "base64_decode: EOF  bytes=%d\n",
			     bytes));
	    break;
	}
	bytes++;

	if (addprefix) {
	    state_add_prefix(s_out);
	    addprefix = 0;
	}

	if (' ' == c || '\t' == c || '\r' == c || '\n' == c)
	    continue;

	if (c == '=') {
	    DPRINT(Debug,10,(&Debug, 
			     "base64_decode: '=' seen  bytes=%d\n",
			     bytes));
	    break;
	}


	code = base64(c);
	if (-1 == code) {
	    DPRINT(Debug,10,(&Debug, 
			     "base64_decode: char=%02x bad  bytes=%d\n",
			     c,bytes));
	    corrupted = TRUE;  /* Perhaps corrupted */

	    continue;   /* Bad characters are supposed to be skipped */
	}

	/* store code value to res */
	res <<= 6;
	res  |= code;
	bit  += 6;

	/* Look if there is enough bits on res for character */
	if (bit >= 8) {
	    /* extract characer value from res */
	    int ch = res >> (bit-8);
	    res   -= ch  << (bit-8);
	    bit -= 8;

	    if (store_ch && ch != '\n') /* for astext */
		state_putc(store_ch,s_out);

	    store_ch = 0;

	    if (astext && ch == '\r')
		store_ch = ch;
	    else {
		if (ch == '\n' && astext && s_out->EOLN_is_CRLF)
		    state_putc('\r',s_out);

		state_putc(ch,s_out);
		if (ch == '\n')
		    addprefix = 1;
	    }      
    
	}

    }

    /* Make sure to flush anything left in the internal buffer. */
    if (store_ch)  /* for astext */
	state_putc(store_ch,s_out);
    
    if (corrupted) {
	if (s_out->displaying) {
	    /* state_nlputs or state_printf is needed for EOLN_is_CRLF
	       conversions */
	    state_nlputs("\n[ ",s_out);
	    state_printf(s_out,CATGETS(elm_msg_cat, MeSet, MeDecodeBASE64Corrupt,
				       "BASE64 data was corrupt!"));
	    state_nlputs(" ]\n",s_out);
	}
	lib_error (CATGETS(elm_msg_cat, MeSet, MeDecodeBASE64Corrupt,
			   "BASE64 data was corrupt!"));
    }
    
    DPRINT(Debug,10,(&Debug, 
		     "base64_decode: read=%d bytes, corrupted=%d.\n",
		     bytes,corrupted));   

    return;
}

void uudecode (s_in, s_out, length, astext)
     in_state_t  *s_in;
     out_state_t *s_out;
     int length;
     int astext;
{
    char buffer[256];  /* uuencoded lines should be under 80 characters */

    int corrupted = FALSE;

    /* Mostly based on base64_decode() */

    int	bytes = 0; /* the total number of bytes read so far */
    int len;
    char store_ch = 0; /* for astext */
    int addprefix = 1;
    
    DPRINT(Debug,10,(&Debug, 
		     "uudecode: length=%d, prefix=%s, astext=%d\n",
		     length,
		     s_out->prefix ? s_out->prefix : "<NULL>",
		     astext));

    do {
	len = state_getl (buffer, sizeof buffer, s_in);

	if (len > 0)
	    bytes += len;	

	if (bytes >= length)
	    break;

    } while (len == 1 && 0 == strcmp(buffer,"\n") ||
	     len == 2 && 0 == strcmp(buffer,"\r\n"));
	    
    if (len < 6 || strncmp(buffer,"begin ",6) != 0) {
	DPRINT(Debug,10,(&Debug, 
			 "uudecode: 'begin' was expected\n"));
	corrupted = TRUE;
	goto fail;
    }

#define UUDECODE(x)  (((x) - ' ') & 077)

    /*  Both space ' ' and '`'  character need to be treated
     *  as 0 ... therefore & 077 is needed
     */


    while (bytes < length) {
	int res = 0;    /* Result of decoding    */
	int bit = 0;    /* Number of bits on res */

	int expected;   /* Expected number of decoded bytes on line */
	int coded = 0;
	int i;

	len = state_getl (buffer, sizeof buffer, s_in);

	if (len > 0)
	    bytes += len;	

	/* 'Remove' newline */
	if (len > 0 && '\n' == buffer[len-1]) {
	    len--;

	    if (len > 0 && '\r' == buffer[len-1])
		len--;
	} else {
	    DPRINT(Debug,10,(&Debug, 
			     "uudecode: newline expected -- too long line?\n"));
	    corrupted = TRUE;
	}


	if (len < 1) {
	    DPRINT(Debug,10,(&Debug, 
			     "uudecode: empty line!\n"));
	    corrupted = TRUE;
	    
	    /* short lines are padded with space and 
	       and space inficates value 0
	    */
	    expected  = 0;
	} else      
	    expected  =  UUDECODE(buffer[0]);

	/* If last line before 'end' ? */
	if (!expected)
	    break;

	if (addprefix) {
	    state_add_prefix(s_out);
	    addprefix = 0;
	}

	for (i = 1; coded < expected; i++) {
	    /* If line is short, it is badded with space
	     * which means value 0
	     */

	    int code = i < len ? UUDECODE(buffer[i]) : 0;

	    if (i >= len || 
		buffer[i] < ' ' ||
		(buffer[i] & 128)) {
		/*Ponentially corrupted */

		corrupted = TRUE;
	    }

	    /* store code value to res */
	    res <<= 6;
	    res  |= code;
	    bit  += 6;
	    
	    /* Look if there is enough bits on res for character */
	    if (bit >= 8) {
		/* extract characer value from res */
		int ch = res >> (bit-8);
		res   -= ch  << (bit-8);
		bit -= 8;
		
		/* count number of bytes decoded from line */
		coded++;

		if (store_ch && ch != '\n') /* for astext */
		    state_putc(store_ch,s_out);
		
		store_ch = 0;
		
		if (astext && ch == '\r')
		    store_ch = ch;
		else {
		    if (ch == '\n' && astext && s_out->EOLN_is_CRLF)
			state_putc('\r',s_out);
		    
		    state_putc(ch,s_out);
		    if (ch == '\n')
			addprefix = 1;
		}      
	    }
	    
	}
	
    }

    /* Make sure to flush anything left in the internal buffer. */
    if (store_ch)  /* for astext */
	state_putc(store_ch,s_out);

    len = state_getl (buffer, sizeof buffer, s_in);

    if (len > 0)
	bytes += len;	

    if (len < 3 || strncmp(buffer,"end",3) != 0) {
	DPRINT(Debug,10,(&Debug, 
			 "uudecode: 'end' was expected\n"));
	corrupted = TRUE;	
    }

    if (bytes > length) {
	DPRINT(Debug,10,(&Debug, 
			 "uudecode: Too many bytes readed (needed %d got %d)\n",
			 length,bytes));
	corrupted = TRUE;	
    }

 fail:
    if (corrupted) {
	if (s_out->displaying) {
	    /* state_nlputs or state_printf is needed for EOLN_is_CRLF
	       conversions */
	    state_nlputs("\n[ ",s_out);
	    state_printf(s_out,CATGETS(elm_msg_cat, MeSet, MeUUDECODECorrupt,
				       "UUENCODED data was corrupt!"));
	    state_nlputs(" ]\n",s_out);
	}
	lib_error (CATGETS(elm_msg_cat, MeSet, MeUUDECODECorrupt,
			   "UUENCODED data was corrupt!"));
    }
    
    DPRINT(Debug,10,(&Debug, 
		     "uudecode: read=%d bytes, corrupted=%d.\n",
		     bytes,corrupted));   

    return;
}

static unsigned char * us_str P_((char *str));
static unsigned char * us_str(str)
     char *str;
{
    return (unsigned char *)str;
}
  
void quoted_printable_decode (s_in, s_out, length, astext)
     in_state_t  *s_in;
     out_state_t *s_out;
     int length;
     int astext;
{
    int bytes = 0; /* number of bytes read */
    int nl = TRUE; /* flag to indicate hitting a new line */
    unsigned char *p;
    int c1, c2;
    unsigned char ch, store_ch = 0;
    char buf[VERY_LONG_STRING];
    int corrupted = 0;

    DPRINT(Debug,10,(&Debug, 			
		     "quoted_printable_decode: length=%d, prefix=%s, astext=%d\n",
		     length,
		     s_out->prefix ? s_out->prefix : "<NULL>",
		     astext));
    
    for (;;) {
	int len;
	
	if (bytes >= length)
	    break;

	if ((len=state_getl (buf, sizeof buf, s_in)) <= 0)
	    break;
	bytes += len;

	p = us_str(buf);
	while (*p) {

	    /* If there is a prefix and this is the beginning of a new 
	     *  line... 
	     */
	    if (nl) {
		state_add_prefix(s_out);
		nl = FALSE;
	    }

	    if (store_ch && *p != '\n') /* for astext */
		state_putc(store_ch,s_out);
	    store_ch = 0;
      
	    if (*p == '=') {
		p++;
		/* Ignore spaces in end of line   -- see MIME */
		if (*p == '\r' || *p == ' ' || *p == '\t') {
		    unsigned char *t = p;
		    while (*t && (*t == '\r' || *t == ' ' || *t == '\t'))
			t++;
		    if (*t && *t == '\n')
			p = t;
		}

		if (*p == '\n') { /* soft linebreak */
		    if (p >= us_str(buf) +len)
			break;
		    p++;
		} else {
		    c1 = hex(*p);
		    if (c1 == -1)
			corrupted = TRUE;
		    p++;
		    c2 = hex(*p);
		    if (c2 == -1)
			corrupted = TRUE;
		    p++;
		    ch = (c1 << 4) | c2;

		    /* We not need here CR LF -> LF removing, because
		     * CRLF's which presents end of line should NOT be 
		     * encoded.
		     */

		    state_putc(ch,s_out);
		}
	    }
	    else {
		if (astext && *p == '\r')
		    store_ch = *p;
		else {
		    unsigned char ch;

		    if (*p == '\n' && astext && s_out->EOLN_is_CRLF)
			state_putc('\r',s_out);

		    ch = *p;
		    state_putc(ch,s_out);
		}
		if (*p == '\n') {
		    nl = TRUE;
		}
		if (p >= us_str(buf) +len)
		    break;
		p++;
	    }  
	}
    }

    /* Flush anything left in the buffer */
    if (store_ch) { /* for astext */
	state_putc(store_ch,s_out);
	store_ch = 0;
    }

    if (corrupted) {
	if (s_out -> displaying) {
	    /* state_nlputs or state_printf is needed for EOLN_is_CRLF
	       conversions */
	    state_nlputs("\n[ ",s_out);
	    state_printf(s_out,
			 CATGETS(elm_msg_cat, MeSet, MeDecodeQPCorrupt,
				 "Seems that QUOTED-PRINTABLE data was corrupted."));
	    state_nlputs(" ]\n",s_out);
	}
	lib_error (CATGETS(elm_msg_cat, MeSet, MeDecodeQPCorrupt,
			   "Seems that QUOTED-PRINTABLE data was corrupted."));
    }
    
    DPRINT(Debug,10,(&Debug, 
		     "quoted_printable_decode: read=%d bytes, corrupted=%d.\n",
		     bytes,corrupted));
    
    return;
}

/* -------------------------------------------------------------------------------------- */

void null_SG_decoder(body,sign,state_in,state_out,micalg, defcharset, mss,
		     badtype)
     mime_t *body; 
     mime_t *sign;
     in_state_t   *state_in;
     out_state_t  *state_out;
     const char *micalg;
     charset_t defcharset;
     struct header_rec *mss;
     type_mismatch_prompt *badtype;
{
    if (state_out->displaying) {	
	/* state_nlputs or state_printf is needed for EOLN_is_CRLF
	   conversions */
     	
	state_nlputs("\n[ ",state_out);
	state_printf(state_out,
		     CATGETS(elm_msg_cat, MeSet, 
			     MeDecodeUnsupportedSig,
			     "Signature %s/%.30s is unsupported, not checked"), 
		     get_major_type_name(sign->TYPE), 
		     get_subtype_name(sign->TYPE));
	state_nlputs(" ]\n",state_out);       
    }
    lib_error(CATGETS(elm_msg_cat, MeSet, MeDecodeUnsupportedSig,
		      "Signature %s/%.30s is unsupported, not checked"),
	      get_major_type_name(sign->TYPE), 
	      get_subtype_name(sign->TYPE));

    mime_decode(body,state_in,state_out, defcharset, mss, badtype);
}

void null_EC_decoder(init,data,state_in,state_out,defcharset, mss, badtype)
     mime_t *init;
     mime_t *data;
     in_state_t   *state_in;
     out_state_t  *state_out;
     charset_t defcharset;
     struct header_rec *mss;
     type_mismatch_prompt *badtype;
{
    /* state_nlputs or state_printf is needed for EOLN_is_CRLF
       conversions */
  	
    state_nlputs("\n[ ",state_out);
    state_printf(state_out,
		 CATGETS(elm_msg_cat, MeSet, 
			 MeDecodeUnsupportedEnc,
			 "Encryption %s/%.30s is unsupported"), 
		 get_major_type_name(init->TYPE), 
		 get_subtype_name(init->TYPE));
    state_nlputs(" ]\n",state_out);
    
    lib_error(CATGETS(elm_msg_cat, MeSet, 
		      MeDecodeUnsupportedEnc,
		      "Encryption %s/%.30s is unsupported"), 
	      get_major_type_name(init->TYPE), 
	      get_subtype_name(init->TYPE));
}


static SG_decoder_t select_SG_decoder P_((const char *protocol));
static SG_decoder_t select_SG_decoder(protocol)
     CONST char *protocol;
{
#ifdef USE_PGP
    if (0 == istrcmp(protocol,"application/pgp-signature")) {
	DPRINT(Debug,11,(&Debug, 
			 "select_SG_decoder [%s] = pgp_SG_decoder\n",
			 protocol));
	return pgp_SG_decoder;
    }
#endif

    DPRINT(Debug,11,(&Debug, 
		     "select_SG_decoder [%s] = null_SG_decoder\n",
		     protocol));
    return null_SG_decoder;
}

/* Prototype */
static void signed_decode P_((mime_t *part, 
			      in_state_t *streamin, 
			      out_state_t *streamout, 
			      charset_t defcharset, 
			      struct header_rec *hdr,
			      type_mismatch_prompt *badtype)); 

/* Returns -1 on failure otherwise same mask than mime_classify_media() */
S_(mime_run_selector signed_selector)
static int signed_selector(p,hdr)     
     mime_t *p;
     struct header_rec * hdr;
{
    int flags = 0;
    mime_t *z;

    int subflag = -1;

    if (p->parser_data &&
	mime_parser_subparts(p->parser_data) > 0 &&
	( z = mime_parser_index(p->parser_data,0)) &&
	
	0 <= ( subflag = mime_classify_media(z,hdr) )

	) {

	CONST char *pv;
	SG_decoder_t sg_decode;
	       
	pv = get_mime_param_compat(p->TYPE_opts,"protocol");

	if (! pv) {
	    DPRINT(Debug,11,(&Debug, 
			     "signed_selector(%p) -- type: %p=%s/%s, flags=%d -- no 'protocol'\n",
			     p,
			     p->TYPE,
			     get_major_type_name(p->TYPE), 
			     get_subtype_name(p->TYPE), 
			     get_type_flags(p->TYPE)));
	    flags |=  NOTPLAIN_need_metamail;
	    goto done;	   	    
	}
	sg_decode = select_SG_decoder(pv);


	if (MIME_selector_magic != p->handler_data->magic)
	    mime_panic(__FILE__,__LINE__,"signed_selector",
		       "Bad magic number (handler_data)");
	
	/* Record selection !! */
	p->handler_data->SG_decoder = sg_decode;

	flags |= subflag;
	
	if (sg_decode != null_SG_decoder) {
	    DPRINT(Debug,11,(&Debug, 
			    "signed_selector(%p) -- type: %p=%s/%s, flags=%d, protocol=%s\n",
			    p,
			    p->TYPE,
			    get_major_type_name(p->TYPE), 
			    get_subtype_name(p->TYPE), 
			    get_type_flags(p->TYPE), 
			    pv));
	    goto done;
	} else if (pagesigned) {
	    DPRINT(Debug,9,(&Debug, 
			    "signed_selector(%p) --  type: %p=%s/%s, flags=%d, protocol=%s, pagesigned=TRUE\n",
			    p,
			    p->TYPE,
			    get_major_type_name(p->TYPE), 
			    get_subtype_name(p->TYPE), 
			    get_type_flags(p->TYPE), 
			    pv));
	    goto done;
	}
    
	
	DPRINT(Debug,11,(&Debug, 
			 "signed_selector(%p) -- type: %p=%s/%s, flags=%d, protocol=%s -- unsupported \n",
			 p,
			 p->TYPE,
			 get_major_type_name(p->TYPE), 
			 get_subtype_name(p->TYPE), 
			 get_type_flags(p->TYPE), 
			 pv));
	flags |=  NOTPLAIN_need_metamail;
    }
    else
	flags |=  NOTPLAIN_need_metamail;

 done:

    DPRINT(Debug,11,(&Debug, 
		     "signed_selector(%p) = %d\n",
		     p,
		     flags));

    return flags;
}

S_(CT_decoder signed_decode)
static void signed_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;
{
    CONST char *pv;
    CONST char *micalg;
    SG_decoder_t decode;

    if (ptr->magic != MIME_magic)
	mime_panic(__FILE__,__LINE__,"signed_decode",
		   "Bad magic number");
    
    if (!in_state_seekable(state_in)) {
	mime_panic(__FILE__,__LINE__,"signed_decode",
		   "signed_decode: unsupported input state");
    }


    if (!ptr->parser_data) {
	CONST char * T = "multipart/signed";
	state_puts("[ ",state_out);
	state_printf(state_out,
		     CATGETS(elm_msg_cat, MeSet, MeDecodeTypeNotparsed,
			     "Content-Type: %s not parsed"),
		     T);
	state_nlputs(" ]\n",state_out);
	lib_error(CATGETS(elm_msg_cat, MeSet, MeDecodeTypeNotparsed,
			  "Content-Type: %s not parsed"),
		  T);
	return;
    }

    
    if (mime_parser_subparts(ptr->parser_data) != 2) {

	/* 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, MeDecodeSignedNosubtypes,
			     "Content-Type: multipart/signed, no subtypes"));
	state_nlputs(" ]\n",state_out);
	lib_error(CATGETS(elm_msg_cat, MeSet, MeDecodeSignedNosubtypes,
			  "Content-Type: multipart/signed, no subtypes"));
	return;
    }
    
    pv = get_mime_param_compat(ptr->TYPE_opts,"protocol");

    if (!pv) {
	/* 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, MeDecodeNoProtocol,
			     "'protocol' parameter is missing from Multipart/Signed -type!"));
	state_nlputs(" ]\n",state_out);
	lib_error(CATGETS(elm_msg_cat, MeSet, MeDecodeNoProtocol,
			  "'protocol' parameter is missing from Multipart/Signed -type!"));
	return;
    }

    micalg = get_mime_param_compat(ptr->TYPE_opts,"micalg");

    if (!micalg) {
	micalg  = "";
    }

    if (MIME_selector_magic != ptr->handler_data->magic)
	mime_panic(__FILE__,__LINE__,"signed_decode",
		   "Bad magic number (handler_data)");
	
    decode = ptr->handler_data->SG_decoder;

    decode(mime_parser_index(ptr->parser_data,0),
	   mime_parser_index(ptr->parser_data,1),
	   state_in,state_out,micalg, defcharset, 
	   mss, badtype);
}


static EC_decoder_t select_EC_decoder P_((const char *protocol));
static EC_decoder_t select_EC_decoder(protocol)
     CONST char *protocol;
{
#ifdef USE_PGP
    if (0 == istrcmp(protocol,"application/pgp-encrypted")) {
	DPRINT(Debug,11,(&Debug, 
			 "select_EC_decoder [%s] = pgp_EC_decoder\n",
			 protocol));
	return pgp_EC_decoder;
    }
#endif

    DPRINT(Debug,11,(&Debug, 
		     "select_EC_decoder [%s] = null_EC_decoder\n",
		     protocol));
    return null_EC_decoder;
}

/* Prototype */
static void encrypted_decode P_((mime_t *part, 
				 in_state_t *streamin, 
				 out_state_t *streamout,
				 charset_t defcharset, 
				 struct header_rec *hdr,
				 type_mismatch_prompt *badtype)); 

/* Returns -1 on failure otherwise same mask than mime_classify_media() */
S_(mime_run_selector encrypted_selector)
static int encrypted_selector(p,hdr)     
     mime_t *p;
     struct header_rec * hdr;
{
    int flags = 0;

    if (p->parser_data &&
	mime_parser_subparts(p->parser_data) > 0) {
	EC_decoder_t ec_decode;
	CONST char *pv;

	pv = get_mime_param_compat(p->TYPE_opts,"protocol");

	if (!pv) {
	    DPRINT(Debug,11,(&Debug, 
			     "encrypted_selector(%p) -- type: %p=%s/%s, flags=%d -- no 'protocol'\n",
			     p,
			     p->TYPE,
			     get_major_type_name(p->TYPE), 
			     get_subtype_name(p->TYPE), 
			     get_type_flags(p->TYPE)));
	    flags |= NOTPLAIN_need_metamail;
	    goto out;
	}
	ec_decode = select_EC_decoder(pv);

	if (MIME_selector_magic != p->handler_data->magic)
	    mime_panic(__FILE__,__LINE__,"encrypted_selector",
		       "Bad magic number (handler_data)");
	
	/* Record selection !! */
	p->handler_data->EC_decoder = ec_decode;

	if (ec_decode != null_EC_decoder) {
	    DPRINT(Debug,11,(&Debug, 
			     "encrypted_selector(%p) -- type: %p=%s/%s, flags=%d, protocol=%s\n",
			     p,
			     p->TYPE,
			     get_major_type_name(p->TYPE), 
			     get_subtype_name(p->TYPE), 
			     get_type_flags(p->TYPE), 
			     pv));

	    /* TODO:            We really not know on here can be show contents
	                        because we must decode it first to see ....
	    */
	   
	    flags = 0;
	    goto out;
	    
	} 
	
	DPRINT(Debug,11,(&Debug, 
			"encrypted_selector(%p) -- type: %p=%s/%s, flags=%d, protocol=%s\n",
			p,
			p->TYPE,
			get_major_type_name(p->TYPE), 
			get_subtype_name(p->TYPE), 
			get_type_flags(p->TYPE), 
			pv));
	flags |= NOTPLAIN_need_metamail;
    } else
	flags |= NOTPLAIN_need_metamail;

 out:

    DPRINT(Debug,11,(&Debug, 
		     "encrypted_selector(%p)=%d\n",
		     p,flags));
    
    return flags;
}

S_(CT_decoder encrypted_decode)
static void encrypted_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;
{
    CONST char *pv;
    EC_decoder_t decode;

    if (ptr->magic != MIME_magic)
	mime_panic(__FILE__,__LINE__,"encrypted_decode",
		   "Bad magic number");
    
    if (!in_state_seekable(state_in)) {
	mime_panic(__FILE__,__LINE__,"encrypted_decode",
		   "encrypted_decode: unsupported input state");
    }
    
    if (!ptr->parser_data) {
 	CONST char * T = "multipart/encrypted";
	state_puts("[ ",state_out);
	state_printf(state_out,
		     CATGETS(elm_msg_cat, MeSet, MeDecodeTypeNotparsed,
			     "Content-Type: %s not parsed"),
		     T);
	state_nlputs(" ]\n",state_out);
	lib_error(CATGETS(elm_msg_cat, MeSet, MeDecodeTypeNotparsed,
			  "Content-Type: %s not parsed"),
		  T);
	return;
    }

    if (mime_parser_subparts(ptr->parser_data) != 2) {
	/* 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, MeDecodeEncryptedNosubtypes,
			     "Content-Type: multipart/encrypted, no subtypes"));
	state_nlputs(" ]\n",state_out);
	lib_error(CATGETS(elm_msg_cat, MeSet, MeDecodeEncryptedNosubtypes,
			  "Content-Type: multipart/encrypted, no subtypes"));
	return;
    }
    
    pv = get_mime_param_compat(ptr->TYPE_opts,"protocol");

    if (!pv) {
	/* 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, MeDecodeNoProtocolEnc,
			     "'protocol' paramater is missing from multipart/encrypted -type!"));
	state_nlputs(" ]\n",state_out);
	lib_error(CATGETS(elm_msg_cat, MeSet, MeDecodeNoProtocolEnc,
			  "'protocol' paramater is missing from multipart/encrypted -type!"));
	return;
    }

    if (MIME_selector_magic != ptr->handler_data->magic)
	mime_panic(__FILE__,__LINE__,"encrypted_selector",
		   "Bad magic number (handler_data)");

    decode = ptr->handler_data->EC_decoder;

    decode(mime_parser_index(ptr->parser_data,0),
	   mime_parser_index(ptr->parser_data,1),
	   state_in,state_out,defcharset, mss, badtype);

}

/* Prototype */
static void alternative_decode P_((mime_t *part, 
				   in_state_t *streamin, 
				   out_state_t *streamout, 
				   charset_t defcharset,
				   struct header_rec *hdr,
				   type_mismatch_prompt *badtype)); 

/* Returns -1 on failure otherwise same mask than mime_classify_media() */
S_(mime_run_selector alternative_selector)
static int alternative_selector(p,hdr)     
     mime_t *p;
     struct header_rec * hdr;
{
    int flags = 0;
    mime_t * z = NULL;

    if (p->parser_data &&
	mime_parser_subparts(p->parser_data) > 0 &&
	0 <= ( flags = mime_classify_best_alternative(p,&z,hdr))) {


	if (MIME_selector_magic != p->handler_data->magic)
	    mime_panic(__FILE__,__LINE__,"alternative_selector",
		       "Bad magic number (handler_data)");
	
	/* Record selection !! */
	p->handler_data->selected_alternative = z;

	/* If pagealternative is not set, we must able to handle
	   all parts ... 
	*/
	if (!pagealternative) {
	    DPRINT(Debug,11,(&Debug, 
			     "alternative_selector:  pagealternative is not set, we must able to handle all parts ...\n"));
	    flags = mime_classify_subparts(p,hdr);
	}

	DPRINT(Debug,11,(&Debug, 
			 "alternative_selector(%p) -- type: %p=%s/%s, "
			 "best_alternative: %p=%s/%s\n",
			 p,
			 p->TYPE,
			 get_major_type_name(p->TYPE), 
			 get_subtype_name(p->TYPE),
			 z->TYPE,
			 get_major_type_name(z->TYPE), 
			 get_subtype_name(z->TYPE)));
	goto out;
    } else	
	flags |= NOTPLAIN_need_metamail;
    
 out:
    
    DPRINT(Debug,11,(&Debug, 
		     "alternative_selector(%p)=%d\n",
		     p,flags));
    
    return flags;
}

S_(CT_decoder alternative_decode)
static void alternative_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;
{
    mime_t *best = NULL;

    if (ptr->magic != MIME_magic)
	mime_panic(__FILE__,__LINE__,"alternative_decode",
		   "Bad magic number");
    
    if (!in_state_seekable(state_in)) {
	mime_panic(__FILE__,__LINE__,"alternative_decode",
		   "alternative_decode: unsupported input state");
    }
    
    if (!ptr->parser_data) {
	CONST char * T = "multipart/alternative";
	state_puts("[ ",state_out);
	state_printf(state_out,
		     CATGETS(elm_msg_cat, MeSet, MeDecodeTypeNotparsed,
			     "Content-Type: %s not parsed"),
		     T);
	state_nlputs(" ]\n",state_out);
	lib_error(CATGETS(elm_msg_cat, MeSet, MeDecodeTypeNotparsed,
			  "Content-Type: %s not parsed"),
		  T);

	return;
    }

    if ( mime_parser_subparts(ptr->parser_data) < 1) {
	/* 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, MeDecodeAlternativeNosubtypes,
			     "Content-Type: multipart/alternative, no subtypes"));
	state_nlputs(" ]\n",state_out);
	lib_error(CATGETS(elm_msg_cat, MeSet, MeDecodeAlternativeNosubtypes,
			  "Content-Type: multipart/alternative, no subtypes"));
	return;
    }


    if (MIME_selector_magic != ptr->handler_data->magic)
	mime_panic(__FILE__,__LINE__,"alternative_decode",
		   "Bad magic number (handler_data)");
    
    best = ptr->handler_data->selected_alternative; 

    if (best) {


	if (best->magic != MIME_magic)
	    mime_panic(__FILE__,__LINE__,"alternative_decode",
		       "Bad magic number (selected alternative)");
	
	DPRINT(Debug,11,(&Debug, 
			 "multipart_alternative: Type: %.15s/%.30s, Encoding: %d, Size: %ld\n",
			 get_major_type_name(best->TYPE), 
			 get_subtype_name(best->TYPE),
			 best->encoding, (long) best->length));
	
	if (in_state_fseek(state_in,best->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, MeDecodeAlternativeSeekFailed,
				 "multipart_alternative: seek failed"));
	    state_nlputs(" ]\n",state_out);
	    lib_error(CATGETS(elm_msg_cat, MeSet, MeDecodeAlternativeSeekFailed,
			      "multipart_alternative: seek failed"));
	    return;
	}


	if (state_out->displaying) {
	    char tmp[40];
	    char *Encoding = "???";

	    switch (best->disposition) { 
	    case DISP_ATTACH:
		state_printf(state_out,CATGETS(elm_msg_cat, MeSet, 
					       MeDecodeSelAttachA, 
					       "[ Selecting attach: "));
		break;
	    default:
		state_printf(state_out,CATGETS(elm_msg_cat, MeSet, 
					       MeDecodeSelPartA,
					       "[ Selecting part: "));
	    }

	    tmp[0] = '\0';
	    if (best->description &&
		string_len(best->description) > 0) {
		state_printf(state_out,CATGETS(elm_msg_cat, MeSet, 
					       MeDecodeDescA,
					       "\"%.60S\" ]\n"),
			     best->description);
		state_puts("[ ",state_out);
	    } else {

		int mp = give_dt_enumerate_as_int(&mime_parameters);
		CONST struct string *dv;
		CONST char * dva;

		/* 0 == plain
		   1 == encoded
		   2 == plain-and-encoded
		*/
		
		
		
		if (mp &&
		    (dv = get_mime_param(best->DISPOSITION_opts,"filename"))
		    ) {
		    state_printf(state_out,CATGETS(elm_msg_cat, MeSet, 
						   MeDecodeDescFilenameA,
						   "Filename: %.60S ]\n"),
				 dv);
		    state_puts("[ ",state_out);
		} else if ((dva = get_mime_param_compat(best->DISPOSITION_opts,
							"filename"))) {
		    state_printf(state_out,CATGETS(elm_msg_cat, MeSet, 
						   MeDecodeDescFilenameA1,
						   "Filename: %.60s ]\n"),
				 dva);
		    state_puts("[ ",state_out);
		}
	    }

	    Encoding = ENCODING(best->encoding);
	    
	    /* state_nlputs or state_printf is needed for EOLN_is_CRLF
	       conversions */

	    state_printf(state_out,CATGETS(elm_msg_cat, MeSet, MeDecodeDescC,
					   "Type: %.15s/%.30s, Encoding: %s, Size: %d ]\n"),
			 get_major_type_name(best->TYPE), 
			 get_subtype_name(best->TYPE),
			 Encoding, best->length);
	    state_nlputs("\n", state_out);

	} else {	    
	    if (best->description) {
		/* state_nlputs or state_printf is needed for EOLN_is_CRLF
		   conversions */

		state_printf(state_out,CATGETS(elm_msg_cat, MeSet, 
					       MeDecodeContentDescA,
					       "Content-Description: %.300S\n"),
			     best->description);
		state_nlputs("\n", state_out);
	    } 
	}

	mime_decode (best, state_in, state_out, defcharset, mss, badtype);
	
    } else {
	/* 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, MeDecodeCtNoAlternative,
			     "Content-Type: multipart/alternative, alternative not found"));
    
	state_nlputs(" ]\n", state_out);
	lib_error(CATGETS(elm_msg_cat, MeSet, MeDecodeCtNoAlternative,
			  "Content-Type: multipart/alternative, alternative not found"));
    }

}

/* Prototype */
static void multipart_0_decode P_((mime_t *part, 
				   in_state_t *streamin, 
				   out_state_t *streamout,
				   charset_t defcharset, 
				   struct header_rec *hdr,
				   type_mismatch_prompt *badtype)); 

/* Returns -1 on failure otherwise same mask than mime_classify_media() */
S_(mime_run_selector multipart_selector)
static int multipart_selector(p,hdr)     
     mime_t *p;
     struct header_rec * hdr;
{
    int flags = 0;
    int Flags = get_type_flags(p->TYPE);

    int subflag = -1;

    if ((0 <= ( subflag = mime_classify_subparts(p,hdr))) &&
	((Flags & MIME_MIXED) || 
	 (Flags & MIME_DIGEST)) ) {

	DPRINT(Debug,11,(&Debug, 
			"multipart_selector(%p) -- type: %p=%s/%s, flags=%d\n",
			p,
			p->TYPE,
			get_major_type_name(p->TYPE), 
			get_subtype_name(p->TYPE), 
			get_type_flags(p->TYPE)));

	if (pagemultipart && 0 != ( subflag & NOTPLAIN_need_metamail)) {
	    flags |= subflag & ~NOTPLAIN_need_metamail;

	    DPRINT(Debug,11,(&Debug, 
			     "multipart_selector(%p)   (pagemultipart=TRUE)\n",
			     p));

	} else
	    flags |= subflag;
	goto done;
    } else if (get_major_type_code(p->TYPE) == MIME_TYPE_MULTIPART && 
	       pagemultipart) {
      	DPRINT(Debug,11,(&Debug, 
			 "multipart_selector(%p) -- type: %p=%s/%s "
			 "(pagemultipart=TRUE)\n",
			 p,
			 p->TYPE,
			 get_major_type_name(p->TYPE), 
			 get_subtype_name(p->TYPE)));
	
	
	if (subflag > 0) {
	    /* Copy	       
	       NOTPLAIN_need_mailcap            0x02
	       NOTPLAIN_canuse_mailcap          0x04
	    */
	    flags |= subflag & ~NOTPLAIN_need_metamail;
	}

	goto done;
    } else
	flags |=  NOTPLAIN_need_metamail;


 done:

    DPRINT(Debug,11,(&Debug, 
		     "multipart_selector(%p) = %d\n",
		     p,flags));

    return flags;
}

static int may_group P_((mime_t *ptr));
static int may_group(ptr)
     mime_t *ptr;
{
    CONST struct string *dv;
    CONST char * dva;
    CONST char * st;

    if (ptr->disposition != DISP_INLINE)
	return 0;

    if (ptr->description &&
	string_len(ptr->description) > 0)
	return 0;

    dv = get_mime_param(ptr->DISPOSITION_opts,"filename");
    if (dv)
	return 0;

    dva = get_mime_param_compat(ptr->DISPOSITION_opts,"filename");
    if (dva)
	return 0;

    if (get_major_type_code(ptr->TYPE) != MIME_TYPE_TEXT ||
	0 != istrcmp(get_subtype_name(ptr->TYPE),"plain"))
	return 0;
    
    if (0 != (ptr->mime_flags & NOTPLAIN_is_fallback) ||
	0 != (ptr->mime_flags & NOTPLAIN_need_metamail))
	return 0;   /* Unsupported charset */
    
    return 1;
}

static void multipart_one_decode  P_((mime_t *att,
				      in_state_t *state_in,
				      out_state_t *state_out,
				      int nattach, int count,
				      charset_t defcharset,
				      struct header_rec *mss,
				      type_mismatch_prompt *badtype));

static void multipart_one_decode(att,state_in,state_out,nattach,count,
				 defcharset,mss,badtype)
     mime_t *att;
     in_state_t *state_in;
     out_state_t *state_out;
     int nattach;
     int count;     
     charset_t defcharset;
     struct header_rec *mss;
     type_mismatch_prompt *badtype;
{
    CONST struct string *dv;

    if (in_state_fseek(state_in,att->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, MeDecodeMultipartSeekFail,
			     "multipart_decode: seek failed"));
	state_nlputs(" ]\n",state_out);
	lib_error(CATGETS(elm_msg_cat, MeSet, MeDecodeMultipartSeekFail,
			  "multipart_decode: seek failed"));
	return;
    }

    /* state_nlputs or state_printf is needed for EOLN_is_CRLF
       conversions */
    if (nattach > 1)
	state_nlputs("\n", state_out);
    
    if (state_out->displaying) {
	char tmp[40];
	char *Encoding = "???";
	
	switch (att->disposition) { 
	case DISP_ATTACH:
	    state_printf(state_out,CATGETS(elm_msg_cat, MeSet, 
					   MeDecodeAttachA, 
					   "[ Attach #%d/%d: "),
			 nattach,count);
	    break;
	default:
	    state_printf(state_out,CATGETS(elm_msg_cat, MeSet, 
					   MeDecodePartA,
					   "[ Part #%d/%d: "),
			 nattach,count);
	}
	
	tmp[0] = '\0';
	if (att->description &&
	    string_len(att->description) > 0) {
	    state_printf(state_out,CATGETS(elm_msg_cat, MeSet, 
					   MeDecodeDescA,
					   "\"%.60S\" ]\n"),
			 att->description);
	    state_puts("[ ",state_out);
	} else {
	    
	    int mp = give_dt_enumerate_as_int(&mime_parameters);
	    CONST struct string *dv;
	    CONST char * dva;
	    
	    /* 0 == plain
	       1 == encoded
	       2 == plain-and-encoded
	    */
	    
	    if (mp &&
		(dv = get_mime_param(att->DISPOSITION_opts,
				     "filename"))
		) {
		state_printf(state_out,CATGETS(elm_msg_cat, MeSet, 
					       MeDecodeDescFilenameA,
					       "Filename: %.60S ]\n"),
			     dv);
		state_puts("[ ",state_out);
	    } else if ((dva = get_mime_param_compat(att->DISPOSITION_opts,
						    "filename"))) {
		state_printf(state_out,CATGETS(elm_msg_cat, MeSet, 
					       MeDecodeDescFilenameA1,
					       "Filename: %.60s ]\n"),
			     dva);
		state_puts("[ ",state_out);
	    }		   
	}
	
	
	Encoding = ENCODING(att->encoding);
	
	/* state_nlputs or state_printf is needed for EOLN_is_CRLF
	   conversions */
	
	state_printf(state_out,CATGETS(elm_msg_cat, MeSet, MeDecodeDescC,
				       "Type: %.15s/%.30s, Encoding: %s, Size: %d ]\n"),
		     get_major_type_name(att->TYPE), 
		     get_subtype_name(att->TYPE),
		     Encoding, att->length);
	state_nlputs("\n", state_out);
	
    } else {	    
	if (att->description) {
	    /* state_nlputs or state_printf is needed for EOLN_is_CRLF
	       conversions */
	    state_printf(state_out,CATGETS(elm_msg_cat, MeSet, 
					   MeDecodeContentDescA,
					   "Content-Description: %.300S\n"),
			 att->description);
	    state_nlputs("\n", state_out);
	} 
    }
    
    mime_decode (att, state_in, state_out, defcharset, mss, badtype);

}


S_(CT_decoder multipart_mixed_decode)
static void multipart_mixed_decode P_((mime_t *ptr,
				      in_state_t *state_in,
				      out_state_t *state_out,
				      charset_t defcharset,
				      struct header_rec *mss,
				       type_mismatch_prompt *badtype));
static void multipart_mixed_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 count;
    int outer, inner = 0;

    /* Group text/plain parts together ... 

       Note that mime parts do not necessary have ending CRLF.
       That also allow changing charset on middle of line.

       RFC 2046 says:

       NOTE:  The CRLF preceding the boundary delimiter line is conceptually
       attached to the boundary so that it is possible to have a part that
       does not end with a CRLF (line  break).  Body parts that must be
       considered to end with line breaks, therefore, must have two CRLFs
       preceding the boundary delimiter line, the first of which is part of
       the preceding body part, and the second of which is part of the
       encapsulation boundary.

    */

    if (ptr->magic != MIME_magic)
	mime_panic(__FILE__,__LINE__,"multipart_mixed_decode",
		   "Bad magic number");

    if (!ptr->parser_data) {
	CONST char * T = "multipart/mixed";
	state_puts("[ ",state_out);
	state_printf(state_out,
		     CATGETS(elm_msg_cat, MeSet, MeDecodeTypeNotparsed,
			     "Content-Type: %s not parsed"),
		     T);
	state_nlputs(" ]\n",state_out);
	lib_error(CATGETS(elm_msg_cat, MeSet, MeDecodeTypeNotparsed,
			  "Content-Type: %s not parsed"),
		  T);

	return;
    }

    if (!in_state_seekable(state_in)) 
	mime_panic(__FILE__,__LINE__,"multipart_mixed_decode",
		   "unsupported input state");
    
    count = mime_parser_subparts(ptr->parser_data);
	
    
    for (outer = 0; outer < count; outer=inner) {
	mime_t *part_outer    = mime_parser_index(ptr->parser_data,outer);

	CONST int  nattach = outer+1;

	if (! may_group(part_outer)) {

	one_part:

	    if (part_outer->magic != MIME_magic)
		mime_panic(__FILE__,__LINE__,"multipart_mixed_decode",
			   "Bad magic number (subpart)");
	    
	    DPRINT(Debug,11,(&Debug, 
			     "multipart_mixed_decode: [%d]: Type: %p (%s/%s), Encoding: %d, Size: %ld\n",
			     nattach, 
			     part_outer->TYPE,
			     get_major_type_name(part_outer->TYPE),
			     get_subtype_name(part_outer->TYPE),
			     part_outer->encoding, (long) part_outer->length));
	    	    
	    multipart_one_decode(part_outer,state_in,state_out,nattach,count,
				 defcharset, mss, badtype);
	    
	    inner = outer+1;
	} else {
	    int scan;
	    int len = 0;

	    for (scan = outer; scan < count; scan++) {
		mime_t *part_scan    = mime_parser_index(ptr->parser_data,scan);
		if (! may_group(part_scan))
		    break;
		
		len +=  part_scan->length;
	    }

	    if (scan == nattach)
		goto one_part;
	    
	    /* state_nlputs or state_printf is needed for EOLN_is_CRLF
	       conversions */
	    if (nattach > 1)
		state_nlputs("\n", state_out);
	    
	    if (state_out->displaying) {
		
		state_printf(state_out,CATGETS(elm_msg_cat, MeSet, 
					       MeDecodePartsDesc,
					       "[ Parts #%d-%d/%d: Type: text/plain, Size: %d ]\n"),
			     nattach,scan,count,len);

		state_nlputs("\n", state_out);
	    }


	    for (inner = outer; inner < scan; inner++) {
		mime_t *part_inner    = mime_parser_index(ptr->parser_data,inner);

		if (part_inner->magic != MIME_magic)
		    mime_panic(__FILE__,__LINE__,"multipart_mixed_decode",
			       "Bad magic number (subpart)");
	    
		DPRINT(Debug,11,(&Debug, 
				 "multipart_mixed_decode: [%d]: Type: %p (%s/%s), Encoding: %d, Size: %ld\n",
				 nattach, 
				 part_inner->TYPE,
				 get_major_type_name(part_inner->TYPE),
				 get_subtype_name(part_inner->TYPE),
				 part_inner->encoding, (long) part_inner->length));



		mime_decode (part_inner, state_in, state_out, defcharset, mss,
			     badtype);
	    }
	}
    }
}


S_(CT_decoder multipart_0_decode)
static void multipart_0_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 count;

    if (ptr->magic != MIME_magic)
	mime_panic(__FILE__,__LINE__,"multipart_0_decode",
		   "Bad magic number");

    if (!ptr->parser_data) {
	CONST char * T = "multipart/*";
	state_puts("[ ",state_out);
	state_printf(state_out,
		     CATGETS(elm_msg_cat, MeSet, MeDecodeTypeNotparsed,
			     "Content-Type: %s not parsed"),
		     T);
	state_nlputs(" ]\n",state_out);
	lib_error(CATGETS(elm_msg_cat, MeSet, MeDecodeTypeNotparsed,
			  "Content-Type: %s not parsed"),
		  T);

	return;
    }

    if (!in_state_seekable(state_in)) {
	mime_panic(__FILE__,__LINE__,"multipart_0_decode",
		   "unsupported input state");
    }

    count = mime_parser_subparts(ptr->parser_data);
    if (!count) {
	/* 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, MeDecodeMultipartNoparts,
			     "Content-Type: multipart/*, no subparts"));
	state_nlputs(" ]\n",state_out);
	lib_error(CATGETS(elm_msg_cat, MeSet, MeDecodeMultipartNoparts,
			  "Content-Type: multipart/*, no subparts"));
    } else {
	int i;

	for (i = 0; i < count; i++) {
	    mime_t *att        = mime_parser_index(ptr->parser_data,i);
	    CONST int  nattach = i+1;
	    
	    if (att->magic != MIME_magic)
		mime_panic(__FILE__,__LINE__,"multipart_0_decode",
			   "Bad magic number (subpart)");
	    
	    DPRINT(Debug,11,(&Debug, 
			     "multipart_0_decode: [%d]: Type: %p (%s/%s), Encoding: %d, Size: %ld\n",
			     nattach, 
			     att->TYPE,
			     get_major_type_name(att->TYPE),
			     get_subtype_name(att->TYPE),
			     att->encoding, (long) att->length));
	    	    
	    multipart_one_decode(att,state_in,state_out,nattach,count,
				 defcharset, mss, badtype);
	
	}
    }
}

/* Prototype */
static void rfc822_decode P_((mime_t *part, 
			      in_state_t *instream, 
			      out_state_t *outstream, 
			      charset_t defcharset,
			      struct header_rec *hdr,
			      type_mismatch_prompt *badtype)); 



int rfc822_header_filter(hdr,flag)
     header_list_ptr hdr;
     int flag;                  /* elm_filter */
{
  char buf[80];

  strfcpy(buf,give_header_name(hdr->header_name),78);
  strfcat(buf,": ", sizeof buf);
  
  if (flag) {
    if (matches_weedlist (buf)) 
      return 0;
    else
      return 1;
  } else
    return 1;
}

static void nomime_decode P_((mime_t *ptr,
			      in_state_t *state_in,
			      out_state_t *state_out));
static void nomime_decode (ptr, state_in, state_out)
     mime_t *ptr;
     in_state_t *state_in;
     out_state_t *state_out;
{
    int j;

    DPRINT(Debug,11,(&Debug, 
		     "nomime_decode -> state: offset=%ld, length=%ld -- ",
		     (long) ptr -> offset, (long) ptr -> length));
    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, 
		     "\n"));
    
    
    if (ptr->magic != MIME_magic)
	mime_panic(__FILE__,__LINE__,"nomime_decode",
		   "Bad magic number");
    
    if (!in_state_seekable(state_in)) {
	mime_panic(__FILE__,__LINE__,"nomime_decode",
		   "nomime_decode: unsupported input state");
    }
    
    
    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, MeDecodeNoMimeSeekFailed,
			     "nomime_decode: seek failed"));
	state_nlputs(" ]\n",state_out);
	lib_error(CATGETS(elm_msg_cat, MeSet, MeDecodeNoMimeSeekFailed,
			  "nomime_decode: seek failed"));
	return;
    }
    
    /* default-nomime-charset gives assumed charset */
    state_out -> filter = default_nomime_charset;

    /* No MIME is just text ... */
    Xbit_decode (state_in, state_out, ptr->length, 1);
    
    DPRINT(Debug,11,(&Debug, 
		     "nomime_decode <- END\n"));
}

/* Returns -1 on failure otherwise same mask than mime_classify_media() */
S_(mime_run_selector rfc822_selector)
static int rfc822_selector(p,hdr)     
     mime_t *p;
     struct header_rec * hdr;
{
    int flags = 0;
    int subflag = -1;


    if (0 <= ( subflag = mime_classify_subparts(p,
						hdr /* Correct? */
						))) {
	DPRINT(Debug,11,(&Debug, 
			 "rfc822_selector(%p) -- type: %p=%s/%s\n",
			 p,
			 p->TYPE,
			 get_major_type_name(p->TYPE), 
			 get_subtype_name(p->TYPE)));
	flags |= subflag;
	goto done;
    } else 
	flags |=  NOTPLAIN_need_metamail;


 done:

    DPRINT(Debug,11,(&Debug, 
		     "rfc822_selector(%p) = %d\n",
		     p,flags));

    return flags;
}

S_(CT_decoder rfc822_decode)
static void rfc822_decode (mt, state_in, state_out, defcharset, mss, badtype)
     mime_t *mt;
     in_state_t *state_in;
     out_state_t *state_out;
     charset_t defcharset;
     struct header_rec *mss;
     type_mismatch_prompt *badtype;
{
    /* This looks a lot like multipart_decode() except that we want to print
     * the headers for the message.  NOTE: "mt" should be a pointer to the
     * RFC822 attachment, NOT its subpart, since we need the offset in order
     * to print out the headers.
     */

    header_list_ptr headers = NULL;
    header_list_ptr From_header, mime_version, osv;
    int decode_headers = 1;

    if (mt->magic != MIME_magic)
	mime_panic(__FILE__,__LINE__,"rfc822_decode",
		   "Bad magic number");
  
    if (!in_state_seekable(state_in)) {
	mime_panic(__FILE__,__LINE__,"rfc822_decode",
		   "rfc822_decode: unsupported input state");
    }

  if (in_state_fseek(state_in,mt->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, MeDecodeRFC822SeekFailed,
			   "rfc822_decode: seek failed"));
      state_nlputs(" ]\n",state_out);
      lib_error(CATGETS(elm_msg_cat, MeSet, MeDecodeRFC822SeekFailed,
			"rfc822_decode: seek failed"));
      return;
  }

  if (!mt->parser_data) {
      	CONST char * T = "message/rfc822";
	state_puts("[ ",state_out);
	state_printf(state_out,
		     CATGETS(elm_msg_cat, MeSet, MeDecodeTypeNotparsed,
			     "Content-Type: %s not parsed"),
		     T);
	state_nlputs(" ]\n",state_out);
	lib_error(CATGETS(elm_msg_cat, MeSet, MeDecodeTypeNotparsed,
			  "Content-Type: %s not parsed"),
		  T);

      return;
  }


  headers = state_read_headers(state_in, RHL_MARK_FOLDING);

  mime_version = locate_header_by_name(headers,"MIME-Version");
  osv          = locate_header_by_name(headers,"X-ELM-OSV");

  if (!mime_version && !req_mime_hdrencoding && !req_mime_hdrencoding)
      decode_headers = 0;
  if (osv && osv->body) {
      char value[20];
      if (mime_get_param("no-hdr-encoding",value,osv->body,
			 sizeof value) && atoi(value) > 0)
	  decode_headers = 0;

      /* FIXME: Update defcharset */
  }
  
  From_header  = locate_header_by_name(headers,"From");

  if (From_header) {
      struct string * buffer = give_decoded_header(From_header,
						   decode_headers,
						   defcharset);

      state_printf(state_out,
		   CATGETS(elm_msg_cat, MeSet, MeDecodeStartMail,
			   "-- Start of included mail From: %S\n"),
		   buffer);

      free_string(&buffer);
  }
  else
      state_printf(state_out,
		   CATGETS(elm_msg_cat, MeSet, MeDecodeStartMail1,
			   "-- Start of included mail.\n"));

  /* state_nlputs or state_printf is needed for EOLN_is_CRLF
     conversions */

  state_nlputs("\n",state_out);

  state_write_headers(state_out,headers,
		      rfc822_header_filter,
		      elm_filter,
		      decode_headers,
		      defcharset
		      );


  delete_headers(&headers); 

  /* state_nlputs or state_printf is needed for EOLN_is_CRLF
     conversions */

  state_nlputs("\n",state_out);

  
  if (mime_parser_subparts(mt->parser_data) != 1) {
      /* 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, MeDecodeNoRFC822,
			   "rfc822_decode: no body of RFC 822 mail"));
      state_nlputs(" ]\n",state_out);
      lib_error(CATGETS(elm_msg_cat, MeSet, MeDecodeNoRFC822,
			"rfc822_decode: no body of RFC 822 mail"));
      return;
  } else {
      mime_t * part = mime_parser_index(mt->parser_data,0);
  
      if (part->magic != MIME_magic)
	  mime_panic(__FILE__,__LINE__,"rfc822_decode",
		     "Bad magic number (parts)");

      /* TODO: Handle Pre-MIME Content-types ... */
      if (mime_version || !req_mime_bodyencoding)
	  mime_decode (part, state_in, state_out, defcharset,

		       /* TODO: Is it correct to pass parent's message
			  record??? (mss) */
		       NULL,
		       badtype);
      else
	  nomime_decode (part, state_in, state_out);
  }

  state_printf(state_out,
	       CATGETS(elm_msg_cat, MeSet, MeDecodeEndMail,
		       "-- End of included mail.\n"));
  return;
}

int run_cte_decoder (ptr, state_in, state_out) 
     mime_t *ptr;
     in_state_t *state_in;
     out_state_t *state_out;
{
    int is_text;

    DPRINT(Debug,11,(&Debug, 
		     "run_cte_decoder -> state: offset=%ld, length=%ld\n",
		     (long) ptr -> offset, (long) ptr -> length));

  if (ptr->magic != MIME_magic)
    mime_panic(__FILE__,__LINE__,"run_cte_decoder",
	       "Bad magic number");


  if (in_state_seekable(state_in)) {
      DPRINT(Debug,11,(&Debug, 
		       "              (file): ftell=%ld\n",
		       in_state_ftell(state_in))); 
  } else {
    	DPRINT(Debug,11,(&Debug, 			
			 "              (\?\?\?\?): magic=%d\n",
			 state_in->magic));
  }


  /* 7bit and 8bit are only allowed for line orienteed types */
  if (ptr->encoding == ENCODING_NONE || 
      ptr->encoding == ENCODING_7BIT || 
      ptr->encoding == ENCODING_8BIT) {
      DPRINT(Debug,11,(&Debug,
		       "run_cte_decoder: textual encoding (%s)\n",
		       ENCODING(ptr->encoding)));
      is_text = 1;
  } else
      is_text = give_text_type_code(ptr->TYPE);
  
  DPRINT(Debug,11,(&Debug, 
		   "run_cte_decoder: is_text=%d; type=%p (%s/%s); encoding =%s (%d)\n",
		   is_text, 
		   ptr->TYPE,
		   get_major_type_name(ptr->TYPE),
		   get_subtype_name(ptr->TYPE),
		   ENCODING(ptr->encoding),
		   ptr->encoding));

  if (ptr->encoding == ENCODING_BASE64)
      base64_decode (state_in, state_out, ptr->length, is_text);
  else if (ptr->encoding == ENCODING_QUOTED)
      quoted_printable_decode (state_in, state_out, ptr->length, is_text);
  else if (ptr->encoding == ENCODING_UUENCODED)
      uudecode (state_in, state_out, ptr->length, is_text);
  else if (ptr->encoding != ENCODING_NONE && 
	   ptr->encoding != ENCODING_7BIT &&
	   ptr->encoding != ENCODING_8BIT &&
	   ptr->encoding != ENCODING_BINARY) {

      DPRINT(Debug,11,(&Debug, 			
		       "run_cte_decoder=0 <- END; \n"));
    if (in_state_seekable(state_in)) {
	DPRINT(Debug,11,(&Debug, 
			 "              (file); ftell=%ld\n",
			 in_state_ftell(state_in))); 
    }
    return 0;
  }
  else
    Xbit_decode (state_in, state_out, ptr->length, is_text);

  DPRINT(Debug,11,(&Debug, 		   
		   "run_cte_decoder=1 <- END; \n"));
  if (in_state_seekable(state_in)) {
      DPRINT(Debug,11,(&Debug, 
		       "              (file); ftell=%ld\n",
		       in_state_ftell(state_in))); 
  }
  
  return 1;
}

int set_filter (ptr, state, default_content_charset,override_charset) 
     mime_t *ptr;
     out_state_t *state;
     charset_t default_content_charset;
     charset_t override_charset;   /* IF set, ignore charset= -paramater */
{
    int code;
    charset_t tmp;

    DPRINT(Debug,40,(&Debug, 
		     "set_filter -> state: offset=%d, length=%ld\n",
		     (long) ptr -> offset, (long) ptr -> length));
    
    if (ptr->magic != MIME_magic)
	mime_panic(__FILE__,__LINE__,"set_filter",
		   "Bad magic number");
    
    state -> filter = NULL; /* So that messages can be printed .. */
           

    code = mime_get_charset (&tmp, ptr->TYPE_opts,
			     state->display_charset,
			     default_content_charset
			     );

    if (override_charset && override_charset != tmp) {

	state_printf(state,
		     CATGETS(elm_msg_cat, MeSet, 
			     MeDecodeCharsetOverride,
				 "[ Charset %.15s ignored, treated as %.15s ]\n"), 
		     tmp->MIME_name ? tmp->MIME_name : "<none>",
		     override_charset->MIME_name ? 
		     override_charset->MIME_name : "<none>");

	state_nlputs("\n",state);

	state -> filter = override_charset;

    } else {
	if (0 == code) {
	    
	    /* Don't show this part (or copy to reply buffer) ... */	  
	    
	    state_printf(state,
			 CATGETS(elm_msg_cat, MeSet, 
				 MeDecodeCharsetSkipping,
				 "[ Charset %.15s unsupported, skipping... ]\n"), 
			 tmp->MIME_name ? tmp->MIME_name : "<none>");
	    
	    if (state->displaying) {
		state_printf(state,
			     CATGETS(elm_msg_cat, MeSet, MeDecodeUseVtoView,
				     "[ Use 'v' to view or save this part. ]\n"));
		state_printf(state,
			     CATGETS(elm_msg_cat, MeSet, MeDecodeUseOtoOverride,
				     "[ You can also use 'O' to override charset information. ]\n"));	    

	    }

	    state -> filter = tmp;
	    
	    DPRINT(Debug,40,(&Debug, 
			     "set_filter=0 filter=%p '%s' (type=%p)\n",
			     state -> filter,
			     state -> filter->MIME_name ?
			     state -> filter->MIME_name : "<none>",
			     state -> filter->charset_type));
	    
	    return 0;
	} else {
	    if (code < 0) {
		
		DPRINT(Debug,40,(&Debug,
				 "   code=%d charset_convert_ok=%d\n",
				 code,charset_convert_ok));
		
		if (code == -3) {   /* Converted without lossage */
		    if (!charset_convert_ok && 
			(!tmp->MIME_name || 0 != istrcmp(tmp->MIME_name,"US-ASCII")))
			state_printf(state,
				     CATGETS(elm_msg_cat, MeSet, 
					     MeDecodeCharsetConvertNoLoss,
					     "[ Charset %.15s converted... ]\n"),
				     tmp->MIME_name ? tmp->MIME_name : 
				     "<none>");
		    
		} else {
		    state_printf(state,
				 CATGETS(elm_msg_cat, MeSet, 
					 MeDecodeCharsetConvert,
					 "[ Charset %.15s unsupported, converting... ]\n"),
				 tmp->MIME_name ? tmp->MIME_name : 
				 "<none>");
		    
		    if (state->displaying) {	
			/* state_nlputs or state_printf is needed for EOLN_is_CRLF
			   conversions */
			
			state_printf(state,
				     CATGETS(elm_msg_cat, MeSet, MeDecodeUseAlsoV,
					     "[ You can also use 'v' to view or save this part. ]\n"));	    
			state_nlputs("\n",state);
		    }
		}
	    }
	    state -> filter = tmp;	    
	}
    }
	
    DPRINT(Debug,40,(&Debug, 
		     "set_filter=1 filter=%p '%s' (type=%p)\n",
		     state -> filter,
		     state -> filter->MIME_name ?
		     state -> filter->MIME_name : "<none>",
		     state -> filter->charset_type));	
    return 1;    
}

/* Prototype */
static void text_decode	P_((mime_t *part, 
			    in_state_t *instream, 
			    out_state_t *outstream, 
			    charset_t defcharset, 
			    struct header_rec *hdr,
			    type_mismatch_prompt *badtype)); 

/* Returns -1 on failure otherwise same mask than mime_classify_media() */
S_(mime_run_selector text_selector)
static int text_selector(p,hdr)     
     mime_t *p;
     struct header_rec * hdr;
{
    int flags = NOTPLAIN_need_metamail;

    /* If mime_get_charsets < 0, we may want call metamail
     * (instead of filtering to US-ASCII)
     */
    int r;
    charset_t buf1;
    charset_t disp_vector[2];

    /* If charset is overrided, do not use metamail ... */
    if (hdr && hdr->override_charset) {
       	DPRINT(Debug,11,(&Debug, 
			 "text_selector(%p) -- override charset %s\n",
			 hdr->override_charset->MIME_name ?
			 hdr->override_charset->MIME_name :
			 "<no MIME name>"));
	flags = 0;
	goto done; 
    }


    disp_vector[0] = display_charset;
    disp_vector[1] = NULL;
	
    if ((r=mime_get_charset(&buf1,p->TYPE_opts,disp_vector,NULL)) > 0) {
	DPRINT(Debug,11,(&Debug, 
			"text_selector(%p) -- type: %p=%s/%s; charset=%s\n",
			p,
			p->TYPE,
			get_major_type_name(p->TYPE), 
			get_subtype_name(p->TYPE),
			buf1->MIME_name ? buf1->MIME_name : 
			"<no MIME name>"));
	flags = 0;
	goto done;
    } 
    if (r < 0 && buf1 == system_charset) {
	DPRINT(Debug,11,(&Debug, 
			"text_selector(%p) -- type: %p=%s/%s; charset=%s -- is system charset ...\n",
			p,
			p->TYPE,
			get_major_type_name(p->TYPE), 
			get_subtype_name(p->TYPE),
			buf1->MIME_name ? buf1->MIME_name : "<no MIME name>"));
	flags = 0;
	goto done;
    }
    
    if (r == -3 && charset_convert_ok) {
	DPRINT(Debug,11,(&Debug, 
			"text_selector(%p) -- type: %p=%s/%s; charset=%s -- charset_convert_ok=TRUE\n",
			p,
			p->TYPE,
			get_major_type_name(p->TYPE), 
			get_subtype_name(p->TYPE),
			buf1->MIME_name ? buf1->MIME_name : "<no MIME name>"));
	flags = 0;
	goto done;
    } 

    if (r < 0 && page_known_charsets) {
	DPRINT(Debug,11,(&Debug, 
			"text_selector(%p) -- type: %p=%s/%s; charset=%s -- page_known_charsets=TRUE\n",
			p,
			p->TYPE,
			get_major_type_name(p->TYPE), 
			get_subtype_name(p->TYPE),
			buf1->MIME_name ? buf1->MIME_name : "<no MIME name>"));
	flags = 0;
	goto done;
    }

    if (-3 == r)                       /* Is fallback -- is conversion */
	flags = NOTPLAIN_is_fallback;

 done:
    DPRINT(Debug,11,(&Debug, 
		     "text_selector(%p) = %d\n",
		     p,
		     flags));

    return flags;
}

static void text_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;
{
    DPRINT(Debug,11,(&Debug, 			
		     "text_decode -> state: offset=%ld, length=%ld \n",
		     (long) ptr -> offset, (long) ptr -> length));

  if (ptr->magic != MIME_magic)
    mime_panic(__FILE__,__LINE__,"text_decode",
	       "Bad magic number");

  if (in_state_seekable(state_in)) {
      DPRINT(Debug,11,(&Debug, 
		       "              (file): ftell=%ld\n",
		       in_state_ftell(state_in))); 
  }

  if (set_filter(ptr,state_out,default_mimetext_charset,
		 mss ? mss->override_charset : NULL)) {
    if (!run_cte_decoder(ptr,state_in, state_out)) {
      state_printf(state_out,
		   CATGETS(elm_msg_cat, MeSet, MeDecodeEncodingSkipping,
			   "[ Unsupported encoding, skipping... ]\n"));
      if (state_out->displaying)	
	  state_printf(state_out,
		       CATGETS(elm_msg_cat, MeSet, MeDecodeUseVEncodedSave,
			       "[ Use 'v' to save this part in encoded form. ]\n"));
    }
  }

  state_out -> filter = NULL;

  DPRINT(Debug,11,(&Debug, 
		   "text_decode <- END; \n"));

  if (in_state_seekable(state_in)) {
      DPRINT(Debug,11,(&Debug, 
		       "          (file); ftell=%ld\n",
		       in_state_ftell(state_in))); 
  }
}

FILE * arrange_binary(ptr,state_in,state_out,newstate2, name)
     mime_t *ptr;
     in_state_t  *state_in;
     out_state_t *state_out;
     in_state_t *newstate2;
     char **name;
{
    FILE * tmpfp = NULL;
    char *fname = NULL;
    char *tmp;

    DPRINT(Debug,11,(&Debug, 
		     "arrange_binary -> state: offset=%ld, length=%ld \n",
		     (long) ptr -> offset, (long) ptr -> length));
    
    if (ptr->magic != MIME_magic)
	mime_panic(__FILE__,__LINE__,"arrange_binary",
		   "Bad magic number");
    
    if (in_state_seekable(state_in)) {
	DPRINT(Debug,11,(&Debug, 
			 "                 (file): ftell=%ld\n",
			 in_state_ftell(state_in))); 
    }
  
    tmp = give_dt_estr_as_str(&temp_dir_e,"tmpdir");
    if (!tmp)
	return NULL;

    fname = elm_message(FRM("%selmdecode-%d"), 
			tmp, getpid ());
    if (NULL == (tmpfp = safeopen_rdwr(fname))) {
	/* state_nlputs or state_printf is needed for EOLN_is_CRLF
	   conversions */

	lib_error(CATGETS(elm_msg_cat, MeSet, MeDecodeFailedCreate,
			  "Failed to create file for decoding."));
	if (state_out) {
	    state_puts("[ ",state_out);
	    state_printf(state_out,
			 CATGETS(elm_msg_cat, MeSet, MeDecodeFailedCreate,
				 "Failed to create file for decoding."));
	    state_nlputs(" ]\n",state_out);
	}
    } else { /* Tempfile opened */
	int c = EOF, prev = EOF;
	int is_binary = ptr->encoding == ENCODING_BINARY;
	long pos;

	if (name)
	    *name = fname;
	else
	    unlink(fname);  /* We can unlink it now ... */

	DPRINT(Debug,11,(&Debug, 
			 "                 : is_binary = %d\n",is_binary));

	if (in_state_fseek(state_in,ptr->begin_offset) != 0) {
	    lib_error(CATGETS(elm_msg_cat, MeSet, MeDecodeArrangeSeekFail,
			      "arrange_binary: seek failed"));
	    return NULL;
	}

	while ((pos = in_state_ftell(state_in)) < 
	       ptr -> offset + ptr ->length &&
	       EOF != (c = state_getc(state_in))) {

	    if (pos < ptr -> offset) {   /*  --- HEADER PART -- */
		if ('\n' == c && '\r' != prev) {
		    if (is_binary) {
			DPRINT(Debug,11,(&Debug, 
					 "arrange_binary -- (header) not really binary input!\n")); 
			is_binary = 0;
		    }
		    fputc('\r',tmpfp);
		}		
	    } else if (!is_binary && '\n' == c && '\r' != prev) {
		fputc('\r',tmpfp);
	    }
	    fputc(c,tmpfp);
	    prev = c;
	}
	rewind(tmpfp); /* Rewind it for reading */	    
	set_in_state_file(tmpfp,newstate2);
    }

    if (!name)
	free(fname);
    return tmpfp;
}

FILE * arrange_decoded(ptr,state_in,state_out,newstate2,name)
     mime_t *ptr;
     in_state_t  *state_in;
     out_state_t *state_out;
     in_state_t *newstate2;
     char **name;
{
    FILE * tmpfp = NULL;
    int result = 1;
    char *fname = NULL;
    out_state_t newstate;
    char *tmp;

    out_state_clear(&newstate,STATE_out_file);
    
    DPRINT(Debug,11,(&Debug, 
		     "arrange_decoded -> state: offset=%ld, length=%ld \n",
		     (long) ptr -> offset, (long) ptr -> length));
    
    if (ptr->magic != MIME_magic)
	mime_panic(__FILE__,__LINE__,"arrange_decoded",
		   "Bad magic number");
    
    if (in_state_seekable(state_in)) {
	DPRINT(Debug,11,(&Debug, 
			 "                 (file): ftell=%ld\n",
			 in_state_ftell(state_in))); 
    }
  
    tmp = give_dt_estr_as_str(&temp_dir_e,"tmpdir");
    if (!tmp)
	return NULL;

    fname = elm_message(FRM("%selmdecode.%d"), 
			tmp, getpid ());
    if (NULL == (tmpfp = safeopen_rdwr(fname))) {
	/* state_nlputs or state_printf is needed for EOLN_is_CRLF
	   conversions */
	
	
	lib_error(CATGETS(elm_msg_cat, MeSet, MeDecodeFailedCreate,
			  "Failed to create file for decoding."));

	if (state_out) {
	    state_puts("[ ",state_out);
	    state_printf(state_out,
			 CATGETS(elm_msg_cat, MeSet, MeDecodeFailedCreate,
				 "Failed to create file for decoding."));
	    state_nlputs(" ]\n",state_out);
	}
	result = 0;
    } else { /* Tempfile opened */

	if (name)
	    *name = fname;
	else
	    unlink(fname);  /* We can unlink it now ... */
    
	if (state_out)
	    newstate.displaying = state_out->displaying;
	else 
	    newstate.displaying = 0;
	
	set_out_state_file(tmpfp, &newstate);
	newstate.prefix          = NULL;             /* No prefix in that pass */
	if (state_out) {
	    newstate.filter          = state_out->filter;
	    newstate.display_charset = state_out->display_charset;
	} else {

	    static charset_t default_vector[2];
	    
	    default_vector[0] = display_charset;
	    default_vector[1] = NULL;

	    newstate.filter          = NULL;
	    newstate.display_charset = default_vector;
	}

	if (run_cte_decoder(ptr,state_in,&newstate)) { 
	    
	    if (EOF == fflush(tmpfp)) {
		/* state_nlputs or state_printf is needed for EOLN_is_CRLF
		   conversions */

		lib_error(CATGETS(elm_msg_cat, MeSet, MeDecodeErrorFlush,
				  "Error when flushing temporary file."));
		if (state_out) {
		    state_puts("[ ",state_out);
		    state_printf(state_out,
				 CATGETS(elm_msg_cat, MeSet, MeDecodeErrorFlush,
					 "Error when flushing temporary file."));
		    state_nlputs(" ]\n",state_out);
		}
		result = 0;
	    }
	    rewind(tmpfp); /* Rewind it for reading */
	    
	    set_in_state_file(tmpfp,newstate2);
	} else { /* Run decoder failed */
	    if (state_out) {
		state_printf(state_out,
			     CATGETS(elm_msg_cat, MeSet, MeDecodeEncodingSkipping,
				 "[ Unsupported encoding, skipping... ]\n"));
		if (state_out->displaying)	
		    state_printf(state_out,
				 CATGETS(elm_msg_cat, MeSet, MeDecodeUseVEncodedSave,
					 "[ Use 'v' to save this part in encoded form. ]\n"));
	    }
	    result = 0;
	}
    }
    out_state_destroy(&newstate);

    DPRINT(Debug,11,(&Debug, 
		     "arrange_decoded; result=%d\n",result));
    
    if (!result && tmpfp) {
	fclose(tmpfp);
	tmpfp = NULL;
	if (name) {
	    unlink(fname);           /* Unlink was delayed */
	    *name = NULL;
	}
	free(fname);

    } else {
	if (!name)
	    free(fname);
    }
    return tmpfp;
}

/* Prototype */
static void elm_decode	P_((mime_t *part, 
			    in_state_t *instream, 
			    out_state_t *outstream, 
			    charset_t defcharset, struct header_rec *hdr,
			    type_mismatch_prompt *badtype)); 

static void elm_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;
{  
  in_state_t newstate2;

  FILE *tmpfp;
  char buffer[LONG_STRING];
  
  in_state_clear(&newstate2,STATE_in_file);

  DPRINT(Debug,11,(&Debug, 
		   "elm_decode -> state: offset=%ld, length=%ld; \n",
		   (long) ptr -> offset, (long) ptr -> length));

  if (ptr->magic != MIME_magic)
    mime_panic(__FILE__,__LINE__,"elm_decode",
	       "Bad magic number");

  if (in_state_seekable(state_in)) {
      DPRINT(Debug,11,(&Debug, 
		       "             (file): ftell=%ld\n",
		       in_state_ftell(state_in))); 
  }

  if (NULL != (tmpfp = 
	       arrange_decoded(ptr,state_in,state_out,&newstate2,NULL))) {

      if (check_type_pattern) {
	  if (!check_type_magic(ptr,&newstate2, state_out, badtype)) {
	      DPRINT(Debug,11,(&Debug, 
			       "elm_decode: mime_type_rejected\n"));
	      goto FAILTYPE;
	  }
      }

      if (set_filter(ptr,state_out,NULL,
		     mss ? mss->override_charset : NULL)) {
	  int len, crypted = 0;
	  int count = 0;
	  int ok = getkey(OFF);
      
	  while((len = state_getl(buffer,sizeof(buffer),&newstate2)) > 0) {
	      count += len;
	      
	      if (ok > 0) {
		  if (!strncmp(buffer, START_ENCODE, strlen(START_ENCODE))) {
		      state_printf(state_out,
				   CATGETS(elm_msg_cat, MeSet, MeDecodeStartElmEncoded,
					   "-- Start of (Elm) encoded section.\n"));
		      crypted = ON;
		      continue;
		  } else if (!strncmp(buffer, END_ENCODE, strlen(END_ENCODE))) {
		      crypted = OFF;
		      state_printf(state_out,
				   CATGETS(elm_msg_cat, MeSet, MeDecodeEndElmEncoded,
					   "-- End of (Elm) encoded section.\n"));
		      continue;
		  } else if (crypted) {
		      no_ret(buffer);
		      encode(buffer);      
		      if (state_out->EOLN_is_CRLF)
			  strfcat(buffer, "\r\n", sizeof buffer);
		      else
			  strfcat(buffer, "\n", sizeof buffer);	    
		  }
	      }
	      state_add_prefix(state_out);
	      state_puts(buffer,state_out);
	  }

	  DPRINT(Debug,11,(&Debug, 
			   "elm_decode: Read %d bytes from temp file\n",
			   count));
      }

  FAILTYPE:
      fclose(tmpfp);
  }

  DPRINT(Debug,11,(&Debug, 
		   "elm_decode <- END; \n"));
  if (in_state_seekable(state_in)) {
      DPRINT(Debug,11,(&Debug, 			
		       "           (file); ftell=%ld\n",
		       in_state_ftell(state_in))); 
  }

  in_state_destroy(&newstate2);
}
 /* Prototype */
static void text_unsupported_decode P_((mime_t *part, 
					in_state_t *instream, 
					out_state_t *outsream,
					charset_t defcharset, 
					struct header_rec *hdr,
					type_mismatch_prompt *badtype));

/* Returns -1 on failure otherwise same mask than mime_classify_media() */
S_(mime_run_selector text_unsupported_selector)
static int text_unsupported_selector(p,hdr)     
     mime_t *p;
     struct header_rec * hdr;
{
    int flags = NOTPLAIN_is_fallback;

    /* Notice: if (decoder == text_unsupported_decode)
     *         we return TRUE, because we want call metamail
     *         if it is available
     */

    DPRINT(Debug,11,(&Debug, 
		     "text_unsupported_selector(%p)=%d\n",
		     p,flags));


    return flags;
}

static void text_unsupported_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;
{
    DPRINT(Debug,11,(&Debug, 
		     "text_unsupported_decode -> state: offset=%ld, length=%ld; \n",
		     (long) ptr -> offset, (long) ptr -> length));

    if (ptr->magic != MIME_magic)
	mime_panic(__FILE__,__LINE__,"text_unsupported_decode",
		   "Bad magic number");

    if (in_state_seekable(state_in)) {
	DPRINT(Debug,11,(&Debug, 
			 "                          (file): ftell=%ld\n",
			 in_state_ftell(state_in))); 
    }

    /* state_nlputs or state_printf is needed for EOLN_is_CRLF
       conversions */

    state_printf(state_out,
		 CATGETS(elm_msg_cat, MeSet, MeDecodeUnsupportedText,
			 "[ %s/%.30s is unsupported, treating like TEXT/PLAIN ]\n"), 
		 get_major_type_name(ptr->TYPE), 
		 get_subtype_name(ptr->TYPE));
    state_nlputs("\n",state_out);
    text_decode (ptr, state_in, state_out, defcharset, mss, badtype);

    DPRINT(Debug,11,(&Debug, 
		     "text_unsupported_decode <- END; \n"));
    if (in_state_seekable(state_in)) {
	DPRINT(Debug,11,(&Debug, 
			 "                        (file); ftell=%ld\n",
			 in_state_ftell(state_in))); 
    }
}

void null_decode (ptr, state_in, state_out, defcharset, mss) 
     mime_t *ptr;
     in_state_t *state_in;
     out_state_t *state_out;
     charset_t defcharset;
     struct header_rec *mss;
{
    DPRINT(Debug,11,(&Debug, 
		     "null_decode <-> state: offset=%ld, length=%ld; \n",
		     (long) ptr -> offset, (long) ptr -> length));
    
    if (ptr->magic != MIME_magic)
	mime_panic(__FILE__,__LINE__,"null_decode",
		   "Bad magic number");
    
    if (in_state_seekable(state_in)) {
	DPRINT(Debug,11,(&Debug, 
			 "               (file): ftell=%ld\n",
			 in_state_ftell(state_in))); 
    }

    state_printf (state_out,
		  CATGETS(elm_msg_cat, MeSet, MeDecodeUnsupportedSkip,
			  "[ %.15s/%.30s is not supported, 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"));
}


#if __GNUC__ 
#define MTH        struct media_type_handle          
#define PAGER(A)   handle_pager, { pager: & A }

#define REGISTER_MT_HANDLER(A,B,R) \
   register_mt_handler(give_media_type2(A,B,1),& R)
#define REGISTER_MT_DEFHANDLER(A,R) \
   register_mt_defhandler(A,& R)

#else
#define MTH        struct COMPAT_media_type_handle
#define PAGER(A)   handle_pager, (void *) & A

#define REGISTER_MT_HANDLER(A,B,R) \
   register_mt_handler(give_media_type2(A,B,1),(struct media_type_handle *)& R)
#define REGISTER_MT_DEFHANDLER(A,R) \
   register_mt_defhandler(A,(struct media_type_handle *)& R)

#endif

static struct  mt_handle_pager alternative_1 = { 
    alternative_decode, alternative_selector }; 
static MTH                     alternative_2 = { PAGER(alternative_1) };
static struct  mt_handle_pager signed_1      = { 
    signed_decode, signed_selector }; 
static MTH                     signed_2      = { PAGER(signed_1) };
static struct  mt_handle_pager encrypted_1   = { 
    encrypted_decode, encrypted_selector }; 	

static MTH                     encrypted_2   = { PAGER(encrypted_1) };

static struct  mt_handle_pager multipart_0_1 = { 
    multipart_0_decode, multipart_selector }; 
static MTH                     multipart_0_2 = { PAGER(multipart_0_1) };

static struct  mt_handle_pager multipart_mixed_1 = { 
    multipart_mixed_decode, multipart_selector }; 
static MTH                 multipart_mixed_2 = { PAGER(multipart_mixed_1) };

static struct  mt_handle_pager rfc822_1      = { 
    rfc822_decode, rfc822_selector }; 
static MTH                     rfc822_2      = { PAGER(rfc822_1) };
static struct  mt_handle_pager text_1        = { 
    text_decode, text_selector }; 
static MTH                     text_2        = { PAGER(text_1) };
#ifdef USE_PGP
static struct  mt_handle_pager pgp_1         = { 
    pgp_decode, pgp_selector }; 
static MTH                     pgp_2         = { PAGER(pgp_1) };
#endif
static struct  mt_handle_pager text_unsupported_1 = { 
    text_unsupported_decode, text_unsupported_selector 
}; 
static MTH                     text_unsupported_2 = { 
    PAGER(text_unsupported_1) };
static struct  mt_handle_pager elm_1          = { 
    elm_decode, text_selector }; 
static MTH                     elm_2          = { PAGER(elm_1) };

static struct  mt_handle_pager partial_1          = { 
    partial_decode, partial_selector }; 
static MTH                     partial_2          = { PAGER(partial_1) };


static int melib_register_decoders_called = 0;

void melib_register_decoders()
{
    if (melib_register_decoders_called)
	return;

    /* multipart types */
    REGISTER_MT_HANDLER(MIME_TYPE_MULTIPART,"alternative",alternative_2);
    REGISTER_MT_HANDLER(MIME_TYPE_MULTIPART,"signed",     signed_2);
    REGISTER_MT_HANDLER(MIME_TYPE_MULTIPART,"encrypted",  encrypted_2);
    REGISTER_MT_HANDLER(MIME_TYPE_MULTIPART,"mixed",      multipart_mixed_2);
    REGISTER_MT_DEFHANDLER(MIME_TYPE_MULTIPART,           multipart_0_2);

    /* message types */
    REGISTER_MT_HANDLER(MIME_TYPE_MESSAGE,"rfc822",       rfc822_2);
    REGISTER_MT_HANDLER(MIME_TYPE_MESSAGE,"delivery-status", text_2);
    REGISTER_MT_HANDLER(MIME_TYPE_MESSAGE,"partial",      partial_2);
    REGISTER_MT_HANDLER(MIME_TYPE_MESSAGE,"disposition-notification", text_2);

    /* text types */
    REGISTER_MT_HANDLER(MIME_TYPE_TEXT,"plain",           text_2);
    REGISTER_MT_HANDLER(MIME_TYPE_TEXT,"rfc822-headers",  text_2);
#ifdef USE_PGP
    REGISTER_MT_HANDLER(MIME_TYPE_TEXT,"x-pgp",           pgp_2);
#endif
    REGISTER_MT_DEFHANDLER(MIME_TYPE_TEXT,                text_unsupported_2);

    /* application types */
#ifdef USE_PGP
    REGISTER_MT_HANDLER(MIME_TYPE_APPLICATION,"pgp",      pgp_2);
    REGISTER_MT_HANDLER(MIME_TYPE_APPLICATION,"x-pgp",    pgp_2);
#endif
    REGISTER_MT_HANDLER(MIME_TYPE_APPLICATION,"X-ELM-encode", elm_2);

#if 0         /* TODO: is this needed? */
    /* Unknown leaf types */
    REGISTER_MT_DEFHANDLER(MIME_TYPE_LEAF,           XXXX);
#endif

    melib_register_decoders_called++;
}

int is_rfc1522 (s)
     char *s;
{
  /* Returns TRUE if the string contains RFC 1522 format encoded data */

  while ((s = strchr (s, '=')) != NULL) {
    s++;
    if (*s == '?')
      return TRUE;
  }
  return FALSE;
}

/*
 * Local Variables:
 *  mode:c
 *  c-basic-offset:4
 *  buffer-file-coding-system: iso-8859-1
 * End:
 */


syntax highlighted by Code2HTML, v. 0.9.1