diff options
Diffstat (limited to 'mail')
-rw-r--r-- | mail/ChangeLog | 10 | ||||
-rw-r--r-- | mail/Makefile.am | 6 | ||||
-rw-r--r-- | mail/html-stream.c | 139 | ||||
-rw-r--r-- | mail/html-stream.h | 26 | ||||
-rw-r--r-- | mail/mail-display.c | 102 | ||||
-rw-r--r-- | mail/mail-display.h | 3 | ||||
-rw-r--r-- | mail/mail-format.c | 793 | ||||
-rw-r--r-- | mail/mail-format.h | 53 |
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 '<', and '"' turns into '"' */ +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, "<"); + out += 4; + break; + + case '>': + strcpy (out, ">"); + out += 4; + break; + + case '&': + strcpy (out, "&"); + out += 5; + break; + + case '"': + strcpy (out, """); + 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 '<', 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 + |