r6875 - in dumbhippo/trunk/client: common/ddm common/hippo linux linux/src



Author: otaylor
Date: 2007-11-07 11:41:12 -0600 (Wed, 07 Nov 2007)
New Revision: 6875

Added:
   dumbhippo/trunk/client/linux/src/hippo-dbus-model-client.c
   dumbhippo/trunk/client/linux/src/hippo-dbus-model-client.h
Modified:
   dumbhippo/trunk/client/common/ddm/ddm-data-model-dbus.c
   dumbhippo/trunk/client/common/ddm/ddm-data-model.c
   dumbhippo/trunk/client/common/ddm/ddm-data-model.h
   dumbhippo/trunk/client/common/ddm/ddm-data-query.c
   dumbhippo/trunk/client/common/ddm/ddm-data-resource-internal.h
   dumbhippo/trunk/client/common/ddm/ddm-data-resource.c
   dumbhippo/trunk/client/common/ddm/ddm-data-resource.h
   dumbhippo/trunk/client/common/ddm/ddm-work-item.c
   dumbhippo/trunk/client/common/ddm/ddm.h
   dumbhippo/trunk/client/common/hippo/hippo-data-model-backend.c
   dumbhippo/trunk/client/linux/Makefile-mugshot.am
   dumbhippo/trunk/client/linux/src/hippo-dbus-model.c
Log:
hippo-dbus-model-client.[ch] hippo-dbus-model.c: Rewrite export of data
  model to D-BUS using DDMClient.
  
ddm-data-model.[ch]ddm-data-resource.c ddm-data-resource-internal.h
  hippo-data-model-backend.c ddm-data-model-dbus.c: Reset the data
  model when reconnecting to the server; discard all remote resources
  and all local properties referencing remote resources.

ddm-data-resource.[ch]: Add refcounting for resources.

ddm-data-query.c: Add errored fetches into 'received_fetch'; this is needed
  to to prevent infinite loops, especially in the disconnected state.


Modified: dumbhippo/trunk/client/common/ddm/ddm-data-model-dbus.c
===================================================================
--- dumbhippo/trunk/client/common/ddm/ddm-data-model-dbus.c	2007-11-07 00:45:26 UTC (rev 6874)
+++ dumbhippo/trunk/client/common/ddm/ddm-data-model-dbus.c	2007-11-07 17:41:12 UTC (rev 6875)
@@ -623,6 +623,8 @@
         return;
     }
 
+    if (is_connected)
+        ddm_data_model_reset(dbus_model->ddm_model);
     ddm_data_model_set_connected(dbus_model->ddm_model, is_connected);
     if (is_connected)
         model_send_pending(dbus_model);

Modified: dumbhippo/trunk/client/common/ddm/ddm-data-model.c
===================================================================
--- dumbhippo/trunk/client/common/ddm/ddm-data-model.c	2007-11-07 00:45:26 UTC (rev 6874)
+++ dumbhippo/trunk/client/common/ddm/ddm-data-model.c	2007-11-07 17:41:12 UTC (rev 6875)
@@ -55,7 +55,9 @@
 static void
 ddm_data_model_init(DDMDataModel *model)
 {
-    model->resources = g_hash_table_new(g_str_hash, g_str_equal);
+    model->resources = g_hash_table_new_full(g_str_hash, g_str_equal,
+                                             NULL,
+                                             (GDestroyNotify)ddm_data_resource_unref);
     model->changed_resources = g_hash_table_new(g_direct_hash, NULL);
     model->work_items = g_queue_new();
 
@@ -446,7 +448,28 @@
     return ensure_resource_internal(model, resource_id, class_id, TRUE);
 }
 
+static gboolean
+model_reset_foreach (gpointer key,
+                     gpointer value,
+                     gpointer data)
+{
+    DDMDataResource *resource = value;
+
+    if (ddm_data_resource_is_local(resource)) {
+        _ddm_data_resource_reset(resource);
+        return FALSE;
+    } else {
+        return TRUE;
+    }
+}
+
 void
