aboutsummaryrefslogtreecommitdiffstats
path: root/em-format/e-mail-formatter-utils.c
diff options
context:
space:
mode:
authorDan Vrátil <dvratil@redhat.com>2012-06-06 21:27:19 +0800
committerDan Vrátil <dvratil@redhat.com>2012-06-06 21:27:19 +0800
commit5b8340563c271fb684a88c6e5bb6dd3bfb629058 (patch)
treec1c7d606fb4ce9fd2fe459a9226bfb9125423991 /em-format/e-mail-formatter-utils.c
parent26a4f24188fd89dbabaff192bec9c54af8fe5a80 (diff)
downloadgsoc2013-evolution-5b8340563c271fb684a88c6e5bb6dd3bfb629058.tar
gsoc2013-evolution-5b8340563c271fb684a88c6e5bb6dd3bfb629058.tar.gz
gsoc2013-evolution-5b8340563c271fb684a88c6e5bb6dd3bfb629058.tar.bz2
gsoc2013-evolution-5b8340563c271fb684a88c6e5bb6dd3bfb629058.tar.lz
gsoc2013-evolution-5b8340563c271fb684a88c6e5bb6dd3bfb629058.tar.xz
gsoc2013-evolution-5b8340563c271fb684a88c6e5bb6dd3bfb629058.tar.zst
gsoc2013-evolution-5b8340563c271fb684a88c6e5bb6dd3bfb629058.zip
Mail formatter rewrite
All mail-parsing and formatting code has been moved to em-format. Parsing is handeled by EMailParser class, formatting by EMailFormatter. Both classes have registry which hold extensions - simple classes that do actual parsing and formatting. Each supported mime-type has it's own parser and formatter extension class.
Diffstat (limited to 'em-format/e-mail-formatter-utils.c')
-rw-r--r--em-format/e-mail-formatter-utils.c434
1 files changed, 434 insertions, 0 deletions
diff --git a/em-format/e-mail-formatter-utils.c b/em-format/e-mail-formatter-utils.c
new file mode 100644
index 0000000000..12ee042c90
--- /dev/null
+++ b/em-format/e-mail-formatter-utils.c
@@ -0,0 +1,434 @@
+/*
+ * 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 <http://www.gnu.org/licenses/>
+ *
+ */
+
+#ifdef HAVE_CONFIG
+#include <config.h>
+#endif
+
+#include "e-mail-formatter-utils.h"
+
+#include <camel/camel.h>
+
+#include <libemail-engine/e-mail-utils.h>
+#include <libemail-engine/mail-config.h>
+#include <e-util/e-util.h>
+#include <e-util/e-datetime-format.h>
+#include <libedataserver/libedataserver.h>
+
+#include <glib/gi18n.h>
+
+#include <string.h>
+
+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)
+{
+ const gchar *fmt, *html;
+ gchar *mhtml = NULL;
+ gboolean is_rtl;
+
+ if (value == NULL)
+ return;
+
+ while (*value == ' ')
+ value++;
+
+ if (!(flags & E_MAIL_FORMATTER_HEADER_FLAG_HTML))
+ html = mhtml = camel_text_to_html (value,
+ e_mail_formatter_get_text_format_flags (formatter), 0);
+ else
+ html = value;
+
+ is_rtl = gtk_widget_get_default_direction () == GTK_TEXT_DIR_RTL;
+
+ if (flags & E_MAIL_FORMATTER_HEADER_FLAG_NOCOLUMNS) {
+ if (flags & E_MAIL_FORMATTER_HEADER_FLAG_BOLD) {
+ fmt = "<tr class=\"header-item\" style=\"display: %s\"><td><b>%s:</b> %s</td></tr>";
+ } else {
+ fmt = "<tr class=\"header-item\" style=\"display: %s\"><td>%s: %s</td></tr>";
+ }
+ } else if (flags & E_MAIL_FORMATTER_HEADER_FLAG_NODEC) {
+ if (is_rtl)
+ fmt = "<tr class=\"header-item rtl\" style=\"display: %s\"><td align=\"right\" valign=\"top\" width=\"100%%\">%2$s</td><th valign=top align=\"left\" nowrap>%1$s<b>&nbsp;</b></th></tr>";
+ else
+ fmt = "<tr class=\"header-item\" style=\"display: %s\"><th align=\"right\" valign=\"top\" nowrap>%s<b>&nbsp;</b></th><td valign=top>%s</td></tr>";
+ } else {
+ if (flags & E_MAIL_FORMATTER_HEADER_FLAG_BOLD) {
+ if (is_rtl)
+ fmt = "<tr class=\"header-item rtl\" style=\"display: %s\"><td align=\"right\" valign=\"top\" width=\"100%%\">%2$s</td><th align=\"left\" nowrap>%1$s:<b>&nbsp;</b></th></tr>";
+ else
+ fmt = "<tr class=\"header-item\" style=\"display: %s\"><th align=\"right\" valign=\"top\" nowrap>%s:<b>&nbsp;</b></th><td>%s</td></tr>";
+ } else {
+ if (is_rtl)
+ fmt = "<tr class=\"header-item rtl\" style=\"display: %s\"><td align=\"right\" valign=\"top\" width=\"100%\">%2$s</td><td align=\"left\" nowrap>%1$s:<b>&nbsp;</b></td></tr>";
+ else
+ fmt = "<tr class=\"header-item\" style=\"display: %s\"><td align=\"right\" valign=\"top\" nowrap>%s:<b>&nbsp;</b></td><td>%s</td></tr>";
+ }
+ }
+
+ g_string_append_printf (buffer, fmt,
+ (flags & E_MAIL_FORMATTER_HEADER_FLAG_HIDDEN ? "none" : "table-row"), label, html);
+
+ g_free (mhtml);
+}
+
+gchar *
+e_mail_formatter_format_address (EMailFormatter *formatter,
+ GString *out,
+ struct _camel_header_address *a,
+ gchar *field,
+ gboolean no_links,
+ gboolean elipsize)
+{
+ guint32 flags = CAMEL_MIME_FILTER_TOHTML_CONVERT_SPACES;
+ gchar *name, *mailto, *addr;
+ gint i = 0;
+ gchar *str = NULL;
+ gint limit = mail_config_get_address_count ();
+
+ while (a) {
+ 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 && *name) {
+ gchar *real, *mailaddr;
+
+ if (strchr (a->name, ',') || strchr (a->name, ';'))
+ g_string_append_printf (out, "&quot;%s&quot;", name);
+ else
+ g_string_append (out, name);
+
+ g_string_append (out, " &lt;");
+
+ /* 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, "<a href=\"mailto:%s\">%s</a>", mailto, addr);
+ g_free (mailto);
+ g_free (addr);
+
+ if (name && *name)
+ g_string_append (out, "&gt;");
+ 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)
+ g_string_append (out, ", ");
+
+ if (!elipsize)
+ continue;
+
+ /* Let us add a '...' if we have more addresses */
+ if (limit > 0 && (i == limit - 1)) {
+ 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) {
+ g_string_append_printf (out,
+ "<span id=\"__evo-moreaddr-%s\" "
+ "style=\"display: none;\">", id);
+ str = g_strdup_printf (
+ "<img src=\"evo-file://%s/plus.png\" "
+ "id=\"__evo-moreaddr-img-%s\" class=\"navigable\">",
+ 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) {
+ g_string_append_printf (out,
+ "</span>"
+ "<span class=\"navigable\" "
+ "id=\"__evo-moreaddr-ellipsis-%s\" "
+ "style=\"display: inline;\">...</span>",
+ id);
+ }
+ }
+
+ return str;
+}
+
+void
+e_mail_formatter_canon_header_name (gchar *name)
+{
+ gchar *inptr = name;
+
+ /* 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,
+ CamelMedium *part,
+ struct _camel_header_raw *header,
+ guint32 flags,
+ const gchar *charset)
+{
+ gchar *name, *buf, *value = NULL;
+ const gchar *label, *txt;
+ gboolean addrspec = FALSE;
+ gchar *str_field = NULL;
+ gint i;
+
+ name = g_alloca (strlen (header->name) + 1);
+ strcpy (name, header->name);
+ e_mail_formatter_canon_header_name (name);
+
+ for (i = 0; addrspec_hdrs[i]; i++) {
+ if (!strcmp (name, addrspec_hdrs[i])) {
+ addrspec = TRUE;
+ break;
+ }
+ }
+
+ label = _(name);
+
+ if (addrspec) {
+ struct _camel_header_address *addrs;
+ GString *html;
+ gchar *img;
+ const gchar *charset = e_mail_formatter_get_charset (formatter) ?
+ e_mail_formatter_get_charset (formatter) :
+ e_mail_formatter_get_default_charset (formatter);
+
+ buf = camel_header_unfold (header->value);
+ if (!(addrs = camel_header_address_decode (buf, charset))) {
+ g_free (buf);
+ return;
+ }
+
+ g_free (buf);
+
+ html = g_string_new("");
+ img = e_mail_formatter_format_address (formatter, html, addrs, (gchar *) label,
+ (flags & E_MAIL_FORMATTER_HEADER_FLAG_NOLINKS),
+ !(flags & E_MAIL_FORMATTER_HEADER_FLAG_NOELIPSIZE));
+
+ if (img) {
+ str_field = g_strdup_printf ("%s%s:", img, label);
+ 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 | E_MAIL_FORMATTER_HEADER_FLAG_BOLD;
+ } else if (!strcmp (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 (!strcmp(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 (!strcmp (name, "Date") || !strcmp (name, "Resent-Date")) {
+ 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++;
+
+ html = camel_text_to_html (txt,
+ e_mail_formatter_get_text_format_flags (formatter), 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 (<I>%s</I>)", html, date_str);
+ g_free (date_str);
+ }
+ g_free (html);
+ }
+ flags |= E_MAIL_FORMATTER_HEADER_FLAG_HTML |
+ E_MAIL_FORMATTER_HEADER_FLAG_BOLD;
+ } else if (!strcmp(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, "<a href=\"news:%s\">%s</a>",
+ 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 |
+ E_MAIL_FORMATTER_HEADER_FLAG_BOLD;
+ } else if (!strcmp (name, "Received") || !strncmp (name, "X-", 2)) {
+ /* 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);
+}
+
+GSList *
+e_mail_formatter_find_rfc822_end_iter (GSList *iter)
+{
+ EMailPart *part;
+ gchar *end;
+
+ part = iter->data;
+ end = g_strconcat (part->id, ".end", NULL);
+ for (; iter != NULL; iter = g_slist_next (iter)) {
+ part = iter->data;
+ if (!part)
+ continue;
+
+ if (g_strcmp0 (part->id, end) == 0) {
+ g_free (end);
+ return iter;
+ }
+ }
+ g_free (end);
+ return iter;
+}