diff options
Diffstat (limited to 'gcr/gcr-importer.c')
-rw-r--r-- | gcr/gcr-importer.c | 1144 |
1 files changed, 311 insertions, 833 deletions
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 */ |