/* 
   elmo - Electronic Mail Operator

   Copyright (C) 2003, 2004 rzyjontko

   This program is free software; you can redistribute it and/or modify
   it under the terms of the GNU General Public License as published by
   the Free Software Foundation; version 2.

   This program is distributed in the hope that it will be useful,
   but WITHOUT ANY WARRANTY; without even the implied warranty of
   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
   GNU General Public License for more details.

   You should have received a copy of the GNU General Public License
   along with this program; if not, write to the Free Software Foundation,
   Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.  

   ----------------------------------------------------------------------

*/
/****************************************************************************
 *    IMPLEMENTATION HEADERS
 ****************************************************************************/

#ifdef HAVE_CONFIG_H
# include <config.h>
#endif

#include <string.h>
#include <stdlib.h>
#include <sys/types.h>
#include <regex.h>
#include <errno.h>

#ifdef HAVE_ICONV
# include <iconv.h>
#endif

#include "mime.h"
#include "xmalloc.h"
#include "file.h"
#include "memchunk.h"
#include "ask.h"
#include "error.h"
#include "str.h"
#include "misc.h"
#include "gettext.h"
#include "debug.h"
#include "rmime.h"
#include "pgp.h"

/****************************************************************************
 *    IMPLEMENTATION PRIVATE DEFINITIONS / ENUMERATIONS / SIMPLE TYPEDEFS
 ****************************************************************************/

#define isupper(a)  ((a) >= 'A' && (a) <= 'Z')
#define islower(a)  ((a) >= 'a' && (a) <= 'z')
#define isdigit(a)  ((a) >= '0' && (a) <= '9')
#define isxdigit(a) (isdigit (a) || ((a) >= 'A' && (a) <= 'F') \
                     || ((a) >= 'a' && (a) <= 'f'))
#define isbase64(a) (isupper (a) || islower (a) || isdigit (a) || (a) == '+' \
                     || (a) == '/' || (a) == '=')

#define BASE64_VAL(a) (isupper (a) ? ((a) - 'A') \
                       : (islower (a) ? ((a) - 'a' + 26) \
                          : (isdigit (a) ? ((a) - '0' + 52) \
                             : ((a) == '+' ? 62 \
                                : ((a) == '/' ? 63 : 0)))))

#define BASE64_SIGN(a) (((a) < 26) ? ('A' + (a)) \
                        : (((a) < 52) ? ('a' + (a) - 26) \
                           : (((a) < 62) ? ('0' + (a) - 52) \
                              : (((a) == 62) ? '+' \
                                 : (((a) == 63) ? '/' : 0)))))
                        
#define DECRYPTED(m) (((m)->decrypted) ? ((m)->decrypted) : ((m)->mime))

/****************************************************************************
 *    IMPLEMENTATION PRIVATE CLASS PROTOTYPES / EXTERNAL CLASS REFERENCES
 ****************************************************************************/
/****************************************************************************
 *    IMPLEMENTATION PRIVATE STRUCTURES / UTILITY CLASSES
 ****************************************************************************/

struct pair_list {
        struct pair_list *next;
        char             *from;
        char             *to;
};


struct rfc2047 {
        int         special_;   /* 1 if _ means a space */
        char       *charset;    /* charset used in first occurence */
        char       *str;        /* header string */
        str_t      *inter;      /* intermediate buffer */
        str_t      *result;     /* resulting buffer */
        regmatch_t  matches[5]; /* 0 - all, 2 - charset,
                                   3 - Q/B, 4 - content */
};


struct mime_handler {
        struct mime_handler *next;
        char                *type;
        char                *handler;
};


struct mime_type {
        struct mime_type *next;
        char             *type;
        regex_t           re;
};

/****************************************************************************
 *    IMPLEMENTATION REQUIRED EXTERNAL REFERENCES (AVOID)
 ****************************************************************************/
/****************************************************************************
 *    IMPLEMENTATION PRIVATE DATA
 ****************************************************************************/

/* These are used to find occurences of something like: =?...?Q?...?= */
static int     encoded_compiled = 0;
static regex_t encoded_string_re;

/* These are used to find mime types like "text/plain; charset=iso-8859-2" */
static int     mime_type_compiled = 0;
static regex_t mime_type_re;

