Another big rewrite of this stuff. Now all (well, most) attachments get a

* mail-display.c, mail-format.c: Another big rewrite of this
	stuff. Now all (well, most) attachments get a small icon with a
	description and a (non-obvious) right-click pop-up menu with
	options to save, open in an external program, or show/hide inline.

	TODO: antialias the icon, add more options to the pop-up for
	certain MIME types, add an icon to the headers, fix PGP to work
	like everything else, fix message/external-body to work again,
	add some icon caching action, etc, etc.

svn path=/trunk/; revision=5326
This commit is contained in:
Dan Winship
2000-09-11 17:12:57 +00:00
parent cab460ef82
commit 040ff5ad09
4 changed files with 514 additions and 366 deletions

View File

@ -1,3 +1,15 @@
2000-09-11 Dan Winship <danw@helixcode.com>
* mail-display.c, mail-format.c: Another big rewrite of this
stuff. Now all (well, most) attachments get a small icon with a
description and a (non-obvious) right-click pop-up menu with
options to save, open in an external program, or show/hide inline.
TODO: antialias the icon, add more options to the pop-up for
certain MIME types, add an icon to the headers, fix PGP to work
like everything else, fix message/external-body to work again,
add some icon caching action, etc, etc.
2000-09-09 Jeffrey Stedfast <fejj@helixcode.com>
* mail-ops.c (do_fetch_mail): Use the CamelUIDCache so that we

View File

