Avoid calling g_error and aborting the application on some errors
[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
27 #include "droute.h"
28 #include "droute-pairhash.h"
29
30 #define CHUNKS_DEFAULT (512)
31
32 #define oom() g_error ("D-Bus out of memory, this message will fail anyway")
33
34 #if defined DROUTE_DEBUG
35     #define _DROUTE_DEBUG(format, args...) g_print (format , ## args)
36 #else
37     #define _DROUTE_DEBUG(format, args...)
38 #endif
39
40 struct _DRouteContext
41 {
42     DBusConnection       *bus;
43     GPtrArray            *registered_paths;
44
45     gchar                *introspect_string;
46 };
47
48 struct _DRoutePath
49 {
50     DRouteContext        *cnx;
51     GStringChunk         *chunks;
52     GPtrArray            *interfaces;
53     GPtrArray            *introspection;
54     GHashTable           *methods;
55     GHashTable           *properties;
56
57     void                   *user_data;
58     DRouteGetDatumFunction  get_datum;
59 };
60
61 /*---------------------------------------------------------------------------*/
62
63 typedef struct PropertyPair
64 {
65     DRoutePropertyFunction get;
66     DRoutePropertyFunction set;
67 } PropertyPair;
68
69 /*---------------------------------------------------------------------------*/
70
71 static DBusHandlerResult
72 handle_message (DBusConnection *bus, DBusMessage *message, void *user_data);
73
74 static DBusMessage *
75 droute_object_does_not_exist_error (DBusMessage *message);
76
77 /*---------------------------------------------------------------------------*/
78
79 static DRoutePath *
80 path_new (DRouteContext *cnx,
81           void    *user_data,
82           DRouteGetDatumFunction get_datum)
83 {
84     DRoutePath *new_path;
85
86     new_path = g_new0 (DRoutePath, 1);
87     new_path->cnx = cnx;
88     new_path->chunks = g_string_chunk_new (CHUNKS_DEFAULT);
89     new_path->interfaces = g_ptr_array_new ();
90     new_path->introspection = g_ptr_array_new ();
91
92     new_path->methods = g_hash_table_new_full ((GHashFunc)str_pair_hash,
93                                                str_pair_equal,
94                                                g_free,
95                                                NULL);
96
97     new_path->properties = g_hash_table_new_full ((GHashFunc)str_pair_hash,
98                                                   str_pair_equal,
99                                                   g_free,
100                                                   NULL);
101
102     new_path->user_data = user_data;
103     new_path->get_datum = get_datum;
104
105     return new_path;
106 }
107
108 static void
109 path_free (DRoutePath *path, gpointer user_data)
110 {
111     g_string_chunk_free  (path->chunks);
112     g_ptr_array_free     (path->interfaces, TRUE);
113     g_ptr_array_free     (path->introspection, FALSE);
114     g_hash_table_destroy (path->methods);
115     g_hash_table_destroy (path->properties);
116 }
117
118 static void *
119 path_get_datum (DRoutePath *path, const gchar *pathstr)
120 {
121     if (path->get_datum != NULL)
122         return (path->get_datum) (pathstr, path->user_data);
123     else
124         return path->user_data;
125 }
126
127 /*---------------------------------------------------------------------------*/
128
129 DRouteContext *
130 droute_new (DBusConnection *bus)
131 {
132     DRouteContext *cnx;
133
134     cnx = g_new0 (DRouteContext, 1);
135     cnx->bus = bus;
136     cnx->registered_paths = g_ptr_array_new ();
137
138     return cnx;
139 }
140
141 void
142 droute_free (DRouteContext *cnx)
143 {
144     g_ptr_array_foreach (cnx->registered_paths, (GFunc) path_free, NULL);
145     g_free (cnx);
146 }
147
148 /*---------------------------------------------------------------------------*/
149
150 DBusConnection *
151 droute_get_bus (DRouteContext *cnx)
152 {
153     return cnx->bus;
154 }
155
156 /*---------------------------------------------------------------------------*/
157
158 static DBusObjectPathVTable droute_vtable =
159 {
160   NULL,
161   &handle_message,
162   NULL, NULL, NULL, NULL
163 };
164
165 DRoutePath *
166 droute_add_one (DRouteContext *cnx,
167                 const char    *path,
168                 const void    *data)
169 {
170     DRoutePath *new_path;
171     gboolean registered;
172
173     new_path = path_new (cnx, (void *) data, NULL);
174
175     registered = dbus_connection_register_object_path (cnx->bus, path, &droute_vtable, new_path);
176     if (!registered)
177       {
178         path_free (new_path, NULL);
179         return NULL;
180       }
181
182     g_ptr_array_add (cnx->registered_paths, new_path);
183     return new_path;
184 }
185
186 DRoutePath *
187 droute_add_many (DRouteContext *cnx,
188                  const char    *path,
189                  const void    *data,
190                  const DRouteGetDatumFunction get_datum)
191 {
192     DRoutePath *new_path;
193
194     new_path = path_new (cnx, (void *) data, get_datum);
195
196     if (!dbus_connection_register_fallback (cnx->bus, path, &droute_vtable, new_path))
197         oom();
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_properties (DBusConnection *bus,
374                    DBusMessage    *message,
375                    DRoutePath     *path,
376                    const gchar    *iface,
377                    const gchar    *member,
378                    const gchar    *pathstr)
379 {
380     DBusMessage *reply;
381     DBusHandlerResult result = DBUS_HANDLER_RESULT_HANDLED;
382
383     if (!g_strcmp0(member, "GetAll"))
384        reply = impl_prop_GetAll (message, path, pathstr);
385     else if (!g_strcmp0 (member, "Get"))
386        reply = impl_prop_GetSet (message, path, pathstr, TRUE);
387     else if (!g_strcmp0 (member, "Set"))
388        reply = impl_prop_GetSet (message, path, pathstr, FALSE);
389     else
390        result = DBUS_HANDLER_RESULT_NOT_YET_HANDLED;
391
392     if (reply)
393       {
394         dbus_connection_send (bus, reply, NULL);
395         dbus_message_unref (reply);
396       }
397
398     return result;
399 }
400
401 /*---------------------------------------------------------------------------*/
402
403 static const char *introspection_header =
404 "<?xml version=\"1.0\"?>\n";
405
406 static const char *introspection_node_element =
407 "<node name=\"%s\">\n";
408
409 static const char *introspection_footer =
410 "</node>";
411
412 static DBusHandlerResult
413 handle_introspection (DBusConnection *bus,
414                       DBusMessage    *message,
415                       DRoutePath     *path,
416                       const gchar    *iface,
417                       const gchar    *member,
418                       const gchar    *pathstr)
419 {
420     GString *output;
421     gchar *final;
422     gint i;
423
424     DBusMessage *reply;
425
426     _DROUTE_DEBUG ("DRoute (handle introspection): %s\n", pathstr);
427
428     if (g_strcmp0 (member, "Introspect"))
429         return DBUS_HANDLER_RESULT_NOT_YET_HANDLED;
430
431     output = g_string_new(introspection_header);
432
433     g_string_append_printf(output, introspection_node_element, pathstr);
434
435     for (i=0; i < path->introspection->len; i++)
436       {
437         gchar *introspect = (gchar *) g_ptr_array_index (path->introspection, i);
438         g_string_append (output, introspect);
439       }
440
441     g_string_append(output, introspection_footer);
442     final = g_string_free(output, FALSE);
443
444     reply = dbus_message_new_method_return (message);
445     if (!reply)
446         oom ();
447     dbus_message_append_args(reply, DBUS_TYPE_STRING, &final,
448                              DBUS_TYPE_INVALID);
449     dbus_connection_send (bus, reply, NULL);
450
451     dbus_message_unref (reply);
452     g_free(final);
453     return DBUS_HANDLER_RESULT_HANDLED;
454 }
455
456 /*---------------------------------------------------------------------------*/
457
458 static DBusHandlerResult
459 handle_other (DBusConnection *bus,
460               DBusMessage    *message,
461               DRoutePath     *path,
462               const gchar    *iface,
463               const gchar    *member,
464               const gchar    *pathstr)
465 {
466     gint result = DBUS_HANDLER_RESULT_NOT_YET_HANDLED;
467
468     StrPair pair;
469     DRouteFunction func;
470     DBusMessage *reply = NULL;
471
472     void *datum;
473
474     pair.one = iface;
475     pair.two = member;
476
477     _DROUTE_DEBUG ("DRoute (handle other): %s|%s on %s\n", member, iface, pathstr);
478
479     func = (DRouteFunction) g_hash_table_lookup (path->methods, &pair);
480     if (func != NULL)
481       {
482         datum = path_get_datum (path, pathstr);
483         if (!datum)
484             reply = droute_object_does_not_exist_error (message);
485         else
486             reply = (func) (bus, message, datum);
487
488         if (!reply)
489           {
490             /* All D-Bus method calls must have a reply.
491              * If one is not provided presume that the call has a void
492              * return and no error has occured.
493              */
494             reply = dbus_message_new_method_return (message);
495           }
496         dbus_connection_send (bus, reply, NULL);
497         dbus_message_unref (reply);
498         result = DBUS_HANDLER_RESULT_HANDLED;
499       }
500
501     _DROUTE_DEBUG ("DRoute (handle other) (reply): type %d\n",
502                    dbus_message_get_type(reply));
503     return result;
504 }
505
506 /*---------------------------------------------------------------------------*/
507
508 static DBusHandlerResult
509 handle_message (DBusConnection *bus, DBusMessage *message, void *user_data)
510 {
511     DRoutePath *path = (DRoutePath *) user_data;
512     const gchar *iface   = dbus_message_get_interface (message);
513     const gchar *member  = dbus_message_get_member (message);
514     const gint   type    = dbus_message_get_type (message);
515     const gchar *pathstr = dbus_message_get_path (message);
516
517     DBusHandlerResult result = DBUS_HANDLER_RESULT_NOT_YET_HANDLED;
518
519     _DROUTE_DEBUG ("DRoute (handle message): %s|%s of type %d on %s\n", member, iface, type, pathstr);
520
521     /* Check for basic reasons not to handle */
522     if (type   != DBUS_MESSAGE_TYPE_METHOD_CALL ||
523         member == NULL ||
524         iface  == NULL)
525         return result;
526
527     if (!strcmp (iface, "org.freedesktop.DBus.Properties"))
528         result = handle_properties (bus, message, path, iface, member, pathstr);
529     else if (!strcmp (iface, "org.freedesktop.DBus.Introspectable"))
530         result = handle_introspection (bus, message, path, iface, member, pathstr);
531     else
532         result = handle_other (bus, message, path, iface, member, pathstr);
533 #if 0
534     if (result == DBUS_HANDLER_RESULT_NOT_YET_HANDLED)
535         g_print ("DRoute | Unhandled message: %s|%s of type %d on %s\n", member, iface, type, pathstr);
536 #endif
537       
538     return result;
539 }
540
541 /*---------------------------------------------------------------------------*/
542
543 static DBusMessage *
544 droute_object_does_not_exist_error (DBusMessage *message)
545 {
546     DBusMessage *reply;
547     gchar       *errmsg;
548
549     errmsg= g_strdup_printf (
550             "Method \"%s\" with signature \"%s\" on interface \"%s\" could not be processed as object %s does not exist\n",
551             dbus_message_get_member (message),
552             dbus_message_get_signature (message),
553             dbus_message_get_interface (message),
554             dbus_message_get_path (message));
555     reply = dbus_message_new_error (message,
556                                     DBUS_ERROR_FAILED,
557                                     errmsg);
558     g_free (errmsg);
559     return reply;
560 }
561
562 /*---------------------------------------------------------------------------*/
563
564 DBusMessage *
565 droute_not_yet_handled_error (DBusMessage *message)
566 {
567     DBusMessage *reply;
568     gchar       *errmsg;
569
570     errmsg= g_strdup_printf (
571             "Method \"%s\" with signature \"%s\" on interface \"%s\" doesn't exist\n",
572             dbus_message_get_member (message),
573             dbus_message_get_signature (message),
574             dbus_message_get_interface (message));
575     reply = dbus_message_new_error (message,
576                                     DBUS_ERROR_UNKNOWN_METHOD,
577                                     errmsg);
578     g_free (errmsg);
579     return reply;
580 }
581
582 DBusMessage *
583 droute_out_of_memory_error (DBusMessage *message)
584 {
585     DBusMessage *reply;
586     gchar       *errmsg;
587
588     errmsg= g_strdup_printf (
589             "Method \"%s\" with signature \"%s\" on interface \"%s\" could not be processed due to lack of memory\n",
590             dbus_message_get_member (message),
591             dbus_message_get_signature (message),
592             dbus_message_get_interface (message));
593     reply = dbus_message_new_error (message,
594                                     DBUS_ERROR_NO_MEMORY,
595                                     errmsg);
596     g_free (errmsg);
597     return reply;
598 }
599
600 DBusMessage *
601 droute_invalid_arguments_error (DBusMessage *message)
602 {
603     DBusMessage *reply;
604     gchar       *errmsg;
605
606     errmsg= g_strdup_printf (
607             "Method \"%s\" with signature \"%s\" on interface \"%s\" was supplied with invalid arguments\n",
608             dbus_message_get_member (message),
609             dbus_message_get_signature (message),
610             dbus_message_get_interface (message));
611     reply = dbus_message_new_error (message,
612                                     DBUS_ERROR_INVALID_ARGS,
613                                     errmsg);
614     g_free (errmsg);
615     return reply;
616 }
617
618 /*END------------------------------------------------------------------------*/