[gnome-keyring] [gcr] Add 'Export Certificate' right click in cert widget.
- From: Stefan Walter <stefw src gnome org>
- To: commits-list gnome org
- Cc:
- Subject: [gnome-keyring] [gcr] Add 'Export Certificate' right click in cert widget.
- Date: Wed, 9 Mar 2011 19:00:09 +0000 (UTC)
commit 5f50b09b92d8cc29a73100c1242a1c1893d6559f
Author: Stef Walter <stefw collabora co uk>
Date: Tue Mar 8 18:12:42 2011 +0100
[gcr] Add 'Export Certificate' right click in cert widget.
Users can right click on the certificate widget, and export the
certificate to DER or PEM format.
gcr/Makefile.am | 1 +
gcr/gcr-certificate-exporter.c | 566 +++++++++++++++++++++++++++++++++++++++
gcr/gcr-certificate-exporter.h | 73 +++++
gcr/gcr-certificate-renderer.c | 79 ++++++-
gcr/gcr-display-view.c | 68 +++++
gcr/gcr-renderer.c | 9 +
gcr/gcr-renderer.h | 6 +
gcr/tests/ui-test-certificate.c | 23 +-
8 files changed, 813 insertions(+), 12 deletions(-)
---
diff --git a/gcr/Makefile.am b/gcr/Makefile.am
index 2201f85..568d36f 100644
--- a/gcr/Makefile.am
+++ b/gcr/Makefile.am
@@ -66,6 +66,7 @@ libgcr GCR_VERSION_SUFFIX@_la_SOURCES = \
gcr-certificate.c gcr-certificate.h \
gcr-certificate-chain.c gcr-certificate-chain.h \
gcr-certificate-renderer.c gcr-certificate-renderer.h \
+ gcr-certificate-exporter.c gcr-certificate-exporter.h \
gcr-certificate-widget.c gcr-certificate-widget.h \
gcr-display-scrolled.c gcr-display-scrolled.h \
gcr-display-view.c gcr-display-view.h \
diff --git a/gcr/gcr-certificate-exporter.c b/gcr/gcr-certificate-exporter.c
new file mode 100644
index 0000000..115b030
--- /dev/null
+++ b/gcr/gcr-certificate-exporter.c
@@ -0,0 +1,566 @@
+/*
+ * Copyright (C) 2011 Collabora Ltd.
+ *
+ * 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.1 of
+ * the License, or (at your option) any later version.
+ *
+ * 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 this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA
+ * 02111-1307, USA.
+ *
+ * Author: Stef Walter <stefw collabora co uk>
+ */
+
+#include "config.h"
+
+#include "gcr-certificate.h"
+#include "gcr-certificate-exporter.h"
+
+#include "egg/egg-openssl.h"
+
+#include <glib/gi18n-lib.h>
+
+enum {
+ PROP_0,
+ PROP_CERTIFICATE,
+ PROP_LABEL,
+ PROP_TRANSIENT_FOR
+};
+
+struct _GcrCertificateExporterPrivate {
+
+ /* Setup stuff */
+ GcrCertificate *certificate;
+ gchar *label;
+ GtkWindow *transient_for;
+
+ /* Used during operation */
+ GtkFileChooser *chooser_dialog;
+ GFile *output_file;
+ GByteArray *buffer;
+ guint buffer_at;
+
+ /* Async stuff */
+ GAsyncReadyCallback callback;
+ gpointer user_data;
+ GCancellable *cancellable;
+ GError *error;
+ gboolean completed;
+};
+
+static const gchar *BAD_FILENAME_CHARS = "/\\<>|?*";
+
+/* Forward declarations */
+static void _gcr_certificate_exporter_async_result_init (GAsyncResultIface *iface);
+static void write_to_outputstream (GcrCertificateExporter *self, GOutputStream *os);
+
+G_DEFINE_TYPE_WITH_CODE (GcrCertificateExporter, _gcr_certificate_exporter, G_TYPE_OBJECT,
+ G_IMPLEMENT_INTERFACE (G_TYPE_ASYNC_RESULT, _gcr_certificate_exporter_async_result_init));
+
+typedef void (*PrepareDataFunc) (GcrCertificateExporter *self);
+
+/* -----------------------------------------------------------------------------
+ * INTERNAL
+ */
+
+static void
+prepare_data_for_der (GcrCertificateExporter *self)
+{
+ gconstpointer data;
+ gsize n_data;
+
+ data = gcr_certificate_get_der_data (self->pv->certificate, &n_data);
+ g_return_if_fail (data);
+
+ self->pv->buffer = g_byte_array_new ();
+ g_byte_array_append (self->pv->buffer, data, n_data);
+}
+
+static void
+prepare_data_for_pem (GcrCertificateExporter *self)
+{
+ gconstpointer data;
+ gpointer encoded;
+ gsize n_data, n_encoded;
+
+ data = gcr_certificate_get_der_data (self->pv->certificate, &n_data);
+ g_return_if_fail (data);
+
+ self->pv->buffer = g_byte_array_new ();
+
+ encoded = egg_openssl_pem_write (data, n_data,
+ g_quark_from_static_string ("CERTIFICATE"),
+ NULL, &n_encoded);
+
+ g_byte_array_append (self->pv->buffer, encoded, n_encoded);
+ g_free (encoded);
+}
+
+static void
+complete_async_result (GcrCertificateExporter *self)
+{
+ g_assert (self->pv->callback);
+ g_assert (!self->pv->completed);
+
+ if (self->pv->chooser_dialog)
+ gtk_widget_hide (GTK_WIDGET (self->pv->chooser_dialog));
+
+ self->pv->completed = TRUE;
+ (self->pv->callback) (G_OBJECT (self), G_ASYNC_RESULT (self),
+ self->pv->user_data);
+}
+
+static void
+on_outputstream_write_ready (GObject *source, GAsyncResult *res, gpointer user_data)
+{
+ GcrCertificateExporter *self = GCR_CERTIFICATE_EXPORTER (user_data);
+ GOutputStream *os = G_OUTPUT_STREAM (source);
+ gssize written;
+
+ written = g_output_stream_write_finish (os, res, &self->pv->error);
+
+ if (self->pv->error) {
+ complete_async_result (self);
+ return;
+ }
+
+ g_return_if_fail (written >= 0);
+ g_return_if_fail (written <= self->pv->buffer->len - self->pv->buffer_at);
+ self->pv->buffer_at += written;
+
+ /* Write next bit, or finished */
+ write_to_outputstream (self, os);
+}
+
+static void
+on_outputstream_closed (GObject *source, GAsyncResult *res, gpointer user_data)
+{
+ GcrCertificateExporter *self = GCR_CERTIFICATE_EXPORTER (user_data);
+ g_output_stream_close_finish (G_OUTPUT_STREAM (source), res, &self->pv->error);
+ complete_async_result (self);
+}
+
+static void
+write_to_outputstream (GcrCertificateExporter *self, GOutputStream *os)
+{
+ gtk_widget_hide (GTK_WIDGET (self->pv->chooser_dialog));
+ g_assert (GTK_IS_WIDGET (self->pv->chooser_dialog));
+
+ /* Are we all done? */
+ g_assert (self->pv->buffer_at <= self->pv->buffer->len);
+ if (self->pv->buffer_at == self->pv->buffer->len) {
+ g_output_stream_close_async (os, G_PRIORITY_DEFAULT,
+ self->pv->cancellable,
+ on_outputstream_closed, self);
+ return;
+ }
+
+ g_output_stream_write_async (os, self->pv->buffer->data + self->pv->buffer_at,
+ self->pv->buffer->len - self->pv->buffer_at,
+ G_PRIORITY_DEFAULT, self->pv->cancellable,
+ on_outputstream_write_ready, self);
+}
+
+static void
+on_replace_file_ready (GObject *source, GAsyncResult *res, gpointer user_data)
+{
+ GcrCertificateExporter *self = GCR_CERTIFICATE_EXPORTER (user_data);
+ GFile *file = G_FILE (source);
+ GFileOutputStream *os;
+
+ os = g_file_replace_finish (file, res, &self->pv->error);
+
+ if (self->pv->error) {
+ complete_async_result (self);
+ return;
+ }
+
+ write_to_outputstream (self, G_OUTPUT_STREAM (os));
+}
+
+static void
+on_replace_dialog_response (GtkDialog *dialog, gint response_id, gpointer user_data)
+{
+ GcrCertificateExporter *self = GCR_CERTIFICATE_EXPORTER (user_data);
+
+ if (response_id == GTK_RESPONSE_ACCEPT) {
+ g_file_replace_async (self->pv->output_file, NULL, FALSE, G_FILE_CREATE_NONE,
+ G_PRIORITY_DEFAULT, self->pv->cancellable,
+ on_replace_file_ready, self);
+ }
+
+ gtk_widget_destroy (GTK_WIDGET (dialog));
+}
+
+static void
+on_cancel_replace_dialog (GCancellable *cancellable, gpointer user_data)
+{
+ gtk_widget_destroy (user_data);
+}
+
+static void
+on_create_file_ready (GObject *source, GAsyncResult *res, gpointer user_data)
+{
+ GcrCertificateExporter *self = GCR_CERTIFICATE_EXPORTER (user_data);
+ GFileOutputStream *os;
+ GtkWidget *dialog;
+
+ os = g_file_create_finish (self->pv->output_file, res, &self->pv->error);
+
+ /* Try again this time replacing the file */
+ if (g_error_matches (self->pv->error, G_IO_ERROR, G_IO_ERROR_EXISTS)) {
+ g_clear_error (&self->pv->error);
+
+ dialog = gtk_message_dialog_new_with_markup (GTK_WINDOW (self->pv->chooser_dialog),
+ GTK_DIALOG_DESTROY_WITH_PARENT, GTK_MESSAGE_QUESTION,
+ GTK_BUTTONS_NONE, _("<b>A file already exists with this name.</b>\n\nDo you want to replace it with a new file?"));
+ gtk_dialog_add_buttons (GTK_DIALOG (dialog),
+ GTK_STOCK_CANCEL, GTK_RESPONSE_CANCEL,
+ _("_Replace"), GTK_RESPONSE_ACCEPT, NULL);
+
+ g_signal_connect (dialog, "response",
+ G_CALLBACK (on_replace_dialog_response), self);
+ if (self->pv->cancellable)
+ g_cancellable_connect (self->pv->cancellable,
+ G_CALLBACK (on_cancel_replace_dialog),
+ g_object_ref (dialog), g_object_unref);
+ gtk_widget_show (dialog);
+
+ return;
+ }
+
+ if (self->pv->error) {
+ complete_async_result (self);
+ return;
+ }
+
+ write_to_outputstream (self, G_OUTPUT_STREAM (os));
+}
+
+static void
+on_chooser_dialog_response (GtkDialog *dialog, gint response_id, gpointer user_data)
+{
+ GcrCertificateExporter *self = GCR_CERTIFICATE_EXPORTER (user_data);
+ GtkFileFilter *filter;
+ PrepareDataFunc prepare_data;
+
+ if (response_id != GTK_RESPONSE_ACCEPT) {
+ g_set_error (&self->pv->error, G_IO_ERROR, G_IO_ERROR_CANCELLED,
+ _("The operation was cancelled."));
+ complete_async_result (self);
+ return;
+ }
+
+ if (self->pv->output_file)
+ g_object_unref (self->pv->output_file);
+ self->pv->output_file = gtk_file_chooser_get_file (self->pv->chooser_dialog);
+ g_return_if_fail (self->pv->output_file);
+
+ filter = gtk_file_chooser_get_filter (self->pv->chooser_dialog);
+ prepare_data = g_object_get_data (G_OBJECT (filter), "prepare-data-func");
+ g_assert (prepare_data);
+
+ if (self->pv->buffer)
+ g_byte_array_free (self->pv->buffer, TRUE);
+ self->pv->buffer = NULL;
+ self->pv->buffer_at = 0;
+
+ /* Prepare the for writing out */
+ (prepare_data) (self);
+
+ /* Try to open the file */
+ g_file_create_async (self->pv->output_file, G_FILE_CREATE_NONE, G_PRIORITY_DEFAULT,
+ self->pv->cancellable, on_create_file_ready,
+ self);
+}
+
+static void
+on_cancel_chooser_dialog (GCancellable *cancellable, gpointer user_data)
+{
+ GtkDialog *dialog = GTK_DIALOG (user_data);
+ gtk_dialog_response (dialog, GTK_RESPONSE_CANCEL);
+}
+
+static void
+exporter_display_chooser (GcrCertificateExporter *self)
+{
+ GtkFileFilter* filter;
+ GtkWidget *dialog;
+ gchar *filename;
+
+ g_assert (!self->pv->chooser_dialog);
+
+ dialog = gtk_file_chooser_dialog_new (_("Export certificate"),
+ NULL, GTK_FILE_CHOOSER_ACTION_SAVE,
+ GTK_STOCK_CANCEL, GTK_RESPONSE_CANCEL,
+ GTK_STOCK_SAVE, GTK_RESPONSE_ACCEPT,
+ NULL);
+
+ self->pv->chooser_dialog = g_object_ref_sink(dialog);
+ gtk_dialog_set_default_response (GTK_DIALOG (dialog),
+ GTK_RESPONSE_ACCEPT);
+ gtk_file_chooser_set_local_only (self->pv->chooser_dialog, FALSE);
+
+ filter = gtk_file_filter_new ();
+ gtk_file_filter_set_name (filter, _("Certificate files"));
+ gtk_file_filter_add_mime_type (filter, "application/x-x509-ca-cert");
+ gtk_file_filter_add_mime_type (filter, "application/x-x509-user-cert");
+ gtk_file_filter_add_mime_type (filter, "application/pkix-cert");
+ gtk_file_filter_add_pattern (filter, "*.cer");
+ gtk_file_filter_add_pattern (filter, "*.crt");
+ g_object_set_data (G_OBJECT (filter), "prepare-data-func", prepare_data_for_der);
+ gtk_file_chooser_add_filter (self->pv->chooser_dialog, filter);
+ gtk_file_chooser_set_filter (self->pv->chooser_dialog, filter);
+
+ filter = gtk_file_filter_new ();
+ gtk_file_filter_set_name (filter, _("PEM files"));
+ gtk_file_filter_add_mime_type (filter, "text/plain");
+ gtk_file_filter_add_pattern (filter, "*.pem");
+ g_object_set_data (G_OBJECT (filter), "prepare-data-func", prepare_data_for_pem);
+ gtk_file_chooser_add_filter (self->pv->chooser_dialog, filter);
+
+ filename = g_strconcat (self->pv->label, ".crt", NULL);
+ g_strdelimit (filename, BAD_FILENAME_CHARS, '_');
+ gtk_file_chooser_set_current_name (self->pv->chooser_dialog, filename);
+ g_free (filename);
+
+ g_signal_connect (self->pv->chooser_dialog, "response",
+ G_CALLBACK (on_chooser_dialog_response), self);
+ if (self->pv->cancellable)
+ g_cancellable_connect (self->pv->cancellable,
+ G_CALLBACK (on_cancel_chooser_dialog), self, NULL);
+
+ gtk_widget_show (GTK_WIDGET (self->pv->chooser_dialog));
+}
+
+/* -----------------------------------------------------------------------------
+ * OBJECT
+ */
+
+static void
+_gcr_certificate_exporter_init (GcrCertificateExporter *self)
+{
+ self->pv = (G_TYPE_INSTANCE_GET_PRIVATE (self, GCR_TYPE_CERTIFICATE_EXPORTER,
+ GcrCertificateExporterPrivate));
+}
+
+static void
+_gcr_certificate_exporter_dispose (GObject *obj)
+{
+ GcrCertificateExporter *self = GCR_CERTIFICATE_EXPORTER (obj);
+
+ if (self->pv->certificate)
+ g_object_unref (self->pv->certificate);
+ self->pv->certificate = NULL;
+
+ if (self->pv->cancellable)
+ g_object_unref (self->pv->cancellable);
+ self->pv->cancellable = NULL;
+
+ G_OBJECT_CLASS (_gcr_certificate_exporter_parent_class)->dispose (obj);
+}
+
+static void
+_gcr_certificate_exporter_finalize (GObject *obj)
+{
+ GcrCertificateExporter *self = GCR_CERTIFICATE_EXPORTER (obj);
+
+ g_free (self->pv->label);
+
+ /*
+ * Should have been freed in _export_finish, which holds a ref to self
+ * so this should never be reached without being finished.
+ */
+ g_assert (!self->pv->chooser_dialog);
+ g_assert (!self->pv->output_file);
+ g_assert (!self->pv->buffer);
+
+ g_clear_error (&self->pv->error);
+
+ G_OBJECT_CLASS (_gcr_certificate_exporter_parent_class)->finalize (obj);
+}
+
+static void
+_gcr_certificate_exporter_set_property (GObject *obj, guint prop_id, const GValue *value,
+ GParamSpec *pspec)
+{
+ GcrCertificateExporter *self = GCR_CERTIFICATE_EXPORTER (obj);
+ GcrCertificate *cert;
+
+ switch (prop_id) {
+ case PROP_CERTIFICATE:
+ cert = g_value_dup_object (value);
+ if (self->pv->certificate)
+ g_object_unref (self->pv->certificate);
+ self->pv->certificate = cert;
+ g_object_notify (G_OBJECT (self), "certificate");
+ break;
+ case PROP_LABEL:
+ g_free (self->pv->label);
+ self->pv->label = g_value_dup_string (value);
+ g_object_notify (obj, "label");
+ break;
+ case PROP_TRANSIENT_FOR:
+ self->pv->transient_for = g_value_get_object (value);
+ g_object_notify (obj, "transient-for");
+ break;
+ default:
+ G_OBJECT_WARN_INVALID_PROPERTY_ID (obj, prop_id, pspec);
+ break;
+ }
+}
+
+static void
+_gcr_certificate_exporter_get_property (GObject *obj, guint prop_id, GValue *value,
+ GParamSpec *pspec)
+{
+ GcrCertificateExporter *self = GCR_CERTIFICATE_EXPORTER (obj);
+
+ switch (prop_id) {
+ case PROP_CERTIFICATE:
+ g_value_set_object (value, self->pv->certificate);
+ break;
+ case PROP_LABEL:
+ g_value_take_string (value, self->pv->label);
+ break;
+ case PROP_TRANSIENT_FOR:
+ g_value_set_object (value, self->pv->transient_for);
+ break;
+ default:
+ G_OBJECT_WARN_INVALID_PROPERTY_ID (obj, prop_id, pspec);
+ break;
+ }
+}
+
+static void
+_gcr_certificate_exporter_class_init (GcrCertificateExporterClass *klass)
+{
+ GObjectClass *gobject_class = G_OBJECT_CLASS (klass);
+
+ _gcr_certificate_exporter_parent_class = g_type_class_peek_parent (klass);
+ g_type_class_add_private (klass, sizeof (GcrCertificateExporterPrivate));
+
+ gobject_class->dispose = _gcr_certificate_exporter_dispose;
+ gobject_class->finalize = _gcr_certificate_exporter_finalize;
+ gobject_class->set_property = _gcr_certificate_exporter_set_property;
+ gobject_class->get_property = _gcr_certificate_exporter_get_property;
+
+ g_object_class_install_property (gobject_class, PROP_CERTIFICATE,
+ g_param_spec_object ("certificate", "Certificate", "Certificate to display.",
+ GCR_TYPE_CERTIFICATE, G_PARAM_READWRITE));
+
+ g_object_class_install_property (gobject_class, PROP_LABEL,
+ g_param_spec_string ("label", "Label", "Label of certificate.",
+ _("Certificate"), G_PARAM_READWRITE));
+
+ g_object_class_install_property (gobject_class, PROP_TRANSIENT_FOR,
+ g_param_spec_object ("transient-for", "Transient For", "Transient for this Window",
+ GTK_TYPE_WINDOW, G_PARAM_READWRITE));
+}
+
+static GObject*
+_gcr_certificate_exporter_get_source_object (GAsyncResult *result)
+{
+ g_return_val_if_fail (GCR_IS_CERTIFICATE_EXPORTER (result), NULL);
+ return G_OBJECT (result);
+}
+
+static gpointer
+_gcr_certificate_exporter_get_user_data (GAsyncResult *result)
+{
+ g_return_val_if_fail (GCR_IS_CERTIFICATE_EXPORTER (result), NULL);
+ return GCR_CERTIFICATE_EXPORTER (result)->pv->user_data;
+}
+
+static void
+_gcr_certificate_exporter_async_result_init (GAsyncResultIface *iface)
+{
+ iface->get_source_object = _gcr_certificate_exporter_get_source_object;
+ iface->get_user_data = _gcr_certificate_exporter_get_user_data;
+}
+
+/* -----------------------------------------------------------------------------
+ * PUBLIC
+ */
+
+GcrCertificateExporter*
+_gcr_certificate_exporter_new (GcrCertificate *certificate, const gchar *label,
+ GtkWindow *transient_for)
+{
+ return g_object_new (GCR_TYPE_CERTIFICATE_EXPORTER,
+ "certificate", certificate,
+ "label", label,
+ "transient-for", transient_for,
+ NULL);
+}
+
+void
+_gcr_certificate_exporter_export_async (GcrCertificateExporter *self,
+ GCancellable *cancellable,
+ GAsyncReadyCallback callback,
+ gpointer user_data)
+{
+ g_return_if_fail (GCR_IS_CERTIFICATE_EXPORTER (self));
+ g_return_if_fail (callback);
+
+ /* Must not have already started */
+ g_return_if_fail (!self->pv->callback);
+ g_return_if_fail (!self->pv->cancellable);
+
+ self->pv->callback = callback;
+ self->pv->user_data = user_data;
+ if (cancellable)
+ self->pv->cancellable = g_object_ref (cancellable);
+
+ exporter_display_chooser (self);
+
+ /* Matching in export_finish */
+ g_object_ref (self);
+}
+
+gboolean
+_gcr_certificate_exporter_export_finish (GcrCertificateExporter *self,
+ GAsyncResult *result,
+ GError **error)
+{
+ gboolean ret = TRUE;
+
+ g_return_val_if_fail (G_ASYNC_RESULT (self) == result, FALSE);
+ g_return_val_if_fail (!error || !*error, FALSE);
+ g_return_val_if_fail (self->pv->completed, FALSE);
+
+ /* Cleanup all the operation stuff */
+ self->pv->callback = NULL;
+
+ if (self->pv->chooser_dialog)
+ g_object_unref (self->pv->chooser_dialog);
+ self->pv->chooser_dialog = NULL;
+
+ if (self->pv->output_file)
+ g_object_unref (self->pv->output_file);
+ self->pv->output_file = NULL;
+
+ if (self->pv->buffer)
+ g_byte_array_free (self->pv->buffer, TRUE);
+ self->pv->buffer = NULL;
+ self->pv->buffer_at = 0;
+
+ self->pv->completed = FALSE;
+
+ if (self->pv->error) {
+ g_propagate_error (error, self->pv->error);
+ ret = FALSE;
+ }
+
+ /* Matches in export_async */
+ g_object_unref (self);
+ return ret;
+}
diff --git a/gcr/gcr-certificate-exporter.h b/gcr/gcr-certificate-exporter.h
new file mode 100644
index 0000000..5f7a9cf
--- /dev/null
+++ b/gcr/gcr-certificate-exporter.h
@@ -0,0 +1,73 @@
+/*
+ * Copyright (C) 2011 Collabora Ltd.
+ *
+ * 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.1 of
+ * the License, or (at your option) any later version.
+ *
+ * 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 this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA
+ * 02111-1307, USA.
+ *
+ * Author: Stef Walter <stefw collabora co uk>
+ */
+
+#if !defined (__GCR_H_INSIDE__) && !defined (GCR_COMPILATION)
+#error "Only <gcr/gcr.h> can be included directly."
+#endif
+
+#ifndef __GCR_CERTIFICATE_EXPORTER_H__
+#define __GCR_CERTIFICATE_EXPORTER_H__
+
+#include <glib-object.h>
+#include <gtk/gtk.h>
+
+#include "gcr-types.h"
+
+G_BEGIN_DECLS
+
+#define GCR_TYPE_CERTIFICATE_EXPORTER (_gcr_certificate_exporter_get_type ())
+#define GCR_CERTIFICATE_EXPORTER(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), GCR_TYPE_CERTIFICATE_EXPORTER, GcrCertificateExporter))
+#define GCR_CERTIFICATE_EXPORTER_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), GCR_TYPE_CERTIFICATE_EXPORTER, GcrCertificateExporterClass))
+#define GCR_IS_CERTIFICATE_EXPORTER(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), GCR_TYPE_CERTIFICATE_EXPORTER))
+#define GCR_IS_CERTIFICATE_EXPORTER_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), GCR_TYPE_CERTIFICATE_EXPORTER))
+#define GCR_CERTIFICATE_EXPORTER_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), GCR_TYPE_CERTIFICATE_EXPORTER, GcrCertificateExporterClass))
+
+typedef struct _GcrCertificateExporter GcrCertificateExporter;
+typedef struct _GcrCertificateExporterClass GcrCertificateExporterClass;
+typedef struct _GcrCertificateExporterPrivate GcrCertificateExporterPrivate;
+
+struct _GcrCertificateExporter {
+ GObject parent;
+ GcrCertificateExporterPrivate *pv;
+};
+
+struct _GcrCertificateExporterClass {
+ GObjectClass parent_class;
+};
+
+GType _gcr_certificate_exporter_get_type (void);
+
+GcrCertificateExporter* _gcr_certificate_exporter_new (GcrCertificate *certificate,
+ const gchar *label,
+ GtkWindow *transient_for);
+
+void _gcr_certificate_exporter_export_async (GcrCertificateExporter *self,
+ GCancellable *cancellable,
+ GAsyncReadyCallback callback,
+ gpointer user_data);
+
+gboolean _gcr_certificate_exporter_export_finish (GcrCertificateExporter *self,
+ GAsyncResult *result,
+ GError **error);
+
+G_END_DECLS
+
+#endif /* __GCR_CERTIFICATE_EXPORTER_H__ */
diff --git a/gcr/gcr-certificate-renderer.c b/gcr/gcr-certificate-renderer.c
index 073d944..760df1e 100644
--- a/gcr/gcr-certificate-renderer.c
+++ b/gcr/gcr-certificate-renderer.c
@@ -20,6 +20,7 @@
#include "config.h"
#include "gcr-certificate.h"
+#include "gcr-certificate-exporter.h"
#include "gcr-certificate-renderer.h"
#include "gcr-display-view.h"
#include "gcr-icons.h"
@@ -174,6 +175,62 @@ on_parsed_dn_part (guint index, GQuark oid, const guchar *value,
g_free (display);
}
+static gboolean
+on_delete_unref_dialog (GtkWidget *widget, GdkEvent *event, gpointer data)
+{
+ g_object_unref (widget);
+ return FALSE;
+}
+
+static void
+on_export_completed (GObject *source, GAsyncResult *result, gpointer user_data)
+{
+ GtkWindow *parent = GTK_WINDOW (user_data);
+ GcrCertificateExporter *exporter = GCR_CERTIFICATE_EXPORTER (source);
+ GError *error = NULL;
+ GtkWidget *dialog;
+
+ if (!_gcr_certificate_exporter_export_finish (exporter, result, &error)) {
+ if (!g_error_matches (error, G_IO_ERROR, G_IO_ERROR_CANCELLED)) {
+ dialog = gtk_message_dialog_new_with_markup (parent,
+ GTK_DIALOG_DESTROY_WITH_PARENT, GTK_MESSAGE_ERROR,
+ GTK_BUTTONS_OK, "<big>%s</big>\n\n%s",
+ _("Couldn't export the certificate."),
+ error->message);
+ gtk_widget_show (dialog);
+ g_signal_connect (dialog, "delete-event",
+ G_CALLBACK (on_delete_unref_dialog), NULL);
+ }
+ }
+
+ /* Matches ref in on_certificate_export */
+ if (parent)
+ g_object_unref (parent);
+}
+
+static void
+on_certificate_export (GtkMenuItem *menuitem, gpointer user_data)
+{
+ GcrCertificateRenderer *self = GCR_CERTIFICATE_RENDERER (user_data);
+ GcrCertificateExporter *exporter;
+ gchar *label;
+ GtkWidget *parent;
+
+ label = calculate_label (self, NULL);
+
+ parent = gtk_widget_get_toplevel (GTK_WIDGET (menuitem));
+ if (parent && !GTK_IS_WINDOW (parent))
+ parent = NULL;
+
+ exporter = _gcr_certificate_exporter_new (self->pv->certificate, label,
+ GTK_WINDOW (parent));
+
+ g_free (label);
+
+ _gcr_certificate_exporter_export_async (exporter, NULL, on_export_completed,
+ parent ? g_object_ref (parent) : NULL);
+}
+
/* -----------------------------------------------------------------------------
* OBJECT
*/
@@ -291,7 +348,7 @@ gcr_certificate_renderer_class_init (GcrCertificateRendererClass *klass)
}
static void
-gcr_certificate_renderer_real_render (GcrRenderer *renderer, GcrViewer *viewer)
+gcr_certificate_renderer_render (GcrRenderer *renderer, GcrViewer *viewer)
{
GcrCertificateRenderer *self;
gconstpointer data, value;
@@ -461,9 +518,27 @@ gcr_certificate_renderer_real_render (GcrRenderer *renderer, GcrViewer *viewer)
}
static void
+gcr_certificate_renderer_populate_popup (GcrRenderer *self, GcrViewer *viewer,
+ GtkMenu *menu)
+{
+ GtkWidget *item;
+
+ item = gtk_separator_menu_item_new ();
+ gtk_widget_show (item);
+ gtk_menu_shell_prepend (GTK_MENU_SHELL (menu), item);
+
+ item = gtk_menu_item_new_with_label ("Export Certificate...");
+ gtk_widget_show (item);
+ g_signal_connect_data (item, "activate", G_CALLBACK (on_certificate_export),
+ g_object_ref (self), (GClosureNotify)g_object_unref, 0);
+ gtk_menu_shell_prepend (GTK_MENU_SHELL (menu), item);
+}
+
+static void
gcr_renderer_iface_init (GcrRendererIface *iface)
{
- iface->render = gcr_certificate_renderer_real_render;
+ iface->render = gcr_certificate_renderer_render;
+ iface->populate_popup = gcr_certificate_renderer_populate_popup;
}
/* -----------------------------------------------------------------------------
diff --git a/gcr/gcr-display-view.c b/gcr/gcr-display-view.c
index df55b26..f5fb2ec 100644
--- a/gcr/gcr-display-view.c
+++ b/gcr/gcr-display-view.c
@@ -62,6 +62,7 @@ struct _GcrDisplayViewPrivate {
GtkTextTag *content_tag;
GtkTextTag *heading_tag;
GtkTextTag *monospace_tag;
+ GcrDisplayItem *current_item;
gboolean have_measurements;
gint minimal_width;
@@ -347,6 +348,29 @@ lookup_display_item (GcrDisplayView *self, GcrRenderer *renderer)
return item;
}
+static GcrDisplayItem*
+find_item_at_iter (GcrDisplayView *self, GtkTextIter *iter)
+{
+ GHashTableIter hi;
+ GcrDisplayItem *item;
+ gpointer value;
+ GtkTextIter start, end;
+
+ g_hash_table_iter_init (&hi, self->pv->items);
+ while (g_hash_table_iter_next (&hi, NULL, &value)) {
+ item = value;
+
+ gtk_text_buffer_get_iter_at_mark (self->pv->buffer, &start, item->beginning);
+ gtk_text_buffer_get_iter_at_mark (self->pv->buffer, &end, item->ending);
+
+ if (gtk_text_iter_compare (iter, &start) >= 0 &&
+ gtk_text_iter_compare (iter, &end) < 0)
+ return item;
+ }
+
+ return NULL;
+}
+
static void
on_renderer_data_changed (GcrRenderer *renderer, GcrViewer *self)
{
@@ -514,6 +538,32 @@ _gcr_display_view_realize (GtkWidget *widget)
style_display_item (widget, value);
}
+static gboolean
+_gcr_display_view_button_press_event (GtkWidget *widget, GdkEventButton *event)
+{
+ GtkTextView *text_view = GTK_TEXT_VIEW (widget);
+ GcrDisplayView *self = GCR_DISPLAY_VIEW (widget);
+ GcrDisplayItem *item;
+ gboolean handled = FALSE;
+ GtkTextIter iter;
+ gint x, y;
+
+ if (GTK_WIDGET_CLASS (_gcr_display_view_parent_class)->button_press_event)
+ handled = GTK_WIDGET_CLASS (_gcr_display_view_parent_class)->button_press_event (
+ widget, event);
+
+ if (event->window == gtk_text_view_get_window (text_view, GTK_TEXT_WINDOW_TEXT)) {
+ gtk_text_view_window_to_buffer_coords (text_view, GTK_TEXT_WINDOW_TEXT,
+ event->x, event->y, &x, &y);
+ gtk_text_view_get_iter_at_location (text_view, &iter, x, y);
+
+ item = find_item_at_iter (self, &iter);
+ self->pv->current_item = item;
+ }
+
+ return handled;
+}
+
#if GTK_CHECK_VERSION (2,91,0)
static gboolean
@@ -583,10 +633,25 @@ _gcr_display_get_preferred_width (GtkWidget *widget, gint *minimal_width,
#endif /* GTK 3.x */
static void
+_gcr_display_view_populate_popup (GtkTextView *text_view, GtkMenu *menu)
+{
+ GcrDisplayView *self = GCR_DISPLAY_VIEW (text_view);
+
+ if (GTK_TEXT_VIEW_CLASS (_gcr_display_view_parent_class)->populate_popup)
+ GTK_TEXT_VIEW_CLASS (_gcr_display_view_parent_class)->populate_popup (text_view, menu);
+
+ /* Ask the current renderer to add menu items */
+ if (self->pv->current_item)
+ gcr_renderer_popuplate_popup (self->pv->current_item->renderer,
+ GCR_VIEWER (self), menu);
+}
+
+static void
_gcr_display_view_class_init (GcrDisplayViewClass *klass)
{
GObjectClass *gobject_class = G_OBJECT_CLASS (klass);
GtkWidgetClass *widget_class = GTK_WIDGET_CLASS (klass);
+ GtkTextViewClass *text_view_class = GTK_TEXT_VIEW_CLASS (klass);
_gcr_display_view_parent_class = g_type_class_peek_parent (klass);
g_type_class_add_private (klass, sizeof (GcrDisplayViewPrivate));
@@ -596,6 +661,7 @@ _gcr_display_view_class_init (GcrDisplayViewClass *klass)
gobject_class->finalize = _gcr_display_view_finalize;
widget_class->realize = _gcr_display_view_realize;
+ widget_class->button_press_event = _gcr_display_view_button_press_event;
#if GTK_CHECK_VERSION (3,0,0)
widget_class->get_preferred_height = _gcr_display_get_preferred_height;
@@ -607,6 +673,8 @@ _gcr_display_view_class_init (GcrDisplayViewClass *klass)
#else
widget_class->expose_event = _gcr_display_view_expose_event;
#endif
+
+ text_view_class->populate_popup = _gcr_display_view_populate_popup;
}
static void
diff --git a/gcr/gcr-renderer.c b/gcr/gcr-renderer.c
index 59ca30a..28bc905 100644
--- a/gcr/gcr-renderer.c
+++ b/gcr/gcr-renderer.c
@@ -89,6 +89,15 @@ gcr_renderer_render (GcrRenderer *self, GcrViewer *viewer)
}
void
+gcr_renderer_popuplate_popup (GcrRenderer *self, GcrViewer *viewer,
+ GtkMenu *menu)
+{
+ g_return_if_fail (GCR_IS_RENDERER (self));
+ if (GCR_RENDERER_GET_INTERFACE (self)->populate_popup)
+ GCR_RENDERER_GET_INTERFACE (self)->populate_popup (self, viewer, menu);
+}
+
+void
gcr_renderer_emit_data_changed (GcrRenderer *self)
{
g_return_if_fail (GCR_IS_RENDERER (self));
diff --git a/gcr/gcr-renderer.h b/gcr/gcr-renderer.h
index a9bde34..bf7e04a 100644
--- a/gcr/gcr-renderer.h
+++ b/gcr/gcr-renderer.h
@@ -45,6 +45,8 @@ struct _GcrRendererIface {
/* virtual */
void (*render) (GcrRenderer *self, GcrViewer *viewer);
+ void (*populate_popup) (GcrRenderer *self, GcrViewer *viewer, GtkMenu *menu);
+
gpointer dummy1;
gpointer dummy2;
gpointer dummy3;
@@ -60,6 +62,10 @@ GType gcr_renderer_get_type (void) G_GNUC_
void gcr_renderer_render (GcrRenderer *self,
GcrViewer *viewer);
+void gcr_renderer_popuplate_popup (GcrRenderer *self,
+ GcrViewer *viewer,
+ GtkMenu *menu);
+
void gcr_renderer_emit_data_changed (GcrRenderer *self);
GcrRenderer* gcr_renderer_create (const gchar *label,
diff --git a/gcr/tests/ui-test-certificate.c b/gcr/tests/ui-test-certificate.c
index 2a181ac..cfb2d8d 100644
--- a/gcr/tests/ui-test-certificate.c
+++ b/gcr/tests/ui-test-certificate.c
@@ -53,13 +53,10 @@ chdir_base_dir (char* argv0)
}
static void
-on_parser_parsed (GcrParser *parser, gpointer unused)
+on_parser_parsed (GcrParser *parser, gpointer user_data)
{
GcrCertificateWidget *details;
- GtkDialog *dialog;
-
- dialog = GTK_DIALOG (gtk_dialog_new ());
- g_object_ref_sink (dialog);
+ GtkDialog *dialog = GTK_DIALOG (user_data);
details = g_object_new (GCR_TYPE_CERTIFICATE_WIDGET,
"attributes", gcr_parser_get_parsed_attributes (parser),
@@ -69,10 +66,6 @@ on_parser_parsed (GcrParser *parser, gpointer unused)
gtk_container_add (GTK_CONTAINER (gtk_dialog_get_content_area (dialog)), GTK_WIDGET (details));
gtk_container_set_border_width (GTK_CONTAINER (dialog), 20);
- gtk_dialog_run (dialog);
- gtk_widget_destroy (GTK_WIDGET (dialog));
-
- g_object_unref (dialog);
}
static void
@@ -82,17 +75,27 @@ test_certificate (const gchar *path)
GError *err = NULL;
guchar *data;
gsize n_data;
+ GtkWidget *dialog;
if (!g_file_get_contents (path, (gchar**)&data, &n_data, NULL))
g_error ("couldn't read file: %s", path);
+ dialog = gtk_dialog_new ();
+ g_object_ref_sink (dialog);
+
parser = gcr_parser_new ();
- g_signal_connect (parser, "parsed", G_CALLBACK (on_parser_parsed), NULL);
+ g_signal_connect (parser, "parsed", G_CALLBACK (on_parser_parsed), dialog);
if (!gcr_parser_parse_data (parser, data, n_data, &err))
g_error ("couldn't parse data: %s", err->message);
g_object_unref (parser);
g_free (data);
+
+ gtk_widget_show (dialog);
+ g_signal_connect (dialog, "delete-event", G_CALLBACK (gtk_main_quit), NULL);
+ gtk_main ();
+
+ g_object_unref (dialog);
}
int
[
Date Prev][
Date Next] [
Thread Prev][
Thread Next]
[
Thread Index]
[
Date Index]
[
Author Index]