[evolution] I#1373 - itip-view: Offer to show other parts from multipart/alternative
- From: Milan Crha <mcrha src gnome org>
- To: commits-list gnome org
- Cc:
- Subject: [evolution] I#1373 - itip-view: Offer to show other parts from multipart/alternative
- Date: Thu, 6 Jan 2022 13:50:35 +0000 (UTC)
commit feb4bcb86acc5e1a78b7a1520d624909ca7b9e3f
Author: Milan Crha <mcrha redhat com>
Date: Thu Jan 6 14:49:57 2022 +0100
I#1373 - itip-view: Offer to show other parts from multipart/alternative
Closes https://gitlab.gnome.org/GNOME/evolution/-/issues/1373
.../org.gnome.evolution.plugin.itip.gschema.xml.in | 5 +
src/modules/calendar/e-calendar-preferences.c | 7 +
src/modules/itip-formatter/e-mail-parser-itip.c | 21 +-
src/modules/itip-formatter/itip-view.c | 336 ++++++++++++++++++---
src/modules/itip-formatter/itip-view.h | 4 +
5 files changed, 312 insertions(+), 61 deletions(-)
---
diff --git a/data/org.gnome.evolution.plugin.itip.gschema.xml.in
b/data/org.gnome.evolution.plugin.itip.gschema.xml.in
index 09b1ba29d9..9c767da406 100644
--- a/data/org.gnome.evolution.plugin.itip.gschema.xml.in
+++ b/data/org.gnome.evolution.plugin.itip.gschema.xml.in
@@ -10,5 +10,10 @@
<_summary>Preserve existing reminder by default</_summary>
<_description>Whether to preset option to preserve existing reminder by default</_description>
</key>
+ <key name="show-message-description" type="b">
+ <default>true</default>
+ <_summary>Show invitation description provided by the sender</_summary>
+ <_description>Set to true to show invitation description as provided by the sender, if such is
available; otherwise generate the invitation description from the iCalendar component</_description>
+ </key>
</schema>
</schemalist>
diff --git a/src/modules/calendar/e-calendar-preferences.c b/src/modules/calendar/e-calendar-preferences.c
index aa278fff81..905c0e2e12 100644
--- a/src/modules/calendar/e-calendar-preferences.c
+++ b/src/modules/calendar/e-calendar-preferences.c
@@ -650,6 +650,13 @@ calendar_preferences_add_itip_formatter_page (EShell *shell,
check, "active",
G_SETTINGS_BIND_DEFAULT);
+ check = gtk_check_button_new_with_mnemonic (_("_Show invitation description provided by the sender"));
+ gtk_box_pack_start (GTK_BOX (inner_vbox), check, FALSE, FALSE, 0);
+
+ g_settings_bind (settings, "show-message-description",
+ check, "active",
+ G_SETTINGS_BIND_DEFAULT);
+
g_object_unref (settings);
/* "Conflict searching" */
diff --git a/src/modules/itip-formatter/e-mail-parser-itip.c b/src/modules/itip-formatter/e-mail-parser-itip.c
index 326d930b63..32b7174905 100644
--- a/src/modules/itip-formatter/e-mail-parser-itip.c
+++ b/src/modules/itip-formatter/e-mail-parser-itip.c
@@ -190,33 +190,16 @@ empe_itip_parse (EMailParserExtension *extension,
GQueue *out_mail_parts)
{
EMailPartItip *itip_part;
- CamelDataWrapper *content;
- CamelStream *stream;
- GByteArray *byte_array;
- gint len;
const CamelContentDisposition *disposition;
GQueue work_queue = G_QUEUE_INIT;
+ gint len;
len = part_id->len;
g_string_append_printf (part_id, ".itip");
itip_part = e_mail_part_itip_new (part, part_id->str);
itip_part->itip_mime_part = g_object_ref (part);
-
- /* This is non-gui thread. Download the part for using in the main thread */
- content = camel_medium_get_content ((CamelMedium *) part);
-
- byte_array = g_byte_array_new ();
- stream = camel_stream_mem_new_with_byte_array (byte_array);
- camel_data_wrapper_decode_to_stream_sync (content, stream, NULL, NULL);
-
- if (byte_array->len == 0)
- itip_part->vcalendar = NULL;
- else
- itip_part->vcalendar = g_strndup (
- (gchar *) byte_array->data, byte_array->len);
-
- g_object_unref (stream);
+ itip_part->vcalendar = itip_view_util_extract_part_content (part, FALSE);
g_queue_push_tail (&work_queue, itip_part);
diff --git a/src/modules/itip-formatter/itip-view.c b/src/modules/itip-formatter/itip-view.c
index e4da17c9c0..8344ac0040 100644
--- a/src/modules/itip-formatter/itip-view.c
+++ b/src/modules/itip-formatter/itip-view.c
@@ -25,6 +25,9 @@
#include <glib/gi18n.h>
#include <libedataserver/libedataserver.h>
+#include <libxml/HTMLparser.h>
+#include <libxml/HTMLtree.h>
+
#include <shell/e-shell.h>
#include <shell/e-shell-utils.h>
@@ -942,7 +945,7 @@ itip_view_get_state_cb (GObject *source_object,
(!g_error_matches (error, WEBKIT_JAVASCRIPT_ERROR,
WEBKIT_JAVASCRIPT_ERROR_SCRIPT_FAILED) ||
/* WebKit can return empty error message, thus ignore those. */
(error->message && *(error->message))))
- g_warning ("Failed to call 'ItipView.GetState()' function: %s:%d: %s",
g_quark_to_string (error->domain), error->code, error->message);
+ g_warning ("Failed to call 'EvoItip.GetState()' function: %s:%d: %s",
g_quark_to_string (error->domain), error->code, error->message);
g_clear_error (&error);
}
@@ -954,7 +957,7 @@ itip_view_get_state_cb (GObject *source_object,
exception = jsc_context_get_exception (jsc_value_get_context (value));
if (exception) {
- g_warning ("Failed to call 'ItipView.GetState()': %s",
jsc_exception_get_message (exception));
+ g_warning ("Failed to call 'EvoItip.GetState()': %s",
jsc_exception_get_message (exception));
jsc_context_clear_exception (jsc_value_get_context (value));
}
@@ -1669,58 +1672,254 @@ itip_view_set_extension_name (ItipView *view,
itip_view_rebuild_source_list (view);
}
+static void
+itip_html_check_characters (gpointer user_data,
+ const xmlChar *str,
+ gint len)
+{
+ gboolean *p_is_empty = user_data;
+ gint ii;
+
+ for (ii = 0; ii < len && *p_is_empty; ii++) {
+ /* a comment starting with "<!--" */
+ if (ii + 3 < len &&
+ str[ii ] == '<' &&
+ str[ii + 1] == '!' &&
+ str[ii + 2] == '-' &&
+ str[ii + 3] == '-') {
+ gint jj;
+
+ for (jj = 4; ii + jj + 2 < len; jj++) {
+ /* move after the comment end "-->" */
+ if (str[ii + jj ] == '-' &&
+ str[ii + jj + 1] == '-' &&
+ str[ii + jj + 2] == '>') {
+ ii += jj + 2;
+ break;
+ }
+ }
+ } else {
+ *p_is_empty = g_ascii_isspace (str[ii]);
+ }
+ }
+}
+
+static void
+itip_html_check_warning (gpointer user_data,
+ const gchar *format,
+ ...)
+{
+ /* ignore any warning */
+}
+
+static void
+itip_html_check_error (gpointer user_data,
+ const gchar *format,
+ ...)
+{
+ /* ignore any error */
+}
+
+static gboolean
+itip_html_is_empty (const gchar *html)
+{
+ htmlParserCtxtPtr ctxt;
+ htmlSAXHandler sax;
+ gboolean is_empty;
+
+ if (!html || !*html)
+ return TRUE;
+
+ memset (&sax, 0, sizeof (htmlSAXHandler));
+
+ is_empty = TRUE;
+ sax.characters = itip_html_check_characters;
+ sax.warning = itip_html_check_warning;
+ sax.error = itip_html_check_error;
+
+ ctxt = htmlCreatePushParserCtxt (&sax, &is_empty, html, strlen (html), "", XML_CHAR_ENCODING_UTF8);
+
+ htmlParseChunk (ctxt, html, 0, 1);
+ htmlFreeParserCtxt (ctxt);
+
+ return is_empty;
+}
+
+typedef struct _FindPartData {
+ CamelMimePart *look_for;
+ CamelMimePart *parent_part;
+} FindPartData;
+
+static gboolean
+itip_view_find_parent_part_cb (CamelMimeMessage *message,
+ CamelMimePart *part,
+ CamelMimePart *parent_part,
+ gpointer user_data)
+{
+ FindPartData *fpd = user_data;
+
+ if (fpd->look_for == part) {
+ if (parent_part)
+ fpd->parent_part = g_object_ref (parent_part);
+
+ return FALSE;
+ }
+
+ return TRUE;
+}
+
+static CamelMimePart *
+itip_view_ref_parent_part (CamelMimeMessage *message,
+ CamelMimePart *part)
+{
+ FindPartData fpd;
+
+ if (!message || !part)
+ return NULL;
+
+ fpd.look_for = part;
+ fpd.parent_part = NULL;
+
+ camel_mime_message_foreach_part (message, itip_view_find_parent_part_cb, &fpd);
+
+ return fpd.parent_part;
+}
+
+static gchar *
+itip_view_dup_alternative_html (EMailPartItip *itip_part)
+{
+ CamelMimePart *parent_part;
+ gchar *html = NULL;
+
+ if (!itip_part->message)
+ return NULL;
+
+ parent_part = itip_view_ref_parent_part (itip_part->message, itip_part->itip_mime_part);
+
+ if (parent_part) {
+ CamelContentType *ct;
+ CamelDataWrapper *containee;
+
+ ct = camel_mime_part_get_content_type (parent_part);
+ containee = camel_medium_get_content (CAMEL_MEDIUM (parent_part));
+
+ if (camel_content_type_is (ct, "multipart", "alternative") && CAMEL_IS_MULTIPART (containee))
{
+ CamelMultipart *multipart = CAMEL_MULTIPART (containee);
+ CamelMimePart *text_part = NULL, *html_part = NULL;
+ guint ii, sz;
+
+ sz = camel_multipart_get_number (multipart);
+
+ for (ii = 0; ii < sz && (!text_part || !html_part); ii++) {
+ CamelMimePart *part = camel_multipart_get_part (multipart, ii);
+
+ if (part == itip_part->itip_mime_part)
+ continue;
+
+ ct = camel_mime_part_get_content_type (part);
+
+ if (camel_content_type_is (ct, "text", "plain"))
+ text_part = part;
+ else if (camel_content_type_is (ct, "text", "html"))
+ html_part = part;
+ }
+
+ if (html_part) {
+ html = itip_view_util_extract_part_content (html_part, FALSE);
+ } else if (text_part) {
+ gchar *content;
+ const gchar *format;
+ guint32 flags =
+ CAMEL_MIME_FILTER_TOHTML_CONVERT_NL |
+ CAMEL_MIME_FILTER_TOHTML_CONVERT_URLS |
+ CAMEL_MIME_FILTER_TOHTML_CONVERT_ADDRESSES;
+
+ if ((format = camel_content_type_param (camel_mime_part_get_content_type
(text_part), "format")) &&
+ !g_ascii_strcasecmp (format, "flowed"))
+ flags |= CAMEL_MIME_FILTER_TOHTML_FORMAT_FLOWED;
+
+ content = itip_view_util_extract_part_content (text_part, TRUE);
+ html = camel_text_to_html (content, flags, 0);
+
+ g_free (content);
+ }
+ }
+ }
+
+ g_clear_object (&parent_part);
+
+ if (html && itip_html_is_empty (html))
+ g_clear_pointer (&html, g_free);
+
+ return html;
+}
+
void
itip_view_write (gpointer itip_part_ptr,
EMailFormatter *formatter,
GString *buffer)
{
gint icon_width, icon_height;
- gchar *header = e_mail_formatter_get_html_header (formatter);
+ GSettings *settings;
+ gchar *header;
+ gchar *alternative_html;
+ header = e_mail_formatter_get_html_header (formatter);
g_string_append (buffer, header);
g_free (header);
- if (!gtk_icon_size_lookup (GTK_ICON_SIZE_BUTTON, &icon_width, &icon_height)) {
- icon_width = 16;
- icon_height = 16;
- }
+ settings = e_util_ref_settings ("org.gnome.evolution.plugin.itip");
- g_string_append_printf (
- buffer,
- "<img src=\"gtk-stock://%s?size=%d\" class=\"itip icon\" width=\"%dpx\" height=\"%dpx\"/>\n",
- MEETING_ICON, GTK_ICON_SIZE_BUTTON, icon_width, icon_height);
+ if (g_settings_get_boolean (settings, "show-message-description"))
+ alternative_html = itip_view_dup_alternative_html (itip_part_ptr);
+ else
+ alternative_html = NULL;
- g_string_append (
- buffer,
- "<div class=\"itip content\" id=\"" DIV_ITIP_CONTENT "\">\n");
+ g_clear_object (&settings);
- /* The first section listing the sender */
- /* FIXME What to do if the send and organizer do not match */
- g_string_append (
- buffer,
- "<div id=\"" TEXT_ROW_SENDER "\" class=\"itip sender\"></div>\n");
+ if (!alternative_html) {
+ if (!gtk_icon_size_lookup (GTK_ICON_SIZE_BUTTON, &icon_width, &icon_height)) {
+ icon_width = 16;
+ icon_height = 16;
+ }
- g_string_append (buffer, "<hr>\n");
+ g_string_append_printf (
+ buffer,
+ "<img src=\"gtk-stock://%s?size=%d\" class=\"itip icon\" width=\"%dpx\"
height=\"%dpx\"/>\n",
+ MEETING_ICON, GTK_ICON_SIZE_BUTTON, icon_width, icon_height);
- /* Elementary event information */
- g_string_append (
- buffer,
- "<table class=\"itip table\" border=\"0\" "
- "cellspacing=\"5\" cellpadding=\"0\">\n");
+ g_string_append (
+ buffer,
+ "<div class=\"itip content\" id=\"" DIV_ITIP_CONTENT "\">\n");
+
+ /* The first section listing the sender */
+ /* FIXME What to do if the send and organizer do not match */
+ g_string_append (
+ buffer,
+ "<div id=\"" TEXT_ROW_SENDER "\" class=\"itip sender\"></div>\n");
- append_text_table_row (buffer, TABLE_ROW_SUMMARY, NULL, NULL);
- append_text_table_row (buffer, TABLE_ROW_LOCATION, _("Location:"), NULL);
- append_text_table_row (buffer, TABLE_ROW_URL, _("URL:"), NULL);
- append_text_table_row (buffer, TABLE_ROW_START_DATE, _("Start time:"), NULL);
- append_text_table_row (buffer, TABLE_ROW_END_DATE, _("End time:"), NULL);
- append_text_table_row (buffer, TABLE_ROW_DUE_DATE, _("Due date:"), NULL);
- append_text_table_row (buffer, TABLE_ROW_ESTIMATED_DURATION, _("Estimated duration:"), NULL);
- append_text_table_row (buffer, TABLE_ROW_STATUS, _("Status:"), NULL);
- append_text_table_row (buffer, TABLE_ROW_COMMENT, _("Comment:"), NULL);
- append_text_table_row (buffer, TABLE_ROW_CATEGORIES, _("Categories:"), NULL);
- append_text_table_row (buffer, TABLE_ROW_ATTENDEES, _("Attendees:"), NULL);
+ g_string_append (buffer, "<hr>\n");
- g_string_append (buffer, "</table>\n");
+ /* Elementary event information */
+ g_string_append (
+ buffer,
+ "<table class=\"itip table\" border=\"0\" "
+ "cellspacing=\"5\" cellpadding=\"0\">\n");
+
+ append_text_table_row (buffer, TABLE_ROW_SUMMARY, NULL, NULL);
+ append_text_table_row (buffer, TABLE_ROW_LOCATION, _("Location:"), NULL);
+ append_text_table_row (buffer, TABLE_ROW_URL, _("URL:"), NULL);
+ append_text_table_row (buffer, TABLE_ROW_START_DATE, _("Start time:"), NULL);
+ append_text_table_row (buffer, TABLE_ROW_END_DATE, _("End time:"), NULL);
+ append_text_table_row (buffer, TABLE_ROW_DUE_DATE, _("Due date:"), NULL);
+ append_text_table_row (buffer, TABLE_ROW_ESTIMATED_DURATION, _("Estimated duration:"), NULL);
+ append_text_table_row (buffer, TABLE_ROW_STATUS, _("Status:"), NULL);
+ append_text_table_row (buffer, TABLE_ROW_COMMENT, _("Comment:"), NULL);
+ append_text_table_row (buffer, TABLE_ROW_CATEGORIES, _("Categories:"), NULL);
+ append_text_table_row (buffer, TABLE_ROW_ATTENDEES, _("Attendees:"), NULL);
+
+ g_string_append (buffer, "</table>\n");
+ }
/* Upper Info items */
g_string_append (
@@ -1728,10 +1927,14 @@ itip_view_write (gpointer itip_part_ptr,
"<table class=\"itip info\" id=\"" TABLE_UPPER_ITIP_INFO "\" border=\"0\" "
"cellspacing=\"5\" cellpadding=\"0\">");
- /* Description */
- g_string_append (
- buffer,
- "<div id=\"" TABLE_ROW_DESCRIPTION "\" class=\"itip description\" hidden=\"\"></div>\n");
+ if (alternative_html) {
+ g_string_append (buffer, alternative_html);
+ } else {
+ /* Description */
+ g_string_append (
+ buffer,
+ "<div id=\"" TABLE_ROW_DESCRIPTION "\" class=\"itip description\"
hidden=\"\"></div>\n");
+ }
g_string_append (buffer, "<hr>\n");
@@ -1782,12 +1985,14 @@ itip_view_write (gpointer itip_part_ptr,
/* Buttons table */
append_buttons_table (buffer, itip_part_ptr);
- /* <div class="itip content" > */
+ /* <div class="itip content" > */
g_string_append (buffer, "</div>\n");
g_string_append (buffer, "<div class=\"itip error\" id=\"" DIV_ITIP_ERROR "\"></div>");
g_string_append (buffer, "</body></html>");
+
+ g_free (alternative_html);
}
void
@@ -6957,3 +7162,50 @@ itip_view_ref_web_view (ItipView *view)
return g_weak_ref_get (view->priv->web_view_weakref);
}
+
+gchar *
+itip_view_util_extract_part_content (CamelMimePart *part,
+ gboolean convert_charset)
+{
+ CamelDataWrapper *dw;
+ CamelStream *stream;
+ GByteArray *byte_array;
+ gchar *content = NULL;
+
+ g_return_val_if_fail (CAMEL_IS_MIME_PART (part), NULL);
+
+ dw = camel_medium_get_content (CAMEL_MEDIUM (part));
+
+ byte_array = g_byte_array_new ();
+ stream = camel_stream_mem_new_with_byte_array (byte_array);
+
+ if (convert_charset) {
+ CamelContentType *ct;
+ const gchar *charset;
+
+ ct = camel_mime_part_get_content_type (part);
+ charset = camel_content_type_param (ct, "charset");
+
+ if (charset && *charset && g_ascii_strcasecmp (charset, "UTF-8") != 0) {
+ CamelStream *filter_stream;
+ CamelMimeFilter *filter;
+
+ filter_stream = camel_stream_filter_new (stream);
+ g_object_unref (stream);
+ stream = filter_stream;
+
+ filter = camel_mime_filter_charset_new (charset, "UTF-8");
+ camel_stream_filter_add (CAMEL_STREAM_FILTER (stream), filter);
+ g_object_unref (filter);
+ }
+ }
+
+ camel_data_wrapper_decode_to_stream_sync (dw, stream, NULL, NULL);
+
+ if (byte_array->len != 0)
+ content = g_strndup ((gchar *) byte_array->data, byte_array->len);
+
+ g_object_unref (stream);
+
+ return content;
+}
diff --git a/src/modules/itip-formatter/itip-view.h b/src/modules/itip-formatter/itip-view.h
index 31e795cc88..cb6ed65d91 100644
--- a/src/modules/itip-formatter/itip-view.h
+++ b/src/modules/itip-formatter/itip-view.h
@@ -254,6 +254,10 @@ void itip_view_set_error (ItipView *view,
const gchar *error_html,
gboolean show_save_btn);
+gchar * itip_view_util_extract_part_content
+ (CamelMimePart *part,
+ gboolean convert_charset);
+
G_END_DECLS
#endif /* ITIP_VIEW_H */
[
Date Prev][
Date Next] [
Thread Prev][
Thread Next]
[
Thread Index]
[
Date Index]
[
Author Index]