[gnome-keyring] [gck] Update PKCS#11 URI to add library support.



commit 982a0df6b8205322c47828c27d351263554fc1d9
Author: Stef Walter <stef memberwebs com>
Date:   Fri Dec 31 08:06:31 2010 -0600

    [gck] Update PKCS#11 URI to add library support.
    
    Add library-description and library-description and library-manufacturer
    parts. And refactor to support parsing in different contexts better.

 docs/reference/gck/gck-docs.sgml    |    1 +
 docs/reference/gck/gck-sections.txt |    9 +
 gck/gck-enumerator.c                |   49 +++--
 gck/gck-misc.c                      |   39 ++++
 gck/gck-modules.c                   |   63 ++++--
 gck/gck-private.h                   |   12 +-
 gck/gck-slot.c                      |   36 +---
 gck/gck-uri.c                       |  217 +++++++++++++++----
 gck/gck.h                           |   29 ++-
 gck/tests/gck-test.h                |   19 --
 gck/tests/test-gck-enumerator.c     |   47 +++--
 gck/tests/test-gck-uri.c            |  405 ++++++++++++++++++++++++++++------
 gck/tests/test-gck.h                |   43 ++++
 13 files changed, 738 insertions(+), 231 deletions(-)
---
diff --git a/docs/reference/gck/gck-docs.sgml b/docs/reference/gck/gck-docs.sgml
index 2846192..5b5e114 100644
--- a/docs/reference/gck/gck-docs.sgml
+++ b/docs/reference/gck/gck-docs.sgml
@@ -20,6 +20,7 @@
 		<xi:include href="xml/gck-attribute.xml"/>
 		<xi:include href="xml/gck-attributes.xml"/>
 		<xi:include href="xml/gck-error.xml"/>
+		<xi:include href="xml/gck-uri.xml"/>
 		<xi:include href="xml/gck-misc.xml"/>
 	</chapter>
 </book>
diff --git a/docs/reference/gck/gck-sections.txt b/docs/reference/gck/gck-sections.txt
index d567b8e..4edad3f 100644
--- a/docs/reference/gck/gck-sections.txt
+++ b/docs/reference/gck/gck-sections.txt
@@ -189,6 +189,15 @@ CKR_GCK_MODULE_PROBLEM
 </SECTION>
 
 <SECTION>
+<FILE>gck-uri</FILE>
+GckUriInfo
+GckUriParseFlags
+gck_uri_parse
+gck_uri_build
+gck_uri_info_free
+</SECTION>
+
+<SECTION>
 <FILE>gck-misc</FILE>
 gck_list_ref_copy
 gck_list_unref_free
diff --git a/gck/gck-enumerator.c b/gck/gck-enumerator.c
index 2e39497..49e71fd 100644
--- a/gck/gck-enumerator.c
+++ b/gck/gck-enumerator.c
@@ -56,8 +56,7 @@ struct _GckEnumeratorState {
 
 	/* Input to enumerator */
 	GList *modules;
-	GckTokenInfo *match_token;
-	GckAttributes *match_attrs;
+	GckUriInfo *match;
 	guint session_options;
 	gboolean authenticate;
 	gchar *password;
@@ -151,12 +150,11 @@ cleanup_state (GckEnumeratorState *args)
 		args->password  = NULL;
 	}
 
-	gck_token_info_free (args->match_token);
-	args->match_token = NULL;
-
-	if (args->match_attrs) {
-		_gck_attributes_unlock (args->match_attrs);
-		gck_attributes_unref (args->match_attrs);
+	if (args->match) {
+		if (args->match->attributes)
+			_gck_attributes_unlock (args->match->attributes);
+		gck_uri_info_free (args->match);
+		args->match = NULL;
 	}
 }
 
@@ -194,6 +192,7 @@ state_slots (GckEnumeratorState *args, gboolean forward)
 	GckSlot *slot;
 	GckModule *module;
 	GckTokenInfo *token_info;
+	gboolean matched;
 
 	g_assert (args->slot == NULL);
 
@@ -215,15 +214,23 @@ state_slots (GckEnumeratorState *args, gboolean forward)
 			return rewind_state (args, state_modules);
 		}
 