+ddm_data_model_reset (DDMDataModel *model)
+{
+    g_hash_table_foreach_remove(model->resources, model_reset_foreach, NULL);
+}
+
+void
 ddm_data_model_set_connected (DDMDataModel   *model,
                               gboolean        connected)
 {

Modified: dumbhippo/trunk/client/common/ddm/ddm-data-model.h
===================================================================
--- dumbhippo/trunk/client/common/ddm/ddm-data-model.h	2007-11-07 00:45:26 UTC (rev 6874)
+++ dumbhippo/trunk/client/common/ddm/ddm-data-model.h	2007-11-07 17:41:12 UTC (rev 6875)
@@ -80,6 +80,13 @@
 /* should only be called by backends */
 
 void ddm_data_model_schedule_flush (DDMDataModel *model);
+
+/* Generally a backend will first reset() and then call set_connected(TRUE);
+ * the reason for the separation is to allow the backend to restablish
+ * local properties that reference remote properties.
+ */
+void ddm_data_model_reset          (DDMDataModel *model);
+
 void ddm_data_model_set_connected  (DDMDataModel *model,
                                     gboolean      connected);
 

Modified: dumbhippo/trunk/client/common/ddm/ddm-data-query.c
===================================================================
--- dumbhippo/trunk/client/common/ddm/ddm-data-query.c	2007-11-07 00:45:26 UTC (rev 6874)
+++ dumbhippo/trunk/client/common/ddm/ddm-data-query.c	2007-11-07 17:41:12 UTC (rev 6875)
@@ -280,7 +280,24 @@
     DDMWorkItem *item;
     GSList *l;
 
-    if (!local) {
+    /* It's possible that we succeeded in getting a list of results, but
+     * couldn't even find out the class_id for the results (particularly
+     * for getResource, where a simple failed query shows this way)
+     * In this case, treat the whole thing as a failure.
+     */
+    for (l = results; l; l = l->next) {
+        if (ddm_data_resource_get_class_id(l->data) == NULL) {
+            g_debug("%s: resource %s has no class ID, failing query",
+                    query->id_string, ddm_data_resource_get_resource_id(l->data));
+            
+            ddm_data_query_error(query,
+                                 DDM_DATA_ERROR_ITEM_NOT_FOUND,
+                                 "Couldn't get details of result items");
+            return;
+        }
+    }
+            
+   if (!local) {
         g_debug("%s: Received response", query->id_string);
         
         for (l = results; l; l = l->next) {
@@ -370,6 +387,31 @@
 
     g_debug("%s: Got error response: %s (%d)", query->id_string, message != NULL ? message : "<null>", error);
 
+    /* For a getResource query for a particular resource, we want to mark the
+     * fetch as 'received', so that we don't try to fetch it again. This is
+     * especially important for keeping our multi-part fetch code from going
+     * into an infinite loop; if we make a fetch, and it returns an error,
+     * don't try again.
+     *
+     * FIXME: This isn't really right; a 404 on a getResource shouldn't
+     * keep us from successfully trying to getResource again later if that
+     * resource appears. Possibly we want to track errored fetches separately
+     * with a timeout for retrying the errored fetch.
+     */
+    if (query->qname == ddm_qname_get("http://mugshot.org/p/system";, "getResource")) {
+        const char *resource_id = g_hash_table_lookup(query->params, "resourceId");
+        if (resource_id == NULL) {
+            /* Shouldn't happen; we validate application-supplied getResource queries */
+            g_warning("%s: Null resource_id for getresource query when marking error response", query->id_string);
+        } else {
+            DDMDataResource *resource = ddm_data_model_lookup_resource(query->model, resource_id);
+            if (resource != NULL) {
+                g_debug("%s: marking fetch 'received' on errored getResource for %s'", query->id_string, resource_id);
+                _ddm_data_resource_fetch_received(resource, query->fetch);
+            }
+        }
+    }
+
     _ddm_data_model_query_answered(query->model, query);
     
     if (query->error_handler)    

Modified: dumbhippo/trunk/client/common/ddm/ddm-data-resource-internal.h
===================================================================
--- dumbhippo/trunk/client/common/ddm/ddm-data-resource-internal.h	2007-11-07 00:45:26 UTC (rev 6874)
+++ dumbhippo/trunk/client/common/ddm/ddm-data-resource-internal.h	2007-11-07 17:41:12 UTC (rev 6875)
@@ -14,6 +14,8 @@
                                          const char      *class_id,
                                          gboolean         local);
 
+void _ddm_data_resource_reset (DDMDataResource *resource);
+
 GSList *_ddm_data_resource_get_default_properties (DDMDataResource *resource);
 
 DDMDataFetch *_ddm_data_resource_get_received_fetch   (DDMDataResource *resource);

Modified: dumbhippo/trunk/client/common/ddm/ddm-data-resource.c
===================================================================
--- dumbhippo/trunk/client/common/ddm/ddm-data-resource.c	2007-11-07 00:45:26 UTC (rev 6874)
+++ dumbhippo/trunk/client/common/ddm/ddm-data-resource.c	2007-11-07 17:41:12 UTC (rev 6875)
@@ -44,6 +44,8 @@
 
 struct _DDMDataResource
 {
+    guint refcount;
+    
     DDMDataModel *model;
     char *resource_id;
     char *class_id;
@@ -129,6 +131,17 @@
     g_warning("Type value '%d' not valid", DDM_DATA_BASE(value->type));
 }
 
+static void
+ddm_data_property_free(DDMDataProperty *property)
+{
+    ddm_data_value_clear(&property->value);
+
+    if (property->default_children)
+        ddm_data_fetch_unref(property->default_children);
+
+    g_free(property);
+}
+
 DDMQName *
 ddm_data_property_get_qname(DDMDataProperty *property)
 {
@@ -178,6 +191,13 @@
     return property->default_children;
 }
 
+static void
+data_client_free(DataClient *data_client)
+{
+    ddm_data_fetch_unref(data_client->fetch);
+    g_free(data_client);
+}
+
 DDMDataResource *
 _ddm_data_resource_new(DDMDataModel *model,
                        const char   *resource_id,
@@ -186,6 +206,8 @@
 {
     DDMDataResource *resource = g_new0(DDMDataResource, 1);
 
+    resource->refcount = 1;
+
     resource->model = model;
     resource->resource_id = g_strdup(resource_id);
     ddm_data_resource_set_class_id(resource, class_id);
@@ -198,7 +220,125 @@
     return resource;
 }
 
+typedef gboolean (*ForeachRemoveFunc) (gpointer data,
+                                       gpointer user_data);
+
+static GSList *
+slist_foreach_remove(GSList            *list,
+                     ForeachRemoveFunc  func,
+                     gpointer           user_data)
+{
+    GSList *l = list;
+    GSList *prev = NULL;
+
+    while (l) {
+        GSList *next = l->next;
+        if ((*func)(l->data, user_data)) {
+            if (prev != NULL)
+                prev->next = next;
+            else
+                list = next;
+        } else {
+            prev = l;
+        }
+
+        l = next;
+    }
+
+    return list;
+}
+
+static gboolean
+reset_resource_value_foreach(gpointer     data,
+                             gpointer     user_data)
+{
+    DDMDataResource *resource = data;
+
+    return !resource->local;
+}
+
+static gboolean
+reset_property_foreach(gpointer    data,
+                       gpointer    user_data)
+{
+    DDMDataProperty *property = data;
+
+    if (DDM_DATA_BASE(property->value.type) != DDM_DATA_RESOURCE)
+        return FALSE;
+
+    if (DDM_DATA_IS_LIST(property->value.type)) {
+        property->value.u.list = slist_foreach_remove(property->value.u.list, reset_resource_value_foreach, NULL);
+        reset_resource_value_foreach(data, user_data);
+        return FALSE;
+    } else {
+        if (!property->value.u.resource->local) {
+            ddm_data_property_free(property);
+            return TRUE;
+        } else {
+            return FALSE;
+        }
+    }
+}
+
 void
+_ddm_data_resource_reset (DDMDataResource *resource)
+{
+    g_return_if_fail(resource != NULL);
+    g_return_if_fail(resource->local);
+
+    resource->properties = slist_foreach_remove(resource->properties, reset_property_foreach, NULL);
+
+    if (resource->requested_fetch != NULL) {
+        ddm_data_fetch_unref(resource->requested_fetch);
+        resource->requested_fetch = NULL;
+    }
+    
+    if (resource->received_fetch != NULL) {
+        ddm_data_fetch_unref(resource->received_fetch);
+        resource->received_fetch = NULL;
+    }
+
+    resource->requested_serial = -1;    
+}
+
+DDMDataResource *
+ddm_data_resource_ref (DDMDataResource *resource)
+{
+    g_return_val_if_fail(resource != NULL, NULL);
+    g_return_val_if_fail(resource->refcount > 0, NULL);
+
+    resource->refcount++;
+
+    return resource;
+}
+
+void
+ddm_data_resource_unref (DDMDataResource *resource)
+{
+    g_return_if_fail(resource != NULL);
+    g_return_if_fail(resource->refcount > 0);
+
+    resource->refcount--;
+    if (resource->refcount == 0) {
+        g_free(resource->resource_id);
+        g_free(resource->class_id);
+
+        g_slist_foreach(resource->clients, (GFunc)data_client_free, NULL);
+        g_slist_foreach(resource->connections, (GFunc)g_free, NULL);
+        g_slist_foreach(resource->properties, (GFunc)ddm_data_property_free, NULL);
+
+        g_slist_free(resource->changed_properties);
+
+        if (resource->received_fetch != NULL)
+            ddm_data_fetch_unref(resource->received_fetch);
+        if (resource->requested_fetch != NULL)
+            ddm_data_fetch_unref(resource->requested_fetch);
+        
+        g_free(resource);
+    }
+}
+
+void
 ddm_data_resource_set_class_id(DDMDataResource    *resource,
                                const char         *class_id)
 {
@@ -543,30 +683,47 @@
     for (l = resource->clients; l; l = l->next) {
         data_client = l->data;
         if (data_client->client == client) {
-            if (fetch)
+            if (fetch) {
                 ddm_data_fetch_ref(fetch);
+                ddm_data_fetch_unref(data_client->fetch);
             
-            ddm_data_fetch_unref(data_client->fetch);
-            if (fetch) {
                 data_client->fetch = fetch;
             } else {
                 resource->clients = g_slist_remove(resource->clients, data_client);
-                g_free(data_client);
+                data_client_free(data_client);
             }
 
             return;
         }
     }
 
-    data_client = g_new(DataClient, 1);
-    data_client->client = client;
-    data_client->fetch = ddm_data_fetch_ref(fetch);
+    if (fetch) {
+        data_client = g_new(DataClient, 1);
+        data_client->client = client;
+        data_client->fetch = ddm_data_fetch_ref(fetch);
+    }
 }
 
+DDMDataFetch *
+ddm_data_resource_get_client_fetch (DDMDataResource *resource,
+                                    DDMClient       *client)
+{
+    GSList *l;
+    DataClient *data_client;
+
+    for (l = resource->clients; l; l = l->next) {
+        data_client = l->data;
+        if (data_client->client == client)
+            return data_client->fetch;
+    }
+
+    return NULL;
+}
+
 void
 ddm_data_resource_disconnect (DDMDataResource *resource,
                               DDMDataFunction  function,
-                              gpointer           user_data)
+                              gpointer         user_data)
 {
     GSList *l;
 
@@ -802,12 +959,7 @@
                 DDMDataProperty *property)
 {
     resource->properties = g_slist_remove(resource->properties, property);
-    ddm_data_value_clear(&property->value);
-    if (property->default_children) {
-        ddm_data_fetch_unref(property->default_children);
-    }
-
-    g_free(property);
+    ddm_data_property_free(property);
 }
 
 /* return value is whether something changed (we need to emit notification) */

Modified: dumbhippo/trunk/client/common/ddm/ddm-data-resource.h
===================================================================
--- dumbhippo/trunk/client/common/ddm/ddm-data-resource.h	2007-11-07 00:45:26 UTC (rev 6874)
+++ dumbhippo/trunk/client/common/ddm/ddm-data-resource.h	2007-11-07 17:41:12 UTC (rev 6875)
@@ -86,6 +86,9 @@
     } u;
 };
 
+DDMDataResource *ddm_data_resource_ref   (DDMDataResource *resource);
+void             ddm_data_resource_unref (DDMDataResource *resource);
+
 void ddm_data_value_get_element(DDMDataValue *value,
                                 GSList         *element_node,
                                 DDMDataValue *element);
@@ -107,10 +110,13 @@
                                          DDMQName        *property,
                                          DDMDataFunction  function,
                                          gpointer         user_data);
-void ddm_data_resource_set_client_fetch (DDMDataResource *resource,
-                                         DDMClient       *client,
-                                         DDMDataFetch    *fetch);
 
+void          ddm_data_resource_set_client_fetch (DDMDataResource *resource,
+                                                  DDMClient       *client,
+                                                  DDMDataFetch    *fetch);
+DDMDataFetch *ddm_data_resource_get_client_fetch (DDMDataResource *resource,
+                                                  DDMClient       *client);
+
 void ddm_data_resource_disconnect       (DDMDataResource *resource,
                                          DDMDataFunction  function,
                                          gpointer         user_data);

Modified: dumbhippo/trunk/client/common/ddm/ddm-work-item.c
===================================================================
--- dumbhippo/trunk/client/common/ddm/ddm-work-item.c	2007-11-07 00:45:26 UTC (rev 6874)
+++ dumbhippo/trunk/client/common/ddm/ddm-work-item.c	2007-11-07 17:41:12 UTC (rev 6875)
@@ -347,8 +347,10 @@
             }
             break;
         case ITEM_QUERY_RESPONSE:
