/* * 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); }