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
794 lines
22 KiB
C
794 lines
22 KiB
C
/* -*- 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));
|
|
}
|