| /* -*- mode: C; c-file-style: "gnu"; indent-tabs-mode: nil; -*- */ |
| /* dbus-message-factory.c Generator of valid and invalid message data for test suite |
| * |
| * 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., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA |
| * |
| */ |
| #include <config.h> |
| |
| #ifndef DOXYGEN_SHOULD_SKIP_THIS |
| |
| #ifdef DBUS_BUILD_TESTS |
| #include "dbus-message-factory.h" |
| #include "dbus-message-private.h" |
| #include "dbus-test.h" |
| #include <stdio.h> |
| |
| typedef enum |
| { |
| CHANGE_TYPE_ADJUST, |
| CHANGE_TYPE_ABSOLUTE |
| } ChangeType; |
| |
| #define BYTE_ORDER_OFFSET 0 |
| #define TYPE_OFFSET 1 |
| #define BODY_LENGTH_OFFSET 4 |
| #define FIELDS_ARRAY_LENGTH_OFFSET 12 |
| |
| static void |
| iter_recurse (DBusMessageDataIter *iter) |
| { |
| iter->depth += 1; |
| _dbus_assert (iter->depth < _DBUS_MESSAGE_DATA_MAX_NESTING); |
| _dbus_assert (iter->sequence_nos[iter->depth] >= 0); |
| } |
| |
| static int |
| iter_get_sequence (DBusMessageDataIter *iter) |
| { |
| _dbus_assert (iter->sequence_nos[iter->depth] >= 0); |
| return iter->sequence_nos[iter->depth]; |
| } |
| |
| static void |
| iter_set_sequence (DBusMessageDataIter *iter, |
| int sequence) |
| { |
| _dbus_assert (sequence >= 0); |
| iter->sequence_nos[iter->depth] = sequence; |
| } |
| |
| static void |
| iter_unrecurse (DBusMessageDataIter *iter) |
| { |
| iter->depth -= 1; |
| _dbus_assert (iter->depth >= 0); |
| } |
| |
| static void |
| iter_next (DBusMessageDataIter *iter) |
| { |
| iter->sequence_nos[iter->depth] += 1; |
| } |
| |
| static dbus_bool_t |
| iter_first_in_series (DBusMessageDataIter *iter) |
| { |
| int i; |
| |
| i = iter->depth; |
| while (i < _DBUS_MESSAGE_DATA_MAX_NESTING) |
| { |
| if (iter->sequence_nos[i] != 0) |
| return FALSE; |
| ++i; |
| } |
| return TRUE; |
| } |
| |
| typedef dbus_bool_t (* DBusInnerGeneratorFunc) (DBusMessageDataIter *iter, |
| DBusMessage **message_p); |
| typedef dbus_bool_t (* DBusMessageGeneratorFunc) (DBusMessageDataIter *iter, |
| DBusString *data, |
| DBusValidity *expected_validity); |
| |
| static void |
| set_reply_serial (DBusMessage *message) |
| { |
| if (message == NULL) |
| _dbus_assert_not_reached ("oom"); |
| if (!dbus_message_set_reply_serial (message, 100)) |
| _dbus_assert_not_reached ("oom"); |
| } |
| |
| static dbus_bool_t |
| generate_trivial_inner (DBusMessageDataIter *iter, |
| DBusMessage **message_p) |
| { |
| DBusMessage *message; |
| |
| switch (iter_get_sequence (iter)) |
| { |
| case 0: |
| message = dbus_message_new_method_call ("org.freedesktop.TextEditor", |
| "/foo/bar", |
| "org.freedesktop.DocumentFactory", |
| "Create"); |
| break; |
| case 1: |
| message = dbus_message_new (DBUS_MESSAGE_TYPE_METHOD_RETURN); |
| set_reply_serial (message); |
| break; |
| case 2: |
| message = dbus_message_new_signal ("/foo/bar", |
| "org.freedesktop.DocumentFactory", |
| "Created"); |
| break; |
| case 3: |
| message = dbus_message_new (DBUS_MESSAGE_TYPE_ERROR); |
| |
| if (!dbus_message_set_error_name (message, |
| "org.freedesktop.TestErrorName")) |
| _dbus_assert_not_reached ("oom"); |
| |
| { |
| DBusMessageIter iter; |
| const char *v_STRING = "This is an error"; |
| |
| dbus_message_iter_init_append (message, &iter); |
| if (!dbus_message_iter_append_basic (&iter, |
| DBUS_TYPE_STRING, |
| &v_STRING)) |
| _dbus_assert_not_reached ("oom"); |
| } |
| |
| set_reply_serial (message); |
| break; |
| default: |
| return FALSE; |
| } |
| |
| if (message == NULL) |
| _dbus_assert_not_reached ("oom"); |
| |
| *message_p = message; |
| |
| return TRUE; |
| } |
| |
| static dbus_bool_t |
| generate_many_bodies_inner (DBusMessageDataIter *iter, |
| DBusMessage **message_p) |
| { |
| DBusMessage *message; |
| DBusString signature; |
| DBusString body; |
| |
| /* Keeping this small makes things go faster */ |
| message = dbus_message_new_method_call ("o.z.F", |
| "/", |
| "o.z.B", |
| "Nah"); |
| if (message == NULL) |
| _dbus_assert_not_reached ("oom"); |
| |
| set_reply_serial (message); |
| |
| if (!_dbus_string_init (&signature) || !_dbus_string_init (&body)) |
| _dbus_assert_not_reached ("oom"); |
| |
| if (dbus_internal_do_not_use_generate_bodies (iter_get_sequence (iter), |
| message->byte_order, |
| &signature, &body)) |
| { |
| const char *v_SIGNATURE; |
| |
| v_SIGNATURE = _dbus_string_get_const_data (&signature); |
| if (!_dbus_header_set_field_basic (&message->header, |
| DBUS_HEADER_FIELD_SIGNATURE, |
| DBUS_TYPE_SIGNATURE, |
| &v_SIGNATURE)) |
| _dbus_assert_not_reached ("oom"); |
| |
| if (!_dbus_string_move (&body, 0, &message->body, 0)) |
| _dbus_assert_not_reached ("oom"); |
| |
| _dbus_marshal_set_uint32 (&message->header.data, BODY_LENGTH_OFFSET, |
| _dbus_string_get_length (&message->body), |
| message->byte_order); |
| |
| *message_p = message; |
| } |
| else |
| { |
| dbus_message_unref (message); |
| *message_p = NULL; |
| } |
| |
| _dbus_string_free (&signature); |
| _dbus_string_free (&body); |
| |
| return *message_p != NULL; |
| } |
| |
| static void |
| generate_from_message (DBusString *data, |
| DBusValidity *expected_validity, |
| DBusMessage *message) |
| { |
| dbus_message_set_serial (message, 1); |
| dbus_message_lock (message); |
| |
| *expected_validity = DBUS_VALID; |
| |
| /* move for efficiency, since we'll nuke the message anyway */ |
| if (!_dbus_string_move (&message->header.data, 0, |
| data, 0)) |
| _dbus_assert_not_reached ("oom"); |
| |
| if (!_dbus_string_copy (&message->body, 0, |
| data, _dbus_string_get_length (data))) |
| _dbus_assert_not_reached ("oom"); |
| } |
| |
| static dbus_bool_t |
| generate_outer (DBusMessageDataIter *iter, |
| DBusString *data, |
| DBusValidity *expected_validity, |
| DBusInnerGeneratorFunc func) |
| { |
| DBusMessage *message; |
| |
| message = NULL; |
| if (!(*func)(iter, &message)) |
| return FALSE; |
| |
| iter_next (iter); |
| |
| _dbus_assert (message != NULL); |
| |
| generate_from_message (data, expected_validity, message); |
| |
| dbus_message_unref (message); |
| |
| return TRUE; |
| } |
| |
| static dbus_bool_t |
| generate_trivial (DBusMessageDataIter *iter, |
| DBusString *data, |
| DBusValidity *expected_validity) |
| { |
| return generate_outer (iter, data, expected_validity, |
| generate_trivial_inner); |
| } |
| |
| static dbus_bool_t |
| generate_many_bodies (DBusMessageDataIter *iter, |
| DBusString *data, |
| DBusValidity *expected_validity) |
| { |
| return generate_outer (iter, data, expected_validity, |
| generate_many_bodies_inner); |
| } |
| |
| static DBusMessage* |
| simple_method_call (void) |
| { |
| DBusMessage *message; |
| /* Keeping this small makes stuff go faster */ |
| message = dbus_message_new_method_call ("o.b.Q", |
| "/f/b", |
| "o.b.Z", |
| "Fro"); |
| if (message == NULL) |
| _dbus_assert_not_reached ("oom"); |
| return message; |
| } |
| |
| static DBusMessage* |
| simple_signal (void) |
| { |
| DBusMessage *message; |
| message = dbus_message_new_signal ("/f/b", |
| "o.b.Z", |
| "Fro"); |
| if (message == NULL) |
| _dbus_assert_not_reached ("oom"); |
| return message; |
| } |
| |
| static DBusMessage* |
| simple_method_return (void) |
| { |
| DBusMessage *message; |
| message = dbus_message_new (DBUS_MESSAGE_TYPE_METHOD_RETURN); |
| if (message == NULL) |
| _dbus_assert_not_reached ("oom"); |
| |
| set_reply_serial (message); |
| |
| return message; |
| } |
| |
| static DBusMessage* |
| simple_error (void) |
| { |
| DBusMessage *message; |
| message = dbus_message_new (DBUS_MESSAGE_TYPE_ERROR); |
| if (message == NULL) |
| _dbus_assert_not_reached ("oom"); |
| |
| if (!dbus_message_set_error_name (message, "foo.bar")) |
| _dbus_assert_not_reached ("oom"); |
| |
| set_reply_serial (message); |
| |
| return message; |
| } |
| |
| static DBusMessage* |
| message_with_nesting_levels (int levels) |
| { |
| DBusMessage *message; |
| dbus_int32_t v_INT32; |
| DBusMessageIter *parents; |
| DBusMessageIter *children; |
| int i; |
| |
| /* If levels is higher it breaks sig_refcount in DBusMessageRealIter |
| * in dbus-message.c, this assert is just to help you know you need |
| * to fix that if you hit it |
| */ |
| _dbus_assert (levels < 256); |
| |
| parents = dbus_new(DBusMessageIter, levels + 1); |
| children = dbus_new(DBusMessageIter, levels + 1); |
| |
| v_INT32 = 42; |
| message = simple_method_call (); |
| |
| i = 0; |
| dbus_message_iter_init_append (message, &parents[i]); |
| while (i < levels) |
| { |
| dbus_message_iter_open_container (&parents[i], DBUS_TYPE_VARIANT, |
| i == (levels - 1) ? |
| DBUS_TYPE_INT32_AS_STRING : |
| DBUS_TYPE_VARIANT_AS_STRING, |
| &children[i]); |
| ++i; |
| parents[i] = children[i-1]; |
| } |
| --i; |
| dbus_message_iter_append_basic (&children[i], DBUS_TYPE_INT32, &v_INT32); |
| while (i >= 0) |
| { |
| dbus_message_iter_close_container (&parents[i], &children[i]); |
| --i; |
| } |
| |
| dbus_free(parents); |
| dbus_free(children); |
| |
| return message; |
| } |
| |
| static dbus_bool_t |
| generate_special (DBusMessageDataIter *iter, |
| DBusString *data, |
| DBusValidity *expected_validity) |
| { |
| int item_seq; |
| DBusMessage *message; |
| int pos; |
| dbus_int32_t v_INT32; |
| |
| _dbus_assert (_dbus_string_get_length (data) == 0); |
| |
| message = NULL; |
| pos = -1; |
| v_INT32 = 42; |
| item_seq = iter_get_sequence (iter); |
| |
| if (item_seq == 0) |
| { |
| message = simple_method_call (); |
| if (!dbus_message_append_args (message, |
| DBUS_TYPE_INT32, &v_INT32, |
| DBUS_TYPE_INT32, &v_INT32, |
| DBUS_TYPE_INT32, &v_INT32, |
| DBUS_TYPE_INVALID)) |
| _dbus_assert_not_reached ("oom"); |
| |
| _dbus_header_get_field_raw (&message->header, |
| DBUS_HEADER_FIELD_SIGNATURE, |
| NULL, &pos); |
| generate_from_message (data, expected_validity, message); |
| |
| /* set an invalid typecode */ |
| _dbus_string_set_byte (data, pos + 1, '$'); |
| |
| *expected_validity = DBUS_INVALID_UNKNOWN_TYPECODE; |
| } |
| else if (item_seq == 1) |
| { |
| char long_sig[DBUS_MAXIMUM_TYPE_RECURSION_DEPTH+2]; |
| const char *v_STRING; |
| int i; |
| |
| message = simple_method_call (); |
| if (!dbus_message_append_args (message, |
| DBUS_TYPE_INT32, &v_INT32, |
| DBUS_TYPE_INT32, &v_INT32, |
| DBUS_TYPE_INT32, &v_INT32, |
| DBUS_TYPE_INVALID)) |
| _dbus_assert_not_reached ("oom"); |
| |
| i = 0; |
| while (i < (DBUS_MAXIMUM_TYPE_RECURSION_DEPTH + 1)) |
| { |
| long_sig[i] = DBUS_TYPE_ARRAY; |
| ++i; |
| } |
| long_sig[i] = DBUS_TYPE_INVALID; |
| |
| v_STRING = long_sig; |
| if (!_dbus_header_set_field_basic (&message->header, |
| DBUS_HEADER_FIELD_SIGNATURE, |
| DBUS_TYPE_SIGNATURE, |
| &v_STRING)) |
| _dbus_assert_not_reached ("oom"); |
| |
| _dbus_header_get_field_raw (&message->header, |
| DBUS_HEADER_FIELD_SIGNATURE, |
| NULL, &pos); |
| generate_from_message (data, expected_validity, message); |
| |
| *expected_validity = DBUS_INVALID_EXCEEDED_MAXIMUM_ARRAY_RECURSION; |
| } |
| else if (item_seq == 2) |
| { |
| char long_sig[DBUS_MAXIMUM_TYPE_RECURSION_DEPTH*2+4]; |
| const char *v_STRING; |
| int i; |
| |
| message = simple_method_call (); |
| if (!dbus_message_append_args (message, |
| DBUS_TYPE_INT32, &v_INT32, |
| DBUS_TYPE_INT32, &v_INT32, |
| DBUS_TYPE_INT32, &v_INT32, |
| DBUS_TYPE_INVALID)) |
| _dbus_assert_not_reached ("oom"); |
| |
| i = 0; |
| while (i <= (DBUS_MAXIMUM_TYPE_RECURSION_DEPTH + 1)) |
| { |
| long_sig[i] = DBUS_STRUCT_BEGIN_CHAR; |
| ++i; |
| } |
| |
| long_sig[i] = DBUS_TYPE_INT32; |
| ++i; |
| |
| while (i < (DBUS_MAXIMUM_TYPE_RECURSION_DEPTH*2 + 3)) |
| { |
| long_sig[i] = DBUS_STRUCT_END_CHAR; |
| ++i; |
| } |
| long_sig[i] = DBUS_TYPE_INVALID; |
| |
| v_STRING = long_sig; |
| if (!_dbus_header_set_field_basic (&message->header, |
| DBUS_HEADER_FIELD_SIGNATURE, |
| DBUS_TYPE_SIGNATURE, |
| &v_STRING)) |
| _dbus_assert_not_reached ("oom"); |
| |
| _dbus_header_get_field_raw (&message->header, |
| DBUS_HEADER_FIELD_SIGNATURE, |
| NULL, &pos); |
| generate_from_message (data, expected_validity, message); |
| |
| *expected_validity = DBUS_INVALID_EXCEEDED_MAXIMUM_STRUCT_RECURSION; |
| } |
| else if (item_seq == 3) |
| { |
| message = simple_method_call (); |
| if (!dbus_message_append_args (message, |
| DBUS_TYPE_INT32, &v_INT32, |
| DBUS_TYPE_INT32, &v_INT32, |
| DBUS_TYPE_INT32, &v_INT32, |
| DBUS_TYPE_INVALID)) |
| _dbus_assert_not_reached ("oom"); |
| |
| _dbus_header_get_field_raw (&message->header, |
| DBUS_HEADER_FIELD_SIGNATURE, |
| NULL, &pos); |
| generate_from_message (data, expected_validity, message); |
| |
| _dbus_string_set_byte (data, pos + 1, DBUS_STRUCT_BEGIN_CHAR); |
| |
| *expected_validity = DBUS_INVALID_STRUCT_STARTED_BUT_NOT_ENDED; |
| } |
| else if (item_seq == 4) |
| { |
| message = simple_method_call (); |
| if (!dbus_message_append_args (message, |
| DBUS_TYPE_INT32, &v_INT32, |
| DBUS_TYPE_INT32, &v_INT32, |
| DBUS_TYPE_INT32, &v_INT32, |
| DBUS_TYPE_INVALID)) |
| _dbus_assert_not_reached ("oom"); |
| |
| _dbus_header_get_field_raw (&message->header, |
| DBUS_HEADER_FIELD_SIGNATURE, |
| NULL, &pos); |
| generate_from_message (data, expected_validity, message); |
| |
| _dbus_string_set_byte (data, pos + 1, DBUS_STRUCT_END_CHAR); |
| |
| *expected_validity = DBUS_INVALID_STRUCT_ENDED_BUT_NOT_STARTED; |
| } |
| else if (item_seq == 5) |
| { |
| message = simple_method_call (); |
| if (!dbus_message_append_args (message, |
| DBUS_TYPE_INT32, &v_INT32, |
| DBUS_TYPE_INT32, &v_INT32, |
| DBUS_TYPE_INT32, &v_INT32, |
| DBUS_TYPE_INVALID)) |
| _dbus_assert_not_reached ("oom"); |
| |
| _dbus_header_get_field_raw (&message->header, |
| DBUS_HEADER_FIELD_SIGNATURE, |
| NULL, &pos); |
| generate_from_message (data, expected_validity, message); |
| |
| _dbus_string_set_byte (data, pos + 1, DBUS_STRUCT_BEGIN_CHAR); |
| _dbus_string_set_byte (data, pos + 2, DBUS_STRUCT_END_CHAR); |
| |
| *expected_validity = DBUS_INVALID_STRUCT_HAS_NO_FIELDS; |
| } |
| else if (item_seq == 6) |
| { |
| message = simple_method_call (); |
| generate_from_message (data, expected_validity, message); |
| |
| _dbus_string_set_byte (data, TYPE_OFFSET, DBUS_MESSAGE_TYPE_INVALID); |
| |
| *expected_validity = DBUS_INVALID_BAD_MESSAGE_TYPE; |
| } |
| else if (item_seq == 7) |
| { |
| /* Messages of unknown type are considered valid */ |
| message = simple_method_call (); |
| generate_from_message (data, expected_validity, message); |
| |
| _dbus_string_set_byte (data, TYPE_OFFSET, 100); |
| |
| *expected_validity = DBUS_VALID; |
| } |
| else if (item_seq == 8) |
| { |
| message = simple_method_call (); |
| generate_from_message (data, expected_validity, message); |
| |
| _dbus_marshal_set_uint32 (data, BODY_LENGTH_OFFSET, |
| DBUS_MAXIMUM_MESSAGE_LENGTH / 2 + 4, |
| message->byte_order); |
| _dbus_marshal_set_uint32 (data, FIELDS_ARRAY_LENGTH_OFFSET, |
| DBUS_MAXIMUM_MESSAGE_LENGTH / 2 + 4, |
| message->byte_order); |
| *expected_validity = DBUS_INVALID_MESSAGE_TOO_LONG; |
| } |
| else if (item_seq == 9) |
| { |
| const char *v_STRING = "not a valid bus name"; |
| message = simple_method_call (); |
| |
| if (!_dbus_header_set_field_basic (&message->header, |
| DBUS_HEADER_FIELD_SENDER, |
| DBUS_TYPE_STRING, &v_STRING)) |
| _dbus_assert_not_reached ("oom"); |
| |
| generate_from_message (data, expected_validity, message); |
| |
| *expected_validity = DBUS_INVALID_BAD_SENDER; |
| } |
| else if (item_seq == 10) |
| { |
| message = simple_method_call (); |
| |
| if (!dbus_message_set_interface (message, DBUS_INTERFACE_LOCAL)) |
| _dbus_assert_not_reached ("oom"); |
| |
| generate_from_message (data, expected_validity, message); |
| |
| *expected_validity = DBUS_INVALID_USES_LOCAL_INTERFACE; |
| } |
| else if (item_seq == 11) |
| { |
| message = simple_method_call (); |
| |
| if (!dbus_message_set_path (message, DBUS_PATH_LOCAL)) |
| _dbus_assert_not_reached ("oom"); |
| |
| generate_from_message (data, expected_validity, message); |
| |
| *expected_validity = DBUS_INVALID_USES_LOCAL_PATH; |
| } |
| else if (item_seq == 12) |
| { |
| /* Method calls don't have to have interface */ |
| message = simple_method_call (); |
| |
| if (!dbus_message_set_interface (message, NULL)) |
| _dbus_assert_not_reached ("oom"); |
| |
| generate_from_message (data, expected_validity, message); |
| |
| *expected_validity = DBUS_VALID; |
| } |
| else if (item_seq == 13) |
| { |
| /* Signals require an interface */ |
| message = simple_signal (); |
| |
| if (!dbus_message_set_interface (message, NULL)) |
| _dbus_assert_not_reached ("oom"); |
| |
| generate_from_message (data, expected_validity, message); |
| |
| *expected_validity = DBUS_INVALID_MISSING_INTERFACE; |
| } |
| else if (item_seq == 14) |
| { |
| message = simple_method_return (); |
| |
| if (!_dbus_header_delete_field (&message->header, DBUS_HEADER_FIELD_REPLY_SERIAL)) |
| _dbus_assert_not_reached ("oom"); |
| |
| generate_from_message (data, expected_validity, message); |
| |
| *expected_validity = DBUS_INVALID_MISSING_REPLY_SERIAL; |
| } |
| else if (item_seq == 15) |
| { |
| message = simple_error (); |
| |
| if (!dbus_message_set_error_name (message, NULL)) |
| _dbus_assert_not_reached ("oom"); |
| |
| generate_from_message (data, expected_validity, message); |
| |
| *expected_validity = DBUS_INVALID_MISSING_ERROR_NAME; |
| } |
| else if (item_seq == 16) |
| { |
| char long_sig[DBUS_MAXIMUM_TYPE_RECURSION_DEPTH*4+10]; |
| const char *v_STRING; |
| int i; |
| int n_begins; |
| |
| message = simple_method_call (); |
| if (!dbus_message_append_args (message, |
| DBUS_TYPE_INT32, &v_INT32, |
| DBUS_TYPE_INT32, &v_INT32, |
| DBUS_TYPE_INT32, &v_INT32, |
| DBUS_TYPE_INVALID)) |
| _dbus_assert_not_reached ("oom"); |
| |
| i = 0; |
| while (i <= (DBUS_MAXIMUM_TYPE_RECURSION_DEPTH*3 + 3)) |
| { |
| long_sig[i] = DBUS_TYPE_ARRAY; |
| ++i; |
| long_sig[i] = DBUS_DICT_ENTRY_BEGIN_CHAR; |
| ++i; |
| long_sig[i] = DBUS_TYPE_INT32; |
| ++i; |
| } |
| n_begins = i / 3; |
| |
| long_sig[i] = DBUS_TYPE_INT32; |
| ++i; |
| |
| while (n_begins > 0) |
| { |
| long_sig[i] = DBUS_DICT_ENTRY_END_CHAR; |
| ++i; |
| n_begins -= 1; |
| } |
| long_sig[i] = DBUS_TYPE_INVALID; |
| |
| v_STRING = long_sig; |
| if (!_dbus_header_set_field_basic (&message->header, |
| DBUS_HEADER_FIELD_SIGNATURE, |
| DBUS_TYPE_SIGNATURE, |
| &v_STRING)) |
| _dbus_assert_not_reached ("oom"); |
| |
| _dbus_header_get_field_raw (&message->header, |
| DBUS_HEADER_FIELD_SIGNATURE, |
| NULL, &pos); |
| generate_from_message (data, expected_validity, message); |
| |
| *expected_validity = DBUS_INVALID_EXCEEDED_MAXIMUM_DICT_ENTRY_RECURSION; |
| } |
| else if (item_seq == 17) |
| { |
| message = simple_method_call (); |
| if (!dbus_message_append_args (message, |
| DBUS_TYPE_INT32, &v_INT32, |
| DBUS_TYPE_INT32, &v_INT32, |
| DBUS_TYPE_INT32, &v_INT32, |
| DBUS_TYPE_INVALID)) |
| _dbus_assert_not_reached ("oom"); |
| |
| _dbus_header_get_field_raw (&message->header, |
| DBUS_HEADER_FIELD_SIGNATURE, |
| NULL, &pos); |
| generate_from_message (data, expected_validity, message); |
| |
| _dbus_string_set_byte (data, pos + 1, DBUS_TYPE_ARRAY); |
| _dbus_string_set_byte (data, pos + 2, DBUS_DICT_ENTRY_BEGIN_CHAR); |
| |
| *expected_validity = DBUS_INVALID_DICT_ENTRY_STARTED_BUT_NOT_ENDED; |
| } |
| else if (item_seq == 18) |
| { |
| message = simple_method_call (); |
| if (!dbus_message_append_args (message, |
| DBUS_TYPE_INT32, &v_INT32, |
| DBUS_TYPE_INT32, &v_INT32, |
| DBUS_TYPE_INT32, &v_INT32, |
| DBUS_TYPE_INVALID)) |
| _dbus_assert_not_reached ("oom"); |
| |
| _dbus_header_get_field_raw (&message->header, |
| DBUS_HEADER_FIELD_SIGNATURE, |
| NULL, &pos); |
| generate_from_message (data, expected_validity, message); |
| |
| _dbus_string_set_byte (data, pos + 1, DBUS_DICT_ENTRY_END_CHAR); |
| |
| *expected_validity = DBUS_INVALID_DICT_ENTRY_ENDED_BUT_NOT_STARTED; |
| } |
| else if (item_seq == 19) |
| { |
| message = simple_method_call (); |
| if (!dbus_message_append_args (message, |
| DBUS_TYPE_INT32, &v_INT32, |
| DBUS_TYPE_INT32, &v_INT32, |
| DBUS_TYPE_INT32, &v_INT32, |
| DBUS_TYPE_INVALID)) |
| _dbus_assert_not_reached ("oom"); |
| |
| _dbus_header_get_field_raw (&message->header, |
| DBUS_HEADER_FIELD_SIGNATURE, |
| NULL, &pos); |
| generate_from_message (data, expected_validity, message); |
| |
| _dbus_string_set_byte (data, pos + 1, DBUS_TYPE_ARRAY); |
| _dbus_string_set_byte (data, pos + 2, DBUS_DICT_ENTRY_BEGIN_CHAR); |
| _dbus_string_set_byte (data, pos + 3, DBUS_DICT_ENTRY_END_CHAR); |
| |
| *expected_validity = DBUS_INVALID_DICT_ENTRY_HAS_NO_FIELDS; |
| } |
| else if (item_seq == 20) |
| { |
| /* 64 levels of nesting is OK */ |
| message = message_with_nesting_levels(64); |
| |
| generate_from_message (data, expected_validity, message); |
| |
| *expected_validity = DBUS_VALID; |
| } |
| else if (item_seq == 21) |
| { |
| /* 65 levels of nesting is not OK */ |
| message = message_with_nesting_levels(65); |
| |
| generate_from_message (data, expected_validity, message); |
| |
| *expected_validity = DBUS_INVALID_NESTED_TOO_DEEPLY; |
| } |
| else |
| { |
| return FALSE; |
| } |
| |
| if (message) |
| dbus_message_unref (message); |
| |
| iter_next (iter); |
| return TRUE; |
| } |
| |
| static dbus_bool_t |
| generate_wrong_length (DBusMessageDataIter *iter, |
| DBusString *data, |
| DBusValidity *expected_validity) |
| { |
| int lengths[] = { -42, -17, -16, -15, -9, -8, -7, -6, -5, -4, -3, -2, -1, |
| 1, 2, 3, 4, 5, 6, 7, 8, 9, 15, 16, 30 }; |
| int adjust; |
| int len_seq; |
| |
| restart: |
| len_seq = iter_get_sequence (iter); |
| if (len_seq == _DBUS_N_ELEMENTS (lengths)) |
| return FALSE; |
| |
| _dbus_assert (len_seq < _DBUS_N_ELEMENTS (lengths)); |
| |
| iter_recurse (iter); |
| if (!generate_many_bodies (iter, data, expected_validity)) |
| { |
| iter_set_sequence (iter, 0); /* reset to first body */ |
| iter_unrecurse (iter); |
| iter_next (iter); /* next length adjustment */ |
| goto restart; |
| } |
| iter_unrecurse (iter); |
| |
| adjust = lengths[len_seq]; |
| |
| if (adjust < 0) |
| { |
| if ((_dbus_string_get_length (data) + adjust) < DBUS_MINIMUM_HEADER_SIZE) |
| _dbus_string_set_length (data, DBUS_MINIMUM_HEADER_SIZE); |
| else |
| _dbus_string_shorten (data, - adjust); |
| *expected_validity = DBUS_INVALID_FOR_UNKNOWN_REASON; |
| } |
| else |
| { |
| if (!_dbus_string_lengthen (data, adjust)) |
| _dbus_assert_not_reached ("oom"); |
| *expected_validity = DBUS_INVALID_TOO_MUCH_DATA; |
| } |
| |
| /* Fixup lengths */ |
| { |
| int old_body_len; |
| int new_body_len; |
| int byte_order; |
| |
| _dbus_assert (_dbus_string_get_length (data) >= DBUS_MINIMUM_HEADER_SIZE); |
| |
| byte_order = _dbus_string_get_byte (data, BYTE_ORDER_OFFSET); |
| old_body_len = _dbus_marshal_read_uint32 (data, |
| BODY_LENGTH_OFFSET, |
| byte_order, |
| NULL); |
| _dbus_assert (old_body_len < _dbus_string_get_length (data)); |
| new_body_len = old_body_len + adjust; |
| if (new_body_len < 0) |
| { |
| new_body_len = 0; |
| /* we just munged the header, and aren't sure how */ |
| *expected_validity = DBUS_VALIDITY_UNKNOWN; |
| } |
| |
| _dbus_verbose ("changing body len from %u to %u by adjust %d\n", |
| old_body_len, new_body_len, adjust); |
| |
| _dbus_marshal_set_uint32 (data, BODY_LENGTH_OFFSET, |
| new_body_len, |
| byte_order); |
| } |
| |
| return TRUE; |
| } |
| |
| static dbus_bool_t |
| generate_byte_changed (DBusMessageDataIter *iter, |
| DBusString *data, |
| DBusValidity *expected_validity) |
| { |
| int byte_seq; |
| int v_BYTE; |
| |
| /* This is a little convoluted to make the bodies the |
| * outer loop and each byte of each body the inner |
| * loop |
| */ |
| |
| restart: |
| if (!generate_many_bodies (iter, data, expected_validity)) |
| return FALSE; |
| |
| iter_recurse (iter); |
| byte_seq = iter_get_sequence (iter); |
| iter_next (iter); |
| iter_unrecurse (iter); |
| |
| if (byte_seq == _dbus_string_get_length (data)) |
| { |
| _dbus_string_set_length (data, 0); |
| /* reset byte count */ |
| iter_recurse (iter); |
| iter_set_sequence (iter, 0); |
| iter_unrecurse (iter); |
| goto restart; |
| } |
| else |
| { |
| /* Undo the "next" in generate_many_bodies */ |
| iter_set_sequence (iter, iter_get_sequence (iter) - 1); |
| } |
| |
| _dbus_assert (byte_seq < _dbus_string_get_length (data)); |
| v_BYTE = _dbus_string_get_byte (data, byte_seq); |
| v_BYTE += byte_seq; /* arbitrary but deterministic change to the byte */ |
| _dbus_string_set_byte (data, byte_seq, v_BYTE); |
| *expected_validity = DBUS_VALIDITY_UNKNOWN; |
| |
| return TRUE; |
| } |
| |
| static dbus_bool_t |
| find_next_typecode (DBusMessageDataIter *iter, |
| DBusString *data, |
| DBusValidity *expected_validity) |
| { |
| int body_seq; |
| int byte_seq; |
| int base_depth; |
| |
| base_depth = iter->depth; |
| |
| restart: |
| _dbus_assert (iter->depth == (base_depth + 0)); |
| _dbus_string_set_length (data, 0); |
| |
| body_seq = iter_get_sequence (iter); |
| |
| if (!generate_many_bodies (iter, data, expected_validity)) |
| return FALSE; |
| /* Undo the "next" in generate_many_bodies */ |
| iter_set_sequence (iter, body_seq); |
| |
| iter_recurse (iter); |
| while (TRUE) |
| { |
| _dbus_assert (iter->depth == (base_depth + 1)); |
| |
| byte_seq = iter_get_sequence (iter); |
| |
| _dbus_assert (byte_seq <= _dbus_string_get_length (data)); |
| |
| if (byte_seq == _dbus_string_get_length (data)) |
| { |
| /* reset byte count */ |
| iter_set_sequence (iter, 0); |
| iter_unrecurse (iter); |
| _dbus_assert (iter->depth == (base_depth + 0)); |
| iter_next (iter); /* go to the next body */ |
| goto restart; |
| } |
| |
| _dbus_assert (byte_seq < _dbus_string_get_length (data)); |
| |
| if (_dbus_type_is_valid (_dbus_string_get_byte (data, byte_seq))) |
| break; |
| else |
| iter_next (iter); |
| } |
| |
| _dbus_assert (byte_seq == iter_get_sequence (iter)); |
| _dbus_assert (byte_seq < _dbus_string_get_length (data)); |
| |
| iter_unrecurse (iter); |
| |
| _dbus_assert (iter->depth == (base_depth + 0)); |
| |
| return TRUE; |
| } |
| |
| static const int typecodes[] = { |
| DBUS_TYPE_INVALID, |
| DBUS_TYPE_BYTE, |
| DBUS_TYPE_BOOLEAN, |
| DBUS_TYPE_INT16, |
| DBUS_TYPE_UINT16, |
| DBUS_TYPE_INT32, |
| DBUS_TYPE_UINT32, |
| DBUS_TYPE_INT64, |
| DBUS_TYPE_UINT64, |
| DBUS_TYPE_DOUBLE, |
| DBUS_TYPE_STRING, |
| DBUS_TYPE_OBJECT_PATH, |
| DBUS_TYPE_SIGNATURE, |
| DBUS_TYPE_ARRAY, |
| DBUS_TYPE_VARIANT, |
| DBUS_STRUCT_BEGIN_CHAR, |
| DBUS_STRUCT_END_CHAR, |
| DBUS_DICT_ENTRY_BEGIN_CHAR, |
| DBUS_DICT_ENTRY_END_CHAR, |
| DBUS_TYPE_UNIX_FD, |
| 255 /* random invalid typecode */ |
| }; |
| |
| static dbus_bool_t |
| generate_typecode_changed (DBusMessageDataIter *iter, |
| DBusString *data, |
| DBusValidity *expected_validity) |
| { |
| int byte_seq; |
| int typecode_seq; |
| int base_depth; |
| |
| base_depth = iter->depth; |
| |
| restart: |
| _dbus_assert (iter->depth == (base_depth + 0)); |
| _dbus_string_set_length (data, 0); |
| |
| if (!find_next_typecode (iter, data, expected_validity)) |
| return FALSE; |
| |
| iter_recurse (iter); |
| byte_seq = iter_get_sequence (iter); |
| |
| _dbus_assert (byte_seq < _dbus_string_get_length (data)); |
| |
| iter_recurse (iter); |
| typecode_seq = iter_get_sequence (iter); |
| iter_next (iter); |
| |
| _dbus_assert (typecode_seq <= _DBUS_N_ELEMENTS (typecodes)); |
| |
| if (typecode_seq == _DBUS_N_ELEMENTS (typecodes)) |
| { |
| _dbus_assert (iter->depth == (base_depth + 2)); |
| iter_set_sequence (iter, 0); /* reset typecode sequence */ |
| iter_unrecurse (iter); |
| _dbus_assert (iter->depth == (base_depth + 1)); |
| iter_next (iter); /* go to the next byte_seq */ |
| iter_unrecurse (iter); |
| _dbus_assert (iter->depth == (base_depth + 0)); |
| goto restart; |
| } |
| |
| _dbus_assert (iter->depth == (base_depth + 2)); |
| iter_unrecurse (iter); |
| _dbus_assert (iter->depth == (base_depth + 1)); |
| iter_unrecurse (iter); |
| _dbus_assert (iter->depth == (base_depth + 0)); |
| |
| #if 0 |
| printf ("Changing byte %d in message %d to %c\n", |
| byte_seq, iter_get_sequence (iter), typecodes[typecode_seq]); |
| #endif |
| |
| _dbus_string_set_byte (data, byte_seq, typecodes[typecode_seq]); |
| *expected_validity = DBUS_VALIDITY_UNKNOWN; |
| return TRUE; |
| } |
| |
| typedef struct |
| { |
| ChangeType type; |
| dbus_uint32_t value; /* cast to signed for adjusts */ |
| } UIntChange; |
| |
| static const UIntChange uint32_changes[] = { |
| { CHANGE_TYPE_ADJUST, (dbus_uint32_t) -1 }, |
| { CHANGE_TYPE_ADJUST, (dbus_uint32_t) -2 }, |
| { CHANGE_TYPE_ADJUST, (dbus_uint32_t) -3 }, |
| { CHANGE_TYPE_ADJUST, (dbus_uint32_t) 1 }, |
| { CHANGE_TYPE_ADJUST, (dbus_uint32_t) 2 }, |
| { CHANGE_TYPE_ADJUST, (dbus_uint32_t) 3 }, |
| { CHANGE_TYPE_ABSOLUTE, _DBUS_UINT32_MAX }, |
| { CHANGE_TYPE_ABSOLUTE, 0 }, |
| { CHANGE_TYPE_ABSOLUTE, 1 }, |
| { CHANGE_TYPE_ABSOLUTE, _DBUS_UINT32_MAX - 1 }, |
| { CHANGE_TYPE_ABSOLUTE, _DBUS_UINT32_MAX - 5 } |
| }; |
| |
| static dbus_bool_t |
| generate_uint32_changed (DBusMessageDataIter *iter, |
| DBusString *data, |
| DBusValidity *expected_validity) |
| { |
| int body_seq; |
| int byte_seq; |
| int change_seq; |
| dbus_uint32_t v_UINT32; |
| int byte_order; |
| const UIntChange *change; |
| int base_depth; |
| |
| /* Outer loop is each body, next loop is each change, |
| * inner loop is each change location |
| */ |
| |
| base_depth = iter->depth; |
| |
| next_body: |
| _dbus_assert (iter->depth == (base_depth + 0)); |
| _dbus_string_set_length (data, 0); |
| body_seq = iter_get_sequence (iter); |
| |
| if (!generate_many_bodies (iter, data, expected_validity)) |
| return FALSE; |
| |
| _dbus_assert (iter->depth == (base_depth + 0)); |
| |
| iter_set_sequence (iter, body_seq); /* undo the "next" from generate_many_bodies */ |
| iter_recurse (iter); |
| next_change: |
| _dbus_assert (iter->depth == (base_depth + 1)); |
| change_seq = iter_get_sequence (iter); |
| |
| if (change_seq == _DBUS_N_ELEMENTS (uint32_changes)) |
| { |
| /* Reset change count */ |
| iter_set_sequence (iter, 0); |
| iter_unrecurse (iter); |
| iter_next (iter); |
| goto next_body; |
| } |
| |
| _dbus_assert (iter->depth == (base_depth + 1)); |
| |
| iter_recurse (iter); |
| _dbus_assert (iter->depth == (base_depth + 2)); |
| byte_seq = iter_get_sequence (iter); |
| /* skip 4 bytes at a time */ |
| iter_next (iter); |
| iter_next (iter); |
| iter_next (iter); |
| iter_next (iter); |
| iter_unrecurse (iter); |
| |
| _dbus_assert (_DBUS_ALIGN_VALUE (byte_seq, 4) == (unsigned) byte_seq); |
| if (byte_seq >= (_dbus_string_get_length (data) - 4)) |
| { |
| /* reset byte count */ |
| _dbus_assert (iter->depth == (base_depth + 1)); |
| iter_recurse (iter); |
| _dbus_assert (iter->depth == (base_depth + 2)); |
| iter_set_sequence (iter, 0); |
| iter_unrecurse (iter); |
| _dbus_assert (iter->depth == (base_depth + 1)); |
| iter_next (iter); |
| goto next_change; |
| } |
| |
| _dbus_assert (byte_seq <= (_dbus_string_get_length (data) - 4)); |
| |
| byte_order = _dbus_string_get_byte (data, BYTE_ORDER_OFFSET); |
| |
| v_UINT32 = _dbus_marshal_read_uint32 (data, byte_seq, byte_order, NULL); |
| |
| change = &uint32_changes[change_seq]; |
| |
| if (change->type == CHANGE_TYPE_ADJUST) |
| { |
| v_UINT32 += (int) change->value; |
| } |
| else |
| { |
| v_UINT32 = change->value; |
| } |
| |
| #if 0 |
| printf ("body %d change %d pos %d ", |
| body_seq, change_seq, byte_seq); |
| |
| if (change->type == CHANGE_TYPE_ADJUST) |
| printf ("adjust by %d", (int) change->value); |
| else |
| printf ("set to %u", change->value); |
| |
| printf (" \t%u -> %u\n", |
| _dbus_marshal_read_uint32 (data, byte_seq, byte_order, NULL), |
| v_UINT32); |
| #endif |
| |
| _dbus_marshal_set_uint32 (data, byte_seq, v_UINT32, byte_order); |
| *expected_validity = DBUS_VALIDITY_UNKNOWN; |
| |
| _dbus_assert (iter->depth == (base_depth + 1)); |
| iter_unrecurse (iter); |
| _dbus_assert (iter->depth == (base_depth + 0)); |
| |
| return TRUE; |
| } |
| |
| typedef struct |
| { |
| const char *name; |
| DBusMessageGeneratorFunc func; |
| } DBusMessageGenerator; |
| |
| static const DBusMessageGenerator generators[] = { |
| { "trivial example of each message type", generate_trivial }, |
| { "assorted arguments", generate_many_bodies }, |
| { "assorted special cases", generate_special }, |
| { "each uint32 modified", generate_uint32_changed }, |
| { "wrong body lengths", generate_wrong_length }, |
| { "each byte modified", generate_byte_changed }, |
| #if 0 |
| /* This is really expensive and doesn't add too much coverage */ |
| { "change each typecode", generate_typecode_changed } |
| #endif |
| }; |
| |
| void |
| _dbus_message_data_free (DBusMessageData *data) |
| { |
| _dbus_string_free (&data->data); |
| } |
| |
| void |
| _dbus_message_data_iter_init (DBusMessageDataIter *iter) |
| { |
| int i; |
| |
| iter->depth = 0; |
| i = 0; |
| while (i < _DBUS_MESSAGE_DATA_MAX_NESTING) |
| { |
| iter->sequence_nos[i] = 0; |
| ++i; |
| } |
| iter->count = 0; |
| } |
| |
| dbus_bool_t |
| _dbus_message_data_iter_get_and_next (DBusMessageDataIter *iter, |
| DBusMessageData *data) |
| { |
| DBusMessageGeneratorFunc func; |
| int generator; |
| |
| restart: |
| generator = iter_get_sequence (iter); |
| |
| if (generator == _DBUS_N_ELEMENTS (generators)) |
| return FALSE; |
| |
| iter_recurse (iter); |
| |
| if (iter_first_in_series (iter)) |
| { |
| printf (" testing message loading: %s ", generators[generator].name); |
| fflush (stdout); |
| } |
| |
| func = generators[generator].func; |
| |
| if (!_dbus_string_init (&data->data)) |
| _dbus_assert_not_reached ("oom"); |
| |
| if ((*func)(iter, &data->data, &data->expected_validity)) |
| ; |
| else |
| { |
| iter_set_sequence (iter, 0); |
| iter_unrecurse (iter); |
| iter_next (iter); /* next generator */ |
| _dbus_string_free (&data->data); |
| printf ("%d test loads cumulative\n", iter->count); |
| goto restart; |
| } |
| iter_unrecurse (iter); |
| |
| iter->count += 1; |
| return TRUE; |
| } |
| |
| #endif /* !DOXYGEN_SHOULD_SKIP_THIS */ |
| |
| #endif /* DBUS_BUILD_TESTS */ |