/* 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 #endif #include #include #include #include #include #ifdef HAVE_ICONV # include #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 * ****************************************************************************/