From cdc2fa1bd8bb14c58a7702c516b113118638ab59 Mon Sep 17 00:00:00 2001 From: Stig Brautaset Date: Sun, 18 Dec 2016 16:16:02 +0000 Subject: [PATCH] Respect format=flowed and delsp=yes for viewing plain-text messages --- lib/mu-msg-sexp.c | 27 ++++++++++++++++ lib/mu-msg.c | 70 +++++++++++++++++++++++++++++++++++++++++ lib/mu-msg.h | 13 ++++++++ lib/tests/test-mu-msg.c | 9 ++++++ mu4e/mu4e-message.el | 19 ++++++++++- 5 files changed, 137 insertions(+), 1 deletion(-) diff --git a/lib/mu-msg-sexp.c b/lib/mu-msg-sexp.c index a3740cdd..f403bc86 100644 --- a/lib/mu-msg-sexp.c +++ b/lib/mu-msg-sexp.c @@ -422,11 +422,31 @@ append_sexp_thread_info (GString *gstr, const MuMsgIterThreadInfo *ti) " :has-child t" : ""); } +static void +append_sexp_param (GString *gstr, GSList *param) +{ + for (;param; param = g_slist_next (param)) { + char *key, *value; + + key = param->data; + key = key ? mu_str_escape_c_literal (key, FALSE) : ""; + + param = g_slist_next (param); + value = param->data; + value = value ? mu_str_escape_c_literal (value, FALSE) : ""; + + g_string_append_printf (gstr, "(\"%s\" . \"%s\")", key, value); + if (param->next) + g_string_append_c (gstr, ' '); + } +} + static void append_message_file_parts (GString *gstr, MuMsg *msg, MuMsgOptions opts) { const char *str; GError *err; + const GSList *params; err = NULL; @@ -445,6 +465,13 @@ append_message_file_parts (GString *gstr, MuMsg *msg, MuMsgOptions opts) if (str || (str = mu_msg_get_header (msg, "X-Mailer"))) append_sexp_attr (gstr, "user-agent", str); + params = mu_msg_get_body_text_content_type_parameters (msg, opts); + if (params) { + g_string_append_printf (gstr, "\t:body-txt-params ("); + append_sexp_param (gstr, params); + g_string_append_printf (gstr, ")\n"); + } + append_sexp_body_attr (gstr, "body-txt", mu_msg_get_body_text(msg, opts)); append_sexp_body_attr (gstr, "body-html", diff --git a/lib/mu-msg.c b/lib/mu-msg.c index 44e99fa1..249c2019 100644 --- a/lib/mu-msg.c +++ b/lib/mu-msg.c @@ -520,6 +520,76 @@ get_body (MuMsg *self, MuMsgOptions opts, gboolean want_html) return g_string_free (bdata.gstr, FALSE); } + +struct _ContentTypeData { + const GMimeContentType *ctype; + gboolean want_html; +}; +typedef struct _ContentTypeData ContentTypeData; + + +static void +find_content_type (MuMsg *msg, MuMsgPart *mpart, ContentTypeData *cdata) +{ + if (!GMIME_IS_PART(mpart->data)) + return; + + /* text-like attachments are included when in text-mode */ + + GMimePart *wanted = NULL; + + if (!cdata->want_html && + (mpart->part_type & MU_MSG_PART_TYPE_TEXT_PLAIN)) + wanted = mpart->data; + else if (!(mpart->part_type & MU_MSG_PART_TYPE_ATTACHMENT) && + cdata->want_html && + (mpart->part_type & MU_MSG_PART_TYPE_TEXT_HTML)) + wanted = mpart->data; + if (wanted) + cdata->ctype = g_mime_object_get_content_type (wanted); +} + + +static const GSList* +get_content_type_parameters (MuMsg *self, MuMsgOptions opts, gboolean want_html) +{ + ContentTypeData cdata; + cdata.want_html = want_html; + cdata.ctype = NULL; + + mu_msg_part_foreach (self, opts, + (MuMsgPartForeachFunc)find_content_type, + &cdata); + + if (cdata.ctype) { + const GSList *paramlist; + const GMimeParam *param; + + paramlist = NULL; + param = g_mime_content_type_get_params (cdata.ctype); + + for (; param; param = param->next) { + paramlist = g_slist_prepend (paramlist, + g_strdup (param->name)); + + paramlist = g_slist_prepend (paramlist, + g_strdup (param->value)); + } + + return free_later_lst(self, g_slist_reverse (paramlist)); + } + return NULL; +} + + +const GSList* +mu_msg_get_body_text_content_type_parameters (MuMsg *self, MuMsgOptions opts) +{ + g_return_val_if_fail (self, NULL); + return get_content_type_parameters(self, opts, FALSE); +} + + const char* mu_msg_get_body_html (MuMsg *self, MuMsgOptions opts) { diff --git a/lib/mu-msg.h b/lib/mu-msg.h index 0358cff8..c43a9033 100644 --- a/lib/mu-msg.h +++ b/lib/mu-msg.h @@ -166,6 +166,19 @@ void mu_msg_cache_values (MuMsg *self); const char* mu_msg_get_body_text (MuMsg *msg, MuMsgOptions opts); +/** + * get the content type parameters for the text body part + * + * @param msg a valid MuMsg* instance + * @param opts options for getting the body + * + * @return the value of the requested body part content type parameter, or + * NULL in case of error or if there is no such body. the returned string + * should *not* be modified or freed. The returned data is in UTF8 or NULL. + */ +const GSList* mu_msg_get_body_text_content_type_parameters (MuMsg *self, MuMsgOptions opts); + + /** * get the html body of this message * diff --git a/lib/tests/test-mu-msg.c b/lib/tests/test-mu-msg.c index cca2ba45..37ea8af2 100644 --- a/lib/tests/test-mu-msg.c +++ b/lib/tests/test-mu-msg.c @@ -184,6 +184,7 @@ static void test_mu_msg_03 (void) { MuMsg *msg; + GSList *params; msg = get_msg (MU_TESTMAILDIR4 "/1283599333.1840_11.cthulhu!2,"); g_assert_cmpstr (mu_msg_get_to(msg), @@ -199,6 +200,14 @@ test_mu_msg_03 (void) g_assert_cmpstr (mu_msg_get_body_text(msg, MU_MSG_OPTION_NONE), ==, "\nLet's write some fünkÿ text\nusing umlauts.\n\nFoo.\n"); + + params = mu_msg_get_body_text_content_type_parameters(msg, MU_MSG_OPTION_NONE); + g_assert_cmpuint (g_slist_length ((GSList*)params), ==, 2); + + g_assert_cmpstr ((char*)params->data,==, "charset"); + params = g_slist_next(params); + g_assert_cmpstr ((char*)params->data,==,"UTF-8"); + g_assert_cmpuint (mu_msg_get_flags(msg), ==, MU_FLAG_UNREAD); /* not seen => unread */ diff --git a/mu4e/mu4e-message.el b/mu4e/mu4e-message.el index d0c12422..87e0308d 100644 --- a/mu4e/mu4e-message.el +++ b/mu4e/mu4e-message.el @@ -32,6 +32,7 @@ (require 'cl) (require 'html2text) +(require 'flow-fill) (defcustom mu4e-html2text-command @@ -189,6 +190,12 @@ representation." ;; otherwise, use text (t nil)))) +(defun mu4e~message-body-has-content-type-param (msg param) + (cdr + (assoc param (mu4e-message-field msg :body-txt-params)))) + +(defun mu4e~safe-iequal (a b) + (and b (equal (downcase b) a))) (defun mu4e-message-body-text (msg &optional prefer-html) "Get the body in text form for this message. @@ -210,7 +217,17 @@ unless PREFER-HTML is non-nil." (mu4e~html2text-wrapper mu4e-html2text-command msg))) (t (mu4e-error "Invalid `mu4e-html2text-command'"))) ;; use a text body - (or (mu4e-message-field msg :body-txt) "")))) + (or (with-temp-buffer + (insert (mu4e-message-field msg :body-txt)) + (if (mu4e~safe-iequal "flowed" + (mu4e~message-body-has-content-type-param + msg "format")) + (fill-flowed nil (mu4e~safe-iequal + "yes" + (mu4e~message-body-has-content-type-param + msg "delsp")))) + (buffer-string)) + "")))) (dolist (func mu4e-message-body-rewrite-functions) (setq body (funcall func msg body))) body))