diff options
Diffstat (limited to 'dbus')
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 (¬ify_user_data_slot)) +    return NULL; +   +  pending = dbus_new (DBusPendingCall, 1); +   +  if (pending == NULL) +    { +      dbus_pending_call_free_data_slot (¬ify_user_data_slot); +      return NULL; +    } + +  timeout = _dbus_timeout_new (timeout_milliseconds, +                               timeout_handler, +			       pending, NULL);   + +  if (timeout == NULL) +    { +      dbus_pending_call_free_data_slot (¬ify_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 (¬ify_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> | 
