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