static int     mime_charset_compiled = 0;
static regex_t mime_charset_re;

/* List of charset conversions */
static struct pair_list *conversions = NULL;

static struct pair_list *prepared = NULL;

/* List of MIME handlers. */
static struct mime_handler *handlers = NULL;

/* List of MIME types. */
static struct mime_type *types = NULL;

/****************************************************************************
 *    INTERFACE DATA
 ****************************************************************************/
/****************************************************************************
 *    IMPLEMENTATION PRIVATE FUNCTION PROTOTYPES
 ****************************************************************************/
/****************************************************************************
 *    QP / BASE64 FUNCTIONS (RFC 2045)
 ****************************************************************************/

/**
 * to -- inclusive
 */
static void
decode_qp (str_t *dest, char *from, char *to, int special_)
{
        char buf[3];

        buf[2] = '\0';
  
        while (*from && from <= to){

                switch (*from){

                        case '_':
                                if (special_){
                                        str_put_char (dest, ' ');
                                }
                                else {
                                        str_put_char (dest, '_');
                                }
                                from++;
                                break;
      
                        case '=':
                                if (isxdigit (from[1]) && isxdigit (from[2])){
                                        buf[0] = from[1];
                                        buf[1] = from[2];
                                        str_put_char (dest,
                                                      strtol (buf, NULL, 16));
                                        from += 3;
                                }
                                else if (from[1] == '\n'){
                                        from += 2;
                                }
                                else {
                                        from += 3;
                                }
                                break;

                        default:
                                str_put_char (dest, *from);
                                from++;
                                break;
                }
        }
}



static void
decode_base64 (str_t *dest, char *from, char *to)
{
        while (from <= to && ! isbase64 (*to))
                to--;
        
        while (*from && from <= to){
                while (*from == '\n' || *from == '\r')
                        from++;
                str_put_char (dest, (BASE64_VAL (from[0]) << 2)
                                  | (BASE64_VAL (from[1]) >> 4));

                if (from[2] == '=')
                        break;
                str_put_char (dest, (BASE64_VAL (from[1]) << 4)
                                  | (BASE64_VAL (from[2]) >> 2));

                if (from[3] == '=')
                        break;
                str_put_char (dest, (BASE64_VAL (from[2]) << 6)
                                  | (BASE64_VAL (from[3]) >> 0));
                from += 4;
        }
}



static void
encode_base64 (str_t *dest, unsigned char *from, unsigned char *to)
{
        int line_len = 0;

        while (from <= to){

                str_put_char (dest, BASE64_SIGN (0x3f & (from[0] >> 2)));

                if (from + 1 > to){
                        str_put_char (dest, BASE64_SIGN (0x3f & (from[0] << 4)));
                        str_put_nchar (dest, '=', 2);
                        break;
                }
                str_put_char (dest, BASE64_SIGN (0x3f & ((from[0] << 4)
                                                         | (from[1] >> 4))));

                if (from + 2 > to){
                        str_put_char (dest, BASE64_SIGN (0x3f & (from[1] << 2)));
                        str_put_char (dest, '=');
                        break;
                }
                str_put_char (dest, BASE64_SIGN (0x3f & ((from[1] << 2)
                                                         | (from[2] >> 6))));
                str_put_char (dest, BASE64_SIGN (0x3f &  (from[2])));

                if (line_len == 76){
                        str_put_char (dest, '\n');
                        line_len = 0;
                }
                else {
                        line_len += 4;
                }
                from += 3;
        }
}

/****************************************************************************
 *    LIST FUNCTIONS
 ****************************************************************************/

static void
list_destroy (struct pair_list *list)
{
        if (list == NULL)
                return;

        list_destroy (list->next);

        if (list->from)
                xfree (list->from);
        if (list->to)
                xfree (list->to);
        xfree (list);
}


static struct pair_list *
list_find (struct pair_list *list, char *from)
{
        if (list == NULL)
                return NULL;

        if (strcasecmp (list->from, from) == 0)
                return list;

        return list_find (list->next, from);
}


static void
destroy_handler (struct mime_handler *list)
{
        if (list == NULL)
                return;

        destroy_handler (list->next);

        if (list->type)
                xfree (list->type);
        if (list->handler)
                xfree (list->handler);
        xfree (list);
}


