droute: Fix compile-time warnings
[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
178     new_path = path_new (cnx, path, FALSE, (void *)data, NULL, NULL, NULL);
179
180     g_ptr_array_add (cnx->registered_paths, new_path);
181     return new_path;
182 }
183
184 DRoutePath *
185 droute_add_many (DRouteContext *cnx,
186                  const char    *path,
187                  const void    *data,
188                  DRouteIntrospectChildrenFunction introspect_children_cb,
189                  void *introspect_children_data,
190                  const DRouteGetDatumFunction get_datum)
191 {
192     DRoutePath *new_path;
193
194     new_path = path_new (cnx, path, TRUE, (void *) data,
195                          introspect_children_cb, introspect_children_data,
196                          get_datum);
197
198     g_ptr_array_add (cnx->registered_paths, new_path);
199     return new_path;
200 }
201
202 /*---------------------------------------------------------------------------*/
203
204 void
205 droute_path_add_interface(DRoutePath *path,
206                           const char *name,
207                           const char *introspect,
208                           const DRouteMethod   *methods,
209                           const DRouteProperty *properties)
210 {
211     gchar *itf;
212
213     g_return_if_fail (name != NULL);
214
215     itf = g_string_chunk_insert (path->chunks, name);
216     g_ptr_array_add (path->interfaces, itf);
217     g_ptr_array_add (path->introspection, (gpointer) introspect);
218
219     for (; methods != NULL && methods->name != NULL; methods++)
220       {
221         gchar *meth;
222
223         meth = g_string_chunk_insert (path->chunks, methods->name);
224         g_hash_table_insert (path->methods, str_pair_new (itf, meth), methods->func);
225       }
226
227     for (; properties != NULL && properties->name != NULL; properties++)
228       {
229         gchar *prop;
230         PropertyPair *pair;
231
232         prop = g_string_chunk_insert (path->chunks, properties->name);
233         pair = g_new (PropertyPair, 1);
234         pair->get = properties->get;
235         pair->set = properties->set;
236         g_hash_table_insert (path->properties, str_pair_new (itf, prop), pair);
237       }
238 }
239
240 /*---------------------------------------------------------------------------*/
241
242 /* The data structures don't support an efficient implementation of GetAll
243  * and I don't really care.
244  */
245 static DBusMessage *
246 impl_prop_GetAll (DBusMessage *message,
247                   DRoutePath  *path,
248                   const char  *pathstr)
249 {
250     DBusMessageIter iter, iter_dict, iter_dict_entry;
251     DBusMessage *reply;
252     DBusError error;
253     GHashTableIter prop_iter;
254
255     StrPair *key;
256     PropertyPair *value;
257     gchar *iface;
258
259     void  *datum = path_get_datum (path, pathstr);
260     if (!datum)
261         return droute_object_does_not_exist_error (message);
262
263     dbus_error_init (&error);
264     if (!dbus_message_get_args
265                 (message, &error, DBUS_TYPE_STRING, &iface, DBUS_TYPE_INVALID))
266         return dbus_message_new_error (message, DBUS_ERROR_FAILED, error.message);
267
268     reply = dbus_message_new_method_return (message);
269     if (!reply)
270         oom ();
271
272     dbus_message_iter_init_append (reply, &iter);
273     if (!dbus_message_iter_open_container
274                 (&iter, DBUS_TYPE_ARRAY, "{sv}", &iter_dict))
275         oom ();
276
277     g_hash_table_iter_init (&prop_iter, path->properties);
278     while (g_hash_table_iter_next (&prop_iter, (gpointer*)&key, (gpointer*)&value))
279       {
280         if (!g_strcmp0 (key->one, iface))
281          {
282            if (!value->get)
283               continue;
284            if (!dbus_message_iter_open_container
285                         (&iter_dict, DBUS_TYPE_DICT_ENTRY, NULL, &iter_dict_entry))
286               oom ();
287            dbus_message_iter_append_basic (&iter_dict_entry, DBUS_TYPE_STRING,
288                                            &key->two);
289            (value->get) (&iter_dict_entry, datum);
290            if (!dbus_message_iter_close_container (&iter_dict, &iter_dict_entry))
291                oom ();
292          }
293       }
294
295     if (!dbus_message_iter_close_container (&iter, &iter_dict))
296         oom ();
297     return reply;
298 }
299
300 static DBusMessage *
301 impl_prop_GetSet (DBusMessage *message,
302                   DRoutePath  *path,
303                   const char  *pathstr,
304                   gboolean     get)
305 {
306     DBusMessage *reply = NULL;
307     DBusError error;
308
309     StrPair pair;
310     PropertyPair *prop_funcs = NULL;
311
312     void *datum;
313
314     dbus_error_init (&error);
315     if (!dbus_message_get_args (message,
316                                 &error,
317                                 DBUS_TYPE_STRING,
318                                 &(pair.one),
319                                 DBUS_TYPE_STRING,
320                                 &(pair.two),
321                                 DBUS_TYPE_INVALID))
322         return dbus_message_new_error (message, DBUS_ERROR_FAILED, error.message);
323
324     _DROUTE_DEBUG ("DRoute (handle prop): %s|%s on %s\n", pair.one, pair.two, pathstr);
325
326     prop_funcs = (PropertyPair *) g_hash_table_lookup (path->properties, &pair);
327     if (!prop_funcs)
328 #ifdef DBUS_ERROR_UNKNOWN_PROPERTY
329         return dbus_message_new_error (message, DBUS_ERROR_UNKNOWN_PROPERTY, "Property unavailable");
330 #else
331         return dbus_message_new_error (message, DBUS_ERROR_FAILED, "Property unavailable");
332 #endif
333
334     datum = path_get_datum (path, pathstr);
335     if (!datum)
336         return droute_object_does_not_exist_error (message);
337
338     if (get && prop_funcs->get)
339       {
340         
341         DBusMessageIter iter;
342
343         _DROUTE_DEBUG ("DRoute (handle prop Get): %s|%s on %s\n", pair.one, pair.two, pathstr);
344
345         reply = dbus_message_new_method_return (message);
346         dbus_message_iter_init_append (reply, &iter);
347         if (!(prop_funcs->get) (&iter, datum))
348           {
349             dbus_message_unref (reply);
350             reply = dbus_message_new_error (message, DBUS_ERROR_FAILED, "Get failed");
351           }
352       }
353     else if (!get && prop_funcs->set)
354       {
355         DBusMessageIter iter;
356
357         _DROUTE_DEBUG ("DRoute (handle prop Get): %s|%s on %s\n", pair.one, pair.two, pathstr);
358
359         dbus_message_iter_init (message, &iter);
360         /* Skip the interface and property name */
361         dbus_message_iter_next(&iter);
362         dbus_message_iter_next(&iter);
363         (prop_funcs->set) (&iter, datum);
364
365         reply = dbus_message_new_method_return (message);
366       }
367 #ifdef DBUS_ERROR_PROPERTY_READ_ONLY
368     else if (!get)
369       {
370         reply = dbus_message_new_error (message, DBUS_ERROR_PROPERTY_READ_ONLY, "Property is read-only");
371       }
372 #endif
373     else
374       {
375         reply = dbus_message_new_error (message, DBUS_ERROR_FAILED, "Getter or setter unavailable");
376       }
377
378     return reply;
379 }
380
381 static DBusHandlerResult
382 handle_dbus (DBusConnection *bus,
383                    DBusMessage    *message,
384                    const gchar    *iface,
385                    const gchar    *member,
386                    const gchar    *pathstr)
387 {
388   static int id = 1;
389   char *id_str = (char *) g_malloc(40);
390   DBusMessage *reply;
391
392   if (strcmp (iface, DBUS_INTERFACE_DBUS) != 0 ||
393       strcmp (member, "Hello") != 0)
394     {
395       g_free (id_str);
396       return DBUS_HANDLER_RESULT_NOT_YET_HANDLED;
397     }
398
399     /* TODO: Fix this hack (we don't handle wrap-around, for instance) */
400     sprintf (id_str, ":1.%d", id++);
401     reply = dbus_message_new_method_return (message);
402     dbus_message_append_args (reply, DBUS_TYPE_STRING, &id_str, DBUS_TYPE_INVALID);
403     dbus_connection_send (bus, reply, NULL);
404   dbus_connection_flush (bus);
405     dbus_message_unref (reply);
406   g_free (id_str);
407     return DBUS_HANDLER_RESULT_HANDLED;
408 }
409
410 static DBusHandlerResult
411 handle_properties (DBusConnection *bus,
412                    DBusMessage    *message,
413                    DRoutePath     *path,
414                    const gchar    *iface,
415                    const gchar    *member,
416                    const gchar    *pathstr)
417 {
418     DBusMessage *reply = NULL;
419     DBusHandlerResult result = DBUS_HANDLER_RESULT_HANDLED;
420
421     if (!g_strcmp0(member, "GetAll"))
422        reply = impl_prop_GetAll (message, path, pathstr);
423     else if (!g_strcmp0 (member, "Get"))
424        reply = impl_prop_GetSet (message, path, pathstr, TRUE);
425     else if (!g_strcmp0 (member, "Set"))
426        reply = impl_prop_GetSet (message, path, pathstr, FALSE);
427     else
428        result = DBUS_HANDLER_RESULT_NOT_YET_HANDLED;
429
430     if (reply)
431       {
432         dbus_connection_send (bus, reply, NULL);
433         dbus_message_unref (reply);
434       }
435
436     return result;
437 }
438
439 /*---------------------------------------------------------------------------*/
440
441 static const char *introspection_header =
442 "<?xml version=\"1.0\"?>\n";
443
444 static const char *introspection_node_element =
445 "<node name=\"%s\">\n";
446
447 static const char *introspection_footer =
448 "</node>";
449
450 static DBusHandlerResult
451 handle_introspection (DBusConnection *bus,
452                       DBusMessage    *message,
453                       DRoutePath     *path,
454                       const gchar    *iface,
455                       const gchar    *member,
456                       const gchar    *pathstr)
457 {
458     GString *output;
459     gchar *final;
460     gint i;
461
462     DBusMessage *reply;
463
464     _DROUTE_DEBUG ("DRoute (handle introspection): %s\n", pathstr);
465
466     if (g_strcmp0 (member, "Introspect"))
467         return DBUS_HANDLER_RESULT_NOT_YET_HANDLED;
468
469     output = g_string_new(introspection_header);
470
471     g_string_append_printf(output, introspection_node_element, pathstr);
472
473     if (!path->get_datum || path_get_datum (path, pathstr))
474       {
475         for (i=0; i < path->introspection->len; i++)
476           {
477             gchar *introspect = (gchar *) g_ptr_array_index (path->introspection, i);
478             g_string_append (output, introspect);
479           }
480       }
481
482     if (path->introspect_children_cb)
483       {
484         gchar *children = (*path->introspect_children_cb) (pathstr, path->introspect_children_data);
485         if (children)
486           {
487             g_string_append (output, children);
488             g_free (children);
489           }
490       }
491
492     g_string_append(output, introspection_footer);
493     final = g_string_free(output, FALSE);
494
495     reply = dbus_message_new_method_return (message);
496     if (!reply)
497         oom ();
498     dbus_message_append_args(reply, DBUS_TYPE_STRING, &final,
499                              DBUS_TYPE_INVALID);
500     dbus_connection_send (bus, reply, NULL);
501
502     dbus_message_unref (reply);
503     g_free(final);
504     return DBUS_HANDLER_RESULT_HANDLED;
505 }
506
507 /*---------------------------------------------------------------------------*/
508
509 static DBusHandlerResult
510 handle_other (DBusConnection *bus,
511               DBusMessage    *message,
512               DRoutePath     *path,
513               const gchar    *iface,
514               const gchar    *member,
515               const gchar    *pathstr)
516 {
517     gint result = DBUS_HANDLER_RESULT_NOT_YET_HANDLED;
518
519     StrPair pair;
520     DRouteFunction func;
521     DBusMessage *reply = NULL;
522
523     void *datum;
524
525     pair.one = iface;
526     pair.two = member;
527
528     _DROUTE_DEBUG ("DRoute (handle other): %s|%s on %s\n", member, iface, pathstr);
529
530     func = (DRouteFunction) g_hash_table_lookup (path->methods, &pair);
531     if (func != NULL)
532       {
533         datum = path_get_datum (path, pathstr);
534         if (!datum)
535             reply = droute_object_does_not_exist_error (message);
536         else
537             reply = (func) (bus, message, datum);
538
539         /* All D-Bus method calls must have a reply.
540          * If one is not provided presume that the caller has already
541          * sent one.
542          */
543         if (reply)
544           {
545             dbus_connection_send (bus, reply, NULL);
546             dbus_message_unref (reply);
547           }
548         result = DBUS_HANDLER_RESULT_HANDLED;
549       }
550
551     _DROUTE_DEBUG ("DRoute (handle other) (reply): type %d\n",
552                    dbus_message_get_type(reply));
553     return result;
554 }
555
556 /*---------------------------------------------------------------------------*/
557
558 static DBusHandlerResult
559 handle_message (DBusConnection *bus, DBusMessage *message, void *user_data)
560 {
561     DRoutePath *path = (DRoutePath *) user_data;
562     const gchar *iface   = dbus_message_get_interface (message);
563     const gchar *member  = dbus_message_get_member (message);
564     const gint   type    = dbus_message_get_type (message);
565     const gchar *pathstr = dbus_message_get_path (message);
566
567     DBusHandlerResult result = DBUS_HANDLER_RESULT_NOT_YET_HANDLED;
568
569     _DROUTE_DEBUG ("DRoute (handle message): %s|%s of type %d on %s\n", member, iface, type, pathstr);
570
571     /* Check for basic reasons not to handle */
572     if (type   != DBUS_MESSAGE_TYPE_METHOD_CALL ||
573         member == NULL ||
574         iface  == NULL)
575         return result;
576
577     if (!strcmp (pathstr, DBUS_PATH_DBUS))
578         result = handle_dbus (bus, message, iface, member, pathstr);
579     else if (!strcmp (iface, "org.freedesktop.DBus.Properties"))
580         result = handle_properties (bus, message, path, iface, member, pathstr);
581     else if (!strcmp (iface, "org.freedesktop.DBus.Introspectable"))
582         result = handle_introspection (bus, message, path, iface, member, pathstr);
583     else
584         result = handle_other (bus, message, path, iface, member, pathstr);
585 #if 0
586     if (result == DBUS_HANDLER_RESULT_NOT_YET_HANDLED)
587         g_print ("DRoute | Unhandled message: %s|%s of type %d on %s\n", member, iface, type, pathstr);
588 #endif
589       
590     return result;
591 }
592
593 /*---------------------------------------------------------------------------*/
594
595 static DBusMessage *
596 droute_object_does_not_exist_error (DBusMessage *message)
597 {
598     DBusMessage *reply;
599     gchar       *errmsg;
600
601     errmsg= g_strdup_printf (
602             "Method \"%s\" with signature \"%s\" on interface \"%s\" could not be processed as object %s does not exist\n",
603             dbus_message_get_member (message),
604             dbus_message_get_signature (message),
605             dbus_message_get_interface (message),
606             dbus_message_get_path (message));
607 #ifdef DBUS_ERROR_UNKNOWN_OBJECT
608     reply = dbus_message_new_error (message,
609                                     DBUS_ERROR_UNKNOWN_OBJECT,
610                                     errmsg);
611 #else
612     reply = dbus_message_new_error (message,
613                                     DBUS_ERROR_FAILED,
614                                     errmsg);
615 #endif
616     g_free (errmsg);
617     return reply;
618 }
619
620 /*---------------------------------------------------------------------------*/
621
622 DBusMessage *
623 droute_not_yet_handled_error (DBusMessage *message)
624 {
625     DBusMessage *reply;
626     gchar       *errmsg;
627
628     errmsg= g_strdup_printf (
629             "Method \"%s\" with signature \"%s\" on interface \"%s\" doesn't exist\n",
630             dbus_message_get_member (message),
631             dbus_message_get_signature (message),
632             dbus_message_get_interface (message));
633     reply = dbus_message_new_error (message,
634                                     DBUS_ERROR_UNKNOWN_METHOD,
635                                     errmsg);
636     g_free (errmsg);
637     return reply;
638 }
639
640 DBusMessage *
641 droute_out_of_memory_error (DBusMessage *message)
642 {
643     DBusMessage *reply;
644     gchar       *errmsg;
645
646     errmsg= g_strdup_printf (
647             "Method \"%s\" with signature \"%s\" on interface \"%s\" could not be processed due to lack of memory\n",
648             dbus_message_get_member (message),
649             dbus_message_get_signature (message),
650             dbus_message_get_interface (message));
651     reply = dbus_message_new_error (message,
652                                     DBUS_ERROR_NO_MEMORY,
653                                     errmsg);
654     g_free (errmsg);
655     return reply;
656 }
657
658 DBusMessage *
659 droute_invalid_arguments_error (DBusMessage *message)
660 {
661     DBusMessage *reply;
662     gchar       *errmsg;
663
664     errmsg= g_strdup_printf (
665             "Method \"%s\" with signature \"%s\" on interface \"%s\" was supplied with invalid arguments\n",
666             dbus_message_get_member (message),
667             dbus_message_get_signature (message),
668             dbus_message_get_interface (message));
669     reply = dbus_message_new_error (message,
670                                     DBUS_ERROR_INVALID_ARGS,
671                                     errmsg);
672     g_free (errmsg);
673     return reply;
674 }
675
676 void
677 droute_path_register (DRoutePath *path, DBusConnection *bus)
678 {
679     if (path->prefix)
680       dbus_connection_register_fallback (bus, path->path, &droute_vtable, path);
681     else
682       dbus_connection_register_object_path (bus, path->path,
683                                             &droute_vtable, path);
684 }
685
686 void
687 droute_path_unregister (DRoutePath *path, DBusConnection *bus)
688 {
689   dbus_connection_unregister_object_path (bus, path->path);
690 }
691
692 void
693 droute_context_register (DRouteContext *cnx, DBusConnection *bus)
694 {
695     g_ptr_array_foreach (cnx->registered_paths, (GFunc) droute_path_register,
696                          bus);
697 }
698
699 void
700 droute_context_unregister (DRouteContext *cnx, DBusConnection *bus)
701 {
702     g_ptr_array_foreach (cnx->registered_paths, (GFunc) droute_path_unregister,
703                          bus);
704 }
705
706 void
707 droute_context_deregister (DRouteContext *cnx, DBusConnection *bus)
708 {
709     g_ptr_array_foreach (cnx->registered_paths, (GFunc) droute_path_unregister,
710                          bus);
711 }
712
713 void
714 droute_intercept_dbus (DBusConnection *bus)
715 {
716     dbus_connection_register_object_path (bus, DBUS_PATH_DBUS,
717                                           &droute_vtable, NULL);
718 }
719
720 void
721 droute_unintercept_dbus (DBusConnection *bus)
722 {
723     dbus_connection_unregister_object_path (bus, DBUS_PATH_DBUS);
724 }
725
726 /*END------------------------------------------------------------------------*/