Correct case of DBus in interface name
[platform/core/uifw/at-spi2-atk.git] / libspi / 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  *
7  * This library is free software; you can redistribute it and/or
8  * modify it under the terms of the GNU Library General Public
9  * License as published by the Free Software Foundation; either
10  * version 2 of the License, or (at your option) any later version.
11  *
12  * This library is distributed in the hope that it will be useful,
13  * but WITHOUT ANY WARRANTY; without even the implied warranty of
14  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
15  * Library General Public License for more details.
16  *
17  * You should have received a copy of the GNU Library General Public
18  * License along with this library; if not, write to the
19  * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
20  * Boston, MA 02111-1307, USA.
21  */
22
23 #include "droute.h"
24 #include <stdlib.h>
25 #include <string.h>
26
27 static DRouteInterface *
28 find_iface (DRouteData * data, const char *name)
29 {
30   GSList *l;
31
32   for (l = data->interfaces; l; l = g_slist_next (l))
33     {
34       DRouteInterface *iface = (DRouteInterface *) l->data;
35       if (iface && iface->name && !strcmp (iface->name, name))
36         return iface;
37     }
38   return NULL;
39 }
40
41 static DBusHandlerResult
42 prop_get_all (DBusConnection * bus, DBusMessage * message, DRouteData * data)
43 {
44   DRouteInterface *iface_def;
45   DRouteProperty *prop;
46   DBusError error;
47   const char *iface;
48   const char *path = dbus_message_get_path (message);
49   DBusMessage *reply;
50   DBusMessageIter iter, iter_dict, iter_dict_entry;
51
52   dbus_error_init (&error);
53   if (!dbus_message_get_args
54       (message, &error, DBUS_TYPE_STRING, &iface, DBUS_TYPE_INVALID))
55     {
56       return DBUS_HANDLER_RESULT_NOT_YET_HANDLED;
57     }
58   reply = dbus_message_new_method_return (message);
59   /* tbd: replace tbd with todo? */
60   if (!reply)
61     goto oom;
62   dbus_message_iter_init_append (reply, &iter);
63   if (!dbus_message_iter_open_container
64       (&iter, DBUS_TYPE_ARRAY, "{sv}", &iter_dict))
65     goto oom;
66   iface_def = find_iface (data, iface);
67   if (!iface_def)
68     return DBUS_HANDLER_RESULT_NOT_YET_HANDLED;
69   if (iface_def->properties)
70     for (prop = iface_def->properties; prop->name; prop++)
71       {
72         if (!prop->get)
73           continue;
74         if (!dbus_message_iter_open_container
75             (&iter_dict, DBUS_TYPE_DICT_ENTRY, NULL, &iter_dict_entry))
76           goto oom;
77         dbus_message_iter_append_basic (&iter_dict_entry, DBUS_TYPE_STRING,
78                                         &prop->name);
79         (*prop->get) (path, &iter_dict_entry, data->user_data);
80         if (!dbus_message_iter_close_container (&iter_dict, &iter_dict_entry))
81           goto oom;
82       }
83   if (!dbus_message_iter_close_container (&iter, &iter_dict))
84     goto oom;
85   dbus_connection_send (bus, reply, NULL);
86   dbus_message_unref (reply);
87   return DBUS_HANDLER_RESULT_HANDLED;
88 oom:
89   /* tbd: return a standard out-of-memory error */
90   return DBUS_HANDLER_RESULT_NOT_YET_HANDLED;
91 }
92
93 static DBusHandlerResult
94 prop (DBusConnection * bus, DBusMessage * message, DRouteData * data)
95 {
96   const char *mode = dbus_message_get_member (message);
97   const char *iface, *member;
98   const char *path = dbus_message_get_path (message);
99   int set;
100   DBusMessage *reply;
101   DBusMessageIter iter;
102   DRouteInterface *iface_def;
103   DRouteProperty *prop = NULL;
104
105   if (!strcmp (mode, "Set"))
106     set = 1;
107   else if (!strcmp (mode, "Get"))
108     set = 0;
109   else
110     return DBUS_HANDLER_RESULT_NOT_YET_HANDLED;
111   reply = dbus_message_new_method_return (message);
112   dbus_message_iter_init ((set ? reply : message), &iter);
113   dbus_message_iter_get_basic (&iter, &iface);
114   dbus_message_iter_next (&iter);
115   dbus_message_iter_get_basic (&iter, &member);
116   iface_def = find_iface (data, iface);
117   if (!iface_def)
118     return DBUS_HANDLER_RESULT_NOT_YET_HANDLED;
119   if (iface_def->properties)
120     for (prop = iface_def->properties;
121          prop->name && strcmp (prop->name, member); prop++)
122       if (!prop || !prop->name)
123         return DBUS_HANDLER_RESULT_NOT_YET_HANDLED;
124   if (set)
125     {
126       if (!prop->set)
127         return DBUS_HANDLER_RESULT_NOT_YET_HANDLED;
128       (*prop->set) (path, &iter, data->user_data);
129     }
130   else
131     {
132       if (!prop->get)
133         return DBUS_HANDLER_RESULT_NOT_YET_HANDLED;
134       (*prop->get) (path, &iter, data->user_data);
135     }
136   dbus_connection_send (bus, reply, NULL);
137   dbus_message_unref (message);
138   return DBUS_HANDLER_RESULT_HANDLED;
139 }
140
141 static DBusHandlerResult
142 introspect (DBusConnection * bus, DBusMessage * message, DRouteData * data)
143 {
144   const char *path = dbus_message_get_path (message);
145   GString *xml;
146   DRouteInterface *iface_def;
147   DRouteMethod *method;
148   DRouteProperty *property;
149   char *str;
150   DBusMessage *reply;
151   GSList *l;
152   void *datum;
153
154   if (strcmp (dbus_message_get_member (message), "Introspect"))
155     {
156       return DBUS_HANDLER_RESULT_NOT_YET_HANDLED;
157     }
158   xml = g_string_new (NULL);
159   /* below code stolen from dbus-glib */
160   g_string_append (xml, DBUS_INTROSPECT_1_0_XML_DOCTYPE_DECL_NODE);
161   g_string_append_printf (xml, "<node name=\"%s\">\n", path);
162
163   /* We are introspectable, though I guess that was pretty obvious */
164   g_string_append_printf (xml, "  <interface name=\"%s\">\n",
165                           DBUS_INTERFACE_INTROSPECTABLE);
166   g_string_append (xml, "    <method name=\"Introspect\">\n");
167   g_string_append_printf (xml,
168                           "      <arg name=\"data\" direction=\"out\" type=\"%s\"/>\n",
169                           DBUS_TYPE_STRING_AS_STRING);
170   g_string_append (xml, "    </method>\n");
171   g_string_append (xml, "  </interface>\n");
172
173   /* We support get/set/getall properties */
174   g_string_append_printf (xml, "  <interface name=\"%s\">\n",
175                           DBUS_INTERFACE_PROPERTIES);
176   g_string_append (xml, "    <method name=\"Get\">\n");
177   g_string_append_printf (xml,
178                           "      <arg name=\"interface\" direction=\"in\" type=\"%s\"/>\n",
179                           DBUS_TYPE_STRING_AS_STRING);
180   g_string_append_printf (xml,
181                           "      <arg name=\"propname\" direction=\"in\" type=\"%s\"/>\n",
182                           DBUS_TYPE_STRING_AS_STRING);
183   g_string_append_printf (xml,
184                           "      <arg name=\"value\" direction=\"out\" type=\"%s\"/>\n",
185                           DBUS_TYPE_VARIANT_AS_STRING);
186   g_string_append (xml, "    </method>\n");
187   g_string_append (xml, "    <method name=\"Set\">\n");
188   g_string_append_printf (xml,
189                           "      <arg name=\"interface\" direction=\"in\" type=\"%s\"/>\n",
190                           DBUS_TYPE_STRING_AS_STRING);
191   g_string_append_printf (xml,
192                           "      <arg name=\"propname\" direction=\"in\" type=\"%s\"/>\n",
193                           DBUS_TYPE_STRING_AS_STRING);
194   g_string_append_printf (xml,
195                           "      <arg name=\"value\" direction=\"in\" type=\"%s\"/>\n",
196                           DBUS_TYPE_VARIANT_AS_STRING);
197   g_string_append (xml, "    </method>\n");
198   g_string_append (xml, "    <method name=\"GetAll\">\n");
199   g_string_append_printf (xml,
200                           "      <arg name=\"interface\" direction=\"in\" type=\"%s\"/>\n",
201                           DBUS_TYPE_STRING_AS_STRING);
202   g_string_append_printf (xml,
203                           "      <arg name=\"props\" direction=\"out\" type=\"%s\"/>\n",
204                           DBUS_TYPE_ARRAY_AS_STRING
205                           DBUS_DICT_ENTRY_BEGIN_CHAR_AS_STRING
206                           DBUS_TYPE_STRING_AS_STRING
207                           DBUS_TYPE_VARIANT_AS_STRING
208                           DBUS_DICT_ENTRY_END_CHAR_AS_STRING);
209
210   g_string_append (xml, "    </method>\n");
211   g_string_append (xml, "  </interface>\n");
212   /* end of stolen code */
213
214   for (l = data->interfaces; l; l = g_slist_next (l))
215     {
216       iface_def = (DRouteInterface *) l->data;
217       datum = (*iface_def->get_datum) (path, data->user_data);
218       if (!datum)
219         continue;
220       g_string_append_printf (xml, "<interface name=\"%s\">\n",
221                               iface_def->name);
222       if (iface_def->methods)
223         for (method = iface_def->methods; method->name; method++)
224           {
225             const char *p = method->arg_desc;
226             g_string_append_printf (xml, "<%s name=\"%s\">\n",
227                                     (method->type ==
228                                      DROUTE_METHOD ? "method" : "signal"),
229                                     method->name);
230             while (*p)
231               {
232                 g_string_append (xml, "<arg type=\"");
233                 g_string_append_len (xml, p, strcspn (p, ",:"));
234                 p += strcspn (p, ",");
235                 if (*p == ',')
236                   p++;
237                 if (strcspn (p, ",:") > 0)
238                   {
239                     g_string_append (xml, "\" name=\"");
240                     g_string_append_len (xml, p, strcspn (p, ",:"));
241                     p += strcspn (p, ",");
242                   }
243                 if (*p == ',')
244                   p++;
245                 g_string_append_printf (xml, "\" direction=\"%s\"/>\n",
246                                         (*p == 'o' ? "out" : "in"));
247                 p += strcspn (p, ":");
248                 if (*p == ':')
249                   p++;
250               }
251             g_string_append_printf (xml, "</%s>\n",
252                                     (method->type ==
253                                      DROUTE_METHOD ? "method" : "signal"));
254           }
255       if (iface_def->properties)
256         for (property = iface_def->properties; property->name; property++)
257           {
258             if (!property->get && !property->set)
259               continue;
260             g_string_append_printf (xml,
261                                     "<property name=\"%s\" type=\"%s\" access=\"%s%s\"/>\n",
262                                     property->name, property->type,
263                                     (property->get ? "read" : ""),
264                                     (property->set ? "write" : ""));
265           }
266       if (iface_def->free_datum)
267         (*iface_def->free_datum) (datum);
268       g_string_append (xml, "</interface>\n");
269     }
270   if (data->introspect_children)
271     {
272       (*data->introspect_children) (path, xml, data->user_data);
273     }
274   g_string_append (xml, "</node>\n");
275   str = g_string_free (xml, FALSE);
276   reply = dbus_message_new_method_return (message);
277   if (reply)
278     {
279       dbus_message_append_args (reply, DBUS_TYPE_STRING, &str,
280                                 DBUS_TYPE_INVALID);
281     }
282   g_free (str);
283   dbus_connection_send (bus, reply, NULL);
284   dbus_message_unref (reply);
285   return DBUS_HANDLER_RESULT_HANDLED;
286 }
287
288 DBusHandlerResult
289 droute_message (DBusConnection * bus, DBusMessage * message, void *user_data)
290 {
291   DRouteData *data = (DRouteData *) user_data;
292   DRouteInterface *iface_def;
293   DRouteMethod *method;
294   const char *iface = dbus_message_get_interface (message);
295   const char *member = dbus_message_get_member (message);
296   int type;
297   DBusError error;
298   DBusMessage *reply;
299
300   dbus_error_init (&error);
301   if (!member)
302     return DBUS_HANDLER_RESULT_NOT_YET_HANDLED;
303   if (iface && !strcmp (iface, "org.freedesktop.DBus.Properties"))
304     {
305       if (!strcmp (member, "GetAll"))
306         return prop_get_all (bus, message, data);
307       return prop (bus, message, data);
308     }
309   else if (iface && !strcmp (iface, "org.freedesktop.DBus.Introspectable"))
310     {
311       return introspect (bus, message, data);
312     }
313   else
314     {
315       int message_type = dbus_message_get_type (message);
316       switch (message_type)
317         {
318         case DBUS_MESSAGE_TYPE_METHOD_CALL:
319           type = DROUTE_METHOD;
320           break;
321         case DBUS_MESSAGE_TYPE_SIGNAL:
322           type = DROUTE_SIGNAL;
323           break;
324         default:
325           return DBUS_HANDLER_RESULT_NOT_YET_HANDLED;
326         }
327     }
328   if (iface)
329     {
330       iface_def = find_iface (data, iface);
331       if (!iface_def)
332         return DBUS_HANDLER_RESULT_NOT_YET_HANDLED;
333       if (iface_def->methods)
334         for (method = iface_def->methods; method->func; method++)
335           {
336             if (method->type == type && !strcmp (method->name, member))
337               {
338                 reply =
339                   (*method->func) (bus, message,
340                                    (method->wants_droute_data ? data : data->
341                                     user_data));
342                 if (reply)
343                   {
344                     dbus_connection_send (bus, reply, NULL);
345                     dbus_message_unref (reply);
346                   }
347                 return DBUS_HANDLER_RESULT_HANDLED;
348               }
349           }
350     }
351   else
352     {
353       GSList *l;
354       if (type == DBUS_MESSAGE_TYPE_SIGNAL)
355         return DBUS_HANDLER_RESULT_NOT_YET_HANDLED;
356       for (l = data->interfaces; l; l = g_slist_next (l))
357         {
358           iface_def = (DRouteInterface *) l->data;
359           if (iface_def->methods)
360             for (method = iface_def->methods; method->func; method++)
361               {
362                 if (method->type == type && !strcmp (method->name, member))
363                   {
364                     reply = (*method->func) (bus, message, data->user_data);
365                     if (reply)
366                       {
367                         dbus_connection_send (bus, reply, NULL);
368                         dbus_message_unref (reply);
369                       }
370                     return DBUS_HANDLER_RESULT_HANDLED;
371                   }
372               }
373         }
374     }
375   return DBUS_HANDLER_RESULT_HANDLED;
376 }
377
378 dbus_bool_t
379 droute_return_v_int32 (DBusMessageIter * iter, dbus_int32_t val)
380 {
381   DBusMessageIter sub;
382
383   if (!dbus_message_iter_open_container
384       (iter, DBUS_TYPE_VARIANT, DBUS_TYPE_INT32_AS_STRING, &sub))
385     {
386       return FALSE;
387     }
388   dbus_message_iter_append_basic (&sub, DBUS_TYPE_INT32, &val);
389   dbus_message_iter_close_container (iter, &sub);
390   return TRUE;
391 }
392
393 dbus_bool_t
394 droute_return_v_double (DBusMessageIter * iter, double val)
395 {
396   DBusMessageIter sub;
397
398   if (!dbus_message_iter_open_container
399       (iter, DBUS_TYPE_VARIANT, DBUS_TYPE_DOUBLE_AS_STRING, &sub))
400     {
401       return FALSE;
402     }
403   dbus_message_iter_append_basic (&sub, DBUS_TYPE_DOUBLE, &val);
404   dbus_message_iter_close_container (iter, &sub);
405   return TRUE;
406 }
407
408 /* Return a string in a variant
409  * will return an empty string if passed a NULL value */
410 dbus_bool_t
411 droute_return_v_string (DBusMessageIter * iter, const char *val)
412 {
413   DBusMessageIter sub;
414
415   if (!val)
416     val = "";
417   if (!dbus_message_iter_open_container
418       (iter, DBUS_TYPE_VARIANT, DBUS_TYPE_STRING_AS_STRING, &sub))
419     {
420       return FALSE;
421     }
422   dbus_message_iter_append_basic (&sub, DBUS_TYPE_STRING, &val);
423   dbus_message_iter_close_container (iter, &sub);
424   return TRUE;
425 }
426
427 dbus_int32_t
428 droute_get_v_int32 (DBusMessageIter * iter)
429 {
430   DBusMessageIter sub;
431   dbus_int32_t rv;
432
433   // TODO- ensure we have the correct type
434   dbus_message_iter_recurse (iter, &sub);
435   dbus_message_iter_get_basic (&sub, &rv);
436   return rv;
437 }
438
439 const char *
440 droute_get_v_string (DBusMessageIter * iter)
441 {
442   DBusMessageIter sub;
443   char *rv;
444
445   // TODO- ensure we have the correct type
446   dbus_message_iter_recurse (iter, &sub);
447   dbus_message_iter_get_basic (&sub, &rv);
448   return rv;
449 }
450
451 dbus_bool_t
452 droute_return_v_object (DBusMessageIter * iter, const char *path)
453 {
454   DBusMessageIter sub;
455
456   if (!dbus_message_iter_open_container
457       (iter, DBUS_TYPE_VARIANT, DBUS_TYPE_OBJECT_PATH_AS_STRING, &sub))
458     {
459       return FALSE;
460     }
461   dbus_message_iter_append_basic (&sub, DBUS_TYPE_OBJECT_PATH, &path);
462   dbus_message_iter_close_container (iter, &sub);
463   return TRUE;
464 }
465
466 dbus_bool_t
467 droute_add_interface (DRouteData * data, const char *name,
468                       DRouteMethod * methods, DRouteProperty * properties,
469                       DRouteGetDatumFunction get_datum,
470                       DRouteFreeDatumFunction free_datum)
471 {
472   DRouteInterface *iface =
473     (DRouteInterface *) malloc (sizeof (DRouteInterface));
474   GSList *new_list;
475
476   if (!iface)
477     return FALSE;
478   iface->name = strdup (name);
479   if (!iface->name)
480     {
481       free (iface);
482       return FALSE;
483     }
484   iface->methods = methods;
485   iface->properties = properties;
486   iface->get_datum = get_datum;
487   iface->free_datum = free_datum;
488   new_list = g_slist_append (data->interfaces, iface);
489   if (!new_list)
490     {
491       free (iface->name);
492       free (iface);
493       return FALSE;
494     }
495   data->interfaces = new_list;
496   return TRUE;
497 }