diff --git a/docs/reference/gio/gio-sections.txt b/docs/reference/gio/gio-sections.txt index f2f97bf0d..391d8460c 100644 --- a/docs/reference/gio/gio-sections.txt +++ b/docs/reference/gio/gio-sections.txt @@ -2334,6 +2334,9 @@ g_dbus_message_new_method_error g_dbus_message_new_method_error_valist g_dbus_message_new_method_error_literal g_dbus_message_print +g_dbus_message_get_locked +g_dbus_message_lock +g_dbus_message_copy g_dbus_message_get_byte_order g_dbus_message_set_byte_order g_dbus_message_get_message_type diff --git a/gio/gdbusmessage.c b/gio/gdbusmessage.c index d29edbe0f..c6181050c 100644 --- a/gio/gdbusmessage.c +++ b/gio/gdbusmessage.c @@ -92,6 +92,8 @@ struct _GDBusMessage GDBusMessageType type; GDBusMessageFlags flags; + gboolean locked; + GDBusMessageByteOrder byte_order; guchar major_protocol_version; guint32 serial; GHashTable *headers; @@ -99,7 +101,12 @@ struct _GDBusMessage #ifdef G_OS_UNIX GUnixFDList *fd_list; #endif - GDBusMessageByteOrder byte_order; +}; + +enum +{ + PROP_0, + PROP_LOCKED }; G_DEFINE_TYPE (GDBusMessage, g_dbus_message, G_TYPE_OBJECT); @@ -122,14 +129,52 @@ g_dbus_message_finalize (GObject *object) G_OBJECT_CLASS (g_dbus_message_parent_class)->finalize (object); } +static void +g_dbus_message_get_property (GObject *object, + guint prop_id, + GValue *value, + GParamSpec *pspec) +{ + GDBusMessage *message = G_DBUS_MESSAGE (object); + + switch (prop_id) + { + case PROP_LOCKED: + g_value_set_boolean (value, g_dbus_message_get_locked (message)); + break; + + default: + G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); + break; + } +} + static void g_dbus_message_class_init (GDBusMessageClass *klass) { GObjectClass *gobject_class; gobject_class = G_OBJECT_CLASS (klass); - gobject_class->finalize = g_dbus_message_finalize; + gobject_class->get_property = g_dbus_message_get_property; + + /** + * GDBusConnection:locked: + * + * A boolean specifying whether the message is locked. + * + * Since: 2.26 + */ + g_object_class_install_property (gobject_class, + PROP_LOCKED, + g_param_spec_boolean ("locked", + P_("Locked"), + P_("Whether the message is locked"), + FALSE, + G_PARAM_READABLE | + G_PARAM_STATIC_NAME | + G_PARAM_STATIC_BLURB | + G_PARAM_STATIC_NICK)); } static void @@ -413,6 +458,13 @@ g_dbus_message_set_byte_order (GDBusMessage *message, GDBusMessageByteOrder byte_order) { g_return_if_fail (G_IS_DBUS_MESSAGE (message)); + + if (message->locked) + { + g_warning ("%s: Attempted to modify a locked message", G_STRFUNC); + return; + } + message->byte_order = byte_order; } @@ -452,6 +504,13 @@ g_dbus_message_set_message_type (GDBusMessage *message, { g_return_if_fail (G_IS_DBUS_MESSAGE (message)); g_return_if_fail (type >=0 && type < 256); + + if (message->locked) + { + g_warning ("%s: Attempted to modify a locked message", G_STRFUNC); + return; + } + message->type = type; } @@ -492,6 +551,13 @@ g_dbus_message_set_flags (GDBusMessage *message, { g_return_if_fail (G_IS_DBUS_MESSAGE (message)); g_return_if_fail (flags >=0 && flags < 256); + + if (message->locked) + { + g_warning ("%s: Attempted to modify a locked message", G_STRFUNC); + return; + } + message->flags = flags; } @@ -528,6 +594,13 @@ g_dbus_message_set_serial (GDBusMessage *message, guint32 serial) { g_return_if_fail (G_IS_DBUS_MESSAGE (message)); + + if (message->locked) + { + g_warning ("%s: Attempted to modify a locked message", G_STRFUNC); + return; + } + message->serial = serial; } @@ -575,6 +648,13 @@ g_dbus_message_set_header (GDBusMessage *message, { g_return_if_fail (G_IS_DBUS_MESSAGE (message)); g_return_if_fail (header_field >=0 && header_field < 256); + + if (message->locked) + { + g_warning ("%s: Attempted to modify a locked message", G_STRFUNC); + return; + } + if (value == NULL) { g_hash_table_remove (message->headers, GUINT_TO_POINTER (header_field)); @@ -659,6 +739,12 @@ g_dbus_message_set_body (GDBusMessage *message, g_return_if_fail (G_IS_DBUS_MESSAGE (message)); g_return_if_fail ((body == NULL) || g_variant_is_of_type (body, G_VARIANT_TYPE_TUPLE)); + if (message->locked) + { + g_warning ("%s: Attempted to modify a locked message", G_STRFUNC); + return; + } + if (message->body != NULL) g_variant_unref (message->body); if (body == NULL) @@ -726,6 +812,13 @@ g_dbus_message_set_unix_fd_list (GDBusMessage *message, { g_return_if_fail (G_IS_DBUS_MESSAGE (message)); g_return_if_fail (fd_list == NULL || G_IS_UNIX_FD_LIST (fd_list)); + + if (message->locked) + { + g_warning ("%s: Attempted to modify a locked message", G_STRFUNC); + return; + } + if (message->fd_list != NULL) g_object_unref (message->fd_list); if (fd_list != NULL) @@ -3047,3 +3140,116 @@ g_dbus_message_print (GDBusMessage *message, return g_string_free (str, FALSE); } + +/** + * g_dbus_message_get_locked: + * @message: A #GDBusMessage. + * + * Checks whether @message is locked. To monitor changes to this + * value, conncet to the #GObject::notify signal to listen for changes + * on the #GDBusMessage:locked property. + * + * Returns: %TRUE if @message is locked, %FALSE otherwise. + * + * Since: 2.26 + */ +gboolean +g_dbus_message_get_locked (GDBusMessage *message) +{ + g_return_val_if_fail (G_IS_DBUS_MESSAGE (message), FALSE); + return message->locked; +} + +/** + * g_dbus_message_lock: + * @message: A #GDBusMessage. + * + * If @message is locked, does nothing. Otherwise locks the message. + * + * Since: 2.26 + */ +void +g_dbus_message_lock (GDBusMessage *message) +{ + g_return_if_fail (G_IS_DBUS_MESSAGE (message)); + + if (message->locked) + goto out; + + message->locked = TRUE; + g_object_notify (G_OBJECT (message), "locked"); + + out: + ; +} + +/** + * g_dbus_message_copy: + * @message: A #GDBusMessage. + * @error: Return location for error or %NULL. + * + * Copies @message. The copy is a deep copy and the returned + * #GDBusMessage is completely identical except that it is guaranteed + * to not be locked and the serial will be set to 0. + * + * This operation can fail if e.g. @message contains file descriptors + * and the per-process or system-wide open files limit is reached. + * + * Returns: A new #GDBusMessage or %NULL if @error is set. Free with + * g_object_unref(). + * + * Since: 2.26 + */ +GDBusMessage * +g_dbus_message_copy (GDBusMessage *message, + GError **error) +{ + GDBusMessage *ret; + GHashTableIter iter; + gpointer header_key; + GVariant *header_value; + + g_return_val_if_fail (G_IS_DBUS_MESSAGE (message), NULL); + g_return_val_if_fail (error == NULL || *error == NULL, FALSE); + + ret = g_dbus_message_new (); + ret->type = message->type; + ret->flags = message->flags; + ret->byte_order = message->byte_order; + ret->major_protocol_version = message->major_protocol_version; + +#ifdef G_OS_UNIX + if (message->fd_list != NULL) + { + gint n; + gint num_fds; + const gint *fds; + + ret->fd_list = g_unix_fd_list_new (); + fds = g_unix_fd_list_peek_fds (message->fd_list, &num_fds); + for (n = 0; n < num_fds; n++) + { + if (g_unix_fd_list_append (ret->fd_list, + fds[n], + error) == -1) + { + g_object_unref (ret); + ret = NULL; + goto out; + } + } + } +#endif + + /* see https://bugzilla.gnome.org/show_bug.cgi?id=624546#c8 for why it's fine + * to just ref (as opposed to deep-copying) the GVariant instances + */ + ret->body = message->body != NULL ? g_variant_ref (message->body) : NULL; + g_hash_table_iter_init (&iter, message->headers); + while (g_hash_table_iter_next (&iter, &header_key, (gpointer) &header_value)) + g_hash_table_insert (ret->headers, header_key, g_variant_ref (header_value)); + + out: + return ret; +} + diff --git a/gio/gdbusmessage.h b/gio/gdbusmessage.h index 2e01e953d..5c4febd26 100644 --- a/gio/gdbusmessage.h +++ b/gio/gdbusmessage.h @@ -58,7 +58,10 @@ GDBusMessage *g_dbus_message_new_method_error_literal (GDBusMessage const gchar *error_message); gchar *g_dbus_message_print (GDBusMessage *message, guint indent); - +gboolean g_dbus_message_get_locked (GDBusMessage *message); +void g_dbus_message_lock (GDBusMessage *message); +GDBusMessage *g_dbus_message_copy (GDBusMessage *message, + GError **error); GDBusMessageByteOrder g_dbus_message_get_byte_order (GDBusMessage *message); void g_dbus_message_set_byte_order (GDBusMessage *message, GDBusMessageByteOrder byte_order); diff --git a/gio/gio.symbols b/gio/gio.symbols index 53dd90fb8..ab53ec982 100644 --- a/gio/gio.symbols +++ b/gio/gio.symbols @@ -1703,6 +1703,9 @@ g_dbus_message_new_method_error_valist g_dbus_message_new_method_reply g_dbus_message_new_signal g_dbus_message_bytes_needed +g_dbus_message_get_locked +g_dbus_message_lock +g_dbus_message_copy g_dbus_message_get_arg0 g_dbus_message_get_body g_dbus_message_get_byte_order diff --git a/gio/tests/Makefile.am b/gio/tests/Makefile.am index eac12a858..d5d679e34 100644 --- a/gio/tests/Makefile.am +++ b/gio/tests/Makefile.am @@ -41,6 +41,7 @@ TEST_PROGS += \ async-close-output-stream \ gdbus-addresses \ network-address \ + gdbus-message \ $(NULL) if OS_UNIX @@ -219,6 +220,9 @@ gdbus_addresses_LDADD = $(progs_ldadd) gdbus_connection_SOURCES = gdbus-connection.c gdbus-sessionbus.c gdbus-sessionbus.h gdbus-tests.h gdbus-tests.c gdbus_connection_LDADD = $(progs_ldadd) +gdbus_message_SOURCES = gdbus-message.c +gdbus_message_LDADD = $(progs_ldadd) + gdbus_names_SOURCES = gdbus-names.c gdbus-sessionbus.c gdbus-sessionbus.h gdbus-tests.h gdbus-tests.c gdbus_names_LDADD = $(progs_ldadd) diff --git a/gio/tests/gdbus-message.c b/gio/tests/gdbus-message.c new file mode 100644 index 000000000..981a9513c --- /dev/null +++ b/gio/tests/gdbus-message.c @@ -0,0 +1,153 @@ +/* GLib testing framework examples and tests + * + * Copyright (C) 2008-2010 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: David Zeuthen + */ + +#include +#include + +/* ---------------------------------------------------------------------------------------------------- */ + +static void +on_notify_locked (GObject *object, + GParamSpec *pspec, + gpointer user_data) +{ + gint *count = user_data; + *count += 1; +} + +static void +message_lock (void) +{ + GDBusMessage *m; + gint count; + + count = 0; + m = g_dbus_message_new (); + g_signal_connect (m, + "notify::locked", + G_CALLBACK (on_notify_locked), + &count); + g_assert (!g_dbus_message_get_locked (m)); + g_dbus_message_lock (m); + g_assert (g_dbus_message_get_locked (m)); + g_assert_cmpint (count, ==, 1); + g_dbus_message_lock (m); + g_assert (g_dbus_message_get_locked (m)); + g_assert_cmpint (count, ==, 1); + + if (g_test_trap_fork (0, G_TEST_TRAP_SILENCE_STDERR)) + g_dbus_message_set_serial (m, 42); + g_test_trap_assert_failed (); g_test_trap_assert_stderr ("*Attempted to modify a locked message*"); + + if (g_test_trap_fork (0, G_TEST_TRAP_SILENCE_STDERR)) + g_dbus_message_set_byte_order (m, G_DBUS_MESSAGE_BYTE_ORDER_BIG_ENDIAN); + g_test_trap_assert_failed (); g_test_trap_assert_stderr ("*Attempted to modify a locked message*"); + + if (g_test_trap_fork (0, G_TEST_TRAP_SILENCE_STDERR)) + g_dbus_message_set_message_type (m, G_DBUS_MESSAGE_TYPE_METHOD_CALL); + g_test_trap_assert_failed (); g_test_trap_assert_stderr ("*Attempted to modify a locked message*"); + + if (g_test_trap_fork (0, G_TEST_TRAP_SILENCE_STDERR)) + g_dbus_message_set_flags (m, G_DBUS_MESSAGE_FLAGS_NONE); + g_test_trap_assert_failed (); g_test_trap_assert_stderr ("*Attempted to modify a locked message*"); + + if (g_test_trap_fork (0, G_TEST_TRAP_SILENCE_STDERR)) + g_dbus_message_set_body (m, NULL); + g_test_trap_assert_failed (); g_test_trap_assert_stderr ("*Attempted to modify a locked message*"); + + if (g_test_trap_fork (0, G_TEST_TRAP_SILENCE_STDERR)) + g_dbus_message_set_header (m, 0, NULL); + g_test_trap_assert_failed (); g_test_trap_assert_stderr ("*Attempted to modify a locked message*"); + + g_object_unref (m); +} + +/* ---------------------------------------------------------------------------------------------------- */ + +static void +message_copy (void) +{ + GDBusMessage *m; + GDBusMessage *copy; + GError *error; + guchar *m_headers; + guchar *copy_headers; + guint n; + + m = g_dbus_message_new_method_call ("org.example.Name", + "/org/example/Object", + "org.example.Interface", + "Method"); + g_dbus_message_set_serial (m, 42); + g_dbus_message_set_byte_order (m, G_DBUS_MESSAGE_BYTE_ORDER_BIG_ENDIAN); + + error = NULL; + copy = g_dbus_message_copy (m, &error); + g_assert_no_error (error); + g_assert (G_IS_DBUS_MESSAGE (copy)); + g_assert (m != copy); + g_assert_cmpint (G_OBJECT (m)->ref_count, ==, 1); + g_assert_cmpint (G_OBJECT (copy)->ref_count, ==, 1); + + g_assert_cmpint (g_dbus_message_get_serial (copy), ==, 0); + g_assert_cmpint (g_dbus_message_get_byte_order (copy), ==, g_dbus_message_get_byte_order (m)); + g_assert_cmpint (g_dbus_message_get_flags (copy), ==, g_dbus_message_get_flags (m)); + g_assert_cmpint (g_dbus_message_get_message_type (copy), ==, g_dbus_message_get_message_type (m)); + m_headers = g_dbus_message_get_header_fields (m); + copy_headers = g_dbus_message_get_header_fields (copy); + g_assert (m_headers != NULL); + g_assert (copy_headers != NULL); + for (n = 0; m_headers[n] != 0; n++) + { + GVariant *m_val; + GVariant *copy_val; + m_val = g_dbus_message_get_header (m, m_headers[n]); + copy_val = g_dbus_message_get_header (m, m_headers[n]); + g_assert (m_val != NULL); + g_assert (copy_val != NULL); + g_assert (g_variant_equal (m_val, copy_val)); + } + g_assert_cmpint (n, >, 0); /* make sure we actually compared headers etc. */ + g_assert_cmpint (copy_headers[n], ==, 0); + g_free (m_headers); + g_free (copy_headers); + + g_object_unref (copy); + g_object_unref (m); +} + +/* ---------------------------------------------------------------------------------------------------- */ + +int +main (int argc, + char *argv[]) +{ + setlocale (LC_ALL, "C"); + + g_type_init (); + g_test_init (&argc, &argv, NULL); + + g_test_add_func ("/gdbus/message/lock", message_lock); + g_test_add_func ("/gdbus/message/copy", message_copy); + return g_test_run(); +} +