X-Git-Url: http://git.maemo.org/git/?a=blobdiff_plain;ds=sidebyside;f=src%2Fmodest-tny-msg-view.c;h=68e8ef5ef94f4dac08225759dbde43a74d0fccda;hb=702b748360f6de61e0780c36a98065d19a495c83;hp=87898c2b9385fc2231c086a078a1ee1fe73df5d9;hpb=d98a8ad53358beb10c9a68772ef6b0583b6ec0f5;p=modest diff --git a/src/modest-tny-msg-view.c b/src/modest-tny-msg-view.c index 87898c2..68e8ef5 100644 --- a/src/modest-tny-msg-view.c +++ b/src/modest-tny-msg-view.c @@ -1,6 +1,34 @@ -/* modest-tny-msg-view.c */ +/* Copyright (c) 2006, Nokia Corporation + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are + * met: + * + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * * Neither the name of the Nokia Corporation nor the names of its + * contributors may be used to endorse or promote products derived from + * this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS + * IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED + * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A + * PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER + * OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR + * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF + * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING + * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + -/* insert (c)/licensing information) */ +/* modest-tny-msg-view.c */ #include "modest-tny-msg-view.h" #include "modest-tny-stream-gtkhtml.h" @@ -11,6 +39,8 @@ #include #include #include +#include +#include /* 'private'/'protected' functions */ static void modest_tny_msg_view_class_init (ModestTnyMsgViewClass *klass); @@ -26,8 +56,14 @@ static gboolean on_link_clicked (GtkWidget *widget, const gchar *uri, static gboolean on_url_requested (GtkWidget *widget, const gchar *uri, GtkHTMLStream *stream, ModestTnyMsgView *msg_view); +static gchar *construct_virtual_filename(const gchar *filename, const gint position, const gchar *id, const gboolean active); +static gchar *construct_virtual_filename_from_mime_part(TnyMsgMimePartIface *msg, const gint position); +#define ATTACHMENT_ID_INLINE "attachment-inline" +#define ATTACHMENT_ID_LINK "attachment-link" +#define PREFIX_LINK_EMAIL "mailto:" +gint virtual_filename_get_pos(const gchar *filename); /* * we need these regexps to find URLs in plain text e-mails */ @@ -56,7 +92,7 @@ struct _UrlMatchPattern { /* list my signals */ enum { - /* MY_SIGNAL_1, */ + MAILTO_CLICKED_SIGNAL, /* MY_SIGNAL_2, */ LAST_SIGNAL }; @@ -65,7 +101,7 @@ typedef struct _ModestTnyMsgViewPrivate ModestTnyMsgViewPrivate; struct _ModestTnyMsgViewPrivate { GtkWidget *gtkhtml; TnyMsgIface *msg; - gboolean show_attachments; + gboolean show_attachments_inline; }; #define MODEST_TNY_MSG_VIEW_GET_PRIVATE(o) (G_TYPE_INSTANCE_GET_PRIVATE((o), \ MODEST_TYPE_TNY_MSG_VIEW, \ @@ -74,7 +110,7 @@ struct _ModestTnyMsgViewPrivate { static GtkContainerClass *parent_class = NULL; /* uncomment the following if you have defined any signals */ -/* static guint signals[LAST_SIGNAL] = {0}; */ +static guint signals[LAST_SIGNAL] = {0}; GType modest_tny_msg_view_get_type (void) @@ -109,6 +145,15 @@ modest_tny_msg_view_class_init (ModestTnyMsgViewClass *klass) gobject_class->finalize = modest_tny_msg_view_finalize; g_type_class_add_private (gobject_class, sizeof(ModestTnyMsgViewPrivate)); + + signals[MAILTO_CLICKED_SIGNAL] = + g_signal_new ("on_mailto_clicked", + G_TYPE_FROM_CLASS (gobject_class), + G_SIGNAL_RUN_FIRST, + G_STRUCT_OFFSET(ModestTnyMsgViewClass, mailto_clicked), + NULL, NULL, + g_cclosure_marshal_VOID__STRING, + G_TYPE_NONE, 1, G_TYPE_STRING/*, 1, G_TYPE_POINTER*/); } static void @@ -122,15 +167,14 @@ modest_tny_msg_view_init (ModestTnyMsgView *obj) priv->gtkhtml = gtk_html_new(); - /* TODO: -> conf-mgr */ - priv->show_attachments = FALSE; + priv->show_attachments_inline = FALSE; gtk_html_set_editable (GTK_HTML(priv->gtkhtml), FALSE); - gtk_html_allow_selection (GTK_HTML(priv->gtkhtml), TRUE); - gtk_html_set_caret_mode (GTK_HTML(priv->gtkhtml), FALSE); - gtk_html_set_blocking (GTK_HTML(priv->gtkhtml), FALSE); - gtk_html_set_images_blocking (GTK_HTML(priv->gtkhtml), FALSE); - + gtk_html_allow_selection (GTK_HTML(priv->gtkhtml), TRUE); + gtk_html_set_caret_mode (GTK_HTML(priv->gtkhtml), FALSE); + gtk_html_set_blocking (GTK_HTML(priv->gtkhtml), FALSE); + gtk_html_set_images_blocking (GTK_HTML(priv->gtkhtml), FALSE); + g_signal_connect (G_OBJECT(priv->gtkhtml), "link_clicked", G_CALLBACK(on_link_clicked), obj); @@ -142,11 +186,11 @@ modest_tny_msg_view_init (ModestTnyMsgView *obj) static void modest_tny_msg_view_finalize (GObject *obj) { - + /* TODO! */ } GtkWidget* -modest_tny_msg_view_new (TnyMsgIface *msg) +modest_tny_msg_view_new (TnyMsgIface *msg, const gboolean show_attachments_inline) { GObject *obj; ModestTnyMsgView* self; @@ -161,33 +205,45 @@ modest_tny_msg_view_new (TnyMsgIface *msg) GTK_POLICY_AUTOMATIC); if (priv->gtkhtml) - gtk_container_add (GTK_CONTAINER(obj), priv->gtkhtml); + gtk_container_add (GTK_CONTAINER(obj), priv->gtkhtml); if (msg) modest_tny_msg_view_set_message (self, msg); + + modest_tny_msg_view_set_show_attachments_inline_flag(self, show_attachments_inline); return GTK_WIDGET(self); } - static gboolean on_link_clicked (GtkWidget *widget, const gchar *uri, ModestTnyMsgView *msg_view) { - ModestTnyMsgViewPrivate *priv; - - - if (g_str_has_prefix(uri, "attachment:")) { - priv = MODEST_TNY_MSG_VIEW_GET_PRIVATE(msg_view); - priv->show_attachments = !priv->show_attachments; - modest_tny_msg_view_set_message(msg_view, priv->msg); + if (g_str_has_prefix(uri, PREFIX_LINK_EMAIL)) { + gchar *s, *p; + /* skip over "mailto:" */ + s = g_strdup(uri + strlen(PREFIX_LINK_EMAIL)); + /* strip ?subject=... and the like */ + for (p = s; p[0]; p++) + if (p[0] == '?') { + p[0] = 0; + break; + } + g_signal_emit(msg_view, signals[MAILTO_CLICKED_SIGNAL], 0, s); + g_free(s); + return TRUE; + } else if (g_str_has_prefix(uri, ATTACHMENT_ID_LINK)) { + /* save or open attachment */ + g_message ("link-to-save: %s", uri); /* FIXME */ + return TRUE; } g_message ("link clicked: %s", uri); /* FIXME */ + return FALSE; + } - static TnyMsgMimePartIface * find_cid_image (TnyMsgIface *msg, const gchar *cid) { @@ -213,30 +269,37 @@ find_cid_image (TnyMsgIface *msg, const gchar *cid) return part; } + static TnyMsgMimePartIface * find_attachment_by_filename (TnyMsgIface *msg, const gchar *fn) { TnyMsgMimePartIface *part = NULL; GList *parts; + gchar *dummy; + gint pos; g_return_val_if_fail (msg, NULL); g_return_val_if_fail (fn, NULL); parts = (GList*) tny_msg_iface_get_parts (msg); - while (parts && !part) { - const gchar *part_fn; - part = TNY_MSG_MIME_PART_IFACE(parts->data); - part_fn = tny_msg_mime_part_iface_get_filename (part); - if (part_fn && strcmp (fn, part_fn) == 0) - return part; /* we found it! */ - - part = NULL; - parts = parts->next; - } + pos = virtual_filename_get_pos(fn); - return part; + if ((pos < 0) || (pos >= g_list_length(parts))) + return NULL; + + part = g_list_nth_data(parts, pos); + + dummy = construct_virtual_filename_from_mime_part(part, pos); + if (strcmp(dummy, fn) == 0) { + g_free(dummy); + return part; + } else { + g_free(dummy); + return NULL; + } } + static gboolean on_url_requested (GtkWidget *widget, const gchar *uri, GtkHTMLStream *stream, @@ -248,6 +311,9 @@ on_url_requested (GtkWidget *widget, const gchar *uri, g_message ("url requested: %s", uri); + if (!modest_tny_msg_view_get_show_attachments_inline_flag(msg_view)) + return TRUE; /* debatable */ + if (g_str_has_prefix (uri, "cid:")) { /* +4 ==> skip "cid:" */ @@ -261,12 +327,11 @@ on_url_requested (GtkWidget *widget, const gchar *uri, tny_msg_mime_part_iface_decode_to_stream (part,tny_stream); gtk_html_stream_close (stream, GTK_HTML_STREAM_OK); } - } else if (g_str_has_prefix (uri, "Attachment:")) { - /* +11 ==> skip... */ - - TnyMsgMimePartIface *part = find_attachment_by_filename (priv->msg, uri + 11); + } else if (g_str_has_prefix (uri, ATTACHMENT_ID_INLINE)) { + TnyMsgMimePartIface *part; + part = find_attachment_by_filename (priv->msg, uri); if (!part) { - g_message ("%s not found", uri + 11); + g_message ("%s not found", uri); gtk_html_stream_close (stream, GTK_HTML_STREAM_ERROR); } else { TnyStreamIface *tny_stream = @@ -279,8 +344,6 @@ on_url_requested (GtkWidget *widget, const gchar *uri, } - - typedef struct { guint offset; guint len; @@ -289,21 +352,138 @@ typedef struct { static gchar * +secure_filename(const gchar *fn) +{ + gchar *tmp, *p; + GString *s; + + s = g_string_new(""); +#if 1 || DEBUG + tmp = g_strdup(fn); + for (p = tmp; p[0] ; p++ ) { + p[0] &= 0x5f; /* 01011111 */ + p[0] |= 0x40; /* 01000000 */ + } + g_string_printf(s, "0x%x:%s", g_str_hash(fn), tmp); + g_free(tmp); + return g_string_free(s, FALSE); +#else + g_string_printf(s, "0x%x", g_str_hash(fn)); + return g_string_free(s, FALSE); +#endif +} + + +static gchar * +construct_virtual_filename(const gchar *filename, + const gint position, + const gchar *id, + const gboolean active) +{ + GString *s; + gchar *fn; + + if (position < 0) + return g_strdup("AttachmentInvalid"); + + s = g_string_new(""); + if (active) + g_string_append(s, ATTACHMENT_ID_INLINE); + else + g_string_append(s, ATTACHMENT_ID_LINK); + g_string_append_printf(s, ":%d:", position); + if (id) + g_string_append(s, id); + g_string_append_c(s, ':'); + + fn = secure_filename(filename); + if (fn) + g_string_append(s, fn); + g_free(fn); + g_string_append_c(s, ':'); + return g_string_free(s, FALSE); +} + + +static gchar * +construct_virtual_filename_from_mime_part(TnyMsgMimePartIface *msg, const gint position) +{ + const gchar *id, *filename; + const gboolean active = TRUE; + + filename = tny_msg_mime_part_iface_get_filename( + TNY_MSG_MIME_PART_IFACE(msg)); + if (!filename) + filename = "[unknown]"; + id = tny_msg_mime_part_iface_get_content_id( + TNY_MSG_MIME_PART_IFACE(msg)); + + return construct_virtual_filename(filename, position, id, active); +} + +const gchar * +get_next_token(const gchar *s, gint *len) +{ + gchar *i1, *i2; + i1 = (char *) s; + i2 = (char *) s; + + while (i2[0]) { + if (i2[0] == ':') + break; + i2++; + } + if (!i2[0]) + return NULL; + *len = i2 - i1; + return ++i2; +} + +/* maybe I should use libregexp */ +gint +virtual_filename_get_pos(const gchar *filename) +{ + const gchar *i1, *i2; + gint len, pos; + GString *dummy; + + /* check prefix */ + if ((!g_str_has_prefix(filename, ATTACHMENT_ID_INLINE ":")) && + (!g_str_has_prefix(filename, ATTACHMENT_ID_LINK ":"))) + return -1; + + i2 = filename; + i2 = get_next_token(i2, &len); + i1 = i2; + + /* get position */ + i2 = get_next_token(i2, &len); + if (i2 == NULL) + return -1; + dummy = g_string_new_len(i1, len); + pos = atoi(dummy->str); + g_string_free(dummy, FALSE); + return pos; +} + + +static gchar * attachments_as_html(ModestTnyMsgView *self, TnyMsgIface *msg) { ModestTnyMsgViewPrivate *priv; gboolean attachments_found = FALSE; GString *appendix; const GList *attachment_list, *attachment; - const gchar *content_type, *filename; + const gchar *content_type, *filename, *id; + gchar *virtual_filename; if (!msg) return g_malloc0(1); priv = MODEST_TNY_MSG_VIEW_GET_PRIVATE (self); - /* CLEANUP: starting a new HTML may be unsupported */ - appendix = g_string_new("\n
Attachments:
\n"); + appendix = g_string_new(""); + g_string_printf(appendix, "\n
%s:
\n", _("Attachments")); attachment_list = tny_msg_iface_get_parts(msg); attachment = attachment_list; @@ -311,24 +491,33 @@ attachments_as_html(ModestTnyMsgView *self, TnyMsgIface *msg) filename = ""; content_type = tny_msg_mime_part_iface_get_content_type( TNY_MSG_MIME_PART_IFACE(attachment->data)); - g_return_if_fail(content_type); - if ( tny_msg_mime_part_iface_content_type_is( - TNY_MSG_MIME_PART_IFACE(attachment->data), - "image/jpeg") - || tny_msg_mime_part_iface_content_type_is( - TNY_MSG_MIME_PART_IFACE(attachment->data), - "image/gif")) { + if (!content_type) + continue; + + if ((strcmp("image/jpeg", content_type) == 0) || + (strcmp("image/gif", content_type) == 0)) { filename = tny_msg_mime_part_iface_get_filename( - TNY_MSG_MIME_PART_IFACE(attachment->data)); + TNY_MSG_MIME_PART_IFACE(attachment->data)); if (!filename) filename = "[unknown]"; else attachments_found = TRUE; - if (priv->show_attachments) { - g_string_append_printf(appendix, "\n
%s\n", filename, filename, filename); - } else { - g_string_append_printf(appendix, "%s: %s
\n", filename, filename, content_type); + id = tny_msg_mime_part_iface_get_content_id( + TNY_MSG_MIME_PART_IFACE(attachment->data)); + if (modest_tny_msg_view_get_show_attachments_inline_flag(self)) { + virtual_filename = construct_virtual_filename(filename, + g_list_position((GList *)attachment_list, (GList *) attachment), + id, TRUE); + g_string_append_printf(appendix, "\n
", virtual_filename); + g_free(virtual_filename); } + virtual_filename = construct_virtual_filename(filename, + g_list_position((GList *)attachment_list, (GList *) attachment), + id, FALSE); + g_string_append_printf(appendix, + "%s: %s
\n", + virtual_filename, filename, content_type); + g_free(virtual_filename); } attachment = attachment->next; } @@ -553,8 +742,7 @@ set_html_message (ModestTnyMsgView *self, TnyMsgMimePartIface *tny_body, TnyMsgI tny_stream_iface_reset (gtkhtml_stream); tny_msg_mime_part_iface_decode_to_stream (tny_body, gtkhtml_stream); html_attachments = attachments_as_html(self, msg); - /* is this clean? */ - gtkhtml_write(gtkhtml_stream, html_attachments, strlen(html_attachments)); + tny_stream_iface_write (gtkhtml_stream, html_attachments, strlen(html_attachments)); tny_stream_iface_reset (gtkhtml_stream); g_object_unref (G_OBJECT(gtkhtml_stream)); @@ -607,10 +795,8 @@ modest_tny_msg_view_get_selected_text (ModestTnyMsgView *self) GtkWidget *html; int len; GtkClipboard *clip; - gchar *text; - GtkTextBuffer *buf; - g_return_if_fail (self); + g_return_val_if_fail (self, NULL); priv = MODEST_TNY_MSG_VIEW_GET_PRIVATE(self); html = priv->gtkhtml; @@ -638,23 +824,52 @@ modest_tny_msg_view_set_message (ModestTnyMsgView *self, TnyMsgIface *msg) priv->msg = msg; fill_gtkhtml_with_txt (self, GTK_HTML(priv->gtkhtml), "", msg); - if (!msg) return; - body = modest_tny_msg_actions_find_body_part (msg, "text/html"); - if (body) { - set_html_message (self, body, msg); - return; - } - - body = modest_tny_msg_actions_find_body_part (msg, "text/plain"); + body = modest_tny_msg_actions_find_body_part (msg, TRUE); if (body) { - set_text_message (self, body, msg); + if (tny_msg_mime_part_iface_content_type_is (body, "text/html")) + set_html_message (self, body, msg); + else + set_text_message (self, body, msg); return; + } else { + /* nothing to show */ } +} + +void +modest_tny_msg_view_redraw (ModestTnyMsgView *self) +{ + ModestTnyMsgViewPrivate *priv; + + g_return_if_fail (self); + priv = MODEST_TNY_MSG_VIEW_GET_PRIVATE(self); + modest_tny_msg_view_set_message(self, priv->msg); +} - /* hmmmmm */ - fill_gtkhtml_with_txt (self, GTK_HTML(priv->gtkhtml), - _("Unsupported message type"), msg); +gboolean +modest_tny_msg_view_get_show_attachments_inline_flag (ModestTnyMsgView *self) +{ + ModestTnyMsgViewPrivate *priv; + + g_return_val_if_fail (self, FALSE); + priv = MODEST_TNY_MSG_VIEW_GET_PRIVATE(self); + return priv->show_attachments_inline; +} + +gboolean +modest_tny_msg_view_set_show_attachments_inline_flag (ModestTnyMsgView *self, gboolean flag) +{ + ModestTnyMsgViewPrivate *priv; + gboolean oldflag; + + g_return_val_if_fail (self, FALSE); + priv = MODEST_TNY_MSG_VIEW_GET_PRIVATE(self); + oldflag = priv->show_attachments_inline; + priv->show_attachments_inline = flag; + if (priv->show_attachments_inline != oldflag) + modest_tny_msg_view_redraw(self); + return priv->show_attachments_inline; }