+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;