summaryrefslogtreecommitdiffstats
path: root/dbus/dbus-marshal-header.c
diff options
context:
space:
mode:
authorHavoc Pennington <hp@redhat.com>2005-01-15 07:15:38 +0000
committerHavoc Pennington <hp@redhat.com>2005-01-15 07:15:38 +0000
commit9c3d566e95c9080f6040c64531b0ccae22bd5d74 (patch)
treed21a18baa5a5ee9855c8a00eb2c1985bc23ca65f /dbus/dbus-marshal-header.c
parent6ec04e917c8b4d477e818aa65ebb5e1fd50e4395 (diff)
2005-01-15 Havoc Pennington <hp@redhat.com>
* 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
Diffstat (limited to 'dbus/dbus-marshal-header.c')
-rw-r--r--dbus/dbus-marshal-header.c1435
1 files changed, 1435 insertions, 0 deletions
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 <stdio.h>
+
+dbus_bool_t
+_dbus_marshal_header_test (void)
+{
+
+ return TRUE;
+}
+
+#endif /* DBUS_BUILD_TESTS */