Update media-service-upnp to version 0.3.0 ( ca17a69 )
[profile/ivi/media-service-upnp.git] / src / device.c
index 0400238..871daf6 100644 (file)
@@ -24,6 +24,7 @@
 #include <libgupnp/gupnp-error.h>
 #include <libsoup/soup.h>
 
+#include "chain-task.h"
 #include "device.h"
 #include "error.h"
 #include "interface.h"
 
 #define MSU_SYSTEM_UPDATE_VAR "SystemUpdateID"
 #define MSU_CONTAINER_UPDATE_VAR "ContainerUpdateIDs"
+#define MEDIA_SERVER_DEVICE_TYPE "urn:schemas-upnp-org:device:MediaServer:"
+
+#define MSU_UPLOAD_STATUS_IN_PROGRESS "IN_PROGRESS"
+#define MSU_UPLOAD_STATUS_CANCELLED "CANCELLED"
+#define MSU_UPLOAD_STATUS_ERROR "ERROR"
+#define MSU_UPLOAD_STATUS_COMPLETED "COMPLETED"
 
 typedef gboolean (*msu_device_count_cb_t)(msu_async_cb_data_t *cb_data,
                                          gint count);
@@ -49,17 +56,32 @@ struct msu_device_object_builder_t_ {
        gboolean needs_child_count;
 };
 
+typedef struct msu_device_upload_job_t_ msu_device_upload_job_t;
+
 typedef struct msu_device_upload_t_ msu_device_upload_t;
 struct msu_device_upload_t_ {
        SoupSession *soup_session;
        SoupMessage *msg;
        GMappedFile *mapped_file;
+       const gchar *status;
+       guint64 bytes_uploaded;
+       guint64 bytes_to_upload;
 };
 
-typedef struct msu_device_upload_job_t_ msu_device_upload_job_t;
 struct msu_device_upload_job_t_ {
        gint upload_id;
        msu_device_t *device;
+       guint remove_idle;
+};
+
+/* Private structure used in chain task */
+typedef struct prv_new_device_ct_t_ prv_new_device_ct_t;
+struct prv_new_device_ct_t_ {
+       msu_device_t *dev;
+       GDBusConnection *connection;
+       const GDBusSubtreeVTable *vtable;
+       void *user_data;
+       GHashTable *property_map;
 };
 
 static void prv_get_child_count(msu_async_cb_data_t *cb_data,
@@ -74,6 +96,11 @@ static void prv_system_update_cb(GUPnPServiceProxy *proxy,
                                GValue *value,
                                gpointer user_data);
 static void prv_msu_device_upload_delete(gpointer up);
+static void prv_msu_upload_job_delete(gpointer up);
+static void prv_get_sr_token_for_props(GUPnPServiceProxy *proxy,
+                            msu_device_t *device,
+                            GCancellable *cancellable,
+                            msu_async_cb_data_t *cb_data);
 
 static void prv_msu_device_object_builder_delete(void *dob)
 {
@@ -118,6 +145,8 @@ static void prv_msu_context_delete(gpointer context)
                                                MSU_CONTAINER_UPDATE_VAR,
                                                prv_container_update_cb,
                                                ctx->device);
+                       gupnp_service_proxy_set_subscribed(ctx->service_proxy,
+                                                          FALSE);
                }
 
                if (ctx->device_proxy)
@@ -158,6 +187,8 @@ void msu_device_delete(void *device)
        msu_device_t *dev = device;
 
        if (dev) {
+               dev->shutting_down = TRUE;
+               g_hash_table_unref(dev->upload_jobs);
                g_hash_table_unref(dev->uploads);
 
                if (dev->timeout_id)
@@ -169,6 +200,10 @@ void msu_device_delete(void *device)
 
                g_ptr_array_unref(dev->contexts);
                g_free(dev->path);
+               g_variant_unref(dev->search_caps);
+               g_variant_unref(dev->sort_caps);
+               g_variant_unref(dev->sort_ext_caps);
+               g_variant_unref(dev->feature_list);
                g_free(dev);
        }
 }
@@ -229,17 +264,31 @@ static void prv_system_update_cb(GUPnPServiceProxy *proxy,
                                 GValue *value,
                                 gpointer user_data)
 {
+       GVariantBuilder *array;
+       GVariant *val;
        msu_device_t *device = user_data;
+       guint suid = g_value_get_uint(value);
+
+       MSU_LOG_DEBUG("System Update %u", suid);
+
+       device->system_update_id = suid;
 
-       MSU_LOG_DEBUG("System Update %u", g_value_get_uint(value));
+       array = g_variant_builder_new(G_VARIANT_TYPE("a{sv}"));
+       g_variant_builder_add(array, "{sv}", MSU_INTERFACE_SYSTEM_UPDATE_ID,
+                             g_variant_new_uint32(suid));
+       val = g_variant_new("(s@a{sv}as)", MSU_INTERFACE_MEDIA_DEVICE,
+                           g_variant_builder_end(array),
+                           NULL);
 
        (void) g_dbus_connection_emit_signal(device->connection,
-                       NULL,
-                       device->path,
-                       MSU_INTERFACE_MEDIA_DEVICE,
-                       MSU_INTERFACE_SYSTEM_UPDATE,
-                       g_variant_new("(u)", g_value_get_uint(value)),
-                       NULL);
+                                            NULL,
+                                            device->path,
+                                            MSU_INTERFACE_PROPERTIES,
+                                            MSU_INTERFACE_PROPERTIES_CHANGED,
+                                            val,
+                                            NULL);
+
+       g_variant_builder_unref(array);
 }
 
 static gboolean prv_re_enable_subscription(gpointer user_data)
@@ -283,7 +332,7 @@ void msu_device_subscribe_to_contents_change(msu_device_t *device)
 {
        msu_device_context_t *context;
 
-       context = msu_device_get_context(device);
+       context = msu_device_get_context(device, NULL);
 
        MSU_LOG_DEBUG("Subscribe for events on context: %s",
                      context->ip_address);
@@ -293,6 +342,7 @@ void msu_device_subscribe_to_contents_change(msu_device_t *device)
                                G_TYPE_UINT,
                                prv_system_update_cb,
                                device);
+
        gupnp_service_proxy_add_notify(context->service_proxy,
                                MSU_CONTAINER_UPDATE_VAR,
                                G_TYPE_STRING,
@@ -308,60 +358,437 @@ void msu_device_subscribe_to_contents_change(msu_device_t *device)
                                context);
 }
 
-gboolean msu_device_new(GDBusConnection *connection,
-                       GUPnPDeviceProxy *proxy,
-                       const gchar *ip_address,
-                       const GDBusSubtreeVTable *vtable,
-                       void *user_data,
-                       guint counter,
-                       msu_device_t **device)
+static void prv_feature_list_add_feature(gchar* root_path,
+                                        GUPnPFeature *feature,
+                                        GVariantBuilder *vb)
 {
-       msu_device_t *dev = g_new0(msu_device_t, 1);
-       guint flags;
-       guint id;
-       GString *new_path = NULL;
+       GVariantBuilder vbo;
+       GVariant *var_obj;
+       const char *name;
+       const char *version;
+       const char *obj_id;
+       gchar **obj;
+       gchar **saved;
+       gchar *path;
 
-       MSU_LOG_DEBUG("Enter");
+       name = gupnp_feature_get_name(feature);
+       version = gupnp_feature_get_version(feature);
+       obj_id = gupnp_feature_get_object_ids(feature);
 
-       dev->connection = connection;
-       dev->contexts = g_ptr_array_new_with_free_func(prv_msu_context_delete);
-       msu_device_append_new_context(dev, ip_address, proxy);
+       g_variant_builder_init(&vbo, G_VARIANT_TYPE("ao"));
 
-       msu_device_subscribe_to_contents_change(dev);
+       if (obj_id != NULL && *obj_id) {
+               obj = g_strsplit(obj_id, ",", 0);
+               saved = obj;
 
-       new_path = g_string_new("");
-       g_string_printf(new_path, "%s/%u", MSU_SERVER_PATH, counter);
+               while (obj && *obj) {
+                       path = msu_path_from_id(root_path, *obj);
+                       g_variant_builder_add(&vbo, "o", path);
+                       g_free(path);
+                       obj++;
+               }
 
-       MSU_LOG_DEBUG("Server Path %s", new_path->str);
+               g_strfreev(saved);
+       }
 
-       flags = G_DBUS_SUBTREE_FLAGS_DISPATCH_TO_UNENUMERATED_NODES;
-       id =  g_dbus_connection_register_subtree(connection, new_path->str,
-                                                vtable, flags, user_data, NULL,
-                                                NULL);
-       if (!id)
+       var_obj = g_variant_builder_end(&vbo);
+       g_variant_builder_add(vb, "(ss@ao)", name, version, var_obj);
+}
+
+static void prv_get_feature_list_analyze(msu_device_t *device, gchar *result)
+{
+       GUPnPFeatureListParser *parser;
+       GUPnPFeature *feature;
+       GList *list;
+       GList *item;
+       GError *error = NULL;
+       GVariantBuilder vb;
+#if MSU_LOG_LEVEL & MSU_LOG_LEVEL_DEBUG
+       char *str;
+#endif
+       parser = gupnp_feature_list_parser_new();
+       list = gupnp_feature_list_parser_parse_text(parser, result, &error);
+
+       if (error != NULL) {
+               MSU_LOG_WARNING("GetFeatureList parsing failed: %s",
+                               error->message);
+               goto on_exit;
+       }
+
+       g_variant_builder_init(&vb, G_VARIANT_TYPE("a(ssao)"));
+       item = list;
+
+       while (item != NULL) {
+               feature = (GUPnPFeature *) item->data;
+               prv_feature_list_add_feature(device->path, feature, &vb);
+               g_object_unref(feature);
+               item = g_list_next(item);
+       }
+
+       device->feature_list = g_variant_ref_sink(g_variant_builder_end(&vb));
+
+#if MSU_LOG_LEVEL & MSU_LOG_LEVEL_DEBUG
+       str = g_variant_print(device->feature_list, FALSE);
+       MSU_LOG_DEBUG("%s = %s", MSU_INTERFACE_PROP_SV_FEATURE_LIST, str);
+       g_free(str);
+#endif
+
+on_exit:
+       g_list_free(list);
+       g_object_unref(parser);
+
+       if (error != NULL)
+               g_error_free(error);
+}
+
+static void prv_get_feature_list_cb(GUPnPServiceProxy *proxy,
+                                   GUPnPServiceProxyAction *action,
+                                   gpointer user_data)
+{
+       gchar *result = NULL;
+       GError *error = NULL;
+       prv_new_device_ct_t *priv_t = (prv_new_device_ct_t *) user_data;
+
+       if (!gupnp_service_proxy_end_action(proxy, action, &error,
+                                           "FeatureList", G_TYPE_STRING,
+                                           &result, NULL)) {
+               MSU_LOG_WARNING("GetFeatureList operation failed: %s",
+                               error->message);
                goto on_error;
+       }
 
-       dev->path = g_string_free(new_path, FALSE);
-       dev->id = id;
+       MSU_LOG_DEBUG("GetFeatureList result: %s", result);
 
-       dev->uploads = g_hash_table_new_full(g_int_hash, g_int_equal, g_free,
-                                            prv_msu_device_upload_delete);
+       prv_get_feature_list_analyze(priv_t->dev, result);
 
-       *device = dev;
+on_error:
+       if (error != NULL)
+               g_error_free(error);
 
-       MSU_LOG_DEBUG("Exit with SUCCESS");
+       g_free(result);
+}
 
-       return TRUE;
+static GUPnPServiceProxyAction *prv_get_feature_list(msu_chain_task_t *chain,
+                                                    gboolean *failed)
+{
+       msu_device_t *device;
+       msu_device_context_t *context;
+
+       device = msu_chain_task_get_device(chain);
+       context = msu_device_get_context(device, NULL);
+       *failed = FALSE;
+
+       return gupnp_service_proxy_begin_action(context->service_proxy,
+                                               "GetFeatureList",
+                                               msu_chain_task_begin_action_cb,
+                                               chain, NULL);
+}
+
+static void prv_get_sort_ext_capabilities_analyze(msu_device_t *device,
+                                                 gchar *result)
+{
+       gchar **caps;
+       gchar **saved;
+#if MSU_LOG_LEVEL & MSU_LOG_LEVEL_DEBUG
+       gchar *props;
+#endif
+       GVariantBuilder sort_ext_caps_vb;
+
+       g_variant_builder_init(&sort_ext_caps_vb, G_VARIANT_TYPE("as"));
+
+       caps = g_strsplit(result, ",", 0);
+       saved = caps;
+
+       while (caps && *caps) {
+               g_variant_builder_add(&sort_ext_caps_vb, "s", *caps);
+               caps++;
+       }
+
+       g_strfreev(saved);
+
+       device->sort_ext_caps = g_variant_ref_sink(g_variant_builder_end(
+                                                       &sort_ext_caps_vb));
+
+#if MSU_LOG_LEVEL & MSU_LOG_LEVEL_DEBUG
+       props = g_variant_print(device->sort_ext_caps, FALSE);
+       MSU_LOG_DEBUG("%s = %s", MSU_INTERFACE_PROP_SV_SORT_EXT_CAPABILITIES,
+                                props);
+       g_free(props);
+#endif
+}
+
+static void prv_get_sort_ext_capabilities_cb(GUPnPServiceProxy *proxy,
+                                          GUPnPServiceProxyAction *action,
+                                          gpointer user_data)
+{
+       gchar *result = NULL;
+       GError *error = NULL;
+       prv_new_device_ct_t *priv_t = (prv_new_device_ct_t *) user_data;
+
+       if (!gupnp_service_proxy_end_action(proxy, action, &error,
+                                           "SortExtensionCaps",
+                                           G_TYPE_STRING, &result, NULL)) {
+               MSU_LOG_WARNING(
+                       "GetSortExtensionCapabilities operation failed: %s",
+                       error->message);
+               goto on_error;
+       }
+
+       MSU_LOG_DEBUG("GetSortExtensionCapabilities result: %s", result);
+
+       prv_get_sort_ext_capabilities_analyze(priv_t->dev, result);
 
 on_error:
-       if (new_path)
-               g_string_free(new_path, TRUE);
 
-       msu_device_delete(dev);
+       if (error)
+               g_error_free(error);
 
-       MSU_LOG_DEBUG("Exit with FAIL");
+       g_free(result);
+}
 
