Use calloc when allocating app data
[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 (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   if (!set)
117     dbus_message_iter_init_append (reply, &iter);
118   iface_def = find_iface (data, iface);
119   if (!iface_def)
120     return DBUS_HANDLER_RESULT_NOT_YET_HANDLED;
121   if (iface_def->properties)
122     for (prop = iface_def->properties;
123          prop->name && strcmp (prop->name, member); prop++)
124       if (!prop || !prop->name)
125         return DBUS_HANDLER_RESULT_NOT_YET_HANDLED;
126   if (set)
127     {
128       if (!prop->set)
129         return DBUS_HANDLER_RESULT_NOT_YET_HANDLED;
130       (*prop->set) (path, &iter, data->user_data);
131     }
132   else
133     {
134       if (!prop->get)
135         return DBUS_HANDLER_RESULT_NOT_YET_HANDLED;
136       (*prop->get) (path, &iter, data->user_data);
137     }
138   dbus_connection_send (bus, reply, NULL);
139   dbus_message_unref (reply);
140   return DBUS_HANDLER_RESULT_HANDLED;
141 }
142
143 static DBusHandlerResult
144 introspect (DBusConnection * bus, DBusMessage * message, DRouteData * data)
145 {
146   const char *path = dbus_message_get_path (message);
147   GString *xml;
148   DRouteInterface *iface_def;
149   DRouteMethod *method;
150   DRouteProperty *property;
151   char *str;
152   DBusMessage *reply;
153   GSList *l;
154   void *datum;
155
156   if (strcmp (dbus_message_get_member (message), "Introspect"))
157     {
158       return DBUS_HANDLER_RESULT_NOT_YET_HANDLED;
159     }
160   xml = g_string_new (NULL);
161   /* below code stolen from dbus-glib */
162   g_string_append (xml, DBUS_INTROSPECT_1_0_XML_DOCTYPE_DECL_NODE);
163   g_string_append_printf (xml, "<node name=\"%s\">\n", path);
164
165   /* We are introspectable, though I guess that was pretty obvious */
166   g_string_append_printf (xml, "  <interface name=\"%s\">\n",
167                           DBUS_INTERFACE_INTROSPECTABLE);
168   g_string_append (xml, "    <method name=\"Introspect\">\n");
169   g_string_append_printf (xml,
170                           "      <arg name=\"data\" direction=\"out\" type=\"%s\"/>\n",
171                           DBUS_TYPE_STRING_AS_STRING);
172   g_string_append (xml, "    </method>\n");
173   g_string_append (xml, "  </interface>\n");
174
175   /* We support get/set/getall properties */
176   g_string_append_printf (xml, "  <interface name=\"%s\">\n",
177                           DBUS_INTERFACE_PROPERTIES);
178   g_string_append (xml, "    <method name=\"Get\">\n");
179   g_string_append_printf (xml,
180                           "      <arg name=\"interface\" direction=\"in\" type=\"%s\"/>\n",
181                           DBUS_TYPE_STRING_AS_STRING);
182   g_string_append_printf (xml,
183                           "      <arg name=\"propname\" direction=\"in\" type=\"%s\"/>\n",
184                           DBUS_TYPE_STRING_AS_STRING);
185   g_string_append_printf (xml,
186                           "      <arg name=\"value\" direction=\"out\" type=\"%s\"/>\n",
187                           DBUS_TYPE_VARIANT_AS_STRING);
188   g_string_append (xml, "    </method>\n");
189   g_string_append (xml, "    <method name=\"Set\">\n");
190   g_string_append_printf (xml,
191                           "      <arg name=\"interface\" direction=\"in\" type=\"%s\"/>\n",
192                           DBUS_TYPE_STRING_AS_STRING);
193   g_string_append_printf (xml,
194                           "      <arg name=\"propname\" direction=\"in\" type=\"%s\"/>\n",
195                           DBUS_TYPE_STRING_AS_STRING);
196   g_string_append_printf (xml,
197                           "      <arg name=\"value\" direction=\"in\" type=\"%s\"/>\n",
198                           DBUS_TYPE_VARIANT_AS_STRING);
199   g_string_append (xml, "    </method>\n");
200   g_string_append (xml, "    <method name=\"GetAll\">\n");
201   g_string_append_printf (xml,
202                           "      <arg name=\"interface\" direction=\"in\" type=\"%s\"/>\n",
203                           DBUS_TYPE_STRING_AS_STRING);
204   g_string_append_printf (xml,
205                           "      <arg name=\"props\" direction=\"out\" type=\"%s\"/>\n",
206                           DBUS_TYPE_ARRAY_AS_STRING
207                           DBUS_DICT_ENTRY_BEGIN_CHAR_AS_STRING
208                           DBUS_TYPE_STRING_AS_STRING
209                           DBUS_TYPE_VARIANT_AS_STRING
210                           DBUS_DICT_ENTRY_END_CHAR_AS_STRING);
211
212   g_string_append (xml, "    </method>\n");
213   g_string_append (xml, "  </interface>\n");
214   /* end of stolen code */
215
216   for (l = data->interfaces; l; l = g_slist_next (l))
217     {
218       iface_def = (DRouteInterface *) l->data;
219       if (iface_def->get_datum)
220         {
221           datum = (*iface_def->get_datum) (path, data->user_data);
222           if (!datum)
223             continue;
224         }
225       g_string_append_printf (xml, "<interface name=\"%s\">\n",
226                               iface_def->name);
227       if (iface_def->methods)
228         for (method = iface_def->methods; method->name; method++)
229           {
230             const char *p = method->arg_desc;
231             g_string_append_printf (xml, "<%s name=\"%s\">\n",
232                                     (method->type ==
233                                      DROUTE_METHOD ? "method" : "signal"),
234                                     method->name);
235             while (*p)
236               {
237                 g_string_append (xml, "<arg type=\"");
238                 g_string_append_len (xml, p, strcspn (p, ",:"));
239                 p += strcspn (p, ",");
240                 if (*p == ',')
241                   p++;
242                 if (strcspn (p, ",:") > 0)
243                   {
244                     g_string_append (xml, "\" name=\"");
245                     g_string_append_len (xml, p, strcspn (p, ",:"));
246                     p += strcspn (p, ",");
247                   }
248                 if (*p == ',')
249                   p++;
250                 g_string_append_printf (xml, "\" direction=\"%s\"/>\n",
251                                         (*p == 'o' ? "out" : "in"));
252                 p += strcspn (p, ":");
253                 if (*p == ':')
254                   p++;
255               }
256             g_string_append_printf (xml, "</%s>\n",
257                                     (method->type ==
258                                      DROUTE_METHOD ? "method" : "signal"));
259           }
260       if (iface_def->properties)
261         for (property = iface_def->properties; property->name; property++)
262           {
263             if (!property->get && !property->set)
264               continue;
265             g_string_append_printf (xml,
266                                     "<property name=\"%s\" type=\"%s\" access=\"%s%s\"/>\n",
267                                     property->name, property->type,
268                                     (property->get ? "read" : ""),
269                                     (property->set ? "write" : ""));
270           }
271       if (iface_def->free_datum)
272         (*iface_def->free_datum) (datum);
273       g_string_append (xml, "</interface>\n");
274     }
275   if (data->introspect_children)
276     {
277       (*data->introspect_children) (path, xml, data->user_data);
278     }
279   g_string_append (xml, "</node>\n");
280   str = g_string_free (xml, FALSE);
281   reply = dbus_message_new_method_return (message);
282   if (reply)
283     {
284       dbus_message_append_args (reply, DBUS_TYPE_STRING, &str,
285                                 DBUS_TYPE_INVALID);
286     }
287   g_free (str);
288   dbus_connection_send (bus, reply, NULL);
289   dbus_message_unref (reply);
290   return DBUS_HANDLER_RESULT_HANDLED;
291 }
292
293 DBusHandlerResult
294 droute_message (DBusConnection * bus, DBusMessage * message, void *user_data)
295 {
296   DRouteData *data = (DRouteData *) user_data;
297   DRouteInterface *iface_def;
298   DRouteMethod *method;
299   const char *iface = dbus_message_get_interface (message);
300   const char *member = dbus_message_get_member (message);
301   int type;
302   DBusError error;
303   DBusMessage *reply;
304
305   dbus_error_init (&error);
306   if (!member)
307     return DBUS_HANDLER_RESULT_NOT_YET_HANDLED;
308   if (iface && !strcmp (iface, "org.freedesktop.DBus.Properties"))
309     {
310       if (!strcmp (member, "GetAll"))
311         return prop_get_all (bus, message, data);
312       return prop (bus, message, data);
313     }
314   else if (iface && !strcmp (iface, "org.freedesktop.DBus.Introspectable"))
315     {
316       return introspect (bus, message, data);
317     }
318   else
319     {
320       int message_type = dbus_message_get_type (message);
321       switch (message_type)
322         {
323         case DBUS_MESSAGE_TYPE_METHOD_CALL:
324           type = DROUTE_METHOD;
325           break;
326         case DBUS_MESSAGE_TYPE_SIGNAL:
327           type = DROUTE_SIGNAL;
328           break;
329         default:
330           return DBUS_HANDLER_RESULT_NOT_YET_HANDLED;
331         }
332     }
333   if (iface)
334     {
335       iface_def = find_iface (data, iface);
336       if (!iface_def)
337         return DBUS_HANDLER_RESULT_NOT_YET_HANDLED;
338       if (iface_def->methods)
339         for (method = iface_def->methods; method->func; method++)
340           {
341             if (method->type == type && !strcmp (method->name, member))
342               {
343                 reply =
344                   (*method->func) (bus, message,
345                                    (method->wants_droute_data ? data : data->
346                                     user_data));
347                 if (reply)
348                   {
349                     dbus_connection_send (bus, reply, NULL);
350                     dbus_message_unref (reply);
351                   }
352                 return DBUS_HANDLER_RESULT_HANDLED;
353               }
354           }
355     }
356   else
357     {
358       GSList *l;
359       if (type == DBUS_MESSAGE_TYPE_SIGNAL)
360         return DBUS_HANDLER_RESULT_NOT_YET_HANDLED;
361       for (l = data->interfaces; l; l = g_slist_next (l))
362         {
363           iface_def = (DRouteInterface *) l->data;
364           if (iface_def->methods)
365             for (method = iface_def->methods; method->func; method++)
366               {
367                 if (method->type == type && !strcmp (method->name, member))
368                   {
369                     reply = (*method->func) (bus, message, data->user_data);
370                     if (reply)
371                       {
372                         dbus_connection_send (bus, reply, NULL);
373                         dbus_message_unref (reply);
374                       }
375                     return DBUS_HANDLER_RESULT_HANDLED;
376                   }
377               }
378         }
379     }
380   return DBUS_HANDLER_RESULT_HANDLED;
381 }
382
383 dbus_bool_t
384 droute_return_v_int32 (DBusMessageIter * iter, dbus_int32_t val)
385 {
386   DBusMessageIter sub;
387
388   if (!dbus_message_iter_open_container
389       (iter, DBUS_TYPE_VARIANT, DBUS_TYPE_INT32_AS_STRING, &sub))
390     {
391       return FALSE;
392     }
393   dbus_message_iter_append_basic (&sub, DBUS_TYPE_INT32, &val);
394   dbus_message_iter_close_container (iter, &sub);
395   return TRUE;
396 }
397
398 dbus_bool_t
399 droute_return_v_double (DBusMessageIter * iter, double val)
400 {
401   DBusMessageIter sub;
402
403   if (!dbus_message_iter_open_container
404       (iter, DBUS_TYPE_VARIANT, DBUS_TYPE_DOUBLE_AS_STRING, &sub))
405     {
406       return FALSE;
407     }
408   dbus_message_iter_append_basic (&sub, DBUS_TYPE_DOUBLE, &val);
409   dbus_message_iter_close_container (iter, &sub);
410   return TRUE;
411 }
412
413 /* Return a string in a variant
414  * will return an empty string if passed a NULL value */
415 dbus_bool_t
416 droute_return_v_string (DBusMessageIter * iter, const char *val)
417 {
418   DBusMessageIter sub;
419
420   if (!val)
421     val = "";
422   if (!dbus_message_iter_open_container
423       (iter, DBUS_TYPE_VARIANT, DBUS_TYPE_STRING_AS_STRING, &sub))
424     {
425       return FALSE;
426     }
427   dbus_message_iter_append_basic (&sub, DBUS_TYPE_STRING, &val);
428   dbus_message_iter_close_container (iter, &sub);
429   return TRUE;
430 }
431
432 dbus_int32_t
433 droute_get_v_int32 (DBusMessageIter * iter)
434 {
435   DBusMessageIter sub;
436   dbus_int32_t rv;
437
438   // TODO- ensure we have the correct type
439   dbus_message_iter_recurse (iter, &sub);
440   dbus_message_iter_get_basic (&sub, &rv);
441   return rv;
442 }
443
444 const char *
445 droute_get_v_string (DBusMessageIter * iter)
446 {
447   DBusMessageIter sub;
448   char *rv;
449
450   // TODO- ensure we have the correct type
451   dbus_message_iter_recurse (iter, &sub);
452   dbus_message_iter_get_basic (&sub, &rv);
453   return rv;
454 }
455
456 dbus_bool_t
457 droute_return_v_object (DBusMessageIter * iter, const char *path)
458 {
459   DBusMessageIter sub;
460
461   if (!dbus_message_iter_open_container
462       (iter, DBUS_TYPE_VARIANT, DBUS_TYPE_OBJECT_PATH_AS_STRING, &sub))
463     {
464       return FALSE;
465     }
466   dbus_message_iter_append_basic (&sub, DBUS_TYPE_OBJECT_PATH, &path);
467   dbus_message_iter_close_container (iter, &sub);
468   return TRUE;
469 }
470
471 dbus_bool_t
472 droute_add_interface (DRouteData * data, const char *name,
473                       DRouteMethod * methods, DRouteProperty * properties,
474                       DRouteGetDatumFunction get_datum,
475                       DRouteFreeDatumFunction free_datum)
476 {
477   DRouteInterface *iface =
478     (DRouteInterface *) malloc (sizeof (DRouteInterface));
479   GSList *new_list;
480
481   if (!iface)
482     return FALSE;
483   iface->name = strdup (name);
484   if (!iface->name)
485     {
486       free (iface);
487       return FALSE;
488     }
489   iface->methods = methods;
490   iface->properties = properties;
491   iface->get_datum = get_datum;
492   iface->free_datum = free_datum;
493   new_list = g_slist_append (data->interfaces, iface);
494   if (!new_list)
495     {
496       free (iface->name);
497       free (iface);
498       return FALSE;
499     }
500   data->interfaces = new_list;
501   return TRUE;
502 }