/* * * 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 * * * Authors: * Jeffrey Stedfast * * Copyright (C) 1999-2008 Novell, Inc. (www.novell.com) * */ #ifdef HAVE_CONFIG_H #include #endif #include #include #include #include #include #include #include #include #include #include #include "em-stripsig-filter.h" #include "em-format-quote.h" struct _EMFormatQuotePrivate { gint dummy; }; static void emfq_format_clone(EMFormat *, CamelFolder *, const gchar *, CamelMimeMessage *, EMFormat *); static void emfq_format_error(EMFormat *emf, CamelStream *stream, const gchar *txt); static void emfq_format_message(EMFormat *, CamelStream *, CamelMimePart *, const EMFormatHandler *); static void emfq_format_source(EMFormat *, CamelStream *, CamelMimePart *); static void emfq_format_attachment(EMFormat *, CamelStream *, CamelMimePart *, const gchar *, const EMFormatHandler *); static void emfq_builtin_init(EMFormatQuoteClass *efhc); static gpointer parent_class; static void emfq_init(GObject *o) { EMFormatQuote *emfq =(EMFormatQuote *) o; /* we want to convert url's etc */ emfq->text_html_flags = CAMEL_MIME_FILTER_TOHTML_PRE | CAMEL_MIME_FILTER_TOHTML_CONVERT_URLS | CAMEL_MIME_FILTER_TOHTML_CONVERT_ADDRESSES; } static void emfq_finalize (GObject *object) { EMFormatQuote *emfq =(EMFormatQuote *) object; if (emfq->stream) camel_object_unref(emfq->stream); g_free(emfq->credits); /* Chain up to parent's finalize() method. */ G_OBJECT_CLASS (parent_class)->finalize (object); } static void emfq_base_init(EMFormatQuoteClass *emfqclass) { emfq_builtin_init(emfqclass); } static void emfq_class_init (EMFormatQuoteClass *class) { GObjectClass *object_class; EMFormatClass *format_class; parent_class = g_type_class_peek_parent (class); g_type_class_add_private (class, sizeof (EMFormatQuotePrivate)); object_class = G_OBJECT_CLASS (class); object_class->finalize = emfq_finalize; format_class = EM_FORMAT_CLASS (class); format_class->format_clone = emfq_format_clone; format_class->format_error = emfq_format_error; format_class->format_source = emfq_format_source; format_class->format_attachment = emfq_format_attachment; } GType em_format_quote_get_type (void) { static GType type = 0; if (G_UNLIKELY (type == 0)) { static const GTypeInfo type_info = { sizeof (EMFormatQuoteClass), (GBaseInitFunc) emfq_base_init, (GBaseFinalizeFunc) NULL, (GClassInitFunc) emfq_class_init, (GClassFinalizeFunc) NULL, NULL, /* class_data */ sizeof (EMFormatQuote), 0, /* n_preallocs */ (GInstanceInitFunc) emfq_init, NULL /* value_table */ }; type = g_type_register_static ( EM_TYPE_FORMAT, "EMFormatQuote", &type_info, 0); } return type; } EMFormatQuote * em_format_quote_new (const gchar *credits, CamelStream *stream, guint32 flags) { EMFormatQuote *emfq; emfq = g_object_new (EM_TYPE_FORMAT_QUOTE, NULL); emfq->credits = g_strdup (credits); emfq->stream = stream; camel_object_ref (stream); emfq->flags = flags; return emfq; } static void emfq_format_empty_line(EMFormat *emf, CamelStream *stream, CamelMimePart *part, const EMFormatHandler *info) { camel_stream_printf(stream, "
\n"); } static void emfq_format_clone(EMFormat *emf, CamelFolder *folder, const gchar *uid, CamelMimeMessage *msg, EMFormat *src) { EMFormatQuote *emfq = (EMFormatQuote *) emf; const EMFormatHandler *handle; GConfClient *gconf; /* Chain up to parent's format_clone() method. */ EM_FORMAT_CLASS (parent_class)->format_clone (emf, folder, uid, msg, src); gconf = gconf_client_get_default (); camel_stream_reset(emfq->stream); if (gconf_client_get_bool(gconf, "/apps/evolution/mail/composer/top_signature", NULL)) emfq_format_empty_line(emf, emfq->stream, (CamelMimePart *)msg, NULL); g_object_unref (gconf); handle = em_format_find_handler(emf, "x-evolution/message/prefix"); if (handle) handle->handler(emf, emfq->stream, (CamelMimePart *)msg, handle); handle = em_format_find_handler(emf, "x-evolution/message/rfc822"); if (handle) handle->handler(emf, emfq->stream, (CamelMimePart *)msg, handle); camel_stream_flush(emfq->stream); g_signal_emit_by_name(emf, "complete"); } static void emfq_format_error(EMFormat *emf, CamelStream *stream, const gchar *txt) { /* FIXME: should we even bother writing error text for quoting? probably not... */ } static void emfq_format_text_header (EMFormatQuote *emfq, CamelStream *stream, const gchar *label, const gchar *value, guint32 flags, gint is_html) { const gchar *html; gchar *mhtml = NULL; if (value == NULL) return; while (*value == ' ') value++; if (!is_html) html = mhtml = camel_text_to_html (value, 0, 0); else html = value; if (flags & EM_FORMAT_HEADER_BOLD) camel_stream_printf (stream, "%s: %s
", label, html); else camel_stream_printf (stream, "%s: %s
", label, html); g_free (mhtml); } 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 }; #if 0 /* FIXME: include Sender and Resent-* headers too? */ /* For Translators only: The following strings are used in the header table in the preview pane */ static gchar *i18n_hdrs[] = { N_("From"), N_("Reply-To"), N_("To"), N_("Cc"), N_("Bcc") }; #endif static void emfq_format_address (GString *out, struct _camel_header_address *a) { guint32 flags = CAMEL_MIME_FILTER_TOHTML_CONVERT_SPACES; gchar *name, *mailto, *addr; 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; g_string_append_printf (out, "%s <", name); /* 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); g_string_append_printf (out, "%s", mailto, addr); g_free (mailto); g_free (addr); if (name && *name) g_string_append (out, ">"); break; case CAMEL_HEADER_ADDRESS_GROUP: g_string_append_printf (out, "%s: ", name); emfq_format_address (out, a->v.members); g_string_append_printf (out, ";"); break; default: g_warning ("Invalid address type"); break; } g_free (name); a = a->next; if (a) g_string_append (out, ", "); } } static void 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++; } } static void emfq_format_header (EMFormat *emf, CamelStream *stream, CamelMedium *part, const gchar *namein, guint32 flags, const gchar *charset) { CamelMimeMessage *msg = (CamelMimeMessage *) part; EMFormatQuote *emfq = (EMFormatQuote *) emf; gchar *name, *buf, *value = NULL; const gchar *txt, *label; gboolean addrspec = FALSE; gint is_html = FALSE; gint i; name = g_alloca (strlen (namein) + 1); strcpy (name, namein); 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; if (!(txt = camel_medium_get_header (part, name))) return; buf = camel_header_unfold (txt); if (!(addrs = camel_header_address_decode (txt, emf->charset ? emf->charset : emf->default_charset))) { g_free (buf); return; } g_free (buf); html = g_string_new (""); emfq_format_address (html, addrs); camel_header_address_unref (addrs); txt = value = html->str; g_string_free (html, FALSE); flags |= EM_FORMAT_HEADER_BOLD; is_html = TRUE; } else if (!strcmp (name, "Subject")) { txt = camel_mime_message_get_subject (msg); label = _("Subject"); flags |= EM_FORMAT_HEADER_BOLD; } else if (!strcmp (name, "X-Evolution-Mailer")) { /* pseudo-header */ if (!(txt = camel_medium_get_header (part, "x-mailer"))) if (!(txt = camel_medium_get_header (part, "user-agent"))) if (!(txt = camel_medium_get_header (part, "x-newsreader"))) if (!(txt = camel_medium_get_header (part, "x-mimeole"))) return; txt = value = camel_header_format_ctext (txt, charset); label = _("Mailer"); flags |= EM_FORMAT_HEADER_BOLD; } else if (!strcmp (name, "Date") || !strcmp (name, "Resent-Date")) { if (!(txt = camel_medium_get_header (part, name))) return; flags |= EM_FORMAT_HEADER_BOLD; } else { txt = camel_medium_get_header (part, name); buf = camel_header_unfold (txt); txt = value = camel_header_decode_string (txt, charset); g_free (buf); } emfq_format_text_header (emfq, stream, label, txt, flags, is_html); g_free (value); } static void emfq_format_headers (EMFormatQuote *emfq, CamelStream *stream, CamelMedium *part) { EMFormat *emf = (EMFormat *) emfq; CamelContentType *ct; const gchar *charset; EMFormatHeader *h; if (!part) return; ct = camel_mime_part_get_content_type ((CamelMimePart *) part); charset = camel_content_type_param (ct, "charset"); charset = camel_iconv_charset_name (charset); /* dump selected headers */ h = (EMFormatHeader *) emf->header_list.head; while (h->next) { emfq_format_header (emf, stream, part, h->name, h->flags, charset); h = h->next; } camel_stream_printf(stream, "
\n"); } static void emfq_format_message_prefix(EMFormat *emf, CamelStream *stream, CamelMimePart *part, const EMFormatHandler *info) { EMFormatQuote *emfq = (EMFormatQuote *) emf; if (emfq->credits) camel_stream_printf(stream, "%s
\n", emfq->credits); } static void emfq_format_message(EMFormat *emf, CamelStream *stream, CamelMimePart *part, const EMFormatHandler *info) { EMFormatQuote *emfq = (EMFormatQuote *) emf; if (emfq->flags & EM_FORMAT_QUOTE_CITE) camel_stream_printf(stream, "\n" "
\n"); if (((CamelMimePart *)emf->message) != part) { camel_stream_printf(stream, "%s
\n", _("-------- Forwarded Message --------")); emfq_format_headers (emfq, stream, (CamelMedium *)part); } else if (emfq->flags & EM_FORMAT_QUOTE_HEADERS) emfq_format_headers (emfq, stream, (CamelMedium *)part); em_format_part (emf, stream, part); if (emfq->flags & EM_FORMAT_QUOTE_CITE) camel_stream_write_string(stream, "
"); } static void emfq_format_source(EMFormat *emf, CamelStream *stream, CamelMimePart *part) { CamelStreamFilter *filtered_stream; CamelMimeFilter *html_filter; filtered_stream = camel_stream_filter_new_with_stream ((CamelStream *) stream); html_filter = camel_mime_filter_tohtml_new (CAMEL_MIME_FILTER_TOHTML_CONVERT_NL | CAMEL_MIME_FILTER_TOHTML_CONVERT_SPACES | CAMEL_MIME_FILTER_TOHTML_ESCAPE_8BIT, 0); camel_stream_filter_add(filtered_stream, html_filter); camel_object_unref(html_filter); em_format_format_text(emf, (CamelStream *)filtered_stream, (CamelDataWrapper *)part); camel_object_unref(filtered_stream); } static void emfq_format_attachment(EMFormat *emf, CamelStream *stream, CamelMimePart *part, const gchar *mime_type, const EMFormatHandler *handle) { if (handle && em_format_is_inline(emf, emf->part_id->str, part, handle)) { gchar *text, *html; camel_stream_write_string(stream, "
\n"); /* output some info about it */ text = em_format_describe_part(part, mime_type); html = camel_text_to_html(text, ((EMFormatQuote *)emf)->text_html_flags & CAMEL_MIME_FILTER_TOHTML_CONVERT_URLS, 0); camel_stream_write_string(stream, html); g_free(html); g_free(text); camel_stream_write_string(stream, "
"); handle->handler(emf, stream, part, handle); } } #include #include #include #include static void emfq_text_plain(EMFormatQuote *emfq, CamelStream *stream, CamelMimePart *part, EMFormatHandler *info) { CamelStreamFilter *filtered_stream; CamelMimeFilter *html_filter; CamelMimeFilter *sig_strip; CamelContentType *type; const gchar *format; guint32 rgb = 0x737373, flags; if (!part) return; flags = emfq->text_html_flags; /* Check for RFC 2646 flowed text. */ type = camel_mime_part_get_content_type(part); if (camel_content_type_is(type, "text", "plain") && (format = camel_content_type_param(type, "format")) && !g_ascii_strcasecmp(format, "flowed")) flags |= CAMEL_MIME_FILTER_TOHTML_FORMAT_FLOWED; filtered_stream = camel_stream_filter_new_with_stream(stream); if (emfq->flags != 0) { sig_strip = em_stripsig_filter_new (); camel_stream_filter_add (filtered_stream, sig_strip); camel_object_unref (sig_strip); } html_filter = camel_mime_filter_tohtml_new(flags, rgb); camel_stream_filter_add(filtered_stream, html_filter); camel_object_unref(html_filter); em_format_format_text((EMFormat *)emfq, (CamelStream *)filtered_stream, (CamelDataWrapper *)part); camel_stream_flush((CamelStream *)filtered_stream); camel_object_unref(filtered_stream); } static void emfq_text_enriched(EMFormatQuote *emfq, CamelStream *stream, CamelMimePart *part, EMFormatHandler *info) { CamelStreamFilter *filtered_stream; CamelMimeFilter *enriched; CamelDataWrapper *dw; guint32 flags = 0; dw = camel_medium_get_content_object((CamelMedium *)part); if (!strcmp(info->mime_type, "text/richtext")) { flags = CAMEL_MIME_FILTER_ENRICHED_IS_RICHTEXT; camel_stream_write_string(stream, "\n\n"); } else { camel_stream_write_string(stream, "\n\n"); } enriched = camel_mime_filter_enriched_new(flags); filtered_stream = camel_stream_filter_new_with_stream (stream); camel_stream_filter_add(filtered_stream, enriched); camel_object_unref(enriched); camel_stream_write_string(stream, "


