[network-manager-applet/lr/pkcs11: 9/12] libnma: add PKCS#11 object chooser dialog



commit 2888de38c30b3ebb6e7b13874599da8fb380e242
Author: Lubomir Rintel <lkundrak v3 sk>
Date:   Fri Feb 24 18:37:12 2017 +0100

    libnma: add PKCS#11 object chooser dialog
    
    Selects a key or a password from a PKCS#11 token and returns an URI.
    Allows unlocking the token with a PIN.

 Makefile.am                                  |   12 +-
 po/POTFILES.in                               |    2 +
 src/libnma/nma-pkcs11-cert-chooser-dialog.c  |  659 ++++++++++++++++++++++++++
 src/libnma/nma-pkcs11-cert-chooser-dialog.h  |   54 +++
 src/libnma/nma-pkcs11-cert-chooser-dialog.ui |  153 ++++++
 src/libnma/nma.gresource.xml                 |    1 +
 6 files changed, 877 insertions(+), 4 deletions(-)
---
diff --git a/Makefile.am b/Makefile.am
index cb8ec07..3ef3e9f 100644
--- a/Makefile.am
+++ b/Makefile.am
@@ -494,10 +494,12 @@ libnma_h_priv = \
 
 if WITH_GCR
 libnma_h_priv += \
-       src/libnma/nma-pkcs11-token-login-dialog.h
+       src/libnma/nma-pkcs11-token-login-dialog.h \
+       src/libnma/nma-pkcs11-cert-chooser-dialog.h
 
 libnma_c_real += \
-       src/libnma/nma-pkcs11-token-login-dialog.c
+       src/libnma/nma-pkcs11-token-login-dialog.c \
+       src/libnma/nma-pkcs11-cert-chooser-dialog.c
 endif
 
 src_libnma_libnmadir = $(includedir)/libnma
