summaryrefslogtreecommitdiffstats
path: root/dbus
diff options
context:
space:
mode:
authorHavoc Pennington <hp@redhat.com>2003-09-30 02:33:11 +0000
committerHavoc Pennington <hp@redhat.com>2003-09-30 02:33:11 +0000
commitdfd1292d525d01914141cc86013589c6e0ea9d5c (patch)
treefd0c5dd4296d970abcd70f16dd39cca711177df0 /dbus
parentc30e28fdae3863651cfd7b5d3d0721a1b21a6919 (diff)
parent626db3fc5c36879186315fcc6de78824a7b75e9b (diff)
2003-09-29 Havoc Pennington <hp@pobox.com>
* Merge dbus-object-names branch. To see the entire patch do cvs diff -r DBUS_OBJECT_NAMES_BRANCHPOINT -r dbus-object-names, it's huuuuge though. To revert, I tagged DBUS_BEFORE_OBJECT_NAMES_MERGE. 2003-09-28 Havoc Pennington <hp@pobox.com> * HACKING: update to reflect new server 2003-09-26 Seth Nickell <seth@gnome.org> * python/dbus.py: * python/examples/example-signals.py: Start implementing some notions of signals. The API is really terrible, but they sort of work (with the exception of being able to filter by service, and to transmit signals *as* a particular service). Need to figure out how to make messages come from the service we registered :-( * python/dbus_bindings.pyx.in: Removed duplicate message_handler callbacks. 2003-09-25 Havoc Pennington <hp@redhat.com> * bus/session.conf.in: fix my mess 2003-09-25 Havoc Pennington <hp@pobox.com> * bus/session.conf.in: fix security policy, reported by Seth Nickell 2003-09-25 Seth Nickell <seth@gnome.org> * python/examples/example-service.py: Johan notices complete wrong code in example-service, but completely wrong in a way that works exactly the same (!). Johan is confused, how could this possibly work? Example code fails to serve purpose of making things clear. Seth fixes. 2003-09-25 Mark McLoughlin <mark@skynet.ie> * doc/dbus-specification.sgml: don't require header fields to be 4-byte aligned and specify that fields should be distinguished from padding by the fact that zero is not a valid field name. * doc/TODO: remove re-alignment item and add item to doc the OBJECT_PATH type. * dbus/dbus-message.c: (HeaderField): rename the original member to value_offset and introduce a name_offset member to keep track of where the field actually begins. (adjust_field_offsets): remove. (append_int_field), (append_uint_field), (append_string_field): don't align the start of the header field to a 4-byte boundary. (get_next_field): impl finding the next marhsalled field after a given field. (re_align_field_recurse): impl re-aligning a number of already marshalled fields. (delete_field): impl deleting a field of any type and re-aligning any following fields. (delete_int_or_uint_field), (delete_string_field): remove. (set_int_field), (set_uint_field): no need to re-check that we have the correct type for the field. (set_string_field): ditto and impl re-aligning any following fields. (decode_header_data): update to take into account that the fields aren't 4-byte aligned any more and the new way to distinguish padding from header fields. Also, don't exit when there is too much header padding. (process_test_subdir): print the directory. (_dbus_message_test): add test to make sure a following field is re-aligned correctly after field deletion. * dbus/dbus-string.[ch]: (_dbus_string_insert_bytes): rename from insert_byte and allow the insert of multiple bytes. (_dbus_string_test): test inserting multiple bytes. * dbus/dbus-marshal.c: (_dbus_marshal_set_string): add warning note to docs about having to re-align any marshalled values following the string. * dbus/dbus-message-builder.c: (append_string_field), (_dbus_message_data_load): don't align the header field. * dbus/dbus-auth.c: (process_test_subdir): print the directory. * test/break-loader.c: (randomly_add_one_byte): upd. for insert_byte change. * test/data/invalid-messages/bad-header-field-alignment.message: new test case. * test/data/valid-messages/unknown-header-field.message: shove a dict in the unknown field. 2003-09-25 Seth Nickell <seth@gnome.org> * python/dbus.py: * python/dbus_bindings.pyx.in: Handle return values. * python/examples/example-client.py: * python/examples/example-service.py: Pass back return values from the service to the client. 2003-09-24 Seth Nickell <seth@gnome.org> * python/dbus.py: Connect Object methods (when you are sharing an object) up... pass in a list of methods to be shared. Sharing all the methods just worked out too weird. You can now create nice Services over the DBus in Python. :-) * python/dbus_bindings.pyx.in: Keep references to user_data tuples passed into C functions so Python doesn't garbage collect on us. Implement MethodReturn and Error subclasses of Message for creating DBusMessage's of those types. * python/examples/example-client.py: * python/examples/example-service.py: Simple example code showing both how create DBus services and objects, and how to use them. 2003-09-23 Havoc Pennington <hp@pobox.com> * glib/dbus-gproxy.c (dbus_gproxy_manager_filter): implement 2003-09-23 Havoc Pennington <hp@redhat.com> * glib/dbus-gproxy.c (dbus_gproxy_connect_signal): implement (dbus_gproxy_disconnect_signal): implement (dbus_gproxy_manager_remove_signal_match): implement (dbus_gproxy_manager_add_signal_match): implement (dbus_gproxy_oneway_call): implement 2003-09-23 Havoc Pennington <hp@pobox.com> * glib/dbus-gproxy.c (struct DBusGProxy): convert to a GObject subclass. This means dropping the transparent thread safety of the proxy; you now need a separate proxy per-thread, or your own locking on the proxy. Probably right anyway. (dbus_gproxy_ref, dbus_gproxy_unref): nuke, just use g_object_ref 2003-09-22 Havoc Pennington <hp@redhat.com> * glib/dbus-gproxy.c (dbus_gproxy_manager_get): implement 2003-09-21 Seth Nickell <seth@gnome.org> First checkin of the Python bindings. * python/.cvsignore: * python/Makefile.am: * python/dbus_bindings.pyx.in: * python/dbus_h_wrapper.h: Pieces for Pyrex to operate on, building a dbus_bindings.so python module for low-level access to the DBus APIs. * python/dbus.py: High-level Python module for accessing DBus objects. * configure.in: * Makefile.am: Build stuff for the python bindings. * acinclude.m4: Extra macro needed for finding the Python C header files. 2003-09-21 Havoc Pennington <hp@pobox.com> * glib/dbus-gproxy.c (dbus_gproxy_manager_new): start implementing the proxy manager, didn't get very far. * dbus/dbus-bus.c (dbus_bus_add_match): new (dbus_bus_remove_match): new * glib/dbus-gproxy.c (dbus_gproxy_new_for_service): add a path_name argument; adjust the other not-yet-implemented gproxy constructors to be what I think they should be. 2003-09-21 Havoc Pennington <hp@pobox.com> * dbus/dbus-bus.c (dbus_bus_get): set exit_on_disconnect to TRUE by default for message bus connections. * dbus/dbus-connection.c (dbus_connection_dispatch): exit if exit_on_disconnect flag is set and we process the disconnected signal. (dbus_connection_set_exit_on_disconnect): new function 2003-09-21 Havoc Pennington <hp@pobox.com> Get matching rules mostly working in the bus; only actually parsing the rule text remains. However, the client side of "signal connections" hasn't been started, this patch is only the bus side. * dbus/dispatch.c: fix for the matching rules changes * bus/driver.c (bus_driver_handle_remove_match) (bus_driver_handle_add_match): send an ack reply from these method calls * glib/dbus-gproxy.c (dbus_gproxy_begin_call): fix order of arguments, reported by Seth Nickell * bus/config-parser.c (append_rule_from_element): support eavesdrop=true|false attribute on policies so match rules can be prevented from snooping on the system bus. * bus/dbus-daemon-1.1.in: consistently use terminology "sender" and "destination" in attribute names; fix some docs bugs; add eavesdrop=true|false attribute * bus/driver.c (bus_driver_handle_add_match) (bus_driver_handle_remove_match): handle AddMatch, RemoveMatch messages * dbus/dbus-protocol.h (DBUS_SERVICE_ORG_FREEDESKTOP_BROADCAST): get rid of broadcast service concept, signals are just always broadcast * bus/signals.c, bus/dispatch.c, bus/connection.c, bus/bus.c: mostly implement matching rules stuff (currently only exposed as signal connections) 2003-09-21 Mark McLoughlin <mark@skynet.ie> * doc/dbus-specification.sgml: Change the header field name to be an enum and update the rest of the spec to reference the fields using the conventinal name. * dbus/dbus-protocol.h: update to reflect the spec. * doc/TODO: add item to remove the 4 byte alignment requirement. * dbus/dbus-message.c: Remove the code to generalise the header/body length and serial number header fields as named header fields so we can reference field names using the protocol values. (append_int_field), (append_uint_field), (append_string_field): Append the field name as a byte rather than four chars. (delete_int_or_uint_field), (delete_string_field): reflect the fact that the field name and typecode now occupy 4 bytes instead of 8. (decode_string_field), (decode_header_data): update to reflect protocol changes and move the field specific encoding from decode_string_field() back into decode_header_data(). * dbus/dbus-internals.[ch]: (_dbus_header_field_to_string): Add utility to aid debugging. * dbus/dbus-message-builder.c: (append_string_field), (_dbus_message_data_load): Update to reflect protocol changes; Change the FIELD_NAME directive to HEADER_FIELD and allow it to take the field's conventional name rather than the actual value. * test/data/*/*.message: Update to use HEADER_FIELD instead of FIELD_NAME; Always align the header on an 8 byte boundary *before* updating the header length. 2003-09-15 Havoc Pennington <hp@pobox.com> * dbus/dbus-pending-call.c: add the get/set object data boilerplate as for DBusConnection, etc. Use generic object data for the notify callback. * glib/dbus-gparser.c (parse_node): parse child nodes * tools/dbus-viewer.c: more hacking on the dbus-viewer * glib/dbus-gutils.c (_dbus_gutils_split_path): add a file to contain functions shared between the convenience lib and the installed lib * glib/Makefile.am (libdbus_glib_1_la_LDFLAGS): add -export-symbols-regex to the GLib library * dbus/dbus-object-tree.c (_dbus_object_tree_dispatch_and_unlock): fix the locking in here, and add a default handler for Introspect() that just returns sub-nodes. 2003-09-14 Havoc Pennington <hp@pobox.com> * glib/dbus-gthread.c (dbus_g_thread_init): rename to make g_foo rather than gfoo consistent * glib/dbus-gproxy.h: delete for now, move contents to dbus-glib.h, because the include files don't work right since we aren't in the dbus/ subdir. * glib/dbus-gproxy.c (dbus_gproxy_send): finish implementing (dbus_gproxy_end_call): finish (dbus_gproxy_begin_call): finish * glib/dbus-gmain.c (dbus_set_g_error): new * glib/dbus-gobject.c (handle_introspect): include information about child nodes in the introspection * dbus/dbus-connection.c (dbus_connection_list_registered): new function to help in implementation of introspection * dbus/dbus-object-tree.c (_dbus_object_tree_list_registered_and_unlock): new function 2003-09-12 Havoc Pennington <hp@pobox.com> * glib/dbus-gidl.h: add common base class for all the foo_info types * tools/dbus-viewer.c: add GTK-based introspection UI thingy similar to kdcop * test/Makefile.am: try test srcdir -ef . in addition to test srcdir = ., one of them should work (yeah lame) * glib/Makefile.am: build the "idl" parser stuff as a convenience library * glib/dbus-gparser.h: make description_load routines return NodeInfo* not Parser* * Makefile.am (SUBDIRS): build test dir after all library dirs * configure.in: add GTK+ detection 2003-09-07 Havoc Pennington <hp@pobox.com> * Make Doxygen contented. 2003-09-07 Havoc Pennington <hp@pobox.com> * doc/dbus-specification.sgml: more updates 2003-09-06 Havoc Pennington <hp@pobox.com> * doc/dbus-specification.sgml: partial updates * bus/dbus-daemon-1.1.in: fix the config file docs for the zillionth time; hopefully I edited the right file this time. * bus/config-parser.c (append_rule_from_element): support send_type, send_path, receive_type, receive_path * bus/policy.c: add message type and path to the list of things that can be "firewalled" 2003-09-06 Havoc Pennington <hp@pobox.com> * dbus/dbus-connection.c (dbus_connection_register_fallback): add this (dbus_connection_register_object_path): make this not handle messages to paths below the given path 2003-09-03 Havoc Pennington <hp@pobox.com> * test/glib/Makefile.am: add this with random glib-linked test programs * glib/Makefile.am: remove the random test programs from here, leave only the unit tests * glib/dbus-gobject.c (_dbus_gobject_test): add test for uscore/javacaps conversion, and fix (get_object_property, set_object_property): change to .NET convention for mapping props to methods, set_FooBar/get_FooBar, since one language has such a convention we may as well copy it. Plus real methods in either getFooBar or get_foo_bar style won't collide with this convention. 2003-09-01 Havoc Pennington <hp@pobox.com> * glib/dbus-gparser.c: implement * glib/dbus-gobject.c: start implementing skeletons support * configure.in: when disabling checks/assert, also define G_DISABLE_ASSERT and G_DISABLE_CHECKS 2003-09-01 Havoc Pennington <hp@pobox.com> * glib/Makefile.am: rearrange a bunch of files and get "make check" framework set up 2003-08-31 Havoc Pennington <hp@pobox.com> * fix build with --disable-tests 2003-08-30 Havoc Pennington <hp@pobox.com> * dbus/dbus-connection.c: purge DBusMessageHandler * dbus/dbus-message-handler.c: remove DBusMessageHandler, just use callbacks everywhere 2003-08-30 Havoc Pennington <hp@pobox.com> * test/data/valid-config-files/system.d/test.conf: change to root for the user so warnings don't get printed * dbus/dbus-message.c: add dbus_message_get_path, dbus_message_set_path * dbus/dbus-object-tree.c (do_test_dispatch): add test of dispatching to a path * dbus/dbus-string.c (_dbus_string_validate_path): add * dbus/dbus-marshal.c (_dbus_demarshal_object_path): implement (_dbus_marshal_object_path): implement * dbus/dbus-protocol.h (DBUS_HEADER_FIELD_PATH): new header field to contain the path to the target object (DBUS_HEADER_FIELD_SENDER_SERVICE): rename DBUS_HEADER_FIELD_SENDER to explicitly say it's the sender service 2003-08-30 Havoc Pennington <hp@pobox.com> * dbus/dbus-object-tree.c: write tests and fix the discovered bugs 2003-08-29 Havoc Pennington <hp@pobox.com> * dbus/dbus-object-tree.c: modify to allow overlapping paths to be registered (struct DBusObjectSubtree): shrink this a lot, since we may have a lot of them (_dbus_object_tree_free_all_unlocked): implement (_dbus_object_tree_dispatch_and_unlock): implement 2003-08-29 Havoc Pennington <hp@pobox.com> * dbus/dbus-internals.h: fix _DBUS_N_GLOBAL_LOCKS 2003-08-28 Havoc Pennington <hp@pobox.com> purge DBusObjectID * dbus/dbus-connection.c: port to no ObjectID, create a DBusObjectTree, rename ObjectTree to ObjectPath in public API * dbus/dbus-connection.h (struct DBusObjectTreeVTable): delete everything except UnregisterFunction and MessageFunction * dbus/dbus-marshal.c: port away from DBusObjectID, add DBUS_TYPE_OBJECT_PATH * dbus/dbus-object-registry.[hc], dbus/dbus-object.[hc], dbus/dbus-objectid.[hc]: remove these, we are moving to path-based object IDs 2003-08-25 Havoc Pennington <hp@pobox.com> Just noticed that dbus_message_test is hosed, I wonder when I broke that. I thought make check was passing earlier... * dbus/dbus-object-tree.c: add new "object tree" to match DCOP container tree, will replace most of dbus-object-registry * dbus/dbus-string.c (_dbus_string_append_printf_valist): fix C99 screwup 2003-08-19 Havoc Pennington <hp@pobox.com> * dbus/dbus-message.c (decode_string_field): support FIELD_SENDER (dbus_message_is_error): fix this function * bus/dbus-daemon-1.1: clarify logic on when <deny>/<allow> rules match * bus/policy.c (bus_client_policy_check_can_receive): fix code to reflect clarified man page (bus_client_policy_check_can_send): ditto * bus/session.conf.in: fixup * bus/system.conf.in: fixup 2003-08-18 Havoc Pennington <hp@redhat.com> * dbus/dbus-hash.c (_dbus_hash_table_insert_two_strings): fix * dbus/dbus-message.c (_dbus_message_loader_queue_messages): fix dumb bug created earlier (wrong order of args to decode_header_data()) * tools/dbus-send.c: port * tools/dbus-print-message.c (print_message): port * test/data/*messages: port all messages over * dbus/dbus-message-builder.c: support including message type * bus/driver.c: port over * bus/dispatch.c: port over to new stuff * dbus/dbus-connection.c (_dbus_connection_new_for_transport): rename disconnect signal to "Disconnected" 2003-08-17 Havoc Pennington <hp@pobox.com> This doesn't compile yet, but syncing up so I can hack on it from work. What are branches for if not broken code? ;-) * dbus/dbus-protocol.h: remove DBUS_HEADER_FIELD_NAME, add DBUS_HEADER_FIELD_INTERFACE, DBUS_HEADER_FIELD_MEMBER, DBUS_HEADER_FIELD_ERROR_NAME * dbus/dbus-hash.c: Introduce DBUS_HASH_TWO_STRINGS as hack to use for the interface+member pairs (string_hash): change to use g_str_hash algorithm (find_direct_function, find_string_function): refactor these to share most code. * dbus/dbus-message.c: port all of this over to support interface/member fields instead of name field * dbus/dbus-object-registry.c: port over * dbus/dbus-string.c (_dbus_string_validate_interface): rename from _dbus_string_validate_name * bus/dbus-daemon-1.1: change file format for the <deny>/<allow> stuff to match new message naming scheme * bus/policy.c: port over * bus/config-parser.c: parse new format 2003-08-16 Havoc Pennington <hp@pobox.com> * dbus/dbus-object-registry.c (add_and_remove_objects): remove broken assertion * glib/dbus-gproxy.c: some hacking 2003-08-15 Havoc Pennington <hp@redhat.com> * dbus/dbus-pending-call.c (dbus_pending_call_block): implement * dbus/dbus-connection.c (dbus_connection_send_with_reply_and_block): factor out internals; change to convert any error replies to DBusError instead of returning them as a message 2003-08-15 Havoc Pennington <hp@pobox.com> * dbus/dbus-connection.c, dbus/dbus-pending-call.c: Finish the pending call stuff 2003-08-14 Havoc Pennington <hp@redhat.com> * dbus/dbus-pending-call.c: start on new object that will replace DBusMessageHandler and ReplyHandlerData for tracking outstanding replies * dbus/dbus-gproxy.c: start on proxy object used to communicate with remote interfaces * dbus/dbus-gidl.c: do the boring boilerplate in here 2003-08-12 Havoc Pennington <hp@pobox.com> * bus/dispatch.c (bus_dispatch): make this return proper DBusHandlerResult to avoid DBUS_ERROR_UNKNOWN_METHOD * dbus/dbus-errors.c (dbus_set_error): use _dbus_string_append_printf_valist * dbus/dbus-string.c (_dbus_string_append_printf_valist) (_dbus_string_append_printf): new * dbus/dbus-errors.h (DBUS_ERROR_UNKNOWN_MESSAGE): change to UNKNOWN_METHOD * dbus/dbus-connection.c (dbus_connection_dispatch): handle DBUS_HANDLER_RESULT_NEED_MEMORY; send default error reply if a message is unhandled. 2003-08-11 Havoc Pennington <hp@pobox.com> * bus/test.c (client_disconnect_handler): change to return HANDLED (would have been REMOVE_MESSAGE) * dbus/dbus-object.h (enum DBusHandlerResult): rename to HANDLED/NOT_YET_HANDLED instead of REMOVE_MESSAGE/ALLOW_MORE_HANDLERS to make it clearer how it should be used. 2003-08-10 Havoc Pennington <hp@pobox.com> * tools/dbus-send.c (main): add --type argument, for now supporting only method_call and signal types. * tools/dbus-print-message.c: print message type * dbus/dbus-connection.c (_dbus_connection_new_for_transport): init connection->objects * doc/dbus-specification.sgml: fix sgml * bus/*.c: port over to object-instance API changes * test/test-service.c: ditto * dbus/dbus-message.c (dbus_message_create_header): allow #NULL name, we will have to fix up the rest of the code to also handle this (dbus_message_new): generic message-creation call (set_string_field): allow appending name field 2003-08-06 Havoc Pennington <hp@pobox.com> * dbus/dbus-object-registry.c: implement signal connection and dispatch * dbus/dbus-connection.c (_dbus_connection_unref_unlocked): new * dbus/dbus-internals.c (_dbus_memdup): new function 2003-08-02 Havoc Pennington <hp@pobox.com> * dbus/dbus-message.c (dbus_message_get_no_reply) (dbus_message_set_no_reply): add these and remove set_is_error/get_is_error * dbus/dbus-protocol.h, doc/dbus-specification.sgml: remove the ERROR flag, since there's now an ERROR type 2003-08-01 Havoc Pennington <hp@pobox.com> * dbus/dbus-object-registry.c (_dbus_object_registry_handle_and_unlock): implement * dbus/dbus-message.c (dbus_message_get_type): new function * doc/dbus-specification.sgml: add "type" byte to messages 2003-08-01 Havoc Pennington <hp@pobox.com> * dbus/dbus-protocol.h (DBUS_MESSAGE_TYPE_*): introduce a message type enum to distinguish kinds of message (DBUS_HEADER_FLAG_NO_REPLY_EXPECTED): flag for a message that need not be replied to 2003-08-01 Havoc Pennington <hp@pobox.com> * dbus/dbus-marshal.c: adapt to DBusObjectID changes (unpack_8_octets): fix no-64-bit-int bug * dbus/dbus-object-registry.c (validate_id): validate the connection ID bits, not just the instance ID. * dbus/dbus-connection.c (_dbus_connection_init_id): initialize the connection-global 33 bits of the object ID * dbus/dbus-object-registry.c (info_from_entry): fill in object ID in the new way * dbus/dbus-objectid.h: rather than high/low bits, specifically define server/client/instance bits. 2003-07-30 Havoc Pennington <hp@pobox.com> * dbus/dbus-connection.c (dbus_connection_register_object): fix build 2003-07-13 Havoc Pennington <hp@pobox.com> * dbus/dbus-object.h (struct DBusObjectVTable): add padding fields to DBusObjectVTable and DBusObjectInfo 2003-07-12 Havoc Pennington <hp@pobox.com> * dbus/dbus-object-registry.c: implement unit test, fix bugs discovered in process * dbus/dbus-connection.c: remove handler_table and register_handler(), add DBusObjectRegistry usage * dbus/dbus-objectid.c (dbus_object_id_is_null) (dbus_object_id_set_null): new functions 2003-07-08 Havoc Pennington <hp@pobox.com> * dbus/dbus-object.c: implement some of this * dbus/dbus-object-registry.c (_dbus_object_registry_add_and_unlock): fill in the object_id out param (_dbus_object_registry_new): handle OOM 2003-07-08 Havoc Pennington <hp@pobox.com> * dbus/dbus-object.h: sketch out an API for registering objects with a connection, that allows us to use as little as 24 bytes per object and lets application code represent an object in any conceivable way. * dbus/dbus-object-registry.c: implement the hard bits of the DBusConnection aspect of object API. Not yet wired up. 2003-07-06 Havoc Pennington <hp@pobox.com> * dbus/dbus-marshal.c (_dbus_marshal_set_object_id): new function (_dbus_marshal_object_id): new (_dbus_demarshal_object_id): new (_dbus_marshal_get_arg_end_pos): support object ID type, and consolidate identical switch cases. Don't conditionalize handling of DBUS_TYPE_UINT64, need to handle the type always. (_dbus_marshal_validate_arg): consolidate identical cases, and handle DBUS_TYPE_OBJECT_ID * dbus/dbus-objectid.c: new file with DBusObjectID data type. * dbus/dbus-protocol.h: add DBUS_TYPE_OBJECT_ID 2003-09-28 Havoc Pennington <hp@pobox.com> * real 0.13 release 2003-09-28 Havoc Pennington <hp@pobox.com> * doc/Makefile.am (dbus-specification.html): testing a funky hack to work with Debian db2html 2003-09-28 Havoc Pennington <hp@pobox.com> * configure.in: 0.13 * doc/Makefile.am (dbus-test-plan.html): accept nonexistence of stylesheet-images for benefit of Debian Change back to using filesystem-linked sockets for the system bus, so only root can create the default system bus address. * bus/system.conf.in: change to use DBUS_SYSTEM_BUS_DEFAULT_ADDRESS * dbus/Makefile.am (INCLUDES): remove DBUS_SYSTEM_BUS_PATH define from here. * configure.in: define DBUS_SYSTEM_BUS_DEFAULT_ADDRESS here, and AC_DEFINE DBUS_SYSTEM_PATH 2003-08-09 Anders Carlsson <andersca@codefactory.se> * doc/TODO: * doc/busconfig.dtd: Add busconfig DTD. 2003-08-09 Anders Carlsson <andersca@codefactory.se> * doc/dbus-specification.sgml: Add activation reply values. 2003-08-05 Havoc Pennington <hp@redhat.com> * configure.in: 0.12 2003-08-05 Anders Carlsson <andersca@codefactory.se> * glib/dbus-gmain.c: (watch_fd_new), (watch_fd_ref), (watch_fd_unref), (dbus_gsource_check), (dbus_gsource_dispatch), (add_watch), (remove_watch), (create_source): Refcount fds, fixes some reentrancy issues. 2003-07-30 Havoc Pennington <hp@redhat.com> * dbus/dbus-bus.c (init_connections_unlocked): fix default system bus address to be abstract if we have abstract sockets * NEWS: update 2003-07-28 Havoc Pennington <hp@redhat.com> * bus/messagebus.in: fix to avoid processname/servicename confusion, from Michael Kearey https://bugzilla.redhat.com/bugzilla/show_bug.cgi?id=100965 2003-07-23 Havoc Pennington <hp@pobox.com> * dbus/dbus-message.c (dbus_message_iter_get_named): fix from Andy Hanton to remove broken "+1" 2003-07-16 Havoc Pennington <hp@pobox.com> * tools/dbus-launch.c (babysit): close stdout/stderr in the babysitter process, as suggested by Thomas Leonard, so an "eval `dbus-launch --exit-with-session`" will actually return 2003-07-16 Havoc Pennington <hp@pobox.com> * configure.in: print out EXPANDED_* variables in the summary at the end; clean up the code that computes EXPANDED_ variables and get the ones using exec_prefix right. Should make things work when you build without --prefix
Diffstat (limited to 'dbus')
-rw-r--r--dbus/Makefile.am6
-rw-r--r--dbus/dbus-address.c28
-rw-r--r--dbus/dbus-auth.c44
-rw-r--r--dbus/dbus-bus.c159
-rw-r--r--dbus/dbus-bus.h7
-rw-r--r--dbus/dbus-connection-internal.h58
-rw-r--r--dbus/dbus-connection.c1248
-rw-r--r--dbus/dbus-connection.h94
-rw-r--r--dbus/dbus-dataslot.h10
-rw-r--r--dbus/dbus-errors.c144
-rw-r--r--dbus/dbus-errors.h18
-rw-r--r--dbus/dbus-hash.c386
-rw-r--r--dbus/dbus-hash.h131
-rw-r--r--dbus/dbus-internals.c58
-rw-r--r--dbus/dbus-internals.h7
-rw-r--r--dbus/dbus-keyring.c3
-rw-r--r--dbus/dbus-mainloop.c5
-rw-r--r--dbus/dbus-mainloop.h5
-rw-r--r--dbus/dbus-marshal.c249
-rw-r--r--dbus/dbus-marshal.h30
-rw-r--r--dbus/dbus-md5.h9
-rw-r--r--dbus/dbus-message-builder.c193
-rw-r--r--dbus/dbus-message.c1605
-rw-r--r--dbus/dbus-message.h89
-rw-r--r--dbus/dbus-object-tree.c1481
-rw-r--r--dbus/dbus-object-tree.h52
-rw-r--r--dbus/dbus-pending-call.c429
-rw-r--r--dbus/dbus-pending-call.h58
-rw-r--r--dbus/dbus-protocol.h69
-rw-r--r--dbus/dbus-server-debug-pipe.c1
-rw-r--r--dbus/dbus-server-protected.h6
-rw-r--r--dbus/dbus-server-unix.c1
-rw-r--r--dbus/dbus-server.c1
-rw-r--r--dbus/dbus-sha.h5
-rw-r--r--dbus/dbus-spawn.c29
-rw-r--r--dbus/dbus-string.c310
-rw-r--r--dbus/dbus-string.h28
-rw-r--r--dbus/dbus-sysdeps.c5
-rw-r--r--dbus/dbus-sysdeps.h53
-rw-r--r--dbus/dbus-test.c20
-rw-r--r--dbus/dbus-test.h4
-rw-r--r--dbus/dbus-threads.c2
-rw-r--r--dbus/dbus-threads.h46
-rw-r--r--dbus/dbus-timeout.c3
-rw-r--r--dbus/dbus-transport-protected.h10
-rw-r--r--dbus/dbus-transport.c2
-rw-r--r--dbus/dbus-types.h8
-rw-r--r--dbus/dbus-userdb.c13
-rw-r--r--dbus/dbus-watch.c3
-rw-r--r--dbus/dbus.h4
50 files changed, 5793 insertions, 1436 deletions
diff --git a/dbus/Makefile.am b/dbus/Makefile.am
index b5fc26ff..dc49ec81 100644
--- a/dbus/Makefile.am
+++ b/dbus/Makefile.am
@@ -15,7 +15,7 @@ dbusinclude_HEADERS= \
dbus-macros.h \
dbus-memory.h \
dbus-message.h \
- dbus-message-handler.h \
+ dbus-pending-call.h \
dbus-protocol.h \
dbus-server.h \
dbus-threads.h \
@@ -39,8 +39,10 @@ DBUS_LIB_SOURCES= \
dbus-keyring.c \
dbus-keyring.h \
dbus-message.c \
- dbus-message-handler.c \
dbus-message-internal.h \
+ dbus-object-tree.c \
+ dbus-object-tree.h \
+ dbus-pending-call.c \
dbus-resources.c \
dbus-resources.h \
dbus-server.c \
diff --git a/dbus/dbus-address.c b/dbus/dbus-address.c
index bf9dbc3b..97af49fd 100644
--- a/dbus/dbus-address.c
+++ b/dbus/dbus-address.c
@@ -25,22 +25,29 @@
#include "dbus-address.h"
#include "dbus-internals.h"
#include "dbus-list.h"
+#include "dbus-string.h"
/**
- * @defgroup DBusAddress Address parsing
- * @ingroup DBus
- * @brief Parsing addresses of D-BUS servers.
+ * @defgroup DBusAddressInternals Address parsing
+ * @ingroup DBusInternals
+ * @brief Implementation of parsing addresses of D-BUS servers.
*
* @{
*/
+
+/**
+ * Internals of DBusAddressEntry
+ */
struct DBusAddressEntry
{
- DBusString method;
+ DBusString method; /**< The address type (unix, tcp, etc.) */
- DBusList *keys;
- DBusList *values;
+ DBusList *keys; /**< List of keys */
+ DBusList *values; /**< List of values */
};
+/** @} */ /* End of internals */
+
static void
dbus_address_entry_free (DBusAddressEntry *entry)
{
@@ -71,6 +78,13 @@ dbus_address_entry_free (DBusAddressEntry *entry)
dbus_free (entry);
}
+/**
+ * @defgroup DBusAddress Address parsing
+ * @ingroup DBus
+ * @brief Parsing addresses of D-BUS servers.
+ *
+ * @{
+ */
/**
* Frees a #NULL-terminated array of address entries.
@@ -371,7 +385,7 @@ dbus_parse_address (const char *address,
}
-/** @} */
+/** @} */ /* End of public API */
#ifdef DBUS_BUILD_TESTS
#include "dbus-test.h"
diff --git a/dbus/dbus-auth.c b/dbus/dbus-auth.c
index b496dba0..cdfd3bb2 100644
--- a/dbus/dbus-auth.c
+++ b/dbus/dbus-auth.c
@@ -28,8 +28,6 @@
#include "dbus-sha.h"
#include "dbus-userdb.h"
-/* See doc/dbus-sasl-profile.txt */
-
/**
* @defgroup DBusAuth Authentication
* @ingroup DBusInternals
@@ -75,10 +73,13 @@ typedef dbus_bool_t (* DBusProcessAuthCommandFunction) (DBusAuth *auth,
const DBusString *command,
const DBusString *args);
+/**
+ * Handler for a given auth protocol command
+ */
typedef struct
{
- const char *command;
- DBusProcessAuthCommandFunction func;
+ const char *command; /**< Name of the command */
+ DBusProcessAuthCommandFunction func; /**< Function to handle the command */
} DBusAuthCommandHandler;
/**
@@ -113,18 +114,21 @@ typedef dbus_bool_t (* DBusAuthDecodeFunction) (DBusAuth *auth,
*/
typedef void (* DBusAuthShutdownFunction) (DBusAuth *auth);
+/**
+ * Virtual table representing a particular auth mechanism.
+ */
typedef struct
{
- const char *mechanism;
- DBusAuthDataFunction server_data_func;
- DBusAuthEncodeFunction server_encode_func;
- DBusAuthDecodeFunction server_decode_func;
- DBusAuthShutdownFunction server_shutdown_func;
- DBusInitialResponseFunction client_initial_response_func;
- DBusAuthDataFunction client_data_func;
- DBusAuthEncodeFunction client_encode_func;
- DBusAuthDecodeFunction client_decode_func;
- DBusAuthShutdownFunction client_shutdown_func;
+ const char *mechanism; /**< Name of the mechanism */
+ DBusAuthDataFunction server_data_func; /**< Function on server side for DATA */
+ DBusAuthEncodeFunction server_encode_func; /**< Function on server side to encode */
+ DBusAuthDecodeFunction server_decode_func; /**< Function on server side to decode */
+ DBusAuthShutdownFunction server_shutdown_func; /**< Function on server side to shut down */
+ DBusInitialResponseFunction client_initial_response_func; /**< Function on client side to handle initial response */
+ DBusAuthDataFunction client_data_func; /**< Function on client side for DATA */
+ DBusAuthEncodeFunction client_encode_func; /**< Function on client side for encode */
+ DBusAuthDecodeFunction client_decode_func; /**< Function on client side for decode */
+ DBusAuthShutdownFunction client_shutdown_func; /**< Function on client side for shutdown */
} DBusAuthMechanismHandler;
/**
@@ -174,17 +178,23 @@ struct DBusAuth
unsigned int buffer_outstanding : 1; /**< Buffer is "checked out" for reading data into */
};
+/**
+ * "Subclass" of DBusAuth for client side
+ */
typedef struct
{
- DBusAuth base;
+ DBusAuth base; /**< Parent class */
DBusList *mechs_to_try; /**< Mechanisms we got from the server that we're going to try using */
} DBusAuthClient;
+/**
+ * "Subclass" of DBusAuth for server side.
+ */
typedef struct
{
- DBusAuth base;
+ DBusAuth base; /**< Parent class */
int failures; /**< Number of times client has been rejected */
int max_failures; /**< Number of times we reject before disconnect */
@@ -2370,7 +2380,7 @@ process_test_subdir (const DBusString *test_base_dir,
goto failed;
}
- printf ("Testing:\n");
+ printf ("Testing %s:\n", subdir);
next:
while (_dbus_directory_get_next_file (dir, &filename, &error))
diff --git a/dbus/dbus-bus.c b/dbus/dbus-bus.c
index 865811fd..0c9f58e1 100644
--- a/dbus/dbus-bus.c
+++ b/dbus/dbus-bus.c
@@ -25,6 +25,7 @@
#include "dbus-bus.h"
#include "dbus-protocol.h"
#include "dbus-internals.h"
+#include "dbus-message.h"
#include <string.h>
/**
@@ -52,6 +53,10 @@
* Block of message-bus-related data we attach to each
* #DBusConnection used with these convenience functions.
*
+ *
+ * @todo get rid of most of these; they should be done
+ * with DBusGProxy and the Qt equivalent, i.e. the same
+ * way any other interface would be used.
*/
typedef struct
{
@@ -338,6 +343,12 @@ dbus_bus_get (DBusBusType type,
_DBUS_UNLOCK (bus);
return NULL;
}
+
+ /* By default we're bound to the lifecycle of
+ * the message bus.
+ */
+ dbus_connection_set_exit_on_disconnect (connection,
+ TRUE);
if (!dbus_bus_register (connection, error))
{
@@ -403,9 +414,10 @@ dbus_bus_register (DBusConnection *connection,
return TRUE;
}
- message = dbus_message_new (DBUS_MESSAGE_HELLO,
- DBUS_SERVICE_DBUS);
-
+ message = dbus_message_new_method_call (DBUS_SERVICE_ORG_FREEDESKTOP_DBUS,
+ DBUS_PATH_ORG_FREEDESKTOP_DBUS,
+ DBUS_INTERFACE_ORG_FREEDESKTOP_DBUS,
+ "Hello");
if (!message)
{
@@ -521,9 +533,10 @@ dbus_bus_acquire_service (DBusConnection *connection,
_dbus_return_val_if_fail (service_name != NULL, 0);
_dbus_return_val_if_error_is_set (error, 0);
- message = dbus_message_new (DBUS_MESSAGE_ACQUIRE_SERVICE,
- DBUS_SERVICE_DBUS);
-
+ message = dbus_message_new_method_call (DBUS_SERVICE_ORG_FREEDESKTOP_DBUS,
+ DBUS_PATH_ORG_FREEDESKTOP_DBUS,
+ DBUS_INTERFACE_ORG_FREEDESKTOP_DBUS,
+ "AcquireService");
if (message == NULL)
{
@@ -595,8 +608,10 @@ dbus_bus_service_exists (DBusConnection *connection,
_dbus_return_val_if_fail (service_name != NULL, FALSE);
_dbus_return_val_if_error_is_set (error, FALSE);
- message = dbus_message_new (DBUS_MESSAGE_SERVICE_EXISTS,
- DBUS_SERVICE_DBUS);
+ message = dbus_message_new_method_call (DBUS_SERVICE_ORG_FREEDESKTOP_DBUS,
+ DBUS_PATH_ORG_FREEDESKTOP_DBUS,
+ DBUS_INTERFACE_ORG_FREEDESKTOP_DBUS,
+ "ServiceExists");
if (message == NULL)
{
_DBUS_SET_OOM (error);
@@ -657,8 +672,10 @@ dbus_bus_activate_service (DBusConnection *connection,
DBusMessage *msg;
DBusMessage *reply;
- msg = dbus_message_new (DBUS_MESSAGE_ACTIVATE_SERVICE,
- DBUS_SERVICE_DBUS);
+ msg = dbus_message_new_method_call (DBUS_SERVICE_ORG_FREEDESKTOP_DBUS,
+ DBUS_PATH_ORG_FREEDESKTOP_DBUS,
+ DBUS_INTERFACE_ORG_FREEDESKTOP_DBUS,
+ "ActivateService");
if (!dbus_message_append_args (msg, DBUS_TYPE_STRING, service_name,
DBUS_TYPE_UINT32, flags, DBUS_TYPE_INVALID))
@@ -669,7 +686,7 @@ dbus_bus_activate_service (DBusConnection *connection,
}
reply = dbus_connection_send_with_reply_and_block (connection, msg,
- -1, error);
+ -1, error);
dbus_message_unref (msg);
if (reply == NULL)
@@ -698,5 +715,125 @@ dbus_bus_activate_service (DBusConnection *connection,
return TRUE;
}
+static void
+send_no_return_values (DBusConnection *connection,
+ DBusMessage *msg,
+ DBusError *error)
+{
+ if (error)
+ {
+ /* Block to check success codepath */
+ DBusMessage *reply;
+
+ reply = dbus_connection_send_with_reply_and_block (connection, msg,
+ -1, error);
+
+ if (reply == NULL)
+ {
+ _DBUS_ASSERT_ERROR_IS_SET (error);
+ return;
+ }
+
+ if (dbus_set_error_from_message (error, reply))
+ {
+ _DBUS_ASSERT_ERROR_IS_SET (error);
+ dbus_message_unref (reply);
+ return;
+ }
+
+ dbus_message_unref (reply);
+ }
+ else
+ {
+ /* Silently-fail nonblocking codepath */
+ if (!dbus_connection_send (connection, msg, NULL))
+ return;
+ }
+}
+
+/**
+ * Adds a match rule to match messages going through the message bus.
+ * The "rule" argument is the string form of a match rule.
+ *
+ * If you pass #NULL for the error, this function will not
+ * block; the match thus won't be added until you flush the
+ * connection, and if there's an error adding the match
+ * (only possible error is lack of resources in the bus),
+ * you won't find out about it.
+ *
+ * If you pass non-#NULL for the error this function will
+ * block until it gets a reply.
+ *
+ * Normal API conventions would have the function return
+ * a boolean value indicating whether the error was set,
+ * but that would require blocking always to determine
+ * the return value.
+ *
+ * @param connection connection to the message bus
+ * @param rule textual form of match rule
+ * @param error location to store any errors
+ */
+void
+dbus_bus_add_match (DBusConnection *connection,
+ const char *rule,
+ DBusError *error)
+{
+ DBusMessage *msg;
+
+ msg = dbus_message_new_method_call (DBUS_SERVICE_ORG_FREEDESKTOP_DBUS,
+ DBUS_PATH_ORG_FREEDESKTOP_DBUS,
+ DBUS_INTERFACE_ORG_FREEDESKTOP_DBUS,
+ "AddMatch");
+
+ if (!dbus_message_append_args (msg, DBUS_TYPE_STRING, rule,
+ DBUS_TYPE_INVALID))
+ {
+ dbus_message_unref (msg);
+ _DBUS_SET_OOM (error);
+ return;
+ }
+
+ send_no_return_values (connection, msg, error);
+
+ dbus_message_unref (msg);
+}
+
+/**
+ * Removes a previously-added match rule "by value" (the most
+ * recently-added identical rule gets removed). The "rule" argument
+ * is the string form of a match rule.
+ *
+ * If you pass #NULL for the error, this function will not
+ * block; otherwise it will. See detailed explanation in
+ * docs for dbus_bus_add_match().
+ *
+ * @param connection connection to the message bus
+ * @param rule textual form of match rule
+ * @param error location to store any errors
+ */
+void
+dbus_bus_remove_match (DBusConnection *connection,
+ const char *rule,
+ DBusError *error)
+{
+ DBusMessage *msg;
+
+ msg = dbus_message_new_method_call (DBUS_SERVICE_ORG_FREEDESKTOP_DBUS,
+ DBUS_PATH_ORG_FREEDESKTOP_DBUS,
+ DBUS_INTERFACE_ORG_FREEDESKTOP_DBUS,
+ "RemoveMatch");
+
+ if (!dbus_message_append_args (msg, DBUS_TYPE_STRING, rule,
+ DBUS_TYPE_INVALID))
+ {
+ dbus_message_unref (msg);
+ _DBUS_SET_OOM (error);
+ return;
+ }
+
+ send_no_return_values (connection, msg, error);
+
+ dbus_message_unref (msg);
+}
/** @} */
diff --git a/dbus/dbus-bus.h b/dbus/dbus-bus.h
index a62a746b..072b0c8e 100644
--- a/dbus/dbus-bus.h
+++ b/dbus/dbus-bus.h
@@ -59,6 +59,13 @@ dbus_bool_t dbus_bus_activate_service (DBusConnection *connection,
dbus_uint32_t *reply,
DBusError *error);
+void dbus_bus_add_match (DBusConnection *connection,
+ const char *rule,
+ DBusError *error);
+void dbus_bus_remove_match (DBusConnection *connection,
+ const char *rule,
+ DBusError *error);
+
DBUS_END_DECLS;
#endif /* DBUS_BUS_H */
diff --git a/dbus/dbus-connection-internal.h b/dbus/dbus-connection-internal.h
index 5ddc0e0a..b19ab636 100644
--- a/dbus/dbus-connection-internal.h
+++ b/dbus/dbus-connection-internal.h
@@ -29,6 +29,8 @@
#include <dbus/dbus-transport.h>
#include <dbus/dbus-resources.h>
#include <dbus/dbus-list.h>
+#include <dbus/dbus-timeout.h>
+#include <dbus/dbus-dataslot.h>
DBUS_BEGIN_DECLS;
@@ -39,9 +41,13 @@ typedef enum
DBUS_ITERATION_BLOCK = 1 << 2 /**< Block if nothing to do. */
} DBusIterationFlags;
+/** default timeout value when waiting for a message reply */
+#define _DBUS_DEFAULT_TIMEOUT_VALUE (15 * 1000)
+
void _dbus_connection_lock (DBusConnection *connection);
void _dbus_connection_unlock (DBusConnection *connection);
void _dbus_connection_ref_unlocked (DBusConnection *connection);
+void _dbus_connection_unref_unlocked (DBusConnection *connection);
dbus_bool_t _dbus_connection_queue_received_message (DBusConnection *connection,
DBusMessage *message);
void _dbus_connection_queue_received_message_link (DBusConnection *connection,
@@ -72,16 +78,52 @@ void _dbus_connection_do_iteration (DBusConnection
unsigned int flags,
int timeout_milliseconds);
void _dbus_connection_notify_disconnected (DBusConnection *connection);
-void _dbus_connection_handler_destroyed_locked (DBusConnection *connection,
- DBusMessageHandler *handler);
-dbus_bool_t _dbus_message_handler_add_connection (DBusMessageHandler *handler,
- DBusConnection *connection);
-void _dbus_message_handler_remove_connection (DBusMessageHandler *handler,
- DBusConnection *connection);
-DBusHandlerResult _dbus_message_handler_handle_message (DBusMessageHandler *handler,
- DBusConnection *connection,
+
+DBusPendingCall* _dbus_pending_call_new (DBusConnection *connection,
+ int timeout_milliseconds,
+ DBusTimeoutHandler timeout_handler);
+void _dbus_pending_call_notify (DBusPendingCall *pending);
+void _dbus_connection_remove_pending_call (DBusConnection *connection,
+ DBusPendingCall *pending);
+DBusMessage* _dbus_connection_block_for_reply (DBusConnection *connection,
+ dbus_uint32_t client_serial,
+ int timeout_milliseconds);
+void _dbus_pending_call_complete_and_unlock (DBusPendingCall *pending,
DBusMessage *message);
+
+/**
+ * @addtogroup DBusPendingCallInternals DBusPendingCall implementation details
+ * @{
+ */
+/**
+ * @brief Internals of DBusPendingCall
+ *
+ * Object representing a reply message that we're waiting for.
+ */
+struct DBusPendingCall
+{
+ DBusAtomic refcount; /**< reference count */
+
+ DBusDataSlotList slot_list; /**< Data stored by allocated integer ID */
+
+ DBusPendingCallNotifyFunction function; /**< Notifier when reply arrives. */
+
+ DBusConnection *connection; /**< Connections we're associated with */
+ DBusMessage *reply; /**< Reply (after we've received it) */
+ DBusTimeout *timeout; /**< Timeout */
+
+ DBusList *timeout_link; /**< Preallocated timeout response */
+
+ dbus_uint32_t reply_serial; /**< Expected serial of reply */
+
+ unsigned int completed : 1; /**< TRUE if completed */
+ unsigned int timeout_added : 1; /**< Have added the timeout */
+};
+
+/** @} End of DBusPendingCallInternals */
+
+
DBUS_END_DECLS;
#endif /* DBUS_CONNECTION_INTERNAL_H */
diff --git a/dbus/dbus-connection.c b/dbus/dbus-connection.c
index 01b2a7bf..ed7d57d0 100644
--- a/dbus/dbus-connection.c
+++ b/dbus/dbus-connection.c
@@ -31,10 +31,12 @@
#include "dbus-list.h"
#include "dbus-hash.h"
#include "dbus-message-internal.h"
-#include "dbus-message-handler.h"
#include "dbus-threads.h"
#include "dbus-protocol.h"
#include "dbus-dataslot.h"
+#include "dbus-string.h"
+#include "dbus-pending-call.h"
+#include "dbus-object-tree.h"
#if 0
#define CONNECTION_LOCK(connection) do { \
@@ -77,7 +79,7 @@
* you to set a function to be used to monitor the dispatch status.
*
* If you're using GLib or Qt add-on libraries for D-BUS, there are
- * special convenience functions in those libraries that hide
+ * special convenience APIs in those libraries that hide
* all the details of dispatch and watch/timeout monitoring.
* For example, dbus_connection_setup_with_g_main().
*
@@ -122,8 +124,32 @@
* @{
*/
-/** default timeout value when waiting for a message reply */
-#define DEFAULT_TIMEOUT_VALUE (15 * 1000)
+/**
+ * Internal struct representing a message filter function
+ */
+typedef struct DBusMessageFilter DBusMessageFilter;
+
+/**
+ * Internal struct representing a message filter function
+ */
+struct DBusMessageFilter
+{
+ DBusAtomic refcount; /**< Reference count */
+ DBusHandleMessageFunction function; /**< Function to call to filter */
+ void *user_data; /**< User data for the function */
+ DBusFreeFunction free_user_data_function; /**< Function to free the user data */
+};
+
+
+/**
+ * Internals of DBusPreallocatedSend
+ */
+struct DBusPreallocatedSend
+{
+ DBusConnection *connection; /**< Connection we'd send the message to */
+ DBusList *queue_link; /**< Preallocated link in the queue */
+ DBusList *counter_link; /**< Preallocated link in the resource counter */
+};
static dbus_bool_t _dbus_modify_sigpipe = TRUE;
@@ -157,12 +183,11 @@ struct DBusConnection
DBusWatchList *watches; /**< Stores active watches. */
DBusTimeoutList *timeouts; /**< Stores active timeouts. */
- DBusHashTable *handler_table; /**< Table of registered DBusMessageHandler */
DBusList *filter_list; /**< List of filters. */
DBusDataSlotList slot_list; /**< Data stored by allocated integer ID */
- DBusHashTable *pending_replies; /**< Hash of message serials and their message handlers. */
+ DBusHashTable *pending_replies; /**< Hash of message serials to #DBusPendingCall. */
dbus_uint32_t client_serial; /**< Client serial. Increments each time a message is sent */
DBusList *disconnect_message_link; /**< Preallocated list node for queueing the disconnection message */
@@ -180,30 +205,38 @@ struct DBusConnection
DBusList *link_cache; /**< A cache of linked list links to prevent contention
* for the global linked list mempool lock
*/
-};
+ DBusObjectTree *objects; /**< Object path handlers registered with this connection */
-typedef struct
-{
- DBusConnection *connection;
- DBusMessageHandler *handler;
- DBusTimeout *timeout;
- int serial;
-
- DBusList *timeout_link; /* Preallocated timeout response */
-
- dbus_bool_t timeout_added;
- dbus_bool_t connection_added;
-} ReplyHandlerData;
-
-static void reply_handler_data_free (ReplyHandlerData *data);
+ unsigned int exit_on_disconnect : 1; /**< If #TRUE, exit after handling disconnect signal */
+};
static void _dbus_connection_remove_timeout_locked (DBusConnection *connection,
DBusTimeout *timeout);
static DBusDispatchStatus _dbus_connection_get_dispatch_status_unlocked (DBusConnection *connection);
static void _dbus_connection_update_dispatch_status_and_unlock (DBusConnection *connection,
DBusDispatchStatus new_status);
+static void _dbus_connection_last_unref (DBusConnection *connection);
+static void
+_dbus_message_filter_ref (DBusMessageFilter *filter)
+{
+ _dbus_assert (filter->refcount.value > 0);
+ _dbus_atomic_inc (&filter->refcount);
+}
+static void
+_dbus_message_filter_unref (DBusMessageFilter *filter)
+{
+ _dbus_assert (filter->refcount.value > 0);
+
+ if (_dbus_atomic_dec (&filter->refcount) == 1)
+ {
+ if (filter->free_user_data_function)
+ (* filter->free_user_data_function) (filter->user_data);
+
+ dbus_free (filter);
+ }
+}
/**
* Acquires the connection lock.
@@ -281,7 +314,7 @@ void
_dbus_connection_queue_received_message_link (DBusConnection *connection,
DBusList *link)
{
- ReplyHandlerData *reply_handler_data;
+ DBusPendingCall *pending;
dbus_int32_t reply_serial;
DBusMessage *message;
@@ -295,14 +328,15 @@ _dbus_connection_queue_received_message_link (DBusConnection *connection,
reply_serial = dbus_message_get_reply_serial (message);
if (reply_serial != -1)
{
- reply_handler_data = _dbus_hash_table_lookup_int (connection->pending_replies,
- reply_serial);
- if (reply_handler_data != NULL)
+ pending = _dbus_hash_table_lookup_int (connection->pending_replies,
+ reply_serial);
+ if (pending != NULL)
{
- if (reply_handler_data->timeout_added)
+ if (pending->timeout_added)
_dbus_connection_remove_timeout_locked (connection,
- reply_handler_data->timeout);
- reply_handler_data->timeout_added = FALSE;
+ pending->timeout);
+
+ pending->timeout_added = FALSE;
}
}
@@ -310,9 +344,11 @@ _dbus_connection_queue_received_message_link (DBusConnection *connection,
_dbus_connection_wakeup_mainloop (connection);
- _dbus_assert (dbus_message_get_name (message) != NULL);
_dbus_verbose ("Message %p (%s) added to incoming queue %p, %d incoming\n",
- message, dbus_message_get_name (message),
+ message,
+ dbus_message_get_interface (message) ?
+ dbus_message_get_interface (message) :
+ "no interface",
connection,
connection->n_incoming);
}
@@ -395,7 +431,10 @@ _dbus_connection_message_sent (DBusConnection *connection,
connection->n_outgoing -= 1;
_dbus_verbose ("Message %p (%s) removed from outgoing queue %p, %d left to send\n",
- message, dbus_message_get_name (message),
+ message,
+ dbus_message_get_interface (message) ?
+ dbus_message_get_interface (message) :
+ "no interface",
connection, connection->n_outgoing);
/* Save this link in the link cache also */
@@ -553,6 +592,118 @@ _dbus_connection_notify_disconnected (DBusConnection *connection)
}
}
+static dbus_bool_t
+_dbus_connection_attach_pending_call_unlocked (DBusConnection *connection,
+ DBusPendingCall *pending)
+{
+ _dbus_assert (pending->reply_serial != 0);
+
+ if (!_dbus_connection_add_timeout (connection, pending->timeout))
+ return FALSE;
+
+ if (!_dbus_hash_table_insert_int (connection->pending_replies,
+ pending->reply_serial,
+ pending))
+ {
+ _dbus_connection_remove_timeout (connection, pending->timeout);
+ return FALSE;
+ }
+
+ pending->timeout_added = TRUE;
+ pending->connection = connection;
+
+ dbus_pending_call_ref (pending);
+
+ return TRUE;
+}
+
+static void
+free_pending_call_on_hash_removal (void *data)
+{
+ DBusPendingCall *pending;
+
+ if (data == NULL)
+ return;
+
+ pending = data;
+
+ if (pending->connection)
+ {
+ if (pending->timeout_added)
+ {
+ _dbus_connection_remove_timeout (pending->connection,
+ pending->timeout);
+ pending->timeout_added = FALSE;
+ }
+
+ pending->connection = NULL;
+
+ dbus_pending_call_unref (pending);
+ }
+}
+
+static void
+_dbus_connection_detach_pending_call_and_unlock (DBusConnection *connection,
+ DBusPendingCall *pending)
+{
+ /* The idea here is to avoid finalizing the pending call
+ * with the lock held, since there's a destroy notifier
+ * in pending call that goes out to application code.
+ */
+ dbus_pending_call_ref (pending);
+ _dbus_hash_table_remove_int (connection->pending_replies,
+ pending->reply_serial);
+ CONNECTION_UNLOCK (connection);
+ dbus_pending_call_unref (pending);
+}
+
+/**
+ * Removes a pending call from the connection, such that
+ * the pending reply will be ignored. May drop the last
+ * reference to the pending call.
+ *
+ * @param connection the connection
+ * @param pending the pending call
+ */
+void
+_dbus_connection_remove_pending_call (DBusConnection *connection,
+ DBusPendingCall *pending)
+{
+ CONNECTION_LOCK (connection);
+ _dbus_connection_detach_pending_call_and_unlock (connection, pending);
+}
+
+/**
+ * Completes a pending call with the given message,
+ * or if the message is #NULL, by timing out the pending call.
+ *
+ * @param pending the pending call
+ * @param message the message to complete the call with, or #NULL
+ * to time out the call
+ */
+void
+_dbus_pending_call_complete_and_unlock (DBusPendingCall *pending,
+ DBusMessage *message)
+{
+ if (message == NULL)
+ {
+ message = pending->timeout_link->data;
+ _dbus_list_clear (&pending->timeout_link);
+ }
+
+ _dbus_verbose (" handing message %p to pending call\n", message);
+
+ _dbus_assert (pending->reply == NULL);
+ pending->reply = message;
+ dbus_message_ref (pending->reply);
+
+ dbus_pending_call_ref (pending); /* in case there's no app with a ref held */
+ _dbus_connection_detach_pending_call_and_unlock (pending->connection, pending);
+
+ /* Must be called unlocked since it invokes app callback */
+ _dbus_pending_call_notify (pending);
+ dbus_pending_call_unref (pending);
+}
/**
* Acquire the transporter I/O path. This must be done before
@@ -664,7 +815,7 @@ _dbus_connection_new_for_transport (DBusTransport *transport)
DBusConnection *connection;
DBusWatchList *watch_list;
DBusTimeoutList *timeout_list;
- DBusHashTable *handler_table, *pending_replies;
+ DBusHashTable *pending_replies;
DBusMutex *mutex;
DBusCondVar *message_returned_cond;
DBusCondVar *dispatch_cond;
@@ -672,10 +823,10 @@ _dbus_connection_new_for_transport (DBusTransport *transport)
DBusList *disconnect_link;
DBusMessage *disconnect_message;
DBusCounter *outgoing_counter;
+ DBusObjectTree *objects;
watch_list = NULL;
connection = NULL;
- handler_table = NULL;
pending_replies = NULL;
timeout_list = NULL;
mutex = NULL;
@@ -685,6 +836,7 @@ _dbus_connection_new_for_transport (DBusTransport *transport)
disconnect_link = NULL;
disconnect_message = NULL;
outgoing_counter = NULL;
+ objects = NULL;
watch_list = _dbus_watch_list_new ();
if (watch_list == NULL)
@@ -692,17 +844,12 @@ _dbus_connection_new_for_transport (DBusTransport *transport)
timeout_list = _dbus_timeout_list_new ();
if (timeout_list == NULL)
- goto error;
-
- handler_table =
- _dbus_hash_table_new (DBUS_HASH_STRING,
- dbus_free, NULL);
- if (handler_table == NULL)
- goto error;
+ goto error;
pending_replies =
_dbus_hash_table_new (DBUS_HASH_INT,
- NULL, (DBusFreeFunction)reply_handler_data_free);
+ NULL,
+ (DBusFreeFunction)free_pending_call_on_hash_removal);
if (pending_replies == NULL)
goto error;
@@ -726,7 +873,10 @@ _dbus_connection_new_for_transport (DBusTransport *transport)
if (io_path_cond == NULL)
goto error;
- disconnect_message = dbus_message_new (DBUS_MESSAGE_LOCAL_DISCONNECT, NULL);
+ disconnect_message = dbus_message_new_signal (DBUS_PATH_ORG_FREEDESKTOP_LOCAL,
+ DBUS_INTERFACE_ORG_FREEDESKTOP_LOCAL,
+ "Disconnected");
+
if (disconnect_message == NULL)
goto error;
@@ -737,6 +887,10 @@ _dbus_connection_new_for_transport (DBusTransport *transport)
outgoing_counter = _dbus_counter_new ();
if (outgoing_counter == NULL)
goto error;
+
+ objects = _dbus_object_tree_new (connection);
+ if (objects == NULL)
+ goto error;
if (_dbus_modify_sigpipe)
_dbus_disable_sigpipe ();
@@ -749,11 +903,12 @@ _dbus_connection_new_for_transport (DBusTransport *transport)
connection->transport = transport;
connection->watches = watch_list;
connection->timeouts = timeout_list;
- connection->handler_table = handler_table;
connection->pending_replies = pending_replies;
connection->outgoing_counter = outgoing_counter;
connection->filter_list = NULL;
connection->last_dispatch_status = DBUS_DISPATCH_COMPLETE; /* so we're notified first time there's data */
+ connection->objects = objects;
+ connection->exit_on_disconnect = FALSE;
_dbus_data_slot_list_init (&connection->slot_list);
@@ -790,9 +945,6 @@ _dbus_connection_new_for_transport (DBusTransport *transport)
if (connection != NULL)
dbus_free (connection);
- if (handler_table)
- _dbus_hash_table_unref (handler_table);
-
if (pending_replies)
_dbus_hash_table_unref (pending_replies);
@@ -804,6 +956,9 @@ _dbus_connection_new_for_transport (DBusTransport *transport)
if (outgoing_counter)
_dbus_counter_unref (outgoing_counter);
+
+ if (objects)
+ _dbus_object_tree_unref (objects);
return NULL;
}
@@ -825,6 +980,39 @@ _dbus_connection_ref_unlocked (DBusConnection *connection)
#endif
}
+/**
+ * Decrements the reference count of a DBusConnection.
+ * Requires that the caller already holds the connection lock.
+ *
+ * @param connection the connection.
+ */
+void
+_dbus_connection_unref_unlocked (DBusConnection *connection)
+{
+ dbus_bool_t last_unref;
+
+ _dbus_return_if_fail (connection != NULL);
+
+ /* The connection lock is better than the global
+ * lock in the atomic increment fallback
+ */
+
+#ifdef DBUS_HAVE_ATOMIC_INT
+ last_unref = (_dbus_atomic_dec (&connection->refcount) == 1);
+#else
+ _dbus_assert (connection->refcount.value > 0);
+
+ connection->refcount.value -= 1;
+ last_unref = (connection->refcount.value == 0);
+#if 0
+ printf ("unref_unlocked() connection %p count = %d\n", connection, connection->refcount.value);
+#endif
+#endif
+
+ if (last_unref)
+ _dbus_connection_last_unref (connection);
+}
+
static dbus_uint32_t
_dbus_connection_get_next_client_serial (DBusConnection *connection)
{
@@ -839,50 +1027,6 @@ _dbus_connection_get_next_client_serial (DBusConnection *connection)
}
/**
- * Used to notify a connection when a DBusMessageHandler is
- * destroyed, so the connection can drop any reference
- * to the handler. This is a private function, but still
- * takes the connection lock. Don't call it with the lock held.
- *
- * @todo needs to check in pending_replies too.
- *
- * @param connection the connection
- * @param handler the handler
- */
-void
-_dbus_connection_handler_destroyed_locked (DBusConnection *connection,
- DBusMessageHandler *handler)
-{
- DBusHashIter iter;
- DBusList *link;
-
- CONNECTION_LOCK (connection);
-
- _dbus_hash_iter_init (connection->handler_table, &iter);
- while (_dbus_hash_iter_next (&iter))
- {
- DBusMessageHandler *h = _dbus_hash_iter_get_value (&iter);
-
- if (h == handler)
- _dbus_hash_iter_remove_entry (&iter);
- }
-
- link = _dbus_list_get_first_link (&connection->filter_list);
- while (link != NULL)
- {
- DBusMessageHandler *h = link->data;
- DBusList *next = _dbus_list_get_next_link (&connection->filter_list, link);
-
- if (h == handler)
- _dbus_list_remove_link (&connection->filter_list,
- link);
-
- link = next;
- }
- CONNECTION_UNLOCK (connection);
-}
-
-/**
* A callback for use with dbus_watch_new() to create a DBusWatch.
*
* @todo This is basically a hack - we could delete _dbus_transport_handle_watch()
@@ -1019,7 +1163,6 @@ free_outgoing_message (void *element,
static void
_dbus_connection_last_unref (DBusConnection *connection)
{
- DBusHashIter iter;
DBusList *link;
_dbus_verbose ("Finalizing connection %p\n", connection);
@@ -1032,6 +1175,8 @@ _dbus_connection_last_unref (DBusConnection *connection)
_dbus_assert (!_dbus_transport_get_is_connected (connection->transport));
/* ---- We're going to call various application callbacks here, hope it doesn't break anything... */
+ _dbus_object_tree_free_all_unlocked (connection->objects);
+
dbus_connection_set_dispatch_status_function (connection, NULL, NULL, NULL);
dbus_connection_set_wakeup_main_function (connection, NULL, NULL, NULL);
dbus_connection_set_unix_user_function (connection, NULL, NULL, NULL);
@@ -1043,29 +1188,24 @@ _dbus_connection_last_unref (DBusConnection *connection)
connection->timeouts = NULL;
_dbus_data_slot_list_free (&connection->slot_list);
- /* ---- Done with stuff that invokes application callbacks */
-
- _dbus_hash_iter_init (connection->handler_table, &iter);
- while (_dbus_hash_iter_next (&iter))
- {
- DBusMessageHandler *h = _dbus_hash_iter_get_value (&iter);
-
- _dbus_message_handler_remove_connection (h, connection);
- }
link = _dbus_list_get_first_link (&connection->filter_list);
while (link != NULL)
{
- DBusMessageHandler *h = link->data;
+ DBusMessageFilter *filter = link->data;
DBusList *next = _dbus_list_get_next_link (&connection->filter_list, link);
-
- _dbus_message_handler_remove_connection (h, connection);
+
+ filter->function = NULL;
+ _dbus_message_filter_unref (filter); /* calls app callback */
+ link->data = NULL;
link = next;
}
+ _dbus_list_clear (&connection->filter_list);
+
+ /* ---- Done with stuff that invokes application callbacks */
- _dbus_hash_table_unref (connection->handler_table);
- connection->handler_table = NULL;
+ _dbus_object_tree_unref (connection->objects);
_dbus_hash_table_unref (connection->pending_replies);
connection->pending_replies = NULL;
@@ -1219,12 +1359,29 @@ dbus_connection_get_is_authenticated (DBusConnection *connection)
return res;
}
-struct DBusPreallocatedSend
+/**
+ * Set whether _exit() should be called when the connection receives a
+ * disconnect signal. The call to _exit() comes after any handlers for
+ * the disconnect signal run; handlers can cancel the exit by calling
+ * this function.
+ *
+ * By default, exit_on_disconnect is #FALSE; but for message bus
+ * connections returned from dbus_bus_get() it will be toggled on
+ * by default.
+ *
+ * @param connection the connection
+ * @param exit_on_disconnect #TRUE if _exit() should be called after a disconnect signal
+ */
+void
+dbus_connection_set_exit_on_disconnect (DBusConnection *connection,
+ dbus_bool_t exit_on_disconnect)
{
- DBusConnection *connection;
- DBusList *queue_link;
- DBusList *counter_link;
-};
+ _dbus_return_if_fail (connection != NULL);
+
+ CONNECTION_LOCK (connection);
+ connection->exit_on_disconnect = exit_on_disconnect != FALSE;
+ CONNECTION_UNLOCK (connection);
+}
static DBusPreallocatedSend*
_dbus_connection_preallocate_send_unlocked (DBusConnection *connection)
@@ -1350,7 +1507,9 @@ _dbus_connection_send_preallocated_unlocked (DBusConnection *connection,
_dbus_verbose ("Message %p (%s) added to outgoing queue %p, %d pending to send\n",
message,
- dbus_message_get_name (message),
+ dbus_message_get_interface (message) ?
+ dbus_message_get_interface (message) :
+ "no interface",
connection,
connection->n_outgoing);
@@ -1398,7 +1557,12 @@ dbus_connection_send_preallocated (DBusConnection *connection,
_dbus_return_if_fail (preallocated != NULL);
_dbus_return_if_fail (message != NULL);
_dbus_return_if_fail (preallocated->connection == connection);
- _dbus_return_if_fail (dbus_message_get_name (message) != NULL);
+ _dbus_return_if_fail (dbus_message_get_type (message) != DBUS_MESSAGE_TYPE_METHOD_CALL ||
+ (dbus_message_get_interface (message) != NULL &&
+ dbus_message_get_member (message) != NULL));
+ _dbus_return_if_fail (dbus_message_get_type (message) != DBUS_MESSAGE_TYPE_SIGNAL ||
+ (dbus_message_get_interface (message) != NULL &&
+ dbus_message_get_member (message) != NULL));
CONNECTION_LOCK (connection);
_dbus_connection_send_preallocated_unlocked (connection,
@@ -1407,6 +1571,28 @@ dbus_connection_send_preallocated (DBusConnection *connection,
CONNECTION_UNLOCK (connection);
}
+static dbus_bool_t
+_dbus_connection_send_unlocked (DBusConnection *connection,
+ DBusMessage *message,
+ dbus_uint32_t *client_serial)
+{
+ DBusPreallocatedSend *preallocated;
+
+ _dbus_assert (connection != NULL);
+ _dbus_assert (message != NULL);
+
+ preallocated = _dbus_connection_preallocate_send_unlocked (connection);
+ if (preallocated == NULL)
+ return FALSE;
+
+
+ _dbus_connection_send_preallocated_unlocked (connection,
+ preallocated,
+ message,
+ client_serial);
+ return TRUE;
+}
+
/**
* Adds a message to the outgoing message queue. Does not block to
* write the message to the network; that happens asynchronously. To
@@ -1430,50 +1616,41 @@ dbus_connection_send (DBusConnection *connection,
DBusMessage *message,
dbus_uint32_t *client_serial)
{
- DBusPreallocatedSend *preallocated;
-
_dbus_return_val_if_fail (connection != NULL, FALSE);
_dbus_return_val_if_fail (message != NULL, FALSE);
CONNECTION_LOCK (connection);
-
- preallocated = _dbus_connection_preallocate_send_unlocked (connection);
- if (preallocated == NULL)
+
+ if (!_dbus_connection_send_unlocked (connection, message, client_serial))
{
CONNECTION_UNLOCK (connection);
return FALSE;
}
- else
- {
- _dbus_connection_send_preallocated_unlocked (connection,
- preallocated,
- message,
- client_serial);
- CONNECTION_UNLOCK (connection);
- return TRUE;
- }
+
+ CONNECTION_UNLOCK (connection);
+ return TRUE;
}
static dbus_bool_t
reply_handler_timeout (void *data)
{
DBusConnection *connection;
- ReplyHandlerData *reply_handler_data = data;
DBusDispatchStatus status;
+ DBusPendingCall *pending = data;
- connection = reply_handler_data->connection;
+ connection = pending->connection;
CONNECTION_LOCK (connection);
- if (reply_handler_data->timeout_link)
+ if (pending->timeout_link)
{
_dbus_connection_queue_synthesized_message_link (connection,
- reply_handler_data->timeout_link);
- reply_handler_data->timeout_link = NULL;
+ pending->timeout_link);
+ pending->timeout_link = NULL;
}
_dbus_connection_remove_timeout (connection,
- reply_handler_data->timeout);
- reply_handler_data->timeout_added = FALSE;
+ pending->timeout);
+ pending->timeout_added = FALSE;
status = _dbus_connection_get_dispatch_status_unlocked (connection);
@@ -1483,52 +1660,29 @@ reply_handler_timeout (void *data)
return TRUE;
}
-static void
-reply_handler_data_free (ReplyHandlerData *data)
-{
- if (!data)
- return;
-
- if (data->timeout_added)
- _dbus_connection_remove_timeout_locked (data->connection,
- data->timeout);
-
- if (data->connection_added)
- _dbus_message_handler_remove_connection (data->handler,
- data->connection);
-
- if (data->timeout_link)
- {
- dbus_message_unref ((DBusMessage *)data->timeout_link->data);
- _dbus_list_free_link (data->timeout_link);
- }
-
- dbus_message_handler_unref (data->handler);
-
- dbus_free (data);
-}
-
/**
* Queues a message to send, as with dbus_connection_send_message(),
- * but also sets up a DBusMessageHandler to receive a reply to the
+ * but also returns a #DBusPendingCall used to receive a reply to the
* message. If no reply is received in the given timeout_milliseconds,
- * expires the pending reply and sends the DBusMessageHandler a
- * synthetic error reply (generated in-process, not by the remote
- * application) indicating that a timeout occurred.
- *
- * Reply handlers see their replies after message filters see them,
- * but before message handlers added with
- * dbus_connection_register_handler() see them, regardless of the
- * reply message's name. Reply handlers are only handed a single
- * message as a reply, after one reply has been seen the handler is
- * removed. If a filter filters out the reply before the handler sees
- * it, the reply is immediately timed out and a timeout error reply is
+ * this function expires the pending reply and generates a synthetic
+ * error reply (generated in-process, not by the remote application)
+ * indicating that a timeout occurred.
+ *
+ * A #DBusPendingCall will see a reply message after any filters, but
+ * before any object instances or other handlers. A #DBusPendingCall
+ * will always see exactly one reply message, unless it's cancelled
+ * with dbus_pending_call_cancel().
+ *
+ * If a filter filters out the reply before the handler sees it, the
+ * reply is immediately timed out and a timeout error reply is
* generated. If a filter removes the timeout error reply then the
- * reply handler will never be called. Filters should not do this.
+ * #DBusPendingCall will get confused. Filtering the timeout error
+ * is thus considered a bug and will print a warning.
*
- * If #NULL is passed for the reply_handler, the timeout reply will
- * still be generated and placed into the message queue, but no
- * specific message handler will receive the reply.
+ * If #NULL is passed for the pending_return, the #DBusPendingCall
+ * will still be generated internally, and used to track
+ * the message reply timeout. This means a timeout error will
+ * occur if no reply arrives, unlike with dbus_connection_send().
*
* If -1 is passed for the timeout, a sane default timeout is used. -1
* is typically the best value for the timeout for this reason, unless
@@ -1538,7 +1692,7 @@ reply_handler_data_free (ReplyHandlerData *data)
*
* @param connection the connection
* @param message the message to send
- * @param reply_handler message handler expecting the reply, or #NULL
+ * @param pending_return return location for a #DBusPendingCall object, or #NULL
* @param timeout_milliseconds timeout in milliseconds or -1 for default
* @returns #TRUE if the message is successfully queued, #FALSE if no memory.
*
@@ -1546,63 +1700,30 @@ reply_handler_data_free (ReplyHandlerData *data)
dbus_bool_t
dbus_connection_send_with_reply (DBusConnection *connection,
DBusMessage *message,
- DBusMessageHandler *reply_handler,
+ DBusPendingCall **pending_return,
int timeout_milliseconds)
{
- DBusTimeout *timeout;
- ReplyHandlerData *data;
+ DBusPendingCall *pending;
DBusMessage *reply;
DBusList *reply_link;
dbus_int32_t serial = -1;
_dbus_return_val_if_fail (connection != NULL, FALSE);
_dbus_return_val_if_fail (message != NULL, FALSE);
- _dbus_return_val_if_fail (reply_handler != NULL, FALSE);
_dbus_return_val_if_fail (timeout_milliseconds >= 0 || timeout_milliseconds == -1, FALSE);
-
- if (timeout_milliseconds == -1)
- timeout_milliseconds = DEFAULT_TIMEOUT_VALUE;
-
- data = dbus_new0 (ReplyHandlerData, 1);
- if (!data)
- return FALSE;
+ if (pending_return)
+ *pending_return = NULL;
- timeout = _dbus_timeout_new (timeout_milliseconds, reply_handler_timeout,
- data, NULL);
+ pending = _dbus_pending_call_new (connection,
+ timeout_milliseconds,
+ reply_handler_timeout);
- if (!timeout)
- {
- reply_handler_data_free (data);
- return FALSE;
- }
+ if (pending == NULL)
+ return FALSE;
CONNECTION_LOCK (connection);
- /* Add timeout */
- if (!_dbus_connection_add_timeout (connection, timeout))
- {
- reply_handler_data_free (data);
- _dbus_timeout_unref (timeout);
- CONNECTION_UNLOCK (connection);
- return FALSE;
- }
-
- /* The connection now owns the reference to the timeout. */
- _dbus_timeout_unref (timeout);
-
- data->timeout_added = TRUE;
- data->timeout = timeout;
- data->connection = connection;
-
- if (!_dbus_message_handler_add_connection (reply_handler, connection))
- {
- CONNECTION_UNLOCK (connection);
- reply_handler_data_free (data);
- return FALSE;
- }
- data->connection_added = TRUE;
-
/* Assign a serial to the message */
if (dbus_message_get_serial (message) == 0)
{
@@ -1610,17 +1731,14 @@ dbus_connection_send_with_reply (DBusConnection *connection,
_dbus_message_set_serial (message, serial);
}
- data->handler = reply_handler;
- data->serial = serial;
-
- dbus_message_handler_ref (reply_handler);
+ pending->reply_serial = serial;
- reply = dbus_message_new_error_reply (message, DBUS_ERROR_NO_REPLY,
- "No reply within specified time");
+ reply = dbus_message_new_error (message, DBUS_ERROR_NO_REPLY,
+ "No reply within specified time");
if (!reply)
{
CONNECTION_UNLOCK (connection);
- reply_handler_data_free (data);
+ dbus_pending_call_unref (pending);
return FALSE;
}
@@ -1629,33 +1747,42 @@ dbus_connection_send_with_reply (DBusConnection *connection,
{
CONNECTION_UNLOCK (connection);
dbus_message_unref (reply);
- reply_handler_data_free (data);
+ dbus_pending_call_unref (pending);
return FALSE;
}
- data->timeout_link = reply_link;
-
- /* Insert the serial in the pending replies hash. */
- if (!_dbus_hash_table_insert_int (connection->pending_replies, serial, data))
+ pending->timeout_link = reply_link;
+
+ /* Insert the serial in the pending replies hash;
+ * hash takes a refcount on DBusPendingCall.
+ * Also, add the timeout.
+ */
+ if (!_dbus_connection_attach_pending_call_unlocked (connection,
+ pending))
{
CONNECTION_UNLOCK (connection);
- reply_handler_data_free (data);
+ dbus_pending_call_unref (pending);
return FALSE;
}
-
- CONNECTION_UNLOCK (connection);
- if (!dbus_connection_send (connection, message, NULL))
+ if (!_dbus_connection_send_unlocked (connection, message, NULL))
{
- /* This will free the handler data too */
- _dbus_hash_table_remove_int (connection->pending_replies, serial);
+ _dbus_connection_detach_pending_call_and_unlock (connection,
+ pending);
return FALSE;
}
+ if (pending_return)
+ {
+ dbus_pending_call_ref (pending);
+ *pending_return = pending;
+ }
+
+ CONNECTION_UNLOCK (connection);
+
return TRUE;
}
-
static DBusMessage*
check_for_reply_unlocked (DBusConnection *connection,
dbus_uint32_t client_serial)
@@ -1682,45 +1809,34 @@ check_for_reply_unlocked (DBusConnection *connection,
}
/**
- * Sends a message and blocks a certain time period while waiting for a reply.
- * This function does not dispatch any message handlers until the main loop
- * has been reached. This function is used to do non-reentrant "method calls."
- * If a reply is received, it is returned, and removed from the incoming
- * message queue. If it is not received, #NULL is returned and the
- * error is set to #DBUS_ERROR_NO_REPLY. If something else goes
- * wrong, result is set to whatever is appropriate, such as
- * #DBUS_ERROR_NO_MEMORY or #DBUS_ERROR_DISCONNECTED.
+ * Blocks a certain time period while waiting for a reply.
+ * If no reply arrives, returns #NULL.
*
* @todo could use performance improvements (it keeps scanning
* the whole message queue for example) and has thread issues,
* see comments in source
*
* @param connection the connection
- * @param message the message to send
+ * @param client_serial the reply serial to wait for
* @param timeout_milliseconds timeout in milliseconds or -1 for default
- * @param error return location for error message
- * @returns the message that is the reply or #NULL with an error code if the
- * function fails.
+ * @returns the message that is the reply or #NULL if no reply
*/
-DBusMessage *
-dbus_connection_send_with_reply_and_block (DBusConnection *connection,
- DBusMessage *message,
- int timeout_milliseconds,
- DBusError *error)
+DBusMessage*
+_dbus_connection_block_for_reply (DBusConnection *connection,
+ dbus_uint32_t client_serial,
+ int timeout_milliseconds)
{
- dbus_uint32_t client_serial;
long start_tv_sec, start_tv_usec;
long end_tv_sec, end_tv_usec;
long tv_sec, tv_usec;
DBusDispatchStatus status;
_dbus_return_val_if_fail (connection != NULL, NULL);
- _dbus_return_val_if_fail (message != NULL, NULL);
- _dbus_return_val_if_fail (timeout_milliseconds >= 0 || timeout_milliseconds == -1, FALSE);
- _dbus_return_val_if_error_is_set (error, NULL);
+ _dbus_return_val_if_fail (client_serial != 0, NULL);
+ _dbus_return_val_if_fail (timeout_milliseconds >= 0 || timeout_milliseconds == -1, FALSE);
if (timeout_milliseconds == -1)
- timeout_milliseconds = DEFAULT_TIMEOUT_VALUE;
+ timeout_milliseconds = _DBUS_DEFAULT_TIMEOUT_VALUE;
/* it would probably seem logical to pass in _DBUS_INT_MAX
* for infinite timeout, but then math below would get
@@ -1729,14 +1845,6 @@ dbus_connection_send_with_reply_and_block (DBusConnection *connection,
if (timeout_milliseconds > _DBUS_ONE_HOUR_IN_MILLISECONDS * 6)
timeout_milliseconds = _DBUS_ONE_HOUR_IN_MILLISECONDS * 6;
- if (!dbus_connection_send (connection, message, &client_serial))
- {
- _DBUS_SET_OOM (error);
- return NULL;
- }
-
- message = NULL;
-
/* Flush message queue */
dbus_connection_flush (connection);
@@ -1778,8 +1886,7 @@ dbus_connection_send_with_reply_and_block (DBusConnection *connection,
{
status = _dbus_connection_get_dispatch_status_unlocked (connection);
- _dbus_verbose ("dbus_connection_send_with_reply_and_block(): got reply %s\n",
- dbus_message_get_name (reply));
+ _dbus_verbose ("dbus_connection_send_with_reply_and_block(): got reply\n");
/* Unlocks, and calls out to user code */
_dbus_connection_update_dispatch_status_and_unlock (connection, status);
@@ -1831,11 +1938,6 @@ dbus_connection_send_with_reply_and_block (DBusConnection *connection,
_dbus_verbose ("dbus_connection_send_with_reply_and_block(): Waited %ld milliseconds and got no reply\n",
(tv_sec - start_tv_sec) * 1000 + (tv_usec - start_tv_usec) / 1000);
-
- if (dbus_connection_get_is_connected (connection))
- dbus_set_error (error, DBUS_ERROR_NO_REPLY, "Message did not receive a reply");
- else
- dbus_set_error (error, DBUS_ERROR_DISCONNECTED, "Disconnected prior to receiving a reply");
/* unlocks and calls out to user code */
_dbus_connection_update_dispatch_status_and_unlock (connection, status);
@@ -1844,6 +1946,70 @@ dbus_connection_send_with_reply_and_block (DBusConnection *connection,
}
/**
+ * Sends a message and blocks a certain time period while waiting for
+ * a reply. This function does not reenter the main loop,
+ * i.e. messages other than the reply are queued up but not
+ * processed. This function is used to do non-reentrant "method
+ * calls."
+ *
+ * If a normal reply is received, it is returned, and removed from the
+ * incoming message queue. If it is not received, #NULL is returned
+ * and the error is set to #DBUS_ERROR_NO_REPLY. If an error reply is
+ * received, it is converted to a #DBusError and returned as an error,
+ * then the reply message is deleted. If something else goes wrong,
+ * result is set to whatever is appropriate, such as
+ * #DBUS_ERROR_NO_MEMORY or #DBUS_ERROR_DISCONNECTED.
+ *
+ * @param connection the connection
+ * @param message the message to send
+ * @param timeout_milliseconds timeout in milliseconds or -1 for default
+ * @param error return location for error message
+ * @returns the message that is the reply or #NULL with an error code if the
+ * function fails.
+ */
+DBusMessage *
+dbus_connection_send_with_reply_and_block (DBusConnection *connection,
+ DBusMessage *message,
+ int timeout_milliseconds,
+ DBusError *error)
+{
+ dbus_uint32_t client_serial;
+ DBusMessage *reply;
+
+ _dbus_return_val_if_fail (connection != NULL, NULL);
+ _dbus_return_val_if_fail (message != NULL, NULL);
+ _dbus_return_val_if_fail (timeout_milliseconds >= 0 || timeout_milliseconds == -1, FALSE);
+ _dbus_return_val_if_error_is_set (error, NULL);
+
+ if (!dbus_connection_send (connection, message, &client_serial))
+ {
+ _DBUS_SET_OOM (error);
+ return NULL;
+ }
+
+ reply = _dbus_connection_block_for_reply (connection,
+ client_serial,
+ timeout_milliseconds);
+
+ if (reply == NULL)
+ {
+ if (dbus_connection_get_is_connected (connection))
+ dbus_set_error (error, DBUS_ERROR_NO_REPLY, "Message did not receive a reply");
+ else
+ dbus_set_error (error, DBUS_ERROR_DISCONNECTED, "Disconnected prior to receiving a reply");
+
+ return NULL;
+ }
+ else if (dbus_set_error_from_message (error, reply))
+ {
+ dbus_message_unref (reply);
+ return NULL;
+ }
+ else
+ return reply;
+}
+
+/**
* Blocks until the outgoing message queue is empty.
*
* @param connection the connection.
@@ -1908,6 +2074,8 @@ dbus_connection_borrow_message (DBusConnection *connection)
DBusDispatchStatus status;
_dbus_return_val_if_fail (connection != NULL, NULL);
+ /* can't borrow during dispatch */
+ _dbus_return_val_if_fail (!connection->dispatch_acquired, NULL);
/* this is called for the side effect that it queues
* up any messages from the transport
@@ -1943,6 +2111,8 @@ dbus_connection_return_message (DBusConnection *connection,
{
_dbus_return_if_fail (connection != NULL);
_dbus_return_if_fail (message != NULL);
+ /* can't borrow during dispatch */
+ _dbus_return_if_fail (!connection->dispatch_acquired);
CONNECTION_LOCK (connection);
@@ -1971,6 +2141,8 @@ dbus_connection_steal_borrowed_message (DBusConnection *connection,
_dbus_return_if_fail (connection != NULL);
_dbus_return_if_fail (message != NULL);
+ /* can't borrow during dispatch */
+ _dbus_return_if_fail (!connection->dispatch_acquired);
CONNECTION_LOCK (connection);
@@ -2007,7 +2179,10 @@ _dbus_connection_pop_message_link_unlocked (DBusConnection *connection)
connection->n_incoming -= 1;
_dbus_verbose ("Message %p (%s) removed from incoming queue %p, %d incoming\n",
- link->data, dbus_message_get_name (link->data),
+ link->data,
+ dbus_message_get_interface (link->data) ?
+ dbus_message_get_interface (link->data) :
+ "no interface",
connection, connection->n_incoming);
return link;
@@ -2040,6 +2215,25 @@ _dbus_connection_pop_message_unlocked (DBusConnection *connection)
return NULL;
}
+static void
+_dbus_connection_putback_message_link_unlocked (DBusConnection *connection,
+ DBusList *message_link)
+{
+ _dbus_assert (message_link != NULL);
+ /* You can't borrow a message while a link is outstanding */
+ _dbus_assert (connection->message_borrowed == NULL);
+
+ _dbus_list_prepend_link (&connection->incoming_messages,
+ message_link);
+ connection->n_incoming += 1;
+
+ _dbus_verbose ("Message %p (%s) put back into queue %p, %d incoming\n",
+ message_link->data,
+ dbus_message_get_interface (message_link->data) ?
+ dbus_message_get_interface (message_link->data) :
+ "no interface",
+ connection, connection->n_incoming);
+}
/**
* Returns the first-received message from the incoming message queue,
@@ -2215,18 +2409,22 @@ dbus_connection_get_dispatch_status (DBusConnection *connection)
* does not necessarily dispatch a message, as the data may
* be part of authentication or the like.
*
+ * @todo some FIXME in here about handling DBUS_HANDLER_RESULT_NEED_MEMORY
+ *
+ * @todo right now a message filter gets run on replies to a pending
+ * call in here, but not in the case where we block without
+ * entering the main loop.
+ *
* @param connection the connection
* @returns dispatch status
*/
DBusDispatchStatus
dbus_connection_dispatch (DBusConnection *connection)
{
- DBusMessageHandler *handler;
DBusMessage *message;
DBusList *link, *filter_list_copy, *message_link;
DBusHandlerResult result;
- ReplyHandlerData *reply_handler_data;
- const char *name;
+ DBusPendingCall *pending;
dbus_int32_t reply_serial;
DBusDispatchStatus status;
@@ -2272,11 +2470,11 @@ dbus_connection_dispatch (DBusConnection *connection)
message = message_link->data;
- result = DBUS_HANDLER_RESULT_ALLOW_MORE_HANDLERS;
+ result = DBUS_HANDLER_RESULT_NOT_YET_HANDLED;
reply_serial = dbus_message_get_reply_serial (message);
- reply_handler_data = _dbus_hash_table_lookup_int (connection->pending_replies,
- reply_serial);
+ pending = _dbus_hash_table_lookup_int (connection->pending_replies,
+ reply_serial);
if (!_dbus_list_copy (&connection->filter_list, &filter_list_copy))
{
@@ -2294,7 +2492,7 @@ dbus_connection_dispatch (DBusConnection *connection)
}
_dbus_list_foreach (&filter_list_copy,
- (DBusForeachFunction)dbus_message_handler_ref,
+ (DBusForeachFunction)_dbus_message_filter_ref,
NULL);
/* We're still protected from dispatch() reentrancy here
@@ -2305,92 +2503,164 @@ dbus_connection_dispatch (DBusConnection *connection)
link = _dbus_list_get_first_link (&filter_list_copy);
while (link != NULL)
{
- DBusMessageHandler *handler = link->data;
+ DBusMessageFilter *filter = link->data;
DBusList *next = _dbus_list_get_next_link (&filter_list_copy, link);
_dbus_verbose (" running filter on message %p\n", message);
- result = _dbus_message_handler_handle_message (handler, connection,
- message);
+ result = (* filter->function) (connection, message, filter->user_data);
- if (result == DBUS_HANDLER_RESULT_REMOVE_MESSAGE)
+ if (result != DBUS_HANDLER_RESULT_NOT_YET_HANDLED)
break;
link = next;
}
_dbus_list_foreach (&filter_list_copy,
- (DBusForeachFunction)dbus_message_handler_unref,
+ (DBusForeachFunction)_dbus_message_filter_unref,
NULL);
_dbus_list_clear (&filter_list_copy);
CONNECTION_LOCK (connection);
+ if (result == DBUS_HANDLER_RESULT_NEED_MEMORY)
+ goto out;
+
/* Did a reply we were waiting on get filtered? */
- if (reply_handler_data && result == DBUS_HANDLER_RESULT_REMOVE_MESSAGE)
+ if (pending && result == DBUS_HANDLER_RESULT_HANDLED)
{
/* Queue the timeout immediately! */
- if (reply_handler_data->timeout_link)
+ if (pending->timeout_link)
{
_dbus_connection_queue_synthesized_message_link (connection,
- reply_handler_data->timeout_link);
- reply_handler_data->timeout_link = NULL;
+ pending->timeout_link);
+ pending->timeout_link = NULL;
}
else
{
/* We already queued the timeout? Then it was filtered! */
- _dbus_warn ("The timeout error with reply serial %d was filtered, so the reply handler will never be called.\n", reply_serial);
+ _dbus_warn ("The timeout error with reply serial %d was filtered, so the DBusPendingCall will never stop pending.\n", reply_serial);
}
}
- if (result == DBUS_HANDLER_RESULT_REMOVE_MESSAGE)
+ if (result == DBUS_HANDLER_RESULT_HANDLED)
goto out;
-
- if (reply_handler_data)
+
+ if (pending)
{
- CONNECTION_UNLOCK (connection);
+ _dbus_pending_call_complete_and_unlock (pending, message);
- _dbus_verbose (" running reply handler on message %p\n", message);
+ pending = NULL;
- result = _dbus_message_handler_handle_message (reply_handler_data->handler,
- connection, message);
- reply_handler_data_free (reply_handler_data);
CONNECTION_LOCK (connection);
goto out;
}
+
+ /* We're still protected from dispatch() reentrancy here
+ * since we acquired the dispatcher
+ */
+ _dbus_verbose (" running object path dispatch on message %p (%s)\n",
+ message,
+ dbus_message_get_interface (message) ?
+ dbus_message_get_interface (message) :
+ "no interface");
- name = dbus_message_get_name (message);
- if (name != NULL)
+ result = _dbus_object_tree_dispatch_and_unlock (connection->objects,
+ message);
+
+ CONNECTION_LOCK (connection);
+
+ if (result != DBUS_HANDLER_RESULT_NOT_YET_HANDLED)
+ goto out;
+
+ if (dbus_message_get_type (message) == DBUS_MESSAGE_TYPE_METHOD_CALL)
{
- handler = _dbus_hash_table_lookup_string (connection->handler_table,
- name);
- if (handler != NULL)
+ DBusMessage *reply;
+ DBusString str;
+ DBusPreallocatedSend *preallocated;
+
+ _dbus_verbose (" sending error %s\n",
+ DBUS_ERROR_UNKNOWN_METHOD);
+
+ if (!_dbus_string_init (&str))
{
- /* We're still protected from dispatch() reentrancy here
- * since we acquired the dispatcher
- */
- CONNECTION_UNLOCK (connection);
+ result = DBUS_HANDLER_RESULT_NEED_MEMORY;
+ goto out;
+ }
+
+ if (!_dbus_string_append_printf (&str,
+ "Method \"%s\" on interface \"%s\" doesn't exist\n",
+ dbus_message_get_member (message),
+ dbus_message_get_interface (message)))
+ {
+ _dbus_string_free (&str);
+ result = DBUS_HANDLER_RESULT_NEED_MEMORY;
+ goto out;
+ }
+
+ reply = dbus_message_new_error (message,
+ DBUS_ERROR_UNKNOWN_METHOD,
+ _dbus_string_get_const_data (&str));
+ _dbus_string_free (&str);
- _dbus_verbose (" running app handler on message %p (%s)\n",
- message, dbus_message_get_name (message));
-
- result = _dbus_message_handler_handle_message (handler, connection,
- message);
- CONNECTION_LOCK (connection);
- if (result == DBUS_HANDLER_RESULT_REMOVE_MESSAGE)
- goto out;
+ if (reply == NULL)
+ {
+ result = DBUS_HANDLER_RESULT_NEED_MEMORY;
+ goto out;
+ }
+
+ preallocated = _dbus_connection_preallocate_send_unlocked (connection);
+
+ if (preallocated == NULL)
+ {
+ dbus_message_unref (reply);
+ result = DBUS_HANDLER_RESULT_NEED_MEMORY;
+ goto out;
}
- }
+ _dbus_connection_send_preallocated_unlocked (connection, preallocated,
+ reply, NULL);
+
+ dbus_message_unref (reply);
+
+ result = DBUS_HANDLER_RESULT_HANDLED;
+ }
+
_dbus_verbose (" done dispatching %p (%s) on connection %p\n", message,
- dbus_message_get_name (message), connection);
+ dbus_message_get_interface (message) ?
+ dbus_message_get_interface (message) :
+ "no interface",
+ connection);
out:
+ if (result == DBUS_HANDLER_RESULT_NEED_MEMORY)
+ {
+ /* Put message back, and we'll start over.
+ * Yes this means handlers must be idempotent if they
+ * don't return HANDLED; c'est la vie.
+ */
+ _dbus_connection_putback_message_link_unlocked (connection,
+ message_link);
+ }
+ else
+ {
+ if (connection->exit_on_disconnect &&
+ dbus_message_is_signal (message,
+ DBUS_INTERFACE_ORG_FREEDESKTOP_LOCAL,
+ "Disconnected"))
+ {
+ _dbus_verbose ("Exiting on Disconnected signal\n");
+ CONNECTION_UNLOCK (connection);
+ _dbus_exit (1);
+ _dbus_assert_not_reached ("Call to exit() returned");
+ }
+
+ _dbus_list_free_link (message_link);
+ dbus_message_unref (message); /* don't want the message to count in max message limits
+ * in computing dispatch status below
+ */
+ }
+
_dbus_connection_release_dispatch (connection);
-
- _dbus_list_free_link (message_link);
- dbus_message_unref (message); /* don't want the message to count in max message limits
- * in computing dispatch status
- */
status = _dbus_connection_get_dispatch_status_unlocked (connection);
@@ -2704,226 +2974,248 @@ dbus_connection_set_unix_user_function (DBusConnection *connection,
}
/**
- * Adds a message filter. Filters are handlers that are run on
- * all incoming messages, prior to the normal handlers
- * registered with dbus_connection_register_handler().
- * Filters are run in the order that they were added.
- * The same handler can be added as a filter more than once, in
- * which case it will be run more than once.
- * Filters added during a filter callback won't be run on the
- * message being processed.
+ * Adds a message filter. Filters are handlers that are run on all
+ * incoming messages, prior to the objects registered with
+ * dbus_connection_register_object_path(). Filters are run in the
+ * order that they were added. The same handler can be added as a
+ * filter more than once, in which case it will be run more than once.
+ * Filters added during a filter callback won't be run on the message
+ * being processed.
*
- * The connection does NOT add a reference to the message handler;
- * instead, if the message handler is finalized, the connection simply
- * forgets about it. Thus the caller of this function must keep a
- * reference to the message handler.
+ * @todo we don't run filters on messages while blocking without
+ * entering the main loop, since filters are run as part of
+ * dbus_connection_dispatch().
*
* @param connection the connection
- * @param handler the handler
+ * @param function function to handle messages
+ * @param user_data user data to pass to the function
+ * @param free_data_function function to use for freeing user data
* @returns #TRUE on success, #FALSE if not enough memory.
*/
dbus_bool_t
-dbus_connection_add_filter (DBusConnection *connection,
- DBusMessageHandler *handler)
+dbus_connection_add_filter (DBusConnection *connection,
+ DBusHandleMessageFunction function,
+ void *user_data,
+ DBusFreeFunction free_data_function)
{
+ DBusMessageFilter *filter;
+
_dbus_return_val_if_fail (connection != NULL, FALSE);
- _dbus_return_val_if_fail (handler != NULL, FALSE);
+ _dbus_return_val_if_fail (function != NULL, FALSE);
+
+ filter = dbus_new0 (DBusMessageFilter, 1);
+ if (filter == NULL)
+ return FALSE;
+ filter->refcount.value = 1;
+
CONNECTION_LOCK (connection);
- if (!_dbus_message_handler_add_connection (handler, connection))
- {
- CONNECTION_UNLOCK (connection);
- return FALSE;
- }
if (!_dbus_list_append (&connection->filter_list,
- handler))
+ filter))
{
- _dbus_message_handler_remove_connection (handler, connection);
+ _dbus_message_filter_unref (filter);
CONNECTION_UNLOCK (connection);
return FALSE;
}
+ /* Fill in filter after all memory allocated,
+ * so we don't run the free_user_data_function
+ * if the add_filter() fails
+ */
+
+ filter->function = function;
+ filter->user_data = user_data;
+ filter->free_user_data_function = free_data_function;
+
CONNECTION_UNLOCK (connection);
return TRUE;
}
/**
* Removes a previously-added message filter. It is a programming
- * error to call this function for a handler that has not
- * been added as a filter. If the given handler was added
- * more than once, only one instance of it will be removed
- * (the most recently-added instance).
+ * error to call this function for a handler that has not been added
+ * as a filter. If the given handler was added more than once, only
+ * one instance of it will be removed (the most recently-added
+ * instance).
*
* @param connection the connection
- * @param handler the handler to remove
+ * @param function the handler to remove
+ * @param user_data user data for the handler to remove
*
*/
void
-dbus_connection_remove_filter (DBusConnection *connection,
- DBusMessageHandler *handler)
+dbus_connection_remove_filter (DBusConnection *connection,
+ DBusHandleMessageFunction function,
+ void *user_data)
{
+ DBusList *link;
+ DBusMessageFilter *filter;
+
_dbus_return_if_fail (connection != NULL);
- _dbus_return_if_fail (handler != NULL);
+ _dbus_return_if_fail (function != NULL);
CONNECTION_LOCK (connection);
- if (!_dbus_list_remove_last (&connection->filter_list, handler))
+
+ filter = NULL;
+
+ link = _dbus_list_get_last_link (&connection->filter_list);
+ while (link != NULL)
{
- _dbus_warn ("Tried to remove a DBusConnection filter that had not been added\n");
- CONNECTION_UNLOCK (connection);
- return;
+ filter = link->data;
+
+ if (filter->function == function &&
+ filter->user_data == user_data)
+ {
+ _dbus_list_remove_link (&connection->filter_list, link);
+ filter->function = NULL;
+
+ break;
+ }
+
+ link = _dbus_list_get_prev_link (&connection->filter_list, link);
}
+
+ CONNECTION_UNLOCK (connection);
- _dbus_message_handler_remove_connection (handler, connection);
+#ifndef DBUS_DISABLE_CHECKS
+ if (filter == NULL)
+ {
+ _dbus_warn ("Attempt to remove filter function %p user data %p, but no such filter has been added\n",
+ function, user_data);
+ return;
+ }
+#endif
+
+ /* Call application code */
+ if (filter->free_user_data_function)
+ (* filter->free_user_data_function) (filter->user_data);
- CONNECTION_UNLOCK (connection);
+ filter->free_user_data_function = NULL;
+ filter->user_data = NULL;
+
+ _dbus_message_filter_unref (filter);
}
/**
- * Registers a handler for a list of message names. A single handler
- * can be registered for any number of message names, but each message
- * name can only have one handler at a time. It's not allowed to call
- * this function with the name of a message that already has a
- * handler. If the function returns #FALSE, the handlers were not
- * registered due to lack of memory.
+ * Registers a handler for a given path in the object hierarchy.
+ * The given vtable handles messages sent to exactly the given path.
*
- * The connection does NOT add a reference to the message handler;
- * instead, if the message handler is finalized, the connection simply
- * forgets about it. Thus the caller of this function must keep a
- * reference to the message handler.
*
- * @todo the messages_to_handle arg may be more convenient if it's a
- * single string instead of an array. Though right now MessageHandler
- * is sort of designed to say be associated with an entire object with
- * multiple methods, that's why for example the connection only
- * weakrefs it. So maybe the "manual" API should be different.
- *
* @param connection the connection
- * @param handler the handler
- * @param messages_to_handle the messages to handle
- * @param n_messages the number of message names in messages_to_handle
- * @returns #TRUE on success, #FALSE if no memory or another handler already exists
- *
- **/
+ * @param path #NULL-terminated array of path elements
+ * @param vtable the virtual table
+ * @param user_data data to pass to functions in the vtable
+ * @returns #FALSE if not enough memory
+ */
dbus_bool_t
-dbus_connection_register_handler (DBusConnection *connection,
- DBusMessageHandler *handler,
- const char **messages_to_handle,
- int n_messages)
+dbus_connection_register_object_path (DBusConnection *connection,
+ const char **path,
+ const DBusObjectPathVTable *vtable,
+ void *user_data)
{
- int i;
-
- _dbus_return_val_if_fail (connection != NULL, FALSE);
- _dbus_return_val_if_fail (handler != NULL, FALSE);
- _dbus_return_val_if_fail (n_messages >= 0, FALSE);
- _dbus_return_val_if_fail (n_messages == 0 || messages_to_handle != NULL, FALSE);
+ dbus_bool_t retval;
+ _dbus_return_val_if_fail (connection != NULL, FALSE);
+ _dbus_return_val_if_fail (path != NULL, FALSE);
+ _dbus_return_val_if_fail (path[0] != NULL, FALSE);
+ _dbus_return_val_if_fail (vtable != NULL, FALSE);
+
CONNECTION_LOCK (connection);
- i = 0;
- while (i < n_messages)
- {
- DBusHashIter iter;
- char *key;
- key = _dbus_strdup (messages_to_handle[i]);
- if (key == NULL)
- goto failed;
-
- if (!_dbus_hash_iter_lookup (connection->handler_table,
- key, TRUE,
- &iter))
- {
- dbus_free (key);
- goto failed;
- }
+ retval = _dbus_object_tree_register (connection->objects,
+ FALSE,
+ path, vtable,
+ user_data);
- if (_dbus_hash_iter_get_value (&iter) != NULL)
- {
- _dbus_warn ("Bug in application: attempted to register a second handler for %s\n",
- messages_to_handle[i]);
- dbus_free (key); /* won't have replaced the old key with the new one */
- goto failed;
- }
+ CONNECTION_UNLOCK (connection);
- if (!_dbus_message_handler_add_connection (handler, connection))
- {
- _dbus_hash_iter_remove_entry (&iter);
- /* key has freed on nuking the entry */
- goto failed;
- }
-
- _dbus_hash_iter_set_value (&iter, handler);
+ return retval;
+}
- ++i;
- }
-
- CONNECTION_UNLOCK (connection);
- return TRUE;
+/**
+ * Registers a fallback handler for a given subsection of the object
+ * hierarchy. The given vtable handles messages at or below the given
+ * path. You can use this to establish a default message handling
+ * policy for a whole "subdirectory."
+ *
+ * @param connection the connection
+ * @param path #NULL-terminated array of path elements
+ * @param vtable the virtual table
+ * @param user_data data to pass to functions in the vtable
+ * @returns #FALSE if not enough memory
+ */
+dbus_bool_t
+dbus_connection_register_fallback (DBusConnection *connection,
+ const char **path,
+ const DBusObjectPathVTable *vtable,
+ void *user_data)
+{
+ dbus_bool_t retval;
- failed:
- /* unregister everything registered so far,
- * so we don't fail partially
- */
- dbus_connection_unregister_handler (connection,
- handler,
- messages_to_handle,
- i);
+ _dbus_return_val_if_fail (connection != NULL, FALSE);
+ _dbus_return_val_if_fail (path != NULL, FALSE);
+ _dbus_return_val_if_fail (path[0] != NULL, FALSE);
+ _dbus_return_val_if_fail (vtable != NULL, FALSE);
+
+ CONNECTION_LOCK (connection);
+
+ retval = _dbus_object_tree_register (connection->objects,
+ TRUE,
+ path, vtable,
+ user_data);
CONNECTION_UNLOCK (connection);
- return FALSE;
+
+ return retval;
}
/**
- * Unregisters a handler for a list of message names. The handlers
- * must have been previously registered.
+ * Unregisters the handler registered with exactly the given path.
+ * It's a bug to call this function for a path that isn't registered.
+ * Can unregister both fallback paths and object paths.
*
* @param connection the connection
- * @param handler the handler
- * @param messages_to_handle the messages to handle
- * @param n_messages the number of message names in messages_to_handle
- *
- **/
+ * @param path the #NULL-terminated array of path elements
+ */
void
-dbus_connection_unregister_handler (DBusConnection *connection,
- DBusMessageHandler *handler,
- const char **messages_to_handle,
- int n_messages)
+dbus_connection_unregister_object_path (DBusConnection *connection,
+ const char **path)
{
- int i;
-
_dbus_return_if_fail (connection != NULL);
- _dbus_return_if_fail (handler != NULL);
- _dbus_return_if_fail (n_messages >= 0);
- _dbus_return_if_fail (n_messages == 0 || messages_to_handle != NULL);
-
+ _dbus_return_if_fail (path != NULL);
+ _dbus_return_if_fail (path[0] != NULL);
+
CONNECTION_LOCK (connection);
- i = 0;
- while (i < n_messages)
- {
- DBusHashIter iter;
- if (!_dbus_hash_iter_lookup (connection->handler_table,
- (char*) messages_to_handle[i], FALSE,
- &iter))
- {
- _dbus_warn ("Bug in application: attempted to unregister handler for %s which was not registered\n",
- messages_to_handle[i]);
- }
- else if (_dbus_hash_iter_get_value (&iter) != handler)
- {
- _dbus_warn ("Bug in application: attempted to unregister handler for %s which was registered by a different handler\n",
- messages_to_handle[i]);
- }
- else
- {
- _dbus_hash_iter_remove_entry (&iter);
- _dbus_message_handler_remove_connection (handler, connection);
- }
+ return _dbus_object_tree_unregister_and_unlock (connection->objects,
+ path);
+}
- ++i;
- }
+/**
+ * Lists the registered fallback handlers and object path handlers at
+ * the given parent_path. The returned array should be freed with
+ * dbus_free_string_array().
+ *
+ * @param connection the connection
+ * @param parent_path the path to list the child handlers of
+ * @param child_entries returns #NULL-terminated array of children
+ * @returns #FALSE if no memory to allocate the child entries
+ */
+dbus_bool_t
+dbus_connection_list_registered (DBusConnection *connection,
+ const char **parent_path,
+ char ***child_entries)
+{
+ _dbus_return_val_if_fail (connection != NULL, FALSE);
+ _dbus_return_val_if_fail (parent_path != NULL, FALSE);
+ _dbus_return_val_if_fail (child_entries != NULL, FALSE);
- CONNECTION_UNLOCK (connection);
+ CONNECTION_LOCK (connection);
+
+ return _dbus_object_tree_list_registered_and_unlock (connection->objects,
+ parent_path,
+ child_entries);
}
static DBusDataSlotAllocator slot_allocator;
diff --git a/dbus/dbus-connection.h b/dbus/dbus-connection.h
index 9f4dd7ae..aa92b30a 100644
--- a/dbus/dbus-connection.h
+++ b/dbus/dbus-connection.h
@@ -1,7 +1,7 @@
/* -*- mode: C; c-file-style: "gnu" -*- */
/* dbus-connection.h DBusConnection object
*
- * Copyright (C) 2002 Red Hat Inc.
+ * Copyright (C) 2002, 2003 Red Hat Inc.
*
* Licensed under the Academic Free License version 1.2
*
@@ -28,22 +28,17 @@
#define DBUS_CONNECTION_H
#include <dbus/dbus-errors.h>
-#include <dbus/dbus-message.h>
#include <dbus/dbus-memory.h>
+#include <dbus/dbus-message.h>
DBUS_BEGIN_DECLS;
-typedef struct DBusConnection DBusConnection;
typedef struct DBusWatch DBusWatch;
typedef struct DBusTimeout DBusTimeout;
-typedef struct DBusMessageHandler DBusMessageHandler;
typedef struct DBusPreallocatedSend DBusPreallocatedSend;
-
-typedef enum
-{
- DBUS_HANDLER_RESULT_REMOVE_MESSAGE, /**< Remove this message, no further processing. */
- DBUS_HANDLER_RESULT_ALLOW_MORE_HANDLERS /**< Run any additional handlers that are interested in this message. */
-} DBusHandlerResult;
+typedef struct DBusPendingCall DBusPendingCall;
+typedef struct DBusConnection DBusConnection;
+typedef struct DBusObjectPathVTable DBusObjectPathVTable;
typedef enum
{
@@ -63,6 +58,13 @@ typedef enum
DBUS_DISPATCH_NEED_MEMORY /**< More memory is needed to continue. */
} DBusDispatchStatus;
+typedef enum
+{
+ DBUS_HANDLER_RESULT_HANDLED, /**< Message has had its effect */
+ DBUS_HANDLER_RESULT_NOT_YET_HANDLED, /**< Message has not had any effect */
+ DBUS_HANDLER_RESULT_NEED_MEMORY /**< Need more memory to return another result */
+} DBusHandlerResult;
+
typedef dbus_bool_t (* DBusAddWatchFunction) (DBusWatch *watch,
void *data);
typedef void (* DBusWatchToggledFunction) (DBusWatch *watch,
@@ -83,6 +85,14 @@ typedef dbus_bool_t (* DBusAllowUnixUserFunction) (DBusConnection *connection,
unsigned long uid,
void *data);
+typedef void (* DBusPendingCallNotifyFunction) (DBusPendingCall *pending,
+ void *user_data);
+
+
+typedef DBusHandlerResult (* DBusHandleMessageFunction) (DBusConnection *connection,
+ DBusMessage *message,
+ void *user_data);
+
DBusConnection* dbus_connection_open (const char *address,
DBusError *error);
void dbus_connection_ref (DBusConnection *connection);
@@ -90,6 +100,8 @@ void dbus_connection_unref (DBusConnection
void dbus_connection_disconnect (DBusConnection *connection);
dbus_bool_t dbus_connection_get_is_connected (DBusConnection *connection);
dbus_bool_t dbus_connection_get_is_authenticated (DBusConnection *connection);
+void dbus_connection_set_exit_on_disconnect (DBusConnection *connection,
+ dbus_bool_t exit_on_disconnect);
void dbus_connection_flush (DBusConnection *connection);
DBusMessage* dbus_connection_borrow_message (DBusConnection *connection);
void dbus_connection_return_message (DBusConnection *connection,
@@ -104,7 +116,7 @@ dbus_bool_t dbus_connection_send (DBusConnection
dbus_uint32_t *client_serial);
dbus_bool_t dbus_connection_send_with_reply (DBusConnection *connection,
DBusMessage *message,
- DBusMessageHandler *reply_handler,
+ DBusPendingCall **pending_return,
int timeout_milliseconds);
DBusMessage * dbus_connection_send_with_reply_and_block (DBusConnection *connection,
DBusMessage *message,
@@ -156,22 +168,18 @@ void dbus_timeout_set_data (DBusTimeout *timeout,
dbus_bool_t dbus_timeout_handle (DBusTimeout *timeout);
dbus_bool_t dbus_timeout_get_enabled (DBusTimeout *timeout);
-/* Handlers */
-dbus_bool_t dbus_connection_add_filter (DBusConnection *connection,
- DBusMessageHandler *handler);
-void dbus_connection_remove_filter (DBusConnection *connection,
- DBusMessageHandler *handler);
+/* Filters */
-dbus_bool_t dbus_connection_register_handler (DBusConnection *connection,
- DBusMessageHandler *handler,
- const char **messages_to_handle,
- int n_messages);
-void dbus_connection_unregister_handler (DBusConnection *connection,
- DBusMessageHandler *handler,
- const char **messages_to_handle,
- int n_messages);
+dbus_bool_t dbus_connection_add_filter (DBusConnection *connection,
+ DBusHandleMessageFunction function,
+ void *user_data,
+ DBusFreeFunction free_data_function);
+void dbus_connection_remove_filter (DBusConnection *connection,
+ DBusHandleMessageFunction function,
+ void *user_data);
+/* Other */
dbus_bool_t dbus_connection_allocate_data_slot (dbus_int32_t *slot_p);
void dbus_connection_free_data_slot (dbus_int32_t *slot_p);
dbus_bool_t dbus_connection_set_data (DBusConnection *connection,
@@ -200,6 +208,44 @@ void dbus_connection_send_preallocated (DBusConnection
dbus_uint32_t *client_serial);
+/* Object tree functionality */
+
+typedef void (* DBusObjectPathUnregisterFunction) (DBusConnection *connection,
+ void *user_data);
+typedef DBusHandlerResult (* DBusObjectPathMessageFunction) (DBusConnection *connection,
+ DBusMessage *message,
+ void *user_data);
+
+/**
+ * Virtual table that must be implemented to handle a portion of the
+ * object path hierarchy.
+ */
+struct DBusObjectPathVTable
+{
+ DBusObjectPathUnregisterFunction unregister_function; /**< Function to unregister this handler */
+ DBusObjectPathMessageFunction message_function; /**< Function to handle messages */
+
+ void (* dbus_internal_pad1) (void *); /**< Reserved for future expansion */
+ void (* dbus_internal_pad2) (void *); /**< Reserved for future expansion */
+ void (* dbus_internal_pad3) (void *); /**< Reserved for future expansion */
+ void (* dbus_internal_pad4) (void *); /**< Reserved for future expansion */
+};
+
+dbus_bool_t dbus_connection_register_object_path (DBusConnection *connection,
+ const char **path,
+ const DBusObjectPathVTable *vtable,
+ void *user_data);
+dbus_bool_t dbus_connection_register_fallback (DBusConnection *connection,
+ const char **path,
+ const DBusObjectPathVTable *vtable,
+ void *user_data);
+void dbus_connection_unregister_object_path (DBusConnection *connection,
+ const char **path);
+
+dbus_bool_t dbus_connection_list_registered (DBusConnection *connection,
+ const char **parent_path,
+ char ***child_entries);
+
DBUS_END_DECLS;
#endif /* DBUS_CONNECTION_H */
diff --git a/dbus/dbus-dataslot.h b/dbus/dbus-dataslot.h
index 6f591eb5..c4a914b4 100644
--- a/dbus/dbus-dataslot.h
+++ b/dbus/dbus-dataslot.h
@@ -40,12 +40,18 @@ struct DBusDataSlot
};
typedef struct DBusAllocatedSlot DBusAllocatedSlot;
+
+/** An allocated slot for storing data
+ */
struct DBusAllocatedSlot
{
dbus_int32_t slot_id; /**< ID of this slot */
int refcount; /**< Number of uses of the slot */
};
+/**
+ * An allocator that tracks a set of slot IDs.
+ */
struct DBusDataSlotAllocator
{
DBusAllocatedSlot *allocated_slots; /**< Allocated slots */
@@ -54,6 +60,10 @@ struct DBusDataSlotAllocator
DBusMutex *lock; /**< thread lock */
};
+/**
+ * Data structure that stores the actual user data set at a given
+ * slot.
+ */
struct DBusDataSlotList
{
DBusDataSlot *slots; /**< Data slots */
diff --git a/dbus/dbus-errors.c b/dbus/dbus-errors.c
index 3cf52363..f7b2f740 100644
--- a/dbus/dbus-errors.c
+++ b/dbus/dbus-errors.c
@@ -23,53 +23,26 @@
*/
#include "dbus-errors.h"
#include "dbus-internals.h"
+#include "dbus-string.h"
#include <stdarg.h>
-#include <stdio.h>
#include <string.h>
/**
- * @defgroup DBusErrors Error reporting
- * @ingroup DBus
- * @brief Error reporting
- *
- * Types and functions related to reporting errors.
- *
- *
- * In essence D-BUS error reporting works as follows:
- *
- * @code
- * DBusError error;
- * dbus_error_init (&error);
- * dbus_some_function (arg1, arg2, &error);
- * if (dbus_error_is_set (&error))
- * {
- * fprintf (stderr, "an error occurred: %s\n", error.message);
- * dbus_error_free (&error);
- * }
- * @endcode
- *
- * There are some rules. An error passed to a D-BUS function must
- * always be unset; you can't pass in an error that's already set. If
- * a function has a return code indicating whether an error occurred,
- * and also a #DBusError parameter, then the error will always be set
- * if and only if the return code indicates an error occurred. i.e.
- * the return code and the error are never going to disagree.
- *
- * An error only needs to be freed if it's been set, not if
- * it's merely been initialized.
- *
- * You can check the specific error that occurred using
- * dbus_error_has_name().
- *
+ * @defgroup DBusErrorInternals Error reporting internals
+ * @ingroup DBusInternals
+ * @brief Error reporting internals
* @{
*/
-
+
+/**
+ * Internals of DBusError
+ */
typedef struct
{
const char *name; /**< error name */
char *message; /**< error message */
- unsigned int const_message : 1; /** Message is not owned by DBusError */
+ unsigned int const_message : 1; /**< Message is not owned by DBusError */
unsigned int dummy2 : 1; /**< placeholder */
unsigned int dummy3 : 1; /**< placeholder */
@@ -127,6 +100,45 @@ message_from_error (const char *error)
return error;
}
+/** @} */ /* End of internals */
+
+/**
+ * @defgroup DBusErrors Error reporting
+ * @ingroup DBus
+ * @brief Error reporting
+ *
+ * Types and functions related to reporting errors.
+ *
+ *
+ * In essence D-BUS error reporting works as follows:
+ *
+ * @code
+ * DBusError error;
+ * dbus_error_init (&error);
+ * dbus_some_function (arg1, arg2, &error);
+ * if (dbus_error_is_set (&error))
+ * {
+ * fprintf (stderr, "an error occurred: %s\n", error.message);
+ * dbus_error_free (&error);
+ * }
+ * @endcode
+ *
+ * There are some rules. An error passed to a D-BUS function must
+ * always be unset; you can't pass in an error that's already set. If
+ * a function has a return code indicating whether an error occurred,
+ * and also a #DBusError parameter, then the error will always be set
+ * if and only if the return code indicates an error occurred. i.e.
+ * the return code and the error are never going to disagree.
+ *
+ * An error only needs to be freed if it's been set, not if
+ * it's merely been initialized.
+ *
+ * You can check the specific error that occurred using
+ * dbus_error_has_name().
+ *
+ * @{
+ */
+
/**
* Initializes a DBusError structure. Does not allocate
* any memory; the error only needs to be freed
@@ -292,9 +304,6 @@ dbus_error_is_set (const DBusError *error)
*
* @todo should be called dbus_error_set()
*
- * @todo stdio.h shouldn't be included in this file,
- * should write _dbus_string_append_printf instead
- *
* @param error the error.
* @param name the error name (not copied!!!)
* @param format printf-style format string.
@@ -306,11 +315,9 @@ dbus_set_error (DBusError *error,
...)
{
DBusRealError *real;
+ DBusString str;
va_list args;
- int message_length;
- char *message;
- char c;
-
+
if (error == NULL)
return;
@@ -321,31 +328,46 @@ dbus_set_error (DBusError *error,
_dbus_assert (error->name == NULL);
_dbus_assert (error->message == NULL);
- if (format == NULL)
- format = message_from_error (name);
-
- va_start (args, format);
- /* Measure the message length */
- message_length = vsnprintf (&c, 1, format, args) + 1;
- va_end (args);
-
- message = dbus_malloc (message_length);
+ if (!_dbus_string_init (&str))
+ goto nomem;
- if (!message)
+ if (format == NULL)
{
- dbus_set_error_const (error, DBUS_ERROR_NO_MEMORY, NULL);
- return;
+ if (!_dbus_string_append (&str,
+ message_from_error (name)))
+ {
+ _dbus_string_free (&str);
+ goto nomem;
+ }
+ }
+ else
+ {
+ va_start (args, format);
+ if (!_dbus_string_append_printf_valist (&str, format, args))
+ {
+ _dbus_string_free (&str);
+ goto nomem;
+ }
+ va_end (args);
}
-
- va_start (args, format);
- vsprintf (message, format, args);
- va_end (args);
real = (DBusRealError *)error;
+
+ if (!_dbus_string_steal_data (&str, &real->message))
+ {
+ _dbus_string_free (&str);
+ goto nomem;
+ }
real->name = name;
- real->message = message;
real->const_message = FALSE;
+
+ _dbus_string_free (&str);
+
+ return;
+
+ nomem:
+ dbus_set_error_const (error, DBUS_ERROR_NO_MEMORY, NULL);
}
-/** @} */
+/** @} */ /* End public API */
diff --git a/dbus/dbus-errors.h b/dbus/dbus-errors.h
index 8d8e16ec..f229188a 100644
--- a/dbus/dbus-errors.h
+++ b/dbus/dbus-errors.h
@@ -35,6 +35,9 @@ DBUS_BEGIN_DECLS;
typedef struct DBusError DBusError;
+/**
+ * Object representing an exception.
+ */
struct DBusError
{
const char *name; /**< error name */
@@ -50,13 +53,8 @@ struct DBusError
};
#define DBUS_ERROR_FAILED "org.freedesktop.DBus.Error.Failed"
-#define DBUS_ERROR_ACTIVATE_SERVICE_NOT_FOUND "org.freedesktop.DBus.Activate.ServiceNotFound"
-#define DBUS_ERROR_SPAWN_EXEC_FAILED "org.freedesktop.DBus.Error.Spawn.ExecFailed"
-#define DBUS_ERROR_SPAWN_FORK_FAILED "org.freedesktop.DBus.Error.Spawn.ForkFailed"
-#define DBUS_ERROR_SPAWN_CHILD_EXITED "org.freedesktop.DBus.Error.Spawn.ChildExited"
-#define DBUS_ERROR_SPAWN_CHILD_SIGNALED "org.freedesktop.DBus.Error.Spawn.ChildSignaled"
-#define DBUS_ERROR_SPAWN_FAILED "org.freedesktop.DBus.Error.Spawn.Failed"
#define DBUS_ERROR_NO_MEMORY "org.freedesktop.DBus.Error.NoMemory"
+#define DBUS_ERROR_ACTIVATE_SERVICE_NOT_FOUND "org.freedesktop.DBus.Error.ServiceNotFound"
#define DBUS_ERROR_SERVICE_DOES_NOT_EXIST "org.freedesktop.DBus.Error.ServiceDoesNotExist"
#define DBUS_ERROR_NO_REPLY "org.freedesktop.DBus.Error.NoReply"
#define DBUS_ERROR_IO_ERROR "org.freedesktop.DBus.Error.IOError"
@@ -72,8 +70,14 @@ struct DBusError
#define DBUS_ERROR_DISCONNECTED "org.freedesktop.DBus.Error.Disconnected"
#define DBUS_ERROR_INVALID_ARGS "org.freedesktop.DBus.Error.InvalidArgs"
#define DBUS_ERROR_FILE_NOT_FOUND "org.freedesktop.DBus.Error.FileNotFound"
-#define DBUS_ERROR_UNKNOWN_MESSAGE "org.freedesktop.DBus.Error.UnknownMessage"
+#define DBUS_ERROR_UNKNOWN_METHOD "org.freedesktop.DBus.Error.UnknownMethod"
#define DBUS_ERROR_TIMED_OUT "org.freedesktop.DBus.Error.TimedOut"
+#define DBUS_ERROR_MATCH_RULE_NOT_FOUND "org.freedesktop.DBus.Error.MatchRuleNotFound"
+#define DBUS_ERROR_SPAWN_EXEC_FAILED "org.freedesktop.DBus.Error.Spawn.ExecFailed"
+#define DBUS_ERROR_SPAWN_FORK_FAILED "org.freedesktop.DBus.Error.Spawn.ForkFailed"
+#define DBUS_ERROR_SPAWN_CHILD_EXITED "org.freedesktop.DBus.Error.Spawn.ChildExited"
+#define DBUS_ERROR_SPAWN_CHILD_SIGNALED "org.freedesktop.DBus.Error.Spawn.ChildSignaled"
+#define DBUS_ERROR_SPAWN_FAILED "org.freedesktop.DBus.Error.Spawn.Failed"
void dbus_error_init (DBusError *error);
void dbus_error_free (DBusError *error);
diff --git a/dbus/dbus-hash.c b/dbus/dbus-hash.c
index 2c410010..d35087b4 100644
--- a/dbus/dbus-hash.c
+++ b/dbus/dbus-hash.c
@@ -221,26 +221,32 @@ typedef struct
int n_entries_on_init; /**< used to detect table resize since initialization */
} DBusRealHashIter;
-static DBusHashEntry* find_direct_function (DBusHashTable *table,
- void *key,
- dbus_bool_t create_if_not_found,
- DBusHashEntry ***bucket,
- DBusPreallocatedHash *preallocated);
-static DBusHashEntry* find_string_function (DBusHashTable *table,
- void *key,
- dbus_bool_t create_if_not_found,
- DBusHashEntry ***bucket,
- DBusPreallocatedHash *preallocated);
-static unsigned int string_hash (const char *str);
-static void rebuild_table (DBusHashTable *table);
-static DBusHashEntry* alloc_entry (DBusHashTable *table);
-static void remove_entry (DBusHashTable *table,
- DBusHashEntry **bucket,
- DBusHashEntry *entry);
-static void free_entry (DBusHashTable *table,
- DBusHashEntry *entry);
-static void free_entry_data (DBusHashTable *table,
- DBusHashEntry *entry);
+static DBusHashEntry* find_direct_function (DBusHashTable *table,
+ void *key,
+ dbus_bool_t create_if_not_found,
+ DBusHashEntry ***bucket,
+ DBusPreallocatedHash *preallocated);
+static DBusHashEntry* find_string_function (DBusHashTable *table,
+ void *key,
+ dbus_bool_t create_if_not_found,
+ DBusHashEntry ***bucket,
+ DBusPreallocatedHash *preallocated);
+static DBusHashEntry* find_two_strings_function (DBusHashTable *table,
+ void *key,
+ dbus_bool_t create_if_not_found,
+ DBusHashEntry ***bucket,
+ DBusPreallocatedHash *preallocated);
+static unsigned int string_hash (const char *str);
+static unsigned int two_strings_hash (const char *str);
+static void rebuild_table (DBusHashTable *table);
+static DBusHashEntry* alloc_entry (DBusHashTable *table);
+static void remove_entry (DBusHashTable *table,
+ DBusHashEntry **bucket,
+ DBusHashEntry *entry);
+static void free_entry (DBusHashTable *table,
+ DBusHashEntry *entry);
+static void free_entry_data (DBusHashTable *table,
+ DBusHashEntry *entry);
/** @} */
@@ -323,6 +329,9 @@ _dbus_hash_table_new (DBusHashType type,
case DBUS_HASH_STRING:
table->find_function = find_string_function;
break;
+ case DBUS_HASH_TWO_STRINGS:
+ table->find_function = find_two_strings_function;
+ break;
default:
_dbus_assert_not_reached ("Unknown hash table type");
break;
@@ -685,6 +694,24 @@ _dbus_hash_iter_get_string_key (DBusHashIter *iter)
}
/**
+ * Gets the key for the current entry.
+ * Only works for hash tables of type #DBUS_HASH_TWO_STRINGS
+ * @param iter the hash table iterator.
+ */
+const char*
+_dbus_hash_iter_get_two_strings_key (DBusHashIter *iter)
+{
+ DBusRealHashIter *real;
+
+ real = (DBusRealHashIter*) iter;
+
+ _dbus_assert (real->table != NULL);
+ _dbus_assert (real->entry != NULL);
+
+ return real->entry->key;
+}
+
+/**
* A low-level but efficient interface for manipulating the hash
* table. It's efficient because you can get, set, and optionally
* create the hash entry while only running the hash function one
@@ -803,64 +830,64 @@ add_entry (DBusHashTable *table,
return entry;
}
+/* This is g_str_hash from GLib which was
+ * extensively discussed/tested/profiled
+ */
static unsigned int
string_hash (const char *str)
{
- register unsigned int result;
- register int c;
+ const char *p = str;
+ unsigned int h = *p;
- /*
- * I tried a zillion different hash functions and asked many other
- * people for advice. Many people had their own favorite functions,
- * all different, but no-one had much idea why they were good ones.
- * I chose the one below (multiply by 9 and add new character)
- * because of the following reasons:
- *
- * 1. Multiplying by 10 is perfect for keys that are decimal strings,
- * and multiplying by 9 is just about as good.
- * 2. Times-9 is (shift-left-3) plus (old). This means that each
- * character's bits hang around in the low-order bits of the
- * hash value for ever, plus they spread fairly rapidly up to
- * the high-order bits to fill out the hash value. This seems
- * works well both for decimal and non-decimal strings.
- */
+ if (h)
+ for (p += 1; *p != '\0'; p++)
+ h = (h << 5) - h + *p;
- /* FIXME the hash function in GLib is better than this one */
-
- result = 0;
- while (TRUE)
- {
- c = *str;
- str++;
- if (c == 0)
- break;
-
- result += (result << 3) + c;
- }
+ return h;
+}
+
+/* This hashes a memory block with two nul-terminated strings
+ * in it, used in dbus-object-registry.c at the moment.
+ */
+static unsigned int
+two_strings_hash (const char *str)
+{
+ const char *p = str;
+ unsigned int h = *p;
+
+ if (h)
+ for (p += 1; *p != '\0'; p++)
+ h = (h << 5) - h + *p;
+
+ for (p += 1; *p != '\0'; p++)
+ h = (h << 5) - h + *p;
- return result;
+ return h;
}
+/** Key comparison function */
+typedef int (* KeyCompareFunc) (const void *key_a, const void *key_b);
+
static DBusHashEntry*
-find_string_function (DBusHashTable *table,
- void *key,
- dbus_bool_t create_if_not_found,
- DBusHashEntry ***bucket,
- DBusPreallocatedHash *preallocated)
+find_generic_function (DBusHashTable *table,
+ void *key,
+ unsigned int idx,
+ KeyCompareFunc compare_func,
+ dbus_bool_t create_if_not_found,
+ DBusHashEntry ***bucket,
+ DBusPreallocatedHash *preallocated)
{
DBusHashEntry *entry;
- unsigned int idx;
if (bucket)
*bucket = NULL;
-
- idx = string_hash (key) & table->mask;
/* Search all of the entries in this bucket. */
entry = table->buckets[idx];
while (entry != NULL)
{
- if (strcmp (key, entry->key) == 0)
+ if ((compare_func == NULL && key == entry->key) ||
+ (compare_func != NULL && (* compare_func) (key, entry->key) == 0))
{
if (bucket)
*bucket = &(table->buckets[idx]);
@@ -878,50 +905,75 @@ find_string_function (DBusHashTable *table,
entry = add_entry (table, idx, key, bucket, preallocated);
else if (preallocated)
_dbus_hash_table_free_preallocated_entry (table, preallocated);
-
+
return entry;
}
static DBusHashEntry*
-find_direct_function (DBusHashTable *table,
+find_string_function (DBusHashTable *table,
void *key,
dbus_bool_t create_if_not_found,
DBusHashEntry ***bucket,
DBusPreallocatedHash *preallocated)
{
- DBusHashEntry *entry;
unsigned int idx;
+
+ idx = string_hash (key) & table->mask;
- if (bucket)
- *bucket = NULL;
+ return find_generic_function (table, key, idx,
+ (KeyCompareFunc) strcmp, create_if_not_found, bucket,
+ preallocated);
+}
+
+static int
+two_strings_cmp (const char *a,
+ const char *b)
+{
+ size_t len_a;
+ size_t len_b;
+ int res;
- idx = RANDOM_INDEX (table, key) & table->mask;
+ res = strcmp (a, b);
+ if (res != 0)
+ return res;
- /* Search all of the entries in this bucket. */
- entry = table->buckets[idx];
- while (entry != NULL)
- {
- if (key == entry->key)
- {
- if (bucket)
- *bucket = &(table->buckets[idx]);
+ len_a = strlen (a);
+ len_b = strlen (b);
- if (preallocated)
- _dbus_hash_table_free_preallocated_entry (table, preallocated);
-
- return entry;
- }
-
- entry = entry->next;
- }
+ return strcmp (a + len_a + 1, b + len_b + 1);
+}
- /* Entry not found. Add a new one to the bucket. */
- if (create_if_not_found)
- entry = add_entry (table, idx, key, bucket, preallocated);
- else if (preallocated)
- _dbus_hash_table_free_preallocated_entry (table, preallocated);
+static DBusHashEntry*
+find_two_strings_function (DBusHashTable *table,
+ void *key,
+ dbus_bool_t create_if_not_found,
+ DBusHashEntry ***bucket,
+ DBusPreallocatedHash *preallocated)
+{
+ unsigned int idx;
+
+ idx = two_strings_hash (key) & table->mask;
- return entry;
+ return find_generic_function (table, key, idx,
+ (KeyCompareFunc) two_strings_cmp, create_if_not_found, bucket,
+ preallocated);
+}
+
+static DBusHashEntry*
+find_direct_function (DBusHashTable *table,
+ void *key,
+ dbus_bool_t create_if_not_found,
+ DBusHashEntry ***bucket,
+ DBusPreallocatedHash *preallocated)
+{
+ unsigned int idx;
+
+ idx = RANDOM_INDEX (table, key) & table->mask;
+
+
+ return find_generic_function (table, key, idx,
+ NULL, create_if_not_found, bucket,
+ preallocated);
}
static void
@@ -1021,6 +1073,9 @@ rebuild_table (DBusHashTable *table)
case DBUS_HASH_STRING:
idx = string_hash (entry->key) & table->mask;
break;
+ case DBUS_HASH_TWO_STRINGS:
+ idx = two_strings_hash (entry->key) & table->mask;
+ break;
case DBUS_HASH_INT:
case DBUS_HASH_ULONG:
case DBUS_HASH_POINTER:
@@ -1070,6 +1125,31 @@ _dbus_hash_table_lookup_string (DBusHashTable *table,
}
/**
+ * Looks up the value for a given string in a hash table
+ * of type #DBUS_HASH_TWO_STRINGS. Returns %NULL if the value
+ * is not present. (A not-present entry is indistinguishable
+ * from an entry with a value of %NULL.)
+ * @param table the hash table.
+ * @param key the string to look up.
+ * @returns the value of the hash entry.
+ */
+void*
+_dbus_hash_table_lookup_two_strings (DBusHashTable *table,
+ const char *key)
+{
+ DBusHashEntry *entry;
+
+ _dbus_assert (table->key_type == DBUS_HASH_TWO_STRINGS);
+
+ entry = (* table->find_function) (table, (char*) key, FALSE, NULL, NULL);
+
+ if (entry)
+ return entry->value;
+ else
+ return NULL;
+}
+
+/**
* Looks up the value for a given integer in a hash table
* of type #DBUS_HASH_INT. Returns %NULL if the value
* is not present. (A not-present entry is indistinguishable
@@ -1184,6 +1264,34 @@ _dbus_hash_table_remove_string (DBusHashTable *table,
* @returns #TRUE if the entry existed
*/
dbus_bool_t
+_dbus_hash_table_remove_two_strings (DBusHashTable *table,
+ const char *key)
+{
+ DBusHashEntry *entry;
+ DBusHashEntry **bucket;
+
+ _dbus_assert (table->key_type == DBUS_HASH_TWO_STRINGS);
+
+ entry = (* table->find_function) (table, (char*) key, FALSE, &bucket, NULL);
+
+ if (entry)
+ {
+ remove_entry (table, bucket, entry);
+ return TRUE;
+ }
+ else
+ return FALSE;
+}
+
+/**
+ * Removes the hash entry for the given key. If no hash entry
+ * for the key exists, does nothing.
+ *
+ * @param table the hash table.
+ * @param key the hash key.
+ * @returns #TRUE if the entry existed
+ */
+dbus_bool_t
_dbus_hash_table_remove_int (DBusHashTable *table,
int key)
{
@@ -1312,6 +1420,47 @@ _dbus_hash_table_insert_string (DBusHashTable *table,
* @param value the hash entry value.
*/
dbus_bool_t
+_dbus_hash_table_insert_two_strings (DBusHashTable *table,
+ char *key,
+ void *value)
+{
+ DBusHashEntry *entry;
+
+ _dbus_assert (table->key_type == DBUS_HASH_TWO_STRINGS);
+
+ entry = (* table->find_function) (table, key, TRUE, NULL, NULL);
+
+ if (entry == NULL)
+ return FALSE; /* no memory */
+
+ if (table->free_key_function && entry->key != key)
+ (* table->free_key_function) (entry->key);
+
+ if (table->free_value_function && entry->value != value)
+ (* table->free_value_function) (entry->value);
+
+ entry->key = key;
+ entry->value = value;
+
+ return TRUE;
+}
+
+/**
+ * Creates a hash entry with the given key and value.
+ * The key and value are not copied; they are stored
+ * in the hash table by reference. If an entry with the
+ * given key already exists, the previous key and value
+ * are overwritten (and freed if the hash table has
+ * a key_free_function and/or value_free_function).
+ *
+ * Returns #FALSE if memory for the new hash entry
+ * can't be allocated.
+ *
+ * @param table the hash table.
+ * @param key the hash entry key.
+ * @param value the hash entry value.
+ */
+dbus_bool_t
_dbus_hash_table_insert_int (DBusHashTable *table,
int key,
void *value)
@@ -1536,6 +1685,28 @@ count_entries (DBusHashTable *table)
return count;
}
+/* Copy the foo\0bar\0 double string thing */
+static char*
+_dbus_strdup2 (const char *str)
+{
+ size_t len;
+ char *copy;
+
+ if (str == NULL)
+ return NULL;
+
+ len = strlen (str);
+ len += strlen ((str + len + 1));
+
+ copy = dbus_malloc (len + 2);
+ if (copy == NULL)
+ return NULL;
+
+ memcpy (copy, str, len + 2);
+
+ return copy;
+}
+
/**
* @ingroup DBusHashTableInternals
* Unit test for DBusHashTable
@@ -1548,6 +1719,7 @@ _dbus_hash_test (void)
DBusHashTable *table1;
DBusHashTable *table2;
DBusHashTable *table3;
+ DBusHashTable *table4;
DBusHashIter iter;
#define N_HASH_KEYS 5000
char **keys;
@@ -1569,7 +1741,16 @@ _dbus_hash_test (void)
i = 0;
while (i < N_HASH_KEYS)
{
- sprintf (keys[i], "Hash key %d", i);
+ int len;
+
+ /* all the hash keys are TWO_STRINGS, but
+ * then we can also use those as regular strings.
+ */
+
+ len = sprintf (keys[i], "Hash key %d", i);
+ sprintf (keys[i] + len + 1, "Two string %d", i);
+ _dbus_assert (*(keys[i] + len) == '\0');
+ _dbus_assert (*(keys[i] + len + 1) != '\0');
++i;
}
printf ("... done.\n");
@@ -1588,6 +1769,12 @@ _dbus_hash_test (void)
NULL, dbus_free);
if (table3 == NULL)
goto out;
+
+ table4 = _dbus_hash_table_new (DBUS_HASH_TWO_STRINGS,
+ dbus_free, dbus_free);
+ if (table4 == NULL)
+ goto out;
+
/* Insert and remove a bunch of stuff, counting the table in between
* to be sure it's not broken and that iteration works
@@ -1624,10 +1811,22 @@ _dbus_hash_test (void)
if (!_dbus_hash_table_insert_ulong (table3,
i, value))
goto out;
+
+ key = _dbus_strdup2 (keys[i]);
+ if (key == NULL)
+ goto out;
+ value = _dbus_strdup ("Value!");
+ if (value == NULL)
+ goto out;
+
+ if (!_dbus_hash_table_insert_two_strings (table4,
+ key, value))
+ goto out;
_dbus_assert (count_entries (table1) == i + 1);
_dbus_assert (count_entries (table2) == i + 1);
_dbus_assert (count_entries (table3) == i + 1);
+ _dbus_assert (count_entries (table4) == i + 1);
value = _dbus_hash_table_lookup_string (table1, keys[i]);
_dbus_assert (value != NULL);
@@ -1640,6 +1839,10 @@ _dbus_hash_test (void)
value = _dbus_hash_table_lookup_ulong (table3, i);
_dbus_assert (value != NULL);
_dbus_assert (strcmp (value, keys[i]) == 0);
+
+ value = _dbus_hash_table_lookup_two_strings (table4, keys[i]);
+ _dbus_assert (value != NULL);
+ _dbus_assert (strcmp (value, "Value!") == 0);
++i;
}
@@ -1654,9 +1857,13 @@ _dbus_hash_test (void)
_dbus_hash_table_remove_ulong (table3, i);
+ _dbus_hash_table_remove_two_strings (table4,
+ keys[i]);
+
_dbus_assert (count_entries (table1) == i);
_dbus_assert (count_entries (table2) == i);
_dbus_assert (count_entries (table3) == i);
+ _dbus_assert (count_entries (table4) == i);
--i;
}
@@ -1664,12 +1871,15 @@ _dbus_hash_test (void)
_dbus_hash_table_ref (table1);
_dbus_hash_table_ref (table2);
_dbus_hash_table_ref (table3);
+ _dbus_hash_table_ref (table4);
_dbus_hash_table_unref (table1);
_dbus_hash_table_unref (table2);
_dbus_hash_table_unref (table3);
+ _dbus_hash_table_unref (table4);
_dbus_hash_table_unref (table1);
_dbus_hash_table_unref (table2);
_dbus_hash_table_unref (table3);
+ _dbus_hash_table_unref (table4);
table3 = NULL;
/* Insert a bunch of stuff then check
diff --git a/dbus/dbus-hash.h b/dbus/dbus-hash.h
index 566d4021..5e7515f9 100644
--- a/dbus/dbus-hash.h
+++ b/dbus/dbus-hash.h
@@ -29,17 +29,17 @@
DBUS_BEGIN_DECLS;
-/* The iterator is on the stack, but its real fields are
- * hidden privately.
+/** Hash iterator object. The iterator is on the stack, but its real
+ * fields are hidden privately.
*/
struct DBusHashIter
{
- void *dummy1;
- void *dummy2;
- void *dummy3;
- void *dummy4;
- int dummy5;
- int dummy6;
+ void *dummy1; /**< Do not use. */
+ void *dummy2; /**< Do not use. */
+ void *dummy3; /**< Do not use. */
+ void *dummy4; /**< Do not use. */
+ int dummy5; /**< Do not use. */
+ int dummy6; /**< Do not use. */
};
typedef struct DBusHashTable DBusHashTable;
@@ -52,61 +52,68 @@ typedef struct DBusHashIter DBusHashIter;
*/
typedef enum
{
- DBUS_HASH_STRING, /**< Hash keys are strings. */
- DBUS_HASH_INT, /**< Hash keys are integers. */
- DBUS_HASH_POINTER, /**< Hash keys are pointers. */
- DBUS_HASH_ULONG /**< Hash keys are unsigned long. */
+ DBUS_HASH_STRING, /**< Hash keys are strings. */
+ DBUS_HASH_TWO_STRINGS, /**< Hash key is two strings in one memory block, i.e. foo\\0bar\\0 */
+ DBUS_HASH_INT, /**< Hash keys are integers. */
+ DBUS_HASH_POINTER, /**< Hash keys are pointers. */
+ DBUS_HASH_ULONG /**< Hash keys are unsigned long. */
} DBusHashType;
-
-DBusHashTable* _dbus_hash_table_new (DBusHashType type,
- DBusFreeFunction key_free_function,
- DBusFreeFunction value_free_function);
-void _dbus_hash_table_ref (DBusHashTable *table);
-void _dbus_hash_table_unref (DBusHashTable *table);
-void _dbus_hash_iter_init (DBusHashTable *table,
- DBusHashIter *iter);
-dbus_bool_t _dbus_hash_iter_next (DBusHashIter *iter);
-void _dbus_hash_iter_remove_entry (DBusHashIter *iter);
-void* _dbus_hash_iter_get_value (DBusHashIter *iter);
-void _dbus_hash_iter_set_value (DBusHashIter *iter,
- void *value);
-int _dbus_hash_iter_get_int_key (DBusHashIter *iter);
-const char* _dbus_hash_iter_get_string_key (DBusHashIter *iter);
-unsigned long _dbus_hash_iter_get_ulong_key (DBusHashIter *iter);
-dbus_bool_t _dbus_hash_iter_lookup (DBusHashTable *table,
- void *key,
- dbus_bool_t create_if_not_found,
- DBusHashIter *iter);
-void* _dbus_hash_table_lookup_string (DBusHashTable *table,
- const char *key);
-void* _dbus_hash_table_lookup_int (DBusHashTable *table,
- int key);
-void* _dbus_hash_table_lookup_pointer (DBusHashTable *table,
- void *key);
-void* _dbus_hash_table_lookup_ulong (DBusHashTable *table,
- unsigned long key);
-dbus_bool_t _dbus_hash_table_remove_string (DBusHashTable *table,
- const char *key);
-dbus_bool_t _dbus_hash_table_remove_int (DBusHashTable *table,
- int key);
-dbus_bool_t _dbus_hash_table_remove_pointer (DBusHashTable *table,
- void *key);
-dbus_bool_t _dbus_hash_table_remove_ulong (DBusHashTable *table,
- unsigned long key);
-dbus_bool_t _dbus_hash_table_insert_string (DBusHashTable *table,
- char *key,
- void *value);
-dbus_bool_t _dbus_hash_table_insert_int (DBusHashTable *table,
- int key,
- void *value);
-dbus_bool_t _dbus_hash_table_insert_pointer (DBusHashTable *table,
- void *key,
- void *value);
-dbus_bool_t _dbus_hash_table_insert_ulong (DBusHashTable *table,
- unsigned long key,
- void *value);
-int _dbus_hash_table_get_n_entries (DBusHashTable *table);
-
+DBusHashTable* _dbus_hash_table_new (DBusHashType type,
+ DBusFreeFunction key_free_function,
+ DBusFreeFunction value_free_function);
+void _dbus_hash_table_ref (DBusHashTable *table);
+void _dbus_hash_table_unref (DBusHashTable *table);
+void _dbus_hash_iter_init (DBusHashTable *table,
+ DBusHashIter *iter);
+dbus_bool_t _dbus_hash_iter_next (DBusHashIter *iter);
+void _dbus_hash_iter_remove_entry (DBusHashIter *iter);
+void* _dbus_hash_iter_get_value (DBusHashIter *iter);
+void _dbus_hash_iter_set_value (DBusHashIter *iter,
+ void *value);
+int _dbus_hash_iter_get_int_key (DBusHashIter *iter);
+const char* _dbus_hash_iter_get_string_key (DBusHashIter *iter);
+const char* _dbus_hash_iter_get_two_strings_key (DBusHashIter *iter);
+unsigned long _dbus_hash_iter_get_ulong_key (DBusHashIter *iter);
+dbus_bool_t _dbus_hash_iter_lookup (DBusHashTable *table,
+ void *key,
+ dbus_bool_t create_if_not_found,
+ DBusHashIter *iter);
+void* _dbus_hash_table_lookup_string (DBusHashTable *table,
+ const char *key);
+void* _dbus_hash_table_lookup_two_strings (DBusHashTable *table,
+ const char *key);
+void* _dbus_hash_table_lookup_int (DBusHashTable *table,
+ int key);
+void* _dbus_hash_table_lookup_pointer (DBusHashTable *table,
+ void *key);
+void* _dbus_hash_table_lookup_ulong (DBusHashTable *table,
+ unsigned long key);
+dbus_bool_t _dbus_hash_table_remove_string (DBusHashTable *table,
+ const char *key);
+dbus_bool_t _dbus_hash_table_remove_two_strings (DBusHashTable *table,
+ const char *key);
+dbus_bool_t _dbus_hash_table_remove_int (DBusHashTable *table,
+ int key);
+dbus_bool_t _dbus_hash_table_remove_pointer (DBusHashTable *table,
+ void *key);
+dbus_bool_t _dbus_hash_table_remove_ulong (DBusHashTable *table,
+ unsigned long key);
+dbus_bool_t _dbus_hash_table_insert_string (DBusHashTable *table,
+ char *key,
+ void *value);
+dbus_bool_t _dbus_hash_table_insert_two_strings (DBusHashTable *table,
+ char *key,
+ void *value);
+dbus_bool_t _dbus_hash_table_insert_int (DBusHashTable *table,
+ int key,
+ void *value);
+dbus_bool_t _dbus_hash_table_insert_pointer (DBusHashTable *table,
+ void *key,
+ void *value);
+dbus_bool_t _dbus_hash_table_insert_ulong (DBusHashTable *table,
+ unsigned long key,
+ void *value);
+int _dbus_hash_table_get_n_entries (DBusHashTable *table);
/* Preallocation */
typedef struct DBusPreallocatedHash DBusPreallocatedHash;
diff --git a/dbus/dbus-internals.c b/dbus/dbus-internals.c
index 6e7f9e16..cf1cc391 100644
--- a/dbus/dbus-internals.c
+++ b/dbus/dbus-internals.c
@@ -248,7 +248,7 @@ _dbus_verbose_reset_real (void)
char*
_dbus_strdup (const char *str)
{
- int len;
+ size_t len;
char *copy;
if (str == NULL)
@@ -266,6 +266,29 @@ _dbus_strdup (const char *str)
}
/**
+ * Duplicates a block of memory. Returns
+ * #NULL on failure.
+ *
+ * @param mem memory to copy
+ * @param n_bytes number of bytes to copy
+ * @returns the copy
+ */
+void*
+_dbus_memdup (const void *mem,
+ size_t n_bytes)
+{
+ void *copy;
+
+ copy = dbus_malloc (n_bytes);
+ if (copy == NULL)
+ return NULL;
+
+ memcpy (copy, mem, n_bytes);
+
+ return copy;
+}
+
+/**
* Duplicates a string array. Result may be freed with
* dbus_free_string_array(). Returns #NULL if memory allocation fails.
* If the array to be duplicated is #NULL, returns #NULL.
@@ -366,7 +389,40 @@ _dbus_type_to_string (int type)
}
}
+/**
+ * Returns a string describing the given name.
+ *
+ * @param header_field the field to describe
+ * @returns a constant string describing the field
+ */
+const char *
+_dbus_header_field_to_string (int header_field)
+{
+ switch (header_field)
+ {
+ case DBUS_HEADER_FIELD_INVALID:
+ return "invalid";
+ case DBUS_HEADER_FIELD_PATH:
+ return "path";
+ case DBUS_HEADER_FIELD_INTERFACE:
+ return "interface";
+ case DBUS_HEADER_FIELD_MEMBER:
+ return "member";
+ case DBUS_HEADER_FIELD_ERROR_NAME:
+ return "error-name";
+ case DBUS_HEADER_FIELD_REPLY_SERIAL:
+ return "reply-serial";
+ case DBUS_HEADER_FIELD_SERVICE:
+ return "service";
+ case DBUS_HEADER_FIELD_SENDER_SERVICE:
+ return "sender-service";
+ default:
+ return "unknown";
+ }
+}
+
#ifndef DBUS_DISABLE_CHECKS
+/** String used in _dbus_return_if_fail macro */
const char _dbus_return_if_fail_warning_format[] =
"Arguments to %s were incorrect, assertion \"%s\" failed in file %s line %d.\n"
"This is normally a bug in some application using the D-BUS library.\n";
diff --git a/dbus/dbus-internals.h b/dbus/dbus-internals.h
index d84017d7..fa1ad19c 100644
--- a/dbus/dbus-internals.h
+++ b/dbus/dbus-internals.h
@@ -144,6 +144,8 @@ extern const char _dbus_return_if_fail_warning_format[];
((void*)_DBUS_ALIGN_VALUE(this, boundary))
char* _dbus_strdup (const char *str);
+void* _dbus_memdup (const void *mem,
+ size_t n_bytes);
dbus_bool_t _dbus_string_array_contains (const char **array,
const char *str);
char** _dbus_dup_string_array (const char **array);
@@ -182,7 +184,8 @@ void _dbus_verbose_bytes_of_string (const DBusString *str,
int len);
-const char* _dbus_type_to_string (int type);
+const char* _dbus_type_to_string (int type);
+const char* _dbus_header_field_to_string (int header_field);
extern const char _dbus_no_memory_message[];
#define _DBUS_SET_OOM(error) dbus_set_error ((error), DBUS_ERROR_NO_MEMORY, _dbus_no_memory_message)
@@ -228,10 +231,10 @@ extern int _dbus_current_generation;
_DBUS_DECLARE_GLOBAL_LOCK (list);
_DBUS_DECLARE_GLOBAL_LOCK (connection_slots);
+_DBUS_DECLARE_GLOBAL_LOCK (pending_call_slots);
_DBUS_DECLARE_GLOBAL_LOCK (server_slots);
_DBUS_DECLARE_GLOBAL_LOCK (message_slots);
_DBUS_DECLARE_GLOBAL_LOCK (atomic);
-_DBUS_DECLARE_GLOBAL_LOCK (message_handler);
_DBUS_DECLARE_GLOBAL_LOCK (bus);
_DBUS_DECLARE_GLOBAL_LOCK (shutdown_funcs);
_DBUS_DECLARE_GLOBAL_LOCK (system_users);
diff --git a/dbus/dbus-keyring.c b/dbus/dbus-keyring.c
index b5974af0..e091d801 100644
--- a/dbus/dbus-keyring.c
+++ b/dbus/dbus-keyring.c
@@ -85,6 +85,9 @@
#define MAX_KEYS_IN_FILE 256
#endif
+/**
+ * A single key from the cookie file
+ */
typedef struct
{
dbus_int32_t id; /**< identifier used to refer to the key */
diff --git a/dbus/dbus-mainloop.c b/dbus/dbus-mainloop.c
index 51eb7b0d..6da5318c 100644
--- a/dbus/dbus-mainloop.c
+++ b/dbus/dbus-mainloop.c
@@ -23,10 +23,12 @@
#include "dbus-mainloop.h"
+#ifndef DOXYGEN_SHOULD_SKIP_THIS
+
#include <dbus/dbus-list.h>
#include <dbus/dbus-sysdeps.h>
-#define MAINLOOP_SPEW 1
+#define MAINLOOP_SPEW 0
struct DBusLoop
{
@@ -876,3 +878,4 @@ _dbus_wait_for_memory (void)
_dbus_sleep_milliseconds (_dbus_get_oom_wait ());
}
+#endif /* DOXYGEN_SHOULD_SKIP_THIS */
diff --git a/dbus/dbus-mainloop.h b/dbus/dbus-mainloop.h
index ac5731f5..8a3cde13 100644
--- a/dbus/dbus-mainloop.h
+++ b/dbus/dbus-mainloop.h
@@ -24,6 +24,8 @@
#ifndef DBUS_MAINLOOP_H
#define DBUS_MAINLOOP_H
+#ifndef DOXYGEN_SHOULD_SKIP_THIS
+
#include <dbus/dbus.h>
typedef struct DBusLoop DBusLoop;
@@ -68,4 +70,7 @@ dbus_bool_t _dbus_loop_dispatch (DBusLoop *loop);
int _dbus_get_oom_wait (void);
void _dbus_wait_for_memory (void);
+#endif /* DOXYGEN_SHOULD_SKIP_THIS */
+
#endif /* DBUS_MAINLOOP_H */
+
diff --git a/dbus/dbus-marshal.c b/dbus/dbus-marshal.c
index 5d7290e3..cb989891 100644
--- a/dbus/dbus-marshal.c
+++ b/dbus/dbus-marshal.c
@@ -73,13 +73,17 @@ swap_bytes (unsigned char *data,
}
#endif /* !DBUS_HAVE_INT64 */
+/**
+ * Union used to manipulate 8 bytes as if they
+ * were various types.
+ */
typedef union
{
#ifdef DBUS_HAVE_INT64
- dbus_int64_t s;
- dbus_uint64_t u;
+ dbus_int64_t s; /**< 64-bit integer */
+ dbus_uint64_t u; /**< 64-bit unsinged integer */
#endif
- double d;
+ double d; /**< double */
} DBusOctets8;
static DBusOctets8
@@ -98,7 +102,8 @@ unpack_8_octets (int byte_order,
r.u = DBUS_UINT64_FROM_BE (*(dbus_uint64_t*)data);
#else
r.d = *(double*)data;
- swap_bytes (&r, sizeof (r));
+ if (byte_order != DBUS_COMPILER_BYTE_ORDER)
+ swap_bytes ((unsigned char*) &r, sizeof (r));
#endif
return r;
@@ -390,6 +395,10 @@ _dbus_marshal_set_uint64 (DBusString *str,
* an existing string or the wrong length will be deleted
* and replaced with the new string.
*
+ * Note: no attempt is made by this function to re-align
+ * any data which has been already marshalled after this
+ * string. Use with caution.
+ *
* @param str the string to write the marshalled string to
* @param offset the byte offset where string should be written
* @param byte_order the byte order to use
@@ -423,6 +432,30 @@ _dbus_marshal_set_string (DBusString *str,
return TRUE;
}
+/**
+ * Sets the existing marshaled object path at the given offset to a new
+ * value. The given offset must point to an existing object path or this
+ * function doesn't make sense.
+ *
+ * @todo implement this function
+ *
+ * @param str the string to write the marshalled path to
+ * @param offset the byte offset where path should be written
+ * @param byte_order the byte order to use
+ * @param path the new path
+ * @param path_len number of elements in the path
+ */
+void
+_dbus_marshal_set_object_path (DBusString *str,
+ int byte_order,
+ int offset,
+ const char **path,
+ int path_len)
+{
+
+ /* FIXME */
+}
+
static dbus_bool_t
marshal_4_octets (DBusString *str,
int byte_order,
@@ -682,7 +715,7 @@ marshal_8_octets_array (DBusString *str,
#ifdef DBUS_HAVE_INT64
*((dbus_uint64_t*)d) = DBUS_UINT64_SWAP_LE_BE (*((dbus_uint64_t*)d));
#else
- swap_bytes (d, 8);
+ swap_bytes ((unsigned char*) d, 8);
#endif
d += 8;
}
@@ -844,6 +877,58 @@ _dbus_marshal_string_array (DBusString *str,
return FALSE;
}
+/**
+ * Marshals an object path value.
+ *
+ * @param str the string to append the marshalled value to
+ * @param byte_order the byte order to use
+ * @param path the path
+ * @param path_len length of the path
+ * @returns #TRUE on success
+ */
+dbus_bool_t
+_dbus_marshal_object_path (DBusString *str,
+ int byte_order,
+ const char **path,
+ int path_len)
+{
+ int array_start, old_string_len;
+ int i;
+
+ old_string_len = _dbus_string_get_length (str);
+
+ /* Set the length to 0 temporarily */
+ if (!_dbus_marshal_uint32 (str, byte_order, 0))
+ goto nomem;
+
+ array_start = _dbus_string_get_length (str);
+
+ i = 0;
+ while (i < path_len)
+ {
+ if (!_dbus_string_append_byte (str, '/'))
+ goto nomem;
+
+ if (!_dbus_string_append (str, path[0]))
+ goto nomem;
+
+ ++i;
+ }
+
+ /* Write the length now that we know it */
+ _dbus_marshal_set_uint32 (str, byte_order,
+ _DBUS_ALIGN_VALUE (old_string_len, sizeof(dbus_uint32_t)),
+ _dbus_string_get_length (str) - array_start);
+
+ return TRUE;
+
+ nomem:
+ /* Restore the previous length */
+ _dbus_string_set_length (str, old_string_len);
+
+ return FALSE;
+}
+
static dbus_uint32_t
demarshal_4_octets (const DBusString *str,
int byte_order,
@@ -1174,7 +1259,7 @@ demarshal_8_octets_array (const DBusString *str,
#ifdef DBUS_HAVE_INT64
retval[i].u = DBUS_UINT64_SWAP_LE_BE (retval[i].u);
#else
- swap_bytes (&retval[i], 8);
+ swap_bytes ((unsigned char *) &retval[i], 8);
#endif
}
}
@@ -1393,6 +1478,105 @@ _dbus_demarshal_string_array (const DBusString *str,
return FALSE;
}
+/** Set to 1 to get a bunch of spew about disassembling the path string */
+#define VERBOSE_DECOMPOSE 0
+
+/**
+ * Demarshals an object path. A path of just "/" is
+ * represented as an empty vector of strings.
+ *
+ * @param str the string containing the data
+ * @param byte_order the byte order
+ * @param pos the position in the string
+ * @param new_pos the new position of the string
+ * @param path address to store new object path
+ * @param path_len length of stored path
+ */
+dbus_bool_t
+_dbus_demarshal_object_path (const DBusString *str,
+ int byte_order,
+ int pos,
+ int *new_pos,
+ char ***path,
+ int *path_len)
+{
+ int len;
+ char **retval;
+ const char *data;
+ int n_components;
+ int i, j, comp;
+
+ len = _dbus_demarshal_uint32 (str, byte_order, pos, &pos);
+ data = _dbus_string_get_const_data_len (str, pos, len + 1);
+ _dbus_assert (data != NULL);
+
+#if VERBOSE_DECOMPOSE
+ _dbus_verbose ("Decomposing path \"%s\"\n",
+ data);
+#endif
+
+ n_components = 0;
+ i = 0;
+ while (i < len)
+ {
+ if (data[i] == '/')
+ n_components += 1;
+ ++i;
+ }
+
+ retval = dbus_new0 (char*, n_components + 1);
+
+ if (retval == NULL)
+ return FALSE;
+
+ comp = 0;
+ i = 0;
+ while (i < len)
+ {
+ if (data[i] == '/')
+ ++i;
+ j = i;
+
+ while (j < len && data[j] != '/')
+ ++j;
+
+ /* Now [i, j) is the path component */
+ _dbus_assert (i < j);
+ _dbus_assert (data[i] != '/');
+ _dbus_assert (j == len || data[j] == '/');
+
+#if VERBOSE_DECOMPOSE
+ _dbus_verbose (" (component in [%d,%d))\n",
+ i, j);
+#endif
+
+ retval[comp] = _dbus_memdup (&data[i], j - i + 1);
+ if (retval[comp] == NULL)
+ {
+ dbus_free_string_array (retval);
+ return FALSE;
+ }
+ retval[comp][j-i] = '\0';
+#if VERBOSE_DECOMPOSE
+ _dbus_verbose (" (component %d = \"%s\")\n",
+ comp, retval[comp]);
+#endif
+
+ ++comp;
+ i = j;
+ }
+ _dbus_assert (i == len);
+
+ *path = retval;
+ if (path_len)
+ *path_len = n_components;
+
+ if (new_pos)
+ *new_pos = pos + len + 1;
+
+ return TRUE;
+}
+
/**
* Returns the position right after the end of an argument. PERFORMS
* NO VALIDATION WHATSOEVER. The message must have been previously
@@ -1435,32 +1619,18 @@ _dbus_marshal_get_arg_end_pos (const DBusString *str,
break;
case DBUS_TYPE_INT32:
- *end_pos = _DBUS_ALIGN_VALUE (pos, sizeof (dbus_int32_t)) + sizeof (dbus_int32_t);
-
- break;
-
case DBUS_TYPE_UINT32:
- *end_pos = _DBUS_ALIGN_VALUE (pos, sizeof (dbus_uint32_t)) + sizeof (dbus_uint32_t);
-
+ *end_pos = _DBUS_ALIGN_VALUE (pos, 4) + 4;
break;
-#ifdef DBUS_HAVE_INT64
case DBUS_TYPE_INT64:
- *end_pos = _DBUS_ALIGN_VALUE (pos, sizeof (dbus_int64_t)) + sizeof (dbus_int64_t);
-
- break;
-
case DBUS_TYPE_UINT64:
- *end_pos = _DBUS_ALIGN_VALUE (pos, sizeof (dbus_uint64_t)) + sizeof (dbus_uint64_t);
-
- break;
-#endif /* DBUS_HAVE_INT64 */
-
case DBUS_TYPE_DOUBLE:
- *end_pos = _DBUS_ALIGN_VALUE (pos, sizeof (double)) + sizeof (double);
-
+
+ *end_pos = _DBUS_ALIGN_VALUE (pos, 8) + 8;
break;
+ case DBUS_TYPE_OBJECT_PATH:
case DBUS_TYPE_STRING:
{
int len;
@@ -1664,6 +1834,7 @@ validate_array_data (const DBusString *str,
case DBUS_TYPE_NIL:
break;
+ case DBUS_TYPE_OBJECT_PATH:
case DBUS_TYPE_STRING:
case DBUS_TYPE_NAMED:
case DBUS_TYPE_ARRAY:
@@ -1744,10 +1915,6 @@ validate_array_data (const DBusString *str,
* returns #TRUE if a valid arg begins at "pos"
*
* @todo security: need to audit this function.
- *
- * @todo For array types that can't be invalid, we should not
- * walk the whole array validating it. e.g. just skip all the
- * int values in an int array.
*
* @param str a string
* @param byte_order the byte order to use
@@ -1842,21 +2009,7 @@ _dbus_marshal_validate_arg (const DBusString *str,
break;
case DBUS_TYPE_INT64:
- case DBUS_TYPE_UINT64:
- {
- int align_8 = _DBUS_ALIGN_VALUE (pos, 8);
-
- if (!_dbus_string_validate_nul (str, pos,
- align_8 - pos))
- {
- _dbus_verbose ("int64/uint64 alignment padding not initialized to nul\n");
- return FALSE;
- }
-
- *end_pos = align_8 + 8;
- }
- break;
-
+ case DBUS_TYPE_UINT64:
case DBUS_TYPE_DOUBLE:
{
int align_8 = _DBUS_ALIGN_VALUE (pos, 8);
@@ -1866,7 +2019,7 @@ _dbus_marshal_validate_arg (const DBusString *str,
if (!_dbus_string_validate_nul (str, pos,
align_8 - pos))
{
- _dbus_verbose ("double alignment padding not initialized to nul\n");
+ _dbus_verbose ("double/int64/uint64/objid alignment padding not initialized to nul\n");
return FALSE;
}
@@ -1874,6 +2027,7 @@ _dbus_marshal_validate_arg (const DBusString *str,
}
break;
+ case DBUS_TYPE_OBJECT_PATH:
case DBUS_TYPE_STRING:
{
int len;
@@ -1887,6 +2041,12 @@ _dbus_marshal_validate_arg (const DBusString *str,
if (!validate_string (str, pos, len, end_pos))
return FALSE;
+
+ if (type == DBUS_TYPE_OBJECT_PATH)
+ {
+ if (!_dbus_string_validate_path (str, pos, len))
+ return FALSE;
+ }
}
break;
@@ -2478,7 +2638,6 @@ _dbus_marshal_test (void)
s = _dbus_demarshal_string (&str, DBUS_BIG_ENDIAN, 0, NULL);
_dbus_assert (strcmp (s, "Hello") == 0);
dbus_free (s);
-
_dbus_string_free (&str);
diff --git a/dbus/dbus-marshal.h b/dbus/dbus-marshal.h
index 1eff8995..27ded007 100644
--- a/dbus/dbus-marshal.h
+++ b/dbus/dbus-marshal.h
@@ -152,11 +152,17 @@ void _dbus_marshal_set_uint64 (DBusString *str,
int offset,
dbus_uint64_t value);
#endif /* DBUS_HAVE_INT64 */
-dbus_bool_t _dbus_marshal_set_string (DBusString *str,
- int byte_order,
- int offset,
- const DBusString *value,
- int len);
+
+dbus_bool_t _dbus_marshal_set_string (DBusString *str,
+ int byte_order,
+ int offset,
+ const DBusString *value,
+ int len);
+void _dbus_marshal_set_object_path (DBusString *str,
+ int byte_order,
+ int offset,
+ const char **path,
+ int path_len);
dbus_bool_t _dbus_marshal_int32 (DBusString *str,
int byte_order,
@@ -208,6 +214,11 @@ dbus_bool_t _dbus_marshal_string_array (DBusString *str,
int byte_order,
const char **value,
int len);
+dbus_bool_t _dbus_marshal_object_path (DBusString *str,
+ int byte_order,
+ const char **path,
+ int path_len);
+
double _dbus_demarshal_double (const DBusString *str,
int byte_order,
int pos,
@@ -278,9 +289,12 @@ dbus_bool_t _dbus_demarshal_string_array (const DBusString *str,
int *new_pos,
char ***array,
int *array_len);
-
-
-
+dbus_bool_t _dbus_demarshal_object_path (const DBusString *str,
+ int byte_order,
+ int pos,
+ int *new_pos,
+ char ***path,
+ int *path_len);
dbus_bool_t _dbus_marshal_get_arg_end_pos (const DBusString *str,
int byte_order,
diff --git a/dbus/dbus-md5.h b/dbus/dbus-md5.h
index 63fc62e8..e31711dd 100644
--- a/dbus/dbus-md5.h
+++ b/dbus/dbus-md5.h
@@ -31,11 +31,14 @@ DBUS_BEGIN_DECLS;
typedef struct DBusMD5Context DBusMD5Context;
+/**
+ * A context used to store the state of the MD5 algorithm
+ */
struct DBusMD5Context
{
- dbus_uint32_t count[2]; /* message length in bits, lsw first */
- dbus_uint32_t abcd[4]; /* digest buffer */
- unsigned char buf[64]; /* accumulate block */
+ dbus_uint32_t count[2]; /**< message length in bits, lsw first */
+ dbus_uint32_t abcd[4]; /**< digest buffer */
+ unsigned char buf[64]; /**< accumulate block */
};
void _dbus_md5_init (DBusMD5Context *context);
diff --git a/dbus/dbus-message-builder.c b/dbus/dbus-message-builder.c
index 390dda75..c9dc8ca5 100644
--- a/dbus/dbus-message-builder.c
+++ b/dbus/dbus-message-builder.c
@@ -40,9 +40,12 @@
* @{
*/
+/**
+ * Saved length
+ */
typedef struct
{
- DBusString name;
+ DBusString name; /**< Name of the length */
int start; /**< Calculate length since here */
int length; /**< length to write */
int offset; /**< where to write it into the data */
@@ -265,6 +268,73 @@ append_saved_length (DBusString *dest,
return TRUE;
}
+static int
+message_type_from_string (const DBusString *str,
+ int start)
+{
+ const char *s;
+
+ s = _dbus_string_get_const_data_len (str, start,
+ _dbus_string_get_length (str) - start);
+
+ if (strncmp (s, "method_call", strlen ("method_call")) == 0)
+ return DBUS_MESSAGE_TYPE_METHOD_CALL;
+ else if (strncmp (s, "method_return", strlen ("method_return")) == 0)
+ return DBUS_MESSAGE_TYPE_METHOD_RETURN;
+ else if (strncmp (s, "signal", strlen ("signal")) == 0)
+ return DBUS_MESSAGE_TYPE_SIGNAL;
+ else if (strncmp (s, "error", strlen ("error")) == 0)
+ return DBUS_MESSAGE_TYPE_ERROR;
+ else if (strncmp (s, "invalid", strlen ("invalid")) == 0)
+ return DBUS_MESSAGE_TYPE_INVALID;
+ else
+ return -1;
+}
+
+static dbus_bool_t
+append_string_field (DBusString *dest,
+ int endian,
+ int field,
+ int type,
+ const char *value)
+{
+ int len;
+
+ if (!_dbus_string_append_byte (dest, field))
+ {
+ _dbus_warn ("couldn't append field name byte\n");
+ return FALSE;
+ }
+
+ if (!_dbus_string_append_byte (dest, type))
+ {
+ _dbus_warn ("could not append typecode byte\n");
+ return FALSE;
+ }
+
+ len = strlen (value);
+
+ if (!_dbus_marshal_uint32 (dest, endian, len))
+ {
+ _dbus_warn ("couldn't append string length\n");
+ return FALSE;
+ }
+
+ if (!_dbus_string_append (dest, value))
+ {
+ _dbus_warn ("couldn't append field value\n");
+ return FALSE;
+ }
+
+ if (!_dbus_string_append_byte (dest, 0))
+ {
+ _dbus_warn ("couldn't append string nul term\n");
+ return FALSE;
+ }
+
+ return TRUE;
+}
+
/**
* Reads the given filename, which should be in "message description
* language" (look at some examples), and builds up the message data
@@ -274,7 +344,8 @@ append_saved_length (DBusString *dest,
*
* The file format is:
* @code
- * VALID_HEADER normal header; byte order, padding, header len, body len, serial
+ * VALID_HEADER <type> normal header; byte order, type, padding, header len, body len, serial
+ * REQUIRED_FIELDS add required fields with placeholder values
* BIG_ENDIAN switch to big endian
* LITTLE_ENDIAN switch to little endian
* OPPOSITE_ENDIAN switch to opposite endian
@@ -286,7 +357,7 @@ append_saved_length (DBusString *dest,
* (or if no START_LENGTH, absolute length)
* LENGTH <name> inserts the saved length of the same name
* CHOP <N> chops last N bytes off the data
- * FIELD_NAME <abcd> inserts 4-byte field name
+ * HEADER_FIELD <fieldname> inserts a header field name byte
* TYPE <typename> inserts a typecode byte
* @endcode
*
@@ -299,6 +370,7 @@ append_saved_length (DBusString *dest,
* UINT64 <N> marshals a UINT64
* DOUBLE <N> marshals a double
* STRING 'Foo' marshals a string
+ * OBJECT_PATH '/foo/bar' marshals an object path
* BYTE_ARRAY { 'a', 3, 4, 5, 6} marshals a BYTE array
* BOOLEAN_ARRAY { false, true, false} marshals a BOOLEAN array
* INT32_ARRAY { 3, 4, 5, 6} marshals an INT32 array
@@ -386,6 +458,13 @@ _dbus_message_data_load (DBusString *dest,
{
int i;
DBusString name;
+ int message_type;
+
+ if (_dbus_string_get_length (&line) < (int) strlen ("VALID_HEADER "))
+ {
+ _dbus_warn ("no args to VALID_HEADER\n");
+ goto parse_failed;
+ }
if (!_dbus_string_append_byte (dest, endian))
{
@@ -393,8 +472,22 @@ _dbus_message_data_load (DBusString *dest,
goto parse_failed;
}
+ message_type = message_type_from_string (&line,
+ strlen ("VALID_HEADER "));
+ if (message_type < 0)
+ {
+ _dbus_warn ("VALID_HEADER not followed by space then known message type\n");
+ goto parse_failed;
+ }
+
+ if (!_dbus_string_append_byte (dest, message_type))
+ {
+ _dbus_warn ("could not append message type\n");
+ goto parse_failed;
+ }
+
i = 0;
- while (i < 3)
+ while (i < 2)
{
if (!_dbus_string_append_byte (dest, '\0'))
{
@@ -424,6 +517,25 @@ _dbus_message_data_load (DBusString *dest,
}
}
else if (_dbus_string_starts_with_c_str (&line,
+ "REQUIRED_FIELDS"))
+ {
+ if (!append_string_field (dest, endian,
+ DBUS_HEADER_FIELD_INTERFACE,
+ DBUS_TYPE_STRING,
+ "org.freedesktop.BlahBlahInterface"))
+ goto parse_failed;
+ if (!append_string_field (dest, endian,
+ DBUS_HEADER_FIELD_MEMBER,
+ DBUS_TYPE_STRING,
+ "BlahBlahMethod"))
+ goto parse_failed;
+ if (!append_string_field (dest, endian,
+ DBUS_HEADER_FIELD_PATH,
+ DBUS_TYPE_OBJECT_PATH,
+ "/blah/blah/path"))
+ goto parse_failed;
+ }
+ else if (_dbus_string_starts_with_c_str (&line,
"BIG_ENDIAN"))
{
endian = DBUS_BIG_ENDIAN;
@@ -561,25 +673,42 @@ _dbus_message_data_load (DBusString *dest,
PERFORM_UNALIGN (dest);
}
else if (_dbus_string_starts_with_c_str (&line,
- "FIELD_NAME"))
+ "HEADER_FIELD"))
{
+ int field;
+
_dbus_string_delete_first_word (&line);
- if (_dbus_string_get_length (&line) != 4)
+ if (_dbus_string_starts_with_c_str (&line, "INVALID"))
+ field = DBUS_HEADER_FIELD_INVALID;
+ else if (_dbus_string_starts_with_c_str (&line, "PATH"))
+ field = DBUS_HEADER_FIELD_PATH;
+ else if (_dbus_string_starts_with_c_str (&line, "INTERFACE"))
+ field = DBUS_HEADER_FIELD_INTERFACE;
+ else if (_dbus_string_starts_with_c_str (&line, "MEMBER"))
+ field = DBUS_HEADER_FIELD_MEMBER;
+ else if (_dbus_string_starts_with_c_str (&line, "ERROR_NAME"))
+ field = DBUS_HEADER_FIELD_ERROR_NAME;
+ else if (_dbus_string_starts_with_c_str (&line, "REPLY_SERIAL"))
+ field = DBUS_HEADER_FIELD_REPLY_SERIAL;
+ else if (_dbus_string_starts_with_c_str (&line, "SERVICE"))
+ field = DBUS_HEADER_FIELD_SERVICE;
+ else if (_dbus_string_starts_with_c_str (&line, "SENDER_SERVICE"))
+ field = DBUS_HEADER_FIELD_SENDER_SERVICE;
+ else if (_dbus_string_starts_with_c_str (&line, "UNKNOWN"))
+ field = 22; /* random unknown header field */
+ else
{
- _dbus_warn ("Field name must be four characters not \"%s\"\n",
- _dbus_string_get_const_data (&line));
+ _dbus_warn ("%s is not a valid header field name\n",
+ _dbus_string_get_const_data (&line));
goto parse_failed;
}
- if (unalign)
- unalign = FALSE;
- else
- _dbus_string_align_length (dest, 4);
-
- if (!_dbus_string_copy (&line, 0, dest,
- _dbus_string_get_length (dest)))
- goto parse_failed;
+ if (!_dbus_string_append_byte (dest, field))
+ {
+ _dbus_warn ("could not append header field name byte\n");
+ goto parse_failed;
+ }
}
else if (_dbus_string_starts_with_c_str (&line,
"TYPE"))
@@ -604,6 +733,8 @@ _dbus_message_data_load (DBusString *dest,
code = DBUS_TYPE_DOUBLE;
else if (_dbus_string_starts_with_c_str (&line, "STRING"))
code = DBUS_TYPE_STRING;
+ else if (_dbus_string_starts_with_c_str (&line, "OBJECT_PATH"))
+ code = DBUS_TYPE_OBJECT_PATH;
else if (_dbus_string_starts_with_c_str (&line, "NAMED"))
code = DBUS_TYPE_NAMED;
else if (_dbus_string_starts_with_c_str (&line, "ARRAY"))
@@ -1226,6 +1357,36 @@ _dbus_message_data_load (DBusString *dest,
PERFORM_UNALIGN (dest);
}
+ else if (_dbus_string_starts_with_c_str (&line,
+ "OBJECT_PATH"))
+ {
+ SAVE_FOR_UNALIGN (dest, 4);
+ int size_offset;
+ int old_len;
+
+ _dbus_string_delete_first_word (&line);
+
+ size_offset = _dbus_string_get_length (dest);
+ size_offset = _DBUS_ALIGN_VALUE (size_offset, 4);
+ if (!_dbus_marshal_uint32 (dest, endian, 0))
+ {
+ _dbus_warn ("Failed to append string size\n");
+ goto parse_failed;
+ }
+
+ old_len = _dbus_string_get_length (dest);
+ if (!append_quoted_string (dest, &line, 0, NULL))
+ {
+ _dbus_warn ("Failed to append quoted string\n");
+ goto parse_failed;
+ }
+
+ _dbus_marshal_set_uint32 (dest, endian, size_offset,
+ /* subtract 1 for nul */
+ _dbus_string_get_length (dest) - old_len - 1);
+
+ PERFORM_UNALIGN (dest);
+ }
else
goto parse_failed;
diff --git a/dbus/dbus-message.c b/dbus/dbus-message.c
index e74191f8..19457468 100644
--- a/dbus/dbus-message.c
+++ b/dbus/dbus-message.c
@@ -42,37 +42,31 @@
* @{
*/
-enum
-{
- FIELD_HEADER_LENGTH,
- FIELD_BODY_LENGTH,
- FIELD_CLIENT_SERIAL,
- FIELD_NAME,
- FIELD_SERVICE,
- FIELD_SENDER,
- FIELD_REPLY_SERIAL,
-
- FIELD_LAST
-};
-
-static dbus_bool_t field_is_named[FIELD_LAST] =
-{
- FALSE, /* FIELD_HEADER_LENGTH */
- FALSE, /* FIELD_BODY_LENGTH */
- FALSE, /* FIELD_CLIENT_SERIAL */
- TRUE, /* FIELD_NAME */
- TRUE, /* FIELD_SERVICE */
- TRUE, /* FIELD_SENDER */
- TRUE /* FIELD_REPLY_SERIAL */
-};
-
+/**
+ * Cached information about a header field in the message
+ */
typedef struct
{
- int offset; /**< Offset to start of field (location of name of field
- * for named fields)
- */
+ int name_offset; /**< Offset to name of field */
+ int value_offset; /**< Offset to value of field */
} HeaderField;
+/** Offset to byte order from start of header */
+#define BYTE_ORDER_OFFSET 0
+/** Offset to type from start of header */
+#define TYPE_OFFSET 1
+/** Offset to flags from start of header */
+#define FLAGS_OFFSET 2
+/** Offset to version from start of header */
+#define VERSION_OFFSET 3
+/** Offset to header length from start of header */
+#define HEADER_LENGTH_OFFSET 4
+/** Offset to body length from start of header */
+#define BODY_LENGTH_OFFSET 8
+/** Offset to client serial from start of header */
+#define CLIENT_SERIAL_OFFSET 12
+
+
/**
* @brief Internals of DBusMessage
*
@@ -89,9 +83,9 @@ struct DBusMessage
* independently realloc it.
*/
- HeaderField header_fields[FIELD_LAST]; /**< Track the location
- * of each field in "header"
- */
+ HeaderField header_fields[DBUS_HEADER_FIELD_LAST + 1]; /**< Track the location
+ * of each field in "header"
+ */
dbus_uint32_t client_serial; /**< Cached client serial value for speed */
dbus_uint32_t reply_serial; /**< Cached reply serial value for speed */
@@ -188,26 +182,6 @@ append_header_padding (DBusMessage *message)
return TRUE;
}
-static void
-adjust_field_offsets (DBusMessage *message,
- int offsets_after,
- int delta)
-{
- int i;
-
- if (delta == 0)
- return;
-
- i = 0;
- while (i < FIELD_LAST)
- {
- if (message->header_fields[i].offset > offsets_after)
- message->header_fields[i].offset += delta;
-
- ++i;
- }
-}
-
#ifdef DBUS_BUILD_TESTS
/* tests-only until it's actually used */
static dbus_int32_t
@@ -216,9 +190,9 @@ get_int_field (DBusMessage *message,
{
int offset;
- _dbus_assert (field < FIELD_LAST);
+ _dbus_assert (field <= DBUS_HEADER_FIELD_LAST);
- offset = message->header_fields[field].offset;
+ offset = message->header_fields[field].value_offset;
if (offset < 0)
return -1; /* useless if -1 is a valid value of course */
@@ -236,9 +210,9 @@ get_uint_field (DBusMessage *message,
{
int offset;
- _dbus_assert (field < FIELD_LAST);
+ _dbus_assert (field <= DBUS_HEADER_FIELD_LAST);
- offset = message->header_fields[field].offset;
+ offset = message->header_fields[field].value_offset;
if (offset < 0)
return -1; /* useless if -1 is a valid value of course */
@@ -257,9 +231,9 @@ get_string_field (DBusMessage *message,
int offset;
const char *data;
- offset = message->header_fields[field].offset;
+ offset = message->header_fields[field].value_offset;
- _dbus_assert (field < FIELD_LAST);
+ _dbus_assert (field <= DBUS_HEADER_FIELD_LAST);
if (offset < 0)
return NULL;
@@ -280,25 +254,45 @@ get_string_field (DBusMessage *message,
return data + (offset + 4);
}
+/* returns FALSE if no memory, TRUE with NULL path if no field */
+static dbus_bool_t
+get_path_field_decomposed (DBusMessage *message,
+ int field,
+ char ***path)
+{
+ int offset;
+
+ offset = message->header_fields[field].value_offset;
+
+ _dbus_assert (field <= DBUS_HEADER_FIELD_LAST);
+
+ if (offset < 0)
+ {
+ *path = NULL;
+ return TRUE;
+ }
+
+ return _dbus_demarshal_object_path (&message->header,
+ message->byte_order,
+ offset,
+ NULL,
+ path, NULL);
+}
+
#ifdef DBUS_BUILD_TESTS
static dbus_bool_t
append_int_field (DBusMessage *message,
int field,
- const char *name,
int value)
{
- int orig_len;
-
_dbus_assert (!message->locked);
clear_header_padding (message);
- orig_len = _dbus_string_get_length (&message->header);
-
- if (!_dbus_string_align_length (&message->header, 4))
- goto failed;
+ message->header_fields[field].name_offset =
+ _dbus_string_get_length (&message->header);
- if (!_dbus_string_append_len (&message->header, name, 4))
+ if (!_dbus_string_append_byte (&message->header, field))
goto failed;
if (!_dbus_string_append_byte (&message->header, DBUS_TYPE_INT32))
@@ -307,7 +301,7 @@ append_int_field (DBusMessage *message,
if (!_dbus_string_align_length (&message->header, 4))
goto failed;
- message->header_fields[field].offset =
+ message->header_fields[field].value_offset =
_dbus_string_get_length (&message->header);
if (!_dbus_marshal_int32 (&message->header, message->byte_order,
@@ -320,8 +314,10 @@ append_int_field (DBusMessage *message,
return TRUE;
failed:
- message->header_fields[field].offset = -1;
- _dbus_string_set_length (&message->header, orig_len);
+ _dbus_string_set_length (&message->header,
+ message->header_fields[field].name_offset);
+ message->header_fields[field].name_offset = -1;
+ message->header_fields[field].value_offset = -1;
/* this must succeed because it was allocated on function entry and
* DBusString doesn't ever realloc smaller
@@ -335,21 +331,16 @@ append_int_field (DBusMessage *message,
static dbus_bool_t
append_uint_field (DBusMessage *message,
int field,
- const char *name,
- int value)
+ int value)
{
- int orig_len;
-
_dbus_assert (!message->locked);
clear_header_padding (message);
- orig_len = _dbus_string_get_length (&message->header);
-
- if (!_dbus_string_align_length (&message->header, 4))
- goto failed;
+ message->header_fields[field].name_offset =
+ _dbus_string_get_length (&message->header);
- if (!_dbus_string_append_len (&message->header, name, 4))
+ if (!_dbus_string_append_byte (&message->header, field))
goto failed;
if (!_dbus_string_append_byte (&message->header, DBUS_TYPE_UINT32))
@@ -358,7 +349,7 @@ append_uint_field (DBusMessage *message,
if (!_dbus_string_align_length (&message->header, 4))
goto failed;
- message->header_fields[field].offset =
+ message->header_fields[field].value_offset =
_dbus_string_get_length (&message->header);
if (!_dbus_marshal_uint32 (&message->header, message->byte_order,
@@ -371,8 +362,10 @@ append_uint_field (DBusMessage *message,
return TRUE;
failed:
- message->header_fields[field].offset = -1;
- _dbus_string_set_length (&message->header, orig_len);
+ _dbus_string_set_length (&message->header,
+ message->header_fields[field].name_offset);
+ message->header_fields[field].name_offset = -1;
+ message->header_fields[field].value_offset = -1;
/* this must succeed because it was allocated on function entry and
* DBusString doesn't ever realloc smaller
@@ -385,30 +378,26 @@ append_uint_field (DBusMessage *message,
static dbus_bool_t
append_string_field (DBusMessage *message,
int field,
- const char *name,
+ int type,
const char *value)
{
- int orig_len;
-
_dbus_assert (!message->locked);
clear_header_padding (message);
- orig_len = _dbus_string_get_length (&message->header);
-
- if (!_dbus_string_align_length (&message->header, 4))
- goto failed;
+ message->header_fields[field].name_offset =
+ _dbus_string_get_length (&message->header);
- if (!_dbus_string_append_len (&message->header, name, 4))
+ if (!_dbus_string_append_byte (&message->header, field))
goto failed;
- if (!_dbus_string_append_byte (&message->header, DBUS_TYPE_STRING))
+ if (!_dbus_string_append_byte (&message->header, type))
goto failed;
if (!_dbus_string_align_length (&message->header, 4))
goto failed;
- message->header_fields[field].offset =
+ message->header_fields[field].value_offset =
_dbus_string_get_length (&message->header);
if (!_dbus_marshal_string (&message->header, message->byte_order,
@@ -421,8 +410,10 @@ append_string_field (DBusMessage *message,
return TRUE;
failed:
- message->header_fields[field].offset = -1;
- _dbus_string_set_length (&message->header, orig_len);
+ _dbus_string_set_length (&message->header,
+ message->header_fields[field].name_offset);
+ message->header_fields[field].name_offset = -1;
+ message->header_fields[field].value_offset = -1;
/* this must succeed because it was allocated on function entry and
* DBusString doesn't ever realloc smaller
@@ -433,73 +424,205 @@ append_string_field (DBusMessage *message,
return FALSE;
}
-#ifdef DBUS_BUILD_TESTS
-/* This isn't used, but building it when tests are enabled just to
- * keep it compiling if we need it in future
- */
-static void
-delete_int_or_uint_field (DBusMessage *message,
- int field)
+static int
+get_next_field (DBusMessage *message,
+ int field)
{
- int offset = message->header_fields[field].offset;
+ int offset = message->header_fields[field].name_offset;
+ int closest;
+ int i;
+ int retval = DBUS_HEADER_FIELD_INVALID;
- _dbus_assert (!message->locked);
- _dbus_assert (field_is_named[field]);
-
- if (offset < 0)
- return;
+ i = 0;
+ closest = _DBUS_INT_MAX;
+ while (i < DBUS_HEADER_FIELD_LAST)
+ {
+ if (message->header_fields[i].name_offset > offset &&
+ message->header_fields[i].name_offset < closest)
+ {
+ closest = message->header_fields[i].name_offset;
+ retval = i;
+ }
+ ++i;
+ }
- clear_header_padding (message);
-
- /* The field typecode and name take up 8 bytes */
- _dbus_string_delete (&message->header,
- offset - 8,
- 12);
+ return retval;
+}
- message->header_fields[field].offset = -1;
-
- adjust_field_offsets (message,
- offset - 8,
- - 12);
+static dbus_bool_t
+re_align_field_recurse (DBusMessage *message,
+ int field,
+ int offset)
+{
+ int old_name_offset = message->header_fields[field].name_offset;
+ int old_value_offset = message->header_fields[field].value_offset;
+ int prev_padding, padding, delta;
+ int type;
+ int next_field;
+ int pos = offset;
+
+ /* padding between the typecode byte and the value itself */
+ prev_padding = old_value_offset - old_name_offset + 2;
+
+ pos++;
+ type = _dbus_string_get_byte (&message->header, pos);
+
+ pos++;
+ switch (type)
+ {
+ case DBUS_TYPE_NIL:
+ case DBUS_TYPE_BYTE:
+ case DBUS_TYPE_BOOLEAN:
+ padding = 0;
+ break;
+ case DBUS_TYPE_INT32:
+ case DBUS_TYPE_UINT32:
+ case DBUS_TYPE_STRING:
+ case DBUS_TYPE_OBJECT_PATH:
+ padding = _DBUS_ALIGN_VALUE (pos, 4) - pos;
+ break;
+ case DBUS_TYPE_INT64:
+ case DBUS_TYPE_UINT64:
+ case DBUS_TYPE_DOUBLE:
+ padding = _DBUS_ALIGN_VALUE (pos, 8) - pos;
+ break;
+ case DBUS_TYPE_NAMED:
+ case DBUS_TYPE_ARRAY:
+ case DBUS_TYPE_DICT:
+ _dbus_assert_not_reached ("no defined header fields may contain a named, array or dict value");
+ break;
+ case DBUS_TYPE_INVALID:
+ default:
+ _dbus_assert_not_reached ("invalid type in marshalled header");
+ break;
+ }
+
+ delta = padding - prev_padding;
+ if (delta > 0)
+ {
+ if (!_dbus_string_insert_bytes (&message->header, pos, delta, 0))
+ return FALSE;
+ }
+ else if (delta < 0)
+ {
+ _dbus_string_delete (&message->header, pos, -delta);
+ }
+
+ next_field = get_next_field (message, field);
+ if (next_field != DBUS_HEADER_FIELD_INVALID)
+ {
+ int next_offset = message->header_fields[next_field].name_offset;
+
+ _dbus_assert (next_offset > 0);
+
+ if (!re_align_field_recurse (message, field,
+ pos + padding + (next_offset - old_value_offset)))
+ goto failed;
+ }
+ else
+ {
+ if (!append_header_padding (message))
+ goto failed;
+ }
- append_header_padding (message);
+ message->header_fields[field].name_offset = offset;
+ message->header_fields[field].value_offset = pos + padding;
+
+ return TRUE;
+
+ failed:
+ if (delta > 0)
+ {
+ _dbus_string_delete (&message->header, pos, delta);
+ }
+ else if (delta < 0)
+ {
+ /* this must succeed because it was allocated on function entry and
+ * DBusString doesn't ever realloc smaller
+ */
+ _dbus_string_insert_bytes (&message->header, pos, -delta, 0);
+ }
+
+ return FALSE;
}
-#endif
-static void
-delete_string_field (DBusMessage *message,
- int field)
+static dbus_bool_t
+delete_field (DBusMessage *message,
+ int field)
{
- int offset = message->header_fields[field].offset;
- int len;
- int delete_len;
-
+ int offset = message->header_fields[field].name_offset;
+ int next_field;
+
_dbus_assert (!message->locked);
- _dbus_assert (field_is_named[field]);
if (offset < 0)
- return;
+ return FALSE;
clear_header_padding (message);
-
- get_string_field (message, field, &len);
-
- /* The field typecode and name take up 8 bytes, and the nul
- * termination is 1 bytes, string length integer is 4 bytes
- */
- delete_len = 8 + 4 + 1 + len;
-
- _dbus_string_delete (&message->header,
- offset - 8,
- delete_len);
- message->header_fields[field].offset = -1;
-
- adjust_field_offsets (message,
- offset - 8,
- - delete_len);
+ next_field = get_next_field (message, field);
+ if (next_field == DBUS_HEADER_FIELD_INVALID)
+ {
+ _dbus_string_set_length (&message->header, offset);
+
+ message->header_fields[field].name_offset = -1;
+ message->header_fields[field].value_offset = -1;
+
+ /* this must succeed because it was allocated on function entry and
+ * DBusString doesn't ever realloc smaller
+ */
+ if (!append_header_padding (message))
+ _dbus_assert_not_reached ("failed to reappend header padding");
+
+ return TRUE;
+ }
+ else
+ {
+ DBusString deleted;
+ int next_offset = message->header_fields[next_field].name_offset;
- append_header_padding (message);
+ _dbus_assert (next_offset > 0);
+
+ if (!_dbus_string_init (&deleted))
+ goto failed;
+
+ if (!_dbus_string_move_len (&message->header,
+ offset, next_offset - offset,
+ &deleted, 0))
+ {
+ _dbus_string_free (&deleted);
+ goto failed;
+ }
+
+ /* appends the header padding */
+ if (!re_align_field_recurse (message, next_field, offset))
+ {
+ /* this must succeed because it was allocated on function entry and
+ * DBusString doesn't ever realloc smaller
+ */
+ if (!_dbus_string_copy (&deleted, 0, &message->header, offset))
+ _dbus_assert_not_reached ("failed to revert to original field");
+
+ _dbus_string_free (&deleted);
+ goto failed;
+ }
+
+ _dbus_string_free (&deleted);
+
+ message->header_fields[field].name_offset = -1;
+ message->header_fields[field].value_offset = -1;
+
+ return TRUE;
+
+ failed:
+ /* this must succeed because it was allocated on function entry and
+ * DBusString doesn't ever realloc smaller
+ */
+ if (!append_header_padding (message))
+ _dbus_assert_not_reached ("failed to reappend header padding");
+
+ return FALSE;
+ }
}
#ifdef DBUS_BUILD_TESTS
@@ -508,20 +631,14 @@ set_int_field (DBusMessage *message,
int field,
int value)
{
- int offset = message->header_fields[field].offset;
+ int offset = message->header_fields[field].value_offset;
_dbus_assert (!message->locked);
if (offset < 0)
{
/* need to append the field */
-
- switch (field)
- {
- default:
- _dbus_assert_not_reached ("appending an int field we don't support appending");
- return FALSE;
- }
+ return append_int_field (message, field, value);
}
else
{
@@ -539,24 +656,14 @@ set_uint_field (DBusMessage *message,
int field,
dbus_uint32_t value)
{
- int offset = message->header_fields[field].offset;
+ int offset = message->header_fields[field].value_offset;
_dbus_assert (!message->locked);
if (offset < 0)
{
/* need to append the field */
-
- switch (field)
- {
- case FIELD_REPLY_SERIAL:
- return append_uint_field (message, field,
- DBUS_HEADER_FIELD_REPLY,
- value);
- default:
- _dbus_assert_not_reached ("appending a uint field we don't support appending");
- return FALSE;
- }
+ return append_uint_field (message, field, value);
}
else
{
@@ -571,9 +678,10 @@ set_uint_field (DBusMessage *message,
static dbus_bool_t
set_string_field (DBusMessage *message,
int field,
+ int type,
const char *value)
{
- int offset = message->header_fields[field].offset;
+ int offset = message->header_fields[field].value_offset;
_dbus_assert (!message->locked);
_dbus_assert (value != NULL);
@@ -581,48 +689,59 @@ set_string_field (DBusMessage *message,
if (offset < 0)
{
/* need to append the field */
-
- switch (field)
- {
- case FIELD_SENDER:
- return append_string_field (message, field,
- DBUS_HEADER_FIELD_SENDER,
- value);
- default:
- _dbus_assert_not_reached ("appending a string field we don't support appending");
- return FALSE;
- }
+ return append_string_field (message, field, type, value);
}
else
{
DBusString v;
- int old_len;
- int new_len;
+ char *old_value;
+ int next_field;
+ int next_offset;
int len;
clear_header_padding (message);
-
- old_len = _dbus_string_get_length (&message->header);
+
+ old_value = _dbus_demarshal_string (&message->header,
+ message->byte_order,
+ offset,
+ &next_offset);
+ if (!old_value)
+ goto failed;
len = strlen (value);
-
+
_dbus_string_init_const_len (&v, value,
len + 1); /* include nul */
if (!_dbus_marshal_set_string (&message->header,
message->byte_order,
- offset, &v,
- len))
- goto failed;
-
- new_len = _dbus_string_get_length (&message->header);
+ offset, &v, len))
+ {
+ dbus_free (old_value);
+ goto failed;
+ }
- adjust_field_offsets (message,
- offset,
- new_len - old_len);
+ next_field = get_next_field (message, field);
+ if (next_field != DBUS_HEADER_FIELD_INVALID)
+ {
+ /* re-appends the header padding */
+ if (!re_align_field_recurse (message, next_field, next_offset))
+ {
+ len = strlen (old_value);
+
+ _dbus_string_init_const_len (&v, old_value,
+ len + 1); /* include nul */
+ if (!_dbus_marshal_set_string (&message->header,
+ message->byte_order,
+ offset, &v, len))
+ _dbus_assert_not_reached ("failed to revert to original string");
+
+ dbus_free (old_value);
+ goto failed;
+ }
+ }
+
+ dbus_free (old_value);
- if (!append_header_padding (message))
- goto failed;
-
return TRUE;
failed:
@@ -649,9 +768,12 @@ _dbus_message_set_serial (DBusMessage *message,
{
_dbus_assert (!message->locked);
_dbus_assert (dbus_message_get_serial (message) == 0);
-
- set_uint_field (message, FIELD_CLIENT_SERIAL,
- serial);
+
+ _dbus_marshal_set_uint32 (&message->header,
+ message->byte_order,
+ CLIENT_SERIAL_OFFSET,
+ serial);
+
message->client_serial = serial;
}
@@ -669,7 +791,8 @@ dbus_message_set_reply_serial (DBusMessage *message,
{
_dbus_assert (!message->locked);
- if (set_uint_field (message, FIELD_REPLY_SERIAL,
+ if (set_uint_field (message,
+ DBUS_HEADER_FIELD_REPLY_SERIAL,
reply_serial))
{
message->reply_serial = reply_serial;
@@ -803,14 +926,25 @@ _dbus_message_remove_size_counter (DBusMessage *message,
static dbus_bool_t
dbus_message_create_header (DBusMessage *message,
- const char *name,
- const char *service)
+ int type,
+ const char *service,
+ const char *path,
+ const char *interface,
+ const char *member,
+ const char *error_name)
{
unsigned int flags;
+
+ _dbus_assert ((interface && member) ||
+ (error_name) ||
+ !(interface || member || error_name));
if (!_dbus_string_append_byte (&message->header, message->byte_order))
return FALSE;
+ if (!_dbus_string_append_byte (&message->header, type))
+ return FALSE;
+
flags = 0;
if (!_dbus_string_append_byte (&message->header, flags))
return FALSE;
@@ -818,37 +952,61 @@ dbus_message_create_header (DBusMessage *message,
if (!_dbus_string_append_byte (&message->header, DBUS_MAJOR_PROTOCOL_VERSION))
return FALSE;
- if (!_dbus_string_append_byte (&message->header, 0))
- return FALSE;
-
- message->header_fields[FIELD_HEADER_LENGTH].offset = 4;
if (!_dbus_marshal_uint32 (&message->header, message->byte_order, 0))
return FALSE;
- message->header_fields[FIELD_BODY_LENGTH].offset = 8;
if (!_dbus_marshal_uint32 (&message->header, message->byte_order, 0))
return FALSE;
- message->header_fields[FIELD_CLIENT_SERIAL].offset = 12;
if (!_dbus_marshal_int32 (&message->header, message->byte_order, -1))
return FALSE;
- /* Marshal message service */
+ /* Marshal all the fields (Marshall Fields?) */
+
+ if (path != NULL)
+ {
+ if (!append_string_field (message,
+ DBUS_HEADER_FIELD_PATH,
+ DBUS_TYPE_OBJECT_PATH,
+ path))
+ return FALSE;
+ }
+
if (service != NULL)
{
if (!append_string_field (message,
- FIELD_SERVICE,
DBUS_HEADER_FIELD_SERVICE,
+ DBUS_TYPE_STRING,
service))
return FALSE;
}
- _dbus_assert (name != NULL);
- if (!append_string_field (message,
- FIELD_NAME,
- DBUS_HEADER_FIELD_NAME,
- name))
- return FALSE;
+ if (interface != NULL)
+ {
+ if (!append_string_field (message,
+ DBUS_HEADER_FIELD_INTERFACE,
+ DBUS_TYPE_STRING,
+ interface))
+ return FALSE;
+ }
+
+ if (member != NULL)
+ {
+ if (!append_string_field (message,
+ DBUS_HEADER_FIELD_MEMBER,
+ DBUS_TYPE_STRING,
+ member))
+ return FALSE;
+ }
+
+ if (error_name != NULL)
+ {
+ if (!append_string_field (message,
+ DBUS_HEADER_FIELD_ERROR_NAME,
+ DBUS_TYPE_STRING,
+ error_name))
+ return FALSE;
+ }
return TRUE;
}
@@ -868,13 +1026,15 @@ _dbus_message_lock (DBusMessage *message)
if (!message->locked)
{
/* Fill in our lengths */
- set_uint_field (message,
- FIELD_HEADER_LENGTH,
- _dbus_string_get_length (&message->header));
+ _dbus_marshal_set_uint32 (&message->header,
+ message->byte_order,
+ HEADER_LENGTH_OFFSET,
+ _dbus_string_get_length (&message->header));
- set_uint_field (message,
- FIELD_BODY_LENGTH,
- _dbus_string_get_length (&message->body));
+ _dbus_marshal_set_uint32 (&message->header,
+ message->byte_order,
+ BODY_LENGTH_OFFSET,
+ _dbus_string_get_length (&message->body));
message->locked = TRUE;
}
@@ -920,9 +1080,10 @@ dbus_message_new_empty_header (void)
_dbus_data_slot_list_init (&message->slot_list);
i = 0;
- while (i < FIELD_LAST)
+ while (i <= DBUS_HEADER_FIELD_LAST)
{
- message->header_fields[i].offset = -1;
+ message->header_fields[i].name_offset = -1;
+ message->header_fields[i].value_offset = -1;
++i;
}
@@ -942,31 +1103,71 @@ dbus_message_new_empty_header (void)
return message;
}
-
/**
- * Constructs a new message. Returns #NULL if memory can't be
- * allocated for the message. The service may be #NULL in which case
- * no service is set; this is appropriate when using D-BUS in a
- * peer-to-peer context (no message bus).
+ * Constructs a new message of the given message type.
+ * Types include #DBUS_MESSAGE_TYPE_METHOD_CALL,
+ * #DBUS_MESSAGE_TYPE_SIGNAL, and so forth.
*
- * @param name name of the message
- * @param destination_service service that the message should be sent to or #NULL
+ * @param message_type type of message
+ * @returns new message or #NULL If no memory
+ */
+DBusMessage*
+dbus_message_new (int message_type)
+{
+ DBusMessage *message;
+
+ _dbus_return_val_if_fail (message_type != DBUS_MESSAGE_TYPE_INVALID, NULL);
+
+ message = dbus_message_new_empty_header ();
+ if (message == NULL)
+ return NULL;
+
+ if (!dbus_message_create_header (message,
+ message_type,
+ NULL, NULL, NULL, NULL, NULL))
+ {
+ dbus_message_unref (message);
+ return NULL;
+ }
+
+ return message;
+}
+
+/**
+ * Constructs a new message to invoke a method on a remote
+ * object. Returns #NULL if memory can't be allocated for the
+ * message. The service may be #NULL in which case no service is set;
+ * this is appropriate when using D-BUS in a peer-to-peer context (no
+ * message bus). The interface may be #NULL, which means that
+ * if multiple methods with the given name exist it is undefined
+ * which one will be invoked.
+ *
+ * @param service service that the message should be sent to or #NULL
+ * @param path object path the message should be sent to
+ * @param interface interface to invoke method on
+ * @param method method to invoke
+ *
* @returns a new DBusMessage, free with dbus_message_unref()
* @see dbus_message_unref()
*/
DBusMessage*
-dbus_message_new (const char *name,
- const char *destination_service)
+dbus_message_new_method_call (const char *service,
+ const char *path,
+ const char *interface,
+ const char *method)
{
DBusMessage *message;
- _dbus_return_val_if_fail (name != NULL, NULL);
+ _dbus_return_val_if_fail (path != NULL, NULL);
+ _dbus_return_val_if_fail (method != NULL, NULL);
message = dbus_message_new_empty_header ();
if (message == NULL)
return NULL;
- if (!dbus_message_create_header (message, name, destination_service))
+ if (!dbus_message_create_header (message,
+ DBUS_MESSAGE_TYPE_METHOD_CALL,
+ service, path, interface, method, NULL))
{
dbus_message_unref (message);
return NULL;
@@ -976,37 +1177,42 @@ dbus_message_new (const char *name,
}
/**
- * Constructs a message that is a reply to some other
- * message. Returns #NULL if memory can't be allocated
- * for the message.
+ * Constructs a message that is a reply to a method call. Returns
+ * #NULL if memory can't be allocated for the message.
*
- * @param original_message the message which the created
+ * @param method_call the message which the created
* message is a reply to.
* @returns a new DBusMessage, free with dbus_message_unref()
- * @see dbus_message_new(), dbus_message_unref()
+ * @see dbus_message_new_method_call(), dbus_message_unref()
*/
DBusMessage*
-dbus_message_new_reply (DBusMessage *original_message)
+dbus_message_new_method_return (DBusMessage *method_call)
{
DBusMessage *message;
- const char *sender, *name;
-
- _dbus_return_val_if_fail (original_message != NULL, NULL);
-
- sender = get_string_field (original_message,
- FIELD_SENDER, NULL);
- name = get_string_field (original_message,
- FIELD_NAME, NULL);
+ const char *sender;
- /* sender is allowed to be null here in peer-to-peer case */
+ _dbus_return_val_if_fail (method_call != NULL, NULL);
- message = dbus_message_new (name, sender);
+ sender = get_string_field (method_call,
+ DBUS_HEADER_FIELD_SENDER_SERVICE,
+ NULL);
+ /* sender is allowed to be null here in peer-to-peer case */
+
+ message = dbus_message_new_empty_header ();
if (message == NULL)
return NULL;
+
+ if (!dbus_message_create_header (message,
+ DBUS_MESSAGE_TYPE_METHOD_RETURN,
+ sender, NULL, NULL, NULL, NULL))
+ {
+ dbus_message_unref (message);
+ return NULL;
+ }
if (!dbus_message_set_reply_serial (message,
- dbus_message_get_serial (original_message)))
+ dbus_message_get_serial (method_call)))
{
dbus_message_unref (message);
return NULL;
@@ -1016,40 +1222,86 @@ dbus_message_new_reply (DBusMessage *original_message)
}
/**
+ * Constructs a new message representing a signal emission. Returns
+ * #NULL if memory can't be allocated for the message.
+ * A signal is identified by its originating interface, and
+ * the name of the signal.
+ *
+ * @param path the path to the object emitting the signal
+ * @param interface the interface the signal is emitted from
+ * @param name name of the signal
+ * @returns a new DBusMessage, free with dbus_message_unref()
+ * @see dbus_message_unref()
+ */
+DBusMessage*
+dbus_message_new_signal (const char *path,
+ const char *interface,
+ const char *name)
+{
+ DBusMessage *message;
+
+ _dbus_return_val_if_fail (path != NULL, NULL);
+ _dbus_return_val_if_fail (interface != NULL, NULL);
+ _dbus_return_val_if_fail (name != NULL, NULL);
+
+ message = dbus_message_new_empty_header ();
+ if (message == NULL)
+ return NULL;
+
+ if (!dbus_message_create_header (message,
+ DBUS_MESSAGE_TYPE_SIGNAL,
+ NULL, path, interface, name, NULL))
+ {
+ dbus_message_unref (message);
+ return NULL;
+ }
+
+ return message;
+}
+
+/**
* Creates a new message that is an error reply to a certain message.
+ * Error replies are possible in response to method calls primarily.
*
- * @param original_message the original message
+ * @param reply_to the original message
* @param error_name the error name
* @param error_message the error message string or #NULL for none
* @returns a new error message
*/
DBusMessage*
-dbus_message_new_error_reply (DBusMessage *original_message,
- const char *error_name,
- const char *error_message)
+dbus_message_new_error (DBusMessage *reply_to,
+ const char *error_name,
+ const char *error_message)
{
DBusMessage *message;
const char *sender;
DBusMessageIter iter;
- _dbus_return_val_if_fail (original_message != NULL, NULL);
+ _dbus_return_val_if_fail (reply_to != NULL, NULL);
_dbus_return_val_if_fail (error_name != NULL, NULL);
- sender = get_string_field (original_message,
- FIELD_SENDER, NULL);
+ sender = get_string_field (reply_to,
+ DBUS_HEADER_FIELD_SENDER_SERVICE,
+ NULL);
/* sender may be NULL for non-message-bus case or
* when the message bus is dealing with an unregistered
* connection.
*/
-
- message = dbus_message_new (error_name, sender);
-
+ message = dbus_message_new_empty_header ();
if (message == NULL)
return NULL;
+
+ if (!dbus_message_create_header (message,
+ DBUS_MESSAGE_TYPE_ERROR,
+ sender, NULL, NULL, NULL, error_name))
+ {
+ dbus_message_unref (message);
+ return NULL;
+ }
if (!dbus_message_set_reply_serial (message,
- dbus_message_get_serial (original_message)))
+ dbus_message_get_serial (reply_to)))
{
dbus_message_unref (message);
return NULL;
@@ -1064,8 +1316,6 @@ dbus_message_new_error_reply (DBusMessage *original_message,
return NULL;
}
}
-
- dbus_message_set_is_error (message, TRUE);
return message;
}
@@ -1129,9 +1379,9 @@ dbus_message_copy (const DBusMessage *message)
return NULL;
}
- for (i = 0; i < FIELD_LAST; i++)
+ for (i = 0; i <= DBUS_HEADER_FIELD_LAST; i++)
{
- retval->header_fields[i].offset = message->header_fields[i].offset;
+ retval->header_fields[i] = message->header_fields[i];
}
return retval;
@@ -1201,17 +1451,268 @@ dbus_message_unref (DBusMessage *message)
}
/**
- * Gets the name of a message.
+ * Gets the type of a message. Types include
+ * DBUS_MESSAGE_TYPE_METHOD_CALL, DBUS_MESSAGE_TYPE_METHOD_RETURN,
+ * DBUS_MESSAGE_TYPE_ERROR, DBUS_MESSAGE_TYPE_SIGNAL, but other types
+ * are allowed and all code must silently ignore messages of unknown
+ * type. DBUS_MESSAGE_TYPE_INVALID will never be returned, however.
+ *
+ *
+ * @param message the message
+ * @returns the type of the message
+ */
+int
+dbus_message_get_type (DBusMessage *message)
+{
+ int type;
+
+ type = _dbus_string_get_byte (&message->header, 1);
+ _dbus_assert (type != DBUS_MESSAGE_TYPE_INVALID);
+
+ return type;
+}
+
+/**
+ * Sets the object path this message is being sent to (for
+ * DBUS_MESSAGE_TYPE_METHOD_CALL) or the one a signal is being
+ * emitted from (for DBUS_MESSAGE_TYPE_SIGNAL).
+ *
+ * @param message the message
+ * @param object_path the path
+ * @returns #FALSE if not enough memory
+ */
+dbus_bool_t
+dbus_message_set_path (DBusMessage *message,
+ const char *object_path)
+{
+ _dbus_return_val_if_fail (message != NULL, FALSE);
+ _dbus_return_val_if_fail (!message->locked, FALSE);
+
+ if (object_path == NULL)
+ {
+ delete_field (message, DBUS_HEADER_FIELD_PATH);
+ return TRUE;
+ }
+ else
+ {
+ return set_string_field (message,
+ DBUS_HEADER_FIELD_PATH,
+ DBUS_TYPE_OBJECT_PATH,
+ object_path);
+ }
+}
+
+/**
+ * Gets the object path this message is being sent to
+ * (for DBUS_MESSAGE_TYPE_METHOD_CALL) or being emitted
+ * from (for DBUS_MESSAGE_TYPE_SIGNAL).
+ *
+ * @param message the message
+ * @returns the path (should not be freed)
+ */
+const char*
+dbus_message_get_path (DBusMessage *message)
+{
+ _dbus_return_val_if_fail (message != NULL, NULL);
+
+ return get_string_field (message, DBUS_HEADER_FIELD_PATH, NULL);
+}
+
+/**
+ * Gets the object path this message is being sent to
+ * (for DBUS_MESSAGE_TYPE_METHOD_CALL) or being emitted
+ * from (for DBUS_MESSAGE_TYPE_SIGNAL) in a decomposed
+ * format (one array element per path component).
+ * Free the returned array with dbus_free_string_array().
+ *
+ * An empty but non-NULL path array means the path "/".
+ * So the path "/foo/bar" becomes { "foo", "bar", NULL }
+ * and the path "/" becomes { NULL }.
+ *
+ * @param message the message
+ * @param path place to store allocated array of path components; #NULL set here if no path field exists
+ * @returns #FALSE if no memory to allocate the array
+ */
+dbus_bool_t
+dbus_message_get_path_decomposed (DBusMessage *message,
+ char ***path)
+{
+ _dbus_return_val_if_fail (message != NULL, FALSE);
+ _dbus_return_val_if_fail (path != NULL, FALSE);
+
+ return get_path_field_decomposed (message,
+ DBUS_HEADER_FIELD_PATH,
+ path);
+}
+
+/**
+ * Sets the interface this message is being sent to
+ * (for DBUS_MESSAGE_TYPE_METHOD_CALL) or
+ * the interface a signal is being emitted from
+ * (for DBUS_MESSAGE_TYPE_SIGNAL).
+ *
+ * @param message the message
+ * @param interface the interface
+ * @returns #FALSE if not enough memory
+ */
+dbus_bool_t
+dbus_message_set_interface (DBusMessage *message,
+ const char *interface)
+{
+ _dbus_return_val_if_fail (message != NULL, FALSE);
+ _dbus_return_val_if_fail (!message->locked, FALSE);
+
+ if (interface == NULL)
+ {
+ delete_field (message, DBUS_HEADER_FIELD_INTERFACE);
+ return TRUE;
+ }
+ else
+ {
+ return set_string_field (message,
+ DBUS_HEADER_FIELD_INTERFACE,
+ DBUS_TYPE_STRING,
+ interface);
+ }
+}
+
+/**
+ * Gets the interface this message is being sent to
+ * (for DBUS_MESSAGE_TYPE_METHOD_CALL) or being emitted
+ * from (for DBUS_MESSAGE_TYPE_SIGNAL).
+ * The interface name is fully-qualified (namespaced).
+ *
+ * @param message the message
+ * @returns the message interface (should not be freed)
+ */
+const char*
+dbus_message_get_interface (DBusMessage *message)
+{
+ _dbus_return_val_if_fail (message != NULL, NULL);
+
+ return get_string_field (message, DBUS_HEADER_FIELD_INTERFACE, NULL);
+}
+
+/**
+ * Sets the interface member being invoked
+ * (DBUS_MESSAGE_TYPE_METHOD_CALL) or emitted
+ * (DBUS_MESSAGE_TYPE_SIGNAL).
+ * The interface name is fully-qualified (namespaced).
+ *
+ * @param message the message
+ * @param member the member
+ * @returns #FALSE if not enough memory
+ */
+dbus_bool_t
+dbus_message_set_member (DBusMessage *message,
+ const char *member)
+{
+ _dbus_return_val_if_fail (message != NULL, FALSE);
+ _dbus_return_val_if_fail (!message->locked, FALSE);
+
+ if (member == NULL)
+ {
+ delete_field (message, DBUS_HEADER_FIELD_MEMBER);
+ return TRUE;
+ }
+ else
+ {
+ return set_string_field (message,
+ DBUS_HEADER_FIELD_MEMBER,
+ DBUS_TYPE_STRING,
+ member);
+ }
+}
+
+/**
+ * Gets the interface member being invoked
+ * (DBUS_MESSAGE_TYPE_METHOD_CALL) or emitted
+ * (DBUS_MESSAGE_TYPE_SIGNAL).
+ *
+ * @param message the message
+ * @returns the member name (should not be freed)
+ */
+const char*
+dbus_message_get_member (DBusMessage *message)
+{
+ _dbus_return_val_if_fail (message != NULL, NULL);
+
+ return get_string_field (message,
+ DBUS_HEADER_FIELD_MEMBER,
+ NULL);
+}
+
+/**
+ * Sets the name of the error (DBUS_MESSAGE_TYPE_ERROR).
+ * The name is fully-qualified (namespaced).
*
* @param message the message
- * @returns the message name (should not be freed)
+ * @param error_name the name
+ * @returns #FALSE if not enough memory
+ */
+dbus_bool_t
+dbus_message_set_error_name (DBusMessage *message,
+ const char *error_name)
+{
+ _dbus_return_val_if_fail (message != NULL, FALSE);
+ _dbus_return_val_if_fail (!message->locked, FALSE);
+
+ if (error_name == NULL)
+ {
+ delete_field (message, DBUS_HEADER_FIELD_ERROR_NAME);
+ return TRUE;
+ }
+ else
+ {
+ return set_string_field (message,
+ DBUS_HEADER_FIELD_ERROR_NAME,
+ DBUS_TYPE_STRING,
+ error_name);
+ }
+}
+
+/**
+ * Gets the error name (DBUS_MESSAGE_TYPE_ERROR only).
+ *
+ * @param message the message
+ * @returns the error name (should not be freed)
*/
const char*
-dbus_message_get_name (DBusMessage *message)
+dbus_message_get_error_name (DBusMessage *message)
{
_dbus_return_val_if_fail (message != NULL, NULL);
- return get_string_field (message, FIELD_NAME, NULL);
+ return get_string_field (message,
+ DBUS_HEADER_FIELD_ERROR_NAME,
+ NULL);
+}
+
+/**
+ * Sets the message's destination service.
+ *
+ * @param message the message
+ * @param destination the destination service name
+ * @returns #FALSE if not enough memory
+ */
+dbus_bool_t
+dbus_message_set_destination (DBusMessage *message,
+ const char *destination)
+{
+ _dbus_return_val_if_fail (message != NULL, FALSE);
+ _dbus_return_val_if_fail (!message->locked, FALSE);
+
+ if (destination == NULL)
+ {
+ delete_field (message, DBUS_HEADER_FIELD_SERVICE);
+ return TRUE;
+ }
+ else
+ {
+ return set_string_field (message,
+ DBUS_HEADER_FIELD_SERVICE,
+ DBUS_TYPE_STRING,
+ destination);
+ }
}
/**
@@ -1225,7 +1726,9 @@ dbus_message_get_destination (DBusMessage *message)
{
_dbus_return_val_if_fail (message != NULL, NULL);
- return get_string_field (message, FIELD_SERVICE, NULL);
+ return get_string_field (message,
+ DBUS_HEADER_FIELD_SERVICE,
+ NULL);
}
/**
@@ -3677,58 +4180,62 @@ dbus_message_set_sender (DBusMessage *message,
if (sender == NULL)
{
- delete_string_field (message, FIELD_SENDER);
+ delete_field (message, DBUS_HEADER_FIELD_SENDER_SERVICE);
return TRUE;
}
else
{
return set_string_field (message,
- FIELD_SENDER,
+ DBUS_HEADER_FIELD_SENDER_SERVICE,
+ DBUS_TYPE_STRING,
sender);
}
}
/**
- * Sets a flag indicating that the message is an error reply
- * message, i.e. an "exception" rather than a normal response.
+ * Sets a flag indicating that the message does not want a reply; if
+ * this flag is set, the other end of the connection may (but is not
+ * required to) optimize by not sending method return or error
+ * replies. If this flag is set, there is no way to know whether the
+ * message successfully arrived at the remote end.
*
* @param message the message
- * @param is_error_reply #TRUE if this is an error message.
+ * @param no_reply #TRUE if no reply is desired
*/
void
-dbus_message_set_is_error (DBusMessage *message,
- dbus_bool_t is_error_reply)
+dbus_message_set_no_reply (DBusMessage *message,
+ dbus_bool_t no_reply)
{
char *header;
_dbus_return_if_fail (message != NULL);
_dbus_return_if_fail (!message->locked);
- header = _dbus_string_get_data_len (&message->header, 1, 1);
+ header = _dbus_string_get_data_len (&message->header, FLAGS_OFFSET, 1);
- if (is_error_reply)
- *header |= DBUS_HEADER_FLAG_ERROR;
+ if (no_reply)
+ *header |= DBUS_HEADER_FLAG_NO_REPLY_EXPECTED;
else
- *header &= ~DBUS_HEADER_FLAG_ERROR;
+ *header &= ~DBUS_HEADER_FLAG_NO_REPLY_EXPECTED;
}
/**
- * Returns #TRUE if the message is an error
- * reply to some previous message we sent.
+ * Returns #TRUE if the message does not expect
+ * a reply.
*
* @param message the message
- * @returns #TRUE if the message is an error
+ * @returns #TRUE if the message sender isn't waiting for a reply
*/
dbus_bool_t
-dbus_message_get_is_error (DBusMessage *message)
+dbus_message_get_no_reply (DBusMessage *message)
{
const char *header;
_dbus_return_val_if_fail (message != NULL, FALSE);
- header = _dbus_string_get_const_data_len (&message->header, 1, 1);
+ header = _dbus_string_get_const_data_len (&message->header, FLAGS_OFFSET, 1);
- return (*header & DBUS_HEADER_FLAG_ERROR) != 0;
+ return (*header & DBUS_HEADER_FLAG_NO_REPLY_EXPECTED) != 0;
}
/**
@@ -3743,31 +4250,120 @@ dbus_message_get_sender (DBusMessage *message)
{
_dbus_return_val_if_fail (message != NULL, NULL);
- return get_string_field (message, FIELD_SENDER, NULL);
+ return get_string_field (message,
+ DBUS_HEADER_FIELD_SENDER_SERVICE,
+ NULL);
+}
+
+static dbus_bool_t
+_dbus_message_has_type_interface_member (DBusMessage *message,
+ int type,
+ const char *interface,
+ const char *method)
+{
+ const char *n;
+
+ _dbus_assert (message != NULL);
+ _dbus_assert (interface != NULL);
+ _dbus_assert (method != NULL);
+
+ if (dbus_message_get_type (message) != type)
+ return FALSE;
+
+ /* Optimize by checking the short method name first
+ * instead of the longer interface name
+ */
+
+ n = dbus_message_get_member (message);
+
+ if (n && strcmp (n, method) == 0)
+ {
+ n = dbus_message_get_interface (message);
+
+ if (n && strcmp (n, interface) == 0)
+ return TRUE;
+ }
+
+ return FALSE;
}
/**
- * Checks whether the message has the given name.
- * If the message has no name or has a different
- * name, returns #FALSE.
+ * Checks whether the message is a method call with the given
+ * interface and member fields. If the message is not
+ * #DBUS_MESSAGE_TYPE_METHOD_CALL, or has a different interface or member field,
+ * returns #FALSE.
*
* @param message the message
- * @param name the name to check (must not be #NULL)
+ * @param interface the name to check (must not be #NULL)
+ * @param method the name to check (must not be #NULL)
*
- * @returns #TRUE if the message has the given name
+ * @returns #TRUE if the message is the specified method call
*/
dbus_bool_t
-dbus_message_has_name (DBusMessage *message,
- const char *name)
+dbus_message_is_method_call (DBusMessage *message,
+ const char *interface,
+ const char *method)
{
- const char *n;
+ _dbus_return_val_if_fail (message != NULL, FALSE);
+ _dbus_return_val_if_fail (interface != NULL, FALSE);
+ _dbus_return_val_if_fail (method != NULL, FALSE);
+
+ return _dbus_message_has_type_interface_member (message,
+ DBUS_MESSAGE_TYPE_METHOD_CALL,
+ interface, method);
+}
+/**
+ * Checks whether the message is a signal with the given
+ * interface and member fields. If the message is not
+ * #DBUS_MESSAGE_TYPE_SIGNAL, or has a different interface or member field,
+ * returns #FALSE.
+ *
+ * @param message the message
+ * @param interface the name to check (must not be #NULL)
+ * @param signal_name the name to check (must not be #NULL)
+ *
+ * @returns #TRUE if the message is the specified signal
+ */
+dbus_bool_t
+dbus_message_is_signal (DBusMessage *message,
+ const char *interface,
+ const char *signal_name)
+{
_dbus_return_val_if_fail (message != NULL, FALSE);
- _dbus_return_val_if_fail (name != NULL, FALSE);
+ _dbus_return_val_if_fail (interface != NULL, FALSE);
+ _dbus_return_val_if_fail (signal_name != NULL, FALSE);
+
+ return _dbus_message_has_type_interface_member (message,
+ DBUS_MESSAGE_TYPE_SIGNAL,
+ interface, signal_name);
+}
+
+/**
+ * Checks whether the message is an error reply with the given error
+ * name. If the message is not #DBUS_MESSAGE_TYPE_ERROR, or has a
+ * different name, returns #FALSE.
+ *
+ * @param message the message
+ * @param error_name the name to check (must not be #NULL)
+ *
+ * @returns #TRUE if the message is the specified error
+ */
+dbus_bool_t
+dbus_message_is_error (DBusMessage *message,
+ const char *error_name)
+{
+ const char *n;
- n = dbus_message_get_name (message);
+ _dbus_return_val_if_fail (message != NULL, FALSE);
+ _dbus_return_val_if_fail (error_name != NULL, FALSE);
+
+ if (dbus_message_get_type (message) != DBUS_MESSAGE_TYPE_ERROR)
+ return FALSE;
- if (n && strcmp (n, name) == 0)
+ n = dbus_message_get_error_name (message);
+
+ if (n && strcmp (n, error_name) == 0)
return TRUE;
else
return FALSE;
@@ -3833,7 +4429,7 @@ dbus_message_has_sender (DBusMessage *message,
/**
* Sets a #DBusError based on the contents of the given
* message. The error is only set if the message
- * is an error message, as in dbus_message_get_is_error().
+ * is an error message, as in DBUS_MESSAGE_TYPE_ERROR.
* The name of the error is set to the name of the message,
* and the error message is set to the first argument
* if the argument exists and is a string.
@@ -3856,7 +4452,7 @@ dbus_set_error_from_message (DBusError *error,
_dbus_return_val_if_fail (message != NULL, FALSE);
_dbus_return_val_if_error_is_set (error, FALSE);
- if (!dbus_message_get_is_error (message))
+ if (dbus_message_get_type (message) != DBUS_MESSAGE_TYPE_ERROR)
return FALSE;
str = NULL;
@@ -3864,7 +4460,7 @@ dbus_set_error_from_message (DBusError *error,
DBUS_TYPE_STRING, &str,
DBUS_TYPE_INVALID);
- dbus_set_error (error, dbus_message_get_name (message),
+ dbus_set_error (error, dbus_message_get_error_name (message),
str ? "%s" : NULL, str);
dbus_free (str);
@@ -4039,51 +4635,31 @@ _dbus_message_loader_get_buffer (DBusMessageLoader *loader,
*/
#define DBUS_MINIMUM_HEADER_SIZE 16
-/** Pack four characters as in "abcd" into a uint32 */
-#define FOUR_CHARS_TO_UINT32(a, b, c, d) \
- ((((dbus_uint32_t)a) << 24) | \
- (((dbus_uint32_t)b) << 16) | \
- (((dbus_uint32_t)c) << 8) | \
- ((dbus_uint32_t)d))
-
-/** DBUS_HEADER_FIELD_NAME packed into a dbus_uint32_t */
-#define DBUS_HEADER_FIELD_NAME_AS_UINT32 \
- FOUR_CHARS_TO_UINT32 ('n', 'a', 'm', 'e')
-
-/** DBUS_HEADER_FIELD_SERVICE packed into a dbus_uint32_t */
-#define DBUS_HEADER_FIELD_SERVICE_AS_UINT32 \
- FOUR_CHARS_TO_UINT32 ('s', 'r', 'v', 'c')
-
-/** DBUS_HEADER_FIELD_REPLY packed into a dbus_uint32_t */
-#define DBUS_HEADER_FIELD_REPLY_AS_UINT32 \
- FOUR_CHARS_TO_UINT32 ('r', 'p', 'l', 'y')
-
-/** DBUS_HEADER_FIELD_SENDER Packed into a dbus_uint32_t */
-#define DBUS_HEADER_FIELD_SENDER_AS_UINT32 \
- FOUR_CHARS_TO_UINT32 ('s', 'n', 'd', 'r')
-
static dbus_bool_t
decode_string_field (const DBusString *data,
- HeaderField fields[FIELD_LAST],
+ int field,
+ HeaderField *header_field,
+ DBusString *field_data,
int pos,
- int type,
- int field,
- const char *field_name)
+ int type)
{
- DBusString tmp;
int string_data_pos;
+
+ _dbus_assert (header_field != NULL);
+ _dbus_assert (field_data != NULL);
- if (fields[field].offset >= 0)
+ if (header_field->name_offset >= 0)
{
_dbus_verbose ("%s field provided twice\n",
- field_name);
+ _dbus_header_field_to_string (field));
return FALSE;
}
if (type != DBUS_TYPE_STRING)
{
_dbus_verbose ("%s field has wrong type %s\n",
- field_name, _dbus_type_to_string (type));
+ _dbus_header_field_to_string (field),
+ _dbus_type_to_string (type));
return FALSE;
}
@@ -4094,40 +4670,16 @@ decode_string_field (const DBusString *data,
string_data_pos = _DBUS_ALIGN_VALUE (pos, 4) + 4;
_dbus_assert (string_data_pos < _dbus_string_get_length (data));
- _dbus_string_init_const (&tmp,
+ _dbus_string_init_const (field_data,
_dbus_string_get_const_data (data) + string_data_pos);
- if (field == FIELD_NAME)
- {
- if (!_dbus_string_validate_name (&tmp, 0, _dbus_string_get_length (&tmp)))
- {
- _dbus_verbose ("%s field has invalid content \"%s\"\n",
- field_name, _dbus_string_get_const_data (&tmp));
- return FALSE;
- }
-
- if (_dbus_string_starts_with_c_str (&tmp,
- DBUS_NAMESPACE_LOCAL_MESSAGE))
- {
- _dbus_verbose ("Message is in the local namespace\n");
- return FALSE;
- }
- }
- else
- {
- if (!_dbus_string_validate_service (&tmp, 0, _dbus_string_get_length (&tmp)))
- {
- _dbus_verbose ("%s field has invalid content \"%s\"\n",
- field_name, _dbus_string_get_const_data (&tmp));
- return FALSE;
- }
- }
-
- fields[field].offset = _DBUS_ALIGN_VALUE (pos, 4);
+ header_field->name_offset = pos;
+ header_field->value_offset = _DBUS_ALIGN_VALUE (pos, 4);
#if 0
- _dbus_verbose ("Found field %s name at offset %d\n",
- field_name, fields[field].offset);
+ _dbus_verbose ("Found field %s at offset %d\n",
+ _dbus_header_field_to_string (field),
+ header_field->value_offset);
#endif
return TRUE;
@@ -4137,47 +4689,38 @@ static dbus_bool_t
decode_header_data (const DBusString *data,
int header_len,
int byte_order,
- HeaderField fields[FIELD_LAST],
+ int message_type,
+ HeaderField fields[DBUS_HEADER_FIELD_LAST + 1],
int *message_padding)
{
- const char *field;
+ DBusString field_data;
int pos, new_pos;
int i;
+ int field;
int type;
if (header_len < 16)
- return FALSE;
+ {
+ _dbus_verbose ("Header length %d is too short\n", header_len);
+ return FALSE;
+ }
i = 0;
- while (i < FIELD_LAST)
+ while (i <= DBUS_HEADER_FIELD_LAST)
{
- fields[i].offset = -1;
+ fields[i].name_offset = -1;
+ fields[i].value_offset = -1;
++i;
}
- fields[FIELD_HEADER_LENGTH].offset = 4;
- fields[FIELD_BODY_LENGTH].offset = 8;
- fields[FIELD_CLIENT_SERIAL].offset = 12;
-
- /* Now handle the named fields. A real named field is at least 4
- * bytes for the name, plus a type code (1 byte) plus padding. So
- * if we have less than 8 bytes left, it must be alignment padding,
- * not a field. While >= 8 bytes can't be entirely alignment
- * padding.
- */
pos = 16;
- while ((pos + 7) < header_len)
+ while (pos < header_len)
{
- pos = _DBUS_ALIGN_VALUE (pos, 4);
-
- if ((pos + 4) > header_len)
- return FALSE;
-
- field =_dbus_string_get_const_data_len (data, pos, 4);
- pos += 4;
+ field = _dbus_string_get_byte (data, pos);
+ if (field == DBUS_HEADER_FIELD_INVALID)
+ break; /* Must be padding */
+ pos++;
- _dbus_assert (_DBUS_ALIGN_ADDRESS (field, 4) == field);
-
if (!_dbus_marshal_validate_type (data, pos, &type, &pos))
{
_dbus_verbose ("Failed to validate type of named header field\n");
@@ -4196,51 +4739,146 @@ decode_header_data (const DBusString *data,
return FALSE;
}
- switch (DBUS_UINT32_FROM_BE (*(int*)field))
+ switch (field)
{
- case DBUS_HEADER_FIELD_SERVICE_AS_UINT32:
- if (!decode_string_field (data, fields, pos, type,
- FIELD_SERVICE,
- DBUS_HEADER_FIELD_SERVICE))
+ case DBUS_HEADER_FIELD_SERVICE:
+ if (!decode_string_field (data, field, &fields[field],
+ &field_data, pos, type))
return FALSE;
+
+ if (!_dbus_string_validate_service (&field_data, 0,
+ _dbus_string_get_length (&field_data)))
+ {
+ _dbus_verbose ("service field has invalid content \"%s\"\n",
+ _dbus_string_get_const_data (&field_data));
+ return FALSE;
+ }
break;
- case DBUS_HEADER_FIELD_NAME_AS_UINT32:
- if (!decode_string_field (data, fields, pos, type,
- FIELD_NAME,
- DBUS_HEADER_FIELD_NAME))
+ case DBUS_HEADER_FIELD_INTERFACE:
+ if (!decode_string_field (data, field, &fields[field],
+ &field_data, pos, type))
return FALSE;
+
+ if (!_dbus_string_validate_interface (&field_data, 0,
+ _dbus_string_get_length (&field_data)))
+ {
+ _dbus_verbose ("interface field has invalid content \"%s\"\n",
+ _dbus_string_get_const_data (&field_data));
+ return FALSE;
+ }
+
+ if (_dbus_string_equal_c_str (&field_data,
+ DBUS_INTERFACE_ORG_FREEDESKTOP_LOCAL))
+ {
+ _dbus_verbose ("Message is on the local interface\n");
+ return FALSE;
+ }
break;
- case DBUS_HEADER_FIELD_SENDER_AS_UINT32:
- if (!decode_string_field (data, fields, pos, type,
- FIELD_SENDER,
- DBUS_HEADER_FIELD_SENDER))
+ case DBUS_HEADER_FIELD_MEMBER:
+ if (!decode_string_field (data, field, &fields[field],
+ &field_data, pos, type))
+ return FALSE;
+
+ if (!_dbus_string_validate_member (&field_data, 0,
+ _dbus_string_get_length (&field_data)))
+ {
+ _dbus_verbose ("member field has invalid content \"%s\"\n",
+ _dbus_string_get_const_data (&field_data));
+ return FALSE;
+ }
+ break;
+
+ case DBUS_HEADER_FIELD_ERROR_NAME:
+ if (!decode_string_field (data, field, &fields[field],
+ &field_data, pos, type))
+ return FALSE;
+
+ if (!_dbus_string_validate_error_name (&field_data, 0,
+ _dbus_string_get_length (&field_data)))
+ {
+ _dbus_verbose ("error-name field has invalid content \"%s\"\n",
+ _dbus_string_get_const_data (&field_data));
+ return FALSE;
+ }
+ break;
+
+ case DBUS_HEADER_FIELD_SENDER_SERVICE:
+ if (!decode_string_field (data, field, &fields[field],
+ &field_data, pos, type))
return FALSE;
+
+ if (!_dbus_string_validate_service (&field_data, 0,
+ _dbus_string_get_length (&field_data)))
+ {
+ _dbus_verbose ("sender-service field has invalid content \"%s\"\n",
+ _dbus_string_get_const_data (&field_data));
+ return FALSE;
+ }
+ break;
+
+ case DBUS_HEADER_FIELD_PATH:
+
+ /* Path was already validated as part of standard
+ * type validation, since there's an OBJECT_PATH
+ * type.
+ */
+
+ if (fields[field].name_offset >= 0)
+ {
+ _dbus_verbose ("path field provided twice\n");
+ return FALSE;
+ }
+ if (type != DBUS_TYPE_OBJECT_PATH)
+ {
+ _dbus_verbose ("path field has wrong type\n");
+ return FALSE;
+ }
+
+ fields[field].name_offset = pos;
+ fields[field].value_offset = _DBUS_ALIGN_VALUE (pos, 4);
+
+ /* No forging signals from the local path */
+ {
+ const char *s;
+ s = _dbus_string_get_const_data_len (data,
+ fields[field].value_offset,
+ _dbus_string_get_length (data) -
+ fields[field].value_offset);
+ if (strcmp (s, DBUS_PATH_ORG_FREEDESKTOP_LOCAL) == 0)
+ {
+ _dbus_verbose ("Message is on the local path\n");
+ return FALSE;
+ }
+ }
+
+ _dbus_verbose ("Found path at offset %d\n",
+ fields[field].value_offset);
break;
- case DBUS_HEADER_FIELD_REPLY_AS_UINT32:
- if (fields[FIELD_REPLY_SERIAL].offset >= 0)
+ case DBUS_HEADER_FIELD_REPLY_SERIAL:
+ if (fields[field].name_offset >= 0)
{
- _dbus_verbose ("%s field provided twice\n",
- DBUS_HEADER_FIELD_REPLY);
+ _dbus_verbose ("reply field provided twice\n");
return FALSE;
}
if (type != DBUS_TYPE_UINT32)
{
- _dbus_verbose ("%s field has wrong type\n", DBUS_HEADER_FIELD_REPLY);
+ _dbus_verbose ("reply field has wrong type\n");
return FALSE;
}
- fields[FIELD_REPLY_SERIAL].offset = _DBUS_ALIGN_VALUE (pos, 4);
+ fields[field].name_offset = pos;
+ fields[field].value_offset = _DBUS_ALIGN_VALUE (pos, 4);
_dbus_verbose ("Found reply serial at offset %d\n",
- fields[FIELD_REPLY_SERIAL].offset);
+ fields[field].value_offset);
break;
default:
- _dbus_verbose ("Ignoring an unknown header field: %.4s at offset %d\n",
+ _dbus_verbose ("Ignoring an unknown header field: %d at offset %d\n",
field, pos);
}
@@ -4250,7 +4888,11 @@ decode_header_data (const DBusString *data,
if (pos < header_len)
{
/* Alignment padding, verify that it's nul */
- _dbus_assert ((header_len - pos) < 8);
+ if ((header_len - pos) >= 8)
+ {
+ _dbus_verbose ("too much header alignment padding\n");
+ return FALSE;
+ }
if (!_dbus_string_validate_nul (data,
pos, (header_len - pos)))
@@ -4260,12 +4902,40 @@ decode_header_data (const DBusString *data,
}
}
- /* Name field is mandatory */
- if (fields[FIELD_NAME].offset < 0)
+ /* Depending on message type, enforce presence of certain fields. */
+ switch (message_type)
{
- _dbus_verbose ("No %s field provided\n",
- DBUS_HEADER_FIELD_NAME);
- return FALSE;
+ case DBUS_MESSAGE_TYPE_SIGNAL:
+ case DBUS_MESSAGE_TYPE_METHOD_CALL:
+ if (fields[DBUS_HEADER_FIELD_PATH].value_offset < 0)
+ {
+ _dbus_verbose ("No path field provided\n");
+ return FALSE;
+ }
+ /* FIXME make this optional, only for method calls */
+ if (fields[DBUS_HEADER_FIELD_INTERFACE].value_offset < 0)
+ {
+ _dbus_verbose ("No interface field provided\n");
+ return FALSE;
+ }
+ if (fields[DBUS_HEADER_FIELD_MEMBER].value_offset < 0)
+ {
+ _dbus_verbose ("No member field provided\n");
+ return FALSE;
+ }
+ break;
+ case DBUS_MESSAGE_TYPE_ERROR:
+ if (fields[DBUS_HEADER_FIELD_ERROR_NAME].value_offset < 0)
+ {
+ _dbus_verbose ("No error-name field provided\n");
+ return FALSE;
+ }
+ break;
+ case DBUS_MESSAGE_TYPE_METHOD_RETURN:
+ break;
+ default:
+ /* An unknown type, spec requires us to ignore it */
+ break;
}
if (message_padding)
@@ -4298,6 +4968,13 @@ _dbus_message_loader_return_buffer (DBusMessageLoader *loader,
/**
* Converts buffered data into messages.
*
+ * @todo we need to check that the proper named header fields exist
+ * for each message type.
+ *
+ * @todo If a message has unknown type, we should probably eat it
+ * right here rather than passing it out to applications. However
+ * it's not an error to see messages of unknown type.
+ *
* @param loader the loader.
* @returns #TRUE if we had enough memory to finish.
*/
@@ -4311,22 +4988,22 @@ _dbus_message_loader_queue_messages (DBusMessageLoader *loader)
{
DBusMessage *message;
const char *header_data;
- int byte_order, header_len, body_len, header_padding;
+ int byte_order, message_type, header_len, body_len, header_padding;
dbus_uint32_t header_len_unsigned, body_len_unsigned;
header_data = _dbus_string_get_const_data_len (&loader->data, 0, 16);
_dbus_assert (_DBUS_ALIGN_ADDRESS (header_data, 4) == header_data);
- if (header_data[2] != DBUS_MAJOR_PROTOCOL_VERSION)
+ if (header_data[VERSION_OFFSET] != DBUS_MAJOR_PROTOCOL_VERSION)
{
_dbus_verbose ("Message has protocol version %d ours is %d\n",
- (int) header_data[2], DBUS_MAJOR_PROTOCOL_VERSION);
+ (int) header_data[VERSION_OFFSET], DBUS_MAJOR_PROTOCOL_VERSION);
loader->corrupted = TRUE;
return TRUE;
}
- byte_order = header_data[0];
+ byte_order = header_data[BYTE_ORDER_OFFSET];
if (byte_order != DBUS_LITTLE_ENDIAN &&
byte_order != DBUS_BIG_ENDIAN)
@@ -4337,6 +5014,18 @@ _dbus_message_loader_queue_messages (DBusMessageLoader *loader)
return TRUE;
}
+ /* Unknown types are ignored, but INVALID is
+ * disallowed
+ */
+ message_type = header_data[TYPE_OFFSET];
+ if (message_type == DBUS_MESSAGE_TYPE_INVALID)
+ {
+ _dbus_verbose ("Message with bad type '%d' received\n",
+ message_type);
+ loader->corrupted = TRUE;
+ return TRUE;
+ }
+
header_len_unsigned = _dbus_unpack_uint32 (byte_order, header_data + 4);
body_len_unsigned = _dbus_unpack_uint32 (byte_order, header_data + 8);
@@ -4383,14 +5072,16 @@ _dbus_message_loader_queue_messages (DBusMessageLoader *loader)
if (_dbus_string_get_length (&loader->data) >= (header_len + body_len))
{
- HeaderField fields[FIELD_LAST];
+ HeaderField fields[DBUS_HEADER_FIELD_LAST + 1];
int i;
int next_arg;
#if 0
_dbus_verbose_bytes_of_string (&loader->data, 0, header_len + body_len);
#endif
- if (!decode_header_data (&loader->data, header_len, byte_order,
+ if (!decode_header_data (&loader->data,
+ header_len, byte_order,
+ message_type,
fields, &header_padding))
{
_dbus_verbose ("Header was invalid\n");
@@ -4448,7 +5139,7 @@ _dbus_message_loader_queue_messages (DBusMessageLoader *loader)
/* Copy in the offsets we found */
i = 0;
- while (i < FIELD_LAST)
+ while (i <= DBUS_HEADER_FIELD_LAST)
{
message->header_fields[i] = fields[i];
++i;
@@ -4498,9 +5189,12 @@ _dbus_message_loader_queue_messages (DBusMessageLoader *loader)
* earlier)
*/
message->reply_serial = get_uint_field (message,
- FIELD_REPLY_SERIAL);
- message->client_serial = get_uint_field (message,
- FIELD_CLIENT_SERIAL);
+ DBUS_HEADER_FIELD_REPLY_SERIAL);
+
+ message->client_serial = _dbus_demarshal_uint32 (&message->header,
+ message->byte_order,
+ CLIENT_SERIAL_OFFSET,
+ NULL);
_dbus_verbose ("Loaded message %p\n", message);
}
@@ -5076,8 +5770,10 @@ check_message_handling (DBusMessage *message)
client_serial = dbus_message_get_serial (message);
/* can't use set_serial due to the assertions at the start of it */
- set_uint_field (message, FIELD_CLIENT_SERIAL,
- client_serial);
+ _dbus_marshal_set_uint32 (&message->header,
+ message->byte_order,
+ CLIENT_SERIAL_OFFSET,
+ client_serial);
if (client_serial != dbus_message_get_serial (message))
{
@@ -5467,7 +6163,7 @@ process_test_subdir (const DBusString *test_base_dir,
goto failed;
}
- printf ("Testing:\n");
+ printf ("Testing %s:\n", subdir);
next:
while (_dbus_directory_get_next_file (dir, &filename, &error))
@@ -5499,10 +6195,11 @@ process_test_subdir (const DBusString *test_base_dir,
printf (" %s\n",
_dbus_string_get_const_data (&filename));
- _dbus_verbose (" expecting %s\n",
+ _dbus_verbose (" expecting %s for %s\n",
validity == _DBUS_MESSAGE_VALID ? "valid" :
(validity == _DBUS_MESSAGE_INVALID ? "invalid" :
- (validity == _DBUS_MESSAGE_INCOMPLETE ? "incomplete" : "unknown")));
+ (validity == _DBUS_MESSAGE_INCOMPLETE ? "incomplete" : "unknown")),
+ _dbus_string_get_const_data (&filename));
if (! (*function) (&full_path, is_raw, validity, user_data))
{
@@ -5821,26 +6518,37 @@ _dbus_message_test (const char *test_data_dir)
_dbus_assert (sizeof (DBusMessageRealIter) <= sizeof (DBusMessageIter));
- message = dbus_message_new ("test.Message", "org.freedesktop.DBus.Test");
- _dbus_assert (dbus_message_has_destination (message, "org.freedesktop.DBus.Test"));
+ message = dbus_message_new_method_call ("org.freedesktop.DBus.TestService",
+ "/org/freedesktop/TestPath",
+ "Foo.TestInterface",
+ "TestMethod");
+ _dbus_assert (dbus_message_has_destination (message, "org.freedesktop.DBus.TestService"));
+ _dbus_assert (dbus_message_is_method_call (message, "Foo.TestInterface",
+ "TestMethod"));
_dbus_message_set_serial (message, 1234);
- dbus_message_set_sender (message, "org.foo.bar");
- _dbus_assert (dbus_message_has_sender (message, "org.foo.bar"));
+ /* string length including nul byte not a multiple of 4 */
+ dbus_message_set_sender (message, "org.foo.bar1");
+ _dbus_assert (dbus_message_has_sender (message, "org.foo.bar1"));
+ dbus_message_set_reply_serial (message, 5678);
dbus_message_set_sender (message, NULL);
- _dbus_assert (!dbus_message_has_sender (message, "org.foo.bar"));
+ _dbus_assert (!dbus_message_has_sender (message, "org.foo.bar1"));
_dbus_assert (dbus_message_get_serial (message) == 1234);
- _dbus_assert (dbus_message_has_destination (message, "org.freedesktop.DBus.Test"));
+ _dbus_assert (dbus_message_get_reply_serial (message) == 5678);
+ _dbus_assert (dbus_message_has_destination (message, "org.freedesktop.DBus.TestService"));
- _dbus_assert (dbus_message_get_is_error (message) == FALSE);
- dbus_message_set_is_error (message, TRUE);
- _dbus_assert (dbus_message_get_is_error (message) == TRUE);
- dbus_message_set_is_error (message, FALSE);
- _dbus_assert (dbus_message_get_is_error (message) == FALSE);
+ _dbus_assert (dbus_message_get_no_reply (message) == FALSE);
+ dbus_message_set_no_reply (message, TRUE);
+ _dbus_assert (dbus_message_get_no_reply (message) == TRUE);
+ dbus_message_set_no_reply (message, FALSE);
+ _dbus_assert (dbus_message_get_no_reply (message) == FALSE);
dbus_message_unref (message);
/* Test the vararg functions */
- message = dbus_message_new ("test.Message", "org.freedesktop.DBus.Test");
+ message = dbus_message_new_method_call ("org.freedesktop.DBus.TestService",
+ "/org/freedesktop/TestPath",
+ "Foo.TestInterface",
+ "TestMethod");
_dbus_message_set_serial (message, 1);
dbus_message_append_args (message,
DBUS_TYPE_INT32, -0x12345678,
@@ -5855,10 +6563,12 @@ _dbus_message_test (const char *test_data_dir)
_DBUS_N_ELEMENTS (our_uint32_array),
DBUS_TYPE_ARRAY, DBUS_TYPE_INT32, our_int32_array,
_DBUS_N_ELEMENTS (our_int32_array),
+#ifdef DBUS_HAVE_INT64
DBUS_TYPE_ARRAY, DBUS_TYPE_UINT64, our_uint64_array,
_DBUS_N_ELEMENTS (our_uint64_array),
DBUS_TYPE_ARRAY, DBUS_TYPE_INT64, our_int64_array,
_DBUS_N_ELEMENTS (our_int64_array),
+#endif
DBUS_TYPE_ARRAY, DBUS_TYPE_STRING, our_string_array,
_DBUS_N_ELEMENTS (our_string_array),
DBUS_TYPE_ARRAY, DBUS_TYPE_DOUBLE, our_double_array,
@@ -5896,15 +6606,24 @@ _dbus_message_test (const char *test_data_dir)
verify_test_message (copy);
- name1 = dbus_message_get_name (message);
- name2 = dbus_message_get_name (copy);
+ name1 = dbus_message_get_interface (message);
+ name2 = dbus_message_get_interface (copy);
+
+ _dbus_assert (strcmp (name1, name2) == 0);
+
+ name1 = dbus_message_get_member (message);
+ name2 = dbus_message_get_member (copy);
_dbus_assert (strcmp (name1, name2) == 0);
dbus_message_unref (message);
dbus_message_unref (copy);
-
- message = dbus_message_new ("test.Message", "org.freedesktop.DBus.Test");
+
+ message = dbus_message_new_method_call ("org.freedesktop.DBus.TestService",
+ "/org/freedesktop/TestPath",
+ "Foo.TestInterface",
+ "TestMethod");
+
_dbus_message_set_serial (message, 1);
dbus_message_set_reply_serial (message, 0x12345678);
diff --git a/dbus/dbus-message.h b/dbus/dbus-message.h
index 9f07565c..888fe862 100644
--- a/dbus/dbus-message.h
+++ b/dbus/dbus-message.h
@@ -31,6 +31,7 @@
#include <dbus/dbus-types.h>
#include <dbus/dbus-arch-deps.h>
#include <dbus/dbus-memory.h>
+#include <dbus/dbus-errors.h>
#include <stdarg.h>
DBUS_BEGIN_DECLS;
@@ -38,45 +39,74 @@ DBUS_BEGIN_DECLS;
typedef struct DBusMessage DBusMessage;
typedef struct DBusMessageIter DBusMessageIter;
+/**
+ * DBusMessageIter struct; contains no public fields
+ */
struct DBusMessageIter
-{
- void *dummy1;
- void *dummy2;
- dbus_uint32_t dummy3;
- int dummy4;
- int dummy5;
- int dummy6;
- int dummy7;
- int dummy8;
- int dummy9;
- int dummy10;
- int dummy11;
- int pad1;
- int pad2;
- void *pad3;
+{
+ void *dummy1; /**< Don't use this */
+ void *dummy2; /**< Don't use this */
+ dbus_uint32_t dummy3; /**< Don't use this */
+ int dummy4; /**< Don't use this */
+ int dummy5; /**< Don't use this */
+ int dummy6; /**< Don't use this */
+ int dummy7; /**< Don't use this */
+ int dummy8; /**< Don't use this */
+ int dummy9; /**< Don't use this */
+ int dummy10; /**< Don't use this */
+ int dummy11; /**< Don't use this */
+ int pad1; /**< Don't use this */
+ int pad2; /**< Don't use this */
+ void *pad3; /**< Don't use this */
};
+DBusMessage* dbus_message_new (int message_type);
+DBusMessage* dbus_message_new_method_call (const char *service,
+ const char *path,
+ const char *interface,
+ const char *method);
+DBusMessage* dbus_message_new_method_return (DBusMessage *method_call);
+DBusMessage* dbus_message_new_signal (const char *path,
+ const char *interface,
+ const char *name);
+DBusMessage* dbus_message_new_error (DBusMessage *reply_to,
+ const char *error_name,
+ const char *error_message);
-DBusMessage* dbus_message_new (const char *name,
- const char *destination_service);
-DBusMessage* dbus_message_new_reply (DBusMessage *original_message);
-DBusMessage* dbus_message_new_error_reply (DBusMessage *original_message,
- const char *error_name,
- const char *error_message);
-DBusMessage *dbus_message_copy (const DBusMessage *message);
+DBusMessage *dbus_message_copy (const DBusMessage *message);
void dbus_message_ref (DBusMessage *message);
void dbus_message_unref (DBusMessage *message);
-const char* dbus_message_get_name (DBusMessage *message);
+int dbus_message_get_type (DBusMessage *message);
+dbus_bool_t dbus_message_set_path (DBusMessage *message,
+ const char *object_path);
+const char* dbus_message_get_path (DBusMessage *message);
+dbus_bool_t dbus_message_set_interface (DBusMessage *message,
+ const char *interface);
+const char* dbus_message_get_interface (DBusMessage *message);
+dbus_bool_t dbus_message_set_member (DBusMessage *message,
+ const char *member);
+const char* dbus_message_get_member (DBusMessage *message);
+dbus_bool_t dbus_message_set_error_name (DBusMessage *message,
+ const char *name);
+const char* dbus_message_get_error_name (DBusMessage *message);
+dbus_bool_t dbus_message_set_destination (DBusMessage *message,
+ const char *destination);
const char* dbus_message_get_destination (DBusMessage *message);
dbus_bool_t dbus_message_set_sender (DBusMessage *message,
const char *sender);
const char* dbus_message_get_sender (DBusMessage *message);
-void dbus_message_set_is_error (DBusMessage *message,
- dbus_bool_t is_error_reply);
-dbus_bool_t dbus_message_get_is_error (DBusMessage *message);
-dbus_bool_t dbus_message_has_name (DBusMessage *message,
- const char *name);
+void dbus_message_set_no_reply (DBusMessage *message,
+ dbus_bool_t no_reply);
+dbus_bool_t dbus_message_get_no_reply (DBusMessage *message);
+dbus_bool_t dbus_message_is_method_call (DBusMessage *message,
+ const char *interface,
+ const char *method);
+dbus_bool_t dbus_message_is_signal (DBusMessage *message,
+ const char *interface,
+ const char *signal_name);
+dbus_bool_t dbus_message_is_error (DBusMessage *message,
+ const char *error_name);
dbus_bool_t dbus_message_has_destination (DBusMessage *message,
const char *service);
dbus_bool_t dbus_message_has_sender (DBusMessage *message,
@@ -86,6 +116,9 @@ dbus_bool_t dbus_message_set_reply_serial (DBusMessage *message,
dbus_uint32_t reply_serial);
dbus_uint32_t dbus_message_get_reply_serial (DBusMessage *message);
+dbus_bool_t dbus_message_get_path_decomposed (DBusMessage *message,
+ char ***path);
+
dbus_bool_t dbus_message_append_args (DBusMessage *message,
int first_arg_type,
...);
diff --git a/dbus/dbus-object-tree.c b/dbus/dbus-object-tree.c
new file mode 100644
index 00000000..07d3ae59
--- /dev/null
+++ b/dbus/dbus-object-tree.c
@@ -0,0 +1,1481 @@
+/* -*- mode: C; c-file-style: "gnu" -*- */
+/* dbus-object-tree.c DBusObjectTree (internals of DBusConnection)
+ *
+ * Copyright (C) 2003 Red Hat Inc.
+ *
+ * Licensed under the Academic Free License version 1.2
+ *
+ * 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-object-tree.h"
+#include "dbus-connection-internal.h"
+#include "dbus-internals.h"
+#include "dbus-hash.h"
+#include "dbus-protocol.h"
+#include "dbus-string.h"
+#include <string.h>
+#include <stdlib.h>
+
+/**
+ * @defgroup DBusObjectTree A hierarchy of objects with container-contained relationship
+ * @ingroup DBusInternals
+ * @brief DBusObjectTree is used by DBusConnection to track the object tree
+ *
+ * Types and functions related to DBusObjectTree. These
+ * are all library-internal.
+ *
+ * @{
+ */
+
+/** Subnode of the object hierarchy */
+typedef struct DBusObjectSubtree DBusObjectSubtree;
+
+static DBusObjectSubtree* _dbus_object_subtree_new (const char *name,
+ const DBusObjectPathVTable *vtable,
+ void *user_data);
+static void _dbus_object_subtree_ref (DBusObjectSubtree *subtree);
+static void _dbus_object_subtree_unref (DBusObjectSubtree *subtree);
+
+/**
+ * Internals of DBusObjectTree
+ */
+struct DBusObjectTree
+{
+ int refcount; /**< Reference count */
+ DBusConnection *connection; /**< Connection this tree belongs to */
+
+ DBusObjectSubtree *root; /**< Root of the tree ("/" node) */
+};
+
+/**
+ * Struct representing a single registered subtree handler, or node
+ * that's a parent of a registered subtree handler. If
+ * message_function != NULL there's actually a handler at this node.
+ */
+struct DBusObjectSubtree
+{
+ DBusAtomic refcount; /**< Reference count */
+ DBusObjectSubtree *parent; /**< Parent node */
+ DBusObjectPathUnregisterFunction unregister_function; /**< Function to call on unregister */
+ DBusObjectPathMessageFunction message_function; /**< Function to handle messages */
+ void *user_data; /**< Data for functions */
+ DBusObjectSubtree **subtrees; /**< Child nodes */
+ int n_subtrees; /**< Number of child nodes */
+ unsigned int subtrees_sorted : 1; /**< Whether children are sorted */
+ unsigned int invoke_as_fallback : 1; /**< Whether to invoke message_function when child nodes don't handle the message */
+ char name[1]; /**< Allocated as large as necessary */
+};
+
+/**
+ * Creates a new object tree, representing a mapping from paths
+ * to handler vtables.
+ *
+ * @param connection the connection this tree belongs to
+ * @returns the new tree or #NULL if no memory
+ */
+DBusObjectTree*
+_dbus_object_tree_new (DBusConnection *connection)
+{
+ DBusObjectTree *tree;
+
+ /* the connection passed in here isn't fully constructed,
+ * so don't do anything more than store a pointer to
+ * it
+ */
+
+ tree = dbus_new0 (DBusObjectTree, 1);
+ if (tree == NULL)
+ goto oom;
+
+ tree->refcount = 1;
+ tree->connection = connection;
+ tree->root = _dbus_object_subtree_new ("/", NULL, NULL);
+ if (tree->root == NULL)
+ goto oom;
+ tree->root->invoke_as_fallback = TRUE;
+
+ return tree;
+
+ oom:
+ if (tree)
+ {
+ dbus_free (tree);
+ }
+
+ return NULL;
+}
+
+/**
+ * Increment the reference count
+ * @param tree the object tree
+ */
+void
+_dbus_object_tree_ref (DBusObjectTree *tree)
+{
+ _dbus_assert (tree->refcount > 0);
+
+ tree->refcount += 1;
+}
+
+/**
+ * Decrement the reference count
+ * @param tree the object tree
+ */
+void
+_dbus_object_tree_unref (DBusObjectTree *tree)
+{
+ _dbus_assert (tree->refcount > 0);
+
+ tree->refcount -= 1;
+
+ if (tree->refcount == 0)
+ {
+ _dbus_object_tree_free_all_unlocked (tree);
+
+ dbus_free (tree);
+ }
+}
+
+static int
+subtree_cmp (DBusObjectSubtree *subtree_a,
+ DBusObjectSubtree *subtree_b)
+{
+ return strcmp (subtree_a->name, subtree_b->name);
+}
+
+static int
+subtree_qsort_cmp (const void *a,
+ const void *b)
+{
+ DBusObjectSubtree **subtree_a_p = (void*) a;
+ DBusObjectSubtree **subtree_b_p = (void*) b;
+
+ return subtree_cmp (*subtree_a_p, *subtree_b_p);
+}
+
+static void
+ensure_sorted (DBusObjectSubtree *subtree)
+{
+ if (subtree->subtrees && !subtree->subtrees_sorted)
+ {
+ qsort (subtree->subtrees,
+ subtree->n_subtrees,
+ sizeof (DBusObjectSubtree*),
+ subtree_qsort_cmp);
+ subtree->subtrees_sorted = TRUE;
+ }
+}
+
+/** Set to 1 to get a bunch of debug spew about finding the
+ * subtree nodes
+ */
+#define VERBOSE_FIND 0
+
+static DBusObjectSubtree*
+find_subtree_recurse (DBusObjectSubtree *subtree,
+ const char **path,
+ dbus_bool_t return_deepest_match,
+ dbus_bool_t create_if_not_found,
+ int *index_in_parent)
+{
+ int i;
+
+ _dbus_assert (!(return_deepest_match && create_if_not_found));
+
+ if (path[0] == NULL)
+ {
+#if VERBOSE_FIND
+ _dbus_verbose (" path exhausted, returning %s\n",
+ subtree->name);
+#endif
+ return subtree;
+ }
+
+#if VERBOSE_FIND
+ _dbus_verbose (" searching children of %s for %s\n",
+ subtree->name, path[0]);
+#endif
+
+ ensure_sorted (subtree);
+
+ /* FIXME we should do a binary search here instead
+ * of O(n)
+ */
+
+ i = 0;
+ while (i < subtree->n_subtrees)
+ {
+ int v;
+
+ v = strcmp (path[0], subtree->subtrees[i]->name);
+
+#if VERBOSE_FIND
+ _dbus_verbose (" %s cmp %s = %d\n",
+ path[0], subtree->subtrees[i]->name,
+ v);
+#endif
+
+ if (v == 0)
+ {
+ if (index_in_parent)
+ {
+#if VERBOSE_FIND
+ _dbus_verbose (" storing parent index %d\n", i);
+#endif
+ *index_in_parent = i;
+ }
+
+ if (return_deepest_match)
+ {
+ DBusObjectSubtree *next;
+
+ next = find_subtree_recurse (subtree->subtrees[i],
+ &path[1], return_deepest_match,
+ create_if_not_found, index_in_parent);
+ if (next == NULL &&
+ subtree->invoke_as_fallback)
+ {
+#if VERBOSE_FIND
+ _dbus_verbose (" no deeper match found, returning %s\n",
+ subtree->name);
+#endif
+ return subtree;
+ }
+ else
+ return next;
+ }
+ else
+ return find_subtree_recurse (subtree->subtrees[i],
+ &path[1], return_deepest_match,
+ create_if_not_found, index_in_parent);
+ }
+ else if (v < 0)
+ {
+ goto not_found;
+ }
+
+ ++i;
+ }
+
+ not_found:
+#if VERBOSE_FIND
+ _dbus_verbose (" no match found, current tree %s, create_if_not_found = %d\n",
+ subtree->name, create_if_not_found);
+#endif
+
+ if (create_if_not_found)
+ {
+ DBusObjectSubtree* child;
+ DBusObjectSubtree **new_subtrees;
+ int new_n_subtrees;
+
+#if VERBOSE_FIND
+ _dbus_verbose (" creating subtree %s\n",
+ path[0]);
+#endif
+
+ child = _dbus_object_subtree_new (path[0],
+ NULL, NULL);
+ if (child == NULL)
+ return NULL;
+
+ /* FIXME we should do the "double alloc each time" standard thing */
+ new_n_subtrees = subtree->n_subtrees + 1;
+ new_subtrees = dbus_realloc (subtree->subtrees,
+ new_n_subtrees * sizeof (DBusObjectSubtree*));
+ if (new_subtrees == NULL)
+ {
+ child->unregister_function = NULL;
+ child->message_function = NULL;
+ _dbus_object_subtree_unref (child);
+ return FALSE;
+ }
+
+ new_subtrees[subtree->n_subtrees] = child;
+ if (index_in_parent)
+ *index_in_parent = subtree->n_subtrees;
+ subtree->subtrees_sorted = FALSE;
+ subtree->n_subtrees = new_n_subtrees;
+ subtree->subtrees = new_subtrees;
+
+ child->parent = subtree;
+
+ return find_subtree_recurse (child,
+ &path[1], return_deepest_match,
+ create_if_not_found, index_in_parent);
+ }
+ else
+ return (return_deepest_match && subtree->invoke_as_fallback) ? subtree : NULL;
+}
+
+static DBusObjectSubtree*
+find_subtree (DBusObjectTree *tree,
+ const char **path,
+ int *index_in_parent)
+{
+ DBusObjectSubtree *subtree;
+
+#if VERBOSE_FIND
+ _dbus_verbose ("Looking for exact registered subtree\n");
+#endif
+
+ subtree = find_subtree_recurse (tree->root, path, FALSE, FALSE, index_in_parent);
+
+ if (subtree && subtree->message_function == NULL)
+ return NULL;
+ else
+ return subtree;
+}
+
+static DBusObjectSubtree*
+find_handler (DBusObjectTree *tree,
+ const char **path)
+{
+#if VERBOSE_FIND
+ _dbus_verbose ("Looking for deepest handler\n");
+#endif
+ return find_subtree_recurse (tree->root, path, TRUE, FALSE, NULL);
+}
+
+static DBusObjectSubtree*
+ensure_subtree (DBusObjectTree *tree,
+ const char **path)
+{
+#if VERBOSE_FIND
+ _dbus_verbose ("Ensuring subtree\n");
+#endif
+ return find_subtree_recurse (tree->root, path, FALSE, TRUE, NULL);
+}
+
+/**
+ * Registers a new subtree in the global object tree.
+ *
+ * @param tree the global object tree
+ * @param fallback #TRUE to handle messages to children of this path
+ * @param path NULL-terminated array of path elements giving path to subtree
+ * @param vtable the vtable used to traverse this subtree
+ * @param user_data user data to pass to methods in the vtable
+ * @returns #FALSE if not enough memory
+ */
+dbus_bool_t
+_dbus_object_tree_register (DBusObjectTree *tree,
+ dbus_bool_t fallback,
+ const char **path,
+ const DBusObjectPathVTable *vtable,
+ void *user_data)
+{
+ DBusObjectSubtree *subtree;
+
+ _dbus_assert (tree != NULL);
+ _dbus_assert (vtable->message_function != NULL);
+ _dbus_assert (path != NULL);
+
+ subtree = ensure_subtree (tree, path);
+ if (subtree == NULL)
+ return FALSE;
+
+#ifndef DBUS_DISABLE_CHECKS
+ if (subtree->message_function != NULL)
+ {
+ _dbus_warn ("A handler is already registered for the path starting with path[0] = \"%s\"\n",
+ path[0] ? path[0] : "null");
+ return FALSE;
+ }
+#else
+ _dbus_assert (subtree->message_function == NULL);
+#endif
+
+ subtree->message_function = vtable->message_function;
+ subtree->unregister_function = vtable->unregister_function;
+ subtree->user_data = user_data;
+ subtree->invoke_as_fallback = fallback != FALSE;
+
+ return TRUE;
+}
+
+/**
+ * Unregisters an object subtree that was registered with the
+ * same path.
+ *
+ * @param tree the global object tree
+ * @param path path to the subtree (same as the one passed to _dbus_object_tree_register())
+ */
+void
+_dbus_object_tree_unregister_and_unlock (DBusObjectTree *tree,
+ const char **path)
+{
+ int i;
+ DBusObjectSubtree *subtree;
+ DBusObjectPathUnregisterFunction unregister_function;
+ void *user_data;
+ DBusConnection *connection;
+
+ _dbus_assert (path != NULL);
+
+ subtree = find_subtree (tree, path, &i);
+
+#ifndef DBUS_DISABLE_CHECKS
+ if (subtree == NULL)
+ {
+ _dbus_warn ("Attempted to unregister path (path[0] = %s path[1] = %s) which isn't registered\n",
+ path[0] ? path[0] : "null",
+ path[1] ? path[1] : "null");
+ return;
+ }
+#else
+ _dbus_assert (subtree != NULL);
+#endif
+
+ _dbus_assert (subtree->parent == NULL ||
+ (i >= 0 && subtree->parent->subtrees[i] == subtree));
+
+ subtree->message_function = NULL;
+
+ unregister_function = subtree->unregister_function;
+ user_data = subtree->user_data;
+
+ subtree->unregister_function = NULL;
+ subtree->user_data = NULL;
+
+ /* If we have no subtrees of our own, remove from
+ * our parent (FIXME could also be more aggressive
+ * and remove our parent if it becomes empty)
+ */
+ if (subtree->parent && subtree->n_subtrees == 0)
+ {
+ /* assumes a 0-byte memmove is OK */
+ memmove (&subtree->parent->subtrees[i],
+ &subtree->parent->subtrees[i+1],
+ (subtree->parent->n_subtrees - i - 1) *
+ sizeof (subtree->parent->subtrees[0]));
+ subtree->parent->n_subtrees -= 1;
+
+ subtree->parent = NULL;
+
+ _dbus_object_subtree_unref (subtree);
+ }
+ subtree = NULL;
+
+ connection = tree->connection;
+
+ /* Unlock and call application code */
+#ifdef DBUS_BUILD_TESTS
+ if (connection)
+#endif
+ {
+ _dbus_connection_ref_unlocked (connection);
+ _dbus_connection_unlock (connection);
+ }
+
+ if (unregister_function)
+ (* unregister_function) (connection, user_data);
+
+#ifdef DBUS_BUILD_TESTS
+ if (connection)
+#endif
+ dbus_connection_unref (connection);
+}
+
+static void
+free_subtree_recurse (DBusConnection *connection,
+ DBusObjectSubtree *subtree)
+{
+ /* Delete them from the end, for slightly
+ * more robustness against odd reentrancy.
+ */
+ while (subtree->n_subtrees > 0)
+ {
+ DBusObjectSubtree *child;
+
+ child = subtree->subtrees[subtree->n_subtrees - 1];
+ subtree->subtrees[subtree->n_subtrees - 1] = NULL;
+ subtree->n_subtrees -= 1;
+ child->parent = NULL;
+
+ free_subtree_recurse (connection, child);
+ }
+
+ /* Call application code */
+ if (subtree->unregister_function)
+ {
+ (* subtree->unregister_function) (connection,
+ subtree->user_data);
+ subtree->message_function = NULL;
+ subtree->unregister_function = NULL;
+ subtree->user_data = NULL;
+ }
+
+ /* Now free ourselves */
+ _dbus_object_subtree_unref (subtree);
+}
+
+/**
+ * Free all the handlers in the tree. Lock on tree's connection
+ * must not be held.
+ *
+ * @param tree the object tree
+ */
+void
+_dbus_object_tree_free_all_unlocked (DBusObjectTree *tree)
+{
+ if (tree->root)
+ free_subtree_recurse (tree->connection,
+ tree->root);
+ tree->root = NULL;
+}
+
+static dbus_bool_t
+_dbus_object_tree_list_registered_unlocked (DBusObjectTree *tree,
+ const char **parent_path,
+ char ***child_entries)
+{
+ DBusObjectSubtree *subtree;
+ char **retval;
+
+ _dbus_assert (parent_path != NULL);
+ _dbus_assert (child_entries != NULL);
+
+ *child_entries = NULL;
+
+ subtree = find_subtree (tree, parent_path, NULL);
+ if (subtree == NULL)
+ {
+ retval = dbus_new0 (char *, 1);
+ if (retval == NULL)
+ goto out;
+ }
+ else
+ {
+ int i;
+ retval = dbus_new0 (char*, subtree->n_subtrees + 1);
+ if (retval == NULL)
+ goto out;
+ i = 0;
+ while (i < subtree->n_subtrees)
+ {
+ retval[i] = _dbus_strdup (subtree->subtrees[i]->name);
+ if (retval[i] == NULL)
+ {
+ dbus_free_string_array (retval);
+ retval = NULL;
+ goto out;
+ }
+ ++i;
+ }
+ }
+
+ out:
+
+ *child_entries = retval;
+ return retval != NULL;
+}
+
+static DBusHandlerResult
+handle_default_introspect_unlocked (DBusObjectTree *tree,
+ DBusMessage *message,
+ const char **path)
+{
+ DBusString xml;
+ DBusHandlerResult result;
+ char **children;
+ int i;
+
+ if (!dbus_message_is_method_call (message,
+ DBUS_INTERFACE_ORG_FREEDESKTOP_INTROSPECTABLE,
+ "Introspect"))
+ return DBUS_HANDLER_RESULT_NOT_YET_HANDLED;
+
+ if (!_dbus_string_init (&xml))
+ return DBUS_HANDLER_RESULT_NEED_MEMORY;
+
+ result = DBUS_HANDLER_RESULT_NEED_MEMORY;
+
+ children = NULL;
+ if (!_dbus_object_tree_list_registered_unlocked (tree, path, &children))
+ goto out;
+
+ if (!_dbus_string_append (&xml, "<node>\n"))
+ goto out;
+
+ i = 0;
+ while (children[i] != NULL)
+ {
+ if (!_dbus_string_append_printf (&xml, " <node name=\"%s\"/>\n",
+ children[i]))
+ goto out;
+
+ ++i;
+ }
+
+ if (!_dbus_string_append (&xml, "</node>\n"))
+ goto out;
+
+ result = DBUS_HANDLER_RESULT_HANDLED;
+
+ out:
+ _dbus_string_free (&xml);
+ dbus_free_string_array (children);
+
+ return result;
+}
+
+/**
+ * Tries to dispatch a message by directing it to handler for the
+ * object path listed in the message header, if any. Messages are
+ * dispatched first to the registered handler that matches the largest
+ * number of path elements; that is, message to /foo/bar/baz would go
+ * to the handler for /foo/bar before the one for /foo.
+ *
+ * @todo thread problems
+ *
+ * @param tree the global object tree
+ * @param message the message to dispatch
+ * @returns whether message was handled successfully
+ */
+DBusHandlerResult
+_dbus_object_tree_dispatch_and_unlock (DBusObjectTree *tree,
+ DBusMessage *message)
+{
+ char **path;
+ DBusList *list;
+ DBusList *link;
+ DBusHandlerResult result;
+ DBusObjectSubtree *subtree;
+
+#if 0
+ _dbus_verbose ("Dispatch of message by object path\n");
+#endif
+
+ path = NULL;
+ if (!dbus_message_get_path_decomposed (message, &path))
+ {
+#ifdef DBUS_BUILD_TESTS
+ if (tree->connection)
+#endif
+ _dbus_connection_unlock (tree->connection);
+
+ _dbus_verbose ("No memory to get decomposed path\n");
+
+ return DBUS_HANDLER_RESULT_NEED_MEMORY;
+ }
+
+ if (path == NULL)
+ {
+#ifdef DBUS_BUILD_TESTS
+ if (tree->connection)
+#endif
+ _dbus_connection_unlock (tree->connection);
+
+ _dbus_verbose ("No path field in message\n");
+ return DBUS_HANDLER_RESULT_NOT_YET_HANDLED;
+ }
+
+ /* Find the deepest path that covers the path in the message */
+ subtree = find_handler (tree, (const char**) path);
+
+ /* Build a list of all paths that cover the path in the message */
+
+ list = NULL;
+
+ while (subtree != NULL)
+ {
+ if (subtree->message_function != NULL)
+ {
+ _dbus_object_subtree_ref (subtree);
+
+ /* run deepest paths first */
+ if (!_dbus_list_append (&list, subtree))
+ {
+ result = DBUS_HANDLER_RESULT_NEED_MEMORY;
+ _dbus_object_subtree_unref (subtree);
+ goto free_and_return;
+ }
+ }
+
+ subtree = subtree->parent;
+ }
+
+ _dbus_verbose ("%d handlers in the path tree for this message\n",
+ _dbus_list_get_length (&list));
+
+ /* Invoke each handler in the list */
+
+ result = DBUS_HANDLER_RESULT_NOT_YET_HANDLED;
+
+ link = _dbus_list_get_first_link (&list);
+ while (link != NULL)
+ {
+ DBusList *next = _dbus_list_get_next_link (&list, link);
+ subtree = link->data;
+
+ /* message_function is NULL if we're unregistered
+ * due to reentrancy
+ */
+ if (subtree->message_function)
+ {
+ DBusObjectPathMessageFunction message_function;
+ void *user_data;
+
+ message_function = subtree->message_function;
+ user_data = subtree->user_data;
+
+#if 0
+ _dbus_verbose (" (invoking a handler)\n");
+#endif
+
+#ifdef DBUS_BUILD_TESTS
+ if (tree->connection)
+#endif
+ _dbus_connection_unlock (tree->connection);
+
+ /* FIXME you could unregister the subtree in another thread
+ * before we invoke the callback, and I can't figure out a
+ * good way to solve this.
+ */
+
+ result = (* message_function) (tree->connection,
+ message,
+ user_data);
+
+#ifdef DBUS_BUILD_TESTS
+ if (tree->connection)
+#endif
+ _dbus_connection_lock (tree->connection);
+
+ if (result != DBUS_HANDLER_RESULT_NOT_YET_HANDLED)
+ goto free_and_return;
+ }
+
+ link = next;
+ }
+
+ free_and_return:
+
+ if (result == DBUS_HANDLER_RESULT_NOT_YET_HANDLED)
+ {
+ /* This hardcoded default handler does a minimal Introspect()
+ */
+ result = handle_default_introspect_unlocked (tree, message,
+ (const char**) path);
+ }
+
+#ifdef DBUS_BUILD_TESTS
+ if (tree->connection)
+#endif
+ _dbus_connection_unlock (tree->connection);
+
+ while (list != NULL)
+ {
+ link = _dbus_list_get_first_link (&list);
+ _dbus_object_subtree_unref (link->data);
+ _dbus_list_remove_link (&list, link);
+ }
+
+ dbus_free_string_array (path);
+
+ return result;
+}
+
+/**
+ * Allocates a subtree object.
+ *
+ * @param name name to duplicate.
+ * @returns newly-allocated subtree
+ */
+static DBusObjectSubtree*
+allocate_subtree_object (const char *name)
+{
+ int len;
+ DBusObjectSubtree *subtree;
+ const size_t front_padding = _DBUS_STRUCT_OFFSET (DBusObjectSubtree, name);
+
+ _dbus_assert (name != NULL);
+
+ len = strlen (name);
+
+ subtree = dbus_malloc (front_padding + (len + 1));
+
+ if (subtree == NULL)
+ return NULL;
+
+ memcpy (subtree->name, name, len + 1);
+
+ return subtree;
+}
+
+static DBusObjectSubtree*
+_dbus_object_subtree_new (const char *name,
+ const DBusObjectPathVTable *vtable,
+ void *user_data)
+{
+ DBusObjectSubtree *subtree;
+
+ subtree = allocate_subtree_object (name);
+ if (subtree == NULL)
+ goto oom;
+
+ _dbus_assert (name != NULL);
+
+ subtree->parent = NULL;
+
+ if (vtable)
+ {
+ subtree->message_function = vtable->message_function;
+ subtree->unregister_function = vtable->unregister_function;
+ }
+ else
+ {
+ subtree->message_function = NULL;
+ subtree->unregister_function = NULL;
+ }
+
+ subtree->user_data = user_data;
+ subtree->refcount.value = 1;
+ subtree->subtrees = NULL;
+ subtree->n_subtrees = 0;
+ subtree->subtrees_sorted = TRUE;
+
+ return subtree;
+
+ oom:
+ if (subtree)
+ {
+ dbus_free (subtree);
+ }
+
+ return NULL;
+}
+
+static void
+_dbus_object_subtree_ref (DBusObjectSubtree *subtree)
+{
+ _dbus_assert (subtree->refcount.value > 0);
+ _dbus_atomic_inc (&subtree->refcount);
+}
+
+static void
+_dbus_object_subtree_unref (DBusObjectSubtree *subtree)
+{
+ _dbus_assert (subtree->refcount.value > 0);
+
+ if (_dbus_atomic_dec (&subtree->refcount) == 1)
+ {
+ _dbus_assert (subtree->unregister_function == NULL);
+ _dbus_assert (subtree->message_function == NULL);
+
+ dbus_free (subtree->subtrees);
+ dbus_free (subtree);
+ }
+}
+
+/**
+ * Lists the registered fallback handlers and object path handlers at
+ * the given parent_path. The returned array should be freed with
+ * dbus_free_string_array().
+ *
+ * @param connection the connection
+ * @param parent_path the path to list the child handlers of
+ * @param child_entries returns #NULL-terminated array of children
+ * @returns #FALSE if no memory to allocate the child entries
+ */
+dbus_bool_t
+_dbus_object_tree_list_registered_and_unlock (DBusObjectTree *tree,
+ const char **parent_path,
+ char ***child_entries)
+{
+ dbus_bool_t result;
+
+ result = _dbus_object_tree_list_registered_unlocked (tree,
+ parent_path,
+ child_entries);
+
+#ifdef DBUS_BUILD_TESTS
+ if (tree->connection)
+#endif
+ _dbus_connection_unlock (tree->connection);
+
+ return result;
+}
+
+/** @} */
+
+#ifdef DBUS_BUILD_TESTS
+#include "dbus-test.h"
+#include <stdio.h>
+
+static char*
+flatten_path (const char **path)
+{
+ DBusString str;
+ int i;
+ char *s;
+
+ if (!_dbus_string_init (&str))
+ return NULL;
+
+ i = 0;
+ while (path[i])
+ {
+ if (!_dbus_string_append_byte (&str, '/'))
+ goto nomem;
+
+ if (!_dbus_string_append (&str, path[i]))
+ goto nomem;
+
+ ++i;
+ }
+
+ if (!_dbus_string_steal_data (&str, &s))
+ goto nomem;
+
+ _dbus_string_free (&str);
+
+ return s;
+
+ nomem:
+ _dbus_string_free (&str);
+ return NULL;
+}
+
+/* Returns TRUE if container is a parent of child
+ */
+static dbus_bool_t
+path_contains (const char **container,
+ const char **child)
+{
+ int i;
+
+ i = 0;
+ while (child[i] != NULL)
+ {
+ int v;
+
+ if (container[i] == NULL)
+ return TRUE; /* container ran out, child continues;
+ * thus the container is a parent of the
+ * child.
+ */
+
+ _dbus_assert (container[i] != NULL);
+ _dbus_assert (child[i] != NULL);
+
+ v = strcmp (container[i], child[i]);
+
+ if (v != 0)
+ return FALSE; /* they overlap until here and then are different,
+ * not overlapping
+ */
+
+ ++i;
+ }
+
+ /* Child ran out; if container also did, they are equal;
+ * otherwise, the child is a parent of the container.
+ */
+ if (container[i] == NULL)
+ return TRUE; /* equal is counted as containing */
+ else
+ return FALSE;
+}
+
+static void
+spew_subtree_recurse (DBusObjectSubtree *subtree,
+ int indent)
+{
+ int i;
+
+ i = 0;
+ while (i < indent)
+ {
+ _dbus_verbose (" ");
+ ++i;
+ }
+
+ _dbus_verbose ("%s (%d children)\n",
+ subtree->name, subtree->n_subtrees);
+
+ i = 0;
+ while (i < subtree->n_subtrees)
+ {
+ spew_subtree_recurse (subtree->subtrees[i], indent + 2);
+
+ ++i;
+ }
+}
+
+static void
+spew_tree (DBusObjectTree *tree)
+{
+ spew_subtree_recurse (tree->root, 0);
+}
+
+/**
+ * Callback data used in tests
+ */
+typedef struct
+{
+ const char **path; /**< Path */
+ dbus_bool_t message_handled; /**< Gets set to true if message handler called */
+ dbus_bool_t handler_unregistered; /**< gets set to true if handler is unregistered */
+
+} TreeTestData;
+
+
+static void
+test_unregister_function (DBusConnection *connection,
+ void *user_data)
+{
+ TreeTestData *ttd = user_data;
+
+ ttd->handler_unregistered = TRUE;
+}
+
+static DBusHandlerResult
+test_message_function (DBusConnection *connection,
+ DBusMessage *message,
+ void *user_data)
+{
+ TreeTestData *ttd = user_data;
+
+ ttd->message_handled = TRUE;
+
+ return DBUS_HANDLER_RESULT_NOT_YET_HANDLED;
+}
+
+static dbus_bool_t
+do_register (DBusObjectTree *tree,
+ const char **path,
+ int i,
+ TreeTestData *tree_test_data)
+{
+ DBusObjectPathVTable vtable = { test_unregister_function,
+ test_message_function, NULL };
+
+ tree_test_data[i].message_handled = FALSE;
+ tree_test_data[i].handler_unregistered = FALSE;
+ tree_test_data[i].path = path;
+
+ if (!_dbus_object_tree_register (tree, TRUE, path,
+ &vtable,
+ &tree_test_data[i]))
+ return FALSE;
+
+ return TRUE;
+}
+
+static dbus_bool_t
+do_test_dispatch (DBusObjectTree *tree,
+ const char **path,
+ int i,
+ TreeTestData *tree_test_data,
+ int n_test_data)
+{
+ DBusMessage *message;
+ int j;
+ DBusHandlerResult result;
+ char *flat;
+
+ message = NULL;
+
+ flat = flatten_path (path);
+ if (flat == NULL)
+ goto oom;
+
+ message = dbus_message_new_method_call (NULL,
+ flat,
+ "org.freedesktop.TestInterface",
+ "Foo");
+ dbus_free (flat);
+ if (message == NULL)
+ goto oom;
+
+ j = 0;
+ while (j < n_test_data)
+ {
+ tree_test_data[j].message_handled = FALSE;
+ ++j;
+ }
+
+ result = _dbus_object_tree_dispatch_and_unlock (tree, message);
+ if (result == DBUS_HANDLER_RESULT_NEED_MEMORY)
+ goto oom;
+
+ _dbus_assert (tree_test_data[i].message_handled);
+
+ j = 0;
+ while (j < n_test_data)
+ {
+ if (tree_test_data[j].message_handled)
+ _dbus_assert (path_contains (tree_test_data[j].path,
+ path));
+ else
+ _dbus_assert (!path_contains (tree_test_data[j].path,
+ path));
+
+ ++j;
+ }
+
+ dbus_message_unref (message);
+
+ return TRUE;
+
+ oom:
+ if (message)
+ dbus_message_unref (message);
+ return FALSE;
+}
+
+static dbus_bool_t
+object_tree_test_iteration (void *data)
+{
+ const char *path1[] = { "foo", NULL };
+ const char *path2[] = { "foo", "bar", NULL };
+ const char *path3[] = { "foo", "bar", "baz", NULL };
+ const char *path4[] = { "foo", "bar", "boo", NULL };
+ const char *path5[] = { "blah", NULL };
+ const char *path6[] = { "blah", "boof", NULL };
+ const char *path7[] = { "blah", "boof", "this", "is", "really", "long", NULL };
+ const char *path8[] = { "childless", NULL };
+ DBusObjectTree *tree;
+ TreeTestData tree_test_data[8];
+ int i;
+
+ tree = NULL;
+
+ tree = _dbus_object_tree_new (NULL);
+ if (tree == NULL)
+ goto out;
+
+ if (!do_register (tree, path1, 0, tree_test_data))
+ goto out;
+
+ _dbus_assert (find_subtree (tree, path1, NULL));
+ _dbus_assert (!find_subtree (tree, path2, NULL));
+ _dbus_assert (!find_subtree (tree, path3, NULL));
+ _dbus_assert (!find_subtree (tree, path4, NULL));
+ _dbus_assert (!find_subtree (tree, path5, NULL));
+ _dbus_assert (!find_subtree (tree, path6, NULL));
+ _dbus_assert (!find_subtree (tree, path7, NULL));
+ _dbus_assert (!find_subtree (tree, path8, NULL));
+
+ _dbus_assert (find_handler (tree, path1));
+ _dbus_assert (find_handler (tree, path2));
+ _dbus_assert (find_handler (tree, path3));
+ _dbus_assert (find_handler (tree, path4));
+ _dbus_assert (find_handler (tree, path5) == tree->root);
+ _dbus_assert (find_handler (tree, path6) == tree->root);
+ _dbus_assert (find_handler (tree, path7) == tree->root);
+ _dbus_assert (find_handler (tree, path8) == tree->root);
+
+ if (!do_register (tree, path2, 1, tree_test_data))
+ goto out;
+
+ _dbus_assert (find_subtree (tree, path1, NULL));
+ _dbus_assert (find_subtree (tree, path2, NULL));
+ _dbus_assert (!find_subtree (tree, path3, NULL));
+ _dbus_assert (!find_subtree (tree, path4, NULL));
+ _dbus_assert (!find_subtree (tree, path5, NULL));
+ _dbus_assert (!find_subtree (tree, path6, NULL));
+ _dbus_assert (!find_subtree (tree, path7, NULL));
+ _dbus_assert (!find_subtree (tree, path8, NULL));
+
+ if (!do_register (tree, path3, 2, tree_test_data))
+ goto out;
+
+ _dbus_assert (find_subtree (tree, path1, NULL));
+ _dbus_assert (find_subtree (tree, path2, NULL));
+ _dbus_assert (find_subtree (tree, path3, NULL));
+ _dbus_assert (!find_subtree (tree, path4, NULL));
+ _dbus_assert (!find_subtree (tree, path5, NULL));
+ _dbus_assert (!find_subtree (tree, path6, NULL));
+ _dbus_assert (!find_subtree (tree, path7, NULL));
+ _dbus_assert (!find_subtree (tree, path8, NULL));
+
+ if (!do_register (tree, path4, 3, tree_test_data))
+ goto out;
+
+ _dbus_assert (find_subtree (tree, path1, NULL));
+ _dbus_assert (find_subtree (tree, path2, NULL));
+ _dbus_assert (find_subtree (tree, path3, NULL));
+ _dbus_assert (find_subtree (tree, path4, NULL));
+ _dbus_assert (!find_subtree (tree, path5, NULL));
+ _dbus_assert (!find_subtree (tree, path6, NULL));
+ _dbus_assert (!find_subtree (tree, path7, NULL));
+ _dbus_assert (!find_subtree (tree, path8, NULL));
+
+ if (!do_register (tree, path5, 4, tree_test_data))
+ goto out;
+
+ _dbus_assert (find_subtree (tree, path1, NULL));
+ _dbus_assert (find_subtree (tree, path2, NULL));
+ _dbus_assert (find_subtree (tree, path3, NULL));
+ _dbus_assert (find_subtree (tree, path4, NULL));
+ _dbus_assert (find_subtree (tree, path5, NULL));
+ _dbus_assert (!find_subtree (tree, path6, NULL));
+ _dbus_assert (!find_subtree (tree, path7, NULL));
+ _dbus_assert (!find_subtree (tree, path8, NULL));
+
+ _dbus_assert (find_handler (tree, path1) != tree->root);
+ _dbus_assert (find_handler (tree, path2) != tree->root);
+ _dbus_assert (find_handler (tree, path3) != tree->root);
+ _dbus_assert (find_handler (tree, path4) != tree->root);
+ _dbus_assert (find_handler (tree, path5) != tree->root);
+ _dbus_assert (find_handler (tree, path6) != tree->root);
+ _dbus_assert (find_handler (tree, path7) != tree->root);
+ _dbus_assert (find_handler (tree, path8) == tree->root);
+
+ if (!do_register (tree, path6, 5, tree_test_data))
+ goto out;
+
+ _dbus_assert (find_subtree (tree, path1, NULL));
+ _dbus_assert (find_subtree (tree, path2, NULL));
+ _dbus_assert (find_subtree (tree, path3, NULL));
+ _dbus_assert (find_subtree (tree, path4, NULL));
+ _dbus_assert (find_subtree (tree, path5, NULL));
+ _dbus_assert (find_subtree (tree, path6, NULL));
+ _dbus_assert (!find_subtree (tree, path7, NULL));
+ _dbus_assert (!find_subtree (tree, path8, NULL));
+
+ if (!do_register (tree, path7, 6, tree_test_data))
+ goto out;
+
+ _dbus_assert (find_subtree (tree, path1, NULL));
+ _dbus_assert (find_subtree (tree, path2, NULL));
+ _dbus_assert (find_subtree (tree, path3, NULL));
+ _dbus_assert (find_subtree (tree, path4, NULL));
+ _dbus_assert (find_subtree (tree, path5, NULL));
+ _dbus_assert (find_subtree (tree, path6, NULL));
+ _dbus_assert (find_subtree (tree, path7, NULL));
+ _dbus_assert (!find_subtree (tree, path8, NULL));
+
+ if (!do_register (tree, path8, 7, tree_test_data))
+ goto out;
+
+ _dbus_assert (find_subtree (tree, path1, NULL));
+ _dbus_assert (find_subtree (tree, path2, NULL));
+ _dbus_assert (find_subtree (tree, path3, NULL));
+ _dbus_assert (find_subtree (tree, path4, NULL));
+ _dbus_assert (find_subtree (tree, path5, NULL));
+ _dbus_assert (find_subtree (tree, path6, NULL));
+ _dbus_assert (find_subtree (tree, path7, NULL));
+ _dbus_assert (find_subtree (tree, path8, NULL));
+
+ _dbus_assert (find_handler (tree, path1) != tree->root);
+ _dbus_assert (find_handler (tree, path2) != tree->root);
+ _dbus_assert (find_handler (tree, path3) != tree->root);
+ _dbus_assert (find_handler (tree, path4) != tree->root);
+ _dbus_assert (find_handler (tree, path5) != tree->root);
+ _dbus_assert (find_handler (tree, path6) != tree->root);
+ _dbus_assert (find_handler (tree, path7) != tree->root);
+ _dbus_assert (find_handler (tree, path8) != tree->root);
+
+ /* Check that destroying tree calls unregister funcs */
+ _dbus_object_tree_unref (tree);
+
+ i = 0;
+ while (i < (int) _DBUS_N_ELEMENTS (tree_test_data))
+ {
+ _dbus_assert (tree_test_data[i].handler_unregistered);
+ _dbus_assert (!tree_test_data[i].message_handled);
+ ++i;
+ }
+
+ /* Now start again and try the individual unregister function */
+ tree = _dbus_object_tree_new (NULL);
+ if (tree == NULL)
+ goto out;
+
+ if (!do_register (tree, path1, 0, tree_test_data))
+ goto out;
+ if (!do_register (tree, path2, 1, tree_test_data))
+ goto out;
+ if (!do_register (tree, path3, 2, tree_test_data))
+ goto out;
+ if (!do_register (tree, path4, 3, tree_test_data))
+ goto out;
+ if (!do_register (tree, path5, 4, tree_test_data))
+ goto out;
+ if (!do_register (tree, path6, 5, tree_test_data))
+ goto out;
+ if (!do_register (tree, path7, 6, tree_test_data))
+ goto out;
+ if (!do_register (tree, path8, 7, tree_test_data))
+ goto out;
+
+ _dbus_object_tree_unregister_and_unlock (tree, path1);
+
+ _dbus_assert (!find_subtree (tree, path1, NULL));
+ _dbus_assert (find_subtree (tree, path2, NULL));
+ _dbus_assert (find_subtree (tree, path3, NULL));
+ _dbus_assert (find_subtree (tree, path4, NULL));
+ _dbus_assert (find_subtree (tree, path5, NULL));
+ _dbus_assert (find_subtree (tree, path6, NULL));
+ _dbus_assert (find_subtree (tree, path7, NULL));
+ _dbus_assert (find_subtree (tree, path8, NULL));
+
+ _dbus_object_tree_unregister_and_unlock (tree, path2);
+
+ _dbus_assert (!find_subtree (tree, path1, NULL));
+ _dbus_assert (!find_subtree (tree, path2, NULL));
+ _dbus_assert (find_subtree (tree, path3, NULL));
+ _dbus_assert (find_subtree (tree, path4, NULL));
+ _dbus_assert (find_subtree (tree, path5, NULL));
+ _dbus_assert (find_subtree (tree, path6, NULL));
+ _dbus_assert (find_subtree (tree, path7, NULL));
+ _dbus_assert (find_subtree (tree, path8, NULL));
+
+ _dbus_object_tree_unregister_and_unlock (tree, path3);
+
+ _dbus_assert (!find_subtree (tree, path1, NULL));
+ _dbus_assert (!find_subtree (tree, path2, NULL));
+ _dbus_assert (!find_subtree (tree, path3, NULL));
+ _dbus_assert (find_subtree (tree, path4, NULL));
+ _dbus_assert (find_subtree (tree, path5, NULL));
+ _dbus_assert (find_subtree (tree, path6, NULL));
+ _dbus_assert (find_subtree (tree, path7, NULL));
+ _dbus_assert (find_subtree (tree, path8, NULL));
+
+ _dbus_object_tree_unregister_and_unlock (tree, path4);
+
+ _dbus_assert (!find_subtree (tree, path1, NULL));
+ _dbus_assert (!find_subtree (tree, path2, NULL));
+ _dbus_assert (!find_subtree (tree, path3, NULL));
+ _dbus_assert (!find_subtree (tree, path4, NULL));
+ _dbus_assert (find_subtree (tree, path5, NULL));
+ _dbus_assert (find_subtree (tree, path6, NULL));
+ _dbus_assert (find_subtree (tree, path7, NULL));
+ _dbus_assert (find_subtree (tree, path8, NULL));
+
+ _dbus_object_tree_unregister_and_unlock (tree, path5);
+
+ _dbus_assert (!find_subtree (tree, path1, NULL));
+ _dbus_assert (!find_subtree (tree, path2, NULL));
+ _dbus_assert (!find_subtree (tree, path3, NULL));
+ _dbus_assert (!find_subtree (tree, path4, NULL));
+ _dbus_assert (!find_subtree (tree, path5, NULL));
+ _dbus_assert (find_subtree (tree, path6, NULL));
+ _dbus_assert (find_subtree (tree, path7, NULL));
+ _dbus_assert (find_subtree (tree, path8, NULL));
+
+ _dbus_object_tree_unregister_and_unlock (tree, path6);
+
+ _dbus_assert (!find_subtree (tree, path1, NULL));
+ _dbus_assert (!find_subtree (tree, path2, NULL));
+ _dbus_assert (!find_subtree (tree, path3, NULL));
+ _dbus_assert (!find_subtree (tree, path4, NULL));
+ _dbus_assert (!find_subtree (tree, path5, NULL));
+ _dbus_assert (!find_subtree (tree, path6, NULL));
+ _dbus_assert (find_subtree (tree, path7, NULL));
+ _dbus_assert (find_subtree (tree, path8, NULL));
+
+ _dbus_object_tree_unregister_and_unlock (tree, path7);
+
+ _dbus_assert (!find_subtree (tree, path1, NULL));
+ _dbus_assert (!find_subtree (tree, path2, NULL));
+ _dbus_assert (!find_subtree (tree, path3, NULL));
+ _dbus_assert (!find_subtree (tree, path4, NULL));
+ _dbus_assert (!find_subtree (tree, path5, NULL));
+ _dbus_assert (!find_subtree (tree, path6, NULL));
+ _dbus_assert (!find_subtree (tree, path7, NULL));
+ _dbus_assert (find_subtree (tree, path8, NULL));
+
+ _dbus_object_tree_unregister_and_unlock (tree, path8);
+
+ _dbus_assert (!find_subtree (tree, path1, NULL));
+ _dbus_assert (!find_subtree (tree, path2, NULL));
+ _dbus_assert (!find_subtree (tree, path3, NULL));
+ _dbus_assert (!find_subtree (tree, path4, NULL));
+ _dbus_assert (!find_subtree (tree, path5, NULL));
+ _dbus_assert (!find_subtree (tree, path6, NULL));
+ _dbus_assert (!find_subtree (tree, path7, NULL));
+ _dbus_assert (!find_subtree (tree, path8, NULL));
+
+ i = 0;
+ while (i < (int) _DBUS_N_ELEMENTS (tree_test_data))
+ {
+ _dbus_assert (tree_test_data[i].handler_unregistered);
+ _dbus_assert (!tree_test_data[i].message_handled);
+ ++i;
+ }
+
+ /* Register it all again, and test dispatch */
+
+ if (!do_register (tree, path1, 0, tree_test_data))
+ goto out;
+ if (!do_register (tree, path2, 1, tree_test_data))
+ goto out;
+ if (!do_register (tree, path3, 2, tree_test_data))
+ goto out;
+ if (!do_register (tree, path4, 3, tree_test_data))
+ goto out;
+ if (!do_register (tree, path5, 4, tree_test_data))
+ goto out;
+ if (!do_register (tree, path6, 5, tree_test_data))
+ goto out;
+ if (!do_register (tree, path7, 6, tree_test_data))
+ goto out;
+ if (!do_register (tree, path8, 7, tree_test_data))
+ goto out;
+
+#if 0
+ spew_tree (tree);
+#endif
+
+ if (!do_test_dispatch (tree, path1, 0, tree_test_data, _DBUS_N_ELEMENTS (tree_test_data)))
+ goto out;
+ if (!do_test_dispatch (tree, path2, 1, tree_test_data, _DBUS_N_ELEMENTS (tree_test_data)))
+ goto out;
+ if (!do_test_dispatch (tree, path3, 2, tree_test_data, _DBUS_N_ELEMENTS (tree_test_data)))
+ goto out;
+ if (!do_test_dispatch (tree, path4, 3, tree_test_data, _DBUS_N_ELEMENTS (tree_test_data)))
+ goto out;
+ if (!do_test_dispatch (tree, path5, 4, tree_test_data, _DBUS_N_ELEMENTS (tree_test_data)))
+ goto out;
+ if (!do_test_dispatch (tree, path6, 5, tree_test_data, _DBUS_N_ELEMENTS (tree_test_data)))
+ goto out;
+ if (!do_test_dispatch (tree, path7, 6, tree_test_data, _DBUS_N_ELEMENTS (tree_test_data)))
+ goto out;
+ if (!do_test_dispatch (tree, path8, 7, tree_test_data, _DBUS_N_ELEMENTS (tree_test_data)))
+ goto out;
+
+ out:
+ if (tree)
+ {
+ /* test ref */
+ _dbus_object_tree_ref (tree);
+ _dbus_object_tree_unref (tree);
+ _dbus_object_tree_unref (tree);
+ }
+
+ return TRUE;
+}
+
+/**
+ * @ingroup DBusObjectTree
+ * Unit test for DBusObjectTree
+ * @returns #TRUE on success.
+ */
+dbus_bool_t
+_dbus_object_tree_test (void)
+{
+ _dbus_test_oom_handling ("object tree",
+ object_tree_test_iteration,
+ NULL);
+
+ return TRUE;
+}
+
+#endif /* DBUS_BUILD_TESTS */
diff --git a/dbus/dbus-object-tree.h b/dbus/dbus-object-tree.h
new file mode 100644
index 00000000..bf34d972
--- /dev/null
+++ b/dbus/dbus-object-tree.h
@@ -0,0 +1,52 @@
+/* -*- mode: C; c-file-style: "gnu" -*- */
+/* dbus-object-tree.h DBusObjectTree (internals of DBusConnection)
+ *
+ * Copyright (C) 2003 Red Hat Inc.
+ *
+ * Licensed under the Academic Free License version 1.2
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ *
+ */
+#ifndef DBUS_OBJECT_TREE_H
+#define DBUS_OBJECT_TREE_H
+
+#include <dbus/dbus-connection.h>
+
+DBUS_BEGIN_DECLS;
+
+typedef struct DBusObjectTree DBusObjectTree;
+
+DBusObjectTree* _dbus_object_tree_new (DBusConnection *connection);
+void _dbus_object_tree_ref (DBusObjectTree *tree);
+void _dbus_object_tree_unref (DBusObjectTree *tree);
+
+dbus_bool_t _dbus_object_tree_register (DBusObjectTree *tree,
+ dbus_bool_t fallback,
+ const char **path,
+ const DBusObjectPathVTable *vtable,
+ void *user_data);
+void _dbus_object_tree_unregister_and_unlock (DBusObjectTree *tree,
+ const char **path);
+DBusHandlerResult _dbus_object_tree_dispatch_and_unlock (DBusObjectTree *tree,
+ DBusMessage *message);
+void _dbus_object_tree_free_all_unlocked (DBusObjectTree *tree);
+
+dbus_bool_t _dbus_object_tree_list_registered_and_unlock (DBusObjectTree *tree,
+ const char **parent_path,
+ char ***child_entries);
+DBUS_END_DECLS;
+
+#endif /* DBUS_OBJECT_TREE_H */
diff --git a/dbus/dbus-pending-call.c b/dbus/dbus-pending-call.c
new file mode 100644
index 00000000..dad444e3
--- /dev/null
+++ b/dbus/dbus-pending-call.c
@@ -0,0 +1,429 @@
+/* -*- mode: C; c-file-style: "gnu" -*- */
+/* dbus-pending-call.c Object representing a call in progress.
+ *
+ * Copyright (C) 2002, 2003 Red Hat Inc.
+ *
+ * Licensed under the Academic Free License version 1.2
+ *
+ * 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-internals.h"
+#include "dbus-connection-internal.h"
+#include "dbus-pending-call.h"
+#include "dbus-list.h"
+#include "dbus-threads.h"
+#include "dbus-test.h"
+
+/**
+ * @defgroup DBusPendingCallInternals DBusPendingCall implementation details
+ * @ingroup DBusInternals
+ * @brief DBusPendingCall private implementation details.
+ *
+ * The guts of DBusPendingCall and its methods.
+ *
+ * @{
+ */
+
+static dbus_int32_t notify_user_data_slot = -1;
+
+/**
+ * Creates a new pending reply object.
+ *
+ * @param connection connection where reply will arrive
+ * @param timeout_milliseconds length of timeout, -1 for default
+ * @param timeout_handler timeout handler, takes pending call as data
+ * @returns a new #DBusPendingCall or #NULL if no memory.
+ */
+DBusPendingCall*
+_dbus_pending_call_new (DBusConnection *connection,
+ int timeout_milliseconds,
+ DBusTimeoutHandler timeout_handler)
+{
+ DBusPendingCall *pending;
+ DBusTimeout *timeout;
+
+ _dbus_return_val_if_fail (timeout_milliseconds >= 0 || timeout_milliseconds == -1, FALSE);
+
+ if (timeout_milliseconds == -1)
+ timeout_milliseconds = _DBUS_DEFAULT_TIMEOUT_VALUE;
+
+ if (!dbus_pending_call_allocate_data_slot (&notify_user_data_slot))
+ return NULL;
+
+ pending = dbus_new (DBusPendingCall, 1);
+
+ if (pending == NULL)
+ {
+ dbus_pending_call_free_data_slot (&notify_user_data_slot);
+ return NULL;
+ }
+
+ timeout = _dbus_timeout_new (timeout_milliseconds,
+ timeout_handler,
+ pending, NULL);
+
+ if (timeout == NULL)
+ {
+ dbus_pending_call_free_data_slot (&notify_user_data_slot);
+ dbus_free (pending);
+ return NULL;
+ }
+
+ pending->refcount.value = 1;
+ pending->connection = connection;
+ pending->timeout = timeout;
+
+ _dbus_data_slot_list_init (&pending->slot_list);
+
+ return pending;
+}
+
+/**
+ * Calls notifier function for the pending call
+ * and sets the call to completed.
+ *
+ * @param pending the pending call
+ *
+ */
+void
+_dbus_pending_call_notify (DBusPendingCall *pending)
+{
+ pending->completed = TRUE;
+
+ if (pending->function)
+ {
+ void *user_data;
+ user_data = dbus_pending_call_get_data (pending,
+ notify_user_data_slot);
+
+ (* pending->function) (pending, user_data);
+ }
+}
+
+/** @} */
+
+/**
+ * @defgroup DBusPendingCall DBusPendingCall
+ * @ingroup DBus
+ * @brief Pending reply to a method call message
+ *
+ * A DBusPendingCall is an object representing an
+ * expected reply. A #DBusPendingCall can be created
+ * when you send a message that should have a reply.
+ *
+ * @{
+ */
+
+/**
+ * @typedef DBusPendingCall
+ *
+ * Opaque data type representing a message pending.
+ */
+
+/**
+ * Increments the reference count on a pending call.
+ *
+ * @param pending the pending call object
+ */
+void
+dbus_pending_call_ref (DBusPendingCall *pending)
+{
+ _dbus_return_if_fail (pending != NULL);
+
+ _dbus_atomic_inc (&pending->refcount);
+}
+
+/**
+ * Decrements the reference count on a pending call,
+ * freeing it if the count reaches 0.
+ *
+ * @param pending the pending call object
+ */
+void
+dbus_pending_call_unref (DBusPendingCall *pending)
+{
+ dbus_bool_t last_unref;
+
+ _dbus_return_if_fail (pending != NULL);
+
+ last_unref = (_dbus_atomic_dec (&pending->refcount) == 1);
+
+ if (last_unref)
+ {
+ /* If we get here, we should be already detached
+ * from the connection, or never attached.
+ */
+ _dbus_assert (pending->connection == NULL);
+ _dbus_assert (!pending->timeout_added);
+
+ /* this assumes we aren't holding connection lock... */
+ _dbus_data_slot_list_free (&pending->slot_list);
+
+ if (pending->timeout != NULL)
+ _dbus_timeout_unref (pending->timeout);
+
+ if (pending->timeout_link)
+ {
+ dbus_message_unref ((DBusMessage *)pending->timeout_link->data);
+ _dbus_list_free_link (pending->timeout_link);
+ pending->timeout_link = NULL;
+ }
+
+ if (pending->reply)
+ {
+ dbus_message_unref (pending->reply);
+ pending->reply = NULL;
+ }
+
+ dbus_free (pending);
+
+ dbus_pending_call_free_data_slot (&notify_user_data_slot);
+ }
+}
+
+/**
+ * Sets a notification function to be called when the reply is
+ * received or the pending call times out.
+ *
+ * @param pending the pending call
+ * @param function notifier function
+ * @param user_data data to pass to notifier function
+ * @param free_user_data function to free the user data
+ * @returns #FALSE if not enough memory
+ */
+dbus_bool_t
+dbus_pending_call_set_notify (DBusPendingCall *pending,
+ DBusPendingCallNotifyFunction function,
+ void *user_data,
+ DBusFreeFunction free_user_data)
+{
+ _dbus_return_val_if_fail (pending != NULL, FALSE);
+
+ /* could invoke application code! */
+ if (!dbus_pending_call_set_data (pending, notify_user_data_slot,
+ user_data, free_user_data))
+ return FALSE;
+
+ pending->function = function;
+
+ return TRUE;
+}
+
+/**
+ * Cancels the pending call, such that any reply
+ * or error received will just be ignored.
+ * Drops at least one reference to the #DBusPendingCall
+ * so will free the call if nobody else is holding
+ * a reference.
+ *
+ * @param pending the pending call
+ */
+void
+dbus_pending_call_cancel (DBusPendingCall *pending)
+{
+ if (pending->connection)
+ _dbus_connection_remove_pending_call (pending->connection,
+ pending);
+}
+
+/**
+ * Checks whether the pending call has received a reply
+ * yet, or not.
+ *
+ * @todo not thread safe? I guess it has to lock though it sucks
+ *
+ * @param pending the pending call
+ * @returns #TRUE if a reply has been received */
+dbus_bool_t
+dbus_pending_call_get_completed (DBusPendingCall *pending)
+{
+ return pending->completed;
+}
+
+/**
+ * Gets the reply, or returns #NULL if none has been received yet. The
+ * reference count is not incremented on the returned message, so you
+ * have to keep a reference count on the pending call (or add one
+ * to the message).
+ *
+ * @todo not thread safe? I guess it has to lock though it sucks
+ * @todo maybe to make this threadsafe, it should be steal_reply(), i.e. only one thread can ever get the message
+ *
+ * @param pending the pending call
+ * @returns the reply message or #NULL.
+ */
+DBusMessage*
+dbus_pending_call_get_reply (DBusPendingCall *pending)
+{
+ return pending->reply;
+}
+
+/**
+ * Block until the pending call is completed. The blocking is as with
+ * dbus_connection_send_with_reply_and_block(); it does not enter the
+ * main loop or process other messages, it simply waits for the reply
+ * in question.
+ *
+ * If the pending call is already completed, this function returns
+ * immediately.
+ *
+ * @todo when you start blocking, the timeout is reset, but it should
+ * really only use time remaining since the pending call was created.
+ *
+ * @param pending the pending call
+ */
+void
+dbus_pending_call_block (DBusPendingCall *pending)
+{
+ DBusMessage *message;
+
+ if (dbus_pending_call_get_completed (pending))
+ return;
+
+ message = _dbus_connection_block_for_reply (pending->connection,
+ pending->reply_serial,
+ dbus_timeout_get_interval (pending->timeout));
+
+ _dbus_connection_lock (pending->connection);
+ _dbus_pending_call_complete_and_unlock (pending, message);
+ dbus_message_unref (message);
+}
+
+static DBusDataSlotAllocator slot_allocator;
+_DBUS_DEFINE_GLOBAL_LOCK (pending_call_slots);
+
+/**
+ * Allocates an integer ID to be used for storing application-specific
+ * data on any DBusPendingCall. The allocated ID may then be used
+ * with dbus_pending_call_set_data() and dbus_pending_call_get_data().
+ * The passed-in slot must be initialized to -1, and is filled in
+ * with the slot ID. If the passed-in slot is not -1, it's assumed
+ * to be already allocated, and its refcount is incremented.
+ *
+ * The allocated slot is global, i.e. all DBusPendingCall objects will
+ * have a slot with the given integer ID reserved.
+ *
+ * @param slot_p address of a global variable storing the slot
+ * @returns #FALSE on failure (no memory)
+ */
+dbus_bool_t
+dbus_pending_call_allocate_data_slot (dbus_int32_t *slot_p)
+{
+ return _dbus_data_slot_allocator_alloc (&slot_allocator,
+ _DBUS_LOCK_NAME (pending_call_slots),
+ slot_p);
+}
+
+/**
+ * Deallocates a global ID for #DBusPendingCall data slots.
+ * dbus_pending_call_get_data() and dbus_pending_call_set_data() may
+ * no longer be used with this slot. Existing data stored on existing
+ * DBusPendingCall objects will be freed when the #DBusPendingCall is
+ * finalized, but may not be retrieved (and may only be replaced if
+ * someone else reallocates the slot). When the refcount on the
+ * passed-in slot reaches 0, it is set to -1.
+ *
+ * @param slot_p address storing the slot to deallocate
+ */
+void
+dbus_pending_call_free_data_slot (dbus_int32_t *slot_p)
+{
+ _dbus_return_if_fail (*slot_p >= 0);
+
+ _dbus_data_slot_allocator_free (&slot_allocator, slot_p);
+}
+
+/**
+ * Stores a pointer on a #DBusPendingCall, along
+ * with an optional function to be used for freeing
+ * the data when the data is set again, or when
+ * the pending call is finalized. The slot number
+ * must have been allocated with dbus_pending_call_allocate_data_slot().
+ *
+ * @param pending the pending_call
+ * @param slot the slot number
+ * @param data the data to store
+ * @param free_data_func finalizer function for the data
+ * @returns #TRUE if there was enough memory to store the data
+ */
+dbus_bool_t
+dbus_pending_call_set_data (DBusPendingCall *pending,
+ dbus_int32_t slot,
+ void *data,
+ DBusFreeFunction free_data_func)
+{
+ DBusFreeFunction old_free_func;
+ void *old_data;
+ dbus_bool_t retval;
+
+ _dbus_return_val_if_fail (pending != NULL, FALSE);
+ _dbus_return_val_if_fail (slot >= 0, FALSE);
+
+ retval = _dbus_data_slot_list_set (&slot_allocator,
+ &pending->slot_list,
+ slot, data, free_data_func,
+ &old_free_func, &old_data);
+
+ if (retval)
+ {
+ if (old_free_func)
+ (* old_free_func) (old_data);
+ }
+
+ return retval;
+}
+
+/**
+ * Retrieves data previously set with dbus_pending_call_set_data().
+ * The slot must still be allocated (must not have been freed).
+ *
+ * @param pending the pending_call
+ * @param slot the slot to get data from
+ * @returns the data, or #NULL if not found
+ */
+void*
+dbus_pending_call_get_data (DBusPendingCall *pending,
+ dbus_int32_t slot)
+{
+ void *res;
+
+ _dbus_return_val_if_fail (pending != NULL, NULL);
+
+ res = _dbus_data_slot_list_get (&slot_allocator,
+ &pending->slot_list,
+ slot);
+
+ return res;
+}
+
+/** @} */
+
+#ifdef DBUS_BUILD_TESTS
+
+/**
+ * @ingroup DBusPendingCallInternals
+ * Unit test for DBusPendingCall.
+ *
+ * @returns #TRUE on success.
+ */
+dbus_bool_t
+_dbus_pending_call_test (const char *test_data_dir)
+{
+
+ return TRUE;
+}
+#endif /* DBUS_BUILD_TESTS */
diff --git a/dbus/dbus-pending-call.h b/dbus/dbus-pending-call.h
new file mode 100644
index 00000000..4f1e92c0
--- /dev/null
+++ b/dbus/dbus-pending-call.h
@@ -0,0 +1,58 @@
+/* -*- mode: C; c-file-style: "gnu" -*- */
+/* dbus-pending-call.h Object representing a call in progress.
+ *
+ * Copyright (C) 2002, 2003 Red Hat Inc.
+ *
+ * Licensed under the Academic Free License version 1.2
+ *
+ * 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
+ *
+ */
+#if !defined (DBUS_INSIDE_DBUS_H) && !defined (DBUS_COMPILATION)
+#error "Only <dbus/dbus.h> can be included directly, this file may disappear or change contents."
+#endif
+
+#ifndef DBUS_PENDING_CALL_H
+#define DBUS_PENDING_CALL_H
+
+#include <dbus/dbus-macros.h>
+#include <dbus/dbus-types.h>
+#include <dbus/dbus-connection.h>
+
+DBUS_BEGIN_DECLS;
+
+void dbus_pending_call_ref (DBusPendingCall *pending);
+void dbus_pending_call_unref (DBusPendingCall *pending);
+dbus_bool_t dbus_pending_call_set_notify (DBusPendingCall *pending,
+ DBusPendingCallNotifyFunction function,
+ void *user_data,
+ DBusFreeFunction free_user_data);
+void dbus_pending_call_cancel (DBusPendingCall *pending);
+dbus_bool_t dbus_pending_call_get_completed (DBusPendingCall *pending);
+DBusMessage* dbus_pending_call_get_reply (DBusPendingCall *pending);
+void dbus_pending_call_block (DBusPendingCall *pending);
+
+dbus_bool_t dbus_pending_call_allocate_data_slot (dbus_int32_t *slot_p);
+void dbus_pending_call_free_data_slot (dbus_int32_t *slot_p);
+dbus_bool_t dbus_pending_call_set_data (DBusPendingCall *pending,
+ dbus_int32_t slot,
+ void *data,
+ DBusFreeFunction free_data_func);
+void* dbus_pending_call_get_data (DBusPendingCall *pending,
+ dbus_int32_t slot);
+
+DBUS_END_DECLS;
+
+#endif /* DBUS_PENDING_CALL_H */
diff --git a/dbus/dbus-protocol.h b/dbus/dbus-protocol.h
index fbdcb6dd..a0cf54ef 100644
--- a/dbus/dbus-protocol.h
+++ b/dbus/dbus-protocol.h
@@ -53,25 +53,54 @@ extern "C" {
#define DBUS_TYPE_NAMED 10
#define DBUS_TYPE_ARRAY 11
#define DBUS_TYPE_DICT 12
+#define DBUS_TYPE_OBJECT_PATH 13
+
+#define DBUS_TYPE_LAST DBUS_TYPE_OBJECT_PATH
-#define DBUS_TYPE_LAST DBUS_TYPE_DICT
-
-/* Max length in bytes of a service or message name */
+/* Max length in bytes of a service or interface or member name */
#define DBUS_MAXIMUM_NAME_LENGTH 256
+/* Types of message */
+#define DBUS_MESSAGE_TYPE_INVALID 0
+#define DBUS_MESSAGE_TYPE_METHOD_CALL 1
+#define DBUS_MESSAGE_TYPE_METHOD_RETURN 2
+#define DBUS_MESSAGE_TYPE_ERROR 3
+#define DBUS_MESSAGE_TYPE_SIGNAL 4
+
/* Header flags */
-#define DBUS_HEADER_FLAG_ERROR 0x1
+#define DBUS_HEADER_FLAG_NO_REPLY_EXPECTED 0x1
/* Header fields */
-#define DBUS_HEADER_FIELD_NAME "name"
-#define DBUS_HEADER_FIELD_SERVICE "srvc"
-#define DBUS_HEADER_FIELD_REPLY "rply"
-#define DBUS_HEADER_FIELD_SENDER "sndr"
-
+#define DBUS_HEADER_FIELD_INVALID 0
+#define DBUS_HEADER_FIELD_PATH 1
+#define DBUS_HEADER_FIELD_INTERFACE 2
+#define DBUS_HEADER_FIELD_MEMBER 3
+#define DBUS_HEADER_FIELD_ERROR_NAME 4
+#define DBUS_HEADER_FIELD_REPLY_SERIAL 5
+#define DBUS_HEADER_FIELD_SERVICE 6
+#define DBUS_HEADER_FIELD_SENDER_SERVICE 7
+
+#define DBUS_HEADER_FIELD_LAST DBUS_HEADER_FIELD_SENDER_SERVICE
+
/* Services */
-#define DBUS_SERVICE_DBUS "org.freedesktop.DBus"
-#define DBUS_SERVICE_BROADCAST "org.freedesktop.DBus.Broadcast"
+#define DBUS_SERVICE_ORG_FREEDESKTOP_DBUS "org.freedesktop.DBus"
+/* Paths */
+#define DBUS_PATH_ORG_FREEDESKTOP_DBUS "/org/freedesktop/DBus"
+#define DBUS_PATH_ORG_FREEDESKTOP_LOCAL "/org/freedesktop/Local"
+
+/* Interfaces, these #define don't do much other than
+ * catch typos at compile time
+ */
+#define DBUS_INTERFACE_ORG_FREEDESKTOP_DBUS "org.freedesktop.DBus"
+#define DBUS_INTERFACE_ORG_FREEDESKTOP_INTROSPECTABLE "org.freedesktop.Introspectable"
+
+/* This is a special interface whose methods can only be invoked
+ * by the local implementation (messages from remote apps aren't
+ * allowed to specify this interface).
+ */
+#define DBUS_INTERFACE_ORG_FREEDESKTOP_LOCAL "org.freedesktop.Local"
+
/* Service owner flags */
#define DBUS_SERVICE_FLAG_PROHIBIT_REPLACEMENT 0x1
#define DBUS_SERVICE_FLAG_REPLACE_EXISTING 0x2
@@ -86,24 +115,6 @@ extern "C" {
#define DBUS_ACTIVATION_REPLY_ACTIVATED 0x0
#define DBUS_ACTIVATION_REPLY_ALREADY_ACTIVE 0x1
-/* Messages */
-#define DBUS_MESSAGE_ACTIVATE_SERVICE "org.freedesktop.DBus.ActivateService"
-#define DBUS_MESSAGE_SERVICE_EXISTS "org.freedesktop.DBus.ServiceExists"
-#define DBUS_MESSAGE_HELLO "org.freedesktop.DBus.Hello"
-#define DBUS_MESSAGE_LIST_SERVICES "org.freedesktop.DBus.ListServices"
-#define DBUS_MESSAGE_ACQUIRE_SERVICE "org.freedesktop.DBus.AcquireService"
-#define DBUS_MESSAGE_SERVICE_ACQUIRED "org.freedesktop.DBus.ServiceAcquired"
-#define DBUS_MESSAGE_SERVICE_CREATED "org.freedesktop.DBus.ServiceCreated"
-#define DBUS_MESSAGE_SERVICE_DELETED "org.freedesktop.DBus.ServiceDeleted"
-#define DBUS_MESSAGE_SERVICE_LOST "org.freedesktop.DBus.ServiceLost"
-
-
-/* This namespace is reserved for locally-synthesized messages, you can't
- * send messages that have this namespace.
- */
-#define DBUS_NAMESPACE_LOCAL_MESSAGE "org.freedesktop.Local."
-#define DBUS_MESSAGE_LOCAL_DISCONNECT DBUS_NAMESPACE_LOCAL_MESSAGE"Disconnect"
-
#ifdef __cplusplus
}
#endif
diff --git a/dbus/dbus-server-debug-pipe.c b/dbus/dbus-server-debug-pipe.c
index 15b78608..6a66391e 100644
--- a/dbus/dbus-server-debug-pipe.c
+++ b/dbus/dbus-server-debug-pipe.c
@@ -27,6 +27,7 @@
#include "dbus-transport-unix.h"
#include "dbus-connection-internal.h"
#include "dbus-hash.h"
+#include "dbus-string.h"
#ifdef DBUS_BUILD_TESTS
diff --git a/dbus/dbus-server-protected.h b/dbus/dbus-server-protected.h
index a3774c31..317805f5 100644
--- a/dbus/dbus-server-protected.h
+++ b/dbus/dbus-server-protected.h
@@ -34,6 +34,9 @@ DBUS_BEGIN_DECLS;
typedef struct DBusServerVTable DBusServerVTable;
+/**
+ * Virtual table to be implemented by all server "subclasses"
+ */
struct DBusServerVTable
{
void (* finalize) (DBusServer *server);
@@ -43,6 +46,9 @@ struct DBusServerVTable
/**< Disconnect this server. */
};
+/**
+ * Internals of DBusServer object
+ */
struct DBusServer
{
int refcount; /**< Reference count. */
diff --git a/dbus/dbus-server-unix.c b/dbus/dbus-server-unix.c
index 036446af..487d60ec 100644
--- a/dbus/dbus-server-unix.c
+++ b/dbus/dbus-server-unix.c
@@ -25,6 +25,7 @@
#include "dbus-server-unix.h"
#include "dbus-transport-unix.h"
#include "dbus-connection-internal.h"
+#include "dbus-string.h"
#include <sys/types.h>
#include <unistd.h>
diff --git a/dbus/dbus-server.c b/dbus/dbus-server.c
index 1c9d53f0..29e20a55 100644
--- a/dbus/dbus-server.c
+++ b/dbus/dbus-server.c
@@ -23,6 +23,7 @@
#include "dbus-server.h"
#include "dbus-server-unix.h"
+#include "dbus-string.h"
#ifdef DBUS_BUILD_TESTS
#include "dbus-server-debug-pipe.h"
#endif
diff --git a/dbus/dbus-sha.h b/dbus/dbus-sha.h
index 7f793844..0a48d197 100644
--- a/dbus/dbus-sha.h
+++ b/dbus/dbus-sha.h
@@ -31,11 +31,14 @@ DBUS_BEGIN_DECLS;
typedef struct DBusSHAContext DBusSHAContext;
+/**
+ * Struct storing state of the SHA algorithm
+ */
struct DBusSHAContext
{
dbus_uint32_t digest[5]; /**< Message digest */
dbus_uint32_t count_lo; /**< 64-bit bit count */
- dbus_uint32_t count_hi;
+ dbus_uint32_t count_hi; /**< No clue */
dbus_uint32_t data[16]; /**< SHA data buffer */
};
diff --git a/dbus/dbus-spawn.c b/dbus/dbus-spawn.c
index d4015561..604b9e7c 100644
--- a/dbus/dbus-spawn.c
+++ b/dbus/dbus-spawn.c
@@ -176,28 +176,31 @@ enum
CHILD_PID /* Followed by pid_t */
};
+/**
+ * Babysitter implementation details
+ */
struct DBusBabysitter
{
- int refcount;
+ int refcount; /**< Reference count */
char *executable; /**< executable name to use in error messages */
- int socket_to_babysitter;
- int error_pipe_from_child;
+ int socket_to_babysitter; /**< Connection to the babysitter process */
+ int error_pipe_from_child; /**< Connection to the process that does the exec() */
- pid_t sitter_pid;
- pid_t grandchild_pid;
+ pid_t sitter_pid; /**< PID Of the babysitter */
+ pid_t grandchild_pid; /**< PID of the grandchild */
- DBusWatchList *watches;
+ DBusWatchList *watches; /**< Watches */
- DBusWatch *error_watch;
- DBusWatch *sitter_watch;
+ DBusWatch *error_watch; /**< Error pipe watch */
+ DBusWatch *sitter_watch; /**< Sitter pipe watch */
- int errnum;
- int status;
- unsigned int have_child_status : 1;
- unsigned int have_fork_errnum : 1;
- unsigned int have_exec_errnum : 1;
+ int errnum; /**< Error number */
+ int status; /**< Exit status code */
+ unsigned int have_child_status : 1; /**< True if child status has been reaped */
+ unsigned int have_fork_errnum : 1; /**< True if we have an error code from fork() */
+ unsigned int have_exec_errnum : 1; /**< True if we have an error code from exec() */
};
static DBusBabysitter*
diff --git a/dbus/dbus-string.c b/dbus/dbus-string.c
index c6f929a8..628cf861 100644
--- a/dbus/dbus-string.c
+++ b/dbus/dbus-string.c
@@ -25,6 +25,8 @@
#include "dbus-string.h"
/* we allow a system header here, for speed/convenience */
#include <string.h>
+/* for vsnprintf */
+#include <stdio.h>
#include "dbus-marshal.h"
#define DBUS_CAN_USE_DBUS_STRING_PRIVATE 1
#include "dbus-string-private.h"
@@ -560,26 +562,30 @@ _dbus_string_get_byte (const DBusString *str,
}
/**
- * Inserts the given byte at the given position.
+ * Inserts a number of bytes of a given value at the
+ * given position.
*
* @param str the string
* @param i the position
+ * @param n_bytes number of bytes
* @param byte the value to insert
* @returns #TRUE on success
*/
dbus_bool_t
-_dbus_string_insert_byte (DBusString *str,
- int i,
- unsigned char byte)
+_dbus_string_insert_bytes (DBusString *str,
+ int i,
+ int n_bytes,
+ unsigned char byte)
{
DBUS_STRING_PREAMBLE (str);
_dbus_assert (i <= real->len);
_dbus_assert (i >= 0);
+ _dbus_assert (n_bytes > 0);
- if (!open_gap (1, real, i))
+ if (!open_gap (n_bytes, real, i))
return FALSE;
- real->str[i] = byte;
+ memset (real->str + i, byte, n_bytes);
return TRUE;
}
@@ -964,7 +970,7 @@ _dbus_string_append_8_aligned (DBusString *str,
p = (dbus_uint64_t*) (real->str + (real->len - 8));
*p = *((dbus_uint64_t*)octets);
#else
- char *p;
+ unsigned char *p;
DBUS_STRING_PREAMBLE (str);
if (!align_length_then_lengthen (str, 8, 8))
@@ -987,6 +993,59 @@ _dbus_string_append_8_aligned (DBusString *str,
}
/**
+ * Appends a printf-style formatted string
+ * to the #DBusString.
+ *
+ * @param str the string
+ * @param format printf format
+ * @param args variable argument list
+ * @returns #FALSE if no memory
+ */
+dbus_bool_t
+_dbus_string_append_printf_valist (DBusString *str,
+ const char *format,
+ va_list args)
+{
+ int len;
+ char c;
+ DBUS_STRING_PREAMBLE (str);
+
+ /* Measure the message length without terminating nul */
+ len = vsnprintf (&c, 1, format, args);
+
+ if (!_dbus_string_lengthen (str, len))
+ return FALSE;
+
+ vsprintf (real->str + (real->len - len),
+ format, args);
+
+ return TRUE;
+}
+
+/**
+ * Appends a printf-style formatted string
+ * to the #DBusString.
+ *
+ * @param str the string
+ * @param format printf format
+ * @returns #FALSE if no memory
+ */
+dbus_bool_t
+_dbus_string_append_printf (DBusString *str,
+ const char *format,
+ ...)
+{
+ va_list args;
+ dbus_bool_t retval;
+
+ va_start (args, format);
+ retval = _dbus_string_append_printf_valist (str, format, args);
+ va_end (args);
+
+ return retval;
+}
+
+/**
* Appends block of bytes with the given length to a DBusString.
*
* @param str the DBusString
@@ -1752,7 +1811,7 @@ _dbus_string_skip_white (const DBusString *str,
}
/**
- * Assigns a newline-terminated or \r\n-terminated line from the front
+ * Assigns a newline-terminated or \\r\\n-terminated line from the front
* of the string to the given dest string. The dest string's previous
* contents are deleted. If the source string contains no newline,
* moves the entire source string to the dest string.
@@ -2791,13 +2850,84 @@ _dbus_string_validate_nul (const DBusString *str,
}
/**
- * Checks that the given range of the string is a valid message name
+ * Checks that the given range of the string is a valid object path
+ * name in the D-BUS protocol. This includes a length restriction,
+ * etc., see the specification. It does not validate UTF-8, that has
+ * to be done separately for now.
+ *
+ * @todo this is inconsistent with most of DBusString in that
+ * it allows a start,len range that isn't in the string.
+ *
+ * @todo change spec to disallow more things, such as spaces in the
+ * path name
+ *
+ * @param str the string
+ * @param start first byte index to check
+ * @param len number of bytes to check
+ * @returns #TRUE if the byte range exists and is a valid name
+ */
+dbus_bool_t
+_dbus_string_validate_path (const DBusString *str,
+ int start,
+ int len)
+{
+ const unsigned char *s;
+ const unsigned char *end;
+ const unsigned char *last_slash;
+
+ DBUS_CONST_STRING_PREAMBLE (str);
+ _dbus_assert (start >= 0);
+ _dbus_assert (len >= 0);
+ _dbus_assert (start <= real->len);
+
+ if (len > real->len - start)
+ return FALSE;
+
+ if (len > DBUS_MAXIMUM_NAME_LENGTH)
+ return FALSE;
+
+ if (len == 0)
+ return FALSE;
+
+ s = real->str + start;
+ end = s + len;
+
+ if (*s != '/')
+ return FALSE;
+ last_slash = s;
+ ++s;
+
+ while (s != end)
+ {
+ if (*s == '/')
+ {
+ if ((s - last_slash) < 2)
+ return FALSE; /* no empty path components allowed */
+
+ last_slash = s;
+ }
+
+ ++s;
+ }
+
+ if ((end - last_slash) < 2 &&
+ len > 1)
+ return FALSE; /* trailing slash not allowed unless the string is "/" */
+
+ return TRUE;
+}
+
+/**
+ * Checks that the given range of the string is a valid interface name
* in the D-BUS protocol. This includes a length restriction, etc.,
* see the specification. It does not validate UTF-8, that has to be
* done separately for now.
*
* @todo this is inconsistent with most of DBusString in that
* it allows a start,len range that isn't in the string.
+ *
+ * @todo change spec to disallow more things, such as spaces in the
+ * interface name
*
* @param str the string
* @param start first byte index to check
@@ -2805,9 +2935,9 @@ _dbus_string_validate_nul (const DBusString *str,
* @returns #TRUE if the byte range exists and is a valid name
*/
dbus_bool_t
-_dbus_string_validate_name (const DBusString *str,
- int start,
- int len)
+_dbus_string_validate_interface (const DBusString *str,
+ int start,
+ int len)
{
const unsigned char *s;
const unsigned char *end;
@@ -2847,6 +2977,89 @@ _dbus_string_validate_name (const DBusString *str,
return TRUE;
}
+/**
+ * Checks that the given range of the string is a valid member name
+ * in the D-BUS protocol. This includes a length restriction, etc.,
+ * see the specification. It does not validate UTF-8, that has to be
+ * done separately for now.
+ *
+ * @todo this is inconsistent with most of DBusString in that
+ * it allows a start,len range that isn't in the string.
+ *
+ * @todo change spec to disallow more things, such as spaces in the
+ * member name
+ *
+ * @param str the string
+ * @param start first byte index to check
+ * @param len number of bytes to check
+ * @returns #TRUE if the byte range exists and is a valid name
+ */
+dbus_bool_t
+_dbus_string_validate_member (const DBusString *str,
+ int start,
+ int len)
+{
+ const unsigned char *s;
+ const unsigned char *end;
+ dbus_bool_t saw_dot;
+
+ DBUS_CONST_STRING_PREAMBLE (str);
+ _dbus_assert (start >= 0);
+ _dbus_assert (len >= 0);
+ _dbus_assert (start <= real->len);
+
+ if (len > real->len - start)
+ return FALSE;
+
+ if (len > DBUS_MAXIMUM_NAME_LENGTH)
+ return FALSE;
+
+ if (len == 0)
+ return FALSE;
+
+ saw_dot = FALSE;
+ s = real->str + start;
+ end = s + len;
+ while (s != end)
+ {
+ if (*s == '.')
+ {
+ saw_dot = TRUE;
+ break;
+ }
+
+ ++s;
+ }
+
+ /* No dot allowed in member names */
+ if (saw_dot)
+ return FALSE;
+
+ return TRUE;
+}
+
+/**
+ * Checks that the given range of the string is a valid error name
+ * in the D-BUS protocol. This includes a length restriction, etc.,
+ * see the specification. It does not validate UTF-8, that has to be
+ * done separately for now.
+ *
+ * @todo this is inconsistent with most of DBusString in that
+ * it allows a start,len range that isn't in the string.
+ *
+ * @param str the string
+ * @param start first byte index to check
+ * @param len number of bytes to check
+ * @returns #TRUE if the byte range exists and is a valid name
+ */
+dbus_bool_t
+_dbus_string_validate_error_name (const DBusString *str,
+ int start,
+ int len)
+{
+ /* Same restrictions as interface name at the moment */
+ return _dbus_string_validate_interface (str, start, len);
+}
/**
* Checks that the given range of the string is a valid service name
@@ -2856,6 +3069,9 @@ _dbus_string_validate_name (const DBusString *str,
*
* @todo this is inconsistent with most of DBusString in that
* it allows a start,len range that isn't in the string.
+ *
+ * @todo change spec to disallow more things, such as spaces in the
+ * service name
*
* @param str the string
* @param start first byte index to check
@@ -3107,6 +3323,24 @@ _dbus_string_test (void)
int lens[] = { 0, 1, 2, 3, 4, 5, 10, 16, 17, 18, 25, 31, 32, 33, 34, 35, 63, 64, 65, 66, 67, 68, 69, 70, 71, 127, 128, 129, 130, 131, 132, 133, 134, 135, 136 };
char *s;
dbus_unichar_t ch;
+ const char *valid_paths[] = {
+ "/",
+ "/foo/bar",
+ "/foo",
+ "/foo/bar/baz"
+ };
+ const char *invalid_paths[] = {
+ "bar",
+ "bar/baz",
+ "/foo/bar/",
+ "/foo/"
+ "foo/",
+ "boo//blah",
+ "//",
+ "///",
+ "foo///blah/",
+ "Hello World"
+ };
i = 0;
while (i < _DBUS_N_ELEMENTS (lens))
@@ -3342,23 +3576,26 @@ _dbus_string_test (void)
_dbus_string_set_byte (&str, 1, 'q');
_dbus_assert (_dbus_string_get_byte (&str, 1) == 'q');
- if (!_dbus_string_insert_byte (&str, 0, 255))
+ if (!_dbus_string_insert_bytes (&str, 0, 1, 255))
_dbus_assert_not_reached ("can't insert byte");
- if (!_dbus_string_insert_byte (&str, 2, 'Z'))
+ if (!_dbus_string_insert_bytes (&str, 2, 4, 'Z'))
_dbus_assert_not_reached ("can't insert byte");
- if (!_dbus_string_insert_byte (&str, _dbus_string_get_length (&str), 'W'))
+ if (!_dbus_string_insert_bytes (&str, _dbus_string_get_length (&str), 1, 'W'))
_dbus_assert_not_reached ("can't insert byte");
_dbus_assert (_dbus_string_get_byte (&str, 0) == 255);
_dbus_assert (_dbus_string_get_byte (&str, 1) == 'H');
_dbus_assert (_dbus_string_get_byte (&str, 2) == 'Z');
- _dbus_assert (_dbus_string_get_byte (&str, 3) == 'q');
- _dbus_assert (_dbus_string_get_byte (&str, 4) == 'l');
- _dbus_assert (_dbus_string_get_byte (&str, 5) == 'l');
- _dbus_assert (_dbus_string_get_byte (&str, 6) == 'o');
- _dbus_assert (_dbus_string_get_byte (&str, 7) == 'W');
+ _dbus_assert (_dbus_string_get_byte (&str, 3) == 'Z');
+ _dbus_assert (_dbus_string_get_byte (&str, 4) == 'Z');
+ _dbus_assert (_dbus_string_get_byte (&str, 5) == 'Z');
+ _dbus_assert (_dbus_string_get_byte (&str, 6) == 'q');
+ _dbus_assert (_dbus_string_get_byte (&str, 7) == 'l');
+ _dbus_assert (_dbus_string_get_byte (&str, 8) == 'l');
+ _dbus_assert (_dbus_string_get_byte (&str, 9) == 'o');
+ _dbus_assert (_dbus_string_get_byte (&str, 10) == 'W');
_dbus_string_free (&str);
@@ -3481,7 +3718,38 @@ _dbus_string_test (void)
/* Base 64 and Hex encoding */
test_roundtrips (test_base64_roundtrip);
test_roundtrips (test_hex_roundtrip);
-
+
+ /* Path validation */
+ i = 0;
+ while (i < (int) _DBUS_N_ELEMENTS (valid_paths))
+ {
+ _dbus_string_init_const (&str, valid_paths[i]);
+
+ if (!_dbus_string_validate_path (&str, 0,
+ _dbus_string_get_length (&str)))
+ {
+ _dbus_warn ("Path \"%s\" should have been valid\n", valid_paths[i]);
+ _dbus_assert_not_reached ("invalid path");
+ }
+
+ ++i;
+ }
+
+ i = 0;
+ while (i < (int) _DBUS_N_ELEMENTS (invalid_paths))
+ {
+ _dbus_string_init_const (&str, invalid_paths[i]);
+
+ if (_dbus_string_validate_path (&str, 0,
+ _dbus_string_get_length (&str)))
+ {
+ _dbus_warn ("Path \"%s\" should have been invalid\n", invalid_paths[i]);
+ _dbus_assert_not_reached ("valid path");
+ }
+
+ ++i;
+ }
+
return TRUE;
}
diff --git a/dbus/dbus-string.h b/dbus/dbus-string.h
index 8fa13805..0b7be8b0 100644
--- a/dbus/dbus-string.h
+++ b/dbus/dbus-string.h
@@ -28,11 +28,15 @@
#include <dbus/dbus-memory.h>
#include <dbus/dbus-types.h>
+#include <dbus/dbus-sysdeps.h>
-DBUS_BEGIN_DECLS;
+#include <stdarg.h>
-typedef struct DBusString DBusString;
+DBUS_BEGIN_DECLS;
+/**
+ * DBusString object
+ */
struct DBusString
{
void *dummy1; /**< placeholder */
@@ -68,8 +72,9 @@ void _dbus_string_set_byte (DBusString *str,
unsigned char byte);
unsigned char _dbus_string_get_byte (const DBusString *str,
int start);
-dbus_bool_t _dbus_string_insert_byte (DBusString *str,
+dbus_bool_t _dbus_string_insert_bytes (DBusString *str,
int i,
+ int n_bytes,
unsigned char byte);
dbus_bool_t _dbus_string_steal_data (DBusString *str,
char **data_return);
@@ -111,6 +116,12 @@ dbus_bool_t _dbus_string_append_4_aligned (DBusString *str,
const unsigned char octets[4]);
dbus_bool_t _dbus_string_append_8_aligned (DBusString *str,
const unsigned char octets[8]);
+dbus_bool_t _dbus_string_append_printf (DBusString *str,
+ const char *format,
+ ...) _DBUS_GNUC_PRINTF (2, 3);
+dbus_bool_t _dbus_string_append_printf_valist (DBusString *str,
+ const char *format,
+ va_list args);
void _dbus_string_delete (DBusString *str,
int start,
int len);
@@ -216,7 +227,16 @@ dbus_bool_t _dbus_string_validate_utf8 (const DBusString *str,
dbus_bool_t _dbus_string_validate_nul (const DBusString *str,
int start,
int len);
-dbus_bool_t _dbus_string_validate_name (const DBusString *str,
+dbus_bool_t _dbus_string_validate_path (const DBusString *str,
+ int start,
+ int len);
+dbus_bool_t _dbus_string_validate_interface (const DBusString *str,
+ int start,
+ int len);
+dbus_bool_t _dbus_string_validate_member (const DBusString *str,
+ int start,
+ int len);
+dbus_bool_t _dbus_string_validate_error_name (const DBusString *str,
int start,
int len);
dbus_bool_t _dbus_string_validate_service (const DBusString *str,
diff --git a/dbus/dbus-sysdeps.c b/dbus/dbus-sysdeps.c
index 5311b202..c3ddf8cd 100644
--- a/dbus/dbus-sysdeps.c
+++ b/dbus/dbus-sysdeps.c
@@ -2515,9 +2515,12 @@ _dbus_path_is_absolute (const DBusString *filename)
return FALSE;
}
+/**
+ * Internals of directory iterator
+ */
struct DBusDirIter
{
- DIR *d;
+ DIR *d; /**< The DIR* from opendir() */
};
diff --git a/dbus/dbus-sysdeps.h b/dbus/dbus-sysdeps.h
index cfe0cd25..363f665d 100644
--- a/dbus/dbus-sysdeps.h
+++ b/dbus/dbus-sysdeps.h
@@ -25,7 +25,6 @@
#ifndef DBUS_SYSDEPS_H
#define DBUS_SYSDEPS_H
-#include <dbus/dbus-string.h>
#include <dbus/dbus-errors.h>
/* this is perhaps bogus, but strcmp() etc. are faster if we use the
@@ -47,6 +46,8 @@ DBUS_BEGIN_DECLS;
* dbus-memory.c)
*/
+typedef struct DBusString DBusString;
+
#if __GNUC__ > 2 || (__GNUC__ == 2 && __GNUC_MINOR__ > 4)
#define _DBUS_GNUC_PRINTF( format_idx, arg_idx ) \
__attribute__((__format__ (__printf__, format_idx, arg_idx)))
@@ -96,12 +97,14 @@ typedef unsigned long dbus_gid_t;
#define DBUS_UID_FORMAT "%lu"
#define DBUS_GID_FORMAT "%lu"
+/**
+ * Struct representing socket credentials
+ */
typedef struct
{
- /* Set to DBUS_PID_UNSET etc. if not available */
- dbus_pid_t pid;
- dbus_uid_t uid;
- dbus_gid_t gid;
+ dbus_pid_t pid; /**< process ID or DBUS_PID_UNSET */
+ dbus_uid_t uid; /**< user ID or DBUS_UID_UNSET */
+ dbus_gid_t gid; /**< group ID or DBUS_GID_UNSET */
} DBusCredentials;
int _dbus_connect_unix_socket (const char *path,
@@ -134,6 +137,9 @@ dbus_bool_t _dbus_credentials_match (const DBusCredentials *expec
typedef struct DBusUserInfo DBusUserInfo;
typedef struct DBusGroupInfo DBusGroupInfo;
+/**
+ * Information about a UNIX user
+ */
struct DBusUserInfo
{
dbus_uid_t uid; /**< UID */
@@ -144,6 +150,9 @@ struct DBusUserInfo
char *homedir; /**< Home directory */
};
+/**
+ * Information about a UNIX group
+ */
struct DBusGroupInfo
{
dbus_gid_t gid; /**< GID */
@@ -172,9 +181,13 @@ dbus_uid_t _dbus_getuid (void);
dbus_gid_t _dbus_getgid (void);
typedef struct DBusAtomic DBusAtomic;
+
+/**
+ * An atomic integer.
+ */
struct DBusAtomic
{
- volatile dbus_int32_t value;
+ volatile dbus_int32_t value; /**< Value of the atomic integer. */
};
dbus_int32_t _dbus_atomic_inc (DBusAtomic *atomic);
@@ -187,11 +200,14 @@ dbus_int32_t _dbus_atomic_dec (DBusAtomic *atomic);
#define _DBUS_POLLHUP 0x0010 /* Hung up */
#define _DBUS_POLLNVAL 0x0020 /* Invalid request: fd not open */
+/**
+ * A portable struct pollfd wrapper.
+ */
typedef struct
{
- int fd;
- short events;
- short revents;
+ int fd; /**< File descriptor */
+ short events; /**< Events to poll for */
+ short revents; /**< Events that occurred */
} DBusPollFD;
int _dbus_poll (DBusPollFD *fds,
@@ -248,16 +264,19 @@ void _dbus_fd_set_close_on_exec (int fd);
void _dbus_exit (int code) _DBUS_GNUC_NORETURN;
+/**
+ * Portable struct with stat() results
+ */
typedef struct
{
- unsigned long mode;
- unsigned long nlink;
- dbus_uid_t uid;
- dbus_gid_t gid;
- unsigned long size;
- unsigned long atime;
- unsigned long mtime;
- unsigned long ctime;
+ unsigned long mode; /**< File mode */
+ unsigned long nlink; /**< Number of hard links */
+ dbus_uid_t uid; /**< User owning file */
+ dbus_gid_t gid; /**< Group owning file */
+ unsigned long size; /**< Size of file */
+ unsigned long atime; /**< Access time */
+ unsigned long mtime; /**< Modify time */
+ unsigned long ctime; /**< Creation time */
} DBusStat;
dbus_bool_t _dbus_stat (const DBusString *filename,
diff --git a/dbus/dbus-test.c b/dbus/dbus-test.c
index 2fbab5a4..b7a09a0e 100644
--- a/dbus/dbus-test.c
+++ b/dbus/dbus-test.c
@@ -81,7 +81,7 @@ dbus_internal_do_not_use_run_tests (const char *test_data_dir)
die ("strings");
check_memleaks ();
-
+
printf ("%s: running sysdeps tests\n", "dbus-test");
if (!_dbus_sysdeps_test ())
die ("sysdeps");
@@ -99,6 +99,12 @@ dbus_internal_do_not_use_run_tests (const char *test_data_dir)
die ("address parsing");
check_memleaks ();
+
+ printf ("%s: running object tree tests\n", "dbus-test");
+ if (!_dbus_object_tree_test ())
+ die ("object tree");
+
+ check_memleaks ();
printf ("%s: running marshalling tests\n", "dbus-test");
if (!_dbus_marshal_test ())
@@ -129,12 +135,6 @@ dbus_internal_do_not_use_run_tests (const char *test_data_dir)
die ("messages");
check_memleaks ();
-
- printf ("%s: running message handler tests\n", "dbus-test");
- if (!_dbus_message_handler_test (test_data_dir))
- die ("message handler");
-
- check_memleaks ();
printf ("%s: running hash table tests\n", "dbus-test");
if (!_dbus_hash_test ())
@@ -179,6 +179,12 @@ dbus_internal_do_not_use_run_tests (const char *test_data_dir)
die ("auth");
check_memleaks ();
+
+ printf ("%s: running pending call tests\n", "dbus-test");
+ if (!_dbus_pending_call_test (test_data_dir))
+ die ("auth");
+
+ check_memleaks ();
printf ("%s: completed successfully\n", "dbus-test");
#else
diff --git a/dbus/dbus-test.h b/dbus/dbus-test.h
index 22a43f79..02b2c279 100644
--- a/dbus/dbus-test.h
+++ b/dbus/dbus-test.h
@@ -43,7 +43,6 @@ dbus_bool_t _dbus_mem_pool_test (void);
dbus_bool_t _dbus_string_test (void);
dbus_bool_t _dbus_address_test (void);
dbus_bool_t _dbus_message_test (const char *test_data_dir);
-dbus_bool_t _dbus_message_handler_test (const char *test_data_dir);
dbus_bool_t _dbus_auth_test (const char *test_data_dir);
dbus_bool_t _dbus_md5_test (void);
dbus_bool_t _dbus_sha_test (const char *test_data_dir);
@@ -53,7 +52,8 @@ dbus_bool_t _dbus_sysdeps_test (void);
dbus_bool_t _dbus_spawn_test (const char *test_data_dir);
dbus_bool_t _dbus_userdb_test (const char *test_data_dir);
dbus_bool_t _dbus_memory_test (void);
-
+dbus_bool_t _dbus_object_tree_test (void);
+dbus_bool_t _dbus_pending_call_test (const char *test_data_dir);
void dbus_internal_do_not_use_run_tests (const char *test_data_dir);
dbus_bool_t dbus_internal_do_not_use_try_message_file (const DBusString *filename,
diff --git a/dbus/dbus-threads.c b/dbus/dbus-threads.c
index b604a397..2170c465 100644
--- a/dbus/dbus-threads.c
+++ b/dbus/dbus-threads.c
@@ -223,10 +223,10 @@ init_global_locks (void)
#define LOCK_ADDR(name) (& _dbus_lock_##name)
LOCK_ADDR (list),
LOCK_ADDR (connection_slots),
+ LOCK_ADDR (pending_call_slots),
LOCK_ADDR (server_slots),
LOCK_ADDR (message_slots),
LOCK_ADDR (atomic),
- LOCK_ADDR (message_handler),
LOCK_ADDR (bus),
LOCK_ADDR (shutdown_funcs),
LOCK_ADDR (system_users)
diff --git a/dbus/dbus-threads.h b/dbus/dbus-threads.h
index 0dcb1040..dea969a2 100644
--- a/dbus/dbus-threads.h
+++ b/dbus/dbus-threads.h
@@ -66,30 +66,34 @@ typedef enum
DBUS_THREAD_FUNCTIONS_ALL_MASK = (1 << 10) - 1
} DBusThreadFunctionsMask;
+/**
+ * Functions that must be implemented to make the D-BUS
+ * library thread-aware.
+ */
typedef struct
{
- unsigned int mask;
-
- DBusMutexNewFunction mutex_new;
- DBusMutexFreeFunction mutex_free;
- DBusMutexLockFunction mutex_lock;
- DBusMutexUnlockFunction mutex_unlock;
-
- DBusCondVarNewFunction condvar_new;
- DBusCondVarFreeFunction condvar_free;
- DBusCondVarWaitFunction condvar_wait;
- DBusCondVarWaitTimeoutFunction condvar_wait_timeout;
- DBusCondVarWakeOneFunction condvar_wake_one;
- DBusCondVarWakeAllFunction condvar_wake_all;
+ unsigned int mask; /**< Mask indicating which functions are present. */
+
+ DBusMutexNewFunction mutex_new; /**< Function to create a mutex */
+ DBusMutexFreeFunction mutex_free; /**< Function to free a mutex */
+ DBusMutexLockFunction mutex_lock; /**< Function to lock a mutex */
+ DBusMutexUnlockFunction mutex_unlock; /**< Function to unlock a mutex */
+
+ DBusCondVarNewFunction condvar_new; /**< Function to create a condition variable */
+ DBusCondVarFreeFunction condvar_free; /**< Function to free a condition variable */
+ DBusCondVarWaitFunction condvar_wait; /**< Function to wait on a condition */
+ DBusCondVarWaitTimeoutFunction condvar_wait_timeout; /**< Function to wait on a condition with a timeout */
+ DBusCondVarWakeOneFunction condvar_wake_one; /**< Function to wake one thread waiting on the condition */
+ DBusCondVarWakeAllFunction condvar_wake_all; /**< Function to wake all threads waiting on the condition */
- void (* padding1) (void);
- void (* padding2) (void);
- void (* padding3) (void);
- void (* padding4) (void);
- void (* padding5) (void);
- void (* padding6) (void);
- void (* padding7) (void);
- void (* padding8) (void);
+ void (* padding1) (void); /**< Reserved for future expansion */
+ void (* padding2) (void); /**< Reserved for future expansion */
+ void (* padding3) (void); /**< Reserved for future expansion */
+ void (* padding4) (void); /**< Reserved for future expansion */
+ void (* padding5) (void); /**< Reserved for future expansion */
+ void (* padding6) (void); /**< Reserved for future expansion */
+ void (* padding7) (void); /**< Reserved for future expansion */
+ void (* padding8) (void); /**< Reserved for future expansion */
} DBusThreadFunctions;
diff --git a/dbus/dbus-timeout.c b/dbus/dbus-timeout.c
index 289d6347..b15089db 100644
--- a/dbus/dbus-timeout.c
+++ b/dbus/dbus-timeout.c
@@ -33,6 +33,9 @@
* @{
*/
+/**
+ * Internals of DBusTimeout
+ */
struct DBusTimeout
{
int refcount; /**< Reference count */
diff --git a/dbus/dbus-transport-protected.h b/dbus/dbus-transport-protected.h
index 1c5b4208..d4d20957 100644
--- a/dbus/dbus-transport-protected.h
+++ b/dbus/dbus-transport-protected.h
@@ -34,6 +34,10 @@ DBUS_BEGIN_DECLS;
typedef struct DBusTransportVTable DBusTransportVTable;
+/**
+ * The virtual table that must be implemented to
+ * create a new kind of transport.
+ */
struct DBusTransportVTable
{
void (* finalize) (DBusTransport *transport);
@@ -69,6 +73,12 @@ struct DBusTransportVTable
/**< Outstanding messages counter changed */
};
+/**
+ * Object representing a transport such as a socket.
+ * A transport can shuttle messages from point A to point B,
+ * and is the backend for a #DBusConnection.
+ *
+ */
struct DBusTransport
{
int refcount; /**< Reference count. */
diff --git a/dbus/dbus-transport.c b/dbus/dbus-transport.c
index 59ec6ea1..4625cf25 100644
--- a/dbus/dbus-transport.c
+++ b/dbus/dbus-transport.c
@@ -813,7 +813,9 @@ _dbus_transport_queue_messages (DBusTransport *transport)
{
DBusDispatchStatus status;
+#if 0
_dbus_verbose ("_dbus_transport_queue_messages()\n");
+#endif
/* Queue any messages */
while ((status = _dbus_transport_get_dispatch_status (transport)) == DBUS_DISPATCH_DATA_REMAINS)
diff --git a/dbus/dbus-types.h b/dbus/dbus-types.h
index 854b6526..99cb45f5 100644
--- a/dbus/dbus-types.h
+++ b/dbus/dbus-types.h
@@ -83,6 +83,10 @@ typedef dbus_uint32_t dbus_unichar_t;
*
* A 64-bit unsigned integer on all platforms that support it.
* If supported, #DBUS_HAVE_INT64 will be defined.
+ *
+ * C99 requires a 64-bit type and most likely all interesting
+ * compilers support one. GLib for example flat-out requires
+ * a 64-bit type.
*/
/**
@@ -90,6 +94,10 @@ typedef dbus_uint32_t dbus_unichar_t;
*
* A 64-bit signed integer on all platforms that support it.
* If supported, #DBUS_HAVE_INT64 will be defined.
+ *
+ * C99 requires a 64-bit type and most likely all interesting
+ * compilers support one. GLib for example flat-out requires
+ * a 64-bit type.
*/
/**
diff --git a/dbus/dbus-userdb.c b/dbus/dbus-userdb.c
index 4a7b7488..95f13981 100644
--- a/dbus/dbus-userdb.c
+++ b/dbus/dbus-userdb.c
@@ -26,14 +26,17 @@
#include "dbus-internals.h"
#include <string.h>
+/**
+ * Internals of DBusUserDatabase
+ */
struct DBusUserDatabase
{
- int refcount;
+ int refcount; /**< Reference count */
- DBusHashTable *users;
- DBusHashTable *groups;
- DBusHashTable *users_by_name;
- DBusHashTable *groups_by_name;
+ DBusHashTable *users; /**< Users in the database by UID */
+ DBusHashTable *groups; /**< Groups in the database by GID */
+ DBusHashTable *users_by_name; /**< Users in the database by name */
+ DBusHashTable *groups_by_name; /**< Groups in the database by name */
};
static void
diff --git a/dbus/dbus-watch.c b/dbus/dbus-watch.c
index 5b59e8c8..f212090a 100644
--- a/dbus/dbus-watch.c
+++ b/dbus/dbus-watch.c
@@ -33,6 +33,9 @@
* @{
*/
+/**
+ * Implementation of DBusWatch
+ */
struct DBusWatch
{
int refcount; /**< Reference count */
diff --git a/dbus/dbus.h b/dbus/dbus.h
index 0dd072ac..99eee18c 100644
--- a/dbus/dbus.h
+++ b/dbus/dbus.h
@@ -1,7 +1,7 @@
/* -*- mode: C; c-file-style: "gnu" -*- */
/* dbus.h Convenience header including all other headers
*
- * Copyright (C) 2002 Red Hat Inc.
+ * Copyright (C) 2002, 2003 Red Hat Inc.
*
* Licensed under the Academic Free License version 1.2
*
@@ -37,7 +37,7 @@
#include <dbus/dbus-errors.h>
#include <dbus/dbus-macros.h>
#include <dbus/dbus-message.h>
-#include <dbus/dbus-message-handler.h>
+#include <dbus/dbus-pending-call.h>
#include <dbus/dbus-protocol.h>
#include <dbus/dbus-server.h>
#include <dbus/dbus-threads.h>