static void
gkm_user_module_init (GkmUserModule *self)
{
	self->unlocked_apps = g_hash_table_new_full (gkm_util_ulong_hash, gkm_util_ulong_equal, gkm_util_ulong_free, NULL);

	/* Our default token info, updated as module runs */
	memcpy (&self->token_info, &user_module_token_info, sizeof (CK_TOKEN_INFO));

	/* For creating stored keys */
	gkm_module_register_factory (GKM_MODULE (self), GKM_FACTORY_USER_PRIVATE_KEY);
	gkm_module_register_factory (GKM_MODULE (self), GKM_FACTORY_USER_PUBLIC_KEY);
}
static GObject*
gkm_roots_module_constructor (GType type, guint n_props, GObjectConstructParam *props)
{
	GkmRootsModule *self = GKM_ROOTS_MODULE (G_OBJECT_CLASS (gkm_roots_module_parent_class)->constructor(type, n_props, props));
	GkmManager *manager;

	g_return_val_if_fail (self, NULL);

#ifdef ROOT_CERTIFICATES
	if (!self->directory)
		self->directory = g_strdup (ROOT_CERTIFICATES);
#endif
	if (self->directory) {
		self->tracker = gkm_file_tracker_new (self->directory, "*", "*.0");
		g_signal_connect (self->tracker, "file-added", G_CALLBACK (file_load), self);
		g_signal_connect (self->tracker, "file-changed", G_CALLBACK (file_load), self);
		g_signal_connect (self->tracker, "file-removed", G_CALLBACK (file_remove), self);
	}

	manager = gkm_module_get_manager (GKM_MODULE (self));
	gkm_manager_add_property_index (manager, "unique", TRUE);
	gkm_manager_add_property_index (manager, "path", FALSE);

	return G_OBJECT (self);
}
static void
gkm_module_finalize (GObject *obj)
{
	GkmModule *self = GKM_MODULE (obj);

	g_hash_table_destroy (self->pv->transient_objects);
	self->pv->transient_objects = NULL;

	g_object_unref (self->pv->transient_store);
	self->pv->transient_store = NULL;

	g_assert (self->pv->token_manager == NULL);

	g_assert (g_hash_table_size (self->pv->apartments_by_id) == 0);
	g_hash_table_destroy (self->pv->apartments_by_id);
	self->pv->apartments_by_id = NULL;

	g_assert (g_hash_table_size (self->pv->sessions_by_handle) == 0);
	g_hash_table_destroy (self->pv->sessions_by_handle);
	self->pv->sessions_by_handle = NULL;

	g_array_free (self->pv->factories, TRUE);
	self->pv->factories = NULL;

	gkm_timer_shutdown ();

	G_OBJECT_CLASS (gkm_module_parent_class)->finalize (obj);
}
static GkmCertificate*
add_certificate_for_data (GkmRootsModule *self, const guchar *data,
                          gsize n_data, const gchar *path)
{
	GkmCertificate *cert;
	GkmManager *manager;
	gchar *hash, *unique;

	g_assert (GKM_IS_ROOTS_MODULE (self));
	g_assert (data);
	g_assert (path);

	manager = gkm_module_get_manager (GKM_MODULE (self));
	g_return_val_if_fail (manager, NULL);

	/* Hash the certificate */
	hash = g_compute_checksum_for_data (G_CHECKSUM_MD5, data, n_data);
	unique = g_strdup_printf ("%s:%s", path, hash);
	g_free (hash);

	/* Try and find a certificate */
	cert = GKM_CERTIFICATE (gkm_manager_find_one_by_string_property (manager, "unique", unique));
	if (cert != NULL) {
		g_free (unique);
		return cert;
	}

	/* Create a new certificate object */
	cert = GKM_CERTIFICATE (gkm_roots_certificate_new (GKM_MODULE (self), unique, path));
	g_free (unique);

	if (!gkm_serializable_load (GKM_SERIALIZABLE (cert), NULL, data, n_data)) {
		g_message ("couldn't parse certificate(s): %s", path);
		g_object_unref (cert);
		return NULL;
	}

	/* Make the certificate show up */
	gkm_object_expose (GKM_OBJECT (cert), TRUE);

	/* And add to our wonderful table */
	g_hash_table_insert (self->certificates, cert, cert);
	return cert;
}
static GObject*
gkm_user_module_constructor (GType type, guint n_props, GObjectConstructParam *props)
{
	GkmUserModule *self = GKM_USER_MODULE (G_OBJECT_CLASS (gkm_user_module_parent_class)->constructor(type, n_props, props));
	g_return_val_if_fail (self, NULL);

	if (!self->directory)
		self->directory = g_build_filename (g_get_home_dir (), ".mate2", "keyrings", NULL);
	self->storage = gkm_user_storage_new (GKM_MODULE (self), self->directory);

	return G_OBJECT (self);
}
static void
file_load (GkmFileTracker *tracker, const gchar *path, GkmRootsModule *self)
{
	ParsePrivate ctx;
	GkmManager *manager;
	GkmCertificate *cert;
	guchar *data;
	GList *objects, *l;
	GError *error = NULL;
	gsize n_data;
	guint num;

	manager = gkm_module_get_manager (GKM_MODULE (self));
	g_return_if_fail (manager);

	/* Read in the public key */
	if (!g_file_get_contents (path, (gchar**)&data, &n_data, &error)) {
		g_warning ("couldn't load root certificates: %s: %s",
		           path, egg_error_message (error));
		return;
	}

	memset (&ctx, 0, sizeof (ctx));
	ctx.path = path;
	ctx.module = self;
	ctx.count = 0;

	/* Checks for what was at this path */
	ctx.checks = g_hash_table_new (g_direct_hash, g_direct_equal);
	objects = gkm_manager_find_by_string_property (manager, "path", path);
	for (l = objects; l; l = g_list_next (l))
		g_hash_table_insert (ctx.checks, l->data, l->data);
	g_list_free (objects);

	/* Try and parse the PEM */
	num = egg_openssl_pem_parse (data, n_data, parsed_pem_block, &ctx);

	/* If no PEM data, try to parse directly as DER  */
	if (ctx.count == 0) {
		cert = add_certificate_for_data (self, data, n_data, path);
		if (cert != NULL)
			g_hash_table_remove (ctx.checks, cert);
	}

	g_hash_table_foreach (ctx.checks, remove_each_certificate, self);
	g_hash_table_destroy (ctx.checks);

	g_free (data);
}
static GObject*
gkm_module_constructor (GType type, guint n_props, GObjectConstructParam *props)
{
	GkmModule *self = GKM_MODULE (G_OBJECT_CLASS (gkm_module_parent_class)->constructor(type, n_props, props));
	CK_ATTRIBUTE attr;

	g_return_val_if_fail (self, NULL);

	/* Register store attributes */
	attr.type = CKA_LABEL;
	attr.pValue = "";
	attr.ulValueLen = 0;
	gkm_store_register_schema (self->pv->transient_store, &attr, NULL, 0);

	return G_OBJECT (self);
}
static void
gkm_module_dispose (GObject *obj)
{
	GkmModule *self = GKM_MODULE (obj);

	g_hash_table_remove_all (self->pv->transient_objects);
	g_hash_table_remove_all (self->pv->sessions_by_handle);
	g_hash_table_remove_all (self->pv->apartments_by_id);

	if (self->pv->token_manager)
		g_object_unref (self->pv->token_manager);
	self->pv->token_manager = NULL;

	g_array_set_size (self->pv->factories, 0);

	G_OBJECT_CLASS (gkm_module_parent_class)->dispose (obj);
}
static void
file_load (EggFileTracker *tracker,
           const gchar *path,
           GkmSshModule *self)
{
	GkmSshPrivateKey *key;
	gchar *private_path;
	GError *error = NULL;
	gchar *unique;

	g_return_if_fail (path);
	g_return_if_fail (GKM_IS_SSH_MODULE (self));

	private_path = private_path_for_public (path);
	if (!private_path || !g_file_test (private_path, G_FILE_TEST_IS_REGULAR)) {
		g_message ("no private key present for public key: %s", path);
		g_free (private_path);
		return;
	}

	/* Create a key if necessary */
	key = g_hash_table_lookup (self->keys_by_path, path);
	if (key == NULL) {
		unique = g_strdup_printf ("ssh-store:%s", private_path);
		key = gkm_ssh_private_key_new (GKM_MODULE (self), unique);
		g_free (unique);

		g_hash_table_replace (self->keys_by_path, g_strdup (path), key);
	}

	/* Parse the data into the key */
	if (!gkm_ssh_private_key_parse (key, path, private_path, &error)) {
		if (error) {
			g_message ("couldn't parse data: %s: %s", path, egg_error_message (error));
			g_clear_error (&error);
		}
		gkm_object_expose (GKM_OBJECT (key), FALSE);

	/* When successful register with the object manager */
	} else {
		gkm_object_expose (GKM_OBJECT (key), TRUE);
	}

	g_free (private_path);
}
static void
file_remove (GkmFileTracker *tracker, const gchar *path, GkmRootsModule *self)
{
	GList *objects, *l;
	GkmManager *manager;

	g_return_if_fail (path);
	g_return_if_fail (GKM_IS_ROOTS_MODULE (self));

	manager = gkm_module_get_manager (GKM_MODULE (self));
	g_return_if_fail (manager);

	objects = gkm_manager_find_by_string_property (manager, "path", path);
	for (l = objects; l; l = g_list_next (l))
		if (!g_hash_table_remove (self->certificates, l->data))
			g_return_if_reached ();
	g_list_free (objects);
}
static void
gkm_module_get_property (GObject *obj, guint prop_id, GValue *value,
                         GParamSpec *pspec)
{
	GkmModule *self = GKM_MODULE (obj);

	switch (prop_id) {
	case PROP_MANAGER:
		g_value_set_object (value, gkm_module_get_manager (self));
		break;
	case PROP_WRITE_PROTECTED:
		g_value_set_boolean (value, gkm_module_get_write_protected (self));
		break;
	default:
		G_OBJECT_WARN_INVALID_PROPERTY_ID (obj, prop_id, pspec);
		break;
	}
}
static void
gkm_module_set_property (GObject *obj, guint prop_id, const GValue *value,
                         GParamSpec *pspec)
{
	GkmModule *self = GKM_MODULE (obj);
	CK_C_INITIALIZE_ARGS_PTR args;

	switch (prop_id) {
	case PROP_INITIALIZE_ARGS:
		args = g_value_get_pointer (value);
		if (args != NULL && args->pReserved != NULL)
			parse_arguments (self, args->pReserved);
		break;
	case PROP_MUTEX:
		self->pv->mutex = g_value_get_pointer (value);
		g_return_if_fail (self->pv->mutex);
		break;
	default:
		G_OBJECT_WARN_INVALID_PROPERTY_ID (obj, prop_id, pspec);
		break;
	}
}