[Xfce4-commits] <postler:master> Show thread parents and render threads as one message

Christian Dywan noreply at xfce.org
Fri Jun 24 02:52:01 CEST 2011


Updating branch refs/heads/master
         to eee7ce4e082ae60501f32bf75740ab02c34ff85d (commit)
       from 8d0ba4be65faaac775832aae6a89f2d6f02b20fe (commit)

commit eee7ce4e082ae60501f32bf75740ab02c34ff85d
Author: Christian Dywan <christian at twotoasts.de>
Date:   Thu Jun 23 22:19:11 2011 +0200

    Show thread parents and render threads as one message
    
    Message parsing moves into Postler.Message proper so
    that multiple messages can be read and rendered.
    
    Signatures of project mail are hidden in thread replies.

 postler/postler-client.vala   |    1 -
 postler/postler-content.vala  |  297 ++++++-----------------------------------
 postler/postler-index.vala    |    5 +-
 postler/postler-message.vala  |  261 ++++++++++++++++++++++++++++++++++++-
 postler/postler-messages.vala |    2 +-
 5 files changed, 304 insertions(+), 262 deletions(-)

diff --git a/postler/postler-client.vala b/postler/postler-client.vala
index 2fb27bb..302fb13 100644
--- a/postler/postler-client.vala
+++ b/postler/postler-client.vala
@@ -85,7 +85,6 @@ namespace Postler {
                 foreach (string message_id in ids) {
                     var child = new Message.from_id (message_id);
                     child.from_hash_table (yield client.get_message (child.id, MessageFlags.NONE));
-                    stdout.printf ("A %s %s\n", child.id, child.subject);
                     messages.append (child);
                 }
             }
diff --git a/postler/postler-content.vala b/postler/postler-content.vala
index f6a89b9..55eb739 100644
--- a/postler/postler-content.vala
+++ b/postler/postler-content.vala
@@ -9,67 +9,6 @@
  See the file COPYING for the full license text.
 */
 
