#include "dbus-gvalue-utils.h"
#include <string.h>
+#include <gio/gio.h>
+
+G_GNUC_NORETURN static void
+oom (const gchar *explanation)
+{
+ g_error ("%s", explanation == NULL ? "Out of memory" : explanation);
+ g_assert_not_reached ();
+}
+
+static DBusMessage *
+reply_or_die (DBusMessage *in_reply_to)
+{
+ DBusMessage *reply;
+
+ g_return_val_if_fail (in_reply_to != NULL, NULL);
+
+ reply = dbus_message_new_method_return (in_reply_to);
+
+ if (reply == NULL)
+ oom ("dbus_message_new_method_return failed: out of memory?");
+
+ return reply;
+}
+
+static DBusMessage *
+error_or_die (DBusMessage *in_reply_to,
+ const gchar *error_name,
+ const gchar *error_message)
+{
+ DBusMessage *reply;
+
+ g_return_val_if_fail (in_reply_to != NULL, NULL);
+ /* error names are syntactically the same as interface names */
+ g_return_val_if_fail (g_dbus_is_interface_name (error_name), NULL);
+ g_return_val_if_fail (g_utf8_validate (error_message, -1, NULL), NULL);
+
+ reply = dbus_message_new_error (in_reply_to, error_name, error_message);
+
+ if (reply == NULL)
+ oom ("dbus_message_new_error failed: out of memory?");
+
+ return reply;
+}
+
+static void
+connection_send_or_die (DBusConnection *connection,
+ DBusMessage *message)
+{
+ g_return_if_fail (connection != NULL);
+ g_return_if_fail (message != NULL);
+
+ if (!dbus_connection_send (connection, message, NULL))
+ oom ("dbus_connection_send failed: out of memory?");
+}
+
static char *lookup_property_name (GObject *object,
const char *wincaps_propiface,
const char *requested_propname);
static char*
uscore_to_wincaps_full (const char *uscore,
- gboolean uppercase_first)
+ gboolean uppercase_first,
+ gboolean strip_underscores)
{
const char *p;
GString *str;
p = uscore;
while (p && *p)
{
- if (*p == '-' || *p == '_')
+ if (*p == '-' || (strip_underscores && *p == '_'))
{
last_was_uscore = TRUE;
}
static char *
uscore_to_wincaps (const char *uscore)
{
- return uscore_to_wincaps_full (uscore, TRUE);
+ return uscore_to_wincaps_full (uscore, TRUE, TRUE);
}
static const char *
RETVAL_ERROR
} RetvalType;
+/*
+ * arg_iterate:
+ * @data: a pointer to the beginning of an argument entry in a string table
+ * @name: (out) (allow-none): used to return the name of the next argument
+ * @in: (out) (allow-none): used to return %TRUE for an "in" argument or
+ * %FALSE for an "out" argument
+ * @constval: (out) (allow-none): used to return %TRUE if the argument is
+ * an "out" argument and has the "C" (const) flag indicating that it
+ * should not be freed after it is returned; normally, "out" arguments
+ * are freed
+ * @retval: (out) (allow-none): used to return %RETVAL_NONE if this
+ * D-Bus argument is not an "out" argument or is obtained like a C "out"
+ * parameter, %RETVAL_ERROR if this argument is obtained from the C
+ * return value and is also used to signal errors, or %RETVAL_NOERROR
+ * if this argument is obtained from the C return value and the method
+ * can never raise an error
+ * @type: (out) (allow-none): used to return the D-Bus signature of this
+ * argument
+ *
+ * The data format is:
+ *
+ * argument name
+ * \0
+ * direction: I or O
+ * \0
+ * if direction == "O":
+ * freeable? F or C
+ * \0
+ * retval? N, E or R
+ * \0
+ * signature
+ * \0
+ *
+ * If none of the arguments has @retval != %RETVAL_NONE, the method is
+ * assumed to return a gboolean, which behaves like %RETVAL_ERROR but is
+ * not sent over D-Bus at all.
+ *
+ * Returns: the value of @data to use for the next call, or a pointer to '\0'
+ * if this function must not be called again
+ */
static const char *
arg_iterate (const char *data,
const char **name,
}
typedef struct {
+ /* owned */
+ GSList *registrations;
+ /* weak ref, or NULL if the object has been disposed */
+ GObject *object;
+} ObjectExport;
+
+typedef struct {
+ /* pseudo-weak ref, never NULL */
DBusGConnection *connection;
+ /* owned, never NULL */
gchar *object_path;
- GObject *object;
+ /* borrowed pointer to parent, never NULL */
+ ObjectExport *export;
} ObjectRegistration;
-static void object_registration_object_died (gpointer user_data, GObject *dead);
+static void object_export_object_died (gpointer user_data, GObject *dead);
+
+static void
+object_export_unregister_all (ObjectExport *oe)
+{
+ while (oe->registrations != NULL)
+ {
+ GSList *old = oe->registrations;
+ ObjectRegistration *o = oe->registrations->data;
+
+ dbus_connection_unregister_object_path (
+ DBUS_CONNECTION_FROM_G_CONNECTION (o->connection), o->object_path);
+
+ /* the link should have been removed by doing that */
+ g_assert (oe->registrations != old);
+ }
+}
+
+static void
+object_export_free (ObjectExport *oe)
+{
+ g_slice_free (ObjectExport, oe);
+}
+
+static ObjectExport *
+object_export_new (void)
+{
+ return g_slice_new0 (ObjectExport);
+}
static ObjectRegistration *
object_registration_new (DBusGConnection *connection,
const gchar *object_path,
- GObject *object)
+ ObjectExport *export)
{
ObjectRegistration *o = g_slice_new0 (ObjectRegistration);
o->connection = connection;
o->object_path = g_strdup (object_path);
- o->object = object;
-
- g_object_weak_ref (o->object, object_registration_object_died, o);
+ o->export = export;
return o;
}
static void
object_registration_free (ObjectRegistration *o)
{
- if (o->object != NULL)
- {
- GSList *registrations;
-
- /* Ok, the object is still around; clear out this particular registration
- * from the registrations list.
- */
- registrations = g_object_steal_data (o->object, "dbus_glib_object_registrations");
- registrations = g_slist_remove (registrations, o);
-
- if (registrations != NULL)
- g_object_set_data (o->object, "dbus_glib_object_registrations", registrations);
-
- g_object_weak_unref (o->object, object_registration_object_died, o);
- }
+ g_assert (o->export != NULL);
+ o->export->registrations = g_slist_remove (o->export->registrations, o);
g_free (o->object_path);
for (i = 0; i < info->n_method_infos; i++)
{
- const char *method_name;
const char *method_interface;
- const char *method_args;
const DBusGMethodInfo *method;
method = &(info->method_infos[i]);
method_interface = method_interface_from_object_info (info, method);
- method_name = method_name_from_object_info (info, method);
- method_args = method_arg_info_from_object_info (info, method);
values = lookup_values (interfaces, method_interface);
values->methods = g_slist_prepend (values->methods, (gpointer) method);
if (!dbus_connection_list_registered (connection,
dbus_message_get_path (message),
&children))
- g_error ("Out of memory");
+ oom (NULL);
xml = g_string_new (NULL);
/* Close the XML, and send it to the requesting app */
g_string_append (xml, "</node>\n");
- ret = dbus_message_new_method_return (message);
- if (ret == NULL)
- g_error ("Out of memory");
+ ret = reply_or_die (message);
dbus_message_append_args (ret,
DBUS_TYPE_STRING, &xml->str,
DBUS_TYPE_INVALID);
- dbus_connection_send (connection, ret, NULL);
+ connection_send_or_die (connection, ret);
dbus_message_unref (ret);
g_string_free (xml, TRUE);
g_value_unset (&value);
- ret = dbus_message_new_method_return (message);
- if (ret == NULL)
- g_error ("out of memory");
+ ret = reply_or_die (message);
}
else
{
- ret = dbus_message_new_error (message,
- DBUS_ERROR_INVALID_ARGS,
- "Argument's D-BUS type can't be converted to a GType");
- if (ret == NULL)
- g_error ("out of memory");
+ ret = error_or_die (message,
+ DBUS_ERROR_INVALID_ARGS,
+ "Argument's D-BUS type can't be converted to a GType");
}
return ret;
}
+/*
+ * @pspec: the paramspec for a D-Bus-exported property
+ *
+ * Returns: a reply for the Get() D-Bus method, either successful or error
+ */
static DBusMessage*
get_object_property (DBusConnection *connection,
DBusMessage *message,
gchar *variant_sig;
DBusMessage *ret;
DBusMessageIter iter, subiter;
+ gchar *error_message = NULL;
- ret = dbus_message_new_method_return (message);
- if (ret == NULL)
- g_error ("out of memory");
-
+ ret = reply_or_die (message);
g_value_init (&value, pspec->value_type);
g_object_get_property (object, pspec->name, &value);
if (variant_sig == NULL)
{
value_gtype = G_VALUE_TYPE (&value);
- g_warning ("Cannot marshal type \"%s\" in variant", g_type_name (value_gtype));
- g_value_unset (&value);
- return ret;
+ error_message = g_strdup_printf (
+ "Internal error: cannot marshal type \"%s\" in variant",
+ g_type_name (value_gtype));
+ goto out;
}
dbus_message_iter_init_append (ret, &iter);
variant_sig,
&subiter))
{
- g_free (variant_sig);
- g_value_unset (&value);
- return ret;
+ error_message = g_strdup_printf (
+ "Internal error: cannot open variant container for signature %s",
+ variant_sig);
+ goto out;
}
if (!_dbus_gvalue_marshal (&subiter, &value))
{
- dbus_message_unref (ret);
- ret = dbus_message_new_error (message,
- DBUS_ERROR_UNKNOWN_METHOD,
- "Can't convert GType of object property to a D-BUS type");
+ dbus_message_iter_abandon_container (&iter, &subiter);
+ error_message = g_strdup_printf (
+ "Internal error: could not marshal type \"%s\" in variant",
+ G_VALUE_TYPE_NAME (&value));
+ goto out;
}
dbus_message_iter_close_container (&iter, &subiter);
+out:
g_value_unset (&value);
g_free (variant_sig);
+ if (error_message != NULL)
+ {
+ dbus_message_unref (ret);
+ ret = error_or_die (message, DBUS_ERROR_FAILED, error_message);
+ g_critical ("%s", error_message);
+ g_free (error_message);
+ }
+
return ret;
}
const char *p;
char *uscore_propname;
- ret = dbus_message_new_method_return (message);
- if (ret == NULL)
- goto oom;
+ ret = reply_or_die (message);
dbus_message_iter_init_append (ret, &iter_ret);
+ /* the types are all hard-coded, so this can only fail via OOM */
if (!dbus_message_iter_open_container (&iter_ret,
DBUS_TYPE_ARRAY,
DBUS_DICT_ENTRY_BEGIN_CHAR_AS_STRING
DBUS_TYPE_VARIANT_AS_STRING
DBUS_DICT_ENTRY_END_CHAR_AS_STRING,
&iter_dict))
- goto oom;
+ oom (NULL);
p = object_info->exported_properties;
while (p != NULL && *p != '\0')
p = property_iterate (p, object_info->format_version, &prop_ifname, &prop_name, &prop_uscored, &access_flags);
+ /* Conventionally, property names are valid member names, but dbus-glib
+ * doesn't enforce this, and some dbus-glib services use GObject-style
+ * property names (e.g. "foo-bar"). */
+ if (!g_utf8_validate (prop_name, -1, NULL))
+ {
+ g_critical ("property name isn't UTF-8: %s", prop_name);
+ continue;
+ }
+
uscore_propname = lookup_property_name (object, wincaps_propiface, prop_name);
pspec = g_object_class_find_property (G_OBJECT_GET_CLASS (object), uscore_propname);
continue;
}
+ /* a signature returned by _dbus_gvalue_to_signature had better be
+ * valid */
+ g_assert (g_variant_is_signature (variant_sig));
+
+ /* type is hard-coded, so this can't fail except by OOM */
if (!dbus_message_iter_open_container (&iter_dict,
DBUS_TYPE_DICT_ENTRY,
NULL,
&iter_dict_entry))
- goto oom;
+ oom (NULL);
+
+ /* prop_name is valid UTF-8, so this can't fail except by OOM; no point
+ * in abandoning @iter_dict_entry since we're about to crash out */
if (!dbus_message_iter_append_basic (&iter_dict_entry, DBUS_TYPE_STRING, &prop_name))
- goto oom;
+ oom (NULL);
+ /* variant_sig has been asserted to be valid, so this can't fail
+ * except by OOM */
if (!dbus_message_iter_open_container (&iter_dict_entry,
DBUS_TYPE_VARIANT,
variant_sig,
&iter_dict_value))
- goto oom;
+ oom (NULL);
+ g_free (variant_sig);
+
+ /* this can fail via programming error: the GObject property was
+ * malformed (non-UTF8 string or something) */
if (!_dbus_gvalue_marshal (&iter_dict_value, &value))
- goto oom;
+ {
+ gchar *contents = g_strdup_value_contents (&value);
+ gchar *error_message = g_strdup_printf (
+ "cannot GetAll(%s): failed to serialize %s value of type %s: %s",
+ wincaps_propiface, prop_name, G_VALUE_TYPE_NAME (&value),
+ contents);
+
+ g_critical ("%s", error_message);
+
+ /* abandon ship! */
+ dbus_message_iter_abandon_container (&iter_dict_entry,
+ &iter_dict_value);
+ dbus_message_iter_abandon_container (&iter_dict, &iter_dict_entry);
+ dbus_message_unref (ret);
+ ret = error_or_die (message, DBUS_ERROR_FAILED, error_message);
+
+ g_free (contents);
+ g_free (error_message);
+ g_value_unset (&value);
+ return ret;
+ }
+ /* these shouldn't fail except by OOM now that we were successful */
if (!dbus_message_iter_close_container (&iter_dict_entry,
&iter_dict_value))
- goto oom;
+ oom (NULL);
if (!dbus_message_iter_close_container (&iter_dict, &iter_dict_entry))
- goto oom;
+ oom (NULL);
g_value_unset (&value);
- g_free (variant_sig);
}
if (!dbus_message_iter_close_container (&iter_ret, &iter_dict))
- goto oom;
+ oom (NULL);
return ret;
-
- oom:
- g_error ("out of memory");
}
static gboolean
g_type_class_unref (klass);
domain_str = info->default_iface;
- code_str = value->value_nick;
+ if (value)
+ {
+ code_str = value->value_nick;
+ }
+ else
+ {
+ g_warning ("Error code %d out of range for GError domain %s",
+ code, g_quark_to_string (domain));
+ code_str = NULL;
+ }
}
}
g_free (uscored);
}
- g_string_append_printf (dbus_error_name, "Code%d", code);
+ /* Map -1 to (unsigned) -1 to avoid "-", which is not valid */
+ g_string_append_printf (dbus_error_name, "Code%u", (unsigned) code);
}
else
{
* reasons; if someone had a lowercase enumeration value,
* previously we'd just send it across unaltered.
*/
- code_str_wincaps = uscore_to_wincaps_full (code_str, FALSE);
+ code_str_wincaps = uscore_to_wincaps_full (code_str, FALSE, FALSE);
g_string_append (dbus_error_name, code_str_wincaps);
g_free (code_str_wincaps);
}
char *error_msg;
error_msg = g_strdup_printf ("Method invoked for %s returned FALSE but did not set error", dbus_message_get_member (message));
- reply = dbus_message_new_error (message, "org.freedesktop.DBus.GLib.ErrorError", error_msg);
+ reply = error_or_die (message, "org.freedesktop.DBus.GLib.ErrorError", error_msg);
g_free (error_msg);
}
else
break;
}
- reply = dbus_message_new_error (message, name, error->message);
+ reply = error_or_die (message, name, error->message);
}
else
{
error_name = gerror_domaincode_to_dbus_error_name (object_info,
dbus_message_get_interface (message),
error->domain, error->code);
- reply = dbus_message_new_error (message, error_name, error->message);
- g_free (error_name);
- }
+ reply = error_or_die (message, error_name, error->message);
+ g_free (error_name);
+ }
}
+
return reply;
}
*/
/**
+ * DBusGMethodInvocation:
+ *
* The context of an asynchronous method call. See dbus_g_method_return() and
* dbus_g_method_return_error().
*/
GValueArray *out_param_gvalues = NULL;
int out_param_count;
int out_param_pos, out_param_gvalue_pos;
- DBusHandlerResult result;
DBusMessage *reply = NULL;
gboolean have_retval;
gboolean retval_signals_error;
{
g_free (in_signature);
g_array_free (types_array, TRUE);
- reply = dbus_message_new_error (message, "org.freedesktop.DBus.GLib.ErrorError", error->message);
- dbus_connection_send (connection, reply, NULL);
+ reply = error_or_die (message, "org.freedesktop.DBus.GLib.ErrorError", error->message);
+ connection_send_or_die (connection, reply);
dbus_message_unref (reply);
g_error_free (error);
return DBUS_HANDLER_RESULT_HANDLED;
NULL, method->function);
if (is_async)
{
- result = DBUS_HANDLER_RESULT_HANDLED;
goto done;
}
+
if (retval_signals_error)
had_error = _dbus_gvalue_signals_error (&return_value);
else
*/
if (send_reply)
{
- reply = dbus_message_new_method_return (message);
- if (reply == NULL)
- goto nomem;
+ reply = reply_or_die (message);
/* Append output arguments to reply */
dbus_message_iter_init_append (reply, &iter);
/* First, append the return value, unless it's synthetic */
if (have_retval && !retval_is_synthetic)
- {
- if (send_reply && !_dbus_gvalue_marshal (&iter, &return_value))
- goto nomem;
+ {
+ if (reply != NULL && !_dbus_gvalue_marshal (&iter, &return_value))
+ {
+ gchar *desc = g_strdup_value_contents (&return_value);
+
+ g_critical ("unable to append retval of type %s for %s: %s",
+ G_VALUE_TYPE_NAME (&return_value),
+ method_name_from_object_info (object_info, method),
+ desc);
+ g_free (desc);
+ /* the reply is now unusable but we still need to free
+ * everything */
+ dbus_message_unref (reply);
+ reply = NULL;
+ }
+
if (!retval_is_constant)
g_value_unset (&return_value);
}
g_value_set_static_boxed (&gvalue, out_param_gvalues->values + out_param_gvalue_pos);
out_param_gvalue_pos++;
}
-
- if (send_reply && !_dbus_gvalue_marshal (&iter, &gvalue))
- goto nomem;
+
+ if (reply && !_dbus_gvalue_marshal (&iter, &gvalue))
+ {
+ gchar *desc = g_strdup_value_contents (&gvalue);
+
+ g_critical ("unable to append OUT arg of type %s for %s: %s",
+ G_VALUE_TYPE_NAME (&gvalue),
+ method_name_from_object_info (object_info, method),
+ desc);
+ g_free (desc);
+ /* the reply is now unusable but we still need to free
+ * everything */
+ dbus_message_unref (reply);
+ reply = NULL;
+ }
+
/* Here we actually free the allocated value; we
* took ownership of it with _dbus_gvalue_take, unless
* an annotation has specified this value as constant.
if (reply)
{
- dbus_connection_send (connection, reply, NULL);
+ connection_send_or_die (connection, reply);
dbus_message_unref (reply);
}
- result = DBUS_HANDLER_RESULT_HANDLED;
- done:
+done:
g_free (in_signature);
+
if (!is_async)
{
g_array_free (out_param_values, TRUE);
g_clear_error (&gerror);
g_value_array_free (value_array);
- return result;
- nomem:
- result = DBUS_HANDLER_RESULT_NEED_MEMORY;
- goto done;
+ return DBUS_HANDLER_RESULT_HANDLED;
}
+/*
+ * @wincaps_propiface: the D-Bus interface name, conventionally WindowsCaps
+ * @requested_propname: the D-Bus property name, conventionally WindowsCaps
+ * @uscore_propname: the GObject property name, conventionally
+ * words_with_underscores or words-with-dashes
+ * @is_set: %TRUE if we're going to set the property, %FALSE if we're going
+ * to get it
+ *
+ * Check that the requested property exists and the requested access is
+ * allowed. If not, reply with a D-Bus AccessDenied error message.
+ *
+ * Returns: %TRUE if property access can continue, or %FALSE if an error
+ * reply has been sent
+ */
static gboolean
check_property_access (DBusConnection *connection,
DBusMessage *message,
const DBusGObjectInfo *object_info;
const char *access_type;
DBusMessage *ret;
+ gchar *error_message;
if (!is_set && !disable_legacy_property_access)
return TRUE;
object_info = lookup_object_info_by_iface (object, wincaps_propiface, TRUE, NULL);
if (!object_info)
{
- ret = dbus_message_new_error_printf (message,
- DBUS_ERROR_ACCESS_DENIED,
- "Interface \"%s\" isn't exported (or may not exist), can't access property \"%s\"",
- wincaps_propiface,
- requested_propname);
- dbus_connection_send (connection, ret, NULL);
- dbus_message_unref (ret);
- return FALSE;
+ error_message = g_strdup_printf (
+ "Interface \"%s\" isn't exported (or may not exist), can't access property \"%s\"",
+ wincaps_propiface, requested_propname);
+
+ goto error;
}
/* Try both forms of property names: "foo_bar" or "FooBar"; for historical
&& !(property_info_from_object_info (object_info, wincaps_propiface, requested_propname, &access_type)
|| property_info_from_object_info (object_info, wincaps_propiface, uscore_propname, &access_type)))
{
- ret = dbus_message_new_error_printf (message,
- DBUS_ERROR_ACCESS_DENIED,
- "Property \"%s\" of interface \"%s\" isn't exported (or may not exist)",
- requested_propname,
- wincaps_propiface);
- dbus_connection_send (connection, ret, NULL);
- dbus_message_unref (ret);
- return FALSE;
+ error_message = g_strdup_printf (
+ "Property \"%s\" of interface \"%s\" isn't exported (or may not exist)",
+ requested_propname, wincaps_propiface);
+
+ goto error;
}
if (strcmp (access_type, "readwrite") == 0)
return TRUE;
- else if (is_set ? strcmp (access_type, "read") == 0
+
+ if (is_set ? strcmp (access_type, "read") == 0
: strcmp (access_type, "write") == 0)
{
- ret = dbus_message_new_error_printf (message,
- DBUS_ERROR_ACCESS_DENIED,
- "Property \"%s\" of interface \"%s\" is not %s",
- requested_propname,
- wincaps_propiface,
- is_set ? "settable" : "readable");
- dbus_connection_send (connection, ret, NULL);
- dbus_message_unref (ret);
- return FALSE;
+ error_message = g_strdup_printf (
+ "Property \"%s\" of interface \"%s\" is not %s",
+ requested_propname,
+ wincaps_propiface,
+ is_set ? "settable" : "readable");
+
+ goto error;
}
+
return TRUE;
+
+error:
+ ret = error_or_die (message, DBUS_ERROR_ACCESS_DENIED, error_message);
+ g_free (error_message);
+
+ connection_send_or_die (connection, ret);
+ dbus_message_unref (ret);
+ return FALSE;
}
static DBusHandlerResult
ObjectRegistration *o;
o = user_data;
- object = G_OBJECT (o->object);
+ /* export is always non-NULL. If the object has been disposed, the weak-ref
+ * callback removes all registrations from the DBusConnection, so this
+ * should never be reached with object = NULL. */
+ object = G_OBJECT (o->export->object);
+ g_assert (object != NULL);
if (dbus_message_is_method_call (message,
DBUS_INTERFACE_INTROSPECTABLE,
if (dbus_message_iter_get_arg_type (&iter) != DBUS_TYPE_STRING)
{
- g_warning ("Property get or set does not have an interface string as first arg\n");
- return DBUS_HANDLER_RESULT_NOT_YET_HANDLED;
+ ret = error_or_die (message, DBUS_ERROR_INVALID_ARGS,
+ "First argument to Get(), GetAll() or Set() must be an interface string");
+ goto out;
}
dbus_message_iter_get_basic (&iter, &wincaps_propiface);
else
return DBUS_HANDLER_RESULT_NOT_YET_HANDLED;
}
- else if (getter || setter)
+ else
{
+ g_assert (getter || setter);
+
if (dbus_message_iter_get_arg_type (&iter) != DBUS_TYPE_STRING)
{
- g_warning ("Property get or set does not have a property name string as second arg\n");
- return DBUS_HANDLER_RESULT_NOT_YET_HANDLED;
+ ret = error_or_die (message, DBUS_ERROR_INVALID_ARGS,
+ "Second argument to Get() or Set() must be a property name string");
+ goto out;
}
+
dbus_message_iter_get_basic (&iter, &requested_propname);
dbus_message_iter_next (&iter);
{
if (dbus_message_iter_get_arg_type (&iter) != DBUS_TYPE_VARIANT)
{
- g_warning ("Property set does not have a variant value as third arg\n");
- return DBUS_HANDLER_RESULT_NOT_YET_HANDLED;
+ ret = error_or_die (message, DBUS_ERROR_INVALID_ARGS,
+ "Third argument to Set() must be a variant");
+ goto out;
}
ret = set_object_property (connection, message, &iter,
object, pspec);
dbus_message_iter_next (&iter);
}
- else if (getter)
+ else
{
+ g_assert (getter);
ret = get_object_property (connection, message,
object, pspec);
}
- else
- {
- g_assert_not_reached ();
- ret = NULL;
- }
}
else
{
- ret = dbus_message_new_error_printf (message,
- DBUS_ERROR_INVALID_ARGS,
- "No such property %s", requested_propname);
+ gchar *error_message = g_strdup_printf ("No such property %s",
+ requested_propname);
+
+ ret = error_or_die (message, DBUS_ERROR_INVALID_ARGS, error_message);
+ g_free (error_message);
}
}
g_assert (ret != NULL);
+ /* FIXME: this should be returned as a D-Bus error, not spammed out
+ * as a warning. This is too late to do that, though - we've already
+ * had any side-effects we were going to have - and it would break
+ * anything that's relying on ability to give us too many arguments. */
if (dbus_message_iter_get_arg_type (&iter) != DBUS_TYPE_INVALID)
g_warning ("Property get, set or set all had too many arguments\n");
- dbus_connection_send (connection, ret, NULL);
+out:
+ connection_send_or_die (connection, ret);
dbus_message_unref (ret);
return DBUS_HANDLER_RESULT_HANDLED;
}
} DBusGSignalClosure;
static GClosure *
-dbus_g_signal_closure_new (DBusGConnection *connection,
- GObject *object,
+dbus_g_signal_closure_new (GObject *object,
const char *signame,
const char *sigiface)
{
closure = (DBusGSignalClosure*) g_closure_new_simple (sizeof (DBusGSignalClosure), NULL);
- closure->connection = dbus_g_connection_ref (connection);
closure->object = object;
closure->signame = signame;
closure->sigiface = sigiface;
}
static void
-dbus_g_signal_closure_finalize (gpointer data,
- GClosure *closure)
-{
- DBusGSignalClosure *sigclosure = (DBusGSignalClosure *) closure;
-
- dbus_g_connection_unref (sigclosure->connection);
-}
-
-static void
emit_signal_for_registration (ObjectRegistration *o,
DBusGSignalClosure *sigclosure,
GValue *retval,
DBusMessageIter iter;
guint i;
+ g_assert (g_variant_is_object_path (o->object_path));
+ g_assert (g_dbus_is_interface_name (sigclosure->sigiface));
+ g_assert (g_dbus_is_member_name (sigclosure->signame));
+
signal = dbus_message_new_signal (o->object_path,
sigclosure->sigiface,
sigclosure->signame);
if (!signal)
- {
- g_error ("out of memory");
- return;
- }
+ oom (NULL);
dbus_message_iter_init_append (signal, &iter);
goto out;
}
}
- dbus_connection_send (DBUS_CONNECTION_FROM_G_CONNECTION (sigclosure->connection),
- signal, NULL);
+
+ connection_send_or_die (DBUS_CONNECTION_FROM_G_CONNECTION (o->connection),
+ signal);
out:
dbus_message_unref (signal);
}
gpointer marshal_data)
{
DBusGSignalClosure *sigclosure;
- GSList *registrations, *iter;
+ const ObjectExport *oe;
+ const GSList *iter;
sigclosure = (DBusGSignalClosure *) closure;
g_assert (retval == NULL);
- registrations = g_object_get_data (sigclosure->object, "dbus_glib_object_registrations");
+ oe = g_object_get_data (sigclosure->object, "dbus_glib_object_registrations");
+ /* If the object has ever been exported, this should exist; it persists until
+ * the object is actually freed. */
+ g_assert (oe != NULL);
- for (iter = registrations; iter; iter = iter->next)
+ for (iter = oe->registrations; iter; iter = iter->next)
{
ObjectRegistration *o = iter->data;
}
static void
-export_signals (DBusGConnection *connection, const GList *info_list, GObject *object)
+export_signals (const GList *info_list, GObject *object)
{
GType gtype;
const char *sigdata;
sigdata = signal_iterate (sigdata, &iface, &signame);
+ if (!g_dbus_is_interface_name (iface))
+ {
+ g_critical ("invalid interface name found in %s: %s",
+ g_type_name (gtype), iface);
+ continue;
+ }
+
+ if (!g_dbus_is_member_name (signame))
+ {
+ g_critical ("invalid signal name found in %s: %s",
+ g_type_name (gtype), signame);
+ continue;
+ }
+
s = _dbus_gutils_wincaps_to_uscore (signame);
id = g_signal_lookup (s, gtype);
continue; /* FIXME: these could be listed as methods ? */
}
- closure = dbus_g_signal_closure_new (connection, object, signame, (char*) iface);
+ closure = dbus_g_signal_closure_new (object, signame, (char*) iface);
g_closure_set_marshal (closure, signal_emitter_marshaller);
g_signal_connect_closure_by_id (object,
closure,
FALSE);
- g_closure_add_finalize_notifier (closure, NULL,
- dbus_g_signal_closure_finalize);
g_free (s);
}
}
/**
* dbus_set_g_error:
* @gerror: an error
- * @error: a #DBusError
+ * @derror: a #DBusError
*
* Store the information from a DBus method error return into a
* GError. For the normal case of an arbitrary remote process,
*/
void
dbus_set_g_error (GError **gerror,
- DBusError *error)
+ DBusError *derror)
{
int code;
- code = dbus_error_to_gerror_code (error->name);
+ g_return_if_fail (derror != NULL);
+ g_return_if_fail (dbus_error_is_set (derror));
+ g_return_if_fail (gerror == NULL || *gerror == NULL);
+
+ code = dbus_error_to_gerror_code (derror->name);
if (code != DBUS_GERROR_REMOTE_EXCEPTION)
g_set_error (gerror, DBUS_GERROR,
code,
"%s",
- error->message);
+ derror->message);
else
g_set_error (gerror, DBUS_GERROR,
code,
"%s%c%s",
- error->message ? error->message : "",
+ derror->message ? derror->message : "",
'\0',
- error->name);
+ derror->name);
}
static void
* class_init() for the object class.
*
* Once introspection information has been installed, instances of the
- * object registered with #dbus_g_connection_register_g_object() can have
+ * object registered with dbus_g_connection_register_g_object() can have
* their methods invoked remotely.
*/
void
/* Called when the object is destroyed */
static void
-object_registration_object_died (gpointer user_data, GObject *dead)
+object_export_object_died (gpointer user_data, GObject *dead)
{
- ObjectRegistration *o = user_data;
+ ObjectExport *oe = user_data;
- g_assert (dead == o->object);
+ g_assert (dead == oe->object);
/* this prevents the weak unref from taking place, which would cause an
* assertion failure since the object has already gone... */
- o->object = NULL;
+ oe->object = NULL;
- /* ... while this results in a call to object_registration_unregistered */
- dbus_connection_unregister_object_path (DBUS_CONNECTION_FROM_G_CONNECTION (o->connection), o->object_path);
+ /* ... while this results in a call to object_registration_unregistered
+ * for each contained registration */
+ object_export_unregister_all (oe);
+
+ /* We deliberately don't remove the ObjectExport yet, in case the object is
+ * resurrected and re-registered: if that happens, we wouldn't want to call
+ * export_signals() again. */
}
/**
* @connection: the D-BUS connection
* @object: the object
*
- * Removes @object from the bus. Properties, methods, and signals
+ * Removes @object from any object paths at which it is exported on
+ * @connection. Properties, methods, and signals
* of the object can no longer be accessed remotely.
*/
void
dbus_g_connection_unregister_g_object (DBusGConnection *connection,
GObject *object)
{
- GList *registrations, *iter;
+ ObjectExport *oe;
+ GSList *registrations;
+
+ g_return_if_fail (connection != NULL);
+ g_return_if_fail (G_IS_OBJECT (object));
+
+ oe = g_object_get_data (object, "dbus_glib_object_registrations");
+
+ g_return_if_fail (oe != NULL);
+ g_return_if_fail (oe->registrations != NULL);
/* Copy the list before iterating it: it will be modified in
* object_registration_free() each time an object path is unregistered.
*/
- registrations = g_list_copy (g_object_get_data (object, "dbus_glib_object_registrations"));
+ for (registrations = g_slist_copy (oe->registrations);
+ registrations != NULL;
+ registrations = g_slist_delete_link (registrations, registrations))
+ {
+ ObjectRegistration *o = registrations->data;
- g_return_if_fail (registrations != NULL);
+ if (o->connection != connection)
+ continue;
- for (iter = registrations; iter; iter = iter->next)
- {
- ObjectRegistration *o = iter->data;
dbus_connection_unregister_object_path (DBUS_CONNECTION_FROM_G_CONNECTION (o->connection),
o->object_path);
}
-
- g_list_free (registrations);
- g_assert (g_object_get_data (object, "dbus_glib_object_registrations") == NULL);
}
/**
const char *at_path,
GObject *object)
{
- GList *info_list;
- GSList *registrations, *iter;
+ ObjectExport *oe;
+ GSList *iter;
ObjectRegistration *o;
- gboolean is_first_registration;
+ DBusError error;
g_return_if_fail (connection != NULL);
- g_return_if_fail (at_path != NULL);
+ g_return_if_fail (g_variant_is_object_path (at_path));
g_return_if_fail (G_IS_OBJECT (object));
- /* This is a GSList of ObjectRegistration* */
- registrations = g_object_steal_data (object, "dbus_glib_object_registrations");
+ oe = g_object_get_data (object, "dbus_glib_object_registrations");
- for (iter = registrations; iter; iter = iter->next)
+ if (oe == NULL)
{
- o = iter->data;
-
- /* Silently ignore duplicate registrations */
- if (strcmp (o->object_path, at_path) == 0 && o->connection == connection)
- return;
- }
-
- is_first_registration = registrations == NULL;
+ GList *info_list = lookup_object_info (object);
- /* This is used to hook up signals below, but we do this check
- * before trying to register the object to make sure we have
- * introspection data for it.
- */
- if (is_first_registration)
- {
- info_list = lookup_object_info (object);
if (info_list == NULL)
{
g_warning ("No introspection data registered for object class \"%s\"",
g_type_name (G_TYPE_FROM_INSTANCE (object)));
return;
}
+
+ /* This adds a hook into every signal for the object. Only do this
+ * on the first registration, because inside the signal marshaller
+ * we emit a signal for each registration.
+ */
+ export_signals (info_list, object);
+ g_list_free (info_list);
+
+ oe = object_export_new ();
+ g_object_set_data_full (object, "dbus_glib_object_registrations", oe,
+ (GDestroyNotify) object_export_free);
}
- else
- info_list = NULL;
- o = object_registration_new (connection, at_path, object);
+ if (oe->object == NULL)
+ {
+ /* Either the ObjectExport is newly-created, or it already existed but
+ * the object was disposed and resurrected, causing the weak ref to
+ * fall off */
+ oe->object = object;
+ g_object_weak_ref (object, object_export_object_died, oe);
+ }
- if (!dbus_connection_register_object_path (DBUS_CONNECTION_FROM_G_CONNECTION (connection),
- at_path,
- &gobject_dbus_vtable,
- o))
+ for (iter = oe->registrations; iter; iter = iter->next)
{
- g_error ("Failed to register GObject with DBusConnection");
- object_registration_free (o);
- g_list_free (info_list);
- return;
+ o = iter->data;
+
+ /* Silently ignore duplicate registrations */
+ if (strcmp (o->object_path, at_path) == 0 && o->connection == connection)
+ return;
}
- if (is_first_registration)
+ o = object_registration_new (connection, at_path, oe);
+
+ dbus_error_init (&error);
+ if (!dbus_connection_try_register_object_path (DBUS_CONNECTION_FROM_G_CONNECTION (connection),
+ at_path,
+ &gobject_dbus_vtable,
+ o,
+ &error))
{
- /* This adds a hook into every signal for the object. Only do this
- * on the first registration, because inside the signal marshaller
- * we emit a signal for each registration.
- */
- export_signals (connection, info_list, object);
- g_list_free (info_list);
+ g_error ("Failed to register GObject with DBusConnection: %s %s",
+ error.name, error.message);
+ dbus_error_free (&error);
+ object_registration_free (o);
+ return;
}
- registrations = g_slist_append (registrations, o);
- g_object_set_data (object, "dbus_glib_object_registrations", registrations);
+ oe->registrations = g_slist_append (oe->registrations, o);
}
/**
gpointer p;
ObjectRegistration *o;
+ g_return_val_if_fail (connection != NULL, NULL);
+ g_return_val_if_fail (g_variant_is_object_path (at_path), NULL);
+
if (!dbus_connection_get_object_path_data (DBUS_CONNECTION_FROM_G_CONNECTION (connection), at_path, &p))
return NULL;
return NULL;
o = p;
- return G_OBJECT (o->object);
+
+ if (o->export->object == NULL)
+ return NULL;
+
+ return G_OBJECT (o->export->object);
}
typedef struct {
* dbus_g_object_register_marshaller:
* @marshaller: a GClosureMarshal to be used for invocation
* @rettype: a GType for the return type of the function
- * @:... The parameter #GTypes, followed by %G_TYPE_INVALID
+ * @...: The parameter #GTypes, followed by %G_TYPE_INVALID
*
* Register a #GClosureMarshal to be used for signal invocations,
* giving its return type and a list of parameter types,
* @types: a C array of GTypes values
*
* Register a #GClosureMarshal to be used for signal invocations.
- * @see_also #dbus_g_object_register_marshaller
+ * @see_also dbus_g_object_register_marshaller()
*/
void
dbus_g_object_register_marshaller_array (GClosureMarshal marshaller,
{
const gchar *sender;
+ g_return_val_if_fail (context != NULL, NULL);
+
sender = dbus_message_get_sender (dbus_g_message_get_message (context->message));
return g_strdup (sender);
}
DBusMessage *
dbus_g_method_get_reply (DBusGMethodInvocation *context)
{
- return dbus_message_new_method_return (dbus_g_message_get_message (context->message));
+ g_return_val_if_fail (context != NULL, NULL);
+
+ return reply_or_die (dbus_g_message_get_message (context->message));
}
/**
* dbus_g_method_send_reply:
- * Send a manually created reply message
* @context: the method context
* @reply: the reply message, will be unreffed
*
+ * Send a manually created reply message.
+ *
* Used as a sidedoor when you can't generate dbus values
* of the correct type due to glib binding limitations
*/
void
dbus_g_method_send_reply (DBusGMethodInvocation *context, DBusMessage *reply)
{
- dbus_connection_send (dbus_g_connection_get_connection (context->connection), reply, NULL);
+ g_return_if_fail (context != NULL);
+ g_return_if_fail (reply != NULL);
+
+ connection_send_or_die (dbus_g_connection_get_connection (context->connection),
+ reply);
dbus_message_unref (reply);
dbus_g_connection_unref (context->connection);
/**
* dbus_g_method_return:
* @context: the method context
+ * @...: zero or more values to return from the method, with their number
+ * and types given by its #DBusGObjectInfo
*
* Send a return message for a given method invocation, with arguments.
* This function also frees the sending context.
char *out_sig;
GArray *argsig;
guint i;
-
+
+ g_return_if_fail (context != NULL);
+
/* This field was initialized inside invoke_object_method; we
* carry it over through the async invocation to here.
*/
if (!context->send_reply)
goto out;
- reply = dbus_message_new_method_return (dbus_g_message_get_message (context->message));
+ reply = dbus_g_method_get_reply (context);
out_sig = method_output_signature_from_object_info (context->object, context->method);
argsig = _dbus_gtypes_from_arg_signature (out_sig, FALSE);
g_warning("%s", error);
g_free (error);
}
- _dbus_gvalue_marshal (&iter, &value);
+ else
+ {
+ if (!_dbus_gvalue_marshal (&iter, &value))
+ g_warning ("failed to marshal parameter %d for method %s",
+ i, dbus_message_get_member (
+ dbus_g_message_get_message (context->message)));
+ }
}
va_end (args);
- dbus_connection_send (dbus_g_connection_get_connection (context->connection), reply, NULL);
+ connection_send_or_die (dbus_g_connection_get_connection (context->connection),
+ reply);
dbus_message_unref (reply);
g_free (out_sig);
{
DBusMessage *reply;
+ g_return_if_fail (context != NULL);
+ g_return_if_fail (error != NULL);
+
/* See comment in dbus_g_method_return */
if (!context->send_reply)
goto out;
reply = gerror_to_dbus_error_message (context->object, dbus_g_message_get_message (context->message), error);
- dbus_connection_send (dbus_g_connection_get_connection (context->connection), reply, NULL);
+ connection_send_or_die (
+ dbus_g_connection_get_connection (context->connection), reply);
dbus_message_unref (reply);
out:
const char *
_dbus_gobject_get_path (GObject *obj)
{
- GSList *registrations;
+ ObjectExport *oe;
ObjectRegistration *o;
- registrations = g_object_get_data (obj, "dbus_glib_object_registrations");
+ oe = g_object_get_data (obj, "dbus_glib_object_registrations");
- if (registrations == NULL)
+ if (oe == NULL || oe->registrations == NULL)
return NULL;
/* First one to have been registered wins */
- o = registrations->data;
+ o = oe->registrations->data;
return o->object_path;
}
};
-/**
- * @ingroup DBusGLibInternals
+/*
* Unit test for GLib GObject integration ("skeletons")
- * Returns: #TRUE on success.
+ * Returns: %TRUE on success.
*/
gboolean
_dbus_gobject_test (const char *test_data_dir)