From 96aaae21b7c5f31623ace38e755a88a4fdf9bff0 Mon Sep 17 00:00:00 2001 From: Packit Date: Sep 25 2020 09:30:56 +0000 Subject: Apply patch evolution-3.28.5-cve-2018-15587-reposition-signature-bar.patch patch_name: evolution-3.28.5-cve-2018-15587-reposition-signature-bar.patch present_in_specfile: true --- diff --git a/src/em-format/e-mail-formatter-utils.c b/src/em-format/e-mail-formatter-utils.c index abd5155..50dae71 100644 --- a/src/em-format/e-mail-formatter-utils.c +++ b/src/em-format/e-mail-formatter-utils.c @@ -549,71 +549,136 @@ e_mail_formatter_format_security_header (EMailFormatter *formatter, EMailPart *part, guint32 flags) { - const gchar* part_id; - gchar* part_id_prefix; - GString* tmp; + struct _validity_flags { + guint32 flags; + const gchar *description_complete; + const gchar *description_partial; + } validity_flags[] = { + { E_MAIL_PART_VALIDITY_PGP | E_MAIL_PART_VALIDITY_SIGNED, N_("GPG signed"), N_("partially GPG signed") }, + { E_MAIL_PART_VALIDITY_PGP | E_MAIL_PART_VALIDITY_ENCRYPTED, N_("GPG encrypted"), N_("partially GPG encrypted") }, + { E_MAIL_PART_VALIDITY_SMIME | E_MAIL_PART_VALIDITY_SIGNED, N_("S/MIME signed"), N_("partially S/MIME signed") }, + { E_MAIL_PART_VALIDITY_SMIME | E_MAIL_PART_VALIDITY_ENCRYPTED, N_("S/MIME encrypted"), N_("partially S/MIME encrypted") } + }; + const gchar *part_id; + gchar *part_id_prefix; GQueue queue = G_QUEUE_INIT; GList *head, *link; + guint32 check_valid_flags = 0; + gint part_id_prefix_len; + gboolean is_partial = FALSE; + guint ii; g_return_if_fail (E_IS_MAIL_PART_HEADERS (part)); /* Get prefix of this PURI */ part_id = e_mail_part_get_id (part); part_id_prefix = g_strndup (part_id, g_strrstr (part_id, ".") - part_id); - - /* Add encryption/signature header */ - tmp = g_string_new (""); + part_id_prefix_len = strlen (part_id_prefix); e_mail_part_list_queue_parts (context->part_list, NULL, &queue); head = g_queue_peek_head_link (&queue); - /* Find first secured part. */ - for (link = head; link != NULL; link = g_list_next(link)) { + /* Ignore the main message, the headers and the end parts */ + #define should_skip_part(_id) \ + (g_strcmp0 (_id, part_id_prefix) == 0 || \ + (_id && g_str_has_suffix (_id, ".rfc822.end")) || \ + (_id && strlen (_id) == part_id_prefix_len + 8 /* strlen (".headers") */ && \ + g_strcmp0 (_id + part_id_prefix_len, ".headers") == 0)) + + /* Check parts for this ID. */ + for (link = head; link != NULL; link = g_list_next (link)) { EMailPart *mail_part = link->data; + const gchar *id = e_mail_part_get_id (mail_part); - if (!e_mail_part_has_validity (mail_part)) + if (!e_mail_part_id_has_prefix (mail_part, part_id_prefix)) continue; - if (!e_mail_part_id_has_prefix (mail_part, part_id_prefix)) + if (should_skip_part (id)) continue; - if (e_mail_part_get_validity (mail_part, E_MAIL_PART_VALIDITY_PGP | E_MAIL_PART_VALIDITY_SIGNED)) { - g_string_append (tmp, _("GPG signed")); + if (!e_mail_part_has_validity (mail_part)) { + /* A part without validity, thus it's partially signed/encrypted */ + is_partial = TRUE; + } else { + guint32 validies = 0; + for (ii = 0; ii < G_N_ELEMENTS (validity_flags); ii++) { + if (e_mail_part_get_validity (mail_part, validity_flags[ii].flags)) + validies |= validity_flags[ii].flags; + } + check_valid_flags |= validies; } - if (e_mail_part_get_validity (mail_part, E_MAIL_PART_VALIDITY_PGP | E_MAIL_PART_VALIDITY_ENCRYPTED)) { - if (tmp->len > 0) - g_string_append (tmp, ", "); - g_string_append (tmp, _("GPG encrypted")); - } + /* Do not traverse sub-messages */ + if (g_str_has_suffix (e_mail_part_get_id (mail_part), ".rfc822") && + !g_str_equal (e_mail_part_get_id (mail_part), part_id_prefix)) + link = e_mail_formatter_find_rfc822_end_iter (link); + } + + if (check_valid_flags) { + GString *tmp; + + if (!is_partial) { + for (link = head; link != NULL && !is_partial; link = g_list_next (link)) { + EMailPart *mail_part = link->data; + const gchar *id = e_mail_part_get_id (mail_part); - if (e_mail_part_get_validity (mail_part, E_MAIL_PART_VALIDITY_SMIME | E_MAIL_PART_VALIDITY_SIGNED)) { - if (tmp->len > 0) - g_string_append (tmp, ", "); - g_string_append (tmp, _("S/MIME signed")); + if (!e_mail_part_id_has_prefix (mail_part, part_id_prefix)) + continue; + + if (should_skip_part (id)) + continue; + + if (!e_mail_part_has_validity (mail_part)) { + /* A part without validity, thus it's partially signed/encrypted */ + is_partial = TRUE; + break; + } + + is_partial = !e_mail_part_get_validity (mail_part, check_valid_flags); + + /* Do not traverse sub-messages */ + if (g_str_has_suffix (e_mail_part_get_id (mail_part), ".rfc822") && + !g_str_equal (e_mail_part_get_id (mail_part), part_id_prefix)) + link = e_mail_formatter_find_rfc822_end_iter (link); + } } - if (e_mail_part_get_validity (mail_part, E_MAIL_PART_VALIDITY_SMIME | E_MAIL_PART_VALIDITY_ENCRYPTED)) { - if (tmp->len > 0) - g_string_append (tmp, ", "); - g_string_append (tmp, _("S/MIME encrypted")); + /* Add encryption/signature header */ + tmp = g_string_new (""); + + for (link = head; link; link = g_list_next (link)) { + EMailPart *mail_part = link->data; + const gchar *id = e_mail_part_get_id (mail_part); + + if (!e_mail_part_has_validity (mail_part) || + !e_mail_part_id_has_prefix (mail_part, part_id_prefix)) + continue; + + if (should_skip_part (id)) + continue; + + for (ii = 0; ii < G_N_ELEMENTS (validity_flags); ii++) { + if (e_mail_part_get_validity (mail_part, validity_flags[ii].flags)) { + if (tmp->len > 0) + g_string_append (tmp, ", "); + g_string_append (tmp, is_partial ? _(validity_flags[ii].description_partial) : _(validity_flags[ii].description_complete)); + } + } + + break; } - break; - } + if (tmp->len > 0) + e_mail_formatter_format_header (formatter, buffer, _("Security"), tmp->str, flags, "UTF-8"); - if (tmp->len > 0) { - e_mail_formatter_format_header ( - formatter, buffer, - _("Security"), tmp->str, - flags, - "UTF-8"); + g_string_free (tmp, TRUE); } + #undef should_skip_part + while (!g_queue_is_empty (&queue)) g_object_unref (g_queue_pop_head (&queue)); - g_string_free (tmp, TRUE); g_free (part_id_prefix); } diff --git a/src/em-format/e-mail-formatter-utils.c.cve-2018-15587-reposition-signature-bar b/src/em-format/e-mail-formatter-utils.c.cve-2018-15587-reposition-signature-bar new file mode 100644 index 0000000..abd5155 --- /dev/null +++ b/src/em-format/e-mail-formatter-utils.c.cve-2018-15587-reposition-signature-bar @@ -0,0 +1,619 @@ +/* + * e-mail-formatter-utils.h + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY + * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with this program; if not, see . + * + */ + +#include "evolution-config.h" + +#include "e-mail-formatter-utils.h" +#include "e-mail-part-headers.h" + +#include +#include + +#include +#include + +#include +#include + +static const gchar *addrspec_hdrs[] = { + "Sender", "From", "Reply-To", "To", "Cc", "Bcc", + "Resent-Sender", "Resent-From", "Resent-Reply-To", + "Resent-To", "Resent-Cc", "Resent-Bcc", NULL +}; + +void +e_mail_formatter_format_text_header (EMailFormatter *formatter, + GString *buffer, + const gchar *label, + const gchar *value, + guint32 flags) +{ + GtkTextDirection direction; + const gchar *fmt, *html; + const gchar *display; + gchar *mhtml = NULL; + + g_return_if_fail (E_IS_MAIL_FORMATTER (formatter)); + g_return_if_fail (buffer != NULL); + g_return_if_fail (label != NULL); + + if (value == NULL) + return; + + while (*value == ' ') + value++; + + if (!(flags & E_MAIL_FORMATTER_HEADER_FLAG_HTML)) { + CamelMimeFilterToHTMLFlags text_format_flags; + + text_format_flags = + e_mail_formatter_get_text_format_flags (formatter); + html = mhtml = camel_text_to_html ( + value, text_format_flags, 0); + } else { + html = value; + } + + direction = gtk_widget_get_default_direction (); + + if (flags & E_MAIL_FORMATTER_HEADER_FLAG_NOCOLUMNS) { + if (flags & E_MAIL_FORMATTER_HEADER_FLAG_BOLD) { + fmt = "" + "%s: %s"; + } else { + fmt = "" + "%s: %s"; + } + } else if (flags & E_MAIL_FORMATTER_HEADER_FLAG_NODEC) { + if (direction == GTK_TEXT_DIR_RTL) + fmt = "" + "%s" + "%s" + ""; + else + fmt = "" + "%s" + "%s" + ""; + } else { + if (direction == GTK_TEXT_DIR_RTL) + fmt = "" + "%s:" + "%s" + ""; + else + fmt = "" + "%s:" + "%s" + ""; + } + + if (flags & E_MAIL_FORMATTER_HEADER_FLAG_HIDDEN) + display = "none"; + else + display = "table-row"; + + g_string_append_printf (buffer, fmt, display, label, html); + + g_free (mhtml); +} + +gchar * +e_mail_formatter_format_address (EMailFormatter *formatter, + GString *out, + struct _camel_header_address *a, + const gchar *field, + gboolean no_links, + gboolean elipsize) +{ + CamelMimeFilterToHTMLFlags flags; + gchar *name, *mailto, *addr; + gint i = 0; + gchar *str = NULL; + gint limit = mail_config_get_address_count (); + + g_return_val_if_fail (E_IS_MAIL_FORMATTER (formatter), NULL); + g_return_val_if_fail (out != NULL, NULL); + g_return_val_if_fail (field != NULL, NULL); + + flags = CAMEL_MIME_FILTER_TOHTML_CONVERT_SPACES; + + while (a != NULL) { + if (a->name) + name = camel_text_to_html (a->name, flags, 0); + else + name = NULL; + + switch (a->type) { + case CAMEL_HEADER_ADDRESS_NAME: + if (name != NULL && *name != '\0') { + gchar *real, *mailaddr; + + if (strchr (a->name, ',') || strchr (a->name, ';')) + g_string_append_printf (out, ""%s"", name); + else + g_string_append (out, name); + + g_string_append (out, " <"); + + /* rfc2368 for mailto syntax and url encoding extras */ + if ((real = camel_header_encode_phrase ((guchar *) a->name))) { + mailaddr = g_strdup_printf ("%s <%s>", real, a->v.addr); + g_free (real); + mailto = camel_url_encode (mailaddr, "?=&()"); + g_free (mailaddr); + } else { + mailto = camel_url_encode (a->v.addr, "?=&()"); + } + } else { + mailto = camel_url_encode (a->v.addr, "?=&()"); + } + addr = camel_text_to_html (a->v.addr, flags, 0); + if (no_links) + g_string_append_printf (out, "%s", addr); + else + g_string_append_printf (out, "%s", mailto, addr); + g_free (mailto); + g_free (addr); + + if (name != NULL && *name != '\0') + g_string_append (out, ">"); + break; + case CAMEL_HEADER_ADDRESS_GROUP: + g_string_append_printf (out, "%s: ", name); + e_mail_formatter_format_address ( + formatter, out, a->v.members, field, + no_links, elipsize); + g_string_append_printf (out, ";"); + break; + default: + g_warning ("Invalid address type"); + break; + } + + g_free (name); + + i++; + a = a->next; + if (a != NULL) + g_string_append (out, ", "); + + if (!elipsize) + continue; + + /* Let us add a '...' if we have more addresses */ + if (limit > 0 && i == limit && a != NULL) { + if (strcmp (field, _("To")) == 0 || + strcmp (field, _("Cc")) == 0 || + strcmp (field, _("Bcc")) == 0) { + + g_string_append ( + out, + ""); + str = g_strdup_printf ( + "", + EVOLUTION_IMAGESDIR); + } + } + } + + if (elipsize && str) { + if (strcmp (field, _("To")) == 0 || + strcmp (field, _("Cc")) == 0 || + strcmp (field, _("Bcc")) == 0) { + + g_string_append ( + out, + "" + "..."); + } + } + + return str; +} + +void +e_mail_formatter_canon_header_name (gchar *name) +{ + gchar *inptr = name; + + g_return_if_fail (name != NULL); + + /* canonicalise the header name... first letter is + * capitalised and any letter following a '-' also gets + * capitalised */ + + if (*inptr >= 'a' && *inptr <= 'z') + *inptr -= 0x20; + + inptr++; + + while (*inptr) { + if (inptr[-1] == '-' && *inptr >= 'a' && *inptr <= 'z') + *inptr -= 0x20; + else if (inptr[-1] != '-' && *inptr >= 'A' && *inptr <= 'Z') + *inptr += 0x20; + + inptr++; + } +} + +void +e_mail_formatter_format_header (EMailFormatter *formatter, + GString *buffer, + const gchar *header_name, + const gchar *header_value, + guint32 flags, + const gchar *charset) +{ + gchar *canon_name, *buf, *value = NULL; + const gchar *label, *txt; + gboolean addrspec = FALSE; + gchar *str_field = NULL; + gint i; + + g_return_if_fail (E_IS_MAIL_FORMATTER (formatter)); + g_return_if_fail (buffer != NULL); + g_return_if_fail (header_name != NULL); + g_return_if_fail (header_value != NULL); + + canon_name = g_alloca (strlen (header_name) + 1); + strcpy (canon_name, header_name); + e_mail_formatter_canon_header_name (canon_name); + + for (i = 0; addrspec_hdrs[i]; i++) { + if (g_ascii_strcasecmp (canon_name, addrspec_hdrs[i]) == 0) { + addrspec = TRUE; + break; + } + } + + label = _(canon_name); + + if (addrspec) { + struct _camel_header_address *addrs; + GString *html; + gchar *img; + gchar *fmt_charset; + + fmt_charset = e_mail_formatter_dup_charset (formatter); + if (fmt_charset == NULL) + fmt_charset = e_mail_formatter_dup_default_charset (formatter); + + buf = camel_header_unfold (header_value); + addrs = camel_header_address_decode (buf, fmt_charset); + if (addrs == NULL) { + g_free (fmt_charset); + g_free (buf); + return; + } + + g_free (fmt_charset); + g_free (buf); + + html = g_string_new (""); + img = e_mail_formatter_format_address ( + formatter, html, addrs, label, + (flags & E_MAIL_FORMATTER_HEADER_FLAG_NOLINKS), + !(flags & E_MAIL_FORMATTER_HEADER_FLAG_NOELIPSIZE)); + + if (img != NULL) { + str_field = g_strdup_printf ("%s: %s", label, img); + label = str_field; + flags |= E_MAIL_FORMATTER_HEADER_FLAG_NODEC; + g_free (img); + } + + camel_header_address_list_clear (&addrs); + txt = value = html->str; + g_string_free (html, FALSE); + + flags |= E_MAIL_FORMATTER_HEADER_FLAG_HTML; + flags |= E_MAIL_FORMATTER_HEADER_FLAG_BOLD; + + } else if (g_str_equal (canon_name, "Subject")) { + buf = camel_header_unfold (header_value); + txt = value = camel_header_decode_string (buf, charset); + g_free (buf); + + flags |= E_MAIL_FORMATTER_HEADER_FLAG_BOLD; + + } else if (g_str_equal (canon_name, "X-Evolution-Mailer")) { + /* pseudo-header */ + label = _("Mailer"); + txt = value = camel_header_format_ctext (header_value, charset); + flags |= E_MAIL_FORMATTER_HEADER_FLAG_BOLD; + + } else if (g_str_equal (canon_name, "Date") || + g_str_equal (canon_name, "Resent-Date")) { + CamelMimeFilterToHTMLFlags text_format_flags; + gint msg_offset, local_tz; + time_t msg_date; + struct tm local; + gchar *html; + gboolean hide_real_date; + + hide_real_date = !e_mail_formatter_get_show_real_date (formatter); + + txt = header_value; + while (*txt == ' ' || *txt == '\t') + txt++; + + text_format_flags = + e_mail_formatter_get_text_format_flags (formatter); + + html = camel_text_to_html (txt, text_format_flags, 0); + + msg_date = camel_header_decode_date (txt, &msg_offset); + e_localtime_with_offset (msg_date, &local, &local_tz); + + /* Convert message offset to minutes (e.g. -0400 --> -240) */ + msg_offset = ((msg_offset / 100) * 60) + (msg_offset % 100); + /* Turn into offset from localtime, not UTC */ + msg_offset -= local_tz / 60; + + /* value will be freed at the end */ + if (!hide_real_date && !msg_offset) { + /* No timezone difference; just + * show the real Date: header. */ + txt = value = html; + } else { + gchar *date_str; + + date_str = e_datetime_format_format ( + "mail", "header", + DTFormatKindDateTime, msg_date); + + if (hide_real_date) { + /* Show only the local-formatted date, losing + * all timezone information like Outlook does. + * Should we attempt to show it somehow? */ + txt = value = date_str; + } else { + txt = value = g_strdup_printf ( + "%s (%s)", html, date_str); + g_free (date_str); + } + g_free (html); + } + + flags |= E_MAIL_FORMATTER_HEADER_FLAG_HTML; + flags |= E_MAIL_FORMATTER_HEADER_FLAG_BOLD; + + } else if (g_str_equal (canon_name, "Newsgroups")) { + GSList *ng, *scan; + GString *html; + + buf = camel_header_unfold (header_value); + + if (!(ng = camel_header_newsgroups_decode (buf))) { + g_free (buf); + return; + } + + g_free (buf); + + html = g_string_new (""); + scan = ng; + while (scan) { + const gchar *newsgroup = scan->data; + + if (flags & E_MAIL_FORMATTER_HEADER_FLAG_NOLINKS) + g_string_append_printf ( + html, "%s", newsgroup); + else + g_string_append_printf ( + html, "%s", + newsgroup, newsgroup); + scan = g_slist_next (scan); + if (scan) + g_string_append_printf (html, ", "); + } + + g_slist_free_full (ng, g_free); + + txt = html->str; + value = g_string_free (html, FALSE); + + flags |= E_MAIL_FORMATTER_HEADER_FLAG_HTML; + flags |= E_MAIL_FORMATTER_HEADER_FLAG_BOLD; + + } else if (g_str_equal (canon_name, "Received") || + g_str_has_prefix (canon_name, "X-")) { + /* don't unfold Received nor extension headers */ + txt = value = camel_header_decode_string (header_value, charset); + + } else { + /* don't unfold Received nor extension headers */ + buf = camel_header_unfold (header_value); + txt = value = camel_header_decode_string (buf, charset); + g_free (buf); + } + + e_mail_formatter_format_text_header ( + formatter, buffer, label, txt, flags); + + g_free (value); + g_free (str_field); +} + +GList * +e_mail_formatter_find_rfc822_end_iter (GList *rfc822_start_iter) +{ + GList *link = rfc822_start_iter; + EMailPart *part; + const gchar *part_id; + gchar *end; + + g_return_val_if_fail (rfc822_start_iter != NULL, NULL); + + part = E_MAIL_PART (link->data); + + part_id = e_mail_part_get_id (part); + g_return_val_if_fail (part_id != NULL, NULL); + + end = g_strconcat (part_id, ".end", NULL); + + while (link != NULL) { + part = E_MAIL_PART (link->data); + + part_id = e_mail_part_get_id (part); + g_return_val_if_fail (part_id != NULL, NULL); + + if (g_strcmp0 (part_id, end) == 0) + break; + + link = g_list_next (link); + } + + g_free (end); + + return link; +} + +gchar * +e_mail_formatter_parse_html_mnemonics (const gchar *label, + gchar **out_access_key) +{ + const gchar *pos = NULL; + GString *html_label = NULL; + + g_return_val_if_fail (label != NULL, NULL); + + if (out_access_key != NULL) + *out_access_key = NULL; + + if (!g_utf8_validate (label, -1, NULL)) { + gchar *res = g_strdup (label); + + g_return_val_if_fail (g_utf8_validate (label, -1, NULL), res); + + return res; + } + + pos = strstr (label, "_"); + if (pos != NULL) { + gunichar uk; + + html_label = g_string_new (""); + g_string_append_len (html_label, label, pos - label); + + pos++; + uk = g_utf8_get_char (pos); + + pos = g_utf8_next_char (pos); + + g_string_append (html_label, ""); + g_string_append_unichar (html_label, uk); + g_string_append (html_label, ""); + g_string_append (html_label, pos); + + if (out_access_key != NULL && uk != 0) { + gchar ukstr[10]; + gint len; + + len = g_unichar_to_utf8 (g_unichar_toupper (uk), ukstr); + if (len > 0) + *out_access_key = g_strndup (ukstr, len); + } + + } else { + html_label = g_string_new (label); + } + + return g_string_free (html_label, FALSE); +} + +void +e_mail_formatter_format_security_header (EMailFormatter *formatter, + EMailFormatterContext *context, + GString *buffer, + EMailPart *part, + guint32 flags) +{ + const gchar* part_id; + gchar* part_id_prefix; + GString* tmp; + GQueue queue = G_QUEUE_INIT; + GList *head, *link; + + g_return_if_fail (E_IS_MAIL_PART_HEADERS (part)); + + /* Get prefix of this PURI */ + part_id = e_mail_part_get_id (part); + part_id_prefix = g_strndup (part_id, g_strrstr (part_id, ".") - part_id); + + /* Add encryption/signature header */ + tmp = g_string_new (""); + + e_mail_part_list_queue_parts (context->part_list, NULL, &queue); + + head = g_queue_peek_head_link (&queue); + + /* Find first secured part. */ + for (link = head; link != NULL; link = g_list_next(link)) { + EMailPart *mail_part = link->data; + + if (!e_mail_part_has_validity (mail_part)) + continue; + + if (!e_mail_part_id_has_prefix (mail_part, part_id_prefix)) + continue; + + if (e_mail_part_get_validity (mail_part, E_MAIL_PART_VALIDITY_PGP | E_MAIL_PART_VALIDITY_SIGNED)) { + g_string_append (tmp, _("GPG signed")); + } + + if (e_mail_part_get_validity (mail_part, E_MAIL_PART_VALIDITY_PGP | E_MAIL_PART_VALIDITY_ENCRYPTED)) { + if (tmp->len > 0) + g_string_append (tmp, ", "); + g_string_append (tmp, _("GPG encrypted")); + } + + if (e_mail_part_get_validity (mail_part, E_MAIL_PART_VALIDITY_SMIME | E_MAIL_PART_VALIDITY_SIGNED)) { + if (tmp->len > 0) + g_string_append (tmp, ", "); + g_string_append (tmp, _("S/MIME signed")); + } + + if (e_mail_part_get_validity (mail_part, E_MAIL_PART_VALIDITY_SMIME | E_MAIL_PART_VALIDITY_ENCRYPTED)) { + if (tmp->len > 0) + g_string_append (tmp, ", "); + g_string_append (tmp, _("S/MIME encrypted")); + } + + break; + } + + if (tmp->len > 0) { + e_mail_formatter_format_header ( + formatter, buffer, + _("Security"), tmp->str, + flags, + "UTF-8"); + } + + while (!g_queue_is_empty (&queue)) + g_object_unref (g_queue_pop_head (&queue)); + + g_string_free (tmp, TRUE); + g_free (part_id_prefix); +} diff --git a/src/em-format/e-mail-parser-application-smime.c b/src/em-format/e-mail-parser-application-smime.c index 87f25b3..e569cd7 100644 --- a/src/em-format/e-mail-parser-application-smime.c +++ b/src/em-format/e-mail-parser-application-smime.c @@ -22,6 +22,7 @@ #include +#include "e-mail-formatter-utils.h" #include "e-mail-parser-extension.h" #include "e-mail-part-utils.h" @@ -104,6 +105,10 @@ empe_app_smime_parse (EMailParserExtension *extension, mail_part, valid, E_MAIL_PART_VALIDITY_ENCRYPTED | E_MAIL_PART_VALIDITY_SMIME); + + /* Do not traverse sub-messages */ + if (g_str_has_suffix (e_mail_part_get_id (mail_part), ".rfc822")) + link = e_mail_formatter_find_rfc822_end_iter (link); } e_queue_transfer (&work_queue, out_mail_parts); diff --git a/src/em-format/e-mail-parser-application-smime.c.cve-2018-15587-reposition-signature-bar b/src/em-format/e-mail-parser-application-smime.c.cve-2018-15587-reposition-signature-bar new file mode 100644 index 0000000..87f25b3 --- /dev/null +++ b/src/em-format/e-mail-parser-application-smime.c.cve-2018-15587-reposition-signature-bar @@ -0,0 +1,158 @@ +/* + * e-mail-parser-application-xpkcs7mime.c + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY + * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with this program; if not, see . + * + */ + +#include "evolution-config.h" + +#include +#include + +#include + +#include "e-mail-parser-extension.h" +#include "e-mail-part-utils.h" + +typedef EMailParserExtension EMailParserApplicationSMIME; +typedef EMailParserExtensionClass EMailParserApplicationSMIMEClass; + +GType e_mail_parser_application_smime_get_type (void); + +G_DEFINE_TYPE ( + EMailParserApplicationSMIME, + e_mail_parser_application_smime, + E_TYPE_MAIL_PARSER_EXTENSION) + +static const gchar *parser_mime_types[] = { + "application/xpkcs7mime", + "application/x-pkcs7-mime", + "application/pkcs7-mime", + "application/pkcs7-signature", + "application/xpkcs7-signature", + "application/x-pkcs7-signature", + NULL +}; + +static gboolean +empe_app_smime_parse (EMailParserExtension *extension, + EMailParser *parser, + CamelMimePart *part, + GString *part_id, + GCancellable *cancellable, + GQueue *out_mail_parts) +{ + CamelCipherContext *context; + CamelMimePart *opart; + CamelCipherValidity *valid; + CamelContentType *ct; + GError *local_error = NULL; + + ct = camel_mime_part_get_content_type (part); + if (camel_content_type_is (ct, "application", "pkcs7-signature") || + camel_content_type_is (ct, "application", "xpkcs7-signature") || + camel_content_type_is (ct, "application", "x-pkcs7-signature")) { + return TRUE; + } + + context = camel_smime_context_new (e_mail_parser_get_session (parser)); + + opart = camel_mime_part_new (); + valid = camel_cipher_context_decrypt_sync ( + context, part, opart, + cancellable, &local_error); + + e_mail_part_preserve_charset_in_content_type (part, opart); + + if (local_error != NULL) { + e_mail_parser_error ( + parser, out_mail_parts, + _("Could not parse S/MIME message: %s"), + local_error->message); + g_error_free (local_error); + + } else { + GQueue work_queue = G_QUEUE_INIT; + GList *head, *link; + gint len = part_id->len; + + g_string_append (part_id, ".encrypted"); + + e_mail_parser_parse_part ( + parser, opart, part_id, cancellable, &work_queue); + + g_string_truncate (part_id, len); + + head = g_queue_peek_head_link (&work_queue); + + /* Update validity flags of all the involved subp-arts */ + for (link = head; link != NULL; link = g_list_next (link)) { + EMailPart *mail_part = link->data; + + e_mail_part_update_validity ( + mail_part, valid, + E_MAIL_PART_VALIDITY_ENCRYPTED | + E_MAIL_PART_VALIDITY_SMIME); + } + + e_queue_transfer (&work_queue, out_mail_parts); + + /* Add a widget with details about the encryption, but only + * when the encrypted isn't itself secured, in that case it + * has created the button itself. */ + if (!e_mail_part_is_secured (opart)) { + EMailPart *mail_part; + + g_string_append (part_id, ".encrypted.button"); + + e_mail_parser_parse_part_as ( + parser, part, part_id, + "application/vnd.evolution.secure-button", + cancellable, &work_queue); + + mail_part = g_queue_peek_head (&work_queue); + + if (mail_part != NULL) + e_mail_part_update_validity ( + mail_part, valid, + E_MAIL_PART_VALIDITY_ENCRYPTED | + E_MAIL_PART_VALIDITY_SMIME); + + e_queue_transfer (&work_queue, out_mail_parts); + + g_string_truncate (part_id, len); + } + + camel_cipher_validity_free (valid); + } + + g_object_unref (opart); + g_object_unref (context); + + return TRUE; +} + +static void +e_mail_parser_application_smime_class_init (EMailParserExtensionClass *class) +{ + class->mime_types = parser_mime_types; + class->priority = G_PRIORITY_LOW; + class->flags = E_MAIL_PARSER_EXTENSION_INLINE; + class->parse = empe_app_smime_parse; +} + +static void +e_mail_parser_application_smime_init (EMailParserExtension *extension) +{ +} diff --git a/src/em-format/e-mail-parser-inlinepgp-encrypted.c b/src/em-format/e-mail-parser-inlinepgp-encrypted.c index c66195c..e342825 100644 --- a/src/em-format/e-mail-parser-inlinepgp-encrypted.c +++ b/src/em-format/e-mail-parser-inlinepgp-encrypted.c @@ -22,6 +22,7 @@ #include +#include "e-mail-formatter-utils.h" #include "e-mail-parser-extension.h" #include "e-mail-part-utils.h" @@ -135,6 +136,10 @@ empe_inlinepgp_encrypted_parse (EMailParserExtension *extension, mail_part, valid, E_MAIL_PART_VALIDITY_ENCRYPTED | E_MAIL_PART_VALIDITY_PGP); + + /* Do not traverse sub-messages */ + if (g_str_has_suffix (e_mail_part_get_id (mail_part), ".rfc822")) + link = e_mail_formatter_find_rfc822_end_iter (link); } e_queue_transfer (&work_queue, out_mail_parts); diff --git a/src/em-format/e-mail-parser-inlinepgp-encrypted.c.cve-2018-15587-reposition-signature-bar b/src/em-format/e-mail-parser-inlinepgp-encrypted.c.cve-2018-15587-reposition-signature-bar new file mode 100644 index 0000000..c66195c --- /dev/null +++ b/src/em-format/e-mail-parser-inlinepgp-encrypted.c.cve-2018-15587-reposition-signature-bar @@ -0,0 +1,186 @@ +/* + * e-mail-parser-inlinepgp-encrypted.c + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY + * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with this program; if not, see . + * + */ + +#include "evolution-config.h" + +#include +#include + +#include + +#include "e-mail-parser-extension.h" +#include "e-mail-part-utils.h" + +typedef EMailParserExtension EMailParserInlinePGPEncrypted; +typedef EMailParserExtensionClass EMailParserInlinePGPEncryptedClass; + +GType e_mail_parser_inline_pgp_encrypted_get_type (void); + +G_DEFINE_TYPE ( + EMailParserInlinePGPEncrypted, + e_mail_parser_inline_pgp_encrypted, + E_TYPE_MAIL_PARSER_EXTENSION) + +static const gchar *parser_mime_types[] = { + "application/x-inlinepgp-encrypted", + NULL +}; + +static gboolean +empe_inlinepgp_encrypted_parse (EMailParserExtension *extension, + EMailParser *parser, + CamelMimePart *part, + GString *part_id, + GCancellable *cancellable, + GQueue *out_mail_parts) +{ + CamelCipherContext *cipher; + CamelCipherValidity *valid; + CamelMimePart *opart; + CamelDataWrapper *dw; + gchar *mime_type; + gint len; + GQueue work_queue = G_QUEUE_INIT; + GList *head, *link; + GError *local_error = NULL; + + if (g_cancellable_is_cancelled (cancellable)) + return FALSE; + + cipher = camel_gpg_context_new (e_mail_parser_get_session (parser)); + + opart = camel_mime_part_new (); + + /* Decrypt the message */ + valid = camel_cipher_context_decrypt_sync ( + cipher, part, opart, cancellable, &local_error); + + if (local_error != NULL) { + e_mail_parser_error ( + parser, out_mail_parts, + _("Could not parse PGP message: %s"), + local_error->message); + g_error_free (local_error); + + e_mail_parser_parse_part_as ( + parser, + part, part_id, + "application/vnd.evolution.source", + cancellable, out_mail_parts); + + g_object_unref (cipher); + g_object_unref (opart); + + return TRUE; + } + + dw = camel_medium_get_content ((CamelMedium *) opart); + mime_type = camel_data_wrapper_get_mime_type (dw); + + /* this ensures to show the 'opart' as inlined, if possible */ + if (mime_type && g_ascii_strcasecmp (mime_type, "application/octet-stream") == 0) { + const gchar *snoop; + + snoop = e_mail_part_snoop_type (opart); + + if (snoop != NULL) { + camel_data_wrapper_set_mime_type (dw, snoop); + + /* Set the MIME type on the 'opart' itself as well. + * If it's "text/plain", then we want the TextPlain + * parser extension to treat it as "text/plain" and + * NOT wrap it as an attachment. */ + camel_data_wrapper_set_mime_type ( + CAMEL_DATA_WRAPPER (opart), snoop); + } + } + + e_mail_part_preserve_charset_in_content_type (part, opart); + g_free (mime_type); + + /* Pass it off to the real formatter */ + len = part_id->len; + g_string_append (part_id, ".inlinepgp_encrypted"); + + mime_type = camel_data_wrapper_get_mime_type (dw); + + g_warn_if_fail (e_mail_parser_parse_part_as ( + parser, opart, part_id, mime_type, + cancellable, &work_queue)); + + g_free (mime_type); + + g_string_truncate (part_id, len); + + head = g_queue_peek_head_link (&work_queue); + + for (link = head; link != NULL; link = g_list_next (link)) { + EMailPart *mail_part = link->data; + + e_mail_part_update_validity ( + mail_part, valid, + E_MAIL_PART_VALIDITY_ENCRYPTED | + E_MAIL_PART_VALIDITY_PGP); + } + + e_queue_transfer (&work_queue, out_mail_parts); + + /* Add a widget with details about the encryption, but only when + * the encrypted isn't itself secured, in that case it has created + * the button itself */ + if (!e_mail_part_is_secured (opart)) { + EMailPart *mail_part; + + g_string_append (part_id, ".inlinepgp_encrypted.button"); + + e_mail_parser_parse_part_as ( + parser, part, part_id, + "application/vnd.evolution.secure-button", + cancellable, &work_queue); + + mail_part = g_queue_peek_head (&work_queue); + if (mail_part != NULL) + e_mail_part_update_validity ( + mail_part, valid, + E_MAIL_PART_VALIDITY_ENCRYPTED | + E_MAIL_PART_VALIDITY_PGP); + + e_queue_transfer (&work_queue, out_mail_parts); + + g_string_truncate (part_id, len); + } + + /* Clean Up */ + camel_cipher_validity_free (valid); + g_object_unref (opart); + g_object_unref (cipher); + + return TRUE; +} + +static void +e_mail_parser_inline_pgp_encrypted_class_init (EMailParserExtensionClass *class) +{ + class->mime_types = parser_mime_types; + class->priority = G_PRIORITY_LOW; + class->parse = empe_inlinepgp_encrypted_parse; +} + +static void +e_mail_parser_inline_pgp_encrypted_init (EMailParserExtension *extension) +{ +} diff --git a/src/em-format/e-mail-parser-inlinepgp-signed.c b/src/em-format/e-mail-parser-inlinepgp-signed.c index 2ac8c3a..3235a1c 100644 --- a/src/em-format/e-mail-parser-inlinepgp-signed.c +++ b/src/em-format/e-mail-parser-inlinepgp-signed.c @@ -22,6 +22,7 @@ #include +#include "e-mail-formatter-utils.h" #include "e-mail-parser-extension.h" #include "e-mail-part-utils.h" @@ -142,6 +143,10 @@ empe_inlinepgp_signed_parse (EMailParserExtension *extension, mail_part, valid, E_MAIL_PART_VALIDITY_SIGNED | E_MAIL_PART_VALIDITY_PGP); + + /* Do not traverse sub-messages */ + if (g_str_has_suffix (e_mail_part_get_id (mail_part), ".rfc822")) + link = e_mail_formatter_find_rfc822_end_iter (link); } e_queue_transfer (&work_queue, out_mail_parts); diff --git a/src/em-format/e-mail-parser-inlinepgp-signed.c.cve-2018-15587-reposition-signature-bar b/src/em-format/e-mail-parser-inlinepgp-signed.c.cve-2018-15587-reposition-signature-bar new file mode 100644 index 0000000..2ac8c3a --- /dev/null +++ b/src/em-format/e-mail-parser-inlinepgp-signed.c.cve-2018-15587-reposition-signature-bar @@ -0,0 +1,196 @@ +/* + * e-mail-parser-inlinepgp-signed.c + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY + * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with this program; if not, see . + * + */ + +#include "evolution-config.h" + +#include +#include + +#include + +#include "e-mail-parser-extension.h" +#include "e-mail-part-utils.h" + +typedef EMailParserExtension EMailParserInlinePGPSigned; +typedef EMailParserExtensionClass EMailParserInlinePGPSignedClass; + +GType e_mail_parser_inline_pgp_signed_get_type (void); + +G_DEFINE_TYPE ( + EMailParserInlinePGPSigned, + e_mail_parser_inline_pgp_signed, + E_TYPE_MAIL_PARSER_EXTENSION) + +static const gchar *parser_mime_types[] = { + "application/x-inlinepgp-signed", + NULL +}; + +static gboolean +empe_inlinepgp_signed_parse (EMailParserExtension *extension, + EMailParser *parser, + CamelMimePart *part, + GString *part_id, + GCancellable *cancellable, + GQueue *out_mail_parts) +{ + CamelStream *filtered_stream; + CamelMimeFilterPgp *pgp_filter; + CamelContentType *content_type; + CamelCipherContext *cipher; + CamelCipherValidity *valid; + CamelDataWrapper *dw; + CamelMimePart *opart; + CamelStream *ostream; + GQueue work_queue = G_QUEUE_INIT; + GList *head, *link; + gchar *type; + gint len; + GError *local_error = NULL; + GByteArray *ba; + + if (g_cancellable_is_cancelled (cancellable)) + return FALSE; + + cipher = camel_gpg_context_new (e_mail_parser_get_session (parser)); + + /* Verify the signature of the message */ + valid = camel_cipher_context_verify_sync ( + cipher, part, cancellable, &local_error); + + if (local_error != NULL) { + e_mail_parser_error ( + parser, out_mail_parts, + _("Error verifying signature: %s"), + local_error->message); + + g_error_free (local_error); + + e_mail_parser_parse_part_as ( + parser, part, part_id, + "application/vnd.evolution.source", + cancellable, out_mail_parts); + + g_object_unref (cipher); + + return TRUE; + } + + /* Setup output stream */ + ostream = camel_stream_mem_new (); + filtered_stream = camel_stream_filter_new (ostream); + + /* Add PGP header / footer filter */ + pgp_filter = (CamelMimeFilterPgp *) camel_mime_filter_pgp_new (); + camel_stream_filter_add ( + CAMEL_STREAM_FILTER (filtered_stream), + CAMEL_MIME_FILTER (pgp_filter)); + g_object_unref (pgp_filter); + + /* Pass through the filters that have been setup */ + dw = camel_medium_get_content ((CamelMedium *) part); + camel_data_wrapper_decode_to_stream_sync ( + dw, (CamelStream *) filtered_stream, cancellable, NULL); + camel_stream_flush ((CamelStream *) filtered_stream, cancellable, NULL); + g_object_unref (filtered_stream); + + /* Create a new text/plain MIME part containing the signed + * content preserving the original part's Content-Type params. */ + content_type = camel_mime_part_get_content_type (part); + type = camel_content_type_format (content_type); + content_type = camel_content_type_decode (type); + g_free (type); + + g_free (content_type->type); + content_type->type = g_strdup ("text"); + g_free (content_type->subtype); + content_type->subtype = g_strdup ("plain"); + type = camel_content_type_format (content_type); + camel_content_type_unref (content_type); + + ba = camel_stream_mem_get_byte_array ((CamelStreamMem *) ostream); + opart = camel_mime_part_new (); + camel_mime_part_set_content (opart, (gchar *) ba->data, ba->len, type); + g_free (type); + + len = part_id->len; + g_string_append (part_id, ".inlinepgp_signed"); + + g_warn_if_fail (e_mail_parser_parse_part ( + parser, opart, part_id, cancellable, &work_queue)); + + head = g_queue_peek_head_link (&work_queue); + + for (link = head; link != NULL; link = g_list_next (link)) { + EMailPart *mail_part = link->data; + + e_mail_part_update_validity ( + mail_part, valid, + E_MAIL_PART_VALIDITY_SIGNED | + E_MAIL_PART_VALIDITY_PGP); + } + + e_queue_transfer (&work_queue, out_mail_parts); + + g_string_truncate (part_id, len); + + /* Add a widget with details about the encryption, but only when + * the encrypted isn't itself secured, in that case it has created + * the button itself */ + if (!e_mail_part_is_secured (opart)) { + EMailPart *mail_part; + + g_string_append (part_id, ".inlinepgp_signed.button"); + + e_mail_parser_parse_part_as ( + parser, part, part_id, + "application/vnd.evolution.secure-button", + cancellable, &work_queue); + + mail_part = g_queue_peek_head (&work_queue); + if (mail_part != NULL) + e_mail_part_update_validity ( + mail_part, valid, + E_MAIL_PART_VALIDITY_SIGNED | + E_MAIL_PART_VALIDITY_PGP); + + e_queue_transfer (&work_queue, out_mail_parts); + + g_string_truncate (part_id, len); + } + + /* Clean Up */ + camel_cipher_validity_free (valid); + g_object_unref (opart); + g_object_unref (ostream); + g_object_unref (cipher); + + return TRUE; +} + +static void +e_mail_parser_inline_pgp_signed_class_init (EMailParserExtensionClass *class) +{ + class->mime_types = parser_mime_types; + class->priority = G_PRIORITY_LOW; + class->parse = empe_inlinepgp_signed_parse; +} + +static void +e_mail_parser_inline_pgp_signed_init (EMailParserExtension *extension) +{ +} diff --git a/src/em-format/e-mail-parser-multipart-encrypted.c b/src/em-format/e-mail-parser-multipart-encrypted.c index 2baa98f..818214c 100644 --- a/src/em-format/e-mail-parser-multipart-encrypted.c +++ b/src/em-format/e-mail-parser-multipart-encrypted.c @@ -21,6 +21,7 @@ #include +#include "e-mail-formatter-utils.h" #include "e-mail-parser-extension.h" #include "e-mail-part-utils.h" @@ -126,6 +127,10 @@ empe_mp_encrypted_parse (EMailParserExtension *extension, mail_part, valid, E_MAIL_PART_VALIDITY_ENCRYPTED | E_MAIL_PART_VALIDITY_PGP); + + /* Do not traverse sub-messages */ + if (g_str_has_suffix (e_mail_part_get_id (mail_part), ".rfc822")) + link = e_mail_formatter_find_rfc822_end_iter (link); } e_queue_transfer (&work_queue, out_mail_parts); diff --git a/src/em-format/e-mail-parser-multipart-encrypted.c.cve-2018-15587-reposition-signature-bar b/src/em-format/e-mail-parser-multipart-encrypted.c.cve-2018-15587-reposition-signature-bar new file mode 100644 index 0000000..2baa98f --- /dev/null +++ b/src/em-format/e-mail-parser-multipart-encrypted.c.cve-2018-15587-reposition-signature-bar @@ -0,0 +1,179 @@ +/* + * e-mail-parser-multipart-encrypted.c + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY + * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with this program; if not, see . + * + */ + +#include "evolution-config.h" + +#include + +#include + +#include "e-mail-parser-extension.h" +#include "e-mail-part-utils.h" + +typedef EMailParserExtension EMailParserMultipartEncrypted; +typedef EMailParserExtensionClass EMailParserMultipartEncryptedClass; + +GType e_mail_parser_multipart_encrypted_get_type (void); + +G_DEFINE_TYPE ( + EMailParserMultipartEncrypted, + e_mail_parser_multipart_encrypted, + E_TYPE_MAIL_PARSER_EXTENSION) + +static const gchar *parser_mime_types[] = { + "multipart/encrypted", + NULL +}; + +static gboolean +empe_mp_encrypted_parse (EMailParserExtension *extension, + EMailParser *parser, + CamelMimePart *part, + GString *part_id, + GCancellable *cancellable, + GQueue *out_mail_parts) +{ + CamelCipherContext *context; + const gchar *protocol; + CamelMimePart *opart; + CamelCipherValidity *valid; + CamelMultipartEncrypted *mpe; + GQueue work_queue = G_QUEUE_INIT; + GList *head, *link; + GError *local_error = NULL; + gint len; + + mpe = (CamelMultipartEncrypted *) camel_medium_get_content ((CamelMedium *) part); + if (!CAMEL_IS_MULTIPART_ENCRYPTED (mpe)) { + e_mail_parser_error ( + parser, out_mail_parts, + _("Could not parse MIME message. " + "Displaying as source.")); + e_mail_parser_parse_part_as ( + parser, part, part_id, + "application/vnd.evolution/source", + cancellable, out_mail_parts); + + return TRUE; + } + + /* Currently we only handle RFC2015-style PGP encryption. */ + protocol = camel_content_type_param (camel_data_wrapper_get_mime_type_field (CAMEL_DATA_WRAPPER (mpe)), "protocol"); + if (!protocol || g_ascii_strcasecmp (protocol, "application/pgp-encrypted") != 0) { + e_mail_parser_error ( + parser, out_mail_parts, + _("Unsupported encryption type for multipart/encrypted")); + e_mail_parser_parse_part_as ( + parser, part, part_id, "multipart/mixed", + cancellable, out_mail_parts); + + return TRUE; + } + + context = camel_gpg_context_new (e_mail_parser_get_session (parser)); + + opart = camel_mime_part_new (); + valid = camel_cipher_context_decrypt_sync ( + context, part, opart, cancellable, &local_error); + + e_mail_part_preserve_charset_in_content_type (part, opart); + + if (local_error != NULL) { + e_mail_parser_error ( + parser, out_mail_parts, + _("Could not parse PGP/MIME message: %s"), + local_error->message); + e_mail_parser_parse_part_as ( + parser, part, part_id, "multipart/mixed", + cancellable, out_mail_parts); + + g_object_unref (opart); + g_object_unref (context); + g_error_free (local_error); + + return TRUE; + } + + len = part_id->len; + g_string_append (part_id, ".encrypted"); + + g_warn_if_fail (e_mail_parser_parse_part ( + parser, opart, part_id, cancellable, &work_queue)); + + g_string_truncate (part_id, len); + + head = g_queue_peek_head_link (&work_queue); + + /* Update validity of all encrypted sub-parts */ + for (link = head; link != NULL; link = g_list_next (link)) { + EMailPart *mail_part = link->data; + + e_mail_part_update_validity ( + mail_part, valid, + E_MAIL_PART_VALIDITY_ENCRYPTED | + E_MAIL_PART_VALIDITY_PGP); + } + + e_queue_transfer (&work_queue, out_mail_parts); + + /* Add a widget with details about the encryption, but only when + * the decrypted part isn't itself secured, in that case it has + * created the button itself. */ + if (!e_mail_part_is_secured (opart)) { + EMailPart *mail_part; + + g_string_append (part_id, ".encrypted.button"); + + e_mail_parser_parse_part_as ( + parser, part, part_id, + "application/vnd.evolution.secure-button", + cancellable, &work_queue); + + mail_part = g_queue_peek_head (&work_queue); + + if (mail_part != NULL) + e_mail_part_update_validity ( + mail_part, valid, + E_MAIL_PART_VALIDITY_ENCRYPTED | + E_MAIL_PART_VALIDITY_PGP); + + e_queue_transfer (&work_queue, out_mail_parts); + + g_string_truncate (part_id, len); + } + + camel_cipher_validity_free (valid); + + /* TODO: Make sure when we finalize this part, it is zero'd out */ + g_object_unref (opart); + g_object_unref (context); + + return TRUE; +} + +static void +e_mail_parser_multipart_encrypted_class_init (EMailParserExtensionClass *class) +{ + class->mime_types = parser_mime_types; + class->priority = G_PRIORITY_LOW; + class->parse = empe_mp_encrypted_parse; +} + +static void +e_mail_parser_multipart_encrypted_init (EMailParserExtension *extension) +{ +} diff --git a/src/em-format/e-mail-parser-multipart-signed.c b/src/em-format/e-mail-parser-multipart-signed.c index a76ca61..ca0133a 100644 --- a/src/em-format/e-mail-parser-multipart-signed.c +++ b/src/em-format/e-mail-parser-multipart-signed.c @@ -21,6 +21,7 @@ #include +#include "e-mail-formatter-utils.h" #include "e-mail-parser-extension.h" #include "e-mail-part-utils.h" @@ -170,6 +171,10 @@ empe_mp_signed_parse (EMailParserExtension *extension, e_mail_part_update_validity ( mail_part, valid, validity_type | E_MAIL_PART_VALIDITY_SIGNED); + + /* Do not traverse sub-messages */ + if (g_str_has_suffix (e_mail_part_get_id (mail_part), ".rfc822")) + link = e_mail_formatter_find_rfc822_end_iter (link); } e_queue_transfer (&work_queue, out_mail_parts); diff --git a/src/em-format/e-mail-parser-multipart-signed.c.cve-2018-15587-reposition-signature-bar b/src/em-format/e-mail-parser-multipart-signed.c.cve-2018-15587-reposition-signature-bar new file mode 100644 index 0000000..a76ca61 --- /dev/null +++ b/src/em-format/e-mail-parser-multipart-signed.c.cve-2018-15587-reposition-signature-bar @@ -0,0 +1,222 @@ +/* + * e-mail-parser-multipart-signed.c + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY + * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with this program; if not, see . + * + */ + +#include "evolution-config.h" + +#include + +#include + +#include "e-mail-parser-extension.h" +#include "e-mail-part-utils.h" + +typedef EMailParserExtension EMailParserMultipartSigned; +typedef EMailParserExtensionClass EMailParserMultipartSignedClass; + +GType e_mail_parser_multipart_signed_get_type (void); + +G_DEFINE_TYPE ( + EMailParserMultipartSigned, + e_mail_parser_multipart_signed, + E_TYPE_MAIL_PARSER_EXTENSION) + +static const gchar *parser_mime_types[] = { + "multipart/signed", + "application/pgp-signature", + NULL +}; + +static gboolean +empe_mp_signed_parse (EMailParserExtension *extension, + EMailParser *parser, + CamelMimePart *part, + GString *part_id, + GCancellable *cancellable, + GQueue *out_mail_parts) +{ + CamelMimePart *cpart = NULL; + CamelMultipart *multipart; + CamelCipherContext *cipher = NULL; + CamelContentType *content_type; + CamelSession *session; + guint32 validity_type; + CamelCipherValidity *valid; + const gchar *protocol = NULL; + GError *local_error = NULL; + gint i, nparts, len; + gboolean secured; + + /* If the part is application/pgp-signature sub-part then skip it. */ + if (!CAMEL_IS_MULTIPART (part)) { + content_type = camel_mime_part_get_content_type (part); + if (camel_content_type_is ( + content_type, "application", "pgp-signature")) { + return TRUE; + } + } + + multipart = (CamelMultipart *) camel_medium_get_content ((CamelMedium *) part); + if (CAMEL_IS_MULTIPART_SIGNED (multipart)) { + cpart = camel_multipart_get_part ( + multipart, CAMEL_MULTIPART_SIGNED_CONTENT); + } + + if (cpart == NULL) { + e_mail_parser_error ( + parser, out_mail_parts, + _("Could not parse MIME message. " + "Displaying as source.")); + e_mail_parser_parse_part_as ( + parser, part, part_id, + "application/vnd.evolution.source", + cancellable, out_mail_parts); + + return TRUE; + } + + content_type = camel_data_wrapper_get_mime_type_field ( + CAMEL_DATA_WRAPPER (multipart)); + if (content_type != NULL) + protocol = camel_content_type_param (content_type, "protocol"); + + session = e_mail_parser_get_session (parser); + /* FIXME: Should be done via a plugin interface */ + /* FIXME: duplicated in em-format-html-display.c */ + if (protocol != NULL) { +#ifdef ENABLE_SMIME + if (g_ascii_strcasecmp ("application/x-pkcs7-signature", protocol) == 0 + || g_ascii_strcasecmp ("application/pkcs7-signature", protocol) == 0) { + cipher = camel_smime_context_new (session); + validity_type = E_MAIL_PART_VALIDITY_SMIME; + } else { +#endif + if (g_ascii_strcasecmp ("application/pgp-signature", protocol) == 0) { + cipher = camel_gpg_context_new (session); + validity_type = E_MAIL_PART_VALIDITY_PGP; + } +#ifdef ENABLE_SMIME + } +#endif + } + + if (cipher == NULL) { + e_mail_parser_error ( + parser, out_mail_parts, + _("Unsupported signature format")); + e_mail_parser_parse_part_as ( + parser, part, part_id, "multipart/mixed", + cancellable, out_mail_parts); + + return TRUE; + } + + valid = camel_cipher_context_verify_sync ( + cipher, part, cancellable, &local_error); + + if (local_error != NULL) { + e_mail_parser_error ( + parser, out_mail_parts, + _("Error verifying signature: %s"), + local_error->message); + e_mail_parser_parse_part_as ( + parser, part, part_id, "multipart/mixed", + cancellable, out_mail_parts); + + g_object_unref (cipher); + g_error_free (local_error); + + return TRUE; + } + + nparts = camel_multipart_get_number (multipart); + secured = FALSE; + len = part_id->len; + for (i = 0; i < nparts; i++) { + GQueue work_queue = G_QUEUE_INIT; + GList *head, *link; + CamelMimePart *subpart; + + subpart = camel_multipart_get_part (multipart, i); + + g_string_append_printf (part_id, ".signed.%d", i); + + g_warn_if_fail (e_mail_parser_parse_part ( + parser, subpart, part_id, cancellable, &work_queue)); + + g_string_truncate (part_id, len); + + if (!secured) + secured = e_mail_part_is_secured (subpart); + + head = g_queue_peek_head_link (&work_queue); + + for (link = head; link != NULL; link = g_list_next (link)) { + EMailPart *mail_part = link->data; + + e_mail_part_update_validity ( + mail_part, valid, + validity_type | E_MAIL_PART_VALIDITY_SIGNED); + } + + e_queue_transfer (&work_queue, out_mail_parts); + } + + /* Add a widget with details about the encryption, but only when + * the encrypted isn't itself secured, in that case it has created + * the button itself. */ + if (!secured) { + GQueue work_queue = G_QUEUE_INIT; + EMailPart *mail_part; + + g_string_append (part_id, ".signed.button"); + + e_mail_parser_parse_part_as ( + parser, part, part_id, + "application/vnd.evolution.secure-button", + cancellable, &work_queue); + + mail_part = g_queue_peek_head (&work_queue); + + if (mail_part != NULL) + e_mail_part_update_validity ( + mail_part, valid, + validity_type | E_MAIL_PART_VALIDITY_SIGNED); + + e_queue_transfer (&work_queue, out_mail_parts); + + g_string_truncate (part_id, len); + } + + camel_cipher_validity_free (valid); + + g_object_unref (cipher); + + return TRUE; +} + +static void +e_mail_parser_multipart_signed_class_init (EMailParserExtensionClass *class) +{ + class->mime_types = parser_mime_types; + class->priority = G_PRIORITY_LOW; + class->parse = empe_mp_signed_parse; +} + +static void +e_mail_parser_multipart_signed_init (EMailParserExtension *extension) +{ +} diff --git a/src/em-format/e-mail-parser.c b/src/em-format/e-mail-parser.c index 96c603f..b1f1511 100644 --- a/src/em-format/e-mail-parser.c +++ b/src/em-format/e-mail-parser.c @@ -79,6 +79,67 @@ GType e_mail_parser_application_smime_get_type (void); static gpointer parent_class; static void +mail_parser_move_security_before_headers (GQueue *part_queue) +{ + GList *link, *last_headers = NULL; + GSList *headers_stack = NULL; + + link = g_queue_peek_head_link (part_queue); + while (link) { + EMailPart *part = link->data; + const gchar *id; + + if (!part) { + link = g_list_next (link); + continue; + } + + id = e_mail_part_get_id (part); + if (!id) { + link = g_list_next (link); + continue; + } + + if (g_str_has_suffix (id, ".rfc822")) { + headers_stack = g_slist_prepend (headers_stack, last_headers); + last_headers = NULL; + } else if (g_str_has_suffix (id, ".rfc822.end")) { + g_warn_if_fail (headers_stack != NULL); + + if (headers_stack) { + last_headers = headers_stack->data; + headers_stack = g_slist_remove (headers_stack, last_headers); + } else { + last_headers = NULL; + } + } + + if (g_strcmp0 (e_mail_part_get_mime_type (part), "application/vnd.evolution.headers") == 0) { + last_headers = link; + link = g_list_next (link); + } else if (g_strcmp0 (e_mail_part_get_mime_type (part), "application/vnd.evolution.secure-button") == 0) { + g_warn_if_fail (last_headers != NULL); + + if (last_headers) { + GList *next = g_list_next (link); + + g_warn_if_fail (g_queue_remove (part_queue, part)); + g_queue_insert_before (part_queue, last_headers, part); + + link = next; + } else { + link = g_list_next (link); + } + } else { + link = g_list_next (link); + } + } + + g_warn_if_fail (headers_stack == NULL); + g_slist_free (headers_stack); +} + +static void mail_parser_run (EMailParser *parser, EMailPartList *part_list, GCancellable *cancellable) @@ -132,6 +193,8 @@ mail_parser_run (EMailParser *parser, break; } + mail_parser_move_security_before_headers (&mail_part_queue); + while (!g_queue_is_empty (&mail_part_queue)) { mail_part = g_queue_pop_head (&mail_part_queue); e_mail_part_list_add_part (part_list, mail_part); diff --git a/src/em-format/e-mail-parser.c.cve-2018-15587-reposition-signature-bar b/src/em-format/e-mail-parser.c.cve-2018-15587-reposition-signature-bar new file mode 100644 index 0000000..96c603f --- /dev/null +++ b/src/em-format/e-mail-parser.c.cve-2018-15587-reposition-signature-bar @@ -0,0 +1,832 @@ +/* + * e-mail-parser.c + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY + * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with this program; if not, see . + * + */ + +#include "evolution-config.h" + +#include "e-mail-parser.h" + +#include + +#include + +#include +#include + +#include "e-mail-parser-extension.h" +#include "e-mail-part-attachment.h" +#include "e-mail-part-utils.h" + +#define E_MAIL_PARSER_GET_PRIVATE(obj) \ + (G_TYPE_INSTANCE_GET_PRIVATE \ + ((obj), E_TYPE_MAIL_PARSER, EMailParserPrivate)) + +#define d(x) + +struct _EMailParserPrivate { + GMutex mutex; + + gint last_error; + + CamelSession *session; +}; + +enum { + PROP_0, + PROP_SESSION +}; + +/* internal parser extensions */ +GType e_mail_parser_application_mbox_get_type (void); +GType e_mail_parser_audio_get_type (void); +GType e_mail_parser_headers_get_type (void); +GType e_mail_parser_message_get_type (void); +GType e_mail_parser_secure_button_get_type (void); +GType e_mail_parser_source_get_type (void); +GType e_mail_parser_image_get_type (void); +GType e_mail_parser_inline_pgp_encrypted_get_type (void); +GType e_mail_parser_inline_pgp_signed_get_type (void); +GType e_mail_parser_message_delivery_status_get_type (void); +GType e_mail_parser_message_external_get_type (void); +GType e_mail_parser_message_rfc822_get_type (void); +GType e_mail_parser_multipart_alternative_get_type (void); +GType e_mail_parser_multipart_apple_double_get_type (void); +GType e_mail_parser_multipart_digest_get_type (void); +GType e_mail_parser_multipart_encrypted_get_type (void); +GType e_mail_parser_multipart_mixed_get_type (void); +GType e_mail_parser_multipart_related_get_type (void); +GType e_mail_parser_multipart_signed_get_type (void); +GType e_mail_parser_text_enriched_get_type (void); +GType e_mail_parser_text_html_get_type (void); +GType e_mail_parser_text_plain_get_type (void); +#ifdef ENABLE_SMIME +GType e_mail_parser_application_smime_get_type (void); +#endif + +static gpointer parent_class; + +static void +mail_parser_run (EMailParser *parser, + EMailPartList *part_list, + GCancellable *cancellable) +{ + EMailExtensionRegistry *reg; + CamelMimeMessage *message; + EMailPart *mail_part; + GQueue *parsers; + GQueue mail_part_queue = G_QUEUE_INIT; + GList *iter; + GString *part_id; + + message = e_mail_part_list_get_message (part_list); + + reg = e_mail_parser_get_extension_registry (parser); + + parsers = e_mail_extension_registry_get_for_mime_type ( + reg, "application/vnd.evolution.message"); + + if (parsers == NULL) + parsers = e_mail_extension_registry_get_for_mime_type ( + reg, "message/*"); + + /* No parsers means the internal Evolution parser + * extensions were not loaded. Something is terribly wrong! */ + g_return_if_fail (parsers != NULL); + + part_id = g_string_new (".message"); + + mail_part = e_mail_part_new (CAMEL_MIME_PART (message), ".message"); + e_mail_part_list_add_part (part_list, mail_part); + g_object_unref (mail_part); + + for (iter = parsers->head; iter; iter = iter->next) { + EMailParserExtension *extension; + gboolean message_handled; + + if (g_cancellable_is_cancelled (cancellable)) + break; + + extension = iter->data; + if (!extension) + continue; + + message_handled = e_mail_parser_extension_parse ( + extension, parser, + CAMEL_MIME_PART (message), + part_id, cancellable, &mail_part_queue); + + if (message_handled) + break; + } + + while (!g_queue_is_empty (&mail_part_queue)) { + mail_part = g_queue_pop_head (&mail_part_queue); + e_mail_part_list_add_part (part_list, mail_part); + g_object_unref (mail_part); + } + + g_string_free (part_id, TRUE); +} + +static void +shell_gone_cb (gpointer user_data, + GObject *gone_extension_registry) +{ + EMailParserClass *class = user_data; + + g_return_if_fail (class != NULL); + + g_clear_object (&class->extension_registry); +} + +static void +mail_parser_set_session (EMailParser *parser, + CamelSession *session) +{ + g_return_if_fail (CAMEL_IS_SESSION (session)); + g_return_if_fail (parser->priv->session == NULL); + + parser->priv->session = g_object_ref (session); +} + +static void +e_mail_parser_set_property (GObject *object, + guint property_id, + const GValue *value, + GParamSpec *pspec) +{ + switch (property_id) { + case PROP_SESSION: + mail_parser_set_session ( + E_MAIL_PARSER (object), + g_value_get_object (value)); + return; + } + + G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec); +} + +static void +e_mail_parser_get_property (GObject *object, + guint property_id, + GValue *value, + GParamSpec *pspec) +{ + switch (property_id) { + case PROP_SESSION: + g_value_set_object ( + value, + e_mail_parser_get_session ( + E_MAIL_PARSER (object))); + return; + } + + G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec); +} + +static void +e_mail_parser_finalize (GObject *object) +{ + EMailParserPrivate *priv; + + priv = E_MAIL_PARSER_GET_PRIVATE (object); + + g_clear_object (&priv->session); + g_mutex_clear (&priv->mutex); + + /* Chain up to parent's finalize() method. */ + G_OBJECT_CLASS (parent_class)->finalize (object); +} + +static void +e_mail_parser_base_init (EMailParserClass *class) +{ + EShell *shell; + + /* Register internal extensions. */ + g_type_ensure (e_mail_parser_application_mbox_get_type ()); + /* This is currently disabled, because the WebKit player requires javascript, + which is disabled in Evolution. */ + /* g_type_ensure (e_mail_parser_audio_get_type ()); */ + g_type_ensure (e_mail_parser_headers_get_type ()); + g_type_ensure (e_mail_parser_message_get_type ()); + g_type_ensure (e_mail_parser_secure_button_get_type ()); + g_type_ensure (e_mail_parser_source_get_type ()); + g_type_ensure (e_mail_parser_image_get_type ()); + g_type_ensure (e_mail_parser_inline_pgp_encrypted_get_type ()); + g_type_ensure (e_mail_parser_inline_pgp_signed_get_type ()); + g_type_ensure (e_mail_parser_message_delivery_status_get_type ()); + g_type_ensure (e_mail_parser_message_external_get_type ()); + g_type_ensure (e_mail_parser_message_rfc822_get_type ()); + g_type_ensure (e_mail_parser_multipart_alternative_get_type ()); + g_type_ensure (e_mail_parser_multipart_apple_double_get_type ()); + g_type_ensure (e_mail_parser_multipart_digest_get_type ()); + g_type_ensure (e_mail_parser_multipart_encrypted_get_type ()); + g_type_ensure (e_mail_parser_multipart_mixed_get_type ()); + g_type_ensure (e_mail_parser_multipart_related_get_type ()); + g_type_ensure (e_mail_parser_multipart_signed_get_type ()); + g_type_ensure (e_mail_parser_text_enriched_get_type ()); + g_type_ensure (e_mail_parser_text_html_get_type ()); + g_type_ensure (e_mail_parser_text_plain_get_type ()); +#ifdef ENABLE_SMIME + g_type_ensure (e_mail_parser_application_smime_get_type ()); +#endif + + class->extension_registry = g_object_new ( + E_TYPE_MAIL_PARSER_EXTENSION_REGISTRY, NULL); + + e_mail_parser_extension_registry_load (class->extension_registry); + + e_extensible_load_extensions (E_EXTENSIBLE (class->extension_registry)); + + shell = e_shell_get_default (); + g_object_weak_ref (G_OBJECT (shell), shell_gone_cb, class); +} + +static void +e_mail_parser_class_init (EMailParserClass *class) +{ + GObjectClass *object_class; + + parent_class = g_type_class_peek_parent (class); + g_type_class_add_private (class, sizeof (EMailParserPrivate)); + + object_class = G_OBJECT_CLASS (class); + object_class->finalize = e_mail_parser_finalize; + object_class->set_property = e_mail_parser_set_property; + object_class->get_property = e_mail_parser_get_property; + + g_object_class_install_property ( + object_class, + PROP_SESSION, + g_param_spec_object ( + "session", + "Camel Session", + NULL, + CAMEL_TYPE_SESSION, + G_PARAM_READWRITE | + G_PARAM_CONSTRUCT_ONLY)); +} + +static void +e_mail_parser_init (EMailParser *parser) +{ + parser->priv = E_MAIL_PARSER_GET_PRIVATE (parser); + + g_mutex_init (&parser->priv->mutex); +} + +GType +e_mail_parser_get_type (void) +{ + static GType type = 0; + + if (G_UNLIKELY (type == 0)) { + static const GTypeInfo type_info = { + sizeof (EMailParserClass), + (GBaseInitFunc) e_mail_parser_base_init, + (GBaseFinalizeFunc) NULL, + (GClassInitFunc) e_mail_parser_class_init, + (GClassFinalizeFunc) NULL, + NULL, /* class_data */ + sizeof (EMailParser), + 0, /* n_preallocs */ + (GInstanceInitFunc) e_mail_parser_init, + NULL /* value_table */ + }; + + type = g_type_register_static ( + G_TYPE_OBJECT, "EMailParser", + &type_info, 0); + } + + return type; +} + +EMailParser * +e_mail_parser_new (CamelSession *session) +{ + g_return_val_if_fail (CAMEL_IS_SESSION (session), NULL); + + return g_object_new ( + E_TYPE_MAIL_PARSER, + "session", session, NULL); +} + +/** + * e_mail_parser_parse_sync: + * @parser: an #EMailParser + * @folder: (allow none) a #CamelFolder containing the @message or %NULL + * @message_uid: (allow none) UID of the @message within the @folder or %NULL + * @message: a #CamelMimeMessage + * @cancellable: (allow-none) a #GCancellable + * + * Parses the @message synchronously. Returns a list of #EMailParts which + * represents structure of the message and additional properties of each part. + * + * Note that this function can block for a while, so it's not a good idea to call + * it from main thread. + * + * Return Value: An #EMailPartsList + */ +EMailPartList * +e_mail_parser_parse_sync (EMailParser *parser, + CamelFolder *folder, + const gchar *message_uid, + CamelMimeMessage *message, + GCancellable *cancellable) +{ + EMailPartList *part_list; + + g_return_val_if_fail (E_IS_MAIL_PARSER (parser), NULL); + g_return_val_if_fail (CAMEL_IS_MIME_MESSAGE (message), NULL); + + part_list = e_mail_part_list_new (message, message_uid, folder); + + mail_parser_run (parser, part_list, cancellable); + + if (camel_debug_start ("emformat:parser")) { + GQueue queue = G_QUEUE_INIT; + + printf ( + "%s finished with EMailPartList:\n", + G_OBJECT_TYPE_NAME (parser)); + + e_mail_part_list_queue_parts (part_list, NULL, &queue); + + while (!g_queue_is_empty (&queue)) { + EMailPart *part; + + part = g_queue_pop_head (&queue); + + printf ( + " id: %s | cid: %s | mime_type: %s | " + "is_hidden: %d | is_attachment: %d\n", + e_mail_part_get_id (part), + e_mail_part_get_cid (part), + e_mail_part_get_mime_type (part), + part->is_hidden ? 1 : 0, + e_mail_part_get_is_attachment (part) ? 1 : 0); + + g_object_unref (part); + } + + camel_debug_end (); + } + + return part_list; +} + +static void +mail_parser_parse_thread (GSimpleAsyncResult *simple, + GObject *source_object, + GCancellable *cancellable) +{ + EMailPartList *part_list; + + part_list = g_simple_async_result_get_op_res_gpointer (simple); + + mail_parser_run ( + E_MAIL_PARSER (source_object), + part_list, cancellable); +} + +/** + * e_mail_parser_parse: + * @parser: an #EMailParser + * @message: a #CamelMimeMessage + * @callback: a #GAsyncReadyCallback + * @cancellable: (allow-none) a #GCancellable + * @user_data: (allow-none) user data passed to the callback + * + * Asynchronous version of e_mail_parser_parse_sync(). + */ +void +e_mail_parser_parse (EMailParser *parser, + CamelFolder *folder, + const gchar *message_uid, + CamelMimeMessage *message, + GAsyncReadyCallback callback, + GCancellable *cancellable, + gpointer user_data) +{ + GSimpleAsyncResult *simple; + EMailPartList *part_list; + + g_return_if_fail (E_IS_MAIL_PARSER (parser)); + g_return_if_fail (CAMEL_IS_MIME_MESSAGE (message)); + + part_list = e_mail_part_list_new (message, message_uid, folder); + + simple = g_simple_async_result_new ( + G_OBJECT (parser), callback, + user_data, e_mail_parser_parse); + + g_simple_async_result_set_check_cancellable (simple, cancellable); + + g_simple_async_result_set_op_res_gpointer ( + simple, part_list, (GDestroyNotify) g_object_unref); + + g_simple_async_result_run_in_thread ( + simple, mail_parser_parse_thread, + G_PRIORITY_DEFAULT, cancellable); + + g_object_unref (simple); +} + +EMailPartList * +e_mail_parser_parse_finish (EMailParser *parser, + GAsyncResult *result, + GError **error) +{ + GSimpleAsyncResult *simple; + EMailPartList *part_list; + + g_return_val_if_fail ( + g_simple_async_result_is_valid ( + result, G_OBJECT (parser), e_mail_parser_parse), NULL); + + simple = G_SIMPLE_ASYNC_RESULT (result); + part_list = g_simple_async_result_get_op_res_gpointer (simple); + + if (camel_debug_start ("emformat:parser")) { + GQueue queue = G_QUEUE_INIT; + + printf ( + "%s finished with EMailPartList:\n", + G_OBJECT_TYPE_NAME (parser)); + + e_mail_part_list_queue_parts (part_list, NULL, &queue); + + while (!g_queue_is_empty (&queue)) { + EMailPart *part; + + part = g_queue_pop_head (&queue); + + printf ( + " id: %s | cid: %s | mime_type: %s | " + "is_hidden: %d | is_attachment: %d\n", + e_mail_part_get_id (part), + e_mail_part_get_cid (part), + e_mail_part_get_mime_type (part), + part->is_hidden ? 1 : 0, + e_mail_part_get_is_attachment (part) ? 1 : 0); + + g_object_unref (part); + } + + camel_debug_end (); + } + + return g_object_ref (part_list); +} + +GQueue * +e_mail_parser_get_parsers_for_part (EMailParser *parser, + CamelMimePart *part) +{ + CamelContentType *ct; + gchar *mime_type; + GQueue *parsers; + + g_return_val_if_fail (E_IS_MAIL_PARSER (parser), NULL); + g_return_val_if_fail (CAMEL_IS_MIME_PART (part), NULL); + + ct = camel_mime_part_get_content_type (part); + if (!ct) { + mime_type = (gchar *) "application/vnd.evolution.error"; + } else { + gchar *tmp; + tmp = camel_content_type_simple (ct); + mime_type = g_ascii_strdown (tmp, -1); + g_free (tmp); + } + + parsers = e_mail_parser_get_parsers (parser, mime_type); + + if (ct) + g_free (mime_type); + + return parsers; +} + +GQueue * +e_mail_parser_get_parsers (EMailParser *parser, + const gchar *mime_type) +{ + EMailExtensionRegistry *reg; + EMailParserClass *parser_class; + gchar *as_mime_type; + GQueue *parsers; + + g_return_val_if_fail (E_IS_MAIL_PARSER (parser), NULL); + + parser_class = E_MAIL_PARSER_GET_CLASS (parser); + g_return_val_if_fail (parser_class != NULL, NULL); + + if (mime_type) + as_mime_type = g_ascii_strdown (mime_type, -1); + else + as_mime_type = NULL; + + reg = E_MAIL_EXTENSION_REGISTRY (parser_class->extension_registry); + + parsers = e_mail_extension_registry_get_for_mime_type (reg, as_mime_type); + if (!parsers) + parsers = e_mail_extension_registry_get_fallback (reg, as_mime_type); + + g_free (as_mime_type); + + return parsers; +} + +gboolean +e_mail_parser_parse_part (EMailParser *parser, + CamelMimePart *part, + GString *part_id, + GCancellable *cancellable, + GQueue *out_mail_parts) +{ + CamelContentType *ct; + gchar *mime_type; + gint handled; + + ct = camel_mime_part_get_content_type (part); + if (!ct) { + mime_type = (gchar *) "application/vnd.evolution.error"; + } else { + gchar *tmp; + tmp = camel_content_type_simple (ct); + mime_type = g_ascii_strdown (tmp, -1); + g_free (tmp); + } + + handled = e_mail_parser_parse_part_as ( + parser, part, part_id, mime_type, + cancellable, out_mail_parts); + + if (ct) { + g_free (mime_type); + } + + return handled; +} + +gboolean +e_mail_parser_parse_part_as (EMailParser *parser, + CamelMimePart *part, + GString *part_id, + const gchar *mime_type, + GCancellable *cancellable, + GQueue *out_mail_parts) +{ + GQueue *parsers; + GList *iter; + gboolean mime_part_handled = FALSE; + + parsers = e_mail_parser_get_parsers (parser, mime_type); + + if (parsers == NULL) { + e_mail_parser_wrap_as_attachment ( + parser, part, part_id, out_mail_parts); + return TRUE; + } + + for (iter = parsers->head; iter; iter = iter->next) { + EMailParserExtension *extension; + + extension = iter->data; + if (!extension) + continue; + + mime_part_handled = e_mail_parser_extension_parse ( + extension, parser, part, part_id, + cancellable, out_mail_parts); + + if (mime_part_handled) + break; + } + + return mime_part_handled; +} + +void +e_mail_parser_error (EMailParser *parser, + GQueue *out_mail_parts, + const gchar *format, + ...) +{ + const gchar *mime_type = "application/vnd.evolution.error"; + EMailPart *mail_part; + CamelMimePart *part; + gchar *errmsg; + gchar *uri; + va_list ap; + + g_return_if_fail (E_IS_MAIL_PARSER (parser)); + g_return_if_fail (out_mail_parts != NULL); + g_return_if_fail (format != NULL); + + va_start (ap, format); + errmsg = g_strdup_vprintf (format, ap); + + part = camel_mime_part_new (); + camel_mime_part_set_content ( + part, errmsg, strlen (errmsg), mime_type); + g_free (errmsg); + va_end (ap); + + g_mutex_lock (&parser->priv->mutex); + parser->priv->last_error++; + uri = g_strdup_printf (".error.%d", parser->priv->last_error); + g_mutex_unlock (&parser->priv->mutex); + + mail_part = e_mail_part_new (part, uri); + e_mail_part_set_mime_type (mail_part, mime_type); + mail_part->is_error = TRUE; + + g_free (uri); + g_object_unref (part); + + g_queue_push_tail (out_mail_parts, mail_part); +} + +static void +attachment_loaded (EAttachment *attachment, + GAsyncResult *res, + gpointer user_data) +{ + EShell *shell; + GtkWindow *window; + + shell = e_shell_get_default (); + window = e_shell_get_active_window (shell); + + e_attachment_load_handle_error (attachment, res, window); + + g_object_unref (attachment); +} + +/* Idle callback */ +static gboolean +load_attachment_idle (EAttachment *attachment) +{ + e_attachment_load_async ( + attachment, + (GAsyncReadyCallback) attachment_loaded, NULL); + + return FALSE; +} + +void +e_mail_parser_wrap_as_attachment (EMailParser *parser, + CamelMimePart *part, + GString *part_id, + GQueue *parts_queue) +{ + EMailPartAttachment *empa; + EAttachment *attachment; + EMailPart *first_part; + const gchar *snoop_mime_type; + GQueue *extensions; + CamelContentType *ct; + gchar *mime_type; + CamelDataWrapper *dw; + GByteArray *ba; + gsize size; + gint part_id_len; + + ct = camel_mime_part_get_content_type (part); + extensions = NULL; + snoop_mime_type = NULL; + if (ct) { + EMailExtensionRegistry *reg; + mime_type = camel_content_type_simple (ct); + + reg = e_mail_parser_get_extension_registry (parser); + extensions = e_mail_extension_registry_get_for_mime_type ( + reg, mime_type); + + if (camel_content_type_is (ct, "text", "*") || + camel_content_type_is (ct, "message", "*")) + snoop_mime_type = mime_type; + else + g_free (mime_type); + } + + if (!snoop_mime_type) + snoop_mime_type = e_mail_part_snoop_type (part); + + if (!extensions) { + EMailExtensionRegistry *reg; + + reg = e_mail_parser_get_extension_registry (parser); + extensions = e_mail_extension_registry_get_for_mime_type ( + reg, snoop_mime_type); + + if (!extensions) { + extensions = e_mail_extension_registry_get_fallback ( + reg, snoop_mime_type); + } + } + + part_id_len = part_id->len; + g_string_append (part_id, ".attachment"); + + empa = e_mail_part_attachment_new (part, part_id->str); + empa->shown = extensions && (!g_queue_is_empty (extensions) && + e_mail_part_is_inline (part, extensions)); + empa->snoop_mime_type = snoop_mime_type; + + first_part = g_queue_peek_head (parts_queue); + if (first_part != NULL && !E_IS_MAIL_PART_ATTACHMENT (first_part)) { + const gchar *id = e_mail_part_get_id (first_part); + empa->part_id_with_attachment = g_strdup (id); + first_part->is_hidden = TRUE; + } + + attachment = e_mail_part_attachment_ref_attachment (empa); + + e_attachment_set_initially_shown (attachment, empa->shown); + e_attachment_set_can_show ( + attachment, + extensions && !g_queue_is_empty (extensions)); + + /* Try to guess size of the attachments */ + dw = camel_medium_get_content (CAMEL_MEDIUM (part)); + ba = camel_data_wrapper_get_byte_array (dw); + if (ba) { + size = ba->len; + + if (camel_mime_part_get_encoding (part) == CAMEL_TRANSFER_ENCODING_BASE64) + size = size / 1.37; + } else { + size = 0; + } + + /* e_attachment_load_async must be called from main thread */ + /* Prioritize ahead of GTK+ redraws. */ + g_idle_add_full ( + G_PRIORITY_HIGH_IDLE, + (GSourceFunc) load_attachment_idle, + g_object_ref (attachment), + NULL); + + if (size != 0) { + GFileInfo *file_info; + + file_info = e_attachment_ref_file_info (attachment); + + if (file_info == NULL) { + file_info = g_file_info_new (); + g_file_info_set_content_type ( + file_info, empa->snoop_mime_type); + } + + g_file_info_set_size (file_info, size); + e_attachment_set_file_info (attachment, file_info); + + g_object_unref (file_info); + } + + g_object_unref (attachment); + + g_string_truncate (part_id, part_id_len); + + /* Push to head, not tail. */ + g_queue_push_head (parts_queue, empa); +} + +CamelSession * +e_mail_parser_get_session (EMailParser *parser) +{ + g_return_val_if_fail (E_IS_MAIL_PARSER (parser), NULL); + + return parser->priv->session; +} + +EMailExtensionRegistry * +e_mail_parser_get_extension_registry (EMailParser *parser) +{ + EMailParserClass *parser_class; + + g_return_val_if_fail (E_IS_MAIL_PARSER (parser), NULL); + + parser_class = E_MAIL_PARSER_GET_CLASS (parser); + g_return_val_if_fail (parser_class != NULL, NULL); + + return E_MAIL_EXTENSION_REGISTRY (parser_class->extension_registry); +} diff --git a/src/em-format/e-mail-part.c b/src/em-format/e-mail-part.c index 135005e..03271ce 100644 --- a/src/em-format/e-mail-part.c +++ b/src/em-format/e-mail-part.c @@ -662,6 +662,15 @@ e_mail_part_update_validity (EMailPart *part, mask = E_MAIL_PART_VALIDITY_PGP | E_MAIL_PART_VALIDITY_SMIME; + /* Auto-add flags when the related part is present */ + if (!(validity_type & E_MAIL_PART_VALIDITY_SIGNED) && + validity->sign.status != CAMEL_CIPHER_VALIDITY_SIGN_NONE) + validity_type |= E_MAIL_PART_VALIDITY_SIGNED; + + if (!(validity_type & E_MAIL_PART_VALIDITY_ENCRYPTED) && + validity->encrypt.status != CAMEL_CIPHER_VALIDITY_ENCRYPT_NONE) + validity_type |= E_MAIL_PART_VALIDITY_ENCRYPTED; + pair = mail_part_find_validity_pair (part, validity_type & mask); if (pair != NULL) { pair->validity_type |= validity_type; diff --git a/src/em-format/e-mail-part.c.cve-2018-15587-reposition-signature-bar b/src/em-format/e-mail-part.c.cve-2018-15587-reposition-signature-bar new file mode 100644 index 0000000..135005e --- /dev/null +++ b/src/em-format/e-mail-part.c.cve-2018-15587-reposition-signature-bar @@ -0,0 +1,798 @@ +/* + * e-mail-part.c + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY + * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with this program; if not, see . + * + */ + +/** + * EMailPart: + * + * The #EMailPart is a wrapper around #CamelMimePart which holds additional + * information about the mime part, like it's ID, encryption type etc. + * + * Each #EMailPart must have a unique ID. The ID is a dot-separated + * hierarchical description of the location of the part within the email + * message. + */ + +#include "evolution-config.h" + +#include "e-mail-part.h" + +#include + +#include "e-mail-part-attachment.h" +#include "e-mail-part-list.h" + +#define E_MAIL_PART_GET_PRIVATE(obj) \ + (G_TYPE_INSTANCE_GET_PRIVATE \ + ((obj), E_TYPE_MAIL_PART, EMailPartPrivate)) + +struct _EMailPartPrivate { + GWeakRef part_list; + CamelMimePart *mime_part; + + gchar *id; + gchar *cid; + gchar *mime_type; + + gboolean is_attachment; + gboolean converted_to_utf8; +}; + +enum { + PROP_0, + PROP_CID, + PROP_CONVERTED_TO_UTF8, + PROP_ID, + PROP_IS_ATTACHMENT, + PROP_MIME_PART, + PROP_MIME_TYPE, + PROP_PART_LIST +}; + +G_DEFINE_TYPE_WITH_CODE ( + EMailPart, + e_mail_part, + G_TYPE_OBJECT, + G_IMPLEMENT_INTERFACE ( + E_TYPE_EXTENSIBLE, NULL)) + +static void +mail_part_validity_pair_free (gpointer ptr) +{ + EMailPartValidityPair *pair = ptr; + + if (!pair) + return; + + camel_cipher_validity_free (pair->validity); + g_free (pair); +} + +static void +mail_part_set_id (EMailPart *part, + const gchar *id) +{ + g_return_if_fail (part->priv->id == NULL); + + part->priv->id = g_strdup (id); +} + +static void +mail_part_set_mime_part (EMailPart *part, + CamelMimePart *mime_part) +{ + g_return_if_fail (part->priv->mime_part == NULL); + + /* The CamelMimePart is optional. */ + if (mime_part != NULL) + part->priv->mime_part = g_object_ref (mime_part); +} + +static void +mail_part_set_property (GObject *object, + guint property_id, + const GValue *value, + GParamSpec *pspec) +{ + switch (property_id) { + case PROP_CID: + e_mail_part_set_cid ( + E_MAIL_PART (object), + g_value_get_string (value)); + return; + + case PROP_CONVERTED_TO_UTF8: + e_mail_part_set_converted_to_utf8 ( + E_MAIL_PART (object), + g_value_get_boolean (value)); + return; + + case PROP_ID: + mail_part_set_id ( + E_MAIL_PART (object), + g_value_get_string (value)); + return; + + case PROP_IS_ATTACHMENT: + e_mail_part_set_is_attachment ( + E_MAIL_PART (object), + g_value_get_boolean (value)); + return; + + case PROP_MIME_PART: + mail_part_set_mime_part ( + E_MAIL_PART (object), + g_value_get_object (value)); + return; + + case PROP_MIME_TYPE: + e_mail_part_set_mime_type ( + E_MAIL_PART (object), + g_value_get_string (value)); + return; + + case PROP_PART_LIST: + e_mail_part_set_part_list ( + E_MAIL_PART (object), + g_value_get_object (value)); + return; + } + + G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec); +} + +static void +mail_part_get_property (GObject *object, + guint property_id, + GValue *value, + GParamSpec *pspec) +{ + switch (property_id) { + case PROP_CID: + g_value_set_string ( + value, + e_mail_part_get_cid ( + E_MAIL_PART (object))); + return; + + case PROP_CONVERTED_TO_UTF8: + g_value_set_boolean ( + value, + e_mail_part_get_converted_to_utf8 ( + E_MAIL_PART (object))); + return; + + case PROP_ID: + g_value_set_string ( + value, + e_mail_part_get_id ( + E_MAIL_PART (object))); + return; + + case PROP_IS_ATTACHMENT: + g_value_set_boolean ( + value, + e_mail_part_get_is_attachment ( + E_MAIL_PART (object))); + return; + + case PROP_MIME_PART: + g_value_take_object ( + value, + e_mail_part_ref_mime_part ( + E_MAIL_PART (object))); + return; + + case PROP_MIME_TYPE: + g_value_set_string ( + value, + e_mail_part_get_mime_type ( + E_MAIL_PART (object))); + return; + + case PROP_PART_LIST: + g_value_take_object ( + value, + e_mail_part_ref_part_list ( + E_MAIL_PART (object))); + return; + } + + G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec); +} + +static void +mail_part_dispose (GObject *object) +{ + EMailPartPrivate *priv; + + priv = E_MAIL_PART_GET_PRIVATE (object); + + g_weak_ref_set (&priv->part_list, NULL); + + g_clear_object (&priv->mime_part); + + /* Chain up to parent's dispose() method. */ + G_OBJECT_CLASS (e_mail_part_parent_class)->dispose (object); +} + +static void +mail_part_finalize (GObject *object) +{ + EMailPart *part = E_MAIL_PART (object); + EMailPartValidityPair *pair; + + g_free (part->priv->id); + g_free (part->priv->cid); + g_free (part->priv->mime_type); + + while ((pair = g_queue_pop_head (&part->validities)) != NULL) + mail_part_validity_pair_free (pair); + + /* Chain up to parent's finalize() method. */ + G_OBJECT_CLASS (e_mail_part_parent_class)->finalize (object); +} + +static void +mail_part_constructed (GObject *object) +{ + /* Chain up to parent's constructed() method. */ + G_OBJECT_CLASS (e_mail_part_parent_class)->constructed (object); + + e_extensible_load_extensions (E_EXTENSIBLE (object)); +} + +static void +e_mail_part_class_init (EMailPartClass *class) +{ + GObjectClass *object_class; + + g_type_class_add_private (class, sizeof (EMailPartPrivate)); + + object_class = G_OBJECT_CLASS (class); + object_class->set_property = mail_part_set_property; + object_class->get_property = mail_part_get_property; + object_class->dispose = mail_part_dispose; + object_class->finalize = mail_part_finalize; + object_class->constructed = mail_part_constructed; + + g_object_class_install_property ( + object_class, + PROP_CID, + g_param_spec_string ( + "cid", + "Content ID", + "The MIME Content-ID", + NULL, + G_PARAM_READWRITE | + G_PARAM_STATIC_STRINGS)); + + g_object_class_install_property ( + object_class, + PROP_CONVERTED_TO_UTF8, + g_param_spec_boolean ( + "converted-to-utf8", + "Converted To UTF8", + "Whether the part content was already converted to UTF-8", + FALSE, + G_PARAM_READWRITE | + G_PARAM_STATIC_STRINGS)); + + g_object_class_install_property ( + object_class, + PROP_ID, + g_param_spec_string ( + "id", + "Part ID", + "The part ID", + NULL, + G_PARAM_READWRITE | + G_PARAM_CONSTRUCT_ONLY | + G_PARAM_STATIC_STRINGS)); + + g_object_class_install_property ( + object_class, + PROP_IS_ATTACHMENT, + g_param_spec_boolean ( + "is-attachment", + "Is Attachment", + "Format the part as an attachment", + FALSE, + G_PARAM_READWRITE | + G_PARAM_CONSTRUCT | + G_PARAM_STATIC_STRINGS)); + + g_object_class_install_property ( + object_class, + PROP_MIME_PART, + g_param_spec_object ( + "mime-part", + "MIME Part", + "The MIME part", + CAMEL_TYPE_MIME_PART, + G_PARAM_READWRITE | + G_PARAM_CONSTRUCT_ONLY | + G_PARAM_STATIC_STRINGS)); + + g_object_class_install_property ( + object_class, + PROP_MIME_TYPE, + g_param_spec_string ( + "mime-type", + "MIME Type", + "The MIME type", + NULL, + G_PARAM_READWRITE | + G_PARAM_STATIC_STRINGS)); + + g_object_class_install_property ( + object_class, + PROP_PART_LIST, + g_param_spec_object ( + "part-list", + "Part List", + "The part list that owns the part", + E_TYPE_MAIL_PART_LIST, + G_PARAM_READWRITE | + G_PARAM_STATIC_STRINGS)); +} + +static void +e_mail_part_init (EMailPart *part) +{ + part->priv = E_MAIL_PART_GET_PRIVATE (part); +} + +/** + * e_mail_part_new: + * @mime_part: (allow-none) a #CamelMimePart or %NULL + * @id: part ID + * + * Creates a new #EMailPart for the given @mime_part. + * + * Return value: a new #EMailPart + */ +EMailPart * +e_mail_part_new (CamelMimePart *mime_part, + const gchar *id) +{ + g_return_val_if_fail (id != NULL, NULL); + + return g_object_new ( + E_TYPE_MAIL_PART, + "id", id, "mime-part", mime_part, NULL); +} + +const gchar * +e_mail_part_get_id (EMailPart *part) +{ + g_return_val_if_fail (E_IS_MAIL_PART (part), NULL); + + return part->priv->id; +} + +const gchar * +e_mail_part_get_cid (EMailPart *part) +{ + g_return_val_if_fail (E_IS_MAIL_PART (part), NULL); + + return part->priv->cid; +} + +void +e_mail_part_set_cid (EMailPart *part, + const gchar *cid) +{ + g_return_if_fail (E_IS_MAIL_PART (part)); + + g_free (part->priv->cid); + part->priv->cid = g_strdup (cid); + + g_object_notify (G_OBJECT (part), "cid"); +} + +gboolean +e_mail_part_id_has_prefix (EMailPart *part, + const gchar *prefix) +{ + g_return_val_if_fail (E_IS_MAIL_PART (part), FALSE); + g_return_val_if_fail (prefix != NULL, FALSE); + + return g_str_has_prefix (part->priv->id, prefix); +} + +gboolean +e_mail_part_id_has_suffix (EMailPart *part, + const gchar *suffix) +{ + g_return_val_if_fail (E_IS_MAIL_PART (part), FALSE); + g_return_val_if_fail (suffix != NULL, FALSE); + + return g_str_has_suffix (part->priv->id, suffix); +} + +gboolean +e_mail_part_id_has_substr (EMailPart *part, + const gchar *substr) +{ + g_return_val_if_fail (E_IS_MAIL_PART (part), FALSE); + g_return_val_if_fail (substr != NULL, FALSE); + + return (strstr (part->priv->id, substr) != NULL); +} + +CamelMimePart * +e_mail_part_ref_mime_part (EMailPart *part) +{ + CamelMimePart *mime_part = NULL; + + g_return_val_if_fail (E_IS_MAIL_PART (part), NULL); + + if (part->priv->mime_part != NULL) + mime_part = g_object_ref (part->priv->mime_part); + + return mime_part; +} + +const gchar * +e_mail_part_get_mime_type (EMailPart *part) +{ + g_return_val_if_fail (E_IS_MAIL_PART (part), NULL); + + return part->priv->mime_type; +} + +void +e_mail_part_set_mime_type (EMailPart *part, + const gchar *mime_type) +{ + g_return_if_fail (E_IS_MAIL_PART (part)); + + if (g_strcmp0 (mime_type, part->priv->mime_type) == 0) + return; + + g_free (part->priv->mime_type); + part->priv->mime_type = g_strdup (mime_type); + + g_object_notify (G_OBJECT (part), "mime-type"); +} + +gboolean +e_mail_part_get_converted_to_utf8 (EMailPart *part) +{ + g_return_val_if_fail (E_IS_MAIL_PART (part), FALSE); + + return part->priv->converted_to_utf8; +} + +void +e_mail_part_set_converted_to_utf8 (EMailPart *part, + gboolean converted_to_utf8) +{ + g_return_if_fail (E_IS_MAIL_PART (part)); + + if (converted_to_utf8 == part->priv->converted_to_utf8) + return; + + part->priv->converted_to_utf8 = converted_to_utf8; + + g_object_notify (G_OBJECT (part), "converted-to-utf8"); +} + +gboolean +e_mail_part_should_show_inline (EMailPart *part) +{ + CamelMimePart *mime_part; + const CamelContentDisposition *disposition; + gboolean res = FALSE; + + g_return_val_if_fail (E_IS_MAIL_PART (part), FALSE); + + /* Automatically expand attachments that have inline + * disposition or the EMailParts have specific + * force_inline flag set. */ + + if (part->force_collapse) + return FALSE; + + if (part->force_inline) + return TRUE; + + if (E_IS_MAIL_PART_ATTACHMENT (part)) { + EMailPartAttachment *empa = E_MAIL_PART_ATTACHMENT (part); + + if (g_strcmp0 (empa->snoop_mime_type, "message/rfc822") == 0) + return TRUE; + } + + mime_part = e_mail_part_ref_mime_part (part); + if (!mime_part) + return FALSE; + + disposition = camel_mime_part_get_content_disposition (mime_part); + if (disposition && disposition->disposition && + g_ascii_strncasecmp (disposition->disposition, "inline", 6) == 0) { + GSettings *settings; + + settings = e_util_ref_settings ("org.gnome.evolution.mail"); + res = g_settings_get_boolean (settings, "display-content-disposition-inline"); + g_clear_object (&settings); + } + + g_object_unref (mime_part); + + return res; +} + +EMailPartList * +e_mail_part_ref_part_list (EMailPart *part) +{ + g_return_val_if_fail (E_IS_MAIL_PART (part), NULL); + + return g_weak_ref_get (&part->priv->part_list); +} + +void +e_mail_part_set_part_list (EMailPart *part, + EMailPartList *part_list) +{ + g_return_if_fail (E_IS_MAIL_PART (part)); + + if (part_list != NULL) + g_return_if_fail (E_IS_MAIL_PART_LIST (part_list)); + + g_weak_ref_set (&part->priv->part_list, part_list); + + g_object_notify (G_OBJECT (part), "part-list"); +} + +gboolean +e_mail_part_get_is_attachment (EMailPart *part) +{ + g_return_val_if_fail (E_IS_MAIL_PART (part), FALSE); + + return part->priv->is_attachment; +} + +void +e_mail_part_set_is_attachment (EMailPart *part, + gboolean is_attachment) +{ + g_return_if_fail (E_IS_MAIL_PART (part)); + + if (is_attachment == part->priv->is_attachment) + return; + + part->priv->is_attachment = is_attachment; + + g_object_notify (G_OBJECT (part), "is-attachment"); +} + +void +e_mail_part_bind_dom_element (EMailPart *part, + EWebView *web_view, + guint64 page_id, + const gchar *element_id) +{ + EMailPartClass *class; + + g_return_if_fail (E_IS_MAIL_PART (part)); + g_return_if_fail (E_IS_WEB_VIEW (web_view)); + g_return_if_fail (page_id != 0); + g_return_if_fail (element_id && *element_id); + + class = E_MAIL_PART_GET_CLASS (part); + g_return_if_fail (class != NULL); + + if (class->bind_dom_element != NULL) + class->bind_dom_element (part, web_view, page_id, element_id); +} + +void +e_mail_part_web_view_loaded (EMailPart *part, + EWebView *web_view) +{ + EMailPartClass *klass; + + g_return_if_fail (E_IS_MAIL_PART (part)); + g_return_if_fail (E_IS_WEB_VIEW (web_view)); + + klass = E_MAIL_PART_GET_CLASS (part); + g_return_if_fail (klass != NULL); + + if (klass->web_view_loaded) + klass->web_view_loaded (part, web_view); +} + +static EMailPartValidityPair * +mail_part_find_validity_pair (EMailPart *part, + EMailPartValidityFlags validity_type) +{ + GList *head, *link; + + head = g_queue_peek_head_link (&part->validities); + + for (link = head; link != NULL; link = g_list_next (link)) { + EMailPartValidityPair *pair = link->data; + + if (pair == NULL) + continue; + + if ((pair->validity_type & validity_type) == validity_type) + return pair; + } + + return NULL; +} + +/** + * e_mail_part_update_validity: + * @part: An #EMailPart + * @validity: a #CamelCipherValidity + * @validity_type: E_MAIL_PART_VALIDITY_* flags + * + * Updates validity of the @part. When the part already has some validity + * set, the new @validity and @validity_type are just appended, preserving + * the original validity. Validities of the same type (PGP or S/MIME) are + * merged together. + */ +void +e_mail_part_update_validity (EMailPart *part, + CamelCipherValidity *validity, + EMailPartValidityFlags validity_type) +{ + EMailPartValidityPair *pair; + EMailPartValidityFlags mask; + + g_return_if_fail (E_IS_MAIL_PART (part)); + g_return_if_fail (validity != NULL); + + mask = E_MAIL_PART_VALIDITY_PGP | E_MAIL_PART_VALIDITY_SMIME; + + pair = mail_part_find_validity_pair (part, validity_type & mask); + if (pair != NULL) { + pair->validity_type |= validity_type; + camel_cipher_validity_envelope (pair->validity, validity); + } else { + pair = g_new0 (EMailPartValidityPair, 1); + pair->validity_type = validity_type; + pair->validity = camel_cipher_validity_clone (validity); + + g_queue_push_tail (&part->validities, pair); + } +} + +/** + * e_mail_part_get_validity: + * @part: An #EMailPart + * @validity_type: E_MAIL_PART_VALIDITY_* flags + * + * Returns, validity of @part contains any validity with the same bits + * as @validity_type set. It should contain all bits of it. + * + * Returns: a #CamelCipherValidity of the given type, %NULL if not found + * + * Since: 3.8 + */ +CamelCipherValidity * +e_mail_part_get_validity (EMailPart *part, + EMailPartValidityFlags validity_type) +{ + EMailPartValidityPair *pair; + + g_return_val_if_fail (E_IS_MAIL_PART (part), NULL); + + pair = mail_part_find_validity_pair (part, validity_type); + + return (pair != NULL) ? pair->validity : NULL; +} + +gboolean +e_mail_part_has_validity (EMailPart *part) +{ + g_return_val_if_fail (E_IS_MAIL_PART (part), FALSE); + + return !g_queue_is_empty (&part->validities); +} + +EMailPartValidityFlags +e_mail_part_get_validity_flags (EMailPart *part) +{ + EMailPartValidityFlags flags = 0; + GList *head, *link; + + g_return_val_if_fail (E_IS_MAIL_PART (part), 0); + + head = g_queue_peek_head_link (&part->validities); + + for (link = head; link != NULL; link = g_list_next (link)) { + EMailPartValidityPair *pair = link->data; + + if (pair != NULL) + flags |= pair->validity_type; + } + + return flags; +} + +static gboolean +from_matches_signers_alt_emails (CamelInternetAddress *from_address, + CamelCipherCertInfo *cinfo) +{ + GSList *props_link; + gboolean matches = FALSE; + + for (props_link = cinfo->properties; props_link && !matches; props_link = g_slist_next (props_link)) { + const CamelCipherCertInfoProperty *prop = props_link->data; + + if (prop && g_strcmp0 (prop->name, CAMEL_CIPHER_CERT_INFO_PROPERTY_SIGNERS_ALT_EMAILS) == 0 && prop->value) { + CamelInternetAddress *address; + gint count, ii; + + address = camel_internet_address_new (); + count = camel_address_unformat (CAMEL_ADDRESS (address), prop->value); + for (ii = 0; ii < count && !matches; ii++) { + const gchar *email = NULL; + + if (camel_internet_address_get (address, ii, NULL, &email) && email && *email) { + matches = camel_internet_address_find_address (from_address, email, NULL) >= 0; + } + } + g_object_unref (address); + break; + } + } + + return matches; +} + +void +e_mail_part_verify_validity_sender (EMailPart *part, + CamelInternetAddress *from_address) +{ + GList *link; + + g_return_if_fail (E_IS_MAIL_PART (part)); + + if (!from_address) + return; + + for (link = g_queue_peek_head_link (&part->validities); link; link = g_list_next (link)) { + EMailPartValidityPair *pair = link->data; + + if (pair && pair->validity && !(pair->validity_type & E_MAIL_PART_VALIDITY_VERIFIED)) { + pair->validity_type |= E_MAIL_PART_VALIDITY_VERIFIED; + + if (pair->validity->sign.status != CAMEL_CIPHER_VALIDITY_SIGN_NONE) { + GList *link2; + gboolean from_matches_signer = FALSE; + + for (link2 = g_queue_peek_head_link (&pair->validity->sign.signers); link2 && !from_matches_signer; link2 = g_list_next (link2)) { + CamelCipherCertInfo *cinfo = link2->data; + + if (cinfo->email && *cinfo->email) { + from_matches_signer = from_matches_signer || + (from_address && camel_internet_address_find_address (from_address, cinfo->email, NULL) >= 0) || + (from_address && from_matches_signers_alt_emails (from_address, cinfo)); + } + } + + if (!from_matches_signer) + pair->validity_type |= E_MAIL_PART_VALIDITY_SENDER_SIGNER_MISMATCH; + } + } + } +}