static void
destroy_type (struct mime_type *list)
{
        if (list == NULL)
                return;

        destroy_type (list->next);

        if (list->type)
                xfree (list->type);
        
        regfree (& list->re);
        xfree (list);
}


static struct mime_type *
find_type (struct mime_type *list, const char *fname)
{
        int ret;
        
        if (list == NULL)
                return NULL;

        ret = regexec (& list->re, fname, 0, NULL, 0);

        if (ret && ret != REG_NOMATCH){
                error_regex (ret, & list->re, NULL);
                return NULL;
        }
        else if (ret)
                return find_type (list->next, fname);

        return list;
}



static int
simple_match (const char *pattern, const char *type)
{
        if (strcmp (pattern, "*") == 0)
                return 1;

        return strcasecmp (pattern, type) == 0;
}


static int
type_match (const char *pattern, const char *type)
{
        char *p_slash = strchr (pattern, '/');
        char *t_slash = strchr (type, '/');
        int   ret;

        if (p_slash == NULL)
                return simple_match (pattern, type);

        if (t_slash == NULL)
                return 0;

        *p_slash = '\0';
        *t_slash = '\0';

        ret      = simple_match (pattern, type);

        *p_slash = '/';
        *t_slash = '/';

        if (ret == 0)
                return 0;

        return simple_match (p_slash + 1, t_slash + 1);
}



static struct mime_handler *
find_mime (struct mime_handler *list, const char *type)
{
        if (list == NULL)
                return NULL;

        if (type_match (list->type, type))
                return list;
        
        return find_mime (list->next, type);
}


/****************************************************************************
 *    IMPLEMENTATION PRIVATE FUNCTIONS
 ****************************************************************************/


static int
is_text_trivial (mime_t *mime)
{
        if (mime == NULL)
                return 0;

        if (mime->type == NULL)
                return 1;

        return 0;
}


static int
is_text (mime_t *mime)
{
        char *handler;
        
        if (is_text_trivial (mime))
                return 1;

        handler = mime_get_handler (mime);

        if (handler == NULL)
                return 0;

        if (strcmp (handler, "elmo") == 0)
                return 1;

        return 0;
}



static int
find_encoded_string (struct rfc2047 *x)
{
        int ret;

        if (! encoded_compiled)
                return 0;

        ret = regexec (& encoded_string_re, x->str, 5, x->matches, 0);

        if (ret && ret != REG_NOMATCH){
                error_regex (ret, & encoded_string_re, NULL);
                return 0;
        }

        return ! ret;
}



static void
save_charset (struct rfc2047 *s)
{
        int len = s->matches[2].rm_eo - s->matches[2].rm_so;
        
        s->charset = xmalloc (len + 1);

        memcpy (s->charset, s->str + s->matches[2].rm_so, len);
        s->charset[len] = '\0';
}



static void
get_intermediate (struct rfc2047 *s)
{
        char *from;
        char *to;
        
        if (s->inter)
                str_clear (s->inter);
        else
                s->inter = str_create ();

        str_put_string_len (s->inter, s->str, s->matches[0].rm_so);

        while (1) {

                from = s->str + s->matches[4].rm_so;
                to   = s->str + s->matches[4].rm_eo - 1;
                
                switch (s->str[s->matches[3].rm_so]){

                        case 'q':
                        case 'Q':
                                decode_qp (s->inter, from, to, s->special_);
                                break;

                        case 'b':
                        case 'B':
                                decode_base64 (s->inter, from, to);
                                break;
                }

                s->str += s->matches[0].rm_eo;
                if (! find_encoded_string (s))
                        break;

                str_put_string_len (s->inter, s->str, s->matches[0].rm_so);
        }

        str_put_string (s->inter, s->str);
}