-       return FALSE;
+static GUPnPServiceProxyAction *prv_get_sort_ext_capabilities(
+                                                       msu_chain_task_t *chain,
+                                                       gboolean *failed)
+{
+       msu_device_t *device;
+       msu_device_context_t *context;
+
+       device = msu_chain_task_get_device(chain);
+       context = msu_device_get_context(device, NULL);
+       *failed = FALSE;
+
+       return gupnp_service_proxy_begin_action(context->service_proxy,
+                                               "GetSortExtensionCapabilities",
+                                               msu_chain_task_begin_action_cb,
+                                               chain, NULL);
+}
+
+static void prv_get_capabilities_analyze(GHashTable *property_map,
+                                        gchar *result,
+                                        GVariant **variant)
+{
+       gchar **caps;
+       gchar **saved;
+       gchar *prop_name;
+       GVariantBuilder caps_vb;
+
+       g_variant_builder_init(&caps_vb, G_VARIANT_TYPE("as"));
+
+       caps = g_strsplit(result, ",", 0);
+       saved = caps;
+
+       while (caps && *caps) {
+               prop_name = g_hash_table_lookup(property_map, *caps);
+
+               if (prop_name)
+                       g_variant_builder_add(&caps_vb, "s", prop_name);
+
+               caps++;
+       }
+
+       g_strfreev(saved);
+
+       *variant = g_variant_ref_sink(g_variant_builder_end(&caps_vb));
+
+#if MSU_LOG_LEVEL & MSU_LOG_LEVEL_DEBUG
+       prop_name = g_variant_print(*variant, FALSE);
+       MSU_LOG_DEBUG("%s = %s", "   Variant", prop_name);
+       g_free(prop_name);
+#endif
+}
+
+static void prv_get_sort_capabilities_cb(GUPnPServiceProxy *proxy,
+                                          GUPnPServiceProxyAction *action,
+                                          gpointer user_data)
+{
+       gchar *result = NULL;
+       GError *error = NULL;
+       prv_new_device_ct_t *priv_t = (prv_new_device_ct_t *) user_data;
+
+       if (!gupnp_service_proxy_end_action(proxy, action, &error, "SortCaps",
+                                           G_TYPE_STRING, &result, NULL)) {
+               MSU_LOG_WARNING("GetSortCapabilities operation failed: %s",
+                               error->message);
+               goto on_error;
+       }
+
+       MSU_LOG_DEBUG("GetSortCapabilities result: %s", result);
+
+       prv_get_capabilities_analyze(priv_t->property_map, result,
+                                    &priv_t->dev->sort_caps);
+
+on_error:
+
+       if (error)
+               g_error_free(error);
+
+       g_free(result);
+}
+
+static GUPnPServiceProxyAction *prv_get_sort_capabilities(
+                                       msu_chain_task_t *chain,
+                                       gboolean *failed)
+{
+       msu_device_t *device;
+       msu_device_context_t *context;
+
+       device = msu_chain_task_get_device(chain);
+       context = msu_device_get_context(device, NULL);
+       *failed = FALSE;
+
+       return gupnp_service_proxy_begin_action(context->service_proxy,
+                                               "GetSortCapabilities",
+                                               msu_chain_task_begin_action_cb,
+                                               chain, NULL);
+}
+
+static void prv_get_search_capabilities_cb(GUPnPServiceProxy *proxy,
+                                          GUPnPServiceProxyAction *action,
+                                          gpointer user_data)
+{
+       gchar *result = NULL;
+       GError *error = NULL;
+       prv_new_device_ct_t *priv_t = (prv_new_device_ct_t *) user_data;
+
+       if (!gupnp_service_proxy_end_action(proxy, action, &error, "SearchCaps",
+                                           G_TYPE_STRING, &result, NULL)) {
+               MSU_LOG_WARNING("GetSearchCapabilities operation failed: %s",
+                               error->message);
+               goto on_error;
+       }
+
+       MSU_LOG_DEBUG("GetSearchCapabilities result: %s", result);
+
+       prv_get_capabilities_analyze(priv_t->property_map, result,
+                                    &priv_t->dev->search_caps);
+
+on_error:
+
+       if (error)
+               g_error_free(error);
+
+       g_free(result);
+}
+
+static GUPnPServiceProxyAction *prv_get_search_capabilities(
+                                       msu_chain_task_t *chain,
+                                       gboolean *failed)
+{
+       msu_device_t *device;
+       msu_device_context_t *context;
+
+       device = msu_chain_task_get_device(chain);
+       context = msu_device_get_context(device, NULL);
+       *failed = FALSE;
+
+       return gupnp_service_proxy_begin_action(context->service_proxy,
+                                               "GetSearchCapabilities",
+                                               msu_chain_task_begin_action_cb,
+                                               chain, NULL);
+}
+
+static GUPnPServiceProxyAction *prv_subscribe(msu_chain_task_t *chain,
+                                             gboolean *failed)
+{
+       msu_device_t *device;
+
+       device = msu_chain_task_get_device(chain);
+       msu_device_subscribe_to_contents_change(device);
+
+       *failed = FALSE;
+
+       return NULL;
+}
+
+static GUPnPServiceProxyAction *prv_declare(msu_chain_task_t *chain,
+                                           gboolean *failed)
+{
+       guint flags;
+       guint id;
+       msu_device_t *device;
+       prv_new_device_ct_t *priv_t;
+
+       device = msu_chain_task_get_device(chain);
+
+       priv_t = (prv_new_device_ct_t *) msu_chain_task_get_user_data(chain);
+
+       flags = G_DBUS_SUBTREE_FLAGS_DISPATCH_TO_UNENUMERATED_NODES;
+       id =  g_dbus_connection_register_subtree(priv_t->connection,
+                                                device->path,
+                                                priv_t->vtable,
+                                                flags,
+                                                priv_t->user_data,
+                                                NULL, NULL);
+       if (id) {
+               device->id = id;
+
+               device->uploads = g_hash_table_new_full(g_int_hash, g_int_equal,
+                                               g_free,
+                                               prv_msu_device_upload_delete);
+               device->upload_jobs =
+                       g_hash_table_new_full(g_int_hash, g_int_equal,
+                                             g_free,
+                                             prv_msu_upload_job_delete);
+
+       } else
+               MSU_LOG_ERROR("g_dbus_connection_register_subtree FAILED");
+
+       *failed = (!id);
+
+       return NULL;
+}
+
+msu_device_t *msu_device_new(GDBusConnection *connection,
+                            GUPnPDeviceProxy *proxy,
+                            const gchar *ip_address,
+                            const GDBusSubtreeVTable *vtable,
+                            void *user_data,
+                            GHashTable *property_map,
+                            guint counter,
+                            msu_chain_task_t *chain)
+{
+       msu_device_t *dev;
+       prv_new_device_ct_t *priv_t;
+       gchar *new_path;
+
+       MSU_LOG_DEBUG("New Device on %s", ip_address);
+
+       new_path = g_strdup_printf("%s/%u", MSU_SERVER_PATH, counter);
+       MSU_LOG_DEBUG("Server Path %s", new_path);
+
+       dev = g_new0(msu_device_t, 1);
+       priv_t = g_new0(prv_new_device_ct_t, 1);
+
+       dev->connection = connection;
+       dev->contexts = g_ptr_array_new_with_free_func(prv_msu_context_delete);
+       dev->path = new_path;
+
+       priv_t->dev = dev;
+       priv_t->connection = connection;
+       priv_t->vtable = vtable;
+       priv_t->user_data = user_data;
+       priv_t->property_map = property_map;
+
+       msu_device_append_new_context(dev, ip_address, proxy);
+
+       msu_chain_task_add(chain, prv_get_search_capabilities, dev,
+                          prv_get_search_capabilities_cb, NULL, priv_t);
+
+       msu_chain_task_add(chain, prv_get_sort_capabilities, dev,
+                          prv_get_sort_capabilities_cb, NULL, priv_t);
+
+       msu_chain_task_add(chain, prv_get_sort_ext_capabilities, dev,
+                          prv_get_sort_ext_capabilities_cb, NULL, priv_t);
+
+       msu_chain_task_add(chain, prv_get_feature_list, dev,
+                          prv_get_feature_list_cb, NULL, priv_t);
+MSU_LOG_DEBUG("AFTER");
+       msu_chain_task_add(chain, prv_subscribe, dev, NULL, NULL, NULL);
+       msu_chain_task_add(chain, prv_declare, dev, NULL, g_free, priv_t);
+
+       msu_chain_task_start(chain);
+
+       return dev;
 }
 
 void msu_device_append_new_context(msu_device_t *device,
@@ -395,19 +822,26 @@ msu_device_t *msu_device_from_path(const gchar *path, GHashTable *device_list)
        return retval;
 }
 
-msu_device_context_t *msu_device_get_context(msu_device_t *device)
+msu_device_context_t *msu_device_get_context(msu_device_t *device,
+                                            msu_client_t *client)
 {
        msu_device_context_t *context;
        unsigned int i;
        const char ip4_local_prefix[] = "127.0.0.";
+       gboolean prefer_local;
+       gboolean is_local;
+
+       prefer_local = (client && client->prefer_local_addresses);
 
        for (i = 0; i < device->contexts->len; ++i) {
                context = g_ptr_array_index(device->contexts, i);
 
-               if (!strncmp(context->ip_address, ip4_local_prefix,
-                            sizeof(ip4_local_prefix) - 1) ||
-                   !strcmp(context->ip_address, "::1") ||
-                   !strcmp(context->ip_address, "0:0:0:0:0:0:0:1"))
+               is_local = (!strncmp(context->ip_address, ip4_local_prefix,
+                                               sizeof(ip4_local_prefix) - 1) ||
+                           !strcmp(context->ip_address, "::1") ||
+                           !strcmp(context->ip_address, "0:0:0:0:0:0:0:1"));
+
+               if (prefer_local == is_local)
                        break;
        }
 
@@ -627,8 +1061,8 @@ no_complete:
        MSU_LOG_DEBUG("Exit");
 }
 
