summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorRichard Hughes <richard@hughsie.com>2015-11-15 10:34:44 +0000
committerRichard Hughes <richard@hughsie.com>2015-11-15 10:39:19 +0000
commit98ed5eba8d32907e7da36f29c266d3d77b28feff (patch)
treecc6ef45c0a37a5ea714c1be74d474644db660b65
parentbc645ec63525248a974a5c31cd6167cd77d34c4f (diff)
downloadappstream-glib-98ed5eba8d32907e7da36f29c266d3d77b28feff.tar.gz
Generate GUID values according to RFC4122
Also, add a 'generate-guid' command to appstream-util.
-rw-r--r--client/as-util.c27
-rw-r--r--configure.ac1
-rw-r--r--contrib/libappstream-glib.spec.in1
-rw-r--r--libappstream-glib/Makefile.am3
-rw-r--r--libappstream-glib/appstream-glib.pc.in2
-rw-r--r--libappstream-glib/as-self-test.c9
-rw-r--r--libappstream-glib/as-utils.c89
7 files changed, 87 insertions, 45 deletions
diff --git a/client/as-util.c b/client/as-util.c
index b156109..19f9c72 100644
--- a/client/as-util.c
+++ b/client/as-util.c
@@ -3436,6 +3436,27 @@ as_util_pad_strings (const gchar *id, const gchar *msg, guint align)
}
/**
+ * as_util_generate_guid:
+ **/
+static gboolean
+as_util_generate_guid (AsUtilPrivate *priv, gchar **values, GError **error)
+{
+ g_autofree gchar *guid = NULL;
+
+ /* check args */
+ if (g_strv_length (values) != 1) {
+ g_set_error_literal (error,
+ AS_ERROR,
+ AS_ERROR_INVALID_ARGUMENTS,
+ "Not enough arguments, expected STRING");
+ return FALSE;
+ }
+ guid = as_utils_guid_from_string (values[0]);
+ g_print ("%s\n", guid);
+ return TRUE;
+}
+
+/**
* as_util_compare:
**/
static gboolean
@@ -3884,6 +3905,12 @@ main (int argc, char *argv[])
/* TRANSLATORS: command description */
_("Compare the contents of two AppStream files"),
as_util_compare);
+ as_util_add (priv->cmd_array,
+ "generate-guid",
+ NULL,
+ /* TRANSLATORS: command description */
+ _("Generate a GUID from an input string"),
+ as_util_generate_guid);
/* sort by command name */
g_ptr_array_sort (priv->cmd_array,
diff --git a/configure.ac b/configure.ac
index 9cecfd2..0385b2c 100644
--- a/configure.ac
+++ b/configure.ac
@@ -128,6 +128,7 @@ fi
AM_CONDITIONAL(HAVE_GPERF, [test x$GPERF != xno])
PKG_CHECK_MODULES(GLIB, glib-2.0 >= 2.45.8 gio-2.0 gobject-2.0 gthread-2.0 gio-unix-2.0 gmodule-2.0)
+PKG_CHECK_MODULES(UUID, uuid)
PKG_CHECK_MODULES(LIBARCHIVE, libarchive)
PKG_CHECK_MODULES(SOUP, libsoup-2.4 >= 2.51.92)
PKG_CHECK_MODULES(GDKPIXBUF, gdk-pixbuf-2.0 >= 2.31.5)
diff --git a/contrib/libappstream-glib.spec.in b/contrib/libappstream-glib.spec.in
index b7e7ecd..6ce5b13 100644
--- a/contrib/libappstream-glib.spec.in
+++ b/contrib/libappstream-glib.spec.in
@@ -21,6 +21,7 @@ BuildRequires: gtk3-devel
BuildRequires: gettext
BuildRequires: intltool
BuildRequires: libgcab1-devel
+BuildRequires: libuuid-devel
# for the builder component
BuildRequires: fontconfig-devel
diff --git a/libappstream-glib/Makefile.am b/libappstream-glib/Makefile.am
index 4c9a33e..a0bf163 100644
--- a/libappstream-glib/Makefile.am
+++ b/libappstream-glib/Makefile.am
@@ -11,6 +11,7 @@ AM_CPPFLAGS = \
$(GDKPIXBUF_CFLAGS) \
$(LIBARCHIVE_CFLAGS) \
$(SOUP_CFLAGS) \
+ $(UUID_CFLAGS) \
$(YAML_CFLAGS) \
-I$(top_srcdir)/libappstream-glib \
-I$(top_builddir)/libappstream-glib \
@@ -148,6 +149,7 @@ libappstream_glib_la_LIBADD = \
$(GDKPIXBUF_LIBS) \
$(LIBARCHIVE_LIBS) \
$(SOUP_LIBS) \
+ $(UUID_LIBS) \
$(YAML_LIBS)
libappstream_glib_la_LDFLAGS = \
@@ -168,6 +170,7 @@ as_self_test_LDADD = \
$(GDKPIXBUF_LIBS) \
$(LIBARCHIVE_LIBS) \
$(SOUP_LIBS) \
+ $(UUID_LIBS) \
$(YAML_LIBS) \
$(lib_LTLIBRARIES)
as_self_test_CFLAGS = $(WARNINGFLAGS_C)
diff --git a/libappstream-glib/appstream-glib.pc.in b/libappstream-glib/appstream-glib.pc.in
index caf82ab..ed7ee3b 100644
--- a/libappstream-glib/appstream-glib.pc.in
+++ b/libappstream-glib/appstream-glib.pc.in
@@ -6,7 +6,7 @@ includedir=@includedir@
Name: appstream-glib
Description: Objects and helper methods to help reading and writing AppStream metadata
Version: @VERSION@
-Requires.private: libarchive, libgcab-1.0
+Requires.private: libarchive, libgcab-1.0, libuuid
Requires: glib-2.0, gobject-2.0, gdk-pixbuf-2.0
Libs: -L${libdir} -lappstream-glib
Cflags: -I${includedir}/libappstream-glib
diff --git a/libappstream-glib/as-self-test.c b/libappstream-glib/as-self-test.c
index 2df61be..5a2a14d 100644
--- a/libappstream-glib/as-self-test.c
+++ b/libappstream-glib/as-self-test.c
@@ -3336,7 +3336,8 @@ as_test_store_speed_desktop_func (void)
static void
as_test_utils_guid_func (void)
{
- g_autofree gchar *guid = NULL;
+ g_autofree gchar *guid1 = NULL;
+ g_autofree gchar *guid2 = NULL;
/* invalid */
g_assert (!as_utils_guid_is_valid (NULL));
@@ -3349,8 +3350,10 @@ as_test_utils_guid_func (void)
g_assert (as_utils_guid_is_valid ("1ff60ab2-3905-06a1-b476-0371f00c9e9b"));
/* make valid */
- guid = as_utils_guid_from_string ("0x8086:0x0406");
- g_assert_cmpstr (guid, ==, "1ff60ab2-3905-06a1-b476-0371f00c9e9b");
+ guid1 = as_utils_guid_from_string ("python.org");
+ g_assert_cmpstr (guid1, ==, "886313e1-3b8a-5372-9b90-0c9aee199e5d");
+ guid2 = as_utils_guid_from_string ("8086:0406");
+ g_assert_cmpstr (guid2, ==, "1fbd1f2c-80f4-5d7c-a6ad-35c7b9bd5486");
}
static void
diff --git a/libappstream-glib/as-utils.c b/libappstream-glib/as-utils.c
index 730b08e..2ed13cf 100644
--- a/libappstream-glib/as-utils.c
+++ b/libappstream-glib/as-utils.c
@@ -38,6 +38,7 @@
#include <archive.h>
#include <libsoup/soup.h>
#include <stdlib.h>
+#include <uuid.h>
#include "as-app.h"
#include "as-enums.h"
@@ -1592,20 +1593,6 @@ as_ptr_array_find_string (GPtrArray *array, const gchar *value)
}
/**
- * as_utils_guid_is_xdigit:
- **/
-static gboolean
-as_utils_guid_is_xdigit (const gchar *str)
-{
- guint i;
- for (i = 0; str[i] != '\0'; i++) {
- if (!g_ascii_isxdigit (str[i]))
- return FALSE;
- }
- return TRUE;
-}
-
-/**
* as_utils_guid_is_valid:
* @guid: string to check
*
@@ -1618,32 +1605,28 @@ as_utils_guid_is_xdigit (const gchar *str)
gboolean
as_utils_guid_is_valid (const gchar *guid)
{
- g_auto(GStrv) split = NULL;
+ gint rc;
+ uuid_t uu;
if (guid == NULL)
return FALSE;
- split = g_strsplit (guid, "-", -1);
- if (g_strv_length (split) != 5)
- return FALSE;
- if (strlen (split[0]) != 8 || !as_utils_guid_is_xdigit (split[0]))
- return FALSE;
- if (strlen (split[1]) != 4 || !as_utils_guid_is_xdigit (split[1]))
- return FALSE;
- if (strlen (split[2]) != 4 || !as_utils_guid_is_xdigit (split[2]))
- return FALSE;
- if (strlen (split[3]) != 4 || !as_utils_guid_is_xdigit (split[3]))
- return FALSE;
- if (strlen (split[4]) != 12 || !as_utils_guid_is_xdigit (split[4]))
- return FALSE;
- return TRUE;
+ rc = uuid_parse (guid, uu);
+ return rc == 0;
}
/**
* as_utils_guid_from_string:
* @str: A source string to use as a key
*
- * Returns a GUID for a given string. This uses SHA1 and some string
- * modification so even small differences in the @str will produce radically
- * different GUID return values.
+ * Returns a GUID for a given string. This uses a hash and so even small
+ * differences in the @str will produce radically different return values.
+ *
+ * The implementation is taken from RFC4122, Section 4.1.3; specifically
+ * using a type-5 SHA-1 hash with a DNS namespace.
+ * The same result can be obtained with this simple python program:
+ *
+ * #!/usr/bin/python
+ * import uuid
+ * print uuid.uuid5(uuid.NAMESPACE_DNS, 'python.org')
*
* Returns: A new GUID, or %NULL if the string was invalid
*
@@ -1652,15 +1635,39 @@ as_utils_guid_is_valid (const gchar *guid)
gchar *
as_utils_guid_from_string (const gchar *str)
{
- gchar *tmp;
- tmp = g_compute_checksum_for_string (G_CHECKSUM_SHA1, str, -1);
- tmp[8] = '-';
- tmp[13] = '-';
- tmp[18] = '-';
- tmp[23] = '-';
- tmp[36] = '\0';
- g_assert (as_utils_guid_is_valid (tmp));
- return tmp;
+ const gchar *namespace_id = "6ba7b810-9dad-11d1-80b4-00c04fd430c8";
+ gchar guid_new[37]; /* 36 plus NUL */
+ gsize digestlen = 20;
+ guint8 hash[20];
+ gint rc;
+ uuid_t uu_namespace;
+ uuid_t uu_new;
+ g_autoptr(GChecksum) csum = NULL;
+
+ /* invalid */
+ if (str == NULL)
+ return NULL;
+
+ /* convert the namespace to binary */
+ rc = uuid_parse (namespace_id, uu_namespace);
+ g_assert (rc == 0);
+
+ /* hash the namespace and then the string */
+ csum = g_checksum_new (G_CHECKSUM_SHA1);
+ g_checksum_update (csum, (guchar *) uu_namespace, 16);
+ g_checksum_update (csum, (guchar *) str, strlen(str));
+ g_checksum_get_digest (csum, hash, &digestlen);
+
+ /* copy most parts of the hash 1:1 */
+ memcpy(uu_new, hash, 16);
+
+ /* set specific bits according to Section 4.1.3 */
+ uu_new[6] = (guint8) ((uu_new[6] & 0x0f) | (5 << 4));
+ uu_new[8] = (guint8) ((uu_new[8] & 0x3f) | 0x80);
+
+ /* return as a string */
+ uuid_unparse (uu_new, guid_new);
+ return g_strdup (guid_new);
}
/**