aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorDan Winship <danw@helixcode.com>2000-04-15 05:11:56 +0800
committerDan Winship <danw@src.gnome.org>2000-04-15 05:11:56 +0800
commitb1724572527683ad15995f2da5896fb41b592783 (patch)
tree210d68475832cd1259f60c44f398d5f5f8e866e1
parentcb0371df5be2888cc2d0ad4c360642ab80e89ec9 (diff)
downloadgsoc2013-evolution-b1724572527683ad15995f2da5896fb41b592783.tar
gsoc2013-evolution-b1724572527683ad15995f2da5896fb41b592783.tar.gz
gsoc2013-evolution-b1724572527683ad15995f2da5896fb41b592783.tar.bz2
gsoc2013-evolution-b1724572527683ad15995f2da5896fb41b592783.tar.lz
gsoc2013-evolution-b1724572527683ad15995f2da5896fb41b592783.tar.xz
gsoc2013-evolution-b1724572527683ad15995f2da5896fb41b592783.tar.zst
gsoc2013-evolution-b1724572527683ad15995f2da5896fb41b592783.zip
Moved from camel/camel-formatter, and changed slightly. (More to come.)
2000-04-14 Dan Winship <danw@helixcode.com> * mail-format.[ch]: Moved from camel/camel-formatter, and changed slightly. (More to come.) * html-stream.[ch]: No longer necessary. mail-format uses GtkHTMLStreamHandles directly. * mail-display.[ch]: update for new message formatting code. svn path=/trunk/; revision=2438
-rw-r--r--mail/ChangeLog10
-rw-r--r--mail/Makefile.am6
-rw-r--r--mail/html-stream.c139
-rw-r--r--mail/html-stream.h26
-rw-r--r--mail/mail-display.c102
-rw-r--r--mail/mail-display.h3
-rw-r--r--mail/mail-format.c793
-rw-r--r--mail/mail-format.h53
8 files changed, 893 insertions, 239 deletions
diff --git a/mail/ChangeLog b/mail/ChangeLog
index 2c661c15e9..edf0e629f5 100644
--- a/mail/ChangeLog
+++ b/mail/ChangeLog
@@ -1,3 +1,13 @@
+2000-04-14 Dan Winship <danw@helixcode.com>
+
+ * mail-format.[ch]: Moved from camel/camel-formatter, and changed
+ slightly. (More to come.)
+
+ * html-stream.[ch]: No longer necessary. mail-format uses
+ GtkHTMLStreamHandles directly.
+
+ * mail-display.[ch]: update for new message formatting code.
+
2000-04-14 Chris Toshok <toshok@helixcode.com>
* folder-browser-factory.c (control_activate): use
diff --git a/mail/Makefile.am b/mail/Makefile.am
index 189faf1dcd..cbe029af2b 100644
--- a/mail/Makefile.am
+++ b/mail/Makefile.am
@@ -29,10 +29,10 @@ evolution_mail_SOURCES = \
folder-browser.h \
folder-browser-factory.c \
folder-browser-factory.h \
- html-stream.c \
- html-stream.h \
- mail-display.h \
mail-display.c \
+ mail-display.h \
+ mail-format.c \
+ mail-format.h \
mail-ops.c \
mail-ops.h \
main.c \
diff --git a/mail/html-stream.c b/mail/html-stream.c
deleted file mode 100644
index bf88823a33..0000000000
--- a/mail/html-stream.c
+++ /dev/null
@@ -1,139 +0,0 @@
-/* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- */
-/*
- * html-stream.c: A CamelStream class that feeds data into a GtkHTML widget
- *
- * Authors:
- * Miguel de Icaza (miguel@helixcode.com)
- * Bertrand Guiheneuf (bg@aful.org)
- *
- * (C) 2000 Helix Code, Inc.
- */
-#include <config.h>
-#include "html-stream.h"
-#include "e-util/e-util.h"
-
-#define PARENT_TYPE camel_stream_get_type ()
-
-static GtkObjectClass *html_stream_parent_class;
-
-/*
- * CamelStream::read method
- *
- * Return 0 bytes read, as this is a write-only stream
- */
-static gint
-html_stream_read (CamelStream *stream, gchar *buffer, gint n)
-{
- return 0;
-}
-
-/*
- * CamelStream::write method
- *
- * Writes @buffer into the HTML widget
- */
-static gint
-html_stream_write (CamelStream *stream, const gchar *buffer, gint n)
-{
- HTMLStream *html_stream = HTML_STREAM (stream);
-
- if (html_stream->gtk_html_stream)
- gtk_html_write (html_stream->gtk_html, html_stream->gtk_html_stream, buffer, n);
- else
- n = 0;
-
- return n;
-}
-
-/*
- * CamelStream::Reset method
- *
- * Reset the html widget that is, prepare it
- * for a new display
- */
-static void
-html_stream_reset (CamelStream *stream)
-{
- HTMLStream *html_stream = HTML_STREAM (stream);
-
- if (html_stream->gtk_html_stream)
- gtk_html_end (html_stream->gtk_html, html_stream->gtk_html_stream, GTK_HTML_STREAM_OK);
-
- html_stream->gtk_html_stream = gtk_html_begin (html_stream->gtk_html, "");
-}
-
-/*
- * CamelStream::available method
- *
- * Return 0, as this is only a write-stream
- */
-static gint
-html_stream_available (CamelStream *stream)
-{
- return 0;
-}
-
-/*
- * CamelStream::eos method.
- *
- * We just return TRUE, as this is not a read-stream
- */
-static gboolean
-html_stream_eos (CamelStream *stream)
-{
- return TRUE;
-}
-
-static void
-html_stream_close (CamelStream *stream)
-{
- HTMLStream *html_stream = HTML_STREAM (stream);
-
- gtk_html_end (html_stream->gtk_html, html_stream->gtk_html_stream, GTK_HTML_STREAM_OK);
- html_stream->gtk_html_stream = NULL;
-}
-
-static void
-html_stream_destroy (GtkObject *object)
-{
-}
-
-static void
-html_stream_class_init (GtkObjectClass *object_class)
-{
- CamelStreamClass *stream_class = (CamelStreamClass *) object_class;
-
- html_stream_parent_class = gtk_type_class (PARENT_TYPE);
-
- object_class->destroy = html_stream_destroy;
-
- stream_class->read = html_stream_read;
- stream_class->write = html_stream_write;
- stream_class->reset = html_stream_reset;
- stream_class->available = html_stream_available;
- stream_class->eos = html_stream_eos;
- stream_class->close = html_stream_close;
-}
-
-CamelStream *
-html_stream_new (GtkHTML *html)
-{
- HTMLStream *html_stream;
-
- g_return_val_if_fail (html != NULL, NULL);
- g_return_val_if_fail (GTK_IS_HTML (html), NULL);
-
- html_stream = gtk_type_new (html_stream_get_type ());
-
- gtk_object_ref (GTK_OBJECT (html));
-
- html_stream->gtk_html_stream = gtk_html_begin (html, "");
-
- html_stream->gtk_html = html;
-
- return CAMEL_STREAM (html_stream);
-}
-
-E_MAKE_TYPE (html_stream, "HTMLStream", HTMLStream, html_stream_class_init, NULL, PARENT_TYPE);
-
-
diff --git a/mail/html-stream.h b/mail/html-stream.h
deleted file mode 100644
index ffa0f4751c..0000000000
--- a/mail/html-stream.h
+++ /dev/null
@@ -1,26 +0,0 @@
-#ifndef _HTML_STREAM_H_
-#define _HTML_STREAM_H_ 1
-
-#include <gtkhtml/gtkhtml.h>
-#include "camel/camel-stream.h"
-
-#define HTML_STREAM_TYPE (html_stream_get_type ())
-#define HTML_STREAM(obj) (GTK_CHECK_CAST((obj), HTML_STREAM_TYPE, HTMLStream))
-#define HTML_STREAM_CLASS(k) (GTK_CHECK_CLASS_CAST ((k), HTML_STREAM_TYPE, HTMLStreamClass))
-#define IS_HTML_STREAM(o) (GTK_CHECK_TYPE((o), HTML_STREAM_TYPE))
-
-typedef struct {
- CamelStream parent_object;
- GtkHTML *gtk_html;
- GtkHTMLStreamHandle *gtk_html_stream;
-} HTMLStream;
-
-typedef struct {
- CamelStreamClass parent_class;
-} HTMLStreamClass;
-
-
-GtkType html_stream_get_type (void);
-CamelStream *html_stream_new (GtkHTML *html);
-
-#endif /* _HTML_STREAM_H_ */
diff --git a/mail/mail-display.c b/mail/mail-display.c
index fa3d3588eb..d36cf5cd20 100644
--- a/mail/mail-display.c
+++ b/mail/mail-display.c
@@ -12,8 +12,7 @@
#include <gnome.h>
#include "e-util/e-util.h"
#include "mail-display.h"
-#include "html-stream.h"
-#include "camel/camel-formatter.h"
+#include "mail-format.h"
/* corba/bonobo stuff */
#include <bonobo.h>
@@ -276,14 +275,8 @@ void
mail_display_set_message (MailDisplay *mail_display,
CamelMedium *medium)
{
- CamelFormatter *camel_formatter;
+ GtkHTMLStreamHandle *headers_stream, *body_stream;
- /* okay, we should not create a formatter
- * each time we need to display a message
- * but I don't know how the formatter reacts
- * to consecutive call to *_to_html - ber */
- camel_formatter = camel_formatter_new ();
-
/*
* for the moment, camel-formatter deals only with
* mime messages, but in the future, it should be
@@ -292,70 +285,52 @@ mail_display_set_message (MailDisplay *mail_display,
* fact, only the medium class has the distinction
* header / body
*/
- if (CAMEL_IS_MIME_MESSAGE (medium)) {
-
- /* we were given a reference to the message in the last call
- * to mail_display_set_message, free it now. */
- if (mail_display->current_message)
- gtk_object_unref (GTK_OBJECT (mail_display->current_message));
-
- mail_display->current_message = CAMEL_MIME_MESSAGE (medium);
- /*
- * reset the html stream to clean
- * the gtkhtml widget
- */
- camel_stream_reset (mail_display->body_stream);
- camel_stream_reset (mail_display->headers_stream);
-
- /*
- * convert the message into html
- * and stream the result to the gtkhtml
- * widgets
- */
- camel_stream_write_string (mail_display->headers_stream, "\n\
+ if (!CAMEL_IS_MIME_MESSAGE (medium))
+ return;
+
+ /* we were given a reference to the message in the last call
+ * to mail_display_set_message, free it now. */
+ if (mail_display->current_message)
+ gtk_object_unref (GTK_OBJECT (mail_display->current_message));
+
+ mail_display->current_message = CAMEL_MIME_MESSAGE (medium);
+
+ headers_stream = gtk_html_begin (mail_display->headers_html_widget, "");
+ body_stream = gtk_html_begin (mail_display->body_html_widget, "");
+
+ /* Convert the message into html and stream the result to the
+ * gtkhtml widgets.
+ */
+ mail_write_html (headers_stream, "\n\
<!doctype html public \"-//w3c//dtd html 4.0 transitional//en\">\n\
<html>\n\
<head>\n\
<meta name=\"GENERATOR\" content=\"Evolution Mail Component (Rhon Rhon release)\">\n\
</head>\n\
-<body text=\"#000000\" bgcolor=\"#999999\">\n\
+<body text=\"#000000\" bgcolor=\"#EEEEEE\">\n\
<font>\n\
-");;
+");
- camel_stream_write_string (mail_display->body_stream, "\n\
+ mail_write_html (body_stream, "\n\
<!doctype html public \"-//w3c//dtd html 4.0 transitional//en\">\n\
<html>\n\
<head>\n\
<meta name=\"GENERATOR\" content=\"Evolution Mail Component (Rhon Rhon release)\">\n\
</head>\n\
<body text=\"#000000\" bgcolor=\"#FFFFFF\">\n\
-");;
+");
+ mail_format_mime_message (CAMEL_MIME_MESSAGE (medium),
+ headers_stream,
+ body_stream);
- camel_formatter_mime_message_to_html
- (camel_formatter,
- CAMEL_MIME_MESSAGE (medium),
- mail_display->headers_stream,
- mail_display->body_stream);
-
-
- gtk_object_unref (GTK_OBJECT (camel_formatter));
-
- camel_stream_write_string (mail_display->headers_stream, "\n\
-</font>\n\
-</body>\n\
-</html>\n\
-");;
+ mail_write_html (headers_stream, "\n</font>\n</body>\n</html>\n");
+ mail_write_html (body_stream, "\n</body>\n</html>\n");
- camel_stream_write_string (mail_display->body_stream, "\n\
-</body>\n\
-</html>\n\
-");;
-
- camel_stream_close (mail_display->body_stream);
- camel_stream_close (mail_display->headers_stream);
-
- }
+ gtk_html_end (mail_display->headers_html_widget, headers_stream,
+ GTK_HTML_STREAM_OK);
+ gtk_html_end (mail_display->body_html_widget, body_stream,
+ GTK_HTML_STREAM_OK);
}
@@ -369,8 +344,7 @@ mail_display_init (GtkObject *object)
MailDisplay *mail_display = MAIL_DISPLAY (object);
/* create the headers html widget */
- mail_display->headers_html_widget = (GtkHTML *) gtk_html_new ();
- mail_display->headers_stream = html_stream_new (mail_display->headers_html_widget);
+ mail_display->headers_html_widget = (GtkHTML *) gtk_html_new ();
gtk_widget_show (GTK_WIDGET (mail_display->headers_html_widget));
/* create the body html widget */
@@ -379,8 +353,6 @@ mail_display_init (GtkObject *object)
"object_requested",
GTK_SIGNAL_FUNC (on_object_requested),
NULL);
-
- mail_display->body_stream = html_stream_new (mail_display->body_html_widget);
gtk_widget_show (GTK_WIDGET (mail_display->body_html_widget));
/* various other initializations */
@@ -435,7 +407,7 @@ mail_display_new (FolderBrowser *parent_folder_browser)
gtk_widget_show (frame_wnd);
gtk_container_add (GTK_CONTAINER (scroll_wnd),
GTK_WIDGET (mail_display->headers_html_widget));
- gtk_widget_set_usize (GTK_WIDGET (scroll_wnd), -1, 50);
+ gtk_widget_set_usize (GTK_WIDGET (scroll_wnd), -1, 100);
/* add it on the top part of the table */
gtk_table_attach (table, GTK_WIDGET (frame_wnd),
0, 1, 0, 1,
@@ -458,12 +430,6 @@ mail_display_new (FolderBrowser *parent_folder_browser)
0, 1, 1, 2,
GTK_EXPAND | GTK_FILL, GTK_EXPAND | GTK_FILL, 0, 0);
-
- /* write the default text to the html widgets */
- camel_stream_write_string (mail_display->headers_stream, default_header_html_string);
- camel_stream_write_string (mail_display->body_stream, default_body_html_string);
-
-
return GTK_WIDGET (mail_display);
}
diff --git a/mail/mail-display.h b/mail/mail-display.h
index 81f1bcb38f..685bd4c8f8 100644
--- a/mail/mail-display.h
+++ b/mail/mail-display.h
@@ -33,10 +33,7 @@ struct _MailDisplay {
FolderBrowser *parent_folder_browser;
GtkHTML * headers_html_widget;
- CamelStream * headers_stream;
-
GtkHTML * body_html_widget;
- CamelStream * body_stream;
CamelMimeMessage *current_message;
};
diff --git a/mail/mail-format.c b/mail/mail-format.c
new file mode 100644
index 0000000000..08c6e81073
--- /dev/null
+++ b/mail/mail-format.c
@@ -0,0 +1,793 @@
+/* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- */
+
+/*
+ * Author :
+ * Matt Loper <matt@helixcode.com>
+ *
+ * Copyright 2000, Helix Code, Inc. (http://www.helixcode.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.
+ *
+ */
+
+#include <config.h>
+#include "mail-format.h"
+#include "camel/hash-table-utils.h"
+
+#include <libgnome/libgnome.h>
+#include <ctype.h> /* for isprint */
+#include <string.h> /* for strstr */
+
+/* We shouldn't be doing this, but I don't feel like fixing it right
+ * now. (It's for gtk_html_stream_write.) When gtkhtml has nicer
+ * interfaces, we can fix it.
+ */
+#include <gtkhtml/gtkhtml-private.h>
+
+static void handle_text_plain (CamelDataWrapper *wrapper,
+ GtkHTMLStreamHandle *stream,
+ CamelDataWrapper *root);
+static void handle_text_html (CamelDataWrapper *wrapper,
+ GtkHTMLStreamHandle *stream,
+ CamelDataWrapper *root);
+static void handle_image (CamelDataWrapper *wrapper,
+ GtkHTMLStreamHandle *stream,
+ CamelDataWrapper *root);
+static void handle_vcard (CamelDataWrapper *wrapper,
+ GtkHTMLStreamHandle *stream,
+ CamelDataWrapper *root);
+static void handle_mime_part (CamelDataWrapper *wrapper,
+ GtkHTMLStreamHandle *stream,
+ CamelDataWrapper *root);
+static void handle_multipart_mixed (CamelDataWrapper *wrapper,
+ GtkHTMLStreamHandle *stream,
+ CamelDataWrapper *root);
+static void handle_multipart_related (CamelDataWrapper *wrapper,
+ GtkHTMLStreamHandle *stream,
+ CamelDataWrapper *root);
+static void handle_multipart_alternative(CamelDataWrapper *wrapper,
+ GtkHTMLStreamHandle *stream,
+ CamelDataWrapper *root);
+static void handle_unknown_type (CamelDataWrapper *wrapper,
+ GtkHTMLStreamHandle *stream,
+ CamelDataWrapper *root);
+
+/* encodes some characters into their 'escaped' version;
+ * so '<' turns into '&lt;', and '"' turns into '&quot;' */
+static gchar *text_to_html (const guchar *input,
+ guint len,
+ guint *encoded_len_return,
+ gboolean convert_newlines_to_br);
+
+/* writes the header info for a mime message into an html stream */
+static void write_header_info_to_stream (CamelMimeMessage* mime_message,
+ GtkHTMLStreamHandle *stream);
+
+/* dispatch html printing via mimetype */
+static void call_handler_function (CamelDataWrapper *wrapper,
+ gchar *mimetype_whole,
+ gchar *mimetype_main,
+ GtkHTMLStreamHandle *stream,
+ CamelDataWrapper *root);
+
+#if 0
+/**
+ * camel_formatter_wrapper_to_html:
+ * @formatter: the camel formatter object
+ * @data_wrapper: the data wrapper
+ * @stream: byte stream where data will be written
+ *
+ * Writes a CamelDataWrapper out, as html, into a stream passed in as
+ * a parameter.
+ **/
+void camel_formatter_wrapper_to_html (CamelFormatter* formatter,
+ CamelDataWrapper* data_wrapper,
+ CamelStream* stream_out)
+{
+ CamelFormatterPrivate* fmt = formatter->priv;
+ gchar *mimetype_whole =
+ g_strdup_printf ("%s/%s",
+ data_wrapper->mime_type->type,
+ data_wrapper->mime_type->subtype);
+
+ debug ("camel_formatter_wrapper_to_html: entered\n");
+ g_assert (formatter && data_wrapper && stream_out);
+
+ /* give the root CamelDataWrapper and the stream to the formatter */
+ initialize_camel_formatter (formatter, data_wrapper, stream_out);
+
+ if (stream_out) {
+
+ /* write everything to the stream */
+ camel_stream_write_string (
+ fmt->stream, "<html><body bgcolor=\"white\">\n");
+ call_handler_function (
+ formatter,
+ data_wrapper,
+ mimetype_whole,
+ data_wrapper->mime_type->type);
+
+ camel_stream_write_string (fmt->stream, "\n</body></html>\n");
+ }
+
+
+ g_free (mimetype_whole);
+}
+#endif
+
+/**
+ * mail_format_mime_message:
+ * @mime_message: the input mime message
+ * @header_stream: HTML stream to write headers to
+ * @body_stream: HTML stream to write data to
+ *
+ * Writes a CamelMimeMessage out, as html, into streams passed in as
+ * a parameter. Either stream may be #NULL.
+ **/
+void
+mail_format_mime_message (CamelMimeMessage *mime_message,
+ GtkHTMLStreamHandle *header_stream,
+ GtkHTMLStreamHandle *body_stream)
+{
+ g_return_if_fail (CAMEL_IS_MIME_MESSAGE (mime_message));
+
+ /* Write the headers fields out as HTML to the header stream. */
+ if (header_stream)
+ write_header_info_to_stream (mime_message, header_stream);
+
+ /* Write the contents of the MIME message to the body stream. */
+ if (body_stream) {
+ mail_write_html (body_stream, "<html><body>\n");
+ call_handler_function (CAMEL_DATA_WRAPPER (mime_message),
+ "message/rfc822",
+ "message",
+ body_stream,
+ CAMEL_DATA_WRAPPER (mime_message));
+ mail_write_html (body_stream, "\n</body></html>\n");
+ }
+}
+
+/* We're maintaining a hashtable of mimetypes -> functions;
+ * Those functions have the following signature...
+ */
+typedef void (*mime_handler_fn) (CamelDataWrapper *wrapper,
+ GtkHTMLStreamHandle *stream,
+ CamelDataWrapper *root);
+
+static gchar*
+lookup_unique_id (CamelDataWrapper *root, CamelDataWrapper *child)
+{
+ /* ** FIXME : replace this with a string representing
+ the location of the objetc in the tree */
+ /* TODO: assert our return value != NULL */
+
+ gchar *temp_hack_uid;
+
+ temp_hack_uid = g_strdup_printf ("%p", camel_data_wrapper_get_output_stream (child));
+
+ return temp_hack_uid;
+}
+
+static GHashTable *mime_function_table;
+
+/* This tries to create a tag, given a mimetype and the child of a
+ * mime message. It can return NULL if it can't match the mimetype to
+ * a bonobo object.
+ */
+static gchar *
+get_bonobo_tag_for_object (CamelDataWrapper *wrapper,
+ gchar *mimetype, CamelDataWrapper *root)
+{
+ char *uid = lookup_unique_id (root, wrapper);
+ const char *goad_id = gnome_mime_get_value (mimetype,
+ "bonobo-goad-id");
+
+ if (goad_id) {
+ return g_strdup_printf ("<object classid=\"%s\"> "
+ "<param name=\"uid\" "
+ "value=\"camel://%s\"> </object>",
+ goad_id, uid);
+ } else
+ return NULL;
+}
+
+
+/*
+ * This takes a mimetype, and tries to map that mimetype to a function
+ * or a bonobo object.
+ *
+ * - If it's mapped to a bonobo object, this function prints a tag
+ * into the stream, designating the bonobo object and a place that
+ * the bonobo object can find data to hydrate from
+ *
+ * - otherwise, the mimetype is mapped to another function, which can
+ * print into the stream
+ */
+static void
+call_handler_function (CamelDataWrapper *wrapper,
+ gchar *mimetype_whole_in, /* ex. "image/jpeg" */
+ gchar *mimetype_main_in, /* ex. "image" */
+ GtkHTMLStreamHandle *stream,
+ CamelDataWrapper *root)
+{
+ mime_handler_fn handler_function = NULL;
+ gchar *mimetype_whole = NULL;
+ gchar *mimetype_main = NULL;
+
+ g_return_if_fail (mimetype_whole_in || mimetype_main_in);
+ g_return_if_fail (CAMEL_IS_DATA_WRAPPER (wrapper));
+ g_return_if_fail (CAMEL_IS_DATA_WRAPPER (root));
+
+ if (mime_function_table == NULL) {
+ mime_function_table = g_hash_table_new (g_strcase_hash,
+ g_strcase_equal);
+
+ /* hook up mime types to functions that handle them */
+ g_hash_table_insert (mime_function_table, "text/plain",
+ handle_text_plain);
+ g_hash_table_insert (mime_function_table, "text/richtext",
+ handle_text_plain);
+ g_hash_table_insert (mime_function_table, "text/html",
+ handle_text_html);
+ g_hash_table_insert (mime_function_table, "multipart/alternative",
+ handle_multipart_alternative);
+ g_hash_table_insert (mime_function_table, "multipart/related",
+ handle_multipart_related);
+ g_hash_table_insert (mime_function_table, "multipart/mixed",
+ handle_multipart_mixed);
+ g_hash_table_insert (mime_function_table, "message/rfc822",
+ handle_mime_part);
+ g_hash_table_insert (mime_function_table, "image",
+ handle_image);
+ g_hash_table_insert (mime_function_table, "vcard",
+ handle_vcard);
+
+ /* RFC 2046 says unrecognized multipart subtypes should
+ * be treated like multipart/mixed.
+ */
+ g_hash_table_insert (mime_function_table, "multipart",
+ handle_multipart_mixed);
+
+ /* Body parts don't have mime parts per se, so Camel
+ * sticks on the following one.
+ */
+ g_hash_table_insert (mime_function_table, "mime/body-part",
+ handle_mime_part);
+ }
+
+ /* Try to find a handler function in our own lookup table */
+ if (mimetype_whole_in) {
+ mimetype_whole = g_strdup (mimetype_whole_in);
+ g_strdown (mimetype_whole);
+
+ handler_function = g_hash_table_lookup (mime_function_table,
+ mimetype_whole);
+ }
+
+ if (mimetype_main_in && !handler_function) {
+ mimetype_main = g_strdup (mimetype_main_in);
+ g_strdown (mimetype_main);
+
+ handler_function = g_hash_table_lookup (mime_function_table,
+ mimetype_main);
+ }
+
+ /* Upon failure, try to find a bonobo object to show the object */
+ if (!handler_function) {
+ gchar *bonobo_tag = NULL;
+
+ if (mimetype_whole)
+ bonobo_tag = get_bonobo_tag_for_object (
+ wrapper, mimetype_whole, root);
+
+ if (mimetype_main && !bonobo_tag)
+ bonobo_tag = get_bonobo_tag_for_object (
+ wrapper, mimetype_main, root);
+
+ if (bonobo_tag) {
+ /* We can print a tag, and return! */
+
+ mail_write_html (stream, bonobo_tag);
+ g_free (bonobo_tag);
+ if (mimetype_whole)
+ g_free (mimetype_whole);
+ if (mimetype_main)
+ g_free (mimetype_main);
+
+ return;
+ }
+ }
+
+ /* Use either a handler function we've found, or a default handler. */
+ if (handler_function)
+ (*handler_function) (wrapper, stream, root);
+ else
+ handle_unknown_type (wrapper, stream, root);
+ if (mimetype_whole)
+ g_free (mimetype_whole);
+ if (mimetype_main)
+ g_free (mimetype_main);
+}
+
+
+/* Convert plain text in equivalent-looking valid HTML. */
+static gchar *
+text_to_html (const guchar *input, guint len,
+ guint *encoded_len_return,
+ gboolean convert_newlines_to_br)
+{
+ const guchar *cur = input;
+ guchar *buffer = NULL;
+ guchar *out = NULL;
+ gint buffer_size = 0;
+ guint count;
+
+ /* Allocate a translation buffer. */
+ buffer_size = len * 2;
+ buffer = g_malloc (buffer_size);
+
+ out = buffer;
+ count = 0;
+
+ while (len--) {
+ if (out - buffer > buffer_size - 100) {
+ gint index = out - buffer;
+
+ buffer_size *= 2;
+ buffer = g_realloc (buffer, buffer_size);
+ out = buffer + index;
+ }
+
+ switch (*cur) {
+ case '<':
+ strcpy (out, "&lt;");
+ out += 4;
+ break;
+
+ case '>':
+ strcpy (out, "&gt;");
+ out += 4;
+ break;
+
+ case '&':
+ strcpy (out, "&amp;");
+ out += 5;
+ break;
+
+ case '"':
+ strcpy (out, "&quot;");
+ out += 6;
+ break;
+
+ case '\n':
+ *out++ = *cur;
+ if (convert_newlines_to_br) {
+ strcpy (out, "<br>");
+ out += 4;
+ }
+ break;
+
+ default:
+ if ((*cur >= 0x20 && *cur < 0x80) ||
+ (*cur == '\r' || *cur == '\t')) {
+ /* Default case, just copy. */
+ *out++ = *cur;
+ } else
+ out += g_snprintf(out, 9, "&#%d;", *cur);
+ break;
+ }
+
+ cur++;
+ }
+
+ *out = '\0';
+ if (encoded_len_return)
+ *encoded_len_return = out - buffer;
+
+ return buffer;
+}
+
+
+static void
+write_field_to_stream (const gchar *description, const gchar *value,
+ GtkHTMLStreamHandle *stream)
+{
+ gchar *s;
+ gchar *encoded_value;
+
+ if (value) {
+ unsigned char *p;
+
+ encoded_value = text_to_html (value, strlen(value),
+ NULL, TRUE);
+ for (p = (unsigned char *)encoded_value; *p; p++) {
+ if (!isprint (*p))
+ *p = '?';
+ }
+ } else
+ encoded_value = g_strdup ("");
+
+ s = g_strdup_printf ("<tr valign=top><th align=right>%s</th>"
+ "<td>%s</td></tr>", description, encoded_value);
+ mail_write_html (stream, s);
+ g_free (encoded_value);
+ g_free (s);
+}
+
+static void
+write_recipients_to_stream (const gchar *recipient_type,
+ const GList *recipients,
+ GtkHTMLStreamHandle *stream)
+{
+ gchar *recipients_string = NULL;
+
+ while (recipients) {
+ gchar *old_string = recipients_string;
+ recipients_string =
+ g_strdup_printf ("%s%s%s",
+ old_string ? old_string : "",
+ old_string ? "; " : "",
+ (gchar *)recipients->data);
+ g_free (old_string);
+
+ recipients = recipients->next;
+ }
+
+ write_field_to_stream (recipient_type, recipients_string, stream);
+ g_free (recipients_string);
+}
+
+
+
+static void
+write_header_info_to_stream (CamelMimeMessage *mime_message,
+ GtkHTMLStreamHandle *stream)
+{
+ GList *recipients;
+
+ mail_write_html (stream, "<table>");
+
+ /* A few fields will probably be available from the mime_message;
+ * for each one that's available, write it to the output stream
+ * with a helper function, 'write_field_to_stream'.
+ */
+
+ write_field_to_stream ("From:",
+ camel_mime_message_get_from (mime_message),
+ stream);
+
+ write_recipients_to_stream ("To:",
+ camel_mime_message_get_recipients (mime_message, CAMEL_RECIPIENT_TYPE_TO),
+ stream);
+
+ recipients = camel_mime_message_get_recipients (mime_message, CAMEL_RECIPIENT_TYPE_CC);
+ if (recipients)
+ write_recipients_to_stream ("Cc:", recipients, stream);
+ write_field_to_stream ("Subject:",
+ camel_mime_message_get_subject (mime_message),
+ stream);
+
+ mail_write_html (stream, "</table>");
+}
+
+/* case-insensitive string comparison */
+static gint
+strcase_equal (gconstpointer v, gconstpointer v2)
+{
+ return g_strcasecmp ((const gchar*) v, (const gchar*)v2) == 0;
+}
+
+#define MIME_TYPE_WHOLE(a) (gmime_content_field_get_mime_type ( \
+ camel_mime_part_get_content_type (CAMEL_MIME_PART (a))))
+#define MIME_TYPE_MAIN(a) ((camel_mime_part_get_content_type (CAMEL_MIME_PART (a)))->type)
+#define MIME_TYPE_SUB(a) ((camel_mime_part_get_content_type (CAMEL_MIME_PART (a)))->subtype)
+
+
+/*----------------------------------------------------------------------*
+ * Mime handling functions
+ *----------------------------------------------------------------------*/
+
+static void
+handle_text_plain (CamelDataWrapper *wrapper, GtkHTMLStreamHandle *stream,
+ CamelDataWrapper *root)
+{
+ gchar *text;
+ CamelStream *wrapper_output_stream;
+ gchar tmp_buffer[4096];
+ gint nb_bytes_read;
+ gboolean empty_text = TRUE;
+
+
+ mail_write_html (stream, "\n<!-- text/plain below -->\n");
+ mail_write_html (stream, "<pre>\n");
+
+ /* FIXME: text/richtext is not difficult to translate into HTML */
+ if (strcmp (wrapper->mime_type->subtype, "richtext") == 0) {
+ mail_write_html (stream, "<center><b>"
+ "<table bgcolor=\"b0b0ff\" cellpadding=3>"
+ "<tr><td>Warning: the following "
+ "richtext may not be formatted correctly. "
+ "</b></td></tr></table></center><br>");
+ }
+
+ /* Get the output stream of the data wrapper. */
+ wrapper_output_stream = camel_data_wrapper_get_output_stream (wrapper);
+ camel_stream_reset (wrapper_output_stream);
+
+ do {
+ /* Read next chunk of text. */
+ nb_bytes_read = camel_stream_read (wrapper_output_stream,
+ tmp_buffer, 4096);
+
+ /* If there's any text, write it to the stream. */
+ if (nb_bytes_read > 0) {
+ int returned_strlen;
+
+ empty_text = FALSE;
+
+ /* replace '<' with '&lt;', etc. */
+ text = text_to_html (tmp_buffer,
+ nb_bytes_read,
+ &returned_strlen,
+ FALSE);
+ mail_write_html (stream, text);
+ g_free (text);
+ }
+ } while (!camel_stream_eos (wrapper_output_stream));
+
+ if (empty_text)
+ mail_write_html (stream, "<b>(empty)</b>");
+
+ mail_write_html (stream, "</pre>\n");
+}
+
+static void
+handle_text_html (CamelDataWrapper *wrapper, GtkHTMLStreamHandle *stream,
+ CamelDataWrapper *root)
+{
+ CamelStream *wrapper_output_stream;
+ gchar tmp_buffer[4096];
+ gint nb_bytes_read;
+ gboolean empty_text = TRUE;
+
+ /* Get the output stream of the data wrapper. */
+ wrapper_output_stream = camel_data_wrapper_get_output_stream (wrapper);
+ camel_stream_reset (wrapper_output_stream);
+
+ /* Write the header. */
+ mail_write_html (stream, "\n<!-- text/html below -->\n");
+
+ do {
+ /* Read next chunk of text. */
+ nb_bytes_read = camel_stream_read (wrapper_output_stream,
+ tmp_buffer, 4096);
+
+ /* If there's any text, write it to the stream */
+ if (nb_bytes_read > 0) {
+ empty_text = FALSE;
+
+ /* Write the buffer to the html stream */
+ gtk_html_stream_write (stream, tmp_buffer,
+ nb_bytes_read);
+ }
+ } while (!camel_stream_eos (wrapper_output_stream));
+
+ if (empty_text)
+ mail_write_html (stream, "<b>(empty)</b>");
+}
+
+static void
+handle_image (CamelDataWrapper *wrapper, GtkHTMLStreamHandle *stream,
+ CamelDataWrapper *root)
+{
+ gchar *uuid;
+ gchar *tag;
+
+ uuid = lookup_unique_id (root, wrapper);
+
+ mail_write_html (stream, "\n<!-- image below -->\n");
+ tag = g_strdup_printf ("<img src=\"camel://%s\">\n", uuid);
+ mail_write_html (stream, tag);
+ g_free (uuid);
+ g_free (tag);
+}
+
+static void
+handle_vcard (CamelDataWrapper *wrapper, GtkHTMLStreamHandle *stream,
+ CamelDataWrapper *root)
+{
+ mail_write_html (stream, "\n<!-- vcard below -->\n");
+
+ /* FIXME: do something here. */
+}
+
+static void
+handle_mime_part (CamelDataWrapper *wrapper, GtkHTMLStreamHandle *stream,
+ CamelDataWrapper *root)
+{
+ CamelMimePart *mime_part;
+ CamelDataWrapper *message_contents;
+ gchar *whole_mime_type;
+
+ g_return_if_fail (CAMEL_IS_MIME_PART (wrapper));
+
+ mime_part = CAMEL_MIME_PART (wrapper);
+ message_contents =
+ camel_medium_get_content_object (CAMEL_MEDIUM (mime_part));
+
+ g_assert (message_contents);
+
+ mail_write_html (stream, "\n<!-- mime message below -->\n");
+
+// mail_write_html (stream,
+// "<table width=95% border=1><tr><td>\n\n");
+
+ /* dispatch the correct handler function for the mime type */
+ whole_mime_type = MIME_TYPE_WHOLE (mime_part);
+ call_handler_function (message_contents,
+ whole_mime_type,
+ MIME_TYPE_MAIN (mime_part),
+ stream, root);
+ g_free (whole_mime_type);
+
+ /* close up the table we opened */
+// mail_write_html (stream,
+// "\n\n</td></tr></table>\n\n");
+}
+
+
+/* called for each body part in a multipart/mixed */
+static void
+display_camel_body_part (CamelMimeBodyPart *body_part,
+ GtkHTMLStreamHandle *stream,
+ CamelDataWrapper *root)
+{
+ gchar *whole_mime_type;
+
+ CamelDataWrapper* contents =
+ camel_medium_get_content_object (CAMEL_MEDIUM (body_part));
+
+ whole_mime_type = MIME_TYPE_WHOLE (body_part);
+ call_handler_function (contents, whole_mime_type,
+ MIME_TYPE_MAIN (body_part),
+ stream, root);
+ g_free (whole_mime_type);
+
+ mail_write_html (stream, "\n<hr>\n");
+}
+
+
+/* Our policy here is this:
+ (1) print text/(plain|html) parts found
+ (2) print vcards and images inline
+ (3) treat all other parts as attachments */
+static void
+handle_multipart_mixed (CamelDataWrapper *wrapper,
+ GtkHTMLStreamHandle *stream, CamelDataWrapper *root)
+{
+ CamelMultipart *mp;
+ int i, nparts;
+
+ g_return_if_fail (CAMEL_IS_MULTIPART (wrapper));
+
+ mp = CAMEL_MULTIPART (wrapper);
+
+ nparts = camel_multipart_get_number (mp);
+ for (i = 0; i < nparts; i++) {
+ CamelMimeBodyPart *body_part =
+ camel_multipart_get_part (mp, i);
+
+ display_camel_body_part (body_part, stream, root);
+ }
+}
+
+static void
+handle_multipart_related (CamelDataWrapper *wrapper,
+ GtkHTMLStreamHandle *stream,
+ CamelDataWrapper *root)
+{
+// CamelMultipart* mp = CAMEL_MULTIPART (wrapper);
+
+ /* FIXME: read RFC, in terms of how a one message
+ may refer to another object */
+}
+
+/* multipart/alternative helper function --
+ * Returns NULL if no displayable msg is found
+ */
+static CamelMimePart *
+find_preferred_alternative (CamelMultipart* multipart)
+{
+ int i, nparts;
+ CamelMimePart* html_part = NULL;
+ CamelMimePart* plain_part = NULL;
+
+ /* Find out out many parts are in it. */
+ nparts = camel_multipart_get_number (multipart);
+
+ /* FIXME: DO LEAF-LOOKUP HERE FOR OTHER MIME-TYPES!!! */
+
+ for (i = 0; i < nparts; i++) {
+ CamelMimeBodyPart *body_part =
+ camel_multipart_get_part (multipart, i);
+
+ if (strcasecmp (MIME_TYPE_MAIN (body_part), "text") != 0)
+ continue;
+
+ if (strcasecmp (MIME_TYPE_SUB (body_part), "plain") == 0)
+ plain_part = CAMEL_MIME_PART (body_part);
+ else if (strcasecmp (MIME_TYPE_SUB (body_part), "html") == 0)
+ html_part = CAMEL_MIME_PART (body_part);
+ }
+
+ if (html_part)
+ return html_part;
+ if (plain_part)
+ return plain_part;
+ return NULL;
+}
+
+/* The current policy for multipart/alternative is this:
+ *
+ * if (we find a text/html body part)
+ * we print it
+ * else if (we find a text/plain body part)
+ * we print it
+ * else
+ * we print nothing
+ */
+static void
+handle_multipart_alternative (CamelDataWrapper *wrapper,
+ GtkHTMLStreamHandle *stream,
+ CamelDataWrapper *root)
+{
+ CamelMultipart *multipart = CAMEL_MULTIPART (wrapper);
+ CamelMimePart *mime_part;
+ gchar *whole_mime_type;
+
+ mime_part = find_preferred_alternative (multipart);
+ if (mime_part) {
+ CamelDataWrapper *contents =
+ camel_medium_get_content_object (
+ CAMEL_MEDIUM (mime_part));
+
+ whole_mime_type = MIME_TYPE_WHOLE (mime_part);
+ call_handler_function (contents, whole_mime_type,
+ MIME_TYPE_MAIN (mime_part),
+ stream, root);
+ g_free (whole_mime_type);
+ }
+}
+
+static void
+handle_unknown_type (CamelDataWrapper *wrapper,
+ GtkHTMLStreamHandle *stream,
+ CamelDataWrapper *root)
+{
+ gchar *tag;
+ char *uid = lookup_unique_id (root, wrapper);
+
+ tag = g_strdup_printf ("<a href=\"camel://%s\">click-me-to-save</a>\n",
+ uid);
+
+ mail_write_html (stream, tag);
+}
+
+
+void
+mail_write_html (GtkHTMLStreamHandle *stream, const char *data)
+{
+ gtk_html_stream_write (stream, data, strlen (data));
+}
diff --git a/mail/mail-format.h b/mail/mail-format.h
new file mode 100644
index 0000000000..87c16a2049
--- /dev/null
+++ b/mail/mail-format.h
@@ -0,0 +1,53 @@
+/* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- */
+
+/*
+ * Author :
+ * Matt Loper <matt@helixcode.com>
+ *
+ * Copyright 2000, Helix Code, Inc. (http://www.helixcode.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.
+ *
+ */
+
+#ifndef MAIL_FORMAT_H
+#define MAIL_FORMAT_H
+
+#ifdef __cplusplus
+extern "C" {
+#pragma }
+#endif /* __cplusplus }*/
+
+#include <gtkhtml/gtkhtml.h>
+#include "camel/camel.h"
+
+void mail_format_mime_message (CamelMimeMessage *mime_message,
+ GtkHTMLStreamHandle *header_stream,
+ GtkHTMLStreamHandle *body_stream);
+
+void mail_write_html (GtkHTMLStreamHandle *stream, const char *data);
+
+CamelMimeMessage *mail_generate_reply (CamelMimeMessage *mime_message);
+
+CamelMimeMessage *mail_generate_forward (CamelMimeMessage *mime_message,
+ gboolean forward_as_attachment,
+ gboolean keep_attachments);
+
+#ifdef __cplusplus
+}
+#endif /* __cplusplus */
+
+#endif // CAMEL_FORMATTER_H
+