/*
* util.c - Source for telepathy-glib utility functions
* Copyright © 2006-2010 Collabora Ltd.
* Copyright © 2006-2008 Nokia Corporation
* Copyright © 1999 Tom Tromey
* Copyright © 2000 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 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
*/
/**
* SECTION:util
* @title: Utilities
* @short_description: Non-Telepathy utility functions
*
* Some utility functions used in telepathy-glib which could have been in
* GLib, but aren't.
*/
#include "config.h"
#include
#include
#ifdef HAVE_GIO_UNIX
#include
#include
#endif /* HAVE_GIO_UNIX */
#include
#include
#include
#include
#include
#include
#include
#define DEBUG_FLAG TP_DEBUG_MISC
#include "debug-internal.h"
#include "simple-client-factory-internal.h"
/**
* tp_verify:
* @R: a requirement (constant expression) to be checked at compile-time
*
* Make an assertion at compile time, like C++0x's proposed static_assert
* keyword. If @R is determined to be true, there is no overhead at runtime;
* if @R is determined to be false, compilation will fail.
*
* This macro can be used at file scope (it expands to a dummy extern
* declaration).
*
* (This is gnulib's verify macro, written by Paul Eggert, Bruno Haible and
* Jim Meyering.)
*
* This macro will be deprecated in a future telepathy-glib release. Please
* use GLib 2.20's G_STATIC_ASSERT() macro in new code.
*
* Since: 0.7.34
*/
/**
* tp_verify_true:
* @R: a requirement (constant expression) to be checked at compile-time
*
* Make an assertion at compile time, like C++0x's proposed static_assert
* keyword. If @R is determined to be true, there is no overhead at runtime,
* and the macro evaluates to 1 as an integer constant expression;
* if @R is determined to be false, compilation will fail.
*
* This macro can be used anywhere that an integer constant expression would
* be allowed.
*
* (This is gnulib's verify_true macro, written by Paul Eggert, Bruno Haible
* and Jim Meyering.)
*
* This macro will be deprecated in a future telepathy-glib release. Please
* use GLib 2.20's G_STATIC_ASSERT() macro in new code.
*
* Returns: 1
*
* Since: 0.7.34
*/
/**
* tp_verify_statement:
* @R: a requirement (constant expression) to be checked at compile-time
*
* Make an assertion at compile time, like C++0x's proposed static_assert
* keyword. If @R is determined to be true, there is no overhead at runtime;
* if @R is determined to be false, compilation will fail.
*
* This macro can be used anywhere that a statement would be allowed; it
* is equivalent to ((void) tp_verify_true (R)).
*
* This macro will be deprecated in a future telepathy-glib release. Please
* use GLib 2.20's G_STATIC_ASSERT() macro in new code.
*
* Since: 0.7.34
*/
/**
* tp_g_ptr_array_contains: (skip)
* @haystack: The pointer array to be searched
* @needle: The pointer to look for
*
*
*
* Returns: %TRUE if @needle is one of the elements of @haystack
*/
gboolean
tp_g_ptr_array_contains (GPtrArray *haystack, gpointer needle)
{
guint i;
g_return_val_if_fail (haystack != NULL, FALSE);
for (i = 0; i < haystack->len; i++)
{
if (g_ptr_array_index (haystack, i) == needle)
return TRUE;
}
return FALSE;
}
static void
add_to_array (gpointer data,
gpointer user_data)
{
g_ptr_array_add (user_data, data);
}
/**
* tp_g_ptr_array_extend: (skip)
* @target: a #GPtrArray to copy items to
* @source: a #GPtrArray to copy items from
*
* Appends all elements of @source to @target. Note that this only copies the
* pointers from @source; any duplication or reference-incrementing must be
* performed by the caller.
*
* After this function has been called, it is safe to call
* g_ptr_array_free() on @source and also free the actual pointer array,
* as long as doing so does not free the data pointed to by the new
* items in @target.
*
* Since: 0.14.3
*/
void
tp_g_ptr_array_extend (GPtrArray *target,
GPtrArray *source)
{
g_return_if_fail (source != NULL);
g_return_if_fail (target != NULL);
g_ptr_array_foreach (source, add_to_array, target);
}
/**
* tp_g_value_slice_new: (skip)
* @type: The type desired for the new GValue
*
* Slice-allocate an empty #GValue. tp_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
* tp_g_value_slice_free() or g_slice_free().
* Since: 0.5.14
*/
GValue *
tp_g_value_slice_new (GType type)
{
GValue *ret = g_slice_new0 (GValue);
g_value_init (ret, type);
return ret;
}
/**
* tp_g_value_slice_new_boolean: (skip)
* @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
* tp_g_value_slice_free() or g_slice_free()
*
* Since: 0.7.27
*/
GValue *
tp_g_value_slice_new_boolean (gboolean b)
{
GValue *v = tp_g_value_slice_new (G_TYPE_BOOLEAN);
g_value_set_boolean (v, b);
return v;
}
/**
* tp_g_value_slice_new_int: (skip)
* @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
* tp_g_value_slice_free() or g_slice_free()
*
* Since: 0.7.27
*/
GValue *
tp_g_value_slice_new_int (gint n)
{
GValue *v = tp_g_value_slice_new (G_TYPE_INT);
g_value_set_int (v, n);
return v;
}
/**
* tp_g_value_slice_new_int64: (skip)
* @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
* tp_g_value_slice_free() or g_slice_free()
*
* Since: 0.7.27
*/
GValue *
tp_g_value_slice_new_int64 (gint64 n)
{
GValue *v = tp_g_value_slice_new (G_TYPE_INT64);
g_value_set_int64 (v, n);
return v;
}
/**
* tp_g_value_slice_new_byte: (skip)
* @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_UCHAR with value @n, to be freed with
* tp_g_value_slice_free() or g_slice_free()
*
* Since: 0.11.0
*/
GValue *
tp_g_value_slice_new_byte (guchar n)
{
GValue *v = tp_g_value_slice_new (G_TYPE_UCHAR);
g_value_set_uchar (v, n);
return v;
}
/**
* tp_g_value_slice_new_uint: (skip)
* @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
* tp_g_value_slice_free() or g_slice_free()
*
* Since: 0.7.27
*/
GValue *
tp_g_value_slice_new_uint (guint n)
{
GValue *v = tp_g_value_slice_new (G_TYPE_UINT);
g_value_set_uint (v, n);
return v;
}
/**
* tp_g_value_slice_new_uint64: (skip)
* @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
* tp_g_value_slice_free() or g_slice_free()
*
* Since: 0.7.27
*/
GValue *
tp_g_value_slice_new_uint64 (guint64 n)
{
GValue *v = tp_g_value_slice_new (G_TYPE_UINT64);
g_value_set_uint64 (v, n);
return v;
}
/**
* tp_g_value_slice_new_double: (skip)
* @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
* tp_g_value_slice_free() or g_slice_free()
*
* Since: 0.7.27
*/
GValue *
tp_g_value_slice_new_double (double n)
{
GValue *v = tp_g_value_slice_new (G_TYPE_DOUBLE);
g_value_set_double (v, n);
return v;
}
/**
* tp_g_value_slice_new_string: (skip)
* @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 tp_g_value_slice_free() or g_slice_free()
*
* Since: 0.7.27
*/
GValue *
tp_g_value_slice_new_string (const gchar *string)
{
GValue *v = tp_g_value_slice_new (G_TYPE_STRING);
g_value_set_string (v, string);
return v;
}
/**
* tp_g_value_slice_new_static_string: (skip)
* @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 tp_g_value_slice_free() or g_slice_free()
*
* Since: 0.7.27
*/
GValue *
tp_g_value_slice_new_static_string (const gchar *string)
{
GValue *v = tp_g_value_slice_new (G_TYPE_STRING);
g_value_set_static_string (v, string);
return v;
}
/**
* tp_g_value_slice_new_take_string: (skip)
* @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 tp_g_value_slice_free() or g_slice_free()
*
* Since: 0.7.27
*/
GValue *
tp_g_value_slice_new_take_string (gchar *string)
{
GValue *v = tp_g_value_slice_new (G_TYPE_STRING);
g_value_take_string (v, string);
return v;
}
/**
* tp_g_value_slice_new_boxed: (skip)
* @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 tp_g_value_slice_free() or g_slice_free()
*
* Since: 0.7.27
*/
GValue *
tp_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 = tp_g_value_slice_new (type);
g_value_set_boxed (v, p);
return v;
}
/**
* tp_g_value_slice_new_static_boxed: (skip)
* @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 tp_g_value_slice_free() or g_slice_free()
*
* Since: 0.7.27
*/
GValue *
tp_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 = tp_g_value_slice_new (type);
g_value_set_static_boxed (v, p);
return v;
}
/**
* tp_g_value_slice_new_take_boxed: (skip)
* @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 tp_g_value_slice_free() or g_slice_free()
*
* Since: 0.7.27
*/
GValue *
tp_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 = tp_g_value_slice_new (type);
g_value_take_boxed (v, p);
return v;
}
/**
* tp_g_value_slice_free: (skip)
* @value: A GValue which was allocated with the g_slice API
*
* Unset and free a slice-allocated GValue.
*
* (GDestroyNotify) tp_g_value_slice_free can be used
* as a destructor for values in a #GHashTable, for example.
*/
void
tp_g_value_slice_free (GValue *value)
{
g_value_unset (value);
g_slice_free (GValue, value);
}
/**
* tp_g_value_slice_dup: (skip)
* @value: A GValue
*
*
*
* Returns: a newly allocated copy of @value, to be freed with
* tp_g_value_slice_free() or g_slice_free().
* Since: 0.5.14
*/
GValue *
tp_g_value_slice_dup (const GValue *value)
{
GValue *ret = tp_g_value_slice_new (G_VALUE_TYPE (value));
g_value_copy (value, ret);
return ret;
}
struct _tp_g_hash_table_update
{
GHashTable *target;
GBoxedCopyFunc key_dup, value_dup;
};
static void
_tp_g_hash_table_update_helper (gpointer key,
gpointer value,
gpointer user_data)
{
struct _tp_g_hash_table_update *data = user_data;
gpointer new_key = (data->key_dup != NULL) ? (data->key_dup) (key) : key;
gpointer new_value = (data->value_dup != NULL) ? (data->value_dup) (value)
: value;
g_hash_table_replace (data->target, new_key, new_value);
}
/**
* tp_g_hash_table_update: (skip)
* @target: The hash table to be updated
* @source: The hash table to update it with (read-only)
* @key_dup: function to duplicate a key from @source so it can be be stored
* in @target. If NULL, the key is not copied, but is used as-is
* @value_dup: function to duplicate a value from @source so it can be stored
* in @target. If NULL, the value is not copied, but is used as-is
*
* Add each item in @source to @target, replacing any existing item with the
* same key. @key_dup and @value_dup are used to duplicate the items; in
* principle they could also be used to convert between types.
*
* Since: 0.7.0
*/
void
tp_g_hash_table_update (GHashTable *target,
GHashTable *source,
GBoxedCopyFunc key_dup,
GBoxedCopyFunc value_dup)
{
struct _tp_g_hash_table_update data = { target, key_dup,
value_dup };
g_return_if_fail (target != NULL);
g_return_if_fail (source != NULL);
g_return_if_fail (target != source);
g_hash_table_foreach (source, _tp_g_hash_table_update_helper, &data);
}
/**
* tp_str_empty: (skip)
* @s: (type utf8) (transfer none): a string
*
* Return %TRUE if @s is empty, counting %NULL as empty.
*
* Returns: (type boolean): %TRUE if @s is either %NULL or ""
*
* Since: 0.11.1
*/
/* no definition here - it's inlined */
/**
* tp_strdiff: (skip)
* @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
tp_strdiff (const gchar *left, const gchar *right)
{
return g_strcmp0 (left, right) != 0;
}
/**
* tp_mixin_offset_cast: (skip)
* @instance: A pointer to a structure
* @offset: The offset of a structure member in bytes, which must not be 0
*
* Extend a pointer by an offset, provided the offset is not 0.
* This is used to cast from an object instance to one of the telepathy-glib
* mixin classes.
*
* Returns: a pointer @offset bytes beyond @instance
*/
gpointer
tp_mixin_offset_cast (gpointer instance, guint offset)
{
g_return_val_if_fail (offset != 0, NULL);
return ((guchar *) instance + offset);
}
/**
* tp_mixin_instance_get_offset: (skip)
* @instance: A pointer to a GObject-derived instance structure
* @quark: A quark that was used to store the offset with g_type_set_qdata()
*
* If the type of @instance, or any of its ancestor types, has had an offset
* attached using qdata with the given @quark, return that offset. If not,
* return 0.
*
* In older telepathy-glib versions, calling this function on an instance that
* did not have the mixin was considered to be a programming error. Since
* version 0.13.9, 0 is returned, without error.
*
* This is used to implement the telepathy-glib mixin classes.
*
* Returns: the offset of the mixin
*/
guint
tp_mixin_instance_get_offset (gpointer instance,
GQuark quark)
{
GType t;
for (t = G_OBJECT_TYPE (instance);
t != 0;
t = g_type_parent (t))
{
gpointer qdata = g_type_get_qdata (t, quark);
if (qdata != NULL)
return GPOINTER_TO_UINT (qdata);
}
return 0;
}
/**
* tp_mixin_class_get_offset: (skip)
* @klass: A pointer to a GObjectClass-derived class structure
* @quark: A quark that was used to store the offset with g_type_set_qdata()
*
* If the type of @klass, or any of its ancestor types, has had an offset
* attached using qdata with the given @quark, return that offset; if not,
* return 0.
*
* In older telepathy-glib versions, calling this function on an instance that
* did not have the mixin was considered to be a programming error. Since
* version 0.13.9, 0 is returned, without error.
*
* This is used to implement the telepathy-glib mixin classes.
*
* Returns: the offset of the mixin class
*/
guint
tp_mixin_class_get_offset (gpointer klass,
GQuark quark)
{
GType t;
for (t = G_OBJECT_CLASS_TYPE (klass);
t != 0;
t = g_type_parent (t))
{
gpointer qdata = g_type_get_qdata (t, quark);
if (qdata != NULL)
return GPOINTER_TO_UINT (qdata);
}
return 0;
}
static inline gboolean
_esc_ident_bad (gchar c, gboolean is_first)
{
return ((c < 'a' || c > 'z') &&
(c < 'A' || c > 'Z') &&
(c < '0' || c > '9' || is_first));
}
/**
* tp_escape_as_identifier:
* @name: The string to be escaped
*
* Escape an arbitrary string so it follows the rules for a C identifier,
* and hence an object path component, interface element component,
* bus name component or member name in D-Bus.
*
* Unlike g_strcanon this is a reversible encoding, so it preserves
* distinctness.
*
* The escaping consists of replacing all non-alphanumerics, and the first
* character if it's a digit, with an underscore and two lower-case hex
* digits:
*
* "0123abc_xyz\x01\xff" -> _30123abc_5fxyz_01_ff
*
* i.e. similar to URI encoding, but with _ taking the role of %, and a
* smaller allowed set. As a special case, "" is escaped to "_" (just for
* completeness, really).
*
* Returns: (transfer full): the escaped string, which must be freed by
* the caller with #g_free
*/
gchar *
tp_escape_as_identifier (const gchar *name)
{
gboolean bad = FALSE;
size_t len = 0;
GString *op;
const gchar *ptr, *first_ok;
g_return_val_if_fail (name != NULL, NULL);
/* fast path for empty name */
if (name[0] == '\0')
return g_strdup ("_");
for (ptr = name; *ptr; ptr++)
{
if (_esc_ident_bad (*ptr, ptr == name))
{
bad = TRUE;
len += 3;
}
else
len++;
}
/* fast path if it's clean */
if (!bad)
return g_strdup (name);
/* If strictly less than ptr, first_ok is the first uncopied safe character.
*/
first_ok = name;
op = g_string_sized_new (len);
for (ptr = name; *ptr; ptr++)
{
if (_esc_ident_bad (*ptr, ptr == name))
{
/* copy preceding safe characters if any */
if (first_ok < ptr)
{
g_string_append_len (op, first_ok, ptr - first_ok);
}
/* escape the unsafe character */
g_string_append_printf (op, "_%02x", (unsigned char)(*ptr));
/* restart after it */
first_ok = ptr + 1;
}
}
/* copy trailing safe characters if any */
if (first_ok < ptr)
{
g_string_append_len (op, first_ok, ptr - first_ok);
}
return g_string_free (op, FALSE);
}
/**
* tp_strv_contains: (skip)
* @strv: a NULL-terminated array of strings, or %NULL (which is treated as an
* empty strv)
* @str: a non-NULL string
*
*
* Returns: TRUE if @str is an element of @strv, according to strcmp().
*
* Since: 0.7.15
*/
gboolean
tp_strv_contains (const gchar * const *strv,
const gchar *str)
{
g_return_val_if_fail (str != NULL, FALSE);
if (strv == NULL)
return FALSE;
while (*strv != NULL)
{
if (!tp_strdiff (str, *strv))
return TRUE;
strv++;
}
return FALSE;
}
/**
* tp_g_key_file_get_int64: (skip)
* @key_file: a non-%NULL #GKeyFile
* @group_name: a non-%NULL group name
* @key: a non-%NULL key
* @error: return location for a #GError
*
* Returns the value associated with @key under @group_name as a signed
* 64-bit integer. This is similar to g_key_file_get_integer() but can return
* 64-bit results without truncation.
*
* Returns: the value associated with the key as a signed 64-bit integer, or
* 0 if the key was not found or could not be parsed.
*
* Since: 0.7.31
* Deprecated: Since 0.21.0. Use g_key_file_get_int64() instead.
*/
gint64
tp_g_key_file_get_int64 (GKeyFile *key_file,
const gchar *group_name,
const gchar *key,
GError **error)
{
gchar *s, *end;
gint64 v;
g_return_val_if_fail (key_file != NULL, -1);
g_return_val_if_fail (group_name != NULL, -1);
g_return_val_if_fail (key != NULL, -1);
s = g_key_file_get_value (key_file, group_name, key, error);
if (s == NULL)
return 0;
v = g_ascii_strtoll (s, &end, 10);
if (*s == '\0' || *end != '\0')
{
g_set_error (error, G_KEY_FILE_ERROR, G_KEY_FILE_ERROR_INVALID_VALUE,
"Key '%s' in group '%s' has value '%s' where int64 was expected",
key, group_name, s);
return 0;
}
g_free (s);
return v;
}
/**
* tp_g_key_file_get_uint64: (skip)
* @key_file: a non-%NULL #GKeyFile
* @group_name: a non-%NULL group name
* @key: a non-%NULL key
* @error: return location for a #GError
*
* Returns the value associated with @key under @group_name as an unsigned
* 64-bit integer. This is similar to g_key_file_get_integer() but can return
* large positive results without truncation.
*
* Returns: the value associated with the key as an unsigned 64-bit integer,
* or 0 if the key was not found or could not be parsed.
*
* Since: 0.7.31
* Deprecated: Since 0.21.0. Use g_key_file_get_uint64() instead.
*/
guint64
tp_g_key_file_get_uint64 (GKeyFile *key_file,
const gchar *group_name,
const gchar *key,
GError **error)
{
gchar *s, *end;
guint64 v;
g_return_val_if_fail (key_file != NULL, -1);
g_return_val_if_fail (group_name != NULL, -1);
g_return_val_if_fail (key != NULL, -1);
s = g_key_file_get_value (key_file, group_name, key, error);
if (s == NULL)
return 0;
v = g_ascii_strtoull (s, &end, 10);
if (*s == '\0' || *end != '\0')
{
g_set_error (error, G_KEY_FILE_ERROR, G_KEY_FILE_ERROR_INVALID_VALUE,
"Key '%s' in group '%s' has value '%s' where uint64 was expected",
key, group_name, s);
return 0;
}
g_free (s);
return v;
}
typedef struct {
GObject *instance;
GObject *observer;
GClosure *closure;
gulong handler_id;
} WeakHandlerCtx;
static WeakHandlerCtx *
whc_new (GObject *instance,
GObject *observer)
{
WeakHandlerCtx *ctx = g_slice_new0 (WeakHandlerCtx);
ctx->instance = instance;
ctx->observer = observer;
return ctx;
}
static void
whc_free (WeakHandlerCtx *ctx)
{
g_slice_free (WeakHandlerCtx, ctx);
}
static void observer_destroyed_cb (gpointer, GObject *);
static void closure_invalidated_cb (gpointer, GClosure *);
/*
* If signal handlers are removed before the object is destroyed, this
* callback will never get triggered.
*/
static void
instance_destroyed_cb (gpointer ctx_,
GObject *where_the_instance_was)
{
WeakHandlerCtx *ctx = ctx_;
/* No need to disconnect the signal here, the instance has gone away. */
g_object_weak_unref (ctx->observer, observer_destroyed_cb, ctx);
g_closure_remove_invalidate_notifier (ctx->closure, ctx,
closure_invalidated_cb);
whc_free (ctx);
}
/* Triggered when the observer is destroyed. */
static void
observer_destroyed_cb (gpointer ctx_,
GObject *where_the_observer_was)
{
WeakHandlerCtx *ctx = ctx_;
g_closure_remove_invalidate_notifier (ctx->closure, ctx,
closure_invalidated_cb);
g_signal_handler_disconnect (ctx->instance, ctx->handler_id);
g_object_weak_unref (ctx->instance, instance_destroyed_cb, ctx);
whc_free (ctx);
}
/* Triggered when either object is destroyed or the handler is disconnected. */
static void
closure_invalidated_cb (gpointer ctx_,
GClosure *where_the_closure_was)
{
WeakHandlerCtx *ctx = ctx_;
g_object_weak_unref (ctx->instance, instance_destroyed_cb, ctx);
g_object_weak_unref (ctx->observer, observer_destroyed_cb, ctx);
whc_free (ctx);
}
/**
* tp_g_signal_connect_object: (skip)
* @instance: the instance to connect to.
* @detailed_signal: a string of the form "signal-name::detail".
* @c_handler: the #GCallback to connect.
* @gobject: the object to pass as data to @c_handler.
* @connect_flags: a combination of #GConnectFlags. Only
* %G_CONNECT_AFTER and %G_CONNECT_SWAPPED are supported by this function.
*
* Connects a #GCallback function to a signal for a particular object, as if
* with g_signal_connect(). Additionally, arranges for the signal handler to be
* disconnected if @gobject is destroyed.
*
* This is similar to g_signal_connect_data(), but uses a closure which
* ensures that the @gobject stays alive during the call to @c_handler
* by temporarily adding a reference count to @gobject.
*
* This is similar to g_signal_connect_object(), but doesn't have the
* documented bug that everyone is too scared to fix. Also, it does not allow
* you to pass in NULL as @gobject
*
* This is intended to be a convenient way for objects to use themselves as
* user_data for callbacks without having to explicitly disconnect all the
* handlers in their finalizers.
*
* Changed in 0.10.4 and 0.11.3: %G_CONNECT_AFTER is now respected.
*
* Returns: the handler id
*
* Since: 0.9.2
*/
gulong
tp_g_signal_connect_object (gpointer instance,
const gchar *detailed_signal,
GCallback c_handler,
gpointer gobject,
GConnectFlags connect_flags)
{
GObject *instance_obj = G_OBJECT (instance);
WeakHandlerCtx *ctx = whc_new (instance_obj, gobject);
g_return_val_if_fail (G_TYPE_CHECK_INSTANCE (instance), 0);
g_return_val_if_fail (detailed_signal != NULL, 0);
g_return_val_if_fail (c_handler != NULL, 0);
g_return_val_if_fail (G_IS_OBJECT (gobject), 0);
g_return_val_if_fail (
(connect_flags & ~(G_CONNECT_AFTER|G_CONNECT_SWAPPED)) == 0, 0);
if (connect_flags & G_CONNECT_SWAPPED)
ctx->closure = g_cclosure_new_object_swap (c_handler, gobject);
else
ctx->closure = g_cclosure_new_object (c_handler, gobject);
ctx->handler_id = g_signal_connect_closure (instance, detailed_signal,
ctx->closure, (connect_flags & G_CONNECT_AFTER) ? TRUE : FALSE);
g_object_weak_ref (instance_obj, instance_destroyed_cb, ctx);
g_object_weak_ref (gobject, observer_destroyed_cb, ctx);
g_closure_add_invalidate_notifier (ctx->closure, ctx,
closure_invalidated_cb);
return ctx->handler_id;
}
/*
* _tp_quark_array_copy:
* @quarks: A 0-terminated list of quarks to copy
*
* Copy a zero-terminated array into a GArray. The trailing
* 0 is not counted in the @len member of the returned
* array, but the @data member is guaranteed to be
* zero-terminated.
*
* Returns: A new GArray containing a copy of @quarks.
*/
GArray *
_tp_quark_array_copy (const GQuark *quarks)
{
GArray *array;
const GQuark *q;
array = g_array_new (TRUE, TRUE, sizeof (GQuark));
for (q = quarks; q != NULL && *q != 0; q++)
{
g_array_append_val (array, *q);
}
return array;
}
/**
* tp_value_array_build: (skip)
* @length: The number of elements that should be in the array
* @type: The type of the first argument.
* @...: The value of the first item in the struct followed by a list of type,
* value pairs terminated by G_TYPE_INVALID.
*
* Creates a new #GValueArray for use with structs, containing the values
* passed in as parameters. The values are copied or reffed as appropriate for
* their type.
*
*
* using tp_value_array_build
*
* GValueArray *array = tp_value_array_build (2,
* G_TYPE_STRING, host,
* G_TYPE_UINT, port,
* G_TYPE_INVALID);
*
*
*
* Returns: a newly created #GValueArray, free with tp_value_array_free()
*
* Since: 0.9.2
*/
GValueArray *
tp_value_array_build (gsize length,
GType type,
...)
{
GValueArray *arr;
GType t;
va_list var_args;
char *error = NULL;
G_GNUC_BEGIN_IGNORE_DEPRECATIONS
arr = g_value_array_new (length);
G_GNUC_END_IGNORE_DEPRECATIONS
va_start (var_args, type);
for (t = type; t != G_TYPE_INVALID; t = va_arg (var_args, GType))
{
GValue *v = arr->values + arr->n_values;
G_GNUC_BEGIN_IGNORE_DEPRECATIONS
g_value_array_append (arr, NULL);
G_GNUC_END_IGNORE_DEPRECATIONS
g_value_init (v, t);
G_VALUE_COLLECT (v, var_args, 0, &error);
if (error != NULL)
{
CRITICAL ("%s", error);
g_free (error);
tp_value_array_free (arr);
va_end (var_args);
return NULL;
}
}
g_warn_if_fail (arr->n_values == length);
va_end (var_args);
return arr;
}
/**
* tp_value_array_unpack: (skip)
* @array: the array to unpack
* @len: The number of elements that should be in the array
* @...: a list of correctly typed pointers to store the values in
*
* Unpacks a #GValueArray into separate variables.
*
* The contents of the values aren't copied into the variables, and so become
* invalid when @array is freed.
*
*
* using tp_value_array_unpack
*
* const gchar *host;
* guint port;
*
* tp_value_array_unpack (array, 2,
* &host,
* &port);
*
*
*
* Since: 0.11.0
*/
void
tp_value_array_unpack (GValueArray *array,
gsize len,
...)
{
va_list var_args;
guint i;
va_start (var_args, len);
for (i = 0; i < len; i++)
{
GValue *value;
char *error = NULL;
if (G_UNLIKELY (i > array->n_values))
{
WARNING ("More parameters than entries in the struct!");
break;
}
G_GNUC_BEGIN_IGNORE_DEPRECATIONS
value = g_value_array_get_nth (array, i);
G_GNUC_END_IGNORE_DEPRECATIONS
G_VALUE_LCOPY (value, var_args, G_VALUE_NOCOPY_CONTENTS, &error);
if (error != NULL)
{
WARNING ("%s", error);
g_free (error);
break;
}
}
va_end (var_args);
}
/**
* TpWeakRef:
*
* A simple wrapper for a weak reference to a #GObject, suitable for use in
* asynchronous calls which should only affect the object if it hasn't already
* been freed.
*
* As well as wrapping a weak reference to an object, this structure can
* contain an extra pointer to arbitrary data. This is useful for asynchronous
* calls which act on an object and some second piece of data, which are quite
* common in practice.
*
* If more than one piece of auxiliary data is required, the @user_data
* argument to the constructor can be a struct or a #GValueArray.
*
* Since: 0.11.3
*/
struct _TpWeakRef {
/**/
gpointer object;
gpointer user_data;
GDestroyNotify destroy;
};
/**
* tp_weak_ref_new: (skip)
* @object: (type GObject.Object): an object to which to take a weak reference
* @user_data: optional additional data to store alongside the weak ref
* @destroy: destructor for @user_data, called when the weak ref
* is freed
*
* Return a new weak reference wrapper for @object.
*
* Returns: (transfer full): a new weak-reference wrapper
*
* Free-function: tp_weak_ref_destroy()
*
* Since: 0.11.3
*/
TpWeakRef *
tp_weak_ref_new (gpointer object,
gpointer user_data,
GDestroyNotify destroy)
{
TpWeakRef *self;
g_return_val_if_fail (G_IS_OBJECT (object), NULL);
self = g_slice_new (TpWeakRef);
self->object = object;
g_object_add_weak_pointer (self->object, &self->object);
self->user_data = user_data;
self->destroy = destroy;
return self;
}
/**
* tp_weak_ref_get_user_data: (skip)
* @self: a weak reference
*
* Return the additional data that was passed to tp_weak_ref_new().
*
* Returns: the additional data supplied in tp_weak_ref_new(), which may be
* %NULL
*
* Since: 0.11.3
*/
gpointer
tp_weak_ref_get_user_data (TpWeakRef *self)
{
return self->user_data;
}
/**
* tp_weak_ref_dup_object: (skip)
* @self: a weak reference
*
* If the weakly referenced object still exists, return a new reference to
* it. Otherwise, return %NULL.
*
* Returns: (type GObject.Object) (transfer full): a new reference, or %NULL
*
* Since: 0.11.3
*/
gpointer
tp_weak_ref_dup_object (TpWeakRef *self)
{
if (self->object != NULL)
return g_object_ref (self->object);
return NULL;
}
/**
* tp_weak_ref_destroy: (skip)
* @self: (transfer full): a weak reference
*
* Free a weak reference wrapper. This drops the weak reference to the
* object (if it still exists), and frees the user data with the user-supplied
* destructor function if one was provided.
*
* Since: 0.11.3
*/
void
tp_weak_ref_destroy (TpWeakRef *self)
{
if (self->object != NULL)
g_object_remove_weak_pointer (self->object, &self->object);
if (self->destroy != NULL)
(self->destroy) (self->user_data);
g_slice_free (TpWeakRef, self);
}
/**
* tp_clear_object: (skip)
* @op: a pointer to a variable, struct member etc. holding a #GObject
*
* Set a variable holding a #GObject to %NULL. If it was not already %NULL,
* unref the object it previously pointed to.
*
* This is exactly equivalent to calling tp_clear_pointer() on @op,
* with @destroy = g_object_unref(). See tp_clear_pointer() for example usage.
*
* Since: 0.11.7
*/
/**
* tp_clear_pointer: (skip)
* @pp: a pointer to a variable, struct member etc. holding a pointer
* @destroy: a function to which a gpointer can be passed, to destroy *@pp
* (if calling this macro from C++, explicitly casting the function to
* #GDestroyNotify may be necessary)
*
* Set a variable holding a pointer to %NULL. If it was not already %NULL,
* unref or destroy the object it previously pointed to with @destroy.
*
* More precisely, if *@pp is non-%NULL, set *@pp to %NULL, then
* call @destroy on the object that *@pp previously pointed to.
*
* This is analogous to g_clear_error() for non-error objects, but also
* ensures that @pp is already %NULL before the destructor is run.
*
* Typical usage is something like this:
*
* |[
* typedef struct {
* TpConnection *conn;
* GError *error;
* GHashTable *table;
* MyStruct *misc;
* } Foo;
* Foo *foo;
*
* ...
*
* tp_clear_object (&foo->conn);
* g_clear_error (&foo->error);
* tp_clear_boxed (G_TYPE_HASH_TABLE, &foo->table);
* tp_clear_pointer (&foo->misc, my_struct_destroy);
* ]|
*
* Since: 0.11.7
*/
/**
* tp_clear_boxed: (skip)
* @gtype: (type GObject.Type): the #GType of *@pp, e.g. %G_TYPE_HASH_TABLE
* @pp: a pointer to a variable, struct member etc. holding a boxed object
*
* Set a variable holding a boxed object to %NULL. If it was not already %NULL,
* destroy the boxed object it previously pointed to, as appropriate for
* @gtype.
*
* More precisely, if *@pp is non-%NULL, set *@pp to %NULL, then
* call g_boxed_free() on the object that *@pp previously pointed to.
*
* This is similar to tp_clear_pointer(); see that function's documentation
* for typical usage.
*
* Since: 0.11.7
*/
/**
* tp_simple_async_report_success_in_idle:
* @source: (allow-none): the source object
* @callback: (scope async): the callback
* @user_data: (closure): user data for @callback
* @source_tag: the source tag for the #GSimpleAsyncResult
*
* Create a new #GSimpleAsyncResult with no operation result, and call
* g_simple_async_result_complete_in_idle() on it.
*
* This is like a successful version of g_simple_async_report_error_in_idle(),
* suitable for asynchronous functions that (conceptually) either succeed and
* return nothing, or raise an error, such as tp_proxy_prepare_async().
*
* The corresponding finish function should not call a function that attempts
* to get a result, such as g_simple_async_result_get_op_res_gpointer().
*
* Since: 0.11.9
*/
void
tp_simple_async_report_success_in_idle (GObject *source,
GAsyncReadyCallback callback,
gpointer user_data,
gpointer source_tag)
{
GSimpleAsyncResult *simple;
simple = g_simple_async_result_new (source, callback, user_data, source_tag);
g_simple_async_result_complete_in_idle (simple);
g_object_unref (simple);
}
/**
* tp_user_action_time_from_x11:
* @x11_time: an X11 timestamp, or 0 to indicate the current time
*
* Convert an X11 timestamp into a user action time as used in Telepathy.
*
* This also works for the timestamps used by GDK 2, GDK 3 and Clutter 1.0;
* it may or may not work with other toolkits or versions.
*
* Returns: a nonzero Telepathy user action time, or
* %TP_USER_ACTION_TIME_CURRENT_TIME
*
* Since: 0.11.13
*/
gint64
tp_user_action_time_from_x11 (guint32 x11_time)
{
if (x11_time == 0)
{
return TP_USER_ACTION_TIME_CURRENT_TIME;
}
return x11_time;
}
/**
* tp_user_action_time_should_present:
* @user_action_time: (type gint64): the Telepathy user action time
* @x11_time: (out) (allow-none): a pointer to guint32 used to
* return an X11 timestamp, or 0 to indicate the current time; if
* %FALSE is returned, the value placed here is not meaningful
*
* Interpret a Telepathy user action time to decide whether a Handler should
* attempt to gain focus. If %TRUE is returned, it would be appropriate to
* call gtk_window_present_with_time() using @x11_time as input, for instance.
*
* @x11_time is used to return a timestamp in the right format for X11,
* GDK 2, GDK 3 and Clutter 1.0; it may or may not work with other
* toolkits or versions.
*
* Returns: %TRUE if it would be appropriate to present a window
*
* Since: 0.11.13
*/
gboolean
tp_user_action_time_should_present (gint64 user_action_time,
guint32 *x11_time)
{
guint32 when = 0;
gboolean ret;
if (user_action_time > 0 && user_action_time <= G_MAXUINT32)
{
when = (guint32) user_action_time;
ret = TRUE;
}
else if (user_action_time == TP_USER_ACTION_TIME_CURRENT_TIME)
{
ret = TRUE;
}
else
{
ret = FALSE;
}
if (ret && x11_time != NULL)
*x11_time = when;
return ret;
}
/* Add each of @quarks to @array if it isn't already present.
*
* There are @n quarks, or if @n == -1, the array is 0-terminated. */
void
_tp_quark_array_merge (GArray *array,
const GQuark *quarks,
gssize n)
{
gssize i;
guint j;
g_return_if_fail (array != NULL);
g_return_if_fail (g_array_get_element_size (array) == sizeof (GQuark));
g_return_if_fail (n >= -1);
g_return_if_fail (n <= 0 || quarks != NULL);
if (quarks == NULL || n == 0)
return;
if (n < 0)
{
n = 0;
for (i = 0; quarks[i] != 0; i++)
n++;
}
else
{
for (i = 0; i < n; i++)
g_return_if_fail (quarks[i] != 0);
}
if (array->len == 0)
{
/* fast-path for the common case: there's nothing to merge with */
g_array_append_vals (array, quarks, n);
return;
}
for (i = 0; i < n; i++)
{
for (j = 0; j < array->len; j++)
{
if (g_array_index (array, GQuark, j) == quarks[i])
goto next_i;
}
g_array_append_val (array, quarks[i]);
next_i:
continue;
}
}
/* Helper to implement functions with 0-terminated list of features in args */
void
_tp_quark_array_merge_valist (GArray *array,
GQuark feature,
va_list var_args)
{
GArray *features;
GQuark f;
features = g_array_new (FALSE, FALSE, sizeof (GQuark));
for (f = feature; f != 0; f = va_arg (var_args, GQuark))
g_array_append_val (features, f);
_tp_quark_array_merge (array, (GQuark *) features->data, features->len);
g_array_unref (features);
}
#ifdef HAVE_GIO_UNIX
GSocketAddress *
_tp_create_temp_unix_socket (GSocketService *service,
gchar **tmpdir,
GError **error)
{
GSocketAddress *address;
gchar *dir = g_dir_make_tmp ("tp-glib-socket.XXXXXX", error);
gchar *name;
if (dir == NULL)
return NULL;
if (g_chmod (dir, 0700) != 0)
{
int e = errno;
g_set_error (error, G_IO_ERROR, g_io_error_from_errno (e),
"unable to set permissions of %s to 0700: %s", dir,
g_strerror (e));
g_free (dir);
return NULL;
}
name = g_build_filename (dir, "s", NULL);
address = g_unix_socket_address_new (name);
g_free (name);
if (!g_socket_listener_add_address (G_SOCKET_LISTENER (service),
address, G_SOCKET_TYPE_STREAM,
G_SOCKET_PROTOCOL_DEFAULT,
NULL, NULL, error))
{
g_object_unref (address);
g_free (dir);
return NULL;
}
if (tmpdir != NULL)
*tmpdir = dir;
else
g_free (dir);
return address;
}
#endif /* HAVE_GIO_UNIX */
GList *
_tp_create_channel_request_list (TpSimpleClientFactory *factory,
GHashTable *request_props)
{
GHashTableIter iter;
GList *result = NULL;
gpointer key, value;
g_hash_table_iter_init (&iter, request_props);
while (g_hash_table_iter_next (&iter, &key, &value))
{
TpChannelRequest *req;
const gchar *path = key;
GHashTable *props = value;
GError *error = NULL;
req = _tp_simple_client_factory_ensure_channel_request (factory, path,
props, &error);
if (req == NULL)
{
DEBUG ("Failed to create TpChannelRequest: %s", error->message);
g_error_free (error);
continue;
}
result = g_list_prepend (result, req);
}
return result;
}
/**
* tp_utf8_make_valid:
* @name: string to coerce into UTF8
*
* Validate that the provided string is valid UTF8. If not,
* replace all invalid bytes with unicode replacement
* character (U+FFFD).
*
* This method is a verbatim copy of glib's internal
* _g_utf8_make_valid() function, and will be deprecated as
* soon as the glib one becomes public.
*
* Returns: (transfer full): a new valid UTF8 string
*
* Since: 0.13.15
*/
gchar *
tp_utf8_make_valid (const gchar *name)
{
GString *string;
const gchar *remainder, *invalid;
gint remaining_bytes, valid_bytes;
g_return_val_if_fail (name != NULL, NULL);
string = NULL;
remainder = name;
remaining_bytes = strlen (name);
while (remaining_bytes != 0)
{
if (g_utf8_validate (remainder, remaining_bytes, &invalid))
break;
valid_bytes = invalid - remainder;
if (string == NULL)
string = g_string_sized_new (remaining_bytes);
g_string_append_len (string, remainder, valid_bytes);
/* append U+FFFD REPLACEMENT CHARACTER */
g_string_append (string, "\357\277\275");
remaining_bytes -= valid_bytes + 1;
remainder = invalid + 1;
}
if (string == NULL)
return g_strdup (name);
g_string_append (string, remainder);
g_assert (g_utf8_validate (string->str, -1, NULL));
return g_string_free (string, FALSE);
}
/*
* _tp_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
_tp_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;
}
}
/*
* _tp_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 *
_tp_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;
}
/*
* _tp_enum_to_nick_nonnull:
*
* The same as _tp_enum_to_nick, but always returns non-NULL.
*/
const gchar *
_tp_enum_to_nick_nonnull (
GType enum_type,
gint value)
{
GEnumClass *klass = g_type_class_ref (enum_type);
GEnumValue *enum_value;
g_return_val_if_fail (klass != NULL, "(incorrect class)");
enum_value = g_enum_get_value (klass, value);
g_type_class_unref (klass);
if (enum_value == NULL)
return "(out-of-range value)";
else if (enum_value->value_nick == NULL)
return "(value with no nickname)";
else
return enum_value->value_nick;
}
gboolean
_tp_bind_connection_status_to_boolean (GBinding *binding,
const GValue *src_value,
GValue *dest_value,
gpointer user_data)
{
gboolean invert = GPOINTER_TO_UINT (user_data);
gboolean value;
g_return_val_if_fail (G_VALUE_HOLDS_UINT (src_value), FALSE);
g_return_val_if_fail (G_VALUE_HOLDS_BOOLEAN (dest_value), FALSE);
value = (g_value_get_uint (src_value) == TP_CONNECTION_STATUS_CONNECTED);
if (invert)
value = !value;
g_value_set_boolean (dest_value, value);
return TRUE;
}
/*
* _tp_determine_socket_address_type:
*
* Determines the best available socket address type.
*
*/
static gboolean
_tp_determine_socket_address_type (GHashTable *supported_sockets,
TpSocketAddressType *address_type,
GError **error)
{
guint i;
TpSocketAddressType types[] = {
#ifdef HAVE_GIO_UNIX
TP_SOCKET_ADDRESS_TYPE_UNIX,
#endif /* HAVE_GIO_UNIX */
TP_SOCKET_ADDRESS_TYPE_IPV4,
TP_SOCKET_ADDRESS_TYPE_IPV6
};
for (i = 0; i < G_N_ELEMENTS (types); i++)
{
GArray *arr = g_hash_table_lookup (supported_sockets,
GUINT_TO_POINTER (types[i]));
if (arr != NULL)
{
*address_type = types[i];
return TRUE;
}
}
/* This should never happen */
g_set_error (error, TP_ERROR,
TP_ERROR_NOT_IMPLEMENTED, "No supported socket types");
return FALSE;
}
/*
* _tp_determine_access_control_type:
*
* Determines the best available socket access control type, falling back to
* TP_SOCKET_ACCESS_CONTROL_LOCALHOST if needed.
*
*/
static gboolean
_tp_determine_access_control_type (GHashTable *supported_sockets,
TpSocketAddressType address_type,
TpSocketAccessControl *access_control,
GError **error)
{
gboolean support_localhost = FALSE;
GArray *arr;
guint i;
arr = g_hash_table_lookup (supported_sockets,
GUINT_TO_POINTER (address_type));
switch (address_type)
{
#ifdef HAVE_GIO_UNIX
case TP_SOCKET_ADDRESS_TYPE_UNIX:
case TP_SOCKET_ADDRESS_TYPE_ABSTRACT_UNIX:
{
/* Preferred order: credentials, localhost */
for (i = 0; i < arr->len; i++)
{
TpSocketAccessControl _access = g_array_index (arr,
TpSocketAccessControl, i);
if (_access == TP_SOCKET_ACCESS_CONTROL_CREDENTIALS)
{
*access_control = _access;
return TRUE;
}
else if (_access == TP_SOCKET_ACCESS_CONTROL_LOCALHOST)
{
support_localhost = TRUE;
}
}
}
break;
#else
case TP_SOCKET_ADDRESS_TYPE_UNIX:
case TP_SOCKET_ADDRESS_TYPE_ABSTRACT_UNIX:
break;
#endif
case TP_SOCKET_ADDRESS_TYPE_IPV6:
case TP_SOCKET_ADDRESS_TYPE_IPV4:
{
/* Preferred order: port, localhost */
for (i = 0; i < arr->len; i++)
{
TpSocketAccessControl _access = g_array_index (arr,
TpSocketAccessControl, i);
if (_access == TP_SOCKET_ACCESS_CONTROL_PORT)
{
*access_control = _access;
return TRUE;
}
else if (_access == TP_SOCKET_ACCESS_CONTROL_LOCALHOST)
{
support_localhost = TRUE;
}
}
}
break;
}
/* This should never happen */
if (!support_localhost)
{
g_set_error (error, TP_ERROR,
TP_ERROR_NOT_IMPLEMENTED, "No supported access control");
return FALSE;
}
*access_control = TP_SOCKET_ACCESS_CONTROL_LOCALHOST;
return TRUE;
}
gboolean
_tp_set_socket_address_type_and_access_control_type (
GHashTable *supported_sockets,
TpSocketAddressType *address_type,
TpSocketAccessControl *access_control,
GError **error)
{
g_return_val_if_fail (address_type != NULL, FALSE);
g_return_val_if_fail (access_control != NULL, FALSE);
if (!_tp_determine_socket_address_type (supported_sockets, address_type,
error))
return FALSE;
return _tp_determine_access_control_type (supported_sockets,
*address_type, access_control, error);
}
GSocket *
_tp_create_client_socket (TpSocketAddressType socket_type,
GError **error)
{
GSocket *client_socket;
GSocketFamily family;
switch (socket_type)
{
#ifdef HAVE_GIO_UNIX
case TP_SOCKET_ADDRESS_TYPE_UNIX:
family = G_SOCKET_FAMILY_UNIX;
break;
#endif
case TP_SOCKET_ADDRESS_TYPE_IPV4:
family = G_SOCKET_FAMILY_IPV4;
break;
case TP_SOCKET_ADDRESS_TYPE_IPV6:
family = G_SOCKET_FAMILY_IPV6;
break;
default:
g_assert_not_reached ();
}
/* Create socket to connect to the CM. We use a GSocket and not a
* GSocketClient because it creates the underlying socket when trying to
* connect and we need to be able to get the local port (needed for
* TP_SOCKET_ACCESS_CONTROL_PORT) of the socket before actually connecting. */
client_socket = g_socket_new (family, G_SOCKET_TYPE_STREAM,
G_SOCKET_PROTOCOL_DEFAULT, error);
if (client_socket == NULL)
return NULL;
if (socket_type == TP_SOCKET_ADDRESS_TYPE_IPV4 ||
socket_type == TP_SOCKET_ADDRESS_TYPE_IPV6)
{
/* Bind local address. This is needed to be able to get the local port
* of the socket and pass it to the CM when using
* TP_SOCKET_ACCESS_CONTROL_PORT. */
GSocketAddress *local_address;
GInetAddress *tmp;
gboolean success;
tmp = g_inet_address_new_any (family);
local_address = g_inet_socket_address_new (tmp, 0);
success = g_socket_bind (client_socket, local_address,
TRUE, error);
g_object_unref (tmp);
g_object_unref (local_address);
if (!success)
return NULL;
}
return client_socket;
}
gboolean
_tp_contacts_to_handles (TpConnection *connection,
guint n_contacts,
TpContact * const *contacts,
GArray **handles)
{
guint i;
g_return_val_if_fail (handles != NULL, FALSE);
g_return_val_if_fail (n_contacts > 0, FALSE);
*handles = g_array_sized_new (FALSE, FALSE, sizeof (TpHandle), n_contacts);
for (i = 0; i < n_contacts; i++)
{
TpHandle handle;
if (!TP_IS_CONTACT (contacts[i]) ||
tp_contact_get_connection (contacts[i]) != connection)
{
tp_clear_pointer (handles, g_array_unref);
return FALSE;
}
handle = tp_contact_get_handle (contacts[i]);
g_array_append_val (*handles, handle);
}
return TRUE;
}
/* table's key can be anything (usually TpHandle) but value must be a
* TpContact */
GPtrArray *
_tp_contacts_from_values (GHashTable *table)
{
GPtrArray *contacts;
GHashTableIter iter;
gpointer value;
if (table == NULL)
return NULL;
contacts = g_ptr_array_new_full (g_hash_table_size (table),
g_object_unref);
g_hash_table_iter_init (&iter, table);
while (g_hash_table_iter_next (&iter, NULL, &value))
{
if (value == NULL)
continue;
g_assert (TP_IS_CONTACT (value));
g_ptr_array_add (contacts, g_object_ref (value));
}
return contacts;
}
/*
* @l: (transfer none) (element-type GLib.Object): a list of #GObject or
* any subclass
*
* Returns: (transfer full): a copy of @l
*/
GList *
_tp_object_list_copy (GList *l)
{
return _tp_g_list_copy_deep (l, (GCopyFunc) g_object_ref, NULL);
}
/*
* @l: (transfer full) (element-type GLib.Object): a list of #GObject or
* any subclass
*
* Unref each item of @l and free the list.
*
* This function can be cast to #GDestroyNotify.
*/
void
_tp_object_list_free (GList *l)
{
g_list_free_full (l, g_object_unref);
}
GList *
_tp_g_list_copy_deep (GList *list,
GCopyFunc func,
gpointer user_data)
{
GList *ret = NULL;
GList *l;
ret = g_list_copy (list);
if (func != NULL)
{
for (l = ret; l != NULL; l = l->next)
l->data = func (l->data, user_data);
}
return ret;
}
/**
* tp_value_array_free:
* @va: a #GValueArray
*
* Free @va. This is exactly the same as g_value_array_free(), but does not
* provoke deprecation warnings from GLib when used in conjunction with
* tp_value_array_build() and tp_value_array_unpack().
*
* Since: 0.23.0
*/
void
(tp_value_array_free) (GValueArray *va)
{
_tp_value_array_free_inline (va);
}