summaryrefslogtreecommitdiff
path: root/common/gvfsdbusutils.c
diff options
context:
space:
mode:
Diffstat (limited to 'common/gvfsdbusutils.c')
-rw-r--r--common/gvfsdbusutils.c1333
1 files changed, 1333 insertions, 0 deletions
diff --git a/common/gvfsdbusutils.c b/common/gvfsdbusutils.c
new file mode 100644
index 00000000..9945ba81
--- /dev/null
+++ b/common/gvfsdbusutils.c
@@ -0,0 +1,1333 @@
+/* GIO - GLib Input, Output and Streaming Library
+ *
+ * Copyright (C) 2006-2007 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 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., 59 Temple Place, Suite 330,
+ * Boston, MA 02111-1307, USA.
+ *
+ * Author: Alexander Larsson <alexl@redhat.com>
+ */
+
+#include <config.h>
+
+#include <stdlib.h>
+#include <string.h>
+
+#include <glib/gi18n-lib.h>
+#include <gvfsdbusutils.h>
+#include <gio/gio.h>
+
+void
+_g_dbus_oom (void)
+{
+ g_error ("DBus failed with out of memory error");
+}
+
+/* We use _ for escaping, so its not valid */
+#define VALID_INITIAL_NAME_CHARACTER(c) \
+ ( ((c) >= 'A' && (c) <= 'Z') || \
+ ((c) >= 'a' && (c) <= 'z') )
+#define VALID_NAME_CHARACTER(c) \
+ ( ((c) >= '0' && (c) <= '9') || \
+ ((c) >= 'A' && (c) <= 'Z') || \
+ ((c) >= 'a' && (c) <= 'z'))
+
+
+static void
+append_escaped_name (GString *s,
+ const char *unescaped)
+{
+ char c;
+ gboolean first;
+ static const gchar hex[16] = "0123456789ABCDEF";
+
+ first = TRUE;
+ while ((c = *unescaped++) != 0)
+ {
+ if (first)
+ {
+ if (VALID_INITIAL_NAME_CHARACTER (c))
+ {
+ g_string_append_c (s, c);
+ continue;
+ }
+ }
+ else
+ {
+ if (VALID_NAME_CHARACTER (c))
+ {
+ g_string_append_c (s, c);
+ continue;
+ }
+ }
+
+ first = FALSE;
+ g_string_append_c (s, '_');
+ g_string_append_c (s, hex[((guchar)c) >> 4]);
+ g_string_append_c (s, hex[((guchar)c) & 0xf]);
+ }
+}
+
+DBusMessage *
+_dbus_message_new_gerror (DBusMessage *message,
+ GQuark domain,
+ gint code,
+ const gchar *format,
+ ...)
+{
+ DBusMessage *reply;
+ va_list args;
+ GError error;
+
+ error.domain = domain;
+ error.code = code;
+ va_start (args, format);
+ error.message = g_strdup_vprintf (format, args);
+ va_end (args);
+
+ reply = _dbus_message_new_from_gerror (message, &error);
+
+ g_free (error.message);
+
+ return reply;
+}
+
+DBusMessage *
+_dbus_message_new_from_gerror (DBusMessage *message,
+ GError *error)
+{
+ DBusMessage *reply;
+ GString *str;
+
+ str = g_string_new ("org.glib.GError.");
+ append_escaped_name (str, g_quark_to_string (error->domain));
+ g_string_append_printf (str, ".c%d", error->code);
+ reply = dbus_message_new_error (message, str->str, error->message);
+ g_string_free (str, TRUE);
+ return reply;
+}
+
+gboolean
+_g_error_from_message (DBusMessage *message,
+ GError **error)
+{
+ DBusError derror;
+
+ dbus_error_init (&derror);
+ if (dbus_set_error_from_message (&derror, message))
+ {
+ _g_error_from_dbus (&derror, error);
+ dbus_error_free (&derror);
+ return TRUE;
+ }
+ return FALSE;
+}
+
+
+
+static void
+append_unescaped_dbus_name (GString *s,
+ const char *escaped,
+ const char *end)
+{
+ guchar c;
+
+ while (escaped < end)
+ {
+ c = *escaped++;
+ if (c == '_' &&
+ escaped < end)
+ {
+ c = g_ascii_xdigit_value (*escaped++) << 4;
+
+ if (escaped < end)
+ c |= g_ascii_xdigit_value (*escaped++);
+ }
+ g_string_append_c (s, c);
+ }
+}
+
+void
+_g_dbus_message_iter_append_cstring (DBusMessageIter *iter, const char *str)
+{
+ DBusMessageIter array;
+
+ if (str == NULL)
+ str = "";
+
+ if (!dbus_message_iter_open_container (iter,
+ DBUS_TYPE_ARRAY,
+ DBUS_TYPE_BYTE_AS_STRING,
+ &array))
+ _g_dbus_oom ();
+
+ if (!dbus_message_iter_append_fixed_array (&array,
+ DBUS_TYPE_BYTE,
+ &str, strlen (str)))
+ _g_dbus_oom ();
+
+ if (!dbus_message_iter_close_container (iter, &array))
+ _g_dbus_oom ();
+}
+
+void
+_g_dbus_message_iter_append_args_valist (DBusMessageIter *iter,
+ int first_arg_type,
+ va_list var_args)
+{
+ int type;
+
+ g_return_if_fail (iter != NULL);
+
+ type = first_arg_type;
+
+ while (type != DBUS_TYPE_INVALID)
+ {
+ if (type == G_DBUS_TYPE_CSTRING)
+ {
+ const char **value_p;
+ const char *value;
+
+ value_p = va_arg (var_args, const char**);
+ value = *value_p;
+
+ _g_dbus_message_iter_append_cstring (iter, value);
+ }
+ else if (dbus_type_is_basic (type))
+ {
+ const void *value;
+ value = va_arg (var_args, const void*);
+
+ if (!dbus_message_iter_append_basic (iter,
+ type,
+ value))
+ _g_dbus_oom ();
+ }
+ else if (type == DBUS_TYPE_ARRAY)
+ {
+ int element_type;
+ DBusMessageIter array;
+ char buf[2];
+
+ element_type = va_arg (var_args, int);
+
+ buf[0] = element_type;
+ buf[1] = '\0';
+ if (!dbus_message_iter_open_container (iter,
+ DBUS_TYPE_ARRAY,
+ buf,
+ &array))
+ _g_dbus_oom ();
+
+ if (dbus_type_is_fixed (element_type))
+ {
+ const void **value;
+ int n_elements;
+
+ value = va_arg (var_args, const void**);
+ n_elements = va_arg (var_args, int);
+
+ if (!dbus_message_iter_append_fixed_array (&array,
+ element_type,
+ value,
+ n_elements))
+ _g_dbus_oom ();
+ }
+ else if (element_type == DBUS_TYPE_STRING ||
+ element_type == DBUS_TYPE_SIGNATURE ||
+ element_type == DBUS_TYPE_OBJECT_PATH)
+ {
+ const char ***value_p;
+ const char **value;
+ int n_elements;
+ int i;
+
+ value_p = va_arg (var_args, const char***);
+ n_elements = va_arg (var_args, int);
+
+ value = *value_p;
+
+ i = 0;
+ while (i < n_elements)
+ {
+ if (!dbus_message_iter_append_basic (&array,
+ element_type,
+ &value[i]))
+ _g_dbus_oom ();
+ ++i;
+ }
+ }
+ else
+ {
+ g_error ("arrays of %d can't be appended with _g_dbus_message_append_args_valist for now\n",
+ element_type);
+ }
+
+ if (!dbus_message_iter_close_container (iter, &array))
+ _g_dbus_oom ();
+ }
+
+ type = va_arg (var_args, int);
+ }
+}
+
+
+void
+_g_dbus_message_append_args_valist (DBusMessage *message,
+ int first_arg_type,
+ va_list var_args)
+{
+ int type;
+ DBusMessageIter iter;
+
+ g_return_if_fail (message != NULL);
+
+ type = first_arg_type;
+
+ dbus_message_iter_init_append (message, &iter);
+
+ _g_dbus_message_iter_append_args_valist (&iter,
+ first_arg_type,
+ var_args);
+}
+
+dbus_bool_t
+_g_dbus_message_iter_get_args_valist (DBusMessageIter *iter,
+ DBusError *error,
+ int first_arg_type,
+ va_list var_args)
+{
+ int spec_type, msg_type, i, dbus_spec_type;
+ dbus_bool_t retval;
+
+
+ retval = FALSE;
+
+ spec_type = first_arg_type;
+ i = 0;
+
+ while (spec_type != DBUS_TYPE_INVALID)
+ {
+ msg_type = dbus_message_iter_get_arg_type (iter);
+
+ if (spec_type == G_DBUS_TYPE_CSTRING)
+ dbus_spec_type = DBUS_TYPE_ARRAY;
+ else
+ dbus_spec_type = spec_type;
+
+ if (msg_type != dbus_spec_type)
+ {
+ dbus_set_error (error, DBUS_ERROR_INVALID_ARGS,
+ "Argument %d is specified to be of type \"%c\", but "
+ "is actually of type \"%c\"\n", i,
+ spec_type,
+ msg_type);
+
+ goto out;
+ }
+
+ if (spec_type == G_DBUS_TYPE_CSTRING)
+ {
+ int element_type;
+ char **ptr;
+ const char *str;
+ int n_elements;
+ DBusMessageIter array;
+
+ element_type = dbus_message_iter_get_element_type (iter);
+ if (DBUS_TYPE_BYTE != element_type)
+ {
+ dbus_set_error (error, DBUS_ERROR_INVALID_ARGS,
+ "Argument %d is specified to be an array of \"char\", but "
+ "is actually an array of \"%d\"\n",
+ i,
+ element_type);
+ goto out;
+ }
+
+ ptr = va_arg (var_args, char**);
+ g_assert (ptr != NULL);
+
+ dbus_message_iter_recurse (iter, &array);
+ dbus_message_iter_get_fixed_array (&array,
+ &str, &n_elements);
+ *ptr = g_strndup (str, n_elements);
+ }
+ else if (dbus_type_is_basic (spec_type))
+ {
+ void *ptr;
+
+ ptr = va_arg (var_args, void*);
+
+ g_assert (ptr != NULL);
+
+ dbus_message_iter_get_basic (iter, ptr);
+ }
+ else if (spec_type == DBUS_TYPE_ARRAY)
+ {
+ int element_type;
+ int spec_element_type;
+ const void **ptr;
+ int *n_elements_p;
+ DBusMessageIter array;
+
+ spec_element_type = va_arg (var_args, int);
+ element_type = dbus_message_iter_get_element_type (iter);
+
+ if (spec_element_type != element_type)
+ {
+ dbus_set_error (error, DBUS_ERROR_INVALID_ARGS,
+ "Argument %d is specified to be an array of \"%d\", but "
+ "is actually an array of \"%d\"\n",
+ i,
+ spec_element_type,
+ element_type);
+
+ goto out;
+ }
+
+ if (dbus_type_is_fixed (spec_element_type))
+ {
+ ptr = va_arg (var_args, const void**);
+ n_elements_p = va_arg (var_args, int*);
+
+ g_assert (ptr != NULL);
+ g_assert (n_elements_p != NULL);
+
+ dbus_message_iter_recurse (iter, &array);
+
+ dbus_message_iter_get_fixed_array (&array,
+ ptr, n_elements_p);
+ }
+ else if (spec_element_type == DBUS_TYPE_STRING ||
+ spec_element_type == DBUS_TYPE_SIGNATURE ||
+ spec_element_type == DBUS_TYPE_OBJECT_PATH)
+ {
+ char ***str_array_p;
+ int n_elements;
+ char **str_array;
+
+ str_array_p = va_arg (var_args, char***);
+ n_elements_p = va_arg (var_args, int*);
+
+ g_assert (str_array_p != NULL);
+ g_assert (n_elements_p != NULL);
+
+ /* Count elements in the array */
+ dbus_message_iter_recurse (iter, &array);
+
+ n_elements = 0;
+ while (dbus_message_iter_get_arg_type (&array) != DBUS_TYPE_INVALID)
+ {
+ ++n_elements;
+ dbus_message_iter_next (&array);
+ }
+
+ str_array = g_new0 (char*, n_elements + 1);
+ if (str_array == NULL)
+ {
+ _g_dbus_oom ();
+ goto out;
+ }
+
+ /* Now go through and dup each string */
+ dbus_message_iter_recurse (iter, &array);
+
+ i = 0;
+ while (i < n_elements)
+ {
+ const char *s;
+ dbus_message_iter_get_basic (&array, &s);
+
+ str_array[i] = g_strdup (s);
+ if (str_array[i] == NULL)
+ {
+ g_strfreev (str_array);
+ _g_dbus_oom ();
+ goto out;
+ }
+
+ ++i;
+
+ if (!dbus_message_iter_next (&array))
+ g_assert (i == n_elements);
+ }
+
+ g_assert (dbus_message_iter_get_arg_type (&array) == DBUS_TYPE_INVALID);
+ g_assert (i == n_elements);
+ g_assert (str_array[i] == NULL);
+
+ *str_array_p = str_array;
+ *n_elements_p = n_elements;
+ }
+ }
+
+ spec_type = va_arg (var_args, int);
+ if (!dbus_message_iter_next (iter) && spec_type != DBUS_TYPE_INVALID)
+ {
+ dbus_set_error (error, DBUS_ERROR_INVALID_ARGS,
+ "Message has only %d arguments, but more were expected", i);
+ goto out;
+ }
+
+ i++;
+ }
+
+ retval = TRUE;
+
+ out:
+ return retval;
+}
+
+dbus_bool_t
+_g_dbus_message_iter_get_args (DBusMessageIter *iter,
+ DBusError *error,
+ int first_arg_type,
+ ...)
+{
+ va_list var_args;
+ dbus_bool_t res;
+
+ va_start (var_args, first_arg_type);
+ res = _g_dbus_message_iter_get_args_valist (iter, error,
+ first_arg_type,
+ var_args);
+ va_end (var_args);
+ return res;
+}
+
+/* Same as the dbus one, except doesn't give OOM and handles
+ G_DBUS_TYPE_CSTRING
+*/
+void
+_g_dbus_message_append_args (DBusMessage *message,
+ int first_arg_type,
+ ...)
+{
+ va_list var_args;
+
+ g_return_if_fail (message != NULL);
+
+ va_start (var_args, first_arg_type);
+ _g_dbus_message_append_args_valist (message,
+ first_arg_type,
+ var_args);
+ va_end (var_args);
+}
+
+void
+_g_dbus_message_iter_append_args (DBusMessageIter *iter,
+ int first_arg_type,
+ ...)
+{
+ va_list var_args;
+
+ g_return_if_fail (iter != NULL);
+
+ va_start (var_args, first_arg_type);
+ _g_dbus_message_iter_append_args_valist (iter,
+ first_arg_type,
+ var_args);
+ va_end (var_args);
+}
+
+void
+_g_error_from_dbus (DBusError *derror,
+ GError **error)
+{
+ const char *name, *end;
+ GString *str;
+ GQuark domain;
+ int code;
+
+ if (g_str_has_prefix (derror->name, "org.glib.GError."))
+ {
+ domain = 0;
+ code = 0;
+
+ name = derror->name + strlen ("org.glib.GError.");
+ end = strchr (name, '.');
+ if (end)
+ {
+ str = g_string_new (NULL);
+ append_unescaped_dbus_name (str, name, end);
+ domain = g_quark_from_string (str->str);
+ g_string_free (str, TRUE);
+
+ end++; /* skip . */
+ if (*end++ == 'c')
+ code = atoi (end);
+ }
+
+ g_set_error_literal (error, domain, code, derror->message);
+ }
+ /* TODO: Special case other types, like DBUS_ERROR_NO_MEMORY etc? */
+ else
+ g_set_error (error, G_IO_ERROR, G_IO_ERROR_FAILED,
+ "DBus error %s: %s", derror->name, derror->message);
+}
+
+GList *
+_g_dbus_bus_list_names_with_prefix (DBusConnection *connection,
+ const char *prefix,
+ DBusError *error)
+{
+ DBusMessage *message, *reply;
+ DBusMessageIter iter, array;
+ GList *names;
+
+ g_return_val_if_fail (connection != NULL, NULL);
+
+ message = dbus_message_new_method_call (DBUS_SERVICE_DBUS,
+ DBUS_PATH_DBUS,
+ DBUS_INTERFACE_DBUS,
+ "ListNames");
+ if (message == NULL)
+ return NULL;
+
+ reply = dbus_connection_send_with_reply_and_block (connection, message, -1, error);
+ dbus_message_unref (message);
+
+ if (reply == NULL)
+ return NULL;
+
+ names = NULL;
+
+ if (!dbus_message_iter_init (reply, &iter) ||
+ (dbus_message_iter_get_arg_type (&iter) != DBUS_TYPE_ARRAY) ||
+ (dbus_message_iter_get_element_type (&iter) != DBUS_TYPE_STRING))
+ goto out;
+
+ for (dbus_message_iter_recurse (&iter, &array);
+ dbus_message_iter_get_arg_type (&array) == DBUS_TYPE_STRING;
+ dbus_message_iter_next (&array))
+ {
+ char *name;
+ dbus_message_iter_get_basic (&array, &name);
+ if (g_str_has_prefix (name, prefix))
+ names = g_list_prepend (names, g_strdup (name));
+ }
+
+ names = g_list_reverse (names);
+
+ out:
+ dbus_message_unref (reply);
+ return names;
+}
+
+/*************************************************************************
+ * Helper fd source *
+ ************************************************************************/
+
+typedef struct
+{
+ GSource source;
+ GPollFD pollfd;
+ GCancellable *cancellable;
+ gulong cancelled_tag;
+} FDSource;
+
+static gboolean
+fd_source_prepare (GSource *source,
+ gint *timeout)
+{
+ FDSource *fd_source = (FDSource *)source;
+ *timeout = -1;
+
+ return g_cancellable_is_cancelled (fd_source->cancellable);
+}
+
+static gboolean
+fd_source_check (GSource *source)
+{
+ FDSource *fd_source = (FDSource *)source;
+
+ return
+ g_cancellable_is_cancelled (fd_source->cancellable) ||
+ fd_source->pollfd.revents != 0;
+}
+
+static gboolean
+fd_source_dispatch (GSource *source,
+ GSourceFunc callback,
+ gpointer user_data)
+
+{
+ GFDSourceFunc func = (GFDSourceFunc)callback;
+ FDSource *fd_source = (FDSource *)source;
+
+ g_assert (func != NULL);
+
+ return (*func) (user_data, fd_source->pollfd.revents, fd_source->pollfd.fd);
+}
+
+static void
+fd_source_finalize (GSource *source)
+{
+ FDSource *fd_source = (FDSource *)source;
+
+ if (fd_source->cancelled_tag)
+ g_cancellable_disconnect (fd_source->cancellable,
+ fd_source->cancelled_tag);
+
+ if (fd_source->cancellable)
+ g_object_unref (fd_source->cancellable);
+}
+
+static GSourceFuncs fd_source_funcs = {
+ fd_source_prepare,
+ fd_source_check,
+ fd_source_dispatch,
+ fd_source_finalize
+};
+
+/* Might be called on another thread */
+static void
+fd_source_cancelled_cb (GCancellable *cancellable,
+ gpointer data)
+{
+ /* Wake up the mainloop in case we're waiting on async calls with FDSource */
+ g_main_context_wakeup (NULL);
+}
+
+/* Two __ to avoid conflict with gio version */
+GSource *
+__g_fd_source_new (int fd,
+ gushort events,
+ GCancellable *cancellable)
+{
+ GSource *source;
+ FDSource *fd_source;
+
+ source = g_source_new (&fd_source_funcs, sizeof (FDSource));
+ fd_source = (FDSource *)source;
+
+ if (cancellable)
+ fd_source->cancellable = g_object_ref (cancellable);
+
+ fd_source->pollfd.fd = fd;
+ fd_source->pollfd.events = events;
+ g_source_add_poll (source, &fd_source->pollfd);
+
+ if (cancellable)
+ fd_source->cancelled_tag =
+ g_cancellable_connect (cancellable,
+ (GCallback)fd_source_cancelled_cb,
+ NULL, NULL);
+
+ return source;
+}
+
+
+/*************************************************************************
+ * *
+ * dbus mainloop integration for async ops *
+ * *
+ *************************************************************************/
+
+static gint32 main_integration_data_slot = -1;
+static GOnce once_init_main_integration = G_ONCE_INIT;
+
+/**
+ * A GSource subclass for dispatching DBusConnection messages.
+ * We need this on top of the IO handlers, because sometimes
+ * there are messages to dispatch queued up but no IO pending.
+ *
+ * The source is owned by the connection (and the main context
+ * while that is alive)
+ */
+typedef struct
+{
+ GSource source;
+
+ DBusConnection *connection;
+ GSList *ios;
+ GSList *timeouts;
+} DBusSource;
+
+typedef struct
+{
+ DBusSource *dbus_source;
+ GSource *source;
+ DBusWatch *watch;
+} IOHandler;
+
+typedef struct
+{
+ DBusSource *dbus_source;
+ GSource *source;
+ DBusTimeout *timeout;
+} TimeoutHandler;
+
+static gpointer
+main_integration_init (gpointer arg)
+{
+ if (!dbus_connection_allocate_data_slot (&main_integration_data_slot))
+ g_error ("Unable to allocate data slot");
+
+ return NULL;
+}
+
+static gboolean
+dbus_source_prepare (GSource *source,
+ gint *timeout)
+{
+ DBusConnection *connection = ((DBusSource *)source)->connection;
+
+ *timeout = -1;
+
+ return (dbus_connection_get_dispatch_status (connection) == DBUS_DISPATCH_DATA_REMAINS);
+}
+
+static gboolean
+dbus_source_check (GSource *source)
+{
+ return FALSE;
+}
+
+static gboolean
+dbus_source_dispatch (GSource *source,
+ GSourceFunc callback,
+ gpointer user_data)
+{
+ DBusConnection *connection = ((DBusSource *)source)->connection;
+
+ dbus_connection_ref (connection);
+
+ /* Only dispatch once - we don't want to starve other GSource */
+ dbus_connection_dispatch (connection);
+
+ dbus_connection_unref (connection);
+
+ return TRUE;
+}
+
+static gboolean
+io_handler_dispatch (gpointer data,
+ GIOCondition condition,
+ int fd)
+{
+ IOHandler *handler = data;
+ guint dbus_condition = 0;
+ DBusConnection *connection;
+
+ connection = handler->dbus_source->connection;
+
+ if (connection)
+ dbus_connection_ref (connection);
+
+ if (condition & G_IO_IN)
+ dbus_condition |= DBUS_WATCH_READABLE;
+ if (condition & G_IO_OUT)
+ dbus_condition |= DBUS_WATCH_WRITABLE;
+ if (condition & G_IO_ERR)
+ dbus_condition |= DBUS_WATCH_ERROR;
+ if (condition & G_IO_HUP)
+ dbus_condition |= DBUS_WATCH_HANGUP;
+
+ /* Note that we don't touch the handler after this, because
+ * dbus may have disabled the watch and thus killed the
+ * handler.
+ */
+ dbus_watch_handle (handler->watch, dbus_condition);
+ handler = NULL;
+
+ if (connection)
+ dbus_connection_unref (connection);
+
+ return TRUE;
+}
+
+static void
+io_handler_free (IOHandler *handler)
+{
+ DBusSource *dbus_source;
+
+ dbus_source = handler->dbus_source;
+ dbus_source->ios = g_slist_remove (dbus_source->ios, handler);
+
+ g_source_destroy (handler->source);
+ g_source_unref (handler->source);
+ g_free (handler);
+}
+
+static void
+dbus_source_add_watch (DBusSource *dbus_source,
+ DBusWatch *watch)
+{
+ guint flags;
+ GIOCondition condition;
+ IOHandler *handler;
+ int fd;
+
+ if (!dbus_watch_get_enabled (watch))
+ return;
+
+ g_assert (dbus_watch_get_data (watch) == NULL);
+
+ flags = dbus_watch_get_flags (watch);
+
+ condition = G_IO_ERR | G_IO_HUP;
+ if (flags & DBUS_WATCH_READABLE)
+ condition |= G_IO_IN;
+ if (flags & DBUS_WATCH_WRITABLE)
+ condition |= G_IO_OUT;
+
+ handler = g_new0 (IOHandler, 1);
+ handler->dbus_source = dbus_source;
+ handler->watch = watch;
+
+#if (DBUS_MAJOR_VERSION == 1 && DBUS_MINOR_VERSION == 1 && DBUS_MICRO_VERSION >= 1) || (DBUS_MAJOR_VERSION == 1 && DBUS_MINOR_VERSION > 1) || (DBUS_MAJOR_VERSION > 1)
+ fd = dbus_watch_get_unix_fd (watch);
+#else
+ fd = dbus_watch_get_fd (watch);
+#endif
+
+ handler->source = __g_fd_source_new (fd, condition, NULL);
+ g_source_set_callback (handler->source,
+ (GSourceFunc) io_handler_dispatch, handler,
+ NULL);
+ g_source_attach (handler->source, NULL);
+
+ dbus_source->ios = g_slist_prepend (dbus_source->ios, handler);
+ dbus_watch_set_data (watch, handler,
+ (DBusFreeFunction)io_handler_free);
+}
+
+static void
+dbus_source_remove_watch (DBusSource *dbus_source,
+ DBusWatch *watch)
+{
+ dbus_watch_set_data (watch, NULL, NULL);
+}
+
+static void
+timeout_handler_free (TimeoutHandler *handler)
+{
+ DBusSource *dbus_source;
+
+ dbus_source = handler->dbus_source;
+ dbus_source->timeouts = g_slist_remove (dbus_source->timeouts, handler);
+
+ g_source_destroy (handler->source);
+ g_source_unref (handler->source);
+ g_free (handler);
+}
+
+static gboolean
+timeout_handler_dispatch (gpointer data)
+{
+ TimeoutHandler *handler = data;
+
+ dbus_timeout_handle (handler->timeout);
+
+ return TRUE;
+}
+
+static void
+dbus_source_add_timeout (DBusSource *dbus_source,
+ DBusTimeout *timeout)
+{
+ TimeoutHandler *handler;
+
+ if (!dbus_timeout_get_enabled (timeout))
+ return;
+
+ g_assert (dbus_timeout_get_data (timeout) == NULL);
+
+ handler = g_new0 (TimeoutHandler, 1);
+ handler->dbus_source = dbus_source;
+ handler->timeout = timeout;
+
+ handler->source = g_timeout_source_new (dbus_timeout_get_interval (timeout));
+ g_source_set_callback (handler->source,
+ timeout_handler_dispatch, handler,
+ NULL);
+ g_source_attach (handler->source, NULL);
+
+ /* handler->source is owned by the context here */
+ dbus_source->timeouts = g_slist_prepend (dbus_source->timeouts, handler);
+
+ dbus_timeout_set_data (timeout, handler,
+ (DBusFreeFunction)timeout_handler_free);
+}
+
+static void
+dbus_source_remove_timeout (DBusSource *source,
+ DBusTimeout *timeout)
+{
+ dbus_timeout_set_data (timeout, NULL, NULL);
+}
+
+static dbus_bool_t
+add_watch (DBusWatch *watch,
+ gpointer data)
+{
+ DBusSource *dbus_source = data;
+
+ dbus_source_add_watch (dbus_source, watch);
+
+ return TRUE;
+}
+
+static void
+remove_watch (DBusWatch *watch,
+ gpointer data)
+{
+ DBusSource *dbus_source = data;
+
+ dbus_source_remove_watch (dbus_source, watch);
+}
+
+static void
+watch_toggled (DBusWatch *watch,
+ void *data)
+{
+ /* Because we just exit on OOM, enable/disable is
+ * no different from add/remove */
+ if (dbus_watch_get_enabled (watch))
+ add_watch (watch, data);
+ else
+ remove_watch (watch, data);
+}
+
+static dbus_bool_t
+add_timeout (DBusTimeout *timeout,
+ void *data)
+{
+ DBusSource *source = data;
+
+ if (!dbus_timeout_get_enabled (timeout))
+ return TRUE;
+
+ dbus_source_add_timeout (source, timeout);
+
+ return TRUE;
+}
+
+static void
+remove_timeout (DBusTimeout *timeout,
+ void *data)
+{
+ DBusSource *source = data;
+
+ dbus_source_remove_timeout (source, timeout);
+}
+
+static void
+timeout_toggled (DBusTimeout *timeout,
+ void *data)
+{
+ /* Because we just exit on OOM, enable/disable is
+ * no different from add/remove
+ */
+ if (dbus_timeout_get_enabled (timeout))
+ add_timeout (timeout, data);
+ else
+ remove_timeout (timeout, data);
+}
+
+static void
+wakeup_main (void *data)
+{
+ g_main_context_wakeup (NULL);
+}
+
+static const GSourceFuncs dbus_source_funcs = {
+ dbus_source_prepare,
+ dbus_source_check,
+ dbus_source_dispatch
+};
+
+/* Called when the connection dies or when we're unintegrating from mainloop */
+static void
+dbus_source_free (DBusSource *dbus_source)
+{
+ while (dbus_source->ios)
+ {
+ IOHandler *handler = dbus_source->ios->data;
+
+ dbus_watch_set_data (handler->watch, NULL, NULL);
+ }
+
+ while (dbus_source->timeouts)
+ {
+ TimeoutHandler *handler = dbus_source->timeouts->data;
+
+ dbus_timeout_set_data (handler->timeout, NULL, NULL);
+ }
+
+ /* Remove from mainloop */
+ g_source_destroy ((GSource *)dbus_source);
+
+ g_source_unref ((GSource *)dbus_source);
+}
+
+void
+_g_dbus_connection_integrate_with_main (DBusConnection *connection)
+{
+ DBusSource *dbus_source;
+
+ g_once (&once_init_main_integration, main_integration_init, NULL);
+
+ g_assert (connection != NULL);
+
+ _g_dbus_connection_remove_from_main (connection);
+
+ dbus_source = (DBusSource *)
+ g_source_new ((GSourceFuncs*)&dbus_source_funcs,
+ sizeof (DBusSource));
+
+ dbus_source->connection = connection;
+
+ if (!dbus_connection_set_watch_functions (connection,
+ add_watch,
+ remove_watch,
+ watch_toggled,
+ dbus_source, NULL))
+ _g_dbus_oom ();
+
+ if (!dbus_connection_set_timeout_functions (connection,
+ add_timeout,
+ remove_timeout,
+ timeout_toggled,
+ dbus_source, NULL))
+ _g_dbus_oom ();
+
+ dbus_connection_set_wakeup_main_function (connection,
+ wakeup_main,
+ dbus_source, NULL);
+
+ /* Owned by both connection and mainloop (until destroy) */
+ g_source_attach ((GSource *)dbus_source, NULL);
+
+ if (!dbus_connection_set_data (connection,
+ main_integration_data_slot,
+ dbus_source, (DBusFreeFunction)dbus_source_free))
+ _g_dbus_oom ();
+}
+
+void
+_g_dbus_connection_remove_from_main (DBusConnection *connection)
+{
+ g_once (&once_init_main_integration, main_integration_init, NULL);
+
+ if (!dbus_connection_set_data (connection,
+ main_integration_data_slot,
+ NULL, NULL))
+ _g_dbus_oom ();
+}
+
+void
+_g_dbus_message_iter_copy (DBusMessageIter *dest,
+ DBusMessageIter *source)
+{
+ int type, element_type;
+
+ while (dbus_message_iter_get_arg_type (source) != DBUS_TYPE_INVALID)
+ {
+ type = dbus_message_iter_get_arg_type (source);
+
+ if (dbus_type_is_basic (type))
+ {
+ dbus_uint64_t value;
+ dbus_message_iter_get_basic (source, &value);
+ dbus_message_iter_append_basic (dest, type, &value);
+ }
+ else if (type == DBUS_TYPE_ARRAY)
+ {
+ DBusMessageIter source_array, dest_array;
+ void *value;
+ int n_elements;
+ char buf[2];
+
+ element_type = dbus_message_iter_get_element_type (source);
+ if (dbus_type_is_fixed (element_type))
+ {
+ buf[0] = element_type;
+ buf[1] = '\0';
+
+ dbus_message_iter_recurse (source, &source_array);
+ dbus_message_iter_get_fixed_array (&source_array, &value, &n_elements);
+
+ if (!dbus_message_iter_open_container (dest, DBUS_TYPE_ARRAY,
+ buf, &dest_array))
+ _g_dbus_oom ();
+
+ if (!dbus_message_iter_append_fixed_array (&dest_array,
+ element_type,
+ &value, n_elements))
+ _g_dbus_oom ();
+
+ if (!dbus_message_iter_close_container (dest, &dest_array))
+ _g_dbus_oom ();
+ }
+ else
+ g_error ("Unsupported array type %c in _g_dbus_message_iter_copy", element_type);
+ }
+ else
+ g_error ("Unsupported type %c in _g_dbus_message_iter_copy", type);
+
+ dbus_message_iter_next (source);
+ }
+
+}
+
+typedef struct {
+ GAsyncDBusCallback callback;
+ gpointer user_data;
+ GError *io_error;
+
+
+ /* protected by async_call lock: */
+ gboolean ran; /* the pending_call reply handler ran */
+ gboolean idle; /* we queued an idle */
+
+ /* only used for idle */
+ DBusPendingCall *pending;
+} AsyncDBusCallData;
+
+/* Lock to protect the data for working around racecondition
+ between send_with_reply and pending_set_notify */
+G_LOCK_DEFINE_STATIC(async_call);
+
+static void
+handle_async_reply (DBusPendingCall *pending,
+ AsyncDBusCallData *data)
+{
+ DBusMessage *reply;
+ GError *error;
+
+ reply = dbus_pending_call_steal_reply (pending);
+
+ error = NULL;
+ if (_g_error_from_message (reply, &error))
+ {
+ if (data->callback)
+ data->callback (NULL, error, data->user_data);
+ g_error_free (error);
+ }
+ else
+ {
+ if (data->callback)
+ data->callback (reply, NULL, data->user_data);
+ }
+
+ dbus_message_unref (reply);
+}
+
+static void
+async_call_reply (DBusPendingCall *pending,
+ void *_data)
+{
+ AsyncDBusCallData *data = _data;
+
+ G_LOCK (async_call);
+ if (data->idle)
+ return;
+ data->ran = TRUE;
+ G_UNLOCK (async_call);
+
+ handle_async_reply (pending, data);
+}
+
+static gboolean
+idle_async_callback (void *_data)
+{
+ AsyncDBusCallData *data = _data;
+ handle_async_reply (data->pending, data);
+ dbus_pending_call_unref (data->pending);
+ return FALSE;
+}
+
+static gboolean
+async_call_error_at_idle (gpointer _data)
+{
+ AsyncDBusCallData *data = _data;
+
+ if (data->callback)
+ data->callback (NULL, data->io_error, data->user_data);
+
+ g_error_free (data->io_error);
+ g_free (data);
+
+ return FALSE;
+}
+
+void
+_g_dbus_connection_call_async (DBusConnection *connection,
+ DBusMessage *message,
+ int timeout_msecs,
+ GAsyncDBusCallback callback,
+ gpointer user_data)
+{
+ AsyncDBusCallData *data;
+ DBusPendingCall *pending_call;
+ DBusError derror;
+
+ data = g_new0 (AsyncDBusCallData, 1);
+ data->callback = callback;
+ data->user_data = user_data;
+
+ if (connection == NULL)
+ {
+ dbus_error_init (&derror);
+ connection = dbus_bus_get (DBUS_BUS_SESSION, &derror);
+ if (connection == NULL)
+ {
+ g_set_error_literal (&data->io_error, G_IO_ERROR, G_IO_ERROR_FAILED,
+ "Can't open dbus connection");
+ g_idle_add (async_call_error_at_idle, data);
+ dbus_error_free (&derror);
+ return;
+ }
+ }
+
+ if (!dbus_connection_send_with_reply (connection, message, &pending_call, timeout_msecs))
+ _g_dbus_oom ();
+
+ if (pending_call == NULL)
+ {
+ g_set_error (&data->io_error, G_IO_ERROR, G_IO_ERROR_FAILED,
+ "Error while getting peer-to-peer dbus connection: %s",
+ "Connection is closed");
+ g_idle_add (async_call_error_at_idle, data);
+ return;
+ }
+
+ if (!dbus_pending_call_set_notify (pending_call,
+ async_call_reply,
+ data, g_free))
+ _g_dbus_oom ();
+
+
+ /* All this is required to work around a race condition between
+ * send_with_reply and pending_call_set_notify :/
+ */
+ G_LOCK (async_call);
+
+ if (dbus_pending_call_get_completed (pending_call) &&
+ !data->ran)
+ {
+ data->idle = TRUE;
+ data->pending = dbus_pending_call_ref (pending_call);
+ g_idle_add (idle_async_callback, data);
+ }
+
+ G_UNLOCK (async_call);
+
+
+ dbus_pending_call_unref (pending_call);
+}