+		matched = TRUE;
+
+		/* Do we have unrecognized matches? */
+		if (args->match->any_unrecognized) {
+			matched = FALSE;
+
 		/* Are we trying to match the slot? */
-		if (args->match_token) {
+		} else if (args->match->token_info) {
 
 			/* No match? Go to next slot */
-			if (!_gck_token_info_match (args->match_token, token_info)) {
-				g_object_unref (slot);
-				gck_token_info_free (token_info);
-				return state_slots;
-			}
+			matched = _gck_token_info_match (args->match->token_info, token_info);
+		}
+
+		if (!matched) {
+			g_object_unref (slot);
+			gck_token_info_free (token_info);
+			return state_slots;
 		}
 
 		module = gck_slot_get_module (slot);
@@ -372,8 +379,8 @@ state_authenticated (GckEnumeratorState *args, gboolean forward)
 	g_assert (args->want_objects);
 	g_assert (args->funcs);
 
-	if (args->match_attrs) {
-		attrs = _gck_attributes_commit_out (args->match_attrs, &n_attrs);
+	if (args->match->attributes) {
+		attrs = _gck_attributes_commit_out (args->match->attributes, &n_attrs);
 	} else {
 		attrs = NULL;
 		n_attrs = 0;
@@ -494,7 +501,7 @@ gck_enumerator_class_init (GckEnumeratorClass *klass)
 
 GckEnumerator*
 _gck_enumerator_new (GList *modules_or_slots, guint session_options,
-                     GckTokenInfo *match_token, GckAttributes *match_attrs)
+                     GckUriInfo *uri_info)
 {
 	GckEnumerator *self;
 	GckEnumeratorState *state;
@@ -514,11 +521,9 @@ _gck_enumerator_new (GList *modules_or_slots, guint session_options,
 		state->handler = state_modules;
 	}
 
-	if (match_attrs) {
-		state->match_attrs = gck_attributes_ref (match_attrs);
-		_gck_attributes_lock (state->match_attrs);
-	}
-	state->match_token = match_token;
+	state->match = uri_info;
+	if (uri_info->attributes)
+		_gck_attributes_lock (uri_info->attributes);
 
 	return self;
 }
diff --git a/gck/gck-misc.c b/gck/gck-misc.c
index 663f9b3..ae8b0d4 100644
--- a/gck/gck-misc.c
+++ b/gck/gck-misc.c
@@ -372,3 +372,42 @@ gck_value_to_boolean (gconstpointer value, gsize length, gboolean *result)
 		*result = *((CK_BBOOL*)value) ? TRUE : FALSE;
 	return TRUE;
 }
+
+static gboolean
+match_info_string (const gchar *match, const gchar *string)
+{
+	/* NULL matches anything */
+	if (match == NULL)
+		return TRUE;
+
+	if (string == NULL)
+		return FALSE;
+
+	return g_str_equal (match, string);
+}
+
+gboolean
+_gck_module_info_match (GckModuleInfo *match, GckModuleInfo *info)
+{
+	/* Matches two GckModuleInfo for use in PKCS#11 URI's */
+
+	g_return_val_if_fail (match, FALSE);
+	g_return_val_if_fail (info, FALSE);
+
+	return (match_info_string (match->library_description, info->library_description) &&
+	        match_info_string (match->manufacturer_id, info->manufacturer_id));
+}
+
+gboolean
+_gck_token_info_match (GckTokenInfo *match, GckTokenInfo *info)
+{
+	/* Matches two GckTokenInfo for use in PKCS#11 URI's */
+
+	g_return_val_if_fail (match, FALSE);
+	g_return_val_if_fail (info, FALSE);
+
+	return (match_info_string (match->label, info->label) &&
+	        match_info_string (match->manufacturer_id, info->manufacturer_id) &&
+	        match_info_string (match->model, info->model) &&
+	        match_info_string (match->serial_number, info->serial_number));
+}
diff --git a/gck/gck-modules.c b/gck/gck-modules.c
index 59ae1c8..c500d55 100644
--- a/gck/gck-modules.c
+++ b/gck/gck-modules.c
@@ -151,32 +151,57 @@ gck_modules_get_slots (GList *modules, gboolean token_present)
 GckEnumerator*
 gck_modules_enumerate_objects (GList *modules, GckAttributes *attrs, guint session_options)
 {
-	return _gck_enumerator_new (modules, session_options, NULL, attrs);
+	GckUriInfo *uri_info;
+
+	g_return_val_if_fail (attrs, NULL);
+
+	uri_info = _gck_uri_info_new ();
+	uri_info->attributes = gck_attributes_ref (attrs);
+
+	return _gck_enumerator_new (modules, session_options, uri_info);
 }
 
 GckSlot*
 gck_modules_token_for_uri (GList *modules, const gchar *uri, GError **error)
 {
-	GckTokenInfo *match, *token;
+	GckTokenInfo *token_info;
 	GckSlot *result = NULL;
+	GckUriInfo *uri_info;
+	GckModuleInfo *module_info;
 	GList *slots;
 	GList *m, *s;
+	gboolean matched;
 
-	if (!gck_uri_parse (uri, &match, NULL, error))
+	uri_info = gck_uri_parse (uri, GCK_URI_PARSE_TOKEN, error);
+	if (uri_info == NULL)
 		return NULL;
 
-	for (m = modules; result == NULL && m != NULL; m = g_list_next (m)) {
-		slots = gck_module_get_slots (m->data, TRUE);
-		for (s = slots; result == NULL && s != NULL; s = g_list_next (s)) {
-			token = gck_slot_get_token_info (s->data);
-			if (token && _gck_token_info_match (match, token))
-				result = g_object_ref (s->data);
-			gck_token_info_free (token);
+	if (!uri_info->any_unrecognized) {
+		for (m = modules; result == NULL && m != NULL; m = g_list_next (m)) {
+			if (uri_info->module_info) {
+				module_info = gck_module_get_info (m->data);
+				matched = _gck_module_info_match (uri_info->module_info, module_info);
+				gck_module_info_free (module_info);
+				if (!matched)
+					continue;
+			}
+
+			slots = gck_module_get_slots (m->data, TRUE);
+			for (s = slots; result == NULL && s != NULL; s = g_list_next (s)) {
+				if (!uri_info->token_info) {
+					result = g_object_ref (s->data);
+				} else {
+					token_info = gck_slot_get_token_info (s->data);
+					if (token_info && _gck_token_info_match (uri_info->token_info, token_info))
+						result = g_object_ref (s->data);
+					gck_token_info_free (token_info);
+				}
+			}
+			gck_list_unref_free (slots);
 		}
-		gck_list_unref_free (slots);
 	}
 
-	gck_token_info_free (match);
+	gck_uri_info_free (uri_info);
 	return result;
 }
 
@@ -224,16 +249,12 @@ GckEnumerator*
 gck_modules_enumerate_uri (GList *modules, const gchar *uri, guint session_options,
                            GError **error)
 {
-	GckTokenInfo *token;
-	GckAttributes *attrs;
-	GckEnumerator *en;
+	GckUriInfo *uri_info;
 
-	if (!gck_uri_parse (uri, &token, &attrs, error))
+	uri_info = gck_uri_parse (uri, GCK_URI_PARSE_OBJECT, error);
+	if (uri_info == NULL)
 		return NULL;
 
-	/* Takes ownership of token info */
-	en = _gck_enumerator_new (modules, session_options, token, attrs);
-	gck_attributes_unref (attrs);
-
-	return en;
+	/* Takes ownership of uri_info */
+	return _gck_enumerator_new (modules, session_options, uri_info);
 }
diff --git a/gck/gck-private.h b/gck/gck-private.h
index 91727fe..544c4d6 100644
--- a/gck/gck-private.h
+++ b/gck/gck-private.h
@@ -72,14 +72,16 @@ gboolean            _gck_module_fire_authenticate_object   (GckModule *module,
                                                              gchar *label,
                                                              gchar **password);
 
+gboolean            _gck_module_info_match                  (GckModuleInfo *match,
+                                                             GckModuleInfo *module_info);
+
 /* -----------------------------------------------------------------------------
  * ENUMERATOR
  */
 
 GckEnumerator*      _gck_enumerator_new                     (GList *modules,
                                                              guint session_options,
-                                                             GckTokenInfo *match_token,
-                                                             GckAttributes *match_attrs);
+                                                             GckUriInfo *uri_info);
 
 /* ----------------------------------------------------------------------------
  * SLOT
@@ -89,6 +91,12 @@ gboolean           _gck_token_info_match                    (GckTokenInfo *match
                                                              GckTokenInfo *info);
 
 /* ----------------------------------------------------------------------------
+ * URI
+ */
+
+GckUriInfo*         _gck_uri_info_new                       (void);
+
+/* ----------------------------------------------------------------------------
  * CALL
  */
 
diff --git a/gck/gck-slot.c b/gck/gck-slot.c
index 8698198..990800d 100644
--- a/gck/gck-slot.c
+++ b/gck/gck-slot.c
@@ -300,33 +300,6 @@ gck_token_info_free (GckTokenInfo *token_info)
 	g_free (token_info);
 }
 
-static gboolean
-match_token_string (const gchar *match, const gchar *string)
-{
-	/* NULL matches anything */
-	if (match == NULL)
-		return TRUE;
-
-	if (string == NULL)
-		return FALSE;
-
-	return g_str_equal (match, string);
-}
-
-gboolean
-_gck_token_info_match (GckTokenInfo *match, GckTokenInfo *info)
-{
-	/* Matches two GckTokenInfo for use in PKCS#11 URI's */
-
-	g_return_val_if_fail (match, FALSE);
-	g_return_val_if_fail (info, FALSE);
-
-	return (match_token_string (match->label, info->label) &&
-	        match_token_string (match->manufacturer_id, info->manufacturer_id) &&
-	        match_token_string (match->model, info->model) &&
-	        match_token_string (match->serial_number, info->serial_number));
-}
-
 /**
  * GckMechanismInfo:
  * @min_key_size: The minimum key size that can be used with this mechanism.
@@ -814,7 +787,14 @@ gck_slot_has_flags (GckSlot *self, gulong flags)
 GckEnumerator*
 gck_slots_enumerate_objects (GList *slots, GckAttributes *attrs, guint session_options)
 {
-	return _gck_enumerator_new (slots, session_options, NULL, attrs);
+	GckUriInfo *uri_info;
+
+	g_return_val_if_fail (attrs, NULL);
+
+	uri_info = _gck_uri_info_new ();
+	uri_info->attributes = gck_attributes_ref (attrs);
+
+	return _gck_enumerator_new (slots, session_options, uri_info);
 }
 
 
diff --git a/gck/gck-uri.c b/gck/gck-uri.c
index be6e6a1..f43d704 100644
--- a/gck/gck-uri.c
+++ b/gck/gck-uri.c
@@ -34,11 +34,51 @@
 #include "egg/egg-hex.h"
 
 /**
- * SECTION:gck-modules
- * @title: GckModule lists
- * @short_description: Dealing with lists of PKCS#11 modules.
+ * SECTION:gck-uri
+ * @title: PKCS11 URIs
+ * @short_description: Parsing and building PKCS\#11 URIs.
  *
- * Xxxxx
+ * <ulink href='http://tools.ietf.org/html/draft-pechanec-pkcs11uri-03'>PKCS#11 URIs</ulink>
+ * are a standard for referring to PKCS#11 modules, tokens, or objects. What the
+ * PKCS\#11 URI refers to depends on the context in which it is used.
+ *
+ * A PKCS\#11 URI can always resolve to more than one object, token or module. A
+ * PKCS\#11 URI that refers to a token, would (when used in a context that expects
+ * objects) refer to all the token on that module.
+ *
+ * In most cases the parsing or building of URIs is handled elsewhere in the GCK
+ * library. For example to enumerate objects that match a PKCS\#11 URI use the
+ * gck_modules_enumerate_uri() function. Or to build a PKCS\#11 URI for a given
+ * object, use the gck_object_build_uri() function.
+ *
+ * To parse a PKCS\#11 URI use the gck_uri_parse() function passing in the type of
+ * context in which you're using the URI. To build a URI use the gck_uri_build()
+ * function.
+ **/
+
+/**
+ * GckUriInfo:
+ * @any_unrecognized: whether any parts of the PKCS\#11 URI were unsupported or unrecognized.
+ * @module_info: information about the PKCS\#11 modules matching the URI.
+ * @token_info: information about the PKCS\#11 tokens matching the URI.
+ * @attributes: information about the PKCS\#11 objects matching the URI.
+ *
+ * Information about the contents of a PKCS\#11 URI. Various fields may be %NULL
+ * depending on the context that the URI was parsed for.
+ *
+ * Since PKCS\#11 URIs represent a set which results from the intersections of
+ * all of the URI parts, if @any_recognized is set to %TRUE then usually the URI
+ * should be treated as not matching anything.
+ */
+
+/**
+ * GckUriContext:
+ * @GCK_URI_CONTEXT_MODULE: the URI will be used to match modules.
+ * @GCK_URI_CONTEXT_TOKEN: the URI will be used to match tokens.
+ * @GCK_URI_CONTEXT_OBJECT: the URI will be used to match objects.
+ * @GCK_URI_CONTEXT_ANY: parse all recognized components of the URI.
+ *
+ * Which context the PKCS\#11 URI will be used in.
  */
 
 #define URI_PREFIX "pkcs11:"
@@ -58,6 +98,12 @@ gck_uri_get_error_quark (void)
 	return domain;
 }
 
