More DBusError leak fixes
[platform/core/uifw/at-spi2-atk.git] / droute / droute.c
index 5b1d9de..838aacd 100644 (file)
@@ -23,6 +23,7 @@
 
 #include <stdlib.h>
 #include <string.h>
+#include <stdio.h>
 
 #include "droute.h"
 #include "droute-pairhash.h"
 
 #define oom() g_error ("D-Bus out of memory, this message will fail anyway")
 
+#if defined DROUTE_DEBUG
+    #define _DROUTE_DEBUG(format, args...) g_print (format , ## args)
+#else
+    #define _DROUTE_DEBUG(format, args...)
+#endif
+
 struct _DRouteContext
 {
-    DBusConnection       *bus;
     GPtrArray            *registered_paths;
 
-    gchar                *introspect_dir;
+    gchar                *introspect_string;
 };
 
 struct _DRoutePath
 {
     DRouteContext        *cnx;
+    gchar *path;
+    gboolean prefix;
     GStringChunk         *chunks;
     GPtrArray            *interfaces;
+    GPtrArray            *introspection;
     GHashTable           *methods;
     GHashTable           *properties;
 
+    DRouteIntrospectChildrenFunction introspect_children_cb;
+    void *introspect_children_data;
     void                   *user_data;
     DRouteGetDatumFunction  get_datum;
 };
@@ -64,19 +75,29 @@ typedef struct PropertyPair
 static DBusHandlerResult
 handle_message (DBusConnection *bus, DBusMessage *message, void *user_data);
 
