summaryrefslogtreecommitdiff
path: root/libdleyna/server
diff options
context:
space:
mode:
authorRegis Merlino <regis.merlino@intel.com>2013-03-11 10:52:35 -0700
committerRegis Merlino <regis.merlino@intel.com>2013-03-11 10:52:35 -0700
commitd929e2f2b70447e3589952d8c69688880b7b514d (patch)
tree935e700c96d309003009e236b58860461efc3918 /libdleyna/server
parent6b9249f2b95ee7e01546bce4ea6366c4e7aa25f7 (diff)
downloaddleyna-server-d929e2f2b70447e3589952d8c69688880b7b514d.tar.gz
[Architecture] Change directory structure to enable build from a master project
Signed-off-by: Regis Merlino <regis.merlino@intel.com>
Diffstat (limited to 'libdleyna/server')
-rw-r--r--libdleyna/server/Makefile.am64
-rw-r--r--libdleyna/server/async.c99
-rw-r--r--libdleyna/server/async.h113
-rw-r--r--libdleyna/server/client.h34
-rw-r--r--libdleyna/server/control-point-server.h30
-rw-r--r--libdleyna/server/device.c4348
-rw-r--r--libdleyna/server/device.h129
-rw-r--r--libdleyna/server/dleyna-server-1.0.pc.in11
-rw-r--r--libdleyna/server/dleyna-server-service.conf.in37
-rw-r--r--libdleyna/server/interface.h191
-rw-r--r--libdleyna/server/path.c154
-rw-r--r--libdleyna/server/path.h36
-rw-r--r--libdleyna/server/props.c1792
-rw-r--r--libdleyna/server/props.h134
-rw-r--r--libdleyna/server/search.c148
-rw-r--r--libdleyna/server/search.h31
-rw-r--r--libdleyna/server/server.c1199
-rw-r--r--libdleyna/server/server.h47
-rw-r--r--libdleyna/server/sort.c98
-rw-r--r--libdleyna/server/sort.h31
-rw-r--r--libdleyna/server/task.c618
-rw-r--r--libdleyna/server/task.h267
-rw-r--r--libdleyna/server/upnp.c1110
-rw-r--r--libdleyna/server/upnp.h109
24 files changed, 10830 insertions, 0 deletions
diff --git a/libdleyna/server/Makefile.am b/libdleyna/server/Makefile.am
new file mode 100644
index 0000000..fcdba64
--- /dev/null
+++ b/libdleyna/server/Makefile.am
@@ -0,0 +1,64 @@
+libdleyna_serverincdir = $(includedir)/dleyna-1.0/libdleyna/server
+
+DLEYNA_SERVER_VERSION = 1:0:0
+
+AM_CFLAGS = $(GLIB_CFLAGS) \
+ $(GIO_CFLAGS) \
+ $(DLEYNA_CORE_CFLAGS) \
+ $(GSSDP_CFLAGS) \
+ $(GUPNP_CFLAGS) \
+ $(GUPNPAV_CFLAGS) \
+ $(GUPNPDLNA_CFLAGS) \
+ $(SOUP_CFLAGS) \
+ -include config.h
+
+ACLOCAL_AMFLAGS = -I m4 ${ACLOCAL_FLAGS}
+
+lib_LTLIBRARIES = libdleyna-server-1.0.la
+
+libdleyna_serverinc_HEADERS = control-point-server.h
+
+libdleyna_server_1_0_la_LDFLAGS = -version-info $(DLEYNA_SERVER_VERSION) \
+ -no-undefined
+
+libdleyna_server_1_0_la_SOURCES = $(libdleyna_serverinc_HEADERS) \
+ server.c \
+ async.c \
+ device.c \
+ path.c \
+ props.c \
+ search.c \
+ sort.c \
+ task.c \
+ upnp.c
+
+libdleyna_server_1_0_la_LIBADD = $(GLIB_LIBS) \
+ $(GIO_LIBS) \
+ $(DLEYNA_CORE_LIBS) \
+ $(GSSDP_LIBS) \
+ $(GUPNP_LIBS) \
+ $(GUPNPAV_LIBS) \
+ $(GUPNPDLNA_LIBS) \
+ $(SOUP_LIBS)
+
+MAINTAINERCLEANFILES = Makefile.in \
+ aclocal.m4 \
+ configure \
+ config.h.in \
+ config.h.in~ \
+ build-aux/depcomp \
+ build-aux/compile \
+ build-aux/missing \
+ build-aux/install-sh
+
+sysconf_DATA = dleyna-server-service.conf
+
+pkgconfigdir = $(libdir)/pkgconfig
+pkgconfig_DATA = dleyna-server-1.0.pc
+
+EXTRA_DIST = $(sysconf_DATA)
+CLEANFILES = $(pkgconfig_DATA) dleyna-server-service.conf
+DISTCLEANFILES = $(pkgconfig_DATA) dleyna-server-service.conf
+
+maintainer-clean-local:
+ rm -rf build-aux
diff --git a/libdleyna/server/async.c b/libdleyna/server/async.c
new file mode 100644
index 0000000..fcdbe9a
--- /dev/null
+++ b/libdleyna/server/async.c
@@ -0,0 +1,99 @@
+/*
+ * dLeyna
+ *
+ * Copyright (C) 2012-2013 Intel Corporation. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms and conditions of the GNU Lesser General Public License,
+ * version 2.1, as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License
+ * for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with this program; if not, write to the Free Software Foundation, Inc.,
+ * 51 Franklin St - Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Mark Ryan <mark.d.ryan@intel.com>
+ *
+ */
+
+#include <libdleyna/core/error.h>
+#include <libdleyna/core/log.h>
+
+#include "async.h"
+#include "server.h"
+
+void dls_async_task_delete(dls_async_task_t *cb_data)
+{
+ switch (cb_data->task.type) {
+ case DLS_TASK_GET_CHILDREN:
+ case DLS_TASK_SEARCH:
+ if (cb_data->ut.bas.vbs)
+ g_ptr_array_unref(cb_data->ut.bas.vbs);
+ break;
+ case DLS_TASK_GET_ALL_PROPS:
+ case DLS_TASK_GET_RESOURCE:
+ if (cb_data->ut.get_all.vb)
+ g_variant_builder_unref(cb_data->ut.get_all.vb);
+ break;
+ case DLS_TASK_UPLOAD_TO_ANY:
+ case DLS_TASK_UPLOAD:
+ g_free(cb_data->ut.upload.mime_type);
+ break;
+ case DLS_TASK_UPDATE_OBJECT:
+ g_free(cb_data->ut.update.current_tag_value);
+ g_free(cb_data->ut.update.new_tag_value);
+ break;
+ case DLS_TASK_CREATE_PLAYLIST:
+ case DLS_TASK_CREATE_PLAYLIST_IN_ANY:
+ g_free(cb_data->ut.playlist.didl);
+ if (cb_data->ut.playlist.collection)
+ g_object_unref(cb_data->ut.playlist.collection);
+ break;
+ default:
+ break;
+ }
+
+ if (cb_data->cancellable)
+ g_object_unref(cb_data->cancellable);
+}
+
+gboolean dls_async_task_complete(gpointer user_data)
+{
+ dls_async_task_t *cb_data = user_data;
+
+ DLEYNA_LOG_DEBUG("Enter. Error %p", (void *)cb_data->error);
+ DLEYNA_LOG_DEBUG_NL();
+
+ if (cb_data->proxy != NULL)
+ g_object_remove_weak_pointer((G_OBJECT(cb_data->proxy)),
+ (gpointer *)&cb_data->proxy);
+
+ cb_data->cb(&cb_data->task, cb_data->error);
+
+ return FALSE;
+}
+
+void dls_async_task_cancelled_cb(GCancellable *cancellable, gpointer user_data)
+{
+ dls_async_task_t *cb_data = user_data;
+
+ if (cb_data->proxy != NULL)
+ gupnp_service_proxy_cancel_action(cb_data->proxy,
+ cb_data->action);
+
+ if (!cb_data->error)
+ cb_data->error = g_error_new(DLEYNA_SERVER_ERROR,
+ DLEYNA_ERROR_CANCELLED,
+ "Operation cancelled.");
+ (void) g_idle_add(dls_async_task_complete, cb_data);
+}
+
+void dls_async_task_cancel(dls_async_task_t *cb_data)
+{
+ if (cb_data->cancellable)
+ g_cancellable_cancel(cb_data->cancellable);
+}
diff --git a/libdleyna/server/async.h b/libdleyna/server/async.h
new file mode 100644
index 0000000..8efb8c5
--- /dev/null
+++ b/libdleyna/server/async.h
@@ -0,0 +1,113 @@
+/*
+ * dLeyna
+ *
+ * Copyright (C) 2012-2013 Intel Corporation. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms and conditions of the GNU Lesser General Public License,
+ * version 2.1, as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License
+ * for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with this program; if not, write to the Free Software Foundation, Inc.,
+ * 51 Franklin St - Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Mark Ryan <mark.d.ryan@intel.com>
+ *
+ */
+
+#ifndef DLS_ASYNC_H__
+#define DLS_ASYNC_H__
+
+#include <libgupnp/gupnp-control-point.h>
+#include <libgupnp-av/gupnp-media-collection.h>
+
+#include <libdleyna/core/task-atom.h>
+
+#include "server.h"
+#include "task.h"
+#include "upnp.h"
+
+typedef struct dls_async_task_t_ dls_async_task_t;
+typedef guint64 dls_upnp_prop_mask;
+
+typedef void (*dls_async_cb_t)(dls_async_task_t *cb_data);
+
+typedef struct dls_async_bas_t_ dls_async_bas_t;
+struct dls_async_bas_t_ {
+ dls_upnp_prop_mask filter_mask;
+ GPtrArray *vbs;
+ const gchar *protocol_info;
+ gboolean need_child_count;
+ guint retrieved;
+ guint max_count;
+ dls_async_cb_t get_children_cb;
+};
+
+typedef struct dls_async_get_prop_t_ dls_async_get_prop_t;
+struct dls_async_get_prop_t_ {
+ GCallback prop_func;
+ const gchar *protocol_info;
+};
+
+typedef struct dls_async_get_all_t_ dls_async_get_all_t;
+struct dls_async_get_all_t_ {
+ GCallback prop_func;
+ GVariantBuilder *vb;
+ dls_upnp_prop_mask filter_mask;
+ const gchar *protocol_info;
+ gboolean need_child_count;
+ gboolean device_object;
+};
+
+typedef struct dls_async_upload_t_ dls_async_upload_t;
+struct dls_async_upload_t_ {
+ const gchar *object_class;
+ gchar *mime_type;
+};
+
+typedef struct dls_async_update_t_ dls_async_update_t;
+struct dls_async_update_t_ {
+ gchar *current_tag_value;
+ gchar *new_tag_value;
+ GHashTable *map;
+};
+
+typedef struct dls_async_playlist_t_ dls_async_playlist_t;
+struct dls_async_playlist_t_ {
+ const dleyna_task_queue_key_t *queue_id;
+ GUPnPMediaCollection *collection;
+ gchar *didl;
+};
+
+struct dls_async_task_t_ {
+ dls_task_t task; /* pseudo inheritance - MUST be first field */
+ dls_upnp_task_complete_t cb;
+ GError *error;
+ GUPnPServiceProxyAction *action;
+ GUPnPServiceProxy *proxy;
+ GCancellable *cancellable;
+ gulong cancel_id;
+ union {
+ dls_async_bas_t bas;
+ dls_async_get_prop_t get_prop;
+ dls_async_get_all_t get_all;
+ dls_async_upload_t upload;
+ dls_async_update_t update;
+ dls_async_playlist_t playlist;
+ } ut;
+};
+
+void dls_async_task_delete(dls_async_task_t *cb_data);
+
+gboolean dls_async_task_complete(gpointer user_data);
+
+void dls_async_task_cancelled_cb(GCancellable *cancellable, gpointer user_data);
+
+void dls_async_task_cancel(dls_async_task_t *cb_data);
+
+#endif /* DLS_ASYNC_H__ */
diff --git a/libdleyna/server/client.h b/libdleyna/server/client.h
new file mode 100644
index 0000000..6d499dd
--- /dev/null
+++ b/libdleyna/server/client.h
@@ -0,0 +1,34 @@
+/*
+ * dLeyna
+ *
+ * Copyright (C) 2012-2013 Intel Corporation. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms and conditions of the GNU Lesser General Public License,
+ * version 2.1, as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License
+ * for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with this program; if not, write to the Free Software Foundation, Inc.,
+ * 51 Franklin St - Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Regis Merlino <regis.merlino@intel.com>
+ *
+ */
+
+#ifndef DLS_CLIENT_H__
+#define DLS_CLIENT_H__
+
+#include <glib.h>
+
+typedef struct dls_client_t_ dls_client_t;
+struct dls_client_t_ {
+ gchar *protocol_info;
+ gboolean prefer_local_addresses;
+};
+
+#endif /* DLS_CLIENT_H__ */
diff --git a/libdleyna/server/control-point-server.h b/libdleyna/server/control-point-server.h
new file mode 100644
index 0000000..1f9876b
--- /dev/null
+++ b/libdleyna/server/control-point-server.h
@@ -0,0 +1,30 @@
+/*
+ * dLeyna
+ *
+ * Copyright (C) 2013 Intel Corporation. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms and conditions of the GNU Lesser General Public License,
+ * version 2.1, as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License
+ * for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with this program; if not, write to the Free Software Foundation, Inc.,
+ * 51 Franklin St - Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Regis Merlino <regis.merlino@intel.com>
+ *
+ */
+
+#ifndef DLEYNA_CONTROL_POINT_SERVER_H__
+#define DLEYNA_CONTROL_POINT_SERVER_H__
+
+#include <libdleyna/core/control-point.h>
+
+const dleyna_control_point_t *dleyna_control_point_get_server(void);
+
+#endif /* DLEYNA_CONTROL_POINT_SERVER_H__ */
diff --git a/libdleyna/server/device.c b/libdleyna/server/device.c
new file mode 100644
index 0000000..c6b7dc4
--- /dev/null
+++ b/libdleyna/server/device.c
@@ -0,0 +1,4348 @@
+/*
+ * dLeyna
+ *
+ * Copyright (C) 2012-2013 Intel Corporation. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms and conditions of the GNU Lesser General Public License,
+ * version 2.1, as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License
+ * for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with this program; if not, write to the Free Software Foundation, Inc.,
+ * 51 Franklin St - Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Mark Ryan <mark.d.ryan@intel.com>
+ *
+ */
+
+#include <string.h>
+#include <libgupnp/gupnp-error.h>
+#include <libgupnp-dlna/gupnp-dlna-profile.h>
+#include <libgupnp-dlna/gupnp-dlna-profile-guesser.h>
+#include <libgupnp-av/gupnp-media-collection.h>
+#include <libgupnp-av/gupnp-cds-last-change-parser.h>
+#include <libsoup/soup.h>
+
+#include <libdleyna/core/error.h>
+#include <libdleyna/core/log.h>
+#include <libdleyna/core/service-task.h>
+
+#include "device.h"
+#include "interface.h"
+#include "path.h"
+#include "server.h"
+
+#define DLS_SYSTEM_UPDATE_VAR "SystemUpdateID"
+#define DLS_CONTAINER_UPDATE_VAR "ContainerUpdateIDs"
+#define DLS_LAST_CHANGE_VAR "LastChange"
+#define DLS_DMS_DEVICE_TYPE "urn:schemas-upnp-org:device:MediaServer:"
+
+#define DLS_UPLOAD_STATUS_IN_PROGRESS "IN_PROGRESS"
+#define DLS_UPLOAD_STATUS_CANCELLED "CANCELLED"
+#define DLS_UPLOAD_STATUS_ERROR "ERROR"
+#define DLS_UPLOAD_STATUS_COMPLETED "COMPLETED"
+
+typedef gboolean(*dls_device_count_cb_t)(dls_async_task_t *cb_data,
+ gint count);
+
+typedef struct dls_device_count_data_t_ dls_device_count_data_t;
+struct dls_device_count_data_t_ {
+ dls_device_count_cb_t cb;
+ dls_async_task_t *cb_data;
+};
+
+typedef struct dls_device_object_builder_t_ dls_device_object_builder_t;
+struct dls_device_object_builder_t_ {
+ GVariantBuilder *vb;
+ gchar *id;
+ gboolean needs_child_count;
+};
+
+typedef struct dls_device_upload_t_ dls_device_upload_t;
+struct dls_device_upload_t_ {
+ SoupSession *soup_session;
+ SoupMessage *msg;
+ GMappedFile *mapped_file;
+ gchar *body;
+ gsize body_length;
+ const gchar *status;
+ guint64 bytes_uploaded;
+ guint64 bytes_to_upload;
+};
+
+typedef struct dls_device_upload_job_t_ dls_device_upload_job_t;
+struct dls_device_upload_job_t_ {
+ gint upload_id;
+ dls_device_t *device;
+ guint remove_idle;
+};
+
+/* Private structure used in chain task */
+typedef struct prv_new_device_ct_t_ prv_new_device_ct_t;
+struct prv_new_device_ct_t_ {
+ dls_device_t *dev;
+ dleyna_connector_id_t connection;
+ const dleyna_connector_dispatch_cb_t *vtable;
+ GHashTable *property_map;
+};
+
+typedef struct prv_new_playlist_ct_t_ prv_new_playlist_ct_t;
+struct prv_new_playlist_ct_t_ {
+ dls_async_task_t *cb_data;
+ gchar *id;
+ gchar *parent_id;
+};
+
+static void prv_get_child_count(dls_async_task_t *cb_data,
+ dls_device_count_cb_t cb, const gchar *id);
+static void prv_retrieve_child_count_for_list(dls_async_task_t *cb_data);
+static void prv_container_update_cb(GUPnPServiceProxy *proxy,
+ const char *variable,
+ GValue *value,
+ gpointer user_data);
+static void prv_system_update_cb(GUPnPServiceProxy *proxy,
+ const char *variable,
+ GValue *value,
+ gpointer user_data);
+static void prv_last_change_cb(GUPnPServiceProxy *proxy,
+ const char *variable,
+ GValue *value,
+ gpointer user_data);
+static void prv_upload_delete(gpointer up);
+static void prv_upload_job_delete(gpointer up);
+static void prv_get_sr_token_for_props(GUPnPServiceProxy *proxy,
+ const dls_device_t *device,
+ dls_async_task_t *cb_data);
+
+static void prv_object_builder_delete(void *dob)
+{
+ dls_device_object_builder_t *builder = dob;
+
+ if (builder) {
+ if (builder->vb)
+ g_variant_builder_unref(builder->vb);
+
+ g_free(builder->id);
+ g_free(builder);
+ }
+}
+
+static void prv_count_data_new(dls_async_task_t *cb_data,
+ dls_device_count_cb_t cb,
+ dls_device_count_data_t **count_data)
+{
+ dls_device_count_data_t *cd;
+
+ cd = g_new(dls_device_count_data_t, 1);
+ cd->cb = cb;
+ cd->cb_data = cb_data;
+
+ *count_data = cd;
+}
+
+static void prv_context_unsubscribe(dls_device_context_t *ctx)
+{
+ if (ctx->timeout_id) {
+ (void) g_source_remove(ctx->timeout_id);
+ ctx->timeout_id = 0;
+ }
+
+ if (ctx->subscribed) {
+ gupnp_service_proxy_remove_notify(
+ ctx->service_proxy,
+ DLS_SYSTEM_UPDATE_VAR,
+ prv_system_update_cb,
+ ctx->device);
+ gupnp_service_proxy_remove_notify(
+ ctx->service_proxy,
+ DLS_CONTAINER_UPDATE_VAR,
+ prv_container_update_cb,
+ ctx->device);
+ gupnp_service_proxy_remove_notify(
+ ctx->service_proxy,
+ DLS_LAST_CHANGE_VAR,
+ prv_last_change_cb,
+ ctx->device);
+
+ gupnp_service_proxy_set_subscribed(ctx->service_proxy,
+ FALSE);
+
+ ctx->subscribed = FALSE;
+ }
+}
+
+static void prv_context_delete(gpointer context)
+{
+ dls_device_context_t *ctx = context;
+
+ if (ctx) {
+ prv_context_unsubscribe(ctx);
+
+ if (ctx->device_proxy)
+ g_object_unref(ctx->device_proxy);
+
+ if (ctx->service_proxy)
+ g_object_unref(ctx->service_proxy);
+
+ g_free(ctx->ip_address);
+ g_free(ctx);
+ }
+}
+
+static void prv_context_new(const gchar *ip_address,
+ GUPnPDeviceProxy *proxy,
+ dls_device_t *device,
+ dls_device_context_t **context)
+{
+ const gchar *service_type =
+ "urn:schemas-upnp-org:service:ContentDirectory";
+ dls_device_context_t *ctx = g_new(dls_device_context_t, 1);
+
+ ctx->ip_address = g_strdup(ip_address);
+ ctx->device_proxy = proxy;
+ ctx->device = device;
+ g_object_ref(proxy);
+ ctx->service_proxy = (GUPnPServiceProxy *)
+ gupnp_device_info_get_service((GUPnPDeviceInfo *)proxy,
+ service_type);
+ ctx->subscribed = FALSE;
+ ctx->timeout_id = 0;
+
+ *context = ctx;
+}
+
+void dls_device_delete(void *device)
+{
+ dls_device_t *dev = device;
+
+ if (dev) {
+ dev->shutting_down = TRUE;
+ g_hash_table_unref(dev->upload_jobs);
+ g_hash_table_unref(dev->uploads);
+
+ if (dev->timeout_id)
+ (void) g_source_remove(dev->timeout_id);
+
+ if (dev->id)
+ (void) dls_server_get_connector()->unpublish_subtree(
+ dev->connection, dev->id);
+
+ g_ptr_array_unref(dev->contexts);
+ g_free(dev->path);
+ g_variant_unref(dev->search_caps);
+ g_variant_unref(dev->sort_caps);
+ g_variant_unref(dev->sort_ext_caps);
+ g_variant_unref(dev->feature_list);
+ g_free(dev);
+ }
+}
+
+void dls_device_unsubscribe(void *device)
+{
+ unsigned int i;
+ dls_device_t *dev = device;
+ dls_device_context_t *context;
+
+ if (dev) {
+ for (i = 0; i < dev->contexts->len; ++i) {
+ context = g_ptr_array_index(dev->contexts, i);
+ prv_context_unsubscribe(context);
+ }
+ }
+}
+
+static void prv_last_change_decode(GUPnPCDSLastChangeEntry *entry,
+ GVariantBuilder *array,
+ const char *root_path)
+{
+ GUPnPCDSLastChangeEvent event;
+ GVariant *state;
+ const char *object_id;
+ const char *parent_id;
+ const char *mclass;
+ const char *media_class;
+ char *key[] = {"ADD", "DEL", "MOD", "DONE"};
+ char *parent_path;
+ char *path = NULL;
+ gboolean sub_update;
+ guint32 update_id;
+
+ object_id = gupnp_cds_last_change_entry_get_object_id(entry);
+ if (!object_id)
+ goto on_error;
+
+ sub_update = gupnp_cds_last_change_entry_is_subtree_update(entry);
+ update_id = gupnp_cds_last_change_entry_get_update_id(entry);
+ path = dls_path_from_id(root_path, object_id);
+ event = gupnp_cds_last_change_entry_get_event(entry);
+
+ switch (event) {
+ case GUPNP_CDS_LAST_CHANGE_EVENT_OBJECT_ADDED:
+ parent_id = gupnp_cds_last_change_entry_get_parent_id(entry);
+ if (!parent_id)
+ goto on_error;
+
+ mclass = gupnp_cds_last_change_entry_get_class(entry);
+ if (!mclass)
+ goto on_error;
+
+ media_class = dls_props_upnp_class_to_media_spec(mclass);
+ if (!media_class)
+ goto on_error;
+
+ parent_path = dls_path_from_id(root_path, parent_id);
+ state = g_variant_new("(oubos)", path, update_id, sub_update,
+ parent_path, media_class);
+ g_free(parent_path);
+ break;
+ case GUPNP_CDS_LAST_CHANGE_EVENT_OBJECT_REMOVED:
+ case GUPNP_CDS_LAST_CHANGE_EVENT_OBJECT_MODIFIED:
+ state = g_variant_new("(oub)", path, update_id, sub_update);
+ break;
+ case GUPNP_CDS_LAST_CHANGE_EVENT_ST_DONE:
+ state = g_variant_new("(ou)", path, update_id);
+ break;
+ case GUPNP_CDS_LAST_CHANGE_EVENT_INVALID:
+ default:
+ goto on_error;
+ break;
+ }
+
+ g_variant_builder_add(array, "(sv)", key[event - 1], state);
+
+on_error:
+
+ g_free(path);
+}
+
+static void prv_last_change_cb(GUPnPServiceProxy *proxy,
+ const char *variable,
+ GValue *value,
+ gpointer user_data)
+{
+ const gchar *last_change;
+ GVariantBuilder array;
+ GVariant *val;
+ dls_device_t *device = user_data;
+ GUPnPCDSLastChangeParser *parser;
+ GList *list;
+ GList *next;
+ GError *error = NULL;
+
+ last_change = g_value_get_string(value);
+
+ DLEYNA_LOG_DEBUG_NL();
+ DLEYNA_LOG_DEBUG("LastChange XML: %s", last_change);
+ DLEYNA_LOG_DEBUG_NL();
+
+ parser = gupnp_cds_last_change_parser_new();
+ list = gupnp_cds_last_change_parser_parse(parser, last_change, &error);
+
+ if (error != NULL) {
+ DLEYNA_LOG_WARNING(
+ "gupnp_cds_last_change_parser_parse parsing failed: %s",
+ error->message);
+ goto on_error;
+ }
+
+ g_variant_builder_init(&array, G_VARIANT_TYPE("a(sv)"));
+ next = list;
+ while (next) {
+ prv_last_change_decode(next->data, &array, device->path);
+ gupnp_cds_last_change_entry_unref(next->data);
+ next = g_list_next(next);
+ }
+
+ val = g_variant_new("(@a(sv))", g_variant_builder_end(&array));
+
+ (void) dls_server_get_connector()->notify(device->connection,
+ device->path,
+ DLEYNA_SERVER_INTERFACE_MEDIA_DEVICE,
+ DLS_INTERFACE_ESV_LAST_CHANGE,
+ val,
+ NULL);
+
+on_error:
+
+ g_list_free(list);
+ g_object_unref(parser);
+
+ if (error != NULL)
+ g_error_free(error);
+}
+
+static void prv_build_container_update_array(const gchar *root_path,
+ const gchar *value,
+ GVariantBuilder *builder)
+{
+ gchar **str_array;
+ int pos = 0;
+ gchar *path;
+ guint id;
+
+ str_array = g_strsplit(value, ",", 0);
+
+ DLEYNA_LOG_DEBUG_NL();
+
+ while (str_array[pos] && str_array[pos + 1]) {
+ path = dls_path_from_id(root_path, str_array[pos++]);
+ id = atoi(str_array[pos++]);
+ g_variant_builder_add(builder, "(ou)", path, id);
+ DLEYNA_LOG_DEBUG("@Id [%s] - Path [%s] - id[%d]",
+ str_array[pos-2], path, id);
+ g_free(path);
+ }
+
+ DLEYNA_LOG_DEBUG_NL();
+
+ g_strfreev(str_array);
+}
+
+static void prv_container_update_cb(GUPnPServiceProxy *proxy,
+ const char *variable,
+ GValue *value,
+ gpointer user_data)
+{
+ dls_device_t *device = user_data;
+ GVariantBuilder array;
+
+ g_variant_builder_init(&array, G_VARIANT_TYPE("a(ou)"));
+
+ prv_build_container_update_array(device->path,
+ g_value_get_string(value),
+ &array);
+
+ (void) dls_server_get_connector()->notify(
+ device->connection,
+ device->path,
+ DLEYNA_SERVER_INTERFACE_MEDIA_DEVICE,
+ DLS_INTERFACE_ESV_CONTAINER_UPDATE_IDS,
+ g_variant_new("(@a(ou))",
+ g_variant_builder_end(&array)),
+ NULL);
+}
+
+static void prv_system_update_cb(GUPnPServiceProxy *proxy,
+ const char *variable,
+ GValue *value,
+ gpointer user_data)
+{
+ GVariantBuilder *array;
+ GVariant *val;
+ dls_device_t *device = user_data;
+ guint suid = g_value_get_uint(value);
+
+ DLEYNA_LOG_DEBUG("System Update %u", suid);
+
+ device->system_update_id = suid;
+
+ array = g_variant_builder_new(G_VARIANT_TYPE("a{sv}"));
+ g_variant_builder_add(array, "{sv}",
+ DLS_INTERFACE_PROP_ESV_SYSTEM_UPDATE_ID,
+ g_variant_new_uint32(suid));
+ val = g_variant_new("(s@a{sv}as)", DLEYNA_SERVER_INTERFACE_MEDIA_DEVICE,
+ g_variant_builder_end(array),
+ NULL);
+
+ (void) dls_server_get_connector()->notify(device->connection,
+ device->path,
+ DLS_INTERFACE_PROPERTIES,
+ DLS_INTERFACE_PROPERTIES_CHANGED,
+ val,
+ NULL);
+
+ g_variant_builder_unref(array);
+}
+
+static gboolean prv_re_enable_subscription(gpointer user_data)
+{
+ dls_device_context_t *context = user_data;
+
+ context->timeout_id = 0;
+
+ return FALSE;
+}
+
+static void prv_subscription_lost_cb(GUPnPServiceProxy *proxy,
+ const GError *reason,
+ gpointer user_data)
+{
+ dls_device_context_t *context = user_data;
+
+ if (!context->timeout_id) {
+ gupnp_service_proxy_set_subscribed(context->service_proxy,
+ TRUE);
+ context->timeout_id = g_timeout_add_seconds(
+ 10,
+ prv_re_enable_subscription,
+ context);
+ } else {
+ g_source_remove(context->timeout_id);
+ gupnp_service_proxy_remove_notify(context->service_proxy,
+ DLS_SYSTEM_UPDATE_VAR,
+ prv_system_update_cb,
+ context->device);
+ gupnp_service_proxy_remove_notify(context->service_proxy,
+ DLS_CONTAINER_UPDATE_VAR,
+ prv_container_update_cb,
+ context->device);
+ gupnp_service_proxy_remove_notify(context->service_proxy,
+ DLS_LAST_CHANGE_VAR,
+ prv_last_change_cb,
+ context->device);
+
+ context->timeout_id = 0;
+ context->subscribed = FALSE;
+ }
+}
+
+void dls_device_subscribe_to_contents_change(dls_device_t *device)
+{
+ dls_device_context_t *context;
+
+ context = dls_device_get_context(device, NULL);
+
+ DLEYNA_LOG_DEBUG("Subscribe for events on context: %s",
+ context->ip_address);
+
+ gupnp_service_proxy_add_notify(context->service_proxy,
+ DLS_SYSTEM_UPDATE_VAR,
+ G_TYPE_UINT,
+ prv_system_update_cb,
+ device);
+
+ gupnp_service_proxy_add_notify(context->service_proxy,
+ DLS_CONTAINER_UPDATE_VAR,
+ G_TYPE_STRING,
+ prv_container_update_cb,
+ device);
+
+ gupnp_service_proxy_add_notify(context->service_proxy,
+ DLS_LAST_CHANGE_VAR,
+ G_TYPE_STRING,
+ prv_last_change_cb,
+ device);
+
+ context->subscribed = TRUE;
+ gupnp_service_proxy_set_subscribed(context->service_proxy, TRUE);
+
+ g_signal_connect(context->service_proxy,
+ "subscription-lost",
+ G_CALLBACK(prv_subscription_lost_cb),
+ context);
+}
+
+static void prv_feature_list_add_feature(gchar *root_path,
+ GUPnPFeature *feature,
+ GVariantBuilder *vb)
+{
+ GVariantBuilder vbo;
+ GVariant *var_obj;
+ const char *name;
+ const char *version;
+ const char *obj_id;
+ gchar **obj;
+ gchar **saved;
+ gchar *path;
+
+ name = gupnp_feature_get_name(feature);
+ version = gupnp_feature_get_version(feature);
+ obj_id = gupnp_feature_get_object_ids(feature);
+
+ g_variant_builder_init(&vbo, G_VARIANT_TYPE("ao"));
+
+ if (obj_id != NULL && *obj_id) {
+ obj = g_strsplit(obj_id, ",", 0);
+ saved = obj;
+
+ while (obj && *obj) {
+ path = dls_path_from_id(root_path, *obj);
+ g_variant_builder_add(&vbo, "o", path);
+ g_free(path);
+ obj++;
+ }
+
+ g_strfreev(saved);
+ }
+
+ var_obj = g_variant_builder_end(&vbo);
+ g_variant_builder_add(vb, "(ss@ao)", name, version, var_obj);
+}
+
+static void prv_get_feature_list_analyze(dls_device_t *device, gchar *result)
+{
+ GUPnPFeatureListParser *parser;
+ GUPnPFeature *feature;
+ GList *list;
+ GList *item;
+ GError *error = NULL;
+ GVariantBuilder vb;
+#if DLEYNA_LOG_LEVEL & DLEYNA_LOG_LEVEL_DEBUG
+ char *str;
+#endif
+ parser = gupnp_feature_list_parser_new();
+ list = gupnp_feature_list_parser_parse_text(parser, result, &error);
+
+ if (error != NULL) {
+ DLEYNA_LOG_WARNING("GetFeatureList parsing failed: %s",
+ error->message);
+ goto on_exit;
+ }
+
+ g_variant_builder_init(&vb, G_VARIANT_TYPE("a(ssao)"));
+ item = list;
+
+ while (item != NULL) {
+ feature = (GUPnPFeature *)item->data;
+ prv_feature_list_add_feature(device->path, feature, &vb);
+ g_object_unref(feature);
+ item = g_list_next(item);
+ }
+
+ device->feature_list = g_variant_ref_sink(g_variant_builder_end(&vb));
+
+#if DLEYNA_LOG_LEVEL & DLEYNA_LOG_LEVEL_DEBUG
+ str = g_variant_print(device->feature_list, FALSE);
+ DLEYNA_LOG_DEBUG("%s = %s", DLS_INTERFACE_PROP_SV_FEATURE_LIST, str);
+ g_free(str);
+#endif
+
+on_exit:
+ g_list_free(list);
+ g_object_unref(parser);
+
+ if (error != NULL)
+ g_error_free(error);
+}
+
+static void prv_get_feature_list_cb(GUPnPServiceProxy *proxy,
+ GUPnPServiceProxyAction *action,
+ gpointer user_data)
+{
+ gchar *result = NULL;
+ GError *error = NULL;
+ prv_new_device_ct_t *priv_t = (prv_new_device_ct_t *)user_data;
+
+ if (!gupnp_service_proxy_end_action(proxy, action, &error,
+ "FeatureList", G_TYPE_STRING,
+ &result, NULL)) {
+ DLEYNA_LOG_WARNING("GetFeatureList operation failed: %s",
+ error->message);
+ goto on_error;
+ }
+
+ DLEYNA_LOG_DEBUG("GetFeatureList result: %s", result);
+
+ prv_get_feature_list_analyze(priv_t->dev, result);
+
+on_error:
+ if (error != NULL)
+ g_error_free(error);
+
+ g_free(result);
+}
+
+static GUPnPServiceProxyAction *prv_get_feature_list(
+ dleyna_service_task_t *task,
+ GUPnPServiceProxy *proxy,
+ gboolean *failed)
+{
+ *failed = FALSE;
+
+ return gupnp_service_proxy_begin_action(
+ proxy, "GetFeatureList",
+ dleyna_service_task_begin_action_cb,
+ task, NULL);
+}
+
+static void prv_get_sort_ext_capabilities_analyze(dls_device_t *device,
+ gchar *result)
+{
+ gchar **caps;
+ gchar **saved;
+#if DLEYNA_LOG_LEVEL & DLEYNA_LOG_LEVEL_DEBUG
+ gchar *props;
+#endif
+ GVariantBuilder sort_ext_caps_vb;
+
+ g_variant_builder_init(&sort_ext_caps_vb, G_VARIANT_TYPE("as"));
+
+ caps = g_strsplit(result, ",", 0);
+ saved = caps;
+
+ while (caps && *caps) {
+ g_variant_builder_add(&sort_ext_caps_vb, "s", *caps);
+ caps++;
+ }
+
+ g_strfreev(saved);
+
+ device->sort_ext_caps = g_variant_ref_sink(g_variant_builder_end(
+ &sort_ext_caps_vb));
+
+#if DLEYNA_LOG_LEVEL & DLEYNA_LOG_LEVEL_DEBUG
+ props = g_variant_print(device->sort_ext_caps, FALSE);
+ DLEYNA_LOG_DEBUG("%s = %s",
+ DLS_INTERFACE_PROP_SV_SORT_EXT_CAPABILITIES, props);
+ g_free(props);
+#endif
+}
+
+static void prv_get_sort_ext_capabilities_cb(GUPnPServiceProxy *proxy,
+ GUPnPServiceProxyAction *action,
+ gpointer user_data)
+{
+ gchar *result = NULL;
+ GError *error = NULL;
+ prv_new_device_ct_t *priv_t = (prv_new_device_ct_t *)user_data;
+
+ if (!gupnp_service_proxy_end_action(proxy, action, &error,
+ "SortExtensionCaps",
+ G_TYPE_STRING, &result, NULL)) {
+ DLEYNA_LOG_WARNING(
+ "GetSortExtensionCapabilities operation failed: %s",
+ error->message);
+ goto on_error;
+ }
+
+ DLEYNA_LOG_DEBUG("GetSortExtensionCapabilities result: %s", result);
+
+ prv_get_sort_ext_capabilities_analyze(priv_t->dev, result);
+
+on_error:
+
+ if (error)
+ g_error_free(error);
+
+ g_free(result);
+}
+
+static GUPnPServiceProxyAction *prv_get_sort_ext_capabilities(
+ dleyna_service_task_t *task,
+ GUPnPServiceProxy *proxy,
+ gboolean *failed)
+{
+ *failed = FALSE;
+
+ return gupnp_service_proxy_begin_action(
+ proxy,
+ "GetSortExtensionCapabilities",
+ dleyna_service_task_begin_action_cb,
+ task, NULL);
+}
+
+static void prv_get_capabilities_analyze(GHashTable *property_map,
+ gchar *result,
+ GVariant **variant)
+{
+ gchar **caps;
+ gchar **saved;
+ gchar *prop_name;
+ GVariantBuilder caps_vb;
+
+ g_variant_builder_init(&caps_vb, G_VARIANT_TYPE("as"));
+
+ caps = g_strsplit(result, ",", 0);
+ saved = caps;
+
+ while (caps && *caps) {
+ prop_name = g_hash_table_lookup(property_map, *caps);
+
+ if (prop_name)
+ g_variant_builder_add(&caps_vb, "s", prop_name);
+
+ caps++;
+ }
+
+ g_strfreev(saved);
+
+ *variant = g_variant_ref_sink(g_variant_builder_end(&caps_vb));
+
+#if DLEYNA_LOG_LEVEL & DLEYNA_LOG_LEVEL_DEBUG
+ prop_name = g_variant_print(*variant, FALSE);
+ DLEYNA_LOG_DEBUG("%s = %s", " Variant", prop_name);
+ g_free(prop_name);
+#endif
+}
+
+static void prv_get_sort_capabilities_cb(GUPnPServiceProxy *proxy,
+ GUPnPServiceProxyAction *action,
+ gpointer user_data)
+{
+ gchar *result = NULL;
+ GError *error = NULL;
+ prv_new_device_ct_t *priv_t = (prv_new_device_ct_t *)user_data;
+
+ if (!gupnp_service_proxy_end_action(proxy, action, &error, "SortCaps",
+ G_TYPE_STRING, &result, NULL)) {
+ DLEYNA_LOG_WARNING("GetSortCapabilities operation failed: %s",
+ error->message);
+ goto on_error;
+ }
+
+ DLEYNA_LOG_DEBUG("GetSortCapabilities result: %s", result);
+
+ prv_get_capabilities_analyze(priv_t->property_map, result,
+ &priv_t->dev->sort_caps);
+
+on_error:
+
+ if (error)
+ g_error_free(error);
+
+ g_free(result);
+}
+
+static GUPnPServiceProxyAction *prv_get_sort_capabilities(
+ dleyna_service_task_t *task,
+ GUPnPServiceProxy *proxy,
+ gboolean *failed)
+{
+ *failed = FALSE;
+
+ return gupnp_service_proxy_begin_action(
+ proxy,
+ "GetSortCapabilities",
+ dleyna_service_task_begin_action_cb,
+ task, NULL);
+}
+
+static void prv_get_search_capabilities_cb(GUPnPServiceProxy *proxy,
+ GUPnPServiceProxyAction *action,
+ gpointer user_data)
+{
+ gchar *result = NULL;
+ GError *error = NULL;
+ prv_new_device_ct_t *priv_t = (prv_new_device_ct_t *)user_data;
+
+ if (!gupnp_service_proxy_end_action(proxy, action, &error, "SearchCaps",
+ G_TYPE_STRING, &result, NULL)) {
+ DLEYNA_LOG_WARNING("GetSearchCapabilities operation failed: %s",
+ error->message);
+ goto on_error;
+ }
+
+ DLEYNA_LOG_DEBUG("GetSearchCapabilities result: %s", result);
+
+ prv_get_capabilities_analyze(priv_t->property_map, result,
+ &priv_t->dev->search_caps);
+
+on_error:
+
+ if (error)
+ g_error_free(error);
+
+ g_free(result);
+}
+
+static GUPnPServiceProxyAction *prv_get_search_capabilities(
+ dleyna_service_task_t *task,
+ GUPnPServiceProxy *proxy,
+ gboolean *failed)
+{
+ *failed = FALSE;
+
+ return gupnp_service_proxy_begin_action(
+ proxy, "GetSearchCapabilities",
+ dleyna_service_task_begin_action_cb,
+ task, NULL);
+}
+
+static GUPnPServiceProxyAction *prv_subscribe(dleyna_service_task_t *task,
+ GUPnPServiceProxy *proxy,
+ gboolean *failed)
+{
+ dls_device_t *device;
+
+ device = (dls_device_t *)dleyna_service_task_get_user_data(task);
+ dls_device_subscribe_to_contents_change(device);
+
+ *failed = FALSE;
+
+ return NULL;
+}
+
+static gboolean prv_subtree_interface_filter(const gchar *object_path,
+ const gchar *node,
+ const gchar *interface)
+{
+ gboolean root_object = FALSE;
+ const gchar *slash;
+ gboolean retval = TRUE;
+
+ /* All objects in the hierarchy support the same interface. Strictly
+ speaking this is not correct as it will allow ListChildren to be
+ executed on a mediaitem object. However, returning the correct
+ interface here would be too inefficient. We would need to either
+ cache the type of all objects encountered so far or issue a UPnP
+ request here to determine the objects type. Best to let the client
+ call ListChildren on a item. This will lead to an error when we
+ execute the UPnP command and we can return an error then.
+
+ We do know however that the root objects are containers. Therefore
+ we can remove the MediaItem2 interface from the root containers. We
+ also know that only the root objects suport the MediaDevice
+ interface.
+ */
+
+ if (dls_path_get_non_root_id(object_path, &slash))
+ root_object = !slash;
+
+ if (root_object && !strcmp(interface, DLS_INTERFACE_MEDIA_ITEM))
+ retval = FALSE;
+ else if (!root_object && !strcmp(interface,
+ DLEYNA_SERVER_INTERFACE_MEDIA_DEVICE))
+ retval = FALSE;
+
+ return retval;
+}
+
+static GUPnPServiceProxyAction *prv_declare(dleyna_service_task_t *task,
+ GUPnPServiceProxy *proxy,
+ gboolean *failed)
+{
+ guint id;
+ dls_device_t *device;
+ prv_new_device_ct_t *priv_t;
+
+ priv_t = (prv_new_device_ct_t *)dleyna_service_task_get_user_data(task);
+ device = priv_t->dev;
+
+ id = dls_server_get_connector()->publish_subtree(priv_t->connection,
+ device->path,
+ priv_t->vtable,
+ DLS_INTERFACE_INFO_MAX,
+ prv_subtree_interface_filter);
+ if (id) {
+ device->id = id;
+
+ device->uploads = g_hash_table_new_full(
+ g_int_hash,
+ g_int_equal,
+ g_free,
+ prv_upload_delete);
+ device->upload_jobs = g_hash_table_new_full(
+ g_int_hash,
+ g_int_equal,
+ g_free,
+ prv_upload_job_delete);
+
+ } else {
+ DLEYNA_LOG_WARNING("dleyna_connector_publish_subtree FAILED");
+ }
+
+ *failed = (!id);
+
+ return NULL;
+}
+
+dls_device_t *dls_device_new(
+ dleyna_connector_id_t connection,
+ GUPnPDeviceProxy *proxy,
+ const gchar *ip_address,
+ const dleyna_connector_dispatch_cb_t *dispatch_table,
+ GHashTable *property_map,
+ guint counter,
+ const dleyna_task_queue_key_t *queue_id)
+{
+ dls_device_t *dev;
+ prv_new_device_ct_t *priv_t;
+ gchar *new_path;
+ dls_device_context_t *context;
+ GUPnPServiceProxy *s_proxy;
+
+ DLEYNA_LOG_DEBUG("New Device on %s", ip_address);
+
+ new_path = g_strdup_printf("%s/%u", DLEYNA_SERVER_PATH, counter);
+ DLEYNA_LOG_DEBUG("Server Path %s", new_path);
+
+ dev = g_new0(dls_device_t, 1);
+ priv_t = g_new0(prv_new_device_ct_t, 1);
+
+ dev->connection = connection;
+ dev->contexts = g_ptr_array_new_with_free_func(prv_context_delete);
+ dev->path = new_path;
+
+ priv_t->dev = dev;
+ priv_t->connection = connection;
+ priv_t->vtable = dispatch_table;
+ priv_t->property_map = property_map;
+
+ context = dls_device_append_new_context(dev, ip_address, proxy);
+ s_proxy = context->service_proxy;
+
+ dleyna_service_task_add(queue_id, prv_get_search_capabilities,
+ s_proxy,
+ prv_get_search_capabilities_cb, NULL, priv_t);
+
+ dleyna_service_task_add(queue_id, prv_get_sort_capabilities,
+ s_proxy,
+ prv_get_sort_capabilities_cb, NULL, priv_t);
+
+ dleyna_service_task_add(queue_id, prv_get_sort_ext_capabilities,
+ s_proxy,
+ prv_get_sort_ext_capabilities_cb, NULL, priv_t);
+
+ dleyna_service_task_add(queue_id, prv_get_feature_list, s_proxy,
+ prv_get_feature_list_cb, NULL, priv_t);
+
+ dleyna_service_task_add(queue_id, prv_subscribe, s_proxy,
+ NULL, NULL, dev);
+
+ dleyna_service_task_add(queue_id, prv_declare, s_proxy,
+ NULL, g_free, priv_t);
+
+ dleyna_task_queue_start(queue_id);
+
+ return dev;
+}
+
+dls_device_context_t *dls_device_append_new_context(dls_device_t *device,
+ const gchar *ip_address,
+ GUPnPDeviceProxy *proxy)
+{
+ dls_device_context_t *context;
+
+ prv_context_new(ip_address, proxy, device, &context);
+ g_ptr_array_add(device->contexts, context);
+
+ return context;
+}
+
+dls_device_t *dls_device_from_path(const gchar *path, GHashTable *device_list)
+{
+ GHashTableIter iter;
+ gpointer value;
+ dls_device_t *device;
+ dls_device_t *retval = NULL;
+
+ g_hash_table_iter_init(&iter, device_list);
+
+ while (g_hash_table_iter_next(&iter, NULL, &value)) {
+ device = value;
+
+ if (!strcmp(device->path, path)) {
+ retval = device;
+ break;
+ }
+ }
+
+ return retval;
+}
+
+dls_device_context_t *dls_device_get_context(const dls_device_t *device,
+ dls_client_t *client)
+{
+ dls_device_context_t *context;
+ unsigned int i;
+ const char ip4_local_prefix[] = "127.0.0.";
+ gboolean prefer_local;
+ gboolean is_local;
+
+ prefer_local = (client && client->prefer_local_addresses);
+
+ for (i = 0; i < device->contexts->len; ++i) {
+ context = g_ptr_array_index(device->contexts, i);
+
+ is_local = (!strncmp(context->ip_address, ip4_local_prefix,
+ sizeof(ip4_local_prefix) - 1) ||
+ !strcmp(context->ip_address, "::1") ||
+ !strcmp(context->ip_address, "0:0:0:0:0:0:0:1"));
+
+ if (prefer_local == is_local)
+ break;
+ }
+
+ if (i == device->contexts->len)
+ context = g_ptr_array_index(device->contexts, 0);
+
+ return context;
+}
+
+static void prv_found_child(GUPnPDIDLLiteParser *parser,
+ GUPnPDIDLLiteObject *object,
+ gpointer user_data)
+{
+ dls_async_task_t *cb_data = user_data;
+ dls_task_t *task = &cb_data->task;
+ dls_task_get_children_t *task_data = &task->ut.get_children;
+ dls_async_bas_t *cb_task_data = &cb_data->ut.bas;
+ dls_device_object_builder_t *builder;
+ gboolean have_child_count;
+
+ DLEYNA_LOG_DEBUG("Enter");
+
+ builder = g_new0(dls_device_object_builder_t, 1);
+
+ if (GUPNP_IS_DIDL_LITE_CONTAINER(object)) {
+ if (!task_data->containers)
+ goto on_error;
+ } else {
+ if (!task_data->items)
+ goto on_error;
+ }
+
+ builder->vb = g_variant_builder_new(G_VARIANT_TYPE("a{sv}"));
+
+ if (!dls_props_add_object(builder->vb, object, task->target.root_path,
+ task->target.path, cb_task_data->filter_mask))
+ goto on_error;
+
+ if (GUPNP_IS_DIDL_LITE_CONTAINER(object)) {
+ dls_props_add_container(builder->vb,
+ (GUPnPDIDLLiteContainer *)object,
+ cb_task_data->filter_mask,
+ &have_child_count);
+
+ if (!have_child_count && (cb_task_data->filter_mask &
+ DLS_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 {
+ dls_props_add_item(builder->vb, object,
+ task->target.root_path,
+ cb_task_data->filter_mask,
+ cb_task_data->protocol_info);
+ }
+
+ g_ptr_array_add(cb_task_data->vbs, builder);
+
+ DLEYNA_LOG_DEBUG("Exit with SUCCESS");
+
+ return;
+
+on_error:
+
+ prv_object_builder_delete(builder);
+
+ DLEYNA_LOG_DEBUG("Exit with FAIL");
+}
+
+static GVariant *prv_children_result_to_variant(dls_async_task_t *cb_data)
+{
+ guint i;
+ dls_device_object_builder_t *builder;
+ dls_async_bas_t *cb_task_data = &cb_data->ut.bas;
+ GVariantBuilder vb;
+
+ g_variant_builder_init(&vb, G_VARIANT_TYPE("aa{sv}"));
+
+ for (i = 0; i < cb_task_data->vbs->len; ++i) {
+ builder = g_ptr_array_index(cb_task_data->vbs, i);
+ g_variant_builder_add(&vb, "@a{sv}",
+ g_variant_builder_end(builder->vb));
+ }
+
+ return g_variant_builder_end(&vb);
+}
+
+static void prv_get_search_ex_result(dls_async_task_t *cb_data)
+{
+ GVariant *out_params[2];
+ dls_async_bas_t *cb_task_data = &cb_data->ut.bas;
+
+ out_params[0] = prv_children_result_to_variant(cb_data);
+ out_params[1] = g_variant_new_uint32(cb_task_data->max_count);
+
+ cb_data->task.result = g_variant_ref_sink(
+ g_variant_new_tuple(out_params, 2));
+}
+
+static void prv_get_children_result(dls_async_task_t *cb_data)
+{
+ GVariant *retval = prv_children_result_to_variant(cb_data);
+ cb_data->task.result = g_variant_ref_sink(retval);
+}
+
+static gboolean prv_child_count_for_list_cb(dls_async_task_t *cb_data,
+ gint count)
+{
+ dls_async_bas_t *cb_task_data = &cb_data->ut.bas;
+ dls_device_object_builder_t *builder;
+
+ builder = g_ptr_array_index(cb_task_data->vbs, cb_task_data->retrieved);
+ dls_props_add_child_count(builder->vb, count);
+ cb_task_data->retrieved++;
+ prv_retrieve_child_count_for_list(cb_data);
+
+ return cb_task_data->retrieved >= cb_task_data->vbs->len;
+}
+
+static void prv_retrieve_child_count_for_list(dls_async_task_t *cb_data)
+{
+ dls_async_bas_t *cb_task_data = &cb_data->ut.bas;
+ dls_device_object_builder_t *builder;
+ guint i;
+
+ for (i = cb_task_data->retrieved; i < cb_task_data->vbs->len; ++i) {
+ builder = g_ptr_array_index(cb_task_data->vbs, i);
+
+ if (builder->needs_child_count)
+ break;
+ }
+
+ cb_task_data->retrieved = i;
+
+ if (i < cb_task_data->vbs->len)
+ prv_get_child_count(cb_data, prv_child_count_for_list_cb,
+ builder->id);
+ else
+ cb_task_data->get_children_cb(cb_data);
+}
+
+static void prv_get_children_cb(GUPnPServiceProxy *proxy,
+ GUPnPServiceProxyAction *action,
+ gpointer user_data)
+{
+ gchar *result = NULL;
+ GUPnPDIDLLiteParser *parser = NULL;
+ GError *upnp_error = NULL;
+ dls_async_task_t *cb_data = user_data;
+ dls_async_bas_t *cb_task_data = &cb_data->ut.bas;
+
+ DLEYNA_LOG_DEBUG("Enter");
+
+ if (!gupnp_service_proxy_end_action(cb_data->proxy, cb_data->action,
+ &upnp_error,
+ "Result", G_TYPE_STRING,
+ &result, NULL)) {
+ DLEYNA_LOG_WARNING("Browse operation failed: %s",
+ upnp_error->message);
+
+ cb_data->error = g_error_new(DLEYNA_SERVER_ERROR,
+ DLEYNA_ERROR_OPERATION_FAILED,
+ "Browse operation failed: %s",
+ upnp_error->message);
+ goto on_error;
+ }
+
+ DLEYNA_LOG_DEBUG("GetChildren result: %s", result);
+
+ parser = gupnp_didl_lite_parser_new();
+
+ g_signal_connect(parser, "object-available" ,
+ G_CALLBACK(prv_found_child), cb_data);
+
+ cb_task_data->vbs = g_ptr_array_new_with_free_func(
+ prv_object_builder_delete);
+
+ if (!gupnp_didl_lite_parser_parse_didl(parser, result, &upnp_error) &&
+ upnp_error->code != GUPNP_XML_ERROR_EMPTY_NODE) {
+ DLEYNA_LOG_WARNING("Unable to parse results of browse: %s",
+ upnp_error->message);
+
+ cb_data->error = g_error_new(DLEYNA_SERVER_ERROR,
+ DLEYNA_ERROR_OPERATION_FAILED,
+ "Unable to parse results of browse: %s",
+ upnp_error->message);
+ goto on_error;
+ }
+
+ if (cb_task_data->need_child_count) {
+ DLEYNA_LOG_DEBUG("Need to retrieve ChildCounts");
+
+ cb_task_data->get_children_cb = prv_get_children_result;
+ prv_retrieve_child_count_for_list(cb_data);
+ goto no_complete;
+ } else {
+ prv_get_children_result(cb_data);
+ }
+
+on_error:
+
+ (void) g_idle_add(dls_async_task_complete, cb_data);
+ g_cancellable_disconnect(cb_data->cancellable, cb_data->cancel_id);
+
+no_complete:
+
+ if (upnp_error)
+ g_error_free(upnp_error);
+
+ if (parser)
+ g_object_unref(parser);
+
+ g_free(result);
+
+ DLEYNA_LOG_DEBUG("Exit");
+}
+
+void dls_device_get_children(dls_client_t *client,
+ dls_task_t *task,
+ const gchar *upnp_filter, const gchar *sort_by)
+{
+ dls_async_task_t *cb_data = (dls_async_task_t *)task;
+ dls_device_context_t *context;
+
+ DLEYNA_LOG_DEBUG("Enter");
+
+ context = dls_device_get_context(task->target.device, client);
+
+ cb_data->action =
+ gupnp_service_proxy_begin_action(context->service_proxy,
+ "Browse",
+ prv_get_children_cb,
+ cb_data,
+ "ObjectID", G_TYPE_STRING,
+ task->target.id,
+
+ "BrowseFlag", G_TYPE_STRING,
+ "BrowseDirectChildren",
+
+ "Filter", G_TYPE_STRING,
+ upnp_filter,
+
+ "StartingIndex", G_TYPE_INT,
+ task->ut.get_children.start,
+ "RequestedCount", G_TYPE_INT,
+ task->ut.get_children.count,
+ "SortCriteria", G_TYPE_STRING,
+ sort_by,
+ NULL);
+
+ cb_data->proxy = context->service_proxy;
+
+ g_object_add_weak_pointer((G_OBJECT(context->service_proxy)),
+ (gpointer *)&cb_data->proxy);
+
+ cb_data->cancel_id = g_cancellable_connect(
+ cb_data->cancellable,
+ G_CALLBACK(dls_async_task_cancelled_cb),
+ cb_data, NULL);
+
+ DLEYNA_LOG_DEBUG("Exit");
+}
+
+static void prv_get_item(GUPnPDIDLLiteParser *parser,
+ GUPnPDIDLLiteObject *object,
+ gpointer user_data)
+{
+ dls_async_task_t *cb_data = user_data;
+ dls_async_get_all_t *cb_task_data = &cb_data->ut.get_all;
+
+ if (!GUPNP_IS_DIDL_LITE_CONTAINER(object))
+ dls_props_add_item(cb_task_data->vb, object,
+ cb_data->task.target.root_path,
+ DLS_UPNP_MASK_ALL_PROPS,
+ cb_task_data->protocol_info);
+ else
+ cb_data->error = g_error_new(DLEYNA_SERVER_ERROR,
+ DLEYNA_ERROR_UNKNOWN_INTERFACE,
+ "Interface not supported on container.");
+}
+
+static void prv_get_container(GUPnPDIDLLiteParser *parser,
+ GUPnPDIDLLiteObject *object,
+ gpointer user_data)
+{
+ dls_async_task_t *cb_data = user_data;
+ dls_async_get_all_t *cb_task_data = &cb_data->ut.get_all;
+ gboolean have_child_count;
+
+ if (GUPNP_IS_DIDL_LITE_CONTAINER(object)) {
+ dls_props_add_container(cb_task_data->vb,
+ (GUPnPDIDLLiteContainer *)object,
+ DLS_UPNP_MASK_ALL_PROPS,
+ &have_child_count);
+ if (!have_child_count)
+ cb_task_data->need_child_count = TRUE;
+ } else {
+ cb_data->error = g_error_new(DLEYNA_SERVER_ERROR,
+ DLEYNA_ERROR_UNKNOWN_INTERFACE,
+ "Interface not supported on item.");
+ }
+}
+
+static void prv_get_object(GUPnPDIDLLiteParser *parser,
+ GUPnPDIDLLiteObject *object,
+ gpointer user_data)
+{
+ dls_async_task_t *cb_data = user_data;
+ dls_async_get_all_t *cb_task_data = &cb_data->ut.get_all;
+ const char *id;
+ const char *parent_path;
+ gchar *path = NULL;
+
+ id = gupnp_didl_lite_object_get_parent_id(object);
+
+ if (!id || !strcmp(id, "-1") || !strcmp(id, "")) {
+ parent_path = cb_data->task.target.root_path;
+ } else {
+ path = dls_path_from_id(cb_data->task.target.root_path, id);
+ parent_path = path;
+ }
+
+ if (!dls_props_add_object(cb_task_data->vb, object,
+ cb_data->task.target.root_path,
+ parent_path, DLS_UPNP_MASK_ALL_PROPS))
+ cb_data->error = g_error_new(DLEYNA_SERVER_ERROR,
+ DLEYNA_ERROR_BAD_RESULT,
+ "Unable to retrieve mandatory object properties");
+ g_free(path);
+}
+
+static void prv_get_all(GUPnPDIDLLiteParser *parser,
+ GUPnPDIDLLiteObject *object,
+ gpointer user_data)
+{
+ dls_async_task_t *cb_data = user_data;
+ dls_async_get_all_t *cb_task_data = &cb_data->ut.get_all;
+ gboolean have_child_count;
+
+ prv_get_object(parser, object, user_data);
+
+ if (!cb_data->error) {
+ if (GUPNP_IS_DIDL_LITE_CONTAINER(object)) {
+ dls_props_add_container(
+ cb_task_data->vb,
+ (GUPnPDIDLLiteContainer *)
+ object, DLS_UPNP_MASK_ALL_PROPS,
+ &have_child_count);
+ if (!have_child_count)
+ cb_task_data->need_child_count = TRUE;
+ } else {
+ dls_props_add_item(cb_task_data->vb,
+ object,
+ cb_data->task.target.root_path,
+ DLS_UPNP_MASK_ALL_PROPS,
+ cb_task_data->protocol_info);
+ }
+ }
+}
+
+static gboolean prv_subscribed(const dls_device_t *device)
+{
+ dls_device_context_t *context;
+ unsigned int i;
+ gboolean subscribed = FALSE;
+
+ for (i = 0; i < device->contexts->len; ++i) {
+ context = g_ptr_array_index(device->contexts, i);
+ if (context->subscribed) {
+ subscribed = TRUE;
+ break;
+ }
+ }
+
+ return subscribed;
+}
+
+static void prv_system_update_id_for_prop_cb(GUPnPServiceProxy *proxy,
+ GUPnPServiceProxyAction *action,
+ gpointer user_data)
+{
+ GError *upnp_error = NULL;
+ guint id;
+ dls_async_task_t *cb_data = user_data;
+
+ DLEYNA_LOG_DEBUG("Enter");
+
+ if (!gupnp_service_proxy_end_action(proxy, action, &upnp_error,
+ "Id", G_TYPE_UINT,
+ &id,
+ NULL)) {
+ DLEYNA_LOG_WARNING("Unable to retrieve ServiceUpdateID: %s %s",
+ g_quark_to_string(upnp_error->domain),
+ upnp_error->message);
+
+ cb_data->error = g_error_new(
+ DLEYNA_SERVER_ERROR,
+ DLEYNA_ERROR_OPERATION_FAILED,
+ "Unable to retrieve ServiceUpdateID: %s",
+ upnp_error->message);
+
+ goto on_complete;
+ }
+
+ cb_data->task.result = g_variant_ref_sink(g_variant_new_uint32(id));
+
+on_complete:
+
+ (void) g_idle_add(dls_async_task_complete, cb_data);
+ g_cancellable_disconnect(cb_data->cancellable, cb_data->cancel_id);
+
+ if (upnp_error)
+ g_error_free(upnp_error);
+
+ DLEYNA_LOG_DEBUG("Exit");
+}
+
+static void prv_get_system_update_id_for_prop(GUPnPServiceProxy *proxy,
+ const dls_device_t *device,
+ dls_async_task_t *cb_data)
+{
+ guint suid;
+
+ DLEYNA_LOG_DEBUG("Enter");
+
+ if (prv_subscribed(device)) {
+ suid = device->system_update_id;
+
+ cb_data->task.result = g_variant_ref_sink(
+ g_variant_new_uint32(suid));
+
+ (void) g_idle_add(dls_async_task_complete, cb_data);
+
+ goto on_complete;
+ }
+
+ gupnp_service_proxy_begin_action(proxy, "GetSystemUpdateID",
+ prv_system_update_id_for_prop_cb,
+ cb_data,
+ NULL);
+
+ cb_data->proxy = proxy;
+
+ g_object_add_weak_pointer((G_OBJECT(proxy)),
+ (gpointer *)&cb_data->proxy);
+
+ cb_data->cancel_id = g_cancellable_connect(
+ cb_data->cancellable,
+ G_CALLBACK(dls_async_task_cancelled_cb),
+ cb_data, NULL);
+
+on_complete:
+
+ DLEYNA_LOG_DEBUG("Exit");
+}
+
+static void prv_system_update_id_for_props_cb(GUPnPServiceProxy *proxy,
+ GUPnPServiceProxyAction *action,
+ gpointer user_data)
+{
+ GError *upnp_error = NULL;
+ guint id;
+ dls_async_task_t *cb_data = user_data;
+ dls_async_get_all_t *cb_task_data = &cb_data->ut.get_all;
+
+ DLEYNA_LOG_DEBUG("Enter");
+
+ if (!gupnp_service_proxy_end_action(proxy, action, &upnp_error,
+ "Id", G_TYPE_UINT,
+ &id,
+ NULL)) {
+ DLEYNA_LOG_WARNING("Unable to retrieve ServiceUpdateID: %s %s",
+ g_quark_to_string(upnp_error->domain),
+ upnp_error->message);
+
+ cb_data->error = g_error_new(
+ DLEYNA_SERVER_ERROR,
+ DLEYNA_ERROR_OPERATION_FAILED,
+ "Unable to retrieve ServiceUpdateID: %s",
+ upnp_error->message);
+
+ goto on_complete;
+ }
+
+ g_variant_builder_add(cb_task_data->vb, "{sv}",
+ DLS_SYSTEM_UPDATE_VAR,
+ g_variant_new_uint32(id));
+
+ cb_data->task.result = g_variant_ref_sink(g_variant_builder_end(
+ cb_task_data->vb));
+
+on_complete:
+
+ if (!cb_data->error)
+ prv_get_sr_token_for_props(proxy, cb_data->task.target.device,
+ cb_data);
+ else {
+ (void) g_idle_add(dls_async_task_complete, cb_data);
+ g_cancellable_disconnect(cb_data->cancellable,
+ cb_data->cancel_id);
+ }
+
+ if (upnp_error)
+ g_error_free(upnp_error);
+
+ DLEYNA_LOG_DEBUG("Exit");
+}
+
+static void prv_get_system_update_id_for_props(GUPnPServiceProxy *proxy,
+ const dls_device_t *device,
+ dls_async_task_t *cb_data)
+{
+ dls_async_get_all_t *cb_task_data;
+ guint suid;
+
+ DLEYNA_LOG_DEBUG("Enter");
+
+ if (prv_subscribed(device)) {
+ suid = device->system_update_id;
+
+ cb_task_data = &cb_data->ut.get_all;
+
+ g_variant_builder_add(cb_task_data->vb, "{sv}",
+ DLS_SYSTEM_UPDATE_VAR,
+ g_variant_new_uint32(suid));
+
+ prv_get_sr_token_for_props(proxy, device, cb_data);
+
+ goto on_complete;
+ }
+
+ gupnp_service_proxy_begin_action(proxy, "GetSystemUpdateID",
+ prv_system_update_id_for_props_cb,
+ cb_data,
+ NULL);
+
+ cb_data->proxy = proxy;
+
+ g_object_add_weak_pointer((G_OBJECT(proxy)),
+ (gpointer *)&cb_data->proxy);
+
+ cb_data->cancel_id = g_cancellable_connect(
+ cb_data->cancellable,
+ G_CALLBACK(dls_async_task_cancelled_cb),
+ cb_data, NULL);
+
+on_complete:
+
+ DLEYNA_LOG_DEBUG("Exit");
+}
+
+static int prv_get_media_server_version(const dls_device_t *device)
+{
+ dls_device_context_t *context;
+ const char *device_type;
+ const char *version;
+
+ context = dls_device_get_context(device, NULL);
+ device_type = gupnp_device_info_get_device_type((GUPnPDeviceInfo *)
+ context->device_proxy);
+
+ if (!g_str_has_prefix(device_type, DLS_DMS_DEVICE_TYPE))
+ goto on_error;
+
+ version = device_type + sizeof(DLS_DMS_DEVICE_TYPE) - 1;
+
+ return atoi(version);
+
+on_error:
+
+ return -1;
+}
+
+static void prv_service_reset_for_prop_cb(GUPnPServiceProxy *proxy,
+ GUPnPServiceProxyAction *action,
+ gpointer user_data)
+{
+ GError *upnp_error = NULL;
+ gchar *token = NULL;
+ dls_async_task_t *cb_data = user_data;
+
+ DLEYNA_LOG_DEBUG("Enter");
+
+ if (!gupnp_service_proxy_end_action(proxy, action, &upnp_error,
+ "ResetToken", G_TYPE_STRING,
+ &token,
+ NULL)) {
+ DLEYNA_LOG_WARNING(
+ "Unable to retrieve ServiceResetToken: %s %s",
+ g_quark_to_string(upnp_error->domain),
+ upnp_error->message);
+
+
+ cb_data->error = g_error_new(DLEYNA_SERVER_ERROR,
+ DLEYNA_ERROR_OPERATION_FAILED,
+ "GetServiceResetToken failed: %s",
+ upnp_error->message);
+
+ goto on_complete;
+ }
+
+ cb_data->task.result = g_variant_ref_sink(g_variant_new_string(token));
+
+ DLEYNA_LOG_DEBUG("Service Reset %s", token);
+
+ g_free(token);
+
+on_complete:
+
+ (void) g_idle_add(dls_async_task_complete, cb_data);
+ g_cancellable_disconnect(cb_data->cancellable, cb_data->cancel_id);
+
+ if (upnp_error)
+ g_error_free(upnp_error);
+
+ DLEYNA_LOG_DEBUG("Exit");
+}
+
+static void prv_get_sr_token_for_prop(GUPnPServiceProxy *proxy,
+ const dls_device_t *device,
+ dls_async_task_t *cb_data)
+{
+ DLEYNA_LOG_DEBUG("Enter");
+
+ if (prv_get_media_server_version(device) < 3) {
+ cb_data->error = g_error_new(DLEYNA_SERVER_ERROR,
+ DLEYNA_ERROR_UNKNOWN_PROPERTY,
+ "Unknown property");
+
+ (void) g_idle_add(dls_async_task_complete, cb_data);
+
+ goto on_error;
+ }
+
+ gupnp_service_proxy_begin_action(proxy, "GetServiceResetToken",
+ prv_service_reset_for_prop_cb,
+ cb_data,
+ NULL);
+
+ cb_data->proxy = proxy;
+
+ g_object_add_weak_pointer((G_OBJECT(proxy)),
+ (gpointer *)&cb_data->proxy);
+
+ cb_data->cancel_id = g_cancellable_connect(
+ cb_data->cancellable,
+ G_CALLBACK(dls_async_task_cancelled_cb),
+ cb_data, NULL);
+
+on_error:
+
+ DLEYNA_LOG_DEBUG("Exit");
+}
+
+static void prv_service_reset_for_props_cb(GUPnPServiceProxy *proxy,
+ GUPnPServiceProxyAction *action,
+ gpointer user_data)
+{
+ GError *upnp_error = NULL;
+ gchar *token = NULL;
+ dls_async_task_t *cb_data = user_data;
+ dls_async_get_all_t *cb_task_data;
+
+ DLEYNA_LOG_DEBUG("Enter");
+
+ if (!gupnp_service_proxy_end_action(proxy, action, &upnp_error,
+ "ResetToken", G_TYPE_STRING,
+ &token,
+ NULL)) {
+ DLEYNA_LOG_WARNING(
+ "Unable to retrieve ServiceResetToken: %s %s",
+ g_quark_to_string(upnp_error->domain),
+ upnp_error->message);
+
+ cb_data->error = g_error_new(DLEYNA_SERVER_ERROR,
+ DLEYNA_ERROR_OPERATION_FAILED,
+ "GetServiceResetToken failed: %s",
+ upnp_error->message);
+
+ goto on_complete;
+ }
+
+ cb_task_data = &cb_data->ut.get_all;
+ g_variant_builder_add(cb_task_data->vb, "{sv}",
+ DLS_INTERFACE_PROP_SV_SERVICE_RESET_TOKEN,
+ g_variant_new_string(token));
+
+ cb_data->task.result = g_variant_ref_sink(g_variant_builder_end(
+ cb_task_data->vb));
+
+ DLEYNA_LOG_DEBUG("Service Reset %s", token);
+
+ g_free(token);
+
+on_complete:
+
+ (void) g_idle_add(dls_async_task_complete, cb_data);
+ g_cancellable_disconnect(cb_data->cancellable, cb_data->cancel_id);
+
+ if (upnp_error)
+ g_error_free(upnp_error);
+
+ DLEYNA_LOG_DEBUG("Exit");
+}
+
+static void prv_get_sr_token_for_props(GUPnPServiceProxy *proxy,
+ const dls_device_t *device,
+ dls_async_task_t *cb_data)
+{
+ dls_async_get_all_t *cb_task_data;
+
+ DLEYNA_LOG_DEBUG("Enter");
+
+ if (prv_get_media_server_version(device) < 3) {
+ cb_task_data = &cb_data->ut.get_all;
+
+ cb_data->task.result = g_variant_ref_sink(g_variant_builder_end(
+ cb_task_data->vb));
+
+ goto on_complete; /* No error here, just skip the property */
+ }
+
+ gupnp_service_proxy_begin_action(proxy, "GetServiceResetToken",
+ prv_service_reset_for_props_cb,
+ cb_data,
+ NULL);
+
+ cb_data->proxy = proxy;
+
+ g_object_add_weak_pointer((G_OBJECT(proxy)),
+ (gpointer *)&cb_data->proxy);
+
+ cb_data->cancel_id = g_cancellable_connect(
+ cb_data->cancellable,
+ G_CALLBACK(dls_async_task_cancelled_cb),
+ cb_data, NULL);
+
+ DLEYNA_LOG_DEBUG("Exit");
+
+ return;
+
+on_complete:
+
+ (void) g_idle_add(dls_async_task_complete, cb_data);
+
+ DLEYNA_LOG_DEBUG("Exit");
+}
+
+static gboolean prv_get_all_child_count_cb(dls_async_task_t *cb_data,
+ gint count)
+{
+ dls_async_get_all_t *cb_task_data = &cb_data->ut.get_all;
+
+ dls_props_add_child_count(cb_task_data->vb, count);
+ if (cb_task_data->device_object)
+ prv_get_system_update_id_for_props(cb_data->proxy,
+ cb_data->task.target.device,
+ cb_data);
+ else
+ cb_data->task.result = g_variant_ref_sink(g_variant_builder_end(
+ cb_task_data->vb));
+
+ return !cb_task_data->device_object;
+}
+
+static void prv_get_all_ms2spec_props_cb(GUPnPServiceProxy *proxy,
+ GUPnPServiceProxyAction *action,
+ gpointer user_data)
+{
+ GError *upnp_error = NULL;
+ gchar *result = NULL;
+ GUPnPDIDLLiteParser *parser = NULL;
+ dls_async_task_t *cb_data = user_data;
+ dls_async_get_all_t *cb_task_data = &cb_data->ut.get_all;
+
+ DLEYNA_LOG_DEBUG("Enter");
+
+ if (!gupnp_service_proxy_end_action(cb_data->proxy, cb_data->action,
+ &upnp_error,
+ "Result", G_TYPE_STRING,
+ &result, NULL)) {
+ DLEYNA_LOG_WARNING("Browse operation failed: %s",
+ upnp_error->message);
+
+ cb_data->error = g_error_new(DLEYNA_SERVER_ERROR,
+ DLEYNA_ERROR_OPERATION_FAILED,
+ "Browse operation failed: %s",
+ upnp_error->message);
+ goto on_error;
+ }
+
+ DLEYNA_LOG_DEBUG("GetMS2SpecProps 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) {
+ DLEYNA_LOG_WARNING("Property not defined for object");
+
+ cb_data->error =
+ g_error_new(DLEYNA_SERVER_ERROR,
+ DLEYNA_ERROR_UNKNOWN_PROPERTY,
+ "Property not defined for object");
+ } else {
+ DLEYNA_LOG_WARNING(
+ "Unable to parse results of browse: %s",
+ upnp_error->message);
+
+ cb_data->error =
+ g_error_new(DLEYNA_SERVER_ERROR,
+ DLEYNA_ERROR_OPERATION_FAILED,
+ "Unable to parse results of browse: %s",
+ upnp_error->message);
+ }
+ goto on_error;
+ }
+
+ if (cb_data->error)
+ goto on_error;
+
+ if (cb_task_data->need_child_count) {
+ DLEYNA_LOG_DEBUG("Need Child Count");
+
+ prv_get_child_count(cb_data, prv_get_all_child_count_cb,
+ cb_data->task.target.id);
+
+ goto no_complete;
+ } else if (cb_data->task.type == DLS_TASK_GET_ALL_PROPS &&
+ cb_task_data->device_object) {
+ prv_get_system_update_id_for_props(proxy,
+ cb_data->task.target.device,
+ cb_data);
+
+ goto no_complete;
+ } else {
+ cb_data->task.result = g_variant_ref_sink(g_variant_builder_end(
+ cb_task_data->vb));
+ }
+
+on_error:
+
+ (void) g_idle_add(dls_async_task_complete, cb_data);
+ g_cancellable_disconnect(cb_data->cancellable, cb_data->cancel_id);
+
+no_complete:
+
+ if (upnp_error)
+ g_error_free(upnp_error);
+
+ if (parser)
+ g_object_unref(parser);
+
+ g_free(result);
+
+ DLEYNA_LOG_DEBUG("Exit");
+}
+
+static void prv_get_all_ms2spec_props(dls_device_context_t *context,
+ dls_async_task_t *cb_data)
+{
+ dls_async_get_all_t *cb_task_data = &cb_data->ut.get_all;
+ dls_task_t *task = &cb_data->task;
+ dls_task_get_props_t *task_data = &task->ut.get_props;
+
+ DLEYNA_LOG_DEBUG("Enter called");
+
+ if (!strcmp(DLS_INTERFACE_MEDIA_CONTAINER, task_data->interface_name)) {
+ cb_task_data->prop_func = G_CALLBACK(prv_get_container);
+ } else if (!strcmp(DLS_INTERFACE_MEDIA_ITEM,
+ task_data->interface_name)) {
+ cb_task_data->prop_func = G_CALLBACK(prv_get_item);
+ } else if (!strcmp(DLS_INTERFACE_MEDIA_OBJECT,
+ task_data->interface_name)) {
+ cb_task_data->prop_func = G_CALLBACK(prv_get_object);
+ } else if (!strcmp("", task_data->interface_name)) {
+ cb_task_data->prop_func = G_CALLBACK(prv_get_all);
+ } else {
+ DLEYNA_LOG_WARNING("Interface is unknown.");
+
+ cb_data->error =
+ g_error_new(DLEYNA_SERVER_ERROR,
+ DLEYNA_ERROR_UNKNOWN_INTERFACE,
+ "Interface is unknown.");
+ goto on_error;
+ }
+
+ cb_data->action = gupnp_service_proxy_begin_action(
+ context->service_proxy, "Browse",
+ prv_get_all_ms2spec_props_cb, cb_data,
+ "ObjectID", G_TYPE_STRING, task->target.id,
+ "BrowseFlag", G_TYPE_STRING, "BrowseMetadata",
+ "Filter", G_TYPE_STRING, "*",
+ "StartingIndex", G_TYPE_INT, 0,
+ "RequestedCount", G_TYPE_INT, 0,
+ "SortCriteria", G_TYPE_STRING,
+ "", NULL);
+
+ cb_data->proxy = context->service_proxy;
+
+ g_object_add_weak_pointer((G_OBJECT(context->service_proxy)),
+ (gpointer *)&cb_data->proxy);
+
+ cb_data->cancel_id = g_cancellable_connect(
+ cb_data->cancellable,
+ G_CALLBACK(dls_async_task_cancelled_cb),
+ cb_data, NULL);
+
+ DLEYNA_LOG_DEBUG("Exit with SUCCESS");
+
+ return;
+
+on_error:
+
+ (void) g_idle_add(dls_async_task_complete, cb_data);
+
+ DLEYNA_LOG_DEBUG("Exit with FAIL");
+
+ return;
+}
+
+void dls_device_get_all_props(dls_client_t *client,
+ dls_task_t *task,
+ gboolean root_object)
+{
+ dls_async_task_t *cb_data = (dls_async_task_t *)task;
+ dls_async_get_all_t *cb_task_data;
+ dls_task_get_props_t *task_data = &task->ut.get_props;
+ dls_device_context_t *context;
+
+ DLEYNA_LOG_DEBUG("Enter");
+
+ context = dls_device_get_context(task->target.device, client);
+ cb_task_data = &cb_data->ut.get_all;
+
+ cb_task_data->vb = g_variant_builder_new(G_VARIANT_TYPE("a{sv}"));
+ cb_task_data->device_object = root_object;
+
+ if (!strcmp(task_data->interface_name,
+ DLEYNA_SERVER_INTERFACE_MEDIA_DEVICE)) {
+ if (root_object) {
+ dls_props_add_device(
+ (GUPnPDeviceInfo *)context->device_proxy,
+ task->target.device,
+ cb_task_data->vb);
+
+ prv_get_system_update_id_for_props(
+ context->service_proxy,
+ task->target.device,
+ cb_data);
+ } else {
+ cb_data->error =
+ g_error_new(DLEYNA_SERVER_ERROR,
+ DLEYNA_ERROR_UNKNOWN_INTERFACE,
+ "Interface is only valid on root objects.");
+
+ (void) g_idle_add(dls_async_task_complete, cb_data);
+ }
+
+ } else if (strcmp(task_data->interface_name, "")) {
+ cb_task_data->device_object = FALSE;
+ prv_get_all_ms2spec_props(context, cb_data);
+ } else {
+ if (root_object)
+ dls_props_add_device(
+ (GUPnPDeviceInfo *)context->device_proxy,
+ task->target.device,
+ cb_task_data->vb);
+
+ prv_get_all_ms2spec_props(context, cb_data);
+ }
+
+ DLEYNA_LOG_DEBUG("Exit");
+}
+
+static void prv_get_object_property(GUPnPDIDLLiteParser *parser,
+ GUPnPDIDLLiteObject *object,
+ gpointer user_data)
+{
+ dls_async_task_t *cb_data = user_data;
+ dls_task_t *task = &cb_data->task;
+ dls_task_get_prop_t *task_data = &task->ut.get_prop;
+
+ if (cb_data->task.result)
+ goto on_error;
+
+ cb_data->task.result = dls_props_get_object_prop(task_data->prop_name,
+ task->target.root_path,
+ object);
+
+on_error:
+
+ return;
+}
+
+static void prv_get_item_property(GUPnPDIDLLiteParser *parser,
+ GUPnPDIDLLiteObject *object,
+ gpointer user_data)
+{
+ dls_async_task_t *cb_data = user_data;
+ dls_task_t *task = &cb_data->task;
+ dls_task_get_prop_t *task_data = &task->ut.get_prop;
+ dls_async_get_prop_t *cb_task_data = &cb_data->ut.get_prop;
+
+ if (cb_data->task.result)
+ goto on_error;
+
+ cb_data->task.result = dls_props_get_item_prop(
+ task_data->prop_name,
+ task->target.root_path,
+ object,
+ cb_task_data->protocol_info);
+
+on_error:
+
+ return;
+}
+
+static void prv_get_container_property(GUPnPDIDLLiteParser *parser,
+ GUPnPDIDLLiteObject *object,
+ gpointer user_data)
+{
+ dls_async_task_t *cb_data = user_data;
+ dls_task_t *task = &cb_data->task;
+ dls_task_get_prop_t *task_data = &task->ut.get_prop;
+
+ if (cb_data->task.result)
+ goto on_error;
+
+ cb_data->task.result = dls_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)
+{
+ dls_async_task_t *cb_data = user_data;
+
+ prv_get_object_property(parser, object, user_data);
+
+ if (cb_data->task.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(dls_async_task_t *cb_data,
+ gint count)
+{
+ DLEYNA_LOG_DEBUG("Enter");
+
+ DLEYNA_LOG_DEBUG("Count %d", count);
+
+ cb_data->task.result = g_variant_ref_sink(
+ g_variant_new_uint32((guint) count));
+
+ DLEYNA_LOG_DEBUG("Exit");
+
+ return TRUE;
+}
+
+static void prv_count_children_cb(GUPnPServiceProxy *proxy,
+ GUPnPServiceProxyAction *action,
+ gpointer user_data)
+{
+ dls_device_count_data_t *count_data = user_data;
+ dls_async_task_t *cb_data = count_data->cb_data;
+ GError *upnp_error = NULL;
+ gint count;
+ gboolean complete = FALSE;
+
+ DLEYNA_LOG_DEBUG("Enter");
+
+ if (!gupnp_service_proxy_end_action(cb_data->proxy, cb_data->action,
+ &upnp_error,
+ "TotalMatches", G_TYPE_INT,
+ &count,
+ NULL)) {
+ DLEYNA_LOG_WARNING("Browse operation failed: %s",
+ upnp_error->message);
+
+ cb_data->error = g_error_new(DLEYNA_SERVER_ERROR,
+ DLEYNA_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(dls_async_task_complete, cb_data);
+ g_cancellable_disconnect(cb_data->cancellable,
+ cb_data->cancel_id);
+ }
+
+ if (upnp_error)
+ g_error_free(upnp_error);
+
+ DLEYNA_LOG_DEBUG("Exit");
+}
+
+static void prv_get_child_count(dls_async_task_t *cb_data,
+ dls_device_count_cb_t cb, const gchar *id)
+{
+ dls_device_count_data_t *count_data;
+
+ DLEYNA_LOG_DEBUG("Enter");
+
+ prv_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);
+
+ DLEYNA_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;
+ dls_async_task_t *cb_data = user_data;
+ dls_async_get_prop_t *cb_task_data = &cb_data->ut.get_prop;
+ dls_task_get_prop_t *task_data = &cb_data->task.ut.get_prop;
+
+ DLEYNA_LOG_DEBUG("Enter");
+
+ if (!gupnp_service_proxy_end_action(cb_data->proxy, cb_data->action,
+ &upnp_error,
+ "Result", G_TYPE_STRING,
+ &result, NULL)) {
+ DLEYNA_LOG_WARNING("Browse operation failed: %s",
+ upnp_error->message);
+
+ cb_data->error = g_error_new(DLEYNA_SERVER_ERROR,
+ DLEYNA_ERROR_OPERATION_FAILED,
+ "Browse operation failed: %s",
+ upnp_error->message);
+ goto on_error;
+ }
+
+ DLEYNA_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) {
+ DLEYNA_LOG_WARNING("Property not defined for object");
+
+ cb_data->error =
+ g_error_new(DLEYNA_SERVER_ERROR,
+ DLEYNA_ERROR_UNKNOWN_PROPERTY,
+ "Property not defined for object");
+ } else {
+ DLEYNA_LOG_WARNING(
+ "Unable to parse results of browse: %s",
+ upnp_error->message);
+
+ cb_data->error =
+ g_error_new(DLEYNA_SERVER_ERROR,
+ DLEYNA_ERROR_OPERATION_FAILED,
+ "Unable to parse results of browse: %s",
+ upnp_error->message);
+ }
+ goto on_error;
+ }
+
+ if (!cb_data->task.result) {
+ DLEYNA_LOG_WARNING("Property not defined for object");
+
+ cb_data->error = g_error_new(DLEYNA_SERVER_ERROR,
+ DLEYNA_ERROR_UNKNOWN_PROPERTY,
+ "Property not defined for object");
+ }
+
+on_error:
+
+ if (cb_data->error && !strcmp(task_data->prop_name,
+ DLS_INTERFACE_PROP_CHILD_COUNT)) {
+ DLEYNA_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->task.target.id);
+ } else {
+ (void) g_idle_add(dls_async_task_complete, 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);
+
+ DLEYNA_LOG_DEBUG("Exit");
+}
+
+static void prv_get_ms2spec_prop(dls_device_context_t *context,
+ dls_prop_map_t *prop_map,
+ dls_task_get_prop_t *task_data,
+ dls_async_task_t *cb_data)
+{
+ dls_async_get_prop_t *cb_task_data;
+ const gchar *filter;
+
+ DLEYNA_LOG_DEBUG("Enter");
+
+ cb_task_data = &cb_data->ut.get_prop;
+
+ if (!prop_map) {
+ cb_data->error = g_error_new(DLEYNA_SERVER_ERROR,
+ DLEYNA_ERROR_UNKNOWN_PROPERTY,
+ "Unknown property");
+ goto on_error;
+ }
+
+ filter = prop_map->filter ? prop_map->upnp_prop_name : "";
+
+ if (!strcmp(DLS_INTERFACE_MEDIA_CONTAINER, task_data->interface_name)) {
+ cb_task_data->prop_func =
+ G_CALLBACK(prv_get_container_property);
+ } else if (!strcmp(DLS_INTERFACE_MEDIA_ITEM,
+ task_data->interface_name)) {
+ cb_task_data->prop_func = G_CALLBACK(prv_get_item_property);
+ } else if (!strcmp(DLS_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 {
+ DLEYNA_LOG_WARNING("Interface is unknown.%s",
+ task_data->interface_name);
+
+ cb_data->error = g_error_new(DLEYNA_SERVER_ERROR,
+ DLEYNA_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->task.target.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;
+
+ g_object_add_weak_pointer((G_OBJECT(context->service_proxy)),
+ (gpointer *)&cb_data->proxy);
+
+ cb_data->cancel_id = g_cancellable_connect(
+ cb_data->cancellable,
+ G_CALLBACK(dls_async_task_cancelled_cb),
+ cb_data, NULL);
+
+ DLEYNA_LOG_DEBUG("Exit with SUCCESS");
+
+ return;
+
+on_error:
+
+ (void) g_idle_add(dls_async_task_complete, cb_data);
+
+ DLEYNA_LOG_DEBUG("Exit with FAIL");
+
+ return;
+}
+
+void dls_device_get_prop(dls_client_t *client,
+ dls_task_t *task,
+ dls_prop_map_t *prop_map, gboolean root_object)
+{
+ dls_async_task_t *cb_data = (dls_async_task_t *)task;
+ dls_task_get_prop_t *task_data = &task->ut.get_prop;
+ dls_device_context_t *context;
+ gboolean complete = FALSE;
+
+ DLEYNA_LOG_DEBUG("Enter");
+
+ context = dls_device_get_context(task->target.device, client);
+
+ if (!strcmp(task_data->interface_name,
+ DLEYNA_SERVER_INTERFACE_MEDIA_DEVICE)) {
+ if (root_object) {
+ if (!strcmp(
+ task_data->prop_name,
+ DLS_INTERFACE_PROP_ESV_SYSTEM_UPDATE_ID)) {
+ prv_get_system_update_id_for_prop(
+ context->service_proxy,
+ task->target.device,
+ cb_data);
+ } else if (!strcmp(
+ task_data->prop_name,
+ DLS_INTERFACE_PROP_SV_SERVICE_RESET_TOKEN)) {
+ prv_get_sr_token_for_prop(
+ context->service_proxy,
+ task->target.device,
+ cb_data);
+ } else {
+ cb_data->task.result =
+ dls_props_get_device_prop(
+ (GUPnPDeviceInfo *)
+ context->device_proxy,
+ task->target.device,
+ task_data->prop_name);
+
+ if (!cb_data->task.result)
+ cb_data->error = g_error_new(
+ DLEYNA_SERVER_ERROR,
+ DLEYNA_ERROR_UNKNOWN_PROPERTY,
+ "Unknown property");
+
+ (void) g_idle_add(dls_async_task_complete,
+ cb_data);
+ }
+
+ } else {
+ cb_data->error =
+ g_error_new(DLEYNA_SERVER_ERROR,
+ DLEYNA_ERROR_UNKNOWN_INTERFACE,
+ "Interface is unknown.");
+
+ (void) g_idle_add(dls_async_task_complete, cb_data);
+ }
+
+ } else if (strcmp(task_data->interface_name, "")) {
+ prv_get_ms2spec_prop(context, prop_map, &task->ut.get_prop,
+ cb_data);
+ } else {
+ if (root_object) {
+ if (!strcmp(
+ task_data->prop_name,
+ DLS_INTERFACE_PROP_ESV_SYSTEM_UPDATE_ID)) {
+ prv_get_system_update_id_for_prop(
+ context->service_proxy,
+ task->target.device,
+ cb_data);
+ complete = TRUE;
+ } else if (!strcmp(
+ task_data->prop_name,
+ DLS_INTERFACE_PROP_SV_SERVICE_RESET_TOKEN)) {
+ prv_get_sr_token_for_prop(
+ context->service_proxy,
+ task->target.device,
+ cb_data);
+ complete = TRUE;
+ } else {
+ cb_data->task.result =
+ dls_props_get_device_prop(
+ (GUPnPDeviceInfo *)
+ context->device_proxy,
+ task->target.device,
+ task_data->prop_name);
+ if (cb_data->task.result) {
+ (void) g_idle_add(
+ dls_async_task_complete,
+ cb_data);
+ complete = TRUE;
+ }
+ }
+ }
+
+ if (!complete)
+ prv_get_ms2spec_prop(context, prop_map,
+ &task->ut.get_prop,
+ cb_data);
+ }
+
+ DLEYNA_LOG_DEBUG("Exit");
+}
+
+static void prv_found_target(GUPnPDIDLLiteParser *parser,
+ GUPnPDIDLLiteObject *object,
+ gpointer user_data)
+{
+ dls_async_task_t *cb_data = user_data;
+ dls_async_bas_t *cb_task_data = &cb_data->ut.bas;
+ const char *id;
+ const char *parent_path;
+ gchar *path = NULL;
+ gboolean have_child_count;
+ dls_device_object_builder_t *builder;
+
+ DLEYNA_LOG_DEBUG("Enter");
+
+ builder = g_new0(dls_device_object_builder_t, 1);
+
+ id = gupnp_didl_lite_object_get_parent_id(object);
+
+ if (!id || !strcmp(id, "-1") || !strcmp(id, "")) {
+ parent_path = cb_data->task.target.root_path;
+ } else {
+ path = dls_path_from_id(cb_data->task.target.root_path, id);
+ parent_path = path;
+ }
+
+ builder->vb = g_variant_builder_new(G_VARIANT_TYPE("a{sv}"));
+
+ if (!dls_props_add_object(builder->vb, object,
+ cb_data->task.target.root_path,
+ parent_path, cb_task_data->filter_mask))
+ goto on_error;
+
+ if (GUPNP_IS_DIDL_LITE_CONTAINER(object)) {
+ dls_props_add_container(builder->vb,
+ (GUPnPDIDLLiteContainer *)object,
+ cb_task_data->filter_mask,
+ &have_child_count);
+
+ if (!have_child_count && (cb_task_data->filter_mask &
+ DLS_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 {
+ dls_props_add_item(builder->vb,
+ object,
+ cb_data->task.target.root_path,
+ cb_task_data->filter_mask,
+ cb_task_data->protocol_info);
+ }
+
+ g_ptr_array_add(cb_task_data->vbs, builder);
+ g_free(path);
+
+ DLEYNA_LOG_DEBUG("Exit with SUCCESS");
+
+ return;
+
+on_error:
+
+ g_free(path);
+ prv_object_builder_delete(builder);
+
+ DLEYNA_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;
+ dls_async_task_t *cb_data = user_data;
+ dls_async_bas_t *cb_task_data = &cb_data->ut.bas;
+
+ DLEYNA_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)) {
+
+ DLEYNA_LOG_WARNING("Search operation failed %s",
+ upnp_error->message);
+
+ cb_data->error = g_error_new(DLEYNA_SERVER_ERROR,
+ DLEYNA_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_object_builder_delete);
+
+ g_signal_connect(parser, "object-available" ,
+ G_CALLBACK(prv_found_target), cb_data);
+
+ DLEYNA_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) {
+ DLEYNA_LOG_WARNING("Unable to parse results of search: %s",
+ upnp_error->message);
+
+ cb_data->error = g_error_new(DLEYNA_SERVER_ERROR,
+ DLEYNA_ERROR_OPERATION_FAILED,
+ "Unable to parse results of search: %s",
+ upnp_error->message);
+ goto on_error;
+ }
+
+ if (cb_task_data->need_child_count) {
+ DLEYNA_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(dls_async_task_complete, 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);
+
+ DLEYNA_LOG_DEBUG("Exit");
+}
+
+void dls_device_search(dls_client_t *client,
+ dls_task_t *task,
+ const gchar *upnp_filter, const gchar *upnp_query,
+ const gchar *sort_by)
+{
+ dls_async_task_t *cb_data = (dls_async_task_t *)task;
+ dls_device_context_t *context;
+
+ DLEYNA_LOG_DEBUG("Enter");
+
+ context = dls_device_get_context(task->target.device, client);
+
+ cb_data->action = gupnp_service_proxy_begin_action(
+ context->service_proxy, "Search",
+ prv_search_cb,
+ cb_data,
+ "ContainerID", G_TYPE_STRING, task->target.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;
+
+ g_object_add_weak_pointer((G_OBJECT(context->service_proxy)),
+ (gpointer *)&cb_data->proxy);
+
+ cb_data->cancel_id = g_cancellable_connect(
+ cb_data->cancellable,
+ G_CALLBACK(dls_async_task_cancelled_cb),
+ cb_data, NULL);
+
+ DLEYNA_LOG_DEBUG("Exit");
+}
+
+static void prv_get_resource(GUPnPDIDLLiteParser *parser,
+ GUPnPDIDLLiteObject *object,
+ gpointer user_data)
+{
+ dls_async_task_t *cb_data = user_data;
+ dls_task_t *task = &cb_data->task;
+ dls_task_get_resource_t *task_data = &task->ut.resource;
+ dls_async_get_all_t *cb_task_data = &cb_data->ut.get_all;
+
+ DLEYNA_LOG_DEBUG("Enter");
+
+ dls_props_add_resource(cb_task_data->vb, object,
+ cb_task_data->filter_mask,
+ task_data->protocol_info);
+}
+
+void dls_device_get_resource(dls_client_t *client,
+ dls_task_t *task,
+ const gchar *upnp_filter)
+{
+ dls_async_task_t *cb_data = (dls_async_task_t *)task;
+ dls_async_get_all_t *cb_task_data;
+ dls_device_context_t *context;
+
+ context = dls_device_get_context(task->target.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, task->target.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;
+
+ g_object_add_weak_pointer((G_OBJECT(context->service_proxy)),
+ (gpointer *)&cb_data->proxy);
+
+ cb_data->cancel_id = g_cancellable_connect(
+ cb_data->cancellable,
+ G_CALLBACK(dls_async_task_cancelled_cb),
+ cb_data, NULL);
+
+ DLEYNA_LOG_DEBUG("Exit");
+}
+
+static gchar *prv_create_new_container_didl(const gchar *parent_id,
+ dls_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 = dls_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 = dls_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 const gchar *prv_get_dlna_profile_name(const gchar *filename)
+{
+ gchar *uri;
+ GError *error = NULL;
+ const gchar *profile_name = NULL;
+ GUPnPDLNAProfile *profile;
+ GUPnPDLNAProfileGuesser *guesser;
+ gboolean relaxed_mode = TRUE;
+ gboolean extended_mode = TRUE;
+
+ guesser = gupnp_dlna_profile_guesser_new(relaxed_mode, extended_mode);
+
+ uri = g_filename_to_uri(filename, NULL, &error);
+ if (uri == NULL) {
+ DLEYNA_LOG_WARNING("Unable to convert filename: %s", filename);
+
+ if (error) {
+ DLEYNA_LOG_WARNING("Error: %s", error->message);
+
+ g_error_free(error);
+ }
+
+ goto on_error;
+ }
+
+ profile = gupnp_dlna_profile_guesser_guess_profile_sync(guesser,
+ uri,
+ 5000,
+ NULL,
+ &error);
+ if (profile == NULL) {
+ DLEYNA_LOG_WARNING("Unable to guess profile for URI: %s", uri);
+
+ if (error) {
+ DLEYNA_LOG_WARNING("Error: %s", error->message);
+
+ g_error_free(error);
+ }
+
+ goto on_error;
+ }
+
+ profile_name = gupnp_dlna_profile_get_name(profile);
+
+on_error:
+ g_object_unref(guesser);
+
+ g_free(uri);
+
+ return profile_name;
+}
+
+static gchar *prv_create_upload_didl(const gchar *parent_id, dls_task_t *task,
+ const gchar *object_class,
+ const gchar *mime_type)
+{
+ GUPnPDIDLLiteWriter *writer;
+ GUPnPDIDLLiteObject *item;
+ gchar *retval;
+ GUPnPProtocolInfo *protocol_info;
+ GUPnPDIDLLiteResource *res;
+ const gchar *profile;
+
+ 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, "*");
+
+ profile = prv_get_dlna_profile_name(task->ut.upload.file_path);
+ if (profile != NULL)
+ gupnp_protocol_info_set_dlna_profile(protocol_info, profile);
+
+ res = gupnp_didl_lite_object_add_resource(item);
+ gupnp_didl_lite_resource_set_protocol_info(res, protocol_info);
+
+ 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)
+{
+ dls_async_task_t *cb_data = user_data;
+
+ DLEYNA_LOG_DEBUG("Enter");
+
+ (void) gupnp_service_proxy_end_action(cb_data->proxy, cb_data->action,
+ NULL, NULL);
+ (void) g_idle_add(dls_async_task_complete, cb_data);
+ g_cancellable_disconnect(cb_data->cancellable, cb_data->cancel_id);
+
+ DLEYNA_LOG_DEBUG("Exit");
+}
+
+static void prv_upload_job_delete(gpointer up_job)
+{
+ dls_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)
+{
+ dls_device_upload_job_t *upload_job = user_data;
+ dls_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);
+
+ DLEYNA_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(dls_device_upload_job_t *upload_job,
+ dls_device_upload_t *upload)
+{
+ GVariant *args;
+
+ args = g_variant_new("(ustt)", upload_job->upload_id, upload->status,
+ upload->bytes_uploaded, upload->bytes_to_upload);
+
+ DLEYNA_LOG_DEBUG(
+ "Emitting: %s (%u %s %"G_GUINT64_FORMAT" %"G_GUINT64_FORMAT")"
+ " on %s",
+ DLS_INTERFACE_UPLOAD_UPDATE, upload_job->upload_id,
+ upload->status, upload->bytes_uploaded,
+ upload->bytes_to_upload, upload_job->device->path);
+
+ (void) dls_server_get_connector()->notify(
+ upload_job->device->connection,
+ upload_job->device->path,
+ DLEYNA_SERVER_INTERFACE_MEDIA_DEVICE,
+ DLS_INTERFACE_UPLOAD_UPDATE,
+ args,
+ NULL);
+}
+
+static void prv_post_finished(SoupSession *session, SoupMessage *msg,
+ gpointer user_data)
+{
+ dls_device_upload_job_t *upload_job = user_data;
+ dls_device_upload_t *upload;
+ gint *upload_id;
+
+ DLEYNA_LOG_DEBUG("Enter");
+
+ DLEYNA_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) {
+ DLEYNA_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 = DLS_UPLOAD_STATUS_COMPLETED;
+ upload->bytes_uploaded = upload->bytes_to_upload;
+ } else if (msg->status_code == SOUP_STATUS_CANCELLED) {
+ upload->status = DLS_UPLOAD_STATUS_CANCELLED;
+ } else {
+ upload->status = DLS_UPLOAD_STATUS_ERROR;
+ }
+
+ DLEYNA_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;
+
+ g_free(upload->body);
+ upload->body = 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_upload_job_delete(upload_job);
+
+ DLEYNA_LOG_DEBUG("Exit");
+}
+
+static void prv_upload_delete(gpointer up)
+{
+ dls_device_upload_t *upload = up;
+
+ DLEYNA_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);
+ else if (upload->body)
+ g_free(upload->body);
+
+ g_free(upload);
+ }
+
+ DLEYNA_LOG_DEBUG("Exit");
+}
+
+static void prv_post_bytes_written(SoupMessage *msg, SoupBuffer *chunk,
+ gpointer user_data)
+{
+ dls_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 dls_device_upload_t *prv_upload_data_new(const gchar *file_path,
+ gchar *body,
+ gsize body_length,
+ const gchar *import_uri,
+ const gchar *mime_type,
+ GError **error)
+{
+ dls_device_upload_t *upload = NULL;
+ GMappedFile *mapped_file = NULL;
+ gchar *up_body = body;
+ gsize up_body_length = body_length;
+
+ DLEYNA_LOG_DEBUG("Enter");
+
+ if (file_path) {
+ mapped_file = g_mapped_file_new(file_path, FALSE, NULL);
+ if (!mapped_file) {
+ DLEYNA_LOG_WARNING("Unable to map %s into memory",
+ file_path);
+
+ *error = g_error_new(DLEYNA_SERVER_ERROR,
+ DLEYNA_ERROR_IO,
+ "Unable to map %s into memory",
+ file_path);
+ goto on_error;
+ }
+
+ up_body = g_mapped_file_get_contents(mapped_file);
+ up_body_length = g_mapped_file_get_length(mapped_file);
+ }
+
+ upload = g_new0(dls_device_upload_t, 1);
+
+ upload->soup_session = soup_session_async_new();
+ upload->msg = soup_message_new("POST", import_uri);
+ upload->mapped_file = mapped_file;
+ upload->body = body;
+ upload->body_length = body_length;
+
+ if (!upload->msg) {
+ DLEYNA_LOG_WARNING("Invalid URI %s", import_uri);
+
+ *error = g_error_new(DLEYNA_SERVER_ERROR,
+ DLEYNA_ERROR_BAD_RESULT,
+ "Invalid URI %s", import_uri);
+ goto on_error;
+ }
+
+ upload->status = DLS_UPLOAD_STATUS_IN_PROGRESS;
+ upload->bytes_to_upload = up_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,
+ up_body, up_body_length);
+ g_signal_connect(upload->msg, "wrote-body-data",
+ G_CALLBACK(prv_post_bytes_written), upload);
+
+ DLEYNA_LOG_DEBUG("Exit with Success");
+
+ return upload;
+
+on_error:
+
+ prv_upload_delete(upload);
+
+ DLEYNA_LOG_WARNING("Exit with Fail");
+
+ return NULL;
+}
+
+static void prv_create_container_cb(GUPnPServiceProxy *proxy,
+ GUPnPServiceProxyAction *action,
+ gpointer user_data)
+{
+ dls_async_task_t *cb_data = user_data;
+ GError *upnp_error = NULL;
+ gchar *result = NULL;
+ gchar *object_id = NULL;
+ gchar *object_path;
+
+ DLEYNA_LOG_DEBUG("Enter");
+
+ if (!gupnp_service_proxy_end_action(cb_data->proxy, cb_data->action,
+ &upnp_error,
+ "ObjectID", G_TYPE_STRING,
+ &object_id,
+ "Result", G_TYPE_STRING,
+ &result,
+ NULL)) {
+ DLEYNA_LOG_WARNING("Create Object operation failed: %s",
+ upnp_error->message);
+
+ cb_data->error = g_error_new(DLEYNA_SERVER_ERROR,
+ DLEYNA_ERROR_OPERATION_FAILED,
+ "Create Object operation failed: %s",
+ upnp_error->message);
+ goto on_error;
+ }
+
+ object_path = dls_path_from_id(cb_data->task.target.root_path,
+ object_id);
+ cb_data->task.result = g_variant_ref_sink(g_variant_new_object_path(
+ object_path));
+ g_free(object_path);
+
+on_error:
+
+ (void) g_idle_add(dls_async_task_complete, 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);
+
+ DLEYNA_LOG_DEBUG("Exit");
+}
+
+static void prv_generic_upload_cb(dls_async_task_t *cb_data,
+ char *file_path,
+ gchar *body,
+ gsize body_length,
+ const gchar *mime_type)
+{
+ gchar *object_id = NULL;
+ gchar *result = NULL;
+ gchar *import_uri = NULL;
+ gchar *object_path;
+ GError *error = NULL;
+ gboolean delete_needed = FALSE;
+ gint *upload_id;
+ GUPnPDIDLLiteParser *parser = NULL;
+ GVariant *out_p[2];
+ dls_device_upload_t *upload;
+ dls_device_upload_job_t *upload_job;
+
+ DLEYNA_LOG_DEBUG("Enter");
+
+ if (!gupnp_service_proxy_end_action(cb_data->proxy, cb_data->action,
+ &error,
+ "ObjectID", G_TYPE_STRING,
+ &object_id,
+ "Result", G_TYPE_STRING,
+ &result,
+ NULL)) {
+ DLEYNA_LOG_WARNING("Create Object operation failed: %s",
+ error->message);
+
+ cb_data->error = g_error_new(DLEYNA_SERVER_ERROR,
+ DLEYNA_ERROR_OPERATION_FAILED,
+ "Create Object operation "
+ " failed: %s",
+ error->message);
+ goto on_error;
+ }
+
+ DLEYNA_LOG_DEBUG_NL();
+ DLEYNA_LOG_DEBUG("Create Object Result: %s", result);
+ DLEYNA_LOG_DEBUG_NL();
+
+ delete_needed = TRUE;
+
+ parser = gupnp_didl_lite_parser_new();
+
+ g_signal_connect(parser, "object-available" ,
+ G_CALLBACK(prv_extract_import_uri), &import_uri);
+
+ if (!gupnp_didl_lite_parser_parse_didl(parser, result, &error) &&
+ error->code != GUPNP_XML_ERROR_EMPTY_NODE) {
+
+ DLEYNA_LOG_WARNING(
+ "Unable to parse results of CreateObject: %s",
+ error->message);
+
+ cb_data->error = g_error_new(DLEYNA_SERVER_ERROR,
+ DLEYNA_ERROR_OPERATION_FAILED,
+ "Unable to parse results of CreateObject: %s",
+ error->message);
+ goto on_error;
+ }
+
+ if (!import_uri) {
+ DLEYNA_LOG_WARNING("Missing Import URI");
+
+ cb_data->error = g_error_new(DLEYNA_SERVER_ERROR,
+ DLEYNA_ERROR_OPERATION_FAILED,
+ "Missing Import URI");
+ goto on_error;
+ }
+
+ DLEYNA_LOG_DEBUG("Import URI %s", import_uri);
+
+ upload = prv_upload_data_new(file_path, body, body_length,
+ import_uri, mime_type, &cb_data->error);
+
+ if (!upload)
+ goto on_error;
+
+ upload_job = g_new0(dls_device_upload_job_t, 1);
+ upload_job->device = cb_data->task.target.device;
+ upload_job->upload_id = (gint) cb_data->task.target.device->upload_id;
+
+ soup_session_queue_message(upload->soup_session, upload->msg,
+ prv_post_finished, upload_job);
+ g_object_ref(upload->msg);
+
+ upload_id = g_new(gint, 1);
+ *upload_id = upload_job->upload_id;
+ g_hash_table_insert(cb_data->task.target.device->uploads, upload_id,
+ upload);
+
+ object_path = dls_path_from_id(cb_data->task.target.root_path,
+ object_id);
+
+ DLEYNA_LOG_DEBUG("Upload ID %u", *upload_id);
+ DLEYNA_LOG_DEBUG("Object ID %s", object_id);
+ DLEYNA_LOG_DEBUG("Object Path %s", object_path);
+
+ out_p[0] = g_variant_new_uint32(*upload_id);
+ out_p[1] = g_variant_new_object_path(object_path);
+ cb_data->task.result = g_variant_ref_sink(g_variant_new_tuple(out_p,
+ 2));
+
+ ++cb_data->task.target.device->upload_id;
+ if (cb_data->task.target.device->upload_id > G_MAXINT)
+ cb_data->task.target.device->upload_id = 0;
+
+ g_free(object_path);
+
+on_error:
+
+ if (cb_data->error && delete_needed) {
+ DLEYNA_LOG_WARNING(
+ "Upload failed deleting created object with id %s",
+ object_id);
+
+ cb_data->action = gupnp_service_proxy_begin_action(
+ cb_data->proxy, "DestroyObject",
+ prv_upload_delete_cb, cb_data,
+ "ObjectID", G_TYPE_STRING, object_id,
+ NULL);
+ } else {
+ (void) g_idle_add(dls_async_task_complete, cb_data);
+ g_cancellable_disconnect(cb_data->cancellable,
+ cb_data->cancel_id);
+ }
+
+ g_free(object_id);
+ g_free(import_uri);
+
+ if (parser)
+ g_object_unref(parser);
+
+ g_free(result);
+
+ if (error)
+ g_error_free(error);
+
+ DLEYNA_LOG_DEBUG("Exit");
+}
+
+static void prv_create_object_upload_cb(GUPnPServiceProxy *proxy,
+ GUPnPServiceProxyAction *action,
+ gpointer user_data)
+{
+ dls_async_task_t *cb_data = user_data;
+
+ prv_generic_upload_cb(cb_data,
+ cb_data->task.ut.upload.file_path,
+ NULL, 0,
+ cb_data->ut.upload.mime_type);
+}
+
+void dls_device_upload(dls_client_t *client,
+ dls_task_t *task, const gchar *parent_id)
+{
+ dls_async_task_t *cb_data = (dls_async_task_t *)task;
+ dls_device_context_t *context;
+ gchar *didl;
+ dls_async_upload_t *cb_task_data;
+
+ DLEYNA_LOG_DEBUG("Enter");
+ DLEYNA_LOG_DEBUG("Uploading file to %s", parent_id);
+
+ context = dls_device_get_context(task->target.device, client);
+ cb_task_data = &cb_data->ut.upload;
+
+ didl = prv_create_upload_didl(parent_id, task,
+ cb_task_data->object_class,
+ cb_task_data->mime_type);
+
+ DLEYNA_LOG_DEBUG_NL();
+ DLEYNA_LOG_DEBUG("DIDL: %s", didl);
+ DLEYNA_LOG_DEBUG_NL();
+
+ cb_data->action = gupnp_service_proxy_begin_action(
+ context->service_proxy, "CreateObject",
+ prv_create_object_upload_cb, cb_data,
+ "ContainerID", G_TYPE_STRING, parent_id,
+ "Elements", G_TYPE_STRING, didl,
+ NULL);
+
+ cb_data->proxy = context->service_proxy;
+
+ g_object_add_weak_pointer((G_OBJECT(context->service_proxy)),
+ (gpointer *)&cb_data->proxy);
+
+ cb_data->cancel_id = g_cancellable_connect(
+ cb_data->cancellable,
+ G_CALLBACK(dls_async_task_cancelled_cb),
+ cb_data, NULL);
+
+ g_free(didl);
+
+ DLEYNA_LOG_DEBUG("Exit");
+}
+
+gboolean dls_device_get_upload_status(dls_task_t *task, GError **error)
+{
+ dls_device_upload_t *upload;
+ gboolean retval = FALSE;
+ GVariant *out_params[3];
+ guint upload_id;
+
+ DLEYNA_LOG_DEBUG("Enter");
+
+ upload_id = task->ut.upload_action.upload_id;
+
+ upload = g_hash_table_lookup(task->target.device->uploads, &upload_id);
+ if (!upload) {
+ *error = g_error_new(DLEYNA_SERVER_ERROR,
+ DLEYNA_ERROR_OBJECT_NOT_FOUND,
+ "Unknown Upload ID %u ", upload_id);
+ goto on_error;
+ }
+
+ out_params[0] = g_variant_new_string(upload->status);
+ out_params[1] = g_variant_new_uint64(upload->bytes_uploaded);
+ out_params[2] = g_variant_new_uint64(upload->bytes_to_upload);
+
+ DLEYNA_LOG_DEBUG(
+ "Upload ( %s %"G_GUINT64_FORMAT" %"G_GUINT64_FORMAT" )",
+ upload->status, upload->bytes_uploaded,
+ upload->bytes_to_upload);
+
+ task->result = g_variant_ref_sink(g_variant_new_tuple(out_params, 3));
+
+ retval = TRUE;
+
+on_error:
+
+ DLEYNA_LOG_DEBUG("Exit");
+
+ return retval;
+}
+
+void dls_device_get_upload_ids(dls_task_t *task)
+{
+ GVariantBuilder vb;
+ GHashTableIter iter;
+ gpointer key;
+
+ DLEYNA_LOG_DEBUG("Enter");
+
+ g_variant_builder_init(&vb, G_VARIANT_TYPE("au"));
+
+ g_hash_table_iter_init(&iter, task->target.device->uploads);
+ while (g_hash_table_iter_next(&iter, &key, NULL))
+ g_variant_builder_add(&vb, "u", (guint32) (*((gint *)key)));
+
+ task->result = g_variant_ref_sink(g_variant_builder_end(&vb));
+
+ DLEYNA_LOG_DEBUG("Exit");
+}
+
+gboolean dls_device_cancel_upload(dls_task_t *task, GError **error)
+{
+ dls_device_upload_t *upload;
+ gboolean retval = FALSE;
+ guint upload_id;
+
+ DLEYNA_LOG_DEBUG("Enter");
+
+ upload_id = task->ut.upload_action.upload_id;
+
+ upload = g_hash_table_lookup(task->target.device->uploads, &upload_id);
+ if (!upload) {
+ *error = g_error_new(DLEYNA_SERVER_ERROR,
+ DLEYNA_ERROR_OBJECT_NOT_FOUND,
+ "Unknown Upload ID %u ", upload_id);
+ goto on_error;
+ }
+
+ if (upload->msg) {
+ soup_session_cancel_message(upload->soup_session, upload->msg,
+ SOUP_STATUS_CANCELLED);
+ DLEYNA_LOG_DEBUG("Cancelling Upload %u ", upload_id);
+ }
+
+ retval = TRUE;
+
+on_error:
+
+ DLEYNA_LOG_DEBUG("Exit");
+
+ return retval;
+}
+
+static void prv_destroy_object_cb(GUPnPServiceProxy *proxy,
+ GUPnPServiceProxyAction *action,
+ gpointer user_data)
+{
+ GError *upnp_error = NULL;
+ dls_async_task_t *cb_data = user_data;
+
+ DLEYNA_LOG_DEBUG("Enter");
+
+ if (!gupnp_service_proxy_end_action(cb_data->proxy, cb_data->action,
+ &upnp_error,
+ NULL)) {
+ DLEYNA_LOG_WARNING("Destroy Object operation failed: %s",
+ upnp_error->message);
+
+ cb_data->error = g_error_new(DLEYNA_SERVER_ERROR,
+ DLEYNA_ERROR_OPERATION_FAILED,
+ "Destroy Object operation failed: %s",
+ upnp_error->message);
+ }
+
+ (void) g_idle_add(dls_async_task_complete, cb_data);
+ g_cancellable_disconnect(cb_data->cancellable, cb_data->cancel_id);
+
+ if (upnp_error)
+ g_error_free(upnp_error);
+
+ DLEYNA_LOG_DEBUG("Exit");
+}
+
+void dls_device_delete_object(dls_client_t *client,
+ dls_task_t *task)
+{
+ dls_async_task_t *cb_data = (dls_async_task_t *)task;
+ dls_device_context_t *context;
+
+ DLEYNA_LOG_DEBUG("Enter");
+
+ context = dls_device_get_context(task->target.device, client);
+
+ cb_data->action = gupnp_service_proxy_begin_action(
+ context->service_proxy, "DestroyObject",
+ prv_destroy_object_cb, cb_data,
+ "ObjectID", G_TYPE_STRING, task->target.id,
+ NULL);
+
+ cb_data->proxy = context->service_proxy;
+
+ g_object_add_weak_pointer((G_OBJECT(context->service_proxy)),
+ (gpointer *)&cb_data->proxy);
+
+ cb_data->cancel_id = g_cancellable_connect(cb_data->cancellable,
+ G_CALLBACK(dls_async_task_cancelled_cb),
+ cb_data, NULL);
+
+ DLEYNA_LOG_DEBUG("Exit");
+}
+
+void dls_device_create_container(dls_client_t *client,
+ dls_task_t *task,
+ const gchar *parent_id)
+{
+ dls_async_task_t *cb_data = (dls_async_task_t *)task;
+ dls_device_context_t *context;
+ gchar *didl;
+
+ DLEYNA_LOG_DEBUG("Enter");
+
+ context = dls_device_get_context(task->target.device, client);
+
+ didl = prv_create_new_container_didl(parent_id, task);
+
+ DLEYNA_LOG_DEBUG("DIDL: %s", didl);
+
+ cb_data->action = gupnp_service_proxy_begin_action(
+ context->service_proxy, "CreateObject",
+ prv_create_container_cb, cb_data,
+ "ContainerID", G_TYPE_STRING, parent_id,
+ "Elements", G_TYPE_STRING, didl,
+ NULL);
+
+ cb_data->proxy = context->service_proxy;
+
+ g_object_add_weak_pointer((G_OBJECT(context->service_proxy)),
+ (gpointer *)&cb_data->proxy);
+
+ cb_data->cancel_id = g_cancellable_connect(
+ cb_data->cancellable,
+ G_CALLBACK(dls_async_task_cancelled_cb),
+ cb_data, NULL);
+
+ g_free(didl);
+
+ DLEYNA_LOG_DEBUG("Exit");
+}
+
+static void prv_update_object_update_cb(GUPnPServiceProxy *proxy,
+ GUPnPServiceProxyAction *action,
+ gpointer user_data)
+{
+ GError *upnp_error = NULL;
+ dls_async_task_t *cb_data = user_data;
+
+ DLEYNA_LOG_DEBUG("Enter");
+
+ if (!gupnp_service_proxy_end_action(cb_data->proxy, cb_data->action,
+ &upnp_error,
+ NULL)) {
+ DLEYNA_LOG_WARNING("Update Object operation failed: %s",
+ upnp_error->message);
+
+ cb_data->error = g_error_new(DLEYNA_SERVER_ERROR,
+ DLEYNA_ERROR_OPERATION_FAILED,
+ "Update Object operation "
+ " failed: %s",
+ upnp_error->message);
+ }
+
+ (void) g_idle_add(dls_async_task_complete, cb_data);
+ g_cancellable_disconnect(cb_data->cancellable, cb_data->cancel_id);
+
+ if (upnp_error)
+ g_error_free(upnp_error);
+
+ DLEYNA_LOG_DEBUG("Exit");
+}
+
+static gchar *prv_get_current_xml_fragment(GUPnPDIDLLiteObject *object,
+ dls_upnp_prop_mask mask)
+{
+ gchar *retval = NULL;
+
+ if (mask & DLS_UPNP_MASK_PROP_DISPLAY_NAME)
+ retval = gupnp_didl_lite_object_get_title_xml_string(object);
+ else if (mask & DLS_UPNP_MASK_PROP_ALBUM)
+ retval = gupnp_didl_lite_object_get_album_xml_string(object);
+ else if (mask & DLS_UPNP_MASK_PROP_DATE)
+ retval = gupnp_didl_lite_object_get_date_xml_string(object);
+ else if (mask & DLS_UPNP_MASK_PROP_TYPE)
+ retval = gupnp_didl_lite_object_get_upnp_class_xml_string(
+ object);
+ else if (mask & DLS_UPNP_MASK_PROP_TRACK_NUMBER)
+ retval = gupnp_didl_lite_object_get_track_number_xml_string(
+ object);
+ else if (mask & DLS_UPNP_MASK_PROP_ARTISTS)
+ retval = gupnp_didl_lite_object_get_artists_xml_string(object);
+
+ return retval;
+}
+
+static gchar *prv_get_new_xml_fragment(GUPnPDIDLLiteObject *object,
+ dls_upnp_prop_mask mask,
+ GVariant *value)
+{
+ GUPnPDIDLLiteContributor *artist;
+ const gchar *artist_name;
+ const gchar *upnp_class;
+ GVariantIter viter;
+ gchar *retval = NULL;
+
+ if (mask & DLS_UPNP_MASK_PROP_DISPLAY_NAME) {
+ gupnp_didl_lite_object_set_title(
+ object,
+ g_variant_get_string(value, NULL));
+
+ retval = gupnp_didl_lite_object_get_title_xml_string(object);
+ } else if (mask & DLS_UPNP_MASK_PROP_ALBUM) {
+ gupnp_didl_lite_object_set_album(
+ object,
+ g_variant_get_string(value, NULL));
+
+ retval = gupnp_didl_lite_object_get_album_xml_string(object);
+ } else if (mask & DLS_UPNP_MASK_PROP_DATE) {
+ gupnp_didl_lite_object_set_date(
+ object,
+ g_variant_get_string(value, NULL));
+
+ retval = gupnp_didl_lite_object_get_date_xml_string(object);
+ } else if (mask & DLS_UPNP_MASK_PROP_TYPE) {
+ upnp_class = dls_props_media_spec_to_upnp_class(
+ g_variant_get_string(value, NULL));
+
+ gupnp_didl_lite_object_set_upnp_class(object, upnp_class);
+
+ retval = gupnp_didl_lite_object_get_upnp_class_xml_string(
+ object);
+ } else if (mask & DLS_UPNP_MASK_PROP_TRACK_NUMBER) {
+ gupnp_didl_lite_object_set_track_number(
+ object,
+ g_variant_get_int32(value));
+
+ retval = gupnp_didl_lite_object_get_track_number_xml_string(
+ object);
+ } else if (mask & DLS_UPNP_MASK_PROP_ARTISTS) {
+ gupnp_didl_lite_object_unset_artists(object);
+
+ (void) g_variant_iter_init(&viter, value);
+
+ while (g_variant_iter_next(&viter, "&s", &artist_name)) {
+ artist = gupnp_didl_lite_object_add_artist(object);
+
+ gupnp_didl_lite_contributor_set_name(artist,
+ artist_name);
+ }
+
+ retval = gupnp_didl_lite_object_get_artists_xml_string(object);
+ }
+
+ return retval;
+}
+
+static void prv_get_xml_fragments(GUPnPDIDLLiteParser *parser,
+ GUPnPDIDLLiteObject *object,
+ gpointer user_data)
+{
+ GString *current_str;
+ GString *new_str;
+ gchar *frag1;
+ gchar *frag2;
+ GVariantIter viter;
+ const gchar *prop;
+ GVariant *value;
+ dls_prop_map_t *prop_map;
+ GUPnPDIDLLiteWriter *writer;
+ GUPnPDIDLLiteObject *scratch_object;
+ gboolean first = TRUE;
+ dls_async_task_t *cb_data = user_data;
+ dls_async_update_t *cb_task_data = &cb_data->ut.update;
+ dls_task_t *task = &cb_data->task;
+ dls_task_update_t *task_data = &task->ut.update;
+
+ DLEYNA_LOG_DEBUG("Enter");
+
+ current_str = g_string_new("");
+ new_str = g_string_new("");
+
+ writer = gupnp_didl_lite_writer_new(NULL);
+ if (GUPNP_IS_DIDL_LITE_CONTAINER(object))
+ scratch_object = GUPNP_DIDL_LITE_OBJECT(
+ gupnp_didl_lite_writer_add_container(writer));
+ else
+ scratch_object = GUPNP_DIDL_LITE_OBJECT(
+ gupnp_didl_lite_writer_add_item(writer));
+
+ (void) g_variant_iter_init(&viter, task_data->to_add_update);
+
+ while (g_variant_iter_next(&viter, "{&sv}", &prop, &value)) {
+
+ DLEYNA_LOG_DEBUG("to_add_update = %s", prop);
+
+ prop_map = g_hash_table_lookup(cb_task_data->map, prop);
+
+ frag1 = prv_get_current_xml_fragment(object, prop_map->type);
+ frag2 = prv_get_new_xml_fragment(scratch_object, prop_map->type,
+ value);
+
+ if (!frag2) {
+ DLEYNA_LOG_DEBUG("Unable to set %s. Skipping", prop);
+
+ g_free(frag1);
+ continue;
+ }
+
+ if (!first) {
+ g_string_append(current_str, ",");
+ g_string_append(new_str, ",");
+ } else {
+ first = FALSE;
+ }
+
+ if (frag1) {
+ g_string_append(current_str, (const gchar *)frag1);
+ g_free(frag1);
+ }
+
+ g_string_append(new_str, (const gchar *)frag2);
+ g_free(frag2);
+ }
+
+ (void) g_variant_iter_init(&viter, task_data->to_delete);
+
+ while (g_variant_iter_next(&viter, "&s", &prop)) {
+ DLEYNA_LOG_DEBUG("to_delete = %s", prop);
+
+ prop_map = g_hash_table_lookup(cb_task_data->map, prop);
+
+ frag1 = prv_get_current_xml_fragment(object, prop_map->type);
+ if (!frag1)
+ continue;
+
+ if (!first)
+ g_string_append(current_str, ",");
+ else
+ first = FALSE;
+
+ g_string_append(current_str, (const gchar *)frag1);
+ g_free(frag1);
+ }
+
+ cb_task_data->current_tag_value = g_string_free(current_str, FALSE);
+ DLEYNA_LOG_DEBUG("current_tag_value = %s",
+ cb_task_data->current_tag_value);
+
+ cb_task_data->new_tag_value = g_string_free(new_str, FALSE);
+ DLEYNA_LOG_DEBUG("new_tag_value = %s", cb_task_data->new_tag_value);
+
+ g_object_unref(scratch_object);
+ g_object_unref(writer);
+
+ DLEYNA_LOG_DEBUG("Exit");
+}
+
+static void prv_update_object_browse_cb(GUPnPServiceProxy *proxy,
+ GUPnPServiceProxyAction *action,
+ gpointer user_data)
+{
+ GError *upnp_error = NULL;
+ dls_async_task_t *cb_data = user_data;
+ dls_async_update_t *cb_task_data = &cb_data->ut.update;
+ GUPnPDIDLLiteParser *parser = NULL;
+ gchar *result = NULL;
+
+ DLEYNA_LOG_DEBUG("Enter");
+
+ if (!gupnp_service_proxy_end_action(cb_data->proxy, cb_data->action,
+ &upnp_error,
+ "Result", G_TYPE_STRING,
+ &result, NULL)) {
+ DLEYNA_LOG_WARNING("Browse Object operation failed: %s",
+ upnp_error->message);
+
+ cb_data->error = g_error_new(DLEYNA_SERVER_ERROR,
+ DLEYNA_ERROR_OPERATION_FAILED,
+ "Browse operation failed: %s",
+ upnp_error->message);
+ goto on_error;
+ }
+
+ DLEYNA_LOG_DEBUG("dls_device_update_ex_object result: %s", result);
+
+ parser = gupnp_didl_lite_parser_new();
+
+ g_signal_connect(parser, "object-available",
+ G_CALLBACK(prv_get_xml_fragments),
+ cb_data);
+
+ if (!gupnp_didl_lite_parser_parse_didl(parser, result, &upnp_error)) {
+ if (upnp_error->code == GUPNP_XML_ERROR_EMPTY_NODE) {
+ DLEYNA_LOG_WARNING("Property not defined for object");
+
+ cb_data->error =
+ g_error_new(DLEYNA_SERVER_ERROR,
+ DLEYNA_ERROR_UNKNOWN_PROPERTY,
+ "Property not defined for object");
+ } else {
+ DLEYNA_LOG_WARNING(
+ "Unable to parse results of browse: %s",
+ upnp_error->message);
+
+ cb_data->error =
+ g_error_new(DLEYNA_SERVER_ERROR,
+ DLEYNA_ERROR_OPERATION_FAILED,
+ "Unable to parse results of browse: %s",
+ upnp_error->message);
+ }
+
+ goto on_error;
+ }
+
+ cb_data->action = gupnp_service_proxy_begin_action(
+ cb_data->proxy, "UpdateObject",
+ prv_update_object_update_cb, cb_data,
+ "ObjectID", G_TYPE_STRING, cb_data->task.target.id,
+ "CurrentTagValue", G_TYPE_STRING,
+ cb_task_data->current_tag_value,
+ "NewTagValue", G_TYPE_STRING, cb_task_data->new_tag_value,
+ NULL);
+
+ goto no_complete;
+
+on_error:
+
+ (void) g_idle_add(dls_async_task_complete, 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);
+
+ DLEYNA_LOG_DEBUG("Exit");
+}
+
+void dls_device_update_object(dls_client_t *client,
+ dls_task_t *task,
+ const gchar *upnp_filter)
+{
+ dls_async_task_t *cb_data = (dls_async_task_t *)task;
+ dls_device_context_t *context;
+
+ DLEYNA_LOG_DEBUG("Enter");
+
+ context = dls_device_get_context(task->target.device, client);
+
+ cb_data->action = gupnp_service_proxy_begin_action(
+ context->service_proxy, "Browse",
+ prv_update_object_browse_cb, cb_data,
+ "ObjectID", G_TYPE_STRING, task->target.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;
+
+ g_object_add_weak_pointer((G_OBJECT(context->service_proxy)),
+ (gpointer *)&cb_data->proxy);
+
+ cb_data->cancel_id = g_cancellable_connect(cb_data->cancellable,
+ G_CALLBACK(dls_async_task_cancelled_cb),
+ cb_data, NULL);
+
+ DLEYNA_LOG_DEBUG("Exit");
+}
+
+static void prv_didls_free(gpointer data)
+{
+ prv_new_playlist_ct_t *priv_t = (prv_new_playlist_ct_t *)data;
+
+ if (priv_t) {
+ g_free(priv_t->id);
+ g_free(priv_t->parent_id);
+ g_free(priv_t);
+ }
+}
+
+static void prv_playlist_upload_cb(GUPnPServiceProxy *proxy,
+ GUPnPServiceProxyAction *action,
+ gpointer user_data)
+{
+ dls_async_task_t *cb_data = user_data;
+ gchar *didls;
+
+ didls = gupnp_media_collection_get_string(
+ cb_data->ut.playlist.collection);
+
+ DLEYNA_LOG_DEBUG_NL();
+ DLEYNA_LOG_DEBUG("Collection: %s", didls);
+ DLEYNA_LOG_DEBUG_NL();
+
+ prv_generic_upload_cb(cb_data, NULL, didls, strlen(didls), "text/xml");
+}
+
+static void prv_create_didls_item_parse_object(GUPnPDIDLLiteParser *parser,
+ GUPnPDIDLLiteObject *object,
+ gpointer user_data)
+{
+ const char *class;
+ const char *title;
+ const char *artist;
+ const char *album;
+ const char *uri = NULL;
+ GList *resources;
+ GUPnPDIDLLiteObject *item_obj;
+ GUPnPDIDLLiteItem *item;
+ GUPnPDIDLLiteResource *res;
+ GUPnPMediaCollection *collection = user_data;
+
+ if (GUPNP_IS_DIDL_LITE_CONTAINER(object))
+ goto exit;
+
+ class = gupnp_didl_lite_object_get_upnp_class(object);
+ title = gupnp_didl_lite_object_get_title(object);
+ artist = gupnp_didl_lite_object_get_artist(object);
+ album = gupnp_didl_lite_object_get_album(object);
+ resources = gupnp_didl_lite_object_get_resources(object);
+
+ if (resources != NULL) {
+ if (resources->data != NULL)
+ uri = gupnp_didl_lite_resource_get_uri(resources->data);
+
+ g_list_free_full(resources, g_object_unref);
+ }
+
+ DLEYNA_LOG_DEBUG("Create DIDL_S Item");
+ DLEYNA_LOG_DEBUG("title: %s", title);
+ DLEYNA_LOG_DEBUG("class: %s", class);
+ DLEYNA_LOG_DEBUG("Artist: %s", artist);
+ DLEYNA_LOG_DEBUG("album: %s", album);
+ DLEYNA_LOG_DEBUG("URI: %s", uri);
+ DLEYNA_LOG_DEBUG_NL();
+
+ item = gupnp_media_collection_add_item(collection);
+ item_obj = GUPNP_DIDL_LITE_OBJECT(item);
+
+ if (title && *title)
+ gupnp_didl_lite_object_set_title(item_obj, title);
+
+ if (class && *class)
+ gupnp_didl_lite_object_set_upnp_class(item_obj, class);
+
+ if (artist && *artist)
+ gupnp_didl_lite_object_set_artist(item_obj, artist);
+
+ if (album && *album)
+ gupnp_didl_lite_object_set_album(item_obj, album);
+
+ if (uri && *uri) {
+ res = gupnp_didl_lite_object_add_resource(item_obj);
+ gupnp_didl_lite_resource_set_uri(res, uri);
+ g_object_unref(res);
+ }
+
+ g_object_unref(item);
+
+exit:
+ return;
+}
+
+static void prv_create_didls_item_browse_cb(GUPnPServiceProxy *proxy,
+ GUPnPServiceProxyAction *action,
+ gpointer user_data)
+{
+ GError *error = NULL;
+ prv_new_playlist_ct_t *priv_t = user_data;
+ dls_async_task_t *cb_data = priv_t->cb_data;
+ GUPnPDIDLLiteParser *parser = NULL;
+ gchar *result = NULL;
+
+ if (!gupnp_service_proxy_end_action(proxy, action, &error,
+ "Result", G_TYPE_STRING,
+ &result, NULL)) {
+ DLEYNA_LOG_WARNING("Browse Object operation failed: %s",
+ error->message);
+ DLEYNA_LOG_DEBUG_NL();
+
+ cb_data->error = g_error_new(DLEYNA_SERVER_ERROR,
+ DLEYNA_ERROR_OPERATION_FAILED,
+ "Browse operation failed: %s",
+ error->message);
+ goto on_exit;
+ }
+
+ DLEYNA_LOG_DEBUG_NL();
+ DLEYNA_LOG_DEBUG("Result: %s", result);
+ DLEYNA_LOG_DEBUG_NL();
+
+ parser = gupnp_didl_lite_parser_new();
+
+ g_signal_connect(parser, "object-available",
+ G_CALLBACK(prv_create_didls_item_parse_object),
+ cb_data->ut.playlist.collection);
+
+ if (gupnp_didl_lite_parser_parse_didl(parser, result, &error))
+ goto on_exit;
+
+ if (error->code == GUPNP_XML_ERROR_EMPTY_NODE) {
+ DLEYNA_LOG_WARNING("Property not defined for object");
+
+ cb_data->error = g_error_new(DLEYNA_SERVER_ERROR,
+ DLEYNA_ERROR_UNKNOWN_PROPERTY,
+ "Property not defined for object");
+ } else {
+ DLEYNA_LOG_WARNING("Unable to parse results of browse: %s",
+ error->message);
+
+ cb_data->error = g_error_new(DLEYNA_SERVER_ERROR,
+ DLEYNA_ERROR_OPERATION_FAILED,
+ "Unable to parse results of browse: %s",
+ error->message);
+ }
+
+on_exit:
+
+ if (cb_data->error != NULL)
+ dleyna_task_processor_cancel_queue(
+ cb_data->ut.playlist.queue_id);
+
+ if (parser)
+ g_object_unref(parser);
+
+ if (error)
+ g_error_free(error);
+
+ g_free(result);
+}
+
+static GUPnPServiceProxyAction *prv_create_didls_item_browse(
+ dleyna_service_task_t *task,
+ GUPnPServiceProxy *proxy,
+ gboolean *failed)
+{
+ prv_new_playlist_ct_t *priv_t;
+
+ priv_t = (prv_new_playlist_ct_t *)dleyna_service_task_get_user_data(
+ task);
+ *failed = FALSE;
+
+ DLEYNA_LOG_DEBUG("Browse for ID: %s", priv_t->id);
+
+ return gupnp_service_proxy_begin_action(
+ proxy, "Browse",
+ dleyna_service_task_begin_action_cb, task,
+ "ObjectID", G_TYPE_STRING, priv_t->id,
+ "BrowseFlag", G_TYPE_STRING, "BrowseMetadata",
+ "Filter", G_TYPE_STRING, "upnp:artist,upnp:album,res",
+ "StartingIndex", G_TYPE_INT, 0,
+ "RequestedCount", G_TYPE_INT, 1,
+ "SortCriteria", G_TYPE_STRING, "", NULL);
+}
+
+static gboolean prv_create_chain_didls_items(dls_task_t *task,
+ GUPnPServiceProxy *proxy,
+ dls_async_task_t *cb_data)
+{
+ gchar *root_path = NULL;
+ gchar *path;
+ gchar *id = NULL;
+ prv_new_playlist_ct_t *priv_t;
+ dls_async_playlist_t *a_playlist = &cb_data->ut.playlist;
+ GVariantIter iter;
+
+ DLEYNA_LOG_DEBUG_NL();
+
+ a_playlist->collection = gupnp_media_collection_new();
+ gupnp_media_collection_set_title(a_playlist->collection,
+ task->ut.playlist.title);
+ gupnp_media_collection_set_author(a_playlist->collection,
+ task->ut.playlist.creator);
+
+ g_variant_iter_init(&iter, task->ut.playlist.item_path);
+
+ while (g_variant_iter_next(&iter, "&o", &path)) {
+ if (!dls_path_get_path_and_id(path, &root_path, &id, NULL)) {
+ DLEYNA_LOG_DEBUG("Can't get id for path %s", path);
+ cb_data->error = g_error_new(
+ DLEYNA_SERVER_ERROR,
+ DLEYNA_ERROR_OBJECT_NOT_FOUND,
+ "Unable to find object for path: %s",
+ path);
+ goto on_error;
+ }
+
+ DLEYNA_LOG_DEBUG("Create Task: @id: %s - Root: %s",
+ id, root_path);
+
+ g_free(root_path);
+
+ priv_t = g_new0(prv_new_playlist_ct_t, 1);
+ priv_t->cb_data = cb_data;
+ priv_t->id = id;
+
+ dleyna_service_task_add(a_playlist->queue_id,
+ prv_create_didls_item_browse,
+ proxy,
+ prv_create_didls_item_browse_cb,
+ prv_didls_free, priv_t);
+ }
+
+ DLEYNA_LOG_DEBUG_NL();
+ return TRUE;
+
+on_error:
+
+ return FALSE;
+}
+
+static void prv_create_playlist_object(dls_task_create_playlist_t *t_playlist,
+ dls_async_playlist_t *a_playlist,
+ char *parent_id)
+{
+ GUPnPDIDLLiteWriter *writer;
+ GUPnPDIDLLiteObject *item;
+ GUPnPProtocolInfo *protocol_info;
+ GUPnPDIDLLiteResource *res;
+ GUPnPDIDLLiteContributor *creator;
+ GUPnPDIDLLiteContributor *author;
+ GTimeVal time_v;
+ gchar *time_c;
+
+ 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, t_playlist->title);
+ gupnp_didl_lite_object_set_genre(item, t_playlist->genre);
+ gupnp_didl_lite_object_set_description(item, t_playlist->desc);
+
+ creator = gupnp_didl_lite_object_add_creator(item);
+ author = gupnp_didl_lite_object_add_author(item);
+ gupnp_didl_lite_contributor_set_name(creator, t_playlist->creator);
+ gupnp_didl_lite_contributor_set_name(author, t_playlist->creator);
+
+ gupnp_didl_lite_object_set_parent_id(item, parent_id);
+ gupnp_didl_lite_object_set_upnp_class(item, "object.item.playlistItem");
+ gupnp_didl_lite_object_set_restricted(item, FALSE);
+
+ protocol_info = gupnp_protocol_info_new();
+ gupnp_protocol_info_set_mime_type(protocol_info, "text/xml");
+ gupnp_protocol_info_set_protocol(protocol_info, "*");
+ gupnp_protocol_info_set_network(protocol_info, "*");
+ gupnp_protocol_info_set_dlna_profile(protocol_info, "DIDL_S");
+
+ res = gupnp_didl_lite_object_add_resource(item);
+ gupnp_didl_lite_resource_set_protocol_info(res, protocol_info);
+
+ g_get_current_time(&time_v);
+ time_c = g_time_val_to_iso8601(&time_v);
+ gupnp_didl_lite_object_set_date(item, time_c);
+
+ /* TODO: Need to compute DLNA Profile */
+
+ a_playlist->didl = gupnp_didl_lite_writer_get_string(writer);
+
+ DLEYNA_LOG_DEBUG("Playlist object %s created", t_playlist->title);
+
+ g_object_unref(res);
+ g_object_unref(protocol_info);
+ g_object_unref(creator);
+ g_object_unref(author);
+ g_object_unref(item);
+ g_object_unref(writer);
+ g_free(time_c);
+}
+
+static void prv_create_didls_chain_end(gboolean cancelled, gpointer data)
+{
+ prv_new_playlist_ct_t *priv_t = data;
+ dls_async_task_t *cb_data = priv_t->cb_data;
+ dls_async_playlist_t *a_playlist;
+ dls_task_create_playlist_t *t_playlist;
+
+ DLEYNA_LOG_DEBUG("Enter");
+
+ if (cb_data->cancel_id) {
+ if (!g_cancellable_is_cancelled(cb_data->cancellable))
+ g_cancellable_disconnect(cb_data->cancellable,
+ cb_data->cancel_id);
+ cb_data->cancel_id = 0;
+ }
+
+ if (cancelled)
+ goto on_clear;
+
+ t_playlist = &cb_data->task.ut.playlist;
+ a_playlist = &cb_data->ut.playlist;
+ prv_create_playlist_object(t_playlist, a_playlist, priv_t->parent_id);
+
+ DLEYNA_LOG_DEBUG("Creating object");
+ cb_data->action = gupnp_service_proxy_begin_action(
+ cb_data->proxy,
+ "CreateObject",
+ prv_playlist_upload_cb, cb_data,
+ "ContainerID", G_TYPE_STRING, priv_t->parent_id,
+ "Elements", G_TYPE_STRING, a_playlist->didl,
+ NULL);
+
+ cb_data->cancel_id = g_cancellable_connect(
+ cb_data->cancellable,
+ G_CALLBACK(dls_async_task_cancelled_cb),
+ cb_data, NULL);
+on_clear:
+
+ if (cancelled) {
+ if (!cb_data->error)
+ cb_data->error = g_error_new(DLEYNA_SERVER_ERROR,
+ DLEYNA_ERROR_CANCELLED,
+ "Operation cancelled.");
+ (void) g_idle_add(dls_async_task_complete, cb_data);
+ }
+
+ prv_didls_free(priv_t);
+
+ cb_data->ut.playlist.queue_id = NULL;
+
+ DLEYNA_LOG_DEBUG("Exit");
+}
+
+static void prv_create_chain_cancelled(GCancellable *cancellable,
+ gpointer user_data)
+{
+ dls_async_task_t *cb_data = user_data;
+ const dleyna_task_queue_key_t *queue_id = cb_data->ut.playlist.queue_id;
+
+ DLEYNA_LOG_DEBUG("Enter");
+
+ dleyna_task_processor_cancel_queue(queue_id);
+}
+
+void dls_device_playlist_upload(dls_client_t *client,
+ dls_task_t *task,
+ const gchar *parent_id)
+{
+ dls_async_task_t *cb_data = (dls_async_task_t *)task;
+ dls_device_context_t *context;
+ prv_new_playlist_ct_t *priv_t;
+ const dleyna_task_queue_key_t *queue_id;
+
+ DLEYNA_LOG_DEBUG("Enter");
+ DLEYNA_LOG_DEBUG("Uploading playlist to %s", parent_id);
+
+ priv_t = g_new0(prv_new_playlist_ct_t, 1);
+ priv_t->cb_data = cb_data;
+ priv_t->parent_id = g_strdup(parent_id);
+
+ queue_id = dleyna_task_processor_add_queue(
+ dls_server_get_task_processor(),
+ dleyna_service_task_create_source(),
+ DLS_SERVER_SINK,
+ DLEYNA_TASK_QUEUE_FLAG_AUTO_REMOVE,
+ dleyna_service_task_process_cb,
+ dleyna_service_task_cancel_cb,
+ dleyna_service_task_delete_cb);
+ dleyna_task_queue_set_finally(queue_id, prv_create_didls_chain_end);
+ dleyna_task_queue_set_user_data(queue_id, priv_t);
+
+ context = dls_device_get_context(task->target.device, client);
+
+ cb_data->proxy = context->service_proxy;
+ cb_data->ut.playlist.queue_id = queue_id;
+
+ g_object_add_weak_pointer((G_OBJECT(context->service_proxy)),
+ (gpointer *)&cb_data->proxy);
+
+ if (prv_create_chain_didls_items(task, cb_data->proxy, cb_data)) {
+ cb_data->cancel_id = g_cancellable_connect(
+ cb_data->cancellable,
+ G_CALLBACK(prv_create_chain_cancelled),
+ cb_data, NULL);
+ dleyna_task_queue_start(queue_id);
+ } else {
+ (void) g_idle_add(dls_async_task_complete, cb_data);
+ }
+
+ DLEYNA_LOG_DEBUG("Exit");
+}
diff --git a/libdleyna/server/device.h b/libdleyna/server/device.h
new file mode 100644
index 0000000..ef3e3df
--- /dev/null
+++ b/libdleyna/server/device.h
@@ -0,0 +1,129 @@
+/*
+ * dLeyna
+ *
+ * Copyright (C) 2012-2013 Intel Corporation. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms and conditions of the GNU Lesser General Public License,
+ * version 2.1, as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License
+ * for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with this program; if not, write to the Free Software Foundation, Inc.,
+ * 51 Franklin St - Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Mark Ryan <mark.d.ryan@intel.com>
+ *
+ */
+
+#ifndef DLS_DEVICE_H__
+#define DLS_DEVICE_H__
+
+#include <libgupnp/gupnp-control-point.h>
+
+#include <libdleyna/core/connector.h>
+#include <libdleyna/core/task-processor.h>
+
+#include "async.h"
+#include "client.h"
+#include "props.h"
+
+struct dls_device_context_t_ {
+ gchar *ip_address;
+ GUPnPDeviceProxy *device_proxy;
+ GUPnPServiceProxy *service_proxy;
+ dls_device_t *device;
+ gboolean subscribed;
+ guint timeout_id;
+};
+
+struct dls_device_t_ {
+ dleyna_connector_id_t connection;
+ guint id;
+ gchar *path;
+ GPtrArray *contexts;
+ guint timeout_id;
+ GHashTable *uploads;
+ GHashTable *upload_jobs;
+ guint upload_id;
+ guint system_update_id;
+ GVariant *search_caps;
+ GVariant *sort_caps;
+ GVariant *sort_ext_caps;
+ GVariant *feature_list;
+ gboolean shutting_down;
+};
+
+dls_device_context_t *dls_device_append_new_context(dls_device_t *device,
+ const gchar *ip_address,
+ GUPnPDeviceProxy *proxy);
+void dls_device_delete(void *device);
+
+void dls_device_unsubscribe(void *device);
+
+dls_device_t *dls_device_new(
+ dleyna_connector_id_t connection,
+ GUPnPDeviceProxy *proxy,
+ const gchar *ip_address,
+ const dleyna_connector_dispatch_cb_t *dispatch_table,
+ GHashTable *filter_map,
+ guint counter,
+ const dleyna_task_queue_key_t *queue_id);
+
+dls_device_t *dls_device_from_path(const gchar *path, GHashTable *device_list);
+
+dls_device_context_t *dls_device_get_context(const dls_device_t *device,
+ dls_client_t *client);
+
+void dls_device_get_children(dls_client_t *client,
+ dls_task_t *task,
+ const gchar *upnp_filter, const gchar *sort_by);
+
+void dls_device_get_all_props(dls_client_t *client,
+ dls_task_t *task,
+ gboolean root_object);
+
+void dls_device_get_prop(dls_client_t *client,
+ dls_task_t *task,
+ dls_prop_map_t *prop_map, gboolean root_object);
+
+void dls_device_search(dls_client_t *client,
+ dls_task_t *task,
+ const gchar *upnp_filter, const gchar *upnp_query,
+ const gchar *sort_by);
+
+void dls_device_get_resource(dls_client_t *client,
+ dls_task_t *task,
+ const gchar *upnp_filter);
+
+void dls_device_subscribe_to_contents_change(dls_device_t *device);
+
+void dls_device_upload(dls_client_t *client,
+ dls_task_t *task, const gchar *parent_id);
+
+gboolean dls_device_get_upload_status(dls_task_t *task, GError **error);
+
+gboolean dls_device_cancel_upload(dls_task_t *task, GError **error);
+
+void dls_device_get_upload_ids(dls_task_t *task);
+
+void dls_device_delete_object(dls_client_t *client,
+ dls_task_t *task);
+
+void dls_device_create_container(dls_client_t *client,
+ dls_task_t *task,
+ const gchar *parent_id);
+
+void dls_device_update_object(dls_client_t *client,
+ dls_task_t *task,
+ const gchar *upnp_filter);
+
+void dls_device_playlist_upload(dls_client_t *client,
+ dls_task_t *task,
+ const gchar *parent_id);
+
+#endif /* DLS_DEVICE_H__ */
diff --git a/libdleyna/server/dleyna-server-1.0.pc.in b/libdleyna/server/dleyna-server-1.0.pc.in
new file mode 100644
index 0000000..4f7f5a2
--- /dev/null
+++ b/libdleyna/server/dleyna-server-1.0.pc.in
@@ -0,0 +1,11 @@
+prefix=@prefix@
+exec_prefix=@exec_prefix@
+libexecdir=@libexecdir@
+includedir=${prefix}/include
+libdir=@libdir@
+
+Name: @PACKAGE@
+Description: UPnP & DLNA server library
+Libs: -L${libdir} -ldleyna-server-1.0
+Requires.private: glib-2.0 gio-2.0 gupnp-1.0 gupnp-av-1.0 dleyna-core-1.0
+Version: @VERSION@ \ No newline at end of file
diff --git a/libdleyna/server/dleyna-server-service.conf.in b/libdleyna/server/dleyna-server-service.conf.in
new file mode 100644
index 0000000..0acca2c
--- /dev/null
+++ b/libdleyna/server/dleyna-server-service.conf.in
@@ -0,0 +1,37 @@
+# Configuration file for dleyna-server
+#
+#
+#
+# General configuration options
+[general]
+
+# true: Service always stay in memory running
+# false: Service quit when the last client disconnects.
+never-quit=@never_quit@
+
+# IPC connector name
+connector-name=@with_connector_name@
+
+# Log configuration options
+[log]
+
+# Define the logging output method. 3 technologies are defined:
+#
+# 0=Syslog
+# 1=GLib
+# 2=File
+log-type=@with_log_type@
+
+# Comma-separated list of logging level.
+# Log levels are: 1=critical, 2=error, 3=warning, 4=message, 5=info, 6=debug
+#
+# Allowed values for log-levels are
+# 0 = disabled
+# 7 = default (=1,2,5)
+# 8 = all (=1,2,3,4,5,6)
+# 1,..,6 = a comma separated list of log level
+#
+# IMPORTANT: This log level is a subset of the log level defined at compile time
+# You can't enable levels disabled at compile time
+# level=8 means all level flags defined at compile time.
+log-level=@with_log_level@
diff --git a/libdleyna/server/interface.h b/libdleyna/server/interface.h
new file mode 100644
index 0000000..fb2695e
--- /dev/null
+++ b/libdleyna/server/interface.h
@@ -0,0 +1,191 @@
+/*
+ * dLeyna
+ *
+ * Copyright (C) 2012-2013 Intel Corporation. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms and conditions of the GNU Lesser General Public License,
+ * version 2.1, as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License
+ * for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with this program; if not, write to the Free Software Foundation, Inc.,
+ * 51 Franklin St - Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Regis Merlino <regis.merlino@intel.com>
+ *
+ */
+
+#ifndef DLEYNA_SERVER_INTERFACE_H__
+#define DLEYNA_SERVER_INTERFACE_H__
+
+enum dls_interface_type_ {
+ DLS_INTERFACE_INFO_PROPERTIES,
+ DLS_INTERFACE_INFO_OBJECT,
+ DLS_INTERFACE_INFO_CONTAINER,
+ DLS_INTERFACE_INFO_ITEM,
+ DLS_INTERFACE_INFO_DEVICE,
+ DLS_INTERFACE_INFO_MAX
+};
+
+#define DLS_INTERFACE_PROPERTIES "org.freedesktop.DBus.Properties"
+#define DLS_INTERFACE_MEDIA_CONTAINER "org.gnome.UPnP.MediaContainer2"
+#define DLS_INTERFACE_MEDIA_OBJECT "org.gnome.UPnP.MediaObject2"
+#define DLS_INTERFACE_MEDIA_ITEM "org.gnome.UPnP.MediaItem2"
+
+/* Object Properties */
+#define DLS_INTERFACE_PROP_PATH "Path"
+#define DLS_INTERFACE_PROP_PARENT "Parent"
+#define DLS_INTERFACE_PROP_RESTRICTED "Restricted"
+#define DLS_INTERFACE_PROP_DISPLAY_NAME "DisplayName"
+#define DLS_INTERFACE_PROP_TYPE "Type"
+#define DLS_INTERFACE_PROP_CREATOR "Creator"
+#define DLS_INTERFACE_PROP_DLNA_MANAGED "DLNAManaged"
+#define DLS_INTERFACE_PROP_OBJECT_UPDATE_ID "ObjectUpdateID"
+
+/* Item Properties */
+#define DLS_INTERFACE_PROP_REFPATH "RefPath"
+#define DLS_INTERFACE_PROP_ARTIST "Artist"
+#define DLS_INTERFACE_PROP_ARTISTS "Artists"
+#define DLS_INTERFACE_PROP_ALBUM "Album"
+#define DLS_INTERFACE_PROP_DATE "Date"
+#define DLS_INTERFACE_PROP_GENRE "Genre"
+#define DLS_INTERFACE_PROP_TRACK_NUMBER "TrackNumber"
+#define DLS_INTERFACE_PROP_ALBUM_ART_URL "AlbumArtURL"
+#define DLS_INTERFACE_PROP_RESOURCES "Resources"
+
+/* Container Properties */
+#define DLS_INTERFACE_PROP_SEARCHABLE "Searchable"
+#define DLS_INTERFACE_PROP_CHILD_COUNT "ChildCount"
+#define DLS_INTERFACE_PROP_CREATE_CLASSES "CreateClasses"
+#define DLS_INTERFACE_PROP_CONTAINER_UPDATE_ID "ContainerUpdateID"
+#define DLS_INTERFACE_PROP_TOTAL_DELETED_CHILD_COUNT "TotalDeletedChildCount"
+
+/* Device Properties */
+#define DLS_INTERFACE_PROP_LOCATION "Location"
+#define DLS_INTERFACE_PROP_UDN "UDN"
+#define DLS_INTERFACE_PROP_DEVICE_TYPE "DeviceType"
+#define DLS_INTERFACE_PROP_FRIENDLY_NAME "FriendlyName"
+#define DLS_INTERFACE_PROP_MANUFACTURER "Manufacturer"
+#define DLS_INTERFACE_PROP_MANUFACTURER_URL "ManufacturerUrl"
+#define DLS_INTERFACE_PROP_MODEL_DESCRIPTION "ModelDescription"
+#define DLS_INTERFACE_PROP_MODEL_NAME "ModelName"
+#define DLS_INTERFACE_PROP_MODEL_NUMBER "ModelNumber"
+#define DLS_INTERFACE_PROP_MODEL_URL "ModelURL"
+#define DLS_INTERFACE_PROP_SERIAL_NUMBER "SerialNumber"
+#define DLS_INTERFACE_PROP_PRESENTATION_URL "PresentationURL"
+#define DLS_INTERFACE_PROP_ICON_URL "IconURL"
+#define DLS_INTERFACE_PROP_SV_DLNA_CAPABILITIES "DLNACaps"
+#define DLS_INTERFACE_PROP_SV_SEARCH_CAPABILITIES "SearchCaps"
+#define DLS_INTERFACE_PROP_SV_SORT_CAPABILITIES "SortCaps"
+#define DLS_INTERFACE_PROP_SV_SORT_EXT_CAPABILITIES "SortExtCaps"
+#define DLS_INTERFACE_PROP_SV_FEATURE_LIST "FeatureList"
+#define DLS_INTERFACE_PROP_SV_SERVICE_RESET_TOKEN "ServiceResetToken"
+
+/* Resources Properties */
+#define DLS_INTERFACE_PROP_MIME_TYPE "MIMEType"
+#define DLS_INTERFACE_PROP_DLNA_PROFILE "DLNAProfile"
+#define DLS_INTERFACE_PROP_SIZE "Size"
+#define DLS_INTERFACE_PROP_DURATION "Duration"
+#define DLS_INTERFACE_PROP_BITRATE "Bitrate"
+#define DLS_INTERFACE_PROP_SAMPLE_RATE "SampleRate"
+#define DLS_INTERFACE_PROP_BITS_PER_SAMPLE "BitsPerSample"
+#define DLS_INTERFACE_PROP_WIDTH "Width"
+#define DLS_INTERFACE_PROP_HEIGHT "Height"
+#define DLS_INTERFACE_PROP_COLOR_DEPTH "ColorDepth"
+#define DLS_INTERFACE_PROP_URLS "URLs"
+#define DLS_INTERFACE_PROP_URL "URL"
+#define DLS_INTERFACE_PROP_UPDATE_COUNT "UpdateCount"
+
+/* Evented State Variable Properties */
+#define DLS_INTERFACE_PROP_ESV_SYSTEM_UPDATE_ID "SystemUpdateID"
+
+#define DLS_INTERFACE_GET_VERSION "GetVersion"
+#define DLS_INTERFACE_GET_SERVERS "GetServers"
+#define DLS_INTERFACE_RELEASE "Release"
+#define DLS_INTERFACE_SET_PROTOCOL_INFO "SetProtocolInfo"
+#define DLS_INTERFACE_PREFER_LOCAL_ADDRESSES "PreferLocalAddresses"
+
+#define DLS_INTERFACE_FOUND_SERVER "FoundServer"
+#define DLS_INTERFACE_LOST_SERVER "LostServer"
+
+#define DLS_INTERFACE_LIST_CHILDREN "ListChildren"
+#define DLS_INTERFACE_LIST_CHILDREN_EX "ListChildrenEx"
+#define DLS_INTERFACE_LIST_ITEMS "ListItems"
+#define DLS_INTERFACE_LIST_ITEMS_EX "ListItemsEx"
+#define DLS_INTERFACE_LIST_CONTAINERS "ListContainers"
+#define DLS_INTERFACE_LIST_CONTAINERS_EX "ListContainersEx"
+#define DLS_INTERFACE_SEARCH_OBJECTS "SearchObjects"
+#define DLS_INTERFACE_SEARCH_OBJECTS_EX "SearchObjectsEx"
+#define DLS_INTERFACE_UPDATE "Update"
+
+#define DLS_INTERFACE_GET_COMPATIBLE_RESOURCE "GetCompatibleResource"
+
+#define DLS_INTERFACE_GET "Get"
+#define DLS_INTERFACE_GET_ALL "GetAll"
+#define DLS_INTERFACE_INTERFACE_NAME "InterfaceName"
+#define DLS_INTERFACE_PROPERTY_NAME "PropertyName"
+#define DLS_INTERFACE_PROPERTIES_VALUE "Properties"
+#define DLS_INTERFACE_VALUE "value"
+#define DLS_INTERFACE_CHILD_TYPES "ChildTypes"
+
+#define DLS_INTERFACE_VERSION "Version"
+#define DLS_INTERFACE_SERVERS "Servers"
+
+#define DLS_INTERFACE_CRITERIA "Criteria"
+#define DLS_INTERFACE_DICT "Dictionary"
+#define DLS_INTERFACE_PATH "Path"
+#define DLS_INTERFACE_QUERY "Query"
+#define DLS_INTERFACE_PROTOCOL_INFO "ProtocolInfo"
+#define DLS_INTERFACE_PREFER "Prefer"
+
+#define DLS_INTERFACE_OFFSET "Offset"
+#define DLS_INTERFACE_MAX "Max"
+#define DLS_INTERFACE_FILTER "Filter"
+#define DLS_INTERFACE_CHILDREN "Children"
+#define DLS_INTERFACE_SORT_BY "SortBy"
+#define DLS_INTERFACE_TOTAL_ITEMS "TotalItems"
+
+#define DLS_INTERFACE_PROPERTIES_CHANGED "PropertiesChanged"
+#define DLS_INTERFACE_CHANGED_PROPERTIES "ChangedProperties"
+#define DLS_INTERFACE_INVALIDATED_PROPERTIES "InvalidatedProperties"
+#define DLS_INTERFACE_ESV_CONTAINER_UPDATE_IDS "ContainerUpdateIDs"
+#define DLS_INTERFACE_CONTAINER_PATHS_ID "ContainerPathsIDs"
+#define DLS_INTERFACE_ESV_LAST_CHANGE "LastChange"
+#define DLS_INTERFACE_LAST_CHANGE_STATE_EVENT "StateEvent"
+
+#define DLS_INTERFACE_DELETE "Delete"
+
+#define DLS_INTERFACE_CREATE_CONTAINER "CreateContainer"
+#define DLS_INTERFACE_CREATE_CONTAINER_IN_ANY "CreateContainerInAnyContainer"
+
+#define DLS_INTERFACE_UPLOAD "Upload"
+#define DLS_INTERFACE_UPLOAD_TO_ANY "UploadToAnyContainer"
+#define DLS_INTERFACE_GET_UPLOAD_STATUS "GetUploadStatus"
+#define DLS_INTERFACE_GET_UPLOAD_IDS "GetUploadIDs"
+#define DLS_INTERFACE_CANCEL_UPLOAD "CancelUpload"
+#define DLS_INTERFACE_TOTAL "Total"
+#define DLS_INTERFACE_LENGTH "Length"
+#define DLS_INTERFACE_FILE_PATH "FilePath"
+#define DLS_INTERFACE_UPLOAD_ID "UploadId"
+#define DLS_INTERFACE_UPLOAD_IDS "UploadIDs"
+#define DLS_INTERFACE_UPLOAD_STATUS "UploadStatus"
+#define DLS_INTERFACE_UPLOAD_UPDATE "UploadUpdate"
+#define DLS_INTERFACE_TO_ADD_UPDATE "ToAddUpdate"
+#define DLS_INTERFACE_TO_DELETE "ToDelete"
+#define DLS_INTERFACE_CANCEL "Cancel"
+
+#define DLS_INTERFACE_CREATE_PLAYLIST "CreatePlaylist"
+#define DLS_INTERFACE_CREATE_PLAYLIST_TO_ANY "CreatePlaylistInAnyContainer"
+#define DLS_INTERFACE_TITLE "Title"
+#define DLS_INTERFACE_CREATOR "Creator"
+#define DLS_INTERFACE_GENRE "Genre"
+#define DLS_INTERFACE_DESCRIPTION "Description"
+#define DLS_INTERFACE_PLAYLIST_ITEMS "PlaylistItems"
+
+
+#endif /* DLEYNA_SERVER_INTERFACE_H__ */
diff --git a/libdleyna/server/path.c b/libdleyna/server/path.c
new file mode 100644
index 0000000..345cba4
--- /dev/null
+++ b/libdleyna/server/path.c
@@ -0,0 +1,154 @@
+/*
+ * dLeyna
+ *
+ * Copyright (C) 2012-2013 Intel Corporation. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms and conditions of the GNU Lesser General Public License,
+ * version 2.1, as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License
+ * for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with this program; if not, write to the Free Software Foundation, Inc.,
+ * 51 Franklin St - Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Mark Ryan <mark.d.ryan@intel.com>
+ *
+ */
+
+#include <stdio.h>
+#include <string.h>
+
+#include <libdleyna/core/error.h>
+
+#include "path.h"
+#include "server.h"
+
+gboolean dls_path_get_non_root_id(const gchar *object_path,
+ const gchar **slash_before_id)
+{
+ gboolean retval = FALSE;
+ unsigned int offset = strlen(DLEYNA_SERVER_PATH) + 1;
+
+ if (!g_str_has_prefix(object_path, DLEYNA_SERVER_PATH "/"))
+ goto on_error;
+
+ if (!object_path[offset])
+ goto on_error;
+
+ *slash_before_id = strchr(&object_path[offset], '/');
+ retval = TRUE;
+
+on_error:
+
+ return retval;
+}
+
+static gchar *prv_object_name_to_id(const gchar *object_name)
+{
+ gchar *retval = NULL;
+ unsigned int object_len = strlen(object_name);
+ unsigned int i;
+ gint hex;
+ gchar byte;
+
+ if (object_len & 1)
+ goto on_error;
+
+ retval = g_malloc((object_len >> 1) + 1);
+
+ for (i = 0; i < object_len; i += 2) {
+ hex = g_ascii_xdigit_value(object_name[i]);
+
+ if (hex == -1)
+ goto on_error;
+
+ byte = hex << 4;
+ hex = g_ascii_xdigit_value(object_name[i + 1]);
+
+ if (hex == -1)
+ goto on_error;
+
+ byte |= hex;
+ retval[i >> 1] = byte;
+ }
+ retval[i >> 1] = 0;
+
+ return retval;
+
+on_error:
+
+ g_free(retval);
+
+ return NULL;
+}
+
+gboolean dls_path_get_path_and_id(const gchar *object_path, gchar **root_path,
+ gchar **id, GError **error)
+{
+ const gchar *slash;
+ gchar *coded_id;
+
+ if (!dls_path_get_non_root_id(object_path, &slash))
+ goto on_error;
+
+ if (!slash) {
+ *root_path = g_strdup(object_path);
+ *id = g_strdup("0");
+ } else {
+ if (!slash[1])
+ goto on_error;
+
+ coded_id = prv_object_name_to_id(slash + 1);
+
+ if (!coded_id)
+ goto on_error;
+
+ *root_path = g_strndup(object_path, slash - object_path);
+ *id = coded_id;
+ }
+
+ return TRUE;
+
+on_error:
+ if (error)
+ *error = g_error_new(DLEYNA_SERVER_ERROR, DLEYNA_ERROR_BAD_PATH,
+ "object path is badly formed.");
+
+ return FALSE;
+}
+
+static gchar *prv_id_to_object_name(const gchar *id)
+{
+ gchar *retval;
+ unsigned int i;
+ unsigned int data_len = strlen(id);
+
+ retval = g_malloc((data_len << 1) + 1);
+ retval[0] = 0;
+
+ for (i = 0; i < data_len; i++)
+ sprintf(&retval[i << 1], "%0x", (guint8) id[i]);
+
+ return retval;
+}
+
+gchar *dls_path_from_id(const gchar *root_path, const gchar *id)
+{
+ gchar *coded_id;
+ gchar *path;
+
+ if (!strcmp(id, "0")) {
+ path = g_strdup(root_path);
+ } else {
+ coded_id = prv_id_to_object_name(id);
+ path = g_strdup_printf("%s/%s", root_path, coded_id);
+ g_free(coded_id);
+ }
+
+ return path;
+}
diff --git a/libdleyna/server/path.h b/libdleyna/server/path.h
new file mode 100644
index 0000000..1ccd12d
--- /dev/null
+++ b/libdleyna/server/path.h
@@ -0,0 +1,36 @@
+/*
+ * dLeyna
+ *
+ * Copyright (C) 2012-2013 Intel Corporation. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms and conditions of the GNU Lesser General Public License,
+ * version 2.1, as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License
+ * for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with this program; if not, write to the Free Software Foundation, Inc.,
+ * 51 Franklin St - Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Mark Ryan <mark.d.ryan@intel.com>
+ *
+ */
+
+#ifndef DLS_PATH_H__
+#define DLS_PATH_H__
+
+#include <glib.h>
+
+gboolean dls_path_get_non_root_id(const gchar *object_path,
+ const gchar **slash_before_id);
+
+gboolean dls_path_get_path_and_id(const gchar *object_path, gchar **root_path,
+ gchar **id, GError **error);
+
+gchar *dls_path_from_id(const gchar *root_path, const gchar *id);
+
+#endif /* DLS_PATH_H__ */
diff --git a/libdleyna/server/props.c b/libdleyna/server/props.c
new file mode 100644
index 0000000..2dbd63b
--- /dev/null
+++ b/libdleyna/server/props.c
@@ -0,0 +1,1792 @@
+/*
+ * dLeyna
+ *
+ * Copyright (C) 2012-2013 Intel Corporation. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms and conditions of the GNU Lesser General Public License,
+ * version 2.1, as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License
+ * for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with this program; if not, write to the Free Software Foundation, Inc.,
+ * 51 Franklin St - Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Mark Ryan <mark.d.ryan@intel.com>
+ *
+ */
+
+#include <string.h>
+#include <libgupnp-av/gupnp-didl-lite-contributor.h>
+
+#include <libdleyna/core/log.h>
+
+#include "device.h"
+#include "interface.h"
+#include "path.h"
+#include "props.h"
+
+static const gchar gUPnPContainer[] = "object.container";
+static const gchar gUPnPAlbum[] = "object.container.album";
+static const gchar gUPnPPerson[] = "object.container.person";
+static const gchar gUPnPGenre[] = "object.container.genre";
+static const gchar gUPnPAudioItem[] = "object.item.audioItem";
+static const gchar gUPnPVideoItem[] = "object.item.videoItem";
+static const gchar gUPnPImageItem[] = "object.item.imageItem";
+static const gchar gUPnPPlaylistItem[] = "object.item.playlistItem";
+static const gchar gUPnPItem[] = "object.item";
+
+static const unsigned int gUPnPContainerLen =
+ (sizeof(gUPnPContainer) / sizeof(gchar)) - 1;
+static const unsigned int gUPnPAlbumLen =
+ (sizeof(gUPnPAlbum) / sizeof(gchar)) - 1;
+static const unsigned int gUPnPPersonLen =
+ (sizeof(gUPnPPerson) / sizeof(gchar)) - 1;
+static const unsigned int gUPnPGenreLen =
+ (sizeof(gUPnPGenre) / sizeof(gchar)) - 1;
+static const unsigned int gUPnPAudioItemLen =
+ (sizeof(gUPnPAudioItem) / sizeof(gchar)) - 1;
+static const unsigned int gUPnPVideoItemLen =
+ (sizeof(gUPnPVideoItem) / sizeof(gchar)) - 1;
+static const unsigned int gUPnPImageItemLen =
+ (sizeof(gUPnPImageItem) / sizeof(gchar)) - 1;
+static const unsigned int gUPnPPlaylistItemLen =
+ (sizeof(gUPnPPlaylistItem) / sizeof(gchar)) - 1;
+static const unsigned int gUPnPItemLen =
+ (sizeof(gUPnPItem) / sizeof(gchar)) - 1;
+
+static const gchar gUPnPPhotoAlbum[] = "object.container.album.photoAlbum";
+static const gchar gUPnPMusicAlbum[] = "object.container.album.musicAlbum";
+static const gchar gUPnPMusicArtist[] = "object.container.person.musicArtist";
+static const gchar gUPnPMovieGenre[] = "object.container.genre.movieGenre";
+static const gchar gUPnPMusicGenre[] = "object.container.genre.musicGenre";
+static const gchar gUPnPMusicTrack[] = "object.item.audioItem.musicTrack";
+static const gchar gUPnPAudioBroadcast[] =
+ "object.item.audioItem.audioBroadcast";
+static const gchar gUPnPAudioBook[] = "object.item.audioItem.audioBook";
+static const gchar gUPnPMovie[] = "object.item.videoItem.movie";
+static const gchar gUPnPMusicVideoClip[] =
+ "object.item.videoItem.musicVideoClip";
+static const gchar gUPnPVideoBroadcast[] =
+ "object.item.videoItem.videoBroadcast";
+static const gchar gUPnPPhoto[] = "object.item.imageItem.photo";
+
+static const gchar gMediaSpec2Container[] = "container";
+static const gchar gMediaSpec2Album[] = "album";
+static const gchar gMediaSpec2AlbumPhoto[] = "album.photo";
+static const gchar gMediaSpec2AlbumMusic[] = "album.music";
+static const gchar gMediaSpec2Person[] = "person";
+static const gchar gMediaSpec2PersonMusicArtist[] = "person.musicartist";
+static const gchar gMediaSpec2Genre[] = "genre";
+static const gchar gMediaSpec2GenreMovie[] = "genre.movie";
+static const gchar gMediaSpec2GenreMusic[] = "genre.music";
+static const gchar gMediaSpec2AudioMusic[] = "audio.music";
+static const gchar gMediaSpec2AudioBroadcast[] = "audio.broadcast";
+static const gchar gMediaSpec2AudioBook[] = "audio.book";
+static const gchar gMediaSpec2Audio[] = "audio";
+static const gchar gMediaSpec2VideoMovie[] = "video.movie";
+static const gchar gMediaSpec2VideoMusicClip[] = "video.musicclip";
+static const gchar gMediaSpec2VideoBroadcast[] = "video.broadcast";
+static const gchar gMediaSpec2Video[] = "video";
+static const gchar gMediaSpec2ImagePhoto[] = "image.photo";
+static const gchar gMediaSpec2Image[] = "image";
+static const gchar gMediaSpec2Playlist[] = "playlist";
+static const gchar gMediaSpec2Item[] = "item";
+
+static dls_prop_map_t *prv_prop_map_new(const gchar *prop_name,
+ dls_upnp_prop_mask type,
+ gboolean filter,
+ gboolean searchable,
+ gboolean updateable)
+{
+ dls_prop_map_t *retval = g_new(dls_prop_map_t, 1);
+ retval->upnp_prop_name = prop_name;
+ retval->type = type;
+ retval->filter = filter;
+ retval->searchable = searchable;
+ retval->updateable = updateable;
+ return retval;
+}
+
+void dls_prop_maps_new(GHashTable **property_map, GHashTable **filter_map)
+{
+ dls_prop_map_t *prop_t;
+ GHashTable *p_map;
+ GHashTable *f_map;
+
+ p_map = g_hash_table_new_full(g_str_hash, g_str_equal, NULL, NULL);
+ f_map = g_hash_table_new_full(g_str_hash, g_str_equal, NULL, g_free);
+
+ /* @childCount */
+ prop_t = prv_prop_map_new("@childCount",
+ DLS_UPNP_MASK_PROP_CHILD_COUNT,
+ TRUE, TRUE, FALSE);
+ g_hash_table_insert(f_map, DLS_INTERFACE_PROP_CHILD_COUNT, prop_t);
+ g_hash_table_insert(p_map, "@childCount",
+ DLS_INTERFACE_PROP_CHILD_COUNT);
+
+ /* @id */
+ prop_t = prv_prop_map_new("@id",
+ DLS_UPNP_MASK_PROP_PATH,
+ FALSE, TRUE, FALSE);
+ g_hash_table_insert(f_map, DLS_INTERFACE_PROP_PATH, prop_t);
+ g_hash_table_insert(p_map, "@id", DLS_INTERFACE_PROP_PATH);
+
+ /* @parentID */
+ prop_t = prv_prop_map_new("@parentID",
+ DLS_UPNP_MASK_PROP_PARENT,
+ FALSE, TRUE, FALSE);
+ g_hash_table_insert(f_map, DLS_INTERFACE_PROP_PARENT, prop_t);
+ g_hash_table_insert(p_map, "@parentID", DLS_INTERFACE_PROP_PARENT);
+
+ /* @refID */
+ prop_t = prv_prop_map_new("@refID",
+ DLS_UPNP_MASK_PROP_REFPATH,
+ TRUE, TRUE, FALSE);
+ g_hash_table_insert(f_map, DLS_INTERFACE_PROP_REFPATH, prop_t);
+ g_hash_table_insert(p_map, "@refID", DLS_INTERFACE_PROP_REFPATH);
+
+ /* @restricted */
+ prop_t = prv_prop_map_new("@restricted",
+ DLS_UPNP_MASK_PROP_RESTRICTED,
+ TRUE, TRUE, FALSE);
+ g_hash_table_insert(f_map, DLS_INTERFACE_PROP_RESTRICTED, prop_t);
+ g_hash_table_insert(p_map, "@restricted",
+ DLS_INTERFACE_PROP_RESTRICTED);
+
+ /* @searchable */
+ prop_t = prv_prop_map_new("@searchable",
+ DLS_UPNP_MASK_PROP_SEARCHABLE,
+ TRUE, TRUE, FALSE);
+ g_hash_table_insert(f_map, DLS_INTERFACE_PROP_SEARCHABLE, prop_t);
+ g_hash_table_insert(p_map, "@searchable",
+ DLS_INTERFACE_PROP_SEARCHABLE);
+
+ /* dc:creator */
+ prop_t = prv_prop_map_new("dc:creator",
+ DLS_UPNP_MASK_PROP_CREATOR,
+ TRUE, TRUE, FALSE);
+ g_hash_table_insert(f_map, DLS_INTERFACE_PROP_CREATOR, prop_t);
+ g_hash_table_insert(p_map, "dc:creator", DLS_INTERFACE_PROP_CREATOR);
+
+ /* dc:date */
+ prop_t = prv_prop_map_new("dc:date",
+ DLS_UPNP_MASK_PROP_DATE,
+ TRUE, TRUE, TRUE);
+ g_hash_table_insert(f_map, DLS_INTERFACE_PROP_DATE, prop_t);
+ g_hash_table_insert(p_map, "dc:date", DLS_INTERFACE_PROP_DATE);
+
+ /* dc:title */
+ prop_t = prv_prop_map_new("dc:title",
+ DLS_UPNP_MASK_PROP_DISPLAY_NAME,
+ FALSE, TRUE, TRUE);
+ g_hash_table_insert(f_map, DLS_INTERFACE_PROP_DISPLAY_NAME, prop_t);
+ g_hash_table_insert(p_map, "dc:title", DLS_INTERFACE_PROP_DISPLAY_NAME);
+
+ /* dlna:dlnaManaged */
+ prop_t = prv_prop_map_new("dlna:dlnaManaged",
+ DLS_UPNP_MASK_PROP_DLNA_MANAGED,
+ TRUE, FALSE, FALSE);
+ g_hash_table_insert(f_map, DLS_INTERFACE_PROP_DLNA_MANAGED, prop_t);
+ g_hash_table_insert(p_map, "dlna:dlnaManaged",
+ DLS_INTERFACE_PROP_DLNA_MANAGED);
+
+ /* res */
+ /* res - RES */
+ prop_t = prv_prop_map_new("res",
+ DLS_UPNP_MASK_PROP_RESOURCES,
+ TRUE, FALSE, FALSE);
+ g_hash_table_insert(f_map, DLS_INTERFACE_PROP_RESOURCES, prop_t);
+
+ /* res - URL */
+ prop_t = prv_prop_map_new("res",
+ DLS_UPNP_MASK_PROP_URL,
+ TRUE, FALSE, FALSE);
+ g_hash_table_insert(f_map, DLS_INTERFACE_PROP_URL, prop_t);
+
+ /* res - URLS */
+ prop_t = prv_prop_map_new("res",
+ DLS_UPNP_MASK_PROP_URLS,
+ TRUE, FALSE, FALSE);
+ g_hash_table_insert(f_map, DLS_INTERFACE_PROP_URLS, prop_t);
+
+ /* res@bitrate */
+ prop_t = prv_prop_map_new("res@bitrate",
+ DLS_UPNP_MASK_PROP_BITRATE,
+ TRUE, TRUE, FALSE);
+ g_hash_table_insert(f_map, DLS_INTERFACE_PROP_BITRATE, prop_t);
+ g_hash_table_insert(p_map, "res@bitrate", DLS_INTERFACE_PROP_BITRATE);
+
+ /* res@bitsPerSample */
+ prop_t = prv_prop_map_new("res@bitsPerSample",
+ DLS_UPNP_MASK_PROP_BITS_PER_SAMPLE,
+ TRUE, TRUE, FALSE);
+ g_hash_table_insert(f_map, DLS_INTERFACE_PROP_BITS_PER_SAMPLE, prop_t);
+ g_hash_table_insert(p_map, "res@bitsPerSample",
+ DLS_INTERFACE_PROP_BITS_PER_SAMPLE);
+
+ /* res@colorDepth */
+ prop_t = prv_prop_map_new("res@colorDepth",
+ DLS_UPNP_MASK_PROP_COLOR_DEPTH,
+ TRUE, TRUE, FALSE);
+ g_hash_table_insert(f_map, DLS_INTERFACE_PROP_COLOR_DEPTH, prop_t);
+ g_hash_table_insert(p_map, "res@colorDepth",
+ DLS_INTERFACE_PROP_COLOR_DEPTH);
+
+ /* res@duration */
+ prop_t = prv_prop_map_new("res@duration",
+ DLS_UPNP_MASK_PROP_DURATION,
+ TRUE, TRUE, FALSE);
+ g_hash_table_insert(f_map, DLS_INTERFACE_PROP_DURATION, prop_t);
+ g_hash_table_insert(p_map, "res@duration",
+ DLS_INTERFACE_PROP_DURATION);
+
+ /* res@protocolInfo */
+ /* res@protocolInfo - DLNA PROFILE*/
+ prop_t = prv_prop_map_new("res@protocolInfo",
+ DLS_UPNP_MASK_PROP_DLNA_PROFILE,
+ TRUE, FALSE, FALSE);
+ g_hash_table_insert(f_map, DLS_INTERFACE_PROP_DLNA_PROFILE, prop_t);
+
+ /* res@protocolInfo - MIME TYPES*/
+ prop_t = prv_prop_map_new("res@protocolInfo",
+ DLS_UPNP_MASK_PROP_MIME_TYPE,
+ TRUE, FALSE, FALSE);
+ g_hash_table_insert(f_map, DLS_INTERFACE_PROP_MIME_TYPE, prop_t);
+
+ /* res@resolution */
+ /* res@resolution - HEIGH */
+ prop_t = prv_prop_map_new("res@resolution",
+ DLS_UPNP_MASK_PROP_HEIGHT,
+ TRUE, FALSE, FALSE);
+ g_hash_table_insert(f_map, DLS_INTERFACE_PROP_HEIGHT, prop_t);
+
+ /* res@resolution - WIDTH */
+ prop_t = prv_prop_map_new("res@resolution",
+ DLS_UPNP_MASK_PROP_WIDTH,
+ TRUE, FALSE, FALSE);
+ g_hash_table_insert(f_map, DLS_INTERFACE_PROP_WIDTH, prop_t);
+
+ /* res@sampleFrequency */
+ prop_t = prv_prop_map_new("res@sampleFrequency",
+ DLS_UPNP_MASK_PROP_SAMPLE_RATE,
+ TRUE, TRUE, FALSE);
+ g_hash_table_insert(f_map, DLS_INTERFACE_PROP_SAMPLE_RATE, prop_t);
+ g_hash_table_insert(p_map, "res@sampleFrequency",
+ DLS_INTERFACE_PROP_SAMPLE_RATE);
+
+ /* res@size */
+ prop_t = prv_prop_map_new("res@size",
+ DLS_UPNP_MASK_PROP_SIZE,
+ TRUE, TRUE, FALSE);
+ g_hash_table_insert(f_map, DLS_INTERFACE_PROP_SIZE, prop_t);
+ g_hash_table_insert(p_map, "res@size", DLS_INTERFACE_PROP_SIZE);
+
+ /* res@updateCount */
+ prop_t = prv_prop_map_new("res@updateCount",
+ DLS_UPNP_MASK_PROP_UPDATE_COUNT,
+ TRUE, TRUE, FALSE);
+ g_hash_table_insert(f_map, DLS_INTERFACE_PROP_UPDATE_COUNT, prop_t);
+ g_hash_table_insert(p_map, "res@updateCount",
+ DLS_INTERFACE_PROP_UPDATE_COUNT);
+
+ /* upnp:album */
+ prop_t = prv_prop_map_new("upnp:album",
+ DLS_UPNP_MASK_PROP_ALBUM,
+ TRUE, TRUE, TRUE);
+ g_hash_table_insert(f_map, DLS_INTERFACE_PROP_ALBUM, prop_t);
+ g_hash_table_insert(p_map, "upnp:album", DLS_INTERFACE_PROP_ALBUM);
+
+ /* upnp:albumArtURI */
+ prop_t = prv_prop_map_new("upnp:albumArtURI",
+ DLS_UPNP_MASK_PROP_ALBUM_ART_URL,
+ TRUE, TRUE, FALSE);
+ g_hash_table_insert(f_map, DLS_INTERFACE_PROP_ALBUM_ART_URL, prop_t);
+ g_hash_table_insert(p_map, "upnp:albumArtURI",
+ DLS_INTERFACE_PROP_ALBUM_ART_URL);
+
+ /* upnp:artist */
+ /* upnp:artist - ARTIST*/
+ prop_t = prv_prop_map_new("upnp:artist",
+ DLS_UPNP_MASK_PROP_ARTIST,
+ TRUE, TRUE, FALSE);
+ g_hash_table_insert(f_map, DLS_INTERFACE_PROP_ARTIST, prop_t);
+ g_hash_table_insert(p_map, "upnp:artist", DLS_INTERFACE_PROP_ARTIST);
+
+ /* upnp:artist - ARTISTS*/
+ prop_t = prv_prop_map_new("upnp:artist",
+ DLS_UPNP_MASK_PROP_ARTISTS,
+ TRUE, FALSE, TRUE);
+ g_hash_table_insert(f_map, DLS_INTERFACE_PROP_ARTISTS, prop_t);
+
+ /* upnp:class */
+ prop_t = prv_prop_map_new("upnp:class",
+ DLS_UPNP_MASK_PROP_TYPE,
+ FALSE, TRUE, TRUE);
+ g_hash_table_insert(f_map, DLS_INTERFACE_PROP_TYPE, prop_t);
+ g_hash_table_insert(p_map, "upnp:class", DLS_INTERFACE_PROP_TYPE);
+
+ /* upnp:containerUpdateID */
+ prop_t = prv_prop_map_new("upnp:containerUpdateID",
+ DLS_UPNP_MASK_PROP_CONTAINER_UPDATE_ID,
+ TRUE, TRUE, FALSE);
+ g_hash_table_insert(f_map, DLS_INTERFACE_PROP_CONTAINER_UPDATE_ID,
+ prop_t);
+ g_hash_table_insert(p_map, "upnp:containerUpdateID",
+ DLS_INTERFACE_PROP_CONTAINER_UPDATE_ID);
+
+ /* upnp:createClass */
+ prop_t = prv_prop_map_new("upnp:createClass",
+ DLS_UPNP_MASK_PROP_CREATE_CLASSES,
+ TRUE, FALSE, FALSE);
+ g_hash_table_insert(f_map, DLS_INTERFACE_PROP_CREATE_CLASSES, prop_t);
+
+ /* upnp:genre */
+ prop_t = prv_prop_map_new("upnp:genre",
+ DLS_UPNP_MASK_PROP_GENRE,
+ TRUE, TRUE, FALSE);
+ g_hash_table_insert(f_map, DLS_INTERFACE_PROP_GENRE, prop_t);
+ g_hash_table_insert(p_map, "upnp:genre", DLS_INTERFACE_PROP_GENRE);
+
+ /* upnp:objectUpdateID */
+ prop_t = prv_prop_map_new("upnp:objectUpdateID",
+ DLS_UPNP_MASK_PROP_OBJECT_UPDATE_ID,
+ TRUE, TRUE, FALSE);
+ g_hash_table_insert(f_map, DLS_INTERFACE_PROP_OBJECT_UPDATE_ID, prop_t);
+ g_hash_table_insert(p_map, "upnp:objectUpdateID",
+ DLS_INTERFACE_PROP_OBJECT_UPDATE_ID);
+
+ /* upnp:originalTrackNumber */
+ prop_t = prv_prop_map_new("upnp:originalTrackNumber",
+ DLS_UPNP_MASK_PROP_TRACK_NUMBER,
+ TRUE, TRUE, TRUE);
+ g_hash_table_insert(f_map, DLS_INTERFACE_PROP_TRACK_NUMBER, prop_t);
+ g_hash_table_insert(p_map, "upnp:originalTrackNumber",
+ DLS_INTERFACE_PROP_TRACK_NUMBER);
+
+ /* upnp:totalDeletedChildCount */
+ prop_t = prv_prop_map_new("upnp:totalDeletedChildCount",
+ DLS_UPNP_MASK_PROP_TOTAL_DELETED_CHILD_COUNT,
+ TRUE, TRUE, FALSE);
+ g_hash_table_insert(f_map, DLS_INTERFACE_PROP_TOTAL_DELETED_CHILD_COUNT,
+ prop_t);
+ g_hash_table_insert(p_map, "upnp:totalDeletedChildCount",
+ DLS_INTERFACE_PROP_TOTAL_DELETED_CHILD_COUNT);
+
+ *filter_map = f_map;
+ *property_map = p_map;
+}
+
+static gchar *prv_compute_upnp_filter(GHashTable *upnp_props)
+{
+ gpointer key;
+ GString *str;
+ GHashTableIter iter;
+
+ str = g_string_new("");
+ g_hash_table_iter_init(&iter, upnp_props);
+ if (g_hash_table_iter_next(&iter, &key, NULL)) {
+ g_string_append(str, (const gchar *)key);
+ while (g_hash_table_iter_next(&iter, &key, NULL)) {
+ g_string_append(str, ",");
+ g_string_append(str, (const gchar *)key);
+ }
+ }
+
+ return g_string_free(str, FALSE);
+}
+
+static dls_upnp_prop_mask prv_parse_filter_list(GHashTable *filter_map,
+ GVariant *filter,
+ gchar **upnp_filter)
+{
+ GVariantIter viter;
+ const gchar *prop;
+ dls_prop_map_t *prop_map;
+ GHashTable *upnp_props;
+ dls_upnp_prop_mask mask = 0;
+
+ upnp_props = g_hash_table_new_full(g_str_hash, g_str_equal,
+ NULL, NULL);
+ (void) g_variant_iter_init(&viter, filter);
+
+ while (g_variant_iter_next(&viter, "&s", &prop)) {
+ prop_map = g_hash_table_lookup(filter_map, prop);
+ if (!prop_map)
+ continue;
+
+ mask |= prop_map->type;
+
+ if (!prop_map->filter)
+ continue;
+
+ g_hash_table_insert(upnp_props,
+ (gpointer) prop_map->upnp_prop_name, NULL);
+ }
+
+ *upnp_filter = prv_compute_upnp_filter(upnp_props);
+ g_hash_table_unref(upnp_props);
+
+ return mask;
+}
+
+dls_upnp_prop_mask dls_props_parse_filter(GHashTable *filter_map,
+ GVariant *filter,
+ gchar **upnp_filter)
+{
+ gchar *str;
+ gboolean parse_filter = TRUE;
+ dls_upnp_prop_mask mask;
+
+ if (g_variant_n_children(filter) == 1) {
+ g_variant_get_child(filter, 0, "&s", &str);
+ if (!strcmp(str, "*"))
+ parse_filter = FALSE;
+ }
+
+ if (parse_filter) {
+ mask = prv_parse_filter_list(filter_map, filter, upnp_filter);
+ } else {
+ mask = DLS_UPNP_MASK_ALL_PROPS;
+ *upnp_filter = g_strdup("*");
+ }
+
+ return mask;
+}
+
+gboolean dls_props_parse_update_filter(GHashTable *filter_map,
+ GVariant *to_add_update,
+ GVariant *to_delete,
+ dls_upnp_prop_mask *mask,
+ gchar **upnp_filter)
+{
+ GVariantIter viter;
+ const gchar *prop;
+ GVariant *value;
+ dls_prop_map_t *prop_map;
+ GHashTable *upnp_props;
+ gboolean retval = FALSE;
+
+ *mask = 0;
+
+ upnp_props = g_hash_table_new_full(g_str_hash, g_str_equal,
+ NULL, NULL);
+
+ (void) g_variant_iter_init(&viter, to_add_update);
+
+ while (g_variant_iter_next(&viter, "{&sv}", &prop, &value)) {
+ DLEYNA_LOG_DEBUG("to_add_update = %s", prop);
+
+ prop_map = g_hash_table_lookup(filter_map, prop);
+
+ if ((!prop_map) || (!prop_map->updateable))
+ goto on_error;
+
+ *mask |= prop_map->type;
+
+ if (!prop_map->filter)
+ continue;
+
+ g_hash_table_insert(upnp_props,
+ (gpointer) prop_map->upnp_prop_name, NULL);
+ }
+
+ (void) g_variant_iter_init(&viter, to_delete);
+
+ while (g_variant_iter_next(&viter, "&s", &prop)) {
+ DLEYNA_LOG_DEBUG("to_delete = %s", prop);
+
+ prop_map = g_hash_table_lookup(filter_map, prop);
+
+ if ((!prop_map) || (!prop_map->updateable) ||
+ (*mask & prop_map->type) != 0)
+ goto on_error;
+
+ *mask |= prop_map->type;
+
+ if (!prop_map->filter)
+ continue;
+
+ g_hash_table_insert(upnp_props,
+ (gpointer) prop_map->upnp_prop_name, NULL);
+ }
+
+ *upnp_filter = prv_compute_upnp_filter(upnp_props);
+
+ retval = TRUE;
+
+on_error:
+
+ g_hash_table_unref(upnp_props);
+
+ return retval;
+}
+
+static void prv_add_string_prop(GVariantBuilder *vb, const gchar *key,
+ const gchar *value)
+{
+ if (value) {
+ DLEYNA_LOG_DEBUG("Prop %s = %s", key, value);
+
+ g_variant_builder_add(vb, "{sv}", key,
+ g_variant_new_string(value));
+ }
+}
+
+static void prv_add_strv_prop(GVariantBuilder *vb, const gchar *key,
+ const gchar **value, unsigned int len)
+{
+ if (len > 0)
+ g_variant_builder_add(vb, "{sv}", key,
+ g_variant_new_strv(value, len));
+}
+
+static void prv_add_path_prop(GVariantBuilder *vb, const gchar *key,
+ const gchar *value)
+{
+ if (value) {
+ DLEYNA_LOG_DEBUG("Prop %s = %s", key, value);
+
+ g_variant_builder_add(vb, "{sv}", key,
+ g_variant_new_object_path(value));
+ }
+}
+
+static void prv_add_uint_prop(GVariantBuilder *vb, const gchar *key,
+ unsigned int value)
+{
+ DLEYNA_LOG_DEBUG("Prop %s = %u", key, value);
+
+ g_variant_builder_add(vb, "{sv}", key, g_variant_new_uint32(value));
+}
+
+static void prv_add_int_prop(GVariantBuilder *vb, const gchar *key,
+ int value)
+{
+ if (value != -1)
+ g_variant_builder_add(vb, "{sv}", key,
+ g_variant_new_int32(value));
+}
+
+static void prv_add_variant_prop(GVariantBuilder *vb, const gchar *key,
+ GVariant *prop)
+{
+ if (prop)
+ g_variant_builder_add(vb, "{sv}", key, prop);
+}
+
+void dls_props_add_child_count(GVariantBuilder *item_vb, gint value)
+{
+ prv_add_int_prop(item_vb, DLS_INTERFACE_PROP_CHILD_COUNT, value);
+}
+
+static void prv_add_bool_prop(GVariantBuilder *vb, const gchar *key,
+ gboolean value)
+{
+ DLEYNA_LOG_DEBUG("Prop %s = %u", key, value);
+
+ g_variant_builder_add(vb, "{sv}", key, g_variant_new_boolean(value));
+}
+
+static void prv_add_int64_prop(GVariantBuilder *vb, const gchar *key,
+ gint64 value)
+{
+ if (value != -1) {
+ DLEYNA_LOG_DEBUG("Prop %s = %"G_GINT64_FORMAT, key, value);
+
+ g_variant_builder_add(vb, "{sv}", key,
+ g_variant_new_int64(value));
+ }
+}
+
+static void prv_add_list_dlna_str(gpointer data, gpointer user_data)
+{
+ GVariantBuilder *vb = (GVariantBuilder *)user_data;
+ gchar *cap_str = (gchar *)data;
+ gchar *str;
+ int value = 0;
+
+ if (g_str_has_prefix(cap_str, "srs-rt-retention-period-")) {
+ str = cap_str + strlen("srs-rt-retention-period-");
+ cap_str = "srs-rt-retention-period";
+
+ if (*str) {
+ if (!g_strcmp0(str, "infinity"))
+ value = -1;
+ else
+ value = atoi(str);
+ }
+ }
+
+ prv_add_uint_prop(vb, cap_str, value);
+}
+
+static GVariant *prv_add_list_dlna_prop(GList *list)
+{
+ GVariantBuilder vb;
+
+ g_variant_builder_init(&vb, G_VARIANT_TYPE("a{sv}"));
+
+ g_list_foreach(list, prv_add_list_dlna_str, &vb);
+
+ return g_variant_builder_end(&vb);
+}
+
+static void prv_add_list_artists_str(gpointer data, gpointer user_data)
+{
+ GVariantBuilder *vb = (GVariantBuilder *)user_data;
+ GUPnPDIDLLiteContributor *contributor = data;
+ const char *str;
+
+ str = gupnp_didl_lite_contributor_get_name(contributor);
+ g_variant_builder_add(vb, "s", str);
+}
+
+static GVariant *prv_get_artists_prop(GList *list)
+{
+ GVariantBuilder vb;
+
+ g_variant_builder_init(&vb, G_VARIANT_TYPE("as"));
+ g_list_foreach(list, prv_add_list_artists_str, &vb);
+
+ return g_variant_builder_end(&vb);
+}
+
+void dls_props_add_device(GUPnPDeviceInfo *proxy,
+ const dls_device_t *device,
+ GVariantBuilder *vb)
+{
+ gchar *str;
+ GList *list;
+ GVariant *dlna_caps;
+
+ prv_add_string_prop(vb, DLS_INTERFACE_PROP_LOCATION,
+ gupnp_device_info_get_location(proxy));
+
+ prv_add_string_prop(vb, DLS_INTERFACE_PROP_UDN,
+ gupnp_device_info_get_udn(proxy));
+
+ prv_add_string_prop(vb, DLS_INTERFACE_PROP_DEVICE_TYPE,
+ gupnp_device_info_get_device_type(proxy));
+
+ str = gupnp_device_info_get_friendly_name(proxy);
+ prv_add_string_prop(vb, DLS_INTERFACE_PROP_FRIENDLY_NAME, str);
+ g_free(str);
+
+ str = gupnp_device_info_get_manufacturer(proxy);
+ prv_add_string_prop(vb, DLS_INTERFACE_PROP_MANUFACTURER, str);
+ g_free(str);
+
+ str = gupnp_device_info_get_manufacturer_url(proxy);
+ prv_add_string_prop(vb, DLS_INTERFACE_PROP_MANUFACTURER_URL, str);
+ g_free(str);
+
+ str = gupnp_device_info_get_model_description(proxy);
+ prv_add_string_prop(vb, DLS_INTERFACE_PROP_MODEL_DESCRIPTION, str);
+ g_free(str);
+
+ str = gupnp_device_info_get_model_name(proxy);
+ prv_add_string_prop(vb, DLS_INTERFACE_PROP_MODEL_NAME, str);
+ g_free(str);
+
+ str = gupnp_device_info_get_model_number(proxy);
+ prv_add_string_prop(vb, DLS_INTERFACE_PROP_MODEL_NUMBER, str);
+ g_free(str);
+
+ str = gupnp_device_info_get_model_url(proxy);
+ prv_add_string_prop(vb, DLS_INTERFACE_PROP_MODEL_URL, str);
+ g_free(str);
+
+ str = gupnp_device_info_get_serial_number(proxy);
+ prv_add_string_prop(vb, DLS_INTERFACE_PROP_SERIAL_NUMBER, str);
+ g_free(str);
+
+ str = gupnp_device_info_get_presentation_url(proxy);
+ prv_add_string_prop(vb, DLS_INTERFACE_PROP_PRESENTATION_URL, str);
+ g_free(str);
+
+ str = gupnp_device_info_get_icon_url(proxy, NULL, -1, -1, -1, FALSE,
+ NULL, NULL, NULL, NULL);
+ prv_add_string_prop(vb, DLS_INTERFACE_PROP_ICON_URL, str);
+ g_free(str);
+
+ list = gupnp_device_info_list_dlna_capabilities(proxy);
+ if (list != NULL) {
+ dlna_caps = prv_add_list_dlna_prop(list);
+ g_variant_builder_add(vb, "{sv}",
+ DLS_INTERFACE_PROP_SV_DLNA_CAPABILITIES,
+ dlna_caps);
+ g_list_free_full(list, g_free);
+ }
+
+ if (device->search_caps != NULL)
+ g_variant_builder_add(vb, "{sv}",
+ DLS_INTERFACE_PROP_SV_SEARCH_CAPABILITIES,
+ device->search_caps);
+
+ if (device->sort_caps != NULL)
+ g_variant_builder_add(vb, "{sv}",
+ DLS_INTERFACE_PROP_SV_SORT_CAPABILITIES,
+ device->sort_caps);
+
+ if (device->sort_ext_caps != NULL)
+ g_variant_builder_add(
+ vb, "{sv}",
+ DLS_INTERFACE_PROP_SV_SORT_EXT_CAPABILITIES,
+ device->sort_ext_caps);
+
+ if (device->feature_list != NULL)
+ g_variant_builder_add(vb, "{sv}",
+ DLS_INTERFACE_PROP_SV_FEATURE_LIST,
+ device->feature_list);
+}
+
+GVariant *dls_props_get_device_prop(GUPnPDeviceInfo *proxy,
+ const dls_device_t *device,
+ const gchar *prop)
+{
+ GVariant *dlna_caps = NULL;
+ GVariant *retval = NULL;
+ const gchar *str = NULL;
+ gchar *copy = NULL;
+ GList *list;
+
+ if (!strcmp(DLS_INTERFACE_PROP_LOCATION, prop)) {
+ str = gupnp_device_info_get_location(proxy);
+ } else if (!strcmp(DLS_INTERFACE_PROP_UDN, prop)) {
+ str = gupnp_device_info_get_udn(proxy);
+ } else if (!strcmp(DLS_INTERFACE_PROP_DEVICE_TYPE, prop)) {
+ str = gupnp_device_info_get_device_type(proxy);
+ } else if (!strcmp(DLS_INTERFACE_PROP_FRIENDLY_NAME, prop)) {
+ copy = gupnp_device_info_get_friendly_name(proxy);
+ str = copy;
+ } else if (!strcmp(DLS_INTERFACE_PROP_MANUFACTURER, prop)) {
+ copy = gupnp_device_info_get_manufacturer(proxy);
+ str = copy;
+ } else if (!strcmp(DLS_INTERFACE_PROP_MANUFACTURER_URL, prop)) {
+ copy = gupnp_device_info_get_manufacturer_url(proxy);
+ str = copy;
+ } else if (!strcmp(DLS_INTERFACE_PROP_MODEL_DESCRIPTION, prop)) {
+ copy = gupnp_device_info_get_model_description(proxy);
+ str = copy;
+ } else if (!strcmp(DLS_INTERFACE_PROP_MODEL_NAME, prop)) {
+ copy = gupnp_device_info_get_model_name(proxy);
+ str = copy;
+ } else if (!strcmp(DLS_INTERFACE_PROP_MODEL_NUMBER, prop)) {
+ copy = gupnp_device_info_get_model_number(proxy);
+ str = copy;
+ } else if (!strcmp(DLS_INTERFACE_PROP_MODEL_URL, prop)) {
+ copy = gupnp_device_info_get_model_url(proxy);
+ str = copy;
+ } else if (!strcmp(DLS_INTERFACE_PROP_SERIAL_NUMBER, prop)) {
+ copy = gupnp_device_info_get_serial_number(proxy);
+ str = copy;
+ } else if (!strcmp(DLS_INTERFACE_PROP_PRESENTATION_URL, prop)) {
+ copy = gupnp_device_info_get_presentation_url(proxy);
+ str = copy;
+ } else if (!strcmp(DLS_INTERFACE_PROP_ICON_URL, prop)) {
+ copy = gupnp_device_info_get_icon_url(proxy, NULL,
+ -1, -1, -1, FALSE,
+ NULL, NULL, NULL, NULL);
+ str = copy;
+ } else if (!strcmp(DLS_INTERFACE_PROP_SV_DLNA_CAPABILITIES, prop)) {
+ list = gupnp_device_info_list_dlna_capabilities(proxy);
+ if (list != NULL) {
+ dlna_caps = prv_add_list_dlna_prop(list);
+ g_list_free_full(list, g_free);
+ retval = g_variant_ref_sink(dlna_caps);
+
+#if DLEYNA_LOG_LEVEL & DLEYNA_LOG_LEVEL_DEBUG
+ copy = g_variant_print(dlna_caps, FALSE);
+ DLEYNA_LOG_DEBUG("Prop %s = %s", prop, copy);
+#endif
+ }
+ } else if (!strcmp(DLS_INTERFACE_PROP_SV_SEARCH_CAPABILITIES, prop)) {
+ if (device->search_caps != NULL) {
+ retval = g_variant_ref(device->search_caps);
+
+#if DLEYNA_LOG_LEVEL & DLEYNA_LOG_LEVEL_DEBUG
+ copy = g_variant_print(device->search_caps, FALSE);
+ DLEYNA_LOG_DEBUG("Prop %s = %s", prop, copy);
+#endif
+ }
+ } else if (!strcmp(DLS_INTERFACE_PROP_SV_SORT_CAPABILITIES, prop)) {
+ if (device->sort_caps != NULL) {
+ retval = g_variant_ref(device->sort_caps);
+
+#if DLEYNA_LOG_LEVEL & DLEYNA_LOG_LEVEL_DEBUG
+ copy = g_variant_print(device->sort_caps, FALSE);
+ DLEYNA_LOG_DEBUG("Prop %s = %s", prop, copy);
+#endif
+ }
+ } else if (!strcmp(DLS_INTERFACE_PROP_SV_SORT_EXT_CAPABILITIES, prop)) {
+ if (device->sort_ext_caps != NULL) {
+ retval = g_variant_ref(device->sort_ext_caps);
+
+#if DLEYNA_LOG_LEVEL & DLEYNA_LOG_LEVEL_DEBUG
+ copy = g_variant_print(device->sort_ext_caps, FALSE);
+ DLEYNA_LOG_DEBUG("Prop %s = %s", prop, copy);
+#endif
+ }
+ } else if (!strcmp(DLS_INTERFACE_PROP_SV_FEATURE_LIST, prop)) {
+ if (device->feature_list != NULL) {
+ retval = g_variant_ref(device->feature_list);
+
+#if DLEYNA_LOG_LEVEL & DLEYNA_LOG_LEVEL_DEBUG
+ copy = g_variant_print(device->feature_list, FALSE);
+ DLEYNA_LOG_DEBUG("Prop %s = %s", prop, copy);
+#endif
+ }
+ }
+
+ if (!retval) {
+ if (str) {
+ DLEYNA_LOG_DEBUG("Prop %s = %s", prop, str);
+
+ retval = g_variant_ref_sink(g_variant_new_string(str));
+ }
+#if DLEYNA_LOG_LEVEL & DLEYNA_LOG_LEVEL_WARNING
+ else
+ DLEYNA_LOG_WARNING("Property %s not defined", prop);
+#endif
+ }
+
+ g_free(copy);
+
+ return retval;
+}
+
+static GUPnPDIDLLiteResource *prv_match_resource(GUPnPDIDLLiteResource *res,
+ gchar **pi_str_array)
+{
+ GUPnPDIDLLiteResource *retval = NULL;
+ GUPnPProtocolInfo *res_pi;
+ GUPnPProtocolInfo *pi;
+ unsigned int i;
+ gboolean match;
+
+ if (!pi_str_array) {
+ retval = res;
+ goto done;
+ }
+
+ res_pi = gupnp_didl_lite_resource_get_protocol_info(res);
+ if (!res_pi)
+ goto done;
+
+ for (i = 0; pi_str_array[i]; ++i) {
+ pi = gupnp_protocol_info_new_from_string(pi_str_array[i],
+ NULL);
+ if (!pi)
+ continue;
+ match = gupnp_protocol_info_is_compatible(pi, res_pi);
+ g_object_unref(pi);
+ if (match) {
+ retval = res;
+ break;
+ }
+ }
+done:
+
+ return retval;
+}
+
+static GUPnPDIDLLiteResource *prv_get_matching_resource
+ (GUPnPDIDLLiteObject *object, const gchar *protocol_info)
+{
+ GUPnPDIDLLiteResource *retval = NULL;
+ GUPnPDIDLLiteResource *res;
+ GList *resources;
+ GList *ptr;
+ gchar **pi_str_array = NULL;
+
+ if (protocol_info)
+ pi_str_array = g_strsplit(protocol_info, ",", 0);
+
+ resources = gupnp_didl_lite_object_get_resources(object);
+ ptr = resources;
+
+ while (ptr) {
+ res = ptr->data;
+ if (!retval) {
+ retval = prv_match_resource(res, pi_str_array);
+ if (!retval)
+ g_object_unref(res);
+ } else {
+ g_object_unref(res);
+ }
+
+ ptr = ptr->next;
+ }
+
+ g_list_free(resources);
+ if (pi_str_array)
+ g_strfreev(pi_str_array);
+
+ return retval;
+}
+
+static void prv_parse_resources(GVariantBuilder *item_vb,
+ GUPnPDIDLLiteResource *res,
+ dls_upnp_prop_mask filter_mask)
+{
+ GUPnPProtocolInfo *protocol_info;
+ int int_val;
+ gint64 int64_val;
+ const char *str_val;
+ guint uint_val;
+
+ if (filter_mask & DLS_UPNP_MASK_PROP_SIZE) {
+ int64_val = gupnp_didl_lite_resource_get_size64(res);
+ prv_add_int64_prop(item_vb, DLS_INTERFACE_PROP_SIZE, int64_val);
+ }
+
+ if (filter_mask & DLS_UPNP_MASK_PROP_BITRATE) {
+ int_val = gupnp_didl_lite_resource_get_bitrate(res);
+ prv_add_int_prop(item_vb, DLS_INTERFACE_PROP_BITRATE, int_val);
+ }
+
+ if (filter_mask & DLS_UPNP_MASK_PROP_SAMPLE_RATE) {
+ int_val = gupnp_didl_lite_resource_get_sample_freq(res);
+ prv_add_int_prop(item_vb, DLS_INTERFACE_PROP_SAMPLE_RATE,
+ int_val);
+ }
+
+ if (filter_mask & DLS_UPNP_MASK_PROP_BITS_PER_SAMPLE) {
+ int_val = gupnp_didl_lite_resource_get_bits_per_sample(res);
+ prv_add_int_prop(item_vb, DLS_INTERFACE_PROP_BITS_PER_SAMPLE,
+ int_val);
+ }
+
+ if (filter_mask & DLS_UPNP_MASK_PROP_DURATION) {
+ int_val = (int) gupnp_didl_lite_resource_get_duration(res);
+ prv_add_int_prop(item_vb, DLS_INTERFACE_PROP_DURATION, int_val);
+ }
+
+ if (filter_mask & DLS_UPNP_MASK_PROP_WIDTH) {
+ int_val = (int) gupnp_didl_lite_resource_get_width(res);
+ prv_add_int_prop(item_vb, DLS_INTERFACE_PROP_WIDTH, int_val);
+ }
+
+ if (filter_mask & DLS_UPNP_MASK_PROP_HEIGHT) {
+ int_val = (int) gupnp_didl_lite_resource_get_height(res);
+ prv_add_int_prop(item_vb, DLS_INTERFACE_PROP_HEIGHT, int_val);
+ }
+
+ if (filter_mask & DLS_UPNP_MASK_PROP_COLOR_DEPTH) {
+ int_val = (int) gupnp_didl_lite_resource_get_color_depth(res);
+ prv_add_int_prop(item_vb, DLS_INTERFACE_PROP_COLOR_DEPTH,
+ int_val);
+ }
+
+ if (filter_mask & DLS_UPNP_MASK_PROP_UPDATE_COUNT) {
+ uint_val = gupnp_didl_lite_resource_get_update_count(res);
+ prv_add_uint_prop(item_vb, DLS_INTERFACE_PROP_UPDATE_COUNT,
+ uint_val);
+ }
+
+ protocol_info = gupnp_didl_lite_resource_get_protocol_info(res);
+
+ if (filter_mask & DLS_UPNP_MASK_PROP_DLNA_PROFILE) {
+ str_val = gupnp_protocol_info_get_dlna_profile(protocol_info);
+ prv_add_string_prop(item_vb, DLS_INTERFACE_PROP_DLNA_PROFILE,
+ str_val);
+ }
+
+ if (filter_mask & DLS_UPNP_MASK_PROP_MIME_TYPE) {
+ str_val = gupnp_protocol_info_get_mime_type(protocol_info);
+ prv_add_string_prop(item_vb, DLS_INTERFACE_PROP_MIME_TYPE,
+ str_val);
+ }
+}
+
+static GVariant *prv_compute_create_classes(GUPnPDIDLLiteContainer *container)
+{
+ GVariantBuilder create_classes_vb;
+ GList *create_classes;
+ GList *ptr;
+ GUPnPDIDLLiteCreateClass *create_class;
+ const char *content;
+ gboolean inc_derived;
+
+ g_variant_builder_init(&create_classes_vb, G_VARIANT_TYPE("a(sb)"));
+
+ create_classes = gupnp_didl_lite_container_get_create_classes_full(
+ container);
+ ptr = create_classes;
+ while (ptr) {
+ create_class = ptr->data;
+ content = gupnp_didl_lite_create_class_get_content(
+ create_class);
+ inc_derived = gupnp_didl_lite_create_class_get_include_derived(
+ create_class);
+ g_variant_builder_add(&create_classes_vb,
+ "(sb)", content, inc_derived);
+ g_object_unref(ptr->data);
+ ptr = g_list_next(ptr);
+ }
+ g_list_free(create_classes);
+
+ return g_variant_builder_end(&create_classes_vb);
+}
+
+const gchar *dls_props_media_spec_to_upnp_class(const gchar *m2spec_class)
+{
+ const gchar *retval = NULL;
+
+ if (!strcmp(m2spec_class, gMediaSpec2AlbumPhoto))
+ retval = gUPnPPhotoAlbum;
+ else if (!strcmp(m2spec_class, gMediaSpec2AlbumMusic))
+ retval = gUPnPMusicAlbum;
+ else if (!strcmp(m2spec_class, gMediaSpec2Album))
+ retval = gUPnPAlbum;
+ else if (!strcmp(m2spec_class, gMediaSpec2PersonMusicArtist))
+ retval = gUPnPMusicArtist;
+ else if (!strcmp(m2spec_class, gMediaSpec2Person))
+ retval = gUPnPPerson;
+ else if (!strcmp(m2spec_class, gMediaSpec2GenreMovie))
+ retval = gUPnPMovieGenre;
+ else if (!strcmp(m2spec_class, gMediaSpec2GenreMusic))
+ retval = gUPnPMusicGenre;
+ else if (!strcmp(m2spec_class, gMediaSpec2Genre))
+ retval = gUPnPGenre;
+ else if (!strcmp(m2spec_class, gMediaSpec2Container))
+ retval = gUPnPContainer;
+ else if (!strcmp(m2spec_class, gMediaSpec2AudioMusic))
+ retval = gUPnPMusicTrack;
+ else if (!strcmp(m2spec_class, gMediaSpec2AudioBroadcast))
+ retval = gUPnPAudioBroadcast;
+ else if (!strcmp(m2spec_class, gMediaSpec2AudioBook))
+ retval = gUPnPAudioBook;
+ else if (!strcmp(m2spec_class, gMediaSpec2Audio))
+ retval = gUPnPAudioItem;
+ else if (!strcmp(m2spec_class, gMediaSpec2VideoMovie))
+ retval = gUPnPMovie;
+ else if (!strcmp(m2spec_class, gMediaSpec2VideoMusicClip))
+ retval = gUPnPMusicVideoClip;
+ else if (!strcmp(m2spec_class, gMediaSpec2VideoBroadcast))
+ retval = gUPnPVideoBroadcast;
+ else if (!strcmp(m2spec_class, gMediaSpec2Video))
+ retval = gUPnPVideoItem;
+ else if (!strcmp(m2spec_class, gMediaSpec2ImagePhoto))
+ retval = gUPnPPhoto;
+ else if (!strcmp(m2spec_class, gMediaSpec2Image))
+ retval = gUPnPImageItem;
+ else if (!strcmp(m2spec_class, gMediaSpec2Playlist))
+ retval = gUPnPPlaylistItem;
+ else if (!strcmp(m2spec_class, gMediaSpec2Item))
+ retval = gUPnPItem;
+
+ return retval;
+}
+
+const gchar *dls_props_upnp_class_to_media_spec(const gchar *upnp_class)
+{
+ const gchar *retval = NULL;
+ const gchar *ptr;
+
+ if (!strncmp(upnp_class, gUPnPAlbum, gUPnPAlbumLen)) {
+ ptr = upnp_class + gUPnPAlbumLen;
+ if (!strcmp(ptr, ".photoAlbum"))
+ retval = gMediaSpec2AlbumPhoto;
+ else if (!strcmp(ptr, ".musicAlbum"))
+ retval = gMediaSpec2AlbumMusic;
+ else
+ retval = gMediaSpec2Album;
+ } else if (!strncmp(upnp_class, gUPnPPerson, gUPnPPersonLen)) {
+ ptr = upnp_class + gUPnPPersonLen;
+ if (!strcmp(ptr, ".musicArtist"))
+ retval = gMediaSpec2PersonMusicArtist;
+ else
+ retval = gMediaSpec2Person;
+ } else if (!strncmp(upnp_class, gUPnPGenre, gUPnPGenreLen)) {
+ ptr = upnp_class + gUPnPGenreLen;
+ if (!strcmp(ptr, ".movieGenre"))
+ retval = gMediaSpec2GenreMovie;
+ else if (!strcmp(ptr, ".musicGenre"))
+ retval = gMediaSpec2GenreMusic;
+ else
+ retval = gMediaSpec2Genre;
+ } else if (!strncmp(upnp_class, gUPnPContainer, gUPnPContainerLen)) {
+ ptr = upnp_class + gUPnPContainerLen;
+ if (!*ptr || *ptr == '.')
+ retval = gMediaSpec2Container;
+ } else if (!strncmp(upnp_class, gUPnPAudioItem, gUPnPAudioItemLen)) {
+ ptr = upnp_class + gUPnPAudioItemLen;
+ if (!strcmp(ptr, ".musicTrack"))
+ retval = gMediaSpec2AudioMusic;
+ else if (!strcmp(ptr, ".audioBroadcast"))
+ retval = gMediaSpec2AudioBroadcast;
+ else if (!strcmp(ptr, ".audioBook"))
+ retval = gMediaSpec2AudioBook;
+ else
+ retval = gMediaSpec2Audio;
+ } else if (!strncmp(upnp_class, gUPnPVideoItem, gUPnPVideoItemLen)) {
+ ptr = upnp_class + gUPnPVideoItemLen;
+ if (!strcmp(ptr, ".movie"))
+ retval = gMediaSpec2VideoMovie;
+ else if (!strcmp(ptr, ".musicVideoClip"))
+ retval = gMediaSpec2VideoMusicClip;
+ else if (!strcmp(ptr, ".videoBroadcast"))
+ retval = gMediaSpec2VideoBroadcast;
+ else
+ retval = gMediaSpec2Video;
+ } else if (!strncmp(upnp_class, gUPnPImageItem, gUPnPImageItemLen)) {
+ ptr = upnp_class + gUPnPImageItemLen;
+ if (!strcmp(ptr, ".photo"))
+ retval = gMediaSpec2ImagePhoto;
+ else
+ retval = gMediaSpec2Image;
+ } else if (!strncmp(upnp_class, gUPnPPlaylistItem,
+ gUPnPPlaylistItemLen)) {
+ retval = gMediaSpec2Playlist;
+ } else if (!strncmp(upnp_class, gUPnPItem, gUPnPItemLen)) {
+ ptr = upnp_class + gUPnPItemLen;
+ if (!*ptr || *ptr == '.')
+ retval = gMediaSpec2Item;
+ }
+
+ return retval;
+}
+
+static GVariant *prv_props_get_dlna_managed_dict(GUPnPOCMFlags flags)
+{
+ GVariantBuilder builder;
+ gboolean managed;
+
+ g_variant_builder_init(&builder, G_VARIANT_TYPE("a{sb}"));
+
+ managed = (flags & GUPNP_OCM_FLAGS_UPLOAD);
+ g_variant_builder_add(&builder, "{sb}", "Upload", managed);
+
+ managed = (flags & GUPNP_OCM_FLAGS_CREATE_CONTAINER);
+ g_variant_builder_add(&builder, "{sb}", "CreateContainer", managed);
+
+ managed = (flags & GUPNP_OCM_FLAGS_DESTROYABLE);
+ g_variant_builder_add(&builder, "{sb}", "Delete", managed);
+
+ managed = (flags & GUPNP_OCM_FLAGS_UPLOAD_DESTROYABLE);
+ g_variant_builder_add(&builder, "{sb}", "UploadDelete", managed);
+
+ managed = (flags & GUPNP_OCM_FLAGS_CHANGE_METADATA);
+ g_variant_builder_add(&builder, "{sb}", "ChangeMeta", managed);
+
+ return g_variant_builder_end(&builder);
+}
+
+gboolean dls_props_add_object(GVariantBuilder *item_vb,
+ GUPnPDIDLLiteObject *object,
+ const char *root_path,
+ const gchar *parent_path,
+ dls_upnp_prop_mask filter_mask)
+{
+ gchar *path = NULL;
+ const char *id;
+ const char *title;
+ const char *creator;
+ const char *upnp_class;
+ const char *media_spec_type;
+ gboolean retval = FALSE;
+ gboolean rest;
+ GUPnPOCMFlags flags;
+ guint uint_val;
+
+ id = gupnp_didl_lite_object_get_id(object);
+ if (!id)
+ goto on_error;
+
+ upnp_class = gupnp_didl_lite_object_get_upnp_class(object);
+ media_spec_type = dls_props_upnp_class_to_media_spec(upnp_class);
+
+ if (!media_spec_type)
+ goto on_error;
+
+ title = gupnp_didl_lite_object_get_title(object);
+ creator = gupnp_didl_lite_object_get_creator(object);
+ rest = gupnp_didl_lite_object_get_restricted(object);
+ path = dls_path_from_id(root_path, id);
+
+ if (filter_mask & DLS_UPNP_MASK_PROP_DISPLAY_NAME)
+ prv_add_string_prop(item_vb, DLS_INTERFACE_PROP_DISPLAY_NAME,
+ title);
+
+ if (filter_mask & DLS_UPNP_MASK_PROP_CREATOR)
+ prv_add_string_prop(item_vb, DLS_INTERFACE_PROP_CREATOR,
+ creator);
+
+ if (filter_mask & DLS_UPNP_MASK_PROP_PATH)
+ prv_add_path_prop(item_vb, DLS_INTERFACE_PROP_PATH, path);
+
+ if (filter_mask & DLS_UPNP_MASK_PROP_PARENT)
+ prv_add_path_prop(item_vb, DLS_INTERFACE_PROP_PARENT,
+ parent_path);
+
+ if (filter_mask & DLS_UPNP_MASK_PROP_TYPE)
+ prv_add_string_prop(item_vb, DLS_INTERFACE_PROP_TYPE,
+ media_spec_type);
+
+ if (filter_mask & DLS_UPNP_MASK_PROP_RESTRICTED)
+ prv_add_bool_prop(item_vb, DLS_INTERFACE_PROP_RESTRICTED, rest);
+
+ if (filter_mask & DLS_UPNP_MASK_PROP_DLNA_MANAGED) {
+ flags = gupnp_didl_lite_object_get_dlna_managed(object);
+ prv_add_variant_prop(item_vb,
+ DLS_INTERFACE_PROP_DLNA_MANAGED,
+ prv_props_get_dlna_managed_dict(flags));
+ }
+
+ if (filter_mask & DLS_UPNP_MASK_PROP_OBJECT_UPDATE_ID) {
+ uint_val = gupnp_didl_lite_object_get_update_id(object);
+ prv_add_uint_prop(item_vb, DLS_INTERFACE_PROP_OBJECT_UPDATE_ID,
+ uint_val);
+ }
+
+ retval = TRUE;
+
+on_error:
+
+ g_free(path);
+
+ return retval;
+}
+
+void dls_props_add_container(GVariantBuilder *item_vb,
+ GUPnPDIDLLiteContainer *object,
+ dls_upnp_prop_mask filter_mask,
+ gboolean *have_child_count)
+{
+ int child_count;
+ gboolean searchable;
+ guint uint_val;
+
+ *have_child_count = FALSE;
+ if (filter_mask & DLS_UPNP_MASK_PROP_CHILD_COUNT) {
+ child_count = gupnp_didl_lite_container_get_child_count(object);
+ if (child_count >= 0) {
+ prv_add_uint_prop(item_vb,
+ DLS_INTERFACE_PROP_CHILD_COUNT,
+ (unsigned int) child_count);
+ *have_child_count = TRUE;
+ }
+ }
+
+ if (filter_mask & DLS_UPNP_MASK_PROP_SEARCHABLE) {
+ searchable = gupnp_didl_lite_container_get_searchable(object);
+ prv_add_bool_prop(item_vb, DLS_INTERFACE_PROP_SEARCHABLE,
+ searchable);
+ }
+
+ if (filter_mask & DLS_UPNP_MASK_PROP_CREATE_CLASSES)
+ prv_add_variant_prop(item_vb,
+ DLS_INTERFACE_PROP_CREATE_CLASSES,
+ prv_compute_create_classes(object));
+
+ if (filter_mask & DLS_UPNP_MASK_PROP_CONTAINER_UPDATE_ID) {
+ uint_val = gupnp_didl_lite_container_get_container_update_id(
+ object);
+ prv_add_uint_prop(item_vb,
+ DLS_INTERFACE_PROP_CONTAINER_UPDATE_ID,
+ uint_val);
+ }
+
+ if (filter_mask & DLS_UPNP_MASK_PROP_TOTAL_DELETED_CHILD_COUNT) {
+ uint_val =
+ gupnp_didl_lite_container_get_total_deleted_child_count(object);
+ prv_add_uint_prop(item_vb,
+ DLS_INTERFACE_PROP_TOTAL_DELETED_CHILD_COUNT,
+ uint_val);
+ }
+}
+
+static GVariant *prv_compute_resources(GUPnPDIDLLiteObject *object,
+ dls_upnp_prop_mask filter_mask)
+{
+ GUPnPDIDLLiteResource *res = NULL;
+ GList *resources;
+ GList *ptr;
+ GVariantBuilder *res_array_vb;
+ GVariantBuilder *res_vb;
+ const char *str_val;
+ GVariant *retval;
+
+ res_array_vb = g_variant_builder_new(G_VARIANT_TYPE("aa{sv}"));
+
+ resources = gupnp_didl_lite_object_get_resources(object);
+ ptr = resources;
+
+ while (ptr) {
+ res = ptr->data;
+ res_vb = g_variant_builder_new(G_VARIANT_TYPE("a{sv}"));
+ if (filter_mask & DLS_UPNP_MASK_PROP_URL) {
+ str_val = gupnp_didl_lite_resource_get_uri(res);
+ if (str_val)
+ prv_add_string_prop(res_vb,
+ DLS_INTERFACE_PROP_URL,
+ str_val);
+ }
+ prv_parse_resources(res_vb, res, filter_mask);
+ g_variant_builder_add(res_array_vb, "@a{sv}",
+ g_variant_builder_end(res_vb));
+ g_variant_builder_unref(res_vb);
+ g_object_unref(ptr->data);
+ ptr = g_list_next(ptr);
+ }
+ retval = g_variant_builder_end(res_array_vb);
+ g_variant_builder_unref(res_array_vb);
+
+ g_list_free(resources);
+
+ return retval;
+}
+
+static void prv_add_resources(GVariantBuilder *item_vb,
+ GUPnPDIDLLiteObject *object,
+ dls_upnp_prop_mask filter_mask)
+{
+ GVariant *val;
+
+ val = prv_compute_resources(object, filter_mask);
+ g_variant_builder_add(item_vb, "{sv}", DLS_INTERFACE_PROP_RESOURCES,
+ val);
+}
+
+void dls_props_add_item(GVariantBuilder *item_vb,
+ GUPnPDIDLLiteObject *object,
+ const gchar *root_path,
+ dls_upnp_prop_mask filter_mask,
+ const gchar *protocol_info)
+{
+ int track_number;
+ GUPnPDIDLLiteResource *res;
+ const char *str_val;
+ char *path;
+ GList *list;
+
+ if (filter_mask & DLS_UPNP_MASK_PROP_ARTIST)
+ prv_add_string_prop(item_vb, DLS_INTERFACE_PROP_ARTIST,
+ gupnp_didl_lite_object_get_artist(object));
+
+ if (filter_mask & DLS_UPNP_MASK_PROP_ARTISTS) {
+ list = gupnp_didl_lite_object_get_artists(object);
+ prv_add_variant_prop(item_vb, DLS_INTERFACE_PROP_ARTISTS,
+ prv_get_artists_prop(list));
+ g_list_free_full(list, g_object_unref);
+ }
+
+ if (filter_mask & DLS_UPNP_MASK_PROP_ALBUM)
+ prv_add_string_prop(item_vb, DLS_INTERFACE_PROP_ALBUM,
+ gupnp_didl_lite_object_get_album(object));
+
+ if (filter_mask & DLS_UPNP_MASK_PROP_DATE)
+ prv_add_string_prop(item_vb, DLS_INTERFACE_PROP_DATE,
+ gupnp_didl_lite_object_get_date(object));
+
+ if (filter_mask & DLS_UPNP_MASK_PROP_GENRE)
+ prv_add_string_prop(item_vb, DLS_INTERFACE_PROP_GENRE,
+ gupnp_didl_lite_object_get_genre(object));
+
+ if (filter_mask & DLS_UPNP_MASK_PROP_TRACK_NUMBER) {
+ track_number = gupnp_didl_lite_object_get_track_number(object);
+ if (track_number >= 0)
+ prv_add_int_prop(item_vb,
+ DLS_INTERFACE_PROP_TRACK_NUMBER,
+ track_number);
+ }
+
+ if (filter_mask & DLS_UPNP_MASK_PROP_ALBUM_ART_URL)
+ prv_add_string_prop(item_vb, DLS_INTERFACE_PROP_ALBUM_ART_URL,
+ gupnp_didl_lite_object_get_album_art(
+ object));
+
+ if (filter_mask & DLS_UPNP_MASK_PROP_REFPATH) {
+ str_val = gupnp_didl_lite_item_get_ref_id(
+ GUPNP_DIDL_LITE_ITEM(object));
+ if (str_val != NULL) {
+ path = dls_path_from_id(root_path, str_val);
+ prv_add_path_prop(item_vb, DLS_INTERFACE_PROP_REFPATH,
+ path);
+ g_free(path);
+ }
+ }
+
+ res = prv_get_matching_resource(object, protocol_info);
+ if (res) {
+ if (filter_mask & DLS_UPNP_MASK_PROP_URLS) {
+ str_val = gupnp_didl_lite_resource_get_uri(res);
+ if (str_val)
+ prv_add_strv_prop(item_vb,
+ DLS_INTERFACE_PROP_URLS,
+ &str_val, 1);
+ }
+ prv_parse_resources(item_vb, res, filter_mask);
+ g_object_unref(res);
+ }
+
+ if (filter_mask & DLS_UPNP_MASK_PROP_RESOURCES)
+ prv_add_resources(item_vb, object, filter_mask);
+}
+
+void dls_props_add_resource(GVariantBuilder *item_vb,
+ GUPnPDIDLLiteObject *object,
+ dls_upnp_prop_mask filter_mask,
+ const gchar *protocol_info)
+{
+ GUPnPDIDLLiteResource *res;
+ const char *str_val;
+
+ res = prv_get_matching_resource(object, protocol_info);
+ if (res) {
+ if (filter_mask & DLS_UPNP_MASK_PROP_URL) {
+ str_val = gupnp_didl_lite_resource_get_uri(res);
+ if (str_val)
+ prv_add_string_prop(item_vb,
+ DLS_INTERFACE_PROP_URL,
+ str_val);
+ }
+ prv_parse_resources(item_vb, res, filter_mask);
+ g_object_unref(res);
+ }
+}
+
+
+static GVariant *prv_get_resource_property(const gchar *prop,
+ GUPnPDIDLLiteResource *res)
+{
+ int int_val;
+ gint64 int64_val;
+ const char *str_val;
+ GVariant *retval = NULL;
+ GUPnPProtocolInfo *protocol_info;
+
+ if (!strcmp(prop, DLS_INTERFACE_PROP_DLNA_PROFILE)) {
+ protocol_info = gupnp_didl_lite_resource_get_protocol_info(res);
+ if (!protocol_info)
+ goto on_error;
+ str_val = gupnp_protocol_info_get_dlna_profile(protocol_info);
+ if (!str_val)
+ goto on_error;
+ retval = g_variant_ref_sink(g_variant_new_string(str_val));
+ } else if (!strcmp(prop, DLS_INTERFACE_PROP_MIME_TYPE)) {
+ protocol_info = gupnp_didl_lite_resource_get_protocol_info(res);
+ if (!protocol_info)
+ goto on_error;
+ str_val = gupnp_protocol_info_get_mime_type(protocol_info);
+ if (!str_val)
+ goto on_error;
+ retval = g_variant_ref_sink(g_variant_new_string(str_val));
+ } else if (!strcmp(prop, DLS_INTERFACE_PROP_SIZE)) {
+ int64_val = gupnp_didl_lite_resource_get_size64(res);
+ if (int64_val == -1)
+ goto on_error;
+ retval = g_variant_ref_sink(g_variant_new_int64(int64_val));
+ } else if (!strcmp(prop, DLS_INTERFACE_PROP_DURATION)) {
+ int_val = (int) gupnp_didl_lite_resource_get_duration(res);
+ if (int_val == -1)
+ goto on_error;
+ retval = g_variant_ref_sink(g_variant_new_int32(int_val));
+ } else if (!strcmp(prop, DLS_INTERFACE_PROP_BITRATE)) {
+ int_val = gupnp_didl_lite_resource_get_bitrate(res);
+ if (int_val == -1)
+ goto on_error;
+ retval = g_variant_ref_sink(g_variant_new_int32(int_val));
+ } else if (!strcmp(prop, DLS_INTERFACE_PROP_SAMPLE_RATE)) {
+ int_val = gupnp_didl_lite_resource_get_sample_freq(res);
+ if (int_val == -1)
+ goto on_error;
+ retval = g_variant_ref_sink(g_variant_new_int32(int_val));
+ } else if (!strcmp(prop, DLS_INTERFACE_PROP_BITS_PER_SAMPLE)) {
+ int_val = gupnp_didl_lite_resource_get_bits_per_sample(res);
+ if (int_val == -1)
+ goto on_error;
+ retval = g_variant_ref_sink(g_variant_new_int32(int_val));
+ } else if (!strcmp(prop, DLS_INTERFACE_PROP_WIDTH)) {
+ int_val = (int) gupnp_didl_lite_resource_get_width(res);
+ if (int_val == -1)
+ goto on_error;
+ retval = g_variant_ref_sink(g_variant_new_int32(int_val));
+ } else if (!strcmp(prop, DLS_INTERFACE_PROP_HEIGHT)) {
+ int_val = (int) gupnp_didl_lite_resource_get_height(res);
+ if (int_val == -1)
+ goto on_error;
+ retval = g_variant_ref_sink(g_variant_new_int32(int_val));
+ } else if (!strcmp(prop, DLS_INTERFACE_PROP_COLOR_DEPTH)) {
+ int_val = (int) gupnp_didl_lite_resource_get_color_depth(res);
+ if (int_val == -1)
+ goto on_error;
+ retval = g_variant_ref_sink(g_variant_new_int32(int_val));
+ } else if (!strcmp(prop, DLS_INTERFACE_PROP_URLS)) {
+ str_val = gupnp_didl_lite_resource_get_uri(res);
+ if (str_val)
+ retval = g_variant_new_strv(&str_val, 1);
+ }
+
+on_error:
+
+ return retval;
+}
+
+GVariant *dls_props_get_object_prop(const gchar *prop, const gchar *root_path,
+ GUPnPDIDLLiteObject *object)
+{
+ const char *id;
+ gchar *path;
+ const char *upnp_class;
+ const char *media_spec_type;
+ const char *title;
+ gboolean rest;
+ GVariant *retval = NULL;
+ GUPnPOCMFlags dlna_managed;
+ guint uint_val;
+
+ if (!strcmp(prop, DLS_INTERFACE_PROP_PARENT)) {
+ id = gupnp_didl_lite_object_get_parent_id(object);
+ if (!id || !strcmp(id, "-1")) {
+ DLEYNA_LOG_DEBUG("Prop %s = %s", prop, root_path);
+
+ retval = g_variant_ref_sink(g_variant_new_string(
+ root_path));
+ } else {
+ path = dls_path_from_id(root_path, id);
+
+ DLEYNA_LOG_DEBUG("Prop %s = %s", prop, path);
+
+ retval = g_variant_ref_sink(g_variant_new_string(
+ path));
+ g_free(path);
+ }
+ } else if (!strcmp(prop, DLS_INTERFACE_PROP_PATH)) {
+ id = gupnp_didl_lite_object_get_id(object);
+ if (!id)
+ goto on_error;
+
+ path = dls_path_from_id(root_path, id);
+
+ DLEYNA_LOG_DEBUG("Prop %s = %s", prop, path);
+
+ retval = g_variant_ref_sink(g_variant_new_string(path));
+ g_free(path);
+ } else if (!strcmp(prop, DLS_INTERFACE_PROP_TYPE)) {
+ upnp_class = gupnp_didl_lite_object_get_upnp_class(object);
+ media_spec_type =
+ dls_props_upnp_class_to_media_spec(upnp_class);
+ if (!media_spec_type)
+ goto on_error;
+
+ DLEYNA_LOG_DEBUG("Prop %s = %s", prop, media_spec_type);
+
+ retval = g_variant_ref_sink(g_variant_new_string(
+ media_spec_type));
+ } else if (!strcmp(prop, DLS_INTERFACE_PROP_DISPLAY_NAME)) {
+ title = gupnp_didl_lite_object_get_title(object);
+ if (!title)
+ goto on_error;
+
+ DLEYNA_LOG_DEBUG("Prop %s = %s", prop, title);
+
+ retval = g_variant_ref_sink(g_variant_new_string(title));
+ } else if (!strcmp(prop, DLS_INTERFACE_PROP_CREATOR)) {
+ title = gupnp_didl_lite_object_get_creator(object);
+ if (!title)
+ goto on_error;
+
+ DLEYNA_LOG_DEBUG("Prop %s = %s", prop, title);
+
+ retval = g_variant_ref_sink(g_variant_new_string(title));
+ } else if (!strcmp(prop, DLS_INTERFACE_PROP_RESTRICTED)) {
+ rest = gupnp_didl_lite_object_get_restricted(object);
+
+ DLEYNA_LOG_DEBUG("Prop %s = %d", prop, rest);
+
+ retval = g_variant_ref_sink(g_variant_new_boolean(rest));
+ } else if (!strcmp(prop, DLS_INTERFACE_PROP_DLNA_MANAGED)) {
+ dlna_managed = gupnp_didl_lite_object_get_dlna_managed(object);
+
+ DLEYNA_LOG_DEBUG("Prop %s = %0x", prop, dlna_managed);
+
+ retval = g_variant_ref_sink(
+ prv_props_get_dlna_managed_dict(dlna_managed));
+ } else if (!strcmp(prop, DLS_INTERFACE_PROP_OBJECT_UPDATE_ID)) {
+ uint_val = gupnp_didl_lite_object_get_update_id(object);
+
+ DLEYNA_LOG_DEBUG("Prop %s = %u", prop, uint_val);
+
+ retval = g_variant_ref_sink(g_variant_new_uint32(uint_val));
+ }
+
+on_error:
+
+ return retval;
+}
+
+GVariant *dls_props_get_item_prop(const gchar *prop, const gchar *root_path,
+ GUPnPDIDLLiteObject *object,
+ const gchar *protocol_info)
+{
+ const gchar *str;
+ gchar *path;
+ gint track_number;
+ GUPnPDIDLLiteResource *res;
+ GVariant *retval = NULL;
+ GList *list;
+
+ if (GUPNP_IS_DIDL_LITE_CONTAINER(object))
+ goto on_error;
+
+ if (!strcmp(prop, DLS_INTERFACE_PROP_ARTIST)) {
+ str = gupnp_didl_lite_object_get_artist(object);
+ if (!str)
+ goto on_error;
+
+ DLEYNA_LOG_DEBUG("Prop %s = %s", prop, str);
+
+ retval = g_variant_ref_sink(g_variant_new_string(str));
+ } else if (!strcmp(prop, DLS_INTERFACE_PROP_ARTISTS)) {
+ list = gupnp_didl_lite_object_get_artists(object);
+ if (!list)
+ goto on_error;
+
+ retval = g_variant_ref_sink(prv_get_artists_prop(list));
+ g_list_free_full(list, g_object_unref);
+
+#if DLEYNA_LOG_LEVEL & DLEYNA_LOG_LEVEL_DEBUG
+ path = g_variant_print(retval, FALSE);
+ DLEYNA_LOG_DEBUG("Prop %s = %s", prop, path);
+ g_free(path);
+#endif
+ } else if (!strcmp(prop, DLS_INTERFACE_PROP_ALBUM)) {
+ str = gupnp_didl_lite_object_get_album(object);
+ if (!str)
+ goto on_error;
+
+ DLEYNA_LOG_DEBUG("Prop %s = %s", prop, str);
+
+ retval = g_variant_ref_sink(g_variant_new_string(str));
+ } else if (!strcmp(prop, DLS_INTERFACE_PROP_DATE)) {
+ str = gupnp_didl_lite_object_get_date(object);
+ if (!str)
+ goto on_error;
+
+ DLEYNA_LOG_DEBUG("Prop %s = %s", prop, str);
+
+ retval = g_variant_ref_sink(g_variant_new_string(str));
+ } else if (!strcmp(prop, DLS_INTERFACE_PROP_GENRE)) {
+ str = gupnp_didl_lite_object_get_genre(object);
+ if (!str)
+ goto on_error;
+
+ DLEYNA_LOG_DEBUG("Prop %s = %s", prop, str);
+
+ retval = g_variant_ref_sink(g_variant_new_string(str));
+ } else if (!strcmp(prop, DLS_INTERFACE_PROP_TRACK_NUMBER)) {
+ track_number = gupnp_didl_lite_object_get_track_number(object);
+ if (track_number < 0)
+ goto on_error;
+
+ DLEYNA_LOG_DEBUG("Prop %s = %d", prop, track_number);
+
+ retval = g_variant_ref_sink(
+ g_variant_new_int32(track_number));
+ } else if (!strcmp(prop, DLS_INTERFACE_PROP_ALBUM_ART_URL)) {
+ str = gupnp_didl_lite_object_get_album_art(object);
+ if (!str)
+ goto on_error;
+
+ DLEYNA_LOG_DEBUG("Prop %s = %s", prop, str);
+
+ retval = g_variant_ref_sink(g_variant_new_string(str));
+ } else if (!strcmp(prop, DLS_INTERFACE_PROP_REFPATH)) {
+ str = gupnp_didl_lite_item_get_ref_id(
+ GUPNP_DIDL_LITE_ITEM(object));
+ if (!str)
+ goto on_error;
+
+ DLEYNA_LOG_DEBUG("Prop %s = %s", prop, str);
+
+ path = dls_path_from_id(root_path, str);
+ retval = g_variant_ref_sink(g_variant_new_string(path));
+ g_free(path);
+ } else if (!strcmp(prop, DLS_INTERFACE_PROP_RESOURCES)) {
+ retval = g_variant_ref_sink(
+ prv_compute_resources(object, DLS_UPNP_MASK_ALL_PROPS));
+ } else {
+ res = prv_get_matching_resource(object, protocol_info);
+ if (!res)
+ goto on_error;
+
+ retval = prv_get_resource_property(prop, res);
+
+ g_object_unref(res);
+ }
+
+on_error:
+
+ return retval;
+}
+
+GVariant *dls_props_get_container_prop(const gchar *prop,
+ GUPnPDIDLLiteObject *object)
+{
+ gint child_count;
+ gboolean searchable;
+ GUPnPDIDLLiteContainer *container;
+ GVariant *retval = NULL;
+ guint uint_val;
+#if DLEYNA_LOG_LEVEL & DLEYNA_LOG_LEVEL_DEBUG
+ gchar *create_classes;
+#endif
+ if (!GUPNP_IS_DIDL_LITE_CONTAINER(object))
+ goto on_error;
+
+ container = (GUPnPDIDLLiteContainer *)object;
+ if (!strcmp(prop, DLS_INTERFACE_PROP_CHILD_COUNT)) {
+ child_count =
+ gupnp_didl_lite_container_get_child_count(container);
+
+ DLEYNA_LOG_DEBUG("Prop %s = %d", prop, child_count);
+
+ if (child_count >= 0) {
+ retval = g_variant_new_uint32((guint) child_count);
+ retval = g_variant_ref_sink(retval);
+ }
+ } else if (!strcmp(prop, DLS_INTERFACE_PROP_SEARCHABLE)) {
+ searchable =
+ gupnp_didl_lite_container_get_searchable(container);
+
+ DLEYNA_LOG_DEBUG("Prop %s = %d", prop, searchable);
+
+ retval = g_variant_ref_sink(
+ g_variant_new_boolean(searchable));
+ } else if (!strcmp(prop, DLS_INTERFACE_PROP_CREATE_CLASSES)) {
+ retval = g_variant_ref_sink(
+ prv_compute_create_classes(container));
+#if DLEYNA_LOG_LEVEL & DLEYNA_LOG_LEVEL_DEBUG
+ create_classes = g_variant_print(retval, FALSE);
+ DLEYNA_LOG_DEBUG("Prop %s = %s", prop, create_classes);
+ g_free(create_classes);
+#endif
+ } else if (!strcmp(prop, DLS_INTERFACE_PROP_CONTAINER_UPDATE_ID)) {
+ uint_val = gupnp_didl_lite_container_get_container_update_id(
+ container);
+
+ DLEYNA_LOG_DEBUG("Prop %s = %u", prop, uint_val);
+
+ retval = g_variant_ref_sink(g_variant_new_uint32(uint_val));
+ } else if (!strcmp(prop,
+ DLS_INTERFACE_PROP_TOTAL_DELETED_CHILD_COUNT)) {
+ uint_val =
+ gupnp_didl_lite_container_get_total_deleted_child_count(
+ container);
+
+ DLEYNA_LOG_DEBUG("Prop %s = %u", prop, uint_val);
+
+ retval = g_variant_ref_sink(g_variant_new_uint32(uint_val));
+ }
+
+on_error:
+
+ return retval;
+}
diff --git a/libdleyna/server/props.h b/libdleyna/server/props.h
new file mode 100644
index 0000000..610fcea
--- /dev/null
+++ b/libdleyna/server/props.h
@@ -0,0 +1,134 @@
+/*
+ * dLeyna
+ *
+ * Copyright (C) 2012-2013 Intel Corporation. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms and conditions of the GNU Lesser General Public License,
+ * version 2.1, as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License
+ * for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with this program; if not, write to the Free Software Foundation, Inc.,
+ * 51 Franklin St - Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Mark Ryan <mark.d.ryan@intel.com>
+ *
+ */
+
+#ifndef DLS_PROPS_H__
+#define DLS_PROPS_H__
+
+#include <libgupnp-av/gupnp-av.h>
+#include "async.h"
+
+#define DLS_UPNP_MASK_PROP_PARENT (1LL << 0)
+#define DLS_UPNP_MASK_PROP_TYPE (1LL << 1)
+#define DLS_UPNP_MASK_PROP_PATH (1LL << 2)
+#define DLS_UPNP_MASK_PROP_DISPLAY_NAME (1LL << 3)
+#define DLS_UPNP_MASK_PROP_CHILD_COUNT (1LL << 4)
+#define DLS_UPNP_MASK_PROP_SEARCHABLE (1LL << 5)
+#define DLS_UPNP_MASK_PROP_URLS (1LL << 6)
+#define DLS_UPNP_MASK_PROP_MIME_TYPE (1LL << 7)
+#define DLS_UPNP_MASK_PROP_ARTIST (1LL << 8)
+#define DLS_UPNP_MASK_PROP_ALBUM (1LL << 9)
+#define DLS_UPNP_MASK_PROP_DATE (1LL << 10)
+#define DLS_UPNP_MASK_PROP_GENRE (1LL << 11)
+#define DLS_UPNP_MASK_PROP_DLNA_PROFILE (1LL << 12)
+#define DLS_UPNP_MASK_PROP_TRACK_NUMBER (1LL << 13)
+#define DLS_UPNP_MASK_PROP_SIZE (1LL << 14)
+#define DLS_UPNP_MASK_PROP_DURATION (1LL << 15)
+#define DLS_UPNP_MASK_PROP_BITRATE (1LL << 16)
+#define DLS_UPNP_MASK_PROP_SAMPLE_RATE (1LL << 17)
+#define DLS_UPNP_MASK_PROP_BITS_PER_SAMPLE (1LL << 18)
+#define DLS_UPNP_MASK_PROP_WIDTH (1LL << 19)
+#define DLS_UPNP_MASK_PROP_HEIGHT (1LL << 20)
+#define DLS_UPNP_MASK_PROP_COLOR_DEPTH (1LL << 21)
+#define DLS_UPNP_MASK_PROP_ALBUM_ART_URL (1LL << 22)
+#define DLS_UPNP_MASK_PROP_RESOURCES (1LL << 23)
+#define DLS_UPNP_MASK_PROP_URL (1LL << 24)
+#define DLS_UPNP_MASK_PROP_REFPATH (1LL << 25)
+#define DLS_UPNP_MASK_PROP_RESTRICTED (1LL << 26)
+#define DLS_UPNP_MASK_PROP_DLNA_MANAGED (1LL << 27)
+#define DLS_UPNP_MASK_PROP_CREATOR (1LL << 28)
+#define DLS_UPNP_MASK_PROP_ARTISTS (1LL << 29)
+#define DLS_UPNP_MASK_PROP_CREATE_CLASSES (1LL << 30)
+#define DLS_UPNP_MASK_PROP_OBJECT_UPDATE_ID (1LL << 31)
+#define DLS_UPNP_MASK_PROP_UPDATE_COUNT (1LL << 32)
+#define DLS_UPNP_MASK_PROP_CONTAINER_UPDATE_ID (1LL << 33)
+#define DLS_UPNP_MASK_PROP_TOTAL_DELETED_CHILD_COUNT (1LL << 34)
+
+#define DLS_UPNP_MASK_ALL_PROPS 0xffffffffffffffff
+
+typedef struct dls_prop_map_t_ dls_prop_map_t;
+struct dls_prop_map_t_ {
+ const gchar *upnp_prop_name;
+ dls_upnp_prop_mask type;
+ gboolean filter;
+ gboolean searchable;
+ gboolean updateable;
+};
+
+void dls_prop_maps_new(GHashTable **property_map, GHashTable **filter_map);
+
+dls_upnp_prop_mask dls_props_parse_filter(GHashTable *filter_map,
+ GVariant *filter,
+ gchar **upnp_filter);
+
+gboolean dls_props_parse_update_filter(GHashTable *filter_map,
+ GVariant *to_add_update,
+ GVariant *to_delete,
+ dls_upnp_prop_mask *mask,
+ gchar **upnp_filter);
+
+void dls_props_add_device(GUPnPDeviceInfo *proxy,
+ const dls_device_t *device,
+ GVariantBuilder *vb);
+
+GVariant *dls_props_get_device_prop(GUPnPDeviceInfo *proxy,
+ const dls_device_t *device,
+ const gchar *prop);
+
+gboolean dls_props_add_object(GVariantBuilder *item_vb,
+ GUPnPDIDLLiteObject *object,
+ const char *root_path,
+ const gchar *parent_path,
+ dls_upnp_prop_mask filter_mask);
+
+GVariant *dls_props_get_object_prop(const gchar *prop, const gchar *root_path,
+ GUPnPDIDLLiteObject *object);
+
+void dls_props_add_container(GVariantBuilder *item_vb,
+ GUPnPDIDLLiteContainer *object,
+ dls_upnp_prop_mask filter_mask,
+ gboolean *have_child_count);
+
+void dls_props_add_child_count(GVariantBuilder *item_vb, gint value);
+
+GVariant *dls_props_get_container_prop(const gchar *prop,
+ GUPnPDIDLLiteObject *object);
+
+void dls_props_add_resource(GVariantBuilder *item_vb,
+ GUPnPDIDLLiteObject *object,
+ dls_upnp_prop_mask filter_mask,
+ const gchar *protocol_info);
+
+void dls_props_add_item(GVariantBuilder *item_vb,
+ GUPnPDIDLLiteObject *object,
+ const gchar *root_path,
+ dls_upnp_prop_mask filter_mask,
+ const gchar *protocol_info);
+
+GVariant *dls_props_get_item_prop(const gchar *prop, const gchar *root_path,
+ GUPnPDIDLLiteObject *object,
+ const gchar *protocol_info);
+
+const gchar *dls_props_media_spec_to_upnp_class(const gchar *m2spec_class);
+
+const gchar *dls_props_upnp_class_to_media_spec(const gchar *upnp_class);
+
+#endif /* DLS_PROPS_H__ */
diff --git a/libdleyna/server/search.c b/libdleyna/server/search.c
new file mode 100644
index 0000000..264654e
--- /dev/null
+++ b/libdleyna/server/search.c
@@ -0,0 +1,148 @@
+/*
+ * dLeyna
+ *
+ * Copyright (C) 2012-2013 Intel Corporation. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms and conditions of the GNU Lesser General Public License,
+ * version 2.1, as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License
+ * for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with this program; if not, write to the Free Software Foundation, Inc.,
+ * 51 Franklin St - Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Mark Ryan <mark.d.ryan@intel.com>
+ *
+ */
+
+#include <string.h>
+
+#include "interface.h"
+#include "path.h"
+#include "props.h"
+#include "search.h"
+
+gchar *dls_search_translate_search_string(GHashTable *filter_map,
+ const gchar *search_string)
+{
+ GRegex *reg;
+ gchar *retval = NULL;
+ GMatchInfo *match_info = NULL;
+ gchar *prop = NULL;
+ gchar *op = NULL;
+ gchar *value = NULL;
+ const gchar *translated_value;
+ dls_prop_map_t *prop_map;
+ GString *str;
+ gint start_pos;
+ gint end_pos;
+ gint old_end_pos = 0;
+ unsigned int skipped;
+ unsigned int search_string_len = strlen(search_string);
+ gchar *root_path;
+ gchar *id;
+
+ reg = g_regex_new("(\\w+)\\s+(=|!=|<|<=|>|>|contains|doesNotContain|"\
+ "derivedfrom|exists)\\s+"\
+ "(\"[^\"]*\"|true|false)",
+ 0, 0, NULL);
+ str = g_string_new("");
+
+ g_regex_match(reg, search_string, 0, &match_info);
+ while (g_match_info_matches(match_info)) {
+ prop = g_match_info_fetch(match_info, 1);
+ if (!prop)
+ goto on_error;
+
+ op = g_match_info_fetch(match_info, 2);
+ if (!op)
+ goto on_error;
+
+ value = g_match_info_fetch(match_info, 3);
+ if (!value)
+ goto on_error;
+
+ /* Handle special cases where we need to translate
+ value as well as property name */
+
+ if (!strcmp(prop, DLS_INTERFACE_PROP_TYPE)) {
+ /* Skip the quotes */
+
+ value[strlen(value) - 1] = 0;
+ translated_value = dls_props_media_spec_to_upnp_class(
+ value + 1);
+ if (!translated_value)
+ goto on_error;
+ g_free(value);
+ value = g_strdup_printf("\"%s\"", translated_value);
+ } else if (!strcmp(prop, DLS_INTERFACE_PROP_PARENT) ||
+ !strcmp(prop, DLS_INTERFACE_PROP_PATH)) {
+ value[strlen(value) - 1] = 0;
+ if (!dls_path_get_path_and_id(value + 1, &root_path,
+ &id, NULL))
+ goto on_error;
+ g_free(root_path);
+ g_free(value);
+ value = g_strdup_printf("\"%s\"", id);
+ g_free(id);
+ }
+
+ prop_map = g_hash_table_lookup(filter_map, prop);
+ if (!prop_map)
+ goto on_error;
+
+ if (!prop_map->searchable)
+ goto on_error;
+
+ if (!g_match_info_fetch_pos(match_info, 0, &start_pos,
+ &end_pos))
+ goto on_error;
+
+ skipped = start_pos - old_end_pos;
+ if (skipped > 0)
+ g_string_append_len(str, &search_string[old_end_pos],
+ skipped);
+ g_string_append_printf(str, "%s %s %s",
+ prop_map->upnp_prop_name, op, value);
+ old_end_pos = end_pos;
+
+ g_free(value);
+ g_free(prop);
+ g_free(op);
+
+ value = NULL;
+ prop = NULL;
+ op = NULL;
+
+ g_match_info_next(match_info, NULL);
+ }
+
+ skipped = search_string_len - old_end_pos;
+ if (skipped > 0)
+ g_string_append_len(str, &search_string[old_end_pos],
+ skipped);
+
+ retval = g_string_free(str, FALSE);
+ str = NULL;
+
+on_error:
+
+ g_free(value);
+ g_free(prop);
+ g_free(op);
+
+ if (match_info)
+ g_match_info_free(match_info);
+
+ if (str)
+ g_string_free(str, TRUE);
+
+ g_regex_unref(reg);
+
+ return retval;
+}
diff --git a/libdleyna/server/search.h b/libdleyna/server/search.h
new file mode 100644
index 0000000..150a764
--- /dev/null
+++ b/libdleyna/server/search.h
@@ -0,0 +1,31 @@
+/*
+ * dLeyna
+ *
+ * Copyright (C) 2012-2013 Intel Corporation. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms and conditions of the GNU Lesser General Public License,
+ * version 2.1, as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License
+ * for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with this program; if not, write to the Free Software Foundation, Inc.,
+ * 51 Franklin St - Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Mark Ryan <mark.d.ryan@intel.com>
+ *
+ */
+
+#ifndef DLS_SEARCH_H__
+#define DLS_SEARCH_H__
+
+#include <glib.h>
+
+gchar *dls_search_translate_search_string(GHashTable *filter_map,
+ const gchar *search_string);
+
+#endif /* DLS_PROPS_H__ */
diff --git a/libdleyna/server/server.c b/libdleyna/server/server.c
new file mode 100644
index 0000000..6cfa036
--- /dev/null
+++ b/libdleyna/server/server.c
@@ -0,0 +1,1199 @@
+/*
+ * dLeyna
+ *
+ * Copyright (C) 2013 Intel Corporation. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms and conditions of the GNU Lesser General Public License,
+ * version 2.1, as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License
+ * for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with this program; if not, write to the Free Software Foundation, Inc.,
+ * 51 Franklin St - Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Mark Ryan <mark.d.ryan@intel.com>
+ * Regis Merlino <regis.merlino@intel.com>
+ *
+ */
+
+#include <glib.h>
+#include <string.h>
+
+#include <libdleyna/core/connector.h>
+#include <libdleyna/core/control-point.h>
+#include <libdleyna/core/error.h>
+#include <libdleyna/core/log.h>
+#include <libdleyna/core/task-processor.h>
+
+#include "async.h"
+#include "client.h"
+#include "control-point-server.h"
+#include "device.h"
+#include "interface.h"
+#include "path.h"
+#include "server.h"
+#include "upnp.h"
+
+#ifdef UA_PREFIX
+ #define DLS_PRG_NAME UA_PREFIX " dLeyna/" VERSION
+#else
+ #define DLS_PRG_NAME "dLeyna/" VERSION
+#endif
+
+
+typedef struct dls_server_context_t_ dls_server_context_t;
+struct dls_server_context_t_ {
+ dleyna_connector_id_t connection;
+ dleyna_task_processor_t *processor;
+ const dleyna_connector_t *connector;
+ dleyna_settings_t *settings;
+ guint dls_id;
+ GHashTable *watchers;
+ dls_upnp_t *upnp;
+};
+
+static dls_server_context_t g_context;
+
+static const gchar g_root_introspection[] =
+ "<node>"
+ " <interface name='"DLEYNA_SERVER_INTERFACE_MANAGER"'>"
+ " <method name='"DLS_INTERFACE_GET_VERSION"'>"
+ " <arg type='s' name='"DLS_INTERFACE_VERSION"'"
+ " direction='out'/>"
+ " </method>"
+ " <method name='"DLS_INTERFACE_RELEASE"'>"
+ " </method>"
+ " <method name='"DLS_INTERFACE_GET_SERVERS"'>"
+ " <arg type='ao' name='"DLS_INTERFACE_SERVERS"'"
+ " direction='out'/>"
+ " </method>"
+ " <method name='"DLS_INTERFACE_SET_PROTOCOL_INFO"'>"
+ " <arg type='s' name='"DLS_INTERFACE_PROTOCOL_INFO"'"
+ " direction='in'/>"
+ " </method>"
+ " <method name='"DLS_INTERFACE_PREFER_LOCAL_ADDRESSES"'>"
+ " <arg type='b' name='"DLS_INTERFACE_PREFER"'"
+ " direction='in'/>"
+ " </method>"
+ " <signal name='"DLS_INTERFACE_FOUND_SERVER"'>"
+ " <arg type='o' name='"DLS_INTERFACE_PATH"'/>"
+ " </signal>"
+ " <signal name='"DLS_INTERFACE_LOST_SERVER"'>"
+ " <arg type='o' name='"DLS_INTERFACE_PATH"'/>"
+ " </signal>"
+ " </interface>"
+ "</node>";
+
+static const gchar g_server_introspection[] =
+ "<node>"
+ " <interface name='"DLS_INTERFACE_PROPERTIES"'>"
+ " <method name='"DLS_INTERFACE_GET"'>"
+ " <arg type='s' name='"DLS_INTERFACE_INTERFACE_NAME"'"
+ " direction='in'/>"
+ " <arg type='s' name='"DLS_INTERFACE_PROPERTY_NAME"'"
+ " direction='in'/>"
+ " <arg type='v' name='"DLS_INTERFACE_VALUE"'"
+ " direction='out'/>"
+ " </method>"
+ " <method name='"DLS_INTERFACE_GET_ALL"'>"
+ " <arg type='s' name='"DLS_INTERFACE_INTERFACE_NAME"'"
+ " direction='in'/>"
+ " <arg type='a{sv}' name='"DLS_INTERFACE_PROPERTIES_VALUE"'"
+ " direction='out'/>"
+ " </method>"
+ " <signal name='"DLS_INTERFACE_PROPERTIES_CHANGED"'>"
+ " <arg type='s' name='"DLS_INTERFACE_INTERFACE_NAME"'/>"
+ " <arg type='a{sv}' name='"DLS_INTERFACE_CHANGED_PROPERTIES"'/>"
+ " <arg type='as' name='"
+ DLS_INTERFACE_INVALIDATED_PROPERTIES"'/>"
+ " </signal>"
+ " </interface>"
+ " <interface name='"DLS_INTERFACE_MEDIA_OBJECT"'>"
+ " <property type='o' name='"DLS_INTERFACE_PROP_PARENT"'"
+ " access='read'/>"
+ " <property type='s' name='"DLS_INTERFACE_PROP_TYPE"'"
+ " access='read'/>"
+ " <property type='o' name='"DLS_INTERFACE_PROP_PATH"'"
+ " access='read'/>"
+ " <property type='s' name='"DLS_INTERFACE_PROP_DISPLAY_NAME"'"
+ " access='read'/>"
+ " <property type='s' name='"DLS_INTERFACE_PROP_CREATOR"'"
+ " access='read'/>"
+ " <property type='b' name='"DLS_INTERFACE_PROP_RESTRICTED"'"
+ " access='read'/>"
+ " <property type='a{sb}' name='"DLS_INTERFACE_PROP_DLNA_MANAGED"'"
+ " access='read'/>"
+ " <property type='u' name='"DLS_INTERFACE_PROP_OBJECT_UPDATE_ID"'"
+ " access='read'/>"
+ " <method name='"DLS_INTERFACE_DELETE"'>"
+ " </method>"
+ " <method name='"DLS_INTERFACE_UPDATE"'>"
+ " <arg type='a{sv}' name='"DLS_INTERFACE_TO_ADD_UPDATE"'"
+ " direction='in'/>"
+ " <arg type='as' name='"DLS_INTERFACE_TO_DELETE"'"
+ " direction='in'/>"
+ " </method>"
+ " </interface>"
+ " <interface name='"DLS_INTERFACE_MEDIA_CONTAINER"'>"
+ " <method name='"DLS_INTERFACE_LIST_CHILDREN"'>"
+ " <arg type='u' name='"DLS_INTERFACE_OFFSET"'"
+ " direction='in'/>"
+ " <arg type='u' name='"DLS_INTERFACE_MAX"'"
+ " direction='in'/>"
+ " <arg type='as' name='"DLS_INTERFACE_FILTER"'"
+ " direction='in'/>"
+ " <arg type='aa{sv}' name='"DLS_INTERFACE_CHILDREN"'"
+ " direction='out'/>"
+ " </method>"
+ " <method name='"DLS_INTERFACE_LIST_CHILDREN_EX"'>"
+ " <arg type='u' name='"DLS_INTERFACE_OFFSET"'"
+ " direction='in'/>"
+ " <arg type='u' name='"DLS_INTERFACE_MAX"'"
+ " direction='in'/>"
+ " <arg type='as' name='"DLS_INTERFACE_FILTER"'"
+ " direction='in'/>"
+ " <arg type='s' name='"DLS_INTERFACE_SORT_BY"'"
+ " direction='in'/>"
+ " <arg type='aa{sv}' name='"DLS_INTERFACE_CHILDREN"'"
+ " direction='out'/>"
+ " </method>"
+ " <method name='"DLS_INTERFACE_LIST_CONTAINERS"'>"
+ " <arg type='u' name='"DLS_INTERFACE_OFFSET"'"
+ " direction='in'/>"
+ " <arg type='u' name='"DLS_INTERFACE_MAX"'"
+ " direction='in'/>"
+ " <arg type='as' name='"DLS_INTERFACE_FILTER"'"
+ " direction='in'/>"
+ " <arg type='aa{sv}' name='"DLS_INTERFACE_CHILDREN"'"
+ " direction='out'/>"
+ " </method>"
+ " <method name='"DLS_INTERFACE_LIST_CONTAINERS_EX"'>"
+ " <arg type='u' name='"DLS_INTERFACE_OFFSET"'"
+ " direction='in'/>"
+ " <arg type='u' name='"DLS_INTERFACE_MAX"'"
+ " direction='in'/>"
+ " <arg type='as' name='"DLS_INTERFACE_FILTER"'"
+ " direction='in'/>"
+ " <arg type='s' name='"DLS_INTERFACE_SORT_BY"'"
+ " direction='in'/>"
+ " <arg type='aa{sv}' name='"DLS_INTERFACE_CHILDREN"'"
+ " direction='out'/>"
+ " </method>"
+ " <method name='"DLS_INTERFACE_LIST_ITEMS"'>"
+ " <arg type='u' name='"DLS_INTERFACE_OFFSET"'"
+ " direction='in'/>"
+ " <arg type='u' name='"DLS_INTERFACE_MAX"'"
+ " direction='in'/>"
+ " <arg type='as' name='"DLS_INTERFACE_FILTER"'"
+ " direction='in'/>"
+ " <arg type='aa{sv}' name='"DLS_INTERFACE_CHILDREN"'"
+ " direction='out'/>"
+ " </method>"
+ " <method name='"DLS_INTERFACE_LIST_ITEMS_EX"'>"
+ " <arg type='u' name='"DLS_INTERFACE_OFFSET"'"
+ " direction='in'/>"
+ " <arg type='u' name='"DLS_INTERFACE_MAX"'"
+ " direction='in'/>"
+ " <arg type='as' name='"DLS_INTERFACE_FILTER"'"
+ " direction='in'/>"
+ " <arg type='s' name='"DLS_INTERFACE_SORT_BY"'"
+ " direction='in'/>"
+ " <arg type='aa{sv}' name='"DLS_INTERFACE_CHILDREN"'"
+ " direction='out'/>"
+ " </method>"
+ " <method name='"DLS_INTERFACE_SEARCH_OBJECTS"'>"
+ " <arg type='s' name='"DLS_INTERFACE_QUERY"'"
+ " direction='in'/>"
+ " <arg type='u' name='"DLS_INTERFACE_OFFSET"'"
+ " direction='in'/>"
+ " <arg type='u' name='"DLS_INTERFACE_MAX"'"
+ " direction='in'/>"
+ " <arg type='as' name='"DLS_INTERFACE_FILTER"'"
+ " direction='in'/>"
+ " <arg type='aa{sv}' name='"DLS_INTERFACE_CHILDREN"'"
+ " direction='out'/>"
+ " </method>"
+ " <method name='"DLS_INTERFACE_SEARCH_OBJECTS_EX"'>"
+ " <arg type='s' name='"DLS_INTERFACE_QUERY"'"
+ " direction='in'/>"
+ " <arg type='u' name='"DLS_INTERFACE_OFFSET"'"
+ " direction='in'/>"
+ " <arg type='u' name='"DLS_INTERFACE_MAX"'"
+ " direction='in'/>"
+ " <arg type='as' name='"DLS_INTERFACE_FILTER"'"
+ " direction='in'/>"
+ " <arg type='s' name='"DLS_INTERFACE_SORT_BY"'"
+ " direction='in'/>"
+ " <arg type='aa{sv}' name='"DLS_INTERFACE_CHILDREN"'"
+ " direction='out'/>"
+ " <arg type='u' name='"DLS_INTERFACE_TOTAL_ITEMS"'"
+ " direction='out'/>"
+ " </method>"
+ " <method name='"DLS_INTERFACE_UPLOAD"'>"
+ " <arg type='s' name='"DLS_INTERFACE_PROP_DISPLAY_NAME"'"
+ " direction='in'/>"
+ " <arg type='s' name='"DLS_INTERFACE_FILE_PATH"'"
+ " direction='in'/>"
+ " <arg type='u' name='"DLS_INTERFACE_UPLOAD_ID"'"
+ " direction='out'/>"
+ " <arg type='o' name='"DLS_INTERFACE_PATH"'"
+ " direction='out'/>"
+ " </method>"
+ " <method name='"DLS_INTERFACE_CREATE_CONTAINER"'>"
+ " <arg type='s' name='"DLS_INTERFACE_PROP_DISPLAY_NAME"'"
+ " direction='in'/>"
+ " <arg type='s' name='"DLS_INTERFACE_PROP_TYPE"'"
+ " direction='in'/>"
+ " <arg type='as' name='"DLS_INTERFACE_CHILD_TYPES"'"
+ " direction='in'/>"
+ " <arg type='o' name='"DLS_INTERFACE_PATH"'"
+ " direction='out'/>"
+ " </method>"
+ " <method name='"DLS_INTERFACE_CREATE_PLAYLIST"'>"
+ " <arg type='s' name='"DLS_INTERFACE_TITLE"'"
+ " direction='in'/>"
+ " <arg type='s' name='"DLS_INTERFACE_CREATOR"'"
+ " direction='in'/>"
+ " <arg type='s' name='"DLS_INTERFACE_GENRE"'"
+ " direction='in'/>"
+ " <arg type='s' name='"DLS_INTERFACE_DESCRIPTION"'"
+ " direction='in'/>"
+ " <arg type='ao' name='"DLS_INTERFACE_PLAYLIST_ITEMS"'"
+ " direction='in'/>"
+ " <arg type='u' name='"DLS_INTERFACE_UPLOAD_ID"'"
+ " direction='out'/>"
+ " <arg type='o' name='"DLS_INTERFACE_PATH"'"
+ " direction='out'/>"
+ " </method>"
+ " <property type='u' name='"DLS_INTERFACE_PROP_CHILD_COUNT"'"
+ " access='read'/>"
+ " <property type='b' name='"DLS_INTERFACE_PROP_SEARCHABLE"'"
+ " access='read'/>"
+ " <property type='a(sb)' name='"
+ DLS_INTERFACE_PROP_CREATE_CLASSES"'"
+ " access='read'/>"
+ " <property type='u' name='"
+ DLS_INTERFACE_PROP_CONTAINER_UPDATE_ID"'"
+ " access='read'/>"
+ " <property type='u' name='"
+ DLS_INTERFACE_PROP_TOTAL_DELETED_CHILD_COUNT"'"
+ " access='read'/>"
+ " </interface>"
+ " <interface name='"DLS_INTERFACE_MEDIA_ITEM"'>"
+ " <method name='"DLS_INTERFACE_GET_COMPATIBLE_RESOURCE"'>"
+ " <arg type='s' name='"DLS_INTERFACE_PROTOCOL_INFO"'"
+ " direction='in'/>"
+ " <arg type='as' name='"DLS_INTERFACE_FILTER"'"
+ " direction='in'/>"
+ " <arg type='a{sv}' name='"DLS_INTERFACE_PROPERTIES_VALUE"'"
+ " direction='out'/>"
+ " </method>"
+ " <property type='as' name='"DLS_INTERFACE_PROP_URLS"'"
+ " access='read'/>"
+ " <property type='s' name='"DLS_INTERFACE_PROP_MIME_TYPE"'"
+ " access='read'/>"
+ " <property type='s' name='"DLS_INTERFACE_PROP_ARTIST"'"
+ " access='read'/>"
+ " <property type='as' name='"DLS_INTERFACE_PROP_ARTISTS"'"
+ " access='read'/>"
+ " <property type='s' name='"DLS_INTERFACE_PROP_ALBUM"'"
+ " access='read'/>"
+ " <property type='s' name='"DLS_INTERFACE_PROP_DATE"'"
+ " access='read'/>"
+ " <property type='s' name='"DLS_INTERFACE_PROP_GENRE"'"
+ " access='read'/>"
+ " <property type='s' name='"DLS_INTERFACE_PROP_DLNA_PROFILE"'"
+ " access='read'/>"
+ " <property type='i' name='"DLS_INTERFACE_PROP_TRACK_NUMBER"'"
+ " access='read'/>"
+ " <property type='x' name='"DLS_INTERFACE_PROP_SIZE"'"
+ " access='read'/>"
+ " <property type='i' name='"DLS_INTERFACE_PROP_DURATION"'"
+ " access='read'/>"
+ " <property type='i' name='"DLS_INTERFACE_PROP_BITRATE"'"
+ " access='read'/>"
+ " <property type='i' name='"DLS_INTERFACE_PROP_SAMPLE_RATE"'"
+ " access='read'/>"
+ " <property type='i' name='"DLS_INTERFACE_PROP_BITS_PER_SAMPLE"'"
+ " access='read'/>"
+ " <property type='i' name='"DLS_INTERFACE_PROP_WIDTH"'"
+ " access='read'/>"
+ " <property type='i' name='"DLS_INTERFACE_PROP_HEIGHT"'"
+ " access='read'/>"
+ " <property type='i' name='"DLS_INTERFACE_PROP_COLOR_DEPTH"'"
+ " access='read'/>"
+ " <property type='s' name='"DLS_INTERFACE_PROP_ALBUM_ART_URL"'"
+ " access='read'/>"
+ " <property type='o' name='"DLS_INTERFACE_PROP_REFPATH"'"
+ " access='read'/>"
+ " <property type='aa{sv}' name='"DLS_INTERFACE_PROP_RESOURCES"'"
+ " access='read'/>"
+ " </interface>"
+ " <interface name='"DLEYNA_SERVER_INTERFACE_MEDIA_DEVICE"'>"
+ " <method name='"DLS_INTERFACE_UPLOAD_TO_ANY"'>"
+ " <arg type='s' name='"DLS_INTERFACE_PROP_DISPLAY_NAME"'"
+ " direction='in'/>"
+ " <arg type='s' name='"DLS_INTERFACE_FILE_PATH"'"
+ " direction='in'/>"
+ " <arg type='u' name='"DLS_INTERFACE_UPLOAD_ID"'"
+ " direction='out'/>"
+ " <arg type='o' name='"DLS_INTERFACE_PATH"'"
+ " direction='out'/>"
+ " </method>"
+ " <method name='"DLS_INTERFACE_GET_UPLOAD_STATUS"'>"
+ " <arg type='u' name='"DLS_INTERFACE_UPLOAD_ID"'"
+ " direction='in'/>"
+ " <arg type='s' name='"DLS_INTERFACE_UPLOAD_STATUS"'"
+ " direction='out'/>"
+ " <arg type='t' name='"DLS_INTERFACE_LENGTH"'"
+ " direction='out'/>"
+ " <arg type='t' name='"DLS_INTERFACE_TOTAL"'"
+ " direction='out'/>"
+ " </method>"
+ " <method name='"DLS_INTERFACE_GET_UPLOAD_IDS"'>"
+ " <arg type='au' name='"DLS_INTERFACE_TOTAL"'"
+ " direction='out'/>"
+ " </method>"
+ " <method name='"DLS_INTERFACE_CANCEL_UPLOAD"'>"
+ " <arg type='u' name='"DLS_INTERFACE_UPLOAD_ID"'"
+ " direction='in'/>"
+ " </method>"
+ " <method name='"DLS_INTERFACE_CREATE_CONTAINER_IN_ANY"'>"
+ " <arg type='s' name='"DLS_INTERFACE_PROP_DISPLAY_NAME"'"
+ " direction='in'/>"
+ " <arg type='s' name='"DLS_INTERFACE_PROP_TYPE"'"
+ " direction='in'/>"
+ " <arg type='as' name='"DLS_INTERFACE_CHILD_TYPES"'"
+ " direction='in'/>"
+ " <arg type='o' name='"DLS_INTERFACE_PATH"'"
+ " direction='out'/>"
+ " </method>"
+ " <method name='"DLS_INTERFACE_CANCEL"'>"
+ " </method>"
+ " <method name='"DLS_INTERFACE_CREATE_PLAYLIST_TO_ANY"'>"
+ " <arg type='s' name='"DLS_INTERFACE_TITLE"'"
+ " direction='in'/>"
+ " <arg type='s' name='"DLS_INTERFACE_CREATOR"'"
+ " direction='in'/>"
+ " <arg type='s' name='"DLS_INTERFACE_GENRE"'"
+ " direction='in'/>"
+ " <arg type='s' name='"DLS_INTERFACE_DESCRIPTION"'"
+ " direction='in'/>"
+ " <arg type='ao' name='"DLS_INTERFACE_PLAYLIST_ITEMS"'"
+ " direction='in'/>"
+ " <arg type='u' name='"DLS_INTERFACE_UPLOAD_ID"'"
+ " direction='out'/>"
+ " <arg type='o' name='"DLS_INTERFACE_PATH"'"
+ " direction='out'/>"
+ " </method>"
+ " <property type='s' name='"DLS_INTERFACE_PROP_LOCATION"'"
+ " access='read'/>"
+ " <property type='s' name='"DLS_INTERFACE_PROP_UDN"'"
+ " access='read'/>"
+ " <property type='s' name='"DLS_INTERFACE_PROP_DEVICE_TYPE"'"
+ " access='read'/>"
+ " <property type='s' name='"DLS_INTERFACE_PROP_FRIENDLY_NAME"'"
+ " access='read'/>"
+ " <property type='s' name='"DLS_INTERFACE_PROP_MANUFACTURER"'"
+ " access='read'/>"
+ " <property type='s' name='"
+ DLS_INTERFACE_PROP_MANUFACTURER_URL"'"
+ " access='read'/>"
+ " <property type='s' name='"
+ DLS_INTERFACE_PROP_MODEL_DESCRIPTION"'"
+ " access='read'/>"
+ " <property type='s' name='"DLS_INTERFACE_PROP_MODEL_NAME"'"
+ " access='read'/>"
+ " <property type='s' name='"DLS_INTERFACE_PROP_MODEL_NUMBER"'"
+ " access='read'/>"
+ " <property type='s' name='"DLS_INTERFACE_PROP_MODEL_URL"'"
+ " access='read'/>"
+ " <property type='s' name='"DLS_INTERFACE_PROP_SERIAL_NUMBER"'"
+ " access='read'/>"
+ " <property type='s' name='"DLS_INTERFACE_PROP_PRESENTATION_URL"'"
+ " access='read'/>"
+ " <property type='s' name='"DLS_INTERFACE_PROP_ICON_URL"'"
+ " access='read'/>"
+ " <property type='a{sv}'name='"
+ DLS_INTERFACE_PROP_SV_DLNA_CAPABILITIES"'"
+ " access='read'/>"
+ " <property type='as' name='"
+ DLS_INTERFACE_PROP_SV_SEARCH_CAPABILITIES"'"
+ " access='read'/>"
+ " <property type='as' name='"
+ DLS_INTERFACE_PROP_SV_SORT_CAPABILITIES"'"
+ " access='read'/>"
+ " <property type='as' name='"
+ DLS_INTERFACE_PROP_SV_SORT_EXT_CAPABILITIES"'"
+ " access='read'/>"
+ " <property type='a(ssao)' name='"
+ DLS_INTERFACE_PROP_SV_FEATURE_LIST"'"
+ " access='read'/>"
+ " <property type='u' name='"
+ DLS_INTERFACE_PROP_ESV_SYSTEM_UPDATE_ID"'"
+ " access='read'/>"
+ " <property type='s' name='"
+ DLS_INTERFACE_PROP_SV_SERVICE_RESET_TOKEN"'"
+ " access='read'/>"
+ " <signal name='"DLS_INTERFACE_ESV_CONTAINER_UPDATE_IDS"'>"
+ " <arg type='a(ou)' name='"DLS_INTERFACE_CONTAINER_PATHS_ID"'/>"
+ " </signal>"
+ " <signal name='"DLS_INTERFACE_ESV_LAST_CHANGE"'>"
+ " <arg type='a(sv)' name='"
+ DLS_INTERFACE_LAST_CHANGE_STATE_EVENT"'/>"
+ " </signal>"
+ " <signal name='"DLS_INTERFACE_UPLOAD_UPDATE"'>"
+ " <arg type='u' name='"DLS_INTERFACE_UPLOAD_ID"'/>"
+ " <arg type='s' name='"DLS_INTERFACE_UPLOAD_STATUS"'/>"
+ " <arg type='t' name='"DLS_INTERFACE_LENGTH"'/>"
+ " <arg type='t' name='"DLS_INTERFACE_TOTAL"'/>"
+ " </signal>"
+ " </interface>"
+ "</node>";
+
+const dleyna_connector_t *dls_server_get_connector(void)
+{
+ return g_context.connector;
+}
+
+dleyna_task_processor_t *dls_server_get_task_processor(void)
+{
+ return g_context.processor;
+}
+
+static void prv_sync_task_complete(dls_task_t *task)
+{
+ dls_task_complete(task);
+ dleyna_task_queue_task_completed(task->atom.queue_id);
+}
+
+static void prv_process_sync_task(dls_task_t *task)
+{
+ dls_client_t *client;
+ const gchar *client_name;
+
+ switch (task->type) {
+ case DLS_TASK_GET_VERSION:
+ prv_sync_task_complete(task);
+ break;
+ case DLS_TASK_GET_SERVERS:
+ task->result = dls_upnp_get_server_ids(g_context.upnp);
+ prv_sync_task_complete(task);
+ break;
+ case DLS_TASK_SET_PROTOCOL_INFO:
+ client_name = dleyna_task_queue_get_source(task->atom.queue_id);
+ client = g_hash_table_lookup(g_context.watchers, client_name);
+ if (client) {
+ g_free(client->protocol_info);
+ if (task->ut.protocol_info.protocol_info[0]) {
+ client->protocol_info =
+ task->ut.protocol_info.protocol_info;
+ task->ut.protocol_info.protocol_info = NULL;
+ } else {
+ client->protocol_info = NULL;
+ }
+ }
+ prv_sync_task_complete(task);
+ break;
+ case DLS_TASK_SET_PREFER_LOCAL_ADDRESSES:
+ client_name = dleyna_task_queue_get_source(task->atom.queue_id);
+ client = g_hash_table_lookup(g_context.watchers, client_name);
+ if (client) {
+ client->prefer_local_addresses =
+ task->ut.prefer_local_addresses.prefer;
+ }
+ prv_sync_task_complete(task);
+ break;
+ case DLS_TASK_GET_UPLOAD_STATUS:
+ dls_upnp_get_upload_status(g_context.upnp, task);
+ dleyna_task_queue_task_completed(task->atom.queue_id);
+ break;
+ case DLS_TASK_GET_UPLOAD_IDS:
+ dls_upnp_get_upload_ids(g_context.upnp, task);
+ dleyna_task_queue_task_completed(task->atom.queue_id);
+ break;
+ case DLS_TASK_CANCEL_UPLOAD:
+ dls_upnp_cancel_upload(g_context.upnp, task);
+ dleyna_task_queue_task_completed(task->atom.queue_id);
+ break;
+ default:
+ break;
+ }
+}
+
+static void prv_async_task_complete(dls_task_t *task, GError *error)
+{
+ DLEYNA_LOG_DEBUG("Enter");
+
+ if (error) {
+ dls_task_fail(task, error);
+ g_error_free(error);
+ } else {
+ dls_task_complete(task);
+ }
+
+ dleyna_task_queue_task_completed(task->atom.queue_id);
+
+ DLEYNA_LOG_DEBUG("Exit");
+}
+
+static void prv_process_async_task(dls_task_t *task)
+{
+ dls_async_task_t *async_task = (dls_async_task_t *)task;
+ dls_client_t *client;
+ const gchar *client_name;
+
+ DLEYNA_LOG_DEBUG("Enter");
+
+ async_task->cancellable = g_cancellable_new();
+ client_name = dleyna_task_queue_get_source(task->atom.queue_id);
+ client = g_hash_table_lookup(g_context.watchers, client_name);
+
+ switch (task->type) {
+ case DLS_TASK_GET_CHILDREN:
+ dls_upnp_get_children(g_context.upnp, client, task,
+ prv_async_task_complete);
+ break;
+ case DLS_TASK_GET_PROP:
+ dls_upnp_get_prop(g_context.upnp, client, task,
+ prv_async_task_complete);
+ break;
+ case DLS_TASK_GET_ALL_PROPS:
+ dls_upnp_get_all_props(g_context.upnp, client, task,
+ prv_async_task_complete);
+ break;
+ case DLS_TASK_SEARCH:
+ dls_upnp_search(g_context.upnp, client, task,
+ prv_async_task_complete);
+ break;
+ case DLS_TASK_GET_RESOURCE:
+ dls_upnp_get_resource(g_context.upnp, client, task,
+ prv_async_task_complete);
+ break;
+ case DLS_TASK_UPLOAD_TO_ANY:
+ dls_upnp_upload_to_any(g_context.upnp, client, task,
+ prv_async_task_complete);
+ break;
+ case DLS_TASK_UPLOAD:
+ dls_upnp_upload(g_context.upnp, client, task,
+ prv_async_task_complete);
+ break;
+ case DLS_TASK_DELETE_OBJECT:
+ dls_upnp_delete_object(g_context.upnp, client, task,
+ prv_async_task_complete);
+ break;
+ case DLS_TASK_CREATE_CONTAINER:
+ dls_upnp_create_container(g_context.upnp, client, task,
+ prv_async_task_complete);
+ break;
+ case DLS_TASK_CREATE_CONTAINER_IN_ANY:
+ dls_upnp_create_container_in_any(g_context.upnp, client, task,
+ prv_async_task_complete);
+ break;
+ case DLS_TASK_UPDATE_OBJECT:
+ dls_upnp_update_object(g_context.upnp, client, task,
+ prv_async_task_complete);
+ break;
+ case DLS_TASK_CREATE_PLAYLIST:
+ dls_upnp_create_playlist(g_context.upnp, client, task,
+ prv_async_task_complete);
+ break;
+ case DLS_TASK_CREATE_PLAYLIST_IN_ANY:
+ dls_upnp_create_playlist_in_any(g_context.upnp, client, task,
+ prv_async_task_complete);
+ break;
+ default:
+ break;
+ }
+
+ DLEYNA_LOG_DEBUG("Exit");
+}
+
+static void prv_process_task(dleyna_task_atom_t *task, gpointer user_data)
+{
+ dls_task_t *client_task = (dls_task_t *)task;
+
+ if (client_task->synchronous)
+ prv_process_sync_task(client_task);
+ else
+ prv_process_async_task(client_task);
+}
+
+static void prv_cancel_task(dleyna_task_atom_t *task, gpointer user_data)
+{
+ dls_task_cancel((dls_task_t *)task);
+}
+
+static void prv_delete_task(dleyna_task_atom_t *task, gpointer user_data)
+{
+ dls_task_delete((dls_task_t *)task);
+}
+
+static void prv_method_call(dleyna_connector_id_t conn,
+ const gchar *sender,
+ const gchar *object,
+ const gchar *interface,
+ const gchar *method,
+ GVariant *parameters,
+ dleyna_connector_msg_id_t invocation);
+
+static void prv_object_method_call(dleyna_connector_id_t conn,
+ const gchar *sender,
+ const gchar *object,
+ const gchar *interface,
+ const gchar *method,
+ GVariant *parameters,
+ dleyna_connector_msg_id_t invocation);
+
+static void prv_item_method_call(dleyna_connector_id_t conn,
+ const gchar *sender,
+ const gchar *object,
+ const gchar *interface,
+ const gchar *method,
+ GVariant *parameters,
+ dleyna_connector_msg_id_t invocation);
+
+static void prv_con_method_call(dleyna_connector_id_t conn,
+ const gchar *sender,
+ const gchar *object,
+ const gchar *interface,
+ const gchar *method,
+ GVariant *parameters,
+ dleyna_connector_msg_id_t invocation);
+
+static void prv_props_method_call(dleyna_connector_id_t conn,
+ const gchar *sender,
+ const gchar *object,
+ const gchar *interface,
+ const gchar *method,
+ GVariant *parameters,
+ dleyna_connector_msg_id_t invocation);
+
+static void prv_device_method_call(dleyna_connector_id_t conn,
+ const gchar *sender,
+ const gchar *object,
+ const gchar *interface,
+ const gchar *method,
+ GVariant *parameters,
+ dleyna_connector_msg_id_t invocation);
+
+static const dleyna_connector_dispatch_cb_t g_root_vtables[1] = {
+ prv_method_call
+};
+
+static const dleyna_connector_dispatch_cb_t
+ g_server_vtables[DLS_INTERFACE_INFO_MAX] = {
+ /* MUST be in the exact same order as g_dls_server_introspection */
+ prv_props_method_call,
+ prv_object_method_call,
+ prv_con_method_call,
+ prv_item_method_call,
+ prv_device_method_call
+};
+
+static void prv_remove_client(const gchar *name)
+{
+ dleyna_task_processor_remove_queues_for_source(g_context.processor,
+ name);
+
+ (void) g_hash_table_remove(g_context.watchers, name);
+
+ if (g_hash_table_size(g_context.watchers) == 0)
+ if (!dleyna_settings_is_never_quit(g_context.settings))
+ dleyna_task_processor_set_quitting(g_context.processor);
+}
+
+static void prv_lost_client(const gchar *name)
+{
+ DLEYNA_LOG_DEBUG("Lost Client %s", name);
+
+ prv_remove_client(name);
+}
+
+static void prv_add_task(dls_task_t *task, const gchar *source,
+ const gchar *sink)
+{
+ dls_client_t *client;
+ const dleyna_task_queue_key_t *queue_id;
+
+ if (!g_hash_table_lookup(g_context.watchers, source)) {
+ client = g_new0(dls_client_t, 1);
+ client->prefer_local_addresses = TRUE;
+ g_context.connector->watch_client(source);
+ g_hash_table_insert(g_context.watchers, g_strdup(source),
+ client);
+ }
+
+ queue_id = dleyna_task_processor_lookup_queue(g_context.processor,
+ source, sink);
+ if (!queue_id)
+ queue_id = dleyna_task_processor_add_queue(
+ g_context.processor,
+ source,
+ sink,
+ DLEYNA_TASK_QUEUE_FLAG_AUTO_START,
+ prv_process_task,
+ prv_cancel_task,
+ prv_delete_task);
+
+ dleyna_task_queue_add_task(queue_id, &task->atom);
+}
+
+static void prv_method_call(dleyna_connector_id_t conn,
+ const gchar *sender, const gchar *object,
+ const gchar *interface,
+ const gchar *method, GVariant *parameters,
+ dleyna_connector_msg_id_t invocation)
+{
+ dls_task_t *task;
+
+ if (!strcmp(method, DLS_INTERFACE_RELEASE)) {
+ prv_remove_client(sender);
+ g_context.connector->return_response(invocation, NULL);
+ } else if (!strcmp(method, DLS_INTERFACE_GET_VERSION)) {
+ task = dls_task_get_version_new(invocation);
+ prv_add_task(task, sender, DLS_SERVER_SINK);
+ } else if (!strcmp(method, DLS_INTERFACE_GET_SERVERS)) {
+ task = dls_task_get_servers_new(invocation);
+ prv_add_task(task, sender, DLS_SERVER_SINK);
+ } else if (!strcmp(method, DLS_INTERFACE_SET_PROTOCOL_INFO)) {
+ task = dls_task_set_protocol_info_new(invocation,
+ parameters);
+ prv_add_task(task, sender, DLS_SERVER_SINK);
+ } else if (!strcmp(method, DLS_INTERFACE_PREFER_LOCAL_ADDRESSES)) {
+ task = dls_task_prefer_local_addresses_new(invocation,
+ parameters);
+ prv_add_task(task, sender, DLS_SERVER_SINK);
+ }
+}
+
+gboolean dls_server_get_object_info(const gchar *object_path,
+ gchar **root_path,
+ gchar **object_id,
+ dls_device_t **device,
+ GError **error)
+{
+ if (!dls_path_get_path_and_id(object_path, root_path, object_id,
+ error)) {
+ DLEYNA_LOG_WARNING("Bad object %s", object_path);
+
+ goto on_error;
+ }
+
+ *device = dls_device_from_path(*root_path,
+ dls_upnp_get_server_udn_map(g_context.upnp));
+
+ if (*device == NULL) {
+ DLEYNA_LOG_WARNING("Cannot locate device for %s", *root_path);
+
+ *error = g_error_new(DLEYNA_SERVER_ERROR,
+ DLEYNA_ERROR_OBJECT_NOT_FOUND,
+ "Cannot locate device corresponding to"
+ " the specified path");
+
+ g_free(*root_path);
+ g_free(*object_id);
+
+ goto on_error;
+ }
+
+ return TRUE;
+
+on_error:
+
+ return FALSE;
+}
+
+static const gchar *prv_get_device_id(const gchar *object, GError **error)
+{
+ dls_device_t *device;
+ gchar *root_path;
+ gchar *id;
+
+ if (!dls_server_get_object_info(object, &root_path, &id, &device,
+ error))
+ goto on_error;
+
+ g_free(id);
+ g_free(root_path);
+
+ return device->path;
+
+on_error:
+
+ return NULL;
+}
+
+static void prv_object_method_call(dleyna_connector_id_t conn,
+ const gchar *sender, const gchar *object,
+ const gchar *interface,
+ const gchar *method, GVariant *parameters,
+ dleyna_connector_msg_id_t invocation)
+{
+ dls_task_t *task;
+ GError *error = NULL;
+
+ if (!strcmp(method, DLS_INTERFACE_DELETE))
+ task = dls_task_delete_new(invocation, object, &error);
+ else if (!strcmp(method, DLS_INTERFACE_UPDATE))
+ task = dls_task_update_new(invocation, object,
+ parameters, &error);
+ else
+ goto finished;
+
+ if (!task) {
+ g_context.connector->return_error(invocation, error);
+ g_error_free(error);
+
+ goto finished;
+ }
+
+ prv_add_task(task, sender, task->target.device->path);
+
+finished:
+
+ return;
+}
+
+static void prv_item_method_call(dleyna_connector_id_t conn,
+ const gchar *sender, const gchar *object,
+ const gchar *interface,
+ const gchar *method, GVariant *parameters,
+ dleyna_connector_msg_id_t invocation)
+{
+ dls_task_t *task;
+ GError *error = NULL;
+
+ if (!strcmp(method, DLS_INTERFACE_GET_COMPATIBLE_RESOURCE)) {
+ task = dls_task_get_resource_new(invocation, object,
+ parameters, &error);
+
+ if (!task) {
+ g_context.connector->return_error(invocation, error);
+ g_error_free(error);
+
+ goto finished;
+ }
+
+ prv_add_task(task, sender, task->target.device->path);
+ }
+
+finished:
+
+ return;
+}
+
+
+static void prv_con_method_call(dleyna_connector_id_t conn,
+ const gchar *sender,
+ const gchar *object,
+ const gchar *interface,
+ const gchar *method,
+ GVariant *parameters,
+ dleyna_connector_msg_id_t invocation)
+{
+ dls_task_t *task;
+ GError *error = NULL;
+
+ if (!strcmp(method, DLS_INTERFACE_LIST_CHILDREN))
+ task = dls_task_get_children_new(invocation, object,
+ parameters, TRUE,
+ TRUE, &error);
+ else if (!strcmp(method, DLS_INTERFACE_LIST_CHILDREN_EX))
+ task = dls_task_get_children_ex_new(invocation, object,
+ parameters, TRUE,
+ TRUE, &error);
+ else if (!strcmp(method, DLS_INTERFACE_LIST_ITEMS))
+ task = dls_task_get_children_new(invocation, object,
+ parameters, TRUE,
+ FALSE, &error);
+ else if (!strcmp(method, DLS_INTERFACE_LIST_ITEMS_EX))
+ task = dls_task_get_children_ex_new(invocation, object,
+ parameters, TRUE,
+ FALSE, &error);
+ else if (!strcmp(method, DLS_INTERFACE_LIST_CONTAINERS))
+ task = dls_task_get_children_new(invocation, object,
+ parameters, FALSE,
+ TRUE, &error);
+ else if (!strcmp(method, DLS_INTERFACE_LIST_CONTAINERS_EX))
+ task = dls_task_get_children_ex_new(invocation, object,
+ parameters, FALSE,
+ TRUE, &error);
+ else if (!strcmp(method, DLS_INTERFACE_SEARCH_OBJECTS))
+ task = dls_task_search_new(invocation, object,
+ parameters, &error);
+ else if (!strcmp(method, DLS_INTERFACE_SEARCH_OBJECTS_EX))
+ task = dls_task_search_ex_new(invocation, object,
+ parameters, &error);
+ else if (!strcmp(method, DLS_INTERFACE_UPLOAD))
+ task = dls_task_upload_new(invocation, object,
+ parameters, &error);
+ else if (!strcmp(method, DLS_INTERFACE_CREATE_CONTAINER))
+ task = dls_task_create_container_new_generic(invocation,
+ DLS_TASK_CREATE_CONTAINER,
+ object, parameters, &error);
+ else if (!strcmp(method, DLS_INTERFACE_CREATE_PLAYLIST))
+ task = dls_task_create_playlist_new(invocation,
+ DLS_TASK_CREATE_PLAYLIST,
+ object, parameters, &error);
+ else
+ goto finished;
+
+ if (!task) {
+ g_context.connector->return_error(invocation, error);
+ g_error_free(error);
+
+ goto finished;
+ }
+
+ prv_add_task(task, sender, task->target.device->path);
+
+finished:
+
+ return;
+}
+
+static void prv_props_method_call(dleyna_connector_id_t conn,
+ const gchar *sender,
+ const gchar *object,
+ const gchar *interface,
+ const gchar *method,
+ GVariant *parameters,
+ dleyna_connector_msg_id_t invocation)
+{
+ dls_task_t *task;
+ GError *error = NULL;
+
+ if (!strcmp(method, DLS_INTERFACE_GET_ALL))
+ task = dls_task_get_props_new(invocation, object,
+ parameters, &error);
+ else if (!strcmp(method, DLS_INTERFACE_GET))
+ task = dls_task_get_prop_new(invocation, object,
+ parameters, &error);
+ else
+ goto finished;
+
+ if (!task) {
+ g_context.connector->return_error(invocation, error);
+ g_error_free(error);
+
+ goto finished;
+ }
+
+ prv_add_task(task, sender, task->target.device->path);
+
+finished:
+
+ return;
+}
+
+static void prv_device_method_call(dleyna_connector_id_t conn,
+ const gchar *sender, const gchar *object,
+ const gchar *interface,
+ const gchar *method, GVariant *parameters,
+ dleyna_connector_msg_id_t invocation)
+{
+ dls_task_t *task;
+ GError *error = NULL;
+ const gchar *device_id;
+ const dleyna_task_queue_key_t *queue_id;
+
+ if (!strcmp(method, DLS_INTERFACE_UPLOAD_TO_ANY)) {
+ task = dls_task_upload_to_any_new(invocation,
+ object, parameters, &error);
+ } else if (!strcmp(method, DLS_INTERFACE_CREATE_CONTAINER_IN_ANY)) {
+ task = dls_task_create_container_new_generic(
+ invocation,
+ DLS_TASK_CREATE_CONTAINER_IN_ANY,
+ object, parameters, &error);
+ } else if (!strcmp(method, DLS_INTERFACE_GET_UPLOAD_STATUS)) {
+ task = dls_task_get_upload_status_new(invocation,
+ object, parameters,
+ &error);
+ } else if (!strcmp(method, DLS_INTERFACE_GET_UPLOAD_IDS)) {
+ task = dls_task_get_upload_ids_new(invocation, object,
+ &error);
+ } else if (!strcmp(method, DLS_INTERFACE_CANCEL_UPLOAD)) {
+ task = dls_task_cancel_upload_new(invocation, object,
+ parameters, &error);
+ } else if (!strcmp(method, DLS_INTERFACE_CREATE_PLAYLIST_TO_ANY)) {
+ task = dls_task_create_playlist_new(
+ invocation,
+ DLS_TASK_CREATE_PLAYLIST_IN_ANY,
+ object, parameters, &error);
+ } else if (!strcmp(method, DLS_INTERFACE_CANCEL)) {
+ task = NULL;
+
+ device_id = prv_get_device_id(object, &error);
+ if (!device_id)
+ goto on_error;
+
+ queue_id = dleyna_task_processor_lookup_queue(
+ g_context.processor,
+ sender,
+ device_id);
+ if (queue_id)
+ dleyna_task_processor_cancel_queue(queue_id);
+
+ g_context.connector->return_response(invocation, NULL);
+
+ goto finished;
+ } else {
+ goto finished;
+ }
+
+on_error:
+
+ if (!task) {
+ g_context.connector->return_error(invocation, error);
+ g_error_free(error);
+
+ goto finished;
+ }
+
+ prv_add_task(task, sender, task->target.device->path);
+
+finished:
+
+ return;
+}
+
+static void prv_found_media_server(const gchar *path, void *user_data)
+{
+ (void) g_context.connector->notify(g_context.connection,
+ DLEYNA_SERVER_OBJECT,
+ DLEYNA_SERVER_INTERFACE_MANAGER,
+ DLS_INTERFACE_FOUND_SERVER,
+ g_variant_new("(o)", path),
+ NULL);
+}
+
+static void prv_lost_media_server(const gchar *path, void *user_data)
+{
+ (void) g_context.connector->notify(g_context.connection,
+ DLEYNA_SERVER_OBJECT,
+ DLEYNA_SERVER_INTERFACE_MANAGER,
+ DLS_INTERFACE_LOST_SERVER,
+ g_variant_new("(o)", path),
+ NULL);
+
+ dleyna_task_processor_remove_queues_for_sink(g_context.processor, path);
+}
+
+static void prv_unregister_client(gpointer user_data)
+{
+ dls_client_t *client = user_data;
+
+ if (client) {
+ g_free(client->protocol_info);
+ g_free(client);
+ }
+}
+
+dls_upnp_t *dls_server_get_upnp(void)
+{
+ return g_context.upnp;
+}
+
+static gboolean prv_control_point_start_service(
+ dleyna_connector_id_t connection)
+{
+ gboolean retval = TRUE;
+
+ g_context.connection = connection;
+
+ g_context.dls_id = g_context.connector->publish_object(
+ connection,
+ DLEYNA_SERVER_OBJECT,
+ TRUE,
+ 0,
+ g_root_vtables);
+
+ if (!g_context.dls_id) {
+ retval = FALSE;
+ goto out;
+ } else {
+ g_context.upnp = dls_upnp_new(connection,
+ g_server_vtables,
+ prv_found_media_server,
+ prv_lost_media_server,
+ NULL);
+ }
+
+out:
+
+ return retval;
+}
+
+static void prv_control_point_stop_service(void)
+{
+ dls_upnp_unsubscribe(g_context.upnp);
+
+ dls_upnp_delete(g_context.upnp);
+
+ if (g_context.connection) {
+ if (g_context.dls_id)
+ g_context.connector->unpublish_object(
+ g_context.connection,
+ g_context.dls_id);
+ }
+}
+
+static void prv_control_point_initialize(const dleyna_connector_t *connector,
+ dleyna_task_processor_t *processor,
+ dleyna_settings_t *settings)
+{
+ memset(&g_context, 0, sizeof(g_context));
+
+ g_context.connector = connector;
+ g_context.processor = processor;
+ g_context.settings = settings;
+
+ g_context.connector->set_client_lost_cb(prv_lost_client);
+
+ g_context.watchers = g_hash_table_new_full(g_str_hash, g_str_equal,
+ g_free, prv_unregister_client);
+
+ g_set_prgname(DLS_PRG_NAME);
+}
+
+static void prv_control_point_free(void)
+{
+ if (g_context.watchers)
+ g_hash_table_unref(g_context.watchers);
+}
+
+static const gchar *prv_control_point_server_name(void)
+{
+ return DLEYNA_SERVER_NAME;
+}
+
+static const gchar *prv_control_point_server_introspection(void)
+{
+ return g_server_introspection;
+}
+
+static const gchar *prv_control_point_root_introspection(void)
+{
+ return g_root_introspection;
+}
+
+static const dleyna_control_point_t g_control_point = {
+ prv_control_point_initialize,
+ prv_control_point_free,
+ prv_control_point_server_name,
+ prv_control_point_server_introspection,
+ prv_control_point_root_introspection,
+ prv_control_point_start_service,
+ prv_control_point_stop_service
+};
+
+const dleyna_control_point_t *dleyna_control_point_get_server(void)
+{
+ return &g_control_point;
+}
diff --git a/libdleyna/server/server.h b/libdleyna/server/server.h
new file mode 100644
index 0000000..01d9356
--- /dev/null
+++ b/libdleyna/server/server.h
@@ -0,0 +1,47 @@
+/*
+ * dLeyna
+ *
+ * Copyright (C) 2013 Intel Corporation. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms and conditions of the GNU Lesser General Public License,
+ * version 2.1, as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License
+ * for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with this program; if not, write to the Free Software Foundation, Inc.,
+ * 51 Franklin St - Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Regis Merlino <regis.merlino@intel.com>
+ *
+ */
+
+#ifndef DLS_SERVER_H__
+#define DLS_SERVER_H__
+
+#include <libdleyna/core/connector.h>
+#include <libdleyna/core/task-processor.h>
+
+#define DLS_SERVER_SINK "dleyna-server"
+
+typedef struct dls_device_t_ dls_device_t;
+typedef struct dls_device_context_t_ dls_device_context_t;
+typedef struct dls_upnp_t_ dls_upnp_t;
+
+gboolean dls_server_get_object_info(const gchar *object_path,
+ gchar **root_path,
+ gchar **object_id,
+ dls_device_t **device,
+ GError **error);
+
+dls_upnp_t *dls_server_get_upnp(void);
+
+dleyna_task_processor_t *dls_server_get_task_processor(void);
+
+const dleyna_connector_t *dls_server_get_connector(void);
+
+#endif /* DLS_SERVER_H__ */
diff --git a/libdleyna/server/sort.c b/libdleyna/server/sort.c
new file mode 100644
index 0000000..d1f3c4d
--- /dev/null
+++ b/libdleyna/server/sort.c
@@ -0,0 +1,98 @@
+/*
+ * dLeyna
+ *
+ * Copyright (C) 2012-2013 Intel Corporation. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms and conditions of the GNU Lesser General Public License,
+ * version 2.1, as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License
+ * for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with this program; if not, write to the Free Software Foundation, Inc.,
+ * 51 Franklin St - Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Mark Ryan <mark.d.ryan@intel.com>
+ *
+ */
+
+#include <string.h>
+
+#include "props.h"
+#include "sort.h"
+
+gchar *dls_sort_translate_sort_string(GHashTable *filter_map,
+ const gchar *sort_string)
+{
+ GRegex *reg;
+ gchar *retval = NULL;
+ GMatchInfo *match_info = NULL;
+ gchar *prop = NULL;
+ gchar *op = NULL;
+ dls_prop_map_t *prop_map;
+ GString *str;
+
+ if (!g_regex_match_simple(
+ "^((\\+|\\-)([^,\\+\\-]+))?(,(\\+|\\-)([^,\\+\\-]+))*$",
+ sort_string, 0, 0))
+ goto no_free;
+
+ reg = g_regex_new("(\\+|\\-)(\\w+)", 0, 0, NULL);
+ str = g_string_new("");
+
+ g_regex_match(reg, sort_string, 0, &match_info);
+ while (g_match_info_matches(match_info)) {
+ op = g_match_info_fetch(match_info, 1);
+ if (!op)
+ goto on_error;
+
+ prop = g_match_info_fetch(match_info, 2);
+ if (!prop)
+ goto on_error;
+
+ prop_map = g_hash_table_lookup(filter_map, prop);
+ if (!prop_map)
+ goto on_error;
+
+ if (!prop_map->searchable)
+ goto on_error;
+
+ g_string_append_printf(str, "%s%s,", op,
+ prop_map->upnp_prop_name);
+
+ g_free(prop);
+ g_free(op);
+
+ prop = NULL;
+ op = NULL;
+
+ g_match_info_next(match_info, NULL);
+ }
+
+ if (str->len > 0)
+ str = g_string_truncate(str, str->len - 1);
+ retval = g_string_free(str, FALSE);
+
+ str = NULL;
+
+on_error:
+
+ g_free(prop);
+ g_free(op);
+
+ if (match_info)
+ g_match_info_free(match_info);
+
+ if (str)
+ g_string_free(str, TRUE);
+
+ g_regex_unref(reg);
+
+no_free:
+
+ return retval;
+}
diff --git a/libdleyna/server/sort.h b/libdleyna/server/sort.h
new file mode 100644
index 0000000..378d163
--- /dev/null
+++ b/libdleyna/server/sort.h
@@ -0,0 +1,31 @@
+/*
+ * dLeyna
+ *
+ * Copyright (C) 2012-2013 Intel Corporation. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms and conditions of the GNU Lesser General Public License,
+ * version 2.1, as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License
+ * for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with this program; if not, write to the Free Software Foundation, Inc.,
+ * 51 Franklin St - Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Mark Ryan <mark.d.ryan@intel.com>
+ *
+ */
+
+#ifndef DLS_SORT_H__
+#define DLS_SORT_H__
+
+#include <glib.h>
+
+gchar *dls_sort_translate_sort_string(GHashTable *filter_map,
+ const gchar *sort_string);
+
+#endif /* DLS_SORT_H__ */
diff --git a/libdleyna/server/task.c b/libdleyna/server/task.c
new file mode 100644
index 0000000..6a8cf85
--- /dev/null
+++ b/libdleyna/server/task.c
@@ -0,0 +1,618 @@
+/*
+ * dLeyna
+ *
+ * Copyright (C) 2012-2013 Intel Corporation. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms and conditions of the GNU Lesser General Public License,
+ * version 2.1, as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License
+ * for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with this program; if not, write to the Free Software Foundation, Inc.,
+ * 51 Franklin St - Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Mark Ryan <mark.d.ryan@intel.com>
+ *
+ */
+
+#include <libdleyna/core/error.h>
+
+#include "async.h"
+
+dls_task_t *dls_task_get_version_new(dleyna_connector_msg_id_t invocation)
+{
+ dls_task_t *task = g_new0(dls_task_t, 1);
+
+ task->type = DLS_TASK_GET_VERSION;
+ task->invocation = invocation;
+ task->result_format = "(@s)";
+ task->result = g_variant_ref_sink(g_variant_new_string(VERSION));
+ task->synchronous = TRUE;
+
+ return task;
+}
+
+dls_task_t *dls_task_get_servers_new(dleyna_connector_msg_id_t invocation)
+{
+ dls_task_t *task = g_new0(dls_task_t, 1);
+
+ task->type = DLS_TASK_GET_SERVERS;
+ task->invocation = invocation;
+ task->result_format = "(@ao)";
+ task->synchronous = TRUE;
+
+ return task;
+}
+
+static void prv_delete(dls_task_t *task)
+{
+ if (!task->synchronous)
+ dls_async_task_delete((dls_async_task_t *)task);
+
+ switch (task->type) {
+ case DLS_TASK_GET_CHILDREN:
+ if (task->ut.get_children.filter)
+ g_variant_unref(task->ut.get_children.filter);
+ g_free(task->ut.get_children.sort_by);
+ break;
+ case DLS_TASK_GET_ALL_PROPS:
+ g_free(task->ut.get_props.interface_name);
+ break;
+ case DLS_TASK_GET_PROP:
+ g_free(task->ut.get_prop.interface_name);
+ g_free(task->ut.get_prop.prop_name);
+ break;
+ case DLS_TASK_SEARCH:
+ g_free(task->ut.search.query);
+ if (task->ut.search.filter)
+ g_variant_unref(task->ut.search.filter);
+ g_free(task->ut.search.sort_by);
+ break;
+ case DLS_TASK_GET_RESOURCE:
+ if (task->ut.resource.filter)
+ g_variant_unref(task->ut.resource.filter);
+ g_free(task->ut.resource.protocol_info);
+ break;
+ case DLS_TASK_SET_PROTOCOL_INFO:
+ if (task->ut.protocol_info.protocol_info)
+ g_free(task->ut.protocol_info.protocol_info);
+ break;
+ case DLS_TASK_UPLOAD_TO_ANY:
+ case DLS_TASK_UPLOAD:
+ g_free(task->ut.upload.display_name);
+ g_free(task->ut.upload.file_path);
+ break;
+ case DLS_TASK_CREATE_CONTAINER:
+ case DLS_TASK_CREATE_CONTAINER_IN_ANY:
+ g_free(task->ut.create_container.display_name);
+ g_free(task->ut.create_container.type);
+ g_variant_unref(task->ut.create_container.child_types);
+ break;
+ case DLS_TASK_UPDATE_OBJECT:
+ if (task->ut.update.to_add_update)
+ g_variant_unref(task->ut.update.to_add_update);
+ if (task->ut.update.to_delete)
+ g_variant_unref(task->ut.update.to_delete);
+ break;
+ case DLS_TASK_CREATE_PLAYLIST:
+ case DLS_TASK_CREATE_PLAYLIST_IN_ANY:
+ g_free(task->ut.playlist.title);
+ g_free(task->ut.playlist.creator);
+ g_free(task->ut.playlist.genre);
+ g_free(task->ut.playlist.desc);
+ if (task->ut.playlist.item_path)
+ g_variant_unref(task->ut.playlist.item_path);
+ break;
+ default:
+ break;
+ }
+
+ g_free(task->target.path);
+ g_free(task->target.root_path);
+ g_free(task->target.id);
+
+ if (task->result)
+ g_variant_unref(task->result);
+
+ g_free(task);
+}
+
+static gboolean prv_set_task_target_info(dls_task_t *task, const gchar *path,
+ GError **error)
+{
+ task->target.path = g_strdup(path);
+ g_strstrip(task->target.path);
+
+ return dls_server_get_object_info(path, &task->target.root_path,
+ &task->target.id,
+ &task->target.device, error);
+}
+
+static dls_task_t *prv_m2spec_task_new(dls_task_type_t type,
+ dleyna_connector_msg_id_t invocation,
+ const gchar *path,
+ const gchar *result_format,
+ GError **error,
+ gboolean synchronous)
+{
+ dls_task_t *task;
+
+ if (synchronous) {
+ task = g_new0(dls_task_t, 1);
+ task->synchronous = TRUE;
+ } else {
+ task = (dls_task_t *)g_new0(dls_async_task_t, 1);
+ }
+
+ if (!prv_set_task_target_info(task, path, error)) {
+ prv_delete(task);
+ task = NULL;
+
+ goto finished;
+ }
+
+ task->type = type;
+ task->invocation = invocation;
+ task->result_format = result_format;
+
+finished:
+
+ return task;
+}
+
+dls_task_t *dls_task_get_children_new(dleyna_connector_msg_id_t invocation,
+ const gchar *path, GVariant *parameters,
+ gboolean items, gboolean containers,
+ GError **error)
+{
+ dls_task_t *task;
+
+ task = prv_m2spec_task_new(DLS_TASK_GET_CHILDREN, invocation, path,
+ "(@aa{sv})", error, FALSE);
+ if (!task)
+ goto finished;
+
+ task->ut.get_children.containers = containers;
+ task->ut.get_children.items = items;
+
+ g_variant_get(parameters, "(uu@as)",
+ &task->ut.get_children.start,
+ &task->ut.get_children.count,
+ &task->ut.get_children.filter);
+
+ task->ut.get_children.sort_by = g_strdup("");
+
+finished:
+
+ return task;
+}
+
+dls_task_t *dls_task_get_children_ex_new(dleyna_connector_msg_id_t invocation,
+ const gchar *path,
+ GVariant *parameters, gboolean items,
+ gboolean containers,
+ GError **error)
+{
+ dls_task_t *task;
+
+ task = prv_m2spec_task_new(DLS_TASK_GET_CHILDREN, invocation, path,
+ "(@aa{sv})", error, FALSE);
+ if (!task)
+ goto finished;
+
+ task->ut.get_children.containers = containers;
+ task->ut.get_children.items = items;
+
+ g_variant_get(parameters, "(uu@ass)",
+ &task->ut.get_children.start,
+ &task->ut.get_children.count,
+ &task->ut.get_children.filter,
+ &task->ut.get_children.sort_by);
+
+finished:
+
+ return task;
+}
+
+dls_task_t *dls_task_get_prop_new(dleyna_connector_msg_id_t invocation,
+ const gchar *path, GVariant *parameters,
+ GError **error)
+{
+ dls_task_t *task;
+
+ task = prv_m2spec_task_new(DLS_TASK_GET_PROP, invocation, path, "(v)",
+ error, FALSE);
+ if (!task)
+ goto finished;
+
+ g_variant_get(parameters, "(ss)", &task->ut.get_prop.interface_name,
+ &task->ut.get_prop.prop_name);
+
+ g_strstrip(task->ut.get_prop.interface_name);
+ g_strstrip(task->ut.get_prop.prop_name);
+
+finished:
+
+ return task;
+}
+
+dls_task_t *dls_task_get_props_new(dleyna_connector_msg_id_t invocation,
+ const gchar *path, GVariant *parameters,
+ GError **error)
+{
+ dls_task_t *task;
+
+ task = prv_m2spec_task_new(DLS_TASK_GET_ALL_PROPS, invocation, path,
+ "(@a{sv})", error, FALSE);
+ if (!task)
+ goto finished;
+
+ g_variant_get(parameters, "(s)", &task->ut.get_props.interface_name);
+ g_strstrip(task->ut.get_props.interface_name);
+
+finished:
+
+ return task;
+}
+
+dls_task_t *dls_task_search_new(dleyna_connector_msg_id_t invocation,
+ const gchar *path, GVariant *parameters,
+ GError **error)
+{
+ dls_task_t *task;
+
+ task = prv_m2spec_task_new(DLS_TASK_SEARCH, invocation, path,
+ "(@aa{sv})", error, FALSE);
+ if (!task)
+ goto finished;
+
+ g_variant_get(parameters, "(suu@as)", &task->ut.search.query,
+ &task->ut.search.start, &task->ut.search.count,
+ &task->ut.search.filter);
+
+ task->ut.search.sort_by = g_strdup("");
+
+finished:
+ return task;
+}
+
+dls_task_t *dls_task_search_ex_new(dleyna_connector_msg_id_t invocation,
+ const gchar *path, GVariant *parameters,
+ GError **error)
+{
+ dls_task_t *task;
+
+ task = prv_m2spec_task_new(DLS_TASK_SEARCH, invocation, path,
+ "(@aa{sv}u)", error, FALSE);
+ if (!task)
+ goto finished;
+
+ g_variant_get(parameters, "(suu@ass)", &task->ut.search.query,
+ &task->ut.search.start, &task->ut.search.count,
+ &task->ut.search.filter, &task->ut.search.sort_by);
+
+ task->multiple_retvals = TRUE;
+
+finished:
+
+ return task;
+}
+
+dls_task_t *dls_task_get_resource_new(dleyna_connector_msg_id_t invocation,
+ const gchar *path, GVariant *parameters,
+ GError **error)
+{
+ dls_task_t *task;
+
+ task = prv_m2spec_task_new(DLS_TASK_GET_RESOURCE, invocation, path,
+ "(@a{sv})", error, FALSE);
+ if (!task)
+ goto finished;
+
+ g_variant_get(parameters, "(s@as)",
+ &task->ut.resource.protocol_info,
+ &task->ut.resource.filter);
+
+finished:
+
+ return task;
+}
+
+dls_task_t *dls_task_set_protocol_info_new(dleyna_connector_msg_id_t invocation,
+ GVariant *parameters)
+{
+ dls_task_t *task = g_new0(dls_task_t, 1);
+
+ task->type = DLS_TASK_SET_PROTOCOL_INFO;
+ task->invocation = invocation;
+ task->synchronous = TRUE;
+ g_variant_get(parameters, "(s)", &task->ut.protocol_info.protocol_info);
+
+ return task;
+}
+
+static dls_task_t *prv_upload_new_generic(dls_task_type_t type,
+ dleyna_connector_msg_id_t invocation,
+ const gchar *path,
+ GVariant *parameters,
+ GError **error)
+{
+ dls_task_t *task;
+
+ task = prv_m2spec_task_new(type, invocation, path,
+ "(uo)", error, FALSE);
+ if (!task)
+ goto finished;
+
+ g_variant_get(parameters, "(ss)", &task->ut.upload.display_name,
+ &task->ut.upload.file_path);
+ g_strstrip(task->ut.upload.file_path);
+ task->multiple_retvals = TRUE;
+
+finished:
+
+ return task;
+}
+
+dls_task_t *dls_task_prefer_local_addresses_new(
+ dleyna_connector_msg_id_t invocation,
+ GVariant *parameters)
+{
+ dls_task_t *task = g_new0(dls_task_t, 1);
+
+ task->type = DLS_TASK_SET_PREFER_LOCAL_ADDRESSES;
+ task->invocation = invocation;
+ task->synchronous = TRUE;
+ g_variant_get(parameters, "(b)",
+ &task->ut.prefer_local_addresses.prefer);
+
+ return task;
+}
+
+dls_task_t *dls_task_upload_to_any_new(dleyna_connector_msg_id_t invocation,
+ const gchar *path, GVariant *parameters,
+ GError **error)
+{
+ return prv_upload_new_generic(DLS_TASK_UPLOAD_TO_ANY, invocation,
+ path, parameters, error);
+}
+
+dls_task_t *dls_task_upload_new(dleyna_connector_msg_id_t invocation,
+ const gchar *path, GVariant *parameters,
+ GError **error)
+{
+ return prv_upload_new_generic(DLS_TASK_UPLOAD, invocation,
+ path, parameters, error);
+}
+
+dls_task_t *dls_task_get_upload_status_new(dleyna_connector_msg_id_t invocation,
+ const gchar *path,
+ GVariant *parameters,
+ GError **error)
+{
+ dls_task_t *task;
+
+ task = prv_m2spec_task_new(DLS_TASK_GET_UPLOAD_STATUS, invocation, path,
+ "(stt)", error, TRUE);
+ if (!task)
+ goto finished;
+
+ g_variant_get(parameters, "(u)",
+ &task->ut.upload_action.upload_id);
+ task->multiple_retvals = TRUE;
+
+finished:
+
+ return task;
+}
+
+dls_task_t *dls_task_get_upload_ids_new(dleyna_connector_msg_id_t invocation,
+ const gchar *path,
+ GError **error)
+{
+ dls_task_t *task;
+
+ task = prv_m2spec_task_new(DLS_TASK_GET_UPLOAD_IDS, invocation, path,
+ "(@au)", error, TRUE);
+ if (!task)
+ goto finished;
+
+finished:
+
+ return task;
+}
+
+dls_task_t *dls_task_cancel_upload_new(dleyna_connector_msg_id_t invocation,
+ const gchar *path,
+ GVariant *parameters,
+ GError **error)
+{
+ dls_task_t *task;
+
+ task = prv_m2spec_task_new(DLS_TASK_CANCEL_UPLOAD, invocation, path,
+ NULL, error, TRUE);
+ if (!task)
+ goto finished;
+
+ g_variant_get(parameters, "(u)",
+ &task->ut.upload_action.upload_id);
+
+finished:
+
+ return task;
+}
+
+dls_task_t *dls_task_delete_new(dleyna_connector_msg_id_t invocation,
+ const gchar *path,
+ GError **error)
+{
+ dls_task_t *task;
+
+ task = prv_m2spec_task_new(DLS_TASK_DELETE_OBJECT, invocation,
+ path, NULL, error, FALSE);
+ return task;
+}
+
+dls_task_t *dls_task_create_container_new_generic(
+ dleyna_connector_msg_id_t invocation,
+ dls_task_type_t type,
+ const gchar *path,
+ GVariant *parameters,
+ GError **error)
+{
+ dls_task_t *task;
+
+ task = prv_m2spec_task_new(type, invocation, path,
+ "(@o)", error, FALSE);
+ if (!task)
+ goto finished;
+
+ g_variant_get(parameters, "(ss@as)",
+ &task->ut.create_container.display_name,
+ &task->ut.create_container.type,
+ &task->ut.create_container.child_types);
+
+finished:
+
+ return task;
+}
+
+dls_task_t *dls_task_create_playlist_new(dleyna_connector_msg_id_t invocation,
+ dls_task_type_t type,
+ const gchar *path,
+ GVariant *parameters,
+ GError **error)
+{
+ dls_task_t *task;
+
+ task = prv_m2spec_task_new(type, invocation, path,
+ "(uo)", error, FALSE);
+ if (!task)
+ goto finished;
+
+ g_variant_get(parameters, "(ssss@ao)",
+ &task->ut.playlist.title,
+ &task->ut.playlist.creator,
+ &task->ut.playlist.genre,
+ &task->ut.playlist.desc,
+ &task->ut.playlist.item_path);
+
+ task->multiple_retvals = TRUE;
+
+finished:
+
+ return task;
+
+}
+
+dls_task_t *dls_task_update_new(dleyna_connector_msg_id_t invocation,
+ const gchar *path, GVariant *parameters,
+ GError **error)
+{
+ dls_task_t *task;
+
+ task = prv_m2spec_task_new(DLS_TASK_UPDATE_OBJECT, invocation, path,
+ NULL, error, FALSE);
+ if (!task)
+ goto finished;
+
+ g_variant_get(parameters, "(@a{sv}@as)",
+ &task->ut.update.to_add_update,
+ &task->ut.update.to_delete);
+
+finished:
+
+ return task;
+}
+
+void dls_task_complete(dls_task_t *task)
+{
+ GVariant *variant = NULL;
+
+ if (!task)
+ goto finished;
+
+ if (task->invocation) {
+ if (task->result_format) {
+ if (task->multiple_retvals)
+ variant = task->result;
+ else
+ variant = g_variant_new(task->result_format,
+ task->result);
+ }
+ dls_server_get_connector()->return_response(task->invocation,
+ variant);
+ task->invocation = NULL;
+ }
+
+finished:
+
+ return;
+}
+
+void dls_task_fail(dls_task_t *task, GError *error)
+{
+ if (!task)
+ goto finished;
+
+ if (task->invocation) {
+ dls_server_get_connector()->return_error(task->invocation,
+ error);
+ task->invocation = NULL;
+ }
+
+finished:
+
+ return;
+}
+
+void dls_task_cancel(dls_task_t *task)
+{
+ GError *error;
+
+ if (!task)
+ goto finished;
+
+ if (task->invocation) {
+ error = g_error_new(DLEYNA_SERVER_ERROR, DLEYNA_ERROR_CANCELLED,
+ "Operation cancelled.");
+ dls_server_get_connector()->return_error(task->invocation,
+ error);
+ task->invocation = NULL;
+ g_error_free(error);
+ }
+
+ if (!task->synchronous)
+ dls_async_task_cancel((dls_async_task_t *)task);
+
+finished:
+
+ return;
+}
+
+void dls_task_delete(dls_task_t *task)
+{
+ GError *error;
+
+ if (!task)
+ goto finished;
+
+ if (task->invocation) {
+ error = g_error_new(DLEYNA_SERVER_ERROR, DLEYNA_ERROR_DIED,
+ "Unable to complete command.");
+ dls_server_get_connector()->return_error(task->invocation,
+ error);
+ g_error_free(error);
+ }
+
+ prv_delete(task);
+
+finished:
+
+ return;
+}
diff --git a/libdleyna/server/task.h b/libdleyna/server/task.h
new file mode 100644
index 0000000..0257a9f
--- /dev/null
+++ b/libdleyna/server/task.h
@@ -0,0 +1,267 @@
+/*
+ * dLeyna
+ *
+ * Copyright (C) 2012-2013 Intel Corporation. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms and conditions of the GNU Lesser General Public License,
+ * version 2.1, as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License
+ * for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with this program; if not, write to the Free Software Foundation, Inc.,
+ * 51 Franklin St - Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Mark Ryan <mark.d.ryan@intel.com>
+ *
+ */
+
+#ifndef DLS_TASK_H__
+#define DLS_TASK_H__
+
+#include <gio/gio.h>
+#include <glib.h>
+
+#include <libdleyna/core/connector.h>
+#include <libdleyna/core/task-atom.h>
+
+#include "server.h"
+
+enum dls_task_type_t_ {
+ DLS_TASK_GET_VERSION,
+ DLS_TASK_GET_SERVERS,
+ DLS_TASK_GET_CHILDREN,
+ DLS_TASK_GET_ALL_PROPS,
+ DLS_TASK_GET_PROP,
+ DLS_TASK_SEARCH,
+ DLS_TASK_GET_RESOURCE,
+ DLS_TASK_SET_PREFER_LOCAL_ADDRESSES,
+ DLS_TASK_SET_PROTOCOL_INFO,
+ DLS_TASK_UPLOAD_TO_ANY,
+ DLS_TASK_UPLOAD,
+ DLS_TASK_GET_UPLOAD_STATUS,
+ DLS_TASK_GET_UPLOAD_IDS,
+ DLS_TASK_CANCEL_UPLOAD,
+ DLS_TASK_DELETE_OBJECT,
+ DLS_TASK_CREATE_CONTAINER,
+ DLS_TASK_CREATE_CONTAINER_IN_ANY,
+ DLS_TASK_UPDATE_OBJECT,
+ DLS_TASK_CREATE_PLAYLIST,
+ DLS_TASK_CREATE_PLAYLIST_IN_ANY
+};
+typedef enum dls_task_type_t_ dls_task_type_t;
+
+typedef void (*dls_cancel_task_t)(void *handle);
+
+typedef struct dls_task_get_children_t_ dls_task_get_children_t;
+struct dls_task_get_children_t_ {
+ gboolean containers;
+ gboolean items;
+ guint start;
+ guint count;
+ GVariant *filter;
+ gchar *sort_by;
+};
+
+typedef struct dls_task_get_props_t_ dls_task_get_props_t;
+struct dls_task_get_props_t_ {
+ gchar *interface_name;
+};
+
+typedef struct dls_task_get_prop_t_ dls_task_get_prop_t;
+struct dls_task_get_prop_t_ {
+ gchar *prop_name;
+ gchar *interface_name;
+};
+
+typedef struct dls_task_search_t_ dls_task_search_t;
+struct dls_task_search_t_ {
+ gchar *query;
+ guint start;
+ guint count;
+ gchar *sort_by;
+ GVariant *filter;
+};
+
+typedef struct dls_task_get_resource_t_ dls_task_get_resource_t;
+struct dls_task_get_resource_t_ {
+ gchar *protocol_info;
+ GVariant *filter;
+};
+
+typedef struct dls_task_set_prefer_local_addresses_t_
+ dls_task_set_prefer_local_addresses_t;
+struct dls_task_set_prefer_local_addresses_t_ {
+ gboolean prefer;
+};
+
+typedef struct dls_task_set_protocol_info_t_ dls_task_set_protocol_info_t;
+struct dls_task_set_protocol_info_t_ {
+ gchar *protocol_info;
+};
+
+typedef struct dls_task_upload_t_ dls_task_upload_t;
+struct dls_task_upload_t_ {
+ gchar *display_name;
+ gchar *file_path;
+};
+
+typedef struct dls_task_upload_action_t_ dls_task_upload_action_t;
+struct dls_task_upload_action_t_ {
+ guint upload_id;
+};
+
+typedef struct dls_task_create_container_t_ dls_task_create_container_t;
+struct dls_task_create_container_t_ {
+ gchar *display_name;
+ gchar *type;
+ GVariant *child_types;
+};
+
+typedef struct dls_task_update_t_ dls_task_update_t;
+struct dls_task_update_t_ {
+ GVariant *to_add_update;
+ GVariant *to_delete;
+};
+
+typedef struct dls_task_create_playlist_t_ dls_task_create_playlist_t;
+struct dls_task_create_playlist_t_ {
+ gchar *title;
+ gchar *creator;
+ gchar *genre;
+ gchar *desc;
+ GVariant *item_path;
+};
+
+typedef struct dls_task_target_info_t_ dls_task_target_info_t;
+struct dls_task_target_info_t_ {
+ gchar *path;
+ gchar *root_path;
+ gchar *id;
+ dls_device_t *device;
+};
+
+typedef struct dls_task_t_ dls_task_t;
+struct dls_task_t_ {
+ dleyna_task_atom_t atom; /* pseudo inheritance - MUST be first field */
+ dls_task_type_t type;
+ dls_task_target_info_t target;
+ const gchar *result_format;
+ GVariant *result;
+ dleyna_connector_msg_id_t invocation;
+ gboolean synchronous;
+ gboolean multiple_retvals;
+ union {
+ dls_task_get_children_t get_children;
+ dls_task_get_props_t get_props;
+ dls_task_get_prop_t get_prop;
+ dls_task_search_t search;
+ dls_task_get_resource_t resource;
+ dls_task_set_prefer_local_addresses_t prefer_local_addresses;
+ dls_task_set_protocol_info_t protocol_info;
+ dls_task_upload_t upload;
+ dls_task_upload_action_t upload_action;
+ dls_task_create_container_t create_container;
+ dls_task_update_t update;
+ dls_task_create_playlist_t playlist;
+ } ut;
+};
+
+dls_task_t *dls_task_get_version_new(dleyna_connector_msg_id_t invocation);
+
+dls_task_t *dls_task_get_servers_new(dleyna_connector_msg_id_t invocation);
+
+dls_task_t *dls_task_get_children_new(dleyna_connector_msg_id_t invocation,
+ const gchar *path, GVariant *parameters,
+ gboolean items, gboolean containers,
+ GError **error);
+
+dls_task_t *dls_task_get_children_ex_new(dleyna_connector_msg_id_t invocation,
+ const gchar *path,
+ GVariant *parameters, gboolean items,
+ gboolean containers,
+ GError **error);
+
+dls_task_t *dls_task_get_prop_new(dleyna_connector_msg_id_t invocation,
+ const gchar *path, GVariant *parameters,
+ GError **error);
+
+dls_task_t *dls_task_get_props_new(dleyna_connector_msg_id_t invocation,
+ const gchar *path, GVariant *parameters,
+ GError **error);
+
+dls_task_t *dls_task_search_new(dleyna_connector_msg_id_t invocation,
+ const gchar *path, GVariant *parameters,
+ GError **error);
+
+dls_task_t *dls_task_search_ex_new(dleyna_connector_msg_id_t invocation,
+ const gchar *path, GVariant *parameters,
+ GError **error);
+
+dls_task_t *dls_task_get_resource_new(dleyna_connector_msg_id_t invocation,
+ const gchar *path, GVariant *parameters,
+ GError **error);
+
+dls_task_t *dls_task_set_protocol_info_new(dleyna_connector_msg_id_t invocation,
+ GVariant *parameters);
+
+dls_task_t *dls_task_prefer_local_addresses_new(
+ dleyna_connector_msg_id_t invocation,
+ GVariant *parameters);
+
+dls_task_t *dls_task_upload_to_any_new(dleyna_connector_msg_id_t invocation,
+ const gchar *path, GVariant *parameters,
+ GError **error);
+
+dls_task_t *dls_task_upload_new(dleyna_connector_msg_id_t invocation,
+ const gchar *path, GVariant *parameters,
+ GError **error);
+
+dls_task_t *dls_task_get_upload_status_new(dleyna_connector_msg_id_t invocation,
+ const gchar *path,
+ GVariant *parameters,
+ GError **error);
+
+dls_task_t *dls_task_get_upload_ids_new(dleyna_connector_msg_id_t invocation,
+ const gchar *path,
+ GError **error);
+
+dls_task_t *dls_task_cancel_upload_new(dleyna_connector_msg_id_t invocation,
+ const gchar *path,
+ GVariant *parameters,
+ GError **error);
+
+dls_task_t *dls_task_delete_new(dleyna_connector_msg_id_t invocation,
+ const gchar *path,
+ GError **error);
+
+dls_task_t *dls_task_create_container_new_generic(
+ dleyna_connector_msg_id_t invocation,
+ dls_task_type_t type,
+ const gchar *path,
+ GVariant *parameters,
+ GError **error);
+
+dls_task_t *dls_task_create_playlist_new(dleyna_connector_msg_id_t invocation,
+ dls_task_type_t type,
+ const gchar *path,
+ GVariant *parameters,
+ GError **error);
+
+dls_task_t *dls_task_update_new(dleyna_connector_msg_id_t invocation,
+ const gchar *path, GVariant *parameters,
+ GError **error);
+
+void dls_task_cancel(dls_task_t *task);
+
+void dls_task_complete(dls_task_t *task);
+
+void dls_task_fail(dls_task_t *task, GError *error);
+
+void dls_task_delete(dls_task_t *task);
+
+#endif /* DLS_TASK_H__ */
diff --git a/libdleyna/server/upnp.c b/libdleyna/server/upnp.c
new file mode 100644
index 0000000..dcb9876
--- /dev/null
+++ b/libdleyna/server/upnp.c
@@ -0,0 +1,1110 @@
+/*
+ * dLeyna
+ *
+ * Copyright (C) 2012-2013 Intel Corporation. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms and conditions of the GNU Lesser General Public License,
+ * version 2.1, as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License
+ * for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with this program; if not, write to the Free Software Foundation, Inc.,
+ * 51 Franklin St - Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Mark Ryan <mark.d.ryan@intel.com>
+ *
+ */
+
+#include <string.h>
+
+#include <libgssdp/gssdp-resource-browser.h>
+#include <libgupnp/gupnp-control-point.h>
+#include <libgupnp/gupnp-error.h>
+
+#include <libdleyna/core/error.h>
+#include <libdleyna/core/log.h>
+#include <libdleyna/core/service-task.h>
+
+#include "async.h"
+#include "device.h"
+#include "interface.h"
+#include "path.h"
+#include "search.h"
+#include "sort.h"
+#include "upnp.h"
+
+struct dls_upnp_t_ {
+ dleyna_connector_id_t connection;
+ const dleyna_connector_dispatch_cb_t *interface_info;
+ GHashTable *filter_map;
+ GHashTable *property_map;
+ dls_upnp_callback_t found_server;
+ dls_upnp_callback_t lost_server;
+ GUPnPContextManager *context_manager;
+ void *user_data;
+ GHashTable *server_udn_map;
+ GHashTable *server_uc_map;
+ guint counter;
+};
+
+/* Private structure used in service task */
+typedef struct prv_device_new_ct_t_ prv_device_new_ct_t;
+struct prv_device_new_ct_t_ {
+ dls_upnp_t *upnp;
+ char *udn;
+ dls_device_t *device;
+ const dleyna_task_queue_key_t *queue_id;
+};
+
+static void prv_device_new_free(prv_device_new_ct_t *priv_t)
+{
+ if (priv_t) {
+ g_free(priv_t->udn);
+ g_free(priv_t);
+ }
+}
+
+static void prv_device_chain_end(gboolean cancelled, gpointer data)
+{
+ dls_device_t *device;
+ prv_device_new_ct_t *priv_t = (prv_device_new_ct_t *)data;
+
+ DLEYNA_LOG_DEBUG("Enter");
+
+ device = priv_t->device;
+
+ if (cancelled)
+ goto on_clear;
+
+ DLEYNA_LOG_DEBUG("Notify new server available: %s", device->path);
+ g_hash_table_insert(priv_t->upnp->server_udn_map, g_strdup(priv_t->udn),
+ device);
+ priv_t->upnp->found_server(device->path, priv_t->upnp->user_data);
+
+on_clear:
+
+ g_hash_table_remove(priv_t->upnp->server_uc_map, priv_t->udn);
+ prv_device_new_free(priv_t);
+
+ if (cancelled)
+ dls_device_delete(device);
+
+ DLEYNA_LOG_DEBUG_NL();
+}
+
+static void prv_server_available_cb(GUPnPControlPoint *cp,
+ GUPnPDeviceProxy *proxy,
+ gpointer user_data)
+{
+ dls_upnp_t *upnp = user_data;
+ const char *udn;
+ dls_device_t *device;
+ const gchar *ip_address;
+ dls_device_context_t *context;
+ const dleyna_task_queue_key_t *queue_id;
+ unsigned int i;
+ prv_device_new_ct_t *priv_t;
+
+ udn = gupnp_device_info_get_udn((GUPnPDeviceInfo *)proxy);
+ if (!udn)
+ goto on_error;
+
+ ip_address = gupnp_context_get_host_ip(
+ gupnp_control_point_get_context(cp));
+
+ DLEYNA_LOG_DEBUG("UDN %s", udn);
+ DLEYNA_LOG_DEBUG("IP Address %s", ip_address);
+
+ device = g_hash_table_lookup(upnp->server_udn_map, udn);
+
+ if (!device) {
+ priv_t = g_hash_table_lookup(upnp->server_uc_map, udn);
+
+ if (priv_t)
+ device = priv_t->device;
+ }
+
+ if (!device) {
+ DLEYNA_LOG_DEBUG("Device not found. Adding");
+ DLEYNA_LOG_DEBUG_NL();
+
+ priv_t = g_new0(prv_device_new_ct_t, 1);
+
+ queue_id = dleyna_task_processor_add_queue(
+ dls_server_get_task_processor(),
+ dleyna_service_task_create_source(),
+ DLS_SERVER_SINK,
+ DLEYNA_TASK_QUEUE_FLAG_AUTO_REMOVE,
+ dleyna_service_task_process_cb,
+ dleyna_service_task_cancel_cb,
+ dleyna_service_task_delete_cb);
+ dleyna_task_queue_set_finally(queue_id, prv_device_chain_end);
+ dleyna_task_queue_set_user_data(queue_id, priv_t);
+
+ device = dls_device_new(upnp->connection, proxy, ip_address,
+ upnp->interface_info,
+ upnp->property_map, upnp->counter,
+ queue_id);
+
+ upnp->counter++;
+
+ priv_t->upnp = upnp;
+ priv_t->udn = g_strdup(udn);
+ priv_t->queue_id = queue_id;
+ priv_t->device = device;
+
+ g_hash_table_insert(upnp->server_uc_map, g_strdup(udn), priv_t);
+ } else {
+ DLEYNA_LOG_DEBUG("Device Found");
+
+ for (i = 0; i < device->contexts->len; ++i) {
+ context = g_ptr_array_index(device->contexts, i);
+ if (!strcmp(context->ip_address, ip_address))
+ break;
+ }
+
+ if (i == device->contexts->len) {
+ DLEYNA_LOG_DEBUG("Adding Context");
+ (void) dls_device_append_new_context(device, ip_address,
+ proxy);
+ }
+
+ DLEYNA_LOG_DEBUG_NL();
+ }
+
+on_error:
+
+ return;
+}
+
+static gboolean prv_subscribe_to_contents_change(gpointer user_data)
+{
+ dls_device_t *device = user_data;
+
+ device->timeout_id = 0;
+ dls_device_subscribe_to_contents_change(device);
+
+ return FALSE;
+}
+
+static void prv_server_unavailable_cb(GUPnPControlPoint *cp,
+ GUPnPDeviceProxy *proxy,
+ gpointer user_data)
+{
+ dls_upnp_t *upnp = user_data;
+ const char *udn;
+ dls_device_t *device;
+ const gchar *ip_address;
+ unsigned int i;
+ dls_device_context_t *context;
+ gboolean subscribed;
+ gboolean under_construction = FALSE;
+ prv_device_new_ct_t *priv_t;
+
+ DLEYNA_LOG_DEBUG("Enter");
+
+ udn = gupnp_device_info_get_udn((GUPnPDeviceInfo *)proxy);
+ if (!udn)
+ goto on_error;
+
+ ip_address = gupnp_context_get_host_ip(
+ gupnp_control_point_get_context(cp));
+
+ DLEYNA_LOG_DEBUG("UDN %s", udn);
+ DLEYNA_LOG_DEBUG("IP Address %s", ip_address);
+
+ device = g_hash_table_lookup(upnp->server_udn_map, udn);
+
+ if (!device) {
+ priv_t = g_hash_table_lookup(upnp->server_uc_map, udn);
+
+ if (priv_t) {
+ device = priv_t->device;
+ under_construction = TRUE;
+ }
+ }
+
+ if (!device) {
+ DLEYNA_LOG_WARNING("Device not found. Ignoring");
+ goto on_error;
+ }
+
+ for (i = 0; i < device->contexts->len; ++i) {
+ context = g_ptr_array_index(device->contexts, i);
+ if (!strcmp(context->ip_address, ip_address))
+ break;
+ }
+
+ if (i >= device->contexts->len)
+ goto on_error;
+
+ subscribed = context->subscribed;
+
+ (void) g_ptr_array_remove_index(device->contexts, i);
+
+ if (device->contexts->len == 0) {
+ if (!under_construction) {
+ DLEYNA_LOG_DEBUG("Last Context lost. Delete device");
+ upnp->lost_server(device->path, upnp->user_data);
+ g_hash_table_remove(upnp->server_udn_map, udn);
+ } else {
+ DLEYNA_LOG_WARNING(
+ "Device under construction. Cancelling");
+
+ dleyna_task_processor_cancel_queue(priv_t->queue_id);
+ }
+ } else if (subscribed && !device->timeout_id) {
+ DLEYNA_LOG_DEBUG("Subscribe on new context");
+
+ device->timeout_id = g_timeout_add_seconds(1,
+ prv_subscribe_to_contents_change,
+ device);
+ }
+
+on_error:
+
+ DLEYNA_LOG_DEBUG("Exit");
+ DLEYNA_LOG_DEBUG_NL();
+
+ return;
+}
+
+static void prv_on_context_available(GUPnPContextManager *context_manager,
+ GUPnPContext *context,
+ gpointer user_data)
+{
+ dls_upnp_t *upnp = user_data;
+ GUPnPControlPoint *cp;
+
+ cp = gupnp_control_point_new(
+ context,
+ "urn:schemas-upnp-org:device:MediaServer:1");
+
+ g_signal_connect(cp, "device-proxy-available",
+ G_CALLBACK(prv_server_available_cb), upnp);
+
+ g_signal_connect(cp, "device-proxy-unavailable",
+ G_CALLBACK(prv_server_unavailable_cb), upnp);
+
+ gssdp_resource_browser_set_active(GSSDP_RESOURCE_BROWSER(cp), TRUE);
+ gupnp_context_manager_manage_control_point(upnp->context_manager, cp);
+ g_object_unref(cp);
+}
+
+dls_upnp_t *dls_upnp_new(dleyna_connector_id_t connection,
+ const dleyna_connector_dispatch_cb_t *dispatch_table,
+ dls_upnp_callback_t found_server,
+ dls_upnp_callback_t lost_server,
+ void *user_data)
+{
+ dls_upnp_t *upnp = g_new0(dls_upnp_t, 1);
+
+ upnp->connection = connection;
+ upnp->interface_info = dispatch_table;
+ upnp->user_data = user_data;
+ upnp->found_server = found_server;
+ upnp->lost_server = lost_server;
+
+ upnp->server_udn_map = g_hash_table_new_full(g_str_hash, g_str_equal,
+ g_free,
+ dls_device_delete);
+
+ upnp->server_uc_map = g_hash_table_new_full(g_str_hash, g_str_equal,
+ g_free, NULL);
+
+ dls_prop_maps_new(&upnp->property_map, &upnp->filter_map);
+
+ upnp->context_manager = gupnp_context_manager_create(0);
+
+ g_signal_connect(upnp->context_manager, "context-available",
+ G_CALLBACK(prv_on_context_available),
+ upnp);
+
+ return upnp;
+}
+
+void dls_upnp_delete(dls_upnp_t *upnp)
+{
+ if (upnp) {
+ g_object_unref(upnp->context_manager);
+ g_hash_table_unref(upnp->property_map);
+ g_hash_table_unref(upnp->filter_map);
+ g_hash_table_unref(upnp->server_udn_map);
+ g_hash_table_unref(upnp->server_uc_map);
+ g_free(upnp);
+ }
+}
+
+GVariant *dls_upnp_get_server_ids(dls_upnp_t *upnp)
+{
+ GVariantBuilder vb;
+ GHashTableIter iter;
+ gpointer value;
+ dls_device_t *device;
+ GVariant *retval;
+
+ DLEYNA_LOG_DEBUG("Enter");
+
+ g_variant_builder_init(&vb, G_VARIANT_TYPE("ao"));
+
+ g_hash_table_iter_init(&iter, upnp->server_udn_map);
+ while (g_hash_table_iter_next(&iter, NULL, &value)) {
+ device = value;
+ DLEYNA_LOG_DEBUG("Have device %s", device->path);
+ g_variant_builder_add(&vb, "o", device->path);
+ }
+
+ retval = g_variant_ref_sink(g_variant_builder_end(&vb));
+
+ DLEYNA_LOG_DEBUG("Exit");
+
+ return retval;
+}
+
+GHashTable *dls_upnp_get_server_udn_map(dls_upnp_t *upnp)
+{
+ return upnp->server_udn_map;
+}
+
+void dls_upnp_get_children(dls_upnp_t *upnp, dls_client_t *client,
+ dls_task_t *task,
+ dls_upnp_task_complete_t cb)
+{
+ dls_async_task_t *cb_data = (dls_async_task_t *)task;
+ dls_async_bas_t *cb_task_data;
+ gchar *upnp_filter = NULL;
+ gchar *sort_by = NULL;
+
+ DLEYNA_LOG_DEBUG("Enter");
+
+ DLEYNA_LOG_DEBUG("Path: %s", task->target.path);
+ DLEYNA_LOG_DEBUG("Start: %u", task->ut.get_children.start);
+ DLEYNA_LOG_DEBUG("Count: %u", task->ut.get_children.count);
+
+ cb_data->cb = cb;
+ cb_task_data = &cb_data->ut.bas;
+
+ cb_task_data->filter_mask =
+ dls_props_parse_filter(upnp->filter_map,
+ task->ut.get_children.filter,
+ &upnp_filter);
+
+ DLEYNA_LOG_DEBUG("Filter Mask 0x%"G_GUINT64_FORMAT"x",
+ cb_task_data->filter_mask);
+
+ sort_by = dls_sort_translate_sort_string(upnp->filter_map,
+ task->ut.get_children.sort_by);
+ if (!sort_by) {
+ DLEYNA_LOG_WARNING("Invalid Sort Criteria");
+
+ cb_data->error = g_error_new(DLEYNA_SERVER_ERROR,
+ DLEYNA_ERROR_BAD_QUERY,
+ "Sort Criteria are not valid");
+ goto on_error;
+ }
+
+ DLEYNA_LOG_DEBUG("Sort By %s", sort_by);
+
+ cb_task_data->protocol_info = client->protocol_info;
+
+ dls_device_get_children(client, task, upnp_filter, sort_by);
+
+on_error:
+
+ if (!cb_data->action)
+ (void) g_idle_add(dls_async_task_complete, cb_data);
+
+ g_free(sort_by);
+ g_free(upnp_filter);
+
+ DLEYNA_LOG_DEBUG("Exit with %s", !cb_data->action ? "FAIL" : "SUCCESS");
+}
+
+void dls_upnp_get_all_props(dls_upnp_t *upnp, dls_client_t *client,
+ dls_task_t *task,
+ dls_upnp_task_complete_t cb)
+{
+ gboolean root_object;
+ dls_async_task_t *cb_data = (dls_async_task_t *)task;
+ dls_async_get_all_t *cb_task_data;
+
+ DLEYNA_LOG_DEBUG("Enter");
+
+ DLEYNA_LOG_DEBUG("Path: %s", task->target.path);
+ DLEYNA_LOG_DEBUG("Interface %s", task->ut.get_prop.interface_name);
+
+ cb_data->cb = cb;
+ cb_task_data = &cb_data->ut.get_all;
+
+ root_object = task->target.id[0] == '0' && task->target.id[1] == 0;
+
+ DLEYNA_LOG_DEBUG("Root Object = %d", root_object);
+
+ cb_task_data->protocol_info = client->protocol_info;
+
+ dls_device_get_all_props(client, task, root_object);
+
+ DLEYNA_LOG_DEBUG("Exit with SUCCESS");
+}
+
+void dls_upnp_get_prop(dls_upnp_t *upnp, dls_client_t *client,
+ dls_task_t *task,
+ dls_upnp_task_complete_t cb)
+{
+ gboolean root_object;
+ dls_async_task_t *cb_data = (dls_async_task_t *)task;
+ dls_async_get_prop_t *cb_task_data;
+ dls_prop_map_t *prop_map;
+ dls_task_get_prop_t *task_data;
+
+ DLEYNA_LOG_DEBUG("Enter");
+
+ DLEYNA_LOG_DEBUG("Path: %s", task->target.path);
+ DLEYNA_LOG_DEBUG("Interface %s", task->ut.get_prop.interface_name);
+ DLEYNA_LOG_DEBUG("Prop.%s", task->ut.get_prop.prop_name);
+
+ task_data = &task->ut.get_prop;
+ cb_data->cb = cb;
+ cb_task_data = &cb_data->ut.get_prop;
+
+ root_object = task->target.id[0] == '0' && task->target.id[1] == 0;
+
+ DLEYNA_LOG_DEBUG("Root Object = %d", root_object);
+
+ cb_task_data->protocol_info = client->protocol_info;
+ prop_map = g_hash_table_lookup(upnp->filter_map, task_data->prop_name);
+
+ dls_device_get_prop(client, task, prop_map, root_object);
+
+ DLEYNA_LOG_DEBUG("Exit with SUCCESS");
+}
+
+void dls_upnp_search(dls_upnp_t *upnp, dls_client_t *client,
+ dls_task_t *task,
+ dls_upnp_task_complete_t cb)
+{
+ gchar *upnp_filter = NULL;
+ gchar *upnp_query = NULL;
+ gchar *sort_by = NULL;
+ dls_async_task_t *cb_data = (dls_async_task_t *)task;
+ dls_async_bas_t *cb_task_data;
+
+ DLEYNA_LOG_DEBUG("Enter");
+
+ DLEYNA_LOG_DEBUG("Path: %s", task->target.path);
+ DLEYNA_LOG_DEBUG("Query: %s", task->ut.search.query);
+ DLEYNA_LOG_DEBUG("Start: %u", task->ut.search.start);
+ DLEYNA_LOG_DEBUG("Count: %u", task->ut.search.count);
+
+ cb_data->cb = cb;
+ cb_task_data = &cb_data->ut.bas;
+
+ cb_task_data->filter_mask =
+ dls_props_parse_filter(upnp->filter_map,
+ task->ut.search.filter, &upnp_filter);
+
+ DLEYNA_LOG_DEBUG("Filter Mask 0x%"G_GUINT64_FORMAT"x",
+ cb_task_data->filter_mask);
+
+ upnp_query = dls_search_translate_search_string(upnp->filter_map,
+ task->ut.search.query);
+ if (!upnp_query) {
+ DLEYNA_LOG_WARNING("Query string is not valid:%s",
+ task->ut.search.query);
+
+ cb_data->error = g_error_new(DLEYNA_SERVER_ERROR,
+ DLEYNA_ERROR_BAD_QUERY,
+ "Query string is not valid.");
+ goto on_error;
+ }
+
+ DLEYNA_LOG_DEBUG("UPnP Query %s", upnp_query);
+
+ sort_by = dls_sort_translate_sort_string(upnp->filter_map,
+ task->ut.search.sort_by);
+ if (!sort_by) {
+ DLEYNA_LOG_WARNING("Invalid Sort Criteria");
+
+ cb_data->error = g_error_new(DLEYNA_SERVER_ERROR,
+ DLEYNA_ERROR_BAD_QUERY,
+ "Sort Criteria are not valid");
+ goto on_error;
+ }
+
+ DLEYNA_LOG_DEBUG("Sort By %s", sort_by);
+
+ cb_task_data->protocol_info = client->protocol_info;
+
+ dls_device_search(client, task, upnp_filter, upnp_query, sort_by);
+on_error:
+
+ if (!cb_data->action)
+ (void) g_idle_add(dls_async_task_complete, cb_data);
+
+ g_free(sort_by);
+ g_free(upnp_query);
+ g_free(upnp_filter);
+
+ DLEYNA_LOG_DEBUG("Exit with %s", !cb_data->action ? "FAIL" : "SUCCESS");
+}
+
+void dls_upnp_get_resource(dls_upnp_t *upnp, dls_client_t *client,
+ dls_task_t *task,
+ dls_upnp_task_complete_t cb)
+{
+ dls_async_task_t *cb_data = (dls_async_task_t *)task;
+ dls_async_get_all_t *cb_task_data;
+ gchar *upnp_filter = NULL;
+
+ DLEYNA_LOG_DEBUG("Enter");
+
+ DLEYNA_LOG_DEBUG("Protocol Info: %s ", task->ut.resource.protocol_info);
+
+ cb_data->cb = cb;
+ cb_task_data = &cb_data->ut.get_all;
+
+ DLEYNA_LOG_DEBUG("Root Path %s Id %s", task->target.root_path,
+ task->target.id);
+
+ cb_task_data->filter_mask =
+ dls_props_parse_filter(upnp->filter_map,
+ task->ut.resource.filter, &upnp_filter);
+
+ DLEYNA_LOG_DEBUG("Filter Mask 0x%"G_GUINT64_FORMAT"x",
+ cb_task_data->filter_mask);
+
+ dls_device_get_resource(client, task, upnp_filter);
+
+ DLEYNA_LOG_DEBUG("Exit");
+}
+
+static gboolean prv_compute_mime_and_class(dls_task_t *task,
+ dls_async_upload_t *cb_task_data,
+ GError **error)
+{
+ gchar *content_type = NULL;
+
+ if (!g_file_test(task->ut.upload.file_path,
+ G_FILE_TEST_IS_REGULAR | G_FILE_TEST_EXISTS)) {
+
+ DLEYNA_LOG_WARNING(
+ "File %s does not exist or is not a regular file",
+ task->ut.upload.file_path);
+
+ *error = g_error_new(DLEYNA_SERVER_ERROR,
+ DLEYNA_ERROR_OBJECT_NOT_FOUND,
+ "File %s does not exist or is not a regular file",
+ task->ut.upload.file_path);
+ goto on_error;
+ }
+
+ content_type = g_content_type_guess(task->ut.upload.file_path, NULL, 0,
+ NULL);
+
+ if (!content_type) {
+
+ DLEYNA_LOG_WARNING("Unable to determine Content Type for %s",
+ task->ut.upload.file_path);
+
+ *error = g_error_new(DLEYNA_SERVER_ERROR, DLEYNA_ERROR_BAD_MIME,
+ "Unable to determine Content Type for %s",
+ task->ut.upload.file_path);
+ goto on_error;
+ }
+
+ cb_task_data->mime_type = g_content_type_get_mime_type(content_type);
+ g_free(content_type);
+
+ if (!cb_task_data->mime_type) {
+
+ DLEYNA_LOG_WARNING("Unable to determine MIME Type for %s",
+ task->ut.upload.file_path);
+
+ *error = g_error_new(DLEYNA_SERVER_ERROR, DLEYNA_ERROR_BAD_MIME,
+ "Unable to determine MIME Type for %s",
+ task->ut.upload.file_path);
+ goto on_error;
+ }
+
+ if (g_content_type_is_a(cb_task_data->mime_type, "image/*")) {
+ cb_task_data->object_class = "object.item.imageItem";
+ } else if (g_content_type_is_a(cb_task_data->mime_type, "audio/*")) {
+ cb_task_data->object_class = "object.item.audioItem";
+ } else if (g_content_type_is_a(cb_task_data->mime_type, "video/*")) {
+ cb_task_data->object_class = "object.item.videoItem";
+ } else {
+
+ DLEYNA_LOG_WARNING("Unsupported MIME Type %s",
+ cb_task_data->mime_type);
+
+ *error = g_error_new(DLEYNA_SERVER_ERROR, DLEYNA_ERROR_BAD_MIME,
+ "Unsupported MIME Type %s",
+ cb_task_data->mime_type);
+ goto on_error;
+ }
+
+ return TRUE;
+
+on_error:
+
+ return FALSE;
+}
+
+void dls_upnp_upload_to_any(dls_upnp_t *upnp, dls_client_t *client,
+ dls_task_t *task,
+ dls_upnp_task_complete_t cb)
+{
+ dls_async_task_t *cb_data = (dls_async_task_t *)task;
+ dls_async_upload_t *cb_task_data;
+
+ DLEYNA_LOG_DEBUG("Enter");
+
+ cb_data->cb = cb;
+ cb_task_data = &cb_data->ut.upload;
+
+ DLEYNA_LOG_DEBUG("Root Path %s Id %s", task->target.root_path,
+ task->target.id);
+
+ if (strcmp(task->target.id, "0")) {
+ DLEYNA_LOG_WARNING("Bad path %s", task->target.path);
+
+ cb_data->error =
+ g_error_new(DLEYNA_SERVER_ERROR, DLEYNA_ERROR_BAD_PATH,
+ "UploadToAnyContainer must be executed on a root path");
+ goto on_error;
+ }
+
+ if (!prv_compute_mime_and_class(task, cb_task_data, &cb_data->error))
+ goto on_error;
+
+ DLEYNA_LOG_DEBUG("MIME Type %s", cb_task_data->mime_type);
+ DLEYNA_LOG_DEBUG("Object class %s", cb_task_data->object_class);
+
+ dls_device_upload(client, task, "DLNA.ORG_AnyContainer");
+
+on_error:
+
+ if (!cb_data->action)
+ (void) g_idle_add(dls_async_task_complete, cb_data);
+
+ DLEYNA_LOG_DEBUG("Exit");
+}
+
+void dls_upnp_upload(dls_upnp_t *upnp, dls_client_t *client, dls_task_t *task,
+ dls_upnp_task_complete_t cb)
+{
+ dls_async_task_t *cb_data = (dls_async_task_t *)task;
+ dls_async_upload_t *cb_task_data;
+
+ DLEYNA_LOG_DEBUG("Enter");
+
+ cb_data->cb = cb;
+ cb_task_data = &cb_data->ut.upload;
+
+ if (!prv_compute_mime_and_class(task, cb_task_data, &cb_data->error))
+ goto on_error;
+
+ DLEYNA_LOG_DEBUG("MIME Type %s", cb_task_data->mime_type);
+ DLEYNA_LOG_DEBUG("Object class %s", cb_task_data->object_class);
+
+ dls_device_upload(client, task, task->target.id);
+
+on_error:
+
+ if (!cb_data->action)
+ (void) g_idle_add(dls_async_task_complete, cb_data);
+
+ DLEYNA_LOG_DEBUG("Exit");
+}
+
+void dls_upnp_get_upload_status(dls_upnp_t *upnp, dls_task_t *task)
+{
+ GError *error = NULL;
+
+ DLEYNA_LOG_DEBUG("Enter");
+
+ DLEYNA_LOG_DEBUG("Root Path %s Id %s", task->target.root_path,
+ task->target.id);
+
+ if (strcmp(task->target.id, "0")) {
+ DLEYNA_LOG_WARNING("Bad path %s", task->target.path);
+
+ error = g_error_new(DLEYNA_SERVER_ERROR, DLEYNA_ERROR_BAD_PATH,
+ "GetUploadStatus must be executed on a root path");
+ goto on_error;
+ }
+
+ (void) dls_device_get_upload_status(task, &error);
+
+on_error:
+
+ if (error) {
+ dls_task_fail(task, error);
+ g_error_free(error);
+ } else {
+ dls_task_complete(task);
+ }
+
+ DLEYNA_LOG_DEBUG("Exit");
+}
+
+void dls_upnp_get_upload_ids(dls_upnp_t *upnp, dls_task_t *task)
+{
+ GError *error = NULL;
+
+ DLEYNA_LOG_DEBUG("Enter");
+
+ DLEYNA_LOG_DEBUG("Root Path %s Id %s", task->target.root_path,
+ task->target.id);
+
+ if (strcmp(task->target.id, "0")) {
+ DLEYNA_LOG_WARNING("Bad path %s", task->target.path);
+
+ error = g_error_new(DLEYNA_SERVER_ERROR, DLEYNA_ERROR_BAD_PATH,
+ "GetUploadIDs must be executed on a root path");
+ goto on_error;
+ }
+
+ dls_device_get_upload_ids(task);
+
+on_error:
+
+ if (error) {
+ dls_task_fail(task, error);
+ g_error_free(error);
+ } else {
+ dls_task_complete(task);
+ }
+
+ DLEYNA_LOG_DEBUG("Exit");
+}
+
+void dls_upnp_cancel_upload(dls_upnp_t *upnp, dls_task_t *task)
+{
+ GError *error = NULL;
+
+ DLEYNA_LOG_DEBUG("Enter");
+
+ DLEYNA_LOG_DEBUG("Root Path %s Id %s", task->target.root_path,
+ task->target.id);
+
+ if (strcmp(task->target.id, "0")) {
+ DLEYNA_LOG_WARNING("Bad path %s", task->target.path);
+
+ error = g_error_new(DLEYNA_SERVER_ERROR, DLEYNA_ERROR_BAD_PATH,
+ "CancelUpload must be executed on a root path");
+ goto on_error;
+ }
+
+ (void) dls_device_cancel_upload(task, &error);
+
+on_error:
+
+ if (error) {
+ dls_task_fail(task, error);
+ g_error_free(error);
+ } else {
+ dls_task_complete(task);
+ }
+
+ DLEYNA_LOG_DEBUG("Exit");
+}
+
+void dls_upnp_delete_object(dls_upnp_t *upnp, dls_client_t *client,
+ dls_task_t *task,
+ dls_upnp_task_complete_t cb)
+{
+ dls_async_task_t *cb_data = (dls_async_task_t *)task;
+
+ DLEYNA_LOG_DEBUG("Enter");
+
+ cb_data->cb = cb;
+
+ DLEYNA_LOG_DEBUG("Root Path %s Id %s", task->target.root_path,
+ task->target.id);
+
+ dls_device_delete_object(client, task);
+
+ DLEYNA_LOG_DEBUG("Exit");
+}
+
+void dls_upnp_create_container(dls_upnp_t *upnp, dls_client_t *client,
+ dls_task_t *task,
+ dls_upnp_task_complete_t cb)
+{
+ dls_async_task_t *cb_data = (dls_async_task_t *)task;
+
+ DLEYNA_LOG_DEBUG("Enter");
+
+ cb_data->cb = cb;
+
+ DLEYNA_LOG_DEBUG("Root Path %s Id %s", task->target.root_path,
+ task->target.id);
+
+ dls_device_create_container(client, task, task->target.id);
+
+ DLEYNA_LOG_DEBUG("Exit");
+}
+
+void dls_upnp_create_container_in_any(dls_upnp_t *upnp, dls_client_t *client,
+ dls_task_t *task,
+ dls_upnp_task_complete_t cb)
+{
+ dls_async_task_t *cb_data = (dls_async_task_t *)task;
+
+ DLEYNA_LOG_DEBUG("Enter");
+
+ cb_data->cb = cb;
+
+ DLEYNA_LOG_DEBUG("Root Path %s Id %s", task->target.root_path,
+ task->target.id);
+
+ if (strcmp(task->target.id, "0")) {
+ DLEYNA_LOG_WARNING("Bad path %s", task->target.path);
+
+ cb_data->error =
+ g_error_new(DLEYNA_SERVER_ERROR, DLEYNA_ERROR_BAD_PATH,
+ "CreateContainerInAnyContainer must be executed on a root path");
+ goto on_error;
+ }
+
+ dls_device_create_container(client, task, "DLNA.ORG_AnyContainer");
+
+on_error:
+
+ if (!cb_data->action)
+ (void) g_idle_add(dls_async_task_complete, cb_data);
+
+ DLEYNA_LOG_DEBUG("Exit");
+}
+
+void dls_upnp_update_object(dls_upnp_t *upnp, dls_client_t *client,
+ dls_task_t *task,
+ dls_upnp_task_complete_t cb)
+{
+ dls_async_task_t *cb_data = (dls_async_task_t *)task;
+ dls_async_update_t *cb_task_data;
+ dls_upnp_prop_mask mask;
+ gchar *upnp_filter = NULL;
+ dls_task_update_t *task_data;
+
+ DLEYNA_LOG_DEBUG("Enter");
+
+ cb_data->cb = cb;
+ cb_task_data = &cb_data->ut.update;
+ task_data = &task->ut.update;
+
+ DLEYNA_LOG_DEBUG("Root Path %s Id %s", task->target.root_path,
+ task->target.id);
+
+ if (!dls_props_parse_update_filter(upnp->filter_map,
+ task_data->to_add_update,
+ task_data->to_delete,
+ &mask, &upnp_filter)) {
+ DLEYNA_LOG_WARNING("Invalid Parameter");
+
+ cb_data->error = g_error_new(DLEYNA_SERVER_ERROR,
+ DLEYNA_ERROR_OPERATION_FAILED,
+ "Invalid Parameter");
+ goto on_error;
+ }
+
+ cb_task_data->map = upnp->filter_map;
+
+ DLEYNA_LOG_DEBUG("Filter = %s", upnp_filter);
+ DLEYNA_LOG_DEBUG("Mask = 0x%"G_GUINT64_FORMAT"x", mask);
+
+ if (mask == 0) {
+ DLEYNA_LOG_WARNING("Empty Parameters");
+
+ cb_data->error = g_error_new(DLEYNA_SERVER_ERROR,
+ DLEYNA_ERROR_OPERATION_FAILED,
+ "Empty Parameters");
+
+ goto on_error;
+ }
+
+ dls_device_update_object(client, task, upnp_filter);
+
+on_error:
+
+ g_free(upnp_filter);
+
+ if (!cb_data->action)
+ (void) g_idle_add(dls_async_task_complete, cb_data);
+
+ DLEYNA_LOG_DEBUG("Exit");
+}
+
+void dls_upnp_create_playlist(dls_upnp_t *upnp, dls_client_t *client,
+ dls_task_t *task,
+ dls_upnp_task_complete_t cb)
+{
+ dls_async_task_t *cb_data = (dls_async_task_t *)task;
+ dls_task_create_playlist_t *task_data;
+
+ DLEYNA_LOG_DEBUG("Enter");
+
+ cb_data->cb = cb;
+ task_data = &task->ut.playlist;
+
+ DLEYNA_LOG_DEBUG("Root Path: %s - Id: %s", task->target.root_path,
+ task->target.id);
+
+ if (!task_data->title || !*task_data->title)
+ goto on_param_error;
+
+ if (!g_variant_n_children(task_data->item_path))
+ goto on_param_error;
+
+ DLEYNA_LOG_DEBUG_NL();
+ DLEYNA_LOG_DEBUG("Title = %s", task_data->title);
+ DLEYNA_LOG_DEBUG("Creator = %s", task_data->creator);
+ DLEYNA_LOG_DEBUG("Genre = %s", task_data->genre);
+ DLEYNA_LOG_DEBUG("Desc = %s", task_data->desc);
+ DLEYNA_LOG_DEBUG_NL();
+
+ dls_device_playlist_upload(client, task, task->target.id);
+
+ DLEYNA_LOG_DEBUG("Exit");
+
+ return;
+
+on_param_error:
+
+ DLEYNA_LOG_WARNING("Invalid Parameter");
+
+ cb_data->error = g_error_new(DLEYNA_SERVER_ERROR,
+ DLEYNA_ERROR_OPERATION_FAILED,
+ "Invalid Parameter");
+
+ (void) g_idle_add(dls_async_task_complete, cb_data);
+
+ DLEYNA_LOG_DEBUG("Exit failure");
+}
+
+void dls_upnp_create_playlist_in_any(dls_upnp_t *upnp, dls_client_t *client,
+ dls_task_t *task,
+ dls_upnp_task_complete_t cb)
+{
+ dls_async_task_t *cb_data = (dls_async_task_t *)task;
+ dls_task_create_playlist_t *task_data;
+
+ DLEYNA_LOG_DEBUG("Enter");
+
+ cb_data->cb = cb;
+ task_data = &task->ut.playlist;
+
+ DLEYNA_LOG_DEBUG("Root Path: %s - Id: %s", task->target.root_path,
+ task->target.id);
+
+ if (strcmp(task->target.id, "0")) {
+ DLEYNA_LOG_WARNING("Bad path %s", task->target.path);
+
+ cb_data->error = g_error_new(DLEYNA_SERVER_ERROR,
+ DLEYNA_ERROR_BAD_PATH,
+ "CreatePlayListInAny must be executed on a root path");
+
+ goto on_error;
+ }
+
+ if (!task_data->title || !*task_data->title)
+ goto on_param_error;
+
+ if (!g_variant_n_children(task_data->item_path))
+ goto on_param_error;
+
+ DLEYNA_LOG_DEBUG_NL();
+ DLEYNA_LOG_DEBUG("Title = %s", task_data->title);
+ DLEYNA_LOG_DEBUG("Creator = %s", task_data->creator);
+ DLEYNA_LOG_DEBUG("Genre = %s", task_data->genre);
+ DLEYNA_LOG_DEBUG("Desc = %s", task_data->desc);
+ DLEYNA_LOG_DEBUG_NL();
+
+ dls_device_playlist_upload(client, task, "DLNA.ORG_AnyContainer");
+
+ DLEYNA_LOG_DEBUG("Exit");
+
+ return;
+
+on_param_error:
+
+ DLEYNA_LOG_WARNING("Invalid Parameter");
+
+ cb_data->error = g_error_new(DLEYNA_SERVER_ERROR,
+ DLEYNA_ERROR_OPERATION_FAILED,
+ "Invalid Parameter");
+on_error:
+
+ (void) g_idle_add(dls_async_task_complete, cb_data);
+
+ DLEYNA_LOG_DEBUG("Exit failure");
+}
+
+void dls_upnp_unsubscribe(dls_upnp_t *upnp)
+{
+ GHashTableIter iter;
+ gpointer value;
+ dls_device_t *device;
+
+ DLEYNA_LOG_DEBUG("Enter");
+
+ g_hash_table_iter_init(&iter, upnp->server_udn_map);
+ while (g_hash_table_iter_next(&iter, NULL, &value)) {
+ device = value;
+ dls_device_unsubscribe(device);
+ }
+
+ DLEYNA_LOG_DEBUG("Exit");
+}
+
+static gboolean prv_device_uc_find(gpointer key, gpointer value,
+ gpointer user_data)
+{
+ prv_device_new_ct_t *priv_t = (prv_device_new_ct_t *)value;
+
+ return (priv_t->device == user_data) ? TRUE : FALSE;
+}
+
+static gboolean prv_device_find(gpointer key, gpointer value,
+ gpointer user_data)
+{
+ return (value == user_data) ? TRUE : FALSE;
+}
+
+gboolean dls_upnp_device_context_exist(dls_device_t *device,
+ dls_device_context_t *context)
+{
+ gpointer result;
+ guint i;
+ gboolean found = FALSE;
+ dls_upnp_t *upnp = dls_server_get_upnp();
+
+ if (upnp == NULL)
+ goto on_exit;
+
+ /* Check if the device still exist */
+ result = g_hash_table_find(upnp->server_udn_map, prv_device_find,
+ device);
+
+ if (result == NULL)
+ if (g_hash_table_find(upnp->server_uc_map, prv_device_uc_find,
+ device) == NULL)
+ goto on_exit;
+
+ /* Search if the context still exist in the device */
+ for (i = 0; i < device->contexts->len; ++i) {
+ if (g_ptr_array_index(device->contexts, i) == context) {
+ found = TRUE;
+ break;
+ }
+ }
+
+on_exit:
+ return found;
+}
diff --git a/libdleyna/server/upnp.h b/libdleyna/server/upnp.h
new file mode 100644
index 0000000..dfe2d1b
--- /dev/null
+++ b/libdleyna/server/upnp.h
@@ -0,0 +1,109 @@
+/*
+ * dLeyna
+ *
+ * Copyright (C) 2012-2013 Intel Corporation. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms and conditions of the GNU Lesser General Public License,
+ * version 2.1, as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License
+ * for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with this program; if not, write to the Free Software Foundation, Inc.,
+ * 51 Franklin St - Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Mark Ryan <mark.d.ryan@intel.com>
+ *
+ */
+
+#ifndef DLS_UPNP_H__
+#define DLS_UPNP_H__
+
+#include <libdleyna/core/connector.h>
+
+#include "client.h"
+#include "async.h"
+
+typedef void (*dls_upnp_callback_t)(const gchar *path, void *user_data);
+typedef void (*dls_upnp_task_complete_t)(dls_task_t *task, GError *error);
+
+dls_upnp_t *dls_upnp_new(dleyna_connector_id_t connection,
+ const dleyna_connector_dispatch_cb_t *dispatch_table,
+ dls_upnp_callback_t found_server,
+ dls_upnp_callback_t lost_server,
+ void *user_data);
+
+void dls_upnp_delete(dls_upnp_t *upnp);
+
+GVariant *dls_upnp_get_server_ids(dls_upnp_t *upnp);
+
+GHashTable *dls_upnp_get_server_udn_map(dls_upnp_t *upnp);
+
+void dls_upnp_get_children(dls_upnp_t *upnp, dls_client_t *client,
+ dls_task_t *task,
+ dls_upnp_task_complete_t cb);
+
+void dls_upnp_get_all_props(dls_upnp_t *upnp, dls_client_t *client,
+ dls_task_t *task,
+ dls_upnp_task_complete_t cb);
+
+void dls_upnp_get_prop(dls_upnp_t *upnp, dls_client_t *client,
+ dls_task_t *task,
+ dls_upnp_task_complete_t cb);
+
+void dls_upnp_search(dls_upnp_t *upnp, dls_client_t *client,
+ dls_task_t *task,
+ dls_upnp_task_complete_t cb);
+
+void dls_upnp_get_resource(dls_upnp_t *upnp, dls_client_t *client,
+ dls_task_t *task,
+ dls_upnp_task_complete_t cb);
+
+void dls_upnp_upload_to_any(dls_upnp_t *upnp, dls_client_t *client,
+ dls_task_t *task,
+ dls_upnp_task_complete_t cb);
+
+void dls_upnp_upload(dls_upnp_t *upnp, dls_client_t *client,
+ dls_task_t *task,
+ dls_upnp_task_complete_t cb);
+
+void dls_upnp_get_upload_status(dls_upnp_t *upnp, dls_task_t *task);
+
+void dls_upnp_get_upload_ids(dls_upnp_t *upnp, dls_task_t *task);
+
+void dls_upnp_cancel_upload(dls_upnp_t *upnp, dls_task_t *task);
+
+void dls_upnp_delete_object(dls_upnp_t *upnp, dls_client_t *client,
+ dls_task_t *task,
+ dls_upnp_task_complete_t cb);
+
+void dls_upnp_create_container(dls_upnp_t *upnp, dls_client_t *client,
+ dls_task_t *task,
+ dls_upnp_task_complete_t cb);
+
+void dls_upnp_create_container_in_any(dls_upnp_t *upnp, dls_client_t *client,
+ dls_task_t *task,
+ dls_upnp_task_complete_t cb);
+
+void dls_upnp_update_object(dls_upnp_t *upnp, dls_client_t *client,
+ dls_task_t *task,
+ dls_upnp_task_complete_t cb);
+
+void dls_upnp_create_playlist(dls_upnp_t *upnp, dls_client_t *client,
+ dls_task_t *task,
+ dls_upnp_task_complete_t cb);
+
+void dls_upnp_create_playlist_in_any(dls_upnp_t *upnp, dls_client_t *client,
+ dls_task_t *task,
+ dls_upnp_task_complete_t cb);
+
+void dls_upnp_unsubscribe(dls_upnp_t *upnp);
+
+gboolean dls_upnp_device_context_exist(dls_device_t *device,
+ dls_device_context_t *context);
+
+#endif /* DLS_UPNP_H__ */