Fix crash if GetChildAtIndex is called with an invalid index
[platform/upstream/at-spi2-core.git] / registryd / registry.c
1 /*
2  * AT-SPI - Assistive Technology Service Provider Interface
3  * (Gnome Accessibility Project; http://developer.gnome.org/projects/gap)
4  *
5  * Copyright 2008, 2010 Codethink Ltd.
6  * Copyright 2001, 2002 Sun Microsystems Inc.,
7  * Copyright 2001, 2002 Ximian, Inc.
8  *
9  * This library is free software; you can redistribute it and/or
10  * modify it under the terms of the GNU Library General Public
11  * License as published by the Free Software Foundation; either
12  * version 2 of the License, or (at your option) any later version.
13  *
14  * This library is distributed in the hope that it will be useful,
15  * but WITHOUT ANY WARRANTY; without even the implied warranty of
16  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
17  * Library General Public License for more details.
18  *
19  * You should have received a copy of the GNU Library General Public
20  * License along with this library; if not, write to the
21  * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
22  * Boston, MA 02111-1307, USA.
23  */
24
25 #include <config.h>
26 #include <string.h>
27 #include <dbus/dbus-glib-lowlevel.h>
28
29 #include "paths.h"
30 #include "registry.h"
31 #include "introspection.h"
32
33 static void
34 children_added_listener (DBusConnection * bus,
35                          gint             index,
36                          const gchar    * name,
37                          const gchar    * path);
38
39 static void
40 children_removed_listener (DBusConnection * bus,
41                            gint             index,
42                            const gchar    * name,
43                            const gchar    * path);
44
45 /*---------------------------------------------------------------------------*/
46
47 typedef struct _SpiReference
48 {
49   gchar *name;
50   gchar *path;
51 } SpiReference;
52
53 static SpiReference *
54 spi_reference_new (const gchar *name, const gchar *path)
55 {
56   SpiReference *ref;
57
58   ref = g_new0 (SpiReference, 1);
59   ref->name = g_strdup (name);
60   ref->path = g_strdup (path);
61
62   return ref;
63 }
64
65 static void
66 spi_reference_free (SpiReference *ref)
67 {
68   g_free (ref->name);
69   g_free (ref->path);
70   g_free (ref);
71 }
72
73 /*---------------------------------------------------------------------------*/
74
75 G_DEFINE_TYPE(SpiRegistry, spi_registry, G_TYPE_OBJECT)
76
77 static void
78 spi_registry_class_init (SpiRegistryClass *klass)
79 {
80   GObjectClass * object_class = (GObjectClass *) klass;
81
82   spi_registry_parent_class = g_type_class_ref (G_TYPE_OBJECT);
83 }
84
85 static void
86 spi_registry_init (SpiRegistry *registry)
87 {
88   registry->apps = g_ptr_array_new_with_free_func ((GDestroyNotify) spi_reference_free);
89 }
90
91 /*---------------------------------------------------------------------------*/
92
93 static dbus_bool_t
94 return_v_string (DBusMessageIter * iter, const gchar * str)
95 {
96   DBusMessageIter variant;
97
98   if (!dbus_message_iter_open_container (iter, DBUS_TYPE_VARIANT, "s",
99                                     &variant))
100     return FALSE;
101     dbus_message_iter_append_basic (&variant, DBUS_TYPE_STRING, &str);
102   dbus_message_iter_close_container (iter, &variant);
103   return TRUE;
104 }
105
106 static dbus_bool_t
107 append_reference (DBusMessageIter * iter, const char * name, const char * path)
108 {
109   DBusMessageIter iter_struct;
110
111   if (!dbus_message_iter_open_container (iter, DBUS_TYPE_STRUCT, NULL,
112                                     &iter_struct))
113     return FALSE;
114   dbus_message_iter_append_basic (&iter_struct, DBUS_TYPE_STRING, &name);
115   dbus_message_iter_append_basic (&iter_struct, DBUS_TYPE_OBJECT_PATH, &path);
116   dbus_message_iter_close_container (iter, &iter_struct);
117   return TRUE;
118 }
119
120 /*---------------------------------------------------------------------------*/
121
122 static gboolean
123 compare_reference (SpiReference *one, SpiReference *two)
124 {
125   if (g_strcmp0 (one->name, two->name) == 0 &&
126       g_strcmp0 (one->path, two->path) == 0)
127     return TRUE;
128   else
129     return FALSE;
130 }
131
132 static gboolean
133 find_index_of_reference (GPtrArray *arr, const gchar *name, const gchar * path, guint *index)
134 {
135   SpiReference *ref;
136   gboolean found = FALSE;
137   guint i = 0;
138
139   ref = spi_reference_new (name, path);
140
141   for (i = 0; i < arr->len && found == FALSE; i++)
142     {
143       if (compare_reference (ref, g_ptr_array_index (arr, i)));
144         {
145           found = TRUE;
146         }
147     }
148
149   spi_reference_free (ref);
150
151   *index = i;
152   return found;
153 }
154
155 static void
156 add_application (SpiRegistry *reg, DBusConnection *bus, const gchar *name, const gchar *path)
157 {
158   g_ptr_array_add (reg->apps, spi_reference_new (name, path));
159   children_added_listener (bus, reg->apps->len - 1, name, path);
160 }
161
162 static void
163 set_id (SpiRegistry *reg, DBusConnection *bus, const gchar *name, const gchar *path)
164 {
165   DBusMessage *message;
166   DBusMessageIter iter, iter_variant;
167   const char *iface_application = "org.a11y.atspi.Application";
168   const char *id = "Id";
169
170   message = dbus_message_new_method_call (name, path,
171                                           DBUS_INTERFACE_PROPERTIES, "Set");
172   if (!message)
173     return;
174   dbus_message_iter_init_append (message, &iter);
175   dbus_message_iter_append_basic (&iter, DBUS_TYPE_STRING, &iface_application);
176   dbus_message_iter_append_basic (&iter, DBUS_TYPE_STRING, &id);
177   dbus_message_iter_open_container (&iter, DBUS_TYPE_VARIANT, "i", &iter_variant);
178   dbus_message_iter_append_basic (&iter_variant, DBUS_TYPE_INT32, &reg->id);
179   /* TODO: This will cause problems if we cycle through 2^31 ids */
180   reg->id++;
181   dbus_message_iter_close_container (&iter, &iter_variant);
182   dbus_connection_send (bus, message, NULL);
183   dbus_message_unref (message);
184 }
185
186 static void
187 remove_application (SpiRegistry *reg, DBusConnection *bus, guint index)
188 {
189   SpiReference *ref = g_ptr_array_index (reg->apps, index);
190
191   /*TODO spi_remove_device_listeners (registry->de_controller, old);*/
192   children_removed_listener (bus, index, ref->name, ref->path);
193   g_ptr_array_remove_index (reg->apps, index);
194 }
195
196 static void
197 handle_disconnection (DBusConnection *bus, DBusMessage *message, void *user_data)
198 {
199   char *name, *old, *new;
200   SpiRegistry *reg = SPI_REGISTRY (user_data);
201
202   if (dbus_message_get_args (message, NULL,
203                              DBUS_TYPE_STRING, &name,
204                              DBUS_TYPE_STRING, &old,
205                              DBUS_TYPE_STRING, &new,
206                              DBUS_TYPE_INVALID))
207     {
208       if (*old != '\0' && *new == '\0')
209         {
210           /* Remove all children with the application name the same as the disconnected application. */
211           guint i;
212           for (i = 0; i < reg->apps->len; i++)
213             {
214               SpiReference *ref  = g_ptr_array_index (reg->apps, i);
215               while (!g_strcmp0 (old, ref->name))
216                 {
217                   children_removed_listener (bus, i, old, ref->path);
218                   g_ptr_array_remove_index (reg->apps, i);
219                 }
220             } 
221         }
222     }
223 }
224
225 static DBusHandlerResult
226 signal_filter (DBusConnection *bus, DBusMessage *message, void *user_data)
227 {
228   SpiRegistry *registry = SPI_REGISTRY (user_data);
229   guint res = DBUS_HANDLER_RESULT_HANDLED;
230   const char *iface = dbus_message_get_interface (message);
231   const char *member = dbus_message_get_member (message);
232
233   if (!g_strcmp0(iface, DBUS_INTERFACE_DBUS) && !g_strcmp0(member, "NameOwnerChanged"))
234       handle_disconnection (bus, message, user_data);
235   else
236       res = DBUS_HANDLER_RESULT_NOT_YET_HANDLED;
237
238   return res;
239 }
240
241 /* org.at_spi.Socket interface */
242 /*---------------------------------------------------------------------------*/
243
244 static DBusMessage*
245 impl_Embed (DBusConnection *bus, DBusMessage *message, void *user_data)
246 {
247   SpiRegistry *reg = SPI_REGISTRY (user_data);
248   DBusMessageIter iter, iter_struct;
249   gchar *app_name, *obj_path;
250
251   DBusMessage *reply = NULL;
252   DBusMessageIter reply_iter;
253
254   dbus_message_iter_init (message, &iter);
255   dbus_message_iter_recurse (&iter, &iter_struct);
256   if (!(dbus_message_iter_get_arg_type (&iter_struct) == DBUS_TYPE_STRING))
257         goto error;
258   dbus_message_iter_get_basic (&iter_struct, &app_name);
259   if (!dbus_message_iter_next (&iter_struct))
260         goto error;
261   if (!(dbus_message_iter_get_arg_type (&iter_struct) == DBUS_TYPE_OBJECT_PATH))
262         goto error;
263   dbus_message_iter_get_basic (&iter_struct, &obj_path);
264
265   add_application(reg, bus, app_name, obj_path);
266
267   set_id (reg, bus, app_name, obj_path);
268
269   reply = dbus_message_new_method_return (message);
270   dbus_message_iter_init_append (reply, &reply_iter);
271   append_reference (&reply_iter, 
272                     dbus_bus_get_unique_name (bus),
273                     SPI_DBUS_PATH_ROOT);
274
275   return reply;
276 error:
277   return dbus_message_new_error (message, DBUS_ERROR_FAILED, "Invalid arguments");
278 }
279
280 static DBusMessage*
281 impl_Unembed (DBusConnection *bus, DBusMessage *message, void *user_data)
282 {
283   SpiRegistry *reg = SPI_REGISTRY (user_data);
284   DBusMessageIter iter, iter_struct;
285   gchar *app_name, *obj_path;
286   guint index;
287
288   dbus_message_iter_init (message, &iter);
289   dbus_message_iter_recurse (&iter, &iter_struct);
290   if (!(dbus_message_iter_get_arg_type (&iter_struct) == DBUS_TYPE_STRING))
291         goto error;
292   dbus_message_iter_get_basic (&iter_struct, &app_name);
293   if (!dbus_message_iter_next (&iter_struct))
294         goto error;
295   if (!(dbus_message_iter_get_arg_type (&iter_struct) == DBUS_TYPE_OBJECT_PATH))
296         goto error;
297   dbus_message_iter_get_basic (&iter_struct, &obj_path);
298
299   if (find_index_of_reference (reg->apps, app_name, obj_path, &index))
300       remove_application(reg, bus, index);
301
302   return NULL;
303 error:
304   return dbus_message_new_error (message, DBUS_ERROR_FAILED, "Invalid arguments");
305 }
306
307 /* org.at_spi.Component interface */
308 /*---------------------------------------------------------------------------*/
309
310 static DBusMessage *
311 impl_Contains (DBusConnection * bus, DBusMessage * message, void *user_data)
312 {
313   dbus_bool_t retval = FALSE;
314   DBusMessage *reply;
315
316   reply = dbus_message_new_method_return (message);
317   dbus_message_append_args (reply, DBUS_TYPE_BOOLEAN, &retval,
318                             DBUS_TYPE_INVALID);
319   return reply;
320 }
321
322 static DBusMessage *
323 impl_GetAccessibleAtPoint (DBusConnection * bus, DBusMessage * message,
324                            void *user_data)
325 {
326   DBusMessage *reply = NULL;
327   DBusMessageIter iter;
328
329   reply = dbus_message_new_method_return (message);
330   dbus_message_iter_init_append (reply, &iter);
331   append_reference (&iter, 
332                     dbus_bus_get_unique_name (bus),
333                     SPI_DBUS_PATH_NULL);
334
335   return reply;
336 }
337
338 static DBusMessage *
339 impl_GetExtents (DBusConnection * bus, DBusMessage * message, void *user_data)
340 {
341   dbus_uint32_t coord_type;
342   dbus_int32_t x = 0, y = 0, width = 1024, height = 768;
343   DBusMessage *reply;
344   DBusMessageIter iter, iter_struct;
345
346   reply = dbus_message_new_method_return (message);
347   dbus_message_iter_init_append (reply, &iter);
348   dbus_message_iter_open_container (&iter, DBUS_TYPE_STRUCT, NULL,
349                                     &iter_struct);
350     dbus_message_iter_append_basic (&iter_struct, DBUS_TYPE_INT32, &x);
351     dbus_message_iter_append_basic (&iter_struct, DBUS_TYPE_INT32, &y);
352     dbus_message_iter_append_basic (&iter_struct, DBUS_TYPE_INT32, &width);
353     dbus_message_iter_append_basic (&iter_struct, DBUS_TYPE_INT32, &height);
354   dbus_message_iter_close_container (&iter, &iter_struct);
355   return reply;
356 }
357
358 static DBusMessage *
359 impl_GetPosition (DBusConnection * bus, DBusMessage * message,
360                   void *user_data)
361 {
362   DBusMessage *reply;
363   dbus_int32_t x = 0, y = 0;
364
365   reply = dbus_message_new_method_return (message);
366   dbus_message_append_args (reply, DBUS_TYPE_INT32, &x, DBUS_TYPE_INT32,
367                             &y, DBUS_TYPE_INVALID);
368   return reply;
369 }
370
371 static DBusMessage *
372 impl_GetSize (DBusConnection * bus, DBusMessage * message, void *user_data)
373 {
374   /* TODO - Get the screen size */
375   DBusMessage *reply;
376   dbus_int32_t width = 1024, height = 768;
377
378   reply = dbus_message_new_method_return (message);
379   dbus_message_append_args (reply, DBUS_TYPE_INT32, &width,
380                             DBUS_TYPE_INT32, &height, DBUS_TYPE_INVALID);
381   return reply;
382 }
383
384 #define LAYER_WIDGET 3;
385
386 static DBusMessage *
387 impl_GetLayer (DBusConnection * bus, DBusMessage * message, void *user_data)
388 {
389   DBusMessage *reply;
390   dbus_uint32_t rv = LAYER_WIDGET;
391
392   reply = dbus_message_new_method_return (message);
393   dbus_message_append_args (reply, DBUS_TYPE_UINT32, &rv,
394                             DBUS_TYPE_INVALID);
395   return reply;
396 }
397
398 static DBusMessage *
399 impl_GetMDIZOrder (DBusConnection * bus, DBusMessage * message,
400                    void *user_data)
401 {
402   DBusMessage *reply;
403   dbus_int16_t rv = 0;
404
405   reply = dbus_message_new_method_return (message);
406   dbus_message_append_args (reply, DBUS_TYPE_INT16, &rv,
407                             DBUS_TYPE_INVALID);
408   return reply;
409 }
410
411 static DBusMessage *
412 impl_GrabFocus (DBusConnection * bus, DBusMessage * message, void *user_data)
413 {
414   DBusMessage *reply;
415   dbus_bool_t retval = FALSE;
416
417   reply = dbus_message_new_method_return (message);
418   dbus_message_append_args (reply, DBUS_TYPE_BOOLEAN, &retval,
419                             DBUS_TYPE_INVALID);
420   return reply;
421 }
422
423 static DBusMessage *
424 impl_GetAlpha (DBusConnection * bus, DBusMessage * message, void *user_data)
425 {
426   double rv = 1.0;
427   DBusMessage *reply;
428
429   reply = dbus_message_new_method_return (message);
430   dbus_message_append_args (reply, DBUS_TYPE_DOUBLE, &rv,
431                             DBUS_TYPE_INVALID);
432   return reply;
433 }
434
435 /* org.at_spi.Accessible interface */
436 /*---------------------------------------------------------------------------*/
437
438 static dbus_bool_t
439 impl_get_Name (DBusMessageIter * iter, void *user_data)
440 {
441   const gchar *name = "main";
442   return return_v_string (iter, name);
443 }
444
445 static dbus_bool_t
446 impl_get_Description (DBusMessageIter * iter, void *user_data)
447 {
448   const gchar *description = "";
449   return return_v_string (iter, description);
450 }
451
452 static dbus_bool_t
453 impl_get_Parent (DBusMessageIter * iter, void *user_data)
454 {
455   const gchar *name = "";
456   DBusMessageIter iter_variant;
457
458   dbus_message_iter_open_container (iter, DBUS_TYPE_VARIANT, "(so)",
459                                     &iter_variant);
460   append_reference (&iter_variant, 
461                     name,
462                     SPI_DBUS_PATH_NULL);
463   dbus_message_iter_close_container (iter, &iter_variant);
464   return TRUE;
465 }
466
467 static dbus_bool_t
468 impl_get_ChildCount (DBusMessageIter * iter, void *user_data)
469 {
470   SpiRegistry *reg = SPI_REGISTRY (user_data);
471   dbus_int32_t rv = reg->apps->len;
472
473   return dbus_message_iter_append_basic (iter, DBUS_TYPE_INT32, &rv);
474 }
475
476 static DBusMessage *
477 impl_GetChildAtIndex (DBusConnection * bus,
478                       DBusMessage * message, void *user_data)
479 {
480   SpiRegistry *reg = SPI_REGISTRY (user_data);
481   DBusMessage *reply;
482   DBusMessageIter iter;
483   DBusError error;
484   SpiReference *ref;
485   dbus_int32_t i;
486
487   dbus_error_init (&error);
488   if (!dbus_message_get_args 
489        (message, &error, DBUS_TYPE_INT32, &i, DBUS_TYPE_INVALID))
490     {
491       return dbus_message_new_error (message, DBUS_ERROR_FAILED, "Invalid arguments");
492     }
493
494   reply = dbus_message_new_method_return (message);
495   dbus_message_iter_init_append (reply, &iter);
496
497   if (i < 0 || i >= reg->apps->len)
498     append_reference (&iter, SPI_DBUS_NAME_REGISTRY, SPI_DBUS_PATH_NULL);
499   else
500     {
501       ref = g_ptr_array_index (reg->apps, i);
502       append_reference (&iter, ref->name, ref->path);
503     }
504
505   return reply;
506 }
507
508 static DBusMessage *
509 impl_GetChildren (DBusConnection * bus,
510                   DBusMessage * message, void *user_data)
511 {
512   DBusMessage *reply = NULL;
513   DBusMessageIter iter, iter_array;
514   SpiRegistry *reg = SPI_REGISTRY (user_data);
515   int i;
516
517   reply = dbus_message_new_method_return (message);
518
519   dbus_message_iter_init_append (reply, &iter);
520   dbus_message_iter_open_container(&iter, DBUS_TYPE_ARRAY, "(so)", &iter_array);
521   for (i=0; i < reg->apps->len; i++)
522     {
523       SpiReference *current = g_ptr_array_index (reg->apps, i);
524       append_reference (&iter_array, current->name, current->path);
525     }
526   dbus_message_iter_close_container(&iter, &iter_array);
527   return reply;
528 }
529
530 static DBusMessage *
531 impl_GetIndexInParent (DBusConnection * bus,
532                        DBusMessage * message, void *user_data)
533 {
534   DBusMessage *reply;
535   dbus_uint32_t rv = 0;
536
537   reply = dbus_message_new_method_return (message);
538   dbus_message_append_args (reply, DBUS_TYPE_UINT32, &rv, DBUS_TYPE_INVALID);
539   return reply;
540 }
541
542 static DBusMessage *
543 impl_GetRelationSet (DBusConnection * bus,
544                      DBusMessage * message, void *user_data)
545 {
546   DBusMessage *reply;
547   DBusMessageIter iter, iter_array;
548
549   reply = dbus_message_new_method_return (message);
550   dbus_message_iter_init_append (reply, &iter);
551   dbus_message_iter_open_container (&iter, DBUS_TYPE_ARRAY, "(ua(so))", &iter_array);
552   dbus_message_iter_close_container (&iter, &iter_array);
553
554   return reply;
555 }
556
557 static DBusMessage *
558 impl_GetRole (DBusConnection * bus, DBusMessage * message, void * user_data)
559 {
560   DBusMessage *reply;
561   dbus_uint32_t rv = 0;
562
563   reply = dbus_message_new_method_return (message);
564   dbus_message_append_args (reply, DBUS_TYPE_UINT32, &rv, DBUS_TYPE_INVALID);
565   return reply;
566 }
567
568 static DBusMessage *
569 impl_GetRoleName (DBusConnection * bus,
570                   DBusMessage * message, void *user_data)
571 {
572   DBusMessage *reply;
573   const char *role_name = "unknown";
574
575   reply = dbus_message_new_method_return (message);
576   dbus_message_append_args (reply, DBUS_TYPE_STRING, &role_name,
577                             DBUS_TYPE_INVALID);
578   return reply;
579 }
580
581 static DBusMessage *
582 impl_GetLocalizedRoleName (DBusConnection * bus,
583                            DBusMessage * message, void *user_data)
584 {
585   /* TODO - Localize this */
586   DBusMessage *reply;
587   const char *role_name = "unknown";
588
589   reply = dbus_message_new_method_return (message);
590   dbus_message_append_args (reply, DBUS_TYPE_STRING, &role_name,
591                             DBUS_TYPE_INVALID);
592   return reply;
593 }
594
595 static DBusMessage *
596 impl_GetState (DBusConnection * bus, DBusMessage * message, void *user_data)
597 {
598   DBusMessage *reply = NULL;
599   DBusMessageIter iter, iter_array;
600
601   dbus_uint32_t states[2] = {0, 0};
602   guint count;
603
604   reply = dbus_message_new_method_return (message);
605   dbus_message_iter_init_append (reply, &iter);
606
607   dbus_message_iter_open_container (&iter, DBUS_TYPE_ARRAY, "u", &iter_array);
608   for (count = 0; count < 2; count++)
609     {
610       dbus_message_iter_append_basic (&iter_array, DBUS_TYPE_UINT32,
611                                       &states[count]);
612     }
613   dbus_message_iter_close_container (&iter, &iter_array);
614   return reply;
615 }
616
617 static DBusMessage *
618 impl_GetAttributes (DBusConnection * bus,
619                     DBusMessage * message, void *user_data)
620 {
621   DBusMessage *reply = NULL;
622   DBusMessageIter iter, array;
623
624   reply = dbus_message_new_method_return (message);
625   dbus_message_iter_init_append (reply, &iter);
626   dbus_message_iter_open_container (&iter, DBUS_TYPE_ARRAY, "{ss}", &array);
627   dbus_message_iter_close_container (&iter, &array);
628
629   return reply;
630 }
631
632 static DBusMessage *
633 impl_GetApplication (DBusConnection * bus,
634                      DBusMessage * message, void *user_data)
635 {
636   DBusMessage *reply = NULL;
637   DBusMessageIter iter;
638
639   reply = dbus_message_new_method_return (message);
640   dbus_message_iter_init_append (reply, &iter);
641   append_reference (&iter,
642                     dbus_bus_get_unique_name (bus),
643                     SPI_DBUS_PATH_NULL);
644
645   return reply;
646 }
647
648 static DBusMessage *
649 impl_GetInterfaces (DBusConnection * bus,
650                     DBusMessage * message, void *user_data)
651 {
652   DBusMessage *reply;
653   DBusMessageIter iter, iter_array;
654
655   const char *acc = SPI_DBUS_INTERFACE_ACCESSIBLE;
656   const char *com = SPI_DBUS_INTERFACE_COMPONENT;
657
658   reply = dbus_message_new_method_return (message);
659
660   dbus_message_iter_init_append (reply, &iter);
661   dbus_message_iter_open_container (&iter, DBUS_TYPE_ARRAY, "s",
662                                     &iter_array);
663     dbus_message_iter_append_basic (&iter_array, DBUS_TYPE_STRING, &acc);
664     dbus_message_iter_append_basic (&iter_array, DBUS_TYPE_STRING, &com);
665   dbus_message_iter_close_container (&iter, &iter_array);
666
667   return reply;
668 }
669
670 /*---------------------------------------------------------------------------*/
671
672 static void 
673 emit_Available (DBusConnection * bus)
674 {
675   DBusMessage *sig;
676   DBusMessageIter iter;
677   
678   sig = dbus_message_new_signal(SPI_DBUS_PATH_ROOT, SPI_DBUS_INTERFACE_SOCKET, "Available");
679
680   dbus_message_iter_init_append(sig, &iter);
681   append_reference (&iter, SPI_DBUS_NAME_REGISTRY, SPI_DBUS_PATH_ROOT);
682
683   dbus_connection_send(bus, sig, NULL);
684   dbus_message_unref(sig);
685 }
686
687 /*---------------------------------------------------------------------------*/
688
689 static const char *introspection_header =
690 "<?xml version=\"1.0\"?>\n";
691
692 static const char *introspection_node_element =
693 "<node name=\"%s\">\n";
694
695 static const char *introspection_footer =
696 "</node>";
697
698 static DBusMessage *
699 impl_Introspect (DBusConnection * bus,
700                  DBusMessage * message, void *user_data)
701 {
702   GString *output;
703   gchar *final;
704   gint i;
705
706   const gchar *pathstr = SPI_DBUS_PATH_ROOT;
707
708   DBusMessage *reply;
709
710   output = g_string_new(introspection_header);
711
712   g_string_append_printf(output, introspection_node_element, pathstr);
713
714   g_string_append (output, spi_org_a11y_atspi_Accessible);
715   g_string_append (output, spi_org_a11y_atspi_Component);
716
717   g_string_append(output, introspection_footer);
718   final = g_string_free(output, FALSE);
719
720   reply = dbus_message_new_method_return (message);
721   dbus_message_append_args(reply, DBUS_TYPE_STRING, &final, DBUS_TYPE_INVALID);
722
723   g_free(final);
724   return reply;
725 }
726
727 /*---------------------------------------------------------------------------*/
728
729 /*
730  * Emits an AT-SPI event.
731  * AT-SPI events names are split into three parts:
732  * class:major:minor
733  * This is mapped onto D-Bus events as:
734  * D-Bus Interface:Signal Name:Detail argument
735  *
736  * Marshals a basic type into the 'any_data' attribute of
737  * the AT-SPI event.
738  */
739 static void 
740 emit_event (DBusConnection *bus,
741             const char *klass,
742             const char *major,
743             const char *minor,
744             dbus_int32_t detail1,
745             dbus_int32_t detail2,
746             const char *name,
747             const char *path)
748 {
749   DBusMessage *sig;
750   DBusMessageIter iter, iter_variant;
751   
752   sig = dbus_message_new_signal(SPI_DBUS_PATH_ROOT, klass, major);
753
754   dbus_message_iter_init_append(sig, &iter);
755
756   dbus_message_iter_append_basic(&iter, DBUS_TYPE_STRING, &minor);
757   dbus_message_iter_append_basic(&iter, DBUS_TYPE_INT32, &detail1);
758   dbus_message_iter_append_basic(&iter, DBUS_TYPE_INT32, &detail2);
759
760   dbus_message_iter_open_container (&iter, DBUS_TYPE_VARIANT, "(so)",
761                                     &iter_variant);
762     append_reference (&iter_variant, name, path);
763   dbus_message_iter_close_container (&iter, &iter_variant);
764
765   append_reference (&iter,
766                     dbus_bus_get_unique_name (bus),
767                     SPI_DBUS_PATH_ROOT);
768
769   dbus_connection_send(bus, sig, NULL);
770   dbus_message_unref(sig);
771 }
772
773 /*---------------------------------------------------------------------------*/
774
775 /*
776  * Children changed signal converter and forwarder.
777  *
778  * Klass (Interface) org.a11y.atspi.Event.Object
779  * Major is the signal name.
780  * Minor is 'add' or 'remove'
781  * detail1 is the index.
782  * detail2 is 0.
783  * any_data is the child reference.
784  */
785
786 static void
787 children_added_listener (DBusConnection * bus,
788                          gint             index,
789                          const gchar    * name,
790                          const gchar    * path)
791 {
792   emit_event (bus, SPI_DBUS_INTERFACE_EVENT_OBJECT, "ChildrenChanged", "add", index, 0,
793               name, path);
794 }
795
796 static void
797 children_removed_listener (DBusConnection * bus,
798                            gint             index,
799                            const gchar    * name,
800                            const gchar    * path)
801 {
802   emit_event (bus, SPI_DBUS_INTERFACE_EVENT_OBJECT, "ChildrenChanged", "remove", index, 0,
803               name, path);
804 }
805
806 /*---------------------------------------------------------------------------*/
807
808 static DBusHandlerResult
809 handle_method (DBusConnection *bus, DBusMessage *message, void *user_data)
810 {
811   DBusHandlerResult result = DBUS_HANDLER_RESULT_NOT_YET_HANDLED;
812
813   const gchar *iface   = dbus_message_get_interface (message);
814   const gchar *member  = dbus_message_get_member (message);
815   const gint   type    = dbus_message_get_type (message);
816
817   DBusMessage *reply = NULL;
818
819   /* Check for basic reasons not to handle */
820   if (type   != DBUS_MESSAGE_TYPE_METHOD_CALL ||
821       member == NULL ||
822       iface  == NULL)
823       return DBUS_HANDLER_RESULT_NOT_YET_HANDLED;
824
825   if (!strcmp (iface, "org.freedesktop.DBus.Properties"))
826     {
827       if (!strcmp (member, "Get"))
828         {
829           const gchar *prop_iface;
830           const gchar *prop_member;
831           DBusError error;
832
833           dbus_error_init (&error);
834           if (dbus_message_get_args (message,
835                                      &error,
836                                      DBUS_TYPE_STRING,
837                                      &prop_iface,
838                                      DBUS_TYPE_STRING,
839                                      &prop_member,
840                                      DBUS_TYPE_INVALID))
841             {
842               DBusMessageIter iter;
843
844               reply = dbus_message_new_method_return (message);
845               dbus_message_iter_init_append (reply, &iter);
846
847
848               if (!strcmp (prop_iface, SPI_DBUS_INTERFACE_ACCESSIBLE))
849                 {
850                   if      (!strcmp (prop_member, "Name"))
851                     impl_get_Name (&iter, user_data);
852                   else if (!strcmp (prop_member, "Description"))
853                     impl_get_Description (&iter, user_data);
854                   else if (!strcmp (prop_member, "Parent"))
855                     impl_get_Parent (&iter, user_data);
856                   else if (!strcmp (prop_member, "ChildCount"))
857                     impl_get_ChildCount (&iter, user_data);
858                   else
859                     {
860                       dbus_message_unref (reply); 
861                       reply = dbus_message_new_error (message, DBUS_ERROR_FAILED, "Property unavailable");
862                     }
863                 }
864             }
865           else
866             {
867               reply = dbus_message_new_error (message, DBUS_ERROR_FAILED, error.message);
868             }
869           result = DBUS_HANDLER_RESULT_HANDLED;
870         }
871       else if (!strcmp (member, "GetAll"))
872         {
873           result = DBUS_HANDLER_RESULT_HANDLED;
874         }
875     }
876
877   if (!strcmp (iface, SPI_DBUS_INTERFACE_ACCESSIBLE))
878     {
879       result = DBUS_HANDLER_RESULT_HANDLED;
880       if      (!strcmp (member, "GetChildAtIndex"))
881           reply = impl_GetChildAtIndex (bus, message, user_data);
882       else if (!strcmp (member, "GetChildren"))
883           reply = impl_GetChildren (bus, message, user_data);
884       else if (!strcmp (member, "GetIndexInParent"))
885           reply = impl_GetIndexInParent (bus, message, user_data);
886       else if (!strcmp (member, "GetRelationSet"))
887           reply = impl_GetRelationSet (bus, message, user_data);
888       else if (!strcmp (member, "GetRole"))
889           reply = impl_GetRole (bus, message, user_data);
890       else if (!strcmp (member, "GetRoleName"))
891           reply = impl_GetRoleName (bus, message, user_data);
892       else if (!strcmp (member, "GetLocalizedRoleName"))
893           reply = impl_GetLocalizedRoleName (bus, message, user_data);
894       else if (!strcmp (member, "GetState"))
895           reply = impl_GetState (bus, message, user_data);
896       else if (!strcmp (member, "GetAttributes"))
897           reply = impl_GetAttributes (bus, message, user_data);
898       else if (!strcmp (member, "GetApplication"))
899           reply = impl_GetApplication (bus, message, user_data);
900       else if (!strcmp (member, "GetInterfaces"))
901           reply = impl_GetInterfaces (bus, message, user_data);
902       else
903          result = DBUS_HANDLER_RESULT_NOT_YET_HANDLED;
904     }
905
906   if (!strcmp (iface, SPI_DBUS_INTERFACE_COMPONENT))
907     {
908       result = DBUS_HANDLER_RESULT_HANDLED;
909       if      (!strcmp (member, "Contains"))
910           reply = impl_Contains (bus, message, user_data);
911       else if (!strcmp (member, "GetAccessibleAtPoint"))
912           reply = impl_GetAccessibleAtPoint (bus, message, user_data);
913       else if (!strcmp (member, "GetExtents"))
914           reply = impl_GetExtents (bus, message, user_data);
915       else if (!strcmp (member, "GetPosition"))
916           reply = impl_GetPosition (bus, message, user_data);
917       else if (!strcmp (member, "GetSize"))
918           reply = impl_GetSize (bus, message, user_data);
919       else if (!strcmp (member, "GetLayer"))
920           reply = impl_GetLayer (bus, message, user_data);
921       else if (!strcmp (member, "GetMDIZOrder"))
922           reply = impl_GetMDIZOrder (bus, message, user_data);
923       else if (!strcmp (member, "GrabFocus"))
924           reply = impl_GrabFocus (bus, message, user_data);
925       else if (!strcmp (member, "GetAlpha"))
926           reply = impl_GetAlpha (bus, message, user_data);
927       else
928          result = DBUS_HANDLER_RESULT_NOT_YET_HANDLED;
929     }
930
931   if (!strcmp (iface, SPI_DBUS_INTERFACE_SOCKET))
932     {
933       result = DBUS_HANDLER_RESULT_HANDLED;
934       if      (!strcmp (member, "Embed"))
935           reply = impl_Embed (bus, message, user_data);
936       else if (!strcmp (member, "Unembed"))
937           reply = impl_Unembed (bus, message, user_data);
938       else
939           result = DBUS_HANDLER_RESULT_NOT_YET_HANDLED;
940     }
941
942   if (!strcmp (iface, "org.freedesktop.DBus.Introspectable"))
943     {
944       result = DBUS_HANDLER_RESULT_HANDLED;
945       if      (!strcmp (member, "Introspect"))
946           reply = impl_Introspect (bus, message, user_data);
947       else
948           result = DBUS_HANDLER_RESULT_NOT_YET_HANDLED;
949     }
950
951   if (result == DBUS_HANDLER_RESULT_HANDLED)
952     {
953       if (!reply)
954         {
955           reply = dbus_message_new_method_return (message);
956         }
957
958       dbus_connection_send (bus, reply, NULL);
959       dbus_message_unref (reply);
960     }
961 #if 0
962   else
963     {
964       g_print ("Registry | Unhandled message : %s|%s\n", iface, member);
965     }
966 #endif
967   
968   return result;
969 }
970
971 /*---------------------------------------------------------------------------*/
972
973 static DBusObjectPathVTable registry_vtable =
974 {
975   NULL,
976   &handle_method,
977   NULL, NULL, NULL, NULL
978 };
979
980 static gchar *app_sig_match_name_owner =
981        "type='signal', interface='org.freedesktop.DBus', member='NameOwnerChanged'";
982
983 SpiRegistry *
984 spi_registry_new (DBusConnection *bus)
985 {
986   SpiRegistry *reg = g_object_new (SPI_REGISTRY_TYPE, NULL);
987
988   reg->bus = bus;
989
990   dbus_bus_add_match (bus, app_sig_match_name_owner, NULL);
991   dbus_connection_add_filter (bus, signal_filter, reg, NULL);
992
993   dbus_connection_register_object_path (bus, SPI_DBUS_PATH_ROOT, &registry_vtable, reg);
994
995   emit_Available (bus);
996
997   return reg;
998 }
999
1000 /*END------------------------------------------------------------------------*/