-            _ddm_data_query_run_response(item->u.query_response.query);
-            break;
+            {
+                _ddm_data_query_run_response(item->u.query_response.query);
+                break;
+            }
         }
     } else {
         g_debug("%s: have unsatisfied fetches; need responses; min_serial=%" G_GINT64_MODIFIER "d", item->id_string,

Modified: dumbhippo/trunk/client/common/ddm/ddm.h
===================================================================
--- dumbhippo/trunk/client/common/ddm/ddm.h	2007-11-07 00:45:26 UTC (rev 6874)
+++ dumbhippo/trunk/client/common/ddm/ddm.h	2007-11-07 17:41:12 UTC (rev 6875)
@@ -14,6 +14,7 @@
 
 #define DDM_INSIDE_DDM_H 1
 
+#include <ddm/ddm-client.h>
 #include <ddm/ddm-data-fetch.h>
 #include <ddm/ddm-data-model.h>
 #include <ddm/ddm-data-model-backend.h>

Modified: dumbhippo/trunk/client/common/hippo/hippo-data-model-backend.c
===================================================================
--- dumbhippo/trunk/client/common/hippo/hippo-data-model-backend.c	2007-11-07 00:45:26 UTC (rev 6874)
+++ dumbhippo/trunk/client/common/hippo/hippo-data-model-backend.c	2007-11-07 17:41:12 UTC (rev 6875)
@@ -79,6 +79,13 @@
     const char *self_id;
     DDMQName *self_id_prop;
     DDMDataValue value;
+
+    /* We first "reset" - deleting all non-local resources from the model, and all property
+     * values that reference non-local properties; then we add back the "self" property, which
+     * references the local resource, *then* we signal that we are reconnected, so that the
+     * self property is already there.
+     */
+    ddm_data_model_reset(hippo_model->ddm_model);
     
     global_resource = ddm_data_model_ensure_local_resource(hippo_model->ddm_model,
                                                            DDM_GLOBAL_RESOURCE, DDM_GLOBAL_RESOURCE_CLASS);
@@ -88,8 +95,7 @@
         self_id = NULL;
     }
 
-    self_id_prop = ddm_qname_get(DDM_GLOBAL_RESOURCE_CLASS,
-                                 "self");
+    self_id_prop = ddm_qname_get(DDM_GLOBAL_RESOURCE_CLASS, "self");
     if (self_id) {
         value.type = DDM_DATA_RESOURCE;
         value.u.resource = ddm_data_model_ensure_resource(hippo_model->ddm_model,
@@ -167,9 +173,15 @@
 
     if (hippo_model == NULL)
         return FALSE;  /* in case model was nuked before getting to idle */
-    
+
+    if (hippo_model->disk_cache == NULL) {
+        ddm_data_query_error(query,
+                             DDM_DATA_ERROR_INTERNAL,
+                             "No connection and query is not cached");
+        return FALSE;
+    }
+
     _hippo_disk_cache_do_query(hippo_model->disk_cache, query);
-    
     return FALSE;
 }
 

Modified: dumbhippo/trunk/client/linux/Makefile-mugshot.am
===================================================================
--- dumbhippo/trunk/client/linux/Makefile-mugshot.am	2007-11-07 00:45:26 UTC (rev 6874)
+++ dumbhippo/trunk/client/linux/Makefile-mugshot.am	2007-11-07 17:41:12 UTC (rev 6875)
@@ -40,6 +40,8 @@
 	src/hippo-dbus-local.h			\
 	src/hippo-dbus-model.c			\
 	src/hippo-dbus-model.h			\
+	src/hippo-dbus-model-client.c		\
+	src/hippo-dbus-model-client.h		\
 	src/hippo-dbus-mugshot.c		\
 	src/hippo-dbus-mugshot.h		\
 	src/hippo-dbus-pidgin.c			\

Added: dumbhippo/trunk/client/linux/src/hippo-dbus-model-client.c
===================================================================
--- dumbhippo/trunk/client/linux/src/hippo-dbus-model-client.c	2007-11-07 00:45:26 UTC (rev 6874)
+++ dumbhippo/trunk/client/linux/src/hippo-dbus-model-client.c	2007-11-07 17:41:12 UTC (rev 6875)
@@ -0,0 +1,658 @@
+/* -*- mode: C; c-basic-offset: 4; indent-tabs-mode: nil; -*- */
+
+#include "hippo-dbus-model-client.h"
+#include "hippo-dbus-server.h"
+#include "hippo-dbus-model.h"
+
+typedef struct _DataClientConnection DataClientConnection;
+typedef struct _DataClientQueryClosure DataClientQueryClosure;
+
+static void hippo_dbus_model_client_iface_init(DDMClientIface *iface);
+
+static void add_resource_to_message (HippoDBusModelClient *client,
+                                     DBusMessageIter      *resource_array_iter,
+                                     DDMDataResource      *resource,
+                                     DDMDataFetch         *fetch,
+                                     gboolean              indirect,
+                                     gboolean              is_notification,
+                                     GSList               *changed_properties);
+
+struct _HippoDBusModelClient {
+    GObject parent;
+
+    DBusConnection *connection;
+    DDMDataModel *model;
+    
+    char *bus_name;
+    char *path;
+    
+    GHashTable *connections;
+    gboolean disconnected;
+};
+
+struct _HippoDBusModelClientClass {
+    GObjectClass parent_class;
+};
+
+struct _DataClientConnection {
+    HippoDBusModelClient *client;
+    DDMDataResource *resource;
+    DDMDataFetch *fetch;
+};
+
+struct _DataClientQueryClosure {
+    HippoDBusModelClient *client;
+    DBusMessage *message;
+    DDMDataFetch *fetch;
+};
+
+G_DEFINE_TYPE_WITH_CODE(HippoDBusModelClient, hippo_dbus_model_client, G_TYPE_OBJECT,
+                        G_IMPLEMENT_INTERFACE(DDM_TYPE_CLIENT, hippo_dbus_model_client_iface_init);)
+
+static DataClientConnection *
+data_client_connection_new (HippoDBusModelClient  *client,
+                            DDMDataResource       *resource)
+{
+    DataClientConnection *connection = g_new0(DataClientConnection, 1);
+
+    connection->client = client;
+    connection->resource = resource;
+    connection->fetch = NULL;
+
+    return connection;
+}
+
+static void
+data_client_connection_set_fetch (DataClientConnection *connection,
+                                  DDMDataFetch       *fetch)
+{
+    if (fetch)
+        ddm_data_fetch_ref(fetch);
+
+    ddm_data_resource_set_client_fetch(connection->resource, DDM_CLIENT(connection->client), fetch);
+    
+    if (connection->fetch)
+        ddm_data_fetch_unref(connection->fetch);
+
+    connection->fetch = fetch;
+}
+
+static void
+data_client_connection_destroy(DataClientConnection *connection)
+{
+    ddm_data_resource_set_client_fetch(connection->resource, DDM_CLIENT(connection->client), NULL);
+    g_free(connection);
+}
+
+/*****************************************************************/
+
+static DataClientQueryClosure *
+data_client_query_closure_new (HippoDBusModelClient *client,
+                               DBusMessage          *message,
+                               DDMDataFetch         *fetch)
+{
+    DataClientQueryClosure *closure = g_new0(DataClientQueryClosure, 1);
+    closure->client = client ? g_object_ref(client) : NULL;
+    closure->message = dbus_message_ref(message);
+    closure->fetch = fetch ? ddm_data_fetch_ref(fetch) : NULL;
+
+    return closure;
+}
+
+static void
+data_client_query_closure_destroy (DataClientQueryClosure *closure)
+{
+    if (closure->client)
+        g_object_unref(closure->client);
+    if (closure->fetch)
+        ddm_data_fetch_unref(closure->fetch);
+    dbus_message_unref(closure->message);
+    g_free(closure);
+}
+
+/*****************************************************************/
+
+static void
+add_property_value_to_message(DBusMessageIter    *property_array_iter,
+                              DDMQName           *property_qname,
+                              DDMDataUpdate       update,
+                              DDMDataValue       *value,
+                              DDMDataCardinality  cardinality)
+{
+    DBusMessageIter property_iter;
+    DBusMessageIter value_iter;
+    char update_byte;
+    char type_byte;
+    char cardinality_byte;
+    const char *value_signature = NULL;
+    
+    switch (update) {
+    case DDM_DATA_UPDATE_ADD:
+        update_byte = 'a';
+        break;
+    case DDM_DATA_UPDATE_REPLACE:
+        update_byte = 'r';
+        break;
+    case DDM_DATA_UPDATE_DELETE:
+        update_byte = 'd';
+        break;
+    case DDM_DATA_UPDATE_CLEAR:
+        update_byte = 'c';
+        break;
+    }
+    
+    switch (value->type) {
+    case DDM_DATA_BOOLEAN:
+        type_byte = 'b';
+        value_signature = "b";
+        break;
+    case DDM_DATA_INTEGER:
+        type_byte = 'i';
+        value_signature = "i";
+        break;
+    case DDM_DATA_LONG:
+        type_byte = 'l';
+        value_signature = "x";
+        break;
+    case DDM_DATA_FLOAT:
+        type_byte = 'f';
+        value_signature = "d";
+        break;
+    case DDM_DATA_NONE: /* Empty list, type doesn't matter */
+    case DDM_DATA_STRING:
+        type_byte = 's';
+        value_signature = "s";
+        break;
+    case DDM_DATA_RESOURCE:
+        type_byte = 'r';
+        value_signature = "s";
+        break;
+    case DDM_DATA_URL:
+        type_byte = 'u';
+        value_signature = "s";
+        break;
+    case DDM_DATA_LIST:
+        break;
+    }
+
+    g_assert(value_signature != NULL);
+    
+    switch (cardinality) {
+    case DDM_DATA_CARDINALITY_01:
+        cardinality_byte = '?';
+        break;
+    case DDM_DATA_CARDINALITY_1:
+        cardinality_byte = '.';
+        break;
+    case DDM_DATA_CARDINALITY_N:
+        cardinality_byte = '*';
+        break;
+    }
+    
+    dbus_message_iter_open_container(property_array_iter, DBUS_TYPE_STRUCT, NULL, &property_iter);
+    dbus_message_iter_append_basic(&property_iter, DBUS_TYPE_STRING, &property_qname->uri);
+    dbus_message_iter_append_basic(&property_iter, DBUS_TYPE_STRING, &property_qname->name);
+    dbus_message_iter_append_basic(&property_iter, DBUS_TYPE_BYTE, &update_byte);
+    dbus_message_iter_append_basic(&property_iter, DBUS_TYPE_BYTE, &type_byte);
+    dbus_message_iter_append_basic(&property_iter, DBUS_TYPE_BYTE, &cardinality_byte);
+
+    dbus_message_iter_open_container(&property_iter, DBUS_TYPE_VARIANT, value_signature, &value_iter);
+    
+    switch (value->type) {
+    case DDM_DATA_BOOLEAN:
+        {
+            dbus_bool_t v = value->u.boolean;
+            dbus_message_iter_append_basic(&value_iter, DBUS_TYPE_BOOLEAN, &v);
+        }
+        break;
+    case DDM_DATA_INTEGER:
+        dbus_message_iter_append_basic(&value_iter, DBUS_TYPE_INT32, &value->u.integer);
+        break;
+    case DDM_DATA_LONG:
+        dbus_message_iter_append_basic(&value_iter, DBUS_TYPE_INT64, &value->u.long_);
+        break;
+    case DDM_DATA_FLOAT:
+        dbus_message_iter_append_basic(&value_iter, DBUS_TYPE_DOUBLE, &value->u.float_);
+        break;
+    case DDM_DATA_NONE:
+        {
+            const char *v = "";
+            dbus_message_iter_append_basic(&value_iter, DBUS_TYPE_STRING, &v);
+        }
+        break;
+    case DDM_DATA_STRING:
+    case DDM_DATA_URL:
+        dbus_message_iter_append_basic(&value_iter, DBUS_TYPE_STRING, &value->u.string);
+        break;
+    case DDM_DATA_RESOURCE:
+        {
+            const char *v = ddm_data_resource_get_resource_id(value->u.resource);
+
+            dbus_message_iter_append_basic(&value_iter, DBUS_TYPE_STRING, &v);
+            
+        }
+        break;
+    case DDM_DATA_LIST:
+        break;
+    }
+    
+    dbus_message_iter_close_container(&property_iter, &value_iter);
+    dbus_message_iter_close_container(property_array_iter, &property_iter);
+}
+
+static void
+add_property_children_to_message(HippoDBusModelClient *client,
+                                 DBusMessageIter      *resource_array_iter,
+                                 DDMDataProperty      *property,
+                                 DDMDataFetch         *children)
+{
+    DDMDataValue value;
+            
+    ddm_data_property_get_value(property, &value);
+    
+    if (value.type == DDM_DATA_RESOURCE) {
+        add_resource_to_message(client, resource_array_iter, value.u.resource, children, TRUE, FALSE, NULL);
+    } else if (value.type == (DDM_DATA_RESOURCE | DDM_DATA_LIST)) {
+        GSList *l;
+        for (l = value.u.list; l; l = l->next)
+            add_resource_to_message(client, resource_array_iter, l->data, children, TRUE, FALSE, NULL);
+    }
+}
+
+static void
+add_property_to_message(DBusMessageIter   *property_array_iter,
+                        DDMDataProperty *property)
+{
+    DDMDataCardinality cardinality;
+    DDMDataValue value;
+    DDMQName *property_qname;
+    
+    ddm_data_property_get_value(property, &value);
+    cardinality = ddm_data_property_get_cardinality(property);
+    property_qname = ddm_data_property_get_qname(property);
+    
+    if (value.type == DDM_DATA_NONE) {
+        add_property_value_to_message(property_array_iter, property_qname,
+                                      DDM_DATA_UPDATE_CLEAR,
+                                      &value, cardinality);
+    } else if (DDM_DATA_IS_LIST(value.type)) {
+        GSList *l;
+        
+        for (l = value.u.list; l; l = l->next) {
+            DDMDataValue element;
+            ddm_data_value_get_element(&value, l, &element);
+            
+            add_property_value_to_message(property_array_iter, property_qname,
+                                          l == value.u.list ? DDM_DATA_UPDATE_REPLACE : DDM_DATA_UPDATE_ADD,
+                                          &element, cardinality);
+        }
+    } else {
+        add_property_value_to_message(property_array_iter, property_qname,
+                                      DDM_DATA_UPDATE_REPLACE,
+                                      &value, cardinality);
+    }
+}
+
+static void
+add_resource_to_message(HippoDBusModelClient *client,
+                        DBusMessageIter      *resource_array_iter,
+                        DDMDataResource      *resource,
+                        DDMDataFetch         *fetch,
+                        gboolean              indirect,
+                        gboolean              is_notification,
+                        GSList               *changed_properties)
+{
+    DDMDataFetchIter fetch_iter;
+    DataClientConnection *connection;
+    DDMDataFetch *new_fetch;
+    DDMDataFetch *total_fetch;
+    DBusMessageIter resource_iter;
+    DBusMessageIter property_array_iter;
+    const char *resource_id;
+    const char *class_id;
+    dbus_bool_t indirect_bool;
+
+    connection = g_hash_table_lookup(client->connections, ddm_data_resource_get_resource_id(resource));
+    if (connection == NULL) {
+        connection = data_client_connection_new(client, resource);
+        g_hash_table_insert(client->connections, (char *)ddm_data_resource_get_resource_id(resource), connection);
+    }
+
+    if (is_notification) {
+        new_fetch = ddm_data_fetch_ref(fetch);
+        total_fetch = ddm_data_fetch_ref(fetch);
+    } else {
+        if (connection->fetch)
+            new_fetch = ddm_data_fetch_subtract(fetch, connection->fetch);
+        else
+            new_fetch = ddm_data_fetch_ref(fetch);
+        
+        if (new_fetch == NULL && indirect)
+            return;
+        
+        if (connection->fetch)
+            total_fetch = ddm_data_fetch_merge(fetch, connection->fetch);
+        else
+            total_fetch = ddm_data_fetch_ref(fetch);
+        
+        data_client_connection_set_fetch(connection, total_fetch);
+    }
+
+    if (new_fetch) {
+        ddm_data_fetch_iter_init(&fetch_iter, resource, new_fetch);
+        while (ddm_data_fetch_iter_has_next(&fetch_iter)) {
+            DDMDataProperty *property;
+            DDMDataFetch *children;
+
+            ddm_data_fetch_iter_next(&fetch_iter, &property, &children);
+
+            /* FIXME: This check on children isn't really right ... if we have a resource-value
+             * property that is default-fetched without default-children, then we should
+             * send an empty resource element for it, because the recipient needs at least
+             * the classId. */
+            if (!children)
+                continue;
+            
+            if (is_notification && g_slist_find(changed_properties, ddm_data_property_get_qname(property)) == NULL)
+                continue;
+            
+            add_property_children_to_message(client, resource_array_iter, property, children);
+        }
+        ddm_data_fetch_iter_clear(&fetch_iter);
+    }
+    
+    resource_id = ddm_data_resource_get_resource_id(resource);
+    class_id = ddm_data_resource_get_class_id(resource);
+    indirect_bool = indirect;
+
+    dbus_message_iter_open_container(resource_array_iter, DBUS_TYPE_STRUCT, NULL, &resource_iter);
+    dbus_message_iter_append_basic(&resource_iter, DBUS_TYPE_STRING, &resource_id);
+    dbus_message_iter_append_basic(&resource_iter, DBUS_TYPE_STRING, &class_id);
+    dbus_message_iter_append_basic(&resource_iter, DBUS_TYPE_BOOLEAN, &indirect_bool);
+    
+    dbus_message_iter_open_container(&resource_iter, DBUS_TYPE_ARRAY, "(ssyyyv)", &property_array_iter);
+
+    if (new_fetch) {
+        ddm_data_fetch_iter_init(&fetch_iter, resource, new_fetch);
+        while (ddm_data_fetch_iter_has_next(&fetch_iter)) {
+            DDMDataProperty *property;
+
+            ddm_data_fetch_iter_next(&fetch_iter, &property, NULL);
+
+            if (is_notification && g_slist_find(changed_properties, ddm_data_property_get_qname(property)) == NULL)
+                continue;
+            
+            add_property_to_message(&property_array_iter, property);
+        }
+        
+        ddm_data_fetch_iter_clear(&fetch_iter);
+    }
+    
+    dbus_message_iter_close_container(&resource_iter, &property_array_iter);
+    dbus_message_iter_close_container(resource_array_iter, &resource_iter);
+
+    if (new_fetch)
+        ddm_data_fetch_unref(new_fetch);
+    ddm_data_fetch_unref(total_fetch);
+}
+
+/*****************************************************************/
+
+static void
+hippo_dbus_model_client_dispose (GObject *object)
+{
+    HippoDBusModelClient *dbus_client = HIPPO_DBUS_MODEL_CLIENT(object);
+    if (!dbus_client->disconnected) {
+        dbus_client->disconnected = TRUE;
+        
+        hippo_dbus_unwatch_for_disconnect(hippo_app_get_dbus(hippo_get_app()),
+                                          dbus_client->bus_name);
+
+        g_hash_table_destroy(dbus_client->connections);
+        dbus_client->connections = NULL;
+    }
+}
+
+static void
+hippo_dbus_model_client_finalize (GObject *object)
+{
+    HippoDBusModelClient *dbus_client = HIPPO_DBUS_MODEL_CLIENT(object);
+    
+    g_free(dbus_client->bus_name);
+    g_free(dbus_client->path);
+}
+
+static void
+hippo_dbus_model_client_init (HippoDBusModelClient *dbus_client)
+{
+    dbus_client->connections = g_hash_table_new_full(g_str_hash, g_str_equal,
+                                                     NULL, (GDestroyNotify)data_client_connection_destroy);
+    dbus_client->disconnected = FALSE;
+}
+
+static void
+hippo_dbus_model_client_class_init (HippoDBusModelClientClass *klass)
+{
+    GObjectClass *object_class = G_OBJECT_CLASS (klass);
+
+    object_class->dispose = hippo_dbus_model_client_dispose;
+    object_class->finalize = hippo_dbus_model_client_finalize;
+}
+
+static gpointer
+hippo_dbus_model_client_begin_notification (DDMClient *client)
+{
+    return NULL;
+}
+
+static void
+hippo_dbus_model_client_notify (DDMClient       *client,
+                                DDMDataResource *resource,
+                                GSList          *changed_properties,
+                                gpointer         notification_data)
+{
+    HippoDBusModelClient *dbus_client = HIPPO_DBUS_MODEL_CLIENT(client);
+    DataClientConnection *client_connection = g_hash_table_lookup(dbus_client->connections,
+                                                                  ddm_data_resource_get_resource_id(resource));
+    DBusMessage *message;
+    DBusMessageIter iter;
+    DBusMessageIter array_iter;
+    
+    message = dbus_message_new_method_call(dbus_client->bus_name, dbus_client->path,
+                                           HIPPO_DBUS_MODEL_CLIENT_INTERFACE, "Notify");
+    
+    dbus_message_iter_init_append(message, &iter);
+
+    dbus_message_iter_open_container(&iter, DBUS_TYPE_ARRAY, "(ssba(ssyyyv))", &array_iter);
+
+    add_resource_to_message(dbus_client, &array_iter,
+                            resource, client_connection->fetch,
+                            FALSE,
+                            TRUE, changed_properties);
+    
+    dbus_message_iter_close_container(&iter, &array_iter);
+
+    dbus_connection_send(dbus_client->connection, message, NULL);
+
+    /* FIXME: We should catch errors, and kick the client connection on error */
+
+    dbus_message_unref(message);
+}
+
+static void
+hippo_dbus_model_client_end_notification (DDMClient       *client,
+                                          gpointer         notification_data)
+{
+}
+
+static void
+hippo_dbus_model_client_iface_init(DDMClientIface *iface)
+{
+    iface->begin_notification = hippo_dbus_model_client_begin_notification;
+    iface->notify = hippo_dbus_model_client_notify;
+    iface->end_notification = hippo_dbus_model_client_end_notification;
+}
+
+/*****************************************************************/
+
+HippoDBusModelClient *
+hippo_dbus_model_client_new (DBusConnection *connection,
+                             DDMDataModel   *model,
+                             const char     *bus_name,
+                             const char     *path)
+{
+    HippoDBusModelClient *dbus_client;
+
+    g_return_val_if_fail(DDM_IS_DATA_MODEL(model), NULL);
+    g_return_val_if_fail(bus_name != NULL, NULL);
+    g_return_val_if_fail(path != NULL, NULL);
+    
+    dbus_client = g_object_new(HIPPO_TYPE_DBUS_MODEL_CLIENT, NULL);
+
+    dbus_client->connection = connection;
+    dbus_client->model = model;
+    dbus_client->bus_name = g_strdup(bus_name);
+    dbus_client->path = g_strdup(path);
+
+    hippo_dbus_watch_for_disconnect(hippo_app_get_dbus(hippo_get_app()),
+                                    bus_name);
+
+    return dbus_client;
+}
+
+const char *
+hippo_dbus_model_client_get_bus_name (HippoDBusModelClient *dbus_client)
+{
+    g_return_val_if_fail(HIPPO_IS_DBUS_MODEL_CLIENT(dbus_client), NULL);
+
+    return dbus_client->bus_name;
+}
+
+void
+hippo_dbus_model_client_disconnected (HippoDBusModelClient *dbus_client)
+{
+    g_return_if_fail(HIPPO_IS_DBUS_MODEL_CLIENT(dbus_client));
+    
+    g_object_run_dispose(G_OBJECT(dbus_client));
+}
+
+/*****************************************************************/
+
+static void
+on_query_success (GSList  *results,
+                  gpointer data)
+{
+    DataClientQueryClosure *closure = data;
+    DBusMessageIter iter;
+    DBusMessageIter array_iter;
+    DBusMessage *reply;
+    GSList *l;
+
+    reply = dbus_message_new_method_return(closure->message);
+    dbus_message_iter_init_append(reply, &iter);
+
+    dbus_message_iter_open_container(&iter, DBUS_TYPE_ARRAY, "(ssba(ssyyyv))", &array_iter);
+
+    for (l = results; l; l = l->next) {
+        add_resource_to_message(closure->client, &array_iter, l->data, closure->fetch, FALSE, FALSE, NULL);
+    }
+    
+    dbus_message_iter_close_container(&iter, &array_iter);
+
+    dbus_connection_send(closure->client->connection, reply, NULL);
+    dbus_message_unref(reply);
+    
+    data_client_query_closure_destroy(closure);
+}
+
+static void
+on_query_error (DDMDataError    error,
+                const char     *message,
+                gpointer        data)
+{
+    DataClientQueryClosure *closure = data;
+    DBusMessage *reply;
+    DBusMessageIter iter;
+    dbus_int32_t code = (dbus_int32_t)error;
+
+    reply = dbus_message_new_error(closure->message,
+                                   HIPPO_DBUS_MODEL_ERROR,
+                                   message);
+    
+    dbus_message_iter_init_append(reply, &iter);
+    dbus_message_iter_append_basic(&iter, DBUS_TYPE_INT32, &code);
+    
+    dbus_connection_send(closure->client->connection, reply, NULL);
+    dbus_message_unref(reply);
+    
+    data_client_query_closure_destroy(closure);
+}
+
+gboolean
+hippo_dbus_model_client_do_query (HippoDBusModelClient *client,
+                                  DBusMessage          *message,
+                                  const char           *method_uri,
+                                  DDMDataFetch         *fetch,
+                                  GHashTable           *params)
+{
+    DataClientQueryClosure *closure;
+    DDMDataQuery *query;
+    char *fetch_string;
+
+    g_return_val_if_fail(HIPPO_IS_DBUS_MODEL_CLIENT(client), FALSE);
+    g_return_val_if_fail(!client->disconnected, FALSE);
+    
+    closure = data_client_query_closure_new(client, message, fetch);
+
+    fetch_string = ddm_data_fetch_to_string(fetch);
+    query = ddm_data_model_query_params(client->model, method_uri, fetch_string, params);
+    g_free(fetch_string);
+    
+    if (query == NULL) {
+        data_client_query_closure_destroy(closure);
+        return FALSE;
+    }
+
+    ddm_data_query_set_multi_handler(query, on_query_success, closure);
+    ddm_data_query_set_error_handler(query, on_query_error, closure);
+
+    return TRUE;
+}
+
+/*****************************************************************/
+
+static void
+on_update_success (gpointer data)
+{
+    DataClientQueryClosure *closure = data;
+    DBusMessage *reply;
+
+    reply = dbus_message_new_method_return(closure->message);
+    dbus_connection_send(closure->client->connection, reply, NULL);
+    dbus_message_unref(reply);
+    
+    data_client_query_closure_destroy(closure);
+}
+
+gboolean
+hippo_dbus_model_client_do_update (DDMDataModel *model,
+                                   DBusMessage  *message,
+                                   const char   *method_uri,
+                                   GHashTable   *params)
+{
+    DataClientQueryClosure *closure;
+    DDMDataQuery *query;
+    
+    closure = data_client_query_closure_new(NULL, message, NULL);
+
+    query = ddm_data_model_update_params(model, method_uri, params);
+    if (query == NULL) {
+        data_client_query_closure_destroy(closure);
+        return FALSE;
+    }
+
+    ddm_data_query_set_update_handler(query, on_update_success, closure);
+    ddm_data_query_set_error_handler(query, on_query_error, closure);
+
+    return TRUE;
+}

Added: dumbhippo/trunk/client/linux/src/hippo-dbus-model-client.h
===================================================================
--- dumbhippo/trunk/client/linux/src/hippo-dbus-model-client.h	2007-11-07 00:45:26 UTC (rev 6874)
+++ dumbhippo/trunk/client/linux/src/hippo-dbus-model-client.h	2007-11-07 17:41:12 UTC (rev 6875)
@@ -0,0 +1,52 @@
+/* -*- mode: C; c-basic-offset: 4; indent-tabs-mode: nil; -*- */
+
+#ifndef __HIPPO_DBUS_MODEL_CLIENT_H__
+#define __HIPPO_DBUS_MODEL_CLIENT_H__
+
+#include <ddm/ddm.h>
+#include "hippo-dbus-helper.h"
+
+G_BEGIN_DECLS
+
+/* Client object representing an incoming D-BUS connection to the data model
+ */
+
+typedef struct _HippoDBusModelClient      HippoDBusModelClient;
+typedef struct _HippoDBusModelClientClass HippoDBusModelClientClass;
+typedef struct _HippoDBusModelClientId    HippoDBusModelClientId;
+
+#define HIPPO_TYPE_DBUS_MODEL_CLIENT              (hippo_dbus_model_client_get_type ())
+#define HIPPO_DBUS_MODEL_CLIENT(object)           (G_TYPE_CHECK_INSTANCE_CAST ((object), HIPPO_TYPE_DBUS_MODEL_CLIENT, HippoDBusModelClient))
+#define HIPPO_DBUS_MODEL_CLIENT_CLASS(klass)      (G_TYPE_CHECK_CLASS_CAST ((klass), HIPPO_TYPE_DBUS_MODEL_CLIENT, HippoDBusModelClientClass))
+#define HIPPO_IS_DBUS_MODEL_CLIENT(object)        (G_TYPE_CHECK_INSTANCE_TYPE ((object), HIPPO_TYPE_DBUS_MODEL_CLIENT))
+#define HIPPO_IS_DBUS_MODEL_CLIENT_CLASS(klass)   (G_TYPE_CHECK_CLASS_TYPE ((klass), HIPPO_TYPE_DBUS_MODEL_CLIENT))
+#define HIPPO_DBUS_MODEL_CLIENT_GET_CLASS(obj)    (G_TYPE_INSTANCE_GET_CLASS ((obj), HIPPO_TYPE_DBUS_MODEL_CLIENT, HippoDBusModelClientClass))
+
+GType hippo_dbus_model_client_get_type (void) G_GNUC_CONST;
+
+HippoDBusModelClient *hippo_dbus_model_client_new (DBusConnection *connection,
+                                                   DDMDataModel   *model,
+                                                   const char     *bus_name,
+                                                   const char     *path);
+
+const char *hippo_dbus_model_client_get_bus_name (HippoDBusModelClient *client);
+void        hippo_dbus_model_client_disconnected (HippoDBusModelClient *client);
+
+gboolean hippo_dbus_model_client_do_query  (HippoDBusModelClient *client,
+                                            DBusMessage          *message,
+                                            const char           *method_uri,
+                                            DDMDataFetch         *fetch,
+                                            GHashTable           *params);
+
+/* Since the update() method doesn't take a notification path, we don't
+ * know or need to know the client it corresponds to. But we put it in
+ * here because of it's close connection to do_query()
+ */
+gboolean hippo_dbus_model_client_do_update (DDMDataModel         *model,
+                                            DBusMessage          *message,
+                                            const char           *method_uri,
+                                            GHashTable           *params);
+
+G_END_DECLS
+
+#endif /* __HIPPO_DBUS_MODEL_CLIENT_H__ */

Modified: dumbhippo/trunk/client/linux/src/hippo-dbus-model.c
===================================================================
--- dumbhippo/trunk/client/linux/src/hippo-dbus-model.c	2007-11-07 00:45:26 UTC (rev 6874)
+++ dumbhippo/trunk/client/linux/src/hippo-dbus-model.c	2007-11-07 17:41:12 UTC (rev 6875)
@@ -6,6 +6,7 @@
 #include "hippo-dbus-helper.h"
 #include <ddm/ddm.h>
 #include "hippo-dbus-model.h"
+#include "hippo-dbus-model-client.h"
 #include "main.h"
 
 /* FIXME it's probably a broken layering whenever we need
@@ -17,97 +18,22 @@
 #include <hippo/hippo-data-cache.h>
 
 typedef struct _DataClientId           DataClientId;
-typedef struct _DataClientConnection   DataClientConnection;
-typedef struct _DataClient             DataClient;
 typedef struct _DataClientMap          DataClientMap;
-typedef struct _DataClientQueryClosure DataClientQueryClosure;
 
-struct _DataClientFetch
-{
-    guint ref_count;
-    
-    char **properties;
-    char **fetch_children;
-};
-
 struct _DataClientId {
     char *bus_name;
     char *path;
 };
 
-struct _DataClientConnection {
-    DataClient *client;
-    DDMDataResource *resource;
-    DDMDataFetch *fetch;
-};
-
-struct _DataClient {
-    guint ref_count;
-    
-    DataClientId id;
-    
-    GHashTable *connections;
-
-    gboolean disconnected;
-};
-
 struct _DataClientMap {
+    DDMDataModel *model;
     GHashTable *clients;
 };
 
-struct _DataClientQueryClosure {
-    DataClient *client;
-    DBusMessage *message;
-    DDMDataFetch *fetch;
-};
-
-static DataClient *data_client_ref         (DataClient      *client);
-static void        data_client_unref       (DataClient      *client);
-static void        add_resource_to_message (DataClient      *client,
-                                            DBusMessageIter *resource_array_iter,
-                                            DDMDataResource *resource,
-                                            DDMDataFetch    *fetch,
-                                            gboolean         indirect,
-                                            gboolean         is_notification,
-                                            GSList          *changed_properties);
 static void        on_connected_changed    (DDMDataModel    *ddm_model,
                                             gboolean         connected,
                                             void            *data);
 
-
-static void
-on_resource_changed(DDMDataResource *resource,
-                    GSList            *changed_properties,
-                    gpointer           data)
-{
-    DBusConnection *connection = hippo_dbus_get_connection(hippo_app_get_dbus(hippo_get_app()));
-    DataClientConnection *client_connection = data;
-    DataClient *client = client_connection->client;
-    DBusMessage *message;
-    DBusMessageIter iter;
-    DBusMessageIter array_iter;
-    
-    message = dbus_message_new_method_call(client->id.bus_name, client->id.path,
-                                           HIPPO_DBUS_MODEL_CLIENT_INTERFACE, "Notify");
-    
-    dbus_message_iter_init_append(message, &iter);
-
-    dbus_message_iter_open_container(&iter, DBUS_TYPE_ARRAY, "(ssba(ssyyyv))", &array_iter);
-
-    add_resource_to_message(client, &array_iter,
-                            client_connection->resource, client_connection->fetch,
-                            FALSE,
-                            TRUE, changed_properties);
-    
-    dbus_message_iter_close_container(&iter, &array_iter);
-
-    dbus_connection_send(connection, message, NULL);
-
-    /* FIXME: We should catch errors, and kick the client connection on error */
-
-    dbus_message_unref(message);
-}
-
 static guint
 data_client_id_hash(const DataClientId *id)
 {
@@ -121,114 +47,26 @@
     return strcmp(a->bus_name, b->bus_name) == 0 && strcmp(a->path, b->path) == 0;
 }
 
-static DataClientConnection *
-data_client_connection_new(DataClient        *client,
-                           DDMDataResource *resource)
+static DataClientId *
+data_client_id_copy(const DataClientId *other)
 {
-    DataClientConnection *connection = g_new0(DataClientConnection, 1);
+    DataClientId *id = g_new(DataClientId, 1);
+    id->bus_name = g_strdup(other->bus_name);
+    id->path = g_strdup(other->path);
 
-    connection->client = client;
-    connection->resource = resource;
-    connection->fetch = NULL;
-
-    ddm_data_resource_connect(connection->resource, NULL,
-                              on_resource_changed, connection);
-
-    return connection;
+    return id;
 }
 
 static void
-data_client_connection_set_fetch(DataClientConnection *connection,
-                                 DDMDataFetch       *fetch)
+data_client_id_free(DataClientId *id)
 {
-    if (fetch)
-        ddm_data_fetch_ref(fetch);
-    
-    if (connection->fetch)
-        ddm_data_fetch_unref(connection->fetch);
+    g_free(id->bus_name);
+    g_free(id->path);
 
-    connection->fetch = fetch;
+    g_free(id);
 }
 
 static void
-data_client_connection_destroy(DataClientConnection *connection)
-{
-    ddm_data_resource_disconnect(connection->resource,
-                                   on_resource_changed, connection);
-    
-    ddm_data_fetch_unref(connection->fetch);
-    g_free(connection);
-}
-
-static DataClientQueryClosure *
-data_client_query_closure_new(DataClient    *client,
-                              DBusMessage   *message,
-                              DDMDataFetch  *fetch)
-{
-    DataClientQueryClosure *closure = g_new0(DataClientQueryClosure, 1);
-    closure->client = client ? data_client_ref(client) : NULL;
-    closure->message = dbus_message_ref(message);
-    closure->fetch = fetch ? ddm_data_fetch_ref(fetch) : NULL;
-
-    return closure;
-}
-
-static void
-data_client_query_closure_destroy(DataClientQueryClosure *closure)
-{
-    if (closure->client)
-        data_client_unref(closure->client);
-    if (closure->fetch)
-        ddm_data_fetch_unref(closure->fetch);
-    dbus_message_unref(closure->message);
-    g_free(closure);
-}
-
-static DataClient *
-data_client_new(DataClientId *id)
-{
-    DataClient *client = g_new(DataClient, 1);
-
-    client->ref_count = 1;
-    
-    client->id.bus_name = g_strdup(id->bus_name);
-    client->id.path = g_strdup(id->path);
-    client->connections = g_hash_table_new_full(g_str_hash, g_str_equal,
-                                                NULL, (GDestroyNotify)data_client_connection_destroy);
-
-    client->disconnected = FALSE;
-
-    hippo_dbus_watch_for_disconnect(hippo_app_get_dbus(hippo_get_app()),
-                                    client->id.bus_name);
-
-    return client;
-}
-
-static DataClient *
-data_client_ref(DataClient *client)
-{
-    client->ref_count++;
-
-    return client;
-}
-
-static void
-data_client_unref(DataClient *client)
-{
-    client->ref_count--;
-
-    if (client->ref_count == 0) {
-        hippo_dbus_unwatch_for_disconnect(hippo_app_get_dbus(hippo_get_app()),
-                                          client->id.bus_name);
-
-        g_free(client->id.bus_name);
-        g_free(client->id.path);
-        g_hash_table_destroy(client->connections);
-        g_free(client);
-    }
-}
-
-static void
 data_client_map_destroy(DataClientMap *map)
 {
     g_hash_table_destroy(map->clients);
@@ -241,8 +79,9 @@
     DataClientMap *map = g_object_get_data(G_OBJECT(ddm_model), "hippo-client-map");
     if (map == NULL) {
         map = g_new0(DataClientMap, 1);
+        map->model = ddm_model;
         map->clients = g_hash_table_new_full((GHashFunc)data_client_id_hash, (GEqualFunc)data_client_id_equal,
-                                             NULL, (GDestroyNotify)data_client_unref);
+                                             (GDestroyNotify)data_client_id_free, (GDestroyNotify)g_object_unref);
         g_object_set_data_full(G_OBJECT(ddm_model), "hippo-client-map",
                                map, (GDestroyNotify)data_client_map_destroy);
     }
@@ -250,20 +89,22 @@
     return map;
 }
 
