<commit_before>/* -*- Mode: C; indent-tabs-mode: t; c-basic-offset: 8; tab-width: 8 -*- */
/* gck-uri.c - the GObject PKCS#11 wrapper library

   Copyright (C) 2010, <NAME>

   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: <NAME> <<EMAIL>>
*/

#include "config.h"

#include "gck.h"
#include "gck-private.h"
#include "gck-marshal.h"

#include <glib/gi18n-lib.h>

#include <string.h>

#include "egg/egg-hex.h"

/**
 * SECTION:gck-uri
 * @title: PKCS11 URIs
 * @short_description: Parsing and building PKCS\#11 URIs.
 *
 * <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:"
#define N_URI_PREFIX 7

GQuark
gck_uri_get_error_quark (void)
{
	static GQuark domain = 0;
	static volatile gsize quark_inited = 0;

	if (g_once_init_enter (&quark_inited)) {
		domain = g_quark_from_static_string ("gck-uri-error");
		g_once_init_leave (&quark_inited, 1);
	}

	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)
{
	gchar *value;
	gint res = 0;

	g_assert (name);
	g_assert (start);
	g_assert (end);

	if (!g_str_equal (name, "object") && !g_str_equal (name, "objecttype"))
		return 0;

	value = g_uri_unescape_segment (start, end, "");
	if (value == 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;
	}

	if (g_str_equal (name, "object")) {
		gck_attributes_add_string (attrs, CKA_LABEL, value);
		res = 1;

	} else if (g_str_equal (name, "objecttype")) {

		res = 1;
		if (g_str_equal (value, "cert"))
			gck_attributes_add_ulong (attrs, CKA_CLASS, CKO_CERTIFICATE);
		else if (g_str_equal (value, "public"))
			gck_attributes_add_ulong (attrs, CKA_CLASS, CKO_PUBLIC_KEY);
		else if (g_str_equal (value, "private"))
			gck_attributes_add_ulong (attrs, CKA_CLASS, CKO_PRIVATE_KEY);
		else if (g_str_equal (value, "secretkey"))
			gck_attributes_add_ulong (attrs, CKA_CLASS, CKO_SECRET_KEY);
		else if (g_str_equal (value, "data"))
			gck_attributes_add_ulong (attrs, CKA_CLASS, CKO_DATA);
		else {
			g_message ("ignoring unsupported value for '%s'", value);
			res = 0;
		}
	} else {
		g_assert_not_reached ();
	}

	g_free (value);
	return res;
}

static gint
parse_binary_attribute (const gchar *name, const gchar *start, const gchar *end,
                        GckAttributes *attrs, GError **error)
{
	guchar *data;
	gsize n_data;

	g_assert (name);
	g_assert (start);
	g_assert (end);
	g_assert (attrs);

	if (!g_str_equal (name, "id"))
		return 0;

	/*
	 * TODO: This requires some work. We're not yet sure about the actual
	 * encoding that's supported here.
	 */

	g_assert (end >= start);
	data = egg_hex_decode_full (start, end - start, ':', 1, &n_data);
	if (data == 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;
	}

	gck_attributes_add_data (attrs, CKA_ID, data, n_data);
	g_free (data);
	return 1;
}

