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