e03da3549ea77522590c08ed7356b1a900b3de99
[framework/uifw/edbus.git] / src / lib / dbus / e_dbus_object.c
1 #ifdef HAVE_CONFIG_H
2 #include "config.h"
3 #endif
4
5 #include <string.h>
6
7 #include "e_dbus_private.h"
8
9 static E_DBus_Interface *introspectable_interface = NULL;
10 static E_DBus_Interface *properties_interface = NULL;
11
12 typedef struct E_DBus_Method E_DBus_Method;
13 typedef struct E_DBus_Signal E_DBus_Signal;
14
15 Eina_Strbuf * e_dbus_object_introspect(E_DBus_Object *obj);
16
17 static void e_dbus_object_unregister(DBusConnection *conn, void *user_data);
18 static DBusHandlerResult e_dbus_object_handler(DBusConnection *conn, DBusMessage *message, void *user_data);
19
20 static void e_dbus_interface_free(E_DBus_Interface *iface);
21
22 static E_DBus_Method *e_dbus_method_new(const char *member, const char *signature, const char *reply_signature, E_DBus_Method_Cb func);
23 static void e_dbus_object_method_free(E_DBus_Method *m);
24
25 static E_DBus_Signal *e_dbus_signal_new(const char *name, const char *signature);
26 static void e_dbus_object_signal_free(E_DBus_Signal *s);
27
28 static void _introspect_indent_append(Eina_Strbuf *buf, int level);
29 static void _introspect_interface_append(Eina_Strbuf *buf, E_DBus_Interface *iface, int level);
30 static void _introspect_method_append(Eina_Strbuf *buf, E_DBus_Method *method, int level);
31 static void _introspect_signal_append(Eina_Strbuf *buf, E_DBus_Signal *signal, int level);
32 static void _introspect_arg_append(Eina_Strbuf *buf, const char *type, const char *direction, int level);
33
34
35 //static Eina_List *standard_methods = NULL;
36
37
38 static DBusObjectPathVTable vtable = {
39   e_dbus_object_unregister,
40   e_dbus_object_handler,
41   NULL,
42   NULL,
43   NULL,
44   NULL
45 };
46
47 struct E_DBus_Object
48 {
49   E_DBus_Connection *conn;
50   char *path;
51   Eina_List *interfaces;
52   char *introspection_data;
53   int introspection_dirty;
54
55   E_DBus_Object_Property_Get_Cb cb_property_get;
56   E_DBus_Object_Property_Set_Cb cb_property_set;
57
58   void *data;
59 };
60
61 struct E_DBus_Interface
62 {
63   char *name;
64   Eina_List *methods;
65   Eina_List *signals;
66   int refcount;
67 };
68
69 struct E_DBus_Method
70 {
71   char *member;
72   char *signature;
73   char *reply_signature;
74   E_DBus_Method_Cb func;
75 };
76
77 struct E_DBus_Signal
78 {
79   char *name;
80   char *signature;
81 };
82
83 static DBusMessage *
84 cb_introspect(E_DBus_Object *obj, DBusMessage *msg)
85 {
86   DBusMessage *ret;
87   Eina_Strbuf *buf;
88
89   if (obj->introspection_dirty || !obj->introspection_data)
90   {
91     buf = e_dbus_object_introspect(obj);
92     if (!buf)
93     {
94       ret = dbus_message_new_error(msg, "org.enlightenment.NotIntrospectable", "This object does not provide introspection data");
95       return ret;
96     }
97
98     if (obj->introspection_data) free(obj->introspection_data);
99     obj->introspection_data = strdup(eina_strbuf_string_get(buf));
100     eina_strbuf_free(buf);
101   }
102   //printf("XML: \n\n%s\n\n", obj->introspection_data);
103   ret = dbus_message_new_method_return(msg);
104   dbus_message_append_args(ret, DBUS_TYPE_STRING, &(obj->introspection_data), DBUS_TYPE_INVALID);
105
106   return ret;
107 }
108
109 static DBusMessage *
110 cb_properties_get(E_DBus_Object *obj, DBusMessage *msg)
111 {
112   DBusMessage *reply;
113   DBusMessageIter iter, sub;
114   DBusError err;
115   int type;
116   void *value;
117   char *property;
118
119   dbus_error_init(&err);
120   dbus_message_get_args(msg, &err, DBUS_TYPE_STRING, &property, DBUS_TYPE_INVALID);
121
122   if (dbus_error_is_set(&err))
123   {
124     return dbus_message_new_error(msg, err.name, err.message);
125   }
126
127   obj->cb_property_get(obj, property, &type, &value);
128   if (type == DBUS_TYPE_INVALID)
129   {
130     return dbus_message_new_error_printf(msg, "org.enlightenment.DBus.InvalidProperty", "The property '%s' does not exist on this object.", property);
131   }
132
133   if (dbus_type_is_basic(type))
134   {
135     reply = dbus_message_new_method_return(msg);
136     dbus_message_iter_init_append(msg, &iter);
137     dbus_message_iter_open_container(&iter, DBUS_TYPE_VARIANT, e_dbus_basic_type_as_string(type), &sub);
138     dbus_message_iter_append_basic(&sub, type, &value);
139     dbus_message_iter_close_container(&iter, &sub);
140     return reply;
141   }
142   else
143   {
144     return dbus_message_new_error(msg, "org.enlightenment.DBus.UnsupportedType", "E_DBus currently only supports properties of a basic type.");
145   }
146 }
147
148 static DBusMessage *
149 cb_properties_set(E_DBus_Object *obj, DBusMessage *msg)
150 {
151   DBusMessageIter iter, sub;
152   int type;
153   void *value;
154   char *property;
155
156   dbus_message_iter_init(msg, &iter);
157   dbus_message_iter_get_basic(&iter, &property);
158   dbus_message_iter_recurse(&iter, &sub);
159   type = dbus_message_iter_get_arg_type(&sub);
160   if (dbus_type_is_basic(type))
161   {
162     dbus_message_iter_get_basic(&sub, &value);
163     if (obj->cb_property_set(obj, property, type, value))
164     {
165       return dbus_message_new_method_return(msg);
166     }
167     else
168     {
169       return dbus_message_new_error_printf(msg, "org.enlightenment.DBus.InvalidProperty", "The property '%s' does not exist on this object.", property);
170     }
171   }
172   else
173   {
174     return dbus_message_new_error(msg, "org.enlightenment.DBus.UnsupportedType", "E_DBus currently only supports properties of a basic type.");
175   }
176
177 }
178
179 int
180 e_dbus_object_init(void)
181 {
182   introspectable_interface = e_dbus_interface_new("org.freedesktop.DBus.Introspectable");
183   properties_interface = e_dbus_interface_new(E_DBUS_FDO_INTERFACE_PROPERTIES);
184   if (!introspectable_interface || !properties_interface)
185   {
186     if (introspectable_interface) e_dbus_interface_unref(introspectable_interface);
187     introspectable_interface = NULL;
188     if (properties_interface) e_dbus_interface_unref(properties_interface);
189     properties_interface = NULL;
190     return 0;
191   }
192
193   e_dbus_interface_method_add(introspectable_interface, "Introspect", "", "s", cb_introspect);
194   e_dbus_interface_method_add(properties_interface, "Get", "s", "v", cb_properties_get);
195   e_dbus_interface_method_add(properties_interface, "Set", "sv", "", cb_properties_set);
196   return 1;
197 }
198
199 void
200 e_dbus_object_shutdown(void)
201 {
202   e_dbus_interface_unref(introspectable_interface);
203   introspectable_interface = NULL;
204
205   e_dbus_interface_unref(properties_interface);
206   properties_interface = NULL;
207 }
208
209 EAPI E_DBus_Object *
210 e_dbus_object_add(E_DBus_Connection *conn, const char *object_path, void *data)
211 {
212   E_DBus_Object *obj;
213
214   EINA_SAFETY_ON_NULL_RETURN_VAL(conn, NULL);
215   EINA_SAFETY_ON_NULL_RETURN_VAL(object_path, NULL);
216
217   obj = calloc(1, sizeof(E_DBus_Object));
218   if (!obj) return NULL;
219
220   if (!dbus_connection_register_object_path(conn->conn, object_path, &vtable, obj))
221   {
222     free(obj);
223     return NULL;
224   }
225
226   obj->conn = conn;
227   e_dbus_connection_ref(conn);
228   obj->path = strdup(object_path);
229   obj->data = data;
230   obj->interfaces = NULL;
231
232   e_dbus_object_interface_attach(obj, introspectable_interface);
233
234   return obj;
235 }
236
237 EAPI void
238 e_dbus_object_free(E_DBus_Object *obj)
239 {
240   E_DBus_Interface *iface;
241
242   if (!obj) return;
243
244   DBG("e_dbus_object_free (%s)", obj->path);
245   dbus_connection_unregister_object_path(obj->conn->conn, obj->path);
246   e_dbus_connection_close(obj->conn);
247
248   if (obj->path) free(obj->path);
249   EINA_LIST_FREE(obj->interfaces, iface)
250     e_dbus_interface_unref(iface);
251
252   if (obj->introspection_data) free(obj->introspection_data);
253
254   free(obj);
255 }
256
257 EAPI void *
258 e_dbus_object_data_get(E_DBus_Object *obj)
259 {
260   return obj->data;
261 }
262
263 EAPI E_DBus_Connection *
264 e_dbus_object_conn_get(E_DBus_Object *obj)
265 {
266   return obj->conn;
267 }
268
269 EAPI const char *
270 e_dbus_object_path_get(E_DBus_Object *obj)
271 {
272   return obj->path;
273 }
274
275 EAPI const Eina_List *
276 e_dbus_object_interfaces_get(E_DBus_Object *obj)
277 {
278   return obj->interfaces;
279 }
280
281 EAPI void
282 e_dbus_object_property_get_cb_set(E_DBus_Object *obj, E_DBus_Object_Property_Get_Cb func)
283 {
284   obj->cb_property_get = func;
285 }
286
287 EAPI void
288 e_dbus_object_property_set_cb_set(E_DBus_Object *obj, E_DBus_Object_Property_Set_Cb func)
289 {
290   obj->cb_property_set = func;
291 }
292
293 EAPI void
294 e_dbus_object_interface_attach(E_DBus_Object *obj, E_DBus_Interface *iface)
295 {
296   EINA_SAFETY_ON_NULL_RETURN(obj);
297   EINA_SAFETY_ON_NULL_RETURN(iface);
298
299   e_dbus_interface_ref(iface);
300   obj->interfaces = eina_list_append(obj->interfaces, iface);
301   obj->introspection_dirty = 1;
302   DBG("e_dbus_object_interface_attach (%s, %s) ", obj->path, iface->name);
303 }
304
305 EAPI void
306 e_dbus_object_interface_detach(E_DBus_Object *obj, E_DBus_Interface *iface)
307 {
308   E_DBus_Interface *found;
309
310   DBG("e_dbus_object_interface_detach (%s, %s) ", obj->path, iface->name);
311   found = eina_list_data_find(obj->interfaces, iface);
312   if (!found) return;
313
314   obj->interfaces = eina_list_remove(obj->interfaces, iface);
315   obj->introspection_dirty = 1;
316   e_dbus_interface_unref(iface);
317 }
318
319 EAPI void
320 e_dbus_interface_ref(E_DBus_Interface *iface)
321 {
322   iface->refcount++;
323   DBG("e_dbus_interface_ref (%s) = %d", iface->name, iface->refcount);
324 }
325
326 EAPI void
327 e_dbus_interface_unref(E_DBus_Interface *iface)
328 {
329   DBG("e_dbus_interface_unref (%s) = %d", iface->name, iface->refcount - 1);
330   if (--(iface->refcount) == 0)
331     e_dbus_interface_free(iface);
332 }
333
334 static void
335 e_dbus_interface_free(E_DBus_Interface *iface)
336 {
337   E_DBus_Method *m;
338   E_DBus_Signal *s;
339
340   if (iface->name) free(iface->name);
341   EINA_LIST_FREE(iface->methods, m)
342     e_dbus_object_method_free(m);
343   EINA_LIST_FREE(iface->signals, s)
344     e_dbus_object_signal_free(s);
345   free(iface);
346 }
347
348
349 EAPI int
350 e_dbus_interface_method_add(E_DBus_Interface *iface, const char *member, const char *signature, const char *reply_signature, E_DBus_Method_Cb func)
351 {
352   E_DBus_Method *m;
353
354   m = e_dbus_method_new(member, signature, reply_signature, func);
355   DBG("E-dbus: Add method %s: %p", member, m);
356   if (!m) return 0;
357
358   iface->methods = eina_list_append(iface->methods, m);
359   return 1;
360 }
361
362 EAPI int
363 e_dbus_interface_signal_add(E_DBus_Interface *iface, const char *name, const char *signature)
364 {
365   E_DBus_Signal *s;
366
367   s = e_dbus_signal_new(name, signature);
368   DBG("E-dbus: Add signal %s: %p", name, s);
369   if (!s) return 0;
370
371   iface->signals = eina_list_append(iface->signals, s);
372   return 1;
373 }
374
375 EAPI E_DBus_Interface *
376 e_dbus_interface_new(const char *interface)
377 {
378   E_DBus_Interface *iface;
379
380   if (!interface) return NULL;
381
382   iface = calloc(1, sizeof(E_DBus_Interface));
383   if (!iface) return NULL;
384
385   iface->refcount = 1;
386   iface->name = strdup(interface);
387   iface->methods = NULL;
388   iface->signals = NULL;
389
390   return iface;
391 }
392
393 static E_DBus_Method *
394 e_dbus_method_new(const char *member, const char *signature, const char *reply_signature, E_DBus_Method_Cb func)
395 {
396   E_DBus_Method *m;
397
398   if (!member || !func) return NULL;
399
400   if (signature && !dbus_signature_validate(signature, NULL)) return NULL;
401   if (reply_signature && !dbus_signature_validate(reply_signature, NULL)) return NULL;
402   m = calloc(1, sizeof(E_DBus_Method));
403   if (!m) return NULL;
404
405   m->member = strdup(member);
406   if (signature)
407     m->signature = strdup(signature);
408   if (reply_signature)
409     m->reply_signature = strdup(reply_signature);
410   m->func = func;
411
412   return m;
413 }
414
415 static void
416 e_dbus_object_method_free(E_DBus_Method *m)
417 {
418   if (!m) return;
419   if (m->member) free(m->member);
420   if (m->signature) free(m->signature);
421   if (m->reply_signature) free(m->reply_signature);
422
423   free(m);
424 }
425
426 static E_DBus_Signal *
427 e_dbus_signal_new(const char *name, const char *signature)
428 {
429   E_DBus_Signal *s;
430
431   if (!name) return NULL;
432
433   if (signature && !dbus_signature_validate(signature, NULL)) return NULL;
434   s = calloc(1, sizeof(E_DBus_Signal));
435   if (!s) return NULL;
436
437   s->name = strdup(name);
438   if (signature)
439     s->signature = strdup(signature);
440
441   return s;
442 }
443
444 static void
445 e_dbus_object_signal_free(E_DBus_Signal *s)
446 {
447   if (!s) return;
448   if (s->name) free(s->name);
449   if (s->signature) free(s->signature);
450   free(s);
451 }
452
453 static E_DBus_Method *
454 e_dbus_object_method_find(E_DBus_Object *obj, const char *interface, const char *member)
455 {
456   E_DBus_Method *m;
457   E_DBus_Interface *iface;
458   Eina_List *l, *ll;
459
460   if (!obj || !member) return NULL;
461
462   EINA_LIST_FOREACH(obj->interfaces, l, iface)
463   {
464     if (strcmp(interface, iface->name)) continue;
465     EINA_LIST_FOREACH(iface->methods, ll, m)
466     {
467       if (!strcmp(member, m->member))
468         return m;
469     }
470   }
471   return NULL;
472 }
473
474 static DBusHandlerResult
475 e_dbus_object_handler(DBusConnection *conn, DBusMessage *message, void *user_data) 
476 {
477   E_DBus_Object *obj;
478   E_DBus_Method *m;
479   DBusMessage *reply;
480   dbus_uint32_t serial;
481
482   obj = user_data;
483   if (!obj)
484     return DBUS_HANDLER_RESULT_NOT_YET_HANDLED;
485
486   m = e_dbus_object_method_find(obj, dbus_message_get_interface(message), dbus_message_get_member(message));
487
488   /* XXX should this send an 'invalid method' error instead? */
489   if (!m) 
490     return DBUS_HANDLER_RESULT_NOT_YET_HANDLED;
491
492   if (m->signature && !dbus_message_has_signature(message, m->signature))
493     reply = dbus_message_new_error_printf(message, "org.enlightenment.InvalidSignature", "Expected signature: %s", m->signature);
494   else
495     reply = m->func(obj, message);
496
497   /* user can choose reply later */
498   if (!reply)
499     return DBUS_HANDLER_RESULT_HANDLED;
500
501   dbus_connection_send(conn, reply, &serial);
502   dbus_message_unref(reply);
503
504   return DBUS_HANDLER_RESULT_HANDLED;
505 }
506
507 static void
508 e_dbus_object_unregister(DBusConnection *conn __UNUSED__, void *user_data __UNUSED__)
509 {
510   /* free up the object struct? */
511 }
512
513 Eina_Strbuf *
514 e_dbus_object_introspect(E_DBus_Object *obj)
515 {
516   Eina_Strbuf *buf;
517   int level = 0;
518   E_DBus_Interface *iface;
519   Eina_List *l;
520
521   buf = eina_strbuf_new();
522
523   /* Doctype */
524   eina_strbuf_append(buf, "<!DOCTYPE node PUBLIC \"-//freedesktop//DTD D-BUS Object Introspection 1.0//EN\"\n \"http://www.freedesktop.org/standards/dbus/1.0/introspect.dtd\">\n");
525
526   eina_strbuf_append(buf, "<node name=\"");
527   eina_strbuf_append(buf, obj->path);
528   eina_strbuf_append(buf, "\">\n");
529   level++;
530
531   EINA_LIST_FOREACH(obj->interfaces, l, iface)
532     _introspect_interface_append(buf, iface, level);
533
534   eina_strbuf_append(buf, "</node>\n");
535   return buf;
536 }
537
538 static void
539 _introspect_indent_append(Eina_Strbuf *buf, int level)
540 {
541   /* XXX optimize this? */
542   int i = level * 2;
543   while (i-- > 0)
544     eina_strbuf_append_char(buf, ' ');
545 }
546 static void
547 _introspect_interface_append(Eina_Strbuf *buf, E_DBus_Interface *iface, int level)
548 {
549   E_DBus_Method *m;
550   E_DBus_Signal *s;
551   Eina_List *l;
552
553   _introspect_indent_append(buf, level);
554   eina_strbuf_append(buf, "<interface name=\"");
555   eina_strbuf_append(buf, iface->name);
556   eina_strbuf_append(buf, "\">\n");
557   level++;
558
559   DBG("introspect iface: %s", iface->name);
560   EINA_LIST_FOREACH(iface->methods, l, m)
561     _introspect_method_append(buf, m, level);
562   EINA_LIST_FOREACH(iface->signals, l, s)
563     _introspect_signal_append(buf, s, level);
564
565   level--;
566   _introspect_indent_append(buf, level);
567   eina_strbuf_append(buf, "</interface>\n");
568 }
569 static void
570 _introspect_method_append(Eina_Strbuf *buf, E_DBus_Method *method, int level)
571 {
572   DBusSignatureIter iter;
573   char *type;
574
575   _introspect_indent_append(buf, level);
576   DBG("introspect method: %s\n", method->member);
577   eina_strbuf_append(buf, "<method name=\"");
578   eina_strbuf_append(buf, method->member);
579   eina_strbuf_append(buf, "\">\n");
580   level++;
581
582   /* append args */
583   if (method->signature && 
584       method->signature[0] &&
585       dbus_signature_validate(method->signature, NULL))
586   {
587     dbus_signature_iter_init(&iter, method->signature);
588     while ((type = dbus_signature_iter_get_signature(&iter)))
589     {
590       _introspect_arg_append(buf, type, "in", level);
591
592       dbus_free(type);
593       if (!dbus_signature_iter_next(&iter)) break;
594     }
595   }
596
597   /* append reply args */
598   if (method->reply_signature &&
599       method->reply_signature[0] &&
600       dbus_signature_validate(method->reply_signature, NULL))
601   {
602     dbus_signature_iter_init(&iter, method->reply_signature);
603     while ((type = dbus_signature_iter_get_signature(&iter)))
604     {
605       _introspect_arg_append(buf, type, "out", level);
606
607       dbus_free(type);
608       if (!dbus_signature_iter_next(&iter)) break;
609     }
610   }
611
612   level--;
613   _introspect_indent_append(buf, level);
614   eina_strbuf_append(buf, "</method>\n");
615 }
616
617 static void
618 _introspect_signal_append(Eina_Strbuf *buf, E_DBus_Signal *s, int level)
619 {
620   DBusSignatureIter iter;
621   char *type;
622
623   _introspect_indent_append(buf, level);
624   DBG("introspect signal: %s", s->name);
625   eina_strbuf_append(buf, "<signal name=\"");
626   eina_strbuf_append(buf, s->name);
627   eina_strbuf_append(buf, "\">\n");
628   level++;
629
630   /* append args */
631   if (s->signature &&
632       s->signature[0] &&
633       dbus_signature_validate(s->signature, NULL))
634   {
635     dbus_signature_iter_init(&iter, s->signature);
636     while ((type = dbus_signature_iter_get_signature(&iter)))
637     {
638       _introspect_arg_append(buf, type, NULL, level);
639
640       dbus_free(type);
641       if (!dbus_signature_iter_next(&iter)) break;
642     }
643   }
644
645   level--;
646   _introspect_indent_append(buf, level);
647   eina_strbuf_append(buf, "</signal>\n");
648 }
649
650 static void
651 _introspect_arg_append(Eina_Strbuf *buf, const char *type, const char *direction, int level)
652 {
653   _introspect_indent_append(buf, level);
654   eina_strbuf_append(buf, "<arg type=\"");
655   eina_strbuf_append(buf, type);
656   if (direction)
657   {
658     eina_strbuf_append(buf, "\" direction=\"");
659     eina_strbuf_append(buf, direction);
660   }
661   eina_strbuf_append(buf, "\"/>\n");
662 }
663