#ifdef HAVE_ICONV
static void
translate (struct rfc2047 *s, char *to)
{
        int      ret;
        iconv_t  id = iconv_open (to, s->charset);
        char    *inptr, *outptr, *result;
        int      insize, outsize, ressize;
        int      bad_chars = 0;

        if (id == (iconv_t) -1){
                if (errno == EINVAL)
                        error_ (0,
                                _("conversion from %s to %s not available"),
                                s->charset, to);
                else
                        error_ (errno, _("couldn't convert from %s to %s"),
                                s->charset, to);
                return;
        }

        inptr   = s->inter->str;
        insize  = s->inter->len;

        result  = xmalloc (insize + 1);
        outptr  = result;
        outsize = ressize = insize;


        while (insize > 0){
                ret = iconv (id, & inptr, & insize, & outptr, & outsize);
                if (ret == (size_t) -1 && errno != E2BIG){
                        bad_chars++;
                        inptr++;
                        insize--;
                }
                if (errno == E2BIG){
                        result   = xrealloc (result, ressize * 2);
                        outptr  += ressize - outsize;
                        outsize += ressize;
                        ressize *= 2;
                }
        }

        if (bad_chars){
                error_ (0, _("some characters were not converted from %s "
                             "to %s due to invalid multibyte sequence"),
                        s->charset, to);
        }

        *outptr   = '\0';
        s->result = str_dup (result);

        xfree (result);
        iconv_close (id);
}
#endif



static void
get_result (struct rfc2047 *s)
{
        struct pair_list *list = list_find (conversions, s->charset);

        if (list == NULL)
                return;

#ifdef HAVE_ICONV
        translate (s, list->to);
#else
        error_ (0, _("conversions are unavailable on your system"));
#endif
}



static char *
get_file_name (char *header)
{
        char *seek = strstr (header, "name=");
        char *start;
        char *result;

        if (seek == NULL)
                seek = strstr (header, "NAME=");
  
        if (seek == NULL)
                return NULL;

        start = seek + 5;
        if (*start == '"')
                start++;

        seek = start;
        while (*seek != '"' && *seek != '\r' && *seek != '\n' && *seek
               && *seek != ';'){
                seek++;
        }
        result = xmalloc (seek - start + 1);
        result[seek - start] = '\0';
        memcpy (result, start, seek - start);

        return mime_decode_header (result, seek - start, 1);
}



static void
get_type (mime_t *mime, char *str)
{
        int         ret;
        int         len;
        regmatch_t  matches[2];

        if (! mime_type_compiled)
                return;

        ret = regexec (& mime_type_re, str, 2, matches, 0);

        if (ret && ret != REG_NOMATCH){
                error_regex (ret, & mime_type_re, NULL);
                return;
        }

        if (ret == REG_NOMATCH){
                error_ (0, _("invalid mime type '%s'"), str);
                return;
        }
                
        len        = matches[0].rm_eo - matches[0].rm_so;
        mime->type = xmalloc (len + 1);
        memcpy (mime->type, str + matches[0].rm_so, len);
        mime->type[len] = '\0';

        if (! mime_charset_compiled)
                return;
        
        ret = regexec (& mime_charset_re, str, 2, matches, 0);

        if (ret && ret != REG_NOMATCH){
                error_regex (ret, & mime_charset_re, NULL);
                return;
        }

        if (ret == REG_NOMATCH || matches[1].rm_so < 0)
                return;
        
        len           = matches[1].rm_eo - matches[1].rm_so;
        mime->charset = xmalloc (len + 1);
        memcpy (mime->charset, str + matches[1].rm_so, len);
        mime->charset[len] = '\0';
}



static void
destroy_s (struct rfc2047 *s)
{
        if (s->inter && s->result){
                str_destroy (s->inter);
        }
        else if (s->inter){
                s->result = s->inter;
        }

        if (s->charset)
                xfree (s->charset);
}



static void
determine_type (mime_t *mime)
{
        struct mime_type *type = find_type (types, mime->file_name);

        if (type == NULL){
                mime->type     = xstrdup ("application/octet-stream");
                mime->encoding = MENC_BASE64;
        }
        else {
                mime->type = xstrdup (type->type);
                if (is_text (mime))
                        mime->encoding = MENC_NONE;
                else
                        mime->encoding = MENC_BASE64;
        }
}



