doxy->h
[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   obj = calloc(1, sizeof(E_DBus_Object));
215   if (!obj) return NULL;
216
217   if (!dbus_connection_register_object_path(conn->conn, object_path, &vtable, obj))
218   {
219     free(obj);
220     return NULL;
221   }
222
223   obj->conn = conn;
224   e_dbus_connection_ref(conn);
225   obj->path = strdup(object_path);
226   obj->data = data;
227   obj->interfaces = NULL;
228
229   e_dbus_object_interface_attach(obj, introspectable_interface);
230
231   return obj;
232 }
233
234 EAPI void
235 e_dbus_object_free(E_DBus_Object *obj)
236 {
237   E_DBus_Interface *iface;
238
239   if (!obj) return;
240
241   DBG("e_dbus_object_free (%s)", obj->path);
242   dbus_connection_unregister_object_path(obj->conn->conn, obj->path);
243   e_dbus_connection_close(obj->conn);
244
245   if (obj->path) free(obj->path);
246   EINA_LIST_FREE(obj->interfaces, iface)
247     e_dbus_interface_unref(iface);
248
249   if (obj->introspection_data) free(obj->introspection_data);
250
251   free(obj);
252 }
253
254 EAPI void *
255 e_dbus_object_data_get(E_DBus_Object *obj)
256 {
257   return obj->data;
258 }
259
260 EAPI E_DBus_Connection *
261 e_dbus_object_conn_get(E_DBus_Object *obj)
262 {
263   return obj->conn;
264 }
265
266 EAPI const char *
267 e_dbus_object_path_get(E_DBus_Object *obj)
268 {
269   return obj->path;
270 }
271
272 EAPI const Eina_List *
273 e_dbus_object_interfaces_get(E_DBus_Object *obj)
274 {
275   return obj->interfaces;
276 }
277
278 EAPI void
279 e_dbus_object_property_get_cb_set(E_DBus_Object *obj, E_DBus_Object_Property_Get_Cb func)
280 {
281   obj->cb_property_get = func;
282 }
283
284 EAPI void
285 e_dbus_object_property_set_cb_set(E_DBus_Object *obj, E_DBus_Object_Property_Set_Cb func)
286 {
287   obj->cb_property_set = func;
288 }
289
290 EAPI void
291 e_dbus_object_interface_attach(E_DBus_Object *obj, E_DBus_Interface *iface)
292 {
293   e_dbus_interface_ref(iface);
294   obj->interfaces = eina_list_append(obj->interfaces, iface);
295   obj->introspection_dirty = 1;
296   DBG("e_dbus_object_interface_attach (%s, %s) ", obj->path, iface->name);
297 }
298
299 EAPI void
300 e_dbus_object_interface_detach(E_DBus_Object *obj, E_DBus_Interface *iface)
301 {
302   E_DBus_Interface *found;
303
304   DBG("e_dbus_object_interface_detach (%s, %s) ", obj->path, iface->name);
305   found = eina_list_data_find(obj->interfaces, iface);
306   if (!found) return;
307
308   obj->interfaces = eina_list_remove(obj->interfaces, iface);
309   obj->introspection_dirty = 1;
310   e_dbus_interface_unref(iface);
311 }
312
313 EAPI void
314 e_dbus_interface_ref(E_DBus_Interface *iface)
315 {
316   iface->refcount++;
317   DBG("e_dbus_interface_ref (%s) = %d", iface->name, iface->refcount);
318 }
319
320 EAPI void
321 e_dbus_interface_unref(E_DBus_Interface *iface)
322 {
323   DBG("e_dbus_interface_unref (%s) = %d", iface->name, iface->refcount - 1);
324   if (--(iface->refcount) == 0)
325     e_dbus_interface_free(iface);
326 }
327
328 static void
329 e_dbus_interface_free(E_DBus_Interface *iface)
330 {
331   E_DBus_Method *m;
332   E_DBus_Signal *s;
333
334   if (iface->name) free(iface->name);
335   EINA_LIST_FREE(iface->methods, m)
336     e_dbus_object_method_free(m);
337   EINA_LIST_FREE(iface->signals, s)
338     e_dbus_object_signal_free(s);
339   free(iface);
340 }
341
342
343 EAPI int
344 e_dbus_interface_method_add(E_DBus_Interface *iface, const char *member, const char *signature, const char *reply_signature, E_DBus_Method_Cb func)
345 {
346   E_DBus_Method *m;
347
348   m = e_dbus_method_new(member, signature, reply_signature, func);
349   DBG("E-dbus: Add method %s: %p", member, m);
350   if (!m) return 0;
351
352   iface->methods = eina_list_append(iface->methods, m);
353   return 1;
354 }
355
356 EAPI int
357 e_dbus_interface_signal_add(E_DBus_Interface *iface, const char *name, const char *signature)
358 {
359   E_DBus_Signal *s;
360
361   s = e_dbus_signal_new(name, signature);
362   DBG("E-dbus: Add signal %s: %p", name, s);
363   if (!s) return 0;
364
365   iface->signals = eina_list_append(iface->signals, s);
366   return 1;
367 }
368
369 EAPI E_DBus_Interface *
370 e_dbus_interface_new(const char *interface)
371 {
372   E_DBus_Interface *iface;
373
374   if (!interface) return NULL;
375
376   iface = calloc(1, sizeof(E_DBus_Interface));
377   if (!iface) return NULL;
378
379   iface->refcount = 1;
380   iface->name = strdup(interface);
381   iface->methods = NULL;
382   iface->signals = NULL;
383
384   return iface;
385 }
386
387 static E_DBus_Method *
388 e_dbus_method_new(const char *member, const char *signature, const char *reply_signature, E_DBus_Method_Cb func)
389 {
390   E_DBus_Method *m;
391
392   if (!member || !func) return NULL;
393
394   if (signature && !dbus_signature_validate(signature, NULL)) return NULL;
395   if (reply_signature && !dbus_signature_validate(reply_signature, NULL)) return NULL;
396   m = calloc(1, sizeof(E_DBus_Method));
397   if (!m) return NULL;
398
399   m->member = strdup(member);
400   if (signature)
401     m->signature = strdup(signature);
402   if (reply_signature)
403     m->reply_signature = strdup(reply_signature);
404   m->func = func;
405
406   return m;
407 }
408
409 static void
410 e_dbus_object_method_free(E_DBus_Method *m)
411 {
412   if (!m) return;
413   if (m->member) free(m->member);
414   if (m->signature) free(m->signature);
415   if (m->reply_signature) free(m->reply_signature);
416
417   free(m);
418 }
419
420 static E_DBus_Signal *
421 e_dbus_signal_new(const char *name, const char *signature)
422 {
423   E_DBus_Signal *s;
424
425   if (!name) return NULL;
426
427   if (signature && !dbus_signature_validate(signature, NULL)) return NULL;
428   s = calloc(1, sizeof(E_DBus_Signal));
429   if (!s) return NULL;
430
431   s->name = strdup(name);
432   if (signature)
433     s->signature = strdup(signature);
434
435   return s;
436 }
437
438 static void
439 e_dbus_object_signal_free(E_DBus_Signal *s)
440 {
441   if (!s) return;
442   if (s->name) free(s->name);
443   if (s->signature) free(s->signature);
444   free(s);
445 }
446
447 static E_DBus_Method *
448 e_dbus_object_method_find(E_DBus_Object *obj, const char *interface, const char *member)
449 {
450   E_DBus_Method *m;
451   E_DBus_Interface *iface;
452   Eina_List *l, *ll;
453
454   if (!obj || !member) return NULL;
455
456   EINA_LIST_FOREACH(obj->interfaces, l, iface)
457   {
458     if (strcmp(interface, iface->name)) continue;
459     EINA_LIST_FOREACH(iface->methods, ll, m)
460     {
461       if (!strcmp(member, m->member))
462         return m;
463     }
464   }
465   return NULL;
466 }
467
468 static DBusHandlerResult
469 e_dbus_object_handler(DBusConnection *conn, DBusMessage *message, void *user_data) 
470 {
471   E_DBus_Object *obj;
472   E_DBus_Method *m;
473   DBusMessage *reply;
474   dbus_uint32_t serial;
475
476   obj = user_data;
477   if (!obj)
478     return DBUS_HANDLER_RESULT_NOT_YET_HANDLED;
479
480   m = e_dbus_object_method_find(obj, dbus_message_get_interface(message), dbus_message_get_member(message));
481
482   /* XXX should this send an 'invalid method' error instead? */
483   if (!m) 
484     return DBUS_HANDLER_RESULT_NOT_YET_HANDLED;
485
486   if (m->signature && !dbus_message_has_signature(message, m->signature))
487     reply = dbus_message_new_error_printf(message, "org.enlightenment.InvalidSignature", "Expected signature: %s", m->signature);
488   else
489     reply = m->func(obj, message);
490
491   /* user can choose reply later */
492   if (!reply)
493     return DBUS_HANDLER_RESULT_HANDLED;
494
495   dbus_connection_send(conn, reply, &serial);
496   dbus_message_unref(reply);
497
498   return DBUS_HANDLER_RESULT_HANDLED;
499 }
500
501 static void
502 e_dbus_object_unregister(DBusConnection *conn __UNUSED__, void *user_data __UNUSED__)
503 {
504   /* free up the object struct? */
505 }
506
507 Eina_Strbuf *
508 e_dbus_object_introspect(E_DBus_Object *obj)
509 {
510   Eina_Strbuf *buf;
511   int level = 0;
512   E_DBus_Interface *iface;
513   Eina_List *l;
514
515   buf = eina_strbuf_new();
516
517   /* Doctype */
518   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");
519
520   eina_strbuf_append(buf, "<node name=\"");
521   eina_strbuf_append(buf, obj->path);
522   eina_strbuf_append(buf, "\">\n");
523   level++;
524
525   EINA_LIST_FOREACH(obj->interfaces, l, iface)
526     _introspect_interface_append(buf, iface, level);
527
528   eina_strbuf_append(buf, "</node>\n");
529   return buf;
530 }
531
532 static void
533 _introspect_indent_append(Eina_Strbuf *buf, int level)
534 {
535   /* XXX optimize this? */
536   int i = level * 2;
537   while (i-- > 0)
538     eina_strbuf_append_char(buf, ' ');
539 }
540 static void
541 _introspect_interface_append(Eina_Strbuf *buf, E_DBus_Interface *iface, int level)
542 {
543   E_DBus_Method *m;
544   E_DBus_Signal *s;
545   Eina_List *l;
546
547   _introspect_indent_append(buf, level);
548   eina_strbuf_append(buf, "<interface name=\"");
549   eina_strbuf_append(buf, iface->name);
550   eina_strbuf_append(buf, "\">\n");
551   level++;
552
553   DBG("introspect iface: %s", iface->name);
554   EINA_LIST_FOREACH(iface->methods, l, m)
555     _introspect_method_append(buf, m, level);
556   EINA_LIST_FOREACH(iface->signals, l, s)
557     _introspect_signal_append(buf, s, level);
558
559   level--;
560   _introspect_indent_append(buf, level);
561   eina_strbuf_append(buf, "</interface>\n");
562 }
563 static void
564 _introspect_method_append(Eina_Strbuf *buf, E_DBus_Method *method, int level)
565 {
566   DBusSignatureIter iter;
567   char *type;
568
569   _introspect_indent_append(buf, level);
570   DBG("introspect method: %s\n", method->member);
571   eina_strbuf_append(buf, "<method name=\"");
572   eina_strbuf_append(buf, method->member);
573   eina_strbuf_append(buf, "\">\n");
574   level++;
575
576   /* append args */
577   if (method->signature && 
578       method->signature[0] &&
579       dbus_signature_validate(method->signature, NULL))
580   {
581     dbus_signature_iter_init(&iter, method->signature);
582     while ((type = dbus_signature_iter_get_signature(&iter)))
583     {
584       _introspect_arg_append(buf, type, "in", level);
585
586       dbus_free(type);
587       if (!dbus_signature_iter_next(&iter)) break;
588     }
589   }
590
591   /* append reply args */
592   if (method->reply_signature &&
593       method->reply_signature[0] &&
594       dbus_signature_validate(method->reply_signature, NULL))
595   {
596     dbus_signature_iter_init(&iter, method->reply_signature);
597     while ((type = dbus_signature_iter_get_signature(&iter)))
598     {
599       _introspect_arg_append(buf, type, "out", level);
600
601       dbus_free(type);
602       if (!dbus_signature_iter_next(&iter)) break;
603     }
604   }
605
606   level--;
607   _introspect_indent_append(buf, level);
608   eina_strbuf_append(buf, "</method>\n");
609 }
610
611 static void
612 _introspect_signal_append(Eina_Strbuf *buf, E_DBus_Signal *s, int level)
613 {
614   DBusSignatureIter iter;
615   char *type;
616
617   _introspect_indent_append(buf, level);
618   DBG("introspect signal: %s", s->name);
619   eina_strbuf_append(buf, "<signal name=\"");
620   eina_strbuf_append(buf, s->name);
621   eina_strbuf_append(buf, "\">\n");
622   level++;
623
624   /* append args */
625   if (s->signature &&
626       s->signature[0] &&
627       dbus_signature_validate(s->signature, NULL))
628   {
629     dbus_signature_iter_init(&iter, s->signature);
630     while ((type = dbus_signature_iter_get_signature(&iter)))
631     {
632       _introspect_arg_append(buf, type, NULL, level);
633
634       dbus_free(type);
635       if (!dbus_signature_iter_next(&iter)) break;
636     }
637   }
638
639   level--;
640   _introspect_indent_append(buf, level);
641   eina_strbuf_append(buf, "</signal>\n");
642 }
643
644 static void
645 _introspect_arg_append(Eina_Strbuf *buf, const char *type, const char *direction, int level)
646 {
647   _introspect_indent_append(buf, level);
648   eina_strbuf_append(buf, "<arg type=\"");
649   eina_strbuf_append(buf, type);
650   if (direction)
651   {
652     eina_strbuf_append(buf, "\" direction=\"");
653     eina_strbuf_append(buf, direction);
654   }
655   eina_strbuf_append(buf, "\"/>\n");
656 }
657