4 * Copyright (C) 2012 Intel Corporation. All rights reserved.
6 * This program is free software; you can redistribute it and/or modify it
7 * under the terms and conditions of the GNU Lesser General Public License,
8 * version 2.1, as published by the Free Software Foundation.
10 * This program is distributed in the hope it will be useful, but WITHOUT
11 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
12 * FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License
15 * You should have received a copy of the GNU Lesser General Public License
16 * along with this program; if not, write to the Free Software Foundation, Inc.,
17 * 51 Franklin St - Fifth Floor, Boston, MA 02110-1301 USA.
19 * Mark Ryan <mark.d.ryan@intel.com>
24 #include <libgupnp/gupnp-error.h>
25 #include <libsoup/soup.h>
27 #include "chain-task.h"
30 #include "interface.h"
34 #define MSU_SYSTEM_UPDATE_VAR "SystemUpdateID"
35 #define MSU_CONTAINER_UPDATE_VAR "ContainerUpdateIDs"
36 #define MEDIA_SERVER_DEVICE_TYPE "urn:schemas-upnp-org:device:MediaServer:"
38 #define MSU_UPLOAD_STATUS_IN_PROGRESS "IN_PROGRESS"
39 #define MSU_UPLOAD_STATUS_CANCELLED "CANCELLED"
40 #define MSU_UPLOAD_STATUS_ERROR "ERROR"
41 #define MSU_UPLOAD_STATUS_COMPLETED "COMPLETED"
43 typedef gboolean (*msu_device_count_cb_t)(msu_async_cb_data_t *cb_data,
46 typedef struct msu_device_count_data_t_ msu_device_count_data_t;
47 struct msu_device_count_data_t_ {
48 msu_device_count_cb_t cb;
49 msu_async_cb_data_t *cb_data;
52 typedef struct msu_device_object_builder_t_ msu_device_object_builder_t;
53 struct msu_device_object_builder_t_ {
56 gboolean needs_child_count;
59 typedef struct msu_device_upload_job_t_ msu_device_upload_job_t;
61 typedef struct msu_device_upload_t_ msu_device_upload_t;
62 struct msu_device_upload_t_ {
63 SoupSession *soup_session;
65 GMappedFile *mapped_file;
67 guint64 bytes_uploaded;
68 guint64 bytes_to_upload;
71 struct msu_device_upload_job_t_ {
77 /* Private structure used in chain task */
78 typedef struct prv_new_device_ct_t_ prv_new_device_ct_t;
79 struct prv_new_device_ct_t_ {
81 GDBusConnection *connection;
82 const GDBusSubtreeVTable *vtable;
84 GHashTable *property_map;
87 static void prv_get_child_count(msu_async_cb_data_t *cb_data,
88 msu_device_count_cb_t cb, const gchar *id);
89 static void prv_retrieve_child_count_for_list(msu_async_cb_data_t *cb_data);
90 static void prv_container_update_cb(GUPnPServiceProxy *proxy,
94 static void prv_system_update_cb(GUPnPServiceProxy *proxy,
98 static void prv_msu_device_upload_delete(gpointer up);
99 static void prv_msu_upload_job_delete(gpointer up);
100 static void prv_get_sr_token_for_props(GUPnPServiceProxy *proxy,
101 msu_device_t *device,
102 GCancellable *cancellable,
103 msu_async_cb_data_t *cb_data);
105 static void prv_msu_device_object_builder_delete(void *dob)
107 msu_device_object_builder_t *builder = dob;
111 g_variant_builder_unref(builder->vb);
118 static void prv_msu_device_count_data_new(msu_async_cb_data_t *cb_data,
119 msu_device_count_cb_t cb,
120 msu_device_count_data_t **count_data)
122 msu_device_count_data_t *cd;
124 cd = g_new(msu_device_count_data_t, 1);
126 cd->cb_data = cb_data;
131 static void prv_msu_context_delete(gpointer context)
133 msu_device_context_t *ctx = context;
137 (void) g_source_remove(ctx->timeout_id);
139 if (ctx->subscribed) {
140 gupnp_service_proxy_remove_notify(ctx->service_proxy,
141 MSU_SYSTEM_UPDATE_VAR,
142 prv_system_update_cb,
144 gupnp_service_proxy_remove_notify(ctx->service_proxy,
145 MSU_CONTAINER_UPDATE_VAR,
146 prv_container_update_cb,
148 gupnp_service_proxy_set_subscribed(ctx->service_proxy,
152 if (ctx->device_proxy)
153 g_object_unref(ctx->device_proxy);
155 if (ctx->service_proxy)
156 g_object_unref(ctx->service_proxy);
158 g_free(ctx->ip_address);
163 static void prv_msu_context_new(const gchar *ip_address,
164 GUPnPDeviceProxy *proxy,
165 msu_device_t *device,
166 msu_device_context_t **context)
168 const gchar *service_type =
169 "urn:schemas-upnp-org:service:ContentDirectory";
170 msu_device_context_t *ctx = g_new(msu_device_context_t, 1);
172 ctx->ip_address = g_strdup(ip_address);
173 ctx->device_proxy = proxy;
174 ctx->device = device;
176 ctx->service_proxy = (GUPnPServiceProxy *)
177 gupnp_device_info_get_service((GUPnPDeviceInfo *) proxy,
179 ctx->subscribed = FALSE;
185 void msu_device_delete(void *device)
187 msu_device_t *dev = device;
190 dev->shutting_down = TRUE;
191 g_hash_table_unref(dev->upload_jobs);
192 g_hash_table_unref(dev->uploads);
195 (void) g_source_remove(dev->timeout_id);
198 (void) g_dbus_connection_unregister_subtree(
199 dev->connection, dev->id);
201 g_ptr_array_unref(dev->contexts);
203 g_variant_unref(dev->search_caps);
204 g_variant_unref(dev->sort_caps);
205 g_variant_unref(dev->sort_ext_caps);
206 g_variant_unref(dev->feature_list);
211 static void prv_build_container_update_array(const gchar *root_path,
213 GVariantBuilder *builder)
220 * value contains (id, version) pairs
221 * we must extract ids only
223 str_array = g_strsplit(value, ",", 0);
225 while (str_array[pos]) {
226 if ((pos % 2) == 0) {
227 path = msu_path_from_id(root_path, str_array[pos]);
228 g_variant_builder_add(builder, "o", path);
235 g_strfreev(str_array);
238 static void prv_container_update_cb(GUPnPServiceProxy *proxy,
239 const char *variable,
243 msu_device_t *device = user_data;
244 GVariantBuilder array;
246 MSU_LOG_DEBUG("Container Update %s", g_value_get_string(value));
248 g_variant_builder_init(&array, G_VARIANT_TYPE("ao"));
249 prv_build_container_update_array(device->path,
250 g_value_get_string(value),
253 (void) g_dbus_connection_emit_signal(device->connection,
256 MSU_INTERFACE_MEDIA_DEVICE,
257 MSU_INTERFACE_CONTAINER_UPDATE,
258 g_variant_new("(@ao)", g_variant_builder_end(&array)),
262 static void prv_system_update_cb(GUPnPServiceProxy *proxy,
263 const char *variable,
267 GVariantBuilder *array;
269 msu_device_t *device = user_data;
270 guint suid = g_value_get_uint(value);
272 MSU_LOG_DEBUG("System Update %u", suid);
274 device->system_update_id = suid;
276 array = g_variant_builder_new(G_VARIANT_TYPE("a{sv}"));
277 g_variant_builder_add(array, "{sv}", MSU_INTERFACE_SYSTEM_UPDATE_ID,
278 g_variant_new_uint32(suid));
279 val = g_variant_new("(s@a{sv}as)", MSU_INTERFACE_MEDIA_DEVICE,
280 g_variant_builder_end(array),
283 (void) g_dbus_connection_emit_signal(device->connection,
286 MSU_INTERFACE_PROPERTIES,
287 MSU_INTERFACE_PROPERTIES_CHANGED,
291 g_variant_builder_unref(array);
294 static gboolean prv_re_enable_subscription(gpointer user_data)
296 msu_device_context_t *context = user_data;
298 context->timeout_id = 0;
303 static void prv_subscription_lost_cb(GUPnPServiceProxy *proxy,
304 const GError *reason,
307 msu_device_context_t *context = user_data;
309 if (!context->timeout_id) {
310 gupnp_service_proxy_set_subscribed(context->service_proxy,
312 context->timeout_id = g_timeout_add_seconds(10,
313 prv_re_enable_subscription,
316 g_source_remove(context->timeout_id);
317 gupnp_service_proxy_remove_notify(context->service_proxy,
318 MSU_SYSTEM_UPDATE_VAR,
319 prv_system_update_cb,
321 gupnp_service_proxy_remove_notify(context->service_proxy,
322 MSU_CONTAINER_UPDATE_VAR,
323 prv_container_update_cb,
326 context->timeout_id = 0;
327 context->subscribed = FALSE;
331 void msu_device_subscribe_to_contents_change(msu_device_t *device)
333 msu_device_context_t *context;
335 context = msu_device_get_context(device, NULL);
337 MSU_LOG_DEBUG("Subscribe for events on context: %s",
338 context->ip_address);
340 gupnp_service_proxy_add_notify(context->service_proxy,
341 MSU_SYSTEM_UPDATE_VAR,
343 prv_system_update_cb,
346 gupnp_service_proxy_add_notify(context->service_proxy,
347 MSU_CONTAINER_UPDATE_VAR,
349 prv_container_update_cb,
352 context->subscribed = TRUE;
353 gupnp_service_proxy_set_subscribed(context->service_proxy, TRUE);
355 g_signal_connect(context->service_proxy,
357 G_CALLBACK(prv_subscription_lost_cb),
361 static void prv_feature_list_add_feature(gchar* root_path,
362 GUPnPFeature *feature,
374 name = gupnp_feature_get_name(feature);
375 version = gupnp_feature_get_version(feature);
376 obj_id = gupnp_feature_get_object_ids(feature);
378 g_variant_builder_init(&vbo, G_VARIANT_TYPE("ao"));
380 if (obj_id != NULL && *obj_id) {
381 obj = g_strsplit(obj_id, ",", 0);
384 while (obj && *obj) {
385 path = msu_path_from_id(root_path, *obj);
386 g_variant_builder_add(&vbo, "o", path);
394 var_obj = g_variant_builder_end(&vbo);
395 g_variant_builder_add(vb, "(ss@ao)", name, version, var_obj);
398 static void prv_get_feature_list_analyze(msu_device_t *device, gchar *result)
400 GUPnPFeatureListParser *parser;
401 GUPnPFeature *feature;
404 GError *error = NULL;
406 #if MSU_LOG_LEVEL & MSU_LOG_LEVEL_DEBUG
409 parser = gupnp_feature_list_parser_new();
410 list = gupnp_feature_list_parser_parse_text(parser, result, &error);
413 MSU_LOG_WARNING("GetFeatureList parsing failed: %s",
418 g_variant_builder_init(&vb, G_VARIANT_TYPE("a(ssao)"));
421 while (item != NULL) {
422 feature = (GUPnPFeature *) item->data;
423 prv_feature_list_add_feature(device->path, feature, &vb);
424 g_object_unref(feature);
425 item = g_list_next(item);
428 device->feature_list = g_variant_ref_sink(g_variant_builder_end(&vb));
430 #if MSU_LOG_LEVEL & MSU_LOG_LEVEL_DEBUG
431 str = g_variant_print(device->feature_list, FALSE);
432 MSU_LOG_DEBUG("%s = %s", MSU_INTERFACE_PROP_SV_FEATURE_LIST, str);
438 g_object_unref(parser);
444 static void prv_get_feature_list_cb(GUPnPServiceProxy *proxy,
445 GUPnPServiceProxyAction *action,
448 gchar *result = NULL;
449 GError *error = NULL;
450 prv_new_device_ct_t *priv_t = (prv_new_device_ct_t *) user_data;
452 if (!gupnp_service_proxy_end_action(proxy, action, &error,
453 "FeatureList", G_TYPE_STRING,
455 MSU_LOG_WARNING("GetFeatureList operation failed: %s",
460 MSU_LOG_DEBUG("GetFeatureList result: %s", result);
462 prv_get_feature_list_analyze(priv_t->dev, result);
471 static GUPnPServiceProxyAction *prv_get_feature_list(msu_chain_task_t *chain,
474 msu_device_t *device;
475 msu_device_context_t *context;
477 device = msu_chain_task_get_device(chain);
478 context = msu_device_get_context(device, NULL);
481 return gupnp_service_proxy_begin_action(context->service_proxy,
483 msu_chain_task_begin_action_cb,
487 static void prv_get_sort_ext_capabilities_analyze(msu_device_t *device,
492 #if MSU_LOG_LEVEL & MSU_LOG_LEVEL_DEBUG
495 GVariantBuilder sort_ext_caps_vb;
497 g_variant_builder_init(&sort_ext_caps_vb, G_VARIANT_TYPE("as"));
499 caps = g_strsplit(result, ",", 0);
502 while (caps && *caps) {
503 g_variant_builder_add(&sort_ext_caps_vb, "s", *caps);
509 device->sort_ext_caps = g_variant_ref_sink(g_variant_builder_end(
512 #if MSU_LOG_LEVEL & MSU_LOG_LEVEL_DEBUG
513 props = g_variant_print(device->sort_ext_caps, FALSE);
514 MSU_LOG_DEBUG("%s = %s", MSU_INTERFACE_PROP_SV_SORT_EXT_CAPABILITIES,
520 static void prv_get_sort_ext_capabilities_cb(GUPnPServiceProxy *proxy,
521 GUPnPServiceProxyAction *action,
524 gchar *result = NULL;
525 GError *error = NULL;
526 prv_new_device_ct_t *priv_t = (prv_new_device_ct_t *) user_data;
528 if (!gupnp_service_proxy_end_action(proxy, action, &error,
530 G_TYPE_STRING, &result, NULL)) {
532 "GetSortExtensionCapabilities operation failed: %s",
537 MSU_LOG_DEBUG("GetSortExtensionCapabilities result: %s", result);
539 prv_get_sort_ext_capabilities_analyze(priv_t->dev, result);
549 static GUPnPServiceProxyAction *prv_get_sort_ext_capabilities(
550 msu_chain_task_t *chain,
553 msu_device_t *device;
554 msu_device_context_t *context;
556 device = msu_chain_task_get_device(chain);
557 context = msu_device_get_context(device, NULL);
560 return gupnp_service_proxy_begin_action(context->service_proxy,
561 "GetSortExtensionCapabilities",
562 msu_chain_task_begin_action_cb,
566 static void prv_get_capabilities_analyze(GHashTable *property_map,
573 GVariantBuilder caps_vb;
575 g_variant_builder_init(&caps_vb, G_VARIANT_TYPE("as"));
577 caps = g_strsplit(result, ",", 0);
580 while (caps && *caps) {
581 prop_name = g_hash_table_lookup(property_map, *caps);
584 g_variant_builder_add(&caps_vb, "s", prop_name);
591 *variant = g_variant_ref_sink(g_variant_builder_end(&caps_vb));
593 #if MSU_LOG_LEVEL & MSU_LOG_LEVEL_DEBUG
594 prop_name = g_variant_print(*variant, FALSE);
595 MSU_LOG_DEBUG("%s = %s", " Variant", prop_name);
600 static void prv_get_sort_capabilities_cb(GUPnPServiceProxy *proxy,
601 GUPnPServiceProxyAction *action,
604 gchar *result = NULL;
605 GError *error = NULL;
606 prv_new_device_ct_t *priv_t = (prv_new_device_ct_t *) user_data;
608 if (!gupnp_service_proxy_end_action(proxy, action, &error, "SortCaps",
609 G_TYPE_STRING, &result, NULL)) {
610 MSU_LOG_WARNING("GetSortCapabilities operation failed: %s",
615 MSU_LOG_DEBUG("GetSortCapabilities result: %s", result);
617 prv_get_capabilities_analyze(priv_t->property_map, result,
618 &priv_t->dev->sort_caps);
628 static GUPnPServiceProxyAction *prv_get_sort_capabilities(
629 msu_chain_task_t *chain,
632 msu_device_t *device;
633 msu_device_context_t *context;
635 device = msu_chain_task_get_device(chain);
636 context = msu_device_get_context(device, NULL);
639 return gupnp_service_proxy_begin_action(context->service_proxy,
640 "GetSortCapabilities",
641 msu_chain_task_begin_action_cb,
645 static void prv_get_search_capabilities_cb(GUPnPServiceProxy *proxy,
646 GUPnPServiceProxyAction *action,
649 gchar *result = NULL;
650 GError *error = NULL;
651 prv_new_device_ct_t *priv_t = (prv_new_device_ct_t *) user_data;
653 if (!gupnp_service_proxy_end_action(proxy, action, &error, "SearchCaps",
654 G_TYPE_STRING, &result, NULL)) {
655 MSU_LOG_WARNING("GetSearchCapabilities operation failed: %s",
660 MSU_LOG_DEBUG("GetSearchCapabilities result: %s", result);
662 prv_get_capabilities_analyze(priv_t->property_map, result,
663 &priv_t->dev->search_caps);
673 static GUPnPServiceProxyAction *prv_get_search_capabilities(
674 msu_chain_task_t *chain,
677 msu_device_t *device;
678 msu_device_context_t *context;
680 device = msu_chain_task_get_device(chain);
681 context = msu_device_get_context(device, NULL);
684 return gupnp_service_proxy_begin_action(context->service_proxy,
685 "GetSearchCapabilities",
686 msu_chain_task_begin_action_cb,
690 static GUPnPServiceProxyAction *prv_subscribe(msu_chain_task_t *chain,
693 msu_device_t *device;
695 device = msu_chain_task_get_device(chain);
696 msu_device_subscribe_to_contents_change(device);
703 static GUPnPServiceProxyAction *prv_declare(msu_chain_task_t *chain,
708 msu_device_t *device;
709 prv_new_device_ct_t *priv_t;
711 device = msu_chain_task_get_device(chain);
713 priv_t = (prv_new_device_ct_t *) msu_chain_task_get_user_data(chain);
715 flags = G_DBUS_SUBTREE_FLAGS_DISPATCH_TO_UNENUMERATED_NODES;
716 id = g_dbus_connection_register_subtree(priv_t->connection,
725 device->uploads = g_hash_table_new_full(g_int_hash, g_int_equal,
727 prv_msu_device_upload_delete);
728 device->upload_jobs =
729 g_hash_table_new_full(g_int_hash, g_int_equal,
731 prv_msu_upload_job_delete);
734 MSU_LOG_ERROR("g_dbus_connection_register_subtree FAILED");
741 msu_device_t *msu_device_new(GDBusConnection *connection,
742 GUPnPDeviceProxy *proxy,
743 const gchar *ip_address,
744 const GDBusSubtreeVTable *vtable,
746 GHashTable *property_map,
748 msu_chain_task_t *chain)
751 prv_new_device_ct_t *priv_t;
754 MSU_LOG_DEBUG("New Device on %s", ip_address);
756 new_path = g_strdup_printf("%s/%u", MSU_SERVER_PATH, counter);
757 MSU_LOG_DEBUG("Server Path %s", new_path);
759 dev = g_new0(msu_device_t, 1);
760 priv_t = g_new0(prv_new_device_ct_t, 1);
762 dev->connection = connection;
763 dev->contexts = g_ptr_array_new_with_free_func(prv_msu_context_delete);
764 dev->path = new_path;
767 priv_t->connection = connection;
768 priv_t->vtable = vtable;
769 priv_t->user_data = user_data;
770 priv_t->property_map = property_map;
772 msu_device_append_new_context(dev, ip_address, proxy);
774 msu_chain_task_add(chain, prv_get_search_capabilities, dev,
775 prv_get_search_capabilities_cb, NULL, priv_t);
777 msu_chain_task_add(chain, prv_get_sort_capabilities, dev,
778 prv_get_sort_capabilities_cb, NULL, priv_t);
780 msu_chain_task_add(chain, prv_get_sort_ext_capabilities, dev,
781 prv_get_sort_ext_capabilities_cb, NULL, priv_t);
783 msu_chain_task_add(chain, prv_get_feature_list, dev,
784 prv_get_feature_list_cb, NULL, priv_t);
785 MSU_LOG_DEBUG("AFTER");
786 msu_chain_task_add(chain, prv_subscribe, dev, NULL, NULL, NULL);
787 msu_chain_task_add(chain, prv_declare, dev, NULL, g_free, priv_t);
789 msu_chain_task_start(chain);
794 void msu_device_append_new_context(msu_device_t *device,
795 const gchar *ip_address,
796 GUPnPDeviceProxy *proxy)
798 msu_device_context_t *context;
800 prv_msu_context_new(ip_address, proxy, device, &context);
801 g_ptr_array_add(device->contexts, context);
804 msu_device_t *msu_device_from_path(const gchar *path, GHashTable *device_list)
808 msu_device_t *device;
809 msu_device_t *retval = NULL;
811 g_hash_table_iter_init(&iter, device_list);
813 while (g_hash_table_iter_next(&iter, NULL, &value)) {
816 if (!strcmp(device->path, path)) {
825 msu_device_context_t *msu_device_get_context(msu_device_t *device,
826 msu_client_t *client)
828 msu_device_context_t *context;
830 const char ip4_local_prefix[] = "127.0.0.";
831 gboolean prefer_local;
834 prefer_local = (client && client->prefer_local_addresses);
836 for (i = 0; i < device->contexts->len; ++i) {
837 context = g_ptr_array_index(device->contexts, i);
839 is_local = (!strncmp(context->ip_address, ip4_local_prefix,
840 sizeof(ip4_local_prefix) - 1) ||
841 !strcmp(context->ip_address, "::1") ||
842 !strcmp(context->ip_address, "0:0:0:0:0:0:0:1"));
844 if (prefer_local == is_local)
848 if (i == device->contexts->len)
849 context = g_ptr_array_index(device->contexts, 0);
854 static void prv_found_child(GUPnPDIDLLiteParser *parser,
855 GUPnPDIDLLiteObject *object,
858 msu_async_cb_data_t *cb_data = user_data;
859 msu_task_t *task = cb_data->task;
860 msu_task_get_children_t *task_data = &task->ut.get_children;
861 msu_async_bas_t *cb_task_data = &cb_data->ut.bas;
862 msu_device_object_builder_t *builder;
863 gboolean have_child_count;
865 MSU_LOG_DEBUG("Enter");
867 builder = g_new0(msu_device_object_builder_t, 1);
869 if (GUPNP_IS_DIDL_LITE_CONTAINER(object)) {
870 if (!task_data->containers)
873 if (!task_data->items)
877 builder->vb = g_variant_builder_new(G_VARIANT_TYPE("a{sv}"));
879 if (!msu_props_add_object(builder->vb, object, cb_task_data->root_path,
880 task->path, cb_task_data->filter_mask))
883 if (GUPNP_IS_DIDL_LITE_CONTAINER(object)) {
884 msu_props_add_container(builder->vb,
885 (GUPnPDIDLLiteContainer *) object,
886 cb_task_data->filter_mask,
889 if (!have_child_count && (cb_task_data->filter_mask &
890 MSU_UPNP_MASK_PROP_CHILD_COUNT)) {
891 builder->needs_child_count = TRUE;
892 builder->id = g_strdup(
893 gupnp_didl_lite_object_get_id(object));
894 cb_task_data->need_child_count = TRUE;
897 msu_props_add_item(builder->vb, object,
898 cb_task_data->root_path,
899 cb_task_data->filter_mask,
900 cb_task_data->protocol_info);
903 g_ptr_array_add(cb_task_data->vbs, builder);
905 MSU_LOG_DEBUG("Exit with SUCCESS");
911 prv_msu_device_object_builder_delete(builder);
913 MSU_LOG_DEBUG("Exit with FAIL");
916 static GVariant *prv_children_result_to_variant(msu_async_cb_data_t *cb_data)
919 msu_device_object_builder_t *builder;
920 msu_async_bas_t *cb_task_data = &cb_data->ut.bas;
923 g_variant_builder_init(&vb, G_VARIANT_TYPE("aa{sv}"));
925 for (i = 0; i < cb_task_data->vbs->len; ++i) {
926 builder = g_ptr_array_index(cb_task_data->vbs, i);
927 g_variant_builder_add(&vb, "@a{sv}",
928 g_variant_builder_end(builder->vb));
931 return g_variant_builder_end(&vb);
934 static void prv_get_search_ex_result(msu_async_cb_data_t *cb_data)
936 GVariant *out_params[2];
937 msu_async_bas_t *cb_task_data = &cb_data->ut.bas;
939 out_params[0] = prv_children_result_to_variant(cb_data);
940 out_params[1] = g_variant_new_uint32(cb_task_data->max_count);
942 cb_data->result = g_variant_ref_sink(
943 g_variant_new_tuple(out_params, 2));
946 static void prv_get_children_result(msu_async_cb_data_t *cb_data)
948 GVariant *retval = prv_children_result_to_variant(cb_data);
949 cb_data->result = g_variant_ref_sink(retval);
952 static gboolean prv_child_count_for_list_cb(msu_async_cb_data_t *cb_data,
955 msu_async_bas_t *cb_task_data = &cb_data->ut.bas;
956 msu_device_object_builder_t *builder;
958 builder = g_ptr_array_index(cb_task_data->vbs, cb_task_data->retrieved);
959 msu_props_add_child_count(builder->vb, count);
960 cb_task_data->retrieved++;
961 prv_retrieve_child_count_for_list(cb_data);
963 return cb_task_data->retrieved >= cb_task_data->vbs->len;
966 static void prv_retrieve_child_count_for_list(msu_async_cb_data_t *cb_data)
968 msu_async_bas_t *cb_task_data = &cb_data->ut.bas;
969 msu_device_object_builder_t *builder;
972 for (i = cb_task_data->retrieved; i < cb_task_data->vbs->len; ++i) {
973 builder = g_ptr_array_index(cb_task_data->vbs, i);
975 if (builder->needs_child_count)
979 cb_task_data->retrieved = i;
981 if (i < cb_task_data->vbs->len)
982 prv_get_child_count(cb_data, prv_child_count_for_list_cb,
985 cb_task_data->get_children_cb(cb_data);
988 static void prv_get_children_cb(GUPnPServiceProxy *proxy,
989 GUPnPServiceProxyAction *action,
992 gchar *result = NULL;
993 GUPnPDIDLLiteParser *parser = NULL;
994 GError *upnp_error = NULL;
995 msu_async_cb_data_t *cb_data = user_data;
996 msu_async_bas_t *cb_task_data = &cb_data->ut.bas;
998 MSU_LOG_DEBUG("Enter");
1000 if (!gupnp_service_proxy_end_action(cb_data->proxy, cb_data->action,
1002 "Result", G_TYPE_STRING,
1004 MSU_LOG_WARNING("Browse operation failed: %s",
1005 upnp_error->message);
1007 cb_data->error = g_error_new(MSU_ERROR,
1008 MSU_ERROR_OPERATION_FAILED,
1009 "Browse operation failed: %s",
1010 upnp_error->message);
1014 MSU_LOG_DEBUG("GetChildren result: %s", result);
1016 parser = gupnp_didl_lite_parser_new();
1018 g_signal_connect(parser, "object-available" ,
1019 G_CALLBACK(prv_found_child), cb_data);
1021 cb_task_data->vbs = g_ptr_array_new_with_free_func(
1022 prv_msu_device_object_builder_delete);
1024 if (!gupnp_didl_lite_parser_parse_didl(parser, result, &upnp_error)
1025 && upnp_error->code != GUPNP_XML_ERROR_EMPTY_NODE) {
1026 MSU_LOG_WARNING("Unable to parse results of browse: %s",
1027 upnp_error->message);
1029 cb_data->error = g_error_new(MSU_ERROR,
1030 MSU_ERROR_OPERATION_FAILED,
1031 "Unable to parse results of "
1032 "browse: %s", upnp_error->message);
1036 if (cb_task_data->need_child_count) {
1037 MSU_LOG_DEBUG("Need to retrieve ChildCounts");
1039 cb_task_data->get_children_cb = prv_get_children_result;
1040 prv_retrieve_child_count_for_list(cb_data);
1043 prv_get_children_result(cb_data);
1048 (void) g_idle_add(msu_async_complete_task, cb_data);
1049 g_cancellable_disconnect(cb_data->cancellable, cb_data->cancel_id);
1054 g_error_free(upnp_error);
1057 g_object_unref(parser);
1061 MSU_LOG_DEBUG("Exit");
1064 void msu_device_get_children(msu_device_t *device, msu_client_t *client,
1065 msu_task_t *task, msu_async_cb_data_t *cb_data,
1066 const gchar *upnp_filter, const gchar *sort_by,
1067 GCancellable *cancellable)
1069 msu_device_context_t *context;
1071 MSU_LOG_DEBUG("Enter");
1073 context = msu_device_get_context(device, client);
1076 gupnp_service_proxy_begin_action(context->service_proxy,
1078 prv_get_children_cb,
1080 "ObjectID", G_TYPE_STRING,
1083 "BrowseFlag", G_TYPE_STRING,
1084 "BrowseDirectChildren",
1086 "Filter", G_TYPE_STRING,
1089 "StartingIndex", G_TYPE_INT,
1090 task->ut.get_children.start,
1091 "RequestedCount", G_TYPE_INT,
1092 task->ut.get_children.count,
1093 "SortCriteria", G_TYPE_STRING,
1096 cb_data->proxy = context->service_proxy;
1098 cb_data->cancel_id =
1099 g_cancellable_connect(cancellable,
1100 G_CALLBACK(msu_async_task_cancelled),
1102 cb_data->cancellable = cancellable;
1104 MSU_LOG_DEBUG("Exit");
1107 static void prv_get_item(GUPnPDIDLLiteParser *parser,
1108 GUPnPDIDLLiteObject *object,
1111 msu_async_cb_data_t *cb_data = user_data;
1112 msu_async_get_all_t *cb_task_data = &cb_data->ut.get_all;
1114 if (!GUPNP_IS_DIDL_LITE_CONTAINER(object))
1115 msu_props_add_item(cb_task_data->vb, object,
1116 cb_task_data->root_path,
1118 cb_task_data->protocol_info);
1120 cb_data->error = g_error_new(MSU_ERROR,
1121 MSU_ERROR_UNKNOWN_INTERFACE,
1122 "Interface not supported on "
1126 static void prv_get_container(GUPnPDIDLLiteParser *parser,
1127 GUPnPDIDLLiteObject *object,
1130 msu_async_cb_data_t *cb_data = user_data;
1131 msu_async_get_all_t *cb_task_data = &cb_data->ut.get_all;
1132 gboolean have_child_count;
1134 if (GUPNP_IS_DIDL_LITE_CONTAINER(object)) {
1135 msu_props_add_container(cb_task_data->vb,
1136 (GUPnPDIDLLiteContainer *) object,
1139 if (!have_child_count)
1140 cb_task_data->need_child_count = TRUE;
1142 cb_data->error = g_error_new(MSU_ERROR,
1143 MSU_ERROR_UNKNOWN_INTERFACE,
1144 "Interface not supported on "
1149 static void prv_get_object(GUPnPDIDLLiteParser *parser,
1150 GUPnPDIDLLiteObject *object,
1153 msu_async_cb_data_t *cb_data = user_data;
1154 msu_async_get_all_t *cb_task_data = &cb_data->ut.get_all;
1156 const char *parent_path;
1159 id = gupnp_didl_lite_object_get_parent_id(object);
1161 if (!id || !strcmp(id, "-1") || !strcmp(id, "")) {
1162 parent_path = cb_task_data->root_path;
1164 path = msu_path_from_id(cb_task_data->root_path, id);
1168 if (!msu_props_add_object(cb_task_data->vb, object,
1169 cb_task_data->root_path,
1170 parent_path, 0xffffffff))
1171 cb_data->error = g_error_new(MSU_ERROR, MSU_ERROR_BAD_RESULT,
1172 "Unable to retrieve mandatory "
1173 " object properties");
1177 static void prv_get_all(GUPnPDIDLLiteParser *parser,
1178 GUPnPDIDLLiteObject *object,
1181 msu_async_cb_data_t *cb_data = user_data;
1182 msu_async_get_all_t *cb_task_data = &cb_data->ut.get_all;
1183 gboolean have_child_count;
1185 prv_get_object(parser, object, user_data);
1187 if (!cb_data->error) {
1188 if (GUPNP_IS_DIDL_LITE_CONTAINER(object)) {
1189 msu_props_add_container(
1191 (GUPnPDIDLLiteContainer *)
1194 if (!have_child_count)
1195 cb_task_data->need_child_count = TRUE;
1197 msu_props_add_item(cb_task_data->vb,
1199 cb_task_data->root_path,
1201 cb_task_data->protocol_info);
1206 static gboolean prv_device_subscribed(msu_device_t *device)
1208 msu_device_context_t *context;
1210 gboolean subscribed = FALSE;
1212 for (i = 0; i < device->contexts->len; ++i) {
1213 context = g_ptr_array_index(device->contexts, i);
1214 if (context->subscribed) {
1223 static void prv_system_update_id_for_prop_cb(GUPnPServiceProxy *proxy,
1224 GUPnPServiceProxyAction *action,
1227 GError *upnp_error = NULL;
1229 msu_async_cb_data_t *cb_data = user_data;
1231 MSU_LOG_DEBUG("Enter");
1233 if (!gupnp_service_proxy_end_action(proxy, action, &upnp_error,
1237 MSU_LOG_ERROR("Unable to retrieve ServiceUpdateID: %s %s",
1238 g_quark_to_string(upnp_error->domain),
1239 upnp_error->message);
1241 cb_data->error = g_error_new(MSU_ERROR,
1242 MSU_ERROR_OPERATION_FAILED,
1243 "Unable to retrieve ServiceUpdateID: %s",
1244 upnp_error->message);
1249 cb_data->result = g_variant_ref_sink(g_variant_new_uint32(id));
1253 (void) g_idle_add(msu_async_complete_task, cb_data);
1254 g_cancellable_disconnect(cb_data->cancellable, cb_data->cancel_id);
1257 g_error_free(upnp_error);
1259 MSU_LOG_DEBUG("Exit");
1262 static void prv_get_system_update_id_for_prop(GUPnPServiceProxy *proxy,
1263 msu_device_t *device,
1264 GCancellable *cancellable,
1265 msu_async_cb_data_t *cb_data)
1269 MSU_LOG_DEBUG("Enter");
1271 if (prv_device_subscribed(device)) {
1272 suid = device->system_update_id;
1274 cb_data->result = g_variant_ref_sink(
1275 g_variant_new_uint32(suid));
1277 (void) g_idle_add(msu_async_complete_task, cb_data);
1282 gupnp_service_proxy_begin_action(proxy, "GetSystemUpdateID",
1283 prv_system_update_id_for_prop_cb,
1287 cb_data->proxy = proxy;
1289 cb_data->cancel_id =
1290 g_cancellable_connect(cancellable,
1291 G_CALLBACK(msu_async_task_cancelled),
1293 cb_data->cancellable = cancellable;
1297 MSU_LOG_DEBUG("Exit");
1300 static void prv_system_update_id_for_props_cb(GUPnPServiceProxy *proxy,
1301 GUPnPServiceProxyAction *action,
1304 GError *upnp_error = NULL;
1306 msu_async_cb_data_t *cb_data = user_data;
1307 msu_async_get_all_t *cb_task_data = &cb_data->ut.get_all;
1309 MSU_LOG_DEBUG("Enter");
1311 if (!gupnp_service_proxy_end_action(proxy, action, &upnp_error,
1315 MSU_LOG_ERROR("Unable to retrieve ServiceUpdateID: %s %s",
1316 g_quark_to_string(upnp_error->domain),
1317 upnp_error->message);
1319 cb_data->error = g_error_new(MSU_ERROR,
1320 MSU_ERROR_OPERATION_FAILED,
1321 "Unable to retrieve ServiceUpdateID: %s",
1322 upnp_error->message);
1327 g_variant_builder_add(cb_task_data->vb, "{sv}",
1328 MSU_SYSTEM_UPDATE_VAR,
1329 g_variant_new_uint32(id));
1331 cb_data->result = g_variant_ref_sink(g_variant_builder_end(
1336 if (!cb_data->error)
1337 prv_get_sr_token_for_props(proxy, cb_task_data->device,
1338 cb_data->cancellable, cb_data);
1340 (void) g_idle_add(msu_async_complete_task, cb_data);
1341 g_cancellable_disconnect(cb_data->cancellable,
1342 cb_data->cancel_id);
1346 g_error_free(upnp_error);
1348 MSU_LOG_DEBUG("Exit");
1351 static void prv_get_system_update_id_for_props(GUPnPServiceProxy *proxy,
1352 msu_device_t *device,
1353 GCancellable *cancellable,
1354 msu_async_cb_data_t *cb_data)
1356 msu_async_get_all_t *cb_task_data;
1359 MSU_LOG_DEBUG("Enter");
1361 if (prv_device_subscribed(device)) {
1362 suid = device->system_update_id;
1364 cb_task_data = &cb_data->ut.get_all;
1366 g_variant_builder_add(cb_task_data->vb, "{sv}",
1367 MSU_SYSTEM_UPDATE_VAR,
1368 g_variant_new_uint32(suid));
1370 prv_get_sr_token_for_props(proxy, device, cancellable, cb_data);
1375 gupnp_service_proxy_begin_action(proxy, "GetSystemUpdateID",
1376 prv_system_update_id_for_props_cb,
1380 cb_data->proxy = proxy;
1382 cb_data->cancel_id =
1383 g_cancellable_connect(cancellable,
1384 G_CALLBACK(msu_async_task_cancelled),
1386 cb_data->cancellable = cancellable;
1390 MSU_LOG_DEBUG("Exit");
1393 static int prv_get_media_server_version(msu_device_t *device)
1395 msu_device_context_t *context;
1396 const char *device_type;
1397 const char *version;
1399 context = msu_device_get_context(device, NULL);
1400 device_type = gupnp_device_info_get_device_type((GUPnPDeviceInfo *)
1401 context->device_proxy);
1403 if (strncmp(device_type, MEDIA_SERVER_DEVICE_TYPE,
1404 sizeof(MEDIA_SERVER_DEVICE_TYPE) - 1))
1407 version = device_type + sizeof(MEDIA_SERVER_DEVICE_TYPE) - 1;
1409 return atoi(version);
1416 static void prv_service_reset_for_prop_cb(GUPnPServiceProxy *proxy,
1417 GUPnPServiceProxyAction *action,
1420 GError *upnp_error = NULL;
1421 gchar *token = NULL;
1422 msu_async_cb_data_t *cb_data = user_data;
1424 MSU_LOG_DEBUG("Enter");
1426 if (!gupnp_service_proxy_end_action(proxy, action, &upnp_error,
1427 "ResetToken", G_TYPE_STRING,
1430 MSU_LOG_ERROR("Unable to retrieve ServiceResetToken: %s %s",
1431 g_quark_to_string(upnp_error->domain),
1432 upnp_error->message);
1435 cb_data->error = g_error_new(MSU_ERROR,
1436 MSU_ERROR_OPERATION_FAILED,
1437 "GetServiceResetToken failed: %s",
1438 upnp_error->message);
1443 cb_data->result = g_variant_ref_sink(g_variant_new_string(token));
1447 MSU_LOG_DEBUG("Service Reset %s", token);
1451 (void) g_idle_add(msu_async_complete_task, cb_data);
1452 g_cancellable_disconnect(cb_data->cancellable, cb_data->cancel_id);
1455 g_error_free(upnp_error);
1457 MSU_LOG_DEBUG("Exit");
1460 static void prv_get_sr_token_for_prop(GUPnPServiceProxy *proxy,
1461 msu_device_t *device,
1462 GCancellable *cancellable,
1463 msu_async_cb_data_t *cb_data)
1465 MSU_LOG_DEBUG("Enter");
1467 if (prv_get_media_server_version(device) < 3) {
1468 cb_data->error = g_error_new(MSU_ERROR,
1469 MSU_ERROR_UNKNOWN_PROPERTY,
1470 "Unknown property");
1472 (void) g_idle_add(msu_async_complete_task, cb_data);
1477 gupnp_service_proxy_begin_action(proxy, "GetServiceResetToken",
1478 prv_service_reset_for_prop_cb,
1482 cb_data->proxy = proxy;
1484 cb_data->cancel_id = g_cancellable_connect(cancellable,
1485 G_CALLBACK(msu_async_task_cancelled),
1487 cb_data->cancellable = cancellable;
1491 MSU_LOG_DEBUG("Exit");
1494 static void prv_service_reset_for_props_cb(GUPnPServiceProxy *proxy,
1495 GUPnPServiceProxyAction *action,
1498 GError *upnp_error = NULL;
1499 gchar *token = NULL;
1500 msu_async_cb_data_t *cb_data = user_data;
1501 msu_async_get_all_t *cb_task_data;
1503 MSU_LOG_DEBUG("Enter");
1505 if (!gupnp_service_proxy_end_action(proxy, action, &upnp_error,
1506 "ResetToken", G_TYPE_STRING,
1509 MSU_LOG_ERROR("Unable to retrieve ServiceResetToken: %s %s",
1510 g_quark_to_string(upnp_error->domain),
1511 upnp_error->message);
1513 cb_data->error = g_error_new(MSU_ERROR,
1514 MSU_ERROR_OPERATION_FAILED,
1515 "GetServiceResetToken failed: %s",
1516 upnp_error->message);
1521 cb_task_data = &cb_data->ut.get_all;
1522 g_variant_builder_add(cb_task_data->vb, "{sv}",
1523 MSU_INTERFACE_PROP_ESV_SERVICE_RESET_TOKEN,
1524 g_variant_new_string(token));
1526 cb_data->result = g_variant_ref_sink(g_variant_builder_end(
1531 MSU_LOG_DEBUG("Service Reset %s", token);
1535 (void) g_idle_add(msu_async_complete_task, cb_data);
1536 g_cancellable_disconnect(cb_data->cancellable, cb_data->cancel_id);
1539 g_error_free(upnp_error);
1541 MSU_LOG_DEBUG("Exit");
1544 static void prv_get_sr_token_for_props(GUPnPServiceProxy *proxy,
1545 msu_device_t *device,
1546 GCancellable *cancellable,
1547 msu_async_cb_data_t *cb_data)
1549 msu_async_get_all_t *cb_task_data;
1551 MSU_LOG_DEBUG("Enter");
1553 if (prv_get_media_server_version(device) < 3) {
1554 cb_task_data = &cb_data->ut.get_all;
1556 cb_data->result = g_variant_ref_sink(g_variant_builder_end(
1559 goto on_complete; /* No error here, just skip the property */
1562 gupnp_service_proxy_begin_action(proxy, "GetServiceResetToken",
1563 prv_service_reset_for_props_cb,
1567 cb_data->proxy = proxy;
1569 cb_data->cancel_id = g_cancellable_connect(cancellable,
1570 G_CALLBACK(msu_async_task_cancelled),
1572 cb_data->cancellable = cancellable;
1576 (void) g_idle_add(msu_async_complete_task, cb_data);
1578 MSU_LOG_DEBUG("Exit");
1581 static gboolean prv_get_all_child_count_cb(msu_async_cb_data_t *cb_data,
1584 msu_async_get_all_t *cb_task_data = &cb_data->ut.get_all;
1586 msu_props_add_child_count(cb_task_data->vb, count);
1587 if (cb_task_data->device_object)
1588 prv_get_system_update_id_for_props(cb_data->proxy,
1589 cb_task_data->device,
1590 cb_data->cancellable,
1593 cb_data->result = g_variant_ref_sink(g_variant_builder_end(
1596 return !cb_task_data->device_object;
1599 static void prv_get_all_ms2spec_props_cb(GUPnPServiceProxy *proxy,
1600 GUPnPServiceProxyAction *action,
1603 GError *upnp_error = NULL;
1604 gchar *result = NULL;
1605 GUPnPDIDLLiteParser *parser = NULL;
1606 msu_async_cb_data_t *cb_data = user_data;
1607 msu_async_get_all_t *cb_task_data = &cb_data->ut.get_all;
1609 MSU_LOG_DEBUG("Enter");
1611 if (!gupnp_service_proxy_end_action(cb_data->proxy, cb_data->action,
1613 "Result", G_TYPE_STRING,
1615 MSU_LOG_WARNING("Browse operation failed: %s",
1616 upnp_error->message);
1618 cb_data->error = g_error_new(MSU_ERROR,
1619 MSU_ERROR_OPERATION_FAILED,
1620 "Browse operation failed: %s",
1621 upnp_error->message);
1625 MSU_LOG_DEBUG("GetMS2SpecProps result: %s", result);
1627 parser = gupnp_didl_lite_parser_new();
1629 g_signal_connect(parser, "object-available" , cb_task_data->prop_func,
1632 if (!gupnp_didl_lite_parser_parse_didl(parser, result, &upnp_error)) {
1633 if (upnp_error->code == GUPNP_XML_ERROR_EMPTY_NODE) {
1634 MSU_LOG_WARNING("Property not defined for object");
1637 g_error_new(MSU_ERROR,
1638 MSU_ERROR_UNKNOWN_PROPERTY,
1639 "Property not defined for object");
1641 MSU_LOG_WARNING("Unable to parse results of browse: %s",
1642 upnp_error->message);
1645 g_error_new(MSU_ERROR,
1646 MSU_ERROR_OPERATION_FAILED,
1647 "Unable to parse results of "
1649 upnp_error->message);
1657 if (cb_task_data->need_child_count) {
1658 MSU_LOG_DEBUG("Need Child Count");
1660 prv_get_child_count(cb_data, prv_get_all_child_count_cb,
1664 } else if (cb_data->task->type == MSU_TASK_GET_ALL_PROPS &&
1665 cb_task_data->device_object) {
1666 prv_get_system_update_id_for_props(proxy, cb_task_data->device,
1667 cb_data->cancellable, cb_data);
1671 cb_data->result = g_variant_ref_sink(g_variant_builder_end(
1677 (void) g_idle_add(msu_async_complete_task, cb_data);
1678 g_cancellable_disconnect(cb_data->cancellable, cb_data->cancel_id);
1683 g_error_free(upnp_error);
1686 g_object_unref(parser);
1690 MSU_LOG_DEBUG("Exit");
1693 static void prv_get_all_ms2spec_props(msu_device_context_t *context,
1694 GCancellable *cancellable,
1695 msu_async_cb_data_t *cb_data)
1697 msu_async_get_all_t *cb_task_data = &cb_data->ut.get_all;
1698 msu_task_t *task = cb_data->task;
1699 msu_task_get_props_t *task_data = &task->ut.get_props;
1701 MSU_LOG_DEBUG("Enter called");
1703 if (!strcmp(MSU_INTERFACE_MEDIA_CONTAINER, task_data->interface_name))
1704 cb_task_data->prop_func = G_CALLBACK(prv_get_container);
1705 else if (!strcmp(MSU_INTERFACE_MEDIA_ITEM, task_data->interface_name))
1706 cb_task_data->prop_func = G_CALLBACK(prv_get_item);
1707 else if (!strcmp(MSU_INTERFACE_MEDIA_OBJECT, task_data->interface_name))
1708 cb_task_data->prop_func = G_CALLBACK(prv_get_object);
1709 else if (!strcmp("", task_data->interface_name))
1710 cb_task_data->prop_func = G_CALLBACK(prv_get_all);
1712 MSU_LOG_WARNING("Interface is unknown.");
1715 g_error_new(MSU_ERROR, MSU_ERROR_UNKNOWN_INTERFACE,
1716 "Interface is unknown.");
1720 cb_data->action = gupnp_service_proxy_begin_action(
1721 context->service_proxy, "Browse",
1722 prv_get_all_ms2spec_props_cb, cb_data,
1723 "ObjectID", G_TYPE_STRING, cb_data->id,
1724 "BrowseFlag", G_TYPE_STRING, "BrowseMetadata",
1725 "Filter", G_TYPE_STRING, "*",
1726 "StartingIndex", G_TYPE_INT, 0,
1727 "RequestedCount", G_TYPE_INT, 0,
1728 "SortCriteria", G_TYPE_STRING,
1731 cb_data->proxy = context->service_proxy;
1733 cb_data->cancel_id =
1734 g_cancellable_connect(cancellable,
1735 G_CALLBACK(msu_async_task_cancelled),
1737 cb_data->cancellable = cancellable;
1739 MSU_LOG_DEBUG("Exit with SUCCESS");
1745 (void) g_idle_add(msu_async_complete_task, cb_data);
1747 MSU_LOG_DEBUG("Exit with FAIL");
1752 void msu_device_get_all_props(msu_device_t *device, msu_client_t *client,
1753 msu_task_t *task, msu_async_cb_data_t *cb_data,
1754 gboolean root_object,
1755 GCancellable *cancellable)
1757 msu_async_get_all_t *cb_task_data;
1758 msu_task_get_props_t *task_data = &task->ut.get_props;
1759 msu_device_context_t *context;
1761 MSU_LOG_DEBUG("Enter");
1763 context = msu_device_get_context(device, client);
1764 cb_task_data = &cb_data->ut.get_all;
1766 cb_task_data->vb = g_variant_builder_new(G_VARIANT_TYPE("a{sv}"));
1767 cb_task_data->device_object = root_object;
1768 cb_task_data->device = device;
1770 if (!strcmp(task_data->interface_name, MSU_INTERFACE_MEDIA_DEVICE)) {
1772 msu_props_add_device(
1773 (GUPnPDeviceInfo *) context->device_proxy,
1777 prv_get_system_update_id_for_props(
1778 context->service_proxy,
1784 g_error_new(MSU_ERROR,
1785 MSU_ERROR_UNKNOWN_INTERFACE,
1786 "Interface is only valid on "
1789 (void) g_idle_add(msu_async_complete_task, cb_data);
1792 } else if (strcmp(task_data->interface_name, "")) {
1793 cb_task_data->device_object = FALSE;
1794 prv_get_all_ms2spec_props(context, cancellable, cb_data);
1797 msu_props_add_device(
1798 (GUPnPDeviceInfo *) context->device_proxy,
1802 prv_get_all_ms2spec_props(context, cancellable, cb_data);
1805 MSU_LOG_DEBUG("Exit");
1808 static void prv_get_object_property(GUPnPDIDLLiteParser *parser,
1809 GUPnPDIDLLiteObject *object,
1812 msu_async_cb_data_t *cb_data = user_data;
1813 msu_task_t *task = cb_data->task;
1814 msu_task_get_prop_t *task_data = &task->ut.get_prop;
1815 msu_async_get_prop_t *cb_task_data = &cb_data->ut.get_prop;
1817 if (cb_data->result)
1820 cb_data->result = msu_props_get_object_prop(task_data->prop_name,
1821 cb_task_data->root_path,
1829 static void prv_get_item_property(GUPnPDIDLLiteParser *parser,
1830 GUPnPDIDLLiteObject *object,
1833 msu_async_cb_data_t *cb_data = user_data;
1834 msu_task_t *task = cb_data->task;
1835 msu_task_get_prop_t *task_data = &task->ut.get_prop;
1836 msu_async_get_prop_t *cb_task_data = &cb_data->ut.get_prop;
1838 if (cb_data->result)
1841 cb_data->result = msu_props_get_item_prop(task_data->prop_name,
1842 cb_task_data->root_path,
1844 cb_task_data->protocol_info);
1851 static void prv_get_container_property(GUPnPDIDLLiteParser *parser,
1852 GUPnPDIDLLiteObject *object,
1855 msu_async_cb_data_t *cb_data = user_data;
1856 msu_task_t *task = cb_data->task;
1857 msu_task_get_prop_t *task_data = &task->ut.get_prop;
1859 if (cb_data->result)
1862 cb_data->result = msu_props_get_container_prop(task_data->prop_name,
1870 static void prv_get_all_property(GUPnPDIDLLiteParser *parser,
1871 GUPnPDIDLLiteObject *object,
1874 msu_async_cb_data_t *cb_data = user_data;
1876 prv_get_object_property(parser, object, user_data);
1878 if (cb_data->result)
1881 if (GUPNP_IS_DIDL_LITE_CONTAINER(object))
1882 prv_get_container_property(parser, object, user_data);
1884 prv_get_item_property(parser, object, user_data);
1891 static gboolean prv_get_child_count_cb(msu_async_cb_data_t *cb_data,
1894 MSU_LOG_DEBUG("Enter");
1896 MSU_LOG_DEBUG("Count %d", count);
1898 cb_data->result = g_variant_ref_sink(
1899 g_variant_new_uint32((guint) count));
1901 MSU_LOG_DEBUG("Exit");
1906 static void prv_count_children_cb(GUPnPServiceProxy *proxy,
1907 GUPnPServiceProxyAction *action,
1910 msu_device_count_data_t *count_data = user_data;
1911 msu_async_cb_data_t *cb_data = count_data->cb_data;
1912 GError *upnp_error = NULL;
1914 gboolean complete = FALSE;
1916 MSU_LOG_DEBUG("Enter");
1918 if (!gupnp_service_proxy_end_action(cb_data->proxy, cb_data->action,
1920 "TotalMatches", G_TYPE_INT,
1923 MSU_LOG_WARNING("Browse operation failed: %s",
1924 upnp_error->message);
1926 cb_data->error = g_error_new(MSU_ERROR,
1927 MSU_ERROR_OPERATION_FAILED,
1928 "Browse operation failed: %s",
1929 upnp_error->message);
1933 complete = count_data->cb(cb_data, count);
1939 if (cb_data->error || complete) {
1940 (void) g_idle_add(msu_async_complete_task, cb_data);
1941 g_cancellable_disconnect(cb_data->cancellable,
1942 cb_data->cancel_id);
1946 g_error_free(upnp_error);
1948 MSU_LOG_DEBUG("Exit");
1951 static void prv_get_child_count(msu_async_cb_data_t *cb_data,
1952 msu_device_count_cb_t cb, const gchar *id)
1954 msu_device_count_data_t *count_data;
1956 MSU_LOG_DEBUG("Enter");
1958 prv_msu_device_count_data_new(cb_data, cb, &count_data);
1960 gupnp_service_proxy_begin_action(cb_data->proxy,
1962 prv_count_children_cb,
1964 "ObjectID", G_TYPE_STRING, id,
1966 "BrowseFlag", G_TYPE_STRING,
1967 "BrowseDirectChildren",
1969 "Filter", G_TYPE_STRING, "",
1971 "StartingIndex", G_TYPE_INT,
1974 "RequestedCount", G_TYPE_INT,
1977 "SortCriteria", G_TYPE_STRING,
1982 MSU_LOG_DEBUG("Exit with SUCCESS");
1985 static void prv_get_ms2spec_prop_cb(GUPnPServiceProxy *proxy,
1986 GUPnPServiceProxyAction *action,
1989 GError *upnp_error = NULL;
1990 gchar *result = NULL;
1991 GUPnPDIDLLiteParser *parser = NULL;
1992 msu_async_cb_data_t *cb_data = user_data;
1993 msu_async_get_prop_t *cb_task_data = &cb_data->ut.get_prop;
1994 msu_task_get_prop_t *task_data = &cb_data->task->ut.get_prop;
1996 MSU_LOG_DEBUG("Enter");
1998 if (!gupnp_service_proxy_end_action(cb_data->proxy, cb_data->action,
2000 "Result", G_TYPE_STRING,
2002 MSU_LOG_WARNING("Browse operation failed: %s",
2003 upnp_error->message);
2005 cb_data->error = g_error_new(MSU_ERROR,
2006 MSU_ERROR_OPERATION_FAILED,
2007 "Browse operation failed: %s",
2008 upnp_error->message);
2012 MSU_LOG_DEBUG("GetMS2SpecProp result: %s", result);
2014 parser = gupnp_didl_lite_parser_new();
2016 g_signal_connect(parser, "object-available" , cb_task_data->prop_func,
2019 if (!gupnp_didl_lite_parser_parse_didl(parser, result, &upnp_error)) {
2020 if (upnp_error->code == GUPNP_XML_ERROR_EMPTY_NODE) {
2021 MSU_LOG_WARNING("Property not defined for object");
2024 g_error_new(MSU_ERROR,
2025 MSU_ERROR_UNKNOWN_PROPERTY,
2026 "Property not defined for object");
2028 MSU_LOG_WARNING("Unable to parse results of browse: %s",
2029 upnp_error->message);
2032 g_error_new(MSU_ERROR,
2033 MSU_ERROR_OPERATION_FAILED,
2034 "Unable to parse results of "
2036 upnp_error->message);
2041 if (!cb_data->result) {
2042 MSU_LOG_WARNING("Property not defined for object");
2044 cb_data->error = g_error_new(MSU_ERROR,
2045 MSU_ERROR_UNKNOWN_PROPERTY,
2046 "Property not defined for object");
2051 if (cb_data->error && !strcmp(task_data->prop_name,
2052 MSU_INTERFACE_PROP_CHILD_COUNT)) {
2053 MSU_LOG_DEBUG("ChildCount not supported by server");
2055 g_error_free(cb_data->error);
2056 cb_data->error = NULL;
2057 prv_get_child_count(cb_data, prv_get_child_count_cb,
2060 (void) g_idle_add(msu_async_complete_task, cb_data);
2061 g_cancellable_disconnect(cb_data->cancellable,
2062 cb_data->cancel_id);
2066 g_error_free(upnp_error);
2069 g_object_unref(parser);
2073 MSU_LOG_DEBUG("Exit");
2076 static void prv_get_ms2spec_prop(msu_device_context_t *context,
2077 msu_prop_map_t *prop_map,
2078 msu_task_get_prop_t *task_data,
2079 GCancellable *cancellable,
2080 msu_async_cb_data_t *cb_data)
2082 msu_async_get_prop_t *cb_task_data;
2083 const gchar *filter;
2085 MSU_LOG_DEBUG("Enter");
2087 cb_task_data = &cb_data->ut.get_prop;
2090 cb_data->error = g_error_new(MSU_ERROR,
2091 MSU_ERROR_UNKNOWN_PROPERTY,
2092 "Unknown property");
2096 filter = prop_map->filter ? prop_map->upnp_prop_name : "";
2098 if (!strcmp(MSU_INTERFACE_MEDIA_CONTAINER, task_data->interface_name)) {
2099 cb_task_data->prop_func =
2100 G_CALLBACK(prv_get_container_property);
2101 } else if (!strcmp(MSU_INTERFACE_MEDIA_ITEM,
2102 task_data->interface_name)) {
2103 cb_task_data->prop_func = G_CALLBACK(prv_get_item_property);
2104 } else if (!strcmp(MSU_INTERFACE_MEDIA_OBJECT,
2105 task_data->interface_name)) {
2106 cb_task_data->prop_func = G_CALLBACK(prv_get_object_property);
2107 } else if (!strcmp("", task_data->interface_name)) {
2108 cb_task_data->prop_func = G_CALLBACK(prv_get_all_property);
2110 MSU_LOG_WARNING("Interface is unknown.%s",
2111 task_data->interface_name);
2113 cb_data->error = g_error_new(MSU_ERROR,
2114 MSU_ERROR_UNKNOWN_INTERFACE,
2115 "Interface is unknown.");
2119 cb_data->action = gupnp_service_proxy_begin_action(
2120 context->service_proxy, "Browse",
2121 prv_get_ms2spec_prop_cb,
2123 "ObjectID", G_TYPE_STRING, cb_data->id,
2124 "BrowseFlag", G_TYPE_STRING,
2126 "Filter", G_TYPE_STRING, filter,
2127 "StartingIndex", G_TYPE_INT, 0,
2128 "RequestedCount", G_TYPE_INT, 0,
2129 "SortCriteria", G_TYPE_STRING,
2133 cb_data->proxy = context->service_proxy;
2135 cb_data->cancel_id =
2136 g_cancellable_connect(cancellable,
2137 G_CALLBACK(msu_async_task_cancelled),
2139 cb_data->cancellable = cancellable;
2141 MSU_LOG_DEBUG("Exit with SUCCESS");
2147 (void) g_idle_add(msu_async_complete_task, cb_data);
2149 MSU_LOG_DEBUG("Exit with FAIL");
2154 void msu_device_get_prop(msu_device_t *device, msu_client_t *client,
2155 msu_task_t *task, msu_async_cb_data_t *cb_data,
2156 msu_prop_map_t *prop_map, gboolean root_object,
2157 GCancellable *cancellable)
2159 msu_task_get_prop_t *task_data = &task->ut.get_prop;
2160 msu_device_context_t *context;
2161 gboolean complete = FALSE;
2163 MSU_LOG_DEBUG("Enter");
2165 context = msu_device_get_context(device, client);
2167 if (!strcmp(task_data->interface_name, MSU_INTERFACE_MEDIA_DEVICE)) {
2169 if (!strcmp(task_data->prop_name,
2170 MSU_INTERFACE_PROP_ESV_SYSTEM_UPDATE_ID)) {
2171 prv_get_system_update_id_for_prop(
2172 context->service_proxy,
2176 } else if (!strcmp(task_data->prop_name,
2177 MSU_INTERFACE_PROP_ESV_SERVICE_RESET_TOKEN)) {
2178 prv_get_sr_token_for_prop(
2179 context->service_proxy,
2185 msu_props_get_device_prop(
2187 context->device_proxy,
2189 task_data->prop_name);
2191 if (!cb_data->result)
2192 cb_data->error = g_error_new(
2194 MSU_ERROR_UNKNOWN_PROPERTY,
2195 "Unknown property");
2197 (void) g_idle_add(msu_async_complete_task,
2203 g_error_new(MSU_ERROR,
2204 MSU_ERROR_UNKNOWN_INTERFACE,
2205 "Interface is unknown.");
2207 (void) g_idle_add(msu_async_complete_task, cb_data);
2210 } else if (strcmp(task_data->interface_name, "")) {
2211 prv_get_ms2spec_prop(context, prop_map, &task->ut.get_prop,
2212 cancellable, cb_data);
2215 if (!strcmp(task_data->prop_name,
2216 MSU_INTERFACE_PROP_ESV_SYSTEM_UPDATE_ID)) {
2217 prv_get_system_update_id_for_prop(
2218 context->service_proxy,
2223 } else if (!strcmp(task_data->prop_name,
2224 MSU_INTERFACE_PROP_ESV_SERVICE_RESET_TOKEN)) {
2225 prv_get_sr_token_for_prop(
2226 context->service_proxy,
2232 cb_data->result = msu_props_get_device_prop(
2234 context->device_proxy,
2236 task_data->prop_name);
2237 if (cb_data->result) {
2239 msu_async_complete_task,
2247 prv_get_ms2spec_prop(context, prop_map,
2248 &task->ut.get_prop, cancellable,
2252 MSU_LOG_DEBUG("Exit");
2255 static void prv_found_target(GUPnPDIDLLiteParser *parser,
2256 GUPnPDIDLLiteObject *object,
2259 msu_async_cb_data_t *cb_data = user_data;
2260 msu_async_bas_t *cb_task_data = &cb_data->ut.bas;
2262 const char *parent_path;
2264 gboolean have_child_count;
2265 msu_device_object_builder_t *builder;
2267 MSU_LOG_DEBUG("Enter");
2269 builder = g_new0(msu_device_object_builder_t, 1);
2271 id = gupnp_didl_lite_object_get_parent_id(object);
2273 if (!id || !strcmp(id, "-1") || !strcmp(id, "")) {
2274 parent_path = cb_task_data->root_path;
2276 path = msu_path_from_id(cb_task_data->root_path, id);
2280 builder->vb = g_variant_builder_new(G_VARIANT_TYPE("a{sv}"));
2282 if (!msu_props_add_object(builder->vb, object, cb_task_data->root_path,
2283 parent_path, cb_task_data->filter_mask))
2286 if (GUPNP_IS_DIDL_LITE_CONTAINER(object)) {
2287 msu_props_add_container(builder->vb,
2288 (GUPnPDIDLLiteContainer *) object,
2289 cb_task_data->filter_mask,
2292 if (!have_child_count && (cb_task_data->filter_mask &
2293 MSU_UPNP_MASK_PROP_CHILD_COUNT)) {
2294 builder->needs_child_count = TRUE;
2295 builder->id = g_strdup(
2296 gupnp_didl_lite_object_get_id(object));
2297 cb_task_data->need_child_count = TRUE;
2300 msu_props_add_item(builder->vb,
2302 cb_task_data->root_path,
2303 cb_task_data->filter_mask,
2304 cb_task_data->protocol_info);
2307 g_ptr_array_add(cb_task_data->vbs, builder);
2310 MSU_LOG_DEBUG("Exit with SUCCESS");
2317 prv_msu_device_object_builder_delete(builder);
2319 MSU_LOG_DEBUG("Exit with FAIL");
2322 static void prv_search_cb(GUPnPServiceProxy *proxy,
2323 GUPnPServiceProxyAction *action,
2326 gchar *result = NULL;
2327 GUPnPDIDLLiteParser *parser = NULL;
2328 GError *upnp_error = NULL;
2329 msu_async_cb_data_t *cb_data = user_data;
2330 msu_async_bas_t *cb_task_data = &cb_data->ut.bas;
2332 MSU_LOG_DEBUG("Enter");
2334 if (!gupnp_service_proxy_end_action(cb_data->proxy, cb_data->action,
2336 "Result", G_TYPE_STRING,
2338 "TotalMatches", G_TYPE_INT,
2339 &cb_task_data->max_count,
2342 MSU_LOG_WARNING("Search operation failed %s",
2343 upnp_error->message);
2345 cb_data->error = g_error_new(MSU_ERROR,
2346 MSU_ERROR_OPERATION_FAILED,
2347 "Search operation failed: %s",
2348 upnp_error->message);
2352 parser = gupnp_didl_lite_parser_new();
2354 cb_task_data->vbs = g_ptr_array_new_with_free_func(
2355 prv_msu_device_object_builder_delete);
2357 g_signal_connect(parser, "object-available" ,
2358 G_CALLBACK(prv_found_target), cb_data);
2360 MSU_LOG_DEBUG("Server Search result: %s", result);
2362 if (!gupnp_didl_lite_parser_parse_didl(parser, result, &upnp_error)
2363 && upnp_error->code != GUPNP_XML_ERROR_EMPTY_NODE) {
2364 MSU_LOG_WARNING("Unable to parse results of search: %s",
2365 upnp_error->message);
2367 cb_data->error = g_error_new(MSU_ERROR,
2368 MSU_ERROR_OPERATION_FAILED,
2369 "Unable to parse results of "
2370 "search: %s", upnp_error->message);
2374 if (cb_task_data->need_child_count) {
2375 MSU_LOG_DEBUG("Need to retrieve child count");
2377 if (cb_data->task->multiple_retvals)
2378 cb_task_data->get_children_cb =
2379 prv_get_search_ex_result;
2381 cb_task_data->get_children_cb = prv_get_children_result;
2382 prv_retrieve_child_count_for_list(cb_data);
2385 if (cb_data->task->multiple_retvals)
2386 prv_get_search_ex_result(cb_data);
2388 prv_get_children_result(cb_data);
2393 (void) g_idle_add(msu_async_complete_task, cb_data);
2394 g_cancellable_disconnect(cb_data->cancellable, cb_data->cancel_id);
2399 g_object_unref(parser);
2404 g_error_free(upnp_error);
2406 MSU_LOG_DEBUG("Exit");
2409 void msu_device_search(msu_device_t *device, msu_client_t *client,
2410 msu_task_t *task, msu_async_cb_data_t *cb_data,
2411 const gchar *upnp_filter, const gchar *upnp_query,
2412 const gchar *sort_by, GCancellable *cancellable)
2414 msu_device_context_t *context;
2416 MSU_LOG_DEBUG("Enter");
2418 context = msu_device_get_context(device, client);
2420 cb_data->action = gupnp_service_proxy_begin_action(
2421 context->service_proxy, "Search",
2424 "ContainerID", G_TYPE_STRING, cb_data->id,
2425 "SearchCriteria", G_TYPE_STRING, upnp_query,
2426 "Filter", G_TYPE_STRING, upnp_filter,
2427 "StartingIndex", G_TYPE_INT, task->ut.search.start,
2428 "RequestedCount", G_TYPE_INT, task->ut.search.count,
2429 "SortCriteria", G_TYPE_STRING, sort_by,
2432 cb_data->proxy = context->service_proxy;
2434 cb_data->cancel_id =
2435 g_cancellable_connect(cancellable,
2436 G_CALLBACK(msu_async_task_cancelled),
2438 cb_data->cancellable = cancellable;
2440 MSU_LOG_DEBUG("Exit");
2443 static void prv_get_resource(GUPnPDIDLLiteParser *parser,
2444 GUPnPDIDLLiteObject *object,
2447 msu_async_cb_data_t *cb_data = user_data;
2448 msu_task_t *task = cb_data->task;
2449 msu_task_get_resource_t *task_data = &task->ut.resource;
2450 msu_async_get_all_t *cb_task_data = &cb_data->ut.get_all;
2452 MSU_LOG_DEBUG("Enter");
2454 msu_props_add_resource(cb_task_data->vb, object,
2455 cb_task_data->filter_mask,
2456 task_data->protocol_info);
2459 void msu_device_get_resource(msu_device_t *device, msu_client_t *client,
2460 msu_task_t *task, msu_async_cb_data_t *cb_data,
2461 const gchar *upnp_filter,
2462 GCancellable *cancellable)
2464 msu_async_get_all_t *cb_task_data;
2465 msu_device_context_t *context;
2467 context = msu_device_get_context(device, client);
2468 cb_task_data = &cb_data->ut.get_all;
2470 cb_task_data->vb = g_variant_builder_new(G_VARIANT_TYPE("a{sv}"));
2471 cb_task_data->prop_func = G_CALLBACK(prv_get_resource);
2472 cb_task_data->device_object = FALSE;
2474 cb_data->action = gupnp_service_proxy_begin_action(
2475 context->service_proxy, "Browse",
2476 prv_get_all_ms2spec_props_cb, cb_data,
2477 "ObjectID", G_TYPE_STRING, cb_data->id,
2478 "BrowseFlag", G_TYPE_STRING, "BrowseMetadata",
2479 "Filter", G_TYPE_STRING, upnp_filter,
2480 "StartingIndex", G_TYPE_INT, 0,
2481 "RequestedCount", G_TYPE_INT, 0,
2482 "SortCriteria", G_TYPE_STRING,
2485 cb_data->proxy = context->service_proxy;
2487 cb_data->cancel_id =
2488 g_cancellable_connect(cancellable,
2489 G_CALLBACK(msu_async_task_cancelled),
2491 cb_data->cancellable = cancellable;
2493 MSU_LOG_DEBUG("Exit");
2496 static gchar *prv_create_new_container_didl(const gchar *parent_id,
2499 GUPnPDIDLLiteWriter *writer;
2500 GUPnPDIDLLiteObject *item;
2501 GUPnPDIDLLiteContainer *container;
2504 GVariant *child_type;
2505 const gchar *actual_type;
2507 writer = gupnp_didl_lite_writer_new(NULL);
2508 item = GUPNP_DIDL_LITE_OBJECT(
2509 gupnp_didl_lite_writer_add_container(writer));
2510 container = GUPNP_DIDL_LITE_CONTAINER(item);
2512 gupnp_didl_lite_object_set_id(item, "");
2513 gupnp_didl_lite_object_set_title(item,
2514 task->ut.create_container.display_name);
2515 gupnp_didl_lite_object_set_parent_id(item, parent_id);
2516 actual_type = msu_props_media_spec_to_upnp_class(
2517 task->ut.create_container.type);
2518 gupnp_didl_lite_object_set_upnp_class(item, actual_type);
2519 gupnp_didl_lite_object_set_restricted(item, FALSE);
2520 gupnp_didl_lite_object_set_dlna_managed(item, GUPNP_OCM_FLAGS_UPLOAD);
2522 g_variant_iter_init(&iter, task->ut.create_container.child_types);
2523 while ((child_type = g_variant_iter_next_value(&iter))) {
2524 actual_type = msu_props_media_spec_to_upnp_class(
2525 g_variant_get_string(child_type, NULL));
2526 if (actual_type != NULL)
2527 gupnp_didl_lite_container_add_create_class(container,
2529 g_variant_unref(child_type);
2532 retval = gupnp_didl_lite_writer_get_string(writer);
2534 g_object_unref(item);
2535 g_object_unref(writer);
2540 static gchar *prv_create_upload_didl(const gchar *parent_id, msu_task_t *task,
2541 const gchar *object_class,
2542 const gchar *mime_type)
2544 GUPnPDIDLLiteWriter *writer;
2545 GUPnPDIDLLiteObject *item;
2547 GUPnPProtocolInfo *protocol_info;
2548 GUPnPDIDLLiteResource *res;
2550 writer = gupnp_didl_lite_writer_new(NULL);
2551 item = GUPNP_DIDL_LITE_OBJECT(gupnp_didl_lite_writer_add_item(writer));
2553 gupnp_didl_lite_object_set_id(item, "");
2554 gupnp_didl_lite_object_set_title(item, task->ut.upload.display_name);
2555 gupnp_didl_lite_object_set_parent_id(item, parent_id);
2556 gupnp_didl_lite_object_set_upnp_class(item, object_class);
2557 gupnp_didl_lite_object_set_restricted(item, FALSE);
2559 protocol_info = gupnp_protocol_info_new();
2560 gupnp_protocol_info_set_mime_type(protocol_info, mime_type);
2561 gupnp_protocol_info_set_protocol(protocol_info, "*");
2562 gupnp_protocol_info_set_network(protocol_info, "*");
2564 res = gupnp_didl_lite_object_add_resource(item);
2565 gupnp_didl_lite_resource_set_protocol_info(res, protocol_info);
2567 /* TODO: Need to compute DLNA Profile */
2569 retval = gupnp_didl_lite_writer_get_string(writer);
2571 g_object_unref(res);
2572 g_object_unref(protocol_info);
2573 g_object_unref(item);
2574 g_object_unref(writer);
2579 static void prv_extract_import_uri(GUPnPDIDLLiteParser *parser,
2580 GUPnPDIDLLiteObject *object,
2583 gchar **import_uri = user_data;
2586 GUPnPDIDLLiteResource *res;
2590 resources = gupnp_didl_lite_object_get_resources(object);
2595 uri = gupnp_didl_lite_resource_get_import_uri(
2598 *import_uri = g_strdup(uri);
2600 g_object_unref(res);
2604 g_list_free(resources);
2608 static void prv_upload_delete_cb(GUPnPServiceProxy *proxy,
2609 GUPnPServiceProxyAction *action,
2612 msu_async_cb_data_t *cb_data = user_data;
2614 MSU_LOG_DEBUG("Enter");
2616 (void) gupnp_service_proxy_end_action(cb_data->proxy, cb_data->action,
2618 (void) g_idle_add(msu_async_complete_task, cb_data);
2619 g_cancellable_disconnect(cb_data->cancellable, cb_data->cancel_id);
2621 MSU_LOG_DEBUG("Exit");
2624 static void prv_msu_upload_job_delete(gpointer up_job)
2626 msu_device_upload_job_t *upload_job = up_job;
2629 if (upload_job->remove_idle)
2630 (void) g_source_remove(upload_job->remove_idle);
2636 static gboolean prv_remove_update_job(gpointer user_data)
2638 msu_device_upload_job_t *upload_job = user_data;
2639 msu_device_upload_t *upload;
2641 upload = g_hash_table_lookup(upload_job->device->uploads,
2642 &upload_job->upload_id);
2644 g_hash_table_remove(upload_job->device->uploads,
2645 &upload_job->upload_id);
2647 MSU_LOG_DEBUG("Removing Upload Object: %d",
2648 upload_job->upload_id);
2651 upload_job->remove_idle = 0;
2652 g_hash_table_remove(upload_job->device->upload_jobs,
2653 &upload_job->upload_id);
2658 static void prv_generate_upload_update(msu_device_upload_job_t *upload_job,
2659 msu_device_upload_t *upload)
2663 args = g_variant_new("(ustt)", upload_job->upload_id, upload->status,
2664 upload->bytes_uploaded, upload->bytes_to_upload);
2667 "Emitting: %s (%u %s %"G_GUINT64_FORMAT" %"G_GUINT64_FORMAT")"
2669 MSU_INTERFACE_UPLOAD_UPDATE, upload_job->upload_id,
2670 upload->status, upload->bytes_uploaded,
2671 upload->bytes_to_upload, upload_job->device->path);
2673 (void) g_dbus_connection_emit_signal(upload_job->device->connection,
2675 upload_job->device->path,
2676 MSU_INTERFACE_MEDIA_DEVICE,
2677 MSU_INTERFACE_UPLOAD_UPDATE,
2682 static void prv_post_finished(SoupSession *session, SoupMessage *msg,
2685 msu_device_upload_job_t *upload_job = user_data;
2686 msu_device_upload_t *upload;
2689 MSU_LOG_DEBUG("Enter");
2691 MSU_LOG_DEBUG("Upload %u finished. Code %u Message %s",
2692 upload_job->upload_id, msg->status_code,
2693 msg->reason_phrase);
2695 /* This is clumsy but we need to distinguish between two cases:
2696 1. We cancel because the process is exitting.
2697 2. We cancel because a client has called CancelUpload.
2699 We could use custom SOUP error messages to distinguish the cases
2700 but device->shutting_down seemed less hacky.
2702 We need this check as if we are shutting down it is
2703 dangerous to manipulate uploads as we are being called from its
2707 if (upload_job->device->shutting_down) {
2708 MSU_LOG_DEBUG("Device shutting down. Cancelling Upload");
2712 upload = g_hash_table_lookup(upload_job->device->uploads,
2713 &upload_job->upload_id);
2715 upload_job->remove_idle =
2716 g_timeout_add(30000, prv_remove_update_job, user_data);
2718 if (SOUP_STATUS_IS_SUCCESSFUL(msg->status_code)) {
2719 upload->status = MSU_UPLOAD_STATUS_COMPLETED;
2720 upload->bytes_uploaded = upload->bytes_to_upload;
2721 } else if (msg->status_code == SOUP_STATUS_CANCELLED) {
2722 upload->status = MSU_UPLOAD_STATUS_CANCELLED;
2724 upload->status = MSU_UPLOAD_STATUS_ERROR;
2727 MSU_LOG_DEBUG("Upload Status: %s", upload->status);
2729 prv_generate_upload_update(upload_job, upload);
2731 g_object_unref(upload->msg);
2734 g_object_unref(upload->soup_session);
2735 upload->soup_session = NULL;
2737 g_mapped_file_unref(upload->mapped_file);
2738 upload->mapped_file = NULL;
2740 upload_id = g_new(gint, 1);
2741 *upload_id = upload_job->upload_id;
2743 g_hash_table_insert(upload_job->device->upload_jobs, upload_id,
2751 prv_msu_upload_job_delete(upload_job);
2753 MSU_LOG_DEBUG("Exit");
2756 static void prv_msu_device_upload_delete(gpointer up)
2758 msu_device_upload_t *upload = up;
2760 MSU_LOG_DEBUG("Enter");
2764 soup_session_cancel_message(upload->soup_session,
2766 SOUP_STATUS_CANCELLED);
2767 g_object_unref(upload->msg);
2770 if (upload->soup_session)
2771 g_object_unref(upload->soup_session);
2773 if (upload->mapped_file)
2774 g_mapped_file_unref(upload->mapped_file);
2779 MSU_LOG_DEBUG("Exit");
2782 static void prv_post_bytes_written(SoupMessage *msg, SoupBuffer *chunk,
2785 msu_device_upload_t *upload = user_data;
2787 upload->bytes_uploaded += chunk->length;
2788 if (upload->bytes_uploaded > upload->bytes_to_upload)
2789 upload->bytes_uploaded = upload->bytes_to_upload;
2792 static msu_device_upload_t *prv_msu_device_upload_new(const gchar *file_path,
2793 const gchar *import_uri,
2794 const gchar *mime_type,
2799 msu_device_upload_t *upload;
2801 MSU_LOG_DEBUG("Enter");
2803 upload = g_new0(msu_device_upload_t, 1);
2805 upload->mapped_file = g_mapped_file_new(file_path, FALSE, NULL);
2806 if (!upload->mapped_file) {
2807 MSU_LOG_WARNING("Unable to map %s into memory", file_path);
2809 *error = g_error_new(MSU_ERROR, MSU_ERROR_IO,
2810 "Unable to map %s into memory",
2815 body = g_mapped_file_get_contents(upload->mapped_file);
2816 body_length = g_mapped_file_get_length(upload->mapped_file);
2818 upload->soup_session = soup_session_async_new();
2819 upload->msg = soup_message_new("POST", import_uri);
2822 MSU_LOG_WARNING("Invalid URI %s", import_uri);
2824 *error = g_error_new(MSU_ERROR, MSU_ERROR_BAD_RESULT,
2825 "Invalid URI %s", import_uri);
2828 upload->status = MSU_UPLOAD_STATUS_IN_PROGRESS;
2829 upload->bytes_to_upload = body_length;
2831 soup_message_headers_set_expectations(upload->msg->request_headers,
2832 SOUP_EXPECTATION_CONTINUE);
2834 soup_message_set_request(upload->msg, mime_type, SOUP_MEMORY_STATIC,
2836 g_signal_connect(upload->msg, "wrote-body-data",
2837 G_CALLBACK(prv_post_bytes_written), upload);
2839 MSU_LOG_DEBUG("Exit with Success");
2845 prv_msu_device_upload_delete(upload);
2847 MSU_LOG_WARNING("Exit with Fail");
2852 static void prv_create_container_cb(GUPnPServiceProxy *proxy,
2853 GUPnPServiceProxyAction *action,
2856 msu_async_cb_data_t *cb_data = user_data;
2857 msu_async_create_container_t *cb_task_data;
2858 GError *upnp_error = NULL;
2859 gchar *result = NULL;
2860 gchar *object_id = NULL;
2863 MSU_LOG_DEBUG("Enter");
2865 cb_task_data = &cb_data->ut.create_container;
2867 if (!gupnp_service_proxy_end_action(cb_data->proxy, cb_data->action,
2869 "ObjectID", G_TYPE_STRING,
2871 "Result", G_TYPE_STRING,
2874 MSU_LOG_ERROR("Create Object operation failed: %s",
2875 upnp_error->message);
2877 cb_data->error = g_error_new(MSU_ERROR,
2878 MSU_ERROR_OPERATION_FAILED,
2879 "Create Object operation "
2881 upnp_error->message);
2885 object_path = msu_path_from_id(cb_task_data->root_path, object_id);
2886 cb_data->result = g_variant_ref_sink(g_variant_new_object_path(
2888 g_free(object_path);
2892 (void) g_idle_add(msu_async_complete_task, cb_data);
2893 g_cancellable_disconnect(cb_data->cancellable, cb_data->cancel_id);
2902 g_error_free(upnp_error);
2904 MSU_LOG_DEBUG("Exit");
2907 static void prv_create_object_cb(GUPnPServiceProxy *proxy,
2908 GUPnPServiceProxyAction *action,
2911 gchar *result = NULL;
2912 GUPnPDIDLLiteParser *parser = NULL;
2913 GError *upnp_error = NULL;
2914 msu_async_cb_data_t *cb_data = user_data;
2915 msu_async_upload_t *cb_task_data = &cb_data->ut.upload;
2916 gchar *import_uri = NULL;
2917 gchar *object_id = NULL;
2918 gboolean delete_needed = FALSE;
2919 GVariant *out_params[2];
2921 msu_device_upload_t *upload;
2923 msu_device_upload_job_t *upload_job;
2925 MSU_LOG_DEBUG("Enter");
2927 if (!gupnp_service_proxy_end_action(cb_data->proxy, cb_data->action,
2929 "ObjectID", G_TYPE_STRING,
2931 "Result", G_TYPE_STRING,
2934 MSU_LOG_WARNING("Create Object operation failed: %s",
2935 upnp_error->message);
2937 cb_data->error = g_error_new(MSU_ERROR,
2938 MSU_ERROR_OPERATION_FAILED,
2939 "Create Object operation "
2941 upnp_error->message);
2945 delete_needed = TRUE;
2946 parser = gupnp_didl_lite_parser_new();
2948 g_signal_connect(parser, "object-available" ,
2949 G_CALLBACK(prv_extract_import_uri), &import_uri);
2951 MSU_LOG_DEBUG("Create Object Result: %s", result);
2953 if (!gupnp_didl_lite_parser_parse_didl(parser, result, &upnp_error)
2954 && upnp_error->code != GUPNP_XML_ERROR_EMPTY_NODE) {
2956 MSU_LOG_WARNING("Unable to parse results of CreateObject: %s",
2957 upnp_error->message);
2959 cb_data->error = g_error_new(MSU_ERROR,
2960 MSU_ERROR_OPERATION_FAILED,
2961 "Unable to parse results of "
2963 upnp_error->message);
2968 MSU_LOG_WARNING("Missing Import URI");
2970 cb_data->error = g_error_new(MSU_ERROR,
2971 MSU_ERROR_OPERATION_FAILED,
2972 "Missing Import URI");
2976 MSU_LOG_DEBUG("Import URI %s", import_uri);
2978 upload = prv_msu_device_upload_new(cb_data->task->ut.upload.file_path,
2979 import_uri, cb_task_data->mime_type,
2984 upload_job = g_new0(msu_device_upload_job_t, 1);
2985 upload_job->device = cb_task_data->device;
2986 upload_job->upload_id = (gint) cb_task_data->device->upload_id;
2988 soup_session_queue_message(upload->soup_session, upload->msg,
2989 prv_post_finished, upload_job);
2990 g_object_ref(upload->msg);
2992 upload_id = g_new(gint, 1);
2993 *upload_id = upload_job->upload_id;
2994 g_hash_table_insert(cb_task_data->device->uploads, upload_id, upload);
2996 object_path = msu_path_from_id(cb_task_data->root_path, object_id);
2998 MSU_LOG_DEBUG("Upload ID %u", *upload_id);
2999 MSU_LOG_DEBUG("Object ID %s", object_id);
3000 MSU_LOG_DEBUG("Object Path %s", object_path);
3002 out_params[0] = g_variant_new_uint32(*upload_id);
3003 out_params[1] = g_variant_new_object_path(object_path);
3004 cb_data->result = g_variant_ref_sink(g_variant_new_tuple(out_params,
3007 ++cb_task_data->device->upload_id;
3008 if (cb_task_data->device->upload_id > G_MAXINT)
3009 cb_task_data->device->upload_id = 0;
3011 g_free(object_path);
3015 if (cb_data->error && delete_needed) {
3016 MSU_LOG_WARNING("Upload failed deleting created object "
3017 "with id %s", object_id);
3019 cb_data->action = gupnp_service_proxy_begin_action(
3020 cb_data->proxy, "DestroyObject", prv_upload_delete_cb,
3021 cb_data, "ObjectID", G_TYPE_STRING,
3024 (void) g_idle_add(msu_async_complete_task, cb_data);
3025 g_cancellable_disconnect(cb_data->cancellable,
3026 cb_data->cancel_id);
3033 g_object_unref(parser);
3038 g_error_free(upnp_error);
3040 MSU_LOG_DEBUG("Exit");
3043 void msu_device_upload(msu_device_t *device, msu_client_t *client,
3044 msu_task_t *task, const gchar *parent_id,
3045 msu_async_cb_data_t *cb_data, GCancellable *cancellable)
3047 msu_device_context_t *context;
3049 msu_async_upload_t *cb_task_data;
3051 MSU_LOG_DEBUG("Enter");
3052 MSU_LOG_DEBUG("Uploading file to %s", parent_id);
3054 context = msu_device_get_context(device, client);
3055 cb_task_data = &cb_data->ut.upload;
3057 didl = prv_create_upload_didl(parent_id, task,
3058 cb_task_data->object_class,
3059 cb_task_data->mime_type);
3061 MSU_LOG_DEBUG("DIDL: %s", didl);
3063 cb_data->action = gupnp_service_proxy_begin_action(
3064 context->service_proxy, "CreateObject",
3065 prv_create_object_cb, cb_data,
3066 "ContainerID", G_TYPE_STRING, parent_id,
3067 "Elements", G_TYPE_STRING, didl,
3070 cb_data->proxy = context->service_proxy;
3071 cb_task_data->device = device;
3073 cb_data->cancel_id =
3074 g_cancellable_connect(cancellable,
3075 G_CALLBACK(msu_async_task_cancelled),
3077 cb_data->cancellable = cancellable;
3081 MSU_LOG_DEBUG("Exit");
3084 gboolean msu_device_get_upload_status(msu_device_t *device,
3085 msu_task_t *task, GError **error)
3087 msu_device_upload_t *upload;
3088 gboolean retval = FALSE;
3089 GVariant *out_params[3];
3092 MSU_LOG_DEBUG("Enter");
3094 upload_id = task->ut.upload_action.upload_id;
3096 upload = g_hash_table_lookup(device->uploads, &upload_id);
3098 *error = g_error_new(MSU_ERROR, MSU_ERROR_OBJECT_NOT_FOUND,
3099 "Unknown Upload ID %u ", upload_id);
3103 out_params[0] = g_variant_new_string(upload->status);
3104 out_params[1] = g_variant_new_uint64(upload->bytes_uploaded);
3105 out_params[2] = g_variant_new_uint64(upload->bytes_to_upload);
3107 MSU_LOG_DEBUG("Upload ( %s %"G_GUINT64_FORMAT" %"G_GUINT64_FORMAT" )",
3108 upload->status, upload->bytes_uploaded,
3109 upload->bytes_to_upload);
3111 task->result = g_variant_ref_sink(g_variant_new_tuple(out_params, 3));
3117 MSU_LOG_DEBUG("Exit");
3122 void msu_device_get_upload_ids(msu_device_t *device, msu_task_t *task)
3125 GHashTableIter iter;
3128 MSU_LOG_DEBUG("Enter");
3130 g_variant_builder_init(&vb, G_VARIANT_TYPE("au"));
3132 g_hash_table_iter_init(&iter, device->uploads);
3133 while (g_hash_table_iter_next(&iter, &key, NULL))
3134 g_variant_builder_add(&vb, "u", (guint32) (*((gint *) key)));
3136 task->result = g_variant_ref_sink(g_variant_builder_end(&vb));
3138 MSU_LOG_DEBUG("Exit");
3141 gboolean msu_device_cancel_upload(msu_device_t *device, msu_task_t *task,
3144 msu_device_upload_t *upload;
3145 gboolean retval = FALSE;
3148 MSU_LOG_DEBUG("Enter");
3150 upload_id = task->ut.upload_action.upload_id;
3152 upload = g_hash_table_lookup(device->uploads, &upload_id);
3154 *error = g_error_new(MSU_ERROR, MSU_ERROR_OBJECT_NOT_FOUND,
3155 "Unknown Upload ID %u ", upload_id);
3160 soup_session_cancel_message(upload->soup_session, upload->msg,
3161 SOUP_STATUS_CANCELLED);
3162 MSU_LOG_DEBUG("Cancelling Upload %u ", upload_id);
3169 MSU_LOG_DEBUG("Exit");
3174 static void prv_destroy_object_cb(GUPnPServiceProxy *proxy,
3175 GUPnPServiceProxyAction *action,
3178 GError *upnp_error = NULL;
3179 msu_async_cb_data_t *cb_data = user_data;
3181 MSU_LOG_DEBUG("Enter");
3183 if (!gupnp_service_proxy_end_action(cb_data->proxy, cb_data->action,
3186 MSU_LOG_WARNING("Destroy Object operation failed: %s",
3187 upnp_error->message);
3189 cb_data->error = g_error_new(MSU_ERROR,
3190 MSU_ERROR_OPERATION_FAILED,
3191 "Destroy Object operation "
3193 upnp_error->message);
3196 (void) g_idle_add(msu_async_complete_task, cb_data);
3197 g_cancellable_disconnect(cb_data->cancellable, cb_data->cancel_id);
3200 g_error_free(upnp_error);
3202 MSU_LOG_DEBUG("Exit");
3205 void msu_device_delete_object(msu_device_t *device, msu_client_t *client,
3207 msu_async_cb_data_t *cb_data,
3208 GCancellable *cancellable)
3210 msu_device_context_t *context;
3212 MSU_LOG_DEBUG("Enter");
3214 context = msu_device_get_context(device, client);
3216 cb_data->action = gupnp_service_proxy_begin_action(
3217 context->service_proxy, "DestroyObject",
3218 prv_destroy_object_cb, cb_data,
3219 "ObjectID", G_TYPE_STRING, cb_data->id,
3222 cb_data->proxy = context->service_proxy;
3224 cb_data->cancel_id = g_cancellable_connect(cancellable,
3225 G_CALLBACK(msu_async_task_cancelled),
3227 cb_data->cancellable = cancellable;
3229 MSU_LOG_DEBUG("Exit");
3232 void msu_device_create_container(msu_device_t *device, msu_client_t *client,
3234 const gchar *parent_id,
3235 msu_async_cb_data_t *cb_data,
3236 GCancellable *cancellable)
3238 msu_device_context_t *context;
3241 MSU_LOG_DEBUG("Enter");
3243 context = msu_device_get_context(device, client);
3245 didl = prv_create_new_container_didl(parent_id, task);
3247 MSU_LOG_DEBUG("DIDL: %s", didl);
3249 cb_data->action = gupnp_service_proxy_begin_action(
3250 context->service_proxy, "CreateObject",
3251 prv_create_container_cb, cb_data,
3252 "ContainerID", G_TYPE_STRING, parent_id,
3253 "Elements", G_TYPE_STRING, didl,
3256 cb_data->proxy = context->service_proxy;
3258 cb_data->cancel_id =
3259 g_cancellable_connect(cancellable,
3260 G_CALLBACK(msu_async_task_cancelled),
3262 cb_data->cancellable = cancellable;
3266 MSU_LOG_DEBUG("Exit");
3269 static void prv_update_object_update_cb(GUPnPServiceProxy *proxy,
3270 GUPnPServiceProxyAction *action,
3273 GError *upnp_error = NULL;
3274 msu_async_cb_data_t *cb_data = user_data;
3276 MSU_LOG_DEBUG("Enter");
3278 if (!gupnp_service_proxy_end_action(cb_data->proxy, cb_data->action,
3281 MSU_LOG_WARNING("Update Object operation failed: %s",
3282 upnp_error->message);
3284 cb_data->error = g_error_new(MSU_ERROR,
3285 MSU_ERROR_OPERATION_FAILED,
3286 "Update Object operation "
3288 upnp_error->message);
3291 (void) g_idle_add(msu_async_complete_task, cb_data);
3292 g_cancellable_disconnect(cb_data->cancellable, cb_data->cancel_id);
3295 g_error_free(upnp_error);
3297 MSU_LOG_DEBUG("Exit");
3300 static gchar *prv_get_current_xml_fragment(GUPnPDIDLLiteObject *object,
3303 gchar *retval = NULL;
3305 if (mask & MSU_UPNP_MASK_PROP_DISPLAY_NAME)
3306 retval = gupnp_didl_lite_object_get_title_xml_string(object);
3307 else if (mask & MSU_UPNP_MASK_PROP_ALBUM)
3308 retval = gupnp_didl_lite_object_get_album_xml_string(object);
3309 else if (mask & MSU_UPNP_MASK_PROP_DATE)
3310 retval = gupnp_didl_lite_object_get_date_xml_string(object);
3311 else if (mask & MSU_UPNP_MASK_PROP_TYPE)
3312 retval = gupnp_didl_lite_object_get_upnp_class_xml_string(
3314 else if (mask & MSU_UPNP_MASK_PROP_TRACK_NUMBER)
3315 retval = gupnp_didl_lite_object_get_track_number_xml_string(
3317 else if (mask & MSU_UPNP_MASK_PROP_ARTISTS)
3318 retval = gupnp_didl_lite_object_get_artists_xml_string(object);
3323 static gchar *prv_get_new_xml_fragment(GUPnPDIDLLiteObject *object,
3327 GUPnPDIDLLiteContributor *artist;
3328 const gchar *artist_name;
3329 const gchar *upnp_class;
3331 gchar *retval = NULL;
3333 if (mask & MSU_UPNP_MASK_PROP_DISPLAY_NAME) {
3334 gupnp_didl_lite_object_set_title(object,
3335 g_variant_get_string(value, NULL));
3337 retval = gupnp_didl_lite_object_get_title_xml_string(object);
3338 } else if (mask & MSU_UPNP_MASK_PROP_ALBUM) {
3339 gupnp_didl_lite_object_set_album(object,
3340 g_variant_get_string(value, NULL));
3342 retval = gupnp_didl_lite_object_get_album_xml_string(object);
3343 } else if (mask & MSU_UPNP_MASK_PROP_DATE) {
3344 gupnp_didl_lite_object_set_date(object,
3345 g_variant_get_string(value, NULL));
3347 retval = gupnp_didl_lite_object_get_date_xml_string(object);
3348 } else if (mask & MSU_UPNP_MASK_PROP_TYPE) {
3349 upnp_class = msu_props_media_spec_to_upnp_class(
3350 g_variant_get_string(value, NULL));
3352 gupnp_didl_lite_object_set_upnp_class(object, upnp_class);
3354 retval = gupnp_didl_lite_object_get_upnp_class_xml_string(
3356 } else if (mask & MSU_UPNP_MASK_PROP_TRACK_NUMBER) {
3357 gupnp_didl_lite_object_set_track_number(object,
3358 g_variant_get_int32(value));
3360 retval = gupnp_didl_lite_object_get_track_number_xml_string(
3362 } else if (mask & MSU_UPNP_MASK_PROP_ARTISTS) {
3363 gupnp_didl_lite_object_unset_artists(object);
3365 (void) g_variant_iter_init(&viter, value);
3367 while (g_variant_iter_next(&viter, "&s", &artist_name)) {
3368 artist = gupnp_didl_lite_object_add_artist(object);
3370 gupnp_didl_lite_contributor_set_name(artist,
3374 retval = gupnp_didl_lite_object_get_artists_xml_string(object);
3380 static void prv_get_xml_fragments(GUPnPDIDLLiteParser *parser,
3381 GUPnPDIDLLiteObject *object,
3384 GString *current_str;
3391 msu_prop_map_t *prop_map;
3392 GUPnPDIDLLiteWriter *writer;
3393 GUPnPDIDLLiteObject *scratch_object;
3394 gboolean first = TRUE;
3395 msu_async_cb_data_t *cb_data = user_data;
3396 msu_async_update_t *cb_task_data = &cb_data->ut.update;
3397 msu_task_t *task = cb_data->task;
3398 msu_task_update_t *task_data = &task->ut.update;
3400 MSU_LOG_DEBUG("Enter");
3402 current_str = g_string_new("");
3403 new_str = g_string_new("");
3405 writer = gupnp_didl_lite_writer_new(NULL);
3406 if (GUPNP_IS_DIDL_LITE_CONTAINER(object))
3407 scratch_object = GUPNP_DIDL_LITE_OBJECT(
3408 gupnp_didl_lite_writer_add_container(writer));
3410 scratch_object = GUPNP_DIDL_LITE_OBJECT(
3411 gupnp_didl_lite_writer_add_item(writer));
3413 (void) g_variant_iter_init(&viter, task_data->to_add_update);
3415 while (g_variant_iter_next(&viter, "{&sv}", &prop, &value)) {
3417 MSU_LOG_DEBUG("to_add_update = %s", prop);
3419 prop_map = g_hash_table_lookup(cb_task_data->map, prop);
3421 frag1 = prv_get_current_xml_fragment(object, prop_map->type);
3422 frag2 = prv_get_new_xml_fragment(scratch_object, prop_map->type,
3426 MSU_LOG_DEBUG("Unable to set %s. Skipping", prop);
3433 g_string_append(current_str, ",");
3434 g_string_append(new_str, ",");
3440 g_string_append(current_str, (const gchar *) frag1);
3444 g_string_append(new_str, (const gchar *) frag2);
3448 (void) g_variant_iter_init(&viter, task_data->to_delete);
3450 while (g_variant_iter_next(&viter, "&s", &prop)) {
3451 MSU_LOG_DEBUG("to_delete = %s", prop);
3453 prop_map = g_hash_table_lookup(cb_task_data->map, prop);
3455 frag1 = prv_get_current_xml_fragment(object, prop_map->type);
3460 g_string_append(current_str, ",");
3464 g_string_append(current_str, (const gchar *) frag1);
3468 cb_task_data->current_tag_value = g_string_free(current_str, FALSE);
3469 MSU_LOG_DEBUG("current_tag_value = %s",
3470 cb_task_data->current_tag_value);
3472 cb_task_data->new_tag_value = g_string_free(new_str, FALSE);
3473 MSU_LOG_DEBUG("new_tag_value = %s", cb_task_data->new_tag_value);
3475 g_object_unref(scratch_object);
3476 g_object_unref(writer);
3478 MSU_LOG_DEBUG("Exit");
3481 static void prv_update_object_browse_cb(GUPnPServiceProxy *proxy,
3482 GUPnPServiceProxyAction *action,
3485 GError *upnp_error = NULL;
3486 msu_async_cb_data_t *cb_data = user_data;
3487 msu_async_update_t *cb_task_data = &cb_data->ut.update;
3488 GUPnPDIDLLiteParser *parser = NULL;
3489 gchar *result = NULL;
3491 MSU_LOG_DEBUG("Enter");
3493 if (!gupnp_service_proxy_end_action(cb_data->proxy, cb_data->action,
3495 "Result", G_TYPE_STRING,
3497 MSU_LOG_WARNING("Browse Object operation failed: %s",
3498 upnp_error->message);
3500 cb_data->error = g_error_new(MSU_ERROR,
3501 MSU_ERROR_OPERATION_FAILED,
3502 "Browse operation failed: %s",
3503 upnp_error->message);
3507 MSU_LOG_DEBUG("msu_device_update_ex_object result: %s", result);
3509 parser = gupnp_didl_lite_parser_new();
3511 g_signal_connect(parser, "object-available",
3512 G_CALLBACK(prv_get_xml_fragments),
3515 if (!gupnp_didl_lite_parser_parse_didl(parser, result, &upnp_error)) {
3516 if (upnp_error->code == GUPNP_XML_ERROR_EMPTY_NODE) {
3517 MSU_LOG_WARNING("Property not defined for object");
3520 g_error_new(MSU_ERROR,
3521 MSU_ERROR_UNKNOWN_PROPERTY,
3522 "Property not defined for object");
3524 MSU_LOG_WARNING("Unable to parse results of browse: %s",
3525 upnp_error->message);
3528 g_error_new(MSU_ERROR,
3529 MSU_ERROR_OPERATION_FAILED,
3530 "Unable to parse results of "
3532 upnp_error->message);
3538 cb_data->action = gupnp_service_proxy_begin_action(
3539 cb_data->proxy, "UpdateObject",
3540 prv_update_object_update_cb, cb_data,
3541 "ObjectID", G_TYPE_STRING, cb_data->id,
3542 "CurrentTagValue", G_TYPE_STRING,
3543 cb_task_data->current_tag_value,
3544 "NewTagValue", G_TYPE_STRING, cb_task_data->new_tag_value,
3551 (void) g_idle_add(msu_async_complete_task, cb_data);
3552 g_cancellable_disconnect(cb_data->cancellable, cb_data->cancel_id);
3557 g_object_unref(parser);
3562 g_error_free(upnp_error);
3564 MSU_LOG_DEBUG("Exit");
3567 void msu_device_update_object(msu_device_t *device, msu_client_t *client,
3569 msu_async_cb_data_t *cb_data,
3570 const gchar *upnp_filter,
3571 GCancellable *cancellable)
3573 msu_device_context_t *context;
3575 MSU_LOG_DEBUG("Enter");
3577 context = msu_device_get_context(device, client);
3579 cb_data->action = gupnp_service_proxy_begin_action(
3580 context->service_proxy, "Browse",
3581 prv_update_object_browse_cb, cb_data,
3582 "ObjectID", G_TYPE_STRING, cb_data->id,
3583 "BrowseFlag", G_TYPE_STRING, "BrowseMetadata",
3584 "Filter", G_TYPE_STRING, upnp_filter,
3585 "StartingIndex", G_TYPE_INT, 0,
3586 "RequestedCount", G_TYPE_INT, 0,
3587 "SortCriteria", G_TYPE_STRING,
3590 cb_data->proxy = context->service_proxy;
3592 cb_data->cancel_id = g_cancellable_connect(cancellable,
3593 G_CALLBACK(msu_async_task_cancelled),
3596 cb_data->cancellable = cancellable;
3598 MSU_LOG_DEBUG("Exit");