-void msu_device_get_children(msu_device_t *device,  msu_task_t *task,
-                            msu_async_cb_data_t *cb_data,
+void msu_device_get_children(msu_device_t *device, msu_client_t *client,
+                            msu_task_t *task, msu_async_cb_data_t *cb_data,
                             const gchar *upnp_filter, const gchar *sort_by,
                             GCancellable *cancellable)
 {
@@ -636,7 +1070,7 @@ void msu_device_get_children(msu_device_t *device,  msu_task_t *task,
 
        MSU_LOG_DEBUG("Enter");
 
-       context = msu_device_get_context(device);
+       context = msu_device_get_context(device, client);
 
        cb_data->action =
                gupnp_service_proxy_begin_action(context->service_proxy,
@@ -769,349 +1203,337 @@ static void prv_get_all(GUPnPDIDLLiteParser *parser,
        }
 }
 
-static gboolean prv_get_all_child_count_cb(msu_async_cb_data_t *cb_data,
-                                      gint count)
+static gboolean prv_device_subscribed(msu_device_t *device)
 {
-       msu_async_get_all_t *cb_task_data = &cb_data->ut.get_all;
+       msu_device_context_t *context;
+       unsigned int i;
+       gboolean subscribed = FALSE;
 
-       msu_props_add_child_count(cb_task_data->vb, count);
-       cb_data->result = g_variant_ref_sink(g_variant_builder_end(
-                                                    cb_task_data->vb));
-       return TRUE;
+       for (i = 0; i < device->contexts->len; ++i) {
+               context = g_ptr_array_index(device->contexts, i);
+               if (context->subscribed) {
+                       subscribed = TRUE;
+                       break;
+               }
+       }
+
+       return subscribed;
 }
 
-static void prv_get_all_ms2spec_props_cb(GUPnPServiceProxy *proxy,
-                                        GUPnPServiceProxyAction *action,
-                                        gpointer user_data)
+static void prv_system_update_id_for_prop_cb(GUPnPServiceProxy *proxy,
+                                   GUPnPServiceProxyAction *action,
+                                   gpointer user_data)
 {
        GError *upnp_error = NULL;
-       gchar *result = NULL;
-       GUPnPDIDLLiteParser *parser = NULL;
+       guint id;
        msu_async_cb_data_t *cb_data = user_data;
-       msu_async_get_all_t *cb_task_data = &cb_data->ut.get_all;
 
        MSU_LOG_DEBUG("Enter");
 
-       if (!gupnp_service_proxy_end_action(cb_data->proxy, cb_data->action,
-                                           &upnp_error,
-                                           "Result", G_TYPE_STRING,
-                                           &result, NULL)) {
-               MSU_LOG_WARNING("Browse operation failed: %s",
-                             upnp_error->message);
+       if (!gupnp_service_proxy_end_action(proxy, action, &upnp_error,
+                                           "Id", G_TYPE_UINT,
+                                           &id,
+                                           NULL)) {
+               MSU_LOG_ERROR("Unable to retrieve ServiceUpdateID: %s %s",
+                              g_quark_to_string(upnp_error->domain),
+                              upnp_error->message);
 
                cb_data->error = g_error_new(MSU_ERROR,
-                                            MSU_ERROR_OPERATION_FAILED,
-                                            "Browse operation failed: %s",
-                                            upnp_error->message);
-               goto on_error;
+                               MSU_ERROR_OPERATION_FAILED,
+                               "Unable to retrieve ServiceUpdateID: %s",
+                               upnp_error->message);
+
+               goto on_complete;
        }
 
-       MSU_LOG_DEBUG("GetMS2SpecProps result: %s", result);
+       cb_data->result = g_variant_ref_sink(g_variant_new_uint32(id));
 
-       parser = gupnp_didl_lite_parser_new();
+on_complete:
 
-       g_signal_connect(parser, "object-available" , cb_task_data->prop_func,
-                        cb_data);
+       (void) g_idle_add(msu_async_complete_task, cb_data);
+       g_cancellable_disconnect(cb_data->cancellable, cb_data->cancel_id);
 
-       if (!gupnp_didl_lite_parser_parse_didl(parser, result, &upnp_error)) {
-               if (upnp_error->code == GUPNP_XML_ERROR_EMPTY_NODE) {
-                       MSU_LOG_WARNING("Property not defined for object");
+       if (upnp_error)
+               g_error_free(upnp_error);
 
-                       cb_data->error =
-                               g_error_new(MSU_ERROR,
-                                           MSU_ERROR_UNKNOWN_PROPERTY,
-                                           "Property not defined for object");
-               } else {
-                       MSU_LOG_WARNING("Unable to parse results of browse: %s",
-                                     upnp_error->message);
+       MSU_LOG_DEBUG("Exit");
+}
 
-                       cb_data->error =
-                               g_error_new(MSU_ERROR,
-                                           MSU_ERROR_OPERATION_FAILED,
-                                           "Unable to parse results of "
-                                           "browse: %s",
-                                           upnp_error->message);
-               }
-               goto on_error;
-       }
+static void prv_get_system_update_id_for_prop(GUPnPServiceProxy *proxy,
+                                    msu_device_t *device,
+                                    GCancellable *cancellable,
+                                    msu_async_cb_data_t *cb_data)
+{
+       guint suid;
 
-       if (cb_data->error)
-               goto on_error;
+       MSU_LOG_DEBUG("Enter");
 
-       if (cb_task_data->need_child_count) {
-               MSU_LOG_DEBUG("Need Child Count");
+       if (prv_device_subscribed(device)) {
+               suid = device->system_update_id;
 
-               prv_get_child_count(cb_data, prv_get_all_child_count_cb,
-                       cb_data->id);
+               cb_data->result = g_variant_ref_sink(
+                                       g_variant_new_uint32(suid));
 
-               goto no_complete;
-       } else {
-               cb_data->result = g_variant_ref_sink(g_variant_builder_end(
-                                                            cb_task_data->vb));
-       }
-
-on_error:
+               (void) g_idle_add(msu_async_complete_task, cb_data);
 
-       (void) g_idle_add(msu_async_complete_task, cb_data);
-       g_cancellable_disconnect(cb_data->cancellable, cb_data->cancel_id);
+               goto on_complete;
+       }
 
-no_complete:
+       gupnp_service_proxy_begin_action(proxy, "GetSystemUpdateID",
+                                        prv_system_update_id_for_prop_cb,
+                                        cb_data,
+                                        NULL);
 
-       if (upnp_error)
-               g_error_free(upnp_error);
+       cb_data->proxy = proxy;
 
-       if (parser)
-               g_object_unref(parser);
+       cb_data->cancel_id =
+       g_cancellable_connect(cancellable,
+                                     G_CALLBACK(msu_async_task_cancelled),
+                                     cb_data, NULL);
+       cb_data->cancellable = cancellable;
 
-       g_free(result);
+on_complete:
 
        MSU_LOG_DEBUG("Exit");
 }
 
-static void prv_get_all_ms2spec_props(msu_device_context_t *context,
-                                     GCancellable *cancellable,
-                                     msu_async_cb_data_t *cb_data)
+static void prv_system_update_id_for_props_cb(GUPnPServiceProxy *proxy,
+                                   GUPnPServiceProxyAction *action,
+                                   gpointer user_data)
 {
+       GError *upnp_error = NULL;
+       guint id;
+       msu_async_cb_data_t *cb_data = user_data;
        msu_async_get_all_t *cb_task_data = &cb_data->ut.get_all;
-       msu_task_t *task = cb_data->task;
-       msu_task_get_props_t *task_data = &task->ut.get_props;
-
-       MSU_LOG_DEBUG("Enter called");
-
-       if (!strcmp(MSU_INTERFACE_MEDIA_CONTAINER, task_data->interface_name))
-               cb_task_data->prop_func = G_CALLBACK(prv_get_container);
-       else if (!strcmp(MSU_INTERFACE_MEDIA_ITEM, task_data->interface_name))
-               cb_task_data->prop_func = G_CALLBACK(prv_get_item);
-       else if (!strcmp(MSU_INTERFACE_MEDIA_OBJECT, task_data->interface_name))
-               cb_task_data->prop_func = G_CALLBACK(prv_get_object);
-       else  if (!strcmp("", task_data->interface_name))
-               cb_task_data->prop_func = G_CALLBACK(prv_get_all);
-       else {
-               MSU_LOG_WARNING("Interface is unknown.");
 
-               cb_data->error =
-                       g_error_new(MSU_ERROR, MSU_ERROR_UNKNOWN_INTERFACE,
-                                   "Interface is unknown.");
-               goto on_error;
-       }
+       MSU_LOG_DEBUG("Enter");
 
-       cb_data->action = gupnp_service_proxy_begin_action(
-               context->service_proxy, "Browse",
-               prv_get_all_ms2spec_props_cb, cb_data,
-               "ObjectID", G_TYPE_STRING, cb_data->id,
-               "BrowseFlag", G_TYPE_STRING, "BrowseMetadata",
-               "Filter", G_TYPE_STRING, "*",
-               "StartingIndex", G_TYPE_INT, 0,
-               "RequestedCount", G_TYPE_INT, 0,
-               "SortCriteria", G_TYPE_STRING,
-               "", NULL);
+       if (!gupnp_service_proxy_end_action(proxy, action, &upnp_error,
+                                           "Id", G_TYPE_UINT,
+                                           &id,
+                                           NULL)) {
+               MSU_LOG_ERROR("Unable to retrieve ServiceUpdateID: %s %s",
+                              g_quark_to_string(upnp_error->domain),
+                              upnp_error->message);
 
-       cb_data->proxy = context->service_proxy;
+               cb_data->error = g_error_new(MSU_ERROR,
+                               MSU_ERROR_OPERATION_FAILED,
+                               "Unable to retrieve ServiceUpdateID: %s",
+                               upnp_error->message);
 
-       cb_data->cancel_id =
-               g_cancellable_connect(cancellable,
-                                     G_CALLBACK(msu_async_task_cancelled),
-                                     cb_data, NULL);
-       cb_data->cancellable = cancellable;
+               goto on_complete;
+       }
 
-       MSU_LOG_DEBUG("Exit with SUCCESS");
+       g_variant_builder_add(cb_task_data->vb, "{sv}",
+                             MSU_SYSTEM_UPDATE_VAR,
+                             g_variant_new_uint32(id));
 
-       return;
+       cb_data->result = g_variant_ref_sink(g_variant_builder_end(
+                                               cb_task_data->vb));
 
-on_error:
+on_complete:
 
-       (void) g_idle_add(msu_async_complete_task, cb_data);
+       if (!cb_data->error)
+               prv_get_sr_token_for_props(proxy, cb_task_data->device,
+                                          cb_data->cancellable, cb_data);
+       else {
+               (void) g_idle_add(msu_async_complete_task, cb_data);
+               g_cancellable_disconnect(cb_data->cancellable,
+                                        cb_data->cancel_id);
+       }
 
-       MSU_LOG_DEBUG("Exit with FAIL");
+       if (upnp_error)
+               g_error_free(upnp_error);
 
-       return;
+       MSU_LOG_DEBUG("Exit");
 }
 
-void msu_device_get_all_props(msu_device_t *device,  msu_task_t *task,
-                             msu_async_cb_data_t *cb_data,
-                             gboolean root_object,
-                             GCancellable *cancellable)
+static void prv_get_system_update_id_for_props(GUPnPServiceProxy *proxy,
+                                    msu_device_t *device,
+                                    GCancellable *cancellable,
+                                    msu_async_cb_data_t *cb_data)
 {
        msu_async_get_all_t *cb_task_data;
-       msu_task_get_props_t *task_data = &task->ut.get_props;
-       msu_device_context_t *context;
+       guint suid;
 
        MSU_LOG_DEBUG("Enter");
 
-       context = msu_device_get_context(device);
-       cb_task_data = &cb_data->ut.get_all;
+       if (prv_device_subscribed(device)) {
+               suid = device->system_update_id;
 
-       cb_task_data->vb = g_variant_builder_new(G_VARIANT_TYPE("a{sv}"));
+               cb_task_data = &cb_data->ut.get_all;
 
-       if (!strcmp(task_data->interface_name, MSU_INTERFACE_MEDIA_DEVICE)) {
-               if (root_object) {
-                       msu_props_add_device(
-                               (GUPnPDeviceInfo *) context->device_proxy,
-                               cb_task_data->vb);
+               g_variant_builder_add(cb_task_data->vb, "{sv}",
+                                     MSU_SYSTEM_UPDATE_VAR,
+                                     g_variant_new_uint32(suid));
 
-                       cb_data->result =
-                               g_variant_ref_sink(g_variant_builder_end(
-                                                          cb_task_data->vb));
-               } else {
-                       cb_data->error =
-                               g_error_new(MSU_ERROR,
-                                           MSU_ERROR_UNKNOWN_INTERFACE,
-                                           "Interface is only valid on "
-                                           "root objects.");
-               }
+               prv_get_sr_token_for_props(proxy, device, cancellable, cb_data);
 
-               (void) g_idle_add(msu_async_complete_task, cb_data);
+               goto on_complete;
+       }
 
-       } else if (strcmp(task_data->interface_name, "")) {
-               prv_get_all_ms2spec_props(context, cancellable, cb_data);
-       } else {
-               if (root_object)
-                       msu_props_add_device(
-                               (GUPnPDeviceInfo *) context->device_proxy,
-                               cb_task_data->vb);
+       gupnp_service_proxy_begin_action(proxy, "GetSystemUpdateID",
+                                        prv_system_update_id_for_props_cb,
+                                        cb_data,
+                                        NULL);
 
-               prv_get_all_ms2spec_props(context, cancellable, cb_data);
-       }
+       cb_data->proxy = proxy;
+
+       cb_data->cancel_id =
+       g_cancellable_connect(cancellable,
+                                     G_CALLBACK(msu_async_task_cancelled),
+                                     cb_data, NULL);
+       cb_data->cancellable = cancellable;
+
+on_complete:
 
        MSU_LOG_DEBUG("Exit");
 }
 
-static void prv_get_object_property(GUPnPDIDLLiteParser *parser,
-                                   GUPnPDIDLLiteObject *object,
-                                   gpointer user_data)
+static int prv_get_media_server_version(msu_device_t *device)
 {
-       msu_async_cb_data_t *cb_data = user_data;
-       msu_task_t *task = cb_data->task;
-       msu_task_get_prop_t *task_data = &task->ut.get_prop;
-       msu_async_get_prop_t *cb_task_data = &cb_data->ut.get_prop;
+       msu_device_context_t *context;
+       const char *device_type;
+       const char *version;
 
-       if (cb_data->result)
+       context = msu_device_get_context(device, NULL);
+       device_type = gupnp_device_info_get_device_type((GUPnPDeviceInfo *)
+                                                       context->device_proxy);
+
+       if (strncmp(device_type, MEDIA_SERVER_DEVICE_TYPE,
+                                       sizeof(MEDIA_SERVER_DEVICE_TYPE) - 1))
                goto on_error;
 
-       cb_data->result = msu_props_get_object_prop(task_data->prop_name,
-                                                   cb_task_data->root_path,
-                                                   object);
+       version = device_type + sizeof(MEDIA_SERVER_DEVICE_TYPE) - 1;
+
+       return atoi(version);
 
 on_error:
 
-       return;
+       return -1;
 }
 
-static void prv_get_item_property(GUPnPDIDLLiteParser *parser,
-                                 GUPnPDIDLLiteObject *object,
-                                 gpointer user_data)
+static void prv_service_reset_for_prop_cb(GUPnPServiceProxy *proxy,
+                                         GUPnPServiceProxyAction *action,
+                                         gpointer user_data)
 {
+       GError *upnp_error = NULL;
+       gchar *token = NULL;
        msu_async_cb_data_t *cb_data = user_data;
-       msu_task_t *task = cb_data->task;
-       msu_task_get_prop_t *task_data = &task->ut.get_prop;
-       msu_async_get_prop_t *cb_task_data = &cb_data->ut.get_prop;
 
-       if (cb_data->result)
-               goto on_error;
+       MSU_LOG_DEBUG("Enter");
 
-       cb_data->result = msu_props_get_item_prop(task_data->prop_name,
-                                                 cb_task_data->root_path,
-                                                 object,
-                                                 cb_task_data->protocol_info);
+       if (!gupnp_service_proxy_end_action(proxy, action, &upnp_error,
+                                           "ResetToken", G_TYPE_STRING,
+                                           &token,
+                                           NULL)) {
+               MSU_LOG_ERROR("Unable to retrieve ServiceResetToken: %s %s",
+                               g_quark_to_string(upnp_error->domain),
+                                upnp_error->message);
 
-on_error:
 
-       return;
-}
+               cb_data->error = g_error_new(MSU_ERROR,
+                                            MSU_ERROR_OPERATION_FAILED,
+                                            "GetServiceResetToken failed: %s",
+                                            upnp_error->message);
 
-static void prv_get_container_property(GUPnPDIDLLiteParser *parser,
-                                      GUPnPDIDLLiteObject *object,
-                                      gpointer user_data)
-{
-       msu_async_cb_data_t *cb_data = user_data;
-       msu_task_t *task = cb_data->task;
-       msu_task_get_prop_t *task_data = &task->ut.get_prop;
+               goto on_complete;
+       }
 
-       if (cb_data->result)
-               goto on_error;
+       cb_data->result = g_variant_ref_sink(g_variant_new_string(token));
 
-       cb_data->result = msu_props_get_container_prop(task_data->prop_name,
-                                                      object);
+       g_free(token);
 
-on_error:
+       MSU_LOG_DEBUG("Service Reset %s", token);
 
-       return;
+on_complete:
+
+       (void) g_idle_add(msu_async_complete_task, cb_data);
+       g_cancellable_disconnect(cb_data->cancellable, cb_data->cancel_id);
+
+       if (upnp_error)
+               g_error_free(upnp_error);
+
+       MSU_LOG_DEBUG("Exit");
 }
 
-static void prv_get_all_property(GUPnPDIDLLiteParser *parser,
-                                GUPnPDIDLLiteObject *object,
-                                gpointer user_data)
+static void prv_get_sr_token_for_prop(GUPnPServiceProxy *proxy,
+                            msu_device_t *device,
+                            GCancellable *cancellable,
+                            msu_async_cb_data_t *cb_data)
 {
-       msu_async_cb_data_t *cb_data = user_data;
-
-       prv_get_object_property(parser, object, user_data);
+       MSU_LOG_DEBUG("Enter");
 
-       if (cb_data->result)
-               goto on_error;
+       if (prv_get_media_server_version(device) < 3) {
+               cb_data->error = g_error_new(MSU_ERROR,
+                                            MSU_ERROR_UNKNOWN_PROPERTY,
+                                            "Unknown property");
 
-       if (GUPNP_IS_DIDL_LITE_CONTAINER(object))
-               prv_get_container_property(parser, object, user_data);
-       else
-               prv_get_item_property(parser, object, user_data);
+               (void) g_idle_add(msu_async_complete_task, cb_data);
 
-on_error:
+               goto on_error;
+       }
 
-       return;
-}
+       gupnp_service_proxy_begin_action(proxy, "GetServiceResetToken",
+                                        prv_service_reset_for_prop_cb,
+                                        cb_data,
+                                        NULL);
 
-static gboolean prv_get_child_count_cb(msu_async_cb_data_t *cb_data,
-                                  gint count)
-{
-       MSU_LOG_DEBUG("Enter");
+       cb_data->proxy = proxy;
 
-       MSU_LOG_DEBUG("Count %d", count);
+       cb_data->cancel_id = g_cancellable_connect(cancellable,
+                                       G_CALLBACK(msu_async_task_cancelled),
+                                       cb_data, NULL);
+       cb_data->cancellable = cancellable;
 
-       cb_data->result =  g_variant_ref_sink(
-               g_variant_new_uint32((guint) count));
+on_error:
 
        MSU_LOG_DEBUG("Exit");
-
-       return TRUE;
 }
 
-static void prv_count_children_cb(GUPnPServiceProxy *proxy,
-                                 GUPnPServiceProxyAction *action,
-                                 gpointer user_data)
+static void prv_service_reset_for_props_cb(GUPnPServiceProxy *proxy,
+                                         GUPnPServiceProxyAction *action,
+                                         gpointer user_data)
 {
-       msu_device_count_data_t *count_data = user_data;
-       msu_async_cb_data_t *cb_data = count_data->cb_data;
        GError *upnp_error = NULL;
-       gint count;
-       gboolean complete = FALSE;
+       gchar *token = NULL;
+       msu_async_cb_data_t *cb_data = user_data;
+       msu_async_get_all_t *cb_task_data;
 
        MSU_LOG_DEBUG("Enter");
 
-       if (!gupnp_service_proxy_end_action(cb_data->proxy, cb_data->action,
-                                           &upnp_error,
-                                           "TotalMatches", G_TYPE_INT,
-                                           &count,
+       if (!gupnp_service_proxy_end_action(proxy, action, &upnp_error,
+                                           "ResetToken", G_TYPE_STRING,
+                                           &token,
                                            NULL)) {
-               MSU_LOG_WARNING("Browse operation failed: %s",
+               MSU_LOG_ERROR("Unable to retrieve ServiceResetToken: %s %s",
+                             g_quark_to_string(upnp_error->domain),
                              upnp_error->message);
 
                cb_data->error = g_error_new(MSU_ERROR,
                                             MSU_ERROR_OPERATION_FAILED,
-                                            "Browse operation failed: %s",
+                                            "GetServiceResetToken failed: %s",
                                             upnp_error->message);
-               goto on_error;
+
+               goto on_complete;
        }
 
-       complete = count_data->cb(cb_data, count);
+       cb_task_data = &cb_data->ut.get_all;
+       g_variant_builder_add(cb_task_data->vb, "{sv}",
+                             MSU_INTERFACE_PROP_ESV_SERVICE_RESET_TOKEN,
+                             g_variant_new_string(token));
 
-on_error:
+       cb_data->result = g_variant_ref_sink(g_variant_builder_end(
+                                               cb_task_data->vb));
 
-       g_free(user_data);
+       g_free(token);
 
-       if (cb_data->error || complete) {
-               (void) g_idle_add(msu_async_complete_task, cb_data);
-               g_cancellable_disconnect(cb_data->cancellable,
-                                        cb_data->cancel_id);
-       }
+       MSU_LOG_DEBUG("Service Reset %s", token);
+
+on_complete:
+
+       (void) g_idle_add(msu_async_complete_task, cb_data);
+       g_cancellable_disconnect(cb_data->cancellable, cb_data->cancel_id);
 
        if (upnp_error)
                g_error_free(upnp_error);
@@ -1119,50 +1541,70 @@ on_error:
        MSU_LOG_DEBUG("Exit");
 }
 
-static void prv_get_child_count(msu_async_cb_data_t *cb_data,
-                               msu_device_count_cb_t cb, const gchar *id)
+static void prv_get_sr_token_for_props(GUPnPServiceProxy *proxy,
+                            msu_device_t *device,
+                            GCancellable *cancellable,
+                            msu_async_cb_data_t *cb_data)
 {
-       msu_device_count_data_t *count_data;
+       msu_async_get_all_t *cb_task_data;
 
        MSU_LOG_DEBUG("Enter");
 
-       prv_msu_device_count_data_new(cb_data, cb, &count_data);
-       cb_data->action =
-               gupnp_service_proxy_begin_action(cb_data->proxy,
-                                                "Browse",
-                                                prv_count_children_cb,
-                                                count_data,
-                                                "ObjectID", G_TYPE_STRING, id,
+       if (prv_get_media_server_version(device) < 3) {
+               cb_task_data = &cb_data->ut.get_all;
 
-                                                "BrowseFlag", G_TYPE_STRING,
-                                                "BrowseDirectChildren",
+               cb_data->result = g_variant_ref_sink(g_variant_builder_end(
+                                                       cb_task_data->vb));
 
-                                                "Filter", G_TYPE_STRING, "",
+               goto on_complete; /* No error here, just skip the property */
+       }
 
-                                                "StartingIndex", G_TYPE_INT,
-                                                0,
+       gupnp_service_proxy_begin_action(proxy, "GetServiceResetToken",
+                                        prv_service_reset_for_props_cb,
+                                        cb_data,
+                                        NULL);
 
-                                                "RequestedCount", G_TYPE_INT,
-                                                1,
+       cb_data->proxy = proxy;
 
-                                                "SortCriteria", G_TYPE_STRING,
-                                                "",
+       cb_data->cancel_id = g_cancellable_connect(cancellable,
+                                       G_CALLBACK(msu_async_task_cancelled),
+                                       cb_data, NULL);
+       cb_data->cancellable = cancellable;
 
-                                                NULL);
+on_complete:
 
-       MSU_LOG_DEBUG("Exit with SUCCESS");
+       (void) g_idle_add(msu_async_complete_task, cb_data);
+
+       MSU_LOG_DEBUG("Exit");
 }
 
-static void prv_get_ms2spec_prop_cb(GUPnPServiceProxy *proxy,
-                                   GUPnPServiceProxyAction *action,
-                                   gpointer user_data)
+static gboolean prv_get_all_child_count_cb(msu_async_cb_data_t *cb_data,
+                                      gint count)
+{
+       msu_async_get_all_t *cb_task_data = &cb_data->ut.get_all;
+
+       msu_props_add_child_count(cb_task_data->vb, count);
+       if (cb_task_data->device_object)
+               prv_get_system_update_id_for_props(cb_data->proxy,
+                                                  cb_task_data->device,
+                                                  cb_data->cancellable,
+                                                  cb_data);
+       else
+               cb_data->result = g_variant_ref_sink(g_variant_builder_end(
+                                                    cb_task_data->vb));
+
+       return !cb_task_data->device_object;
+}
+
+static void prv_get_all_ms2spec_props_cb(GUPnPServiceProxy *proxy,
+                                        GUPnPServiceProxyAction *action,
+                                        gpointer user_data)
 {
        GError *upnp_error = NULL;
        gchar *result = NULL;
        GUPnPDIDLLiteParser *parser = NULL;
        msu_async_cb_data_t *cb_data = user_data;
-       msu_async_get_prop_t *cb_task_data = &cb_data->ut.get_prop;
-       msu_task_get_prop_t *task_data = &cb_data->task->ut.get_prop;
+       msu_async_get_all_t *cb_task_data = &cb_data->ut.get_all;
 
        MSU_LOG_DEBUG("Enter");
 
@@ -1180,7 +1622,7 @@ static void prv_get_ms2spec_prop_cb(GUPnPServiceProxy *proxy,
                goto on_error;
        }
 
-       MSU_LOG_DEBUG("GetMS2SpecProp result: %s", result);
+       MSU_LOG_DEBUG("GetMS2SpecProps result: %s", result);
 
        parser = gupnp_didl_lite_parser_new();
 
@@ -1209,30 +1651,34 @@ static void prv_get_ms2spec_prop_cb(GUPnPServiceProxy *proxy,
                goto on_error;
        }
 
-       if (!cb_data->result) {
-               MSU_LOG_WARNING("Property not defined for object");
+       if (cb_data->error)
+               goto on_error;
 
-               cb_data->error = g_error_new(MSU_ERROR,
-                                            MSU_ERROR_UNKNOWN_PROPERTY,
-                                            "Property not defined for object");
-       }
+       if (cb_task_data->need_child_count) {
+               MSU_LOG_DEBUG("Need Child Count");
 
-on_error:
+               prv_get_child_count(cb_data, prv_get_all_child_count_cb,
+                       cb_data->id);
 
-       if (cb_data->error && !strcmp(task_data->prop_name,
-                                     MSU_INTERFACE_PROP_CHILD_COUNT)) {
-               MSU_LOG_DEBUG("ChildCount not supported by server");
+               goto no_complete;
+       } else if (cb_data->task->type == MSU_TASK_GET_ALL_PROPS &&
+                                               cb_task_data->device_object) {
+               prv_get_system_update_id_for_props(proxy, cb_task_data->device,
+                                               cb_data->cancellable, cb_data);
 
-               g_error_free(cb_data->error);
-               cb_data->error = NULL;
-               prv_get_child_count(cb_data, prv_get_child_count_cb,
-                                   cb_data->id);
+               goto no_complete;
        } else {
-               (void) g_idle_add(msu_async_complete_task, cb_data);
-               g_cancellable_disconnect(cb_data->cancellable,
-                                        cb_data->cancel_id);
+               cb_data->result = g_variant_ref_sink(g_variant_builder_end(
+                                                            cb_task_data->vb));
        }
 
+on_error:
+
+       (void) g_idle_add(msu_async_complete_task, cb_data);
+       g_cancellable_disconnect(cb_data->cancellable, cb_data->cancel_id);
+
+no_complete:
+
        if (upnp_error)
                g_error_free(upnp_error);
 
@@ -1244,62 +1690,43 @@ on_error:
        MSU_LOG_DEBUG("Exit");
 }
 
-static void prv_get_ms2spec_prop(msu_device_context_t *context,
-                                msu_prop_map_t *prop_map,
-                                msu_task_get_prop_t *task_data,
-                                GCancellable *cancellable,
-                                msu_async_cb_data_t *cb_data)
+static void prv_get_all_ms2spec_props(msu_device_context_t *context,
+                                     GCancellable *cancellable,
+                                     msu_async_cb_data_t *cb_data)
 {
-       msu_async_get_prop_t *cb_task_data;
-       const gchar *filter;
-
-       MSU_LOG_DEBUG("Enter");
-
-       cb_task_data = &cb_data->ut.get_prop;
-
-       if (!prop_map) {
-               cb_data->error = g_error_new(MSU_ERROR,
-                                            MSU_ERROR_UNKNOWN_PROPERTY,
-                                            "Unknown property");
-               goto on_error;
-       }
+       msu_async_get_all_t *cb_task_data = &cb_data->ut.get_all;
+       msu_task_t *task = cb_data->task;
+       msu_task_get_props_t *task_data = &task->ut.get_props;
 
-       filter = prop_map->filter ? prop_map->upnp_prop_name : "";
+       MSU_LOG_DEBUG("Enter called");
 
-       if (!strcmp(MSU_INTERFACE_MEDIA_CONTAINER, task_data->interface_name)) {
-               cb_task_data->prop_func =
-                       G_CALLBACK(prv_get_container_property);
-       } else if (!strcmp(MSU_INTERFACE_MEDIA_ITEM,
-                          task_data->interface_name)) {
-               cb_task_data->prop_func = G_CALLBACK(prv_get_item_property);
-       } else if (!strcmp(MSU_INTERFACE_MEDIA_OBJECT,
-                          task_data->interface_name)) {
-               cb_task_data->prop_func = G_CALLBACK(prv_get_object_property);
-       } else  if (!strcmp("", task_data->interface_name)) {
-               cb_task_data->prop_func = G_CALLBACK(prv_get_all_property);
-       } else {
-               MSU_LOG_WARNING("Interface is unknown.%s",
-                             task_data->interface_name);
+       if (!strcmp(MSU_INTERFACE_MEDIA_CONTAINER, task_data->interface_name))
+               cb_task_data->prop_func = G_CALLBACK(prv_get_container);
+       else if (!strcmp(MSU_INTERFACE_MEDIA_ITEM, task_data->interface_name))
+               cb_task_data->prop_func = G_CALLBACK(prv_get_item);
+       else if (!strcmp(MSU_INTERFACE_MEDIA_OBJECT, task_data->interface_name))
+               cb_task_data->prop_func = G_CALLBACK(prv_get_object);
+       else  if (!strcmp("", task_data->interface_name))
+               cb_task_data->prop_func = G_CALLBACK(prv_get_all);
+       else {
+               MSU_LOG_WARNING("Interface is unknown.");
 
-               cb_data->error = g_error_new(MSU_ERROR,
-                                            MSU_ERROR_UNKNOWN_INTERFACE,
-                                            "Interface is unknown.");
+               cb_data->error =
+                       g_error_new(MSU_ERROR, MSU_ERROR_UNKNOWN_INTERFACE,
+                                   "Interface is unknown.");
                goto on_error;
        }
 
        cb_data->action = gupnp_service_proxy_begin_action(
                context->service_proxy, "Browse",
-               prv_get_ms2spec_prop_cb,
-               cb_data,
+               prv_get_all_ms2spec_props_cb, cb_data,
                "ObjectID", G_TYPE_STRING, cb_data->id,
-               "BrowseFlag", G_TYPE_STRING,
-               "BrowseMetadata",
-               "Filter", G_TYPE_STRING, filter,
+               "BrowseFlag", G_TYPE_STRING, "BrowseMetadata",
+               "Filter", G_TYPE_STRING, "*",
                "StartingIndex", G_TYPE_INT, 0,
                "RequestedCount", G_TYPE_INT, 0,
                "SortCriteria", G_TYPE_STRING,
-               "",
-               NULL);
+               "", NULL);
 
        cb_data->proxy = context->service_proxy;
 
@@ -1322,202 +1749,1285 @@ on_error:
        return;
 }
 
-void msu_device_get_prop(msu_device_t *device,  msu_task_t *task,
-                        msu_async_cb_data_t *cb_data,
-                        msu_prop_map_t *prop_map, gboolean root_object,
-                        GCancellable *cancellable)
+void msu_device_get_all_props(msu_device_t *device,  msu_client_t *client,
+                             msu_task_t *task, msu_async_cb_data_t *cb_data,
+                             gboolean root_object,
+                             GCancellable *cancellable)
 {
-       msu_task_get_prop_t *task_data = &task->ut.get_prop;
+       msu_async_get_all_t *cb_task_data;
+       msu_task_get_props_t *task_data = &task->ut.get_props;
        msu_device_context_t *context;
 
        MSU_LOG_DEBUG("Enter");
 
-       context = msu_device_get_context(device);
+       context = msu_device_get_context(device, client);
+       cb_task_data = &cb_data->ut.get_all;
+
+       cb_task_data->vb = g_variant_builder_new(G_VARIANT_TYPE("a{sv}"));
+       cb_task_data->device_object = root_object;
+       cb_task_data->device = device;
 
        if (!strcmp(task_data->interface_name, MSU_INTERFACE_MEDIA_DEVICE)) {
                if (root_object) {
-                       cb_data->result =
-                               msu_props_get_device_prop(
-                                       (GUPnPDeviceInfo *)
-                                       context->device_proxy,
-                                       task_data->prop_name);
-                       if (!cb_data->result)
-                               cb_data->error = g_error_new(
-                                       MSU_ERROR,
-                                       MSU_ERROR_UNKNOWN_PROPERTY,
-                                       "Unknown property");
+                       msu_props_add_device(
+                               (GUPnPDeviceInfo *) context->device_proxy,
+                               device,
+                               cb_task_data->vb);
+
+                       prv_get_system_update_id_for_props(
+                                                       context->service_proxy,
+                                                       device,
+                                                       cancellable,
+                                                       cb_data);
                } else {
                        cb_data->error =
                                g_error_new(MSU_ERROR,
                                            MSU_ERROR_UNKNOWN_INTERFACE,
-                                           "Interface is unknown.");
-               }
+                                           "Interface is only valid on "
+                                           "root objects.");
 
-               (void) g_idle_add(msu_async_complete_task, cb_data);
+                       (void) g_idle_add(msu_async_complete_task, cb_data);
+               }
 
        } else if (strcmp(task_data->interface_name, "")) {
-               prv_get_ms2spec_prop(context, prop_map, &task->ut.get_prop,
-                                    cancellable, cb_data);
+               cb_task_data->device_object = FALSE;
+               prv_get_all_ms2spec_props(context, cancellable, cb_data);
        } else {
                if (root_object)
-                       cb_data->result = msu_props_get_device_prop(
-                               (GUPnPDeviceInfo *)
-                               context->device_proxy,
-                               task_data->prop_name);
+                       msu_props_add_device(
+                               (GUPnPDeviceInfo *) context->device_proxy,
+                               device,
+                               cb_task_data->vb);
 
-               if (cb_data->result)
-                       (void) g_idle_add(msu_async_complete_task, cb_data);
-               else
-                       prv_get_ms2spec_prop(context, prop_map,
-                                            &task->ut.get_prop, cancellable,
-                                            cb_data);
+               prv_get_all_ms2spec_props(context, cancellable, cb_data);
        }
 
        MSU_LOG_DEBUG("Exit");
 }
 
-static void prv_found_target(GUPnPDIDLLiteParser *parser,
-                            GUPnPDIDLLiteObject *object,
-                            gpointer user_data)
+static void prv_get_object_property(GUPnPDIDLLiteParser *parser,
+                                   GUPnPDIDLLiteObject *object,
+                                   gpointer user_data)
 {
        msu_async_cb_data_t *cb_data = user_data;
-       msu_async_bas_t *cb_task_data = &cb_data->ut.bas;
-       const char *id;
-       const char *parent_path;
-       gchar *path = NULL;
-       gboolean have_child_count;
-       msu_device_object_builder_t *builder;
+       msu_task_t *task = cb_data->task;
+       msu_task_get_prop_t *task_data = &task->ut.get_prop;
+       msu_async_get_prop_t *cb_task_data = &cb_data->ut.get_prop;
 
-       MSU_LOG_DEBUG("Enter");
+       if (cb_data->result)
+               goto on_error;
 
-       builder = g_new0(msu_device_object_builder_t, 1);
+       cb_data->result = msu_props_get_object_prop(task_data->prop_name,
+                                                   cb_task_data->root_path,
+                                                   object);
 
-       id = gupnp_didl_lite_object_get_parent_id(object);
+on_error:
 
-       if (!id || !strcmp(id, "-1") || !strcmp(id, "")) {
-               parent_path = cb_task_data->root_path;
-       } else {
-               path = msu_path_from_id(cb_task_data->root_path, id);
-               parent_path = path;
-       }
+       return;
+}
 
-       builder->vb = g_variant_builder_new(G_VARIANT_TYPE("a{sv}"));
+static void prv_get_item_property(GUPnPDIDLLiteParser *parser,
+                                 GUPnPDIDLLiteObject *object,
+                                 gpointer user_data)
+{
+       msu_async_cb_data_t *cb_data = user_data;
+       msu_task_t *task = cb_data->task;
+       msu_task_get_prop_t *task_data = &task->ut.get_prop;
+       msu_async_get_prop_t *cb_task_data = &cb_data->ut.get_prop;
 
-       if (!msu_props_add_object(builder->vb, object, cb_task_data->root_path,
-                                 parent_path, cb_task_data->filter_mask))
+       if (cb_data->result)
                goto on_error;
 
-       if (GUPNP_IS_DIDL_LITE_CONTAINER(object)) {
-               msu_props_add_container(builder->vb,
-                                       (GUPnPDIDLLiteContainer *) object,
-                                       cb_task_data->filter_mask,
-                                       &have_child_count);
-
-               if (!have_child_count && (cb_task_data->filter_mask &
-                                         MSU_UPNP_MASK_PROP_CHILD_COUNT)) {
-                       builder->needs_child_count = TRUE;
-                       builder->id = g_strdup(
-                               gupnp_didl_lite_object_get_id(object));
-                       cb_task_data->need_child_count = TRUE;
-               }
-       } else {
-               msu_props_add_item(builder->vb,
-                                  object,
-                                  cb_task_data->root_path,
-                                  cb_task_data->filter_mask,
-                                  cb_task_data->protocol_info);
-       }
-
-       g_ptr_array_add(cb_task_data->vbs, builder);
-       g_free(path);
-
-       MSU_LOG_DEBUG("Exit with SUCCESS");
-
-       return;
+       cb_data->result = msu_props_get_item_prop(task_data->prop_name,
+                                                 cb_task_data->root_path,
+                                                 object,
+                                                 cb_task_data->protocol_info);
 
 on_error:
 
-       g_free(path);
-       prv_msu_device_object_builder_delete(builder);
-
-       MSU_LOG_DEBUG("Exit with FAIL");
+       return;
 }
 
-static void prv_search_cb(GUPnPServiceProxy *proxy,
-                         GUPnPServiceProxyAction *action,
-                         gpointer user_data)
-{
-       gchar *result = NULL;
+static void prv_get_container_property(GUPnPDIDLLiteParser *parser,
+                                      GUPnPDIDLLiteObject *object,
+                                      gpointer user_data)
+{
+       msu_async_cb_data_t *cb_data = user_data;
+       msu_task_t *task = cb_data->task;
+       msu_task_get_prop_t *task_data = &task->ut.get_prop;
+
+       if (cb_data->result)
+               goto on_error;
+
+       cb_data->result = msu_props_get_container_prop(task_data->prop_name,
+                                                      object);
+
+on_error:
+
+       return;
+}
+
+static void prv_get_all_property(GUPnPDIDLLiteParser *parser,
+                                GUPnPDIDLLiteObject *object,
+                                gpointer user_data)
+{
+       msu_async_cb_data_t *cb_data = user_data;
+
+       prv_get_object_property(parser, object, user_data);
+
+       if (cb_data->result)
+               goto on_error;
+
+       if (GUPNP_IS_DIDL_LITE_CONTAINER(object))
+               prv_get_container_property(parser, object, user_data);
+       else
+               prv_get_item_property(parser, object, user_data);
+
+on_error:
+
+       return;
+}
+
+static gboolean prv_get_child_count_cb(msu_async_cb_data_t *cb_data,
+                                  gint count)
+{
+       MSU_LOG_DEBUG("Enter");
+
+       MSU_LOG_DEBUG("Count %d", count);
+
+       cb_data->result =  g_variant_ref_sink(
+               g_variant_new_uint32((guint) count));
+
+       MSU_LOG_DEBUG("Exit");
+
+       return TRUE;
+}
+
+static void prv_count_children_cb(GUPnPServiceProxy *proxy,
+                                 GUPnPServiceProxyAction *action,
+                                 gpointer user_data)
+{
+       msu_device_count_data_t *count_data = user_data;
+       msu_async_cb_data_t *cb_data = count_data->cb_data;
+       GError *upnp_error = NULL;
+       gint count;
+       gboolean complete = FALSE;
+
+       MSU_LOG_DEBUG("Enter");
+
+       if (!gupnp_service_proxy_end_action(cb_data->proxy, cb_data->action,
+                                           &upnp_error,
+                                           "TotalMatches", G_TYPE_INT,
+                                           &count,
+                                           NULL)) {
+               MSU_LOG_WARNING("Browse operation failed: %s",
+                             upnp_error->message);
+
+               cb_data->error = g_error_new(MSU_ERROR,
+                                            MSU_ERROR_OPERATION_FAILED,
+                                            "Browse operation failed: %s",
+                                            upnp_error->message);
+               goto on_error;
+       }
+
+       complete = count_data->cb(cb_data, count);
+
+on_error:
+
+       g_free(user_data);
+
+       if (cb_data->error || complete) {
+               (void) g_idle_add(msu_async_complete_task, cb_data);
+               g_cancellable_disconnect(cb_data->cancellable,
+                                        cb_data->cancel_id);
+       }
+
+       if (upnp_error)
+               g_error_free(upnp_error);
+
+       MSU_LOG_DEBUG("Exit");
+}
+
+static void prv_get_child_count(msu_async_cb_data_t *cb_data,
+                               msu_device_count_cb_t cb, const gchar *id)
+{
+       msu_device_count_data_t *count_data;
+
+       MSU_LOG_DEBUG("Enter");
+
+       prv_msu_device_count_data_new(cb_data, cb, &count_data);
+       cb_data->action =
+               gupnp_service_proxy_begin_action(cb_data->proxy,
+                                                "Browse",
+                                                prv_count_children_cb,
+                                                count_data,
+                                                "ObjectID", G_TYPE_STRING, id,
+
+                                                "BrowseFlag", G_TYPE_STRING,
+                                                "BrowseDirectChildren",
+
+                                                "Filter", G_TYPE_STRING, "",
+
+                                                "StartingIndex", G_TYPE_INT,
+                                                0,
+
+                                                "RequestedCount", G_TYPE_INT,
+                                                1,
+
+                                                "SortCriteria", G_TYPE_STRING,
+                                                "",
+
+                                                NULL);
+
+       MSU_LOG_DEBUG("Exit with SUCCESS");
+}
+
+static void prv_get_ms2spec_prop_cb(GUPnPServiceProxy *proxy,
+                                   GUPnPServiceProxyAction *action,
+                                   gpointer user_data)
+{
+       GError *upnp_error = NULL;
+       gchar *result = NULL;
+       GUPnPDIDLLiteParser *parser = NULL;
+       msu_async_cb_data_t *cb_data = user_data;
+       msu_async_get_prop_t *cb_task_data = &cb_data->ut.get_prop;
+       msu_task_get_prop_t *task_data = &cb_data->task->ut.get_prop;
+
+       MSU_LOG_DEBUG("Enter");
+
+       if (!gupnp_service_proxy_end_action(cb_data->proxy, cb_data->action,
+                                           &upnp_error,
+                                           "Result", G_TYPE_STRING,
+                                           &result, NULL)) {
+               MSU_LOG_WARNING("Browse operation failed: %s",
+                             upnp_error->message);
+
+               cb_data->error = g_error_new(MSU_ERROR,
+                                            MSU_ERROR_OPERATION_FAILED,
+                                            "Browse operation failed: %s",
+                                            upnp_error->message);
+               goto on_error;
+       }
+
+       MSU_LOG_DEBUG("GetMS2SpecProp result: %s", result);
+
+       parser = gupnp_didl_lite_parser_new();
+
+       g_signal_connect(parser, "object-available" , cb_task_data->prop_func,
+                        cb_data);
+
+       if (!gupnp_didl_lite_parser_parse_didl(parser, result, &upnp_error)) {
+               if (upnp_error->code == GUPNP_XML_ERROR_EMPTY_NODE) {
+                       MSU_LOG_WARNING("Property not defined for object");
+
+                       cb_data->error =
+                               g_error_new(MSU_ERROR,
+                                           MSU_ERROR_UNKNOWN_PROPERTY,
+                                           "Property not defined for object");
+               } else {
+                       MSU_LOG_WARNING("Unable to parse results of browse: %s",
+                                     upnp_error->message);
+
+                       cb_data->error =
+                               g_error_new(MSU_ERROR,
+                                           MSU_ERROR_OPERATION_FAILED,
+                                           "Unable to parse results of "
+                                           "browse: %s",
+                                           upnp_error->message);
+               }
+               goto on_error;
+       }
+
+       if (!cb_data->result) {
+               MSU_LOG_WARNING("Property not defined for object");
+
+               cb_data->error = g_error_new(MSU_ERROR,
+                                            MSU_ERROR_UNKNOWN_PROPERTY,
+                                            "Property not defined for object");
+       }
+
+on_error:
+
+       if (cb_data->error && !strcmp(task_data->prop_name,
+                                     MSU_INTERFACE_PROP_CHILD_COUNT)) {
+               MSU_LOG_DEBUG("ChildCount not supported by server");
+
+               g_error_free(cb_data->error);
+               cb_data->error = NULL;
+               prv_get_child_count(cb_data, prv_get_child_count_cb,
+                                   cb_data->id);
+       } else {
+               (void) g_idle_add(msu_async_complete_task, cb_data);
+               g_cancellable_disconnect(cb_data->cancellable,
+                                        cb_data->cancel_id);
+       }
+
+       if (upnp_error)
+               g_error_free(upnp_error);
+
+       if (parser)
+               g_object_unref(parser);
+
+       g_free(result);
+
+       MSU_LOG_DEBUG("Exit");
+}
+
+static void prv_get_ms2spec_prop(msu_device_context_t *context,
+                                msu_prop_map_t *prop_map,
+                                msu_task_get_prop_t *task_data,
+                                GCancellable *cancellable,
+                                msu_async_cb_data_t *cb_data)
+{
+       msu_async_get_prop_t *cb_task_data;
+       const gchar *filter;
+
+       MSU_LOG_DEBUG("Enter");
+
+       cb_task_data = &cb_data->ut.get_prop;
+
+       if (!prop_map) {
+               cb_data->error = g_error_new(MSU_ERROR,
+                                            MSU_ERROR_UNKNOWN_PROPERTY,
+                                            "Unknown property");
+               goto on_error;
+       }
+
+       filter = prop_map->filter ? prop_map->upnp_prop_name : "";
+
+       if (!strcmp(MSU_INTERFACE_MEDIA_CONTAINER, task_data->interface_name)) {
+               cb_task_data->prop_func =
+                       G_CALLBACK(prv_get_container_property);
+       } else if (!strcmp(MSU_INTERFACE_MEDIA_ITEM,
+                          task_data->interface_name)) {
+               cb_task_data->prop_func = G_CALLBACK(prv_get_item_property);
+       } else if (!strcmp(MSU_INTERFACE_MEDIA_OBJECT,
+                          task_data->interface_name)) {
+               cb_task_data->prop_func = G_CALLBACK(prv_get_object_property);
+       } else  if (!strcmp("", task_data->interface_name)) {
+               cb_task_data->prop_func = G_CALLBACK(prv_get_all_property);
+       } else {
+               MSU_LOG_WARNING("Interface is unknown.%s",
+                             task_data->interface_name);
+
+               cb_data->error = g_error_new(MSU_ERROR,
+                                            MSU_ERROR_UNKNOWN_INTERFACE,
+                                            "Interface is unknown.");
+               goto on_error;
+       }
+
+       cb_data->action = gupnp_service_proxy_begin_action(
+               context->service_proxy, "Browse",
+               prv_get_ms2spec_prop_cb,
+               cb_data,
+               "ObjectID", G_TYPE_STRING, cb_data->id,
+               "BrowseFlag", G_TYPE_STRING,
+               "BrowseMetadata",
+               "Filter", G_TYPE_STRING, filter,
+               "StartingIndex", G_TYPE_INT, 0,
+               "RequestedCount", G_TYPE_INT, 0,
+               "SortCriteria", G_TYPE_STRING,
+               "",
+               NULL);
+
+       cb_data->proxy = context->service_proxy;
+
+       cb_data->cancel_id =
+               g_cancellable_connect(cancellable,
+                                     G_CALLBACK(msu_async_task_cancelled),
+                                     cb_data, NULL);
+       cb_data->cancellable = cancellable;
+
+       MSU_LOG_DEBUG("Exit with SUCCESS");
+
+       return;
+
+on_error:
+
+       (void) g_idle_add(msu_async_complete_task, cb_data);
+
+       MSU_LOG_DEBUG("Exit with FAIL");
+
+       return;
+}
+
+void msu_device_get_prop(msu_device_t *device, msu_client_t *client,
+                        msu_task_t *task, msu_async_cb_data_t *cb_data,
+                        msu_prop_map_t *prop_map, gboolean root_object,
+                        GCancellable *cancellable)
+{
+       msu_task_get_prop_t *task_data = &task->ut.get_prop;
+       msu_device_context_t *context;
+       gboolean complete = FALSE;
+
+       MSU_LOG_DEBUG("Enter");
+
+       context = msu_device_get_context(device, client);
+
+       if (!strcmp(task_data->interface_name, MSU_INTERFACE_MEDIA_DEVICE)) {
+               if (root_object) {
+                       if (!strcmp(task_data->prop_name,
+                               MSU_INTERFACE_PROP_ESV_SYSTEM_UPDATE_ID)) {
+                               prv_get_system_update_id_for_prop(
+                                                       context->service_proxy,
+                                                       device,
+                                                       cancellable,
+                                                       cb_data);
+                       } else if (!strcmp(task_data->prop_name,
+                                 MSU_INTERFACE_PROP_ESV_SERVICE_RESET_TOKEN)) {
+                               prv_get_sr_token_for_prop(
+                                                       context->service_proxy,
+                                                       device,
+                                                       cancellable,
+                                                       cb_data);
+                       } else {
+                               cb_data->result =
+                                       msu_props_get_device_prop(
+                                               (GUPnPDeviceInfo *)
+                                               context->device_proxy,
+                                               device,
+                                               task_data->prop_name);
+
+                               if (!cb_data->result)
+                                       cb_data->error = g_error_new(
+                                               MSU_ERROR,
+                                               MSU_ERROR_UNKNOWN_PROPERTY,
+                                               "Unknown property");
+
+                               (void) g_idle_add(msu_async_complete_task,
+                                                 cb_data);
+                       }
+
+               } else {
+                       cb_data->error =
+                               g_error_new(MSU_ERROR,
+                                           MSU_ERROR_UNKNOWN_INTERFACE,
+                                           "Interface is unknown.");
+
+                       (void) g_idle_add(msu_async_complete_task, cb_data);
+               }
+
+       } else if (strcmp(task_data->interface_name, "")) {
+               prv_get_ms2spec_prop(context, prop_map, &task->ut.get_prop,
+                                    cancellable, cb_data);
+       } else {
+               if (root_object) {
+                       if (!strcmp(task_data->prop_name,
+                               MSU_INTERFACE_PROP_ESV_SYSTEM_UPDATE_ID)) {
+                               prv_get_system_update_id_for_prop(
+                                                       context->service_proxy,
+                                                       device,
+                                                       cancellable,
+                                                       cb_data);
+                               complete = TRUE;
+                       } else if (!strcmp(task_data->prop_name,
+                                 MSU_INTERFACE_PROP_ESV_SERVICE_RESET_TOKEN)) {
+                               prv_get_sr_token_for_prop(
+                                                       context->service_proxy,
+                                                       device,
+                                                       cancellable,
+                                                       cb_data);
+                               complete = TRUE;
+                       } else {
+                               cb_data->result = msu_props_get_device_prop(
+                                       (GUPnPDeviceInfo *)
+                                       context->device_proxy,
+                                       device,
+                                       task_data->prop_name);
+                               if (cb_data->result) {
+                                       (void) g_idle_add(
+                                                       msu_async_complete_task,
+                                                       cb_data);
+                                       complete = TRUE;
+                               }
+                       }
+               }
+
+               if (!complete)
+                       prv_get_ms2spec_prop(context, prop_map,
+                                            &task->ut.get_prop, cancellable,
+                                            cb_data);
+       }
+
+       MSU_LOG_DEBUG("Exit");
+}
+
+static void prv_found_target(GUPnPDIDLLiteParser *parser,
+                            GUPnPDIDLLiteObject *object,
+                            gpointer user_data)
+{
+       msu_async_cb_data_t *cb_data = user_data;
+       msu_async_bas_t *cb_task_data = &cb_data->ut.bas;
+       const char *id;
+       const char *parent_path;
+       gchar *path = NULL;
+       gboolean have_child_count;
+       msu_device_object_builder_t *builder;
+
+       MSU_LOG_DEBUG("Enter");
+
+       builder = g_new0(msu_device_object_builder_t, 1);
+
+       id = gupnp_didl_lite_object_get_parent_id(object);
+
+       if (!id || !strcmp(id, "-1") || !strcmp(id, "")) {
+               parent_path = cb_task_data->root_path;
+       } else {
+               path = msu_path_from_id(cb_task_data->root_path, id);
+               parent_path = path;
+       }
+
+       builder->vb = g_variant_builder_new(G_VARIANT_TYPE("a{sv}"));
+
+       if (!msu_props_add_object(builder->vb, object, cb_task_data->root_path,
+                                 parent_path, cb_task_data->filter_mask))
+               goto on_error;
+
+       if (GUPNP_IS_DIDL_LITE_CONTAINER(object)) {
+               msu_props_add_container(builder->vb,
+                                       (GUPnPDIDLLiteContainer *) object,
+                                       cb_task_data->filter_mask,
+                                       &have_child_count);
+
+               if (!have_child_count && (cb_task_data->filter_mask &
+                                         MSU_UPNP_MASK_PROP_CHILD_COUNT)) {
+                       builder->needs_child_count = TRUE;
+                       builder->id = g_strdup(
+                               gupnp_didl_lite_object_get_id(object));
+                       cb_task_data->need_child_count = TRUE;
+               }
+       } else {
+               msu_props_add_item(builder->vb,
+                                  object,
+                                  cb_task_data->root_path,
+                                  cb_task_data->filter_mask,
+                                  cb_task_data->protocol_info);
+       }
+
+       g_ptr_array_add(cb_task_data->vbs, builder);
+       g_free(path);
+
+       MSU_LOG_DEBUG("Exit with SUCCESS");
+
+       return;
+
+on_error:
+
+       g_free(path);
+       prv_msu_device_object_builder_delete(builder);
+
+       MSU_LOG_DEBUG("Exit with FAIL");
+}
+
+static void prv_search_cb(GUPnPServiceProxy *proxy,
+                         GUPnPServiceProxyAction *action,
+                         gpointer user_data)
+{
+       gchar *result = NULL;
+       GUPnPDIDLLiteParser *parser = NULL;
+       GError *upnp_error = NULL;
+       msu_async_cb_data_t *cb_data = user_data;
+       msu_async_bas_t *cb_task_data = &cb_data->ut.bas;
+
+       MSU_LOG_DEBUG("Enter");
+
+       if (!gupnp_service_proxy_end_action(cb_data->proxy, cb_data->action,
+                                           &upnp_error,
+                                           "Result", G_TYPE_STRING,
+                                           &result,
+                                           "TotalMatches", G_TYPE_INT,
+                                           &cb_task_data->max_count,
+                                           NULL)) {
+
+               MSU_LOG_WARNING("Search operation failed %s",
+                             upnp_error->message);
+
+               cb_data->error = g_error_new(MSU_ERROR,
+                                            MSU_ERROR_OPERATION_FAILED,
+                                            "Search operation failed: %s",
+                                            upnp_error->message);
+               goto on_error;
+       }
+
+       parser = gupnp_didl_lite_parser_new();
+
+       cb_task_data->vbs = g_ptr_array_new_with_free_func(
+               prv_msu_device_object_builder_delete);
+
+       g_signal_connect(parser, "object-available" ,
+                        G_CALLBACK(prv_found_target), cb_data);
+
+       MSU_LOG_DEBUG("Server Search result: %s", result);
+
+       if (!gupnp_didl_lite_parser_parse_didl(parser, result, &upnp_error)
+               && upnp_error->code != GUPNP_XML_ERROR_EMPTY_NODE) {
+               MSU_LOG_WARNING("Unable to parse results of search: %s",
+                             upnp_error->message);
+
+               cb_data->error = g_error_new(MSU_ERROR,
+                                            MSU_ERROR_OPERATION_FAILED,
+                                            "Unable to parse results of "
+                                            "search: %s", upnp_error->message);
+               goto on_error;
+       }
+
+       if (cb_task_data->need_child_count) {
+               MSU_LOG_DEBUG("Need to retrieve child count");
+
+               if (cb_data->task->multiple_retvals)
+                       cb_task_data->get_children_cb =
+                               prv_get_search_ex_result;
+               else
+                       cb_task_data->get_children_cb = prv_get_children_result;
+               prv_retrieve_child_count_for_list(cb_data);
+               goto no_complete;
+       } else {
+               if (cb_data->task->multiple_retvals)
+                       prv_get_search_ex_result(cb_data);
+               else
+                       prv_get_children_result(cb_data);
+       }
+
+on_error:
+
+       (void) g_idle_add(msu_async_complete_task, cb_data);
+       g_cancellable_disconnect(cb_data->cancellable, cb_data->cancel_id);
+
+no_complete:
+
+       if (parser)
+               g_object_unref(parser);
+
+       g_free(result);
+
+       if (upnp_error)
+               g_error_free(upnp_error);
+
+       MSU_LOG_DEBUG("Exit");
+}
+
+void msu_device_search(msu_device_t *device, msu_client_t *client,
+                      msu_task_t *task, msu_async_cb_data_t *cb_data,
+                      const gchar *upnp_filter, const gchar *upnp_query,
+                      const gchar *sort_by, GCancellable *cancellable)
+{
+       msu_device_context_t *context;
+
+       MSU_LOG_DEBUG("Enter");
+
+       context = msu_device_get_context(device, client);
+
+       cb_data->action = gupnp_service_proxy_begin_action(
+               context->service_proxy, "Search",
+               prv_search_cb,
+               cb_data,
+               "ContainerID", G_TYPE_STRING, cb_data->id,
+               "SearchCriteria", G_TYPE_STRING, upnp_query,
+               "Filter", G_TYPE_STRING, upnp_filter,
+               "StartingIndex", G_TYPE_INT, task->ut.search.start,
+               "RequestedCount", G_TYPE_INT, task->ut.search.count,
+               "SortCriteria", G_TYPE_STRING, sort_by,
+               NULL);
+
+       cb_data->proxy = context->service_proxy;
+
+       cb_data->cancel_id =
+               g_cancellable_connect(cancellable,
+                                     G_CALLBACK(msu_async_task_cancelled),
+                                     cb_data, NULL);
+       cb_data->cancellable = cancellable;
+
+       MSU_LOG_DEBUG("Exit");
+}
+
+static void prv_get_resource(GUPnPDIDLLiteParser *parser,
+                            GUPnPDIDLLiteObject *object,
+                            gpointer user_data)
+{
+       msu_async_cb_data_t *cb_data = user_data;
+       msu_task_t *task = cb_data->task;
+       msu_task_get_resource_t *task_data = &task->ut.resource;
+       msu_async_get_all_t *cb_task_data = &cb_data->ut.get_all;
+
+       MSU_LOG_DEBUG("Enter");
+
+       msu_props_add_resource(cb_task_data->vb, object,
+                              cb_task_data->filter_mask,
+                              task_data->protocol_info);
+}
+
+void msu_device_get_resource(msu_device_t *device, msu_client_t *client,
+                            msu_task_t *task, msu_async_cb_data_t *cb_data,
+                            const gchar *upnp_filter,
+                            GCancellable *cancellable)
+{
+       msu_async_get_all_t *cb_task_data;
+       msu_device_context_t *context;
+
+       context = msu_device_get_context(device, client);
+       cb_task_data = &cb_data->ut.get_all;
+
+       cb_task_data->vb = g_variant_builder_new(G_VARIANT_TYPE("a{sv}"));
+       cb_task_data->prop_func = G_CALLBACK(prv_get_resource);
+       cb_task_data->device_object = FALSE;
+
+       cb_data->action = gupnp_service_proxy_begin_action(
+               context->service_proxy, "Browse",
+               prv_get_all_ms2spec_props_cb, cb_data,
+               "ObjectID", G_TYPE_STRING, cb_data->id,
+               "BrowseFlag", G_TYPE_STRING, "BrowseMetadata",
+               "Filter", G_TYPE_STRING, upnp_filter,
+               "StartingIndex", G_TYPE_INT, 0,
+               "RequestedCount", G_TYPE_INT, 0,
+               "SortCriteria", G_TYPE_STRING,
+               "", NULL);
+
+       cb_data->proxy = context->service_proxy;
+
+       cb_data->cancel_id =
+               g_cancellable_connect(cancellable,
+                                     G_CALLBACK(msu_async_task_cancelled),
+                                     cb_data, NULL);
+       cb_data->cancellable = cancellable;
+
+       MSU_LOG_DEBUG("Exit");
+}
+
+static gchar *prv_create_new_container_didl(const gchar *parent_id,
+                                           msu_task_t *task)
+{
+       GUPnPDIDLLiteWriter *writer;
+       GUPnPDIDLLiteObject *item;
+       GUPnPDIDLLiteContainer *container;
+       gchar *retval;
+       GVariantIter iter;
+       GVariant *child_type;
+       const gchar *actual_type;
+
+       writer = gupnp_didl_lite_writer_new(NULL);
+       item = GUPNP_DIDL_LITE_OBJECT(
+                               gupnp_didl_lite_writer_add_container(writer));
+       container = GUPNP_DIDL_LITE_CONTAINER(item);
+
+       gupnp_didl_lite_object_set_id(item, "");
+       gupnp_didl_lite_object_set_title(item,
+                                       task->ut.create_container.display_name);
+       gupnp_didl_lite_object_set_parent_id(item, parent_id);
+       actual_type = msu_props_media_spec_to_upnp_class(
+                                               task->ut.create_container.type);
+       gupnp_didl_lite_object_set_upnp_class(item, actual_type);
+       gupnp_didl_lite_object_set_restricted(item, FALSE);
+       gupnp_didl_lite_object_set_dlna_managed(item, GUPNP_OCM_FLAGS_UPLOAD);
+
+       g_variant_iter_init(&iter, task->ut.create_container.child_types);
+       while ((child_type = g_variant_iter_next_value(&iter))) {
+               actual_type = msu_props_media_spec_to_upnp_class(
+                                       g_variant_get_string(child_type, NULL));
+               if (actual_type != NULL)
+                       gupnp_didl_lite_container_add_create_class(container,
+                                                                  actual_type);
+               g_variant_unref(child_type);
+       }
+
+       retval = gupnp_didl_lite_writer_get_string(writer);
+
+       g_object_unref(item);
+       g_object_unref(writer);
+
+       return retval;
+}
+
+static gchar *prv_create_upload_didl(const gchar *parent_id, msu_task_t *task,
+                                    const gchar *object_class,
+                                    const gchar *mime_type)
+{
+       GUPnPDIDLLiteWriter *writer;
+       GUPnPDIDLLiteObject *item;
+       gchar *retval;
+       GUPnPProtocolInfo *protocol_info;
+       GUPnPDIDLLiteResource *res;
+
+       writer = gupnp_didl_lite_writer_new(NULL);
+       item = GUPNP_DIDL_LITE_OBJECT(gupnp_didl_lite_writer_add_item(writer));
+
+       gupnp_didl_lite_object_set_id(item, "");
+       gupnp_didl_lite_object_set_title(item, task->ut.upload.display_name);
+       gupnp_didl_lite_object_set_parent_id(item, parent_id);
+       gupnp_didl_lite_object_set_upnp_class(item, object_class);
+       gupnp_didl_lite_object_set_restricted(item, FALSE);
+
+       protocol_info = gupnp_protocol_info_new();
+       gupnp_protocol_info_set_mime_type(protocol_info, mime_type);
+       gupnp_protocol_info_set_protocol(protocol_info, "*");
+       gupnp_protocol_info_set_network(protocol_info, "*");
+
+       res = gupnp_didl_lite_object_add_resource(item);
+       gupnp_didl_lite_resource_set_protocol_info(res, protocol_info);
+
+       /* TODO: Need to compute DLNA Profile */
+
+       retval = gupnp_didl_lite_writer_get_string(writer);
+
+       g_object_unref(res);
+       g_object_unref(protocol_info);
+       g_object_unref(item);
+       g_object_unref(writer);
+
+       return retval;
+}
+
+static void prv_extract_import_uri(GUPnPDIDLLiteParser *parser,
+                                  GUPnPDIDLLiteObject *object,
+                                  gpointer user_data)
+{
+       gchar **import_uri = user_data;
+       GList *resources;
+       GList *ptr;
+       GUPnPDIDLLiteResource *res;
+       const gchar *uri;
+
+       if (!*import_uri) {
+               resources = gupnp_didl_lite_object_get_resources(object);
+               ptr = resources;
+               while (ptr) {
+                       res = ptr->data;
+                       if (!*import_uri) {
+                               uri = gupnp_didl_lite_resource_get_import_uri(
+                                       res);
+                               if (uri)
+                                       *import_uri = g_strdup(uri);
+                       }
+                       g_object_unref(res);
+                       ptr = ptr->next;
+               }
+
+               g_list_free(resources);
+       }
+}
+
+static void prv_upload_delete_cb(GUPnPServiceProxy *proxy,
+                                GUPnPServiceProxyAction *action,
+                                gpointer user_data)
+{
+       msu_async_cb_data_t *cb_data = user_data;
+
+       MSU_LOG_DEBUG("Enter");
+
+       (void) gupnp_service_proxy_end_action(cb_data->proxy, cb_data->action,
+                                             NULL, NULL);
+       (void) g_idle_add(msu_async_complete_task, cb_data);
+       g_cancellable_disconnect(cb_data->cancellable, cb_data->cancel_id);
+
+       MSU_LOG_DEBUG("Exit");
+}
+
+static void prv_msu_upload_job_delete(gpointer up_job)
+{
+       msu_device_upload_job_t *upload_job = up_job;
+
+       if (up_job) {
+               if (upload_job->remove_idle)
+                       (void) g_source_remove(upload_job->remove_idle);
+
+               g_free(upload_job);
+       }
+}
+
+static gboolean prv_remove_update_job(gpointer user_data)
+{
+       msu_device_upload_job_t *upload_job = user_data;
+       msu_device_upload_t *upload;
+
+       upload = g_hash_table_lookup(upload_job->device->uploads,
+                                    &upload_job->upload_id);
+       if (upload) {
+               g_hash_table_remove(upload_job->device->uploads,
+                                   &upload_job->upload_id);
+
+               MSU_LOG_DEBUG("Removing Upload Object: %d",
+                       upload_job->upload_id);
+       }
+
+       upload_job->remove_idle = 0;
+       g_hash_table_remove(upload_job->device->upload_jobs,
+                           &upload_job->upload_id);
+
+       return FALSE;
+}
+
+static void prv_generate_upload_update(msu_device_upload_job_t *upload_job,
+                                      msu_device_upload_t *upload)
+{
+       GVariant *args;
+
+       args = g_variant_new("(ustt)", upload_job->upload_id, upload->status,
+                            upload->bytes_uploaded, upload->bytes_to_upload);
+
+       MSU_LOG_DEBUG(
+               "Emitting: %s (%u %s %"G_GUINT64_FORMAT" %"G_GUINT64_FORMAT")"
+               " on %s",
+               MSU_INTERFACE_UPLOAD_UPDATE, upload_job->upload_id,
+               upload->status, upload->bytes_uploaded,
+               upload->bytes_to_upload, upload_job->device->path);
+
+       (void) g_dbus_connection_emit_signal(upload_job->device->connection,
+                                            NULL,
+                                            upload_job->device->path,
+                                            MSU_INTERFACE_MEDIA_DEVICE,
+                                            MSU_INTERFACE_UPLOAD_UPDATE,
+                                            args,
+                                            NULL);
+}
+
+static void prv_post_finished(SoupSession *session, SoupMessage *msg,
+                             gpointer user_data)
+{
+       msu_device_upload_job_t *upload_job = user_data;
+       msu_device_upload_t *upload;
+       gint *upload_id;
+
+       MSU_LOG_DEBUG("Enter");
+
+       MSU_LOG_DEBUG("Upload %u finished.  Code %u Message %s",
+                     upload_job->upload_id, msg->status_code,
+                     msg->reason_phrase);
+
+       /* This is clumsy but we need to distinguish between two cases:
+          1. We cancel because the process is exitting.
+          2. We cancel because a client has called CancelUpload.
+
+          We could use custom SOUP error messages to distinguish the cases
+          but device->shutting_down seemed less hacky.
+
+          We need this check as if we are shutting down it is
+          dangerous to manipulate uploads as we are being called from its
+          destructor.
+       */
+
+       if (upload_job->device->shutting_down) {
+               MSU_LOG_DEBUG("Device shutting down. Cancelling Upload");
+               goto on_error;
+       }
+
+       upload = g_hash_table_lookup(upload_job->device->uploads,
+                                    &upload_job->upload_id);
+       if (upload) {
+               upload_job->remove_idle =
+                       g_timeout_add(30000, prv_remove_update_job, user_data);
+
+               if (SOUP_STATUS_IS_SUCCESSFUL(msg->status_code)) {
+                       upload->status = MSU_UPLOAD_STATUS_COMPLETED;
+                       upload->bytes_uploaded = upload->bytes_to_upload;
+               } else if (msg->status_code == SOUP_STATUS_CANCELLED) {
+                       upload->status = MSU_UPLOAD_STATUS_CANCELLED;
+               } else {
+                       upload->status = MSU_UPLOAD_STATUS_ERROR;
+               }
+
+               MSU_LOG_DEBUG("Upload Status: %s", upload->status);
+
+               prv_generate_upload_update(upload_job, upload);
+
+               g_object_unref(upload->msg);
+               upload->msg = NULL;
+
+               g_object_unref(upload->soup_session);
+               upload->soup_session = NULL;
+
+               g_mapped_file_unref(upload->mapped_file);
+               upload->mapped_file = NULL;
+
+               upload_id = g_new(gint, 1);
+               *upload_id = upload_job->upload_id;
+
+               g_hash_table_insert(upload_job->device->upload_jobs, upload_id,
+                                   upload_job);
+
+               upload_job = NULL;
+       }
+
+on_error:
+
+       prv_msu_upload_job_delete(upload_job);
+
+       MSU_LOG_DEBUG("Exit");
+}
+
+static void prv_msu_device_upload_delete(gpointer up)
+{
+       msu_device_upload_t *upload = up;
+
+       MSU_LOG_DEBUG("Enter");
+
+       if (upload) {
+               if (upload->msg) {
+                       soup_session_cancel_message(upload->soup_session,
+                                                   upload->msg,
+                                                   SOUP_STATUS_CANCELLED);
+                       g_object_unref(upload->msg);
+               }
+
+               if (upload->soup_session)
+                       g_object_unref(upload->soup_session);
+
+               if (upload->mapped_file)
+                       g_mapped_file_unref(upload->mapped_file);
+
+               g_free(upload);
+       }
+
+       MSU_LOG_DEBUG("Exit");
+}
+
+static void prv_post_bytes_written(SoupMessage *msg, SoupBuffer *chunk,
+                                  gpointer user_data)
+{
+       msu_device_upload_t *upload = user_data;
+
+       upload->bytes_uploaded += chunk->length;
+       if (upload->bytes_uploaded > upload->bytes_to_upload)
+               upload->bytes_uploaded = upload->bytes_to_upload;
+}
+
+static msu_device_upload_t *prv_msu_device_upload_new(const gchar *file_path,
+                                                     const gchar *import_uri,
+                                                     const gchar *mime_type,
+                                                     GError **error)
+{
+       const char *body;
+       gsize body_length;
+       msu_device_upload_t *upload;
+
+       MSU_LOG_DEBUG("Enter");
+
+       upload = g_new0(msu_device_upload_t, 1);
+
+       upload->mapped_file = g_mapped_file_new(file_path, FALSE, NULL);
+       if (!upload->mapped_file) {
+               MSU_LOG_WARNING("Unable to map %s into memory", file_path);
+
+               *error = g_error_new(MSU_ERROR, MSU_ERROR_IO,
+                                    "Unable to map %s into memory",
+                                    file_path);
+               goto on_error;
+       }
+
+       body = g_mapped_file_get_contents(upload->mapped_file);
+       body_length = g_mapped_file_get_length(upload->mapped_file);
+
+       upload->soup_session = soup_session_async_new();
+       upload->msg = soup_message_new("POST", import_uri);
+
+       if (!upload->msg) {
+               MSU_LOG_WARNING("Invalid URI %s", import_uri);
+
+               *error = g_error_new(MSU_ERROR, MSU_ERROR_BAD_RESULT,
+                                    "Invalid URI %s", import_uri);
+               goto on_error;
+       }
+       upload->status = MSU_UPLOAD_STATUS_IN_PROGRESS;
+       upload->bytes_to_upload = body_length;
+
+       soup_message_headers_set_expectations(upload->msg->request_headers,
+                                             SOUP_EXPECTATION_CONTINUE);
+
+       soup_message_set_request(upload->msg, mime_type, SOUP_MEMORY_STATIC,
+                                body, body_length);
+       g_signal_connect(upload->msg, "wrote-body-data",
+                        G_CALLBACK(prv_post_bytes_written), upload);
+
+       MSU_LOG_DEBUG("Exit with Success");
+
+       return upload;
+
+on_error:
+
+       prv_msu_device_upload_delete(upload);
+
+       MSU_LOG_WARNING("Exit with Fail");
+
+       return NULL;
+}
+
+static void prv_create_container_cb(GUPnPServiceProxy *proxy,
+                GUPnPServiceProxyAction *action,
+                gpointer user_data)
+{
+       msu_async_cb_data_t *cb_data = user_data;
+       msu_async_create_container_t *cb_task_data;
+       GError *upnp_error = NULL;
+       gchar *result = NULL;
+       gchar *object_id = NULL;
+       gchar *object_path;
+
+       MSU_LOG_DEBUG("Enter");
+
+       cb_task_data = &cb_data->ut.create_container;
+
+       if (!gupnp_service_proxy_end_action(cb_data->proxy, cb_data->action,
+                                           &upnp_error,
+                                           "ObjectID", G_TYPE_STRING,
+                                           &object_id,
+                                           "Result", G_TYPE_STRING,
+                                           &result,
+                                           NULL)) {
+               MSU_LOG_ERROR("Create Object operation failed: %s",
+                                                       upnp_error->message);
+
+               cb_data->error = g_error_new(MSU_ERROR,
+                                            MSU_ERROR_OPERATION_FAILED,
+                                            "Create Object operation "
+                                            " failed: %s",
+                                            upnp_error->message);
+               goto on_error;
+       }
+
+       object_path = msu_path_from_id(cb_task_data->root_path, object_id);
+       cb_data->result = g_variant_ref_sink(g_variant_new_object_path(
+                                                               object_path));
+       g_free(object_path);
+
+on_error:
+
+       (void) g_idle_add(msu_async_complete_task, cb_data);
+       g_cancellable_disconnect(cb_data->cancellable, cb_data->cancel_id);
+
+       if (object_id)
+               g_free(object_id);
+
+       if (result)
+               g_free(result);
+
+       if (upnp_error)
+               g_error_free(upnp_error);
+
+       MSU_LOG_DEBUG("Exit");
+}
+
+static void prv_create_object_cb(GUPnPServiceProxy *proxy,
+                                GUPnPServiceProxyAction *action,
+                                gpointer user_data)
+{
+       gchar *result = NULL;
        GUPnPDIDLLiteParser *parser = NULL;
        GError *upnp_error = NULL;
        msu_async_cb_data_t *cb_data = user_data;
-       msu_async_bas_t *cb_task_data = &cb_data->ut.bas;
+       msu_async_upload_t *cb_task_data = &cb_data->ut.upload;
+       gchar *import_uri = NULL;
+       gchar *object_id = NULL;
+       gboolean delete_needed = FALSE;
+       GVariant *out_params[2];
+       gchar *object_path;
+       msu_device_upload_t *upload;
+       gint *upload_id;
+       msu_device_upload_job_t *upload_job;
 
        MSU_LOG_DEBUG("Enter");
 
        if (!gupnp_service_proxy_end_action(cb_data->proxy, cb_data->action,
                                            &upnp_error,
+                                           "ObjectID", G_TYPE_STRING,
+                                           &object_id,
                                            "Result", G_TYPE_STRING,
                                            &result,
-                                           "TotalMatches", G_TYPE_INT,
-                                           &cb_task_data->max_count,
                                            NULL)) {
+               MSU_LOG_WARNING("Create Object operation failed: %s",
+                               upnp_error->message);
 
-               MSU_LOG_WARNING("Search operation failed %s",
-                             upnp_error->message);
+               cb_data->error = g_error_new(MSU_ERROR,
+                                            MSU_ERROR_OPERATION_FAILED,
+                                            "Create Object operation "
+                                            " failed: %s",
+                                            upnp_error->message);
+               goto on_error;
+       }
+
+       delete_needed = TRUE;
+       parser = gupnp_didl_lite_parser_new();
+
+       g_signal_connect(parser, "object-available" ,
+                        G_CALLBACK(prv_extract_import_uri), &import_uri);
+
+       MSU_LOG_DEBUG("Create Object Result: %s", result);
+
+       if (!gupnp_didl_lite_parser_parse_didl(parser, result, &upnp_error)
+               && upnp_error->code != GUPNP_XML_ERROR_EMPTY_NODE) {
+
+               MSU_LOG_WARNING("Unable to parse results of CreateObject: %s",
+                               upnp_error->message);
 
                cb_data->error = g_error_new(MSU_ERROR,
                                             MSU_ERROR_OPERATION_FAILED,
-                                            "Search operation failed: %s",
+                                            "Unable to parse results of "
+                                            "CreateObject: %s",
                                             upnp_error->message);
                goto on_error;
        }
 
-       parser = gupnp_didl_lite_parser_new();
+       if (!import_uri) {
+               MSU_LOG_WARNING("Missing Import URI");
+
+               cb_data->error = g_error_new(MSU_ERROR,
+                                            MSU_ERROR_OPERATION_FAILED,
+                                            "Missing Import URI");
+               goto on_error;
+       }
+
+       MSU_LOG_DEBUG("Import URI %s", import_uri);
+
+       upload = prv_msu_device_upload_new(cb_data->task->ut.upload.file_path,
+                                          import_uri, cb_task_data->mime_type,
+                                          &cb_data->error);
+       if (!upload)
+               goto on_error;
+
+       upload_job = g_new0(msu_device_upload_job_t, 1);
+       upload_job->device = cb_task_data->device;
+       upload_job->upload_id = (gint) cb_task_data->device->upload_id;
+
+       soup_session_queue_message(upload->soup_session, upload->msg,
+                                  prv_post_finished, upload_job);
+       g_object_ref(upload->msg);
+
+       upload_id = g_new(gint, 1);
+       *upload_id = upload_job->upload_id;
+       g_hash_table_insert(cb_task_data->device->uploads, upload_id, upload);
+
+       object_path = msu_path_from_id(cb_task_data->root_path, object_id);
 
-       cb_task_data->vbs = g_ptr_array_new_with_free_func(
-               prv_msu_device_object_builder_delete);
+       MSU_LOG_DEBUG("Upload ID %u", *upload_id);
+       MSU_LOG_DEBUG("Object ID %s", object_id);
+       MSU_LOG_DEBUG("Object Path %s", object_path);
 
-       g_signal_connect(parser, "object-available" ,
-                        G_CALLBACK(prv_found_target), cb_data);
+       out_params[0] = g_variant_new_uint32(*upload_id);
+       out_params[1] = g_variant_new_object_path(object_path);
+       cb_data->result = g_variant_ref_sink(g_variant_new_tuple(out_params,
+                                                                2));
 
-       MSU_LOG_DEBUG("Server Search result: %s", result);
+       ++cb_task_data->device->upload_id;
+       if (cb_task_data->device->upload_id > G_MAXINT)
+               cb_task_data->device->upload_id = 0;
 
-       if (!gupnp_didl_lite_parser_parse_didl(parser, result, &upnp_error)
-               && upnp_error->code != GUPNP_XML_ERROR_EMPTY_NODE) {
-               MSU_LOG_WARNING("Unable to parse results of search: %s",
-                             upnp_error->message);
+       g_free(object_path);
 
-               cb_data->error = g_error_new(MSU_ERROR,
-                                            MSU_ERROR_OPERATION_FAILED,
-                                            "Unable to parse results of "
-                                            "search: %s", upnp_error->message);
-               goto on_error;
-       }
+on_error:
 
-       if (cb_task_data->need_child_count) {
-               MSU_LOG_DEBUG("Need to retrieve child count");
+       if (cb_data->error && delete_needed) {
+               MSU_LOG_WARNING("Upload failed deleting created object "
+                               "with id %s", object_id);
 
-               if (cb_data->task->multiple_retvals)
-                       cb_task_data->get_children_cb =
-                               prv_get_search_ex_result;
-               else
-                       cb_task_data->get_children_cb = prv_get_children_result;
-               prv_retrieve_child_count_for_list(cb_data);
-               goto no_complete;
+               cb_data->action = gupnp_service_proxy_begin_action(
+                       cb_data->proxy, "DestroyObject", prv_upload_delete_cb,
+                       cb_data, "ObjectID", G_TYPE_STRING,
+                       object_id, NULL);
        } else {
-               if (cb_data->task->multiple_retvals)
-                       prv_get_search_ex_result(cb_data);
-               else
-                       prv_get_children_result(cb_data);
+               (void) g_idle_add(msu_async_complete_task, cb_data);
+               g_cancellable_disconnect(cb_data->cancellable,
+                                        cb_data->cancel_id);
        }
 
-on_error:
-
-       (void) g_idle_add(msu_async_complete_task, cb_data);
-       g_cancellable_disconnect(cb_data->cancellable, cb_data->cancel_id);
-
-no_complete:
+       g_free(object_id);
+       g_free(import_uri);
 
        if (parser)
                g_object_unref(parser);
@@ -1530,30 +3040,35 @@ no_complete:
        MSU_LOG_DEBUG("Exit");
 }
 
-void msu_device_search(msu_device_t *device,  msu_task_t *task,
-                      msu_async_cb_data_t *cb_data,
-                      const gchar *upnp_filter, const gchar *upnp_query,
-                      const gchar *sort_by, GCancellable *cancellable)
+void msu_device_upload(msu_device_t *device, msu_client_t *client,
+                      msu_task_t *task, const gchar *parent_id,
+                      msu_async_cb_data_t *cb_data, GCancellable *cancellable)
 {
        msu_device_context_t *context;
+       gchar *didl;
+       msu_async_upload_t *cb_task_data;
 
        MSU_LOG_DEBUG("Enter");
+       MSU_LOG_DEBUG("Uploading file to %s", parent_id);
+
+       context = msu_device_get_context(device, client);
+       cb_task_data = &cb_data->ut.upload;
+
+       didl = prv_create_upload_didl(parent_id, task,
+                                     cb_task_data->object_class,
+                                     cb_task_data->mime_type);
 
-       context = msu_device_get_context(device);
+       MSU_LOG_DEBUG("DIDL: %s", didl);
 
        cb_data->action = gupnp_service_proxy_begin_action(
-               context->service_proxy, "Search",
-               prv_search_cb,
-               cb_data,
-               "ContainerID", G_TYPE_STRING, cb_data->id,
-               "SearchCriteria", G_TYPE_STRING, upnp_query,
-               "Filter", G_TYPE_STRING, upnp_filter,
-               "StartingIndex", G_TYPE_INT, task->ut.search.start,
-               "RequestedCount", G_TYPE_INT, task->ut.search.count,
-               "SortCriteria", G_TYPE_STRING, sort_by,
+               context->service_proxy, "CreateObject",
+               prv_create_object_cb, cb_data,
+               "ContainerID", G_TYPE_STRING, parent_id,
+               "Elements", G_TYPE_STRING, didl,
                NULL);
 
        cb_data->proxy = context->service_proxy;
+       cb_task_data->device = device;
 
        cb_data->cancel_id =
                g_cancellable_connect(cancellable,
@@ -1561,369 +3076,482 @@ void msu_device_search(msu_device_t *device,  msu_task_t *task,
                                      cb_data, NULL);
        cb_data->cancellable = cancellable;
 
+       g_free(didl);
+
        MSU_LOG_DEBUG("Exit");
 }
 
-static void prv_get_resource(GUPnPDIDLLiteParser *parser,
-                            GUPnPDIDLLiteObject *object,
-                            gpointer user_data)
+gboolean msu_device_get_upload_status(msu_device_t *device,
+                                     msu_task_t *task, GError **error)
 {
-       msu_async_cb_data_t *cb_data = user_data;
-       msu_task_t *task = cb_data->task;
-       msu_task_get_resource_t *task_data = &task->ut.resource;
-       msu_async_get_all_t *cb_task_data = &cb_data->ut.get_all;
+       msu_device_upload_t *upload;
+       gboolean retval = FALSE;
+       GVariant *out_params[3];
+       guint upload_id;
 
        MSU_LOG_DEBUG("Enter");
 
-       msu_props_add_resource(cb_task_data->vb, object,
-                              cb_task_data->filter_mask,
-                              task_data->protocol_info);
+       upload_id = task->ut.upload_action.upload_id;
+
+       upload = g_hash_table_lookup(device->uploads, &upload_id);
+       if (!upload) {
+               *error = g_error_new(MSU_ERROR, MSU_ERROR_OBJECT_NOT_FOUND,
+                                    "Unknown Upload ID %u ", upload_id);
+               goto on_error;
+       }
+
+       out_params[0] = g_variant_new_string(upload->status);
+       out_params[1] = g_variant_new_uint64(upload->bytes_uploaded);
+       out_params[2] = g_variant_new_uint64(upload->bytes_to_upload);
+
+       MSU_LOG_DEBUG("Upload ( %s %"G_GUINT64_FORMAT" %"G_GUINT64_FORMAT" )",
+                     upload->status, upload->bytes_uploaded,
+                     upload->bytes_to_upload);
+
+       task->result = g_variant_ref_sink(g_variant_new_tuple(out_params, 3));
+
+       retval = TRUE;
+
+on_error:
+
+       MSU_LOG_DEBUG("Exit");
+
+       return retval;
 }
 
-void msu_device_get_resource(msu_device_t *device,  msu_task_t *task,
-                            msu_async_cb_data_t *cb_data,
-                            const gchar *upnp_filter,
-                            GCancellable *cancellable)
+void msu_device_get_upload_ids(msu_device_t *device, msu_task_t *task)
 {
-       msu_async_get_all_t *cb_task_data;
-       msu_device_context_t *context;
-
-       context = msu_device_get_context(device);
-       cb_task_data = &cb_data->ut.get_all;
+       GVariantBuilder vb;
+       GHashTableIter iter;
+       gpointer key;
 
-       cb_task_data->vb = g_variant_builder_new(G_VARIANT_TYPE("a{sv}"));
-       cb_task_data->prop_func = G_CALLBACK(prv_get_resource);
+       MSU_LOG_DEBUG("Enter");
 
-       cb_data->action = gupnp_service_proxy_begin_action(
-               context->service_proxy, "Browse",
-               prv_get_all_ms2spec_props_cb, cb_data,
-               "ObjectID", G_TYPE_STRING, cb_data->id,
-               "BrowseFlag", G_TYPE_STRING, "BrowseMetadata",
-               "Filter", G_TYPE_STRING, upnp_filter,
-               "StartingIndex", G_TYPE_INT, 0,
-               "RequestedCount", G_TYPE_INT, 0,
-               "SortCriteria", G_TYPE_STRING,
-               "", NULL);
+       g_variant_builder_init(&vb, G_VARIANT_TYPE("au"));
 
-       cb_data->proxy = context->service_proxy;
+       g_hash_table_iter_init(&iter, device->uploads);
+       while (g_hash_table_iter_next(&iter, &key, NULL))
+               g_variant_builder_add(&vb, "u", (guint32) (*((gint *) key)));
 
-       cb_data->cancel_id =
-               g_cancellable_connect(cancellable,
-                                     G_CALLBACK(msu_async_task_cancelled),
-                                     cb_data, NULL);
-       cb_data->cancellable = cancellable;
+       task->result = g_variant_ref_sink(g_variant_builder_end(&vb));
 
        MSU_LOG_DEBUG("Exit");
 }
 
-static gchar *prv_create_upload_didl(const gchar *parent_id, msu_task_t *task,
-                                    const gchar *object_class,
-                                    const gchar *mime_type)
+gboolean msu_device_cancel_upload(msu_device_t *device, msu_task_t *task,
+                                 GError **error)
 {
-       GUPnPDIDLLiteWriter *writer;
-       GUPnPDIDLLiteObject *item;
-       gchar *retval;
-       GUPnPProtocolInfo *protocol_info;
-       GUPnPDIDLLiteResource *res;
+       msu_device_upload_t *upload;
+       gboolean retval = FALSE;
+       guint upload_id;
 
-       writer = gupnp_didl_lite_writer_new(NULL);
-       item = GUPNP_DIDL_LITE_OBJECT(gupnp_didl_lite_writer_add_item(writer));
+       MSU_LOG_DEBUG("Enter");
 
-       gupnp_didl_lite_object_set_id(item, "");
-       gupnp_didl_lite_object_set_title(item, task->ut.upload.display_name);
-       gupnp_didl_lite_object_set_parent_id(item, parent_id);
-       gupnp_didl_lite_object_set_upnp_class(item, object_class);
-       gupnp_didl_lite_object_set_restricted(item, FALSE);
+       upload_id = task->ut.upload_action.upload_id;
 
-       protocol_info = gupnp_protocol_info_new();
-       gupnp_protocol_info_set_mime_type(protocol_info, mime_type);
-       gupnp_protocol_info_set_protocol(protocol_info, "*");
-       gupnp_protocol_info_set_network(protocol_info, "*");
+       upload = g_hash_table_lookup(device->uploads, &upload_id);
+       if (!upload) {
+               *error = g_error_new(MSU_ERROR, MSU_ERROR_OBJECT_NOT_FOUND,
+                                    "Unknown Upload ID %u ", upload_id);
+               goto on_error;
+       }
 
-       res = gupnp_didl_lite_object_add_resource(item);
-       gupnp_didl_lite_resource_set_protocol_info(res, protocol_info);
+       if (upload->msg) {
+               soup_session_cancel_message(upload->soup_session, upload->msg,
+                                           SOUP_STATUS_CANCELLED);
+               MSU_LOG_DEBUG("Cancelling Upload %u ", upload_id);
+       }
 
-       /* TODO: Need to compute DLNA Profile */
+       retval = TRUE;
 
-       retval = gupnp_didl_lite_writer_get_string(writer);
+on_error:
 
-       g_object_unref(res);
-       g_object_unref(protocol_info);
-       g_object_unref(item);
-       g_object_unref(writer);
+       MSU_LOG_DEBUG("Exit");
 
        return retval;
 }
 
-static void prv_extract_import_uri(GUPnPDIDLLiteParser *parser,
-                                  GUPnPDIDLLiteObject *object,
-                                  gpointer user_data)
+static void prv_destroy_object_cb(GUPnPServiceProxy *proxy,
+                                 GUPnPServiceProxyAction *action,
+                                 gpointer user_data)
 {
-       gchar **import_uri = user_data;
-       GList *resources;
-       GList *ptr;
-       GUPnPDIDLLiteResource *res;
-       const gchar *uri;
+       GError *upnp_error = NULL;
+       msu_async_cb_data_t *cb_data = user_data;
 
-       if (!*import_uri) {
-               ptr = resources = gupnp_didl_lite_object_get_resources(object);
-               while (ptr) {
-                       res = ptr->data;
-                       if (!*import_uri) {
-                               uri = gupnp_didl_lite_resource_get_import_uri(
-                                       res);
-                               if (uri)
-                                       *import_uri = g_strdup(uri);
-                       }
-                       g_object_unref(res);
-                       ptr = ptr->next;
-               }
+       MSU_LOG_DEBUG("Enter");
 
-               g_list_free(resources);
+       if (!gupnp_service_proxy_end_action(cb_data->proxy, cb_data->action,
+                                           &upnp_error,
+                                           NULL)) {
+               MSU_LOG_WARNING("Destroy Object operation failed: %s",
+                               upnp_error->message);
+
+               cb_data->error = g_error_new(MSU_ERROR,
+                                            MSU_ERROR_OPERATION_FAILED,
+                                            "Destroy Object operation "
+                                            " failed: %s",
+                                            upnp_error->message);
        }
+
+       (void) g_idle_add(msu_async_complete_task, cb_data);
+       g_cancellable_disconnect(cb_data->cancellable, cb_data->cancel_id);
+
+       if (upnp_error)
+               g_error_free(upnp_error);
+
+       MSU_LOG_DEBUG("Exit");
+}
+
+void msu_device_delete_object(msu_device_t *device, msu_client_t *client,
+                             msu_task_t *task,
+                             msu_async_cb_data_t *cb_data,
+                             GCancellable *cancellable)
+{
+       msu_device_context_t *context;
+
+       MSU_LOG_DEBUG("Enter");
+
+       context = msu_device_get_context(device, client);
+
+       cb_data->action = gupnp_service_proxy_begin_action(
+                               context->service_proxy, "DestroyObject",
+                               prv_destroy_object_cb, cb_data,
+                               "ObjectID", G_TYPE_STRING, cb_data->id,
+                               NULL);
+
+       cb_data->proxy = context->service_proxy;
+
+       cb_data->cancel_id = g_cancellable_connect(cancellable,
+                                       G_CALLBACK(msu_async_task_cancelled),
+                                       cb_data, NULL);
+       cb_data->cancellable = cancellable;
+
+       MSU_LOG_DEBUG("Exit");
+}
+
+void msu_device_create_container(msu_device_t *device, msu_client_t *client,
+                                msu_task_t *task,
+                                const gchar *parent_id,
+                                msu_async_cb_data_t *cb_data,
+                                GCancellable *cancellable)
+{
+       msu_device_context_t *context;
+       gchar *didl;
+
+       MSU_LOG_DEBUG("Enter");
+
+       context = msu_device_get_context(device, client);
+
+       didl = prv_create_new_container_didl(parent_id, task);
+
+       MSU_LOG_DEBUG("DIDL: %s", didl);
+
+       cb_data->action = gupnp_service_proxy_begin_action(
+                               context->service_proxy, "CreateObject",
+                               prv_create_container_cb, cb_data,
+                               "ContainerID", G_TYPE_STRING, parent_id,
+                               "Elements", G_TYPE_STRING, didl,
+                               NULL);
+
+       cb_data->proxy = context->service_proxy;
+
+       cb_data->cancel_id =
+               g_cancellable_connect(cancellable,
+                                     G_CALLBACK(msu_async_task_cancelled),
+                                     cb_data, NULL);
+       cb_data->cancellable = cancellable;
+
+       g_free(didl);
+
+       MSU_LOG_DEBUG("Exit");
 }
 
-static void prv_upload_delete_cb(GUPnPServiceProxy *proxy,
-                                GUPnPServiceProxyAction *action,
-                                gpointer user_data)
+static void prv_update_object_update_cb(GUPnPServiceProxy *proxy,
+                                       GUPnPServiceProxyAction *action,
+                                       gpointer user_data)
 {
+       GError *upnp_error = NULL;
        msu_async_cb_data_t *cb_data = user_data;
 
        MSU_LOG_DEBUG("Enter");
 
-       (void) gupnp_service_proxy_end_action(cb_data->proxy, cb_data->action,
-                                             NULL, NULL);
+       if (!gupnp_service_proxy_end_action(cb_data->proxy, cb_data->action,
+                                           &upnp_error,
+                                           NULL)) {
+               MSU_LOG_WARNING("Update Object operation failed: %s",
+                               upnp_error->message);
+
+               cb_data->error = g_error_new(MSU_ERROR,
+                                            MSU_ERROR_OPERATION_FAILED,
+                                            "Update Object operation "
+                                            " failed: %s",
+                                            upnp_error->message);
+       }
+
        (void) g_idle_add(msu_async_complete_task, cb_data);
        g_cancellable_disconnect(cb_data->cancellable, cb_data->cancel_id);
 
+       if (upnp_error)
+               g_error_free(upnp_error);
+
        MSU_LOG_DEBUG("Exit");
 }
 
-static void prv_post_finished(SoupSession *session, SoupMessage *msg,
-                             gpointer user_data)
+static gchar *prv_get_current_xml_fragment(GUPnPDIDLLiteObject *object,
+                                          guint32 mask)
 {
-       msu_device_upload_job_t *upload_job = user_data;
+       gchar *retval = NULL;
+
+       if (mask & MSU_UPNP_MASK_PROP_DISPLAY_NAME)
+               retval = gupnp_didl_lite_object_get_title_xml_string(object);
+       else if (mask & MSU_UPNP_MASK_PROP_ALBUM)
+               retval = gupnp_didl_lite_object_get_album_xml_string(object);
+       else if (mask & MSU_UPNP_MASK_PROP_DATE)
+               retval = gupnp_didl_lite_object_get_date_xml_string(object);
+       else if (mask & MSU_UPNP_MASK_PROP_TYPE)
+               retval = gupnp_didl_lite_object_get_upnp_class_xml_string(
+                       object);
+       else if (mask & MSU_UPNP_MASK_PROP_TRACK_NUMBER)
+               retval = gupnp_didl_lite_object_get_track_number_xml_string(
+                       object);
+       else if (mask & MSU_UPNP_MASK_PROP_ARTISTS)
+               retval = gupnp_didl_lite_object_get_artists_xml_string(object);
 
-       MSU_LOG_DEBUG("Enter");
+       return retval;
+}
 
-       MSU_LOG_DEBUG("Upload %u finished.  Code %u Message %s",
-                     upload_job->upload_id, msg->status_code,
-                     msg->reason_phrase);
+static gchar *prv_get_new_xml_fragment(GUPnPDIDLLiteObject *object,
+                                      guint32 mask,
+                                      GVariant *value)
+{
+       GUPnPDIDLLiteContributor *artist;
+       const gchar *artist_name;
+       const gchar *upnp_class;
+       GVariantIter viter;
+       gchar *retval = NULL;
+
+       if (mask & MSU_UPNP_MASK_PROP_DISPLAY_NAME) {
+               gupnp_didl_lite_object_set_title(object,
+                       g_variant_get_string(value, NULL));
+
+               retval = gupnp_didl_lite_object_get_title_xml_string(object);
+       } else if (mask & MSU_UPNP_MASK_PROP_ALBUM) {
+               gupnp_didl_lite_object_set_album(object,
+                       g_variant_get_string(value, NULL));
+
+               retval = gupnp_didl_lite_object_get_album_xml_string(object);
+       } else if (mask & MSU_UPNP_MASK_PROP_DATE) {
+               gupnp_didl_lite_object_set_date(object,
+                       g_variant_get_string(value, NULL));
+
+               retval = gupnp_didl_lite_object_get_date_xml_string(object);
+       } else if (mask & MSU_UPNP_MASK_PROP_TYPE) {
+               upnp_class = msu_props_media_spec_to_upnp_class(
+                                       g_variant_get_string(value, NULL));
+
+               gupnp_didl_lite_object_set_upnp_class(object, upnp_class);
+
+               retval = gupnp_didl_lite_object_get_upnp_class_xml_string(
+                       object);
+       } else if (mask & MSU_UPNP_MASK_PROP_TRACK_NUMBER) {
+               gupnp_didl_lite_object_set_track_number(object,
+                                       g_variant_get_int32(value));
+
+               retval = gupnp_didl_lite_object_get_track_number_xml_string(
+                       object);
+       } else if (mask & MSU_UPNP_MASK_PROP_ARTISTS) {
+               gupnp_didl_lite_object_unset_artists(object);
+
+               (void) g_variant_iter_init(&viter, value);
+
+               while (g_variant_iter_next(&viter, "&s", &artist_name)) {
+                       artist = gupnp_didl_lite_object_add_artist(object);
+
+                       gupnp_didl_lite_contributor_set_name(artist,
+                                                            artist_name);
+               }
 
-       if (msg->status_code != SOUP_STATUS_CANCELLED)
-               g_hash_table_remove(upload_job->device->uploads,
-                                   &upload_job->upload_id);
-       g_free(upload_job);
+               retval = gupnp_didl_lite_object_get_artists_xml_string(object);
+       }
 
-       MSU_LOG_DEBUG("Exit");
+       return retval;
 }
 
-static void prv_msu_device_upload_delete(gpointer up)
+static void prv_get_xml_fragments(GUPnPDIDLLiteParser *parser,
+                                 GUPnPDIDLLiteObject *object,
+                                 gpointer user_data)
 {
-       msu_device_upload_t *upload = up;
+       GString *current_str;
+       GString *new_str;
+       gchar *frag1;
+       gchar *frag2;
+       GVariantIter viter;
+       const gchar *prop;
+       GVariant *value;
+       msu_prop_map_t *prop_map;
+       GUPnPDIDLLiteWriter *writer;
+       GUPnPDIDLLiteObject *scratch_object;
+       gboolean first = TRUE;
+       msu_async_cb_data_t *cb_data = user_data;
+       msu_async_update_t *cb_task_data = &cb_data->ut.update;
+       msu_task_t *task = cb_data->task;
+       msu_task_update_t *task_data = &task->ut.update;
 
        MSU_LOG_DEBUG("Enter");
 
-       if (upload) {
-               if (upload->msg) {
-                       soup_session_cancel_message(upload->soup_session,
-                                                   upload->msg,
-                                                   SOUP_STATUS_CANCELLED);
-                       g_object_unref(upload->msg);
-               }
+       current_str = g_string_new("");
+       new_str = g_string_new("");
 
-               if (upload->soup_session)
-                       g_object_unref(upload->soup_session);
+       writer = gupnp_didl_lite_writer_new(NULL);
+       if (GUPNP_IS_DIDL_LITE_CONTAINER(object))
+               scratch_object = GUPNP_DIDL_LITE_OBJECT(
+                       gupnp_didl_lite_writer_add_container(writer));
+       else
+               scratch_object = GUPNP_DIDL_LITE_OBJECT(
+                       gupnp_didl_lite_writer_add_item(writer));
 
-               if (upload->mapped_file)
-                       g_mapped_file_unref(upload->mapped_file);
+       (void) g_variant_iter_init(&viter, task_data->to_add_update);
 
-               g_free(upload);
-       }
+       while (g_variant_iter_next(&viter, "{&sv}", &prop, &value)) {
 
-       MSU_LOG_DEBUG("Exit");
-}
+               MSU_LOG_DEBUG("to_add_update = %s", prop);
 
-static msu_device_upload_t *prv_msu_device_upload_new(const gchar *file_path,
-                                                     const gchar *import_uri,
-                                                     const gchar *mime_type,
-                                                     GError **error)
-{
-       const char *body;
-       gsize body_length;
-       msu_device_upload_t *upload;
+               prop_map = g_hash_table_lookup(cb_task_data->map, prop);
 
-       MSU_LOG_DEBUG("Enter");
+               frag1 = prv_get_current_xml_fragment(object, prop_map->type);
+               frag2 = prv_get_new_xml_fragment(scratch_object, prop_map->type,
+                                                value);
 
-       upload = g_new0(msu_device_upload_t, 1);
+               if (!frag2) {
+                       MSU_LOG_DEBUG("Unable to set %s.  Skipping", prop);
 
-       upload->mapped_file = g_mapped_file_new(file_path, FALSE, NULL);
-       if (!upload->mapped_file) {
-               MSU_LOG_WARNING("Unable to map %s into memory", file_path);
+                       g_free(frag1);
+                       continue;
+               }
 
-               *error = g_error_new(MSU_ERROR, MSU_ERROR_IO,
-                                    "Unable to map %s into memory",
-                                    file_path);
-               goto on_error;
-       }
+               if (!first) {
+                       g_string_append(current_str, ",");
+                       g_string_append(new_str, ",");
+               } else {
+                       first = FALSE;
+               }
 
-       body = g_mapped_file_get_contents(upload->mapped_file);
-       body_length = g_mapped_file_get_length(upload->mapped_file);
+               if (frag1) {
+                       g_string_append(current_str, (const gchar *) frag1);
+                       g_free(frag1);
+               }
 
-       upload->soup_session = soup_session_async_new();
-       upload->msg = soup_message_new("POST", import_uri);
+               g_string_append(new_str, (const gchar *) frag2);
+               g_free(frag2);
+       }
 
-       if (!upload->msg) {
-               MSU_LOG_WARNING("Invalid URI %s", import_uri);
+       (void) g_variant_iter_init(&viter, task_data->to_delete);
 
-               *error = g_error_new(MSU_ERROR, MSU_ERROR_BAD_RESULT,
-                                    "Invalid URI %s", import_uri);
-               goto on_error;
-       }
+       while (g_variant_iter_next(&viter, "&s", &prop)) {
+               MSU_LOG_DEBUG("to_delete = %s", prop);
 
-       soup_message_headers_set_expectations(upload->msg->request_headers,
-                                             SOUP_EXPECTATION_CONTINUE);
+               prop_map = g_hash_table_lookup(cb_task_data->map, prop);
 
-       soup_message_set_request(upload->msg, mime_type, SOUP_MEMORY_STATIC,
-                                body, body_length);
+               frag1 = prv_get_current_xml_fragment(object, prop_map->type);
+               if (!frag1)
+                       continue;
 
-       MSU_LOG_DEBUG("Exit with Success");
+               if (!first)
+                       g_string_append(current_str, ",");
+               else
+                       first = FALSE;
 
-       return upload;
+               g_string_append(current_str, (const gchar *) frag1);
+               g_free(frag1);
+       }
 
-on_error:
+       cb_task_data->current_tag_value = g_string_free(current_str, FALSE);
+       MSU_LOG_DEBUG("current_tag_value = %s",
+                     cb_task_data->current_tag_value);
 
-       prv_msu_device_upload_delete(upload);
+       cb_task_data->new_tag_value = g_string_free(new_str, FALSE);
+       MSU_LOG_DEBUG("new_tag_value = %s", cb_task_data->new_tag_value);
 
-       MSU_LOG_WARNING("Exit with Fail");
+       g_object_unref(scratch_object);
+       g_object_unref(writer);
 
-       return NULL;
+       MSU_LOG_DEBUG("Exit");
 }
 
-static void prv_create_object_cb(GUPnPServiceProxy *proxy,
-                                GUPnPServiceProxyAction *action,
-                                gpointer user_data)
+static void prv_update_object_browse_cb(GUPnPServiceProxy *proxy,
+                                       GUPnPServiceProxyAction *action,
+                                       gpointer user_data)
 {
-       gchar *result = NULL;
-       GUPnPDIDLLiteParser *parser = NULL;
        GError *upnp_error = NULL;
        msu_async_cb_data_t *cb_data = user_data;
-       msu_async_upload_t *cb_task_data = &cb_data->ut.upload;
-       gchar *import_uri = NULL;
-       gchar *object_id = NULL;
-       gboolean delete_needed = FALSE;
-       GVariant *out_params[2];
-       gchar *object_path;
-       msu_device_upload_t *upload;
-       gint *upload_id;
-       msu_device_upload_job_t *upload_job;
+       msu_async_update_t *cb_task_data = &cb_data->ut.update;
+       GUPnPDIDLLiteParser *parser = NULL;
+       gchar *result = NULL;
 
        MSU_LOG_DEBUG("Enter");
 
        if (!gupnp_service_proxy_end_action(cb_data->proxy, cb_data->action,
                                            &upnp_error,
-                                           "ObjectID", G_TYPE_STRING,
-                                           &object_id,
                                            "Result", G_TYPE_STRING,
-                                           &result,
-                                           NULL)) {
-               MSU_LOG_WARNING("Create Object operation failed: %s",
+                                           &result, NULL)) {
+               MSU_LOG_WARNING("Browse Object operation failed: %s",
                                upnp_error->message);
 
                cb_data->error = g_error_new(MSU_ERROR,
                                             MSU_ERROR_OPERATION_FAILED,
-                                            "Create Object operation "
-                                            " failed: %s",
+                                            "Browse operation failed: %s",
                                             upnp_error->message);
                goto on_error;
        }
 
-       delete_needed = TRUE;
-       parser = gupnp_didl_lite_parser_new();
-
-       g_signal_connect(parser, "object-available" ,
-                        G_CALLBACK(prv_extract_import_uri), &import_uri);
+       MSU_LOG_DEBUG("msu_device_update_ex_object result: %s", result);
 
-       MSU_LOG_DEBUG("Create Object Result: %s", result);
+       parser = gupnp_didl_lite_parser_new();
 
-       if (!gupnp_didl_lite_parser_parse_didl(parser, result, &upnp_error)
-               && upnp_error->code != GUPNP_XML_ERROR_EMPTY_NODE) {
+       g_signal_connect(parser, "object-available",
+                        G_CALLBACK(prv_get_xml_fragments),
+                        cb_data);
 
-               MSU_LOG_WARNING("Unable to parse results of CreateObject: %s",
-                               upnp_error->message);
+       if (!gupnp_didl_lite_parser_parse_didl(parser, result, &upnp_error)) {
+               if (upnp_error->code == GUPNP_XML_ERROR_EMPTY_NODE) {
+                       MSU_LOG_WARNING("Property not defined for object");
 
-               cb_data->error = g_error_new(MSU_ERROR,
-                                            MSU_ERROR_OPERATION_FAILED,
-                                            "Unable to parse results of "
-                                            "CreateObject: %s",
-                                            upnp_error->message);
-               goto on_error;
-       }
+                       cb_data->error =
+                               g_error_new(MSU_ERROR,
+                                           MSU_ERROR_UNKNOWN_PROPERTY,
+                                           "Property not defined for object");
+               } else {
+                       MSU_LOG_WARNING("Unable to parse results of browse: %s",
+                                     upnp_error->message);
 
-       if (!import_uri) {
-               MSU_LOG_WARNING("Missing Import URI");
+                       cb_data->error =
+                               g_error_new(MSU_ERROR,
+                                           MSU_ERROR_OPERATION_FAILED,
+                                           "Unable to parse results of "
+                                           "browse: %s",
+                                           upnp_error->message);
+               }
 
-               cb_data->error = g_error_new(MSU_ERROR,
-                                            MSU_ERROR_OPERATION_FAILED,
-                                            "Missing Import URI");
                goto on_error;
        }
 
-       MSU_LOG_DEBUG("Import URI %s", import_uri);
-
-       upload = prv_msu_device_upload_new(cb_data->task->ut.upload.file_path,
-                                          import_uri, cb_task_data->mime_type,
-                                          &cb_data->error);
-       if (!upload)
-               goto on_error;
-
-       upload_job = g_new(msu_device_upload_job_t, 1);
-       upload_job->device = cb_task_data->device;
-       upload_job->upload_id = (gint) cb_task_data->device->upload_id;
-
-       soup_session_queue_message(upload->soup_session, upload->msg,
-                                  prv_post_finished, upload_job);
-       g_object_ref(upload->msg);
-
-       upload_id = g_new(gint, 1);
-       *upload_id = upload_job->upload_id;
-       g_hash_table_insert(cb_task_data->device->uploads, upload_id, upload);
-
-       object_path = msu_path_from_id(cb_task_data->root_path, object_id);
-
-       MSU_LOG_DEBUG("Upload ID %u", *upload_id);
-       MSU_LOG_DEBUG("Object ID %s", object_id);
-       MSU_LOG_DEBUG("Object Path %s", object_path);
-
-       out_params[0] = g_variant_new_uint32(*upload_id);
-       out_params[1] = g_variant_new_object_path(object_path);
-       cb_data->result = g_variant_ref_sink(g_variant_new_tuple(out_params,
-                                                                2));
-
-       ++cb_task_data->device->upload_id;
-       if (cb_task_data->device->upload_id > G_MAXINT)
-               cb_task_data->device->upload_id = 0;
+       cb_data->action = gupnp_service_proxy_begin_action(
+               cb_data->proxy, "UpdateObject",
+               prv_update_object_update_cb, cb_data,
+               "ObjectID", G_TYPE_STRING, cb_data->id,
+               "CurrentTagValue", G_TYPE_STRING,
+               cb_task_data->current_tag_value,
+               "NewTagValue", G_TYPE_STRING, cb_task_data->new_tag_value,
+               NULL);
 
-       g_free(object_path);
+       goto no_complete;
 
 on_error:
 
-       if (cb_data->error && delete_needed) {
-               MSU_LOG_WARNING("Upload failed deleting created object "
-                               "with id %s", object_id);
-
-               cb_data->action = gupnp_service_proxy_begin_action(
-                       cb_data->proxy, "DestroyObject", prv_upload_delete_cb,
-                       cb_data, "ObjectID", G_TYPE_STRING,
-                       object_id, NULL);
-       } else {
-               (void) g_idle_add(msu_async_complete_task, cb_data);
-               g_cancellable_disconnect(cb_data->cancellable,
-                                        cb_data->cancel_id);
-       }
+       (void) g_idle_add(msu_async_complete_task, cb_data);
+       g_cancellable_disconnect(cb_data->cancellable, cb_data->cancel_id);
 
-       g_free(object_id);
-       g_free(import_uri);
+no_complete:
 
        if (parser)
                g_object_unref(parser);
@@ -1936,43 +3564,36 @@ on_error:
        MSU_LOG_DEBUG("Exit");
 }
 
-void msu_device_upload(msu_device_t *device,  msu_task_t *task,
-                      const gchar *parent_id, msu_async_cb_data_t *cb_data,
-                      GCancellable *cancellable)
+void msu_device_update_object(msu_device_t *device, msu_client_t *client,
+                             msu_task_t *task,
+                             msu_async_cb_data_t *cb_data,
+                             const gchar *upnp_filter,
+                             GCancellable *cancellable)
 {
        msu_device_context_t *context;
-       gchar *didl;
-       msu_async_upload_t *cb_task_data;
 
        MSU_LOG_DEBUG("Enter");
-       MSU_LOG_DEBUG("Uploading file to %s", parent_id);
-
-       context = msu_device_get_context(device);
-       cb_task_data = &cb_data->ut.upload;
-
-       didl = prv_create_upload_didl(parent_id, task,
-                                     cb_task_data->object_class,
-                                     cb_task_data->mime_type);
 
-       MSU_LOG_DEBUG("DIDL: %s", didl);
+       context = msu_device_get_context(device, client);
 
        cb_data->action = gupnp_service_proxy_begin_action(
-               context->service_proxy, "CreateObject",
-               prv_create_object_cb, cb_data,
-               "ContainerID", G_TYPE_STRING, parent_id,
-               "Elements", G_TYPE_STRING, didl,
-               NULL);
+                               context->service_proxy, "Browse",
+                               prv_update_object_browse_cb, cb_data,
+                               "ObjectID", G_TYPE_STRING, cb_data->id,
+                               "BrowseFlag", G_TYPE_STRING, "BrowseMetadata",
+                               "Filter", G_TYPE_STRING, upnp_filter,
+                               "StartingIndex", G_TYPE_INT, 0,
+                               "RequestedCount", G_TYPE_INT, 0,
+                               "SortCriteria", G_TYPE_STRING,
+                               "", NULL);
 
        cb_data->proxy = context->service_proxy;
-       cb_task_data->device = device;
 
-       cb_data->cancel_id =
-               g_cancellable_connect(cancellable,
-                                     G_CALLBACK(msu_async_task_cancelled),
-                                     cb_data, NULL);
-       cb_data->cancellable = cancellable;
+       cb_data->cancel_id = g_cancellable_connect(cancellable,
+                                       G_CALLBACK(msu_async_task_cancelled),
+                                       cb_data, NULL);
 
-       g_free(didl);
+       cb_data->cancellable = cancellable;
 
        MSU_LOG_DEBUG("Exit");
 }