static gint
parse_token_attribute (const gchar *name, const gchar *start, const gchar *end,
                       GckTokenInfo *token, GError **error)
{
	gchar **value;
	gchar *string;

	g_assert (name);
	g_assert (start);
	g_assert (end);
	g_assert (token);

	if (g_str_equal (name, "model"))
		value = &(token->model);
	else if (g_str_equal (name, "manufacturer"))
		value = &(token->manufacturer_id);
	else if (g_str_equal (name, "serial"))
		value = &(token->serial_number);
	else if (g_str_equal (name, "token"))
		value = &(token->label);
	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;
}

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, "library-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)
{
	const gchar *spos, *epos;
	gchar *key = NULL;
	gboolean ret = FALSE;
	GckUriInfo *uri_info = NULL;
	gint res;

	g_return_val_if_fail (uri, FALSE);
	g_return_val_if_fail (!error || !*error, FALSE);

	if (!g_str_has_prefix (uri, URI_PREFIX)) {
		g_set_error_literal (error, GCK_URI_ERROR, GCK_URI_BAD_PREFIX,
		                     _("The URI has does not have the 'pkcs11' scheme."));
		goto cleanup;
	}

	uri += N_URI_PREFIX;

	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, ';');
		if (spos == NULL) {
			spos = uri + strlen (uri);
			g_assert (*spos == '\0');
			if (spos == uri)
				break;
		}

		epos = strchr (uri, '=');
		if (epos == NULL || spos == uri || epos == uri || epos >= spos) {<commit_msg>Fix gck_uri_parse error message<commit_after>/* -*- Mode: C; indent-tabs-mode: t; c-basic-offset: 8; tab-width: 8 -*- */
/* gck-uri.c - the GObject PKCS#11 wrapper library

   Copyright (C) 2010, <NAME>

   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: <NAME> <<EMAIL>>
*/

#include "config.h"

#include "gck.h"
#include "gck-private.h"
#include "gck-marshal.h"

#include <glib/gi18n-lib.h>

#include <string.h>

#include "egg/egg-hex.h"

/**
 * SECTION:gck-uri
 * @title: PKCS11 URIs
 * @short_description: Parsing and building PKCS\#11 URIs.
 *
 * <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:"
#define N_URI_PREFIX 7

GQuark
gck_uri_get_error_quark (void)
{
	static GQuark domain = 0;
	static volatile gsize quark_inited = 0;

	if (g_once_init_enter (&quark_inited)) {
		domain = g_quark_from_static_string ("gck-uri-error");
		g_once_init_leave (&quark_inited, 1);
	}

	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)
{
	gchar *value;
	gint res = 0;

	g_assert (name);
	g_assert (start);
	g_assert (end);

	if (!g_str_equal (name, "object") && !g_str_equal (name, "objecttype"))
		return 0;

	value = g_uri_unescape_segment (start, end, "");
	if (value == 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;
	}

	if (g_str_equal (name, "object")) {
		gck_attributes_add_string (attrs, CKA_LABEL, value);
		res = 1;

	} else if (g_str_equal (name, "objecttype")) {

		res = 1;
		if (g_str_equal (value, "cert"))
			gck_attributes_add_ulong (attrs, CKA_CLASS, CKO_CERTIFICATE);
		else if (g_str_equal (value, "public"))
			gck_attributes_add_ulong (attrs, CKA_CLASS, CKO_PUBLIC_KEY);
		else if (g_str_equal (value, "private"))
			gck_attributes_add_ulong (attrs, CKA_CLASS, CKO_PRIVATE_KEY);
		else if (g_str_equal (value, "secretkey"))
			gck_attributes_add_ulong (attrs, CKA_CLASS, CKO_SECRET_KEY);
		else if (g_str_equal (value, "data"))
			gck_attributes_add_ulong (attrs, CKA_CLASS, CKO_DATA);
		else {
			g_message ("ignoring unsupported value for '%s'", value);
			res = 0;
		}
	} else {
		g_assert_not_reached ();
	}

	g_free (value);
	return res;
}

static gint
parse_binary_attribute (const gchar *name, const gchar *start, const gchar *end,
                        GckAttributes *attrs, GError **error)
{
	guchar *data;
	gsize n_data;

	g_assert (name);
	g_assert (start);
	g_assert (end);
	g_assert (attrs);

	if (!g_str_equal (name, "id"))
		return 0;

	/*
	 * TODO: This requires some work. We're not yet sure about the actual
	 * encoding that's supported here.
	 */

	g_assert (end >= start);
	data = egg_hex_decode_full (start, end - start, ':', 1, &n_data);
	if (data == 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;
	}

	gck_attributes_add_data (attrs, CKA_ID, data, n_data);
	g_free (data);
	return 1;
}

static gint
parse_token_attribute (const gchar *name, const gchar *start, const gchar *end,
                       GckTokenInfo *token, GError **error)
{
	gchar **value;
	gchar *string;

	g_assert (name);
	g_assert (start);
	g_assert (end);
	g_assert (token);

	if (g_str_equal (name, "model"))
		value = &(token->model);
	else if (g_str_equal (name, "manufacturer"))
		value = &(token->manufacturer_id);
	else if (g_str_equal (name, "serial"))
		value = &(token->serial_number);
	else if (g_str_equal (name, "token"))
		value = &(token->label);
	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;
}

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, "library-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)
{
	const gchar *spos, *epos;
	gchar *key = NULL;
	gboolean ret = FALSE;
	GckUriInfo *uri_info = NULL;
	gint res;

	g_return_val_if_fail (uri, FALSE);
	g_return_val_if_fail (!error || !*error, FALSE);

	if (!g_str_has_prefix (uri, URI_PREFIX)) {
		g_set_error_literal (error, GCK_URI_ERROR, GCK_URI_BAD_PREFIX,
		                     _("The URI does not have the 'pkcs11' scheme."));
		goto cleanup;
	}

	uri += N_URI_PREFIX;

	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, ';');
		if (spos == NULL) {
			spos = uri + strlen (uri);
			g_assert (*spos == '\0');
			if (spos == uri)
				break;
		}

		epos = strchr (uri, '=');
		if (epos == NULL || spos == uri || epos == uri || epos >= spos) {
{'fraction_non_alphanumeric': 0.11471623405191377, 'fraction_numerical': 0.010448746150461945, 'mean_word_length': 3.784267298079453, 'pattern_counts': {'":': 0, '<': 19, '<?xml version=': 0, '>': 41, 'https://': 0, 'lorem ipsum': 0, 'www.': 0, 'xml': 0}, 'pii_count': 0, 'substrings_counts': 0, 'word_list_counts': {'cursed_substrings.json': 0, 'profanity_word_list.json': 0, 'sexual_word_list.json': 0, 'zh_pornsignals.json': 0}}
{'dir': 'git-commits-cleaned', 'id': '7097471', 'n_tokens_mistral': 6800, 'n_tokens_neox': 6351, 'n_words': 2112}