#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);
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,
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)
{
MSU_CONTAINER_UPDATE_VAR,
prv_container_update_cb,
ctx->device);
+ gupnp_service_proxy_set_subscribed(ctx->service_proxy,
+ FALSE);
}
if (ctx->device_proxy)
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)
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);
}
}
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)
{
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);
G_TYPE_UINT,
prv_system_update_cb,
device);
+
gupnp_service_proxy_add_notify(context->service_proxy,
MSU_CONTAINER_UPDATE_VAR,
G_TYPE_STRING,
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,
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;
}
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)
{
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,
}
}
-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);
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");
goto on_error;
}
- MSU_LOG_DEBUG("GetMS2SpecProp result: %s", result);
+ MSU_LOG_DEBUG("GetMS2SpecProps result: %s", result);
parser = gupnp_didl_lite_parser_new();
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);
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;
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);
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,
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);
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");
}