7 #include "e_dbus_private.h"
9 static E_DBus_Interface *introspectable_interface = NULL;
10 static E_DBus_Interface *properties_interface = NULL;
12 typedef struct E_DBus_Method E_DBus_Method;
13 typedef struct E_DBus_Signal E_DBus_Signal;
15 Eina_Strbuf * e_dbus_object_introspect(E_DBus_Object *obj);
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);
20 static void e_dbus_interface_free(E_DBus_Interface *iface);
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);
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);
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);
35 //static Eina_List *standard_methods = NULL;
38 static DBusObjectPathVTable vtable = {
39 e_dbus_object_unregister,
40 e_dbus_object_handler,
49 E_DBus_Connection *conn;
51 Eina_List *interfaces;
52 char *introspection_data;
53 int introspection_dirty;
55 E_DBus_Object_Property_Get_Cb cb_property_get;
56 E_DBus_Object_Property_Set_Cb cb_property_set;
61 struct E_DBus_Interface
73 char *reply_signature;
74 E_DBus_Method_Cb func;
84 cb_introspect(E_DBus_Object *obj, DBusMessage *msg)
89 if (obj->introspection_dirty || !obj->introspection_data)
91 buf = e_dbus_object_introspect(obj);
94 ret = dbus_message_new_error(msg, "org.enlightenment.NotIntrospectable", "This object does not provide introspection data");
98 if (obj->introspection_data) free(obj->introspection_data);
99 obj->introspection_data = strdup(eina_strbuf_string_get(buf));
100 eina_strbuf_free(buf);
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);
110 cb_properties_get(E_DBus_Object *obj, DBusMessage *msg)
113 DBusMessageIter iter, sub;
119 dbus_error_init(&err);
120 dbus_message_get_args(msg, &err, DBUS_TYPE_STRING, &property, DBUS_TYPE_INVALID);
122 if (dbus_error_is_set(&err))
124 return dbus_message_new_error(msg, err.name, err.message);
127 obj->cb_property_get(obj, property, &type, &value);
128 if (type == DBUS_TYPE_INVALID)
130 return dbus_message_new_error_printf(msg, "org.enlightenment.DBus.InvalidProperty", "The property '%s' does not exist on this object.", property);
133 if (dbus_type_is_basic(type))
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);
144 return dbus_message_new_error(msg, "org.enlightenment.DBus.UnsupportedType", "E_DBus currently only supports properties of a basic type.");
149 cb_properties_set(E_DBus_Object *obj, DBusMessage *msg)
151 DBusMessageIter iter, sub;
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))
162 dbus_message_iter_get_basic(&sub, &value);
163 if (obj->cb_property_set(obj, property, type, value))
165 return dbus_message_new_method_return(msg);
169 return dbus_message_new_error_printf(msg, "org.enlightenment.DBus.InvalidProperty", "The property '%s' does not exist on this object.", property);
174 return dbus_message_new_error(msg, "org.enlightenment.DBus.UnsupportedType", "E_DBus currently only supports properties of a basic type.");
180 e_dbus_object_init(void)
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)
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;
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);
200 e_dbus_object_shutdown(void)
202 e_dbus_interface_unref(introspectable_interface);
203 introspectable_interface = NULL;
205 e_dbus_interface_unref(properties_interface);
206 properties_interface = NULL;
212 * @param conn the connection on with the object should listen
213 * @param object_path a unique string identifying an object (e.g. org/enlightenment/WindowManager
214 * @param data custom data to set on the object (retrievable via
215 * e_dbus_object_data_get())
218 e_dbus_object_add(E_DBus_Connection *conn, const char *object_path, void *data)
222 obj = calloc(1, sizeof(E_DBus_Object));
223 if (!obj) return NULL;
225 if (!dbus_connection_register_object_path(conn->conn, object_path, &vtable, obj))
232 e_dbus_connection_ref(conn);
233 obj->path = strdup(object_path);
235 obj->interfaces = NULL;
237 e_dbus_object_interface_attach(obj, introspectable_interface);
245 * @param obj the object to free
248 e_dbus_object_free(E_DBus_Object *obj)
250 E_DBus_Interface *iface;
254 DBG("e_dbus_object_free (%s)", obj->path);
255 dbus_connection_unregister_object_path(obj->conn->conn, obj->path);
256 e_dbus_connection_close(obj->conn);
258 if (obj->path) free(obj->path);
259 EINA_LIST_FREE(obj->interfaces, iface)
260 e_dbus_interface_unref(iface);
262 if (obj->introspection_data) free(obj->introspection_data);
268 * @brief Fetch the data pointer for a dbus object
269 * @param obj the dbus object
272 e_dbus_object_data_get(E_DBus_Object *obj)
278 * @brief Get the dbus connection of a dbus object
279 * @param obj the dbus object
281 EAPI E_DBus_Connection *
282 e_dbus_object_conn_get(E_DBus_Object *obj)
288 * @brief Get the path of a dbus object
289 * @param obj the dbus object
292 e_dbus_object_path_get(E_DBus_Object *obj)
298 * @brief Get the interfaces of a dbus object
299 * @param obj the dbus object
301 EAPI const Eina_List *
302 e_dbus_object_interfaces_get(E_DBus_Object *obj)
304 return obj->interfaces;
308 * @brief Sets the callback to fetch properties from an object
309 * @param obj the object
310 * @param func the callback
313 e_dbus_object_property_get_cb_set(E_DBus_Object *obj, E_DBus_Object_Property_Get_Cb func)
315 obj->cb_property_get = func;
319 * @brief Sets the callback to set properties on an object
320 * @param obj the object
321 * @param func the callback
324 e_dbus_object_property_set_cb_set(E_DBus_Object *obj, E_DBus_Object_Property_Set_Cb func)
326 obj->cb_property_set = func;
330 e_dbus_object_interface_attach(E_DBus_Object *obj, E_DBus_Interface *iface)
332 e_dbus_interface_ref(iface);
333 obj->interfaces = eina_list_append(obj->interfaces, iface);
334 obj->introspection_dirty = 1;
335 DBG("e_dbus_object_interface_attach (%s, %s) ", obj->path, iface->name);
339 e_dbus_object_interface_detach(E_DBus_Object *obj, E_DBus_Interface *iface)
341 E_DBus_Interface *found;
343 DBG("e_dbus_object_interface_detach (%s, %s) ", obj->path, iface->name);
344 found = eina_list_data_find(obj->interfaces, iface);
345 if (found == NULL) return;
347 obj->interfaces = eina_list_remove(obj->interfaces, iface);
348 obj->introspection_dirty = 1;
349 e_dbus_interface_unref(iface);
353 e_dbus_interface_ref(E_DBus_Interface *iface)
356 DBG("e_dbus_interface_ref (%s) = %d", iface->name, iface->refcount);
360 e_dbus_interface_unref(E_DBus_Interface *iface)
362 DBG("e_dbus_interface_unref (%s) = %d", iface->name, iface->refcount - 1);
363 if (--(iface->refcount) == 0)
364 e_dbus_interface_free(iface);
368 e_dbus_interface_free(E_DBus_Interface *iface)
373 if (iface->name) free(iface->name);
374 EINA_LIST_FREE(iface->methods, m)
375 e_dbus_object_method_free(m);
376 EINA_LIST_FREE(iface->signals, s)
377 e_dbus_object_signal_free(s);
383 * Add a method to an object
385 * @param iface the E_DBus_Interface to which this method belongs
386 * @param member the name of the method
387 * @param signature an optional message signature. if provided, then messages
388 * with invalid signatures will be automatically rejected
389 * (an Error response will be sent) and introspection data
392 * @return 1 if successful, 0 if failed (e.g. no memory)
395 e_dbus_interface_method_add(E_DBus_Interface *iface, const char *member, const char *signature, const char *reply_signature, E_DBus_Method_Cb func)
399 m = e_dbus_method_new(member, signature, reply_signature, func);
400 DBG("E-dbus: Add method %s: %p", member, m);
403 iface->methods = eina_list_append(iface->methods, m);
408 * Add a signal to an object
410 * @param iface the E_DBus_Interface to which this signal belongs
411 * @param name the name of the signal
412 * @param signature an optional message signature.
414 * @return 1 if successful, 0 if failed (e.g. no memory)
417 e_dbus_interface_signal_add(E_DBus_Interface *iface, const char *name, const char *signature)
421 s = e_dbus_signal_new(name, signature);
422 DBG("E-dbus: Add signal %s: %p", name, s);
425 iface->signals = eina_list_append(iface->signals, s);
429 EAPI E_DBus_Interface *
430 e_dbus_interface_new(const char *interface)
432 E_DBus_Interface *iface;
434 if (!interface) return NULL;
436 iface = calloc(1, sizeof(E_DBus_Interface));
437 if (!iface) return NULL;
440 iface->name = strdup(interface);
441 iface->methods = NULL;
442 iface->signals = NULL;
447 static E_DBus_Method *
448 e_dbus_method_new(const char *member, const char *signature, const char *reply_signature, E_DBus_Method_Cb func)
452 if (!member || !func) return NULL;
454 if (signature && !dbus_signature_validate(signature, NULL)) return NULL;
455 if (reply_signature && !dbus_signature_validate(reply_signature, NULL)) return NULL;
456 m = calloc(1, sizeof(E_DBus_Method));
459 m->member = strdup(member);
461 m->signature = strdup(signature);
463 m->reply_signature = strdup(reply_signature);
470 e_dbus_object_method_free(E_DBus_Method *m)
473 if (m->member) free(m->member);
474 if (m->signature) free(m->signature);
475 if (m->reply_signature) free(m->reply_signature);
480 static E_DBus_Signal *
481 e_dbus_signal_new(const char *name, const char *signature)
485 if (!name) return NULL;
487 if (signature && !dbus_signature_validate(signature, NULL)) return NULL;
488 s = calloc(1, sizeof(E_DBus_Signal));
491 s->name = strdup(name);
493 s->signature = strdup(signature);
499 e_dbus_object_signal_free(E_DBus_Signal *s)
502 if (s->name) free(s->name);
503 if (s->signature) free(s->signature);
507 static E_DBus_Method *
508 e_dbus_object_method_find(E_DBus_Object *obj, const char *interface, const char *member)
511 E_DBus_Interface *iface;
514 if (!obj || !member) return NULL;
516 EINA_LIST_FOREACH(obj->interfaces, l, iface)
518 if (strcmp(interface, iface->name)) continue;
519 EINA_LIST_FOREACH(iface->methods, ll, m)
521 if (!strcmp(member, m->member))
528 static DBusHandlerResult
529 e_dbus_object_handler(DBusConnection *conn, DBusMessage *message, void *user_data)
534 dbus_uint32_t serial;
538 return DBUS_HANDLER_RESULT_NOT_YET_HANDLED;
540 m = e_dbus_object_method_find(obj, dbus_message_get_interface(message), dbus_message_get_member(message));
542 /* XXX should this send an 'invalid method' error instead? */
544 return DBUS_HANDLER_RESULT_NOT_YET_HANDLED;
546 if (m->signature && !dbus_message_has_signature(message, m->signature))
547 reply = dbus_message_new_error_printf(message, "org.enlightenment.InvalidSignature", "Expected signature: %s", m->signature);
549 reply = m->func(obj, message);
551 /* user can choose reply later */
553 return DBUS_HANDLER_RESULT_HANDLED;
555 dbus_connection_send(conn, reply, &serial);
556 dbus_message_unref(reply);
558 return DBUS_HANDLER_RESULT_HANDLED;
562 e_dbus_object_unregister(DBusConnection *conn __UNUSED__, void *user_data __UNUSED__)
564 /* free up the object struct? */
568 e_dbus_object_introspect(E_DBus_Object *obj)
572 E_DBus_Interface *iface;
575 buf = eina_strbuf_new();
578 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");
580 eina_strbuf_append(buf, "<node name=\"");
581 eina_strbuf_append(buf, obj->path);
582 eina_strbuf_append(buf, "\">\n");
585 EINA_LIST_FOREACH(obj->interfaces, l, iface)
586 _introspect_interface_append(buf, iface, level);
588 eina_strbuf_append(buf, "</node>\n");
593 _introspect_indent_append(Eina_Strbuf *buf, int level)
595 /* XXX optimize this? */
598 eina_strbuf_append_char(buf, ' ');
601 _introspect_interface_append(Eina_Strbuf *buf, E_DBus_Interface *iface, int level)
607 _introspect_indent_append(buf, level);
608 eina_strbuf_append(buf, "<interface name=\"");
609 eina_strbuf_append(buf, iface->name);
610 eina_strbuf_append(buf, "\">\n");
613 DBG("introspect iface: %s", iface->name);
614 EINA_LIST_FOREACH(iface->methods, l, m)
615 _introspect_method_append(buf, m, level);
616 EINA_LIST_FOREACH(iface->signals, l, s)
617 _introspect_signal_append(buf, s, level);
620 _introspect_indent_append(buf, level);
621 eina_strbuf_append(buf, "</interface>\n");
624 _introspect_method_append(Eina_Strbuf *buf, E_DBus_Method *method, int level)
626 DBusSignatureIter iter;
629 _introspect_indent_append(buf, level);
630 DBG("introspect method: %s\n", method->member);
631 eina_strbuf_append(buf, "<method name=\"");
632 eina_strbuf_append(buf, method->member);
633 eina_strbuf_append(buf, "\">\n");
637 if (method->signature &&
638 method->signature[0] &&
639 dbus_signature_validate(method->signature, NULL))
641 dbus_signature_iter_init(&iter, method->signature);
642 while ((type = dbus_signature_iter_get_signature(&iter)))
644 _introspect_arg_append(buf, type, "in", level);
647 if (!dbus_signature_iter_next(&iter)) break;
651 /* append reply args */
652 if (method->reply_signature &&
653 method->reply_signature[0] &&
654 dbus_signature_validate(method->reply_signature, NULL))
656 dbus_signature_iter_init(&iter, method->reply_signature);
657 while ((type = dbus_signature_iter_get_signature(&iter)))
659 _introspect_arg_append(buf, type, "out", level);
662 if (!dbus_signature_iter_next(&iter)) break;
667 _introspect_indent_append(buf, level);
668 eina_strbuf_append(buf, "</method>\n");
672 _introspect_signal_append(Eina_Strbuf *buf, E_DBus_Signal *s, int level)
674 DBusSignatureIter iter;
677 _introspect_indent_append(buf, level);
678 DBG("introspect signal: %s", s->name);
679 eina_strbuf_append(buf, "<signal name=\"");
680 eina_strbuf_append(buf, s->name);
681 eina_strbuf_append(buf, "\">\n");
687 dbus_signature_validate(s->signature, NULL))
689 dbus_signature_iter_init(&iter, s->signature);
690 while ((type = dbus_signature_iter_get_signature(&iter)))
692 _introspect_arg_append(buf, type, NULL, level);
695 if (!dbus_signature_iter_next(&iter)) break;
700 _introspect_indent_append(buf, level);
701 eina_strbuf_append(buf, "</signal>\n");
705 _introspect_arg_append(Eina_Strbuf *buf, const char *type, const char *direction, int level)
707 _introspect_indent_append(buf, level);
708 eina_strbuf_append(buf, "<arg type=\"");
709 eina_strbuf_append(buf, type);
712 eina_strbuf_append(buf, "\" direction=\"");
713 eina_strbuf_append(buf, direction);
715 eina_strbuf_append(buf, "\"/>\n");