static void
register_new_type (char *type, char *re)
{
        int ret;
        struct mime_type *new;

        new       = xmalloc (sizeof (struct mime_type));
        new->type = type;

        ret       = regcomp (& new->re, re,
                             REG_ICASE | REG_EXTENDED | REG_NEWLINE);
        
        if (ret){
                error_regex (ret, & new->re, re);
                regfree (& new->re);
                xfree (type);
                xfree (re);
                return;
        }

        new->next = types;
        types     = new;
}



static void
register_new_handler (char *type, char *handler)
{
        struct mime_handler *new;
        
        new          = xmalloc (sizeof (struct mime_handler));
        new->type    = type;
        new->handler = handler;

        new->next = handlers;
        handlers  = new;
}


static int
leaf_count (mime_t *mime)
{
        int i;
        int result = 0;
        
        if (mime == NULL)
                return 0;

        if (mime->parts == NULL)
                return 1;
        
        for (i = 0; i < mime->parts->count; i++){
                result += leaf_count (mime->parts->array[i]);
        }
        return result;
}


static mime_t *
first_leaf (mime_t *mime)
{
        if (mime == NULL)
                return NULL;

        if (mime->parts == NULL)
                return mime;

        return first_leaf (mime->parts->array[0]);
}


static mime_t *
last_leaf (mime_t *mime)
{
        if (mime == NULL)
                return NULL;

        if (mime->parts == NULL)
                return mime;

        return last_leaf (mime->parts->array[mime->parts->count - 1]);
}



static mime_t *
nth_leaf (mime_t *mime, int *index)
{
        int      i;
        mime_t  *result;
        rmime_t *parts;
        
        if (mime == NULL)
                return NULL;

        if (*index == 0)
                return first_leaf (mime);

        parts = mime->parts;

        if (parts == NULL){
                (*index)--;
                return NULL;
        }

        for (i = 0; i < parts->count; i++){
                result = nth_leaf (parts->array[i], index);
                if (result != NULL)
                        return result;
        }
        return NULL;
}


static mime_t *
first_text (mime_t *mime)
{
        int     i;
        mime_t *result;
        
        if (is_text (mime))
                return mime;

        if (mime->parts == NULL)
                return NULL;
        
        for (i = 0; i < mime->parts->count; i++){
                result = first_text (mime->parts->array[i]);
                if (result != NULL)
                        return result;
        }

        return NULL;
}



static int
has_attachment (mime_t *mime)
{
        int i;
        
        if (mime == NULL)
                return 0;

        if (mime->file_name != NULL)
                return 1;

        if (mime->parts == NULL)
                return 0;

        for (i = 0; i < mime->parts->count; i++){
                if (has_attachment (mime->parts->array[i]))
                        return 1;
        }
        return 0;
}

/****************************************************************************
 *    INTERFACE FUNCTIONS
 ****************************************************************************/


void
mime_init (void)
{
        int ret;
        
        ret = regcomp (& encoded_string_re,
                       "(\n[ \t])?=\\?([^?]+)\\?([QB])\\?([^?]+)\\?=",
                       REG_ICASE | REG_EXTENDED | REG_NEWLINE);

        if (ret){
                encoded_compiled = 0;
                error_regex (ret, & encoded_string_re, "internal re");
                regfree (& encoded_string_re);
        }
        else {
                encoded_compiled = 1;
        }

        ret = regcomp (& mime_type_re,
                       "[a-z\\-]+(/[a-z0-9\\.\\-]+)?",
                       REG_ICASE | REG_EXTENDED | REG_NEWLINE);

        if (ret){
                mime_type_compiled = 0;
                error_regex (ret, & mime_type_re, "internal re");
                regfree (& mime_type_re);
        }
        else {
                mime_type_compiled = 1;
        }

        ret = regcomp (& mime_charset_re,
                       "charset=\"?([a-z0-9\\-]+)\"?",
                       REG_ICASE | REG_EXTENDED | REG_NEWLINE);

        if (ret){
                mime_charset_compiled = 1;
                error_regex (ret, & mime_charset_re, "internal re");
                regfree (& mime_charset_re);
        }
        else {
                mime_charset_compiled = 1;
        }
}



