/* gck-object-cache.c - the GObject PKCS#11 wrapper library
Copyright (C) 2011 Collabora Ltd.
The Gnome Keyring Library is free software; you can redistribute it and/or
modify it under the terms of the GNU Library General Public License as
published by the Free Software Foundation; either version 2 of the
License, or (at your option) any later version.
The Gnome Keyring Library is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
Library General Public License for more details.
You should have received a copy of the GNU Library General Public
License along with the Gnome Library; see the file COPYING.LIB. If not,
see .
Author: Stef Walter
*/
#include "config.h"
#include "gck.h"
#include "gck-private.h"
#include
/**
* GckObjectCache:
*
* An interface implemented by derived classes of [class@Object] to indicate
* which attributes they'd like an enumerator to retrieve.
*
* These attributes are then cached on the object and can be retrieved through
* the [property@ObjectCache:attributes] property.
*/
/**
* GckObjectCacheInterface:
* @interface: parent interface
* @default_types: (array length=n_default_types): attribute types that an
* enumerator should retrieve
* @n_default_types: number of attribute types to be retrieved
* @fill: virtual method to add attributes to the cache
*
* Interface for [iface@ObjectCache]. If the @default_types field is
* implemented by a implementing class, then that will be used by a
* [class@Enumerator] which has been setup using
* [method@Enumerator.set_object_type]
*
* The implementation for @populate should add the attributes to the
* cache. It must be thread safe.
*/
G_DEFINE_INTERFACE (GckObjectCache, gck_object_cache, GCK_TYPE_OBJECT);
static void
gck_object_cache_default_init (GckObjectCacheInterface *iface)
{
/**
* GckObjectCache:attributes:
*
* The attributes cached on this object.
*/
g_object_interface_install_property (iface,
g_param_spec_boxed ("attributes", "Attributes", "PKCS#11 Attributes",
GCK_TYPE_ATTRIBUTES, G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
}
/**
* gck_object_cache_get_attributes: (skip):
* @object: an object with an attribute cache
*
* Gets the attributes cached on this object.
*
* Returns: (transfer full) (nullable): the attributes
*/
GckAttributes *
gck_object_cache_get_attributes (GckObjectCache *object)
{
GckAttributes *attributes = NULL;
g_return_val_if_fail (GCK_IS_OBJECT_CACHE (object), NULL);
g_object_get (object, "attributes", &attributes, NULL);
return attributes;
}
/**
* gck_object_cache_set_attributes:
* @object: an object with an attribute cache
* @attrs: (nullable): the attributes to set
*
* Sets the attributes cached on this object.
*/
void
gck_object_cache_set_attributes (GckObjectCache *object,
GckAttributes *attrs)
{
g_return_if_fail (GCK_IS_OBJECT_CACHE (object));
g_object_set (object, "attributes", attrs, NULL);
}
/**
* gck_object_cache_fill:
* @object: an object with the cache
* @attrs: the attributes to cache
*
* Adds the attributes to the set cached on this object. If an attribute is
* already present in the cache it will be overridden by this value.
*
* This will be done in a thread-safe manner.
*/
void
gck_object_cache_fill (GckObjectCache *object,
GckAttributes *attrs)
{
GckObjectCacheInterface *iface;
g_return_if_fail (GCK_IS_OBJECT_CACHE (object));
g_return_if_fail (attrs != NULL);
iface = GCK_OBJECT_CACHE_GET_IFACE (object);
g_return_if_fail (iface->fill != NULL);
(iface->fill) (object, attrs);
}
/**
* gck_object_cache_update:
* @object: the object with the cache
* @attr_types: (array length=n_attr_types): the types of attributes to update
* @n_attr_types: the number of attribute types
* @cancellable: optional cancellation object
* @error: location to place an error
*
* Update the object cache with given attributes. If an attribute already
* exists in the cache, it will be updated, and if it doesn't it will be added.
*
* This may block, use the asynchronous version when this is not desirable
*
* Returns: whether the cache update was successful
*/
gboolean
gck_object_cache_update (GckObjectCache *object,
const gulong *attr_types,
gint n_attr_types,
GCancellable *cancellable,
GError **error)
{
GckObjectCacheInterface *iface;
GckAttributes *attrs;
g_return_val_if_fail (GCK_IS_OBJECT_CACHE (object), FALSE);
g_return_val_if_fail (attr_types != NULL || n_attr_types == 0, FALSE);
g_return_val_if_fail (cancellable == NULL || G_IS_CANCELLABLE (cancellable), FALSE);
g_return_val_if_fail (error == NULL || *error == NULL, FALSE);
iface = GCK_OBJECT_CACHE_GET_IFACE (object);
if (attr_types == NULL) {
attr_types = iface->default_types;
n_attr_types = iface->n_default_types;
if (attr_types == NULL || n_attr_types == 0) {
g_warning ("no attribute types passed to gck_object_cache_update() "
"and no default types on object.");
return FALSE;
}
}
attrs = gck_object_get_full (GCK_OBJECT (object),
attr_types, n_attr_types,
cancellable, error);
if (attrs != NULL) {
gck_object_cache_fill (object, attrs);
gck_attributes_unref (attrs);
}
return attrs != NULL;
}
static void
on_cache_object_get (GObject *source,
GAsyncResult *result,
gpointer user_data)
{
GTask *task = G_TASK (user_data);
GckAttributes *attrs;
GError *error = NULL;
attrs = gck_object_get_finish (GCK_OBJECT (source), result, &error);
if (error == NULL) {
gck_object_cache_fill (GCK_OBJECT_CACHE (source), attrs);
gck_attributes_unref (attrs);
g_task_return_boolean (task, TRUE);
} else {
g_task_return_error (task, g_steal_pointer (&error));
}
g_clear_object (&task);
}
/**
* gck_object_cache_update_async:
* @object: the object with the cache
* @attr_types: (array length=n_attr_types): the types of attributes to update
* @n_attr_types: the number of attribute types
* @cancellable: optional cancellation object
* @callback: called when the operation completes
* @user_data: data to be passed to the callback
*
* Update the object cache with given attributes. If an attribute already
* exists in the cache, it will be updated, and if it doesn't it will be added.
*
* This call will return immediately and complete asynchronously.
*/
void
gck_object_cache_update_async (GckObjectCache *object,
const gulong *attr_types,
gint n_attr_types,
GCancellable *cancellable,
GAsyncReadyCallback callback,
gpointer user_data)
{
GckObjectCacheInterface *iface;
GTask *task;
g_return_if_fail (GCK_IS_OBJECT_CACHE (object));
g_return_if_fail (attr_types != NULL || n_attr_types == 0);
g_return_if_fail (cancellable == NULL || G_IS_CANCELLABLE (cancellable));
iface = GCK_OBJECT_CACHE_GET_IFACE (object);
if (attr_types == NULL) {
attr_types = iface->default_types;
n_attr_types = iface->n_default_types;
if (attr_types == NULL || n_attr_types == 0) {
g_warning ("no attribute types passed to gck_object_cache_update_async() "
"and no default types on object.");
return;
}
}
task = g_task_new (object, cancellable, callback, user_data);
g_task_set_source_tag (task, gck_object_cache_update_async);
gck_object_get_async (GCK_OBJECT (object), attr_types, n_attr_types,
cancellable, on_cache_object_get, g_steal_pointer (&task));
g_clear_object (&task);
}
/**
* gck_object_cache_update_finish:
* @object: the object with the cache
* @result: the asynchronous result passed to the callback
* @error: location to place an error
*
* Complete an asynchronous operation to update the object cache with given
* attributes.
*
* Returns: whether the cache update was successful
*/
gboolean
gck_object_cache_update_finish (GckObjectCache *object,
GAsyncResult *result,
GError **error)
{
g_return_val_if_fail (GCK_IS_OBJECT_CACHE (object), FALSE);
g_return_val_if_fail (g_task_is_valid (result, object), FALSE);
g_return_val_if_fail (error == NULL || *error == NULL, FALSE);
return g_task_propagate_boolean (G_TASK (result), error);
}
static gboolean
check_have_attributes (GckAttributes *attrs,
const gulong *attr_types,
gint n_attr_types)
{
gint i;
if (attrs == NULL)
return FALSE;
for (i = 0; i < n_attr_types; i++) {
if (!gck_attributes_find (attrs, attr_types[i]))
return FALSE;
}
return TRUE;
}
/**
* gck_object_cache_lookup:
* @object: the object
* @attr_types: (array length=n_attr_types): the types of attributes to update
* @n_attr_types: the number of attribute types
* @cancellable: optional cancellation object
* @error: location to place an error
*
* Lookup attributes in the cache, or retrieve them from the object if necessary.
*
* If @object is a #GckObjectCache then this will lookup the attributes there
* first if available, otherwise will read them from the object and update
* the cache.
*
* If @object is not a #GckObjectCache, then the attributes will simply be
* read from the object.
*
* This may block, use the asynchronous version when this is not desirable
*
* Returns: (transfer full): the attributes retrieved or %NULL on failure
*/
GckAttributes *
gck_object_cache_lookup (GckObject *object,
const gulong *attr_types,
gint n_attr_types,
GCancellable *cancellable,
GError **error)
{
GckAttributes *attrs;
GckObjectCache *cache;
g_return_val_if_fail (GCK_IS_OBJECT (object), NULL);
g_return_val_if_fail (attr_types != NULL || n_attr_types == 0, NULL);
g_return_val_if_fail (cancellable == NULL || G_IS_CANCELLABLE (cancellable), NULL);
g_return_val_if_fail (error == NULL || *error == NULL, NULL);
if (GCK_IS_OBJECT_CACHE (object)) {
cache = GCK_OBJECT_CACHE (object);
attrs = gck_object_cache_get_attributes (cache);
if (check_have_attributes (attrs, attr_types, n_attr_types))
return attrs;
gck_attributes_unref (attrs);
if (!gck_object_cache_update (cache, attr_types, n_attr_types,
cancellable, error))
return NULL;
return gck_object_cache_get_attributes (cache);
} else {
return gck_object_get_full (object, attr_types, n_attr_types,
cancellable, error);
}
}
/**
* gck_object_cache_lookup_async:
* @object: the object
* @attr_types: (array length=n_attr_types): the types of attributes to update
* @n_attr_types: the number of attribute types
* @cancellable: optional cancellation object
* @callback: called when the operation completes
* @user_data: data to pass to the callback
*
* Lookup attributes in the cache, or retrieve them from the object if necessary.
*
* If @object is a #GckObjectCache then this will lookup the attributes there
* first if available, otherwise will read them from the object and update
* the cache.
*
* If @object is not a #GckObjectCache, then the attributes will simply be
* read from the object.
*
* This will return immediately and complete asynchronously
*/
void
gck_object_cache_lookup_async (GckObject *object,
const gulong *attr_types,
gint n_attr_types,
GCancellable *cancellable,
GAsyncReadyCallback callback,
gpointer user_data)
{
g_return_if_fail (GCK_IS_OBJECT (object));
g_return_if_fail (attr_types != NULL || n_attr_types == 0);
g_return_if_fail (cancellable == NULL || G_IS_CANCELLABLE (cancellable));
if (GCK_IS_OBJECT_CACHE (object)) {
GckObjectCache *cache;
GckAttributes *attrs;
gboolean have;
cache = GCK_OBJECT_CACHE (object);
attrs = gck_object_cache_get_attributes (cache);
have = check_have_attributes (attrs, attr_types, n_attr_types);
gck_attributes_unref (attrs);
if (have) {
GTask *task;
task = g_task_new (cache, cancellable, callback, user_data);
g_task_set_source_tag (task, gck_object_cache_lookup_async);
g_task_return_boolean (task, TRUE);
g_clear_object (&task);
} else {
gck_object_cache_update_async (cache, attr_types, n_attr_types,
cancellable, callback, user_data);
}
} else {
gck_object_get_async (object, attr_types, n_attr_types, cancellable,
callback, user_data);
}
}
/**
* gck_object_cache_lookup_finish:
* @object: the object
* @result: the asynchrounous result passed to the callback
* @error: location to place an error
*
* Complete an operation to lookup attributes in the cache or retrieve them
* from the object if necessary.
*
* Returns: (transfer full): the attributes retrieved or %NULL on failure
*/
GckAttributes *
gck_object_cache_lookup_finish (GckObject *object,
GAsyncResult *result,
GError **error)
{
GckObjectCache *cache;
g_return_val_if_fail (GCK_IS_OBJECT (object), NULL);
g_return_val_if_fail (error == NULL || *error == NULL, NULL);
if (GCK_IS_OBJECT_CACHE (object)) {
cache = GCK_OBJECT_CACHE (object);
if (!g_task_is_valid (result, object))
if (!gck_object_cache_update_finish (cache, result, error))
return NULL;
return gck_object_cache_get_attributes (cache);
}
return gck_object_get_finish (object, result, error);
}