/* guuid.c - UUID functions
 *
 * Copyright (C) 2013-2015, 2017 Red Hat, Inc.
 *
 * This library is free software; you can redistribute it and/or modify
 * it under the terms of the GNU Lesser General Public License as
 * published by the Free Software Foundation; either version 2.1 of the
 * licence, or (at your option) any later version.
 *
 * This 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 Lesser General Public
 * License for more details.
 *
 * You should have received a copy of the GNU Lesser General Public
 * License along with this library; if not, write to the Free Software
 * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301
 * USA.
 *
 * Authors: Marc-André Lureau <marcandre.lureau@redhat.com>
 */

#include "config.h"
#include <string.h>

#include "gi18n.h"
#include "gstrfuncs.h"
#include "grand.h"
#include "gmessages.h"
#include "gchecksum.h"

#include "guuid.h"

typedef struct {
  guint8 bytes[16];
} GUuid;

/*
 * g_uuid_to_string:
 * @uuid: a #GUuid
 *
 * Creates a string representation of @uuid, of the form
 * 06e023d5-86d8-420e-8103-383e4566087a (no braces nor urn:uuid:
 * prefix).
 *
 * Returns: (transfer full): A string that should be freed with g_free().
 * Since: STATIC
 */
static gchar *
g_uuid_to_string (const GUuid *uuid)
{
  const guint8 *bytes;

  g_return_val_if_fail (uuid != NULL, NULL);

  bytes = uuid->bytes;

  return g_strdup_printf ("%02x%02x%02x%02x-%02x%02x-%02x%02x-%02x%02x"
                          "-%02x%02x%02x%02x%02x%02x",
                          bytes[0], bytes[1], bytes[2], bytes[3],
                          bytes[4], bytes[5], bytes[6], bytes[7],
                          bytes[8], bytes[9], bytes[10], bytes[11],
                          bytes[12], bytes[13], bytes[14], bytes[15]);
}

static gboolean
uuid_parse_string (const gchar *str,
                   GUuid       *uuid)
{
  GUuid tmp;
  guint8 *bytes = tmp.bytes;
  gint i, j, hi, lo;
  guint expected_len = 36;

  if (strlen (str) != expected_len)
    return FALSE;

  for (i = 0, j = 0; i < 16;)
    {
      if (j == 8 || j == 13 || j == 18 || j == 23)
        {
          if (str[j++] != '-')
            return FALSE;

          continue;
        }

      hi = g_ascii_xdigit_value (str[j++]);
      lo = g_ascii_xdigit_value (str[j++]);

      if (hi == -1 || lo == -1)
        return FALSE;

      bytes[i++] = hi << 4 | lo;
    }

  if (uuid != NULL)
    *uuid = tmp;

  return TRUE;
}

/**
 * g_uuid_string_is_valid:
 * @str: a string representing a UUID
 *
 * Parses the string @str and verify if it is a UUID.
 *
 * The function accepts the following syntax:
 *
 * - simple forms (e.g. `f81d4fae-7dec-11d0-a765-00a0c91e6bf6`)
 *
 * Note that hyphens are required within the UUID string itself,
 * as per the aforementioned RFC.
 *
 * Returns: %TRUE if @str is a valid UUID, %FALSE otherwise.
 * Since: 2.52
 */
gboolean
g_uuid_string_is_valid (const gchar *str)
{
  g_return_val_if_fail (str != NULL, FALSE);

  return uuid_parse_string (str, NULL);
}

static void
uuid_set_version (GUuid *uuid, guint version)
{
  guint8 *bytes = uuid->bytes;

  /*
   * Set the four most significant bits (bits 12 through 15) of the
   * time_hi_and_version field to the 4-bit version number from
   * Section 4.1.3.
   */
  bytes[6] &= 0x0f;
  bytes[6] |= version << 4;
  /*
   * Set the two most significant bits (bits 6 and 7) of the
   * clock_seq_hi_and_reserved to zero and one, respectively.
   */
  bytes[8] &= 0x3f;
  bytes[8] |= 0x80;
}

/*
 * g_uuid_generate_v4:
 * @uuid: a #GUuid
 *
 * Generates a random UUID (RFC 4122 version 4).
 * Since: STATIC
 */
static void
g_uuid_generate_v4 (GUuid *uuid)
{
  int i;
  guint8 *bytes;
  guint32 *ints;

  g_return_if_fail (uuid != NULL);

  bytes = uuid->bytes;
  ints = (guint32 *) bytes;
  for (i = 0; i < 4; i++)
    ints[i] = g_random_int ();

  uuid_set_version (uuid, 4);
}

/**
 * g_uuid_string_random:
 *
 * Generates a random UUID (RFC 4122 version 4) as a string. It has the same
 * randomness guarantees as #GRand, so must not be used for cryptographic
 * purposes such as key generation, nonces, salts or one-time pads.
 *
 * Returns: (transfer full): A string that should be freed with g_free().
 * Since: 2.52
 */
gchar *
g_uuid_string_random (void)
{
  GUuid uuid;

  g_uuid_generate_v4 (&uuid);

  return g_uuid_to_string (&uuid);
}
