/*
* evolution-secure-button.c
*
* 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_H
#include <config.h>
#endif
#include "e-mail-format-extensions.h"
#include <glib/gi18n-lib.h>
#include <em-format/e-mail-formatter-extension.h>
#include <em-format/e-mail-formatter.h>
#include <e-util/e-util.h>
#if defined (HAVE_NSS) && defined (ENABLE_SMIME)
#include "certificate-viewer.h"
#include "e-cert-db.h"
#endif
#include <camel/camel.h>
typedef struct _EMailFormatterSecureButton {
GObject parent;
} EMailFormatterSecureButton;
typedef struct _EMailFormatterSecureButtonClass {
GObjectClass parent_class;
} EMailFormatterSecureButtonClass;
static void e_mail_formatter_formatter_extension_interface_init (EMailFormatterExtensionInterface *iface);
static void e_mail_formatter_mail_extension_interface_init (EMailExtensionInterface *iface);
G_DEFINE_TYPE_EXTENDED (
EMailFormatterSecureButton,
e_mail_formatter_secure_button,
G_TYPE_OBJECT,
0,
G_IMPLEMENT_INTERFACE (
E_TYPE_MAIL_EXTENSION,
e_mail_formatter_mail_extension_interface_init)
G_IMPLEMENT_INTERFACE (
E_TYPE_MAIL_FORMATTER_EXTENSION,
e_mail_formatter_formatter_extension_interface_init));
static const gchar *formatter_mime_types[] = { "application/vnd.evolution.widget.secure-button", NULL };
static const struct {
const gchar *icon, *shortdesc, *description;
} smime_sign_table[5] = {
{ "stock_signature-bad", N_("Unsigned"), N_("This message is not signed. There is no guarantee that this message is authentic.") },
{ "stock_signature-ok", N_("Valid signature"), N_("This message is signed and is valid meaning that it is very likely that this message is authentic.") },
{ "stock_signature-bad", N_("Invalid signature"), N_("The signature of this message cannot be verified, it may have been altered in transit.") },
{ "stock_signature", N_("Valid signature, but cannot verify sender"), N_("This message is signed with a valid signature, but the sender of the message cannot be verified.") },
{ "stock_signature-bad", N_("Signature exists, but need public key"), N_("This message is signed with a signature, but there is no corresponding public key.") },
};
static const struct {
const gchar *icon, *shortdesc, *description;
} smime_encrypt_table[4] = {
{ "stock_lock-broken", N_("Unencrypted"), N_("This message is not encrypted. Its content may be viewed in transit across the Internet.") },
{ "stock_lock-ok", N_("Encrypted, weak"), N_("This message is encrypted, but with a weak encryption algorithm. It would be difficult, but not impossible for an outsider to view the content of this message in a practical amount of time.") },
{ "stock_lock-ok", N_("Encrypted"), N_("This message is encrypted. It would be difficult for an outsider to view the content of this message.") },
{ "stock_lock-ok", N_("Encrypted, strong"), N_("This message is encrypted, with a strong encryption algorithm. It would be very difficult for an outsider to view the content of this message in a practical amount of time.") },
};
static const GdkRGBA smime_sign_colour[5] = {
{ 0 }, { 0.53, 0.73, 0.53, 1 }, { 0.73, 0.53, 0.53, 1 }, { 0.91, 0.82, 0.13, 1 }, { 0 },
};
static gboolean
emfe_secure_button_format (EMailFormatterExtension *extension,
EMailFormatter *formatter,
EMailFormatterContext *context,
EMailPart *part,
CamelStream *stream,
GCancellable *cancellable)
{
gchar *str;
if ((context->mode != E_MAIL_FORMATTER_MODE_NORMAL) &&
(context->mode != E_MAIL_FORMATTER_MODE_RAW) &&
(context->mode != E_MAIL_FORMATTER_MODE_ALL_HEADERS))
return FALSE;
str = g_strdup_printf (
"<object type=\"application/vnd.evolution.widget.secure-button\" "
"height=\"20\" width=\"100%%\" data=\"%s\" id=\"%s\"></object>",
part->id, part->id);
camel_stream_write_string (stream, str, cancellable, NULL);
g_free (str);
return TRUE;
}
#if defined (HAVE_NSS) && defined (ENABLE_SMIME)
static void
viewcert_clicked (GtkWidget *button,
GtkWidget *parent)
{
CamelCipherCertInfo *info = g_object_get_data ((GObject *) button, "e-cert-info");
ECert *ec = NULL;
if (info->cert_data)
ec = e_cert_new (CERT_DupCertificate (info->cert_data));
if (ec != NULL) {
GtkWidget *w = certificate_viewer_show (ec);
/* oddly enough certificate_viewer_show doesn't ... */
gtk_widget_show (w);
g_signal_connect (
w, "response",
G_CALLBACK (gtk_widget_destroy), NULL);
if (w && parent)
gtk_window_set_transient_for (
(GtkWindow *) w, (GtkWindow *) parent);
g_object_unref (ec);
} else {
g_warning ("can't find certificate for %s <%s>",
info->name ? info->name : "",
info->email ? info->email : "");
}
}
#endif
static void
info_response (GtkWidget *widget,
guint button,
gpointer user_data)
{
gtk_widget_destroy (widget);
}
static void
add_cert_table (GtkWidget *grid,
GQueue *certlist,
gpointer user_data)
{
GList *head, *link;
GtkTable *table;
gint n = 0;
table = (GtkTable *) gtk_table_new (certlist->length, 2, FALSE);
head = g_queue_peek_head_link (certlist);
for (link = head; link != NULL; link = g_list_next (link)) {
CamelCipherCertInfo *info = link->data;
gchar *la = NULL;
const gchar *l = NULL;
if (info->name) {
if (info->email && strcmp (info->name, info->email) != 0)
l = la = g_strdup_printf ("%s <%s>", info->name, info->email);
else
l = info->name;
} else {
if (info->email)
l = info->email;
}
if (l) {
GtkWidget *w;
#if defined (HAVE_NSS) && defined (ENABLE_SMIME)
ECert *ec = NULL;
#endif
w = gtk_label_new (l);
gtk_misc_set_alignment ((GtkMisc *) w, 0.0, 0.5);
g_free (la);
gtk_table_attach (table, w, 0, 1, n, n + 1, GTK_FILL, GTK_FILL, 3, 3);
#if defined (HAVE_NSS) && defined (ENABLE_SMIME)
w = gtk_button_new_with_mnemonic (_("_View Certificate"));
gtk_table_attach (table, w, 1, 2, n, n + 1, 0, 0, 3, 3);
g_object_set_data ((GObject *) w, "e-cert-info", info);
g_signal_connect (
w, "clicked",
G_CALLBACK (viewcert_clicked), grid);
if (info->cert_data)
ec = e_cert_new (CERT_DupCertificate (info->cert_data));
if (ec == NULL)
gtk_widget_set_sensitive (w, FALSE);
else
g_object_unref (ec);
#else
w = gtk_label_new (_("This certificate is not viewable"));
gtk_table_attach (table, w, 1, 2, n, n + 1, 0, 0, 3, 3);
#endif
n++;
}
}
gtk_container_add (GTK_CONTAINER (grid), GTK_WIDGET (table));
}
static void
format_cert_infos (GQueue *cert_infos,
GString *output_buffer)
{
GQueue valid = G_QUEUE_INIT;
GList *head, *link;
head = g_queue_peek_head_link (cert_infos);
/* Make sure we have a valid CamelCipherCertInfo before
* appending anything to the output buffer, so we don't
* end up with "()". */
for (link = head; link != NULL; link = g_list_next (link)) {
CamelCipherCertInfo *cinfo = link->data;
if ((cinfo->name != NULL && *cinfo->name != '\0') ||
(cinfo->email != NULL && *cinfo->email != '\0')) {
g_queue_push_tail (&valid, cinfo);
}
}
if (g_queue_is_empty (&valid))
return;
g_string_append (output_buffer, " (");
while (!g_queue_is_empty (&valid)) {
CamelCipherCertInfo *cinfo;
cinfo = g_queue_pop_head (&valid);
if (cinfo->name != NULL && *cinfo->name != '\0') {
g_string_append (output_buffer, cinfo->name);
if (cinfo->email != NULL && *cinfo->email != '\0') {
g_string_append (output_buffer, " <");
g_string_append (output_buffer, cinfo->email);
g_string_append (output_buffer, ">");
}
} else if (cinfo->email != NULL && *cinfo->email != '\0') {
g_string_append (output_buffer, cinfo->email);
}
if (!g_queue_is_empty (&valid))
g_string_append (output_buffer, ", ");
}
g_string_append_c (output_buffer, ')');
}
static void
secure_button_clicked_cb (GtkWidget *widget,
EMailPart *part)
{
GtkBuilder *builder;
GtkWidget *grid, *w;
GtkWidget *dialog;
builder = gtk_builder_new ();
e_load_ui_builder_definition (builder, "mail-dialogs.ui");
dialog = e_builder_get_widget (builder, "message_security_dialog");
grid = e_builder_get_widget (builder, "signature_grid");
w = gtk_label_new (_(smime_sign_table[part->validity->sign.status].description));
gtk_misc_set_alignment ((GtkMisc *) w, 0.0, 0.5);
gtk_label_set_line_wrap ((GtkLabel *) w, TRUE);
gtk_container_add (GTK_CONTAINER (grid), w);
if (part->validity->sign.description) {
GtkTextBuffer *buffer;
buffer = gtk_text_buffer_new (NULL);
gtk_text_buffer_set_text (
buffer, part->validity->sign.description,
strlen (part->validity->sign.description));
w = g_object_new (gtk_scrolled_window_get_type (),
"hscrollbar_policy", GTK_POLICY_AUTOMATIC,
"vscrollbar_policy", GTK_POLICY_AUTOMATIC,
"shadow_type", GTK_SHADOW_IN,
"expand", TRUE,
"child", g_object_new (gtk_text_view_get_type (),
"buffer", buffer,
"cursor_visible", FALSE,
"editable", FALSE,
"width_request", 500,
"height_request", 160,
NULL),
NULL);
g_object_unref (buffer);
gtk_container_add (GTK_CONTAINER (grid), w);
}
if (!g_queue_is_empty (&part->validity->sign.signers))
add_cert_table (
grid, &part->validity->sign.signers, NULL);
gtk_widget_show_all (grid);
grid = e_builder_get_widget (builder, "encryption_grid");
w = gtk_label_new (_(smime_encrypt_table[part->validity->encrypt.status].description));
gtk_misc_set_alignment ((GtkMisc *) w, 0.0, 0.5);
gtk_label_set_line_wrap ((GtkLabel *) w, TRUE);
gtk_container_add (GTK_CONTAINER (grid), w);
if (part->validity->encrypt.description) {
GtkTextBuffer *buffer;
buffer = gtk_text_buffer_new (NULL);
gtk_text_buffer_set_text (
buffer, part->validity->encrypt.description,
strlen (part->validity->encrypt.description));
w = g_object_new (gtk_scrolled_window_get_type (),
"hscrollbar_policy", GTK_POLICY_AUTOMATIC,
"vscrollbar_policy", GTK_POLICY_AUTOMATIC,
"shadow_type", GTK_SHADOW_IN,
"expand", TRUE,
"child", g_object_new (gtk_text_view_get_type (),
"buffer", buffer,
"cursor_visible", FALSE,
"editable", FALSE,
"width_request", 500,
"height_request", 160,
NULL),
NULL);
g_object_unref (buffer);
gtk_container_add (GTK_CONTAINER (grid), w);
}
if (!g_queue_is_empty (&part->validity->encrypt.encrypters))
add_cert_table (grid, &part->validity->encrypt.encrypters, NULL);
gtk_widget_show_all (grid);
g_object_unref (builder);
g_signal_connect (
dialog, "response",
G_CALLBACK (info_response), NULL);
gtk_widget_show (dialog);
}
static GtkWidget *
emfe_secure_button_get_widget (EMailFormatterExtension *extension,
EMailPartList *context,
EMailPart *part,
GHashTable *params)
{
GtkWidget *box, *button, *layout, *widget;
const gchar *icon_name;
gchar *description;
GString *buffer;
buffer = g_string_new ("");
if (part->validity->sign.status != CAMEL_CIPHER_VALIDITY_SIGN_NONE) {
const gchar *desc;
gint status;
status = part->validity->sign.status;
desc = smime_sign_table[status].shortdesc;
g_string_append (buffer, gettext (desc));
format_cert_infos (&part->validity->sign.signers, buffer);
}
if (part->validity->encrypt.status != CAMEL_CIPHER_VALIDITY_ENCRYPT_NONE) {
const gchar *desc;
gint status;
if (part->validity->sign.status != CAMEL_CIPHER_VALIDITY_SIGN_NONE)
g_string_append (buffer, "\n");
status = part->validity->encrypt.status;
desc = smime_encrypt_table[status].shortdesc;
g_string_append (buffer, gettext (desc));
}
description = g_string_free (buffer, FALSE);
/* FIXME: need to have it based on encryption and signing too */
if (part->validity->sign.status != 0)
icon_name = smime_sign_table[part->validity->sign.status].icon;
else
icon_name = smime_encrypt_table[part->validity->encrypt.status].icon;
box = gtk_event_box_new ();
if (part->validity->sign.status != 0)
gtk_widget_override_background_color (box, GTK_STATE_FLAG_NORMAL,
&smime_sign_colour[part->validity->sign.status]);
layout = gtk_box_new (GTK_ORIENTATION_HORIZONTAL, 5);
gtk_container_add (GTK_CONTAINER (box), layout);
button = gtk_button_new ();
gtk_box_pack_start (GTK_BOX (layout), button, FALSE, FALSE, 0);
g_signal_connect (button, "clicked",
G_CALLBACK (secure_button_clicked_cb), part);
widget = gtk_image_new_from_icon_name (
icon_name, GTK_ICON_SIZE_LARGE_TOOLBAR);
gtk_button_set_image (GTK_BUTTON (button), widget);
widget = gtk_label_new (description);
gtk_box_pack_start (GTK_BOX (layout), widget, FALSE, FALSE, 0);
gtk_widget_show_all (box);
g_free (description);
return box;
}
static const gchar *
emfe_secure_button_get_display_name (EMailFormatterExtension *extension)
{
return NULL;
}
static const gchar *
emfe_secure_button_get_description (EMailFormatterExtension *extension)
{
return NULL;
}
static const gchar **
emfe_secure_button_mime_types (EMailExtension *extension)
{
return formatter_mime_types;
}
static void
e_mail_formatter_secure_button_class_init (EMailFormatterSecureButtonClass *class)
{
}
static void
e_mail_formatter_formatter_extension_interface_init (EMailFormatterExtensionInterface *iface)
{
iface->format = emfe_secure_button_format;
iface->get_widget = emfe_secure_button_get_widget;
iface->get_display_name = emfe_secure_button_get_display_name;
iface->get_description = emfe_secure_button_get_description;
}
static void
e_mail_formatter_mail_extension_interface_init (EMailExtensionInterface *iface)
{
iface->mime_types = emfe_secure_button_mime_types;
}
static void
e_mail_formatter_secure_button_init (EMailFormatterSecureButton *extension)
{
}