* dbus/dbus-auth.c (client_try_next_mechanism): Remove logic to
[platform/upstream/dbus.git] / dbus / dbus-object-tree.c
1 /* -*- mode: C; c-file-style: "gnu" -*- */
2 /* dbus-object-tree.c  DBusObjectTree (internals of DBusConnection)
3  *
4  * Copyright (C) 2003  Red Hat Inc.
5  *
6  * Licensed under the Academic Free License version 2.0
7  *
8  * This program is free software; you can redistribute it and/or modify
9  * it under the terms of the GNU General Public License as published by
10  * the Free Software Foundation; either version 2 of the License, or
11  * (at your option) any later version.
12  *
13  * This program is distributed in the hope that it will be useful,
14  * but WITHOUT ANY WARRANTY; without even the implied warranty of
15  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
16  * GNU General Public License for more details.
17  *
18  * You should have received a copy of the GNU General Public License
19  * along with this program; if not, write to the Free Software
20  * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
21  *
22  */
23 #include "dbus-object-tree.h"
24 #include "dbus-connection-internal.h"
25 #include "dbus-internals.h"
26 #include "dbus-hash.h"
27 #include "dbus-protocol.h"
28 #include "dbus-string.h"
29 #include <string.h>
30 #include <stdlib.h>
31
32 /**
33  * @defgroup DBusObjectTree A hierarchy of objects with container-contained relationship
34  * @ingroup  DBusInternals
35  * @brief DBusObjectTree is used by DBusConnection to track the object tree
36  *
37  * Types and functions related to DBusObjectTree. These
38  * are all library-internal.
39  *
40  * @{
41  */
42
43 /** Subnode of the object hierarchy */
44 typedef struct DBusObjectSubtree DBusObjectSubtree;
45
46 static DBusObjectSubtree* _dbus_object_subtree_new   (const char                  *name,
47                                                       const DBusObjectPathVTable  *vtable,
48                                                       void                        *user_data);
49 static DBusObjectSubtree* _dbus_object_subtree_ref   (DBusObjectSubtree           *subtree);
50 static void               _dbus_object_subtree_unref (DBusObjectSubtree           *subtree);
51
52 /**
53  * Internals of DBusObjectTree
54  */
55 struct DBusObjectTree
56 {
57   int                 refcount;   /**< Reference count */
58   DBusConnection     *connection; /**< Connection this tree belongs to */
59
60   DBusObjectSubtree  *root;       /**< Root of the tree ("/" node) */
61 };
62
63 /**
64  * Struct representing a single registered subtree handler, or node
65  * that's a parent of a registered subtree handler. If
66  * message_function != NULL there's actually a handler at this node.
67  */
68 struct DBusObjectSubtree
69 {
70   DBusAtomic                         refcount;            /**< Reference count */
71   DBusObjectSubtree                 *parent;              /**< Parent node */
72   DBusObjectPathUnregisterFunction   unregister_function; /**< Function to call on unregister */
73   DBusObjectPathMessageFunction      message_function;    /**< Function to handle messages */
74   void                              *user_data;           /**< Data for functions */
75   DBusObjectSubtree                **subtrees;            /**< Child nodes */
76   int                                n_subtrees;          /**< Number of child nodes */
77   unsigned int                       subtrees_sorted : 1; /**< Whether children are sorted */
78   unsigned int                       invoke_as_fallback : 1; /**< Whether to invoke message_function when child nodes don't handle the message */
79   char                               name[1]; /**< Allocated as large as necessary */
80 };
81
82 /**
83  * Creates a new object tree, representing a mapping from paths
84  * to handler vtables.
85  *
86  * @param connection the connection this tree belongs to
87  * @returns the new tree or #NULL if no memory
88  */
89 DBusObjectTree*
90 _dbus_object_tree_new (DBusConnection *connection)
91 {
92   DBusObjectTree *tree;
93
94   /* the connection passed in here isn't fully constructed,
95    * so don't do anything more than store a pointer to
96    * it
97    */
98
99   tree = dbus_new0 (DBusObjectTree, 1);
100   if (tree == NULL)
101     goto oom;
102
103   tree->refcount = 1;
104   tree->connection = connection;
105   tree->root = _dbus_object_subtree_new ("/", NULL, NULL);
106   if (tree->root == NULL)
107     goto oom;
108   tree->root->invoke_as_fallback = TRUE;
109   
110   return tree;
111
112  oom:
113   if (tree)
114     {
115       dbus_free (tree);
116     }
117
118   return NULL;
119 }
120
121 /**
122  * Increment the reference count
123  * @param tree the object tree
124  * @returns the object tree
125  */
126 DBusObjectTree *
127 _dbus_object_tree_ref (DBusObjectTree *tree)
128 {
129   _dbus_assert (tree->refcount > 0);
130
131   tree->refcount += 1;
132
133   return tree;
134 }
135
136 /**
137  * Decrement the reference count
138  * @param tree the object tree
139  */
140 void
141 _dbus_object_tree_unref (DBusObjectTree *tree)
142 {
143   _dbus_assert (tree->refcount > 0);
144
145   tree->refcount -= 1;
146
147   if (tree->refcount == 0)
148     {
149       _dbus_object_tree_free_all_unlocked (tree);
150
151       dbus_free (tree);
152     }
153 }
154
155 static int
156 subtree_cmp (DBusObjectSubtree *subtree_a,
157              DBusObjectSubtree *subtree_b)
158 {
159   return strcmp (subtree_a->name, subtree_b->name);
160 }
161
162 static int
163 subtree_qsort_cmp (const void *a,
164                    const void *b)
165 {
166   DBusObjectSubtree **subtree_a_p = (void*) a;
167   DBusObjectSubtree **subtree_b_p = (void*) b;
168
169   return subtree_cmp (*subtree_a_p, *subtree_b_p);
170 }
171
172 static void
173 ensure_sorted (DBusObjectSubtree *subtree)
174 {
175   if (subtree->subtrees && !subtree->subtrees_sorted)
176     {
177       qsort (subtree->subtrees,
178              subtree->n_subtrees,
179              sizeof (DBusObjectSubtree*),
180              subtree_qsort_cmp);
181       subtree->subtrees_sorted = TRUE;
182     }
183 }
184
185 /** Set to 1 to get a bunch of debug spew about finding the
186  * subtree nodes
187  */
188 #define VERBOSE_FIND 0
189
190 static DBusObjectSubtree*
191 find_subtree_recurse (DBusObjectSubtree  *subtree,
192                       const char        **path,
193                       dbus_bool_t         return_deepest_match,
194                       dbus_bool_t         create_if_not_found,
195                       int                *index_in_parent)
196 {
197   int i;
198
199   _dbus_assert (!(return_deepest_match && create_if_not_found));
200
201   if (path[0] == NULL)
202     {
203 #if VERBOSE_FIND
204       _dbus_verbose ("  path exhausted, returning %s\n",
205                      subtree->name);
206 #endif
207       return subtree;
208     }
209
210 #if VERBOSE_FIND
211   _dbus_verbose ("  searching children of %s for %s\n",
212                  subtree->name, path[0]);
213 #endif
214   
215   ensure_sorted (subtree);
216
217   /* FIXME we should do a binary search here instead
218    * of O(n)
219    */
220
221   i = 0;
222   while (i < subtree->n_subtrees)
223     {
224       int v;
225
226       v = strcmp (path[0], subtree->subtrees[i]->name);
227
228 #if VERBOSE_FIND
229       _dbus_verbose ("  %s cmp %s = %d\n",
230                      path[0], subtree->subtrees[i]->name,
231                      v);
232 #endif
233       
234       if (v == 0)
235         {
236           if (index_in_parent)
237             {
238 #if VERBOSE_FIND
239               _dbus_verbose ("  storing parent index %d\n", i);
240 #endif
241               *index_in_parent = i;
242             }
243
244           if (return_deepest_match)
245             {
246               DBusObjectSubtree *next;
247
248               next = find_subtree_recurse (subtree->subtrees[i],
249                                            &path[1], return_deepest_match,
250                                            create_if_not_found, index_in_parent);
251               if (next == NULL &&
252                   subtree->invoke_as_fallback)
253                 {
254 #if VERBOSE_FIND
255                   _dbus_verbose ("  no deeper match found, returning %s\n",
256                                  subtree->name);
257 #endif
258                   return subtree;
259                 }
260               else
261                 return next;
262             }
263           else
264             return find_subtree_recurse (subtree->subtrees[i],
265                                          &path[1], return_deepest_match,
266                                          create_if_not_found, index_in_parent);
267         }
268       else if (v < 0)
269         {
270           goto not_found;
271         }
272
273       ++i;
274     }
275
276  not_found:
277 #if VERBOSE_FIND
278   _dbus_verbose ("  no match found, current tree %s, create_if_not_found = %d\n",
279                  subtree->name, create_if_not_found);
280 #endif
281   
282   if (create_if_not_found)
283     {
284       DBusObjectSubtree* child;
285       DBusObjectSubtree **new_subtrees;
286       int new_n_subtrees;
287
288 #if VERBOSE_FIND
289       _dbus_verbose ("  creating subtree %s\n",
290                      path[0]);
291 #endif
292       
293       child = _dbus_object_subtree_new (path[0],
294                                         NULL, NULL);
295       if (child == NULL)
296         return NULL;
297
298       /* FIXME we should do the "double alloc each time" standard thing */
299       new_n_subtrees = subtree->n_subtrees + 1;
300       new_subtrees = dbus_realloc (subtree->subtrees,
301                                    new_n_subtrees * sizeof (DBusObjectSubtree*));
302       if (new_subtrees == NULL)
303         {
304           child->unregister_function = NULL;
305           child->message_function = NULL;
306           _dbus_object_subtree_unref (child);
307           return FALSE;
308         }
309
310       new_subtrees[subtree->n_subtrees] = child;
311       if (index_in_parent)
312         *index_in_parent = subtree->n_subtrees;
313       subtree->subtrees_sorted = FALSE;
314       subtree->n_subtrees = new_n_subtrees;
315       subtree->subtrees = new_subtrees;
316
317       child->parent = subtree;
318
319       return find_subtree_recurse (child,
320                                    &path[1], return_deepest_match,
321                                    create_if_not_found, index_in_parent);
322     }
323   else
324     return (return_deepest_match && subtree->invoke_as_fallback) ? subtree : NULL;
325 }
326
327 static DBusObjectSubtree*
328 find_subtree (DBusObjectTree *tree,
329               const char    **path,
330               int            *index_in_parent)
331 {
332   DBusObjectSubtree *subtree;
333
334 #if VERBOSE_FIND
335   _dbus_verbose ("Looking for exact registered subtree\n");
336 #endif
337   
338   subtree = find_subtree_recurse (tree->root, path, FALSE, FALSE, index_in_parent);
339
340   if (subtree && subtree->message_function == NULL)
341     return NULL;
342   else
343     return subtree;
344 }
345
346 static DBusObjectSubtree*
347 find_handler (DBusObjectTree *tree,
348               const char    **path)
349 {
350 #if VERBOSE_FIND
351   _dbus_verbose ("Looking for deepest handler\n");
352 #endif
353   return find_subtree_recurse (tree->root, path, TRUE, FALSE, NULL);
354 }
355
356 static DBusObjectSubtree*
357 ensure_subtree (DBusObjectTree *tree,
358                 const char    **path)
359 {
360 #if VERBOSE_FIND
361   _dbus_verbose ("Ensuring subtree\n");
362 #endif
363   return find_subtree_recurse (tree->root, path, FALSE, TRUE, NULL);
364 }
365
366 /**
367  * Registers a new subtree in the global object tree.
368  *
369  * @param tree the global object tree
370  * @param fallback #TRUE to handle messages to children of this path
371  * @param path NULL-terminated array of path elements giving path to subtree
372  * @param vtable the vtable used to traverse this subtree
373  * @param user_data user data to pass to methods in the vtable
374  * @returns #FALSE if not enough memory
375  */
376 dbus_bool_t
377 _dbus_object_tree_register (DBusObjectTree              *tree,
378                             dbus_bool_t                  fallback,
379                             const char                 **path,
380                             const DBusObjectPathVTable  *vtable,
381                             void                        *user_data)
382 {
383   DBusObjectSubtree  *subtree;
384
385   _dbus_assert (tree != NULL);
386   _dbus_assert (vtable->message_function != NULL);
387   _dbus_assert (path != NULL);
388
389   subtree = ensure_subtree (tree, path);
390   if (subtree == NULL)
391     return FALSE;
392
393 #ifndef DBUS_DISABLE_CHECKS
394   if (subtree->message_function != NULL)
395     {
396       _dbus_warn ("A handler is already registered for the path starting with path[0] = \"%s\"\n",
397                   path[0] ? path[0] : "null");
398       return FALSE;
399     }
400 #else
401   _dbus_assert (subtree->message_function == NULL);
402 #endif
403
404   subtree->message_function = vtable->message_function;
405   subtree->unregister_function = vtable->unregister_function;
406   subtree->user_data = user_data;
407   subtree->invoke_as_fallback = fallback != FALSE;
408   
409   return TRUE;
410 }
411
412 /**
413  * Unregisters an object subtree that was registered with the
414  * same path.
415  *
416  * @param tree the global object tree
417  * @param path path to the subtree (same as the one passed to _dbus_object_tree_register())
418  */
419 void
420 _dbus_object_tree_unregister_and_unlock (DBusObjectTree          *tree,
421                                          const char             **path)
422 {
423   int i;
424   DBusObjectSubtree *subtree;
425   DBusObjectPathUnregisterFunction unregister_function;
426   void *user_data;
427   DBusConnection *connection;
428
429   _dbus_assert (path != NULL);
430
431   subtree = find_subtree (tree, path, &i);
432
433 #ifndef DBUS_DISABLE_CHECKS
434   if (subtree == NULL)
435     {
436       _dbus_warn ("Attempted to unregister path (path[0] = %s path[1] = %s) which isn't registered\n",
437                   path[0] ? path[0] : "null",
438                   path[1] ? path[1] : "null");
439       return;
440     }
441 #else
442   _dbus_assert (subtree != NULL);
443 #endif
444
445   _dbus_assert (subtree->parent == NULL ||
446                 (i >= 0 && subtree->parent->subtrees[i] == subtree));
447
448   subtree->message_function = NULL;
449
450   unregister_function = subtree->unregister_function;
451   user_data = subtree->user_data;
452
453   subtree->unregister_function = NULL;
454   subtree->user_data = NULL;
455
456   /* If we have no subtrees of our own, remove from
457    * our parent (FIXME could also be more aggressive
458    * and remove our parent if it becomes empty)
459    */
460   if (subtree->parent && subtree->n_subtrees == 0)
461     {
462       /* assumes a 0-byte memmove is OK */
463       memmove (&subtree->parent->subtrees[i],
464                &subtree->parent->subtrees[i+1],
465                (subtree->parent->n_subtrees - i - 1) *
466                sizeof (subtree->parent->subtrees[0]));
467       subtree->parent->n_subtrees -= 1;
468
469       subtree->parent = NULL;
470
471       _dbus_object_subtree_unref (subtree);
472     }
473   subtree = NULL;
474
475   connection = tree->connection;
476
477   /* Unlock and call application code */
478 #ifdef DBUS_BUILD_TESTS
479   if (connection)
480 #endif
481     {
482       _dbus_connection_ref_unlocked (connection);
483       _dbus_connection_unlock (connection);
484     }
485
486   if (unregister_function)
487     (* unregister_function) (connection, user_data);
488
489 #ifdef DBUS_BUILD_TESTS
490   if (connection)
491 #endif
492     dbus_connection_unref (connection);
493 }
494
495 static void
496 free_subtree_recurse (DBusConnection    *connection,
497                       DBusObjectSubtree *subtree)
498 {
499   /* Delete them from the end, for slightly
500    * more robustness against odd reentrancy.
501    */
502   while (subtree->n_subtrees > 0)
503     {
504       DBusObjectSubtree *child;
505
506       child = subtree->subtrees[subtree->n_subtrees - 1];
507       subtree->subtrees[subtree->n_subtrees - 1] = NULL;
508       subtree->n_subtrees -= 1;
509       child->parent = NULL;
510
511       free_subtree_recurse (connection, child);
512     }
513
514   /* Call application code */
515   if (subtree->unregister_function)
516     {
517       (* subtree->unregister_function) (connection,
518                                         subtree->user_data);
519       subtree->message_function = NULL;
520       subtree->unregister_function = NULL;
521       subtree->user_data = NULL;
522     }
523
524   /* Now free ourselves */
525   _dbus_object_subtree_unref (subtree);
526 }
527
528 /**
529  * Free all the handlers in the tree. Lock on tree's connection
530  * must not be held.
531  *
532  * @param tree the object tree
533  */
534 void
535 _dbus_object_tree_free_all_unlocked (DBusObjectTree *tree)
536 {
537   if (tree->root)
538     free_subtree_recurse (tree->connection,
539                           tree->root);
540   tree->root = NULL;
541 }
542
543 static dbus_bool_t
544 _dbus_object_tree_list_registered_unlocked (DBusObjectTree *tree,
545                                             const char    **parent_path,
546                                             char         ***child_entries)
547 {
548   DBusObjectSubtree *subtree;
549   char **retval;
550   
551   _dbus_assert (parent_path != NULL);
552   _dbus_assert (child_entries != NULL);
553
554   *child_entries = NULL;
555   
556   subtree = find_subtree (tree, parent_path, NULL);
557   if (subtree == NULL)
558     {
559       retval = dbus_new0 (char *, 1);
560       if (retval == NULL)
561         goto out;
562     }
563   else
564     {
565       int i;
566       retval = dbus_new0 (char*, subtree->n_subtrees + 1);
567       if (retval == NULL)
568         goto out;
569       i = 0;
570       while (i < subtree->n_subtrees)
571         {
572           retval[i] = _dbus_strdup (subtree->subtrees[i]->name);
573           if (retval[i] == NULL)
574             {
575               dbus_free_string_array (retval);
576               retval = NULL;
577               goto out;
578             }
579           ++i;
580         }
581     }
582
583  out:
584     
585   *child_entries = retval;
586   return retval != NULL;
587 }
588
589 static DBusHandlerResult
590 handle_default_introspect_unlocked (DBusObjectTree          *tree,
591                                     DBusMessage             *message,
592                                     const char             **path)
593 {
594   DBusString xml;
595   DBusHandlerResult result;
596   char **children;
597   int i;
598
599   if (!dbus_message_is_method_call (message,
600                                     DBUS_INTERFACE_ORG_FREEDESKTOP_INTROSPECTABLE,
601                                     "Introspect"))
602     return DBUS_HANDLER_RESULT_NOT_YET_HANDLED;
603   
604   if (!_dbus_string_init (&xml))
605     return DBUS_HANDLER_RESULT_NEED_MEMORY;
606
607   result = DBUS_HANDLER_RESULT_NEED_MEMORY;
608
609   children = NULL;
610   if (!_dbus_object_tree_list_registered_unlocked (tree, path, &children))
611     goto out;
612
613   if (!_dbus_string_append (&xml, "<node>\n"))
614     goto out;
615
616   i = 0;
617   while (children[i] != NULL)
618     {
619       if (!_dbus_string_append_printf (&xml, "  <node name=\"%s\"/>\n",
620                                        children[i]))
621         goto out;
622
623       ++i;
624     }
625
626   if (!_dbus_string_append (&xml, "</node>\n"))
627     goto out;
628   
629   result = DBUS_HANDLER_RESULT_HANDLED;
630   
631  out:
632   _dbus_string_free (&xml);
633   dbus_free_string_array (children);
634   
635   return result;
636 }
637
638 /**
639  * Tries to dispatch a message by directing it to handler for the
640  * object path listed in the message header, if any. Messages are
641  * dispatched first to the registered handler that matches the largest
642  * number of path elements; that is, message to /foo/bar/baz would go
643  * to the handler for /foo/bar before the one for /foo.
644  *
645  * @todo thread problems
646  *
647  * @param tree the global object tree
648  * @param message the message to dispatch
649  * @returns whether message was handled successfully
650  */
651 DBusHandlerResult
652 _dbus_object_tree_dispatch_and_unlock (DBusObjectTree          *tree,
653                                        DBusMessage             *message)
654 {
655   char **path;
656   DBusList *list;
657   DBusList *link;
658   DBusHandlerResult result;
659   DBusObjectSubtree *subtree;
660   
661 #if 0
662   _dbus_verbose ("Dispatch of message by object path\n");
663 #endif
664   
665   path = NULL;
666   if (!dbus_message_get_path_decomposed (message, &path))
667     {
668 #ifdef DBUS_BUILD_TESTS
669       if (tree->connection)
670 #endif
671         _dbus_connection_unlock (tree->connection);
672       
673       _dbus_verbose ("No memory to get decomposed path\n");
674
675       return DBUS_HANDLER_RESULT_NEED_MEMORY;
676     }
677
678   if (path == NULL)
679     {
680 #ifdef DBUS_BUILD_TESTS
681       if (tree->connection)
682 #endif
683         _dbus_connection_unlock (tree->connection);
684       
685       _dbus_verbose ("No path field in message\n");
686       return DBUS_HANDLER_RESULT_NOT_YET_HANDLED;
687     }
688   
689   /* Find the deepest path that covers the path in the message */
690   subtree = find_handler (tree, (const char**) path);
691   
692   /* Build a list of all paths that cover the path in the message */
693
694   list = NULL;
695
696   while (subtree != NULL)
697     {
698       if (subtree->message_function != NULL)
699         {
700           _dbus_object_subtree_ref (subtree);
701
702           /* run deepest paths first */
703           if (!_dbus_list_append (&list, subtree))
704             {
705               result = DBUS_HANDLER_RESULT_NEED_MEMORY;
706               _dbus_object_subtree_unref (subtree);
707               goto free_and_return;
708             }
709         }
710
711       subtree = subtree->parent;
712     }
713
714   _dbus_verbose ("%d handlers in the path tree for this message\n",
715                  _dbus_list_get_length (&list));
716
717   /* Invoke each handler in the list */
718
719   result = DBUS_HANDLER_RESULT_NOT_YET_HANDLED;
720
721   link = _dbus_list_get_first_link (&list);
722   while (link != NULL)
723     {
724       DBusList *next = _dbus_list_get_next_link (&list, link);
725       subtree = link->data;
726
727       /* message_function is NULL if we're unregistered
728        * due to reentrancy
729        */
730       if (subtree->message_function)
731         {
732           DBusObjectPathMessageFunction message_function;
733           void *user_data;
734
735           message_function = subtree->message_function;
736           user_data = subtree->user_data;
737
738 #if 0
739           _dbus_verbose ("  (invoking a handler)\n");
740 #endif
741           
742 #ifdef DBUS_BUILD_TESTS
743           if (tree->connection)
744 #endif
745             _dbus_connection_unlock (tree->connection);
746
747           /* FIXME you could unregister the subtree in another thread
748            * before we invoke the callback, and I can't figure out a
749            * good way to solve this.
750            */
751
752           result = (* message_function) (tree->connection,
753                                          message,
754                                          user_data);
755
756 #ifdef DBUS_BUILD_TESTS
757           if (tree->connection)
758 #endif
759             _dbus_connection_lock (tree->connection);
760
761           if (result != DBUS_HANDLER_RESULT_NOT_YET_HANDLED)
762             goto free_and_return;
763         }
764
765       link = next;
766     }
767
768  free_and_return:
769
770   if (result == DBUS_HANDLER_RESULT_NOT_YET_HANDLED)
771     {
772       /* This hardcoded default handler does a minimal Introspect()
773        */
774       result = handle_default_introspect_unlocked (tree, message,
775                                                    (const char**) path);
776     }
777
778 #ifdef DBUS_BUILD_TESTS
779   if (tree->connection)
780 #endif
781     _dbus_connection_unlock (tree->connection);
782   
783   while (list != NULL)
784     {
785       link = _dbus_list_get_first_link (&list);
786       _dbus_object_subtree_unref (link->data);
787       _dbus_list_remove_link (&list, link);
788     }
789   
790   dbus_free_string_array (path);
791
792   return result;
793 }
794
795 /**
796  * Allocates a subtree object.
797  *
798  * @param name name to duplicate.
799  * @returns newly-allocated subtree
800  */
801 static DBusObjectSubtree*
802 allocate_subtree_object (const char *name)
803 {
804   int len;
805   DBusObjectSubtree *subtree;
806   const size_t front_padding = _DBUS_STRUCT_OFFSET (DBusObjectSubtree, name);
807
808   _dbus_assert (name != NULL);
809
810   len = strlen (name);
811
812   subtree = dbus_malloc (front_padding + (len + 1));
813
814   if (subtree == NULL)
815     return NULL;
816
817   memcpy (subtree->name, name, len + 1);
818
819   return subtree;
820 }
821
822 static DBusObjectSubtree*
823 _dbus_object_subtree_new (const char                  *name,
824                           const DBusObjectPathVTable  *vtable,
825                           void                        *user_data)
826 {
827   DBusObjectSubtree *subtree;
828
829   subtree = allocate_subtree_object (name);
830   if (subtree == NULL)
831     goto oom;
832
833   _dbus_assert (name != NULL);
834
835   subtree->parent = NULL;
836
837   if (vtable)
838     {
839       subtree->message_function = vtable->message_function;
840       subtree->unregister_function = vtable->unregister_function;
841     }
842   else
843     {
844       subtree->message_function = NULL;
845       subtree->unregister_function = NULL;
846     }
847
848   subtree->user_data = user_data;
849   subtree->refcount.value = 1;
850   subtree->subtrees = NULL;
851   subtree->n_subtrees = 0;
852   subtree->subtrees_sorted = TRUE;
853   
854   return subtree;
855
856  oom:
857   if (subtree)
858     {
859       dbus_free (subtree);
860     }
861
862   return NULL;
863 }
864
865 static DBusObjectSubtree *
866 _dbus_object_subtree_ref (DBusObjectSubtree *subtree)
867 {
868   _dbus_assert (subtree->refcount.value > 0);
869   _dbus_atomic_inc (&subtree->refcount);
870
871   return subtree;
872 }
873
874 static void
875 _dbus_object_subtree_unref (DBusObjectSubtree *subtree)
876 {
877   _dbus_assert (subtree->refcount.value > 0);
878
879   if (_dbus_atomic_dec (&subtree->refcount) == 1)
880     {
881       _dbus_assert (subtree->unregister_function == NULL);
882       _dbus_assert (subtree->message_function == NULL);
883
884       dbus_free (subtree->subtrees);
885       dbus_free (subtree);
886     }
887 }
888
889 /**
890  * Lists the registered fallback handlers and object path handlers at
891  * the given parent_path. The returned array should be freed with
892  * dbus_free_string_array().
893  *
894  * @param connection the connection
895  * @param parent_path the path to list the child handlers of
896  * @param child_entries returns #NULL-terminated array of children
897  * @returns #FALSE if no memory to allocate the child entries
898  */
899 dbus_bool_t
900 _dbus_object_tree_list_registered_and_unlock (DBusObjectTree *tree,
901                                               const char    **parent_path,
902                                               char         ***child_entries)
903 {
904   dbus_bool_t result;
905
906   result = _dbus_object_tree_list_registered_unlocked (tree,
907                                                        parent_path,
908                                                        child_entries);
909   
910 #ifdef DBUS_BUILD_TESTS
911   if (tree->connection)
912 #endif
913     _dbus_connection_unlock (tree->connection);
914
915   return result;
916 }
917      
918 /** @} */
919
920 #ifdef DBUS_BUILD_TESTS
921 #include "dbus-test.h"
922 #include <stdio.h>
923
924 static char*
925 flatten_path (const char **path)
926 {
927   DBusString str;
928   int i;
929   char *s;
930
931   if (!_dbus_string_init (&str))
932     return NULL;
933
934   i = 0;
935   while (path[i])
936     {
937       if (!_dbus_string_append_byte (&str, '/'))
938         goto nomem;
939
940       if (!_dbus_string_append (&str, path[i]))
941         goto nomem;
942
943       ++i;
944     }
945
946   if (!_dbus_string_steal_data (&str, &s))
947     goto nomem;
948
949   _dbus_string_free (&str);
950
951   return s;
952
953  nomem:
954   _dbus_string_free (&str);
955   return NULL;
956 }
957
958 /* Returns TRUE if container is a parent of child
959  */
960 static dbus_bool_t
961 path_contains (const char **container,
962                const char **child)
963 {
964   int i;
965
966   i = 0;
967   while (child[i] != NULL)
968     {
969       int v;
970
971       if (container[i] == NULL)
972         return TRUE; /* container ran out, child continues;
973                       * thus the container is a parent of the
974                       * child.
975                       */
976
977       _dbus_assert (container[i] != NULL);
978       _dbus_assert (child[i] != NULL);
979
980       v = strcmp (container[i], child[i]);
981
982       if (v != 0)
983         return FALSE; /* they overlap until here and then are different,
984                        * not overlapping
985                        */
986
987       ++i;
988     }
989
990   /* Child ran out; if container also did, they are equal;
991    * otherwise, the child is a parent of the container.
992    */
993   if (container[i] == NULL)
994     return TRUE; /* equal is counted as containing */
995   else
996     return FALSE;
997 }
998
999 static void
1000 spew_subtree_recurse (DBusObjectSubtree *subtree,
1001                       int                indent)
1002 {
1003   int i;
1004
1005   i = 0;
1006   while (i < indent)
1007     {
1008       _dbus_verbose (" ");
1009       ++i;
1010     }
1011
1012   _dbus_verbose ("%s (%d children)\n",
1013                  subtree->name, subtree->n_subtrees);
1014
1015   i = 0;
1016   while (i < subtree->n_subtrees)
1017     {
1018       spew_subtree_recurse (subtree->subtrees[i], indent + 2);
1019
1020       ++i;
1021     }
1022 }
1023
1024 static void
1025 spew_tree (DBusObjectTree *tree)
1026 {
1027   spew_subtree_recurse (tree->root, 0);
1028 }
1029
1030 /**
1031  * Callback data used in tests
1032  */
1033 typedef struct
1034 {
1035   const char **path; /**< Path */
1036   dbus_bool_t message_handled; /**< Gets set to true if message handler called */
1037   dbus_bool_t handler_unregistered; /**< gets set to true if handler is unregistered */
1038
1039 } TreeTestData;
1040
1041
1042 static void
1043 test_unregister_function (DBusConnection  *connection,
1044                           void            *user_data)
1045 {
1046   TreeTestData *ttd = user_data;
1047
1048   ttd->handler_unregistered = TRUE;
1049 }
1050
1051 static DBusHandlerResult
1052 test_message_function (DBusConnection  *connection,
1053                        DBusMessage     *message,
1054                        void            *user_data)
1055 {
1056   TreeTestData *ttd = user_data;
1057
1058   ttd->message_handled = TRUE;
1059
1060   return DBUS_HANDLER_RESULT_NOT_YET_HANDLED;
1061 }
1062
1063 static dbus_bool_t
1064 do_register (DBusObjectTree *tree,
1065              const char    **path,
1066              int             i,
1067              TreeTestData   *tree_test_data)
1068 {
1069   DBusObjectPathVTable vtable = { test_unregister_function,
1070                                   test_message_function, NULL };
1071
1072   tree_test_data[i].message_handled = FALSE;
1073   tree_test_data[i].handler_unregistered = FALSE;
1074   tree_test_data[i].path = path;
1075
1076   if (!_dbus_object_tree_register (tree, TRUE, path,
1077                                    &vtable,
1078                                    &tree_test_data[i]))
1079     return FALSE;
1080
1081   return TRUE;
1082 }
1083
1084 static dbus_bool_t
1085 do_test_dispatch (DBusObjectTree *tree,
1086                   const char    **path,
1087                   int             i,
1088                   TreeTestData   *tree_test_data,
1089                   int             n_test_data)
1090 {
1091   DBusMessage *message;
1092   int j;
1093   DBusHandlerResult result;
1094   char *flat;
1095
1096   message = NULL;
1097   
1098   flat = flatten_path (path);
1099   if (flat == NULL)
1100     goto oom;
1101
1102   message = dbus_message_new_method_call (NULL,
1103                                           flat,
1104                                           "org.freedesktop.TestInterface",
1105                                           "Foo");
1106   dbus_free (flat);
1107   if (message == NULL)
1108     goto oom;
1109
1110   j = 0;
1111   while (j < n_test_data)
1112     {
1113       tree_test_data[j].message_handled = FALSE;
1114       ++j;
1115     }
1116
1117   result = _dbus_object_tree_dispatch_and_unlock (tree, message);
1118   if (result == DBUS_HANDLER_RESULT_NEED_MEMORY)
1119     goto oom;
1120
1121   _dbus_assert (tree_test_data[i].message_handled);
1122
1123   j = 0;
1124   while (j < n_test_data)
1125     {
1126       if (tree_test_data[j].message_handled)
1127         _dbus_assert (path_contains (tree_test_data[j].path,
1128                                      path));
1129       else
1130         _dbus_assert (!path_contains (tree_test_data[j].path,
1131                                       path));
1132
1133       ++j;
1134     }
1135
1136   dbus_message_unref (message);
1137
1138   return TRUE;
1139
1140  oom:
1141   if (message)
1142     dbus_message_unref (message);
1143   return FALSE;
1144 }
1145
1146 static dbus_bool_t
1147 object_tree_test_iteration (void *data)
1148 {
1149   const char *path1[] = { "foo", NULL };
1150   const char *path2[] = { "foo", "bar", NULL };
1151   const char *path3[] = { "foo", "bar", "baz", NULL };
1152   const char *path4[] = { "foo", "bar", "boo", NULL };
1153   const char *path5[] = { "blah", NULL };
1154   const char *path6[] = { "blah", "boof", NULL };
1155   const char *path7[] = { "blah", "boof", "this", "is", "really", "long", NULL };
1156   const char *path8[] = { "childless", NULL };
1157   DBusObjectTree *tree;
1158   TreeTestData tree_test_data[8];
1159   int i;
1160
1161   tree = NULL;
1162
1163   tree = _dbus_object_tree_new (NULL);
1164   if (tree == NULL)
1165     goto out;
1166
1167   if (!do_register (tree, path1, 0, tree_test_data))
1168     goto out;
1169
1170   _dbus_assert (find_subtree (tree, path1, NULL));
1171   _dbus_assert (!find_subtree (tree, path2, NULL));
1172   _dbus_assert (!find_subtree (tree, path3, NULL));
1173   _dbus_assert (!find_subtree (tree, path4, NULL));
1174   _dbus_assert (!find_subtree (tree, path5, NULL));
1175   _dbus_assert (!find_subtree (tree, path6, NULL));
1176   _dbus_assert (!find_subtree (tree, path7, NULL));
1177   _dbus_assert (!find_subtree (tree, path8, NULL));
1178
1179   _dbus_assert (find_handler (tree, path1));
1180   _dbus_assert (find_handler (tree, path2));
1181   _dbus_assert (find_handler (tree, path3));
1182   _dbus_assert (find_handler (tree, path4));
1183   _dbus_assert (find_handler (tree, path5) == tree->root);
1184   _dbus_assert (find_handler (tree, path6) == tree->root);
1185   _dbus_assert (find_handler (tree, path7) == tree->root);
1186   _dbus_assert (find_handler (tree, path8) == tree->root);
1187
1188   if (!do_register (tree, path2, 1, tree_test_data))
1189     goto out;
1190
1191   _dbus_assert (find_subtree (tree, path1, NULL));
1192   _dbus_assert (find_subtree (tree, path2, NULL));
1193   _dbus_assert (!find_subtree (tree, path3, NULL));
1194   _dbus_assert (!find_subtree (tree, path4, NULL));
1195   _dbus_assert (!find_subtree (tree, path5, NULL));
1196   _dbus_assert (!find_subtree (tree, path6, NULL));
1197   _dbus_assert (!find_subtree (tree, path7, NULL));
1198   _dbus_assert (!find_subtree (tree, path8, NULL));
1199
1200   if (!do_register (tree, path3, 2, tree_test_data))
1201     goto out;
1202
1203   _dbus_assert (find_subtree (tree, path1, NULL));
1204   _dbus_assert (find_subtree (tree, path2, NULL));
1205   _dbus_assert (find_subtree (tree, path3, NULL));
1206   _dbus_assert (!find_subtree (tree, path4, NULL));
1207   _dbus_assert (!find_subtree (tree, path5, NULL));
1208   _dbus_assert (!find_subtree (tree, path6, NULL));
1209   _dbus_assert (!find_subtree (tree, path7, NULL));
1210   _dbus_assert (!find_subtree (tree, path8, NULL));
1211   
1212   if (!do_register (tree, path4, 3, tree_test_data))
1213     goto out;
1214
1215   _dbus_assert (find_subtree (tree, path1, NULL));
1216   _dbus_assert (find_subtree (tree, path2, NULL));
1217   _dbus_assert (find_subtree (tree, path3, NULL));  
1218   _dbus_assert (find_subtree (tree, path4, NULL));
1219   _dbus_assert (!find_subtree (tree, path5, NULL));
1220   _dbus_assert (!find_subtree (tree, path6, NULL));
1221   _dbus_assert (!find_subtree (tree, path7, NULL));
1222   _dbus_assert (!find_subtree (tree, path8, NULL));
1223   
1224   if (!do_register (tree, path5, 4, tree_test_data))
1225     goto out;
1226
1227   _dbus_assert (find_subtree (tree, path1, NULL));
1228   _dbus_assert (find_subtree (tree, path2, NULL));
1229   _dbus_assert (find_subtree (tree, path3, NULL));
1230   _dbus_assert (find_subtree (tree, path4, NULL));
1231   _dbus_assert (find_subtree (tree, path5, NULL));
1232   _dbus_assert (!find_subtree (tree, path6, NULL));
1233   _dbus_assert (!find_subtree (tree, path7, NULL));
1234   _dbus_assert (!find_subtree (tree, path8, NULL));
1235   
1236   _dbus_assert (find_handler (tree, path1) != tree->root);
1237   _dbus_assert (find_handler (tree, path2) != tree->root);
1238   _dbus_assert (find_handler (tree, path3) != tree->root);
1239   _dbus_assert (find_handler (tree, path4) != tree->root);
1240   _dbus_assert (find_handler (tree, path5) != tree->root);
1241   _dbus_assert (find_handler (tree, path6) != tree->root);
1242   _dbus_assert (find_handler (tree, path7) != tree->root);
1243   _dbus_assert (find_handler (tree, path8) == tree->root);
1244
1245   if (!do_register (tree, path6, 5, tree_test_data))
1246     goto out;
1247
1248   _dbus_assert (find_subtree (tree, path1, NULL));
1249   _dbus_assert (find_subtree (tree, path2, NULL));
1250   _dbus_assert (find_subtree (tree, path3, NULL));
1251   _dbus_assert (find_subtree (tree, path4, NULL));
1252   _dbus_assert (find_subtree (tree, path5, NULL));
1253   _dbus_assert (find_subtree (tree, path6, NULL));
1254   _dbus_assert (!find_subtree (tree, path7, NULL));
1255   _dbus_assert (!find_subtree (tree, path8, NULL));
1256
1257   if (!do_register (tree, path7, 6, tree_test_data))
1258     goto out;
1259
1260   _dbus_assert (find_subtree (tree, path1, NULL));
1261   _dbus_assert (find_subtree (tree, path2, NULL));
1262   _dbus_assert (find_subtree (tree, path3, NULL));
1263   _dbus_assert (find_subtree (tree, path4, NULL));
1264   _dbus_assert (find_subtree (tree, path5, NULL));
1265   _dbus_assert (find_subtree (tree, path6, NULL));
1266   _dbus_assert (find_subtree (tree, path7, NULL));
1267   _dbus_assert (!find_subtree (tree, path8, NULL));
1268
1269   if (!do_register (tree, path8, 7, tree_test_data))
1270     goto out;
1271
1272   _dbus_assert (find_subtree (tree, path1, NULL));
1273   _dbus_assert (find_subtree (tree, path2, NULL));
1274   _dbus_assert (find_subtree (tree, path3, NULL));
1275   _dbus_assert (find_subtree (tree, path4, NULL));
1276   _dbus_assert (find_subtree (tree, path5, NULL));
1277   _dbus_assert (find_subtree (tree, path6, NULL));
1278   _dbus_assert (find_subtree (tree, path7, NULL));
1279   _dbus_assert (find_subtree (tree, path8, NULL));
1280   
1281   _dbus_assert (find_handler (tree, path1) != tree->root);
1282   _dbus_assert (find_handler (tree, path2) != tree->root);
1283   _dbus_assert (find_handler (tree, path3) != tree->root);
1284   _dbus_assert (find_handler (tree, path4) != tree->root);
1285   _dbus_assert (find_handler (tree, path5) != tree->root);
1286   _dbus_assert (find_handler (tree, path6) != tree->root);
1287   _dbus_assert (find_handler (tree, path7) != tree->root);
1288   _dbus_assert (find_handler (tree, path8) != tree->root);
1289   
1290   /* Check that destroying tree calls unregister funcs */
1291   _dbus_object_tree_unref (tree);
1292
1293   i = 0;
1294   while (i < (int) _DBUS_N_ELEMENTS (tree_test_data))
1295     {
1296       _dbus_assert (tree_test_data[i].handler_unregistered);
1297       _dbus_assert (!tree_test_data[i].message_handled);
1298       ++i;
1299     }
1300
1301   /* Now start again and try the individual unregister function */
1302   tree = _dbus_object_tree_new (NULL);
1303   if (tree == NULL)
1304     goto out;
1305
1306   if (!do_register (tree, path1, 0, tree_test_data))
1307     goto out;
1308   if (!do_register (tree, path2, 1, tree_test_data))
1309     goto out;
1310   if (!do_register (tree, path3, 2, tree_test_data))
1311     goto out;
1312   if (!do_register (tree, path4, 3, tree_test_data))
1313     goto out;
1314   if (!do_register (tree, path5, 4, tree_test_data))
1315     goto out;
1316   if (!do_register (tree, path6, 5, tree_test_data))
1317     goto out;
1318   if (!do_register (tree, path7, 6, tree_test_data))
1319     goto out;
1320   if (!do_register (tree, path8, 7, tree_test_data))
1321     goto out;
1322   
1323   _dbus_object_tree_unregister_and_unlock (tree, path1);
1324
1325   _dbus_assert (!find_subtree (tree, path1, NULL));
1326   _dbus_assert (find_subtree (tree, path2, NULL));
1327   _dbus_assert (find_subtree (tree, path3, NULL));
1328   _dbus_assert (find_subtree (tree, path4, NULL));
1329   _dbus_assert (find_subtree (tree, path5, NULL));
1330   _dbus_assert (find_subtree (tree, path6, NULL));
1331   _dbus_assert (find_subtree (tree, path7, NULL));
1332   _dbus_assert (find_subtree (tree, path8, NULL));
1333
1334   _dbus_object_tree_unregister_and_unlock (tree, path2);
1335
1336   _dbus_assert (!find_subtree (tree, path1, NULL));
1337   _dbus_assert (!find_subtree (tree, path2, NULL));
1338   _dbus_assert (find_subtree (tree, path3, NULL));
1339   _dbus_assert (find_subtree (tree, path4, NULL));
1340   _dbus_assert (find_subtree (tree, path5, NULL));
1341   _dbus_assert (find_subtree (tree, path6, NULL));
1342   _dbus_assert (find_subtree (tree, path7, NULL));
1343   _dbus_assert (find_subtree (tree, path8, NULL));
1344   
1345   _dbus_object_tree_unregister_and_unlock (tree, path3);
1346
1347   _dbus_assert (!find_subtree (tree, path1, NULL));
1348   _dbus_assert (!find_subtree (tree, path2, NULL));
1349   _dbus_assert (!find_subtree (tree, path3, NULL));
1350   _dbus_assert (find_subtree (tree, path4, NULL));
1351   _dbus_assert (find_subtree (tree, path5, NULL));
1352   _dbus_assert (find_subtree (tree, path6, NULL));
1353   _dbus_assert (find_subtree (tree, path7, NULL));
1354   _dbus_assert (find_subtree (tree, path8, NULL));
1355   
1356   _dbus_object_tree_unregister_and_unlock (tree, path4);
1357
1358   _dbus_assert (!find_subtree (tree, path1, NULL));
1359   _dbus_assert (!find_subtree (tree, path2, NULL));
1360   _dbus_assert (!find_subtree (tree, path3, NULL));
1361   _dbus_assert (!find_subtree (tree, path4, NULL));
1362   _dbus_assert (find_subtree (tree, path5, NULL));
1363   _dbus_assert (find_subtree (tree, path6, NULL));
1364   _dbus_assert (find_subtree (tree, path7, NULL));
1365   _dbus_assert (find_subtree (tree, path8, NULL));
1366   
1367   _dbus_object_tree_unregister_and_unlock (tree, path5);
1368
1369   _dbus_assert (!find_subtree (tree, path1, NULL));
1370   _dbus_assert (!find_subtree (tree, path2, NULL));
1371   _dbus_assert (!find_subtree (tree, path3, NULL));
1372   _dbus_assert (!find_subtree (tree, path4, NULL));
1373   _dbus_assert (!find_subtree (tree, path5, NULL));
1374   _dbus_assert (find_subtree (tree, path6, NULL));
1375   _dbus_assert (find_subtree (tree, path7, NULL));
1376   _dbus_assert (find_subtree (tree, path8, NULL));
1377   
1378   _dbus_object_tree_unregister_and_unlock (tree, path6);
1379
1380   _dbus_assert (!find_subtree (tree, path1, NULL));
1381   _dbus_assert (!find_subtree (tree, path2, NULL));
1382   _dbus_assert (!find_subtree (tree, path3, NULL));
1383   _dbus_assert (!find_subtree (tree, path4, NULL));
1384   _dbus_assert (!find_subtree (tree, path5, NULL));
1385   _dbus_assert (!find_subtree (tree, path6, NULL));
1386   _dbus_assert (find_subtree (tree, path7, NULL));
1387   _dbus_assert (find_subtree (tree, path8, NULL));
1388
1389   _dbus_object_tree_unregister_and_unlock (tree, path7);
1390
1391   _dbus_assert (!find_subtree (tree, path1, NULL));
1392   _dbus_assert (!find_subtree (tree, path2, NULL));
1393   _dbus_assert (!find_subtree (tree, path3, NULL));
1394   _dbus_assert (!find_subtree (tree, path4, NULL));
1395   _dbus_assert (!find_subtree (tree, path5, NULL));
1396   _dbus_assert (!find_subtree (tree, path6, NULL));
1397   _dbus_assert (!find_subtree (tree, path7, NULL));
1398   _dbus_assert (find_subtree (tree, path8, NULL));
1399
1400   _dbus_object_tree_unregister_and_unlock (tree, path8);
1401
1402   _dbus_assert (!find_subtree (tree, path1, NULL));
1403   _dbus_assert (!find_subtree (tree, path2, NULL));
1404   _dbus_assert (!find_subtree (tree, path3, NULL));
1405   _dbus_assert (!find_subtree (tree, path4, NULL));
1406   _dbus_assert (!find_subtree (tree, path5, NULL));
1407   _dbus_assert (!find_subtree (tree, path6, NULL));
1408   _dbus_assert (!find_subtree (tree, path7, NULL));
1409   _dbus_assert (!find_subtree (tree, path8, NULL));
1410   
1411   i = 0;
1412   while (i < (int) _DBUS_N_ELEMENTS (tree_test_data))
1413     {
1414       _dbus_assert (tree_test_data[i].handler_unregistered);
1415       _dbus_assert (!tree_test_data[i].message_handled);
1416       ++i;
1417     }
1418
1419   /* Register it all again, and test dispatch */
1420
1421   if (!do_register (tree, path1, 0, tree_test_data))
1422     goto out;
1423   if (!do_register (tree, path2, 1, tree_test_data))
1424     goto out;
1425   if (!do_register (tree, path3, 2, tree_test_data))
1426     goto out;
1427   if (!do_register (tree, path4, 3, tree_test_data))
1428     goto out;
1429   if (!do_register (tree, path5, 4, tree_test_data))
1430     goto out;
1431   if (!do_register (tree, path6, 5, tree_test_data))
1432     goto out;
1433   if (!do_register (tree, path7, 6, tree_test_data))
1434     goto out;
1435   if (!do_register (tree, path8, 7, tree_test_data))
1436     goto out;
1437
1438 #if 0
1439   spew_tree (tree);
1440 #endif
1441   
1442   if (!do_test_dispatch (tree, path1, 0, tree_test_data, _DBUS_N_ELEMENTS (tree_test_data)))
1443     goto out;
1444   if (!do_test_dispatch (tree, path2, 1, tree_test_data, _DBUS_N_ELEMENTS (tree_test_data)))
1445     goto out;
1446   if (!do_test_dispatch (tree, path3, 2, tree_test_data, _DBUS_N_ELEMENTS (tree_test_data)))
1447     goto out;
1448   if (!do_test_dispatch (tree, path4, 3, tree_test_data, _DBUS_N_ELEMENTS (tree_test_data)))
1449     goto out;
1450   if (!do_test_dispatch (tree, path5, 4, tree_test_data, _DBUS_N_ELEMENTS (tree_test_data)))
1451     goto out;
1452   if (!do_test_dispatch (tree, path6, 5, tree_test_data, _DBUS_N_ELEMENTS (tree_test_data)))
1453     goto out;
1454   if (!do_test_dispatch (tree, path7, 6, tree_test_data, _DBUS_N_ELEMENTS (tree_test_data)))
1455     goto out;
1456   if (!do_test_dispatch (tree, path8, 7, tree_test_data, _DBUS_N_ELEMENTS (tree_test_data)))
1457     goto out;
1458   
1459  out:
1460   if (tree)
1461     {
1462       /* test ref */
1463       _dbus_object_tree_ref (tree);
1464       _dbus_object_tree_unref (tree);
1465       _dbus_object_tree_unref (tree);
1466     }
1467
1468   return TRUE;
1469 }
1470
1471 /**
1472  * @ingroup DBusObjectTree
1473  * Unit test for DBusObjectTree
1474  * @returns #TRUE on success.
1475  */
1476 dbus_bool_t
1477 _dbus_object_tree_test (void)
1478 {
1479   _dbus_test_oom_handling ("object tree",
1480                            object_tree_test_iteration,
1481                            NULL);
1482
1483   return TRUE;
1484 }
1485
1486 #endif /* DBUS_BUILD_TESTS */