void
mime_post_setup (void)
{
        register_new_handler (xstrdup ("text/plain"), xstrdup ("elmo"));
        register_new_handler (xstrdup ("message/rfc822"), xstrdup ("elmo"));
        register_new_handler (xstrdup ("text/html"), xstrdup ("elmo"));
        register_new_handler (xstrdup ("multipart/encrypted"),
                              xstrdup ("elmo-pgp"));
        register_new_handler (xstrdup ("multipart/signed"),
                              xstrdup ("elmo-pgp"));

        register_new_type (xstrdup ("text/plain"),
                           "(\\.txt$)|(README)|(\\.diff$)|(\\.asc$)"
                           "|(\\.[ch]$)|(\\.cpp$)|(\\.cc$)|(\\.java$)"
                           "|(\\.sh$)|(\\.tex$)|(\\.pl$)|(\\.po$)"
                           "|(\\.dsc$)");
        register_new_type (xstrdup ("text/html"), "\\.[dsx]?html?$");
        register_new_type (xstrdup ("text/rtf"), "\\.rtf$");
        register_new_type (xstrdup ("text/xml"), "\\.x[ms]l$");
        register_new_type (xstrdup ("text/css"), "\\.css$");
        register_new_type (xstrdup ("image/gif"), "\\.gif$");
        register_new_type (xstrdup ("image/jpeg"), "\\.jpe?g?$");
        register_new_type (xstrdup ("image/png"), "\\.png$");
        register_new_type (xstrdup ("image/bmp"), "\\.bmp$");
        register_new_type (xstrdup ("image/pcx"), "\\.pcx$");
        register_new_type (xstrdup ("image/tiff"), "\\.tiff?$");
        register_new_type (xstrdup ("audio/mpeg"),
                           "(\\.mp[23]$)|(\\.mpe?ga$)");
        register_new_type (xstrdup ("audio/midi"), "\\.midi?$");
        register_new_type (xstrdup ("video/mpeg"), "\\.mp[eg]g?$");
}



void
mime_free_resources (void)
{
        if (encoded_compiled)
                regfree (& encoded_string_re);
        encoded_compiled = 0;

        if (mime_type_compiled)
                regfree (& mime_type_re);
        mime_type_compiled = 0;

        if (mime_charset_compiled)
                regfree (& mime_charset_re);
        mime_charset_compiled = 0;

        list_destroy (conversions);
        conversions = NULL;

        list_destroy (prepared);
        prepared = NULL;

        destroy_handler (handlers);
        handlers = NULL;

        destroy_type (types);
        types = NULL;
}



void
mime_add_charset (char *str)
{
        if (prepared){
                prepared->to   = str;
                prepared->next = conversions;
                conversions    = prepared;

                prepared       = NULL;
        }
        else {
                prepared       = xmalloc (sizeof (struct pair_list));
                prepared->from = str;
                prepared->to   = NULL;
                prepared->next = NULL;
        }
}



void
mime_register_type (char *type, char *re)
{
        register_new_type (type, re);
        xfree (re);
}



void
mime_register_handler (char *type, char *handler)
{
        register_new_handler (type, handler);
}



char *
mime_get_handler (mime_t *mime)
{
        struct mime_handler *handler;

        if (mime->type == NULL)
                return NULL;

        handler = find_mime (handlers, mime->type);

        if (handler == NULL || handler->handler == NULL){
                return NULL;
        }
        
        return handler->handler;
}



void
mime_decrypt (mime_info_t *mime_info)
{
        char *handler;
        
        if (mime_info == NULL || mime_info->mime == NULL)
                return;

        if (mime_info->decrypted)
                return;

        handler = mime_get_handler (mime_info->mime);

        if (handler == NULL)
                return;

        if (strcmp (handler, "elmo-pgp") == 0){
                pgp_decrypt_verify (mime_info);
        }
}



/* RFC2047 */
char *
mime_decode_header (char *str, int len, int free_s)
{
        int             ret;
        struct rfc2047  s;

        s.str      = str;
        s.inter    = NULL;
        s.result   = NULL;
        s.special_ = 1;
        
        ret = find_encoded_string (& s);
        if (! ret && free_s){
                return str;
        }
        else if (! ret){
                return xstrdup (str);
        }

        save_charset (& s);
        get_intermediate (& s);
        get_result (& s);
        
        if (free_s)
                xfree (str);

        destroy_s (& s);
        return str_finished (s.result);
}



