From 07fc421041196cb6911a4e61298891f0b42f8cd6 Mon Sep 17 00:00:00 2001 From: Sergio Villar Senin Date: Fri, 6 Nov 2009 14:30:44 +0100 Subject: [PATCH] Properly reply to pure HTML emails. Replaced Camel implementation with a GtkHTML based one Fixes NB#140731 --- src/Makefile.am | 4 +- src/modest-formatter.c | 117 +--------------- src/modest-stream-html-to-text.c | 283 ++++++++++++++++++++++++++++++++++++++ src/modest-stream-html-to-text.h | 78 +++++++++++ 4 files changed, 370 insertions(+), 112 deletions(-) create mode 100644 src/modest-stream-html-to-text.c create mode 100644 src/modest-stream-html-to-text.h diff --git a/src/Makefile.am b/src/Makefile.am index b720850..5f73136 100644 --- a/src/Makefile.am +++ b/src/Makefile.am @@ -147,6 +147,8 @@ libmodest_la_SOURCES=\ modest-transport-account-decorator.h \ modest-stream-text-to-html.c \ modest-stream-text-to-html.h \ + modest-stream-html-to-text.c \ + modest-stream-html-to-text.h \ modest-ui-actions.c \ modest-ui-actions.h \ modest-ui-dimming-manager.c \ @@ -232,4 +234,4 @@ EXTRA_DIST=modest-marshal.list \ pkgconfigdir = $(libdir)/pkgconfig pkgconfig_DATA = modest-plugin-1.0.pc -DISTCLEANFILES = $(pkgconfig_DATA) \ No newline at end of file +DISTCLEANFILES = $(pkgconfig_DATA) diff --git a/src/modest-formatter.c b/src/modest-formatter.c index 90f45cf..0328be8 100644 --- a/src/modest-formatter.c +++ b/src/modest-formatter.c @@ -33,11 +33,11 @@ #include #include #include -#include #include "modest-formatter.h" #include "modest-text-utils.h" #include "modest-tny-platform-factory.h" -#include +#include "modest-runtime.h" +#include "modest-stream-html-to-text.h" #define LINE_WRAP 78 #define MAX_BODY_LINES 1024 @@ -68,22 +68,6 @@ static gchar* modest_formatter_wrapper_inline (ModestFormatter *self, const gch static TnyMimePart *find_body_parent (TnyMimePart *part); -static guint -count_end_tag_lines (const gchar *haystack, const gchar *needle) -{ - gchar *tmp; - guint lines = 0; - - tmp = g_strstr_len (haystack, g_utf8_strlen (haystack, -1), ">\n"); - while (tmp && (tmp <= needle)) { - lines++; - tmp += 2; - tmp = g_strstr_len (tmp, g_utf8_strlen (tmp, -1), ">\n"); - } - - return lines; -} - static gchar * extract_text (ModestFormatter *self, TnyMimePart *body) { @@ -104,7 +88,7 @@ extract_text (ModestFormatter *self, TnyMimePart *body) is_html = (g_strcmp0 (tny_mime_part_get_content_type (body), "text/html") == 0); if (is_html) { - input_stream = tny_camel_html_to_text_stream_new (mp_stream); + input_stream = modest_stream_html_to_text_new (mp_stream); } else { input_stream = g_object_ref (mp_stream); } @@ -114,82 +98,6 @@ extract_text (ModestFormatter *self, TnyMimePart *body) line_chars = 0; lines = 0; - /* For pure HTML emails tny_camel_html_to_text_stream inserts - a \n for every ">\n" found in the email including the HTML - headers (, ...). For that reason we need to - remove them from the resulting text as it is artificially - added by the stream */ - if (is_html) { - const guint BUFFER_SIZE = 1024; - TnyStream *is; - gboolean look_for_end_tag, found; - gchar buffer [BUFFER_SIZE + 1]; - gchar *needle; - - is = g_object_ref (mp_stream); - look_for_end_tag = FALSE; - found = FALSE; - - /* This algorithm does not work if the body tag is - spread along 2 different stream reads. But there - are not a lot of changes for this to happen as the - buffer size is big enough in most situations. In - the worst case, when it's not found we just accept - the original translation with the extra "\n" */ - while (!tny_stream_is_eos (is) && !found) { - gint n_read; - - needle = NULL; - memset (buffer, 0, BUFFER_SIZE); - n_read = tny_stream_read (is, buffer, BUFFER_SIZE); - - if (G_UNLIKELY (n_read < 0)) - break; - - buffer[n_read] = '\0'; - - /* If we found body,then look for the end of the tag */ - if (look_for_end_tag) { - needle = strchr (buffer, '>'); - - if (needle) { - found = TRUE; - lines += count_end_tag_lines (buffer, needle); - break; - } - } else { - gchar *closing; - - /* Try to find the tag. There - is no other HTML tag starting by - "bo", and we can detect more cases - were tag falls into two - different stream reads */ - needle = g_strstr_len (buffer, n_read, "'); - if (closing) { - if (*(closing + 1) == '\n') - lines++; - found = TRUE; - break; - } - } - } - if (!found) - lines = 0; - tny_stream_reset (is); - - g_object_unref (is); - } - first_time = TRUE; while (!tny_stream_is_eos (input_stream)) { gchar buffer [128]; @@ -225,22 +133,9 @@ extract_text (ModestFormatter *self, TnyMimePart *body) if (offset - buffer > 0) { gint n_write = 0, to_write = 0; - gchar *buffer_ptr; - - /* Discard lines artificially inserted by - Camel when translating from HTML to - text. Do it only for the first read */ - buffer_ptr = buffer; - if (G_UNLIKELY (first_time) && lines) { - int i; - for (i=0; i < lines; i++) { - buffer_ptr = strchr (buffer_ptr, '\n'); - buffer_ptr++; - } - first_time = FALSE; - } - to_write = offset - buffer_ptr; - n_write = tny_stream_write (stream, buffer_ptr, to_write); + + to_write = offset - buffer; + n_write = tny_stream_write (stream, buffer, to_write); total += n_write; } else if (n_read == -1) { break; diff --git a/src/modest-stream-html-to-text.c b/src/modest-stream-html-to-text.c new file mode 100644 index 0000000..4a0f609 --- /dev/null +++ b/src/modest-stream-html-to-text.c @@ -0,0 +1,283 @@ +/* Copyright (c) 2009, 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. + */ + + +/* modest-stream-text-to-html.c */ + +#include "modest-stream-html-to-text.h" +#include +#include +#include +#include + + +/* 'private'/'protected' functions */ +static void modest_stream_html_to_text_class_init (ModestStreamHtmlToTextClass *klass); +static void modest_stream_html_to_text_init (ModestStreamHtmlToText *obj); +static void modest_stream_html_to_text_finalize (GObject *obj); +static void modest_stream_html_to_text_iface_init (gpointer g_iface, gpointer iface_data); + +typedef struct _ModestStreamHtmlToTextPrivate ModestStreamHtmlToTextPrivate; +struct _ModestStreamHtmlToTextPrivate { + GString *buffer; + gint position; + GtkHTML *html; +}; +#define MODEST_STREAM_HTML_TO_TEXT_GET_PRIVATE(o) (G_TYPE_INSTANCE_GET_PRIVATE((o), \ + MODEST_TYPE_STREAM_HTML_TO_TEXT, \ + ModestStreamHtmlToTextPrivate)) +/* globals */ +static GObjectClass *parent_class = NULL; + +GType +modest_stream_html_to_text_get_type (void) +{ + static GType my_type = 0; + if (!my_type) { + static const GTypeInfo my_info = { + sizeof(ModestStreamHtmlToTextClass), + NULL, /* base init */ + NULL, /* base finalize */ + (GClassInitFunc) modest_stream_html_to_text_class_init, + NULL, /* class finalize */ + NULL, /* class data */ + sizeof(ModestStreamHtmlToText), + 1, /* n_preallocs */ + (GInstanceInitFunc) modest_stream_html_to_text_init, + NULL + }; + + static const GInterfaceInfo iface_info = { + (GInterfaceInitFunc) modest_stream_html_to_text_iface_init, + NULL, /* interface_finalize */ + NULL /* interface_data */ + }; + + my_type = g_type_register_static (G_TYPE_OBJECT, + "ModestStreamHtmlToText", + &my_info, 0); + + g_type_add_interface_static (my_type, TNY_TYPE_STREAM, + &iface_info); + + } + return my_type; +} + +static void +modest_stream_html_to_text_class_init (ModestStreamHtmlToTextClass *klass) +{ + GObjectClass *gobject_class; + gobject_class = (GObjectClass*) klass; + + parent_class = g_type_class_peek_parent (klass); + gobject_class->finalize = modest_stream_html_to_text_finalize; + + g_type_class_add_private (gobject_class, sizeof(ModestStreamHtmlToTextPrivate)); +} + +static void +modest_stream_html_to_text_init (ModestStreamHtmlToText *obj) +{ + ModestStreamHtmlToTextPrivate *priv; + priv = MODEST_STREAM_HTML_TO_TEXT_GET_PRIVATE(obj); + + priv->position = 0; + priv->buffer = NULL; + priv->html = NULL; +} + +static void +modest_stream_html_to_text_finalize (GObject *obj) +{ + ModestStreamHtmlToTextPrivate *priv; + + priv = MODEST_STREAM_HTML_TO_TEXT_GET_PRIVATE(obj); + + if (priv->buffer) + g_string_free (priv->buffer, TRUE); +} + +static gboolean +export_to_txt_cb (const HTMLEngine * engine, + const char *data, + unsigned int len, + void *user_data) +{ + ModestStreamHtmlToTextPrivate *priv; + + priv = MODEST_STREAM_HTML_TO_TEXT_GET_PRIVATE(user_data); + + if (!priv->buffer) + priv->buffer = g_string_new (data); + else + g_string_append (priv->buffer, data); + + return TRUE; +} + +static gboolean +parse_input_stream (ModestStreamHtmlToText *self, + TnyStream *in_stream) +{ + GString *buffer; + GtkHTMLStream *stream = NULL; + ModestStreamHtmlToTextPrivate *priv; + const guint BUFF_SIZE = 4096; + gchar buff[BUFF_SIZE]; + + priv = MODEST_STREAM_HTML_TO_TEXT_GET_PRIVATE(self); + + buffer = g_string_new (NULL); + while (!tny_stream_is_eos (in_stream)) { + gint read; + read = tny_stream_read (in_stream, buff, BUFF_SIZE); + buffer = g_string_append_len (buffer, buff, read); + } + tny_stream_reset (in_stream); + + priv->html = g_object_new (GTK_TYPE_HTML, "visible", FALSE, NULL); + gtk_html_set_default_engine (priv->html, TRUE); + stream = gtk_html_begin_full(priv->html, NULL, "text/html", 0); + gtk_html_write(priv->html, stream, buffer->str, buffer->len); + gtk_html_end(priv->html, stream, 0); + + return gtk_html_export (priv->html, "text/plain", + (GtkHTMLSaveReceiverFn) export_to_txt_cb, self); +} + +TnyStream * +modest_stream_html_to_text_new (TnyStream *in_stream) +{ + GObject *obj; + + obj = G_OBJECT(g_object_new(MODEST_TYPE_STREAM_HTML_TO_TEXT, NULL)); + + if (!parse_input_stream ((ModestStreamHtmlToText *) obj, in_stream)) { + g_warning ("%s: error parsing the input stream", __FUNCTION__); + g_object_unref (obj); + obj = NULL; + } + + return (TnyStream *) obj; +} + +/* the rest are interface functions */ +static ssize_t +html_to_text_read (TnyStream *self, char *buffer, size_t n) +{ + ModestStreamHtmlToTextPrivate *priv; + gint i; + + priv = MODEST_STREAM_HTML_TO_TEXT_GET_PRIVATE (self); + + for (i = 0; (i < n) && ((priv->position + i) < priv->buffer->len) ; i++) + buffer[i] = priv->buffer->str[priv->position + i]; + + priv->position += i; + + return i; +} + +static ssize_t +html_to_text_write (TnyStream *self, const char *buffer, size_t n) +{ + return -1; /* we cannot write */ +} + +static gint +html_to_text_flush (TnyStream *self) +{ + /* ModestStreamHtmlToTextPrivate *priv = MODEST_STREAM_HTML_TO_TEXT_GET_PRIVATE (self); */ + + return 0; +} + + +static gint +html_to_text_close (TnyStream *self) +{ + ModestStreamHtmlToTextPrivate *priv; + + priv = MODEST_STREAM_HTML_TO_TEXT_GET_PRIVATE(self); + + tny_stream_flush (self); + + return 0; +} + + +static gboolean +html_to_text_is_eos (TnyStream *self) +{ + ModestStreamHtmlToTextPrivate *priv; + + priv = MODEST_STREAM_HTML_TO_TEXT_GET_PRIVATE(self); + + return (priv->position >= (priv->buffer->len - 1)); +} + + + +static gint +html_to_text_reset (TnyStream *self) +{ + ModestStreamHtmlToTextPrivate *priv; + + priv = MODEST_STREAM_HTML_TO_TEXT_GET_PRIVATE(self); + priv->position = 0; + + return priv->position; +} + + +static ssize_t +html_to_text_write_to_stream (TnyStream *self, TnyStream *output) +{ + return 0; +} + + +static void +modest_stream_html_to_text_iface_init (gpointer g_iface, gpointer iface_data) +{ + TnyStreamIface *klass; + + g_return_if_fail (g_iface); + + klass = (TnyStreamIface*) g_iface; + + klass->read = html_to_text_read; + klass->write = html_to_text_write; + klass->flush = html_to_text_flush; + klass->close = html_to_text_close; + klass->is_eos = html_to_text_is_eos; + klass->reset = html_to_text_reset; + klass->write_to_stream = html_to_text_write_to_stream; +} diff --git a/src/modest-stream-html-to-text.h b/src/modest-stream-html-to-text.h new file mode 100644 index 0000000..25baf62 --- /dev/null +++ b/src/modest-stream-html-to-text.h @@ -0,0 +1,78 @@ +/* Copyright (c) 2007, 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. + */ + + +/* modest-stream-text-to-html.h */ + +#ifndef __MODEST_STREAM_HTML_TO_TEXT_H__ +#define __MODEST_STREAM_HTML_TO_TEXT_H__ + +#include +#include +#include + +G_BEGIN_DECLS + +/* convenience macros */ +#define MODEST_TYPE_STREAM_HTML_TO_TEXT (modest_stream_html_to_text_get_type()) +#define MODEST_STREAM_HTML_TO_TEXT(obj) (G_TYPE_CHECK_INSTANCE_CAST((obj),MODEST_TYPE_STREAM_HTML_TO_TEXT,ModestStreamHtmlToText)) +#define MODEST_STREAM_HTML_TO_TEXT_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST((klass),MODEST_TYPE_STREAM_HTML_TO_TEXT,ModestStreamHtmlToTextClass)) +#define MODEST_IS_STREAM_HTML_TO_TEXT(obj) (G_TYPE_CHECK_INSTANCE_TYPE((obj),MODEST_TYPE_STREAM_HTML_TO_TEXT)) +#define MODEST_IS_STREAM_HTML_TO_TEXT_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE((klass),MODEST_TYPE_STREAM_HTML_TO_TEXT)) +#define MODEST_STREAM_HTML_TO_TEXT_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS((obj),MODEST_TYPE_STREAM_HTML_TO_TEXT,ModestStreamHtmlToTextClass)) + +typedef struct _ModestStreamHtmlToText ModestStreamHtmlToText; +typedef struct _ModestStreamHtmlToTextClass ModestStreamHtmlToTextClass; + +struct _ModestStreamHtmlToText { + GObject parent; +}; + +struct _ModestStreamHtmlToTextClass { + GObjectClass parent_class; +}; + +GType modest_stream_html_to_text_get_type (void) G_GNUC_CONST; + + +/** + * modest_stream_html_to_text_new: + * @stream: a #GtkHTMLStream + * + * creates a new #ModestStreamHtmlToText + * + * Returns: a new #ModestStreamHtmlToText + **/ +TnyStream* modest_stream_html_to_text_new (TnyStream *out_stream); + + +G_END_DECLS + +#endif /* __MODEST_STREAM_HTML_TO_TEXT_H__ */ + -- 1.7.9.5