-static DataClient *
+static HippoDBusModelClient *
 data_client_map_get_client(DataClientMap *map,
                            const char    *bus_name,
                            const char    *path)
 {
-    DataClient *client;
+    HippoDBusModelClient *client;
     DataClientId id;
     id.bus_name = (char *)bus_name;
     id.path = (char *)path;
 
     client = g_hash_table_lookup(map->clients, &id);
     if (client == NULL) {
-        client = data_client_new(&id);
-        g_hash_table_insert(map->clients, &client->id, client);
+        DBusConnection *connection = hippo_dbus_get_connection(hippo_app_get_dbus(hippo_get_app()));
+
+        client = hippo_dbus_model_client_new(connection, map->model, bus_name, path);
+        g_hash_table_insert(map->clients, data_client_id_copy(&id), client);
     }
 
     return client;
@@ -312,348 +153,11 @@
     return NULL;
 }
 
-static void
-add_property_value_to_message(DBusMessageIter      *property_array_iter,
-                              DDMQName           *property_qname,
-                              DDMDataUpdate       update,
-                              DDMDataValue       *value,
-                              DDMDataCardinality  cardinality)
-{
-    DBusMessageIter property_iter;
-    DBusMessageIter value_iter;
-    char update_byte;
-    char type_byte;
-    char cardinality_byte;
-    const char *value_signature = NULL;
-    
-    switch (update) {
-    case DDM_DATA_UPDATE_ADD:
-        update_byte = 'a';
-        break;
-    case DDM_DATA_UPDATE_REPLACE:
-        update_byte = 'r';
-        break;
-    case DDM_DATA_UPDATE_DELETE:
-        update_byte = 'd';
-        break;
-    case DDM_DATA_UPDATE_CLEAR:
-        update_byte = 'c';
-        break;
-    }
-    
-    switch (value->type) {
-    case DDM_DATA_BOOLEAN:
-        type_byte = 'b';
-        value_signature = "b";
-        break;
-    case DDM_DATA_INTEGER:
-        type_byte = 'i';
-        value_signature = "i";
-        break;
-    case DDM_DATA_LONG:
-        type_byte = 'l';
-        value_signature = "x";
-        break;
-    case DDM_DATA_FLOAT:
-        type_byte = 'f';
-        value_signature = "d";
-        break;
-    case DDM_DATA_NONE: /* Empty list, type doesn't matter */
-    case DDM_DATA_STRING:
-        type_byte = 's';
-        value_signature = "s";
-        break;
-    case DDM_DATA_RESOURCE:
-        type_byte = 'r';
-        value_signature = "s";
-        break;
-    case DDM_DATA_URL:
-        type_byte = 'u';
-        value_signature = "s";
-        break;
-    case DDM_DATA_LIST:
-        break;
-    }
-
-    g_assert(value_signature != NULL);
-    
-    switch (cardinality) {
-    case DDM_DATA_CARDINALITY_01:
-        cardinality_byte = '?';
-        break;
-    case DDM_DATA_CARDINALITY_1:
-        cardinality_byte = '.';
-        break;
-    case DDM_DATA_CARDINALITY_N:
-        cardinality_byte = '*';
-        break;
-    }
-    
-    dbus_message_iter_open_container(property_array_iter, DBUS_TYPE_STRUCT, NULL, &property_iter);
-    dbus_message_iter_append_basic(&property_iter, DBUS_TYPE_STRING, &property_qname->uri);
-    dbus_message_iter_append_basic(&property_iter, DBUS_TYPE_STRING, &property_qname->name);
-    dbus_message_iter_append_basic(&property_iter, DBUS_TYPE_BYTE, &update_byte);
-    dbus_message_iter_append_basic(&property_iter, DBUS_TYPE_BYTE, &type_byte);
-    dbus_message_iter_append_basic(&property_iter, DBUS_TYPE_BYTE, &cardinality_byte);
-
-    dbus_message_iter_open_container(&property_iter, DBUS_TYPE_VARIANT, value_signature, &value_iter);
-    
-    switch (value->type) {
-    case DDM_DATA_BOOLEAN:
-        {
-            dbus_bool_t v = value->u.boolean;
-            dbus_message_iter_append_basic(&value_iter, DBUS_TYPE_BOOLEAN, &v);
-        }
-        break;
-    case DDM_DATA_INTEGER:
-        dbus_message_iter_append_basic(&value_iter, DBUS_TYPE_INT32, &value->u.integer);
-        break;
-    case DDM_DATA_LONG:
-        dbus_message_iter_append_basic(&value_iter, DBUS_TYPE_INT64, &value->u.long_);
-        break;
-    case DDM_DATA_FLOAT:
-        dbus_message_iter_append_basic(&value_iter, DBUS_TYPE_DOUBLE, &value->u.float_);
-        break;
-    case DDM_DATA_NONE:
-        {
-            const char *v = "";
-            dbus_message_iter_append_basic(&value_iter, DBUS_TYPE_STRING, &v);
-        }
-        break;
-    case DDM_DATA_STRING:
-    case DDM_DATA_URL:
-        dbus_message_iter_append_basic(&value_iter, DBUS_TYPE_STRING, &value->u.string);
-        break;
-    case DDM_DATA_RESOURCE:
-        {
-            const char *v = ddm_data_resource_get_resource_id(value->u.resource);
-
-            dbus_message_iter_append_basic(&value_iter, DBUS_TYPE_STRING, &v);
-            
-        }
-        break;
-    case DDM_DATA_LIST:
-        break;
-    }
-    
-    dbus_message_iter_close_container(&property_iter, &value_iter);
-    dbus_message_iter_close_container(property_array_iter, &property_iter);
-}
-
-static void
-add_property_children_to_message(DataClient        *client,
-                                 DBusMessageIter   *resource_array_iter,
-                                 DDMDataProperty *property,
-                                 DDMDataFetch    *children)
-{
-    DDMDataValue value;
-            
-    ddm_data_property_get_value(property, &value);
-    
-    if (value.type == DDM_DATA_RESOURCE) {
-        add_resource_to_message(client, resource_array_iter, value.u.resource, children, TRUE, FALSE, NULL);
-    } else if (value.type == (DDM_DATA_RESOURCE | DDM_DATA_LIST)) {
-        GSList *l;
-        for (l = value.u.list; l; l = l->next)
-            add_resource_to_message(client, resource_array_iter, l->data, children, TRUE, FALSE, NULL);
-    }
-}
-
-static void
-add_property_to_message(DBusMessageIter   *property_array_iter,
-                        DDMDataProperty *property)
-{
-    DDMDataCardinality cardinality;
-    DDMDataValue value;
-    DDMQName *property_qname;
-    
-    ddm_data_property_get_value(property, &value);
-    cardinality = ddm_data_property_get_cardinality(property);
-    property_qname = ddm_data_property_get_qname(property);
-    
-    if (value.type == DDM_DATA_NONE) {
-        add_property_value_to_message(property_array_iter, property_qname,
-                                      DDM_DATA_UPDATE_CLEAR,
-                                      &value, cardinality);
-    } else if (DDM_DATA_IS_LIST(value.type)) {
-        GSList *l;
-        
-        for (l = value.u.list; l; l = l->next) {
-            DDMDataValue element;
-            ddm_data_value_get_element(&value, l, &element);
-            
-            add_property_value_to_message(property_array_iter, property_qname,
-                                          l == value.u.list ? DDM_DATA_UPDATE_REPLACE : DDM_DATA_UPDATE_ADD,
-                                          &element, cardinality);
-        }
-    } else {
-        add_property_value_to_message(property_array_iter, property_qname,
-                                      DDM_DATA_UPDATE_REPLACE,
-                                      &value, cardinality);
-    }
-}
-
-static void
-add_resource_to_message(DataClient        *client,
-                        DBusMessageIter   *resource_array_iter,
-                        DDMDataResource *resource,
-                        DDMDataFetch    *fetch,
-                        gboolean           indirect,
-                        gboolean           is_notification,
-                        GSList            *changed_properties)
-{
-    DDMDataFetchIter fetch_iter;
-    DataClientConnection *connection;
-    DDMDataFetch *new_fetch;
-    DDMDataFetch *total_fetch;
-    DBusMessageIter resource_iter;
-    DBusMessageIter property_array_iter;
-    const char *resource_id;
-    const char *class_id;
-    dbus_bool_t indirect_bool;
-
-    connection = g_hash_table_lookup(client->connections, ddm_data_resource_get_resource_id(resource));
-    if (connection == NULL) {
-        connection = data_client_connection_new(client, resource);
-        g_hash_table_insert(client->connections, (char *)ddm_data_resource_get_resource_id(resource), connection);
-    }
-
-    if (is_notification) {
-        new_fetch = ddm_data_fetch_ref(fetch);
-        total_fetch = ddm_data_fetch_ref(fetch);
-    } else {
-        if (connection->fetch)
-            new_fetch = ddm_data_fetch_subtract(fetch, connection->fetch);
-        else
-            new_fetch = ddm_data_fetch_ref(fetch);
-        
-        if (new_fetch == NULL && indirect)
-            return;
-        
-        if (connection->fetch)
-            total_fetch = ddm_data_fetch_merge(fetch, connection->fetch);
-        else
-            total_fetch = ddm_data_fetch_ref(fetch);
-        
-        data_client_connection_set_fetch(connection, total_fetch);
-    }
-
-    if (new_fetch) {
-        ddm_data_fetch_iter_init(&fetch_iter, resource, new_fetch);
-        while (ddm_data_fetch_iter_has_next(&fetch_iter)) {
-            DDMDataProperty *property;
-            DDMDataFetch *children;
-
-            ddm_data_fetch_iter_next(&fetch_iter, &property, &children);
-
-            /* FIXME: This check on children isn't really right ... if we have a resource-value
-             * property that is default-fetched without default-children, then we should
-             * send an empty resource element for it, because the recipient needs at least
-             * the classId. */
-            if (!children)
-                continue;
-            
-            if (is_notification && g_slist_find(changed_properties, ddm_data_property_get_qname(property)) == NULL)
-                continue;
-            
-            add_property_children_to_message(client, resource_array_iter, property, children);
-        }
-        ddm_data_fetch_iter_clear(&fetch_iter);
-    }
-    
-    resource_id = ddm_data_resource_get_resource_id(resource);
-    class_id = ddm_data_resource_get_class_id(resource);
-    indirect_bool = indirect;
-
-    dbus_message_iter_open_container(resource_array_iter, DBUS_TYPE_STRUCT, NULL, &resource_iter);
-    dbus_message_iter_append_basic(&resource_iter, DBUS_TYPE_STRING, &resource_id);
-    dbus_message_iter_append_basic(&resource_iter, DBUS_TYPE_STRING, &class_id);
-    dbus_message_iter_append_basic(&resource_iter, DBUS_TYPE_BOOLEAN, &indirect_bool);
-    
-    dbus_message_iter_open_container(&resource_iter, DBUS_TYPE_ARRAY, "(ssyyyv)", &property_array_iter);
-
-    if (new_fetch) {
-        ddm_data_fetch_iter_init(&fetch_iter, resource, new_fetch);
-        while (ddm_data_fetch_iter_has_next(&fetch_iter)) {
-            DDMDataProperty *property;
-
-            ddm_data_fetch_iter_next(&fetch_iter, &property, NULL);
-
-            if (is_notification && g_slist_find(changed_properties, ddm_data_property_get_qname(property)) == NULL)
-                continue;
-            
-            add_property_to_message(&property_array_iter, property);
-        }
-        
-        ddm_data_fetch_iter_clear(&fetch_iter);
-    }
-    
-    dbus_message_iter_close_container(&resource_iter, &property_array_iter);
-    dbus_message_iter_close_container(resource_array_iter, &resource_iter);
-
-    if (new_fetch)
-        ddm_data_fetch_unref(new_fetch);
-    ddm_data_fetch_unref(total_fetch);
-}
-
-static void
-on_query_success(GSList  *results,
-                 gpointer data)
-{
-    DBusConnection *connection = hippo_dbus_get_connection(hippo_app_get_dbus(hippo_get_app()));
-    DataClientQueryClosure *closure = data;
-    DBusMessageIter iter;
-    DBusMessageIter array_iter;
-    DBusMessage *reply;
-    GSList *l;
-
-    reply = dbus_message_new_method_return(closure->message);
-    dbus_message_iter_init_append(reply, &iter);
-
-    dbus_message_iter_open_container(&iter, DBUS_TYPE_ARRAY, "(ssba(ssyyyv))", &array_iter);
-
-    for (l = results; l; l = l->next) {
-        add_resource_to_message(closure->client, &array_iter, l->data, closure->fetch, FALSE, FALSE, NULL);
-    }
-    
-    dbus_message_iter_close_container(&iter, &array_iter);
-
-    dbus_connection_send(connection, reply, NULL);
-    dbus_message_unref(reply);
-    
-    data_client_query_closure_destroy(closure);
-}
-
-static void
-on_query_error(DDMDataError    error,
-               const char     *message,
-               gpointer        data)
-{
-    DBusConnection *connection = hippo_dbus_get_connection(hippo_app_get_dbus(hippo_get_app()));
-    DataClientQueryClosure *closure = data;
-    DBusMessage *reply;
-    DBusMessageIter iter;
-    dbus_int32_t code = (dbus_int32_t)error;
-
-    reply = dbus_message_new_error(closure->message,
-                                   HIPPO_DBUS_MODEL_ERROR,
-                                   message);
-    
-    dbus_message_iter_init_append(reply, &iter);
-    dbus_message_iter_append_basic(&iter, DBUS_TYPE_INT32, &code);
-    
-    dbus_connection_send(connection, reply, NULL);
-    dbus_message_unref(reply);
-    
-    data_client_query_closure_destroy(closure);
-}
-
 static DBusMessage*
 handle_query (void            *object,
               DBusMessage     *message,
               DBusError       *error)
 {
-    DBusConnection *connection;
     DDMDataModel *model;
     const char *notification_path;
     const char *method_uri;
@@ -661,12 +165,9 @@
     DDMDataFetch *fetch;
     GHashTable *params = NULL;
     DBusMessageIter iter;
-    DDMDataQuery *query;
     DataClientMap *client_map;
-    DataClient *client;
-    DataClientQueryClosure *closure;
+    HippoDBusModelClient *client;
     
-    connection = hippo_dbus_get_connection(hippo_app_get_dbus(hippo_get_app()));
     model = hippo_app_get_data_model(hippo_get_app());
 
     dbus_message_iter_init (message, &iter);
@@ -716,86 +217,33 @@
     client_map = data_client_map_get(model);
     client = data_client_map_get_client(client_map, dbus_message_get_sender(message), notification_path);
 
-    closure = data_client_query_closure_new(client, message, fetch);
-
-    /* We short-circuit m:getResource requests for resources with the local
-     * online-desktop scheme and handle them against the current contents of the cache.
-     */
-    if (strcmp(method_uri, "http://mugshot.org/p/system#getResource";) == 0) {
-        const char *resource_id = g_hash_table_lookup(params, "resourceId");
-        if (resource_id == NULL) {
-            data_client_query_closure_destroy(closure);
-            return dbus_message_new_error(message,
-                                          DBUS_ERROR_INVALID_ARGS,
-                                          _("resourceId parameter is mandatory for m:getResource query"));
-        }
-
-        if (g_str_has_prefix(resource_id, "online-desktop:")) {
-            DDMDataResource *resource = ddm_data_model_lookup_resource(model, resource_id);
-            GSList *results;
-
-            if (resource == NULL) {
-                data_client_query_closure_destroy(closure);
-                return dbus_message_new_error(message,
-                                              DBUS_ERROR_FAILED,
-                                              _("Couldn't find local resource"));
-            }
-
-            results = g_slist_prepend(NULL, resource);
-            on_query_success(results, closure);
-            g_slist_free(results);
-
-            return NULL;
-        }
-    }
-
-    query = ddm_data_model_query_params(model, method_uri, fetch_string, params);
-    g_hash_table_destroy(params);
-    
-    if (query == NULL) {
-        data_client_query_closure_destroy(closure);
+    if (!hippo_dbus_model_client_do_query(client, message, method_uri, fetch, params)) {
+        /* We've already validated most arguments, so don't worry too much about getting a
+         * good error message if something goes wrong at this point
+         */
         return dbus_message_new_error(message,
                                       DBUS_ERROR_FAILED,
                                       _("Couldn't send query"));
     }
 
-    ddm_data_query_set_multi_handler(query, on_query_success, closure);
-    ddm_data_query_set_error_handler(query, on_query_error, closure);
+    g_hash_table_destroy(params);
 
     ddm_data_fetch_unref(fetch);
 
     return NULL;
 }
 
-static void
-on_update_success(gpointer data)
-{
-    DBusConnection *connection = hippo_dbus_get_connection(hippo_app_get_dbus(hippo_get_app()));
-    DataClientQueryClosure *closure = data;
-    DBusMessage *reply;
-
-    reply = dbus_message_new_method_return(closure->message);
-    dbus_connection_send(connection, reply, NULL);
-    dbus_message_unref(reply);
-    
-    data_client_query_closure_destroy(closure);
-}
-
 static DBusMessage*
 handle_update (void            *object,
                DBusMessage     *message,
                DBusError       *error)
 {
-    DBusConnection *connection;
     DDMDataModel *model;
     const char *method_uri;
     GHashTable *params = NULL;
     DBusMessageIter iter;
-    DDMDataQuery *query;
-    DataClientQueryClosure *closure;
     
-    connection = hippo_dbus_get_connection(hippo_app_get_dbus(hippo_get_app()));
-    model = hippo_app_get_data_model(hippo_get_app());    
+    model = hippo_app_get_data_model(hippo_get_app());
 
     dbus_message_iter_init (message, &iter);
     
@@ -818,20 +266,17 @@
                                       DBUS_ERROR_INVALID_ARGS,
                                       _("Too many arguments"));
 
-    closure = data_client_query_closure_new(NULL, message, NULL);
-    query = ddm_data_model_update_params(model, method_uri, params);
-    g_hash_table_destroy(params);
-    
-    if (query == NULL) {
-        data_client_query_closure_destroy(closure);
+    if (!hippo_dbus_model_client_do_update(model, message, method_uri, params)) {
+        /* We've already validated most arguments, so don't worry too much about getting a
+         * good error message if something goes wrong at this point
+         */
         return dbus_message_new_error(message,
                                       DBUS_ERROR_FAILED,
-                                      _("Couldn't send query"));
+                                      _("Couldn't send update"));
     }
 
-    ddm_data_query_set_update_handler(query, on_update_success, closure);
-    ddm_data_query_set_error_handler(query, on_query_error, closure);
-
+    g_hash_table_destroy(params);
+    
     return NULL;
 }
 
@@ -840,13 +285,10 @@
                DBusMessage     *message,
                DBusError       *error)
 {
-    DBusConnection *connection;
     const char *notification_path;
     const char *resource_id;
     DBusMessageIter iter;
     
-    connection = hippo_dbus_get_connection(hippo_app_get_dbus(hippo_get_app()));
-
     dbus_message_iter_init (message, &iter);
 
     if (dbus_message_iter_get_arg_type(&iter) != DBUS_TYPE_OBJECT_PATH) {
@@ -1079,11 +521,11 @@
                   gpointer value,
                   gpointer data)
 {
-    DataClient *client = value;
+    HippoDBusModelClient *client = value;
     const char *name = data;
 
-    if (strcmp(client->id.bus_name, name) == 0) {
-        client->disconnected = TRUE;
+    if (strcmp(hippo_dbus_model_client_get_bus_name(client), name) == 0) {
+        hippo_dbus_model_client_disconnected(client);
         return TRUE;
     } else {
         return FALSE;



[Date Prev][Date Next]   [Thread Prev][Thread Next]   [Thread Index] [Date Index] [Author Index]