@@ -867,12 +869,14 @@ CFILE_GLOB = $(top_srcdir)/src/libnma/*.c
 IGNORE_HFILES = \
        nma-resources.h \
        nma-file-cert-chooser.h \
-       nma-pkcs11-token-login-dialog.h
+       nma-pkcs11-token-login-dialog.h \
+       nma-pkcs11-cert-chooser-dialog.h
 
 mkdb_ignore_c_files = \
        nma-resources.c \
        nma-file-cert-chooser.c \
-       nma-pkcs11-token-login-dialog.c
+       nma-pkcs11-token-login-dialog.c \
+       nma-pkcs11-cert-chooser-dialog.c
 
 MKDB_OPTIONS = --ignore-files "$(IGNORE_HFILES) $(mkdb_ignore_c_files)"
 
diff --git a/po/POTFILES.in b/po/POTFILES.in
index a735f38..98aea74 100644
--- a/po/POTFILES.in
+++ b/po/POTFILES.in
@@ -78,6 +78,8 @@ src/ethernet-dialog.c
 src/libnma/nma-file-cert-chooser.c
 src/libnma/nma-mobile-providers.c
 src/libnma/nma-mobile-wizard.c
+src/libnma/nma-pkcs11-cert-chooser-dialog.c
+[type: gettext/glade]src/libnma/nma-pkcs11-cert-chooser-dialog.ui
 src/libnma/nma-pkcs11-token-login-dialog.c
 [type: gettext/glade]src/libnma/nma-pkcs11-token-login-dialog.ui
 src/libnma/nma-ui-utils.c
diff --git a/src/libnma/nma-pkcs11-cert-chooser-dialog.c b/src/libnma/nma-pkcs11-cert-chooser-dialog.c
new file mode 100644
index 0000000..5f852e2
--- /dev/null
+++ b/src/libnma/nma-pkcs11-cert-chooser-dialog.c
@@ -0,0 +1,659 @@
+/* NetworkManager Applet -- allow user control over networking
+ *
+ * Lubomir Rintel <lkundrak v3 sk>
+ *
+ * This library 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) any later version.
+ *
+ * This library 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 library; if not, write to the
+ * Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
+ * Boston, MA 02110-1301 USA.
+ *
+ * Copyright (C) 2016,2017 Red Hat, Inc.
+ */
+
+#include <string.h>
+
+#include <gtk/gtk.h>
+#include <gck/gck.h>
+#include <gcr/gcr.h>
+
+#include "nm-default.h"
+#include "nma-pkcs11-cert-chooser-dialog.h"
+#include "nma-pkcs11-token-login-dialog.h"
+
+/**
+ * SECTION:nma-pkcs11-cert-chooser-dialog
+ * @title: NMAPkcs11CertChooserDialog
+ * @short_description: The PKCS11 Object Chooser Dialog
+ *
+ * #NMAPkcs11CertChooserDialog selects an object from a PKCS11 token,
+ * optionally allowing the user to specify the PIN and log in.
+ */
+
+enum {
+       COLUMN_LABEL,
+       COLUMN_ISSUER,
+       COLUMN_HAS_KEY,
+       COLUMN_ATTRIBUTES,
+       N_COLUMNS
+};
+
+struct _NMAPkcs11CertChooserDialogPrivate {
+       GckSlot *slot;
+       GtkListStore *cert_store;
+       GtkListStore *key_store;
+       GtkWidget *login_button;
+
+       guchar *pin_value;
+       gulong pin_length;
+       gboolean remember_pin;
+
+       GtkRevealer *error_revealer;
+       GtkLabel *error_label;
+       GtkTreeView *objects_view;
+       GtkTreeViewColumn *list_name_column;
+       GtkCellRenderer *list_name_renderer;
+       GtkTreeViewColumn *list_issued_by_column;
+       GtkCellRenderer *list_issued_by_renderer;
+};
+
+#define NMA_PKCS11_CERT_CHOOSER_DIALOG_GET_PRIVATE(o) (G_TYPE_INSTANCE_GET_PRIVATE ((o), \
+                                                       NMA_TYPE_PKCS11_CERT_CHOOSER_DIALOG, \
+                                                       NMAPkcs11CertChooserDialogPrivate))
+
+G_DEFINE_TYPE_WITH_CODE (NMAPkcs11CertChooserDialog, nma_pkcs11_cert_chooser_dialog, GTK_TYPE_DIALOG,
+                         G_ADD_PRIVATE (NMAPkcs11CertChooserDialog))
+
+#define NMA_RESPONSE_LOGIN 1
+
+enum {
+       PROP_0,
+       PROP_SLOT,
+};
+
+typedef struct {
+       GckAttributes *attrs;
+       gboolean has_key;
+} IdMatchData;
+
+static gboolean
+id_match (GtkTreeModel *model,
+          GtkTreePath *path,
+          GtkTreeIter *iter,
+          gpointer user_data)
+{
+       IdMatchData *data = user_data;
+       GckAttributes *attrs = NULL;
+       const GckAttribute *attr1, *attr2;
+
+       attr1 = gck_attributes_find (data->attrs, CKA_ID);
+       if (!attr1 || !attr1->value || !attr1->length)
+               goto out;
+
+       gtk_tree_model_get (model, iter, COLUMN_ATTRIBUTES, &attrs, -1);
+       attr2 = gck_attributes_find (attrs, CKA_ID);
+       if (!attr2 || !attr2->value || !attr2->length)
+               goto out;
+
+       if (attr1->length != attr2->length)
+               goto out;
+
+       if (memcmp (attr1->value, attr2->value, attr1->length))
+               goto out;
+
+       data->has_key = TRUE;
+       gtk_list_store_set (GTK_LIST_STORE (model), iter,
+                           COLUMN_HAS_KEY, TRUE, -1);
+
+       if (attrs)
+               gck_attributes_unref (attrs);
+out:
+       return data->has_key;
+}
+
+static void
+object_details (GObject *source_object, GAsyncResult *res, gpointer user_data)
+{
+       GckObject *object = GCK_OBJECT (source_object);
+       NMAPkcs11CertChooserDialog *self = NMA_PKCS11_CERT_CHOOSER_DIALOG (user_data);
+       NMAPkcs11CertChooserDialogPrivate *priv = NMA_PKCS11_CERT_CHOOSER_DIALOG_GET_PRIVATE (self);
+       GckAttributes *attrs;
+       GtkTreeIter iter;
+       CK_OBJECT_CLASS cka_class;
+       const GckAttribute *attr;
+       GcrCertificate *cert;
+       gchar *label, *issuer;
+       GError *error = NULL;
+       GtkListStore *store1, *store2;
+       IdMatchData data;
+
+       attrs = gck_object_get_finish (object, res, &error);
+       if (!attrs) {
+               /* No better idea than to just ignore the object. */
+               g_warning ("Error getting attributes: %s\n", error->message);
+               g_error_free (error);
+               goto out;
+       }
+
+       if (!gck_attributes_find_ulong (attrs, CKA_CLASS, &cka_class)) {
+               g_warning ("An object without CKA_CLASS\n");
+               goto out;
+       }
+
+       switch (cka_class) {
+       case CKO_CERTIFICATE:
+               store1 = priv->cert_store;
+               store2 = priv->key_store;
+               break;
+       case CKO_PRIVATE_KEY:
+               store1 = priv->key_store;
+               store2 = priv->cert_store;
+               break;
+       default:
+               goto out;
+       }
+
+       /* See if there's a matching object in another store. */
+       data.attrs = attrs;
+       data.has_key = FALSE;
+       gtk_tree_model_foreach (GTK_TREE_MODEL (store2),
+                               id_match,
+                               &data);
+
+       attr = gck_attributes_find (attrs, CKA_VALUE);
+       if (attr && attr->value && attr->length) {
+               cert = gcr_simple_certificate_new (attr->value, attr->length);
+               label = gcr_certificate_get_subject_name (cert);
+               issuer = gcr_certificate_get_issuer_name (cert);
+               g_object_unref (cert);
+       } else {
+               attr = gck_attributes_find (attrs, CKA_LABEL);
+               if (attr && attr->value && attr->length) {
+                       label = g_malloc (attr->length + 1);
+                       memcpy (label, attr->value, attr->length);
+                       label[attr->length] = '\0';
+               } else {
+                       label = g_strdup (_("(Unknown)"));
+               }
+               issuer = g_memdup ("", 1);
+       }
+
+       gtk_list_store_append (store1, &iter);
+       gtk_list_store_set (store1, &iter,
+                           COLUMN_LABEL, label,
+                           COLUMN_ISSUER, issuer,
+                           COLUMN_HAS_KEY, data.has_key,
+                           COLUMN_ATTRIBUTES, attrs,
+                           -1);
+
+       g_free (label);
+       g_free (issuer);
+
+out:
+       if (attrs)
+               gck_attributes_unref (attrs);
+}
+
+static void
+next_object (GObject *obj, GAsyncResult *res, gpointer user_data)
+{
+       NMAPkcs11CertChooserDialog *self = NMA_PKCS11_CERT_CHOOSER_DIALOG (user_data);
+       GckEnumerator *enm = GCK_ENUMERATOR (obj);
+       GList *objects;
+       GList *iter;
+       GError *error = NULL;
+
+       objects = gck_enumerator_next_finish (enm, res, &error);
+       if (error) {
+               /* No better idea than to just ignore the object. */
+               g_warning ("Error getting object: %s", error->message);
+               g_error_free (error);
+               return;
+       }
+
+       for (iter = objects; iter; iter = iter->next) {
+               GckObject *object = GCK_OBJECT (iter->data);
+               const gulong attr_types[] = { CKA_ID, CKA_LABEL, CKA_ISSUER,
+                                             CKA_VALUE, CKA_CLASS };
+
+               gck_object_get_async (object, attr_types,
+                                     sizeof(attr_types) / sizeof(attr_types[0]),
+                                     NULL, object_details, self);
+       }
+
+       gck_list_unref_free (objects);
+}
+
+static void
+reload_slot (NMAPkcs11CertChooserDialog *self, GckSession *session)
+{
+       NMAPkcs11CertChooserDialogPrivate *priv = NMA_PKCS11_CERT_CHOOSER_DIALOG_GET_PRIVATE (self);
+       GckEnumerator *enm;
+
+       gtk_list_store_clear (priv->key_store);
+       gtk_list_store_clear (priv->cert_store);
+       enm = gck_session_enumerate_objects (session, gck_attributes_new_empty (GCK_INVALID));
+       gck_enumerator_next_async (enm, -1, NULL, next_object, self);
+}
+
+static void
+logged_in (GObject *obj, GAsyncResult *res, gpointer user_data)
+{
+       NMAPkcs11CertChooserDialog *self = NMA_PKCS11_CERT_CHOOSER_DIALOG (user_data);
+       NMAPkcs11CertChooserDialogPrivate *priv = NMA_PKCS11_CERT_CHOOSER_DIALOG_GET_PRIVATE (self);
+       GckSession *session = GCK_SESSION (obj);
+       GError *error = NULL;
+
+       if (!gck_session_login_finish (session, res, &error)) {
+               g_prefix_error (&error, _("Error logging in: "));
+               gtk_label_set_label (priv->error_label, error->message);
+               gtk_revealer_set_reveal_child (priv->error_revealer, TRUE);
+               g_error_free (error);
+       } else {
+               gtk_revealer_set_reveal_child (priv->error_revealer, FALSE);
+               gtk_widget_set_sensitive (priv->login_button, FALSE);
+               reload_slot (self, session);
+               g_clear_object (&session);
+       }
+}
+
+static void
+session_opened (GObject *obj, GAsyncResult *res, gpointer user_data)
+{
+       NMAPkcs11CertChooserDialog *self = NMA_PKCS11_CERT_CHOOSER_DIALOG (user_data);
+       NMAPkcs11CertChooserDialogPrivate *priv = NMA_PKCS11_CERT_CHOOSER_DIALOG_GET_PRIVATE (self);
+       GckSession *session;
+       GError *error = NULL;
+
+       session = gck_slot_open_session_finish (priv->slot, res, &error);
+       if (error) {
+               g_prefix_error (&error, _("Error opening a session: "));
+               gtk_label_set_label (priv->error_label, error->message);
+               gtk_revealer_set_reveal_child (priv->error_revealer, TRUE);
+               g_error_free (error);
+               return;
+       }
+
+       if (priv->pin_value) {
+               gck_session_login_async (session, CKU_USER,
+                                        priv->pin_value, priv->pin_length,
+                                        NULL, logged_in, self);
+       } else {
+               reload_slot (self, session);
+               g_clear_object (&session);
+       }
+}
+
+static void
+row_activated (GtkTreeView *tree_view, GtkTreePath *path,
+               GtkTreeViewColumn *column, gpointer user_data)
+{
+       if (gtk_window_activate_default (GTK_WINDOW (user_data)))
+               return;
+}
+
+static void
+cursor_changed (GtkTreeView *tree_view, gpointer user_data)
+{
+       NMAPkcs11CertChooserDialog *self = NMA_PKCS11_CERT_CHOOSER_DIALOG (user_data);
+       gchar *uri;
+
+       uri = nma_pkcs11_cert_chooser_dialog_get_uri (self);
+       gtk_dialog_set_response_sensitive (GTK_DIALOG (self), GTK_RESPONSE_ACCEPT, uri != NULL);
+       g_free (uri);
+}
+
+static void
+error_close (GtkInfoBar *bar, gint response_id, gpointer user_data)
+{
+       NMAPkcs11CertChooserDialog *self = user_data;
+       NMAPkcs11CertChooserDialogPrivate *priv = self->priv;
+
+       gtk_revealer_set_reveal_child (priv->error_revealer, FALSE);
+}
+
+static void
+login_clicked (GtkButton *button, gpointer user_data)
+{
+       NMAPkcs11CertChooserDialog *self = NMA_PKCS11_CERT_CHOOSER_DIALOG (user_data);
+       NMAPkcs11CertChooserDialogPrivate *priv = NMA_PKCS11_CERT_CHOOSER_DIALOG_GET_PRIVATE (self);
+       GtkWidget *dialog;
+       GckTokenInfo *token_info;
+       gboolean has_pin_pad = FALSE;
+
+       /* See if the token has a PIN pad. */
+       token_info = gck_slot_get_token_info (priv->slot);
+       g_return_if_fail (token_info);
+       if (token_info->flags & CKF_PROTECTED_AUTHENTICATION_PATH)
+               has_pin_pad = TRUE;
+       gck_token_info_free (token_info);
+
+       if (priv->pin_value)
+               g_free (priv->pin_value);
+
+       if (has_pin_pad) {
+               /* Login with empty credentials makes the token
+                * log in on its PIN pad. */
+               priv->pin_length = 0;
+               priv->pin_value =  g_memdup ("", 1);
+               priv->remember_pin = TRUE;
+               gck_slot_open_session_async (priv->slot, GCK_SESSION_READ_ONLY, NULL, session_opened, self);
+               return;
+       }
+
+       /* The token doesn't have a PIN pad. Ask for PIN. */
+        dialog = nma_pkcs11_token_login_dialog_new (priv->slot);
+       gtk_window_set_transient_for (GTK_WINDOW (dialog), GTK_WINDOW (self));
+       gtk_dialog_set_default_response (GTK_DIALOG (dialog), GTK_RESPONSE_ACCEPT);
+
+       if (gtk_dialog_run (GTK_DIALOG (dialog)) == GTK_RESPONSE_ACCEPT) {
+               priv->pin_length = nma_pkcs11_token_login_dialog_get_pin_length 
(NMA_PKCS11_TOKEN_LOGIN_DIALOG (dialog));
+               priv->pin_value = g_memdup (nma_pkcs11_token_login_dialog_get_pin_value 
(NMA_PKCS11_TOKEN_LOGIN_DIALOG (dialog)),
+                                           priv->pin_length + 1);
+               priv->remember_pin = nma_pkcs11_token_login_dialog_get_remember_pin 
(NMA_PKCS11_TOKEN_LOGIN_DIALOG (dialog));
+               gck_slot_open_session_async (priv->slot, GCK_SESSION_READ_ONLY, NULL, session_opened, self);
+       }
+
+       gtk_widget_destroy (dialog);
+}
+
+static void
+get_property (GObject *object, guint prop_id, GValue *value, GParamSpec *pspec)
+{
+       NMAPkcs11CertChooserDialog *self = NMA_PKCS11_CERT_CHOOSER_DIALOG (object);
+       NMAPkcs11CertChooserDialogPrivate *priv = NMA_PKCS11_CERT_CHOOSER_DIALOG_GET_PRIVATE (self);
+
+       switch (prop_id) {
+       case PROP_SLOT:
+               if (priv->slot)
+                       g_value_set_object (value, priv->slot);
+               break;
+       default:
+               G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
+       }
+}
+
+static void
+set_property (GObject *object, guint prop_id, const GValue *value, GParamSpec *pspec)
+{
+       NMAPkcs11CertChooserDialog *self = NMA_PKCS11_CERT_CHOOSER_DIALOG (object);
+       NMAPkcs11CertChooserDialogPrivate *priv = NMA_PKCS11_CERT_CHOOSER_DIALOG_GET_PRIVATE (self);
+       GckTokenInfo *token_info;
+
+       switch (prop_id) {
+       case PROP_SLOT:
+               priv->slot = g_value_dup_object (value);
+               token_info = gck_slot_get_token_info (priv->slot);
+               g_return_if_fail (token_info);
+               if ((token_info->flags & CKF_LOGIN_REQUIRED) == 0)
+                       gtk_widget_set_sensitive (priv->login_button, FALSE);
+               gck_token_info_free (token_info);
+               gck_slot_open_session_async (priv->slot, GCK_SESSION_READ_ONLY, NULL, session_opened, self);
+               break;
+       default:
+               G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
+       }
+}
+
+static void
+finalize (GObject *object)
+{
+       NMAPkcs11CertChooserDialog *self = NMA_PKCS11_CERT_CHOOSER_DIALOG (object);
+       NMAPkcs11CertChooserDialogPrivate *priv = NMA_PKCS11_CERT_CHOOSER_DIALOG_GET_PRIVATE (self);
+
+       g_clear_object (&priv->cert_store);
+       g_clear_object (&priv->key_store);
+       g_clear_object (&priv->slot);
+
+       if (priv->pin_value) {
+               g_free (priv->pin_value);
+               priv->pin_value = NULL;
+       }
+
+       G_OBJECT_CLASS (nma_pkcs11_cert_chooser_dialog_parent_class)->finalize (object);
+}
+
+static void
+nma_pkcs11_cert_chooser_dialog_class_init (NMAPkcs11CertChooserDialogClass *klass)
+{
+        GObjectClass *object_class = G_OBJECT_CLASS (klass);
+       GtkWidgetClass *widget_class = GTK_WIDGET_CLASS (klass);
+
+       object_class->get_property = get_property;
+       object_class->set_property = set_property;
+       object_class->finalize = finalize;
+
+       g_object_class_install_property (object_class, PROP_SLOT,
+               g_param_spec_object ("slot", "PKCS#11 Slot", "PKCS#11 Slot",
+                                    GCK_TYPE_SLOT, G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
+
+       gtk_widget_class_set_template_from_resource (widget_class,
+                                                    
"/org/freedesktop/network-manager-applet/nma-pkcs11-cert-chooser-dialog.ui");
+
+       gtk_widget_class_bind_template_child_private (widget_class, NMAPkcs11CertChooserDialog, objects_view);
+       gtk_widget_class_bind_template_child_private (widget_class, NMAPkcs11CertChooserDialog, 
list_name_column);
+       gtk_widget_class_bind_template_child_private (widget_class, NMAPkcs11CertChooserDialog, 
list_name_renderer);
+       gtk_widget_class_bind_template_child_private (widget_class, NMAPkcs11CertChooserDialog, 
list_issued_by_column);
+       gtk_widget_class_bind_template_child_private (widget_class, NMAPkcs11CertChooserDialog, 
list_issued_by_renderer);
+       gtk_widget_class_bind_template_child_private (widget_class, NMAPkcs11CertChooserDialog, 
error_revealer);
+       gtk_widget_class_bind_template_child_private (widget_class, NMAPkcs11CertChooserDialog, error_label);
+       gtk_widget_class_bind_template_child_private (widget_class, NMAPkcs11CertChooserDialog, login_button);
+
+       gtk_widget_class_bind_template_callback (widget_class, row_activated);
+       gtk_widget_class_bind_template_callback (widget_class, cursor_changed);
+       gtk_widget_class_bind_template_callback (widget_class, error_close);
+       gtk_widget_class_bind_template_callback (widget_class, login_clicked);
+}
+
+static void
+nma_pkcs11_cert_chooser_dialog_init (NMAPkcs11CertChooserDialog *self)
+{
+       NMAPkcs11CertChooserDialogPrivate *priv;
+
+       self->priv = nma_pkcs11_cert_chooser_dialog_get_instance_private (self);
+       priv = NMA_PKCS11_CERT_CHOOSER_DIALOG_GET_PRIVATE (self);
+
+       gtk_widget_init_template (GTK_WIDGET (self));
+
+       gtk_tree_view_column_set_attributes (priv->list_name_column,
+                                            priv->list_name_renderer,
+                                            "text", 0, NULL);
+       gtk_tree_view_column_set_attributes (priv->list_issued_by_column,
+                                            priv->list_issued_by_renderer,
+                                            "text", 1, NULL);
+
+       priv->cert_store = gtk_list_store_new (N_COLUMNS,
+                                              G_TYPE_STRING,
+                                              G_TYPE_STRING,
+                                              G_TYPE_BOOLEAN,
+                                              GCK_TYPE_ATTRIBUTES);
+       priv->key_store = gtk_list_store_new (N_COLUMNS,
+                                             G_TYPE_STRING,
+                                             G_TYPE_STRING,
+                                             G_TYPE_BOOLEAN,
+                                             GCK_TYPE_ATTRIBUTES);
+}
+
+static GtkWidget *
+nma_pkcs11_cert_chooser_dialog_new_valist (GckSlot *slot,
+                                           CK_OBJECT_CLASS object_class,
+                                           const gchar *title, GtkWindow *parent,
+                                           GtkDialogFlags flags,
+                                           const gchar *first_button_text,
+                                           va_list varargs)
+{
+       NMAPkcs11CertChooserDialogPrivate *priv;
+       GtkWidget *self;
+       const char *button_text = first_button_text;
+       gint response_id;
+
+       self = g_object_new (NMA_TYPE_PKCS11_CERT_CHOOSER_DIALOG,
+                            "use-header-bar", !!(flags & GTK_DIALOG_USE_HEADER_BAR),
+                            "title", title,
+                            "slot", slot,
+                            NULL);
+
+       priv = NMA_PKCS11_CERT_CHOOSER_DIALOG_GET_PRIVATE (self);
+       switch (object_class) {
+       case CKO_CERTIFICATE:
+               gtk_tree_view_set_model (GTK_TREE_VIEW (priv->objects_view),
+                                        GTK_TREE_MODEL (priv->cert_store));
+               break;
+       case CKO_PRIVATE_KEY:
+               gtk_tree_view_set_model (GTK_TREE_VIEW (priv->objects_view),
+                                        GTK_TREE_MODEL (priv->key_store));
+               break;
+       default:
+               g_warn_if_reached ();
+       }
+
+       if (parent)
+               gtk_window_set_transient_for (GTK_WINDOW (self), parent);
+
+       while (button_text) {
+               response_id = va_arg (varargs, gint);
+               gtk_dialog_add_button (GTK_DIALOG (self), button_text, response_id);
+               button_text = va_arg (varargs, const gchar *);
+       }
+
+       gtk_dialog_set_default_response (GTK_DIALOG (self), GTK_RESPONSE_ACCEPT);
+       gtk_dialog_set_response_sensitive (GTK_DIALOG (self), GTK_RESPONSE_ACCEPT, FALSE);
+
+       return self;
+}
+
+/**
+ * nma_pkcs11_cert_chooser_dialog_get_uri:
+ * @dialog: the #NMAPkcs11CertChooserDialog instance
+ *
+ * Obtain the URI of the selected obejct.
+ *
+ * Returns: the URI or %NULL if none was selected.
+ */
+gchar *
+nma_pkcs11_cert_chooser_dialog_get_uri (NMAPkcs11CertChooserDialog *dialog)
+{
+       NMAPkcs11CertChooserDialogPrivate *priv = NMA_PKCS11_CERT_CHOOSER_DIALOG_GET_PRIVATE (dialog);
+       GtkTreeModel *model;
+       GtkTreePath *path;
+       GtkTreeIter iter;
+       GckAttributes *attrs;
+       gboolean has_key;
+       GckBuilder *builder;
+       GckUriData uri_data = { 0, };
+       gchar *uri;
+
+       gtk_tree_view_get_cursor (priv->objects_view, &path, NULL);
+       if (path == NULL)
+               return NULL;
+
+       model = gtk_tree_view_get_model (GTK_TREE_VIEW (priv->objects_view));
+       if (!gtk_tree_model_get_iter (model, &iter, path))
+               g_return_val_if_reached (NULL);
+
+       gtk_tree_model_get (model, &iter,
+                           COLUMN_HAS_KEY, &has_key,
+                           COLUMN_ATTRIBUTES, &attrs, -1);
+
+       builder = gck_builder_new (GCK_BUILDER_NONE);
+       if (has_key) {
+               /* We do have a object with matching id in the other store (a key)
+                * but its other properties (label) may be unset or missing.
+                * Still, we want an URI that matches both. */
+               gck_builder_add_only (builder, attrs, CKA_ID, GCK_INVALID);
+       } else {
+               gck_builder_add_all (builder, attrs);
+       }
+
+       uri_data.attributes = gck_builder_end (builder);
+       uri_data.token_info = gck_slot_get_token_info (priv->slot);
+       uri = gck_uri_build (&uri_data, GCK_URI_FOR_OBJECT_ON_TOKEN);
+
+       gck_attributes_unref (uri_data.attributes);
+       gck_attributes_unref (attrs);
+
+       return uri;
+}
+
+/**
+ * nma_pkcs11_cert_chooser_dialog_get_pin:
+ * @dialog: the #NMAPkcs11CertChooserDialog instance
+ *
+ * Obtain the PIN that was used to unlock the token.
+ *
+ * Returns: the PIN, %NULL if the token was not logged into or an emtpy
+ *   string ("") if the protected authentication path was used.
+ */
+gchar *
+nma_pkcs11_cert_chooser_dialog_get_pin (NMAPkcs11CertChooserDialog *dialog)
+{
+       NMAPkcs11CertChooserDialogPrivate *priv = NMA_PKCS11_CERT_CHOOSER_DIALOG_GET_PRIVATE (dialog);
+
+       return g_strdup ((gchar *) priv->pin_value);
+}
+
+/**
+ * nma_pkcs11_cert_chooser_dialog_get_remember_pin:
+ * @dialog: the #NMAPkcs11CertChooserDialog instance
+ *
+ * Obtain the value of the "Remember PIN" checkbox during the token login.
+ *
+ * Returns: TRUE if the user chose to remember the PIN, FALSE
+ *   if not or if the tokin was not logged into at all.
+ */
+gboolean
+nma_pkcs11_cert_chooser_dialog_get_remember_pin (NMAPkcs11CertChooserDialog *dialog)
+{
+       NMAPkcs11CertChooserDialogPrivate *priv = NMA_PKCS11_CERT_CHOOSER_DIALOG_GET_PRIVATE (dialog);
+
+       return priv->remember_pin;
+}
+
+/**
+ * nma_pkcs11_cert_chooser_dialog_new:
+ * @slot: the PKCS11 slot the token is in
+ * @object_class: CKA_CLASS of object to be selected
+ * @title: The dialog window title
+ * @parent: (allow-none): The parent window or %NULL
+ * @flags: The dialog flags
+ * @first_button_text: (allow-none): The text of the first button
+ * @...: response ID for the first button, texts and response ids for other buttons, terminated with %NULL
+ *
+ * Creates the new #NMAPkcs11CertChooserDialog.
+ *
+ * Returns: newly created #NMAPkcs11CertChooserDialog
+ */
+
+GtkWidget *
+nma_pkcs11_cert_chooser_dialog_new (GckSlot *slot,
+                                    CK_OBJECT_CLASS object_class,
+                                    const gchar *title,
+                                    GtkWindow *parent,
+                                    GtkDialogFlags flags,
+                                    const gchar *first_button_text,
+                                    ...)
+{
+       GtkWidget *result;
+       va_list varargs;
+
+       va_start (varargs, first_button_text);
+       result = nma_pkcs11_cert_chooser_dialog_new_valist (slot,
+                                                           object_class,
+                                                           title,
+                                                           parent,
+                                                           flags,
+                                                           first_button_text,
+                                                           varargs);
+       va_end (varargs);
+
+       return result;
+}
diff --git a/src/libnma/nma-pkcs11-cert-chooser-dialog.h b/src/libnma/nma-pkcs11-cert-chooser-dialog.h
new file mode 100644
index 0000000..b27adfa
--- /dev/null
+++ b/src/libnma/nma-pkcs11-cert-chooser-dialog.h
@@ -0,0 +1,54 @@
+/* NetworkManager Applet -- allow user control over networking
+ *
+ * Lubomir Rintel <lkundrak v3 sk>
+ *
+ * This library 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) any later version.
+ *
+ * This library 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 library; if not, write to the
+ * Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
+ * Boston, MA 02110-1301 USA.
+ *
+ * Copyright (C) 2016,2017 Red Hat, Inc.
+ */
+
+#ifndef __NMA_PKCS11_CERT_CHOOSER_DIALOG_H__
+#define __NMA_PKCS11_CERT_CHOOSER_DIALOG_H__
+
+#include <gtk/gtk.h>
+#include <gck/gck.h>
+
+typedef struct _NMAPkcs11CertChooserDialogPrivate NMAPkcs11CertChooserDialogPrivate;
+
+struct _NMAPkcs11CertChooserDialog {
+        GtkDialog parent_instance;
+
+       NMAPkcs11CertChooserDialogPrivate *priv;
+};
+
+#define NMA_TYPE_PKCS11_CERT_CHOOSER_DIALOG nma_pkcs11_cert_chooser_dialog_get_type ()
+G_DECLARE_FINAL_TYPE (NMAPkcs11CertChooserDialog, nma_pkcs11_cert_chooser_dialog, NMA, 
PKCS11_CERT_CHOOSER_DIALOG, GtkDialog)
+
+GtkWidget *nma_pkcs11_cert_chooser_dialog_new (GckSlot *slot,
+                                               CK_OBJECT_CLASS object_class,
+                                               const gchar *title,
+                                               GtkWindow *parent,
+                                               GtkDialogFlags flags,
+                                               const gchar *first_button_text,
+                                               ...);
+
+gchar *nma_pkcs11_cert_chooser_dialog_get_uri (NMAPkcs11CertChooserDialog *dialog);
+
+gchar *nma_pkcs11_cert_chooser_dialog_get_pin (NMAPkcs11CertChooserDialog *dialog);
+
+gboolean nma_pkcs11_cert_chooser_dialog_get_remember_pin (NMAPkcs11CertChooserDialog *dialog);
+                                                
+#endif /* __NMA_PKCS11_CERT_CHOOSER_DIALOG_H__ */
diff --git a/src/libnma/nma-pkcs11-cert-chooser-dialog.ui b/src/libnma/nma-pkcs11-cert-chooser-dialog.ui
new file mode 100644
index 0000000..3141dba
--- /dev/null
+++ b/src/libnma/nma-pkcs11-cert-chooser-dialog.ui
@@ -0,0 +1,153 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!-- Generated with glade 3.20.0 -->
+<interface domain="gtk30">
+  <requires lib="gtk+" version="3.10"/>
+  <template class="NMAPkcs11CertChooserDialog" parent="GtkDialog">
+    <property name="can_focus">False</property>
+    <property name="role">NMAPkcs11CertChooserDialog</property>
+    <property name="type_hint">dialog</property>
+    <child internal-child="vbox">
+      <object class="GtkBox" id="dialog-vbox1">
+        <property name="visible">True</property>
+        <property name="can_focus">False</property>
+        <property name="orientation">vertical</property>
+        <child internal-child="action_area">
+          <object class="GtkButtonBox" id="dialog-action_area1">
+            <property name="can_focus">False</property>
+            <property name="layout_style">end</property>
+            <child>
+              <object class="GtkButton" id="login_button">
+                <property name="label" translatable="yes">_Unlock token</property>
+                <property name="visible">True</property>
+                <property name="can_focus">True</property>
+                <property name="receives_default">True</property>
+                <property name="use_underline">True</property>
+                <signal name="clicked" handler="login_clicked" swapped="no"/>
+              </object>
+              <packing>
+                <property name="expand">True</property>
+                <property name="fill">True</property>
+                <property name="position">0</property>
+              </packing>
+            </child>
+          </object>
+          <packing>
+            <property name="expand">False</property>
+            <property name="fill">False</property>
+            <property name="pack_type">end</property>
+            <property name="position">0</property>
+          </packing>
+        </child>
+        <child>
+          <object class="GtkBox">
+            <property name="visible">True</property>
+            <property name="can_focus">False</property>
+            <property name="orientation">vertical</property>
+            <child>
+              <object class="GtkRevealer" id="error_revealer">
+                <property name="visible">True</property>
+                <property name="can_focus">False</property>
+                <child>
+                  <object class="GtkInfoBar" id="error_bar">
+                    <property name="visible">True</property>
+                    <property name="can_focus">False</property>
+                    <property name="message_type">error</property>
+                    <property name="show_close_button">True</property>
+                    <signal name="response" handler="error_close" swapped="no"/>
+                    <child internal-child="action_area">
+                      <object class="GtkButtonBox">
+                        <property name="can_focus">False</property>
+                      </object>
+                      <packing>
+                        <property name="expand">False</property>
+                        <property name="fill">False</property>
+                        <property name="position">0</property>
+                      </packing>
+                    </child>
+                    <child internal-child="content_area">
+                      <object class="GtkBox">
+                        <property name="can_focus">False</property>
+                        <child>
+                          <object class="GtkLabel" id="error_label">
+                            <property name="visible">True</property>
+                            <property name="can_focus">False</property>
+                          </object>
+                          <packing>
+                            <property name="expand">False</property>
+                            <property name="fill">True</property>
+                            <property name="position">0</property>
+                          </packing>
+                        </child>
+                      </object>
+                      <packing>
+                        <property name="expand">False</property>
+                        <property name="fill">False</property>
+                        <property name="position">0</property>
+                      </packing>
+                    </child>
+                  </object>
+                </child>
+              </object>
+              <packing>
+                <property name="expand">False</property>
+                <property name="fill">True</property>
+                <property name="position">0</property>
+              </packing>
+            </child>
+            <child>
+              <object class="GtkScrolledWindow">
+                <property name="width_request">300</property>
+                <property name="height_request">200</property>
+                <property name="visible">True</property>
+                <property name="can_focus">False</property>
+                <property name="hscrollbar_policy">never</property>
+                <child>
+                  <object class="GtkTreeView" id="objects_view">
+                    <property name="visible">True</property>
+                    <property name="can_focus">False</property>
+                    <property name="enable_search">False</property>
+                    <signal name="cursor-changed" handler="cursor_changed" swapped="no"/>
+                    <signal name="row-activated" handler="row_activated" swapped="no"/>
+                    <child internal-child="selection">
+                      <object class="GtkTreeSelection" id="objects_view_selection"/>
+                    </child>
+                    <child>
+                      <object class="GtkTreeViewColumn" id="list_name_column">
+                        <property name="title" translatable="yes">Name</property>
+                        <child>
+                          <object class="GtkCellRendererText" id="list_name_renderer">
+                            <property name="xpad">6</property>
+                          </object>
+                        </child>
+                      </object>
+                    </child>
+                    <child>
+                      <object class="GtkTreeViewColumn" id="list_issued_by_column">
+                        <property name="title" translatable="yes">Issued By</property>
+                        <child>
+                          <object class="GtkCellRendererText" id="list_issued_by_renderer">
+                            <property name="xpad">6</property>
+                          </object>
+                        </child>
+                      </object>
+                    </child>
+                  </object>
+                </child>
+              </object>
+              <packing>
+                <property name="expand">True</property>
+                <property name="fill">True</property>
+                <property name="position">1</property>
+              </packing>
+            </child>
+          </object>
+          <packing>
+            <property name="expand">True</property>
+            <property name="fill">True</property>
+            <property name="position">1</property>
+          </packing>
+        </child>
+      </object>
+    </child>
+  </template>
+</interface>
diff --git a/src/libnma/nma.gresource.xml b/src/libnma/nma.gresource.xml
index a9133e4..24d92fb 100644
--- a/src/libnma/nma.gresource.xml
+++ b/src/libnma/nma.gresource.xml
@@ -3,5 +3,6 @@
        <gresource prefix="/org/freedesktop/network-manager-applet">
                <file preprocess="xml-stripblanks">wifi.ui</file>
                <file preprocess="xml-stripblanks">nma-pkcs11-token-login-dialog.ui</file>
+               <file preprocess="xml-stripblanks">nma-pkcs11-cert-chooser-dialog.ui</file>
        </gresource>
 </gresources>


[Date Prev][Date Next]   [Thread Prev][Thread Next]   [Thread Index] [Date Index] [Author Index]