diff options
Diffstat (limited to 'mail/em-format-html.c')
-rw-r--r-- | mail/em-format-html.c | 1627 |
1 files changed, 0 insertions, 1627 deletions
diff --git a/mail/em-format-html.c b/mail/em-format-html.c deleted file mode 100644 index f3b9b46c5a..0000000000 --- a/mail/em-format-html.c +++ /dev/null @@ -1,1627 +0,0 @@ -/* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- */ -/* - * Authors: Michael Zucchi <notzed@ximian.com> - * - * Copyright 2003 Ximian, Inc. (www.ximian.com) - * - * 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; either version 2 of the License, or - * (at your option) any later version. - * - * 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 Street #330, Boston, MA 02111-1307, USA. - * - */ - -#ifdef HAVE_CONFIG_H -#include <config.h> -#endif - -#include <stdio.h> -#include <string.h> -#include <sys/types.h> -#include <sys/stat.h> -#include <fcntl.h> -#include <ctype.h> - -#include <gal/util/e-iconv.h> -#include <gal/util/e-util.h> /* for e_utf8_strftime, what about e_time_format_time? */ -#include "e-util/e-time-utils.h" - -#include <gtkhtml/gtkhtml.h> -#include <gtkhtml/gtkhtml-embedded.h> -#include <gtkhtml/gtkhtml-stream.h> -#include <gtkhtml/htmlengine.h> - -#include <libgnomevfs/gnome-vfs-utils.h> -#include <libgnomevfs/gnome-vfs-mime-utils.h> -#include <libgnomevfs/gnome-vfs-mime-handlers.h> - -#include <camel/camel-mime-message.h> -#include <camel/camel-stream.h> -#include <camel/camel-stream-filter.h> -#include <camel/camel-mime-filter.h> -#include <camel/camel-mime-filter-tohtml.h> -#include <camel/camel-mime-filter-enriched.h> -#include <camel/camel-multipart.h> -#include <camel/camel-multipart-signed.h> -#include <camel/camel-gpg-context.h> -#include <camel/camel-smime-context.h> -#include <camel/camel-stream-mem.h> -#include <camel/camel-url.h> -#include <camel/camel-stream-fs.h> -#include <camel/camel-string-utils.h> -#include <camel/camel-http-stream.h> -#include <camel/camel-data-cache.h> -#include <camel/camel-file-utils.h> - - -#include <e-util/e-msgport.h> - -#include "mail-component.h" -#include "mail-mt.h" - -#include "em-format-html.h" -#include "em-html-stream.h" -#include "em-utils.h" - -#define d(x) - -#define EFH_TABLE_OPEN "<table>" - -struct _EMFormatHTMLPrivate { - struct _CamelMedium *last_part; /* not reffed, DO NOT dereference */ - volatile int format_id; /* format thread id */ - guint format_timeout_id; - struct _format_msg *format_timeout_msg; - - /* Table that re-maps text parts into a mutlipart/mixed */ - GHashTable *text_inline_parts; - - EDList pending_jobs; - GMutex *lock; -}; - -static void efh_url_requested(GtkHTML *html, const char *url, GtkHTMLStream *handle, EMFormatHTML *efh); -static gboolean efh_object_requested(GtkHTML *html, GtkHTMLEmbedded *eb, EMFormatHTML *efh); -static void efh_gtkhtml_destroy(GtkHTML *html, EMFormatHTML *efh); - -static void efh_format_clone(EMFormat *, CamelMedium *, EMFormat *); -static void efh_format_error(EMFormat *emf, CamelStream *stream, const char *txt); -static void efh_format_message(EMFormat *, CamelStream *, CamelMedium *); -static void efh_format_source(EMFormat *, CamelStream *, CamelMimePart *); -static void efh_format_attachment(EMFormat *, CamelStream *, CamelMimePart *, const char *, const EMFormatHandler *); -static gboolean efh_busy(EMFormat *); - -static void efh_builtin_init(EMFormatHTMLClass *efhc); - -static void efh_write_image(EMFormat *emf, CamelStream *stream, EMFormatPURI *puri); - -static EMFormatClass *efh_parent; -static CamelDataCache *emfh_http_cache; - -#define EMFH_HTTP_CACHE_PATH "http" - -static void -efh_init(GObject *o) -{ - EMFormatHTML *efh = (EMFormatHTML *)o; - - efh->priv = g_malloc0(sizeof(*efh->priv)); - - e_dlist_init(&efh->pending_object_list); - e_dlist_init(&efh->priv->pending_jobs); - efh->priv->lock = g_mutex_new(); - efh->priv->format_id = -1; - efh->priv->text_inline_parts = g_hash_table_new(NULL, NULL); - - efh->html = (GtkHTML *)gtk_html_new(); - gtk_html_set_blocking(efh->html, FALSE); - g_object_ref(efh->html); - gtk_object_sink((GtkObject *)efh->html); - - gtk_html_set_default_content_type(efh->html, "text/html; charset=utf-8"); - gtk_html_set_editable(efh->html, FALSE); - - g_signal_connect(efh->html, "destroy", G_CALLBACK(efh_gtkhtml_destroy), efh); - g_signal_connect(efh->html, "url_requested", G_CALLBACK(efh_url_requested), efh); - g_signal_connect(efh->html, "object_requested", G_CALLBACK(efh_object_requested), efh); - - efh->body_colour = 0xeeeeee; - efh->text_colour = 0; - efh->frame_colour = 0x3f3f3f; - efh->content_colour = 0xffffff; - efh->text_html_flags = CAMEL_MIME_FILTER_TOHTML_CONVERT_NL | CAMEL_MIME_FILTER_TOHTML_CONVERT_SPACES - | CAMEL_MIME_FILTER_TOHTML_MARK_CITATION; -} - -static void -efh_gtkhtml_destroy(GtkHTML *html, EMFormatHTML *efh) -{ - if (efh->priv->format_timeout_id != 0) { - g_source_remove(efh->priv->format_timeout_id); - efh->priv->format_timeout_id = 0; - mail_msg_free(efh->priv->format_timeout_msg); - efh->priv->format_timeout_msg = NULL; - } - - /* This probably works ... */ - if (efh->priv->format_id != -1) - mail_msg_cancel(efh->priv->format_id); - - if (efh->html) { - g_object_unref(efh->html); - efh->html = NULL; - } -} - -static void -efh_free_inline_parts(void *key, void *data, void *user) -{ - camel_object_unref(data); -} - -static void -efh_finalise(GObject *o) -{ - EMFormatHTML *efh = (EMFormatHTML *)o; - - /* FIXME: check for leaked stuff */ - - em_format_html_clear_pobject(efh); - camel_cipher_validity_free(efh->valid); - - efh_gtkhtml_destroy(efh->html, efh); - - g_hash_table_foreach(efh->priv->text_inline_parts, efh_free_inline_parts, NULL); - g_hash_table_destroy(efh->priv->text_inline_parts); - - g_free(efh->priv); - - ((GObjectClass *)efh_parent)->finalize(o); -} - -static void -efh_base_init(EMFormatHTMLClass *efhklass) -{ - efh_builtin_init(efhklass); -} - -static void -efh_class_init(GObjectClass *klass) -{ - ((EMFormatClass *)klass)->format_clone = efh_format_clone; - ((EMFormatClass *)klass)->format_error = efh_format_error; - ((EMFormatClass *)klass)->format_message = efh_format_message; - ((EMFormatClass *)klass)->format_source = efh_format_source; - ((EMFormatClass *)klass)->format_attachment = efh_format_attachment; - ((EMFormatClass *)klass)->busy = efh_busy; - - klass->finalize = efh_finalise; -} - -GType -em_format_html_get_type(void) -{ - static GType type = 0; - - if (type == 0) { - static const GTypeInfo info = { - sizeof(EMFormatHTMLClass), - (GBaseInitFunc)efh_base_init, NULL, - (GClassInitFunc)efh_class_init, - NULL, NULL, - sizeof(EMFormatHTML), 0, - (GInstanceInitFunc)efh_init - }; - const char *base_directory = mail_component_peek_base_directory (mail_component_peek ()); - char *path; - - efh_parent = g_type_class_ref(em_format_get_type()); - type = g_type_register_static(em_format_get_type(), "EMFormatHTML", &info, 0); - - /* cache expiry - 2 hour access, 1 day max */ - path = alloca(strlen(base_directory)+16); - sprintf(path, "%s/cache", base_directory); - emfh_http_cache = camel_data_cache_new(path, 0, NULL); - camel_data_cache_set_expire_age(emfh_http_cache, 24*60*60); - camel_data_cache_set_expire_access(emfh_http_cache, 2*60*60); - } - - return type; -} - -EMFormatHTML *em_format_html_new(void) -{ - EMFormatHTML *efh; - - efh = g_object_new(em_format_html_get_type(), 0); - - return efh; -} - -/* force loading of http images */ -void em_format_html_load_http(EMFormatHTML *emfh) -{ - if (emfh->load_http) - return; - - /* This will remain set while we're still rendering the same message, then it wont be */ - emfh->load_http_now = TRUE; - d(printf("redrawing with images forced on\n")); - em_format_format_clone((EMFormat *)emfh, emfh->format.message, (EMFormat *)emfh); -} - -void -em_format_html_set_load_http(EMFormatHTML *emfh, int state) -{ - if (emfh->load_http ^ state) { - emfh->load_http = state; - em_format_format_clone((EMFormat *)emfh, emfh->format.message, (EMFormat *)emfh); - } -} - -void -em_format_html_set_mark_citations(EMFormatHTML *emfh, int state, guint32 citation_colour) -{ - if (emfh->mark_citations ^ state || emfh->citation_colour != citation_colour) { - emfh->mark_citations = state; - emfh->citation_colour = citation_colour; - em_format_format_clone((EMFormat *)emfh, emfh->format.message, (EMFormat *)emfh); - } -} - -void -em_format_html_set_xmailer_mask(EMFormatHTML *emfh, unsigned int xmailer_mask) -{ - if (emfh->xmailer_mask ^ xmailer_mask) { - emfh->xmailer_mask = xmailer_mask; - em_format_format_clone((EMFormat *)emfh, emfh->format.message, (EMFormat *)emfh); - } -} - -CamelMimePart * -em_format_html_file_part(EMFormatHTML *efh, const char *mime_type, const char *path, const char *name) -{ - CamelMimePart *part; - CamelStream *stream; - CamelDataWrapper *dw; - char *filename; - - filename = g_build_filename(path, name, NULL); - stream = camel_stream_fs_new_with_name(filename, O_RDONLY, 0); - g_free(filename); - if (stream == NULL) - return NULL; - - part = camel_mime_part_new(); - dw = camel_data_wrapper_new(); - camel_data_wrapper_construct_from_stream(dw, stream); - camel_object_unref(stream); - if (mime_type) - camel_data_wrapper_set_mime_type(dw, mime_type); - part = camel_mime_part_new(); - camel_medium_set_content_object((CamelMedium *)part, dw); - camel_object_unref(dw); - camel_mime_part_set_filename(part, name); - - return part; -} - -/* all this api is a pain in the bum ... */ - -EMFormatHTMLPObject * -em_format_html_add_pobject(EMFormatHTML *efh, size_t size, const char *classid, CamelMimePart *part, EMFormatHTMLPObjectFunc func) -{ - EMFormatHTMLPObject *pobj; - - g_assert(size >= sizeof(EMFormatHTMLPObject)); - - pobj = g_malloc0(size); - if (classid) { - pobj->classid = g_strdup(classid); - } else { - static unsigned int uriid = 0; - - pobj->classid = g_strdup_printf("e-object:///%u", uriid++); - } - - pobj->format = efh; - pobj->func = func; - pobj->part = part; - - e_dlist_addtail(&efh->pending_object_list, (EDListNode *)pobj); - - return pobj; -} - -EMFormatHTMLPObject * -em_format_html_find_pobject(EMFormatHTML *emf, const char *classid) -{ - EMFormatHTMLPObject *pw; - - pw = (EMFormatHTMLPObject *)emf->pending_object_list.head; - while (pw->next) { - if (!strcmp(pw->classid, classid)) - return pw; - pw = pw->next; - } - - return NULL; -} - -EMFormatHTMLPObject * -em_format_html_find_pobject_func(EMFormatHTML *emf, CamelMimePart *part, EMFormatHTMLPObjectFunc func) -{ - EMFormatHTMLPObject *pw; - - pw = (EMFormatHTMLPObject *)emf->pending_object_list.head; - while (pw->next) { - if (pw->func == func && pw->part == part) - return pw; - pw = pw->next; - } - - return NULL; -} - -void -em_format_html_remove_pobject(EMFormatHTML *emf, EMFormatHTMLPObject *pobject) -{ - e_dlist_remove((EDListNode *)pobject); - if (pobject->free) - pobject->free(pobject); - g_free(pobject->classid); - g_free(pobject); -} - -void -em_format_html_clear_pobject(EMFormatHTML *emf) -{ - d(printf("clearing pending objects\n")); - while (!e_dlist_empty(&emf->pending_object_list)) - em_format_html_remove_pobject(emf, (EMFormatHTMLPObject *)emf->pending_object_list.head); -} - -struct _EMFormatHTMLJob * -em_format_html_job_new(EMFormatHTML *emfh, void (*callback)(struct _EMFormatHTMLJob *job, int cancelled), void *data) -{ - struct _EMFormatHTMLJob *job = g_malloc0(sizeof(*job)); - - job->format = emfh; - job->puri_level = ((EMFormat *)emfh)->pending_uri_level; - job->callback = callback; - job->u.data = data; - if (((EMFormat *)emfh)->base) - job->base = camel_url_copy(((EMFormat *)emfh)->base); - - return job; -} - -void -em_format_html_job_queue(EMFormatHTML *emfh, struct _EMFormatHTMLJob *job) -{ - g_mutex_lock(emfh->priv->lock); - e_dlist_addtail(&emfh->priv->pending_jobs, (EDListNode *)job); - g_mutex_unlock(emfh->priv->lock); -} - -/* ********************************************************************** */ - -static void emfh_getpuri(struct _EMFormatHTMLJob *job, int cancelled) -{ - d(printf(" running getpuri task\n")); - if (!cancelled) - job->u.puri->func((EMFormat *)job->format, job->stream, job->u.puri); -} - -static void emfh_gethttp(struct _EMFormatHTMLJob *job, int cancelled) -{ - CamelStream *cistream = NULL, *costream = NULL, *instream = NULL; - CamelURL *url; - ssize_t n, total = 0; - char buffer[1500]; - - if (cancelled - || (url = camel_url_new(job->u.uri, NULL)) == NULL) - goto badurl; - - d(printf(" running load uri task: %s\n", job->u.uri)); - - if (emfh_http_cache) - instream = cistream = camel_data_cache_get(emfh_http_cache, EMFH_HTTP_CACHE_PATH, job->u.uri, NULL); - - if (instream == NULL) { - char *proxy; - - if (!job->format->load_http_now) { - /* TODO: Ideally we would put the http requests into another queue and only send them out - if the user selects 'load images', when they do. The problem is how to maintain this - state with multiple renderings, and how to adjust the thread dispatch/setup routine to handle it */ - /* FIXME: Need to handle 'load if sender in addressbook' case too */ - camel_url_free(url); - goto done; - } - - instream = camel_http_stream_new(CAMEL_HTTP_METHOD_GET, ((EMFormat *)job->format)->session, url); - proxy = em_utils_get_proxy_uri(); - camel_http_stream_set_proxy((CamelHttpStream *)instream, proxy); - g_free(proxy); - camel_operation_start(NULL, _("Retrieving `%s'"), job->u.uri); - } else - camel_operation_start_transient(NULL, _("Retrieving `%s'"), job->u.uri); - - camel_url_free(url); - - if (instream == NULL) - goto done; - - if (emfh_http_cache != NULL && cistream == NULL) - costream = camel_data_cache_add(emfh_http_cache, EMFH_HTTP_CACHE_PATH, job->u.uri, NULL); - - do { - /* FIXME: progress reporting in percentage, can we get the length always? do we care? */ - n = camel_stream_read(instream, buffer, 1500); - if (n > 0) { - camel_operation_progress_count(NULL, total); - total += n; - d(printf(" read %d bytes\n", n)); - if (costream && camel_stream_write(costream, buffer, n) == -1) { - camel_data_cache_remove(emfh_http_cache, EMFH_HTTP_CACHE_PATH, job->u.uri, NULL); - camel_object_unref(costream); - costream = NULL; - } - - camel_stream_write(job->stream, buffer, n); - } else if (n < 0 && costream) { - camel_data_cache_remove(emfh_http_cache, EMFH_HTTP_CACHE_PATH, job->u.uri, NULL); - camel_object_unref(costream); - costream = NULL; - } - } while (n>0); - - /* indicates success */ - if (n == 0) - camel_stream_close(job->stream); - - if (costream) - camel_object_unref(costream); - - camel_object_unref(instream); -done: - camel_operation_end(NULL); -badurl: - g_free(job->u.uri); -} - -/* ********************************************************************** */ - -static void -efh_url_requested(GtkHTML *html, const char *url, GtkHTMLStream *handle, EMFormatHTML *efh) -{ - EMFormatPURI *puri; - struct _EMFormatHTMLJob *job = NULL; - - d(printf("url requested, html = %p, url '%s'\n", html, url)); - - puri = em_format_find_visible_puri((EMFormat *)efh, url); - if (puri) { - puri->use_count++; - - d(printf(" adding puri job\n")); - job = em_format_html_job_new(efh, emfh_getpuri, puri); - } else if (g_ascii_strncasecmp(url, "http:", 5) == 0 || g_ascii_strncasecmp(url, "https:", 6) == 0) { - d(printf(" adding job, get %s\n", url)); - job = em_format_html_job_new(efh, emfh_gethttp, g_strdup(url)); - } else { - d(printf("HTML Includes reference to unknown uri '%s'\n", url)); - gtk_html_stream_close(handle, GTK_HTML_STREAM_ERROR); - } - - if (job) { - job->stream = em_html_stream_new(html, handle); - em_format_html_job_queue(efh, job); - } -} - -static gboolean -efh_object_requested(GtkHTML *html, GtkHTMLEmbedded *eb, EMFormatHTML *efh) -{ - EMFormatHTMLPObject *pobject; - int res = FALSE; - - if (eb->classid == NULL) - return FALSE; - - pobject = em_format_html_find_pobject(efh, eb->classid); - if (pobject) { - /* This stops recursion of the part */ - e_dlist_remove((EDListNode *)pobject); - res = pobject->func(efh, eb, pobject); - e_dlist_addhead(&efh->pending_object_list, (EDListNode *)pobject); - } else { - printf("HTML Includes reference to unknown object '%s'\n", eb->classid); - } - - return res; -} - -/* ********************************************************************** */ -#include "em-inline-filter.h" -#include <camel/camel-stream-null.h> - -/* FIXME: This is duplicated in em-format-html-display, should be exported or in security module */ -static const struct { - const char *icon, *shortdesc; -} smime_sign_table[4] = { - { "pgp-signature-nokey.png", N_("Unsigned") }, - { "pgp-signature-ok.png", N_("Valid signature") }, - { "pgp-signature-bad.png", N_("Invalid signature") }, - { "pgp-signature-nokey.png", N_("Valid signature, cannot verify sender") }, -}; - -static const struct { - const char *icon, *shortdesc; -} smime_encrypt_table[4] = { - { NULL, N_("Unencrypted") }, - { "pgp-signature-ok.png", N_("Encrypted, weak"),}, - { "pgp-signature-ok.png", N_("Encrypted") }, - { "pgp-signature-ok.png", N_("Encrypted, strong") }, -}; - -/* TODO: this could probably be virtual on em-format-html - then we only need one version of each type handler */ -static void -efh_output_secure(EMFormat *emf, CamelStream *stream, CamelMimePart *part, CamelCipherValidity *valid) -{ - CamelCipherValidity *save = ((EMFormatHTML *)emf)->valid_parent; - - /* Note: this same logic is in efhd_output_secure */ - if (((EMFormatHTML *)emf)->valid == NULL) { - ((EMFormatHTML *)emf)->valid = valid; - } else { - e_dlist_addtail(&((EMFormatHTML *)emf)->valid_parent->children, (EDListNode *)valid); - camel_cipher_validity_envelope(((EMFormatHTML *)emf)->valid_parent, valid); - } - - ((EMFormatHTML *)emf)->valid_parent = valid; - em_format_part(emf, stream, part); - ((EMFormatHTML *)emf)->valid_parent = save; - - if (((EMFormatHTML *)emf)->valid == valid - && (valid->encrypt.status != CAMEL_CIPHER_VALIDITY_ENCRYPT_NONE - || valid->sign.status != CAMEL_CIPHER_VALIDITY_SIGN_NONE)) { - char *classid; - CamelMimePart *iconpart; - - camel_stream_printf(stream, "<table border=0 width=\"100%%\" cellpadding=3 cellspacing=0 bgcolor=%s><tr>", - valid->sign.status == CAMEL_CIPHER_VALIDITY_SIGN_GOOD?"#88bb88":"#bb8888"); - - classid = g_strdup_printf("smime:///em-format-html/%p/icon/signed", part); - camel_stream_printf(stream, "<td valign=\"top\"><img src=\"%s\"></td><td valign=\"top\" width=\"100%%\">", classid); - iconpart = em_format_html_file_part((EMFormatHTML *)emf, "image/png", - EVOLUTION_ICONSDIR, smime_sign_table[valid->sign.status].icon); - if (iconpart) { - (void)em_format_add_puri(emf, sizeof(EMFormatPURI), classid, iconpart, efh_write_image); - camel_object_unref(iconpart); - } - g_free(classid); - - if (valid->sign.status != CAMEL_CIPHER_VALIDITY_SIGN_NONE) { - camel_stream_printf(stream, "%s<br>", _(smime_sign_table[valid->sign.status].shortdesc)); - } - - if (valid->encrypt.status != CAMEL_CIPHER_VALIDITY_ENCRYPT_NONE) { - camel_stream_printf(stream, "%s<br>", _(smime_encrypt_table[valid->encrypt.status].shortdesc)); - } - - camel_stream_printf(stream, "</td></tr></table>"); - } -} - -static void -efh_application_xpkcs7mime(EMFormat *emf, CamelStream *stream, CamelMimePart *part, const EMFormatHandler *info) -{ - CamelCipherContext *context; - CamelException *ex; - extern CamelSession *session; - CamelMimePart *opart; - CamelCipherValidity *valid; - - ex = camel_exception_new(); - - context = camel_smime_context_new(session); - - opart = camel_mime_part_new(); - valid = camel_cipher_decrypt(context, part, opart, ex); - if (valid == NULL) { - em_format_format_error(emf, stream, ex->desc?ex->desc:_("Could not parse S/MIME message: Unknown error")); - em_format_part_as(emf, stream, part, NULL); - } else { - efh_output_secure(emf, stream, opart, valid); - } - - camel_object_unref(opart); - camel_object_unref(context); - camel_exception_free(ex); -} - -static void -efh_text_plain(EMFormatHTML *efh, CamelStream *stream, CamelMimePart *part, EMFormatHandler *info) -{ - CamelStreamFilter *filtered_stream; - CamelMimeFilter *html_filter; - CamelMultipart *mp; - CamelContentType *type; - const char *format; - guint32 rgb = 0x737373, flags; - int i, count; - - camel_stream_printf (stream, - "<table bgcolor=\"#%06x\" cellspacing=0 cellpadding=1 width=100%%><tr><td>\n" - "<table bgcolor=\"#%06x\" cellspacing=0 cellpadding=0 width=100%%><tr><td>\n" - "<table cellspacing=0 cellpadding=10><td><tr>\n", - efh->frame_colour & 0xffffff, efh->content_colour & 0xffffff); - - flags = efh->text_html_flags; - - /* Check for RFC 2646 flowed text. */ - type = camel_mime_part_get_content_type(part); - if (camel_content_type_is (type, "text", "plain") - && (format = camel_content_type_param (type, "format")) - && !g_ascii_strcasecmp(format, "flowed")) - flags |= CAMEL_MIME_FILTER_TOHTML_FORMAT_FLOWED; - - /* This scans the text part for inline-encoded data, creates - a multipart of all the parts inside it. */ - - /* FIXME: We should discard this multipart if it only contains - the original text, but it makes this hash lookup more complex */ - - /* TODO: We could probably put this in the superclass, since - no knowledge of html is required - but this messes with - filters a bit. Perhaps the superclass should just deal with - html anyway and be done with it ... */ - - mp = g_hash_table_lookup(efh->priv->text_inline_parts, part); - if (mp == NULL) { - EMInlineFilter *inline_filter; - CamelStream *null; - - null = camel_stream_null_new(); - filtered_stream = camel_stream_filter_new_with_stream(null); - camel_object_unref(null); - inline_filter = em_inline_filter_new(camel_mime_part_get_encoding(part)); - camel_stream_filter_add(filtered_stream, (CamelMimeFilter *)inline_filter); - camel_data_wrapper_write_to_stream(camel_medium_get_content_object((CamelMedium *)part), (CamelStream *)filtered_stream); - camel_stream_close((CamelStream *)filtered_stream); - camel_object_unref(filtered_stream); - mp = em_inline_filter_get_multipart(inline_filter); - g_hash_table_insert(efh->priv->text_inline_parts, part, mp); - camel_object_unref(inline_filter); - } - - filtered_stream = camel_stream_filter_new_with_stream(stream); - html_filter = camel_mime_filter_tohtml_new(flags, rgb); - camel_stream_filter_add(filtered_stream, html_filter); - camel_object_unref(html_filter); - - /* We handle our made-up multipart here, so we don't recursively call ourselves */ - - count = camel_multipart_get_number(mp); - for (i=0;i<count;i++) { - CamelMimePart *newpart = camel_multipart_get_part(mp, i); - - type = camel_mime_part_get_content_type(newpart); - if (camel_content_type_is (type, "text", "plain")) { - camel_stream_write_string(stream, "<tt>\n"); - em_format_format_text((EMFormat *)efh, (CamelStream *)filtered_stream, camel_medium_get_content_object((CamelMedium *)newpart)); - camel_stream_flush((CamelStream *)filtered_stream); - camel_stream_write_string(stream, "</tt>\n"); - } else { - em_format_part((EMFormat *)efh, stream, newpart); - } - } - - camel_object_unref(filtered_stream); - camel_stream_write_string(stream, - "</td></tr></table>\n" - "</td></tr></table>\n" - "</td></tr></table>\n"); -} - -static void -efh_text_enriched(EMFormatHTML *efh, CamelStream *stream, CamelMimePart *part, EMFormatHandler *info) -{ - CamelStreamFilter *filtered_stream; - CamelMimeFilter *enriched; - CamelDataWrapper *dw; - guint32 flags = 0; - - dw = camel_medium_get_content_object((CamelMedium *)part); - - if (!strcmp(info->mime_type, "text/richtext")) { - flags = CAMEL_MIME_FILTER_ENRICHED_IS_RICHTEXT; - camel_stream_write_string( stream, "\n<!-- text/richtext -->\n"); - } else { - camel_stream_write_string( stream, "\n<!-- text/enriched -->\n"); - } - - enriched = camel_mime_filter_enriched_new(flags); - filtered_stream = camel_stream_filter_new_with_stream (stream); - camel_stream_filter_add(filtered_stream, enriched); - camel_object_unref(enriched); - - camel_stream_write_string(stream, EFH_TABLE_OPEN "<tr><td><tt>\n"); - em_format_format_text((EMFormat *)efh, (CamelStream *)filtered_stream, dw); - - camel_stream_write_string(stream, "</tt></td></tr></table>\n"); - camel_object_unref(filtered_stream); -} - -static void -efh_write_text_html(EMFormat *emf, CamelStream *stream, EMFormatPURI *puri) -{ - em_format_format_text(emf, stream, camel_medium_get_content_object((CamelMedium *)puri->part)); -} - -static void -efh_text_html(EMFormatHTML *efh, CamelStream *stream, CamelMimePart *part, EMFormatHandler *info) -{ - const char *location, *base; - EMFormatPURI *puri; - - camel_stream_printf (stream, - "<table bgcolor=\"#%06x\" cellspacing=0 cellpadding=1 width=100%%><tr><td>\n" - "<table bgcolor=\"#%06x\" cellspacing=0 cellpadding=0 width=100%%><tr><td>\n" - "<!-- text/html -->\n", - efh->frame_colour & 0xffffff, efh->content_colour & 0xffffff); - - if ((base = camel_medium_get_header((CamelMedium *)part, "Content-Base"))) { - char *base_url; - size_t len; - - len = strlen(base); - if (*base == '"' && *(base + len - 1) == '"') { - len -= 2; - base_url = alloca(len + 1); - memcpy(base_url, base + 1, len); - base_url[len] = '\0'; - base = base_url; - } - - /* FIXME: set base needs to go on the gtkhtml stream? */ - gtk_html_set_base(efh->html, base); - } - - puri = em_format_add_puri((EMFormat *)efh, sizeof(EMFormatPURI), NULL, part, efh_write_text_html); - location = puri->uri?puri->uri:puri->cid; - d(printf("adding iframe, location %s\n", location)); - camel_stream_printf(stream, - "<iframe src=\"%s\" frameborder=0 scrolling=no>could not get %s</iframe>\n" - "</td></tr></table>\n" - "</td></tr></table>\n", - location, location); -} - -/* This is a lot of code for something useless ... */ -static void -efh_message_external(EMFormatHTML *efh, CamelStream *stream, CamelMimePart *part, EMFormatHandler *info) -{ - CamelContentType *type; - const char *access_type; - char *url = NULL, *desc = NULL; - - /* needs to be cleaner */ - type = camel_mime_part_get_content_type(part); - access_type = camel_content_type_param (type, "access-type"); - if (!access_type) { - camel_stream_printf(stream, _("Malformed external-body part.")); - return; - } - - if (!g_ascii_strcasecmp(access_type, "ftp") || - !g_ascii_strcasecmp(access_type, "anon-ftp")) { - const char *name, *site, *dir, *mode; - char *path; - char ftype[16]; - - name = camel_content_type_param (type, "name"); - site = camel_content_type_param (type, "site"); - dir = camel_content_type_param (type, "directory"); - mode = camel_content_type_param (type, "mode"); - if (name == NULL || site == NULL) - goto fail; - - /* Generate the path. */ - if (dir) - path = g_strdup_printf("/%s/%s", *dir=='/'?dir+1:dir, name); - else - path = g_strdup_printf("/%s", *name=='/'?name+1:name); - - if (mode && &mode) - sprintf(ftype, ";type=%c", *mode); - else - ftype[0] = 0; - - url = g_strdup_printf ("ftp://%s%s%s", site, path, ftype); - g_free (path); - desc = g_strdup_printf (_("Pointer to FTP site (%s)"), url); - } else if (!g_ascii_strcasecmp (access_type, "local-file")) { - const char *name, *site; - - name = camel_content_type_param (type, "name"); - site = camel_content_type_param (type, "site"); - if (name == NULL) - goto fail; - - url = g_strdup_printf ("file:///%s", *name == '/' ? name+1:name); - if (site) - desc = g_strdup_printf(_("Pointer to local file (%s) valid at site \"%s\""), name, site); - else - desc = g_strdup_printf(_("Pointer to local file (%s)"), name); - } else if (!g_ascii_strcasecmp (access_type, "URL")) { - const char *urlparam; - char *s, *d; - - /* RFC 2017 */ - - urlparam = camel_content_type_param (type, "url"); - if (urlparam == NULL) - goto fail; - - /* For obscure MIMEy reasons, the URL may be split into words */ - url = g_strdup (urlparam); - s = d = url; - while (*s) { - /* FIXME: use camel_isspace */ - if (!isspace ((unsigned char)*s)) - *d++ = *s; - s++; - } - *d = 0; - desc = g_strdup_printf (_("Pointer to remote data (%s)"), url); - } else - goto fail; - - camel_stream_printf(stream, "<a href=\"%s\">%s</a>", url, desc); - g_free(url); - g_free(desc); - - return; - -fail: - camel_stream_printf(stream, _("Pointer to unknown external data (\"%s\" type)"), access_type); -} - -static void -emfh_write_related(EMFormat *emf, CamelStream *stream, EMFormatPURI *puri) -{ - em_format_format_content(emf, stream, puri->part); - camel_stream_close(stream); -} - -static void -emfh_multipart_related_check(struct _EMFormatHTMLJob *job, int cancelled) -{ - struct _EMFormatPURITree *ptree; - EMFormatPURI *puri, *purin; - - if (cancelled) - return; - - d(printf(" running multipart/related check task\n")); - - ptree = job->puri_level; - puri = (EMFormatPURI *)ptree->uri_list.head; - purin = puri->next; - while (purin) { - if (puri->use_count == 0) { - d(printf("part '%s' '%s' used '%d'\n", puri->uri?puri->uri:"", puri->cid, puri->use_count)); - if (puri->func == emfh_write_related) - em_format_part((EMFormat *)job->format, (CamelStream *)job->stream, puri->part); - /* else it was probably added by a previous format this loop */ - } - puri = purin; - purin = purin->next; - } -} - -/* RFC 2387 */ -static void -efh_multipart_related(EMFormat *emf, CamelStream *stream, CamelMimePart *part, const EMFormatHandler *info) -{ - CamelMultipart *mp = (CamelMultipart *)camel_medium_get_content_object((CamelMedium *)part); - CamelMimePart *body_part, *display_part = NULL; - CamelContentType *content_type; - const char *location, *start; - int i, nparts; - CamelURL *base_save = NULL; - EMFormatPURI *puri; - struct _EMFormatHTMLJob *job; - - if (!CAMEL_IS_MULTIPART(mp)) { - em_format_format_source(emf, stream, part); - return; - } - - nparts = camel_multipart_get_number(mp); - content_type = camel_mime_part_get_content_type(part); - start = camel_content_type_param (content_type, "start"); - if (start && strlen(start)>2) { - int len; - const char *cid; - - /* strip <>'s */ - len = strlen (start) - 2; - start++; - - for (i=0; i<nparts; i++) { - body_part = camel_multipart_get_part(mp, i); - cid = camel_mime_part_get_content_id(body_part); - - if (cid && !strncmp(cid, start, len) && strlen(cid) == len) { - display_part = body_part; - break; - } - } - } else { - display_part = camel_multipart_get_part(mp, 0); - } - - if (display_part == NULL) { - em_format_part_as(emf, stream, part, "multipart/mixed"); - return; - } - - /* stack of present location and pending uri's */ - location = camel_mime_part_get_content_location(part); - if (location) { - d(printf("setting content location %s\n", location)); - base_save = emf->base; - emf->base = camel_url_new(location, NULL); - } - em_format_push_level(emf); - - /* queue up the parts for possible inclusion */ - for (i = 0; i < nparts; i++) { - body_part = camel_multipart_get_part(mp, i); - if (body_part != display_part) { - puri = em_format_add_puri(emf, sizeof(EMFormatPURI), NULL, body_part, emfh_write_related); - d(printf(" part '%s' '%s' added\n", puri->uri?puri->uri:"", puri->cid)); - } - } - - em_format_part(emf, stream, display_part); - camel_stream_flush(stream); - - /* queue a job to check for un-referenced parts to add as attachments */ - job = em_format_html_job_new((EMFormatHTML *)emf, emfh_multipart_related_check, NULL); - job->stream = stream; - camel_object_ref(stream); - em_format_html_job_queue((EMFormatHTML *)emf, job); - - em_format_pull_level(emf); - - if (location) { - camel_url_free(emf->base); - emf->base = base_save; - } -} - -static void -efh_multipart_signed(EMFormat *emf, CamelStream *stream, CamelMimePart *part, const EMFormatHandler *info) -{ - CamelMimePart *cpart; - CamelMultipartSigned *mps; - CamelCipherContext *cipher = NULL; - - mps = (CamelMultipartSigned *)camel_medium_get_content_object((CamelMedium *)part); - if (!CAMEL_IS_MULTIPART_SIGNED(mps) - || (cpart = camel_multipart_get_part((CamelMultipart *)mps, CAMEL_MULTIPART_SIGNED_CONTENT)) == NULL) { - em_format_format_error(emf, stream, _("Could not parse MIME message. Displaying as source.")); - em_format_format_source(emf, stream, part); - return; - } - - /* FIXME: Should be done via a plugin interface */ - /* FIXME: duplicated in em-format-html-display.c */ - if (g_ascii_strcasecmp("application/x-pkcs7-signature", mps->protocol) == 0) - cipher = camel_smime_context_new(emf->session); - else if (g_ascii_strcasecmp("application/pgp-signature", mps->protocol) == 0) - cipher = camel_gpg_context_new(emf->session); - - if (cipher == NULL) { - em_format_format_error(emf, stream, _("Unsupported signature format")); - em_format_part_as(emf, stream, part, NULL); - } else { - CamelException *ex = camel_exception_new(); - CamelCipherValidity *valid; - - valid = camel_cipher_verify(cipher, part, ex); - if (valid == NULL) { - em_format_format_error(emf, stream, ex->desc?ex->desc:_("Unknown error verifying signature")); - em_format_part_as(emf, stream, part, NULL); - } else { - efh_output_secure(emf, stream, cpart, valid); - } - - camel_exception_free(ex); - camel_object_unref(cipher); - } -} - -static void -efh_write_image(EMFormat *emf, CamelStream *stream, EMFormatPURI *puri) -{ - CamelDataWrapper *dw = camel_medium_get_content_object((CamelMedium *)puri->part); - - d(printf("writing image '%s'\n", puri->uri?puri->uri:puri->cid)); - camel_data_wrapper_decode_to_stream(dw, stream); - camel_stream_close(stream); -} - -static void -efh_image(EMFormatHTML *efh, CamelStream *stream, CamelMimePart *part, EMFormatHandler *info) -{ - EMFormatPURI *puri; - const char *location; - - puri = em_format_add_puri((EMFormat *)efh, sizeof(EMFormatPURI), NULL, part, efh_write_image); - location = puri->uri?puri->uri:puri->cid; - d(printf("adding image '%s'\n", location)); - camel_stream_printf(stream, "<img hspace=10 vspace=10 src=\"%s\">", location); -} - -static EMFormatHandler type_builtin_table[] = { - { "application/x-pkcs7-mime", (EMFormatFunc)efh_application_xpkcs7mime }, - - { "image/gif", (EMFormatFunc)efh_image }, - { "image/jpeg", (EMFormatFunc)efh_image }, - { "image/png", (EMFormatFunc)efh_image }, - { "image/x-png", (EMFormatFunc)efh_image }, - { "image/tiff", (EMFormatFunc)efh_image }, - { "image/x-bmp", (EMFormatFunc)efh_image }, - { "image/bmp", (EMFormatFunc)efh_image }, - { "image/svg", (EMFormatFunc)efh_image }, - { "image/x-cmu-raster", (EMFormatFunc)efh_image }, - { "image/x-ico", (EMFormatFunc)efh_image }, - { "image/x-portable-anymap", (EMFormatFunc)efh_image }, - { "image/x-portable-bitmap", (EMFormatFunc)efh_image }, - { "image/x-portable-graymap", (EMFormatFunc)efh_image }, - { "image/x-portable-pixmap", (EMFormatFunc)efh_image }, - { "image/x-xpixmap", (EMFormatFunc)efh_image }, - { "text/enriched", (EMFormatFunc)efh_text_enriched }, - { "text/plain", (EMFormatFunc)efh_text_plain }, - { "text/html", (EMFormatFunc)efh_text_html }, - { "text/richtext", (EMFormatFunc)efh_text_enriched }, - /*{ "text/ *", (EMFormatFunc)efh_text_plain },*/ - { "message/external-body", (EMFormatFunc)efh_message_external }, - { "multipart/signed", (EMFormatFunc)efh_multipart_signed }, - { "multipart/related", (EMFormatFunc)efh_multipart_related }, - - /* This is where one adds those busted, non-registered types, - that some idiot mailer writers out there decide to pull out - of their proverbials at random. */ - - { "image/jpg", (EMFormatFunc)efh_image }, - { "image/pjpeg", (EMFormatFunc)efh_image }, -}; - -static void -efh_builtin_init(EMFormatHTMLClass *efhc) -{ - int i; - - for (i=0;i<sizeof(type_builtin_table)/sizeof(type_builtin_table[0]);i++) - em_format_class_add_handler((EMFormatClass *)efhc, &type_builtin_table[i]); -} - -/* ********************************************************************** */ - -/* Sigh, this is so we have a cancellable, async rendering thread */ -struct _format_msg { - struct _mail_msg msg; - - EMFormatHTML *format; - EMFormat *format_source; - EMHTMLStream *estream; - CamelMedium *message; -}; - -static char *efh_format_desc(struct _mail_msg *mm, int done) -{ - return g_strdup(_("Formatting message")); -} - -static void efh_format_do(struct _mail_msg *mm) -{ - struct _format_msg *m = (struct _format_msg *)mm; - struct _EMFormatHTMLJob *job; - struct _EMFormatPURITree *puri_level; - int cancelled = FALSE; - CamelURL *base; - - if (m->format->html == NULL) - return; - - camel_stream_printf((CamelStream *)m->estream, - "<!doctype html public \"-//W3C//DTD HTML 4.0 TRANSITIONAL//EN\">\n<html>\n" - "<head>\n<meta name=\"generator\" content=\"Evolution Mail Component\">\n</head>\n" - "<body bgcolor =\"#%06x\" text=\"#%06x\" marginwidth=6 marginheight=6>\n", - m->format->body_colour & 0xffffff, - m->format->text_colour & 0xffffff); - - /* <insert top-header stuff here> */ - - if (((EMFormat *)m->format)->mode == EM_FORMAT_SOURCE) - em_format_format_source((EMFormat *)m->format, (CamelStream *)m->estream, (CamelMimePart *)m->message); - else - em_format_format_message((EMFormat *)m->format, (CamelStream *)m->estream, m->message); - - camel_stream_write_string((CamelStream *)m->estream, "</body>\n</html>\n"); - camel_stream_close((CamelStream *)m->estream); - camel_object_unref(m->estream); - m->estream = NULL; - - puri_level = ((EMFormat *)m->format)->pending_uri_level; - base = ((EMFormat *)m->format)->base; - - /* now dispatch any added tasks ... */ - g_mutex_lock(m->format->priv->lock); - while ((job = (struct _EMFormatHTMLJob *)e_dlist_remhead(&m->format->priv->pending_jobs))) { - g_mutex_unlock(m->format->priv->lock); - - /* This is an implicit check to see if the gtkhtml has been destroyed */ - if (!cancelled) - cancelled = m->format->html == NULL; - - /* Now do an explicit check for user cancellation */ - if (!cancelled) - cancelled = camel_operation_cancel_check(NULL); - - /* call jobs even if cancelled, so they can clean up resources */ - ((EMFormat *)m->format)->pending_uri_level = job->puri_level; - if (job->base) - ((EMFormat *)m->format)->base = job->base; - job->callback(job, cancelled); - ((EMFormat *)m->format)->base = base; - - /* clean up the job */ - camel_object_unref(job->stream); - if (job->base) - camel_url_free(job->base); - g_free(job); - - g_mutex_lock(m->format->priv->lock); - } - g_mutex_unlock(m->format->priv->lock); - d(printf("out of jobs, done\n")); - - ((EMFormat *)m->format)->pending_uri_level = puri_level; -} - -static void efh_format_done(struct _mail_msg *mm) -{ - struct _format_msg *m = (struct _format_msg *)mm; - - d(printf("formatting finished\n")); - - m->format->priv->format_id = -1; - g_signal_emit_by_name(m->format, "complete"); -} - -static void efh_format_free(struct _mail_msg *mm) -{ - struct _format_msg *m = (struct _format_msg *)mm; - - d(printf("formatter freed\n")); - g_object_unref(m->format); - if (m->estream) { - camel_stream_close((CamelStream *)m->estream); - camel_object_unref(m->estream); - } - if (m->message) - camel_object_unref(m->message); - if (m->format_source) - g_object_unref(m->format_source); -} - -static struct _mail_msg_op efh_format_op = { - efh_format_desc, - efh_format_do, - efh_format_done, - efh_format_free, -}; - -static gboolean -efh_format_timeout(struct _format_msg *m) -{ - GtkHTMLStream *hstream; - EMFormatHTML *efh = m->format; - struct _EMFormatHTMLPrivate *p = efh->priv; - - if (m->format->html == NULL) { - mail_msg_free(m); - return FALSE; - } - - d(printf("timeout called ...\n")); - if (p->format_id != -1) { - d(printf(" still waiting for cancellation to take effect, waiting ...\n")); - return TRUE; - } - - g_assert(e_dlist_empty(&p->pending_jobs)); - - d(printf(" ready to go, firing off format thread\n")); - - /* call super-class to kick it off */ - efh_parent->format_clone((EMFormat *)efh, m->message, m->format_source); - em_format_html_clear_pobject(m->format); - - if (efh->valid) { - camel_cipher_validity_free(efh->valid); - efh->valid = NULL; - efh->valid_parent = NULL; - } - - if (m->message == NULL) { - hstream = gtk_html_begin(efh->html); - gtk_html_stream_close(hstream, GTK_HTML_STREAM_OK); - mail_msg_free(m); - p->last_part = NULL; - } else { - /*hstream = gtk_html_begin(efh->html);*/ - hstream = NULL; - m->estream = (EMHTMLStream *)em_html_stream_new(efh->html, hstream); - - if (p->last_part == m->message) { - /* HACK: so we redraw in the same spot */ - /* FIXME: It doesn't work! */ - efh->html->engine->newPage = FALSE; - } else { - /* clear cache of inline-scanned text parts */ - g_hash_table_foreach(p->text_inline_parts, efh_free_inline_parts, NULL); - g_hash_table_destroy(p->text_inline_parts); - p->text_inline_parts = g_hash_table_new(NULL, NULL); - - p->last_part = m->message; - /* FIXME: Need to handle 'load if sender in addressbook' case too */ - efh->load_http_now = efh->load_http; - } - - efh->priv->format_id = m->msg.seq; - e_thread_put(mail_thread_new, (EMsg *)m); - } - - efh->priv->format_timeout_id = 0; - efh->priv->format_timeout_msg = NULL; - - return FALSE; -} - -static void efh_format_clone(EMFormat *emf, CamelMedium *part, EMFormat *emfsource) -{ - EMFormatHTML *efh = (EMFormatHTML *)emf; - struct _format_msg *m; - - /* How to sub-class ? Might need to adjust api ... */ - - if (efh->html == NULL) - return; - - d(printf("efh_format called\n")); - if (efh->priv->format_timeout_id != 0) { - d(printf(" timeout for last still active, removing ...\n")); - g_source_remove(efh->priv->format_timeout_id); - efh->priv->format_timeout_id = 0; - mail_msg_free(efh->priv->format_timeout_msg); - efh->priv->format_timeout_msg = NULL; - } - - m = mail_msg_new(&efh_format_op, NULL, sizeof(*m)); - m->format = (EMFormatHTML *)emf; - g_object_ref(emf); - m->format_source = emfsource; - if (emfsource) - g_object_ref(emfsource); - m->message = part; - if (part) - camel_object_ref(part); - - if (efh->priv->format_id == -1) { - d(printf(" idle, forcing format\n")); - efh_format_timeout(m); - } else { - d(printf(" still busy, cancelling and queuing wait\n")); - /* cancel and poll for completion */ - mail_msg_cancel(efh->priv->format_id); - efh->priv->format_timeout_msg = m; - efh->priv->format_timeout_id = g_timeout_add(100, (GSourceFunc)efh_format_timeout, m); - } -} - -static void efh_format_error(EMFormat *emf, CamelStream *stream, const char *txt) -{ - char *html; - - html = camel_text_to_html (txt, CAMEL_MIME_FILTER_TOHTML_CONVERT_NL|CAMEL_MIME_FILTER_TOHTML_CONVERT_URLS, 0); - camel_stream_printf(stream, "<em><font color=\"red\">%s</font></em><br>", html); - g_free(html); -} - -static void -efh_format_text_header(EMFormat *emf, CamelStream *stream, const char *label, const char *value, guint32 flags) -{ - char *mhtml = NULL; - const char *fmt, *html; - - if (value == NULL) - return; - - while (*value == ' ') - value++; - - if (flags & EM_FORMAT_HTML_HEADER_HTML) - html = value; - else - html = mhtml = camel_text_to_html(value, ((EMFormatHTML *)emf)->text_html_flags, 0); - - if (((EMFormatHTML *)emf)->simple_headers) { - fmt = "<b>%s</b>: %s<br>"; - } else { - if (flags & EM_FORMAT_HTML_HEADER_NOCOLUMNS) { - if (flags & EM_FORMAT_HEADER_BOLD) - fmt = "<tr><td><b>%s:</b> %s</td></tr>"; - else - fmt = "<tr><td>%s: %s</td></tr>"; - } else { - if (flags & EM_FORMAT_HEADER_BOLD) - fmt = "<tr><th align=\"right\" valign=\"top\">%s:<b> </b></th><td>%s</td></tr>"; - else - fmt = "<tr><td align=\"right\" valign=\"top\">%s:<b> </b></td><td>%s</td></tr>"; - } - } - - camel_stream_printf(stream, fmt, label, html); - g_free(mhtml); -} - -static void -efh_format_address(EMFormat *emf, CamelStream *stream, const CamelInternetAddress *cia, const char *name, guint32 flags) -{ - char *text; - - if (cia == NULL || !camel_internet_address_get(cia, 0, NULL, NULL)) - return; - - text = camel_address_format((CamelAddress *)cia); - efh_format_text_header(emf, stream, name, text, flags | EM_FORMAT_HEADER_BOLD); - g_free(text); -} - -static void -efh_format_header(EMFormat *emf, CamelStream *stream, CamelMedium *part, const char *namein, guint32 flags, const char *charset) -{ -#define msg ((CamelMimeMessage *)part) -#define efh ((EMFormatHTML *)emf) - char *name; - - name = alloca(strlen(namein)+1); - strcpy(name, namein); - camel_strdown(name); - - if (!strcmp(name, "from")) - efh_format_address(emf, stream, camel_mime_message_get_from(msg), _("From"), flags); - else if (!strcmp(name, "reply-to")) - efh_format_address(emf, stream, camel_mime_message_get_reply_to(msg), _("Reply-To"), flags); - else if (!strcmp(name, "to")) - efh_format_address(emf, stream, camel_mime_message_get_recipients(msg, CAMEL_RECIPIENT_TYPE_TO), _("To"), flags); - else if (!strcmp(name, "cc")) - efh_format_address(emf, stream, camel_mime_message_get_recipients(msg, CAMEL_RECIPIENT_TYPE_CC), _("Cc"), flags); - else if (!strcmp(name, "bcc")) - efh_format_address(emf, stream, camel_mime_message_get_recipients(msg, CAMEL_RECIPIENT_TYPE_BCC), _("Bcc"), flags); - else { - const char *txt, *label; - char *value = NULL; - - if (!strcmp(name, "subject")) { - txt = camel_mime_message_get_subject(msg); - label = _("Subject"); - flags |= EM_FORMAT_HEADER_BOLD; - } else if (!strcmp(name, "x-evolution-mailer")) { /* pseudo-header */ - txt = camel_medium_get_header(part, "x-mailer"); - if (txt == NULL) - txt = camel_medium_get_header(part, "user-agent"); - if (txt == NULL - || ((efh->xmailer_mask & EM_FORMAT_HTML_XMAILER_OTHER) == 0 - && ((efh->xmailer_mask & EM_FORMAT_HTML_XMAILER_EVOLUTION) == 0 - || strstr(txt, "Evolution") == NULL))) - return; - - label = _("Mailer"); - flags |= EM_FORMAT_HEADER_BOLD; - } else if (!strcmp(name, "date")) { - int msg_offset, local_tz; - time_t msg_date; - struct tm local; - const char *date; - - date = camel_medium_get_header(part, "date"); - if (date == NULL) - return; - - /* Show the local timezone equivalent in brackets if the sender is remote */ - msg_date = camel_header_decode_date (date, &msg_offset); - e_localtime_with_offset(msg_date, &local, &local_tz); - - /* Convert message offset to minutes (e.g. -0400 --> -240) */ - msg_offset = ((msg_offset / 100) * 60) + (msg_offset % 100); - /* Turn into offset from localtime, not UTC */ - msg_offset -= local_tz / 60; - - if (msg_offset) { - char buf[32], *html; - - msg_offset += (local.tm_hour * 60) + local.tm_min; - if (msg_offset >= (24 * 60) || msg_offset < 0) { - /* translators: strftime format for local time equivalent in Date header display, with day */ - e_utf8_strftime(buf, sizeof(buf), _("<I> (%a, %R %Z)</I>"), &local); - } else { - /* translators: strftime format for local time equivalent in Date header display, without day */ - e_utf8_strftime(buf, sizeof(buf), _("<I> (%R %Z)</I>"), &local); - } - - html = camel_text_to_html(date, ((EMFormatHTML *)emf)->text_html_flags, 0); - txt = value = g_strdup_printf("%s %s", html, buf); - g_free(html); - flags |= EM_FORMAT_HTML_HEADER_HTML; - } else { - txt = date; - } - - label = _("Date"); - flags |= EM_FORMAT_HEADER_BOLD; - } else { - txt = camel_medium_get_header(part, name); - value = camel_header_decode_string (txt, charset); - txt = value; - label = namein; - } - - efh_format_text_header(emf, stream, label, txt, flags); - g_free(value); - } -#undef msg -#undef efh -} - -void -em_format_html_format_headers(EMFormatHTML *efh, CamelStream *stream, CamelMedium *part) -{ - EMFormatHeader *h; - const char *charset; - CamelContentType *ct; -#define emf ((EMFormat *)efh) - - ct = camel_mime_part_get_content_type((CamelMimePart *)part); - charset = camel_content_type_param (ct, "charset"); - charset = e_iconv_charset_name(charset); - - if (!efh->simple_headers) - camel_stream_printf(stream, - "<font color=\"#%06x\">\n" - "<table cellpadding=0>\n", - efh->text_colour & 0xffffff); - - /* dump selected headers */ - h = (EMFormatHeader *)emf->header_list.head; - if (h->next == NULL || emf->mode == EM_FORMAT_ALLHEADERS) { - struct _camel_header_raw *header; - - header = ((CamelMimePart *)part)->headers; - while (header) { - efh_format_header(emf, stream, part, header->name, EM_FORMAT_HTML_HEADER_NOCOLUMNS, charset); - header = header->next; - } - } else { - while (h->next) { - efh_format_header(emf, stream, part, h->name, h->flags, charset); - h = h->next; - } - } - - if (!efh->simple_headers) - camel_stream_printf(stream, - "</table>\n</font>\n"); -#undef emf -} - -static void efh_format_message(EMFormat *emf, CamelStream *stream, CamelMedium *part) -{ -#define efh ((EMFormatHTML *)emf) - CamelCipherValidity *save = efh->valid, *save_parent = efh->valid_parent; - - efh->valid = NULL; - efh->valid_parent = NULL; - - if (emf->message != part) - camel_stream_printf(stream, "<blockquote>\n"); - - if (!efh->hide_headers) - em_format_html_format_headers(efh, stream, part); - - camel_stream_printf(stream, "<table height=6><tr><td><a></a></td></tr></table>\n"); - em_format_part(emf, stream, (CamelMimePart *)part); - - if (emf->message != part) - camel_stream_printf(stream, "</blockquote>\n"); - - camel_cipher_validity_free(efh->valid); - - efh->valid = save; - efh->valid_parent = save_parent; -#undef efh -} - -static void efh_format_source(EMFormat *emf, CamelStream *stream, CamelMimePart *part) -{ - CamelStreamFilter *filtered_stream; - CamelMimeFilter *html_filter; - CamelDataWrapper *dw = (CamelDataWrapper *)part; - - filtered_stream = camel_stream_filter_new_with_stream ((CamelStream *) stream); - html_filter = camel_mime_filter_tohtml_new (CAMEL_MIME_FILTER_TOHTML_CONVERT_NL - | CAMEL_MIME_FILTER_TOHTML_CONVERT_SPACES - | CAMEL_MIME_FILTER_TOHTML_ESCAPE_8BIT, 0); - camel_stream_filter_add(filtered_stream, html_filter); - camel_object_unref(html_filter); - - camel_stream_write_string((CamelStream *)stream, EFH_TABLE_OPEN "<tr><td><tt>"); - em_format_format_text(emf, (CamelStream *)filtered_stream, dw); - camel_object_unref(filtered_stream); - - camel_stream_write_string(stream, "</tt></td></tr></table>"); -} - -static void -efh_format_attachment(EMFormat *emf, CamelStream *stream, CamelMimePart *part, const char *mime_type, const EMFormatHandler *handle) -{ - char *text, *html; - - /* we display all inlined attachments only */ - - /* this could probably be cleaned up ... */ - camel_stream_write_string(stream, - "<table border=1 cellspacing=0 cellpadding=0><tr><td>" - "<table width=10 cellspacing=0 cellpadding=0>" - "<tr><td></td></tr></table></td>" - "<td><table width=3 cellspacing=0 cellpadding=0>" - "<tr><td></td></tr></table></td><td><font size=-1>\n"); - - /* output some info about it */ - text = em_format_describe_part(part, mime_type); - html = camel_text_to_html(text, ((EMFormatHTML *)emf)->text_html_flags & CAMEL_MIME_FILTER_TOHTML_CONVERT_URLS, 0); - camel_stream_write_string(stream, html); - g_free(html); - g_free(text); - - camel_stream_write_string(stream, "</font></td></tr><tr></table>"); - - if (handle && em_format_is_inline(emf, part)) - handle->handler(emf, stream, part, handle); -} - -static gboolean -efh_busy(EMFormat *emf) -{ - return (((EMFormatHTML *)emf)->priv->format_id != -1); -} |