Add missing standard header / fix prototype
[platform/core/uifw/at-spi2-atk.git] / droute / droute.c
1 /*
2  * AT-SPI - Assistive Technology Service Provider Interface
3  * (Gnome Accessibility Project; http://developer.gnome.org/projects/gap)
4  *
5  * Copyright 2008 Novell, Inc.
6  * Copyright 2008 Codethink Ltd.
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 #include <stdlib.h>
25 #include <string.h>
26 #include <stdio.h>
27
28 #include "droute.h"
29 #include "droute-pairhash.h"
30
31 #define CHUNKS_DEFAULT (512)
32
33 #define oom() g_error ("D-Bus out of memory, this message will fail anyway")
34
35 #if defined DROUTE_DEBUG
36     #define _DROUTE_DEBUG(format, args...) g_print (format , ## args)
37 #else
38     #define _DROUTE_DEBUG(format, args...)
39 #endif
40
41 struct _DRouteContext
42 {
43     GPtrArray            *registered_paths;
44
45     gchar                *introspect_string;
46 };
47
48 struct _DRoutePath
49 {
50     DRouteContext        *cnx;
51     gchar *path;
52     gboolean prefix;
53     GStringChunk         *chunks;
54     GPtrArray            *interfaces;
55     GPtrArray            *introspection;
56     GHashTable           *methods;
57     GHashTable           *properties;
58
59     DRouteIntrospectChildrenFunction introspect_children_cb;
60     void *introspect_children_data;
61     void                   *user_data;
62     DRouteGetDatumFunction  get_datum;
63 };
64
65 /*---------------------------------------------------------------------------*/
66
67 typedef struct PropertyPair
68 {
69     DRoutePropertyFunction get;
70     DRoutePropertyFunction set;
71 } PropertyPair;
72
73 /*---------------------------------------------------------------------------*/
74
75 static DBusHandlerResult
76 handle_message (DBusConnection *bus, DBusMessage *message, void *user_data);
77
78 static DBusMessage *
79 droute_object_does_not_exist_error (DBusMessage *message);
80
81 /*---------------------------------------------------------------------------*/
82
83 static DRoutePath *
84 path_new (DRouteContext *cnx,
85           const char *path,
86           gboolean prefix,
87           void    *user_data,
88           DRouteIntrospectChildrenFunction introspect_children_cb,
89           void *introspect_children_data,
90           DRouteGetDatumFunction get_datum)
91 {
92     DRoutePath *new_path;
93
94     new_path = g_new0 (DRoutePath, 1);
95     new_path->cnx = cnx;
96     new_path->path = g_strdup (path);
97     new_path->prefix = prefix;
98     new_path->chunks = g_string_chunk_new (CHUNKS_DEFAULT);
99     new_path->interfaces = g_ptr_array_new ();
100     new_path->introspection = g_ptr_array_new ();
101
102     new_path->methods = g_hash_table_new_full ((GHashFunc)str_pair_hash,
103                                                str_pair_equal,
104                                                g_free,
105                                                NULL);
106
107     new_path->properties = g_hash_table_new_full ((GHashFunc)str_pair_hash,
108                                                   str_pair_equal,
109                                                   g_free,
110                                                   NULL);
111
112     new_path->introspect_children_cb = introspect_children_cb;
113     new_path->introspect_children_data = introspect_children_data;
114     new_path->user_data = user_data;
115     new_path->get_datum = get_datum;
116
117     return new_path;
118 }
119
120 static void
121 path_free (DRoutePath *path, gpointer user_data)
122 {
123     g_free (path->path);
124     g_string_chunk_free  (path->chunks);
125     g_ptr_array_free     (path->interfaces, TRUE);
126     g_ptr_array_free     (path->introspection, FALSE);
127     g_hash_table_destroy (path->methods);
128     g_hash_table_destroy (path->properties);
129 }
130
131 static void *
132 path_get_datum (DRoutePath *path, const gchar *pathstr)
133 {
134     if (path->get_datum != NULL)
135         return (path->get_datum) (pathstr, path->user_data);
136     else
137         return path->user_data;
138 }
139
140 /*---------------------------------------------------------------------------*/
141
142 DRouteContext *
143 droute_new ()
144 {
145     DRouteContext *cnx;
146
147     cnx = g_new0 (DRouteContext, 1);
148     cnx->registered_paths = g_ptr_array_new ();
149
150     return cnx;
151 }
152
153 void
154 droute_free (DRouteContext *cnx)
155 {
156     g_ptr_array_foreach (cnx->registered_paths, (GFunc) path_free, NULL);
157     g_free (cnx);
158 }
159
160 /*---------------------------------------------------------------------------*/
161
162 /*---------------------------------------------------------------------------*/
163
164 static DBusObjectPathVTable droute_vtable =
165 {
166   NULL,
167   &handle_message,
168   NULL, NULL, NULL, NULL
169 };
170
171 DRoutePath *
172 droute_add_one (DRouteContext *cnx,
173                 const char    *path,
174                 const void    *data)
175 {
176     DRoutePath *new_path;
177     gboolean registered;
178
179     new_path = path_new (cnx, path, FALSE, (void *)data, NULL, NULL, NULL);
180
181     g_ptr_array_add (cnx->registered_paths, new_path);
182     return new_path;
183 }
184
185 DRoutePath *
186 droute_add_many (DRouteContext *cnx,
187                  const char    *path,
188                  const void    *data,
189                  DRouteIntrospectChildrenFunction introspect_children_cb,
190                  void *introspect_children_data,
191                  const DRouteGetDatumFunction get_datum)
192 {
193     DRoutePath *new_path;
194
195     new_path = path_new (cnx, path, TRUE, (void *) data,
196                          introspect_children_cb, introspect_children_data,
197                          get_datum);
198
199     g_ptr_array_add (cnx->registered_paths, new_path);
200     return new_path;
201 }
202
203 /*---------------------------------------------------------------------------*/
204
205 void
206 droute_path_add_interface(DRoutePath *path,
207                           const char *name,
208                           const char *introspect,
209                           const DRouteMethod   *methods,
210                           const DRouteProperty *properties)
211 {
212     gchar *itf;
213
214     g_return_if_fail (name != NULL);
215
216     itf = g_string_chunk_insert (path->chunks, name);
217     g_ptr_array_add (path->interfaces, itf);
218     g_ptr_array_add (path->introspection, introspect);
219
220     for (; methods != NULL && methods->name != NULL; methods++)
221       {
222         gchar *meth;
223
224         meth = g_string_chunk_insert (path->chunks, methods->name);
225         g_hash_table_insert (path->methods, str_pair_new (itf, meth), methods->func);
226       }
227
228     for (; properties != NULL && properties->name != NULL; properties++)
229       {
230         gchar *prop;
231         PropertyPair *pair;
232
233         prop = g_string_chunk_insert (path->chunks, properties->name);
234         pair = g_new (PropertyPair, 1);
235         pair->get = properties->get;
236         pair->set = properties->set;
237         g_hash_table_insert (path->properties, str_pair_new (itf, prop), pair);
238       }
239 }
240
241 /*---------------------------------------------------------------------------*/
242
243 /* The data structures don't support an efficient implementation of GetAll
244  * and I don't really care.
245  */
246 static DBusMessage *
247 impl_prop_GetAll (DBusMessage *message,
248                   DRoutePath  *path,
249                   const char  *pathstr)
250 {
251     DBusMessageIter iter, iter_dict, iter_dict_entry;
252     DBusMessage *reply;
253     DBusError error;
254     GHashTableIter prop_iter;
255
256     StrPair *key;
257     PropertyPair *value;
258     gchar *iface;
259
260     void  *datum = path_get_datum (path, pathstr);
261     if (!datum)
262         return droute_object_does_not_exist_error (message);
263
264     dbus_error_init (&error);
265     if (!dbus_message_get_args
266                 (message, &error, DBUS_TYPE_STRING, &iface, DBUS_TYPE_INVALID))
267         return dbus_message_new_error (message, DBUS_ERROR_FAILED, error.message);
268
269     reply = dbus_message_new_method_return (message);
270     if (!reply)
271         oom ();
272
273     dbus_message_iter_init_append (reply, &iter);
274     if (!dbus_message_iter_open_container
275                 (&iter, DBUS_TYPE_ARRAY, "{sv}", &iter_dict))
276         oom ();
277
278     g_hash_table_iter_init (&prop_iter, path->properties);
279     while (g_hash_table_iter_next (&prop_iter, (gpointer*)&key, (gpointer*)&value))
280       {
281         if (!g_strcmp0 (key->one, iface))
282          {
283            if (!value->get)
284               continue;
285            if (!dbus_message_iter_open_container
286                         (&iter_dict, DBUS_TYPE_DICT_ENTRY, NULL, &iter_dict_entry))
287               oom ();
288            dbus_message_iter_append_basic (&iter_dict_entry, DBUS_TYPE_STRING,
289                                            &key->two);
290            (value->get) (&iter_dict_entry, datum);
291            if (!dbus_message_iter_close_container (&iter_dict, &iter_dict_entry))
292                oom ();
293          }
294       }
295
296     if (!dbus_message_iter_close_container (&iter, &iter_dict))
297         oom ();
298     return reply;
299 }
300
301 static DBusMessage *
302 impl_prop_GetSet (DBusMessage *message,
303                   DRoutePath  *path,
304                   const char  *pathstr,
305                   gboolean     get)
306 {
307     DBusMessage *reply = NULL;
308     DBusError error;
309
310     StrPair pair;
311     PropertyPair *prop_funcs = NULL;
312
313     void *datum;
314
315     dbus_error_init (&error);
316     if (!dbus_message_get_args (message,
317                                 &error,
318                                 DBUS_TYPE_STRING,
319                                 &(pair.one),
320                                 DBUS_TYPE_STRING,
321                                 &(pair.two),
322                                 DBUS_TYPE_INVALID))
323         return dbus_message_new_error (message, DBUS_ERROR_FAILED, error.message);
324
325     _DROUTE_DEBUG ("DRoute (handle prop): %s|%s on %s\n", pair.one, pair.two, pathstr);
326
327     prop_funcs = (PropertyPair *) g_hash_table_lookup (path->properties, &pair);
328     if (!prop_funcs)
329         return dbus_message_new_error (message, DBUS_ERROR_FAILED, "Property unavailable");
330
331     datum = path_get_datum (path, pathstr);
332     if (!datum)
333         return droute_object_does_not_exist_error (message);
334
335     if (get && prop_funcs->get)
336       {
337         
338         DBusMessageIter iter;
339
340         _DROUTE_DEBUG ("DRoute (handle prop Get): %s|%s on %s\n", pair.one, pair.two, pathstr);
341
342         reply = dbus_message_new_method_return (message);
343         dbus_message_iter_init_append (reply, &iter);
344         if (!(prop_funcs->get) (&iter, datum))
345           {
346             dbus_message_unref (reply);
347             reply = dbus_message_new_error (message, DBUS_ERROR_FAILED, "Get failed");
348           }
349       }
350     else if (!get && prop_funcs->set)
351       {
352         DBusMessageIter iter;
353
354         _DROUTE_DEBUG ("DRoute (handle prop Get): %s|%s on %s\n", pair.one, pair.two, pathstr);
355
356         dbus_message_iter_init (message, &iter);
357         /* Skip the interface and property name */
358         dbus_message_iter_next(&iter);
359         dbus_message_iter_next(&iter);
360         (prop_funcs->set) (&iter, datum);
361
362         reply = dbus_message_new_method_return (message);
363       }
364     else
365       {
366         reply = dbus_message_new_error (message, DBUS_ERROR_FAILED, "Getter or setter unavailable");
367       }
368
369     return reply;
370 }
371
372 static DBusHandlerResult
373 handle_dbus (DBusConnection *bus,
374                    DBusMessage    *message,
375                    const gchar    *iface,
376                    const gchar    *member,
377                    const gchar    *pathstr)
378 {
379   static int id = 1;
380   char *id_str = (char *) g_malloc(40);
381   DBusMessage *reply;
382
383     if (strcmp (iface, DBUS_INTERFACE_DBUS) != 0 ||
384         strcmp (member, "Hello") != 0)
385     return DBUS_HANDLER_RESULT_NOT_YET_HANDLED;
386
387     /* TODO: Fix this hack (we don't handle wrap-around, for instance) */
388     sprintf (id_str, ":1.%d", id++);
389     reply = dbus_message_new_method_return (message);
390     dbus_message_append_args (reply, DBUS_TYPE_STRING, &id_str, DBUS_TYPE_INVALID);
391     dbus_connection_send (bus, reply, NULL);
392   dbus_connection_flush (bus);
393     dbus_message_unref (reply);
394   g_free (id_str);
395     return DBUS_HANDLER_RESULT_HANDLED;
396 }
397
398 static DBusHandlerResult
399 handle_properties (DBusConnection *bus,
400                    DBusMessage    *message,
401                    DRoutePath     *path,
402                    const gchar    *iface,
403                    const gchar    *member,
404                    const gchar    *pathstr)
405 {
406     DBusMessage *reply;
407     DBusHandlerResult result = DBUS_HANDLER_RESULT_HANDLED;
408
409     if (!g_strcmp0(member, "GetAll"))
410        reply = impl_prop_GetAll (message, path, pathstr);
411     else if (!g_strcmp0 (member, "Get"))
412        reply = impl_prop_GetSet (message, path, pathstr, TRUE);
413     else if (!g_strcmp0 (member, "Set"))
414        reply = impl_prop_GetSet (message, path, pathstr, FALSE);
415     else
416        result = DBUS_HANDLER_RESULT_NOT_YET_HANDLED;
417
418     if (reply)
419       {
420         dbus_connection_send (bus, reply, NULL);
421         dbus_message_unref (reply);
422       }
423
424     return result;
425 }
426
427 /*---------------------------------------------------------------------------*/
428
429 static const char *introspection_header =
430 "<?xml version=\"1.0\"?>\n";
431
432 static const char *introspection_node_element =
433 "<node name=\"%s\">\n";
434
435 static const char *introspection_footer =
436 "</node>";
437
438 static DBusHandlerResult
439 handle_introspection (DBusConnection *bus,
440                       DBusMessage    *message,
441                       DRoutePath     *path,
442                       const gchar    *iface,
443                       const gchar    *member,
444                       const gchar    *pathstr)
445 {
446     GString *output;
447     gchar *final;
448     gint i;
449
450     DBusMessage *reply;
451
452     _DROUTE_DEBUG ("DRoute (handle introspection): %s\n", pathstr);
453
454     if (g_strcmp0 (member, "Introspect"))
455         return DBUS_HANDLER_RESULT_NOT_YET_HANDLED;
456
457     output = g_string_new(introspection_header);
458
459     g_string_append_printf(output, introspection_node_element, pathstr);
460
461     if (!path->get_datum || path_get_datum (path, pathstr))
462       {
463         for (i=0; i < path->introspection->len; i++)
464           {
465             gchar *introspect = (gchar *) g_ptr_array_index (path->introspection, i);
466             g_string_append (output, introspect);
467           }
468       }
469
470     if (path->introspect_children_cb)
471       {
472         gchar *children = (*path->introspect_children_cb) (pathstr, path->introspect_children_data);
473         if (children)
474           {
475             g_string_append (output, children);
476             g_free (children);
477           }
478       }
479
480     g_string_append(output, introspection_footer);
481     final = g_string_free(output, FALSE);
482
483     reply = dbus_message_new_method_return (message);
484     if (!reply)
485         oom ();
486     dbus_message_append_args(reply, DBUS_TYPE_STRING, &final,
487                              DBUS_TYPE_INVALID);
488     dbus_connection_send (bus, reply, NULL);
489
490     dbus_message_unref (reply);
491     g_free(final);
492     return DBUS_HANDLER_RESULT_HANDLED;
493 }
494
495 /*---------------------------------------------------------------------------*/
496
497 static DBusHandlerResult
498 handle_other (DBusConnection *bus,
499               DBusMessage    *message,
500               DRoutePath     *path,
501               const gchar    *iface,
502               const gchar    *member,
503               const gchar    *pathstr)
504 {
505     gint result = DBUS_HANDLER_RESULT_NOT_YET_HANDLED;
506
507     StrPair pair;
508     DRouteFunction func;
509     DBusMessage *reply = NULL;
510
511     void *datum;
512
513     pair.one = iface;
514     pair.two = member;
515
516     _DROUTE_DEBUG ("DRoute (handle other): %s|%s on %s\n", member, iface, pathstr);
517
518     func = (DRouteFunction) g_hash_table_lookup (path->methods, &pair);
519     if (func != NULL)
520       {
521         datum = path_get_datum (path, pathstr);
522         if (!datum)
523             reply = droute_object_does_not_exist_error (message);
524         else
525             reply = (func) (bus, message, datum);
526
527         if (!reply)
528           {
529             /* All D-Bus method calls must have a reply.
530              * If one is not provided presume that the call has a void
531              * return and no error has occured.
532              */
533             reply = dbus_message_new_method_return (message);
534           }
535         dbus_connection_send (bus, reply, NULL);
536         dbus_message_unref (reply);
537         result = DBUS_HANDLER_RESULT_HANDLED;
538       }
539
540     _DROUTE_DEBUG ("DRoute (handle other) (reply): type %d\n",
541                    dbus_message_get_type(reply));
542     return result;
543 }
544
545 /*---------------------------------------------------------------------------*/
546
547 static DBusHandlerResult
548 handle_message (DBusConnection *bus, DBusMessage *message, void *user_data)
549 {
550     DRoutePath *path = (DRoutePath *) user_data;
551     const gchar *iface   = dbus_message_get_interface (message);
552     const gchar *member  = dbus_message_get_member (message);
553     const gint   type    = dbus_message_get_type (message);
554     const gchar *pathstr = dbus_message_get_path (message);
555
556     DBusHandlerResult result = DBUS_HANDLER_RESULT_NOT_YET_HANDLED;
557
558     _DROUTE_DEBUG ("DRoute (handle message): %s|%s of type %d on %s\n", member, iface, type, pathstr);
559
560     /* Check for basic reasons not to handle */
561     if (type   != DBUS_MESSAGE_TYPE_METHOD_CALL ||
562         member == NULL ||
563         iface  == NULL)
564         return result;
565
566     if (!strcmp (pathstr, DBUS_PATH_DBUS))
567         result = handle_dbus (bus, message, iface, member, pathstr);
568     else if (!strcmp (iface, "org.freedesktop.DBus.Properties"))
569         result = handle_properties (bus, message, path, iface, member, pathstr);
570     else if (!strcmp (iface, "org.freedesktop.DBus.Introspectable"))
571         result = handle_introspection (bus, message, path, iface, member, pathstr);
572     else
573         result = handle_other (bus, message, path, iface, member, pathstr);
574 #if 0
575     if (result == DBUS_HANDLER_RESULT_NOT_YET_HANDLED)
576         g_print ("DRoute | Unhandled message: %s|%s of type %d on %s\n", member, iface, type, pathstr);
577 #endif
578       
579     return result;
580 }
581
582 /*---------------------------------------------------------------------------*/
583
584 static DBusMessage *
585 droute_object_does_not_exist_error (DBusMessage *message)
586 {
587     DBusMessage *reply;
588     gchar       *errmsg;
589
590     errmsg= g_strdup_printf (
591             "Method \"%s\" with signature \"%s\" on interface \"%s\" could not be processed as object %s does not exist\n",
592             dbus_message_get_member (message),
593             dbus_message_get_signature (message),
594             dbus_message_get_interface (message),
595             dbus_message_get_path (message));
596     reply = dbus_message_new_error (message,
597                                     DBUS_ERROR_FAILED,
598                                     errmsg);
599     g_free (errmsg);
600     return reply;
601 }
602
603 /*---------------------------------------------------------------------------*/
604
605 DBusMessage *
606 droute_not_yet_handled_error (DBusMessage *message)
607 {
608     DBusMessage *reply;
609     gchar       *errmsg;
610
611     errmsg= g_strdup_printf (
612             "Method \"%s\" with signature \"%s\" on interface \"%s\" doesn't exist\n",
613             dbus_message_get_member (message),
614             dbus_message_get_signature (message),
615             dbus_message_get_interface (message));
616     reply = dbus_message_new_error (message,
617                                     DBUS_ERROR_UNKNOWN_METHOD,
618                                     errmsg);
619     g_free (errmsg);
620     return reply;
621 }
622
623 DBusMessage *
624 droute_out_of_memory_error (DBusMessage *message)
625 {
626     DBusMessage *reply;
627     gchar       *errmsg;
628
629     errmsg= g_strdup_printf (
630             "Method \"%s\" with signature \"%s\" on interface \"%s\" could not be processed due to lack of memory\n",
631             dbus_message_get_member (message),
632             dbus_message_get_signature (message),
633             dbus_message_get_interface (message));
634     reply = dbus_message_new_error (message,
635                                     DBUS_ERROR_NO_MEMORY,
636                                     errmsg);
637     g_free (errmsg);
638     return reply;
639 }
640
641 DBusMessage *
642 droute_invalid_arguments_error (DBusMessage *message)
643 {
644     DBusMessage *reply;
645     gchar       *errmsg;
646
647     errmsg= g_strdup_printf (
648             "Method \"%s\" with signature \"%s\" on interface \"%s\" was supplied with invalid arguments\n",
649             dbus_message_get_member (message),
650             dbus_message_get_signature (message),
651             dbus_message_get_interface (message));
652     reply = dbus_message_new_error (message,
653                                     DBUS_ERROR_INVALID_ARGS,
654                                     errmsg);
655     g_free (errmsg);
656     return reply;
657 }
658
659 void
660 droute_path_register (DRoutePath *path, DBusConnection *bus)
661 {
662     if (path->prefix)
663       dbus_connection_register_fallback (bus, path->path, &droute_vtable, path);
664     else
665       dbus_connection_register_object_path (bus, path->path,
666                                             &droute_vtable, path);
667 }
668
669 void
670 droute_context_register (DRouteContext *cnx, DBusConnection *bus)
671 {
672     g_ptr_array_foreach (cnx->registered_paths, (GFunc) droute_path_register,
673                          bus);
674 }
675
676 void
677 droute_intercept_dbus (DBusConnection *bus)
678 {
679     dbus_connection_register_object_path (bus, DBUS_PATH_DBUS,
680                                           &droute_vtable, NULL);
681 }
682 /*END------------------------------------------------------------------------*/