@ -16,6 +16,7 @@
#include <gnome.h>
#include "e-util/e-util.h"
#include "e-util/e-html-utils.h"
#include "e-util/e-popup-menu.h"
#include "mail-display.h"
#include "mail.h"
@ -29,7 +30,7 @@
static GtkObjectClass *mail_display_parent_class;
static void redisplay (MailDisplay *md);
static void redisplay (MailDisplay *md, gboolean unscroll);
/*----------------------------------------------------------------------*
* Callbacks
@ -116,22 +117,92 @@ make_safe_filename (const char *prefix, CamelMimePart *part)
return safe;
}
static CamelMimePart *
part_for_url (const char *url, MailDisplay *md)
static void
save_data_cb (GtkWidget *widget, gpointer user_data)
{
GHashTable *urls;
GtkFileSelection *file_select = (GtkFileSelection *)
gtk_widget_get_ancestor (widget, GTK_TYPE_FILE_SELECTION);
urls = g_datalist_get_data (md->data, "urls");
g_return_val_if_fail (urls != NULL, NULL);
return g_hash_table_lookup (urls, url);
write_data_to_file (user_data,
gtk_file_selection_get_filename (file_select),
FALSE);
gtk_widget_destroy (GTK_WIDGET (file_select));
}
static gboolean
idle_redisplay (gpointer data)
{
MailDisplay *md = data;
md->idle_id = 0;
redisplay (md, FALSE);
return FALSE;
}
static void
launch_external (const char *url, MailDisplay *md)
queue_redisplay (MailDisplay *md)
{
const char *command = url + 21;
char *tmpl, *tmpdir, *filename, *argv[2];
CamelMimePart *part = part_for_url (url, md);
if (!md->idle_id) {
md->idle_id = g_idle_add_full (G_PRIORITY_LOW, idle_redisplay,
md, NULL);
}
}
static void
on_link_clicked (GtkHTML *html, const char *url, gpointer user_data)
{
MailDisplay *md = user_data;
if (!g_strncasecmp (url, "news:", 5) ||
!g_strncasecmp (url, "nntp:", 5))
g_warning ("Can't handle news URLs yet.");
else if (!g_strncasecmp (url, "mailto:", 7))
send_to_url (url);
else if (!strcmp (url, "x-evolution-decode-pgp:")) {
g_datalist_set_data (md->data, "show_pgp",
GINT_TO_POINTER (1));
queue_redisplay (md);
} else
gnome_url_show (url);
}
static void
save_cb (GtkWidget *widget, gpointer user_data)
{
CamelMimePart *part = gtk_object_get_data (user_data, "CamelMimePart");
GtkFileSelection *file_select;
char *filename;
filename = make_safe_filename (g_get_home_dir (), part);
file_select = GTK_FILE_SELECTION (
gtk_file_selection_new ("Save Attachment"));
gtk_file_selection_set_filename (file_select, filename);
g_free (filename);
gtk_signal_connect (GTK_OBJECT (file_select->ok_button), "clicked",
GTK_SIGNAL_FUNC (save_data_cb), part);
gtk_signal_connect_object (GTK_OBJECT (file_select->cancel_button),
"clicked",
GTK_SIGNAL_FUNC (gtk_widget_destroy),
GTK_OBJECT (file_select));
gtk_widget_show (GTK_WIDGET (file_select));
}
static void
launch_cb (GtkWidget *widget, gpointer user_data)
{
CamelMimePart *part = gtk_object_get_data (user_data, "CamelMimePart");
GnomeVFSMimeApplication *app;
GMimeContentField *content_type;
char *mime_type, *tmpl, *tmpdir, *filename, *argv[2];
content_type = camel_mime_part_get_content_type (part);
mime_type = gmime_content_field_get_mime_type (content_type);
app = gnome_vfs_mime_get_default_application (mime_type);
g_free (mime_type);
g_return_if_fail (app != NULL);
tmpl = g_strdup ("/tmp/evolution.XXXXXX");
#ifdef HAVE_MKDTEMP
@ -160,7 +231,7 @@ launch_external (const char *url, MailDisplay *md)
return;
}
argv[0] = (char *)command;
argv[0] = app->command;
argv[1] = filename;
gnome_execute_async (tmpdir, 2, argv);
@ -169,82 +240,86 @@ launch_external (const char *url, MailDisplay *md)
}
static void
save_data_cb (GtkWidget *widget, gpointer user_data)
inline_cb (GtkWidget *widget, gpointer user_data)
{
GtkFileSelection *file_select = (GtkFileSelection *)
gtk_widget_get_ancestor (widget, GTK_TYPE_FILE_SELECTION);
MailDisplay *md = gtk_object_get_data (user_data, "MailDisplay");
CamelMimePart *part = gtk_object_get_data (user_data, "CamelMimePart");
write_data_to_file (user_data,
gtk_file_selection_get_filename (file_select),
FALSE);
gtk_widget_destroy (GTK_WIDGET (file_select));
}
if (mail_part_is_inline (part))
camel_mime_part_set_disposition (part, "attachment");
else
camel_mime_part_set_disposition (part, "inline");
static void
save_data (const char *cid, MailDisplay *md)
{
GtkFileSelection *file_select;
char *filename;
CamelMimePart *part;
part = part_for_url (cid, md);
g_return_if_fail (part != NULL);
filename = make_safe_filename (g_get_home_dir (), part);
file_select = GTK_FILE_SELECTION (gtk_file_selection_new ("Save Attachment"));
gtk_file_selection_set_filename (file_select, filename);
g_free (filename);
gtk_signal_connect (GTK_OBJECT (file_select->ok_button), "clicked",
GTK_SIGNAL_FUNC (save_data_cb), part);
gtk_signal_connect_object (GTK_OBJECT (file_select->cancel_button),
"clicked",
GTK_SIGNAL_FUNC (gtk_widget_destroy),
GTK_OBJECT (file_select));
gtk_widget_show (GTK_WIDGET (file_select));
queue_redisplay (md);
}
static gboolean
idle_redisplay (gpointer data)
pixmap_press (GtkWidget *ebox, GdkEventButton *event, gpointer user_data)
{
MailDisplay *md = data;
EPopupMenu menu[] = {
{ N_("Save to Disk..."), NULL,
GTK_SIGNAL_FUNC (save_cb), 0 },
{ N_("Open in %s..."), NULL,
GTK_SIGNAL_FUNC (launch_cb), 1 },
{ N_("View Inline"), NULL,
GTK_SIGNAL_FUNC (inline_cb), 2 },
{ NULL, NULL, NULL, 0 }
};
CamelMimePart *part;
MailMimeHandler *handler;
int mask;
md->idle_id = 0;
redisplay (md);
return FALSE;
}
if (event->button != 3)
return FALSE;
static void
queue_redisplay (MailDisplay *md)
{
if (!md->idle_id) {
md->idle_id = g_idle_add_full (G_PRIORITY_LOW, idle_redisplay,
md, NULL);
part = gtk_object_get_data (user_data, "CamelMimePart");
handler = mail_lookup_handler (gtk_object_get_data (user_data,
"mime_type"));
/* External view item */
if (handler && handler->application) {
menu[1].name = g_strdup_printf (menu[1].name,
handler->application->name);
} else {
menu[1].name = g_strdup_printf (menu[1].name,
N_("External Viewer"));
mask |= 1;
}
}
static void
on_link_clicked (GtkHTML *html, const char *url, gpointer user_data)
{
MailDisplay *md = user_data;
/* Inline view item */
if (handler && handler->builtin) {
if (!mail_part_is_inline (part)) {
if (handler->component) {
OAF_Property *prop;
char *name;
if (!g_strncasecmp (url, "news:", 5) ||
!g_strncasecmp (url, "nntp:", 5))
g_warning ("Can't handle news URLs yet.");
else if (!g_strncasecmp (url, "mailto:", 7))
send_to_url (url);
else if (!strncmp (url, "cid:", 4))
save_data (url, md);
else if (!strncmp (url, "x-evolution-external:", 21))
launch_external (url, md);
else if (!strcmp (url, "x-evolution-decode-pgp:")) {
g_datalist_set_data (md->data, "show_pgp",
GINT_TO_POINTER (1));
queue_redisplay (md);
} else
gnome_url_show (url);
}
prop = oaf_server_info_prop_find (
handler->component, "name");
if (!prop) {
prop = oaf_server_info_prop_find (
handler->component,
"description");
}
if (prop && prop->v._d == OAF_P_STRING)
name = prop->v._u.value_string;
else
name = "bonobo";
menu[2].name = g_strdup_printf (
N_("View Inline (via %s)"), name);
} else
menu[2].name = g_strdup (menu[2].name);
} else
menu[2].name = g_strdup (N_("Hide"));
} else {
menu[2].name = g_strdup (menu[2].name);
mask |= 2;
}
e_popup_menu_run (menu, event, mask, 0, user_data);
g_free (menu[1].name);
g_free (menu[2].name);
return TRUE;
}
static gboolean
on_object_requested (GtkHTML *html, GtkHTMLEmbedded *eb, gpointer data)
@ -261,28 +336,77 @@ on_object_requested (GtkHTML *html, GtkHTMLEmbedded *eb, gpointer data)
GByteArray *ba;
CamelStream *cstream;
BonoboStream *bstream;
char *cid;
if (strncmp (eb->classid, "cid:", 4) != 0)
cid = eb->classid;
if (!strncmp (cid, "popup:", 6))
cid += 6;
if (strncmp (cid, "cid:", 4) != 0)
return FALSE;
urls = g_datalist_get_data (md->data, "urls");
g_return_val_if_fail (urls != NULL, FALSE);
medium = g_hash_table_lookup (urls, eb->classid);
medium = g_hash_table_lookup (urls, cid);
g_return_val_if_fail (CAMEL_IS_MEDIUM (medium), FALSE);
wrapper = camel_medium_get_content_object (medium);
if (cid != eb->classid) {
/* This is a part wrapper */
const char *icon;
GtkWidget *pixmap, *ebox;
icon = gnome_vfs_mime_get_value (eb->type, "icon-filename");
if (icon) {
pixmap = gnome_pixmap_new_from_file_at_size (icon,
24, 24);
} else {
char *filename;
filename = gnome_pixmap_file ("gnome-unknown.png");
pixmap = gnome_pixmap_new_from_file_at_size (filename,
24, 24);
g_free (filename);
}
ebox = gtk_event_box_new ();
gtk_widget_set_sensitive (GTK_WIDGET (ebox), TRUE);
gtk_widget_add_events (GTK_WIDGET (ebox),
GDK_BUTTON_PRESS_MASK);
gtk_signal_connect (GTK_OBJECT (ebox), "button_press_event",
GTK_SIGNAL_FUNC (pixmap_press), ebox);
gtk_object_set_data (GTK_OBJECT (ebox), "MailDisplay", md);
gtk_object_set_data (GTK_OBJECT (ebox), "CamelMimePart",
medium);
gtk_object_set_data_full (GTK_OBJECT (ebox), "mime_type",
g_strdup (eb->type),
(GDestroyNotify)g_free);
gtk_container_add (GTK_CONTAINER (ebox), pixmap);
gtk_widget_show_all (ebox);
gtk_container_add (GTK_CONTAINER (eb), ebox);
return TRUE;
}
component = gnome_vfs_mime_get_default_component (eb->type);
if (!component)
return FALSE;
embedded = bonobo_widget_new_subdoc (component->iid, NULL);
if (!embedded)
if (embedded) {
/* FIXME: as of bonobo 0.18, there's an extra
* client_site dereference in the BonoboWidget
* destruction path that we have to balance out to
* prevent problems.
*/
bonobo_object_ref (bonobo_widget_get_client_site (
BONOBO_WIDGET (embedded)));
} else
embedded = bonobo_widget_new_control (component->iid, NULL);
CORBA_free (component);
if (!embedded)
return FALSE;
server = bonobo_widget_get_server (BONOBO_WIDGET (embedded));
server = bonobo_widget_get_server (BONOBO_WIDGET (embedded));
persist = (Bonobo_PersistStream) bonobo_object_client_query_interface (
server, "IDL:Bonobo/PersistStream:1.0", NULL);
if (persist == CORBA_OBJECT_NIL) {
@ -293,6 +417,7 @@ on_object_requested (GtkHTML *html, GtkHTMLEmbedded *eb, gpointer data)
/* Write the data to a CamelStreamMem... */
ba = g_byte_array_new ();
cstream = camel_stream_mem_new_with_byte_array (ba);
wrapper = camel_medium_get_content_object (medium);
camel_data_wrapper_write_to_stream (wrapper, cstream);
/* ...convert the CamelStreamMem to a BonoboStreamMem... */
@ -422,9 +547,15 @@ clear_data (CamelObject *object, gpointer event_data, gpointer user_data)
}
static void
redisplay (MailDisplay *md)
redisplay (MailDisplay *md, gboolean unscroll)
{
GtkAdjustment *adj;
gfloat oldv;
if (!unscroll) {
adj = e_scroll_frame_get_vadjustment (md->scroll);
oldv = adj->value;
}
md->stream = gtk_html_begin (md->html);
mail_html_write (md->html, md->stream, "%s%s", HTML_HEADER,
@ -439,13 +570,17 @@ redisplay (MailDisplay *md)
gtk_html_end (md->html, md->stream, GTK_HTML_STREAM_OK);
md->stream = NULL;
adj = e_scroll_frame_get_vadjustment (md->scroll);
gtk_adjustment_set_value (adj, 0);
e_scroll_frame_set_vadjustment (md->scroll, adj);
adj = e_scroll_frame_get_hadjustment (md->scroll);
gtk_adjustment_set_value (adj, 0);
e_scroll_frame_set_hadjustment (md->scroll, adj);
if (unscroll) {
adj = e_scroll_frame_get_hadjustment (md->scroll);
gtk_adjustment_set_value (adj, 0);
e_scroll_frame_set_hadjustment (md->scroll, adj);
} else {
adj = e_scroll_frame_get_vadjustment (md->scroll);
if (oldv < adj->upper) {
gtk_adjustment_set_value (adj, oldv);
e_scroll_frame_set_vadjustment (md->scroll, adj);
}
}
}
/**
@ -472,7 +607,7 @@ mail_display_set_message (MailDisplay *md, CamelMedium *medium)
md->current_message = (CamelMimeMessage*)medium;
g_datalist_init (md->data);
redisplay (md);
redisplay (md, TRUE);
if (medium) {
camel_object_hook_event (CAMEL_OBJECT (medium), "finalize",
clear_data, *(md->data));

View File

@ -31,6 +31,7 @@
#include <libgnome/libgnome.h>
#include <libgnomevfs/gnome-vfs-mime-info.h>
#include <libgnomevfs/gnome-vfs-mime-handlers.h>
#include <liboaf/liboaf.h>
#include <ctype.h> /* for isprint */
#include <string.h> /* for strstr */
@ -69,9 +70,6 @@ static gboolean handle_multipart_appledouble (CamelMimePart *part,
static gboolean handle_multipart_encrypted (CamelMimePart *part,
const char *mime_type,
MailDisplay *md);
static gboolean handle_audio (CamelMimePart *part,
const char *mime_type,
MailDisplay *md);
static gboolean handle_message_rfc822 (CamelMimePart *part,
const char *mime_type,
MailDisplay *md);
@ -79,15 +77,9 @@ static gboolean handle_message_external_body (CamelMimePart *part,
const char *mime_type,
MailDisplay *md);
static gboolean handle_unknown_type (CamelMimePart *part,
const char *mime_type,
MailDisplay *md);
static gboolean handle_via_bonobo (CamelMimePart *part,
const char *mime_type,
MailDisplay *md);
static gboolean handle_via_external (CamelMimePart *part,
const char *mime_type,
MailDisplay *md);
/* writes the header info for a mime message into an html stream */
static void write_headers (CamelMimeMessage *message, MailDisplay *md);
@ -95,7 +87,6 @@ static void write_headers (CamelMimeMessage *message, MailDisplay *md);
/* dispatch html printing via mimetype */
static gboolean call_handler_function (CamelMimePart *part, MailDisplay *md);
static void
free_url (gpointer key, gpointer value, gpointer data)
{
@ -226,21 +217,13 @@ get_url_for_icon (const char *icon_name, MailDisplay *md)
}
/* We're maintaining a hashtable of mimetypes -> functions;
* Those functions have the following signature...
*/
typedef gboolean (*mime_handler_fn) (CamelMimePart *part,
const char *mime_type,
MailDisplay *md);
static GHashTable *mime_function_table, *mime_fallback_table;
static GHashTable *mime_handler_table, *mime_function_table;
static void
setup_function_table (void)
setup_mime_tables (void)
{
mime_handler_table = g_hash_table_new (g_str_hash, g_str_equal);
mime_function_table = g_hash_table_new (g_str_hash, g_str_equal);
mime_fallback_table = g_hash_table_new (g_str_hash, g_str_equal);
g_hash_table_insert (mime_function_table, "text/plain",
handle_text_plain);
@ -251,11 +234,34 @@ setup_function_table (void)
g_hash_table_insert (mime_function_table, "text/html",
handle_text_html);
g_hash_table_insert (mime_function_table, "image/*",
g_hash_table_insert (mime_function_table, "image/gif",
handle_image);
g_hash_table_insert (mime_function_table, "image/jpeg",
handle_image);
g_hash_table_insert (mime_function_table, "image/png",
handle_image);
g_hash_table_insert (mime_function_table, "image/x-png",
handle_image);
g_hash_table_insert (mime_function_table, "image/tiff",
handle_image);
g_hash_table_insert (mime_function_table, "image/x-bmp",
handle_image);
g_hash_table_insert (mime_function_table, "image/bmp",
handle_image);
g_hash_table_insert (mime_function_table, "image/x-cmu-raster",
handle_image);
g_hash_table_insert (mime_function_table, "image/x-ico",
handle_image);
g_hash_table_insert (mime_function_table, "image/x-portable-anymap",
handle_image);
g_hash_table_insert (mime_function_table, "image/x-portable-bitmap",
handle_image);
g_hash_table_insert (mime_function_table, "image/x-portable-graymap",
handle_image);
g_hash_table_insert (mime_function_table, "image/x-portable-pixmap",
handle_image);
g_hash_table_insert (mime_function_table, "image/x-xpixmap",
handle_image);
g_hash_table_insert (mime_function_table, "audio/*",
handle_audio);
g_hash_table_insert (mime_function_table, "message/rfc822",
handle_message_rfc822);
@ -279,97 +285,221 @@ setup_function_table (void)
* as text/plain (as long as you recognize the character set),
* and unrecognized multipart subtypes as multipart/mixed.
*/
g_hash_table_insert (mime_fallback_table, "text/*",
g_hash_table_insert (mime_function_table, "text/*",
handle_text_plain);
g_hash_table_insert (mime_function_table, "multipart/*",
handle_multipart_mixed);
}
static mime_handler_fn
lookup_handler (const char *mime_type, gboolean *generic)
static gboolean
component_supports (OAF_ServerInfo *component, const char *mime_type)
{
mime_handler_fn handler_function;
OAF_Property *prop;
CORBA_sequence_CORBA_string stringv;
int i;
prop = oaf_server_info_prop_find (component,
"bonobo:supported_mime_types");
if (!prop || prop->v._d != OAF_P_STRINGV)
return FALSE;
stringv = prop->v._u.value_stringv;
for (i = 0; i < stringv._length; i++) {
if (!g_strcasecmp (mime_type, stringv._buffer[i]))
return TRUE;
}
return FALSE;
}
/**
* mail_lookup_handler:
* @mime_type: a MIME type
*
* Looks up the MIME type in its own tables and GNOME-VFS's and returns
* a MailMimeHandler structure detailing the component, application,
* and built-in handlers (if any) for that MIME type. (If the component
* is non-%NULL, the built-in handler will always be handle_via_bonobo().)
* The MailMimeHandler's @generic field is set if the match was for the
* MIME supertype rather than the exact type.
*
* Return value: a MailMimeHandler (which should not be freed), or %NULL
* if no handlers are available.
**/
MailMimeHandler *
mail_lookup_handler (const char *mime_type)
{
MailMimeHandler *handler;
char *mime_type_main;
GnomeVFSMimeAction *action;
if (mime_function_table == NULL)
setup_function_table ();
if (mime_handler_table == NULL)
setup_mime_tables ();
/* See if we've already found it. */
handler = g_hash_table_lookup (mime_handler_table, mime_type);
if (handler)
return handler;
/* No. Create a new one and look up application and full type
* handler. If we find a builtin, create the handler and
* register it.
*/
handler = g_new0 (MailMimeHandler, 1);
handler->application =
gnome_vfs_mime_get_default_application (mime_type);
handler->builtin =
g_hash_table_lookup (mime_function_table, mime_type);
if (handler->builtin) {
handler->generic = FALSE;
goto reg;
}
/* Try for a exact component match. */
handler->component = gnome_vfs_mime_get_default_component (mime_type);
if (handler->component &&
component_supports (handler->component, mime_type)) {
handler->generic = FALSE;
handler->builtin = handle_via_bonobo;
goto reg;
}
/* Try for a generic builtin match. */
mime_type_main = g_strdup_printf ("%.*s/*",
(int)strcspn (mime_type, "/"),
mime_type);
/* OK. There are 6 possibilities, which we try in this order:
* 1) full match in the main table
* 2) partial match in the main table
* 3) full match in gnome_vfs_mime_*
* 4) full match in the fallback table
* 5) partial match in the fallback table
* 6) partial match in gnome_vfs_mime_*
*
* Of these, 1-4 are considered exact matches, and 5 and 6 are
* considered generic.
*/
/* Check for full match in mime_function_table. */
handler_function = g_hash_table_lookup (mime_function_table,
mime_type);
if (!handler_function) {
handler_function = g_hash_table_lookup (mime_function_table,
mime_type_main);
if (handler_function) {
/* Optimize this for the next time through. */
g_hash_table_insert (mime_function_table,
g_strdup (mime_type),
handler_function);
}
}
if (handler_function) {
g_free (mime_type_main);
*generic = FALSE;
return handler_function;
}
action = gnome_vfs_mime_get_default_action_without_fallback (mime_type);
if (action) {
if (action->action_type == GNOME_VFS_MIME_ACTION_TYPE_COMPONENT)
handler_function = handle_via_bonobo;
else
handler_function = handle_via_external;
/* Optimize this for the next time through. */
g_hash_table_insert (mime_function_table,
g_strdup (mime_type), handler_function);
g_free (mime_type_main);
gnome_vfs_mime_action_free (action);
*generic = FALSE;
return handler_function;
}
handler_function = g_hash_table_lookup (mime_fallback_table,
mime_type);
if (handler_function)
*generic = FALSE;
else {
handler_function = g_hash_table_lookup (mime_fallback_table,
mime_type_main);
if (!handler_function) {
action = gnome_vfs_mime_get_default_action (mime_type_main);
if (action) {
if (action->action_type ==
GNOME_VFS_MIME_ACTION_TYPE_COMPONENT)
handler_function = handle_via_bonobo;
else
handler_function = handle_via_external;
gnome_vfs_mime_action_free (action);
}
}
*generic = TRUE;
}
handler->builtin = g_hash_table_lookup (mime_function_table,
mime_type_main);
g_free (mime_type_main);
return handler_function;
if (handler->builtin) {
handler->generic = TRUE;
if (handler->component) {
CORBA_free (handler->component);
handler->component = NULL;
}
goto reg;
}
/* Try for a generic component match. */
if (handler->component) {
handler->generic = TRUE;
handler->builtin = handle_via_bonobo;
goto reg;
}
/* If we at least got an application, use that. */
if (handler->application) {
handler->generic = TRUE;
goto reg;
}
/* Nada. */
g_free (handler);
return NULL;
reg:
g_hash_table_insert (mime_handler_table, g_strdup (mime_type),
handler);
return handler;
}
/* An "anonymous" MIME part is one that we shouldn't call attention
* to the existence of, but simply display.
*/
static gboolean
is_anonymous (CamelMimePart *part, const char *mime_type)
{
if (!g_strncasecmp (mime_type, "multipart/", 10) ||
!g_strncasecmp (mime_type, "message/", 8))
return TRUE;
if (!g_strncasecmp (mime_type, "text/", 5) &&
!camel_mime_part_get_filename (part))
return TRUE;
return FALSE;
}
/**
* mail_part_is_inline:
* @part: a CamelMimePart
*
* Return value: whether or not the part should/will be displayed inline.
**/
gboolean
mail_part_is_inline (CamelMimePart *part)
{
const char *disposition;
GMimeContentField *content_type;
const char *mime_type;
/* If it has an explicit disposition, return that. */
disposition = camel_mime_part_get_disposition (part);
if (disposition)
return g_strcasecmp (disposition, "inline") == 0;
/* Certain types should default to inline. FIXME: this should
* be customizable.
*/
content_type = camel_mime_part_get_content_type (part);
mime_type = gmime_content_field_get_mime_type (content_type);
if (!g_strncasecmp (mime_type, "message/", 8))
return TRUE;
/* Otherwise, display it inline if it's "anonymous", and
* as an attachment otherwise.
*/
return is_anonymous (part, mime_type);
}
static void
attachment_header (CamelMimePart *part, const char *mime_type,
gboolean is_inline, MailDisplay *md)
{
const char *info;
char *htmlinfo;
/* No header for anonymous inline parts. */
if (is_inline && is_anonymous (part, mime_type))
return;
/* Start the table, create the pop-up object. */
mail_html_write (md->html, md->stream, "<table><tr><td>"
"<object classid=\"popup:%s\" type=\"%s\">"
"</object></td><td><font size=-1>",
get_cid (part, md), mime_type);
/* Write the MIME type */
info = gnome_vfs_mime_get_value (mime_type, "description");
htmlinfo = e_text_to_html (info ? info : mime_type, 0);
mail_html_write (md->html, md->stream, "%s attachment", htmlinfo);
g_free (htmlinfo);
/* Write the name, if we have it. */
info = camel_mime_part_get_filename (part);
if (info) {
htmlinfo = e_text_to_html (info, 0);
mail_html_write (md->html, md->stream, " (%s)", htmlinfo);
g_free (htmlinfo);
}
/* Write a description, if we have one. */
info = camel_mime_part_get_description (part);
if (info) {
htmlinfo = e_text_to_html (info, E_TEXT_TO_HTML_CONVERT_URLS);
mail_html_write (md->html, md->stream, ", \"%s\"", htmlinfo);
g_free (htmlinfo);
}
#if 0
/* Describe the click action, if any. */
if (action) {
mail_html_write (md->html, md->stream,
"<br>Click on the icon to %s.", action);
}
#endif
mail_html_write (md->html, md->stream, "</font></td></tr></table>");
}
static gboolean
@ -377,18 +507,31 @@ call_handler_function (CamelMimePart *part, MailDisplay *md)
{
CamelDataWrapper *wrapper;
char *mime_type;
mime_handler_fn handler_function = NULL;
gboolean generic, output;
MailMimeHandler *handler;
gboolean output, is_inline;
wrapper = camel_medium_get_content_object (CAMEL_MEDIUM (part));
mime_type = camel_data_wrapper_get_mime_type (wrapper);
g_strdown (mime_type);
handler_function = lookup_handler (mime_type, &generic);
if (handler_function)
output = (*handler_function) (part, mime_type, md);
handler = mail_lookup_handler (mime_type);
if (!handler) {
char *id_type;
id_type = mail_identify_mime_part (part);
if (id_type) {
g_free (mime_type);
mime_type = id_type;
handler = mail_lookup_handler (id_type);
}
}
is_inline = mail_part_is_inline (part);
attachment_header (part, mime_type, is_inline, md);
if (handler && handler->builtin && is_inline)
output = (*handler->builtin) (part, mime_type, md);
else
output = handle_unknown_type (part, mime_type, md);
output = TRUE;
g_free (mime_type);
return output;
@ -528,7 +671,7 @@ handle_text_plain (CamelMimePart *part, const char *mime_type,
text = get_data_wrapper_text (wrapper);
if (!text)
return FALSE;
/* Check for RFC 2646 flowed text. */
type = camel_mime_part_get_content_type (part);
format = gmime_content_field_get_parameter (type, "format");
@ -655,6 +798,7 @@ fake_mime_part_from_data (const char *data, int len, const char *type)
part = camel_mime_part_new ();
camel_medium_set_content_object (CAMEL_MEDIUM (part), wrapper);
camel_object_unref (CAMEL_OBJECT (wrapper));
camel_mime_part_set_disposition (part, "inline");
return part;
}
@ -1177,7 +1321,7 @@ find_preferred_alternative (CamelMultipart *multipart, gboolean want_plain)
{
int i, nparts;
CamelMimePart *preferred_part = NULL;
gboolean generic;
MailMimeHandler *handler;
nparts = camel_multipart_get_number (multipart);
for (i = 0; i < nparts; i++) {
@ -1188,8 +1332,8 @@ find_preferred_alternative (CamelMultipart *multipart, gboolean want_plain)
g_strdown (mime_type);
if (want_plain && !strcmp (mime_type, "text/plain"))
return part;
if (lookup_handler (mime_type, &generic) &&
(!preferred_part || !generic))
handler = mail_lookup_handler (mime_type);
if (handler && (!preferred_part || !handler->generic))
preferred_part = part;
g_free (mime_type);
}
@ -1213,7 +1357,7 @@ handle_multipart_alternative (CamelMimePart *part, const char *mime_type,
if (mime_part)
return call_handler_function (mime_part, md);
else
return handle_unknown_type (part, mime_type, md);
return handle_multipart_mixed (part, mime_type, md);
}
/* RFC 1740 */
@ -1236,82 +1380,6 @@ handle_multipart_appledouble (CamelMimePart *part, const char *mime_type,
return call_handler_function (part, md);
}
static void
handle_mystery (CamelMimePart *part, MailDisplay *md,
const char *url, const char *icon_name, const char *id,
const char *action)
{
const char *info;
char *htmlinfo;
mail_html_write (md->html, md->stream, "<table><tr><td>");
/* Draw the icon, surrounded by an <a href> if we have a URL,
* or a plain inactive border if not.
*/
if (url) {
mail_html_write (md->html, md->stream,
"<a href=\"%s\">", url);
} else {
mail_html_write (md->html, md->stream,
"<table border=2><tr><td>");
}
mail_html_write (md->html, md->stream, "<img src=\"%s\">",
get_url_for_icon (icon_name, md));
if (url)
mail_html_write (md->html, md->stream, "</a>");
else
mail_html_write (md->html, md->stream, "</td></tr></table>");
mail_html_write (md->html, md->stream, "</td><td>%s<br>", id);
/* Write a description, if we have one. */
info = camel_mime_part_get_description (part);
if (info) {
htmlinfo = e_text_to_html (info, E_TEXT_TO_HTML_CONVERT_URLS);
mail_html_write (md->html, md->stream, "Description: %s<br>",
htmlinfo);
g_free (htmlinfo);
}
/* Write the name, if we have it. */
info = camel_mime_part_get_filename (part);
if (info) {
htmlinfo = e_text_to_html (info, 0);
mail_html_write (md->html, md->stream, "Name: %s<br>",
htmlinfo);
g_free (htmlinfo);
}
/* Describe the click action, if any. */
if (action) {
mail_html_write (md->html, md->stream,
"<br>Click on the icon to %s.", action);
}
mail_html_write (md->html, md->stream, "</td></tr></table>");
}
static gboolean
handle_audio (CamelMimePart *part, const char *mime_type, MailDisplay *md)
{
char *id;
const char *desc;
desc = gnome_vfs_mime_get_value (mime_type, "description");
if (desc)
id = g_strdup_printf ("%s data", desc);
else {
id = g_strdup_printf ("Audio data in \"%s\" format.",
mime_type);
}
handle_mystery (part, md, get_cid (part, md), "gnome-audio2.png",
id, "play it");
g_free (id);
return TRUE;
}
static gboolean
handle_message_rfc822 (CamelMimePart *part, const char *mime_type,
MailDisplay *md)
@ -1429,103 +1497,23 @@ handle_message_external_body (CamelMimePart *part, const char *mime_type,
desc = g_strdup ("Malformed external-body part.");
}
#if 0 /* FIXME */
handle_mystery (part, md, url, "gnome-globe.png", desc,
url ? "open it in a browser" : NULL);
#endif
g_free (desc);
g_free (url);
return TRUE;
}
static gboolean
handle_undisplayable (CamelMimePart *part, const char *mime_type,
MailDisplay *md)
{
const char *desc;
char *id;
desc = gnome_vfs_mime_get_value (mime_type, "description");
if (desc)
id = g_strdup (desc);
else
id = g_strdup_printf ("Data of type \"%s\".", mime_type);
handle_mystery (part, md, get_cid (part, md), "gnome-question.png",
id, "save it to disk");
g_free (id);
return TRUE;
}
static gboolean
handle_unknown_type (CamelMimePart *part, const char *mime_type,
MailDisplay *md)
{
char *type;
/* Don't give up quite yet. */
type = mail_identify_mime_part (part);
if (type) {
mime_handler_fn handler_function;
gboolean generic, output;
handler_function = lookup_handler (type, &generic);
if (handler_function &&
handler_function != handle_unknown_type) {
output = (*handler_function) (part, type, md);
g_free (type);
return output;
}
} else
type = g_strdup (mime_type);
/* OK. Give up. */
handle_undisplayable (part, type, md);
g_free (type);
return TRUE;
}
static gboolean
handle_via_bonobo (CamelMimePart *part, const char *mime_type,
MailDisplay *md)
{
mail_html_write (md->html, md->stream,
"<object classid=\"%s\" type=\"%s\">",
"<object classid=\"%s\" type=\"%s\"></object>",
get_cid (part, md), mime_type);
/* Call handle_undisplayable to output its HTML inside the
* <object> ... </object>. It will only be displayed if the
* object loading fails.
*/
handle_undisplayable (part, mime_type, md);
mail_html_write (md->html, md->stream, "</object>");
return TRUE;
}
static gboolean
handle_via_external (CamelMimePart *part, const char *mime_type,
MailDisplay *md)
{
GnomeVFSMimeApplication *app;
const char *desc, *icon;
char *action, *url;
app = gnome_vfs_mime_get_default_application (mime_type);
g_return_val_if_fail (app != NULL, FALSE);
desc = gnome_vfs_mime_get_value (mime_type, "description");
icon = gnome_vfs_mime_get_value (mime_type, "icon-filename");
if (!icon)
icon = "gnome-unknown.png";
action = g_strdup_printf ("open the file in %s", app->name);
url = g_strdup_printf ("x-evolution-external:%s", app->command);
handle_mystery (part, md, url, icon, desc, action);
add_url (url, part, md);
g_free (action);
return TRUE;
}
@ -1536,7 +1524,7 @@ mail_get_message_body (CamelDataWrapper *data, gboolean want_plain, gboolean *is
CamelMimePart *subpart;
int i, nparts;
char *subtext, *old;
const char *boundary, *disp;
const char *boundary;
char *text = NULL;
GMimeContentField *mime_type;
@ -1587,8 +1575,7 @@ mail_get_message_body (CamelDataWrapper *data, gboolean want_plain, gboolean *is
for (i = 0; i < nparts; i++) {
subpart = camel_multipart_get_part (mp, i);
disp = camel_mime_part_get_disposition (subpart);
if (disp && g_strcasecmp (disp, "inline") != 0)
if (!mail_part_is_inline (subpart))
continue;
data = camel_medium_get_content_object (

View File

@ -18,6 +18,7 @@
*/
#include <gtkhtml/gtkhtml.h>
#include <libgnomevfs/gnome-vfs-mime-handlers.h>
#include "camel/camel.h"
#include "composer/e-msg-composer.h"
#include "mail-config.h"
@ -43,6 +44,19 @@ char *mail_crypto_openpgp_clearsign (const char *plaintext,
void mail_format_mime_message (CamelMimeMessage *mime_message,
MailDisplay *md);
typedef gboolean (*MailMimeHandlerFn) (CamelMimePart *part,
const char *mime_type,
MailDisplay *md);
typedef struct {
gboolean generic;
OAF_ServerInfo *component;
GnomeVFSMimeApplication *application;
MailMimeHandlerFn builtin;
} MailMimeHandler;
MailMimeHandler *mail_lookup_handler (const char *mime_type);
gboolean mail_part_is_inline (CamelMimePart *part);
EMsgComposer *mail_generate_reply (CamelMimeMessage *mime_message,
gboolean to_all);