X-Git-Url: http://git.maemo.org/git/?a=blobdiff_plain;f=src%2Fmodest-tny-msg-view.c;h=13ece7ae5879e1df133511af7a2e1cafe2163f07;hb=5bf2f24ec962205cc2c0af1e658189913ce7cfef;hp=c23098798c2446062c57119fc42e704d3cfd1e0c;hpb=760ddf92f15a11e080a26c560c2f09beddc5f32c;p=modest diff --git a/src/modest-tny-msg-view.c b/src/modest-tny-msg-view.c index c230987..13ece7a 100644 --- a/src/modest-tny-msg-view.c +++ b/src/modest-tny-msg-view.c @@ -4,11 +4,15 @@ #include "modest-tny-msg-view.h" #include "modest-tny-stream-gtkhtml.h" +#include "modest-tny-msg-actions.h" + #include #include #include #include #include +#include +#include /* 'private'/'protected' functions */ static void modest_tny_msg_view_class_init (ModestTnyMsgViewClass *klass); @@ -17,15 +21,20 @@ static void modest_tny_msg_view_finalize (GObject *obj); static GSList* get_url_matches (GString *txt); -static gboolean fill_gtkhtml_with_txt (GtkHTML* gtkhtml, const gchar* txt); +static gboolean fill_gtkhtml_with_txt (ModestTnyMsgView *self, GtkHTML* gtkhtml, const gchar* txt, TnyMsgIface *msg); static gboolean on_link_clicked (GtkWidget *widget, const gchar *uri, ModestTnyMsgView *msg_view); 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" +gint virtual_filename_get_pos(const gchar *filename); /* * we need these regexps to find URLs in plain text e-mails */ @@ -63,6 +72,7 @@ typedef struct _ModestTnyMsgViewPrivate ModestTnyMsgViewPrivate; struct _ModestTnyMsgViewPrivate { GtkWidget *gtkhtml; TnyMsgIface *msg; + gboolean show_attachments_inline; }; #define MODEST_TNY_MSG_VIEW_GET_PRIVATE(o) (G_TYPE_INSTANCE_GET_PRIVATE((o), \ MODEST_TYPE_TNY_MSG_VIEW, \ @@ -118,13 +128,15 @@ modest_tny_msg_view_init (ModestTnyMsgView *obj) priv->msg = NULL; priv->gtkhtml = gtk_html_new(); + + 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); @@ -136,11 +148,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; @@ -155,25 +167,33 @@ 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) { + + 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) { @@ -188,6 +208,7 @@ find_cid_image (TnyMsgIface *msg, const gchar *cid) const gchar *part_cid; part = TNY_MSG_MIME_PART_IFACE(parts->data); part_cid = tny_msg_mime_part_iface_get_content_id (part); + printf("CMP:%s:%s\n", cid, part_cid); if (part_cid && strcmp (cid, part_cid) == 0) return part; /* we found it! */ @@ -199,6 +220,36 @@ find_cid_image (TnyMsgIface *msg, const gchar *cid) } +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); + pos = virtual_filename_get_pos(fn); + + 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, @@ -210,6 +261,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:" */ @@ -223,13 +277,23 @@ 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_ID_INLINE)) { + TnyMsgMimePartIface *part; + part = find_attachment_by_filename (priv->msg, uri); + if (!part) { + g_message ("%s not found", uri); + gtk_html_stream_close (stream, GTK_HTML_STREAM_ERROR); + } else { + TnyStreamIface *tny_stream = + TNY_STREAM_IFACE(modest_tny_stream_gtkhtml_new(stream)); + tny_msg_mime_part_iface_decode_to_stream (part,tny_stream); + gtk_html_stream_close (stream, GTK_HTML_STREAM_OK); + } } return TRUE; } - - typedef struct { guint offset; guint len; @@ -237,6 +301,182 @@ typedef struct { } url_match_t; +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, *id; + gchar *virtual_filename; + + if (!msg) + return g_malloc0(1); + + priv = MODEST_TNY_MSG_VIEW_GET_PRIVATE (self); + + appendix = g_string_new(""); + g_string_printf(appendix, "\n
%s:
\n", _("Attachments")); + + attachment_list = tny_msg_iface_get_parts(msg); + attachment = attachment_list; + while (attachment) { + filename = ""; + content_type = tny_msg_mime_part_iface_get_content_type( + TNY_MSG_MIME_PART_IFACE(attachment->data)); + 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)); + if (!filename) + filename = "[unknown]"; + else + attachments_found = TRUE; + 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; + } + g_string_append(appendix, ""); + if (!attachments_found) + g_string_assign(appendix, ""); + return g_string_free(appendix, FALSE); +} + static void hyperlinkify_plain_text (GString *txt) { @@ -261,8 +501,10 @@ hyperlinkify_plain_text (GString *txt) g_free (url); g_free (repl); - + + g_free (cursor->data); } + g_slist_free (match_list); } @@ -322,7 +564,7 @@ convert_to_html (const gchar *data) } } } - + g_string_append (html, ""); hyperlinkify_plain_text (html); @@ -410,45 +652,31 @@ get_url_matches (GString *txt) } static gboolean -fill_gtkhtml_with_txt (GtkHTML* gtkhtml, const gchar* txt) +fill_gtkhtml_with_txt (ModestTnyMsgView *self, GtkHTML* gtkhtml, const gchar* txt, TnyMsgIface *msg) { - gchar *html; + GString *html; + gchar *html_attachments; g_return_val_if_fail (gtkhtml, FALSE); g_return_val_if_fail (txt, FALSE); - html = convert_to_html (txt); - gtk_html_load_from_string (gtkhtml, html, strlen(html)); - g_free (html); + html = g_string_new(convert_to_html (txt)); + html_attachments = attachments_as_html(self, msg); + g_string_append(html, html_attachments); + + gtk_html_load_from_string (gtkhtml, html->str, html->len); + g_string_free (html, TRUE); + g_free(html_attachments); return TRUE; } -static TnyMsgMimePartIface * -find_body_part (TnyMsgIface *msg, const gchar *mime_type) -{ - TnyMsgMimePartIface *part = NULL; - GList *parts; - - g_return_val_if_fail (msg, NULL); - g_return_val_if_fail (mime_type, NULL); - - parts = (GList*) tny_msg_iface_get_parts (msg); - while (parts && !part) { - part = TNY_MSG_MIME_PART_IFACE(parts->data); - if (!tny_msg_mime_part_iface_content_type_is (part, mime_type)) - part = NULL; - parts = parts->next; - } - - return part; -} - static gboolean -set_html_message (ModestTnyMsgView *self, TnyMsgMimePartIface *tny_body) +set_html_message (ModestTnyMsgView *self, TnyMsgMimePartIface *tny_body, TnyMsgIface *msg) { + gchar *html_attachments; TnyStreamIface *gtkhtml_stream; ModestTnyMsgViewPrivate *priv; @@ -463,9 +691,12 @@ set_html_message (ModestTnyMsgView *self, TnyMsgMimePartIface *tny_body) 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); + tny_stream_iface_write (gtkhtml_stream, html_attachments, strlen(html_attachments)); tny_stream_iface_reset (gtkhtml_stream); g_object_unref (G_OBJECT(gtkhtml_stream)); + g_free (html_attachments); return TRUE; } @@ -474,7 +705,7 @@ set_html_message (ModestTnyMsgView *self, TnyMsgMimePartIface *tny_body) /* this is a hack --> we use the tny_text_buffer_stream to * get the message text, then write to gtkhtml 'by hand' */ static gboolean -set_text_message (ModestTnyMsgView *self, TnyMsgMimePartIface *tny_body) +set_text_message (ModestTnyMsgView *self, TnyMsgMimePartIface *tny_body, TnyMsgIface *msg) { GtkTextBuffer *buf; GtkTextIter begin, end; @@ -497,8 +728,8 @@ set_text_message (ModestTnyMsgView *self, TnyMsgMimePartIface *tny_body) gtk_text_buffer_get_bounds (buf, &begin, &end); txt = gtk_text_buffer_get_text (buf, &begin, &end, FALSE); - fill_gtkhtml_with_txt (GTK_HTML(priv->gtkhtml), txt); - + fill_gtkhtml_with_txt (self, GTK_HTML(priv->gtkhtml), txt, msg); + g_object_unref (G_OBJECT(txt_stream)); g_object_unref (G_OBJECT(buf)); @@ -506,7 +737,7 @@ set_text_message (ModestTnyMsgView *self, TnyMsgMimePartIface *tny_body) return TRUE; } -GtkTextBuffer * +gchar * modest_tny_msg_view_get_selected_text (ModestTnyMsgView *self) { ModestTnyMsgViewPrivate *priv; @@ -514,28 +745,20 @@ 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; /* I'm sure there is a better way to check for selected text */ sel = gtk_html_get_selection_html(GTK_HTML(html), &len); - if (sel == NULL) + if (!sel) return NULL; + g_free(sel); clip = gtk_widget_get_clipboard(html, GDK_SELECTION_PRIMARY); - text = gtk_clipboard_wait_for_text(clip); - if (text == NULL) - return NULL; - - buf = gtk_text_buffer_new(NULL); - gtk_text_buffer_set_text(buf, text, -1); - g_free(text); - return buf; + return gtk_clipboard_wait_for_text(clip); } void @@ -550,24 +773,53 @@ modest_tny_msg_view_set_message (ModestTnyMsgView *self, TnyMsgIface *msg) priv->msg = msg; - fill_gtkhtml_with_txt (GTK_HTML(priv->gtkhtml), ""); - + fill_gtkhtml_with_txt (self, GTK_HTML(priv->gtkhtml), "", msg); if (!msg) return; - body = find_body_part (msg, "text/html"); + body = modest_tny_msg_actions_find_body_part (msg, TRUE); if (body) { - set_html_message (self, body); - return; - } - - body = find_body_part (msg, "text/plain"); - if (body) { - set_text_message (self, body); + 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; - /* hmmmmm */ - fill_gtkhtml_with_txt (GTK_HTML(priv->gtkhtml), - _("Unsupported message type")); + g_return_if_fail (self); + priv = MODEST_TNY_MSG_VIEW_GET_PRIVATE(self); + modest_tny_msg_view_set_message(self, priv->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; }