D-Bus  1.10.12
dbus-object-tree.c
00001 /* -*- mode: C; c-file-style: "gnu"; indent-tabs-mode: nil; -*- */
00002 /* dbus-object-tree.c  DBusObjectTree (internals of DBusConnection)
00003  *
00004  * Copyright (C) 2003, 2005  Red Hat Inc.
00005  *
00006  * Licensed under the Academic Free License version 2.1
00007  *
00008  * This program is free software; you can redistribute it and/or modify
00009  * it under the terms of the GNU General Public License as published by
00010  * the Free Software Foundation; either version 2 of the License, or
00011  * (at your option) any later version.
00012  *
00013  * This program is distributed in the hope that it will be useful,
00014  * but WITHOUT ANY WARRANTY; without even the implied warranty of
00015  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
00016  * GNU General Public License for more details.
00017  *
00018  * You should have received a copy of the GNU General Public License
00019  * along with this program; if not, write to the Free Software
00020  * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA
00021  *
00022  */
00023 
00024 #include <config.h>
00025 #include "dbus-object-tree.h"
00026 #include "dbus-connection-internal.h"
00027 #include "dbus-internals.h"
00028 #include "dbus-hash.h"
00029 #include "dbus-protocol.h"
00030 #include "dbus-string.h"
00031 #include <string.h>
00032 #include <stdlib.h>
00033 
00046 typedef struct DBusObjectSubtree DBusObjectSubtree;
00047 
00048 static DBusObjectSubtree* _dbus_object_subtree_new   (const char                  *name,
00049                                                       const DBusObjectPathVTable  *vtable,
00050                                                       void                        *user_data);
00051 static DBusObjectSubtree* _dbus_object_subtree_ref   (DBusObjectSubtree           *subtree);
00052 static void               _dbus_object_subtree_unref (DBusObjectSubtree           *subtree);
00053 
00057 struct DBusObjectTree
00058 {
00059   int                 refcount;   
00060   DBusConnection     *connection; 
00062   DBusObjectSubtree  *root;       
00063 };
00064 
00070 struct DBusObjectSubtree
00071 {
00072   DBusAtomic                         refcount;            
00073   DBusObjectSubtree                 *parent;              
00074   DBusObjectPathUnregisterFunction   unregister_function; 
00075   DBusObjectPathMessageFunction      message_function;    
00076   void                              *user_data;           
00077   DBusObjectSubtree                **subtrees;            
00078   int                                n_subtrees;          
00079   int                                max_subtrees;        
00080   unsigned int                       invoke_as_fallback : 1; 
00081   char                               name[1]; 
00082 };
00083 
00091 DBusObjectTree*
00092 _dbus_object_tree_new (DBusConnection *connection)
00093 {
00094   DBusObjectTree *tree;
00095 
00096   /* the connection passed in here isn't fully constructed,
00097    * so don't do anything more than store a pointer to
00098    * it
00099    */
00100 
00101   tree = dbus_new0 (DBusObjectTree, 1);
00102   if (tree == NULL)
00103     goto oom;
00104 
00105   tree->refcount = 1;
00106   tree->connection = connection;
00107   tree->root = _dbus_object_subtree_new ("/", NULL, NULL);
00108   if (tree->root == NULL)
00109     goto oom;
00110   tree->root->invoke_as_fallback = TRUE;
00111   
00112   return tree;
00113 
00114  oom:
00115   if (tree)
00116     {
00117       dbus_free (tree);
00118     }
00119 
00120   return NULL;
00121 }
00122 
00128 DBusObjectTree *
00129 _dbus_object_tree_ref (DBusObjectTree *tree)
00130 {
00131   _dbus_assert (tree->refcount > 0);
00132 
00133   tree->refcount += 1;
00134 
00135   return tree;
00136 }
00137 
00142 void
00143 _dbus_object_tree_unref (DBusObjectTree *tree)
00144 {
00145   _dbus_assert (tree->refcount > 0);
00146 
00147   tree->refcount -= 1;
00148 
00149   if (tree->refcount == 0)
00150     {
00151       _dbus_object_tree_free_all_unlocked (tree);
00152 
00153       dbus_free (tree);
00154     }
00155 }
00156 
00160 #define VERBOSE_FIND 0
00161 
00162 static DBusObjectSubtree*
00163 find_subtree_recurse (DBusObjectSubtree  *subtree,
00164                       const char        **path,
00165                       dbus_bool_t         create_if_not_found,
00166                       int                *index_in_parent,
00167                       dbus_bool_t        *exact_match)
00168 {
00169   int i, j;
00170   dbus_bool_t return_deepest_match;
00171 
00172   return_deepest_match = exact_match != NULL;
00173 
00174   _dbus_assert (!(return_deepest_match && create_if_not_found));
00175 
00176   if (path[0] == NULL)
00177     {
00178 #if VERBOSE_FIND
00179       _dbus_verbose ("  path exhausted, returning %s\n",
00180                      subtree->name);
00181 #endif
00182       if (exact_match != NULL)
00183         *exact_match = TRUE;
00184       return subtree;
00185     }
00186 
00187 #if VERBOSE_FIND
00188   _dbus_verbose ("  searching children of %s for %s\n",
00189                  subtree->name, path[0]);
00190 #endif
00191   
00192   i = 0;
00193   j = subtree->n_subtrees;
00194   while (i < j)
00195     {
00196       int k, v;
00197 
00198       k = (i + j) / 2;
00199       v = strcmp (path[0], subtree->subtrees[k]->name);
00200 
00201 #if VERBOSE_FIND
00202       _dbus_verbose ("  %s cmp %s = %d\n",
00203                      path[0], subtree->subtrees[k]->name,
00204                      v);
00205 #endif
00206       
00207       if (v == 0)
00208         {
00209           if (index_in_parent)
00210             {
00211 #if VERBOSE_FIND
00212               _dbus_verbose ("  storing parent index %d\n", k);
00213 #endif
00214               *index_in_parent = k;
00215             }
00216 
00217           if (return_deepest_match)
00218             {
00219               DBusObjectSubtree *next;
00220 
00221               next = find_subtree_recurse (subtree->subtrees[k],
00222                                            &path[1], create_if_not_found, 
00223                                            index_in_parent, exact_match);
00224               if (next == NULL &&
00225                   subtree->invoke_as_fallback)
00226                 {
00227 #if VERBOSE_FIND
00228                   _dbus_verbose ("  no deeper match found, returning %s\n",
00229                                  subtree->name);
00230 #endif
00231                   if (exact_match != NULL)
00232                     *exact_match = FALSE;
00233                   return subtree;
00234                 }
00235               else
00236                 return next;
00237             }
00238           else
00239             return find_subtree_recurse (subtree->subtrees[k],
00240                                          &path[1], create_if_not_found, 
00241                                          index_in_parent, exact_match);
00242         }
00243       else if (v < 0)
00244         {
00245           j = k;
00246         }
00247       else
00248         {
00249           i = k + 1;
00250         }
00251     }
00252 
00253 #if VERBOSE_FIND
00254   _dbus_verbose ("  no match found, current tree %s, create_if_not_found = %d\n",
00255                  subtree->name, create_if_not_found);
00256 #endif
00257   
00258   if (create_if_not_found)
00259     {
00260       DBusObjectSubtree* child;
00261       int child_pos, new_n_subtrees;
00262 
00263 #if VERBOSE_FIND
00264       _dbus_verbose ("  creating subtree %s\n",
00265                      path[0]);
00266 #endif
00267       
00268       child = _dbus_object_subtree_new (path[0],
00269                                         NULL, NULL);
00270       if (child == NULL)
00271         return NULL;
00272 
00273       new_n_subtrees = subtree->n_subtrees + 1;
00274       if (new_n_subtrees > subtree->max_subtrees)
00275         {
00276           int new_max_subtrees;
00277           DBusObjectSubtree **new_subtrees;
00278 
00279           new_max_subtrees = subtree->max_subtrees == 0 ? 1 : 2 * subtree->max_subtrees;
00280           new_subtrees = dbus_realloc (subtree->subtrees,
00281                                        new_max_subtrees * sizeof (DBusObjectSubtree*));
00282           if (new_subtrees == NULL)
00283             {
00284               _dbus_object_subtree_unref (child);
00285               return NULL;
00286             }
00287           subtree->subtrees = new_subtrees;
00288           subtree->max_subtrees = new_max_subtrees;
00289         }
00290 
00291       /* The binary search failed, so i == j points to the 
00292          place the child should be inserted. */
00293       child_pos = i;
00294       _dbus_assert (child_pos < new_n_subtrees &&
00295                     new_n_subtrees <= subtree->max_subtrees);
00296       if (child_pos + 1 < new_n_subtrees)
00297         {
00298           memmove (&subtree->subtrees[child_pos+1], 
00299                    &subtree->subtrees[child_pos], 
00300                    (new_n_subtrees - child_pos - 1) * 
00301                    sizeof subtree->subtrees[0]);
00302         }
00303       subtree->subtrees[child_pos] = child;
00304 
00305       if (index_in_parent)
00306         *index_in_parent = child_pos;
00307       subtree->n_subtrees = new_n_subtrees;
00308       child->parent = subtree;
00309 
00310       return find_subtree_recurse (child,
00311                                    &path[1], create_if_not_found, 
00312                                    index_in_parent, exact_match);
00313     }
00314   else
00315     {
00316       if (exact_match != NULL)
00317         *exact_match = FALSE;
00318       return (return_deepest_match && subtree->invoke_as_fallback) ? subtree : NULL;
00319     }
00320 }
00321 
00322 #ifdef DBUS_ENABLE_EMBEDDED_TESTS
00323 static DBusObjectSubtree*
00324 find_subtree (DBusObjectTree *tree,
00325               const char    **path,
00326               int            *index_in_parent)
00327 {
00328   DBusObjectSubtree *subtree;
00329 
00330 #if VERBOSE_FIND
00331   _dbus_verbose ("Looking for exact registered subtree\n");
00332 #endif
00333   
00334   subtree = find_subtree_recurse (tree->root, path, FALSE, index_in_parent, NULL);
00335 
00336   if (subtree && subtree->message_function == NULL)
00337     return NULL;
00338   else
00339     return subtree;
00340 }
00341 #endif
00342 
00343 static DBusObjectSubtree*
00344 lookup_subtree (DBusObjectTree *tree,
00345                 const char    **path)
00346 {
00347 #if VERBOSE_FIND
00348   _dbus_verbose ("Looking for subtree\n");
00349 #endif
00350   return find_subtree_recurse (tree->root, path, FALSE, NULL, NULL);
00351 }
00352 
00353 static DBusObjectSubtree*
00354 find_handler (DBusObjectTree *tree,
00355               const char    **path,
00356               dbus_bool_t    *exact_match)
00357 {
00358 #if VERBOSE_FIND
00359   _dbus_verbose ("Looking for deepest handler\n");
00360 #endif
00361   _dbus_assert (exact_match != NULL);
00362 
00363   *exact_match = FALSE; /* ensure always initialized */
00364   
00365   return find_subtree_recurse (tree->root, path, FALSE, NULL, exact_match);
00366 }
00367 
00368 static DBusObjectSubtree*
00369 ensure_subtree (DBusObjectTree *tree,
00370                 const char    **path)
00371 {
00372 #if VERBOSE_FIND
00373   _dbus_verbose ("Ensuring subtree\n");
00374 #endif
00375   return find_subtree_recurse (tree->root, path, TRUE, NULL, NULL);
00376 }
00377 
00378 static char *flatten_path (const char **path);
00379 
00392 dbus_bool_t
00393 _dbus_object_tree_register (DBusObjectTree              *tree,
00394                             dbus_bool_t                  fallback,
00395                             const char                 **path,
00396                             const DBusObjectPathVTable  *vtable,
00397                             void                        *user_data,
00398                             DBusError                   *error)
00399 {
00400   DBusObjectSubtree  *subtree;
00401 
00402   _dbus_assert (tree != NULL);
00403   _dbus_assert (vtable->message_function != NULL);
00404   _dbus_assert (path != NULL);
00405 
00406   subtree = ensure_subtree (tree, path);
00407   if (subtree == NULL)
00408     {
00409       _DBUS_SET_OOM (error);
00410       return FALSE;
00411     }
00412 
00413   if (subtree->message_function != NULL)
00414     {
00415       if (error != NULL)
00416         {
00417           char *complete_path = flatten_path (path);
00418 
00419           dbus_set_error (error, DBUS_ERROR_OBJECT_PATH_IN_USE,
00420                           "A handler is already registered for %s",
00421                           complete_path ? complete_path
00422                                         : "(cannot represent path: out of memory!)");
00423 
00424           dbus_free (complete_path);
00425         }
00426 
00427       return FALSE;
00428     }
00429 
00430   subtree->message_function = vtable->message_function;
00431   subtree->unregister_function = vtable->unregister_function;
00432   subtree->user_data = user_data;
00433   subtree->invoke_as_fallback = fallback != FALSE;
00434 
00435   return TRUE;
00436 }
00437 
00449 static dbus_bool_t
00450 unregister_subtree (DBusObjectSubtree                 *subtree,
00451                     DBusObjectPathUnregisterFunction  *unregister_function_out,
00452                     void                             **user_data_out)
00453 {
00454   _dbus_assert (subtree != NULL);
00455   _dbus_assert (unregister_function_out != NULL);
00456   _dbus_assert (user_data_out != NULL);
00457 
00458   /* Confirm subtree is registered */
00459   if (subtree->message_function != NULL)
00460     {
00461       subtree->message_function = NULL;
00462 
00463       *unregister_function_out = subtree->unregister_function;
00464       *user_data_out = subtree->user_data;
00465 
00466       subtree->unregister_function = NULL;
00467       subtree->user_data = NULL;
00468 
00469       return TRUE;
00470     }
00471   else
00472     {
00473       /* Assert that this unregistered subtree is either the root node or has
00474          children, otherwise we have a dangling path which should never
00475          happen */
00476       _dbus_assert (subtree->parent == NULL || subtree->n_subtrees > 0);
00477 
00478       /* The subtree is not registered */
00479       return FALSE;
00480     }
00481 }
00482 
00494 static dbus_bool_t
00495 attempt_child_removal (DBusObjectSubtree  *parent,
00496                        int child_index)
00497 {
00498   /* Candidate for removal */
00499   DBusObjectSubtree* candidate;
00500 
00501   _dbus_assert (parent != NULL);
00502   _dbus_assert (child_index >= 0 && child_index < parent->n_subtrees);
00503 
00504   candidate = parent->subtrees[child_index];
00505   _dbus_assert (candidate != NULL);
00506 
00507   if (candidate->n_subtrees == 0 && candidate->message_function == NULL)
00508     {
00509       /* The candidate node is childless and is not a registered
00510          path, so... */
00511 
00512       /* ... remove it from its parent... */
00513       /* Assumes a 0-byte memmove is OK */
00514       memmove (&parent->subtrees[child_index],
00515                &parent->subtrees[child_index + 1],
00516                (parent->n_subtrees - child_index - 1)
00517                * sizeof (parent->subtrees[0]));
00518       parent->n_subtrees -= 1;
00519 
00520       /* ... and free it */
00521       candidate->parent = NULL;
00522       _dbus_object_subtree_unref (candidate);
00523 
00524       return TRUE;
00525     }
00526   return FALSE;
00527 }
00528 
00566 static dbus_bool_t
00567 unregister_and_free_path_recurse
00568 (DBusObjectSubtree                 *subtree,
00569  const char                       **path,
00570  dbus_bool_t                       *continue_removal_attempts,
00571  DBusObjectPathUnregisterFunction  *unregister_function_out,
00572  void                             **user_data_out)
00573 {
00574   int i, j;
00575 
00576   _dbus_assert (continue_removal_attempts != NULL);
00577   _dbus_assert (*continue_removal_attempts);
00578   _dbus_assert (unregister_function_out != NULL);
00579   _dbus_assert (user_data_out != NULL);
00580 
00581   if (path[0] == NULL)
00582     return unregister_subtree (subtree, unregister_function_out, user_data_out);
00583 
00584   i = 0;
00585   j = subtree->n_subtrees;
00586   while (i < j)
00587     {
00588       int k, v;
00589 
00590       k = (i + j) / 2;
00591       v = strcmp (path[0], subtree->subtrees[k]->name);
00592 
00593       if (v == 0)
00594         {
00595           dbus_bool_t freed;
00596           freed = unregister_and_free_path_recurse (subtree->subtrees[k],
00597                                                     &path[1],
00598                                                     continue_removal_attempts,
00599                                                     unregister_function_out,
00600                                                     user_data_out);
00601           if (freed && *continue_removal_attempts)
00602             *continue_removal_attempts = attempt_child_removal (subtree, k);
00603           return freed;
00604         }
00605       else if (v < 0)
00606         {
00607           j = k;
00608         }
00609       else
00610         {
00611           i = k + 1;
00612         }
00613     }
00614   return FALSE;
00615 }
00616 
00624 void
00625 _dbus_object_tree_unregister_and_unlock (DBusObjectTree          *tree,
00626                                          const char             **path)
00627 {
00628   dbus_bool_t found_subtree;
00629   dbus_bool_t continue_removal_attempts;
00630   DBusObjectPathUnregisterFunction unregister_function;
00631   void *user_data;
00632   DBusConnection *connection;
00633 
00634   _dbus_assert (tree != NULL);
00635   _dbus_assert (path != NULL);
00636 
00637   continue_removal_attempts = TRUE;
00638   unregister_function = NULL;
00639   user_data = NULL;
00640 
00641   found_subtree = unregister_and_free_path_recurse (tree->root,
00642                                                     path,
00643                                                     &continue_removal_attempts,
00644                                                     &unregister_function,
00645                                                     &user_data);
00646 
00647 #ifndef DBUS_DISABLE_CHECKS
00648   if (found_subtree == FALSE)
00649     {
00650       _dbus_warn ("Attempted to unregister path (path[0] = %s path[1] = %s) which isn't registered\n",
00651                   path[0] ? path[0] : "null",
00652                   (path[0] && path[1]) ? path[1] : "null");
00653       goto unlock;    
00654     }
00655 #else
00656   _dbus_assert (found_subtree == TRUE);
00657 #endif
00658 
00659 unlock:
00660   connection = tree->connection;
00661 
00662   /* Unlock and call application code */
00663 #ifdef DBUS_ENABLE_EMBEDDED_TESTS
00664   if (connection)
00665 #endif
00666     {
00667       _dbus_connection_ref_unlocked (connection);
00668       _dbus_verbose ("unlock\n");
00669       _dbus_connection_unlock (connection);
00670     }
00671 
00672   if (unregister_function)
00673     (* unregister_function) (connection, user_data);
00674 
00675 #ifdef DBUS_ENABLE_EMBEDDED_TESTS
00676   if (connection)
00677 #endif
00678     dbus_connection_unref (connection);
00679 }
00680 
00681 static void
00682 free_subtree_recurse (DBusConnection    *connection,
00683                       DBusObjectSubtree *subtree)
00684 {
00685   /* Delete them from the end, for slightly
00686    * more robustness against odd reentrancy.
00687    */
00688   while (subtree->n_subtrees > 0)
00689     {
00690       DBusObjectSubtree *child;
00691 
00692       child = subtree->subtrees[subtree->n_subtrees - 1];
00693       subtree->subtrees[subtree->n_subtrees - 1] = NULL;
00694       subtree->n_subtrees -= 1;
00695       child->parent = NULL;
00696 
00697       free_subtree_recurse (connection, child);
00698     }
00699 
00700   /* Call application code */
00701   if (subtree->unregister_function)
00702     (* subtree->unregister_function) (connection,
00703                                       subtree->user_data);
00704 
00705   subtree->message_function = NULL;
00706   subtree->unregister_function = NULL;
00707   subtree->user_data = NULL;
00708 
00709   /* Now free ourselves */
00710   _dbus_object_subtree_unref (subtree);
00711 }
00712 
00719 void
00720 _dbus_object_tree_free_all_unlocked (DBusObjectTree *tree)
00721 {
00722   if (tree->root)
00723     free_subtree_recurse (tree->connection,
00724                           tree->root);
00725   tree->root = NULL;
00726 }
00727 
00728 static dbus_bool_t
00729 _dbus_object_tree_list_registered_unlocked (DBusObjectTree *tree,
00730                                             const char    **parent_path,
00731                                             char         ***child_entries)
00732 {
00733   DBusObjectSubtree *subtree;
00734   char **retval;
00735   
00736   _dbus_assert (parent_path != NULL);
00737   _dbus_assert (child_entries != NULL);
00738 
00739   *child_entries = NULL;
00740   
00741   subtree = lookup_subtree (tree, parent_path);
00742   if (subtree == NULL)
00743     {
00744       retval = dbus_new0 (char *, 1);
00745     }
00746   else
00747     {
00748       int i;
00749       retval = dbus_new0 (char*, subtree->n_subtrees + 1);
00750       if (retval == NULL)
00751         goto out;
00752       i = 0;
00753       while (i < subtree->n_subtrees)
00754         {
00755           retval[i] = _dbus_strdup (subtree->subtrees[i]->name);
00756           if (retval[i] == NULL)
00757             {
00758               dbus_free_string_array (retval);
00759               retval = NULL;
00760               goto out;
00761             }
00762           ++i;
00763         }
00764     }
00765 
00766  out:
00767     
00768   *child_entries = retval;
00769   return retval != NULL;
00770 }
00771 
00772 static DBusHandlerResult
00773 handle_default_introspect_and_unlock (DBusObjectTree          *tree,
00774                                       DBusMessage             *message,
00775                                       const char             **path)
00776 {
00777   DBusString xml;
00778   DBusHandlerResult result;
00779   char **children;
00780   int i;
00781   DBusMessage *reply;
00782   DBusMessageIter iter;
00783   const char *v_STRING;
00784   dbus_bool_t already_unlocked;
00785 
00786   /* We have the connection lock here */
00787 
00788   already_unlocked = FALSE;
00789   
00790   _dbus_verbose (" considering default Introspect() handler...\n");
00791 
00792   reply = NULL;
00793   
00794   if (!dbus_message_is_method_call (message,
00795                                     DBUS_INTERFACE_INTROSPECTABLE,
00796                                     "Introspect"))
00797     {
00798 #ifdef DBUS_ENABLE_EMBEDDED_TESTS
00799       if (tree->connection)
00800 #endif
00801         {
00802           _dbus_verbose ("unlock\n");
00803           _dbus_connection_unlock (tree->connection);
00804         }
00805       
00806       return DBUS_HANDLER_RESULT_NOT_YET_HANDLED;
00807     }
00808 
00809   _dbus_verbose (" using default Introspect() handler!\n");
00810   
00811   if (!_dbus_string_init (&xml))
00812     {
00813 #ifdef DBUS_ENABLE_EMBEDDED_TESTS
00814       if (tree->connection)
00815 #endif
00816         {
00817           _dbus_verbose ("unlock\n");
00818           _dbus_connection_unlock (tree->connection);
00819         }
00820 
00821       return DBUS_HANDLER_RESULT_NEED_MEMORY;
00822     }
00823 
00824   result = DBUS_HANDLER_RESULT_NEED_MEMORY;
00825 
00826   children = NULL;
00827   if (!_dbus_object_tree_list_registered_unlocked (tree, path, &children))
00828     goto out;
00829 
00830   if (!_dbus_string_append (&xml, DBUS_INTROSPECT_1_0_XML_DOCTYPE_DECL_NODE))
00831     goto out;
00832   
00833   if (!_dbus_string_append (&xml, "<node>\n"))
00834     goto out;
00835 
00836   i = 0;
00837   while (children[i] != NULL)
00838     {
00839       if (!_dbus_string_append_printf (&xml, "  <node name=\"%s\"/>\n",
00840                                        children[i]))
00841         goto out;
00842 
00843       ++i;
00844     }
00845 
00846   if (!_dbus_string_append (&xml, "</node>\n"))
00847     goto out;
00848 
00849   reply = dbus_message_new_method_return (message);
00850   if (reply == NULL)
00851     goto out;
00852 
00853   dbus_message_iter_init_append (reply, &iter);
00854   v_STRING = _dbus_string_get_const_data (&xml);
00855   if (!dbus_message_iter_append_basic (&iter, DBUS_TYPE_STRING, &v_STRING))
00856     goto out;
00857   
00858 #ifdef DBUS_ENABLE_EMBEDDED_TESTS
00859   if (tree->connection)
00860 #endif
00861     {
00862       already_unlocked = TRUE;
00863       
00864       if (!_dbus_connection_send_and_unlock (tree->connection, reply, NULL))
00865         goto out;
00866     }
00867   
00868   result = DBUS_HANDLER_RESULT_HANDLED;
00869   
00870  out:
00871 #ifdef DBUS_ENABLE_EMBEDDED_TESTS
00872   if (tree->connection)
00873 #endif
00874     {
00875       if (!already_unlocked)
00876         {
00877           _dbus_verbose ("unlock\n");
00878           _dbus_connection_unlock (tree->connection);
00879         }
00880     }
00881   
00882   _dbus_string_free (&xml);
00883   dbus_free_string_array (children);
00884   if (reply)
00885     dbus_message_unref (reply);
00886   
00887   return result;
00888 }
00889 
00904 DBusHandlerResult
00905 _dbus_object_tree_dispatch_and_unlock (DBusObjectTree          *tree,
00906                                        DBusMessage             *message,
00907                                        dbus_bool_t             *found_object)
00908 {
00909   char **path;
00910   dbus_bool_t exact_match;
00911   DBusList *list;
00912   DBusList *link;
00913   DBusHandlerResult result;
00914   DBusObjectSubtree *subtree;
00915   
00916 #if 0
00917   _dbus_verbose ("Dispatch of message by object path\n");
00918 #endif
00919   
00920   path = NULL;
00921   if (!dbus_message_get_path_decomposed (message, &path))
00922     {
00923 #ifdef DBUS_ENABLE_EMBEDDED_TESTS
00924       if (tree->connection)
00925 #endif
00926         {
00927           _dbus_verbose ("unlock\n");
00928           _dbus_connection_unlock (tree->connection);
00929         }
00930       
00931       _dbus_verbose ("No memory to get decomposed path\n");
00932 
00933       return DBUS_HANDLER_RESULT_NEED_MEMORY;
00934     }
00935 
00936   if (path == NULL)
00937     {
00938 #ifdef DBUS_ENABLE_EMBEDDED_TESTS
00939       if (tree->connection)
00940 #endif
00941         {
00942           _dbus_verbose ("unlock\n");
00943           _dbus_connection_unlock (tree->connection);
00944         }
00945       
00946       _dbus_verbose ("No path field in message\n");
00947       return DBUS_HANDLER_RESULT_NOT_YET_HANDLED;
00948     }
00949   
00950   /* Find the deepest path that covers the path in the message */
00951   subtree = find_handler (tree, (const char**) path, &exact_match);
00952   
00953   if (found_object)
00954     *found_object = !!subtree;
00955 
00956   /* Build a list of all paths that cover the path in the message */
00957 
00958   list = NULL;
00959 
00960   while (subtree != NULL)
00961     {
00962       if (subtree->message_function != NULL && (exact_match || subtree->invoke_as_fallback))
00963         {
00964           _dbus_object_subtree_ref (subtree);
00965 
00966           /* run deepest paths first */
00967           if (!_dbus_list_append (&list, subtree))
00968             {
00969               result = DBUS_HANDLER_RESULT_NEED_MEMORY;
00970               _dbus_object_subtree_unref (subtree);
00971               goto free_and_return;
00972             }
00973         }
00974 
00975       exact_match = FALSE;
00976       subtree = subtree->parent;
00977     }
00978 
00979   _dbus_verbose ("%d handlers in the path tree for this message\n",
00980                  _dbus_list_get_length (&list));
00981 
00982   /* Invoke each handler in the list */
00983 
00984   result = DBUS_HANDLER_RESULT_NOT_YET_HANDLED;
00985 
00986   link = _dbus_list_get_first_link (&list);
00987   while (link != NULL)
00988     {
00989       DBusList *next = _dbus_list_get_next_link (&list, link);
00990       subtree = link->data;
00991 
00992       /* message_function is NULL if we're unregistered
00993        * due to reentrancy
00994        */
00995       if (subtree->message_function)
00996         {
00997           DBusObjectPathMessageFunction message_function;
00998           void *user_data;
00999 
01000           message_function = subtree->message_function;
01001           user_data = subtree->user_data;
01002 
01003 #if 0
01004           _dbus_verbose ("  (invoking a handler)\n");
01005 #endif
01006           
01007 #ifdef DBUS_ENABLE_EMBEDDED_TESTS
01008           if (tree->connection)
01009 #endif
01010             {
01011               _dbus_verbose ("unlock\n");
01012               _dbus_connection_unlock (tree->connection);
01013             }
01014 
01015           /* FIXME you could unregister the subtree in another thread
01016            * before we invoke the callback, and I can't figure out a
01017            * good way to solve this.
01018            */
01019 
01020           result = (* message_function) (tree->connection,
01021                                          message,
01022                                          user_data);
01023 
01024 #ifdef DBUS_ENABLE_EMBEDDED_TESTS
01025           if (tree->connection)
01026 #endif
01027             _dbus_connection_lock (tree->connection);
01028 
01029           if (result != DBUS_HANDLER_RESULT_NOT_YET_HANDLED)
01030             goto free_and_return;
01031         }
01032 
01033       link = next;
01034     }
01035 
01036  free_and_return:
01037 
01038   if (result == DBUS_HANDLER_RESULT_NOT_YET_HANDLED)
01039     {
01040       /* This hardcoded default handler does a minimal Introspect()
01041        */
01042       result = handle_default_introspect_and_unlock (tree, message,
01043                                                      (const char**) path);
01044     }
01045   else
01046     {
01047 #ifdef DBUS_ENABLE_EMBEDDED_TESTS
01048       if (tree->connection)
01049 #endif
01050         {
01051           _dbus_verbose ("unlock\n");
01052           _dbus_connection_unlock (tree->connection);
01053         }
01054     }
01055   
01056   while (list != NULL)
01057     {
01058       link = _dbus_list_get_first_link (&list);
01059       _dbus_object_subtree_unref (link->data);
01060       _dbus_list_remove_link (&list, link);
01061     }
01062   
01063   dbus_free_string_array (path);
01064 
01065   return result;
01066 }
01067 
01076 void*
01077 _dbus_object_tree_get_user_data_unlocked (DBusObjectTree *tree,
01078                                           const char    **path)
01079 {
01080   dbus_bool_t exact_match;
01081   DBusObjectSubtree *subtree;
01082 
01083   _dbus_assert (tree != NULL);
01084   _dbus_assert (path != NULL);
01085   
01086   /* Find the deepest path that covers the path in the message */
01087   subtree = find_handler (tree, (const char**) path, &exact_match);
01088 
01089   if ((subtree == NULL) || !exact_match)
01090     {
01091       _dbus_verbose ("No object at specified path found\n");
01092       return NULL;
01093     }
01094 
01095   return subtree->user_data;
01096 }
01097 
01104 static DBusObjectSubtree*
01105 allocate_subtree_object (const char *name)
01106 {
01107   int len;
01108   DBusObjectSubtree *subtree;
01109   const size_t front_padding = _DBUS_STRUCT_OFFSET (DBusObjectSubtree, name);
01110 
01111   _dbus_assert (name != NULL);
01112 
01113   len = strlen (name);
01114 
01115   subtree = dbus_malloc0 (MAX (front_padding + (len + 1), sizeof (DBusObjectSubtree)));
01116 
01117   if (subtree == NULL)
01118     return NULL;
01119 
01120   memcpy (subtree->name, name, len + 1);
01121 
01122   return subtree;
01123 }
01124 
01125 static DBusObjectSubtree*
01126 _dbus_object_subtree_new (const char                  *name,
01127                           const DBusObjectPathVTable  *vtable,
01128                           void                        *user_data)
01129 {
01130   DBusObjectSubtree *subtree;
01131 
01132   subtree = allocate_subtree_object (name);
01133   if (subtree == NULL)
01134     goto oom;
01135 
01136   _dbus_assert (name != NULL);
01137 
01138   subtree->parent = NULL;
01139 
01140   if (vtable)
01141     {
01142       subtree->message_function = vtable->message_function;
01143       subtree->unregister_function = vtable->unregister_function;
01144     }
01145   else
01146     {
01147       subtree->message_function = NULL;
01148       subtree->unregister_function = NULL;
01149     }
01150 
01151   subtree->user_data = user_data;
01152   _dbus_atomic_inc (&subtree->refcount);
01153   subtree->subtrees = NULL;
01154   subtree->n_subtrees = 0;
01155   subtree->max_subtrees = 0;
01156   subtree->invoke_as_fallback = FALSE;
01157 
01158   return subtree;
01159 
01160  oom:
01161   return NULL;
01162 }
01163 
01164 static DBusObjectSubtree *
01165 _dbus_object_subtree_ref (DBusObjectSubtree *subtree)
01166 {
01167 #ifdef DBUS_DISABLE_ASSERT
01168   _dbus_atomic_inc (&subtree->refcount);
01169 #else
01170   dbus_int32_t old_value;
01171 
01172   old_value = _dbus_atomic_inc (&subtree->refcount);
01173   _dbus_assert (old_value > 0);
01174 #endif
01175 
01176   return subtree;
01177 }
01178 
01179 static void
01180 _dbus_object_subtree_unref (DBusObjectSubtree *subtree)
01181 {
01182   dbus_int32_t old_value;
01183 
01184   old_value = _dbus_atomic_dec (&subtree->refcount);
01185   _dbus_assert (old_value > 0);
01186 
01187   if (old_value == 1)
01188     {
01189       _dbus_assert (subtree->unregister_function == NULL);
01190       _dbus_assert (subtree->message_function == NULL);
01191 
01192       dbus_free (subtree->subtrees);
01193       dbus_free (subtree);
01194     }
01195 }
01196 
01207 dbus_bool_t
01208 _dbus_object_tree_list_registered_and_unlock (DBusObjectTree *tree,
01209                                               const char    **parent_path,
01210                                               char         ***child_entries)
01211 {
01212   dbus_bool_t result;
01213 
01214   result = _dbus_object_tree_list_registered_unlocked (tree,
01215                                                        parent_path,
01216                                                        child_entries);
01217   
01218 #ifdef DBUS_ENABLE_EMBEDDED_TESTS
01219   if (tree->connection)
01220 #endif
01221     {
01222       _dbus_verbose ("unlock\n");
01223       _dbus_connection_unlock (tree->connection);
01224     }
01225 
01226   return result;
01227 }
01228 
01229 
01231 #define VERBOSE_DECOMPOSE 0
01232 
01243 dbus_bool_t
01244 _dbus_decompose_path (const char*     data,
01245                       int             len,
01246                       char         ***path,
01247                       int            *path_len)
01248 {
01249   char **retval;
01250   int n_components;
01251   int i, j, comp;
01252 
01253   _dbus_assert (data != NULL);
01254   _dbus_assert (path != NULL);
01255   
01256 #if VERBOSE_DECOMPOSE
01257   _dbus_verbose ("Decomposing path \"%s\"\n",
01258                  data);
01259 #endif
01260   
01261   n_components = 0;
01262   if (len > 1) /* if path is not just "/" */
01263     {
01264       i = 0;
01265       while (i < len)
01266         {
01267           _dbus_assert (data[i] != '\0');
01268           if (data[i] == '/')
01269             n_components += 1;
01270           ++i;
01271         }
01272     }
01273   
01274   retval = dbus_new0 (char*, n_components + 1);
01275 
01276   if (retval == NULL)
01277     return FALSE;
01278 
01279   comp = 0;
01280   if (n_components == 0)
01281     i = 1;
01282   else
01283     i = 0;
01284   while (comp < n_components)
01285     {
01286       _dbus_assert (i < len);
01287       
01288       if (data[i] == '/')
01289         ++i;
01290       j = i;
01291 
01292       while (j < len && data[j] != '/')
01293         ++j;
01294 
01295       /* Now [i, j) is the path component */
01296       _dbus_assert (i < j);
01297       _dbus_assert (data[i] != '/');
01298       _dbus_assert (j == len || data[j] == '/');
01299 
01300 #if VERBOSE_DECOMPOSE
01301       _dbus_verbose ("  (component in [%d,%d))\n",
01302                      i, j);
01303 #endif
01304       
01305       retval[comp] = _dbus_memdup (&data[i], j - i + 1);
01306       if (retval[comp] == NULL)
01307         {
01308           dbus_free_string_array (retval);
01309           return FALSE;
01310         }
01311       retval[comp][j-i] = '\0';
01312 #if VERBOSE_DECOMPOSE
01313       _dbus_verbose ("  (component %d = \"%s\")\n",
01314                      comp, retval[comp]);
01315 #endif
01316 
01317       ++comp;
01318       i = j;
01319     }
01320   _dbus_assert (i == len);
01321   
01322   *path = retval;
01323   if (path_len)
01324     *path_len = n_components;
01325   
01326   return TRUE;
01327 }
01328 
01331 static char*
01332 flatten_path (const char **path)
01333 {
01334   DBusString str;
01335   char *s;
01336 
01337   if (!_dbus_string_init (&str))
01338     return NULL;
01339 
01340   if (path[0] == NULL)
01341     {
01342       if (!_dbus_string_append_byte (&str, '/'))
01343         goto nomem;
01344     }
01345   else
01346     {
01347       int i;
01348       
01349       i = 0;
01350       while (path[i])
01351         {
01352           if (!_dbus_string_append_byte (&str, '/'))
01353             goto nomem;
01354           
01355           if (!_dbus_string_append (&str, path[i]))
01356             goto nomem;
01357           
01358           ++i;
01359         }
01360     }
01361 
01362   if (!_dbus_string_steal_data (&str, &s))
01363     goto nomem;
01364 
01365   _dbus_string_free (&str);
01366 
01367   return s;
01368 
01369  nomem:
01370   _dbus_string_free (&str);
01371   return NULL;
01372 }
01373 
01374 
01375 #ifdef DBUS_ENABLE_EMBEDDED_TESTS
01376 
01377 #ifndef DOXYGEN_SHOULD_SKIP_THIS
01378 
01379 #include "dbus-test.h"
01380 #include <stdio.h>
01381 
01382 typedef enum 
01383 {
01384   STR_EQUAL,
01385   STR_PREFIX,
01386   STR_DIFFERENT
01387 } StrComparison;
01388 
01389 /* Returns TRUE if container is a parent of child
01390  */
01391 static StrComparison
01392 path_contains (const char **container,
01393                const char **child)
01394 {
01395   int i;
01396 
01397   i = 0;
01398   while (child[i] != NULL)
01399     {
01400       int v;
01401 
01402       if (container[i] == NULL)
01403         return STR_PREFIX; /* container ran out, child continues;
01404                             * thus the container is a parent of the
01405                             * child.
01406                             */
01407 
01408       _dbus_assert (container[i] != NULL);
01409       _dbus_assert (child[i] != NULL);
01410 
01411       v = strcmp (container[i], child[i]);
01412 
01413       if (v != 0)
01414         return STR_DIFFERENT; /* they overlap until here and then are different,
01415                                * not overlapping
01416                                */
01417 
01418       ++i;
01419     }
01420 
01421   /* Child ran out; if container also did, they are equal;
01422    * otherwise, the child is a parent of the container.
01423    */
01424   if (container[i] == NULL)
01425     return STR_EQUAL;
01426   else
01427     return STR_DIFFERENT;
01428 }
01429 
01430 #if 0
01431 static void
01432 spew_subtree_recurse (DBusObjectSubtree *subtree,
01433                       int                indent)
01434 {
01435   int i;
01436 
01437   i = 0;
01438   while (i < indent)
01439     {
01440       _dbus_verbose (" ");
01441       ++i;
01442     }
01443 
01444   _dbus_verbose ("%s (%d children)\n",
01445                  subtree->name, subtree->n_subtrees);
01446 
01447   i = 0;
01448   while (i < subtree->n_subtrees)
01449     {
01450       spew_subtree_recurse (subtree->subtrees[i], indent + 2);
01451 
01452       ++i;
01453     }
01454 }
01455 
01456 static void
01457 spew_tree (DBusObjectTree *tree)
01458 {
01459   spew_subtree_recurse (tree->root, 0);
01460 }
01461 #endif
01462 
01466 typedef struct
01467 {
01468   const char **path; 
01469   dbus_bool_t handler_fallback; 
01470   dbus_bool_t message_handled; 
01471   dbus_bool_t handler_unregistered; 
01472 } TreeTestData;
01473 
01474 
01475 static void
01476 test_unregister_function (DBusConnection  *connection,
01477                           void            *user_data)
01478 {
01479   TreeTestData *ttd = user_data;
01480 
01481   ttd->handler_unregistered = TRUE;
01482 }
01483 
01484 static DBusHandlerResult
01485 test_message_function (DBusConnection  *connection,
01486                        DBusMessage     *message,
01487                        void            *user_data)
01488 {
01489   TreeTestData *ttd = user_data;
01490 
01491   ttd->message_handled = TRUE;
01492 
01493   return DBUS_HANDLER_RESULT_NOT_YET_HANDLED;
01494 }
01495 
01496 static dbus_bool_t
01497 do_register (DBusObjectTree *tree,
01498              const char    **path,
01499              dbus_bool_t     fallback,
01500              int             i,
01501              TreeTestData   *tree_test_data)
01502 {
01503   DBusObjectPathVTable vtable = { test_unregister_function,
01504                                   test_message_function, NULL };
01505 
01506   tree_test_data[i].message_handled = FALSE;
01507   tree_test_data[i].handler_unregistered = FALSE;
01508   tree_test_data[i].handler_fallback = fallback;
01509   tree_test_data[i].path = path;
01510 
01511   if (!_dbus_object_tree_register (tree, fallback, path,
01512                                    &vtable,
01513                                    &tree_test_data[i],
01514                                    NULL))
01515     return FALSE;
01516 
01517   _dbus_assert (_dbus_object_tree_get_user_data_unlocked (tree, path) ==
01518                 &tree_test_data[i]);
01519   
01520   return TRUE;
01521 }
01522 
01523 static dbus_bool_t
01524 do_test_dispatch (DBusObjectTree *tree,
01525                   const char    **path,
01526                   int             i,
01527                   TreeTestData   *tree_test_data,
01528                   int             n_test_data)
01529 {
01530   DBusMessage *message;
01531   int j;
01532   DBusHandlerResult result;
01533   char *flat;
01534 
01535   message = NULL;
01536   
01537   flat = flatten_path (path);
01538   if (flat == NULL)
01539     goto oom;
01540 
01541   message = dbus_message_new_method_call (NULL,
01542                                           flat,
01543                                           "org.freedesktop.TestInterface",
01544                                           "Foo");
01545   dbus_free (flat);
01546   if (message == NULL)
01547     goto oom;
01548 
01549   j = 0;
01550   while (j < n_test_data)
01551     {
01552       tree_test_data[j].message_handled = FALSE;
01553       ++j;
01554     }
01555 
01556   result = _dbus_object_tree_dispatch_and_unlock (tree, message, NULL);
01557   if (result == DBUS_HANDLER_RESULT_NEED_MEMORY)
01558     goto oom;
01559 
01560   _dbus_assert (tree_test_data[i].message_handled);
01561 
01562   j = 0;
01563   while (j < n_test_data)
01564     {
01565       if (tree_test_data[j].message_handled)
01566         {
01567           if (tree_test_data[j].handler_fallback)
01568             _dbus_assert (path_contains (tree_test_data[j].path,
01569                                          path) != STR_DIFFERENT);
01570           else
01571             _dbus_assert (path_contains (tree_test_data[j].path, path) == STR_EQUAL);
01572         }
01573       else
01574         {
01575           if (tree_test_data[j].handler_fallback)
01576             _dbus_assert (path_contains (tree_test_data[j].path,
01577                                          path) == STR_DIFFERENT);
01578           else
01579             _dbus_assert (path_contains (tree_test_data[j].path, path) != STR_EQUAL);
01580         }
01581 
01582       ++j;
01583     }
01584 
01585   dbus_message_unref (message);
01586 
01587   return TRUE;
01588 
01589  oom:
01590   if (message)
01591     dbus_message_unref (message);
01592   return FALSE;
01593 }
01594 
01595 static size_t
01596 string_array_length (const char **array)
01597 {
01598   size_t i;
01599   for (i = 0; array[i]; i++) ;
01600   return i;
01601 }
01602 
01603 typedef struct
01604 {
01605   const char *path;
01606   const char *result[20];
01607 } DecomposePathTest;
01608 
01609 static DecomposePathTest decompose_tests[] = {
01610   { "/foo", { "foo", NULL } },
01611   { "/foo/bar", { "foo", "bar", NULL } },
01612   { "/", { NULL } },
01613   { "/a/b", { "a", "b", NULL } },
01614   { "/a/b/c", { "a", "b", "c", NULL } },
01615   { "/a/b/c/d", { "a", "b", "c", "d", NULL } },
01616   { "/foo/bar/q", { "foo", "bar", "q", NULL } },
01617   { "/foo/bar/this/is/longer", { "foo", "bar", "this", "is", "longer", NULL } }
01618 };
01619 
01620 static dbus_bool_t
01621 run_decompose_tests (void)
01622 {
01623   int i;
01624 
01625   i = 0;
01626   while (i < _DBUS_N_ELEMENTS (decompose_tests))
01627     {
01628       char **result;
01629       int    result_len;
01630       int    expected_len;
01631 
01632       if (!_dbus_decompose_path (decompose_tests[i].path,
01633                                  strlen (decompose_tests[i].path),
01634                                  &result, &result_len))
01635         return FALSE;
01636 
01637       expected_len = string_array_length (decompose_tests[i].result);
01638       
01639       if (result_len != (int) string_array_length ((const char**)result) ||
01640           expected_len != result_len ||
01641           path_contains (decompose_tests[i].result,
01642                          (const char**) result) != STR_EQUAL)
01643         {
01644           int real_len = string_array_length ((const char**)result);
01645           _dbus_warn ("Expected decompose of %s to have len %d, returned %d, appears to have %d\n",
01646                       decompose_tests[i].path, expected_len, result_len,
01647                       real_len);
01648           _dbus_warn ("Decompose resulted in elements: { ");
01649           i = 0;
01650           while (i < real_len)
01651             {
01652               _dbus_warn ("\"%s\"%s", result[i],
01653                           (i + 1) == real_len ? "" : ", ");
01654               ++i;
01655             }
01656           _dbus_warn ("}\n");
01657           _dbus_assert_not_reached ("path decompose failed\n");
01658         }
01659 
01660       dbus_free_string_array (result);
01661 
01662       ++i;
01663     }
01664   
01665   return TRUE;
01666 }
01667 
01668 static DBusObjectSubtree*
01669 find_subtree_registered_or_unregistered (DBusObjectTree *tree,
01670                                          const char    **path)
01671 {
01672 #if VERBOSE_FIND
01673   _dbus_verbose ("Looking for exact subtree, registered or unregistered\n");
01674 #endif
01675 
01676   return find_subtree_recurse (tree->root, path, FALSE, NULL, NULL);
01677 }
01678 
01679 static dbus_bool_t
01680 object_tree_test_iteration (void *data)
01681 {
01682   const char *path0[] = { NULL };
01683   const char *path1[] = { "foo", NULL };
01684   const char *path2[] = { "foo", "bar", NULL };
01685   const char *path3[] = { "foo", "bar", "baz", NULL };
01686   const char *path4[] = { "foo", "bar", "boo", NULL };
01687   const char *path5[] = { "blah", NULL };
01688   const char *path6[] = { "blah", "boof", NULL };
01689   const char *path7[] = { "blah", "boof", "this", "is", "really", "long", NULL };
01690   const char *path8[] = { "childless", NULL };
01691   const char *path9[] = { "blah", "a", NULL };
01692   const char *path10[] = { "blah", "b", NULL };
01693   const char *path11[] = { "blah", "c", NULL };
01694   const char *path12[] = { "blah", "a", "d", NULL };
01695   const char *path13[] = { "blah", "b", "d", NULL };
01696   const char *path14[] = { "blah", "c", "d", NULL };
01697   DBusObjectPathVTable test_vtable = { NULL, test_message_function, NULL };
01698   DBusObjectTree *tree;
01699   TreeTestData tree_test_data[9];
01700   int i;
01701   dbus_bool_t exact_match;
01702 
01703   if (!run_decompose_tests ())
01704     return FALSE;
01705   
01706   tree = NULL;
01707 
01708   tree = _dbus_object_tree_new (NULL);
01709   if (tree == NULL)
01710     goto out;
01711 
01712   if (!do_register (tree, path0, TRUE, 0, tree_test_data))
01713     goto out;
01714 
01715   _dbus_assert (find_subtree (tree, path0, NULL));
01716   _dbus_assert (!find_subtree (tree, path1, NULL));
01717   _dbus_assert (!find_subtree (tree, path2, NULL));
01718   _dbus_assert (!find_subtree (tree, path3, NULL));
01719   _dbus_assert (!find_subtree (tree, path4, NULL));
01720   _dbus_assert (!find_subtree (tree, path5, NULL));
01721   _dbus_assert (!find_subtree (tree, path6, NULL));
01722   _dbus_assert (!find_subtree (tree, path7, NULL));
01723   _dbus_assert (!find_subtree (tree, path8, NULL));
01724 
01725   _dbus_assert (find_handler (tree, path0, &exact_match) && exact_match);
01726   _dbus_assert (find_handler (tree, path1, &exact_match) == tree->root && !exact_match);
01727   _dbus_assert (find_handler (tree, path2, &exact_match) == tree->root && !exact_match);
01728   _dbus_assert (find_handler (tree, path3, &exact_match) == tree->root && !exact_match);
01729   _dbus_assert (find_handler (tree, path4, &exact_match) == tree->root && !exact_match);
01730   _dbus_assert (find_handler (tree, path5, &exact_match) == tree->root && !exact_match);
01731   _dbus_assert (find_handler (tree, path6, &exact_match) == tree->root && !exact_match);
01732   _dbus_assert (find_handler (tree, path7, &exact_match) == tree->root && !exact_match);
01733   _dbus_assert (find_handler (tree, path8, &exact_match) == tree->root && !exact_match);
01734   
01735   if (!do_register (tree, path1, TRUE, 1, tree_test_data))
01736     goto out;
01737 
01738   _dbus_assert (find_subtree (tree, path0, NULL));
01739   _dbus_assert (find_subtree (tree, path1, NULL));
01740   _dbus_assert (!find_subtree (tree, path2, NULL));
01741   _dbus_assert (!find_subtree (tree, path3, NULL));
01742   _dbus_assert (!find_subtree (tree, path4, NULL));
01743   _dbus_assert (!find_subtree (tree, path5, NULL));
01744   _dbus_assert (!find_subtree (tree, path6, NULL));
01745   _dbus_assert (!find_subtree (tree, path7, NULL));
01746   _dbus_assert (!find_subtree (tree, path8, NULL));
01747 
01748   _dbus_assert (find_handler (tree, path0, &exact_match) &&  exact_match);
01749   _dbus_assert (find_handler (tree, path1, &exact_match) &&  exact_match);
01750   _dbus_assert (find_handler (tree, path2, &exact_match) && !exact_match);
01751   _dbus_assert (find_handler (tree, path3, &exact_match) && !exact_match);
01752   _dbus_assert (find_handler (tree, path4, &exact_match) && !exact_match);
01753   _dbus_assert (find_handler (tree, path5, &exact_match) == tree->root && !exact_match);
01754   _dbus_assert (find_handler (tree, path6, &exact_match) == tree->root && !exact_match);
01755   _dbus_assert (find_handler (tree, path7, &exact_match) == tree->root && !exact_match);
01756   _dbus_assert (find_handler (tree, path8, &exact_match) == tree->root && !exact_match);
01757 
01758   if (!do_register (tree, path2, TRUE, 2, tree_test_data))
01759     goto out;
01760 
01761   _dbus_assert (find_subtree (tree, path1, NULL));
01762   _dbus_assert (find_subtree (tree, path2, NULL));
01763   _dbus_assert (!find_subtree (tree, path3, NULL));
01764   _dbus_assert (!find_subtree (tree, path4, NULL));
01765   _dbus_assert (!find_subtree (tree, path5, NULL));
01766   _dbus_assert (!find_subtree (tree, path6, NULL));
01767   _dbus_assert (!find_subtree (tree, path7, NULL));
01768   _dbus_assert (!find_subtree (tree, path8, NULL));
01769 
01770   if (!do_register (tree, path3, TRUE, 3, tree_test_data))
01771     goto out;
01772 
01773   _dbus_assert (find_subtree (tree, path0, NULL));
01774   _dbus_assert (find_subtree (tree, path1, NULL));
01775   _dbus_assert (find_subtree (tree, path2, NULL));
01776   _dbus_assert (find_subtree (tree, path3, NULL));
01777   _dbus_assert (!find_subtree (tree, path4, NULL));
01778   _dbus_assert (!find_subtree (tree, path5, NULL));
01779   _dbus_assert (!find_subtree (tree, path6, NULL));
01780   _dbus_assert (!find_subtree (tree, path7, NULL));
01781   _dbus_assert (!find_subtree (tree, path8, NULL));
01782   
01783   if (!do_register (tree, path4, TRUE, 4, tree_test_data))
01784     goto out;
01785 
01786   _dbus_assert (find_subtree (tree, path0, NULL));
01787   _dbus_assert (find_subtree (tree, path1, NULL));
01788   _dbus_assert (find_subtree (tree, path2, NULL));
01789   _dbus_assert (find_subtree (tree, path3, NULL));  
01790   _dbus_assert (find_subtree (tree, path4, NULL));
01791   _dbus_assert (!find_subtree (tree, path5, NULL));
01792   _dbus_assert (!find_subtree (tree, path6, NULL));
01793   _dbus_assert (!find_subtree (tree, path7, NULL));
01794   _dbus_assert (!find_subtree (tree, path8, NULL));
01795   
01796   if (!do_register (tree, path5, TRUE, 5, tree_test_data))
01797     goto out;
01798 
01799   _dbus_assert (find_subtree (tree, path0, NULL));
01800   _dbus_assert (find_subtree (tree, path1, NULL));
01801   _dbus_assert (find_subtree (tree, path2, NULL));
01802   _dbus_assert (find_subtree (tree, path3, NULL));
01803   _dbus_assert (find_subtree (tree, path4, NULL));
01804   _dbus_assert (find_subtree (tree, path5, NULL));
01805   _dbus_assert (!find_subtree (tree, path6, NULL));
01806   _dbus_assert (!find_subtree (tree, path7, NULL));
01807   _dbus_assert (!find_subtree (tree, path8, NULL));
01808 
01809   _dbus_assert (find_handler (tree, path0, &exact_match) == tree->root &&  exact_match);
01810   _dbus_assert (find_handler (tree, path1, &exact_match) != tree->root &&  exact_match);
01811   _dbus_assert (find_handler (tree, path2, &exact_match) != tree->root &&  exact_match);
01812   _dbus_assert (find_handler (tree, path3, &exact_match) != tree->root &&  exact_match);
01813   _dbus_assert (find_handler (tree, path4, &exact_match) != tree->root &&  exact_match);
01814   _dbus_assert (find_handler (tree, path5, &exact_match) != tree->root &&  exact_match);
01815   _dbus_assert (find_handler (tree, path6, &exact_match) != tree->root && !exact_match);
01816   _dbus_assert (find_handler (tree, path7, &exact_match) != tree->root && !exact_match);
01817   _dbus_assert (find_handler (tree, path8, &exact_match) == tree->root && !exact_match);
01818 
01819   if (!do_register (tree, path6, TRUE, 6, tree_test_data))
01820     goto out;
01821 
01822   _dbus_assert (find_subtree (tree, path0, NULL));
01823   _dbus_assert (find_subtree (tree, path1, NULL));
01824   _dbus_assert (find_subtree (tree, path2, NULL));
01825   _dbus_assert (find_subtree (tree, path3, NULL));
01826   _dbus_assert (find_subtree (tree, path4, NULL));
01827   _dbus_assert (find_subtree (tree, path5, NULL));
01828   _dbus_assert (find_subtree (tree, path6, NULL));
01829   _dbus_assert (!find_subtree (tree, path7, NULL));
01830   _dbus_assert (!find_subtree (tree, path8, NULL));
01831 
01832   if (!do_register (tree, path7, TRUE, 7, tree_test_data))
01833     goto out;
01834 
01835   _dbus_assert (find_subtree (tree, path0, NULL));
01836   _dbus_assert (find_subtree (tree, path1, NULL));
01837   _dbus_assert (find_subtree (tree, path2, NULL));
01838   _dbus_assert (find_subtree (tree, path3, NULL));
01839   _dbus_assert (find_subtree (tree, path4, NULL));
01840   _dbus_assert (find_subtree (tree, path5, NULL));
01841   _dbus_assert (find_subtree (tree, path6, NULL));
01842   _dbus_assert (find_subtree (tree, path7, NULL));
01843   _dbus_assert (!find_subtree (tree, path8, NULL));
01844 
01845   if (!do_register (tree, path8, TRUE, 8, tree_test_data))
01846     goto out;
01847 
01848   _dbus_assert (find_subtree (tree, path0, NULL));
01849   _dbus_assert (find_subtree (tree, path1, NULL));
01850   _dbus_assert (find_subtree (tree, path2, NULL));
01851   _dbus_assert (find_subtree (tree, path3, NULL));
01852   _dbus_assert (find_subtree (tree, path4, NULL));
01853   _dbus_assert (find_subtree (tree, path5, NULL));
01854   _dbus_assert (find_subtree (tree, path6, NULL));
01855   _dbus_assert (find_subtree (tree, path7, NULL));
01856   _dbus_assert (find_subtree (tree, path8, NULL));
01857 
01858   _dbus_assert (find_handler (tree, path0, &exact_match) == tree->root &&  exact_match);
01859   _dbus_assert (find_handler (tree, path1, &exact_match) != tree->root && exact_match);
01860   _dbus_assert (find_handler (tree, path2, &exact_match) != tree->root && exact_match);
01861   _dbus_assert (find_handler (tree, path3, &exact_match) != tree->root && exact_match);
01862   _dbus_assert (find_handler (tree, path4, &exact_match) != tree->root && exact_match);
01863   _dbus_assert (find_handler (tree, path5, &exact_match) != tree->root && exact_match);
01864   _dbus_assert (find_handler (tree, path6, &exact_match) != tree->root && exact_match);
01865   _dbus_assert (find_handler (tree, path7, &exact_match) != tree->root && exact_match);
01866   _dbus_assert (find_handler (tree, path8, &exact_match) != tree->root && exact_match);
01867   
01868   /* test the list_registered function */
01869 
01870   {
01871     const char *root[] = { NULL };
01872     char **child_entries;
01873     int nb;
01874 
01875     _dbus_object_tree_list_registered_unlocked (tree, path1, &child_entries);
01876     if (child_entries != NULL)
01877       {
01878         nb = string_array_length ((const char**)child_entries);
01879         _dbus_assert (nb == 1);
01880         dbus_free_string_array (child_entries);
01881       }
01882 
01883     _dbus_object_tree_list_registered_unlocked (tree, path2, &child_entries);
01884     if (child_entries != NULL)
01885       {
01886         nb = string_array_length ((const char**)child_entries);
01887         _dbus_assert (nb == 2);
01888         dbus_free_string_array (child_entries);
01889       }
01890 
01891     _dbus_object_tree_list_registered_unlocked (tree, path8, &child_entries);
01892     if (child_entries != NULL)
01893       {
01894         nb = string_array_length ((const char**)child_entries);
01895         _dbus_assert (nb == 0);
01896         dbus_free_string_array (child_entries);
01897       }
01898 
01899     _dbus_object_tree_list_registered_unlocked (tree, root, &child_entries);
01900     if (child_entries != NULL)
01901       {
01902         nb = string_array_length ((const char**)child_entries);
01903         _dbus_assert (nb == 3);
01904         dbus_free_string_array (child_entries);
01905       }
01906   }
01907 
01908   /* Check that destroying tree calls unregister funcs */
01909   _dbus_object_tree_unref (tree);
01910 
01911   i = 0;
01912   while (i < (int) _DBUS_N_ELEMENTS (tree_test_data))
01913     {
01914       _dbus_assert (tree_test_data[i].handler_unregistered);
01915       _dbus_assert (!tree_test_data[i].message_handled);
01916       ++i;
01917     }
01918 
01919   /* Now start again and try the individual unregister function */
01920   tree = _dbus_object_tree_new (NULL);
01921   if (tree == NULL)
01922     goto out;
01923 
01924   if (!do_register (tree, path0, TRUE, 0, tree_test_data))
01925     goto out;
01926   if (!do_register (tree, path1, TRUE, 1, tree_test_data))
01927     goto out;
01928   if (!do_register (tree, path2, TRUE, 2, tree_test_data))
01929     goto out;
01930   if (!do_register (tree, path3, TRUE, 3, tree_test_data))
01931     goto out;
01932   if (!do_register (tree, path4, TRUE, 4, tree_test_data))
01933     goto out;
01934   if (!do_register (tree, path5, TRUE, 5, tree_test_data))
01935     goto out;
01936   if (!do_register (tree, path6, TRUE, 6, tree_test_data))
01937     goto out;
01938   if (!do_register (tree, path7, TRUE, 7, tree_test_data))
01939     goto out;
01940   if (!do_register (tree, path8, TRUE, 8, tree_test_data))
01941     goto out;
01942 
01943   _dbus_object_tree_unregister_and_unlock (tree, path0);
01944   _dbus_assert (_dbus_object_tree_get_user_data_unlocked (tree, path0) == NULL);
01945 
01946   _dbus_assert (!find_subtree (tree, path0, NULL));
01947   _dbus_assert (find_subtree (tree, path1, NULL));
01948   _dbus_assert (find_subtree (tree, path2, NULL));
01949   _dbus_assert (find_subtree (tree, path3, NULL));
01950   _dbus_assert (find_subtree (tree, path4, NULL));
01951   _dbus_assert (find_subtree (tree, path5, NULL));
01952   _dbus_assert (find_subtree (tree, path6, NULL));
01953   _dbus_assert (find_subtree (tree, path7, NULL));
01954   _dbus_assert (find_subtree (tree, path8, NULL));
01955   
01956   _dbus_object_tree_unregister_and_unlock (tree, path1);
01957   _dbus_assert (_dbus_object_tree_get_user_data_unlocked (tree, path1) == NULL);
01958 
01959   _dbus_assert (!find_subtree (tree, path0, NULL));
01960   _dbus_assert (!find_subtree (tree, path1, NULL));
01961   _dbus_assert (find_subtree (tree, path2, NULL));
01962   _dbus_assert (find_subtree (tree, path3, NULL));
01963   _dbus_assert (find_subtree (tree, path4, NULL));
01964   _dbus_assert (find_subtree (tree, path5, NULL));
01965   _dbus_assert (find_subtree (tree, path6, NULL));
01966   _dbus_assert (find_subtree (tree, path7, NULL));
01967   _dbus_assert (find_subtree (tree, path8, NULL));
01968 
01969   _dbus_object_tree_unregister_and_unlock (tree, path2);
01970   _dbus_assert (_dbus_object_tree_get_user_data_unlocked (tree, path2) == NULL);
01971 
01972   _dbus_assert (!find_subtree (tree, path0, NULL));
01973   _dbus_assert (!find_subtree (tree, path1, NULL));
01974   _dbus_assert (!find_subtree (tree, path2, NULL));
01975   _dbus_assert (find_subtree (tree, path3, NULL));
01976   _dbus_assert (find_subtree (tree, path4, NULL));
01977   _dbus_assert (find_subtree (tree, path5, NULL));
01978   _dbus_assert (find_subtree (tree, path6, NULL));
01979   _dbus_assert (find_subtree (tree, path7, NULL));
01980   _dbus_assert (find_subtree (tree, path8, NULL));
01981   
01982   _dbus_object_tree_unregister_and_unlock (tree, path3);
01983   _dbus_assert (_dbus_object_tree_get_user_data_unlocked (tree, path3) == NULL);
01984 
01985   _dbus_assert (!find_subtree (tree, path0, NULL));
01986   _dbus_assert (!find_subtree (tree, path1, NULL));
01987   _dbus_assert (!find_subtree (tree, path2, NULL));
01988   _dbus_assert (!find_subtree (tree, path3, NULL));
01989   _dbus_assert (find_subtree (tree, path4, NULL));
01990   _dbus_assert (find_subtree (tree, path5, NULL));
01991   _dbus_assert (find_subtree (tree, path6, NULL));
01992   _dbus_assert (find_subtree (tree, path7, NULL));
01993   _dbus_assert (find_subtree (tree, path8, NULL));
01994   
01995   _dbus_object_tree_unregister_and_unlock (tree, path4);
01996   _dbus_assert (_dbus_object_tree_get_user_data_unlocked (tree, path4) == NULL);
01997 
01998   _dbus_assert (!find_subtree (tree, path0, NULL));
01999   _dbus_assert (!find_subtree (tree, path1, NULL));
02000   _dbus_assert (!find_subtree (tree, path2, NULL));
02001   _dbus_assert (!find_subtree (tree, path3, NULL));
02002   _dbus_assert (!find_subtree (tree, path4, NULL));
02003   _dbus_assert (find_subtree (tree, path5, NULL));
02004   _dbus_assert (find_subtree (tree, path6, NULL));
02005   _dbus_assert (find_subtree (tree, path7, NULL));
02006   _dbus_assert (find_subtree (tree, path8, NULL));
02007   
02008   _dbus_object_tree_unregister_and_unlock (tree, path5);
02009   _dbus_assert (_dbus_object_tree_get_user_data_unlocked (tree, path5) == NULL);
02010 
02011   _dbus_assert (!find_subtree (tree, path0, NULL));
02012   _dbus_assert (!find_subtree (tree, path1, NULL));
02013   _dbus_assert (!find_subtree (tree, path2, NULL));
02014   _dbus_assert (!find_subtree (tree, path3, NULL));
02015   _dbus_assert (!find_subtree (tree, path4, NULL));
02016   _dbus_assert (!find_subtree (tree, path5, NULL));
02017   _dbus_assert (find_subtree (tree, path6, NULL));
02018   _dbus_assert (find_subtree (tree, path7, NULL));
02019   _dbus_assert (find_subtree (tree, path8, NULL));
02020   
02021   _dbus_object_tree_unregister_and_unlock (tree, path6);
02022   _dbus_assert (_dbus_object_tree_get_user_data_unlocked (tree, path6) == NULL);
02023 
02024   _dbus_assert (!find_subtree (tree, path0, NULL));
02025   _dbus_assert (!find_subtree (tree, path1, NULL));
02026   _dbus_assert (!find_subtree (tree, path2, NULL));
02027   _dbus_assert (!find_subtree (tree, path3, NULL));
02028   _dbus_assert (!find_subtree (tree, path4, NULL));
02029   _dbus_assert (!find_subtree (tree, path5, NULL));
02030   _dbus_assert (!find_subtree (tree, path6, NULL));
02031   _dbus_assert (find_subtree (tree, path7, NULL));
02032   _dbus_assert (find_subtree (tree, path8, NULL));
02033 
02034   _dbus_object_tree_unregister_and_unlock (tree, path7);
02035   _dbus_assert (_dbus_object_tree_get_user_data_unlocked (tree, path7) == NULL);
02036 
02037   _dbus_assert (!find_subtree (tree, path0, NULL));
02038   _dbus_assert (!find_subtree (tree, path1, NULL));
02039   _dbus_assert (!find_subtree (tree, path2, NULL));
02040   _dbus_assert (!find_subtree (tree, path3, NULL));
02041   _dbus_assert (!find_subtree (tree, path4, NULL));
02042   _dbus_assert (!find_subtree (tree, path5, NULL));
02043   _dbus_assert (!find_subtree (tree, path6, NULL));
02044   _dbus_assert (!find_subtree (tree, path7, NULL));
02045   _dbus_assert (find_subtree (tree, path8, NULL));
02046 
02047   _dbus_object_tree_unregister_and_unlock (tree, path8);
02048   _dbus_assert (_dbus_object_tree_get_user_data_unlocked (tree, path8) == NULL);
02049 
02050   _dbus_assert (!find_subtree (tree, path0, NULL));
02051   _dbus_assert (!find_subtree (tree, path1, NULL));
02052   _dbus_assert (!find_subtree (tree, path2, NULL));
02053   _dbus_assert (!find_subtree (tree, path3, NULL));
02054   _dbus_assert (!find_subtree (tree, path4, NULL));
02055   _dbus_assert (!find_subtree (tree, path5, NULL));
02056   _dbus_assert (!find_subtree (tree, path6, NULL));
02057   _dbus_assert (!find_subtree (tree, path7, NULL));
02058   _dbus_assert (!find_subtree (tree, path8, NULL));
02059   
02060   i = 0;
02061   while (i < (int) _DBUS_N_ELEMENTS (tree_test_data))
02062     {
02063       _dbus_assert (tree_test_data[i].handler_unregistered);
02064       _dbus_assert (!tree_test_data[i].message_handled);
02065       ++i;
02066     }
02067 
02068   /* Test removal of newly-childless unregistered nodes */
02069   if (!do_register (tree, path2, TRUE, 2, tree_test_data))
02070     goto out;
02071 
02072   _dbus_object_tree_unregister_and_unlock (tree, path2);
02073   _dbus_assert (!find_subtree_registered_or_unregistered (tree, path2));
02074   _dbus_assert (!find_subtree_registered_or_unregistered (tree, path1));
02075   _dbus_assert (find_subtree_registered_or_unregistered (tree, path0));
02076 
02077   /* Test that unregistered parents cannot be freed out from under their
02078      children */
02079   if (!do_register (tree, path2, TRUE, 2, tree_test_data))
02080     goto out;
02081 
02082   _dbus_assert (!find_subtree (tree, path1, NULL));
02083   _dbus_assert (find_subtree_registered_or_unregistered (tree, path1));
02084   _dbus_assert (find_subtree_registered_or_unregistered (tree, path0));
02085 
02086 #if 0
02087   /* This triggers the "Attempted to unregister path ..." warning message */
02088   _dbus_object_tree_unregister_and_unlock (tree, path1);
02089 #endif
02090   _dbus_assert (find_subtree (tree, path2, NULL));
02091   _dbus_assert (!find_subtree (tree, path1, NULL));
02092   _dbus_assert (find_subtree_registered_or_unregistered (tree, path1));
02093   _dbus_assert (find_subtree_registered_or_unregistered (tree, path0));
02094 
02095   _dbus_object_tree_unregister_and_unlock (tree, path2);
02096   _dbus_assert (!find_subtree (tree, path2, NULL));
02097   _dbus_assert (!find_subtree_registered_or_unregistered (tree, path2));
02098   _dbus_assert (!find_subtree_registered_or_unregistered (tree, path1));
02099   _dbus_assert (find_subtree_registered_or_unregistered (tree, path0));
02100 
02101   /* Test that registered parents cannot be freed out from under their
02102      children, and that if they are unregistered before their children, they
02103      are still freed when their children are unregistered */
02104   if (!do_register (tree, path1, TRUE, 1, tree_test_data))
02105     goto out;
02106   if (!do_register (tree, path2, TRUE, 2, tree_test_data))
02107     goto out;
02108 
02109   _dbus_assert (find_subtree (tree, path1, NULL));
02110   _dbus_assert (find_subtree (tree, path2, NULL));
02111 
02112   _dbus_object_tree_unregister_and_unlock (tree, path1);
02113   _dbus_assert (!find_subtree (tree, path1, NULL));
02114   _dbus_assert (find_subtree (tree, path2, NULL));
02115   _dbus_assert (find_subtree_registered_or_unregistered (tree, path1));
02116   _dbus_assert (find_subtree_registered_or_unregistered (tree, path0));
02117 
02118   _dbus_object_tree_unregister_and_unlock (tree, path2);
02119   _dbus_assert (!find_subtree (tree, path1, NULL));
02120   _dbus_assert (!find_subtree_registered_or_unregistered (tree, path1));
02121   _dbus_assert (!find_subtree (tree, path2, NULL));
02122   _dbus_assert (!find_subtree_registered_or_unregistered (tree, path2));
02123   _dbus_assert (find_subtree_registered_or_unregistered (tree, path0));
02124 
02125   /* Test with NULL unregister_function and user_data */
02126   if (!_dbus_object_tree_register (tree, TRUE, path2,
02127                                    &test_vtable,
02128                                    NULL,
02129                                    NULL))
02130     goto out;
02131 
02132   _dbus_assert (_dbus_object_tree_get_user_data_unlocked (tree, path2) == NULL);
02133   _dbus_object_tree_unregister_and_unlock (tree, path2);
02134   _dbus_assert (!find_subtree (tree, path2, NULL));
02135   _dbus_assert (!find_subtree_registered_or_unregistered (tree, path2));
02136   _dbus_assert (!find_subtree_registered_or_unregistered (tree, path1));
02137   _dbus_assert (find_subtree_registered_or_unregistered (tree, path0));
02138 
02139   /* Test freeing a long path */
02140   if (!do_register (tree, path3, TRUE, 3, tree_test_data))
02141     goto out;
02142 
02143   _dbus_object_tree_unregister_and_unlock (tree, path3);
02144   _dbus_assert (!find_subtree (tree, path3, NULL));
02145   _dbus_assert (!find_subtree_registered_or_unregistered (tree, path3));
02146   _dbus_assert (!find_subtree_registered_or_unregistered (tree, path2));
02147   _dbus_assert (!find_subtree_registered_or_unregistered (tree, path1));
02148   _dbus_assert (find_subtree_registered_or_unregistered (tree, path0));
02149 
02150   /* Test freeing multiple children from the same path */
02151   if (!do_register (tree, path3, TRUE, 3, tree_test_data))
02152     goto out;
02153   if (!do_register (tree, path4, TRUE, 4, tree_test_data))
02154     goto out;
02155 
02156   _dbus_assert (find_subtree (tree, path3, NULL));
02157   _dbus_assert (find_subtree (tree, path4, NULL));
02158 
02159   _dbus_object_tree_unregister_and_unlock (tree, path3);
02160   _dbus_assert (!find_subtree (tree, path3, NULL));
02161   _dbus_assert (!find_subtree_registered_or_unregistered (tree, path3));
02162   _dbus_assert (find_subtree (tree, path4, NULL));
02163   _dbus_assert (find_subtree_registered_or_unregistered (tree, path4));
02164   _dbus_assert (find_subtree_registered_or_unregistered (tree, path2));
02165   _dbus_assert (find_subtree_registered_or_unregistered (tree, path1));
02166 
02167   _dbus_object_tree_unregister_and_unlock (tree, path4);
02168   _dbus_assert (!find_subtree (tree, path4, NULL));
02169   _dbus_assert (!find_subtree_registered_or_unregistered (tree, path4));
02170   _dbus_assert (!find_subtree (tree, path3, NULL));
02171   _dbus_assert (!find_subtree_registered_or_unregistered (tree, path3));
02172   _dbus_assert (!find_subtree_registered_or_unregistered (tree, path2));
02173   _dbus_assert (!find_subtree_registered_or_unregistered (tree, path1));
02174 
02175   /* Test subtree removal */
02176   if (!_dbus_object_tree_register (tree, TRUE, path12,
02177                                    &test_vtable,
02178                                    NULL,
02179                                    NULL))
02180     goto out;
02181 
02182   _dbus_assert (find_subtree (tree, path12, NULL));
02183 
02184   if (!_dbus_object_tree_register (tree, TRUE, path13,
02185                                    &test_vtable,
02186                                    NULL,
02187                                    NULL))
02188     goto out;
02189 
02190   _dbus_assert (find_subtree (tree, path13, NULL));
02191 
02192   if (!_dbus_object_tree_register (tree, TRUE, path14,
02193                                    &test_vtable,
02194                                    NULL,
02195                                    NULL))
02196     goto out;
02197 
02198   _dbus_assert (find_subtree (tree, path14, NULL));
02199 
02200   _dbus_object_tree_unregister_and_unlock (tree, path12);
02201 
02202   _dbus_assert (!find_subtree_registered_or_unregistered (tree, path12));
02203   _dbus_assert (find_subtree (tree, path13, NULL));
02204   _dbus_assert (find_subtree (tree, path14, NULL));
02205   _dbus_assert (!find_subtree_registered_or_unregistered (tree, path9));
02206   _dbus_assert (find_subtree_registered_or_unregistered (tree, path5));
02207 
02208   if (!_dbus_object_tree_register (tree, TRUE, path12,
02209                                    &test_vtable,
02210                                    NULL,
02211                                    NULL))
02212     goto out;
02213 
02214   _dbus_assert (find_subtree (tree, path12, NULL));
02215 
02216   _dbus_object_tree_unregister_and_unlock (tree, path13);
02217 
02218   _dbus_assert (find_subtree (tree, path12, NULL));
02219   _dbus_assert (!find_subtree_registered_or_unregistered (tree, path13));
02220   _dbus_assert (find_subtree (tree, path14, NULL));
02221   _dbus_assert (!find_subtree_registered_or_unregistered (tree, path10));
02222   _dbus_assert (find_subtree_registered_or_unregistered (tree, path5));
02223 
02224   if (!_dbus_object_tree_register (tree, TRUE, path13,
02225                                    &test_vtable,
02226                                    NULL,
02227                                    NULL))
02228     goto out;
02229 
02230   _dbus_assert (find_subtree (tree, path13, NULL));
02231 
02232   _dbus_object_tree_unregister_and_unlock (tree, path14);
02233 
02234   _dbus_assert (find_subtree (tree, path12, NULL));
02235   _dbus_assert (find_subtree (tree, path13, NULL));
02236   _dbus_assert (!find_subtree_registered_or_unregistered (tree, path14));
02237   _dbus_assert (!find_subtree_registered_or_unregistered (tree, path11));
02238   _dbus_assert (find_subtree_registered_or_unregistered (tree, path5));
02239 
02240   _dbus_object_tree_unregister_and_unlock (tree, path12);
02241 
02242   _dbus_assert (!find_subtree_registered_or_unregistered (tree, path12));
02243   _dbus_assert (!find_subtree_registered_or_unregistered (tree, path9));
02244   _dbus_assert (find_subtree_registered_or_unregistered (tree, path5));
02245 
02246   _dbus_object_tree_unregister_and_unlock (tree, path13);
02247 
02248   _dbus_assert (!find_subtree_registered_or_unregistered (tree, path13));
02249   _dbus_assert (!find_subtree_registered_or_unregistered (tree, path10));
02250   _dbus_assert (!find_subtree_registered_or_unregistered (tree, path5));
02251 
02252 #if 0
02253   /* Test attempting to unregister non-existent paths.  These trigger
02254      "Attempted to unregister path ..." warning messages */
02255   _dbus_object_tree_unregister_and_unlock (tree, path0);
02256   _dbus_object_tree_unregister_and_unlock (tree, path1);
02257   _dbus_object_tree_unregister_and_unlock (tree, path2);
02258   _dbus_object_tree_unregister_and_unlock (tree, path3);
02259   _dbus_object_tree_unregister_and_unlock (tree, path4);
02260 #endif
02261 
02262   /* Register it all again, and test dispatch */
02263   
02264   if (!do_register (tree, path0, TRUE, 0, tree_test_data))
02265     goto out;
02266   if (!do_register (tree, path1, FALSE, 1, tree_test_data))
02267     goto out;
02268   if (!do_register (tree, path2, TRUE, 2, tree_test_data))
02269     goto out;
02270   if (!do_register (tree, path3, TRUE, 3, tree_test_data))
02271     goto out;
02272   if (!do_register (tree, path4, TRUE, 4, tree_test_data))
02273     goto out;
02274   if (!do_register (tree, path5, TRUE, 5, tree_test_data))
02275     goto out;
02276   if (!do_register (tree, path6, FALSE, 6, tree_test_data))
02277     goto out;
02278   if (!do_register (tree, path7, TRUE, 7, tree_test_data))
02279     goto out;
02280   if (!do_register (tree, path8, TRUE, 8, tree_test_data))
02281     goto out;
02282 
02283 #if 0
02284   spew_tree (tree);
02285 #endif
02286 
02287   if (!do_test_dispatch (tree, path0, 0, tree_test_data, _DBUS_N_ELEMENTS (tree_test_data)))
02288     goto out;
02289   if (!do_test_dispatch (tree, path1, 1, tree_test_data, _DBUS_N_ELEMENTS (tree_test_data)))
02290     goto out;
02291   if (!do_test_dispatch (tree, path2, 2, tree_test_data, _DBUS_N_ELEMENTS (tree_test_data)))
02292     goto out;
02293   if (!do_test_dispatch (tree, path3, 3, tree_test_data, _DBUS_N_ELEMENTS (tree_test_data)))
02294     goto out;
02295   if (!do_test_dispatch (tree, path4, 4, tree_test_data, _DBUS_N_ELEMENTS (tree_test_data)))
02296     goto out;
02297   if (!do_test_dispatch (tree, path5, 5, tree_test_data, _DBUS_N_ELEMENTS (tree_test_data)))
02298     goto out;
02299   if (!do_test_dispatch (tree, path6, 6, tree_test_data, _DBUS_N_ELEMENTS (tree_test_data)))
02300     goto out;
02301   if (!do_test_dispatch (tree, path7, 7, tree_test_data, _DBUS_N_ELEMENTS (tree_test_data)))
02302     goto out;
02303   if (!do_test_dispatch (tree, path8, 8, tree_test_data, _DBUS_N_ELEMENTS (tree_test_data)))
02304     goto out;
02305   
02306  out:
02307   if (tree)
02308     {
02309       /* test ref */
02310       _dbus_object_tree_ref (tree);
02311       _dbus_object_tree_unref (tree);
02312       _dbus_object_tree_unref (tree);
02313     }
02314 
02315   return TRUE;
02316 }
02317 
02323 dbus_bool_t
02324 _dbus_object_tree_test (void)
02325 {
02326   _dbus_test_oom_handling ("object tree",
02327                            object_tree_test_iteration,
02328                            NULL);
02329 
02330   return TRUE;
02331 }
02332 
02333 #endif /* !DOXYGEN_SHOULD_SKIP_THIS */
02334 
02335 #endif /* DBUS_ENABLE_EMBEDDED_TESTS */