char *
mime_decode_utf8 (char *str)
{
        struct rfc2047 s;

        s.str     = NULL;
        s.inter   = NULL;
        s.charset = xstrdup ("utf-8");
        s.inter   = str_dup (str);

        get_result (& s);

        destroy_s (& s);
        return str_finished (s.result);
}



str_t *
mime_decode (mime_t *mime, char *str, int free_s)
{
        int             len;
        str_t          *result = NULL;
        struct rfc2047  s;

        if (mime == NULL || str == NULL)
                return NULL;

        len = mime->off_end - mime->off_start;

        switch (mime->encoding){

                case MENC_NONE:
                case MENC_7BIT:
                case MENC_8BIT:
                        result = str_from_str (str, len);
                        break;

                case MENC_QP:
                        result = str_create_size (len + 1);
                        decode_qp (result, str, str + len - 1, 0);
                        break;

                case MENC_BASE64:
                        result = str_create_size (len + 1);
                        decode_base64 (result, str, str + len - 1);
                        break;

                case MENC_UUENCODE:
                        error_ (0, _("uudecode unimplemented yet"));
                        return str_from_str (str, len);

                default:
                        debug_msg (DEBUG_ERROR, "invalid encoding in mime_decode");
                        error_ (0, "%s", _("You have found a bug. Please submit a bug report"));
                        return NULL;
        }

        if (result->str != str && free_s)
                xfree (str);
        
        if (mime->charset == NULL)
                return result;

        s.charset = mime->charset;
        s.inter   = result;
        s.result  = NULL;

        get_result (& s);

        s.charset = NULL;

        destroy_s (& s);
        return s.result;
}



str_t *
mime_encode (mime_t *mime, char *bytes, int len)
{
        int    rlen;
        str_t *result;

        switch (mime->encoding){

                case MENC_NONE:
                case MENC_7BIT:
                case MENC_8BIT:
                        return str_from_str (bytes, len);

                case MENC_QP:
                        error_ (0, _("q-p encoding unimplemented yet"));
                        return str_from_str (bytes, len);

                case MENC_BASE64:
                        rlen   = (4 * len) / 3;
                        rlen  += rlen / 76 + 1;
                        rlen  += 5;
                        result = str_create_size (rlen + 1);
                        encode_base64 (result, bytes, bytes + len - 1);
                        xfree (bytes);
                        return result;

                case MENC_UUENCODE:
                        error_ (0, _("uuencode unimplemented yet"));
                        return str_from_str (bytes, len);

                default:
                        debug_msg (DEBUG_ERROR, "invalid encoding in mime_encode");
                        error_ (0, "%s", _("You have found a bug. Please submit a bug report"));
                        return NULL;
        }
}



mime_info_t *
mime_info_create (void)
{
        mime_info_t *mime_info;

        mime_info = xmalloc (sizeof (mime_info_t));

        mime_info->file      = NULL;
        mime_info->mime      = mime_create ();
        mime_info->d_file    = NULL;
        mime_info->decrypted = NULL;
        mime_info->sig       = SIG_NOT_SIGNED;
        mime_info->sig_text  = NULL;

        return mime_info;
}



mime_t *
mime_create (void)
{
        mime_t *mime;

        mime             = xmalloc (sizeof (mime_t));

        mime->encoding   = MENC_NONE;
        mime->type       = NULL;
        mime->charset    = NULL;

        mime->off_header = -1;
        mime->off_start  = -1;
        mime->off_end    = -1;
        mime->off_bound  = -1;

        mime->file_name  = NULL;
        mime->parts      = NULL;

        return mime;
}



mime_t *
mime_from_file_name (const char *fname)
{
        mime_t *mime;

        mime            = mime_create ();
        mime->file_name = xstrdup (fname);

        determine_type (mime);
        
        return mime;
}



void
mime_destroy (mime_t *mime)
{
        rmime_destroy (mime->parts);
        
        if (mime->file_name)
                xfree (mime->file_name);
        if (mime->type)
                xfree (mime->type);
        if (mime->charset)
                xfree (mime->charset);
        xfree (mime);
}



