[seahorse] pkcs11: Initial port of pkcs11 code to vala



commit e6892135a287994680c551a72aeff3287b421e91
Author: Stef Walter <stefw gnome org>
Date:   Mon Mar 3 09:06:21 2014 +0100

    pkcs11: Initial port of pkcs11 code to vala
    
    Some bits are not yet ported, but this is the basic effort.
    
    Bump the Gcr dependency to 3.11.91, which is as yet unreleased
    work on that library.

 common/config.vapi                   |   11 +
 configure.ac                         |    4 +-
 libseahorse/seahorse-util.c          |   19 +
 libseahorse/seahorse-util.h          |    5 +
 pkcs11/Makefile.am                   |   26 +-
 pkcs11/certificate-der-exporter.vala |    2 +-
 pkcs11/pkcs11-certificate.vala       |  243 +++++++
 pkcs11/pkcs11-deleter.vala           |   83 +++
 pkcs11/pkcs11-key-deleter.vala       |   78 +++
 pkcs11/pkcs11-module.vala            |    2 +-
 pkcs11/pkcs11-private-key.vala       |  132 ++++
 pkcs11/pkcs11-properties.vala        |  226 +++++++
 pkcs11/pkcs11-request.vala           |  157 +++++
 pkcs11/pkcs11-token.vala             |  490 +++++++++++++++
 pkcs11/seahorse-certificate.c        |    1 -
 pkcs11/seahorse-certificate.h        |   60 --
 pkcs11/seahorse-pkcs11-backend.c     |   11 +-
 pkcs11/seahorse-pkcs11-backend.h     |    1 -
 pkcs11/seahorse-pkcs11-deleter.c     |  234 -------
 pkcs11/seahorse-pkcs11-deleter.h     |   54 --
 pkcs11/seahorse-pkcs11-generate.c    |   11 +-
 pkcs11/seahorse-pkcs11-helpers.c     |   48 --
 pkcs11/seahorse-pkcs11-helpers.h     |   33 -
 pkcs11/seahorse-pkcs11-key-deleter.c |  149 -----
 pkcs11/seahorse-pkcs11-key-deleter.h |   41 --
 pkcs11/seahorse-pkcs11-properties.c  |  462 --------------
 pkcs11/seahorse-pkcs11-properties.h  |   43 --
 pkcs11/seahorse-pkcs11-request.c     |  375 -----------
 pkcs11/seahorse-pkcs11-request.h     |   33 -
 pkcs11/seahorse-private-key.c        |  403 ------------
 pkcs11/seahorse-private-key.h        |   58 --
 pkcs11/seahorse-token.c              | 1142 ----------------------------------
 pkcs11/seahorse-token.h              |   70 --
 po/POTFILES.in                       |   14 +-
 po/POTFILES.skip                     |    7 +
 35 files changed, 1486 insertions(+), 3242 deletions(-)
---
diff --git a/common/config.vapi b/common/config.vapi
index 056021f..98e8474 100644
--- a/common/config.vapi
+++ b/common/config.vapi
@@ -14,6 +14,8 @@ namespace Config
  * the current C code base. In general we want to port to vala instead of
  * listing stuff here. Otherwise things will get unmanageable.
  */
+
+[CCode (cheader_filename = "libseahorse/seahorse-util.h")]
 namespace Seahorse {
 
 [CCode (cheader_filename = "libseahorse/seahorse-prefs.h")]
@@ -27,4 +29,13 @@ namespace Application {
        public unowned Gtk.Application @get();
 }
 
+public static GLib.HashFunc<ulong?> ulong_hash;
+public static GLib.EqualFunc<ulong?> ulong_equal;
+
+[CCode (cheader_filename = "libseahorse/seahorse-interaction.h")]
+public class Interaction : GLib.TlsInteraction {
+       public Interaction(Gtk.Window? parent);
+       public Gtk.Window? parent;
+}
+
 }
diff --git a/configure.ac b/configure.ac
index fef9982..771857c 100644
--- a/configure.ac
+++ b/configure.ac
@@ -3,8 +3,8 @@ AC_INIT([seahorse], [3.11.91],
         [http://bugzilla.gnome.org/enter_bug.cgi?product=seahorse],
         [seahorse])
 
-GCK_REQUIRED=3.3.4
-GCR_REQUIRED=3.9.1
+GCK_REQUIRED=3.11.91
+GCR_REQUIRED=3.11.91
 
 GTK_REQ=3.4.0
 GTK_MAX=GTK_VERSION_3_4
diff --git a/libseahorse/seahorse-util.c b/libseahorse/seahorse-util.c
index 3bf3cd9..e77931a 100644
--- a/libseahorse/seahorse-util.c
+++ b/libseahorse/seahorse-util.c
@@ -570,3 +570,22 @@ seahorse_util_parse_version (const char *version)
        g_strfreev(tokens);
        return ret;
 }