+GckUriInfo*
+_gck_uri_info_new (void)
+{
+	return g_slice_new0 (GckUriInfo);
+}
+
 static gint
 parse_string_attribute (const gchar *name, const gchar *start, const gchar *end,
                         GckAttributes *attrs, GError **error)
@@ -177,14 +223,60 @@ parse_token_attribute (const gchar *name, const gchar *start, const gchar *end,
 	return 1;
 }
 
-gboolean
-gck_uri_parse (const gchar *uri, GckTokenInfo **token, GckAttributes **attrs, GError **error)
+static gint
+parse_library_attribute (const gchar *name, const gchar *start, const gchar *end,
+                         GckModuleInfo *library, GError **error)
+{
+	gchar **value;
+	gchar *string;
+
+	g_assert (name);
+	g_assert (start);
+	g_assert (end);
+	g_assert (library);
+
+	if (g_str_equal (name, "library-description"))
+		value = &(library->library_description);
+	else if (g_str_equal (name, "manufacturer"))
+		value = &(library->manufacturer_id);
+	else
+		return 0;
+
+	string = g_uri_unescape_segment (start, end, "");
+	if (string == NULL) {
+		g_set_error (error, GCK_URI_ERROR, GCK_URI_BAD_ENCODING,
+		             _("The URI has invalid syntax. The '%s' field encoding is invalid."), name);
+		return -1;
+	}
+
+	g_free (*value);
+	*value = string;
+
+	return 1;
+}
+
+/**
+ * gck_uri_parse:
+ * @uri: the URI to parse.
+ * @flags: the context in which the URI will be used.
+ * @error: a #GError, or %NULL.
+ *
+ * Parse a PKCS\#11 URI for use in a given context.
+ *
+ * The result will contain the fields that are relevant for
+ * the given context. See #GckUriInfo  for more info.
+ * Other fields will be set to %NULL.
+ *
+ * Return value: a newly allocated #GckUriInfo, which should be freed with
+ * 	gck_uri_info_free().
+ */
+GckUriInfo*
+gck_uri_parse (const gchar *uri, GckUriParseFlags flags, GError **error)
 {
-	GckAttributes *rattrs = NULL;
-	GckTokenInfo *rtoken = NULL;
 	const gchar *spos, *epos;
 	gchar *key = NULL;
 	gboolean ret = FALSE;
+	GckUriInfo *uri_info = NULL;
 	gint res;
 
 	g_return_val_if_fail (uri, FALSE);
@@ -197,8 +289,14 @@ gck_uri_parse (const gchar *uri, GckTokenInfo **token, GckAttributes **attrs, GE
 	}
 
 	uri += N_URI_PREFIX;
-	rattrs = gck_attributes_new ();
-	rtoken = g_new0 (GckTokenInfo, 1);
+
+	uri_info = _gck_uri_info_new ();
+	if ((flags & GCK_URI_PARSE_MODULE) == GCK_URI_PARSE_MODULE)
+		uri_info->module_info = g_new0 (GckModuleInfo, 1);
+	if ((flags & GCK_URI_PARSE_TOKEN) == GCK_URI_PARSE_TOKEN)
+		uri_info->token_info = g_new0 (GckTokenInfo, 1);
+	if ((flags & GCK_URI_PARSE_OBJECT) == GCK_URI_PARSE_OBJECT)
+		uri_info->attributes = gck_attributes_new ();
 
 	for (;;) {
 		spos = strchr (uri, ';');
@@ -220,15 +318,21 @@ gck_uri_parse (const gchar *uri, GckTokenInfo **token, GckAttributes **attrs, GE
 		key = g_strndup (uri, epos - uri);
 		epos++;
 
-		res = parse_string_attribute (key, epos, spos, rattrs, error);
-		if (res == 0)
-			res = parse_binary_attribute (key, epos, spos, rattrs, error);
-		if (res == 0)
-			res = parse_token_attribute (key, epos, spos, rtoken, error);
+		res = 0;
+		if (uri_info->attributes)
+			res = parse_string_attribute (key, epos, spos, uri_info->attributes, error);
+		if (res == 0 && uri_info->attributes)
+			res = parse_binary_attribute (key, epos, spos, uri_info->attributes, error);
+		if (res == 0 && uri_info->token_info)
+			res = parse_token_attribute (key, epos, spos, uri_info->token_info, error);
+		if (res == 0 && uri_info->module_info)
+			res = parse_library_attribute (key, epos, spos, uri_info->module_info, error);
 		if (res < 0)
 			goto cleanup;
-		if (res == 0)
-			g_message ("Ignoring unsupported field '%s'", key);
+		if (res == 0) {
+			g_message ("Ignoring unrecognized or unsupported field '%s'", key);
+			uri_info->any_unrecognized = TRUE;
+		}
 
 		if (*spos == '\0')
 			break;
@@ -238,21 +342,13 @@ gck_uri_parse (const gchar *uri, GckTokenInfo **token, GckAttributes **attrs, GE
 	ret = TRUE;
 
 cleanup:
-	if (ret && token) {
-		*token = rtoken;
-		rtoken = NULL;
-	}
-	if (ret && attrs) {
-		*attrs = rattrs;
-		rattrs = NULL;
+	if (!ret) {
+		gck_uri_info_free (uri_info);
+		uri_info = NULL;
 	}
 
-	gck_token_info_free (rtoken);
-	if (rattrs)
-		gck_attributes_unref (rattrs);
-
 	g_free (key);
-	return ret;
+	return uri_info;
 }
 
 static void
@@ -267,8 +363,6 @@ build_string_attribute (const gchar *name, const gchar *value,
 
 	if (!value)
 		return;
-	if (!value[0])
-		return;
 
 	segment = g_uri_escape_string (value, "", FALSE);
 	if (!*first)
@@ -290,10 +384,7 @@ build_binary_attribute (const gchar *name, gconstpointer data, gsize n_data,
 	g_assert (first);
 	g_assert (result);
 	g_assert (name);
-
-	if (!n_data)
-		return;
-	g_assert (data);
+	g_assert (!n_data || data);
 
 	segment = egg_hex_encode_full (data, n_data, FALSE, ':', 1);
 	if (!*first)
@@ -306,8 +397,17 @@ build_binary_attribute (const gchar *name, gconstpointer data, gsize n_data,
 	g_free (segment);
 }
 
+/**
+ * gck_uri_build:
+ * @uri_info: the info to build the URI from.
+ *
+ * Build a PKCS\#11 URI. Any set fields of @uri_info will be used to build
+ * the URI.
+ *
+ * Return value: a newly allocated string containing a PKCS\#11 URI.
+ */
 gchar*
-gck_uri_build (GckTokenInfo *token, GckAttributes *attrs)
+gck_uri_build (GckUriInfo *uri_info)
 {
 	GckAttribute *attr;
 	GString *result;
@@ -315,21 +415,28 @@ gck_uri_build (GckTokenInfo *token, GckAttributes *attrs)
 	gulong klass;
 	gboolean first = TRUE;
 
+	g_return_val_if_fail (uri_info, NULL);
+
 	result = g_string_new (URI_PREFIX);
 
-	if (token) {
-		build_string_attribute ("model", token->model, result, &first);
-		build_string_attribute ("manufacturer", token->manufacturer_id, result, &first);
-		build_string_attribute ("serial", token->serial_number, result, &first);
-		build_string_attribute ("token", token->label, result, &first);
+	if (uri_info->module_info) {
+		build_string_attribute ("library-description", uri_info->module_info->library_description, result, &first);
+		build_string_attribute ("library-manufacturer", uri_info->module_info->manufacturer_id, result, &first);
+	}
+
+	if (uri_info->token_info) {
+		build_string_attribute ("model", uri_info->token_info->model, result, &first);
+		build_string_attribute ("manufacturer", uri_info->token_info->manufacturer_id, result, &first);
+		build_string_attribute ("serial", uri_info->token_info->serial_number, result, &first);
+		build_string_attribute ("token", uri_info->token_info->label, result, &first);
 	}
 
-	if (attrs) {
-		if (gck_attributes_find_string (attrs, CKA_LABEL, &value)) {
+	if (uri_info->attributes) {
+		if (gck_attributes_find_string (uri_info->attributes, CKA_LABEL, &value)) {
 			build_string_attribute ("object", value, result, &first);
 			g_free (value);
 		}
-		if (gck_attributes_find_ulong (attrs, CKA_CLASS, &klass)) {
+		if (gck_attributes_find_ulong (uri_info->attributes, CKA_CLASS, &klass)) {
 			if (klass == CKO_CERTIFICATE)
 				build_string_attribute ("objecttype", "cert", result, &first);
 			else if (klass == CKO_PUBLIC_KEY)
@@ -341,10 +448,30 @@ gck_uri_build (GckTokenInfo *token, GckAttributes *attrs)
 			else if (klass == CKO_DATA)
 				build_string_attribute ("objecttype", "data", result, &first);
 		}
-		attr = gck_attributes_find (attrs, CKA_ID);
+		attr = gck_attributes_find (uri_info->attributes, CKA_ID);
 		if (attr != NULL)
 			build_binary_attribute ("id", attr->value, attr->length, result, &first);
 	}
 
 	return g_string_free (result, FALSE);
 }
+
+/**
+ * gck_uri_info_free:
+ * @uri_info: URI info to free.
+ *
+ * Free a #GckUriInfo.
+ */
+void
+gck_uri_info_free (GckUriInfo *uri_info)
+{
+	if (uri_info) {
+		if (uri_info->attributes)
+			gck_attributes_unref (uri_info->attributes);
+		if (uri_info->module_info)
+			gck_module_info_free (uri_info->module_info);
+		if (uri_info->token_info)
+			gck_token_info_free (uri_info->token_info);
+		g_slice_free (GckUriInfo, uri_info);
+	}
+}
diff --git a/gck/gck.h b/gck/gck.h
index 4a4a564..0f62be0 100644
--- a/gck/gck.h
+++ b/gck/gck.h
@@ -1300,17 +1300,34 @@ enum {
 	GCK_URI_BAD_SYNTAX = 3
 };
 
+typedef enum {
+	GCK_URI_PARSE_MODULE = (1 << 1),
+	GCK_URI_PARSE_TOKEN =   (1 << 2) | GCK_URI_PARSE_MODULE,
+	GCK_URI_PARSE_OBJECT =  (1 << 3) | GCK_URI_PARSE_TOKEN,
+	GCK_URI_PARSE_ANY =     0xFFFFFFFF,
+} GckUriParseFlags;
+
+typedef struct _GckUriInfo {
+	gboolean any_unrecognized;
+	GckModuleInfo *module_info;
+	GckTokenInfo *token_info;
+	GckAttributes *attributes;
+
+	/*< private >*/
+	gpointer dummy[4];
+} GckUriInfo;
+
 #define             GCK_URI_ERROR                           (gck_uri_get_error_quark ())
 
 GQuark              gck_uri_get_error_quark                 (void);
 
-gchar*              gck_uri_build                           (GckTokenInfo *token,
-                                                             GckAttributes *attrs);
+gchar*              gck_uri_build                           (GckUriInfo *uri_info);
 
-gboolean            gck_uri_parse                           (const gchar *uri,
-                                                             GckTokenInfo **token,
-                                                             GckAttributes **attrs,
-                                                             GError **err);
+GckUriInfo*         gck_uri_parse                           (const gchar *uri,
+                                                             GckUriParseFlags flags,
+                                                             GError **error);
+
+void                gck_uri_info_free                       (GckUriInfo *uri_info);
 
 G_END_DECLS
 
diff --git a/gck/tests/test-gck-enumerator.c b/gck/tests/test-gck-enumerator.c
index 2d86563..a9148f2 100644
--- a/gck/tests/test-gck-enumerator.c
+++ b/gck/tests/test-gck-enumerator.c
@@ -31,20 +31,24 @@ TESTING_TEARDOWN(enumerator)
 
 TESTING_TEST(enumerator_create)
 {
+	GckUriInfo *uri_info;
 	GckEnumerator *en;
 
-	en = _gck_enumerator_new (modules, 0, NULL, NULL);
+	uri_info = _gck_uri_info_new ();
+	en = _gck_enumerator_new (modules, 0, uri_info);
 	g_assert (GCK_IS_ENUMERATOR (en));
 	g_object_unref (en);
 }
 
 TESTING_TEST(enumerator_create_slots)
 {
+	GckUriInfo *uri_info;
 	GckEnumerator *en;
 	GList *slots;
 
+	uri_info = _gck_uri_info_new ();
 	slots = gck_module_get_slots (module, FALSE);
-	en = _gck_enumerator_new (slots, 0, NULL, NULL);
+	en = _gck_enumerator_new (slots, 0, uri_info);
 	g_assert (GCK_IS_ENUMERATOR (en));
 	g_object_unref (en);
 	gck_list_unref_free (slots);
@@ -52,11 +56,13 @@ TESTING_TEST(enumerator_create_slots)
 
 TESTING_TEST(enumerator_next)
 {
+	GckUriInfo *uri_info;
 	GError *error = NULL;
 	GckEnumerator *en;
 	GckObject *obj;
 
-	en = _gck_enumerator_new (modules, 0, NULL, NULL);
+	uri_info = _gck_uri_info_new ();
+	en = _gck_enumerator_new (modules, 0, uri_info);
 	g_assert (GCK_IS_ENUMERATOR (en));
 
 	obj = gck_enumerator_next (en, NULL, &error);
@@ -68,13 +74,15 @@ TESTING_TEST(enumerator_next)
 
 TESTING_TEST(enumerator_next_slots)
 {
+	GckUriInfo *uri_info;
 	GError *error = NULL;
 	GList *slots = NULL;
 	GckEnumerator *en;
 	GckObject *obj;
 
+	uri_info = _gck_uri_info_new ();
 	slots = gck_module_get_slots (module, FALSE);
-	en = _gck_enumerator_new (slots, 0, NULL, NULL);
+	en = _gck_enumerator_new (slots, 0, uri_info);
 	g_assert (GCK_IS_ENUMERATOR (en));
 
 	obj = gck_enumerator_next (en, NULL, &error);
@@ -87,11 +95,13 @@ TESTING_TEST(enumerator_next_slots)
 
 TESTING_TEST(enumerator_next_and_resume)
 {
+	GckUriInfo *uri_info;
 	GError *error = NULL;
 	GckEnumerator *en;
 	GckObject *obj, *obj2;
 
-	en = _gck_enumerator_new (modules, 0, NULL, NULL);
+	uri_info = _gck_uri_info_new ();
+	en = _gck_enumerator_new (modules, 0, uri_info);
 	g_assert (GCK_IS_ENUMERATOR (en));
 
 	obj = gck_enumerator_next (en, NULL, &error);
@@ -111,11 +121,13 @@ TESTING_TEST(enumerator_next_and_resume)
 
 TESTING_TEST(enumerator_next_n)
 {
+	GckUriInfo *uri_info;
 	GError *error = NULL;
 	GckEnumerator *en;
 	GList *objects, *l;
 
-	en = _gck_enumerator_new (modules, 0, NULL, NULL);
+	uri_info = _gck_uri_info_new ();
+	en = _gck_enumerator_new (modules, 0, uri_info);
 	g_assert (GCK_IS_ENUMERATOR (en));
 
 	objects = gck_enumerator_next_n (en, -1, NULL, &error);
@@ -138,12 +150,14 @@ fetch_async_result (GObject *source, GAsyncResult *result, gpointer user_data)
 
 TESTING_TEST(enumerator_next_async)
 {
+	GckUriInfo *uri_info;
 	GAsyncResult *result = NULL;
 	GError *error = NULL;
 	GckEnumerator *en;
 	GList *objects, *l;
 
-	en = _gck_enumerator_new (modules, 0, NULL, NULL);
+	uri_info = _gck_uri_info_new ();
+	en = _gck_enumerator_new (modules, 0, uri_info);
 	g_assert (GCK_IS_ENUMERATOR (en));
 
 	gck_enumerator_next_async (en, -1, NULL, fetch_async_result, &result);
@@ -163,16 +177,16 @@ TESTING_TEST(enumerator_next_async)
 
 TESTING_TEST(enumerator_attributes)
 {
-	GckAttributes *attrs;
+	GckUriInfo *uri_info;
 	GError *error = NULL;
 	GckEnumerator *en;
 	GList *objects;
 
-	attrs = gck_attributes_new ();
-	gck_attributes_add_string (attrs, CKA_LABEL, "Private Capitalize Key");
-	en = _gck_enumerator_new (modules, 0, NULL, attrs);
+	uri_info = _gck_uri_info_new ();
+	uri_info->attributes = gck_attributes_new ();
+	gck_attributes_add_string (uri_info->attributes, CKA_LABEL, "Private Capitalize Key");
+	en = _gck_enumerator_new (modules, 0, uri_info);
 	g_assert (GCK_IS_ENUMERATOR (en));
-	gck_attributes_unref (attrs);
 
 	objects = gck_enumerator_next_n (en, -1, NULL, &error);
 	SUCCESS_RES (objects, error);
@@ -185,14 +199,15 @@ TESTING_TEST(enumerator_attributes)
 
 TESTING_TEST(enumerator_token_match)
 {
-	GckTokenInfo *token;
+	GckUriInfo *uri_info;
 	GError *error = NULL;
 	GckEnumerator *en;
 	GList *objects;
 
-	token = g_new0 (GckTokenInfo, 1);
-	token->label = g_strdup ("Invalid token name");
-	en = _gck_enumerator_new (modules, 0, token, NULL);
+	uri_info = _gck_uri_info_new ();
+	uri_info->token_info = g_new0 (GckTokenInfo, 1);
+	uri_info->token_info->label = g_strdup ("Invalid token name");
+	en = _gck_enumerator_new (modules, 0, uri_info);
 	g_assert (GCK_IS_ENUMERATOR (en));
 
 	objects = gck_enumerator_next_n (en, -1, NULL, &error);
diff --git a/gck/tests/test-gck-uri.c b/gck/tests/test-gck-uri.c
index 03b8a2b..340d5db 100644
--- a/gck/tests/test-gck-uri.c
+++ b/gck/tests/test-gck-uri.c
@@ -1,3 +1,25 @@
+/* -*- Mode: C; indent-tabs-mode: t; c-basic-offset: 8; tab-width: 8 -*- */
+/* test-gck-uri.c: Test routines for PKCS#11 URIs
+
+   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,
+   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 <glib.h>
 #include <string.h>
@@ -19,135 +41,188 @@ TESTING_TEARDOWN(uri)
 TESTING_TEST(uri_parse)
 {
 	GError *error = NULL;
-	if (!gck_uri_parse ("pkcs11:", NULL, NULL, &error))
-		g_assert_not_reached ();
+	GckUriInfo *uri_info;
+
+	uri_info = gck_uri_parse ("pkcs11:", GCK_URI_PARSE_MODULE, &error);
+	g_assert (uri_info != NULL);
+	g_assert_no_error (error);
+
+	g_assert (uri_info->attributes == NULL);
+	g_assert (uri_info->token_info == NULL);
+
+	g_assert (uri_info->module_info != NULL);
+	g_assert (uri_info->module_info->library_description == NULL);
+	g_assert (uri_info->module_info->manufacturer_id == NULL);
+
+	gck_uri_info_free (uri_info);
 }
 
 TESTING_TEST(uri_parse_bad_scheme)
 {
 	GError *error = NULL;
-	if (gck_uri_parse ("http:\\example.com\test", NULL, NULL, &error))
-		g_assert_not_reached ();
-	g_assert (g_error_matches (error, GCK_URI_ERROR, GCK_URI_BAD_PREFIX));
+	GckUriInfo *uri_info;
+
+	uri_info = gck_uri_parse ("http:\\example.com\test", GCK_URI_PARSE_ANY, &error);
+	g_assert (uri_info == NULL);
+	g_assert_error (error, GCK_URI_ERROR, GCK_URI_BAD_PREFIX);
 	g_error_free (error);
 }
 
 TESTING_TEST(uri_parse_with_label)
 {
 	GError *error = NULL;
-	GckAttributes *attrs;
+	GckUriInfo *uri_info;
 	gchar *value;
 
-	if (!gck_uri_parse ("pkcs11:object=Test%20Label", NULL, &attrs, &error))
-		g_assert_not_reached ();
+	uri_info = gck_uri_parse ("pkcs11:object=Test%20Label", GCK_URI_PARSE_ANY, &error);
+	g_assert (uri_info != NULL);
+	g_assert (uri_info->attributes != NULL);
 
-	if (!gck_attributes_find_string (attrs, CKA_LABEL, &value))
+	if (!gck_attributes_find_string (uri_info->attributes, CKA_LABEL, &value))
 		g_assert_not_reached ();
 
 	g_assert_cmpstr (value, ==, "Test Label");
 	g_free (value);
+
+	gck_uri_info_free (uri_info);
 }
+
 TESTING_TEST(uri_parse_with_label_and_klass)
 {
 	GError *error = NULL;
-	GckAttributes *attrs;
+	GckUriInfo *uri_info;
 	gchar *value;
 	gulong klass;
 
-	if (!gck_uri_parse ("pkcs11:object=Test%20Label;objecttype=cert", NULL, &attrs, &error))
-		g_assert_not_reached ();
+	uri_info = gck_uri_parse ("pkcs11:object=Test%20Label;objecttype=cert", GCK_URI_PARSE_ANY, &error);
+	g_assert (uri_info);
+	g_assert (uri_info->attributes);
 
-	if (!gck_attributes_find_string (attrs, CKA_LABEL, &value))
+	if (!gck_attributes_find_string (uri_info->attributes, CKA_LABEL, &value))
 		g_assert_not_reached ();
 
-	if (!gck_attributes_find_ulong (attrs, CKA_CLASS, &klass))
+	if (!gck_attributes_find_ulong (uri_info->attributes, CKA_CLASS, &klass))
 		g_assert_not_reached ();
 
 	g_assert_cmpstr (value, ==, "Test Label");
 	g_assert (klass == CKO_CERTIFICATE);
 	g_free (value);
+
+	gck_uri_info_free (uri_info);
 }
 
 TESTING_TEST(uri_parse_with_id)
 {
 	GError *error = NULL;
-	GckAttributes *attrs;
 	GckAttribute *attr;
+	GckUriInfo *uri_info;
 
-	if (!gck_uri_parse ("pkcs11:id=54:45:53:54", NULL, &attrs, &error))
-		g_assert_not_reached ();
+	uri_info = gck_uri_parse ("pkcs11:id=54:45:53:54", GCK_URI_PARSE_OBJECT, &error);
+	g_assert (uri_info != NULL);
+	g_assert (uri_info->attributes != NULL);
 
-	attr = gck_attributes_find (attrs, CKA_ID);
+	attr = gck_attributes_find (uri_info->attributes, CKA_ID);
 	g_assert (attr);
 	g_assert (attr->value);
 	g_assert (attr->length == 4);
 	g_assert (memcmp (attr->value, "TEST", 4) == 0);
 
-	gck_attributes_unref (attrs);
+	gck_uri_info_free (uri_info);
 }
 
 TESTING_TEST(uri_parse_with_bad_string_encoding)
 {
 	GError *error = NULL;
-	if (gck_uri_parse ("pkcs11:object=Test%", NULL, NULL, &error))
-		g_assert_not_reached ();
-	g_assert (g_error_matches (error, GCK_URI_ERROR, GCK_URI_BAD_ENCODING));
+	GckUriInfo *uri_info;
+
+	uri_info = gck_uri_parse ("pkcs11:object=Test%", GCK_URI_PARSE_OBJECT, &error);
+	g_assert (uri_info == NULL);
+	g_assert_error (error, GCK_URI_ERROR, GCK_URI_BAD_ENCODING);
 	g_error_free (error);
 }
 
 TESTING_TEST(uri_parse_with_bad_binary_encoding)
 {
 	GError *error = NULL;
-	if (gck_uri_parse ("pkcs11:id=xxxxx", NULL, NULL, &error))
-		g_assert_not_reached ();
-	g_assert (g_error_matches (error, GCK_URI_ERROR, GCK_URI_BAD_ENCODING));
+	GckUriInfo *uri_info;
+	uri_info = gck_uri_parse ("pkcs11:id=xxxxx", GCK_URI_PARSE_ANY, &error);
+	g_assert (!uri_info);
+	g_assert_error (error, GCK_URI_ERROR, GCK_URI_BAD_ENCODING);
 	g_error_free (error);
 }
 
 TESTING_TEST(uri_parse_with_token)
 {
 	GError *error = NULL;
-	GckTokenInfo *token = NULL;
-
-	if (!gck_uri_parse ("pkcs11:token=Token%20Label;serial=3333;model=Deluxe;manufacturer=Me",
-	                    &token, NULL, &error))
-		g_assert_not_reached ();
-
-	g_assert (token);
-	g_assert_cmpstr (token->label, ==, "Token Label");
-	g_assert_cmpstr (token->serial_number, ==, "3333");
-	g_assert_cmpstr (token->model, ==, "Deluxe");
-	g_assert_cmpstr (token->manufacturer_id, ==, "Me");
-	gck_token_info_free (token);
+	GckUriInfo *uri_info = NULL;
+
+	uri_info = gck_uri_parse ("pkcs11:token=Token%20Label;serial=3333;model=Deluxe;manufacturer=Me",
+	                          GCK_URI_PARSE_TOKEN, &error);
+
+	g_assert (uri_info);
+	g_assert (uri_info->token_info);
+	g_assert_cmpstr (uri_info->token_info->label, ==, "Token Label");
+	g_assert_cmpstr (uri_info->token_info->serial_number, ==, "3333");
+	g_assert_cmpstr (uri_info->token_info->model, ==, "Deluxe");
+	g_assert_cmpstr (uri_info->token_info->manufacturer_id, ==, "Me");
+	gck_uri_info_free (uri_info);
 }
 
 TESTING_TEST(uri_parse_with_token_bad_encoding)
 {
 	GError *error = NULL;
+	GckUriInfo *uri_info;
 
-	if (gck_uri_parse ("pkcs11:token=Token%", NULL, NULL, &error))
-		g_assert_not_reached ();
-
-	g_assert (g_error_matches (error, GCK_URI_ERROR, GCK_URI_BAD_ENCODING));
+	uri_info = gck_uri_parse ("pkcs11:token=Token%", GCK_URI_PARSE_TOKEN, &error);
+	g_assert (!uri_info);
+	g_assert_error (error, GCK_URI_ERROR, GCK_URI_BAD_ENCODING);
 	g_error_free (error);
 }
 
 TESTING_TEST(uri_parse_with_bad_syntax)
 {
 	GError *error = NULL;
+	GckUriInfo *uri_info;
 
-	if (gck_uri_parse ("pkcs11:token", NULL, NULL, &error))
-		g_assert_not_reached ();
-
+	uri_info = gck_uri_parse ("pkcs11:token", GCK_URI_PARSE_ANY, &error);
+	g_assert (uri_info == NULL);
 	g_assert (g_error_matches (error, GCK_URI_ERROR, GCK_URI_BAD_SYNTAX));
 	g_error_free (error);
 }
 
+TESTING_TEST(uri_parse_with_library)
+{
+	GError *error = NULL;
+	GckUriInfo *uri_info = NULL;
+
+	uri_info = gck_uri_parse ("pkcs11:library-description=The%20Library;manufacturer=Me",
+	                          GCK_URI_PARSE_MODULE, &error);
+
+	g_assert (uri_info);
+	g_assert (uri_info->module_info);
+	g_assert_cmpstr (uri_info->module_info->manufacturer_id, ==, "Me");
+	g_assert_cmpstr (uri_info->module_info->library_description, ==, "The Library");
+	gck_uri_info_free (uri_info);
+}
+
+TESTING_TEST(uri_parse_with_library_bad_encoding)
+{
+	GError *error = NULL;
+	GckUriInfo *uri_info;
+
+	uri_info = gck_uri_parse ("pkcs11:library-description=Library%", GCK_URI_PARSE_MODULE, &error);
+	g_assert (!uri_info);
+	g_assert_error (error, GCK_URI_ERROR, GCK_URI_BAD_ENCODING);
+	g_error_free (error);
+}
+
 TESTING_TEST(uri_build_empty)
 {
-	gchar *uri = NULL;
+	GckUriInfo uri_info;
+	gchar *uri;
 
-	uri = gck_uri_build (NULL, NULL);
+	memset (&uri_info, 0, sizeof (uri_info));
+	uri = gck_uri_build (&uri_info);
 	g_assert_cmpstr (uri, ==, "pkcs11:");
 	g_free (uri);
 }
@@ -155,25 +230,27 @@ TESTING_TEST(uri_build_empty)
 TESTING_TEST(uri_build_with_token_info)
 {
 	gchar *uri = NULL;
-	GckTokenInfo *token;
-	GckTokenInfo *check;
+	GckUriInfo uri_info;
+	GckUriInfo *check;
 
-	token = g_new0 (GckTokenInfo, 1);
-	token->label = g_strdup ("The Label");
-	token->serial_number = g_strdup ("44444");
-	token->manufacturer_id = g_strdup ("Me");
-	token->model = g_strdup ("Deluxe");
+	memset (&uri_info, 0, sizeof (uri_info));
+	uri_info.token_info = g_new0 (GckTokenInfo, 1);
+	uri_info.token_info->label = g_strdup ("The Label");
+	uri_info.token_info->serial_number = g_strdup ("44444");
+	uri_info.token_info->manufacturer_id = g_strdup ("Me");
+	uri_info.token_info->model = g_strdup ("Deluxe");
 
-	uri = gck_uri_build (token, NULL);
+	uri = gck_uri_build (&uri_info);
 	g_assert (uri);
 
-	if (!gck_uri_parse (uri, &check, NULL, NULL))
-		g_assert_not_reached ();
+	check = gck_uri_parse (uri, GCK_URI_PARSE_TOKEN, NULL);
+	g_assert (check);
+	g_assert (check->token_info);
 
-	g_assert (_gck_token_info_match (token, check));
+	g_assert (_gck_token_info_match (uri_info.token_info, check->token_info));
 
-	gck_token_info_free (check);
-	gck_token_info_free (token);
+	gck_token_info_free (uri_info.token_info);
+	gck_uri_info_free (check);
 
 	g_assert (g_str_has_prefix (uri, "pkcs11:"));
 	g_assert (strstr (uri, "token=The%20Label"));
@@ -184,41 +261,85 @@ TESTING_TEST(uri_build_with_token_info)
 	g_free (uri);
 }
 
+TESTING_TEST(uri_build_with_token_null_info)
+{
+	gchar *uri = NULL;
+	GckUriInfo uri_info;
+
+	memset (&uri_info, 0, sizeof (uri_info));
+	uri_info.token_info = g_new0 (GckTokenInfo, 1);
+	uri_info.token_info->label = g_strdup ("The Label");
+
+	uri = gck_uri_build (&uri_info);
+	g_assert (uri);
+
+	g_assert (g_str_has_prefix (uri, "pkcs11:"));
+	g_assert (strstr (uri, "token=The%20Label"));
+	g_assert (!strstr (uri, "serial="));
+
+	gck_token_info_free (uri_info.token_info);
+	g_free (uri);
+}
+
+TESTING_TEST(uri_build_with_token_empty_info)
+{
+	gchar *uri = NULL;
+	GckUriInfo uri_info;
+
+	memset (&uri_info, 0, sizeof (uri_info));
+	uri_info.token_info = g_new0 (GckTokenInfo, 1);
+	uri_info.token_info->label = g_strdup ("The Label");
+	uri_info.token_info->serial_number = g_strdup ("");
+
+	uri = gck_uri_build (&uri_info);
+	g_assert (uri);
+
+	g_assert (g_str_has_prefix (uri, "pkcs11:"));
+	g_assert (strstr (uri, "token=The%20Label"));
+	g_assert (strstr (uri, "serial="));
+
+	gck_token_info_free (uri_info.token_info);
+	g_free (uri);
+}
+
 TESTING_TEST(uri_build_with_attributes)
 {
 	gchar *uri = NULL;
-	GckAttributes *attrs;
+	GckUriInfo uri_info;
+	GckUriInfo *check;
 	gchar *string;
 	gulong value;
 	GckAttribute *attr;
 
-	attrs = gck_attributes_new ();
-	gck_attributes_add_string (attrs, CKA_LABEL, "The Label");
-	gck_attributes_add_ulong (attrs, CKA_CLASS, CKO_DATA);
-	gck_attributes_add_data (attrs, CKA_ID, "TEST", 4);
+	memset (&uri_info, 0, sizeof (uri_info));
+	uri_info.attributes = gck_attributes_new ();
+	gck_attributes_add_string (uri_info.attributes, CKA_LABEL, "The Label");
+	gck_attributes_add_ulong (uri_info.attributes, CKA_CLASS, CKO_DATA);
+	gck_attributes_add_data (uri_info.attributes, CKA_ID, "TEST", 4);
 
-	uri = gck_uri_build (NULL, attrs);
+	uri = gck_uri_build (&uri_info);
 	g_assert (uri);
 
-	gck_attributes_unref (attrs);
+	gck_attributes_unref (uri_info.attributes);
 
-	if (!gck_uri_parse (uri, NULL, &attrs, NULL))
-		g_assert_not_reached ();
+	check = gck_uri_parse (uri, GCK_URI_PARSE_ANY, NULL);
+	g_assert (check);
+	g_assert (check->attributes);
 
-	if (!gck_attributes_find_string (attrs, CKA_LABEL, &string))
+	if (!gck_attributes_find_string (check->attributes, CKA_LABEL, &string))
 		g_assert_not_reached ();
 	g_assert_cmpstr (string, ==, "The Label");
 
-	if (!gck_attributes_find_ulong (attrs, CKA_CLASS, &value))
+	if (!gck_attributes_find_ulong (check->attributes, CKA_CLASS, &value))
 		g_assert_not_reached ();
 	g_assert (value == CKO_DATA);
 
-	attr = gck_attributes_find (attrs, CKA_ID);
+	attr = gck_attributes_find (check->attributes, CKA_ID);
 	g_assert (attr);
 	g_assert (attr->length == 4);
 	g_assert (memcmp (attr->value, "TEST", 4) == 0);
 
-	gck_attributes_unref (attrs);
+	gck_uri_info_free (check);
 
 	g_assert (g_str_has_prefix (uri, "pkcs11:"));
 	g_assert (strstr (uri, "object=The%20Label"));
@@ -227,3 +348,143 @@ TESTING_TEST(uri_build_with_attributes)
 
 	g_free (uri);
 }
+
+TESTING_TEST (uri_parse_private_key)
+{
+	GckUriInfo *uri_info;
+	GError *error = NULL;
+	gulong klass;
+
+	uri_info = gck_uri_parse ("pkcs11:objecttype=private", GCK_URI_PARSE_OBJECT, &error);
+	g_assert (uri_info);
+	g_assert_no_error (error);
+
+	g_assert (uri_info->attributes);
+	if (!gck_attributes_find_ulong (uri_info->attributes, CKA_CLASS, &klass))
+		g_assert_not_reached ();
+	gck_assert_cmpulong (klass, ==, CKO_PRIVATE_KEY);
+
+	gck_uri_info_free (uri_info);
+}
+
+TESTING_TEST (uri_parse_parse_secret_key)
+{
+	GckUriInfo *uri_info;
+	GError *error = NULL;
+	gulong klass;
+
+	uri_info = gck_uri_parse ("pkcs11:objecttype=secretkey", GCK_URI_PARSE_OBJECT, &error);
+	g_assert (uri_info);
+	g_assert_no_error (error);
+
+	g_assert (uri_info->attributes);
+	if (!gck_attributes_find_ulong (uri_info->attributes, CKA_CLASS, &klass))
+		g_assert_not_reached ();
+	gck_assert_cmpulong (klass, ==, CKO_SECRET_KEY);
+
+	gck_uri_info_free (uri_info);
+}
+
+
+TESTING_TEST (uri_parse_parse_unknown_objecttype)
+{
+	GckUriInfo *uri_info;
+	GError *error = NULL;
+	gulong klass;
+
+	uri_info = gck_uri_parse ("pkcs11:objecttype=unknown", GCK_URI_PARSE_OBJECT, &error);
+	g_assert (uri_info);
+	g_assert_no_error (error);
+
+	g_assert (uri_info->attributes);
+	g_assert (uri_info->any_unrecognized == TRUE);
+	if (gck_attributes_find_ulong (uri_info->attributes, CKA_CLASS, &klass))
+		g_assert_not_reached ();
+
+	gck_uri_info_free (uri_info);
+}
+
+TESTING_TEST (uri_build_objecttype_cert)
+{
+	GckUriInfo *uri_info;
+	gchar *uri;
+
+	uri_info = _gck_uri_info_new ();
+	uri_info->attributes = gck_attributes_new ();
+	gck_attributes_add_ulong (uri_info->attributes, CKA_CLASS, CKO_CERTIFICATE);
+
+	uri = gck_uri_build (uri_info);
+	g_assert (uri);
+	g_assert (strstr (uri, "objecttype=cert"));
+
+	gck_uri_info_free (uri_info);
+	g_free (uri);
+}
+
+TESTING_TEST (uri_build_objecttype_private)
+{
+	GckUriInfo *uri_info;
+	gchar *uri;
+
+	uri_info = _gck_uri_info_new ();
+	uri_info->attributes = gck_attributes_new ();
+	gck_attributes_add_ulong (uri_info->attributes, CKA_CLASS, CKO_PRIVATE_KEY);
+
+	uri = gck_uri_build (uri_info);
+	g_assert (uri);
+	g_assert (strstr (uri, "objecttype=private"));
+
+	gck_uri_info_free (uri_info);
+	g_free (uri);
+}
+
+TESTING_TEST (uri_build_objecttype_public)
+{
+	GckUriInfo *uri_info;
+	gchar *uri;
+
+	uri_info = _gck_uri_info_new ();
+	uri_info->attributes = gck_attributes_new ();
+	gck_attributes_add_ulong (uri_info->attributes, CKA_CLASS, CKO_PUBLIC_KEY);
+
+	uri = gck_uri_build (uri_info);
+	g_assert (uri);
+	g_assert (strstr (uri, "objecttype=public"));
+
+	gck_uri_info_free (uri_info);
+	g_free (uri);
+}
+
+TESTING_TEST (uri_build_objecttype_secret)
+{
+	GckUriInfo *uri_info;
+	gchar *uri;
+
+	uri_info = _gck_uri_info_new ();
+	uri_info->attributes = gck_attributes_new ();
+	gck_attributes_add_ulong (uri_info->attributes, CKA_CLASS, CKO_SECRET_KEY);
+
+	uri = gck_uri_build (uri_info);
+	g_assert (uri);
+	g_assert (strstr (uri, "objecttype=secretkey"));
+
+	gck_uri_info_free (uri_info);
+	g_free (uri);
+}
+
+TESTING_TEST (uri_build_with_library)
+{
+	GckUriInfo *uri_info;
+	gchar *uri;
+
+	uri_info = _gck_uri_info_new ();
+	uri_info->module_info = g_new0 (GckModuleInfo, 1);
+	uri_info->module_info->library_description = g_strdup ("The Description");
+
+	uri = gck_uri_build (uri_info);
+	g_assert (uri);
+	g_assert (strstr (uri, "library-description=The%20Description"));
+
+	gck_uri_info_free (uri_info);
+	g_free (uri);
+}
diff --git a/gck/tests/test-gck.h b/gck/tests/test-gck.h
new file mode 100644
index 0000000..e14d1c8
--- /dev/null
+++ b/gck/tests/test-gck.h
@@ -0,0 +1,43 @@
+/* -*- Mode: C; indent-tabs-mode: t; c-basic-offset: 8; tab-width: 8 -*- */
+/* gck-uri.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,
+   write to the Free Software Foundation, Inc., 59 Temple Place - Suite 330,
+   Boston, MA 02111-1307, USA.
+
+   Author: Stef Walter <stefw collabora co uk>
+*/
+
+#ifndef TEST_GCK_H_
+#define TEST_GCK_H_
+
+#include "gck.h"
+#include "gck-mock.h"
+#include "gck-test.h"
+
+#define FAIL_RES(res, e) do { \
+	g_assert ((res) ? FALSE : TRUE); \
+	g_assert ((e) && (e)->message && "error should be set"); \
+	g_clear_error (&e); \
+	} while (0)
+
+#define SUCCESS_RES(res, err) do { \
+	if (!(res)) g_printerr ("error: %s\n", err && err->message ? err->message : ""); \
+	g_assert ((res) ? TRUE : FALSE && "should have succeeded"); \
+	g_clear_error (&err); \
+	} while(0)
+
+#endif /* TEST_GCK_H_ */



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