*
* Copyright (C) 2002, 2003, 2004, 2005 Red Hat Inc.
* Copyright (C) 2002, 2003 CodeFactory AB
+ * Copyright (C) 2015 Samsung Electronics
*
* Licensed under the Academic Free License version 2.1
*
#include "dbus-list.h"
#include "dbus-threads-internal.h"
#ifdef HAVE_UNIX_FD_PASSING
+#include "dbus-sysdeps.h"
#include "dbus-sysdeps-unix.h"
#endif
+#include "dbus-marshal-gvariant.h"
+#include "dbus-protocol-gvariant.h"
#include <string.h>
(type == DBUS_TYPE_STRING || type == DBUS_TYPE_SIGNATURE || \
type == DBUS_TYPE_OBJECT_PATH)
+static unsigned char _dbus_default_protocol_version = DBUS_MAJOR_PROTOCOL_VERSION; /* DBUS_PROTOCOL_VERSION_GVARIANT; */
+static dbus_bool_t _dbus_first_bus_open = FALSE;
+
+static void protocol_strategy_last_type (int type);
+static void protocol_strategy_static (int type);
+
+typedef void (*DBusProtocolStrategyFunction)(int type);
+static DBusProtocolStrategyFunction _dbus_protocol_strategy_bus_function = protocol_strategy_last_type;
+static DBusProtocolStrategyFunction _dbus_protocol_strategy_message_function = protocol_strategy_static;
+
static void dbus_message_finalize (DBusMessage *message);
/**
/** typedef for internals of message iterator */
typedef struct DBusMessageRealIter DBusMessageRealIter;
+#define BUILD_BUG_ON(condition) ((void)sizeof(char[1 - 2*!!(condition)]))
+
/**
* @brief Internals of DBusMessageIter
*
} u; /**< the type writer or reader that does all the work */
};
+static dbus_bool_t
+_dbus_header_is_gvariant (const DBusHeader *header)
+{
+ return (header->protocol_version == DBUS_PROTOCOL_VERSION_GVARIANT);
+}
+
+static dbus_bool_t
+_dbus_message_is_gvariant (const DBusMessage *message)
+{
+ return _dbus_header_is_gvariant (&message->header);
+}
+
+static void
+_dbus_message_toggle_gvariant (DBusMessage *message, dbus_bool_t gvariant)
+{
+ message->header.protocol_version = gvariant ? DBUS_PROTOCOL_VERSION_GVARIANT : DBUS_MAJOR_PROTOCOL_VERSION;
+}
+
static void
-get_const_signature (DBusHeader *header,
+get_const_signature (DBusMessage *message,
const DBusString **type_str_p,
int *type_pos_p)
{
- if (_dbus_header_get_field_raw (header,
- DBUS_HEADER_FIELD_SIGNATURE,
- type_str_p,
- type_pos_p))
+ dbus_bool_t got_signature = FALSE;
+ if (_dbus_message_is_gvariant (message) && message->locked)
{
- *type_pos_p += 1; /* skip the signature length which is 1 byte */
+ /* only locked GVariant messages have signatures in the body */
+ /*
+ * in case of received GVariant message, there may be no signature field in a header,
+ * but in the body. However, it is not nul-terminated.
+ * So, we need to allocate space and put it into message.
+ * It could also happen before, so check message->signature for already existing.
+ * FIXME: That may kinda break oom-safety.
+ * For now - if oom, then return empty signature.
+ */
+ if (message->signature == NULL)
+ {
+ int type_str_len;
+ got_signature = _dbus_message_gvariant_get_signature (message,
+ type_str_p,
+ type_pos_p,
+ &type_str_len);
+ if (got_signature && type_str_len > 1)
+ {
+ message->signature = dbus_new (DBusString, 1);
+ got_signature = got_signature &&
+ _dbus_string_init_preallocated (message->signature, type_str_len - 1);
+
+ /* we need to copy the signature, but to ensure backward compatibility
+ * it must be stripped off outer parentheses - they are always there */
+ got_signature = got_signature &&
+ _dbus_string_copy_len (*type_str_p, *type_pos_p + 1, type_str_len - 2,
+ message->signature, 0);
+ got_signature = got_signature &&
+ _dbus_string_append_byte (message->signature, 0);
+ }
+ }
+ else
+ got_signature = TRUE;
+
+ if (got_signature)
+ {
+ *type_str_p = message->signature;
+ *type_pos_p = 0;
+ }
}
- else
+ else if (_dbus_header_get_field_raw (&message->header,
+ DBUS_HEADER_FIELD_SIGNATURE,
+ type_str_p,
+ type_pos_p))
+ {
+ if (!_dbus_message_is_gvariant (message))
+ *type_pos_p += 1; /* skip the signature length which is 1 byte */
+ got_signature = TRUE;
+ }
+ if (!got_signature)
{
*type_str_p = &_dbus_empty_signature_str;
*type_pos_p = 0;
_dbus_verbose ("Swapping message into compiler byte order\n");
- get_const_signature (&message->header, &type_str, &type_pos);
+ get_const_signature (message, &type_str, &type_pos);
_dbus_marshal_byteswap (type_str, type_pos,
byte_order,
*body = &message->body;
}
+void _dbus_message_set_timeout_ms(DBusMessage *message, int timeout_ms) {
+ message->timeout_ms = timeout_ms;
+}
+int _dbus_message_get_timeout_ms(DBusMessage const *message) {
+ return message->timeout_ms;
+}
+
/**
* Gets the unix fds to be sent over the network for this message.
* This function is guaranteed to always return the same data once a
{
if (!message->locked)
{
- _dbus_header_update_lengths (&message->header,
- _dbus_string_get_length (&message->body));
+ if (!_dbus_message_is_gvariant (message))
+ _dbus_header_update_lengths (&message->header,
+ _dbus_string_get_length (&message->body));
+ else
+ _dbus_message_finalize_gvariant (message, TRUE);
/* must have a signature if you have a body */
_dbus_assert (_dbus_string_get_length (&message->body) == 0 ||
close_unix_fds(int *fds, unsigned *n_fds)
{
DBusError e;
- int i;
+ unsigned int i;
if (*n_fds <= 0)
return;
close_unix_fds(message->unix_fds, &message->n_unix_fds);
#endif
+ if (NULL != message->signature)
+ {
+ _dbus_string_free (message->signature);
+ dbus_free (message->signature);
+ message->signature = NULL;
+ }
+
+ if (NULL != message->unique_sender)
+ {
+ _dbus_string_free (message->unique_sender);
+ dbus_free (message->unique_sender);
+ message->unique_sender = NULL;
+ }
+
was_cached = FALSE;
if (!_DBUS_LOCK (message_cache))
/* copy var_args first, then we can do another iteration over it to
* free memory and close unix fds if parse failed at some point.
*/
- va_copy (copy_args, var_args);
+ DBUS_VA_COPY (copy_args, var_args);
while (spec_type != DBUS_TYPE_INVALID)
{
dbus_message_set_reply_serial (DBusMessage *message,
dbus_uint32_t reply_serial)
{
+ int type = DBUS_TYPE_UINT32;
+
_dbus_return_val_if_fail (message != NULL, FALSE);
_dbus_return_val_if_fail (!message->locked, FALSE);
_dbus_return_val_if_fail (reply_serial != 0, FALSE); /* 0 is invalid */
+ if (_dbus_message_is_gvariant (message))
+ {
+ dbus_uint64_t reply_serial_uint64 = reply_serial;
+ type = DBUS_TYPE_UINT64;
+ return _dbus_header_set_field_basic (&message->header,
+ DBUS_HEADER_FIELD_REPLY_SERIAL,
+ type,
+ &reply_serial_uint64);
+ }
+
return _dbus_header_set_field_basic (&message->header,
DBUS_HEADER_FIELD_REPLY_SERIAL,
- DBUS_TYPE_UINT32,
+ type,
&reply_serial);
}
dbus_message_get_reply_serial (DBusMessage *message)
{
dbus_uint32_t v_UINT32;
+ dbus_uint64_t v_UINT64;
+ int type = DBUS_TYPE_UINT32;
+ void *value = &v_UINT32;
_dbus_return_val_if_fail (message != NULL, 0);
+ if (_dbus_message_is_gvariant (message))
+ {
+ type = DBUS_TYPE_UINT64;
+ value = &v_UINT64;
+ }
+
if (_dbus_header_get_field_basic (&message->header,
DBUS_HEADER_FIELD_REPLY_SERIAL,
- DBUS_TYPE_UINT32,
- &v_UINT32))
- return v_UINT32;
+ type,
+ value))
+ {
+ if (_dbus_message_is_gvariant (message))
+ return v_UINT64;
+ else
+ return v_UINT32;
+ }
else
return 0;
}
}
static DBusMessage*
-dbus_message_new_empty_header (void)
+dbus_message_new_empty_header (dbus_bool_t gvariant)
{
DBusMessage *message;
dbus_bool_t from_cache;
#endif
message->counters = NULL;
message->size_counter_delta = 0;
+ message->timeout_ms = -1;
message->changed_stamp = 0;
#ifdef HAVE_UNIX_FD_PASSING
message->unix_fd_counter_delta = 0;
#endif
+ _dbus_message_toggle_gvariant (message, gvariant); /* this works only if kdbus is enabled */
+
if (!from_cache)
_dbus_data_slot_list_init (&message->slot_list);
}
}
+ message->signature = NULL;
+ message->unique_sender = NULL;
+ message->gvariant_body_last_offset = GVARIANT_LAST_OFFSET_NOT_SET;
+ message->gvariant_body_last_pos = 0;
+
return message;
}
-/**
- * Constructs a new message of the given message type.
- * Types include #DBUS_MESSAGE_TYPE_METHOD_CALL,
- * #DBUS_MESSAGE_TYPE_SIGNAL, and so forth.
- *
- * Usually you want to use dbus_message_new_method_call(),
- * dbus_message_new_method_return(), dbus_message_new_signal(),
- * or dbus_message_new_error() instead.
- *
- * @param message_type type of message
- * @returns new message or #NULL if no memory
- */
-DBusMessage*
-dbus_message_new (int message_type)
+static DBusMessage*
+_dbus_message_create_protocol_version (int message_type,
+ const char *destination,
+ const char *path,
+ const char *interface,
+ const char *member,
+ const char *error_name,
+ dbus_bool_t gvariant)
{
DBusMessage *message;
- _dbus_return_val_if_fail (message_type != DBUS_MESSAGE_TYPE_INVALID, NULL);
+ _dbus_assert (message_type != DBUS_MESSAGE_TYPE_INVALID);
- message = dbus_message_new_empty_header ();
+ message = dbus_message_new_empty_header (gvariant);
if (message == NULL)
return NULL;
- if (!_dbus_header_create (&message->header,
+ if (!(_dbus_message_is_gvariant(message) ? _dbus_header_gvariant_create : _dbus_header_create) (&message->header,
DBUS_COMPILER_BYTE_ORDER,
message_type,
- NULL, NULL, NULL, NULL, NULL))
+ destination, path, interface, member, error_name))
{
dbus_message_unref (message);
return NULL;
return message;
}
+static DBusMessage*
+_dbus_message_create (int message_type,
+ const char *destination,
+ const char *path,
+ const char *interface,
+ const char *member,
+ const char *error_name)
+{
+ return _dbus_message_create_protocol_version(message_type,
+ destination,
+ path,
+ interface,
+ member,
+ error_name,
+ _dbus_default_protocol_version == DBUS_PROTOCOL_VERSION_GVARIANT);
+}
+
+/**
+ * Constructs a new message of the given message type.
+ * Types include #DBUS_MESSAGE_TYPE_METHOD_CALL,
+ * #DBUS_MESSAGE_TYPE_SIGNAL, and so forth.
+ *
+ * Usually you want to use dbus_message_new_method_call(),
+ * dbus_message_new_method_return(), dbus_message_new_signal(),
+ * or dbus_message_new_error() instead.
+ *
+ * @param message_type type of message
+ * @returns new message or #NULL if no memory
+ */
+DBusMessage*
+dbus_message_new (int message_type)
+{
+ return _dbus_message_create(message_type,
+ NULL, NULL, NULL, NULL, NULL);
+}
+
/**
* Constructs a new message to invoke a method on a remote
* object. Returns #NULL if memory can't be allocated for the
const char *iface,
const char *method)
{
- DBusMessage *message;
-
_dbus_return_val_if_fail (path != NULL, NULL);
_dbus_return_val_if_fail (method != NULL, NULL);
_dbus_return_val_if_fail (destination == NULL ||
_dbus_check_is_valid_interface (iface), NULL);
_dbus_return_val_if_fail (_dbus_check_is_valid_member (method), NULL);
- message = dbus_message_new_empty_header ();
- if (message == NULL)
- return NULL;
-
- if (!_dbus_header_create (&message->header,
- DBUS_COMPILER_BYTE_ORDER,
- DBUS_MESSAGE_TYPE_METHOD_CALL,
- destination, path, iface, method, NULL))
- {
- dbus_message_unref (message);
- return NULL;
- }
-
- return message;
+ return _dbus_message_create(DBUS_MESSAGE_TYPE_METHOD_CALL,
+ destination, path, iface, method, NULL);
}
/**
/* sender is allowed to be null here in peer-to-peer case */
- message = dbus_message_new_empty_header ();
+ message = _dbus_message_create (DBUS_MESSAGE_TYPE_METHOD_RETURN,
+ sender, NULL, NULL, NULL, NULL);
if (message == NULL)
return NULL;
- if (!_dbus_header_create (&message->header,
- DBUS_COMPILER_BYTE_ORDER,
- DBUS_MESSAGE_TYPE_METHOD_RETURN,
- sender, NULL, NULL, NULL, NULL))
- {
- dbus_message_unref (message);
- return NULL;
- }
-
dbus_message_set_no_reply (message, TRUE);
if (!dbus_message_set_reply_serial (message,
_dbus_return_val_if_fail (_dbus_check_is_valid_interface (iface), NULL);
_dbus_return_val_if_fail (_dbus_check_is_valid_member (name), NULL);
- message = dbus_message_new_empty_header ();
+ message = _dbus_message_create (DBUS_MESSAGE_TYPE_SIGNAL,
+ NULL, path, iface, name, NULL);
if (message == NULL)
return NULL;
- if (!_dbus_header_create (&message->header,
- DBUS_COMPILER_BYTE_ORDER,
- DBUS_MESSAGE_TYPE_SIGNAL,
- NULL, path, iface, name, NULL))
- {
- dbus_message_unref (message);
- return NULL;
- }
-
dbus_message_set_no_reply (message, TRUE);
return message;
* when the message bus is dealing with an unregistered
* connection.
*/
- message = dbus_message_new_empty_header ();
+ message = _dbus_message_create (DBUS_MESSAGE_TYPE_ERROR,
+ sender, NULL, NULL, NULL, error_name);
if (message == NULL)
return NULL;
- if (!_dbus_header_create (&message->header,
- DBUS_COMPILER_BYTE_ORDER,
- DBUS_MESSAGE_TYPE_ERROR,
- sender, NULL, NULL, NULL, error_name))
- {
- dbus_message_unref (message);
- return NULL;
- }
-
dbus_message_set_no_reply (message, TRUE);
if (!dbus_message_set_reply_serial (message,
#ifndef DBUS_DISABLE_CHECKS
retval->generation = message->generation;
#endif
+ _dbus_message_toggle_gvariant (retval, _dbus_message_is_gvariant (message));
if (!_dbus_header_copy (&message->header, &retval->header))
{
DBusMessageRealIter *real,
int iter_type)
{
- _dbus_assert (sizeof (DBusMessageRealIter) <= sizeof (DBusMessageIter));
+ _DBUS_STATIC_ASSERT (sizeof (DBusMessageRealIter) <= sizeof (DBusMessageIter));
/* Since the iterator will read or write who-knows-what from the
* message, we need to get in the right byte order
const DBusString *type_str;
int type_pos;
+ BUILD_BUG_ON (sizeof(DBusMessageIter) != sizeof(DBusMessageRealIter));
+
_dbus_return_val_if_fail (message != NULL, FALSE);
_dbus_return_val_if_fail (iter != NULL, FALSE);
- get_const_signature (&message->header, &type_str, &type_pos);
+ get_const_signature (message, &type_str, &type_pos);
_dbus_message_iter_init_common (message, real,
DBUS_MESSAGE_ITER_TYPE_READER);
_dbus_type_reader_init (&real->u.reader,
- _dbus_header_get_byte_order (&message->header),
- type_str, type_pos,
- &message->body,
- 0);
+ _dbus_header_get_byte_order (&message->header),
+ type_str, type_pos,
+ &message->body,
+ 0);
+
+ if (_dbus_message_is_gvariant (message))
+ _dbus_type_reader_gvariant_init (&real->u.reader, message);
return _dbus_type_reader_get_current_type (&real->u.reader) != DBUS_TYPE_INVALID;
}
}
/**
+ * Returns the number of elements in the array-typed value pointed
+ * to by the iterator.
+ * Note that this function is O(1) for arrays of fixed-size types
+ * but O(n) for arrays of variable-length types such as strings,
+ * so it may be a bad idea to use it.
+ *
+ * @param iter the iterator
+ * @returns the number of elements in the array
+ */
+int
+dbus_message_iter_get_element_count (DBusMessageIter *iter)
+{
+ DBusMessageRealIter *real = (DBusMessageRealIter *)iter;
+ DBusTypeReader array;
+ int element_type;
+ int n_elements = 0;
+
+ _dbus_return_val_if_fail (_dbus_message_iter_check (real), 0);
+ _dbus_return_val_if_fail (_dbus_type_reader_get_current_type (&real->u.reader)
+ == DBUS_TYPE_ARRAY, 0);
+
+ element_type = _dbus_type_reader_get_element_type (&real->u.reader);
+ _dbus_type_reader_recurse (&real->u.reader, &array);
+ if (dbus_type_is_fixed (element_type))
+ {
+ int alignment = _dbus_type_get_alignment (element_type);
+ int total_len = _dbus_type_reader_get_array_length (&array);
+ n_elements = total_len / alignment;
+ }
+ else
+ {
+ while (_dbus_type_reader_get_current_type (&array) != DBUS_TYPE_INVALID)
+ {
+ ++n_elements;
+ _dbus_type_reader_next (&array);
+ }
+ }
+
+ return n_elements;
+}
+
+/**
* Returns the number of bytes in the array as marshaled in the wire
* protocol. The iterator must currently be inside an array-typed
* value.
*
* This function is deprecated on the grounds that it is stupid. Why
* would you want to know how many bytes are in the array as marshaled
- * in the wire protocol? For now, use the n_elements returned from
- * dbus_message_iter_get_fixed_array() instead, or iterate over the
- * array values and count them.
+ * in the wire protocol? Use dbus_message_iter_get_element_count() instead.
*
- * @todo introduce a variant of this get_n_elements that returns
- * the number of elements, though with a non-fixed array it will not
- * be very efficient, so maybe it's not good.
- *
* @param iter the iterator
* @returns the number of bytes in the array
*/
* when a value is actually appended. That means that init() never fails
* due to OOM.
*/
- _dbus_type_writer_init_types_delayed (&real->u.writer,
- _dbus_header_get_byte_order (&message->header),
- &message->body,
- _dbus_string_get_length (&message->body));
+ _dbus_type_writer_gvariant_init_types_delayed (
+ &real->u.writer,
+ _dbus_header_get_byte_order (&message->header),
+ &message->body,
+ _dbus_string_get_length (&message->body),
+ _dbus_message_is_gvariant (message),
+ &message->gvariant_body_last_offset,
+ &message->gvariant_body_last_pos);
}
/**
if (current_sig)
{
int current_len;
+ int additional_size_for_len = 0;
- current_len = _dbus_string_get_byte (current_sig, current_sig_pos);
- current_sig_pos += 1; /* move on to sig data */
+ if (!real->u.writer.gvariant)
+ {
+ current_len = _dbus_string_get_byte (current_sig, current_sig_pos);
+ current_sig_pos += 1; /* move on to sig data */
+ additional_size_for_len = 4;
+ }
+ else
+ {
+ /* GVariant has no length field, simply string */
+ current_len = strlen (_dbus_string_get_const_data (current_sig) + current_sig_pos);
+ }
- if (!_dbus_string_init_preallocated (str, current_len + 4))
+ if (!_dbus_string_init_preallocated (str, current_len + additional_size_for_len))
{
dbus_free (str);
return FALSE;
_dbus_return_val_if_fail (message != NULL, NULL);
+ if (NULL != message->unique_sender)
+ return _dbus_string_get_const_data (message->unique_sender);
+
v = NULL; /* in case field doesn't exist */
_dbus_header_get_field_basic (&message->header,
DBUS_HEADER_FIELD_SENDER,
_dbus_return_val_if_fail (message != NULL, NULL);
- get_const_signature (&message->header, &type_str, &type_pos);
+ get_const_signature (message, &type_str, &type_pos);
return _dbus_string_get_const_data_len (type_str, type_pos, 0);
}
loader->n_unix_fds += n_fds;
loader->unix_fds_outstanding = FALSE;
+
+ if (n_fds && loader->unix_fds_change)
+ loader->unix_fds_change (loader->unix_fds_change_data);
#else
_dbus_assert_not_reached("Platform doesn't support unix fd passing");
#endif
/* 2. VALIDATE BODY */
if (mode != DBUS_VALIDATION_MODE_WE_TRUST_THIS_DATA_ABSOLUTELY)
{
- get_const_signature (&message->header, &type_str, &type_pos);
-
- /* Because the bytes_remaining arg is NULL, this validates that the
- * body is the right length
- */
- validity = _dbus_validate_body_with_reason (type_str,
- type_pos,
- byte_order,
- NULL,
- &loader->data,
- header_len,
- body_len);
+ if (_dbus_message_is_gvariant (message))
+ {
+ type_str = NULL;
+ type_pos = 0;
+ validity = _dbus_validate_gvariant_body_with_reason (type_str,
+ type_pos,
+ byte_order,
+ NULL,
+ &loader->data,
+ header_len,
+ body_len);
+ }
+ else
+ {
+
+ get_const_signature (message, &type_str, &type_pos);
+
+ /* Because the bytes_remaining arg is NULL, this validates that the
+ * body is the right length
+ */
+
+ validity = _dbus_validate_body_with_reason (type_str,
+ type_pos,
+ byte_order,
+ NULL,
+ &loader->data,
+ header_len,
+ body_len);
+ }
if (validity != DBUS_VALID)
{
_dbus_verbose ("Failed to validate message body code %d\n", validity);
message->n_unix_fds_allocated = message->n_unix_fds = n_unix_fds;
loader->n_unix_fds -= n_unix_fds;
- memmove(loader->unix_fds + n_unix_fds, loader->unix_fds, loader->n_unix_fds);
+ memmove (loader->unix_fds, loader->unix_fds + n_unix_fds, loader->n_unix_fds * sizeof (loader->unix_fds[0]));
+
+ if (loader->unix_fds_change)
+ loader->unix_fds_change (loader->unix_fds_change_data);
}
else
message->unix_fds = NULL;
return FALSE;
}
+static dbus_bool_t
+set_unique_sender (DBusMessage *message, uint64_t unique_sender_id)
+{
+ if (NULL == message->unique_sender)
+ {
+ message->unique_sender = dbus_new (DBusString, 1);
+ if (NULL == message->unique_sender)
+ return FALSE;
+
+ if (!_dbus_string_init (message->unique_sender))
+ return FALSE;
+ }
+
+ _dbus_string_set_length (message->unique_sender, 0);
+
+ if (!_dbus_string_append_printf (message->unique_sender, ":1.%llu", (unsigned long long)unique_sender_id))
+ {
+ _dbus_string_free (message->unique_sender);
+ dbus_free (message->unique_sender);
+ message->unique_sender = NULL;
+ return FALSE;
+ }
+
+ return TRUE;
+}
+
/**
* Converts buffered data into messages, if we have enough data. If
* we don't have enough data, does nothing.
{
DBusValidity validity;
int byte_order, fields_array_len, header_len, body_len;
+ dbus_bool_t is_gvariant;
if (_dbus_header_have_message_untrusted (loader->max_message_size,
&validity,
&header_len,
&body_len,
&loader->data, 0,
- _dbus_string_get_length (&loader->data)))
+ _dbus_string_get_length (&loader->data),
+ &is_gvariant))
{
DBusMessage *message;
_dbus_assert (validity == DBUS_VALID);
- message = dbus_message_new_empty_header ();
+ message = dbus_message_new_empty_header (is_gvariant);
if (message == NULL)
return FALSE;
return loader->corrupted;
}
+ if (_dbus_message_is_gvariant (message))
+ {
+ set_unique_sender (message, _dbus_message_loader_get_unique_sender_id (loader));
+ message->locked = TRUE;
+ }
+
_dbus_assert (loader->messages != NULL);
_dbus_assert (_dbus_list_find_last (&loader->messages, message) != NULL);
}
return TRUE;
}
+DBusMessage *
+_dbus_decode_kmsg (DBusString *data,
+ uint64_t sender_id,
+ int *fds,
+ unsigned n_fds)
+{
+ if (_dbus_string_get_length (data) >= DBUS_MINIMUM_HEADER_SIZE)
+ {
+
+ DBusValidity validity;
+ int byte_order, fields_array_len, header_len, body_len;
+ dbus_bool_t is_gvariant;
+
+ if (_dbus_header_have_message_untrusted (DBUS_MAXIMUM_MESSAGE_LENGTH,
+ &validity,
+ &byte_order,
+ &fields_array_len,
+ &header_len,
+ &body_len,
+ data, 0,
+ _dbus_string_get_length (data),
+ &is_gvariant))
+ {
+ DBusMessage *message;
+ dbus_uint32_t n_unix_fds = 0;
+ const DBusString *type_str = NULL;
+ int type_pos = 0;
+
+ _dbus_assert (validity == DBUS_VALID);
+
+ message = dbus_message_new_empty_header (is_gvariant);
+ if (message == NULL)
+ return NULL;
+
+ /*
+ * Validate and copy over header
+ */
+ _dbus_assert (_dbus_string_get_length (&message->header.data) == 0);
+ _dbus_assert ((header_len + body_len) <= _dbus_string_get_length (data));
+
+ if (!_dbus_header_load (&message->header,
+ DBUS_VALIDATION_MODE_DATA_IS_UNTRUSTED,
+ &validity,
+ byte_order,
+ fields_array_len,
+ header_len,
+ body_len,
+ data, 0,
+ _dbus_string_get_length (data)))
+ {
+ _dbus_verbose ("Failed to load header for new message code %d\n", validity);
+ dbus_message_unref (message);
+ return NULL;
+ }
+
+ _dbus_assert (validity == DBUS_VALID);
+
+ /*
+ * Validate body
+ */
+ if (_dbus_message_is_gvariant (message))
+ {
+ validity = _dbus_validate_gvariant_body_with_reason (type_str,
+ type_pos,
+ byte_order,
+ NULL,
+ data,
+ header_len,
+ body_len);
+ }
+ else
+ {
+ _dbus_verbose ("Not valid GVariant dbus message\n");
+ dbus_message_unref (message);
+ return NULL;
+ }
+
+ if (validity != DBUS_VALID)
+ {
+ _dbus_verbose ("Failed to validate message body code %d\n", validity);
+ dbus_message_unref (message);
+ return NULL;
+ }
+
+ /*
+ * Copy over Unix FDS
+ */
+ _dbus_header_get_field_basic(&message->header,
+ DBUS_HEADER_FIELD_UNIX_FDS,
+ DBUS_TYPE_UINT32,
+ &n_unix_fds);
+
+#ifdef HAVE_UNIX_FD_PASSING
+
+ if (n_unix_fds > n_fds)
+ {
+ _dbus_verbose("Message contains references to more unix fds than were sent %u != %u\n",
+ n_unix_fds, message->n_unix_fds);
+ dbus_message_unref (message);
+ return NULL;
+ }
+
+ /* If this was a recycled message there might still be
+ some memory allocated for the fds */
+ dbus_free(message->unix_fds);
+
+ if (n_unix_fds > 0)
+ {
+ message->unix_fds = _dbus_memdup(fds, n_unix_fds * sizeof(message->unix_fds[0]));
+ if (message->unix_fds == NULL)
+ {
+ _dbus_verbose ("Failed to allocate file descriptor array\n");
+ dbus_message_unref (message);
+ return NULL;
+ }
+
+ message->n_unix_fds_allocated = message->n_unix_fds = n_unix_fds;
+ }
+ else
+ message->unix_fds = NULL;
+#else
+ if (n_unix_fds > 0)
+ {
+ _dbus_verbose ("Hmm, message claims to come with file descriptors "
+ "but that's not supported on our platform, disconnecting.\n");
+ dbus_message_unref (message);
+ return NULL;
+ }
+#endif
+
+ /*
+ * Copy over message body
+ */
+ _dbus_assert (_dbus_string_get_length (&message->body) == 0);
+ _dbus_assert (_dbus_string_get_length (data) >= (header_len + body_len));
+
+ if (!_dbus_string_copy_len (data, header_len, body_len, &message->body, 0))
+ {
+ _dbus_verbose ("Failed to move body into new message\n");
+ dbus_message_unref (message);
+ return NULL;
+ }
+
+ _dbus_assert (_dbus_string_get_length (&message->header.data) == header_len);
+ _dbus_assert (_dbus_string_get_length (&message->body) == body_len);
+
+ set_unique_sender (message, sender_id);
+ message->locked = TRUE;
+
+ /* Yupi, we have DBusMessage* */
+ return message;
+ }
+ else
+ {
+ _dbus_verbose ("Data broken with invalid code %d\n", validity);
+ return NULL;
+ } /* if _dbus_header_have_message_untrusted() */
+
+ }
+ else
+ {
+ _dbus_verbose ("message size < DBUS_MINIMUM_HEADER_SIZE\n");
+ return NULL;
+ } /* if DBUS_MINIMUM_HEADER_SIZE */
+}
+
/**
* Peeks at first loaded message, returns #NULL if no messages have
* been queued.
return loader->max_message_unix_fds;
}
+/**
+ * Return how many file descriptors are pending in the loader
+ *
+ * @param loader the loader
+ */
+int
+_dbus_message_loader_get_pending_fds_count (DBusMessageLoader *loader)
+{
+#ifdef HAVE_UNIX_FD_PASSING
+ return loader->n_unix_fds;
+#else
+ return 0;
+#endif
+}
+
+/**
+ * Register a function to be called whenever the number of pending file
+ * descriptors in the loader change.
+ *
+ * @param loader the loader
+ * @param callback the callback
+ * @param data the data for the callback
+ */
+void
+_dbus_message_loader_set_pending_fds_function (DBusMessageLoader *loader,
+ void (* callback) (void *),
+ void *data)
+{
+#ifdef HAVE_UNIX_FD_PASSING
+ loader->unix_fds_change = callback;
+ loader->unix_fds_change_data = data;
+#endif
+}
+
+void
+_dbus_message_loader_set_unique_sender_id (DBusMessageLoader *loader,
+ uint64_t id)
+{
+ loader->unique_sender_id = id;
+}
+
+uint64_t
+_dbus_message_loader_get_unique_sender_id (DBusMessageLoader *loader)
+{
+ return loader->unique_sender_id;
+}
+
static DBusDataSlotAllocator slot_allocator =
_DBUS_DATA_SLOT_ALLOCATOR_INIT (_DBUS_LOCK_NAME (message_slots));
return NULL;
_dbus_message_loader_get_buffer (loader, &buffer);
- _dbus_string_append_len (buffer, str, len);
+
+ if (!_dbus_string_append_len (buffer, str, len))
+ goto fail_oom;
+
_dbus_message_loader_return_buffer (loader, buffer);
if (!_dbus_message_loader_queue_messages (loader))
int byte_order, fields_array_len, header_len, body_len;
DBusValidity validity = DBUS_VALID;
int have_message;
+ dbus_bool_t is_gvariant;
if (!buf || len < DBUS_MINIMUM_HEADER_SIZE)
return 0;
&header_len,
&body_len,
&str, 0,
- len);
+ len,
+ &is_gvariant);
_dbus_string_free (&str);
if (validity == DBUS_VALID)
}
}
+static dbus_bool_t
+_dbus_message_copy_recursive(DBusMessageIter *iter, DBusMessageIter *dest)
+{
+ dbus_bool_t res = TRUE;
+ int current_type;
+
+ while ((current_type = dbus_message_iter_get_arg_type (iter)) != DBUS_TYPE_INVALID) {
+ if (dbus_type_is_basic(current_type)) {
+ DBusBasicValue value;
+ dbus_message_iter_get_basic (iter, &value);
+ dbus_message_iter_append_basic (dest, current_type, &value);
+ }
+ else {
+ DBusMessageIter sub;
+ DBusMessageIter dest_sub;
+ char *sig = NULL;
+
+ dbus_message_iter_recurse (iter, &sub);
+ if (DBUS_TYPE_VARIANT == current_type)
+ sig = dbus_message_iter_get_signature (&sub);
+ else if (DBUS_TYPE_ARRAY == current_type)
+ sig = dbus_message_iter_get_signature (&sub);
+
+ res = res && dbus_message_iter_open_container (dest, current_type, sig, &dest_sub);
+ dbus_free(sig);
+ res = res && _dbus_message_copy_recursive (&sub, &dest_sub);
+ res = res && dbus_message_iter_close_container (dest, &dest_sub);
+
+ if (!res) {
+ return FALSE;
+ }
+ }
+
+ dbus_message_iter_next (iter);
+ }
+
+ return TRUE;
+}
+
+void
+_dbus_on_new_bus (int type)
+{
+ _dbus_assert (type == DBUS_MAJOR_PROTOCOL_VERSION || type == DBUS_PROTOCOL_VERSION_GVARIANT);
+ _dbus_protocol_strategy_bus_function (type);
+}
+
+static void
+_dbus_on_send_message (int type)
+{
+ _dbus_assert (type == DBUS_MAJOR_PROTOCOL_VERSION || type == DBUS_PROTOCOL_VERSION_GVARIANT);
+ _dbus_protocol_strategy_message_function (type);
+}
+
+DBusMessage *
+_dbus_message_remarshal (DBusMessage *message, dbus_bool_t gvariant)
+{
+ DBusMessage *ret;
+ DBusMessageIter iter, ret_iter;
+ size_t i;
+ dbus_uint32_t serial;
+ const char *sender;
+
+ _dbus_assert (message->locked);
+
+ _dbus_on_send_message (gvariant ? DBUS_PROTOCOL_VERSION_GVARIANT : DBUS_MAJOR_PROTOCOL_VERSION);
+
+ ret = _dbus_message_create_protocol_version (dbus_message_get_type(message),
+ dbus_message_get_destination(message),
+ dbus_message_get_path(message),
+ dbus_message_get_interface(message),
+ dbus_message_get_member(message),
+ dbus_message_get_error_name(message),
+ gvariant);
+
+ dbus_message_iter_init (message, &iter);
+ dbus_message_iter_init_append (ret, &ret_iter);
+ if (!_dbus_message_copy_recursive(&iter, &ret_iter))
+ return NULL;
+
+#ifdef HAVE_UNIX_FD_PASSING
+ ret->unix_fds = dbus_new(int, message->n_unix_fds);
+ if (ret->unix_fds == NULL && message->n_unix_fds > 0)
+ goto err;
+
+ ret->n_unix_fds_allocated = message->n_unix_fds;
+
+ for (i = 0; i < message->n_unix_fds; ++i) {
+ ret->unix_fds[i] = _dbus_dup(message->unix_fds[i], NULL);
+
+ if (ret->unix_fds[i] < 0)
+ goto err;
+ }
+
+ ret->n_unix_fds = message->n_unix_fds;
+#endif
+
+ /* Remarshal data in header:
+ byte order (already set)
+ type (already set)
+ flags - only those we understand
+ version (already set)
+ body length
+ serial
+ fields array (length)
+ fields:
+ path (already set)
+ interface (already set)
+ member (already set)
+ error name (already set)
+ reply serial
+ destination (already set)
+ sender
+ signature (set during copy, but an action needed for conversion to GVariant)
+ unix fds
+ */
+
+ /* FLAGS */
+ _dbus_header_toggle_flag (&ret->header, DBUS_HEADER_FLAG_NO_REPLY_EXPECTED,
+ _dbus_header_get_flag (&message->header, DBUS_HEADER_FLAG_NO_REPLY_EXPECTED));
+
+ _dbus_header_toggle_flag (&ret->header, DBUS_HEADER_FLAG_NO_AUTO_START,
+ _dbus_header_get_flag (&message->header, DBUS_HEADER_FLAG_NO_AUTO_START));
+
+ /* SERIAL / COOKIE */
+ serial = dbus_message_get_serial (message);
+
+ if (0 != serial)
+ dbus_message_set_serial (ret, serial);
+
+ /* Field: REPLY_SERIAL */
+ serial = dbus_message_get_reply_serial (message);
+
+ if (0 != serial && !dbus_message_set_reply_serial (ret, serial))
+ goto err;
+
+ /* Field: SENDER */
+ sender = dbus_message_get_sender (message);
+
+ if (NULL != sender && !dbus_message_set_sender (ret, sender))
+ goto err;
+
+ /* BODY LENGTH */
+ if (!gvariant)
+ _dbus_header_update_lengths (&ret->header,
+ _dbus_string_get_length (&ret->body));
+ /* For GVariant: */
+ /* Field: SIGNATURE to body; add body offset - this is done with dbus_message_lock() */
+ else
+ dbus_message_lock (ret);
+
+ return ret;
+
+err:
+ _dbus_header_free (&ret->header);
+ _dbus_string_free (&ret->body);
+
+#ifdef HAVE_UNIX_FD_PASSING
+ close_unix_fds(ret->unix_fds, &ret->n_unix_fds);
+ dbus_free(ret->unix_fds);
+#endif
+
+ return NULL;
+}
+
+void
+dbus_set_protocol_version (unsigned char version)
+{
+ _dbus_default_protocol_version = version;
+}
+
+static void
+protocol_strategy_first_type (int type)
+{
+ /* change protocol once */
+ if (!_dbus_first_bus_open)
+ {
+ _dbus_first_bus_open = TRUE;
+ _dbus_default_protocol_version = type;
+ }
+}
+
+static void
+protocol_strategy_last_type (int type)
+{
+ /* change protocol every time it is needed */
+ if (_dbus_default_protocol_version != type)
+ _dbus_default_protocol_version = type;
+}
+
+static void
+protocol_strategy_static (int type)
+{
+ /* do not change */
+}
+
+void
+dbus_set_default_protocol_strategy (const char *strategy_name)
+{
+ if (strcmp (strategy_name, "first-bus") == 0)
+ {
+ _dbus_protocol_strategy_bus_function = protocol_strategy_first_type;
+ _dbus_protocol_strategy_message_function = protocol_strategy_static;
+ }
+ else if (strcmp (strategy_name, "dbus1") == 0)
+ {
+ _dbus_default_protocol_version = DBUS_MAJOR_PROTOCOL_VERSION;
+ _dbus_protocol_strategy_bus_function = protocol_strategy_static;
+ _dbus_protocol_strategy_message_function = protocol_strategy_static;
+ }
+ else if (strcmp (strategy_name, "gvariant") == 0)
+ {
+ _dbus_default_protocol_version = DBUS_PROTOCOL_VERSION_GVARIANT;
+ _dbus_protocol_strategy_bus_function = protocol_strategy_static;
+ _dbus_protocol_strategy_message_function = protocol_strategy_static;
+ }
+ else if (strcmp (strategy_name, "last-message") == 0)
+ {
+ _dbus_protocol_strategy_bus_function = protocol_strategy_static;
+ _dbus_protocol_strategy_message_function = protocol_strategy_last_type;
+ }
+ else /* "last-bus" is default strategy */
+ {
+ _dbus_protocol_strategy_bus_function = protocol_strategy_last_type;
+ _dbus_protocol_strategy_message_function = protocol_strategy_static;
+ }
+}
+
+DBusMessage *
+_dbus_generate_local_error_message (dbus_uint32_t serial,
+ char *error_name,
+ char *error_msg)
+{
+ DBusMessage *message;
+ message = dbus_message_new (DBUS_MESSAGE_TYPE_ERROR);
+ if (!message)
+ goto out;
+
+ if (!dbus_message_set_error_name (message, error_name))
+ {
+ dbus_message_unref (message);
+ message = NULL;
+ goto out;
+ }
+
+ dbus_message_set_no_reply (message, TRUE);
+
+ if (!dbus_message_set_reply_serial (message,
+ serial))
+ {
+ dbus_message_unref (message);
+ message = NULL;
+ goto out;
+ }
+
+ if (error_msg != NULL)
+ {
+ DBusMessageIter iter;
+
+ dbus_message_iter_init_append (message, &iter);
+ if (!dbus_message_iter_append_basic (&iter,
+ DBUS_TYPE_STRING,
+ &error_msg))
+ {
+ dbus_message_unref (message);
+ message = NULL;
+ goto out;
+ }
+ }
+
+ out:
+ return message;
+}
+
+dbus_bool_t
+_dbus_message_assure_dbus1 (DBusMessage **message)
+{
+ if ((*message)->header.protocol_version != DBUS_MAJOR_PROTOCOL_VERSION)
+ {
+ *message = _dbus_message_remarshal (*message, FALSE);
+ return TRUE;
+ }
+ return FALSE;
+}
+
+dbus_bool_t
+_dbus_message_assure_gvariant (DBusMessage **message)
+{
+ if ((*message)->header.protocol_version != DBUS_PROTOCOL_VERSION_GVARIANT)
+ {
+ *message = _dbus_message_remarshal (*message, TRUE);
+ return TRUE;
+ }
+ return FALSE;
+}
+
/** @} */
/* tests in dbus-message-util.c */