+
+guint
+seahorse_ulong_hash (gconstpointer v)
+{
+       const signed char *p = v;
+       guint32 i, h = *p;
+
+       for(i = 0; i < sizeof (gulong); ++i)
+               h = (h << 5) - h + *(p++);
+
+       return h;
+}
+
+gboolean
+seahorse_ulong_equal (gconstpointer v1,
+                             gconstpointer v2)
+{
+       return *((const gulong*)v1) == *((const gulong*)v2);
+}
diff --git a/libseahorse/seahorse-util.h b/libseahorse/seahorse-util.h
index 1b8c12b..e95a756 100644
--- a/libseahorse/seahorse-util.h
+++ b/libseahorse/seahorse-util.h
@@ -83,6 +83,11 @@ void        seahorse_util_determine_popup_menu_position  (GtkMenu *menu,
 
 SeahorseVersion seahorse_util_parse_version   (const char *version);
 
+guint       seahorse_ulong_hash    (gconstpointer v);
+
+gboolean    seahorse_ulong_equal   (gconstpointer v1,
+                                    gconstpointer v2);
+
 #define seahorse_util_version(a,b,c,d) ((SeahorseVersion)a << 48) + ((SeahorseVersion)b << 32) \
                                      + ((SeahorseVersion)c << 16) +  (SeahorseVersion)d
 
diff --git a/pkcs11/Makefile.am b/pkcs11/Makefile.am
index b8d8dbf..781347e 100644
--- a/pkcs11/Makefile.am
+++ b/pkcs11/Makefile.am
@@ -13,7 +13,7 @@ AM_CPPFLAGS = -I$(top_builddir) \
        -I$(top_builddir)/common \
        -I$(top_srcdir)/libseahorse \
        $(SEAHORSE_CFLAGS) \
-       -DSEAHORSE_UIDIR=\""$(uidir)"\" \
+       -DUIDIR=\""$(uidir)"\" \
        -DLOCALEDIR=\"$(localedir)\" \
        -DEXECDIR=\""$(seahorselibexecbindir)"\" \
        -DGETTEXT_PACKAGE=\""seahorse\""
@@ -24,34 +24,36 @@ noinst_LTLIBRARIES = \
 
 AM_VALAFLAGS = \
        $(VALA_FLAGS) \
-       --use-header \
        --header=seahorse-pkcs11.h \
+       --use-header \
        --vapidir $(top_builddir)/common \
+       --pkg config \
        --pkg common \
        --pkg gtk+-3.0 \
        --pkg gcr-3 \
+       --pkg gcr-ui-3 \
+       --pkg pkcs11 \
        $(NULL)
 
 libvala_code_la_SOURCES = \
        certificate-der-exporter.vala \
+       pkcs11-certificate.vala \
+       pkcs11-deleter.vala \
+       pkcs11-key-deleter.vala \
        pkcs11-module.vala \
+       pkcs11-private-key.vala \
+       pkcs11-properties.vala \
+       pkcs11-request.vala \
+       pkcs11-token.vala \
        $(NULL)
 
 libvala_code_la_CFLAGS = \
-       -include config.h -w \
+       -include seahorse-pkcs11.h -w \
        $(NULL)
 
 libseahorse_pkcs11_la_SOURCES = \
-       seahorse-certificate.c seahorse-certificate.h \
        seahorse-pkcs11-backend.c seahorse-pkcs11-backend.h \
-       seahorse-pkcs11-deleter.c seahorse-pkcs11-deleter.h \
-       seahorse-pkcs11-helpers.c seahorse-pkcs11-helpers.h \
-       seahorse-pkcs11-generate.c seahorse-pkcs11-generate.h \
-       seahorse-pkcs11-key-deleter.c seahorse-pkcs11-key-deleter.h \
-       seahorse-pkcs11-properties.c seahorse-pkcs11-properties.h \
-       seahorse-pkcs11-request.c seahorse-pkcs11-request.h \
-       seahorse-private-key.c seahorse-private-key.h \
-       seahorse-token.c seahorse-token.h
+       seahorse-pkcs11-generate.c seahorse-pkcs11-generate.h
 
 libseahorse_pkcs11_la_LIBADD = \
        $(top_builddir)/common/libcommon.la \
diff --git a/pkcs11/certificate-der-exporter.vala b/pkcs11/certificate-der-exporter.vala
index 2824d4c..e8789f5 100644
--- a/pkcs11/certificate-der-exporter.vala
+++ b/pkcs11/certificate-der-exporter.vala
@@ -82,4 +82,4 @@ public class CertificateDerExporter : GLib.Object, Exporter {
 
 }
 
-}
\ No newline at end of file
+}
diff --git a/pkcs11/pkcs11-certificate.vala b/pkcs11/pkcs11-certificate.vala
new file mode 100644
index 0000000..93a5891
--- /dev/null
+++ b/pkcs11/pkcs11-certificate.vala
@@ -0,0 +1,243 @@
+/*
+ * Seahorse
+ *
+ * Copyright (C) 2008 Stefan Walter
+ * Copyright (C) 2011 Collabora Ltd.
+ * Copyright (C) 2013 Red Hat, Inc.
+ *
+ * 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.
+ */
+
+namespace Seahorse {
+namespace Pkcs11 {
+
+public class Certificate : Gck.Object, Gcr.Comparable, Gcr.Certificate,
+                           Gck.ObjectCache, Deletable, Exportable, Viewable {
+       public Token? place {
+               owned get { return (Token?)this._token.get(); }
+               set { this._token.set(value); }
+       }
+
+       public Flags object_flags {
+               get { ensure_flags(); return this._flags; }
+       }
+
+       public Gtk.ActionGroup? actions {
+               get { return null; }
+       }
+
+       public PrivateKey? partner {
+               owned get { return (PrivateKey?)this._private_key.get(); }
+               set {
+                       this._private_key.set(value);
+                       this._icon = null;
+                       this.notify_property("partner");
+                       this.notify_property("icon");
+                       this.notify_property("description");
+               }
+       }
+
+       public Gck.Attributes attributes {
+               owned get { return this._attributes; }
+               set {
+                       this._attributes = value;
+                       this.notify_property("attributes");
+               }
+       }
+
+       public bool deletable {
+               get {
+                       var token = this.place;
+                       if (token == null)
+                               return false;
+                       return token.is_deletable(this);
+               }
+       }
+
+       public bool exportable {
+               get { return this._der != null; }
+       }
+
+       public GLib.Icon icon {
+               owned get {
+                       if (this._icon != null)
+                               return this._icon;
+                       var icon = new GLib.ThemedIcon(Gcr.ICON_CERTIFICATE);
+                       if (this._private_key.get() != null) {
+                               var eicon = new GLib.ThemedIcon (Gcr.ICON_KEY);
+                               var emblem = new GLib.Emblem (eicon);
+                               this._icon = new GLib.EmblemedIcon (icon, emblem);
+                       } else {
+                               this._icon = icon;
+                       }
+                       return this._icon;
+               }
+       }
+
+       public string description {
+               owned get {
+                       ensure_flags ();
+                       if (this._private_key.get() != null)
+                               return _("Personal certificate and key");
+                       if ((this._flags & Flags.PERSONAL) == Flags.PERSONAL)
+                               return _("Personal certificate");
+                       else
+                               return _("Certificate");
+               }
+       }
+
+       public string? label {
+               owned get { return get_subject_name(); }
+       }
+
+       public string? subject {
+               owned get { return get_subject_name(); }
+       }
+
+       public string? markup {
+               owned get { return get_markup_text(); }
+       }
+
+       public string? issuer {
+               owned get { return get_issuer_name(); }
+       }
+
+       public GLib.Date expiry {
+               owned get { return get_expiry_date(); }
+       }
+
+       private GLib.WeakRef _token;
+       private Gck.Attributes? _attributes;
+       private unowned Gck.Attribute? _der;
+       private GLib.WeakRef _private_key;
+       private GLib.Icon? _icon;
+       private Flags _flags;
+
+       private static uint8[] EMPTY = { };
+
+       construct {
+               this._flags = (Flags)uint.MAX;
+               this._der = null;
+               this._private_key = GLib.WeakRef(null);
+               this._token = GLib.WeakRef(null);
+
+               this.notify.connect((pspec) => {
+                       if (pspec.name != "attributes")
+                               return;
+                       if (this._attributes != null)
+                               this._der = this._attributes.find(CKA.VALUE);
+                       notify_property ("label");
+                       notify_property ("markup");
+                       notify_property ("subject");
+                       notify_property ("issuer");
+                       notify_property ("expiry");
+               });
+
+               if (this._attributes != null)
+                       this._der = this._attributes.find(CKA.VALUE);
+       }
+
+       public override void dispose() {
+               this.partner = null;
+               base.dispose();
+       }
+
+       public Gtk.Window? create_viewer(Gtk.Window? parent) {
+               var viewer = new Pkcs11.Properties(this, parent);
+               viewer.show();
+               return viewer;
+       }
+
+       public Seahorse.Deleter create_deleter() {
+               Seahorse.Deleter deleter;
+
+               PrivateKey? key = this.partner;
+               if (key == null) {
+                       deleter = new Pkcs11.Deleter(this);
+               } else {
+                       deleter = key.create_deleter();
+                       if (!deleter.add_object(this))
+                               GLib.return_val_if_reached(null);
+               }
+
+               return deleter;
+       }
+
+       public GLib.List<Exporter> create_exporters(ExporterType type) {
+               var exporters = new GLib.List<Exporter>();
+
+               if (this.exportable) {
+                       var exporter = new CertificateDerExporter(this);
+                       exporters.append(exporter);
+               }
+
+               return exporters;
+       }
+
+       public void fill(Gck.Attributes attributes) {
+               Gck.Builder builder = new Gck.Builder(Gck.BuilderFlags.NONE);
+
+               if (this._attributes != null)
+                       builder.add_all(this._attributes);
+               builder.set_all(attributes);
+               this._attributes = builder.steal();
+               this.notify_property("attributes");
+       }
+
+       public unowned uint8[] get_der_data() {
+               if (this._der == null)
+                       return EMPTY;
+               return this._der.get_data();
+       }
+
+       public int compare (Gcr.Comparable? other) {
+               if (other == null)
+                       return -1;
+               unowned uint8[] data1 = this.get_der_data();
+               unowned uint8[] data2 = ((Gcr.Certificate)other).get_der_data();
+               return Gcr.Comparable.memcmp(data1, data2);
+       }
+
+       private Flags calc_is_personal_and_trusted() {
+               ulong category = 0;
+               bool is_ca;
+
+               /* If a matching private key, then this is personal*/
+               if (this._private_key.get() != null)
+                       return Flags.PERSONAL | Flags.TRUSTED;
+
+               if (this._attributes != null &&
+                   this._attributes.find_ulong (CKA.CERTIFICATE_CATEGORY, out category)) {
+                       if (category == 2)
+                               return 0;
+                       else if (category == 1)
+                               return Flags.PERSONAL;
+               }
+
+               if (get_basic_constraints (out is_ca, null))
+                       return is_ca ? 0 : Flags.PERSONAL;
+
+               return Flags.PERSONAL;
+       }
+
+       private void ensure_flags() {
+               if (this._flags == uint.MAX)
+                       this._flags = Flags.EXPORTABLE | calc_is_personal_and_trusted ();
+       }
+}
+
+}
+}
diff --git a/pkcs11/pkcs11-deleter.vala b/pkcs11/pkcs11-deleter.vala
new file mode 100644
index 0000000..34efc0e
--- /dev/null
+++ b/pkcs11/pkcs11-deleter.vala
@@ -0,0 +1,83 @@
+/*
+ * Seahorse
+ *
+ * Copyright (C) 2013 Red Hat Inc.
+ *
+ * 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 redhat com>
+ */
+
+namespace Seahorse {
+namespace Pkcs11 {
+
+public class Deleter : Seahorse.Deleter {
+       protected GLib.List<Gck.Object> objects;
+
+       public override Gtk.Dialog create_confirm(Gtk.Window? parent) {
+               var num = this.objects.length();
+               if (num == 1) {
+                       string label;
+                       this.objects.data.get("label", out label);
+                       return new DeleteDialog(parent, _("Are you sure you want to permanently delete %s?"), 
label);
+               } else {
+                       return new DeleteDialog(parent, GLib.ngettext ("Are you sure you want to permanently 
delete %d certificate?",
+                                                                      "Are you sure you want to permanently 
delete %d certificates?", num), num);
+               }
+       }
+
+       public Deleter(Gck.Object object) {
+               if (!add_object(object))
+                       GLib.assert_not_reached();
+       }
+
+       public override unowned GLib.List<weak GLib.Object> get_objects() {
+               return this.objects;
+       }
+
+       public override bool add_object (GLib.Object obj) {
+               if (obj is Certificate) {
+                       this.objects.append((Gck.Object)obj);
+                       return true;
+               }
+               return false;
+       }
+
+       public override async bool delete(GLib.Cancellable? cancellable) throws GLib.Error {
+               var objects = this.objects.copy();
+               foreach (var object in objects) {
+                       try {
+                               yield object.destroy_async(cancellable);
+
+                               Token? token;
+                               object.get("place", out token);
+                               if (token != null)
+                                       token.remove_object(object);
+
+                       } catch (GLib.Error e) {
+                               /* Ignore objects that have gone away */
+                               if (e.domain != Gck.Error.get_quark() ||
+                                   e.code != CKR.OBJECT_HANDLE_INVALID)
+                                       throw e;
+                       }
+               }
+               return true;
+       }
+}
+
+
+}
+}
diff --git a/pkcs11/pkcs11-key-deleter.vala b/pkcs11/pkcs11-key-deleter.vala
new file mode 100644
index 0000000..3b34dbc
--- /dev/null
+++ b/pkcs11/pkcs11-key-deleter.vala
@@ -0,0 +1,78 @@
+/*
+ * Seahorse
+ *
+ * Copyright (C) 2013 Red Hat Inc.
+ *
+ * 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 redhat com>
+ */
+
+namespace Seahorse {
+namespace Pkcs11 {
+
+class KeyDeleter : Deleter {
+       private Certificate? _cert;
+       private PrivateKey? _key;
+       private string? _label;
+
+       public override Gtk.Dialog create_confirm(Gtk.Window? parent) {
+               var dialog = new DeleteDialog(parent, _("Are you sure you want to permanently delete %s?"), 
this._label);
+               dialog.check_label = _("I understand that this key will be permanently deleted.");
+               dialog.check_require = true;
+               return dialog;
+       }
+
+       public KeyDeleter(Gck.Object cert_or_key) {
+               base(cert_or_key);
+       }
+
+       public override bool add_object (GLib.Object obj) {
+               GLib.Object? partner;
+
+               if (obj is PrivateKey) {
+                       if (this._key != null)
+                               return false;
+                       if (this._cert != null) {
+                               partner = this._cert.partner;
+                               if (partner != obj)
+                                       return false;
+                       }
+                       this._key = (PrivateKey)obj;
+                       this.objects.prepend(this._key);
+
+               } else if (obj is Certificate) {
+                       if (this._cert != null)
+                               return false;
+                       if (this._key != null) {
+                               partner = this._key.partner;
+                               if (partner != obj)
+                                       return false;
+                       }
+                       this._cert = (Certificate)obj;
+                       this.objects.prepend(this._cert);
+               } else {
+                       return false;
+               }
+
+               if (this._label == null)
+                       obj.get("label", out this._label);
+               return true;
+       }
+}
+
+}
+}
diff --git a/pkcs11/pkcs11-module.vala b/pkcs11/pkcs11-module.vala
index 11f59b5..87d5ea2 100644
--- a/pkcs11/pkcs11-module.vala
+++ b/pkcs11/pkcs11-module.vala
@@ -23,4 +23,4 @@ namespace Seahorse {
 
 public const string PKCS11_NAME = "pkcs11";
 
-}
\ No newline at end of file
+}
diff --git a/pkcs11/pkcs11-private-key.vala b/pkcs11/pkcs11-private-key.vala
new file mode 100644
index 0000000..c6adcfa
--- /dev/null
+++ b/pkcs11/pkcs11-private-key.vala
@@ -0,0 +1,132 @@
+/*
+ * Seahorse
+ *
+ * Copyright (C) 2008 Stefan Walter
+ * Copyright (C) 2011 Collabora Ltd.
+ * Copyright (C) 2013 Red Hat Inc.
+ *
+ * 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.
+ */
+
+namespace Seahorse {
+namespace Pkcs11 {
+
+public class PrivateKey : Gck.Object, Gck.ObjectCache,
+                          Deletable, Exportable, Viewable {
+       public Token? place {
+               owned get { return (Token?)this._token.get(); }
+               set { this._token.set(value); }
+       }
+
+       public Flags object_flags {
+               get { return Flags.PERSONAL; }
+       }
+
+       public Gtk.ActionGroup? actions {
+               get { return null; }
+       }
+
+       public Certificate? partner {
+               owned get { return (Certificate?)this._certificate.get(); }
+               set {
+                       this._certificate.set(value);
+                       notify_property("partner");
+                       notify_property("description");
+               }
+       }
+
+       public string? label {
+               owned get {
+                       if (this._attributes != null) {
+                               string label;
+                               if (this._attributes.find_string(CKA.LABEL, out label))
+                                       return label;
+                       }
+                       Certificate? cert = this.partner;
+                       if (cert != null)
+                               return cert.label;
+                       return _("Unnamed private key");
+               }
+       }
+
+       public string? markup {
+               owned get { return GLib.Markup.escape_text(this.label, -1); }
+       }
+
+       public string? description {
+               get { return _("Private key"); }
+       }
+
+       public GLib.Icon? icon {
+               get {
+                       if (this._icon == null)
+                               this._icon = new GLib.ThemedIcon(Gcr.ICON_KEY);
+                       return this._icon;
+               }
+       }
+
+       public Gck.Attributes attributes {
+               owned get { return this._attributes; }
+               set {
+                       this._attributes = value;
+                       notify_property("attributes");
+               }
+       }
+
+       public bool deletable {
+               get {
+                       Token ?token = this.place;
+                       return token == null ? false : token.is_deletable(this);
+               }
+       }
+
+       public bool exportable {
+               get { return false; }
+       }
+
+       private GLib.WeakRef _token;
+       private Gck.Attributes? _attributes;
+       private GLib.WeakRef _certificate;
+       private GLib.Icon? _icon;
+
+       public void fill(Gck.Attributes attributes) {
+               Gck.Builder builder = new Gck.Builder(Gck.BuilderFlags.NONE);
+               if (this._attributes != null)
+                       builder.add_all(this._attributes);
+               builder.set_all(attributes);
+               this._attributes = builder.steal();
+               notify_property("attributes");
+       }
+
+       public Seahorse.Deleter create_deleter() {
+               return new KeyDeleter(this);
+       }
+
+       public GLib.List<Exporter> create_exporters(ExporterType type) {
+               /* In the future we may exporters here, but for now no exporting */
+               var exporters = new GLib.List<Exporter>();
+               return exporters;
+       }
+
+       public Gtk.Window? create_viewer(Gtk.Window? parent) {
+               var viewer = new Pkcs11.Properties(this, parent);
+               viewer.show();
+               return viewer;
+       }
+}
+
+}
+}
diff --git a/pkcs11/pkcs11-properties.vala b/pkcs11/pkcs11-properties.vala
new file mode 100644
index 0000000..c91b75a
--- /dev/null
+++ b/pkcs11/pkcs11-properties.vala
@@ -0,0 +1,226 @@
+/*
+ * Seahorse
+ *
+ * Copyright (C) 2008 Stefan Walter
+ * Copyright (C) 2013 Red Hat Inc.
+ *
+ * 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 redhat com>
+ */
+
+namespace Seahorse {
+namespace Pkcs11 {
+
+public class Properties : Gtk.Window {
+       public Gck.Object object { construct; get; }
+
+       private static string UI_STRING =
+               """<ui>
+                       <toolbar name='Toolbar'>
+                               <toolitem action='export-object'/>
+                               <toolitem action='delete-object'/>
+                               <separator name='MiddleSeparator' expand='true'/>
+                               <toolitem action='request-certificate'/>
+                       </toolbar>
+               </ui>""";
+
+       private Gtk.Box _content;
+       private Gcr.Viewer _viewer;
+       private GLib.Cancellable _cancellable;
+       private Gck.Object _request_key;
+       private Gtk.UIManager _ui_manager;
+       private Gtk.ActionGroup _actions;
+
+       public Properties(Gck.Object object,
+                         Gtk.Window window) {
+               GLib.Object(object: object, transient_for: window);
+       }
+
+       construct {
+               this._cancellable = new GLib.Cancellable();
+               set_default_size (400, 400);
+
+               this._content = new Gtk.Box(Gtk.Orientation.VERTICAL, 0);
+               this.add(this._content);
+               this._content.show();
+
+               this._viewer = Gcr.Viewer.new_scrolled();
+               this._content.add(this._viewer);
+               this._viewer.set_hexpand(true);
+               this._viewer.set_vexpand(true);
+               this._viewer.show();
+
+               /* ... */
+
+               this._actions = new Gtk.ActionGroup("Pkcs11Actions");
+               this._actions.set_translation_domain(Config.GETTEXT_PACKAGE);
+               this._actions.add_actions(UI_ACTIONS, this);
+               var action = this._actions.get_action("delete-object");
+               this.object.bind_property("deletable", action, "sensitive",
+                                         GLib.BindingFlags.SYNC_CREATE);
+               action = this._actions.get_action("export-object");
+               this.object.bind_property("exportable", action, "sensitive",
+                                         GLib.BindingFlags.SYNC_CREATE);
+               var request = this._actions.get_action("request-certificate");
+               request.is_important = true;
+               request.visible = false;
+
+               this._ui_manager = new Gtk.UIManager();
+               this._ui_manager.insert_action_group(this._actions, 0);
+               this._ui_manager.add_widget.connect((widget) => {
+                       if (!(widget is Gtk.Toolbar))
+                               return;
+
+                       this._content.pack_start(widget, false, true, 0);
+                       this._content.reorder_child(widget, 0);
+
+                       widget.get_style_context().add_class(Gtk.STYLE_CLASS_PRIMARY_TOOLBAR);
+                       widget.reset_style();
+                       widget.show();
+               });
+               try {
+                       this._ui_manager.add_ui_from_string(UI_STRING, -1);
+               } catch (GLib.Error err) {
+                       GLib.critical ("%s", err.message);
+               }
+               this._ui_manager.ensure_update();
+
+               this.object.notify["label"].connect(() => { this.update_label(); });
+               this.update_label();
+               this.add_renderer_for_object(this.object);
+               this.check_certificate_request_capable(this.object);
+
+               GLib.Object? partner;
+               this.object.get("partner", out partner);
+               if (partner != null) {
+                       this.add_renderer_for_object(partner);
+                       this.check_certificate_request_capable(partner);
+               }
+
+               GLib.List<Exporter> exporters;
+               if (this.object is Exportable)
+                       exporters = ((Exportable)this.object).create_exporters(ExporterType.ANY);
+
+               var export = this._actions.get_action("export-object");
+               export.set_visible(exporters != null);
+
+               this._viewer.grab_focus();
+       }
+
+       public override void dispose() {
+               this._cancellable.cancel();
+               base.dispose();
+       }
+
+       private void update_label() {
+               string? label;
+               string? description;
+               this.object.get("label", out label, "description", out description);
+               if (label == null || label == "")
+                       label = _("Unnamed");
+               this.set_title("%s - %s".printf(label, description));
+       }
+
+       private void add_renderer_for_object(GLib.Object object) {
+               Gck.Attributes? attributes = null;
+               string? label = null;
+
+               object.get("label", &label, "attributes", &attributes);
+               if (attributes != null) {
+                       var renderer = Gcr.Renderer.create(label, attributes);
+                       if (renderer != null) {
+                               object.bind_property("label", renderer, "label",
+                                                    GLib.BindingFlags.DEFAULT);
+                               object.bind_property("attributes", renderer, "attributes",
+                                                    GLib.BindingFlags.DEFAULT);
+
+                               if (renderer.get_class().find_property("object") != null)
+                                       renderer.set("object", object);
+
+                               this._viewer.add_renderer(renderer);
+                       }
+               }
+       }
+
+       private void on_export_certificate(Gtk.Action action) {
+               GLib.List<GLib.Object> objects = null;
+               objects.append(this.object);
+               try {
+                       Exportable.export_to_prompt_wait(objects, this);
+               } catch (GLib.Error err) {
+                       Util.show_error(this, _("Failed to export certificate"), err.message);
+               }
+       }
+
+       private void on_delete_objects(Gtk.Action action) {
+               GLib.Object? partner;
+               this.object.get("partner", out partner);
+
+               Deleter deleter;
+               if (partner != null || this.object is PrivateKey) {
+                       deleter = new KeyDeleter((Gck.Object)this.object);
+                       if (!deleter.add_object(partner))
+                               GLib.assert_not_reached();
+               } else {
+                       deleter = new Deleter((Gck.Object)this.object);
+               }
+
+               if (deleter.prompt(this)) {
+                       deleter.delete.begin(this._cancellable, (obj, res) => {
+                               try {
+                                       if (deleter.delete.end(res))
+                                               this.destroy();
+                               } catch (GLib.Error err) {
+                                       Util.show_error(this, _("Couldn't delete"), err.message);
+                               }
+                       });
+               }
+       }
+
+       private void on_request_certificate(Gtk.Action action) {
+               Request.prompt(this, this._request_key);
+       }
+
+       private static const Gtk.ActionEntry[] UI_ACTIONS = {
+               { "export-object", Gtk.Stock.SAVE_AS, N_("_Export"), "",
+                 N_("Export the certificate"), on_export_certificate },
+               { "delete-object", Gtk.Stock.DELETE, N_("_Delete"), "<Ctrl>Delete",
+                 N_("Delete this certificate or key"), on_delete_objects },
+               { "request-certificate", null, N_("Request _Certificate"), null,
+                 N_("Create a certificate request file for this key"), on_request_certificate },
+       };
+
+       private void check_certificate_request_capable(GLib.Object object) {
+               if (!(object is PrivateKey))
+                       return;
+
+               Gcr.CertificateRequest.capable_async.begin((PrivateKey)object, this._cancellable, (obj, res) 
=> {
+                       try {
+                               if (Gcr.CertificateRequest.capable_async.end(res)) {
+                                       var request = this._actions.get_action("request-certificate");
+                                       request.set_visible(true);
+                                       this._request_key = (PrivateKey)object;
+                               }
+                       } catch (GLib.Error err) {
+                               GLib.message("couldn't check capabilities of private key: %s", err.message);
+                       }
+               });
+       }
+}
+
+}
+}
diff --git a/pkcs11/pkcs11-request.vala b/pkcs11/pkcs11-request.vala
new file mode 100644
index 0000000..fa59714
--- /dev/null
+++ b/pkcs11/pkcs11-request.vala
@@ -0,0 +1,157 @@
+/*
+ * Seahorse
+ *
+ * Copyright (C) 2008 Stefan Walter
+ * Copyright (C) 2011 Collabora Ltd.
+ * Copyright (C) 2013 Red Hat Inc.
+ *
+ * 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.
+ *
+ * Stef Walter <stefw redhat com>
+ */
+
+namespace Seahorse {
+namespace Pkcs11 {
+
+public class Request : Gtk.Dialog {
+       public PrivateKey private_key { construct; get; }
+
+       Gtk.Entry _name_entry;
+       uint8[] _encoded;
+
+       construct {
+               var builder = new Gtk.Builder();
+               var path = Config.UIDIR + "seahorse-pkcs11-request.xml";
+               try {
+                       builder.add_from_file(path);
+               } catch (GLib.Error err) {
+                       GLib.warning("couldn't load ui file: %s", path);
+                       return;
+               }
+
+               this.set_resizable(false);
+               var content = this.get_content_area();
+               var widget = (Gtk.Widget)builder.get_object("pkcs11-request");
+               content.add(widget);
+               widget.show();
+
+               this._name_entry = (Gtk.Entry)builder.get_object("request-name");
+               this._name_entry.changed.connect(() => { update_response(); });
+
+               /* The buttons */
+               this.add_buttons(Gtk.Stock.CANCEL, Gtk.ResponseType.CANCEL,
+                                _("Create"), Gtk.ResponseType.OK);
+               this.set_default_response (Gtk.ResponseType.OK);
+
+               this.update_response ();
+
+               GLib.return_if_fail (this.private_key is Gck.Object);
+       }
+
+       public static void prompt(Gtk.Window? parent,
+                                 Gck.Object private_key) {
+               var dialog = (Request)GLib.Object.new(typeof(Request), transient_for: parent,
+                                                     private_key: private_key);
+               dialog.run();
+       }
+
+       public override void response(int response_id) {
+               if (response_id == Gtk.ResponseType.OK) {
+                       var interaction = new Interaction(this.transient_for);
+                       var session = this.private_key.get_session();
+                       session.set_interaction(interaction);
+
+                       var req = 
Gcr.CertificateRequest.prepare(Gcr.CertificateRequestFormat.CERTIFICATE_REQUEST_PKCS10,
+                                                                this.private_key);
+                       req.set_cn(this._name_entry.get_text());
+                       req.complete_async.begin(null, (obj, res) => {
+                               try {
+                                       req.complete_async.end(res);
+                                       this.save_certificate_request(req, this.transient_for);
+                               } catch (GLib.Error err) {
+                                       Util.show_error(this.transient_for, _("Couldn't create certificate 
request"), err.message);
+                               }
+                       });
+
+                       this.hide();
+               }
+       }
+
+       private void update_response() {
+               string name = this._name_entry.get_text();
+               this.set_response_sensitive(Gtk.ResponseType.OK, name != "");
+       }
+
+       private static string BAD_FILENAME_CHARS = "/\\<>|?*";
+
+       private void save_certificate_request(Gcr.CertificateRequest req,
+                                             Gtk.Window? parent) {
+               var chooser = new Gtk.FileChooserDialog(_("Save certificate request"),
+                                                       parent, Gtk.FileChooserAction.SAVE,
+                                                       Gtk.Stock.CANCEL, Gtk.ResponseType.CANCEL,
+                                                       Gtk.Stock.SAVE, Gtk.ResponseType.ACCEPT);
+
+               chooser.set_default_response(Gtk.ResponseType.ACCEPT);
+               chooser.set_local_only(false);
+
+               var der_filter = new Gtk.FileFilter();
+               der_filter.set_name(_("Certificate request"));
+               der_filter.add_mime_type("application/pkcs10");
+               der_filter.add_pattern("*.p10");
+               der_filter.add_pattern("*.csr");
+               chooser.add_filter(der_filter);
+               chooser.set_filter(der_filter);
+
+               var pem_filter = new Gtk.FileFilter();
+               pem_filter.set_name(_("PEM encoded request"));
+               pem_filter.add_mime_type("application/pkcs10+pem");
+               pem_filter.add_pattern("*.pem");
+               chooser.add_filter(pem_filter);
+
+               string? label;
+               this.private_key.get("label", out label);
+               if (label == null || label == "")
+                       label = "Certificate Request";
+               var filename = label + ".csr";
+               filename = filename.delimit(BAD_FILENAME_CHARS, '_');
+               chooser.set_current_name(filename);
+
+               chooser.set_do_overwrite_confirmation(true);
+
+               var response = chooser.run();
+               if (response == Gtk.ResponseType.ACCEPT) {
+                       bool textual = chooser.get_filter() == pem_filter;
+                       this._encoded = req.encode(textual);
+
+                       var file = chooser.get_file();
+                       file.replace_contents_async.begin(this._encoded, null, false,
+                                                         GLib.FileCreateFlags.NONE,
+                                                         null, (obj, res) => {
+                               try {
+                                       string new_etag;
+                                       file.replace_contents_async.end(res, out new_etag);
+                               } catch (GLib.Error err) {
+                                       Util.show_error(parent, _("Couldn't save certificate request"), 
err.message);
+                               }
+                       });
+               }
+
+               chooser.destroy();
+       }
+}
+
+}
+}
diff --git a/pkcs11/pkcs11-token.vala b/pkcs11/pkcs11-token.vala
new file mode 100644
index 0000000..d26e6b7
--- /dev/null
+++ b/pkcs11/pkcs11-token.vala
@@ -0,0 +1,490 @@
+/*
+ * Seahorse
+ *
+ * Copyright (C) 2006 Stefan Walter
+ * Copyright (C) 2011 Collabora Ltd.
+ * Copyright (C) 2013 Red Hat, Inc.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 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 General Public License for more details.
+ * You should have received a copy of the GNU 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.
+ */
+
+namespace Seahorse {
+namespace Pkcs11 {
+
+public class Token : GLib.Object, Gcr.Collection, Place, Lockable {
+
+       public bool unlockable {
+               get {
+                       this.ensure_token_info();
+                       if ((this._info.flags & CKF.LOGIN_REQUIRED) == 0)
+                               return false;
+                       if ((this._info.flags & CKF.USER_PIN_INITIALIZED) == 0)
+                               return false;
+                       return !is_session_logged_in(this._session);
+               }
+       }
+
+       public bool lockable {
+               get {
+                       this.ensure_token_info();
+                       if ((this._info.flags & CKF.LOGIN_REQUIRED) == 0)
+                               return false;
+                       if ((this._info.flags & CKF.USER_PIN_INITIALIZED) == 0)
+                               return false;
+                       return is_session_logged_in(this._session);
+               }
+       }
+
+       public Gck.TokenInfo info {
+               get { return this.ensure_token_info(); }
+       }
+
+       public Gck.Session session {
+               get { return this._session; }
+               set {
+                       this._session = session;
+                       notify_property("session");
+                       notify_property("lockable");
+                       notify_property("unlockable");
+               }
+       }
+
+       public Gck.Slot slot {
+               get { return this._slot; }
+               construct { this._slot = value; }
+       }
+
+       public string label {
+               owned get {
+                       var token = this._slot.get_token_info();
+                       if (token == null)
+                               return C_("Label", "Unknown");
+                       return token.label;
+               }
+       }
+
+       public string description {
+               owned get {
+                       var token = this._slot.get_token_info();
+                       if (token == null)
+                               return "";
+                       return token.manufacturer_id;
+               }
+       }
+
+       public string uri {
+               owned get { return this._uri; }
+       }
+
+       public GLib.Icon icon {
+               owned get {
+                       var token = this._slot.get_token_info();
+                       if (token == null)
+                               return new GLib.ThemedIcon(Gtk.Stock.DIALOG_QUESTION);
+                       return Gcr.icon_for_token(token);
+               }
+       }
+
+       public Gtk.ActionGroup? actions {
+               owned get { return null; }
+       }
+
+       public Flags object_flags {
+               get { return 0; }
+       }
+
+       public unowned GLib.Array<ulong> mechanisms {
+               get {
+                       if (this._mechanisms == null)
+                               this._mechanisms = this._slot.get_mechanisms();
+                       return this._mechanisms;
+               }
+       }
+
+       private Gck.Slot _slot;
+       private string _uri;
+       private Gck.TokenInfo? _info;
+       private GLib.Array<ulong> _mechanisms;
+       private Gck.Session? _session;
+       private GLib.HashTable<ulong?, GLib.Object> _object_for_handle;
+       private GLib.HashTable<Gck.Attribute, GLib.GenericArray<GLib.Object>> _objects_for_id;
+       private GLib.HashTable<GLib.Object, Gck.Attribute> _id_for_object;
+       private GLib.HashTable<GLib.Object, GLib.Object> _objects_visible;
+
+       public Token(Gck.Slot slot) {
+               GLib.Object(
+                       slot: slot
+               );
+       }
+
+       construct {
+               this._object_for_handle = new GLib.HashTable<ulong?, GLib.Object>(ulong_hash, ulong_equal);
+               this._objects_for_id = new GLib.HashTable<Gck.Attribute, 
GLib.GenericArray<GLib.Object>>(Gck.Attribute.hash, Gck.Attribute.equal);
+               this._id_for_object = new GLib.HashTable<GLib.Object, unowned 
Gck.Attribute>(GLib.direct_hash, GLib.direct_equal);
+               this._objects_visible = new GLib.HashTable<GLib.Object, GLib.Object>(GLib.direct_hash, 
GLib.direct_equal);
+
+               /* TODO: Does this happen in the background? It really should. */
+               this.load.begin(null);
+
+               var data = new Gck.UriData();
+               this.ensure_token_info();
+               data.token_info = this._info;
+               this._uri = Gck.uri_build(data, Gck.UriFlags.FOR_TOKEN);
+       }
+
+       public override void dispose() {
+               this._slot = null;
+               this._session = null;
+       }
+
+       public async bool lock(GLib.TlsInteraction? interaction,
+                              GLib.Cancellable? cancellable) throws GLib.Error {
+               if (!is_session_logged_in(this._session))
+                       return true;
+
+               yield this._session.logout_async(cancellable);
+               return yield this.load(cancellable);
+       }
+
+       public async bool unlock(GLib.TlsInteraction? interaction,
+                                GLib.Cancellable? cancellable) throws GLib.Error {
+               if (is_session_logged_in (this._session))
+                       return true;
+               if (this._session != null) {
+                       return yield this._session.login_interactive_async(CKU.USER, interaction, 
cancellable);
+               } else {
+                       var options = calculate_session_options();
+                       this._session = yield this._slot.open_session_async(options | 
Gck.SessionOptions.LOGIN_USER,
+                                                                           cancellable);
+                       return true;
+               }
+       }
+
+       public bool contains (GLib.Object object) {
+               return this._objects_visible.lookup(object) != null;
+       }
+
+       public uint get_length() {
+               return this._objects_visible.size();
+       }
+
+       public GLib.List<weak GLib.Object> get_objects() {
+               return this._objects_visible.get_values();
+       }
+
+       public bool is_deletable(Gck.Object object) {
+               this.ensure_token_info();
+
+               if ((this._info.flags & CKF.WRITE_PROTECTED) == CKF.WRITE_PROTECTED)
+                       return false;
+
+               Gck.Attributes? attributes;
+               object.get("attributes", out attributes);
+
+               if (attributes != null) {
+                       bool ret = true;
+                       attributes.find_boolean(CKA.MODIFIABLE, out ret);
+                       return ret;
+               }
+
+               return false;
+       }
+
+       public void remove_object(Gck.Object object) {
+               GLib.List<Gck.Object> objects = null;
+               objects.append(object);
+               remove_objects(objects);
+       }
+
+       public bool has_mechanism(ulong mechanism) {
+               return Gck.mechanisms_check(this.mechanisms, mechanism, Gck.INVALID);
+       }
+
+       private static bool is_session_logged_in(Gck.Session? session) {
+               if (session == null)
+                       return false;
+               var info = session.get_info();
+               return (info != null) &&
+                      (info.state == CKS.RW_USER_FUNCTIONS ||
+                       info.state == CKS.RO_USER_FUNCTIONS ||
+                       info.state == CKS.RW_SO_FUNCTIONS);
+       }
+
+       private unowned Gck.TokenInfo ensure_token_info() {
+               if (this._info == null)
+                       this.update_token_info();
+               return this._info;
+       }
+
+       private void update_token_info() {
+               var info = this._slot.get_token_info();
+               if (info != null) {
+                       this._info = info;
+                       this.notify_property("info");
+                       this.notify_property("lockable");
+                       this.notify_property("unlockable");
+               }
+       }
+
+       private void update_id_map(GLib.Object object,
+                                  Gck.Attribute* id) {
+               bool add = false;
+               bool remove = false;
+
+               var pid = this._id_for_object.lookup(object);
+               if (id == null) {
+                       if (pid != null) {
+                               id = pid;
+                               remove = true;
+                       }
+               } else {
+                       if (pid == null) {
+                               add = true;
+                       } else if (!id->equal(pid)) {
+                               remove = true;
+                               add = true;
+                       }
+               }
+
+               if (add) {
+                       unowned GLib.GenericArray<GLib.Object>? objects;
+                       objects = this._objects_for_id.lookup(id);
+                       if (objects == null) {
+                               var objs = new GLib.GenericArray<GLib.Object>();
+                               this._objects_for_id.insert(id, objs);
+                               objects = objs;
+                       }
+                       objects.add(object);
+                       this._id_for_object.insert(object, id);
+               }
+
+               /* Remove this object from the map */
+               if (remove) {
+                       if (!this._id_for_object.remove(object))
+                               GLib.assert_not_reached();
+                       var objects = this._objects_for_id.lookup(id);
+                       GLib.assert(objects != null);
+                       GLib.assert(objects.length > 0);
+                       if (objects.length == 1) {
+                               if (!this._objects_for_id.remove(id))
+                                       GLib.assert_not_reached();
+                       } else {
+                               if (!objects.remove(object))
+                                       GLib.assert_not_reached();
+                       }
+               }
+       }
+
+       private GLib.Object? lookup_id_map(GLib.Type object_type,
+                                          Gck.Attribute* id) {
+               if (id == null)
+                       return null;
+               var objects = this._objects_for_id.lookup(id);
+               if (objects == null)
+                       return null;
+               for (var i = 0; i < objects.length; i++) {
+                       if (objects[i].get_type().is_a(object_type))
+                               return objects[i];
+               }
+               return null;
+       }
+
+       private void update_visibility(GLib.List<GLib.Object> objects,
+                                      bool visible) {
+               foreach (var object in objects) {
+                       bool have = (this._objects_visible.lookup(object) != null);
+                       if (!have && visible) {
+                               this._objects_visible.insert(object, object);
+                               this.emit_added(object);
+                       } else if (have && !visible) {
+                               if (!this._objects_visible.remove(object))
+                                       GLib.assert_not_reached();
+                               this.emit_removed(object);
+                       }
+               }
+
+       }
+
+       private static bool make_certificate_key_pair(Certificate certificate,
+                                                     PrivateKey private_key) {
+               if (certificate.partner != null || private_key.partner != null)
+                       return false;
+               certificate.partner = private_key;
+               private_key.partner = certificate;
+               return true;
+       }
+
+       private static GLib.Object? break_certificate_key_pair(GLib.Object object) {
+               GLib.Object? pair = null;
+               if (object is Certificate) {
+                       var certificate = (Certificate)object;
+                       pair = certificate.partner;
+                       certificate.partner = null;
+               } else if (object is PrivateKey) {
+                       var private_key = (PrivateKey)object;
+                       pair = private_key.partner;
+                       private_key.partner = null;
+               }
+               return pair;
+       }
+
+       private void receive_objects(GLib.List<GLib.Object> objects) {
+               var show = new GLib.List<GLib.Object>();
+               var hide = new GLib.List<GLib.Object>();
+
+               foreach (var object in objects) {
+                       if (!(object is Gck.Object && object is Gck.ObjectCache))
+                               continue;
+                       var handle = ((Gck.Object)object).handle;
+                       var attrs = ((Gck.ObjectCache)object).attributes;
+
+                       var prev = this._object_for_handle.lookup(handle);
+                       if (prev == null) {
+                               this._object_for_handle.insert(handle, object);
+                               object.set("place", this);
+                       } else if (prev != object) {
+                               object.set("attributes", attrs);
+                               object = prev;
+                       }
+
+                       unowned Gck.Attribute? id = null;
+                       if (attrs != null)
+                               id = attrs.find(CKA.ID);
+                       this.update_id_map(object, id);
+
+                       if (object is Certificate) {
+                               var pair = this.lookup_id_map(typeof(PrivateKey), id);
+                               if (pair != null && make_certificate_key_pair((Certificate)object, 
(PrivateKey)pair))
+                                       hide.prepend(pair);
+                               show.prepend(object);
+                       } else if (object is PrivateKey) {
+                               var pair = this.lookup_id_map(typeof(Certificate), id);
+                               if (pair != null && make_certificate_key_pair((Certificate)pair, 
(PrivateKey)object))
+                                       hide.prepend(object);
+                               else
+                                       show.prepend(object);
+                       } else {
+                               show.prepend(object);
+                       }
+               }
+
+               update_visibility(hide, false);
+               update_visibility(show, true);
+       }
+
+       private void remove_objects(GLib.List<GLib.Object> objects) {
+               var depaired = new GLib.List<GLib.Object>();
+               var hide = new GLib.List<GLib.Object>();
+
+               foreach (var object in objects) {
+                       var pair = break_certificate_key_pair(object);
+                       if (pair != null)
+                               depaired.prepend(pair);
+                       update_id_map(object, null);
+                       hide.prepend(object);
+               }
+
+               /* Remove the ownership of these */
+               foreach (var object in objects) {
+                       var handle = ((Gck.Object)object).handle;
+                       object.set("place", null);
+                       this._object_for_handle.remove(handle);
+               }
+
+               update_visibility(hide, false);
+
+               /* Add everything that was paired */
+               receive_objects(depaired);
+       }
+
+       private Gck.SessionOptions calculate_session_options() {
+               this.ensure_token_info();
+               if ((this._info.flags & CKF.WRITE_PROTECTED) == CKF.WRITE_PROTECTED)
+                       return Gck.SessionOptions.READ_ONLY;
+               else
+                       return Gck.SessionOptions.READ_WRITE;
+       }
+
+       public async bool load(GLib.Cancellable? cancellable) throws GLib.Error {
+               var checks = new GLib.HashTable<ulong?, GLib.Object>(ulong_hash, ulong_equal);
+
+               /* Make note of all the objects that were there */
+               this.update_token_info();
+               foreach (var object in this.get_objects()) {
+                       var handle = ((Gck.Object)object).handle;
+                       checks.insert(handle, object);
+               }
+
+               if (this._session == null) {
+                       var options = this.calculate_session_options();
+                       this._session = yield this._slot.open_session_async(options, cancellable);
+               }
+
+               var builder = new Gck.Builder(Gck.BuilderFlags.NONE);
+               builder.add_boolean(CKA.TOKEN, true);
+               builder.add_ulong(CKA.CLASS, CKO.CERTIFICATE);
+
+               const ulong[] CERTIFICATE_ATTRS = {
+                       CKA.VALUE,
+                       CKA.ID,
+                       CKA.LABEL,
+                       CKA.CLASS,
+                       CKA.CERTIFICATE_CATEGORY,
+                       CKA.MODIFIABLE
+               };
+
+               var enumerator = this._session.enumerate_objects(builder.end());
+               enumerator.set_object_type(typeof(Certificate), CERTIFICATE_ATTRS);
+
+               builder = new Gck.Builder(Gck.BuilderFlags.NONE);
+               builder.add_boolean(CKA.TOKEN, true);
+               builder.add_ulong(CKA.CLASS, CKO.PRIVATE_KEY);
+
+               const ulong[] KEY_ATTRS = {
+                       CKA.MODULUS_BITS,
+                       CKA.ID,
+                       CKA.LABEL,
+                       CKA.CLASS,
+                       CKA.KEY_TYPE,
+                       CKA.MODIFIABLE,
+               };
+
+               var chained = this._session.enumerate_objects(builder.end());
+               chained.set_object_type(typeof(PrivateKey), KEY_ATTRS);
+               enumerator.set_chained(chained);
+
+               for (;;) {
+                       var objects = yield enumerator.next_async(16, cancellable);
+
+                       /* Otherwise we're done, remove everything not found */
+                       if (objects == null) {
+                               remove_objects(checks.get_values());
+                               return true;
+                       }
+
+                       this.receive_objects(objects);
+
+                       /* Remove all objects that were found from the check table */
+                       foreach (var object in objects) {
+                               var handle = ((Gck.Object)object).handle;
+                               checks.remove(handle);
+                       }
+               }
+       }
+}
+
+}
+}
diff --git a/pkcs11/seahorse-certificate.c b/pkcs11/seahorse-certificate.c
index 43e1499..aa114cc 100644
--- a/pkcs11/seahorse-certificate.c
+++ b/pkcs11/seahorse-certificate.c
@@ -25,7 +25,6 @@
 #include "seahorse-certificate.h"
 #include "seahorse-pkcs11.h"
 #include "seahorse-pkcs11-deleter.h"
-#include "seahorse-pkcs11-helpers.h"
 #include "seahorse-pkcs11-properties.h"
 #include "seahorse-private-key.h"
 #include "seahorse-token.h"
diff --git a/pkcs11/seahorse-pkcs11-backend.c b/pkcs11/seahorse-pkcs11-backend.c
index 79987e1..54542c3 100644
--- a/pkcs11/seahorse-pkcs11-backend.c
+++ b/pkcs11/seahorse-pkcs11-backend.c
@@ -24,7 +24,6 @@
 #include "seahorse-pkcs11.h"
 #include "seahorse-pkcs11-backend.h"
 #include "seahorse-pkcs11-generate.h"
-#include "seahorse-token.h"
 
 #include "seahorse-util.h"
 
@@ -144,7 +143,7 @@ on_initialized_registered (GObject *unused,
                        if (token == NULL)
                                continue;
                        if (is_token_usable (self, s->data, token)) {
-                               place = SEAHORSE_PLACE (seahorse_token_new (s->data));
+                               place = SEAHORSE_PLACE (seahorse_pkcs11_token_new (s->data));
                                self->tokens = g_list_append (self->tokens, place);
                                gcr_collection_emit_added (GCR_COLLECTION (self), G_OBJECT (place));
                        }
@@ -307,7 +306,7 @@ seahorse_pkcs11_backend_lookup_place (SeahorseBackend *backend,
                return NULL;
 
        for (l = self->tokens; l != NULL; l = g_list_next (l)) {
-               if (gck_slot_match (seahorse_token_get_slot (l->data), uri_data))
+               if (gck_slot_match (seahorse_pkcs11_token_get_slot (l->data), uri_data))
                        break;
        }
 
@@ -350,18 +349,18 @@ static gboolean
 on_filter_writable (GObject *object,
                     gpointer user_data)
 {
-       SeahorseToken *token = SEAHORSE_TOKEN (object);
+       SeahorsePkcs11Token *token = SEAHORSE_PKCS11_TOKEN (object);
        guint mechanism = GPOINTER_TO_UINT (user_data);
        GckTokenInfo *info;
 
-       info = seahorse_token_get_info (token);
+       info = seahorse_pkcs11_token_get_info (token);
        g_return_val_if_fail (info != NULL, FALSE);
 
        if (info->flags & CKF_WRITE_PROTECTED)
                return FALSE;
 
        if (mechanism != G_MAXUINT) {
-               if (!seahorse_token_has_mechanism (token, (gulong)mechanism))
+               if (!seahorse_pkcs11_token_has_mechanism (token, (gulong)mechanism))
                        return FALSE;
        }
 
diff --git a/pkcs11/seahorse-pkcs11-backend.h b/pkcs11/seahorse-pkcs11-backend.h
index 19693c4..f8ff3ab 100644
--- a/pkcs11/seahorse-pkcs11-backend.h
+++ b/pkcs11/seahorse-pkcs11-backend.h
@@ -22,7 +22,6 @@
 #define SEAHORSE_PKCS11_BACKEND_H_
 
 #include "seahorse-pkcs11.h"
-#include "seahorse-token.h"
 
 #include <gcr/gcr.h>
 
diff --git a/pkcs11/seahorse-pkcs11-generate.c b/pkcs11/seahorse-pkcs11-generate.c
index 260df79..c5608b5 100644
--- a/pkcs11/seahorse-pkcs11-generate.c
+++ b/pkcs11/seahorse-pkcs11-generate.c
@@ -24,7 +24,6 @@
 
 #include "seahorse-pkcs11-backend.h"
 #include "seahorse-pkcs11-generate.h"
-#include "seahorse-token.h"
 
 #include "seahorse-common.h"
 #include "seahorse-progress.h"
@@ -51,7 +50,7 @@ struct _SeahorsePkcs11Generate {
 
        GtkEntry *label_entry;
 
-       SeahorseToken *token;
+       SeahorsePkcs11Token *token;
        GtkComboBox *token_box;
        GcrCollectionModel *token_model;
 
@@ -244,7 +243,7 @@ on_token_changed (GtkComboBox *combo_box,
        model = GTK_TREE_MODEL (self->mechanism_store);
        valid = gtk_tree_model_get_iter_first (model, &iter);
        if (self->token) {
-               mechanisms = seahorse_token_get_mechanisms (self->token);
+               mechanisms = seahorse_pkcs11_token_get_mechanisms (self->token);
                for (i = 0; mechanisms && i < mechanisms->len; i++) {
                        type = g_array_index (mechanisms, gulong, i);
                        label = get_available_mechanism_label (type);
@@ -295,7 +294,7 @@ on_mechanism_changed (GtkComboBox *widget,
                gtk_tree_model_get (GTK_TREE_MODEL (self->mechanism_store), &iter,
                                    MECHANISM_TYPE, &self->mechanism->type, -1);
 
-               slot = seahorse_token_get_slot (self->token);
+               slot = seahorse_pkcs11_token_get_slot (self->token);
                info = gck_slot_get_mechanism_info (slot, self->mechanism->type);
                g_return_if_fail (info != NULL);
 
@@ -351,7 +350,7 @@ seahorse_pkcs11_generate_constructed (GObject *obj)
        G_OBJECT_CLASS (seahorse_pkcs11_generate_parent_class)->constructed (obj);
 
        builder = gtk_builder_new ();
-       path = SEAHORSE_UIDIR "/seahorse-pkcs11-generate.xml";
+       path = UIDIR "/seahorse-pkcs11-generate.xml";
        gtk_builder_add_from_file (builder, path, &error);
        if (error != NULL) {
                g_warning ("couldn't load ui file: %s", path);
@@ -450,7 +449,7 @@ seahorse_pkcs11_generate_response (GtkDialog *dialog,
                parent = gtk_window_get_transient_for (GTK_WINDOW (self));
                interaction = seahorse_interaction_new (parent);
 
-               gck_session_open_async (seahorse_token_get_slot (self->token),
+               gck_session_open_async (seahorse_pkcs11_token_get_slot (self->token),
                                        GCK_SESSION_READ_WRITE | GCK_SESSION_LOGIN_USER,
                                        interaction, self->cancellable,
                                        on_generate_open_session, g_object_ref (self));
diff --git a/po/POTFILES.in b/po/POTFILES.in
index 345c0f4..1397aa6 100644
--- a/po/POTFILES.in
+++ b/po/POTFILES.in
@@ -71,17 +71,17 @@ pgp/seahorse-transfer.c
 pgp/seahorse-unknown.c
 pgp/seahorse-unknown-source.c
 pkcs11/certificate-der-exporter.vala
-pkcs11/seahorse-certificate.c
+pkcs11/pkcs11-certificate.vala
 pkcs11/seahorse-pkcs11-backend.c
-pkcs11/seahorse-pkcs11-deleter.c
+pkcs11/pkcs11-deleter.vala
 pkcs11/seahorse-pkcs11-generate.c
 [type: gettext/glade]pkcs11/seahorse-pkcs11-generate.xml
-pkcs11/seahorse-pkcs11-key-deleter.c
-pkcs11/seahorse-pkcs11-properties.c
-pkcs11/seahorse-pkcs11-request.c
+pkcs11/pkcs11-key-deleter.vala
+pkcs11/pkcs11-properties.vala
+pkcs11/pkcs11-request.vala
 [type: gettext/glade]pkcs11/seahorse-pkcs11-request.xml
-pkcs11/seahorse-private-key.c
-pkcs11/seahorse-token.c
+pkcs11/pkcs11-private-key.vala
+pkcs11/pkcs11-token.vala
 [type: gettext/glade]src/seahorse-change-passphrase.xml
 src/seahorse.desktop.in.in
 src/seahorse-generate-select.c
diff --git a/po/POTFILES.skip b/po/POTFILES.skip
index c2fc4dd..75d4e94 100644
--- a/po/POTFILES.skip
+++ b/po/POTFILES.skip
@@ -8,4 +8,11 @@ gkr/gkr-item.c
 gkr/gkr-keyring-add.c
 gkr/gkr-keyring.c
 pkcs11/certificate-der-exporter.c
+pkcs11/pkcs11-certificate.c
+pkcs11/pkcs11-deleter.c
+pkcs11/pkcs11-key-deleter.c
+pkcs11/pkcs11-private-key.c
+pkcs11/pkcs11-properties.c
+pkcs11/pkcs11-request.c
+pkcs11/pkcs11-token.c
 src/seahorse.desktop.in


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