diff options
-rw-r--r-- | docs/reference/gck/gck-sections.txt | 2 | ||||
-rw-r--r-- | docs/reference/gcr/Makefile.am | 4 | ||||
-rw-r--r-- | docs/reference/gcr/gcr-sections.txt | 79 | ||||
-rw-r--r-- | gck/gck-misc.c | 12 | ||||
-rw-r--r-- | gck/gck.h | 4 | ||||
-rw-r--r-- | gck/gck.symbols | 1 | ||||
-rw-r--r-- | gcr/Makefile.am | 4 | ||||
-rw-r--r-- | gcr/gcr-base.symbols | 12 | ||||
-rw-r--r-- | gcr/gcr-certificate-widget.c | 7 | ||||
-rw-r--r-- | gcr/gcr-deprecated-base.h | 6 | ||||
-rw-r--r-- | gcr/gcr-deprecated.h | 5 | ||||
-rw-r--r-- | gcr/gcr-gnupg-importer.c | 315 | ||||
-rw-r--r-- | gcr/gcr-gnupg-importer.h | 67 | ||||
-rw-r--r-- | gcr/gcr-gnupg-process.c | 7 | ||||
-rw-r--r-- | gcr/gcr-gnupg-process.h | 2 | ||||
-rw-r--r-- | gcr/gcr-import-dialog.c | 15 | ||||
-rw-r--r-- | gcr/gcr-import-dialog.h | 17 | ||||
-rw-r--r-- | gcr/gcr-importer.c | 1144 | ||||
-rw-r--r-- | gcr/gcr-importer.h | 109 | ||||
-rw-r--r-- | gcr/gcr-internal.h | 1 | ||||
-rw-r--r-- | gcr/gcr-key-widget.c | 7 | ||||
-rw-r--r-- | gcr/gcr-pkcs11-importer.c | 582 | ||||
-rw-r--r-- | gcr/gcr-pkcs11-importer.h | 68 | ||||
-rw-r--r-- | gcr/gcr-record.h | 11 | ||||
-rw-r--r-- | gcr/gcr.symbols | 14 | ||||
-rw-r--r-- | po/POTFILES.in | 1 | ||||
-rw-r--r-- | tool/gkr-tool-import.c | 138 |
27 files changed, 1602 insertions, 1032 deletions
diff --git a/docs/reference/gck/gck-sections.txt b/docs/reference/gck/gck-sections.txt index 21069a8d..e200c28c 100644 --- a/docs/reference/gck/gck-sections.txt +++ b/docs/reference/gck/gck-sections.txt @@ -315,4 +315,6 @@ gck_value_to_ulong GCK_INVALID <SUBSECTION Private> gck_get_error_quark +GCK_TYPE_LIST +gck_list_get_boxed_type </SECTION> diff --git a/docs/reference/gcr/Makefile.am b/docs/reference/gcr/Makefile.am index 47499c97..d13563fd 100644 --- a/docs/reference/gcr/Makefile.am +++ b/docs/reference/gcr/Makefile.am @@ -64,11 +64,15 @@ IGNORE_HFILES= \ gcr-display-scrolled.h \ gcr-display-view.h \ gcr-failure-renderer.h \ + gcr-gnupg-renderer.h \ gcr-icons.h \ gcr-import-dialog.h \ gcr-internal.h \ gcr-live-search.h \ gcr-marshal.h \ + gcr-openpgp.h \ + gcr-pkcs11-renderer.h \ + gcr-record.h \ gcr-unlock-renderer.h \ gcr-xxx.h \ gcr-zzz.h diff --git a/docs/reference/gcr/gcr-sections.txt b/docs/reference/gcr/gcr-sections.txt index 08e7dd0a..22e19b05 100644 --- a/docs/reference/gcr/gcr-sections.txt +++ b/docs/reference/gcr/gcr-sections.txt @@ -80,31 +80,40 @@ GcrGeneralNameType <SECTION> <FILE>gcr-importer</FILE> GcrImporter -GcrImporterClass -GcrImporterPromptBehavior -gcr_importer_new -gcr_importer_listen -gcr_importer_queue +GcrImporterIface gcr_importer_import gcr_importer_import_async gcr_importer_import_finish -gcr_importer_get_slot -gcr_importer_set_slot -gcr_importer_get_prompt_behavior -gcr_importer_set_prompt_behavior -gcr_importer_get_parser -gcr_importer_set_parser +gcr_importer_register +gcr_importer_register_well_known +gcr_importer_create_for_parsed +gcr_importer_queue_for_parsed +gcr_importer_queue_and_filter_for_parsed <SUBSECTION Standard> -GcrImporterPrivate GCR_IMPORTER GCR_IS_IMPORTER GCR_TYPE_IMPORTER gcr_importer_get_type -GCR_IMPORTER_CLASS -GCR_IS_IMPORTER_CLASS -GCR_IMPORTER_GET_CLASS -GCR_TYPE_IMPORTER_PROMPT_BEHAVIOR -gcr_importer_prompt_behavior_get_type +GCR_IMPORTER_GET_INTERFACE +<SUBSECTION Private> +GCR_GNUPG_IMPORTER +GCR_GNUPG_IMPORTER_CLASS +GCR_GNUPG_IMPORTER_GET_CLASS +GCR_IS_GNUPG_IMPORTER +GCR_IS_GNUPG_IMPORTER_CLASS +GCR_IS_PKCS11_IMPORTER +GCR_IS_PKCS11_IMPORTER_CLASS +GCR_PKCS11_IMPORTER +GCR_PKCS11_IMPORTER_CLASS +GCR_PKCS11_IMPORTER_GET_CLASS +GCR_TYPE_GNUPG_IMPORTER +GCR_TYPE_PKCS11_IMPORTER +GcrGnupgImporter +GcrGnupgImporterClass +GcrGnupgImporterPrivate +GcrPkcs11Importer +GcrPkcs11ImporterClass +GcrPkcs11ImporterPrivate </SECTION> <SECTION> @@ -533,17 +542,6 @@ GCR_VIEWER_WINDOW_GET_CLASS <SECTION> <FILE>gcr-private</FILE> <SUBSECTION Private> -GCR_RECORD_SCHEMA_PUB -GCR_RECORD_SCHEMA_UID -GCR_RECORD_SCHEMA_SEC -GCR_RECORD_SCHEMA_ATTRIBUTE -GCR_RECORD_SCHEMA_FPR -GCR_RECORD_SCHEMA_XA1 -GCR_RECORD_SCHEMA_RVK -GCR_RECORD_SCHEMA_SIG -GCR_RECORD_SCHEMA_SSB -GCR_RECORD_SCHEMA_SUB -GCR_RECORD_SCHEMA_UAT GCR_GNUPG_COLLECTION GCR_GNUPG_COLLECTION_CLASS GCR_GNUPG_COLLECTION_GET_CLASS @@ -557,19 +555,6 @@ GCR_IS_GNUPG_KEY GCR_IS_GNUPG_KEY_CLASS GCR_TYPE_GNUPG_COLLECTION GCR_TYPE_GNUPG_KEY -GCR_TYPE_RECORD -GcrRecordColumns -GcrRecordPubColumns -GcrRecordUidColumns -GcrRecordSecColumns -GcrRecordAttributeColumns -GcrRecordFprColumns -GcrRecordXa1Columns -GcrRecordKeyColumns -GcrRecordRvkColumns -GcrRecordSigColumns -GcrRecordUatColumns -GcrRecord GCR_GNUPG_PROCESS GCR_GNUPG_PROCESS_CLASS GCR_GNUPG_PROCESS_GET_CLASS @@ -600,7 +585,6 @@ GcrMemoryIconPrivate GCR_ERROR gcr_error_get_domain GcrOpensshPubCallback -GcrOpenpgpCallback GCR_CALLBACK_OUTPUT_STREAM GCR_CALLBACK_OUTPUT_STREAM_CLASS GCR_CALLBACK_OUTPUT_STREAM_GET_CLASS @@ -610,15 +594,4 @@ GCR_TYPE_CALLBACK_OUTPUT_STREAM GcrCallbackOutputFunc GcrCallbackOutputStream GcrCallbackOutputStreamClass -GcrOpenpgpParseFlags -GCR_GNUPG_RENDERER -GCR_GNUPG_RENDERER_CLASS -GCR_GNUPG_RENDERER_GET_CLASS -GCR_IS_GNUPG_RENDERER -GCR_IS_GNUPG_RENDERER_CLASS -GCR_TYPE_GNUPG_RENDERER -GcrGnupgRenderer -GcrGnupgRendererClass -GcrGnupgRendererPrivate -GcrOpenpgpAlgo </SECTION> diff --git a/gck/gck-misc.c b/gck/gck-misc.c index 4004954d..563f40dc 100644 --- a/gck/gck-misc.c +++ b/gck/gck-misc.c @@ -212,6 +212,18 @@ _gck_stringize_rv (CK_RV rv) * library or PKCS11 in general. */ +GType +gck_list_get_boxed_type (void) +{ + static GType type = 0; + if (!type) + type = g_boxed_type_register_static ("GckList", + (GBoxedCopyFunc)gck_list_ref_copy, + (GBoxedFreeFunc)gck_list_unref_free); + return type; + +} + /** * gck_list_unref_free: * @reflist: List of Gobject reference counted pointers. @@ -50,6 +50,10 @@ G_BEGIN_DECLS GQuark gck_get_error_quark (void); +#define GCK_TYPE_LIST (gck_list_get_boxed_type ()) + +GType gck_list_get_boxed_type (void) G_GNUC_CONST; + GList* gck_list_ref_copy (GList *reflist); void gck_list_unref_free (GList *reflist); diff --git a/gck/gck.symbols b/gck/gck.symbols index c6cfb1e2..18df7b65 100644 --- a/gck/gck.symbols +++ b/gck/gck.symbols @@ -59,6 +59,7 @@ gck_enumerator_next_n gck_get_error_quark gck_list_ref_copy gck_list_unref_free +gck_list_get_boxed_type gck_mechanism_info_free gck_mechanisms_check gck_message_from_rv diff --git a/gcr/Makefile.am b/gcr/Makefile.am index f4f86e06..4d31f9a4 100644 --- a/gcr/Makefile.am +++ b/gcr/Makefile.am @@ -97,11 +97,13 @@ libgcr_base_@GCR_MAJOR@_la_SOURCES = \ gcr-comparable.c gcr-comparable.h \ gcr-debug.c gcr-debug.h \ gcr-gnupg-collection.c gcr-gnupg-collection.h \ + gcr-gnupg-importer.c gcr-gnupg-importer.h \ gcr-gnupg-key.c gcr-gnupg-key.h \ gcr-gnupg-process.c gcr-gnupg-process.h \ gcr-gnupg-records.c gcr-gnupg-records.h \ gcr-gnupg-util.c gcr-gnupg-util.h \ gcr-library.c gcr-library.h \ + gcr-importer.c gcr-importer.h \ gcr-internal.h \ gcr-memory.c \ gcr-memory-icon.c gcr-memory-icon.h \ @@ -109,6 +111,7 @@ libgcr_base_@GCR_MAJOR@_la_SOURCES = \ gcr-openssh.c gcr-openssh.h \ gcr-parser.c gcr-parser.h \ gcr-pkcs11-certificate.c gcr-pkcs11-certificate.h \ + gcr-pkcs11-importer.c gcr-pkcs11-importer.h \ gcr-record.c gcr-record.h \ gcr-simple-certificate.c gcr-simple-certificate.h \ gcr-simple-collection.c gcr-simple-collection.h \ @@ -136,7 +139,6 @@ libgcr_@GCR_MAJOR@_la_SOURCES = \ gcr-gnupg-records.c gcr-gnupg-records.h \ gcr-icons.c gcr-icons.h \ gcr-import-dialog.c gcr-import-dialog.h \ - gcr-importer.c gcr-importer.h \ gcr-key-renderer.c gcr-key-renderer.h \ gcr-key-widget.c gcr-key-widget.h \ gcr-list-selector.c gcr-list-selector.h gcr-list-selector-private.h \ diff --git a/gcr/gcr-base.symbols b/gcr/gcr-base.symbols index 49b54e85..015543c6 100644 --- a/gcr/gcr-base.symbols +++ b/gcr/gcr-base.symbols @@ -52,6 +52,18 @@ gcr_data_error_get_domain gcr_data_error_get_type gcr_data_format_get_type gcr_error_get_domain +gcr_importer_get_parser +gcr_importer_get_prompt_behavior +gcr_importer_get_slot +gcr_importer_create_for_parsed +gcr_importer_get_type +gcr_importer_import +gcr_importer_import_async +gcr_importer_import_finish +gcr_importer_queue_and_filter_for_parsed +gcr_importer_queue_for_parsed +gcr_importer_register +gcr_importer_register_well_known gcr_parser_add_password gcr_parser_format_disable gcr_parser_format_enable diff --git a/gcr/gcr-certificate-widget.c b/gcr/gcr-certificate-widget.c index 82a8d91f..272354bd 100644 --- a/gcr/gcr-certificate-widget.c +++ b/gcr/gcr-certificate-widget.c @@ -169,7 +169,6 @@ static void gcr_certificate_widget_class_init (GcrCertificateWidgetClass *klass) { GObjectClass *gobject_class = G_OBJECT_CLASS (klass); - GckAttributes *registered; gcr_certificate_widget_parent_class = g_type_class_peek_parent (klass); g_type_class_add_private (klass, sizeof (GcrCertificateWidgetPrivate)); @@ -186,12 +185,6 @@ gcr_certificate_widget_class_init (GcrCertificateWidgetClass *klass) g_object_class_install_property (gobject_class, PROP_ATTRIBUTES, g_param_spec_boxed ("attributes", "Attributes", "Attributes which contain the certificate", GCK_TYPE_ATTRIBUTES, G_PARAM_READWRITE)); - - /* Register this as a renderer which can be loaded */ - registered = gck_attributes_new (); - gck_attributes_add_ulong (registered, CKA_CLASS, CKO_CERTIFICATE); - gcr_renderer_register (GCR_TYPE_CERTIFICATE_WIDGET, registered); - gck_attributes_unref (registered); } /* ----------------------------------------------------------------------------- diff --git a/gcr/gcr-deprecated-base.h b/gcr/gcr-deprecated-base.h index 9f812817..b8536709 100644 --- a/gcr/gcr-deprecated-base.h +++ b/gcr/gcr-deprecated-base.h @@ -31,6 +31,7 @@ #include <glib.h> +#include "gcr-importer.h" #include "gcr-parser.h" #include "gcr-simple-collection.h" @@ -43,6 +44,11 @@ GQuark gcr_error_get_domain (void) G_GNUC_CONS gboolean gcr_simple_collection_contains (GcrSimpleCollection *self, GObject *object); +GcrParser * gcr_importer_get_parser (GcrImporter *self); + +void gcr_importer_set_parser (GcrImporter *self, + GcrParser *parser); + G_END_DECLS #endif /* GCR_DISABLE_DEPRECATED */ diff --git a/gcr/gcr-deprecated.h b/gcr/gcr-deprecated.h index b3bfad74..25cbc1d9 100644 --- a/gcr/gcr-deprecated.h +++ b/gcr/gcr-deprecated.h @@ -41,11 +41,6 @@ G_BEGIN_DECLS void gcr_renderer_render (GcrRenderer *self, GcrViewer *viewer); -GcrParser* gcr_importer_get_parser (GcrImporter *self); - -void gcr_importer_set_parser (GcrImporter *self, - GcrParser *parser); - G_END_DECLS #endif /* GCR_DISABLE_DEPRECATED */ diff --git a/gcr/gcr-gnupg-importer.c b/gcr/gcr-gnupg-importer.c new file mode 100644 index 00000000..f527938c --- /dev/null +++ b/gcr/gcr-gnupg-importer.c @@ -0,0 +1,315 @@ +/* + * gnome-keyring + * + * Copyright (C) 2011 Collabora Ltd. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as + * published by the Free Software Foundation; either version 2.1 of + * the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA + * 02111-1307, USA. + * + * Author: Stef Walter <stefw@collabora.co.uk> + */ + +#include "config.h" + +#include "gcr-gnupg-importer.h" +#include "gcr-gnupg-process.h" +#include "gcr-internal.h" + +#include <glib/gi18n-lib.h> + +enum { + PROP_0, + PROP_LABEL, + PROP_ICON, + PROP_IMPORTED, + PROP_DIRECTORY +}; + +struct _GcrGnupgImporterPrivate { + GcrGnupgProcess *process; + GMemoryInputStream *packets; + GArray *imported; +}; + +static void gcr_gnupg_importer_iface (GcrImporterIface *iface); + +G_DEFINE_TYPE_WITH_CODE (GcrGnupgImporter, _gcr_gnupg_importer, G_TYPE_OBJECT, + G_IMPLEMENT_INTERFACE (GCR_TYPE_IMPORTER, gcr_gnupg_importer_iface); +); + +static void +_gcr_gnupg_importer_init (GcrGnupgImporter *self) +{ + self->pv = G_TYPE_INSTANCE_GET_PRIVATE (self, GCR_TYPE_GNUPG_IMPORTER, GcrGnupgImporterPrivate); + self->pv->packets = G_MEMORY_INPUT_STREAM (g_memory_input_stream_new ()); + self->pv->imported = g_array_new (TRUE, TRUE, sizeof (gchar *)); +} + +static void +_gcr_gnupg_importer_dispose (GObject *obj) +{ + GcrGnupgImporter *self = GCR_GNUPG_IMPORTER (obj); + + if (self->pv->process) + g_object_run_dispose (G_OBJECT (self->pv->process)); + g_clear_object (&self->pv->process); + g_clear_object (&self->pv->packets); + + G_OBJECT_CLASS (_gcr_gnupg_importer_parent_class)->dispose (obj); +} + +static void +_gcr_gnupg_importer_finalize (GObject *obj) +{ + GcrGnupgImporter *self = GCR_GNUPG_IMPORTER (obj); + + g_array_free (self->pv->imported, TRUE); + + G_OBJECT_CLASS (_gcr_gnupg_importer_parent_class)->finalize (obj); +} + +static gchar * +calculate_label (GcrGnupgImporter *self) +{ + const gchar *directory; + + directory = _gcr_gnupg_process_get_directory (self->pv->process); + if (directory == NULL) + return g_strdup (_("GnuPG Keyring")); + else + return g_strdup_printf (_("GnuPG Keyring: %s"), directory); +} + +static GIcon * +calculate_icon (GcrGnupgImporter *self) +{ + const gchar *directory; + + directory = _gcr_gnupg_process_get_directory (self->pv->process); + if (directory == NULL) + return g_themed_icon_new ("user-home"); + else + return g_themed_icon_new ("folder"); +} + +static gboolean +on_process_status_record (GcrGnupgProcess *process, + GcrRecord *record, + gpointer user_data) +{ + GcrGnupgImporter *self = GCR_GNUPG_IMPORTER (user_data); + const gchar *value; + gchar *fingerprint; + + if (_gcr_record_get_schema (record) != GCR_RECORD_SCHEMA_IMPORT_OK) + return TRUE; + + value = _gcr_record_get_raw (record, GCR_RECORD_IMPORT_FINGERPRINT); + if (value != NULL && value[0] != 0) { + fingerprint = g_strdup (value); + g_array_append_val (self->pv->imported, fingerprint); + } + + return TRUE; +} + +static void +_gcr_gnupg_importer_set_property (GObject *obj, + guint prop_id, + const GValue *value, + GParamSpec *pspec) +{ + GcrGnupgImporter *self = GCR_GNUPG_IMPORTER (obj); + + switch (prop_id) { + case PROP_DIRECTORY: + self->pv->process = _gcr_gnupg_process_new (g_value_get_string (value), + NULL); + _gcr_gnupg_process_set_input_stream (self->pv->process, G_INPUT_STREAM (self->pv->packets)); + g_signal_connect (self->pv->process, "status-record", G_CALLBACK (on_process_status_record), self); + break; + default: + G_OBJECT_WARN_INVALID_PROPERTY_ID (obj, prop_id, pspec); + break; + } +} + +static void +_gcr_gnupg_importer_get_property (GObject *obj, + guint prop_id, + GValue *value, + GParamSpec *pspec) +{ + GcrGnupgImporter *self = GCR_GNUPG_IMPORTER (obj); + + switch (prop_id) { + case PROP_LABEL: + g_value_take_string (value, calculate_label (self)); + break; + case PROP_ICON: + g_value_take_object (value, calculate_icon (self)); + break; + case PROP_IMPORTED: + g_value_set_boxed (value, _gcr_gnupg_importer_get_imported (self)); + break; + case PROP_DIRECTORY: + g_value_set_string (value, _gcr_gnupg_process_get_directory (self->pv->process)); + break; + default: + G_OBJECT_WARN_INVALID_PROPERTY_ID (obj, prop_id, pspec); + break; + } +} + +static void +_gcr_gnupg_importer_class_init (GcrGnupgImporterClass *klass) +{ + GObjectClass *gobject_class = G_OBJECT_CLASS (klass); + GckAttributes *registered; + + gobject_class->dispose = _gcr_gnupg_importer_dispose; + gobject_class->finalize = _gcr_gnupg_importer_finalize; + gobject_class->set_property = _gcr_gnupg_importer_set_property; + gobject_class->get_property = _gcr_gnupg_importer_get_property; + + g_type_class_add_private (gobject_class, sizeof (GcrGnupgImporterPrivate)); + + g_object_class_override_property (gobject_class, PROP_LABEL, "label"); + + g_object_class_override_property (gobject_class, PROP_ICON, "icon"); + + g_object_class_install_property (gobject_class, PROP_IMPORTED, + g_param_spec_boxed ("imported", "Imported", "Fingerprints of imported keys", + G_TYPE_STRV, G_PARAM_READABLE)); + + g_object_class_install_property (gobject_class, PROP_DIRECTORY, + g_param_spec_string ("directory", "Directory", "Directory to import keys to", + NULL, G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY)); + + registered = gck_attributes_new (); + gck_attributes_add_ulong (registered, CKA_CLASS, CKO_GCR_GNUPG_RECORDS); + gcr_importer_register (GCR_TYPE_GNUPG_IMPORTER, registered); + gck_attributes_unref (registered); + + _gcr_initialize_library (); +} + +static GList * +_gcr_gnupg_importer_create_for_parsed (GcrParser *parser) +{ + GcrImporter *self; + + if (gcr_parser_get_parsed_format (parser) != GCR_FORMAT_OPENPGP_PACKET) + return FALSE; + + self = _gcr_gnupg_importer_new (NULL); + if (!gcr_importer_queue_for_parsed (self, parser)) + g_assert_not_reached (); + + return g_list_append (NULL, self); +} + +static gboolean +_gcr_gnupg_importer_queue_for_parsed (GcrImporter *importer, + GcrParser *parser) +{ + GcrGnupgImporter *self = GCR_GNUPG_IMPORTER (importer); + gconstpointer block; + gsize n_block; + + if (gcr_parser_get_parsed_format (parser) != GCR_FORMAT_OPENPGP_PACKET) + return FALSE; + + block = gcr_parser_get_parsed_block (parser, &n_block); + g_return_val_if_fail (block, FALSE); + + g_memory_input_stream_add_data (self->pv->packets, g_memdup (block, n_block), + n_block, g_free); + return TRUE; +} + +static void +on_process_run_complete (GObject *source, + GAsyncResult *result, + gpointer user_data) +{ + GSimpleAsyncResult *res = G_SIMPLE_ASYNC_RESULT (user_data); + GError *error = NULL; + + if (!_gcr_gnupg_process_run_finish (GCR_GNUPG_PROCESS (source), result, &error)) + g_simple_async_result_take_error (res, error); + + g_simple_async_result_complete (res); + g_object_unref (res); +} + +static void +_gcr_gnupg_importer_import_async (GcrImporter *importer, + GCancellable *cancellable, + GAsyncReadyCallback callback, + gpointer user_data) +{ + GcrGnupgImporter *self = GCR_GNUPG_IMPORTER (importer); + GSimpleAsyncResult *res; + const gchar *argv[] = { "--import", NULL }; + + res = g_simple_async_result_new (G_OBJECT (importer), callback, user_data, + _gcr_gnupg_importer_import_async); + + _gcr_gnupg_process_run_async (self->pv->process, argv, NULL, + GCR_GNUPG_PROCESS_WITH_STATUS, + cancellable, on_process_run_complete, + g_object_ref (res)); + + g_object_unref (res); +} + +static gboolean +_gcr_gnupg_importer_import_finish (GcrImporter *importer, + GAsyncResult *result, + GError **error) +{ + g_return_val_if_fail (g_simple_async_result_is_valid (result, G_OBJECT (importer), + _gcr_gnupg_importer_import_async), FALSE); + + if (g_simple_async_result_propagate_error (G_SIMPLE_ASYNC_RESULT (result), error)) + return FALSE; + + return TRUE; +} + +static void +gcr_gnupg_importer_iface (GcrImporterIface *iface) +{ + iface->create_for_parsed = _gcr_gnupg_importer_create_for_parsed; + iface->queue_for_parsed = _gcr_gnupg_importer_queue_for_parsed; + iface->import_async = _gcr_gnupg_importer_import_async; + iface->import_finish = _gcr_gnupg_importer_import_finish; +} + +GcrImporter * +_gcr_gnupg_importer_new (const gchar *directory) +{ + return g_object_new (GCR_TYPE_GNUPG_IMPORTER, + "directory", directory, + NULL); +} + +const gchar ** +_gcr_gnupg_importer_get_imported (GcrGnupgImporter *self) +{ + g_return_val_if_fail (GCR_IS_GNUPG_IMPORTER (self), NULL); + return (const gchar **)self->pv->imported->data; +} diff --git a/gcr/gcr-gnupg-importer.h b/gcr/gcr-gnupg-importer.h new file mode 100644 index 00000000..8511af84 --- /dev/null +++ b/gcr/gcr-gnupg-importer.h @@ -0,0 +1,67 @@ +/* + * gnome-keyring + * + * Copyright (C) 2011 Collabora Ltd. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as + * published by the Free Software Foundation; either version 2.1 of + * the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA + * 02111-1307, USA. + * + * Author: Stef Walter <stefw@collabora.co.uk> + */ + +#if !defined (__GCR_H_INSIDE__) && !defined (GCR_COMPILATION) +#error "Only <gcr/gcr.h> can be included directly." +#endif + +#ifndef __GCR_GNUPG_IMPORTER_H__ +#define __GCR_GNUPG_IMPORTER_H__ + +#include "gcr-importer.h" + +#include <glib-object.h> + +G_BEGIN_DECLS + +#define GCR_TYPE_GNUPG_IMPORTER (_gcr_gnupg_importer_get_type ()) +#define GCR_GNUPG_IMPORTER(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), GCR_TYPE_GNUPG_IMPORTER, GcrGnupgImporter)) +#define GCR_GNUPG_IMPORTER_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), GCR_TYPE_GNUPG_IMPORTER, GcrGnupgImporterClass)) +#define GCR_IS_GNUPG_IMPORTER(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), GCR_TYPE_GNUPG_IMPORTER)) +#define GCR_IS_GNUPG_IMPORTER_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), GCR_TYPE_GNUPG_IMPORTER)) +#define GCR_GNUPG_IMPORTER_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), GCR_TYPE_GNUPG_IMPORTER, GcrGnupgImporterClass)) + +typedef struct _GcrGnupgImporter GcrGnupgImporter; +typedef struct _GcrGnupgImporterClass GcrGnupgImporterClass; +typedef struct _GcrGnupgImporterPrivate GcrGnupgImporterPrivate; + +struct _GcrGnupgImporter { + GObject parent; + + /*< private >*/ + GcrGnupgImporterPrivate *pv; +}; + +struct _GcrGnupgImporterClass { + GObjectClass parent_class; +}; + +GType _gcr_gnupg_importer_get_type (void) G_GNUC_CONST; + +GcrImporter * _gcr_gnupg_importer_new (const gchar *directory); + +const gchar ** _gcr_gnupg_importer_get_imported (GcrGnupgImporter *self); + +G_END_DECLS + +#endif /* __GCR_IMPORTER_H__ */ diff --git a/gcr/gcr-gnupg-process.c b/gcr/gcr-gnupg-process.c index ed4592aa..0e87ea9a 100644 --- a/gcr/gcr-gnupg-process.c +++ b/gcr/gcr-gnupg-process.c @@ -343,6 +343,13 @@ _gcr_gnupg_process_new (const gchar *directory, const gchar *executable) NULL); } +const gchar * +_gcr_gnupg_process_get_directory (GcrGnupgProcess *self) +{ + g_return_val_if_fail (GCR_GNUPG_PROCESS (self), NULL); + return self->pv->directory; +} + GInputStream * _gcr_gnupg_process_get_input_stream (GcrGnupgProcess *self) { diff --git a/gcr/gcr-gnupg-process.h b/gcr/gcr-gnupg-process.h index 8eabfeba..820f371c 100644 --- a/gcr/gcr-gnupg-process.h +++ b/gcr/gcr-gnupg-process.h @@ -68,6 +68,8 @@ GType _gcr_gnupg_process_get_type (void) G_GNUC_CON GcrGnupgProcess* _gcr_gnupg_process_new (const gchar *directory, const gchar *executable); +const gchar * _gcr_gnupg_process_get_directory (GcrGnupgProcess *self); + GInputStream * _gcr_gnupg_process_get_input_stream (GcrGnupgProcess *self); void _gcr_gnupg_process_set_input_stream (GcrGnupgProcess *self, diff --git a/gcr/gcr-import-dialog.c b/gcr/gcr-import-dialog.c index 11960350..2c73924c 100644 --- a/gcr/gcr-import-dialog.c +++ b/gcr/gcr-import-dialog.c @@ -27,21 +27,16 @@ #include "egg/egg-entry-buffer.h" +#if TODO + enum { PROP_0, - PROP_SELECTED_SLOT, - PROP_PASSWORD, + PROP_IMPORTER, + PROP_IMPORTERS, PROP_PRIMARY_TEXT, PROP_SECONDARY_TEXT }; -enum { - COLUMN_SLOT, - COLUMN_ICON, - COLUMN_LABEL, - N_COLUMNS -}; - struct _GcrImportDialogPrivate { GtkBuilder *builder; GtkEntry *entry; @@ -460,3 +455,5 @@ _gcr_import_dialog_set_secondary_text (GcrImportDialog *self, const gchar *text) gtk_label_set_markup (GTK_LABEL (gtk_builder_get_object (self->pv->builder, "secondary-text")), text); g_object_notify (G_OBJECT (self), "primary-text"); } + +#endif /* TODO */ diff --git a/gcr/gcr-import-dialog.h b/gcr/gcr-import-dialog.h index 00e147e4..692e71ab 100644 --- a/gcr/gcr-import-dialog.h +++ b/gcr/gcr-import-dialog.h @@ -52,24 +52,15 @@ struct _GcrImportDialogClass { GType _gcr_import_dialog_get_type (void); -GcrImportDialog* _gcr_import_dialog_new (void); +GcrImportDialog * _gcr_import_dialog_new (GList *importers); gboolean _gcr_import_dialog_run (GcrImportDialog *self, GtkWindow *parent); -GckSlot* _gcr_import_dialog_get_selected_slot (GcrImportDialog *self); +GcrImporter * _gcr_import_dialog_get_importer (GcrImportDialog *self); -void _gcr_import_dialog_set_selected_slot (GcrImportDialog *self, - GckSlot *slot); - -void _gcr_import_dialog_show_selected_slot (GcrImportDialog *self); - -void _gcr_import_dialog_hide_selected_slot (GcrImportDialog *self); - -const gchar* _gcr_import_dialog_get_password (GcrImportDialog *self); - -void _gcr_import_dialog_set_password (GcrImportDialog *self, - const gchar *password); +void _gcr_import_dialog_set_importer (GcrImportDialog *self, + GcrImporter *importer); void _gcr_import_dialog_show_password (GcrImportDialog *self); diff --git a/gcr/gcr-importer.c b/gcr/gcr-importer.c index 1f0f6dcd..49d8cd66 100644 --- a/gcr/gcr-importer.c +++ b/gcr/gcr-importer.c @@ -1,976 +1,454 @@ -/* +/* * gnome-keyring - * - * Copyright (C) 2008 Stefan Walter - * - * This program is free software; you can redistribute it and/or modify + * + * Copyright (C) 2011 Collabora Ltd. + * + * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU Lesser General Public License as * published by the Free Software Foundation; either version 2.1 of * the License, or (at your option) any later version. - * + * * This program is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. - * + * * You should have received a copy of the GNU Lesser General Public * License along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA - * 02111-1307, USA. + * 02111-1307, USA. + * + * Author: Stef Walter <stefw@collabora.co.uk> */ #include "config.h" -#include "gcr-import-dialog.h" +#include "gcr-base.h" #include "gcr-importer.h" #include "gcr-internal.h" #include "gcr-marshal.h" +#include "gcr-gnupg-importer.h" #include "gcr-parser.h" +#include "gcr-pkcs11-importer.h" #include <glib/gi18n-lib.h> /** * SECTION:gcr-importer * @title: GcrImporter - * @short_description: Import objects into PKCS\#11 slots. + * @short_description: Import certificates and keys + * + * An interface which allows importing of certificates and keys. Each + * #GcrImporter is registered with a set of PKCS#11 attributes to match + * stuff that it can import. * - * A #GcrImporter can be used to import items into PKCS\#11 slots. It's most - * often used to parse the objects parsed with a #GcrParser. Use - * gcr_importer_listen() to hook up the importer to the parser. + * An importer gets passed a #GcrParser and accesses the currently parsed + * item. To create a set of importers that can import the currently parsed + * item in a #GcrParser, use gcr_importer_create_for_parsed(). The list of + * importers returned has the parsed item queued for import. * - * Items are queued, and then imported with gcr_importer_import() or - * gcr_importer_import_async(). + * To queue additional items with a importer use gcr_importer_queue_for_parsed(). + * In addition you can try and queue an additional item with a set of importers + * using the gcr_importer_queue_and_filter_for_parsed(). + * + * To start the import use gcr_importer_import() or the async variants. */ /** * GcrImporter: * - * Imports items into PKCS\#11 + * Imports certificates and keys */ /** - * GcrImporterClass: - * @parent_class: The parent class - * @queued: Signal which is fired when an item is queued - * @imported: Signal which is fired when an item is imported + * GcrImporterIface: * - * The class for #GcrImporter. + * Interface implemented for a #GcrImporter. */ -/** - * GcrImporterPromptBehavior: - * @GCR_IMPORTER_PROMPT_NEEDED: Prompt when needed. - * @GCR_IMPORTER_PROMPT_ALWAYS: Always prompt. - * @GCR_IMPORTER_PROMPT_NEVER: Never prompt. - * - * Flags for the prompting behavior of #GcrImporter. - */ +typedef GcrImporterIface GcrImporterInterface; -enum { - PROP_0, - PROP_SLOT, - PROP_PROMPT_BEHAVIOR -}; - -enum { - QUEUED, - IMPORTED, - LAST_SIGNAL -}; - -static guint signals[LAST_SIGNAL] = { 0 }; - -struct _GcrImporterPrivate { - GckSlot *slot; - GcrParser *parser; - GcrImporterPromptBehavior behavior; - - /* Information about last import */ - GError *error; - gboolean succeeded; - - /* State data during import */ - gboolean processing; - GCancellable *cancel; - gboolean prompted; - gboolean async; - GByteArray *buffer; - GckSession *session; - GQueue queue; - gboolean any_private; - - /* Extra async stuff */ - GAsyncReadyCallback callback; - gpointer user_data; -}; - -/* State forward declarations */ -static void state_cancelled (GcrImporter *self, gboolean async); -static void state_complete (GcrImporter *self, gboolean async); -static void state_create_object (GcrImporter *self, gboolean async); -static void state_open_session (GcrImporter *self, gboolean async); -static void state_initialize_pin (GcrImporter *self, gboolean async); - -static void gcr_importer_async_result (GAsyncResultIface *iface); -G_DEFINE_TYPE_WITH_CODE (GcrImporter, gcr_importer, G_TYPE_OBJECT, - G_IMPLEMENT_INTERFACE (G_TYPE_ASYNC_RESULT, gcr_importer_async_result)); - -#define BLOCK 4096 - -/* ----------------------------------------------------------------------------- - * INTERNAL - */ +G_DEFINE_INTERFACE (GcrImporter, gcr_importer, 0); -static void -cleanup_state_data (GcrImporter *self) -{ +typedef struct _GcrRegistered { GckAttributes *attrs; + GType importer_type; +} GcrRegistered; - if (self->pv->buffer) - g_byte_array_free (self->pv->buffer, TRUE); - self->pv->buffer = NULL; - - if (self->pv->session) - g_object_unref (self->pv->session); - self->pv->session = NULL; - - while ((attrs = g_queue_pop_head (&self->pv->queue)) != NULL) - gck_attributes_unref (attrs); - g_assert (g_queue_is_empty (&self->pv->queue)); - self->pv->any_private = FALSE; - - if (self->pv->cancel) - g_object_unref (self->pv->cancel); - self->pv->cancel = NULL; -} +static GArray *registered_importers = NULL; +static gboolean registered_sorted = FALSE; static void -cleanup_import_data (GcrImporter *self) -{ - if (self->pv->error) - g_clear_error (&self->pv->error); - self->pv->succeeded = TRUE; -} - -static void -next_state (GcrImporter *self, void (*state) (GcrImporter*, gboolean)) -{ - g_assert (GCR_IS_IMPORTER (self)); - g_assert (self->pv->processing); - g_assert (state); - - if (self->pv->cancel && g_cancellable_is_cancelled (self->pv->cancel)) - state = state_cancelled; - - (state) (self, self->pv->async); -} - -static const gchar* -prepare_auth_primary (CK_OBJECT_CLASS klass) -{ - if (klass == CKO_PRIVATE_KEY) - return _("Enter password to unlock the private key"); - else if (klass == CKO_CERTIFICATE) - return _("Enter password to unlock the certificate"); - else - return _("Enter password to unlock"); -} - -static gchar* -prepare_auth_secondary (CK_OBJECT_CLASS klass, const gchar *label) -{ - if (label == NULL) { - if (klass == CKO_PRIVATE_KEY) { - /* TRANSLATORS: The key is locked. */ - return g_strdup (_("In order to import the private key, it must be unlocked")); - } else if (klass == CKO_CERTIFICATE) { - /* TRANSLATORS: The certificate is locked. */ - return g_strdup (_("In order to import the certificate, it must be unlocked")); - } else { - /* TRANSLATORS: The data is locked. */ - return g_strdup (_("In order to import the data, it must be unlocked")); - } - } else { - if (klass == CKO_PRIVATE_KEY) { - /* TRANSLATORS: The key is locked. */ - return g_strdup_printf (_("In order to import the private key '%s', it must be unlocked"), label); - } else if (klass == CKO_CERTIFICATE) { - /* TRANSLATORS: The certificate is locked. */ - return g_strdup_printf (_("In order to import the certificate '%s', it must be unlocked"), label); - } else { - /* TRANSLATORS: The object '%s' is locked. */ - return g_strdup_printf (_("In order to import '%s', it must be unlocked"), label); - } +gcr_importer_default_init (GcrImporterIface *iface) +{ + static volatile gsize initialized = 0; + + if (g_once_init_enter (&initialized)) { + + /** + * GcrImporter:label: + * + * The label for the importer. + */ + g_object_interface_install_property (iface, + g_param_spec_string ("label", "Label", "The label for the importer", + "", G_PARAM_READABLE)); + + /** + * GcrImporter:icon: + * + * The icon for the importer. + */ + g_object_interface_install_property (iface, + g_param_spec_object ("icon", "Icon", "The icon for the importer", + G_TYPE_ICON, G_PARAM_READABLE)); + + g_once_init_leave (&initialized, 1); } } -static void -on_parser_parsed (GcrParser *parser, GcrImporter *self) +/** + * gcr_importer_register: + * @importer_type: the GType of the importer being registered + * @attrs: the attributes that this importer is compatible with + * + * Register an importer to handle parsed items that match the given attributes. + */ +void +gcr_importer_register (GType importer_type, + GckAttributes *attrs) { - GckAttributes *attrs; + GcrRegistered registered; - g_return_if_fail (GCR_IS_PARSER (parser)); - g_return_if_fail (GCR_IS_IMPORTER (self)); + if (!registered_importers) + registered_importers = g_array_new (FALSE, FALSE, sizeof (GcrRegistered)); - attrs = gcr_parser_get_parsed_attributes (parser); - g_return_if_fail (attrs); - - gcr_importer_queue (self, gcr_parser_get_parsed_label (parser), attrs); + registered.importer_type = importer_type; + registered.attrs = gck_attributes_ref (attrs); + g_array_append_val (registered_importers, registered); + registered_sorted = FALSE; } -static gboolean -on_parser_authenticate (GcrParser *parser, gint count, GcrImporter *self) +static gint +sort_registered_by_n_attrs (gconstpointer a, gconstpointer b) { - GcrImportDialog *dialog; - GckAttributes *attrs; - const gchar *password; - gchar *text, *label; - GckSlot *slot; - gulong klass; - - dialog = _gcr_import_dialog_new (); - - if (self->pv->slot) - _gcr_import_dialog_set_selected_slot (dialog, self->pv->slot); - - /* Figure out the text for the dialog */ - attrs = gcr_parser_get_parsed_attributes (parser); - g_return_val_if_fail (attrs, FALSE); - - if (!gck_attributes_find_ulong (attrs, CKA_CLASS, &klass)) - klass = (gulong)-1; - if (!gck_attributes_find_string (attrs, CKA_LABEL, &label)) - label = NULL; + const GcrRegistered *ra = a; + const GcrRegistered *rb = b; + gulong na, nb; - text = prepare_auth_secondary (klass, label); - _gcr_import_dialog_set_primary_text (dialog, prepare_auth_primary (klass)); - _gcr_import_dialog_set_secondary_text (dialog, text); - g_free (label); - g_free (text); + g_assert (a); + g_assert (b); - if (!_gcr_import_dialog_run (dialog, NULL)) - return FALSE; + na = gck_attributes_count (ra->attrs); + nb = gck_attributes_count (rb->attrs); - slot = _gcr_import_dialog_get_selected_slot (dialog); - gcr_importer_set_slot (self, slot); - - password = _gcr_import_dialog_get_password (dialog); - gcr_parser_add_password (parser, password); - - g_object_unref (dialog); - self->pv->prompted = TRUE; - return TRUE; + /* Note we're sorting in reverse order */ + if (na < nb) + return 1; + return (na == nb) ? 0 : -1; } -/* --------------------------------------------------------------------------------- - * COMPLETE +/** + * gcr_importer_create_for_parsed: + * @parsed: a parser with a parsed item to import + * + * Create a set of importers which can import this parsed item. + * The parsed item is represented by the state of the GcrParser at the + * time of calling this method. + * + * Returns: a list of importers which can import the parsed item, which + * should be freed with gck_list_unref_free(). */ - -static void -state_complete (GcrImporter *self, gboolean async) -{ - if (async && self->pv->callback != NULL) - (self->pv->callback) (G_OBJECT (self), G_ASYNC_RESULT (self), self->pv->user_data); - - cleanup_state_data (self); - self->pv->processing = FALSE; -} - -static void -state_failure (GcrImporter *self, gboolean async) -{ - self->pv->succeeded = FALSE; - next_state (self, state_complete); -} - -static void -state_cancelled (GcrImporter *self, gboolean async) +GList * +gcr_importer_create_for_parsed (GcrParser *parser) { - if (self->pv->cancel && g_cancellable_is_cancelled (self->pv->cancel)) - g_cancellable_cancel (self->pv->cancel); - if (self->pv->error) - g_error_free (self->pv->error); - self->pv->error = g_error_new_literal (GCR_DATA_ERROR, GCR_ERROR_CANCELLED, _("The operation was cancelled")); - next_state (self, state_failure); -} + GcrRegistered *registered; + GcrImporterIface *iface; + gpointer instance_class; + GckAttributes *attrs; + gboolean matched; + gulong n_attrs; + GList *results = NULL; + gulong j; + gsize i; -/* --------------------------------------------------------------------------------- - * CREATE OBJECTS - */ + g_return_val_if_fail (GCR_IS_PARSER (parser), NULL); -static void -complete_create_object (GcrImporter *self, GckObject *object, GError *error) -{ - if (object == NULL) { - g_propagate_error (&self->pv->error, error); - next_state (self, state_failure); - - } else { - g_signal_emit (self, signals[IMPORTED], 0, object); - g_object_unref (object); - next_state (self, state_create_object); - } -} + gcr_importer_register_well_known (); -static void -on_create_object (GObject *obj, GAsyncResult *res, gpointer user_data) -{ - GError *error = NULL; - GckObject *object = gck_session_create_object_finish (GCK_SESSION (obj), res, &error); - complete_create_object (GCR_IMPORTER (user_data), object, error); -} + if (!registered_importers) + return NULL; -static void -state_create_object (GcrImporter *self, gboolean async) -{ - GckAttributes *attrs; - GckObject *object; - GError *error = NULL; - - /* No more objects */ - if (g_queue_is_empty (&self->pv->queue)) { - next_state (self, state_complete); - - } else { - - /* Pop first one off the list */ - attrs = g_queue_pop_head (&self->pv->queue); - g_assert (attrs); - - gck_attributes_add_boolean (attrs, CKA_TOKEN, CK_TRUE); - - if (async) { - gck_session_create_object_async (self->pv->session, attrs, self->pv->cancel, - on_create_object, self); - } else { - object = gck_session_create_object (self->pv->session, attrs, self->pv->cancel, &error); - complete_create_object (self, object, error); - } - - gck_attributes_unref (attrs); + if (!registered_sorted) { + g_array_sort (registered_importers, sort_registered_by_n_attrs); + registered_sorted = TRUE; } -} -/* --------------------------------------------------------------------------------- - * OPEN SESSION - */ + attrs = gcr_parser_get_parsed_attributes (parser); + if (attrs != NULL) + gck_attributes_ref (attrs); + else + attrs = gck_attributes_new (); -static void -complete_open_session (GcrImporter *self, GckSession *session, GError *error) -{ - if (!session) { - g_propagate_error (&self->pv->error, error); - next_state (self, state_failure); - } else { - self->pv->session = session; - next_state (self, state_create_object); - } -} + for (i = 0; i < registered_importers->len; ++i) { + registered = &(g_array_index (registered_importers, GcrRegistered, i)); + n_attrs = gck_attributes_count (registered->attrs); -static void -on_open_session (GObject *obj, GAsyncResult *res, gpointer user_data) -{ - GError *error = NULL; - GckSession *session = gck_slot_open_session_finish (GCK_SLOT (obj), res, &error); - complete_open_session (GCR_IMPORTER (user_data), session, error); -} + matched = TRUE; -static void -state_open_session (GcrImporter *self, gboolean async) -{ - guint options = GCK_SESSION_READ_WRITE; - GckSession *session; - GError *error = NULL; - - if (!self->pv->slot) { - g_set_error (&self->pv->error, GCR_DATA_ERROR, GCR_ERROR_FAILURE, _("No location available to import to")); - next_state (self, state_failure); - - } else { - if (self->pv->any_private) - options |= GCK_SESSION_LOGIN_USER; - - if (async) { - gck_slot_open_session_async (self->pv->slot, options, self->pv->cancel, - on_open_session, self); - } else { - session = gck_slot_open_session_full (self->pv->slot, options, 0, NULL, NULL, - self->pv->cancel, &error); - complete_open_session (self, session, error); + for (j = 0; j < n_attrs; ++j) { + if (!gck_attributes_contains (attrs, gck_attributes_at (registered->attrs, j))) { + matched = FALSE; + break; + } } - } -} -/* --------------------------------------------------------------------------------- - * INITIALIZE TOKEN - * - * HACK: This is a big temporary hack to get, until the next version - * when we can fix this correctly. - */ + if (matched) { + instance_class = g_type_class_ref (registered->importer_type); -static CK_RV -hacky_perform_initialize_pin (GckSlot *slot) -{ - CK_FUNCTION_LIST_PTR funcs; - CK_SESSION_HANDLE session; - CK_SLOT_ID slot_id; - CK_RV rv; - - /* - * This hack only works when: - * - * - Module is protected authentication path - * - No other sessions are open. - * - * Thankfully this is the case with gnome-keyring-daemon and - * the gnome-keyring tool. - */ - - funcs = gck_module_get_functions (gck_slot_get_module (slot)); - g_return_val_if_fail (funcs, CKR_GENERAL_ERROR); - slot_id = gck_slot_get_handle (slot); - - rv = funcs->C_OpenSession (slot_id, CKF_RW_SESSION | CKF_SERIAL_SESSION, NULL, NULL, &session); - if (rv != CKR_OK) - return rv; - - rv = funcs->C_Login (session, CKU_SO, NULL, 0); - if (rv == CKR_OK) { - rv = funcs->C_InitPIN (session, NULL, 0); - funcs->C_Logout (session); - } - - funcs->C_CloseSession (session); - - return rv; -} + iface = g_type_interface_peek (instance_class, GCR_TYPE_IMPORTER); + g_return_val_if_fail (iface != NULL, NULL); + g_return_val_if_fail (iface->create_for_parsed, NULL); + results = g_list_concat (results, (iface->create_for_parsed) (parser)); -static void -state_initialize_pin (GcrImporter *self, gboolean async) -{ - GckTokenInfo *info; - gboolean initialize; - CK_RV rv; - - g_assert (GCR_IS_IMPORTER (self)); - - /* HACK: Doesn't function when async */ - if (!async) { - g_return_if_fail (self->pv->slot); - info = gck_slot_get_token_info (self->pv->slot); - g_return_if_fail (info); - - initialize = !(info->flags & CKF_USER_PIN_INITIALIZED); - gck_token_info_free (info); - - if (initialize) { - rv = hacky_perform_initialize_pin (self->pv->slot); - if (rv != CKR_OK) { - g_propagate_error (&self->pv->error, g_error_new (GCK_ERROR, rv, "%s", gck_message_from_rv (rv))); - next_state (self, state_failure); - return; - } + g_type_class_unref (instance_class); } } - next_state (self, state_open_session); + gck_attributes_unref (attrs); + return results; } -/* --------------------------------------------------------------------------------- - * IMPORT PROMPT +/** + * gcr_importer_queue_for_parsed: + * @importer: an importer to add additional items to + * @parser: a parser with a parsed item to import + * + * Queues an additional item to be imported. The parsed item is represented + * by the state of the #GcrParser at the time of calling this method. + * + * If the parsed item is incompatible with the importer, then this will + * fail and the item will not be queued. + * + * Returns: whether the item was queued or not */ - -static void -complete_import_prompt (GcrImporter *self, GcrImportDialog *dialog, gint response) +gboolean +gcr_importer_queue_for_parsed (GcrImporter *importer, + GcrParser *parser) { - GckSlot *slot; + GcrImporterIface *iface; - gtk_widget_hide (GTK_WIDGET (dialog)); - self->pv->prompted = TRUE; + g_return_val_if_fail (GCR_IS_IMPORTER (importer), FALSE); + g_return_val_if_fail (GCR_IS_PARSER (parser), FALSE); - /* No dialog or dialog completed */ - if (response == GTK_RESPONSE_OK) { + iface = GCR_IMPORTER_GET_INTERFACE (importer); + g_return_val_if_fail (iface != NULL, FALSE); + g_return_val_if_fail (iface->queue_for_parsed != NULL, FALSE); - slot = _gcr_import_dialog_get_selected_slot (dialog); - gcr_importer_set_slot (self, slot); - next_state (self, state_initialize_pin); - - /* The dialog was cancelled or closed */ - } else { - next_state (self, state_cancelled); - } -} - -static void -on_prompt_response (GtkDialog *dialog, gint response, gpointer user_data) -{ - complete_import_prompt (GCR_IMPORTER (user_data), GCR_IMPORT_DIALOG (dialog), response); - g_object_unref (dialog); + return (iface->queue_for_parsed) (importer, parser); } -static void -state_import_prompt (GcrImporter *self, gboolean async) -{ - GcrImportDialog *dialog; - gboolean prompt; - gint response; - - g_assert (GCR_IS_IMPORTER (self)); - - /* No need to prompt */ - if (self->pv->prompted == TRUE) - prompt = FALSE; - else if (self->pv->behavior == GCR_IMPORTER_PROMPT_ALWAYS) - prompt = TRUE; - else if (self->pv->behavior == GCR_IMPORTER_PROMPT_NEVER) - prompt = FALSE; - else - prompt = self->pv->slot ? FALSE : TRUE; - - if (prompt == FALSE) { - next_state (self, state_initialize_pin); - - } else { - - dialog = _gcr_import_dialog_new (); - - _gcr_import_dialog_set_primary_text (dialog, _("Import Certificates/Keys")); - _gcr_import_dialog_hide_password (dialog); - - if (self->pv->slot) { - _gcr_import_dialog_set_selected_slot (dialog, self->pv->slot); - _gcr_import_dialog_hide_selected_slot (dialog); - } else { - _gcr_import_dialog_set_secondary_text (dialog, _("Choose a location to store the imported certificates/keys.")); - } - - /* Prompt without blocking main loop */ - if (async) { - g_signal_connect (dialog, "response", G_CALLBACK (on_prompt_response), self); - gtk_widget_show (GTK_WIDGET (dialog)); - - /* Block mainloop */ - } else { - response = gtk_dialog_run (GTK_DIALOG (dialog)); - complete_import_prompt (self, dialog, response); - g_object_unref (dialog); - } - } -} - -/* ----------------------------------------------------------------------------- - * OBJECT +/** + * gcr_importer_queue_and_filter_for_parsed: + * @importer: a set of importers + * @parser: a parser with a parsed item to import + * + * Queues an additional item to be imported in all compattible importers + * in the set. The parsed item is represented by the state of the #GcrParser + * at the time of calling this method. + * + * If the parsed item is incompatible with an importer, then that the item + * will not be queued on that importer. + * + * Returns: a new set of importers that queued the item, which should be freed + * with gck_list_unref_free(). */ - -static GObject* -gcr_importer_constructor (GType type, guint n_props, GObjectConstructParam *props) +GList * +gcr_importer_queue_and_filter_for_parsed (GList *importers, + GcrParser *parser) { - GcrImporter *self = GCR_IMPORTER (G_OBJECT_CLASS (gcr_importer_parent_class)->constructor(type, n_props, props)); - g_return_val_if_fail (self, NULL); - - return G_OBJECT (self); -} + GList *results = NULL; + GList *l; -static void -gcr_importer_init (GcrImporter *self) -{ - self->pv = G_TYPE_INSTANCE_GET_PRIVATE (self, GCR_TYPE_IMPORTER, GcrImporterPrivate); - self->pv->behavior = GCR_IMPORTER_PROMPT_NEEDED; - g_queue_init (&self->pv->queue); -} + for (l = importers; l != NULL; l = g_list_next (l)) { + if (gcr_importer_queue_for_parsed (l->data, parser)) + results = g_list_prepend (results, g_object_ref (l->data)); + } -static void -gcr_importer_dispose (GObject *obj) -{ - GcrImporter *self = GCR_IMPORTER (obj); - - cleanup_state_data (self); - cleanup_import_data (self); - - if (self->pv->parser) - g_object_unref (self->pv->parser); - self->pv->parser = NULL; - - if (self->pv->slot) - g_object_unref (self->pv->slot); - self->pv->slot = NULL; - - G_OBJECT_CLASS (gcr_importer_parent_class)->dispose (obj); + return g_list_reverse (results); } -static void -gcr_importer_finalize (GObject *obj) -{ - GcrImporter *self = GCR_IMPORTER (obj); - - g_assert (!self->pv->parser); - g_assert (!self->pv->slot); - - G_OBJECT_CLASS (gcr_importer_parent_class)->finalize (obj); -} +typedef struct { + gboolean complete; + GCond *cond; + GMutex *mutex; + GError *error; + GMainContext *context; +} ImportClosure; static void -gcr_importer_set_property (GObject *obj, guint prop_id, const GValue *value, - GParamSpec *pspec) +on_import_async_complete (GObject *source, + GAsyncResult *result, + gpointer user_data) { - GcrImporter *self = GCR_IMPORTER (obj); - - switch (prop_id) { - case PROP_SLOT: - gcr_importer_set_slot (self, g_value_get_object (value)); - break; - case PROP_PROMPT_BEHAVIOR: - gcr_importer_set_prompt_behavior (self, (GcrImporterPromptBehavior)g_value_get_int (value)); - break; - default: - G_OBJECT_WARN_INVALID_PROPERTY_ID (obj, prop_id, pspec); - break; - } -} + ImportClosure *closure = user_data; + GError *error = NULL; -static void -gcr_importer_get_property (GObject *obj, guint prop_id, GValue *value, - GParamSpec *pspec) -{ - GcrImporter *self = GCR_IMPORTER (obj); - - switch (prop_id) { - case PROP_SLOT: - g_value_set_object (value, gcr_importer_get_slot (self)); - break; - case PROP_PROMPT_BEHAVIOR: - g_value_set_int (value, gcr_importer_get_prompt_behavior (self)); - break; - default: - G_OBJECT_WARN_INVALID_PROPERTY_ID (obj, prop_id, pspec); - break; + if (!gcr_importer_import_finish (GCR_IMPORTER (source), result, &error)) { + if (error == NULL) { + g_warning ("%s::import_finished returned false, but did not set error", + G_OBJECT_TYPE_NAME (source)); + } } -} -static void -gcr_importer_class_init (GcrImporterClass *klass) -{ - GObjectClass *gobject_class = G_OBJECT_CLASS (klass); - - gobject_class->constructor = gcr_importer_constructor; - gobject_class->dispose = gcr_importer_dispose; - gobject_class->finalize = gcr_importer_finalize; - gobject_class->set_property = gcr_importer_set_property; - gobject_class->get_property = gcr_importer_get_property; - - g_type_class_add_private (gobject_class, sizeof (GcrImporterPrivate)); - - g_object_class_install_property (gobject_class, PROP_SLOT, - g_param_spec_object ("slot", "Slot", "PKCS#11 slot to import data into", - GCK_TYPE_SLOT, G_PARAM_READWRITE)); - - g_object_class_install_property (gobject_class, PROP_PROMPT_BEHAVIOR, - g_param_spec_int ("prompt-behavior", "Prompt Behavior", "Import Prompt Behavior", - 0, G_MAXINT, GCR_IMPORTER_PROMPT_NEEDED, G_PARAM_READWRITE)); - - /** - * GcrImporter::queued: - * @label: The label of the queued item. - * @attrs: The attributes of the queued item. - * - * This signal is emitted when an item is queued for import. - */ - signals[QUEUED] = g_signal_new ("queued", GCR_TYPE_IMPORTER, - G_SIGNAL_RUN_FIRST, G_STRUCT_OFFSET (GcrImporterClass, queued), - NULL, NULL, _gcr_marshal_VOID__STRING_BOXED, - G_TYPE_NONE, 1, G_TYPE_STRING, GCK_TYPE_ATTRIBUTES); - - /** - * GcrImporter::imported: - * @object: The object which was imported. - * - * This signal is emitted when an item has been imported. - */ - signals[IMPORTED] = g_signal_new ("imported", GCR_TYPE_IMPORTER, - G_SIGNAL_RUN_FIRST, G_STRUCT_OFFSET (GcrImporterClass, imported), - NULL, NULL, g_cclosure_marshal_VOID__OBJECT, - G_TYPE_NONE, 1, GCK_TYPE_OBJECT); -} + g_mutex_lock (closure->mutex); -static gpointer -gcr_importer_real_get_user_data (GAsyncResult *base) -{ - g_return_val_if_fail (GCR_IS_IMPORTER (base), NULL); - return GCR_IMPORTER (base)->pv->user_data; -} + closure->complete = TRUE; + closure->error = error; + g_cond_signal (closure->cond); -static GObject* -gcr_importer_real_get_source_object (GAsyncResult *base) -{ - g_return_val_if_fail (GCR_IS_IMPORTER (base), NULL); - return G_OBJECT (base); -} - -static void -gcr_importer_async_result (GAsyncResultIface *iface) -{ - iface->get_source_object = gcr_importer_real_get_source_object; - iface->get_user_data = gcr_importer_real_get_user_data; -} - -/* ----------------------------------------------------------------------------- - * PUBLIC - */ - -/** - * gcr_importer_new: - * - * Create a new #GcrImporter. - * - * Returns: A newly allocated importer, which should be released with - * g_object_unref(). - */ -GcrImporter* -gcr_importer_new (void) -{ - return g_object_new (GCR_TYPE_IMPORTER, NULL); -} - -/** - * gcr_importer_get_slot: - * @self: The importer - * - * Get the PKCS\#11 slot the items will be imported to, or after - * an import operation, which slot they have been imported to. - * - * Returns: The slot. - */ -GckSlot* -gcr_importer_get_slot (GcrImporter *self) -{ - g_return_val_if_fail (GCR_IS_IMPORTER (self), NULL); - return self->pv->slot; -} - -/** - * gcr_importer_set_slot: - * @self: The importer - * @slot: The slot to import to - * - * Set the PKCS\#11 slot to import the items to. - */ -void -gcr_importer_set_slot (GcrImporter *self, GckSlot *slot) -{ - g_return_if_fail (GCR_IS_IMPORTER (self)); - - if (slot) - g_object_ref (slot); - if (self->pv->slot) - g_object_unref (self->pv->slot); - self->pv->slot = slot; - g_object_notify (G_OBJECT (self), "slot"); -} - -/** - * gcr_importer_get_prompt_behavior: - * @self: The importer - * - * Get the type of prompting configured for this importer. - * - * Returns: The prompting flags. - */ -GcrImporterPromptBehavior -gcr_importer_get_prompt_behavior (GcrImporter *self) -{ - g_return_val_if_fail (GCR_IS_IMPORTER (self), GCR_IMPORTER_PROMPT_NEEDED); - return self->pv->behavior; -} - -/** - * gcr_importer_set_prompt_behavior: - * @self: The importer - * @behavior: The prompt behavior flag - * - * Set the type of prompting desired during import. - */ -void -gcr_importer_set_prompt_behavior (GcrImporter *self, GcrImporterPromptBehavior behavior) -{ - g_return_if_fail (GCR_IMPORTER (self)); - self->pv->behavior = behavior; - g_object_notify (G_OBJECT (self), "prompt-behavior"); + g_mutex_unlock (closure->mutex); } /** * gcr_importer_import: - * @self: The importer - * @cancellable: An optional cancellation object - * @error: A location to raise an error on failure + * @importer: the importer + * @cancellable: a #GCancellable, or %NULL + * @error: the location to place an error on failure, or %NULL * - * Start an synchronous import operation of the items that have been queued. + * Import the queued items in the importer. This call will block + * until the operation completes. * - * Returns: Whether the import was successful or not. + * Returns: whether the items were imported successfully or not */ gboolean -gcr_importer_import (GcrImporter *self, GCancellable *cancellable, GError **error) +gcr_importer_import (GcrImporter *importer, + GCancellable *cancellable, + GError **error) { - g_return_val_if_fail (GCR_IS_IMPORTER (self), FALSE); - g_return_val_if_fail (!error || !*error, FALSE); - g_return_val_if_fail (!self->pv->processing, FALSE); - - cleanup_import_data (self); - - if (cancellable) - self->pv->cancel = g_object_ref (cancellable); - self->pv->processing = TRUE; - self->pv->async = FALSE; - - next_state (self, state_import_prompt); - - g_assert (!self->pv->processing); - g_assert (!self->pv->cancel); - - if (!self->pv->succeeded) { - g_propagate_error (error, self->pv->error); - self->pv->error = NULL; - return FALSE; - } - - return TRUE; -} + gboolean result; + ImportClosure *closure; + GcrImporterIface *iface; -/** - * gcr_importer_import_async: - * @self: The importer - * @cancellable: An optional cancellation object - * @callback: Call when the operation result is ready - * @user_data: Data to pass to the callback - * - * Start an asynchronous import operation of the items that have been queued. - */ -void -gcr_importer_import_async (GcrImporter *self, GCancellable *cancellable, - GAsyncReadyCallback callback, gpointer user_data) -{ - g_return_if_fail (GCR_IS_IMPORTER (self)); - g_return_if_fail (!self->pv->processing); + g_return_val_if_fail (GCR_IS_IMPORTER (importer), FALSE); + g_return_val_if_fail (cancellable == NULL || G_IS_CANCELLABLE (cancellable), FALSE); + g_return_val_if_fail (error == NULL || *error == NULL, FALSE); - cleanup_import_data (self); + iface = GCR_IMPORTER_GET_INTERFACE (importer); + if (iface->import_sync) + return (iface->import_sync) (importer, cancellable, error); - if (cancellable) - self->pv->cancel = g_object_ref (cancellable); - self->pv->processing = TRUE; - self->pv->async = TRUE; - self->pv->callback = callback; - self->pv->user_data = user_data; + g_return_val_if_fail (iface->import_async != NULL, FALSE); + g_return_val_if_fail (iface->import_finish != NULL, FALSE); - next_state (self, state_import_prompt); - g_assert (self->pv->processing); -} + closure = g_new0 (ImportClosure, 1); + closure->cond = g_cond_new (); + closure->mutex = g_mutex_new (); + closure->context = g_main_context_get_thread_default (); + g_mutex_lock (closure->mutex); -/** - * gcr_importer_import_finish: - * @self: The importer - * @result: The operation result - * @error: A location to raise an error on failure. - * - * Complete an asynchronous import operation. - * - * Returns: Whether the operation was successful or not. - */ -gboolean -gcr_importer_import_finish (GcrImporter *self, GAsyncResult *result, GError **error) -{ - g_return_val_if_fail (GCR_IS_IMPORTER (self), FALSE); - g_return_val_if_fail (GCR_IMPORTER (result) == self, FALSE); - g_return_val_if_fail (!error || !*error, FALSE); - g_return_val_if_fail (!self->pv->processing, FALSE); + (iface->import_async) (importer, cancellable, on_import_async_complete, closure); + + /* + * Handle the case where we've been called from within the main context + * or in the case where the main context is not running. This approximates + * the behavior of a modal dialog. + */ + if (g_main_context_acquire (closure->context)) { + while (!closure->complete) { + g_mutex_unlock (closure->mutex); + g_main_context_iteration (closure->context, TRUE); + g_mutex_lock (closure->mutex); + } - g_assert (!self->pv->cancel); + g_main_context_release (closure->context); - if (!self->pv->succeeded) { - g_propagate_error (error, self->pv->error); - self->pv->error = NULL; - return FALSE; + /* + * Handle the case where we're in a different thread than the main + * context and a main loop is running. + */ + } else { + while (!closure->complete) + g_cond_wait (closure->cond, closure->mutex); } - - return TRUE; -} -/** - * gcr_importer_listen: - * @self: The importer - * @parser: The parser to listen to - * - * Listen for parse events from the #GcrParser, and queue parsed items for - * importing. - */ -void -gcr_importer_listen (GcrImporter *self, GcrParser *parser) -{ - g_return_if_fail (GCR_IS_IMPORTER (self)); - g_return_if_fail (GCR_IS_PARSER (parser)); + g_mutex_unlock (closure->mutex); + + result = (closure->error == NULL); + if (closure->error) + g_propagate_error (error, closure->error); - /* Listen in to the parser */ - g_signal_connect_object (parser, "parsed", G_CALLBACK (on_parser_parsed), self, 0); - g_signal_connect_object (parser, "authenticate", G_CALLBACK (on_parser_authenticate), self, 0); + g_cond_free (closure->cond); + g_mutex_free (closure->mutex); + g_free (closure); + + return result; } /** - * gcr_importer_queue: - * @self: The importer - * @label: Label of item to import - * @attrs: Attributes of item to import + * gcr_importer_import_async: + * @importer: the importer + * @cancellable: a #GCancellable, or %NULL + * @callback: called when the operation completes + * @user_data: data to be passed to the callback * - * Queue the importing of an item. Use gcr_importer_listen() to automatically - * queue items parsed by a #GcrParser. + * Import the queued items in the importer. This function returns immediately + * and completes asynchronously. */ void -gcr_importer_queue (GcrImporter *self, const gchar *label, GckAttributes *attrs) +gcr_importer_import_async (GcrImporter *importer, + GCancellable *cancellable, + GAsyncReadyCallback callback, + gpointer user_data) { - gboolean is_private; + GcrImporterIface *iface; - g_return_if_fail (GCR_IS_IMPORTER (self)); - g_return_if_fail (attrs); + g_return_if_fail (GCR_IS_IMPORTER (importer)); + g_return_if_fail (cancellable == NULL || G_IS_CANCELLABLE (cancellable)); - if (!gck_attributes_find_boolean (attrs, CKA_PRIVATE, &is_private)) - is_private = FALSE; - if (is_private) - self->pv->any_private = TRUE; + iface = GCR_IMPORTER_GET_INTERFACE (importer); + g_return_if_fail (iface != NULL); + g_return_if_fail (iface->import_async != NULL); - g_queue_push_tail (&self->pv->queue, gck_attributes_ref (attrs)); - g_signal_emit (self, signals[QUEUED], 0, label, attrs); + return (iface->import_async) (importer, cancellable, callback, user_data); } -#ifndef GCR_DISABLE_DEPRECATED - /** - * gcr_importer_get_parser: - * @self: An importer + * gcr_importer_import_finish: + * @importer: the importer + * @result: an asynchronous result + * @error: the location to place an error on failure, or %NULL * - * Has no effect. Use gcr_importer_listen() instead. + * Complete an asynchronous operation to import queued items. * - * Returns: %NULL is always returned. - * Deprecated: Since 3.0.0 + * Returns: whether the import succeeded or failed */ -GcrParser* -gcr_importer_get_parser (GcrImporter *self) +gboolean +gcr_importer_import_finish (GcrImporter *importer, + GAsyncResult *result, + GError **error) { - g_warning ("gcr_importer_get_parser() is no longer supported " - "Use gcr_importer_listen() instead."); - return NULL; + GcrImporterIface *iface; + + g_return_val_if_fail (GCR_IS_IMPORTER (importer), FALSE); + g_return_val_if_fail (G_IS_ASYNC_RESULT (result), FALSE); + g_return_val_if_fail (error == NULL || *error == NULL, FALSE); + + iface = GCR_IMPORTER_GET_INTERFACE (importer); + g_return_val_if_fail (iface != NULL, FALSE); + g_return_val_if_fail (iface->import_finish != NULL, FALSE); + + return (iface->import_finish) (importer, result, error); } /** - * gcr_importer_set_parser: - * @self: An importer - * @parser: A parser - * - * Has no effect. Use gcr_importer_listen() instead. + * gcr_importer_register_well_known: * - * Deprecated: Since 3.0.0 + * Register built-in PKCS#11 and GnuPG importers. */ void -gcr_importer_set_parser (GcrImporter *self, GcrParser *parser) +gcr_importer_register_well_known (void) { - g_warning ("gcr_importer_set_parser() is no longer supported " - "Use gcr_importer_listen() instead."); + g_type_class_unref (g_type_class_ref (GCR_TYPE_PKCS11_IMPORTER)); + g_type_class_unref (g_type_class_ref (GCR_TYPE_GNUPG_IMPORTER)); } - -#endif /* GCR_DISABLE_DEPRECATED */ diff --git a/gcr/gcr-importer.h b/gcr/gcr-importer.h index 892b5d48..1441e327 100644 --- a/gcr/gcr-importer.h +++ b/gcr/gcr-importer.h @@ -1,22 +1,24 @@ -/* +/* * gnome-keyring - * - * Copyright (C) 2008 Stefan Walter - * - * This program is free software; you can redistribute it and/or modify + * + * Copyright (C) 2011 Collabora Ltd. + * + * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU Lesser General Public License as * published by the Free Software Foundation; either version 2.1 of * the License, or (at your option) any later version. - * + * * This program is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. - * + * * You should have received a copy of the GNU Lesser General Public * License along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA - * 02111-1307, USA. + * 02111-1307, USA. + * + * Author: Stef Walter <stefw@collabora.co.uk> */ #if !defined (__GCR_INSIDE_HEADER__) && !defined (GCR_COMPILATION) @@ -27,77 +29,70 @@ #define __GCR_IMPORTER_H__ #include "gcr-parser.h" -#include "gcr-types.h" #include <glib-object.h> G_BEGIN_DECLS -typedef enum { - GCR_IMPORTER_PROMPT_NEEDED, - GCR_IMPORTER_PROMPT_ALWAYS, - GCR_IMPORTER_PROMPT_NEVER -} GcrImporterPromptBehavior; - -#define GCR_TYPE_IMPORTER (gcr_importer_get_type ()) -#define GCR_IMPORTER(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), GCR_TYPE_IMPORTER, GcrImporter)) -#define GCR_IMPORTER_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), GCR_TYPE_IMPORTER, GcrImporterClass)) -#define GCR_IS_IMPORTER(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), GCR_TYPE_IMPORTER)) -#define GCR_IS_IMPORTER_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), GCR_TYPE_IMPORTER)) -#define GCR_IMPORTER_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), GCR_TYPE_IMPORTER, GcrImporterClass)) +#define GCR_TYPE_IMPORTER (gcr_importer_get_type ()) +#define GCR_IMPORTER(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), GCR_TYPE_IMPORTER, GcrImporter)) +#define GCR_IS_IMPORTER(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), GCR_TYPE_IMPORTER)) +#define GCR_IMPORTER_GET_INTERFACE(inst) (G_TYPE_INSTANCE_GET_INTERFACE ((inst), GCR_TYPE_IMPORTER, GcrImporterIface)) typedef struct _GcrImporter GcrImporter; -typedef struct _GcrImporterClass GcrImporterClass; -typedef struct _GcrImporterPrivate GcrImporterPrivate; +typedef struct _GcrImporterIface GcrImporterIface; -struct _GcrImporter { - GObject parent; +struct _GcrImporterIface { + GTypeInterface parent; - /*< private >*/ - GcrImporterPrivate *pv; -}; + GList * (*create_for_parsed) (GcrParser *parser); -struct _GcrImporterClass { - GObjectClass parent_class; + gboolean (*queue_for_parsed) (GcrImporter *importer, + GcrParser *parser); - /* signals */ - void (*queued) (GcrImporter *self, const gchar *label, GckAttributes *attrs); - void (*imported) (GcrImporter *self, GckObject *object); -}; + gboolean (*import_sync) (GcrImporter *importer, + GCancellable *cancellable, + GError **error); -GType gcr_importer_get_type (void); + void (*import_async) (GcrImporter *importer, + GCancellable *cancellable, + GAsyncReadyCallback callback, + gpointer user_data); -GcrImporter* gcr_importer_new (void); + gboolean (*import_finish) (GcrImporter *importer, + GAsyncResult *result, + GError **error); + + gpointer reserved[14]; +}; -GckSlot* gcr_importer_get_slot (GcrImporter *self); +GType gcr_importer_get_type (void); -void gcr_importer_set_slot (GcrImporter *self, - GckSlot *slot); +GList * gcr_importer_create_for_parsed (GcrParser *parser); -GcrImporterPromptBehavior gcr_importer_get_prompt_behavior (GcrImporter *self); +gboolean gcr_importer_queue_for_parsed (GcrImporter *importer, + GcrParser *parser); -void gcr_importer_set_prompt_behavior (GcrImporter *self, - GcrImporterPromptBehavior behavior); +GList * gcr_importer_queue_and_filter_for_parsed (GList *importers, + GcrParser *parser); -void gcr_importer_queue (GcrImporter *self, - const gchar *label, - GckAttributes *attrs); +gboolean gcr_importer_import (GcrImporter *importer, + GCancellable *cancellable, + GError **error); -void gcr_importer_listen (GcrImporter *self, - GcrParser *parser); +void gcr_importer_import_async (GcrImporter *importer, + GCancellable *cancellable, + GAsyncReadyCallback callback, + gpointer user_data); -gboolean gcr_importer_import (GcrImporter *self, - GCancellable *cancellable, - GError **error); +gboolean gcr_importer_import_finish (GcrImporter *importer, + GAsyncResult *result, + GError **error); -void gcr_importer_import_async (GcrImporter *self, - GCancellable *cancellable, - GAsyncReadyCallback callback, - gpointer user_data); +void gcr_importer_register (GType importer_type, + GckAttributes *attrs); -gboolean gcr_importer_import_finish (GcrImporter *self, - GAsyncResult *result, - GError **error); +void gcr_importer_register_well_known (void); G_END_DECLS diff --git a/gcr/gcr-internal.h b/gcr/gcr-internal.h index 9ae38315..fdf37488 100644 --- a/gcr/gcr-internal.h +++ b/gcr/gcr-internal.h @@ -25,6 +25,7 @@ #define GCR_INTERNAL_H_ #include <glib.h> +#include <gio/gio.h> /* Should only be used internally */ #define GCR_SUCCESS 0 diff --git a/gcr/gcr-key-widget.c b/gcr/gcr-key-widget.c index 59eb04ff..be9139ef 100644 --- a/gcr/gcr-key-widget.c +++ b/gcr/gcr-key-widget.c @@ -160,7 +160,6 @@ static void gcr_key_widget_class_init (GcrKeyWidgetClass *klass) { GObjectClass *gobject_class = G_OBJECT_CLASS (klass); - GckAttributes *registered; gcr_key_widget_parent_class = g_type_class_peek_parent (klass); g_type_class_add_private (klass, sizeof (GcrKeyWidgetPrivate)); @@ -173,12 +172,6 @@ gcr_key_widget_class_init (GcrKeyWidgetClass *klass) g_object_class_install_property (gobject_class, PROP_ATTRIBUTES, g_param_spec_boxed ("attributes", "Attributes", "The data displayed in the widget", GCK_TYPE_ATTRIBUTES, G_PARAM_READWRITE)); - - /* Register this as a view which can be loaded */ - registered = gck_attributes_new (); - gck_attributes_add_ulong (registered, CKA_CLASS, CKO_PRIVATE_KEY); - gcr_renderer_register (GCR_TYPE_KEY_WIDGET, registered); - gck_attributes_unref (registered); } /* ----------------------------------------------------------------------------- diff --git a/gcr/gcr-pkcs11-importer.c b/gcr/gcr-pkcs11-importer.c new file mode 100644 index 00000000..714e13a2 --- /dev/null +++ b/gcr/gcr-pkcs11-importer.c @@ -0,0 +1,582 @@ +/* + * gnome-keyring + * + * Copyright (C) 2008 Stefan Walter + * Copyright (C) 2011 Collabora Ltd. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as + * published by the Free Software Foundation; either version 2.1 of + * the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA + * 02111-1307, USA. + * + * Author: Stef Walter <stefw@collabora.co.uk> + */ + +#include "config.h" + +#include "gcr-internal.h" +#include "gcr-library.h" +#include "gcr-parser.h" +#include "gcr-pkcs11-importer.h" + +#include <gck/gck.h> + +#include <glib/gi18n-lib.h> + +enum { + PROP_0, + PROP_LABEL, + PROP_ICON, + PROP_SLOT, + PROP_IMPORTED +}; + +struct _GcrPkcs11ImporterPrivate { + GckSlot *slot; + GList *objects; + GckSession *session; + GQueue queue; + gboolean any_private; +}; + +typedef struct { + GcrPkcs11Importer *importer; + GCancellable *cancellable; + gboolean prompted; + gboolean async; +} GcrImporterData; + +/* State forward declarations */ +static void state_cancelled (GSimpleAsyncResult *res, + gboolean async); + +static void state_complete (GSimpleAsyncResult *res, + gboolean async); + +static void state_create_object (GSimpleAsyncResult *res, + gboolean async); + +static void state_open_session (GSimpleAsyncResult *res, + gboolean async); + +static void _gcr_pkcs11_importer_init_iface (GcrImporterIface *iface); + +G_DEFINE_TYPE_WITH_CODE (GcrPkcs11Importer, _gcr_pkcs11_importer, G_TYPE_OBJECT, + G_IMPLEMENT_INTERFACE (GCR_TYPE_IMPORTER, _gcr_pkcs11_importer_init_iface); +); + +#define BLOCK 4096 + +static void +gcr_importer_data_free (gpointer data) +{ + GcrImporterData *state = data; + + g_clear_object (&state->cancellable); + g_clear_object (&state->importer); + g_free (state); +} + +static void +next_state (GSimpleAsyncResult *res, + void (*state) (GSimpleAsyncResult *, gboolean)) +{ + GcrImporterData *data = g_simple_async_result_get_op_res_gpointer (res); + + g_assert (state); + + if (g_cancellable_is_cancelled (data->cancellable)) + state = state_cancelled; + + (state) (res, data->async); +} + +/* --------------------------------------------------------------------------------- + * COMPLETE + */ + +static void +state_complete (GSimpleAsyncResult *res, + gboolean async) +{ + g_simple_async_result_complete (res); +} + +static void +state_cancelled (GSimpleAsyncResult *res, + gboolean async) +{ + GcrImporterData *data = g_simple_async_result_get_op_res_gpointer (res); + GError *error = NULL; + + if (data->cancellable && !g_cancellable_is_cancelled (data->cancellable)) + g_cancellable_cancel (data->cancellable); + + g_cancellable_set_error_if_cancelled (data->cancellable, &error); + g_simple_async_result_take_error (res, error); + next_state (res, state_complete); +} + +/* --------------------------------------------------------------------------------- + * CREATE OBJECTS + */ + +static void +complete_create_object (GSimpleAsyncResult *res, + GckObject *object, + GError *error) +{ + GcrImporterData *data = g_simple_async_result_get_op_res_gpointer (res); + GcrPkcs11Importer *self = data->importer; + + if (object == NULL) { + g_simple_async_result_take_error (res, error); + next_state (res, state_complete); + + } else { + self->pv->objects = g_list_append (self->pv->objects, object); + next_state (res, state_create_object); + } +} + +static void +on_create_object (GObject *source, + GAsyncResult *result, + gpointer user_data) +{ + GSimpleAsyncResult *res = G_SIMPLE_ASYNC_RESULT (user_data); + GError *error = NULL; + GckObject *object; + + object = gck_session_create_object_finish (GCK_SESSION (source), result, &error); + complete_create_object (res, object, error); + g_object_unref (res); +} + +static void +state_create_object (GSimpleAsyncResult *res, + gboolean async) +{ + GcrImporterData *data = g_simple_async_result_get_op_res_gpointer (res); + GcrPkcs11Importer *self = data->importer; + GckAttributes *attrs; + GckObject *object; + GError *error = NULL; + + /* No more objects */ + if (g_queue_is_empty (&self->pv->queue)) { + next_state (res, state_complete); + + } else { + + /* Pop first one off the list */ + attrs = g_queue_pop_head (&self->pv->queue); + g_assert (attrs != NULL); + + gck_attributes_add_boolean (attrs, CKA_TOKEN, CK_TRUE); + + if (async) { + gck_session_create_object_async (self->pv->session, attrs, + data->cancellable, on_create_object, + g_object_ref (res)); + } else { + object = gck_session_create_object (self->pv->session, attrs, + data->cancellable, &error); + complete_create_object (res, object, error); + } + + gck_attributes_unref (attrs); + } +} + +/* --------------------------------------------------------------------------------- + * OPEN SESSION + */ + +static void +complete_open_session (GSimpleAsyncResult *res, + GckSession *session, + GError *error) +{ + GcrImporterData *data = g_simple_async_result_get_op_res_gpointer (res); + GcrPkcs11Importer *self = data->importer; + + if (!session) { + g_simple_async_result_take_error (res, error); + next_state (res, state_complete); + + } else { + g_clear_object (&self->pv->session); + self->pv->session = session; + next_state (res, state_create_object); + } +} + +static void +on_open_session (GObject *source, + GAsyncResult *result, + gpointer user_data) +{ + GSimpleAsyncResult *res = G_SIMPLE_ASYNC_RESULT (user_data); + GError *error = NULL; + GckSession *session; + + session = gck_slot_open_session_finish (GCK_SLOT (source), result, &error); + complete_open_session (res, session, error); + g_object_unref (res); +} + +static void +state_open_session (GSimpleAsyncResult *res, + gboolean async) +{ + GcrImporterData *data = g_simple_async_result_get_op_res_gpointer (res); + GcrPkcs11Importer *self = data->importer; + guint options = GCK_SESSION_READ_WRITE; + GckSession *session; + GError *error = NULL; + + if (self->pv->any_private) + options |= GCK_SESSION_LOGIN_USER; + + if (async) { + gck_slot_open_session_async (self->pv->slot, options, + data->cancellable, on_open_session, + g_object_ref (res)); + } else { + session = gck_slot_open_session_full (self->pv->slot, options, 0, + NULL, NULL, data->cancellable, &error); + complete_open_session (res, session, error); + } +} + +static void +_gcr_pkcs11_importer_init (GcrPkcs11Importer *self) +{ + self->pv = G_TYPE_INSTANCE_GET_PRIVATE (self, GCR_TYPE_PKCS11_IMPORTER, GcrPkcs11ImporterPrivate); + g_queue_init (&self->pv->queue); +} + +static void +_gcr_pkcs11_importer_dispose (GObject *obj) +{ + GcrPkcs11Importer *self = GCR_PKCS11_IMPORTER (obj); + + gck_list_unref_free (self->pv->objects); + self->pv->objects = NULL; + g_clear_object (&self->pv->session); + + while (!g_queue_is_empty (&self->pv->queue)) + gck_attributes_unref (g_queue_pop_head (&self->pv->queue)); + + G_OBJECT_CLASS (_gcr_pkcs11_importer_parent_class)->dispose (obj); +} + +static void +_gcr_pkcs11_importer_finalize (GObject *obj) +{ + GcrPkcs11Importer *self = GCR_PKCS11_IMPORTER (obj); + + g_clear_object (&self->pv->slot); + + G_OBJECT_CLASS (_gcr_pkcs11_importer_parent_class)->finalize (obj); +} + +static void +_gcr_pkcs11_importer_set_property (GObject *obj, + guint prop_id, + const GValue *value, + GParamSpec *pspec) +{ + GcrPkcs11Importer *self = GCR_PKCS11_IMPORTER (obj); + + switch (prop_id) { + case PROP_SLOT: + self->pv->slot = g_value_dup_object (value); + g_return_if_fail (self->pv->slot); + break; + default: + G_OBJECT_WARN_INVALID_PROPERTY_ID (obj, prop_id, pspec); + break; + } +} + +static gchar * +calculate_label (GcrPkcs11Importer *self) +{ + GckTokenInfo *info; + gchar *result; + + info = gck_slot_get_token_info (self->pv->slot); + result = g_strdup (info->label); + gck_token_info_free (info); + + return result; +} + +static GIcon * +calculate_icon (GcrPkcs11Importer *self) +{ + GckTokenInfo *info; + GIcon *result; + + info = gck_slot_get_token_info (self->pv->slot); + if (g_strcmp0 (info->manufacturer_id, "Gnome Keyring") == 0) + result = g_themed_icon_new ("home-folder"); + else + result = g_themed_icon_new ("media-flash"); + gck_token_info_free (info); + + return result; +} + +static void +_gcr_pkcs11_importer_get_property (GObject *obj, + guint prop_id, + GValue *value, + GParamSpec *pspec) +{ + GcrPkcs11Importer *self = GCR_PKCS11_IMPORTER (obj); + + switch (prop_id) { + case PROP_LABEL: + g_value_take_string (value, calculate_label (self)); + break; + case PROP_ICON: + g_value_take_object (value, calculate_icon (self)); + break; + case PROP_SLOT: + g_value_set_object (value, _gcr_pkcs11_importer_get_slot (self)); + break; + case PROP_IMPORTED: + g_value_set_boxed (value, _gcr_pkcs11_importer_get_imported (self)); + break; + default: + G_OBJECT_WARN_INVALID_PROPERTY_ID (obj, prop_id, pspec); + break; + } +} + +static void +_gcr_pkcs11_importer_class_init (GcrPkcs11ImporterClass *klass) +{ + GObjectClass *gobject_class = G_OBJECT_CLASS (klass); + GckAttributes *registered; + + gobject_class->dispose = _gcr_pkcs11_importer_dispose; + gobject_class->finalize = _gcr_pkcs11_importer_finalize; + gobject_class->set_property = _gcr_pkcs11_importer_set_property; + gobject_class->get_property = _gcr_pkcs11_importer_get_property; + + g_type_class_add_private (gobject_class, sizeof (GcrPkcs11ImporterPrivate)); + + g_object_class_override_property (gobject_class, PROP_LABEL, "label"); + + g_object_class_override_property (gobject_class, PROP_ICON, "icon"); + + g_object_class_install_property (gobject_class, PROP_SLOT, + g_param_spec_object ("slot", "Slot", "PKCS#11 slot to import data into", + GCK_TYPE_SLOT, G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY)); + + g_object_class_install_property (gobject_class, PROP_IMPORTED, + g_param_spec_boxed ("imported", "Imported", "Imported objects", + GCK_TYPE_LIST, G_PARAM_READABLE)); + + registered = gck_attributes_new (); + gck_attributes_add_ulong (registered, CKA_CLASS, CKO_CERTIFICATE); + gck_attributes_add_ulong (registered, CKA_CERTIFICATE_TYPE, CKC_X_509); + gcr_importer_register (GCR_TYPE_PKCS11_IMPORTER, registered); + gck_attributes_unref (registered); + + registered = gck_attributes_new (); + gck_attributes_add_ulong (registered, CKA_CLASS, CKO_PRIVATE_KEY); + gcr_importer_register (GCR_TYPE_PKCS11_IMPORTER, registered); + gck_attributes_unref (registered); + + _gcr_initialize_library (); +} + +static GList * +list_all_slots (void) +{ + GError *error = NULL; + GList *modules, *l; + GList *results = NULL; + + if (!_gcr_initialize_pkcs11 (NULL, &error)) { + g_warning ("couldn't initialize PKCS#11 modules: %s", error->message); + g_clear_error (&error); + return NULL; + } + + modules = gcr_pkcs11_get_modules (); + for (l = modules; l != NULL; l = g_list_next (l)) + results = g_list_concat (results, gck_modules_get_slots (modules, TRUE)); + gck_list_unref_free (modules); + + return results; +} + +static const char *token_blacklist[] = { + "pkcs11:manufacturer=Gnome%20Keyring;serial=1:SECRET:MAIN", + NULL +}; + +static gboolean +is_slot_importable (GckSlot *slot, + GckTokenInfo *token) +{ + GError *error = NULL; + GckUriData *uri; + gboolean match; + guint i; + + if (token->flags & CKF_WRITE_PROTECTED) + return FALSE; + if (!(token->flags & CKF_TOKEN_INITIALIZED)) + return FALSE; + if ((token->flags & CKF_LOGIN_REQUIRED) && + !(token->flags & CKF_USER_PIN_INITIALIZED)) + return FALSE; + + for (i = 0; token_blacklist[i] != NULL; i++) { + uri = gck_uri_parse (token_blacklist[i], GCK_URI_FOR_TOKEN | GCK_URI_FOR_MODULE, &error); + if (uri == NULL) { + g_warning ("couldn't parse pkcs11 blacklist uri: %s", error->message); + g_clear_error (&error); + continue; + } + + match = gck_slot_match (slot, uri); + gck_uri_data_free (uri); + + if (match) + return FALSE; + } + + return TRUE; +} + +static GList * +_gcr_pkcs11_importer_create_for_parsed (GcrParser *parser) +{ + GcrImporter *self; + GList *slots, *l; + GList *results = NULL; + GckTokenInfo *token_info; + gboolean importable; + + slots = list_all_slots (); + for (l = slots; l != NULL; l = g_list_next (l)) { + token_info = gck_slot_get_token_info (l->data); + importable = is_slot_importable (l->data, token_info); + gck_token_info_free (token_info); + + if (importable) { + self = _gcr_pkcs11_importer_new (l->data); + if (!gcr_importer_queue_for_parsed (self, parser)) + g_assert_not_reached (); + results = g_list_prepend (results, self); + } + } + gck_list_unref_free (slots); + + return g_list_reverse (results); +} + +static gboolean +_gcr_pkcs11_importer_queue_for_parsed (GcrImporter *importer, + GcrParser *parser) +{ + GcrPkcs11Importer *self = GCR_PKCS11_IMPORTER (importer); + GckAttributes *attrs; + gboolean is_private; + + attrs = gcr_parser_get_parsed_attributes (parser); + + if (!gck_attributes_find_boolean (attrs, CKA_PRIVATE, &is_private)) + is_private = FALSE; + if (is_private) + self->pv->any_private = TRUE; + + g_queue_push_tail (&self->pv->queue, gck_attributes_ref (attrs)); + return TRUE; +} + +static void +_gcr_pkcs11_importer_import_async (GcrImporter *importer, + GCancellable *cancellable, + GAsyncReadyCallback callback, + gpointer user_data) +{ + GSimpleAsyncResult *res; + GcrImporterData *data; + + res = g_simple_async_result_new (G_OBJECT (importer), callback, user_data, + _gcr_pkcs11_importer_import_async); + data = g_new0 (GcrImporterData, 1); + data->async = TRUE; + data->importer = g_object_ref (importer); + data->cancellable = cancellable ? g_object_ref (cancellable) : NULL; + g_simple_async_result_set_op_res_gpointer (res, data, gcr_importer_data_free); + + next_state (res, state_open_session); + g_object_unref (res); +} + +static gboolean +_gcr_pkcs11_importer_import_finish (GcrImporter *importer, + GAsyncResult *result, + GError **error) +{ + g_return_val_if_fail (g_simple_async_result_is_valid (result, G_OBJECT (importer), + _gcr_pkcs11_importer_import_async), FALSE); + + if (g_simple_async_result_propagate_error (G_SIMPLE_ASYNC_RESULT (result), error)) + return FALSE; + + return TRUE; +} + +static void +_gcr_pkcs11_importer_init_iface (GcrImporterIface *iface) +{ + iface->create_for_parsed = _gcr_pkcs11_importer_create_for_parsed; + iface->queue_for_parsed = _gcr_pkcs11_importer_queue_for_parsed; + iface->import_async = _gcr_pkcs11_importer_import_async; + iface->import_finish = _gcr_pkcs11_importer_import_finish; +} + +GcrImporter * +_gcr_pkcs11_importer_new (GckSlot *slot) +{ + g_return_val_if_fail (GCK_IS_SLOT (slot), NULL); + + return g_object_new (GCR_TYPE_PKCS11_IMPORTER, + "slot", slot, + NULL); +} + +GckSlot * +_gcr_pkcs11_importer_get_slot (GcrPkcs11Importer *self) +{ + g_return_val_if_fail (GCR_IS_PKCS11_IMPORTER (self), NULL); + return self->pv->slot; +} + +GList * +_gcr_pkcs11_importer_get_imported (GcrPkcs11Importer *self) +{ + g_return_val_if_fail (GCR_IS_PKCS11_IMPORTER (self), NULL); + return self->pv->objects; +} diff --git a/gcr/gcr-pkcs11-importer.h b/gcr/gcr-pkcs11-importer.h new file mode 100644 index 00000000..bbf752ca --- /dev/null +++ b/gcr/gcr-pkcs11-importer.h @@ -0,0 +1,68 @@ +/* + * gnome-keyring + * + * Copyright (C) 2008 Stefan Walter + * Copyright (C) 2011 Collabora Ltd. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as + * published by the Free Software Foundation; either version 2.1 of + * the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA + * 02111-1307, USA. + * + * Author: Stef Walter <stefw@collabora.co.uk> + */ + +#if !defined (__GCR_H_INSIDE__) && !defined (GCR_COMPILATION) +#error "Only <gcr/gcr.h> can be included directly." +#endif + +#ifndef __GCR_PKCS11_IMPORTER_H__ +#define __GCR_PKCS11_IMPORTER_H__ + +#include "gcr-importer.h" + +G_BEGIN_DECLS + +#define GCR_TYPE_PKCS11_IMPORTER (_gcr_pkcs11_importer_get_type ()) +#define GCR_PKCS11_IMPORTER(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), GCR_TYPE_PKCS11_IMPORTER, GcrPkcs11Importer)) +#define GCR_PKCS11_IMPORTER_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), GCR_TYPE_PKCS11_IMPORTER, GcrPkcs11ImporterClass)) +#define GCR_IS_PKCS11_IMPORTER(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), GCR_TYPE_PKCS11_IMPORTER)) +#define GCR_IS_PKCS11_IMPORTER_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), GCR_TYPE_PKCS11_IMPORTER)) +#define GCR_PKCS11_IMPORTER_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), GCR_TYPE_PKCS11_IMPORTER, GcrPkcs11ImporterClass)) + +typedef struct _GcrPkcs11Importer GcrPkcs11Importer; +typedef struct _GcrPkcs11ImporterClass GcrPkcs11ImporterClass; +typedef struct _GcrPkcs11ImporterPrivate GcrPkcs11ImporterPrivate; + +struct _GcrPkcs11Importer { + GObject parent; + + /*< private >*/ + GcrPkcs11ImporterPrivate *pv; +}; + +struct _GcrPkcs11ImporterClass { + GObjectClass parent_class; +}; + +GType _gcr_pkcs11_importer_get_type (void); + +GcrImporter * _gcr_pkcs11_importer_new (GckSlot *slot); + +GckSlot * _gcr_pkcs11_importer_get_slot (GcrPkcs11Importer *self); + +GList * _gcr_pkcs11_importer_get_imported (GcrPkcs11Importer *self); + +G_END_DECLS + +#endif /* __GCR_PKCS11_IMPORTER_H__ */ diff --git a/gcr/gcr-record.h b/gcr/gcr-record.h index 39153751..37201cef 100644 --- a/gcr/gcr-record.h +++ b/gcr/gcr-record.h @@ -52,6 +52,7 @@ G_BEGIN_DECLS #define GCR_RECORD_SCHEMA_ATTRIBUTE (g_quark_from_static_string ("ATTRIBUTE")) +#define GCR_RECORD_SCHEMA_IMPORT_OK (g_quark_from_static_string ("IMPORT_OK")) #define GCR_RECORD_SCHEMA_FPR (g_quark_from_static_string ("fpr")) #define GCR_RECORD_SCHEMA_PUB (g_quark_from_static_string ("pub")) #define GCR_RECORD_SCHEMA_SUB (g_quark_from_static_string ("sub")) @@ -83,6 +84,16 @@ typedef enum { } GcrRecordAttributeColumns; /* + * Columns for IMPORT_OK and IMPORT_PROBLEM status message. eg: + * [GNUPG:] IMPORT_OK 1 6BD9050FD8FC941B43412DCC68B7AB8957548DCD + * [GNUPG:] IMPORT_PROBLEM 1 + */ +typedef enum { + GCR_RECORD_IMPORT_REASON = 1, + GCR_RECORD_IMPORT_FINGERPRINT +} GcrRecordImportColumns; + +/* * Columns for fpr schema, add them as they're used. eg: * fpr:::::::::ECAF7590EB3443B5C7CF3ACB6C7EE1B8621CC013: */ diff --git a/gcr/gcr.symbols b/gcr/gcr.symbols index 5781424b..d7703def 100644 --- a/gcr/gcr.symbols +++ b/gcr/gcr.symbols @@ -42,20 +42,6 @@ gcr_combo_selector_get_selected gcr_combo_selector_get_type gcr_combo_selector_new gcr_combo_selector_set_selected -gcr_importer_get_parser -gcr_importer_get_prompt_behavior -gcr_importer_get_slot -gcr_importer_get_type -gcr_importer_import -gcr_importer_import_async -gcr_importer_import_finish -gcr_importer_listen -gcr_importer_new -gcr_importer_prompt_behavior_get_type -gcr_importer_queue -gcr_importer_set_parser -gcr_importer_set_prompt_behavior -gcr_importer_set_slot gcr_key_renderer_get_attributes gcr_key_renderer_get_type gcr_key_renderer_new diff --git a/po/POTFILES.in b/po/POTFILES.in index d10485d6..6579f434 100644 --- a/po/POTFILES.in +++ b/po/POTFILES.in @@ -23,6 +23,7 @@ gcr/gcr-certificate-widget.c gcr/gcr-display-view.c gcr/gcr-failure-renderer.c [type: gettext/glade]gcr/gcr-import-dialog.ui +gcr/gcr-gnupg-importer.c gcr/gcr-gnupg-key.c gcr/gcr-gnupg-process.c gcr/gcr-gnupg-renderer.c diff --git a/tool/gkr-tool-import.c b/tool/gkr-tool-import.c index fdb8e018..05f26ddc 100644 --- a/tool/gkr-tool-import.c +++ b/tool/gkr-tool-import.c @@ -39,7 +39,16 @@ static GOptionEntry import_entries[] = { }; static void -on_imported (GcrImporter *importer, GckObject *object) +imported_fingerprint (const gchar *fingerprint, + const gchar *destination) +{ + g_print ("%s: imported openpgp\n", destination); + g_print ("\tfingerprint: %s\n", fingerprint); +} + +static void +imported_object (GckObject *object, + const gchar *destination) { gulong attr_types[3]; GckAttributes *attrs; @@ -67,30 +76,30 @@ on_imported (GcrImporter *importer, GckObject *object) switch (klass) { case CKO_CERTIFICATE: - message = "Imported certificate: %s\n"; + message = "%s: imported certificate: %s\n"; break; case CKO_DATA: - message = "Imported data: %s\n"; + message = "%s: imported data: %s\n"; break; case CKO_PRIVATE_KEY: - message = "Imported private key: %s\n"; + message = "%s: imported private key: %s\n"; break; case CKO_PUBLIC_KEY: - message = "Imported public key: %s\n"; + message = "%s: imported public key: %s\n"; break; case CKO_SECRET_KEY: - message = "Imported secret key: %s\n"; + message = "%s: imported secret key: %s\n"; break; default: - message = "Imported object: %s\n"; + message = "%s: imported object: %s\n"; break; }; - g_print (message, label); + g_print (message, destination, label); if (id) { hex = egg_hex_encode (id->value, id->length); - g_print ("\tID: %s\n", hex); + g_print ("\tidentifier: %s\n", hex); g_free (hex); } @@ -98,18 +107,70 @@ on_imported (GcrImporter *importer, GckObject *object) g_free (label); } +static void +imported_display (GcrImporter *importer) +{ + GParamSpec *spec; + gchar *label = NULL; + + spec = g_object_class_find_property (G_OBJECT_GET_CLASS (importer), "imported"); + if (spec == NULL) + return; + + g_object_get (importer, "label", &label, NULL); + + if (spec->value_type == GCK_TYPE_LIST) { + GList *list, *l; + g_object_get (importer, "imported", &list, NULL); + for (l = list; l != NULL; l = g_list_next (l)) + imported_object (l->data, label); + gck_list_unref_free (list); + + } else if (spec->value_type == G_TYPE_STRV) { + gchar **fingerprints; + guint i; + g_object_get (importer, "imported", &fingerprints, NULL); + for (i = 0; fingerprints && fingerprints[i] != NULL; i++) + imported_fingerprint (fingerprints[i], label); + g_strfreev (fingerprints); + } +} + +typedef struct { + GList *importers; + gboolean num_parsed; +} ImportClosure; + +static void +on_parser_parsed (GcrParser *parser, + gpointer user_data) +{ + ImportClosure *closure = user_data; + GList *filtered; + + if (closure->num_parsed == 0) { + closure->importers = gcr_importer_create_for_parsed (parser); + } else { + filtered = gcr_importer_queue_and_filter_for_parsed (closure->importers, parser); + gck_list_unref_free (closure->importers); + closure->importers = filtered; + } + + closure->num_parsed++; +} + int gkr_tool_import (int argc, char *argv[]) { - GcrImporter *importer; GcrParser *parser; GError *error = NULL; GInputStream *input; - gboolean res; + ImportClosure *closure; GFile *file; gchar **imp; int ret = 0; - + GList *l; + ret = gkr_tool_parse_options (&argc, &argv, import_entries); if (ret != 0) return ret; @@ -118,39 +179,50 @@ gkr_tool_import (int argc, char *argv[]) gkr_tool_handle_error (NULL, "specify files to import"); return 2; } - - importer = gcr_importer_new (); - gcr_importer_set_prompt_behavior (importer, GCR_IMPORTER_PROMPT_NEEDED); - - if (!gkr_tool_mode_quiet) - g_signal_connect (importer, "imported", G_CALLBACK (on_imported), NULL); - + + parser = gcr_parser_new (); + closure = g_new0 (ImportClosure, 1); + g_signal_connect (parser, "parsed", G_CALLBACK (on_parser_parsed), closure); + for (imp = import_files; *imp; ++imp) { file = g_file_new_for_commandline_arg (*imp); input = G_INPUT_STREAM (g_file_read (file, NULL, &error)); g_object_unref (file); - if (!input) { + if (input == NULL) { gkr_tool_handle_error (&error, "couldn't read file: %s", *imp); ret = 1; + } else { - parser = gcr_parser_new (); - gcr_importer_listen (importer, parser); - res = gcr_parser_parse_stream (parser, input, NULL, &error); + if (!gcr_parser_parse_stream (parser, input, NULL, &error)) { + if (error->code != GCR_ERROR_CANCELLED) + gkr_tool_handle_error (&error, "couldn't parse: %s", *imp); + ret = 1; + } + g_object_unref (input); - g_object_unref (parser); + } + } - if (res == TRUE) - res = gcr_importer_import (importer, NULL, &error); + if (closure->importers == NULL) { + gkr_tool_handle_error (NULL, "couldn't find any place to import files"); + ret = 1; + } - if (res == FALSE) { - if (!error || error->code != GCR_ERROR_CANCELLED) - gkr_tool_handle_error (&error, "couldn't import file: %s", *imp); - ret = 1; - } + for (l = closure->importers; l != NULL; l = g_list_next (l)) { + if (gcr_importer_import (l->data, NULL, &error)) { + if (!gkr_tool_mode_quiet) + imported_display (l->data); + } else { + if (error->code != GCR_ERROR_CANCELLED) + gkr_tool_handle_error (&error, "couldn't import"); + ret = 1; } } - - g_object_unref (importer); + + gck_list_unref_free (closure->importers); + g_free (closure); + + g_object_unref (parser); return ret; } |