/*
* wocky-utils.c - Code for Wocky utility functions
* Copyright © 2007–2010 Collabora Ltd.
*
* 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 License, or (at your option) any later version.
*
* This 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
* 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
*
* wocky_g_value_slice_* functions have been copied from telepathy-glib's
* util.c file:
* Copyright (C) 2006-2007 Collabora Ltd.
* Copyright (C) 2006-2007 Nokia Corporation
*/
#include "wocky-utils.h"
#include
#include
/**
* wocky_strdiff:
* @left: The first string to compare (may be NULL)
* @right: The second string to compare (may be NULL)
*
* Return %TRUE if the given strings are different. Unlike #strcmp this
* function will handle null pointers, treating them as distinct from any
* string.
*
* Returns: %FALSE if @left and @right are both %NULL, or if
* neither is %NULL and both have the same contents; %TRUE otherwise
*/
gboolean
wocky_strdiff (const gchar *left,
const gchar *right)
{
return g_strcmp0 (left, right) != 0;
}
static gboolean
validate_jid_node (const gchar *node)
{
/* See RFC 3920 §3.3. */
const gchar *c;
for (c = node; *c; c++)
if (strchr ("\"&'/:<>@", *c))
/* RFC 3920 §A.5 */
return FALSE;
return TRUE;
}
static gboolean
validate_jid_domain (const gchar *domain)
{
/* XXX: This doesn't do proper validation: it checks the character
* range for ASCII characters, but lets through any non-ASCII characters. See
* the ifdef-d out tests in wocky-jid-validation-test.c for examples of
* erroneously accepted JIDs. In theory, we check that the domain is a
* well-formed IDN or an IPv4/IPv6 address literal.
*
* See RFC 3920 §3.2.
*/
const gchar *c;
for (c = domain; *c; c++)
{
if ((unsigned char) *c >= 0x7F)
continue;
if (!g_ascii_isalnum (*c) && !strchr (":-.", *c))
return FALSE;
}
return TRUE;
}
/**
* wocky_decode_jid:
* @jid: a JID
* @node: address to which return the username/room part of the JID
* @domain: address to which return the server/service part of the JID
* @resource: address to which return the resource/nick part of the JID
*
* If the JID is valid, returns TRUE and sets the caller's
* node/domain/resource pointers if they are not NULL. The node and resource
* pointers will be set to NULL if the respective part is not present in the
* JID. The node and domain are lower-cased because the Jabber protocol treats
* them case-insensitively.
*
* XXX: Do nodeprep/resourceprep and length checking.
*
* See RFC 3920 §3.
*
* Returns: %TRUE if the JID is valid
*/
gboolean
wocky_decode_jid (const gchar *jid,
gchar **node,
gchar **domain,
gchar **resource)
{
char *tmp_jid, *tmp_node, *tmp_domain, *tmp_resource;
g_assert (jid != NULL);
if (node != NULL)
*node = NULL;
if (domain != NULL)
*domain = NULL;
if (resource != NULL)
*resource = NULL;
/* Take a local copy so we don't modify the caller's string. */
tmp_jid = g_strdup (jid);
/* If there's a slash in tmp_jid, split it in two and take the second part as
* the resource.
*/
tmp_resource = strchr (tmp_jid, '/');
if (tmp_resource != NULL)
{
*tmp_resource = '\0';
tmp_resource++;
}
else
{
tmp_resource = NULL;
}
/* If there's an at sign in tmp_jid, split it in two and set tmp_node and
* tmp_domain appropriately. Otherwise, tmp_node is NULL and the domain is
* the whole string.
*/
tmp_domain = strchr (tmp_jid, '@');
if (tmp_domain != NULL)
{
*tmp_domain = '\0';
tmp_domain++;
tmp_node = tmp_jid;
}
else
{
tmp_domain = tmp_jid;
tmp_node = NULL;
}
/* Domain must be non-empty and not contain invalid characters. If the node
* or the resource exist, they must be non-empty and the node must not
* contain invalid characters.
*/
if (*tmp_domain == '\0' ||
!validate_jid_domain (tmp_domain) ||
(tmp_node != NULL &&
(*tmp_node == '\0' || !validate_jid_node (tmp_node))) ||
(tmp_resource != NULL && *tmp_resource == '\0'))
{
g_free (tmp_jid);
return FALSE;
}
/* the server must be stored after we find the resource, in case we
* truncated a resource from it */
if (domain != NULL)
*domain = g_utf8_strdown (tmp_domain, -1);
/* store the username if the user provided a pointer */
if (tmp_node != NULL && node != NULL)
*node = g_utf8_strdown (tmp_node, -1);
/* store the resource if the user provided a pointer */
if (tmp_resource != NULL && resource != NULL)
*resource = g_strdup (tmp_resource);
/* free our working copy */
g_free (tmp_jid);
return TRUE;
}
/**
* wocky_normalise_jid:
* @jid: a JID
*
* Returns: a normalised JID, using the same rules as wocky_decode_jid(),
* or %NULL if the JID could not be sensibly decoded.
* This value should be freed when you are done with it.
*/
gchar *
wocky_normalise_jid (const gchar *jid)
{
gchar *node = NULL;
gchar *domain = NULL;
gchar *resource = NULL;
gchar *rval = NULL;
if (jid == NULL)
return NULL;
if (!wocky_decode_jid (jid, &node, &domain, &resource))
return NULL;
rval = wocky_compose_jid (node, domain, resource);
g_free (node);
g_free (domain);
g_free (resource);
return rval;
}
static inline gsize
strlen0 (const gchar *s)
{
return (s == NULL ? 0 : strlen (s));
}
/**
* wocky_compose_jid:
* @node: the node part of a JID, possibly empty or %NULL
* @domain: the non-%NULL domain part of a JID
* @resource: the resource part of a JID, possibly empty or %NULL
*
* Composes a JID from its parts. If @node is empty or %NULL, the '@'
* separator is also omitted; if @resource is empty or %NULL, the '/' separator
* is also omitted.
*
* Returns: a JID constructed from @node, @domain and @resource
*/
gchar *
wocky_compose_jid (const gchar *node,
const gchar *domain,
const gchar *resource)
{
GString *normal = NULL;
normal = g_string_sized_new (strlen0 (node) + strlen0 (domain) +
strlen0 (resource) + 2);
if (node != NULL && *node != '\0')
g_string_printf (normal, "%s@%s", node, domain);
else
g_string_printf (normal, "%s", domain);
if (resource != NULL && *resource != '\0' && normal->len > 0)
g_string_append_printf (normal, "/%s", resource);
return g_string_free (normal, FALSE);
}
/**
* wocky_g_value_slice_new:
* @type: The type desired for the new GValue
*
* Slice-allocate an empty #GValue. wocky_g_value_slice_new_boolean() and similar
* functions are likely to be more convenient to use for the types supported.
*
* Returns: a newly allocated, newly initialized #GValue, to be freed with
* wocky_g_value_slice_free() or g_slice_free().
* Since: 0.5.14
*/
GValue *
wocky_g_value_slice_new (GType type)
{
GValue *ret = g_slice_new0 (GValue);
g_value_init (ret, type);
return ret;
}
/**
* wocky_g_value_slice_new_boolean:
* @b: a boolean value
*
* Slice-allocate and initialize a #GValue. This function is convenient to
* use when constructing hash tables from string to #GValue, for example.
*
* Returns: a #GValue of type %G_TYPE_BOOLEAN with value @b, to be freed with
* wocky_g_value_slice_free() or g_slice_free()
*
* Since: 0.7.27
*/
GValue *
wocky_g_value_slice_new_boolean (gboolean b)
{
GValue *v = wocky_g_value_slice_new (G_TYPE_BOOLEAN);
g_value_set_boolean (v, b);
return v;
}
/**
* wocky_g_value_slice_new_int:
* @n: an integer
*
* Slice-allocate and initialize a #GValue. This function is convenient to
* use when constructing hash tables from string to #GValue, for example.
*
* Returns: a #GValue of type %G_TYPE_INT with value @n, to be freed with
* wocky_g_value_slice_free() or g_slice_free()
*
* Since: 0.7.27
*/
GValue *
wocky_g_value_slice_new_int (gint n)
{
GValue *v = wocky_g_value_slice_new (G_TYPE_INT);
g_value_set_int (v, n);
return v;
}
/**
* wocky_g_value_slice_new_int64:
* @n: a 64-bit integer
*
* Slice-allocate and initialize a #GValue. This function is convenient to
* use when constructing hash tables from string to #GValue, for example.
*
* Returns: a #GValue of type %G_TYPE_INT64 with value @n, to be freed with
* wocky_g_value_slice_free() or g_slice_free()
*
* Since: 0.7.27
*/
GValue *
wocky_g_value_slice_new_int64 (gint64 n)
{
GValue *v = wocky_g_value_slice_new (G_TYPE_INT64);
g_value_set_int64 (v, n);
return v;
}
/**
* wocky_g_value_slice_new_uint:
* @n: an unsigned integer
*
* Slice-allocate and initialize a #GValue. This function is convenient to
* use when constructing hash tables from string to #GValue, for example.
*
* Returns: a #GValue of type %G_TYPE_UINT with value @n, to be freed with
* wocky_g_value_slice_free() or g_slice_free()
*
* Since: 0.7.27
*/
GValue *
wocky_g_value_slice_new_uint (guint n)
{
GValue *v = wocky_g_value_slice_new (G_TYPE_UINT);
g_value_set_uint (v, n);
return v;
}
/**
* wocky_g_value_slice_new_uint64:
* @n: a 64-bit unsigned integer
*
* Slice-allocate and initialize a #GValue. This function is convenient to
* use when constructing hash tables from string to #GValue, for example.
*
* Returns: a #GValue of type %G_TYPE_UINT64 with value @n, to be freed with
* wocky_g_value_slice_free() or g_slice_free()
*
* Since: 0.7.27
*/
GValue *
wocky_g_value_slice_new_uint64 (guint64 n)
{
GValue *v = wocky_g_value_slice_new (G_TYPE_UINT64);
g_value_set_uint64 (v, n);
return v;
}
/**
* wocky_g_value_slice_new_double:
* @d: a number
*
* Slice-allocate and initialize a #GValue. This function is convenient to
* use when constructing hash tables from string to #GValue, for example.
*
* Returns: a #GValue of type %G_TYPE_DOUBLE with value @n, to be freed with
* wocky_g_value_slice_free() or g_slice_free()
*
* Since: 0.7.27
*/
GValue *
wocky_g_value_slice_new_double (double n)
{
GValue *v = wocky_g_value_slice_new (G_TYPE_DOUBLE);
g_value_set_double (v, n);
return v;
}
/**
* wocky_g_value_slice_new_string:
* @string: a string to be copied into the value
*
* Slice-allocate and initialize a #GValue. This function is convenient to
* use when constructing hash tables from string to #GValue, for example.
*
* Returns: a #GValue of type %G_TYPE_STRING whose value is a copy of @string,
* to be freed with wocky_g_value_slice_free() or g_slice_free()
*
* Since: 0.7.27
*/
GValue *
wocky_g_value_slice_new_string (const gchar *string)
{
GValue *v = wocky_g_value_slice_new (G_TYPE_STRING);
g_value_set_string (v, string);
return v;
}
/**
* wocky_g_value_slice_new_static_string:
* @string: a static string which must remain valid forever, to be pointed to
* by the value
*
* Slice-allocate and initialize a #GValue. This function is convenient to
* use when constructing hash tables from string to #GValue, for example.
*
* Returns: a #GValue of type %G_TYPE_STRING whose value is @string,
* to be freed with wocky_g_value_slice_free() or g_slice_free()
*
* Since: 0.7.27
*/
GValue *
wocky_g_value_slice_new_static_string (const gchar *string)
{
GValue *v = wocky_g_value_slice_new (G_TYPE_STRING);
g_value_set_static_string (v, string);
return v;
}
/**
* wocky_g_value_slice_new_take_string:
* @string: a string which will be freed with g_free() by the returned #GValue
* (the caller must own it before calling this function, but no longer owns
* it after this function returns)
*
* Slice-allocate and initialize a #GValue. This function is convenient to
* use when constructing hash tables from string to #GValue, for example.
*
* Returns: a #GValue of type %G_TYPE_STRING whose value is @string,
* to be freed with wocky_g_value_slice_free() or g_slice_free()
*
* Since: 0.7.27
*/
GValue *
wocky_g_value_slice_new_take_string (gchar *string)
{
GValue *v = wocky_g_value_slice_new (G_TYPE_STRING);
g_value_take_string (v, string);
return v;
}
/**
* wocky_g_value_slice_new_boxed:
* @type: a boxed type
* @p: a pointer of type @type, which will be copied
*
* Slice-allocate and initialize a #GValue. This function is convenient to
* use when constructing hash tables from string to #GValue, for example.
*
* Returns: a #GValue of type @type whose value is a copy of @p,
* to be freed with wocky_g_value_slice_free() or g_slice_free()
*
* Since: 0.7.27
*/
GValue *
wocky_g_value_slice_new_boxed (GType type,
gconstpointer p)
{
GValue *v;
g_return_val_if_fail (G_TYPE_FUNDAMENTAL (type) == G_TYPE_BOXED, NULL);
v = wocky_g_value_slice_new (type);
g_value_set_boxed (v, p);
return v;
}
/**
* wocky_g_value_slice_new_static_boxed:
* @type: a boxed type
* @p: a pointer of type @type, which must remain valid forever
*
* Slice-allocate and initialize a #GValue. This function is convenient to
* use when constructing hash tables from string to #GValue, for example.
*
* Returns: a #GValue of type @type whose value is @p,
* to be freed with wocky_g_value_slice_free() or g_slice_free()
*
* Since: 0.7.27
*/
GValue *
wocky_g_value_slice_new_static_boxed (GType type,
gconstpointer p)
{
GValue *v;
g_return_val_if_fail (G_TYPE_FUNDAMENTAL (type) == G_TYPE_BOXED, NULL);
v = wocky_g_value_slice_new (type);
g_value_set_static_boxed (v, p);
return v;
}
/**
* wocky_g_value_slice_new_take_boxed:
* @type: a boxed type
* @p: a pointer of type @type which will be freed with g_boxed_free() by the
* returned #GValue (the caller must own it before calling this function, but
* no longer owns it after this function returns)
*
* Slice-allocate and initialize a #GValue. This function is convenient to
* use when constructing hash tables from string to #GValue, for example.
*
* Returns: a #GValue of type @type whose value is @p,
* to be freed with wocky_g_value_slice_free() or g_slice_free()
*
* Since: 0.7.27
*/
GValue *
wocky_g_value_slice_new_take_boxed (GType type,
gpointer p)
{
GValue *v;
g_return_val_if_fail (G_TYPE_FUNDAMENTAL (type) == G_TYPE_BOXED, NULL);
v = wocky_g_value_slice_new (type);
g_value_take_boxed (v, p);
return v;
}
/**
* wocky_g_value_slice_free:
* @value: A GValue which was allocated with the g_slice API
*
* Unset and free a slice-allocated GValue.
*
* (GDestroyNotify) wocky_g_value_slice_free can be used
* as a destructor for values in a #GHashTable, for example.
*/
void
wocky_g_value_slice_free (GValue *value)
{
g_value_unset (value);
g_slice_free (GValue, value);
}
/**
* wocky_g_value_slice_dup:
* @value: A GValue
*
*
*
* Returns: a newly allocated copy of @value, to be freed with
* wocky_g_value_slice_free() or g_slice_free().
* Since: 0.5.14
*/
GValue *
wocky_g_value_slice_dup (const GValue *value)
{
GValue *ret = wocky_g_value_slice_new (G_VALUE_TYPE (value));
g_value_copy (value, ret);
return ret;
}
/**
* wocky_enum_from_nick:
* @enum_type: the GType of a subtype of GEnum
* @nick: a non-%NULL string purporting to be the nickname of a value of
* @enum_type
* @value: the address at which to store the value of @enum_type corresponding
* to @nick if this functions returns %TRUE; if this function returns
* %FALSE, this variable will be left untouched.
*
*
*
* Returns: %TRUE if @nick is a member of @enum_type, or %FALSE otherwise
*/
gboolean
wocky_enum_from_nick (
GType enum_type,
const gchar *nick,
gint *value)
{
GEnumClass *klass = g_type_class_ref (enum_type);
GEnumValue *enum_value;
g_return_val_if_fail (klass != NULL, FALSE);
g_return_val_if_fail (value != NULL, FALSE);
enum_value = g_enum_get_value_by_nick (klass, nick);
g_type_class_unref (klass);
if (enum_value != NULL)
{
*value = enum_value->value;
return TRUE;
}
else
{
return FALSE;
}
}
/**
* wocky_enum_to_nick:
* @enum_type: the GType of a subtype of GEnum
* @value: a value of @enum_type
*
*
*
* Returns: the nickname of @value, or %NULL if it is not, in fact, a value of
* @enum_type
*/
const gchar *
wocky_enum_to_nick (
GType enum_type,
gint value)
{
GEnumClass *klass = g_type_class_ref (enum_type);
GEnumValue *enum_value;
g_return_val_if_fail (klass != NULL, NULL);
enum_value = g_enum_get_value (klass, value);
g_type_class_unref (klass);
if (enum_value != NULL)
return enum_value->value_nick;
else
return NULL;
}
/**
* wocky_absolutize_path:
* @path: an absolute or relative path
*
* Return an absolute form of @path. This cleans up duplicate slashes, "." or
* ".." path segments, etc., and prepends g_get_current_dir() if necessary, but
* does not necessarily resolve symlinks.
*
* Returns: an absolute path which must be freed with g_free(), or possibly
* %NULL for invalid filenames
*/
gchar *
wocky_absolutize_path (const gchar *path)
{
GFile *cwd, *absolute;
gchar *cwd_str, *ret;
cwd_str = g_get_current_dir ();
cwd = g_file_new_for_path (cwd_str);
g_free (cwd_str);
if (cwd == NULL)
return NULL;
absolute = g_file_resolve_relative_path (cwd, path);
if (absolute == NULL)
{
g_object_unref (cwd);
return NULL;
}
ret = g_file_get_path (absolute); /* possibly NULL */
g_object_unref (cwd);
g_object_unref (absolute);
return ret;
}
GList *
wocky_list_deep_copy (GBoxedCopyFunc copy,
const GList *items)
{
GList *ret = NULL;
const GList *l;
g_return_val_if_fail (copy != NULL, NULL);
for (l = items; l != NULL; l = l->next)
ret = g_list_prepend (ret, copy (l->data));
return g_list_reverse (ret);
}
GString *
wocky_g_string_dup (const GString *str)
{
if (str == NULL)
return NULL;
return g_string_new_len (str->str, str->len);
}
void
wocky_g_string_free (GString *str)
{
if (str != NULL)
g_string_free (str, TRUE);
}