Started work on component interface; various fixes and cleanups
[platform/upstream/at-spi2-core.git] / atspi / atspi-misc.c
1 /*
2  * AT-SPI - Assistive Technology Service Provider Interface
3  * (Gnome Accessibility Project; http://developer.gnome.org/projects/gap)
4  *
5  * Copyright 2001, 2002 Sun Microsystems Inc.,
6  * Copyright 2001, 2002 Ximian, Inc.
7  *
8  * This library is free software; you can redistribute it and/or
9  * modify it under the terms of the GNU Library General Public
10  * License as published by the Free Software Foundation; either
11  * version 2 of the License, or (at your option) any later version.
12  *
13  * This library 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 GNU
16  * Library General Public License for more details.
17  *
18  * You should have received a copy of the GNU Library General Public
19  * License along with this library; if not, write to the
20  * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
21  * Boston, MA 02111-1307, USA.
22  */
23
24 /*
25  *
26  * Basic SPI initialization and event loop function prototypes
27  *
28  */
29
30 #include "atspi-private.h"
31
32 static DBusConnection *bus = NULL;
33 static GHashTable *apps = NULL;
34 static GHashTable *live_refs = NULL;
35 static GQueue *exception_handlers = NULL;
36 static DBusError exception;
37
38 const char *atspi_path_dec = ATSPI_DBUS_PATH_DEC;
39 const char *atspi_path_registry = ATSPI_DBUS_PATH_REGISTRY;
40 const char *atspi_path_root = ATSPI_DBUS_PATH_ROOT;
41 const char *atspi_bus_registry = ATSPI_DBUS_NAME_REGISTRY;
42 const char *atspi_interface_accessible = ATSPI_DBUS_INTERFACE_ACCESSIBLE;
43 const char *atspi_interface_action = ATSPI_DBUS_INTERFACE_ACTION;
44 const char *atspi_interface_application = ATSPI_DBUS_INTERFACE_APPLICATION;
45 const char *atspi_interface_component = ATSPI_DBUS_INTERFACE_COMPONENT;
46 const char *atspi_interface_dec = ATSPI_DBUS_INTERFACE_DEC;
47 const char *atspi_interface_device_event_listener = ATSPI_DBUS_INTERFACE_DEVICE_EVENT_LISTENER;
48 const char *atspi_interface_document = ATSPI_DBUS_INTERFACE_DOCUMENT;
49 const char *atspi_interface_editable_text = ATSPI_DBUS_INTERFACE_EDITABLE_TEXT;
50 const char *atspi_interface_hyperlink = ATSPI_DBUS_INTERFACE_HYPERLINK;
51 const char *atspi_interface_hypertext = ATSPI_DBUS_INTERFACE_HYPERTEXT;
52 const char *atspi_interface_image = ATSPI_DBUS_INTERFACE_IMAGE;
53 const char *atspi_interface_registry = ATSPI_DBUS_INTERFACE_REGISTRY;
54 const char *atspi_interface_selection = ATSPI_DBUS_INTERFACE_SELECTION;
55 const char *atspi_interface_table = ATSPI_DBUS_INTERFACE_TABLE;
56 const char *atspi_interface_text = ATSPI_DBUS_INTERFACE_TEXT;
57 const char *atspi_interface_cache = ATSPI_DBUS_INTERFACE_CACHE;
58 const char *atspi_interface_value = ATSPI_DBUS_INTERFACE_VALUE;
59
60 static const char *interfaces[] =
61 {
62   ATSPI_DBUS_INTERFACE_ACCESSIBLE,
63   ATSPI_DBUS_INTERFACE_ACTION,
64   ATSPI_DBUS_INTERFACE_APPLICATION,
65   ATSPI_DBUS_INTERFACE_COLLECTION,
66   ATSPI_DBUS_INTERFACE_COMPONENT,
67   ATSPI_DBUS_INTERFACE_DOCUMENT,
68   ATSPI_DBUS_INTERFACE_EDITABLE_TEXT,
69   ATSPI_DBUS_INTERFACE_HYPERLINK,
70   ATSPI_DBUS_INTERFACE_HYPERTEXT,
71   ATSPI_DBUS_INTERFACE_IMAGE,
72   "org.a11y.atspi.LoginHelper",
73   ATSPI_DBUS_INTERFACE_SELECTION,
74   ATSPI_DBUS_INTERFACE_TABLE,
75   ATSPI_DBUS_INTERFACE_TEXT,
76   ATSPI_DBUS_INTERFACE_VALUE,
77   NULL
78 };
79
80 gint
81 _atspi_get_iface_num (const char *iface)
82 {
83   /* TODO: Use a binary search or hash to improve performance */
84   int i;
85
86   for (i = 0; interfaces[i]; i++)
87   {
88     if (!strcmp(iface, interfaces[i])) return i;
89   }
90   return -1;
91 }
92
93 static GHashTable *
94 get_live_refs (void)
95 {
96   if (!live_refs) 
97     {
98       live_refs = g_hash_table_new (g_direct_hash, g_direct_equal);
99     }
100   return live_refs;
101 }
102
103 /* TODO: Add an application parameter */
104 DBusConnection *
105 _atspi_bus ()
106 {
107   if (!bus)
108     atspi_init ();
109   return bus;
110 }
111
112 #define APP_IS_REGISTRY(app) (!strcmp (app->bus_name, atspi_bus_registry))
113
114 static void
115 cleanup ()
116 {
117   GHashTable *refs;
118
119   refs = live_refs;
120   live_refs = NULL;
121   if (refs)
122     {
123       g_hash_table_destroy (refs);
124     }
125 }
126
127 static gboolean atspi_inited = FALSE;
128
129 static GHashTable *app_hash = NULL;
130
131 static AtspiApplication *
132 get_application (const char *bus_name)
133 {
134   AtspiApplication *app = NULL;
135   char *bus_name_dup;
136
137   if (!app_hash)
138   {
139     app_hash = g_hash_table_new_full (g_str_hash, g_str_equal, g_free, (GDestroyNotify)g_hash_table_unref);
140     if (!app_hash) return NULL;
141   }
142   app = g_hash_table_lookup (app_hash, bus_name);
143   if (app) return app;
144   bus_name_dup = g_strdup (bus_name);
145   if (!bus_name_dup) return NULL;
146   // TODO: change below to something that will send state-change:defunct notification if necessary */
147   app = _atspi_application_new (bus_name);
148   if (!app) return NULL;
149   app->bus_name = bus_name_dup;
150   if (APP_IS_REGISTRY (app))
151   {
152     app->hash = g_hash_table_new_full (g_str_hash, g_str_equal, g_free, g_object_unref);
153   }
154   else
155   {
156     app->hash = g_hash_table_new_full (g_int_hash, g_int_equal, g_free, g_object_unref);
157   }
158   g_hash_table_insert (app_hash, bus_name_dup, app);
159   return app;
160 }
161
162 static AtspiAccessible *
163 ref_accessible (const char *app_name, const char *path)
164 {
165   AtspiApplication *app = get_application (app_name);
166   AtspiAccessible *a;
167
168   if (!strcmp (path, "/org/a11y/atspi/accessible/root"))
169   {
170     if (!app->root)
171     {
172       app->root = atspi_accessible_new (app, atspi_path_root);
173       app->root->accessible_parent = atspi_get_desktop (0);
174     }
175     return g_object_ref (app->root);
176   }
177
178   a = g_hash_table_lookup (app->hash, path);
179   if (a)
180   {
181     g_object_ref (a);
182     return a;
183   }
184   a = atspi_accessible_new (app, path);
185   if (!a)
186     return NULL;
187   g_hash_table_insert (app->hash, a->path, a);
188   g_object_ref (a);     /* for the hash */
189   return a;
190 }
191
192 typedef struct
193 {
194   char *path;
195   char *parent;
196   GArray *children;
197   GArray *interfaces;
198   char *name;
199   dbus_uint32_t role;
200   char *description;
201   GArray *state_bitflags;
202 } CACHE_ADDITION;
203
204 static DBusHandlerResult
205 handle_remove_accessible (DBusConnection *bus, DBusMessage *message, void *user_data)
206 {
207   const char *sender = dbus_message_get_sender (message);
208   AtspiApplication *app;
209   const char *path;
210   DBusMessageIter iter, iter_struct;
211   const char *signature = dbus_message_get_signature (message);
212   AtspiAccessible *a;
213   int id;
214
215   if (strcmp (signature, "(so)") != 0)
216   {
217     g_warning ("at-spi: Unknown signature %s for RemoveAccessible", signature);
218     return DBUS_HANDLER_RESULT_HANDLED;
219   }
220
221   dbus_message_iter_init (message, &iter);
222   dbus_message_iter_recurse (&iter, &iter_struct);
223   dbus_message_iter_get_basic (&iter_struct, &sender);
224   dbus_message_iter_get_basic (&iter_struct, &path);
225   app = get_application (sender);
226   a = ref_accessible (sender, path);
227   if (a->accessible_parent && g_list_find (a->accessible_parent->children, a))
228   {
229     a->accessible_parent->children = g_list_remove (a->accessible_parent->children, a);
230     g_object_unref (a);
231   }
232   g_hash_table_remove (app->hash, app->bus_name);
233   g_object_unref (a);   /* unref our own ref */
234   return DBUS_HANDLER_RESULT_HANDLED;
235 }
236
237 static gboolean
238 add_app_to_desktop (AtspiAccessible *a, const char *bus_name)
239 {
240   DBusError error;
241   char *root_path;
242
243   dbus_error_init (&error);
244   AtspiAccessible *obj = ref_accessible (bus_name, atspi_path_root);
245   if (obj)
246   {
247     GList *new_list = g_list_append (a->children, obj);
248     if (new_list)
249     {
250       a->children = new_list;
251       return TRUE;
252     }
253   }
254   else
255   {
256     g_warning ("Error calling getRoot for %s: %s", bus_name, error.message);
257   }
258   return FALSE;
259 }
260
261 static void
262 send_children_changed (AtspiAccessible *parent, AtspiAccessible *child, gboolean add)
263 {
264   AtspiEvent e;
265
266   memset (&e, 0, sizeof(e));
267   e.type = (add? "object:children-changed:add": "object:children-changed:remove");
268   e.source = parent;
269   e.detail1 = g_list_index (parent->children, child);
270 #if 0
271   g_warning ("atspi: TODO: Finish events");
272   atspi_dispatch_event (&e);
273 #endif
274 }
275
276 static void
277 unref_object_and_descendants (AtspiAccessible *obj)
278 {
279   GList *l;
280
281   for (l = obj->children; l; l = l->next)
282   {
283     unref_object_and_descendants (l->data);
284   }
285   g_object_unref (obj);
286 }
287
288 static gboolean
289 remove_app_from_desktop (AtspiAccessible *a, const char *bus_name)
290 {
291   GList *l;
292   AtspiAccessible *child;
293
294   for (l = a->children; l; l = l->next)
295   {
296     child = l->data;
297     if (!strcmp (bus_name, child->app->bus_name)) break;
298   }
299   if (!l)
300   {
301     g_warning ("Removing unregistered app %s; doing nothing\n", bus_name);
302     return FALSE;
303   }
304   send_children_changed (a, child, FALSE);
305   a->children = g_list_remove (a->children, child);
306   unref_object_and_descendants (child);
307   return TRUE;
308 }
309
310 static AtspiAccessible *desktop;
311
312 static void
313 get_reference_from_iter (DBusMessageIter *iter, const char **app_name, const char **path)
314 {
315   DBusMessageIter iter_struct;
316
317   dbus_message_iter_recurse (iter, &iter_struct);
318   dbus_message_iter_get_basic (&iter_struct, app_name);
319   dbus_message_iter_next (&iter_struct);
320   dbus_message_iter_get_basic (&iter_struct, path);
321   dbus_message_iter_next (iter);
322 }
323
324 static void
325 add_accessible_from_iter (DBusMessageIter *iter)
326 {
327   gint i;
328   GList *new_list;
329   DBusMessageIter iter_struct, iter_array;
330   const char *app_name, *path;
331   AtspiApplication *app;
332   AtspiAccessible *accessible;
333   const char *name, *description;
334   dbus_uint32_t role;
335   dbus_uint32_t *states;
336   int count;
337
338   dbus_message_iter_recurse (iter, &iter_struct);
339
340   /* get accessible */
341   get_reference_from_iter (&iter_struct, &app_name, &path);
342   accessible = ref_accessible (app_name, path);
343
344   /* Get application: TODO */
345   dbus_message_iter_next (&iter_struct);
346
347   /* get parent */
348   get_reference_from_iter (&iter_struct, &app_name, &path);
349   accessible->accessible_parent = ref_accessible (app_name, path);
350
351   /* Get children */
352   dbus_message_iter_recurse (&iter_struct, &iter_array);
353   while (dbus_message_iter_get_arg_type (&iter_array) != DBUS_TYPE_INVALID)
354   {
355     AtspiAccessible *child;
356     get_reference_from_iter (&iter_array, &app_name, &path);
357     child = ref_accessible (app_name, path);
358     new_list = g_list_append (accessible->children, child);
359     if (new_list) accessible->children = new_list;
360   }
361
362   /* interfaces */
363   accessible->interfaces = 0;
364   dbus_message_iter_next (&iter_struct);
365   dbus_message_iter_recurse (&iter_struct, &iter_array);
366   while (dbus_message_iter_get_arg_type (&iter_array) != DBUS_TYPE_INVALID)
367   {
368     const char *iface;
369     gint n;
370     dbus_message_iter_get_basic (&iter_array, &iface);
371     if (!strcmp (iface, "org.freedesktop.DBus.Introspectable")) continue;
372     n = _atspi_get_iface_num (iface);
373     if (n == -1)
374     {
375       g_warning ("Unknown interface %s", iface);
376     }
377     else accessible->interfaces |= (1 << n);
378     dbus_message_iter_next (&iter_array);
379   }
380   dbus_message_iter_next (&iter_struct);
381
382   /* name */
383   dbus_message_iter_get_basic (&iter_struct, &name);
384   accessible->name = g_strdup (name);
385   dbus_message_iter_next (&iter_struct);
386
387   /* role */
388   dbus_message_iter_get_basic (&iter_struct, &role);
389   accessible->role = role;
390   dbus_message_iter_next (&iter_struct);
391
392   /* description */
393   dbus_message_iter_get_basic (&iter_struct, &description);
394   accessible->description = g_strdup (description);
395   dbus_message_iter_next (&iter_struct);
396
397   dbus_message_iter_recurse (&iter_struct, &iter_array);
398   dbus_message_iter_get_fixed_array (&iter_array, &states, &count);
399   if (count != 2)
400   {
401     g_warning ("at-spi: expected 2 values in states array; got %d\n", count);
402     accessible->states = atspi_state_set_new (0);
403   }
404   else
405   {
406     guint64 val = ((guint64)states [1]) << 32;
407     val += states [0];
408     accessible->states = atspi_state_set_new (val);
409   }
410   dbus_message_iter_next (&iter_struct);
411
412   /* This is a bit of a hack since the cache holds a ref, so we don't need
413    * the one provided for us anymore */
414   g_object_unref (accessible);
415 }
416
417 static void
418 add_accessibles (const char *app_name)
419 {
420   DBusError error;
421   DBusMessage *message, *reply;
422   DBusMessageIter iter, iter_array;
423
424   AtspiApplication *app = get_application (app_name);
425   /* TODO: Move this functionality into app initializer? */
426   dbus_error_init (&error);
427   message = dbus_message_new_method_call (app_name, "/org/a11y/atspi/cache", atspi_interface_cache, "GetItems");
428   reply = _atspi_dbus_send_with_reply_and_block (message);
429   if (!reply || strcmp (dbus_message_get_signature (reply), "a((so)(so)(so)a(so)assusau)") != 0)
430   {
431     g_warning ("at-spi: Error in GetItems");
432     return;
433     if (reply)
434       dbus_message_unref (reply);
435   }
436   dbus_message_iter_init (reply, &iter);
437   dbus_message_iter_recurse (&iter, &iter_array);
438   while (dbus_message_iter_get_arg_type (&iter_array) != DBUS_TYPE_INVALID)
439   {
440     add_accessible_from_iter (&iter_array);
441     dbus_message_iter_next (&iter_array);
442   }
443   dbus_message_unref (reply);
444 }
445
446 /* TODO: Do we stil need this function? */
447 static AtspiAccessible *
448 ref_accessible_desktop (AtspiApplication *app)
449 {
450   DBusError error;
451   GArray *apps = NULL;
452   GArray *additions;
453   gint i;
454   DBusMessage *message, *reply;
455   DBusMessageIter iter, iter_array;
456
457   if (desktop)
458   {
459     g_object_ref (desktop);
460     return desktop;
461   }
462   desktop = atspi_accessible_new (app, atspi_path_root);
463   if (!desktop)
464   {
465     return NULL;
466   }
467   g_hash_table_insert (app->hash, desktop->path, desktop);
468   g_object_ref (desktop);       /* for the hash */
469   desktop->name = g_strdup ("main");
470   dbus_error_init (&error);
471   message = dbus_message_new_method_call (atspi_bus_registry,
472         atspi_path_root,
473         atspi_interface_accessible,
474         "GetChildren");
475   if (!message)
476     return;
477   reply = _atspi_dbus_send_with_reply_and_block (message);
478   if (!reply || strcmp (dbus_message_get_signature (reply), "a(so)") != 0)
479   {
480     g_error ("Couldn't get application list: %s", error.message);
481     if (reply)
482       dbus_message_unref (reply);
483     return;
484   }
485   dbus_message_iter_init (reply, &iter);
486   dbus_message_iter_recurse (&iter, &iter_array);
487   while (dbus_message_iter_get_arg_type (&iter_array) != DBUS_TYPE_INVALID)
488   {
489     const char *app_name, *path;
490     get_reference_from_iter (&iter_array, &app_name, &path);
491     add_accessibles (app_name);
492     add_app_to_desktop (desktop, app_name);
493   }
494   dbus_message_unref (reply);
495   return desktop;
496 }
497
498 AtspiAccessible *
499 _atspi_ref_accessible (const char *app, const char *path)
500 {
501   AtspiApplication *a = get_application (app);
502   if (!a) return NULL;
503   if ( APP_IS_REGISTRY(a))
504   {
505     return ref_accessible_desktop (a);
506   }
507   return ref_accessible (app, path);
508 }
509
510 AtspiAccessible *
511 _atspi_dbus_return_accessible_from_message (DBusMessage *message)
512 {
513   DBusMessageIter iter;
514   const char *app_name, *path;
515   AtspiAccessible *retval = NULL;
516   const char *signature = dbus_message_get_signature (message);
517    
518   if (!strcmp (signature, "(so)"))
519   {
520     dbus_message_iter_init (message, &iter);
521     get_reference_from_iter (&iter, &app_name, &path);
522     retval = _atspi_ref_accessible (app_name, path);
523   }
524   else
525   {
526     g_warning ("Atspi: Called __atspi_dbus_return_accessible_from_message with strange signature %s", signature);
527   }
528   dbus_message_unref (message);
529   return retval;
530 }
531
532 /* TODO: Remove this function. We should not need it anymore.
533  * If we do, it's a bug.
534  */
535 AtspiAccessible *
536 _atspi_ref_related_accessible (AtspiAccessible *obj, const AtspiReference *ref)
537 {
538   const char *app = (ref->name && ref->name[0]? ref->name: obj->app->bus_name);
539   return ref_accessible (app, obj->path);
540 }
541
542 const char *cache_signal_type = "((so)(so)a(so)assusau)";
543
544 static DBusHandlerResult
545 handle_add_accessible (DBusConnection *bus, DBusMessage *message, void *user_data)
546 {
547   DBusMessageIter iter;
548   const char *sender = dbus_message_get_sender (message);
549   AtspiApplication *app = get_application (sender);
550   const char *type = cache_signal_type;
551
552   if (strcmp (dbus_message_get_signature (message), cache_signal_type) != 0)
553   {
554     g_warning ("atspi: AddAccessible with unknown signature %s\n", dbus_message_get_signature (message));
555     return;
556   }
557
558   dbus_message_iter_init (message, &iter);
559   add_accessible_from_iter (&iter);
560 }
561
562 static DBusHandlerResult
563 atspi_dbus_filter (DBusConnection *bus, DBusMessage *message, void *data)
564 {
565   int type = dbus_message_get_type (message);
566   const char *interface = dbus_message_get_interface (message);
567   const char *member = dbus_message_get_member (message); 
568   dbus_uint32_t v;
569   char *bus_name;
570
571   if (type == DBUS_MESSAGE_TYPE_SIGNAL &&
572       !strncmp (interface, "org.a11y.atspi.Event.", 28))
573   {
574     g_warning ("atspi: TODO: event");
575     //return handle_event (bus, message, data);
576   }
577   if (dbus_message_is_method_call (message, atspi_interface_device_event_listener, "notifyEvent"))
578   {
579     g_warning ("atspi: TODO: DeviceEvent");
580     //return handle_device_event (bus, message, data);
581   }
582   if (dbus_message_is_signal (message, atspi_interface_cache, "AddAccessible"))
583   {
584     return handle_add_accessible (bus, message, data);
585   }
586   if (dbus_message_is_signal (message, atspi_interface_cache, "RemoveAccessible"))
587   {
588     return handle_remove_accessible (bus, message, data);
589   }
590   /* TODO: Handle ChildrenChanged, StateChanged, PropertyChanged */
591   return DBUS_HANDLER_RESULT_NOT_YET_HANDLED;
592 }
593
594 static const char *signal_interfaces[] =
595 {
596   "org.a11y.atspi.Event.Object",
597   "org.a11y.atspi.Event.Window",
598   "org.a11y.atspi.Event.Mouse",
599   "org.a11y.atspi.Event.Terminal",
600   "org.a11y.atspi.Event.Document",
601   "org.a11y.atspi.Event.Focus",
602   NULL
603 };
604
605 /**
606  * atspi_init:
607  *
608  * Connects to the accessibility registry and initializes the SPI.
609  *
610  * Returns: 0 on success, otherwise an integer error code.  
611  **/
612 int
613 atspi_init (void)
614 {
615   DBusError error;
616   char *match;
617   int i;
618
619   if (atspi_inited)
620     {
621       return 1;
622     }
623
624   atspi_inited = TRUE;
625
626   g_type_init ();
627
628   get_live_refs();
629   g_atexit (cleanup);
630
631   dbus_error_init (&error);
632   bus = dbus_bus_get (DBUS_BUS_SESSION, &error);
633   if (!bus)
634   {
635     g_error ("Couldn't get session bus");
636     return 2;
637   }
638   dbus_bus_register (bus, &error);
639   dbus_connection_setup_with_g_main(bus, g_main_context_default());
640   dbus_connection_add_filter (bus, atspi_dbus_filter, NULL, NULL);
641   match = g_strdup_printf ("type='signal',interface='%s',member='AddAccessible'", atspi_interface_cache);
642   dbus_error_init (&error);
643   dbus_bus_add_match (bus, match, &error);
644   g_free (match);
645   match = g_strdup_printf ("type='signal',interface='%s',member='RemoveAccessible'", atspi_interface_cache);
646   dbus_bus_add_match (bus, match, &error);
647   g_free (match);
648   for (i = 0; signal_interfaces[i]; i++)
649   {
650     match = g_strdup_printf ("type='signal',interface='%s'", signal_interfaces[i]);
651     dbus_bus_add_match (bus, match, &error);
652     g_free (match);
653   }
654   return 0;
655 }
656
657   static GMainLoop *mainloop;
658
659 /**
660  * atspi_event_main:
661  *
662  * Starts/enters the main event loop for the AT-SPI services.
663  *
664  * (NOTE: This method does not return control, it is exited via a call to
665  *  atspi_event_quit () from within an event handler).
666  *
667  **/
668 void
669 atspi_event_main (void)
670 {
671   mainloop = g_main_loop_new (NULL, FALSE);
672   g_main_loop_run (mainloop);
673 }
674
675 /**
676  * atspi_event_quit:
677  *
678  * Quits the last main event loop for the SPI services,
679  * see atspi_event_main
680  **/
681 void
682 atspi_event_quit (void)
683 {
684   g_main_loop_quit (mainloop);
685 }
686
687 /**
688  * atspi_exit:
689  *
690  * Disconnects from the Accessibility Registry and releases 
691  * any floating resources. Call only once at exit.
692  *
693  * Returns: 0 if there were no leaks, otherwise non zero.
694  **/
695 int
696 atspi_exit (void)
697 {
698   int leaked;
699
700   if (!atspi_inited)
701     {
702       return 0;
703     }
704
705   atspi_inited = FALSE;
706
707   if (live_refs)
708     {
709       leaked = g_hash_table_size (live_refs);
710     }
711   else
712     {
713       leaked = 0;
714     }
715
716   cleanup ();
717
718   return leaked;
719 }
720
721 dbus_bool_t
722 _atspi_dbus_call (AtspiAccessible *obj, const char *interface, const char *method, GError **error, const char *type, ...)
723 {
724   va_list args;
725   dbus_bool_t retval;
726   DBusError err;
727
728   dbus_error_init (&err);
729   va_start (args, type);
730   retval = dbind_method_call_reentrant_va (_atspi_bus(), obj->app->bus_name, obj->path, interface, method, &err, type, args);
731   va_end (args);
732   if (dbus_error_is_set (&err))
733   {
734     /* TODO: Set gerror */
735     dbus_error_free (&err);
736   }
737   return retval;
738 }
739
740 DBusMessage *
741 _atspi_dbus_call_partial (AtspiAccessible *obj, const char *interface, const char *method, GError **error, const char *type, ...)
742 {
743   va_list args;
744   dbus_bool_t retval;
745   DBusError err;
746     DBusMessage *msg = NULL, *reply = NULL;
747     DBusMessageIter iter;
748     const char *p;
749
750   dbus_error_init (&err);
751   va_start (args, type);
752
753     msg = dbus_message_new_method_call (obj->app->bus_name, obj->path, interface, method);
754     if (!msg)
755         goto out;
756
757     p = type;
758     dbus_message_iter_init_append (msg, &iter);
759     dbind_any_marshal_va (&iter, &p, args);
760
761     reply = dbind_send_and_allow_reentry (_atspi_bus(), msg, &err);
762 out:
763   va_end (args);
764   if (dbus_error_is_set (&err))
765   {
766     /* TODO: Set gerror */
767     dbus_error_free (&err);
768   }
769   return reply;
770 }
771
772 dbus_bool_t
773 _atspi_dbus_get_property (AtspiAccessible *obj, const char *interface, const char *name, GError **error, const char *type, void *data)
774 {
775   DBusMessage *message, *reply;
776   DBusMessageIter iter, iter_variant;
777   DBusError err;
778   dbus_bool_t retval = FALSE;
779
780   message = dbus_message_new_method_call (obj->app->bus_name, obj->path, "org.freedesktop.DBus.Properties", "Get");
781   if (!message)
782   {
783     // TODO: throw exception
784     goto done;
785   }
786   dbus_message_append_args (message, DBUS_TYPE_STRING, &interface, DBUS_TYPE_STRING, &name, DBUS_TYPE_INVALID);
787   dbus_error_init (&err);
788   reply = dbus_connection_send_with_reply_and_block (_atspi_bus(), message, 1000, &err);
789   dbus_message_unref (message);
790   if (!reply)
791   {
792     // TODO: throw exception
793     goto done;
794   }
795   dbus_message_iter_init (reply, &iter);
796   dbus_message_iter_recurse (&iter, &iter_variant);
797   if (dbus_message_iter_get_arg_type (&iter_variant) != type[0])
798   {
799     g_warning ("atspi_dbus_get_property: Wrong type: expected %s, got %c\n", type, dbus_message_iter_get_arg_type (&iter_variant));
800     goto done;
801   }
802   dbus_message_iter_get_basic (&iter_variant, data);
803   dbus_message_unref (reply);
804   if (type[0] == 's') *(char **)data = g_strdup (*(char **)data);
805   retval = TRUE;
806 done:
807   return retval;
808 }
809
810 DBusMessage *
811 _atspi_dbus_send_with_reply_and_block (DBusMessage *message)
812 {
813   DBusMessage *reply;
814   DBusError err;
815
816   dbus_error_init (&err);
817   g_warning ("TODO: Write _atspi_dbus_send_with_reply_and_block");
818   reply = dbus_connection_send_with_reply_and_block (_atspi_bus(), message, 1000, &err);
819   dbus_message_unref (message);
820   return reply;
821 }
822
823 GHashTable *
824 _atspi_dbus_hash_from_message (DBusMessage *message)
825 {
826   GHashTable *hash = g_hash_table_new (g_str_hash, g_str_equal);
827   DBusMessageIter iter, iter_array, iter_dict;
828   const char *signature;
829
830   signature = dbus_message_get_signature (message);
831
832   if (strcmp (signature, "a{ss}") != 0)
833     {
834       g_warning ("Trying to get hash from message of unexpected type %s\n", signature);
835       return NULL;
836     }
837
838   dbus_message_iter_init (message, &iter);
839   dbus_message_iter_recurse (&iter, &iter_array);
840   while (dbus_message_iter_get_arg_type (&iter_array) != DBUS_TYPE_INVALID)
841   {
842     const char *name, *value;
843     dbus_message_iter_recurse (&iter_array, &iter_dict);
844     dbus_message_iter_get_basic (&iter_dict, &name);
845     dbus_message_iter_get_basic (&iter_dict, &value);
846     g_hash_table_insert (hash, g_strdup (name), g_strdup (value));
847     dbus_message_iter_next (&iter_array);
848   }
849   return hash;
850 }
851
852 GArray *
853 _atspi_dbus_attribute_array_from_message (DBusMessage *message)
854 {
855   GArray *array = g_array_new (TRUE, TRUE, sizeof (gchar *));
856   DBusMessageIter iter, iter_array, iter_dict;
857   const char *signature;
858   gint count = 0;
859
860   signature = dbus_message_get_signature (message);
861
862   if (strcmp (signature, "a{ss}") != 0)
863     {
864       g_warning ("Trying to get hash from message of unexpected type %s\n", signature);
865       return NULL;
866     }
867
868   dbus_message_iter_init (message, &iter);
869
870   dbus_message_iter_recurse (&iter, &iter_array);
871   while (dbus_message_iter_get_arg_type (&iter_array) != DBUS_TYPE_INVALID)
872   {
873     const char *name, *value;
874     gchar *str;
875     GArray *new_array;
876     dbus_message_iter_recurse (&iter_array, &iter_dict);
877     dbus_message_iter_get_basic (&iter_dict, &name);
878     dbus_message_iter_get_basic (&iter_dict, &value);
879     str = g_strdup_printf ("%s:;%s", name, value);
880     new_array = g_array_append_val (array, str);
881     if (new_array)
882       array = new_array;
883     dbus_message_iter_next (&iter);;
884   }
885   return array;
886 }
887