/* * 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; either * version 2 of the License, or (at your option) version 3. * * 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 * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with the program; if not, see * */ #ifdef HAVE_CONFIG #include #endif #include "e-mail-formatter-utils.h" #include #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) { const gchar *id = NULL; if (strcmp (field, _("To")) == 0) { id = "to"; } else if (strcmp (field, _("Cc")) == 0) { id = "cc"; } else if (strcmp (field, _("Bcc")) == 0) { id = "bcc"; } if (id != NULL) { g_string_append_printf ( out, "", id); str = g_strdup_printf ( "", EVOLUTION_IMAGESDIR, id); } } } if (elipsize && str) { const gchar *id = NULL; if (strcmp (field, _("To")) == 0) { id = "to"; } else if (strcmp (field, _("Cc")) == 0) { id = "cc"; } else if (strcmp (field, _("Bcc")) == 0) { id = "bcc"; } if (id != NULL) { g_string_append_printf ( out, "" "...", id); } } 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 >= '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_str_equal (canon_name, addrspec_hdrs[i])) { addrspec = TRUE; break; } } label = _(canon_name); if (addrspec) { struct _camel_header_address *addrs; GString *html; gchar *img; gchar *charset; charset = e_mail_formatter_dup_charset (formatter); if (charset == NULL) charset = e_mail_formatter_dup_default_charset (formatter); buf = camel_header_unfold (header_value); addrs = camel_header_address_decode (buf, charset); if (addrs == NULL) { g_free (charset); g_free (buf); return; } g_free (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")) { struct _camel_header_newsgroup *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) { if (flags & E_MAIL_FORMATTER_HEADER_FLAG_NOLINKS) g_string_append_printf ( html, "%s", scan->newsgroup); else g_string_append_printf ( html, "%s", scan->newsgroup, scan->newsgroup); scan = scan->next; if (scan) g_string_append_printf (html, ", "); } camel_header_newsgroups_free (ng); txt = 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, "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; pos = strstr (label, "_"); if (pos != NULL) { gchar ak = pos[1]; /* Convert to uppercase */ if (ak >= 'a') ak = ak - 32; html_label = g_string_new (""); g_string_append_len (html_label, label, pos - label); g_string_append_printf (html_label, "%c", pos[1]); g_string_append (html_label, &pos[2]); if (out_access_key != NULL && ak != '\0') *out_access_key = g_strdup_printf ("%c", ak); } else { html_label = g_string_new (label); } return g_string_free (html_label, FALSE); }