/*
*
* 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/>
*
*
* Authors:
* Jeffrey Stedfast <fejj@ximian.com>
*
* Copyright (C) 1999-2008 Novell, Inc. (www.novell.com)
*
*/
#ifdef HAVE_CONFIG_H
#include <config.h>
#endif
#include <string.h>
#include <glib/gi18n.h>
#include <gtk/gtk.h>
#include "em-format-html-print.h"
#include "em-format-html-display.h"
#include "e-mail-attachment-bar.h"
#include <e-util/e-print.h>
#include <e-util/e-util.h>
#include <widgets/misc/e-attachment-store.h>
#include <libemail-engine/mail-ops.h>
#include "em-format-html-print.h"
#define d(x)
static gpointer parent_class = NULL;
struct _EMFormatHTMLPrintPrivate {
EMFormatHTML *original_formatter;
EMFormatPURI *top_level_puri;
/* List of attachment PURIs */
GList *attachments;
};
enum {
PROP_0,
PROP_ORIGINAL_FORMATTER
};
static void efhp_write_print_layout (EMFormat *emf, EMFormatPURI *puri, CamelStream *stream, EMFormatWriterInfo *info, GCancellable *cancellable);
static void efhp_write_headers (EMFormat *emf, EMFormatPURI *puri, CamelStream *stream, EMFormatWriterInfo *info, GCancellable *cancellable);
static void efhp_write_inline_attachment (EMFormat *emf, EMFormatPURI *puri, CamelStream *stream, EMFormatWriterInfo *info, GCancellable *cancellable);
static void
efhp_write_attachments_list (EMFormatHTMLPrint *efhp,
CamelStream *stream,
EMFormatWriterInfo *info,
GCancellable *cancellable)
{
GString *str;
GList *iter;
if (!efhp->priv->attachments)
return;
str = g_string_new (
"<table border=\"0\" cellspacing=\"5\" cellpadding=\"0\" "
"class=\"attachments-list\" >\n");
g_string_append_printf (str,
"<tr><th colspan=\"2\"><h1>%s</h1></td></tr>\n"
"<tr><th>%s</th><th>%s</th></tr>\n",
_("Attachments"), _("Name"), _("Size"));
for (iter = efhp->priv->attachments; iter; iter = iter->next) {
EMFormatPURI *puri = iter->data;
EAttachment *attachment;
GFileInfo *fi;
gchar *name, *size;
GByteArray *ba;
CamelDataWrapper *dw;
attachment = ((EMFormatAttachmentPURI *) puri)->attachment;
fi = e_attachment_get_file_info (attachment);
if (!fi)
continue;
if (e_attachment_get_description (attachment) &&
*e_attachment_get_description (attachment)) {
name = g_strdup_printf ("%s (%s)",
e_attachment_get_description (attachment),
g_file_info_get_display_name (fi));
} else {
name = g_strdup (g_file_info_get_display_name (fi));
}
dw = camel_medium_get_content ((CamelMedium *) puri->part);
ba = camel_data_wrapper_get_byte_array (dw);
size = g_format_size (ba->len);
g_string_append_printf (str, "<tr><td>%s</td><td>%s</td></tr>\n",
name, size);
g_free (name);
g_free (size);
}
g_string_append (str, "</table>\n");
camel_stream_write_string (stream, str->str, cancellable, NULL);
g_string_free (str, TRUE);
}
static void
efhp_write_headers (EMFormat *emf,
EMFormatPURI *puri,
CamelStream *stream,
EMFormatWriterInfo *info,
GCancellable *cancellable)
{
struct _camel_header_raw raw_header;
GString *str, *tmp;
gchar *subject;
const gchar *buf;
EMFormatPURI *p;
GList *iter;
gint attachments_count;
gchar *puri_prefix;
buf = camel_medium_get_header (CAMEL_MEDIUM (puri->part), "subject");
subject = camel_header_decode_string (buf, "UTF-8");
str = g_string_new ("<table border=\"0\" cellspacing=\"5\" " \
"cellpadding=\"0\" class=\"printing-header\">\n");
g_string_append_printf (
str,
"<tr class=\"header-item\">"
"<td colspan=\"2\"><h1>%s</h1></td>"
"</tr>\n",
subject);
g_free (subject);
for (iter = g_queue_peek_head_link (&emf->header_list); iter; iter = iter->next) {
EMFormatHeader *header = iter->data;
raw_header.name = header->name;
/* Skip 'Subject' header, it's already displayed. */
if (g_ascii_strncasecmp (header->name, "Subject", 7) == 0)
continue;
if (header->value && *header->value) {
raw_header.value = header->value;
em_format_html_format_header (emf, str,
CAMEL_MEDIUM (puri->part), &raw_header,
header->flags | EM_FORMAT_HTML_HEADER_NOLINKS,
"UTF-8");
} else {
raw_header.value = g_strdup (camel_medium_get_header (
CAMEL_MEDIUM (emf->message), header->name));
if (raw_header.value && *raw_header.value) {
em_format_html_format_header (emf, str,
CAMEL_MEDIUM (puri->part), &raw_header,
header->flags | EM_FORMAT_HTML_HEADER_NOLINKS,
"UTF-8");
}
if (raw_header.value)
g_free (raw_header.value);
}
}
/* Get prefix of this PURI */
puri_prefix = g_strndup (puri->uri, g_strrstr (puri->uri, ".") - puri->uri);
/* Add encryption/signature header */
raw_header.name = _("Security");
tmp = g_string_new ("");
/* Find first secured part. */
for (iter = emf->mail_part_list, puri; iter; iter = iter->next) {
p = iter->data;
if (p->validity_type == 0)
continue;
if (!g_str_has_prefix (p->uri, puri_prefix))
continue;
if ((p->validity_type & EM_FORMAT_VALIDITY_FOUND_PGP) &&
(p->validity_type & EM_FORMAT_VALIDITY_FOUND_SIGNED)) {
g_string_append (tmp, _("GPG signed"));
}
if ((p->validity_type & EM_FORMAT_VALIDITY_FOUND_PGP) &&
(p->validity_type & EM_FORMAT_VALIDITY_FOUND_ENCRYPTED)) {
if (tmp->len > 0) g_string_append (tmp, ", ");
g_string_append (tmp, _("GPG encrpyted"));
}
if ((p->validity_type & EM_FORMAT_VALIDITY_FOUND_SMIME) &&
(p->validity_type & EM_FORMAT_VALIDITY_FOUND_SIGNED)) {
if (tmp->len > 0) g_string_append (tmp, ", ");
g_string_append (tmp, _("S/MIME signed"));
}
if ((p->validity_type & EM_FORMAT_VALIDITY_FOUND_SMIME) &&
(p->validity_type & EM_FORMAT_VALIDITY_FOUND_ENCRYPTED)) {
if (tmp->len > 0) g_string_append (tmp, ", ");
g_string_append (tmp, _("S/MIME encrpyted"));
}
break;
}
if (tmp->len > 0) {
raw_header.value = tmp->str;
em_format_html_format_header (emf, str, CAMEL_MEDIUM (p->part),
&raw_header, EM_FORMAT_HEADER_BOLD | EM_FORMAT_HTML_HEADER_NOLINKS, "UTF-8");
}
g_string_free (tmp, TRUE);
/* Count attachments and display the number as a header */
attachments_count = 0;
for (iter = emf->mail_part_list; iter; iter = iter ? iter->next : iter) {
p = iter->data;
if (!g_str_has_prefix (p->uri, puri_prefix))
continue;
if ((p->is_attachment || g_str_has_suffix(p->uri, ".attachment")) &&
(!p->cid)) {
attachments_count++;
/* EFHD sometimes creates two PURIs per attachment! */
if (iter->next && iter->next->data) {
EMFormatPURI *p2 = iter->next->data;
if (g_str_has_prefix (p2->uri, p->uri))
iter = iter->next;
}
}
}
if (attachments_count > 0) {
raw_header.name = _("Attachments");
raw_header.value = g_strdup_printf ("%d", attachments_count);
em_format_html_format_header (emf, str, CAMEL_MEDIUM (puri->part),
&raw_header, EM_FORMAT_HEADER_BOLD | EM_FORMAT_HTML_HEADER_NOLINKS, "UTF-8");
g_free (raw_header.value);
}
g_string_append (str, "</table>");
camel_stream_write_string (stream, str->str, cancellable, NULL);
g_string_free (str, TRUE);
g_free (puri_prefix);
}
static void
efhp_write_inline_attachment (EMFormat *emf,
EMFormatPURI *puri,
CamelStream *stream,
EMFormatWriterInfo *info,
GCancellable *cancellable)
{
gchar *name;
EMFormatAttachmentPURI *att_puri = (EMFormatAttachmentPURI *) puri;
EAttachment *attachment;
GFileInfo *fi;
attachment = att_puri->attachment;
fi = e_attachment_get_file_info (attachment);
if (e_attachment_get_description (attachment) &&
*e_attachment_get_description (attachment)) {
name = g_strdup_printf ("<h2>Attachment: %s (%s)</h2>\n",
e_attachment_get_description (attachment),
g_file_info_get_display_name (fi));
} else {
name = g_strdup_printf ("<h2>Attachment: %s</h2>\n",
g_file_info_get_display_name (fi));
}
camel_stream_write_string (stream, name, cancellable, NULL);
g_free (name);
}
static void
efhp_write_print_layout (EMFormat *emf,
EMFormatPURI *puri,
CamelStream *stream,
EMFormatWriterInfo *info,
GCancellable *cancellable)
{
GList *iter;
EMFormatWriterInfo print_info = {
EM_FORMAT_WRITE_MODE_PRINTING, FALSE, FALSE };
EMFormatHTMLPrint *efhp = EM_FORMAT_HTML_PRINT (emf);
g_list_free (efhp->priv->attachments);
efhp->priv->attachments = NULL;
camel_stream_write_string (stream,
"<!DOCTYPE HTML>\n<html>\n"
"<head>\n<meta name=\"generator\" content=\"Evolution Mail Component\" />\n"
"<title>Evolution Mail Display</title>\n"
"<link type=\"text/css\" rel=\"stylesheet\" media=\"print\" "
"href=\"evo-file://" EVOLUTION_PRIVDATADIR "/theme/webview-print.css\" />\n"
"</head>\n"
"<body style=\"background: #FFF; color: #000;\">",
cancellable, NULL);
for (iter = emf->mail_part_list; iter != NULL; iter = iter ? iter->next : iter) {
EMFormatPURI *puri = iter->data;
if (g_str_has_suffix (puri->uri, "print_layout"))
continue;
/* To late to change .headers writer_func, do it manually. */
if (g_str_has_suffix (puri->uri, ".headers")) {
efhp_write_headers (emf, puri, stream, &print_info, cancellable);
continue;
}
if (g_str_has_suffix (puri->uri, ".rfc822")) {
puri->write_func (emf, puri, stream, &print_info, cancellable);
while (iter && !g_str_has_suffix (puri->uri, ".rfc822.end")) {
iter = iter->next;
if (iter)
puri = iter->data;
}
if (!iter)
break;
continue;
}
if (puri->is_attachment || g_str_has_suffix (puri->uri, ".attachment")) {
const EMFormatHandler *handler;
CamelContentType *ct;
gchar *mime_type;
if (puri->cid && g_ascii_strncasecmp (puri->cid, "cid:", 4) == 0)
continue;
ct = camel_mime_part_get_content_type (puri->part);
mime_type = camel_content_type_simple (ct);
handler = em_format_find_handler (puri->emf, mime_type);
d(printf("Handler for PURI %s (%s): %s\n", puri->uri, mime_type,
handler ? handler->mime_type : "(null)"));
g_free (mime_type);
efhp->priv->attachments =
g_list_append (efhp->priv->attachments, puri);
/* If we can't inline this attachment, skip it */
if (handler && puri->write_func) {
efhp_write_inline_attachment (puri->emf, puri,
stream, &print_info, cancellable);
if (iter->next && iter->next->data) {
EMFormatPURI *p;
p = iter->next->data;
/* Has the next PURI the same prefix? */
if (p->write_func &&
g_str_has_prefix (p->uri, puri->uri)) {
p->write_func (emf, p, stream,
&print_info, cancellable);
iter = iter->next;
} else {
if (puri->write_func) {
puri->write_func (emf, puri,
stream, &print_info,
cancellable);
}
}
}
}
continue;
}
/* Ignore widget parts and unwritable non-attachment parts */
if (puri->write_func == NULL)
continue;
/* Passed all tests, probably a regular part - display it */
puri->write_func (puri->emf, puri, stream, &print_info, cancellable);
}
efhp_write_attachments_list (efhp, stream, &print_info, cancellable);
camel_stream_write_string (stream, "</body></html>", cancellable, NULL);
}
static void
efhp_finalize (GObject *object)
{
EMFormatHTMLPrint *efhp = EM_FORMAT_HTML_PRINT (object);
if (efhp->priv->original_formatter) {
g_object_unref (efhp->priv->original_formatter);
efhp->priv->original_formatter = NULL;
}
if (efhp->priv->top_level_puri) {
em_format_puri_free (efhp->priv->top_level_puri);
efhp->priv->top_level_puri = NULL;
}
if (efhp->priv->attachments) {
g_list_free (efhp->priv->attachments);
efhp->priv->attachments = NULL;
}
/* Chain up to parent's finalize() method. */
G_OBJECT_CLASS (parent_class)->finalize (object);
}
static gboolean
efhp_is_inline (EMFormat *emf,
const gchar *part_id,
CamelMimePart *mime_part,
const EMFormatHandler *handle)
{
/* When printing, inline any part that has a handler. */
return (handle != NULL);
}
static void
efhp_set_orig_formatter (EMFormatHTMLPrint *efhp,
EMFormat *formatter)
{
EMFormat *emfp, *emfs;
EMFormatPURI *puri;
GHashTableIter iter;
gpointer key, value;
efhp->priv->original_formatter = g_object_ref (formatter);
emfp = EM_FORMAT (efhp);
emfs = EM_FORMAT (formatter);
emfp->mail_part_list = g_list_copy (emfs->mail_part_list);
/* Make a shallow copy of the table. This table will NOT destroy
* the PURIs when free'd! */
if (emfp->mail_part_table)
g_hash_table_unref (emfp->mail_part_table);
emfp->mail_part_table = g_hash_table_new (g_str_hash, g_str_equal);
g_hash_table_iter_init (&iter, emfs->mail_part_table);
while (g_hash_table_iter_next (&iter, &key, &value))
g_hash_table_insert (emfp->mail_part_table, key, value);
if (emfs->folder)
emfp->folder = g_object_ref (emfs->folder);
emfp->message_uid = g_strdup (emfs->message_uid);
emfp->message = g_object_ref (emfs->message);
/* Add a generic PURI that will write a HTML layout
* for all the parts */
puri = em_format_puri_new (EM_FORMAT (efhp),
sizeof (EMFormatPURI), NULL, "print_layout");
puri->write_func = efhp_write_print_layout;
puri->mime_type = g_strdup ("text/html");
em_format_add_puri (EM_FORMAT (efhp), puri);
efhp->priv->top_level_puri = puri;
}
static EMFormatHandler type_builtin_table[] = {
{ (gchar *) "x-evolution/message/headers", 0, efhp_write_headers, },
};
static void
efhp_builtin_init (EMFormatHTMLPrintClass *efhc)
{
EMFormatClass *emfc;
gint ii;
emfc = (EMFormatClass *) efhc;
for (ii = 0; ii < G_N_ELEMENTS (type_builtin_table); ii++)
em_format_class_add_handler (
emfc, &type_builtin_table[ii]);
}
static void
efhp_set_property (GObject *object,
guint prop_id,
const GValue *value,
GParamSpec *pspec)
{
switch (prop_id) {
case PROP_ORIGINAL_FORMATTER:
efhp_set_orig_formatter (
EM_FORMAT_HTML_PRINT (object),
(EMFormat *) g_value_get_object (value));
return;
}
G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
}
static void
efhp_get_property (GObject *object,
guint prop_id,
GValue *value,
GParamSpec *pspec)
{
EMFormatHTMLPrintPrivate *priv;
priv = EM_FORMAT_HTML_PRINT (object)->priv;
switch (prop_id) {
case PROP_ORIGINAL_FORMATTER:
g_value_set_pointer (value,
priv->original_formatter);
return;
}
G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
}
static void
em_format_html_print_base_init (EMFormatHTMLPrintClass *klass)
{
efhp_builtin_init (klass);
}
static void
em_format_html_print_class_init (EMFormatHTMLPrintClass *klass)
{
GObjectClass *object_class;
EMFormatClass *format_class;
parent_class = g_type_class_peek_parent (klass);
g_type_class_add_private (klass, sizeof (EMFormatHTMLPrintPrivate));
object_class = G_OBJECT_CLASS (klass);
object_class->finalize = efhp_finalize;
object_class->set_property = efhp_set_property;
object_class->get_property = efhp_get_property;
format_class = EM_FORMAT_CLASS (klass);
format_class->is_inline = efhp_is_inline;
g_object_class_install_property (
object_class,
PROP_ORIGINAL_FORMATTER,
g_param_spec_object (
"original-formatter",
NULL,
NULL,
EM_TYPE_FORMAT,
G_PARAM_WRITABLE |
G_PARAM_CONSTRUCT_ONLY));
}
static void
em_format_html_print_init (EMFormatHTMLPrint *efhp)
{
efhp->priv = G_TYPE_INSTANCE_GET_PRIVATE (
efhp, EM_TYPE_FORMAT_HTML_PRINT, EMFormatHTMLPrintPrivate);
efhp->priv->attachments = NULL;
efhp->export_filename = NULL;
}
GType
em_format_html_print_get_type (void)
{
static GType type = 0;
if (G_UNLIKELY (type == 0)) {
static const GTypeInfo type_info = {
sizeof (EMFormatHTMLPrintClass),
(GBaseInitFunc) em_format_html_print_base_init,
(GBaseFinalizeFunc) NULL,
(GClassInitFunc) em_format_html_print_class_init,
(GClassFinalizeFunc) NULL,
NULL, /* class_data */
sizeof (EMFormatHTMLPrint),
0, /* n_preallocs */
(GInstanceInitFunc) em_format_html_print_init,
NULL /* value_table */
};
type = g_type_register_static (
em_format_html_get_type(), "EMFormatHTMLPrint",
&type_info, 0);
}
return type;
}
EMFormatHTMLPrint *
em_format_html_print_new (EMFormatHTML *source)
{
EMFormatHTMLPrint *efhp;
CamelSession *session;
g_return_val_if_fail (EM_IS_FORMAT_HTML (source), NULL);
session = em_format_get_session (EM_FORMAT (source));
efhp = g_object_new (
EM_TYPE_FORMAT_HTML_PRINT,
"session", session,
"original-formatter", source,
NULL);
return efhp;
}