+static DBusMessage *
+droute_object_does_not_exist_error (DBusMessage *message);
+
 /*---------------------------------------------------------------------------*/
 
 static DRoutePath *
 path_new (DRouteContext *cnx,
+          const char *path,
+          gboolean prefix,
           void    *user_data,
+          DRouteIntrospectChildrenFunction introspect_children_cb,
+          void *introspect_children_data,
           DRouteGetDatumFunction get_datum)
 {
     DRoutePath *new_path;
 
-    new_path = g_new0 (DRoutePath, 0);
+    new_path = g_new0 (DRoutePath, 1);
     new_path->cnx = cnx;
+    new_path->path = g_strdup (path);
+    new_path->prefix = prefix;
     new_path->chunks = g_string_chunk_new (CHUNKS_DEFAULT);
     new_path->interfaces = g_ptr_array_new ();
+    new_path->introspection = g_ptr_array_new ();
 
     new_path->methods = g_hash_table_new_full ((GHashFunc)str_pair_hash,
                                                str_pair_equal,
@@ -86,8 +107,10 @@ path_new (DRouteContext *cnx,
     new_path->properties = g_hash_table_new_full ((GHashFunc)str_pair_hash,
                                                   str_pair_equal,
                                                   g_free,
-                                                  NULL);
+                                                  g_free);
 
+    new_path->introspect_children_cb = introspect_children_cb;
+    new_path->introspect_children_data = introspect_children_data;
     new_path->user_data = user_data;
     new_path->get_datum = get_datum;
 
@@ -97,10 +120,13 @@ path_new (DRouteContext *cnx,
 static void
 path_free (DRoutePath *path, gpointer user_data)
 {
+    g_free (path->path);
     g_string_chunk_free  (path->chunks);
     g_ptr_array_free     (path->interfaces, TRUE);
+    g_free(g_ptr_array_free     (path->introspection, FALSE));
     g_hash_table_destroy (path->methods);
     g_hash_table_destroy (path->properties);
+    g_free (path);
 }
 
 static void *
@@ -115,26 +141,28 @@ path_get_datum (DRoutePath *path, const gchar *pathstr)
 /*---------------------------------------------------------------------------*/
 
 DRouteContext *
-droute_new (DBusConnection *bus, const char *introspect_dir)
+droute_new ()
 {
     DRouteContext *cnx;
 
     cnx = g_new0 (DRouteContext, 1);
-    cnx->bus = bus;
     cnx->registered_paths = g_ptr_array_new ();
-    cnx->introspect_dir = g_strdup(introspect_dir);
+
+    return cnx;
 }
 
 void
 droute_free (DRouteContext *cnx)
 {
-    g_pointer_array_foreach ((GFunc) path_free, cnx->registered_paths, NULL);
-    g_free (cnx->introspect_dir);
+    g_ptr_array_foreach (cnx->registered_paths, (GFunc) path_free, NULL);
+    g_ptr_array_free (cnx->registered_paths, TRUE);
     g_free (cnx);
 }
 
 /*---------------------------------------------------------------------------*/
 
+/*---------------------------------------------------------------------------*/
+
 static DBusObjectPathVTable droute_vtable =
 {
   NULL,
@@ -149,10 +177,7 @@ droute_add_one (DRouteContext *cnx,
 {
     DRoutePath *new_path;
 
-    new_path = path_new (cnx, (void *) data, NULL);
-
-    if (!dbus_connection_register_object_path (cnx->bus, path, &droute_vtable, new_path))
-        oom();
+    new_path = path_new (cnx, path, FALSE, (void *)data, NULL, NULL, NULL);
 
     g_ptr_array_add (cnx->registered_paths, new_path);
     return new_path;
@@ -162,14 +187,15 @@ DRoutePath *
 droute_add_many (DRouteContext *cnx,
                  const char    *path,
                  const void    *data,
+                 DRouteIntrospectChildrenFunction introspect_children_cb,
+                 void *introspect_children_data,
                  const DRouteGetDatumFunction get_datum)
 {
     DRoutePath *new_path;
 
-    new_path = path_new (cnx, (void *) data, get_datum);
-
-    if (!dbus_connection_register_fallback (cnx->bus, path, &droute_vtable, new_path))
-        oom();
+    new_path = path_new (cnx, path, TRUE, (void *) data,
+                         introspect_children_cb, introspect_children_data,
+                         get_datum);
 
     g_ptr_array_add (cnx->registered_paths, new_path);
     return new_path;
@@ -180,17 +206,19 @@ droute_add_many (DRouteContext *cnx,
 void
 droute_path_add_interface(DRoutePath *path,
                           const char *name,
+                          const char *introspect,
                           const DRouteMethod   *methods,
                           const DRouteProperty *properties)
 {
     gchar *itf;
 
-    g_return_if_fail (name == NULL);
+    g_return_if_fail (name != NULL);
 
     itf = g_string_chunk_insert (path->chunks, name);
     g_ptr_array_add (path->interfaces, itf);
+    g_ptr_array_add (path->introspection, (gpointer) introspect);
 
-    for (; methods->name != NULL; methods++)
+    for (; methods != NULL && methods->name != NULL; methods++)
       {
         gchar *meth;
 
@@ -198,7 +226,7 @@ droute_path_add_interface(DRoutePath *path,
         g_hash_table_insert (path->methods, str_pair_new (itf, meth), methods->func);
       }
 
-    for (; properties->name != NULL; properties++)
+    for (; properties != NULL && properties->name != NULL; properties++)
       {
         gchar *prop;
         PropertyPair *pair;
@@ -231,12 +259,18 @@ impl_prop_GetAll (DBusMessage *message,
     gchar *iface;
 
     void  *datum = path_get_datum (path, pathstr);
+    if (!datum)
+       return droute_object_does_not_exist_error (message);
 
     dbus_error_init (&error);
-
     if (!dbus_message_get_args
                 (message, &error, DBUS_TYPE_STRING, &iface, DBUS_TYPE_INVALID))
-        return dbus_message_new_error (message, DBUS_ERROR_FAILED, error.message);
+      {
+        DBusMessage *ret;
+        ret = dbus_message_new_error (message, DBUS_ERROR_FAILED, error.message);
+        dbus_error_free (&error);
+        return ret;
+      }
 
     reply = dbus_message_new_method_return (message);
     if (!reply)
@@ -250,7 +284,7 @@ impl_prop_GetAll (DBusMessage *message,
     g_hash_table_iter_init (&prop_iter, path->properties);
     while (g_hash_table_iter_next (&prop_iter, (gpointer*)&key, (gpointer*)&value))
       {
-        if (!g_strcmp (key->one, iface))
+        if (!g_strcmp0 (key->one, iface))
          {
            if (!value->get)
               continue;
@@ -258,7 +292,7 @@ impl_prop_GetAll (DBusMessage *message,
                         (&iter_dict, DBUS_TYPE_DICT_ENTRY, NULL, &iter_dict_entry))
               oom ();
            dbus_message_iter_append_basic (&iter_dict_entry, DBUS_TYPE_STRING,
-                                           key->two);
+                                           &key->two);
            (value->get) (&iter_dict_entry, datum);
            if (!dbus_message_iter_close_container (&iter_dict, &iter_dict_entry))
                oom ();
@@ -280,8 +314,11 @@ impl_prop_GetSet (DBusMessage *message,
     DBusError error;
 
     StrPair pair;
-    PropertyPair *prop_funcs;
+    PropertyPair *prop_funcs = NULL;
 
+    void *datum;
+
+    dbus_error_init (&error);
     if (!dbus_message_get_args (message,
                                 &error,
                                 DBUS_TYPE_STRING,
@@ -289,36 +326,104 @@ impl_prop_GetSet (DBusMessage *message,
                                 DBUS_TYPE_STRING,
                                 &(pair.two),
                                 DBUS_TYPE_INVALID))
-        return dbus_message_new_error (message, DBUS_ERROR_FAILED, error.message);
+      {
+        DBusMessage *ret;
+        ret = dbus_message_new_error (message, DBUS_ERROR_FAILED, error.message);
+        dbus_error_free (&error);
+      }
+
+    _DROUTE_DEBUG ("DRoute (handle prop): %s|%s on %s\n", pair.one, pair.two, pathstr);
 
     prop_funcs = (PropertyPair *) g_hash_table_lookup (path->properties, &pair);
     if (!prop_funcs)
-        return dbus_message_new_error (message, DBUS_ERROR_FAILED, "Property unavailable");
+      {
+        DBusMessage *ret;
+#ifdef DBUS_ERROR_UNKNOWN_PROPERTY
+        ret = dbus_message_new_error (message, DBUS_ERROR_UNKNOWN_PROPERTY, "Property unavailable");
+#else
+        ret = dbus_message_new_error (message, DBUS_ERROR_FAILED, "Property unavailable");
+#endif
+        dbus_error_free (&error);
+        return ret;
+      }
+
+    datum = path_get_datum (path, pathstr);
+    if (!datum)
+       return droute_object_does_not_exist_error (message);
 
     if (get && prop_funcs->get)
       {
-        void *datum = path_get_datum (path, pathstr);
+        
         DBusMessageIter iter;
 
+        _DROUTE_DEBUG ("DRoute (handle prop Get): %s|%s on %s\n", pair.one, pair.two, pathstr);
+
         reply = dbus_message_new_method_return (message);
         dbus_message_iter_init_append (reply, &iter);
-        (prop_funcs->get) (&iter, datum);
+        if (!(prop_funcs->get) (&iter, datum))
+         {
+           dbus_message_unref (reply);
+            reply = dbus_message_new_error (message, DBUS_ERROR_FAILED, "Get failed");
+         }
       }
     else if (!get && prop_funcs->set)
       {
-        void *datum = path_get_datum (path, pathstr);
         DBusMessageIter iter;
 
-        dbus_message_iter_init_append (message, &iter);
+        _DROUTE_DEBUG ("DRoute (handle prop Get): %s|%s on %s\n", pair.one, pair.two, pathstr);
+
+        dbus_message_iter_init (message, &iter);
         /* Skip the interface and property name */
         dbus_message_iter_next(&iter);
         dbus_message_iter_next(&iter);
-        (prop_funcs->get) (&iter, datum);
+        (prop_funcs->set) (&iter, datum);
+
+        reply = dbus_message_new_method_return (message);
+      }
+#ifdef DBUS_ERROR_PROPERTY_READ_ONLY
+    else if (!get)
+      {
+        reply = dbus_message_new_error (message, DBUS_ERROR_PROPERTY_READ_ONLY, "Property is read-only");
       }
+#endif
+    else
+      {
+        reply = dbus_message_new_error (message, DBUS_ERROR_FAILED, "Getter or setter unavailable");
+      }
+
     return reply;
 }
 
 static DBusHandlerResult
+handle_dbus (DBusConnection *bus,
+                   DBusMessage    *message,
+                   const gchar    *iface,
+                   const gchar    *member,
+                   const gchar    *pathstr)
+{
+  static int id = 1;
+  char *id_str = (char *) g_malloc(40);
+  DBusMessage *reply;
+
+  if (strcmp (iface, DBUS_INTERFACE_DBUS) != 0 ||
+      strcmp (member, "Hello") != 0)
+    {
+      g_free (id_str);
+      return DBUS_HANDLER_RESULT_NOT_YET_HANDLED;
+    }
+
+    /* TODO: Fix this hack (we don't handle wrap-around, for instance) */
+    sprintf (id_str, ":1.%d", id++);
+    reply = dbus_message_new_method_return (message);
+    dbus_message_append_args (reply, DBUS_TYPE_STRING, &id_str, DBUS_TYPE_INVALID);
+    dbus_connection_send (bus, reply, NULL);
+  dbus_connection_flush (bus);
+    dbus_message_unref (reply);
+  g_free (id_str);
+    return DBUS_HANDLER_RESULT_HANDLED;
+}
+
+static DBusHandlerResult
 handle_properties (DBusConnection *bus,
                    DBusMessage    *message,
                    DRoutePath     *path,
@@ -326,7 +431,7 @@ handle_properties (DBusConnection *bus,
                    const gchar    *member,
                    const gchar    *pathstr)
 {
-    DBusMessage *reply;
+    DBusMessage *reply = NULL;
     DBusHandlerResult result = DBUS_HANDLER_RESULT_HANDLED;
 
     if (!g_strcmp0(member, "GetAll"))
@@ -338,6 +443,12 @@ handle_properties (DBusConnection *bus,
     else
        result = DBUS_HANDLER_RESULT_NOT_YET_HANDLED;
 
+    if (reply)
+      {
+        dbus_connection_send (bus, reply, NULL);
+        dbus_message_unref (reply);
+      }
+
     return result;
 }
 
@@ -352,37 +463,8 @@ static const char *introspection_node_element =
 static const char *introspection_footer =
 "</node>";
 
-static void
-append_interface (GString     *str,
-                  const gchar *interface,
-                  const gchar *directory)
-{
-  gchar *filename;
-  gchar *contents;
-  gsize len;
-
-  GError *err = NULL;
-
-  filename = g_build_filename (directory, interface, NULL);
-
-  if (g_file_get_contents (filename, &contents, &len, &err))
-    {
-      g_string_append_len (str, contents, len);
-    }
-  else
-    {
-      g_warning ("AT-SPI: Cannot find introspection XML file %s - %s",
-                 filename, err->message);
-      g_error_free (err);
-    }
-
-  g_string_append (str, "\n");
-  g_free (filename);
-  g_free (contents);
-}
-
 static DBusHandlerResult
-handle_intropsection (DBusConnection *bus,
+handle_introspection (DBusConnection *bus,
                       DBusMessage    *message,
                       DRoutePath     *path,
                       const gchar    *iface,
@@ -395,17 +477,32 @@ handle_intropsection (DBusConnection *bus,
 
     DBusMessage *reply;
 
-    if (g_strcmp (member, "Introspect"))
+    _DROUTE_DEBUG ("DRoute (handle introspection): %s\n", pathstr);
+
+    if (g_strcmp0 (member, "Introspect"))
         return DBUS_HANDLER_RESULT_NOT_YET_HANDLED;
 
     output = g_string_new(introspection_header);
 
     g_string_append_printf(output, introspection_node_element, pathstr);
 
-    for (i=0; i < path->interfaces->len; i++)
+    if (!path->get_datum || path_get_datum (path, pathstr))
       {
-        gchar *interface = (gchar *) g_ptr_array_index (path->interfaces, i);
-        append_interface(output, interface, path->cnx->introspect_dir);
+        for (i=0; i < path->introspection->len; i++)
+          {
+            gchar *introspect = (gchar *) g_ptr_array_index (path->introspection, i);
+            g_string_append (output, introspect);
+          }
+      }
+
+    if (path->introspect_children_cb)
+      {
+        gchar *children = (*path->introspect_children_cb) (pathstr, path->introspect_children_data);
+        if (children)
+          {
+            g_string_append (output, children);
+            g_free (children);
+          }
       }
 
     g_string_append(output, introspection_footer);
@@ -437,18 +534,28 @@ handle_other (DBusConnection *bus,
 
     StrPair pair;
     DRouteFunction func;
-    DBusMessage *reply;
+    DBusMessage *reply = NULL;
+
+    void *datum;
 
     pair.one = iface;
     pair.two = member;
 
+    _DROUTE_DEBUG ("DRoute (handle other): %s|%s on %s\n", member, iface, pathstr);
+
     func = (DRouteFunction) g_hash_table_lookup (path->methods, &pair);
     if (func != NULL)
       {
-        void *datum = path_get_datum (path, pathstr);
-
-        reply = (func) (bus, message, datum);
-
+        datum = path_get_datum (path, pathstr);
+        if (!datum)
+           reply = droute_object_does_not_exist_error (message);
+        else
+            reply = (func) (bus, message, datum);
+
+        /* All D-Bus method calls must have a reply.
+         * If one is not provided presume that the caller has already
+         * sent one.
+         */
         if (reply)
           {
             dbus_connection_send (bus, reply, NULL);
@@ -456,6 +563,9 @@ handle_other (DBusConnection *bus,
           }
         result = DBUS_HANDLER_RESULT_HANDLED;
       }
+
+    _DROUTE_DEBUG ("DRoute (handle other) (reply): type %d\n",
+                   dbus_message_get_type(reply));
     return result;
 }
 
@@ -470,19 +580,163 @@ handle_message (DBusConnection *bus, DBusMessage *message, void *user_data)
     const gint   type    = dbus_message_get_type (message);
     const gchar *pathstr = dbus_message_get_path (message);
 
+    DBusHandlerResult result = DBUS_HANDLER_RESULT_NOT_YET_HANDLED;
+
+    _DROUTE_DEBUG ("DRoute (handle message): %s|%s of type %d on %s\n", member, iface, type, pathstr);
+
     /* Check for basic reasons not to handle */
     if (type   != DBUS_MESSAGE_TYPE_METHOD_CALL ||
         member == NULL ||
         iface  == NULL)
-        return DBUS_HANDLER_RESULT_NOT_YET_HANDLED;
+        return result;
+
+    if (!strcmp (pathstr, DBUS_PATH_DBUS))
+        result = handle_dbus (bus, message, iface, member, pathstr);
+    else if (!strcmp (iface, "org.freedesktop.DBus.Properties"))
+        result = handle_properties (bus, message, path, iface, member, pathstr);
+    else if (!strcmp (iface, "org.freedesktop.DBus.Introspectable"))
+        result = handle_introspection (bus, message, path, iface, member, pathstr);
+    else
+        result = handle_other (bus, message, path, iface, member, pathstr);
+#if 0
+    if (result == DBUS_HANDLER_RESULT_NOT_YET_HANDLED)
+        g_print ("DRoute | Unhandled message: %s|%s of type %d on %s\n", member, iface, type, pathstr);
+#endif
+      
+    return result;
+}
+
+/*---------------------------------------------------------------------------*/
+
+static DBusMessage *
+droute_object_does_not_exist_error (DBusMessage *message)
+{
+    DBusMessage *reply;
+    gchar       *errmsg;
+
+    errmsg= g_strdup_printf (
+            "Method \"%s\" with signature \"%s\" on interface \"%s\" could not be processed as object %s does not exist\n",
+            dbus_message_get_member (message),
+            dbus_message_get_signature (message),
+            dbus_message_get_interface (message),
+            dbus_message_get_path (message));
+#ifdef DBUS_ERROR_UNKNOWN_OBJECT
+    reply = dbus_message_new_error (message,
+                                    DBUS_ERROR_UNKNOWN_OBJECT,
+                                    errmsg);
+#else
+    reply = dbus_message_new_error (message,
+                                    DBUS_ERROR_FAILED,
+                                    errmsg);
+#endif
+    g_free (errmsg);
+    return reply;
+}
+
+/*---------------------------------------------------------------------------*/
+
+DBusMessage *
+droute_not_yet_handled_error (DBusMessage *message)
+{
+    DBusMessage *reply;
+    gchar       *errmsg;
+
+    errmsg= g_strdup_printf (
+            "Method \"%s\" with signature \"%s\" on interface \"%s\" doesn't exist\n",
+            dbus_message_get_member (message),
+            dbus_message_get_signature (message),
+            dbus_message_get_interface (message));
+    reply = dbus_message_new_error (message,
+                                    DBUS_ERROR_UNKNOWN_METHOD,
+                                    errmsg);
+    g_free (errmsg);
+    return reply;
+}
+
+DBusMessage *
+droute_out_of_memory_error (DBusMessage *message)
+{
+    DBusMessage *reply;
+    gchar       *errmsg;
+
+    errmsg= g_strdup_printf (
+            "Method \"%s\" with signature \"%s\" on interface \"%s\" could not be processed due to lack of memory\n",
+            dbus_message_get_member (message),
+            dbus_message_get_signature (message),
+            dbus_message_get_interface (message));
+    reply = dbus_message_new_error (message,
+                                    DBUS_ERROR_NO_MEMORY,
+                                    errmsg);
+    g_free (errmsg);
+    return reply;
+}
 
-    if (!strcmp (iface, "org.freedesktop.DBus.Properties"))
-        return handle_properties (bus, message, path, iface, member, pathstr);
+DBusMessage *
+droute_invalid_arguments_error (DBusMessage *message)
+{
+    DBusMessage *reply;
+    gchar       *errmsg;
+
+    errmsg= g_strdup_printf (
+            "Method \"%s\" with signature \"%s\" on interface \"%s\" was supplied with invalid arguments\n",
+            dbus_message_get_member (message),
+            dbus_message_get_signature (message),
+            dbus_message_get_interface (message));
+    reply = dbus_message_new_error (message,
+                                    DBUS_ERROR_INVALID_ARGS,
+                                    errmsg);
+    g_free (errmsg);
+    return reply;
+}
 
-    if (!strcmp (iface, "org.freedesktop.DBus.Introspectable"))
-        return handle_introspection (bus, message, path, iface, member, pathstr);
+void
+droute_path_register (DRoutePath *path, DBusConnection *bus)
+{
+    if (path->prefix)
+      dbus_connection_register_fallback (bus, path->path, &droute_vtable, path);
+    else
+      dbus_connection_register_object_path (bus, path->path,
+                                            &droute_vtable, path);
+}
+
+void
+droute_path_unregister (DRoutePath *path, DBusConnection *bus)
+{
+  dbus_connection_unregister_object_path (bus, path->path);
+}
 
-    return handle_other (bus, message, path, iface, member, pathstr);
+void
+droute_context_register (DRouteContext *cnx, DBusConnection *bus)
+{
+    g_ptr_array_foreach (cnx->registered_paths, (GFunc) droute_path_register,
+                         bus);
+}
+
+void
+droute_context_unregister (DRouteContext *cnx, DBusConnection *bus)
+{
+    g_ptr_array_foreach (cnx->registered_paths, (GFunc) droute_path_unregister,
+                         bus);
+}
+
+void
+droute_context_deregister (DRouteContext *cnx, DBusConnection *bus)
+{
+    g_ptr_array_foreach (cnx->registered_paths, (GFunc) droute_path_unregister,
+                         bus);
+}
+
+void
+droute_intercept_dbus (DBusConnection *bus)
+{
+    dbus_connection_register_object_path (bus, DBUS_PATH_DBUS,
+                                          &droute_vtable, NULL);
+}
+
+void
+droute_unintercept_dbus (DBusConnection *bus)
+{
+    dbus_connection_unregister_object_path (bus, DBUS_PATH_DBUS);
 }
 
 /*END------------------------------------------------------------------------*/