void
mime_info_destroy (mime_info_t *mime_info)
{
        if (mime_info->mime)
                mime_destroy (mime_info->mime);

        if (mime_info->d_file){
                unlink (mime_info->d_file);
                xfree (mime_info->d_file);
        }
        if (mime_info->decrypted)
                mime_destroy (mime_info->decrypted);
        if (mime_info->sig_text)
                xfree (mime_info->sig_text);
        xfree (mime_info);
}



void
mime_set_from_header (mime_t *mime, char *str)
{
        mime->file_name = get_file_name (str);

        get_type (mime, str);
}



void
mime_complete_file_name (mime_t *mime, char *str)
{
        if (mime->file_name)
                return;

        mime->file_name = get_file_name (str);
}



char *
mime_encoding_str (mime_t *mime)
{
        switch (mime->encoding){

                case MENC_NONE:
                        return NULL;
                
                case MENC_7BIT:
                        return "7bit";

                case MENC_8BIT:
                        return "8bit";
                
                case MENC_QP:
                        return "quoted-printable";

                case MENC_BASE64:
                        return "base64";

                case MENC_UUENCODE:
                        return "uuencode";
        }
        return NULL;
}



void
mime_dump (memchunk_t *memchunk, mime_t *mime)
{
        memchunk_intdump (memchunk, mime->encoding);
        memchunk_strdump (memchunk, mime->type);
        memchunk_strdump (memchunk, mime->charset);

        memchunk_intdump (memchunk, mime->off_header);
        memchunk_intdump (memchunk, mime->off_start);
        memchunk_intdump (memchunk, mime->off_end);
        memchunk_intdump (memchunk, mime->off_bound);

        memchunk_strdump (memchunk, mime->file_name);

        rmime_dump (memchunk, mime->parts);
}



mime_info_t *
mime_info_read (memchunk_t *memchunk)
{
        mime_info_t *mime_info;

        mime_info = mime_info_create ();
        mime_read (memchunk, mime_info->mime);

        return mime_info;
}



void
mime_read (memchunk_t *memchunk, mime_t *mime)
{
        mime->encoding   = memchunk_intget (memchunk);
        mime->type       = memchunk_strget (memchunk);
        mime->charset    = memchunk_strget (memchunk);

        mime->off_header = memchunk_intget (memchunk);
        mime->off_start  = memchunk_intget (memchunk);
        mime->off_end    = memchunk_intget (memchunk);
        mime->off_bound  = memchunk_intget (memchunk);

        mime->file_name  = memchunk_strget (memchunk);
        mime->parts      = rmime_read (memchunk);
}



mime_t *
mime_last (mime_info_t *mime_info)
{
        mime_t *mime;

        mime = DECRYPTED (mime_info);
        return last_leaf (mime);
}



mime_t *
mime_first (mime_info_t *mime_info)
{
        mime_t *mime;

        mime = DECRYPTED (mime_info);
        return first_leaf (mime);
}



mime_t *
mime_nth_leaf (mime_info_t *mime_info, int index)
{
        mime_t *mime;

        mime = DECRYPTED (mime_info);
        return nth_leaf (mime, & index);
}



int
mime_leaf_count (mime_info_t *mime_info)
{
        mime_t *mime;
        
        mime = DECRYPTED (mime_info);
        return leaf_count (mime);
}



mime_t *
mime_first_text (mime_info_t *mime_info)
{
        mime_t  *mime;
        
        mime = DECRYPTED (mime_info);
        return first_text (mime);
}



char
mime_attachment_indicator (mime_info_t *mime_info)
{
        mime_t *mime;

        if (mime_info == NULL)
                return ' ';
        
        mime = DECRYPTED (mime_info);

        if (mime == NULL || mime->type == NULL)
                return ' ';
        
        if (strcasecmp (mime->type, "multipart/encrypted") == 0){
                return 'e';
        }

        if (strcasecmp (mime->type, "multipart/signed") == 0){
                return 's';
        }

        if (has_attachment (mime))
                return '+';
                
        return ' ';
}

/****************************************************************************
 *    INTERFACE CLASS BODIES
 ****************************************************************************/
/****************************************************************************
 *
 *    END MODULE mime.c
 *
 ****************************************************************************/


syntax highlighted by Code2HTML, v. 0.9.1