mirror of
https://gitlab.gnome.org/GNOME/glib.git
synced 2024-12-27 07:56:14 +01:00
fb60ebb0df
Previously, the code only validated the types of *required* message headers, and did not validate optional ones. Now, the headers are validated in one step, and a subsequent step checks whether the required ones are present. The existing tests have been updated to match the new error message wording. More tests will be added to test the new behaviour in a subsequent commit. Signed-off-by: Philip Withnall <pwithnall@gnome.org> Fixes: #3093
4031 lines
121 KiB
C
4031 lines
121 KiB
C
/* GDBus - GLib D-Bus Library
|
||
*
|
||
* Copyright (C) 2008-2010 Red Hat, Inc.
|
||
*
|
||
* SPDX-License-Identifier: LGPL-2.1-or-later
|
||
*
|
||
* 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, see <http://www.gnu.org/licenses/>.
|
||
*
|
||
* Author: David Zeuthen <davidz@redhat.com>
|
||
*/
|
||
|
||
/* Uncomment to debug serializer code */
|
||
/* #define DEBUG_SERIALIZER */
|
||
|
||
#include "config.h"
|
||
|
||
#include <string.h>
|
||
#include <errno.h>
|
||
#include <sys/types.h>
|
||
#include <sys/stat.h>
|
||
|
||
#if MAJOR_IN_MKDEV
|
||
#include <sys/mkdev.h>
|
||
#elif MAJOR_IN_SYSMACROS
|
||
#include <sys/sysmacros.h>
|
||
#elif MAJOR_IN_TYPES
|
||
#include <sys/types.h>
|
||
#else
|
||
#define MAJOR_MINOR_NOT_FOUND 1
|
||
#endif
|
||
|
||
#include "gdbusutils.h"
|
||
#include "gdbusmessage.h"
|
||
#include "gdbuserror.h"
|
||
#include "gioenumtypes.h"
|
||
#include "ginputstream.h"
|
||
#include "gdatainputstream.h"
|
||
#include "gmemoryinputstream.h"
|
||
#include "goutputstream.h"
|
||
#include "gdataoutputstream.h"
|
||
#include "gmemoryoutputstream.h"
|
||
#include "gseekable.h"
|
||
#include "gioerror.h"
|
||
#include "gdbusprivate.h"
|
||
#include "gutilsprivate.h"
|
||
|
||
#ifdef G_OS_UNIX
|
||
#include "gunixfdlist.h"
|
||
#endif
|
||
|
||
#include "glibintl.h"
|
||
|
||
/* See https://dbus.freedesktop.org/doc/dbus-specification.html#message-protocol-marshaling-signature
|
||
* This is 64 containers plus 1 value within them. */
|
||
#define G_DBUS_MAX_TYPE_DEPTH (64 + 1)
|
||
|
||
typedef struct _GMemoryBuffer GMemoryBuffer;
|
||
struct _GMemoryBuffer
|
||
{
|
||
gsize len;
|
||
gsize valid_len;
|
||
gsize pos;
|
||
gchar *data;
|
||
GDataStreamByteOrder byte_order;
|
||
};
|
||
|
||
static gboolean
|
||
g_memory_buffer_is_byteswapped (GMemoryBuffer *mbuf)
|
||
{
|
||
#if G_BYTE_ORDER == G_LITTLE_ENDIAN
|
||
return mbuf->byte_order == G_DATA_STREAM_BYTE_ORDER_BIG_ENDIAN;
|
||
#else
|
||
return mbuf->byte_order == G_DATA_STREAM_BYTE_ORDER_LITTLE_ENDIAN;
|
||
#endif
|
||
}
|
||
|
||
static guchar
|
||
g_memory_buffer_read_byte (GMemoryBuffer *mbuf,
|
||
GError **error)
|
||
{
|
||
g_return_val_if_fail (error == NULL || *error == NULL, 0);
|
||
|
||
if (mbuf->pos >= mbuf->valid_len)
|
||
{
|
||
g_set_error (error,
|
||
G_IO_ERROR,
|
||
G_IO_ERROR_INVALID_ARGUMENT,
|
||
"Unexpected end of message while reading byte.");
|
||
return 0;
|
||
}
|
||
return mbuf->data [mbuf->pos++];
|
||
}
|
||
|
||
static gint16
|
||
g_memory_buffer_read_int16 (GMemoryBuffer *mbuf,
|
||
GError **error)
|
||
{
|
||
gint16 v;
|
||
|
||
g_return_val_if_fail (error == NULL || *error == NULL, -1);
|
||
|
||
if (mbuf->pos > mbuf->valid_len - 2)
|
||
{
|
||
g_set_error (error,
|
||
G_IO_ERROR,
|
||
G_IO_ERROR_INVALID_ARGUMENT,
|
||
"Unexpected end of message while reading int16.");
|
||
return 0;
|
||
}
|
||
|
||
memcpy (&v, mbuf->data + mbuf->pos, 2);
|
||
mbuf->pos += 2;
|
||
|
||
if (g_memory_buffer_is_byteswapped (mbuf))
|
||
v = GUINT16_SWAP_LE_BE (v);
|
||
|
||
return v;
|
||
}
|
||
|
||
static guint16
|
||
g_memory_buffer_read_uint16 (GMemoryBuffer *mbuf,
|
||
GError **error)
|
||
{
|
||
guint16 v;
|
||
|
||
g_return_val_if_fail (error == NULL || *error == NULL, 0);
|
||
|
||
if (mbuf->pos > mbuf->valid_len - 2)
|
||
{
|
||
g_set_error (error,
|
||
G_IO_ERROR,
|
||
G_IO_ERROR_INVALID_ARGUMENT,
|
||
"Unexpected end of message while reading uint16.");
|
||
return 0;
|
||
}
|
||
|
||
memcpy (&v, mbuf->data + mbuf->pos, 2);
|
||
mbuf->pos += 2;
|
||
|
||
if (g_memory_buffer_is_byteswapped (mbuf))
|
||
v = GUINT16_SWAP_LE_BE (v);
|
||
|
||
return v;
|
||
}
|
||
|
||
static gint32
|
||
g_memory_buffer_read_int32 (GMemoryBuffer *mbuf,
|
||
GError **error)
|
||
{
|
||
gint32 v;
|
||
|
||
g_return_val_if_fail (error == NULL || *error == NULL, -1);
|
||
|
||
if (mbuf->pos > mbuf->valid_len - 4)
|
||
{
|
||
g_set_error (error,
|
||
G_IO_ERROR,
|
||
G_IO_ERROR_INVALID_ARGUMENT,
|
||
"Unexpected end of message while reading int32.");
|
||
return 0;
|
||
}
|
||
|
||
memcpy (&v, mbuf->data + mbuf->pos, 4);
|
||
mbuf->pos += 4;
|
||
|
||
if (g_memory_buffer_is_byteswapped (mbuf))
|
||
v = GUINT32_SWAP_LE_BE (v);
|
||
|
||
return v;
|
||
}
|
||
|
||
static guint32
|
||
g_memory_buffer_read_uint32 (GMemoryBuffer *mbuf,
|
||
GError **error)
|
||
{
|
||
guint32 v;
|
||
|
||
g_return_val_if_fail (error == NULL || *error == NULL, 0);
|
||
|
||
if (mbuf->pos > mbuf->valid_len - 4)
|
||
{
|
||
g_set_error (error,
|
||
G_IO_ERROR,
|
||
G_IO_ERROR_INVALID_ARGUMENT,
|
||
"Unexpected end of message while reading uint32.");
|
||
return 0;
|
||
}
|
||
|
||
memcpy (&v, mbuf->data + mbuf->pos, 4);
|
||
mbuf->pos += 4;
|
||
|
||
if (g_memory_buffer_is_byteswapped (mbuf))
|
||
v = GUINT32_SWAP_LE_BE (v);
|
||
|
||
return v;
|
||
}
|
||
|
||
static gint64
|
||
g_memory_buffer_read_int64 (GMemoryBuffer *mbuf,
|
||
GError **error)
|
||
{
|
||
gint64 v;
|
||
|
||
g_return_val_if_fail (error == NULL || *error == NULL, -1);
|
||
|
||
if (mbuf->pos > mbuf->valid_len - 8)
|
||
{
|
||
g_set_error (error,
|
||
G_IO_ERROR,
|
||
G_IO_ERROR_INVALID_ARGUMENT,
|
||
"Unexpected end of message while reading int64.");
|
||
return 0;
|
||
}
|
||
|
||
memcpy (&v, mbuf->data + mbuf->pos, 8);
|
||
mbuf->pos += 8;
|
||
|
||
if (g_memory_buffer_is_byteswapped (mbuf))
|
||
v = GUINT64_SWAP_LE_BE (v);
|
||
|
||
return v;
|
||
}
|
||
|
||
static guint64
|
||
g_memory_buffer_read_uint64 (GMemoryBuffer *mbuf,
|
||
GError **error)
|
||
{
|
||
guint64 v;
|
||
|
||
g_return_val_if_fail (error == NULL || *error == NULL, 0);
|
||
|
||
if (mbuf->pos > mbuf->valid_len - 8)
|
||
{
|
||
g_set_error (error,
|
||
G_IO_ERROR,
|
||
G_IO_ERROR_INVALID_ARGUMENT,
|
||
"Unexpected end of message while reading uint64.");
|
||
return 0;
|
||
}
|
||
|
||
memcpy (&v, mbuf->data + mbuf->pos, 8);
|
||
mbuf->pos += 8;
|
||
|
||
if (g_memory_buffer_is_byteswapped (mbuf))
|
||
v = GUINT64_SWAP_LE_BE (v);
|
||
|
||
return v;
|
||
}
|
||
|
||
#define MIN_ARRAY_SIZE 128
|
||
|
||
static void
|
||
array_resize (GMemoryBuffer *mbuf,
|
||
gsize size)
|
||
{
|
||
gpointer data;
|
||
gsize len;
|
||
|
||
if (mbuf->len == size)
|
||
return;
|
||
|
||
len = mbuf->len;
|
||
data = g_realloc (mbuf->data, size);
|
||
|
||
if (size > len)
|
||
memset ((guint8 *)data + len, 0, size - len);
|
||
|
||
mbuf->data = data;
|
||
mbuf->len = size;
|
||
|
||
if (mbuf->len < mbuf->valid_len)
|
||
mbuf->valid_len = mbuf->len;
|
||
}
|
||
|
||
static gboolean
|
||
g_memory_buffer_write (GMemoryBuffer *mbuf,
|
||
const void *buffer,
|
||
gsize count)
|
||
{
|
||
guint8 *dest;
|
||
gsize new_size;
|
||
|
||
if (count == 0)
|
||
return TRUE;
|
||
|
||
/* Check for address space overflow, but only if the buffer is resizable.
|
||
Otherwise we just do a short write and don't worry. */
|
||
if (mbuf->pos + count < mbuf->pos)
|
||
return FALSE;
|
||
|
||
if (mbuf->pos + count > mbuf->len)
|
||
{
|
||
/* At least enough to fit the write, rounded up
|
||
for greater than linear growth.
|
||
TODO: This wastes a lot of memory at large buffer sizes.
|
||
Figure out a more rational allocation strategy. */
|
||
new_size = g_nearest_pow (mbuf->pos + count);
|
||
/* Check for overflow again. We have checked if
|
||
pos + count > G_MAXSIZE, but now check if g_nearest_pow () has
|
||
overflowed */
|
||
if (new_size == 0)
|
||
return FALSE;
|
||
|
||
new_size = MAX (new_size, MIN_ARRAY_SIZE);
|
||
array_resize (mbuf, new_size);
|
||
}
|
||
|
||
dest = (guint8 *)mbuf->data + mbuf->pos;
|
||
memcpy (dest, buffer, count);
|
||
mbuf->pos += count;
|
||
|
||
if (mbuf->pos > mbuf->valid_len)
|
||
mbuf->valid_len = mbuf->pos;
|
||
|
||
return TRUE;
|
||
}
|
||
|
||
static gboolean
|
||
g_memory_buffer_put_byte (GMemoryBuffer *mbuf,
|
||
guchar data)
|
||
{
|
||
return g_memory_buffer_write (mbuf, &data, 1);
|
||
}
|
||
|
||
static gboolean
|
||
g_memory_buffer_put_int16 (GMemoryBuffer *mbuf,
|
||
gint16 data)
|
||
{
|
||
switch (mbuf->byte_order)
|
||
{
|
||
case G_DATA_STREAM_BYTE_ORDER_BIG_ENDIAN:
|
||
data = GINT16_TO_BE (data);
|
||
break;
|
||
case G_DATA_STREAM_BYTE_ORDER_LITTLE_ENDIAN:
|
||
data = GINT16_TO_LE (data);
|
||
break;
|
||
case G_DATA_STREAM_BYTE_ORDER_HOST_ENDIAN:
|
||
default:
|
||
break;
|
||
}
|
||
|
||
return g_memory_buffer_write (mbuf, &data, 2);
|
||
}
|
||
|
||
static gboolean
|
||
g_memory_buffer_put_uint16 (GMemoryBuffer *mbuf,
|
||
guint16 data)
|
||
{
|
||
switch (mbuf->byte_order)
|
||
{
|
||
case G_DATA_STREAM_BYTE_ORDER_BIG_ENDIAN:
|
||
data = GUINT16_TO_BE (data);
|
||
break;
|
||
case G_DATA_STREAM_BYTE_ORDER_LITTLE_ENDIAN:
|
||
data = GUINT16_TO_LE (data);
|
||
break;
|
||
case G_DATA_STREAM_BYTE_ORDER_HOST_ENDIAN:
|
||
default:
|
||
break;
|
||
}
|
||
|
||
return g_memory_buffer_write (mbuf, &data, 2);
|
||
}
|
||
|
||
static gboolean
|
||
g_memory_buffer_put_int32 (GMemoryBuffer *mbuf,
|
||
gint32 data)
|
||
{
|
||
switch (mbuf->byte_order)
|
||
{
|
||
case G_DATA_STREAM_BYTE_ORDER_BIG_ENDIAN:
|
||
data = GINT32_TO_BE (data);
|
||
break;
|
||
case G_DATA_STREAM_BYTE_ORDER_LITTLE_ENDIAN:
|
||
data = GINT32_TO_LE (data);
|
||
break;
|
||
case G_DATA_STREAM_BYTE_ORDER_HOST_ENDIAN:
|
||
default:
|
||
break;
|
||
}
|
||
|
||
return g_memory_buffer_write (mbuf, &data, 4);
|
||
}
|
||
|
||
static gboolean
|
||
g_memory_buffer_put_uint32 (GMemoryBuffer *mbuf,
|
||
guint32 data)
|
||
{
|
||
switch (mbuf->byte_order)
|
||
{
|
||
case G_DATA_STREAM_BYTE_ORDER_BIG_ENDIAN:
|
||
data = GUINT32_TO_BE (data);
|
||
break;
|
||
case G_DATA_STREAM_BYTE_ORDER_LITTLE_ENDIAN:
|
||
data = GUINT32_TO_LE (data);
|
||
break;
|
||
case G_DATA_STREAM_BYTE_ORDER_HOST_ENDIAN:
|
||
default:
|
||
break;
|
||
}
|
||
|
||
return g_memory_buffer_write (mbuf, &data, 4);
|
||
}
|
||
|
||
static gboolean
|
||
g_memory_buffer_put_int64 (GMemoryBuffer *mbuf,
|
||
gint64 data)
|
||
{
|
||
switch (mbuf->byte_order)
|
||
{
|
||
case G_DATA_STREAM_BYTE_ORDER_BIG_ENDIAN:
|
||
data = GINT64_TO_BE (data);
|
||
break;
|
||
case G_DATA_STREAM_BYTE_ORDER_LITTLE_ENDIAN:
|
||
data = GINT64_TO_LE (data);
|
||
break;
|
||
case G_DATA_STREAM_BYTE_ORDER_HOST_ENDIAN:
|
||
default:
|
||
break;
|
||
}
|
||
|
||
return g_memory_buffer_write (mbuf, &data, 8);
|
||
}
|
||
|
||
static gboolean
|
||
g_memory_buffer_put_uint64 (GMemoryBuffer *mbuf,
|
||
guint64 data)
|
||
{
|
||
switch (mbuf->byte_order)
|
||
{
|
||
case G_DATA_STREAM_BYTE_ORDER_BIG_ENDIAN:
|
||
data = GUINT64_TO_BE (data);
|
||
break;
|
||
case G_DATA_STREAM_BYTE_ORDER_LITTLE_ENDIAN:
|
||
data = GUINT64_TO_LE (data);
|
||
break;
|
||
case G_DATA_STREAM_BYTE_ORDER_HOST_ENDIAN:
|
||
default:
|
||
break;
|
||
}
|
||
|
||
return g_memory_buffer_write (mbuf, &data, 8);
|
||
}
|
||
|
||
static gboolean
|
||
g_memory_buffer_put_string (GMemoryBuffer *mbuf,
|
||
const char *str)
|
||
{
|
||
g_return_val_if_fail (str != NULL, FALSE);
|
||
|
||
return g_memory_buffer_write (mbuf, str, strlen (str));
|
||
}
|
||
|
||
typedef struct _GDBusMessageClass GDBusMessageClass;
|
||
|
||
/**
|
||
* GDBusMessageClass:
|
||
*
|
||
* Class structure for #GDBusMessage.
|
||
*
|
||
* Since: 2.26
|
||
*/
|
||
struct _GDBusMessageClass
|
||
{
|
||
/*< private >*/
|
||
GObjectClass parent_class;
|
||
};
|
||
|
||
/**
|
||
* GDBusMessage:
|
||
*
|
||
* A type for representing D-Bus messages that can be sent or received
|
||
* on a [class@Gio.DBusConnection].
|
||
*
|
||
* Since: 2.26
|
||
*/
|
||
struct _GDBusMessage
|
||
{
|
||
/*< private >*/
|
||
GObject parent_instance;
|
||
|
||
GDBusMessageType type;
|
||
GDBusMessageFlags flags;
|
||
gboolean locked;
|
||
GDBusMessageByteOrder byte_order;
|
||
guchar major_protocol_version;
|
||
guint32 serial;
|
||
GHashTable *headers;
|
||
GVariant *body;
|
||
GVariant *arg0_cache; /* (nullable) (owned) */
|
||
#ifdef G_OS_UNIX
|
||
GUnixFDList *fd_list;
|
||
#endif
|
||
};
|
||
|
||
enum
|
||
{
|
||
PROP_0,
|
||
PROP_LOCKED
|
||
};
|
||
|
||
G_DEFINE_TYPE (GDBusMessage, g_dbus_message, G_TYPE_OBJECT)
|
||
|
||
static void
|
||
g_dbus_message_finalize (GObject *object)
|
||
{
|
||
GDBusMessage *message = G_DBUS_MESSAGE (object);
|
||
|
||
if (message->headers != NULL)
|
||
g_hash_table_unref (message->headers);
|
||
if (message->body != NULL)
|
||
g_variant_unref (message->body);
|
||
g_clear_pointer (&message->arg0_cache, g_variant_unref);
|
||
#ifdef G_OS_UNIX
|
||
if (message->fd_list != NULL)
|
||
g_object_unref (message->fd_list);
|
||
#endif
|
||
|
||
if (G_OBJECT_CLASS (g_dbus_message_parent_class)->finalize != NULL)
|
||
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", NULL, NULL,
|
||
FALSE,
|
||
G_PARAM_READABLE |
|
||
G_PARAM_STATIC_NAME |
|
||
G_PARAM_STATIC_BLURB |
|
||
G_PARAM_STATIC_NICK));
|
||
}
|
||
|
||
static void
|
||
g_dbus_message_init (GDBusMessage *message)
|
||
{
|
||
/* Any D-Bus implementation is supposed to handle both Big and
|
||
* Little Endian encodings and the Endianness is part of the D-Bus
|
||
* message - we prefer to use Big Endian (since it's Network Byte
|
||
* Order and just easier to read for humans) but if the machine is
|
||
* Little Endian we use that for performance reasons.
|
||
*/
|
||
#if G_BYTE_ORDER == G_LITTLE_ENDIAN
|
||
message->byte_order = G_DBUS_MESSAGE_BYTE_ORDER_LITTLE_ENDIAN;
|
||
#else
|
||
/* this could also be G_PDP_ENDIAN */
|
||
message->byte_order = G_DBUS_MESSAGE_BYTE_ORDER_BIG_ENDIAN;
|
||
#endif
|
||
message->headers = g_hash_table_new_full (g_direct_hash,
|
||
g_direct_equal,
|
||
NULL,
|
||
(GDestroyNotify) g_variant_unref);
|
||
}
|
||
|
||
/**
|
||
* g_dbus_message_new:
|
||
*
|
||
* Creates a new empty #GDBusMessage.
|
||
*
|
||
* Returns: A #GDBusMessage. Free with g_object_unref().
|
||
*
|
||
* Since: 2.26
|
||
*/
|
||
GDBusMessage *
|
||
g_dbus_message_new (void)
|
||
{
|
||
return g_object_new (G_TYPE_DBUS_MESSAGE, NULL);
|
||
}
|
||
|
||
/**
|
||
* g_dbus_message_new_method_call:
|
||
* @name: (nullable): A valid D-Bus name or %NULL.
|
||
* @path: A valid object path.
|
||
* @interface_: (nullable): A valid D-Bus interface name or %NULL.
|
||
* @method: A valid method name.
|
||
*
|
||
* Creates a new #GDBusMessage for a method call.
|
||
*
|
||
* Returns: A #GDBusMessage. Free with g_object_unref().
|
||
*
|
||
* Since: 2.26
|
||
*/
|
||
GDBusMessage *
|
||
g_dbus_message_new_method_call (const gchar *name,
|
||
const gchar *path,
|
||
const gchar *interface_,
|
||
const gchar *method)
|
||
{
|
||
GDBusMessage *message;
|
||
|
||
g_return_val_if_fail (name == NULL || g_dbus_is_name (name), NULL);
|
||
g_return_val_if_fail (g_variant_is_object_path (path), NULL);
|
||
g_return_val_if_fail (g_dbus_is_member_name (method), NULL);
|
||
g_return_val_if_fail (interface_ == NULL || g_dbus_is_interface_name (interface_), NULL);
|
||
|
||
message = g_dbus_message_new ();
|
||
message->type = G_DBUS_MESSAGE_TYPE_METHOD_CALL;
|
||
|
||
if (name != NULL)
|
||
g_dbus_message_set_destination (message, name);
|
||
g_dbus_message_set_path (message, path);
|
||
g_dbus_message_set_member (message, method);
|
||
if (interface_ != NULL)
|
||
g_dbus_message_set_interface (message, interface_);
|
||
|
||
return message;
|
||
}
|
||
|
||
/**
|
||
* g_dbus_message_new_signal:
|
||
* @path: A valid object path.
|
||
* @interface_: A valid D-Bus interface name.
|
||
* @signal: A valid signal name.
|
||
*
|
||
* Creates a new #GDBusMessage for a signal emission.
|
||
*
|
||
* Returns: A #GDBusMessage. Free with g_object_unref().
|
||
*
|
||
* Since: 2.26
|
||
*/
|
||
GDBusMessage *
|
||
g_dbus_message_new_signal (const gchar *path,
|
||
const gchar *interface_,
|
||
const gchar *signal)
|
||
{
|
||
GDBusMessage *message;
|
||
|
||
g_return_val_if_fail (g_variant_is_object_path (path), NULL);
|
||
g_return_val_if_fail (g_dbus_is_member_name (signal), NULL);
|
||
g_return_val_if_fail (g_dbus_is_interface_name (interface_), NULL);
|
||
|
||
message = g_dbus_message_new ();
|
||
message->type = G_DBUS_MESSAGE_TYPE_SIGNAL;
|
||
message->flags = G_DBUS_MESSAGE_FLAGS_NO_REPLY_EXPECTED;
|
||
|
||
g_dbus_message_set_path (message, path);
|
||
g_dbus_message_set_member (message, signal);
|
||
g_dbus_message_set_interface (message, interface_);
|
||
|
||
return message;
|
||
}
|
||
|
||
|
||
/**
|
||
* g_dbus_message_new_method_reply:
|
||
* @method_call_message: A message of type %G_DBUS_MESSAGE_TYPE_METHOD_CALL to
|
||
* create a reply message to.
|
||
*
|
||
* Creates a new #GDBusMessage that is a reply to @method_call_message.
|
||
*
|
||
* Returns: (transfer full): #GDBusMessage. Free with g_object_unref().
|
||
*
|
||
* Since: 2.26
|
||
*/
|
||
GDBusMessage *
|
||
g_dbus_message_new_method_reply (GDBusMessage *method_call_message)
|
||
{
|
||
GDBusMessage *message;
|
||
const gchar *sender;
|
||
|
||
g_return_val_if_fail (G_IS_DBUS_MESSAGE (method_call_message), NULL);
|
||
g_return_val_if_fail (g_dbus_message_get_message_type (method_call_message) == G_DBUS_MESSAGE_TYPE_METHOD_CALL, NULL);
|
||
g_return_val_if_fail (g_dbus_message_get_serial (method_call_message) != 0, NULL);
|
||
|
||
message = g_dbus_message_new ();
|
||
message->type = G_DBUS_MESSAGE_TYPE_METHOD_RETURN;
|
||
message->flags = G_DBUS_MESSAGE_FLAGS_NO_REPLY_EXPECTED;
|
||
/* reply with same endianness */
|
||
message->byte_order = method_call_message->byte_order;
|
||
|
||
g_dbus_message_set_reply_serial (message, g_dbus_message_get_serial (method_call_message));
|
||
sender = g_dbus_message_get_sender (method_call_message);
|
||
if (sender != NULL)
|
||
g_dbus_message_set_destination (message, sender);
|
||
|
||
return message;
|
||
}
|
||
|
||
/**
|
||
* g_dbus_message_new_method_error:
|
||
* @method_call_message: A message of type %G_DBUS_MESSAGE_TYPE_METHOD_CALL to
|
||
* create a reply message to.
|
||
* @error_name: A valid D-Bus error name.
|
||
* @error_message_format: The D-Bus error message in a printf() format.
|
||
* @...: Arguments for @error_message_format.
|
||
*
|
||
* Creates a new #GDBusMessage that is an error reply to @method_call_message.
|
||
*
|
||
* Returns: (transfer full): A #GDBusMessage. Free with g_object_unref().
|
||
*
|
||
* Since: 2.26
|
||
*/
|
||
GDBusMessage *
|
||
g_dbus_message_new_method_error (GDBusMessage *method_call_message,
|
||
const gchar *error_name,
|
||
const gchar *error_message_format,
|
||
...)
|
||
{
|
||
GDBusMessage *ret;
|
||
va_list var_args;
|
||
|
||
va_start (var_args, error_message_format);
|
||
ret = g_dbus_message_new_method_error_valist (method_call_message,
|
||
error_name,
|
||
error_message_format,
|
||
var_args);
|
||
va_end (var_args);
|
||
|
||
return ret;
|
||
}
|
||
|
||
/**
|
||
* g_dbus_message_new_method_error_literal:
|
||
* @method_call_message: A message of type %G_DBUS_MESSAGE_TYPE_METHOD_CALL to
|
||
* create a reply message to.
|
||
* @error_name: A valid D-Bus error name.
|
||
* @error_message: The D-Bus error message.
|
||
*
|
||
* Creates a new #GDBusMessage that is an error reply to @method_call_message.
|
||
*
|
||
* Returns: (transfer full): A #GDBusMessage. Free with g_object_unref().
|
||
*
|
||
* Since: 2.26
|
||
*/
|
||
GDBusMessage *
|
||
g_dbus_message_new_method_error_literal (GDBusMessage *method_call_message,
|
||
const gchar *error_name,
|
||
const gchar *error_message)
|
||
{
|
||
GDBusMessage *message;
|
||
const gchar *sender;
|
||
|
||
g_return_val_if_fail (G_IS_DBUS_MESSAGE (method_call_message), NULL);
|
||
g_return_val_if_fail (g_dbus_message_get_message_type (method_call_message) == G_DBUS_MESSAGE_TYPE_METHOD_CALL, NULL);
|
||
g_return_val_if_fail (g_dbus_message_get_serial (method_call_message) != 0, NULL);
|
||
g_return_val_if_fail (g_dbus_is_name (error_name), NULL);
|
||
g_return_val_if_fail (error_message != NULL, NULL);
|
||
|
||
message = g_dbus_message_new ();
|
||
message->type = G_DBUS_MESSAGE_TYPE_ERROR;
|
||
message->flags = G_DBUS_MESSAGE_FLAGS_NO_REPLY_EXPECTED;
|
||
/* reply with same endianness */
|
||
message->byte_order = method_call_message->byte_order;
|
||
|
||
g_dbus_message_set_reply_serial (message, g_dbus_message_get_serial (method_call_message));
|
||
g_dbus_message_set_error_name (message, error_name);
|
||
g_dbus_message_set_body (message, g_variant_new ("(s)", error_message));
|
||
|
||
sender = g_dbus_message_get_sender (method_call_message);
|
||
if (sender != NULL)
|
||
g_dbus_message_set_destination (message, sender);
|
||
|
||
return message;
|
||
}
|
||
|
||
/**
|
||
* g_dbus_message_new_method_error_valist:
|
||
* @method_call_message: A message of type %G_DBUS_MESSAGE_TYPE_METHOD_CALL to
|
||
* create a reply message to.
|
||
* @error_name: A valid D-Bus error name.
|
||
* @error_message_format: The D-Bus error message in a printf() format.
|
||
* @var_args: Arguments for @error_message_format.
|
||
*
|
||
* Like g_dbus_message_new_method_error() but intended for language bindings.
|
||
*
|
||
* Returns: (transfer full): A #GDBusMessage. Free with g_object_unref().
|
||
*
|
||
* Since: 2.26
|
||
*/
|
||
G_GNUC_PRINTF(3, 0)
|
||
GDBusMessage *
|
||
g_dbus_message_new_method_error_valist (GDBusMessage *method_call_message,
|
||
const gchar *error_name,
|
||
const gchar *error_message_format,
|
||
va_list var_args)
|
||
{
|
||
GDBusMessage *ret;
|
||
gchar *error_message;
|
||
error_message = g_strdup_vprintf (error_message_format, var_args);
|
||
ret = g_dbus_message_new_method_error_literal (method_call_message,
|
||
error_name,
|
||
error_message);
|
||
g_free (error_message);
|
||
return ret;
|
||
}
|
||
|
||
/* ---------------------------------------------------------------------------------------------------- */
|
||
|
||
/**
|
||
* g_dbus_message_get_byte_order:
|
||
* @message: A #GDBusMessage.
|
||
*
|
||
* Gets the byte order of @message.
|
||
*
|
||
* Returns: The byte order.
|
||
*/
|
||
GDBusMessageByteOrder
|
||
g_dbus_message_get_byte_order (GDBusMessage *message)
|
||
{
|
||
g_return_val_if_fail (G_IS_DBUS_MESSAGE (message), (GDBusMessageByteOrder) 0);
|
||
return message->byte_order;
|
||
}
|
||
|
||
/**
|
||
* g_dbus_message_set_byte_order:
|
||
* @message: A #GDBusMessage.
|
||
* @byte_order: The byte order.
|
||
*
|
||
* Sets the byte order of @message.
|
||
*/
|
||
void
|
||
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;
|
||
}
|
||
|
||
/* ---------------------------------------------------------------------------------------------------- */
|
||
|
||
/* TODO: need GI annotations to specify that any guchar value goes for the type */
|
||
|
||
/**
|
||
* g_dbus_message_get_message_type:
|
||
* @message: A #GDBusMessage.
|
||
*
|
||
* Gets the type of @message.
|
||
*
|
||
* Returns: A 8-bit unsigned integer (typically a value from the #GDBusMessageType enumeration).
|
||
*
|
||
* Since: 2.26
|
||
*/
|
||
GDBusMessageType
|
||
g_dbus_message_get_message_type (GDBusMessage *message)
|
||
{
|
||
g_return_val_if_fail (G_IS_DBUS_MESSAGE (message), G_DBUS_MESSAGE_TYPE_INVALID);
|
||
return message->type;
|
||
}
|
||
|
||
/**
|
||
* g_dbus_message_set_message_type:
|
||
* @message: A #GDBusMessage.
|
||
* @type: A 8-bit unsigned integer (typically a value from the #GDBusMessageType enumeration).
|
||
*
|
||
* Sets @message to be of @type.
|
||
*
|
||
* Since: 2.26
|
||
*/
|
||
void
|
||
g_dbus_message_set_message_type (GDBusMessage *message,
|
||
GDBusMessageType type)
|
||
{
|
||
g_return_if_fail (G_IS_DBUS_MESSAGE (message));
|
||
g_return_if_fail ((guint) type < 256);
|
||
|
||
if (message->locked)
|
||
{
|
||
g_warning ("%s: Attempted to modify a locked message", G_STRFUNC);
|
||
return;
|
||
}
|
||
|
||
message->type = type;
|
||
}
|
||
|
||
/* ---------------------------------------------------------------------------------------------------- */
|
||
|
||
/* TODO: need GI annotations to specify that any guchar value goes for flags */
|
||
|
||
/**
|
||
* g_dbus_message_get_flags:
|
||
* @message: A #GDBusMessage.
|
||
*
|
||
* Gets the flags for @message.
|
||
*
|
||
* Returns: Flags that are set (typically values from the #GDBusMessageFlags enumeration bitwise ORed together).
|
||
*
|
||
* Since: 2.26
|
||
*/
|
||
GDBusMessageFlags
|
||
g_dbus_message_get_flags (GDBusMessage *message)
|
||
{
|
||
g_return_val_if_fail (G_IS_DBUS_MESSAGE (message), G_DBUS_MESSAGE_FLAGS_NONE);
|
||
return message->flags;
|
||
}
|
||
|
||
/**
|
||
* g_dbus_message_set_flags:
|
||
* @message: A #GDBusMessage.
|
||
* @flags: Flags for @message that are set (typically values from the #GDBusMessageFlags
|
||
* enumeration bitwise ORed together).
|
||
*
|
||
* Sets the flags to set on @message.
|
||
*
|
||
* Since: 2.26
|
||
*/
|
||
void
|
||
g_dbus_message_set_flags (GDBusMessage *message,
|
||
GDBusMessageFlags flags)
|
||
{
|
||
g_return_if_fail (G_IS_DBUS_MESSAGE (message));
|
||
g_return_if_fail ((guint) flags < 256);
|
||
|
||
if (message->locked)
|
||
{
|
||
g_warning ("%s: Attempted to modify a locked message", G_STRFUNC);
|
||
return;
|
||
}
|
||
|
||
message->flags = flags;
|
||
}
|
||
|
||
/* ---------------------------------------------------------------------------------------------------- */
|
||
|
||
/**
|
||
* g_dbus_message_get_serial:
|
||
* @message: A #GDBusMessage.
|
||
*
|
||
* Gets the serial for @message.
|
||
*
|
||
* Returns: A #guint32.
|
||
*
|
||
* Since: 2.26
|
||
*/
|
||
guint32
|
||
g_dbus_message_get_serial (GDBusMessage *message)
|
||
{
|
||
g_return_val_if_fail (G_IS_DBUS_MESSAGE (message), 0);
|
||
return message->serial;
|
||
}
|
||
|
||
/**
|
||
* g_dbus_message_set_serial:
|
||
* @message: A #GDBusMessage.
|
||
* @serial: A #guint32.
|
||
*
|
||
* Sets the serial for @message.
|
||
*
|
||
* Since: 2.26
|
||
*/
|
||
void
|
||
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;
|
||
}
|
||
|
||
/* ---------------------------------------------------------------------------------------------------- */
|
||
|
||
/* TODO: need GI annotations to specify that any guchar value goes for header_field */
|
||
|
||
/**
|
||
* g_dbus_message_get_header:
|
||
* @message: A #GDBusMessage.
|
||
* @header_field: A 8-bit unsigned integer (typically a value from the #GDBusMessageHeaderField enumeration)
|
||
*
|
||
* Gets a header field on @message.
|
||
*
|
||
* The caller is responsible for checking the type of the returned #GVariant
|
||
* matches what is expected.
|
||
*
|
||
* Returns: (transfer none) (nullable): A #GVariant with the value if the header was found, %NULL
|
||
* otherwise. Do not free, it is owned by @message.
|
||
*
|
||
* Since: 2.26
|
||
*/
|
||
GVariant *
|
||
g_dbus_message_get_header (GDBusMessage *message,
|
||
GDBusMessageHeaderField header_field)
|
||
{
|
||
g_return_val_if_fail (G_IS_DBUS_MESSAGE (message), NULL);
|
||
g_return_val_if_fail ((guint) header_field < 256, NULL);
|
||
return g_hash_table_lookup (message->headers, GUINT_TO_POINTER (header_field));
|
||
}
|
||
|
||
/**
|
||
* g_dbus_message_set_header:
|
||
* @message: A #GDBusMessage.
|
||
* @header_field: A 8-bit unsigned integer (typically a value from the #GDBusMessageHeaderField enumeration)
|
||
* @value: (nullable): A #GVariant to set the header field or %NULL to clear the header field.
|
||
*
|
||
* Sets a header field on @message.
|
||
*
|
||
* If @value is floating, @message assumes ownership of @value.
|
||
*
|
||
* Since: 2.26
|
||
*/
|
||
void
|
||
g_dbus_message_set_header (GDBusMessage *message,
|
||
GDBusMessageHeaderField header_field,
|
||
GVariant *value)
|
||
{
|
||
g_return_if_fail (G_IS_DBUS_MESSAGE (message));
|
||
g_return_if_fail ((guint) 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));
|
||
}
|
||
else
|
||
{
|
||
g_hash_table_insert (message->headers, GUINT_TO_POINTER (header_field), g_variant_ref_sink (value));
|
||
}
|
||
}
|
||
|
||
/**
|
||
* g_dbus_message_get_header_fields:
|
||
* @message: A #GDBusMessage.
|
||
*
|
||
* Gets an array of all header fields on @message that are set.
|
||
*
|
||
* Returns: (array zero-terminated=1): An array of header fields
|
||
* terminated by %G_DBUS_MESSAGE_HEADER_FIELD_INVALID. Each element
|
||
* is a #guchar. Free with g_free().
|
||
*
|
||
* Since: 2.26
|
||
*/
|
||
guchar *
|
||
g_dbus_message_get_header_fields (GDBusMessage *message)
|
||
{
|
||
GPtrArray *keys;
|
||
GArray *array;
|
||
|
||
g_return_val_if_fail (G_IS_DBUS_MESSAGE (message), NULL);
|
||
|
||
keys = g_hash_table_get_keys_as_ptr_array (message->headers);
|
||
array = g_array_sized_new (FALSE, FALSE, sizeof (guchar), keys->len + 1);
|
||
|
||
for (guint i = 0; i < keys->len; ++i)
|
||
{
|
||
guchar val = GPOINTER_TO_UINT (g_ptr_array_index (keys, i));
|
||
g_array_append_val (array, val);
|
||
}
|
||
|
||
g_assert (array->len == keys->len);
|
||
g_clear_pointer (&keys, g_ptr_array_unref);
|
||
|
||
guchar invalid_field = G_DBUS_MESSAGE_HEADER_FIELD_INVALID;
|
||
g_array_append_val (array, invalid_field);
|
||
|
||
return (guchar *) g_array_free (array, FALSE);
|
||
}
|
||
|
||
/* ---------------------------------------------------------------------------------------------------- */
|
||
|
||
/**
|
||
* g_dbus_message_get_body:
|
||
* @message: A #GDBusMessage.
|
||
*
|
||
* Gets the body of a message.
|
||
*
|
||
* Returns: (nullable) (transfer none): A #GVariant or %NULL if the body is
|
||
* empty. Do not free, it is owned by @message.
|
||
*
|
||
* Since: 2.26
|
||
*/
|
||
GVariant *
|
||
g_dbus_message_get_body (GDBusMessage *message)
|
||
{
|
||
g_return_val_if_fail (G_IS_DBUS_MESSAGE (message), NULL);
|
||
return message->body;
|
||
}
|
||
|
||
/**
|
||
* g_dbus_message_set_body:
|
||
* @message: A #GDBusMessage.
|
||
* @body: Either %NULL or a #GVariant that is a tuple.
|
||
*
|
||
* Sets the body @message. As a side-effect the
|
||
* %G_DBUS_MESSAGE_HEADER_FIELD_SIGNATURE header field is set to the
|
||
* type string of @body (or cleared if @body is %NULL).
|
||
*
|
||
* If @body is floating, @message assumes ownership of @body.
|
||
*
|
||
* Since: 2.26
|
||
*/
|
||
void
|
||
g_dbus_message_set_body (GDBusMessage *message,
|
||
GVariant *body)
|
||
{
|
||
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)
|
||
{
|
||
message->body = NULL;
|
||
message->arg0_cache = NULL;
|
||
g_dbus_message_set_signature (message, NULL);
|
||
}
|
||
else
|
||
{
|
||
const gchar *type_string;
|
||
gsize type_string_len;
|
||
gchar *signature;
|
||
|
||
message->body = g_variant_ref_sink (body);
|
||
|
||
if (g_variant_is_of_type (message->body, G_VARIANT_TYPE_TUPLE) &&
|
||
g_variant_n_children (message->body) > 0)
|
||
message->arg0_cache = g_variant_get_child_value (message->body, 0);
|
||
else
|
||
message->arg0_cache = NULL;
|
||
|
||
type_string = g_variant_get_type_string (body);
|
||
type_string_len = strlen (type_string);
|
||
g_assert (type_string_len >= 2);
|
||
signature = g_strndup (type_string + 1, type_string_len - 2);
|
||
g_dbus_message_set_signature (message, signature);
|
||
g_free (signature);
|
||
}
|
||
}
|
||
|
||
/* ---------------------------------------------------------------------------------------------------- */
|
||
|
||
#ifdef G_OS_UNIX
|
||
/**
|
||
* g_dbus_message_get_unix_fd_list:
|
||
* @message: A #GDBusMessage.
|
||
*
|
||
* Gets the UNIX file descriptors associated with @message, if any.
|
||
*
|
||
* This method is only available on UNIX.
|
||
*
|
||
* The file descriptors normally correspond to %G_VARIANT_TYPE_HANDLE
|
||
* values in the body of the message. For example,
|
||
* if g_variant_get_handle() returns 5, that is intended to be a reference
|
||
* to the file descriptor that can be accessed by
|
||
* `g_unix_fd_list_get (list, 5, ...)`.
|
||
*
|
||
* Returns: (nullable) (transfer none): A #GUnixFDList or %NULL if no file descriptors are
|
||
* associated. Do not free, this object is owned by @message.
|
||
*
|
||
* Since: 2.26
|
||
*/
|
||
GUnixFDList *
|
||
g_dbus_message_get_unix_fd_list (GDBusMessage *message)
|
||
{
|
||
g_return_val_if_fail (G_IS_DBUS_MESSAGE (message), NULL);
|
||
return message->fd_list;
|
||
}
|
||
|
||
/**
|
||
* g_dbus_message_set_unix_fd_list:
|
||
* @message: A #GDBusMessage.
|
||
* @fd_list: (nullable): A #GUnixFDList or %NULL.
|
||
*
|
||
* Sets the UNIX file descriptors associated with @message. As a
|
||
* side-effect the %G_DBUS_MESSAGE_HEADER_FIELD_NUM_UNIX_FDS header
|
||
* field is set to the number of fds in @fd_list (or cleared if
|
||
* @fd_list is %NULL).
|
||
*
|
||
* This method is only available on UNIX.
|
||
*
|
||
* When designing D-Bus APIs that are intended to be interoperable,
|
||
* please note that non-GDBus implementations of D-Bus can usually only
|
||
* access file descriptors if they are referenced by a value of type
|
||
* %G_VARIANT_TYPE_HANDLE in the body of the message.
|
||
*
|
||
* Since: 2.26
|
||
*/
|
||
void
|
||
g_dbus_message_set_unix_fd_list (GDBusMessage *message,
|
||
GUnixFDList *fd_list)
|
||
{
|
||
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)
|
||
{
|
||
message->fd_list = g_object_ref (fd_list);
|
||
g_dbus_message_set_num_unix_fds (message, g_unix_fd_list_get_length (fd_list));
|
||
}
|
||
else
|
||
{
|
||
message->fd_list = NULL;
|
||
g_dbus_message_set_num_unix_fds (message, 0);
|
||
}
|
||
}
|
||
#endif
|
||
|
||
/* ---------------------------------------------------------------------------------------------------- */
|
||
|
||
static guint
|
||
get_type_fixed_size (const GVariantType *type)
|
||
{
|
||
/* NB: we do not treat 'b' as fixed-size here because GVariant and
|
||
* D-Bus disagree about the size.
|
||
*/
|
||
switch (*g_variant_type_peek_string (type))
|
||
{
|
||
case 'y':
|
||
return 1;
|
||
case 'n': case 'q':
|
||
return 2;
|
||
case 'i': case 'u': case 'h':
|
||
return 4;
|
||
case 'x': case 't': case 'd':
|
||
return 8;
|
||
default:
|
||
return 0;
|
||
}
|
||
}
|
||
|
||
static const char *
|
||
message_type_to_string (GDBusMessageType message_type)
|
||
{
|
||
switch (message_type)
|
||
{
|
||
case G_DBUS_MESSAGE_TYPE_INVALID:
|
||
return "INVALID";
|
||
case G_DBUS_MESSAGE_TYPE_METHOD_CALL:
|
||
return "METHOD_CALL";
|
||
case G_DBUS_MESSAGE_TYPE_METHOD_RETURN:
|
||
return "METHOD_RETURN";
|
||
case G_DBUS_MESSAGE_TYPE_ERROR:
|
||
return "ERROR";
|
||
case G_DBUS_MESSAGE_TYPE_SIGNAL:
|
||
return "SIGNAL";
|
||
default:
|
||
return "unknown-type";
|
||
}
|
||
}
|
||
|
||
static const char *
|
||
message_header_field_to_string (GDBusMessageHeaderField field)
|
||
{
|
||
switch (field)
|
||
{
|
||
case G_DBUS_MESSAGE_HEADER_FIELD_INVALID:
|
||
return "INVALID";
|
||
case G_DBUS_MESSAGE_HEADER_FIELD_PATH:
|
||
return "PATH";
|
||
case G_DBUS_MESSAGE_HEADER_FIELD_INTERFACE:
|
||
return "INTERFACE";
|
||
case G_DBUS_MESSAGE_HEADER_FIELD_MEMBER:
|
||
return "MEMBER";
|
||
case G_DBUS_MESSAGE_HEADER_FIELD_ERROR_NAME:
|
||
return "ERROR_NAME";
|
||
case G_DBUS_MESSAGE_HEADER_FIELD_REPLY_SERIAL:
|
||
return "REPLY_SERIAL";
|
||
case G_DBUS_MESSAGE_HEADER_FIELD_DESTINATION:
|
||
return "DESTINATION";
|
||
case G_DBUS_MESSAGE_HEADER_FIELD_SENDER:
|
||
return "SENDER";
|
||
case G_DBUS_MESSAGE_HEADER_FIELD_SIGNATURE:
|
||
return "SIGNATURE";
|
||
case G_DBUS_MESSAGE_HEADER_FIELD_NUM_UNIX_FDS:
|
||
return "NUM_UNIX_FDS";
|
||
default:
|
||
return "unknown-field";
|
||
}
|
||
}
|
||
|
||
static gboolean
|
||
validate_header (GDBusMessage *message,
|
||
GDBusMessageHeaderField field,
|
||
GVariant *header_value,
|
||
const GVariantType *expected_type,
|
||
GError **error)
|
||
{
|
||
g_assert (header_value != NULL);
|
||
|
||
if (!g_variant_is_of_type (header_value, expected_type))
|
||
{
|
||
char *expected_type_string = g_variant_type_dup_string (expected_type);
|
||
g_set_error (error,
|
||
G_IO_ERROR,
|
||
G_IO_ERROR_INVALID_ARGUMENT,
|
||
_("%s message: %s header field is invalid; expected a value of type ‘%s’"),
|
||
message_type_to_string (message->type),
|
||
message_header_field_to_string (field),
|
||
expected_type_string);
|
||
g_free (expected_type_string);
|
||
return FALSE;
|
||
}
|
||
|
||
return TRUE;
|
||
}
|
||
|
||
static gboolean
|
||
require_header (GDBusMessage *message,
|
||
GDBusMessageHeaderField field,
|
||
GError **error)
|
||
{
|
||
GVariant *header_value = g_dbus_message_get_header (message, field);
|
||
|
||
if (header_value == NULL)
|
||
{
|
||
g_set_error (error,
|
||
G_IO_ERROR,
|
||
G_IO_ERROR_INVALID_ARGUMENT,
|
||
_("%s message: %s header field is missing or invalid"),
|
||
message_type_to_string (message->type),
|
||
message_header_field_to_string (field));
|
||
return FALSE;
|
||
}
|
||
|
||
return TRUE;
|
||
}
|
||
|
||
/* Implement the validation rules given in
|
||
* https://dbus.freedesktop.org/doc/dbus-specification.html#message-protocol-header-fields */
|
||
static gboolean
|
||
validate_headers (GDBusMessage *message,
|
||
GError **error)
|
||
{
|
||
gboolean ret;
|
||
GHashTableIter headers_iter;
|
||
gpointer key;
|
||
GVariant *header_value;
|
||
|
||
g_return_val_if_fail (G_IS_DBUS_MESSAGE (message), FALSE);
|
||
g_return_val_if_fail (error == NULL || *error == NULL, FALSE);
|
||
|
||
ret = FALSE;
|
||
|
||
/* Validate the types of all known headers. */
|
||
g_hash_table_iter_init (&headers_iter, message->headers);
|
||
while (g_hash_table_iter_next (&headers_iter, &key, (gpointer) &header_value))
|
||
{
|
||
GDBusMessageHeaderField field_type = GPOINTER_TO_INT (key);
|
||
|
||
switch (field_type)
|
||
{
|
||
case G_DBUS_MESSAGE_HEADER_FIELD_INVALID:
|
||
/* The invalid header must be rejected as per
|
||
* https://dbus.freedesktop.org/doc/dbus-specification.html#message-protocol-header-fields */
|
||
g_set_error (error,
|
||
G_IO_ERROR,
|
||
G_IO_ERROR_INVALID_ARGUMENT,
|
||
_("%s message: INVALID header field supplied"),
|
||
message_type_to_string (message->type));
|
||
goto out;
|
||
case G_DBUS_MESSAGE_HEADER_FIELD_PATH:
|
||
if (!validate_header (message, field_type, header_value, G_VARIANT_TYPE_OBJECT_PATH, error))
|
||
goto out;
|
||
if (g_strcmp0 (g_variant_get_string (header_value, NULL), "/org/freedesktop/DBus/Local") == 0)
|
||
{
|
||
g_set_error (error,
|
||
G_IO_ERROR,
|
||
G_IO_ERROR_INVALID_ARGUMENT,
|
||
_("%s message: PATH header field is using the reserved value /org/freedesktop/DBus/Local"),
|
||
message_type_to_string (message->type));
|
||
goto out;
|
||
}
|
||
break;
|
||
case G_DBUS_MESSAGE_HEADER_FIELD_INTERFACE:
|
||
if (!validate_header (message, field_type, header_value, G_VARIANT_TYPE_STRING, error))
|
||
goto out;
|
||
if (!g_dbus_is_interface_name (g_variant_get_string (header_value, NULL)))
|
||
{
|
||
g_set_error (error,
|
||
G_IO_ERROR,
|
||
G_IO_ERROR_INVALID_ARGUMENT,
|
||
_("%s message: INTERFACE header field does not contain a valid interface name"),
|
||
message_type_to_string (message->type));
|
||
goto out;
|
||
}
|
||
if (g_strcmp0 (g_variant_get_string (header_value, NULL), "org.freedesktop.DBus.Local") == 0)
|
||
{
|
||
g_set_error (error,
|
||
G_IO_ERROR,
|
||
G_IO_ERROR_INVALID_ARGUMENT,
|
||
_("%s message: INTERFACE header field is using the reserved value org.freedesktop.DBus.Local"),
|
||
message_type_to_string (message->type));
|
||
goto out;
|
||
}
|
||
break;
|
||
case G_DBUS_MESSAGE_HEADER_FIELD_MEMBER:
|
||
if (!validate_header (message, field_type, header_value, G_VARIANT_TYPE_STRING, error))
|
||
goto out;
|
||
if (!g_dbus_is_member_name (g_variant_get_string (header_value, NULL)))
|
||
{
|
||
g_set_error (error,
|
||
G_IO_ERROR,
|
||
G_IO_ERROR_INVALID_ARGUMENT,
|
||
_("%s message: MEMBER header field does not contain a valid member name"),
|
||
message_type_to_string (message->type));
|
||
goto out;
|
||
}
|
||
break;
|
||
case G_DBUS_MESSAGE_HEADER_FIELD_ERROR_NAME:
|
||
if (!validate_header (message, field_type, header_value, G_VARIANT_TYPE_STRING, error))
|
||
goto out;
|
||
if (!g_dbus_is_error_name (g_variant_get_string (header_value, NULL)))
|
||
{
|
||
g_set_error (error,
|
||
G_IO_ERROR,
|
||
G_IO_ERROR_INVALID_ARGUMENT,
|
||
_("%s message: ERROR_NAME header field does not contain a valid error name"),
|
||
message_type_to_string (message->type));
|
||
goto out;
|
||
}
|
||
break;
|
||
case G_DBUS_MESSAGE_HEADER_FIELD_REPLY_SERIAL:
|
||
if (!validate_header (message, field_type, header_value, G_VARIANT_TYPE_UINT32, error))
|
||
goto out;
|
||
break;
|
||
case G_DBUS_MESSAGE_HEADER_FIELD_DESTINATION:
|
||
if (!validate_header (message, field_type, header_value, G_VARIANT_TYPE_STRING, error))
|
||
goto out;
|
||
break;
|
||
case G_DBUS_MESSAGE_HEADER_FIELD_SENDER:
|
||
if (!validate_header (message, field_type, header_value, G_VARIANT_TYPE_STRING, error))
|
||
goto out;
|
||
break;
|
||
case G_DBUS_MESSAGE_HEADER_FIELD_SIGNATURE:
|
||
if (!validate_header (message, field_type, header_value, G_VARIANT_TYPE_SIGNATURE, error))
|
||
goto out;
|
||
break;
|
||
case G_DBUS_MESSAGE_HEADER_FIELD_NUM_UNIX_FDS:
|
||
if (!validate_header (message, field_type, header_value, G_VARIANT_TYPE_UINT32, error))
|
||
goto out;
|
||
break;
|
||
default:
|
||
/* Ignore unknown fields as per
|
||
* https://dbus.freedesktop.org/doc/dbus-specification.html#message-protocol-header-fields. */
|
||
continue;
|
||
}
|
||
}
|
||
|
||
/* Check for message-type-specific required headers. */
|
||
switch (message->type)
|
||
{
|
||
case G_DBUS_MESSAGE_TYPE_INVALID:
|
||
g_set_error_literal (error,
|
||
G_IO_ERROR,
|
||
G_IO_ERROR_INVALID_ARGUMENT,
|
||
_("type is INVALID"));
|
||
goto out;
|
||
|
||
case G_DBUS_MESSAGE_TYPE_METHOD_CALL:
|
||
if (!require_header (message, G_DBUS_MESSAGE_HEADER_FIELD_PATH, error) ||
|
||
!require_header (message, G_DBUS_MESSAGE_HEADER_FIELD_MEMBER, error))
|
||
goto out;
|
||
break;
|
||
|
||
case G_DBUS_MESSAGE_TYPE_METHOD_RETURN:
|
||
if (!require_header (message, G_DBUS_MESSAGE_HEADER_FIELD_REPLY_SERIAL, error))
|
||
goto out;
|
||
break;
|
||
|
||
case G_DBUS_MESSAGE_TYPE_ERROR:
|
||
if (!require_header (message, G_DBUS_MESSAGE_HEADER_FIELD_ERROR_NAME, error) ||
|
||
!require_header (message, G_DBUS_MESSAGE_HEADER_FIELD_REPLY_SERIAL, error))
|
||
goto out;
|
||
break;
|
||
|
||
case G_DBUS_MESSAGE_TYPE_SIGNAL:
|
||
if (!require_header (message, G_DBUS_MESSAGE_HEADER_FIELD_PATH, error) ||
|
||
!require_header (message, G_DBUS_MESSAGE_HEADER_FIELD_INTERFACE, error) ||
|
||
!require_header (message, G_DBUS_MESSAGE_HEADER_FIELD_MEMBER, error))
|
||
goto out;
|
||
break;
|
||
|
||
default:
|
||
/* hitherto unknown type - nothing to check */
|
||
break;
|
||
}
|
||
|
||
ret = TRUE;
|
||
|
||
out:
|
||
g_assert (ret || (error == NULL || *error != NULL));
|
||
return ret;
|
||
}
|
||
|
||
/* ---------------------------------------------------------------------------------------------------- */
|
||
|
||
static gboolean
|
||
ensure_input_padding (GMemoryBuffer *buf,
|
||
gsize padding_size)
|
||
{
|
||
gsize offset;
|
||
gsize wanted_offset;
|
||
|
||
offset = buf->pos;
|
||
wanted_offset = ((offset + padding_size - 1) / padding_size) * padding_size;
|
||
buf->pos = wanted_offset;
|
||
return TRUE;
|
||
}
|
||
|
||
static const gchar *
|
||
read_string (GMemoryBuffer *mbuf,
|
||
gsize len,
|
||
GError **error)
|
||
{
|
||
gchar *str;
|
||
const gchar *end_valid;
|
||
|
||
if G_UNLIKELY (mbuf->pos + len >= mbuf->valid_len || mbuf->pos + len < mbuf->pos)
|
||
{
|
||
mbuf->pos = mbuf->valid_len;
|
||
/* G_GSIZE_FORMAT doesn't work with gettext, so we use %lu */
|
||
g_set_error (error,
|
||
G_IO_ERROR,
|
||
G_IO_ERROR_INVALID_ARGUMENT,
|
||
g_dngettext (GETTEXT_PACKAGE,
|
||
"Wanted to read %lu byte but only got %lu",
|
||
"Wanted to read %lu bytes but only got %lu",
|
||
(gulong)len),
|
||
(gulong)len,
|
||
(gulong)(mbuf->valid_len - mbuf->pos));
|
||
return NULL;
|
||
}
|
||
|
||
if G_UNLIKELY (mbuf->data[mbuf->pos + len] != '\0')
|
||
{
|
||
str = g_strndup (mbuf->data + mbuf->pos, len);
|
||
g_set_error (error,
|
||
G_IO_ERROR,
|
||
G_IO_ERROR_INVALID_ARGUMENT,
|
||
_("Expected NUL byte after the string “%s” but found byte %d"),
|
||
str, mbuf->data[mbuf->pos + len]);
|
||
g_free (str);
|
||
mbuf->pos += len + 1;
|
||
return NULL;
|
||
}
|
||
|
||
str = mbuf->data + mbuf->pos;
|
||
mbuf->pos += len + 1;
|
||
|
||
if G_UNLIKELY (!g_utf8_validate (str, -1, &end_valid))
|
||
{
|
||
gint offset;
|
||
gchar *valid_str;
|
||
offset = (gint) (end_valid - str);
|
||
valid_str = g_strndup (str, offset);
|
||
g_set_error (error,
|
||
G_IO_ERROR,
|
||
G_IO_ERROR_INVALID_ARGUMENT,
|
||
_("Expected valid UTF-8 string but found invalid bytes at byte offset %d (length of string is %d). "
|
||
"The valid UTF-8 string up until that point was “%s”"),
|
||
offset,
|
||
(gint) len,
|
||
valid_str);
|
||
g_free (valid_str);
|
||
return NULL;
|
||
}
|
||
|
||
return str;
|
||
}
|
||
|
||
static gconstpointer
|
||
read_bytes (GMemoryBuffer *mbuf,
|
||
gsize len,
|
||
GError **error)
|
||
{
|
||
gconstpointer result;
|
||
|
||
if G_UNLIKELY (mbuf->pos + len > mbuf->valid_len || mbuf->pos + len < mbuf->pos)
|
||
{
|
||
mbuf->pos = mbuf->valid_len;
|
||
/* G_GSIZE_FORMAT doesn't work with gettext, so we use %lu */
|
||
g_set_error (error,
|
||
G_IO_ERROR,
|
||
G_IO_ERROR_INVALID_ARGUMENT,
|
||
g_dngettext (GETTEXT_PACKAGE,
|
||
"Wanted to read %lu byte but only got %lu",
|
||
"Wanted to read %lu bytes but only got %lu",
|
||
(gulong)len),
|
||
(gulong)len,
|
||
(gulong)(mbuf->valid_len - mbuf->pos));
|
||
return NULL;
|
||
}
|
||
|
||
result = mbuf->data + mbuf->pos;
|
||
mbuf->pos += len;
|
||
|
||
return result;
|
||
}
|
||
|
||
/* if just_align==TRUE, don't read a value, just align the input stream wrt padding */
|
||
|
||
/* returns a non-floating GVariant! */
|
||
static GVariant *
|
||
parse_value_from_blob (GMemoryBuffer *buf,
|
||
const GVariantType *type,
|
||
guint max_depth,
|
||
gboolean just_align,
|
||
guint indent,
|
||
GError **error)
|
||
{
|
||
GVariant *ret = NULL;
|
||
GError *local_error = NULL;
|
||
#ifdef DEBUG_SERIALIZER
|
||
gboolean is_leaf;
|
||
#endif /* DEBUG_SERIALIZER */
|
||
const gchar *type_string;
|
||
|
||
if (max_depth == 0)
|
||
{
|
||
g_set_error_literal (&local_error,
|
||
G_IO_ERROR,
|
||
G_IO_ERROR_INVALID_ARGUMENT,
|
||
_("Value nested too deeply"));
|
||
goto fail;
|
||
}
|
||
|
||
type_string = g_variant_type_peek_string (type);
|
||
|
||
#ifdef DEBUG_SERIALIZER
|
||
{
|
||
gchar *s;
|
||
s = g_variant_type_dup_string (type);
|
||
g_print ("%*s%s type %s from offset 0x%04x",
|
||
indent, "",
|
||
just_align ? "Aligning" : "Reading",
|
||
s,
|
||
(gint) buf->pos);
|
||
g_free (s);
|
||
}
|
||
#endif /* DEBUG_SERIALIZER */
|
||
|
||
#ifdef DEBUG_SERIALIZER
|
||
is_leaf = TRUE;
|
||
#endif /* DEBUG_SERIALIZER */
|
||
switch (type_string[0])
|
||
{
|
||
case 'b': /* G_VARIANT_TYPE_BOOLEAN */
|
||
ensure_input_padding (buf, 4);
|
||
if (!just_align)
|
||
{
|
||
gboolean v;
|
||
v = g_memory_buffer_read_uint32 (buf, &local_error);
|
||
if (local_error)
|
||
goto fail;
|
||
ret = g_variant_new_boolean (v);
|
||
}
|
||
break;
|
||
|
||
case 'y': /* G_VARIANT_TYPE_BYTE */
|
||
if (!just_align)
|
||
{
|
||
guchar v;
|
||
v = g_memory_buffer_read_byte (buf, &local_error);
|
||
if (local_error)
|
||
goto fail;
|
||
ret = g_variant_new_byte (v);
|
||
}
|
||
break;
|
||
|
||
case 'n': /* G_VARIANT_TYPE_INT16 */
|
||
ensure_input_padding (buf, 2);
|
||
if (!just_align)
|
||
{
|
||
gint16 v;
|
||
v = g_memory_buffer_read_int16 (buf, &local_error);
|
||
if (local_error)
|
||
goto fail;
|
||
ret = g_variant_new_int16 (v);
|
||
}
|
||
break;
|
||
|
||
case 'q': /* G_VARIANT_TYPE_UINT16 */
|
||
ensure_input_padding (buf, 2);
|
||
if (!just_align)
|
||
{
|
||
guint16 v;
|
||
v = g_memory_buffer_read_uint16 (buf, &local_error);
|
||
if (local_error)
|
||
goto fail;
|
||
ret = g_variant_new_uint16 (v);
|
||
}
|
||
break;
|
||
|
||
case 'i': /* G_VARIANT_TYPE_INT32 */
|
||
ensure_input_padding (buf, 4);
|
||
if (!just_align)
|
||
{
|
||
gint32 v;
|
||
v = g_memory_buffer_read_int32 (buf, &local_error);
|
||
if (local_error)
|
||
goto fail;
|
||
ret = g_variant_new_int32 (v);
|
||
}
|
||
break;
|
||
|
||
case 'u': /* G_VARIANT_TYPE_UINT32 */
|
||
ensure_input_padding (buf, 4);
|
||
if (!just_align)
|
||
{
|
||
guint32 v;
|
||
v = g_memory_buffer_read_uint32 (buf, &local_error);
|
||
if (local_error)
|
||
goto fail;
|
||
ret = g_variant_new_uint32 (v);
|
||
}
|
||
break;
|
||
|
||
case 'x': /* G_VARIANT_TYPE_INT64 */
|
||
ensure_input_padding (buf, 8);
|
||
if (!just_align)
|
||
{
|
||
gint64 v;
|
||
v = g_memory_buffer_read_int64 (buf, &local_error);
|
||
if (local_error)
|
||
goto fail;
|
||
ret = g_variant_new_int64 (v);
|
||
}
|
||
break;
|
||
|
||
case 't': /* G_VARIANT_TYPE_UINT64 */
|
||
ensure_input_padding (buf, 8);
|
||
if (!just_align)
|
||
{
|
||
guint64 v;
|
||
v = g_memory_buffer_read_uint64 (buf, &local_error);
|
||
if (local_error)
|
||
goto fail;
|
||
ret = g_variant_new_uint64 (v);
|
||
}
|
||
break;
|
||
|
||
case 'd': /* G_VARIANT_TYPE_DOUBLE */
|
||
ensure_input_padding (buf, 8);
|
||
if (!just_align)
|
||
{
|
||
union {
|
||
guint64 v_uint64;
|
||
gdouble v_double;
|
||
} u;
|
||
G_STATIC_ASSERT (sizeof (gdouble) == sizeof (guint64));
|
||
u.v_uint64 = g_memory_buffer_read_uint64 (buf, &local_error);
|
||
if (local_error)
|
||
goto fail;
|
||
ret = g_variant_new_double (u.v_double);
|
||
}
|
||
break;
|
||
|
||
case 's': /* G_VARIANT_TYPE_STRING */
|
||
ensure_input_padding (buf, 4);
|
||
if (!just_align)
|
||
{
|
||
guint32 len;
|
||
const gchar *v;
|
||
len = g_memory_buffer_read_uint32 (buf, &local_error);
|
||
if (local_error)
|
||
goto fail;
|
||
v = read_string (buf, (gsize) len, &local_error);
|
||
if (v == NULL)
|
||
goto fail;
|
||
ret = g_variant_new_string (v);
|
||
}
|
||
break;
|
||
|
||
case 'o': /* G_VARIANT_TYPE_OBJECT_PATH */
|
||
ensure_input_padding (buf, 4);
|
||
if (!just_align)
|
||
{
|
||
guint32 len;
|
||
const gchar *v;
|
||
len = g_memory_buffer_read_uint32 (buf, &local_error);
|
||
if (local_error)
|
||
goto fail;
|
||
v = read_string (buf, (gsize) len, &local_error);
|
||
if (v == NULL)
|
||
goto fail;
|
||
if (!g_variant_is_object_path (v))
|
||
{
|
||
g_set_error (&local_error,
|
||
G_IO_ERROR,
|
||
G_IO_ERROR_INVALID_ARGUMENT,
|
||
_("Parsed value “%s” is not a valid D-Bus object path"),
|
||
v);
|
||
goto fail;
|
||
}
|
||
ret = g_variant_new_object_path (v);
|
||
}
|
||
break;
|
||
|
||
case 'g': /* G_VARIANT_TYPE_SIGNATURE */
|
||
if (!just_align)
|
||
{
|
||
guchar len;
|
||
const gchar *v;
|
||
len = g_memory_buffer_read_byte (buf, &local_error);
|
||
if (local_error)
|
||
goto fail;
|
||
v = read_string (buf, (gsize) len, &local_error);
|
||
if (v == NULL)
|
||
goto fail;
|
||
if (!g_variant_is_signature (v))
|
||
{
|
||
g_set_error (&local_error,
|
||
G_IO_ERROR,
|
||
G_IO_ERROR_INVALID_ARGUMENT,
|
||
_("Parsed value “%s” is not a valid D-Bus signature"),
|
||
v);
|
||
goto fail;
|
||
}
|
||
ret = g_variant_new_signature (v);
|
||
}
|
||
break;
|
||
|
||
case 'h': /* G_VARIANT_TYPE_HANDLE */
|
||
ensure_input_padding (buf, 4);
|
||
if (!just_align)
|
||
{
|
||
gint32 v;
|
||
v = g_memory_buffer_read_int32 (buf, &local_error);
|
||
if (local_error)
|
||
goto fail;
|
||
ret = g_variant_new_handle (v);
|
||
}
|
||
break;
|
||
|
||
case 'a': /* G_VARIANT_TYPE_ARRAY */
|
||
ensure_input_padding (buf, 4);
|
||
|
||
/* If we are only aligning for this array type, it is the child type of
|
||
* another array, which is empty. So, we do not need to add padding for
|
||
* this nonexistent array's elements: we only need to align for this
|
||
* array itself (4 bytes). See
|
||
* <https://bugzilla.gnome.org/show_bug.cgi?id=673612>.
|
||
*/
|
||
if (!just_align)
|
||
{
|
||
guint32 array_len;
|
||
const GVariantType *element_type;
|
||
guint fixed_size;
|
||
|
||
array_len = g_memory_buffer_read_uint32 (buf, &local_error);
|
||
if (local_error)
|
||
goto fail;
|
||
|
||
#ifdef DEBUG_SERIALIZER
|
||
is_leaf = FALSE;
|
||
g_print (": array spans 0x%04x bytes\n", array_len);
|
||
#endif /* DEBUG_SERIALIZER */
|
||
|
||
if (array_len > (2<<26))
|
||
{
|
||
/* G_GUINT32_FORMAT doesn't work with gettext, so use u */
|
||
g_set_error (&local_error,
|
||
G_IO_ERROR,
|
||
G_IO_ERROR_INVALID_ARGUMENT,
|
||
g_dngettext (GETTEXT_PACKAGE,
|
||
"Encountered array of length %u byte. Maximum length is 2<<26 bytes (64 MiB).",
|
||
"Encountered array of length %u bytes. Maximum length is 2<<26 bytes (64 MiB).",
|
||
array_len),
|
||
array_len);
|
||
goto fail;
|
||
}
|
||
|
||
element_type = g_variant_type_element (type);
|
||
fixed_size = get_type_fixed_size (element_type);
|
||
|
||
/* Fast-path the cases like 'ay', etc. */
|
||
if (fixed_size != 0)
|
||
{
|
||
gconstpointer array_data;
|
||
|
||
if (array_len % fixed_size != 0)
|
||
{
|
||
g_set_error (&local_error,
|
||
G_IO_ERROR,
|
||
G_IO_ERROR_INVALID_ARGUMENT,
|
||
_("Encountered array of type “a%c”, expected to have a length a multiple "
|
||
"of %u bytes, but found to be %u bytes in length"),
|
||
g_variant_type_peek_string (element_type)[0], fixed_size, array_len);
|
||
goto fail;
|
||
}
|
||
|
||
if (max_depth == 1)
|
||
{
|
||
/* If we had recursed into parse_value_from_blob() again to
|
||
* parse the array values, this would have been emitted. */
|
||
g_set_error_literal (&local_error,
|
||
G_IO_ERROR,
|
||
G_IO_ERROR_INVALID_ARGUMENT,
|
||
_("Value nested too deeply"));
|
||
goto fail;
|
||
}
|
||
|
||
ensure_input_padding (buf, fixed_size);
|
||
array_data = read_bytes (buf, array_len, &local_error);
|
||
if (array_data == NULL)
|
||
goto fail;
|
||
|
||
ret = g_variant_new_fixed_array (element_type, array_data, array_len / fixed_size, fixed_size);
|
||
|
||
if (g_memory_buffer_is_byteswapped (buf))
|
||
{
|
||
GVariant *tmp = g_variant_ref_sink (ret);
|
||
ret = g_variant_byteswap (tmp);
|
||
g_variant_unref (tmp);
|
||
}
|
||
}
|
||
else
|
||
{
|
||
GVariantBuilder builder;
|
||
goffset offset;
|
||
goffset target;
|
||
|
||
g_variant_builder_init (&builder, type);
|
||
|
||
if (array_len == 0)
|
||
{
|
||
GVariant *item G_GNUC_UNUSED /* when compiling with G_DISABLE_ASSERT */;
|
||
item = parse_value_from_blob (buf,
|
||
element_type,
|
||
max_depth - 1,
|
||
TRUE,
|
||
indent + 2,
|
||
NULL);
|
||
g_assert (item == NULL);
|
||
}
|
||
else
|
||
{
|
||
offset = buf->pos;
|
||
target = offset + array_len;
|
||
while (offset < target)
|
||
{
|
||
GVariant *item;
|
||
item = parse_value_from_blob (buf,
|
||
element_type,
|
||
max_depth - 1,
|
||
FALSE,
|
||
indent + 2,
|
||
&local_error);
|
||
if (item == NULL)
|
||
{
|
||
g_variant_builder_clear (&builder);
|
||
goto fail;
|
||
}
|
||
g_variant_builder_add_value (&builder, item);
|
||
g_variant_unref (item);
|
||
|
||
/* Array elements must not be zero-length. There are no
|
||
* valid zero-length serialisations of any types which
|
||
* can be array elements in the D-Bus wire format, so this
|
||
* assertion should always hold.
|
||
*
|
||
* See https://gitlab.gnome.org/GNOME/glib/-/issues/2557
|
||
*/
|
||
g_assert (buf->pos > (gsize) offset);
|
||
|
||
offset = buf->pos;
|
||
}
|
||
}
|
||
|
||
ret = g_variant_builder_end (&builder);
|
||
}
|
||
}
|
||
break;
|
||
|
||
default:
|
||
if (g_variant_type_is_dict_entry (type))
|
||
{
|
||
const GVariantType *key_type;
|
||
const GVariantType *value_type;
|
||
GVariant *key;
|
||
GVariant *value;
|
||
|
||
ensure_input_padding (buf, 8);
|
||
|
||
#ifdef DEBUG_SERIALIZER
|
||
is_leaf = FALSE;
|
||
g_print ("\n");
|
||
#endif /* DEBUG_SERIALIZER */
|
||
|
||
if (!just_align)
|
||
{
|
||
key_type = g_variant_type_key (type);
|
||
key = parse_value_from_blob (buf,
|
||
key_type,
|
||
max_depth - 1,
|
||
FALSE,
|
||
indent + 2,
|
||
&local_error);
|
||
if (key == NULL)
|
||
goto fail;
|
||
value_type = g_variant_type_value (type);
|
||
value = parse_value_from_blob (buf,
|
||
value_type,
|
||
max_depth - 1,
|
||
FALSE,
|
||
indent + 2,
|
||
&local_error);
|
||
if (value == NULL)
|
||
{
|
||
g_variant_unref (key);
|
||
goto fail;
|
||
}
|
||
ret = g_variant_new_dict_entry (key, value);
|
||
g_variant_unref (key);
|
||
g_variant_unref (value);
|
||
}
|
||
}
|
||
else if (g_variant_type_is_tuple (type))
|
||
{
|
||
ensure_input_padding (buf, 8);
|
||
|
||
#ifdef DEBUG_SERIALIZER
|
||
is_leaf = FALSE;
|
||
g_print ("\n");
|
||
#endif /* DEBUG_SERIALIZER */
|
||
|
||
if (!just_align)
|
||
{
|
||
const GVariantType *element_type;
|
||
GVariantBuilder builder;
|
||
|
||
g_variant_builder_init (&builder, type);
|
||
element_type = g_variant_type_first (type);
|
||
if (!element_type)
|
||
{
|
||
g_variant_builder_clear (&builder);
|
||
g_set_error_literal (&local_error,
|
||
G_IO_ERROR,
|
||
G_IO_ERROR_INVALID_ARGUMENT,
|
||
_("Empty structures (tuples) are not allowed in D-Bus"));
|
||
goto fail;
|
||
}
|
||
|
||
while (element_type != NULL)
|
||
{
|
||
GVariant *item;
|
||
item = parse_value_from_blob (buf,
|
||
element_type,
|
||
max_depth - 1,
|
||
FALSE,
|
||
indent + 2,
|
||
&local_error);
|
||
if (item == NULL)
|
||
{
|
||
g_variant_builder_clear (&builder);
|
||
goto fail;
|
||
}
|
||
g_variant_builder_add_value (&builder, item);
|
||
g_variant_unref (item);
|
||
|
||
element_type = g_variant_type_next (element_type);
|
||
}
|
||
ret = g_variant_builder_end (&builder);
|
||
}
|
||
}
|
||
else if (g_variant_type_is_variant (type))
|
||
{
|
||
#ifdef DEBUG_SERIALIZER
|
||
is_leaf = FALSE;
|
||
g_print ("\n");
|
||
#endif /* DEBUG_SERIALIZER */
|
||
|
||
if (!just_align)
|
||
{
|
||
guchar siglen;
|
||
const gchar *sig;
|
||
GVariantType *variant_type;
|
||
GVariant *value;
|
||
|
||
siglen = g_memory_buffer_read_byte (buf, &local_error);
|
||
if (local_error)
|
||
goto fail;
|
||
sig = read_string (buf, (gsize) siglen, &local_error);
|
||
if (sig == NULL)
|
||
goto fail;
|
||
if (!g_variant_is_signature (sig) ||
|
||
!g_variant_type_string_is_valid (sig))
|
||
{
|
||
/* A D-Bus signature can contain zero or more complete types,
|
||
* but a GVariant has to be exactly one complete type. */
|
||
g_set_error (&local_error,
|
||
G_IO_ERROR,
|
||
G_IO_ERROR_INVALID_ARGUMENT,
|
||
_("Parsed value “%s” for variant is not a valid D-Bus signature"),
|
||
sig);
|
||
goto fail;
|
||
}
|
||
|
||
if (max_depth <= g_variant_type_string_get_depth_ (sig))
|
||
{
|
||
/* Catch the type nesting being too deep without having to
|
||
* parse the data. We don’t have to check this for static
|
||
* container types (like arrays and tuples, above) because
|
||
* the g_variant_type_string_is_valid() check performed before
|
||
* the initial parse_value_from_blob() call should check the
|
||
* static type nesting. */
|
||
g_set_error_literal (&local_error,
|
||
G_IO_ERROR,
|
||
G_IO_ERROR_INVALID_ARGUMENT,
|
||
_("Value nested too deeply"));
|
||
goto fail;
|
||
}
|
||
|
||
variant_type = g_variant_type_new (sig);
|
||
value = parse_value_from_blob (buf,
|
||
variant_type,
|
||
max_depth - 1,
|
||
FALSE,
|
||
indent + 2,
|
||
&local_error);
|
||
g_variant_type_free (variant_type);
|
||
if (value == NULL)
|
||
goto fail;
|
||
ret = g_variant_new_variant (value);
|
||
g_variant_unref (value);
|
||
}
|
||
}
|
||
else
|
||
{
|
||
gchar *s;
|
||
s = g_variant_type_dup_string (type);
|
||
g_set_error (&local_error,
|
||
G_IO_ERROR,
|
||
G_IO_ERROR_INVALID_ARGUMENT,
|
||
_("Error deserializing GVariant with type string “%s” from the D-Bus wire format"),
|
||
s);
|
||
g_free (s);
|
||
goto fail;
|
||
}
|
||
break;
|
||
}
|
||
|
||
g_assert ((just_align && ret == NULL) || (!just_align && ret != NULL));
|
||
|
||
#ifdef DEBUG_SERIALIZER
|
||
if (ret != NULL)
|
||
{
|
||
if (is_leaf)
|
||
{
|
||
gchar *s;
|
||
if (g_variant_type_equal (type, G_VARIANT_TYPE_BYTE))
|
||
{
|
||
s = g_strdup_printf ("0x%02x '%c'", g_variant_get_byte (ret), g_variant_get_byte (ret));
|
||
}
|
||
else
|
||
{
|
||
s = g_variant_print (ret, FALSE);
|
||
}
|
||
g_print (": %s\n", s);
|
||
g_free (s);
|
||
}
|
||
}
|
||
#endif /* DEBUG_SERIALIZER */
|
||
|
||
/* sink the reference, if floating */
|
||
if (ret != NULL)
|
||
g_variant_take_ref (ret);
|
||
return ret;
|
||
|
||
fail:
|
||
#ifdef DEBUG_SERIALIZER
|
||
g_print ("\n"
|
||
"%*sFAILURE: %s (%s, %d)\n",
|
||
indent, "",
|
||
local_error->message,
|
||
g_quark_to_string (local_error->domain),
|
||
local_error->code);
|
||
#endif /* DEBUG_SERIALIZER */
|
||
g_propagate_error (error, local_error);
|
||
return NULL;
|
||
}
|
||
|
||
/* ---------------------------------------------------------------------------------------------------- */
|
||
|
||
/* message_header must be at least 16 bytes */
|
||
|
||
/**
|
||
* g_dbus_message_bytes_needed:
|
||
* @blob: (array length=blob_len) (element-type guint8): A blob representing a binary D-Bus message.
|
||
* @blob_len: The length of @blob (must be at least 16).
|
||
* @error: Return location for error or %NULL.
|
||
*
|
||
* Utility function to calculate how many bytes are needed to
|
||
* completely deserialize the D-Bus message stored at @blob.
|
||
*
|
||
* Returns: Number of bytes needed or -1 if @error is set (e.g. if
|
||
* @blob contains invalid data or not enough data is available to
|
||
* determine the size).
|
||
*
|
||
* Since: 2.26
|
||
*/
|
||
gssize
|
||
g_dbus_message_bytes_needed (guchar *blob,
|
||
gsize blob_len,
|
||
GError **error)
|
||
{
|
||
gssize ret;
|
||
|
||
ret = -1;
|
||
|
||
g_return_val_if_fail (blob != NULL, -1);
|
||
g_return_val_if_fail (error == NULL || *error == NULL, -1);
|
||
g_return_val_if_fail (blob_len >= 16, -1);
|
||
|
||
if (blob[0] == 'l')
|
||
{
|
||
/* core header (12 bytes) + ARRAY of STRUCT of (BYTE,VARIANT) */
|
||
ret = 12 + 4 + GUINT32_FROM_LE (((guint32 *) blob)[3]);
|
||
/* round up so it's a multiple of 8 */
|
||
ret = 8 * ((ret + 7)/8);
|
||
/* finally add the body size */
|
||
ret += GUINT32_FROM_LE (((guint32 *) blob)[1]);
|
||
}
|
||
else if (blob[0] == 'B')
|
||
{
|
||
/* core header (12 bytes) + ARRAY of STRUCT of (BYTE,VARIANT) */
|
||
ret = 12 + 4 + GUINT32_FROM_BE (((guint32 *) blob)[3]);
|
||
/* round up so it's a multiple of 8 */
|
||
ret = 8 * ((ret + 7)/8);
|
||
/* finally add the body size */
|
||
ret += GUINT32_FROM_BE (((guint32 *) blob)[1]);
|
||
}
|
||
else
|
||
{
|
||
g_set_error (error,
|
||
G_IO_ERROR,
|
||
G_IO_ERROR_INVALID_ARGUMENT,
|
||
"Unable to determine message blob length - given blob is malformed");
|
||
}
|
||
|
||
if (ret > (1<<27))
|
||
{
|
||
g_set_error (error,
|
||
G_IO_ERROR,
|
||
G_IO_ERROR_INVALID_ARGUMENT,
|
||
"Blob indicates that message exceeds maximum message length (128MiB)");
|
||
ret = -1;
|
||
}
|
||
|
||
return ret;
|
||
}
|
||
|
||
/* ---------------------------------------------------------------------------------------------------- */
|
||
|
||
/**
|
||
* g_dbus_message_new_from_blob:
|
||
* @blob: (array length=blob_len) (element-type guint8): A blob representing a binary D-Bus message.
|
||
* @blob_len: The length of @blob.
|
||
* @capabilities: A #GDBusCapabilityFlags describing what protocol features are supported.
|
||
* @error: Return location for error or %NULL.
|
||
*
|
||
* Creates a new #GDBusMessage from the data stored at @blob. The byte
|
||
* order that the message was in can be retrieved using
|
||
* g_dbus_message_get_byte_order().
|
||
*
|
||
* If the @blob cannot be parsed, contains invalid fields, or contains invalid
|
||
* headers, %G_IO_ERROR_INVALID_ARGUMENT will be returned.
|
||
*
|
||
* Returns: A new #GDBusMessage or %NULL if @error is set. Free with
|
||
* g_object_unref().
|
||
*
|
||
* Since: 2.26
|
||
*/
|
||
GDBusMessage *
|
||
g_dbus_message_new_from_blob (guchar *blob,
|
||
gsize blob_len,
|
||
GDBusCapabilityFlags capabilities,
|
||
GError **error)
|
||
{
|
||
GError *local_error = NULL;
|
||
GMemoryBuffer mbuf;
|
||
GDBusMessage *message;
|
||
guchar endianness;
|
||
guchar major_protocol_version;
|
||
guint32 message_body_len;
|
||
GVariant *headers;
|
||
GVariant *item;
|
||
GVariantIter iter;
|
||
GVariant *signature;
|
||
|
||
/* TODO: check against @capabilities */
|
||
|
||
g_return_val_if_fail (blob != NULL, NULL);
|
||
g_return_val_if_fail (error == NULL || *error == NULL, NULL);
|
||
|
||
message = g_dbus_message_new ();
|
||
|
||
memset (&mbuf, 0, sizeof (mbuf));
|
||
mbuf.data = (gchar *)blob;
|
||
mbuf.len = mbuf.valid_len = blob_len;
|
||
|
||
endianness = g_memory_buffer_read_byte (&mbuf, &local_error);
|
||
if (local_error)
|
||
goto fail;
|
||
|
||
switch (endianness)
|
||
{
|
||
case 'l':
|
||
mbuf.byte_order = G_DATA_STREAM_BYTE_ORDER_LITTLE_ENDIAN;
|
||
message->byte_order = G_DBUS_MESSAGE_BYTE_ORDER_LITTLE_ENDIAN;
|
||
break;
|
||
case 'B':
|
||
mbuf.byte_order = G_DATA_STREAM_BYTE_ORDER_BIG_ENDIAN;
|
||
message->byte_order = G_DBUS_MESSAGE_BYTE_ORDER_BIG_ENDIAN;
|
||
break;
|
||
default:
|
||
g_set_error (&local_error,
|
||
G_IO_ERROR,
|
||
G_IO_ERROR_INVALID_ARGUMENT,
|
||
_("Invalid endianness value. Expected 0x6c (“l”) or 0x42 (“B”) but found value 0x%02x"),
|
||
endianness);
|
||
goto fail;
|
||
}
|
||
|
||
message->type = g_memory_buffer_read_byte (&mbuf, &local_error);
|
||
if (local_error)
|
||
goto fail;
|
||
message->flags = g_memory_buffer_read_byte (&mbuf, &local_error);
|
||
if (local_error)
|
||
goto fail;
|
||
major_protocol_version = g_memory_buffer_read_byte (&mbuf, &local_error);
|
||
if (local_error)
|
||
goto fail;
|
||
if (major_protocol_version != 1)
|
||
{
|
||
g_set_error (&local_error,
|
||
G_IO_ERROR,
|
||
G_IO_ERROR_INVALID_ARGUMENT,
|
||
_("Invalid major protocol version. Expected 1 but found %d"),
|
||
major_protocol_version);
|
||
goto fail;
|
||
}
|
||
message_body_len = g_memory_buffer_read_uint32 (&mbuf, &local_error);
|
||
if (local_error)
|
||
goto fail;
|
||
message->serial = g_memory_buffer_read_uint32 (&mbuf, &local_error);
|
||
if (local_error)
|
||
goto fail;
|
||
|
||
#ifdef DEBUG_SERIALIZER
|
||
g_print ("Parsing blob (blob_len = 0x%04x bytes)\n", (gint) blob_len);
|
||
{
|
||
gchar *s;
|
||
s = _g_dbus_hexdump ((const gchar *) blob, blob_len, 2);
|
||
g_print ("%s\n", s);
|
||
g_free (s);
|
||
}
|
||
#endif /* DEBUG_SERIALIZER */
|
||
|
||
#ifdef DEBUG_SERIALIZER
|
||
g_print ("Parsing headers (blob_len = 0x%04x bytes)\n", (gint) blob_len);
|
||
#endif /* DEBUG_SERIALIZER */
|
||
headers = parse_value_from_blob (&mbuf,
|
||
G_VARIANT_TYPE ("a{yv}"),
|
||
G_DBUS_MAX_TYPE_DEPTH + 2 /* for the a{yv} */,
|
||
FALSE,
|
||
2,
|
||
&local_error);
|
||
if (headers == NULL)
|
||
goto fail;
|
||
g_variant_iter_init (&iter, headers);
|
||
while ((item = g_variant_iter_next_value (&iter)) != NULL)
|
||
{
|
||
guchar header_field;
|
||
GVariant *value;
|
||
g_variant_get (item,
|
||
"{yv}",
|
||
&header_field,
|
||
&value);
|
||
g_dbus_message_set_header (message, header_field, value);
|
||
g_variant_unref (value);
|
||
g_variant_unref (item);
|
||
}
|
||
g_variant_unref (headers);
|
||
|
||
signature = g_dbus_message_get_header (message, G_DBUS_MESSAGE_HEADER_FIELD_SIGNATURE);
|
||
if (signature != NULL)
|
||
{
|
||
const gchar *signature_str;
|
||
gsize signature_str_len;
|
||
|
||
if (!g_variant_is_of_type (signature, G_VARIANT_TYPE_SIGNATURE))
|
||
{
|
||
g_set_error_literal (&local_error,
|
||
G_IO_ERROR,
|
||
G_IO_ERROR_INVALID_ARGUMENT,
|
||
_("Signature header found but is not of type signature"));
|
||
goto fail;
|
||
}
|
||
|
||
signature_str = g_variant_get_string (signature, &signature_str_len);
|
||
|
||
/* signature but no body */
|
||
if (message_body_len == 0 && signature_str_len > 0)
|
||
{
|
||
g_set_error (&local_error,
|
||
G_IO_ERROR,
|
||
G_IO_ERROR_INVALID_ARGUMENT,
|
||
_("Signature header with signature “%s” found but message body is empty"),
|
||
signature_str);
|
||
goto fail;
|
||
}
|
||
else if (signature_str_len > 0)
|
||
{
|
||
GVariantType *variant_type;
|
||
gchar *tupled_signature_str = g_strdup_printf ("(%s)", signature_str);
|
||
|
||
if (!g_variant_is_signature (signature_str) ||
|
||
!g_variant_type_string_is_valid (tupled_signature_str))
|
||
{
|
||
g_set_error (&local_error,
|
||
G_IO_ERROR,
|
||
G_IO_ERROR_INVALID_ARGUMENT,
|
||
_("Parsed value “%s” is not a valid D-Bus signature (for body)"),
|
||
signature_str);
|
||
g_free (tupled_signature_str);
|
||
goto fail;
|
||
}
|
||
|
||
variant_type = g_variant_type_new (tupled_signature_str);
|
||
g_free (tupled_signature_str);
|
||
#ifdef DEBUG_SERIALIZER
|
||
g_print ("Parsing body (blob_len = 0x%04x bytes)\n", (gint) blob_len);
|
||
#endif /* DEBUG_SERIALIZER */
|
||
message->body = parse_value_from_blob (&mbuf,
|
||
variant_type,
|
||
G_DBUS_MAX_TYPE_DEPTH + 1 /* for the surrounding tuple */,
|
||
FALSE,
|
||
2,
|
||
&local_error);
|
||
g_variant_type_free (variant_type);
|
||
|
||
if (message->body != NULL &&
|
||
g_variant_is_of_type (message->body, G_VARIANT_TYPE_TUPLE) &&
|
||
g_variant_n_children (message->body) > 0)
|
||
message->arg0_cache = g_variant_get_child_value (message->body, 0);
|
||
else
|
||
message->arg0_cache = NULL;
|
||
|
||
if (message->body == NULL)
|
||
goto fail;
|
||
}
|
||
}
|
||
else
|
||
{
|
||
/* no signature, this is only OK if the body is empty */
|
||
if (message_body_len != 0)
|
||
{
|
||
/* G_GUINT32_FORMAT doesn't work with gettext, just use %u */
|
||
g_set_error (&local_error,
|
||
G_IO_ERROR,
|
||
G_IO_ERROR_INVALID_ARGUMENT,
|
||
g_dngettext (GETTEXT_PACKAGE,
|
||
"No signature header in message but the message body is %u byte",
|
||
"No signature header in message but the message body is %u bytes",
|
||
message_body_len),
|
||
message_body_len);
|
||
goto fail;
|
||
}
|
||
}
|
||
|
||
if (!validate_headers (message, &local_error))
|
||
{
|
||
g_prefix_error (&local_error, _("Cannot deserialize message: "));
|
||
goto fail;
|
||
}
|
||
|
||
return message;
|
||
|
||
fail:
|
||
g_clear_object (&message);
|
||
g_propagate_error (error, local_error);
|
||
return NULL;
|
||
}
|
||
|
||
/* ---------------------------------------------------------------------------------------------------- */
|
||
|
||
static gsize
|
||
ensure_output_padding (GMemoryBuffer *mbuf,
|
||
gsize padding_size)
|
||
{
|
||
gsize offset;
|
||
gsize wanted_offset;
|
||
gsize padding_needed;
|
||
guint n;
|
||
|
||
offset = mbuf->pos;
|
||
wanted_offset = ((offset + padding_size - 1) / padding_size) * padding_size;
|
||
padding_needed = wanted_offset - offset;
|
||
|
||
for (n = 0; n < padding_needed; n++)
|
||
g_memory_buffer_put_byte (mbuf, '\0');
|
||
|
||
return padding_needed;
|
||
}
|
||
|
||
/* note that value can be NULL for e.g. empty arrays - type is never NULL */
|
||
static gboolean
|
||
append_value_to_blob (GVariant *value,
|
||
const GVariantType *type,
|
||
GMemoryBuffer *mbuf,
|
||
gsize *out_padding_added,
|
||
GError **error)
|
||
{
|
||
gsize padding_added;
|
||
const gchar *type_string;
|
||
|
||
type_string = g_variant_type_peek_string (type);
|
||
|
||
padding_added = 0;
|
||
|
||
switch (type_string[0])
|
||
{
|
||
case 'b': /* G_VARIANT_TYPE_BOOLEAN */
|
||
padding_added = ensure_output_padding (mbuf, 4);
|
||
if (value != NULL)
|
||
{
|
||
gboolean v = g_variant_get_boolean (value);
|
||
g_memory_buffer_put_uint32 (mbuf, v);
|
||
}
|
||
break;
|
||
|
||
case 'y': /* G_VARIANT_TYPE_BYTE */
|
||
if (value != NULL)
|
||
{
|
||
guint8 v = g_variant_get_byte (value);
|
||
g_memory_buffer_put_byte (mbuf, v);
|
||
}
|
||
break;
|
||
|
||
case 'n': /* G_VARIANT_TYPE_INT16 */
|
||
padding_added = ensure_output_padding (mbuf, 2);
|
||
if (value != NULL)
|
||
{
|
||
gint16 v = g_variant_get_int16 (value);
|
||
g_memory_buffer_put_int16 (mbuf, v);
|
||
}
|
||
break;
|
||
|
||
case 'q': /* G_VARIANT_TYPE_UINT16 */
|
||
padding_added = ensure_output_padding (mbuf, 2);
|
||
if (value != NULL)
|
||
{
|
||
guint16 v = g_variant_get_uint16 (value);
|
||
g_memory_buffer_put_uint16 (mbuf, v);
|
||
}
|
||
break;
|
||
|
||
case 'i': /* G_VARIANT_TYPE_INT32 */
|
||
padding_added = ensure_output_padding (mbuf, 4);
|
||
if (value != NULL)
|
||
{
|
||
gint32 v = g_variant_get_int32 (value);
|
||
g_memory_buffer_put_int32 (mbuf, v);
|
||
}
|
||
break;
|
||
|
||
case 'u': /* G_VARIANT_TYPE_UINT32 */
|
||
padding_added = ensure_output_padding (mbuf, 4);
|
||
if (value != NULL)
|
||
{
|
||
guint32 v = g_variant_get_uint32 (value);
|
||
g_memory_buffer_put_uint32 (mbuf, v);
|
||
}
|
||
break;
|
||
|
||
case 'x': /* G_VARIANT_TYPE_INT64 */
|
||
padding_added = ensure_output_padding (mbuf, 8);
|
||
if (value != NULL)
|
||
{
|
||
gint64 v = g_variant_get_int64 (value);
|
||
g_memory_buffer_put_int64 (mbuf, v);
|
||
}
|
||
break;
|
||
|
||
case 't': /* G_VARIANT_TYPE_UINT64 */
|
||
padding_added = ensure_output_padding (mbuf, 8);
|
||
if (value != NULL)
|
||
{
|
||
guint64 v = g_variant_get_uint64 (value);
|
||
g_memory_buffer_put_uint64 (mbuf, v);
|
||
}
|
||
break;
|
||
|
||
case 'd': /* G_VARIANT_TYPE_DOUBLE */
|
||
padding_added = ensure_output_padding (mbuf, 8);
|
||
if (value != NULL)
|
||
{
|
||
union {
|
||
guint64 v_uint64;
|
||
gdouble v_double;
|
||
} u;
|
||
G_STATIC_ASSERT (sizeof (gdouble) == sizeof (guint64));
|
||
u.v_double = g_variant_get_double (value);
|
||
g_memory_buffer_put_uint64 (mbuf, u.v_uint64);
|
||
}
|
||
break;
|
||
|
||
case 's': /* G_VARIANT_TYPE_STRING */
|
||
padding_added = ensure_output_padding (mbuf, 4);
|
||
if (value != NULL)
|
||
{
|
||
gsize len;
|
||
const gchar *v;
|
||
#ifndef G_DISABLE_ASSERT
|
||
const gchar *end;
|
||
#endif
|
||
|
||
v = g_variant_get_string (value, &len);
|
||
g_assert (g_utf8_validate (v, -1, &end) && (end == v + len));
|
||
g_memory_buffer_put_uint32 (mbuf, len);
|
||
g_memory_buffer_put_string (mbuf, v);
|
||
g_memory_buffer_put_byte (mbuf, '\0');
|
||
}
|
||
break;
|
||
|
||
case 'o': /* G_VARIANT_TYPE_OBJECT_PATH */
|
||
padding_added = ensure_output_padding (mbuf, 4);
|
||
if (value != NULL)
|
||
{
|
||
gsize len;
|
||
const gchar *v = g_variant_get_string (value, &len);
|
||
g_assert (g_variant_is_object_path (v));
|
||
g_memory_buffer_put_uint32 (mbuf, len);
|
||
g_memory_buffer_put_string (mbuf, v);
|
||
g_memory_buffer_put_byte (mbuf, '\0');
|
||
}
|
||
break;
|
||
|
||
case 'g': /* G_VARIANT_TYPE_SIGNATURE */
|
||
if (value != NULL)
|
||
{
|
||
gsize len;
|
||
const gchar *v = g_variant_get_string (value, &len);
|
||
g_assert (g_variant_is_signature (v));
|
||
g_memory_buffer_put_byte (mbuf, len);
|
||
g_memory_buffer_put_string (mbuf, v);
|
||
g_memory_buffer_put_byte (mbuf, '\0');
|
||
}
|
||
break;
|
||
|
||
case 'h': /* G_VARIANT_TYPE_HANDLE */
|
||
padding_added = ensure_output_padding (mbuf, 4);
|
||
if (value != NULL)
|
||
{
|
||
gint32 v = g_variant_get_handle (value);
|
||
g_memory_buffer_put_int32 (mbuf, v);
|
||
}
|
||
break;
|
||
|
||
case 'a': /* G_VARIANT_TYPE_ARRAY */
|
||
{
|
||
const GVariantType *element_type;
|
||
GVariant *item;
|
||
GVariantIter iter;
|
||
goffset array_len_offset;
|
||
goffset array_payload_begin_offset;
|
||
goffset cur_offset;
|
||
gsize array_len;
|
||
guint fixed_size;
|
||
|
||
padding_added = ensure_output_padding (mbuf, 4);
|
||
if (value != NULL)
|
||
{
|
||
/* array length - will be filled in later */
|
||
array_len_offset = mbuf->valid_len;
|
||
g_memory_buffer_put_uint32 (mbuf, 0xF00DFACE);
|
||
|
||
/* From the D-Bus spec:
|
||
*
|
||
* "A UINT32 giving the length of the array data in bytes,
|
||
* followed by alignment padding to the alignment boundary of
|
||
* the array element type, followed by each array element. The
|
||
* array length is from the end of the alignment padding to
|
||
* the end of the last element, i.e. it does not include the
|
||
* padding after the length, or any padding after the last
|
||
* element."
|
||
*
|
||
* Thus, we need to count how much padding the first element
|
||
* contributes and subtract that from the array length.
|
||
*/
|
||
array_payload_begin_offset = mbuf->valid_len;
|
||
|
||
element_type = g_variant_type_element (type);
|
||
fixed_size = get_type_fixed_size (element_type);
|
||
|
||
if (g_variant_n_children (value) == 0)
|
||
{
|
||
gsize padding_added_for_item;
|
||
if (!append_value_to_blob (NULL,
|
||
element_type,
|
||
mbuf,
|
||
&padding_added_for_item,
|
||
error))
|
||
goto fail;
|
||
array_payload_begin_offset += padding_added_for_item;
|
||
}
|
||
else if (fixed_size != 0)
|
||
{
|
||
GVariant *use_value;
|
||
|
||
if (g_memory_buffer_is_byteswapped (mbuf))
|
||
use_value = g_variant_byteswap (value);
|
||
else
|
||
use_value = g_variant_ref (value);
|
||
|
||
array_payload_begin_offset += ensure_output_padding (mbuf, fixed_size);
|
||
|
||
array_len = g_variant_get_size (use_value);
|
||
g_memory_buffer_write (mbuf, g_variant_get_data (use_value), array_len);
|
||
g_variant_unref (use_value);
|
||
}
|
||
else
|
||
{
|
||
guint n;
|
||
n = 0;
|
||
g_variant_iter_init (&iter, value);
|
||
while ((item = g_variant_iter_next_value (&iter)) != NULL)
|
||
{
|
||
gsize padding_added_for_item;
|
||
if (!append_value_to_blob (item,
|
||
g_variant_get_type (item),
|
||
mbuf,
|
||
&padding_added_for_item,
|
||
error))
|
||
{
|
||
g_variant_unref (item);
|
||
goto fail;
|
||
}
|
||
g_variant_unref (item);
|
||
if (n == 0)
|
||
{
|
||
array_payload_begin_offset += padding_added_for_item;
|
||
}
|
||
n++;
|
||
}
|
||
}
|
||
|
||
cur_offset = mbuf->valid_len;
|
||
array_len = cur_offset - array_payload_begin_offset;
|
||
mbuf->pos = array_len_offset;
|
||
|
||
g_memory_buffer_put_uint32 (mbuf, array_len);
|
||
mbuf->pos = cur_offset;
|
||
}
|
||
}
|
||
break;
|
||
|
||
default:
|
||
if (g_variant_type_is_dict_entry (type) || g_variant_type_is_tuple (type))
|
||
{
|
||
if (!g_variant_type_first (type))
|
||
{
|
||
g_set_error_literal (error,
|
||
G_IO_ERROR,
|
||
G_IO_ERROR_INVALID_ARGUMENT,
|
||
_("Empty structures (tuples) are not allowed in D-Bus"));
|
||
goto fail;
|
||
}
|
||
|
||
padding_added = ensure_output_padding (mbuf, 8);
|
||
if (value != NULL)
|
||
{
|
||
GVariant *item;
|
||
GVariantIter iter;
|
||
g_variant_iter_init (&iter, value);
|
||
while ((item = g_variant_iter_next_value (&iter)) != NULL)
|
||
{
|
||
if (!append_value_to_blob (item,
|
||
g_variant_get_type (item),
|
||
mbuf,
|
||
NULL,
|
||
error))
|
||
{
|
||
g_variant_unref (item);
|
||
goto fail;
|
||
}
|
||
g_variant_unref (item);
|
||
}
|
||
}
|
||
}
|
||
else if (g_variant_type_is_variant (type))
|
||
{
|
||
if (value != NULL)
|
||
{
|
||
GVariant *child;
|
||
const gchar *signature;
|
||
child = g_variant_get_child_value (value, 0);
|
||
signature = g_variant_get_type_string (child);
|
||
g_memory_buffer_put_byte (mbuf, strlen (signature));
|
||
g_memory_buffer_put_string (mbuf, signature);
|
||
g_memory_buffer_put_byte (mbuf, '\0');
|
||
if (!append_value_to_blob (child,
|
||
g_variant_get_type (child),
|
||
mbuf,
|
||
NULL,
|
||
error))
|
||
{
|
||
g_variant_unref (child);
|
||
goto fail;
|
||
}
|
||
g_variant_unref (child);
|
||
}
|
||
}
|
||
else
|
||
{
|
||
g_set_error (error,
|
||
G_IO_ERROR,
|
||
G_IO_ERROR_INVALID_ARGUMENT,
|
||
_("Error serializing GVariant with type string “%s” to the D-Bus wire format"),
|
||
g_variant_get_type_string (value));
|
||
goto fail;
|
||
}
|
||
break;
|
||
}
|
||
|
||
if (out_padding_added != NULL)
|
||
*out_padding_added = padding_added;
|
||
|
||
return TRUE;
|
||
|
||
fail:
|
||
return FALSE;
|
||
}
|
||
|
||
static gboolean
|
||
append_body_to_blob (GVariant *value,
|
||
GMemoryBuffer *mbuf,
|
||
GError **error)
|
||
{
|
||
GVariant *item;
|
||
GVariantIter iter;
|
||
|
||
if (!g_variant_is_of_type (value, G_VARIANT_TYPE_TUPLE))
|
||
{
|
||
g_set_error (error,
|
||
G_IO_ERROR,
|
||
G_IO_ERROR_INVALID_ARGUMENT,
|
||
"Expected a tuple for the body of the GDBusMessage.");
|
||
goto fail;
|
||
}
|
||
|
||
g_variant_iter_init (&iter, value);
|
||
while ((item = g_variant_iter_next_value (&iter)) != NULL)
|
||
{
|
||
if (!append_value_to_blob (item,
|
||
g_variant_get_type (item),
|
||
mbuf,
|
||
NULL,
|
||
error))
|
||
{
|
||
g_variant_unref (item);
|
||
goto fail;
|
||
}
|
||
g_variant_unref (item);
|
||
}
|
||
return TRUE;
|
||
|
||
fail:
|
||
return FALSE;
|
||
}
|
||
|
||
/* ---------------------------------------------------------------------------------------------------- */
|
||
|
||
/**
|
||
* g_dbus_message_to_blob:
|
||
* @message: A #GDBusMessage.
|
||
* @out_size: Return location for size of generated blob.
|
||
* @capabilities: A #GDBusCapabilityFlags describing what protocol features are supported.
|
||
* @error: Return location for error.
|
||
*
|
||
* Serializes @message to a blob. The byte order returned by
|
||
* g_dbus_message_get_byte_order() will be used.
|
||
*
|
||
* Returns: (array length=out_size) (transfer full): A pointer to a
|
||
* valid binary D-Bus message of @out_size bytes generated by @message
|
||
* or %NULL if @error is set. Free with g_free().
|
||
*
|
||
* Since: 2.26
|
||
*/
|
||
guchar *
|
||
g_dbus_message_to_blob (GDBusMessage *message,
|
||
gsize *out_size,
|
||
GDBusCapabilityFlags capabilities,
|
||
GError **error)
|
||
{
|
||
GMemoryBuffer mbuf;
|
||
guchar *ret;
|
||
gsize size;
|
||
goffset body_len_offset;
|
||
goffset body_start_offset;
|
||
gsize body_size;
|
||
GVariant *header_fields;
|
||
GVariantBuilder builder;
|
||
GHashTableIter hash_iter;
|
||
gpointer key;
|
||
GVariant *header_value;
|
||
GVariant *signature;
|
||
const gchar *signature_str;
|
||
gint num_fds_in_message;
|
||
gint num_fds_according_to_header;
|
||
|
||
/* TODO: check against @capabilities */
|
||
|
||
ret = NULL;
|
||
|
||
g_return_val_if_fail (G_IS_DBUS_MESSAGE (message), NULL);
|
||
g_return_val_if_fail (out_size != NULL, NULL);
|
||
g_return_val_if_fail (error == NULL || *error == NULL, NULL);
|
||
|
||
memset (&mbuf, 0, sizeof (mbuf));
|
||
mbuf.len = MIN_ARRAY_SIZE;
|
||
mbuf.data = g_malloc (mbuf.len);
|
||
|
||
mbuf.byte_order = G_DATA_STREAM_BYTE_ORDER_HOST_ENDIAN;
|
||
switch (message->byte_order)
|
||
{
|
||
case G_DBUS_MESSAGE_BYTE_ORDER_BIG_ENDIAN:
|
||
mbuf.byte_order = G_DATA_STREAM_BYTE_ORDER_BIG_ENDIAN;
|
||
break;
|
||
case G_DBUS_MESSAGE_BYTE_ORDER_LITTLE_ENDIAN:
|
||
mbuf.byte_order = G_DATA_STREAM_BYTE_ORDER_LITTLE_ENDIAN;
|
||
break;
|
||
}
|
||
|
||
/* Core header */
|
||
g_memory_buffer_put_byte (&mbuf, (guchar) message->byte_order);
|
||
g_memory_buffer_put_byte (&mbuf, message->type);
|
||
g_memory_buffer_put_byte (&mbuf, message->flags);
|
||
g_memory_buffer_put_byte (&mbuf, 1); /* major protocol version */
|
||
body_len_offset = mbuf.valid_len;
|
||
/* body length - will be filled in later */
|
||
g_memory_buffer_put_uint32 (&mbuf, 0xF00DFACE);
|
||
g_memory_buffer_put_uint32 (&mbuf, message->serial);
|
||
|
||
num_fds_in_message = 0;
|
||
#ifdef G_OS_UNIX
|
||
if (message->fd_list != NULL)
|
||
num_fds_in_message = g_unix_fd_list_get_length (message->fd_list);
|
||
#endif
|
||
num_fds_according_to_header = g_dbus_message_get_num_unix_fds (message);
|
||
if (num_fds_in_message != num_fds_according_to_header)
|
||
{
|
||
g_set_error (error,
|
||
G_IO_ERROR,
|
||
G_IO_ERROR_INVALID_ARGUMENT,
|
||
_("Number of file descriptors in message (%d) differs from header field (%d)"),
|
||
num_fds_in_message,
|
||
num_fds_according_to_header);
|
||
goto out;
|
||
}
|
||
|
||
if (!validate_headers (message, error))
|
||
{
|
||
g_prefix_error (error, _("Cannot serialize message: "));
|
||
goto out;
|
||
}
|
||
|
||
g_variant_builder_init (&builder, G_VARIANT_TYPE ("a{yv}"));
|
||
g_hash_table_iter_init (&hash_iter, message->headers);
|
||
while (g_hash_table_iter_next (&hash_iter, &key, (gpointer) &header_value))
|
||
{
|
||
g_variant_builder_add (&builder,
|
||
"{yv}",
|
||
(guchar) GPOINTER_TO_UINT (key),
|
||
header_value);
|
||
}
|
||
header_fields = g_variant_builder_end (&builder);
|
||
|
||
if (!append_value_to_blob (header_fields,
|
||
g_variant_get_type (header_fields),
|
||
&mbuf,
|
||
NULL,
|
||
error))
|
||
{
|
||
g_variant_unref (header_fields);
|
||
goto out;
|
||
}
|
||
g_variant_unref (header_fields);
|
||
|
||
/* header size must be a multiple of 8 */
|
||
ensure_output_padding (&mbuf, 8);
|
||
|
||
body_start_offset = mbuf.valid_len;
|
||
|
||
signature = g_dbus_message_get_header (message, G_DBUS_MESSAGE_HEADER_FIELD_SIGNATURE);
|
||
|
||
if (signature != NULL && !g_variant_is_of_type (signature, G_VARIANT_TYPE_SIGNATURE))
|
||
{
|
||
g_set_error_literal (error,
|
||
G_IO_ERROR,
|
||
G_IO_ERROR_INVALID_ARGUMENT,
|
||
_("Signature header found but is not of type signature"));
|
||
goto out;
|
||
}
|
||
|
||
signature_str = NULL;
|
||
if (signature != NULL)
|
||
signature_str = g_variant_get_string (signature, NULL);
|
||
if (message->body != NULL)
|
||
{
|
||
gchar *tupled_signature_str;
|
||
if (signature == NULL)
|
||
{
|
||
g_set_error (error,
|
||
G_IO_ERROR,
|
||
G_IO_ERROR_INVALID_ARGUMENT,
|
||
_("Message body has signature “%s” but there is no signature header"),
|
||
g_variant_get_type_string (message->body));
|
||
goto out;
|
||
}
|
||
tupled_signature_str = g_strdup_printf ("(%s)", signature_str);
|
||
if (g_strcmp0 (tupled_signature_str, g_variant_get_type_string (message->body)) != 0)
|
||
{
|
||
g_set_error (error,
|
||
G_IO_ERROR,
|
||
G_IO_ERROR_INVALID_ARGUMENT,
|
||
_("Message body has type signature “%s” but signature in the header field is “%s”"),
|
||
g_variant_get_type_string (message->body), tupled_signature_str);
|
||
g_free (tupled_signature_str);
|
||
goto out;
|
||
}
|
||
g_free (tupled_signature_str);
|
||
if (!append_body_to_blob (message->body, &mbuf, error))
|
||
goto out;
|
||
}
|
||
else
|
||
{
|
||
if (signature != NULL && strlen (signature_str) > 0)
|
||
{
|
||
g_set_error (error,
|
||
G_IO_ERROR,
|
||
G_IO_ERROR_INVALID_ARGUMENT,
|
||
_("Message body is empty but signature in the header field is “(%s)”"),
|
||
signature_str);
|
||
goto out;
|
||
}
|
||
}
|
||
|
||
/* OK, we're done writing the message - set the body length */
|
||
size = mbuf.valid_len;
|
||
body_size = size - body_start_offset;
|
||
|
||
mbuf.pos = body_len_offset;
|
||
|
||
g_memory_buffer_put_uint32 (&mbuf, body_size);
|
||
|
||
*out_size = size;
|
||
ret = (guchar *)mbuf.data;
|
||
|
||
out:
|
||
if (ret == NULL)
|
||
g_free (mbuf.data);
|
||
|
||
return ret;
|
||
}
|
||
|
||
/* ---------------------------------------------------------------------------------------------------- */
|
||
|
||
static guint32
|
||
get_uint32_header (GDBusMessage *message,
|
||
GDBusMessageHeaderField header_field)
|
||
{
|
||
GVariant *value;
|
||
guint32 ret;
|
||
|
||
ret = 0;
|
||
value = g_hash_table_lookup (message->headers, GUINT_TO_POINTER (header_field));
|
||
if (value != NULL && g_variant_is_of_type (value, G_VARIANT_TYPE_UINT32))
|
||
ret = g_variant_get_uint32 (value);
|
||
|
||
return ret;
|
||
}
|
||
|
||
static const gchar *
|
||
get_string_header (GDBusMessage *message,
|
||
GDBusMessageHeaderField header_field)
|
||
{
|
||
GVariant *value;
|
||
const gchar *ret;
|
||
|
||
ret = NULL;
|
||
value = g_hash_table_lookup (message->headers, GUINT_TO_POINTER (header_field));
|
||
if (value != NULL && g_variant_is_of_type (value, G_VARIANT_TYPE_STRING))
|
||
ret = g_variant_get_string (value, NULL);
|
||
|
||
return ret;
|
||
}
|
||
|
||
static const gchar *
|
||
get_object_path_header (GDBusMessage *message,
|
||
GDBusMessageHeaderField header_field)
|
||
{
|
||
GVariant *value;
|
||
const gchar *ret;
|
||
|
||
ret = NULL;
|
||
value = g_hash_table_lookup (message->headers, GUINT_TO_POINTER (header_field));
|
||
if (value != NULL && g_variant_is_of_type (value, G_VARIANT_TYPE_OBJECT_PATH))
|
||
ret = g_variant_get_string (value, NULL);
|
||
|
||
return ret;
|
||
}
|
||
|
||
static const gchar *
|
||
get_signature_header (GDBusMessage *message,
|
||
GDBusMessageHeaderField header_field)
|
||
{
|
||
GVariant *value;
|
||
const gchar *ret;
|
||
|
||
ret = NULL;
|
||
value = g_hash_table_lookup (message->headers, GUINT_TO_POINTER (header_field));
|
||
if (value != NULL && g_variant_is_of_type (value, G_VARIANT_TYPE_SIGNATURE))
|
||
ret = g_variant_get_string (value, NULL);
|
||
|
||
return ret;
|
||
}
|
||
|
||
/* ---------------------------------------------------------------------------------------------------- */
|
||
|
||
static void
|
||
set_uint32_header (GDBusMessage *message,
|
||
GDBusMessageHeaderField header_field,
|
||
guint32 value)
|
||
{
|
||
g_dbus_message_set_header (message,
|
||
header_field,
|
||
g_variant_new_uint32 (value));
|
||
}
|
||
|
||
static void
|
||
set_string_header (GDBusMessage *message,
|
||
GDBusMessageHeaderField header_field,
|
||
const gchar *value)
|
||
{
|
||
g_dbus_message_set_header (message,
|
||
header_field,
|
||
value == NULL ? NULL : g_variant_new_string (value));
|
||
}
|
||
|
||
static void
|
||
set_object_path_header (GDBusMessage *message,
|
||
GDBusMessageHeaderField header_field,
|
||
const gchar *value)
|
||
{
|
||
g_dbus_message_set_header (message,
|
||
header_field,
|
||
value == NULL ? NULL : g_variant_new_object_path (value));
|
||
}
|
||
|
||
static void
|
||
set_signature_header (GDBusMessage *message,
|
||
GDBusMessageHeaderField header_field,
|
||
const gchar *value)
|
||
{
|
||
g_dbus_message_set_header (message,
|
||
header_field,
|
||
value == NULL ? NULL : g_variant_new_signature (value));
|
||
}
|
||
|
||
/* ---------------------------------------------------------------------------------------------------- */
|
||
|
||
/**
|
||
* g_dbus_message_get_reply_serial:
|
||
* @message: A #GDBusMessage.
|
||
*
|
||
* Convenience getter for the %G_DBUS_MESSAGE_HEADER_FIELD_REPLY_SERIAL header field.
|
||
*
|
||
* Returns: The value.
|
||
*
|
||
* Since: 2.26
|
||
*/
|
||
guint32
|
||
g_dbus_message_get_reply_serial (GDBusMessage *message)
|
||
{
|
||
g_return_val_if_fail (G_IS_DBUS_MESSAGE (message), 0);
|
||
return get_uint32_header (message, G_DBUS_MESSAGE_HEADER_FIELD_REPLY_SERIAL);
|
||
}
|
||
|
||
/**
|
||
* g_dbus_message_set_reply_serial:
|
||
* @message: A #GDBusMessage.
|
||
* @value: The value to set.
|
||
*
|
||
* Convenience setter for the %G_DBUS_MESSAGE_HEADER_FIELD_REPLY_SERIAL header field.
|
||
*
|
||
* Since: 2.26
|
||
*/
|
||
void
|
||
g_dbus_message_set_reply_serial (GDBusMessage *message,
|
||
guint32 value)
|
||
{
|
||
g_return_if_fail (G_IS_DBUS_MESSAGE (message));
|
||
set_uint32_header (message, G_DBUS_MESSAGE_HEADER_FIELD_REPLY_SERIAL, value);
|
||
}
|
||
|
||
/* ---------------------------------------------------------------------------------------------------- */
|
||
|
||
/**
|
||
* g_dbus_message_get_interface:
|
||
* @message: A #GDBusMessage.
|
||
*
|
||
* Convenience getter for the %G_DBUS_MESSAGE_HEADER_FIELD_INTERFACE header field.
|
||
*
|
||
* Returns: (nullable): The value.
|
||
*
|
||
* Since: 2.26
|
||
*/
|
||
const gchar *
|
||
g_dbus_message_get_interface (GDBusMessage *message)
|
||
{
|
||
g_return_val_if_fail (G_IS_DBUS_MESSAGE (message), NULL);
|
||
return get_string_header (message, G_DBUS_MESSAGE_HEADER_FIELD_INTERFACE);
|
||
}
|
||
|
||
/**
|
||
* g_dbus_message_set_interface:
|
||
* @message: A #GDBusMessage.
|
||
* @value: (nullable): The value to set.
|
||
*
|
||
* Convenience setter for the %G_DBUS_MESSAGE_HEADER_FIELD_INTERFACE header field.
|
||
*
|
||
* Since: 2.26
|
||
*/
|
||
void
|
||
g_dbus_message_set_interface (GDBusMessage *message,
|
||
const gchar *value)
|
||
{
|
||
g_return_if_fail (G_IS_DBUS_MESSAGE (message));
|
||
g_return_if_fail (value == NULL || g_dbus_is_interface_name (value));
|
||
set_string_header (message, G_DBUS_MESSAGE_HEADER_FIELD_INTERFACE, value);
|
||
}
|
||
|
||
/* ---------------------------------------------------------------------------------------------------- */
|
||
|
||
/**
|
||
* g_dbus_message_get_member:
|
||
* @message: A #GDBusMessage.
|
||
*
|
||
* Convenience getter for the %G_DBUS_MESSAGE_HEADER_FIELD_MEMBER header field.
|
||
*
|
||
* Returns: (nullable): The value.
|
||
*
|
||
* Since: 2.26
|
||
*/
|
||
const gchar *
|
||
g_dbus_message_get_member (GDBusMessage *message)
|
||
{
|
||
g_return_val_if_fail (G_IS_DBUS_MESSAGE (message), NULL);
|
||
return get_string_header (message, G_DBUS_MESSAGE_HEADER_FIELD_MEMBER);
|
||
}
|
||
|
||
/**
|
||
* g_dbus_message_set_member:
|
||
* @message: A #GDBusMessage.
|
||
* @value: (nullable): The value to set.
|
||
*
|
||
* Convenience setter for the %G_DBUS_MESSAGE_HEADER_FIELD_MEMBER header field.
|
||
*
|
||
* Since: 2.26
|
||
*/
|
||
void
|
||
g_dbus_message_set_member (GDBusMessage *message,
|
||
const gchar *value)
|
||
{
|
||
g_return_if_fail (G_IS_DBUS_MESSAGE (message));
|
||
g_return_if_fail (value == NULL || g_dbus_is_member_name (value));
|
||
set_string_header (message, G_DBUS_MESSAGE_HEADER_FIELD_MEMBER, value);
|
||
}
|
||
|
||
/* ---------------------------------------------------------------------------------------------------- */
|
||
|
||
/**
|
||
* g_dbus_message_get_path:
|
||
* @message: A #GDBusMessage.
|
||
*
|
||
* Convenience getter for the %G_DBUS_MESSAGE_HEADER_FIELD_PATH header field.
|
||
*
|
||
* Returns: (nullable): The value.
|
||
*
|
||
* Since: 2.26
|
||
*/
|
||
const gchar *
|
||
g_dbus_message_get_path (GDBusMessage *message)
|
||
{
|
||
g_return_val_if_fail (G_IS_DBUS_MESSAGE (message), NULL);
|
||
return get_object_path_header (message, G_DBUS_MESSAGE_HEADER_FIELD_PATH);
|
||
}
|
||
|
||
/**
|
||
* g_dbus_message_set_path:
|
||
* @message: A #GDBusMessage.
|
||
* @value: (nullable): The value to set.
|
||
*
|
||
* Convenience setter for the %G_DBUS_MESSAGE_HEADER_FIELD_PATH header field.
|
||
*
|
||
* Since: 2.26
|
||
*/
|
||
void
|
||
g_dbus_message_set_path (GDBusMessage *message,
|
||
const gchar *value)
|
||
{
|
||
g_return_if_fail (G_IS_DBUS_MESSAGE (message));
|
||
g_return_if_fail (value == NULL || g_variant_is_object_path (value));
|
||
set_object_path_header (message, G_DBUS_MESSAGE_HEADER_FIELD_PATH, value);
|
||
}
|
||
|
||
/* ---------------------------------------------------------------------------------------------------- */
|
||
|
||
/**
|
||
* g_dbus_message_get_sender:
|
||
* @message: A #GDBusMessage.
|
||
*
|
||
* Convenience getter for the %G_DBUS_MESSAGE_HEADER_FIELD_SENDER header field.
|
||
*
|
||
* Returns: (nullable): The value.
|
||
*
|
||
* Since: 2.26
|
||
*/
|
||
const gchar *
|
||
g_dbus_message_get_sender (GDBusMessage *message)
|
||
{
|
||
g_return_val_if_fail (G_IS_DBUS_MESSAGE (message), NULL);
|
||
return get_string_header (message, G_DBUS_MESSAGE_HEADER_FIELD_SENDER);
|
||
}
|
||
|
||
/**
|
||
* g_dbus_message_set_sender:
|
||
* @message: A #GDBusMessage.
|
||
* @value: (nullable): The value to set.
|
||
*
|
||
* Convenience setter for the %G_DBUS_MESSAGE_HEADER_FIELD_SENDER header field.
|
||
*
|
||
* Since: 2.26
|
||
*/
|
||
void
|
||
g_dbus_message_set_sender (GDBusMessage *message,
|
||
const gchar *value)
|
||
{
|
||
g_return_if_fail (G_IS_DBUS_MESSAGE (message));
|
||
g_return_if_fail (value == NULL || g_dbus_is_name (value));
|
||
set_string_header (message, G_DBUS_MESSAGE_HEADER_FIELD_SENDER, value);
|
||
}
|
||
|
||
/* ---------------------------------------------------------------------------------------------------- */
|
||
|
||
/**
|
||
* g_dbus_message_get_destination:
|
||
* @message: A #GDBusMessage.
|
||
*
|
||
* Convenience getter for the %G_DBUS_MESSAGE_HEADER_FIELD_DESTINATION header field.
|
||
*
|
||
* Returns: (nullable): The value.
|
||
*
|
||
* Since: 2.26
|
||
*/
|
||
const gchar *
|
||
g_dbus_message_get_destination (GDBusMessage *message)
|
||
{
|
||
g_return_val_if_fail (G_IS_DBUS_MESSAGE (message), NULL);
|
||
return get_string_header (message, G_DBUS_MESSAGE_HEADER_FIELD_DESTINATION);
|
||
}
|
||
|
||
/**
|
||
* g_dbus_message_set_destination:
|
||
* @message: A #GDBusMessage.
|
||
* @value: (nullable): The value to set.
|
||
*
|
||
* Convenience setter for the %G_DBUS_MESSAGE_HEADER_FIELD_DESTINATION header field.
|
||
*
|
||
* Since: 2.26
|
||
*/
|
||
void
|
||
g_dbus_message_set_destination (GDBusMessage *message,
|
||
const gchar *value)
|
||
{
|
||
g_return_if_fail (G_IS_DBUS_MESSAGE (message));
|
||
g_return_if_fail (value == NULL || g_dbus_is_name (value));
|
||
set_string_header (message, G_DBUS_MESSAGE_HEADER_FIELD_DESTINATION, value);
|
||
}
|
||
|
||
/* ---------------------------------------------------------------------------------------------------- */
|
||
|
||
/**
|
||
* g_dbus_message_get_error_name:
|
||
* @message: A #GDBusMessage.
|
||
*
|
||
* Convenience getter for the %G_DBUS_MESSAGE_HEADER_FIELD_ERROR_NAME header field.
|
||
*
|
||
* Returns: (nullable): The value.
|
||
*
|
||
* Since: 2.26
|
||
*/
|
||
const gchar *
|
||
g_dbus_message_get_error_name (GDBusMessage *message)
|
||
{
|
||
g_return_val_if_fail (G_IS_DBUS_MESSAGE (message), NULL);
|
||
return get_string_header (message, G_DBUS_MESSAGE_HEADER_FIELD_ERROR_NAME);
|
||
}
|
||
|
||
/**
|
||
* g_dbus_message_set_error_name:
|
||
* @message: (nullable): A #GDBusMessage.
|
||
* @value: The value to set.
|
||
*
|
||
* Convenience setter for the %G_DBUS_MESSAGE_HEADER_FIELD_ERROR_NAME header field.
|
||
*
|
||
* Since: 2.26
|
||
*/
|
||
void
|
||
g_dbus_message_set_error_name (GDBusMessage *message,
|
||
const gchar *value)
|
||
{
|
||
g_return_if_fail (G_IS_DBUS_MESSAGE (message));
|
||
g_return_if_fail (value == NULL || g_dbus_is_error_name (value));
|
||
set_string_header (message, G_DBUS_MESSAGE_HEADER_FIELD_ERROR_NAME, value);
|
||
}
|
||
|
||
/* ---------------------------------------------------------------------------------------------------- */
|
||
|
||
/**
|
||
* g_dbus_message_get_signature:
|
||
* @message: A #GDBusMessage.
|
||
*
|
||
* Convenience getter for the %G_DBUS_MESSAGE_HEADER_FIELD_SIGNATURE header field.
|
||
*
|
||
* This will always be non-%NULL, but may be an empty string.
|
||
*
|
||
* Returns: (not nullable): The value.
|
||
*
|
||
* Since: 2.26
|
||
*/
|
||
const gchar *
|
||
g_dbus_message_get_signature (GDBusMessage *message)
|
||
{
|
||
const gchar *ret;
|
||
g_return_val_if_fail (G_IS_DBUS_MESSAGE (message), NULL);
|
||
ret = get_signature_header (message, G_DBUS_MESSAGE_HEADER_FIELD_SIGNATURE);
|
||
if (ret == NULL)
|
||
ret = "";
|
||
return ret;
|
||
}
|
||
|
||
/**
|
||
* g_dbus_message_set_signature:
|
||
* @message: A #GDBusMessage.
|
||
* @value: (nullable): The value to set.
|
||
*
|
||
* Convenience setter for the %G_DBUS_MESSAGE_HEADER_FIELD_SIGNATURE header field.
|
||
*
|
||
* Since: 2.26
|
||
*/
|
||
void
|
||
g_dbus_message_set_signature (GDBusMessage *message,
|
||
const gchar *value)
|
||
{
|
||
g_return_if_fail (G_IS_DBUS_MESSAGE (message));
|
||
g_return_if_fail (value == NULL || g_variant_is_signature (value));
|
||
set_signature_header (message, G_DBUS_MESSAGE_HEADER_FIELD_SIGNATURE, value);
|
||
}
|
||
|
||
/* ---------------------------------------------------------------------------------------------------- */
|
||
|
||
/**
|
||
* g_dbus_message_get_arg0:
|
||
* @message: A #GDBusMessage.
|
||
*
|
||
* Convenience to get the first item in the body of @message.
|
||
*
|
||
* See [method@Gio.DBusMessage.get_arg0_path] for returning object-path-typed
|
||
* arg0 values.
|
||
*
|
||
* Returns: (nullable): The string item or %NULL if the first item in the body of
|
||
* @message is not a string.
|
||
*
|
||
* Since: 2.26
|
||
*/
|
||
const gchar *
|
||
g_dbus_message_get_arg0 (GDBusMessage *message)
|
||
{
|
||
g_return_val_if_fail (G_IS_DBUS_MESSAGE (message), NULL);
|
||
|
||
if (message->arg0_cache != NULL &&
|
||
g_variant_is_of_type (message->arg0_cache, G_VARIANT_TYPE_STRING))
|
||
return g_variant_get_string (message->arg0_cache, NULL);
|
||
|
||
return NULL;
|
||
}
|
||
|
||
/**
|
||
* g_dbus_message_get_arg0_path:
|
||
* @message: A `GDBusMessage`.
|
||
*
|
||
* Convenience to get the first item in the body of @message.
|
||
*
|
||
* See [method@Gio.DBusMessage.get_arg0] for returning string-typed arg0 values.
|
||
*
|
||
* Returns: (nullable): The object path item or `NULL` if the first item in the
|
||
* body of @message is not an object path.
|
||
*
|
||
* Since: 2.80
|
||
*/
|
||
const gchar *
|
||
g_dbus_message_get_arg0_path (GDBusMessage *message)
|
||
{
|
||
g_return_val_if_fail (G_IS_DBUS_MESSAGE (message), NULL);
|
||
|
||
if (message->arg0_cache != NULL &&
|
||
g_variant_is_of_type (message->arg0_cache, G_VARIANT_TYPE_OBJECT_PATH))
|
||
return g_variant_get_string (message->arg0_cache, NULL);
|
||
|
||
return NULL;
|
||
}
|
||
|
||
/* ---------------------------------------------------------------------------------------------------- */
|
||
|
||
/**
|
||
* g_dbus_message_get_num_unix_fds:
|
||
* @message: A #GDBusMessage.
|
||
*
|
||
* Convenience getter for the %G_DBUS_MESSAGE_HEADER_FIELD_NUM_UNIX_FDS header field.
|
||
*
|
||
* Returns: The value.
|
||
*
|
||
* Since: 2.26
|
||
*/
|
||
guint32
|
||
g_dbus_message_get_num_unix_fds (GDBusMessage *message)
|
||
{
|
||
g_return_val_if_fail (G_IS_DBUS_MESSAGE (message), 0);
|
||
return get_uint32_header (message, G_DBUS_MESSAGE_HEADER_FIELD_NUM_UNIX_FDS);
|
||
}
|
||
|
||
/**
|
||
* g_dbus_message_set_num_unix_fds:
|
||
* @message: A #GDBusMessage.
|
||
* @value: The value to set.
|
||
*
|
||
* Convenience setter for the %G_DBUS_MESSAGE_HEADER_FIELD_NUM_UNIX_FDS header field.
|
||
*
|
||
* Since: 2.26
|
||
*/
|
||
void
|
||
g_dbus_message_set_num_unix_fds (GDBusMessage *message,
|
||
guint32 value)
|
||
{
|
||
g_return_if_fail (G_IS_DBUS_MESSAGE (message));
|
||
set_uint32_header (message, G_DBUS_MESSAGE_HEADER_FIELD_NUM_UNIX_FDS, value);
|
||
}
|
||
|
||
/* ---------------------------------------------------------------------------------------------------- */
|
||
|
||
/**
|
||
* g_dbus_message_to_gerror:
|
||
* @message: A #GDBusMessage.
|
||
* @error: The #GError to set.
|
||
*
|
||
* If @message is not of type %G_DBUS_MESSAGE_TYPE_ERROR does
|
||
* nothing and returns %FALSE.
|
||
*
|
||
* Otherwise this method encodes the error in @message as a #GError
|
||
* using g_dbus_error_set_dbus_error() using the information in the
|
||
* %G_DBUS_MESSAGE_HEADER_FIELD_ERROR_NAME header field of @message as
|
||
* well as the first string item in @message's body.
|
||
*
|
||
* Returns: %TRUE if @error was set, %FALSE otherwise.
|
||
*
|
||
* Since: 2.26
|
||
*/
|
||
gboolean
|
||
g_dbus_message_to_gerror (GDBusMessage *message,
|
||
GError **error)
|
||
{
|
||
gboolean ret;
|
||
const gchar *error_name;
|
||
|
||
g_return_val_if_fail (G_IS_DBUS_MESSAGE (message), FALSE);
|
||
|
||
ret = FALSE;
|
||
if (message->type != G_DBUS_MESSAGE_TYPE_ERROR)
|
||
goto out;
|
||
|
||
error_name = g_dbus_message_get_error_name (message);
|
||
if (error_name != NULL)
|
||
{
|
||
GVariant *body;
|
||
|
||
body = g_dbus_message_get_body (message);
|
||
|
||
if (body != NULL && g_variant_is_of_type (body, G_VARIANT_TYPE ("(s)")))
|
||
{
|
||
const gchar *error_message;
|
||
g_variant_get (body, "(&s)", &error_message);
|
||
g_dbus_error_set_dbus_error (error,
|
||
error_name,
|
||
error_message,
|
||
NULL);
|
||
}
|
||
else
|
||
{
|
||
/* these two situations are valid, yet pretty rare */
|
||
if (body != NULL)
|
||
{
|
||
g_dbus_error_set_dbus_error (error,
|
||
error_name,
|
||
"",
|
||
_("Error return with body of type “%s”"),
|
||
g_variant_get_type_string (body));
|
||
}
|
||
else
|
||
{
|
||
g_dbus_error_set_dbus_error (error,
|
||
error_name,
|
||
"",
|
||
_("Error return with empty body"));
|
||
}
|
||
}
|
||
}
|
||
else
|
||
{
|
||
/* TODO: this shouldn't happen - should check this at message serialization
|
||
* time and disconnect the peer.
|
||
*/
|
||
g_set_error (error,
|
||
G_IO_ERROR,
|
||
G_IO_ERROR_FAILED,
|
||
"Error return without error-name header!");
|
||
}
|
||
|
||
ret = TRUE;
|
||
|
||
out:
|
||
return ret;
|
||
}
|
||
|
||
/* ---------------------------------------------------------------------------------------------------- */
|
||
|
||
static gchar *
|
||
flags_to_string (GType flags_type, guint value)
|
||
{
|
||
GString *s;
|
||
GFlagsClass *klass;
|
||
guint n;
|
||
|
||
klass = g_type_class_ref (flags_type);
|
||
s = g_string_new (NULL);
|
||
for (n = 0; n < 32; n++)
|
||
{
|
||
if ((value & (1<<n)) != 0)
|
||
{
|
||
GFlagsValue *flags_value;
|
||
flags_value = g_flags_get_first_value (klass, (1<<n));
|
||
if (s->len > 0)
|
||
g_string_append_c (s, ',');
|
||
if (flags_value != NULL)
|
||
g_string_append (s, flags_value->value_nick);
|
||
else
|
||
g_string_append_printf (s, "unknown (bit %d)", n);
|
||
}
|
||
}
|
||
if (s->len == 0)
|
||
g_string_append (s, "none");
|
||
g_type_class_unref (klass);
|
||
return g_string_free (s, FALSE);
|
||
}
|
||
|
||
static gint
|
||
_sort_keys_func (gconstpointer a,
|
||
gconstpointer b)
|
||
{
|
||
gint ia;
|
||
gint ib;
|
||
|
||
ia = GPOINTER_TO_INT (a);
|
||
ib = GPOINTER_TO_INT (b);
|
||
|
||
return ia - ib;
|
||
}
|
||
|
||
/**
|
||
* g_dbus_message_print:
|
||
* @message: A #GDBusMessage.
|
||
* @indent: Indentation level.
|
||
*
|
||
* Produces a human-readable multi-line description of @message.
|
||
*
|
||
* The contents of the description has no ABI guarantees, the contents
|
||
* and formatting is subject to change at any time. Typical output
|
||
* looks something like this:
|
||
* ```
|
||
* Type: method-call
|
||
* Flags: none
|
||
* Version: 0
|
||
* Serial: 4
|
||
* Headers:
|
||
* path -> objectpath '/org/gtk/GDBus/TestObject'
|
||
* interface -> 'org.gtk.GDBus.TestInterface'
|
||
* member -> 'GimmeStdout'
|
||
* destination -> ':1.146'
|
||
* Body: ()
|
||
* UNIX File Descriptors:
|
||
* (none)
|
||
* ```
|
||
* or
|
||
* ```
|
||
* Type: method-return
|
||
* Flags: no-reply-expected
|
||
* Version: 0
|
||
* Serial: 477
|
||
* Headers:
|
||
* reply-serial -> uint32 4
|
||
* destination -> ':1.159'
|
||
* sender -> ':1.146'
|
||
* num-unix-fds -> uint32 1
|
||
* Body: ()
|
||
* UNIX File Descriptors:
|
||
* fd 12: dev=0:10,mode=020620,ino=5,uid=500,gid=5,rdev=136:2,size=0,atime=1273085037,mtime=1273085851,ctime=1272982635
|
||
* ```
|
||
*
|
||
* Returns: (not nullable): A string that should be freed with [func@GLib.free].
|
||
*
|
||
* Since: 2.26
|
||
*/
|
||
gchar *
|
||
g_dbus_message_print (GDBusMessage *message,
|
||
guint indent)
|
||
{
|
||
GString *str;
|
||
gchar *s;
|
||
GList *keys;
|
||
GList *l;
|
||
|
||
g_return_val_if_fail (G_IS_DBUS_MESSAGE (message), NULL);
|
||
|
||
str = g_string_new (NULL);
|
||
|
||
s = _g_dbus_enum_to_string (G_TYPE_DBUS_MESSAGE_TYPE, message->type);
|
||
g_string_append_printf (str, "%*sType: %s\n", indent, "", s);
|
||
g_free (s);
|
||
s = flags_to_string (G_TYPE_DBUS_MESSAGE_FLAGS, message->flags);
|
||
g_string_append_printf (str, "%*sFlags: %s\n", indent, "", s);
|
||
g_free (s);
|
||
g_string_append_printf (str, "%*sVersion: %d\n", indent, "", message->major_protocol_version);
|
||
g_string_append_printf (str, "%*sSerial: %d\n", indent, "", message->serial);
|
||
|
||
g_string_append_printf (str, "%*sHeaders:\n", indent, "");
|
||
keys = g_hash_table_get_keys (message->headers);
|
||
keys = g_list_sort (keys, _sort_keys_func);
|
||
if (keys != NULL)
|
||
{
|
||
for (l = keys; l != NULL; l = l->next)
|
||
{
|
||
gint key = GPOINTER_TO_INT (l->data);
|
||
GVariant *value;
|
||
gchar *value_str;
|
||
|
||
value = g_hash_table_lookup (message->headers, l->data);
|
||
g_assert (value != NULL);
|
||
|
||
s = _g_dbus_enum_to_string (G_TYPE_DBUS_MESSAGE_HEADER_FIELD, key);
|
||
value_str = g_variant_print (value, TRUE);
|
||
g_string_append_printf (str, "%*s %s -> %s\n", indent, "", s, value_str);
|
||
g_free (s);
|
||
g_free (value_str);
|
||
}
|
||
}
|
||
else
|
||
{
|
||
g_string_append_printf (str, "%*s (none)\n", indent, "");
|
||
}
|
||
g_list_free (keys);
|
||
g_string_append_printf (str, "%*sBody: ", indent, "");
|
||
if (message->body != NULL)
|
||
{
|
||
g_variant_print_string (message->body,
|
||
str,
|
||
TRUE);
|
||
}
|
||
else
|
||
{
|
||
g_string_append (str, "()");
|
||
}
|
||
g_string_append (str, "\n");
|
||
#ifdef G_OS_UNIX
|
||
g_string_append_printf (str, "%*sUNIX File Descriptors:\n", indent, "");
|
||
if (message->fd_list != NULL)
|
||
{
|
||
gint num_fds;
|
||
const gint *fds;
|
||
gint n;
|
||
|
||
fds = g_unix_fd_list_peek_fds (message->fd_list, &num_fds);
|
||
if (num_fds > 0)
|
||
{
|
||
for (n = 0; n < num_fds; n++)
|
||
{
|
||
GString *fs;
|
||
struct stat statbuf;
|
||
fs = g_string_new (NULL);
|
||
if (fstat (fds[n], &statbuf) == 0)
|
||
{
|
||
#ifndef MAJOR_MINOR_NOT_FOUND
|
||
g_string_append_printf (fs, "%s" "dev=%d:%d", fs->len > 0 ? "," : "",
|
||
(gint) major (statbuf.st_dev), (gint) minor (statbuf.st_dev));
|
||
#endif
|
||
g_string_append_printf (fs, "%s" "mode=0%o", fs->len > 0 ? "," : "",
|
||
(guint) statbuf.st_mode);
|
||
g_string_append_printf (fs, "%s" "ino=%" G_GUINT64_FORMAT, fs->len > 0 ? "," : "",
|
||
(guint64) statbuf.st_ino);
|
||
g_string_append_printf (fs, "%s" "uid=%u", fs->len > 0 ? "," : "",
|
||
(guint) statbuf.st_uid);
|
||
g_string_append_printf (fs, "%s" "gid=%u", fs->len > 0 ? "," : "",
|
||
(guint) statbuf.st_gid);
|
||
#ifndef MAJOR_MINOR_NOT_FOUND
|
||
g_string_append_printf (fs, "%s" "rdev=%d:%d", fs->len > 0 ? "," : "",
|
||
(gint) major (statbuf.st_rdev), (gint) minor (statbuf.st_rdev));
|
||
#endif
|
||
g_string_append_printf (fs, "%s" "size=%" G_GUINT64_FORMAT, fs->len > 0 ? "," : "",
|
||
(guint64) statbuf.st_size);
|
||
g_string_append_printf (fs, "%s" "atime=%" G_GUINT64_FORMAT, fs->len > 0 ? "," : "",
|
||
(guint64) statbuf.st_atime);
|
||
g_string_append_printf (fs, "%s" "mtime=%" G_GUINT64_FORMAT, fs->len > 0 ? "," : "",
|
||
(guint64) statbuf.st_mtime);
|
||
g_string_append_printf (fs, "%s" "ctime=%" G_GUINT64_FORMAT, fs->len > 0 ? "," : "",
|
||
(guint64) statbuf.st_ctime);
|
||
}
|
||
else
|
||
{
|
||
int errsv = errno;
|
||
g_string_append_printf (fs, "(fstat failed: %s)", g_strerror (errsv));
|
||
}
|
||
g_string_append_printf (str, "%*s fd %d: %s\n", indent, "", fds[n], fs->str);
|
||
g_string_free (fs, TRUE);
|
||
}
|
||
}
|
||
else
|
||
{
|
||
g_string_append_printf (str, "%*s (empty)\n", indent, "");
|
||
}
|
||
}
|
||
else
|
||
{
|
||
g_string_append_printf (str, "%*s (none)\n", indent, "");
|
||
}
|
||
#endif
|
||
|
||
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.
|
||
*
|
||
* This operation can fail if e.g. @message contains file descriptors
|
||
* and the per-process or system-wide open files limit is reached.
|
||
*
|
||
* Returns: (transfer full): 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, NULL);
|
||
|
||
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;
|
||
ret->serial = message->serial;
|
||
|
||
#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;
|
||
ret->arg0_cache = message->arg0_cache != NULL ? g_variant_ref (message->arg0_cache) : 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));
|
||
|
||
#ifdef G_OS_UNIX
|
||
out:
|
||
#endif
|
||
return ret;
|
||
}
|