aboutsummaryrefslogblamecommitdiffstats
path: root/mail/em-format-html-print.c
blob: 150c16dbf56244c08d189209f86f3e5081884ece (plain) (tree)
1
2
3
4
5
6
7
8
9
10
11
  
  



                                                                
  



                                                                    
  
                                                                   
                                                                             
  




                                                        


   




                    
                       
                    
 


                                   
                           

                                            



                                     



































                                                                                                                                                       

                                                                          
                                    


                                                               















                                                                           
                                                          









                                                                            
                                                                                 





                                    
                                            























                                                                             
                                                                              

                                


                                                    


































                                                                                          
                                                                                    



































































































                                                                                                     
                                                                         


                                                                  
                                                                    


























                                                                                            
                                                                                                  




















                                                                                         
                                                                                      

















                                                                                         
                                                                                         





                                                                                

                                                                                        













































                                                                                     

           
                               
 
                                                                
 













                                                                 
 
                                                     
                                                         

 









                                                                
           

                                                 
 


































                                                                           

 



                                                                            
           
                                                
 

                            
 
                                      
 


                                                                  

 




                                       
 
                          
 





                                                                         
 
                                                                   

 




                                     
 
                                       
 
                                                   
 
                          
 




                                                          
 
                                                                   
 
 
           
                                                              
 
                                  

 
           
                                                               
 

                                    
 

                                                                            
 



                                                       
 

                                                 
 










                                                 
 







                                                                           

 

                                    
 


















                                                                         
         







                                               
                              
 






                                                                



                                             
 
/*
 *
 * 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;
}