diff options
Diffstat (limited to 'dbus/dbus-message.c')
-rw-r--r-- | dbus/dbus-message.c | 594 |
1 files changed, 535 insertions, 59 deletions
diff --git a/dbus/dbus-message.c b/dbus/dbus-message.c index 864887ad..fb28ced0 100644 --- a/dbus/dbus-message.c +++ b/dbus/dbus-message.c @@ -2,6 +2,7 @@ /* dbus-message.c DBusMessage object * * Copyright (C) 2002 Red Hat Inc. + * Copyright (C) 2002, 2003 CodeFactory AB * * Licensed under the Academic Free License version 1.2 * @@ -64,9 +65,26 @@ struct DBusMessage DBusString body; /**< Body network data. */ + char byte_order; /**< Message byte order. */ + + char *name; /**< Message name. */ + char *service; /**< Message destination service. */ + + dbus_int32_t client_serial; /**< Client serial or -1 if not set */ + dbus_int32_t reply_serial; /**< Reply serial or -1 if not set */ + unsigned int locked : 1; /**< Message being sent, no modifications allowed. */ }; +struct DBusMessageIter +{ + int refcount; /**< Reference count */ + + int pos; /**< Current position in the string */ + + DBusMessage *message; /**< Message used */ +}; + /** * Gets the data to be sent over the network for this message. * The header and then the body should be written out. @@ -89,6 +107,69 @@ _dbus_message_get_network_data (DBusMessage *message, } /** + * Sets the client serial of a message. + * This can only be done once on a message. + * + * @param message the message + * @param client_serial the client serial + */ +void +_dbus_message_set_client_serial (DBusMessage *message, + dbus_int32_t client_serial) +{ + _dbus_assert (message->client_serial == -1); + + message->client_serial = client_serial; +} + +static void +dbus_message_write_header (DBusMessage *message) +{ + char *header; + + _dbus_assert (message->client_serial != -1); + + _dbus_string_append_byte (&message->header, DBUS_COMPILER_BYTE_ORDER); + _dbus_string_append_len (&message->header, "\0\0\0", 3); + + /* We just lengthen the string here and pack in the real length later */ + _dbus_string_lengthen (&message->header, 4); + + _dbus_marshal_int32 (&message->header, DBUS_COMPILER_BYTE_ORDER, _dbus_string_get_length (&message->body)); + + /* Marshal client serial */ + _dbus_marshal_int32 (&message->header, DBUS_COMPILER_BYTE_ORDER, message->client_serial); + + /* Marshal message service */ + if (message->service) + { + _dbus_string_append_len (&message->header, DBUS_HEADER_FIELD_SERVICE, 4); + _dbus_string_append_byte (&message->header, DBUS_TYPE_STRING); + + _dbus_marshal_string (&message->header, DBUS_COMPILER_BYTE_ORDER, message->service); + } + + /* Marshal message name */ + _dbus_string_append_len (&message->header, DBUS_HEADER_FIELD_NAME, 4); + _dbus_string_append_byte (&message->header, DBUS_TYPE_STRING); + + _dbus_marshal_string (&message->header, DBUS_COMPILER_BYTE_ORDER, message->name); + + /* Marshal reply serial */ + if (message->reply_serial != -1) + { + _dbus_string_append_len (&message->header, DBUS_HEADER_FIELD_REPLY, 4); + + _dbus_string_append_byte (&message->header, DBUS_TYPE_INT32); + _dbus_marshal_int32 (&message->header, DBUS_COMPILER_BYTE_ORDER, message->reply_serial); + } + + /* Fill in the length */ + _dbus_string_get_data_len (&message->header, &header, 4, 4); + dbus_pack_int32 (_dbus_string_get_length (&message->header), DBUS_COMPILER_BYTE_ORDER, header); +} + +/** * 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 @@ -98,8 +179,11 @@ _dbus_message_get_network_data (DBusMessage *message, * @param message the message to lock. */ void -_dbus_message_lock (DBusMessage *message) +_dbus_message_lock (DBusMessage *message) { + if (!message->locked) + dbus_message_write_header (message); + message->locked = TRUE; } @@ -128,12 +212,16 @@ _dbus_message_lock (DBusMessage *message) /** * Constructs a new message. Returns #NULL if memory * can't be allocated for the message. - * - * @return a new DBusMessage, free with dbus_message_unref() + * + * @param service service that the message should be sent to + * should be sent to + * @param name name of the message + * @returns a new DBusMessage, free with dbus_message_unref() * @see dbus_message_unref() */ DBusMessage* -dbus_message_new (void) +dbus_message_new (const char *service, + const char *name) { DBusMessage *message; @@ -142,7 +230,14 @@ dbus_message_new (void) return NULL; message->refcount = 1; + message->byte_order = DBUS_COMPILER_BYTE_ORDER; + message->service = _dbus_strdup (service); + message->name = _dbus_strdup (name); + + message->client_serial = -1; + message->reply_serial = -1; + if (!_dbus_string_init (&message->header, _DBUS_MAX_MESSAGE_LENGTH)) { dbus_free (message); @@ -156,13 +251,6 @@ dbus_message_new (void) return NULL; } - /* We need to decide what a message contains. ;-) */ - /* (not bothering to check failure of these appends) */ - _dbus_string_append (&message->header, "H"); - _dbus_string_append_byte (&message->header, '\0'); - _dbus_string_append (&message->body, "Body"); - _dbus_string_append_byte (&message->body, '\0'); - return message; } @@ -176,6 +264,8 @@ dbus_message_new (void) void dbus_message_ref (DBusMessage *message) { + _dbus_assert (message->refcount > 0); + message->refcount += 1; } @@ -188,7 +278,6 @@ dbus_message_ref (DBusMessage *message) void dbus_message_unref (DBusMessage *message) { - _dbus_assert (message != NULL); _dbus_assert (message->refcount > 0); message->refcount -= 1; @@ -197,6 +286,7 @@ dbus_message_unref (DBusMessage *message) _dbus_string_free (&message->header); _dbus_string_free (&message->body); + dbus_free (message->name); dbus_free (message); } } @@ -210,9 +300,7 @@ dbus_message_unref (DBusMessage *message) const char* dbus_message_get_name (DBusMessage *message) { - /* FIXME */ - - return NULL; + return message->name; } /** @@ -226,7 +314,6 @@ dbus_bool_t dbus_message_append_int32 (DBusMessage *message, dbus_int32_t value) { - _dbus_assert (message != NULL); _dbus_assert (!message->locked); if (!_dbus_string_append_byte (&message->body, DBUS_TYPE_INT32)) @@ -250,7 +337,6 @@ dbus_bool_t dbus_message_append_uint32 (DBusMessage *message, dbus_uint32_t value) { - _dbus_assert (message != NULL); _dbus_assert (!message->locked); if (!_dbus_string_append_byte (&message->body, DBUS_TYPE_UINT32)) @@ -274,10 +360,9 @@ dbus_bool_t dbus_message_append_double (DBusMessage *message, double value) { - _dbus_assert (message != NULL); _dbus_assert (!message->locked); - if (!_dbus_string_append_byte (&message->body, DBUS_TYPE_INT32)) + if (!_dbus_string_append_byte (&message->body, DBUS_TYPE_DOUBLE)) { _dbus_string_shorten (&message->body, 1); return FALSE; @@ -298,11 +383,9 @@ dbus_bool_t dbus_message_append_string (DBusMessage *message, const char *value) { - _dbus_assert (message != NULL); _dbus_assert (!message->locked); - _dbus_assert (value != NULL); - if (!_dbus_string_append_byte (&message->body, DBUS_TYPE_UTF8_STRING)) + if (!_dbus_string_append_byte (&message->body, DBUS_TYPE_STRING)) { _dbus_string_shorten (&message->body, 1); return FALSE; @@ -325,9 +408,7 @@ dbus_message_append_byte_array (DBusMessage *message, unsigned const char *value, int len) { - _dbus_assert (message != NULL); _dbus_assert (!message->locked); - _dbus_assert (value != NULL); if (!_dbus_string_append_byte (&message->body, DBUS_TYPE_BYTE_ARRAY)) { @@ -339,6 +420,198 @@ dbus_message_append_byte_array (DBusMessage *message, DBUS_COMPILER_BYTE_ORDER, value, len); } +/** + * Returns a DBusMessageIter representing the fields of the + * message passed in. + * + * @param message the message + * @returns a new iter. + */ +DBusMessageIter * +dbus_message_get_fields_iter (DBusMessage *message) +{ + DBusMessageIter *iter; + + iter = dbus_new (DBusMessageIter, 1); + + dbus_message_ref (message); + + iter->refcount = 1; + iter->message = message; + iter->pos = 0; + + return iter; +} + +/** + * Increments the reference count of a DBusMessageIter. + * + * @param iter the message iter + * @see dbus_message_iter_unref + */ +void +dbus_message_iter_ref (DBusMessageIter *iter) +{ + _dbus_assert (iter->refcount > 0); + + iter->refcount += 1; +} + +/** + * Decrements the reference count of a DBusMessageIter. + * + * @param iter The message iter + * @see dbus_message_iter_ref + */ +void +dbus_message_iter_unref (DBusMessageIter *iter) +{ + _dbus_assert (iter->refcount > 0); + + iter->refcount -= 1; + + if (iter->refcount == 0) + { + dbus_message_unref (iter->message); + + dbus_free (iter); + } +} + +/** + * 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) +{ + int end_pos; + + if (!_dbus_marshal_get_field_end_pos (&iter->message->body, iter->message->byte_order, + iter->pos, &end_pos)) + return FALSE; + + if (end_pos >= _dbus_string_get_length (&iter->message->body)) + 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) +{ + int end_pos; + + if (!_dbus_marshal_get_field_end_pos (&iter->message->body, iter->message->byte_order, + iter->pos, &end_pos)) + return FALSE; + + if (end_pos >= _dbus_string_get_length (&iter->message->body)) + return FALSE; + + iter->pos = end_pos; + + return TRUE; +} + +/** + * Returns the field type of the field that the + * message iterator points at. + * + * @param iter the message iter + * @returns the field type + */ +int +dbus_message_iter_get_field_type (DBusMessageIter *iter) +{ + const char *data; + + if (iter->pos >= _dbus_string_get_length (&iter->message->body)) + return DBUS_TYPE_INVALID; + + _dbus_string_get_const_data_len (&iter->message->body, &data, iter->pos, 1); + + if (*data > DBUS_TYPE_INVALID && *data <= DBUS_TYPE_STRING) + return *data; + + return DBUS_TYPE_INVALID; +} + +/** + * 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. + * + * @see dbus_message_iter_get_field_type + * @param iter the message iter + * @returns the string + */ +char * +dbus_message_iter_get_string (DBusMessageIter *iter) +{ + _dbus_assert (dbus_message_iter_get_field_type (iter) == DBUS_TYPE_STRING); + + return _dbus_demarshal_string (&iter->message->body, iter->message->byte_order, + iter->pos + 1, 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 string value before using this function. + * + * @see dbus_message_iter_get_field_type + * @param iter the message iter + * @returns the integer + */ +int +dbus_message_iter_get_int32 (DBusMessageIter *iter) +{ + return _dbus_demarshal_int32 (&iter->message->body, iter->message->byte_order, + iter->pos + 1, NULL); +} + +/** + * 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 string value before using this function. + * + * @see dbus_message_iter_get_field_type + * @param iter the message iter + * @returns the integer + */ +int +dbus_message_iter_get_uint32 (DBusMessageIter *iter) +{ + return _dbus_demarshal_uint32 (&iter->message->body, iter->message->byte_order, + iter->pos + 1, NULL); +} + +/** + * 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. + * + * @see dbus_message_iter_get_field_type + * @param iter the message iter + * @returns the double + */ +double +dbus_message_iter_get_double (DBusMessageIter *iter) +{ + return _dbus_demarshal_double (&iter->message->body, iter->message->byte_order, + iter->pos + 1, NULL); +} + /** @} */ /** @@ -478,6 +751,65 @@ _dbus_message_loader_get_buffer (DBusMessageLoader *loader, } /** + * The smallest header size that can occur. + * (It won't be valid) + */ +#define DBUS_MINIMUM_HEADER_SIZE 16 + +static dbus_bool_t +decode_header_data (DBusString *data, + int header_len, + int byte_order, + dbus_int32_t *client_serial, + char **service, + char **name) +{ + const char *field; + int pos, new_pos; + + /* First demarshal the client serial */ + *client_serial = _dbus_demarshal_int32 (data, byte_order, 12, &pos); + + *service = NULL; + *name = NULL; + + /* Now handle the fields */ + while (pos < header_len) + { + _dbus_string_get_const_data_len (data, &field, pos, 4); + pos += 4; + + if (pos > header_len) + return FALSE; + + if (strncmp (field, DBUS_HEADER_FIELD_SERVICE, 4) == 0) + { + *service = _dbus_demarshal_string (data, byte_order, pos + 1, &new_pos); + } + else if (strncmp (field, DBUS_HEADER_FIELD_NAME, 4) == 0) + { + *name = _dbus_demarshal_string (data, byte_order, pos + 1, &new_pos); + } + else + { + _dbus_verbose ("Encountered an unknown header field: %c%c%c%c\n", + field[0], field[1], field[2], field[3]); + + if (!_dbus_marshal_get_field_end_pos (data, byte_order, pos, &new_pos)) + return FALSE; + } + + if (new_pos > header_len) + return FALSE; + + pos = new_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 * in. This function must always be called, even if no bytes were @@ -495,49 +827,67 @@ _dbus_message_loader_return_buffer (DBusMessageLoader *loader, _dbus_assert (loader->buffer_outstanding); _dbus_assert (buffer == &loader->data); - /* FIXME fake implementation just creates a message for every 7 - * bytes. The real implementation will pass ownership of - * loader->data bytes to new messages, to avoid memcpy. We can also - * smart-realloc loader->data to shrink it if it's too big, though - * _dbus_message_loader_get_buffer() could strategically arrange for - * that to usually not happen. - */ - loader->buffer_outstanding = FALSE; if (loader->corrupted) return; - - while (_dbus_string_get_length (&loader->data) >= 7) + + while (_dbus_string_get_length (&loader->data) >= 16) { - DBusMessage *message; - const char *d; - - _dbus_string_get_const_data (&loader->data, &d); - if (d[0] != 'H' || - d[1] != '\0' || - d[2] != 'B' || - d[3] != 'o' || - d[4] != 'd' || - d[5] != 'y' || - d[6] != '\0') - { - _dbus_verbose_bytes (d, - _dbus_string_get_length (&loader->data)); - loader->corrupted = TRUE; - return; - } + DBusMessage *message; + const char *header_data; + int byte_order, header_len, body_len; - message = dbus_message_new (); - if (message == NULL) - break; /* ugh, postpone this I guess. */ + _dbus_string_get_const_data_len (&loader->data, &header_data, 0, 16); + byte_order = header_data[0]; - _dbus_list_append (&loader->messages, message); + if (byte_order != DBUS_LITTLE_ENDIAN && + byte_order != DBUS_BIG_ENDIAN) + { + loader->corrupted = TRUE; + return; + } - _dbus_string_delete (&loader->data, - 0, 7); - - _dbus_verbose ("Loaded message %p\n", message); + header_len = dbus_unpack_int32 (byte_order, header_data + 4); + body_len = dbus_unpack_int32 (byte_order, header_data + 8); + + if (header_len + body_len > _DBUS_MAX_MESSAGE_LENGTH) + { + loader->corrupted = TRUE; + + return; + } + + if (_dbus_string_get_length (&loader->data) >= header_len + body_len) + { + dbus_int32_t client_serial; + char *service, *name; + + if (!decode_header_data (&loader->data, header_len, byte_order, + &client_serial, &service, &name)) + { + loader->corrupted = TRUE; + + return; + } + + message = dbus_message_new (service, name); + dbus_free (service); + dbus_free (name); + + if (message == NULL) + break; /* ugh, postpone this I guess. */ + + _dbus_string_copy (&loader->data, header_len, &message->body, 0); + _dbus_message_set_client_serial (message, client_serial); + + _dbus_list_append (&loader->messages, message); + _dbus_string_delete (&loader->data, 0, header_len + body_len); + + _dbus_verbose ("Loaded message %p\n", message); + } + else + break; } } @@ -572,3 +922,129 @@ _dbus_message_loader_get_is_corrupted (DBusMessageLoader *loader) } /** @} */ +#ifdef DBUS_BUILD_TESTS +#include "dbus-test.h" +#include <stdio.h> + +static void +message_iter_test (DBusMessage *message) +{ + DBusMessageIter *iter; + char *str; + + iter = dbus_message_get_fields_iter (message); + + /* String tests */ + if (dbus_message_iter_get_field_type (iter) != DBUS_TYPE_STRING) + _dbus_assert_not_reached ("Field 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 fields"); + + /* Signed integer tests */ + if (dbus_message_iter_get_field_type (iter) != DBUS_TYPE_INT32) + _dbus_assert_not_reached ("Field 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_field_type (iter) != DBUS_TYPE_UINT32) + _dbus_assert_not_reached ("Field type isn't int32"); + + if (dbus_message_iter_get_int32 (iter) != 0xedd1e) + _dbus_assert_not_reached ("Unsigned integers differ"); + + if (!dbus_message_iter_next (iter)) + _dbus_assert_not_reached ("Reached end of fields"); + + /* Double tests */ + if (dbus_message_iter_get_field_type (iter) != DBUS_TYPE_DOUBLE) + _dbus_assert_not_reached ("Field 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 ("Didn't reach end of fields"); + + dbus_message_iter_unref (iter); +} + +/** + * @ingroup DBusMessageInternals + * Unit test for DBusMessage. + * + * @returns #TRUE on success. + */ +dbus_bool_t +_dbus_message_test (void) +{ + DBusMessage *message; + + DBusMessageLoader *loader; + int i; + const char *data; + + message = dbus_message_new ("org.freedesktop.DBus.Test", "testMessage"); + message->client_serial = 1; + dbus_message_append_string (message, "Test string"); + dbus_message_append_int32 (message, -0x12345678); + dbus_message_append_uint32 (message, 0xedd1e); + dbus_message_append_double (message, 3.14159); + + message_iter_test (message); + + /* Message loader test */ + _dbus_message_lock (message); + loader = _dbus_message_loader_new (); + + /* Write the header data one byte at a time */ + _dbus_string_get_const_data (&message->header, &data); + for (i = 0; i < _dbus_string_get_length (&message->header); i++) + { + DBusString *buffer; + + _dbus_message_loader_get_buffer (loader, &buffer); + _dbus_string_append_byte (buffer, data[i]); + _dbus_message_loader_return_buffer (loader, buffer, 1); + } + + /* Write the body data one byte at a time */ + _dbus_string_get_const_data (&message->body, &data); + for (i = 0; i < _dbus_string_get_length (&message->body); i++) + { + DBusString *buffer; + + _dbus_message_loader_get_buffer (loader, &buffer); + _dbus_string_append_byte (buffer, data[i]); + _dbus_message_loader_return_buffer (loader, buffer, 1); + } + + dbus_message_unref (message); + + /* Now pop back the message */ + 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"); + + message_iter_test (message); + + dbus_message_unref (message); + _dbus_message_loader_unref (loader); + + return TRUE; +} + +#endif /* DBUS_BUILD_TESTS */ |