"); em_format_format_text((EMFormat *)emfq, (CamelStream *)filtered_stream, (CamelDataWrapper *)part); camel_object_unref(filtered_stream); } static void emfq_text_html(EMFormat *emf, CamelStream *stream, CamelMimePart *part, EMFormatHandler *info) { camel_stream_write_string(stream, "\n\n"); em_format_format_text(emf, stream, (CamelDataWrapper *)part); } static void emfq_ignore(EMFormat *emf, CamelStream *stream, CamelMimePart *part, EMFormatHandler *info) { /* NOOP */ } static EMFormatHandler type_builtin_table[] = { { (gchar *) "text/plain", (EMFormatFunc)emfq_text_plain }, { (gchar *) "text/enriched", (EMFormatFunc)emfq_text_enriched }, { (gchar *) "text/richtext", (EMFormatFunc)emfq_text_enriched }, { (gchar *) "text/html", (EMFormatFunc)emfq_text_html }, /* { (gchar *) "multipart/related",(EMFormatFunc)emfq_multipart_related },*/ { (gchar *) "message/external-body", (EMFormatFunc)emfq_ignore }, { (gchar *) "multipart/appledouble", (EMFormatFunc)emfq_ignore }, /* internal evolution types */ { (gchar *) "x-evolution/evolution-rss-feed", (EMFormatFunc)emfq_text_html }, { (gchar *) "x-evolution/message/rfc822", (EMFormatFunc)emfq_format_message }, { (gchar *) "x-evolution/message/prefix", (EMFormatFunc)emfq_format_message_prefix }, }; static void emfq_builtin_init(EMFormatQuoteClass *efhc) { gint i; for (i=0;i