From 9c3d566e95c9080f6040c64531b0ccae22bd5d74 Mon Sep 17 00:00:00 2001 From: Havoc Pennington Date: Sat, 15 Jan 2005 07:15:38 +0000 Subject: 2005-01-15 Havoc Pennington * Land the new message args API and type system. This patch is huge, but the public API change is not really large. The set of D-BUS types has changed somewhat, and the arg "getters" are more geared toward language bindings; they don't make a copy, etc. There are also some known issues. See these emails for details on this huge patch: http://lists.freedesktop.org/archives/dbus/2004-December/001836.html http://lists.freedesktop.org/archives/dbus/2005-January/001922.html * dbus/dbus-marshal-*: all the new stuff * dbus/dbus-message.c: basically rewritten * dbus/dbus-memory.c (check_guards): with "guards" enabled, init freed blocks to be all non-nul bytes so using freed memory is less likely to work right * dbus/dbus-internals.c (_dbus_test_oom_handling): add DBUS_FAIL_MALLOC=N environment variable, so you can do DBUS_FAIL_MALLOC=0 to skip the out-of-memory checking, or DBUS_FAIL_MALLOC=10 to make it really, really, really slow and thorough. * qt/message.cpp: port to the new message args API (operator<<): use str.utf8() rather than str.unicode() (pretty sure this is right from the Qt docs?) * glib/dbus-gvalue.c: port to the new message args API * bus/dispatch.c, bus/driver.c: port to the new message args API * dbus/dbus-string.c (_dbus_string_init_const_len): initialize the "locked" flag to TRUE and align_offset to 0; I guess we never looked at these anyhow, but seems cleaner. * dbus/dbus-string.h (_DBUS_STRING_ALLOCATION_PADDING): move allocation padding macro to this header; use it to implement (_DBUS_STRING_STATIC): ability to declare a static string. * dbus/dbus-message.c (_dbus_message_has_type_interface_member): change to return TRUE if the interface is not set. * dbus/dbus-string.[hc]: move the D-BUS specific validation stuff to dbus-marshal-validate.[hc] * dbus/dbus-marshal-basic.c (_dbus_type_to_string): move here from dbus-internals.c * dbus/Makefile.am: cut over from dbus-marshal.[hc] to dbus-marshal-*.[hc] * dbus/dbus-object-tree.c (_dbus_decompose_path): move this function here from dbus-marshal.c --- dbus/.cvsignore | 2 + dbus/Makefile.am | 12 +- dbus/dbus-auth-script.c | 1 - dbus/dbus-bus.c | 25 +- dbus/dbus-connection.c | 10 - dbus/dbus-internals.c | 76 +- dbus/dbus-internals.h | 6 +- dbus/dbus-mainloop.c | 2 +- dbus/dbus-marshal-basic.c | 199 +- dbus/dbus-marshal-basic.h | 122 +- dbus/dbus-marshal-header.c | 1435 ++++++++ dbus/dbus-marshal-header.h | 128 + dbus/dbus-marshal-recursive.c | 938 ++++- dbus/dbus-marshal-recursive.h | 23 +- dbus/dbus-marshal-validate.c | 1265 +++++++ dbus/dbus-marshal-validate.h | 128 + dbus/dbus-marshal.c | 2941 ---------------- dbus/dbus-marshal.h | 348 -- dbus/dbus-memory.c | 15 +- dbus/dbus-message-builder.c | 10 +- dbus/dbus-message-internal.h | 2 +- dbus/dbus-message.c | 7838 +++++++++++++---------------------------- dbus/dbus-message.h | 150 +- dbus/dbus-object-tree.c | 91 +- dbus/dbus-object-tree.h | 6 + dbus/dbus-protocol-new.h | 226 -- dbus/dbus-protocol.h | 131 +- dbus/dbus-sha.c | 2 +- dbus/dbus-string.c | 820 +---- dbus/dbus-string.h | 49 +- dbus/dbus-test.c | 24 +- dbus/dbus-test.h | 43 +- 32 files changed, 6759 insertions(+), 10309 deletions(-) create mode 100644 dbus/dbus-marshal-header.c create mode 100644 dbus/dbus-marshal-header.h delete mode 100644 dbus/dbus-marshal.c delete mode 100644 dbus/dbus-marshal.h delete mode 100644 dbus/dbus-protocol-new.h (limited to 'dbus') diff --git a/dbus/.cvsignore b/dbus/.cvsignore index 02bb3e6e..51cd09cb 100644 --- a/dbus/.cvsignore +++ b/dbus/.cvsignore @@ -12,3 +12,5 @@ dbus-test dbus-arch-deps.h .dbus-keyrings dbus-glib-error-enum.h +*.gcno +*.gcda diff --git a/dbus/Makefile.am b/dbus/Makefile.am index a9ec8808..12c9444f 100644 --- a/dbus/Makefile.am +++ b/dbus/Makefile.am @@ -54,6 +54,12 @@ DBUS_LIB_SOURCES= \ dbus-errors.c \ dbus-keyring.c \ dbus-keyring.h \ + dbus-marshal-header.c \ + dbus-marshal-header.h \ + dbus-marshal-recursive.c \ + dbus-marshal-recursive.h \ + dbus-marshal-validate.c \ + dbus-marshal-validate.h \ dbus-message.c \ dbus-message-internal.h \ dbus-object-tree.c \ @@ -98,13 +104,11 @@ DBUS_SHARED_SOURCES= \ dbus-internals.h \ dbus-list.c \ dbus-list.h \ - dbus-marshal.c \ - dbus-marshal.h \ + dbus-marshal-basic.c \ + dbus-marshal-basic.h \ dbus-memory.c \ dbus-mempool.c \ dbus-mempool.h \ - dbus-message-builder.c \ - dbus-message-builder.h \ dbus-spawn.c \ dbus-spawn.h \ dbus-string.c \ diff --git a/dbus/dbus-auth-script.c b/dbus/dbus-auth-script.c index 2ed2af37..f32f1c1d 100644 --- a/dbus/dbus-auth-script.c +++ b/dbus/dbus-auth-script.c @@ -29,7 +29,6 @@ #include "dbus-string.h" #include "dbus-hash.h" #include "dbus-internals.h" -#include "dbus-marshal.h" #include "dbus-userdb.h" /** diff --git a/dbus/dbus-bus.c b/dbus/dbus-bus.c index be296fc7..bc1750b6 100644 --- a/dbus/dbus-bus.c +++ b/dbus/dbus-bus.c @@ -479,8 +479,13 @@ dbus_bus_register (DBusConnection *connection, DBUS_TYPE_INVALID)) goto out; - bd->base_service = name; - + bd->base_service = _dbus_strdup (name); + if (bd->base_service == NULL) + { + _DBUS_SET_OOM (error); + goto out; + } + retval = TRUE; out: @@ -578,7 +583,7 @@ dbus_bus_get_unix_user (DBusConnection *connection, } if (!dbus_message_append_args (message, - DBUS_TYPE_STRING, service, + DBUS_TYPE_STRING, &service, DBUS_TYPE_INVALID)) { dbus_message_unref (message); @@ -659,8 +664,8 @@ dbus_bus_acquire_service (DBusConnection *connection, } if (!dbus_message_append_args (message, - DBUS_TYPE_STRING, service_name, - DBUS_TYPE_UINT32, flags, + DBUS_TYPE_STRING, &service_name, + DBUS_TYPE_UINT32, &flags, DBUS_TYPE_INVALID)) { dbus_message_unref (message); @@ -731,7 +736,7 @@ dbus_bus_service_exists (DBusConnection *connection, } if (!dbus_message_append_args (message, - DBUS_TYPE_STRING, service_name, + DBUS_TYPE_STRING, &service_name, DBUS_TYPE_INVALID)) { dbus_message_unref (message); @@ -791,8 +796,8 @@ dbus_bus_activate_service (DBusConnection *connection, DBUS_INTERFACE_ORG_FREEDESKTOP_DBUS, "ActivateService"); - if (!dbus_message_append_args (msg, DBUS_TYPE_STRING, service_name, - DBUS_TYPE_UINT32, flags, DBUS_TYPE_INVALID)) + if (!dbus_message_append_args (msg, DBUS_TYPE_STRING, &service_name, + DBUS_TYPE_UINT32, &flags, DBUS_TYPE_INVALID)) { dbus_message_unref (msg); _DBUS_SET_OOM (error); @@ -895,7 +900,7 @@ dbus_bus_add_match (DBusConnection *connection, return; } - if (!dbus_message_append_args (msg, DBUS_TYPE_STRING, rule, + if (!dbus_message_append_args (msg, DBUS_TYPE_STRING, &rule, DBUS_TYPE_INVALID)) { dbus_message_unref (msg); @@ -933,7 +938,7 @@ dbus_bus_remove_match (DBusConnection *connection, DBUS_INTERFACE_ORG_FREEDESKTOP_DBUS, "RemoveMatch"); - if (!dbus_message_append_args (msg, DBUS_TYPE_STRING, rule, + if (!dbus_message_append_args (msg, DBUS_TYPE_STRING, &rule, DBUS_TYPE_INVALID)) { dbus_message_unref (msg); diff --git a/dbus/dbus-connection.c b/dbus/dbus-connection.c index 41ca0a77..197d186c 100644 --- a/dbus/dbus-connection.c +++ b/dbus/dbus-connection.c @@ -38,7 +38,6 @@ #include "dbus-string.h" #include "dbus-pending-call.h" #include "dbus-object-tree.h" -#include "dbus-marshal.h" #if 0 #define CONNECTION_LOCK(connection) do { \ @@ -1562,15 +1561,6 @@ _dbus_connection_send_preallocated_unlocked (DBusConnection *connection, connection->n_outgoing += 1; sig = dbus_message_get_signature (message); -#ifndef DBUS_DISABLE_ASSERT - { - DBusString foo; - _dbus_verbose (" validating signature '%s'\n", sig); - _dbus_string_init_const (&foo, sig); - _dbus_assert (_dbus_string_validate_signature (&foo, 0, - _dbus_string_get_length (&foo))); - } -#endif _dbus_verbose ("Message %p (%d %s %s '%s') added to outgoing queue %p, %d pending to send\n", message, diff --git a/dbus/dbus-internals.c b/dbus/dbus-internals.c index 6d2395fd..2aa26805 100644 --- a/dbus/dbus-internals.c +++ b/dbus/dbus-internals.c @@ -354,42 +354,6 @@ _dbus_string_array_contains (const char **array, return FALSE; } -/** - * Returns a string describing the given type. - * - * @param type the type to describe - * @returns a constant string describing the type - */ -const char * -_dbus_type_to_string (int type) -{ - switch (type) - { - case DBUS_TYPE_INVALID: - return "invalid"; - case DBUS_TYPE_NIL: - return "nil"; - case DBUS_TYPE_BOOLEAN: - return "boolean"; - case DBUS_TYPE_INT32: - return "int32"; - case DBUS_TYPE_UINT32: - return "uint32"; - case DBUS_TYPE_DOUBLE: - return "double"; - case DBUS_TYPE_STRING: - return "string"; - case DBUS_TYPE_CUSTOM: - return "custom"; - case DBUS_TYPE_ARRAY: - return "array"; - case DBUS_TYPE_DICT: - return "dict"; - default: - return "unknown"; - } -} - /** * Returns a string describing the given name. * @@ -525,6 +489,9 @@ _dbus_test_oom_handling (const char *description, void *data) { int approx_mallocs; + const char *setting; + int max_failures_to_try; + int i; /* Run once to see about how many mallocs are involved */ @@ -540,21 +507,30 @@ _dbus_test_oom_handling (const char *description, _dbus_verbose ("\n=================\n%s: about %d mallocs total\n=================\n", description, approx_mallocs); - _dbus_set_fail_alloc_failures (1); - if (!run_failing_each_malloc (approx_mallocs, description, func, data)) - return FALSE; - - _dbus_set_fail_alloc_failures (2); - if (!run_failing_each_malloc (approx_mallocs, description, func, data)) - return FALSE; - - _dbus_set_fail_alloc_failures (3); - if (!run_failing_each_malloc (approx_mallocs, description, func, data)) - return FALSE; + setting = _dbus_getenv ("DBUS_TEST_MALLOC_FAILURES"); + if (setting != NULL) + { + DBusString str; + long v; + _dbus_string_init_const (&str, setting); + v = 4; + if (!_dbus_string_parse_int (&str, 0, &v, NULL)) + _dbus_warn ("couldn't parse '%s' as integer\n", setting); + max_failures_to_try = v; + } + else + { + max_failures_to_try = 4; + } - _dbus_set_fail_alloc_failures (4); - if (!run_failing_each_malloc (approx_mallocs, description, func, data)) - return FALSE; + i = setting ? max_failures_to_try - 1 : 1; + while (i < max_failures_to_try) + { + _dbus_set_fail_alloc_failures (i); + if (!run_failing_each_malloc (approx_mallocs, description, func, data)) + return FALSE; + ++i; + } _dbus_verbose ("\n=================\n%s: all iterations passed\n=================\n", description); diff --git a/dbus/dbus-internals.h b/dbus/dbus-internals.h index 8560d4ec..d1c65615 100644 --- a/dbus/dbus-internals.h +++ b/dbus/dbus-internals.h @@ -170,6 +170,7 @@ extern const char _dbus_return_if_fail_warning_format[]; #define _DBUS_ALIGN_ADDRESS(this, boundary) \ ((void*)_DBUS_ALIGN_VALUE(this, boundary)) + char* _dbus_strdup (const char *str); void* _dbus_memdup (const void *mem, size_t n_bytes); @@ -207,13 +208,12 @@ dbus_bool_t _dbus_set_fd_nonblocking (int fd, DBusError *error); void _dbus_verbose_bytes (const unsigned char *data, - int len); + int len, + int offset); void _dbus_verbose_bytes_of_string (const DBusString *str, int start, int len); - -const char* _dbus_type_to_string (int type); const char* _dbus_header_field_to_string (int header_field); extern const char _dbus_no_memory_message[]; diff --git a/dbus/dbus-mainloop.c b/dbus/dbus-mainloop.c index 99369d48..b3089bbc 100644 --- a/dbus/dbus-mainloop.c +++ b/dbus/dbus-mainloop.c @@ -30,7 +30,7 @@ #define MAINLOOP_SPEW 0 -#ifdef MAINLOOP_SPEW +#if MAINLOOP_SPEW #ifdef DBUS_ENABLE_VERBOSE_MODE static const char* watch_flags_to_string (int flags) diff --git a/dbus/dbus-marshal-basic.c b/dbus/dbus-marshal-basic.c index 11ebfaf1..7504019b 100644 --- a/dbus/dbus-marshal-basic.c +++ b/dbus/dbus-marshal-basic.c @@ -150,6 +150,7 @@ swap_8_octets (DBusBasicValue *value, } } +#if 0 static DBusBasicValue unpack_8_octets (int byte_order, const unsigned char *data) @@ -171,6 +172,7 @@ unpack_8_octets (int byte_order, return r; } +#endif /** * Unpacks a 32 bit unsigned integer from a data pointer @@ -404,10 +406,12 @@ _dbus_marshal_set_basic (DBusString *str, break; case DBUS_TYPE_STRING: case DBUS_TYPE_OBJECT_PATH: + _dbus_assert (vp->str != NULL); return set_string (str, pos, vp->str, byte_order, old_end_pos, new_end_pos); break; case DBUS_TYPE_SIGNATURE: + _dbus_assert (vp->str != NULL); return set_signature (str, pos, vp->str, byte_order, old_end_pos, new_end_pos); break; @@ -429,6 +433,8 @@ read_4_octets (const DBusString *str, if (new_pos) *new_pos = pos + 4; + _dbus_assert (pos + 4 <= _dbus_string_get_length (str)); + return unpack_4_octets (byte_order, _dbus_string_get_const_data (str) + pos); } @@ -554,52 +560,52 @@ _dbus_marshal_read_basic (const DBusString *str, } /** - * Reads an array of fixed-length basic values. Does not work for - * arrays of string or container types. + * Reads a block of fixed-length basic values, as an optimization + * vs. reading each one individually into a new buffer. * - * This function returns the array in-place; it does not make a copy, + * This function returns the data in-place; it does not make a copy, * and it does not swap the bytes. * * If you ask for #DBUS_TYPE_DOUBLE you will get a "const double*" back * and the "value" argument should be a "const double**" and so on. * - * @todo last I checked only the test suite uses this function - * + * @todo we aren't using this function (except in the test suite) + * * @param str the string to read from * @param pos position to read from * @param element_type type of array elements * @param value place to return the array - * @param n_elements place to return number of array elements + * @param n_elements number of array elements to read * @param byte_order the byte order, used to read the array length * @param new_pos #NULL or location to store a position after the elements */ void -_dbus_marshal_read_fixed_array (const DBusString *str, +_dbus_marshal_read_fixed_multi (const DBusString *str, int pos, int element_type, void *value, - int *n_elements, + int n_elements, int byte_order, int *new_pos) { - dbus_uint32_t array_len; + int array_len; int alignment; _dbus_assert (_dbus_type_is_fixed (element_type)); _dbus_assert (_dbus_type_is_basic (element_type)); - pos = _DBUS_ALIGN_VALUE (pos, 4); - - array_len = _dbus_marshal_read_uint32 (str, pos, byte_order, &pos); - +#if 0 + _dbus_verbose ("reading %d elements of %s\n", + n_elements, _dbus_type_to_string (element_type)); +#endif + alignment = _dbus_type_get_alignment (element_type); pos = _DBUS_ALIGN_VALUE (pos, alignment); + + array_len = n_elements * alignment; *(const DBusBasicValue**) value = (void*) _dbus_string_get_const_data_len (str, pos, array_len); - - *n_elements = array_len / alignment; - if (new_pos) *new_pos = pos + array_len; } @@ -682,16 +688,12 @@ marshal_len_followed_by_bytes (int marshal_as, if (insert_at > _dbus_string_get_length (str)) _dbus_warn ("insert_at = %d string len = %d data_len = %d\n", insert_at, _dbus_string_get_length (str), data_len); - + if (marshal_as == MARSHAL_AS_BYTE_ARRAY) value_len = data_len; else value_len = data_len + 1; /* value has a nul */ - /* FIXME this is probably broken for byte arrays because - * DBusString wants strings to be nul-terminated? - * Maybe I planned on this when writing init_const_len though - */ _dbus_string_init_const_len (&value_str, value, value_len); pos = insert_at; @@ -814,9 +816,11 @@ _dbus_marshal_write_basic (DBusString *str, case DBUS_TYPE_STRING: case DBUS_TYPE_OBJECT_PATH: + _dbus_assert (vp->str != NULL); return marshal_string (str, insert_at, vp->str, byte_order, pos_after); break; case DBUS_TYPE_SIGNATURE: + _dbus_assert (vp->str != NULL); return marshal_signature (str, insert_at, vp->str, pos_after); break; default: @@ -834,9 +838,23 @@ marshal_1_octets_array (DBusString *str, int byte_order, int *pos_after) { - return marshal_len_followed_by_bytes (MARSHAL_AS_BYTE_ARRAY, - str, insert_at, value, n_elements, - byte_order, pos_after); + int pos; + DBusString value_str; + + _dbus_string_init_const_len (&value_str, value, n_elements); + + pos = insert_at; + + if (!_dbus_string_copy_len (&value_str, 0, n_elements, + str, pos)) + return FALSE; + + pos += n_elements; + + if (pos_after) + *pos_after = pos; + + return TRUE; } static void @@ -885,7 +903,7 @@ swap_array (DBusString *str, } static dbus_bool_t -marshal_fixed_array (DBusString *str, +marshal_fixed_multi (DBusString *str, int insert_at, const DBusBasicValue *value, int n_elements, @@ -896,18 +914,15 @@ marshal_fixed_array (DBusString *str, int old_string_len; int array_start; DBusString t; + int len_in_bytes; + _dbus_assert (n_elements <= DBUS_MAXIMUM_ARRAY_LENGTH / alignment); + old_string_len = _dbus_string_get_length (str); - /* The array length is the length in bytes of the array, - * *excluding* alignment padding. - */ - if (!marshal_4_octets (str, insert_at, n_elements * alignment, - byte_order, &array_start)) - goto error; - - _dbus_verbose ("marshaled len %d at %d array start %d\n", n_elements * alignment, insert_at, array_start); - + len_in_bytes = n_elements * alignment; + array_start = insert_at; + /* Note that we do alignment padding unconditionally * even if the array is empty; this means that * padding + len is always equal to the number of bytes @@ -919,7 +934,7 @@ marshal_fixed_array (DBusString *str, _dbus_string_init_const_len (&t, (const unsigned char*) value, - n_elements * alignment); + len_in_bytes); if (!_dbus_string_copy (&t, 0, str, array_start)) @@ -927,6 +942,9 @@ marshal_fixed_array (DBusString *str, swap_array (str, array_start, n_elements, byte_order, alignment); + if (pos_after) + *pos_after = array_start + len_in_bytes; + return TRUE; error: @@ -937,9 +955,9 @@ marshal_fixed_array (DBusString *str, } /** - * Marshals an array of values of fixed-length type. - * _dbus_type_is_fixed() returns #TRUE for these types, - * which are the basic types minus the string-like types. + * Marshals a block of values of fixed-length type all at once, as an + * optimization. _dbus_type_is_fixed() returns #TRUE for fixed-length + * types, which are the basic types minus the string-like types. * * The value argument should be the adddress of an * array, so e.g. "const dbus_uint32_t**" @@ -948,13 +966,13 @@ marshal_fixed_array (DBusString *str, * @param insert_at where to insert the value * @param element_type type of array elements * @param value address of an array to marshal - * @param len number of elements in the array + * @param n_elements number of elements in the array * @param byte_order byte order * @param pos_after #NULL or the position after the type * @returns #TRUE on success **/ dbus_bool_t -_dbus_marshal_write_fixed_array (DBusString *str, +_dbus_marshal_write_fixed_multi (DBusString *str, int insert_at, int element_type, const void *value, @@ -963,9 +981,15 @@ _dbus_marshal_write_fixed_array (DBusString *str, int *pos_after) { const void* vp = *(const DBusBasicValue**)value; - + _dbus_assert (_dbus_type_is_fixed (element_type)); + _dbus_assert (n_elements >= 0); +#if 0 + _dbus_verbose ("writing %d elements of %s\n", + n_elements, _dbus_type_to_string (element_type)); +#endif + switch (element_type) { case DBUS_TYPE_BOOLEAN: @@ -976,12 +1000,12 @@ _dbus_marshal_write_fixed_array (DBusString *str, break; case DBUS_TYPE_INT32: case DBUS_TYPE_UINT32: - return marshal_fixed_array (str, insert_at, vp, n_elements, byte_order, 4, pos_after); + return marshal_fixed_multi (str, insert_at, vp, n_elements, byte_order, 4, pos_after); break; case DBUS_TYPE_INT64: case DBUS_TYPE_UINT64: case DBUS_TYPE_DOUBLE: - return marshal_fixed_array (str, insert_at, vp, n_elements, byte_order, 8, pos_after); + return marshal_fixed_multi (str, insert_at, vp, n_elements, byte_order, 8, pos_after); break; default: @@ -1008,6 +1032,9 @@ _dbus_marshal_skip_basic (const DBusString *str, int byte_order, int *pos) { + _dbus_assert (byte_order == DBUS_LITTLE_ENDIAN || + byte_order == DBUS_BIG_ENDIAN); + switch (type) { case DBUS_TYPE_BYTE: @@ -1031,7 +1058,7 @@ _dbus_marshal_skip_basic (const DBusString *str, int len; len = _dbus_marshal_read_uint32 (str, *pos, byte_order, pos); - + *pos += len + 1; /* length plus nul */ } break; @@ -1231,6 +1258,50 @@ _dbus_type_is_fixed (int typecode) } } +/** + * Returns a string describing the given type. + * + * @param typecode the type to describe + * @returns a constant string describing the type + */ +const char * +_dbus_type_to_string (int typecode) +{ + switch (typecode) + { + case DBUS_TYPE_INVALID: + return "invalid"; + case DBUS_TYPE_BOOLEAN: + return "boolean"; + case DBUS_TYPE_BYTE: + return "byte"; + case DBUS_TYPE_INT32: + return "int32"; + case DBUS_TYPE_UINT32: + return "uint32"; + case DBUS_TYPE_DOUBLE: + return "double"; + case DBUS_TYPE_STRING: + return "string"; + case DBUS_TYPE_OBJECT_PATH: + return "object_path"; + case DBUS_TYPE_SIGNATURE: + return "signature"; + case DBUS_TYPE_STRUCT: + return "struct"; + case DBUS_TYPE_ARRAY: + return "array"; + case DBUS_TYPE_VARIANT: + return "variant"; + case DBUS_STRUCT_BEGIN_CHAR: + return "begin_struct"; + case DBUS_STRUCT_END_CHAR: + return "end_struct"; + default: + return "unknown"; + } +} + /** * If in verbose mode, print a block of binary data. * @@ -1367,6 +1438,10 @@ swap_test_array (void *array, int alignment) { DBusString t; + + if (alignment == 1) + return; + _dbus_string_init_const_len (&t, array, len_bytes); swap_array (&t, 0, len_bytes / alignment, byte_order, alignment); } @@ -1420,8 +1495,13 @@ swap_test_array (void *array, #define MARSHAL_FIXED_ARRAY(typename, byte_order, literal) \ do { \ + int next; \ + v_UINT32 = sizeof(literal); \ + if (!_dbus_marshal_write_basic (&str, pos, DBUS_TYPE_UINT32, &v_UINT32, \ + byte_order, &next)) \ + _dbus_assert_not_reached ("no memory"); \ v_ARRAY_##typename = literal; \ - if (!_dbus_marshal_write_fixed_array (&str, pos, DBUS_TYPE_##typename, \ + if (!_dbus_marshal_write_fixed_multi (&str, next, DBUS_TYPE_##typename, \ &v_ARRAY_##typename, _DBUS_N_ELEMENTS(literal), \ byte_order, NULL)) \ _dbus_assert_not_reached ("no memory"); \ @@ -1429,10 +1509,14 @@ swap_test_array (void *array, #define DEMARSHAL_FIXED_ARRAY(typename, byte_order) \ do { \ - _dbus_marshal_read_fixed_array (&str, pos, DBUS_TYPE_##typename, &v_ARRAY_##typename, \ - &n_elements, byte_order, &pos); \ - swap_test_array (v_ARRAY_##typename, n_elements * sizeof(v_ARRAY_##typename[0]), \ - byte_order, sizeof(v_ARRAY_##typename[0])); \ + int next; \ + alignment = _dbus_type_get_alignment (DBUS_TYPE_##typename); \ + v_UINT32 = _dbus_marshal_read_uint32 (&str, dump_pos, byte_order, &next); \ + _dbus_marshal_read_fixed_multi (&str, next, DBUS_TYPE_##typename, &v_ARRAY_##typename, \ + v_UINT32/alignment, \ + byte_order, NULL); \ + swap_test_array (v_ARRAY_##typename, v_UINT32, \ + byte_order, alignment); \ } while (0) #define DEMARSHAL_FIXED_ARRAY_AND_CHECK(typename, byte_order, literal) \ @@ -1461,9 +1545,10 @@ swap_test_array (void *array, dbus_bool_t _dbus_marshal_test (void) { + int alignment; DBusString str; int pos, dump_pos; - int n_elements; + unsigned char array1[5] = { 3, 4, 0, 1, 9 }; dbus_int32_t array4[3] = { 123, 456, 789 }; #ifdef DBUS_HAVE_INT64 dbus_int64_t array8[3] = { DBUS_INT64_CONSTANT (0x123ffffffff), @@ -1471,6 +1556,7 @@ _dbus_marshal_test (void) DBUS_INT64_CONSTANT (0x789ffffffff) }; dbus_int64_t *v_ARRAY_INT64; #endif + unsigned char *v_ARRAY_BYTE; dbus_int32_t *v_ARRAY_INT32; double *v_ARRAY_DOUBLE; DBusString t; @@ -1553,6 +1639,9 @@ _dbus_marshal_test (void) MARSHAL_TEST_FIXED_ARRAY (INT32, DBUS_BIG_ENDIAN, array4); MARSHAL_TEST_FIXED_ARRAY (INT32, DBUS_LITTLE_ENDIAN, array4); + MARSHAL_TEST_FIXED_ARRAY (BYTE, DBUS_BIG_ENDIAN, array1); + MARSHAL_TEST_FIXED_ARRAY (BYTE, DBUS_LITTLE_ENDIAN, array1); + #ifdef DBUS_HAVE_INT64 MARSHAL_TEST_FIXED_ARRAY (INT64, DBUS_BIG_ENDIAN, array8); MARSHAL_TEST_FIXED_ARRAY (INT64, DBUS_LITTLE_ENDIAN, array8); @@ -1675,16 +1764,18 @@ _dbus_marshal_test (void) _dbus_string_get_const_data (&str))); /* unsigned little */ - _dbus_marshal_set_uint32 (&str, DBUS_LITTLE_ENDIAN, - 0, 0x123456); + _dbus_marshal_set_uint32 (&str, + 0, 0x123456, + DBUS_LITTLE_ENDIAN); _dbus_assert (0x123456 == _dbus_unpack_uint32 (DBUS_LITTLE_ENDIAN, _dbus_string_get_const_data (&str))); /* unsigned big */ - _dbus_marshal_set_uint32 (&str, DBUS_BIG_ENDIAN, - 0, 0x123456); + _dbus_marshal_set_uint32 (&str, + 0, 0x123456, + DBUS_BIG_ENDIAN); _dbus_assert (0x123456 == _dbus_unpack_uint32 (DBUS_BIG_ENDIAN, diff --git a/dbus/dbus-marshal-basic.h b/dbus/dbus-marshal-basic.h index 72ae2834..ddfce18e 100644 --- a/dbus/dbus-marshal-basic.h +++ b/dbus/dbus-marshal-basic.h @@ -35,120 +35,6 @@ #error "config.h not included here" #endif -/****************************************************** Remove later */ -#undef DBUS_TYPE_INVALID -#undef DBUS_TYPE_NIL -#undef DBUS_TYPE_CUSTOM -#undef DBUS_TYPE_BYTE -#undef DBUS_TYPE_INT32 -#undef DBUS_TYPE_UINT32 -#undef DBUS_TYPE_INT64 -#undef DBUS_TYPE_UINT64 -#undef DBUS_TYPE_DOUBLE -#undef DBUS_TYPE_STRING -#undef DBUS_TYPE_OBJECT_PATH -#undef DBUS_TYPE_ARRAY -#undef DBUS_TYPE_DICT -#undef DBUS_TYPE_VARIANT -#undef DBUS_TYPE_STRUCT -#undef DBUS_NUMBER_OF_TYPES - - -/* Never a legitimate type */ -#define DBUS_TYPE_INVALID ((int) '\0') -#define DBUS_TYPE_INVALID_AS_STRING "\0" - -/* Primitive types */ -#define DBUS_TYPE_BYTE ((int) 'y') -#define DBUS_TYPE_BYTE_AS_STRING "y" -#define DBUS_TYPE_BOOLEAN ((int) 'b') -#define DBUS_TYPE_BOOLEAN_AS_STRING "b" -#define DBUS_TYPE_INT32 ((int) 'i') -#define DBUS_TYPE_INT32_AS_STRING "i" - -#define DBUS_TYPE_UINT32 ((int) 'u') -#define DBUS_TYPE_UINT32_AS_STRING "u" -#define DBUS_TYPE_INT64 ((int) 'x') -#define DBUS_TYPE_INT64_AS_STRING "x" -#define DBUS_TYPE_UINT64 ((int) 't') -#define DBUS_TYPE_UINT64_AS_STRING "t" - -#define DBUS_TYPE_DOUBLE ((int) 'd') -#define DBUS_TYPE_DOUBLE_AS_STRING "d" -#define DBUS_TYPE_STRING ((int) 's') -#define DBUS_TYPE_STRING_AS_STRING "s" -#define DBUS_TYPE_OBJECT_PATH ((int) 'o') -#define DBUS_TYPE_OBJECT_PATH_AS_STRING "o" -#define DBUS_TYPE_SIGNATURE ((int) 'g') -#define DBUS_TYPE_SIGNATURE_AS_STRING "g" - -/* Compound types */ -#define DBUS_TYPE_ARRAY ((int) 'a') -#define DBUS_TYPE_ARRAY_AS_STRING "a" -#define DBUS_TYPE_VARIANT ((int) 'v') -#define DBUS_TYPE_VARIANT_AS_STRING "v" - -/* STRUCT is sort of special since its code can't appear in a type string, - * instead DBUS_STRUCT_BEGIN_CHAR has to appear - */ -#define DBUS_TYPE_STRUCT ((int) 'r') -#define DBUS_TYPE_STRUCT_AS_STRING "r" - -/* Does not count INVALID */ -#define DBUS_NUMBER_OF_TYPES (13) - -/* characters other than typecodes that appear in type signatures */ -#define DBUS_STRUCT_BEGIN_CHAR ((int) '(') -#define DBUS_STRUCT_BEGIN_CHAR_AS_STRING "(" -#define DBUS_STRUCT_END_CHAR ((int) ')') -#define DBUS_STRUCT_END_CHAR_AS_STRING ")" - -#define DBUS_MAXIMUM_SIGNATURE_LENGTH 255 -#define DBUS_MAXIMUM_ARRAY_LENGTH (67108864) -#define DBUS_MAXIMUM_ARRAY_LENGTH_BITS 26 -#define DBUS_MAXIMUM_MESSAGE_LENGTH (DBUS_MAXIMUM_ARRAY_LENGTH * 2) -#define DBUS_MAXIMUM_MESSAGE_LENGTH_BITS 27 - -static const char * -_hack_dbus_type_to_string (int type) -{ - switch (type) - { - case DBUS_TYPE_INVALID: - return "invalid"; - case DBUS_TYPE_BOOLEAN: - return "boolean"; - case DBUS_TYPE_INT32: - return "int32"; - case DBUS_TYPE_UINT32: - return "uint32"; - case DBUS_TYPE_DOUBLE: - return "double"; - case DBUS_TYPE_STRING: - return "string"; - case DBUS_TYPE_OBJECT_PATH: - return "object_path"; - case DBUS_TYPE_SIGNATURE: - return "signature"; - case DBUS_TYPE_STRUCT: - return "struct"; - case DBUS_TYPE_ARRAY: - return "array"; - case DBUS_TYPE_VARIANT: - return "variant"; - case DBUS_STRUCT_BEGIN_CHAR: - return "begin_struct"; - case DBUS_STRUCT_END_CHAR: - return "end_struct"; - default: - return "unknown"; - } -} - -#define _dbus_type_to_string(t) _hack_dbus_type_to_string(t) - -/****************************************************** Remove later */ - #ifdef WORDS_BIGENDIAN #define DBUS_COMPILER_BYTE_ORDER DBUS_BIG_ENDIAN #else @@ -275,7 +161,7 @@ dbus_bool_t _dbus_marshal_write_basic (DBusString *str, const void *value, int byte_order, int *pos_after); -dbus_bool_t _dbus_marshal_write_fixed_array (DBusString *str, +dbus_bool_t _dbus_marshal_write_fixed_multi (DBusString *str, int insert_at, int element_type, const void *value, @@ -288,11 +174,11 @@ void _dbus_marshal_read_basic (const DBusString *str, void *value, int byte_order, int *new_pos); -void _dbus_marshal_read_fixed_array (const DBusString *str, +void _dbus_marshal_read_fixed_multi (const DBusString *str, int pos, int element_type, void *value, - int *n_elements, + int n_elements, int byte_order, int *new_pos); void _dbus_marshal_skip_basic (const DBusString *str, @@ -316,7 +202,7 @@ int _dbus_type_get_alignment (int typecode); dbus_bool_t _dbus_type_is_basic (int typecode); dbus_bool_t _dbus_type_is_container (int typecode); dbus_bool_t _dbus_type_is_fixed (int typecode); - +const char* _dbus_type_to_string (int typecode); diff --git a/dbus/dbus-marshal-header.c b/dbus/dbus-marshal-header.c new file mode 100644 index 00000000..34940429 --- /dev/null +++ b/dbus/dbus-marshal-header.c @@ -0,0 +1,1435 @@ +/* -*- mode: C; c-file-style: "gnu" -*- */ +/* dbus-marshal-header.c Managing marshaling/demarshaling of message headers + * + * Copyright (C) 2005 Red Hat, Inc. + * + * Licensed under the Academic Free License version 2.1 + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program 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 General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * + */ + +#include "dbus-marshal-header.h" +#include "dbus-marshal-recursive.h" + +/** + * @addtogroup DBusMarshal + * + * @{ + */ + + +/* Not thread locked, but strictly const/read-only so should be OK + */ +_DBUS_STRING_DEFINE_STATIC(_dbus_header_signature_str, DBUS_HEADER_SIGNATURE); +_DBUS_STRING_DEFINE_STATIC(_dbus_local_interface_str, DBUS_INTERFACE_ORG_FREEDESKTOP_LOCAL); +_DBUS_STRING_DEFINE_STATIC(_dbus_local_path_str, DBUS_PATH_ORG_FREEDESKTOP_LOCAL); + +#define FIELDS_ARRAY_SIGNATURE_OFFSET 6 +#define FIELDS_ARRAY_ELEMENT_SIGNATURE_OFFSET 7 + + +/** Offset to byte order from start of header */ +#define BYTE_ORDER_OFFSET 0 +/** Offset to type from start of header */ +#define TYPE_OFFSET 1 +/** Offset to flags from start of header */ +#define FLAGS_OFFSET 2 +/** Offset to version from start of header */ +#define VERSION_OFFSET 3 +/** Offset to body length from start of header */ +#define BODY_LENGTH_OFFSET 4 +/** Offset to client serial from start of header */ +#define SERIAL_OFFSET 8 +/** Offset to fields array length from start of header */ +#define FIELDS_ARRAY_LENGTH_OFFSET 12 +/** Offset to first field in header */ +#define FIRST_FIELD_OFFSET 16 + +typedef struct +{ + unsigned char code; + unsigned char type; +} HeaderFieldType; + +static const HeaderFieldType +_dbus_header_field_types[DBUS_HEADER_FIELD_LAST+1] = { + { DBUS_HEADER_FIELD_INVALID, DBUS_TYPE_INVALID }, + { DBUS_HEADER_FIELD_PATH, DBUS_TYPE_OBJECT_PATH }, + { DBUS_HEADER_FIELD_INTERFACE, DBUS_TYPE_STRING }, + { DBUS_HEADER_FIELD_MEMBER, DBUS_TYPE_STRING }, + { DBUS_HEADER_FIELD_ERROR_NAME, DBUS_TYPE_STRING }, + { DBUS_HEADER_FIELD_REPLY_SERIAL, DBUS_TYPE_UINT32 }, + { DBUS_HEADER_FIELD_DESTINATION, DBUS_TYPE_STRING }, + { DBUS_HEADER_FIELD_SENDER, DBUS_TYPE_STRING }, + { DBUS_HEADER_FIELD_SIGNATURE, DBUS_TYPE_SIGNATURE } +}; + +#define EXPECTED_TYPE_OF_FIELD(field) (_dbus_header_field_types[field].type) + +#define MAX_POSSIBLE_HEADER_PADDING 7 +static dbus_bool_t +reserve_header_padding (DBusHeader *header) +{ + _dbus_assert (header->padding <= MAX_POSSIBLE_HEADER_PADDING); + + if (!_dbus_string_lengthen (&header->data, + MAX_POSSIBLE_HEADER_PADDING - header->padding)) + return FALSE; + header->padding = MAX_POSSIBLE_HEADER_PADDING; + return TRUE; +} + +static void +correct_header_padding (DBusHeader *header) +{ + int unpadded_len; + + _dbus_assert (header->padding == 7); + + _dbus_string_shorten (&header->data, header->padding); + unpadded_len = _dbus_string_get_length (&header->data); + + if (!_dbus_string_align_length (&header->data, 8)) + _dbus_assert_not_reached ("couldn't pad header though enough padding was preallocated"); + + header->padding = _dbus_string_get_length (&header->data) - unpadded_len; +} + +#define HEADER_END_BEFORE_PADDING(header) \ + (_dbus_string_get_length (&(header)->data) - (header)->padding) + +/** + * Invalidates all fields in the cache. This may be used when the + * cache is totally uninitialized (contains junk) so should not + * look at what's in there now. + * + * @param header the header + */ +static void +_dbus_header_cache_invalidate_all (DBusHeader *header) +{ + int i; + + i = 0; + while (i <= DBUS_HEADER_FIELD_LAST) + { + header->fields[i].value_pos = _DBUS_HEADER_FIELD_VALUE_UNKNOWN; + ++i; + } +} + +/** + * Caches one field + * + * @param header the header + * @param field_code the field + * @param variant_reader the reader for the variant in the field + */ +static void +_dbus_header_cache_one (DBusHeader *header, + int field_code, + DBusTypeReader *variant_reader) +{ + int variant_type; + + variant_type = _dbus_type_reader_get_current_type (variant_reader); + + header->fields[field_code].value_pos = + _dbus_type_reader_get_value_pos (variant_reader); + +#if 0 + _dbus_verbose ("cached value_pos %d for field %d\n", + header->fields[field_code].value_pos, field_code) +#endif +} + +/** + * Revalidates the fields cache + * + * @param header the header + */ +static void +_dbus_header_cache_revalidate (DBusHeader *header) +{ + DBusTypeReader array; + DBusTypeReader reader; + int i; + + i = 0; + while (i <= DBUS_HEADER_FIELD_LAST) + { + header->fields[i].value_pos = _DBUS_HEADER_FIELD_VALUE_NONEXISTENT; + ++i; + } + + _dbus_type_reader_init (&reader, + header->byte_order, + &_dbus_header_signature_str, + FIELDS_ARRAY_SIGNATURE_OFFSET, + &header->data, + FIELDS_ARRAY_LENGTH_OFFSET); + + _dbus_type_reader_recurse (&reader, &array); + + while (_dbus_type_reader_get_current_type (&array) != DBUS_TYPE_INVALID) + { + DBusTypeReader sub; + DBusTypeReader variant; + unsigned char field_code; + + _dbus_type_reader_recurse (&array, &sub); + + _dbus_assert (_dbus_type_reader_get_current_type (&sub) == DBUS_TYPE_BYTE); + _dbus_type_reader_read_basic (&sub, &field_code); + + /* Unknown fields should be ignored */ + if (field_code > DBUS_HEADER_FIELD_LAST) + goto next_field; + + _dbus_type_reader_next (&sub); + + _dbus_assert (_dbus_type_reader_get_current_type (&sub) == DBUS_TYPE_VARIANT); + _dbus_type_reader_recurse (&sub, &variant); + + _dbus_header_cache_one (header, field_code, &variant); + + next_field: + _dbus_type_reader_next (&array); + } +} + +/** + * Checks for a field, updating the cache if required. + * + * @param header the header + * @param field the field to check + * @returns #FALSE if the field doesn't exist + */ +static dbus_bool_t +_dbus_header_cache_check (DBusHeader *header, + int field) +{ + _dbus_assert (field <= DBUS_HEADER_FIELD_LAST); + + if (header->fields[field].value_pos == _DBUS_HEADER_FIELD_VALUE_UNKNOWN) + _dbus_header_cache_revalidate (header); + + if (header->fields[field].value_pos == _DBUS_HEADER_FIELD_VALUE_NONEXISTENT) + return FALSE; + + return TRUE; +} + +/** + * Checks whether a field is known not to exist. It may exist + * even if it's not known to exist. + * + * @param header the header + * @param field the field to check + * @returns #FALSE if the field definitely doesn't exist + */ +static dbus_bool_t +_dbus_header_cache_known_nonexistent (DBusHeader *header, + int field) +{ + _dbus_assert (field <= DBUS_HEADER_FIELD_LAST); + + return (header->fields[field].value_pos == _DBUS_HEADER_FIELD_VALUE_NONEXISTENT); +} + +/** + * Writes a struct of { byte, variant } with the given basic type. + * + * @param writer the writer (should be ready to write a struct) + * @param type the type of the value + * @param value the value as for _dbus_marshal_set_basic() + * @returns #FALSE if no memory + */ +static dbus_bool_t +write_basic_field (DBusTypeWriter *writer, + int field, + int type, + const void *value) +{ + DBusTypeWriter sub; + DBusTypeWriter variant; + int start; + int padding; + unsigned char field_byte; + DBusString contained_type; + char buf[2]; + + start = writer->value_pos; + padding = _dbus_string_get_length (writer->value_str) - start; + + if (!_dbus_type_writer_recurse (writer, DBUS_TYPE_STRUCT, + NULL, 0, &sub)) + goto append_failed; + + field_byte = field; + if (!_dbus_type_writer_write_basic (&sub, DBUS_TYPE_BYTE, + &field_byte)) + goto append_failed; + + buf[0] = type; + buf[1] = '\0'; + _dbus_string_init_const_len (&contained_type, buf, 1); + + if (!_dbus_type_writer_recurse (&sub, DBUS_TYPE_VARIANT, + &contained_type, 0, &variant)) + goto append_failed; + + if (!_dbus_type_writer_write_basic (&variant, type, value)) + goto append_failed; + + if (!_dbus_type_writer_unrecurse (&sub, &variant)) + goto append_failed; + + if (!_dbus_type_writer_unrecurse (writer, &sub)) + goto append_failed; + + return TRUE; + + append_failed: + _dbus_string_delete (writer->value_str, + start, + _dbus_string_get_length (writer->value_str) - start - padding); + return FALSE; +} + +/** + * Sets a struct of { byte, variant } with the given basic type. + * + * @param reader the reader (should be iterating over the array pointing at the field to set) + * @param type the type of the value + * @param value the value as for _dbus_marshal_set_basic() + * @param realign_root where to realign from + * @returns #FALSE if no memory + */ +static dbus_bool_t +set_basic_field (DBusTypeReader *reader, + int field, + int type, + const void *value, + const DBusTypeReader *realign_root) +{ + DBusTypeReader sub; + DBusTypeReader variant; + unsigned char v_BYTE; + + _dbus_type_reader_recurse (reader, &sub); + + _dbus_assert (_dbus_type_reader_get_current_type (&sub) == DBUS_TYPE_BYTE); +#ifndef DBUS_DISABLE_ASSERT + _dbus_type_reader_read_basic (&sub, &v_BYTE); + _dbus_assert (((int) v_BYTE) == field); +#endif + + if (!_dbus_type_reader_next (&sub)) + _dbus_assert_not_reached ("no variant field?"); + + _dbus_type_reader_recurse (&sub, &variant); + _dbus_assert (_dbus_type_reader_get_current_type (&variant) == type); + + if (!_dbus_type_reader_set_basic (&variant, value, realign_root)) + return FALSE; + + return TRUE; +} + +/** + * Gets the type of the message. + * + * @param header the header + * @returns the type + */ +int +_dbus_header_get_message_type (DBusHeader *header) +{ + int type; + + type = _dbus_string_get_byte (&header->data, TYPE_OFFSET); + _dbus_assert (type != DBUS_MESSAGE_TYPE_INVALID); + + return type; +} + +/** + * Sets the serial number of a header. This can only be done once on + * a header. + * + * @param header the header + * @param serial the serial + */ +void +_dbus_header_set_serial (DBusHeader *header, + dbus_uint32_t serial) +{ + /* we use this function to set the serial on outgoing + * messages, and to reset the serial in dbus_message_copy; + * this assertion should catch a double-set on outgoing. + */ + _dbus_assert (_dbus_header_get_serial (header) == 0 || + serial == 0); + + _dbus_marshal_set_uint32 (&header->data, + SERIAL_OFFSET, + serial, + header->byte_order); +} + +/** + * See dbus_message_get_serial() + * + * @param header the header + * @returns the client serial + */ +dbus_uint32_t +_dbus_header_get_serial (DBusHeader *header) +{ + return _dbus_marshal_read_uint32 (&header->data, + SERIAL_OFFSET, + header->byte_order, + NULL); +} + +/** + * Re-initializes a header that was previously initialized and never + * freed. After this, to make the header valid you have to call + * _dbus_header_create(). + * + * @param header header to re-initialize + * @param byte_order byte order of the header + */ +void +_dbus_header_reinit (DBusHeader *header, + int byte_order) +{ + _dbus_string_set_length (&header->data, 0); + + header->byte_order = byte_order; + header->padding = 0; + + _dbus_header_cache_invalidate_all (header); +} + +/** + * Initializes a header, but doesn't prepare it for use; + * to make the header valid, you have to call _dbus_header_create(). + * + * @param header header to initialize + * @param byte_order byte order of the header + * @returns #FALSE if not enough memory + */ +dbus_bool_t +_dbus_header_init (DBusHeader *header, + int byte_order) +{ + if (!_dbus_string_init_preallocated (&header->data, 32)) + return FALSE; + + _dbus_header_reinit (header, byte_order); + + return TRUE; +} + +/** + * Frees a header. + * + * @param header the header + */ +void +_dbus_header_free (DBusHeader *header) +{ + _dbus_string_free (&header->data); +} + +/** + * Initializes dest with a copy of the given header. + * Resets the message serial to 0 on the copy. + * + * @param header header to copy + * @param dest destination for copy + * @returns #FALSE if not enough memory + */ +dbus_bool_t +_dbus_header_copy (const DBusHeader *header, + DBusHeader *dest) +{ + *dest = *header; + + if (!_dbus_string_init_preallocated (&dest->data, + _dbus_string_get_length (&header->data))) + return FALSE; + + if (!_dbus_string_copy (&header->data, 0, &dest->data, 0)) + { + _dbus_string_free (&dest->data); + return FALSE; + } + + /* Reset the serial */ + _dbus_header_set_serial (dest, 0); + + return TRUE; +} + +/** + * Fills in the primary fields of the header, so the header is ready + * for use. #NULL may be specified for some or all of the fields to + * avoid adding those fields. Some combinations of fields don't make + * sense, and passing them in will trigger an assertion failure. + * + * @param header the header + * @param message_type the message type + * @param destination service field or #NULL + * @param path path field or #NULL + * @param interface interface field or #NULL + * @param member member field or #NULL + * @param error_name error name or #NULL + * @returns #FALSE if not enough memory + */ +dbus_bool_t +_dbus_header_create (DBusHeader *header, + int message_type, + const char *destination, + const char *path, + const char *interface, + const char *member, + const char *error_name) +{ + unsigned char v_BYTE; + dbus_uint32_t v_UINT32; + DBusTypeWriter writer; + DBusTypeWriter array; + + _dbus_assert ((interface && member) || + (error_name) || + !(interface || member || error_name)); + _dbus_assert (_dbus_string_get_length (&header->data) == 0); + + if (!reserve_header_padding (header)) + return FALSE; + + _dbus_type_writer_init_values_only (&writer, header->byte_order, + &_dbus_header_signature_str, 0, + &header->data, + HEADER_END_BEFORE_PADDING (header)); + + v_BYTE = header->byte_order; + if (!_dbus_type_writer_write_basic (&writer, DBUS_TYPE_BYTE, + &v_BYTE)) + goto oom; + + v_BYTE = message_type; + if (!_dbus_type_writer_write_basic (&writer, DBUS_TYPE_BYTE, + &v_BYTE)) + goto oom; + + v_BYTE = 0; /* flags */ + if (!_dbus_type_writer_write_basic (&writer, DBUS_TYPE_BYTE, + &v_BYTE)) + goto oom; + + v_BYTE = DBUS_MAJOR_PROTOCOL_VERSION; + if (!_dbus_type_writer_write_basic (&writer, DBUS_TYPE_BYTE, + &v_BYTE)) + goto oom; + + v_UINT32 = 0; /* body length */ + if (!_dbus_type_writer_write_basic (&writer, DBUS_TYPE_UINT32, + &v_UINT32)) + goto oom; + + v_UINT32 = 0; /* serial */ + if (!_dbus_type_writer_write_basic (&writer, DBUS_TYPE_UINT32, + &v_UINT32)) + goto oom; + + if (!_dbus_type_writer_recurse (&writer, DBUS_TYPE_ARRAY, + &_dbus_header_signature_str, + FIELDS_ARRAY_SIGNATURE_OFFSET, + &array)) + goto oom; + + /* Marshal all the fields (Marshall Fields?) */ + + if (path != NULL) + { + if (!write_basic_field (&array, + DBUS_HEADER_FIELD_PATH, + DBUS_TYPE_OBJECT_PATH, + &path)) + goto oom; + } + + if (destination != NULL) + { + if (!write_basic_field (&array, + DBUS_HEADER_FIELD_DESTINATION, + DBUS_TYPE_STRING, + &destination)) + goto oom; + } + + if (interface != NULL) + { + if (!write_basic_field (&array, + DBUS_HEADER_FIELD_INTERFACE, + DBUS_TYPE_STRING, + &interface)) + goto oom; + } + + if (member != NULL) + { + if (!write_basic_field (&array, + DBUS_HEADER_FIELD_MEMBER, + DBUS_TYPE_STRING, + &member)) + goto oom; + } + + if (error_name != NULL) + { + if (!write_basic_field (&array, + DBUS_HEADER_FIELD_ERROR_NAME, + DBUS_TYPE_STRING, + &error_name)) + goto oom; + } + + if (!_dbus_type_writer_unrecurse (&writer, &array)) + goto oom; + + correct_header_padding (header); + + return TRUE; + + oom: + _dbus_string_delete (&header->data, 0, + _dbus_string_get_length (&header->data) - header->padding); + correct_header_padding (header); + + return FALSE; +} + +/** + * Given data long enough to contain the length of the message body + * and the fields array, check whether the data is long enough to + * contain the entire message (assuming the claimed lengths are + * accurate). Also checks that the lengths are in sanity parameters. + * + * @param validity return location for why the data is invalid if it is + * @param byte_order return location for byte order + * @param fields_array_len return location for claimed fields array length + * @param header_len return location for claimed header length + * @param body_len return location for claimed body length + * @param str the data + * @param start start of data, 8-aligned + * @param len length of data + * @returns #TRUE if the data is long enough for the claimed length, and the lengths were valid + */ +dbus_bool_t +_dbus_header_have_message_untrusted (int max_message_length, + DBusValidity *validity, + int *byte_order, + int *fields_array_len, + int *header_len, + int *body_len, + const DBusString *str, + int start, + int len) + +{ + dbus_uint32_t header_len_unsigned; + dbus_uint32_t fields_array_len_unsigned; + dbus_uint32_t body_len_unsigned; + + _dbus_assert (start >= 0); + _dbus_assert (start < _DBUS_INT_MAX / 2); + _dbus_assert (len >= 0); + + _dbus_assert (start == (int) _DBUS_ALIGN_VALUE (start, 8)); + + *byte_order = _dbus_string_get_byte (str, start + BYTE_ORDER_OFFSET); + + if (*byte_order != DBUS_LITTLE_ENDIAN && *byte_order != DBUS_BIG_ENDIAN) + { + *validity = DBUS_INVALID_BAD_BYTE_ORDER; + return FALSE; + } + + _dbus_assert (FIELDS_ARRAY_LENGTH_OFFSET + 4 <= len); + fields_array_len_unsigned = _dbus_marshal_read_uint32 (str, start + FIELDS_ARRAY_LENGTH_OFFSET, + *byte_order, NULL); + + if (fields_array_len_unsigned > (unsigned) max_message_length) + { + *validity = DBUS_INVALID_INSANE_FIELDS_ARRAY_LENGTH; + return FALSE; + } + + _dbus_assert (BODY_LENGTH_OFFSET + 4 < len); + body_len_unsigned = _dbus_marshal_read_uint32 (str, start + BODY_LENGTH_OFFSET, + *byte_order, NULL); + + if (body_len_unsigned > (unsigned) max_message_length) + { + *validity = DBUS_INVALID_INSANE_BODY_LENGTH; + return FALSE; + } + + header_len_unsigned = FIRST_FIELD_OFFSET + fields_array_len_unsigned; + header_len_unsigned = _DBUS_ALIGN_VALUE (header_len_unsigned, 8); + + /* overflow should be impossible since the lengths aren't allowed to + * be huge. + */ + _dbus_assert (max_message_length < _DBUS_INT_MAX / 2); + if (body_len_unsigned + header_len_unsigned > (unsigned) max_message_length) + { + *validity = DBUS_INVALID_MESSAGE_TOO_LONG; + return FALSE; + } + + _dbus_assert (body_len_unsigned < (unsigned) _DBUS_INT_MAX); + _dbus_assert (fields_array_len_unsigned < (unsigned) _DBUS_INT_MAX); + _dbus_assert (header_len_unsigned < (unsigned) _DBUS_INT_MAX); + + *body_len = body_len_unsigned; + *fields_array_len = fields_array_len_unsigned; + *header_len = header_len_unsigned; + + *validity = DBUS_VALID; + + _dbus_verbose ("have %d bytes, need body %u + header %u = %u\n", + len, body_len_unsigned, header_len_unsigned, + body_len_unsigned + header_len_unsigned); + + return (body_len_unsigned + header_len_unsigned) <= (unsigned) len; +} + +static DBusValidity +check_mandatory_fields (DBusHeader *header) +{ +#define REQUIRE_FIELD(name) do { if (header->fields[DBUS_HEADER_FIELD_##name].value_pos < 0) return DBUS_INVALID_MISSING_##name; } while (0) + + switch (_dbus_header_get_message_type (header)) + { + case DBUS_MESSAGE_TYPE_SIGNAL: + REQUIRE_FIELD (INTERFACE); + /* FALL THRU - signals also require the path and member */ + case DBUS_MESSAGE_TYPE_METHOD_CALL: + REQUIRE_FIELD (PATH); + REQUIRE_FIELD (MEMBER); + break; + case DBUS_MESSAGE_TYPE_ERROR: + REQUIRE_FIELD (ERROR_NAME); + REQUIRE_FIELD (REPLY_SERIAL); + break; + case DBUS_MESSAGE_TYPE_METHOD_RETURN: + REQUIRE_FIELD (REPLY_SERIAL); + break; + default: + /* other message types allowed but ignored */ + break; + } + + return DBUS_VALID; +} + +static DBusValidity +load_and_validate_field (DBusHeader *header, + int field, + DBusTypeReader *variant_reader) +{ + int type; + int expected_type; + const DBusString *value_str; + int value_pos; + dbus_uint32_t v_UINT32; + int bad_string_code; + dbus_bool_t (* string_validation_func) (const DBusString *str, + int start, int len); + + /* Supposed to have been checked already */ + _dbus_assert (field <= DBUS_HEADER_FIELD_LAST); + _dbus_assert (field != DBUS_HEADER_FIELD_INVALID); + + /* Before we can cache a field, we need to know it has the right type */ + type = _dbus_type_reader_get_current_type (variant_reader); + + _dbus_assert (_dbus_header_field_types[field].code == field); + + expected_type = EXPECTED_TYPE_OF_FIELD (field); + if (type != expected_type) + { + _dbus_verbose ("Field %d should have type %d but has %d\n", + field, expected_type, type); + return DBUS_INVALID_HEADER_FIELD_HAS_WRONG_TYPE; + } + + /* If the field was provided twice, we aren't happy */ + if (header->fields[field].value_pos >= 0) + { + _dbus_verbose ("Header field %d seen a second time\n", field); + return DBUS_INVALID_HEADER_FIELD_APPEARS_TWICE; + } + + /* Now we can cache and look at the field content */ + _dbus_verbose ("initially caching field %d\n", field); + _dbus_header_cache_one (header, field, variant_reader); + + string_validation_func = NULL; + + /* make compiler happy that all this is initialized */ + v_UINT32 = 0; + value_str = NULL; + value_pos = -1; + bad_string_code = DBUS_VALID; + + if (expected_type == DBUS_TYPE_UINT32) + { + _dbus_header_get_field_basic (header, field, expected_type, + &v_UINT32); + } + else if (expected_type == DBUS_TYPE_STRING || + expected_type == DBUS_TYPE_OBJECT_PATH || + expected_type == DBUS_TYPE_SIGNATURE) + { + _dbus_header_get_field_raw (header, field, + &value_str, &value_pos); + } + else + { + _dbus_assert_not_reached ("none of the known fields should have this type"); + } + + switch (field) + { + case DBUS_HEADER_FIELD_DESTINATION: + string_validation_func = _dbus_validate_service; + bad_string_code = DBUS_INVALID_BAD_DESTINATION; + break; + case DBUS_HEADER_FIELD_INTERFACE: + string_validation_func = _dbus_validate_interface; + bad_string_code = DBUS_INVALID_BAD_INTERFACE; + + if (_dbus_string_equal_substring (&_dbus_local_interface_str, + 0, + _dbus_string_get_length (&_dbus_local_interface_str), + value_str, value_pos)) + { + _dbus_verbose ("Message is on the local interface\n"); + return DBUS_INVALID_USES_LOCAL_INTERFACE; + } + break; + + case DBUS_HEADER_FIELD_MEMBER: + string_validation_func = _dbus_validate_member; + bad_string_code = DBUS_INVALID_BAD_MEMBER; + break; + + case DBUS_HEADER_FIELD_ERROR_NAME: + string_validation_func = _dbus_validate_error_name; + bad_string_code = DBUS_INVALID_BAD_ERROR_NAME; + break; + + case DBUS_HEADER_FIELD_SENDER: + string_validation_func = _dbus_validate_service; + bad_string_code = DBUS_INVALID_BAD_SENDER; + break; + + case DBUS_HEADER_FIELD_PATH: + /* OBJECT_PATH was validated generically due to its type */ + string_validation_func = NULL; + + _dbus_verbose ("value_str %p value_pos %d value_str_len %d\n", + value_str, value_pos, + _dbus_string_get_length (value_str)); + if (_dbus_string_equal_substring (&_dbus_local_path_str, + 0, + _dbus_string_get_length (&_dbus_local_path_str), + value_str, value_pos)) + { + _dbus_verbose ("Message is from the local path\n"); + return DBUS_INVALID_USES_LOCAL_PATH; + } + break; + + case DBUS_HEADER_FIELD_REPLY_SERIAL: + /* Can't be 0 */ + if (v_UINT32 == 0) + { + return DBUS_INVALID_BAD_SERIAL; + } + break; + + case DBUS_HEADER_FIELD_SIGNATURE: + /* SIGNATURE validated generically due to its type */ + string_validation_func = NULL; + break; + + default: + _dbus_assert_not_reached ("unknown field shouldn't be seen here"); + break; + } + + if (string_validation_func) + { + dbus_uint32_t len; + + _dbus_assert (bad_string_code != DBUS_VALID); + + len = _dbus_marshal_read_uint32 (value_str, value_pos, + header->byte_order, NULL); + + if (!(*string_validation_func) (value_str, value_pos + 4, len)) + return bad_string_code; + } + + return DBUS_VALID; +} + +/** + * Creates a message header from untrusted data. The return value + * is #TRUE if there was enough memory and the data was valid. If it + * returns #TRUE, the header will be created. If it returns #FALSE + * and *validity == #DBUS_VALID, then there wasn't enough memory. If + * it returns #FALSE and *validity != #DBUS_VALID then the data was + * invalid. + * + * The byte_order, fields_array_len, and body_len args should be from + * _dbus_header_have_message_untrusted(). Validation performed in + * _dbus_header_have_message_untrusted() is assumed to have been + * already done. + * + * @param header the header (must be initialized) + * @param validity return location for invalidity reason + * @param byte_order byte order from header + * @param fields_array_len claimed length of fields array + * @param body_len claimed length of body + * @param header_len claimed length of header + * @param str a string + * @param start start of header, 8-aligned + * @param len length of string to look at + * @returns #FALSE if no memory or data was invalid, #TRUE otherwise + */ +dbus_bool_t +_dbus_header_load_untrusted (DBusHeader *header, + DBusValidity *validity, + int byte_order, + int fields_array_len, + int header_len, + int body_len, + const DBusString *str, + int start, + int len) +{ + int leftover; + DBusValidity v; + DBusTypeReader reader; + DBusTypeReader array_reader; + unsigned char v_byte; + dbus_uint32_t v_uint32; + dbus_uint32_t serial; + int padding_start; + int padding_len; + int i; + + _dbus_assert (start == (int) _DBUS_ALIGN_VALUE (start, 8)); + _dbus_assert (header_len <= len); + _dbus_assert (_dbus_string_get_length (&header->data) == 0); + + if (!_dbus_string_copy_len (str, start, header_len, &header->data, 0)) + { + _dbus_verbose ("Failed to copy buffer into new header\n"); + *validity = DBUS_VALID; + return FALSE; + } + + v = _dbus_validate_body_with_reason (&_dbus_header_signature_str, 0, + byte_order, + &leftover, + str, start, len); + + if (v != DBUS_VALID) + { + *validity = v; + goto invalid; + } + + _dbus_assert (leftover < len); + + padding_len = header_len - (FIRST_FIELD_OFFSET + fields_array_len); + padding_start = start + FIRST_FIELD_OFFSET + fields_array_len; + _dbus_assert (start + header_len == (int) _DBUS_ALIGN_VALUE (padding_start, 8)); + _dbus_assert (start + header_len == padding_start + padding_len); + + if (!_dbus_string_validate_nul (str, padding_start, padding_len)) + { + *validity = DBUS_INVALID_ALIGNMENT_PADDING_NOT_NUL; + goto invalid; + } + + header->padding = padding_len; + + /* We now know the data is well-formed, but we have to check that + * it's valid. + */ + + _dbus_type_reader_init (&reader, + byte_order, + &_dbus_header_signature_str, 0, + str, start); + + /* BYTE ORDER */ + _dbus_assert (_dbus_type_reader_get_current_type (&reader) == DBUS_TYPE_BYTE); + _dbus_assert (_dbus_type_reader_get_value_pos (&reader) == BYTE_ORDER_OFFSET); + _dbus_type_reader_read_basic (&reader, &v_byte); + _dbus_type_reader_next (&reader); + + _dbus_assert (v_byte == byte_order); + + /* MESSAGE TYPE */ + _dbus_assert (_dbus_type_reader_get_current_type (&reader) == DBUS_TYPE_BYTE); + _dbus_assert (_dbus_type_reader_get_value_pos (&reader) == TYPE_OFFSET); + _dbus_type_reader_read_basic (&reader, &v_byte); + _dbus_type_reader_next (&reader); + + /* unknown message types are supposed to be ignored, so only validation here is + * that it isn't invalid + */ + if (v_byte == DBUS_MESSAGE_TYPE_INVALID) + { + *validity = DBUS_INVALID_BAD_MESSAGE_TYPE; + goto invalid; + } + + /* FLAGS */ + _dbus_assert (_dbus_type_reader_get_current_type (&reader) == DBUS_TYPE_BYTE); + _dbus_assert (_dbus_type_reader_get_value_pos (&reader) == FLAGS_OFFSET); + _dbus_type_reader_read_basic (&reader, &v_byte); + _dbus_type_reader_next (&reader); + + /* unknown flags should be ignored */ + + /* PROTOCOL VERSION */ + _dbus_assert (_dbus_type_reader_get_current_type (&reader) == DBUS_TYPE_BYTE); + _dbus_assert (_dbus_type_reader_get_value_pos (&reader) == VERSION_OFFSET); + _dbus_type_reader_read_basic (&reader, &v_byte); + _dbus_type_reader_next (&reader); + + if (v_byte != DBUS_MAJOR_PROTOCOL_VERSION) + { + *validity = DBUS_INVALID_BAD_PROTOCOL_VERSION; + goto invalid; + } + + /* BODY LENGTH */ + _dbus_assert (_dbus_type_reader_get_current_type (&reader) == DBUS_TYPE_UINT32); + _dbus_assert (_dbus_type_reader_get_value_pos (&reader) == BODY_LENGTH_OFFSET); + _dbus_type_reader_read_basic (&reader, &v_uint32); + _dbus_type_reader_next (&reader); + + _dbus_assert (body_len == (signed) v_uint32); + + /* SERIAL */ + _dbus_assert (_dbus_type_reader_get_current_type (&reader) == DBUS_TYPE_UINT32); + _dbus_assert (_dbus_type_reader_get_value_pos (&reader) == SERIAL_OFFSET); + _dbus_type_reader_read_basic (&reader, &serial); + _dbus_type_reader_next (&reader); + + if (serial == 0) + { + *validity = DBUS_INVALID_BAD_SERIAL; + goto invalid; + } + + _dbus_assert (_dbus_type_reader_get_current_type (&reader) == DBUS_TYPE_ARRAY); + _dbus_assert (_dbus_type_reader_get_value_pos (&reader) == FIELDS_ARRAY_LENGTH_OFFSET); + + _dbus_type_reader_recurse (&reader, &array_reader); + while (_dbus_type_reader_get_current_type (&array_reader) != DBUS_TYPE_INVALID) + { + DBusTypeReader struct_reader; + DBusTypeReader variant_reader; + unsigned char field_code; + + _dbus_assert (_dbus_type_reader_get_current_type (&array_reader) == DBUS_TYPE_STRUCT); + + _dbus_type_reader_recurse (&array_reader, &struct_reader); + + _dbus_assert (_dbus_type_reader_get_current_type (&struct_reader) == DBUS_TYPE_BYTE); + _dbus_type_reader_read_basic (&struct_reader, &field_code); + _dbus_type_reader_next (&struct_reader); + + if (field_code == DBUS_HEADER_FIELD_INVALID) + { + _dbus_verbose ("invalid header field code\n"); + *validity = DBUS_INVALID_HEADER_FIELD_CODE; + goto invalid; + } + + if (field_code > DBUS_HEADER_FIELD_LAST) + { + _dbus_verbose ("unknown header field code %d, skipping\n", + field_code); + goto next_field; + } + + _dbus_assert (_dbus_type_reader_get_current_type (&struct_reader) == DBUS_TYPE_VARIANT); + _dbus_type_reader_recurse (&struct_reader, &variant_reader); + + v = load_and_validate_field (header, field_code, &variant_reader); + if (v != DBUS_VALID) + { + _dbus_verbose ("Field %d was invalid\n", field_code); + *validity = v; + goto invalid; + } + + next_field: + _dbus_type_reader_next (&array_reader); + } + + /* Anything we didn't fill in is now known not to exist */ + i = 0; + while (i <= DBUS_HEADER_FIELD_LAST) + { + if (header->fields[i].value_pos == _DBUS_HEADER_FIELD_VALUE_UNKNOWN) + header->fields[i].value_pos = _DBUS_HEADER_FIELD_VALUE_NONEXISTENT; + ++i; + } + + v = check_mandatory_fields (header); + if (v != DBUS_VALID) + { + _dbus_verbose ("Mandatory fields were missing, code %d\n", v); + *validity = v; + goto invalid; + } + + *validity = DBUS_VALID; + return TRUE; + + invalid: + _dbus_string_set_length (&header->data, 0); + return FALSE; +} + +/** + * Fills in the correct body length. + * + * @param header the header + * @param body_len the length of the body + */ +void +_dbus_header_update_lengths (DBusHeader *header, + int body_len) +{ + _dbus_marshal_set_uint32 (&header->data, + BODY_LENGTH_OFFSET, + body_len, + header->byte_order); +} + +static dbus_bool_t +find_field_for_modification (DBusHeader *header, + int field, + DBusTypeReader *reader, + DBusTypeReader *realign_root) +{ + dbus_bool_t retval; + + retval = FALSE; + + _dbus_type_reader_init (realign_root, + header->byte_order, + &_dbus_header_signature_str, + FIELDS_ARRAY_SIGNATURE_OFFSET, + &header->data, + FIELDS_ARRAY_LENGTH_OFFSET); + + _dbus_type_reader_recurse (realign_root, reader); + + while (_dbus_type_reader_get_current_type (reader) != DBUS_TYPE_INVALID) + { + DBusTypeReader sub; + unsigned char field_code; + + _dbus_type_reader_recurse (reader, &sub); + + _dbus_assert (_dbus_type_reader_get_current_type (&sub) == DBUS_TYPE_BYTE); + _dbus_type_reader_read_basic (&sub, &field_code); + + if (field_code == (unsigned) field) + { + _dbus_assert (_dbus_type_reader_get_current_type (reader) == DBUS_TYPE_STRUCT); + retval = TRUE; + goto done; + } + + _dbus_type_reader_next (reader); + } + + done: + return retval; +} + +/** + * Sets the value of a field with basic type. If the value is a string + * value, it isn't allowed to be #NULL. If the field doesn't exist, + * it will be created. + * + * @param header the header + * @param field the field to set + * @param type the type of the value + * @param value the value as for _dbus_marshal_set_basic() + * @returns #FALSE if no memory + */ +dbus_bool_t +_dbus_header_set_field_basic (DBusHeader *header, + int field, + int type, + const void *value) +{ + _dbus_return_val_if_fail (field <= DBUS_HEADER_FIELD_LAST, FALSE); + + if (!reserve_header_padding (header)) + return FALSE; + + /* If the field exists we set, otherwise we append */ + if (_dbus_header_cache_check (header, field)) + { + DBusTypeReader reader; + DBusTypeReader realign_root; + + if (!find_field_for_modification (header, field, + &reader, &realign_root)) + _dbus_assert_not_reached ("field was marked present in cache but wasn't found"); + + if (!set_basic_field (&reader, field, type, value, &realign_root)) + return FALSE; + } + else + { + DBusTypeWriter writer; + DBusTypeWriter array; + + _dbus_type_writer_init_values_only (&writer, + header->byte_order, + &_dbus_header_signature_str, + FIELDS_ARRAY_SIGNATURE_OFFSET, + &header->data, + FIELDS_ARRAY_LENGTH_OFFSET); + + /* recurse into array without creating a new length, and jump to + * end of array. + */ + if (!_dbus_type_writer_append_array (&writer, + &_dbus_header_signature_str, + FIELDS_ARRAY_ELEMENT_SIGNATURE_OFFSET, + &array)) + _dbus_assert_not_reached ("recurse into ARRAY should not have used memory"); + + _dbus_assert (array.u.array.len_pos == FIELDS_ARRAY_LENGTH_OFFSET); + _dbus_assert (array.u.array.start_pos == FIRST_FIELD_OFFSET); + _dbus_assert (array.value_pos == HEADER_END_BEFORE_PADDING (header)); + + if (!write_basic_field (&array, + field, type, value)) + return FALSE; + + if (!_dbus_type_writer_unrecurse (&writer, &array)) + _dbus_assert_not_reached ("unrecurse from ARRAY should not have used memory"); + } + + correct_header_padding (header); + + /* We could be smarter about this (only invalidate fields after the + * one we modified, or even only if the one we modified changed + * length). But this hack is a start. + */ + _dbus_header_cache_invalidate_all (header); + + return TRUE; +} + +/** + * Gets the value of a field with basic type. If the field + * doesn't exist, returns #FALSE, otherwise returns #TRUE. + * + * @param header the header + * @param field the field to get + * @param type the type of the value + * @param value the value as for _dbus_marshal_read_basic() + * @returns #FALSE if the field doesn't exist + */ +dbus_bool_t +_dbus_header_get_field_basic (DBusHeader *header, + int field, + int type, + void *value) +{ + _dbus_assert (field != DBUS_HEADER_FIELD_INVALID); + _dbus_assert (field <= DBUS_HEADER_FIELD_LAST); + _dbus_assert (_dbus_header_field_types[field].code == field); + /* in light of this you might ask why the type is passed in; + * the only rationale I can think of is so the caller has + * to specify its expectation and breaks if we change it + */ + _dbus_assert (type == EXPECTED_TYPE_OF_FIELD (field)); + + if (!_dbus_header_cache_check (header, field)) + return FALSE; + + _dbus_assert (header->fields[field].value_pos >= 0); + + _dbus_marshal_read_basic (&header->data, + header->fields[field].value_pos, + type, value, header->byte_order, + NULL); + + return TRUE; +} + +/** + * Gets the raw marshaled data for a field. If the field doesn't + * exist, returns #FALSE, otherwise returns #TRUE. Returns the start + * of the marshaled data, i.e. usually the byte where the length + * starts (for strings and arrays) or for basic types just the value + * itself. + * + * @param header the header + * @param field the field to get + * @param str return location for the data string + * @param pos return location for start of field value + * @returns #FALSE if the field doesn't exist + */ +dbus_bool_t +_dbus_header_get_field_raw (DBusHeader *header, + int field, + const DBusString **str, + int *pos) +{ + if (!_dbus_header_cache_check (header, field)) + return FALSE; + + if (str) + *str = &header->data; + if (pos) + *pos = header->fields[field].value_pos; + + return TRUE; +} + +/** + * Deletes a field, if it exists. + * + * @param header the header + * @param field the field to delete + * @returns #FALSE if no memory + */ +dbus_bool_t +_dbus_header_delete_field (DBusHeader *header, + int field) +{ + DBusTypeReader reader; + DBusTypeReader realign_root; + + if (_dbus_header_cache_known_nonexistent (header, field)) + return TRUE; /* nothing to do */ + + /* Scan to the field we want, delete and realign, reappend + * padding. Field may turn out not to exist. + */ + if (!find_field_for_modification (header, field, + &reader, &realign_root)) + return TRUE; /* nothing to do */ + + if (!reserve_header_padding (header)) + return FALSE; + + if (!_dbus_type_reader_delete (&reader, + &realign_root)) + return FALSE; + + correct_header_padding (header); + + _dbus_header_cache_invalidate_all (header); + + _dbus_assert (!_dbus_header_cache_check (header, field)); /* Expensive assertion ... */ + + return TRUE; +} + +/** + * Toggles a message flag bit, turning on the bit if value = TRUE and + * flipping it off if value = FALSE. + * + * @param header the header + * @param flag the message flag to toggle + * @param value toggle on or off + */ +void +_dbus_header_toggle_flag (DBusHeader *header, + dbus_uint32_t flag, + dbus_bool_t value) +{ + unsigned char *flags_p; + + flags_p = _dbus_string_get_data_len (&header->data, FLAGS_OFFSET, 1); + + if (value) + *flags_p |= flag; + else + *flags_p &= ~flag; +} + +/** + * Gets a message flag bit, returning TRUE if the bit is set. + * + * @param header the header + * @param flag the message flag to get + * @returns #TRUE if the flag is set + */ +dbus_bool_t +_dbus_header_get_flag (DBusHeader *header, + dbus_uint32_t flag) +{ + const unsigned char *flags_p; + + flags_p = _dbus_string_get_const_data_len (&header->data, FLAGS_OFFSET, 1); + + return (*flags_p & flag) != 0; +} + +/** @} */ + +#ifdef DBUS_BUILD_TESTS +#include "dbus-test.h" +#include + +dbus_bool_t +_dbus_marshal_header_test (void) +{ + + return TRUE; +} + +#endif /* DBUS_BUILD_TESTS */ diff --git a/dbus/dbus-marshal-header.h b/dbus/dbus-marshal-header.h new file mode 100644 index 00000000..32bf51eb --- /dev/null +++ b/dbus/dbus-marshal-header.h @@ -0,0 +1,128 @@ +/* -*- mode: C; c-file-style: "gnu" -*- */ +/* dbus-marshal-header.h Managing marshaling/demarshaling of message headers + * + * Copyright (C) 2005 Red Hat, Inc. + * + * Licensed under the Academic Free License version 2.1 + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program 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 General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * + */ + +#ifndef DBUS_MARSHAL_HEADER_H +#define DBUS_MARSHAL_HEADER_H + +#include +#include +#include + +#ifndef PACKAGE +#error "config.h not included here" +#endif + +typedef struct DBusHeader DBusHeader; +typedef struct DBusHeaderField DBusHeaderField; + +#define _DBUS_HEADER_FIELD_VALUE_UNKNOWN -1 +#define _DBUS_HEADER_FIELD_VALUE_NONEXISTENT -2 + +/** + * Cached information about a header field in the message + */ +struct DBusHeaderField +{ + int value_pos; /**< Position of field value, or -1/-2 */ +}; + +struct DBusHeader +{ + DBusString data; /**< Header network data, stored + * separately from body so we can + * independently realloc it. + */ + + DBusHeaderField fields[DBUS_HEADER_FIELD_LAST + 1]; /**< Track the location + * of each field in header + */ + + dbus_uint32_t padding : 3; /**< bytes of alignment in header */ + dbus_uint32_t byte_order : 8; /**< byte order of header */ +}; + +dbus_bool_t _dbus_header_init (DBusHeader *header, + int byte_order); +void _dbus_header_free (DBusHeader *header); +void _dbus_header_reinit (DBusHeader *header, + int byte_order); +dbus_bool_t _dbus_header_create (DBusHeader *header, + int type, + const char *destination, + const char *path, + const char *interface, + const char *member, + const char *error_name); +dbus_bool_t _dbus_header_copy (const DBusHeader *header, + DBusHeader *dest); +int _dbus_header_get_message_type (DBusHeader *header); +void _dbus_header_set_serial (DBusHeader *header, + dbus_uint32_t serial); +dbus_uint32_t _dbus_header_get_serial (DBusHeader *header); +void _dbus_header_update_lengths (DBusHeader *header, + int body_len); +dbus_bool_t _dbus_header_set_field_basic (DBusHeader *header, + int field, + int type, + const void *value); +dbus_bool_t _dbus_header_get_field_basic (DBusHeader *header, + int field, + int type, + void *value); +dbus_bool_t _dbus_header_get_field_raw (DBusHeader *header, + int field, + const DBusString **str, + int *pos); +dbus_bool_t _dbus_header_delete_field (DBusHeader *header, + int field); +void _dbus_header_toggle_flag (DBusHeader *header, + dbus_uint32_t flag, + dbus_bool_t value); +dbus_bool_t _dbus_header_get_flag (DBusHeader *header, + dbus_uint32_t flag); +dbus_bool_t _dbus_header_ensure_signature (DBusHeader *header, + DBusString **type_str, + int *type_pos); +dbus_bool_t _dbus_header_have_message_untrusted (int max_message_length, + DBusValidity *validity, + int *byte_order, + int *fields_array_len, + int *header_len, + int *body_len, + const DBusString *str, + int start, + int len); +dbus_bool_t _dbus_header_load_untrusted (DBusHeader *header, + DBusValidity *validity, + int byte_order, + int fields_array_len, + int header_len, + int body_len, + const DBusString *str, + int start, + int len); + + + + +#endif /* DBUS_MARSHAL_HEADER_H */ diff --git a/dbus/dbus-marshal-recursive.c b/dbus/dbus-marshal-recursive.c index a3ef0217..3b2ce91d 100644 --- a/dbus/dbus-marshal-recursive.c +++ b/dbus/dbus-marshal-recursive.c @@ -29,7 +29,29 @@ * @addtogroup DBusMarshal * @{ */ -#define RECURSIVE_MARSHAL_TRACE 0 +#define RECURSIVE_MARSHAL_READ_TRACE 0 +#define RECURSIVE_MARSHAL_WRITE_TRACE 0 + +static void +free_fixups (DBusList **fixups) +{ + DBusList *link; + + link = _dbus_list_get_first_link (fixups); + while (link != NULL) + { + DBusList *next; + + next = _dbus_list_get_next_link (fixups, link); + + dbus_free (link->data); + _dbus_list_free_link (link); + + link = next; + } + + *fixups = NULL; +} static void apply_and_free_fixups (DBusList **fixups, @@ -37,7 +59,7 @@ apply_and_free_fixups (DBusList **fixups, { DBusList *link; -#if RECURSIVE_MARSHAL_TRACE +#if RECURSIVE_MARSHAL_WRITE_TRACE if (*fixups) _dbus_verbose (" %d FIXUPS to apply\n", _dbus_list_get_length (fixups)); @@ -56,7 +78,7 @@ apply_and_free_fixups (DBusList **fixups, f = link->data; -#if RECURSIVE_MARSHAL_TRACE +#if RECURSIVE_MARSHAL_WRITE_TRACE _dbus_verbose (" applying FIXUP to reader %p at pos %d new_len = %d old len %d\n", reader, f->len_pos_in_reader, f->new_len, _dbus_marshal_read_uint32 (reader->value_str, @@ -198,8 +220,10 @@ array_reader_get_array_len (const DBusTypeReader *reader) reader->byte_order, NULL); +#if RECURSIVE_MARSHAL_READ_TRACE _dbus_verbose (" reader %p len_pos %d array len %u len_offset %d\n", reader, len_pos, array_len, reader->array_len_offset); +#endif _dbus_assert (reader->u.array.start_pos - len_pos - 4 < 8); @@ -230,7 +254,7 @@ array_reader_recurse (DBusTypeReader *sub, _dbus_assert ((sub->u.array.start_pos - (len_pos + 4)) < 8); /* only 3 bits in array_len_offset */ sub->array_len_offset = sub->u.array.start_pos - (len_pos + 4); -#if RECURSIVE_MARSHAL_TRACE +#if RECURSIVE_MARSHAL_READ_TRACE _dbus_verbose (" type reader %p array start = %d len_offset = %d array len = %d array element type = %s\n", sub, sub->u.array.start_pos, @@ -262,7 +286,7 @@ variant_reader_recurse (DBusTypeReader *sub, sub->value_pos = _DBUS_ALIGN_VALUE (sub->value_pos, 8); -#if RECURSIVE_MARSHAL_TRACE +#if RECURSIVE_MARSHAL_READ_TRACE _dbus_verbose (" type reader %p variant containing '%s'\n", sub, _dbus_string_get_const_data_len (sub->type_str, @@ -345,14 +369,21 @@ base_reader_next (DBusTypeReader *reader, { DBusTypeReader sub; - /* Recurse into the struct or variant */ - _dbus_type_reader_recurse (reader, &sub); - - /* Skip everything in this subreader */ - while (_dbus_type_reader_next (&sub)) + if (reader->klass->types_only && current_type == DBUS_TYPE_VARIANT) + ; + else { - /* nothing */; + /* Recurse into the struct or variant */ + _dbus_type_reader_recurse (reader, &sub); + + /* Skip everything in this subreader */ + while (_dbus_type_reader_next (&sub)) + { + /* nothing */; + } } + if (!reader->klass->types_only) + reader->value_pos = sub.value_pos; /* Now we are at the end of this container; for variants, the * subreader's type_pos is totally inapplicable (it's in the @@ -363,9 +394,6 @@ base_reader_next (DBusTypeReader *reader, reader->type_pos += 1; else reader->type_pos = sub.type_pos; - - if (!reader->klass->types_only) - reader->value_pos = sub.value_pos; } break; @@ -435,7 +463,7 @@ array_reader_next (DBusTypeReader *reader, end_pos = reader->u.array.start_pos + array_reader_get_array_len (reader); -#if RECURSIVE_MARSHAL_TRACE +#if RECURSIVE_MARSHAL_READ_TRACE _dbus_verbose (" reader %p array next START start_pos = %d end_pos = %d value_pos = %d current_type = %s\n", reader, reader->u.array.start_pos, @@ -487,7 +515,7 @@ array_reader_next (DBusTypeReader *reader, break; } -#if RECURSIVE_MARSHAL_TRACE +#if RECURSIVE_MARSHAL_READ_TRACE _dbus_verbose (" reader %p array next END start_pos = %d end_pos = %d value_pos = %d current_type = %s\n", reader, reader->u.array.start_pos, @@ -602,7 +630,7 @@ _dbus_type_reader_init (DBusTypeReader *reader, reader_init (reader, byte_order, type_str, type_pos, value_str, value_pos); -#if RECURSIVE_MARSHAL_TRACE +#if RECURSIVE_MARSHAL_READ_TRACE _dbus_verbose (" type reader %p init type_pos = %d value_pos = %d remaining sig '%s'\n", reader, reader->type_pos, reader->value_pos, _dbus_string_get_const_data_len (reader->type_str, reader->type_pos, 0)); @@ -626,7 +654,7 @@ _dbus_type_reader_init_from_mark (DBusTypeReader *reader, if (reader->klass->init_from_mark) (* reader->klass->init_from_mark) (reader, mark); -#if RECURSIVE_MARSHAL_TRACE +#if RECURSIVE_MARSHAL_READ_TRACE _dbus_verbose (" type reader %p init from mark type_pos = %d value_pos = %d remaining sig '%s'\n", reader, reader->type_pos, reader->value_pos, _dbus_string_get_const_data_len (reader->type_str, reader->type_pos, 0)); @@ -643,7 +671,7 @@ _dbus_type_reader_init_types_only (DBusTypeReader *reader, reader_init (reader, DBUS_COMPILER_BYTE_ORDER /* irrelevant */, type_str, type_pos, NULL, _DBUS_INT_MAX /* crashes if we screw up */); -#if RECURSIVE_MARSHAL_TRACE +#if RECURSIVE_MARSHAL_READ_TRACE _dbus_verbose (" type reader %p init types only type_pos = %d remaining sig '%s'\n", reader, reader->type_pos, _dbus_string_get_const_data_len (reader->type_str, reader->type_pos, 0)); @@ -666,7 +694,7 @@ _dbus_type_reader_init_types_only_from_mark (DBusTypeReader *reader, if (reader->klass->init_from_mark) (* reader->klass->init_from_mark) (reader, mark); -#if RECURSIVE_MARSHAL_TRACE +#if RECURSIVE_MARSHAL_READ_TRACE _dbus_verbose (" type reader %p init types only from mark type_pos = %d remaining sig '%s'\n", reader, reader->type_pos, _dbus_string_get_const_data_len (reader->type_str, reader->type_pos, 0)); @@ -714,6 +742,25 @@ _dbus_type_reader_get_current_type (const DBusTypeReader *reader) return t; } +int +_dbus_type_reader_get_array_type (const DBusTypeReader *reader) +{ + int element_type; + + _dbus_assert (_dbus_type_reader_get_current_type (reader) == DBUS_TYPE_ARRAY); + + element_type = first_type_in_signature (reader->type_str, + reader->type_pos + 1); + + return element_type; +} + +int +_dbus_type_reader_get_value_pos (const DBusTypeReader *reader) +{ + return reader->value_pos; +} + dbus_bool_t _dbus_type_reader_array_is_empty (const DBusTypeReader *reader) { @@ -723,7 +770,7 @@ _dbus_type_reader_array_is_empty (const DBusTypeReader *reader) _dbus_assert (!reader->klass->types_only); /* reader is supposed to be at an array child */ -#if RECURSIVE_MARSHAL_TRACE +#if RECURSIVE_MARSHAL_READ_TRACE _dbus_verbose ("checking array len at %d\n", reader->value_pos); #endif @@ -733,13 +780,33 @@ _dbus_type_reader_array_is_empty (const DBusTypeReader *reader) &array_len, reader->byte_order, NULL); -#if RECURSIVE_MARSHAL_TRACE +#if RECURSIVE_MARSHAL_READ_TRACE _dbus_verbose (" ... array len = %d\n", array_len); #endif return array_len == 0; } +/** + * Get the address of the marshaled value in the data being read. The + * address may not be aligned; you have to align it to the type of the + * value you want to read. Most of the demarshal routines do this for + * you. + * + * @param reader the reader + * @param value_location the address of the marshaled value + */ +void +_dbus_type_reader_read_raw (const DBusTypeReader *reader, + const unsigned char **value_location) +{ + _dbus_assert (!reader->klass->types_only); + + *value_location = _dbus_string_get_const_data_len (reader->value_str, + reader->value_pos, + 0); +} + void _dbus_type_reader_read_basic (const DBusTypeReader *reader, void *value) @@ -757,7 +824,7 @@ _dbus_type_reader_read_basic (const DBusTypeReader *reader, NULL); -#if RECURSIVE_MARSHAL_TRACE +#if RECURSIVE_MARSHAL_READ_TRACE _dbus_verbose (" type reader %p read basic type_pos = %d value_pos = %d remaining sig '%s'\n", reader, reader->type_pos, reader->value_pos, _dbus_string_get_const_data_len (reader->type_str, reader->type_pos, 0)); @@ -765,8 +832,9 @@ _dbus_type_reader_read_basic (const DBusTypeReader *reader, } /** - * Reads an array of fixed-length basic values. Does not work for - * arrays of string or container types. + * Reads a block of fixed-length basic values, from the current point + * in an array to the end of the array. Does not work for arrays of + * string or container types. * * This function returns the array in-place; it does not make a copy, * and it does not swap the bytes. @@ -775,11 +843,11 @@ _dbus_type_reader_read_basic (const DBusTypeReader *reader, * and the "value" argument should be a "const double**" and so on. * * @param reader the reader to read from - * @param value place to return the array + * @param value place to return the array values * @param n_elements place to return number of array elements */ void -_dbus_type_reader_read_fixed_array (const DBusTypeReader *reader, +_dbus_type_reader_read_fixed_multi (const DBusTypeReader *reader, void *value, int *n_elements) { @@ -787,6 +855,7 @@ _dbus_type_reader_read_fixed_array (const DBusTypeReader *reader, int end_pos; int remaining_len; int alignment; + int total_len; _dbus_assert (!reader->klass->types_only); _dbus_assert (reader->klass == &array_reader_class); @@ -799,9 +868,19 @@ _dbus_type_reader_read_fixed_array (const DBusTypeReader *reader, alignment = _dbus_type_get_alignment (element_type); - end_pos = reader->u.array.start_pos + array_reader_get_array_len (reader); + _dbus_assert (reader->value_pos >= reader->u.array.start_pos); + + total_len = array_reader_get_array_len (reader); + end_pos = reader->u.array.start_pos + total_len; remaining_len = end_pos - reader->value_pos; +#if RECURSIVE_MARSHAL_READ_TRACE + _dbus_verbose ("end_pos %d total_len %d remaining_len %d value_pos %d\n", + end_pos, total_len, remaining_len, reader->value_pos); +#endif + + _dbus_assert (remaining_len <= total_len); + if (remaining_len == 0) *(const DBusBasicValue**) value = NULL; else @@ -813,7 +892,7 @@ _dbus_type_reader_read_fixed_array (const DBusTypeReader *reader, *n_elements = remaining_len / alignment; _dbus_assert ((remaining_len % alignment) == 0); -#if RECURSIVE_MARSHAL_TRACE +#if RECURSIVE_MARSHAL_READ_TRACE _dbus_verbose (" type reader %p read fixed array type_pos = %d value_pos = %d remaining sig '%s'\n", reader, reader->type_pos, reader->value_pos, _dbus_string_get_const_data_len (reader->type_str, reader->type_pos, 0)); @@ -874,7 +953,7 @@ _dbus_type_reader_recurse (DBusTypeReader *reader, (* sub->klass->recurse) (sub, reader); -#if RECURSIVE_MARSHAL_TRACE +#if RECURSIVE_MARSHAL_READ_TRACE _dbus_verbose (" type reader %p RECURSED type_pos = %d value_pos = %d remaining sig '%s'\n", sub, sub->type_pos, sub->value_pos, _dbus_string_get_const_data_len (sub->type_str, sub->type_pos, 0)); @@ -896,7 +975,7 @@ _dbus_type_reader_next (DBusTypeReader *reader) t = _dbus_type_reader_get_current_type (reader); -#if RECURSIVE_MARSHAL_TRACE +#if RECURSIVE_MARSHAL_READ_TRACE _dbus_verbose (" type reader %p START next() { type_pos = %d value_pos = %d remaining sig '%s' current_type = %s\n", reader, reader->type_pos, reader->value_pos, _dbus_string_get_const_data_len (reader->type_str, reader->type_pos, 0), @@ -908,7 +987,7 @@ _dbus_type_reader_next (DBusTypeReader *reader) (* reader->klass->next) (reader, t); -#if RECURSIVE_MARSHAL_TRACE +#if RECURSIVE_MARSHAL_READ_TRACE _dbus_verbose (" type reader %p END next() type_pos = %d value_pos = %d remaining sig '%s' current_type = %s\n", reader, reader->type_pos, reader->value_pos, _dbus_string_get_const_data_len (reader->type_str, reader->type_pos, 0), @@ -984,12 +1063,10 @@ replacement_block_init (ReplacementBlock *block, if (!_dbus_string_init (&block->replacement)) return FALSE; - /* ALIGN_OFFSET is the offset to add to get to an 8-boundary; so 8 - - * ALIGN_OFFSET is the padding to have the same align properties in + /* % 8 is the padding to have the same align properties in * our replacement string as we do at the position being replaced */ - block->padding = 8 - _DBUS_ALIGN_OFFSET (reader->value_pos, 8); - _dbus_assert (block->padding >= 0); + block->padding = reader->value_pos % 8; if (!_dbus_string_lengthen (&block->replacement, block->padding)) goto oom; @@ -1017,6 +1094,10 @@ replacement_block_replace (ReplacementBlock *block, realign_reader = *realign_root; +#if RECURSIVE_MARSHAL_WRITE_TRACE + _dbus_verbose ("INITIALIZING replacement block writer %p at value_pos %d\n", + &writer, _dbus_string_get_length (&block->replacement)); +#endif _dbus_type_writer_init_values_only (&writer, realign_reader.byte_order, realign_reader.type_str, @@ -1024,6 +1105,12 @@ replacement_block_replace (ReplacementBlock *block, &block->replacement, _dbus_string_get_length (&block->replacement)); + _dbus_assert (realign_reader.value_pos <= reader->value_pos); + +#if RECURSIVE_MARSHAL_WRITE_TRACE + _dbus_verbose ("COPYING from reader at value_pos %d to writer %p starting after value_pos %d\n", + realign_reader.value_pos, &writer, reader->value_pos); +#endif fixups = NULL; if (!_dbus_type_writer_write_reader_partial (&writer, &realign_reader, @@ -1033,14 +1120,15 @@ replacement_block_replace (ReplacementBlock *block, &fixups)) goto oom; -#if RECURSIVE_MARSHAL_TRACE - _dbus_verbose ("REPLACEMENT at padding %d len %d\n", padding, - _dbus_string_get_length (&block->replacement) - padding); - _dbus_verbose_bytes_of_string (&block->replacement, padding, - _dbus_string_get_length (&block->replacement) - padding); - _dbus_verbose ("TO BE REPLACED at value_pos = %d (align pad %d) len %d\n", - reader->value_pos, (int) (8 - _DBUS_ALIGN_OFFSET (reader->value_pos, 8)), - realign_reader.value_pos - reader->value_pos); +#if RECURSIVE_MARSHAL_WRITE_TRACE + _dbus_verbose ("REPLACEMENT at padding %d len %d\n", block->padding, + _dbus_string_get_length (&block->replacement) - block->padding); + _dbus_verbose_bytes_of_string (&block->replacement, block->padding, + _dbus_string_get_length (&block->replacement) - block->padding); + _dbus_verbose ("TO BE REPLACED at value_pos = %d (align pad %d) len %d realign_reader.value_pos %d\n", + reader->value_pos, reader->value_pos % 8, + realign_reader.value_pos - reader->value_pos, + realign_reader.value_pos); _dbus_verbose_bytes_of_string (reader->value_str, reader->value_pos, realign_reader.value_pos - reader->value_pos); @@ -1063,6 +1151,7 @@ replacement_block_replace (ReplacementBlock *block, oom: _dbus_string_set_length (&block->replacement, orig_len); + free_fixups (&fixups); return FALSE; } @@ -1113,14 +1202,19 @@ reader_set_basic_variable_length (DBusTypeReader *reader, return FALSE; /* Write the new basic value */ - +#if RECURSIVE_MARSHAL_WRITE_TRACE + _dbus_verbose ("INITIALIZING writer %p to write basic value at value_pos %d of replacement string\n", + &writer, _dbus_string_get_length (&block.replacement)); +#endif _dbus_type_writer_init_values_only (&writer, reader->byte_order, reader->type_str, reader->type_pos, &block.replacement, _dbus_string_get_length (&block.replacement)); - +#if RECURSIVE_MARSHAL_WRITE_TRACE + _dbus_verbose ("WRITING basic value to writer %p (replacement string)\n", &writer); +#endif if (!_dbus_type_writer_write_basic (&writer, current_type, value)) goto out; @@ -1191,16 +1285,21 @@ _dbus_type_reader_set_basic (DBusTypeReader *reader, int current_type; _dbus_assert (!reader->klass->types_only); + _dbus_assert (reader->value_str == realign_root->value_str); + _dbus_assert (reader->value_pos >= realign_root->value_pos); current_type = _dbus_type_reader_get_current_type (reader); -#if RECURSIVE_MARSHAL_TRACE - _dbus_verbose (" type reader %p set basic type_pos = %d value_pos = %d remaining sig '%s' realign_root = %p with value_pos %d current_type = %s\n", +#if RECURSIVE_MARSHAL_WRITE_TRACE + _dbus_verbose (" SET BASIC type reader %p type_pos = %d value_pos = %d remaining sig '%s' realign_root = %p with value_pos %d current_type = %s\n", reader, reader->type_pos, reader->value_pos, _dbus_string_get_const_data_len (reader->type_str, reader->type_pos, 0), realign_root, realign_root ? realign_root->value_pos : -1, _dbus_type_to_string (current_type)); + _dbus_verbose_bytes_of_string (realign_root->value_str, realign_root->value_pos, + _dbus_string_get_length (realign_root->value_str) - + realign_root->value_pos); #endif _dbus_assert (_dbus_type_is_basic (current_type)); @@ -1325,7 +1424,7 @@ _dbus_type_writer_init (DBusTypeWriter *writer, writer->type_pos_is_expectation = FALSE; writer->enabled = TRUE; -#if RECURSIVE_MARSHAL_TRACE +#if RECURSIVE_MARSHAL_WRITE_TRACE _dbus_verbose ("writer %p init remaining sig '%s'\n", writer, writer->type_str ? _dbus_string_get_const_data_len (writer->type_str, writer->type_pos, 0) : @@ -1333,6 +1432,58 @@ _dbus_type_writer_init (DBusTypeWriter *writer, #endif } +/** + * Initialize a write iterator, with the signature to be provided + * later. + * + * @param writer the writer to init + * @param byte_order the byte order to marshal into + * @param value_str the string to write values into + * @param value_pos where to insert values + * + */ +void +_dbus_type_writer_init_types_delayed (DBusTypeWriter *writer, + int byte_order, + DBusString *value_str, + int value_pos) +{ + _dbus_type_writer_init (writer, byte_order, + NULL, 0, value_str, value_pos); +} + +/** + * Adds type string to the writer, if it had none. + * + * @param writer the writer to init + * @param type_str type string to add + * @param type_pos type position + * + */ +void +_dbus_type_writer_add_types (DBusTypeWriter *writer, + DBusString *type_str, + int type_pos) +{ + if (writer->type_str == NULL) /* keeps us from using this as setter */ + { + writer->type_str = type_str; + writer->type_pos = type_pos; + } +} + +/** + * Removes type string from the writer. + * + * @param writer the writer to remove from + */ +void +_dbus_type_writer_remove_types (DBusTypeWriter *writer) +{ + writer->type_str = NULL; + writer->type_pos = -1; +} + /** * Like _dbus_type_writer_init(), except the type string * passed in should correspond to an existing signature that @@ -1438,19 +1589,21 @@ writer_recurse_init_and_check (DBusTypeWriter *writer, } #endif /* DBUS_DISABLE_CHECKS */ -#if RECURSIVE_MARSHAL_TRACE - _dbus_verbose (" type writer %p recurse parent %s type_pos = %d value_pos = %d is_expectation = %d remaining sig '%s'\n", +#if RECURSIVE_MARSHAL_WRITE_TRACE + _dbus_verbose (" type writer %p recurse parent %s type_pos = %d value_pos = %d is_expectation = %d remaining sig '%s' enabled = %d\n", writer, _dbus_type_to_string (writer->container_type), writer->type_pos, writer->value_pos, writer->type_pos_is_expectation, writer->type_str ? _dbus_string_get_const_data_len (writer->type_str, writer->type_pos, 0) : - "unknown"); - _dbus_verbose (" type writer %p recurse sub %s type_pos = %d value_pos = %d is_expectation = %d\n", + "unknown", + writer->enabled); + _dbus_verbose (" type writer %p recurse sub %s type_pos = %d value_pos = %d is_expectation = %d enabled = %d\n", sub, _dbus_type_to_string (sub->container_type), sub->type_pos, sub->value_pos, - sub->type_pos_is_expectation); + sub->type_pos_is_expectation, + sub->enabled); #endif } @@ -1463,12 +1616,13 @@ write_or_verify_typecode (DBusTypeWriter *writer, * or variant has type_pos pointing to the next place to insert a * typecode. */ -#if RECURSIVE_MARSHAL_TRACE - _dbus_verbose (" type writer %p write_or_verify start type_pos = %d remaining sig '%s'\n", +#if RECURSIVE_MARSHAL_WRITE_TRACE + _dbus_verbose (" type writer %p write_or_verify start type_pos = %d remaining sig '%s' enabled = %d\n", writer, writer->type_pos, writer->type_str ? _dbus_string_get_const_data_len (writer->type_str, writer->type_pos, 0) : - "unknown"); + "unknown", + writer->enabled); #endif if (writer->type_str == NULL) @@ -1508,7 +1662,7 @@ write_or_verify_typecode (DBusTypeWriter *writer, writer->type_pos += 1; } -#if RECURSIVE_MARSHAL_TRACE +#if RECURSIVE_MARSHAL_WRITE_TRACE _dbus_verbose (" type writer %p write_or_verify end type_pos = %d remaining sig '%s'\n", writer, writer->type_pos, _dbus_string_get_const_data_len (writer->type_str, writer->type_pos, 0)); @@ -1558,7 +1712,8 @@ writer_recurse_array (DBusTypeWriter *writer, const DBusString *contained_type, int contained_type_start, int contained_type_len, - DBusTypeWriter *sub) + DBusTypeWriter *sub, + dbus_bool_t is_array_append) { dbus_uint32_t value = 0; int alignment; @@ -1583,7 +1738,7 @@ writer_recurse_array (DBusTypeWriter *writer, } #endif /* DBUS_DISABLE_CHECKS */ - if (writer->enabled) + if (writer->enabled && !is_array_append) { /* 3 pad + 4 bytes for the array length, and 4 bytes possible padding * before array values @@ -1633,12 +1788,19 @@ writer_recurse_array (DBusTypeWriter *writer, if (writer->enabled) { - /* Write the length */ + /* Write (or jump over, if is_array_append) the length */ sub->u.array.len_pos = _DBUS_ALIGN_VALUE (sub->value_pos, 4); - if (!_dbus_type_writer_write_basic_no_typecode (sub, DBUS_TYPE_UINT32, - &value)) - _dbus_assert_not_reached ("should not have failed to insert array len"); + if (is_array_append) + { + sub->value_pos += 4; + } + else + { + if (!_dbus_type_writer_write_basic_no_typecode (sub, DBUS_TYPE_UINT32, + &value)) + _dbus_assert_not_reached ("should not have failed to insert array len"); + } _dbus_assert (sub->u.array.len_pos == sub->value_pos - 4); @@ -1651,32 +1813,48 @@ writer_recurse_array (DBusTypeWriter *writer, aligned = _DBUS_ALIGN_VALUE (sub->value_pos, alignment); if (aligned != sub->value_pos) { - if (!_dbus_string_insert_bytes (sub->value_str, - sub->value_pos, - aligned - sub->value_pos, - '\0')) - _dbus_assert_not_reached ("should not have failed to insert alignment padding"); + if (!is_array_append) + { + if (!_dbus_string_insert_bytes (sub->value_str, + sub->value_pos, + aligned - sub->value_pos, + '\0')) + _dbus_assert_not_reached ("should not have failed to insert alignment padding"); + } sub->value_pos = aligned; } -#if RECURSIVE_MARSHAL_TRACE - _dbus_verbose (" type writer %p recurse array done remaining sig '%s' array start_pos = %d len_pos = %d\n", sub, - sub->type_str ? - _dbus_string_get_const_data_len (sub->type_str, sub->type_pos, 0) : - "unknown", - sub->u.array.start_pos, sub->u.array.len_pos); -#endif + sub->u.array.start_pos = sub->value_pos; + + if (is_array_append) + { + dbus_uint32_t len; + + len = _dbus_marshal_read_uint32 (sub->value_str, + sub->u.array.len_pos, + sub->byte_order, NULL); + + sub->value_pos += len; + } } else { /* not enabled, so we won't write the len_pos; set it to -1 to so indicate */ sub->u.array.len_pos = -1; + sub->u.array.start_pos = sub->value_pos; } - sub->u.array.start_pos = sub->value_pos; - _dbus_assert (sub->u.array.start_pos == sub->value_pos); _dbus_assert (sub->u.array.len_pos < sub->u.array.start_pos); + _dbus_assert (is_array_append || sub->u.array.start_pos == sub->value_pos); + +#if RECURSIVE_MARSHAL_WRITE_TRACE + _dbus_verbose (" type writer %p recurse array done remaining sig '%s' array start_pos = %d len_pos = %d value_pos = %d\n", sub, + sub->type_str ? + _dbus_string_get_const_data_len (sub->type_str, sub->type_pos, 0) : + "unknown", + sub->u.array.start_pos, sub->u.array.len_pos, sub->value_pos); +#endif return TRUE; } @@ -1781,7 +1959,8 @@ _dbus_type_writer_recurse_contained_len (DBusTypeWriter *writer, const DBusString *contained_type, int contained_type_start, int contained_type_len, - DBusTypeWriter *sub) + DBusTypeWriter *sub, + dbus_bool_t is_array_append) { writer_recurse_init_and_check (writer, container_type, sub); @@ -1795,7 +1974,7 @@ _dbus_type_writer_recurse_contained_len (DBusTypeWriter *writer, case DBUS_TYPE_ARRAY: return writer_recurse_array (writer, contained_type, contained_type_start, contained_type_len, - sub); + sub, is_array_append); break; case DBUS_TYPE_VARIANT: return writer_recurse_variant (writer, @@ -1827,7 +2006,41 @@ _dbus_type_writer_recurse (DBusTypeWriter *writer, contained_type, contained_type_start, contained_type_len, - sub); + sub, + FALSE); +} + +/** + * Append to an existing array. Essentially, the writer will read an + * existing length at the write location; jump over that length; and + * write new fields. On unrecurse(), the existing length will be + * updated. + * + * @param writer the writer + * @param contained_type element type + * @param contained_type_start position of element type + * @param sub the subwriter to init + * @returns #FALSE if no memory + */ +dbus_bool_t +_dbus_type_writer_append_array (DBusTypeWriter *writer, + const DBusString *contained_type, + int contained_type_start, + DBusTypeWriter *sub) +{ + int contained_type_len; + + if (contained_type) + contained_type_len = find_len_of_complete_type (contained_type, contained_type_start); + else + contained_type_len = 0; + + return _dbus_type_writer_recurse_contained_len (writer, DBUS_TYPE_ARRAY, + contained_type, + contained_type_start, + contained_type_len, + sub, + TRUE); } static int @@ -1845,7 +2058,7 @@ _dbus_type_writer_unrecurse (DBusTypeWriter *writer, _dbus_assert (!writer->type_pos_is_expectation || (writer->type_pos_is_expectation && sub->type_pos_is_expectation)); -#if RECURSIVE_MARSHAL_TRACE +#if RECURSIVE_MARSHAL_WRITE_TRACE _dbus_verbose (" type writer %p unrecurse type_pos = %d value_pos = %d is_expectation = %d container_type = %s\n", writer, writer->type_pos, writer->value_pos, writer->type_pos_is_expectation, _dbus_type_to_string (writer->container_type)); @@ -1872,12 +2085,12 @@ _dbus_type_writer_unrecurse (DBusTypeWriter *writer, sub->u.array.len_pos, len, sub->byte_order); -#if RECURSIVE_MARSHAL_TRACE +#if RECURSIVE_MARSHAL_WRITE_TRACE _dbus_verbose (" filled in sub array len to %u at len_pos %d\n", len, sub->u.array.len_pos); #endif } -#if RECURSIVE_MARSHAL_TRACE +#if RECURSIVE_MARSHAL_WRITE_TRACE else { _dbus_verbose (" not filling in sub array len because we were disabled when we passed the len\n"); @@ -1940,7 +2153,7 @@ _dbus_type_writer_unrecurse (DBusTypeWriter *writer, writer->value_pos = sub->value_pos; -#if RECURSIVE_MARSHAL_TRACE +#if RECURSIVE_MARSHAL_WRITE_TRACE _dbus_verbose (" type writer %p unrecursed type_pos = %d value_pos = %d remaining sig '%s'\n", writer, writer->type_pos, writer->value_pos, writer->type_str ? @@ -1959,7 +2172,7 @@ _dbus_type_writer_write_basic (DBusTypeWriter *writer, dbus_bool_t retval; /* First ensure that our type realloc will succeed */ - if (writer->type_str != NULL) + if (!writer->type_pos_is_expectation && writer->type_str != NULL) { if (!_dbus_string_alloc_space (writer->type_str, 1)) return FALSE; @@ -1976,17 +2189,19 @@ _dbus_type_writer_write_basic (DBusTypeWriter *writer, retval = TRUE; out: -#if RECURSIVE_MARSHAL_TRACE - _dbus_verbose (" type writer %p basic type_pos = %d value_pos = %d is_expectation = %d\n", - writer, writer->type_pos, writer->value_pos, writer->type_pos_is_expectation); +#if RECURSIVE_MARSHAL_WRITE_TRACE + _dbus_verbose (" type writer %p basic type_pos = %d value_pos = %d is_expectation = %d enabled = %d\n", + writer, writer->type_pos, writer->value_pos, writer->type_pos_is_expectation, + writer->enabled); #endif return retval; } /** - * Writes an array of fixed-length basic values, i.e. those that - * are both _dbus_type_is_fixed() and _dbus_type_is_basic(). + * Writes a block of fixed-length basic values, i.e. those that are + * both _dbus_type_is_fixed() and _dbus_type_is_basic(). The block + * must be written inside an array. * * The value parameter should be the address of said array of values, * so e.g. if it's an array of double, pass in "const double**" @@ -1998,7 +2213,7 @@ _dbus_type_writer_write_basic (DBusTypeWriter *writer, * @returns #FALSE if no memory */ dbus_bool_t -_dbus_type_writer_write_fixed_array (DBusTypeWriter *writer, +_dbus_type_writer_write_fixed_multi (DBusTypeWriter *writer, int element_type, const void *value, int n_elements) @@ -2006,13 +2221,19 @@ _dbus_type_writer_write_fixed_array (DBusTypeWriter *writer, _dbus_assert (writer->container_type == DBUS_TYPE_ARRAY); _dbus_assert (_dbus_type_is_fixed (element_type)); _dbus_assert (writer->type_pos_is_expectation); + _dbus_assert (n_elements >= 0); + +#if RECURSIVE_MARSHAL_WRITE_TRACE + _dbus_verbose (" type writer %p entering fixed multi type_pos = %d value_pos = %d n_elements %d\n", + writer, writer->type_pos, writer->value_pos, n_elements); +#endif if (!write_or_verify_typecode (writer, element_type)) _dbus_assert_not_reached ("OOM should not happen if only verifying typecode"); if (writer->enabled) { - if (!_dbus_marshal_write_fixed_array (writer->value_str, + if (!_dbus_marshal_write_fixed_multi (writer->value_str, writer->value_pos, element_type, value, @@ -2022,9 +2243,9 @@ _dbus_type_writer_write_fixed_array (DBusTypeWriter *writer, return FALSE; } -#if RECURSIVE_MARSHAL_TRACE - _dbus_verbose (" type writer %p fixed array type_pos = %d value_pos = %d\n", - writer, writer->type_pos, writer->value_pos); +#if RECURSIVE_MARSHAL_WRITE_TRACE + _dbus_verbose (" type writer %p fixed multi written new type_pos = %d new value_pos = %d n_elements %d\n", + writer, writer->type_pos, writer->value_pos, n_elements); #endif return TRUE; @@ -2040,9 +2261,9 @@ enable_if_after (DBusTypeWriter *writer, if (!writer->enabled && _dbus_type_reader_greater_than (reader, start_after)) { _dbus_type_writer_set_enabled (writer, TRUE); -#if RECURSIVE_MARSHAL_TRACE - _dbus_verbose ("ENABLING writer %p because reader at value_pos %d is after reader at value_pos %d\n", - writer, reader->value_pos, start_after->value_pos); +#if RECURSIVE_MARSHAL_WRITE_TRACE + _dbus_verbose ("ENABLING writer %p at %d because reader at value_pos %d is after reader at value_pos %d\n", + writer, writer->value_pos, reader->value_pos, start_after->value_pos); #endif } @@ -2084,7 +2305,8 @@ writer_write_reader_helper (DBusTypeWriter *writer, const DBusTypeReader *start_after, int start_after_new_pos, int start_after_new_len, - DBusList **fixups) + DBusList **fixups, + dbus_bool_t inside_start_after) { int current_type; @@ -2101,6 +2323,20 @@ writer_write_reader_helper (DBusTypeWriter *writer, dbus_bool_t past_start_after; int reader_array_len_pos; int reader_array_start_pos; + dbus_bool_t this_is_start_after; + + /* type_pos is checked since e.g. in a struct the struct + * and its first field have the same value_pos. + * type_str will differ in reader/start_after for variants + * where type_str is inside the value_str + */ + if (!inside_start_after && start_after && + reader->value_pos == start_after->value_pos && + reader->type_str == start_after->type_str && + reader->type_pos == start_after->type_pos) + this_is_start_after = TRUE; + else + this_is_start_after = FALSE; _dbus_type_reader_recurse (reader, &subreader); @@ -2119,19 +2355,49 @@ writer_write_reader_helper (DBusTypeWriter *writer, _dbus_type_reader_get_signature (&subreader, &sig_str, &sig_start, &sig_len); - enable_if_after (writer, &subreader, start_after); +#if RECURSIVE_MARSHAL_WRITE_TRACE + _dbus_verbose ("about to recurse into %s reader at %d subreader at %d writer at %d start_after reader at %d write target len %d inside_start_after = %d this_is_start_after = %d\n", + _dbus_type_to_string (current_type), + reader->value_pos, + subreader.value_pos, + writer->value_pos, + start_after ? start_after->value_pos : -1, + _dbus_string_get_length (writer->value_str), + inside_start_after, this_is_start_after); +#endif + + if (!inside_start_after && !this_is_start_after) + enable_if_after (writer, &subreader, start_after); enabled_at_recurse = writer->enabled; if (!_dbus_type_writer_recurse_contained_len (writer, current_type, sig_str, sig_start, sig_len, - &subwriter)) + &subwriter, FALSE)) goto oom; +#if RECURSIVE_MARSHAL_WRITE_TRACE + _dbus_verbose ("recursed into subwriter at %d write target len %d\n", + subwriter.value_pos, + _dbus_string_get_length (subwriter.value_str)); +#endif + if (!writer_write_reader_helper (&subwriter, &subreader, start_after, start_after_new_pos, start_after_new_len, - fixups)) + fixups, + inside_start_after || + this_is_start_after)) goto oom; - enable_if_after (writer, &subreader, start_after); +#if RECURSIVE_MARSHAL_WRITE_TRACE + _dbus_verbose ("about to unrecurse from %s subreader at %d writer at %d subwriter at %d write target len %d\n", + _dbus_type_to_string (current_type), + subreader.value_pos, + writer->value_pos, + subwriter.value_pos, + _dbus_string_get_length (writer->value_str)); +#endif + + if (!inside_start_after && !this_is_start_after) + enable_if_after (writer, &subreader, start_after); past_start_after = writer->enabled; if (!_dbus_type_writer_unrecurse (writer, &subwriter)) goto oom; @@ -2151,8 +2417,9 @@ writer_write_reader_helper (DBusTypeWriter *writer, int bytes_before_start_after; int old_len; - /* this is moderately unkosher since we already unrecursed, - * but it works as long as unrecurse doesn't break us on purpose + /* this subwriter access is moderately unkosher since we + * already unrecursed, but it works as long as unrecurse + * doesn't break us on purpose */ bytes_written_after_start_after = writer_get_array_len (&subwriter); @@ -2172,7 +2439,7 @@ writer_write_reader_helper (DBusTypeWriter *writer, if (old_len != fixup.new_len && !append_fixup (fixups, &fixup)) goto oom; -#if RECURSIVE_MARSHAL_TRACE +#if RECURSIVE_MARSHAL_WRITE_TRACE _dbus_verbose ("Generated fixup len_pos_in_reader = %d new_len = %d reader_array_start_pos = %d start_after->value_pos = %d bytes_before_start_after = %d start_after_new_len = %d bytes_written_after_start_after = %d\n", fixup.len_pos_in_reader, fixup.new_len, @@ -2190,11 +2457,31 @@ writer_write_reader_helper (DBusTypeWriter *writer, _dbus_assert (_dbus_type_is_basic (current_type)); +#if RECURSIVE_MARSHAL_WRITE_TRACE + _dbus_verbose ("Reading basic value %s at %d\n", + _dbus_type_to_string (current_type), + reader->value_pos); +#endif + _dbus_type_reader_read_basic (reader, &val); - enable_if_after (writer, reader, start_after); +#if RECURSIVE_MARSHAL_WRITE_TRACE + _dbus_verbose ("Writing basic value %s at %d write target len %d inside_start_after = %d\n", + _dbus_type_to_string (current_type), + writer->value_pos, + _dbus_string_get_length (writer->value_str), + inside_start_after); +#endif + if (!inside_start_after) + enable_if_after (writer, reader, start_after); if (!_dbus_type_writer_write_basic (writer, current_type, &val)) goto oom; +#if RECURSIVE_MARSHAL_WRITE_TRACE + _dbus_verbose ("Wrote basic value %s, new value_pos %d write target len %d\n", + _dbus_type_to_string (current_type), + writer->value_pos, + _dbus_string_get_length (writer->value_str)); +#endif } _dbus_type_reader_next (reader); @@ -2265,7 +2552,7 @@ _dbus_type_writer_write_reader_partial (DBusTypeWriter *writer, if (!writer_write_reader_helper (writer, reader, start_after, start_after_new_pos, start_after_new_len, - fixups)) + fixups, FALSE)) goto oom; _dbus_type_writer_set_enabled (writer, orig_enabled); @@ -2572,6 +2859,15 @@ struct TestTypeNodeClass int seed); dbus_bool_t (* build_signature) (TestTypeNode *node, DBusString *str); + dbus_bool_t (* write_multi) (TestTypeNode *node, + DataBlock *block, + DBusTypeWriter *writer, + int seed, + int count); + dbus_bool_t (* read_multi) (TestTypeNode *node, + DBusTypeReader *reader, + int seed, + int count); }; struct TestTypeNodeContainerClass @@ -2595,6 +2891,15 @@ static dbus_bool_t int32_set_value (TestTypeNode *node, DBusTypeReader *reader, DBusTypeReader *realign_root, int seed); +static dbus_bool_t int32_write_multi (TestTypeNode *node, + DataBlock *block, + DBusTypeWriter *writer, + int seed, + int count); +static dbus_bool_t int32_read_multi (TestTypeNode *node, + DBusTypeReader *reader, + int seed, + int count); static dbus_bool_t int64_write_value (TestTypeNode *node, DataBlock *block, DBusTypeWriter *writer, @@ -2721,7 +3026,9 @@ static const TestTypeNodeClass int32_class = { int32_write_value, int32_read_value, int32_set_value, - NULL + NULL, + int32_write_multi, + int32_read_multi }; static const TestTypeNodeClass uint32_class = { @@ -2733,7 +3040,9 @@ static const TestTypeNodeClass uint32_class = { int32_write_value, /* recycle from int32 */ int32_read_value, /* recycle from int32 */ int32_set_value, /* recycle from int32 */ - NULL + NULL, + int32_write_multi, /* recycle from int32 */ + int32_read_multi /* recycle from int32 */ }; static const TestTypeNodeClass int64_class = { @@ -2745,7 +3054,9 @@ static const TestTypeNodeClass int64_class = { int64_write_value, int64_read_value, int64_set_value, - NULL + NULL, + NULL, /* FIXME */ + NULL /* FIXME */ }; static const TestTypeNodeClass uint64_class = { @@ -2757,7 +3068,9 @@ static const TestTypeNodeClass uint64_class = { int64_write_value, /* recycle from int64 */ int64_read_value, /* recycle from int64 */ int64_set_value, /* recycle from int64 */ - NULL + NULL, + NULL, /* FIXME */ + NULL /* FIXME */ }; static const TestTypeNodeClass string_0_class = { @@ -2769,6 +3082,8 @@ static const TestTypeNodeClass string_0_class = { string_write_value, string_read_value, string_set_value, + NULL, + NULL, NULL }; @@ -2781,6 +3096,8 @@ static const TestTypeNodeClass string_1_class = { string_write_value, string_read_value, string_set_value, + NULL, + NULL, NULL }; @@ -2794,6 +3111,8 @@ static const TestTypeNodeClass string_3_class = { string_write_value, string_read_value, string_set_value, + NULL, + NULL, NULL }; @@ -2807,6 +3126,8 @@ static const TestTypeNodeClass string_8_class = { string_write_value, string_read_value, string_set_value, + NULL, + NULL, NULL }; @@ -2819,7 +3140,9 @@ static const TestTypeNodeClass bool_class = { bool_write_value, bool_read_value, bool_set_value, - NULL + NULL, + NULL, /* FIXME */ + NULL /* FIXME */ }; static const TestTypeNodeClass byte_class = { @@ -2831,7 +3154,9 @@ static const TestTypeNodeClass byte_class = { byte_write_value, byte_read_value, byte_set_value, - NULL + NULL, + NULL, /* FIXME */ + NULL /* FIXME */ }; static const TestTypeNodeClass double_class = { @@ -2843,7 +3168,9 @@ static const TestTypeNodeClass double_class = { double_write_value, double_read_value, double_set_value, - NULL + NULL, + NULL, /* FIXME */ + NULL /* FIXME */ }; static const TestTypeNodeClass object_path_class = { @@ -2855,6 +3182,8 @@ static const TestTypeNodeClass object_path_class = { object_path_write_value, object_path_read_value, object_path_set_value, + NULL, + NULL, NULL }; @@ -2867,6 +3196,8 @@ static const TestTypeNodeClass signature_class = { signature_write_value, signature_read_value, signature_set_value, + NULL, + NULL, NULL }; @@ -2879,7 +3210,9 @@ static const TestTypeNodeClass struct_1_class = { struct_write_value, struct_read_value, struct_set_value, - struct_build_signature + struct_build_signature, + NULL, + NULL }; static const TestTypeNodeClass struct_2_class = { @@ -2891,9 +3224,13 @@ static const TestTypeNodeClass struct_2_class = { struct_write_value, struct_read_value, struct_set_value, - struct_build_signature + struct_build_signature, + NULL, + NULL }; +static dbus_bool_t arrays_write_fixed_in_blocks = FALSE; + static const TestTypeNodeClass array_0_class = { DBUS_TYPE_ARRAY, sizeof (TestTypeNodeContainer), @@ -2903,7 +3240,9 @@ static const TestTypeNodeClass array_0_class = { array_write_value, array_read_value, array_set_value, - array_build_signature + array_build_signature, + NULL, + NULL }; static const TestTypeNodeClass array_1_class = { @@ -2915,7 +3254,9 @@ static const TestTypeNodeClass array_1_class = { array_write_value, array_read_value, array_set_value, - array_build_signature + array_build_signature, + NULL, + NULL }; static const TestTypeNodeClass array_2_class = { @@ -2927,7 +3268,9 @@ static const TestTypeNodeClass array_2_class = { array_write_value, array_read_value, array_set_value, - array_build_signature + array_build_signature, + NULL, + NULL }; static const TestTypeNodeClass array_9_class = { @@ -2939,7 +3282,9 @@ static const TestTypeNodeClass array_9_class = { array_write_value, array_read_value, array_set_value, - array_build_signature + array_build_signature, + NULL, + NULL }; static const TestTypeNodeClass variant_class = { @@ -2951,6 +3296,8 @@ static const TestTypeNodeClass variant_class = { variant_write_value, variant_read_value, variant_set_value, + NULL, + NULL, NULL }; @@ -3100,6 +3447,40 @@ node_append_child (TestTypeNode *node, return TRUE; } +static dbus_bool_t +node_write_multi (TestTypeNode *node, + DataBlock *block, + DBusTypeWriter *writer, + int seed, + int n_copies) +{ + dbus_bool_t retval; + + _dbus_assert (node->klass->write_multi != NULL); + retval = (* node->klass->write_multi) (node, block, writer, seed, n_copies); + +#if 0 + /* Handy to see where things break, but too expensive to do all the time */ + data_block_verify (block); +#endif + + return retval; +} + +static dbus_bool_t +node_read_multi (TestTypeNode *node, + DBusTypeReader *reader, + int seed, + int n_copies) +{ + _dbus_assert (node->klass->read_multi != NULL); + + if (!(* node->klass->read_multi) (node, reader, seed, n_copies)) + return FALSE; + + return TRUE; +} + static int n_iterations_completed_total = 0; static int n_iterations_completed_this_test = 0; static int n_iterations_expected_this_test = 0; @@ -3304,6 +3685,102 @@ run_test_set_values (NodeIterationData *nid) return retval; } +static dbus_bool_t +run_test_delete_values (NodeIterationData *nid) +{ + DBusTypeReader reader; + dbus_bool_t retval; + int t; + + _dbus_verbose ("%s\n", _DBUS_FUNCTION_NAME); + + retval = FALSE; + + data_block_init_reader_writer (nid->block, + &reader, NULL); + + while ((t = _dbus_type_reader_get_current_type (&reader)) != DBUS_TYPE_INVALID) + { + /* Right now, deleting only works on array elements. We delete + * all array elements, and then verify that there aren't any + * left. + */ + if (t == DBUS_TYPE_ARRAY) + { + DBusTypeReader array; + int n_elements; + int elem_type; + + _dbus_type_reader_recurse (&reader, &array); + n_elements = 0; + while (_dbus_type_reader_get_current_type (&array) != DBUS_TYPE_INVALID) + { + n_elements += 1; + _dbus_type_reader_next (&array); + } + + /* reset to start of array */ + _dbus_type_reader_recurse (&reader, &array); + _dbus_verbose ("recursing into deletion loop reader.value_pos = %d array.value_pos = %d array.u.start_pos = %d\n", + reader.value_pos, array.value_pos, array.u.array.start_pos); + while ((elem_type = _dbus_type_reader_get_current_type (&array)) != DBUS_TYPE_INVALID) + { + /* We don't want to always delete from the same part of the array. */ + static int cycle = 0; + int elem; + + _dbus_assert (n_elements > 0); + _dbus_assert (!_dbus_type_reader_array_is_empty (&reader)); + + elem = cycle; + if (elem == 3 || elem >= n_elements) /* end of array */ + elem = n_elements - 1; + + _dbus_verbose ("deleting array element %d of %d type %s cycle %d reader pos %d elem pos %d\n", + elem, n_elements, _dbus_type_to_string (elem_type), + cycle, reader.value_pos, array.value_pos); + while (elem > 0) + { + if (!_dbus_type_reader_next (&array)) + _dbus_assert_not_reached ("should have had another element\n"); + --elem; + } + + if (!_dbus_type_reader_delete (&array, &reader)) + goto out; + + n_elements -= 1; + + /* reset */ + _dbus_type_reader_recurse (&reader, &array); + + if (cycle > 2) + cycle = 0; + else + cycle += 1; + } + } + _dbus_type_reader_next (&reader); + } + + /* Check that there are no array elements left */ + data_block_init_reader_writer (nid->block, + &reader, NULL); + + while ((t = _dbus_type_reader_get_current_type (&reader)) != DBUS_TYPE_INVALID) + { + if (t == DBUS_TYPE_ARRAY) + _dbus_assert (_dbus_type_reader_array_is_empty (&reader)); + + _dbus_type_reader_next (&reader); + } + + retval = TRUE; + + out: + return retval; +} + static dbus_bool_t run_test_nodes_iteration (void *data) { @@ -3376,6 +3853,9 @@ run_test_nodes_iteration (void *data) if (!run_test_set_values (nid)) goto out; + if (!run_test_delete_values (nid)) + goto out; + if (!run_test_copy (nid)) goto out; @@ -3625,6 +4105,20 @@ make_and_run_test_nodes (void) } } + start_next_test ("Each value by itself with arrays as blocks %d iterations\n", N_VALUES); + arrays_write_fixed_in_blocks = TRUE; + { + TestTypeNode *node; + i = 0; + while ((node = value_generator (&i))) + { + run_test_nodes (&node, 1); + + node_destroy (node); + } + } + arrays_write_fixed_in_blocks = FALSE; + start_next_test ("All values in one big toplevel %d iteration\n", 1); { TestTypeNode *nodes[N_VALUES]; @@ -3668,10 +4162,19 @@ make_and_run_test_nodes (void) make_and_run_values_inside_container (container_klass, 1); } - n_iterations_completed_this_test = 0; - n_iterations_expected_this_test = N_CONTAINERS * N_VALUES; - _dbus_verbose (">>> >>> Each container of same container of each value %d iterations\n", - n_iterations_completed_this_test); + start_next_test ("Each container containing each value with arrays as blocks %d iterations\n", + N_CONTAINERS * N_VALUES); + arrays_write_fixed_in_blocks = TRUE; + for (i = 0; i < N_CONTAINERS; i++) + { + const TestTypeNodeClass *container_klass = container_nodes[i]; + + make_and_run_values_inside_container (container_klass, 1); + } + arrays_write_fixed_in_blocks = FALSE; + + start_next_test ("Each container of same container of each value %d iterations\n", + N_CONTAINERS * N_VALUES); for (i = 0; i < N_CONTAINERS; i++) { const TestTypeNodeClass *container_klass = container_nodes[i]; @@ -3865,8 +4368,6 @@ make_and_run_test_nodes (void) TEST_OOM_HANDLING ? "was" : "was not"); } -dbus_bool_t _dbus_marshal_recursive_test (void); - dbus_bool_t _dbus_marshal_recursive_test (void) { @@ -3875,20 +4376,6 @@ _dbus_marshal_recursive_test (void) return TRUE; } -#if 1 -dbus_bool_t _dbus_marshal_test (void); -int -main (int argc, char **argv) -{ - _dbus_marshal_test (); - - _dbus_marshal_recursive_test (); - - return 0; -} -#endif /* main() */ - - /* * * @@ -3897,6 +4384,7 @@ main (int argc, char **argv) * * */ +#define MAX_MULTI_COUNT 5 #define SAMPLE_INT32 12345678 @@ -3986,6 +4474,55 @@ int32_set_value (TestTypeNode *node, realign_root); } +static dbus_bool_t +int32_write_multi (TestTypeNode *node, + DataBlock *block, + DBusTypeWriter *writer, + int seed, + int count) +{ + /* also used for uint32 */ + dbus_int32_t values[MAX_MULTI_COUNT]; + dbus_int32_t *v_ARRAY_INT32 = values; + int i; + + for (i = 0; i < count; ++i) + values[i] = int32_from_seed (seed + i); + + return _dbus_type_writer_write_fixed_multi (writer, + node->klass->typecode, + &v_ARRAY_INT32, count); +} + +static dbus_bool_t +int32_read_multi (TestTypeNode *node, + DBusTypeReader *reader, + int seed, + int count) +{ + /* also used for uint32 */ + dbus_int32_t *values; + int n_elements; + int i; + + check_expected_type (reader, node->klass->typecode); + + _dbus_type_reader_read_fixed_multi (reader, + &values, + &n_elements); + + if (n_elements != count) + _dbus_warn ("got %d elements expected %d\n", n_elements, count); + _dbus_assert (n_elements == count); + + for (i = 0; i < count; i++) + _dbus_assert (_dbus_unpack_int32 (reader->byte_order, + (const unsigned char*)values + (i * 4)) == + int32_from_seed (seed + i)); + + return TRUE; +} + #ifdef DBUS_HAVE_INT64 static dbus_int64_t int64_from_seed (int seed) @@ -4162,6 +4699,15 @@ string_set_value (TestTypeNode *node, string_from_seed (buf, node->klass->subclass_detail, seed); +#if RECURSIVE_MARSHAL_WRITE_TRACE + { + const char *old; + _dbus_type_reader_read_basic (reader, &old); + _dbus_verbose ("SETTING new string '%s' len %d in place of '%s' len %d\n", + v_string, strlen (v_string), old, strlen (old)); + } +#endif + return _dbus_type_reader_set_basic (reader, &v_string, realign_root); @@ -4686,6 +5232,8 @@ array_write_value (TestTypeNode *node, DBusString element_signature; int i; int n_copies; + int element_type; + TestTypeNode *child; n_copies = node->klass->subclass_detail; @@ -4696,33 +5244,47 @@ array_write_value (TestTypeNode *node, if (!_dbus_string_init (&element_signature)) return FALSE; - if (!node_build_signature (_dbus_list_get_first (&container->children), + child = _dbus_list_get_first (&container->children); + + if (!node_build_signature (child, &element_signature)) goto oom; + element_type = first_type_in_signature (&element_signature, 0); + if (!_dbus_type_writer_recurse (writer, DBUS_TYPE_ARRAY, &element_signature, 0, &sub)) goto oom; - i = 0; - while (i < n_copies) + if (arrays_write_fixed_in_blocks && + _dbus_type_is_fixed (element_type) && + child->klass->write_multi) { - DBusList *link; - - link = _dbus_list_get_first_link (&container->children); - while (link != NULL) + if (!node_write_multi (child, block, &sub, seed, n_copies)) + goto oom; + } + else + { + i = 0; + while (i < n_copies) { - TestTypeNode *child = link->data; - DBusList *next = _dbus_list_get_next_link (&container->children, link); + DBusList *link; - if (!node_write_value (child, block, &sub, seed + i)) - goto oom; + link = _dbus_list_get_first_link (&container->children); + while (link != NULL) + { + TestTypeNode *child = link->data; + DBusList *next = _dbus_list_get_next_link (&container->children, link); - link = next; - } + if (!node_write_value (child, block, &sub, seed + i)) + goto oom; - ++i; + link = next; + } + + ++i; + } } if (!_dbus_type_writer_unrecurse (writer, &sub)) @@ -4747,48 +5309,64 @@ array_read_or_set_value (TestTypeNode *node, DBusTypeReader sub; int i; int n_copies; + TestTypeNode *child; n_copies = node->klass->subclass_detail; check_expected_type (reader, DBUS_TYPE_ARRAY); + child = _dbus_list_get_first (&container->children); + if (n_copies > 0) { _dbus_assert (!_dbus_type_reader_array_is_empty (reader)); _dbus_type_reader_recurse (reader, &sub); - i = 0; - while (i < n_copies) + if (realign_root == NULL && arrays_write_fixed_in_blocks && + _dbus_type_is_fixed (_dbus_type_reader_get_array_type (reader)) && + child->klass->read_multi) { - DBusList *link; - - link = _dbus_list_get_first_link (&container->children); - while (link != NULL) + if (!node_read_multi (child, &sub, seed, n_copies)) + return FALSE; + } + else + { + i = 0; + while (i < n_copies) { - TestTypeNode *child = link->data; - DBusList *next = _dbus_list_get_next_link (&container->children, link); + DBusList *link; - if (realign_root == NULL) - { - if (!node_read_value (child, &sub, seed + i)) - return FALSE; - } - else + link = _dbus_list_get_first_link (&container->children); + while (link != NULL) { - if (!node_set_value (child, &sub, realign_root, seed + i)) - return FALSE; + TestTypeNode *child = link->data; + DBusList *next = _dbus_list_get_next_link (&container->children, link); + + _dbus_assert (child->klass->typecode == + _dbus_type_reader_get_array_type (reader)); + + if (realign_root == NULL) + { + if (!node_read_value (child, &sub, seed + i)) + return FALSE; + } + else + { + if (!node_set_value (child, &sub, realign_root, seed + i)) + return FALSE; + } + + if (i == (n_copies - 1) && next == NULL) + NEXT_EXPECTING_FALSE (&sub); + else + NEXT_EXPECTING_TRUE (&sub); + + link = next; } - if (i == (n_copies - 1) && next == NULL) - NEXT_EXPECTING_FALSE (&sub); - else - NEXT_EXPECTING_TRUE (&sub); - - link = next; + ++i; } - - ++i; } } else diff --git a/dbus/dbus-marshal-recursive.h b/dbus/dbus-marshal-recursive.h index 765ee56b..203e2f8c 100644 --- a/dbus/dbus-marshal-recursive.h +++ b/dbus/dbus-marshal-recursive.h @@ -1,7 +1,7 @@ /* -*- mode: C; c-file-style: "gnu" -*- */ /* dbus-marshal-recursive.h Marshalling routines for recursive types * - * Copyright (C) 2004 Red Hat, Inc. + * Copyright (C) 2004, 2005 Red Hat, Inc. * * Licensed under the Academic Free License version 2.1 * @@ -148,12 +148,16 @@ void _dbus_type_reader_init_types_only_from_mark (DBusTypeReader * void _dbus_type_reader_save_mark (const DBusTypeReader *reader, DBusTypeMark *mark); int _dbus_type_reader_get_current_type (const DBusTypeReader *reader); +int _dbus_type_reader_get_array_type (const DBusTypeReader *reader); +int _dbus_type_reader_get_value_pos (const DBusTypeReader *reader); dbus_bool_t _dbus_type_reader_array_is_empty (const DBusTypeReader *reader); void _dbus_type_reader_read_basic (const DBusTypeReader *reader, void *value); -void _dbus_type_reader_read_fixed_array (const DBusTypeReader *reader, +void _dbus_type_reader_read_fixed_multi (const DBusTypeReader *reader, void *value, int *n_elements); +void _dbus_type_reader_read_raw (const DBusTypeReader *reader, + const unsigned char **value_location); void _dbus_type_reader_recurse (DBusTypeReader *reader, DBusTypeReader *subreader); dbus_bool_t _dbus_type_reader_next (DBusTypeReader *reader); @@ -176,6 +180,14 @@ void _dbus_type_writer_init (DBusTypeWriter *write int type_pos, DBusString *value_str, int value_pos); +void _dbus_type_writer_init_types_delayed (DBusTypeWriter *writer, + int byte_order, + DBusString *value_str, + int value_pos); +void _dbus_type_writer_add_types (DBusTypeWriter *writer, + DBusString *type_str, + int type_pos); +void _dbus_type_writer_remove_types (DBusTypeWriter *writer); void _dbus_type_writer_init_values_only (DBusTypeWriter *writer, int byte_order, const DBusString *type_str, @@ -185,7 +197,7 @@ void _dbus_type_writer_init_values_only (DBusTypeWriter *write dbus_bool_t _dbus_type_writer_write_basic (DBusTypeWriter *writer, int type, const void *value); -dbus_bool_t _dbus_type_writer_write_fixed_array (DBusTypeWriter *writer, +dbus_bool_t _dbus_type_writer_write_fixed_multi (DBusTypeWriter *writer, int element_type, const void *value, int n_elements); @@ -196,6 +208,10 @@ dbus_bool_t _dbus_type_writer_recurse (DBusTypeWriter *write DBusTypeWriter *sub); dbus_bool_t _dbus_type_writer_unrecurse (DBusTypeWriter *writer, DBusTypeWriter *sub); +dbus_bool_t _dbus_type_writer_append_array (DBusTypeWriter *writer, + const DBusString *contained_type, + int contained_type_start, + DBusTypeWriter *sub); dbus_bool_t _dbus_type_writer_write_reader (DBusTypeWriter *writer, DBusTypeReader *reader); dbus_bool_t _dbus_type_writer_write_reader_partial (DBusTypeWriter *writer, @@ -208,5 +224,4 @@ void _dbus_type_writer_set_enabled (DBusTypeWriter *write dbus_bool_t enabled); - #endif /* DBUS_MARSHAL_RECURSIVE_H */ diff --git a/dbus/dbus-marshal-validate.c b/dbus/dbus-marshal-validate.c index 97041189..cf409758 100644 --- a/dbus/dbus-marshal-validate.c +++ b/dbus/dbus-marshal-validate.c @@ -23,6 +23,7 @@ #include "dbus-internals.h" #include "dbus-marshal-validate.h" +#include "dbus-marshal-recursive.h" /** * @addtogroup DBusMarshal @@ -30,15 +31,1279 @@ * @{ */ +/** + * Verifies that the range of type_str from type_pos to type_end is a + * valid signature. If this function returns #TRUE, it will be safe + * to iterate over the signature with a types-only #DBusTypeReader. + * The range passed in should NOT include the terminating + * nul/DBUS_TYPE_INVALID. + * + * @param type_str the string + * @param type_pos where the typecodes start + * @param len length of typecodes + * @returns #DBUS_VALID if valid, reason why invalid otherwise + */ +DBusValidity +_dbus_validate_signature_with_reason (const DBusString *type_str, + int type_pos, + int len) +{ + const unsigned char *p; + const unsigned char *end; + int last; + int struct_depth; + int array_depth; + + _dbus_assert (type_str != NULL); + _dbus_assert (type_pos < _DBUS_INT_MAX - len); + _dbus_assert (len >= 0); + _dbus_assert (type_pos >= 0); + + if (len > DBUS_MAXIMUM_SIGNATURE_LENGTH) + return DBUS_INVALID_SIGNATURE_TOO_LONG; + + p = _dbus_string_get_const_data_len (type_str, type_pos, 0); + end = _dbus_string_get_const_data_len (type_str, type_pos + len, 0); + struct_depth = 0; + array_depth = 0; + last = DBUS_TYPE_INVALID; + + while (p != end) + { + switch (*p) + { + case DBUS_TYPE_BYTE: + case DBUS_TYPE_BOOLEAN: + case DBUS_TYPE_INT32: + case DBUS_TYPE_UINT32: + case DBUS_TYPE_INT64: + case DBUS_TYPE_UINT64: + case DBUS_TYPE_DOUBLE: + case DBUS_TYPE_STRING: + case DBUS_TYPE_OBJECT_PATH: + case DBUS_TYPE_SIGNATURE: + case DBUS_TYPE_VARIANT: + break; + + case DBUS_TYPE_ARRAY: + array_depth += 1; + if (array_depth > DBUS_MAXIMUM_TYPE_RECURSION_DEPTH) + return DBUS_INVALID_EXCEEDED_MAXIMUM_ARRAY_RECURSION; + break; + + case DBUS_STRUCT_BEGIN_CHAR: + struct_depth += 1; + + if (struct_depth > DBUS_MAXIMUM_TYPE_RECURSION_DEPTH) + return DBUS_INVALID_EXCEEDED_MAXIMUM_STRUCT_RECURSION; + break; + + case DBUS_STRUCT_END_CHAR: + if (struct_depth == 0) + return DBUS_INVALID_STRUCT_ENDED_BUT_NOT_STARTED; + + if (last == DBUS_STRUCT_BEGIN_CHAR) + return DBUS_INVALID_STRUCT_HAS_NO_FIELDS; + + struct_depth -= 1; + break; + + case DBUS_TYPE_STRUCT: /* doesn't appear in signatures */ + default: + return DBUS_INVALID_UNKNOWN_TYPECODE; + } + + if (*p != DBUS_TYPE_ARRAY) + array_depth = 0; + + last = *p; + ++p; + } + + if (array_depth > 0) + return DBUS_INVALID_MISSING_ARRAY_ELEMENT_TYPE; + + if (struct_depth > 0) + return DBUS_INVALID_STRUCT_STARTED_BUT_NOT_ENDED; + + return DBUS_VALID; +} + +static DBusValidity +validate_body_helper (DBusTypeReader *reader, + int byte_order, + dbus_bool_t walk_reader_to_end, + const unsigned char *p, + const unsigned char *end, + const unsigned char **new_p) +{ + int current_type; + + while ((current_type = _dbus_type_reader_get_current_type (reader)) != DBUS_TYPE_INVALID) + { + const unsigned char *a; + int alignment; + + _dbus_verbose (" validating value of type %s type reader %p type_pos %d p %p end %p %d remain\n", + _dbus_type_to_string (current_type), reader, reader->type_pos, p, end, + (int) (end - p)); + + /* Guarantee that p has one byte to look at */ + if (p == end) + return DBUS_INVALID_NOT_ENOUGH_DATA; + + switch (current_type) + { + case DBUS_TYPE_BYTE: + ++p; + break; + + case DBUS_TYPE_BOOLEAN: + if (!(*p == 0 || *p == 1)) + return DBUS_INVALID_BOOLEAN_NOT_ZERO_OR_ONE; + ++p; + break; + + case DBUS_TYPE_INT32: + case DBUS_TYPE_UINT32: + case DBUS_TYPE_INT64: + case DBUS_TYPE_UINT64: + case DBUS_TYPE_DOUBLE: + alignment = _dbus_type_get_alignment (current_type); + a = _DBUS_ALIGN_ADDRESS (p, alignment); + if (a >= end) + return DBUS_INVALID_NOT_ENOUGH_DATA; + while (p != a) + { + if (*p != '\0') + return DBUS_INVALID_ALIGNMENT_PADDING_NOT_NUL; + ++p; + } + p += alignment; + break; + + case DBUS_TYPE_ARRAY: + case DBUS_TYPE_STRING: + case DBUS_TYPE_OBJECT_PATH: + { + dbus_uint32_t claimed_len; + + a = _DBUS_ALIGN_ADDRESS (p, 4); + if (a + 4 >= end) + return DBUS_INVALID_NOT_ENOUGH_DATA; + while (p != a) + { + if (*p != '\0') + return DBUS_INVALID_ALIGNMENT_PADDING_NOT_NUL; + ++p; + } + + claimed_len = _dbus_unpack_uint32 (byte_order, p); + p += 4; + + if (current_type == DBUS_TYPE_ARRAY) + { + int array_elem_type = _dbus_type_reader_get_array_type (reader); + alignment = _dbus_type_get_alignment (array_elem_type); + p = _DBUS_ALIGN_ADDRESS (p, alignment); + } + + if (claimed_len > (unsigned long) (end - p)) + return DBUS_INVALID_STRING_LENGTH_OUT_OF_BOUNDS; + + if (current_type == DBUS_TYPE_OBJECT_PATH) + { + DBusString str; + _dbus_string_init_const_len (&str, p, claimed_len); + if (!_dbus_validate_path (&str, 0, + _dbus_string_get_length (&str))) + return DBUS_INVALID_BAD_PATH; + + p += claimed_len; + } + else if (current_type == DBUS_TYPE_STRING) + { + DBusString str; + _dbus_string_init_const_len (&str, p, claimed_len); + if (!_dbus_string_validate_utf8 (&str, 0, + _dbus_string_get_length (&str))) + return DBUS_INVALID_BAD_UTF8_IN_STRING; + + p += claimed_len; + } + else if (current_type == DBUS_TYPE_ARRAY && claimed_len > 0) + { + DBusTypeReader sub; + DBusValidity validity; + const unsigned char *array_end; + + /* Remember that the reader is types only, so we can't + * use it to iterate over elements. It stays the same + * for all elements. + */ + _dbus_type_reader_recurse (reader, &sub); + + array_end = p + claimed_len; + + while (p < array_end) + { + validity = validate_body_helper (&sub, byte_order, FALSE, p, end, &p); + if (validity != DBUS_VALID) + return validity; + } + + if (p != array_end) + return DBUS_INVALID_ARRAY_LENGTH_INCORRECT; + } + + /* check nul termination */ + if (current_type != DBUS_TYPE_ARRAY) + { + if (p == end) + return DBUS_INVALID_NOT_ENOUGH_DATA; + + if (*p != '\0') + return DBUS_INVALID_STRING_MISSING_NUL; + ++p; + } + } + break; + + case DBUS_TYPE_SIGNATURE: + { + dbus_uint32_t claimed_len; + DBusString str; + + claimed_len = *p; + ++p; + + /* 1 is for nul termination */ + if (claimed_len + 1 > (unsigned long) (end - p)) + return DBUS_INVALID_SIGNATURE_LENGTH_OUT_OF_BOUNDS; + + _dbus_string_init_const_len (&str, p, claimed_len); + if (!_dbus_validate_signature (&str, 0, + _dbus_string_get_length (&str))) + return DBUS_INVALID_BAD_SIGNATURE; + + p += claimed_len; + + _dbus_assert (p < end); + if (*p != DBUS_TYPE_INVALID) + return DBUS_INVALID_SIGNATURE_MISSING_NUL; + + ++p; + + _dbus_verbose ("p = %p end = %p claimed_len %u\n", p, end, claimed_len); + } + break; + + case DBUS_TYPE_VARIANT: + { + /* 1 byte sig len, sig typecodes, align to 8-boundary, values. */ + /* In addition to normal signature validation, we need to be sure + * the signature contains only a single (possibly container) type. + */ + dbus_uint32_t claimed_len; + DBusString sig; + DBusTypeReader sub; + DBusValidity validity; + + claimed_len = *p; + ++p; + + /* + 1 for nul */ + if (claimed_len + 1 > (unsigned long) (end - p)) + return DBUS_INVALID_VARIANT_SIGNATURE_LENGTH_OUT_OF_BOUNDS; + + _dbus_string_init_const_len (&sig, p, claimed_len); + if (!_dbus_validate_signature (&sig, 0, + _dbus_string_get_length (&sig))) + return DBUS_INVALID_VARIANT_SIGNATURE_BAD; + + p += claimed_len; + + if (*p != DBUS_TYPE_INVALID) + return DBUS_INVALID_VARIANT_SIGNATURE_MISSING_NUL; + ++p; + + a = _DBUS_ALIGN_ADDRESS (p, 8); + if (a > end) + return DBUS_INVALID_NOT_ENOUGH_DATA; + while (p != a) + { + if (*p != '\0') + return DBUS_INVALID_ALIGNMENT_PADDING_NOT_NUL; + ++p; + } + + _dbus_type_reader_init_types_only (&sub, &sig, 0); + + if (_dbus_type_reader_get_current_type (&sub) == DBUS_TYPE_INVALID) + return DBUS_INVALID_VARIANT_SIGNATURE_EMPTY; + + validity = validate_body_helper (&sub, byte_order, FALSE, p, end, &p); + if (validity != DBUS_VALID) + return validity; + + if (_dbus_type_reader_next (&sub)) + return DBUS_INVALID_VARIANT_SIGNATURE_SPECIFIES_MULTIPLE_VALUES; + + _dbus_assert (_dbus_type_reader_get_current_type (&sub) == DBUS_TYPE_INVALID); + } + break; + + case DBUS_TYPE_STRUCT: + { + DBusTypeReader sub; + DBusValidity validity; + + a = _DBUS_ALIGN_ADDRESS (p, 8); + if (a > end) + return DBUS_INVALID_NOT_ENOUGH_DATA; + while (p != a) + { + if (*p != '\0') + return DBUS_INVALID_ALIGNMENT_PADDING_NOT_NUL; + ++p; + } + + _dbus_type_reader_recurse (reader, &sub); + + validity = validate_body_helper (&sub, byte_order, TRUE, p, end, &p); + if (validity != DBUS_VALID) + return validity; + } + break; + + default: + _dbus_assert_not_reached ("invalid typecode in supposedly-validated signature"); + break; + } + + _dbus_verbose (" validated value of type %s type reader %p type_pos %d p %p end %p %d remain\n", + _dbus_type_to_string (current_type), reader, reader->type_pos, p, end, + (int) (end - p)); + + if (p > end) + { + _dbus_verbose ("not enough data!!! p = %p end = %p end-p = %d\n", + p, end, (int) (end - p)); + return DBUS_INVALID_NOT_ENOUGH_DATA; + } + + if (walk_reader_to_end) + _dbus_type_reader_next (reader); + else + break; + } + + if (new_p) + *new_p = p; + + return DBUS_VALID; +} + +/** + * Verifies that the range of value_str from value_pos to value_end is + * a legitimate value of type expected_signature. If this function + * returns #TRUE, it will be safe to iterate over the values with + * #DBusTypeReader. The signature is assumed to be already valid. + * + * If bytes_remaining is not #NULL, then leftover bytes will be stored + * there and #DBUS_VALID returned. If it is #NULL, then + * #DBUS_INVALID_TOO_MUCH_DATA will be returned if bytes are left + * over. + * + * @param expected_signature the expected types in the value_str + * @param expected_signature_start where in expected_signature is the signature + * @param byte_order the byte order + * @param bytes_remaining place to store leftover bytes + * @param value_pos where the values start + * @param len length of values after value_pos + * @returns #DBUS_VALID if valid, reason why invalid otherwise + */ +DBusValidity +_dbus_validate_body_with_reason (const DBusString *expected_signature, + int expected_signature_start, + int byte_order, + int *bytes_remaining, + const DBusString *value_str, + int value_pos, + int len) +{ + DBusTypeReader reader; + const unsigned char *p; + const unsigned char *end; + DBusValidity validity; + + _dbus_assert (len >= 0); + _dbus_assert (value_pos >= 0); + _dbus_assert (value_pos <= _dbus_string_get_length (value_str) - len); + + _dbus_verbose ("validating body from pos %d len %d sig '%s'\n", + value_pos, len, _dbus_string_get_const_data_len (expected_signature, + expected_signature_start, + 0)); + + _dbus_type_reader_init_types_only (&reader, + expected_signature, expected_signature_start); + + p = _dbus_string_get_const_data_len (value_str, value_pos, len); + end = p + len; + + validity = validate_body_helper (&reader, byte_order, TRUE, p, end, &p); + if (validity != DBUS_VALID) + return validity; + + if (p < end) + { + if (bytes_remaining) + *bytes_remaining = end - p; + else + return DBUS_INVALID_TOO_MUCH_DATA; + } + + return DBUS_VALID; +} + +/** + * Checks that the given range of the string is a valid object path + * name in the D-BUS protocol. Part of the validation ensures that + * the object path contains only ASCII. + * + * @todo this is inconsistent with most of DBusString in that + * it allows a start,len range that extends past the string end. + * + * @todo change spec to disallow more things, such as spaces in the + * path name + * + * @param str the string + * @param start first byte index to check + * @param len number of bytes to check + * @returns #TRUE if the byte range exists and is a valid name + */ +dbus_bool_t +_dbus_validate_path (const DBusString *str, + int start, + int len) +{ + const unsigned char *s; + const unsigned char *end; + const unsigned char *last_slash; + + _dbus_assert (start >= 0); + _dbus_assert (len >= 0); + _dbus_assert (start <= _dbus_string_get_length (str)); + + if (len > _dbus_string_get_length (str) - start) + return FALSE; + + if (len == 0) + return FALSE; + + s = _dbus_string_get_const_data (str) + start; + end = s + len; + + if (*s != '/') + return FALSE; + last_slash = s; + ++s; + + while (s != end) + { + if (*s == '/') + { + if ((s - last_slash) < 2) + return FALSE; /* no empty path components allowed */ + + last_slash = s; + } + else + { + if (_DBUS_UNLIKELY (!_DBUS_ISASCII (*s))) + return FALSE; + } + + ++s; + } + + if ((end - last_slash) < 2 && + len > 1) + return FALSE; /* trailing slash not allowed unless the string is "/" */ + + return TRUE; +} + +/** + * Determine wether the given charater is valid as the first charater + * in a name. + */ +#define VALID_INITIAL_NAME_CHARACTER(c) \ + ( ((c) >= 'A' && (c) <= 'Z') || \ + ((c) >= 'a' && (c) <= 'z') || \ + ((c) == '_') ) + +/** + * Determine wether the given charater is valid as a second or later + * character in a name + */ +#define VALID_NAME_CHARACTER(c) \ + ( ((c) >= '0' && (c) <= '9') || \ + ((c) >= 'A' && (c) <= 'Z') || \ + ((c) >= 'a' && (c) <= 'z') || \ + ((c) == '_') ) + +/** + * Checks that the given range of the string is a valid interface name + * in the D-BUS protocol. This includes a length restriction and an + * ASCII subset, see the specification. + * + * @todo this is inconsistent with most of DBusString in that + * it allows a start,len range that extends past the string end. + * + * @param str the string + * @param start first byte index to check + * @param len number of bytes to check + * @returns #TRUE if the byte range exists and is a valid name + */ +dbus_bool_t +_dbus_validate_interface (const DBusString *str, + int start, + int len) +{ + const unsigned char *s; + const unsigned char *end; + const unsigned char *iface; + const unsigned char *last_dot; + + _dbus_assert (start >= 0); + _dbus_assert (len >= 0); + _dbus_assert (start <= _dbus_string_get_length (str)); + + if (len > _dbus_string_get_length (str) - start) + return FALSE; + + if (len > DBUS_MAXIMUM_NAME_LENGTH) + return FALSE; + + if (len == 0) + return FALSE; + + last_dot = NULL; + iface = _dbus_string_get_const_data (str) + start; + end = iface + len; + s = iface; + + /* check special cases of first char so it doesn't have to be done + * in the loop. Note we know len > 0 + */ + if (_DBUS_UNLIKELY (*s == '.')) /* disallow starting with a . */ + return FALSE; + else if (_DBUS_UNLIKELY (!VALID_INITIAL_NAME_CHARACTER (*s))) + return FALSE; + else + ++s; + + while (s != end) + { + if (*s == '.') + { + if (_DBUS_UNLIKELY ((s + 1) == end)) + return FALSE; + else if (_DBUS_UNLIKELY (!VALID_INITIAL_NAME_CHARACTER (*(s + 1)))) + return FALSE; + last_dot = s; + ++s; /* we just validated the next char, so skip two */ + } + else if (_DBUS_UNLIKELY (!VALID_NAME_CHARACTER (*s))) + { + return FALSE; + } + + ++s; + } + + if (_DBUS_UNLIKELY (last_dot == NULL)) + return FALSE; + + return TRUE; +} + +/** + * Checks that the given range of the string is a valid member name + * in the D-BUS protocol. This includes a length restriction, etc., + * see the specification. + * + * @todo this is inconsistent with most of DBusString in that + * it allows a start,len range that extends past the string end. + * + * @param str the string + * @param start first byte index to check + * @param len number of bytes to check + * @returns #TRUE if the byte range exists and is a valid name + */ +dbus_bool_t +_dbus_validate_member (const DBusString *str, + int start, + int len) +{ + const unsigned char *s; + const unsigned char *end; + const unsigned char *member; + + _dbus_assert (start >= 0); + _dbus_assert (len >= 0); + _dbus_assert (start <= _dbus_string_get_length (str)); + + if (len > _dbus_string_get_length (str) - start) + return FALSE; + + if (len > DBUS_MAXIMUM_NAME_LENGTH) + return FALSE; + + if (len == 0) + return FALSE; + + member = _dbus_string_get_const_data (str) + start; + end = member + len; + s = member; + + /* check special cases of first char so it doesn't have to be done + * in the loop. Note we know len > 0 + */ + + if (_DBUS_UNLIKELY (!VALID_INITIAL_NAME_CHARACTER (*s))) + return FALSE; + else + ++s; + + while (s != end) + { + if (_DBUS_UNLIKELY (!VALID_NAME_CHARACTER (*s))) + { + return FALSE; + } + + ++s; + } + + return TRUE; +} + +/** + * Checks that the given range of the string is a valid error name + * in the D-BUS protocol. This includes a length restriction, etc., + * see the specification. + * + * @todo this is inconsistent with most of DBusString in that + * it allows a start,len range that extends past the string end. + * + * @param str the string + * @param start first byte index to check + * @param len number of bytes to check + * @returns #TRUE if the byte range exists and is a valid name + */ +dbus_bool_t +_dbus_validate_error_name (const DBusString *str, + int start, + int len) +{ + /* Same restrictions as interface name at the moment */ + return _dbus_validate_interface (str, start, len); +} + +/* This assumes the first char exists and is ':' */ +static dbus_bool_t +_dbus_validate_base_service (const DBusString *str, + int start, + int len) +{ + const unsigned char *s; + const unsigned char *end; + const unsigned char *service; + + _dbus_assert (start >= 0); + _dbus_assert (len >= 0); + _dbus_assert (start <= _dbus_string_get_length (str)); + + if (len > _dbus_string_get_length (str) - start) + return FALSE; + + if (len > DBUS_MAXIMUM_NAME_LENGTH) + return FALSE; + + _dbus_assert (len > 0); + + service = _dbus_string_get_const_data (str) + start; + end = service + len; + _dbus_assert (*service == ':'); + s = service + 1; + + while (s != end) + { + if (*s == '.') + { + if (_DBUS_UNLIKELY ((s + 1) == end)) + return FALSE; + if (_DBUS_UNLIKELY (!VALID_NAME_CHARACTER (*(s + 1)))) + return FALSE; + ++s; /* we just validated the next char, so skip two */ + } + else if (_DBUS_UNLIKELY (!VALID_NAME_CHARACTER (*s))) + { + return FALSE; + } + + ++s; + } + + return TRUE; +} + +/** + * Checks that the given range of the string is a valid service name + * in the D-BUS protocol. This includes a length restriction, etc., + * see the specification. + * + * @todo this is inconsistent with most of DBusString in that + * it allows a start,len range that extends past the string end. + * + * @param str the string + * @param start first byte index to check + * @param len number of bytes to check + * @returns #TRUE if the byte range exists and is a valid name + */ +dbus_bool_t +_dbus_validate_service (const DBusString *str, + int start, + int len) +{ + if (_DBUS_UNLIKELY (len == 0)) + return FALSE; + if (_dbus_string_get_byte (str, start) == ':') + return _dbus_validate_base_service (str, start, len); + else + return _dbus_validate_interface (str, start, len); +} + +/** + * Checks that the given range of the string is a valid message type + * signature in the D-BUS protocol. + * + * @todo this is inconsistent with most of DBusString in that + * it allows a start,len range that extends past the string end. + * + * @param str the string + * @param start first byte index to check + * @param len number of bytes to check + * @returns #TRUE if the byte range exists and is a valid signature + */ +dbus_bool_t +_dbus_validate_signature (const DBusString *str, + int start, + int len) +{ + _dbus_assert (start >= 0); + _dbus_assert (start <= _dbus_string_get_length (str)); + _dbus_assert (len >= 0); + + if (len > _dbus_string_get_length (str) - start) + return FALSE; + + return _dbus_validate_signature_with_reason (str, start, len) == DBUS_VALID; +} + +/* If the compiler hates these semicolons, add "extern int + * allow_parens" at the end in the the macro perhaps + */ +DEFINE_DBUS_NAME_CHECK(path); +DEFINE_DBUS_NAME_CHECK(interface); +DEFINE_DBUS_NAME_CHECK(member); +DEFINE_DBUS_NAME_CHECK(error_name); +DEFINE_DBUS_NAME_CHECK(service); +DEFINE_DBUS_NAME_CHECK(signature); + /** @} */ #ifdef DBUS_BUILD_TESTS #include "dbus-test.h" #include +typedef struct +{ + const char *data; + DBusValidity expected; +} ValidityTest; + +static void +run_validity_tests (const ValidityTest *tests, + int n_tests, + DBusValidity (* func) (const DBusString*,int,int)) +{ + int i; + + for (i = 0; i < n_tests; i++) + { + DBusString str; + DBusValidity v; + + _dbus_string_init_const (&str, tests[i].data); + + v = (*func) (&str, 0, _dbus_string_get_length (&str)); + + if (v != tests[i].expected) + { + _dbus_warn ("Improper validation result %d for '%s'\n", + v, tests[i].data); + _dbus_assert_not_reached ("test failed"); + } + + ++i; + } +} + +static const ValidityTest signature_tests[] = { + { "", DBUS_VALID }, + { "i", DBUS_VALID }, + { "ai", DBUS_VALID }, + { "(i)", DBUS_VALID }, + { "q", DBUS_INVALID_UNKNOWN_TYPECODE }, + { "a", DBUS_INVALID_MISSING_ARRAY_ELEMENT_TYPE }, + { "aaaaaa", DBUS_INVALID_MISSING_ARRAY_ELEMENT_TYPE }, + { "ii(ii)a", DBUS_INVALID_MISSING_ARRAY_ELEMENT_TYPE }, + { "ia", DBUS_INVALID_MISSING_ARRAY_ELEMENT_TYPE }, + /* DBUS_INVALID_SIGNATURE_TOO_LONG, */ /* too hard to test this way */ + { "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa", + DBUS_INVALID_EXCEEDED_MAXIMUM_ARRAY_RECURSION }, + { "((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((ii))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))", + DBUS_INVALID_EXCEEDED_MAXIMUM_STRUCT_RECURSION }, + { ")", DBUS_INVALID_STRUCT_ENDED_BUT_NOT_STARTED }, + { "i)", DBUS_INVALID_STRUCT_ENDED_BUT_NOT_STARTED }, + { "a)", DBUS_INVALID_STRUCT_ENDED_BUT_NOT_STARTED }, + { "(", DBUS_INVALID_STRUCT_STARTED_BUT_NOT_ENDED }, + { "(i", DBUS_INVALID_STRUCT_STARTED_BUT_NOT_ENDED }, + { "(iiiii", DBUS_INVALID_STRUCT_STARTED_BUT_NOT_ENDED }, + { "(ai", DBUS_INVALID_STRUCT_STARTED_BUT_NOT_ENDED }, + { "()", DBUS_INVALID_STRUCT_HAS_NO_FIELDS }, + { "(())", DBUS_INVALID_STRUCT_HAS_NO_FIELDS }, + { "a()", DBUS_INVALID_STRUCT_HAS_NO_FIELDS }, + { "i()", DBUS_INVALID_STRUCT_HAS_NO_FIELDS }, + { "()i", DBUS_INVALID_STRUCT_HAS_NO_FIELDS } +}; + dbus_bool_t _dbus_marshal_validate_test (void) { + DBusString str; + int i; + + const char *valid_paths[] = { + "/", + "/foo/bar", + "/foo", + "/foo/bar/baz" + }; + const char *invalid_paths[] = { + "bar", + "bar/baz", + "/foo/bar/", + "/foo/" + "foo/", + "boo//blah", + "//", + "///", + "foo///blah/", + "Hello World", + "", + " ", + "foo bar" + }; + + const char *valid_interfaces[] = { + "org.freedesktop.Foo", + "Bar.Baz", + "Blah.Blah.Blah.Blah.Blah", + "a.b", + "a.b.c.d.e.f.g", + "a0.b1.c2.d3.e4.f5.g6", + "abc123.foo27" + }; + const char *invalid_interfaces[] = { + ".", + "", + "..", + ".Foo.Bar", + "..Foo.Bar", + "Foo.Bar.", + "Foo.Bar..", + "Foo", + "9foo.bar.baz", + "foo.bar..baz", + "foo.bar...baz", + "foo.bar.b..blah", + ":", + ":0-1", + "10", + ":11.34324", + "0.0.0", + "0..0", + "foo.Bar.%", + "foo.Bar!!", + "!Foo.bar.bz", + "foo.$.blah", + "", + " ", + "foo bar" + }; + + const char *valid_base_services[] = { + ":0", + ":a", + ":", + ":.a", + ":.1", + ":0.1", + ":000.2222", + ":.blah", + ":abce.freedesktop.blah" + }; + const char *invalid_base_services[] = { + ":-", + ":!", + ":0-10", + ":blah.", + ":blah.", + ":blah..org", + ":blah.org..", + ":..blah.org", + "", + " ", + "foo bar" + }; + + const char *valid_members[] = { + "Hello", + "Bar", + "foobar", + "_foobar", + "foo89" + }; + + const char *invalid_members[] = { + "9Hello", + "10", + "1", + "foo-bar", + "blah.org", + ".blah", + "blah.", + "Hello.", + "!foo", + "", + " ", + "foo bar" + }; + + const char *valid_signatures[] = { + "", + "sss", + "i", + "b" + }; + + const char *invalid_signatures[] = { + " ", + "not a valid signature", + "123", + ".", + "(" + }; + + /* Signature with reason */ + + run_validity_tests (signature_tests, _DBUS_N_ELEMENTS (signature_tests), + _dbus_validate_signature_with_reason); + + /* Path validation */ + i = 0; + while (i < (int) _DBUS_N_ELEMENTS (valid_paths)) + { + _dbus_string_init_const (&str, valid_paths[i]); + + if (!_dbus_validate_path (&str, 0, + _dbus_string_get_length (&str))) + { + _dbus_warn ("Path \"%s\" should have been valid\n", valid_paths[i]); + _dbus_assert_not_reached ("invalid path"); + } + + ++i; + } + + i = 0; + while (i < (int) _DBUS_N_ELEMENTS (invalid_paths)) + { + _dbus_string_init_const (&str, invalid_paths[i]); + + if (_dbus_validate_path (&str, 0, + _dbus_string_get_length (&str))) + { + _dbus_warn ("Path \"%s\" should have been invalid\n", invalid_paths[i]); + _dbus_assert_not_reached ("valid path"); + } + + ++i; + } + + /* Interface validation */ + i = 0; + while (i < (int) _DBUS_N_ELEMENTS (valid_interfaces)) + { + _dbus_string_init_const (&str, valid_interfaces[i]); + + if (!_dbus_validate_interface (&str, 0, + _dbus_string_get_length (&str))) + { + _dbus_warn ("Interface \"%s\" should have been valid\n", valid_interfaces[i]); + _dbus_assert_not_reached ("invalid interface"); + } + + ++i; + } + + i = 0; + while (i < (int) _DBUS_N_ELEMENTS (invalid_interfaces)) + { + _dbus_string_init_const (&str, invalid_interfaces[i]); + + if (_dbus_validate_interface (&str, 0, + _dbus_string_get_length (&str))) + { + _dbus_warn ("Interface \"%s\" should have been invalid\n", invalid_interfaces[i]); + _dbus_assert_not_reached ("valid interface"); + } + + ++i; + } + + /* Service validation (check that valid interfaces are valid services, + * and invalid interfaces are invalid services except if they start with ':') + */ + i = 0; + while (i < (int) _DBUS_N_ELEMENTS (valid_interfaces)) + { + _dbus_string_init_const (&str, valid_interfaces[i]); + + if (!_dbus_validate_service (&str, 0, + _dbus_string_get_length (&str))) + { + _dbus_warn ("Service \"%s\" should have been valid\n", valid_interfaces[i]); + _dbus_assert_not_reached ("invalid service"); + } + + ++i; + } + + i = 0; + while (i < (int) _DBUS_N_ELEMENTS (invalid_interfaces)) + { + if (invalid_interfaces[i][0] != ':') + { + _dbus_string_init_const (&str, invalid_interfaces[i]); + + if (_dbus_validate_service (&str, 0, + _dbus_string_get_length (&str))) + { + _dbus_warn ("Service \"%s\" should have been invalid\n", invalid_interfaces[i]); + _dbus_assert_not_reached ("valid service"); + } + } + + ++i; + } + + /* Base service validation */ + i = 0; + while (i < (int) _DBUS_N_ELEMENTS (valid_base_services)) + { + _dbus_string_init_const (&str, valid_base_services[i]); + + if (!_dbus_validate_service (&str, 0, + _dbus_string_get_length (&str))) + { + _dbus_warn ("Service \"%s\" should have been valid\n", valid_base_services[i]); + _dbus_assert_not_reached ("invalid base service"); + } + + ++i; + } + + i = 0; + while (i < (int) _DBUS_N_ELEMENTS (invalid_base_services)) + { + _dbus_string_init_const (&str, invalid_base_services[i]); + + if (_dbus_validate_service (&str, 0, + _dbus_string_get_length (&str))) + { + _dbus_warn ("Service \"%s\" should have been invalid\n", invalid_base_services[i]); + _dbus_assert_not_reached ("valid base service"); + } + + ++i; + } + + + /* Error name validation (currently identical to interfaces) + */ + i = 0; + while (i < (int) _DBUS_N_ELEMENTS (valid_interfaces)) + { + _dbus_string_init_const (&str, valid_interfaces[i]); + + if (!_dbus_validate_error_name (&str, 0, + _dbus_string_get_length (&str))) + { + _dbus_warn ("Error name \"%s\" should have been valid\n", valid_interfaces[i]); + _dbus_assert_not_reached ("invalid error name"); + } + + ++i; + } + + i = 0; + while (i < (int) _DBUS_N_ELEMENTS (invalid_interfaces)) + { + if (invalid_interfaces[i][0] != ':') + { + _dbus_string_init_const (&str, invalid_interfaces[i]); + + if (_dbus_validate_error_name (&str, 0, + _dbus_string_get_length (&str))) + { + _dbus_warn ("Error name \"%s\" should have been invalid\n", invalid_interfaces[i]); + _dbus_assert_not_reached ("valid error name"); + } + } + + ++i; + } + + /* Member validation */ + i = 0; + while (i < (int) _DBUS_N_ELEMENTS (valid_members)) + { + _dbus_string_init_const (&str, valid_members[i]); + + if (!_dbus_validate_member (&str, 0, + _dbus_string_get_length (&str))) + { + _dbus_warn ("Member \"%s\" should have been valid\n", valid_members[i]); + _dbus_assert_not_reached ("invalid member"); + } + + ++i; + } + + i = 0; + while (i < (int) _DBUS_N_ELEMENTS (invalid_members)) + { + _dbus_string_init_const (&str, invalid_members[i]); + + if (_dbus_validate_member (&str, 0, + _dbus_string_get_length (&str))) + { + _dbus_warn ("Member \"%s\" should have been invalid\n", invalid_members[i]); + _dbus_assert_not_reached ("valid member"); + } + + ++i; + } + + /* Signature validation */ + i = 0; + while (i < (int) _DBUS_N_ELEMENTS (valid_signatures)) + { + _dbus_string_init_const (&str, valid_signatures[i]); + + if (!_dbus_validate_signature (&str, 0, + _dbus_string_get_length (&str))) + { + _dbus_warn ("Signature \"%s\" should have been valid\n", valid_signatures[i]); + _dbus_assert_not_reached ("invalid signature"); + } + + ++i; + } + + i = 0; + while (i < (int) _DBUS_N_ELEMENTS (invalid_signatures)) + { + _dbus_string_init_const (&str, invalid_signatures[i]); + + if (_dbus_validate_signature (&str, 0, + _dbus_string_get_length (&str))) + { + _dbus_warn ("Signature \"%s\" should have been invalid\n", invalid_signatures[i]); + _dbus_assert_not_reached ("valid signature"); + } + + ++i; + } + + /* Validate claimed length longer than real length */ + _dbus_string_init_const (&str, "abc.efg"); + if (_dbus_validate_service (&str, 0, 8)) + _dbus_assert_not_reached ("validated too-long string"); + if (_dbus_validate_interface (&str, 0, 8)) + _dbus_assert_not_reached ("validated too-long string"); + if (_dbus_validate_error_name (&str, 0, 8)) + _dbus_assert_not_reached ("validated too-long string"); + + _dbus_string_init_const (&str, "abc"); + if (_dbus_validate_member (&str, 0, 4)) + _dbus_assert_not_reached ("validated too-long string"); + + _dbus_string_init_const (&str, "sss"); + if (_dbus_validate_signature (&str, 0, 4)) + _dbus_assert_not_reached ("validated too-long signature"); + + /* Validate string exceeding max name length */ + if (!_dbus_string_init (&str)) + _dbus_assert_not_reached ("no memory"); + + while (_dbus_string_get_length (&str) <= DBUS_MAXIMUM_NAME_LENGTH) + if (!_dbus_string_append (&str, "abc.def")) + _dbus_assert_not_reached ("no memory"); + + if (_dbus_validate_service (&str, 0, _dbus_string_get_length (&str))) + _dbus_assert_not_reached ("validated overmax string"); + if (_dbus_validate_interface (&str, 0, _dbus_string_get_length (&str))) + _dbus_assert_not_reached ("validated overmax string"); + if (_dbus_validate_error_name (&str, 0, _dbus_string_get_length (&str))) + _dbus_assert_not_reached ("validated overmax string"); + + /* overlong member */ + _dbus_string_set_length (&str, 0); + while (_dbus_string_get_length (&str) <= DBUS_MAXIMUM_NAME_LENGTH) + if (!_dbus_string_append (&str, "abc")) + _dbus_assert_not_reached ("no memory"); + + if (_dbus_validate_member (&str, 0, _dbus_string_get_length (&str))) + _dbus_assert_not_reached ("validated overmax string"); + + /* overlong base service */ + _dbus_string_set_length (&str, 0); + _dbus_string_append (&str, ":"); + while (_dbus_string_get_length (&str) <= DBUS_MAXIMUM_NAME_LENGTH) + if (!_dbus_string_append (&str, "abc")) + _dbus_assert_not_reached ("no memory"); + + if (_dbus_validate_service (&str, 0, _dbus_string_get_length (&str))) + _dbus_assert_not_reached ("validated overmax string"); + + _dbus_string_free (&str); return TRUE; } diff --git a/dbus/dbus-marshal-validate.h b/dbus/dbus-marshal-validate.h index 86cbf7ea..8f4e7ef6 100644 --- a/dbus/dbus-marshal-validate.h +++ b/dbus/dbus-marshal-validate.h @@ -31,5 +31,133 @@ #error "config.h not included here" #endif +/** + * This is primarily used in unit testing, so we can verify that each + * invalid message is invalid for the expected reasons. Thus we really + * want a distinct enum value for every codepath leaving the validator + * functions. Enum values are specified manually for ease of debugging + * (so you can see the enum value given a printf) + */ +typedef enum +{ + DBUS_VALID = 0, + DBUS_INVALID_UNKNOWN_TYPECODE = 1, + DBUS_INVALID_MISSING_ARRAY_ELEMENT_TYPE = 2, + DBUS_INVALID_SIGNATURE_TOO_LONG = 3, + DBUS_INVALID_EXCEEDED_MAXIMUM_ARRAY_RECURSION = 4, + DBUS_INVALID_EXCEEDED_MAXIMUM_STRUCT_RECURSION = 5, + DBUS_INVALID_STRUCT_ENDED_BUT_NOT_STARTED = 6, + DBUS_INVALID_STRUCT_STARTED_BUT_NOT_ENDED = 7, + DBUS_INVALID_STRUCT_HAS_NO_FIELDS = 8, + DBUS_INVALID_ALIGNMENT_PADDING_NOT_NUL = 9, + DBUS_INVALID_BOOLEAN_NOT_ZERO_OR_ONE = 10, + DBUS_INVALID_NOT_ENOUGH_DATA = 11, + DBUS_INVALID_TOO_MUCH_DATA = 12, + DBUS_INVALID_BAD_BYTE_ORDER = 13, + DBUS_INVALID_BAD_PROTOCOL_VERSION = 14, + DBUS_INVALID_BAD_MESSAGE_TYPE = 15, + DBUS_INVALID_BAD_SERIAL = 16, + DBUS_INVALID_INSANE_FIELDS_ARRAY_LENGTH = 17, + DBUS_INVALID_INSANE_BODY_LENGTH = 18, + DBUS_INVALID_MESSAGE_TOO_LONG = 19, + DBUS_INVALID_HEADER_FIELD_CODE = 20, + DBUS_INVALID_HEADER_FIELD_HAS_WRONG_TYPE = 21, + DBUS_INVALID_USES_LOCAL_INTERFACE = 22, + DBUS_INVALID_USES_LOCAL_PATH = 23, + DBUS_INVALID_HEADER_FIELD_APPEARS_TWICE = 24, + DBUS_INVALID_BAD_DESTINATION = 25, + DBUS_INVALID_BAD_INTERFACE = 26, + DBUS_INVALID_BAD_MEMBER = 27, + DBUS_INVALID_BAD_ERROR_NAME = 28, + DBUS_INVALID_BAD_SENDER = 29, + DBUS_INVALID_MISSING_PATH = 30, + DBUS_INVALID_MISSING_INTERFACE = 31, + DBUS_INVALID_MISSING_MEMBER = 32, + DBUS_INVALID_MISSING_ERROR_NAME = 33, + DBUS_INVALID_MISSING_REPLY_SERIAL = 34, + DBUS_INVALID_STRING_LENGTH_OUT_OF_BOUNDS = 35, + DBUS_INVALID_ARRAY_LENGTH_OUT_OF_BOUNDS = 36, + DBUS_INVALID_ARRAY_LENGTH_EXCEEDS_MAXIMUM = 37, + DBUS_INVALID_BAD_PATH = 38, + DBUS_INVALID_SIGNATURE_LENGTH_OUT_OF_BOUNDS = 39, + DBUS_INVALID_BAD_SIGNATURE = 40, + DBUS_INVALID_BAD_UTF8_IN_STRING = 41, + DBUS_INVALID_ARRAY_LENGTH_INCORRECT = 42, + DBUS_INVALID_VARIANT_SIGNATURE_LENGTH_OUT_OF_BOUNDS = 43, + DBUS_INVALID_VARIANT_SIGNATURE_BAD = 44, + DBUS_INVALID_VARIANT_SIGNATURE_EMPTY = 45, + DBUS_INVALID_VARIANT_SIGNATURE_SPECIFIES_MULTIPLE_VALUES = 46, + DBUS_INVALID_VARIANT_SIGNATURE_MISSING_NUL = 47, + DBUS_INVALID_STRING_MISSING_NUL = 48, + DBUS_INVALID_SIGNATURE_MISSING_NUL = 49, +} DBusValidity; + +DBusValidity _dbus_validate_signature_with_reason (const DBusString *type_str, + int type_pos, + int len); +DBusValidity _dbus_validate_body_with_reason (const DBusString *expected_signature, + int expected_signature_start, + int byte_order, + int *bytes_remaining, + const DBusString *value_str, + int value_pos, + int len); + +dbus_bool_t _dbus_validate_path (const DBusString *str, + int start, + int len); +dbus_bool_t _dbus_validate_interface (const DBusString *str, + int start, + int len); +dbus_bool_t _dbus_validate_member (const DBusString *str, + int start, + int len); +dbus_bool_t _dbus_validate_error_name (const DBusString *str, + int start, + int len); +dbus_bool_t _dbus_validate_service (const DBusString *str, + int start, + int len); +dbus_bool_t _dbus_validate_signature (const DBusString *str, + int start, + int len); + +#ifdef DBUS_DISABLE_CHECKS + +/* Be sure they don't exist, since we don't want to use them outside of checks + * and so we want the compile failure. + */ +#define DECLARE_DBUS_NAME_CHECK(what) +#define DEFINE_DBUS_NAME_CHECK(what) + +#else /* !DBUS_DISABLE_CHECKS */ + +/* A name check is used in _dbus_return_if_fail(), it's not suitable + * for validating untrusted data. use _dbus_validate_##what for that. + */ +#define DECLARE_DBUS_NAME_CHECK(what) \ +dbus_bool_t _dbus_check_is_valid_##what (const char *name) + +#define DEFINE_DBUS_NAME_CHECK(what) \ +dbus_bool_t \ +_dbus_check_is_valid_##what (const char *name) \ +{ \ + DBusString str; \ + \ + if (name == NULL) \ + return FALSE; \ + \ + _dbus_string_init_const (&str, name); \ + return _dbus_validate_##what (&str, 0, \ + _dbus_string_get_length (&str)); \ +} +#endif /* !DBUS_DISABLE_CHECKS */ + +DECLARE_DBUS_NAME_CHECK(path); +DECLARE_DBUS_NAME_CHECK(interface); +DECLARE_DBUS_NAME_CHECK(member); +DECLARE_DBUS_NAME_CHECK(error_name); +DECLARE_DBUS_NAME_CHECK(service); +DECLARE_DBUS_NAME_CHECK(signature); #endif /* DBUS_MARSHAL_VALIDATE_H */ diff --git a/dbus/dbus-marshal.c b/dbus/dbus-marshal.c deleted file mode 100644 index 00821daf..00000000 --- a/dbus/dbus-marshal.c +++ /dev/null @@ -1,2941 +0,0 @@ -/* -*- mode: C; c-file-style: "gnu" -*- */ -/* dbus-marshal.c Marshalling routines - * - * Copyright (C) 2002 CodeFactory AB - * Copyright (C) 2003, 2004 Red Hat, Inc. - * - * Licensed under the Academic Free License version 2.1 - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation; either version 2 of the License, or - * (at your option) any later version. - * - * This program 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 General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program; if not, write to the Free Software - * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA - * - */ - -#include "dbus-marshal.h" -#include "dbus-internals.h" -#define DBUS_CAN_USE_DBUS_STRING_PRIVATE 1 -#include "dbus-string-private.h" - -#include - -/** - * @defgroup DBusMarshal marshaling and unmarshaling - * @ingroup DBusInternals - * @brief functions to marshal/unmarshal data from the wire - * - * Types and functions related to converting primitive data types from - * wire format to native machine format, and vice versa. - * - * @{ - */ - -static dbus_uint32_t -unpack_4_octets (int byte_order, - const unsigned char *data) -{ - _dbus_assert (_DBUS_ALIGN_ADDRESS (data, 4) == data); - - if (byte_order == DBUS_LITTLE_ENDIAN) - return DBUS_UINT32_FROM_LE (*(dbus_uint32_t*)data); - else - return DBUS_UINT32_FROM_BE (*(dbus_uint32_t*)data); -} - -#ifndef DBUS_HAVE_INT64 -/* from ORBit */ -static void -swap_bytes (unsigned char *data, - unsigned int len) -{ - unsigned char *p1 = data; - unsigned char *p2 = data + len - 1; - - while (p1 < p2) - { - unsigned char tmp = *p1; - *p1 = *p2; - *p2 = tmp; - - --p2; - ++p1; - } -} -#endif /* !DBUS_HAVE_INT64 */ - -/** - * Union used to manipulate 8 bytes as if they - * were various types. - */ -typedef union -{ -#ifdef DBUS_HAVE_INT64 - dbus_int64_t s; /**< 64-bit integer */ - dbus_uint64_t u; /**< 64-bit unsinged integer */ -#endif - double d; /**< double */ -} DBusOctets8; - -static DBusOctets8 -unpack_8_octets (int byte_order, - const unsigned char *data) -{ - DBusOctets8 r; - - _dbus_assert (_DBUS_ALIGN_ADDRESS (data, 8) == data); - _dbus_assert (sizeof (r) == 8); - -#ifdef DBUS_HAVE_INT64 - if (byte_order == DBUS_LITTLE_ENDIAN) - r.u = DBUS_UINT64_FROM_LE (*(dbus_uint64_t*)data); - else - r.u = DBUS_UINT64_FROM_BE (*(dbus_uint64_t*)data); -#else - r.d = *(double*)data; - if (byte_order != DBUS_COMPILER_BYTE_ORDER) - swap_bytes ((unsigned char*) &r, sizeof (r)); -#endif - - return r; -} - -/** - * Unpacks a 32 bit unsigned integer from a data pointer - * - * @param byte_order The byte order to use - * @param data the data pointer - * @returns the integer - */ -dbus_uint32_t -_dbus_unpack_uint32 (int byte_order, - const unsigned char *data) -{ - return unpack_4_octets (byte_order, data); -} - -/** - * Unpacks a 32 bit signed integer from a data pointer - * - * @param byte_order The byte order to use - * @param data the data pointer - * @returns the integer - */ -dbus_int32_t -_dbus_unpack_int32 (int byte_order, - const unsigned char *data) -{ - return (dbus_int32_t) unpack_4_octets (byte_order, data); -} - -#ifdef DBUS_HAVE_INT64 -/** - * Unpacks a 64 bit unsigned integer from a data pointer - * - * @param byte_order The byte order to use - * @param data the data pointer - * @returns the integer - */ -dbus_uint64_t -_dbus_unpack_uint64 (int byte_order, - const unsigned char *data) -{ - DBusOctets8 r; - - r = unpack_8_octets (byte_order, data); - - return r.u; -} - -/** - * Unpacks a 64 bit signed integer from a data pointer - * - * @param byte_order The byte order to use - * @param data the data pointer - * @returns the integer - */ -dbus_int64_t -_dbus_unpack_int64 (int byte_order, - const unsigned char *data) -{ - DBusOctets8 r; - - r = unpack_8_octets (byte_order, data); - - return r.s; -} - -#endif /* DBUS_HAVE_INT64 */ - -static void -pack_4_octets (dbus_uint32_t value, - int byte_order, - unsigned char *data) -{ - _dbus_assert (_DBUS_ALIGN_ADDRESS (data, 4) == data); - - if ((byte_order) == DBUS_LITTLE_ENDIAN) - *((dbus_uint32_t*)(data)) = DBUS_UINT32_TO_LE (value); - else - *((dbus_uint32_t*)(data)) = DBUS_UINT32_TO_BE (value); -} - -static void -pack_8_octets (DBusOctets8 value, - int byte_order, - unsigned char *data) -{ - _dbus_assert (_DBUS_ALIGN_ADDRESS (data, 8) == data); - -#ifdef DBUS_HAVE_INT64 - if ((byte_order) == DBUS_LITTLE_ENDIAN) - *((dbus_uint64_t*)(data)) = DBUS_UINT64_TO_LE (value.u); - else - *((dbus_uint64_t*)(data)) = DBUS_UINT64_TO_BE (value.u); -#else - memcpy (data, &value, 8); - if (byte_order != DBUS_COMPILER_BYTE_ORDER) - swap_bytes ((unsigned char *)data, 8); -#endif -} - -/** - * Packs a 32 bit unsigned integer into a data pointer. - * - * @param value the value - * @param byte_order the byte order to use - * @param data the data pointer - */ -void -_dbus_pack_uint32 (dbus_uint32_t value, - int byte_order, - unsigned char *data) -{ - pack_4_octets (value, byte_order, data); -} - -/** - * Packs a 32 bit signed integer into a data pointer. - * - * @param value the value - * @param byte_order the byte order to use - * @param data the data pointer - */ -void -_dbus_pack_int32 (dbus_int32_t value, - int byte_order, - unsigned char *data) -{ - pack_4_octets ((dbus_uint32_t) value, byte_order, data); -} - -#ifdef DBUS_HAVE_INT64 -/** - * Packs a 64 bit unsigned integer into a data pointer. - * - * @param value the value - * @param byte_order the byte order to use - * @param data the data pointer - */ -void -_dbus_pack_uint64 (dbus_uint64_t value, - int byte_order, - unsigned char *data) -{ - DBusOctets8 r; - r.u = value; - pack_8_octets (r, byte_order, data); -} - -/** - * Packs a 64 bit signed integer into a data pointer. - * - * @param value the value - * @param byte_order the byte order to use - * @param data the data pointer - */ -void -_dbus_pack_int64 (dbus_int64_t value, - int byte_order, - unsigned char *data) -{ - DBusOctets8 r; - r.s = value; - pack_8_octets (r, byte_order, data); -} -#endif /* DBUS_HAVE_INT64 */ - -static void -set_4_octets (DBusString *str, - int byte_order, - int offset, - dbus_uint32_t value) -{ - char *data; - - _dbus_assert (byte_order == DBUS_LITTLE_ENDIAN || - byte_order == DBUS_BIG_ENDIAN); - - data = _dbus_string_get_data_len (str, offset, 4); - - _dbus_pack_uint32 (value, byte_order, data); -} - -static void -set_8_octets (DBusString *str, - int byte_order, - int offset, - DBusOctets8 value) -{ - char *data; - - _dbus_assert (byte_order == DBUS_LITTLE_ENDIAN || - byte_order == DBUS_BIG_ENDIAN); - - data = _dbus_string_get_data_len (str, offset, 8); - - pack_8_octets (value, byte_order, data); -} - -/** - * Sets the 4 bytes at the given offset to a marshaled signed integer, - * replacing anything found there previously. - * - * @param str the string to write the marshalled int to - * @param offset the byte offset where int should be written - * @param byte_order the byte order to use - * @param value the value - * - */ -void -_dbus_marshal_set_int32 (DBusString *str, - int byte_order, - int offset, - dbus_int32_t value) -{ - set_4_octets (str, byte_order, offset, (dbus_uint32_t) value); -} - -/** - * Sets the 4 bytes at the given offset to a marshaled unsigned - * integer, replacing anything found there previously. - * - * @param str the string to write the marshalled int to - * @param offset the byte offset where int should be written - * @param byte_order the byte order to use - * @param value the value - * - */ -void -_dbus_marshal_set_uint32 (DBusString *str, - int byte_order, - int offset, - dbus_uint32_t value) -{ - set_4_octets (str, byte_order, offset, value); -} - -#ifdef DBUS_HAVE_INT64 - -/** - * Sets the 8 bytes at the given offset to a marshaled signed integer, - * replacing anything found there previously. - * - * @param str the string to write the marshalled int to - * @param offset the byte offset where int should be written - * @param byte_order the byte order to use - * @param value the value - * - */ -void -_dbus_marshal_set_int64 (DBusString *str, - int byte_order, - int offset, - dbus_int64_t value) -{ - DBusOctets8 r; - r.s = value; - set_8_octets (str, byte_order, offset, r); -} - -/** - * Sets the 8 bytes at the given offset to a marshaled unsigned - * integer, replacing anything found there previously. - * - * @param str the string to write the marshalled int to - * @param offset the byte offset where int should be written - * @param byte_order the byte order to use - * @param value the value - * - */ -void -_dbus_marshal_set_uint64 (DBusString *str, - int byte_order, - int offset, - dbus_uint64_t value) -{ - DBusOctets8 r; - r.u = value; - set_8_octets (str, byte_order, offset, r); -} -#endif /* DBUS_HAVE_INT64 */ - -/** - * Sets the existing marshaled string at the given offset with - * a new marshaled string. The given offset must point to - * an existing string or the wrong length will be deleted - * and replaced with the new string. - * - * Note: no attempt is made by this function to re-align - * any data which has been already marshalled after this - * string. Use with caution. - * - * @param str the string to write the marshalled string to - * @param offset the byte offset where string should be written - * @param byte_order the byte order to use - * @param value the value - * @param len the length to use - * @returns #TRUE on success - * - */ -dbus_bool_t -_dbus_marshal_set_string (DBusString *str, - int byte_order, - int offset, - const DBusString *value, - int len) -{ - int old_len; - - _dbus_assert (byte_order == DBUS_LITTLE_ENDIAN || - byte_order == DBUS_BIG_ENDIAN); - - old_len = _dbus_demarshal_uint32 (str, byte_order, - offset, NULL); - - if (!_dbus_string_replace_len (value, 0, len, - str, offset + 4, old_len)) - return FALSE; - - _dbus_marshal_set_uint32 (str, byte_order, - offset, len); - - return TRUE; -} - -/** - * Sets the existing marshaled object path at the given offset to a new - * value. The given offset must point to an existing object path or this - * function doesn't make sense. - * - * @todo implement this function - * - * @param str the string to write the marshalled path to - * @param offset the byte offset where path should be written - * @param byte_order the byte order to use - * @param path the new path - * @param path_len number of elements in the path - */ -void -_dbus_marshal_set_object_path (DBusString *str, - int byte_order, - int offset, - const char **path, - int path_len) -{ - - /* FIXME */ -} - -static dbus_bool_t -marshal_4_octets (DBusString *str, - int byte_order, - dbus_uint32_t value) -{ - _dbus_assert (sizeof (value) == 4); - - if (byte_order != DBUS_COMPILER_BYTE_ORDER) - value = DBUS_UINT32_SWAP_LE_BE (value); - - return _dbus_string_append_4_aligned (str, - (const unsigned char *)&value); -} - -static dbus_bool_t -marshal_8_octets (DBusString *str, - int byte_order, - DBusOctets8 value) -{ - _dbus_assert (sizeof (value) == 8); - - if (byte_order != DBUS_COMPILER_BYTE_ORDER) - pack_8_octets (value, byte_order, (unsigned char*) &value); /* pack into self, swapping as we go */ - - return _dbus_string_append_8_aligned (str, - (const unsigned char *)&value); -} - -/** - * Marshals a double value. - * - * @param str the string to append the marshalled value to - * @param byte_order the byte order to use - * @param value the value - * @returns #TRUE on success - */ -dbus_bool_t -_dbus_marshal_double (DBusString *str, - int byte_order, - double value) -{ - DBusOctets8 r; - r.d = value; - return marshal_8_octets (str, byte_order, r); -} - -/** - * Marshals a 32 bit signed integer value. - * - * @param str the string to append the marshalled value to - * @param byte_order the byte order to use - * @param value the value - * @returns #TRUE on success - */ -dbus_bool_t -_dbus_marshal_int32 (DBusString *str, - int byte_order, - dbus_int32_t value) -{ - return marshal_4_octets (str, byte_order, (dbus_uint32_t) value); -} - -/** - * Marshals a 32 bit unsigned integer value. - * - * @param str the string to append the marshalled value to - * @param byte_order the byte order to use - * @param value the value - * @returns #TRUE on success - */ -dbus_bool_t -_dbus_marshal_uint32 (DBusString *str, - int byte_order, - dbus_uint32_t value) -{ - return marshal_4_octets (str, byte_order, value); -} - - -#ifdef DBUS_HAVE_INT64 -/** - * Marshals a 64 bit signed integer value. - * - * @param str the string to append the marshalled value to - * @param byte_order the byte order to use - * @param value the value - * @returns #TRUE on success - */ -dbus_bool_t -_dbus_marshal_int64 (DBusString *str, - int byte_order, - dbus_int64_t value) -{ - DBusOctets8 r; - r.s = value; - return marshal_8_octets (str, byte_order, r); -} - -/** - * Marshals a 64 bit unsigned integer value. - * - * @param str the string to append the marshalled value to - * @param byte_order the byte order to use - * @param value the value - * @returns #TRUE on success - */ -dbus_bool_t -_dbus_marshal_uint64 (DBusString *str, - int byte_order, - dbus_uint64_t value) -{ - DBusOctets8 r; - r.u = value; - return marshal_8_octets (str, byte_order, r); -} - -#endif /* DBUS_HAVE_INT64 */ - -/** - * Marshals a UTF-8 string - * - * @todo: If the string append fails we need to restore - * the old length. (also for other marshallers) - * - * @param str the string to append the marshalled value to - * @param byte_order the byte order to use - * @param value the string - * @returns #TRUE on success - */ -dbus_bool_t -_dbus_marshal_string (DBusString *str, - int byte_order, - const char *value) -{ - int len, old_string_len; - - old_string_len = _dbus_string_get_length (str); - - len = strlen (value); - - if (!_dbus_marshal_uint32 (str, byte_order, len)) - { - /* Restore the previous length */ - _dbus_string_set_length (str, old_string_len); - - return FALSE; - } - - return _dbus_string_append_len (str, value, len + 1); -} - -/** - * Marshals a UTF-8 string - * - * @todo: If the string append fails we need to restore - * the old length. (also for other marshallers) - * - * @param str the string to append the marshalled value to - * @param byte_order the byte order to use - * @param value the string - * @param len length of string to marshal in bytes - * @returns #TRUE on success - */ -dbus_bool_t -_dbus_marshal_string_len (DBusString *str, - int byte_order, - const char *value, - int len) -{ - int old_string_len; - - old_string_len = _dbus_string_get_length (str); - - if (!_dbus_marshal_uint32 (str, byte_order, len)) - { - /* Restore the previous length */ - _dbus_string_set_length (str, old_string_len); - - return FALSE; - } - - if (!_dbus_string_append_len (str, value, len)) - return FALSE; - - /* add a nul byte */ - if (!_dbus_string_lengthen (str, 1)) - return FALSE; - - return TRUE; -} - -/** - * Marshals a byte array - * - * @param str the string to append the marshalled value to - * @param byte_order the byte order to use - * @param value the array - * @param len number of elements in the array - * @returns #TRUE on success - */ -dbus_bool_t -_dbus_marshal_byte_array (DBusString *str, - int byte_order, - const unsigned char *value, - int len) -{ - int old_string_len; - - old_string_len = _dbus_string_get_length (str); - - if (!_dbus_marshal_uint32 (str, byte_order, len)) - { - /* Restore the previous length */ - _dbus_string_set_length (str, old_string_len); - - return FALSE; - } - - if (len == 0) - return TRUE; - else - return _dbus_string_append_len (str, value, len); -} - -static dbus_bool_t -marshal_4_octets_array (DBusString *str, - int byte_order, - const dbus_uint32_t *value, - int len) -{ - int old_string_len; - int array_start; - - old_string_len = _dbus_string_get_length (str); - - if (!_dbus_marshal_uint32 (str, byte_order, len * 4)) - goto error; - - array_start = _dbus_string_get_length (str); - - if (!_dbus_string_append_len (str, (const unsigned char*) value, - len * 4)) - goto error; - - if (byte_order != DBUS_COMPILER_BYTE_ORDER) - { - const unsigned char *d; - const unsigned char *end; - - d = _dbus_string_get_data (str) + array_start; - end = d + len * 4; - while (d != end) - { - *((dbus_uint32_t*)d) = DBUS_UINT32_SWAP_LE_BE (*((dbus_uint32_t*)d)); - d += 4; - } - } - - return TRUE; - - error: - /* Restore previous length */ - _dbus_string_set_length (str, old_string_len); - - return FALSE; -} - -static dbus_bool_t -marshal_8_octets_array (DBusString *str, - int byte_order, - const DBusOctets8 *value, - int len) -{ - int old_string_len; - int array_start; - - old_string_len = _dbus_string_get_length (str); - - if (!_dbus_marshal_uint32 (str, byte_order, len * 8)) - goto error; - - array_start = _dbus_string_get_length (str); - - if (!_dbus_string_append_len (str, (const unsigned char*) value, - len * 8)) - goto error; - - if (byte_order != DBUS_COMPILER_BYTE_ORDER) - { - const unsigned char *d; - const unsigned char *end; - - d = _dbus_string_get_data (str) + array_start; - end = d + len * 8; - while (d != end) - { -#ifdef DBUS_HAVE_INT64 - *((dbus_uint64_t*)d) = DBUS_UINT64_SWAP_LE_BE (*((dbus_uint64_t*)d)); -#else - swap_bytes ((unsigned char*) d, 8); -#endif - d += 8; - } - } - - return TRUE; - - error: - /* Restore previous length */ - _dbus_string_set_length (str, old_string_len); - - return FALSE; -} - -/** - * Marshals a 32 bit signed integer array - * - * @param str the string to append the marshalled value to - * @param byte_order the byte order to use - * @param value the array - * @param len the length of the array - * @returns #TRUE on success - */ -dbus_bool_t -_dbus_marshal_int32_array (DBusString *str, - int byte_order, - const dbus_int32_t *value, - int len) -{ - return marshal_4_octets_array (str, byte_order, - (const dbus_uint32_t*) value, - len); -} - -/** - * Marshals a 32 bit unsigned integer array - * - * @param str the string to append the marshalled value to - * @param byte_order the byte order to use - * @param value the array - * @param len the length of the array - * @returns #TRUE on success - */ -dbus_bool_t -_dbus_marshal_uint32_array (DBusString *str, - int byte_order, - const dbus_uint32_t *value, - int len) -{ - return marshal_4_octets_array (str, byte_order, - value, - len); -} - -#ifdef DBUS_HAVE_INT64 - -/** - * Marshals a 64 bit signed integer array - * - * @param str the string to append the marshalled value to - * @param byte_order the byte order to use - * @param value the array - * @param len the length of the array - * @returns #TRUE on success - */ -dbus_bool_t -_dbus_marshal_int64_array (DBusString *str, - int byte_order, - const dbus_int64_t *value, - int len) -{ - return marshal_8_octets_array (str, byte_order, - (const DBusOctets8*) value, - len); -} - -/** - * Marshals a 64 bit unsigned integer array - * - * @param str the string to append the marshalled value to - * @param byte_order the byte order to use - * @param value the array - * @param len the length of the array - * @returns #TRUE on success - */ -dbus_bool_t -_dbus_marshal_uint64_array (DBusString *str, - int byte_order, - const dbus_uint64_t *value, - int len) -{ - return marshal_8_octets_array (str, byte_order, - (const DBusOctets8*) value, - len); -} - -#endif /* DBUS_HAVE_INT64 */ - -/** - * Marshals a double array - * - * @param str the string to append the marshalled value to - * @param byte_order the byte order to use - * @param value the array - * @param len the length of the array - * @returns #TRUE on success - */ -dbus_bool_t -_dbus_marshal_double_array (DBusString *str, - int byte_order, - const double *value, - int len) -{ - return marshal_8_octets_array (str, byte_order, - (const DBusOctets8*) value, - len); -} - -/** - * Marshals a string array - * - * @param str the string to append the marshalled value to - * @param byte_order the byte order to use - * @param value the array - * @param len the length of the array - * @returns #TRUE on success - */ -dbus_bool_t -_dbus_marshal_string_array (DBusString *str, - int byte_order, - const char **value, - int len) -{ - int i, old_string_len, array_start; - - old_string_len = _dbus_string_get_length (str); - - /* Set the length to 0 temporarily */ - if (!_dbus_marshal_uint32 (str, byte_order, 0)) - goto error; - - array_start = _dbus_string_get_length (str); - - for (i = 0; i < len; i++) - if (!_dbus_marshal_string (str, byte_order, value[i])) - goto error; - - /* Write the length now that we know it */ - _dbus_marshal_set_uint32 (str, byte_order, - _DBUS_ALIGN_VALUE (old_string_len, sizeof(dbus_uint32_t)), - _dbus_string_get_length (str) - array_start); - - return TRUE; - - error: - /* Restore previous length */ - _dbus_string_set_length (str, old_string_len); - - return FALSE; -} - -/** - * Marshals an object path value. - * - * @param str the string to append the marshalled value to - * @param byte_order the byte order to use - * @param path the path - * @param path_len length of the path - * @returns #TRUE on success - */ -dbus_bool_t -_dbus_marshal_object_path (DBusString *str, - int byte_order, - const char **path, - int path_len) -{ - int array_start, old_string_len; - int i; - - old_string_len = _dbus_string_get_length (str); - - /* Set the length to 0 temporarily */ - if (!_dbus_marshal_uint32 (str, byte_order, 0)) - goto nomem; - - array_start = _dbus_string_get_length (str); - - i = 0; - while (i < path_len) - { - if (!_dbus_string_append_byte (str, '/')) - goto nomem; - - if (!_dbus_string_append (str, path[0])) - goto nomem; - - ++i; - } - - /* Write the length now that we know it */ - _dbus_marshal_set_uint32 (str, byte_order, - _DBUS_ALIGN_VALUE (old_string_len, sizeof(dbus_uint32_t)), - _dbus_string_get_length (str) - array_start); - - return TRUE; - - nomem: - /* Restore the previous length */ - _dbus_string_set_length (str, old_string_len); - - return FALSE; -} - -static dbus_uint32_t -demarshal_4_octets (const DBusString *str, - int byte_order, - int pos, - int *new_pos) -{ - const DBusRealString *real = (const DBusRealString*) str; - - pos = _DBUS_ALIGN_VALUE (pos, 4); - - if (new_pos) - *new_pos = pos + 4; - - return unpack_4_octets (byte_order, real->str + pos); -} - -static DBusOctets8 -demarshal_8_octets (const DBusString *str, - int byte_order, - int pos, - int *new_pos) -{ - const DBusRealString *real = (const DBusRealString*) str; - - pos = _DBUS_ALIGN_VALUE (pos, 8); - - if (new_pos) - *new_pos = pos + 8; - - return unpack_8_octets (byte_order, real->str + pos); -} - -/** - * Demarshals a double. - * - * @param str the string containing the data - * @param byte_order the byte order - * @param pos the position in the string - * @param new_pos the new position of the string - * @returns the demarshaled double. - */ -double -_dbus_demarshal_double (const DBusString *str, - int byte_order, - int pos, - int *new_pos) -{ - DBusOctets8 r; - - r = demarshal_8_octets (str, byte_order, pos, new_pos); - - return r.d; -} - -/** - * Demarshals a 32 bit signed integer. - * - * @param str the string containing the data - * @param byte_order the byte order - * @param pos the position in the string - * @param new_pos the new position of the string - * @returns the demarshaled integer. - */ -dbus_int32_t -_dbus_demarshal_int32 (const DBusString *str, - int byte_order, - int pos, - int *new_pos) -{ - return (dbus_int32_t) demarshal_4_octets (str, byte_order, pos, new_pos); -} - -/** - * Demarshals a 32 bit unsigned integer. - * - * @param str the string containing the data - * @param byte_order the byte order - * @param pos the position in the string - * @param new_pos the new position of the string - * @returns the demarshaled integer. - */ -dbus_uint32_t -_dbus_demarshal_uint32 (const DBusString *str, - int byte_order, - int pos, - int *new_pos) -{ - return demarshal_4_octets (str, byte_order, pos, new_pos); -} - -#ifdef DBUS_HAVE_INT64 - -/** - * Demarshals a 64 bit signed integer. - * - * @param str the string containing the data - * @param byte_order the byte order - * @param pos the position in the string - * @param new_pos the new position of the string - * @returns the demarshaled integer. - */ -dbus_int64_t -_dbus_demarshal_int64 (const DBusString *str, - int byte_order, - int pos, - int *new_pos) -{ - DBusOctets8 r; - - r = demarshal_8_octets (str, byte_order, pos, new_pos); - - return r.s; -} - -/** - * Demarshals a 64 bit unsigned integer. - * - * @param str the string containing the data - * @param byte_order the byte order - * @param pos the position in the string - * @param new_pos the new position of the string - * @returns the demarshaled integer. - */ -dbus_uint64_t -_dbus_demarshal_uint64 (const DBusString *str, - int byte_order, - int pos, - int *new_pos) -{ - DBusOctets8 r; - - r = demarshal_8_octets (str, byte_order, pos, new_pos); - - return r.u; -} - -#endif /* DBUS_HAVE_INT64 */ - -/** - * Demarshals a basic type - * - * @param str the string containing the data - * @param type type of value to demarshal - * @param value pointer to return value data - * @param byte_order the byte order - * @param pos pointer to position in the string, - * updated on return to new position - **/ -void -_dbus_demarshal_basic_type (const DBusString *str, - int type, - void *value, - int byte_order, - int *pos) -{ - const char *str_data = _dbus_string_get_const_data (str); - - switch (type) - { - case DBUS_TYPE_BYTE: - case DBUS_TYPE_BOOLEAN: - *(unsigned char *) value = _dbus_string_get_byte (str, *pos); - (*pos)++; - break; - case DBUS_TYPE_INT32: - case DBUS_TYPE_UINT32: - *pos = _DBUS_ALIGN_VALUE (*pos, 4); - *(dbus_uint32_t *) value = *(dbus_uint32_t *)(str_data + *pos); - if (byte_order != DBUS_COMPILER_BYTE_ORDER) - *(dbus_uint32_t *) value = DBUS_UINT32_SWAP_LE_BE (*(dbus_uint32_t *) value); - *pos += 4; - break; -#ifdef DBUS_HAVE_INT64 - case DBUS_TYPE_INT64: - case DBUS_TYPE_UINT64: -#endif /* DBUS_HAVE_INT64 */ - case DBUS_TYPE_DOUBLE: - *pos = _DBUS_ALIGN_VALUE (*pos, 8); - memcpy (value, str_data + *pos, 8); - if (byte_order != DBUS_COMPILER_BYTE_ORDER) -#ifdef DBUS_HAVE_INT64 - *(dbus_uint64_t *) value = DBUS_UINT64_SWAP_LE_BE (*(dbus_uint64_t *) value); -#else - swap_bytes (value, 8); -#endif - *pos += 8; - break; - default: - _dbus_assert_not_reached ("not a basic type"); - break; - } -} - -/** - * Demarshals an UTF-8 string. - * - * @todo Should we check the string to make sure - * that it's valid UTF-8, and maybe "fix" the string - * if it's broken? - * - * @todo Should probably demarshal to a DBusString, - * having memcpy() in here is Evil(tm). - * - * @param str the string containing the data - * @param byte_order the byte order - * @param pos the position in the string - * @param new_pos the new position of the string - * @returns the demarshaled string. - */ -char * -_dbus_demarshal_string (const DBusString *str, - int byte_order, - int pos, - int *new_pos) -{ - int len; - char *retval; - const char *data; - - len = _dbus_demarshal_uint32 (str, byte_order, pos, &pos); - - retval = dbus_malloc (len + 1); - - if (!retval) - return NULL; - - data = _dbus_string_get_const_data_len (str, pos, len + 1); - - if (!data) - return NULL; - - memcpy (retval, data, len + 1); - - if (new_pos) - *new_pos = pos + len + 1; - - return retval; -} - -/** - * Demarshals a byte array. - * - * @todo Should probably demarshal to a DBusString, - * having memcpy() in here is Evil(tm). - * - * @param str the string containing the data - * @param byte_order the byte order - * @param pos the position in the string - * @param new_pos the new position of the string - * @param array the array - * @param array_len length of the demarshaled data - - * @returns #TRUE on success - */ -dbus_bool_t -_dbus_demarshal_byte_array (const DBusString *str, - int byte_order, - int pos, - int *new_pos, - unsigned char **array, - int *array_len) -{ - int len; - unsigned char *retval; - const char *data; - - len = _dbus_demarshal_uint32 (str, byte_order, pos, &pos); - - if (len == 0) - { - *array_len = len; - *array = NULL; - - if (new_pos) - *new_pos = pos; - - return TRUE; - } - - retval = dbus_malloc (len); - - if (!retval) - return FALSE; - - data = _dbus_string_get_const_data_len (str, pos, len); - - if (!data) - { - dbus_free (retval); - return FALSE; - } - - memcpy (retval, data, len); - - if (new_pos) - *new_pos = pos + len; - - *array = retval; - *array_len = len; - - return TRUE; -} - -static dbus_bool_t -demarshal_4_octets_array (const DBusString *str, - int byte_order, - int pos, - int *new_pos, - dbus_uint32_t **array, - int *array_len) -{ - int len, i; - dbus_uint32_t *retval; - int byte_len; - - byte_len = _dbus_demarshal_uint32 (str, byte_order, pos, &pos); - len = byte_len / 4; - - if (len == 0) - { - *array_len = 0; - *array = NULL; - - if (new_pos) - *new_pos = pos; - - return TRUE; - } - - if (!_dbus_string_copy_data_len (str, (char**) &retval, - pos, byte_len)) - return FALSE; - - if (byte_order != DBUS_COMPILER_BYTE_ORDER) - { - for (i = 0; i < len; i++) - retval[i] = DBUS_UINT32_SWAP_LE_BE (retval[i]); - } - - if (new_pos) - *new_pos = pos + byte_len; - - *array_len = len; - *array = retval; - - return TRUE; -} - -static dbus_bool_t -demarshal_8_octets_array (const DBusString *str, - int byte_order, - int pos, - int *new_pos, - DBusOctets8 **array, - int *array_len) -{ - int len, i; - DBusOctets8 *retval; - int byte_len; - - byte_len = _dbus_demarshal_uint32 (str, byte_order, pos, &pos); - len = byte_len / 8; - - if (len == 0) - { - *array_len = 0; - *array = NULL; - - if (new_pos) - *new_pos = pos; - - return TRUE; - } - - if (!_dbus_string_copy_data_len (str, (char**) &retval, - pos, byte_len)) - return FALSE; - - if (byte_order != DBUS_COMPILER_BYTE_ORDER) - { - for (i = 0; i < len; i++) - { -#ifdef DBUS_HAVE_INT64 - retval[i].u = DBUS_UINT64_SWAP_LE_BE (retval[i].u); -#else - swap_bytes ((unsigned char *) &retval[i], 8); -#endif - } - } - - if (new_pos) - *new_pos = pos + byte_len; - - *array_len = len; - *array = retval; - - return TRUE; -} - -/** - * Demarshals a 32 bit signed integer array. - * - * @param str the string containing the data - * @param byte_order the byte order - * @param pos the position in the string - * @param new_pos the new position of the string - * @param array the array - * @param array_len length of the demarshaled data - * @returns #TRUE on success - */ -dbus_bool_t -_dbus_demarshal_int32_array (const DBusString *str, - int byte_order, - int pos, - int *new_pos, - dbus_int32_t **array, - int *array_len) -{ - return demarshal_4_octets_array (str, byte_order, pos, new_pos, - (dbus_uint32_t**) array, array_len); -} - -/** - * Demarshals a 32 bit unsigned integer array. - * - * @param str the string containing the data - * @param byte_order the byte order - * @param pos the position in the string - * @param new_pos the new position of the string - * @param array the array - * @param array_len length of the demarshaled data - * @returns #TRUE on success - */ -dbus_bool_t -_dbus_demarshal_uint32_array (const DBusString *str, - int byte_order, - int pos, - int *new_pos, - dbus_uint32_t **array, - int *array_len) -{ - return demarshal_4_octets_array (str, byte_order, pos, new_pos, - array, array_len); -} - -#ifdef DBUS_HAVE_INT64 - -/** - * Demarshals a 64 bit signed integer array. - * - * @param str the string containing the data - * @param byte_order the byte order - * @param pos the position in the string - * @param new_pos the new position of the string - * @param array the array - * @param array_len length of the demarshaled data - * @returns #TRUE on success - */ -dbus_bool_t -_dbus_demarshal_int64_array (const DBusString *str, - int byte_order, - int pos, - int *new_pos, - dbus_int64_t **array, - int *array_len) -{ - return demarshal_8_octets_array (str, byte_order, pos, new_pos, - (DBusOctets8**) array, array_len); -} - -/** - * Demarshals a 64 bit unsigned integer array. - * - * @param str the string containing the data - * @param byte_order the byte order - * @param pos the position in the string - * @param new_pos the new position of the string - * @param array the array - * @param array_len length of the demarshaled data - * @returns #TRUE on success - */ -dbus_bool_t -_dbus_demarshal_uint64_array (const DBusString *str, - int byte_order, - int pos, - int *new_pos, - dbus_uint64_t **array, - int *array_len) -{ - return demarshal_8_octets_array (str, byte_order, pos, new_pos, - (DBusOctets8**) array, array_len); -} - -#endif /* DBUS_HAVE_INT64 */ - -/** - * Demarshals a double array. - * - * @param str the string containing the data - * @param byte_order the byte order - * @param pos the position in the string - * @param new_pos the new position of the string - * @param array the array - * @param array_len length of the demarshaled data - * @returns #TRUE on success - */ -dbus_bool_t -_dbus_demarshal_double_array (const DBusString *str, - int byte_order, - int pos, - int *new_pos, - double **array, - int *array_len) -{ - return demarshal_8_octets_array (str, byte_order, pos, new_pos, - (DBusOctets8**) array, array_len); -} - - -/** - * Demarshals an array of basic types - * - * @param str the string containing the data - * @param element_type type of array elements to demarshal - * @param array pointer to pointer to array data - * @param array_len pointer to array length - * @param byte_order the byte order - * @param pos pointer to position in the string, - * updated on return to new position - **/ -dbus_bool_t -_dbus_demarshal_basic_type_array (const DBusString *str, - int element_type, - void **array, - int *array_len, - int byte_order, - int *pos) -{ - switch (element_type) - { - case DBUS_TYPE_BOOLEAN: - /* FIXME: do we want to post-normalize these ? */ - case DBUS_TYPE_BYTE: - return _dbus_demarshal_byte_array (str, byte_order, *pos, pos, - (unsigned char **)array, array_len); - break; - case DBUS_TYPE_INT32: - case DBUS_TYPE_UINT32: - return demarshal_4_octets_array (str, byte_order, *pos, pos, - (dbus_uint32_t **)array, array_len); - break; -#ifdef DBUS_HAVE_INT64 - case DBUS_TYPE_INT64: - case DBUS_TYPE_UINT64: -#endif /* DBUS_HAVE_INT64 */ - case DBUS_TYPE_DOUBLE: - return demarshal_8_octets_array (str, byte_order, *pos, pos, - (DBusOctets8**) array, array_len); - default: - _dbus_assert_not_reached ("not a basic type"); - break; - } - return FALSE; -} - -/** - * Demarshals a string array. - * - * @param str the string containing the data - * @param byte_order the byte order - * @param pos the position in the string - * @param new_pos the new position of the string - * @param array the array - * @param array_len location for length of the demarshaled data or NULL - * @returns #TRUE on success - */ -dbus_bool_t -_dbus_demarshal_string_array (const DBusString *str, - int byte_order, - int pos, - int *new_pos, - char ***array, - int *array_len) -{ - int bytes_len, i; - int len, allocated; - int end_pos; - char **retval; - - bytes_len = _dbus_demarshal_uint32 (str, byte_order, pos, &pos); - - if (bytes_len == 0) - { - *array_len = 0; - *array = NULL; - - if (new_pos) - *new_pos = pos; - - return TRUE; - } - - len = 0; - allocated = 4; - end_pos = pos + bytes_len; - - retval = dbus_new (char *, allocated); - - if (!retval) - return FALSE; - - while (pos < end_pos) - { - retval[len] = _dbus_demarshal_string (str, byte_order, pos, &pos); - - if (retval[len] == NULL) - goto error; - - len += 1; - - if (len >= allocated - 1) /* -1 for NULL termination */ - { - char **newp; - newp = dbus_realloc (retval, - sizeof (char*) * allocated * 2); - if (newp == NULL) - goto error; - - allocated *= 2; - retval = newp; - } - } - - retval[len] = NULL; - - if (new_pos) - *new_pos = pos; - - *array = retval; - *array_len = len; - - return TRUE; - - error: - for (i = 0; i < len; i++) - dbus_free (retval[i]); - dbus_free (retval); - - return FALSE; -} - -/** Set to 1 to get a bunch of spew about disassembling the path string */ -#define VERBOSE_DECOMPOSE 0 - -/** - * Decompose an object path. A path of just "/" is - * represented as an empty vector of strings. - * - * @param data the path data - * @param len the length of the path string - * @param path address to store new object path - * @param path_len length of stored path - */ -dbus_bool_t -_dbus_decompose_path (const char* data, - int len, - char ***path, - int *path_len) -{ - char **retval; - int n_components; - int i, j, comp; - - _dbus_assert (data != NULL); - -#if VERBOSE_DECOMPOSE - _dbus_verbose ("Decomposing path \"%s\"\n", - data); -#endif - - n_components = 0; - i = 0; - while (i < len) - { - if (data[i] == '/') - n_components += 1; - ++i; - } - - retval = dbus_new0 (char*, n_components + 1); - - if (retval == NULL) - return FALSE; - - comp = 0; - i = 0; - while (i < len) - { - if (data[i] == '/') - ++i; - j = i; - - while (j < len && data[j] != '/') - ++j; - - /* Now [i, j) is the path component */ - _dbus_assert (i < j); - _dbus_assert (data[i] != '/'); - _dbus_assert (j == len || data[j] == '/'); - -#if VERBOSE_DECOMPOSE - _dbus_verbose (" (component in [%d,%d))\n", - i, j); -#endif - - retval[comp] = _dbus_memdup (&data[i], j - i + 1); - if (retval[comp] == NULL) - { - dbus_free_string_array (retval); - return FALSE; - } - retval[comp][j-i] = '\0'; -#if VERBOSE_DECOMPOSE - _dbus_verbose (" (component %d = \"%s\")\n", - comp, retval[comp]); -#endif - - ++comp; - i = j; - } - _dbus_assert (i == len); - - *path = retval; - if (path_len) - *path_len = n_components; - - return TRUE; -} - -/** - * Demarshals an object path. A path of just "/" is - * represented as an empty vector of strings. - * - * @param str the string containing the data - * @param byte_order the byte order - * @param pos the position in the string - * @param new_pos the new position of the string - * @param path address to store new object path - * @param path_len length of stored path - */ -dbus_bool_t -_dbus_demarshal_object_path (const DBusString *str, - int byte_order, - int pos, - int *new_pos, - char ***path, - int *path_len) -{ - int len; - const char *data; - - len = _dbus_demarshal_uint32 (str, byte_order, pos, &pos); - data = _dbus_string_get_const_data_len (str, pos, len + 1); - - if (!_dbus_decompose_path (data, len, path, path_len)) - return FALSE; - - if (new_pos) - *new_pos = pos + len + 1; - - return TRUE; -} - -/** - * Returns the position right after the end of an argument. PERFORMS - * NO VALIDATION WHATSOEVER. The message must have been previously - * validated. - * - * @param str a string - * @param byte_order the byte order to use - * @param type the type of the argument - * @param pos the pos where the arg starts - * @param end_pos pointer where the position right - * after the end position will follow - * @returns TRUE if more data exists after the arg - */ -dbus_bool_t -_dbus_marshal_get_arg_end_pos (const DBusString *str, - int byte_order, - int type, - int pos, - int *end_pos) -{ - if (pos >= _dbus_string_get_length (str)) - return FALSE; - - switch (type) - { - case DBUS_TYPE_INVALID: - return FALSE; - break; - - case DBUS_TYPE_NIL: - *end_pos = pos; - break; - - case DBUS_TYPE_BYTE: - *end_pos = pos + 1; - break; - - case DBUS_TYPE_BOOLEAN: - *end_pos = pos + 1; - break; - - case DBUS_TYPE_INT32: - case DBUS_TYPE_UINT32: - *end_pos = _DBUS_ALIGN_VALUE (pos, 4) + 4; - break; - - case DBUS_TYPE_INT64: - case DBUS_TYPE_UINT64: - case DBUS_TYPE_DOUBLE: - - *end_pos = _DBUS_ALIGN_VALUE (pos, 8) + 8; - break; - - case DBUS_TYPE_OBJECT_PATH: - case DBUS_TYPE_STRING: - { - int len; - - /* Demarshal the length */ - len = _dbus_demarshal_uint32 (str, byte_order, pos, &pos); - - *end_pos = pos + len + 1; - } - break; - - case DBUS_TYPE_CUSTOM: - { - int len; - - /* Demarshal the string length */ - len = _dbus_demarshal_uint32 (str, byte_order, pos, &pos); - - pos += len + 1; - - /* Demarshal the data length */ - len = _dbus_demarshal_uint32 (str, byte_order, pos, &pos); - - *end_pos = pos + len; - } - break; - - case DBUS_TYPE_ARRAY: - { - int len; - - /* Demarshal the length */ - len = _dbus_demarshal_uint32 (str, byte_order, pos, &pos); - - *end_pos = pos + len; - } - break; - - case DBUS_TYPE_DICT: - { - int len; - - /* Demarshal the length */ - len = _dbus_demarshal_uint32 (str, byte_order, pos, &pos); - - *end_pos = pos + len; - } - break; - - default: - _dbus_warn ("Unknown message arg type %d\n", type); - _dbus_assert_not_reached ("Unknown message argument type\n"); - return FALSE; - } - - if (*end_pos > _dbus_string_get_length (str)) - return FALSE; - - return TRUE; -} - -/** - * Demarshals and validates a length; returns < 0 if the validation - * fails. The length is required to be small enough that - * len*sizeof(double) will not overflow, and small enough to fit in a - * signed integer. DOES NOT check whether the length points - * beyond the end of the string, because it doesn't know the - * size of array elements. - * - * @param str the string - * @param byte_order the byte order - * @param pos the unaligned string position (snap to next aligned) - * @param new_pos return location for new position. - */ -static int -demarshal_and_validate_len (const DBusString *str, - int byte_order, - int pos, - int *new_pos) -{ - int align_4 = _DBUS_ALIGN_VALUE (pos, 4); - unsigned int len; - - _dbus_assert (new_pos != NULL); - - if ((align_4 + 4) > _dbus_string_get_length (str)) - { - _dbus_verbose ("not enough room in message for array length\n"); - return -1; - } - - if (!_dbus_string_validate_nul (str, pos, - align_4 - pos)) - { - _dbus_verbose ("array length alignment padding not initialized to nul at %d\n", pos); - return -1; - } - - len = _dbus_demarshal_uint32 (str, byte_order, align_4, new_pos); - - /* note that the len is the number of bytes, so we need it to be - * at least SIZE_T_MAX, but make it smaller just to keep things - * sane. We end up using ints for most sizes to avoid unsigned mess - * so limit to maximum 32-bit signed int divided by at least 8, more - * for a bit of paranoia margin. INT_MAX/32 is about 65 megabytes. - */ -#define MAX_ARRAY_LENGTH (((unsigned int)_DBUS_INT_MAX) / 32) - if (len > MAX_ARRAY_LENGTH) - { - _dbus_verbose ("array length %u exceeds maximum of %u at pos %d\n", - len, MAX_ARRAY_LENGTH, pos); - return -1; - } - else - return (int) len; -} - -static dbus_bool_t -validate_string (const DBusString *str, - int pos, - int len_without_nul, - int *end_pos) -{ - *end_pos = pos + len_without_nul + 1; - - if (*end_pos > _dbus_string_get_length (str)) - { - _dbus_verbose ("string length outside length of the message\n"); - return FALSE; - } - - if (_dbus_string_get_byte (str, pos + len_without_nul) != '\0') - { - _dbus_verbose ("string arg not nul-terminated\n"); - return FALSE; - } - - if (!_dbus_string_validate_utf8 (str, pos, len_without_nul)) - { - _dbus_verbose ("string is not valid UTF-8\n"); - return FALSE; - } - - return TRUE; -} - -/** - * Validates and returns a typecode at a specific position - * in the message - * - * @param str a string - * @param type the type of the argument - * @param pos the pos where the typecode starts - * @param end_pos pointer where the position right - * after the end position will follow - * @returns #TRUE if the type is valid. - */ -dbus_bool_t -_dbus_marshal_validate_type (const DBusString *str, - int pos, - int *type, - int *end_pos) -{ - const char *data; - - if (pos >= _dbus_string_get_length (str)) - return FALSE; - - data = _dbus_string_get_const_data_len (str, pos, 1); - - if (_dbus_type_is_valid (*data)) - { - *type = *data; - if (end_pos != NULL) - *end_pos = pos + 1; - return TRUE; - } - - _dbus_verbose ("'%c' %d invalid type code\n", (int) *data, (int) *data); - - return FALSE; -} - -/* Faster validator for array data that doesn't call - * validate_arg for each value - */ -static dbus_bool_t -validate_array_data (const DBusString *str, - int byte_order, - int depth, - int type, - int array_type_pos, - int pos, - int *new_pos, - int end) -{ - switch (type) - { - case DBUS_TYPE_INVALID: - return FALSE; - break; - - case DBUS_TYPE_NIL: - break; - - case DBUS_TYPE_OBJECT_PATH: - case DBUS_TYPE_STRING: - case DBUS_TYPE_CUSTOM: - case DBUS_TYPE_ARRAY: - case DBUS_TYPE_DICT: - /* This clean recursion to validate_arg is what we - * are doing logically for all types, but we don't - * really want to call validate_arg for every byte - * in a byte array, so the primitive types are - * special-cased. - */ - while (pos < end) - { - if (!_dbus_marshal_validate_arg (str, byte_order, depth, - type, array_type_pos, pos, &pos)) - return FALSE; - } - break; - - case DBUS_TYPE_BYTE: - pos = end; - break; - - case DBUS_TYPE_BOOLEAN: - while (pos < end) - { - unsigned char c; - - c = _dbus_string_get_byte (str, pos); - - if (!(c == 0 || c == 1)) - { - _dbus_verbose ("boolean value must be either 0 or 1, not %d\n", c); - return FALSE; - } - - ++pos; - } - break; - - case DBUS_TYPE_INT32: - case DBUS_TYPE_UINT32: - /* Call validate arg one time to check alignment padding - * at start of array - */ - if (!_dbus_marshal_validate_arg (str, byte_order, depth, - type, array_type_pos, pos, &pos)) - return FALSE; - pos = _DBUS_ALIGN_VALUE (end, 4); - break; - - case DBUS_TYPE_INT64: - case DBUS_TYPE_UINT64: - case DBUS_TYPE_DOUBLE: - /* Call validate arg one time to check alignment padding - * at start of array - */ - if (!_dbus_marshal_validate_arg (str, byte_order, depth, - type, array_type_pos, pos, &pos)) - return FALSE; - pos = _DBUS_ALIGN_VALUE (end, 8); - break; - - default: - _dbus_verbose ("Unknown message arg type %d\n", type); - return FALSE; - } - - *new_pos = pos; - - return TRUE; -} - -/** - * Validates an argument of a specific type, checking that it - * is well-formed, for example no ludicrous length fields, strings - * are nul-terminated, etc. - * Returns the end position of the argument in end_pos, and - * returns #TRUE if a valid arg begins at "pos" - * - * @todo security: need to audit this function. - * - * @param str a string - * @param byte_order the byte order to use - * @param depth current recursion depth, to prevent excessive recursion - * @param type the type of the argument - * @param array_type_pos the position of the current array type, or - * -1 if not in an array - * @param pos the pos where the arg starts - * @param end_pos pointer where the position right - * after the end position will follow - * @returns #TRUE if the arg is valid. - */ -dbus_bool_t -_dbus_marshal_validate_arg (const DBusString *str, - int byte_order, - int depth, - int type, - int array_type_pos, - int pos, - int *end_pos) -{ - if (pos > _dbus_string_get_length (str)) - { - _dbus_verbose ("Validation went off the end of the message\n"); - return FALSE; - } - -#define MAX_VALIDATION_DEPTH 32 - - if (depth > MAX_VALIDATION_DEPTH) - { - _dbus_verbose ("Maximum recursion depth reached validating message\n"); - return FALSE; - } - - switch (type) - { - case DBUS_TYPE_INVALID: - return FALSE; - break; - - case DBUS_TYPE_NIL: - *end_pos = pos; - break; - - case DBUS_TYPE_BYTE: - if (1 > _dbus_string_get_length (str) - pos) - { - _dbus_verbose ("no room for byte value\n"); - return FALSE; - } - - *end_pos = pos + 1; - break; - - case DBUS_TYPE_BOOLEAN: - { - unsigned char c; - - if (1 > _dbus_string_get_length (str) - pos) - { - _dbus_verbose ("no room for boolean value\n"); - return FALSE; - } - - c = _dbus_string_get_byte (str, pos); - - if (!(c == 0 || c == 1)) - { - _dbus_verbose ("boolean value must be either 0 or 1, not %d\n", c); - return FALSE; - } - - *end_pos = pos + 1; - } - break; - - case DBUS_TYPE_INT32: - case DBUS_TYPE_UINT32: - { - int align_4 = _DBUS_ALIGN_VALUE (pos, 4); - - if (!_dbus_string_validate_nul (str, pos, - align_4 - pos)) - { - _dbus_verbose ("int32/uint32 alignment padding not initialized to nul\n"); - return FALSE; - } - - *end_pos = align_4 + 4; - } - break; - - case DBUS_TYPE_INT64: - case DBUS_TYPE_UINT64: - case DBUS_TYPE_DOUBLE: - { - int align_8 = _DBUS_ALIGN_VALUE (pos, 8); - - _dbus_verbose_bytes_of_string (str, pos, (align_8 + 8 - pos)); - - if (!_dbus_string_validate_nul (str, pos, - align_8 - pos)) - { - _dbus_verbose ("double/int64/uint64/objid alignment padding not initialized to nul at %d\n", pos); - return FALSE; - } - - *end_pos = align_8 + 8; - } - break; - - case DBUS_TYPE_OBJECT_PATH: - case DBUS_TYPE_STRING: - { - int len; - - /* Demarshal the length, which does NOT include - * nul termination - */ - len = demarshal_and_validate_len (str, byte_order, pos, &pos); - if (len < 0) - return FALSE; - - if (!validate_string (str, pos, len, end_pos)) - return FALSE; - - if (type == DBUS_TYPE_OBJECT_PATH) - { - if (!_dbus_string_validate_path (str, pos, len)) - return FALSE; - } - } - break; - - case DBUS_TYPE_CUSTOM: - { - int len; - - /* Demarshal the string length, which does NOT include - * nul termination - */ - len = demarshal_and_validate_len (str, byte_order, pos, &pos); - if (len < 0) - return FALSE; - - if (!validate_string (str, pos, len, &pos)) - return FALSE; - - /* Validate data */ - len = demarshal_and_validate_len (str, byte_order, pos, &pos); - if (len < 0) - return FALSE; - - *end_pos = pos + len; - } - break; - - case DBUS_TYPE_ARRAY: - { - int len; - int end; - int array_type; - - if (array_type_pos == -1) - { - array_type_pos = pos; - - do - { - if (!_dbus_marshal_validate_type (str, pos, &array_type, &pos)) - { - _dbus_verbose ("invalid array type\n"); - return FALSE; - } - - /* NIL values take up no space, so you couldn't iterate over an array of them. - * array of nil seems useless anyway; the useful thing might be array of - * (nil OR string) but we have no framework for that. - */ - if (array_type == DBUS_TYPE_NIL) - { - _dbus_verbose ("array of NIL is not allowed\n"); - return FALSE; - } - } - while (array_type == DBUS_TYPE_ARRAY); - } - else - array_type_pos++; - - if (!_dbus_marshal_validate_type (str, array_type_pos, &array_type, NULL)) - { - _dbus_verbose ("invalid array type\n"); - return FALSE; - } - - len = demarshal_and_validate_len (str, byte_order, pos, &pos); - if (len < 0) - { - _dbus_verbose ("invalid array length (<0)\n"); - return FALSE; - } - - if (len > _dbus_string_get_length (str) - pos) - { - _dbus_verbose ("array length outside length of the message\n"); - return FALSE; - } - - end = pos + len; - - if (len > 0 && !validate_array_data (str, byte_order, depth + 1, - array_type, array_type_pos, - pos, &pos, end)) - { - _dbus_verbose ("invalid array data\n"); - return FALSE; - } - - if (pos < end) - { - /* This should not be able to happen, as long as validate_arg moves forward; - * but the check is here just to be paranoid. - */ - _dbus_verbose ("array length %d specified was longer than actual array contents by %d\n", - len, end - pos); - return FALSE; - } - - if (pos > end) - { - _dbus_verbose ("array contents exceeds array length %d by %d\n", len, pos - end); - return FALSE; - } - - *end_pos = pos; - } - break; - - case DBUS_TYPE_DICT: - { - int dict_type; - int len; - int end; - - len = demarshal_and_validate_len (str, byte_order, pos, &pos); - if (len < 0) - return FALSE; - - if (len > _dbus_string_get_length (str) - pos) - { - _dbus_verbose ("dict length outside length of the message\n"); - return FALSE; - } - - end = pos + len; - - while (pos < end) - { - /* Validate name */ - if (!_dbus_marshal_validate_arg (str, byte_order, depth + 1, - DBUS_TYPE_STRING, -1, pos, &pos)) - return FALSE; - - if (!_dbus_marshal_validate_type (str, pos, &dict_type, &pos)) - { - _dbus_verbose ("invalid dict entry type at offset %d\n", pos); - return FALSE; - } - - /* Validate element */ - if (!_dbus_marshal_validate_arg (str, byte_order, depth + 1, - dict_type, -1, pos, &pos)) - { - _dbus_verbose ("dict arg invalid at offset %d\n", pos); - return FALSE; - } - } - - if (pos > end) - { - _dbus_verbose ("dict contents exceed stated dict length\n"); - return FALSE; - } - - *end_pos = pos; - } - break; - - default: - _dbus_verbose ("Unknown message arg type %d\n", type); - return FALSE; - } - - if (*end_pos > _dbus_string_get_length (str)) - return FALSE; - - return TRUE; -} - -/** - * Return #TRUE if the typecode is a valid typecode - * - * @returns #TRUE if valid - */ -dbus_bool_t -_dbus_type_is_valid (int typecode) -{ - switch (typecode) - { - case DBUS_TYPE_NIL: - case DBUS_TYPE_BYTE: - case DBUS_TYPE_BOOLEAN: - case DBUS_TYPE_INT32: - case DBUS_TYPE_UINT32: - case DBUS_TYPE_INT64: - case DBUS_TYPE_UINT64: - case DBUS_TYPE_DOUBLE: - case DBUS_TYPE_STRING: - case DBUS_TYPE_CUSTOM: - case DBUS_TYPE_ARRAY: - case DBUS_TYPE_DICT: - case DBUS_TYPE_OBJECT_PATH: - return TRUE; - - default: - return FALSE; - } -} - -/** - * If in verbose mode, print a block of binary data. - * - * @todo right now it prints even if not in verbose mode - * - * @param data the data - * @param len the length of the data - */ -void -_dbus_verbose_bytes (const unsigned char *data, - int len) -{ - int i; - const unsigned char *aligned; - - _dbus_assert (len >= 0); - - /* Print blanks on first row if appropriate */ - aligned = _DBUS_ALIGN_ADDRESS (data, 4); - if (aligned > data) - aligned -= 4; - _dbus_assert (aligned <= data); - - if (aligned != data) - { - _dbus_verbose ("%4d\t%p: ", - (data - aligned), aligned); - while (aligned != data) - { - _dbus_verbose (" "); - ++aligned; - } - } - - /* now print the bytes */ - i = 0; - while (i < len) - { - if (_DBUS_ALIGN_ADDRESS (&data[i], 4) == &data[i]) - { - _dbus_verbose ("%4d\t%p: ", - i, &data[i]); - } - - if (data[i] >= 32 && - data[i] <= 126) - _dbus_verbose (" '%c' ", data[i]); - else - _dbus_verbose ("0x%s%x ", - data[i] <= 0xf ? "0" : "", data[i]); - - ++i; - - if (_DBUS_ALIGN_ADDRESS (&data[i], 4) == &data[i]) - { - if (i > 3) - _dbus_verbose ("BE: %d LE: %d", - _dbus_unpack_uint32 (DBUS_BIG_ENDIAN, &data[i-4]), - _dbus_unpack_uint32 (DBUS_LITTLE_ENDIAN, &data[i-4])); - - if (i > 7 && - _DBUS_ALIGN_ADDRESS (&data[i], 8) == &data[i]) - { - _dbus_verbose (" dbl: %g", - *(double*)&data[i-8]); - } - - _dbus_verbose ("\n"); - } - } - - _dbus_verbose ("\n"); -} - -/** - * Dump the given part of the string to verbose log. - * - * @param str the string - * @param start the start of range to dump - * @param len length of range - */ -void -_dbus_verbose_bytes_of_string (const DBusString *str, - int start, - int len) -{ - const char *d; - int real_len; - - real_len = _dbus_string_get_length (str); - - _dbus_assert (start >= 0); - - if (start > real_len) - { - _dbus_verbose (" [%d,%d) is not inside string of length %d\n", - start, len, real_len); - return; - } - - if ((start + len) > real_len) - { - _dbus_verbose (" [%d,%d) extends outside string of length %d\n", - start, len, real_len); - len = real_len - start; - } - - d = _dbus_string_get_const_data_len (str, start, len); - - _dbus_verbose_bytes (d, len); -} - -/** - * Marshals a basic type - * - * @param str string to marshal to - * @param type type of value - * @param value pointer to value - * @param byte_order byte order - * @returns #TRUE on success - **/ -dbus_bool_t -_dbus_marshal_basic_type (DBusString *str, - char type, - void *value, - int byte_order) -{ - dbus_bool_t retval; - - switch (type) - { - case DBUS_TYPE_BYTE: - case DBUS_TYPE_BOOLEAN: - retval = _dbus_string_append_byte (str, *(unsigned char *)value); - break; - case DBUS_TYPE_INT32: - case DBUS_TYPE_UINT32: - return marshal_4_octets (str, byte_order, *(dbus_uint32_t *)value); - break; -#ifdef DBUS_HAVE_INT64 - case DBUS_TYPE_INT64: - case DBUS_TYPE_UINT64: - retval = _dbus_marshal_uint64 (str, byte_order, *(dbus_uint64_t *)value); - break; -#endif /* DBUS_HAVE_INT64 */ - case DBUS_TYPE_DOUBLE: - retval = _dbus_marshal_double (str, byte_order, *(double *)value); - break; - default: - _dbus_assert_not_reached ("not a basic type"); - retval = FALSE; - break; - } - return retval; -} - -/** - * Marshals a basic type array - * - * @param str string to marshal to - * @param element_type type of array elements - * @param value pointer to value - * @param len length of value data in elements - * @param byte_order byte order - * @returns #TRUE on success - **/ -dbus_bool_t -_dbus_marshal_basic_type_array (DBusString *str, - char element_type, - const void *value, - int len, - int byte_order) -{ - switch (element_type) - { - case DBUS_TYPE_BOOLEAN: - /* FIXME: we canonicalize to 0 or 1 for the single boolean case - * should we here too ? */ - case DBUS_TYPE_BYTE: - return _dbus_marshal_byte_array (str, byte_order, value, len); - break; - case DBUS_TYPE_INT32: - case DBUS_TYPE_UINT32: - return marshal_4_octets_array (str, byte_order, value, len); - break; -#ifdef DBUS_HAVE_INT64 - case DBUS_TYPE_INT64: - case DBUS_TYPE_UINT64: -#endif /* DBUS_HAVE_INT64 */ - case DBUS_TYPE_DOUBLE: - return marshal_8_octets_array (str, byte_order, value, len); - break; - default: - _dbus_assert_not_reached ("non basic type in array"); - break; - } - return FALSE; -} - -/** @} */ - -#ifdef DBUS_BUILD_TESTS -#include "dbus-test.h" -#include - -dbus_bool_t -_dbus_marshal_test (void) -{ - DBusString str; - char *tmp1, *tmp2; - int pos = 0, len; - dbus_int32_t array1[3] = { 0x123, 0x456, 0x789 }, *array2; -#ifdef DBUS_HAVE_INT64 - dbus_int64_t array3[3] = { DBUS_INT64_CONSTANT (0x123ffffffff), - DBUS_INT64_CONSTANT (0x456ffffffff), - DBUS_INT64_CONSTANT (0x789ffffffff) }, *array4; -#endif - char *s; - DBusString t; - - if (!_dbus_string_init (&str)) - _dbus_assert_not_reached ("failed to init string"); - - /* Marshal doubles */ - if (!_dbus_marshal_double (&str, DBUS_BIG_ENDIAN, 3.14)) - _dbus_assert_not_reached ("could not marshal double value"); - if (!_dbus_demarshal_double (&str, DBUS_BIG_ENDIAN, pos, &pos) == 3.14) - _dbus_assert_not_reached ("demarshal failed"); - - if (!_dbus_marshal_double (&str, DBUS_LITTLE_ENDIAN, 3.14)) - _dbus_assert_not_reached ("could not marshal double value"); - if (!_dbus_demarshal_double (&str, DBUS_LITTLE_ENDIAN, pos, &pos) == 3.14) - _dbus_assert_not_reached ("demarshal failed"); - - /* Marshal signed integers */ - if (!_dbus_marshal_int32 (&str, DBUS_BIG_ENDIAN, -12345678)) - _dbus_assert_not_reached ("could not marshal signed integer value"); - if (!_dbus_demarshal_int32 (&str, DBUS_BIG_ENDIAN, pos, &pos) == -12345678) - _dbus_assert_not_reached ("demarshal failed"); - - if (!_dbus_marshal_int32 (&str, DBUS_LITTLE_ENDIAN, -12345678)) - _dbus_assert_not_reached ("could not marshal signed integer value"); - if (!_dbus_demarshal_int32 (&str, DBUS_LITTLE_ENDIAN, pos, &pos) == -12345678) - _dbus_assert_not_reached ("demarshal failed"); - - /* Marshal unsigned integers */ - if (!_dbus_marshal_uint32 (&str, DBUS_BIG_ENDIAN, 0x12345678)) - _dbus_assert_not_reached ("could not marshal signed integer value"); - if (!_dbus_demarshal_uint32 (&str, DBUS_BIG_ENDIAN, pos, &pos) == 0x12345678) - _dbus_assert_not_reached ("demarshal failed"); - - if (!_dbus_marshal_uint32 (&str, DBUS_LITTLE_ENDIAN, 0x12345678)) - _dbus_assert_not_reached ("could not marshal signed integer value"); - if (!_dbus_demarshal_uint32 (&str, DBUS_LITTLE_ENDIAN, pos, &pos) == 0x12345678) - _dbus_assert_not_reached ("demarshal failed"); - -#ifdef DBUS_HAVE_INT64 - /* Marshal signed integers */ - if (!_dbus_marshal_int64 (&str, DBUS_BIG_ENDIAN, DBUS_INT64_CONSTANT (-0x123456789abc7))) - _dbus_assert_not_reached ("could not marshal signed integer value"); - if (_dbus_demarshal_int64 (&str, DBUS_BIG_ENDIAN, pos, &pos) != DBUS_INT64_CONSTANT (-0x123456789abc7)) - _dbus_assert_not_reached ("demarshal failed"); - - if (!_dbus_marshal_int64 (&str, DBUS_LITTLE_ENDIAN, DBUS_INT64_CONSTANT (-0x123456789abc7))) - _dbus_assert_not_reached ("could not marshal signed integer value"); - if (_dbus_demarshal_int64 (&str, DBUS_LITTLE_ENDIAN, pos, &pos) != DBUS_INT64_CONSTANT (-0x123456789abc7)) - _dbus_assert_not_reached ("demarshal failed"); - - /* Marshal unsigned integers */ - if (!_dbus_marshal_uint64 (&str, DBUS_BIG_ENDIAN, DBUS_UINT64_CONSTANT (0x123456789abc7))) - _dbus_assert_not_reached ("could not marshal signed integer value"); - if (!(_dbus_demarshal_uint64 (&str, DBUS_BIG_ENDIAN, pos, &pos) == DBUS_UINT64_CONSTANT (0x123456789abc7))) - _dbus_assert_not_reached ("demarshal failed"); - - if (!_dbus_marshal_uint64 (&str, DBUS_LITTLE_ENDIAN, DBUS_UINT64_CONSTANT (0x123456789abc7))) - _dbus_assert_not_reached ("could not marshal signed integer value"); - if (!(_dbus_demarshal_uint64 (&str, DBUS_LITTLE_ENDIAN, pos, &pos) == DBUS_UINT64_CONSTANT (0x123456789abc7))) - _dbus_assert_not_reached ("demarshal failed"); -#endif /* DBUS_HAVE_INT64 */ - - /* Marshal strings */ - tmp1 = "This is the dbus test string"; - if (!_dbus_marshal_string (&str, DBUS_BIG_ENDIAN, tmp1)) - _dbus_assert_not_reached ("could not marshal string"); - tmp2 = _dbus_demarshal_string (&str, DBUS_BIG_ENDIAN, pos, &pos); - if (!strcmp (tmp1, tmp2) == 0) - _dbus_assert_not_reached ("demarshal failed"); - dbus_free (tmp2); - - tmp1 = "This is the dbus test string"; - if (!_dbus_marshal_string (&str, DBUS_LITTLE_ENDIAN, tmp1)) - _dbus_assert_not_reached ("could not marshal string"); - tmp2 = _dbus_demarshal_string (&str, DBUS_LITTLE_ENDIAN, pos, &pos); - if (!strcmp (tmp1, tmp2) == 0) - _dbus_assert_not_reached ("demarshal failed"); - dbus_free (tmp2); - - /* Marshal signed integer arrays */ - if (!_dbus_marshal_int32_array (&str, DBUS_BIG_ENDIAN, array1, 3)) - _dbus_assert_not_reached ("could not marshal integer array"); - if (!_dbus_demarshal_int32_array (&str, DBUS_BIG_ENDIAN, pos, &pos, &array2, &len)) - _dbus_assert_not_reached ("could not demarshal integer array"); - - if (len != 3) - _dbus_assert_not_reached ("Signed integer array lengths differ!\n"); - dbus_free (array2); - -#ifdef DBUS_HAVE_INT64 - /* Marshal 64-bit signed integer arrays */ - if (!_dbus_marshal_int64_array (&str, DBUS_BIG_ENDIAN, array3, 3)) - _dbus_assert_not_reached ("could not marshal integer array"); - if (!_dbus_demarshal_int64_array (&str, DBUS_BIG_ENDIAN, pos, &pos, &array4, &len)) - _dbus_assert_not_reached ("could not demarshal integer array"); - - if (len != 3) - _dbus_assert_not_reached ("Signed integer array lengths differ!\n"); - dbus_free (array4); - - /* set/pack 64-bit integers */ - _dbus_string_set_length (&str, 8); - - /* signed little */ - _dbus_marshal_set_int64 (&str, DBUS_LITTLE_ENDIAN, - 0, DBUS_INT64_CONSTANT (-0x123456789abc7)); - - _dbus_assert (DBUS_INT64_CONSTANT (-0x123456789abc7) == - _dbus_unpack_int64 (DBUS_LITTLE_ENDIAN, - _dbus_string_get_const_data (&str))); - - /* signed big */ - _dbus_marshal_set_int64 (&str, DBUS_BIG_ENDIAN, - 0, DBUS_INT64_CONSTANT (-0x123456789abc7)); - - _dbus_assert (DBUS_INT64_CONSTANT (-0x123456789abc7) == - _dbus_unpack_int64 (DBUS_BIG_ENDIAN, - _dbus_string_get_const_data (&str))); - - /* signed little pack */ - _dbus_pack_int64 (DBUS_INT64_CONSTANT (-0x123456789abc7), - DBUS_LITTLE_ENDIAN, - _dbus_string_get_data (&str)); - - _dbus_assert (DBUS_INT64_CONSTANT (-0x123456789abc7) == - _dbus_unpack_int64 (DBUS_LITTLE_ENDIAN, - _dbus_string_get_const_data (&str))); - - /* signed big pack */ - _dbus_pack_int64 (DBUS_INT64_CONSTANT (-0x123456789abc7), - DBUS_BIG_ENDIAN, - _dbus_string_get_data (&str)); - - _dbus_assert (DBUS_INT64_CONSTANT (-0x123456789abc7) == - _dbus_unpack_int64 (DBUS_BIG_ENDIAN, - _dbus_string_get_const_data (&str))); - - /* unsigned little */ - _dbus_marshal_set_uint64 (&str, DBUS_LITTLE_ENDIAN, - 0, DBUS_UINT64_CONSTANT (0x123456789abc7)); - - _dbus_assert (DBUS_UINT64_CONSTANT (0x123456789abc7) == - _dbus_unpack_uint64 (DBUS_LITTLE_ENDIAN, - _dbus_string_get_const_data (&str))); - - /* unsigned big */ - _dbus_marshal_set_uint64 (&str, DBUS_BIG_ENDIAN, - 0, DBUS_UINT64_CONSTANT (0x123456789abc7)); - - _dbus_assert (DBUS_UINT64_CONSTANT (0x123456789abc7) == - _dbus_unpack_uint64 (DBUS_BIG_ENDIAN, - _dbus_string_get_const_data (&str))); - - /* unsigned little pack */ - _dbus_pack_uint64 (DBUS_UINT64_CONSTANT (0x123456789abc7), - DBUS_LITTLE_ENDIAN, - _dbus_string_get_data (&str)); - - _dbus_assert (DBUS_UINT64_CONSTANT (0x123456789abc7) == - _dbus_unpack_uint64 (DBUS_LITTLE_ENDIAN, - _dbus_string_get_const_data (&str))); - - /* unsigned big pack */ - _dbus_pack_uint64 (DBUS_UINT64_CONSTANT (0x123456789abc7), - DBUS_BIG_ENDIAN, - _dbus_string_get_data (&str)); - - _dbus_assert (DBUS_UINT64_CONSTANT (0x123456789abc7) == - _dbus_unpack_uint64 (DBUS_BIG_ENDIAN, - _dbus_string_get_const_data (&str))); - -#endif - - /* set/pack 32-bit integers */ - _dbus_string_set_length (&str, 4); - - /* signed little */ - _dbus_marshal_set_int32 (&str, DBUS_LITTLE_ENDIAN, - 0, -0x123456); - - _dbus_assert (-0x123456 == - _dbus_unpack_int32 (DBUS_LITTLE_ENDIAN, - _dbus_string_get_const_data (&str))); - - /* signed big */ - _dbus_marshal_set_int32 (&str, DBUS_BIG_ENDIAN, - 0, -0x123456); - - _dbus_assert (-0x123456 == - _dbus_unpack_int32 (DBUS_BIG_ENDIAN, - _dbus_string_get_const_data (&str))); - - /* signed little pack */ - _dbus_pack_int32 (-0x123456, - DBUS_LITTLE_ENDIAN, - _dbus_string_get_data (&str)); - - _dbus_assert (-0x123456 == - _dbus_unpack_int32 (DBUS_LITTLE_ENDIAN, - _dbus_string_get_const_data (&str))); - - /* signed big pack */ - _dbus_pack_int32 (-0x123456, - DBUS_BIG_ENDIAN, - _dbus_string_get_data (&str)); - - _dbus_assert (-0x123456 == - _dbus_unpack_int32 (DBUS_BIG_ENDIAN, - _dbus_string_get_const_data (&str))); - - /* unsigned little */ - _dbus_marshal_set_uint32 (&str, DBUS_LITTLE_ENDIAN, - 0, 0x123456); - - _dbus_assert (0x123456 == - _dbus_unpack_uint32 (DBUS_LITTLE_ENDIAN, - _dbus_string_get_const_data (&str))); - - /* unsigned big */ - _dbus_marshal_set_uint32 (&str, DBUS_BIG_ENDIAN, - 0, 0x123456); - - _dbus_assert (0x123456 == - _dbus_unpack_uint32 (DBUS_BIG_ENDIAN, - _dbus_string_get_const_data (&str))); - - /* unsigned little pack */ - _dbus_pack_uint32 (0x123456, - DBUS_LITTLE_ENDIAN, - _dbus_string_get_data (&str)); - - _dbus_assert (0x123456 == - _dbus_unpack_uint32 (DBUS_LITTLE_ENDIAN, - _dbus_string_get_const_data (&str))); - - /* unsigned big pack */ - _dbus_pack_uint32 (0x123456, - DBUS_BIG_ENDIAN, - _dbus_string_get_data (&str)); - - _dbus_assert (0x123456 == - _dbus_unpack_uint32 (DBUS_BIG_ENDIAN, - _dbus_string_get_const_data (&str))); - - - /* Strings */ - - _dbus_string_set_length (&str, 0); - - _dbus_marshal_string (&str, DBUS_LITTLE_ENDIAN, - "Hello world"); - - s = _dbus_demarshal_string (&str, DBUS_LITTLE_ENDIAN, 0, NULL); - _dbus_assert (strcmp (s, "Hello world") == 0); - dbus_free (s); - - _dbus_string_init_const (&t, "Hello world foo"); - - _dbus_marshal_set_string (&str, DBUS_LITTLE_ENDIAN, 0, - &t, _dbus_string_get_length (&t)); - - s = _dbus_demarshal_string (&str, DBUS_LITTLE_ENDIAN, 0, NULL); - _dbus_assert (strcmp (s, "Hello world foo") == 0); - dbus_free (s); - - _dbus_string_init_const (&t, "Hello"); - - _dbus_marshal_set_string (&str, DBUS_LITTLE_ENDIAN, 0, - &t, _dbus_string_get_length (&t)); - - s = _dbus_demarshal_string (&str, DBUS_LITTLE_ENDIAN, 0, NULL); - _dbus_assert (strcmp (s, "Hello") == 0); - dbus_free (s); - - /* Strings (big endian) */ - - _dbus_string_set_length (&str, 0); - - _dbus_marshal_string (&str, DBUS_BIG_ENDIAN, - "Hello world"); - - s = _dbus_demarshal_string (&str, DBUS_BIG_ENDIAN, 0, NULL); - _dbus_assert (strcmp (s, "Hello world") == 0); - dbus_free (s); - - _dbus_string_init_const (&t, "Hello world foo"); - - _dbus_marshal_set_string (&str, DBUS_BIG_ENDIAN, 0, - &t, _dbus_string_get_length (&t)); - - s = _dbus_demarshal_string (&str, DBUS_BIG_ENDIAN, 0, NULL); - _dbus_assert (strcmp (s, "Hello world foo") == 0); - dbus_free (s); - - _dbus_string_init_const (&t, "Hello"); - - _dbus_marshal_set_string (&str, DBUS_BIG_ENDIAN, 0, - &t, _dbus_string_get_length (&t)); - - s = _dbus_demarshal_string (&str, DBUS_BIG_ENDIAN, 0, NULL); - _dbus_assert (strcmp (s, "Hello") == 0); - dbus_free (s); - - _dbus_string_free (&str); - - return TRUE; -} - -#endif /* DBUS_BUILD_TESTS */ diff --git a/dbus/dbus-marshal.h b/dbus/dbus-marshal.h deleted file mode 100644 index dccfc1db..00000000 --- a/dbus/dbus-marshal.h +++ /dev/null @@ -1,348 +0,0 @@ -/* -*- mode: C; c-file-style: "gnu" -*- */ -/* dbus-marshal.h Marshalling routines - * - * Copyright (C) 2002 CodeFactory AB - * - * Licensed under the Academic Free License version 2.1 - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation; either version 2 of the License, or - * (at your option) any later version. - * - * This program 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 General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program; if not, write to the Free Software - * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA - * - */ - -#ifndef DBUS_MARSHAL_H -#define DBUS_MARSHAL_H - -#include -#include -#include -#include -#include - -#ifndef PACKAGE -#error "config.h not included here" -#endif - -#ifdef WORDS_BIGENDIAN -#define DBUS_COMPILER_BYTE_ORDER DBUS_BIG_ENDIAN -#else -#define DBUS_COMPILER_BYTE_ORDER DBUS_LITTLE_ENDIAN -#endif - -#define DBUS_UINT32_SWAP_LE_BE_CONSTANT(val) ((dbus_uint32_t) ( \ - (((dbus_uint32_t) (val) & (dbus_uint32_t) 0x000000ffU) << 24) | \ - (((dbus_uint32_t) (val) & (dbus_uint32_t) 0x0000ff00U) << 8) | \ - (((dbus_uint32_t) (val) & (dbus_uint32_t) 0x00ff0000U) >> 8) | \ - (((dbus_uint32_t) (val) & (dbus_uint32_t) 0xff000000U) >> 24))) - -#ifdef DBUS_HAVE_INT64 - -#define DBUS_UINT64_SWAP_LE_BE_CONSTANT(val) ((dbus_uint64_t) ( \ - (((dbus_uint64_t) (val) & \ - (dbus_uint64_t) DBUS_UINT64_CONSTANT (0x00000000000000ff)) << 56) | \ - (((dbus_uint64_t) (val) & \ - (dbus_uint64_t) DBUS_UINT64_CONSTANT (0x000000000000ff00)) << 40) | \ - (((dbus_uint64_t) (val) & \ - (dbus_uint64_t) DBUS_UINT64_CONSTANT (0x0000000000ff0000)) << 24) | \ - (((dbus_uint64_t) (val) & \ - (dbus_uint64_t) DBUS_UINT64_CONSTANT (0x00000000ff000000)) << 8) | \ - (((dbus_uint64_t) (val) & \ - (dbus_uint64_t) DBUS_UINT64_CONSTANT (0x000000ff00000000)) >> 8) | \ - (((dbus_uint64_t) (val) & \ - (dbus_uint64_t) DBUS_UINT64_CONSTANT (0x0000ff0000000000)) >> 24) | \ - (((dbus_uint64_t) (val) & \ - (dbus_uint64_t) DBUS_UINT64_CONSTANT (0x00ff000000000000)) >> 40) | \ - (((dbus_uint64_t) (val) & \ - (dbus_uint64_t) DBUS_UINT64_CONSTANT (0xff00000000000000)) >> 56))) -#endif /* DBUS_HAVE_INT64 */ - -#define DBUS_UINT32_SWAP_LE_BE(val) (DBUS_UINT32_SWAP_LE_BE_CONSTANT (val)) -#define DBUS_INT32_SWAP_LE_BE(val) ((dbus_int32_t)DBUS_UINT32_SWAP_LE_BE_CONSTANT (val)) - -#ifdef DBUS_HAVE_INT64 -#define DBUS_UINT64_SWAP_LE_BE(val) (DBUS_UINT64_SWAP_LE_BE_CONSTANT (val)) -#define DBUS_INT64_SWAP_LE_BE(val) ((dbus_int64_t)DBUS_UINT64_SWAP_LE_BE_CONSTANT (val)) -#endif /* DBUS_HAVE_INT64 */ - -#ifdef WORDS_BIGENDIAN -#define DBUS_INT32_TO_BE(val) ((dbus_int32_t) (val)) -#define DBUS_UINT32_TO_BE(val) ((dbus_uint32_t) (val)) -#define DBUS_INT32_TO_LE(val) (DBUS_INT32_SWAP_LE_BE (val)) -#define DBUS_UINT32_TO_LE(val) (DBUS_UINT32_SWAP_LE_BE (val)) -# ifdef DBUS_HAVE_INT64 -#define DBUS_INT64_TO_BE(val) ((dbus_int64_t) (val)) -#define DBUS_UINT64_TO_BE(val) ((dbus_uint64_t) (val)) -#define DBUS_INT64_TO_LE(val) (DBUS_INT64_SWAP_LE_BE (val)) -#define DBUS_UINT64_TO_LE(val) (DBUS_UINT64_SWAP_LE_BE (val)) -# endif /* DBUS_HAVE_INT64 */ -#else -#define DBUS_INT32_TO_LE(val) ((dbus_int32_t) (val)) -#define DBUS_UINT32_TO_LE(val) ((dbus_uint32_t) (val)) -#define DBUS_INT32_TO_BE(val) ((dbus_int32_t) DBUS_UINT32_SWAP_LE_BE (val)) -#define DBUS_UINT32_TO_BE(val) (DBUS_UINT32_SWAP_LE_BE (val)) -# ifdef DBUS_HAVE_INT64 -#define DBUS_INT64_TO_LE(val) ((dbus_int64_t) (val)) -#define DBUS_UINT64_TO_LE(val) ((dbus_uint64_t) (val)) -#define DBUS_INT64_TO_BE(val) ((dbus_int64_t) DBUS_UINT64_SWAP_LE_BE (val)) -#define DBUS_UINT64_TO_BE(val) (DBUS_UINT64_SWAP_LE_BE (val)) -# endif /* DBUS_HAVE_INT64 */ -#endif - -/* The transformation is symmetric, so the FROM just maps to the TO. */ -#define DBUS_INT32_FROM_LE(val) (DBUS_INT32_TO_LE (val)) -#define DBUS_UINT32_FROM_LE(val) (DBUS_UINT32_TO_LE (val)) -#define DBUS_INT32_FROM_BE(val) (DBUS_INT32_TO_BE (val)) -#define DBUS_UINT32_FROM_BE(val) (DBUS_UINT32_TO_BE (val)) -#ifdef DBUS_HAVE_INT64 -#define DBUS_INT64_FROM_LE(val) (DBUS_INT64_TO_LE (val)) -#define DBUS_UINT64_FROM_LE(val) (DBUS_UINT64_TO_LE (val)) -#define DBUS_INT64_FROM_BE(val) (DBUS_INT64_TO_BE (val)) -#define DBUS_UINT64_FROM_BE(val) (DBUS_UINT64_TO_BE (val)) -#endif /* DBUS_HAVE_INT64 */ - -void _dbus_pack_int32 (dbus_int32_t value, - int byte_order, - unsigned char *data); -dbus_int32_t _dbus_unpack_int32 (int byte_order, - const unsigned char *data); -void _dbus_pack_uint32 (dbus_uint32_t value, - int byte_order, - unsigned char *data); -dbus_uint32_t _dbus_unpack_uint32 (int byte_order, - const unsigned char *data); -#ifdef DBUS_HAVE_INT64 -void _dbus_pack_int64 (dbus_int64_t value, - int byte_order, - unsigned char *data); -dbus_int64_t _dbus_unpack_int64 (int byte_order, - const unsigned char *data); -void _dbus_pack_uint64 (dbus_uint64_t value, - int byte_order, - unsigned char *data); -dbus_uint64_t _dbus_unpack_uint64 (int byte_order, - const unsigned char *data); -#endif /* DBUS_HAVE_INT64 */ - -void _dbus_marshal_set_int32 (DBusString *str, - int byte_order, - int offset, - dbus_int32_t value); -void _dbus_marshal_set_uint32 (DBusString *str, - int byte_order, - int offset, - dbus_uint32_t value); -#ifdef DBUS_HAVE_INT64 -void _dbus_marshal_set_int64 (DBusString *str, - int byte_order, - int offset, - dbus_int64_t value); -void _dbus_marshal_set_uint64 (DBusString *str, - int byte_order, - int offset, - dbus_uint64_t value); -#endif /* DBUS_HAVE_INT64 */ - -dbus_bool_t _dbus_marshal_set_string (DBusString *str, - int byte_order, - int offset, - const DBusString *value, - int len); -void _dbus_marshal_set_object_path (DBusString *str, - int byte_order, - int offset, - const char **path, - int path_len); - -dbus_bool_t _dbus_marshal_int32 (DBusString *str, - int byte_order, - dbus_int32_t value); -dbus_bool_t _dbus_marshal_uint32 (DBusString *str, - int byte_order, - dbus_uint32_t value); -#ifdef DBUS_HAVE_INT64 -dbus_bool_t _dbus_marshal_int64 (DBusString *str, - int byte_order, - dbus_int64_t value); -dbus_bool_t _dbus_marshal_uint64 (DBusString *str, - int byte_order, - dbus_uint64_t value); -#endif /* DBUS_HAVE_INT64 */ -dbus_bool_t _dbus_marshal_double (DBusString *str, - int byte_order, - double value); -dbus_bool_t _dbus_marshal_string (DBusString *str, - int byte_order, - const char *value); -dbus_bool_t _dbus_marshal_string_len (DBusString *str, - int byte_order, - const char *value, - int len); -dbus_bool_t _dbus_marshal_basic_type (DBusString *str, - char type, - void *value, - int byte_order); -dbus_bool_t _dbus_marshal_byte_array (DBusString *str, - int byte_order, - const unsigned char *value, - int len); -dbus_bool_t _dbus_marshal_int32_array (DBusString *str, - int byte_order, - const dbus_int32_t *value, - int len); -dbus_bool_t _dbus_marshal_uint32_array (DBusString *str, - int byte_order, - const dbus_uint32_t *value, - int len); -#ifdef DBUS_HAVE_INT64 -dbus_bool_t _dbus_marshal_int64_array (DBusString *str, - int byte_order, - const dbus_int64_t *value, - int len); -dbus_bool_t _dbus_marshal_uint64_array (DBusString *str, - int byte_order, - const dbus_uint64_t *value, - int len); -#endif /* DBUS_HAVE_INT64 */ -dbus_bool_t _dbus_marshal_double_array (DBusString *str, - int byte_order, - const double *value, - int len); -dbus_bool_t _dbus_marshal_basic_type_array (DBusString *str, - char element_type, - const void *value, - int len, - int byte_order); - -dbus_bool_t _dbus_marshal_string_array (DBusString *str, - int byte_order, - const char **value, - int len); -dbus_bool_t _dbus_marshal_object_path (DBusString *str, - int byte_order, - const char **path, - int path_len); - -double _dbus_demarshal_double (const DBusString *str, - int byte_order, - int pos, - int *new_pos); -dbus_int32_t _dbus_demarshal_int32 (const DBusString *str, - int byte_order, - int pos, - int *new_pos); -dbus_uint32_t _dbus_demarshal_uint32 (const DBusString *str, - int byte_order, - int pos, - int *new_pos); -#ifdef DBUS_HAVE_INT64 -dbus_int64_t _dbus_demarshal_int64 (const DBusString *str, - int byte_order, - int pos, - int *new_pos); -dbus_uint64_t _dbus_demarshal_uint64 (const DBusString *str, - int byte_order, - int pos, - int *new_pos); -#endif /* DBUS_HAVE_INT64 */ -void _dbus_demarshal_basic_type (const DBusString *str, - int type, - void *value, - int byte_order, - int *pos); -char * _dbus_demarshal_string (const DBusString *str, - int byte_order, - int pos, - int *new_pos); -dbus_bool_t _dbus_demarshal_byte_array (const DBusString *str, - int byte_order, - int pos, - int *new_pos, - unsigned char **array, - int *array_len); -dbus_bool_t _dbus_demarshal_int32_array (const DBusString *str, - int byte_order, - int pos, - int *new_pos, - dbus_int32_t **array, - int *array_len); -dbus_bool_t _dbus_demarshal_uint32_array (const DBusString *str, - int byte_order, - int pos, - int *new_pos, - dbus_uint32_t **array, - int *array_len); -#ifdef DBUS_HAVE_INT64 -dbus_bool_t _dbus_demarshal_int64_array (const DBusString *str, - int byte_order, - int pos, - int *new_pos, - dbus_int64_t **array, - int *array_len); -dbus_bool_t _dbus_demarshal_uint64_array (const DBusString *str, - int byte_order, - int pos, - int *new_pos, - dbus_uint64_t **array, - int *array_len); -#endif /* DBUS_HAVE_INT64 */ -dbus_bool_t _dbus_demarshal_double_array (const DBusString *str, - int byte_order, - int pos, - int *new_pos, - double **array, - int *array_len); -dbus_bool_t _dbus_demarshal_basic_type_array (const DBusString *str, - int type, - void **array, - int *array_len, - int byte_order, - int *pos); - -dbus_bool_t _dbus_demarshal_string_array (const DBusString *str, - int byte_order, - int pos, - int *new_pos, - char ***array, - int *array_len); -dbus_bool_t _dbus_decompose_path (const char* data, - int len, - char ***path, - int *path_len); -dbus_bool_t _dbus_demarshal_object_path (const DBusString *str, - int byte_order, - int pos, - int *new_pos, - char ***path, - int *path_len); - -dbus_bool_t _dbus_marshal_get_arg_end_pos (const DBusString *str, - int byte_order, - int type, - int pos, - int *end_pos); -dbus_bool_t _dbus_marshal_validate_type (const DBusString *str, - int pos, - int *type, - int *end_pos); -dbus_bool_t _dbus_marshal_validate_arg (const DBusString *str, - int depth, - int byte_order, - int type, - int array_type_pos, - int pos, - int *end_pos); - -dbus_bool_t _dbus_type_is_valid (int typecode); - -#endif /* DBUS_MARSHAL_H */ diff --git a/dbus/dbus-memory.c b/dbus/dbus-memory.c index 3b59c26f..b28c6b11 100644 --- a/dbus/dbus-memory.c +++ b/dbus/dbus-memory.c @@ -317,7 +317,8 @@ source_string (BlockSource source) } static void -check_guards (void *free_block) +check_guards (void *free_block, + dbus_bool_t overwrite) { if (free_block != NULL) { @@ -364,6 +365,10 @@ check_guards (void *free_block) i += 4; } + /* set memory to anything but nul bytes */ + if (overwrite) + memset (free_block, 'g', requested_bytes); + if (failed) _dbus_assert_not_reached ("guard value corruption"); } @@ -401,7 +406,7 @@ set_guards (void *real_block, i += 4; } - check_guards (block + GUARD_START_OFFSET); + check_guards (block + GUARD_START_OFFSET, FALSE); return block + GUARD_START_OFFSET; } @@ -558,7 +563,7 @@ dbus_realloc (void *memory, size_t old_bytes; void *block; - check_guards (memory); + check_guards (memory, FALSE); block = realloc (((unsigned char*)memory) - GUARD_START_OFFSET, bytes + GUARD_EXTRA_SIZE); @@ -566,7 +571,7 @@ dbus_realloc (void *memory, old_bytes = *(dbus_uint32_t*)block; if (block && bytes >= old_bytes) /* old guards shouldn't have moved */ - check_guards (((unsigned char*)block) + GUARD_START_OFFSET); + check_guards (((unsigned char*)block) + GUARD_START_OFFSET, FALSE); return set_guards (block, bytes, SOURCE_REALLOC); } @@ -607,7 +612,7 @@ dbus_free (void *memory) #ifdef DBUS_BUILD_TESTS if (guards) { - check_guards (memory); + check_guards (memory, TRUE); if (memory) { n_blocks_outstanding -= 1; diff --git a/dbus/dbus-message-builder.c b/dbus/dbus-message-builder.c index 7b2b67ef..b5b7564e 100644 --- a/dbus/dbus-message-builder.c +++ b/dbus/dbus-message-builder.c @@ -1051,9 +1051,10 @@ _dbus_message_data_load (DBusString *dest, goto parse_failed; } - _dbus_marshal_set_uint32 (dest, endian, size_offset, + _dbus_marshal_set_uint32 (dest, size_offset, /* subtract 1 for nul */ - _dbus_string_get_length (dest) - old_len - 1); + _dbus_string_get_length (dest) - old_len - 1, + endian); PERFORM_UNALIGN (dest); } @@ -1094,9 +1095,10 @@ _dbus_message_data_load (DBusString *dest, goto parse_failed; } - _dbus_marshal_set_uint32 (dest, endian, size_offset, + _dbus_marshal_set_uint32 (dest, size_offset, /* subtract 1 for nul */ - _dbus_string_get_length (dest) - old_len - 1); + _dbus_string_get_length (dest) - old_len - 1, + endian); PERFORM_UNALIGN (dest); } diff --git a/dbus/dbus-message-internal.h b/dbus/dbus-message-internal.h index cd8f82ad..f03e13f6 100644 --- a/dbus/dbus-message-internal.h +++ b/dbus/dbus-message-internal.h @@ -38,7 +38,7 @@ void _dbus_message_get_network_data (DBusMessage *message, void _dbus_message_lock (DBusMessage *message); void _dbus_message_unlock (DBusMessage *message); void _dbus_message_set_serial (DBusMessage *message, - dbus_int32_t serial); + dbus_uint32_t serial); dbus_bool_t _dbus_message_add_size_counter (DBusMessage *message, DBusCounter *counter); void _dbus_message_add_size_counter_link (DBusMessage *message, diff --git a/dbus/dbus-message.c b/dbus/dbus-message.c index 143ac96a..8cb3dc64 100644 --- a/dbus/dbus-message.c +++ b/dbus/dbus-message.c @@ -1,11 +1,11 @@ /* -*- mode: C; c-file-style: "gnu" -*- */ /* dbus-message.c DBusMessage object * - * Copyright (C) 2002, 2003, 2004 Red Hat Inc. + * Copyright (C) 2002, 2003, 2004, 2005 Red Hat Inc. * Copyright (C) 2002, 2003 CodeFactory AB * * Licensed under the Academic Free License version 2.1 - * + * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or @@ -15,7 +15,7 @@ * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. - * + * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA @@ -23,12 +23,14 @@ */ #include "dbus-internals.h" -#include "dbus-marshal.h" +#include "dbus-marshal-recursive.h" +#include "dbus-marshal-validate.h" +#include "dbus-marshal-header.h" #include "dbus-message.h" #include "dbus-message-internal.h" +#include "dbus-object-tree.h" #include "dbus-memory.h" #include "dbus-list.h" -#include "dbus-message-builder.h" #include "dbus-dataslot.h" #include @@ -42,34 +44,24 @@ * @{ */ -/** - * Cached information about a header field in the message +static dbus_bool_t dbus_message_iter_get_args (DBusMessageIter *iter, + DBusError *error, + int first_arg_type, + ...); +static dbus_bool_t dbus_message_iter_get_args_valist (DBusMessageIter *iter, + DBusError *error, + int first_arg_type, + va_list var_args); + +/* Not thread locked, but strictly const/read-only so should be OK */ -typedef struct -{ - int name_offset; /**< Offset to name of field */ - int value_offset; /**< Offset to value of field */ -} HeaderField; - -/** Offset to byte order from start of header */ -#define BYTE_ORDER_OFFSET 0 -/** Offset to type from start of header */ -#define TYPE_OFFSET 1 -/** Offset to flags from start of header */ -#define FLAGS_OFFSET 2 -/** Offset to version from start of header */ -#define VERSION_OFFSET 3 -/** Offset to header length from start of header */ -#define HEADER_LENGTH_OFFSET 4 -/** Offset to body length from start of header */ -#define BODY_LENGTH_OFFSET 8 -/** Offset to client serial from start of header */ -#define CLIENT_SERIAL_OFFSET 12 +_DBUS_STRING_DEFINE_STATIC(_dbus_empty_signature_str, ""); +#define CHANGED_STAMP_BITS 21 /** * @brief Internals of DBusMessage - * + * * Object representing a message received from or to be sent to * another application. This is an opaque object, all members * are private. @@ -78,30 +70,18 @@ struct DBusMessage { DBusAtomic refcount; /**< Reference count */ - DBusString header; /**< Header network data, stored - * separately from body so we can - * independently realloc it. - */ + DBusHeader header; /**< Header network data and associated cache */ - HeaderField header_fields[DBUS_HEADER_FIELD_LAST + 1]; /**< Track the location - * of each field in "header" - */ - - dbus_uint32_t client_serial; /**< Cached client serial value for speed */ - dbus_uint32_t reply_serial; /**< Cached reply serial value for speed */ - - int header_padding; /**< bytes of alignment in header */ - DBusString body; /**< Body network data. */ char byte_order; /**< Message byte order. */ unsigned int locked : 1; /**< Message being sent, no modifications allowed. */ - + DBusList *size_counters; /**< 0-N DBusCounter used to track message size. */ long size_counter_delta; /**< Size we incremented the size counters by. */ - dbus_uint32_t changed_stamp; /**< Incremented when iterators are invalidated. */ + dbus_uint32_t changed_stamp : CHANGED_STAMP_BITS; /**< Incremented when iterators are invalidated. */ DBusDataSlotList slot_list; /**< Data stored by allocated integer ID */ @@ -110,10 +90,12 @@ struct DBusMessage #endif }; +/* these have wacky values to help trap uninitialized iterators; + * but has to fit in 3 bits + */ enum { - DBUS_MESSAGE_ITER_TYPE_MESSAGE, - DBUS_MESSAGE_ITER_TYPE_ARRAY, - DBUS_MESSAGE_ITER_TYPE_DICT + DBUS_MESSAGE_ITER_TYPE_READER = 3, + DBUS_MESSAGE_ITER_TYPE_WRITER = 7 }; /** typedef for internals of message iterator */ @@ -121,27 +103,20 @@ typedef struct DBusMessageRealIter DBusMessageRealIter; /** * @brief Internals of DBusMessageIter - * + * * Object representing a position in a message. All fields are internal. */ struct DBusMessageRealIter { - DBusMessageRealIter *parent_iter; /**< parent iter, or NULL */ DBusMessage *message; /**< Message used */ - dbus_uint32_t changed_stamp; /**< stamp to detect invalid iters */ - - /* This is an int instead of an enum to get a guaranteed size for the dummy: */ - int type; /**< type of iter */ - - int pos; /**< Current position in the string */ - int end; /**< position right after the container */ - int container_start; /**< offset of the start of the container */ - int container_length_pos; /**< offset of the length of the container */ - - int wrote_dict_key; /**< whether we wrote the dict key for the current dict element */ - - int array_type_pos; /**< pointer to the position of the array element type */ - int array_type_done; /**< TRUE if the array type is fully finished */ + dbus_uint32_t changed_stamp : CHANGED_STAMP_BITS; /**< stamp to detect invalid iters */ + dbus_uint32_t iter_type : 3; /**< whether this is a reader or writer iter */ + dbus_uint32_t sig_refcount : 8; /**< depth of open_signature() */ + union + { + DBusTypeWriter writer; + DBusTypeReader reader; + } u; }; /** @@ -160,3483 +135,1367 @@ _dbus_message_get_network_data (DBusMessage *message, const DBusString **body) { _dbus_assert (message->locked); - - *header = &message->header; + + *header = &message->header.data; *body = &message->body; } -static void -clear_header_padding (DBusMessage *message) -{ - _dbus_string_shorten (&message->header, - message->header_padding); - message->header_padding = 0; -} - -#ifdef DBUS_DISABLE_CHECKS -#define is_valid_error_name(x) TRUE -#else -static dbus_bool_t -is_valid_error_name (const char *error_name) +/** + * Sets the serial number of a message. + * This can only be done once on a message. + * + * @param message the message + * @param serial the serial + */ +void +_dbus_message_set_serial (DBusMessage *message, + dbus_uint32_t serial) { - DBusString the_error_name; + _dbus_return_if_fail (message != NULL); + _dbus_return_if_fail (!message->locked); + _dbus_return_if_fail (dbus_message_get_serial (message) == 0); - if (error_name == NULL) - return FALSE; - - _dbus_string_init_const (&the_error_name, error_name); - return _dbus_string_validate_error_name (&the_error_name, 0, - _dbus_string_get_length (&the_error_name)); + _dbus_header_set_serial (&message->header, serial); } -#endif -static dbus_bool_t -append_header_padding (DBusMessage *message) +/** + * Adds a counter to be incremented immediately with the + * size of this message, and decremented by the size + * of this message when this message if finalized. + * The link contains a counter with its refcount already + * incremented, but the counter itself not incremented. + * Ownership of link and counter refcount is passed to + * the message. + * + * @param message the message + * @param link link with counter as data + */ +void +_dbus_message_add_size_counter_link (DBusMessage *message, + DBusList *link) { - int old_len; - old_len = _dbus_string_get_length (&message->header); - if (!_dbus_string_align_length (&message->header, 8)) - return FALSE; + /* right now we don't recompute the delta when message + * size changes, and that's OK for current purposes + * I think, but could be important to change later. + * Do recompute it whenever there are no outstanding counters, + * since it's basically free. + */ + if (message->size_counters == NULL) + { + message->size_counter_delta = + _dbus_string_get_length (&message->header.data) + + _dbus_string_get_length (&message->body); - message->header_padding = _dbus_string_get_length (&message->header) - old_len; +#if 0 + _dbus_verbose ("message has size %ld\n", + message->size_counter_delta); +#endif + } - return TRUE; -} + _dbus_list_append_link (&message->size_counters, link); -#ifdef DBUS_BUILD_TESTS -/* tests-only until it's actually used */ -static dbus_int32_t -get_int_field (DBusMessage *message, - int field) -{ - int offset; - - _dbus_assert (field <= DBUS_HEADER_FIELD_LAST); - - offset = message->header_fields[field].value_offset; - - if (offset < 0) - return -1; /* useless if -1 is a valid value of course */ - - return _dbus_demarshal_int32 (&message->header, - message->byte_order, - offset, - NULL); + _dbus_counter_adjust (link->data, message->size_counter_delta); } -#endif -static dbus_uint32_t -get_uint_field (DBusMessage *message, - int field) +/** + * Adds a counter to be incremented immediately with the + * size of this message, and decremented by the size + * of this message when this message if finalized. + * + * @param message the message + * @param counter the counter + * @returns #FALSE if no memory + */ +dbus_bool_t +_dbus_message_add_size_counter (DBusMessage *message, + DBusCounter *counter) { - int offset; - - _dbus_assert (field <= DBUS_HEADER_FIELD_LAST); - - offset = message->header_fields[field].value_offset; - - if (offset < 0) - return 0; /* useless if 0 is a valid value of course */ - - return _dbus_demarshal_uint32 (&message->header, - message->byte_order, - offset, - NULL); + DBusList *link; + + link = _dbus_list_alloc_link (counter); + if (link == NULL) + return FALSE; + + _dbus_counter_ref (counter); + _dbus_message_add_size_counter_link (message, link); + + return TRUE; } -static const char* -get_string_field (DBusMessage *message, - int field, - int *len) +/** + * Removes a counter tracking the size of this message, and decrements + * the counter by the size of this message. + * + * @param message the message + * @param link_return return the link used + * @param counter the counter + */ +void +_dbus_message_remove_size_counter (DBusMessage *message, + DBusCounter *counter, + DBusList **link_return) { - int offset; - const char *data; + DBusList *link; - _dbus_return_val_if_fail (message->generation == _dbus_current_generation, - NULL); - - offset = message->header_fields[field].value_offset; + link = _dbus_list_find_last (&message->size_counters, + counter); + _dbus_assert (link != NULL); - _dbus_assert (field <= DBUS_HEADER_FIELD_LAST); - - if (offset < 0) - return NULL; + _dbus_list_unlink (&message->size_counters, + link); + if (link_return) + *link_return = link; + else + _dbus_list_free_link (link); - /* offset points to string length, string data follows it */ - /* FIXME _dbus_demarshal_const_string() that returned - * a reference to the string plus its len might be nice. - */ - - if (len) - *len = _dbus_demarshal_uint32 (&message->header, - message->byte_order, - offset, - NULL); + _dbus_counter_adjust (counter, - message->size_counter_delta); - data = _dbus_string_get_const_data (&message->header); - - return data + (offset + 4); + _dbus_counter_unref (counter); } -/* returns FALSE if no memory, TRUE with NULL path if no field */ -static dbus_bool_t -get_path_field_decomposed (DBusMessage *message, - int field, - char ***path) +/** + * Locks a message. Allows checking that applications don't keep a + * reference to a message in the outgoing queue and change it + * underneath us. Messages are locked when they enter the outgoing + * queue (dbus_connection_send_message()), and the library complains + * if the message is modified while locked. + * + * @param message the message to lock. + */ +void +_dbus_message_lock (DBusMessage *message) { - int offset; + if (!message->locked) + { + _dbus_header_update_lengths (&message->header, + _dbus_string_get_length (&message->body)); - offset = message->header_fields[field].value_offset; + /* must have a signature if you have a body */ + _dbus_assert (_dbus_string_get_length (&message->body) == 0 || + dbus_message_get_signature (message) != NULL); - _dbus_assert (field <= DBUS_HEADER_FIELD_LAST); - - if (offset < 0) - { - *path = NULL; - return TRUE; + message->locked = TRUE; } - - return _dbus_demarshal_object_path (&message->header, - message->byte_order, - offset, - NULL, - path, NULL); } -#ifdef DBUS_BUILD_TESTS static dbus_bool_t -append_int_field (DBusMessage *message, - int field, - int value) +set_or_delete_string_field (DBusMessage *message, + int field, + int typecode, + const char *value) { - _dbus_assert (!message->locked); - - clear_header_padding (message); - - message->header_fields[field].name_offset = - _dbus_string_get_length (&message->header); - - if (!_dbus_string_append_byte (&message->header, field)) - goto failed; - - if (!_dbus_string_append_byte (&message->header, DBUS_TYPE_INT32)) - goto failed; - - if (!_dbus_string_align_length (&message->header, 4)) - goto failed; - - message->header_fields[field].value_offset = - _dbus_string_get_length (&message->header); - - if (!_dbus_marshal_int32 (&message->header, message->byte_order, - value)) - goto failed; - - if (!append_header_padding (message)) - goto failed; - - return TRUE; - - failed: - _dbus_string_set_length (&message->header, - message->header_fields[field].name_offset); - message->header_fields[field].name_offset = -1; - message->header_fields[field].value_offset = -1; + if (value == NULL) + return _dbus_header_delete_field (&message->header, field); + else + return _dbus_header_set_field_basic (&message->header, + field, + typecode, + &value); +} - /* this must succeed because it was allocated on function entry and - * DBusString doesn't ever realloc smaller - */ - if (!append_header_padding (message)) - _dbus_assert_not_reached ("failed to reappend header padding"); - return FALSE; +static void +get_const_signature (DBusHeader *header, + 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)) + { + *type_pos_p += 1; /* skip the signature length which is 1 byte */ + } + else + { + *type_str_p = &_dbus_empty_signature_str; + *type_pos_p = 0; + } } -#endif +#if 0 +/* Probably we don't need to use this */ +/** + * Sets the signature of the message, i.e. the arguments in the + * message payload. The signature includes only "in" arguments for + * #DBUS_MESSAGE_TYPE_METHOD_CALL and only "out" arguments for + * #DBUS_MESSAGE_TYPE_METHOD_RETURN, so is slightly different from + * what you might expect (it does not include the signature of the + * entire C++-style method). + * + * The signature is a string made up of type codes such as + * #DBUS_TYPE_INT32. The string is terminated with nul (nul is also + * the value of #DBUS_TYPE_INVALID). The macros such as + * #DBUS_TYPE_INT32 evaluate to integers; to assemble a signature you + * may find it useful to use the string forms, such as + * #DBUS_TYPE_INT32_AS_STRING. + * + * An "unset" or #NULL signature is considered the same as an empty + * signature. In fact dbus_message_get_signature() will never return + * #NULL. + * + * @param message the message + * @param signature the type signature or #NULL to unset + * @returns #FALSE if no memory + */ static dbus_bool_t -append_uint_field (DBusMessage *message, - int field, - int value) +_dbus_message_set_signature (DBusMessage *message, + const char *signature) { - _dbus_assert (!message->locked); - - clear_header_padding (message); - - message->header_fields[field].name_offset = - _dbus_string_get_length (&message->header); - - if (!_dbus_string_append_byte (&message->header, field)) - goto failed; - - if (!_dbus_string_append_byte (&message->header, DBUS_TYPE_UINT32)) - goto failed; + _dbus_return_val_if_fail (message != NULL, FALSE); + _dbus_return_val_if_fail (!message->locked, FALSE); + _dbus_return_val_if_fail (signature == NULL || + _dbus_check_is_valid_signature (signature)); + /* can't delete the signature if you have a message body */ + _dbus_return_val_if_fail (_dbus_string_get_length (&message->body) == 0 || + signature != NULL); - if (!_dbus_string_align_length (&message->header, 4)) - goto failed; - - message->header_fields[field].value_offset = - _dbus_string_get_length (&message->header); - - if (!_dbus_marshal_uint32 (&message->header, message->byte_order, - value)) - goto failed; + return set_or_delete_string_field (message, + DBUS_HEADER_FIELD_SIGNATURE, + DBUS_TYPE_SIGNATURE, + signature); +} +#endif - if (!append_header_padding (message)) - goto failed; - - return TRUE; - - failed: - _dbus_string_set_length (&message->header, - message->header_fields[field].name_offset); - message->header_fields[field].name_offset = -1; - message->header_fields[field].value_offset = -1; +/** @} */ - /* this must succeed because it was allocated on function entry and - * DBusString doesn't ever realloc smaller - */ - if (!append_header_padding (message)) - _dbus_assert_not_reached ("failed to reappend header padding"); - return FALSE; -} +/** + * @defgroup DBusMessage DBusMessage + * @ingroup DBus + * @brief Message to be sent or received over a DBusConnection. + * + * A DBusMessage is the most basic unit of communication over a + * DBusConnection. A DBusConnection represents a stream of messages + * received from a remote application, and a stream of messages + * sent to a remote application. + * + * @{ + */ -/** The maximum number of bytes of overhead to append to a string - * (fieldcode + typecode + alignment + length + nul + headerpadding) +/** + * @typedef DBusMessage + * + * Opaque data type representing a message received from or to be + * sent to another application. */ -#define MAX_BYTES_OVERHEAD_TO_APPEND_A_STRING (1 + 1 + 3 + 4 + 1 + 8) -static dbus_bool_t -append_string_field_len (DBusMessage *message, - int field, - int type, - const char *value, - int value_len) +/** + * Returns the serial of a message or 0 if none has been specified. + * The message's serial number is provided by the application sending + * the message and is used to identify replies to this message. All + * messages received on a connection will have a serial, but messages + * you haven't sent yet may return 0. + * + * @param message the message + * @returns the client serial + */ +dbus_uint32_t +dbus_message_get_serial (DBusMessage *message) { - _dbus_assert (!message->locked); - - clear_header_padding (message); - - message->header_fields[field].name_offset = - _dbus_string_get_length (&message->header); - - if (!_dbus_string_append_byte (&message->header, field)) - goto failed; - - if (!_dbus_string_append_byte (&message->header, type)) - goto failed; + _dbus_return_val_if_fail (message != NULL, 0); - if (!_dbus_string_align_length (&message->header, 4)) - goto failed; - - message->header_fields[field].value_offset = - _dbus_string_get_length (&message->header); - - if (!_dbus_marshal_string_len (&message->header, message->byte_order, - value, value_len)) - goto failed; + return _dbus_header_get_serial (&message->header); +} - if (!append_header_padding (message)) - goto failed; - - return TRUE; - - failed: - _dbus_string_set_length (&message->header, - message->header_fields[field].name_offset); - message->header_fields[field].name_offset = -1; - message->header_fields[field].value_offset = -1; +/** + * Sets the reply serial of a message (the client serial + * of the message this is a reply to). + * + * @param message the message + * @param reply_serial the client serial + * @returns #FALSE if not enough memory + */ +dbus_bool_t +dbus_message_set_reply_serial (DBusMessage *message, + dbus_uint32_t reply_serial) +{ + _dbus_return_val_if_fail (message != NULL, FALSE); + _dbus_return_val_if_fail (!message->locked, FALSE); - /* this must succeed because it was allocated on function entry and - * DBusString doesn't ever realloc smaller - */ - if (!append_header_padding (message)) - _dbus_assert_not_reached ("failed to reappend header padding"); - - return FALSE; + return _dbus_header_set_field_basic (&message->header, + DBUS_HEADER_FIELD_REPLY_SERIAL, + DBUS_TYPE_UINT32, + &reply_serial); } -static dbus_bool_t -append_string_field (DBusMessage *message, - int field, - int type, - const char *value) +/** + * Returns the serial that the message is a reply to or 0 if none. + * + * @param message the message + * @returns the reply serial + */ +dbus_uint32_t +dbus_message_get_reply_serial (DBusMessage *message) { - int value_len; + dbus_uint32_t v_UINT32; - value_len = strlen (value); + _dbus_return_val_if_fail (message != NULL, 0); - return append_string_field_len (message, field, type, value, value_len); + if (_dbus_header_get_field_basic (&message->header, + DBUS_HEADER_FIELD_REPLY_SERIAL, + DBUS_TYPE_UINT32, + &v_UINT32)) + return v_UINT32; + else + return 0; } -static int -get_type_alignment (int type) +static void +free_size_counter (void *element, + void *data) { - int alignment; - - switch (type) - { - case DBUS_TYPE_NIL: - case DBUS_TYPE_BYTE: - case DBUS_TYPE_BOOLEAN: - alignment = 0; - break; - - case DBUS_TYPE_INT32: - case DBUS_TYPE_UINT32: - case DBUS_TYPE_STRING: - case DBUS_TYPE_OBJECT_PATH: - /* These are aligned 4 because they have a length as the - * first field; - */ - case DBUS_TYPE_CUSTOM: - case DBUS_TYPE_DICT: - alignment = 4; - break; - - case DBUS_TYPE_INT64: - case DBUS_TYPE_UINT64: - case DBUS_TYPE_DOUBLE: - alignment = 8; - break; - - case DBUS_TYPE_ARRAY: - _dbus_assert_not_reached ("passed an ARRAY type to get_type_alignment()"); - alignment = 0; /* quiet gcc */ - break; - - case DBUS_TYPE_INVALID: - default: - _dbus_assert_not_reached ("passed an invalid or unknown type to get_type_alignment()"); - alignment = 0; /* quiet gcc */ - break; - } + DBusCounter *counter = element; + DBusMessage *message = data; - return alignment; + _dbus_counter_adjust (counter, - message->size_counter_delta); + + _dbus_counter_unref (counter); } -static dbus_bool_t -iterate_one_field (const DBusString *str, - int byte_order, - int name_offset, - int *next_offset_p, - int *field_name_p, - DBusString *append_copy_to, - int *copy_name_offset_p, - int *copy_value_offset_p) +static void +dbus_message_finalize (DBusMessage *message) { - int name, type, array_type; - int alignment; - int type_len; - int type_pos; - int value_pos; - int value_len; - int value_end; - int pos; + _dbus_assert (message->refcount.value == 0); -#if 0 - _dbus_verbose ("%s: name_offset=%d, append to %p\n", - _DBUS_FUNCTION_NAME, name_offset, append_copy_to); -#endif - - pos = name_offset; - - name = _dbus_string_get_byte (str, name_offset); - pos++; - - type_pos = pos; - type = _dbus_string_get_byte (str, type_pos); - pos++; - type_len = 1; - - array_type = type; - /* find out the type of our array */ - while (array_type == DBUS_TYPE_ARRAY) - { - pos++; - type_len++; - array_type = _dbus_string_get_byte (str, pos); - } + /* This calls application callbacks! */ + _dbus_data_slot_list_free (&message->slot_list); -#if 0 - _dbus_verbose ("%s: name %s, type '%c' %d at %d len %d, array type '%c' %d\n", - _DBUS_FUNCTION_NAME, - _dbus_header_field_to_string (name), - type, type, type_pos, type_len, array_type, array_type); -#endif - -#ifndef DBUS_DISABLE_ASSERT - if (!_dbus_type_is_valid (array_type)) - { - _dbus_warn ("type '%c' %d is not valid in %s\n", - array_type, array_type, _DBUS_FUNCTION_NAME); - _dbus_assert_not_reached ("invalid type"); - } -#endif - - alignment = get_type_alignment (array_type); - - if (alignment > 0) - pos = _DBUS_ALIGN_VALUE (pos, alignment); - - _dbus_verbose ("%s: alignment %d value at pos %d\n", - _DBUS_FUNCTION_NAME, alignment, pos); - - /* pos now points to our value */ - if (!_dbus_marshal_get_arg_end_pos (str, byte_order, - type, pos, &value_end)) - _dbus_assert_not_reached ("failed to get the byte after this header"); - - value_pos = pos; - value_len = value_end - value_pos; - - _dbus_verbose ("%s: value_pos %d value_len %d value_end %d\n", - _DBUS_FUNCTION_NAME, value_pos, value_len, value_end); - - if (next_offset_p) - *next_offset_p = pos + value_len; - - if (field_name_p) - *field_name_p = name; - - if (append_copy_to) - { - int orig_len; - - orig_len = _dbus_string_get_length (append_copy_to); - - if (copy_name_offset_p) - *copy_name_offset_p = orig_len; - - if (!_dbus_string_append_byte (append_copy_to, name)) - goto failed_copy; - - if (!_dbus_string_copy_len (str, type_pos, type_len, - append_copy_to, - _dbus_string_get_length (append_copy_to))) - goto failed_copy; - - if (!_dbus_string_align_length (append_copy_to, alignment)) - goto failed_copy; - - if (copy_value_offset_p) - *copy_value_offset_p = _dbus_string_get_length (append_copy_to); - - if (!_dbus_string_copy_len (str, value_pos, value_len, - append_copy_to, - _dbus_string_get_length (append_copy_to))) - goto failed_copy; - - return TRUE; + _dbus_list_foreach (&message->size_counters, + free_size_counter, message); + _dbus_list_clear (&message->size_counters); - failed_copy: - _dbus_verbose ("%s: Failed copying old fields to new string\n", - _DBUS_FUNCTION_NAME); - _dbus_string_set_length (append_copy_to, orig_len); - return FALSE; - } - else - return TRUE; + _dbus_header_free (&message->header); + _dbus_string_free (&message->body); + + dbus_free (message); } -#ifndef DBUS_DISABLE_ASSERT +/* Message Cache + * + * We cache some DBusMessage to reduce the overhead of allocating + * them. In my profiling this consistently made about an 8% + * difference. It avoids the malloc for the message, the malloc for + * the slot list, the malloc for the header string and body string, + * and the associated free() calls. It does introduce another global + * lock which could be a performance issue in certain cases. + * + * For the echo client/server the round trip time goes from around + * .000077 to .000069 with the message cache on my laptop. The sysprof + * change is as follows (numbers are cumulative percentage): + * + * with message cache implemented as array as it is now (0.000069 per): + * new_empty_header 1.46 + * mutex_lock 0.56 # i.e. _DBUS_LOCK(message_cache) + * mutex_unlock 0.25 + * self 0.41 + * unref 2.24 + * self 0.68 + * list_clear 0.43 + * mutex_lock 0.33 # i.e. _DBUS_LOCK(message_cache) + * mutex_unlock 0.25 + * + * with message cache implemented as list (0.000070 per roundtrip): + * new_empty_header 2.72 + * list_pop_first 1.88 + * unref 3.3 + * list_prepend 1.63 + * + * without cache (0.000077 per roundtrip): + * new_empty_header 6.7 + * string_init_preallocated 3.43 + * dbus_malloc 2.43 + * dbus_malloc0 2.59 + * + * unref 4.02 + * string_free 1.82 + * dbus_free 1.63 + * dbus_free 0.71 + * + * If you implement the message_cache with a list, the primary reason + * it's slower is that you add another thread lock (on the DBusList + * mempool). + */ + +/* Avoid caching huge messages */ +#define MAX_MESSAGE_SIZE_TO_CACHE _DBUS_ONE_MEGABYTE +/* Avoid caching too many messages */ +#define MAX_MESSAGE_CACHE_SIZE 5 + +_DBUS_DEFINE_GLOBAL_LOCK (message_cache); +static DBusMessage *message_cache[MAX_MESSAGE_CACHE_SIZE]; +static int message_cache_count = 0; +static dbus_bool_t message_cache_shutdown_registered = FALSE; + static void -verify_header_fields (DBusMessage *message) +dbus_message_cache_shutdown (void *data) { int i; + + _DBUS_LOCK (message_cache); + i = 0; - while (i <= DBUS_HEADER_FIELD_LAST) + while (i < MAX_MESSAGE_CACHE_SIZE) { - if (message->header_fields[i].name_offset >= 0) - _dbus_assert (_dbus_string_get_byte (&message->header, - message->header_fields[i].name_offset) == - i); + if (message_cache[i]) + dbus_message_finalize (message_cache[i]); + ++i; } + + message_cache_count = 0; + message_cache_shutdown_registered = FALSE; + + _DBUS_UNLOCK (message_cache); } -#else /* DBUS_DISABLE_ASSERT */ -#define verify_header_fields(x) -#endif /* DBUS_DISABLE_ASSERT */ -/* In this function we delete one field and re-align all the fields - * following it. +/** + * Tries to get a message from the message cache. The retrieved + * message will have junk in it, so it still needs to be cleared out + * in dbus_message_new_empty_header() + * + * @returns the message, or #NULL if none cached */ -static dbus_bool_t -delete_one_and_re_align (DBusMessage *message, - int name_offset_to_delete) +static DBusMessage* +dbus_message_get_cached (void) { - DBusString copy; - int new_fields_front_padding; - int next_offset; - int field_name; - dbus_bool_t retval; - HeaderField new_header_fields[DBUS_HEADER_FIELD_LAST+1]; - - _dbus_assert (name_offset_to_delete < _dbus_string_get_length (&message->header)); - verify_header_fields (message); - - _dbus_verbose ("%s: Deleting one field at offset %d\n", - _DBUS_FUNCTION_NAME, - name_offset_to_delete); - - retval = FALSE; + DBusMessage *message; + int i; + + message = NULL; + + _DBUS_LOCK (message_cache); + + _dbus_assert (message_cache_count >= 0); - clear_header_padding (message); - - if (!_dbus_string_init_preallocated (©, - _dbus_string_get_length (&message->header) - - name_offset_to_delete + 8)) + if (message_cache_count == 0) { - _dbus_verbose ("%s: Failed to init string to hold copy of fields\n", - _DBUS_FUNCTION_NAME); - goto out_0; + _DBUS_UNLOCK (message_cache); + return NULL; } - - /* Align the name offset of the first field in the same way it's - * aligned in the real header - */ - new_fields_front_padding = name_offset_to_delete % 8; - - if (!_dbus_string_insert_bytes (©, 0, new_fields_front_padding, - '\0')) - _dbus_assert_not_reached ("Should not have failed to insert bytes into preallocated string\n"); - - memcpy (new_header_fields, message->header_fields, - sizeof (new_header_fields)); - - /* Now just re-marshal each field in the header to our temporary - * buffer, skipping the first one. The tricky part is that the - * fields are padded as if for previous_name_offset, but are in fact - * at unaligned_name_offset + + /* This is not necessarily true unless count > 0, and + * message_cache is uninitialized until the shutdown is + * registered */ + _dbus_assert (message_cache_shutdown_registered); - if (!iterate_one_field (&message->header, - message->byte_order, - name_offset_to_delete, - &next_offset, - &field_name, NULL, NULL, NULL)) - _dbus_assert_not_reached ("shouldn't have failed to alloc memory to skip the deleted field"); - - _dbus_verbose (" Skipping %s field which will be deleted; next_offset = %d\n", - _dbus_header_field_to_string (field_name), next_offset); - - /* This field no longer exists */ - if (field_name <= DBUS_HEADER_FIELD_LAST) - { - new_header_fields[field_name].name_offset = -1; - new_header_fields[field_name].value_offset = -1; - } - - while (next_offset < _dbus_string_get_length (&message->header)) + i = 0; + while (i < MAX_MESSAGE_CACHE_SIZE) { - int copy_name_offset; - int copy_value_offset; - - if (!iterate_one_field (&message->header, - message->byte_order, - next_offset, - &next_offset, - &field_name, - ©, - ©_name_offset, - ©_value_offset)) - { - _dbus_verbose ("%s: OOM iterating one field\n", - _DBUS_FUNCTION_NAME); - goto out_1; - } - - if (field_name <= DBUS_HEADER_FIELD_LAST) + if (message_cache[i]) { - new_header_fields[field_name].name_offset = copy_name_offset - new_fields_front_padding + name_offset_to_delete; - new_header_fields[field_name].value_offset = copy_value_offset - new_fields_front_padding + name_offset_to_delete; - - _dbus_verbose (" Re-adding %s field at name_offset = %d value_offset = %d; next_offset = %d\n", - _dbus_header_field_to_string (field_name), - new_header_fields[field_name].name_offset, - new_header_fields[field_name].value_offset, - next_offset); + message = message_cache[i]; + message_cache[i] = NULL; + message_cache_count -= 1; + break; } + ++i; } + _dbus_assert (message_cache_count >= 0); + _dbus_assert (i < MAX_MESSAGE_CACHE_SIZE); + _dbus_assert (message != NULL); - if (!_dbus_string_replace_len (©, - new_fields_front_padding, - _dbus_string_get_length (©) - new_fields_front_padding, - &message->header, - name_offset_to_delete, - _dbus_string_get_length (&message->header) - name_offset_to_delete)) - { - _dbus_verbose ("%s: OOM moving copy back into header\n", - _DBUS_FUNCTION_NAME); - goto out_1; - } - - memcpy (message->header_fields, new_header_fields, - sizeof (new_header_fields)); - verify_header_fields (message); - - retval = TRUE; - - out_1: - _dbus_string_free (©); - - out_0: - if (!append_header_padding (message)) - _dbus_assert_not_reached ("Failed to re-append header padding in re_align_field_recurse()"); - - return retval; + _DBUS_UNLOCK (message_cache); + + _dbus_assert (message->refcount.value == 0); + _dbus_assert (message->size_counters == NULL); + + return message; } -static dbus_bool_t -delete_field (DBusMessage *message, - int field, - int prealloc_header_space) +/** + * Tries to cache a message, otherwise finalize it. + * + * @param message the message + */ +static void +dbus_message_cache_or_finalize (DBusMessage *message) { - int offset; + dbus_bool_t was_cached; + int i; - _dbus_assert (!message->locked); + _dbus_assert (message->refcount.value == 0); - /* Prealloc */ - if (!_dbus_string_lengthen (&message->header, prealloc_header_space)) - { - _dbus_verbose ("failed to prealloc %d bytes header space\n", - prealloc_header_space); - return FALSE; - } - _dbus_string_shorten (&message->header, prealloc_header_space); + /* This calls application code and has to be done first thing + * without holding the lock + */ + _dbus_data_slot_list_clear (&message->slot_list); - /* Delete */ - offset = message->header_fields[field].name_offset; - if (offset < 0) + _dbus_list_foreach (&message->size_counters, + free_size_counter, message); + _dbus_list_clear (&message->size_counters); + + was_cached = FALSE; + + _DBUS_LOCK (message_cache); + + if (!message_cache_shutdown_registered) { - _dbus_verbose ("header field didn't exist, no need to delete\n"); - return TRUE; /* field didn't exist */ + _dbus_assert (message_cache_count == 0); + + if (!_dbus_register_shutdown_func (dbus_message_cache_shutdown, NULL)) + goto out; + + i = 0; + while (i < MAX_MESSAGE_CACHE_SIZE) + { + message_cache[i] = NULL; + ++i; + } + + message_cache_shutdown_registered = TRUE; } - return delete_one_and_re_align (message, offset); + _dbus_assert (message_cache_count >= 0); + + if ((_dbus_string_get_length (&message->header.data) + + _dbus_string_get_length (&message->body)) > + MAX_MESSAGE_SIZE_TO_CACHE) + goto out; + + if (message_cache_count >= MAX_MESSAGE_CACHE_SIZE) + goto out; + + /* Find empty slot */ + i = 0; + while (message_cache[i] != NULL) + ++i; + + _dbus_assert (i < MAX_MESSAGE_CACHE_SIZE); + + _dbus_assert (message_cache[i] == NULL); + message_cache[i] = message; + message_cache_count += 1; + was_cached = TRUE; + + out: + _DBUS_UNLOCK (message_cache); + + if (!was_cached) + dbus_message_finalize (message); } -#ifdef DBUS_BUILD_TESTS -static dbus_bool_t -set_int_field (DBusMessage *message, - int field, - int value) +static DBusMessage* +dbus_message_new_empty_header (void) { - int offset = message->header_fields[field].value_offset; + DBusMessage *message; + dbus_bool_t from_cache; - _dbus_assert (!message->locked); + message = dbus_message_get_cached (); - _dbus_verbose ("set_int_field() field %d value '%d'\n", - field, value); - - if (offset < 0) + if (message != NULL) { - /* need to append the field */ - return append_int_field (message, field, value); + from_cache = TRUE; } else { - _dbus_marshal_set_int32 (&message->header, - message->byte_order, - offset, value); - - return TRUE; - } -} + from_cache = FALSE; + message = dbus_new (DBusMessage, 1); + if (message == NULL) + return NULL; +#ifndef DBUS_DISABLE_CHECKS + message->generation = _dbus_current_generation; #endif + } -static dbus_bool_t -set_uint_field (DBusMessage *message, - int field, - dbus_uint32_t value) -{ - int offset = message->header_fields[field].value_offset; + message->refcount.value = 1; + message->byte_order = DBUS_COMPILER_BYTE_ORDER; + message->locked = FALSE; + message->size_counters = NULL; + message->size_counter_delta = 0; + message->changed_stamp = 0; - _dbus_assert (!message->locked); + if (!from_cache) + _dbus_data_slot_list_init (&message->slot_list); - _dbus_verbose ("set_uint_field() field %d value '%u'\n", - field, value); - - if (offset < 0) + if (from_cache) { - /* need to append the field */ - return append_uint_field (message, field, value); + _dbus_header_reinit (&message->header, message->byte_order); + _dbus_string_set_length (&message->body, 0); } else { - _dbus_marshal_set_uint32 (&message->header, - message->byte_order, - offset, value); - - return TRUE; - } -} - -static dbus_bool_t -set_string_field (DBusMessage *message, - int field, - int type, - const char *value) -{ - int prealloc; - int value_len; - - _dbus_assert (!message->locked); - - value_len = value ? strlen (value) : 0; - - /* the prealloc is so the append_string_field() - * below won't fail, leaving us in inconsistent state - */ - prealloc = value_len + MAX_BYTES_OVERHEAD_TO_APPEND_A_STRING; - - _dbus_verbose ("set_string_field() field %d prealloc %d value '%s'\n", - field, prealloc, value); - - if (!delete_field (message, field, prealloc)) - return FALSE; - - if (value != NULL) - { - /* need to append the field */ - if (!append_string_field_len (message, field, type, value, value_len)) - _dbus_assert_not_reached ("Appending string field shouldn't have failed, due to preallocation"); - } - - return TRUE; -} - -/** - * Sets the serial number of a message. - * This can only be done once on a message. - * - * @param message the message - * @param serial the serial - */ -void -_dbus_message_set_serial (DBusMessage *message, - dbus_int32_t serial) -{ - _dbus_assert (!message->locked); - _dbus_assert (dbus_message_get_serial (message) == 0); - - _dbus_marshal_set_uint32 (&message->header, - message->byte_order, - CLIENT_SERIAL_OFFSET, - serial); - - message->client_serial = serial; -} - -/** - * Sets the reply serial of a message (the client serial - * of the message this is a reply to). - * - * @param message the message - * @param reply_serial the client serial - * @returns #FALSE if not enough memory - */ -dbus_bool_t -dbus_message_set_reply_serial (DBusMessage *message, - dbus_uint32_t reply_serial) -{ - _dbus_assert (!message->locked); - - if (set_uint_field (message, - DBUS_HEADER_FIELD_REPLY_SERIAL, - reply_serial)) - { - message->reply_serial = reply_serial; - return TRUE; - } - else - return FALSE; -} - -/** - * Returns the serial of a message or 0 if none has been specified. - * The message's serial number is provided by the application sending - * the message and is used to identify replies to this message. All - * messages received on a connection will have a serial, but messages - * you haven't sent yet may return 0. - * - * @param message the message - * @returns the client serial - */ -dbus_uint32_t -dbus_message_get_serial (DBusMessage *message) -{ - return message->client_serial; -} - -/** - * Returns the serial that the message is a reply to or 0 if none. - * - * @param message the message - * @returns the reply serial - */ -dbus_uint32_t -dbus_message_get_reply_serial (DBusMessage *message) -{ - return message->reply_serial; -} - -/** - * Adds a counter to be incremented immediately with the - * size of this message, and decremented by the size - * of this message when this message if finalized. - * The link contains a counter with its refcount already - * incremented, but the counter itself not incremented. - * Ownership of link and counter refcount is passed to - * the message. - * - * @param message the message - * @param link link with counter as data - */ -void -_dbus_message_add_size_counter_link (DBusMessage *message, - DBusList *link) -{ - /* right now we don't recompute the delta when message - * size changes, and that's OK for current purposes - * I think, but could be important to change later. - * Do recompute it whenever there are no outstanding counters, - * since it's basically free. - */ - if (message->size_counters == NULL) - { - message->size_counter_delta = - _dbus_string_get_length (&message->header) + - _dbus_string_get_length (&message->body); - -#if 0 - _dbus_verbose ("message has size %ld\n", - message->size_counter_delta); -#endif - } - - _dbus_list_append_link (&message->size_counters, link); - - _dbus_counter_adjust (link->data, message->size_counter_delta); -} - -/** - * Adds a counter to be incremented immediately with the - * size of this message, and decremented by the size - * of this message when this message if finalized. - * - * @param message the message - * @param counter the counter - * @returns #FALSE if no memory - */ -dbus_bool_t -_dbus_message_add_size_counter (DBusMessage *message, - DBusCounter *counter) -{ - DBusList *link; - - link = _dbus_list_alloc_link (counter); - if (link == NULL) - return FALSE; - - _dbus_counter_ref (counter); - _dbus_message_add_size_counter_link (message, link); - - return TRUE; -} - -/** - * Removes a counter tracking the size of this message, and decrements - * the counter by the size of this message. - * - * @param message the message - * @param link_return return the link used - * @param counter the counter - */ -void -_dbus_message_remove_size_counter (DBusMessage *message, - DBusCounter *counter, - DBusList **link_return) -{ - DBusList *link; - - link = _dbus_list_find_last (&message->size_counters, - counter); - _dbus_assert (link != NULL); - - _dbus_list_unlink (&message->size_counters, - link); - if (link_return) - *link_return = link; - else - _dbus_list_free_link (link); - - _dbus_counter_adjust (counter, - message->size_counter_delta); - - _dbus_counter_unref (counter); -} - -static dbus_bool_t -dbus_message_create_header (DBusMessage *message, - int type, - const char *service, - const char *path, - const char *interface, - const char *member, - const char *error_name) -{ - unsigned int flags; - - _dbus_assert ((interface && member) || - (error_name) || - !(interface || member || error_name)); - _dbus_assert (error_name == NULL || is_valid_error_name (error_name)); - - if (!_dbus_string_append_byte (&message->header, message->byte_order)) - return FALSE; - - if (!_dbus_string_append_byte (&message->header, type)) - return FALSE; - - flags = 0; - if (!_dbus_string_append_byte (&message->header, flags)) - return FALSE; - - if (!_dbus_string_append_byte (&message->header, DBUS_MAJOR_PROTOCOL_VERSION)) - return FALSE; - - /* header length */ - if (!_dbus_marshal_uint32 (&message->header, message->byte_order, 0)) - return FALSE; - - /* body length */ - if (!_dbus_marshal_uint32 (&message->header, message->byte_order, 0)) - return FALSE; - - /* serial */ - if (!_dbus_marshal_uint32 (&message->header, message->byte_order, 0)) - return FALSE; - - /* Marshal all the fields (Marshall Fields?) */ - - if (path != NULL) - { - if (!append_string_field (message, - DBUS_HEADER_FIELD_PATH, - DBUS_TYPE_OBJECT_PATH, - path)) - return FALSE; - } - - if (service != NULL) - { - if (!append_string_field (message, - DBUS_HEADER_FIELD_DESTINATION, - DBUS_TYPE_STRING, - service)) - return FALSE; - } - - if (interface != NULL) - { - if (!append_string_field (message, - DBUS_HEADER_FIELD_INTERFACE, - DBUS_TYPE_STRING, - interface)) - return FALSE; - } - - if (member != NULL) - { - if (!append_string_field (message, - DBUS_HEADER_FIELD_MEMBER, - DBUS_TYPE_STRING, - member)) - return FALSE; - } - - if (error_name != NULL) - { - if (!append_string_field (message, - DBUS_HEADER_FIELD_ERROR_NAME, - DBUS_TYPE_STRING, - error_name)) - return FALSE; - } - - /* @todo if we make signature optional when body is empty, we don't - * need to do this here. - */ - if (!append_string_field (message, - DBUS_HEADER_FIELD_SIGNATURE, - DBUS_TYPE_STRING, - "")) - return FALSE; - - return TRUE; -} - -/** - * Locks a message. Allows checking that applications don't keep a - * reference to a message in the outgoing queue and change it - * underneath us. Messages are locked when they enter the outgoing - * queue (dbus_connection_send_message()), and the library complains - * if the message is modified while locked. - * - * @param message the message to lock. - */ -void -_dbus_message_lock (DBusMessage *message) -{ - if (!message->locked) - { - /* Fill in our lengths */ - _dbus_marshal_set_uint32 (&message->header, - message->byte_order, - HEADER_LENGTH_OFFSET, - _dbus_string_get_length (&message->header)); - - _dbus_marshal_set_uint32 (&message->header, - message->byte_order, - BODY_LENGTH_OFFSET, - _dbus_string_get_length (&message->body)); - - message->locked = TRUE; - } -} - -/** @} */ - -/** - * @defgroup DBusMessage DBusMessage - * @ingroup DBus - * @brief Message to be sent or received over a DBusConnection. - * - * A DBusMessage is the most basic unit of communication over a - * DBusConnection. A DBusConnection represents a stream of messages - * received from a remote application, and a stream of messages - * sent to a remote application. - * - * @{ - */ - -/** - * Sets the signature of the message, i.e. the arguments in the - * message payload. The signature includes only "in" arguments for - * #DBUS_MESSAGE_TYPE_METHOD_CALL and only "out" arguments for - * #DBUS_MESSAGE_TYPE_METHOD_RETURN, so is slightly different from - * what you might expect (it does not include the signature of the - * entire C++-style method). - * - * The signature is a string made up of type codes such - * as #DBUS_TYPE_STRING. The string is terminated with nul - * (nul is also the value of #DBUS_TYPE_INVALID). - * - * @param message the message - * @param signature the type signature or #NULL to unset - * @returns #FALSE if no memory - */ -static dbus_bool_t -dbus_message_set_signature (DBusMessage *message, - const char *signature) -{ - _dbus_return_val_if_fail (message != NULL, FALSE); - _dbus_return_val_if_fail (!message->locked, FALSE); - - return set_string_field (message, - DBUS_HEADER_FIELD_SIGNATURE, - DBUS_TYPE_STRING, - signature); -} - -/** - * Appends to the signature of the message. - * (currently a static function, maybe should be public?) - * - * @param message the message - * @param append_bytes nul-terminated bytes to append to the type signature - * @returns #FALSE if no memory - */ -static dbus_bool_t -dbus_message_append_to_signature (DBusMessage *message, - const char *append_bytes) -{ - const char *signature; - DBusString append_str; - dbus_bool_t retval; - - _dbus_return_val_if_fail (append_bytes != NULL, FALSE); - _dbus_return_val_if_fail (message != NULL, FALSE); - _dbus_return_val_if_fail (!message->locked, FALSE); - - retval = FALSE; - - /* FIXME Just really inefficient for the moment; later we could - * speed it up a lot by poking more directly at the header data - */ - signature = dbus_message_get_signature (message); - - if (!_dbus_string_init (&append_str)) - return FALSE; - - if (signature && !_dbus_string_append (&append_str, signature)) - goto out; - - if (!_dbus_string_append (&append_str, append_bytes)) - goto out; - - if (!set_string_field (message, - DBUS_HEADER_FIELD_SIGNATURE, - DBUS_TYPE_STRING, - _dbus_string_get_const_data (&append_str))) - goto out; - - retval = TRUE; - - out: - - _dbus_string_free (&append_str); - - return retval; -} - -/** - * Appends one byte to the signature of the message. - * Internal function. - * - * @param message the message - * @param append_byte the byte - * @returns #FALSE if no memory - */ -static dbus_bool_t -_dbus_message_append_byte_to_signature (DBusMessage *message, - unsigned char append_byte) -{ - char buf[2]; - - _dbus_return_val_if_fail (message != NULL, FALSE); - _dbus_return_val_if_fail (!message->locked, FALSE); - - buf[0] = append_byte; - buf[1] = '\0'; - - return dbus_message_append_to_signature (message, buf); -} - -/** - * Removes the last byte from the signature of the message. - * Internal function. - * - * @param message the message - */ -static void -_dbus_message_remove_byte_from_signature (DBusMessage *message) -{ - const char *signature; - - _dbus_return_if_fail (message != NULL); - _dbus_return_if_fail (!message->locked); - - signature = dbus_message_get_signature (message); - - _dbus_return_if_fail (signature != NULL); - - if (!delete_field (message, - DBUS_HEADER_FIELD_SIGNATURE, - 0)) - _dbus_assert_not_reached ("failed to delete signature field"); - - /* reappend one shorter (could this be any less efficient? the code will - * go away later anyhow) - */ - if (!append_string_field_len (message, DBUS_HEADER_FIELD_SIGNATURE, - DBUS_TYPE_STRING, signature, - strlen (signature) - 1)) - _dbus_assert_not_reached ("reappending shorter signature shouldn't have failed"); -} - -/** - * @typedef DBusMessage - * - * Opaque data type representing a message received from or to be - * sent to another application. - */ - -static void -free_size_counter (void *element, - void *data) -{ - DBusCounter *counter = element; - DBusMessage *message = data; - - _dbus_counter_adjust (counter, - message->size_counter_delta); - - _dbus_counter_unref (counter); -} - -static void -dbus_message_finalize (DBusMessage *message) -{ - _dbus_assert (message->refcount.value == 0); - - /* This calls application callbacks! */ - _dbus_data_slot_list_free (&message->slot_list); - - _dbus_list_foreach (&message->size_counters, - free_size_counter, message); - _dbus_list_clear (&message->size_counters); - - _dbus_string_free (&message->header); - _dbus_string_free (&message->body); - - dbus_free (message); -} - -/* Message Cache - * - * We cache some DBusMessage to reduce the overhead of allocating - * them. In my profiling this consistently made about an 8% - * difference. It avoids the malloc for the message, the malloc for - * the slot list, the malloc for the header string and body string, - * and the associated free() calls. It does introduce another global - * lock which could be a performance issue in certain cases. - * - * For the echo client/server the round trip time goes from around - * .000077 to .000069 with the message cache on my laptop. The sysprof - * change is as follows (numbers are cumulative percentage): - * - * with message cache implemented as array as it is now (0.000069 per): - * new_empty_header 1.46 - * mutex_lock 0.56 # i.e. _DBUS_LOCK(message_cache) - * mutex_unlock 0.25 - * self 0.41 - * unref 2.24 - * self 0.68 - * list_clear 0.43 - * mutex_lock 0.33 # i.e. _DBUS_LOCK(message_cache) - * mutex_unlock 0.25 - * - * with message cache implemented as list (0.000070 per roundtrip): - * new_empty_header 2.72 - * list_pop_first 1.88 - * unref 3.3 - * list_prepend 1.63 - * - * without cache (0.000077 per roundtrip): - * new_empty_header 6.7 - * string_init_preallocated 3.43 - * dbus_malloc 2.43 - * dbus_malloc0 2.59 - * - * unref 4.02 - * string_free 1.82 - * dbus_free 1.63 - * dbus_free 0.71 - * - * If you implement the message_cache with a list, the primary reason - * it's slower is that you add another thread lock (on the DBusList - * mempool). - */ - -/* Avoid caching huge messages */ -#define MAX_MESSAGE_SIZE_TO_CACHE _DBUS_ONE_MEGABYTE -/* Avoid caching too many messages */ -#define MAX_MESSAGE_CACHE_SIZE 5 - -_DBUS_DEFINE_GLOBAL_LOCK (message_cache); -static DBusMessage *message_cache[MAX_MESSAGE_CACHE_SIZE]; -static int message_cache_count = 0; -static dbus_bool_t message_cache_shutdown_registered = FALSE; - -static void -dbus_message_cache_shutdown (void *data) -{ - int i; - - _DBUS_LOCK (message_cache); - - i = 0; - while (i < MAX_MESSAGE_CACHE_SIZE) - { - if (message_cache[i]) - dbus_message_finalize (message_cache[i]); - - ++i; - } - - message_cache_count = 0; - message_cache_shutdown_registered = FALSE; - - _DBUS_UNLOCK (message_cache); -} - -/** - * Tries to get a message from the message cache. The retrieved - * message will have junk in it, so it still needs to be cleared out - * in dbus_message_new_empty_header() - * - * @returns the message, or #NULL if none cached - */ -static DBusMessage* -dbus_message_get_cached (void) -{ - DBusMessage *message; - int i; - - message = NULL; - - _DBUS_LOCK (message_cache); - - _dbus_assert (message_cache_count >= 0); - - if (message_cache_count == 0) - { - _DBUS_UNLOCK (message_cache); - return NULL; - } - - /* This is not necessarily true unless count > 0, and - * message_cache is uninitialized until the shutdown is - * registered - */ - _dbus_assert (message_cache_shutdown_registered); - - i = 0; - while (i < MAX_MESSAGE_CACHE_SIZE) - { - if (message_cache[i]) - { - message = message_cache[i]; - message_cache[i] = NULL; - message_cache_count -= 1; - break; - } - ++i; - } - _dbus_assert (message_cache_count >= 0); - _dbus_assert (i < MAX_MESSAGE_CACHE_SIZE); - _dbus_assert (message != NULL); - - _DBUS_UNLOCK (message_cache); - - _dbus_assert (message->refcount.value == 0); - _dbus_assert (message->size_counters == NULL); - - return message; -} - -/** - * Tries to cache a message, otherwise finalize it. - * - * @param message the message - */ -static void -dbus_message_cache_or_finalize (DBusMessage *message) -{ - dbus_bool_t was_cached; - int i; - - _dbus_assert (message->refcount.value == 0); - - /* This calls application code and has to be done first thing - * without holding the lock - */ - _dbus_data_slot_list_clear (&message->slot_list); - - _dbus_list_foreach (&message->size_counters, - free_size_counter, message); - _dbus_list_clear (&message->size_counters); - - was_cached = FALSE; - - _DBUS_LOCK (message_cache); - - if (!message_cache_shutdown_registered) - { - _dbus_assert (message_cache_count == 0); - - if (!_dbus_register_shutdown_func (dbus_message_cache_shutdown, NULL)) - goto out; - - i = 0; - while (i < MAX_MESSAGE_CACHE_SIZE) - { - message_cache[i] = NULL; - ++i; - } - - message_cache_shutdown_registered = TRUE; - } - - _dbus_assert (message_cache_count >= 0); - - if ((_dbus_string_get_length (&message->header) + - _dbus_string_get_length (&message->body)) > - MAX_MESSAGE_SIZE_TO_CACHE) - goto out; - - if (message_cache_count >= MAX_MESSAGE_CACHE_SIZE) - goto out; - - /* Find empty slot */ - i = 0; - while (message_cache[i] != NULL) - ++i; - - _dbus_assert (i < MAX_MESSAGE_CACHE_SIZE); - - _dbus_assert (message_cache[i] == NULL); - message_cache[i] = message; - message_cache_count += 1; - was_cached = TRUE; - - out: - _DBUS_UNLOCK (message_cache); - - if (!was_cached) - dbus_message_finalize (message); -} - -static DBusMessage* -dbus_message_new_empty_header (void) -{ - DBusMessage *message; - int i; - dbus_bool_t from_cache; - - message = dbus_message_get_cached (); - - if (message != NULL) - { - from_cache = TRUE; - } - else - { - from_cache = FALSE; - message = dbus_new (DBusMessage, 1); - if (message == NULL) - return NULL; -#ifndef DBUS_DISABLE_CHECKS - message->generation = _dbus_current_generation; -#endif - } - - message->refcount.value = 1; - message->byte_order = DBUS_COMPILER_BYTE_ORDER; - message->client_serial = 0; - message->reply_serial = 0; - message->locked = FALSE; - message->header_padding = 0; - message->size_counters = NULL; - message->size_counter_delta = 0; - message->changed_stamp = 0; - - if (!from_cache) - _dbus_data_slot_list_init (&message->slot_list); - - i = 0; - while (i <= DBUS_HEADER_FIELD_LAST) - { - message->header_fields[i].name_offset = -1; - message->header_fields[i].value_offset = -1; - ++i; - } - - if (from_cache) - { - /* These can't fail since they shorten the string */ - _dbus_string_set_length (&message->header, 0); - _dbus_string_set_length (&message->body, 0); - } - else - { - if (!_dbus_string_init_preallocated (&message->header, 32)) - { - dbus_free (message); - return NULL; - } - - if (!_dbus_string_init_preallocated (&message->body, 32)) - { - _dbus_string_free (&message->header); - dbus_free (message); - return NULL; - } - } - - 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. - * - * @param message_type type of message - * @returns new message or #NULL If no memory - */ -DBusMessage* -dbus_message_new (int message_type) -{ - DBusMessage *message; - - _dbus_return_val_if_fail (message_type != DBUS_MESSAGE_TYPE_INVALID, NULL); - - message = dbus_message_new_empty_header (); - if (message == NULL) - return NULL; - - if (!dbus_message_create_header (message, - message_type, - NULL, NULL, NULL, NULL, NULL)) - { - dbus_message_unref (message); - return NULL; - } - - return message; -} - -/** - * Constructs a new message to invoke a method on a remote - * object. Returns #NULL if memory can't be allocated for the - * message. The service may be #NULL in which case no service is set; - * this is appropriate when using D-BUS in a peer-to-peer context (no - * message bus). The interface may be #NULL, which means that - * if multiple methods with the given name exist it is undefined - * which one will be invoked. - * - * @param service service that the message should be sent to or #NULL - * @param path object path the message should be sent to - * @param interface interface to invoke method on - * @param method method to invoke - * - * @returns a new DBusMessage, free with dbus_message_unref() - * @see dbus_message_unref() - */ -DBusMessage* -dbus_message_new_method_call (const char *service, - const char *path, - const char *interface, - const char *method) -{ - DBusMessage *message; - - _dbus_return_val_if_fail (path != NULL, NULL); - _dbus_return_val_if_fail (method != NULL, NULL); - - message = dbus_message_new_empty_header (); - if (message == NULL) - return NULL; - - if (!dbus_message_create_header (message, - DBUS_MESSAGE_TYPE_METHOD_CALL, - service, path, interface, method, NULL)) - { - dbus_message_unref (message); - return NULL; - } - - return message; -} - -/** - * Constructs a message that is a reply to a method call. Returns - * #NULL if memory can't be allocated for the message. - * - * @param method_call the message which the created - * message is a reply to. - * @returns a new DBusMessage, free with dbus_message_unref() - * @see dbus_message_new_method_call(), dbus_message_unref() - */ -DBusMessage* -dbus_message_new_method_return (DBusMessage *method_call) -{ - DBusMessage *message; - const char *sender; - - _dbus_return_val_if_fail (method_call != NULL, NULL); - - sender = get_string_field (method_call, - DBUS_HEADER_FIELD_SENDER, - NULL); - - /* sender is allowed to be null here in peer-to-peer case */ - - message = dbus_message_new_empty_header (); - if (message == NULL) - return NULL; - - if (!dbus_message_create_header (message, - 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_message_get_serial (method_call))) - { - dbus_message_unref (message); - return NULL; - } - - return message; -} - -/** - * Constructs a new message representing a signal emission. Returns - * #NULL if memory can't be allocated for the message. - * A signal is identified by its originating interface, and - * the name of the signal. - * - * @param path the path to the object emitting the signal - * @param interface the interface the signal is emitted from - * @param name name of the signal - * @returns a new DBusMessage, free with dbus_message_unref() - * @see dbus_message_unref() - */ -DBusMessage* -dbus_message_new_signal (const char *path, - const char *interface, - const char *name) -{ - DBusMessage *message; - - _dbus_return_val_if_fail (path != NULL, NULL); - _dbus_return_val_if_fail (interface != NULL, NULL); - _dbus_return_val_if_fail (name != NULL, NULL); - - message = dbus_message_new_empty_header (); - if (message == NULL) - return NULL; - - if (!dbus_message_create_header (message, - DBUS_MESSAGE_TYPE_SIGNAL, - NULL, path, interface, name, NULL)) - { - dbus_message_unref (message); - return NULL; - } - - dbus_message_set_no_reply (message, TRUE); - - return message; -} - -/** - * Creates a new message that is an error reply to a certain message. - * Error replies are possible in response to method calls primarily. - * - * @param reply_to the original message - * @param error_name the error name - * @param error_message the error message string or #NULL for none - * @returns a new error message - */ -DBusMessage* -dbus_message_new_error (DBusMessage *reply_to, - const char *error_name, - const char *error_message) -{ - DBusMessage *message; - const char *sender; - DBusMessageIter iter; - - _dbus_return_val_if_fail (reply_to != NULL, NULL); - _dbus_return_val_if_fail (error_name != NULL, NULL); - _dbus_return_val_if_fail (is_valid_error_name (error_name), NULL); - - sender = get_string_field (reply_to, - DBUS_HEADER_FIELD_SENDER, - NULL); - - /* sender may be NULL for non-message-bus case or - * when the message bus is dealing with an unregistered - * connection. - */ - message = dbus_message_new_empty_header (); - if (message == NULL) - return NULL; - - if (!dbus_message_create_header (message, - 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, - dbus_message_get_serial (reply_to))) - { - dbus_message_unref (message); - return NULL; - } - - if (error_message != NULL) - { - dbus_message_append_iter_init (message, &iter); - if (!dbus_message_iter_append_string (&iter, error_message)) - { - dbus_message_unref (message); - return NULL; - } - } - - return message; -} - -/** - * Creates a new message that is an error reply to a certain message. - * Error replies are possible in response to method calls primarily. - * - * @param reply_to the original message - * @param error_name the error name - * @param error_format the error message string to be printed - * @param ... value of first argument, list of additional values to print - * @returns a new error message - */ -DBusMessage* -dbus_message_new_error_printf (DBusMessage *reply_to, - const char *error_name, - const char *error_format, - ...) -{ - va_list args; - DBusString str; - DBusMessage *message; - - _dbus_return_val_if_fail (reply_to != NULL, NULL); - _dbus_return_val_if_fail (error_name != NULL, NULL); - _dbus_return_val_if_fail (is_valid_error_name (error_name), NULL); - - if (!_dbus_string_init (&str)) - return NULL; - - va_start (args, error_format); - - if (_dbus_string_append_printf_valist (&str, error_format, args)) - message = dbus_message_new_error (reply_to, error_name, - _dbus_string_get_const_data (&str)); - else - message = NULL; - - _dbus_string_free (&str); - - va_end (args); - - return message; -} - - -/** - * Creates a new message that is an exact replica of the message - * specified, except that its refcount is set to 1. - * - * @param message the message. - * @returns the new message. - */ -DBusMessage * -dbus_message_copy (const DBusMessage *message) -{ - DBusMessage *retval; - int i; - - _dbus_return_val_if_fail (message != NULL, NULL); - - retval = dbus_new0 (DBusMessage, 1); - if (retval == NULL) - return NULL; - - retval->refcount.value = 1; - retval->byte_order = message->byte_order; - retval->client_serial = message->client_serial; - retval->reply_serial = message->reply_serial; - retval->header_padding = message->header_padding; - retval->locked = FALSE; -#ifndef DBUS_DISABLE_CHECKS - retval->generation = message->generation; -#endif - - if (!_dbus_string_init_preallocated (&retval->header, - _dbus_string_get_length (&message->header))) - { - dbus_free (retval); - return NULL; - } - - if (!_dbus_string_init_preallocated (&retval->body, - _dbus_string_get_length (&message->body))) - { - _dbus_string_free (&retval->header); - dbus_free (retval); - return NULL; - } - - if (!_dbus_string_copy (&message->header, 0, - &retval->header, 0)) - goto failed_copy; - - if (!_dbus_string_copy (&message->body, 0, - &retval->body, 0)) - goto failed_copy; - - for (i = 0; i <= DBUS_HEADER_FIELD_LAST; i++) - { - retval->header_fields[i] = message->header_fields[i]; - } - - return retval; - - failed_copy: - _dbus_string_free (&retval->header); - _dbus_string_free (&retval->body); - dbus_free (retval); - - return NULL; -} - - -/** - * Increments the reference count of a DBusMessage. - * - * @param message The message - * @returns the message - * @see dbus_message_unref - */ -DBusMessage * -dbus_message_ref (DBusMessage *message) -{ - dbus_int32_t old_refcount; - - _dbus_return_val_if_fail (message != NULL, NULL); - _dbus_return_val_if_fail (message->generation == _dbus_current_generation, NULL); - - old_refcount = _dbus_atomic_inc (&message->refcount); - _dbus_assert (old_refcount >= 1); - - return message; -} - -/** - * Decrements the reference count of a DBusMessage. - * - * @param message The message - * @see dbus_message_ref - */ -void -dbus_message_unref (DBusMessage *message) -{ - dbus_int32_t old_refcount; - - _dbus_return_if_fail (message != NULL); - _dbus_return_if_fail (message->generation == _dbus_current_generation); - - old_refcount = _dbus_atomic_dec (&message->refcount); - - _dbus_assert (old_refcount >= 0); - - if (old_refcount == 1) - { - /* Calls application callbacks! */ - dbus_message_cache_or_finalize (message); - } -} - -/** - * Gets the type of a message. Types include - * DBUS_MESSAGE_TYPE_METHOD_CALL, DBUS_MESSAGE_TYPE_METHOD_RETURN, - * DBUS_MESSAGE_TYPE_ERROR, DBUS_MESSAGE_TYPE_SIGNAL, but other types - * are allowed and all code must silently ignore messages of unknown - * type. DBUS_MESSAGE_TYPE_INVALID will never be returned, however. - * - * - * @param message the message - * @returns the type of the message - */ -int -dbus_message_get_type (DBusMessage *message) -{ - int type; - - type = _dbus_string_get_byte (&message->header, 1); - _dbus_assert (type != DBUS_MESSAGE_TYPE_INVALID); - - return type; -} - -/** - * Sets the object path this message is being sent to (for - * DBUS_MESSAGE_TYPE_METHOD_CALL) or the one a signal is being - * emitted from (for DBUS_MESSAGE_TYPE_SIGNAL). - * - * @param message the message - * @param object_path the path or #NULL to unset - * @returns #FALSE if not enough memory - */ -dbus_bool_t -dbus_message_set_path (DBusMessage *message, - const char *object_path) -{ - _dbus_return_val_if_fail (message != NULL, FALSE); - _dbus_return_val_if_fail (!message->locked, FALSE); - - return set_string_field (message, - DBUS_HEADER_FIELD_PATH, - DBUS_TYPE_OBJECT_PATH, - object_path); -} - -/** - * Gets the object path this message is being sent to - * (for DBUS_MESSAGE_TYPE_METHOD_CALL) or being emitted - * from (for DBUS_MESSAGE_TYPE_SIGNAL). - * - * @param message the message - * @returns the path (should not be freed) - */ -const char* -dbus_message_get_path (DBusMessage *message) -{ - _dbus_return_val_if_fail (message != NULL, NULL); - - return get_string_field (message, DBUS_HEADER_FIELD_PATH, NULL); -} - -/** - * Gets the object path this message is being sent to - * (for DBUS_MESSAGE_TYPE_METHOD_CALL) or being emitted - * from (for DBUS_MESSAGE_TYPE_SIGNAL) in a decomposed - * format (one array element per path component). - * Free the returned array with dbus_free_string_array(). - * - * An empty but non-NULL path array means the path "/". - * So the path "/foo/bar" becomes { "foo", "bar", NULL } - * and the path "/" becomes { NULL }. - * - * @param message the message - * @param path place to store allocated array of path components; #NULL set here if no path field exists - * @returns #FALSE if no memory to allocate the array - */ -dbus_bool_t -dbus_message_get_path_decomposed (DBusMessage *message, - char ***path) -{ - _dbus_return_val_if_fail (message != NULL, FALSE); - _dbus_return_val_if_fail (path != NULL, FALSE); - - return get_path_field_decomposed (message, - DBUS_HEADER_FIELD_PATH, - path); -} - -/** - * Sets the interface this message is being sent to - * (for DBUS_MESSAGE_TYPE_METHOD_CALL) or - * the interface a signal is being emitted from - * (for DBUS_MESSAGE_TYPE_SIGNAL). - * - * @param message the message - * @param interface the interface or #NULL to unset - * @returns #FALSE if not enough memory - */ -dbus_bool_t -dbus_message_set_interface (DBusMessage *message, - const char *interface) -{ - _dbus_return_val_if_fail (message != NULL, FALSE); - _dbus_return_val_if_fail (!message->locked, FALSE); - - return set_string_field (message, - DBUS_HEADER_FIELD_INTERFACE, - DBUS_TYPE_STRING, - interface); -} - -/** - * Gets the interface this message is being sent to - * (for DBUS_MESSAGE_TYPE_METHOD_CALL) or being emitted - * from (for DBUS_MESSAGE_TYPE_SIGNAL). - * The interface name is fully-qualified (namespaced). - * - * @param message the message - * @returns the message interface (should not be freed) - */ -const char* -dbus_message_get_interface (DBusMessage *message) -{ - _dbus_return_val_if_fail (message != NULL, NULL); - - return get_string_field (message, DBUS_HEADER_FIELD_INTERFACE, NULL); -} - -/** - * Sets the interface member being invoked - * (DBUS_MESSAGE_TYPE_METHOD_CALL) or emitted - * (DBUS_MESSAGE_TYPE_SIGNAL). - * The interface name is fully-qualified (namespaced). - * - * @param message the message - * @param member the member or #NULL to unset - * @returns #FALSE if not enough memory - */ -dbus_bool_t -dbus_message_set_member (DBusMessage *message, - const char *member) -{ - _dbus_return_val_if_fail (message != NULL, FALSE); - _dbus_return_val_if_fail (!message->locked, FALSE); - - return set_string_field (message, - DBUS_HEADER_FIELD_MEMBER, - DBUS_TYPE_STRING, - member); -} - -/** - * Gets the interface member being invoked - * (DBUS_MESSAGE_TYPE_METHOD_CALL) or emitted - * (DBUS_MESSAGE_TYPE_SIGNAL). - * - * @param message the message - * @returns the member name (should not be freed) - */ -const char* -dbus_message_get_member (DBusMessage *message) -{ - _dbus_return_val_if_fail (message != NULL, NULL); - - return get_string_field (message, - DBUS_HEADER_FIELD_MEMBER, - NULL); -} - -/** - * Sets the name of the error (DBUS_MESSAGE_TYPE_ERROR). - * The name is fully-qualified (namespaced). - * - * @param message the message - * @param error_name the name or #NULL to unset - * @returns #FALSE if not enough memory - */ -dbus_bool_t -dbus_message_set_error_name (DBusMessage *message, - const char *error_name) -{ - _dbus_return_val_if_fail (message != NULL, FALSE); - _dbus_return_val_if_fail (!message->locked, FALSE); - _dbus_return_val_if_fail (error_name == NULL || is_valid_error_name (error_name), FALSE); - - return set_string_field (message, - DBUS_HEADER_FIELD_ERROR_NAME, - DBUS_TYPE_STRING, - error_name); -} - -/** - * Gets the error name (DBUS_MESSAGE_TYPE_ERROR only). - * - * @param message the message - * @returns the error name (should not be freed) - */ -const char* -dbus_message_get_error_name (DBusMessage *message) -{ - _dbus_return_val_if_fail (message != NULL, NULL); - - return get_string_field (message, - DBUS_HEADER_FIELD_ERROR_NAME, - NULL); -} - -/** - * Sets the message's destination service. - * - * @param message the message - * @param destination the destination service name or #NULL to unset - * @returns #FALSE if not enough memory - */ -dbus_bool_t -dbus_message_set_destination (DBusMessage *message, - const char *destination) -{ - _dbus_return_val_if_fail (message != NULL, FALSE); - _dbus_return_val_if_fail (!message->locked, FALSE); - - return set_string_field (message, - DBUS_HEADER_FIELD_DESTINATION, - DBUS_TYPE_STRING, - destination); -} - -/** - * Gets the destination service of a message. - * - * @param message the message - * @returns the message destination service (should not be freed) - */ -const char* -dbus_message_get_destination (DBusMessage *message) -{ - _dbus_return_val_if_fail (message != NULL, NULL); - - return get_string_field (message, - DBUS_HEADER_FIELD_DESTINATION, - NULL); -} - -/** - * Appends fields to a message given a variable argument list. The - * variable argument list should contain the type of the argument - * followed by the value to add. Array values are specified by an int - * typecode followed by a pointer to the array followed by an int - * giving the length of the array. The argument list must be - * terminated with #DBUS_TYPE_INVALID. - * - * This function doesn't support dicts or non-fundamental arrays. - * - * This function supports #DBUS_TYPE_INT64 and #DBUS_TYPE_UINT64 - * only if #DBUS_HAVE_INT64 is defined. - * - * @param message the message - * @param first_arg_type type of the first argument - * @param ... value of first argument, list of additional type-value pairs - * @returns #TRUE on success - */ -dbus_bool_t -dbus_message_append_args (DBusMessage *message, - int first_arg_type, - ...) -{ - dbus_bool_t retval; - va_list var_args; - - _dbus_return_val_if_fail (message != NULL, FALSE); - - va_start (var_args, first_arg_type); - retval = dbus_message_append_args_valist (message, - first_arg_type, - var_args); - va_end (var_args); - - return retval; -} - -/* Swap into our byte order if message isn't already. - * Done only when required, which allows the bus to avoid - * ever doing this as it routes messages. - */ -static void -_dbus_message_ensure_our_byte_order (DBusMessage *message) -{ - if (message->byte_order == DBUS_COMPILER_BYTE_ORDER) - return; - - - - -} - -/** - * Gets arguments from a message given a variable argument list. - * The variable argument list should contain the type of the - * argumen followed by a pointer to where the value should be - * stored. The list is terminated with #DBUS_TYPE_INVALID. - * - * @param message the message - * @param error error to be filled in on failure - * @param first_arg_type the first argument type - * @param ... location for first argument value, then list of type-location pairs - * @returns #FALSE if the error was set - */ -dbus_bool_t -dbus_message_get_args (DBusMessage *message, - DBusError *error, - int first_arg_type, - ...) -{ - dbus_bool_t retval; - va_list var_args; - - _dbus_return_val_if_fail (message != NULL, FALSE); - _dbus_return_val_if_error_is_set (error, FALSE); - - va_start (var_args, first_arg_type); - retval = dbus_message_get_args_valist (message, error, first_arg_type, var_args); - va_end (var_args); - - return retval; -} - -/** - * This function takes a va_list for use by language bindings - * - * @todo We need to free the argument data when an error occurs. - * - * @see dbus_message_get_args - * @param message the message - * @param error error to be filled in - * @param first_arg_type type of the first argument - * @param var_args return location for first argument, followed by list of type/location pairs - * @returns #FALSE if error was set - */ -dbus_bool_t -dbus_message_get_args_valist (DBusMessage *message, - DBusError *error, - int first_arg_type, - va_list var_args) -{ - DBusMessageIter iter; - - _dbus_return_val_if_fail (message != NULL, FALSE); - _dbus_return_val_if_error_is_set (error, FALSE); - - dbus_message_iter_init (message, &iter); - return dbus_message_iter_get_args_valist (&iter, error, first_arg_type, var_args); -} - -/** - * Gets arguments from a message iterator given a variable argument list. - * The variable argument list should contain the type of the - * argumen followed by a pointer to where the value should be - * stored. The list is terminated with 0. - * - * @param iter the message iterator - * @param error error to be filled in on failure - * @param first_arg_type the first argument type - * @param ... location for first argument value, then list of type-location pairs - * @returns #FALSE if the error was set - */ -dbus_bool_t -dbus_message_iter_get_args (DBusMessageIter *iter, - DBusError *error, - int first_arg_type, - ...) -{ - dbus_bool_t retval; - va_list var_args; - - _dbus_return_val_if_fail (iter != NULL, FALSE); - _dbus_return_val_if_error_is_set (error, FALSE); - - va_start (var_args, first_arg_type); - retval = dbus_message_iter_get_args_valist (iter, error, first_arg_type, var_args); - va_end (var_args); - - return retval; -} - -/** - * Initializes a DBusMessageIter representing the arguments of the - * message passed in. - * - * @param message the message - * @param iter pointer to an iterator to initialize - * @returns #FALSE if the message has no arguments - */ -dbus_bool_t -dbus_message_iter_init (DBusMessage *message, - DBusMessageIter *iter) -{ - DBusMessageRealIter *real = (DBusMessageRealIter *)iter; - - _dbus_return_val_if_fail (message != NULL, FALSE); - _dbus_return_val_if_fail (iter != NULL, FALSE); - - _dbus_assert (sizeof (DBusMessageRealIter) <= sizeof (DBusMessageIter)); - - real->message = message; - real->parent_iter = NULL; - real->changed_stamp = message->changed_stamp; - - real->type = DBUS_MESSAGE_ITER_TYPE_MESSAGE; - real->pos = 0; - real->end = _dbus_string_get_length (&message->body); - - real->container_start = 0; - real->container_length_pos = 0; - real->wrote_dict_key = 0; - real->array_type_pos = 0; - - return real->end > real->pos; -} - -#ifndef DBUS_DISABLE_CHECKS -static dbus_bool_t -dbus_message_iter_check (DBusMessageRealIter *iter) -{ - if (iter == NULL) - { - _dbus_warn ("dbus iterator check failed: iterator is NULL\n"); - return FALSE; - } - - if (iter->changed_stamp != iter->message->changed_stamp) - { - _dbus_warn ("dbus iterator check failed: invalid iterator, must re-initialize it after modifying the message\n"); - return FALSE; - } - - if (iter->pos < 0 || iter->pos > iter->end) - { - _dbus_warn ("dbus iterator check failed: invalid position\n"); - return FALSE; - } - - return TRUE; -} -#endif /* DBUS_DISABLE_CHECKS */ - -static int -skip_array_type (DBusMessageRealIter *iter, int pos) -{ - const char *data; - - do - { - data = _dbus_string_get_const_data_len (&iter->message->body, - pos++, 1); - } - while (*data == DBUS_TYPE_ARRAY); - - return pos; -} - -/* FIXME what are these _dbus_type_is_valid() checks for? - * haven't we validated the message? - */ -static int -dbus_message_iter_get_data_start (DBusMessageRealIter *iter, int *type) -{ - const char *data; - int pos, len; - - switch (iter->type) - { - case DBUS_MESSAGE_ITER_TYPE_MESSAGE: - data = _dbus_string_get_const_data_len (&iter->message->body, - iter->pos, 1); - if (_dbus_type_is_valid (*data)) - *type = *data; - else - *type = DBUS_TYPE_INVALID; - - return skip_array_type (iter, iter->pos); - - case DBUS_MESSAGE_ITER_TYPE_ARRAY: - data = _dbus_string_get_const_data_len (&iter->message->body, - iter->array_type_pos, 1); - if (_dbus_type_is_valid (*data)) - *type = *data; - else - *type = DBUS_TYPE_INVALID; - - return iter->pos; - - case DBUS_MESSAGE_ITER_TYPE_DICT: - /* Get the length of the string */ - len = _dbus_demarshal_uint32 (&iter->message->body, - iter->message->byte_order, - iter->pos, &pos); - pos = pos + len + 1; - - data = _dbus_string_get_const_data_len (&iter->message->body, - pos, 1); - if (_dbus_type_is_valid (*data)) - *type = *data; - else - *type = DBUS_TYPE_INVALID; + if (!_dbus_header_init (&message->header, message->byte_order)) + { + dbus_free (message); + return NULL; + } - return skip_array_type (iter, pos); - - default: - _dbus_assert_not_reached ("Invalid iter type"); - break; + if (!_dbus_string_init_preallocated (&message->body, 32)) + { + _dbus_header_free (&message->header); + dbus_free (message); + return NULL; + } } - *type = DBUS_TYPE_INVALID; - return iter->pos; -} - - -/** - * Checks if an iterator has any more fields. - * - * @param iter the message iter - * @returns #TRUE if there are more fields - * following - */ -dbus_bool_t -dbus_message_iter_has_next (DBusMessageIter *iter) -{ - DBusMessageRealIter *real = (DBusMessageRealIter *)iter; - int end_pos; - int type, pos; - - _dbus_return_val_if_fail (dbus_message_iter_check (real), FALSE); - - if (real->pos >= real->end) - return FALSE; - - pos = dbus_message_iter_get_data_start (real, &type); - - if (!_dbus_marshal_get_arg_end_pos (&real->message->body, - real->message->byte_order, - type, pos, &end_pos)) - return FALSE; - - if (end_pos >= real->end) - return FALSE; - - return TRUE; -} - -/** - * Moves the iterator to the next field. - * - * @param iter The message iter - * @returns #TRUE if the iterator was moved to the next field - */ -dbus_bool_t -dbus_message_iter_next (DBusMessageIter *iter) -{ - DBusMessageRealIter *real = (DBusMessageRealIter *)iter; - int end_pos; - int type, pos; - - _dbus_return_val_if_fail (dbus_message_iter_check (real), FALSE); - - pos = dbus_message_iter_get_data_start (real, &type); - - if (!_dbus_marshal_get_arg_end_pos (&real->message->body, - real->message->byte_order, - type, pos, &end_pos)) - return FALSE; - - if (end_pos >= real->end) - return FALSE; - - real->pos = end_pos; - return TRUE; + return message; } /** - * Returns the argument type of the argument that the - * message iterator points at. + * Constructs a new message of the given message type. + * Types include #DBUS_MESSAGE_TYPE_METHOD_CALL, + * #DBUS_MESSAGE_TYPE_SIGNAL, and so forth. * - * @param iter the message iter - * @returns the field type + * @param message_type type of message + * @returns new message or #NULL If no memory */ -int -dbus_message_iter_get_arg_type (DBusMessageIter *iter) +DBusMessage* +dbus_message_new (int message_type) { - DBusMessageRealIter *real = (DBusMessageRealIter *)iter; - int type, pos; - - _dbus_return_val_if_fail (dbus_message_iter_check (real), DBUS_TYPE_INVALID); + DBusMessage *message; - if (real->pos >= real->end) - { - _dbus_verbose (" iterator at or beyond end of message\n"); - return DBUS_TYPE_INVALID; - } + _dbus_return_val_if_fail (message_type != DBUS_MESSAGE_TYPE_INVALID, NULL); - pos = dbus_message_iter_get_data_start (real, &type); - - return type; -} + message = dbus_message_new_empty_header (); + if (message == NULL) + return NULL; -/* FIXME why do we validate the typecode in here, hasn't the message - * already been verified? - */ -static int -iter_get_array_type (DBusMessageRealIter *iter, int *array_type_pos) -{ - const char *data; - int _array_type_pos; - int len, pos; - - switch (iter->type) + if (!_dbus_header_create (&message->header, + message_type, + NULL, NULL, NULL, NULL, NULL)) { - case DBUS_MESSAGE_ITER_TYPE_MESSAGE: - _array_type_pos = iter->pos + 1; - break; - case DBUS_MESSAGE_ITER_TYPE_ARRAY: - _array_type_pos = iter->array_type_pos + 1; - break; - case DBUS_MESSAGE_ITER_TYPE_DICT: - /* Get the length of the string */ - len = _dbus_demarshal_uint32 (&iter->message->body, - iter->message->byte_order, - iter->pos, &pos); - pos = pos + len + 1; - data = _dbus_string_get_const_data_len (&iter->message->body, - pos + 1, 1); - _array_type_pos = pos + 1; - break; - default: - _dbus_assert_not_reached ("wrong iter type"); - return DBUS_TYPE_INVALID; + dbus_message_unref (message); + return NULL; } - if (array_type_pos != NULL) - *array_type_pos = _array_type_pos; - - data = _dbus_string_get_const_data_len (&iter->message->body, - _array_type_pos, 1); - if (_dbus_type_is_valid (*data)) - return *data; - - return DBUS_TYPE_INVALID; + return message; } - /** - * Returns the element type of the array that the - * message iterator points at. Note that you need - * to check that the iterator points to an array - * prior to using this function. + * Constructs a new message to invoke a method on a remote + * object. Returns #NULL if memory can't be allocated for the + * message. The destination may be #NULL in which case no destination + * is set; this is appropriate when using D-BUS in a peer-to-peer + * context (no message bus). The interface may be #NULL, which means + * that if multiple methods with the given name exist it is undefined + * which one will be invoked. + * + * @param destination service that the message should be sent to or #NULL + * @param path object path the message should be sent to + * @param interface interface to invoke method on + * @param method method to invoke * - * @param iter the message iter - * @returns the field type + * @returns a new DBusMessage, free with dbus_message_unref() + * @see dbus_message_unref() */ -int -dbus_message_iter_get_array_type (DBusMessageIter *iter) +DBusMessage* +dbus_message_new_method_call (const char *destination, + const char *path, + const char *interface, + const char *method) { - DBusMessageRealIter *real = (DBusMessageRealIter *)iter; - int type, pos; - - _dbus_return_val_if_fail (dbus_message_iter_check (real), DBUS_TYPE_INVALID); + DBusMessage *message; - if (real->pos >= real->end) - return DBUS_TYPE_INVALID; + _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_service (destination), NULL); + _dbus_return_val_if_fail (_dbus_check_is_valid_path (path), NULL); + _dbus_return_val_if_fail (interface == NULL || + _dbus_check_is_valid_interface (interface), NULL); + _dbus_return_val_if_fail (_dbus_check_is_valid_member (method), NULL); - pos = dbus_message_iter_get_data_start (real, &type); + message = dbus_message_new_empty_header (); + if (message == NULL) + return NULL; - _dbus_assert (type == DBUS_TYPE_ARRAY); + if (!_dbus_header_create (&message->header, + DBUS_MESSAGE_TYPE_METHOD_CALL, + destination, path, interface, method, NULL)) + { + dbus_message_unref (message); + return NULL; + } - return iter_get_array_type (real, NULL); + return message; } - /** - * Returns the string value that an iterator may point to. - * Note that you need to check that the iterator points to - * a string value before using this function. + * Constructs a message that is a reply to a method call. Returns + * #NULL if memory can't be allocated for the message. * - * @see dbus_message_iter_get_arg_type - * @param iter the message iter - * @returns the string + * @param method_call the message which the created + * message is a reply to. + * @returns a new DBusMessage, free with dbus_message_unref() + * @see dbus_message_new_method_call(), dbus_message_unref() */ -char * -dbus_message_iter_get_string (DBusMessageIter *iter) +DBusMessage* +dbus_message_new_method_return (DBusMessage *method_call) { - DBusMessageRealIter *real = (DBusMessageRealIter *)iter; - int type, pos; + DBusMessage *message; + const char *sender; - _dbus_return_val_if_fail (dbus_message_iter_check (real), NULL); - pos = dbus_message_iter_get_data_start (real, &type); - - _dbus_assert (type == DBUS_TYPE_STRING); + _dbus_return_val_if_fail (method_call != NULL, NULL); - return _dbus_demarshal_string (&real->message->body, real->message->byte_order, - pos, NULL); -} + sender = dbus_message_get_sender (method_call); -/** - * Returns the object path value that an iterator may point to. - * Note that you need to check that the iterator points to - * an object path value before using this function. - * - * @see dbus_message_iter_get_arg_type - * @param iter the message iter - * @returns the path - */ -char * -dbus_message_iter_get_object_path (DBusMessageIter *iter) -{ - DBusMessageRealIter *real = (DBusMessageRealIter *)iter; - int type, pos; + /* sender is allowed to be null here in peer-to-peer case */ + + message = dbus_message_new_empty_header (); + if (message == NULL) + return NULL; + + if (!_dbus_header_create (&message->header, + DBUS_MESSAGE_TYPE_METHOD_RETURN, + sender, NULL, NULL, NULL, NULL)) + { + dbus_message_unref (message); + return NULL; + } - _dbus_return_val_if_fail (dbus_message_iter_check (real), NULL); + dbus_message_set_no_reply (message, TRUE); - pos = dbus_message_iter_get_data_start (real, &type); - - _dbus_assert (type == DBUS_TYPE_OBJECT_PATH); + if (!dbus_message_set_reply_serial (message, + dbus_message_get_serial (method_call))) + { + dbus_message_unref (message); + return NULL; + } - return _dbus_demarshal_string (&real->message->body, real->message->byte_order, - pos, NULL); + return message; } /** - * Returns the name and data from a custom type that an iterator may - * point to. Note that you need to check that the iterator points to a - * custom type before using this function. + * Constructs a new message representing a signal emission. Returns + * #NULL if memory can't be allocated for the message. A signal is + * identified by its originating interface, and the name of the + * signal. * - * @see dbus_message_iter_get_arg_type - * @param iter the message iter - * @param name return location for the name of the custom type - * @param value return location for data - * @param len return location for length of data - * @returns TRUE if get succeed - * + * @param path the path to the object emitting the signal + * @param interface the interface the signal is emitted from + * @param name name of the signal + * @returns a new DBusMessage, free with dbus_message_unref() + * @see dbus_message_unref() */ -dbus_bool_t -dbus_message_iter_get_custom (DBusMessageIter *iter, - char **name, - unsigned char **value, - int *len) +DBusMessage* +dbus_message_new_signal (const char *path, + const char *interface, + const char *name) { - DBusMessageRealIter *real = (DBusMessageRealIter *)iter; - int type, pos; - char *_name; + DBusMessage *message; - _dbus_return_val_if_fail (dbus_message_iter_check (real), FALSE); + _dbus_return_val_if_fail (path != NULL, NULL); + _dbus_return_val_if_fail (interface != NULL, NULL); + _dbus_return_val_if_fail (name != NULL, NULL); + _dbus_return_val_if_fail (_dbus_check_is_valid_path (path), NULL); + _dbus_return_val_if_fail (_dbus_check_is_valid_interface (interface), NULL); + _dbus_return_val_if_fail (_dbus_check_is_valid_member (name), NULL); - pos = dbus_message_iter_get_data_start (real, &type); - - _dbus_assert (type == DBUS_TYPE_CUSTOM); - - _name = _dbus_demarshal_string (&real->message->body, real->message->byte_order, - pos, &pos); + message = dbus_message_new_empty_header (); + if (message == NULL) + return NULL; - if (_name == NULL) - return FALSE; - - if (!_dbus_demarshal_byte_array (&real->message->body, real->message->byte_order, - pos, NULL, value, len)) + if (!_dbus_header_create (&message->header, + DBUS_MESSAGE_TYPE_SIGNAL, + NULL, path, interface, name, NULL)) { - dbus_free (_name); - return FALSE; + dbus_message_unref (message); + return NULL; } - *name = _name; - - return TRUE; -} + dbus_message_set_no_reply (message, TRUE); -static void -_dbus_message_iter_get_basic_type (DBusMessageIter *iter, - char type, - void *value) -{ - DBusMessageRealIter *real = (DBusMessageRealIter *)iter; - int item_type, pos; - - _dbus_return_if_fail (dbus_message_iter_check (real)); - - pos = dbus_message_iter_get_data_start (real, &item_type); - - _dbus_assert (type == item_type); - - _dbus_demarshal_basic_type (&real->message->body, - type, value, - real->message->byte_order, - &pos); + return message; } - /** - * This function takes a va_list for use by language bindings - * - * This function supports #DBUS_TYPE_INT64 and #DBUS_TYPE_UINT64 - * only if #DBUS_HAVE_INT64 is defined. - * - * @todo this function (or some lower-level non-convenience function) - * needs better error handling; should allow the application to - * distinguish between out of memory, and bad data from the remote - * app. It also needs to not leak a bunch of args when it gets - * to the arg that's bad, as that would be a security hole - * (allow one app to force another to leak memory) - * - * @todo We need to free the argument data when an error occurs. + * Creates a new message that is an error reply to a certain message. + * Error replies are possible in response to method calls primarily. * - * @see dbus_message_get_args - * @param iter the message iter - * @param error error to be filled in - * @param first_arg_type type of the first argument - * @param var_args return location for first argument, followed by list of type/location pairs - * @returns #FALSE if error was set + * @param reply_to the original message + * @param error_name the error name + * @param error_message the error message string or #NULL for none + * @returns a new error message */ -dbus_bool_t -dbus_message_iter_get_args_valist (DBusMessageIter *iter, - DBusError *error, - int first_arg_type, - va_list var_args) +DBusMessage* +dbus_message_new_error (DBusMessage *reply_to, + const char *error_name, + const char *error_message) { - int spec_type, msg_type, i; - dbus_bool_t retval; + DBusMessage *message; + const char *sender; + DBusMessageIter iter; - _dbus_return_val_if_fail (iter != NULL, FALSE); - _dbus_return_val_if_error_is_set (error, FALSE); + _dbus_return_val_if_fail (reply_to != NULL, NULL); + _dbus_return_val_if_fail (error_name != NULL, NULL); + _dbus_return_val_if_fail (_dbus_check_is_valid_error_name (error_name), NULL); - retval = FALSE; - - spec_type = first_arg_type; - i = 0; - - while (spec_type != DBUS_TYPE_INVALID) + sender = dbus_message_get_sender (reply_to); + + /* sender may be NULL for non-message-bus case or + * when the message bus is dealing with an unregistered + * connection. + */ + message = dbus_message_new_empty_header (); + if (message == NULL) + return NULL; + + if (!_dbus_header_create (&message->header, + DBUS_MESSAGE_TYPE_ERROR, + sender, NULL, NULL, NULL, error_name)) { - msg_type = dbus_message_iter_get_arg_type (iter); - - if (msg_type != spec_type) - { - dbus_set_error (error, DBUS_ERROR_INVALID_ARGS, - "Argument %d is specified to be of type \"%s\", but " - "is actually of type \"%s\"\n", i, - _dbus_type_to_string (spec_type), - _dbus_type_to_string (msg_type)); + dbus_message_unref (message); + return NULL; + } - goto out; - } + dbus_message_set_no_reply (message, TRUE); - switch (spec_type) - { - case DBUS_TYPE_NIL: - break; - case DBUS_TYPE_BOOLEAN: - { - dbus_bool_t *ptr; - - ptr = va_arg (var_args, dbus_bool_t *); - - *ptr = dbus_message_iter_get_boolean (iter); - break; - } - case DBUS_TYPE_BYTE: - case DBUS_TYPE_INT32: - case DBUS_TYPE_UINT32: -#ifdef DBUS_HAVE_INT64 - case DBUS_TYPE_INT64: - case DBUS_TYPE_UINT64: -#endif /* DBUS_HAVE_INT64 */ - case DBUS_TYPE_DOUBLE: - { - void *ptr = va_arg (var_args, double *); - _dbus_message_iter_get_basic_type (iter, spec_type, ptr); - break; - } - - case DBUS_TYPE_STRING: - { - char **ptr; - - ptr = va_arg (var_args, char **); - - *ptr = dbus_message_iter_get_string (iter); - - if (!*ptr) - { - dbus_set_error (error, DBUS_ERROR_NO_MEMORY, NULL); - goto out; - } - - break; - } - - case DBUS_TYPE_OBJECT_PATH: - { - char **ptr; - - ptr = va_arg (var_args, char **); - - *ptr = dbus_message_iter_get_object_path (iter); - - if (!*ptr) - { - dbus_set_error (error, DBUS_ERROR_NO_MEMORY, NULL); - goto out; - } - - break; - } - - case DBUS_TYPE_CUSTOM: - { - char **name; - unsigned char **data; - int *len; - - name = va_arg (var_args, char **); - data = va_arg (var_args, unsigned char **); - len = va_arg (var_args, int *); - - if (!dbus_message_iter_get_custom (iter, name, data, len)) - { - dbus_set_error (error, DBUS_ERROR_NO_MEMORY, NULL); - goto out; - } - } - break; - case DBUS_TYPE_ARRAY: - { - void **data; - int *len, type; - dbus_bool_t err = FALSE; - - type = va_arg (var_args, int); - data = va_arg (var_args, void *); - len = va_arg (var_args, int *); - - _dbus_return_val_if_fail (data != NULL, FALSE); - _dbus_return_val_if_fail (len != NULL, FALSE); - - if (dbus_message_iter_get_array_type (iter) != type) - { - dbus_set_error (error, DBUS_ERROR_INVALID_ARGS, - "Argument %d is specified to be of type \"array of %s\", but " - "is actually of type \"array of %s\"\n", i, - _dbus_type_to_string (type), - _dbus_type_to_string (dbus_message_iter_get_array_type (iter))); - goto out; - } - - switch (type) - { - case DBUS_TYPE_BYTE: - err = !dbus_message_iter_get_byte_array (iter, (unsigned char **)data, len); - break; - case DBUS_TYPE_BOOLEAN: - err = !dbus_message_iter_get_boolean_array (iter, (unsigned char **)data, len); - break; - case DBUS_TYPE_INT32: - err = !dbus_message_iter_get_int32_array (iter, (dbus_int32_t **)data, len); - break; - case DBUS_TYPE_UINT32: - err = !dbus_message_iter_get_uint32_array (iter, (dbus_uint32_t **)data, len); - break; -#ifdef DBUS_HAVE_INT64 - case DBUS_TYPE_INT64: - err = !dbus_message_iter_get_int64_array (iter, (dbus_int64_t **)data, len); - break; - case DBUS_TYPE_UINT64: - err = !dbus_message_iter_get_uint64_array (iter, (dbus_uint64_t **)data, len); - break; -#endif /* DBUS_HAVE_INT64 */ - case DBUS_TYPE_DOUBLE: - err = !dbus_message_iter_get_double_array (iter, (double **)data, len); - break; - case DBUS_TYPE_STRING: - err = !dbus_message_iter_get_string_array (iter, (char ***)data, len); - break; - case DBUS_TYPE_OBJECT_PATH: - err = !dbus_message_iter_get_object_path_array (iter, (char ***)data, len); - break; - - case DBUS_TYPE_NIL: - case DBUS_TYPE_ARRAY: - case DBUS_TYPE_CUSTOM: - case DBUS_TYPE_DICT: - _dbus_warn ("dbus_message_get_args_valist doesn't support recursive arrays\n"); - dbus_set_error (error, DBUS_ERROR_NOT_SUPPORTED, NULL); - goto out; - default: - _dbus_warn ("Unknown field type %d\n", type); - dbus_set_error (error, DBUS_ERROR_NOT_SUPPORTED, NULL); - goto out; - } - if (err) - { - dbus_set_error (error, DBUS_ERROR_NO_MEMORY, NULL); - goto out; - } - } - break; - case DBUS_TYPE_DICT: - _dbus_warn ("dbus_message_get_args_valist doesn't support dicts\n"); - dbus_set_error (error, DBUS_ERROR_NOT_SUPPORTED, NULL); - goto out; - default: - dbus_set_error (error, DBUS_ERROR_NOT_SUPPORTED, NULL); - _dbus_warn ("Unknown field type %d\n", spec_type); - goto out; - } + if (!dbus_message_set_reply_serial (message, + dbus_message_get_serial (reply_to))) + { + dbus_message_unref (message); + return NULL; + } - spec_type = va_arg (var_args, int); - if (!dbus_message_iter_next (iter) && spec_type != DBUS_TYPE_INVALID) + if (error_message != NULL) + { + dbus_message_append_iter_init (message, &iter); + if (!dbus_message_iter_append_basic (&iter, + DBUS_TYPE_STRING, + &error_message)) { - dbus_set_error (error, DBUS_ERROR_INVALID_ARGS, - "Message has only %d arguments, but more were expected", i); - goto out; + dbus_message_unref (message); + return NULL; } - - i++; } - - retval = TRUE; - - out: - - return retval; + + return message; } /** - * Returns the byte value that an iterator may point to. - * Note that you need to check that the iterator points to - * a byte value before using this function. + * Creates a new message that is an error reply to a certain message. + * Error replies are possible in response to method calls primarily. * - * @see dbus_message_iter_get_arg_type - * @param iter the message iter - * @returns the byte value + * @param reply_to the original message + * @param error_name the error name + * @param error_format the error message format as with printf + * @param ... format string arguments + * @returns a new error message */ -unsigned char -dbus_message_iter_get_byte (DBusMessageIter *iter) +DBusMessage* +dbus_message_new_error_printf (DBusMessage *reply_to, + const char *error_name, + const char *error_format, + ...) { - unsigned char value = 0; - - _dbus_message_iter_get_basic_type (iter, DBUS_TYPE_BYTE, &value); + va_list args; + DBusString str; + DBusMessage *message; - return value; -} + _dbus_return_val_if_fail (reply_to != NULL, NULL); + _dbus_return_val_if_fail (error_name != NULL, NULL); + _dbus_return_val_if_fail (_dbus_check_is_valid_error_name (error_name), NULL); -/** - * Returns the boolean value that an iterator may point to. - * Note that you need to check that the iterator points to - * a boolean value before using this function. - * - * @see dbus_message_iter_get_arg_type - * @param iter the message iter - * @returns the boolean value - */ -dbus_bool_t -dbus_message_iter_get_boolean (DBusMessageIter *iter) -{ - unsigned char value = 0; + if (!_dbus_string_init (&str)) + return NULL; - _dbus_message_iter_get_basic_type (iter, DBUS_TYPE_BOOLEAN, &value); + va_start (args, error_format); - return (value != FALSE); -} + if (_dbus_string_append_printf_valist (&str, error_format, args)) + message = dbus_message_new_error (reply_to, error_name, + _dbus_string_get_const_data (&str)); + else + message = NULL; -/** - * Returns the 32 bit signed integer value that an iterator may point to. - * Note that you need to check that the iterator points to - * a 32-bit integer value before using this function. - * - * @see dbus_message_iter_get_arg_type - * @param iter the message iter - * @returns the integer - */ -dbus_int32_t -dbus_message_iter_get_int32 (DBusMessageIter *iter) -{ - dbus_int32_t value = 0; + _dbus_string_free (&str); - _dbus_message_iter_get_basic_type (iter, DBUS_TYPE_INT32, &value); + va_end (args); - return value; + return message; } + /** - * Returns the 32 bit unsigned integer value that an iterator may point to. - * Note that you need to check that the iterator points to - * a 32-bit unsigned integer value before using this function. + * Creates a new message that is an exact replica of the message + * specified, except that its refcount is set to 1, its message serial + * is reset to 0, and if the original message was "locked" (in the + * outgoing message queue and thus not modifiable) the new message + * will not be locked. * - * @see dbus_message_iter_get_arg_type - * @param iter the message iter - * @returns the integer + * @param message the message. + * @returns the new message. */ -dbus_uint32_t -dbus_message_iter_get_uint32 (DBusMessageIter *iter) +DBusMessage * +dbus_message_copy (const DBusMessage *message) { - dbus_int32_t value = 0; + DBusMessage *retval; - _dbus_message_iter_get_basic_type (iter, DBUS_TYPE_UINT32, &value); + _dbus_return_val_if_fail (message != NULL, NULL); - return value; -} + retval = dbus_new0 (DBusMessage, 1); + if (retval == NULL) + return NULL; -#ifdef DBUS_HAVE_INT64 + retval->refcount.value = 1; + retval->byte_order = message->byte_order; + retval->locked = FALSE; +#ifndef DBUS_DISABLE_CHECKS + retval->generation = message->generation; +#endif -/** - * Returns the 64 bit signed integer value that an iterator may point - * to. Note that you need to check that the iterator points to a - * 64-bit integer value before using this function. - * - * This function only exists if #DBUS_HAVE_INT64 is defined. - * - * @see dbus_message_iter_get_arg_type - * @param iter the message iter - * @returns the integer - */ -dbus_int64_t -dbus_message_iter_get_int64 (DBusMessageIter *iter) -{ - dbus_int64_t value = 0; + if (!_dbus_header_copy (&message->header, &retval->header)) + { + dbus_free (retval); + return NULL; + } - _dbus_message_iter_get_basic_type (iter, DBUS_TYPE_INT64, &value); + if (!_dbus_string_init_preallocated (&retval->body, + _dbus_string_get_length (&message->body))) + { + _dbus_header_free (&retval->header); + dbus_free (retval); + return NULL; + } - return value; -} + if (!_dbus_string_copy (&message->body, 0, + &retval->body, 0)) + goto failed_copy; -/** - * Returns the 64 bit unsigned integer value that an iterator may point to. - * Note that you need to check that the iterator points to - * a 64-bit unsigned integer value before using this function. - * - * This function only exists if #DBUS_HAVE_INT64 is defined. - * - * @see dbus_message_iter_get_arg_type - * @param iter the message iter - * @returns the integer - */ -dbus_uint64_t -dbus_message_iter_get_uint64 (DBusMessageIter *iter) -{ - dbus_uint64_t value = 0; + return retval; - _dbus_message_iter_get_basic_type (iter, DBUS_TYPE_UINT64, &value); + failed_copy: + _dbus_header_free (&retval->header); + _dbus_string_free (&retval->body); + dbus_free (retval); - return value; + return NULL; } -#endif /* DBUS_HAVE_INT64 */ /** - * Returns the double value that an iterator may point to. - * Note that you need to check that the iterator points to - * a string value before using this function. + * Increments the reference count of a DBusMessage. * - * @see dbus_message_iter_get_arg_type - * @param iter the message iter - * @returns the double + * @param message The message + * @returns the message + * @see dbus_message_unref */ -double -dbus_message_iter_get_double (DBusMessageIter *iter) +DBusMessage * +dbus_message_ref (DBusMessage *message) { - double value = 0.0; + dbus_int32_t old_refcount; - _dbus_message_iter_get_basic_type (iter, DBUS_TYPE_DOUBLE, &value); + _dbus_return_val_if_fail (message != NULL, NULL); + _dbus_return_val_if_fail (message->generation == _dbus_current_generation, NULL); - return value; -} + old_refcount = _dbus_atomic_inc (&message->refcount); + _dbus_assert (old_refcount >= 1); -/** - * Initializes an iterator for the array that the iterator - * may point to. Note that you need to check that the iterator - * points to an array prior to using this function. - * - * The array element type is returned in array_type, and the array - * iterator can only be used to get that type of data. - * - * @param iter the iterator - * @param array_iter pointer to an iterator to initialize - * @param array_type gets set to the type of the array elements - * @returns #FALSE if the array is empty - */ -dbus_bool_t -dbus_message_iter_init_array_iterator (DBusMessageIter *iter, - DBusMessageIter *array_iter, - int *array_type) -{ - DBusMessageRealIter *real = (DBusMessageRealIter *)iter; - DBusMessageRealIter *array_real = (DBusMessageRealIter *)array_iter; - int type, pos, len_pos, len, array_type_pos; - int _array_type; - - _dbus_return_val_if_fail (dbus_message_iter_check (real), FALSE); - - pos = dbus_message_iter_get_data_start (real, &type); - - _dbus_assert (type == DBUS_TYPE_ARRAY); - - _array_type = iter_get_array_type (real, &array_type_pos); - - len_pos = _DBUS_ALIGN_VALUE (pos, sizeof (dbus_uint32_t)); - len = _dbus_demarshal_uint32 (&real->message->body, real->message->byte_order, - pos, &pos); - - array_real->parent_iter = real; - array_real->message = real->message; - array_real->changed_stamp = real->message->changed_stamp; - - array_real->type = DBUS_MESSAGE_ITER_TYPE_ARRAY; - array_real->pos = pos; - array_real->end = pos + len; - - array_real->container_start = pos; - array_real->container_length_pos = len_pos; - array_real->wrote_dict_key = 0; - array_real->array_type_pos = array_type_pos; - array_real->array_type_done = TRUE; - - if (array_type != NULL) - *array_type = _array_type; - - return len > 0; + return message; } - /** - * Initializes an iterator for the dict that the iterator - * may point to. Note that you need to check that the iterator - * points to a dict prior to using this function. + * Decrements the reference count of a DBusMessage. * - * @param iter the iterator - * @param dict_iter pointer to an iterator to initialize - * @returns #FALSE if the dict is empty + * @param message The message + * @see dbus_message_ref */ -dbus_bool_t -dbus_message_iter_init_dict_iterator (DBusMessageIter *iter, - DBusMessageIter *dict_iter) -{ - DBusMessageRealIter *real = (DBusMessageRealIter *)iter; - DBusMessageRealIter *dict_real = (DBusMessageRealIter *)dict_iter; - int type, pos, len_pos, len; - - _dbus_return_val_if_fail (dbus_message_iter_check (real), FALSE); - - pos = dbus_message_iter_get_data_start (real, &type); - - _dbus_assert (type == DBUS_TYPE_DICT); - - len_pos = _DBUS_ALIGN_VALUE (pos, sizeof (dbus_uint32_t)); - len = _dbus_demarshal_uint32 (&real->message->body, real->message->byte_order, - pos, &pos); - - dict_real->parent_iter = real; - dict_real->message = real->message; - dict_real->changed_stamp = real->message->changed_stamp; - - dict_real->type = DBUS_MESSAGE_ITER_TYPE_DICT; - dict_real->pos = pos; - dict_real->end = pos + len; - - dict_real->container_start = pos; - dict_real->container_length_pos = len_pos; - dict_real->wrote_dict_key = 0; - - return len > 0; -} - -static dbus_bool_t -_dbus_message_iter_get_basic_type_array (DBusMessageIter *iter, - char type, - void **array, - int *array_len) +void +dbus_message_unref (DBusMessage *message) { - DBusMessageRealIter *real = (DBusMessageRealIter *)iter; - int item_type, pos; + dbus_int32_t old_refcount; - _dbus_return_val_if_fail (dbus_message_iter_check (real), FALSE); + _dbus_return_if_fail (message != NULL); + _dbus_return_if_fail (message->generation == _dbus_current_generation); - pos = dbus_message_iter_get_data_start (real, &item_type); - - _dbus_assert (item_type == DBUS_TYPE_ARRAY); + old_refcount = _dbus_atomic_dec (&message->refcount); - item_type = iter_get_array_type (real, NULL); - - _dbus_assert (type == item_type); + _dbus_assert (old_refcount >= 0); - return _dbus_demarshal_basic_type_array (&real->message->body, - item_type, array, array_len, - real->message->byte_order, &pos); + if (old_refcount == 1) + { + /* Calls application callbacks! */ + dbus_message_cache_or_finalize (message); + } } /** - * Returns the byte array that the iterator may point to. - * Note that you need to check that the iterator points - * to a byte array prior to using this function. + * Gets the type of a message. Types include + * #DBUS_MESSAGE_TYPE_METHOD_CALL, #DBUS_MESSAGE_TYPE_METHOD_RETURN, + * #DBUS_MESSAGE_TYPE_ERROR, #DBUS_MESSAGE_TYPE_SIGNAL, but other + * types are allowed and all code must silently ignore messages of + * unknown type. DBUS_MESSAGE_TYPE_INVALID will never be returned, + * however. * - * @param iter the iterator - * @param value return location for array values - * @param len return location for length of byte array - * @returns #TRUE on success - */ -dbus_bool_t -dbus_message_iter_get_byte_array (DBusMessageIter *iter, - unsigned char **value, - int *len) -{ - return _dbus_message_iter_get_basic_type_array (iter, DBUS_TYPE_BYTE, - (void **) value, len); -} - -/** - * Returns the boolean array that the iterator may point to. Note that - * you need to check that the iterator points to an array of the - * correct type prior to using this function. * - * @param iter the iterator - * @param value return location for the array - * @param len return location for the array length - * @returns #TRUE on success + * @param message the message + * @returns the type of the message */ -dbus_bool_t -dbus_message_iter_get_boolean_array (DBusMessageIter *iter, - unsigned char **value, - int *len) +int +dbus_message_get_type (DBusMessage *message) { - return _dbus_message_iter_get_basic_type_array (iter, DBUS_TYPE_BOOLEAN, - (void **) value, len); -} + _dbus_return_val_if_fail (message != NULL, DBUS_MESSAGE_TYPE_INVALID); -/** - * Returns the 32 bit signed integer array that the iterator may point - * to. Note that you need to check that the iterator points to an - * array of the correct type prior to using this function. - * - * @param iter the iterator - * @param value return location for the array - * @param len return location for the array length - * @returns #TRUE on success - */ -dbus_bool_t -dbus_message_iter_get_int32_array (DBusMessageIter *iter, - dbus_int32_t **value, - int *len) -{ - return _dbus_message_iter_get_basic_type_array (iter, DBUS_TYPE_INT32, - (void **) value, len); + return _dbus_header_get_message_type (&message->header); } /** - * Returns the 32 bit unsigned integer array that the iterator may point - * to. Note that you need to check that the iterator points to an - * array of the correct type prior to using this function. + * Appends fields to a message given a variable argument list. The + * variable argument list should contain the type of each argument + * followed by the value to append. Appendable types are basic types, + * and arrays of fixed-length basic types. To append variable-length + * basic types, or any more complex value, you have to use an iterator + * rather than this function. * - * @param iter the iterator - * @param value return location for the array - * @param len return location for the array length + * To append a basic type, specify its type code followed by the + * value. For example: + * + * @code + * DBUS_TYPE_INT32, 42, + * DBUS_TYPE_STRING, "Hello World" + * @endcode + * or + * @code + * dbus_int32_t val = 42; + * DBUS_TYPE_INT32, val + * @endcode + * + * Be sure that your provided value is the right size. For example, this + * won't work: + * @code + * DBUS_TYPE_INT64, 42 + * @endcode + * Because the "42" will be a 32-bit integer. You need to cast to + * 64-bit. + * + * To append an array of fixed-length basic types, pass in the + * DBUS_TYPE_ARRAY typecode, the element typecode, the address of + * the array pointer, and a 32-bit integer giving the number of + * elements in the array. So for example: + * @code + * const dbus_int32_t array[] = { 1, 2, 3 }; + * const dbus_int32_t *v_ARRAY = array; + * DBUS_TYPE_ARRAY, DBUS_TYPE_INT32, &v_ARRAY, 3 + * @endcode + * + * @warning in C, given "int array[]", "&array == array" (the + * comp.lang.c FAQ says otherwise, but gcc and the FAQ don't agree). + * So if you're using an array instead of a pointer you have to create + * a pointer variable, assign the array to it, then take the address + * of the pointer variable. For strings it works to write + * const char *array = "Hello" and then use &array though. + * + * The last argument to this function must be #DBUS_TYPE_INVALID, + * marking the end of the argument list. + * + * @todo support DBUS_TYPE_STRUCT and DBUS_TYPE_VARIANT and complex arrays + * + * @todo If this fails due to lack of memory, the message is hosed and + * you have to start over building the whole message. + * + * @param message the message + * @param first_arg_type type of the first argument + * @param ... value of first argument, list of additional type-value pairs * @returns #TRUE on success */ dbus_bool_t -dbus_message_iter_get_uint32_array (DBusMessageIter *iter, - dbus_uint32_t **value, - int *len) +dbus_message_append_args (DBusMessage *message, + int first_arg_type, + ...) { - return _dbus_message_iter_get_basic_type_array (iter, DBUS_TYPE_UINT32, - (void **) value, len); -} + dbus_bool_t retval; + va_list var_args; -#ifdef DBUS_HAVE_INT64 + _dbus_return_val_if_fail (message != NULL, FALSE); -/** - * Returns the 64 bit signed integer array that the iterator may point - * to. Note that you need to check that the iterator points to an - * array of the correct type prior to using this function. - * - * This function only exists if #DBUS_HAVE_INT64 is defined. - * - * @param iter the iterator - * @param value return location for the array - * @param len return location for the array length - * @returns #TRUE on success - */ -dbus_bool_t -dbus_message_iter_get_int64_array (DBusMessageIter *iter, - dbus_int64_t **value, - int *len) -{ - return _dbus_message_iter_get_basic_type_array (iter, DBUS_TYPE_INT64, - (void **) value, len); + va_start (var_args, first_arg_type); + retval = dbus_message_append_args_valist (message, + first_arg_type, + var_args); + va_end (var_args); + + return retval; } /** - * Returns the 64 bit unsigned integer array that the iterator may point - * to. Note that you need to check that the iterator points to an - * array of the correct type prior to using this function. + * This function takes a va_list for use by language bindings. + * It's otherwise the same as dbus_message_append_args(). * - * This function only exists if #DBUS_HAVE_INT64 is defined. + * @todo for now, if this function fails due to OOM it will leave + * the message half-written and you have to discard the message + * and start over. * - * @param iter the iterator - * @param value return location for the array - * @param len return location for the array length + * @see dbus_message_append_args. + * @param message the message + * @param first_arg_type type of first argument + * @param var_args value of first argument, then list of type/value pairs * @returns #TRUE on success */ dbus_bool_t -dbus_message_iter_get_uint64_array (DBusMessageIter *iter, - dbus_uint64_t **value, - int *len) +dbus_message_append_args_valist (DBusMessage *message, + int first_arg_type, + va_list var_args) { - return _dbus_message_iter_get_basic_type_array (iter, DBUS_TYPE_UINT64, - (void **) value, len); -} + int type; + DBusMessageIter iter; -#endif /* DBUS_HAVE_INT64 */ + _dbus_return_val_if_fail (message != NULL, FALSE); -/** - * Returns the double array that the iterator may point to. Note that - * you need to check that the iterator points to an array of the - * correct type prior to using this function. - * - * @param iter the iterator - * @param value return location for the array - * @param len return location for the array length - * @returns #TRUE on success - */ -dbus_bool_t -dbus_message_iter_get_double_array (DBusMessageIter *iter, - double **value, - int *len) -{ - return _dbus_message_iter_get_basic_type_array (iter, DBUS_TYPE_DOUBLE, - (void **) value, len); + type = first_arg_type; + + dbus_message_append_iter_init (message, &iter); + + while (type != DBUS_TYPE_INVALID) + { + if (_dbus_type_is_basic (type)) + { + const DBusBasicValue *value; + value = va_arg (var_args, const DBusBasicValue*); + + if (!dbus_message_iter_append_basic (&iter, + type, + value)) + goto failed; + } + else if (type == DBUS_TYPE_ARRAY) + { + int element_type; + const DBusBasicValue **value; + int n_elements; + DBusMessageIter array; + char buf[2]; + + element_type = va_arg (var_args, int); + +#ifndef DBUS_DISABLE_CHECKS + if (!_dbus_type_is_fixed (element_type)) + { + _dbus_warn ("arrays of %s can't be appended with %s for now\n", + _dbus_type_to_string (element_type), + _DBUS_FUNCTION_NAME); + goto failed; + } +#endif + + value = va_arg (var_args, const DBusBasicValue**); + n_elements = va_arg (var_args, int); + + buf[0] = element_type; + buf[1] = '\0'; + if (!dbus_message_iter_open_container (&iter, + DBUS_TYPE_ARRAY, + buf, + &array)) + goto failed; + + if (!dbus_message_iter_append_fixed_array (&array, + element_type, + value, + n_elements)) + goto failed; + + if (!dbus_message_iter_close_container (&iter, &array)) + goto failed; + } +#ifndef DBUS_DISABLE_CHECKS + else + { + _dbus_warn ("type %s isn't supported yet in %s\n", + _dbus_type_to_string (type), _DBUS_FUNCTION_NAME); + goto failed; + } +#endif + + type = va_arg (var_args, int); + } + + return TRUE; + + failed: + return FALSE; } /** - * Returns the string array that the iterator may point to. - * Note that you need to check that the iterator points - * to a string array prior to using this function. + * Gets arguments from a message given a variable argument list. The + * supported types include those supported by + * dbus_message_append_args(); that is, basic types and arrays of + * fixed-length basic types. The arguments are the same as they would + * be for dbus_message_iter_get_basic() or + * dbus_message_iter_get_fixed_array(). * - * The returned value is a #NULL-terminated array of strings. - * Each string is a separate malloc block, and the array - * itself is a malloc block. You can free this type of - * string array with dbus_free_string_array(). + * In addition to those types, arrays of string, object path, and + * signature are supported; but these are returned as allocated memory + * and must be freed with dbus_free_string_array(), while the other + * types are returned as const references. * - * @param iter the iterator - * @param value return location for string values - * @param len return location for length of byte array - * @returns #TRUE on success + * The variable argument list should contain the type of the argument + * followed by a pointer to where the value should be stored. The list + * is terminated with #DBUS_TYPE_INVALID. + * + * The returned values are constant; do not free them. They point + * into the #DBusMessage. + * + * If the requested arguments are not present, or do not have the + * requested types, then an error will be set. + * + * @todo support DBUS_TYPE_STRUCT and DBUS_TYPE_VARIANT and complex arrays + * + * @param message the message + * @param error error to be filled in on failure + * @param first_arg_type the first argument type + * @param ... location for first argument value, then list of type-location pairs + * @returns #FALSE if the error was set */ dbus_bool_t -dbus_message_iter_get_string_array (DBusMessageIter *iter, - char ***value, - int *len) +dbus_message_get_args (DBusMessage *message, + DBusError *error, + int first_arg_type, + ...) { - DBusMessageRealIter *real = (DBusMessageRealIter *)iter; - int type, pos; - - _dbus_return_val_if_fail (dbus_message_iter_check (real), FALSE); + dbus_bool_t retval; + va_list var_args; - pos = dbus_message_iter_get_data_start (real, &type); - - _dbus_assert (type == DBUS_TYPE_ARRAY); + _dbus_return_val_if_fail (message != NULL, FALSE); + _dbus_return_val_if_error_is_set (error, FALSE); - type = iter_get_array_type (real, NULL); - _dbus_assert (type == DBUS_TYPE_STRING); + va_start (var_args, first_arg_type); + retval = dbus_message_get_args_valist (message, error, first_arg_type, var_args); + va_end (var_args); - if (!_dbus_demarshal_string_array (&real->message->body, real->message->byte_order, - pos, NULL, value, len)) - return FALSE; - else - return TRUE; + return retval; } /** - * Returns the object path array that the iterator may point to. - * Note that you need to check that the iterator points - * to an object path array prior to using this function. - * - * The returned value is a #NULL-terminated array of strings. - * Each string is a separate malloc block, and the array - * itself is a malloc block. You can free this type of - * array with dbus_free_string_array(). + * This function takes a va_list for use by language bindings. It is + * otherwise the same as dbus_message_get_args(). * - * @param iter the iterator - * @param value return location for string values - * @param len return location for length of byte array - * @returns #TRUE on success + * @see dbus_message_get_args + * @param message the message + * @param error error to be filled in + * @param first_arg_type type of the first argument + * @param var_args return location for first argument, followed by list of type/location pairs + * @returns #FALSE if error was set */ dbus_bool_t -dbus_message_iter_get_object_path_array (DBusMessageIter *iter, - char ***value, - int *len) +dbus_message_get_args_valist (DBusMessage *message, + DBusError *error, + int first_arg_type, + va_list var_args) { - DBusMessageRealIter *real = (DBusMessageRealIter *)iter; - int type, pos; - - _dbus_return_val_if_fail (dbus_message_iter_check (real), FALSE); - - pos = dbus_message_iter_get_data_start (real, &type); - - _dbus_assert (type == DBUS_TYPE_ARRAY); + DBusMessageIter iter; - type = iter_get_array_type (real, NULL); - _dbus_assert (type == DBUS_TYPE_OBJECT_PATH); + _dbus_return_val_if_fail (message != NULL, FALSE); + _dbus_return_val_if_error_is_set (error, FALSE); - if (!_dbus_demarshal_string_array (&real->message->body, real->message->byte_order, - pos, NULL, value, len)) - return FALSE; - else - return TRUE; + dbus_message_iter_init (message, &iter); + return dbus_message_iter_get_args_valist (&iter, error, first_arg_type, var_args); } /** - * Returns the key name fot the dict entry that an iterator - * may point to. Note that you need to check that the iterator - * points to a dict entry before using this function. + * Reads arguments from a message iterator given a variable argument + * list. Only arguments of basic type and arrays of fixed-length + * basic type may be read with this function. See + * dbus_message_get_args() for more details. * - * @see dbus_message_iter_init_dict_iterator - * @param iter the message iter - * @returns the key name + * @todo this is static for now because there's no corresponding + * iter_append_args() and I'm not sure we need this function to be + * public since dbus_message_get_args() is what you usually want + * + * @param iter the message iterator + * @param error error to be filled in on failure + * @param first_arg_type the first argument type + * @param ... location for first argument value, then list of type-location pairs + * @returns #FALSE if the error was set */ -char * -dbus_message_iter_get_dict_key (DBusMessageIter *iter) +static dbus_bool_t +dbus_message_iter_get_args (DBusMessageIter *iter, + DBusError *error, + int first_arg_type, + ...) { - DBusMessageRealIter *real = (DBusMessageRealIter *)iter; + dbus_bool_t retval; + va_list var_args; + + _dbus_return_val_if_fail (iter != NULL, FALSE); + _dbus_return_val_if_error_is_set (error, FALSE); + + va_start (var_args, first_arg_type); + retval = dbus_message_iter_get_args_valist (iter, error, first_arg_type, var_args); + va_end (var_args); - _dbus_return_val_if_fail (dbus_message_iter_check (real), NULL); + return retval; +} - _dbus_assert (real->type == DBUS_MESSAGE_ITER_TYPE_DICT); +static void +_dbus_message_iter_init_common (DBusMessage *message, + DBusMessageRealIter *real, + int iter_type) +{ + _dbus_assert (sizeof (DBusMessageRealIter) <= sizeof (DBusMessageIter)); - return _dbus_demarshal_string (&real->message->body, real->message->byte_order, - real->pos, NULL); + real->message = message; + real->changed_stamp = message->changed_stamp; + real->iter_type = iter_type; + real->sig_refcount = 0; } /** - * Initializes a DBusMessageIter pointing to the end of the - * message. This iterator can be used to append data to the - * message. + * Initializes a #DBusMessageIter for reading the arguments of the + * message passed in. * * @param message the message * @param iter pointer to an iterator to initialize + * @returns #FALSE if the message has no arguments */ -void -dbus_message_append_iter_init (DBusMessage *message, - DBusMessageIter *iter) +dbus_bool_t +dbus_message_iter_init (DBusMessage *message, + DBusMessageIter *iter) { DBusMessageRealIter *real = (DBusMessageRealIter *)iter; + const DBusString *type_str; + int type_pos; - _dbus_return_if_fail (message != NULL); - _dbus_return_if_fail (iter != NULL); - - real->message = message; - real->parent_iter = NULL; - real->changed_stamp = message->changed_stamp; - - real->type = DBUS_MESSAGE_ITER_TYPE_MESSAGE; - real->end = _dbus_string_get_length (&real->message->body); - real->pos = real->end; - - real->container_length_pos = 0; - real->wrote_dict_key = 0; + _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); + + _dbus_message_iter_init_common (message, real, + DBUS_MESSAGE_ITER_TYPE_READER); + + _dbus_type_reader_init (&real->u.reader, + message->byte_order, + type_str, type_pos, + &message->body, + 0); + + return _dbus_type_reader_has_next (&real->u.reader); } #ifndef DBUS_DISABLE_CHECKS static dbus_bool_t -dbus_message_iter_append_check (DBusMessageRealIter *iter) +_dbus_message_iter_check (DBusMessageRealIter *iter) { if (iter == NULL) { - _dbus_warn ("dbus iterator check failed: NULL iterator\n"); + _dbus_warn ("dbus message iterator is NULL\n"); return FALSE; } - - if (iter->message->locked) + + if (iter->iter_type == DBUS_MESSAGE_ITER_TYPE_READER) { - _dbus_warn ("dbus iterator check failed: message is locked (has already been sent)\n"); - return FALSE; + if (iter->u.reader.byte_order != iter->message->byte_order) + { + _dbus_warn ("dbus message changed byte order since iterator was created\n"); + return FALSE; + } } - - if (iter->changed_stamp != iter->message->changed_stamp) + else if (iter->iter_type == DBUS_MESSAGE_ITER_TYPE_WRITER) { - _dbus_warn ("dbus iterator check failed: invalid iterator, must re-initialize it after modifying the message"); - return FALSE; + if (iter->u.writer.byte_order != iter->message->byte_order) + { + _dbus_warn ("dbus message changed byte order since append iterator was created\n"); + return FALSE; + } } - - if (iter->pos != iter->end) + else { - _dbus_warn ("dbus iterator check failed: can only append at end of message"); + _dbus_warn ("dbus message iterator looks uninitialized or corrupted\n"); return FALSE; } - - if (iter->pos != _dbus_string_get_length (&iter->message->body)) + + if (iter->changed_stamp != iter->message->changed_stamp) { - _dbus_warn ("dbus iterator check failed: append pos not at end of message string"); + _dbus_warn ("dbus message iterator invalid because the message has been modified (or perhaps the iterator is just uninitialized)\n"); return FALSE; } @@ -3644,1057 +1503,1111 @@ dbus_message_iter_append_check (DBusMessageRealIter *iter) } #endif /* DBUS_DISABLE_CHECKS */ -static dbus_bool_t -dbus_message_iter_append_type (DBusMessageRealIter *iter, - int type) -{ - const char *data; - - switch (iter->type) - { - case DBUS_MESSAGE_ITER_TYPE_MESSAGE: - if (!_dbus_string_append_byte (&iter->message->body, type)) - return FALSE; - - if (!_dbus_message_append_byte_to_signature (iter->message, type)) - { - _dbus_string_shorten (&iter->message->body, 1); - return FALSE; - } - break; - - case DBUS_MESSAGE_ITER_TYPE_ARRAY: - data = _dbus_string_get_const_data_len (&iter->message->body, - iter->array_type_pos, 1); - if (type != *data) - { - _dbus_warn ("Appended element of wrong type for array\n"); - return FALSE; - } - break; - - case DBUS_MESSAGE_ITER_TYPE_DICT: - if (!iter->wrote_dict_key) - { - _dbus_warn ("Appending dict data before key name\n"); - return FALSE; - } - - if (!_dbus_string_append_byte (&iter->message->body, type)) - return FALSE; - - break; - - default: - _dbus_assert_not_reached ("Invalid iter type"); - break; - } - - return TRUE; -} - -static void -dbus_message_iter_update_after_change (DBusMessageRealIter *iter) -{ - iter->changed_stamp = iter->message->changed_stamp; - - /* Set new end of iter */ - iter->end = _dbus_string_get_length (&iter->message->body); - iter->pos = iter->end; - - /* Set container length */ - if (iter->type == DBUS_MESSAGE_ITER_TYPE_DICT || - (iter->type == DBUS_MESSAGE_ITER_TYPE_ARRAY && iter->array_type_done)) - _dbus_marshal_set_uint32 (&iter->message->body, - iter->message->byte_order, - iter->container_length_pos, - iter->end - iter->container_start); - - if (iter->parent_iter) - dbus_message_iter_update_after_change (iter->parent_iter); -} - -static void -dbus_message_iter_append_done (DBusMessageRealIter *iter) -{ - iter->message->changed_stamp++; - dbus_message_iter_update_after_change (iter); - iter->wrote_dict_key = FALSE; -} - /** - * Appends a nil value to the message + * Checks if an iterator has any more fields. * - * @param iter an iterator pointing to the end of the message - * @returns #TRUE on success + * @param iter the message iter + * @returns #TRUE if there are more fields + * following */ dbus_bool_t -dbus_message_iter_append_nil (DBusMessageIter *iter) -{ - DBusMessageRealIter *real = (DBusMessageRealIter *)iter; - - _dbus_return_val_if_fail (dbus_message_iter_append_check (real), FALSE); - - if (!dbus_message_iter_append_type (real, DBUS_TYPE_NIL)) - return FALSE; - - dbus_message_iter_append_done (real); - - return TRUE; -} - -static dbus_bool_t -dbus_message_iter_append_basic (DBusMessageIter *iter, - char type, - void *value) +dbus_message_iter_has_next (DBusMessageIter *iter) { DBusMessageRealIter *real = (DBusMessageRealIter *)iter; - _dbus_return_val_if_fail (dbus_message_iter_append_check (real), FALSE); - - if (!dbus_message_iter_append_type (real, type)) - return FALSE; - - if (!_dbus_marshal_basic_type (&real->message->body, - type, value, - real->message->byte_order)) - { - _dbus_string_set_length (&real->message->body, real->pos); - return FALSE; - } + _dbus_return_val_if_fail (_dbus_message_iter_check (real), FALSE); + _dbus_return_val_if_fail (real->iter_type == DBUS_MESSAGE_ITER_TYPE_READER, FALSE); - dbus_message_iter_append_done (real); - - return TRUE; + return _dbus_type_reader_has_next (&real->u.reader); } /** - * Appends a boolean value to the message + * Moves the iterator to the next field, if any. If there's no next + * field, returns #FALSE. If the iterator moves forward, returns + * #TRUE. * - * @param iter an iterator pointing to the end of the message - * @param value the boolean value - * @returns #TRUE on success + * @param iter the message iter + * @returns #TRUE if the iterator was moved to the next field */ dbus_bool_t -dbus_message_iter_append_boolean (DBusMessageIter *iter, - dbus_bool_t value) +dbus_message_iter_next (DBusMessageIter *iter) { - unsigned char val = (value != FALSE); - return dbus_message_iter_append_basic (iter, DBUS_TYPE_BOOLEAN, &val); + DBusMessageRealIter *real = (DBusMessageRealIter *)iter; + + _dbus_return_val_if_fail (_dbus_message_iter_check (real), FALSE); + _dbus_return_val_if_fail (real->iter_type == DBUS_MESSAGE_ITER_TYPE_READER, FALSE); + + return _dbus_type_reader_next (&real->u.reader); } /** - * Appends a byte to the message + * Returns the argument type of the argument that the message iterator + * points to. If the iterator is at the end of the message, returns + * #DBUS_TYPE_INVALID. You can thus write a loop as follows: * - * @param iter an iterator pointing to the end of the message - * @param value the byte value - * @returns #TRUE on success + * @code + * dbus_message_iter_init (&iter); + * while ((current_type = dbus_message_iter_get_arg_type (&iter)) != DBUS_TYPE_INVALID) + * dbus_message_iter_next (&iter); + * @endcode + * + * @param iter the message iter + * @returns the argument type */ -dbus_bool_t -dbus_message_iter_append_byte (DBusMessageIter *iter, - unsigned char value) +int +dbus_message_iter_get_arg_type (DBusMessageIter *iter) { - return dbus_message_iter_append_basic (iter, DBUS_TYPE_BYTE, &value); + DBusMessageRealIter *real = (DBusMessageRealIter *)iter; + + _dbus_return_val_if_fail (_dbus_message_iter_check (real), DBUS_TYPE_INVALID); + _dbus_return_val_if_fail (real->iter_type == DBUS_MESSAGE_ITER_TYPE_READER, FALSE); + + return _dbus_type_reader_get_current_type (&real->u.reader); } /** - * Appends a 32 bit signed integer to the message. + * Returns the element type of the array that the message iterator + * points to. Note that you need to check that the iterator points to + * an array prior to using this function. * - * @param iter an iterator pointing to the end of the message - * @param value the integer value - * @returns #TRUE on success + * @param iter the message iter + * @returns the array element type */ -dbus_bool_t -dbus_message_iter_append_int32 (DBusMessageIter *iter, - dbus_int32_t value) +int +dbus_message_iter_get_array_type (DBusMessageIter *iter) { - return dbus_message_iter_append_basic (iter, DBUS_TYPE_INT32, &value); + DBusMessageRealIter *real = (DBusMessageRealIter *)iter; + + _dbus_return_val_if_fail (_dbus_message_iter_check (real), DBUS_TYPE_INVALID); + _dbus_return_val_if_fail (real->iter_type == DBUS_MESSAGE_ITER_TYPE_READER, DBUS_TYPE_INVALID); + _dbus_return_val_if_fail (dbus_message_iter_get_arg_type (iter) == DBUS_TYPE_ARRAY, DBUS_TYPE_INVALID); + + return _dbus_type_reader_get_array_type (&real->u.reader); } /** - * Appends a 32 bit unsigned integer to the message. + * Recurses into a container value when reading values from a message, + * initializing a sub-iterator to use for traversing the child values + * of the container. * - * @param iter an iterator pointing to the end of the message - * @param value the integer value - * @returns #TRUE on success + * Note that this recurses into a value, not a type, so you can only + * recurse if the value exists. The main implication of this is that + * if you have for example an empty array of array of int32, you can + * recurse into the outermost array, but it will have no values, so + * you won't be able to recurse further. There's no array of int32 to + * recurse into. + * + * @param iter the message iterator + * @param sub the sub-iterator to initialize */ -dbus_bool_t -dbus_message_iter_append_uint32 (DBusMessageIter *iter, - dbus_uint32_t value) +void +dbus_message_iter_recurse (DBusMessageIter *iter, + DBusMessageIter *sub) { - return dbus_message_iter_append_basic (iter, DBUS_TYPE_UINT32, &value); -} + DBusMessageRealIter *real = (DBusMessageRealIter *)iter; + DBusMessageRealIter *real_sub = (DBusMessageRealIter *)sub; -#ifdef DBUS_HAVE_INT64 + _dbus_return_if_fail (_dbus_message_iter_check (real)); + _dbus_return_if_fail (sub != NULL); + + *real_sub = *real; + _dbus_type_reader_recurse (&real->u.reader, &real_sub->u.reader); +} /** - * Appends a 64 bit signed integer to the message. + * Reads a basic-typed value from the message iterator. + * Basic types are the non-containers such as integer and string. * - * This function only exists if #DBUS_HAVE_INT64 is defined. + * The value argument should be the address of a location to store + * the returned value. So for int32 it should be a "dbus_int32_t*" + * and for string a "const char**". The returned value is + * by reference and should not be freed. * - * @param iter an iterator pointing to the end of the message - * @param value the integer value - * @returns #TRUE on success + * All returned values are guaranteed to fit in 8 bytes. So you can + * write code like this: + * + * @code + * #ifdef DBUS_HAVE_INT64 + * dbus_uint64_t value; + * int type; + * dbus_message_iter_get_basic (&read_iter, &value); + * type = dbus_message_iter_get_arg_type (&read_iter); + * dbus_message_iter_append_basic (&write_iter, type, &value); + * #endif + * @endcode + * + * To avoid the #DBUS_HAVE_INT64 conditional, create a struct or + * something that occupies at least 8 bytes, e.g. you could use a + * struct with two int32 values in it. dbus_uint64_t is just one + * example of a type that's large enough to hold any possible value. + * + * Be sure you have somehow checked that + * dbus_message_iter_get_arg_type() matches the type you are + * expecting, or you'll crash when you try to use an integer as a + * string or something. + * + * @param iter the iterator + * @param value location to store the value */ -dbus_bool_t -dbus_message_iter_append_int64 (DBusMessageIter *iter, - dbus_int64_t value) +void +dbus_message_iter_get_basic (DBusMessageIter *iter, + void *value) { - return dbus_message_iter_append_basic (iter, DBUS_TYPE_INT64, &value); + DBusMessageRealIter *real = (DBusMessageRealIter *)iter; + + _dbus_return_if_fail (_dbus_message_iter_check (real)); + _dbus_return_if_fail (value != NULL); + + _dbus_type_reader_read_basic (&real->u.reader, + value); } /** - * Appends a 64 bit unsigned integer to the message. + * Reads a block of fixed-length values from the message iterator. + * Fixed-length values are those basic types that are not string-like, + * such as integers, bool, double. The block read will be from the + * current position in the array until the end of the array. * - * This function only exists if #DBUS_HAVE_INT64 is defined. + * The value argument should be the address of a location to store the + * returned array. So for int32 it should be a "const dbus_int32_t**" + * The returned value is by reference and should not be freed. * - * @param iter an iterator pointing to the end of the message - * @param value the integer value - * @returns #TRUE on success + * @param iter the iterator + * @param value location to store the block + * @param n_elements number of elements in the block */ -dbus_bool_t -dbus_message_iter_append_uint64 (DBusMessageIter *iter, - dbus_uint64_t value) +void +dbus_message_iter_get_fixed_array (DBusMessageIter *iter, + void *value, + int *n_elements) { - return dbus_message_iter_append_basic (iter, DBUS_TYPE_UINT64, &value); -} + DBusMessageRealIter *real = (DBusMessageRealIter *)iter; -#endif /* DBUS_HAVE_INT64 */ + _dbus_return_if_fail (_dbus_message_iter_check (real)); + _dbus_return_if_fail (value != NULL); + _dbus_return_if_fail (_dbus_type_is_fixed (_dbus_type_reader_get_array_type (&real->u.reader))); -/** - * Appends a double value to the message. - * - * @param iter an iterator pointing to the end of the message - * @param value the double value - * @returns #TRUE on success - */ -dbus_bool_t -dbus_message_iter_append_double (DBusMessageIter *iter, - double value) -{ - return dbus_message_iter_append_basic (iter, DBUS_TYPE_DOUBLE, &value); + _dbus_type_reader_read_fixed_multi (&real->u.reader, + value, n_elements); } /** - * Appends a UTF-8 string to the message. + * This function takes a va_list for use by language bindings and is + * otherwise the same as dbus_message_iter_get_args(). + * dbus_message_get_args() is the place to go for complete + * documentation. * - * @todo add return_val_if_fail(UTF-8 is valid) + * @todo this is static for now, should be public if + * dbus_message_iter_get_args_valist() is made public. * - * @param iter an iterator pointing to the end of the message - * @param value the string - * @returns #TRUE on success + * @see dbus_message_get_args + * @param iter the message iter + * @param error error to be filled in + * @param first_arg_type type of the first argument + * @param var_args return location for first argument, followed by list of type/location pairs + * @returns #FALSE if error was set */ -dbus_bool_t -dbus_message_iter_append_string (DBusMessageIter *iter, - const char *value) +static dbus_bool_t +dbus_message_iter_get_args_valist (DBusMessageIter *iter, + DBusError *error, + int first_arg_type, + va_list var_args) { DBusMessageRealIter *real = (DBusMessageRealIter *)iter; + int spec_type, msg_type, i; + dbus_bool_t retval; - _dbus_return_val_if_fail (dbus_message_iter_append_check (real), FALSE); - - if (!dbus_message_iter_append_type (real, DBUS_TYPE_STRING)) - return FALSE; - - if (!_dbus_marshal_string (&real->message->body, real->message->byte_order, value)) + _dbus_return_val_if_fail (_dbus_message_iter_check (real), FALSE); + _dbus_return_val_if_error_is_set (error, FALSE); + + retval = FALSE; + + spec_type = first_arg_type; + i = 0; + + while (spec_type != DBUS_TYPE_INVALID) { - _dbus_string_set_length (&real->message->body, real->pos); - return FALSE; + msg_type = dbus_message_iter_get_arg_type (iter); + + if (msg_type != spec_type) + { + dbus_set_error (error, DBUS_ERROR_INVALID_ARGS, + "Argument %d is specified to be of type \"%s\", but " + "is actually of type \"%s\"\n", i, + _dbus_type_to_string (spec_type), + _dbus_type_to_string (msg_type)); + + goto out; + } + + if (_dbus_type_is_basic (spec_type)) + { + DBusBasicValue *ptr; + + ptr = va_arg (var_args, DBusBasicValue*); + + _dbus_return_val_if_fail (ptr != NULL, FALSE); + + _dbus_type_reader_read_basic (&real->u.reader, + ptr); + } + else if (spec_type == DBUS_TYPE_ARRAY) + { + int element_type; + int spec_element_type; + const DBusBasicValue **ptr; + int *n_elements_p; + DBusTypeReader array; + + spec_element_type = va_arg (var_args, int); + element_type = _dbus_type_reader_get_array_type (&real->u.reader); + + if (spec_element_type != element_type) + { + dbus_set_error (error, DBUS_ERROR_INVALID_ARGS, + "Argument %d is specified to be an array of \"%s\", but " + "is actually an array of \"%s\"\n", + i, + _dbus_type_to_string (spec_element_type), + _dbus_type_to_string (element_type)); + + goto out; + } + + if (_dbus_type_is_fixed (spec_element_type)) + { + ptr = va_arg (var_args, const DBusBasicValue**); + n_elements_p = va_arg (var_args, int*); + + _dbus_return_val_if_fail (ptr != NULL, FALSE); + _dbus_return_val_if_fail (n_elements_p != NULL, FALSE); + + _dbus_type_reader_recurse (&real->u.reader, &array); + + _dbus_type_reader_read_fixed_multi (&array, + ptr, n_elements_p); + } + else if (spec_element_type == DBUS_TYPE_STRING || + spec_element_type == DBUS_TYPE_SIGNATURE || + spec_element_type == DBUS_TYPE_OBJECT_PATH) + { + char ***str_array_p; + int i; + char **str_array; + + str_array_p = va_arg (var_args, char***); + n_elements_p = va_arg (var_args, int*); + + _dbus_return_val_if_fail (str_array_p != NULL, FALSE); + _dbus_return_val_if_fail (n_elements_p != NULL, FALSE); + + /* Count elements in the array */ + _dbus_type_reader_recurse (&real->u.reader, &array); + + i = 0; + if (_dbus_type_reader_has_next (&array)) + { + while (_dbus_type_reader_next (&array)) + ++i; + } + + str_array = dbus_new0 (char*, i + 1); + if (str_array == NULL) + { + _DBUS_SET_OOM (error); + goto out; + } + + /* Now go through and dup each string */ + _dbus_type_reader_recurse (&real->u.reader, &array); + + i = 0; + if (_dbus_type_reader_has_next (&array)) + { + do + { + const char *s; + _dbus_type_reader_read_basic (&array, + &s); + + str_array[i] = _dbus_strdup (s); + if (str_array[i] == NULL) + { + dbus_free_string_array (str_array); + _DBUS_SET_OOM (error); + goto out; + } + + ++i; + } + while (_dbus_type_reader_next (&array)); + } + + *str_array_p = str_array; + *n_elements_p = i; + } +#ifndef DBUS_DISABLE_CHECKS + else + { + _dbus_warn ("you can't read arrays of container types (struct, variant, array) with %s for now\n", + _DBUS_FUNCTION_NAME); + goto out; + } +#endif + } +#ifndef DBUS_DISABLE_CHECKS + else + { + _dbus_warn ("you can only read arrays and basic types with %s for now\n", + _DBUS_FUNCTION_NAME); + goto out; + } +#endif + + spec_type = va_arg (var_args, int); + if (!_dbus_type_reader_next (&real->u.reader) && spec_type != DBUS_TYPE_INVALID) + { + dbus_set_error (error, DBUS_ERROR_INVALID_ARGS, + "Message has only %d arguments, but more were expected", i); + goto out; + } + + i++; } - dbus_message_iter_append_done (real); - - return TRUE; + retval = TRUE; + + out: + + return retval; } /** - * Appends an object path to the message. + * Initializes a #DBusMessageIter for appending arguments to the end + * of a message. * - * @todo add return_val_if_fail(UTF-8 is valid) + * @todo If appending any of the arguments fails due to lack of + * memory, generally the message is hosed and you have to start over + * building the whole message. * - * @param iter an iterator pointing to the end of the message - * @param value the object path - * @returns #TRUE on success + * @param message the message + * @param iter pointer to an iterator to initialize */ -dbus_bool_t -dbus_message_iter_append_object_path (DBusMessageIter *iter, - const char *value) +void +dbus_message_append_iter_init (DBusMessage *message, + DBusMessageIter *iter) { DBusMessageRealIter *real = (DBusMessageRealIter *)iter; - _dbus_return_val_if_fail (dbus_message_iter_append_check (real), FALSE); - - if (!dbus_message_iter_append_type (real, DBUS_TYPE_OBJECT_PATH)) - return FALSE; - - if (!_dbus_marshal_string (&real->message->body, real->message->byte_order, value)) - { - _dbus_string_set_length (&real->message->body, real->pos); - return FALSE; - } + _dbus_return_if_fail (message != NULL); + _dbus_return_if_fail (iter != NULL); - dbus_message_iter_append_done (real); - - return TRUE; + _dbus_message_iter_init_common (message, real, + DBUS_MESSAGE_ITER_TYPE_WRITER); + + /* We create the signature string and point iterators at it "on demand" + * when a value is actually appended. That means that init() never fails + * due to OOM. + */ + _dbus_type_writer_init_types_delayed (&real->u.writer, + message->byte_order, + &message->body, + _dbus_string_get_length (&message->body)); } /** - * Appends a custom type data chunk to the message. A custom - * type is simply an arbitrary UTF-8 string used as a type - * tag, plus an array of arbitrary bytes to be interpreted - * according to the type tag. - * - * @param iter an iterator pointing to the end of the message - * @param name the name of the type - * @param data the binary data used to store the value - * @param len the length of the binary data in bytes - * @returns #TRUE on success + * Creates a temporary signature string containing the current + * signature, stores it in the iterator, and points the iterator to + * the end of it. Used any time we write to the message. + * + * @param real an iterator without a type_str + * @returns #FALSE if no memory */ -dbus_bool_t -dbus_message_iter_append_custom (DBusMessageIter *iter, - const char *name, - const unsigned char *data, - int len) +static dbus_bool_t +_dbus_message_iter_open_signature (DBusMessageRealIter *real) { - DBusMessageRealIter *real = (DBusMessageRealIter *)iter; + DBusString *str; + const DBusString *current_sig; + int current_sig_pos; + + _dbus_assert (real->iter_type == DBUS_MESSAGE_ITER_TYPE_WRITER); - _dbus_return_val_if_fail (dbus_message_iter_append_check (real), FALSE); + if (real->u.writer.type_str != NULL) + { + _dbus_assert (real->sig_refcount > 0); + real->sig_refcount += 1; + return TRUE; + } - if (!dbus_message_iter_append_type (real, DBUS_TYPE_CUSTOM)) + str = dbus_new (DBusString, 1); + if (str == NULL) return FALSE; - - if (!_dbus_marshal_string (&real->message->body, real->message->byte_order, name)) + + if (!_dbus_header_get_field_raw (&real->message->header, + DBUS_HEADER_FIELD_SIGNATURE, + ¤t_sig, ¤t_sig_pos)) + current_sig = NULL; + + if (current_sig) { - _dbus_string_set_length (&real->message->body, real->pos); - return FALSE; + int current_len; + + current_len = _dbus_string_get_byte (current_sig, current_sig_pos); + current_sig_pos += 1; /* move on to sig data */ + + if (!_dbus_string_init_preallocated (str, current_len + 4)) + { + dbus_free (str); + return FALSE; + } + + if (!_dbus_string_copy_len (current_sig, current_sig_pos, current_len, + str, 0)) + { + _dbus_string_free (str); + dbus_free (str); + return FALSE; + } } - - if (!_dbus_marshal_byte_array (&real->message->body, real->message->byte_order, data, len)) + else { - _dbus_string_set_length (&real->message->body, real->pos); - return FALSE; + if (!_dbus_string_init_preallocated (str, 4)) + { + dbus_free (str); + return FALSE; + } } - dbus_message_iter_append_done (real); - + real->sig_refcount = 1; + + _dbus_type_writer_add_types (&real->u.writer, + str, _dbus_string_get_length (str)); return TRUE; } - /** - * Appends a dict key name to the message. The iterator used - * must point to a dict. + * Sets the new signature as the message signature, frees the + * signature string, and marks the iterator as not having a type_str + * anymore. Frees the signature even if it fails, so you can't + * really recover from failure. Kinda busted. * - * @param iter an iterator pointing to the end of the message - * @param value the string - * @returns #TRUE on success + * @param real an iterator without a type_str + * @returns #FALSE if no memory */ -dbus_bool_t -dbus_message_iter_append_dict_key (DBusMessageIter *iter, - const char *value) +static dbus_bool_t +_dbus_message_iter_close_signature (DBusMessageRealIter *real) { - DBusMessageRealIter *real = (DBusMessageRealIter *)iter; + DBusString *str; + const char *v_STRING; + dbus_bool_t retval; - _dbus_return_val_if_fail (dbus_message_iter_append_check (real), FALSE); - _dbus_assert (real->type == DBUS_MESSAGE_ITER_TYPE_DICT); - - if (real->wrote_dict_key) - { - _dbus_warn ("Appending multiple dict key names\n"); - return FALSE; - } - - if (!_dbus_marshal_string (&real->message->body, real->message->byte_order, value)) - { - return FALSE; - } + _dbus_assert (real->iter_type == DBUS_MESSAGE_ITER_TYPE_WRITER); + _dbus_assert (real->u.writer.type_str != NULL); + _dbus_assert (real->sig_refcount > 0); - dbus_message_iter_append_done (real); - real->wrote_dict_key = TRUE; - - return TRUE; -} + real->sig_refcount -= 1; -static dbus_bool_t -array_iter_type_mark_done (DBusMessageRealIter *iter) -{ - int len_pos; - - if (iter->type == DBUS_MESSAGE_ITER_TYPE_ARRAY) - array_iter_type_mark_done (iter->parent_iter); - else + if (real->sig_refcount > 0) return TRUE; + _dbus_assert (real->sig_refcount == 0); - len_pos = _DBUS_ALIGN_VALUE (_dbus_string_get_length (&iter->message->body), - sizeof (dbus_uint32_t)); + retval = TRUE; - /* Empty length for now, backfill later */ - if (!_dbus_marshal_uint32 (&iter->message->body, iter->message->byte_order, 0)) - { - _dbus_string_set_length (&iter->message->body, iter->pos); - return FALSE; - } + str = real->u.writer.type_str; - iter->container_start = _dbus_string_get_length (&iter->message->body); - iter->container_length_pos = len_pos; - iter->array_type_done = TRUE; + v_STRING = _dbus_string_get_const_data (str); + if (!_dbus_header_set_field_basic (&real->message->header, + DBUS_HEADER_FIELD_SIGNATURE, + DBUS_TYPE_SIGNATURE, + &v_STRING)) + retval = FALSE; - return TRUE; + _dbus_type_writer_remove_types (&real->u.writer); + _dbus_string_free (str); + dbus_free (str); + + return retval; } +#ifndef DBUS_DISABLE_CHECKS static dbus_bool_t -append_array_type (DBusMessageRealIter *real, - int element_type, - dbus_bool_t *array_type_done, - int *array_type_pos) +_dbus_message_iter_append_check (DBusMessageRealIter *iter) { - int existing_element_type; - - if (!dbus_message_iter_append_type (real, DBUS_TYPE_ARRAY)) + if (!_dbus_message_iter_check (iter)) return FALSE; - - if (real->type == DBUS_MESSAGE_ITER_TYPE_ARRAY && - real->array_type_done) - { - existing_element_type = iter_get_array_type (real, array_type_pos); - if (existing_element_type != element_type) - { - _dbus_warn ("Appending array of %s, when expecting array of %s\n", - _dbus_type_to_string (element_type), - _dbus_type_to_string (existing_element_type)); - _dbus_string_set_length (&real->message->body, real->pos); - return FALSE; - } - if (array_type_done != NULL) - *array_type_done = TRUE; - } - else - { - if (array_type_pos != NULL) - *array_type_pos = _dbus_string_get_length (&real->message->body); - - if (!_dbus_message_append_byte_to_signature (real->message, element_type)) - { - _dbus_string_set_length (&real->message->body, real->pos); - return FALSE; - } - - /* Append element type */ - if (!_dbus_string_append_byte (&real->message->body, element_type)) - { - _dbus_message_remove_byte_from_signature (real->message); - _dbus_string_set_length (&real->message->body, real->pos); - return FALSE; - } - - if (array_type_done != NULL) - *array_type_done = element_type != DBUS_TYPE_ARRAY; - - if (element_type != DBUS_TYPE_ARRAY && - !array_iter_type_mark_done (real)) - { - _dbus_message_remove_byte_from_signature (real->message); - return FALSE; - } + if (iter->message->locked) + { + _dbus_warn ("dbus append iterator can't be used: message is locked (has already been sent)\n"); + return FALSE; } return TRUE; } +#endif /* DBUS_DISABLE_CHECKS */ /** - * Appends an array to the message and initializes an iterator that - * can be used to append to the array. + * Appends a basic-typed value to the message. The basic types are the + * non-container types such as integer and string. * - * @param iter an iterator pointing to the end of the message - * @param array_iter pointer to an iter that will be initialized - * @param element_type the type of the array elements - * @returns #TRUE on success + * The "value" argument should be the address of a basic-typed value. + * So for string, const char**. For integer, dbus_int32_t*. + * + * @todo If this fails due to lack of memory, the message is hosed and + * you have to start over building the whole message. + * + * @param iter the append iterator + * @param type the type of the value + * @param value the address of the value + * @returns #FALSE if not enough memory */ dbus_bool_t -dbus_message_iter_append_array (DBusMessageIter *iter, - DBusMessageIter *array_iter, - int element_type) +dbus_message_iter_append_basic (DBusMessageIter *iter, + int type, + const void *value) { DBusMessageRealIter *real = (DBusMessageRealIter *)iter; - DBusMessageRealIter *array_real = (DBusMessageRealIter *)array_iter; - int len_pos; - int array_type_pos; - dbus_bool_t array_type_done; + dbus_bool_t ret; - if (element_type == DBUS_TYPE_NIL) - { - _dbus_warn ("Can't create NIL arrays\n"); - return FALSE; - } - - _dbus_return_val_if_fail (dbus_message_iter_append_check (real), FALSE); + _dbus_return_val_if_fail (_dbus_message_iter_append_check (real), FALSE); + _dbus_return_val_if_fail (real->iter_type == DBUS_MESSAGE_ITER_TYPE_WRITER, FALSE); + _dbus_return_val_if_fail (_dbus_type_is_basic (type), FALSE); + _dbus_return_val_if_fail (value != NULL, FALSE); - if (!append_array_type (real, element_type, &array_type_done, &array_type_pos)) + if (!_dbus_message_iter_open_signature (real)) return FALSE; - len_pos = _DBUS_ALIGN_VALUE (_dbus_string_get_length (&real->message->body), sizeof (dbus_uint32_t)); + ret = _dbus_type_writer_write_basic (&real->u.writer, type, value); - if (array_type_done) - { - /* Empty length for now, backfill later */ - if (!_dbus_marshal_uint32 (&real->message->body, real->message->byte_order, 0)) - { - _dbus_string_set_length (&real->message->body, real->pos); - return FALSE; - } - } - - array_real->parent_iter = real; - array_real->message = real->message; - array_real->changed_stamp = real->message->changed_stamp; - - array_real->type = DBUS_MESSAGE_ITER_TYPE_ARRAY; - array_real->pos = _dbus_string_get_length (&real->message->body); - array_real->end = array_real->end; - - array_real->container_start = array_real->pos; - array_real->container_length_pos = len_pos; - array_real->wrote_dict_key = 0; - array_real->array_type_done = array_type_done; - array_real->array_type_pos = array_type_pos; - - dbus_message_iter_append_done (array_real); - - return TRUE; + if (!_dbus_message_iter_close_signature (real)) + ret = FALSE; + + return ret; } /** - * Appends a dict to the message and initializes an iterator that - * can be used to append to the dict. + * Appends a block of fixed-length values to an array. The + * fixed-length types are all basic types that are not string-like. So + * int32, double, bool, etc. You must call + * dbus_message_iter_open_container() to open an array of values + * before calling this function. You may call this function multiple + * times (and intermixed with calls to + * dbus_message_iter_append_basic()) for the same array. * - * @param iter an iterator pointing to the end of the message - * @param dict_iter pointer to an iter that will be initialized - * @returns #TRUE on success + * The "value" argument should be the address of the array. So for + * integer, "dbus_int32_t**" is expected for example. + * + * @warning in C, given "int array[]", "&array == array" (the + * comp.lang.c FAQ says otherwise, but gcc and the FAQ don't agree). + * So if you're using an array instead of a pointer you have to create + * a pointer variable, assign the array to it, then take the address + * of the pointer variable. + * @code + * const dbus_int32_t array[] = { 1, 2, 3 }; + * const dbus_int32_t *v_ARRAY = array; + * if (!dbus_message_iter_append_fixed_array (&iter, DBUS_TYPE_INT32, &v_ARRAY, 3)) + * fprintf (stderr, "No memory!\n"); + * @endcode + * For strings it works to write const char *array = "Hello" and then + * use &array though. + * + * @todo If this fails due to lack of memory, the message is hosed and + * you have to start over building the whole message. + * + * @param iter the append iterator + * @param type the type of the array elements + * @param value the address of the array + * @param n_elements the number of elements to append + * @returns #FALSE if not enough memory */ dbus_bool_t -dbus_message_iter_append_dict (DBusMessageIter *iter, - DBusMessageIter *dict_iter) +dbus_message_iter_append_fixed_array (DBusMessageIter *iter, + int element_type, + const void *value, + int n_elements) { DBusMessageRealIter *real = (DBusMessageRealIter *)iter; - DBusMessageRealIter *dict_real = (DBusMessageRealIter *)dict_iter; - int len_pos; - - _dbus_return_val_if_fail (dbus_message_iter_append_check (real), FALSE); - - if (!dbus_message_iter_append_type (real, DBUS_TYPE_DICT)) - return FALSE; - - len_pos = _DBUS_ALIGN_VALUE (_dbus_string_get_length (&real->message->body), sizeof (dbus_uint32_t)); - - /* Empty length for now, backfill later */ - if (!_dbus_marshal_uint32 (&real->message->body, real->message->byte_order, 0)) - { - _dbus_string_set_length (&real->message->body, real->pos); - return FALSE; - } - - dict_real->parent_iter = real; - dict_real->message = real->message; - dict_real->changed_stamp = real->message->changed_stamp; - - dict_real->type = DBUS_MESSAGE_ITER_TYPE_DICT; - dict_real->pos = _dbus_string_get_length (&real->message->body); - dict_real->end = dict_real->end; - - dict_real->container_start = dict_real->pos; - dict_real->container_length_pos = len_pos; - dict_real->wrote_dict_key = 0; - - dbus_message_iter_append_done (dict_real); - - real->wrote_dict_key = FALSE; - - return TRUE; -} - -static dbus_bool_t -_dbus_message_iter_append_basic_array (DBusMessageIter *iter, - char type, - const void *value, - int len) + dbus_bool_t ret; + + _dbus_return_val_if_fail (_dbus_message_iter_append_check (real), FALSE); + _dbus_return_val_if_fail (real->iter_type == DBUS_MESSAGE_ITER_TYPE_WRITER, FALSE); + _dbus_return_val_if_fail (_dbus_type_is_fixed (element_type), FALSE); + _dbus_return_val_if_fail (real->u.writer.container_type == DBUS_TYPE_ARRAY, FALSE); + _dbus_return_val_if_fail (value != NULL, FALSE); + _dbus_return_val_if_fail (n_elements >= 0, FALSE); + _dbus_return_val_if_fail (n_elements <= + DBUS_MAXIMUM_ARRAY_LENGTH / _dbus_type_get_alignment (element_type), + FALSE); + + ret = _dbus_type_writer_write_fixed_multi (&real->u.writer, element_type, value, n_elements); + + return ret; +} + +/** + * Appends a container-typed value to the message; you are required to + * append the contents of the container using the returned + * sub-iterator, and then call + * dbus_message_iter_close_container(). Container types are for + * example struct, variant, and array. For variants, the + * contained_signature should be the type of the single value inside + * the variant. For structs, contained_signature should be #NULL; it + * will be set to whatever types you write into the struct. For + * arrays, contained_signature should be the type of the array + * elements. + * + * @todo If this fails due to lack of memory, the message is hosed and + * you have to start over building the whole message. + * + * @param iter the append iterator + * @param type the type of the value + * @param contained_signature the type of container contents + * @param sub sub-iterator to initialize + * @returns #FALSE if not enough memory + */ +dbus_bool_t +dbus_message_iter_open_container (DBusMessageIter *iter, + int type, + const char *contained_signature, + DBusMessageIter *sub) { DBusMessageRealIter *real = (DBusMessageRealIter *)iter; - - _dbus_return_val_if_fail (dbus_message_iter_append_check (real), FALSE); - - if (!append_array_type (real, type, NULL, NULL)) + DBusMessageRealIter *real_sub = (DBusMessageRealIter *)sub; + DBusString contained_str; + + _dbus_return_val_if_fail (_dbus_message_iter_append_check (real), FALSE); + _dbus_return_val_if_fail (real->iter_type == DBUS_MESSAGE_ITER_TYPE_WRITER, FALSE); + _dbus_return_val_if_fail (_dbus_type_is_container (type), FALSE); + _dbus_return_val_if_fail (sub != NULL, FALSE); + _dbus_return_val_if_fail ((type == DBUS_TYPE_STRUCT && + contained_signature == NULL) || + contained_signature != NULL, FALSE); + + if (!_dbus_message_iter_open_signature (real)) return FALSE; - - if (!_dbus_marshal_basic_type_array (&real->message->body, - type, value, len, - real->message->byte_order)) - { - _dbus_string_set_length (&real->message->body, real->pos); - return FALSE; - } - dbus_message_iter_append_done (real); - - return TRUE; + _dbus_string_init_const (&contained_str, contained_signature); + + *real_sub = *real; + return _dbus_type_writer_recurse (&real->u.writer, + type, + &contained_str, 0, + &real_sub->u.writer); } /** - * This function takes a va_list for use by language bindings. - * It's otherwise the same as dbus_message_append_args(). + * Closes a container-typed value appended to the message; may write + * out more information to the message known only after the entire + * container is written, and may free resources created by + * dbus_message_iter_open_container(). * - * @todo: Shouldn't this function clean up the changes to the message - * on failures? (Yes) - - * @see dbus_message_append_args. - * @param message the message - * @param first_arg_type type of first argument - * @param var_args value of first argument, then list of type/value pairs - * @returns #TRUE on success + * @todo If this fails due to lack of memory, the message is hosed and + * you have to start over building the whole message. + * + * @param iter the append iterator + * @param sub sub-iterator to close + * @returns #FALSE if not enough memory */ dbus_bool_t -dbus_message_append_args_valist (DBusMessage *message, - int first_arg_type, - va_list var_args) +dbus_message_iter_close_container (DBusMessageIter *iter, + DBusMessageIter *sub) { - int type, old_len; - DBusMessageIter iter; + DBusMessageRealIter *real = (DBusMessageRealIter *)iter; + DBusMessageRealIter *real_sub = (DBusMessageRealIter *)sub; + dbus_bool_t ret; - _dbus_return_val_if_fail (message != NULL, FALSE); - - old_len = _dbus_string_get_length (&message->body); - - type = first_arg_type; + _dbus_return_val_if_fail (_dbus_message_iter_append_check (real), FALSE); + _dbus_return_val_if_fail (real->iter_type == DBUS_MESSAGE_ITER_TYPE_WRITER, FALSE); + _dbus_return_val_if_fail (_dbus_message_iter_append_check (real_sub), FALSE); + _dbus_return_val_if_fail (real_sub->iter_type == DBUS_MESSAGE_ITER_TYPE_WRITER, FALSE); - dbus_message_append_iter_init (message, &iter); - - while (type != DBUS_TYPE_INVALID) - { - switch (type) - { - case DBUS_TYPE_NIL: - if (!dbus_message_iter_append_nil (&iter)) - goto errorout; - break; - case DBUS_TYPE_BYTE: - /* Read an int from varargs, because the original unsigned - * char has been promoted to int. */ - if (!dbus_message_iter_append_byte (&iter, va_arg (var_args, int))) - goto errorout; - break; - case DBUS_TYPE_BOOLEAN: - if (!dbus_message_iter_append_boolean (&iter, va_arg (var_args, dbus_bool_t))) - goto errorout; - break; - case DBUS_TYPE_INT32: - /* FIXME this is probably wrong, because an int passed in probably gets - * converted to plain "int" not necessarily 32-bit. - */ - if (!dbus_message_iter_append_int32 (&iter, va_arg (var_args, dbus_int32_t))) - goto errorout; - break; - case DBUS_TYPE_UINT32: - /* FIXME this is probably wrong, because an int passed in probably gets - * converted to plain "int" not necessarily 32-bit. - */ - if (!dbus_message_iter_append_uint32 (&iter, va_arg (var_args, dbus_uint32_t))) - goto errorout; - break; -#ifdef DBUS_HAVE_INT64 - case DBUS_TYPE_INT64: - if (!dbus_message_iter_append_int64 (&iter, va_arg (var_args, dbus_int64_t))) - goto errorout; - break; - case DBUS_TYPE_UINT64: - if (!dbus_message_iter_append_uint64 (&iter, va_arg (var_args, dbus_uint64_t))) - goto errorout; - break; -#endif /* DBUS_HAVE_INT64 */ - case DBUS_TYPE_DOUBLE: - if (!dbus_message_iter_append_double (&iter, va_arg (var_args, double))) - goto errorout; - break; - case DBUS_TYPE_STRING: - if (!dbus_message_iter_append_string (&iter, va_arg (var_args, const char *))) - goto errorout; - break; - case DBUS_TYPE_OBJECT_PATH: - if (!dbus_message_iter_append_object_path (&iter, va_arg (var_args, const char*))) - goto errorout; - break; - case DBUS_TYPE_CUSTOM: - { - const char *name; - unsigned char *data; - int len; - - name = va_arg (var_args, const char *); - data = va_arg (var_args, unsigned char *); - len = va_arg (var_args, int); - - if (!dbus_message_iter_append_custom (&iter, name, data, len)) - goto errorout; - break; - } - case DBUS_TYPE_ARRAY: - { - void *data; - int len, type; - - type = va_arg (var_args, int); - data = va_arg (var_args, void *); - len = va_arg (var_args, int); - - switch (type) - { - case DBUS_TYPE_BYTE: - case DBUS_TYPE_BOOLEAN: - case DBUS_TYPE_INT32: - case DBUS_TYPE_UINT32: -#ifdef DBUS_HAVE_INT64 - case DBUS_TYPE_INT64: - case DBUS_TYPE_UINT64: -#endif /* DBUS_HAVE_INT64 */ - case DBUS_TYPE_DOUBLE: - if (!_dbus_message_iter_append_basic_array (&iter, type, data, len)) - goto errorout; - break; - case DBUS_TYPE_STRING: - if (!dbus_message_iter_append_string_array (&iter, (const char **)data, len)) - goto errorout; - break; - case DBUS_TYPE_OBJECT_PATH: - if (!dbus_message_iter_append_object_path_array (&iter, (const char **)data, len)) - goto errorout; - break; - case DBUS_TYPE_NIL: - case DBUS_TYPE_ARRAY: - case DBUS_TYPE_CUSTOM: - case DBUS_TYPE_DICT: - _dbus_warn ("dbus_message_append_args_valist doesn't support recursive arrays\n"); - goto errorout; - default: - _dbus_warn ("Unknown field type %d\n", type); - goto errorout; - } - } - break; - - case DBUS_TYPE_DICT: - _dbus_warn ("dbus_message_append_args_valist doesn't support dicts\n"); - goto errorout; - default: - _dbus_warn ("Unknown field type %d\n", type); - goto errorout; - } + ret = _dbus_type_writer_unrecurse (&real->u.writer, + &real_sub->u.writer); - type = va_arg (var_args, int); - } + if (!_dbus_message_iter_close_signature (real)) + ret = FALSE; - return TRUE; + return ret; +} - errorout: - return FALSE; +/** + * Sets a flag indicating that the message does not want a reply; if + * this flag is set, the other end of the connection may (but is not + * required to) optimize by not sending method return or error + * replies. If this flag is set, there is no way to know whether the + * message successfully arrived at the remote end. Normally you know a + * message was received when you receive the reply to it. + * + * @param message the message + * @param no_reply #TRUE if no reply is desired + */ +void +dbus_message_set_no_reply (DBusMessage *message, + dbus_bool_t no_reply) +{ + _dbus_return_if_fail (message != NULL); + _dbus_return_if_fail (!message->locked); + + _dbus_header_toggle_flag (&message->header, + DBUS_HEADER_FLAG_NO_REPLY_EXPECTED, + no_reply); } /** - * Appends a boolean array to the message. + * Returns #TRUE if the message does not expect + * a reply. * - * @param iter an iterator pointing to the end of the message - * @param value the array - * @param len the length of the array - * @returns #TRUE on success + * @param message the message + * @returns #TRUE if the message sender isn't waiting for a reply */ dbus_bool_t -dbus_message_iter_append_boolean_array (DBusMessageIter *iter, - unsigned const char *value, - int len) +dbus_message_get_no_reply (DBusMessage *message) { - return _dbus_message_iter_append_basic_array (iter, DBUS_TYPE_BOOLEAN, - value, len); + _dbus_return_val_if_fail (message != NULL, FALSE); + + return _dbus_header_get_flag (&message->header, + DBUS_HEADER_FLAG_NO_REPLY_EXPECTED); } /** - * Appends a 32 bit signed integer array to the message. + * Sets a flag indicating that the addressed service will be + * auto-activated before the message is delivered. When this flag is + * set, the message is held until the service is succesfully activated + * or fails to activate. In case of failure, the reply will be an + * activation error. If this flag is not set (the default * - * @param iter an iterator pointing to the end of the message - * @param value the array - * @param len the length of the array - * @returns #TRUE on success + * @param message the message + * @param auto_activation #TRUE if auto-activation is desired */ -dbus_bool_t -dbus_message_iter_append_int32_array (DBusMessageIter *iter, - const dbus_int32_t *value, - int len) +void +dbus_message_set_auto_activation (DBusMessage *message, + dbus_bool_t auto_activation) { - return _dbus_message_iter_append_basic_array (iter, DBUS_TYPE_INT32, - value, len); + _dbus_return_if_fail (message != NULL); + _dbus_return_if_fail (!message->locked); + + _dbus_header_toggle_flag (&message->header, + DBUS_HEADER_FLAG_AUTO_ACTIVATION, + auto_activation); } /** - * Appends a 32 bit unsigned integer array to the message. + * Returns #TRUE if the message will cause the addressed service to be + * auto-activated. * - * @param iter an iterator pointing to the end of the message - * @param value the array - * @param len the length of the array - * @returns #TRUE on success + * @param message the message + * @returns #TRUE if the message will use auto-activation */ dbus_bool_t -dbus_message_iter_append_uint32_array (DBusMessageIter *iter, - const dbus_uint32_t *value, - int len) +dbus_message_get_auto_activation (DBusMessage *message) { - return _dbus_message_iter_append_basic_array (iter, DBUS_TYPE_UINT32, - value, len); + _dbus_return_val_if_fail (message != NULL, FALSE); + + return _dbus_header_get_flag (&message->header, + DBUS_HEADER_FLAG_AUTO_ACTIVATION); } -#ifdef DBUS_HAVE_INT64 /** - * Appends a 64 bit signed integer array to the message. - * - * This function only exists if #DBUS_HAVE_INT64 is defined. + * Sets the object path this message is being sent to (for + * DBUS_MESSAGE_TYPE_METHOD_CALL) or the one a signal is being + * emitted from (for DBUS_MESSAGE_TYPE_SIGNAL). * - * @param iter an iterator pointing to the end of the message - * @param value the array - * @param len the length of the array - * @returns #TRUE on success + * @param message the message + * @param object_path the path or #NULL to unset + * @returns #FALSE if not enough memory */ dbus_bool_t -dbus_message_iter_append_int64_array (DBusMessageIter *iter, - const dbus_int64_t *value, - int len) +dbus_message_set_path (DBusMessage *message, + const char *object_path) { - return _dbus_message_iter_append_basic_array (iter, DBUS_TYPE_INT64, - value, len); + _dbus_return_val_if_fail (message != NULL, FALSE); + _dbus_return_val_if_fail (!message->locked, FALSE); + _dbus_return_val_if_fail (object_path == NULL || + _dbus_check_is_valid_path (object_path), + FALSE); + + return set_or_delete_string_field (message, + DBUS_HEADER_FIELD_PATH, + DBUS_TYPE_OBJECT_PATH, + object_path); } /** - * Appends a 64 bit unsigned integer array to the message. + * Gets the object path this message is being sent to (for + * DBUS_MESSAGE_TYPE_METHOD_CALL) or being emitted from (for + * DBUS_MESSAGE_TYPE_SIGNAL). Returns #NULL if none. * - * This function only exists if #DBUS_HAVE_INT64 is defined. - * - * @param iter an iterator pointing to the end of the message - * @param value the array - * @param len the length of the array - * @returns #TRUE on success + * @param message the message + * @returns the path (should not be freed) or #NULL */ -dbus_bool_t -dbus_message_iter_append_uint64_array (DBusMessageIter *iter, - const dbus_uint64_t *value, - int len) +const char* +dbus_message_get_path (DBusMessage *message) { - return _dbus_message_iter_append_basic_array (iter, DBUS_TYPE_UINT64, - value, len); + const char *v; + + _dbus_return_val_if_fail (message != NULL, NULL); + + v = NULL; /* in case field doesn't exist */ + _dbus_header_get_field_basic (&message->header, + DBUS_HEADER_FIELD_PATH, + DBUS_TYPE_OBJECT_PATH, + &v); + return v; } -#endif /* DBUS_HAVE_INT64 */ /** - * Appends a double array to the message. + * Gets the object path this message is being sent to + * (for DBUS_MESSAGE_TYPE_METHOD_CALL) or being emitted + * from (for DBUS_MESSAGE_TYPE_SIGNAL) in a decomposed + * format (one array element per path component). + * Free the returned array with dbus_free_string_array(). * - * @param iter an iterator pointing to the end of the message - * @param value the array - * @param len the length of the array - * @returns #TRUE on success + * An empty but non-NULL path array means the path "/". + * So the path "/foo/bar" becomes { "foo", "bar", NULL } + * and the path "/" becomes { NULL }. + * + * @todo this could be optimized by using the len from the message + * instead of calling strlen() again + * + * @param message the message + * @param path place to store allocated array of path components; #NULL set here if no path field exists + * @returns #FALSE if no memory to allocate the array */ dbus_bool_t -dbus_message_iter_append_double_array (DBusMessageIter *iter, - const double *value, - int len) +dbus_message_get_path_decomposed (DBusMessage *message, + char ***path) { - return _dbus_message_iter_append_basic_array (iter, DBUS_TYPE_DOUBLE, - value, len); + const char *v; + + _dbus_return_val_if_fail (message != NULL, FALSE); + _dbus_return_val_if_fail (path != NULL, FALSE); + + *path = NULL; + + v = dbus_message_get_path (message); + if (v != NULL) + { + if (!_dbus_decompose_path (v, strlen (v), + path, NULL)) + return FALSE; + } + return TRUE; } /** - * Appends a byte array to the message. + * Sets the interface this message is being sent to + * (for DBUS_MESSAGE_TYPE_METHOD_CALL) or + * the interface a signal is being emitted from + * (for DBUS_MESSAGE_TYPE_SIGNAL). * - * @param iter an iterator pointing to the end of the message - * @param value the array - * @param len the length of the array - * @returns #TRUE on success + * @param message the message + * @param interface the interface or #NULL to unset + * @returns #FALSE if not enough memory */ dbus_bool_t -dbus_message_iter_append_byte_array (DBusMessageIter *iter, - unsigned const char *value, - int len) +dbus_message_set_interface (DBusMessage *message, + const char *interface) { - return _dbus_message_iter_append_basic_array (iter, DBUS_TYPE_BYTE, - value, len); + _dbus_return_val_if_fail (message != NULL, FALSE); + _dbus_return_val_if_fail (!message->locked, FALSE); + _dbus_return_val_if_fail (interface == NULL || + _dbus_check_is_valid_interface (interface), + FALSE); + + return set_or_delete_string_field (message, + DBUS_HEADER_FIELD_INTERFACE, + DBUS_TYPE_STRING, + interface); } /** - * Appends a string array to the message. + * Gets the interface this message is being sent to + * (for DBUS_MESSAGE_TYPE_METHOD_CALL) or being emitted + * from (for DBUS_MESSAGE_TYPE_SIGNAL). + * The interface name is fully-qualified (namespaced). + * Returns #NULL if none. * - * @param iter an iterator pointing to the end of the message - * @param value the array - * @param len the length of the array - * @returns #TRUE on success + * @param message the message + * @returns the message interface (should not be freed) or #NULL */ -dbus_bool_t -dbus_message_iter_append_string_array (DBusMessageIter *iter, - const char **value, - int len) +const char* +dbus_message_get_interface (DBusMessage *message) { - DBusMessageRealIter *real = (DBusMessageRealIter *)iter; + const char *v; - _dbus_return_val_if_fail (dbus_message_iter_append_check (real), FALSE); + _dbus_return_val_if_fail (message != NULL, NULL); - if (!append_array_type (real, DBUS_TYPE_STRING, NULL, NULL)) - return FALSE; - - if (!_dbus_marshal_string_array (&real->message->body, real->message->byte_order, value, len)) - { - _dbus_string_set_length (&real->message->body, real->pos); - return FALSE; - } + v = NULL; /* in case field doesn't exist */ + _dbus_header_get_field_basic (&message->header, + DBUS_HEADER_FIELD_INTERFACE, + DBUS_TYPE_STRING, + &v); + return v; +} + +/** + * Sets the interface member being invoked + * (DBUS_MESSAGE_TYPE_METHOD_CALL) or emitted + * (DBUS_MESSAGE_TYPE_SIGNAL). + * The interface name is fully-qualified (namespaced). + * + * @param message the message + * @param member the member or #NULL to unset + * @returns #FALSE if not enough memory + */ +dbus_bool_t +dbus_message_set_member (DBusMessage *message, + const char *member) +{ + _dbus_return_val_if_fail (message != NULL, FALSE); + _dbus_return_val_if_fail (!message->locked, FALSE); + _dbus_return_val_if_fail (member == NULL || + _dbus_check_is_valid_member (member), + FALSE); - dbus_message_iter_append_done (real); - - return TRUE; + return set_or_delete_string_field (message, + DBUS_HEADER_FIELD_MEMBER, + DBUS_TYPE_STRING, + member); } /** - * Appends an object path array to the message. + * Gets the interface member being invoked + * (DBUS_MESSAGE_TYPE_METHOD_CALL) or emitted + * (DBUS_MESSAGE_TYPE_SIGNAL). Returns #NULL if none. * - * @param iter an iterator pointing to the end of the message - * @param value the array - * @param len the length of the array - * @returns #TRUE on success + * @param message the message + * @returns the member name (should not be freed) or #NULL */ -dbus_bool_t -dbus_message_iter_append_object_path_array (DBusMessageIter *iter, - const char **value, - int len) +const char* +dbus_message_get_member (DBusMessage *message) { - DBusMessageRealIter *real = (DBusMessageRealIter *)iter; - - _dbus_return_val_if_fail (dbus_message_iter_append_check (real), FALSE); + const char *v; - if (!append_array_type (real, DBUS_TYPE_OBJECT_PATH, NULL, NULL)) - return FALSE; - - if (!_dbus_marshal_string_array (&real->message->body, real->message->byte_order, value, len)) - { - _dbus_string_set_length (&real->message->body, real->pos); - return FALSE; - } + _dbus_return_val_if_fail (message != NULL, NULL); - dbus_message_iter_append_done (real); - - return TRUE; + v = NULL; /* in case field doesn't exist */ + _dbus_header_get_field_basic (&message->header, + DBUS_HEADER_FIELD_MEMBER, + DBUS_TYPE_STRING, + &v); + return v; } /** - * Sets the message sender. + * Sets the name of the error (DBUS_MESSAGE_TYPE_ERROR). + * The name is fully-qualified (namespaced). * * @param message the message - * @param sender the sender or #NULL to unset + * @param error_name the name or #NULL to unset * @returns #FALSE if not enough memory */ dbus_bool_t -dbus_message_set_sender (DBusMessage *message, - const char *sender) +dbus_message_set_error_name (DBusMessage *message, + const char *error_name) { _dbus_return_val_if_fail (message != NULL, FALSE); _dbus_return_val_if_fail (!message->locked, FALSE); + _dbus_return_val_if_fail (error_name == NULL || + _dbus_check_is_valid_error_name (error_name), + FALSE); - return set_string_field (message, - DBUS_HEADER_FIELD_SENDER, - DBUS_TYPE_STRING, - sender); + return set_or_delete_string_field (message, + DBUS_HEADER_FIELD_ERROR_NAME, + DBUS_TYPE_STRING, + error_name); } /** - * Sets a flag indicating that the message does not want a reply; if - * this flag is set, the other end of the connection may (but is not - * required to) optimize by not sending method return or error - * replies. If this flag is set, there is no way to know whether the - * message successfully arrived at the remote end. + * Gets the error name (DBUS_MESSAGE_TYPE_ERROR only) + * or #NULL if none. * * @param message the message - * @param no_reply #TRUE if no reply is desired + * @returns the error name (should not be freed) or #NULL */ -void -dbus_message_set_no_reply (DBusMessage *message, - dbus_bool_t no_reply) +const char* +dbus_message_get_error_name (DBusMessage *message) { - char *header; + const char *v; - _dbus_return_if_fail (message != NULL); - _dbus_return_if_fail (!message->locked); - - header = _dbus_string_get_data_len (&message->header, FLAGS_OFFSET, 1); - - if (no_reply) - *header |= DBUS_HEADER_FLAG_NO_REPLY_EXPECTED; - else - *header &= ~DBUS_HEADER_FLAG_NO_REPLY_EXPECTED; + _dbus_return_val_if_fail (message != NULL, NULL); + + v = NULL; /* in case field doesn't exist */ + _dbus_header_get_field_basic (&message->header, + DBUS_HEADER_FIELD_ERROR_NAME, + DBUS_TYPE_STRING, + &v); + return v; } /** - * Returns #TRUE if the message does not expect - * a reply. + * Sets the message's destination service. * * @param message the message - * @returns #TRUE if the message sender isn't waiting for a reply + * @param destination the destination service name or #NULL to unset + * @returns #FALSE if not enough memory */ dbus_bool_t -dbus_message_get_no_reply (DBusMessage *message) +dbus_message_set_destination (DBusMessage *message, + const char *destination) { - const char *header; - _dbus_return_val_if_fail (message != NULL, FALSE); - - header = _dbus_string_get_const_data_len (&message->header, FLAGS_OFFSET, 1); + _dbus_return_val_if_fail (!message->locked, FALSE); + _dbus_return_val_if_fail (destination == NULL || + _dbus_check_is_valid_service (destination), + FALSE); - return (*header & DBUS_HEADER_FLAG_NO_REPLY_EXPECTED) != 0; + return set_or_delete_string_field (message, + DBUS_HEADER_FIELD_DESTINATION, + DBUS_TYPE_STRING, + destination); } - /** - * Sets a flag indicating that the addressed service will be auto-activated - * before the message is delivered. When this flag is set, the message is held - * until the service is succesfully activated or fail to activate. In case of - * failure, the reply will be an activation error. + * Gets the destination service of a message or #NULL if there is + * none set. * * @param message the message - * @param auto_activation #TRUE if auto-activation is desired + * @returns the message destination service (should not be freed) or #NULL */ -void -dbus_message_set_auto_activation (DBusMessage *message, - dbus_bool_t auto_activation) +const char* +dbus_message_get_destination (DBusMessage *message) { - char *header; + const char *v; - _dbus_return_if_fail (message != NULL); - _dbus_return_if_fail (!message->locked); - - header = _dbus_string_get_data_len (&message->header, FLAGS_OFFSET, 1); - - if (auto_activation) - *header |= DBUS_HEADER_FLAG_AUTO_ACTIVATION; - else - *header &= ~DBUS_HEADER_FLAG_AUTO_ACTIVATION; + _dbus_return_val_if_fail (message != NULL, NULL); + + v = NULL; /* in case field doesn't exist */ + _dbus_header_get_field_basic (&message->header, + DBUS_HEADER_FIELD_DESTINATION, + DBUS_TYPE_STRING, + &v); + return v; } /** - * Returns #TRUE if the message will cause the addressed service to be - * auto-activated. + * Sets the message sender. * * @param message the message - * @returns #TRUE if the message will use auto-activation + * @param sender the sender or #NULL to unset + * @returns #FALSE if not enough memory */ dbus_bool_t -dbus_message_get_auto_activation (DBusMessage *message) +dbus_message_set_sender (DBusMessage *message, + const char *sender) { - const char *header; - _dbus_return_val_if_fail (message != NULL, FALSE); - - header = _dbus_string_get_const_data_len (&message->header, FLAGS_OFFSET, 1); + _dbus_return_val_if_fail (!message->locked, FALSE); + _dbus_return_val_if_fail (sender == NULL || + _dbus_check_is_valid_service (sender), + FALSE); - return (*header & DBUS_HEADER_FLAG_AUTO_ACTIVATION) != 0; + return set_or_delete_string_field (message, + DBUS_HEADER_FIELD_SENDER, + DBUS_TYPE_STRING, + sender); } /** @@ -4707,11 +2620,16 @@ dbus_message_get_auto_activation (DBusMessage *message) const char* dbus_message_get_sender (DBusMessage *message) { + const char *v; + _dbus_return_val_if_fail (message != NULL, NULL); - - return get_string_field (message, - DBUS_HEADER_FIELD_SENDER, - NULL); + + v = NULL; /* in case field doesn't exist */ + _dbus_header_get_field_basic (&message->header, + DBUS_HEADER_FIELD_SENDER, + DBUS_TYPE_STRING, + &v); + return v; } /** @@ -4722,49 +2640,52 @@ dbus_message_get_sender (DBusMessage *message) * what you might expect (it does not include the signature of the * entire C++-style method). * - * The signature is a string made up of type codes such - * as #DBUS_TYPE_STRING. The string is terminated with nul - * (nul is also the value of #DBUS_TYPE_INVALID). - * + * The signature is a string made up of type codes such as + * #DBUS_TYPE_INT32. The string is terminated with nul (nul is also + * the value of #DBUS_TYPE_INVALID). + * * @param message the message * @returns the type signature */ const char* dbus_message_get_signature (DBusMessage *message) { + const DBusString *type_str; + int type_pos; + _dbus_return_val_if_fail (message != NULL, NULL); - return get_string_field (message, - DBUS_HEADER_FIELD_SIGNATURE, - NULL); + get_const_signature (&message->header, &type_str, &type_pos); + + return _dbus_string_get_const_data_len (type_str, type_pos, 0); } static dbus_bool_t _dbus_message_has_type_interface_member (DBusMessage *message, int type, const char *interface, - const char *method) + const char *member) { const char *n; _dbus_assert (message != NULL); _dbus_assert (interface != NULL); - _dbus_assert (method != NULL); + _dbus_assert (member != NULL); if (dbus_message_get_type (message) != type) return FALSE; - /* Optimize by checking the short method name first + /* Optimize by checking the short member name first * instead of the longer interface name - */ + */ n = dbus_message_get_member (message); - if (n && strcmp (n, method) == 0) + if (n && strcmp (n, member) == 0) { n = dbus_message_get_interface (message); - - if (n && strcmp (n, interface) == 0) + + if (n == NULL || strcmp (n, interface) == 0) return TRUE; } @@ -4774,13 +2695,15 @@ _dbus_message_has_type_interface_member (DBusMessage *message, /** * Checks whether the message is a method call with the given * interface and member fields. If the message is not - * #DBUS_MESSAGE_TYPE_METHOD_CALL, or has a different interface or member field, - * returns #FALSE. + * #DBUS_MESSAGE_TYPE_METHOD_CALL, or has a different interface or + * member field, returns #FALSE. If the interface field is missing, + * then it will be assumed equal to the provided interface. The D-BUS + * protocol allows method callers to leave out the interface name. * * @param message the message * @param interface the name to check (must not be #NULL) * @param method the name to check (must not be #NULL) - * + * * @returns #TRUE if the message is the specified method call */ dbus_bool_t @@ -4791,6 +2714,9 @@ dbus_message_is_method_call (DBusMessage *message, _dbus_return_val_if_fail (message != NULL, FALSE); _dbus_return_val_if_fail (interface != NULL, FALSE); _dbus_return_val_if_fail (method != NULL, FALSE); + /* don't check that interface/method are valid since it would be + * expensive, and not catch many common errors + */ return _dbus_message_has_type_interface_member (message, DBUS_MESSAGE_TYPE_METHOD_CALL, @@ -4798,15 +2724,16 @@ dbus_message_is_method_call (DBusMessage *message, } /** - * Checks whether the message is a signal with the given - * interface and member fields. If the message is not - * #DBUS_MESSAGE_TYPE_SIGNAL, or has a different interface or member field, - * returns #FALSE. + * Checks whether the message is a signal with the given interface and + * member fields. If the message is not #DBUS_MESSAGE_TYPE_SIGNAL, or + * has a different interface or member field, returns #FALSE. If the + * interface field in the message is missing, it is assumed to match + * any interface you pass in to this function. * * @param message the message * @param interface the name to check (must not be #NULL) * @param signal_name the name to check (must not be #NULL) - * + * * @returns #TRUE if the message is the specified signal */ dbus_bool_t @@ -4817,6 +2744,9 @@ dbus_message_is_signal (DBusMessage *message, _dbus_return_val_if_fail (message != NULL, FALSE); _dbus_return_val_if_fail (interface != NULL, FALSE); _dbus_return_val_if_fail (signal_name != NULL, FALSE); + /* don't check that interface/name are valid since it would be + * expensive, and not catch many common errors + */ return _dbus_message_has_type_interface_member (message, DBUS_MESSAGE_TYPE_SIGNAL, @@ -4830,7 +2760,7 @@ dbus_message_is_signal (DBusMessage *message, * * @param message the message * @param error_name the name to check (must not be #NULL) - * + * * @returns #TRUE if the message is the specified error */ dbus_bool_t @@ -4838,11 +2768,13 @@ dbus_message_is_error (DBusMessage *message, const char *error_name) { const char *n; - + _dbus_return_val_if_fail (message != NULL, FALSE); _dbus_return_val_if_fail (error_name != NULL, FALSE); - _dbus_return_val_if_fail (is_valid_error_name (error_name), FALSE); - + /* don't check that error_name is valid since it would be expensive, + * and not catch many common errors + */ + if (dbus_message_get_type (message) != DBUS_MESSAGE_TYPE_ERROR) return FALSE; @@ -4861,7 +2793,7 @@ dbus_message_is_error (DBusMessage *message, * * @param message the message * @param service the service to check (must not be #NULL) - * + * * @returns #TRUE if the message has the given destination service */ dbus_bool_t @@ -4872,7 +2804,10 @@ dbus_message_has_destination (DBusMessage *message, _dbus_return_val_if_fail (message != NULL, FALSE); _dbus_return_val_if_fail (service != NULL, FALSE); - + /* don't check that service name is valid since it would be expensive, + * and not catch many common errors + */ + s = dbus_message_get_destination (message); if (s && strcmp (s, service) == 0) @@ -4890,9 +2825,12 @@ dbus_message_has_destination (DBusMessage *message, * function to prove the sender didn't own service Foo, you can * only use it to prove that it did. * + * @todo this function is probably useless unless we make a hard guarantee + * that the sender field in messages will always be the base service name + * * @param message the message * @param service the service to check (must not be #NULL) - * + * * @returns #TRUE if the message has the given origin service */ dbus_bool_t @@ -4903,7 +2841,10 @@ dbus_message_has_sender (DBusMessage *message, _dbus_return_val_if_fail (message != NULL, FALSE); _dbus_return_val_if_fail (service != NULL, FALSE); - + /* don't check that service name is valid since it would be expensive, + * and not catch many common errors + */ + s = dbus_message_get_sender (message); if (s && strcmp (s, service) == 0) @@ -4913,9 +2854,9 @@ dbus_message_has_sender (DBusMessage *message, } /** - * Checks whether the message has the given signature; - * see dbus_message_get_signature() for more details on - * what the signature looks like. + * Checks whether the message has the given signature; see + * dbus_message_get_signature() for more details on what the signature + * looks like. * * @param message the message * @param signature typecode array @@ -4929,7 +2870,10 @@ dbus_message_has_signature (DBusMessage *message, _dbus_return_val_if_fail (message != NULL, FALSE); _dbus_return_val_if_fail (signature != NULL, FALSE); - + /* don't check that signature is valid since it would be expensive, + * and not catch many common errors + */ + s = dbus_message_get_signature (message); if (s && strcmp (s, signature) == 0) @@ -4947,9 +2891,14 @@ dbus_message_has_signature (DBusMessage *message, * if the argument exists and is a string. * * The return value indicates whether the error was set (the error is - * set if and only if the message is an error message). - * So you can check for an error reply and convert it to DBusError - * in one go. + * set if and only if the message is an error message). So you can + * check for an error reply and convert it to DBusError in one go: + * @code + * if (dbus_set_error_from_message (error, reply)) + * return error; + * else + * process reply; + * @endcode * * @param error the error to set * @param message the message to set it from @@ -4959,11 +2908,11 @@ dbus_bool_t dbus_set_error_from_message (DBusError *error, DBusMessage *message) { - char *str; + const char *str; _dbus_return_val_if_fail (message != NULL, FALSE); _dbus_return_val_if_error_is_set (error, FALSE); - + if (dbus_message_get_type (message) != DBUS_MESSAGE_TYPE_ERROR) return FALSE; @@ -4975,8 +2924,6 @@ dbus_set_error_from_message (DBusError *error, dbus_set_error (error, dbus_message_get_error_name (message), str ? "%s" : NULL, str); - dbus_free (str); - return TRUE; } @@ -5001,18 +2948,8 @@ dbus_set_error_from_message (DBusError *error, * @todo write tests for break-loader that a) randomly delete header * fields and b) set string fields to zero-length and other funky * values. - * - */ - -/* we definitely use signed ints for sizes, so don't exceed - * _DBUS_INT_MAX; and add 16 for paranoia, since a message - * over 128M is pretty nuts anyhow. - */ - -/** - * The maximum sane message size. + * */ -#define MAX_SANE_MESSAGE_SIZE (_DBUS_INT_MAX/16) /** * Implementation details of DBusMessageLoader. @@ -5023,11 +2960,11 @@ struct DBusMessageLoader int refcount; /**< Reference count. */ DBusString data; /**< Buffered data */ - + DBusList *messages; /**< Complete messages. */ long max_message_size; /**< Maximum size of a message */ - + unsigned int buffer_outstanding : 1; /**< Someone is using the buffer to read */ unsigned int corrupted : 1; /**< We got broken data, and are no longer working */ @@ -5035,7 +2972,7 @@ struct DBusMessageLoader /** * The initial buffer size of the message loader. - * + * * @todo this should be based on min header size plus some average * body size, or something. Or rather, the min header size only, if we * want to try to read only the header, store that in a DBusMessage, @@ -5059,14 +2996,14 @@ _dbus_message_loader_new (void) loader = dbus_new0 (DBusMessageLoader, 1); if (loader == NULL) return NULL; - + loader->refcount = 1; /* Try to cap message size at something that won't *totally* hose * the system if we have a couple of them. */ loader->max_message_size = _DBUS_ONE_MEGABYTE * 32; - + if (!_dbus_string_init (&loader->data)) { dbus_free (loader); @@ -5076,7 +3013,7 @@ _dbus_message_loader_new (void) /* preallocate the buffer for speed, ignore failure */ _dbus_string_set_length (&loader->data, INITIAL_LOADER_DATA_LEN); _dbus_string_set_length (&loader->data, 0); - + return loader; } @@ -5129,7 +3066,7 @@ _dbus_message_loader_unref (DBusMessageLoader *loader) * or reallocs. * * @todo we need to enforce a max length on strings in header fields. - * + * * @param loader the message loader. * @param buffer the buffer */ @@ -5140,375 +3077,17 @@ _dbus_message_loader_get_buffer (DBusMessageLoader *loader, _dbus_assert (!loader->buffer_outstanding); *buffer = &loader->data; - + loader->buffer_outstanding = TRUE; } /** - * The smallest header size that can occur. - * (It won't be valid) + * The smallest header size that can occur. (It won't be valid due to + * missing required header fields.) This is 4 bytes, two uint32, an + * array length. */ #define DBUS_MINIMUM_HEADER_SIZE 16 -static dbus_bool_t -decode_string_field (const DBusString *data, - int field, - HeaderField *header_field, - DBusString *field_data, - int pos, - int type) -{ - int string_data_pos; - - _dbus_assert (header_field != NULL); - _dbus_assert (field_data != NULL); - - if (header_field->name_offset >= 0) - { - _dbus_verbose ("%s field provided twice\n", - _dbus_header_field_to_string (field)); - return FALSE; - } - - if (type != DBUS_TYPE_STRING) - { - _dbus_verbose ("%s field has wrong type %s\n", - _dbus_header_field_to_string (field), - _dbus_type_to_string (type)); - return FALSE; - } - - /* skip padding after typecode, skip string length; - * we assume that the string arg has already been validated - * for sanity and UTF-8 - */ - string_data_pos = _DBUS_ALIGN_VALUE (pos, 4) + 4; - _dbus_assert (string_data_pos < _dbus_string_get_length (data)); - - _dbus_string_init_const (field_data, - _dbus_string_get_const_data (data) + string_data_pos); - - header_field->name_offset = pos - 2; - header_field->value_offset = _DBUS_ALIGN_VALUE (pos, 4); - -#if 0 - _dbus_verbose ("Found field %s at offset %d\n", - _dbus_header_field_to_string (field), - header_field->value_offset); -#endif - - return TRUE; -} - -/* FIXME because the service/interface/member/error names are already - * validated to be in the particular ASCII subset, UTF-8 validating - * them could be skipped as a probably-interesting optimization. - * The UTF-8 validation shows up in callgrind-type profiles but - * not so much in sample/time-based profiles. - */ -static dbus_bool_t -decode_header_data (const DBusString *data, - int header_len, - int byte_order, - int message_type, - HeaderField fields[DBUS_HEADER_FIELD_LAST + 1], - int *message_padding) -{ - DBusString field_data; - int pos, new_pos; - int i; - int field; - int type; - dbus_bool_t signature_required; - - if (header_len < 16) - { - _dbus_verbose ("Header length %d is too short\n", header_len); - return FALSE; - } - - i = 0; - while (i <= DBUS_HEADER_FIELD_LAST) - { - fields[i].name_offset = -1; - fields[i].value_offset = -1; - ++i; - } - - pos = 16; - while (pos < header_len) - { - field = _dbus_string_get_byte (data, pos); - if (field == DBUS_HEADER_FIELD_INVALID) - break; /* Must be padding */ - pos++; - - if (!_dbus_marshal_validate_type (data, pos, &type, &pos)) - { - _dbus_verbose ("Failed to validate type of named header field pos = %d\n", - pos); - return FALSE; - } - - if (!_dbus_marshal_validate_arg (data, byte_order, 0, type, -1, pos, &new_pos)) - { - _dbus_verbose ("Failed to validate argument to named header field pos = %d\n", - pos); - return FALSE; - } - - if (new_pos > header_len) - { - _dbus_verbose ("Named header field tries to extend beyond header length\n"); - return FALSE; - } - - switch (field) - { - case DBUS_HEADER_FIELD_DESTINATION: - if (!decode_string_field (data, field, &fields[field], - &field_data, pos, type)) - return FALSE; - - if (!_dbus_string_validate_service (&field_data, 0, - _dbus_string_get_length (&field_data))) - { - _dbus_verbose ("service field has invalid content \"%s\"\n", - _dbus_string_get_const_data (&field_data)); - return FALSE; - } - break; - - case DBUS_HEADER_FIELD_INTERFACE: - if (!decode_string_field (data, field, &fields[field], - &field_data, pos, type)) - return FALSE; - - if (!_dbus_string_validate_interface (&field_data, 0, - _dbus_string_get_length (&field_data))) - { - _dbus_verbose ("interface field has invalid content \"%s\"\n", - _dbus_string_get_const_data (&field_data)); - return FALSE; - } - - if (_dbus_string_equal_c_str (&field_data, - DBUS_INTERFACE_ORG_FREEDESKTOP_LOCAL)) - { - _dbus_verbose ("Message is on the local interface\n"); - return FALSE; - } - break; - - case DBUS_HEADER_FIELD_MEMBER: - if (!decode_string_field (data, field, &fields[field], - &field_data, pos, type)) - return FALSE; - - if (!_dbus_string_validate_member (&field_data, 0, - _dbus_string_get_length (&field_data))) - { - _dbus_verbose ("member field has invalid content \"%s\"\n", - _dbus_string_get_const_data (&field_data)); - return FALSE; - } - break; - - case DBUS_HEADER_FIELD_ERROR_NAME: - if (!decode_string_field (data, field, &fields[field], - &field_data, pos, type)) - return FALSE; - - if (!_dbus_string_validate_error_name (&field_data, 0, - _dbus_string_get_length (&field_data))) - { - _dbus_verbose ("error-name field has invalid content \"%s\"\n", - _dbus_string_get_const_data (&field_data)); - return FALSE; - } - break; - - case DBUS_HEADER_FIELD_SENDER: - if (!decode_string_field (data, field, &fields[field], - &field_data, pos, type)) - return FALSE; - - if (!_dbus_string_validate_service (&field_data, 0, - _dbus_string_get_length (&field_data))) - { - _dbus_verbose ("sender-service field has invalid content \"%s\"\n", - _dbus_string_get_const_data (&field_data)); - return FALSE; - } - break; - - case DBUS_HEADER_FIELD_PATH: - - /* Path was already validated as part of standard - * type validation, since there's an OBJECT_PATH - * type. - */ - - if (fields[field].name_offset >= 0) - { - _dbus_verbose ("path field provided twice\n"); - return FALSE; - } - if (type != DBUS_TYPE_OBJECT_PATH) - { - _dbus_verbose ("path field has wrong type\n"); - return FALSE; - } - - fields[field].name_offset = pos - 2; - fields[field].value_offset = _DBUS_ALIGN_VALUE (pos, 4); - - /* No forging signals from the local path */ - { - const char *s; - s = _dbus_string_get_const_data_len (data, - fields[field].value_offset, - _dbus_string_get_length (data) - - fields[field].value_offset); - if (strcmp (s, DBUS_PATH_ORG_FREEDESKTOP_LOCAL) == 0) - { - _dbus_verbose ("Message is on the local path\n"); - return FALSE; - } - } - - _dbus_verbose ("Found path at offset %d\n", - fields[field].value_offset); - break; - - case DBUS_HEADER_FIELD_REPLY_SERIAL: - if (fields[field].name_offset >= 0) - { - _dbus_verbose ("reply field provided twice\n"); - return FALSE; - } - - if (type != DBUS_TYPE_UINT32) - { - _dbus_verbose ("reply field has wrong type\n"); - return FALSE; - } - - fields[field].name_offset = pos - 2; - fields[field].value_offset = _DBUS_ALIGN_VALUE (pos, 4); - - _dbus_verbose ("Found reply serial %u at offset %d\n", - _dbus_demarshal_uint32 (data, - byte_order, - fields[field].value_offset, - NULL), - fields[field].value_offset); - break; - - case DBUS_HEADER_FIELD_SIGNATURE: - if (!decode_string_field (data, field, &fields[field], - &field_data, pos, type)) - return FALSE; - - if (!_dbus_string_validate_signature (&field_data, 0, - _dbus_string_get_length (&field_data))) - { - _dbus_verbose ("signature field has invalid content \"%s\"\n", - _dbus_string_get_const_data (&field_data)); - return FALSE; - } - break; - - default: - _dbus_verbose ("Ignoring an unknown header field: %d at offset %d\n", - field, pos); - } - - pos = new_pos; - } - - if (pos < header_len) - { - /* Alignment padding, verify that it's nul */ - if ((header_len - pos) >= 8) - { - _dbus_verbose ("too much header alignment padding\n"); - return FALSE; - } - - if (!_dbus_string_validate_nul (data, - pos, (header_len - pos))) - { - _dbus_verbose ("header alignment padding is not nul\n"); - return FALSE; - } - } - - /* Depending on message type, enforce presence of certain fields. */ - signature_required = TRUE; - - switch (message_type) - { - case DBUS_MESSAGE_TYPE_SIGNAL: - case DBUS_MESSAGE_TYPE_METHOD_CALL: - if (fields[DBUS_HEADER_FIELD_PATH].value_offset < 0) - { - _dbus_verbose ("No path field provided\n"); - return FALSE; - } - /* FIXME make this optional, only for method calls */ - if (fields[DBUS_HEADER_FIELD_INTERFACE].value_offset < 0) - { - _dbus_verbose ("No interface field provided\n"); - return FALSE; - } - if (fields[DBUS_HEADER_FIELD_MEMBER].value_offset < 0) - { - _dbus_verbose ("No member field provided\n"); - return FALSE; - } - break; - case DBUS_MESSAGE_TYPE_ERROR: - if (fields[DBUS_HEADER_FIELD_ERROR_NAME].value_offset < 0) - { - _dbus_verbose ("No error-name field provided\n"); - return FALSE; - } - if (fields[DBUS_HEADER_FIELD_REPLY_SERIAL].value_offset < 0) - { - _dbus_verbose ("No reply serial field provided in error\n"); - return FALSE; - } - break; - case DBUS_MESSAGE_TYPE_METHOD_RETURN: - if (fields[DBUS_HEADER_FIELD_REPLY_SERIAL].value_offset < 0) - { - _dbus_verbose ("No reply serial field provided in method return\n"); - return FALSE; - } - break; - default: - /* An unknown type, spec requires us to ignore it */ - signature_required = FALSE; - break; - } - - /* FIXME allow omitting signature field for a message with no arguments? */ - if (signature_required) - { - if (fields[DBUS_HEADER_FIELD_SIGNATURE].value_offset < 0) - { - _dbus_verbose ("No signature field provided\n"); - return FALSE; - } - } - - if (message_padding) - *message_padding = header_len - pos; - - return TRUE; -} - /** * Returns a buffer obtained from _dbus_message_loader_get_buffer(), * indicating to the loader how many bytes of the buffer were filled @@ -5530,94 +3109,96 @@ _dbus_message_loader_return_buffer (DBusMessageLoader *loader, loader->buffer_outstanding = FALSE; } +/* + * FIXME when we move the header out of the buffer, that memmoves all + * buffered messages. Kind of crappy. + * + * Also we copy the header and body, which is kind of crappy. To + * avoid this, we have to allow header and body to be in a single + * memory block, which is good for messages we read and bad for + * messages we are creating. But we could move_len() the buffer into + * this single memory block, and move_len() will just swap the buffers + * if you're moving the entire buffer replacing the dest string. + * + * We could also have the message loader tell the transport how many + * bytes to read; so it would first ask for some arbitrary number like + * 256, then if the message was incomplete it would use the + * header/body len to ask for exactly the size of the message (or + * blocks the size of a typical kernel buffer for the socket). That + * way we don't get trailing bytes in the buffer that have to be + * memmoved. Though I suppose we also don't have a chance of reading a + * bunch of small messages at once, so the optimization may be stupid. + * + * Another approach would be to keep a "start" index into + * loader->data and only delete it occasionally, instead of after + * each message is loaded. + * + * load_message() returns FALSE if not enough memory + */ static dbus_bool_t -load_one_message (DBusMessageLoader *loader, - int byte_order, - int message_type, - int header_len, - int body_len) +load_message (DBusMessageLoader *loader, + DBusMessage *message, + int byte_order, + int fields_array_len, + int header_len, + int body_len) { - DBusMessage *message; - HeaderField fields[DBUS_HEADER_FIELD_LAST + 1]; - int i; - int next_arg; dbus_bool_t oom; - int header_padding; - - message = NULL; + DBusValidity validity; + const DBusString *type_str; + int type_pos; + oom = FALSE; - + #if 0 _dbus_verbose_bytes_of_string (&loader->data, 0, header_len /* + body_len */); -#endif +#endif - if (!decode_header_data (&loader->data, - header_len, byte_order, - message_type, - fields, &header_padding)) - { - _dbus_verbose ("Header was invalid\n"); - loader->corrupted = TRUE; + /* 1. 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 (&loader->data)); + + if (!_dbus_header_load_untrusted (&message->header, + &validity, + byte_order, + fields_array_len, + header_len, + body_len, + &loader->data, 0, + _dbus_string_get_length (&loader->data))) + { + _dbus_verbose ("Failed to load header for new message code %d\n", validity); + if (validity == DBUS_VALID) + oom = TRUE; goto failed; } - - next_arg = header_len; - while (next_arg < (header_len + body_len)) - { - int type; - int prev = next_arg; - if (!_dbus_marshal_validate_type (&loader->data, next_arg, - &type, &next_arg)) - { - _dbus_verbose ("invalid typecode at offset %d\n", prev); - loader->corrupted = TRUE; - goto failed; - } - - if (!_dbus_marshal_validate_arg (&loader->data, - byte_order, - 0, - type, -1, - next_arg, - &next_arg)) - { - _dbus_verbose ("invalid type data at %d, next_arg\n", next_arg); - loader->corrupted = TRUE; - goto failed; - } + _dbus_assert (validity == DBUS_VALID); - _dbus_assert (next_arg > prev); - } - - if (next_arg > (header_len + body_len)) - { - _dbus_verbose ("end of last arg at %d but message has len %d+%d=%d\n", - next_arg, header_len, body_len, - header_len + body_len); - loader->corrupted = TRUE; - goto failed; - } + message->byte_order = byte_order; - message = dbus_message_new_empty_header (); - if (message == NULL) - { - _dbus_verbose ("Failed to allocate empty message\n"); - oom = TRUE; + /* 2. VALIDATE BODY */ + + 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 (validity != DBUS_VALID) + { + _dbus_verbose ("Failed to validate message body code %d\n", validity); goto failed; } - message->byte_order = byte_order; - message->header_padding = header_padding; - - /* Copy in the offsets we found */ - i = 0; - while (i <= DBUS_HEADER_FIELD_LAST) - { - message->header_fields[i] = fields[i]; - ++i; - } - + /* 3. COPY OVER BODY AND QUEUE MESSAGE */ + if (!_dbus_list_append (&loader->messages, message)) { _dbus_verbose ("Failed to append new message to loader queue\n"); @@ -5625,51 +3206,22 @@ load_one_message (DBusMessageLoader *loader, goto failed; } - _dbus_assert (_dbus_string_get_length (&message->header) == 0); _dbus_assert (_dbus_string_get_length (&message->body) == 0); - _dbus_assert (_dbus_string_get_length (&loader->data) >= (header_len + body_len)); - - if (!_dbus_string_move_len (&loader->data, 0, header_len, &message->header, 0)) - { - _dbus_verbose ("Failed to move header into new message\n"); - oom = TRUE; - goto failed; - } - - if (!_dbus_string_move_len (&loader->data, 0, body_len, &message->body, 0)) + + if (!_dbus_string_copy_len (&loader->data, header_len, body_len, &message->body, 0)) { _dbus_verbose ("Failed to move body into new message\n"); - oom = TRUE; goto failed; } - _dbus_assert (_dbus_string_get_length (&message->header) == header_len); + _dbus_string_delete (&loader->data, 0, header_len + body_len); + + _dbus_assert (_dbus_string_get_length (&message->header.data) == header_len); _dbus_assert (_dbus_string_get_length (&message->body) == body_len); - /* Fill in caches (we checked the types of these fields - * earlier) - */ - message->reply_serial = get_uint_field (message, - DBUS_HEADER_FIELD_REPLY_SERIAL); - - message->client_serial = _dbus_demarshal_uint32 (&message->header, - message->byte_order, - CLIENT_SERIAL_OFFSET, - NULL); - if (message->client_serial == 0 || - (message->header_fields[DBUS_HEADER_FIELD_REPLY_SERIAL].value_offset >= 0 && message->reply_serial == 0)) - { - _dbus_verbose ("client_serial = %d reply_serial = %d, one of these no good\n", - message->client_serial, - message->reply_serial); - - loader->corrupted = TRUE; - goto failed; - } - _dbus_verbose ("Loaded message %p\n", message); _dbus_assert (!oom); @@ -5678,153 +3230,76 @@ load_one_message (DBusMessageLoader *loader, return TRUE; failed: - + /* Clean up */ - - if (message != NULL) - { - /* Put the data back so we can try again later if it was an OOM issue */ - if (_dbus_string_get_length (&message->body) > 0) - { - dbus_bool_t result; - - result = _dbus_string_copy_len (&message->body, 0, body_len, - &loader->data, 0); - - _dbus_assert (result); /* because DBusString never reallocs smaller */ - } - - if (_dbus_string_get_length (&message->header) > 0) - { - dbus_bool_t result; - - result = _dbus_string_copy_len (&message->header, 0, header_len, - &loader->data, 0); - - _dbus_assert (result); /* because DBusString never reallocs smaller */ - } - /* does nothing if the message isn't in the list */ - _dbus_list_remove_last (&loader->messages, message); + /* does nothing if the message isn't in the list */ + _dbus_list_remove_last (&loader->messages, message); - dbus_message_unref (message); - } + if (!oom) + loader->corrupted = TRUE; + + _dbus_verbose_bytes_of_string (&loader->data, 0, _dbus_string_get_length (&loader->data)); - return !oom; } /** - * Converts buffered data into messages. + * Converts buffered data into messages, if we have enough data. If + * we don't have enough data, does nothing. * * @todo we need to check that the proper named header fields exist * for each message type. - * + * * @todo If a message has unknown type, we should probably eat it * right here rather than passing it out to applications. However * it's not an error to see messages of unknown type. - * + * * @param loader the loader. * @returns #TRUE if we had enough memory to finish. */ dbus_bool_t _dbus_message_loader_queue_messages (DBusMessageLoader *loader) { - while (!loader->corrupted && _dbus_string_get_length (&loader->data) >= 16) + while (!loader->corrupted && + _dbus_string_get_length (&loader->data) >= DBUS_MINIMUM_HEADER_SIZE) { - const char *header_data; - int byte_order, message_type, header_len, body_len; - dbus_uint32_t header_len_unsigned, body_len_unsigned; - - header_data = _dbus_string_get_const_data_len (&loader->data, 0, 16); + DBusValidity validity; + int byte_order, fields_array_len, header_len, body_len; - _dbus_assert (_DBUS_ALIGN_ADDRESS (header_data, 4) == header_data); - - if (header_data[VERSION_OFFSET] != DBUS_MAJOR_PROTOCOL_VERSION) - { - _dbus_verbose ("Message has protocol version %d ours is %d\n", - (int) header_data[VERSION_OFFSET], DBUS_MAJOR_PROTOCOL_VERSION); - loader->corrupted = TRUE; - return TRUE; - } - - byte_order = header_data[BYTE_ORDER_OFFSET]; - - if (byte_order != DBUS_LITTLE_ENDIAN && - byte_order != DBUS_BIG_ENDIAN) - { - _dbus_verbose ("Message with bad byte order '%c' received\n", - byte_order); - loader->corrupted = TRUE; - return TRUE; - } - - /* Unknown types are ignored, but INVALID is - * disallowed - */ - message_type = header_data[TYPE_OFFSET]; - if (message_type == DBUS_MESSAGE_TYPE_INVALID) + if (_dbus_header_have_message_untrusted (loader->max_message_size, + &validity, + &byte_order, + &fields_array_len, + &header_len, + &body_len, + &loader->data, 0, + _dbus_string_get_length (&loader->data))) { - _dbus_verbose ("Message with bad type '%d' received\n", - message_type); - loader->corrupted = TRUE; - return TRUE; - } - - header_len_unsigned = _dbus_unpack_uint32 (byte_order, - (const unsigned char *) header_data + 4); - body_len_unsigned = _dbus_unpack_uint32 (byte_order, - (const unsigned char *) header_data + 8); - - if (header_len_unsigned < 16) - { - _dbus_verbose ("Message had broken too-small header length %u\n", - header_len_unsigned); - loader->corrupted = TRUE; - return TRUE; - } + DBusMessage *message; - if (header_len_unsigned > (unsigned) MAX_SANE_MESSAGE_SIZE || - body_len_unsigned > (unsigned) MAX_SANE_MESSAGE_SIZE) - { - _dbus_verbose ("Header or body length too large (%u %u)\n", - header_len_unsigned, - body_len_unsigned); - loader->corrupted = TRUE; - return TRUE; - } + _dbus_assert (validity == DBUS_VALID); - /* Now that we know the values are in signed range, get - * rid of stupid unsigned, just causes bugs - */ - header_len = header_len_unsigned; - body_len = body_len_unsigned; + message = dbus_message_new_empty_header (); + if (message == NULL) + return FALSE; - if (_DBUS_ALIGN_VALUE (header_len, 8) != header_len_unsigned) + if (!load_message (loader, message, + byte_order, fields_array_len, + header_len, body_len)) + { + dbus_message_unref (message); + return FALSE; + } + } + else { - - _dbus_verbose ("header length %d is not aligned to 8 bytes\n", - header_len); - loader->corrupted = TRUE; + _dbus_verbose ("Initial peek at header says we don't have a whole message yet, or data broken with invalid code %d\n", + validity); + if (validity != DBUS_VALID) + loader->corrupted = TRUE; return TRUE; } - - if (header_len + body_len > loader->max_message_size) - { - _dbus_verbose ("Message claimed length header = %d body = %d exceeds max message length %ld\n", - header_len, body_len, loader->max_message_size); - loader->corrupted = TRUE; - return TRUE; - } - - if (_dbus_string_get_length (&loader->data) >= (header_len + body_len)) - { - if (!load_one_message (loader, byte_order, message_type, - header_len, body_len)) - return FALSE; - } - else - return TRUE; } return TRUE; @@ -5912,11 +3387,11 @@ void _dbus_message_loader_set_max_message_size (DBusMessageLoader *loader, long size) { - if (size > MAX_SANE_MESSAGE_SIZE) + if (size > DBUS_MAXIMUM_MESSAGE_LENGTH) { _dbus_verbose ("clamping requested max message size %ld to %d\n", - size, MAX_SANE_MESSAGE_SIZE); - size = MAX_SANE_MESSAGE_SIZE; + size, DBUS_MAXIMUM_MESSAGE_LENGTH); + size = DBUS_MAXIMUM_MESSAGE_LENGTH; } loader->max_message_size = size; } @@ -5943,7 +3418,7 @@ _DBUS_DEFINE_GLOBAL_LOCK (message_slots); * The passed-in slot must be initialized to -1, and is filled in * with the slot ID. If the passed-in slot is not -1, it's assumed * to be already allocated, and its refcount is incremented. - * + * * The allocated slot is global, i.e. all DBusMessage objects will * have a slot with the given integer ID reserved. * @@ -5973,7 +3448,7 @@ void dbus_message_free_data_slot (dbus_int32_t *slot_p) { _dbus_return_if_fail (*slot_p >= 0); - + _dbus_data_slot_allocator_free (&slot_allocator, slot_p); } @@ -6033,7 +3508,7 @@ dbus_message_get_data (DBusMessage *message, void *res; _dbus_return_val_if_fail (message != NULL, NULL); - + res = _dbus_data_slot_list_get (&slot_allocator, &message->slot_list, slot); @@ -6042,502 +3517,69 @@ dbus_message_get_data (DBusMessage *message, } /** - * Utility function to convert a machine-readable (not translated) - * string into a D-BUS message type. - * - * @code - * "method_call" -> DBUS_MESSAGE_TYPE_METHOD_CALL - * "method_return" -> DBUS_MESSAGE_TYPE_METHOD_RETURN - * "signal" -> DBUS_MESSAGE_TYPE_SIGNAL - * "error" -> DBUS_MESSAGE_TYPE_ERROR - * anything else -> DBUS_MESSAGE_TYPE_INVALID - * @endcode - * - */ -int -dbus_message_type_from_string (const char *type_str) -{ - if (strcmp (type_str, "method_call") == 0) - return DBUS_MESSAGE_TYPE_METHOD_CALL; - if (strcmp (type_str, "method_return") == 0) - return DBUS_MESSAGE_TYPE_METHOD_RETURN; - else if (strcmp (type_str, "signal") == 0) - return DBUS_MESSAGE_TYPE_SIGNAL; - else if (strcmp (type_str, "error") == 0) - return DBUS_MESSAGE_TYPE_ERROR; - else - return DBUS_MESSAGE_TYPE_INVALID; -} - -/** - * Utility function to convert a D-BUS message type into a - * machine-readable string (not translated). - * - * @code - * DBUS_MESSAGE_TYPE_METHOD_CALL -> "method_call" - * DBUS_MESSAGE_TYPE_METHOD_RETURN -> "method_return" - * DBUS_MESSAGE_TYPE_SIGNAL -> "signal" - * DBUS_MESSAGE_TYPE_ERROR -> "error" - * DBUS_MESSAGE_TYPE_INVALID -> "invalid" - * @endcode - * - */ -const char * -dbus_message_type_to_string (int type) -{ - switch (type) - { - case DBUS_MESSAGE_TYPE_METHOD_CALL: - return "method_call"; - case DBUS_MESSAGE_TYPE_METHOD_RETURN: - return "method_return"; - case DBUS_MESSAGE_TYPE_SIGNAL: - return "signal"; - case DBUS_MESSAGE_TYPE_ERROR: - return "error"; - default: - return "invalid"; - } -} - -/** @} */ -#ifdef DBUS_BUILD_TESTS -#include "dbus-test.h" -#include -#include - -static void -message_iter_test (DBusMessage *message) -{ - DBusMessageIter iter, dict, dict2, array, array2; - char *str; - unsigned char *data; - dbus_int32_t *our_int_array; - int len; - - dbus_message_iter_init (message, &iter); - - /* String tests */ - if (dbus_message_iter_get_arg_type (&iter) != DBUS_TYPE_STRING) - _dbus_assert_not_reached ("Argument type isn't string"); - - str = dbus_message_iter_get_string (&iter); - if (strcmp (str, "Test string") != 0) - _dbus_assert_not_reached ("Strings differ"); - dbus_free (str); - - if (!dbus_message_iter_next (&iter)) - _dbus_assert_not_reached ("Reached end of arguments"); - - /* Signed integer tests */ - if (dbus_message_iter_get_arg_type (&iter) != DBUS_TYPE_INT32) - _dbus_assert_not_reached ("Argument type isn't int32"); - - if (dbus_message_iter_get_int32 (&iter) != -0x12345678) - _dbus_assert_not_reached ("Signed integers differ"); - - if (!dbus_message_iter_next (&iter)) - _dbus_assert_not_reached ("Reached end of fields"); - - /* Unsigned integer tests */ - if (dbus_message_iter_get_arg_type (&iter) != DBUS_TYPE_UINT32) - _dbus_assert_not_reached ("Argument type isn't int32"); - - if (dbus_message_iter_get_uint32 (&iter) != 0xedd1e) - _dbus_assert_not_reached ("Unsigned integers differ"); - - if (!dbus_message_iter_next (&iter)) - _dbus_assert_not_reached ("Reached end of arguments"); - - /* Double tests */ - if (dbus_message_iter_get_arg_type (&iter) != DBUS_TYPE_DOUBLE) - _dbus_assert_not_reached ("Argument type isn't double"); - - if (dbus_message_iter_get_double (&iter) != 3.14159) - _dbus_assert_not_reached ("Doubles differ"); - - if (!dbus_message_iter_next (&iter)) - _dbus_assert_not_reached ("Reached end of arguments"); - - if (dbus_message_iter_get_arg_type (&iter) != DBUS_TYPE_ARRAY) - _dbus_assert_not_reached ("Argument type not an array"); - - if (dbus_message_iter_get_array_type (&iter) != DBUS_TYPE_DOUBLE) - _dbus_assert_not_reached ("Array type not double"); - - - dbus_message_iter_init_array_iterator (&iter, &array, NULL); - - if (dbus_message_iter_get_arg_type (&array) != DBUS_TYPE_DOUBLE) - _dbus_assert_not_reached ("Argument type isn't double"); - - if (dbus_message_iter_get_double (&array) != 1.5) - _dbus_assert_not_reached ("Unsigned integers differ"); - - if (!dbus_message_iter_next (&array)) - _dbus_assert_not_reached ("Reached end of arguments"); - - if (dbus_message_iter_get_arg_type (&array) != DBUS_TYPE_DOUBLE) - _dbus_assert_not_reached ("Argument type isn't double"); - - if (dbus_message_iter_get_double (&array) != 2.5) - _dbus_assert_not_reached ("Unsigned integers differ"); - - if (dbus_message_iter_next (&array)) - _dbus_assert_not_reached ("Didn't reach end of arguments"); - - if (!dbus_message_iter_next (&iter)) - _dbus_assert_not_reached ("Reached end of arguments"); - - - /* dict */ - - if (dbus_message_iter_get_arg_type (&iter) != DBUS_TYPE_DICT) - _dbus_assert_not_reached ("not dict type"); - - dbus_message_iter_init_dict_iterator (&iter, &dict); - - str = dbus_message_iter_get_dict_key (&dict); - if (str == NULL || strcmp (str, "test") != 0) - _dbus_assert_not_reached ("wrong dict key"); - dbus_free (str); - - if (dbus_message_iter_get_arg_type (&dict) != DBUS_TYPE_UINT32) - _dbus_assert_not_reached ("wrong dict entry type"); - - if (dbus_message_iter_get_uint32 (&dict) != 0xDEADBEEF) - _dbus_assert_not_reached ("wrong dict entry value"); - - /* dict (in dict) */ - - if (!dbus_message_iter_next (&dict)) - _dbus_assert_not_reached ("reached end of dict"); - - if (dbus_message_iter_get_arg_type (&dict) != DBUS_TYPE_DICT) - _dbus_assert_not_reached ("not dict type"); - - dbus_message_iter_init_dict_iterator (&dict, &dict2); - - str = dbus_message_iter_get_dict_key (&dict2); - if (str == NULL || strcmp (str, "dictkey") != 0) - _dbus_assert_not_reached ("wrong dict key"); - dbus_free (str); - - if (dbus_message_iter_get_arg_type (&dict2) != DBUS_TYPE_STRING) - _dbus_assert_not_reached ("wrong dict entry type"); - - str = dbus_message_iter_get_string (&dict2); - if (str == NULL || strcmp (str, "dictvalue") != 0) - _dbus_assert_not_reached ("wrong dict entry value"); - dbus_free (str); - - if (dbus_message_iter_next (&dict2)) - _dbus_assert_not_reached ("didn't reach end of dict"); - - if (!dbus_message_iter_next (&dict)) - _dbus_assert_not_reached ("reached end of dict"); - - /* array of array of int32 (in dict) */ - - str = dbus_message_iter_get_dict_key (&dict); - if (str == NULL || strcmp (str, "array") != 0) - _dbus_assert_not_reached ("wrong dict key"); - dbus_free (str); - - if (dbus_message_iter_get_arg_type (&dict) != DBUS_TYPE_ARRAY) - _dbus_assert_not_reached ("Argument type not an array"); - - if (dbus_message_iter_get_array_type (&dict) != DBUS_TYPE_ARRAY) - _dbus_assert_not_reached ("Array type not array"); - - dbus_message_iter_init_array_iterator (&dict, &array, NULL); - - if (dbus_message_iter_get_arg_type (&array) != DBUS_TYPE_ARRAY) - _dbus_assert_not_reached ("Argument type isn't array"); - - if (dbus_message_iter_get_array_type (&array) != DBUS_TYPE_INT32) - _dbus_assert_not_reached ("Array type not int32"); - - dbus_message_iter_init_array_iterator (&array, &array2, NULL); - - if (dbus_message_iter_get_arg_type (&array2) != DBUS_TYPE_INT32) - _dbus_assert_not_reached ("Argument type isn't int32"); - - if (dbus_message_iter_get_int32 (&array2) != 0x12345678) - _dbus_assert_not_reached ("Signed integers differ"); - - if (!dbus_message_iter_next (&array2)) - _dbus_assert_not_reached ("Reached end of arguments"); - - if (dbus_message_iter_get_int32 (&array2) != 0x23456781) - _dbus_assert_not_reached ("Signed integers differ"); - - if (dbus_message_iter_next (&array2)) - _dbus_assert_not_reached ("Didn't reached end of arguments"); - - if (!dbus_message_iter_next (&array)) - _dbus_assert_not_reached ("Reached end of arguments"); - - if (dbus_message_iter_get_array_type (&array) != DBUS_TYPE_INT32) - _dbus_assert_not_reached ("Array type not int32"); - - if (!dbus_message_iter_get_int32_array (&array, - &our_int_array, - &len)) - _dbus_assert_not_reached ("couldn't get int32 array"); - - _dbus_assert (len == 3); - _dbus_assert (our_int_array[0] == 0x34567812 && - our_int_array[1] == 0x45678123 && - our_int_array[2] == 0x56781234); - dbus_free (our_int_array); - - if (dbus_message_iter_next (&array)) - _dbus_assert_not_reached ("Didn't reach end of array"); - - if (dbus_message_iter_next (&dict)) - _dbus_assert_not_reached ("Didn't reach end of dict"); - - if (!dbus_message_iter_next (&iter)) - _dbus_assert_not_reached ("Reached end of arguments"); - - if (dbus_message_iter_get_arg_type (&iter) != DBUS_TYPE_BYTE) - { - _dbus_warn ("type was: %d\n", dbus_message_iter_get_arg_type (&iter)); - _dbus_assert_not_reached ("wrong type after dict (should be byte)"); - } - - if (dbus_message_iter_get_byte (&iter) != 0xF0) - _dbus_assert_not_reached ("wrong value after dict"); - - - if (!dbus_message_iter_next (&iter)) - _dbus_assert_not_reached ("Reached end of arguments"); - - if (dbus_message_iter_get_arg_type (&iter) != DBUS_TYPE_NIL) - _dbus_assert_not_reached ("not a nil type"); - - if (!dbus_message_iter_next (&iter)) - _dbus_assert_not_reached ("Reached end of arguments"); - - if (dbus_message_iter_get_arg_type (&iter) != DBUS_TYPE_CUSTOM) - _dbus_assert_not_reached ("wrong type after dict"); - - if (!dbus_message_iter_get_custom (&iter, &str, &data, &len)) - _dbus_assert_not_reached ("failed to get custom type"); - - _dbus_assert (strcmp (str, "MyTypeName")==0); - _dbus_assert (len == 5); - _dbus_assert (strcmp (data, "data")==0); - dbus_free (str); - dbus_free (data); - - if (!dbus_message_iter_next (&iter)) - _dbus_assert_not_reached ("Reached end of arguments"); - - if (dbus_message_iter_get_byte (&iter) != 0xF0) - _dbus_assert_not_reached ("wrong value after custom"); - - if (!dbus_message_iter_next (&iter)) - _dbus_assert_not_reached ("Reached end of arguments"); - - if (dbus_message_iter_get_arg_type (&iter) != DBUS_TYPE_ARRAY) - _dbus_assert_not_reached ("no array"); - - if (dbus_message_iter_get_array_type (&iter) != DBUS_TYPE_INT32) - _dbus_assert_not_reached ("Array type not int32"); - - if (dbus_message_iter_init_array_iterator (&iter, &array, NULL)) - _dbus_assert_not_reached ("non empty array"); - - if (!dbus_message_iter_next (&iter)) - _dbus_assert_not_reached ("Reached end of arguments"); - - if (dbus_message_iter_get_byte (&iter) != 0xF0) - _dbus_assert_not_reached ("wrong value after empty array"); - - if (!dbus_message_iter_next (&iter)) - _dbus_assert_not_reached ("Reached end of arguments"); - - if (dbus_message_iter_get_arg_type (&iter) != DBUS_TYPE_DICT) - _dbus_assert_not_reached ("non dict"); - - if (dbus_message_iter_init_dict_iterator (&iter, &dict)) - _dbus_assert_not_reached ("non empty dict"); - - if (!dbus_message_iter_next (&iter)) - _dbus_assert_not_reached ("Reached end of arguments"); - - if (dbus_message_iter_get_byte (&iter) != 0xF0) - _dbus_assert_not_reached ("wrong value after empty dict"); - - if (dbus_message_iter_next (&iter)) - _dbus_assert_not_reached ("Didn't reach end of arguments"); + * Utility function to convert a machine-readable (not translated) + * string into a D-BUS message type. + * + * @code + * "method_call" -> DBUS_MESSAGE_TYPE_METHOD_CALL + * "method_return" -> DBUS_MESSAGE_TYPE_METHOD_RETURN + * "signal" -> DBUS_MESSAGE_TYPE_SIGNAL + * "error" -> DBUS_MESSAGE_TYPE_ERROR + * anything else -> DBUS_MESSAGE_TYPE_INVALID + * @endcode + * + */ +int +dbus_message_type_from_string (const char *type_str) +{ + if (strcmp (type_str, "method_call") == 0) + return DBUS_MESSAGE_TYPE_METHOD_CALL; + if (strcmp (type_str, "method_return") == 0) + return DBUS_MESSAGE_TYPE_METHOD_RETURN; + else if (strcmp (type_str, "signal") == 0) + return DBUS_MESSAGE_TYPE_SIGNAL; + else if (strcmp (type_str, "error") == 0) + return DBUS_MESSAGE_TYPE_ERROR; + else + return DBUS_MESSAGE_TYPE_INVALID; } - -static dbus_bool_t -check_message_handling_type (DBusMessageIter *iter, - int type) +/** + * Utility function to convert a D-BUS message type into a + * machine-readable string (not translated). + * + * @code + * DBUS_MESSAGE_TYPE_METHOD_CALL -> "method_call" + * DBUS_MESSAGE_TYPE_METHOD_RETURN -> "method_return" + * DBUS_MESSAGE_TYPE_SIGNAL -> "signal" + * DBUS_MESSAGE_TYPE_ERROR -> "error" + * DBUS_MESSAGE_TYPE_INVALID -> "invalid" + * @endcode + * + */ +const char * +dbus_message_type_to_string (int type) { - DBusMessageIter child_iter; - switch (type) { - case DBUS_TYPE_NIL: - break; - case DBUS_TYPE_BYTE: - dbus_message_iter_get_byte (iter); - break; - case DBUS_TYPE_BOOLEAN: - dbus_message_iter_get_boolean (iter); - break; - case DBUS_TYPE_INT32: - dbus_message_iter_get_int32 (iter); - break; - case DBUS_TYPE_UINT32: - dbus_message_iter_get_uint32 (iter); - break; - case DBUS_TYPE_INT64: -#ifdef DBUS_HAVE_INT64 - dbus_message_iter_get_int64 (iter); -#endif - break; - case DBUS_TYPE_UINT64: -#ifdef DBUS_HAVE_INT64 - dbus_message_iter_get_uint64 (iter); -#endif - break; - case DBUS_TYPE_DOUBLE: - dbus_message_iter_get_double (iter); - break; - case DBUS_TYPE_STRING: - { - char *str; - str = dbus_message_iter_get_string (iter); - if (str == NULL) - { - _dbus_warn ("NULL string in message\n"); - return FALSE; - } - dbus_free (str); - } - break; - case DBUS_TYPE_CUSTOM: - { - char *name; - unsigned char *data; - int len; - - if (!dbus_message_iter_get_custom (iter, &name, &data, &len)) - { - _dbus_warn ("error reading name from custom type\n"); - return FALSE; - } - dbus_free (data); - dbus_free (name); - } - break; - case DBUS_TYPE_ARRAY: - { - int array_type; - - dbus_message_iter_init_array_iterator (iter, &child_iter, &array_type); - - while (dbus_message_iter_has_next (&child_iter)) - { - if (!check_message_handling_type (&child_iter, array_type)) - { - _dbus_warn ("error in array element\n"); - return FALSE; - } - - if (!dbus_message_iter_next (&child_iter)) - break; - } - } - break; - case DBUS_TYPE_DICT: - { - int entry_type; - char *key; - - dbus_message_iter_init_dict_iterator (iter, &child_iter); - - while ((entry_type = dbus_message_iter_get_arg_type (&child_iter)) != DBUS_TYPE_INVALID) - { - key = dbus_message_iter_get_dict_key (&child_iter); - if (key == NULL) - { - _dbus_warn ("error reading dict key\n"); - return FALSE; - } - dbus_free (key); - - if (!check_message_handling_type (&child_iter, entry_type)) - { - _dbus_warn ("error in dict value\n"); - return FALSE; - } - - if (!dbus_message_iter_next (&child_iter)) - break; - } - } - break; - + case DBUS_MESSAGE_TYPE_METHOD_CALL: + return "method_call"; + case DBUS_MESSAGE_TYPE_METHOD_RETURN: + return "method_return"; + case DBUS_MESSAGE_TYPE_SIGNAL: + return "signal"; + case DBUS_MESSAGE_TYPE_ERROR: + return "error"; default: - _dbus_warn ("unknown type %d\n", type); - return FALSE; - break; + return "invalid"; } - return TRUE; } - - -static dbus_bool_t -check_message_handling (DBusMessage *message) -{ - DBusMessageIter iter; - int type; - dbus_bool_t retval; - dbus_uint32_t client_serial; - - retval = FALSE; - - client_serial = dbus_message_get_serial (message); - - /* can't use set_serial due to the assertions at the start of it */ - _dbus_marshal_set_uint32 (&message->header, - message->byte_order, - CLIENT_SERIAL_OFFSET, - client_serial); - - if (client_serial != dbus_message_get_serial (message)) - { - _dbus_warn ("get/set cycle for client_serial did not succeed\n"); - goto failed; - } - - /* If we implement message_set_arg (message, n, value) - * then we would want to test it here - */ - - dbus_message_iter_init (message, &iter); - while ((type = dbus_message_iter_get_arg_type (&iter)) != DBUS_TYPE_INVALID) - { - if (!check_message_handling_type (&iter, type)) - goto failed; - if (!dbus_message_iter_next (&iter)) - break; - } - - retval = TRUE; - - failed: - return retval; -} +/** @} */ +#ifdef DBUS_BUILD_TESTS +#include "dbus-test.h" +#include +#include static dbus_bool_t check_have_valid_message (DBusMessageLoader *loader) @@ -6550,33 +3592,36 @@ check_have_valid_message (DBusMessageLoader *loader) if (!_dbus_message_loader_queue_messages (loader)) _dbus_assert_not_reached ("no memory to queue messages"); - + if (_dbus_message_loader_get_is_corrupted (loader)) { _dbus_warn ("loader corrupted on message that was expected to be valid\n"); goto failed; } - + message = _dbus_message_loader_pop_message (loader); if (message == NULL) { _dbus_warn ("didn't load message that was expected to be valid (message not popped)\n"); goto failed; } - + if (_dbus_string_get_length (&loader->data) > 0) { _dbus_warn ("had leftover bytes from expected-to-be-valid single message\n"); goto failed; } +#if 0 + /* FIXME */ /* Verify that we're able to properly deal with the message. * For example, this would detect improper handling of messages * in nonstandard byte order. */ if (!check_message_handling (message)) - goto failed; - + goto failed; +#endif + retval = TRUE; failed: @@ -6595,7 +3640,7 @@ check_invalid_message (DBusMessageLoader *loader) if (!_dbus_message_loader_queue_messages (loader)) _dbus_assert_not_reached ("no memory to queue messages"); - + if (!_dbus_message_loader_get_is_corrupted (loader)) { _dbus_warn ("loader not corrupted on message that was expected to be invalid\n"); @@ -6619,13 +3664,13 @@ check_incomplete_message (DBusMessageLoader *loader) if (!_dbus_message_loader_queue_messages (loader)) _dbus_assert_not_reached ("no memory to queue messages"); - + if (_dbus_message_loader_get_is_corrupted (loader)) { _dbus_warn ("loader corrupted on message that was expected to be valid (but incomplete)\n"); goto failed; } - + message = _dbus_message_loader_pop_message (loader); if (message != NULL) { @@ -6647,7 +3692,7 @@ check_loader_results (DBusMessageLoader *loader, { if (!_dbus_message_loader_queue_messages (loader)) _dbus_assert_not_reached ("no memory to queue messages"); - + switch (validity) { case _DBUS_MESSAGE_VALID: @@ -6680,7 +3725,7 @@ dbus_internal_do_not_use_load_message_file (const DBusString *filename, { dbus_bool_t retval; - retval = FALSE; + retval = FALSE; if (is_raw) { @@ -6699,7 +3744,9 @@ dbus_internal_do_not_use_load_message_file (const DBusString *filename, } else { - if (!_dbus_message_data_load (data, filename)) + if (FALSE) /* Message builder disabled, probably permanently, + * I want to do it another way + */ { _dbus_warn ("Could not load message file %s\n", _dbus_string_get_const_data (filename)); @@ -6708,7 +3755,7 @@ dbus_internal_do_not_use_load_message_file (const DBusString *filename, } retval = TRUE; - + failed: return retval; @@ -6732,7 +3779,7 @@ dbus_internal_do_not_use_try_message_file (const DBusString *filename, dbus_bool_t retval; retval = FALSE; - + if (!_dbus_string_init (&data)) _dbus_assert_not_reached ("could not allocate string\n"); @@ -6749,11 +3796,11 @@ dbus_internal_do_not_use_try_message_file (const DBusString *filename, if (_dbus_string_get_length (&data) > 0) _dbus_verbose_bytes_of_string (&data, 0, _dbus_string_get_length (&data)); - + _dbus_warn ("Failed message loader test on %s\n", _dbus_string_get_const_data (filename)); } - + _dbus_string_free (&data); return retval; @@ -6780,14 +3827,14 @@ dbus_internal_do_not_use_try_message_data (const DBusString *data, retval = FALSE; /* Write the data one byte at a time */ - + loader = _dbus_message_loader_new (); /* check some trivial loader functions */ _dbus_message_loader_ref (loader); _dbus_message_loader_unref (loader); _dbus_message_loader_get_max_message_size (loader); - + len = _dbus_string_get_length (data); for (i = 0; i < len; i++) { @@ -6798,7 +3845,7 @@ dbus_internal_do_not_use_try_message_data (const DBusString *data, _dbus_string_get_byte (data, i)); _dbus_message_loader_return_buffer (loader, buffer, 1); } - + if (!check_loader_results (loader, expected_validity)) goto failed; @@ -6806,26 +3853,26 @@ dbus_internal_do_not_use_try_message_data (const DBusString *data, loader = NULL; /* Write the data all at once */ - + loader = _dbus_message_loader_new (); { DBusString *buffer; - + _dbus_message_loader_get_buffer (loader, &buffer); _dbus_string_copy (data, 0, buffer, _dbus_string_get_length (buffer)); _dbus_message_loader_return_buffer (loader, buffer, 1); } - + if (!check_loader_results (loader, expected_validity)) goto failed; _dbus_message_loader_unref (loader); - loader = NULL; + loader = NULL; /* Write the data 2 bytes at a time */ - + loader = _dbus_message_loader_new (); len = _dbus_string_get_length (data); @@ -6841,20 +3888,20 @@ dbus_internal_do_not_use_try_message_data (const DBusString *data, _dbus_string_get_byte (data, i+1)); _dbus_message_loader_return_buffer (loader, buffer, 1); } - + if (!check_loader_results (loader, expected_validity)) goto failed; _dbus_message_loader_unref (loader); loader = NULL; - + retval = TRUE; - + failed: - + if (loader) _dbus_message_loader_unref (loader); - + return retval; } @@ -6873,17 +3920,17 @@ process_test_subdir (const DBusString *test_base_dir, retval = FALSE; dir = NULL; - + if (!_dbus_string_init (&test_directory)) _dbus_assert_not_reached ("didn't allocate test_directory\n"); _dbus_string_init_const (&filename, subdir); - + if (!_dbus_string_copy (test_base_dir, 0, &test_directory, 0)) _dbus_assert_not_reached ("couldn't copy test_base_dir to test_directory"); - - if (!_dbus_concat_dir_and_file (&test_directory, &filename)) + + if (!_dbus_concat_dir_and_file (&test_directory, &filename)) _dbus_assert_not_reached ("couldn't allocate full path"); _dbus_string_free (&filename); @@ -6902,13 +3949,13 @@ process_test_subdir (const DBusString *test_base_dir, } printf ("Testing %s:\n", subdir); - + next: while (_dbus_directory_get_next_file (dir, &filename, &error)) { DBusString full_path; dbus_bool_t is_raw; - + if (!_dbus_string_init (&full_path)) _dbus_assert_not_reached ("couldn't init string"); @@ -6932,13 +3979,13 @@ process_test_subdir (const DBusString *test_base_dir, printf (" %s\n", _dbus_string_get_const_data (&filename)); - + _dbus_verbose (" expecting %s for %s\n", validity == _DBUS_MESSAGE_VALID ? "valid" : (validity == _DBUS_MESSAGE_INVALID ? "invalid" : (validity == _DBUS_MESSAGE_INCOMPLETE ? "incomplete" : "unknown")), _dbus_string_get_const_data (&filename)); - + if (! (*function) (&full_path, is_raw, validity, user_data)) { _dbus_string_free (&full_path); @@ -6956,9 +4003,9 @@ process_test_subdir (const DBusString *test_base_dir, dbus_error_free (&error); goto failed; } - + retval = TRUE; - + failed: if (dir) @@ -6968,7 +4015,7 @@ process_test_subdir (const DBusString *test_base_dir, return retval; } - + /** * Runs the given function on every message file in the test suite. * The function should return #FALSE on test failure or fatal error. @@ -6987,7 +4034,7 @@ dbus_internal_do_not_use_foreach_message_file (const char *test_d dbus_bool_t retval; retval = FALSE; - + _dbus_string_init_const (&test_directory, test_data_dir); if (!process_test_subdir (&test_directory, "valid-messages", @@ -6997,52 +4044,147 @@ dbus_internal_do_not_use_foreach_message_file (const char *test_d if (!process_test_subdir (&test_directory, "invalid-messages", _DBUS_MESSAGE_INVALID, func, user_data)) goto failed; - + if (!process_test_subdir (&test_directory, "incomplete-messages", _DBUS_MESSAGE_INCOMPLETE, func, user_data)) goto failed; retval = TRUE; - + failed: _dbus_string_free (&test_directory); - + return retval; } +#define GET_AND_CHECK(iter, typename, literal) \ + do { \ + if (dbus_message_iter_get_arg_type (&iter) != DBUS_TYPE_##typename) \ + _dbus_assert_not_reached ("got wrong argument type from message iter"); \ + dbus_message_iter_get_basic (&iter, &v_##typename); \ + if (v_##typename != literal) \ + _dbus_assert_not_reached ("got wrong value from message iter"); \ + } while (0) + +#define GET_AND_CHECK_STRCMP(iter, typename, literal) \ + do { \ + if (dbus_message_iter_get_arg_type (&iter) != DBUS_TYPE_##typename) \ + _dbus_assert_not_reached ("got wrong argument type from message iter"); \ + dbus_message_iter_get_basic (&iter, &v_##typename); \ + if (strcmp (v_##typename, literal) != 0) \ + _dbus_assert_not_reached ("got wrong value from message iter"); \ + } while (0) + +#define GET_AND_CHECK_AND_NEXT(iter, typename, literal) \ + do { \ + GET_AND_CHECK(iter, typename, literal); \ + if (!dbus_message_iter_next (&iter)) \ + _dbus_assert_not_reached ("failed to move iter to next"); \ + } while (0) + +#define GET_AND_CHECK_STRCMP_AND_NEXT(iter, typename, literal) \ + do { \ + GET_AND_CHECK_STRCMP(iter, typename, literal); \ + if (!dbus_message_iter_next (&iter)) \ + _dbus_assert_not_reached ("failed to move iter to next"); \ + } while (0) + +static void +message_iter_test (DBusMessage *message) +{ + DBusMessageIter iter, array, array2; + const char *v_STRING; + double v_DOUBLE; + dbus_int32_t v_INT32; + dbus_uint32_t v_UINT32; +#ifdef DBUS_HAVE_INT64 + dbus_int64_t v_INT64; + dbus_uint64_t v_UINT64; +#endif + unsigned char v_BYTE; + unsigned char v_BOOLEAN; + + const dbus_int32_t *our_int_array; + int len; + + dbus_message_iter_init (message, &iter); + + GET_AND_CHECK_STRCMP_AND_NEXT (iter, STRING, "Test string"); + GET_AND_CHECK_AND_NEXT (iter, INT32, -0x12345678); + GET_AND_CHECK_AND_NEXT (iter, UINT32, 0xedd1e); + GET_AND_CHECK_AND_NEXT (iter, DOUBLE, 3.14159); + + if (dbus_message_iter_get_arg_type (&iter) != DBUS_TYPE_ARRAY) + _dbus_assert_not_reached ("Argument type not an array"); + + if (dbus_message_iter_get_array_type (&iter) != DBUS_TYPE_DOUBLE) + _dbus_assert_not_reached ("Array type not double"); + + dbus_message_iter_recurse (&iter, &array); + + GET_AND_CHECK_AND_NEXT (array, DOUBLE, 1.5); + GET_AND_CHECK (array, DOUBLE, 2.5); + + if (dbus_message_iter_next (&array)) + _dbus_assert_not_reached ("Didn't reach end of array"); + + if (!dbus_message_iter_next (&iter)) + _dbus_assert_not_reached ("Reached end of arguments"); + + GET_AND_CHECK_AND_NEXT (iter, BYTE, 0xF0); + + if (dbus_message_iter_get_arg_type (&iter) != DBUS_TYPE_ARRAY) + _dbus_assert_not_reached ("no array"); + + if (dbus_message_iter_get_array_type (&iter) != DBUS_TYPE_INT32) + _dbus_assert_not_reached ("Array type not int32"); + + /* Empty array */ + dbus_message_iter_recurse (&iter, &array); + + if (dbus_message_iter_next (&array)) + _dbus_assert_not_reached ("Didn't reach end of array"); + + if (!dbus_message_iter_next (&iter)) + _dbus_assert_not_reached ("Reached end of arguments"); + + GET_AND_CHECK (iter, BYTE, 0xF0); + + if (dbus_message_iter_next (&iter)) + _dbus_assert_not_reached ("Didn't reach end of arguments"); +} + static void verify_test_message (DBusMessage *message) { - DBusMessageIter iter, dict; + DBusMessageIter iter; DBusError error; dbus_int32_t our_int; - char *our_str; + const char *our_str; double our_double; - dbus_bool_t our_bool; + unsigned char our_bool; unsigned char our_byte_1, our_byte_2; dbus_uint32_t our_uint32; - dbus_int32_t *our_uint32_array; + const dbus_int32_t *our_uint32_array = (void*)0xdeadbeef; int our_uint32_array_len; - dbus_int32_t *our_int32_array; + dbus_int32_t *our_int32_array = (void*)0xdeadbeef; int our_int32_array_len; - char **our_string_array; - int our_string_array_len; #ifdef DBUS_HAVE_INT64 dbus_int64_t our_int64; dbus_uint64_t our_uint64; - dbus_int64_t *our_uint64_array; + dbus_int64_t *our_uint64_array = (void*)0xdeadbeef; int our_uint64_array_len; - dbus_int64_t *our_int64_array; + const dbus_int64_t *our_int64_array = (void*)0xdeadbeef; int our_int64_array_len; #endif - double *our_double_array; + const double *our_double_array = (void*)0xdeadbeef; int our_double_array_len; - unsigned char *our_byte_array; + const unsigned char *our_byte_array = (void*)0xdeadbeef; int our_byte_array_len; - unsigned char *our_boolean_array; + const unsigned char *our_boolean_array = (void*)0xdeadbeef; int our_boolean_array_len; - + dbus_message_iter_init (message, &iter); dbus_error_init (&error); @@ -7057,7 +4199,6 @@ verify_test_message (DBusMessage *message) DBUS_TYPE_BOOLEAN, &our_bool, DBUS_TYPE_BYTE, &our_byte_1, DBUS_TYPE_BYTE, &our_byte_2, - DBUS_TYPE_NIL, DBUS_TYPE_ARRAY, DBUS_TYPE_UINT32, &our_uint32_array, &our_uint32_array_len, DBUS_TYPE_ARRAY, DBUS_TYPE_INT32, @@ -7068,8 +4209,6 @@ verify_test_message (DBusMessage *message) DBUS_TYPE_ARRAY, DBUS_TYPE_INT64, &our_int64_array, &our_int64_array_len, #endif - DBUS_TYPE_ARRAY, DBUS_TYPE_STRING, - &our_string_array, &our_string_array_len, DBUS_TYPE_ARRAY, DBUS_TYPE_DOUBLE, &our_double_array, &our_double_array_len, DBUS_TYPE_ARRAY, DBUS_TYPE_BYTE, @@ -7092,13 +4231,12 @@ verify_test_message (DBusMessage *message) if (our_uint64 != DBUS_UINT64_CONSTANT (0x123456789abcd)) _dbus_assert_not_reached ("64-bit unsigned integers differ!"); #endif - + if (our_double != 3.14159) _dbus_assert_not_reached ("doubles differ!"); if (strcmp (our_str, "Test string") != 0) _dbus_assert_not_reached ("strings differ!"); - dbus_free (our_str); if (!our_bool) _dbus_assert_not_reached ("booleans differ"); @@ -7115,7 +4253,6 @@ verify_test_message (DBusMessage *message) our_uint32_array[2] != 0x34567812 || our_uint32_array[3] != 0x45678123) _dbus_assert_not_reached ("uint array differs"); - dbus_free (our_uint32_array); if (our_int32_array_len != 4 || our_int32_array[0] != 0x12345678 || @@ -7123,7 +4260,6 @@ verify_test_message (DBusMessage *message) our_int32_array[2] != 0x34567812 || our_int32_array[3] != -0x45678123) _dbus_assert_not_reached ("int array differs"); - dbus_free (our_int32_array); #ifdef DBUS_HAVE_INT64 if (our_uint64_array_len != 4 || @@ -7132,27 +4268,14 @@ verify_test_message (DBusMessage *message) our_uint64_array[2] != 0x34567812 || our_uint64_array[3] != 0x45678123) _dbus_assert_not_reached ("uint64 array differs"); - dbus_free (our_uint64_array); - + if (our_int64_array_len != 4 || our_int64_array[0] != 0x12345678 || our_int64_array[1] != -0x23456781 || our_int64_array[2] != 0x34567812 || our_int64_array[3] != -0x45678123) _dbus_assert_not_reached ("int64 array differs"); - dbus_free (our_int64_array); #endif /* DBUS_HAVE_INT64 */ - - if (our_string_array_len != 4) - _dbus_assert_not_reached ("string array has wrong length"); - - if (strcmp (our_string_array[0], "Foo") != 0 || - strcmp (our_string_array[1], "bar") != 0 || - strcmp (our_string_array[2], "") != 0 || - strcmp (our_string_array[3], "woo woo woo woo") != 0) - _dbus_assert_not_reached ("string array differs"); - - dbus_free_string_array (our_string_array); if (our_double_array_len != 3) _dbus_assert_not_reached ("double array had wrong length"); @@ -7165,8 +4288,6 @@ verify_test_message (DBusMessage *message) our_double_array[2] != -300.0) _dbus_assert_not_reached ("double array had wrong values"); - dbus_free (our_double_array); - if (our_byte_array_len != 4) _dbus_assert_not_reached ("byte array had wrong length"); @@ -7176,8 +4297,6 @@ verify_test_message (DBusMessage *message) our_byte_array[3] != 234) _dbus_assert_not_reached ("byte array had wrong values"); - dbus_free (our_byte_array); - if (our_boolean_array_len != 5) _dbus_assert_not_reached ("bool array had wrong length"); @@ -7188,42 +4307,6 @@ verify_test_message (DBusMessage *message) our_boolean_array[4] != FALSE) _dbus_assert_not_reached ("bool array had wrong values"); - dbus_free (our_boolean_array); - - if (dbus_message_iter_get_arg_type (&iter) != DBUS_TYPE_DICT) - _dbus_assert_not_reached ("not dict type"); - - dbus_message_iter_init_dict_iterator (&iter, &dict); - - our_str = dbus_message_iter_get_dict_key (&dict); - if (our_str == NULL || strcmp (our_str, "test") != 0) - _dbus_assert_not_reached ("wrong dict key"); - dbus_free (our_str); - - if (dbus_message_iter_get_arg_type (&dict) != DBUS_TYPE_UINT32) - { - _dbus_verbose ("dict entry type: %d\n", dbus_message_iter_get_arg_type (&dict)); - _dbus_assert_not_reached ("wrong dict entry type"); - } - - if ((our_uint32 = dbus_message_iter_get_uint32 (&dict)) != 0xDEADBEEF) - { - _dbus_verbose ("dict entry val: %x\n", our_uint32); - _dbus_assert_not_reached ("wrong dict entry value"); - } - - if (dbus_message_iter_next (&dict)) - _dbus_assert_not_reached ("Didn't reach end of dict"); - - if (!dbus_message_iter_next (&iter)) - _dbus_assert_not_reached ("Reached end of arguments"); - - if (dbus_message_iter_get_arg_type (&iter) != DBUS_TYPE_UINT32) - _dbus_assert_not_reached ("wrong type after dict"); - - if (dbus_message_iter_get_uint32 (&iter) != 0xCAFEBABE) - _dbus_assert_not_reached ("wrong value after dict"); - if (dbus_message_iter_next (&iter)) _dbus_assert_not_reached ("Didn't reach end of arguments"); } @@ -7249,21 +4332,40 @@ _dbus_message_test (const char *test_data_dir) { 0x12345678, 0x23456781, 0x34567812, 0x45678123 }; const dbus_uint32_t our_int32_array[] = { 0x12345678, -0x23456781, 0x34567812, -0x45678123 }; + const dbus_uint32_t *v_ARRAY_UINT32 = our_uint32_array; + const dbus_int32_t *v_ARRAY_INT32 = our_int32_array; #ifdef DBUS_HAVE_INT64 const dbus_uint64_t our_uint64_array[] = { 0x12345678, 0x23456781, 0x34567812, 0x45678123 }; const dbus_uint64_t our_int64_array[] = { 0x12345678, -0x23456781, 0x34567812, -0x45678123 }; + const dbus_uint64_t *v_ARRAY_UINT64 = our_uint64_array; + const dbus_int64_t *v_ARRAY_INT64 = our_int64_array; #endif const char *our_string_array[] = { "Foo", "bar", "", "woo woo woo woo" }; + const char **v_ARRAY_STRING = our_string_array; const double our_double_array[] = { 0.1234, 9876.54321, -300.0 }; + const double *v_ARRAY_DOUBLE = our_double_array; const unsigned char our_byte_array[] = { 'a', 'b', 'c', 234 }; + const unsigned char *v_ARRAY_BYTE = our_byte_array; const unsigned char our_boolean_array[] = { TRUE, FALSE, TRUE, TRUE, FALSE }; + const unsigned char *v_ARRAY_BOOLEAN = our_boolean_array; char sig[64]; const char *s; char *t; DBusError error; - + const char *v_STRING; + double v_DOUBLE; + dbus_int32_t v_INT32; + dbus_uint32_t v_UINT32; +#ifdef DBUS_HAVE_INT64 + dbus_int64_t v_INT64; + dbus_uint64_t v_UINT64; +#endif + unsigned char v_BYTE; + unsigned char v2_BYTE; + unsigned char v_BOOLEAN; + _dbus_assert (sizeof (DBusMessageRealIter) <= sizeof (DBusMessageIter)); message = dbus_message_new_method_call ("org.freedesktop.DBus.TestService", @@ -7276,13 +4378,29 @@ _dbus_message_test (const char *test_data_dir) _dbus_assert (strcmp (dbus_message_get_path (message), "/org/freedesktop/TestPath") == 0); _dbus_message_set_serial (message, 1234); + /* string length including nul byte not a multiple of 4 */ if (!dbus_message_set_sender (message, "org.foo.bar1")) _dbus_assert_not_reached ("out of memory"); + _dbus_assert (dbus_message_has_sender (message, "org.foo.bar1")); dbus_message_set_reply_serial (message, 5678); + + _dbus_verbose_bytes_of_string (&message->header.data, 0, + _dbus_string_get_length (&message->header.data)); + _dbus_verbose_bytes_of_string (&message->body, 0, + _dbus_string_get_length (&message->body)); + if (!dbus_message_set_sender (message, NULL)) _dbus_assert_not_reached ("out of memory"); + + + _dbus_verbose_bytes_of_string (&message->header.data, 0, + _dbus_string_get_length (&message->header.data)); + _dbus_verbose_bytes_of_string (&message->body, 0, + _dbus_string_get_length (&message->body)); + + _dbus_assert (!dbus_message_has_sender (message, "org.foo.bar1")); _dbus_assert (dbus_message_get_serial (message) == 1234); _dbus_assert (dbus_message_get_reply_serial (message) == 5678); @@ -7295,7 +4413,7 @@ _dbus_message_test (const char *test_data_dir) _dbus_assert (dbus_message_get_no_reply (message) == FALSE); /* Set/get some header fields */ - + if (!dbus_message_set_path (message, "/foo")) _dbus_assert_not_reached ("out of memory"); _dbus_assert (strcmp (dbus_message_get_path (message), @@ -7305,7 +4423,7 @@ _dbus_message_test (const char *test_data_dir) _dbus_assert_not_reached ("out of memory"); _dbus_assert (strcmp (dbus_message_get_interface (message), "org.Foo") == 0); - + if (!dbus_message_set_member (message, "Bar")) _dbus_assert_not_reached ("out of memory"); _dbus_assert (strcmp (dbus_message_get_member (message), @@ -7321,14 +4439,14 @@ _dbus_message_test (const char *test_data_dir) _dbus_assert_not_reached ("out of memory"); _dbus_assert (strcmp (dbus_message_get_interface (message), "org.Foo.Bar") == 0); - + if (!dbus_message_set_member (message, "BarFoo")) _dbus_assert_not_reached ("out of memory"); _dbus_assert (strcmp (dbus_message_get_member (message), "BarFoo") == 0); /* Realloc shorter again */ - + if (!dbus_message_set_path (message, "/foo")) _dbus_assert_not_reached ("out of memory"); _dbus_assert (strcmp (dbus_message_get_path (message), @@ -7338,57 +4456,60 @@ _dbus_message_test (const char *test_data_dir) _dbus_assert_not_reached ("out of memory"); _dbus_assert (strcmp (dbus_message_get_interface (message), "org.Foo") == 0); - + if (!dbus_message_set_member (message, "Bar")) _dbus_assert_not_reached ("out of memory"); _dbus_assert (strcmp (dbus_message_get_member (message), "Bar") == 0); - + dbus_message_unref (message); - + /* Test the vararg functions */ message = dbus_message_new_method_call ("org.freedesktop.DBus.TestService", "/org/freedesktop/TestPath", "Foo.TestInterface", "TestMethod"); _dbus_message_set_serial (message, 1); + + v_INT32 = -0x12345678; +#ifdef DBUS_HAVE_INT64 + v_INT64 = DBUS_INT64_CONSTANT (-0x123456789abcd); + v_UINT64 = DBUS_UINT64_CONSTANT (0x123456789abcd); +#endif + v_STRING = "Test string"; + v_DOUBLE = 3.14159; + v_BOOLEAN = TRUE; + v_BYTE = 42; + v2_BYTE = 24; + dbus_message_append_args (message, - DBUS_TYPE_INT32, -0x12345678, + DBUS_TYPE_INT32, &v_INT32, #ifdef DBUS_HAVE_INT64 - DBUS_TYPE_INT64, DBUS_INT64_CONSTANT (-0x123456789abcd), - DBUS_TYPE_UINT64, DBUS_UINT64_CONSTANT (0x123456789abcd), + DBUS_TYPE_INT64, &v_INT64, + DBUS_TYPE_UINT64, &v_UINT64, #endif - DBUS_TYPE_STRING, "Test string", - DBUS_TYPE_DOUBLE, 3.14159, - DBUS_TYPE_BOOLEAN, TRUE, - DBUS_TYPE_BYTE, (unsigned char) 42, - DBUS_TYPE_BYTE, 24, - DBUS_TYPE_NIL, - DBUS_TYPE_ARRAY, DBUS_TYPE_UINT32, our_uint32_array, + DBUS_TYPE_STRING, &v_STRING, + DBUS_TYPE_DOUBLE, &v_DOUBLE, + DBUS_TYPE_BOOLEAN, &v_BOOLEAN, + DBUS_TYPE_BYTE, &v_BYTE, + DBUS_TYPE_BYTE, &v2_BYTE, + DBUS_TYPE_ARRAY, DBUS_TYPE_UINT32, &v_ARRAY_UINT32, _DBUS_N_ELEMENTS (our_uint32_array), - DBUS_TYPE_ARRAY, DBUS_TYPE_INT32, our_int32_array, + DBUS_TYPE_ARRAY, DBUS_TYPE_INT32, &v_ARRAY_INT32, _DBUS_N_ELEMENTS (our_int32_array), #ifdef DBUS_HAVE_INT64 - DBUS_TYPE_ARRAY, DBUS_TYPE_UINT64, our_uint64_array, + DBUS_TYPE_ARRAY, DBUS_TYPE_UINT64, &v_ARRAY_UINT64, _DBUS_N_ELEMENTS (our_uint64_array), - DBUS_TYPE_ARRAY, DBUS_TYPE_INT64, our_int64_array, + DBUS_TYPE_ARRAY, DBUS_TYPE_INT64, &v_ARRAY_INT64, _DBUS_N_ELEMENTS (our_int64_array), #endif - DBUS_TYPE_ARRAY, DBUS_TYPE_STRING, our_string_array, - _DBUS_N_ELEMENTS (our_string_array), - DBUS_TYPE_ARRAY, DBUS_TYPE_DOUBLE, our_double_array, + DBUS_TYPE_ARRAY, DBUS_TYPE_DOUBLE, &v_ARRAY_DOUBLE, _DBUS_N_ELEMENTS (our_double_array), - DBUS_TYPE_ARRAY, DBUS_TYPE_BYTE, our_byte_array, + DBUS_TYPE_ARRAY, DBUS_TYPE_BYTE, &v_ARRAY_BYTE, _DBUS_N_ELEMENTS (our_byte_array), - DBUS_TYPE_ARRAY, DBUS_TYPE_BOOLEAN, our_boolean_array, + DBUS_TYPE_ARRAY, DBUS_TYPE_BOOLEAN, &v_ARRAY_BOOLEAN, _DBUS_N_ELEMENTS (our_boolean_array), - 0); - - dbus_message_append_iter_init (message, &iter); - dbus_message_iter_append_dict (&iter, &child_iter); - dbus_message_iter_append_dict_key (&child_iter, "test"); - dbus_message_iter_append_uint32 (&child_iter, 0xDEADBEEF); - dbus_message_iter_append_uint32 (&iter, 0xCAFEBABE); + DBUS_TYPE_INVALID); i = 0; sig[i++] = DBUS_TYPE_INT32; @@ -7401,7 +4522,6 @@ _dbus_message_test (const char *test_data_dir) sig[i++] = DBUS_TYPE_BOOLEAN; sig[i++] = DBUS_TYPE_BYTE; sig[i++] = DBUS_TYPE_BYTE; - sig[i++] = DBUS_TYPE_NIL; sig[i++] = DBUS_TYPE_ARRAY; sig[i++] = DBUS_TYPE_UINT32; sig[i++] = DBUS_TYPE_ARRAY; @@ -7412,47 +4532,45 @@ _dbus_message_test (const char *test_data_dir) sig[i++] = DBUS_TYPE_ARRAY; sig[i++] = DBUS_TYPE_INT64; #endif - sig[i++] = DBUS_TYPE_ARRAY; - sig[i++] = DBUS_TYPE_STRING; sig[i++] = DBUS_TYPE_ARRAY; sig[i++] = DBUS_TYPE_DOUBLE; sig[i++] = DBUS_TYPE_ARRAY; sig[i++] = DBUS_TYPE_BYTE; sig[i++] = DBUS_TYPE_ARRAY; sig[i++] = DBUS_TYPE_BOOLEAN; - sig[i++] = DBUS_TYPE_DICT; - sig[i++] = DBUS_TYPE_UINT32; sig[i++] = DBUS_TYPE_INVALID; _dbus_assert (i < (int) _DBUS_N_ELEMENTS (sig)); - - _dbus_verbose_bytes_of_string (&message->header, 0, - _dbus_string_get_length (&message->header)); + + _dbus_verbose ("HEADER\n"); + _dbus_verbose_bytes_of_string (&message->header.data, 0, + _dbus_string_get_length (&message->header.data)); + _dbus_verbose ("BODY\n"); _dbus_verbose_bytes_of_string (&message->body, 0, _dbus_string_get_length (&message->body)); - + _dbus_verbose ("Signature expected \"%s\" actual \"%s\"\n", sig, dbus_message_get_signature (message)); - + s = dbus_message_get_signature (message); - + _dbus_assert (dbus_message_has_signature (message, sig)); _dbus_assert (strcmp (s, sig) == 0); - + verify_test_message (message); copy = dbus_message_copy (message); - - _dbus_assert (message->client_serial == copy->client_serial); - _dbus_assert (message->reply_serial == copy->reply_serial); - _dbus_assert (message->header_padding == copy->header_padding); - - _dbus_assert (_dbus_string_get_length (&message->header) == - _dbus_string_get_length (©->header)); + + _dbus_assert (dbus_message_get_reply_serial (message) == + dbus_message_get_reply_serial (copy)); + _dbus_assert (message->header.padding == copy->header.padding); + + _dbus_assert (_dbus_string_get_length (&message->header.data) == + _dbus_string_get_length (©->header.data)); _dbus_assert (_dbus_string_get_length (&message->body) == _dbus_string_get_length (©->body)); - + verify_test_message (copy); name1 = dbus_message_get_interface (message); @@ -7464,10 +4582,12 @@ _dbus_message_test (const char *test_data_dir) name2 = dbus_message_get_member (copy); _dbus_assert (strcmp (name1, name2) == 0); - - dbus_message_unref (message); + + dbus_message_unref (message); dbus_message_unref (copy); +#if 0 + /* FIXME */ message = dbus_message_new_method_call ("org.freedesktop.DBus.TestService", "/org/freedesktop/TestPath", "Foo.TestInterface", @@ -7510,14 +4630,14 @@ _dbus_message_test (const char *test_data_dir) dbus_message_iter_append_int32 (&child_iter3, 0x34567812); dbus_message_iter_append_int32 (&child_iter3, 0x45678123); dbus_message_iter_append_int32 (&child_iter3, 0x56781234); - + dbus_message_iter_append_byte (&iter, 0xF0); dbus_message_iter_append_nil (&iter); dbus_message_iter_append_custom (&iter, "MyTypeName", "data", 5); - + dbus_message_iter_append_byte (&iter, 0xF0); dbus_message_iter_append_array (&iter, &child_iter, DBUS_TYPE_INT32); @@ -7529,7 +4649,7 @@ _dbus_message_test (const char *test_data_dir) dbus_message_iter_append_byte (&iter, 0xF0); message_iter_test (message); - + /* Message loader test */ _dbus_message_lock (message); loader = _dbus_message_loader_new (); @@ -7537,7 +4657,7 @@ _dbus_message_test (const char *test_data_dir) /* check ref/unref */ _dbus_message_loader_ref (loader); _dbus_message_loader_unref (loader); - + /* Write the header data one byte at a time */ data = _dbus_string_get_const_data (&message->header); for (i = 0; i < _dbus_string_get_length (&message->header); i++) @@ -7566,19 +4686,19 @@ _dbus_message_test (const char *test_data_dir) /* Now pop back the message */ if (!_dbus_message_loader_queue_messages (loader)) _dbus_assert_not_reached ("no memory to queue messages"); - + if (_dbus_message_loader_get_is_corrupted (loader)) _dbus_assert_not_reached ("message loader corrupted"); - + message = _dbus_message_loader_pop_message (loader); if (!message) _dbus_assert_not_reached ("received a NULL message"); if (dbus_message_get_reply_serial (message) != 0x12345678) _dbus_assert_not_reached ("reply serial fields differ"); - + message_iter_test (message); - + dbus_message_unref (message); _dbus_message_loader_unref (loader); @@ -7598,13 +4718,13 @@ _dbus_message_test (const char *test_data_dir) dbus_error_init (&error); if (!dbus_message_get_args (message, &error, DBUS_TYPE_STRING, &t, DBUS_TYPE_INVALID)) - + { _dbus_warn ("Failed to get expected string arg: %s\n", error.message); exit (1); } dbus_free (t); - + dbus_message_unref (message); /* This ServiceAcquired message used to trigger a bug in @@ -7613,14 +4733,14 @@ _dbus_message_test (const char *test_data_dir) message = dbus_message_new_signal (DBUS_PATH_ORG_FREEDESKTOP_DBUS, DBUS_INTERFACE_ORG_FREEDESKTOP_DBUS, "ServiceAcquired"); - + if (message == NULL) _dbus_assert_not_reached ("out of memory"); _dbus_verbose ("Bytes after creation\n"); _dbus_verbose_bytes_of_string (&message->header, 0, _dbus_string_get_length (&message->header)); - + if (!dbus_message_set_destination (message, ":1.0") || !dbus_message_append_args (message, DBUS_TYPE_STRING, ":1.0", @@ -7630,7 +4750,7 @@ _dbus_message_test (const char *test_data_dir) _dbus_verbose ("Bytes after set_destination() and append_args()\n"); _dbus_verbose_bytes_of_string (&message->header, 0, _dbus_string_get_length (&message->header)); - + if (!dbus_message_set_sender (message, "org.freedesktop.DBus")) _dbus_assert_not_reached ("out of memory"); @@ -7647,7 +4767,7 @@ _dbus_message_test (const char *test_data_dir) dbus_message_get_signature (message)); _dbus_assert_not_reached ("signal has wrong signature"); } - + /* have to set destination again to reproduce the bug */ if (!dbus_message_set_destination (message, ":1.0")) _dbus_assert_not_reached ("out of memory"); @@ -7655,7 +4775,7 @@ _dbus_message_test (const char *test_data_dir) _dbus_verbose ("Bytes after set_destination()\n"); _dbus_verbose_bytes_of_string (&message->header, 0, _dbus_string_get_length (&message->header)); - + /* When the bug happened the above set_destination() would * corrupt the signature */ @@ -7669,15 +4789,15 @@ _dbus_message_test (const char *test_data_dir) dbus_error_init (&error); if (!dbus_message_get_args (message, &error, DBUS_TYPE_STRING, &t, DBUS_TYPE_INVALID)) - + { _dbus_warn ("Failed to get expected string arg for signal: %s\n", error.message); exit (1); } - dbus_free (t); - + dbus_free (t); + dbus_message_unref (message); - + /* Now load every message in test_data_dir if we have one */ if (test_data_dir == NULL) return TRUE; @@ -7686,6 +4806,10 @@ _dbus_message_test (const char *test_data_dir) (DBusForeachMessageFileFunc) dbus_internal_do_not_use_try_message_file, NULL); + +#endif /* Commented out most tests for now */ + + return TRUE; } #endif /* DBUS_BUILD_TESTS */ diff --git a/dbus/dbus-message.h b/dbus/dbus-message.h index e1894c72..fd6b96d4 100644 --- a/dbus/dbus-message.h +++ b/dbus/dbus-message.h @@ -144,14 +144,6 @@ dbus_bool_t dbus_message_get_args_valist (DBusMessage *message, DBusError *error, int first_arg_type, va_list var_args); -dbus_bool_t dbus_message_iter_get_args (DBusMessageIter *iter, - DBusError *error, - int first_arg_type, - ...); -dbus_bool_t dbus_message_iter_get_args_valist (DBusMessageIter *iter, - DBusError *error, - int first_arg_type, - va_list var_args); dbus_bool_t dbus_message_iter_init (DBusMessage *message, @@ -160,125 +152,29 @@ dbus_bool_t dbus_message_iter_has_next (DBusMessageIter *iter dbus_bool_t dbus_message_iter_next (DBusMessageIter *iter); int dbus_message_iter_get_arg_type (DBusMessageIter *iter); int dbus_message_iter_get_array_type (DBusMessageIter *iter); -unsigned char dbus_message_iter_get_byte (DBusMessageIter *iter); -dbus_bool_t dbus_message_iter_get_boolean (DBusMessageIter *iter); -dbus_int32_t dbus_message_iter_get_int32 (DBusMessageIter *iter); -dbus_uint32_t dbus_message_iter_get_uint32 (DBusMessageIter *iter); -#ifdef DBUS_HAVE_INT64 -dbus_int64_t dbus_message_iter_get_int64 (DBusMessageIter *iter); -dbus_uint64_t dbus_message_iter_get_uint64 (DBusMessageIter *iter); -#endif /* DBUS_HAVE_INT64 */ -double dbus_message_iter_get_double (DBusMessageIter *iter); -char * dbus_message_iter_get_string (DBusMessageIter *iter); -char * dbus_message_iter_get_object_path (DBusMessageIter *iter); -char * dbus_message_iter_get_dict_key (DBusMessageIter *iter); -dbus_bool_t dbus_message_iter_get_custom (DBusMessageIter *iter, - char **name, - unsigned char **value, - int *len); - -dbus_bool_t dbus_message_iter_init_array_iterator (DBusMessageIter *iter, - DBusMessageIter *array_iter, - int *array_type); -dbus_bool_t dbus_message_iter_init_dict_iterator (DBusMessageIter *iter, - DBusMessageIter *dict_iter); -dbus_bool_t dbus_message_iter_get_byte_array (DBusMessageIter *iter, - unsigned char **value, - int *len); -dbus_bool_t dbus_message_iter_get_boolean_array (DBusMessageIter *iter, - unsigned char **value, - int *len); -dbus_bool_t dbus_message_iter_get_int32_array (DBusMessageIter *iter, - dbus_int32_t **value, - int *len); -dbus_bool_t dbus_message_iter_get_uint32_array (DBusMessageIter *iter, - dbus_uint32_t **value, - int *len); -#ifdef DBUS_HAVE_INT64 -dbus_bool_t dbus_message_iter_get_int64_array (DBusMessageIter *iter, - dbus_int64_t **value, - int *len); -dbus_bool_t dbus_message_iter_get_uint64_array (DBusMessageIter *iter, - dbus_uint64_t **value, - int *len); -#endif /* DBUS_HAVE_INT64 */ -dbus_bool_t dbus_message_iter_get_double_array (DBusMessageIter *iter, - double **value, - int *len); -dbus_bool_t dbus_message_iter_get_string_array (DBusMessageIter *iter, - char ***value, - int *len); -dbus_bool_t dbus_message_iter_get_object_path_array (DBusMessageIter *iter, - char ***value, - int *len); - - -void dbus_message_append_iter_init (DBusMessage *message, - DBusMessageIter *iter); -dbus_bool_t dbus_message_iter_append_nil (DBusMessageIter *iter); -dbus_bool_t dbus_message_iter_append_boolean (DBusMessageIter *iter, - dbus_bool_t value); -dbus_bool_t dbus_message_iter_append_byte (DBusMessageIter *iter, - unsigned char value); -dbus_bool_t dbus_message_iter_append_int32 (DBusMessageIter *iter, - dbus_int32_t value); -dbus_bool_t dbus_message_iter_append_uint32 (DBusMessageIter *iter, - dbus_uint32_t value); -#ifdef DBUS_HAVE_INT64 -dbus_bool_t dbus_message_iter_append_int64 (DBusMessageIter *iter, - dbus_int64_t value); -dbus_bool_t dbus_message_iter_append_uint64 (DBusMessageIter *iter, - dbus_uint64_t value); -#endif /* DBUS_HAVE_INT64 */ -dbus_bool_t dbus_message_iter_append_double (DBusMessageIter *iter, - double value); -dbus_bool_t dbus_message_iter_append_string (DBusMessageIter *iter, - const char *value); -dbus_bool_t dbus_message_iter_append_object_path (DBusMessageIter *iter, - const char *value); -dbus_bool_t dbus_message_iter_append_custom (DBusMessageIter *iter, - const char *name, - const unsigned char *data, - int len); -dbus_bool_t dbus_message_iter_append_dict_key (DBusMessageIter *iter, - const char *value); -dbus_bool_t dbus_message_iter_append_array (DBusMessageIter *iter, - DBusMessageIter *array_iter, - int element_type); -dbus_bool_t dbus_message_iter_append_dict (DBusMessageIter *iter, - DBusMessageIter *dict_iter); - -/* Helpers for normal types: */ -dbus_bool_t dbus_message_iter_append_boolean_array (DBusMessageIter *iter, - unsigned const char *value, - int len); -dbus_bool_t dbus_message_iter_append_int32_array (DBusMessageIter *iter, - const dbus_int32_t *value, - int len); -dbus_bool_t dbus_message_iter_append_uint32_array (DBusMessageIter *iter, - const dbus_uint32_t *value, - int len); -#ifdef DBUS_HAVE_INT64 -dbus_bool_t dbus_message_iter_append_int64_array (DBusMessageIter *iter, - const dbus_int64_t *value, - int len); -dbus_bool_t dbus_message_iter_append_uint64_array (DBusMessageIter *iter, - const dbus_uint64_t *value, - int len); -#endif /* DBUS_HAVE_INT64 */ -dbus_bool_t dbus_message_iter_append_double_array (DBusMessageIter *iter, - const double *value, - int len); -dbus_bool_t dbus_message_iter_append_byte_array (DBusMessageIter *iter, - unsigned const char *value, - int len); -dbus_bool_t dbus_message_iter_append_string_array (DBusMessageIter *iter, - const char **value, - int len); -dbus_bool_t dbus_message_iter_append_object_path_array (DBusMessageIter *iter, - const char **value, - int len); - +void dbus_message_iter_recurse (DBusMessageIter *iter, + DBusMessageIter *sub); +void dbus_message_iter_get_basic (DBusMessageIter *iter, + void *value); +void dbus_message_iter_get_fixed_array (DBusMessageIter *iter, + void *value, + int *n_elements); + +void dbus_message_append_iter_init (DBusMessage *message, + DBusMessageIter *iter); +dbus_bool_t dbus_message_iter_append_basic (DBusMessageIter *iter, + int type, + const void *value); +dbus_bool_t dbus_message_iter_append_fixed_array (DBusMessageIter *iter, + int element_type, + const void *value, + int n_elements); +dbus_bool_t dbus_message_iter_open_container (DBusMessageIter *iter, + int type, + const char *contained_signature, + DBusMessageIter *sub); +dbus_bool_t dbus_message_iter_close_container (DBusMessageIter *iter, + DBusMessageIter *sub); dbus_bool_t dbus_set_error_from_message (DBusError *error, diff --git a/dbus/dbus-object-tree.c b/dbus/dbus-object-tree.c index afce2442..694185fb 100644 --- a/dbus/dbus-object-tree.c +++ b/dbus/dbus-object-tree.c @@ -937,7 +937,96 @@ _dbus_object_tree_list_registered_and_unlock (DBusObjectTree *tree, return result; } - + + +/** Set to 1 to get a bunch of spew about disassembling the path string */ +#define VERBOSE_DECOMPOSE 0 + +/** + * Decompose an object path. A path of just "/" is + * represented as an empty vector of strings. + * + * @param data the path data + * @param len the length of the path string + * @param path address to store new object path + * @param path_len length of stored path + */ +dbus_bool_t +_dbus_decompose_path (const char* data, + int len, + char ***path, + int *path_len) +{ + char **retval; + int n_components; + int i, j, comp; + + _dbus_assert (data != NULL); + +#if VERBOSE_DECOMPOSE + _dbus_verbose ("Decomposing path \"%s\"\n", + data); +#endif + + n_components = 0; + i = 0; + while (i < len) + { + if (data[i] == '/') + n_components += 1; + ++i; + } + + retval = dbus_new0 (char*, n_components + 1); + + if (retval == NULL) + return FALSE; + + comp = 0; + i = 0; + while (i < len) + { + if (data[i] == '/') + ++i; + j = i; + + while (j < len && data[j] != '/') + ++j; + + /* Now [i, j) is the path component */ + _dbus_assert (i < j); + _dbus_assert (data[i] != '/'); + _dbus_assert (j == len || data[j] == '/'); + +#if VERBOSE_DECOMPOSE + _dbus_verbose (" (component in [%d,%d))\n", + i, j); +#endif + + retval[comp] = _dbus_memdup (&data[i], j - i + 1); + if (retval[comp] == NULL) + { + dbus_free_string_array (retval); + return FALSE; + } + retval[comp][j-i] = '\0'; +#if VERBOSE_DECOMPOSE + _dbus_verbose (" (component %d = \"%s\")\n", + comp, retval[comp]); +#endif + + ++comp; + i = j; + } + _dbus_assert (i == len); + + *path = retval; + if (path_len) + *path_len = n_components; + + return TRUE; +} + /** @} */ #ifdef DBUS_BUILD_TESTS diff --git a/dbus/dbus-object-tree.h b/dbus/dbus-object-tree.h index 52d5bca1..6d8484e0 100644 --- a/dbus/dbus-object-tree.h +++ b/dbus/dbus-object-tree.h @@ -47,6 +47,12 @@ void _dbus_object_tree_free_all_unlocked (DBusObjectTree dbus_bool_t _dbus_object_tree_list_registered_and_unlock (DBusObjectTree *tree, const char **parent_path, char ***child_entries); + +dbus_bool_t _dbus_decompose_path (const char *data, + int len, + char ***path, + int *path_len); + DBUS_END_DECLS #endif /* DBUS_OBJECT_TREE_H */ diff --git a/dbus/dbus-protocol-new.h b/dbus/dbus-protocol-new.h deleted file mode 100644 index 482500f2..00000000 --- a/dbus/dbus-protocol-new.h +++ /dev/null @@ -1,226 +0,0 @@ -/* -*- mode: C; c-file-style: "gnu" -*- */ -/* dbus-protocol.h D-Bus protocol constants - * - * Copyright (C) 2002, 2003 CodeFactory AB - * Copyright (C) 2004 Red Hat, Inc. - * - * Licensed under the Academic Free License version 2.1 - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation; either version 2 of the License, or - * (at your option) any later version. - * - * This program 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 General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program; if not, write to the Free Software - * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA - * - */ - -#ifndef DBUS_PROTOCOL_H -#define DBUS_PROTOCOL_H - -/* Don't include anything in here from anywhere else. It's - * intended for use by any random library. - */ - -#ifdef __cplusplus -extern "C" { -#endif - -/* Message byte order */ -#define DBUS_LITTLE_ENDIAN ('l') /* LSB first */ -#define DBUS_BIG_ENDIAN ('B') /* MSB first */ - -/* Protocol version */ -#define DBUS_MAJOR_PROTOCOL_VERSION 1 - -/* Never a legitimate type */ -#define DBUS_TYPE_INVALID ((int) '\0') -#define DBUS_TYPE_INVALID_AS_STRING "\0" - -/* Primitive types */ -#define DBUS_TYPE_BYTE ((int) 'y') -#define DBUS_TYPE_BYTE_AS_STRING "y" -#define DBUS_TYPE_BOOLEAN ((int) 'b') -#define DBUS_TYPE_BOOLEAN_AS_STRING "b" -#define DBUS_TYPE_INT32 ((int) 'i') -#define DBUS_TYPE_INT32_AS_STRING "i" - -#define DBUS_TYPE_UINT32 ((int) 'u') -#define DBUS_TYPE_UINT32_AS_STRING "u" -#define DBUS_TYPE_INT64 ((int) 'x') -#define DBUS_TYPE_INT64_AS_STRING "x" -#define DBUS_TYPE_UINT64 ((int) 't') -#define DBUS_TYPE_UINT64_AS_STRING "t" - -#define DBUS_TYPE_DOUBLE ((int) 'd') -#define DBUS_TYPE_DOUBLE_AS_STRING "d" -#define DBUS_TYPE_STRING ((int) 's') -#define DBUS_TYPE_STRING_AS_STRING "s" -#define DBUS_TYPE_OBJECT_PATH ((int) 'o') -#define DBUS_TYPE_OBJECT_PATH_AS_STRING "o" -#define DBUS_TYPE_SIGNATURE ((int) 'g') -#define DBUS_TYPE_SIGNATURE_AS_STRING "g" - -/* Compound types */ -#define DBUS_TYPE_ARRAY ((int) 'a') -#define DBUS_TYPE_ARRAY_AS_STRING "a" -#define DBUS_TYPE_VARIANT ((int) 'v') -#define DBUS_TYPE_VARIANT_AS_STRING "v" - -/* STRUCT is sort of special since its code can't appear in a type string, - * instead DBUS_STRUCT_BEGIN_CHAR has to appear - */ -#define DBUS_TYPE_STRUCT ((int) 'r') -#define DBUS_TYPE_STRUCT_AS_STRING "r" - -/* Does not count INVALID */ -#define DBUS_NUMBER_OF_TYPES (13) - -/* characters other than typecodes that appear in type signatures */ -#define DBUS_STRUCT_BEGIN_CHAR ((int) '(') -#define DBUS_STRUCT_BEGIN_CHAR_AS_STRING "(" -#define DBUS_STRUCT_END_CHAR ((int) ')') -#define DBUS_STRUCT_END_CHAR_AS_STRING ")" - -/* Max length in bytes of a service or interface or member name (not - * object path, paths are unlimited). This is limited because lots of - * stuff is O(n) in this number, plus it would be obnoxious to type in - * a paragraph-long method name so most likely something like that - * would be an exploit. - */ -#define DBUS_MAXIMUM_NAME_LENGTH 255 - -/* This one is 255 so it fits in a byte */ -#define DBUS_MAXIMUM_SIGNATURE_LENGTH 255 - -/* Max length of a match rule string; to keep people from hosing the - * daemon with some huge rule - */ -#define DBUS_MAXIMUM_MATCH_RULE_LENGTH 1024 - -/* Max length of a marshaled array in bytes (64M, 2^26) We use signed - * int for lengths so must be INT_MAX or less. We need something a - * bit smaller than INT_MAX because the array is inside a message with - * header info, etc. so an INT_MAX array wouldn't allow the message - * overhead. The 64M number is an attempt at a larger number than - * we'd reasonably ever use, but small enough that your bus would chew - * through it fairly quickly without locking up forever. If you have - * data that's likely to be larger than this, you should probably be - * sending it in multiple incremental messages anyhow. - */ -#define DBUS_MAXIMUM_ARRAY_LENGTH (67108864) -/* Number of bits you need in an unsigned to store the max array size */ -#define DBUS_MAXIMUM_ARRAY_LENGTH_BITS 26 - -/* The maximum total message size including header and body; similar - * rationale to max array size. - */ -#define DBUS_MAXIMUM_MESSAGE_LENGTH (DBUS_MAXIMUM_ARRAY_LENGTH * 2) -/* Number of bits you need in an unsigned to store the max message size */ -#define DBUS_MAXIMUM_MESSAGE_LENGTH_BITS 27 - -/* Types of message */ -#define DBUS_MESSAGE_TYPE_INVALID 0 -#define DBUS_MESSAGE_TYPE_METHOD_CALL 1 -#define DBUS_MESSAGE_TYPE_METHOD_RETURN 2 -#define DBUS_MESSAGE_TYPE_ERROR 3 -#define DBUS_MESSAGE_TYPE_SIGNAL 4 - -/* Header flags */ -#define DBUS_HEADER_FLAG_NO_REPLY_EXPECTED 0x1 -#define DBUS_HEADER_FLAG_AUTO_ACTIVATION 0x2 - -/* Header fields */ -#define DBUS_HEADER_FIELD_INVALID 0 -#define DBUS_HEADER_FIELD_PATH 1 -#define DBUS_HEADER_FIELD_INTERFACE 2 -#define DBUS_HEADER_FIELD_MEMBER 3 -#define DBUS_HEADER_FIELD_ERROR_NAME 4 -#define DBUS_HEADER_FIELD_REPLY_SERIAL 5 -#define DBUS_HEADER_FIELD_DESTINATION 6 -#define DBUS_HEADER_FIELD_SENDER 7 -#define DBUS_HEADER_FIELD_SIGNATURE 8 - -#define DBUS_HEADER_FIELD_LAST DBUS_HEADER_FIELD_SIGNATURE - -/* Services */ -#define DBUS_SERVICE_ORG_FREEDESKTOP_DBUS "org.freedesktop.DBus" - -/* Paths */ -#define DBUS_PATH_ORG_FREEDESKTOP_DBUS "/org/freedesktop/DBus" -#define DBUS_PATH_ORG_FREEDESKTOP_LOCAL "/org/freedesktop/Local" - -/* Interfaces, these #define don't do much other than - * catch typos at compile time - */ -#define DBUS_INTERFACE_ORG_FREEDESKTOP_DBUS "org.freedesktop.DBus" -#define DBUS_INTERFACE_ORG_FREEDESKTOP_INTROSPECTABLE "org.freedesktop.Introspectable" - -/* This is a special interface whose methods can only be invoked - * by the local implementation (messages from remote apps aren't - * allowed to specify this interface). - */ -#define DBUS_INTERFACE_ORG_FREEDESKTOP_LOCAL "org.freedesktop.Local" - -/* Service owner flags */ -#define DBUS_SERVICE_FLAG_PROHIBIT_REPLACEMENT 0x1 -#define DBUS_SERVICE_FLAG_REPLACE_EXISTING 0x2 - -/* Service replies */ -#define DBUS_SERVICE_REPLY_PRIMARY_OWNER 0x1 -#define DBUS_SERVICE_REPLY_IN_QUEUE 0x2 -#define DBUS_SERVICE_REPLY_SERVICE_EXISTS 0x4 -#define DBUS_SERVICE_REPLY_ALREADY_OWNER 0x8 - -/* Activation replies */ -#define DBUS_ACTIVATION_REPLY_ACTIVATED 0x0 -#define DBUS_ACTIVATION_REPLY_ALREADY_ACTIVE 0x1 - -/* Errors */ -/* WARNING these get autoconverted to an enum in dbus-glib.h. Thus, - * if you change the order it breaks the ABI. Keep them in order. - * Also, don't change the formatting since that will break the sed - * script. - */ -#define DBUS_ERROR_FAILED "org.freedesktop.DBus.Error.Failed" -#define DBUS_ERROR_NO_MEMORY "org.freedesktop.DBus.Error.NoMemory" -#define DBUS_ERROR_ACTIVATE_SERVICE_NOT_FOUND "org.freedesktop.DBus.Error.ServiceNotFound" -#define DBUS_ERROR_SERVICE_DOES_NOT_EXIST "org.freedesktop.DBus.Error.ServiceDoesNotExist" -#define DBUS_ERROR_SERVICE_HAS_NO_OWNER "org.freedesktop.DBus.Error.ServiceHasNoOwner" -#define DBUS_ERROR_NO_REPLY "org.freedesktop.DBus.Error.NoReply" -#define DBUS_ERROR_IO_ERROR "org.freedesktop.DBus.Error.IOError" -#define DBUS_ERROR_BAD_ADDRESS "org.freedesktop.DBus.Error.BadAddress" -#define DBUS_ERROR_NOT_SUPPORTED "org.freedesktop.DBus.Error.NotSupported" -#define DBUS_ERROR_LIMITS_EXCEEDED "org.freedesktop.DBus.Error.LimitsExceeded" -#define DBUS_ERROR_ACCESS_DENIED "org.freedesktop.DBus.Error.AccessDenied" -#define DBUS_ERROR_AUTH_FAILED "org.freedesktop.DBus.Error.AuthFailed" -#define DBUS_ERROR_NO_SERVER "org.freedesktop.DBus.Error.NoServer" -#define DBUS_ERROR_TIMEOUT "org.freedesktop.DBus.Error.Timeout" -#define DBUS_ERROR_NO_NETWORK "org.freedesktop.DBus.Error.NoNetwork" -#define DBUS_ERROR_ADDRESS_IN_USE "org.freedesktop.DBus.Error.AddressInUse" -#define DBUS_ERROR_DISCONNECTED "org.freedesktop.DBus.Error.Disconnected" -#define DBUS_ERROR_INVALID_ARGS "org.freedesktop.DBus.Error.InvalidArgs" -#define DBUS_ERROR_FILE_NOT_FOUND "org.freedesktop.DBus.Error.FileNotFound" -#define DBUS_ERROR_UNKNOWN_METHOD "org.freedesktop.DBus.Error.UnknownMethod" -#define DBUS_ERROR_TIMED_OUT "org.freedesktop.DBus.Error.TimedOut" -#define DBUS_ERROR_MATCH_RULE_NOT_FOUND "org.freedesktop.DBus.Error.MatchRuleNotFound" -#define DBUS_ERROR_MATCH_RULE_INVALID "org.freedesktop.DBus.Error.MatchRuleInvalid" -#define DBUS_ERROR_SPAWN_EXEC_FAILED "org.freedesktop.DBus.Error.Spawn.ExecFailed" -#define DBUS_ERROR_SPAWN_FORK_FAILED "org.freedesktop.DBus.Error.Spawn.ForkFailed" -#define DBUS_ERROR_SPAWN_CHILD_EXITED "org.freedesktop.DBus.Error.Spawn.ChildExited" -#define DBUS_ERROR_SPAWN_CHILD_SIGNALED "org.freedesktop.DBus.Error.Spawn.ChildSignaled" -#define DBUS_ERROR_SPAWN_FAILED "org.freedesktop.DBus.Error.Spawn.Failed" -#define DBUS_ERROR_UNIX_PROCESS_ID_UNKNOWN "org.freedesktop.DBus.Error.UnixProcessIdUnknown" - -#ifdef __cplusplus -} -#endif - -#endif /* DBUS_PROTOCOL_H */ diff --git a/dbus/dbus-protocol.h b/dbus/dbus-protocol.h index ce49a38d..6edd84f1 100644 --- a/dbus/dbus-protocol.h +++ b/dbus/dbus-protocol.h @@ -2,9 +2,10 @@ /* dbus-protocol.h D-Bus protocol constants * * Copyright (C) 2002, 2003 CodeFactory AB + * Copyright (C) 2004, 2005 Red Hat, Inc. * * Licensed under the Academic Free License version 2.1 - * + * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or @@ -14,7 +15,7 @@ * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. - * + * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA @@ -34,46 +35,115 @@ extern "C" { /* Message byte order */ #define DBUS_LITTLE_ENDIAN ('l') /* LSB first */ -#define DBUS_BIG_ENDIAN ('B') /* MSB first */ +#define DBUS_BIG_ENDIAN ('B') /* MSB first */ /* Protocol version */ -#define DBUS_MAJOR_PROTOCOL_VERSION 0 +#define DBUS_MAJOR_PROTOCOL_VERSION 1 -/* Data types */ +/* Never a legitimate type */ #define DBUS_TYPE_INVALID ((int) '\0') -#define DBUS_TYPE_NIL ((int) 'v') +#define DBUS_TYPE_INVALID_AS_STRING "\0" + +/* Primitive types */ #define DBUS_TYPE_BYTE ((int) 'y') +#define DBUS_TYPE_BYTE_AS_STRING "y" #define DBUS_TYPE_BOOLEAN ((int) 'b') +#define DBUS_TYPE_BOOLEAN_AS_STRING "b" #define DBUS_TYPE_INT32 ((int) 'i') +#define DBUS_TYPE_INT32_AS_STRING "i" + #define DBUS_TYPE_UINT32 ((int) 'u') +#define DBUS_TYPE_UINT32_AS_STRING "u" #define DBUS_TYPE_INT64 ((int) 'x') +#define DBUS_TYPE_INT64_AS_STRING "x" #define DBUS_TYPE_UINT64 ((int) 't') +#define DBUS_TYPE_UINT64_AS_STRING "t" + #define DBUS_TYPE_DOUBLE ((int) 'd') +#define DBUS_TYPE_DOUBLE_AS_STRING "d" #define DBUS_TYPE_STRING ((int) 's') -#define DBUS_TYPE_CUSTOM ((int) 'c') -#define DBUS_TYPE_ARRAY ((int) 'a') -#define DBUS_TYPE_DICT ((int) 'm') +#define DBUS_TYPE_STRING_AS_STRING "s" #define DBUS_TYPE_OBJECT_PATH ((int) 'o') +#define DBUS_TYPE_OBJECT_PATH_AS_STRING "o" +#define DBUS_TYPE_SIGNATURE ((int) 'g') +#define DBUS_TYPE_SIGNATURE_AS_STRING "g" + +/* Compound types */ +#define DBUS_TYPE_ARRAY ((int) 'a') +#define DBUS_TYPE_ARRAY_AS_STRING "a" +#define DBUS_TYPE_VARIANT ((int) 'v') +#define DBUS_TYPE_VARIANT_AS_STRING "v" +/* STRUCT is sort of special since its code can't appear in a type string, + * instead DBUS_STRUCT_BEGIN_CHAR has to appear + */ +#define DBUS_TYPE_STRUCT ((int) 'r') +#define DBUS_TYPE_STRUCT_AS_STRING "r" + +/* Does not count INVALID */ #define DBUS_NUMBER_OF_TYPES (13) -/* Max length in bytes of a service or interface or member name */ -#define DBUS_MAXIMUM_NAME_LENGTH 256 +/* characters other than typecodes that appear in type signatures */ +#define DBUS_STRUCT_BEGIN_CHAR ((int) '(') +#define DBUS_STRUCT_BEGIN_CHAR_AS_STRING "(" +#define DBUS_STRUCT_END_CHAR ((int) ')') +#define DBUS_STRUCT_END_CHAR_AS_STRING ")" + +/* Max length in bytes of a service or interface or member name (not + * object path, paths are unlimited). This is limited because lots of + * stuff is O(n) in this number, plus it would be obnoxious to type in + * a paragraph-long method name so most likely something like that + * would be an exploit. + */ +#define DBUS_MAXIMUM_NAME_LENGTH 255 -/* Max length of a match rule string */ +/* This one is 255 so it fits in a byte */ +#define DBUS_MAXIMUM_SIGNATURE_LENGTH 255 + +/* Max length of a match rule string; to keep people from hosing the + * daemon with some huge rule + */ #define DBUS_MAXIMUM_MATCH_RULE_LENGTH 1024 +/* Max length of a marshaled array in bytes (64M, 2^26) We use signed + * int for lengths so must be INT_MAX or less. We need something a + * bit smaller than INT_MAX because the array is inside a message with + * header info, etc. so an INT_MAX array wouldn't allow the message + * overhead. The 64M number is an attempt at a larger number than + * we'd reasonably ever use, but small enough that your bus would chew + * through it fairly quickly without locking up forever. If you have + * data that's likely to be larger than this, you should probably be + * sending it in multiple incremental messages anyhow. + */ +#define DBUS_MAXIMUM_ARRAY_LENGTH (67108864) +/* Number of bits you need in an unsigned to store the max array size */ +#define DBUS_MAXIMUM_ARRAY_LENGTH_BITS 26 + +/* The maximum total message size including header and body; similar + * rationale to max array size. + */ +#define DBUS_MAXIMUM_MESSAGE_LENGTH (DBUS_MAXIMUM_ARRAY_LENGTH * 2) +/* Number of bits you need in an unsigned to store the max message size */ +#define DBUS_MAXIMUM_MESSAGE_LENGTH_BITS 27 + +/* Depth of recursion in the type tree. This is automatically limited + * to DBUS_MAXIMUM_SIGNATURE_LENGTH since you could only have an array + * of array of array of ... that fit in the max signature. But that's + * probably a bit too large. + */ +#define DBUS_MAXIMUM_TYPE_RECURSION_DEPTH 32 + /* Types of message */ #define DBUS_MESSAGE_TYPE_INVALID 0 #define DBUS_MESSAGE_TYPE_METHOD_CALL 1 #define DBUS_MESSAGE_TYPE_METHOD_RETURN 2 #define DBUS_MESSAGE_TYPE_ERROR 3 #define DBUS_MESSAGE_TYPE_SIGNAL 4 - + /* Header flags */ #define DBUS_HEADER_FLAG_NO_REPLY_EXPECTED 0x1 #define DBUS_HEADER_FLAG_AUTO_ACTIVATION 0x2 - + /* Header fields */ #define DBUS_HEADER_FIELD_INVALID 0 #define DBUS_HEADER_FIELD_PATH 1 @@ -87,25 +157,52 @@ extern "C" { #define DBUS_HEADER_FIELD_LAST DBUS_HEADER_FIELD_SIGNATURE +/* Header format is defined as a signature: + * byte byte order + * byte message type ID + * byte flags + * byte protocol version + * uint32 body length + * uint32 serial + * array of struct (byte,variant) (field name, value) + * + * The length of the header can be computed as the + * fixed size of the initial data, plus the length of + * the array at the end, plus padding to an 8-boundary. + */ +#define DBUS_HEADER_SIGNATURE \ + DBUS_TYPE_BYTE_AS_STRING \ + DBUS_TYPE_BYTE_AS_STRING \ + DBUS_TYPE_BYTE_AS_STRING \ + DBUS_TYPE_BYTE_AS_STRING \ + DBUS_TYPE_UINT32_AS_STRING \ + DBUS_TYPE_UINT32_AS_STRING \ + DBUS_TYPE_ARRAY_AS_STRING \ + DBUS_STRUCT_BEGIN_CHAR_AS_STRING \ + DBUS_TYPE_BYTE_AS_STRING \ + DBUS_TYPE_VARIANT_AS_STRING \ + DBUS_STRUCT_END_CHAR_AS_STRING + + /* Services */ #define DBUS_SERVICE_ORG_FREEDESKTOP_DBUS "org.freedesktop.DBus" /* Paths */ #define DBUS_PATH_ORG_FREEDESKTOP_DBUS "/org/freedesktop/DBus" #define DBUS_PATH_ORG_FREEDESKTOP_LOCAL "/org/freedesktop/Local" - + /* Interfaces, these #define don't do much other than * catch typos at compile time */ #define DBUS_INTERFACE_ORG_FREEDESKTOP_DBUS "org.freedesktop.DBus" #define DBUS_INTERFACE_ORG_FREEDESKTOP_INTROSPECTABLE "org.freedesktop.Introspectable" - + /* This is a special interface whose methods can only be invoked * by the local implementation (messages from remote apps aren't * allowed to specify this interface). */ #define DBUS_INTERFACE_ORG_FREEDESKTOP_LOCAL "org.freedesktop.Local" - + /* Service owner flags */ #define DBUS_SERVICE_FLAG_PROHIBIT_REPLACEMENT 0x1 #define DBUS_SERVICE_FLAG_REPLACE_EXISTING 0x2 diff --git a/dbus/dbus-sha.c b/dbus/dbus-sha.c index d26fb320..b386e452 100644 --- a/dbus/dbus-sha.c +++ b/dbus/dbus-sha.c @@ -23,8 +23,8 @@ */ #include "dbus-internals.h" -#include "dbus-marshal.h" #include "dbus-sha.h" +#include "dbus-marshal-basic.h" /* for byteswap routines */ #include /* The following comments have the history of where this code diff --git a/dbus/dbus-string.c b/dbus/dbus-string.c index 7040e155..777461ed 100644 --- a/dbus/dbus-string.c +++ b/dbus/dbus-string.c @@ -27,10 +27,11 @@ #include /* for vsnprintf */ #include -#include "dbus-marshal.h" #define DBUS_CAN_USE_DBUS_STRING_PRIVATE 1 #include "dbus-string-private.h" -#include "dbus-protocol.h" +#include "dbus-marshal-basic.h" /* probably should be removed by moving the usage of DBUS_TYPE + * into the marshaling-related files + */ /* for DBUS_VA_COPY */ #include "dbus-sysdeps.h" @@ -77,25 +78,18 @@ * @{ */ -/** - * We allocate 1 byte for nul termination, plus 7 bytes for possible - * align_offset, so we always need 8 bytes on top of the string's - * length to be in the allocated block. - */ -#define ALLOCATION_PADDING 8 - /** * This is the maximum max length (and thus also the maximum length) * of a DBusString */ -#define MAX_MAX_LENGTH (_DBUS_INT_MAX - ALLOCATION_PADDING) +#define MAX_MAX_LENGTH (_DBUS_INT_MAX - _DBUS_STRING_ALLOCATION_PADDING) /** * Checks a bunch of assertions about a string object * * @param real the DBusRealString */ -#define DBUS_GENERIC_STRING_PREAMBLE(real) _dbus_assert ((real) != NULL); _dbus_assert (!(real)->invalid); _dbus_assert ((real)->len >= 0); _dbus_assert ((real)->allocated >= 0); _dbus_assert ((real)->max_length >= 0); _dbus_assert ((real)->len <= ((real)->allocated - ALLOCATION_PADDING)); _dbus_assert ((real)->len <= (real)->max_length) +#define DBUS_GENERIC_STRING_PREAMBLE(real) _dbus_assert ((real) != NULL); _dbus_assert (!(real)->invalid); _dbus_assert ((real)->len >= 0); _dbus_assert ((real)->allocated >= 0); _dbus_assert ((real)->max_length >= 0); _dbus_assert ((real)->len <= ((real)->allocated - _DBUS_STRING_ALLOCATION_PADDING)); _dbus_assert ((real)->len <= (real)->max_length) /** * Checks assertions about a string object that needs to be @@ -142,7 +136,7 @@ fixup_alignment (DBusRealString *real) unsigned int old_align_offset; /* we have to have extra space in real->allocated for the align offset and nul byte */ - _dbus_assert (real->len <= real->allocated - ALLOCATION_PADDING); + _dbus_assert (real->len <= real->allocated - _DBUS_STRING_ALLOCATION_PADDING); old_align_offset = real->align_offset; real_block = real->str - old_align_offset; @@ -205,11 +199,11 @@ _dbus_string_init_preallocated (DBusString *str, * an existing string, e.g. in _dbus_string_steal_data() */ - real->str = dbus_malloc (ALLOCATION_PADDING + allocate_size); + real->str = dbus_malloc (_DBUS_STRING_ALLOCATION_PADDING + allocate_size); if (real->str == NULL) return FALSE; - real->allocated = ALLOCATION_PADDING + allocate_size; + real->allocated = _DBUS_STRING_ALLOCATION_PADDING + allocate_size; real->len = 0; real->str[real->len] = '\0'; @@ -299,10 +293,12 @@ _dbus_string_init_const_len (DBusString *str, real->str = (char*) value; real->len = len; - real->allocated = real->len + ALLOCATION_PADDING; /* a lie, just to avoid special-case assertions... */ + real->allocated = real->len + _DBUS_STRING_ALLOCATION_PADDING; /* a lie, just to avoid special-case assertions... */ real->max_length = real->len + 1; real->constant = TRUE; + real->locked = TRUE; real->invalid = FALSE; + real->align_offset = 0; /* We don't require const strings to be 8-byte aligned as the * memory is coming from elsewhere. @@ -356,7 +352,7 @@ _dbus_string_lock (DBusString *str) char *new_str; int new_allocated; - new_allocated = real->len + ALLOCATION_PADDING; + new_allocated = real->len + _DBUS_STRING_ALLOCATION_PADDING; new_str = dbus_realloc (real->str - real->align_offset, new_allocated); @@ -380,8 +376,8 @@ reallocate_for_length (DBusRealString *real, /* at least double our old allocation to avoid O(n), avoiding * overflow */ - if (real->allocated > (MAX_MAX_LENGTH + ALLOCATION_PADDING) / 2) - new_allocated = MAX_MAX_LENGTH + ALLOCATION_PADDING; + if (real->allocated > (MAX_MAX_LENGTH + _DBUS_STRING_ALLOCATION_PADDING) / 2) + new_allocated = MAX_MAX_LENGTH + _DBUS_STRING_ALLOCATION_PADDING; else new_allocated = real->allocated * 2; @@ -403,7 +399,7 @@ reallocate_for_length (DBusRealString *real, /* But be sure we always alloc at least space for the new length */ new_allocated = MAX (new_allocated, - new_length + ALLOCATION_PADDING); + new_length + _DBUS_STRING_ALLOCATION_PADDING); _dbus_assert (new_allocated >= real->allocated); /* code relies on this */ new_str = dbus_realloc (real->str - real->align_offset, new_allocated); @@ -426,7 +422,7 @@ set_length (DBusRealString *real, /* exceeding max length is the same as failure to allocate memory */ if (_DBUS_UNLIKELY (new_length > real->max_length)) return FALSE; - else if (new_length > (real->allocated - ALLOCATION_PADDING) && + else if (new_length > (real->allocated - _DBUS_STRING_ALLOCATION_PADDING) && _DBUS_UNLIKELY (!reallocate_for_length (real, new_length))) return FALSE; else @@ -1179,10 +1175,10 @@ _dbus_string_insert_alignment (DBusString *str, { DBUS_STRING_PREAMBLE (str); - if (!align_insert_point_then_open_gap (str, insert_at, 8, 0)) + if (!align_insert_point_then_open_gap (str, insert_at, alignment, 0)) return FALSE; - _dbus_assert (_DBUS_ALIGN_VALUE (*insert_at, 8) == (unsigned) *insert_at); + _dbus_assert (_DBUS_ALIGN_VALUE (*insert_at, alignment) == (unsigned) *insert_at); return TRUE; } @@ -2752,387 +2748,6 @@ _dbus_string_validate_nul (const DBusString *str, return TRUE; } -/** - * Checks that the given range of the string is a valid object path - * name in the D-BUS protocol. This includes a length restriction, - * etc., see the specification. It does not validate UTF-8, that has - * to be done separately for now. - * - * @todo this is inconsistent with most of DBusString in that - * it allows a start,len range that extends past the string end. - * - * @todo change spec to disallow more things, such as spaces in the - * path name - * - * @param str the string - * @param start first byte index to check - * @param len number of bytes to check - * @returns #TRUE if the byte range exists and is a valid name - */ -dbus_bool_t -_dbus_string_validate_path (const DBusString *str, - int start, - int len) -{ - const unsigned char *s; - const unsigned char *end; - const unsigned char *last_slash; - - DBUS_CONST_STRING_PREAMBLE (str); - _dbus_assert (start >= 0); - _dbus_assert (len >= 0); - _dbus_assert (start <= real->len); - - if (len > real->len - start) - return FALSE; - - if (len > DBUS_MAXIMUM_NAME_LENGTH) - return FALSE; - - if (len == 0) - return FALSE; - - s = real->str + start; - end = s + len; - - if (*s != '/') - return FALSE; - last_slash = s; - ++s; - - while (s != end) - { - if (*s == '/') - { - if ((s - last_slash) < 2) - return FALSE; /* no empty path components allowed */ - - last_slash = s; - } - - ++s; - } - - if ((end - last_slash) < 2 && - len > 1) - return FALSE; /* trailing slash not allowed unless the string is "/" */ - - return TRUE; -} - -/** - * Determine wether the given charater is valid as the first charater - * in a name. - */ -#define VALID_INITIAL_NAME_CHARACTER(c) \ - ( ((c) >= 'A' && (c) <= 'Z') || \ - ((c) >= 'a' && (c) <= 'z') || \ - ((c) == '_') ) - -/** - * Determine wether the given charater is valid as a second or later - * character in a nam - */ -#define VALID_NAME_CHARACTER(c) \ - ( ((c) >= '0' && (c) <= '9') || \ - ((c) >= 'A' && (c) <= 'Z') || \ - ((c) >= 'a' && (c) <= 'z') || \ - ((c) == '_') ) - -/** - * Checks that the given range of the string is a valid interface name - * in the D-BUS protocol. This includes a length restriction and an - * ASCII subset, see the specification. - * - * @todo this is inconsistent with most of DBusString in that - * it allows a start,len range that extends past the string end. - * - * @param str the string - * @param start first byte index to check - * @param len number of bytes to check - * @returns #TRUE if the byte range exists and is a valid name - */ -dbus_bool_t -_dbus_string_validate_interface (const DBusString *str, - int start, - int len) -{ - const unsigned char *s; - const unsigned char *end; - const unsigned char *iface; - const unsigned char *last_dot; - - DBUS_CONST_STRING_PREAMBLE (str); - _dbus_assert (start >= 0); - _dbus_assert (len >= 0); - _dbus_assert (start <= real->len); - - if (len > real->len - start) - return FALSE; - - if (len > DBUS_MAXIMUM_NAME_LENGTH) - return FALSE; - - if (len == 0) - return FALSE; - - last_dot = NULL; - iface = real->str + start; - end = iface + len; - s = iface; - - /* check special cases of first char so it doesn't have to be done - * in the loop. Note we know len > 0 - */ - if (_DBUS_UNLIKELY (*s == '.')) /* disallow starting with a . */ - return FALSE; - else if (_DBUS_UNLIKELY (!VALID_INITIAL_NAME_CHARACTER (*s))) - return FALSE; - else - ++s; - - while (s != end) - { - if (*s == '.') - { - if (_DBUS_UNLIKELY ((s + 1) == end)) - return FALSE; - else if (_DBUS_UNLIKELY (!VALID_INITIAL_NAME_CHARACTER (*(s + 1)))) - return FALSE; - last_dot = s; - ++s; /* we just validated the next char, so skip two */ - } - else if (_DBUS_UNLIKELY (!VALID_NAME_CHARACTER (*s))) - { - return FALSE; - } - - ++s; - } - - if (_DBUS_UNLIKELY (last_dot == NULL)) - return FALSE; - - return TRUE; -} - -/** - * Checks that the given range of the string is a valid member name - * in the D-BUS protocol. This includes a length restriction, etc., - * see the specification. - * - * @todo this is inconsistent with most of DBusString in that - * it allows a start,len range that extends past the string end. - * - * @param str the string - * @param start first byte index to check - * @param len number of bytes to check - * @returns #TRUE if the byte range exists and is a valid name - */ -dbus_bool_t -_dbus_string_validate_member (const DBusString *str, - int start, - int len) -{ - const unsigned char *s; - const unsigned char *end; - const unsigned char *member; - - DBUS_CONST_STRING_PREAMBLE (str); - _dbus_assert (start >= 0); - _dbus_assert (len >= 0); - _dbus_assert (start <= real->len); - - if (len > real->len - start) - return FALSE; - - if (len > DBUS_MAXIMUM_NAME_LENGTH) - return FALSE; - - if (len == 0) - return FALSE; - - member = real->str + start; - end = member + len; - s = member; - - /* check special cases of first char so it doesn't have to be done - * in the loop. Note we know len > 0 - */ - - if (_DBUS_UNLIKELY (!VALID_INITIAL_NAME_CHARACTER (*s))) - return FALSE; - else - ++s; - - while (s != end) - { - if (_DBUS_UNLIKELY (!VALID_NAME_CHARACTER (*s))) - { - return FALSE; - } - - ++s; - } - - return TRUE; -} - -/** - * Checks that the given range of the string is a valid error name - * in the D-BUS protocol. This includes a length restriction, etc., - * see the specification. - * - * @todo this is inconsistent with most of DBusString in that - * it allows a start,len range that extends past the string end. - * - * @param str the string - * @param start first byte index to check - * @param len number of bytes to check - * @returns #TRUE if the byte range exists and is a valid name - */ -dbus_bool_t -_dbus_string_validate_error_name (const DBusString *str, - int start, - int len) -{ - /* Same restrictions as interface name at the moment */ - return _dbus_string_validate_interface (str, start, len); -} - -/* This assumes the first char exists and is ':' */ -static dbus_bool_t -_dbus_string_validate_base_service (const DBusString *str, - int start, - int len) -{ - const unsigned char *s; - const unsigned char *end; - const unsigned char *service; - - DBUS_CONST_STRING_PREAMBLE (str); - _dbus_assert (start >= 0); - _dbus_assert (len >= 0); - _dbus_assert (start <= real->len); - - if (len > real->len - start) - return FALSE; - - if (len > DBUS_MAXIMUM_NAME_LENGTH) - return FALSE; - - _dbus_assert (len > 0); - - service = real->str + start; - end = service + len; - _dbus_assert (*service == ':'); - s = service + 1; - - while (s != end) - { - if (*s == '.') - { - if (_DBUS_UNLIKELY ((s + 1) == end)) - return FALSE; - if (_DBUS_UNLIKELY (!VALID_NAME_CHARACTER (*(s + 1)))) - return FALSE; - ++s; /* we just validated the next char, so skip two */ - } - else if (_DBUS_UNLIKELY (!VALID_NAME_CHARACTER (*s))) - { - return FALSE; - } - - ++s; - } - - return TRUE; -} - -/** - * Checks that the given range of the string is a valid service name - * in the D-BUS protocol. This includes a length restriction, etc., - * see the specification. - * - * @todo this is inconsistent with most of DBusString in that - * it allows a start,len range that extends past the string end. - * - * @param str the string - * @param start first byte index to check - * @param len number of bytes to check - * @returns #TRUE if the byte range exists and is a valid name - */ -dbus_bool_t -_dbus_string_validate_service (const DBusString *str, - int start, - int len) -{ - if (_DBUS_UNLIKELY (len == 0)) - return FALSE; - if (_dbus_string_get_byte (str, start) == ':') - return _dbus_string_validate_base_service (str, start, len); - else - return _dbus_string_validate_interface (str, start, len); -} - -/** - * Checks that the given range of the string is a valid message type - * signature in the D-BUS protocol. - * - * @todo this is inconsistent with most of DBusString in that - * it allows a start,len range that extends past the string end. - * - * @param str the string - * @param start first byte index to check - * @param len number of bytes to check - * @returns #TRUE if the byte range exists and is a valid signature - */ -dbus_bool_t -_dbus_string_validate_signature (const DBusString *str, - int start, - int len) -{ - const unsigned char *s; - const unsigned char *end; - DBUS_CONST_STRING_PREAMBLE (str); - _dbus_assert (start >= 0); - _dbus_assert (start <= real->len); - _dbus_assert (len >= 0); - - if (len > real->len - start) - return FALSE; - - s = real->str + start; - end = s + len; - while (s != end) - { - switch (*s) - { - case DBUS_TYPE_NIL: - case DBUS_TYPE_BYTE: - case DBUS_TYPE_BOOLEAN: - case DBUS_TYPE_INT32: - case DBUS_TYPE_UINT32: - case DBUS_TYPE_INT64: - case DBUS_TYPE_UINT64: - case DBUS_TYPE_DOUBLE: - case DBUS_TYPE_STRING: - case DBUS_TYPE_CUSTOM: - case DBUS_TYPE_ARRAY: - case DBUS_TYPE_DICT: - case DBUS_TYPE_OBJECT_PATH: - break; - - default: - return FALSE; - } - - ++s; - } - - return TRUE; -} - /** * Clears all allocated bytes in the string to zero. * @@ -3393,127 +3008,6 @@ _dbus_string_test (void) int lens[] = { 0, 1, 2, 3, 4, 5, 10, 16, 17, 18, 25, 31, 32, 33, 34, 35, 63, 64, 65, 66, 67, 68, 69, 70, 71, 127, 128, 129, 130, 131, 132, 133, 134, 135, 136 }; char *s; dbus_unichar_t ch; - const char *valid_paths[] = { - "/", - "/foo/bar", - "/foo", - "/foo/bar/baz" - }; - const char *invalid_paths[] = { - "bar", - "bar/baz", - "/foo/bar/", - "/foo/" - "foo/", - "boo//blah", - "//", - "///", - "foo///blah/", - "Hello World", - "", - " ", - "foo bar" - }; - - const char *valid_interfaces[] = { - "org.freedesktop.Foo", - "Bar.Baz", - "Blah.Blah.Blah.Blah.Blah", - "a.b", - "a.b.c.d.e.f.g", - "a0.b1.c2.d3.e4.f5.g6", - "abc123.foo27" - }; - const char *invalid_interfaces[] = { - ".", - "", - "..", - ".Foo.Bar", - "..Foo.Bar", - "Foo.Bar.", - "Foo.Bar..", - "Foo", - "9foo.bar.baz", - "foo.bar..baz", - "foo.bar...baz", - "foo.bar.b..blah", - ":", - ":0-1", - "10", - ":11.34324", - "0.0.0", - "0..0", - "foo.Bar.%", - "foo.Bar!!", - "!Foo.bar.bz", - "foo.$.blah", - "", - " ", - "foo bar" - }; - - const char *valid_base_services[] = { - ":0", - ":a", - ":", - ":.a", - ":.1", - ":0.1", - ":000.2222", - ":.blah", - ":abce.freedesktop.blah" - }; - const char *invalid_base_services[] = { - ":-", - ":!", - ":0-10", - ":blah.", - ":blah.", - ":blah..org", - ":blah.org..", - ":..blah.org", - "", - " ", - "foo bar" - }; - - const char *valid_members[] = { - "Hello", - "Bar", - "foobar", - "_foobar", - "foo89" - }; - - const char *invalid_members[] = { - "9Hello", - "10", - "1", - "foo-bar", - "blah.org", - ".blah", - "blah.", - "Hello.", - "!foo", - "", - " ", - "foo bar" - }; - - const char *valid_signatures[] = { - "", - "sss", - "i", - "b" - }; - - const char *invalid_signatures[] = { - " ", - "not a valid signature", - "123", - ".", - "(" - }; i = 0; while (i < _DBUS_N_ELEMENTS (lens)) @@ -3901,284 +3395,6 @@ _dbus_string_test (void) _dbus_string_free (&other); test_roundtrips (test_hex_roundtrip); - - /* Path validation */ - i = 0; - while (i < (int) _DBUS_N_ELEMENTS (valid_paths)) - { - _dbus_string_init_const (&str, valid_paths[i]); - - if (!_dbus_string_validate_path (&str, 0, - _dbus_string_get_length (&str))) - { - _dbus_warn ("Path \"%s\" should have been valid\n", valid_paths[i]); - _dbus_assert_not_reached ("invalid path"); - } - - ++i; - } - - i = 0; - while (i < (int) _DBUS_N_ELEMENTS (invalid_paths)) - { - _dbus_string_init_const (&str, invalid_paths[i]); - - if (_dbus_string_validate_path (&str, 0, - _dbus_string_get_length (&str))) - { - _dbus_warn ("Path \"%s\" should have been invalid\n", invalid_paths[i]); - _dbus_assert_not_reached ("valid path"); - } - - ++i; - } - - /* Interface validation */ - i = 0; - while (i < (int) _DBUS_N_ELEMENTS (valid_interfaces)) - { - _dbus_string_init_const (&str, valid_interfaces[i]); - - if (!_dbus_string_validate_interface (&str, 0, - _dbus_string_get_length (&str))) - { - _dbus_warn ("Interface \"%s\" should have been valid\n", valid_interfaces[i]); - _dbus_assert_not_reached ("invalid interface"); - } - - ++i; - } - - i = 0; - while (i < (int) _DBUS_N_ELEMENTS (invalid_interfaces)) - { - _dbus_string_init_const (&str, invalid_interfaces[i]); - - if (_dbus_string_validate_interface (&str, 0, - _dbus_string_get_length (&str))) - { - _dbus_warn ("Interface \"%s\" should have been invalid\n", invalid_interfaces[i]); - _dbus_assert_not_reached ("valid interface"); - } - - ++i; - } - - /* Service validation (check that valid interfaces are valid services, - * and invalid interfaces are invalid services except if they start with ':') - */ - i = 0; - while (i < (int) _DBUS_N_ELEMENTS (valid_interfaces)) - { - _dbus_string_init_const (&str, valid_interfaces[i]); - - if (!_dbus_string_validate_service (&str, 0, - _dbus_string_get_length (&str))) - { - _dbus_warn ("Service \"%s\" should have been valid\n", valid_interfaces[i]); - _dbus_assert_not_reached ("invalid service"); - } - - ++i; - } - - i = 0; - while (i < (int) _DBUS_N_ELEMENTS (invalid_interfaces)) - { - if (invalid_interfaces[i][0] != ':') - { - _dbus_string_init_const (&str, invalid_interfaces[i]); - - if (_dbus_string_validate_service (&str, 0, - _dbus_string_get_length (&str))) - { - _dbus_warn ("Service \"%s\" should have been invalid\n", invalid_interfaces[i]); - _dbus_assert_not_reached ("valid service"); - } - } - - ++i; - } - - /* Base service validation */ - i = 0; - while (i < (int) _DBUS_N_ELEMENTS (valid_base_services)) - { - _dbus_string_init_const (&str, valid_base_services[i]); - - if (!_dbus_string_validate_service (&str, 0, - _dbus_string_get_length (&str))) - { - _dbus_warn ("Service \"%s\" should have been valid\n", valid_base_services[i]); - _dbus_assert_not_reached ("invalid base service"); - } - - ++i; - } - - i = 0; - while (i < (int) _DBUS_N_ELEMENTS (invalid_base_services)) - { - _dbus_string_init_const (&str, invalid_base_services[i]); - - if (_dbus_string_validate_service (&str, 0, - _dbus_string_get_length (&str))) - { - _dbus_warn ("Service \"%s\" should have been invalid\n", invalid_base_services[i]); - _dbus_assert_not_reached ("valid base service"); - } - - ++i; - } - - - /* Error name validation (currently identical to interfaces) - */ - i = 0; - while (i < (int) _DBUS_N_ELEMENTS (valid_interfaces)) - { - _dbus_string_init_const (&str, valid_interfaces[i]); - - if (!_dbus_string_validate_error_name (&str, 0, - _dbus_string_get_length (&str))) - { - _dbus_warn ("Error name \"%s\" should have been valid\n", valid_interfaces[i]); - _dbus_assert_not_reached ("invalid error name"); - } - - ++i; - } - - i = 0; - while (i < (int) _DBUS_N_ELEMENTS (invalid_interfaces)) - { - if (invalid_interfaces[i][0] != ':') - { - _dbus_string_init_const (&str, invalid_interfaces[i]); - - if (_dbus_string_validate_error_name (&str, 0, - _dbus_string_get_length (&str))) - { - _dbus_warn ("Error name \"%s\" should have been invalid\n", invalid_interfaces[i]); - _dbus_assert_not_reached ("valid error name"); - } - } - - ++i; - } - - /* Member validation */ - i = 0; - while (i < (int) _DBUS_N_ELEMENTS (valid_members)) - { - _dbus_string_init_const (&str, valid_members[i]); - - if (!_dbus_string_validate_member (&str, 0, - _dbus_string_get_length (&str))) - { - _dbus_warn ("Member \"%s\" should have been valid\n", valid_members[i]); - _dbus_assert_not_reached ("invalid member"); - } - - ++i; - } - - i = 0; - while (i < (int) _DBUS_N_ELEMENTS (invalid_members)) - { - _dbus_string_init_const (&str, invalid_members[i]); - - if (_dbus_string_validate_member (&str, 0, - _dbus_string_get_length (&str))) - { - _dbus_warn ("Member \"%s\" should have been invalid\n", invalid_members[i]); - _dbus_assert_not_reached ("valid member"); - } - - ++i; - } - - /* Signature validation */ - i = 0; - while (i < (int) _DBUS_N_ELEMENTS (valid_signatures)) - { - _dbus_string_init_const (&str, valid_signatures[i]); - - if (!_dbus_string_validate_signature (&str, 0, - _dbus_string_get_length (&str))) - { - _dbus_warn ("Signature \"%s\" should have been valid\n", valid_signatures[i]); - _dbus_assert_not_reached ("invalid signature"); - } - - ++i; - } - - i = 0; - while (i < (int) _DBUS_N_ELEMENTS (invalid_signatures)) - { - _dbus_string_init_const (&str, invalid_signatures[i]); - - if (_dbus_string_validate_signature (&str, 0, - _dbus_string_get_length (&str))) - { - _dbus_warn ("Signature \"%s\" should have been invalid\n", invalid_signatures[i]); - _dbus_assert_not_reached ("valid signature"); - } - - ++i; - } - - /* Validate claimed length longer than real length */ - _dbus_string_init_const (&str, "abc.efg"); - if (_dbus_string_validate_service (&str, 0, 8)) - _dbus_assert_not_reached ("validated too-long string"); - if (_dbus_string_validate_interface (&str, 0, 8)) - _dbus_assert_not_reached ("validated too-long string"); - if (_dbus_string_validate_error_name (&str, 0, 8)) - _dbus_assert_not_reached ("validated too-long string"); - - _dbus_string_init_const (&str, "abc"); - if (_dbus_string_validate_member (&str, 0, 4)) - _dbus_assert_not_reached ("validated too-long string"); - - _dbus_string_init_const (&str, "sss"); - if (_dbus_string_validate_signature (&str, 0, 4)) - _dbus_assert_not_reached ("validated too-long signature"); - - /* Validate string exceeding max name length */ - if (!_dbus_string_init (&str)) - _dbus_assert_not_reached ("no memory"); - - while (_dbus_string_get_length (&str) <= DBUS_MAXIMUM_NAME_LENGTH) - if (!_dbus_string_append (&str, "abc.def")) - _dbus_assert_not_reached ("no memory"); - - if (_dbus_string_validate_service (&str, 0, _dbus_string_get_length (&str))) - _dbus_assert_not_reached ("validated overmax string"); - if (_dbus_string_validate_interface (&str, 0, _dbus_string_get_length (&str))) - _dbus_assert_not_reached ("validated overmax string"); - if (_dbus_string_validate_error_name (&str, 0, _dbus_string_get_length (&str))) - _dbus_assert_not_reached ("validated overmax string"); - - /* overlong member */ - _dbus_string_set_length (&str, 0); - while (_dbus_string_get_length (&str) <= DBUS_MAXIMUM_NAME_LENGTH) - if (!_dbus_string_append (&str, "abc")) - _dbus_assert_not_reached ("no memory"); - - if (_dbus_string_validate_member (&str, 0, _dbus_string_get_length (&str))) - _dbus_assert_not_reached ("validated overmax string"); - - /* overlong base service */ - _dbus_string_set_length (&str, 0); - _dbus_string_append (&str, ":"); - while (_dbus_string_get_length (&str) <= DBUS_MAXIMUM_NAME_LENGTH) - if (!_dbus_string_append (&str, "abc")) - _dbus_assert_not_reached ("no memory"); - - if (_dbus_string_validate_service (&str, 0, _dbus_string_get_length (&str))) - _dbus_assert_not_reached ("validated overmax string"); _dbus_string_free (&str); diff --git a/dbus/dbus-string.h b/dbus/dbus-string.h index 792b8194..f0ae1e65 100644 --- a/dbus/dbus-string.h +++ b/dbus/dbus-string.h @@ -39,10 +39,10 @@ DBUS_BEGIN_DECLS */ struct DBusString { - void *dummy1; /**< placeholder */ - int dummy2; /**< placeholder */ - int dummy3; /**< placeholder */ - int dummy4; /**< placeholder */ + const void *dummy1; /**< placeholder */ + int dummy2; /**< placeholder */ + int dummy3; /**< placeholder */ + int dummy4; /**< placeholder */ unsigned int dummy5 : 1; /**< placeholder */ unsigned int dummy6 : 1; /**< placeholder */ unsigned int dummy7 : 1; /**< placeholder */ @@ -264,27 +264,32 @@ dbus_bool_t _dbus_string_validate_utf8 (const DBusString *str, dbus_bool_t _dbus_string_validate_nul (const DBusString *str, int start, int len); -dbus_bool_t _dbus_string_validate_path (const DBusString *str, - int start, - int len); -dbus_bool_t _dbus_string_validate_interface (const DBusString *str, - int start, - int len); -dbus_bool_t _dbus_string_validate_member (const DBusString *str, - int start, - int len); -dbus_bool_t _dbus_string_validate_error_name (const DBusString *str, - int start, - int len); -dbus_bool_t _dbus_string_validate_service (const DBusString *str, - int start, - int len); -dbus_bool_t _dbus_string_validate_signature (const DBusString *str, - int start, - int len); void _dbus_string_zero (DBusString *str); +/** + * We allocate 1 byte for nul termination, plus 7 bytes for possible + * align_offset, so we always need 8 bytes on top of the string's + * length to be in the allocated block. + */ +#define _DBUS_STRING_ALLOCATION_PADDING 8 + +/** + * Defines a static const variable with type #DBusString called "name" + * containing the given string literal. + * + * @param name the name of the variable + * @param str the string value + */ +#define _DBUS_STRING_DEFINE_STATIC(name, str) \ + static const char _dbus_static_string_##name[] = str; \ + static const DBusString name = { _dbus_static_string_##name, \ + sizeof(_dbus_static_string_##name), \ + sizeof(_dbus_static_string_##name) + \ + _DBUS_STRING_ALLOCATION_PADDING, \ + sizeof(_dbus_static_string_##name), \ + TRUE, TRUE, FALSE, 0 } + DBUS_END_DECLS #endif /* DBUS_STRING_H */ diff --git a/dbus/dbus-test.c b/dbus/dbus-test.c index df45ba6f..8654d86e 100644 --- a/dbus/dbus-test.c +++ b/dbus/dbus-test.c @@ -118,6 +118,16 @@ dbus_internal_do_not_use_run_tests (const char *test_data_dir) check_memleaks (); +#if 1 + printf ("%s: running recursive marshalling tests\n", "dbus-test"); + if (!_dbus_marshal_recursive_test ()) + die ("recursive marshal"); + + check_memleaks (); +#else + _dbus_warn ("recursive marshal tests disabled\n"); +#endif + printf ("%s: running memory tests\n", "dbus-test"); if (!_dbus_memory_test ()) die ("memory"); @@ -135,6 +145,18 @@ dbus_internal_do_not_use_run_tests (const char *test_data_dir) die ("lists"); check_memleaks (); + + printf ("%s: running validation tests\n", "dbus-test"); + if (!_dbus_marshal_validate_test ()) + die ("validation"); + + check_memleaks (); + + printf ("%s: running header marshal tests\n", "dbus-test"); + if (!_dbus_marshal_header_test ()) + die ("header marshal"); + + check_memleaks (); printf ("%s: running message tests\n", "dbus-test"); if (!_dbus_message_test (test_data_dir)) @@ -197,5 +219,3 @@ dbus_internal_do_not_use_run_tests (const char *test_data_dir) printf ("Not compiled with unit tests, not running any\n"); #endif } - - diff --git a/dbus/dbus-test.h b/dbus/dbus-test.h index 1933c79b..b9865f09 100644 --- a/dbus/dbus-test.h +++ b/dbus/dbus-test.h @@ -35,26 +35,29 @@ typedef enum _DBUS_MESSAGE_UNKNOWN } DBusMessageValidity; -dbus_bool_t _dbus_hash_test (void); -dbus_bool_t _dbus_dict_test (void); -dbus_bool_t _dbus_list_test (void); -dbus_bool_t _dbus_marshal_test (void); -dbus_bool_t _dbus_mem_pool_test (void); -dbus_bool_t _dbus_string_test (void); -dbus_bool_t _dbus_address_test (void); -dbus_bool_t _dbus_server_test (void); -dbus_bool_t _dbus_message_test (const char *test_data_dir); -dbus_bool_t _dbus_auth_test (const char *test_data_dir); -dbus_bool_t _dbus_md5_test (void); -dbus_bool_t _dbus_sha_test (const char *test_data_dir); -dbus_bool_t _dbus_keyring_test (void); -dbus_bool_t _dbus_data_slot_test (void); -dbus_bool_t _dbus_sysdeps_test (void); -dbus_bool_t _dbus_spawn_test (const char *test_data_dir); -dbus_bool_t _dbus_userdb_test (const char *test_data_dir); -dbus_bool_t _dbus_memory_test (void); -dbus_bool_t _dbus_object_tree_test (void); -dbus_bool_t _dbus_pending_call_test (const char *test_data_dir); +dbus_bool_t _dbus_hash_test (void); +dbus_bool_t _dbus_dict_test (void); +dbus_bool_t _dbus_list_test (void); +dbus_bool_t _dbus_marshal_test (void); +dbus_bool_t _dbus_marshal_recursive_test (void); +dbus_bool_t _dbus_marshal_header_test (void); +dbus_bool_t _dbus_marshal_validate_test (void); +dbus_bool_t _dbus_mem_pool_test (void); +dbus_bool_t _dbus_string_test (void); +dbus_bool_t _dbus_address_test (void); +dbus_bool_t _dbus_server_test (void); +dbus_bool_t _dbus_message_test (const char *test_data_dir); +dbus_bool_t _dbus_auth_test (const char *test_data_dir); +dbus_bool_t _dbus_md5_test (void); +dbus_bool_t _dbus_sha_test (const char *test_data_dir); +dbus_bool_t _dbus_keyring_test (void); +dbus_bool_t _dbus_data_slot_test (void); +dbus_bool_t _dbus_sysdeps_test (void); +dbus_bool_t _dbus_spawn_test (const char *test_data_dir); +dbus_bool_t _dbus_userdb_test (const char *test_data_dir); +dbus_bool_t _dbus_memory_test (void); +dbus_bool_t _dbus_object_tree_test (void); +dbus_bool_t _dbus_pending_call_test (const char *test_data_dir); void dbus_internal_do_not_use_run_tests (const char *test_data_dir); dbus_bool_t dbus_internal_do_not_use_try_message_file (const DBusString *filename, -- cgit