-public class Postler.MessagePart : Object {
-    public StringBuilder? body;
-    public string? mime_type;
-    public string? charset;
-    public bool plain_text;
-    public string? content_disposition;
-    public string? content_id;
-    public string? filename;
-    public MessagePart (string mime_type) {
-        body = new StringBuilder ();
-        this.mime_type = mime_type;
-        plain_text = mime_type == "text/plain";
-        filename = null;
-    }
-
-    public bool is_empty () {
-        bool empty = true;
-        if (!(body == null)) {
-            var b_str = body.str.strip ();
-            string[] b_str_parts = b_str.split ("<br>");
-            long str_length = 0;
-            foreach (var current_line in b_str_parts) {
-                string[] split_split = current_line.strip().split("\n");
-                foreach (var between_linebreaks in split_split)
-                str_length = between_linebreaks.strip().length;
-            }
-            if (str_length != 0)
-                empty = false;
-        }
-        return empty;
-    }
-
-    public string get_plain_text () {
-        string body_plain = body.str;
-        if ((body != null) && (mime_type != null)) {
-            try {
-                string plain_text = "";
-                int std_in;
-                int std_out;
-                string[] argv = { "lynx", "-force_html", "-width=900", "-dump",
-                                  "-display_charset=utf-8", "-stdin" };
-                Process.spawn_async_with_pipes (null, argv, null,
-                    SpawnFlags.SEARCH_PATH, null, null,
-                    out std_in, out std_out, null);
-                IOChannel ioc_stdout = new IOChannel.unix_new (std_out);
-                IOChannel ioc_stdin = new IOChannel.unix_new (std_in);
-                char[] content_chars = (char[]) (body.str.data);
-                var status = ioc_stdin.write_chars (content_chars, null);
-                ioc_stdin.shutdown (true);
-                if (status != IOStatus.ERROR)
-                    status = ioc_stdout.read_to_end (out plain_text, null);
-                if (status != IOStatus.ERROR)
-                    body_plain = plain_text;
-            } catch (Error error) {
-                GLib.message (_("Error converting HTML to text: %s"), error.message);
-            }
-        }
-        return body_plain;
-    }
-}
-
 struct Postler.EmoticonMapping {
     public string token;
     public string icon_name;
@@ -79,7 +18,6 @@ public class Postler.Content : WebKit.WebView {
     AccountInfo? selected_account;
     public Message message { get; private set; }
     string charset;
-    string content_encoding;
     string carbon_copy;
     string blind_copy;
     string reply;
@@ -125,6 +63,15 @@ public class Postler.Content : WebKit.WebView {
         .body {
             font-size: 100% !important;
         }
+        .reply, .project_reply {
+            border-radius: 15px;
+            border: 1px solid ButtonText;
+            background-color: ButtonFace; color: ButtonText;
+            padding: 1em; margin: 1em;
+        }
+        .project_reply .signature {
+            display: none;
+        }
         /* Quotations */
         blockquote {
             margin: 1em;
@@ -424,27 +371,6 @@ public class Postler.Content : WebKit.WebView {
         "([a-zA-Z0-9.\\-]+@[a-zA-Z0-9.\\-]+[a-zA-Z0-9.]+)"
     };
 
-    static bool evaluate_hex (GLib.MatchInfo     match_info,
-                              GLib.StringBuilder result) {
-        string match = "0x" + match_info.fetch (2);
-        result.append_printf ("%c", (int)match.to_ulong (null));
-        return false;
-    }
-
-    internal static string quoted_printable_decode (string quoted) {
-        /* =20 is not hex-encoded, to faciliate quote handling */
-        string unquoted = quoted.replace ("=20", " ");
-        try {
-            var regex = new GLib.Regex ("([=]([0-9A-F][0-9A-F]))");
-            return regex.replace_eval (unquoted, -1, 0, 0, evaluate_hex);
-        }
-        catch (GLib.RegexError error) {
-            GLib.critical (_("Failed to decode string \"%s\": %s"),
-                           unquoted, error.message);
-        }
-        return unquoted;
-    }
-
     static string parse_encoded (string quoted, out string charset) {
         return Postler.Messages.parse_encoded (quoted, out charset);
     }
@@ -599,45 +525,6 @@ public class Postler.Content : WebKit.WebView {
             "text/html", "UTF-8", "about:blank");
     }
 
-    static void parse_content_type (string? content_type, ref string charset,
-        ref string boundary, ref string mime_type, ref string filename) {
-        if (content_type == null)
-            return;
-
-        string[] parts = content_type.replace (" = ", "=").split_set ("; \t");
-        filename = null;
-        for (int i = 0; i < parts.length; i++) {
-            string part = parts[i];
-            unowned string? next = parts[i + 1];
-            if ("\"" in part && next != null && "\"" in next) {
-                part += parts[i + 1];
-                i++;
-            }
-
-            if (part.has_prefix ("charset="))
-                charset = part.substring (8, part.length - 8).replace ("\"", "");
-            else if (part.has_prefix ("name=") || part.has_prefix ("NAME="))
-                filename = part.substring (5, part.length - 5).replace ("\"", "");
-            else if (part.has_prefix ("filename="))
-                filename = part.substring (9, part.length - 9).replace ("\"", "");
-            else if (part.down ().has_prefix ("boundary="))
-                boundary = part.substring (9, part.length - 9).replace ("\"", "");
-            else if (part != "" && !part.contains ("="))
-                mime_type = ascii_strdown (part);
-        }
-
-        if (filename != null) {
-            string filename_charset;
-            filename = parse_encoded (filename, out filename_charset);
-        }
-
-        if (mime_type == "application/octet-stream" && filename != null) {
-            uchar[] data = {};
-            bool uncertain;
-            mime_type = g_content_type_guess (filename, data, out uncertain);
-        }
-    }
-
     bool mime_type_is_text (string mime_type) {
         return g_content_type_is_a (mime_type, "text/plain");
     }
@@ -675,7 +562,7 @@ public class Postler.Content : WebKit.WebView {
 
         string? boundary = null;
         string? fname = null;
-        parse_content_type (content_type, ref charset, ref boundary,
+        Postler.Message.parse_content_type (content_type, ref charset, ref boundary,
                             ref mime_type, ref fname);
         load_string ("""
             <style text="text/css">
@@ -735,149 +622,41 @@ public class Postler.Content : WebKit.WebView {
             list_unsubscribe = linkify_address (list_unsubscribe, null);
         }
 
-        message_parts = new List<MessagePart> ();
-        content_encoding = message.get_field ("content-transfer-encoding");
-        parse_body (message.get_stream (), message.get_field ("content-type"));
+        message.parse_body ();
+        message_parts = message.parts.copy ();
         notify_property ("message-parts");
     }
 
-    void parse_body (DataInputStream stream, string? content_type) throws GLib.Error {
-
+    public async bool display (string location, AccountInfo? account_info=null) {
+        selected_account = account_info;
         try {
-            string? boundary = null;
-            string mime_type = "text/plain";
-            string? fname = null;
-            parse_content_type (content_type, ref charset, ref boundary,
-                                ref mime_type, ref fname);
-
-            uint multipart = mime_type.has_prefix ("multipart/") ? 1 : 0;
-            if (multipart > 0 && boundary != null)
-                boundary = boundary.replace ("\"", " ").strip ();
-            else
-                multipart = 0;
-            if (!(mime_type_is_text (mime_type) || multipart > 0)) {
-                throw new GLib.FileError.FAILED (
-                    _("This message of type \"%s\" can't be displayed.").
-                    printf (mime_type));
-            }
-
-            MessagePart? message_part = null;
-            /* Message body starts here */
-            if (multipart == 0) {
-                message_part = new MessagePart (mime_type);
-                message_parts.append (message_part);
-            }
-
-            string inner_boundary = "";
-            string line;
-            string previous_line = "";
-            while ((line = stream.read_line (null, null)) != null) {
-                if (multipart > 0) {
-                    if (line.has_prefix ("--")) {
-                        if (line == "--" + inner_boundary) {
-                            message_part = new MessagePart ("text/plain");
-                            message_parts.append (message_part);
-                            multipart = 2;
-                            continue;
-                        } else if (line == "--" + boundary) {
-                            inner_boundary = "";
-                            message_part = new MessagePart ("text/plain");
-                            message_parts.append (message_part);
-                            multipart = 2;
-                            continue;
-                        } else if (line == "--" + boundary + "--") {
-                            /* Final boundary, we can skip it */
-                            continue;
-                        }
-                    }
-                    else if (multipart == 2) {
-                        if (line == "") {
-                            multipart++;
-                            continue;
-                        }
-
-                        if (line[0] == '\t' || line[0] == ' ')
-                        line = previous_line + " " + line.chug ();
-                        previous_line = line;
-
-                        string[] parts = line.split (":", 2);
-                        string field = ascii_strdown (parts[0] ?? "");
-                        if (field == "content-type") {
-                            string ctype = charset;
-                            string mtype = "text/plain";
-                            fname = null;
-                            parse_content_type (parts[1].strip (), ref ctype,
-                                ref inner_boundary, ref mtype, ref fname);
-                            message_part.filename = fname;
-                            message_part.mime_type = mtype;
-                            message_part.charset = ctype;
-                            message_part.plain_text = mtype == "text/plain";
-                        }
-                        else if (field == "content-transfer-encoding") {
-                            content_encoding = ascii_strdown (parts[1].strip ());
-                        }
-                        else if (field == "content-disposition" ) {
-                            string content_disposition = parts[1].strip ();
-                            string cset = charset;
-                            string mtype = "text/plain";
-                            fname = null;
-                            parse_content_type (content_disposition, ref cset,
-                                ref inner_boundary, ref mtype, ref fname);
-                            message_part.filename = fname;
-                            if (content_disposition.has_prefix ("attachment"))
-                                message_part.content_disposition = content_disposition;
-                            else if (content_disposition.has_prefix ("inline"))
-                                message_part.content_disposition = content_disposition;
-                        }
-                        else if (field == "content-id") {
-                            string content_id = parts[1].strip ();
-                            if (content_id[0] == '<' && content_id.has_suffix (">"))
-                                message_part.content_id = content_id.slice (1, -1);
+            parse_message (location);
+            var client = new Postler.Client ();
+            GLib.List<Message> thread = yield client.get_thread (message);
+            if (thread.nth_data (0) != null) {
+                text_part = null;
+                html_part = new MessagePart ("text/html");
+                string reply_class = "reply";
+                if (message.project != null)
+                    reply_class = "project_reply";
+                foreach (var child in thread) {
+                    message = new Message.from_file (File.new_for_path (child.get_path ()));
+                    message.parse_body ();
+                    foreach (var thread_part in message.parts) {
+                        if (mime_type_is_text (thread_part.mime_type)) {
+                            html_part.body.append ("<div class=\"%s\">%s</div>"
+                                .printf (reply_class,
+                                         render_plain_text (thread_part.body.str)));
+                            break;
                         }
-                        continue;
                     }
-                    else if (multipart == 1)
-                        continue;
-                }
-
-                if (content_encoding == "quoted-printable")
-                    line = quoted_printable_decode (line);
-                else if (content_encoding == "base64"
-                      && mime_type_is_text (message_part.mime_type))
-                    line = line != "" ? (string)GLib.Base64.decode (line) : "";
-                try {
-                    string cset = message_part.charset ?? charset;
-                    if (cset != "UTF-8")
-                        line = GLib.convert (line, -1, "UTF-8", cset, null);
-                }
-                catch (GLib.ConvertError error) { }
-                if (message_part.plain_text)
-                    line = line.replace ("<", "<");
-                if (content_encoding == "base64") {
-                    if (message_part.plain_text)
-                        line = line.replace ("\n", "<br>");
-                    message_part.body.append (line);
-                } else {
-                    message_part.body.append (line);
-                    if (content_encoding == "quoted-printable" && line.has_suffix ("="))
-                        message_part.body.truncate (message_part.body.len - 1);
-                    else if (message_part.plain_text)
-                        message_part.body.append ("<br>");
-                    else if (mime_type_is_text (message_part.mime_type))
-                        message_part.body.append ("\n");
-                    else
-                        message_part.body.append (" ");
                 }
+                message_parts = new List<MessagePart> ();
+                message_parts.append (html_part);
+                notify_property ("message-parts");
+                display_part (html_part);
+                return false;
             }
-        } catch (GLib.Error error) {
-            throw error;
-        }
-    }
-
-    public bool display (string location, AccountInfo? account_info=null) {
-        selected_account = account_info;
-        try {
-            parse_message (location);
 
             /* Look for an HTML part, or otherwise plain text */
             html_part = null;
@@ -991,6 +770,8 @@ public class Postler.Content : WebKit.WebView {
         void* link_color;
         widget.style_get ("link-color", out link_color);
         return style_sheet.replace (
+            "ButtonText", color_to_rgb (widget.style.fg[state])).replace (
+            "ButtonFace", color_to_rgb (widget.style.bg[state])).replace (
             "WindowText", color_to_rgb (widget.style.fg[state])).replace (
             "Window", color_to_rgb (widget.style.base[state])).replace (
             "Link", color_to_rgb (*((Gdk.Color*)link_color)));
diff --git a/postler/postler-index.vala b/postler/postler-index.vala
index 2178618..db2ee44 100644
--- a/postler/postler-index.vala
+++ b/postler/postler-index.vala
@@ -218,7 +218,10 @@ namespace Postler {
         public string[] get_messages (string folder) throws GLib.Error {
             if (statement_list == null) {
                 if (database.prepare_v2 ("""
-                    SELECT id FROM messages WHERE uri LIKE ?1 GROUP BY id ORDER BY date ASC
+                    SELECT threads AS thread_id FROM messages
+                    WHERE uri LIKE ?1 GROUP BY threads
+                    HAVING (SELECT COUNT (*) FROM messages WHERE id = thread_id)
+                    ORDER BY date ASC
                     """,
                     -1, out statement_list) != Sqlite.OK)
                     throw new GLib.FileError.FAILED (_("Failed to list messages: %s"), database.errmsg ());
diff --git a/postler/postler-message.vala b/postler/postler-message.vala
index 24fa0ee..9c6ae4e 100644
--- a/postler/postler-message.vala
+++ b/postler/postler-message.vala
@@ -10,6 +10,69 @@
 */
 
 namespace Postler {
+    public class MessagePart : Object {
+        public StringBuilder? body;
+        public string? mime_type;
+        public string? charset;
+        public bool plain_text;
+        public string? content_disposition;
+        public string? content_id;
+        public string? filename;
+
+        public MessagePart (string mime_type) {
+            body = new StringBuilder ();
+            this.mime_type = mime_type;
+            plain_text = mime_type == "text/plain";
+            filename = null;
+        }
+
+        public bool is_empty () {
+            bool empty = true;
+            if (!(body == null)) {
+                var b_str = body.str.strip ();
+                string[] b_str_parts = b_str.split ("<br>");
+                long str_length = 0;
+                foreach (var current_line in b_str_parts) {
+                    string[] split_split = current_line.strip().split("\n");
+                    foreach (var between_linebreaks in split_split)
+                    str_length = between_linebreaks.strip().length;
+                }
+                if (str_length != 0)
+                    empty = false;
+            }
+            return empty;
+        }
+
+        public string get_plain_text () {
+            string body_plain = body.str;
+            if ((body != null) && (mime_type != null)) {
+                try {
+                    string plain_text = "";
+                    int std_in;
+                    int std_out;
+                    string[] argv = { "lynx", "-force_html", "-width=900", "-dump",
+                                      "-display_charset=utf-8", "-stdin" };
+                    Process.spawn_async_with_pipes (null, argv, null,
+                        SpawnFlags.SEARCH_PATH, null, null,
+                        out std_in, out std_out, null);
+                    IOChannel ioc_stdout = new IOChannel.unix_new (std_out);
+                    IOChannel ioc_stdin = new IOChannel.unix_new (std_in);
+                    char[] content_chars = (char[]) (body.str.data);
+                    var status = ioc_stdin.write_chars (content_chars, null);
+                    ioc_stdin.shutdown (true);
+                    if (status != IOStatus.ERROR)
+                        status = ioc_stdout.read_to_end (out plain_text, null);
+                    if (status != IOStatus.ERROR)
+                        body_plain = plain_text;
+                }
+                catch (Error error) {
+                    GLib.message (_("Error converting HTML to text: %s"), error.message);
+                }
+            }
+            return body_plain;
+        }
+    }
+
     public enum MessageFlags {
         NONE = '0',
         MARK_READ = '1',
@@ -43,8 +106,8 @@ namespace Postler {
         public string list { public get; set; }
         GLib.HashTable<string,string> fields = new GLib.HashTable<string,string> (str_hash, str_equal);
         public string get_field (string field) { return fields.lookup (field); }
+        public GLib.List<MessagePart>? parts = null;
         GLib.DataInputStream? stream = null;
-        public GLib.DataInputStream get_stream () { return stream; }
 
         bool init (GLib.Cancellable? cancellable = null) throws GLib.Error {
             return false;
@@ -273,6 +336,202 @@ namespace Postler {
                     reply_to = null;
             }
         }
+
+        public static void parse_content_type (string? content_type, ref string charset,
+            ref string boundary, ref string mime_type, ref string filename) {
+
+            if (content_type == null)
+                return;
+
+            string[] parts = content_type.replace (" = ", "=").split_set ("; \t");
+            filename = null;
+            for (int i = 0; i < parts.length; i++) {
+                string part = parts[i];
+                unowned string? next = parts[i + 1];
+                if ("\"" in part && next != null && "\"" in next) {
+                    part += parts[i + 1];
+                    i++;
+                }
+
+                if (part.has_prefix ("charset="))
+                    charset = part.substring (8, part.length - 8).replace ("\"", "");
+                else if (part.has_prefix ("name=") || part.has_prefix ("NAME="))
+                    filename = part.substring (5, part.length - 5).replace ("\"", "");
+                else if (part.has_prefix ("filename="))
+                    filename = part.substring (9, part.length - 9).replace ("\"", "");
+                else if (part.down ().has_prefix ("boundary="))
+                    boundary = part.substring (9, part.length - 9).replace ("\"", "");
+                else if (part != "" && !part.contains ("="))
+                    mime_type = ascii_strdown (part);
+            }
+
+            if (filename != null) {
+                string filename_charset;
+                filename = parse_encoded (filename, out filename_charset);
+            }
+
+            if (mime_type == "application/octet-stream" && filename != null) {
+                uchar[] data = {};
+                bool uncertain;
+                mime_type = g_content_type_guess (filename, data, out uncertain);
+            }
+        }
+
+        bool mime_type_is_text (string mime_type) {
+            return g_content_type_is_a (mime_type, "text/plain");
+        }
+
+        static bool evaluate_hex (GLib.MatchInfo match_info, GLib.StringBuilder result) {
+            string match = "0x" + match_info.fetch (2);
+            result.append_printf ("%c", (int)match.to_ulong (null));
+            return false;
+        }
+
+        public static string quoted_printable_decode (string quoted) {
+            /* =20 is not hex-encoded, to faciliate quote handling */
+            string unquoted = quoted.replace ("=20", " ");
+            try {
+                var regex = new GLib.Regex ("([=]([0-9A-F][0-9A-F]))");
+                return regex.replace_eval (unquoted, -1, 0, 0, evaluate_hex);
+            }
+            catch (GLib.RegexError error) {
+                GLib.critical (_("Failed to decode string \"%s\": %s"),
+                               unquoted, error.message);
+            }
+            return unquoted;
+        }
+
+        public void parse_body () throws GLib.Error
+            requires (stream != null) {
+
+            parts = new GLib.List<MessagePart> ();
+            string? content_encoding = get_field ("content-transfer-encoding");
+            string? boundary = null;
+            string mime_type = "text/plain";
+            string? fname = null;
+            parse_content_type (get_field ("content-type"), ref charset,
+                                ref boundary, ref mime_type, ref fname);
+
+            uint multipart = mime_type.has_prefix ("multipart/") ? 1 : 0;
+            if (multipart > 0 && boundary != null)
+                boundary = boundary.replace ("\"", " ").strip ();
+            else
+                multipart = 0;
+            if (!(mime_type_is_text (mime_type) || multipart > 0)) {
+                throw new GLib.FileError.FAILED (
+                    _("This message of type \"%s\" can't be displayed.").
+                    printf (mime_type));
+            }
+
+            MessagePart? message_part = null;
+            /* Message body starts here */
+            if (multipart == 0) {
+                message_part = new MessagePart (mime_type);
+                parts.append (message_part);
+            }
+
+            string inner_boundary = "";
+            string line;
+            string previous_line = "";
+            while ((line = stream.read_line (null, null)) != null) {
+                if (multipart > 0) {
+                    if (line.has_prefix ("--")) {
+                        if (line == "--" + inner_boundary) {
+                            message_part = new MessagePart ("text/plain");
+                            parts.append (message_part);
+                            multipart = 2;
+                            continue;
+                        } else if (line == "--" + boundary) {
+                            inner_boundary = "";
+                            message_part = new MessagePart ("text/plain");
+                            parts.append (message_part);
+                            multipart = 2;
+                            continue;
+                        } else if (line == "--" + boundary + "--") {
+                            /* Final boundary, we can skip it */
+                            continue;
+                        }
+                    }
+                    else if (multipart == 2) {
+                        if (line == "") {
+                            multipart++;
+                            continue;
+                        }
+
+                        if (line[0] == '\t' || line[0] == ' ')
+                        line = previous_line + " " + line.chug ();
+                        previous_line = line;
+
+                        string[] parts = line.split (":", 2);
+                        string field = ascii_strdown (parts[0] ?? "");
+                        if (field == "content-type") {
+                            string ctype = charset;
+                            string mtype = "text/plain";
+                            fname = null;
+                            parse_content_type (parts[1].strip (), ref ctype,
+                                ref inner_boundary, ref mtype, ref fname);
+                            message_part.filename = fname;
+                            message_part.mime_type = mtype;
+                            message_part.charset = ctype;
+                            message_part.plain_text = mtype == "text/plain";
+                        }
+                        else if (field == "content-transfer-encoding") {
+                            content_encoding = ascii_strdown (parts[1].strip ());
+                        }
+                        else if (field == "content-disposition" ) {
+                            string content_disposition = parts[1].strip ();
+                            string cset = charset;
+                            string mtype = "text/plain";
+                            fname = null;
+                            parse_content_type (content_disposition, ref cset,
+                                ref inner_boundary, ref mtype, ref fname);
+                            message_part.filename = fname;
+                            if (content_disposition.has_prefix ("attachment"))
+                                message_part.content_disposition = content_disposition;
+                            else if (content_disposition.has_prefix ("inline"))
+                                message_part.content_disposition = content_disposition;
+                        }
+                        else if (field == "content-id") {
+                            string content_id = parts[1].strip ();
+                            if (content_id[0] == '<' && content_id.has_suffix (">"))
+                                message_part.content_id = content_id.slice (1, -1);
+                        }
+                        continue;
+                    }
+                    else if (multipart == 1)
+                        continue;
+                }
+
+                if (content_encoding == "quoted-printable")
+                    line = quoted_printable_decode (line);
+                else if (content_encoding == "base64"
+                      && mime_type_is_text (message_part.mime_type))
+                    line = line != "" ? (string)GLib.Base64.decode (line) : "";
+                try {
+                    string cset = message_part.charset ?? charset;
+                    if (cset != "UTF-8")
+                        line = GLib.convert (line, -1, "UTF-8", cset, null);
+                }
+                catch (GLib.ConvertError error) { }
+                if (message_part.plain_text)
+                    line = line.replace ("<", "<");
+                if (content_encoding == "base64") {
+                    if (message_part.plain_text)
+                        line = line.replace ("\n", "<br>");
+                    message_part.body.append (line);
+                } else {
+                    message_part.body.append (line);
+                    if (content_encoding == "quoted-printable" && line.has_suffix ("="))
+                        message_part.body.truncate (message_part.body.len - 1);
+                    else if (message_part.plain_text)
+                        message_part.body.append ("<br>");
+                    else if (mime_type_is_text (message_part.mime_type))
+                        message_part.body.append ("\n");
+                    else
+                        message_part.body.append (" ");
+                }
+            }
+        }
     }
 }
 
diff --git a/postler/postler-messages.vala b/postler/postler-messages.vala
index 9fd75b2..b25890f 100644
--- a/postler/postler-messages.vala
+++ b/postler/postler-messages.vala
@@ -337,7 +337,7 @@ public class Postler.Messages : Gtk.TreeView {
         string unquoted;
         if (encoding == 'Q') {
             unquoted = pieces[0].replace (" =", " ").replace ("_", " ");
-            unquoted = Postler.Content.quoted_printable_decode (unquoted);
+            unquoted = Postler.Message.quoted_printable_decode (unquoted);
         }
         else if (encoding == 'B') 
             unquoted = (string)GLib.Base64.decode (pieces[0]);



More information about the Xfce4-commits mailing list