Codebase list dleyna-server / d929e2f
[Architecture] Change directory structure to enable build from a master project Signed-off-by: Regis Merlino <regis.merlino@intel.com> Regis Merlino 11 years ago
53 changed file(s) with 10861 addition(s) and 10851 deletion(s). Raw diff Collapse all Expand all
3535 m4/lt~obsolete.m4
3636
3737 server/com.intel.dleyna-server.service
38 lib/dleyna-server-service.conf
39 lib/dleyna-server-1.0.pc
38 libdleyna/server/dleyna-server-service.conf
39 libdleyna/server/dleyna-server-1.0.pc
4040
4141 server/dleyna-server-service
4242 test/dbus/dms-info
0 SUBDIRS = lib
0 SUBDIRS = libdleyna/server
11
22 if BUILD_SERVER
33 SUBDIRS += server test/dbus
88 AC_CONFIG_HEADERS([config.h])
99 AC_CONFIG_AUX_DIR([build-aux])
1010 AC_CONFIG_MACRO_DIR([m4])
11 AC_CONFIG_SRCDIR([lib/server.c])
11 AC_CONFIG_SRCDIR([libdleyna/server/server.c])
1212
1313 AC_PREFIX_DEFAULT(/usr/local)
1414
3737 PKG_PROG_PKG_CONFIG(0.16)
3838 PKG_CHECK_MODULES([GLIB], [glib-2.0 >= 2.28])
3939 PKG_CHECK_MODULES([GIO], [gio-2.0 >= 2.28])
40 PKG_CHECK_MODULES([DLEYNA_CORE], [dleyna-core-1.0 >= 0.0.1])
4140 PKG_CHECK_MODULES([GSSDP], [gssdp-1.0 >= 0.13.2])
4241 PKG_CHECK_MODULES([GUPNP], [gupnp-1.0 >= 0.19.1])
4342 PKG_CHECK_MODULES([GUPNPAV], [gupnp-av-1.0 >= 0.11.5])
7877 AC_DEFINE_UNQUOTED([DLEYNA_LOG_LEVEL_DEFAULT], [${LOG_LEVEL_7}], [Log level flag to display default level messages])
7978 AC_DEFINE_UNQUOTED([DLEYNA_LOG_LEVEL_ALL], [${LOG_LEVEL_8}], [Log level flag for all messages])
8079
80 AC_ARG_ENABLE(master-build,,
81 [],
82 [master_build=no])
83
84 AS_IF([test "x$master_build" = "xno"],
85 [PKG_CHECK_MODULES([DLEYNA_CORE], [dleyna-core-1.0 >= 0.0.1])],
86 [this_abs_top_srcdir=`cd "$srcdir" && pwd`;
87 DLEYNA_CORE_CFLAGS="-I$this_abs_top_srcdir/../dleyna-core";
88 DLEYNA_CORE_LIBS="-L$this_abs_top_srcdir/../dleyna-core/.libs -ldleyna-core-1.0"
89 ])
90
8191 AC_ARG_ENABLE(debug,
8292 AS_HELP_STRING(
8393 [--enable-debug],
216226 AC_SUBST([with_log_level])
217227 AC_SUBST([with_log_type])
218228
219 AC_CONFIG_FILES([Makefile \
220 lib/Makefile \
221 lib/dleyna-server-1.0.pc \
222 lib/dleyna-server-service.conf \
223 server/com.intel.dleyna-server.service \
224 server/dleyna-server-service-1.0.pc \
225 server/Makefile \
229 AC_CONFIG_FILES([Makefile \
230 libdleyna/server/Makefile \
231 libdleyna/server/dleyna-server-1.0.pc \
232 libdleyna/server/dleyna-server-service.conf \
233 server/com.intel.dleyna-server.service \
234 server/dleyna-server-service-1.0.pc \
235 server/Makefile \
226236 test/dbus/Makefile
227237 ])
228238
+0
-64
lib/Makefile.am less more
0 libdleyna_serverincdir = $(includedir)/dleyna-1.0/libdleyna/server
1
2 DLEYNA_SERVER_VERSION = 1:0:0
3
4 AM_CFLAGS = $(GLIB_CFLAGS) \
5 $(GIO_CFLAGS) \
6 $(DLEYNA_CORE_CFLAGS) \
7 $(GSSDP_CFLAGS) \
8 $(GUPNP_CFLAGS) \
9 $(GUPNPAV_CFLAGS) \
10 $(GUPNPDLNA_CFLAGS) \
11 $(SOUP_CFLAGS) \
12 -include config.h
13
14 ACLOCAL_AMFLAGS = -I m4 ${ACLOCAL_FLAGS}
15
16 lib_LTLIBRARIES = libdleyna-server-1.0.la
17
18 libdleyna_serverinc_HEADERS = control-point-server.h
19
20 libdleyna_server_1_0_la_LDFLAGS = -version-info $(DLEYNA_SERVER_VERSION) \
21 -no-undefined
22
23 libdleyna_server_1_0_la_SOURCES = $(libdleyna_serverinc_HEADERS) \
24 server.c \
25 async.c \
26 device.c \
27 path.c \
28 props.c \
29 search.c \
30 sort.c \
31 task.c \
32 upnp.c
33
34 libdleyna_server_1_0_la_LIBADD = $(GLIB_LIBS) \
35 $(GIO_LIBS) \
36 $(DLEYNA_CORE_LIBS) \
37 $(GSSDP_LIBS) \
38 $(GUPNP_LIBS) \
39 $(GUPNPAV_LIBS) \
40 $(GUPNPDLNA_LIBS) \
41 $(SOUP_LIBS)
42
43 MAINTAINERCLEANFILES = Makefile.in \
44 aclocal.m4 \
45 configure \
46 config.h.in \
47 config.h.in~ \
48 build-aux/depcomp \
49 build-aux/compile \
50 build-aux/missing \
51 build-aux/install-sh
52
53 sysconf_DATA = dleyna-server-service.conf
54
55 pkgconfigdir = $(libdir)/pkgconfig
56 pkgconfig_DATA = dleyna-server-1.0.pc
57
58 EXTRA_DIST = $(sysconf_DATA)
59 CLEANFILES = $(pkgconfig_DATA) dleyna-server-service.conf
60 DISTCLEANFILES = $(pkgconfig_DATA) dleyna-server-service.conf
61
62 maintainer-clean-local:
63 rm -rf build-aux
+0
-99
lib/async.c less more
0 /*
1 * dLeyna
2 *
3 * Copyright (C) 2012-2013 Intel Corporation. All rights reserved.
4 *
5 * This program is free software; you can redistribute it and/or modify it
6 * under the terms and conditions of the GNU Lesser General Public License,
7 * version 2.1, as published by the Free Software Foundation.
8 *
9 * This program is distributed in the hope it will be useful, but WITHOUT
10 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
11 * FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License
12 * for more details.
13 *
14 * You should have received a copy of the GNU Lesser General Public License
15 * along with this program; if not, write to the Free Software Foundation, Inc.,
16 * 51 Franklin St - Fifth Floor, Boston, MA 02110-1301 USA.
17 *
18 * Mark Ryan <mark.d.ryan@intel.com>
19 *
20 */
21
22 #include <libdleyna/core/error.h>
23 #include <libdleyna/core/log.h>
24
25 #include "async.h"
26 #include "server.h"
27
28 void dls_async_task_delete(dls_async_task_t *cb_data)
29 {
30 switch (cb_data->task.type) {
31 case DLS_TASK_GET_CHILDREN:
32 case DLS_TASK_SEARCH:
33 if (cb_data->ut.bas.vbs)
34 g_ptr_array_unref(cb_data->ut.bas.vbs);
35 break;
36 case DLS_TASK_GET_ALL_PROPS:
37 case DLS_TASK_GET_RESOURCE:
38 if (cb_data->ut.get_all.vb)
39 g_variant_builder_unref(cb_data->ut.get_all.vb);
40 break;
41 case DLS_TASK_UPLOAD_TO_ANY:
42 case DLS_TASK_UPLOAD:
43 g_free(cb_data->ut.upload.mime_type);
44 break;
45 case DLS_TASK_UPDATE_OBJECT:
46 g_free(cb_data->ut.update.current_tag_value);
47 g_free(cb_data->ut.update.new_tag_value);
48 break;
49 case DLS_TASK_CREATE_PLAYLIST:
50 case DLS_TASK_CREATE_PLAYLIST_IN_ANY:
51 g_free(cb_data->ut.playlist.didl);
52 if (cb_data->ut.playlist.collection)
53 g_object_unref(cb_data->ut.playlist.collection);
54 break;
55 default:
56 break;
57 }
58
59 if (cb_data->cancellable)
60 g_object_unref(cb_data->cancellable);
61 }
62
63 gboolean dls_async_task_complete(gpointer user_data)
64 {
65 dls_async_task_t *cb_data = user_data;
66
67 DLEYNA_LOG_DEBUG("Enter. Error %p", (void *)cb_data->error);
68 DLEYNA_LOG_DEBUG_NL();
69
70 if (cb_data->proxy != NULL)
71 g_object_remove_weak_pointer((G_OBJECT(cb_data->proxy)),
72 (gpointer *)&cb_data->proxy);
73
74 cb_data->cb(&cb_data->task, cb_data->error);
75
76 return FALSE;
77 }
78
79 void dls_async_task_cancelled_cb(GCancellable *cancellable, gpointer user_data)
80 {
81 dls_async_task_t *cb_data = user_data;
82
83 if (cb_data->proxy != NULL)
84 gupnp_service_proxy_cancel_action(cb_data->proxy,
85 cb_data->action);
86
87 if (!cb_data->error)
88 cb_data->error = g_error_new(DLEYNA_SERVER_ERROR,
89 DLEYNA_ERROR_CANCELLED,
90 "Operation cancelled.");
91 (void) g_idle_add(dls_async_task_complete, cb_data);
92 }
93
94 void dls_async_task_cancel(dls_async_task_t *cb_data)
95 {
96 if (cb_data->cancellable)
97 g_cancellable_cancel(cb_data->cancellable);
98 }
+0
-113
lib/async.h less more
0 /*
1 * dLeyna
2 *
3 * Copyright (C) 2012-2013 Intel Corporation. All rights reserved.
4 *
5 * This program is free software; you can redistribute it and/or modify it
6 * under the terms and conditions of the GNU Lesser General Public License,
7 * version 2.1, as published by the Free Software Foundation.
8 *
9 * This program is distributed in the hope it will be useful, but WITHOUT
10 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
11 * FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License
12 * for more details.
13 *
14 * You should have received a copy of the GNU Lesser General Public License
15 * along with this program; if not, write to the Free Software Foundation, Inc.,
16 * 51 Franklin St - Fifth Floor, Boston, MA 02110-1301 USA.
17 *
18 * Mark Ryan <mark.d.ryan@intel.com>
19 *
20 */
21
22 #ifndef DLS_ASYNC_H__
23 #define DLS_ASYNC_H__
24
25 #include <libgupnp/gupnp-control-point.h>
26 #include <libgupnp-av/gupnp-media-collection.h>
27
28 #include <libdleyna/core/task-atom.h>
29
30 #include "server.h"
31 #include "task.h"
32 #include "upnp.h"
33
34 typedef struct dls_async_task_t_ dls_async_task_t;
35 typedef guint64 dls_upnp_prop_mask;
36
37 typedef void (*dls_async_cb_t)(dls_async_task_t *cb_data);
38
39 typedef struct dls_async_bas_t_ dls_async_bas_t;
40 struct dls_async_bas_t_ {
41 dls_upnp_prop_mask filter_mask;
42 GPtrArray *vbs;
43 const gchar *protocol_info;
44 gboolean need_child_count;
45 guint retrieved;
46 guint max_count;
47 dls_async_cb_t get_children_cb;
48 };
49
50 typedef struct dls_async_get_prop_t_ dls_async_get_prop_t;
51 struct dls_async_get_prop_t_ {
52 GCallback prop_func;
53 const gchar *protocol_info;
54 };
55
56 typedef struct dls_async_get_all_t_ dls_async_get_all_t;
57 struct dls_async_get_all_t_ {
58 GCallback prop_func;
59 GVariantBuilder *vb;
60 dls_upnp_prop_mask filter_mask;
61 const gchar *protocol_info;
62 gboolean need_child_count;
63 gboolean device_object;
64 };
65
66 typedef struct dls_async_upload_t_ dls_async_upload_t;
67 struct dls_async_upload_t_ {
68 const gchar *object_class;
69 gchar *mime_type;
70 };
71
72 typedef struct dls_async_update_t_ dls_async_update_t;
73 struct dls_async_update_t_ {
74 gchar *current_tag_value;
75 gchar *new_tag_value;
76 GHashTable *map;
77 };
78
79 typedef struct dls_async_playlist_t_ dls_async_playlist_t;
80 struct dls_async_playlist_t_ {
81 const dleyna_task_queue_key_t *queue_id;
82 GUPnPMediaCollection *collection;
83 gchar *didl;
84 };
85
86 struct dls_async_task_t_ {
87 dls_task_t task; /* pseudo inheritance - MUST be first field */
88 dls_upnp_task_complete_t cb;
89 GError *error;
90 GUPnPServiceProxyAction *action;
91 GUPnPServiceProxy *proxy;
92 GCancellable *cancellable;
93 gulong cancel_id;
94 union {
95 dls_async_bas_t bas;
96 dls_async_get_prop_t get_prop;
97 dls_async_get_all_t get_all;
98 dls_async_upload_t upload;
99 dls_async_update_t update;
100 dls_async_playlist_t playlist;
101 } ut;
102 };
103
104 void dls_async_task_delete(dls_async_task_t *cb_data);
105
106 gboolean dls_async_task_complete(gpointer user_data);
107
108 void dls_async_task_cancelled_cb(GCancellable *cancellable, gpointer user_data);
109
110 void dls_async_task_cancel(dls_async_task_t *cb_data);
111
112 #endif /* DLS_ASYNC_H__ */
+0
-34
lib/client.h less more
0 /*
1 * dLeyna
2 *
3 * Copyright (C) 2012-2013 Intel Corporation. All rights reserved.
4 *
5 * This program is free software; you can redistribute it and/or modify it
6 * under the terms and conditions of the GNU Lesser General Public License,
7 * version 2.1, as published by the Free Software Foundation.
8 *
9 * This program is distributed in the hope it will be useful, but WITHOUT
10 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
11 * FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License
12 * for more details.
13 *
14 * You should have received a copy of the GNU Lesser General Public License
15 * along with this program; if not, write to the Free Software Foundation, Inc.,
16 * 51 Franklin St - Fifth Floor, Boston, MA 02110-1301 USA.
17 *
18 * Regis Merlino <regis.merlino@intel.com>
19 *
20 */
21
22 #ifndef DLS_CLIENT_H__
23 #define DLS_CLIENT_H__
24
25 #include <glib.h>
26
27 typedef struct dls_client_t_ dls_client_t;
28 struct dls_client_t_ {
29 gchar *protocol_info;
30 gboolean prefer_local_addresses;
31 };
32
33 #endif /* DLS_CLIENT_H__ */
+0
-30
lib/control-point-server.h less more
0 /*
1 * dLeyna
2 *
3 * Copyright (C) 2013 Intel Corporation. All rights reserved.
4 *
5 * This program is free software; you can redistribute it and/or modify it
6 * under the terms and conditions of the GNU Lesser General Public License,
7 * version 2.1, as published by the Free Software Foundation.
8 *
9 * This program is distributed in the hope it will be useful, but WITHOUT
10 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
11 * FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License
12 * for more details.
13 *
14 * You should have received a copy of the GNU Lesser General Public License
15 * along with this program; if not, write to the Free Software Foundation, Inc.,
16 * 51 Franklin St - Fifth Floor, Boston, MA 02110-1301 USA.
17 *
18 * Regis Merlino <regis.merlino@intel.com>
19 *
20 */
21
22 #ifndef DLEYNA_CONTROL_POINT_SERVER_H__
23 #define DLEYNA_CONTROL_POINT_SERVER_H__
24
25 #include <libdleyna/core/control-point.h>
26
27 const dleyna_control_point_t *dleyna_control_point_get_server(void);
28
29 #endif /* DLEYNA_CONTROL_POINT_SERVER_H__ */
+0
-4348
lib/device.c less more
0 /*
1 * dLeyna
2 *
3 * Copyright (C) 2012-2013 Intel Corporation. All rights reserved.
4 *
5 * This program is free software; you can redistribute it and/or modify it
6 * under the terms and conditions of the GNU Lesser General Public License,
7 * version 2.1, as published by the Free Software Foundation.
8 *
9 * This program is distributed in the hope it will be useful, but WITHOUT
10 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
11 * FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License
12 * for more details.
13 *
14 * You should have received a copy of the GNU Lesser General Public License
15 * along with this program; if not, write to the Free Software Foundation, Inc.,
16 * 51 Franklin St - Fifth Floor, Boston, MA 02110-1301 USA.
17 *
18 * Mark Ryan <mark.d.ryan@intel.com>
19 *
20 */
21
22 #include <string.h>
23 #include <libgupnp/gupnp-error.h>
24 #include <libgupnp-dlna/gupnp-dlna-profile.h>
25 #include <libgupnp-dlna/gupnp-dlna-profile-guesser.h>
26 #include <libgupnp-av/gupnp-media-collection.h>
27 #include <libgupnp-av/gupnp-cds-last-change-parser.h>
28 #include <libsoup/soup.h>
29
30 #include <libdleyna/core/error.h>
31 #include <libdleyna/core/log.h>
32 #include <libdleyna/core/service-task.h>
33
34 #include "device.h"
35 #include "interface.h"
36 #include "path.h"
37 #include "server.h"
38
39 #define DLS_SYSTEM_UPDATE_VAR "SystemUpdateID"
40 #define DLS_CONTAINER_UPDATE_VAR "ContainerUpdateIDs"
41 #define DLS_LAST_CHANGE_VAR "LastChange"
42 #define DLS_DMS_DEVICE_TYPE "urn:schemas-upnp-org:device:MediaServer:"
43
44 #define DLS_UPLOAD_STATUS_IN_PROGRESS "IN_PROGRESS"
45 #define DLS_UPLOAD_STATUS_CANCELLED "CANCELLED"
46 #define DLS_UPLOAD_STATUS_ERROR "ERROR"
47 #define DLS_UPLOAD_STATUS_COMPLETED "COMPLETED"
48
49 typedef gboolean(*dls_device_count_cb_t)(dls_async_task_t *cb_data,
50 gint count);
51
52 typedef struct dls_device_count_data_t_ dls_device_count_data_t;
53 struct dls_device_count_data_t_ {
54 dls_device_count_cb_t cb;
55 dls_async_task_t *cb_data;
56 };
57
58 typedef struct dls_device_object_builder_t_ dls_device_object_builder_t;
59 struct dls_device_object_builder_t_ {
60 GVariantBuilder *vb;
61 gchar *id;
62 gboolean needs_child_count;
63 };
64
65 typedef struct dls_device_upload_t_ dls_device_upload_t;
66 struct dls_device_upload_t_ {
67 SoupSession *soup_session;
68 SoupMessage *msg;
69 GMappedFile *mapped_file;
70 gchar *body;
71 gsize body_length;
72 const gchar *status;
73 guint64 bytes_uploaded;
74 guint64 bytes_to_upload;
75 };
76
77 typedef struct dls_device_upload_job_t_ dls_device_upload_job_t;
78 struct dls_device_upload_job_t_ {
79 gint upload_id;
80 dls_device_t *device;
81 guint remove_idle;
82 };
83
84 /* Private structure used in chain task */
85 typedef struct prv_new_device_ct_t_ prv_new_device_ct_t;
86 struct prv_new_device_ct_t_ {
87 dls_device_t *dev;
88 dleyna_connector_id_t connection;
89 const dleyna_connector_dispatch_cb_t *vtable;
90 GHashTable *property_map;
91 };
92
93 typedef struct prv_new_playlist_ct_t_ prv_new_playlist_ct_t;
94 struct prv_new_playlist_ct_t_ {
95 dls_async_task_t *cb_data;
96 gchar *id;
97 gchar *parent_id;
98 };
99
100 static void prv_get_child_count(dls_async_task_t *cb_data,
101 dls_device_count_cb_t cb, const gchar *id);
102 static void prv_retrieve_child_count_for_list(dls_async_task_t *cb_data);
103 static void prv_container_update_cb(GUPnPServiceProxy *proxy,
104 const char *variable,
105 GValue *value,
106 gpointer user_data);
107 static void prv_system_update_cb(GUPnPServiceProxy *proxy,
108 const char *variable,
109 GValue *value,
110 gpointer user_data);
111 static void prv_last_change_cb(GUPnPServiceProxy *proxy,
112 const char *variable,
113 GValue *value,
114 gpointer user_data);
115 static void prv_upload_delete(gpointer up);
116 static void prv_upload_job_delete(gpointer up);
117 static void prv_get_sr_token_for_props(GUPnPServiceProxy *proxy,
118 const dls_device_t *device,
119 dls_async_task_t *cb_data);
120
121 static void prv_object_builder_delete(void *dob)
122 {
123 dls_device_object_builder_t *builder = dob;
124
125 if (builder) {
126 if (builder->vb)
127 g_variant_builder_unref(builder->vb);
128
129 g_free(builder->id);
130 g_free(builder);
131 }
132 }
133
134 static void prv_count_data_new(dls_async_task_t *cb_data,
135 dls_device_count_cb_t cb,
136 dls_device_count_data_t **count_data)
137 {
138 dls_device_count_data_t *cd;
139
140 cd = g_new(dls_device_count_data_t, 1);
141 cd->cb = cb;
142 cd->cb_data = cb_data;
143
144 *count_data = cd;
145 }
146
147 static void prv_context_unsubscribe(dls_device_context_t *ctx)
148 {
149 if (ctx->timeout_id) {
150 (void) g_source_remove(ctx->timeout_id);
151 ctx->timeout_id = 0;
152 }
153
154 if (ctx->subscribed) {
155 gupnp_service_proxy_remove_notify(
156 ctx->service_proxy,
157 DLS_SYSTEM_UPDATE_VAR,
158 prv_system_update_cb,
159 ctx->device);
160 gupnp_service_proxy_remove_notify(
161 ctx->service_proxy,
162 DLS_CONTAINER_UPDATE_VAR,
163 prv_container_update_cb,
164 ctx->device);
165 gupnp_service_proxy_remove_notify(
166 ctx->service_proxy,
167 DLS_LAST_CHANGE_VAR,
168 prv_last_change_cb,
169 ctx->device);
170
171 gupnp_service_proxy_set_subscribed(ctx->service_proxy,
172 FALSE);
173
174 ctx->subscribed = FALSE;
175 }
176 }
177
178 static void prv_context_delete(gpointer context)
179 {
180 dls_device_context_t *ctx = context;
181
182 if (ctx) {
183 prv_context_unsubscribe(ctx);
184
185 if (ctx->device_proxy)
186 g_object_unref(ctx->device_proxy);
187
188 if (ctx->service_proxy)
189 g_object_unref(ctx->service_proxy);
190
191 g_free(ctx->ip_address);
192 g_free(ctx);
193 }
194 }
195
196 static void prv_context_new(const gchar *ip_address,
197 GUPnPDeviceProxy *proxy,
198 dls_device_t *device,
199 dls_device_context_t **context)
200 {
201 const gchar *service_type =
202 "urn:schemas-upnp-org:service:ContentDirectory";
203 dls_device_context_t *ctx = g_new(dls_device_context_t, 1);
204
205 ctx->ip_address = g_strdup(ip_address);
206 ctx->device_proxy = proxy;
207 ctx->device = device;
208 g_object_ref(proxy);
209 ctx->service_proxy = (GUPnPServiceProxy *)
210 gupnp_device_info_get_service((GUPnPDeviceInfo *)proxy,
211 service_type);
212 ctx->subscribed = FALSE;
213 ctx->timeout_id = 0;
214
215 *context = ctx;
216 }
217
218 void dls_device_delete(void *device)
219 {
220 dls_device_t *dev = device;
221
222 if (dev) {
223 dev->shutting_down = TRUE;
224 g_hash_table_unref(dev->upload_jobs);
225 g_hash_table_unref(dev->uploads);
226
227 if (dev->timeout_id)
228 (void) g_source_remove(dev->timeout_id);
229
230 if (dev->id)
231 (void) dls_server_get_connector()->unpublish_subtree(
232 dev->connection, dev->id);
233
234 g_ptr_array_unref(dev->contexts);
235 g_free(dev->path);
236 g_variant_unref(dev->search_caps);
237 g_variant_unref(dev->sort_caps);
238 g_variant_unref(dev->sort_ext_caps);
239 g_variant_unref(dev->feature_list);
240 g_free(dev);
241 }
242 }
243
244 void dls_device_unsubscribe(void *device)
245 {
246 unsigned int i;
247 dls_device_t *dev = device;
248 dls_device_context_t *context;
249
250 if (dev) {
251 for (i = 0; i < dev->contexts->len; ++i) {
252 context = g_ptr_array_index(dev->contexts, i);
253 prv_context_unsubscribe(context);
254 }
255 }
256 }
257
258 static void prv_last_change_decode(GUPnPCDSLastChangeEntry *entry,
259 GVariantBuilder *array,
260 const char *root_path)
261 {
262 GUPnPCDSLastChangeEvent event;
263 GVariant *state;
264 const char *object_id;
265 const char *parent_id;
266 const char *mclass;
267 const char *media_class;
268 char *key[] = {"ADD", "DEL", "MOD", "DONE"};
269 char *parent_path;
270 char *path = NULL;
271 gboolean sub_update;
272 guint32 update_id;
273
274 object_id = gupnp_cds_last_change_entry_get_object_id(entry);
275 if (!object_id)
276 goto on_error;
277
278 sub_update = gupnp_cds_last_change_entry_is_subtree_update(entry);
279 update_id = gupnp_cds_last_change_entry_get_update_id(entry);
280 path = dls_path_from_id(root_path, object_id);
281 event = gupnp_cds_last_change_entry_get_event(entry);
282
283 switch (event) {
284 case GUPNP_CDS_LAST_CHANGE_EVENT_OBJECT_ADDED:
285 parent_id = gupnp_cds_last_change_entry_get_parent_id(entry);
286 if (!parent_id)
287 goto on_error;
288
289 mclass = gupnp_cds_last_change_entry_get_class(entry);
290 if (!mclass)
291 goto on_error;
292
293 media_class = dls_props_upnp_class_to_media_spec(mclass);
294 if (!media_class)
295 goto on_error;
296
297 parent_path = dls_path_from_id(root_path, parent_id);
298 state = g_variant_new("(oubos)", path, update_id, sub_update,
299 parent_path, media_class);
300 g_free(parent_path);
301 break;
302 case GUPNP_CDS_LAST_CHANGE_EVENT_OBJECT_REMOVED:
303 case GUPNP_CDS_LAST_CHANGE_EVENT_OBJECT_MODIFIED:
304 state = g_variant_new("(oub)", path, update_id, sub_update);
305 break;
306 case GUPNP_CDS_LAST_CHANGE_EVENT_ST_DONE:
307 state = g_variant_new("(ou)", path, update_id);
308 break;
309 case GUPNP_CDS_LAST_CHANGE_EVENT_INVALID:
310 default:
311 goto on_error;
312 break;
313 }
314
315 g_variant_builder_add(array, "(sv)", key[event - 1], state);
316
317 on_error:
318
319 g_free(path);
320 }
321
322 static void prv_last_change_cb(GUPnPServiceProxy *proxy,
323 const char *variable,
324 GValue *value,
325 gpointer user_data)
326 {
327 const gchar *last_change;
328 GVariantBuilder array;
329 GVariant *val;
330 dls_device_t *device = user_data;
331 GUPnPCDSLastChangeParser *parser;
332 GList *list;
333 GList *next;
334 GError *error = NULL;
335
336 last_change = g_value_get_string(value);
337
338 DLEYNA_LOG_DEBUG_NL();
339 DLEYNA_LOG_DEBUG("LastChange XML: %s", last_change);
340 DLEYNA_LOG_DEBUG_NL();
341
342 parser = gupnp_cds_last_change_parser_new();
343 list = gupnp_cds_last_change_parser_parse(parser, last_change, &error);
344
345 if (error != NULL) {
346 DLEYNA_LOG_WARNING(
347 "gupnp_cds_last_change_parser_parse parsing failed: %s",
348 error->message);
349 goto on_error;
350 }
351
352 g_variant_builder_init(&array, G_VARIANT_TYPE("a(sv)"));
353 next = list;
354 while (next) {
355 prv_last_change_decode(next->data, &array, device->path);
356 gupnp_cds_last_change_entry_unref(next->data);
357 next = g_list_next(next);
358 }
359
360 val = g_variant_new("(@a(sv))", g_variant_builder_end(&array));
361
362 (void) dls_server_get_connector()->notify(device->connection,
363 device->path,
364 DLEYNA_SERVER_INTERFACE_MEDIA_DEVICE,
365 DLS_INTERFACE_ESV_LAST_CHANGE,
366 val,
367 NULL);
368
369 on_error:
370
371 g_list_free(list);
372 g_object_unref(parser);
373
374 if (error != NULL)
375 g_error_free(error);
376 }
377
378 static void prv_build_container_update_array(const gchar *root_path,
379 const gchar *value,
380 GVariantBuilder *builder)
381 {
382 gchar **str_array;
383 int pos = 0;
384 gchar *path;
385 guint id;
386
387 str_array = g_strsplit(value, ",", 0);
388
389 DLEYNA_LOG_DEBUG_NL();
390
391 while (str_array[pos] && str_array[pos + 1]) {
392 path = dls_path_from_id(root_path, str_array[pos++]);
393 id = atoi(str_array[pos++]);
394 g_variant_builder_add(builder, "(ou)", path, id);
395 DLEYNA_LOG_DEBUG("@Id [%s] - Path [%s] - id[%d]",
396 str_array[pos-2], path, id);
397 g_free(path);
398 }
399
400 DLEYNA_LOG_DEBUG_NL();
401
402 g_strfreev(str_array);
403 }
404
405 static void prv_container_update_cb(GUPnPServiceProxy *proxy,
406 const char *variable,
407 GValue *value,
408 gpointer user_data)
409 {
410 dls_device_t *device = user_data;
411 GVariantBuilder array;
412
413 g_variant_builder_init(&array, G_VARIANT_TYPE("a(ou)"));
414
415 prv_build_container_update_array(device->path,
416 g_value_get_string(value),
417 &array);
418
419 (void) dls_server_get_connector()->notify(
420 device->connection,
421 device->path,
422 DLEYNA_SERVER_INTERFACE_MEDIA_DEVICE,
423 DLS_INTERFACE_ESV_CONTAINER_UPDATE_IDS,
424 g_variant_new("(@a(ou))",
425 g_variant_builder_end(&array)),
426 NULL);
427 }
428
429 static void prv_system_update_cb(GUPnPServiceProxy *proxy,
430 const char *variable,
431 GValue *value,
432 gpointer user_data)
433 {
434 GVariantBuilder *array;
435 GVariant *val;
436 dls_device_t *device = user_data;
437 guint suid = g_value_get_uint(value);
438
439 DLEYNA_LOG_DEBUG("System Update %u", suid);
440
441 device->system_update_id = suid;
442
443 array = g_variant_builder_new(G_VARIANT_TYPE("a{sv}"));
444 g_variant_builder_add(array, "{sv}",
445 DLS_INTERFACE_PROP_ESV_SYSTEM_UPDATE_ID,
446 g_variant_new_uint32(suid));
447 val = g_variant_new("(s@a{sv}as)", DLEYNA_SERVER_INTERFACE_MEDIA_DEVICE,
448 g_variant_builder_end(array),
449 NULL);
450
451 (void) dls_server_get_connector()->notify(device->connection,
452 device->path,
453 DLS_INTERFACE_PROPERTIES,
454 DLS_INTERFACE_PROPERTIES_CHANGED,
455 val,
456 NULL);
457
458 g_variant_builder_unref(array);
459 }
460
461 static gboolean prv_re_enable_subscription(gpointer user_data)
462 {
463 dls_device_context_t *context = user_data;
464
465 context->timeout_id = 0;
466
467 return FALSE;
468 }
469
470 static void prv_subscription_lost_cb(GUPnPServiceProxy *proxy,
471 const GError *reason,
472 gpointer user_data)
473 {
474 dls_device_context_t *context = user_data;
475
476 if (!context->timeout_id) {
477 gupnp_service_proxy_set_subscribed(context->service_proxy,
478 TRUE);
479 context->timeout_id = g_timeout_add_seconds(
480 10,
481 prv_re_enable_subscription,
482 context);
483 } else {
484 g_source_remove(context->timeout_id);
485 gupnp_service_proxy_remove_notify(context->service_proxy,
486 DLS_SYSTEM_UPDATE_VAR,
487 prv_system_update_cb,
488 context->device);
489 gupnp_service_proxy_remove_notify(context->service_proxy,
490 DLS_CONTAINER_UPDATE_VAR,
491 prv_container_update_cb,
492 context->device);
493 gupnp_service_proxy_remove_notify(context->service_proxy,
494 DLS_LAST_CHANGE_VAR,
495 prv_last_change_cb,
496 context->device);
497
498 context->timeout_id = 0;
499 context->subscribed = FALSE;
500 }
501 }
502
503 void dls_device_subscribe_to_contents_change(dls_device_t *device)
504 {
505 dls_device_context_t *context;
506
507 context = dls_device_get_context(device, NULL);
508
509 DLEYNA_LOG_DEBUG("Subscribe for events on context: %s",
510 context->ip_address);
511
512 gupnp_service_proxy_add_notify(context->service_proxy,
513 DLS_SYSTEM_UPDATE_VAR,
514 G_TYPE_UINT,
515 prv_system_update_cb,
516 device);
517
518 gupnp_service_proxy_add_notify(context->service_proxy,
519 DLS_CONTAINER_UPDATE_VAR,
520 G_TYPE_STRING,
521 prv_container_update_cb,
522 device);
523
524 gupnp_service_proxy_add_notify(context->service_proxy,
525 DLS_LAST_CHANGE_VAR,
526 G_TYPE_STRING,
527 prv_last_change_cb,
528 device);
529
530 context->subscribed = TRUE;
531 gupnp_service_proxy_set_subscribed(context->service_proxy, TRUE);
532
533 g_signal_connect(context->service_proxy,
534 "subscription-lost",
535 G_CALLBACK(prv_subscription_lost_cb),
536 context);
537 }
538
539 static void prv_feature_list_add_feature(gchar *root_path,
540 GUPnPFeature *feature,
541 GVariantBuilder *vb)
542 {
543 GVariantBuilder vbo;
544 GVariant *var_obj;
545 const char *name;
546 const char *version;
547 const char *obj_id;
548 gchar **obj;
549 gchar **saved;
550 gchar *path;
551
552 name = gupnp_feature_get_name(feature);
553 version = gupnp_feature_get_version(feature);
554 obj_id = gupnp_feature_get_object_ids(feature);
555
556 g_variant_builder_init(&vbo, G_VARIANT_TYPE("ao"));
557
558 if (obj_id != NULL && *obj_id) {
559 obj = g_strsplit(obj_id, ",", 0);
560 saved = obj;
561
562 while (obj && *obj) {
563 path = dls_path_from_id(root_path, *obj);
564 g_variant_builder_add(&vbo, "o", path);
565 g_free(path);
566 obj++;
567 }
568
569 g_strfreev(saved);
570 }
571
572 var_obj = g_variant_builder_end(&vbo);
573 g_variant_builder_add(vb, "(ss@ao)", name, version, var_obj);
574 }
575
576 static void prv_get_feature_list_analyze(dls_device_t *device, gchar *result)
577 {
578 GUPnPFeatureListParser *parser;
579 GUPnPFeature *feature;
580 GList *list;
581 GList *item;
582 GError *error = NULL;
583 GVariantBuilder vb;
584 #if DLEYNA_LOG_LEVEL & DLEYNA_LOG_LEVEL_DEBUG
585 char *str;
586 #endif
587 parser = gupnp_feature_list_parser_new();
588 list = gupnp_feature_list_parser_parse_text(parser, result, &error);
589
590 if (error != NULL) {
591 DLEYNA_LOG_WARNING("GetFeatureList parsing failed: %s",
592 error->message);
593 goto on_exit;
594 }
595
596 g_variant_builder_init(&vb, G_VARIANT_TYPE("a(ssao)"));
597 item = list;
598
599 while (item != NULL) {
600 feature = (GUPnPFeature *)item->data;
601 prv_feature_list_add_feature(device->path, feature, &vb);
602 g_object_unref(feature);
603 item = g_list_next(item);
604 }
605
606 device->feature_list = g_variant_ref_sink(g_variant_builder_end(&vb));
607
608 #if DLEYNA_LOG_LEVEL & DLEYNA_LOG_LEVEL_DEBUG
609 str = g_variant_print(device->feature_list, FALSE);
610 DLEYNA_LOG_DEBUG("%s = %s", DLS_INTERFACE_PROP_SV_FEATURE_LIST, str);
611 g_free(str);
612 #endif
613
614 on_exit:
615 g_list_free(list);
616 g_object_unref(parser);
617
618 if (error != NULL)
619 g_error_free(error);
620 }
621
622 static void prv_get_feature_list_cb(GUPnPServiceProxy *proxy,
623 GUPnPServiceProxyAction *action,
624 gpointer user_data)
625 {
626 gchar *result = NULL;
627 GError *error = NULL;
628 prv_new_device_ct_t *priv_t = (prv_new_device_ct_t *)user_data;
629
630 if (!gupnp_service_proxy_end_action(proxy, action, &error,
631 "FeatureList", G_TYPE_STRING,
632 &result, NULL)) {
633 DLEYNA_LOG_WARNING("GetFeatureList operation failed: %s",
634 error->message);
635 goto on_error;
636 }
637
638 DLEYNA_LOG_DEBUG("GetFeatureList result: %s", result);
639
640 prv_get_feature_list_analyze(priv_t->dev, result);
641
642 on_error:
643 if (error != NULL)
644 g_error_free(error);
645
646 g_free(result);
647 }
648
649 static GUPnPServiceProxyAction *prv_get_feature_list(
650 dleyna_service_task_t *task,
651 GUPnPServiceProxy *proxy,
652 gboolean *failed)
653 {
654 *failed = FALSE;
655
656 return gupnp_service_proxy_begin_action(
657 proxy, "GetFeatureList",
658 dleyna_service_task_begin_action_cb,
659 task, NULL);
660 }
661
662 static void prv_get_sort_ext_capabilities_analyze(dls_device_t *device,
663 gchar *result)
664 {
665 gchar **caps;
666 gchar **saved;
667 #if DLEYNA_LOG_LEVEL & DLEYNA_LOG_LEVEL_DEBUG
668 gchar *props;
669 #endif
670 GVariantBuilder sort_ext_caps_vb;
671
672 g_variant_builder_init(&sort_ext_caps_vb, G_VARIANT_TYPE("as"));
673
674 caps = g_strsplit(result, ",", 0);
675 saved = caps;
676
677 while (caps && *caps) {
678 g_variant_builder_add(&sort_ext_caps_vb, "s", *caps);
679 caps++;
680 }
681
682 g_strfreev(saved);
683
684 device->sort_ext_caps = g_variant_ref_sink(g_variant_builder_end(
685 &sort_ext_caps_vb));
686
687 #if DLEYNA_LOG_LEVEL & DLEYNA_LOG_LEVEL_DEBUG
688 props = g_variant_print(device->sort_ext_caps, FALSE);
689 DLEYNA_LOG_DEBUG("%s = %s",
690 DLS_INTERFACE_PROP_SV_SORT_EXT_CAPABILITIES, props);
691 g_free(props);
692 #endif
693 }
694
695 static void prv_get_sort_ext_capabilities_cb(GUPnPServiceProxy *proxy,
696 GUPnPServiceProxyAction *action,
697 gpointer user_data)
698 {
699 gchar *result = NULL;
700 GError *error = NULL;
701 prv_new_device_ct_t *priv_t = (prv_new_device_ct_t *)user_data;
702
703 if (!gupnp_service_proxy_end_action(proxy, action, &error,
704 "SortExtensionCaps",
705 G_TYPE_STRING, &result, NULL)) {
706 DLEYNA_LOG_WARNING(
707 "GetSortExtensionCapabilities operation failed: %s",
708 error->message);
709 goto on_error;
710 }
711
712 DLEYNA_LOG_DEBUG("GetSortExtensionCapabilities result: %s", result);
713
714 prv_get_sort_ext_capabilities_analyze(priv_t->dev, result);
715
716 on_error:
717
718 if (error)
719 g_error_free(error);
720
721 g_free(result);
722 }
723
724 static GUPnPServiceProxyAction *prv_get_sort_ext_capabilities(
725 dleyna_service_task_t *task,
726 GUPnPServiceProxy *proxy,
727 gboolean *failed)
728 {
729 *failed = FALSE;
730
731 return gupnp_service_proxy_begin_action(
732 proxy,
733 "GetSortExtensionCapabilities",
734 dleyna_service_task_begin_action_cb,
735 task, NULL);
736 }
737
738 static void prv_get_capabilities_analyze(GHashTable *property_map,
739 gchar *result,
740 GVariant **variant)
741 {
742 gchar **caps;
743 gchar **saved;
744 gchar *prop_name;
745 GVariantBuilder caps_vb;
746
747 g_variant_builder_init(&caps_vb, G_VARIANT_TYPE("as"));
748
749 caps = g_strsplit(result, ",", 0);
750 saved = caps;
751
752 while (caps && *caps) {
753 prop_name = g_hash_table_lookup(property_map, *caps);
754
755 if (prop_name)
756 g_variant_builder_add(&caps_vb, "s", prop_name);
757
758 caps++;
759 }
760
761 g_strfreev(saved);
762
763 *variant = g_variant_ref_sink(g_variant_builder_end(&caps_vb));
764
765 #if DLEYNA_LOG_LEVEL & DLEYNA_LOG_LEVEL_DEBUG
766 prop_name = g_variant_print(*variant, FALSE);
767 DLEYNA_LOG_DEBUG("%s = %s", " Variant", prop_name);
768 g_free(prop_name);
769 #endif
770 }
771
772 static void prv_get_sort_capabilities_cb(GUPnPServiceProxy *proxy,
773 GUPnPServiceProxyAction *action,
774 gpointer user_data)
775 {
776 gchar *result = NULL;
777 GError *error = NULL;
778 prv_new_device_ct_t *priv_t = (prv_new_device_ct_t *)user_data;
779
780 if (!gupnp_service_proxy_end_action(proxy, action, &error, "SortCaps",
781 G_TYPE_STRING, &result, NULL)) {
782 DLEYNA_LOG_WARNING("GetSortCapabilities operation failed: %s",
783 error->message);
784 goto on_error;
785 }
786
787 DLEYNA_LOG_DEBUG("GetSortCapabilities result: %s", result);
788
789 prv_get_capabilities_analyze(priv_t->property_map, result,
790 &priv_t->dev->sort_caps);
791
792 on_error:
793
794 if (error)
795 g_error_free(error);
796
797 g_free(result);
798 }
799
800 static GUPnPServiceProxyAction *prv_get_sort_capabilities(
801 dleyna_service_task_t *task,
802 GUPnPServiceProxy *proxy,
803 gboolean *failed)
804 {
805 *failed = FALSE;
806
807 return gupnp_service_proxy_begin_action(
808 proxy,
809 "GetSortCapabilities",
810 dleyna_service_task_begin_action_cb,
811 task, NULL);
812 }
813
814 static void prv_get_search_capabilities_cb(GUPnPServiceProxy *proxy,
815 GUPnPServiceProxyAction *action,
816 gpointer user_data)
817 {
818 gchar *result = NULL;
819 GError *error = NULL;
820 prv_new_device_ct_t *priv_t = (prv_new_device_ct_t *)user_data;
821
822 if (!gupnp_service_proxy_end_action(proxy, action, &error, "SearchCaps",
823 G_TYPE_STRING, &result, NULL)) {
824 DLEYNA_LOG_WARNING("GetSearchCapabilities operation failed: %s",
825 error->message);
826 goto on_error;
827 }
828
829 DLEYNA_LOG_DEBUG("GetSearchCapabilities result: %s", result);
830
831 prv_get_capabilities_analyze(priv_t->property_map, result,
832 &priv_t->dev->search_caps);
833
834 on_error:
835
836 if (error)
837 g_error_free(error);
838
839 g_free(result);
840 }
841
842 static GUPnPServiceProxyAction *prv_get_search_capabilities(
843 dleyna_service_task_t *task,
844 GUPnPServiceProxy *proxy,
845 gboolean *failed)
846 {
847 *failed = FALSE;
848
849 return gupnp_service_proxy_begin_action(
850 proxy, "GetSearchCapabilities",
851 dleyna_service_task_begin_action_cb,
852 task, NULL);
853 }
854
855 static GUPnPServiceProxyAction *prv_subscribe(dleyna_service_task_t *task,
856 GUPnPServiceProxy *proxy,
857 gboolean *failed)
858 {
859 dls_device_t *device;
860
861 device = (dls_device_t *)dleyna_service_task_get_user_data(task);
862 dls_device_subscribe_to_contents_change(device);
863
864 *failed = FALSE;
865
866 return NULL;
867 }
868
869 static gboolean prv_subtree_interface_filter(const gchar *object_path,
870 const gchar *node,
871 const gchar *interface)
872 {
873 gboolean root_object = FALSE;
874 const gchar *slash;
875 gboolean retval = TRUE;
876
877 /* All objects in the hierarchy support the same interface. Strictly
878 speaking this is not correct as it will allow ListChildren to be
879 executed on a mediaitem object. However, returning the correct
880 interface here would be too inefficient. We would need to either
881 cache the type of all objects encountered so far or issue a UPnP
882 request here to determine the objects type. Best to let the client
883 call ListChildren on a item. This will lead to an error when we
884 execute the UPnP command and we can return an error then.
885
886 We do know however that the root objects are containers. Therefore
887 we can remove the MediaItem2 interface from the root containers. We
888 also know that only the root objects suport the MediaDevice
889 interface.
890 */
891
892 if (dls_path_get_non_root_id(object_path, &slash))
893 root_object = !slash;
894
895 if (root_object && !strcmp(interface, DLS_INTERFACE_MEDIA_ITEM))
896 retval = FALSE;
897 else if (!root_object && !strcmp(interface,
898 DLEYNA_SERVER_INTERFACE_MEDIA_DEVICE))
899 retval = FALSE;
900
901 return retval;
902 }
903
904 static GUPnPServiceProxyAction *prv_declare(dleyna_service_task_t *task,
905 GUPnPServiceProxy *proxy,
906 gboolean *failed)
907 {
908 guint id;
909 dls_device_t *device;
910 prv_new_device_ct_t *priv_t;
911
912 priv_t = (prv_new_device_ct_t *)dleyna_service_task_get_user_data(task);
913 device = priv_t->dev;
914
915 id = dls_server_get_connector()->publish_subtree(priv_t->connection,
916 device->path,
917 priv_t->vtable,
918 DLS_INTERFACE_INFO_MAX,
919 prv_subtree_interface_filter);
920 if (id) {
921 device->id = id;
922
923 device->uploads = g_hash_table_new_full(
924 g_int_hash,
925 g_int_equal,
926 g_free,
927 prv_upload_delete);
928 device->upload_jobs = g_hash_table_new_full(
929 g_int_hash,
930 g_int_equal,
931 g_free,
932 prv_upload_job_delete);
933
934 } else {
935 DLEYNA_LOG_WARNING("dleyna_connector_publish_subtree FAILED");
936 }
937
938 *failed = (!id);
939
940 return NULL;
941 }
942
943 dls_device_t *dls_device_new(
944 dleyna_connector_id_t connection,
945 GUPnPDeviceProxy *proxy,
946 const gchar *ip_address,
947 const dleyna_connector_dispatch_cb_t *dispatch_table,
948 GHashTable *property_map,
949 guint counter,
950 const dleyna_task_queue_key_t *queue_id)
951 {
952 dls_device_t *dev;
953 prv_new_device_ct_t *priv_t;
954 gchar *new_path;
955 dls_device_context_t *context;
956 GUPnPServiceProxy *s_proxy;
957
958 DLEYNA_LOG_DEBUG("New Device on %s", ip_address);
959
960 new_path = g_strdup_printf("%s/%u", DLEYNA_SERVER_PATH, counter);
961 DLEYNA_LOG_DEBUG("Server Path %s", new_path);
962
963 dev = g_new0(dls_device_t, 1);
964 priv_t = g_new0(prv_new_device_ct_t, 1);
965
966 dev->connection = connection;
967 dev->contexts = g_ptr_array_new_with_free_func(prv_context_delete);
968 dev->path = new_path;
969
970 priv_t->dev = dev;
971 priv_t->connection = connection;
972 priv_t->vtable = dispatch_table;
973 priv_t->property_map = property_map;
974
975 context = dls_device_append_new_context(dev, ip_address, proxy);
976 s_proxy = context->service_proxy;
977
978 dleyna_service_task_add(queue_id, prv_get_search_capabilities,
979 s_proxy,
980 prv_get_search_capabilities_cb, NULL, priv_t);
981
982 dleyna_service_task_add(queue_id, prv_get_sort_capabilities,
983 s_proxy,
984 prv_get_sort_capabilities_cb, NULL, priv_t);
985
986 dleyna_service_task_add(queue_id, prv_get_sort_ext_capabilities,
987 s_proxy,
988 prv_get_sort_ext_capabilities_cb, NULL, priv_t);
989
990 dleyna_service_task_add(queue_id, prv_get_feature_list, s_proxy,
991 prv_get_feature_list_cb, NULL, priv_t);
992
993 dleyna_service_task_add(queue_id, prv_subscribe, s_proxy,
994 NULL, NULL, dev);
995
996 dleyna_service_task_add(queue_id, prv_declare, s_proxy,
997 NULL, g_free, priv_t);
998
999 dleyna_task_queue_start(queue_id);
1000
1001 return dev;
1002 }
1003
1004 dls_device_context_t *dls_device_append_new_context(dls_device_t *device,
1005 const gchar *ip_address,
1006 GUPnPDeviceProxy *proxy)
1007 {
1008 dls_device_context_t *context;
1009
1010 prv_context_new(ip_address, proxy, device, &context);
1011 g_ptr_array_add(device->contexts, context);
1012
1013 return context;
1014 }
1015
1016 dls_device_t *dls_device_from_path(const gchar *path, GHashTable *device_list)
1017 {
1018 GHashTableIter iter;
1019 gpointer value;
1020 dls_device_t *device;
1021 dls_device_t *retval = NULL;
1022
1023 g_hash_table_iter_init(&iter, device_list);
1024
1025 while (g_hash_table_iter_next(&iter, NULL, &value)) {
1026 device = value;
1027
1028 if (!strcmp(device->path, path)) {
1029 retval = device;
1030 break;
1031 }
1032 }
1033
1034 return retval;
1035 }
1036
1037 dls_device_context_t *dls_device_get_context(const dls_device_t *device,
1038 dls_client_t *client)
1039 {
1040 dls_device_context_t *context;
1041 unsigned int i;
1042 const char ip4_local_prefix[] = "127.0.0.";
1043 gboolean prefer_local;
1044 gboolean is_local;
1045
1046 prefer_local = (client && client->prefer_local_addresses);
1047
1048 for (i = 0; i < device->contexts->len; ++i) {
1049 context = g_ptr_array_index(device->contexts, i);
1050
1051 is_local = (!strncmp(context->ip_address, ip4_local_prefix,
1052 sizeof(ip4_local_prefix) - 1) ||
1053 !strcmp(context->ip_address, "::1") ||
1054 !strcmp(context->ip_address, "0:0:0:0:0:0:0:1"));
1055
1056 if (prefer_local == is_local)
1057 break;
1058 }
1059
1060 if (i == device->contexts->len)
1061 context = g_ptr_array_index(device->contexts, 0);
1062
1063 return context;
1064 }
1065
1066 static void prv_found_child(GUPnPDIDLLiteParser *parser,
1067 GUPnPDIDLLiteObject *object,
1068 gpointer user_data)
1069 {
1070 dls_async_task_t *cb_data = user_data;
1071 dls_task_t *task = &cb_data->task;
1072 dls_task_get_children_t *task_data = &task->ut.get_children;
1073 dls_async_bas_t *cb_task_data = &cb_data->ut.bas;
1074 dls_device_object_builder_t *builder;
1075 gboolean have_child_count;
1076
1077 DLEYNA_LOG_DEBUG("Enter");
1078
1079 builder = g_new0(dls_device_object_builder_t, 1);
1080
1081 if (GUPNP_IS_DIDL_LITE_CONTAINER(object)) {
1082 if (!task_data->containers)
1083 goto on_error;
1084 } else {
1085 if (!task_data->items)
1086 goto on_error;
1087 }
1088
1089 builder->vb = g_variant_builder_new(G_VARIANT_TYPE("a{sv}"));
1090
1091 if (!dls_props_add_object(builder->vb, object, task->target.root_path,
1092 task->target.path, cb_task_data->filter_mask))
1093 goto on_error;
1094
1095 if (GUPNP_IS_DIDL_LITE_CONTAINER(object)) {
1096 dls_props_add_container(builder->vb,
1097 (GUPnPDIDLLiteContainer *)object,
1098 cb_task_data->filter_mask,
1099 &have_child_count);
1100
1101 if (!have_child_count && (cb_task_data->filter_mask &
1102 DLS_UPNP_MASK_PROP_CHILD_COUNT)) {
1103 builder->needs_child_count = TRUE;
1104 builder->id = g_strdup(
1105 gupnp_didl_lite_object_get_id(object));
1106 cb_task_data->need_child_count = TRUE;
1107 }
1108 } else {
1109 dls_props_add_item(builder->vb, object,
1110 task->target.root_path,
1111 cb_task_data->filter_mask,
1112 cb_task_data->protocol_info);
1113 }
1114
1115 g_ptr_array_add(cb_task_data->vbs, builder);
1116
1117 DLEYNA_LOG_DEBUG("Exit with SUCCESS");
1118
1119 return;
1120
1121 on_error:
1122
1123 prv_object_builder_delete(builder);
1124
1125 DLEYNA_LOG_DEBUG("Exit with FAIL");
1126 }
1127
1128 static GVariant *prv_children_result_to_variant(dls_async_task_t *cb_data)
1129 {
1130 guint i;
1131 dls_device_object_builder_t *builder;
1132 dls_async_bas_t *cb_task_data = &cb_data->ut.bas;
1133 GVariantBuilder vb;
1134
1135 g_variant_builder_init(&vb, G_VARIANT_TYPE("aa{sv}"));
1136
1137 for (i = 0; i < cb_task_data->vbs->len; ++i) {
1138 builder = g_ptr_array_index(cb_task_data->vbs, i);
1139 g_variant_builder_add(&vb, "@a{sv}",
1140 g_variant_builder_end(builder->vb));
1141 }
1142
1143 return g_variant_builder_end(&vb);
1144 }
1145
1146 static void prv_get_search_ex_result(dls_async_task_t *cb_data)
1147 {
1148 GVariant *out_params[2];
1149 dls_async_bas_t *cb_task_data = &cb_data->ut.bas;
1150
1151 out_params[0] = prv_children_result_to_variant(cb_data);
1152 out_params[1] = g_variant_new_uint32(cb_task_data->max_count);
1153
1154 cb_data->task.result = g_variant_ref_sink(
1155 g_variant_new_tuple(out_params, 2));
1156 }
1157
1158 static void prv_get_children_result(dls_async_task_t *cb_data)
1159 {
1160 GVariant *retval = prv_children_result_to_variant(cb_data);
1161 cb_data->task.result = g_variant_ref_sink(retval);
1162 }
1163
1164 static gboolean prv_child_count_for_list_cb(dls_async_task_t *cb_data,
1165 gint count)
1166 {
1167 dls_async_bas_t *cb_task_data = &cb_data->ut.bas;
1168 dls_device_object_builder_t *builder;
1169
1170 builder = g_ptr_array_index(cb_task_data->vbs, cb_task_data->retrieved);
1171 dls_props_add_child_count(builder->vb, count);
1172 cb_task_data->retrieved++;
1173 prv_retrieve_child_count_for_list(cb_data);
1174
1175 return cb_task_data->retrieved >= cb_task_data->vbs->len;
1176 }
1177
1178 static void prv_retrieve_child_count_for_list(dls_async_task_t *cb_data)
1179 {
1180 dls_async_bas_t *cb_task_data = &cb_data->ut.bas;
1181 dls_device_object_builder_t *builder;
1182 guint i;
1183
1184 for (i = cb_task_data->retrieved; i < cb_task_data->vbs->len; ++i) {
1185 builder = g_ptr_array_index(cb_task_data->vbs, i);
1186
1187 if (builder->needs_child_count)
1188 break;
1189 }
1190
1191 cb_task_data->retrieved = i;
1192
1193 if (i < cb_task_data->vbs->len)
1194 prv_get_child_count(cb_data, prv_child_count_for_list_cb,
1195 builder->id);
1196 else
1197 cb_task_data->get_children_cb(cb_data);
1198 }
1199
1200 static void prv_get_children_cb(GUPnPServiceProxy *proxy,
1201 GUPnPServiceProxyAction *action,
1202 gpointer user_data)
1203 {
1204 gchar *result = NULL;
1205 GUPnPDIDLLiteParser *parser = NULL;
1206 GError *upnp_error = NULL;
1207 dls_async_task_t *cb_data = user_data;
1208 dls_async_bas_t *cb_task_data = &cb_data->ut.bas;
1209
1210 DLEYNA_LOG_DEBUG("Enter");
1211
1212 if (!gupnp_service_proxy_end_action(cb_data->proxy, cb_data->action,
1213 &upnp_error,
1214 "Result", G_TYPE_STRING,
1215 &result, NULL)) {
1216 DLEYNA_LOG_WARNING("Browse operation failed: %s",
1217 upnp_error->message);
1218
1219 cb_data->error = g_error_new(DLEYNA_SERVER_ERROR,
1220 DLEYNA_ERROR_OPERATION_FAILED,
1221 "Browse operation failed: %s",
1222 upnp_error->message);
1223 goto on_error;
1224 }
1225
1226 DLEYNA_LOG_DEBUG("GetChildren result: %s", result);
1227
1228 parser = gupnp_didl_lite_parser_new();
1229
1230 g_signal_connect(parser, "object-available" ,
1231 G_CALLBACK(prv_found_child), cb_data);
1232
1233 cb_task_data->vbs = g_ptr_array_new_with_free_func(
1234 prv_object_builder_delete);
1235
1236 if (!gupnp_didl_lite_parser_parse_didl(parser, result, &upnp_error) &&
1237 upnp_error->code != GUPNP_XML_ERROR_EMPTY_NODE) {
1238 DLEYNA_LOG_WARNING("Unable to parse results of browse: %s",
1239 upnp_error->message);
1240
1241 cb_data->error = g_error_new(DLEYNA_SERVER_ERROR,
1242 DLEYNA_ERROR_OPERATION_FAILED,
1243 "Unable to parse results of browse: %s",
1244 upnp_error->message);
1245 goto on_error;
1246 }
1247
1248 if (cb_task_data->need_child_count) {
1249 DLEYNA_LOG_DEBUG("Need to retrieve ChildCounts");
1250
1251 cb_task_data->get_children_cb = prv_get_children_result;
1252 prv_retrieve_child_count_for_list(cb_data);
1253 goto no_complete;
1254 } else {
1255 prv_get_children_result(cb_data);
1256 }
1257
1258 on_error:
1259
1260 (void) g_idle_add(dls_async_task_complete, cb_data);
1261 g_cancellable_disconnect(cb_data->cancellable, cb_data->cancel_id);
1262
1263 no_complete:
1264
1265 if (upnp_error)
1266 g_error_free(upnp_error);
1267
1268 if (parser)
1269 g_object_unref(parser);
1270
1271 g_free(result);
1272
1273 DLEYNA_LOG_DEBUG("Exit");
1274 }
1275
1276 void dls_device_get_children(dls_client_t *client,
1277 dls_task_t *task,
1278 const gchar *upnp_filter, const gchar *sort_by)
1279 {
1280 dls_async_task_t *cb_data = (dls_async_task_t *)task;
1281 dls_device_context_t *context;
1282
1283 DLEYNA_LOG_DEBUG("Enter");
1284
1285 context = dls_device_get_context(task->target.device, client);
1286
1287 cb_data->action =
1288 gupnp_service_proxy_begin_action(context->service_proxy,
1289 "Browse",
1290 prv_get_children_cb,
1291 cb_data,
1292 "ObjectID", G_TYPE_STRING,
1293 task->target.id,
1294
1295 "BrowseFlag", G_TYPE_STRING,
1296 "BrowseDirectChildren",
1297
1298 "Filter", G_TYPE_STRING,
1299 upnp_filter,
1300
1301 "StartingIndex", G_TYPE_INT,
1302 task->ut.get_children.start,
1303 "RequestedCount", G_TYPE_INT,
1304 task->ut.get_children.count,
1305 "SortCriteria", G_TYPE_STRING,
1306 sort_by,
1307 NULL);
1308
1309 cb_data->proxy = context->service_proxy;
1310
1311 g_object_add_weak_pointer((G_OBJECT(context->service_proxy)),
1312 (gpointer *)&cb_data->proxy);
1313
1314 cb_data->cancel_id = g_cancellable_connect(
1315 cb_data->cancellable,
1316 G_CALLBACK(dls_async_task_cancelled_cb),
1317 cb_data, NULL);
1318
1319 DLEYNA_LOG_DEBUG("Exit");
1320 }
1321
1322 static void prv_get_item(GUPnPDIDLLiteParser *parser,
1323 GUPnPDIDLLiteObject *object,
1324 gpointer user_data)
1325 {
1326 dls_async_task_t *cb_data = user_data;
1327 dls_async_get_all_t *cb_task_data = &cb_data->ut.get_all;
1328
1329 if (!GUPNP_IS_DIDL_LITE_CONTAINER(object))
1330 dls_props_add_item(cb_task_data->vb, object,
1331 cb_data->task.target.root_path,
1332 DLS_UPNP_MASK_ALL_PROPS,
1333 cb_task_data->protocol_info);
1334 else
1335 cb_data->error = g_error_new(DLEYNA_SERVER_ERROR,
1336 DLEYNA_ERROR_UNKNOWN_INTERFACE,
1337 "Interface not supported on container.");
1338 }
1339
1340 static void prv_get_container(GUPnPDIDLLiteParser *parser,
1341 GUPnPDIDLLiteObject *object,
1342 gpointer user_data)
1343 {
1344 dls_async_task_t *cb_data = user_data;
1345 dls_async_get_all_t *cb_task_data = &cb_data->ut.get_all;
1346 gboolean have_child_count;
1347
1348 if (GUPNP_IS_DIDL_LITE_CONTAINER(object)) {
1349 dls_props_add_container(cb_task_data->vb,
1350 (GUPnPDIDLLiteContainer *)object,
1351 DLS_UPNP_MASK_ALL_PROPS,
1352 &have_child_count);
1353 if (!have_child_count)
1354 cb_task_data->need_child_count = TRUE;
1355 } else {
1356 cb_data->error = g_error_new(DLEYNA_SERVER_ERROR,
1357 DLEYNA_ERROR_UNKNOWN_INTERFACE,
1358 "Interface not supported on item.");
1359 }
1360 }
1361
1362 static void prv_get_object(GUPnPDIDLLiteParser *parser,
1363 GUPnPDIDLLiteObject *object,
1364 gpointer user_data)
1365 {
1366 dls_async_task_t *cb_data = user_data;
1367 dls_async_get_all_t *cb_task_data = &cb_data->ut.get_all;
1368 const char *id;
1369 const char *parent_path;
1370 gchar *path = NULL;
1371
1372 id = gupnp_didl_lite_object_get_parent_id(object);
1373
1374 if (!id || !strcmp(id, "-1") || !strcmp(id, "")) {
1375 parent_path = cb_data->task.target.root_path;
1376 } else {
1377 path = dls_path_from_id(cb_data->task.target.root_path, id);
1378 parent_path = path;
1379 }
1380
1381 if (!dls_props_add_object(cb_task_data->vb, object,
1382 cb_data->task.target.root_path,
1383 parent_path, DLS_UPNP_MASK_ALL_PROPS))
1384 cb_data->error = g_error_new(DLEYNA_SERVER_ERROR,
1385 DLEYNA_ERROR_BAD_RESULT,
1386 "Unable to retrieve mandatory object properties");
1387 g_free(path);
1388 }
1389
1390 static void prv_get_all(GUPnPDIDLLiteParser *parser,
1391 GUPnPDIDLLiteObject *object,
1392 gpointer user_data)
1393 {
1394 dls_async_task_t *cb_data = user_data;
1395 dls_async_get_all_t *cb_task_data = &cb_data->ut.get_all;
1396 gboolean have_child_count;
1397
1398 prv_get_object(parser, object, user_data);
1399
1400 if (!cb_data->error) {
1401 if (GUPNP_IS_DIDL_LITE_CONTAINER(object)) {
1402 dls_props_add_container(
1403 cb_task_data->vb,
1404 (GUPnPDIDLLiteContainer *)
1405 object, DLS_UPNP_MASK_ALL_PROPS,
1406 &have_child_count);
1407 if (!have_child_count)
1408 cb_task_data->need_child_count = TRUE;
1409 } else {
1410 dls_props_add_item(cb_task_data->vb,
1411 object,
1412 cb_data->task.target.root_path,
1413 DLS_UPNP_MASK_ALL_PROPS,
1414 cb_task_data->protocol_info);
1415 }
1416 }
1417 }
1418
1419 static gboolean prv_subscribed(const dls_device_t *device)
1420 {
1421 dls_device_context_t *context;
1422 unsigned int i;
1423 gboolean subscribed = FALSE;
1424
1425 for (i = 0; i < device->contexts->len; ++i) {
1426 context = g_ptr_array_index(device->contexts, i);
1427 if (context->subscribed) {
1428 subscribed = TRUE;
1429 break;
1430 }
1431 }
1432
1433 return subscribed;
1434 }
1435
1436 static void prv_system_update_id_for_prop_cb(GUPnPServiceProxy *proxy,
1437 GUPnPServiceProxyAction *action,
1438 gpointer user_data)
1439 {
1440 GError *upnp_error = NULL;
1441 guint id;
1442 dls_async_task_t *cb_data = user_data;
1443
1444 DLEYNA_LOG_DEBUG("Enter");
1445
1446 if (!gupnp_service_proxy_end_action(proxy, action, &upnp_error,
1447 "Id", G_TYPE_UINT,
1448 &id,
1449 NULL)) {
1450 DLEYNA_LOG_WARNING("Unable to retrieve ServiceUpdateID: %s %s",
1451 g_quark_to_string(upnp_error->domain),
1452 upnp_error->message);
1453
1454 cb_data->error = g_error_new(
1455 DLEYNA_SERVER_ERROR,
1456 DLEYNA_ERROR_OPERATION_FAILED,
1457 "Unable to retrieve ServiceUpdateID: %s",
1458 upnp_error->message);
1459
1460 goto on_complete;
1461 }
1462
1463 cb_data->task.result = g_variant_ref_sink(g_variant_new_uint32(id));
1464
1465 on_complete:
1466
1467 (void) g_idle_add(dls_async_task_complete, cb_data);
1468 g_cancellable_disconnect(cb_data->cancellable, cb_data->cancel_id);
1469
1470 if (upnp_error)
1471 g_error_free(upnp_error);
1472
1473 DLEYNA_LOG_DEBUG("Exit");
1474 }
1475
1476 static void prv_get_system_update_id_for_prop(GUPnPServiceProxy *proxy,
1477 const dls_device_t *device,
1478 dls_async_task_t *cb_data)
1479 {
1480 guint suid;
1481
1482 DLEYNA_LOG_DEBUG("Enter");
1483
1484 if (prv_subscribed(device)) {
1485 suid = device->system_update_id;
1486
1487 cb_data->task.result = g_variant_ref_sink(
1488 g_variant_new_uint32(suid));
1489
1490 (void) g_idle_add(dls_async_task_complete, cb_data);
1491
1492 goto on_complete;
1493 }
1494
1495 gupnp_service_proxy_begin_action(proxy, "GetSystemUpdateID",
1496 prv_system_update_id_for_prop_cb,
1497 cb_data,
1498 NULL);
1499
1500 cb_data->proxy = proxy;
1501
1502 g_object_add_weak_pointer((G_OBJECT(proxy)),
1503 (gpointer *)&cb_data->proxy);
1504
1505 cb_data->cancel_id = g_cancellable_connect(
1506 cb_data->cancellable,
1507 G_CALLBACK(dls_async_task_cancelled_cb),
1508 cb_data, NULL);
1509
1510 on_complete:
1511
1512 DLEYNA_LOG_DEBUG("Exit");
1513 }
1514
1515 static void prv_system_update_id_for_props_cb(GUPnPServiceProxy *proxy,
1516 GUPnPServiceProxyAction *action,
1517 gpointer user_data)
1518 {
1519 GError *upnp_error = NULL;
1520 guint id;
1521 dls_async_task_t *cb_data = user_data;
1522 dls_async_get_all_t *cb_task_data = &cb_data->ut.get_all;
1523
1524 DLEYNA_LOG_DEBUG("Enter");
1525
1526 if (!gupnp_service_proxy_end_action(proxy, action, &upnp_error,
1527 "Id", G_TYPE_UINT,
1528 &id,
1529 NULL)) {
1530 DLEYNA_LOG_WARNING("Unable to retrieve ServiceUpdateID: %s %s",
1531 g_quark_to_string(upnp_error->domain),
1532 upnp_error->message);
1533
1534 cb_data->error = g_error_new(
1535 DLEYNA_SERVER_ERROR,
1536 DLEYNA_ERROR_OPERATION_FAILED,
1537 "Unable to retrieve ServiceUpdateID: %s",
1538 upnp_error->message);
1539
1540 goto on_complete;
1541 }
1542
1543 g_variant_builder_add(cb_task_data->vb, "{sv}",
1544 DLS_SYSTEM_UPDATE_VAR,
1545 g_variant_new_uint32(id));
1546
1547 cb_data->task.result = g_variant_ref_sink(g_variant_builder_end(
1548 cb_task_data->vb));
1549
1550 on_complete:
1551
1552 if (!cb_data->error)
1553 prv_get_sr_token_for_props(proxy, cb_data->task.target.device,
1554 cb_data);
1555 else {
1556 (void) g_idle_add(dls_async_task_complete, cb_data);
1557 g_cancellable_disconnect(cb_data->cancellable,
1558 cb_data->cancel_id);
1559 }
1560
1561 if (upnp_error)
1562 g_error_free(upnp_error);
1563
1564 DLEYNA_LOG_DEBUG("Exit");
1565 }
1566
1567 static void prv_get_system_update_id_for_props(GUPnPServiceProxy *proxy,
1568 const dls_device_t *device,
1569 dls_async_task_t *cb_data)
1570 {
1571 dls_async_get_all_t *cb_task_data;
1572 guint suid;
1573
1574 DLEYNA_LOG_DEBUG("Enter");
1575
1576 if (prv_subscribed(device)) {
1577 suid = device->system_update_id;
1578
1579 cb_task_data = &cb_data->ut.get_all;
1580
1581 g_variant_builder_add(cb_task_data->vb, "{sv}",
1582 DLS_SYSTEM_UPDATE_VAR,
1583 g_variant_new_uint32(suid));
1584
1585 prv_get_sr_token_for_props(proxy, device, cb_data);
1586
1587 goto on_complete;
1588 }
1589
1590 gupnp_service_proxy_begin_action(proxy, "GetSystemUpdateID",
1591 prv_system_update_id_for_props_cb,
1592 cb_data,
1593 NULL);
1594
1595 cb_data->proxy = proxy;
1596
1597 g_object_add_weak_pointer((G_OBJECT(proxy)),
1598 (gpointer *)&cb_data->proxy);
1599
1600 cb_data->cancel_id = g_cancellable_connect(
1601 cb_data->cancellable,
1602 G_CALLBACK(dls_async_task_cancelled_cb),
1603 cb_data, NULL);
1604
1605 on_complete:
1606
1607 DLEYNA_LOG_DEBUG("Exit");
1608 }
1609
1610 static int prv_get_media_server_version(const dls_device_t *device)
1611 {
1612 dls_device_context_t *context;
1613 const char *device_type;
1614 const char *version;
1615
1616 context = dls_device_get_context(device, NULL);
1617 device_type = gupnp_device_info_get_device_type((GUPnPDeviceInfo *)
1618 context->device_proxy);
1619
1620 if (!g_str_has_prefix(device_type, DLS_DMS_DEVICE_TYPE))
1621 goto on_error;
1622
1623 version = device_type + sizeof(DLS_DMS_DEVICE_TYPE) - 1;
1624
1625 return atoi(version);
1626
1627 on_error:
1628
1629 return -1;
1630 }
1631
1632 static void prv_service_reset_for_prop_cb(GUPnPServiceProxy *proxy,
1633 GUPnPServiceProxyAction *action,
1634 gpointer user_data)
1635 {
1636 GError *upnp_error = NULL;
1637 gchar *token = NULL;
1638 dls_async_task_t *cb_data = user_data;
1639
1640 DLEYNA_LOG_DEBUG("Enter");
1641
1642 if (!gupnp_service_proxy_end_action(proxy, action, &upnp_error,
1643 "ResetToken", G_TYPE_STRING,
1644 &token,
1645 NULL)) {
1646 DLEYNA_LOG_WARNING(
1647 "Unable to retrieve ServiceResetToken: %s %s",
1648 g_quark_to_string(upnp_error->domain),
1649 upnp_error->message);
1650
1651
1652 cb_data->error = g_error_new(DLEYNA_SERVER_ERROR,
1653 DLEYNA_ERROR_OPERATION_FAILED,
1654 "GetServiceResetToken failed: %s",
1655 upnp_error->message);
1656
1657 goto on_complete;
1658 }
1659
1660 cb_data->task.result = g_variant_ref_sink(g_variant_new_string(token));
1661
1662 DLEYNA_LOG_DEBUG("Service Reset %s", token);
1663
1664 g_free(token);
1665
1666 on_complete:
1667
1668 (void) g_idle_add(dls_async_task_complete, cb_data);
1669 g_cancellable_disconnect(cb_data->cancellable, cb_data->cancel_id);
1670
1671 if (upnp_error)
1672 g_error_free(upnp_error);
1673
1674 DLEYNA_LOG_DEBUG("Exit");
1675 }
1676
1677 static void prv_get_sr_token_for_prop(GUPnPServiceProxy *proxy,
1678 const dls_device_t *device,
1679 dls_async_task_t *cb_data)
1680 {
1681 DLEYNA_LOG_DEBUG("Enter");
1682
1683 if (prv_get_media_server_version(device) < 3) {
1684 cb_data->error = g_error_new(DLEYNA_SERVER_ERROR,
1685 DLEYNA_ERROR_UNKNOWN_PROPERTY,
1686 "Unknown property");
1687
1688 (void) g_idle_add(dls_async_task_complete, cb_data);
1689
1690 goto on_error;
1691 }
1692
1693 gupnp_service_proxy_begin_action(proxy, "GetServiceResetToken",
1694 prv_service_reset_for_prop_cb,
1695 cb_data,
1696 NULL);
1697
1698 cb_data->proxy = proxy;
1699
1700 g_object_add_weak_pointer((G_OBJECT(proxy)),
1701 (gpointer *)&cb_data->proxy);
1702
1703 cb_data->cancel_id = g_cancellable_connect(
1704 cb_data->cancellable,
1705 G_CALLBACK(dls_async_task_cancelled_cb),
1706 cb_data, NULL);
1707
1708 on_error:
1709
1710 DLEYNA_LOG_DEBUG("Exit");
1711 }
1712
1713 static void prv_service_reset_for_props_cb(GUPnPServiceProxy *proxy,
1714 GUPnPServiceProxyAction *action,
1715 gpointer user_data)
1716 {
1717 GError *upnp_error = NULL;
1718 gchar *token = NULL;
1719 dls_async_task_t *cb_data = user_data;
1720 dls_async_get_all_t *cb_task_data;
1721
1722 DLEYNA_LOG_DEBUG("Enter");
1723
1724 if (!gupnp_service_proxy_end_action(proxy, action, &upnp_error,
1725 "ResetToken", G_TYPE_STRING,
1726 &token,
1727 NULL)) {
1728 DLEYNA_LOG_WARNING(
1729 "Unable to retrieve ServiceResetToken: %s %s",
1730 g_quark_to_string(upnp_error->domain),
1731 upnp_error->message);
1732
1733 cb_data->error = g_error_new(DLEYNA_SERVER_ERROR,
1734 DLEYNA_ERROR_OPERATION_FAILED,
1735 "GetServiceResetToken failed: %s",
1736 upnp_error->message);
1737
1738 goto on_complete;
1739 }
1740
1741 cb_task_data = &cb_data->ut.get_all;
1742 g_variant_builder_add(cb_task_data->vb, "{sv}",
1743 DLS_INTERFACE_PROP_SV_SERVICE_RESET_TOKEN,
1744 g_variant_new_string(token));
1745
1746 cb_data->task.result = g_variant_ref_sink(g_variant_builder_end(
1747 cb_task_data->vb));
1748
1749 DLEYNA_LOG_DEBUG("Service Reset %s", token);
1750
1751 g_free(token);
1752
1753 on_complete:
1754
1755 (void) g_idle_add(dls_async_task_complete, cb_data);
1756 g_cancellable_disconnect(cb_data->cancellable, cb_data->cancel_id);
1757
1758 if (upnp_error)
1759 g_error_free(upnp_error);
1760
1761 DLEYNA_LOG_DEBUG("Exit");
1762 }
1763
1764 static void prv_get_sr_token_for_props(GUPnPServiceProxy *proxy,
1765 const dls_device_t *device,
1766 dls_async_task_t *cb_data)
1767 {
1768 dls_async_get_all_t *cb_task_data;
1769
1770 DLEYNA_LOG_DEBUG("Enter");
1771
1772 if (prv_get_media_server_version(device) < 3) {
1773 cb_task_data = &cb_data->ut.get_all;
1774
1775 cb_data->task.result = g_variant_ref_sink(g_variant_builder_end(
1776 cb_task_data->vb));
1777
1778 goto on_complete; /* No error here, just skip the property */
1779 }
1780
1781 gupnp_service_proxy_begin_action(proxy, "GetServiceResetToken",
1782 prv_service_reset_for_props_cb,
1783 cb_data,
1784 NULL);
1785
1786 cb_data->proxy = proxy;
1787
1788 g_object_add_weak_pointer((G_OBJECT(proxy)),
1789 (gpointer *)&cb_data->proxy);
1790
1791 cb_data->cancel_id = g_cancellable_connect(
1792 cb_data->cancellable,
1793 G_CALLBACK(dls_async_task_cancelled_cb),
1794 cb_data, NULL);
1795
1796 DLEYNA_LOG_DEBUG("Exit");
1797
1798 return;
1799
1800 on_complete:
1801
1802 (void) g_idle_add(dls_async_task_complete, cb_data);
1803
1804 DLEYNA_LOG_DEBUG("Exit");
1805 }
1806
1807 static gboolean prv_get_all_child_count_cb(dls_async_task_t *cb_data,
1808 gint count)
1809 {
1810 dls_async_get_all_t *cb_task_data = &cb_data->ut.get_all;
1811
1812 dls_props_add_child_count(cb_task_data->vb, count);
1813 if (cb_task_data->device_object)
1814 prv_get_system_update_id_for_props(cb_data->proxy,
1815 cb_data->task.target.device,
1816 cb_data);
1817 else
1818 cb_data->task.result = g_variant_ref_sink(g_variant_builder_end(
1819 cb_task_data->vb));
1820
1821 return !cb_task_data->device_object;
1822 }
1823
1824 static void prv_get_all_ms2spec_props_cb(GUPnPServiceProxy *proxy,
1825 GUPnPServiceProxyAction *action,
1826 gpointer user_data)
1827 {
1828 GError *upnp_error = NULL;
1829 gchar *result = NULL;
1830 GUPnPDIDLLiteParser *parser = NULL;
1831 dls_async_task_t *cb_data = user_data;
1832 dls_async_get_all_t *cb_task_data = &cb_data->ut.get_all;
1833
1834 DLEYNA_LOG_DEBUG("Enter");
1835
1836 if (!gupnp_service_proxy_end_action(cb_data->proxy, cb_data->action,
1837 &upnp_error,
1838 "Result", G_TYPE_STRING,
1839 &result, NULL)) {
1840 DLEYNA_LOG_WARNING("Browse operation failed: %s",
1841 upnp_error->message);
1842
1843 cb_data->error = g_error_new(DLEYNA_SERVER_ERROR,
1844 DLEYNA_ERROR_OPERATION_FAILED,
1845 "Browse operation failed: %s",
1846 upnp_error->message);
1847 goto on_error;
1848 }
1849
1850 DLEYNA_LOG_DEBUG("GetMS2SpecProps result: %s", result);
1851
1852 parser = gupnp_didl_lite_parser_new();
1853
1854 g_signal_connect(parser, "object-available" , cb_task_data->prop_func,
1855 cb_data);
1856
1857 if (!gupnp_didl_lite_parser_parse_didl(parser, result, &upnp_error)) {
1858 if (upnp_error->code == GUPNP_XML_ERROR_EMPTY_NODE) {
1859 DLEYNA_LOG_WARNING("Property not defined for object");
1860
1861 cb_data->error =
1862 g_error_new(DLEYNA_SERVER_ERROR,
1863 DLEYNA_ERROR_UNKNOWN_PROPERTY,
1864 "Property not defined for object");
1865 } else {
1866 DLEYNA_LOG_WARNING(
1867 "Unable to parse results of browse: %s",
1868 upnp_error->message);
1869
1870 cb_data->error =
1871 g_error_new(DLEYNA_SERVER_ERROR,
1872 DLEYNA_ERROR_OPERATION_FAILED,
1873 "Unable to parse results of browse: %s",
1874 upnp_error->message);
1875 }
1876 goto on_error;
1877 }
1878
1879 if (cb_data->error)
1880 goto on_error;
1881
1882 if (cb_task_data->need_child_count) {
1883 DLEYNA_LOG_DEBUG("Need Child Count");
1884
1885 prv_get_child_count(cb_data, prv_get_all_child_count_cb,
1886 cb_data->task.target.id);
1887
1888 goto no_complete;
1889 } else if (cb_data->task.type == DLS_TASK_GET_ALL_PROPS &&
1890 cb_task_data->device_object) {
1891 prv_get_system_update_id_for_props(proxy,
1892 cb_data->task.target.device,
1893 cb_data);
1894
1895 goto no_complete;
1896 } else {
1897 cb_data->task.result = g_variant_ref_sink(g_variant_builder_end(
1898 cb_task_data->vb));
1899 }
1900
1901 on_error:
1902
1903 (void) g_idle_add(dls_async_task_complete, cb_data);
1904 g_cancellable_disconnect(cb_data->cancellable, cb_data->cancel_id);
1905
1906 no_complete:
1907
1908 if (upnp_error)
1909 g_error_free(upnp_error);
1910
1911 if (parser)
1912 g_object_unref(parser);
1913
1914 g_free(result);
1915
1916 DLEYNA_LOG_DEBUG("Exit");
1917 }
1918
1919 static void prv_get_all_ms2spec_props(dls_device_context_t *context,
1920 dls_async_task_t *cb_data)
1921 {
1922 dls_async_get_all_t *cb_task_data = &cb_data->ut.get_all;
1923 dls_task_t *task = &cb_data->task;
1924 dls_task_get_props_t *task_data = &task->ut.get_props;
1925
1926 DLEYNA_LOG_DEBUG("Enter called");
1927
1928 if (!strcmp(DLS_INTERFACE_MEDIA_CONTAINER, task_data->interface_name)) {
1929 cb_task_data->prop_func = G_CALLBACK(prv_get_container);
1930 } else if (!strcmp(DLS_INTERFACE_MEDIA_ITEM,
1931 task_data->interface_name)) {
1932 cb_task_data->prop_func = G_CALLBACK(prv_get_item);
1933 } else if (!strcmp(DLS_INTERFACE_MEDIA_OBJECT,
1934 task_data->interface_name)) {
1935 cb_task_data->prop_func = G_CALLBACK(prv_get_object);
1936 } else if (!strcmp("", task_data->interface_name)) {
1937 cb_task_data->prop_func = G_CALLBACK(prv_get_all);
1938 } else {
1939 DLEYNA_LOG_WARNING("Interface is unknown.");
1940
1941 cb_data->error =
1942 g_error_new(DLEYNA_SERVER_ERROR,
1943 DLEYNA_ERROR_UNKNOWN_INTERFACE,
1944 "Interface is unknown.");
1945 goto on_error;
1946 }
1947
1948 cb_data->action = gupnp_service_proxy_begin_action(
1949 context->service_proxy, "Browse",
1950 prv_get_all_ms2spec_props_cb, cb_data,
1951 "ObjectID", G_TYPE_STRING, task->target.id,
1952 "BrowseFlag", G_TYPE_STRING, "BrowseMetadata",
1953 "Filter", G_TYPE_STRING, "*",
1954 "StartingIndex", G_TYPE_INT, 0,
1955 "RequestedCount", G_TYPE_INT, 0,
1956 "SortCriteria", G_TYPE_STRING,
1957 "", NULL);
1958
1959 cb_data->proxy = context->service_proxy;
1960
1961 g_object_add_weak_pointer((G_OBJECT(context->service_proxy)),
1962 (gpointer *)&cb_data->proxy);
1963
1964 cb_data->cancel_id = g_cancellable_connect(
1965 cb_data->cancellable,
1966 G_CALLBACK(dls_async_task_cancelled_cb),
1967 cb_data, NULL);
1968
1969 DLEYNA_LOG_DEBUG("Exit with SUCCESS");
1970
1971 return;
1972
1973 on_error:
1974
1975 (void) g_idle_add(dls_async_task_complete, cb_data);
1976
1977 DLEYNA_LOG_DEBUG("Exit with FAIL");
1978
1979 return;
1980 }
1981
1982 void dls_device_get_all_props(dls_client_t *client,
1983 dls_task_t *task,
1984 gboolean root_object)
1985 {
1986 dls_async_task_t *cb_data = (dls_async_task_t *)task;
1987 dls_async_get_all_t *cb_task_data;
1988 dls_task_get_props_t *task_data = &task->ut.get_props;
1989 dls_device_context_t *context;
1990
1991 DLEYNA_LOG_DEBUG("Enter");
1992
1993 context = dls_device_get_context(task->target.device, client);
1994 cb_task_data = &cb_data->ut.get_all;
1995
1996 cb_task_data->vb = g_variant_builder_new(G_VARIANT_TYPE("a{sv}"));
1997 cb_task_data->device_object = root_object;
1998
1999 if (!strcmp(task_data->interface_name,
2000 DLEYNA_SERVER_INTERFACE_MEDIA_DEVICE)) {
2001 if (root_object) {
2002 dls_props_add_device(
2003 (GUPnPDeviceInfo *)context->device_proxy,
2004 task->target.device,
2005 cb_task_data->vb);
2006
2007 prv_get_system_update_id_for_props(
2008 context->service_proxy,
2009 task->target.device,
2010 cb_data);
2011 } else {
2012 cb_data->error =
2013 g_error_new(DLEYNA_SERVER_ERROR,
2014 DLEYNA_ERROR_UNKNOWN_INTERFACE,
2015 "Interface is only valid on root objects.");
2016
2017 (void) g_idle_add(dls_async_task_complete, cb_data);
2018 }
2019
2020 } else if (strcmp(task_data->interface_name, "")) {
2021 cb_task_data->device_object = FALSE;
2022 prv_get_all_ms2spec_props(context, cb_data);
2023 } else {
2024 if (root_object)
2025 dls_props_add_device(
2026 (GUPnPDeviceInfo *)context->device_proxy,
2027 task->target.device,
2028 cb_task_data->vb);
2029
2030 prv_get_all_ms2spec_props(context, cb_data);
2031 }
2032
2033 DLEYNA_LOG_DEBUG("Exit");
2034 }
2035
2036 static void prv_get_object_property(GUPnPDIDLLiteParser *parser,
2037 GUPnPDIDLLiteObject *object,
2038 gpointer user_data)
2039 {
2040 dls_async_task_t *cb_data = user_data;
2041 dls_task_t *task = &cb_data->task;
2042 dls_task_get_prop_t *task_data = &task->ut.get_prop;
2043
2044 if (cb_data->task.result)
2045 goto on_error;
2046
2047 cb_data->task.result = dls_props_get_object_prop(task_data->prop_name,
2048 task->target.root_path,
2049 object);
2050
2051 on_error:
2052
2053 return;
2054 }
2055
2056 static void prv_get_item_property(GUPnPDIDLLiteParser *parser,
2057 GUPnPDIDLLiteObject *object,
2058 gpointer user_data)
2059 {
2060 dls_async_task_t *cb_data = user_data;
2061 dls_task_t *task = &cb_data->task;
2062 dls_task_get_prop_t *task_data = &task->ut.get_prop;
2063 dls_async_get_prop_t *cb_task_data = &cb_data->ut.get_prop;
2064
2065 if (cb_data->task.result)
2066 goto on_error;
2067
2068 cb_data->task.result = dls_props_get_item_prop(
2069 task_data->prop_name,
2070 task->target.root_path,
2071 object,
2072 cb_task_data->protocol_info);
2073
2074 on_error:
2075
2076 return;
2077 }
2078
2079 static void prv_get_container_property(GUPnPDIDLLiteParser *parser,
2080 GUPnPDIDLLiteObject *object,
2081 gpointer user_data)
2082 {
2083 dls_async_task_t *cb_data = user_data;
2084 dls_task_t *task = &cb_data->task;
2085 dls_task_get_prop_t *task_data = &task->ut.get_prop;
2086
2087 if (cb_data->task.result)
2088 goto on_error;
2089
2090 cb_data->task.result = dls_props_get_container_prop(
2091 task_data->prop_name,
2092 object);
2093
2094 on_error:
2095
2096 return;
2097 }
2098
2099 static void prv_get_all_property(GUPnPDIDLLiteParser *parser,
2100 GUPnPDIDLLiteObject *object,
2101 gpointer user_data)
2102 {
2103 dls_async_task_t *cb_data = user_data;
2104
2105 prv_get_object_property(parser, object, user_data);
2106
2107 if (cb_data->task.result)
2108 goto on_error;
2109
2110 if (GUPNP_IS_DIDL_LITE_CONTAINER(object))
2111 prv_get_container_property(parser, object, user_data);
2112 else
2113 prv_get_item_property(parser, object, user_data);
2114
2115 on_error:
2116
2117 return;
2118 }
2119
2120 static gboolean prv_get_child_count_cb(dls_async_task_t *cb_data,
2121 gint count)
2122 {
2123 DLEYNA_LOG_DEBUG("Enter");
2124
2125 DLEYNA_LOG_DEBUG("Count %d", count);
2126
2127 cb_data->task.result = g_variant_ref_sink(
2128 g_variant_new_uint32((guint) count));
2129
2130 DLEYNA_LOG_DEBUG("Exit");
2131
2132 return TRUE;
2133 }
2134
2135 static void prv_count_children_cb(GUPnPServiceProxy *proxy,
2136 GUPnPServiceProxyAction *action,
2137 gpointer user_data)
2138 {
2139 dls_device_count_data_t *count_data = user_data;
2140 dls_async_task_t *cb_data = count_data->cb_data;
2141 GError *upnp_error = NULL;
2142 gint count;
2143 gboolean complete = FALSE;
2144
2145 DLEYNA_LOG_DEBUG("Enter");
2146
2147 if (!gupnp_service_proxy_end_action(cb_data->proxy, cb_data->action,
2148 &upnp_error,
2149 "TotalMatches", G_TYPE_INT,
2150 &count,
2151 NULL)) {
2152 DLEYNA_LOG_WARNING("Browse operation failed: %s",
2153 upnp_error->message);
2154
2155 cb_data->error = g_error_new(DLEYNA_SERVER_ERROR,
2156 DLEYNA_ERROR_OPERATION_FAILED,
2157 "Browse operation failed: %s",
2158 upnp_error->message);
2159 goto on_error;
2160 }
2161
2162 complete = count_data->cb(cb_data, count);
2163
2164 on_error:
2165
2166 g_free(user_data);
2167
2168 if (cb_data->error || complete) {
2169 (void) g_idle_add(dls_async_task_complete, cb_data);
2170 g_cancellable_disconnect(cb_data->cancellable,
2171 cb_data->cancel_id);
2172 }
2173
2174 if (upnp_error)
2175 g_error_free(upnp_error);
2176
2177 DLEYNA_LOG_DEBUG("Exit");
2178 }
2179
2180 static void prv_get_child_count(dls_async_task_t *cb_data,
2181 dls_device_count_cb_t cb, const gchar *id)
2182 {
2183 dls_device_count_data_t *count_data;
2184
2185 DLEYNA_LOG_DEBUG("Enter");
2186
2187 prv_count_data_new(cb_data, cb, &count_data);
2188 cb_data->action =
2189 gupnp_service_proxy_begin_action(cb_data->proxy,
2190 "Browse",
2191 prv_count_children_cb,
2192 count_data,
2193 "ObjectID", G_TYPE_STRING, id,
2194
2195 "BrowseFlag", G_TYPE_STRING,
2196 "BrowseDirectChildren",
2197
2198 "Filter", G_TYPE_STRING, "",
2199
2200 "StartingIndex", G_TYPE_INT,
2201 0,
2202
2203 "RequestedCount", G_TYPE_INT,
2204 1,
2205
2206 "SortCriteria", G_TYPE_STRING,
2207 "",
2208
2209 NULL);
2210
2211 DLEYNA_LOG_DEBUG("Exit with SUCCESS");
2212 }
2213
2214 static void prv_get_ms2spec_prop_cb(GUPnPServiceProxy *proxy,
2215 GUPnPServiceProxyAction *action,
2216 gpointer user_data)
2217 {
2218 GError *upnp_error = NULL;
2219 gchar *result = NULL;
2220 GUPnPDIDLLiteParser *parser = NULL;
2221 dls_async_task_t *cb_data = user_data;
2222 dls_async_get_prop_t *cb_task_data = &cb_data->ut.get_prop;
2223 dls_task_get_prop_t *task_data = &cb_data->task.ut.get_prop;
2224
2225 DLEYNA_LOG_DEBUG("Enter");
2226
2227 if (!gupnp_service_proxy_end_action(cb_data->proxy, cb_data->action,
2228 &upnp_error,
2229 "Result", G_TYPE_STRING,
2230 &result, NULL)) {
2231 DLEYNA_LOG_WARNING("Browse operation failed: %s",
2232 upnp_error->message);
2233
2234 cb_data->error = g_error_new(DLEYNA_SERVER_ERROR,
2235 DLEYNA_ERROR_OPERATION_FAILED,
2236 "Browse operation failed: %s",
2237 upnp_error->message);
2238 goto on_error;
2239 }
2240
2241 DLEYNA_LOG_DEBUG("GetMS2SpecProp result: %s", result);
2242
2243 parser = gupnp_didl_lite_parser_new();
2244
2245 g_signal_connect(parser, "object-available" , cb_task_data->prop_func,
2246 cb_data);
2247
2248 if (!gupnp_didl_lite_parser_parse_didl(parser, result, &upnp_error)) {
2249 if (upnp_error->code == GUPNP_XML_ERROR_EMPTY_NODE) {
2250 DLEYNA_LOG_WARNING("Property not defined for object");
2251
2252 cb_data->error =
2253 g_error_new(DLEYNA_SERVER_ERROR,
2254 DLEYNA_ERROR_UNKNOWN_PROPERTY,
2255 "Property not defined for object");
2256 } else {
2257 DLEYNA_LOG_WARNING(
2258 "Unable to parse results of browse: %s",
2259 upnp_error->message);
2260
2261 cb_data->error =
2262 g_error_new(DLEYNA_SERVER_ERROR,
2263 DLEYNA_ERROR_OPERATION_FAILED,
2264 "Unable to parse results of browse: %s",
2265 upnp_error->message);
2266 }
2267 goto on_error;
2268 }
2269
2270 if (!cb_data->task.result) {
2271 DLEYNA_LOG_WARNING("Property not defined for object");
2272
2273 cb_data->error = g_error_new(DLEYNA_SERVER_ERROR,
2274 DLEYNA_ERROR_UNKNOWN_PROPERTY,
2275 "Property not defined for object");
2276 }
2277
2278 on_error:
2279
2280 if (cb_data->error && !strcmp(task_data->prop_name,
2281 DLS_INTERFACE_PROP_CHILD_COUNT)) {
2282 DLEYNA_LOG_DEBUG("ChildCount not supported by server");
2283
2284 g_error_free(cb_data->error);
2285 cb_data->error = NULL;
2286 prv_get_child_count(cb_data, prv_get_child_count_cb,
2287 cb_data->task.target.id);
2288 } else {
2289 (void) g_idle_add(dls_async_task_complete, cb_data);
2290 g_cancellable_disconnect(cb_data->cancellable,
2291 cb_data->cancel_id);
2292 }
2293
2294 if (upnp_error)
2295 g_error_free(upnp_error);
2296
2297 if (parser)
2298 g_object_unref(parser);
2299
2300 g_free(result);
2301
2302 DLEYNA_LOG_DEBUG("Exit");
2303 }
2304
2305 static void prv_get_ms2spec_prop(dls_device_context_t *context,
2306 dls_prop_map_t *prop_map,
2307 dls_task_get_prop_t *task_data,
2308 dls_async_task_t *cb_data)
2309 {
2310 dls_async_get_prop_t *cb_task_data;
2311 const gchar *filter;
2312
2313 DLEYNA_LOG_DEBUG("Enter");
2314
2315 cb_task_data = &cb_data->ut.get_prop;
2316
2317 if (!prop_map) {
2318 cb_data->error = g_error_new(DLEYNA_SERVER_ERROR,
2319 DLEYNA_ERROR_UNKNOWN_PROPERTY,
2320 "Unknown property");
2321 goto on_error;
2322 }
2323
2324 filter = prop_map->filter ? prop_map->upnp_prop_name : "";
2325
2326 if (!strcmp(DLS_INTERFACE_MEDIA_CONTAINER, task_data->interface_name)) {
2327 cb_task_data->prop_func =
2328 G_CALLBACK(prv_get_container_property);
2329 } else if (!strcmp(DLS_INTERFACE_MEDIA_ITEM,
2330 task_data->interface_name)) {
2331 cb_task_data->prop_func = G_CALLBACK(prv_get_item_property);
2332 } else if (!strcmp(DLS_INTERFACE_MEDIA_OBJECT,
2333 task_data->interface_name)) {
2334 cb_task_data->prop_func = G_CALLBACK(prv_get_object_property);
2335 } else if (!strcmp("", task_data->interface_name)) {
2336 cb_task_data->prop_func = G_CALLBACK(prv_get_all_property);
2337 } else {
2338 DLEYNA_LOG_WARNING("Interface is unknown.%s",
2339 task_data->interface_name);
2340
2341 cb_data->error = g_error_new(DLEYNA_SERVER_ERROR,
2342 DLEYNA_ERROR_UNKNOWN_INTERFACE,
2343 "Interface is unknown.");
2344 goto on_error;
2345 }
2346
2347 cb_data->action = gupnp_service_proxy_begin_action(
2348 context->service_proxy, "Browse",
2349 prv_get_ms2spec_prop_cb,
2350 cb_data,
2351 "ObjectID", G_TYPE_STRING, cb_data->task.target.id,
2352 "BrowseFlag", G_TYPE_STRING,
2353 "BrowseMetadata",
2354 "Filter", G_TYPE_STRING, filter,
2355 "StartingIndex", G_TYPE_INT, 0,
2356 "RequestedCount", G_TYPE_INT, 0,
2357 "SortCriteria", G_TYPE_STRING,
2358 "",
2359 NULL);
2360
2361 cb_data->proxy = context->service_proxy;
2362
2363 g_object_add_weak_pointer((G_OBJECT(context->service_proxy)),
2364 (gpointer *)&cb_data->proxy);
2365
2366 cb_data->cancel_id = g_cancellable_connect(
2367 cb_data->cancellable,
2368 G_CALLBACK(dls_async_task_cancelled_cb),
2369 cb_data, NULL);
2370
2371 DLEYNA_LOG_DEBUG("Exit with SUCCESS");
2372
2373 return;
2374
2375 on_error:
2376
2377 (void) g_idle_add(dls_async_task_complete, cb_data);
2378
2379 DLEYNA_LOG_DEBUG("Exit with FAIL");
2380
2381 return;
2382 }
2383
2384 void dls_device_get_prop(dls_client_t *client,
2385 dls_task_t *task,
2386 dls_prop_map_t *prop_map, gboolean root_object)
2387 {
2388 dls_async_task_t *cb_data = (dls_async_task_t *)task;
2389 dls_task_get_prop_t *task_data = &task->ut.get_prop;
2390 dls_device_context_t *context;
2391 gboolean complete = FALSE;
2392
2393 DLEYNA_LOG_DEBUG("Enter");
2394
2395 context = dls_device_get_context(task->target.device, client);
2396
2397 if (!strcmp(task_data->interface_name,
2398 DLEYNA_SERVER_INTERFACE_MEDIA_DEVICE)) {
2399 if (root_object) {
2400 if (!strcmp(
2401 task_data->prop_name,
2402 DLS_INTERFACE_PROP_ESV_SYSTEM_UPDATE_ID)) {
2403 prv_get_system_update_id_for_prop(
2404 context->service_proxy,
2405 task->target.device,
2406 cb_data);
2407 } else if (!strcmp(
2408 task_data->prop_name,
2409 DLS_INTERFACE_PROP_SV_SERVICE_RESET_TOKEN)) {
2410 prv_get_sr_token_for_prop(
2411 context->service_proxy,
2412 task->target.device,
2413 cb_data);
2414 } else {
2415 cb_data->task.result =
2416 dls_props_get_device_prop(
2417 (GUPnPDeviceInfo *)
2418 context->device_proxy,
2419 task->target.device,
2420 task_data->prop_name);
2421
2422 if (!cb_data->task.result)
2423 cb_data->error = g_error_new(
2424 DLEYNA_SERVER_ERROR,
2425 DLEYNA_ERROR_UNKNOWN_PROPERTY,
2426 "Unknown property");
2427
2428 (void) g_idle_add(dls_async_task_complete,
2429 cb_data);
2430 }
2431
2432 } else {
2433 cb_data->error =
2434 g_error_new(DLEYNA_SERVER_ERROR,
2435 DLEYNA_ERROR_UNKNOWN_INTERFACE,
2436 "Interface is unknown.");
2437
2438 (void) g_idle_add(dls_async_task_complete, cb_data);
2439 }
2440
2441 } else if (strcmp(task_data->interface_name, "")) {
2442 prv_get_ms2spec_prop(context, prop_map, &task->ut.get_prop,
2443 cb_data);
2444 } else {
2445 if (root_object) {
2446 if (!strcmp(
2447 task_data->prop_name,
2448 DLS_INTERFACE_PROP_ESV_SYSTEM_UPDATE_ID)) {
2449 prv_get_system_update_id_for_prop(
2450 context->service_proxy,
2451 task->target.device,
2452 cb_data);
2453 complete = TRUE;
2454 } else if (!strcmp(
2455 task_data->prop_name,
2456 DLS_INTERFACE_PROP_SV_SERVICE_RESET_TOKEN)) {
2457 prv_get_sr_token_for_prop(
2458 context->service_proxy,
2459 task->target.device,
2460 cb_data);
2461 complete = TRUE;
2462 } else {
2463 cb_data->task.result =
2464 dls_props_get_device_prop(
2465 (GUPnPDeviceInfo *)
2466 context->device_proxy,
2467 task->target.device,
2468 task_data->prop_name);
2469 if (cb_data->task.result) {
2470 (void) g_idle_add(
2471 dls_async_task_complete,
2472 cb_data);
2473 complete = TRUE;
2474 }
2475 }
2476 }
2477
2478 if (!complete)
2479 prv_get_ms2spec_prop(context, prop_map,
2480 &task->ut.get_prop,
2481 cb_data);
2482 }
2483
2484 DLEYNA_LOG_DEBUG("Exit");
2485 }
2486
2487 static void prv_found_target(GUPnPDIDLLiteParser *parser,
2488 GUPnPDIDLLiteObject *object,
2489 gpointer user_data)
2490 {
2491 dls_async_task_t *cb_data = user_data;
2492 dls_async_bas_t *cb_task_data = &cb_data->ut.bas;
2493 const char *id;
2494 const char *parent_path;
2495 gchar *path = NULL;
2496 gboolean have_child_count;
2497 dls_device_object_builder_t *builder;
2498
2499 DLEYNA_LOG_DEBUG("Enter");
2500
2501 builder = g_new0(dls_device_object_builder_t, 1);
2502
2503 id = gupnp_didl_lite_object_get_parent_id(object);
2504
2505 if (!id || !strcmp(id, "-1") || !strcmp(id, "")) {
2506 parent_path = cb_data->task.target.root_path;
2507 } else {
2508 path = dls_path_from_id(cb_data->task.target.root_path, id);
2509 parent_path = path;
2510 }
2511
2512 builder->vb = g_variant_builder_new(G_VARIANT_TYPE("a{sv}"));
2513
2514 if (!dls_props_add_object(builder->vb, object,
2515 cb_data->task.target.root_path,
2516 parent_path, cb_task_data->filter_mask))
2517 goto on_error;
2518
2519 if (GUPNP_IS_DIDL_LITE_CONTAINER(object)) {
2520 dls_props_add_container(builder->vb,
2521 (GUPnPDIDLLiteContainer *)object,
2522 cb_task_data->filter_mask,
2523 &have_child_count);
2524
2525 if (!have_child_count && (cb_task_data->filter_mask &
2526 DLS_UPNP_MASK_PROP_CHILD_COUNT)) {
2527 builder->needs_child_count = TRUE;
2528 builder->id = g_strdup(
2529 gupnp_didl_lite_object_get_id(object));
2530 cb_task_data->need_child_count = TRUE;
2531 }
2532 } else {
2533 dls_props_add_item(builder->vb,
2534 object,
2535 cb_data->task.target.root_path,
2536 cb_task_data->filter_mask,
2537 cb_task_data->protocol_info);
2538 }
2539
2540 g_ptr_array_add(cb_task_data->vbs, builder);
2541 g_free(path);
2542
2543 DLEYNA_LOG_DEBUG("Exit with SUCCESS");
2544
2545 return;
2546
2547 on_error:
2548
2549 g_free(path);
2550 prv_object_builder_delete(builder);
2551
2552 DLEYNA_LOG_DEBUG("Exit with FAIL");
2553 }
2554
2555 static void prv_search_cb(GUPnPServiceProxy *proxy,
2556 GUPnPServiceProxyAction *action,
2557 gpointer user_data)
2558 {
2559 gchar *result = NULL;
2560 GUPnPDIDLLiteParser *parser = NULL;
2561 GError *upnp_error = NULL;
2562 dls_async_task_t *cb_data = user_data;
2563 dls_async_bas_t *cb_task_data = &cb_data->ut.bas;
2564
2565 DLEYNA_LOG_DEBUG("Enter");
2566
2567 if (!gupnp_service_proxy_end_action(cb_data->proxy, cb_data->action,
2568 &upnp_error,
2569 "Result", G_TYPE_STRING,
2570 &result,
2571 "TotalMatches", G_TYPE_INT,
2572 &cb_task_data->max_count,
2573 NULL)) {
2574
2575 DLEYNA_LOG_WARNING("Search operation failed %s",
2576 upnp_error->message);
2577
2578 cb_data->error = g_error_new(DLEYNA_SERVER_ERROR,
2579 DLEYNA_ERROR_OPERATION_FAILED,
2580 "Search operation failed: %s",
2581 upnp_error->message);
2582 goto on_error;
2583 }
2584
2585 parser = gupnp_didl_lite_parser_new();
2586
2587 cb_task_data->vbs = g_ptr_array_new_with_free_func(
2588 prv_object_builder_delete);
2589
2590 g_signal_connect(parser, "object-available" ,
2591 G_CALLBACK(prv_found_target), cb_data);
2592
2593 DLEYNA_LOG_DEBUG("Server Search result: %s", result);
2594
2595 if (!gupnp_didl_lite_parser_parse_didl(parser, result, &upnp_error) &&
2596 upnp_error->code != GUPNP_XML_ERROR_EMPTY_NODE) {
2597 DLEYNA_LOG_WARNING("Unable to parse results of search: %s",
2598 upnp_error->message);
2599
2600 cb_data->error = g_error_new(DLEYNA_SERVER_ERROR,
2601 DLEYNA_ERROR_OPERATION_FAILED,
2602 "Unable to parse results of search: %s",
2603 upnp_error->message);
2604 goto on_error;
2605 }
2606
2607 if (cb_task_data->need_child_count) {
2608 DLEYNA_LOG_DEBUG("Need to retrieve child count");
2609
2610 if (cb_data->task.multiple_retvals)
2611 cb_task_data->get_children_cb =
2612 prv_get_search_ex_result;
2613 else
2614 cb_task_data->get_children_cb = prv_get_children_result;
2615 prv_retrieve_child_count_for_list(cb_data);
2616 goto no_complete;
2617 } else {
2618 if (cb_data->task.multiple_retvals)
2619 prv_get_search_ex_result(cb_data);
2620 else
2621 prv_get_children_result(cb_data);
2622 }
2623
2624 on_error:
2625
2626 (void) g_idle_add(dls_async_task_complete, cb_data);
2627 g_cancellable_disconnect(cb_data->cancellable, cb_data->cancel_id);
2628
2629 no_complete:
2630
2631 if (parser)
2632 g_object_unref(parser);
2633
2634 g_free(result);
2635
2636 if (upnp_error)
2637 g_error_free(upnp_error);
2638
2639 DLEYNA_LOG_DEBUG("Exit");
2640 }
2641
2642 void dls_device_search(dls_client_t *client,
2643 dls_task_t *task,
2644 const gchar *upnp_filter, const gchar *upnp_query,
2645 const gchar *sort_by)
2646 {
2647 dls_async_task_t *cb_data = (dls_async_task_t *)task;
2648 dls_device_context_t *context;
2649
2650 DLEYNA_LOG_DEBUG("Enter");
2651
2652 context = dls_device_get_context(task->target.device, client);
2653
2654 cb_data->action = gupnp_service_proxy_begin_action(
2655 context->service_proxy, "Search",
2656 prv_search_cb,
2657 cb_data,
2658 "ContainerID", G_TYPE_STRING, task->target.id,
2659 "SearchCriteria", G_TYPE_STRING, upnp_query,
2660 "Filter", G_TYPE_STRING, upnp_filter,
2661 "StartingIndex", G_TYPE_INT, task->ut.search.start,
2662 "RequestedCount", G_TYPE_INT, task->ut.search.count,
2663 "SortCriteria", G_TYPE_STRING, sort_by,
2664 NULL);
2665
2666 cb_data->proxy = context->service_proxy;
2667
2668 g_object_add_weak_pointer((G_OBJECT(context->service_proxy)),
2669 (gpointer *)&cb_data->proxy);
2670
2671 cb_data->cancel_id = g_cancellable_connect(
2672 cb_data->cancellable,
2673 G_CALLBACK(dls_async_task_cancelled_cb),
2674 cb_data, NULL);
2675
2676 DLEYNA_LOG_DEBUG("Exit");
2677 }
2678
2679 static void prv_get_resource(GUPnPDIDLLiteParser *parser,
2680 GUPnPDIDLLiteObject *object,
2681 gpointer user_data)
2682 {
2683 dls_async_task_t *cb_data = user_data;
2684 dls_task_t *task = &cb_data->task;
2685 dls_task_get_resource_t *task_data = &task->ut.resource;
2686 dls_async_get_all_t *cb_task_data = &cb_data->ut.get_all;
2687
2688 DLEYNA_LOG_DEBUG("Enter");
2689
2690 dls_props_add_resource(cb_task_data->vb, object,
2691 cb_task_data->filter_mask,
2692 task_data->protocol_info);
2693 }
2694
2695 void dls_device_get_resource(dls_client_t *client,
2696 dls_task_t *task,
2697 const gchar *upnp_filter)
2698 {
2699 dls_async_task_t *cb_data = (dls_async_task_t *)task;
2700 dls_async_get_all_t *cb_task_data;
2701 dls_device_context_t *context;
2702
2703 context = dls_device_get_context(task->target.device, client);
2704 cb_task_data = &cb_data->ut.get_all;
2705
2706 cb_task_data->vb = g_variant_builder_new(G_VARIANT_TYPE("a{sv}"));
2707 cb_task_data->prop_func = G_CALLBACK(prv_get_resource);
2708 cb_task_data->device_object = FALSE;
2709
2710 cb_data->action = gupnp_service_proxy_begin_action(
2711 context->service_proxy, "Browse",
2712 prv_get_all_ms2spec_props_cb, cb_data,
2713 "ObjectID", G_TYPE_STRING, task->target.id,
2714 "BrowseFlag", G_TYPE_STRING, "BrowseMetadata",
2715 "Filter", G_TYPE_STRING, upnp_filter,
2716 "StartingIndex", G_TYPE_INT, 0,
2717 "RequestedCount", G_TYPE_INT, 0,
2718 "SortCriteria", G_TYPE_STRING,
2719 "", NULL);
2720
2721 cb_data->proxy = context->service_proxy;
2722
2723 g_object_add_weak_pointer((G_OBJECT(context->service_proxy)),
2724 (gpointer *)&cb_data->proxy);
2725
2726 cb_data->cancel_id = g_cancellable_connect(
2727 cb_data->cancellable,
2728 G_CALLBACK(dls_async_task_cancelled_cb),
2729 cb_data, NULL);
2730
2731 DLEYNA_LOG_DEBUG("Exit");
2732 }
2733
2734 static gchar *prv_create_new_container_didl(const gchar *parent_id,
2735 dls_task_t *task)
2736 {
2737 GUPnPDIDLLiteWriter *writer;
2738 GUPnPDIDLLiteObject *item;
2739 GUPnPDIDLLiteContainer *container;
2740 gchar *retval;
2741 GVariantIter iter;
2742 GVariant *child_type;
2743 const gchar *actual_type;
2744
2745 writer = gupnp_didl_lite_writer_new(NULL);
2746 item = GUPNP_DIDL_LITE_OBJECT(
2747 gupnp_didl_lite_writer_add_container(writer));
2748 container = GUPNP_DIDL_LITE_CONTAINER(item);
2749
2750 gupnp_didl_lite_object_set_id(item, "");
2751 gupnp_didl_lite_object_set_title(
2752 item,
2753 task->ut.create_container.display_name);
2754 gupnp_didl_lite_object_set_parent_id(item, parent_id);
2755 actual_type = dls_props_media_spec_to_upnp_class(
2756 task->ut.create_container.type);
2757 gupnp_didl_lite_object_set_upnp_class(item, actual_type);
2758 gupnp_didl_lite_object_set_restricted(item, FALSE);
2759 gupnp_didl_lite_object_set_dlna_managed(item, GUPNP_OCM_FLAGS_UPLOAD);
2760
2761 g_variant_iter_init(&iter, task->ut.create_container.child_types);
2762 while ((child_type = g_variant_iter_next_value(&iter))) {
2763 actual_type = dls_props_media_spec_to_upnp_class(
2764 g_variant_get_string(child_type, NULL));
2765 if (actual_type != NULL)
2766 gupnp_didl_lite_container_add_create_class(container,
2767 actual_type);
2768 g_variant_unref(child_type);
2769 }
2770
2771 retval = gupnp_didl_lite_writer_get_string(writer);
2772
2773 g_object_unref(item);
2774 g_object_unref(writer);
2775
2776 return retval;
2777 }
2778
2779 static const gchar *prv_get_dlna_profile_name(const gchar *filename)
2780 {
2781 gchar *uri;
2782 GError *error = NULL;
2783 const gchar *profile_name = NULL;
2784 GUPnPDLNAProfile *profile;
2785 GUPnPDLNAProfileGuesser *guesser;
2786 gboolean relaxed_mode = TRUE;
2787 gboolean extended_mode = TRUE;
2788
2789 guesser = gupnp_dlna_profile_guesser_new(relaxed_mode, extended_mode);
2790
2791 uri = g_filename_to_uri(filename, NULL, &error);
2792 if (uri == NULL) {
2793 DLEYNA_LOG_WARNING("Unable to convert filename: %s", filename);
2794
2795 if (error) {
2796 DLEYNA_LOG_WARNING("Error: %s", error->message);
2797
2798 g_error_free(error);
2799 }
2800
2801 goto on_error;
2802 }
2803
2804 profile = gupnp_dlna_profile_guesser_guess_profile_sync(guesser,
2805 uri,
2806 5000,
2807 NULL,
2808 &error);
2809 if (profile == NULL) {
2810 DLEYNA_LOG_WARNING("Unable to guess profile for URI: %s", uri);
2811
2812 if (error) {
2813 DLEYNA_LOG_WARNING("Error: %s", error->message);
2814
2815 g_error_free(error);
2816 }
2817
2818 goto on_error;
2819 }
2820
2821 profile_name = gupnp_dlna_profile_get_name(profile);
2822
2823 on_error:
2824 g_object_unref(guesser);
2825
2826 g_free(uri);
2827
2828 return profile_name;
2829 }
2830
2831 static gchar *prv_create_upload_didl(const gchar *parent_id, dls_task_t *task,
2832 const gchar *object_class,
2833 const gchar *mime_type)
2834 {
2835 GUPnPDIDLLiteWriter *writer;
2836 GUPnPDIDLLiteObject *item;
2837 gchar *retval;
2838 GUPnPProtocolInfo *protocol_info;
2839 GUPnPDIDLLiteResource *res;
2840 const gchar *profile;
2841
2842 writer = gupnp_didl_lite_writer_new(NULL);
2843 item = GUPNP_DIDL_LITE_OBJECT(gupnp_didl_lite_writer_add_item(writer));
2844
2845 gupnp_didl_lite_object_set_id(item, "");
2846 gupnp_didl_lite_object_set_title(item, task->ut.upload.display_name);
2847 gupnp_didl_lite_object_set_parent_id(item, parent_id);
2848 gupnp_didl_lite_object_set_upnp_class(item, object_class);
2849 gupnp_didl_lite_object_set_restricted(item, FALSE);
2850
2851 protocol_info = gupnp_protocol_info_new();
2852 gupnp_protocol_info_set_mime_type(protocol_info, mime_type);
2853 gupnp_protocol_info_set_protocol(protocol_info, "*");
2854 gupnp_protocol_info_set_network(protocol_info, "*");
2855
2856 profile = prv_get_dlna_profile_name(task->ut.upload.file_path);
2857 if (profile != NULL)
2858 gupnp_protocol_info_set_dlna_profile(protocol_info, profile);
2859
2860 res = gupnp_didl_lite_object_add_resource(item);
2861 gupnp_didl_lite_resource_set_protocol_info(res, protocol_info);
2862
2863 retval = gupnp_didl_lite_writer_get_string(writer);
2864
2865 g_object_unref(res);
2866 g_object_unref(protocol_info);
2867 g_object_unref(item);
2868 g_object_unref(writer);
2869
2870 return retval;
2871 }
2872
2873 static void prv_extract_import_uri(GUPnPDIDLLiteParser *parser,
2874 GUPnPDIDLLiteObject *object,
2875 gpointer user_data)
2876 {
2877 gchar **import_uri = user_data;
2878 GList *resources;
2879 GList *ptr;
2880 GUPnPDIDLLiteResource *res;
2881 const gchar *uri;
2882
2883 if (!*import_uri) {
2884 resources = gupnp_didl_lite_object_get_resources(object);
2885 ptr = resources;
2886 while (ptr) {
2887 res = ptr->data;
2888 if (!*import_uri) {
2889 uri = gupnp_didl_lite_resource_get_import_uri(
2890 res);
2891 if (uri)
2892 *import_uri = g_strdup(uri);
2893 }
2894 g_object_unref(res);
2895 ptr = ptr->next;
2896 }
2897
2898 g_list_free(resources);
2899 }
2900 }
2901
2902 static void prv_upload_delete_cb(GUPnPServiceProxy *proxy,
2903 GUPnPServiceProxyAction *action,
2904 gpointer user_data)
2905 {
2906 dls_async_task_t *cb_data = user_data;
2907
2908 DLEYNA_LOG_DEBUG("Enter");
2909
2910 (void) gupnp_service_proxy_end_action(cb_data->proxy, cb_data->action,
2911 NULL, NULL);
2912 (void) g_idle_add(dls_async_task_complete, cb_data);
2913 g_cancellable_disconnect(cb_data->cancellable, cb_data->cancel_id);
2914
2915 DLEYNA_LOG_DEBUG("Exit");
2916 }
2917
2918 static void prv_upload_job_delete(gpointer up_job)
2919 {
2920 dls_device_upload_job_t *upload_job = up_job;
2921
2922 if (up_job) {
2923 if (upload_job->remove_idle)
2924 (void) g_source_remove(upload_job->remove_idle);
2925
2926 g_free(upload_job);
2927 }
2928 }
2929
2930 static gboolean prv_remove_update_job(gpointer user_data)
2931 {
2932 dls_device_upload_job_t *upload_job = user_data;
2933 dls_device_upload_t *upload;
2934
2935 upload = g_hash_table_lookup(upload_job->device->uploads,
2936 &upload_job->upload_id);
2937 if (upload) {
2938 g_hash_table_remove(upload_job->device->uploads,
2939 &upload_job->upload_id);
2940
2941 DLEYNA_LOG_DEBUG("Removing Upload Object: %d",
2942 upload_job->upload_id);
2943 }
2944
2945 upload_job->remove_idle = 0;
2946 g_hash_table_remove(upload_job->device->upload_jobs,
2947 &upload_job->upload_id);
2948
2949 return FALSE;
2950 }
2951
2952 static void prv_generate_upload_update(dls_device_upload_job_t *upload_job,
2953 dls_device_upload_t *upload)
2954 {
2955 GVariant *args;
2956
2957 args = g_variant_new("(ustt)", upload_job->upload_id, upload->status,
2958 upload->bytes_uploaded, upload->bytes_to_upload);
2959
2960 DLEYNA_LOG_DEBUG(
2961 "Emitting: %s (%u %s %"G_GUINT64_FORMAT" %"G_GUINT64_FORMAT")"
2962 " on %s",
2963 DLS_INTERFACE_UPLOAD_UPDATE, upload_job->upload_id,
2964 upload->status, upload->bytes_uploaded,
2965 upload->bytes_to_upload, upload_job->device->path);
2966
2967 (void) dls_server_get_connector()->notify(
2968 upload_job->device->connection,
2969 upload_job->device->path,
2970 DLEYNA_SERVER_INTERFACE_MEDIA_DEVICE,
2971 DLS_INTERFACE_UPLOAD_UPDATE,
2972 args,
2973 NULL);
2974 }
2975
2976 static void prv_post_finished(SoupSession *session, SoupMessage *msg,
2977 gpointer user_data)
2978 {
2979 dls_device_upload_job_t *upload_job = user_data;
2980 dls_device_upload_t *upload;
2981 gint *upload_id;
2982
2983 DLEYNA_LOG_DEBUG("Enter");
2984
2985 DLEYNA_LOG_DEBUG("Upload %u finished. Code %u Message %s",
2986 upload_job->upload_id, msg->status_code,
2987 msg->reason_phrase);
2988
2989 /* This is clumsy but we need to distinguish between two cases:
2990 1. We cancel because the process is exitting.
2991 2. We cancel because a client has called CancelUpload.
2992
2993 We could use custom SOUP error messages to distinguish the cases
2994 but device->shutting_down seemed less hacky.
2995
2996 We need this check as if we are shutting down it is
2997 dangerous to manipulate uploads as we are being called from its
2998 destructor.
2999 */
3000
3001 if (upload_job->device->shutting_down) {
3002 DLEYNA_LOG_DEBUG("Device shutting down. Cancelling Upload");
3003 goto on_error;
3004 }
3005
3006 upload = g_hash_table_lookup(upload_job->device->uploads,
3007 &upload_job->upload_id);
3008 if (upload) {
3009 upload_job->remove_idle =
3010 g_timeout_add(30000, prv_remove_update_job, user_data);
3011
3012 if (SOUP_STATUS_IS_SUCCESSFUL(msg->status_code)) {
3013 upload->status = DLS_UPLOAD_STATUS_COMPLETED;
3014 upload->bytes_uploaded = upload->bytes_to_upload;
3015 } else if (msg->status_code == SOUP_STATUS_CANCELLED) {
3016 upload->status = DLS_UPLOAD_STATUS_CANCELLED;
3017 } else {
3018 upload->status = DLS_UPLOAD_STATUS_ERROR;
3019 }
3020
3021 DLEYNA_LOG_DEBUG("Upload Status: %s", upload->status);
3022
3023 prv_generate_upload_update(upload_job, upload);
3024
3025 g_object_unref(upload->msg);
3026 upload->msg = NULL;
3027
3028 g_object_unref(upload->soup_session);
3029 upload->soup_session = NULL;
3030
3031 g_mapped_file_unref(upload->mapped_file);
3032 upload->mapped_file = NULL;
3033
3034 g_free(upload->body);
3035 upload->body = NULL;
3036
3037 upload_id = g_new(gint, 1);
3038 *upload_id = upload_job->upload_id;
3039
3040 g_hash_table_insert(upload_job->device->upload_jobs, upload_id,
3041 upload_job);
3042
3043 upload_job = NULL;
3044 }
3045
3046 on_error:
3047
3048 prv_upload_job_delete(upload_job);
3049
3050 DLEYNA_LOG_DEBUG("Exit");
3051 }
3052
3053 static void prv_upload_delete(gpointer up)
3054 {
3055 dls_device_upload_t *upload = up;
3056
3057 DLEYNA_LOG_DEBUG("Enter");
3058
3059 if (upload) {
3060 if (upload->msg) {
3061 soup_session_cancel_message(upload->soup_session,
3062 upload->msg,
3063 SOUP_STATUS_CANCELLED);
3064 g_object_unref(upload->msg);
3065 }
3066
3067 if (upload->soup_session)
3068 g_object_unref(upload->soup_session);
3069
3070 if (upload->mapped_file)
3071 g_mapped_file_unref(upload->mapped_file);
3072 else if (upload->body)
3073 g_free(upload->body);
3074
3075 g_free(upload);
3076 }
3077
3078 DLEYNA_LOG_DEBUG("Exit");
3079 }
3080
3081 static void prv_post_bytes_written(SoupMessage *msg, SoupBuffer *chunk,
3082 gpointer user_data)
3083 {
3084 dls_device_upload_t *upload = user_data;
3085
3086 upload->bytes_uploaded += chunk->length;
3087 if (upload->bytes_uploaded > upload->bytes_to_upload)
3088 upload->bytes_uploaded = upload->bytes_to_upload;
3089 }
3090
3091 static dls_device_upload_t *prv_upload_data_new(const gchar *file_path,
3092 gchar *body,
3093 gsize body_length,
3094 const gchar *import_uri,
3095 const gchar *mime_type,
3096 GError **error)
3097 {
3098 dls_device_upload_t *upload = NULL;
3099 GMappedFile *mapped_file = NULL;
3100 gchar *up_body = body;
3101 gsize up_body_length = body_length;
3102
3103 DLEYNA_LOG_DEBUG("Enter");
3104
3105 if (file_path) {
3106 mapped_file = g_mapped_file_new(file_path, FALSE, NULL);
3107 if (!mapped_file) {
3108 DLEYNA_LOG_WARNING("Unable to map %s into memory",
3109 file_path);
3110
3111 *error = g_error_new(DLEYNA_SERVER_ERROR,
3112 DLEYNA_ERROR_IO,
3113 "Unable to map %s into memory",
3114 file_path);
3115 goto on_error;
3116 }
3117
3118 up_body = g_mapped_file_get_contents(mapped_file);
3119 up_body_length = g_mapped_file_get_length(mapped_file);
3120 }
3121
3122 upload = g_new0(dls_device_upload_t, 1);
3123
3124 upload->soup_session = soup_session_async_new();
3125 upload->msg = soup_message_new("POST", import_uri);
3126 upload->mapped_file = mapped_file;
3127 upload->body = body;
3128 upload->body_length = body_length;
3129
3130 if (!upload->msg) {
3131 DLEYNA_LOG_WARNING("Invalid URI %s", import_uri);
3132
3133 *error = g_error_new(DLEYNA_SERVER_ERROR,
3134 DLEYNA_ERROR_BAD_RESULT,
3135 "Invalid URI %s", import_uri);
3136 goto on_error;
3137 }
3138
3139 upload->status = DLS_UPLOAD_STATUS_IN_PROGRESS;
3140 upload->bytes_to_upload = up_body_length;
3141
3142 soup_message_headers_set_expectations(upload->msg->request_headers,
3143 SOUP_EXPECTATION_CONTINUE);
3144
3145 soup_message_set_request(upload->msg, mime_type, SOUP_MEMORY_STATIC,
3146 up_body, up_body_length);
3147 g_signal_connect(upload->msg, "wrote-body-data",
3148 G_CALLBACK(prv_post_bytes_written), upload);
3149
3150 DLEYNA_LOG_DEBUG("Exit with Success");
3151
3152 return upload;
3153
3154 on_error:
3155
3156 prv_upload_delete(upload);
3157
3158 DLEYNA_LOG_WARNING("Exit with Fail");
3159
3160 return NULL;
3161 }
3162
3163 static void prv_create_container_cb(GUPnPServiceProxy *proxy,
3164 GUPnPServiceProxyAction *action,
3165 gpointer user_data)
3166 {
3167 dls_async_task_t *cb_data = user_data;
3168 GError *upnp_error = NULL;
3169 gchar *result = NULL;
3170 gchar *object_id = NULL;
3171 gchar *object_path;
3172
3173 DLEYNA_LOG_DEBUG("Enter");
3174
3175 if (!gupnp_service_proxy_end_action(cb_data->proxy, cb_data->action,
3176 &upnp_error,
3177 "ObjectID", G_TYPE_STRING,
3178 &object_id,
3179 "Result", G_TYPE_STRING,
3180 &result,
3181 NULL)) {
3182 DLEYNA_LOG_WARNING("Create Object operation failed: %s",
3183 upnp_error->message);
3184
3185 cb_data->error = g_error_new(DLEYNA_SERVER_ERROR,
3186 DLEYNA_ERROR_OPERATION_FAILED,
3187 "Create Object operation failed: %s",
3188 upnp_error->message);
3189 goto on_error;
3190 }
3191
3192 object_path = dls_path_from_id(cb_data->task.target.root_path,
3193 object_id);
3194 cb_data->task.result = g_variant_ref_sink(g_variant_new_object_path(
3195 object_path));
3196 g_free(object_path);
3197
3198 on_error:
3199
3200 (void) g_idle_add(dls_async_task_complete, cb_data);
3201 g_cancellable_disconnect(cb_data->cancellable, cb_data->cancel_id);
3202
3203 if (object_id)
3204 g_free(object_id);
3205
3206 if (result)
3207 g_free(result);
3208
3209 if (upnp_error)
3210 g_error_free(upnp_error);
3211
3212 DLEYNA_LOG_DEBUG("Exit");
3213 }
3214
3215 static void prv_generic_upload_cb(dls_async_task_t *cb_data,
3216 char *file_path,
3217 gchar *body,
3218 gsize body_length,
3219 const gchar *mime_type)
3220 {
3221 gchar *object_id = NULL;
3222 gchar *result = NULL;
3223 gchar *import_uri = NULL;
3224 gchar *object_path;
3225 GError *error = NULL;
3226 gboolean delete_needed = FALSE;
3227 gint *upload_id;
3228 GUPnPDIDLLiteParser *parser = NULL;
3229 GVariant *out_p[2];
3230 dls_device_upload_t *upload;
3231 dls_device_upload_job_t *upload_job;
3232
3233 DLEYNA_LOG_DEBUG("Enter");
3234
3235 if (!gupnp_service_proxy_end_action(cb_data->proxy, cb_data->action,
3236 &error,
3237 "ObjectID", G_TYPE_STRING,
3238 &object_id,
3239 "Result", G_TYPE_STRING,
3240 &result,
3241 NULL)) {
3242 DLEYNA_LOG_WARNING("Create Object operation failed: %s",
3243 error->message);
3244
3245 cb_data->error = g_error_new(DLEYNA_SERVER_ERROR,
3246 DLEYNA_ERROR_OPERATION_FAILED,
3247 "Create Object operation "
3248 " failed: %s",
3249 error->message);
3250 goto on_error;
3251 }
3252
3253 DLEYNA_LOG_DEBUG_NL();
3254 DLEYNA_LOG_DEBUG("Create Object Result: %s", result);
3255 DLEYNA_LOG_DEBUG_NL();
3256
3257 delete_needed = TRUE;
3258
3259 parser = gupnp_didl_lite_parser_new();
3260
3261 g_signal_connect(parser, "object-available" ,
3262 G_CALLBACK(prv_extract_import_uri), &import_uri);
3263
3264 if (!gupnp_didl_lite_parser_parse_didl(parser, result, &error) &&
3265 error->code != GUPNP_XML_ERROR_EMPTY_NODE) {
3266
3267 DLEYNA_LOG_WARNING(
3268 "Unable to parse results of CreateObject: %s",
3269 error->message);
3270
3271 cb_data->error = g_error_new(DLEYNA_SERVER_ERROR,
3272 DLEYNA_ERROR_OPERATION_FAILED,
3273 "Unable to parse results of CreateObject: %s",
3274 error->message);
3275 goto on_error;
3276 }
3277
3278 if (!import_uri) {
3279 DLEYNA_LOG_WARNING("Missing Import URI");
3280
3281 cb_data->error = g_error_new(DLEYNA_SERVER_ERROR,
3282 DLEYNA_ERROR_OPERATION_FAILED,
3283 "Missing Import URI");
3284 goto on_error;
3285 }
3286
3287 DLEYNA_LOG_DEBUG("Import URI %s", import_uri);
3288
3289 upload = prv_upload_data_new(file_path, body, body_length,
3290 import_uri, mime_type, &cb_data->error);
3291
3292 if (!upload)
3293 goto on_error;
3294
3295 upload_job = g_new0(dls_device_upload_job_t, 1);
3296 upload_job->device = cb_data->task.target.device;
3297 upload_job->upload_id = (gint) cb_data->task.target.device->upload_id;
3298
3299 soup_session_queue_message(upload->soup_session, upload->msg,
3300 prv_post_finished, upload_job);
3301 g_object_ref(upload->msg);
3302
3303 upload_id = g_new(gint, 1);
3304 *upload_id = upload_job->upload_id;
3305 g_hash_table_insert(cb_data->task.target.device->uploads, upload_id,
3306 upload);
3307
3308 object_path = dls_path_from_id(cb_data->task.target.root_path,
3309 object_id);
3310
3311 DLEYNA_LOG_DEBUG("Upload ID %u", *upload_id);
3312 DLEYNA_LOG_DEBUG("Object ID %s", object_id);
3313 DLEYNA_LOG_DEBUG("Object Path %s", object_path);
3314
3315 out_p[0] = g_variant_new_uint32(*upload_id);
3316 out_p[1] = g_variant_new_object_path(object_path);
3317 cb_data->task.result = g_variant_ref_sink(g_variant_new_tuple(out_p,
3318 2));
3319
3320 ++cb_data->task.target.device->upload_id;
3321 if (cb_data->task.target.device->upload_id > G_MAXINT)
3322 cb_data->task.target.device->upload_id = 0;
3323
3324 g_free(object_path);
3325
3326 on_error:
3327
3328 if (cb_data->error && delete_needed) {
3329 DLEYNA_LOG_WARNING(
3330 "Upload failed deleting created object with id %s",
3331 object_id);
3332
3333 cb_data->action = gupnp_service_proxy_begin_action(
3334 cb_data->proxy, "DestroyObject",
3335 prv_upload_delete_cb, cb_data,
3336 "ObjectID", G_TYPE_STRING, object_id,
3337 NULL);
3338 } else {
3339 (void) g_idle_add(dls_async_task_complete, cb_data);
3340 g_cancellable_disconnect(cb_data->cancellable,
3341 cb_data->cancel_id);
3342 }
3343
3344 g_free(object_id);
3345 g_free(import_uri);
3346
3347 if (parser)
3348 g_object_unref(parser);
3349
3350 g_free(result);
3351
3352 if (error)
3353 g_error_free(error);
3354
3355 DLEYNA_LOG_DEBUG("Exit");
3356 }
3357
3358 static void prv_create_object_upload_cb(GUPnPServiceProxy *proxy,
3359 GUPnPServiceProxyAction *action,
3360 gpointer user_data)
3361 {
3362 dls_async_task_t *cb_data = user_data;
3363
3364 prv_generic_upload_cb(cb_data,
3365 cb_data->task.ut.upload.file_path,
3366 NULL, 0,
3367 cb_data->ut.upload.mime_type);
3368 }
3369
3370 void dls_device_upload(dls_client_t *client,
3371 dls_task_t *task, const gchar *parent_id)
3372 {
3373 dls_async_task_t *cb_data = (dls_async_task_t *)task;
3374 dls_device_context_t *context;
3375 gchar *didl;
3376 dls_async_upload_t *cb_task_data;
3377
3378 DLEYNA_LOG_DEBUG("Enter");
3379 DLEYNA_LOG_DEBUG("Uploading file to %s", parent_id);
3380
3381 context = dls_device_get_context(task->target.device, client);
3382 cb_task_data = &cb_data->ut.upload;
3383
3384 didl = prv_create_upload_didl(parent_id, task,
3385 cb_task_data->object_class,
3386 cb_task_data->mime_type);
3387
3388 DLEYNA_LOG_DEBUG_NL();
3389 DLEYNA_LOG_DEBUG("DIDL: %s", didl);
3390 DLEYNA_LOG_DEBUG_NL();
3391
3392 cb_data->action = gupnp_service_proxy_begin_action(
3393 context->service_proxy, "CreateObject",
3394 prv_create_object_upload_cb, cb_data,
3395 "ContainerID", G_TYPE_STRING, parent_id,
3396 "Elements", G_TYPE_STRING, didl,
3397 NULL);
3398
3399 cb_data->proxy = context->service_proxy;
3400
3401 g_object_add_weak_pointer((G_OBJECT(context->service_proxy)),
3402 (gpointer *)&cb_data->proxy);
3403
3404 cb_data->cancel_id = g_cancellable_connect(
3405 cb_data->cancellable,
3406 G_CALLBACK(dls_async_task_cancelled_cb),
3407 cb_data, NULL);
3408
3409 g_free(didl);
3410
3411 DLEYNA_LOG_DEBUG("Exit");
3412 }
3413
3414 gboolean dls_device_get_upload_status(dls_task_t *task, GError **error)
3415 {
3416 dls_device_upload_t *upload;
3417 gboolean retval = FALSE;
3418 GVariant *out_params[3];
3419 guint upload_id;
3420
3421 DLEYNA_LOG_DEBUG("Enter");
3422
3423 upload_id = task->ut.upload_action.upload_id;
3424
3425 upload = g_hash_table_lookup(task->target.device->uploads, &upload_id);
3426 if (!upload) {
3427 *error = g_error_new(DLEYNA_SERVER_ERROR,
3428 DLEYNA_ERROR_OBJECT_NOT_FOUND,
3429 "Unknown Upload ID %u ", upload_id);
3430 goto on_error;
3431 }
3432
3433 out_params[0] = g_variant_new_string(upload->status);
3434 out_params[1] = g_variant_new_uint64(upload->bytes_uploaded);
3435 out_params[2] = g_variant_new_uint64(upload->bytes_to_upload);
3436
3437 DLEYNA_LOG_DEBUG(
3438 "Upload ( %s %"G_GUINT64_FORMAT" %"G_GUINT64_FORMAT" )",
3439 upload->status, upload->bytes_uploaded,
3440 upload->bytes_to_upload);
3441
3442 task->result = g_variant_ref_sink(g_variant_new_tuple(out_params, 3));
3443
3444 retval = TRUE;
3445
3446 on_error:
3447
3448 DLEYNA_LOG_DEBUG("Exit");
3449
3450 return retval;
3451 }
3452
3453 void dls_device_get_upload_ids(dls_task_t *task)
3454 {
3455 GVariantBuilder vb;
3456 GHashTableIter iter;
3457 gpointer key;
3458
3459 DLEYNA_LOG_DEBUG("Enter");
3460
3461 g_variant_builder_init(&vb, G_VARIANT_TYPE("au"));
3462
3463 g_hash_table_iter_init(&iter, task->target.device->uploads);
3464 while (g_hash_table_iter_next(&iter, &key, NULL))
3465 g_variant_builder_add(&vb, "u", (guint32) (*((gint *)key)));
3466
3467 task->result = g_variant_ref_sink(g_variant_builder_end(&vb));
3468
3469 DLEYNA_LOG_DEBUG("Exit");
3470 }
3471
3472 gboolean dls_device_cancel_upload(dls_task_t *task, GError **error)
3473 {
3474 dls_device_upload_t *upload;
3475 gboolean retval = FALSE;
3476 guint upload_id;
3477
3478 DLEYNA_LOG_DEBUG("Enter");
3479
3480 upload_id = task->ut.upload_action.upload_id;
3481
3482 upload = g_hash_table_lookup(task->target.device->uploads, &upload_id);
3483 if (!upload) {
3484 *error = g_error_new(DLEYNA_SERVER_ERROR,
3485 DLEYNA_ERROR_OBJECT_NOT_FOUND,
3486 "Unknown Upload ID %u ", upload_id);
3487 goto on_error;
3488 }
3489
3490 if (upload->msg) {
3491 soup_session_cancel_message(upload->soup_session, upload->msg,
3492 SOUP_STATUS_CANCELLED);
3493 DLEYNA_LOG_DEBUG("Cancelling Upload %u ", upload_id);
3494 }
3495
3496 retval = TRUE;
3497
3498 on_error:
3499
3500 DLEYNA_LOG_DEBUG("Exit");
3501
3502 return retval;
3503 }
3504
3505 static void prv_destroy_object_cb(GUPnPServiceProxy *proxy,
3506 GUPnPServiceProxyAction *action,
3507 gpointer user_data)
3508 {
3509 GError *upnp_error = NULL;
3510 dls_async_task_t *cb_data = user_data;
3511
3512 DLEYNA_LOG_DEBUG("Enter");
3513
3514 if (!gupnp_service_proxy_end_action(cb_data->proxy, cb_data->action,
3515 &upnp_error,
3516 NULL)) {
3517 DLEYNA_LOG_WARNING("Destroy Object operation failed: %s",
3518 upnp_error->message);
3519
3520 cb_data->error = g_error_new(DLEYNA_SERVER_ERROR,
3521 DLEYNA_ERROR_OPERATION_FAILED,
3522 "Destroy Object operation failed: %s",
3523 upnp_error->message);
3524 }
3525
3526 (void) g_idle_add(dls_async_task_complete, cb_data);
3527 g_cancellable_disconnect(cb_data->cancellable, cb_data->cancel_id);
3528
3529 if (upnp_error)
3530 g_error_free(upnp_error);
3531
3532 DLEYNA_LOG_DEBUG("Exit");
3533 }
3534
3535 void dls_device_delete_object(dls_client_t *client,
3536 dls_task_t *task)
3537 {
3538 dls_async_task_t *cb_data = (dls_async_task_t *)task;
3539 dls_device_context_t *context;
3540
3541 DLEYNA_LOG_DEBUG("Enter");
3542
3543 context = dls_device_get_context(task->target.device, client);
3544
3545 cb_data->action = gupnp_service_proxy_begin_action(
3546 context->service_proxy, "DestroyObject",
3547 prv_destroy_object_cb, cb_data,
3548 "ObjectID", G_TYPE_STRING, task->target.id,
3549 NULL);
3550
3551 cb_data->proxy = context->service_proxy;
3552
3553 g_object_add_weak_pointer((G_OBJECT(context->service_proxy)),
3554 (gpointer *)&cb_data->proxy);
3555
3556 cb_data->cancel_id = g_cancellable_connect(cb_data->cancellable,
3557 G_CALLBACK(dls_async_task_cancelled_cb),
3558 cb_data, NULL);
3559
3560 DLEYNA_LOG_DEBUG("Exit");
3561 }
3562
3563 void dls_device_create_container(dls_client_t *client,
3564 dls_task_t *task,
3565 const gchar *parent_id)
3566 {
3567 dls_async_task_t *cb_data = (dls_async_task_t *)task;
3568 dls_device_context_t *context;
3569 gchar *didl;
3570
3571 DLEYNA_LOG_DEBUG("Enter");
3572
3573 context = dls_device_get_context(task->target.device, client);
3574
3575 didl = prv_create_new_container_didl(parent_id, task);
3576
3577 DLEYNA_LOG_DEBUG("DIDL: %s", didl);
3578
3579 cb_data->action = gupnp_service_proxy_begin_action(
3580 context->service_proxy, "CreateObject",
3581 prv_create_container_cb, cb_data,
3582 "ContainerID", G_TYPE_STRING, parent_id,
3583 "Elements", G_TYPE_STRING, didl,
3584 NULL);
3585
3586 cb_data->proxy = context->service_proxy;
3587
3588 g_object_add_weak_pointer((G_OBJECT(context->service_proxy)),
3589 (gpointer *)&cb_data->proxy);
3590
3591 cb_data->cancel_id = g_cancellable_connect(
3592 cb_data->cancellable,
3593 G_CALLBACK(dls_async_task_cancelled_cb),
3594 cb_data, NULL);
3595
3596 g_free(didl);
3597
3598 DLEYNA_LOG_DEBUG("Exit");
3599 }
3600
3601 static void prv_update_object_update_cb(GUPnPServiceProxy *proxy,
3602 GUPnPServiceProxyAction *action,
3603 gpointer user_data)
3604 {
3605 GError *upnp_error = NULL;
3606 dls_async_task_t *cb_data = user_data;
3607
3608 DLEYNA_LOG_DEBUG("Enter");
3609
3610 if (!gupnp_service_proxy_end_action(cb_data->proxy, cb_data->action,
3611 &upnp_error,
3612 NULL)) {
3613 DLEYNA_LOG_WARNING("Update Object operation failed: %s",
3614 upnp_error->message);
3615
3616 cb_data->error = g_error_new(DLEYNA_SERVER_ERROR,
3617 DLEYNA_ERROR_OPERATION_FAILED,
3618 "Update Object operation "
3619 " failed: %s",
3620 upnp_error->message);
3621 }
3622
3623 (void) g_idle_add(dls_async_task_complete, cb_data);
3624 g_cancellable_disconnect(cb_data->cancellable, cb_data->cancel_id);
3625
3626 if (upnp_error)
3627 g_error_free(upnp_error);
3628
3629 DLEYNA_LOG_DEBUG("Exit");
3630 }
3631
3632 static gchar *prv_get_current_xml_fragment(GUPnPDIDLLiteObject *object,
3633 dls_upnp_prop_mask mask)
3634 {
3635 gchar *retval = NULL;
3636
3637 if (mask & DLS_UPNP_MASK_PROP_DISPLAY_NAME)
3638 retval = gupnp_didl_lite_object_get_title_xml_string(object);
3639 else if (mask & DLS_UPNP_MASK_PROP_ALBUM)
3640 retval = gupnp_didl_lite_object_get_album_xml_string(object);
3641 else if (mask & DLS_UPNP_MASK_PROP_DATE)
3642 retval = gupnp_didl_lite_object_get_date_xml_string(object);
3643 else if (mask & DLS_UPNP_MASK_PROP_TYPE)
3644 retval = gupnp_didl_lite_object_get_upnp_class_xml_string(
3645 object);
3646 else if (mask & DLS_UPNP_MASK_PROP_TRACK_NUMBER)
3647 retval = gupnp_didl_lite_object_get_track_number_xml_string(
3648 object);
3649 else if (mask & DLS_UPNP_MASK_PROP_ARTISTS)
3650 retval = gupnp_didl_lite_object_get_artists_xml_string(object);
3651
3652 return retval;
3653 }
3654
3655 static gchar *prv_get_new_xml_fragment(GUPnPDIDLLiteObject *object,
3656 dls_upnp_prop_mask mask,
3657 GVariant *value)
3658 {
3659 GUPnPDIDLLiteContributor *artist;
3660 const gchar *artist_name;
3661 const gchar *upnp_class;
3662 GVariantIter viter;
3663 gchar *retval = NULL;
3664
3665 if (mask & DLS_UPNP_MASK_PROP_DISPLAY_NAME) {
3666 gupnp_didl_lite_object_set_title(
3667 object,
3668 g_variant_get_string(value, NULL));
3669
3670 retval = gupnp_didl_lite_object_get_title_xml_string(object);
3671 } else if (mask & DLS_UPNP_MASK_PROP_ALBUM) {
3672 gupnp_didl_lite_object_set_album(
3673 object,
3674 g_variant_get_string(value, NULL));
3675
3676 retval = gupnp_didl_lite_object_get_album_xml_string(object);
3677 } else if (mask & DLS_UPNP_MASK_PROP_DATE) {
3678 gupnp_didl_lite_object_set_date(
3679 object,
3680 g_variant_get_string(value, NULL));
3681
3682 retval = gupnp_didl_lite_object_get_date_xml_string(object);
3683 } else if (mask & DLS_UPNP_MASK_PROP_TYPE) {
3684 upnp_class = dls_props_media_spec_to_upnp_class(
3685 g_variant_get_string(value, NULL));
3686
3687 gupnp_didl_lite_object_set_upnp_class(object, upnp_class);
3688
3689 retval = gupnp_didl_lite_object_get_upnp_class_xml_string(
3690 object);
3691 } else if (mask & DLS_UPNP_MASK_PROP_TRACK_NUMBER) {
3692 gupnp_didl_lite_object_set_track_number(
3693 object,
3694 g_variant_get_int32(value));
3695
3696 retval = gupnp_didl_lite_object_get_track_number_xml_string(
3697 object);
3698 } else if (mask & DLS_UPNP_MASK_PROP_ARTISTS) {
3699 gupnp_didl_lite_object_unset_artists(object);
3700
3701 (void) g_variant_iter_init(&viter, value);
3702
3703 while (g_variant_iter_next(&viter, "&s", &artist_name)) {
3704 artist = gupnp_didl_lite_object_add_artist(object);
3705
3706 gupnp_didl_lite_contributor_set_name(artist,
3707 artist_name);
3708 }
3709
3710 retval = gupnp_didl_lite_object_get_artists_xml_string(object);
3711 }
3712
3713 return retval;
3714 }
3715
3716 static void prv_get_xml_fragments(GUPnPDIDLLiteParser *parser,
3717 GUPnPDIDLLiteObject *object,
3718 gpointer user_data)
3719 {
3720 GString *current_str;
3721 GString *new_str;
3722 gchar *frag1;
3723 gchar *frag2;
3724 GVariantIter viter;
3725 const gchar *prop;
3726 GVariant *value;
3727 dls_prop_map_t *prop_map;
3728 GUPnPDIDLLiteWriter *writer;
3729 GUPnPDIDLLiteObject *scratch_object;
3730 gboolean first = TRUE;
3731 dls_async_task_t *cb_data = user_data;
3732 dls_async_update_t *cb_task_data = &cb_data->ut.update;
3733 dls_task_t *task = &cb_data->task;
3734 dls_task_update_t *task_data = &task->ut.update;
3735
3736 DLEYNA_LOG_DEBUG("Enter");
3737
3738 current_str = g_string_new("");
3739 new_str = g_string_new("");
3740
3741 writer = gupnp_didl_lite_writer_new(NULL);
3742 if (GUPNP_IS_DIDL_LITE_CONTAINER(object))
3743 scratch_object = GUPNP_DIDL_LITE_OBJECT(
3744 gupnp_didl_lite_writer_add_container(writer));
3745 else
3746 scratch_object = GUPNP_DIDL_LITE_OBJECT(
3747 gupnp_didl_lite_writer_add_item(writer));
3748
3749 (void) g_variant_iter_init(&viter, task_data->to_add_update);
3750
3751 while (g_variant_iter_next(&viter, "{&sv}", &prop, &value)) {
3752
3753 DLEYNA_LOG_DEBUG("to_add_update = %s", prop);
3754
3755 prop_map = g_hash_table_lookup(cb_task_data->map, prop);
3756
3757 frag1 = prv_get_current_xml_fragment(object, prop_map->type);
3758 frag2 = prv_get_new_xml_fragment(scratch_object, prop_map->type,
3759 value);
3760
3761 if (!frag2) {
3762 DLEYNA_LOG_DEBUG("Unable to set %s. Skipping", prop);
3763
3764 g_free(frag1);
3765 continue;
3766 }
3767
3768 if (!first) {
3769 g_string_append(current_str, ",");
3770 g_string_append(new_str, ",");
3771 } else {
3772 first = FALSE;
3773 }
3774
3775 if (frag1) {
3776 g_string_append(current_str, (const gchar *)frag1);
3777 g_free(frag1);
3778 }
3779
3780 g_string_append(new_str, (const gchar *)frag2);
3781 g_free(frag2);
3782 }
3783
3784 (void) g_variant_iter_init(&viter, task_data->to_delete);
3785
3786 while (g_variant_iter_next(&viter, "&s", &prop)) {
3787 DLEYNA_LOG_DEBUG("to_delete = %s", prop);
3788
3789 prop_map = g_hash_table_lookup(cb_task_data->map, prop);
3790
3791 frag1 = prv_get_current_xml_fragment(object, prop_map->type);
3792 if (!frag1)
3793 continue;
3794
3795 if (!first)
3796 g_string_append(current_str, ",");
3797 else
3798 first = FALSE;
3799
3800 g_string_append(current_str, (const gchar *)frag1);
3801 g_free(frag1);
3802 }
3803
3804 cb_task_data->current_tag_value = g_string_free(current_str, FALSE);
3805 DLEYNA_LOG_DEBUG("current_tag_value = %s",
3806 cb_task_data->current_tag_value);
3807
3808 cb_task_data->new_tag_value = g_string_free(new_str, FALSE);
3809 DLEYNA_LOG_DEBUG("new_tag_value = %s", cb_task_data->new_tag_value);
3810
3811 g_object_unref(scratch_object);
3812 g_object_unref(writer);
3813
3814 DLEYNA_LOG_DEBUG("Exit");
3815 }
3816
3817 static void prv_update_object_browse_cb(GUPnPServiceProxy *proxy,
3818 GUPnPServiceProxyAction *action,
3819 gpointer user_data)
3820 {
3821 GError *upnp_error = NULL;
3822 dls_async_task_t *cb_data = user_data;
3823 dls_async_update_t *cb_task_data = &cb_data->ut.update;
3824 GUPnPDIDLLiteParser *parser = NULL;
3825 gchar *result = NULL;
3826
3827 DLEYNA_LOG_DEBUG("Enter");
3828
3829 if (!gupnp_service_proxy_end_action(cb_data->proxy, cb_data->action,
3830 &upnp_error,
3831 "Result", G_TYPE_STRING,
3832 &result, NULL)) {
3833 DLEYNA_LOG_WARNING("Browse Object operation failed: %s",
3834 upnp_error->message);
3835
3836 cb_data->error = g_error_new(DLEYNA_SERVER_ERROR,
3837 DLEYNA_ERROR_OPERATION_FAILED,
3838 "Browse operation failed: %s",
3839 upnp_error->message);
3840 goto on_error;
3841 }
3842
3843 DLEYNA_LOG_DEBUG("dls_device_update_ex_object result: %s", result);
3844
3845 parser = gupnp_didl_lite_parser_new();
3846
3847 g_signal_connect(parser, "object-available",
3848 G_CALLBACK(prv_get_xml_fragments),
3849 cb_data);
3850
3851 if (!gupnp_didl_lite_parser_parse_didl(parser, result, &upnp_error)) {
3852 if (upnp_error->code == GUPNP_XML_ERROR_EMPTY_NODE) {
3853 DLEYNA_LOG_WARNING("Property not defined for object");
3854
3855 cb_data->error =
3856 g_error_new(DLEYNA_SERVER_ERROR,
3857 DLEYNA_ERROR_UNKNOWN_PROPERTY,
3858 "Property not defined for object");
3859 } else {
3860 DLEYNA_LOG_WARNING(
3861 "Unable to parse results of browse: %s",
3862 upnp_error->message);
3863
3864 cb_data->error =
3865 g_error_new(DLEYNA_SERVER_ERROR,
3866 DLEYNA_ERROR_OPERATION_FAILED,
3867 "Unable to parse results of browse: %s",
3868 upnp_error->message);
3869 }
3870
3871 goto on_error;
3872 }
3873
3874 cb_data->action = gupnp_service_proxy_begin_action(
3875 cb_data->proxy, "UpdateObject",
3876 prv_update_object_update_cb, cb_data,
3877 "ObjectID", G_TYPE_STRING, cb_data->task.target.id,
3878 "CurrentTagValue", G_TYPE_STRING,
3879 cb_task_data->current_tag_value,
3880 "NewTagValue", G_TYPE_STRING, cb_task_data->new_tag_value,
3881 NULL);
3882
3883 goto no_complete;
3884
3885 on_error:
3886
3887 (void) g_idle_add(dls_async_task_complete, cb_data);
3888 g_cancellable_disconnect(cb_data->cancellable, cb_data->cancel_id);
3889
3890 no_complete:
3891
3892 if (parser)
3893 g_object_unref(parser);
3894
3895 g_free(result);
3896
3897 if (upnp_error)
3898 g_error_free(upnp_error);
3899
3900 DLEYNA_LOG_DEBUG("Exit");
3901 }
3902
3903 void dls_device_update_object(dls_client_t *client,
3904 dls_task_t *task,
3905 const gchar *upnp_filter)
3906 {
3907 dls_async_task_t *cb_data = (dls_async_task_t *)task;
3908 dls_device_context_t *context;
3909
3910 DLEYNA_LOG_DEBUG("Enter");
3911
3912 context = dls_device_get_context(task->target.device, client);
3913
3914 cb_data->action = gupnp_service_proxy_begin_action(
3915 context->service_proxy, "Browse",
3916 prv_update_object_browse_cb, cb_data,
3917 "ObjectID", G_TYPE_STRING, task->target.id,
3918 "BrowseFlag", G_TYPE_STRING, "BrowseMetadata",
3919 "Filter", G_TYPE_STRING, upnp_filter,
3920 "StartingIndex", G_TYPE_INT, 0,
3921 "RequestedCount", G_TYPE_INT, 0,
3922 "SortCriteria", G_TYPE_STRING,
3923 "", NULL);
3924
3925 cb_data->proxy = context->service_proxy;
3926
3927 g_object_add_weak_pointer((G_OBJECT(context->service_proxy)),
3928 (gpointer *)&cb_data->proxy);
3929
3930 cb_data->cancel_id = g_cancellable_connect(cb_data->cancellable,
3931 G_CALLBACK(dls_async_task_cancelled_cb),
3932 cb_data, NULL);
3933
3934 DLEYNA_LOG_DEBUG("Exit");
3935 }
3936
3937 static void prv_didls_free(gpointer data)
3938 {
3939 prv_new_playlist_ct_t *priv_t = (prv_new_playlist_ct_t *)data;
3940
3941 if (priv_t) {
3942 g_free(priv_t->id);
3943 g_free(priv_t->parent_id);
3944 g_free(priv_t);
3945 }
3946 }
3947
3948 static void prv_playlist_upload_cb(GUPnPServiceProxy *proxy,
3949 GUPnPServiceProxyAction *action,
3950 gpointer user_data)
3951 {
3952 dls_async_task_t *cb_data = user_data;
3953 gchar *didls;
3954
3955 didls = gupnp_media_collection_get_string(
3956 cb_data->ut.playlist.collection);
3957
3958 DLEYNA_LOG_DEBUG_NL();
3959 DLEYNA_LOG_DEBUG("Collection: %s", didls);
3960 DLEYNA_LOG_DEBUG_NL();
3961
3962 prv_generic_upload_cb(cb_data, NULL, didls, strlen(didls), "text/xml");
3963 }
3964
3965 static void prv_create_didls_item_parse_object(GUPnPDIDLLiteParser *parser,
3966 GUPnPDIDLLiteObject *object,
3967 gpointer user_data)
3968 {
3969 const char *class;
3970 const char *title;
3971 const char *artist;
3972 const char *album;
3973 const char *uri = NULL;
3974 GList *resources;
3975 GUPnPDIDLLiteObject *item_obj;
3976 GUPnPDIDLLiteItem *item;
3977 GUPnPDIDLLiteResource *res;
3978 GUPnPMediaCollection *collection = user_data;
3979
3980 if (GUPNP_IS_DIDL_LITE_CONTAINER(object))
3981 goto exit;
3982
3983 class = gupnp_didl_lite_object_get_upnp_class(object);
3984 title = gupnp_didl_lite_object_get_title(object);
3985 artist = gupnp_didl_lite_object_get_artist(object);
3986 album = gupnp_didl_lite_object_get_album(object);
3987 resources = gupnp_didl_lite_object_get_resources(object);
3988
3989 if (resources != NULL) {
3990 if (resources->data != NULL)
3991 uri = gupnp_didl_lite_resource_get_uri(resources->data);
3992
3993 g_list_free_full(resources, g_object_unref);
3994 }
3995
3996 DLEYNA_LOG_DEBUG("Create DIDL_S Item");
3997 DLEYNA_LOG_DEBUG("title: %s", title);
3998 DLEYNA_LOG_DEBUG("class: %s", class);
3999 DLEYNA_LOG_DEBUG("Artist: %s", artist);
4000 DLEYNA_LOG_DEBUG("album: %s", album);
4001 DLEYNA_LOG_DEBUG("URI: %s", uri);
4002 DLEYNA_LOG_DEBUG_NL();
4003
4004 item = gupnp_media_collection_add_item(collection);
4005 item_obj = GUPNP_DIDL_LITE_OBJECT(item);
4006
4007 if (title && *title)
4008 gupnp_didl_lite_object_set_title(item_obj, title);
4009
4010 if (class && *class)
4011 gupnp_didl_lite_object_set_upnp_class(item_obj, class);
4012
4013 if (artist && *artist)
4014 gupnp_didl_lite_object_set_artist(item_obj, artist);
4015
4016 if (album && *album)
4017 gupnp_didl_lite_object_set_album(item_obj, album);
4018
4019 if (uri && *uri) {
4020 res = gupnp_didl_lite_object_add_resource(item_obj);
4021 gupnp_didl_lite_resource_set_uri(res, uri);
4022 g_object_unref(res);
4023 }
4024
4025 g_object_unref(item);
4026
4027 exit:
4028 return;
4029 }
4030
4031 static void prv_create_didls_item_browse_cb(GUPnPServiceProxy *proxy,
4032 GUPnPServiceProxyAction *action,
4033 gpointer user_data)
4034 {
4035 GError *error = NULL;
4036 prv_new_playlist_ct_t *priv_t = user_data;
4037 dls_async_task_t *cb_data = priv_t->cb_data;
4038 GUPnPDIDLLiteParser *parser = NULL;
4039 gchar *result = NULL;
4040
4041 if (!gupnp_service_proxy_end_action(proxy, action, &error,
4042 "Result", G_TYPE_STRING,
4043 &result, NULL)) {
4044 DLEYNA_LOG_WARNING("Browse Object operation failed: %s",
4045 error->message);
4046 DLEYNA_LOG_DEBUG_NL();
4047
4048 cb_data->error = g_error_new(DLEYNA_SERVER_ERROR,
4049 DLEYNA_ERROR_OPERATION_FAILED,
4050 "Browse operation failed: %s",
4051 error->message);
4052 goto on_exit;
4053 }
4054
4055 DLEYNA_LOG_DEBUG_NL();
4056 DLEYNA_LOG_DEBUG("Result: %s", result);
4057 DLEYNA_LOG_DEBUG_NL();
4058
4059 parser = gupnp_didl_lite_parser_new();
4060
4061 g_signal_connect(parser, "object-available",
4062 G_CALLBACK(prv_create_didls_item_parse_object),
4063 cb_data->ut.playlist.collection);
4064
4065 if (gupnp_didl_lite_parser_parse_didl(parser, result, &error))
4066 goto on_exit;
4067
4068 if (error->code == GUPNP_XML_ERROR_EMPTY_NODE) {
4069 DLEYNA_LOG_WARNING("Property not defined for object");
4070
4071 cb_data->error = g_error_new(DLEYNA_SERVER_ERROR,
4072 DLEYNA_ERROR_UNKNOWN_PROPERTY,
4073 "Property not defined for object");
4074 } else {
4075 DLEYNA_LOG_WARNING("Unable to parse results of browse: %s",
4076 error->message);
4077
4078 cb_data->error = g_error_new(DLEYNA_SERVER_ERROR,
4079 DLEYNA_ERROR_OPERATION_FAILED,
4080 "Unable to parse results of browse: %s",
4081 error->message);
4082 }
4083
4084 on_exit:
4085
4086 if (cb_data->error != NULL)
4087 dleyna_task_processor_cancel_queue(
4088 cb_data->ut.playlist.queue_id);
4089
4090 if (parser)
4091 g_object_unref(parser);
4092
4093 if (error)
4094 g_error_free(error);
4095
4096 g_free(result);
4097 }
4098
4099 static GUPnPServiceProxyAction *prv_create_didls_item_browse(
4100 dleyna_service_task_t *task,
4101 GUPnPServiceProxy *proxy,
4102 gboolean *failed)
4103 {
4104 prv_new_playlist_ct_t *priv_t;
4105
4106 priv_t = (prv_new_playlist_ct_t *)dleyna_service_task_get_user_data(
4107 task);
4108 *failed = FALSE;
4109
4110 DLEYNA_LOG_DEBUG("Browse for ID: %s", priv_t->id);
4111
4112 return gupnp_service_proxy_begin_action(
4113 proxy, "Browse",
4114 dleyna_service_task_begin_action_cb, task,
4115 "ObjectID", G_TYPE_STRING, priv_t->id,
4116 "BrowseFlag", G_TYPE_STRING, "BrowseMetadata",
4117 "Filter", G_TYPE_STRING, "upnp:artist,upnp:album,res",
4118 "StartingIndex", G_TYPE_INT, 0,
4119 "RequestedCount", G_TYPE_INT, 1,
4120 "SortCriteria", G_TYPE_STRING, "", NULL);
4121 }
4122
4123 static gboolean prv_create_chain_didls_items(dls_task_t *task,
4124 GUPnPServiceProxy *proxy,
4125 dls_async_task_t *cb_data)
4126 {
4127 gchar *root_path = NULL;
4128 gchar *path;
4129 gchar *id = NULL;
4130 prv_new_playlist_ct_t *priv_t;
4131 dls_async_playlist_t *a_playlist = &cb_data->ut.playlist;
4132 GVariantIter iter;
4133
4134 DLEYNA_LOG_DEBUG_NL();
4135
4136 a_playlist->collection = gupnp_media_collection_new();
4137 gupnp_media_collection_set_title(a_playlist->collection,
4138 task->ut.playlist.title);
4139 gupnp_media_collection_set_author(a_playlist->collection,
4140 task->ut.playlist.creator);
4141
4142 g_variant_iter_init(&iter, task->ut.playlist.item_path);
4143
4144 while (g_variant_iter_next(&iter, "&o", &path)) {
4145 if (!dls_path_get_path_and_id(path, &root_path, &id, NULL)) {
4146 DLEYNA_LOG_DEBUG("Can't get id for path %s", path);
4147 cb_data->error = g_error_new(
4148 DLEYNA_SERVER_ERROR,
4149 DLEYNA_ERROR_OBJECT_NOT_FOUND,
4150 "Unable to find object for path: %s",
4151 path);
4152 goto on_error;
4153 }
4154
4155 DLEYNA_LOG_DEBUG("Create Task: @id: %s - Root: %s",
4156 id, root_path);
4157
4158 g_free(root_path);
4159
4160 priv_t = g_new0(prv_new_playlist_ct_t, 1);
4161 priv_t->cb_data = cb_data;
4162 priv_t->id = id;
4163
4164 dleyna_service_task_add(a_playlist->queue_id,
4165 prv_create_didls_item_browse,
4166 proxy,
4167 prv_create_didls_item_browse_cb,
4168 prv_didls_free, priv_t);
4169 }
4170
4171 DLEYNA_LOG_DEBUG_NL();
4172 return TRUE;
4173
4174 on_error:
4175
4176 return FALSE;
4177 }
4178
4179 static void prv_create_playlist_object(dls_task_create_playlist_t *t_playlist,
4180 dls_async_playlist_t *a_playlist,
4181 char *parent_id)
4182 {
4183 GUPnPDIDLLiteWriter *writer;
4184 GUPnPDIDLLiteObject *item;
4185 GUPnPProtocolInfo *protocol_info;
4186 GUPnPDIDLLiteResource *res;
4187 GUPnPDIDLLiteContributor *creator;
4188 GUPnPDIDLLiteContributor *author;
4189 GTimeVal time_v;
4190 gchar *time_c;
4191
4192 writer = gupnp_didl_lite_writer_new(NULL);
4193 item = GUPNP_DIDL_LITE_OBJECT(gupnp_didl_lite_writer_add_item(writer));
4194
4195 gupnp_didl_lite_object_set_id(item, "");
4196 gupnp_didl_lite_object_set_title(item, t_playlist->title);
4197 gupnp_didl_lite_object_set_genre(item, t_playlist->genre);
4198 gupnp_didl_lite_object_set_description(item, t_playlist->desc);
4199
4200 creator = gupnp_didl_lite_object_add_creator(item);
4201 author = gupnp_didl_lite_object_add_author(item);
4202 gupnp_didl_lite_contributor_set_name(creator, t_playlist->creator);
4203 gupnp_didl_lite_contributor_set_name(author, t_playlist->creator);
4204
4205 gupnp_didl_lite_object_set_parent_id(item, parent_id);
4206 gupnp_didl_lite_object_set_upnp_class(item, "object.item.playlistItem");
4207 gupnp_didl_lite_object_set_restricted(item, FALSE);
4208
4209 protocol_info = gupnp_protocol_info_new();
4210 gupnp_protocol_info_set_mime_type(protocol_info, "text/xml");
4211 gupnp_protocol_info_set_protocol(protocol_info, "*");
4212 gupnp_protocol_info_set_network(protocol_info, "*");
4213 gupnp_protocol_info_set_dlna_profile(protocol_info, "DIDL_S");
4214
4215 res = gupnp_didl_lite_object_add_resource(item);
4216 gupnp_didl_lite_resource_set_protocol_info(res, protocol_info);
4217
4218 g_get_current_time(&time_v);
4219 time_c = g_time_val_to_iso8601(&time_v);
4220 gupnp_didl_lite_object_set_date(item, time_c);
4221
4222 /* TODO: Need to compute DLNA Profile */
4223
4224 a_playlist->didl = gupnp_didl_lite_writer_get_string(writer);
4225
4226 DLEYNA_LOG_DEBUG("Playlist object %s created", t_playlist->title);
4227
4228 g_object_unref(res);
4229 g_object_unref(protocol_info);
4230 g_object_unref(creator);
4231 g_object_unref(author);
4232 g_object_unref(item);
4233 g_object_unref(writer);
4234 g_free(time_c);
4235 }
4236
4237 static void prv_create_didls_chain_end(gboolean cancelled, gpointer data)
4238 {
4239 prv_new_playlist_ct_t *priv_t = data;
4240 dls_async_task_t *cb_data = priv_t->cb_data;
4241 dls_async_playlist_t *a_playlist;
4242 dls_task_create_playlist_t *t_playlist;
4243
4244 DLEYNA_LOG_DEBUG("Enter");
4245
4246 if (cb_data->cancel_id) {
4247 if (!g_cancellable_is_cancelled(cb_data->cancellable))
4248 g_cancellable_disconnect(cb_data->cancellable,
4249 cb_data->cancel_id);
4250 cb_data->cancel_id = 0;
4251 }
4252
4253 if (cancelled)
4254 goto on_clear;
4255
4256 t_playlist = &cb_data->task.ut.playlist;
4257 a_playlist = &cb_data->ut.playlist;
4258 prv_create_playlist_object(t_playlist, a_playlist, priv_t->parent_id);
4259
4260 DLEYNA_LOG_DEBUG("Creating object");
4261 cb_data->action = gupnp_service_proxy_begin_action(
4262 cb_data->proxy,
4263 "CreateObject",
4264 prv_playlist_upload_cb, cb_data,
4265 "ContainerID", G_TYPE_STRING, priv_t->parent_id,
4266 "Elements", G_TYPE_STRING, a_playlist->didl,
4267 NULL);
4268
4269 cb_data->cancel_id = g_cancellable_connect(
4270 cb_data->cancellable,
4271 G_CALLBACK(dls_async_task_cancelled_cb),
4272 cb_data, NULL);
4273 on_clear:
4274
4275 if (cancelled) {
4276 if (!cb_data->error)
4277 cb_data->error = g_error_new(DLEYNA_SERVER_ERROR,
4278 DLEYNA_ERROR_CANCELLED,
4279 "Operation cancelled.");
4280 (void) g_idle_add(dls_async_task_complete, cb_data);
4281 }
4282
4283 prv_didls_free(priv_t);
4284
4285 cb_data->ut.playlist.queue_id = NULL;
4286
4287 DLEYNA_LOG_DEBUG("Exit");
4288 }
4289
4290 static void prv_create_chain_cancelled(GCancellable *cancellable,
4291 gpointer user_data)
4292 {
4293 dls_async_task_t *cb_data = user_data;
4294 const dleyna_task_queue_key_t *queue_id = cb_data->ut.playlist.queue_id;
4295
4296 DLEYNA_LOG_DEBUG("Enter");
4297
4298 dleyna_task_processor_cancel_queue(queue_id);
4299 }
4300
4301 void dls_device_playlist_upload(dls_client_t *client,
4302 dls_task_t *task,
4303 const gchar *parent_id)
4304 {
4305 dls_async_task_t *cb_data = (dls_async_task_t *)task;
4306 dls_device_context_t *context;
4307 prv_new_playlist_ct_t *priv_t;
4308 const dleyna_task_queue_key_t *queue_id;
4309
4310 DLEYNA_LOG_DEBUG("Enter");
4311 DLEYNA_LOG_DEBUG("Uploading playlist to %s", parent_id);
4312
4313 priv_t = g_new0(prv_new_playlist_ct_t, 1);
4314 priv_t->cb_data = cb_data;
4315 priv_t->parent_id = g_strdup(parent_id);
4316
4317 queue_id = dleyna_task_processor_add_queue(
4318 dls_server_get_task_processor(),
4319 dleyna_service_task_create_source(),
4320 DLS_SERVER_SINK,
4321 DLEYNA_TASK_QUEUE_FLAG_AUTO_REMOVE,
4322 dleyna_service_task_process_cb,
4323 dleyna_service_task_cancel_cb,
4324 dleyna_service_task_delete_cb);
4325 dleyna_task_queue_set_finally(queue_id, prv_create_didls_chain_end);
4326 dleyna_task_queue_set_user_data(queue_id, priv_t);
4327
4328 context = dls_device_get_context(task->target.device, client);
4329
4330 cb_data->proxy = context->service_proxy;
4331 cb_data->ut.playlist.queue_id = queue_id;
4332
4333 g_object_add_weak_pointer((G_OBJECT(context->service_proxy)),
4334 (gpointer *)&cb_data->proxy);
4335
4336 if (prv_create_chain_didls_items(task, cb_data->proxy, cb_data)) {
4337 cb_data->cancel_id = g_cancellable_connect(
4338 cb_data->cancellable,
4339 G_CALLBACK(prv_create_chain_cancelled),
4340 cb_data, NULL);
4341 dleyna_task_queue_start(queue_id);
4342 } else {
4343 (void) g_idle_add(dls_async_task_complete, cb_data);
4344 }
4345
4346 DLEYNA_LOG_DEBUG("Exit");
4347 }
+0
-129
lib/device.h less more
0 /*
1 * dLeyna
2 *
3 * Copyright (C) 2012-2013 Intel Corporation. All rights reserved.
4 *
5 * This program is free software; you can redistribute it and/or modify it
6 * under the terms and conditions of the GNU Lesser General Public License,
7 * version 2.1, as published by the Free Software Foundation.
8 *
9 * This program is distributed in the hope it will be useful, but WITHOUT
10 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
11 * FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License
12 * for more details.
13 *
14 * You should have received a copy of the GNU Lesser General Public License
15 * along with this program; if not, write to the Free Software Foundation, Inc.,
16 * 51 Franklin St - Fifth Floor, Boston, MA 02110-1301 USA.
17 *
18 * Mark Ryan <mark.d.ryan@intel.com>
19 *
20 */
21
22 #ifndef DLS_DEVICE_H__
23 #define DLS_DEVICE_H__
24
25 #include <libgupnp/gupnp-control-point.h>
26
27 #include <libdleyna/core/connector.h>
28 #include <libdleyna/core/task-processor.h>
29
30 #include "async.h"
31 #include "client.h"
32 #include "props.h"
33
34 struct dls_device_context_t_ {
35 gchar *ip_address;
36 GUPnPDeviceProxy *device_proxy;
37 GUPnPServiceProxy *service_proxy;
38 dls_device_t *device;
39 gboolean subscribed;
40 guint timeout_id;
41 };
42
43 struct dls_device_t_ {
44 dleyna_connector_id_t connection;
45 guint id;
46 gchar *path;
47 GPtrArray *contexts;
48 guint timeout_id;
49 GHashTable *uploads;
50 GHashTable *upload_jobs;
51 guint upload_id;
52 guint system_update_id;
53 GVariant *search_caps;
54 GVariant *sort_caps;
55 GVariant *sort_ext_caps;
56 GVariant *feature_list;
57 gboolean shutting_down;
58 };
59
60 dls_device_context_t *dls_device_append_new_context(dls_device_t *device,
61 const gchar *ip_address,
62 GUPnPDeviceProxy *proxy);
63 void dls_device_delete(void *device);
64
65 void dls_device_unsubscribe(void *device);
66
67 dls_device_t *dls_device_new(
68 dleyna_connector_id_t connection,
69 GUPnPDeviceProxy *proxy,
70 const gchar *ip_address,
71 const dleyna_connector_dispatch_cb_t *dispatch_table,
72 GHashTable *filter_map,
73 guint counter,
74 const dleyna_task_queue_key_t *queue_id);
75
76 dls_device_t *dls_device_from_path(const gchar *path, GHashTable *device_list);
77
78 dls_device_context_t *dls_device_get_context(const dls_device_t *device,
79 dls_client_t *client);
80
81 void dls_device_get_children(dls_client_t *client,
82 dls_task_t *task,
83 const gchar *upnp_filter, const gchar *sort_by);
84
85 void dls_device_get_all_props(dls_client_t *client,
86 dls_task_t *task,
87 gboolean root_object);
88
89 void dls_device_get_prop(dls_client_t *client,
90 dls_task_t *task,
91 dls_prop_map_t *prop_map, gboolean root_object);
92
93 void dls_device_search(dls_client_t *client,
94 dls_task_t *task,
95 const gchar *upnp_filter, const gchar *upnp_query,
96 const gchar *sort_by);
97
98 void dls_device_get_resource(dls_client_t *client,
99 dls_task_t *task,
100 const gchar *upnp_filter);
101
102 void dls_device_subscribe_to_contents_change(dls_device_t *device);
103
104 void dls_device_upload(dls_client_t *client,
105 dls_task_t *task, const gchar *parent_id);
106
107 gboolean dls_device_get_upload_status(dls_task_t *task, GError **error);
108
109 gboolean dls_device_cancel_upload(dls_task_t *task, GError **error);
110
111 void dls_device_get_upload_ids(dls_task_t *task);
112
113 void dls_device_delete_object(dls_client_t *client,
114 dls_task_t *task);
115
116 void dls_device_create_container(dls_client_t *client,
117 dls_task_t *task,
118 const gchar *parent_id);
119
120 void dls_device_update_object(dls_client_t *client,
121 dls_task_t *task,
122 const gchar *upnp_filter);
123
124 void dls_device_playlist_upload(dls_client_t *client,
125 dls_task_t *task,
126 const gchar *parent_id);
127
128 #endif /* DLS_DEVICE_H__ */
+0
-11
lib/dleyna-server-1.0.pc.in less more
0 prefix=@prefix@
1 exec_prefix=@exec_prefix@
2 libexecdir=@libexecdir@
3 includedir=${prefix}/include
4 libdir=@libdir@
5
6 Name: @PACKAGE@
7 Description: UPnP & DLNA server library
8 Libs: -L${libdir} -ldleyna-server-1.0
9 Requires.private: glib-2.0 gio-2.0 gupnp-1.0 gupnp-av-1.0 dleyna-core-1.0
10 Version: @VERSION@
+0
-37
lib/dleyna-server-service.conf.in less more
0 # Configuration file for dleyna-server
1 #
2 #
3 #
4 # General configuration options
5 [general]
6
7 # true: Service always stay in memory running
8 # false: Service quit when the last client disconnects.
9 never-quit=@never_quit@
10
11 # IPC connector name
12 connector-name=@with_connector_name@
13
14 # Log configuration options
15 [log]
16
17 # Define the logging output method. 3 technologies are defined:
18 #
19 # 0=Syslog
20 # 1=GLib
21 # 2=File
22 log-type=@with_log_type@
23
24 # Comma-separated list of logging level.
25 # Log levels are: 1=critical, 2=error, 3=warning, 4=message, 5=info, 6=debug
26 #
27 # Allowed values for log-levels are
28 # 0 = disabled
29 # 7 = default (=1,2,5)
30 # 8 = all (=1,2,3,4,5,6)
31 # 1,..,6 = a comma separated list of log level
32 #
33 # IMPORTANT: This log level is a subset of the log level defined at compile time
34 # You can't enable levels disabled at compile time
35 # level=8 means all level flags defined at compile time.
36 log-level=@with_log_level@
+0
-191
lib/interface.h less more
0 /*
1 * dLeyna
2 *
3 * Copyright (C) 2012-2013 Intel Corporation. All rights reserved.
4 *
5 * This program is free software; you can redistribute it and/or modify it
6 * under the terms and conditions of the GNU Lesser General Public License,
7 * version 2.1, as published by the Free Software Foundation.
8 *
9 * This program is distributed in the hope it will be useful, but WITHOUT
10 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
11 * FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License
12 * for more details.
13 *
14 * You should have received a copy of the GNU Lesser General Public License
15 * along with this program; if not, write to the Free Software Foundation, Inc.,
16 * 51 Franklin St - Fifth Floor, Boston, MA 02110-1301 USA.
17 *
18 * Regis Merlino <regis.merlino@intel.com>
19 *
20 */
21
22 #ifndef DLEYNA_SERVER_INTERFACE_H__
23 #define DLEYNA_SERVER_INTERFACE_H__
24
25 enum dls_interface_type_ {
26 DLS_INTERFACE_INFO_PROPERTIES,
27 DLS_INTERFACE_INFO_OBJECT,
28 DLS_INTERFACE_INFO_CONTAINER,
29 DLS_INTERFACE_INFO_ITEM,
30 DLS_INTERFACE_INFO_DEVICE,
31 DLS_INTERFACE_INFO_MAX
32 };
33
34 #define DLS_INTERFACE_PROPERTIES "org.freedesktop.DBus.Properties"
35 #define DLS_INTERFACE_MEDIA_CONTAINER "org.gnome.UPnP.MediaContainer2"
36 #define DLS_INTERFACE_MEDIA_OBJECT "org.gnome.UPnP.MediaObject2"
37 #define DLS_INTERFACE_MEDIA_ITEM "org.gnome.UPnP.MediaItem2"
38
39 /* Object Properties */
40 #define DLS_INTERFACE_PROP_PATH "Path"
41 #define DLS_INTERFACE_PROP_PARENT "Parent"
42 #define DLS_INTERFACE_PROP_RESTRICTED "Restricted"
43 #define DLS_INTERFACE_PROP_DISPLAY_NAME "DisplayName"
44 #define DLS_INTERFACE_PROP_TYPE "Type"
45 #define DLS_INTERFACE_PROP_CREATOR "Creator"
46 #define DLS_INTERFACE_PROP_DLNA_MANAGED "DLNAManaged"
47 #define DLS_INTERFACE_PROP_OBJECT_UPDATE_ID "ObjectUpdateID"
48
49 /* Item Properties */
50 #define DLS_INTERFACE_PROP_REFPATH "RefPath"
51 #define DLS_INTERFACE_PROP_ARTIST "Artist"
52 #define DLS_INTERFACE_PROP_ARTISTS "Artists"
53 #define DLS_INTERFACE_PROP_ALBUM "Album"
54 #define DLS_INTERFACE_PROP_DATE "Date"
55 #define DLS_INTERFACE_PROP_GENRE "Genre"
56 #define DLS_INTERFACE_PROP_TRACK_NUMBER "TrackNumber"
57 #define DLS_INTERFACE_PROP_ALBUM_ART_URL "AlbumArtURL"
58 #define DLS_INTERFACE_PROP_RESOURCES "Resources"
59
60 /* Container Properties */
61 #define DLS_INTERFACE_PROP_SEARCHABLE "Searchable"
62 #define DLS_INTERFACE_PROP_CHILD_COUNT "ChildCount"
63 #define DLS_INTERFACE_PROP_CREATE_CLASSES "CreateClasses"
64 #define DLS_INTERFACE_PROP_CONTAINER_UPDATE_ID "ContainerUpdateID"
65 #define DLS_INTERFACE_PROP_TOTAL_DELETED_CHILD_COUNT "TotalDeletedChildCount"
66
67 /* Device Properties */
68 #define DLS_INTERFACE_PROP_LOCATION "Location"
69 #define DLS_INTERFACE_PROP_UDN "UDN"
70 #define DLS_INTERFACE_PROP_DEVICE_TYPE "DeviceType"
71 #define DLS_INTERFACE_PROP_FRIENDLY_NAME "FriendlyName"
72 #define DLS_INTERFACE_PROP_MANUFACTURER "Manufacturer"
73 #define DLS_INTERFACE_PROP_MANUFACTURER_URL "ManufacturerUrl"
74 #define DLS_INTERFACE_PROP_MODEL_DESCRIPTION "ModelDescription"
75 #define DLS_INTERFACE_PROP_MODEL_NAME "ModelName"
76 #define DLS_INTERFACE_PROP_MODEL_NUMBER "ModelNumber"
77 #define DLS_INTERFACE_PROP_MODEL_URL "ModelURL"
78 #define DLS_INTERFACE_PROP_SERIAL_NUMBER "SerialNumber"
79 #define DLS_INTERFACE_PROP_PRESENTATION_URL "PresentationURL"
80 #define DLS_INTERFACE_PROP_ICON_URL "IconURL"
81 #define DLS_INTERFACE_PROP_SV_DLNA_CAPABILITIES "DLNACaps"
82 #define DLS_INTERFACE_PROP_SV_SEARCH_CAPABILITIES "SearchCaps"
83 #define DLS_INTERFACE_PROP_SV_SORT_CAPABILITIES "SortCaps"
84 #define DLS_INTERFACE_PROP_SV_SORT_EXT_CAPABILITIES "SortExtCaps"
85 #define DLS_INTERFACE_PROP_SV_FEATURE_LIST "FeatureList"
86 #define DLS_INTERFACE_PROP_SV_SERVICE_RESET_TOKEN "ServiceResetToken"
87
88 /* Resources Properties */
89 #define DLS_INTERFACE_PROP_MIME_TYPE "MIMEType"
90 #define DLS_INTERFACE_PROP_DLNA_PROFILE "DLNAProfile"
91 #define DLS_INTERFACE_PROP_SIZE "Size"
92 #define DLS_INTERFACE_PROP_DURATION "Duration"
93 #define DLS_INTERFACE_PROP_BITRATE "Bitrate"
94 #define DLS_INTERFACE_PROP_SAMPLE_RATE "SampleRate"
95 #define DLS_INTERFACE_PROP_BITS_PER_SAMPLE "BitsPerSample"
96 #define DLS_INTERFACE_PROP_WIDTH "Width"
97 #define DLS_INTERFACE_PROP_HEIGHT "Height"
98 #define DLS_INTERFACE_PROP_COLOR_DEPTH "ColorDepth"
99 #define DLS_INTERFACE_PROP_URLS "URLs"
100 #define DLS_INTERFACE_PROP_URL "URL"
101 #define DLS_INTERFACE_PROP_UPDATE_COUNT "UpdateCount"
102
103 /* Evented State Variable Properties */
104 #define DLS_INTERFACE_PROP_ESV_SYSTEM_UPDATE_ID "SystemUpdateID"
105
106 #define DLS_INTERFACE_GET_VERSION "GetVersion"
107 #define DLS_INTERFACE_GET_SERVERS "GetServers"
108 #define DLS_INTERFACE_RELEASE "Release"
109 #define DLS_INTERFACE_SET_PROTOCOL_INFO "SetProtocolInfo"
110 #define DLS_INTERFACE_PREFER_LOCAL_ADDRESSES "PreferLocalAddresses"
111
112 #define DLS_INTERFACE_FOUND_SERVER "FoundServer"
113 #define DLS_INTERFACE_LOST_SERVER "LostServer"
114
115 #define DLS_INTERFACE_LIST_CHILDREN "ListChildren"
116 #define DLS_INTERFACE_LIST_CHILDREN_EX "ListChildrenEx"
117 #define DLS_INTERFACE_LIST_ITEMS "ListItems"
118 #define DLS_INTERFACE_LIST_ITEMS_EX "ListItemsEx"
119 #define DLS_INTERFACE_LIST_CONTAINERS "ListContainers"
120 #define DLS_INTERFACE_LIST_CONTAINERS_EX "ListContainersEx"
121 #define DLS_INTERFACE_SEARCH_OBJECTS "SearchObjects"
122 #define DLS_INTERFACE_SEARCH_OBJECTS_EX "SearchObjectsEx"
123 #define DLS_INTERFACE_UPDATE "Update"
124
125 #define DLS_INTERFACE_GET_COMPATIBLE_RESOURCE "GetCompatibleResource"
126
127 #define DLS_INTERFACE_GET "Get"
128 #define DLS_INTERFACE_GET_ALL "GetAll"
129 #define DLS_INTERFACE_INTERFACE_NAME "InterfaceName"
130 #define DLS_INTERFACE_PROPERTY_NAME "PropertyName"
131 #define DLS_INTERFACE_PROPERTIES_VALUE "Properties"
132 #define DLS_INTERFACE_VALUE "value"
133 #define DLS_INTERFACE_CHILD_TYPES "ChildTypes"
134
135 #define DLS_INTERFACE_VERSION "Version"
136 #define DLS_INTERFACE_SERVERS "Servers"
137
138 #define DLS_INTERFACE_CRITERIA "Criteria"
139 #define DLS_INTERFACE_DICT "Dictionary"
140 #define DLS_INTERFACE_PATH "Path"
141 #define DLS_INTERFACE_QUERY "Query"
142 #define DLS_INTERFACE_PROTOCOL_INFO "ProtocolInfo"
143 #define DLS_INTERFACE_PREFER "Prefer"
144
145 #define DLS_INTERFACE_OFFSET "Offset"
146 #define DLS_INTERFACE_MAX "Max"
147 #define DLS_INTERFACE_FILTER "Filter"
148 #define DLS_INTERFACE_CHILDREN "Children"
149 #define DLS_INTERFACE_SORT_BY "SortBy"
150 #define DLS_INTERFACE_TOTAL_ITEMS "TotalItems"
151
152 #define DLS_INTERFACE_PROPERTIES_CHANGED "PropertiesChanged"
153 #define DLS_INTERFACE_CHANGED_PROPERTIES "ChangedProperties"
154 #define DLS_INTERFACE_INVALIDATED_PROPERTIES "InvalidatedProperties"
155 #define DLS_INTERFACE_ESV_CONTAINER_UPDATE_IDS "ContainerUpdateIDs"
156 #define DLS_INTERFACE_CONTAINER_PATHS_ID "ContainerPathsIDs"
157 #define DLS_INTERFACE_ESV_LAST_CHANGE "LastChange"
158 #define DLS_INTERFACE_LAST_CHANGE_STATE_EVENT "StateEvent"
159
160 #define DLS_INTERFACE_DELETE "Delete"
161
162 #define DLS_INTERFACE_CREATE_CONTAINER "CreateContainer"
163 #define DLS_INTERFACE_CREATE_CONTAINER_IN_ANY "CreateContainerInAnyContainer"
164
165 #define DLS_INTERFACE_UPLOAD "Upload"
166 #define DLS_INTERFACE_UPLOAD_TO_ANY "UploadToAnyContainer"
167 #define DLS_INTERFACE_GET_UPLOAD_STATUS "GetUploadStatus"
168 #define DLS_INTERFACE_GET_UPLOAD_IDS "GetUploadIDs"
169 #define DLS_INTERFACE_CANCEL_UPLOAD "CancelUpload"
170 #define DLS_INTERFACE_TOTAL "Total"
171 #define DLS_INTERFACE_LENGTH "Length"
172 #define DLS_INTERFACE_FILE_PATH "FilePath"
173 #define DLS_INTERFACE_UPLOAD_ID "UploadId"
174 #define DLS_INTERFACE_UPLOAD_IDS "UploadIDs"
175 #define DLS_INTERFACE_UPLOAD_STATUS "UploadStatus"
176 #define DLS_INTERFACE_UPLOAD_UPDATE "UploadUpdate"
177 #define DLS_INTERFACE_TO_ADD_UPDATE "ToAddUpdate"
178 #define DLS_INTERFACE_TO_DELETE "ToDelete"
179 #define DLS_INTERFACE_CANCEL "Cancel"
180
181 #define DLS_INTERFACE_CREATE_PLAYLIST "CreatePlaylist"
182 #define DLS_INTERFACE_CREATE_PLAYLIST_TO_ANY "CreatePlaylistInAnyContainer"
183 #define DLS_INTERFACE_TITLE "Title"
184 #define DLS_INTERFACE_CREATOR "Creator"
185 #define DLS_INTERFACE_GENRE "Genre"
186 #define DLS_INTERFACE_DESCRIPTION "Description"
187 #define DLS_INTERFACE_PLAYLIST_ITEMS "PlaylistItems"
188
189
190 #endif /* DLEYNA_SERVER_INTERFACE_H__ */
+0
-154
lib/path.c less more
0 /*
1 * dLeyna
2 *
3 * Copyright (C) 2012-2013 Intel Corporation. All rights reserved.
4 *
5 * This program is free software; you can redistribute it and/or modify it
6 * under the terms and conditions of the GNU Lesser General Public License,
7 * version 2.1, as published by the Free Software Foundation.
8 *
9 * This program is distributed in the hope it will be useful, but WITHOUT
10 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
11 * FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License
12 * for more details.
13 *
14 * You should have received a copy of the GNU Lesser General Public License
15 * along with this program; if not, write to the Free Software Foundation, Inc.,
16 * 51 Franklin St - Fifth Floor, Boston, MA 02110-1301 USA.
17 *
18 * Mark Ryan <mark.d.ryan@intel.com>
19 *
20 */
21
22 #include <stdio.h>
23 #include <string.h>
24
25 #include <libdleyna/core/error.h>
26
27 #include "path.h"
28 #include "server.h"
29
30 gboolean dls_path_get_non_root_id(const gchar *object_path,
31 const gchar **slash_before_id)
32 {
33 gboolean retval = FALSE;
34 unsigned int offset = strlen(DLEYNA_SERVER_PATH) + 1;
35
36 if (!g_str_has_prefix(object_path, DLEYNA_SERVER_PATH "/"))
37 goto on_error;
38
39 if (!object_path[offset])
40 goto on_error;
41
42 *slash_before_id = strchr(&object_path[offset], '/');
43 retval = TRUE;
44
45 on_error:
46
47 return retval;
48 }
49
50 static gchar *prv_object_name_to_id(const gchar *object_name)
51 {
52 gchar *retval = NULL;
53 unsigned int object_len = strlen(object_name);
54 unsigned int i;
55 gint hex;
56 gchar byte;
57
58 if (object_len & 1)
59 goto on_error;
60
61 retval = g_malloc((object_len >> 1) + 1);
62
63 for (i = 0; i < object_len; i += 2) {
64 hex = g_ascii_xdigit_value(object_name[i]);
65
66 if (hex == -1)
67 goto on_error;
68
69 byte = hex << 4;
70 hex = g_ascii_xdigit_value(object_name[i + 1]);
71
72 if (hex == -1)
73 goto on_error;
74
75 byte |= hex;
76 retval[i >> 1] = byte;
77 }
78 retval[i >> 1] = 0;
79
80 return retval;
81
82 on_error:
83
84 g_free(retval);
85
86 return NULL;
87 }
88
89 gboolean dls_path_get_path_and_id(const gchar *object_path, gchar **root_path,
90 gchar **id, GError **error)
91 {
92 const gchar *slash;
93 gchar *coded_id;
94
95 if (!dls_path_get_non_root_id(object_path, &slash))
96 goto on_error;
97
98 if (!slash) {
99 *root_path = g_strdup(object_path);
100 *id = g_strdup("0");
101 } else {
102 if (!slash[1])
103 goto on_error;
104
105 coded_id = prv_object_name_to_id(slash + 1);
106
107 if (!coded_id)
108 goto on_error;
109
110 *root_path = g_strndup(object_path, slash - object_path);
111 *id = coded_id;
112 }
113
114 return TRUE;
115
116 on_error:
117 if (error)
118 *error = g_error_new(DLEYNA_SERVER_ERROR, DLEYNA_ERROR_BAD_PATH,
119 "object path is badly formed.");
120
121 return FALSE;
122 }
123
124 static gchar *prv_id_to_object_name(const gchar *id)
125 {
126 gchar *retval;
127 unsigned int i;
128 unsigned int data_len = strlen(id);
129
130 retval = g_malloc((data_len << 1) + 1);
131 retval[0] = 0;
132
133 for (i = 0; i < data_len; i++)
134 sprintf(&retval[i << 1], "%0x", (guint8) id[i]);
135
136 return retval;
137 }
138
139 gchar *dls_path_from_id(const gchar *root_path, const gchar *id)
140 {
141 gchar *coded_id;
142 gchar *path;
143
144 if (!strcmp(id, "0")) {
145 path = g_strdup(root_path);
146 } else {
147 coded_id = prv_id_to_object_name(id);
148 path = g_strdup_printf("%s/%s", root_path, coded_id);
149 g_free(coded_id);
150 }
151
152 return path;
153 }
+0
-36
lib/path.h less more
0 /*
1 * dLeyna
2 *
3 * Copyright (C) 2012-2013 Intel Corporation. All rights reserved.
4 *
5 * This program is free software; you can redistribute it and/or modify it
6 * under the terms and conditions of the GNU Lesser General Public License,
7 * version 2.1, as published by the Free Software Foundation.
8 *
9 * This program is distributed in the hope it will be useful, but WITHOUT
10 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
11 * FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License
12 * for more details.
13 *
14 * You should have received a copy of the GNU Lesser General Public License
15 * along with this program; if not, write to the Free Software Foundation, Inc.,
16 * 51 Franklin St - Fifth Floor, Boston, MA 02110-1301 USA.
17 *
18 * Mark Ryan <mark.d.ryan@intel.com>
19 *
20 */
21
22 #ifndef DLS_PATH_H__
23 #define DLS_PATH_H__
24
25 #include <glib.h>
26
27 gboolean dls_path_get_non_root_id(const gchar *object_path,
28 const gchar **slash_before_id);
29
30 gboolean dls_path_get_path_and_id(const gchar *object_path, gchar **root_path,
31 gchar **id, GError **error);
32
33 gchar *dls_path_from_id(const gchar *root_path, const gchar *id);
34
35 #endif /* DLS_PATH_H__ */
+0
-1792
lib/props.c less more
0 /*
1 * dLeyna
2 *
3 * Copyright (C) 2012-2013 Intel Corporation. All rights reserved.
4 *
5 * This program is free software; you can redistribute it and/or modify it
6 * under the terms and conditions of the GNU Lesser General Public License,
7 * version 2.1, as published by the Free Software Foundation.
8 *
9 * This program is distributed in the hope it will be useful, but WITHOUT
10 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
11 * FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License
12 * for more details.
13 *
14 * You should have received a copy of the GNU Lesser General Public License
15 * along with this program; if not, write to the Free Software Foundation, Inc.,
16 * 51 Franklin St - Fifth Floor, Boston, MA 02110-1301 USA.
17 *
18 * Mark Ryan <mark.d.ryan@intel.com>
19 *
20 */
21
22 #include <string.h>
23 #include <libgupnp-av/gupnp-didl-lite-contributor.h>
24
25 #include <libdleyna/core/log.h>
26
27 #include "device.h"
28 #include "interface.h"
29 #include "path.h"
30 #include "props.h"
31
32 static const gchar gUPnPContainer[] = "object.container";
33 static const gchar gUPnPAlbum[] = "object.container.album";
34 static const gchar gUPnPPerson[] = "object.container.person";
35 static const gchar gUPnPGenre[] = "object.container.genre";
36 static const gchar gUPnPAudioItem[] = "object.item.audioItem";
37 static const gchar gUPnPVideoItem[] = "object.item.videoItem";
38 static const gchar gUPnPImageItem[] = "object.item.imageItem";
39 static const gchar gUPnPPlaylistItem[] = "object.item.playlistItem";
40 static const gchar gUPnPItem[] = "object.item";
41
42 static const unsigned int gUPnPContainerLen =
43 (sizeof(gUPnPContainer) / sizeof(gchar)) - 1;
44 static const unsigned int gUPnPAlbumLen =
45 (sizeof(gUPnPAlbum) / sizeof(gchar)) - 1;
46 static const unsigned int gUPnPPersonLen =
47 (sizeof(gUPnPPerson) / sizeof(gchar)) - 1;
48 static const unsigned int gUPnPGenreLen =
49 (sizeof(gUPnPGenre) / sizeof(gchar)) - 1;
50 static const unsigned int gUPnPAudioItemLen =
51 (sizeof(gUPnPAudioItem) / sizeof(gchar)) - 1;
52 static const unsigned int gUPnPVideoItemLen =
53 (sizeof(gUPnPVideoItem) / sizeof(gchar)) - 1;
54 static const unsigned int gUPnPImageItemLen =
55 (sizeof(gUPnPImageItem) / sizeof(gchar)) - 1;
56 static const unsigned int gUPnPPlaylistItemLen =
57 (sizeof(gUPnPPlaylistItem) / sizeof(gchar)) - 1;
58 static const unsigned int gUPnPItemLen =
59 (sizeof(gUPnPItem) / sizeof(gchar)) - 1;
60
61 static const gchar gUPnPPhotoAlbum[] = "object.container.album.photoAlbum";
62 static const gchar gUPnPMusicAlbum[] = "object.container.album.musicAlbum";
63 static const gchar gUPnPMusicArtist[] = "object.container.person.musicArtist";
64 static const gchar gUPnPMovieGenre[] = "object.container.genre.movieGenre";
65 static const gchar gUPnPMusicGenre[] = "object.container.genre.musicGenre";
66 static const gchar gUPnPMusicTrack[] = "object.item.audioItem.musicTrack";
67 static const gchar gUPnPAudioBroadcast[] =
68 "object.item.audioItem.audioBroadcast";
69 static const gchar gUPnPAudioBook[] = "object.item.audioItem.audioBook";
70 static const gchar gUPnPMovie[] = "object.item.videoItem.movie";
71 static const gchar gUPnPMusicVideoClip[] =
72 "object.item.videoItem.musicVideoClip";
73 static const gchar gUPnPVideoBroadcast[] =
74 "object.item.videoItem.videoBroadcast";
75 static const gchar gUPnPPhoto[] = "object.item.imageItem.photo";
76
77 static const gchar gMediaSpec2Container[] = "container";
78 static const gchar gMediaSpec2Album[] = "album";
79 static const gchar gMediaSpec2AlbumPhoto[] = "album.photo";
80 static const gchar gMediaSpec2AlbumMusic[] = "album.music";
81 static const gchar gMediaSpec2Person[] = "person";
82 static const gchar gMediaSpec2PersonMusicArtist[] = "person.musicartist";
83 static const gchar gMediaSpec2Genre[] = "genre";
84 static const gchar gMediaSpec2GenreMovie[] = "genre.movie";
85 static const gchar gMediaSpec2GenreMusic[] = "genre.music";
86 static const gchar gMediaSpec2AudioMusic[] = "audio.music";
87 static const gchar gMediaSpec2AudioBroadcast[] = "audio.broadcast";
88 static const gchar gMediaSpec2AudioBook[] = "audio.book";
89 static const gchar gMediaSpec2Audio[] = "audio";
90 static const gchar gMediaSpec2VideoMovie[] = "video.movie";
91 static const gchar gMediaSpec2VideoMusicClip[] = "video.musicclip";
92 static const gchar gMediaSpec2VideoBroadcast[] = "video.broadcast";
93 static const gchar gMediaSpec2Video[] = "video";
94 static const gchar gMediaSpec2ImagePhoto[] = "image.photo";
95 static const gchar gMediaSpec2Image[] = "image";
96 static const gchar gMediaSpec2Playlist[] = "playlist";
97 static const gchar gMediaSpec2Item[] = "item";
98
99 static dls_prop_map_t *prv_prop_map_new(const gchar *prop_name,
100 dls_upnp_prop_mask type,
101 gboolean filter,
102 gboolean searchable,
103 gboolean updateable)
104 {
105 dls_prop_map_t *retval = g_new(dls_prop_map_t, 1);
106 retval->upnp_prop_name = prop_name;
107 retval->type = type;
108 retval->filter = filter;
109 retval->searchable = searchable;
110 retval->updateable = updateable;
111 return retval;
112 }
113
114 void dls_prop_maps_new(GHashTable **property_map, GHashTable **filter_map)
115 {
116 dls_prop_map_t *prop_t;
117 GHashTable *p_map;
118 GHashTable *f_map;
119
120 p_map = g_hash_table_new_full(g_str_hash, g_str_equal, NULL, NULL);
121 f_map = g_hash_table_new_full(g_str_hash, g_str_equal, NULL, g_free);
122
123 /* @childCount */
124 prop_t = prv_prop_map_new("@childCount",
125 DLS_UPNP_MASK_PROP_CHILD_COUNT,
126 TRUE, TRUE, FALSE);
127 g_hash_table_insert(f_map, DLS_INTERFACE_PROP_CHILD_COUNT, prop_t);
128 g_hash_table_insert(p_map, "@childCount",
129 DLS_INTERFACE_PROP_CHILD_COUNT);
130
131 /* @id */
132 prop_t = prv_prop_map_new("@id",
133 DLS_UPNP_MASK_PROP_PATH,
134 FALSE, TRUE, FALSE);
135 g_hash_table_insert(f_map, DLS_INTERFACE_PROP_PATH, prop_t);
136 g_hash_table_insert(p_map, "@id", DLS_INTERFACE_PROP_PATH);
137
138 /* @parentID */
139 prop_t = prv_prop_map_new("@parentID",
140 DLS_UPNP_MASK_PROP_PARENT,
141 FALSE, TRUE, FALSE);
142 g_hash_table_insert(f_map, DLS_INTERFACE_PROP_PARENT, prop_t);
143 g_hash_table_insert(p_map, "@parentID", DLS_INTERFACE_PROP_PARENT);
144
145 /* @refID */
146 prop_t = prv_prop_map_new("@refID",
147 DLS_UPNP_MASK_PROP_REFPATH,
148 TRUE, TRUE, FALSE);
149 g_hash_table_insert(f_map, DLS_INTERFACE_PROP_REFPATH, prop_t);
150 g_hash_table_insert(p_map, "@refID", DLS_INTERFACE_PROP_REFPATH);
151
152 /* @restricted */
153 prop_t = prv_prop_map_new("@restricted",
154 DLS_UPNP_MASK_PROP_RESTRICTED,
155 TRUE, TRUE, FALSE);
156 g_hash_table_insert(f_map, DLS_INTERFACE_PROP_RESTRICTED, prop_t);
157 g_hash_table_insert(p_map, "@restricted",
158 DLS_INTERFACE_PROP_RESTRICTED);
159
160 /* @searchable */
161 prop_t = prv_prop_map_new("@searchable",
162 DLS_UPNP_MASK_PROP_SEARCHABLE,
163 TRUE, TRUE, FALSE);
164 g_hash_table_insert(f_map, DLS_INTERFACE_PROP_SEARCHABLE, prop_t);
165 g_hash_table_insert(p_map, "@searchable",
166 DLS_INTERFACE_PROP_SEARCHABLE);
167
168 /* dc:creator */
169 prop_t = prv_prop_map_new("dc:creator",
170 DLS_UPNP_MASK_PROP_CREATOR,
171 TRUE, TRUE, FALSE);
172 g_hash_table_insert(f_map, DLS_INTERFACE_PROP_CREATOR, prop_t);
173 g_hash_table_insert(p_map, "dc:creator", DLS_INTERFACE_PROP_CREATOR);
174
175 /* dc:date */
176 prop_t = prv_prop_map_new("dc:date",
177 DLS_UPNP_MASK_PROP_DATE,
178 TRUE, TRUE, TRUE);
179 g_hash_table_insert(f_map, DLS_INTERFACE_PROP_DATE, prop_t);
180 g_hash_table_insert(p_map, "dc:date", DLS_INTERFACE_PROP_DATE);
181
182 /* dc:title */
183 prop_t = prv_prop_map_new("dc:title",
184 DLS_UPNP_MASK_PROP_DISPLAY_NAME,
185 FALSE, TRUE, TRUE);
186 g_hash_table_insert(f_map, DLS_INTERFACE_PROP_DISPLAY_NAME, prop_t);
187 g_hash_table_insert(p_map, "dc:title", DLS_INTERFACE_PROP_DISPLAY_NAME);
188
189 /* dlna:dlnaManaged */
190 prop_t = prv_prop_map_new("dlna:dlnaManaged",
191 DLS_UPNP_MASK_PROP_DLNA_MANAGED,
192 TRUE, FALSE, FALSE);
193 g_hash_table_insert(f_map, DLS_INTERFACE_PROP_DLNA_MANAGED, prop_t);
194 g_hash_table_insert(p_map, "dlna:dlnaManaged",
195 DLS_INTERFACE_PROP_DLNA_MANAGED);
196
197 /* res */
198 /* res - RES */
199 prop_t = prv_prop_map_new("res",
200 DLS_UPNP_MASK_PROP_RESOURCES,
201 TRUE, FALSE, FALSE);
202 g_hash_table_insert(f_map, DLS_INTERFACE_PROP_RESOURCES, prop_t);
203
204 /* res - URL */
205 prop_t = prv_prop_map_new("res",
206 DLS_UPNP_MASK_PROP_URL,
207 TRUE, FALSE, FALSE);
208 g_hash_table_insert(f_map, DLS_INTERFACE_PROP_URL, prop_t);
209
210 /* res - URLS */
211 prop_t = prv_prop_map_new("res",
212 DLS_UPNP_MASK_PROP_URLS,
213 TRUE, FALSE, FALSE);
214 g_hash_table_insert(f_map, DLS_INTERFACE_PROP_URLS, prop_t);
215
216 /* res@bitrate */
217 prop_t = prv_prop_map_new("res@bitrate",
218 DLS_UPNP_MASK_PROP_BITRATE,
219 TRUE, TRUE, FALSE);
220 g_hash_table_insert(f_map, DLS_INTERFACE_PROP_BITRATE, prop_t);
221 g_hash_table_insert(p_map, "res@bitrate", DLS_INTERFACE_PROP_BITRATE);
222
223 /* res@bitsPerSample */
224 prop_t = prv_prop_map_new("res@bitsPerSample",
225 DLS_UPNP_MASK_PROP_BITS_PER_SAMPLE,
226 TRUE, TRUE, FALSE);
227 g_hash_table_insert(f_map, DLS_INTERFACE_PROP_BITS_PER_SAMPLE, prop_t);
228 g_hash_table_insert(p_map, "res@bitsPerSample",
229 DLS_INTERFACE_PROP_BITS_PER_SAMPLE);
230
231 /* res@colorDepth */
232 prop_t = prv_prop_map_new("res@colorDepth",
233 DLS_UPNP_MASK_PROP_COLOR_DEPTH,
234 TRUE, TRUE, FALSE);
235 g_hash_table_insert(f_map, DLS_INTERFACE_PROP_COLOR_DEPTH, prop_t);
236 g_hash_table_insert(p_map, "res@colorDepth",
237 DLS_INTERFACE_PROP_COLOR_DEPTH);
238
239 /* res@duration */
240 prop_t = prv_prop_map_new("res@duration",
241 DLS_UPNP_MASK_PROP_DURATION,
242 TRUE, TRUE, FALSE);
243 g_hash_table_insert(f_map, DLS_INTERFACE_PROP_DURATION, prop_t);
244 g_hash_table_insert(p_map, "res@duration",
245 DLS_INTERFACE_PROP_DURATION);
246
247 /* res@protocolInfo */
248 /* res@protocolInfo - DLNA PROFILE*/
249 prop_t = prv_prop_map_new("res@protocolInfo",
250 DLS_UPNP_MASK_PROP_DLNA_PROFILE,
251 TRUE, FALSE, FALSE);
252 g_hash_table_insert(f_map, DLS_INTERFACE_PROP_DLNA_PROFILE, prop_t);
253
254 /* res@protocolInfo - MIME TYPES*/
255 prop_t = prv_prop_map_new("res@protocolInfo",
256 DLS_UPNP_MASK_PROP_MIME_TYPE,
257 TRUE, FALSE, FALSE);
258 g_hash_table_insert(f_map, DLS_INTERFACE_PROP_MIME_TYPE, prop_t);
259
260 /* res@resolution */
261 /* res@resolution - HEIGH */
262 prop_t = prv_prop_map_new("res@resolution",
263 DLS_UPNP_MASK_PROP_HEIGHT,
264 TRUE, FALSE, FALSE);
265 g_hash_table_insert(f_map, DLS_INTERFACE_PROP_HEIGHT, prop_t);
266
267 /* res@resolution - WIDTH */
268 prop_t = prv_prop_map_new("res@resolution",
269 DLS_UPNP_MASK_PROP_WIDTH,
270 TRUE, FALSE, FALSE);
271 g_hash_table_insert(f_map, DLS_INTERFACE_PROP_WIDTH, prop_t);
272
273 /* res@sampleFrequency */
274 prop_t = prv_prop_map_new("res@sampleFrequency",
275 DLS_UPNP_MASK_PROP_SAMPLE_RATE,
276 TRUE, TRUE, FALSE);
277 g_hash_table_insert(f_map, DLS_INTERFACE_PROP_SAMPLE_RATE, prop_t);
278 g_hash_table_insert(p_map, "res@sampleFrequency",
279 DLS_INTERFACE_PROP_SAMPLE_RATE);
280
281 /* res@size */
282 prop_t = prv_prop_map_new("res@size",
283 DLS_UPNP_MASK_PROP_SIZE,
284 TRUE, TRUE, FALSE);
285 g_hash_table_insert(f_map, DLS_INTERFACE_PROP_SIZE, prop_t);
286 g_hash_table_insert(p_map, "res@size", DLS_INTERFACE_PROP_SIZE);
287
288 /* res@updateCount */
289 prop_t = prv_prop_map_new("res@updateCount",
290 DLS_UPNP_MASK_PROP_UPDATE_COUNT,
291 TRUE, TRUE, FALSE);
292 g_hash_table_insert(f_map, DLS_INTERFACE_PROP_UPDATE_COUNT, prop_t);
293 g_hash_table_insert(p_map, "res@updateCount",
294 DLS_INTERFACE_PROP_UPDATE_COUNT);
295
296 /* upnp:album */
297 prop_t = prv_prop_map_new("upnp:album",
298 DLS_UPNP_MASK_PROP_ALBUM,
299 TRUE, TRUE, TRUE);
300 g_hash_table_insert(f_map, DLS_INTERFACE_PROP_ALBUM, prop_t);
301 g_hash_table_insert(p_map, "upnp:album", DLS_INTERFACE_PROP_ALBUM);
302
303 /* upnp:albumArtURI */
304 prop_t = prv_prop_map_new("upnp:albumArtURI",
305 DLS_UPNP_MASK_PROP_ALBUM_ART_URL,
306 TRUE, TRUE, FALSE);
307 g_hash_table_insert(f_map, DLS_INTERFACE_PROP_ALBUM_ART_URL, prop_t);
308 g_hash_table_insert(p_map, "upnp:albumArtURI",
309 DLS_INTERFACE_PROP_ALBUM_ART_URL);
310
311 /* upnp:artist */
312 /* upnp:artist - ARTIST*/
313 prop_t = prv_prop_map_new("upnp:artist",
314 DLS_UPNP_MASK_PROP_ARTIST,
315 TRUE, TRUE, FALSE);
316 g_hash_table_insert(f_map, DLS_INTERFACE_PROP_ARTIST, prop_t);
317 g_hash_table_insert(p_map, "upnp:artist", DLS_INTERFACE_PROP_ARTIST);
318
319 /* upnp:artist - ARTISTS*/
320 prop_t = prv_prop_map_new("upnp:artist",
321 DLS_UPNP_MASK_PROP_ARTISTS,
322 TRUE, FALSE, TRUE);
323 g_hash_table_insert(f_map, DLS_INTERFACE_PROP_ARTISTS, prop_t);
324
325 /* upnp:class */
326 prop_t = prv_prop_map_new("upnp:class",
327 DLS_UPNP_MASK_PROP_TYPE,
328 FALSE, TRUE, TRUE);
329 g_hash_table_insert(f_map, DLS_INTERFACE_PROP_TYPE, prop_t);
330 g_hash_table_insert(p_map, "upnp:class", DLS_INTERFACE_PROP_TYPE);
331
332 /* upnp:containerUpdateID */
333 prop_t = prv_prop_map_new("upnp:containerUpdateID",
334 DLS_UPNP_MASK_PROP_CONTAINER_UPDATE_ID,
335 TRUE, TRUE, FALSE);
336 g_hash_table_insert(f_map, DLS_INTERFACE_PROP_CONTAINER_UPDATE_ID,
337 prop_t);
338 g_hash_table_insert(p_map, "upnp:containerUpdateID",
339 DLS_INTERFACE_PROP_CONTAINER_UPDATE_ID);
340
341 /* upnp:createClass */
342 prop_t = prv_prop_map_new("upnp:createClass",
343 DLS_UPNP_MASK_PROP_CREATE_CLASSES,
344 TRUE, FALSE, FALSE);
345 g_hash_table_insert(f_map, DLS_INTERFACE_PROP_CREATE_CLASSES, prop_t);
346
347 /* upnp:genre */
348 prop_t = prv_prop_map_new("upnp:genre",
349 DLS_UPNP_MASK_PROP_GENRE,
350 TRUE, TRUE, FALSE);
351 g_hash_table_insert(f_map, DLS_INTERFACE_PROP_GENRE, prop_t);
352 g_hash_table_insert(p_map, "upnp:genre", DLS_INTERFACE_PROP_GENRE);
353
354 /* upnp:objectUpdateID */
355 prop_t = prv_prop_map_new("upnp:objectUpdateID",
356 DLS_UPNP_MASK_PROP_OBJECT_UPDATE_ID,
357 TRUE, TRUE, FALSE);
358 g_hash_table_insert(f_map, DLS_INTERFACE_PROP_OBJECT_UPDATE_ID, prop_t);
359 g_hash_table_insert(p_map, "upnp:objectUpdateID",
360 DLS_INTERFACE_PROP_OBJECT_UPDATE_ID);
361
362 /* upnp:originalTrackNumber */
363 prop_t = prv_prop_map_new("upnp:originalTrackNumber",
364 DLS_UPNP_MASK_PROP_TRACK_NUMBER,
365 TRUE, TRUE, TRUE);
366 g_hash_table_insert(f_map, DLS_INTERFACE_PROP_TRACK_NUMBER, prop_t);
367 g_hash_table_insert(p_map, "upnp:originalTrackNumber",
368 DLS_INTERFACE_PROP_TRACK_NUMBER);
369
370 /* upnp:totalDeletedChildCount */
371 prop_t = prv_prop_map_new("upnp:totalDeletedChildCount",
372 DLS_UPNP_MASK_PROP_TOTAL_DELETED_CHILD_COUNT,
373 TRUE, TRUE, FALSE);
374 g_hash_table_insert(f_map, DLS_INTERFACE_PROP_TOTAL_DELETED_CHILD_COUNT,
375 prop_t);
376 g_hash_table_insert(p_map, "upnp:totalDeletedChildCount",
377 DLS_INTERFACE_PROP_TOTAL_DELETED_CHILD_COUNT);
378
379 *filter_map = f_map;
380 *property_map = p_map;
381 }
382
383 static gchar *prv_compute_upnp_filter(GHashTable *upnp_props)
384 {
385 gpointer key;
386 GString *str;
387 GHashTableIter iter;
388
389 str = g_string_new("");
390 g_hash_table_iter_init(&iter, upnp_props);
391 if (g_hash_table_iter_next(&iter, &key, NULL)) {
392 g_string_append(str, (const gchar *)key);
393 while (g_hash_table_iter_next(&iter, &key, NULL)) {
394 g_string_append(str, ",");
395 g_string_append(str, (const gchar *)key);
396 }
397 }
398
399 return g_string_free(str, FALSE);
400 }
401
402 static dls_upnp_prop_mask prv_parse_filter_list(GHashTable *filter_map,
403 GVariant *filter,
404 gchar **upnp_filter)
405 {
406 GVariantIter viter;
407 const gchar *prop;
408 dls_prop_map_t *prop_map;
409 GHashTable *upnp_props;
410 dls_upnp_prop_mask mask = 0;
411
412 upnp_props = g_hash_table_new_full(g_str_hash, g_str_equal,
413 NULL, NULL);
414 (void) g_variant_iter_init(&viter, filter);
415
416 while (g_variant_iter_next(&viter, "&s", &prop)) {
417 prop_map = g_hash_table_lookup(filter_map, prop);
418 if (!prop_map)
419 continue;
420
421 mask |= prop_map->type;
422
423 if (!prop_map->filter)
424 continue;
425
426 g_hash_table_insert(upnp_props,
427 (gpointer) prop_map->upnp_prop_name, NULL);
428 }
429
430 *upnp_filter = prv_compute_upnp_filter(upnp_props);
431 g_hash_table_unref(upnp_props);
432
433 return mask;
434 }
435
436 dls_upnp_prop_mask dls_props_parse_filter(GHashTable *filter_map,
437 GVariant *filter,
438 gchar **upnp_filter)
439 {
440 gchar *str;
441 gboolean parse_filter = TRUE;
442 dls_upnp_prop_mask mask;
443
444 if (g_variant_n_children(filter) == 1) {
445 g_variant_get_child(filter, 0, "&s", &str);
446 if (!strcmp(str, "*"))
447 parse_filter = FALSE;
448 }
449
450 if (parse_filter) {
451 mask = prv_parse_filter_list(filter_map, filter, upnp_filter);
452 } else {
453 mask = DLS_UPNP_MASK_ALL_PROPS;
454 *upnp_filter = g_strdup("*");
455 }
456
457 return mask;
458 }
459
460 gboolean dls_props_parse_update_filter(GHashTable *filter_map,
461 GVariant *to_add_update,
462 GVariant *to_delete,
463 dls_upnp_prop_mask *mask,
464 gchar **upnp_filter)
465 {
466 GVariantIter viter;
467 const gchar *prop;
468 GVariant *value;
469 dls_prop_map_t *prop_map;
470 GHashTable *upnp_props;
471 gboolean retval = FALSE;
472
473 *mask = 0;
474
475 upnp_props = g_hash_table_new_full(g_str_hash, g_str_equal,
476 NULL, NULL);
477
478 (void) g_variant_iter_init(&viter, to_add_update);
479
480 while (g_variant_iter_next(&viter, "{&sv}", &prop, &value)) {
481 DLEYNA_LOG_DEBUG("to_add_update = %s", prop);
482
483 prop_map = g_hash_table_lookup(filter_map, prop);
484
485 if ((!prop_map) || (!prop_map->updateable))
486 goto on_error;
487
488 *mask |= prop_map->type;
489
490 if (!prop_map->filter)
491 continue;
492
493 g_hash_table_insert(upnp_props,
494 (gpointer) prop_map->upnp_prop_name, NULL);
495 }
496
497 (void) g_variant_iter_init(&viter, to_delete);
498
499 while (g_variant_iter_next(&viter, "&s", &prop)) {
500 DLEYNA_LOG_DEBUG("to_delete = %s", prop);
501
502 prop_map = g_hash_table_lookup(filter_map, prop);
503
504 if ((!prop_map) || (!prop_map->updateable) ||
505 (*mask & prop_map->type) != 0)
506 goto on_error;
507
508 *mask |= prop_map->type;
509
510 if (!prop_map->filter)
511 continue;
512
513 g_hash_table_insert(upnp_props,
514 (gpointer) prop_map->upnp_prop_name, NULL);
515 }
516
517 *upnp_filter = prv_compute_upnp_filter(upnp_props);
518
519 retval = TRUE;
520
521 on_error:
522
523 g_hash_table_unref(upnp_props);
524
525 return retval;
526 }
527
528 static void prv_add_string_prop(GVariantBuilder *vb, const gchar *key,
529 const gchar *value)
530 {
531 if (value) {
532 DLEYNA_LOG_DEBUG("Prop %s = %s", key, value);
533
534 g_variant_builder_add(vb, "{sv}", key,
535 g_variant_new_string(value));
536 }
537 }
538
539 static void prv_add_strv_prop(GVariantBuilder *vb, const gchar *key,
540 const gchar **value, unsigned int len)
541 {
542 if (len > 0)
543 g_variant_builder_add(vb, "{sv}", key,
544 g_variant_new_strv(value, len));
545 }
546
547 static void prv_add_path_prop(GVariantBuilder *vb, const gchar *key,
548 const gchar *value)
549 {
550 if (value) {
551 DLEYNA_LOG_DEBUG("Prop %s = %s", key, value);
552
553 g_variant_builder_add(vb, "{sv}", key,
554 g_variant_new_object_path(value));
555 }
556 }
557
558 static void prv_add_uint_prop(GVariantBuilder *vb, const gchar *key,
559 unsigned int value)
560 {
561 DLEYNA_LOG_DEBUG("Prop %s = %u", key, value);
562
563 g_variant_builder_add(vb, "{sv}", key, g_variant_new_uint32(value));
564 }
565
566 static void prv_add_int_prop(GVariantBuilder *vb, const gchar *key,
567 int value)
568 {
569 if (value != -1)
570 g_variant_builder_add(vb, "{sv}", key,
571 g_variant_new_int32(value));
572 }
573
574 static void prv_add_variant_prop(GVariantBuilder *vb, const gchar *key,
575 GVariant *prop)
576 {
577 if (prop)
578 g_variant_builder_add(vb, "{sv}", key, prop);
579 }
580
581 void dls_props_add_child_count(GVariantBuilder *item_vb, gint value)
582 {
583 prv_add_int_prop(item_vb, DLS_INTERFACE_PROP_CHILD_COUNT, value);
584 }
585
586 static void prv_add_bool_prop(GVariantBuilder *vb, const gchar *key,
587 gboolean value)
588 {
589 DLEYNA_LOG_DEBUG("Prop %s = %u", key, value);
590
591 g_variant_builder_add(vb, "{sv}", key, g_variant_new_boolean(value));
592 }
593
594 static void prv_add_int64_prop(GVariantBuilder *vb, const gchar *key,
595 gint64 value)
596 {
597 if (value != -1) {
598 DLEYNA_LOG_DEBUG("Prop %s = %"G_GINT64_FORMAT, key, value);
599
600 g_variant_builder_add(vb, "{sv}", key,
601 g_variant_new_int64(value));
602 }
603 }
604
605 static void prv_add_list_dlna_str(gpointer data, gpointer user_data)
606 {
607 GVariantBuilder *vb = (GVariantBuilder *)user_data;
608 gchar *cap_str = (gchar *)data;
609 gchar *str;
610 int value = 0;
611
612 if (g_str_has_prefix(cap_str, "srs-rt-retention-period-")) {
613 str = cap_str + strlen("srs-rt-retention-period-");
614 cap_str = "srs-rt-retention-period";
615
616 if (*str) {
617 if (!g_strcmp0(str, "infinity"))
618 value = -1;
619 else
620 value = atoi(str);
621 }
622 }
623
624 prv_add_uint_prop(vb, cap_str, value);
625 }
626
627 static GVariant *prv_add_list_dlna_prop(GList *list)
628 {
629 GVariantBuilder vb;
630
631 g_variant_builder_init(&vb, G_VARIANT_TYPE("a{sv}"));
632
633 g_list_foreach(list, prv_add_list_dlna_str, &vb);
634
635 return g_variant_builder_end(&vb);
636 }
637
638 static void prv_add_list_artists_str(gpointer data, gpointer user_data)
639 {
640 GVariantBuilder *vb = (GVariantBuilder *)user_data;
641 GUPnPDIDLLiteContributor *contributor = data;
642 const char *str;
643
644 str = gupnp_didl_lite_contributor_get_name(contributor);
645 g_variant_builder_add(vb, "s", str);
646 }
647
648 static GVariant *prv_get_artists_prop(GList *list)
649 {
650 GVariantBuilder vb;
651
652 g_variant_builder_init(&vb, G_VARIANT_TYPE("as"));
653 g_list_foreach(list, prv_add_list_artists_str, &vb);
654
655 return g_variant_builder_end(&vb);
656 }
657
658 void dls_props_add_device(GUPnPDeviceInfo *proxy,
659 const dls_device_t *device,
660 GVariantBuilder *vb)
661 {
662 gchar *str;
663 GList *list;
664 GVariant *dlna_caps;
665
666 prv_add_string_prop(vb, DLS_INTERFACE_PROP_LOCATION,
667 gupnp_device_info_get_location(proxy));
668
669 prv_add_string_prop(vb, DLS_INTERFACE_PROP_UDN,
670 gupnp_device_info_get_udn(proxy));
671
672 prv_add_string_prop(vb, DLS_INTERFACE_PROP_DEVICE_TYPE,
673 gupnp_device_info_get_device_type(proxy));
674
675 str = gupnp_device_info_get_friendly_name(proxy);
676 prv_add_string_prop(vb, DLS_INTERFACE_PROP_FRIENDLY_NAME, str);
677 g_free(str);
678
679 str = gupnp_device_info_get_manufacturer(proxy);
680 prv_add_string_prop(vb, DLS_INTERFACE_PROP_MANUFACTURER, str);
681 g_free(str);
682
683 str = gupnp_device_info_get_manufacturer_url(proxy);
684 prv_add_string_prop(vb, DLS_INTERFACE_PROP_MANUFACTURER_URL, str);
685 g_free(str);
686
687 str = gupnp_device_info_get_model_description(proxy);
688 prv_add_string_prop(vb, DLS_INTERFACE_PROP_MODEL_DESCRIPTION, str);
689 g_free(str);
690
691 str = gupnp_device_info_get_model_name(proxy);
692 prv_add_string_prop(vb, DLS_INTERFACE_PROP_MODEL_NAME, str);
693 g_free(str);
694
695 str = gupnp_device_info_get_model_number(proxy);
696 prv_add_string_prop(vb, DLS_INTERFACE_PROP_MODEL_NUMBER, str);
697 g_free(str);
698
699 str = gupnp_device_info_get_model_url(proxy);
700 prv_add_string_prop(vb, DLS_INTERFACE_PROP_MODEL_URL, str);
701 g_free(str);
702
703 str = gupnp_device_info_get_serial_number(proxy);
704 prv_add_string_prop(vb, DLS_INTERFACE_PROP_SERIAL_NUMBER, str);
705 g_free(str);
706
707 str = gupnp_device_info_get_presentation_url(proxy);
708 prv_add_string_prop(vb, DLS_INTERFACE_PROP_PRESENTATION_URL, str);
709 g_free(str);
710
711 str = gupnp_device_info_get_icon_url(proxy, NULL, -1, -1, -1, FALSE,
712 NULL, NULL, NULL, NULL);
713 prv_add_string_prop(vb, DLS_INTERFACE_PROP_ICON_URL, str);
714 g_free(str);
715
716 list = gupnp_device_info_list_dlna_capabilities(proxy);
717 if (list != NULL) {
718 dlna_caps = prv_add_list_dlna_prop(list);
719 g_variant_builder_add(vb, "{sv}",
720 DLS_INTERFACE_PROP_SV_DLNA_CAPABILITIES,
721 dlna_caps);
722 g_list_free_full(list, g_free);
723 }
724
725 if (device->search_caps != NULL)
726 g_variant_builder_add(vb, "{sv}",
727 DLS_INTERFACE_PROP_SV_SEARCH_CAPABILITIES,
728 device->search_caps);
729
730 if (device->sort_caps != NULL)
731 g_variant_builder_add(vb, "{sv}",
732 DLS_INTERFACE_PROP_SV_SORT_CAPABILITIES,
733 device->sort_caps);
734
735 if (device->sort_ext_caps != NULL)
736 g_variant_builder_add(
737 vb, "{sv}",
738 DLS_INTERFACE_PROP_SV_SORT_EXT_CAPABILITIES,
739 device->sort_ext_caps);
740
741 if (device->feature_list != NULL)
742 g_variant_builder_add(vb, "{sv}",
743 DLS_INTERFACE_PROP_SV_FEATURE_LIST,
744 device->feature_list);
745 }
746
747 GVariant *dls_props_get_device_prop(GUPnPDeviceInfo *proxy,
748 const dls_device_t *device,
749 const gchar *prop)
750 {
751 GVariant *dlna_caps = NULL;
752 GVariant *retval = NULL;
753 const gchar *str = NULL;
754 gchar *copy = NULL;
755 GList *list;
756
757 if (!strcmp(DLS_INTERFACE_PROP_LOCATION, prop)) {
758 str = gupnp_device_info_get_location(proxy);
759 } else if (!strcmp(DLS_INTERFACE_PROP_UDN, prop)) {
760 str = gupnp_device_info_get_udn(proxy);
761 } else if (!strcmp(DLS_INTERFACE_PROP_DEVICE_TYPE, prop)) {
762 str = gupnp_device_info_get_device_type(proxy);
763 } else if (!strcmp(DLS_INTERFACE_PROP_FRIENDLY_NAME, prop)) {
764 copy = gupnp_device_info_get_friendly_name(proxy);
765 str = copy;
766 } else if (!strcmp(DLS_INTERFACE_PROP_MANUFACTURER, prop)) {
767 copy = gupnp_device_info_get_manufacturer(proxy);
768 str = copy;
769 } else if (!strcmp(DLS_INTERFACE_PROP_MANUFACTURER_URL, prop)) {
770 copy = gupnp_device_info_get_manufacturer_url(proxy);
771 str = copy;
772 } else if (!strcmp(DLS_INTERFACE_PROP_MODEL_DESCRIPTION, prop)) {
773 copy = gupnp_device_info_get_model_description(proxy);
774 str = copy;
775 } else if (!strcmp(DLS_INTERFACE_PROP_MODEL_NAME, prop)) {
776 copy = gupnp_device_info_get_model_name(proxy);
777 str = copy;
778 } else if (!strcmp(DLS_INTERFACE_PROP_MODEL_NUMBER, prop)) {
779 copy = gupnp_device_info_get_model_number(proxy);
780 str = copy;
781 } else if (!strcmp(DLS_INTERFACE_PROP_MODEL_URL, prop)) {
782 copy = gupnp_device_info_get_model_url(proxy);
783 str = copy;
784 } else if (!strcmp(DLS_INTERFACE_PROP_SERIAL_NUMBER, prop)) {
785 copy = gupnp_device_info_get_serial_number(proxy);
786 str = copy;
787 } else if (!strcmp(DLS_INTERFACE_PROP_PRESENTATION_URL, prop)) {
788 copy = gupnp_device_info_get_presentation_url(proxy);
789 str = copy;
790 } else if (!strcmp(DLS_INTERFACE_PROP_ICON_URL, prop)) {
791 copy = gupnp_device_info_get_icon_url(proxy, NULL,
792 -1, -1, -1, FALSE,
793 NULL, NULL, NULL, NULL);
794 str = copy;
795 } else if (!strcmp(DLS_INTERFACE_PROP_SV_DLNA_CAPABILITIES, prop)) {
796 list = gupnp_device_info_list_dlna_capabilities(proxy);
797 if (list != NULL) {
798 dlna_caps = prv_add_list_dlna_prop(list);
799 g_list_free_full(list, g_free);
800 retval = g_variant_ref_sink(dlna_caps);
801
802 #if DLEYNA_LOG_LEVEL & DLEYNA_LOG_LEVEL_DEBUG
803 copy = g_variant_print(dlna_caps, FALSE);
804 DLEYNA_LOG_DEBUG("Prop %s = %s", prop, copy);
805 #endif
806 }
807 } else if (!strcmp(DLS_INTERFACE_PROP_SV_SEARCH_CAPABILITIES, prop)) {
808 if (device->search_caps != NULL) {
809 retval = g_variant_ref(device->search_caps);
810
811 #if DLEYNA_LOG_LEVEL & DLEYNA_LOG_LEVEL_DEBUG
812 copy = g_variant_print(device->search_caps, FALSE);
813 DLEYNA_LOG_DEBUG("Prop %s = %s", prop, copy);
814 #endif
815 }
816 } else if (!strcmp(DLS_INTERFACE_PROP_SV_SORT_CAPABILITIES, prop)) {
817 if (device->sort_caps != NULL) {
818 retval = g_variant_ref(device->sort_caps);
819
820 #if DLEYNA_LOG_LEVEL & DLEYNA_LOG_LEVEL_DEBUG
821 copy = g_variant_print(device->sort_caps, FALSE);
822 DLEYNA_LOG_DEBUG("Prop %s = %s", prop, copy);
823 #endif
824 }
825 } else if (!strcmp(DLS_INTERFACE_PROP_SV_SORT_EXT_CAPABILITIES, prop)) {
826 if (device->sort_ext_caps != NULL) {
827 retval = g_variant_ref(device->sort_ext_caps);
828
829 #if DLEYNA_LOG_LEVEL & DLEYNA_LOG_LEVEL_DEBUG
830 copy = g_variant_print(device->sort_ext_caps, FALSE);
831 DLEYNA_LOG_DEBUG("Prop %s = %s", prop, copy);
832 #endif
833 }
834 } else if (!strcmp(DLS_INTERFACE_PROP_SV_FEATURE_LIST, prop)) {
835 if (device->feature_list != NULL) {
836 retval = g_variant_ref(device->feature_list);
837
838 #if DLEYNA_LOG_LEVEL & DLEYNA_LOG_LEVEL_DEBUG
839 copy = g_variant_print(device->feature_list, FALSE);
840 DLEYNA_LOG_DEBUG("Prop %s = %s", prop, copy);
841 #endif
842 }
843 }
844
845 if (!retval) {
846 if (str) {
847 DLEYNA_LOG_DEBUG("Prop %s = %s", prop, str);
848
849 retval = g_variant_ref_sink(g_variant_new_string(str));
850 }
851 #if DLEYNA_LOG_LEVEL & DLEYNA_LOG_LEVEL_WARNING
852 else
853 DLEYNA_LOG_WARNING("Property %s not defined", prop);
854 #endif
855 }
856
857 g_free(copy);
858
859 return retval;
860 }
861
862 static GUPnPDIDLLiteResource *prv_match_resource(GUPnPDIDLLiteResource *res,
863 gchar **pi_str_array)
864 {
865 GUPnPDIDLLiteResource *retval = NULL;
866 GUPnPProtocolInfo *res_pi;
867 GUPnPProtocolInfo *pi;
868 unsigned int i;
869 gboolean match;
870
871 if (!pi_str_array) {
872 retval = res;
873 goto done;
874 }
875
876 res_pi = gupnp_didl_lite_resource_get_protocol_info(res);
877 if (!res_pi)
878 goto done;
879
880 for (i = 0; pi_str_array[i]; ++i) {
881 pi = gupnp_protocol_info_new_from_string(pi_str_array[i],
882 NULL);
883 if (!pi)
884 continue;
885 match = gupnp_protocol_info_is_compatible(pi, res_pi);
886 g_object_unref(pi);
887 if (match) {
888 retval = res;
889 break;
890 }
891 }
892 done:
893
894 return retval;
895 }
896
897 static GUPnPDIDLLiteResource *prv_get_matching_resource
898 (GUPnPDIDLLiteObject *object, const gchar *protocol_info)
899 {
900 GUPnPDIDLLiteResource *retval = NULL;
901 GUPnPDIDLLiteResource *res;
902 GList *resources;
903 GList *ptr;
904 gchar **pi_str_array = NULL;
905
906 if (protocol_info)
907 pi_str_array = g_strsplit(protocol_info, ",", 0);
908
909 resources = gupnp_didl_lite_object_get_resources(object);
910 ptr = resources;
911
912 while (ptr) {
913 res = ptr->data;
914 if (!retval) {
915 retval = prv_match_resource(res, pi_str_array);
916 if (!retval)
917 g_object_unref(res);
918 } else {
919 g_object_unref(res);
920 }
921
922 ptr = ptr->next;
923 }
924
925 g_list_free(resources);
926 if (pi_str_array)
927 g_strfreev(pi_str_array);
928
929 return retval;
930 }
931
932 static void prv_parse_resources(GVariantBuilder *item_vb,
933 GUPnPDIDLLiteResource *res,
934 dls_upnp_prop_mask filter_mask)
935 {
936 GUPnPProtocolInfo *protocol_info;
937 int int_val;
938 gint64 int64_val;
939 const char *str_val;
940 guint uint_val;
941
942 if (filter_mask & DLS_UPNP_MASK_PROP_SIZE) {
943 int64_val = gupnp_didl_lite_resource_get_size64(res);
944 prv_add_int64_prop(item_vb, DLS_INTERFACE_PROP_SIZE, int64_val);
945 }
946
947 if (filter_mask & DLS_UPNP_MASK_PROP_BITRATE) {
948 int_val = gupnp_didl_lite_resource_get_bitrate(res);
949 prv_add_int_prop(item_vb, DLS_INTERFACE_PROP_BITRATE, int_val);
950 }
951
952 if (filter_mask & DLS_UPNP_MASK_PROP_SAMPLE_RATE) {
953 int_val = gupnp_didl_lite_resource_get_sample_freq(res);
954 prv_add_int_prop(item_vb, DLS_INTERFACE_PROP_SAMPLE_RATE,
955 int_val);
956 }
957
958 if (filter_mask & DLS_UPNP_MASK_PROP_BITS_PER_SAMPLE) {
959 int_val = gupnp_didl_lite_resource_get_bits_per_sample(res);
960 prv_add_int_prop(item_vb, DLS_INTERFACE_PROP_BITS_PER_SAMPLE,
961 int_val);
962 }
963
964 if (filter_mask & DLS_UPNP_MASK_PROP_DURATION) {
965 int_val = (int) gupnp_didl_lite_resource_get_duration(res);
966 prv_add_int_prop(item_vb, DLS_INTERFACE_PROP_DURATION, int_val);
967 }
968
969 if (filter_mask & DLS_UPNP_MASK_PROP_WIDTH) {
970 int_val = (int) gupnp_didl_lite_resource_get_width(res);
971 prv_add_int_prop(item_vb, DLS_INTERFACE_PROP_WIDTH, int_val);
972 }
973
974 if (filter_mask & DLS_UPNP_MASK_PROP_HEIGHT) {
975 int_val = (int) gupnp_didl_lite_resource_get_height(res);
976 prv_add_int_prop(item_vb, DLS_INTERFACE_PROP_HEIGHT, int_val);
977 }
978
979 if (filter_mask & DLS_UPNP_MASK_PROP_COLOR_DEPTH) {
980 int_val = (int) gupnp_didl_lite_resource_get_color_depth(res);
981 prv_add_int_prop(item_vb, DLS_INTERFACE_PROP_COLOR_DEPTH,
982 int_val);
983 }
984
985 if (filter_mask & DLS_UPNP_MASK_PROP_UPDATE_COUNT) {
986 uint_val = gupnp_didl_lite_resource_get_update_count(res);
987 prv_add_uint_prop(item_vb, DLS_INTERFACE_PROP_UPDATE_COUNT,
988 uint_val);
989 }
990
991 protocol_info = gupnp_didl_lite_resource_get_protocol_info(res);
992
993 if (filter_mask & DLS_UPNP_MASK_PROP_DLNA_PROFILE) {
994 str_val = gupnp_protocol_info_get_dlna_profile(protocol_info);
995 prv_add_string_prop(item_vb, DLS_INTERFACE_PROP_DLNA_PROFILE,
996 str_val);
997 }
998
999 if (filter_mask & DLS_UPNP_MASK_PROP_MIME_TYPE) {
1000 str_val = gupnp_protocol_info_get_mime_type(protocol_info);
1001 prv_add_string_prop(item_vb, DLS_INTERFACE_PROP_MIME_TYPE,
1002 str_val);
1003 }
1004 }
1005
1006 static GVariant *prv_compute_create_classes(GUPnPDIDLLiteContainer *container)
1007 {
1008 GVariantBuilder create_classes_vb;
1009 GList *create_classes;
1010 GList *ptr;
1011 GUPnPDIDLLiteCreateClass *create_class;
1012 const char *content;
1013 gboolean inc_derived;
1014
1015 g_variant_builder_init(&create_classes_vb, G_VARIANT_TYPE("a(sb)"));
1016
1017 create_classes = gupnp_didl_lite_container_get_create_classes_full(
1018 container);
1019 ptr = create_classes;
1020 while (ptr) {
1021 create_class = ptr->data;
1022 content = gupnp_didl_lite_create_class_get_content(
1023 create_class);
1024 inc_derived = gupnp_didl_lite_create_class_get_include_derived(
1025 create_class);
1026 g_variant_builder_add(&create_classes_vb,
1027 "(sb)", content, inc_derived);
1028 g_object_unref(ptr->data);
1029 ptr = g_list_next(ptr);
1030 }
1031 g_list_free(create_classes);
1032
1033 return g_variant_builder_end(&create_classes_vb);
1034 }
1035
1036 const gchar *dls_props_media_spec_to_upnp_class(const gchar *m2spec_class)
1037 {
1038 const gchar *retval = NULL;
1039
1040 if (!strcmp(m2spec_class, gMediaSpec2AlbumPhoto))
1041 retval = gUPnPPhotoAlbum;
1042 else if (!strcmp(m2spec_class, gMediaSpec2AlbumMusic))
1043 retval = gUPnPMusicAlbum;
1044 else if (!strcmp(m2spec_class, gMediaSpec2Album))
1045 retval = gUPnPAlbum;
1046 else if (!strcmp(m2spec_class, gMediaSpec2PersonMusicArtist))
1047 retval = gUPnPMusicArtist;
1048 else if (!strcmp(m2spec_class, gMediaSpec2Person))
1049 retval = gUPnPPerson;
1050 else if (!strcmp(m2spec_class, gMediaSpec2GenreMovie))
1051 retval = gUPnPMovieGenre;
1052 else if (!strcmp(m2spec_class, gMediaSpec2GenreMusic))
1053 retval = gUPnPMusicGenre;
1054 else if (!strcmp(m2spec_class, gMediaSpec2Genre))
1055 retval = gUPnPGenre;
1056 else if (!strcmp(m2spec_class, gMediaSpec2Container))
1057 retval = gUPnPContainer;
1058 else if (!strcmp(m2spec_class, gMediaSpec2AudioMusic))
1059 retval = gUPnPMusicTrack;
1060 else if (!strcmp(m2spec_class, gMediaSpec2AudioBroadcast))
1061 retval = gUPnPAudioBroadcast;
1062 else if (!strcmp(m2spec_class, gMediaSpec2AudioBook))
1063 retval = gUPnPAudioBook;
1064 else if (!strcmp(m2spec_class, gMediaSpec2Audio))
1065 retval = gUPnPAudioItem;
1066 else if (!strcmp(m2spec_class, gMediaSpec2VideoMovie))
1067 retval = gUPnPMovie;
1068 else if (!strcmp(m2spec_class, gMediaSpec2VideoMusicClip))
1069 retval = gUPnPMusicVideoClip;
1070 else if (!strcmp(m2spec_class, gMediaSpec2VideoBroadcast))
1071 retval = gUPnPVideoBroadcast;
1072 else if (!strcmp(m2spec_class, gMediaSpec2Video))
1073 retval = gUPnPVideoItem;
1074 else if (!strcmp(m2spec_class, gMediaSpec2ImagePhoto))
1075 retval = gUPnPPhoto;
1076 else if (!strcmp(m2spec_class, gMediaSpec2Image))
1077 retval = gUPnPImageItem;
1078 else if (!strcmp(m2spec_class, gMediaSpec2Playlist))
1079 retval = gUPnPPlaylistItem;
1080 else if (!strcmp(m2spec_class, gMediaSpec2Item))
1081 retval = gUPnPItem;
1082
1083 return retval;
1084 }
1085
1086 const gchar *dls_props_upnp_class_to_media_spec(const gchar *upnp_class)
1087 {
1088 const gchar *retval = NULL;
1089 const gchar *ptr;
1090
1091 if (!strncmp(upnp_class, gUPnPAlbum, gUPnPAlbumLen)) {
1092 ptr = upnp_class + gUPnPAlbumLen;
1093 if (!strcmp(ptr, ".photoAlbum"))
1094 retval = gMediaSpec2AlbumPhoto;
1095 else if (!strcmp(ptr, ".musicAlbum"))
1096 retval = gMediaSpec2AlbumMusic;
1097 else
1098 retval = gMediaSpec2Album;
1099 } else if (!strncmp(upnp_class, gUPnPPerson, gUPnPPersonLen)) {
1100 ptr = upnp_class + gUPnPPersonLen;
1101 if (!strcmp(ptr, ".musicArtist"))
1102 retval = gMediaSpec2PersonMusicArtist;
1103 else
1104 retval = gMediaSpec2Person;
1105 } else if (!strncmp(upnp_class, gUPnPGenre, gUPnPGenreLen)) {
1106 ptr = upnp_class + gUPnPGenreLen;
1107 if (!strcmp(ptr, ".movieGenre"))
1108 retval = gMediaSpec2GenreMovie;
1109 else if (!strcmp(ptr, ".musicGenre"))
1110 retval = gMediaSpec2GenreMusic;
1111 else
1112 retval = gMediaSpec2Genre;
1113 } else if (!strncmp(upnp_class, gUPnPContainer, gUPnPContainerLen)) {
1114 ptr = upnp_class + gUPnPContainerLen;
1115 if (!*ptr || *ptr == '.')
1116 retval = gMediaSpec2Container;
1117 } else if (!strncmp(upnp_class, gUPnPAudioItem, gUPnPAudioItemLen)) {
1118 ptr = upnp_class + gUPnPAudioItemLen;
1119 if (!strcmp(ptr, ".musicTrack"))
1120 retval = gMediaSpec2AudioMusic;
1121 else if (!strcmp(ptr, ".audioBroadcast"))
1122 retval = gMediaSpec2AudioBroadcast;
1123 else if (!strcmp(ptr, ".audioBook"))
1124 retval = gMediaSpec2AudioBook;
1125 else
1126 retval = gMediaSpec2Audio;
1127 } else if (!strncmp(upnp_class, gUPnPVideoItem, gUPnPVideoItemLen)) {
1128 ptr = upnp_class + gUPnPVideoItemLen;
1129 if (!strcmp(ptr, ".movie"))
1130 retval = gMediaSpec2VideoMovie;
1131 else if (!strcmp(ptr, ".musicVideoClip"))
1132 retval = gMediaSpec2VideoMusicClip;
1133 else if (!strcmp(ptr, ".videoBroadcast"))
1134 retval = gMediaSpec2VideoBroadcast;
1135 else
1136 retval = gMediaSpec2Video;
1137 } else if (!strncmp(upnp_class, gUPnPImageItem, gUPnPImageItemLen)) {
1138 ptr = upnp_class + gUPnPImageItemLen;
1139 if (!strcmp(ptr, ".photo"))
1140 retval = gMediaSpec2ImagePhoto;
1141 else
1142 retval = gMediaSpec2Image;
1143 } else if (!strncmp(upnp_class, gUPnPPlaylistItem,
1144 gUPnPPlaylistItemLen)) {
1145 retval = gMediaSpec2Playlist;
1146 } else if (!strncmp(upnp_class, gUPnPItem, gUPnPItemLen)) {
1147 ptr = upnp_class + gUPnPItemLen;
1148 if (!*ptr || *ptr == '.')
1149 retval = gMediaSpec2Item;
1150 }
1151
1152 return retval;
1153 }
1154
1155 static GVariant *prv_props_get_dlna_managed_dict(GUPnPOCMFlags flags)
1156 {
1157 GVariantBuilder builder;
1158 gboolean managed;
1159
1160 g_variant_builder_init(&builder, G_VARIANT_TYPE("a{sb}"));
1161
1162 managed = (flags & GUPNP_OCM_FLAGS_UPLOAD);
1163 g_variant_builder_add(&builder, "{sb}", "Upload", managed);
1164
1165 managed = (flags & GUPNP_OCM_FLAGS_CREATE_CONTAINER);
1166 g_variant_builder_add(&builder, "{sb}", "CreateContainer", managed);
1167
1168 managed = (flags & GUPNP_OCM_FLAGS_DESTROYABLE);
1169 g_variant_builder_add(&builder, "{sb}", "Delete", managed);
1170
1171 managed = (flags & GUPNP_OCM_FLAGS_UPLOAD_DESTROYABLE);
1172 g_variant_builder_add(&builder, "{sb}", "UploadDelete", managed);
1173
1174 managed = (flags & GUPNP_OCM_FLAGS_CHANGE_METADATA);
1175 g_variant_builder_add(&builder, "{sb}", "ChangeMeta", managed);
1176
1177 return g_variant_builder_end(&builder);
1178 }
1179
1180 gboolean dls_props_add_object(GVariantBuilder *item_vb,
1181 GUPnPDIDLLiteObject *object,
1182 const char *root_path,
1183 const gchar *parent_path,
1184 dls_upnp_prop_mask filter_mask)
1185 {
1186 gchar *path = NULL;
1187 const char *id;
1188 const char *title;
1189 const char *creator;
1190 const char *upnp_class;
1191 const char *media_spec_type;
1192 gboolean retval = FALSE;
1193 gboolean rest;
1194 GUPnPOCMFlags flags;
1195 guint uint_val;
1196
1197 id = gupnp_didl_lite_object_get_id(object);
1198 if (!id)
1199 goto on_error;
1200
1201 upnp_class = gupnp_didl_lite_object_get_upnp_class(object);
1202 media_spec_type = dls_props_upnp_class_to_media_spec(upnp_class);
1203
1204 if (!media_spec_type)
1205 goto on_error;
1206
1207 title = gupnp_didl_lite_object_get_title(object);
1208 creator = gupnp_didl_lite_object_get_creator(object);
1209 rest = gupnp_didl_lite_object_get_restricted(object);
1210 path = dls_path_from_id(root_path, id);
1211
1212 if (filter_mask & DLS_UPNP_MASK_PROP_DISPLAY_NAME)
1213 prv_add_string_prop(item_vb, DLS_INTERFACE_PROP_DISPLAY_NAME,
1214 title);
1215
1216 if (filter_mask & DLS_UPNP_MASK_PROP_CREATOR)
1217 prv_add_string_prop(item_vb, DLS_INTERFACE_PROP_CREATOR,
1218 creator);
1219
1220 if (filter_mask & DLS_UPNP_MASK_PROP_PATH)
1221 prv_add_path_prop(item_vb, DLS_INTERFACE_PROP_PATH, path);
1222
1223 if (filter_mask & DLS_UPNP_MASK_PROP_PARENT)
1224 prv_add_path_prop(item_vb, DLS_INTERFACE_PROP_PARENT,
1225 parent_path);
1226
1227 if (filter_mask & DLS_UPNP_MASK_PROP_TYPE)
1228 prv_add_string_prop(item_vb, DLS_INTERFACE_PROP_TYPE,
1229 media_spec_type);
1230
1231 if (filter_mask & DLS_UPNP_MASK_PROP_RESTRICTED)
1232 prv_add_bool_prop(item_vb, DLS_INTERFACE_PROP_RESTRICTED, rest);
1233
1234 if (filter_mask & DLS_UPNP_MASK_PROP_DLNA_MANAGED) {
1235 flags = gupnp_didl_lite_object_get_dlna_managed(object);
1236 prv_add_variant_prop(item_vb,
1237 DLS_INTERFACE_PROP_DLNA_MANAGED,
1238 prv_props_get_dlna_managed_dict(flags));
1239 }
1240
1241 if (filter_mask & DLS_UPNP_MASK_PROP_OBJECT_UPDATE_ID) {
1242 uint_val = gupnp_didl_lite_object_get_update_id(object);
1243 prv_add_uint_prop(item_vb, DLS_INTERFACE_PROP_OBJECT_UPDATE_ID,
1244 uint_val);
1245 }
1246
1247 retval = TRUE;
1248
1249 on_error:
1250
1251 g_free(path);
1252
1253 return retval;
1254 }
1255
1256 void dls_props_add_container(GVariantBuilder *item_vb,
1257 GUPnPDIDLLiteContainer *object,
1258 dls_upnp_prop_mask filter_mask,
1259 gboolean *have_child_count)
1260 {
1261 int child_count;
1262 gboolean searchable;
1263 guint uint_val;
1264
1265 *have_child_count = FALSE;
1266 if (filter_mask & DLS_UPNP_MASK_PROP_CHILD_COUNT) {
1267 child_count = gupnp_didl_lite_container_get_child_count(object);
1268 if (child_count >= 0) {
1269 prv_add_uint_prop(item_vb,
1270 DLS_INTERFACE_PROP_CHILD_COUNT,
1271 (unsigned int) child_count);
1272 *have_child_count = TRUE;
1273 }
1274 }
1275
1276 if (filter_mask & DLS_UPNP_MASK_PROP_SEARCHABLE) {
1277 searchable = gupnp_didl_lite_container_get_searchable(object);
1278 prv_add_bool_prop(item_vb, DLS_INTERFACE_PROP_SEARCHABLE,
1279 searchable);
1280 }
1281
1282 if (filter_mask & DLS_UPNP_MASK_PROP_CREATE_CLASSES)
1283 prv_add_variant_prop(item_vb,
1284 DLS_INTERFACE_PROP_CREATE_CLASSES,
1285 prv_compute_create_classes(object));
1286
1287 if (filter_mask & DLS_UPNP_MASK_PROP_CONTAINER_UPDATE_ID) {
1288 uint_val = gupnp_didl_lite_container_get_container_update_id(
1289 object);
1290 prv_add_uint_prop(item_vb,
1291 DLS_INTERFACE_PROP_CONTAINER_UPDATE_ID,
1292 uint_val);
1293 }
1294
1295 if (filter_mask & DLS_UPNP_MASK_PROP_TOTAL_DELETED_CHILD_COUNT) {
1296 uint_val =
1297 gupnp_didl_lite_container_get_total_deleted_child_count(object);
1298 prv_add_uint_prop(item_vb,
1299 DLS_INTERFACE_PROP_TOTAL_DELETED_CHILD_COUNT,
1300 uint_val);
1301 }
1302 }
1303
1304 static GVariant *prv_compute_resources(GUPnPDIDLLiteObject *object,
1305 dls_upnp_prop_mask filter_mask)
1306 {
1307 GUPnPDIDLLiteResource *res = NULL;
1308 GList *resources;
1309 GList *ptr;
1310 GVariantBuilder *res_array_vb;
1311 GVariantBuilder *res_vb;
1312 const char *str_val;
1313 GVariant *retval;
1314
1315 res_array_vb = g_variant_builder_new(G_VARIANT_TYPE("aa{sv}"));
1316
1317 resources = gupnp_didl_lite_object_get_resources(object);
1318 ptr = resources;
1319
1320 while (ptr) {
1321 res = ptr->data;
1322 res_vb = g_variant_builder_new(G_VARIANT_TYPE("a{sv}"));
1323 if (filter_mask & DLS_UPNP_MASK_PROP_URL) {
1324 str_val = gupnp_didl_lite_resource_get_uri(res);
1325 if (str_val)
1326 prv_add_string_prop(res_vb,
1327 DLS_INTERFACE_PROP_URL,
1328 str_val);
1329 }
1330 prv_parse_resources(res_vb, res, filter_mask);
1331 g_variant_builder_add(res_array_vb, "@a{sv}",
1332 g_variant_builder_end(res_vb));
1333 g_variant_builder_unref(res_vb);
1334 g_object_unref(ptr->data);
1335 ptr = g_list_next(ptr);
1336 }
1337 retval = g_variant_builder_end(res_array_vb);
1338 g_variant_builder_unref(res_array_vb);
1339
1340 g_list_free(resources);
1341
1342 return retval;
1343 }
1344
1345 static void prv_add_resources(GVariantBuilder *item_vb,
1346 GUPnPDIDLLiteObject *object,
1347 dls_upnp_prop_mask filter_mask)
1348 {
1349 GVariant *val;
1350
1351 val = prv_compute_resources(object, filter_mask);
1352 g_variant_builder_add(item_vb, "{sv}", DLS_INTERFACE_PROP_RESOURCES,
1353 val);
1354 }
1355
1356 void dls_props_add_item(GVariantBuilder *item_vb,
1357 GUPnPDIDLLiteObject *object,
1358 const gchar *root_path,
1359 dls_upnp_prop_mask filter_mask,
1360 const gchar *protocol_info)
1361 {
1362 int track_number;
1363 GUPnPDIDLLiteResource *res;
1364 const char *str_val;
1365 char *path;
1366 GList *list;
1367
1368 if (filter_mask & DLS_UPNP_MASK_PROP_ARTIST)
1369 prv_add_string_prop(item_vb, DLS_INTERFACE_PROP_ARTIST,
1370 gupnp_didl_lite_object_get_artist(object));
1371
1372 if (filter_mask & DLS_UPNP_MASK_PROP_ARTISTS) {
1373 list = gupnp_didl_lite_object_get_artists(object);
1374 prv_add_variant_prop(item_vb, DLS_INTERFACE_PROP_ARTISTS,
1375 prv_get_artists_prop(list));
1376 g_list_free_full(list, g_object_unref);
1377 }
1378
1379 if (filter_mask & DLS_UPNP_MASK_PROP_ALBUM)
1380 prv_add_string_prop(item_vb, DLS_INTERFACE_PROP_ALBUM,
1381 gupnp_didl_lite_object_get_album(object));
1382
1383 if (filter_mask & DLS_UPNP_MASK_PROP_DATE)
1384 prv_add_string_prop(item_vb, DLS_INTERFACE_PROP_DATE,
1385 gupnp_didl_lite_object_get_date(object));
1386
1387 if (filter_mask & DLS_UPNP_MASK_PROP_GENRE)
1388 prv_add_string_prop(item_vb, DLS_INTERFACE_PROP_GENRE,
1389 gupnp_didl_lite_object_get_genre(object));
1390
1391 if (filter_mask & DLS_UPNP_MASK_PROP_TRACK_NUMBER) {
1392 track_number = gupnp_didl_lite_object_get_track_number(object);
1393 if (track_number >= 0)
1394 prv_add_int_prop(item_vb,
1395 DLS_INTERFACE_PROP_TRACK_NUMBER,
1396 track_number);
1397 }
1398
1399 if (filter_mask & DLS_UPNP_MASK_PROP_ALBUM_ART_URL)
1400 prv_add_string_prop(item_vb, DLS_INTERFACE_PROP_ALBUM_ART_URL,
1401 gupnp_didl_lite_object_get_album_art(
1402 object));
1403
1404 if (filter_mask & DLS_UPNP_MASK_PROP_REFPATH) {
1405 str_val = gupnp_didl_lite_item_get_ref_id(
1406 GUPNP_DIDL_LITE_ITEM(object));
1407 if (str_val != NULL) {
1408 path = dls_path_from_id(root_path, str_val);
1409 prv_add_path_prop(item_vb, DLS_INTERFACE_PROP_REFPATH,
1410 path);
1411 g_free(path);
1412 }
1413 }
1414
1415 res = prv_get_matching_resource(object, protocol_info);
1416 if (res) {
1417 if (filter_mask & DLS_UPNP_MASK_PROP_URLS) {
1418 str_val = gupnp_didl_lite_resource_get_uri(res);
1419 if (str_val)
1420 prv_add_strv_prop(item_vb,
1421 DLS_INTERFACE_PROP_URLS,
1422 &str_val, 1);
1423 }
1424 prv_parse_resources(item_vb, res, filter_mask);
1425 g_object_unref(res);
1426 }
1427
1428 if (filter_mask & DLS_UPNP_MASK_PROP_RESOURCES)
1429 prv_add_resources(item_vb, object, filter_mask);
1430 }
1431
1432 void dls_props_add_resource(GVariantBuilder *item_vb,
1433 GUPnPDIDLLiteObject *object,
1434 dls_upnp_prop_mask filter_mask,
1435 const gchar *protocol_info)
1436 {
1437 GUPnPDIDLLiteResource *res;
1438 const char *str_val;
1439
1440 res = prv_get_matching_resource(object, protocol_info);
1441 if (res) {
1442 if (filter_mask & DLS_UPNP_MASK_PROP_URL) {
1443 str_val = gupnp_didl_lite_resource_get_uri(res);
1444 if (str_val)
1445 prv_add_string_prop(item_vb,
1446 DLS_INTERFACE_PROP_URL,
1447 str_val);
1448 }
1449 prv_parse_resources(item_vb, res, filter_mask);
1450 g_object_unref(res);
1451 }
1452 }
1453
1454
1455 static GVariant *prv_get_resource_property(const gchar *prop,
1456 GUPnPDIDLLiteResource *res)
1457 {
1458 int int_val;
1459 gint64 int64_val;
1460 const char *str_val;
1461 GVariant *retval = NULL;
1462 GUPnPProtocolInfo *protocol_info;
1463
1464 if (!strcmp(prop, DLS_INTERFACE_PROP_DLNA_PROFILE)) {
1465 protocol_info = gupnp_didl_lite_resource_get_protocol_info(res);
1466 if (!protocol_info)
1467 goto on_error;
1468 str_val = gupnp_protocol_info_get_dlna_profile(protocol_info);
1469 if (!str_val)
1470 goto on_error;
1471 retval = g_variant_ref_sink(g_variant_new_string(str_val));
1472 } else if (!strcmp(prop, DLS_INTERFACE_PROP_MIME_TYPE)) {
1473 protocol_info = gupnp_didl_lite_resource_get_protocol_info(res);
1474 if (!protocol_info)
1475 goto on_error;
1476 str_val = gupnp_protocol_info_get_mime_type(protocol_info);
1477 if (!str_val)
1478 goto on_error;
1479 retval = g_variant_ref_sink(g_variant_new_string(str_val));
1480 } else if (!strcmp(prop, DLS_INTERFACE_PROP_SIZE)) {
1481 int64_val = gupnp_didl_lite_resource_get_size64(res);
1482 if (int64_val == -1)
1483 goto on_error;
1484 retval = g_variant_ref_sink(g_variant_new_int64(int64_val));
1485 } else if (!strcmp(prop, DLS_INTERFACE_PROP_DURATION)) {
1486 int_val = (int) gupnp_didl_lite_resource_get_duration(res);
1487 if (int_val == -1)
1488 goto on_error;
1489 retval = g_variant_ref_sink(g_variant_new_int32(int_val));
1490 } else if (!strcmp(prop, DLS_INTERFACE_PROP_BITRATE)) {
1491 int_val = gupnp_didl_lite_resource_get_bitrate(res);
1492 if (int_val == -1)
1493 goto on_error;
1494 retval = g_variant_ref_sink(g_variant_new_int32(int_val));
1495 } else if (!strcmp(prop, DLS_INTERFACE_PROP_SAMPLE_RATE)) {
1496 int_val = gupnp_didl_lite_resource_get_sample_freq(res);
1497 if (int_val == -1)
1498 goto on_error;
1499 retval = g_variant_ref_sink(g_variant_new_int32(int_val));
1500 } else if (!strcmp(prop, DLS_INTERFACE_PROP_BITS_PER_SAMPLE)) {
1501 int_val = gupnp_didl_lite_resource_get_bits_per_sample(res);
1502 if (int_val == -1)
1503 goto on_error;
1504 retval = g_variant_ref_sink(g_variant_new_int32(int_val));
1505 } else if (!strcmp(prop, DLS_INTERFACE_PROP_WIDTH)) {
1506 int_val = (int) gupnp_didl_lite_resource_get_width(res);
1507 if (int_val == -1)
1508 goto on_error;
1509 retval = g_variant_ref_sink(g_variant_new_int32(int_val));
1510 } else if (!strcmp(prop, DLS_INTERFACE_PROP_HEIGHT)) {
1511 int_val = (int) gupnp_didl_lite_resource_get_height(res);
1512 if (int_val == -1)
1513 goto on_error;
1514 retval = g_variant_ref_sink(g_variant_new_int32(int_val));
1515 } else if (!strcmp(prop, DLS_INTERFACE_PROP_COLOR_DEPTH)) {
1516 int_val = (int) gupnp_didl_lite_resource_get_color_depth(res);
1517 if (int_val == -1)
1518 goto on_error;
1519 retval = g_variant_ref_sink(g_variant_new_int32(int_val));
1520 } else if (!strcmp(prop, DLS_INTERFACE_PROP_URLS)) {
1521 str_val = gupnp_didl_lite_resource_get_uri(res);
1522 if (str_val)
1523 retval = g_variant_new_strv(&str_val, 1);
1524 }
1525
1526 on_error:
1527
1528 return retval;
1529 }
1530
1531 GVariant *dls_props_get_object_prop(const gchar *prop, const gchar *root_path,
1532 GUPnPDIDLLiteObject *object)
1533 {
1534 const char *id;
1535 gchar *path;
1536 const char *upnp_class;
1537 const char *media_spec_type;
1538 const char *title;
1539 gboolean rest;
1540 GVariant *retval = NULL;
1541 GUPnPOCMFlags dlna_managed;
1542 guint uint_val;
1543
1544 if (!strcmp(prop, DLS_INTERFACE_PROP_PARENT)) {
1545 id = gupnp_didl_lite_object_get_parent_id(object);
1546 if (!id || !strcmp(id, "-1")) {
1547 DLEYNA_LOG_DEBUG("Prop %s = %s", prop, root_path);
1548
1549 retval = g_variant_ref_sink(g_variant_new_string(
1550 root_path));
1551 } else {
1552 path = dls_path_from_id(root_path, id);
1553
1554 DLEYNA_LOG_DEBUG("Prop %s = %s", prop, path);
1555
1556 retval = g_variant_ref_sink(g_variant_new_string(
1557 path));
1558 g_free(path);
1559 }
1560 } else if (!strcmp(prop, DLS_INTERFACE_PROP_PATH)) {
1561 id = gupnp_didl_lite_object_get_id(object);
1562 if (!id)
1563 goto on_error;
1564
1565 path = dls_path_from_id(root_path, id);
1566
1567 DLEYNA_LOG_DEBUG("Prop %s = %s", prop, path);
1568
1569 retval = g_variant_ref_sink(g_variant_new_string(path));
1570 g_free(path);
1571 } else if (!strcmp(prop, DLS_INTERFACE_PROP_TYPE)) {
1572 upnp_class = gupnp_didl_lite_object_get_upnp_class(object);
1573 media_spec_type =
1574 dls_props_upnp_class_to_media_spec(upnp_class);
1575 if (!media_spec_type)
1576 goto on_error;
1577
1578 DLEYNA_LOG_DEBUG("Prop %s = %s", prop, media_spec_type);
1579
1580 retval = g_variant_ref_sink(g_variant_new_string(
1581 media_spec_type));
1582 } else if (!strcmp(prop, DLS_INTERFACE_PROP_DISPLAY_NAME)) {
1583 title = gupnp_didl_lite_object_get_title(object);
1584 if (!title)
1585 goto on_error;
1586
1587 DLEYNA_LOG_DEBUG("Prop %s = %s", prop, title);
1588
1589 retval = g_variant_ref_sink(g_variant_new_string(title));
1590 } else if (!strcmp(prop, DLS_INTERFACE_PROP_CREATOR)) {
1591 title = gupnp_didl_lite_object_get_creator(object);
1592 if (!title)
1593 goto on_error;
1594
1595 DLEYNA_LOG_DEBUG("Prop %s = %s", prop, title);
1596
1597 retval = g_variant_ref_sink(g_variant_new_string(title));
1598 } else if (!strcmp(prop, DLS_INTERFACE_PROP_RESTRICTED)) {
1599 rest = gupnp_didl_lite_object_get_restricted(object);
1600
1601 DLEYNA_LOG_DEBUG("Prop %s = %d", prop, rest);
1602
1603 retval = g_variant_ref_sink(g_variant_new_boolean(rest));
1604 } else if (!strcmp(prop, DLS_INTERFACE_PROP_DLNA_MANAGED)) {
1605 dlna_managed = gupnp_didl_lite_object_get_dlna_managed(object);
1606
1607 DLEYNA_LOG_DEBUG("Prop %s = %0x", prop, dlna_managed);
1608
1609 retval = g_variant_ref_sink(
1610 prv_props_get_dlna_managed_dict(dlna_managed));
1611 } else if (!strcmp(prop, DLS_INTERFACE_PROP_OBJECT_UPDATE_ID)) {
1612 uint_val = gupnp_didl_lite_object_get_update_id(object);
1613
1614 DLEYNA_LOG_DEBUG("Prop %s = %u", prop, uint_val);
1615
1616 retval = g_variant_ref_sink(g_variant_new_uint32(uint_val));
1617 }
1618
1619 on_error:
1620
1621 return retval;
1622 }
1623
1624 GVariant *dls_props_get_item_prop(const gchar *prop, const gchar *root_path,
1625 GUPnPDIDLLiteObject *object,
1626 const gchar *protocol_info)
1627 {
1628 const gchar *str;
1629 gchar *path;
1630 gint track_number;
1631 GUPnPDIDLLiteResource *res;
1632 GVariant *retval = NULL;
1633 GList *list;
1634
1635 if (GUPNP_IS_DIDL_LITE_CONTAINER(object))
1636 goto on_error;
1637
1638 if (!strcmp(prop, DLS_INTERFACE_PROP_ARTIST)) {
1639 str = gupnp_didl_lite_object_get_artist(object);
1640 if (!str)
1641 goto on_error;
1642
1643 DLEYNA_LOG_DEBUG("Prop %s = %s", prop, str);
1644
1645 retval = g_variant_ref_sink(g_variant_new_string(str));
1646 } else if (!strcmp(prop, DLS_INTERFACE_PROP_ARTISTS)) {
1647 list = gupnp_didl_lite_object_get_artists(object);
1648 if (!list)
1649 goto on_error;
1650
1651 retval = g_variant_ref_sink(prv_get_artists_prop(list));
1652 g_list_free_full(list, g_object_unref);
1653
1654 #if DLEYNA_LOG_LEVEL & DLEYNA_LOG_LEVEL_DEBUG
1655 path = g_variant_print(retval, FALSE);
1656 DLEYNA_LOG_DEBUG("Prop %s = %s", prop, path);
1657 g_free(path);
1658 #endif
1659 } else if (!strcmp(prop, DLS_INTERFACE_PROP_ALBUM)) {
1660 str = gupnp_didl_lite_object_get_album(object);
1661 if (!str)
1662 goto on_error;
1663
1664 DLEYNA_LOG_DEBUG("Prop %s = %s", prop, str);
1665
1666 retval = g_variant_ref_sink(g_variant_new_string(str));
1667 } else if (!strcmp(prop, DLS_INTERFACE_PROP_DATE)) {
1668 str = gupnp_didl_lite_object_get_date(object);
1669 if (!str)
1670 goto on_error;
1671
1672 DLEYNA_LOG_DEBUG("Prop %s = %s", prop, str);
1673
1674 retval = g_variant_ref_sink(g_variant_new_string(str));
1675 } else if (!strcmp(prop, DLS_INTERFACE_PROP_GENRE)) {
1676 str = gupnp_didl_lite_object_get_genre(object);
1677 if (!str)
1678 goto on_error;
1679
1680 DLEYNA_LOG_DEBUG("Prop %s = %s", prop, str);
1681
1682 retval = g_variant_ref_sink(g_variant_new_string(str));
1683 } else if (!strcmp(prop, DLS_INTERFACE_PROP_TRACK_NUMBER)) {
1684 track_number = gupnp_didl_lite_object_get_track_number(object);
1685 if (track_number < 0)
1686 goto on_error;
1687
1688 DLEYNA_LOG_DEBUG("Prop %s = %d", prop, track_number);
1689
1690 retval = g_variant_ref_sink(
1691 g_variant_new_int32(track_number));
1692 } else if (!strcmp(prop, DLS_INTERFACE_PROP_ALBUM_ART_URL)) {
1693 str = gupnp_didl_lite_object_get_album_art(object);
1694 if (!str)
1695 goto on_error;
1696
1697 DLEYNA_LOG_DEBUG("Prop %s = %s", prop, str);
1698
1699 retval = g_variant_ref_sink(g_variant_new_string(str));
1700 } else if (!strcmp(prop, DLS_INTERFACE_PROP_REFPATH)) {
1701 str = gupnp_didl_lite_item_get_ref_id(
1702 GUPNP_DIDL_LITE_ITEM(object));
1703 if (!str)
1704 goto on_error;
1705
1706 DLEYNA_LOG_DEBUG("Prop %s = %s", prop, str);
1707
1708 path = dls_path_from_id(root_path, str);
1709 retval = g_variant_ref_sink(g_variant_new_string(path));
1710 g_free(path);
1711 } else if (!strcmp(prop, DLS_INTERFACE_PROP_RESOURCES)) {
1712 retval = g_variant_ref_sink(
1713 prv_compute_resources(object, DLS_UPNP_MASK_ALL_PROPS));
1714 } else {
1715 res = prv_get_matching_resource(object, protocol_info);
1716 if (!res)
1717 goto on_error;
1718
1719 retval = prv_get_resource_property(prop, res);
1720
1721 g_object_unref(res);
1722 }
1723
1724 on_error:
1725
1726 return retval;
1727 }
1728
1729 GVariant *dls_props_get_container_prop(const gchar *prop,
1730 GUPnPDIDLLiteObject *object)
1731 {
1732 gint child_count;
1733 gboolean searchable;
1734 GUPnPDIDLLiteContainer *container;
1735 GVariant *retval = NULL;
1736 guint uint_val;
1737 #if DLEYNA_LOG_LEVEL & DLEYNA_LOG_LEVEL_DEBUG
1738 gchar *create_classes;
1739 #endif
1740 if (!GUPNP_IS_DIDL_LITE_CONTAINER(object))
1741 goto on_error;
1742
1743 container = (GUPnPDIDLLiteContainer *)object;
1744 if (!strcmp(prop, DLS_INTERFACE_PROP_CHILD_COUNT)) {
1745 child_count =
1746 gupnp_didl_lite_container_get_child_count(container);
1747
1748 DLEYNA_LOG_DEBUG("Prop %s = %d", prop, child_count);
1749
1750 if (child_count >= 0) {
1751 retval = g_variant_new_uint32((guint) child_count);
1752 retval = g_variant_ref_sink(retval);
1753 }
1754 } else if (!strcmp(prop, DLS_INTERFACE_PROP_SEARCHABLE)) {
1755 searchable =
1756 gupnp_didl_lite_container_get_searchable(container);
1757
1758 DLEYNA_LOG_DEBUG("Prop %s = %d", prop, searchable);
1759
1760 retval = g_variant_ref_sink(
1761 g_variant_new_boolean(searchable));
1762 } else if (!strcmp(prop, DLS_INTERFACE_PROP_CREATE_CLASSES)) {
1763 retval = g_variant_ref_sink(
1764 prv_compute_create_classes(container));
1765 #if DLEYNA_LOG_LEVEL & DLEYNA_LOG_LEVEL_DEBUG
1766 create_classes = g_variant_print(retval, FALSE);
1767 DLEYNA_LOG_DEBUG("Prop %s = %s", prop, create_classes);
1768 g_free(create_classes);
1769 #endif
1770 } else if (!strcmp(prop, DLS_INTERFACE_PROP_CONTAINER_UPDATE_ID)) {
1771 uint_val = gupnp_didl_lite_container_get_container_update_id(
1772 container);
1773
1774 DLEYNA_LOG_DEBUG("Prop %s = %u", prop, uint_val);
1775
1776 retval = g_variant_ref_sink(g_variant_new_uint32(uint_val));
1777 } else if (!strcmp(prop,
1778 DLS_INTERFACE_PROP_TOTAL_DELETED_CHILD_COUNT)) {
1779 uint_val =
1780 gupnp_didl_lite_container_get_total_deleted_child_count(
1781 container);
1782
1783 DLEYNA_LOG_DEBUG("Prop %s = %u", prop, uint_val);
1784
1785 retval = g_variant_ref_sink(g_variant_new_uint32(uint_val));
1786 }
1787
1788 on_error:
1789
1790 return retval;
1791 }
+0
-134
lib/props.h less more
0 /*
1 * dLeyna
2 *
3 * Copyright (C) 2012-2013 Intel Corporation. All rights reserved.
4 *
5 * This program is free software; you can redistribute it and/or modify it
6 * under the terms and conditions of the GNU Lesser General Public License,
7 * version 2.1, as published by the Free Software Foundation.
8 *
9 * This program is distributed in the hope it will be useful, but WITHOUT
10 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
11 * FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License
12 * for more details.
13 *
14 * You should have received a copy of the GNU Lesser General Public License
15 * along with this program; if not, write to the Free Software Foundation, Inc.,
16 * 51 Franklin St - Fifth Floor, Boston, MA 02110-1301 USA.
17 *
18 * Mark Ryan <mark.d.ryan@intel.com>
19 *
20 */
21
22 #ifndef DLS_PROPS_H__
23 #define DLS_PROPS_H__
24
25 #include <libgupnp-av/gupnp-av.h>
26 #include "async.h"
27
28 #define DLS_UPNP_MASK_PROP_PARENT (1LL << 0)
29 #define DLS_UPNP_MASK_PROP_TYPE (1LL << 1)
30 #define DLS_UPNP_MASK_PROP_PATH (1LL << 2)
31 #define DLS_UPNP_MASK_PROP_DISPLAY_NAME (1LL << 3)
32 #define DLS_UPNP_MASK_PROP_CHILD_COUNT (1LL << 4)
33 #define DLS_UPNP_MASK_PROP_SEARCHABLE (1LL << 5)
34 #define DLS_UPNP_MASK_PROP_URLS (1LL << 6)
35 #define DLS_UPNP_MASK_PROP_MIME_TYPE (1LL << 7)
36 #define DLS_UPNP_MASK_PROP_ARTIST (1LL << 8)
37 #define DLS_UPNP_MASK_PROP_ALBUM (1LL << 9)
38 #define DLS_UPNP_MASK_PROP_DATE (1LL << 10)
39 #define DLS_UPNP_MASK_PROP_GENRE (1LL << 11)
40 #define DLS_UPNP_MASK_PROP_DLNA_PROFILE (1LL << 12)
41 #define DLS_UPNP_MASK_PROP_TRACK_NUMBER (1LL << 13)
42 #define DLS_UPNP_MASK_PROP_SIZE (1LL << 14)
43 #define DLS_UPNP_MASK_PROP_DURATION (1LL << 15)
44 #define DLS_UPNP_MASK_PROP_BITRATE (1LL << 16)
45 #define DLS_UPNP_MASK_PROP_SAMPLE_RATE (1LL << 17)
46 #define DLS_UPNP_MASK_PROP_BITS_PER_SAMPLE (1LL << 18)
47 #define DLS_UPNP_MASK_PROP_WIDTH (1LL << 19)
48 #define DLS_UPNP_MASK_PROP_HEIGHT (1LL << 20)
49 #define DLS_UPNP_MASK_PROP_COLOR_DEPTH (1LL << 21)
50 #define DLS_UPNP_MASK_PROP_ALBUM_ART_URL (1LL << 22)
51 #define DLS_UPNP_MASK_PROP_RESOURCES (1LL << 23)
52 #define DLS_UPNP_MASK_PROP_URL (1LL << 24)
53 #define DLS_UPNP_MASK_PROP_REFPATH (1LL << 25)
54 #define DLS_UPNP_MASK_PROP_RESTRICTED (1LL << 26)
55 #define DLS_UPNP_MASK_PROP_DLNA_MANAGED (1LL << 27)
56 #define DLS_UPNP_MASK_PROP_CREATOR (1LL << 28)
57 #define DLS_UPNP_MASK_PROP_ARTISTS (1LL << 29)
58 #define DLS_UPNP_MASK_PROP_CREATE_CLASSES (1LL << 30)
59 #define DLS_UPNP_MASK_PROP_OBJECT_UPDATE_ID (1LL << 31)
60 #define DLS_UPNP_MASK_PROP_UPDATE_COUNT (1LL << 32)
61 #define DLS_UPNP_MASK_PROP_CONTAINER_UPDATE_ID (1LL << 33)
62 #define DLS_UPNP_MASK_PROP_TOTAL_DELETED_CHILD_COUNT (1LL << 34)
63
64 #define DLS_UPNP_MASK_ALL_PROPS 0xffffffffffffffff
65
66 typedef struct dls_prop_map_t_ dls_prop_map_t;
67 struct dls_prop_map_t_ {
68 const gchar *upnp_prop_name;
69 dls_upnp_prop_mask type;
70 gboolean filter;
71 gboolean searchable;
72 gboolean updateable;
73 };
74
75 void dls_prop_maps_new(GHashTable **property_map, GHashTable **filter_map);
76
77 dls_upnp_prop_mask dls_props_parse_filter(GHashTable *filter_map,
78 GVariant *filter,
79 gchar **upnp_filter);
80
81 gboolean dls_props_parse_update_filter(GHashTable *filter_map,
82 GVariant *to_add_update,
83 GVariant *to_delete,
84 dls_upnp_prop_mask *mask,
85 gchar **upnp_filter);
86
87 void dls_props_add_device(GUPnPDeviceInfo *proxy,
88 const dls_device_t *device,
89 GVariantBuilder *vb);
90
91 GVariant *dls_props_get_device_prop(GUPnPDeviceInfo *proxy,
92 const dls_device_t *device,
93 const gchar *prop);
94
95 gboolean dls_props_add_object(GVariantBuilder *item_vb,
96 GUPnPDIDLLiteObject *object,
97 const char *root_path,
98 const gchar *parent_path,
99 dls_upnp_prop_mask filter_mask);
100
101 GVariant *dls_props_get_object_prop(const gchar *prop, const gchar *root_path,
102 GUPnPDIDLLiteObject *object);
103
104 void dls_props_add_container(GVariantBuilder *item_vb,
105 GUPnPDIDLLiteContainer *object,
106 dls_upnp_prop_mask filter_mask,
107 gboolean *have_child_count);
108
109 void dls_props_add_child_count(GVariantBuilder *item_vb, gint value);
110
111 GVariant *dls_props_get_container_prop(const gchar *prop,
112 GUPnPDIDLLiteObject *object);
113
114 void dls_props_add_resource(GVariantBuilder *item_vb,
115 GUPnPDIDLLiteObject *object,
116 dls_upnp_prop_mask filter_mask,
117 const gchar *protocol_info);
118
119 void dls_props_add_item(GVariantBuilder *item_vb,
120 GUPnPDIDLLiteObject *object,
121 const gchar *root_path,
122 dls_upnp_prop_mask filter_mask,
123 const gchar *protocol_info);
124
125 GVariant *dls_props_get_item_prop(const gchar *prop, const gchar *root_path,
126 GUPnPDIDLLiteObject *object,
127 const gchar *protocol_info);
128
129 const gchar *dls_props_media_spec_to_upnp_class(const gchar *m2spec_class);
130
131 const gchar *dls_props_upnp_class_to_media_spec(const gchar *upnp_class);
132
133 #endif /* DLS_PROPS_H__ */
+0
-148
lib/search.c less more
0 /*
1 * dLeyna
2 *
3 * Copyright (C) 2012-2013 Intel Corporation. All rights reserved.
4 *
5 * This program is free software; you can redistribute it and/or modify it
6 * under the terms and conditions of the GNU Lesser General Public License,
7 * version 2.1, as published by the Free Software Foundation.
8 *
9 * This program is distributed in the hope it will be useful, but WITHOUT
10 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
11 * FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License
12 * for more details.
13 *
14 * You should have received a copy of the GNU Lesser General Public License
15 * along with this program; if not, write to the Free Software Foundation, Inc.,
16 * 51 Franklin St - Fifth Floor, Boston, MA 02110-1301 USA.
17 *
18 * Mark Ryan <mark.d.ryan@intel.com>
19 *
20 */
21
22 #include <string.h>
23
24 #include "interface.h"
25 #include "path.h"
26 #include "props.h"
27 #include "search.h"
28
29 gchar *dls_search_translate_search_string(GHashTable *filter_map,
30 const gchar *search_string)
31 {
32 GRegex *reg;
33 gchar *retval = NULL;
34 GMatchInfo *match_info = NULL;
35 gchar *prop = NULL;
36 gchar *op = NULL;
37 gchar *value = NULL;
38 const gchar *translated_value;
39 dls_prop_map_t *prop_map;
40 GString *str;
41 gint start_pos;
42 gint end_pos;
43 gint old_end_pos = 0;
44 unsigned int skipped;
45 unsigned int search_string_len = strlen(search_string);
46 gchar *root_path;
47 gchar *id;
48
49 reg = g_regex_new("(\\w+)\\s+(=|!=|<|<=|>|>|contains|doesNotContain|"\
50 "derivedfrom|exists)\\s+"\
51 "(\"[^\"]*\"|true|false)",
52 0, 0, NULL);
53 str = g_string_new("");
54
55 g_regex_match(reg, search_string, 0, &match_info);
56 while (g_match_info_matches(match_info)) {
57 prop = g_match_info_fetch(match_info, 1);
58 if (!prop)
59 goto on_error;
60
61 op = g_match_info_fetch(match_info, 2);
62 if (!op)
63 goto on_error;
64
65 value = g_match_info_fetch(match_info, 3);
66 if (!value)
67 goto on_error;
68
69 /* Handle special cases where we need to translate
70 value as well as property name */
71
72 if (!strcmp(prop, DLS_INTERFACE_PROP_TYPE)) {
73 /* Skip the quotes */
74
75 value[strlen(value) - 1] = 0;
76 translated_value = dls_props_media_spec_to_upnp_class(
77 value + 1);
78 if (!translated_value)
79 goto on_error;
80 g_free(value);
81 value = g_strdup_printf("\"%s\"", translated_value);
82 } else if (!strcmp(prop, DLS_INTERFACE_PROP_PARENT) ||
83 !strcmp(prop, DLS_INTERFACE_PROP_PATH)) {
84 value[strlen(value) - 1] = 0;
85 if (!dls_path_get_path_and_id(value + 1, &root_path,
86 &id, NULL))
87 goto on_error;
88 g_free(root_path);
89 g_free(value);
90 value = g_strdup_printf("\"%s\"", id);
91 g_free(id);
92 }
93
94 prop_map = g_hash_table_lookup(filter_map, prop);
95 if (!prop_map)
96 goto on_error;
97
98 if (!prop_map->searchable)
99 goto on_error;
100
101 if (!g_match_info_fetch_pos(match_info, 0, &start_pos,
102 &end_pos))
103 goto on_error;
104
105 skipped = start_pos - old_end_pos;
106 if (skipped > 0)
107 g_string_append_len(str, &search_string[old_end_pos],
108 skipped);
109 g_string_append_printf(str, "%s %s %s",
110 prop_map->upnp_prop_name, op, value);
111 old_end_pos = end_pos;
112
113 g_free(value);
114 g_free(prop);
115 g_free(op);
116
117 value = NULL;
118 prop = NULL;
119 op = NULL;
120
121 g_match_info_next(match_info, NULL);
122 }
123
124 skipped = search_string_len - old_end_pos;
125 if (skipped > 0)
126 g_string_append_len(str, &search_string[old_end_pos],
127 skipped);
128
129 retval = g_string_free(str, FALSE);
130 str = NULL;
131
132 on_error:
133
134 g_free(value);
135 g_free(prop);
136 g_free(op);
137
138 if (match_info)
139 g_match_info_free(match_info);
140
141 if (str)
142 g_string_free(str, TRUE);
143
144 g_regex_unref(reg);
145
146 return retval;
147 }
+0
-31
lib/search.h less more
0 /*
1 * dLeyna
2 *
3 * Copyright (C) 2012-2013 Intel Corporation. All rights reserved.
4 *
5 * This program is free software; you can redistribute it and/or modify it
6 * under the terms and conditions of the GNU Lesser General Public License,
7 * version 2.1, as published by the Free Software Foundation.
8 *
9 * This program is distributed in the hope it will be useful, but WITHOUT
10 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
11 * FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License
12 * for more details.
13 *
14 * You should have received a copy of the GNU Lesser General Public License
15 * along with this program; if not, write to the Free Software Foundation, Inc.,
16 * 51 Franklin St - Fifth Floor, Boston, MA 02110-1301 USA.
17 *
18 * Mark Ryan <mark.d.ryan@intel.com>
19 *
20 */
21
22 #ifndef DLS_SEARCH_H__
23 #define DLS_SEARCH_H__
24
25 #include <glib.h>
26
27 gchar *dls_search_translate_search_string(GHashTable *filter_map,
28 const gchar *search_string);
29
30 #endif /* DLS_PROPS_H__ */
+0
-1199
lib/server.c less more
0 /*
1 * dLeyna
2 *
3 * Copyright (C) 2013 Intel Corporation. All rights reserved.
4 *
5 * This program is free software; you can redistribute it and/or modify it
6 * under the terms and conditions of the GNU Lesser General Public License,
7 * version 2.1, as published by the Free Software Foundation.
8 *
9 * This program is distributed in the hope it will be useful, but WITHOUT
10 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
11 * FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License
12 * for more details.
13 *
14 * You should have received a copy of the GNU Lesser General Public License
15 * along with this program; if not, write to the Free Software Foundation, Inc.,
16 * 51 Franklin St - Fifth Floor, Boston, MA 02110-1301 USA.
17 *
18 * Mark Ryan <mark.d.ryan@intel.com>
19 * Regis Merlino <regis.merlino@intel.com>
20 *
21 */
22
23 #include <glib.h>
24 #include <string.h>
25
26 #include <libdleyna/core/connector.h>
27 #include <libdleyna/core/control-point.h>
28 #include <libdleyna/core/error.h>
29 #include <libdleyna/core/log.h>
30 #include <libdleyna/core/task-processor.h>
31
32 #include "async.h"
33 #include "client.h"
34 #include "control-point-server.h"
35 #include "device.h"
36 #include "interface.h"
37 #include "path.h"
38 #include "server.h"
39 #include "upnp.h"
40
41 #ifdef UA_PREFIX
42 #define DLS_PRG_NAME UA_PREFIX " dLeyna/" VERSION
43 #else
44 #define DLS_PRG_NAME "dLeyna/" VERSION
45 #endif
46
47
48 typedef struct dls_server_context_t_ dls_server_context_t;
49 struct dls_server_context_t_ {
50 dleyna_connector_id_t connection;
51 dleyna_task_processor_t *processor;
52 const dleyna_connector_t *connector;
53 dleyna_settings_t *settings;
54 guint dls_id;
55 GHashTable *watchers;
56 dls_upnp_t *upnp;
57 };
58
59 static dls_server_context_t g_context;
60
61 static const gchar g_root_introspection[] =
62 "<node>"
63 " <interface name='"DLEYNA_SERVER_INTERFACE_MANAGER"'>"
64 " <method name='"DLS_INTERFACE_GET_VERSION"'>"
65 " <arg type='s' name='"DLS_INTERFACE_VERSION"'"
66 " direction='out'/>"
67 " </method>"
68 " <method name='"DLS_INTERFACE_RELEASE"'>"
69 " </method>"
70 " <method name='"DLS_INTERFACE_GET_SERVERS"'>"
71 " <arg type='ao' name='"DLS_INTERFACE_SERVERS"'"
72 " direction='out'/>"
73 " </method>"
74 " <method name='"DLS_INTERFACE_SET_PROTOCOL_INFO"'>"
75 " <arg type='s' name='"DLS_INTERFACE_PROTOCOL_INFO"'"
76 " direction='in'/>"
77 " </method>"
78 " <method name='"DLS_INTERFACE_PREFER_LOCAL_ADDRESSES"'>"
79 " <arg type='b' name='"DLS_INTERFACE_PREFER"'"
80 " direction='in'/>"
81 " </method>"
82 " <signal name='"DLS_INTERFACE_FOUND_SERVER"'>"
83 " <arg type='o' name='"DLS_INTERFACE_PATH"'/>"
84 " </signal>"
85 " <signal name='"DLS_INTERFACE_LOST_SERVER"'>"
86 " <arg type='o' name='"DLS_INTERFACE_PATH"'/>"
87 " </signal>"
88 " </interface>"
89 "</node>";
90
91 static const gchar g_server_introspection[] =
92 "<node>"
93 " <interface name='"DLS_INTERFACE_PROPERTIES"'>"
94 " <method name='"DLS_INTERFACE_GET"'>"
95 " <arg type='s' name='"DLS_INTERFACE_INTERFACE_NAME"'"
96 " direction='in'/>"
97 " <arg type='s' name='"DLS_INTERFACE_PROPERTY_NAME"'"
98 " direction='in'/>"
99 " <arg type='v' name='"DLS_INTERFACE_VALUE"'"
100 " direction='out'/>"
101 " </method>"
102 " <method name='"DLS_INTERFACE_GET_ALL"'>"
103 " <arg type='s' name='"DLS_INTERFACE_INTERFACE_NAME"'"
104 " direction='in'/>"
105 " <arg type='a{sv}' name='"DLS_INTERFACE_PROPERTIES_VALUE"'"
106 " direction='out'/>"
107 " </method>"
108 " <signal name='"DLS_INTERFACE_PROPERTIES_CHANGED"'>"
109 " <arg type='s' name='"DLS_INTERFACE_INTERFACE_NAME"'/>"
110 " <arg type='a{sv}' name='"DLS_INTERFACE_CHANGED_PROPERTIES"'/>"
111 " <arg type='as' name='"
112 DLS_INTERFACE_INVALIDATED_PROPERTIES"'/>"
113 " </signal>"
114 " </interface>"
115 " <interface name='"DLS_INTERFACE_MEDIA_OBJECT"'>"
116 " <property type='o' name='"DLS_INTERFACE_PROP_PARENT"'"
117 " access='read'/>"
118 " <property type='s' name='"DLS_INTERFACE_PROP_TYPE"'"
119 " access='read'/>"
120 " <property type='o' name='"DLS_INTERFACE_PROP_PATH"'"
121 " access='read'/>"
122 " <property type='s' name='"DLS_INTERFACE_PROP_DISPLAY_NAME"'"
123 " access='read'/>"
124 " <property type='s' name='"DLS_INTERFACE_PROP_CREATOR"'"
125 " access='read'/>"
126 " <property type='b' name='"DLS_INTERFACE_PROP_RESTRICTED"'"
127 " access='read'/>"
128 " <property type='a{sb}' name='"DLS_INTERFACE_PROP_DLNA_MANAGED"'"
129 " access='read'/>"
130 " <property type='u' name='"DLS_INTERFACE_PROP_OBJECT_UPDATE_ID"'"
131 " access='read'/>"
132 " <method name='"DLS_INTERFACE_DELETE"'>"
133 " </method>"
134 " <method name='"DLS_INTERFACE_UPDATE"'>"
135 " <arg type='a{sv}' name='"DLS_INTERFACE_TO_ADD_UPDATE"'"
136 " direction='in'/>"
137 " <arg type='as' name='"DLS_INTERFACE_TO_DELETE"'"
138 " direction='in'/>"
139 " </method>"
140 " </interface>"
141 " <interface name='"DLS_INTERFACE_MEDIA_CONTAINER"'>"
142 " <method name='"DLS_INTERFACE_LIST_CHILDREN"'>"
143 " <arg type='u' name='"DLS_INTERFACE_OFFSET"'"
144 " direction='in'/>"
145 " <arg type='u' name='"DLS_INTERFACE_MAX"'"
146 " direction='in'/>"
147 " <arg type='as' name='"DLS_INTERFACE_FILTER"'"
148 " direction='in'/>"
149 " <arg type='aa{sv}' name='"DLS_INTERFACE_CHILDREN"'"
150 " direction='out'/>"
151 " </method>"
152 " <method name='"DLS_INTERFACE_LIST_CHILDREN_EX"'>"
153 " <arg type='u' name='"DLS_INTERFACE_OFFSET"'"
154 " direction='in'/>"
155 " <arg type='u' name='"DLS_INTERFACE_MAX"'"
156 " direction='in'/>"
157 " <arg type='as' name='"DLS_INTERFACE_FILTER"'"
158 " direction='in'/>"
159 " <arg type='s' name='"DLS_INTERFACE_SORT_BY"'"
160 " direction='in'/>"
161 " <arg type='aa{sv}' name='"DLS_INTERFACE_CHILDREN"'"
162 " direction='out'/>"
163 " </method>"
164 " <method name='"DLS_INTERFACE_LIST_CONTAINERS"'>"
165 " <arg type='u' name='"DLS_INTERFACE_OFFSET"'"
166 " direction='in'/>"
167 " <arg type='u' name='"DLS_INTERFACE_MAX"'"
168 " direction='in'/>"
169 " <arg type='as' name='"DLS_INTERFACE_FILTER"'"
170 " direction='in'/>"
171 " <arg type='aa{sv}' name='"DLS_INTERFACE_CHILDREN"'"
172 " direction='out'/>"
173 " </method>"
174 " <method name='"DLS_INTERFACE_LIST_CONTAINERS_EX"'>"
175 " <arg type='u' name='"DLS_INTERFACE_OFFSET"'"
176 " direction='in'/>"
177 " <arg type='u' name='"DLS_INTERFACE_MAX"'"
178 " direction='in'/>"
179 " <arg type='as' name='"DLS_INTERFACE_FILTER"'"
180 " direction='in'/>"
181 " <arg type='s' name='"DLS_INTERFACE_SORT_BY"'"
182 " direction='in'/>"
183 " <arg type='aa{sv}' name='"DLS_INTERFACE_CHILDREN"'"
184 " direction='out'/>"
185 " </method>"
186 " <method name='"DLS_INTERFACE_LIST_ITEMS"'>"
187 " <arg type='u' name='"DLS_INTERFACE_OFFSET"'"
188 " direction='in'/>"
189 " <arg type='u' name='"DLS_INTERFACE_MAX"'"
190 " direction='in'/>"
191 " <arg type='as' name='"DLS_INTERFACE_FILTER"'"
192 " direction='in'/>"
193 " <arg type='aa{sv}' name='"DLS_INTERFACE_CHILDREN"'"
194 " direction='out'/>"
195 " </method>"
196 " <method name='"DLS_INTERFACE_LIST_ITEMS_EX"'>"
197 " <arg type='u' name='"DLS_INTERFACE_OFFSET"'"
198 " direction='in'/>"
199 " <arg type='u' name='"DLS_INTERFACE_MAX"'"
200 " direction='in'/>"
201 " <arg type='as' name='"DLS_INTERFACE_FILTER"'"
202 " direction='in'/>"
203 " <arg type='s' name='"DLS_INTERFACE_SORT_BY"'"
204 " direction='in'/>"
205 " <arg type='aa{sv}' name='"DLS_INTERFACE_CHILDREN"'"
206 " direction='out'/>"
207 " </method>"
208 " <method name='"DLS_INTERFACE_SEARCH_OBJECTS"'>"
209 " <arg type='s' name='"DLS_INTERFACE_QUERY"'"
210 " direction='in'/>"
211 " <arg type='u' name='"DLS_INTERFACE_OFFSET"'"
212 " direction='in'/>"
213 " <arg type='u' name='"DLS_INTERFACE_MAX"'"
214 " direction='in'/>"
215 " <arg type='as' name='"DLS_INTERFACE_FILTER"'"
216 " direction='in'/>"
217 " <arg type='aa{sv}' name='"DLS_INTERFACE_CHILDREN"'"
218 " direction='out'/>"
219 " </method>"
220 " <method name='"DLS_INTERFACE_SEARCH_OBJECTS_EX"'>"
221 " <arg type='s' name='"DLS_INTERFACE_QUERY"'"
222 " direction='in'/>"
223 " <arg type='u' name='"DLS_INTERFACE_OFFSET"'"
224 " direction='in'/>"
225 " <arg type='u' name='"DLS_INTERFACE_MAX"'"
226 " direction='in'/>"
227 " <arg type='as' name='"DLS_INTERFACE_FILTER"'"
228 " direction='in'/>"
229 " <arg type='s' name='"DLS_INTERFACE_SORT_BY"'"
230 " direction='in'/>"
231 " <arg type='aa{sv}' name='"DLS_INTERFACE_CHILDREN"'"
232 " direction='out'/>"
233 " <arg type='u' name='"DLS_INTERFACE_TOTAL_ITEMS"'"
234 " direction='out'/>"
235 " </method>"
236 " <method name='"DLS_INTERFACE_UPLOAD"'>"
237 " <arg type='s' name='"DLS_INTERFACE_PROP_DISPLAY_NAME"'"
238 " direction='in'/>"
239 " <arg type='s' name='"DLS_INTERFACE_FILE_PATH"'"
240 " direction='in'/>"
241 " <arg type='u' name='"DLS_INTERFACE_UPLOAD_ID"'"
242 " direction='out'/>"
243 " <arg type='o' name='"DLS_INTERFACE_PATH"'"
244 " direction='out'/>"
245 " </method>"
246 " <method name='"DLS_INTERFACE_CREATE_CONTAINER"'>"
247 " <arg type='s' name='"DLS_INTERFACE_PROP_DISPLAY_NAME"'"
248 " direction='in'/>"
249 " <arg type='s' name='"DLS_INTERFACE_PROP_TYPE"'"
250 " direction='in'/>"
251 " <arg type='as' name='"DLS_INTERFACE_CHILD_TYPES"'"
252 " direction='in'/>"
253 " <arg type='o' name='"DLS_INTERFACE_PATH"'"
254 " direction='out'/>"
255 " </method>"
256 " <method name='"DLS_INTERFACE_CREATE_PLAYLIST"'>"
257 " <arg type='s' name='"DLS_INTERFACE_TITLE"'"
258 " direction='in'/>"
259 " <arg type='s' name='"DLS_INTERFACE_CREATOR"'"
260 " direction='in'/>"
261 " <arg type='s' name='"DLS_INTERFACE_GENRE"'"
262 " direction='in'/>"
263 " <arg type='s' name='"DLS_INTERFACE_DESCRIPTION"'"
264 " direction='in'/>"
265 " <arg type='ao' name='"DLS_INTERFACE_PLAYLIST_ITEMS"'"
266 " direction='in'/>"
267 " <arg type='u' name='"DLS_INTERFACE_UPLOAD_ID"'"
268 " direction='out'/>"
269 " <arg type='o' name='"DLS_INTERFACE_PATH"'"
270 " direction='out'/>"
271 " </method>"
272 " <property type='u' name='"DLS_INTERFACE_PROP_CHILD_COUNT"'"
273 " access='read'/>"
274 " <property type='b' name='"DLS_INTERFACE_PROP_SEARCHABLE"'"
275 " access='read'/>"
276 " <property type='a(sb)' name='"
277 DLS_INTERFACE_PROP_CREATE_CLASSES"'"
278 " access='read'/>"
279 " <property type='u' name='"
280 DLS_INTERFACE_PROP_CONTAINER_UPDATE_ID"'"
281 " access='read'/>"
282 " <property type='u' name='"
283 DLS_INTERFACE_PROP_TOTAL_DELETED_CHILD_COUNT"'"
284 " access='read'/>"
285 " </interface>"
286 " <interface name='"DLS_INTERFACE_MEDIA_ITEM"'>"
287 " <method name='"DLS_INTERFACE_GET_COMPATIBLE_RESOURCE"'>"
288 " <arg type='s' name='"DLS_INTERFACE_PROTOCOL_INFO"'"
289 " direction='in'/>"
290 " <arg type='as' name='"DLS_INTERFACE_FILTER"'"
291 " direction='in'/>"
292 " <arg type='a{sv}' name='"DLS_INTERFACE_PROPERTIES_VALUE"'"
293 " direction='out'/>"
294 " </method>"
295 " <property type='as' name='"DLS_INTERFACE_PROP_URLS"'"
296 " access='read'/>"
297 " <property type='s' name='"DLS_INTERFACE_PROP_MIME_TYPE"'"
298 " access='read'/>"
299 " <property type='s' name='"DLS_INTERFACE_PROP_ARTIST"'"
300 " access='read'/>"
301 " <property type='as' name='"DLS_INTERFACE_PROP_ARTISTS"'"
302 " access='read'/>"
303 " <property type='s' name='"DLS_INTERFACE_PROP_ALBUM"'"
304 " access='read'/>"
305 " <property type='s' name='"DLS_INTERFACE_PROP_DATE"'"
306 " access='read'/>"
307 " <property type='s' name='"DLS_INTERFACE_PROP_GENRE"'"
308 " access='read'/>"
309 " <property type='s' name='"DLS_INTERFACE_PROP_DLNA_PROFILE"'"
310 " access='read'/>"
311 " <property type='i' name='"DLS_INTERFACE_PROP_TRACK_NUMBER"'"
312 " access='read'/>"
313 " <property type='x' name='"DLS_INTERFACE_PROP_SIZE"'"
314 " access='read'/>"
315 " <property type='i' name='"DLS_INTERFACE_PROP_DURATION"'"
316 " access='read'/>"
317 " <property type='i' name='"DLS_INTERFACE_PROP_BITRATE"'"
318 " access='read'/>"
319 " <property type='i' name='"DLS_INTERFACE_PROP_SAMPLE_RATE"'"
320 " access='read'/>"
321 " <property type='i' name='"DLS_INTERFACE_PROP_BITS_PER_SAMPLE"'"
322 " access='read'/>"
323 " <property type='i' name='"DLS_INTERFACE_PROP_WIDTH"'"
324 " access='read'/>"
325 " <property type='i' name='"DLS_INTERFACE_PROP_HEIGHT"'"
326 " access='read'/>"
327 " <property type='i' name='"DLS_INTERFACE_PROP_COLOR_DEPTH"'"
328 " access='read'/>"
329 " <property type='s' name='"DLS_INTERFACE_PROP_ALBUM_ART_URL"'"
330 " access='read'/>"
331 " <property type='o' name='"DLS_INTERFACE_PROP_REFPATH"'"
332 " access='read'/>"
333 " <property type='aa{sv}' name='"DLS_INTERFACE_PROP_RESOURCES"'"
334 " access='read'/>"
335 " </interface>"
336 " <interface name='"DLEYNA_SERVER_INTERFACE_MEDIA_DEVICE"'>"
337 " <method name='"DLS_INTERFACE_UPLOAD_TO_ANY"'>"
338 " <arg type='s' name='"DLS_INTERFACE_PROP_DISPLAY_NAME"'"
339 " direction='in'/>"
340 " <arg type='s' name='"DLS_INTERFACE_FILE_PATH"'"
341 " direction='in'/>"
342 " <arg type='u' name='"DLS_INTERFACE_UPLOAD_ID"'"
343 " direction='out'/>"
344 " <arg type='o' name='"DLS_INTERFACE_PATH"'"
345 " direction='out'/>"
346 " </method>"
347 " <method name='"DLS_INTERFACE_GET_UPLOAD_STATUS"'>"
348 " <arg type='u' name='"DLS_INTERFACE_UPLOAD_ID"'"
349 " direction='in'/>"
350 " <arg type='s' name='"DLS_INTERFACE_UPLOAD_STATUS"'"
351 " direction='out'/>"
352 " <arg type='t' name='"DLS_INTERFACE_LENGTH"'"
353 " direction='out'/>"
354 " <arg type='t' name='"DLS_INTERFACE_TOTAL"'"
355 " direction='out'/>"
356 " </method>"
357 " <method name='"DLS_INTERFACE_GET_UPLOAD_IDS"'>"
358 " <arg type='au' name='"DLS_INTERFACE_TOTAL"'"
359 " direction='out'/>"
360 " </method>"
361 " <method name='"DLS_INTERFACE_CANCEL_UPLOAD"'>"
362 " <arg type='u' name='"DLS_INTERFACE_UPLOAD_ID"'"
363 " direction='in'/>"
364 " </method>"
365 " <method name='"DLS_INTERFACE_CREATE_CONTAINER_IN_ANY"'>"
366 " <arg type='s' name='"DLS_INTERFACE_PROP_DISPLAY_NAME"'"
367 " direction='in'/>"
368 " <arg type='s' name='"DLS_INTERFACE_PROP_TYPE"'"
369 " direction='in'/>"
370 " <arg type='as' name='"DLS_INTERFACE_CHILD_TYPES"'"
371 " direction='in'/>"
372 " <arg type='o' name='"DLS_INTERFACE_PATH"'"
373 " direction='out'/>"
374 " </method>"
375 " <method name='"DLS_INTERFACE_CANCEL"'>"
376 " </method>"
377 " <method name='"DLS_INTERFACE_CREATE_PLAYLIST_TO_ANY"'>"
378 " <arg type='s' name='"DLS_INTERFACE_TITLE"'"
379 " direction='in'/>"
380 " <arg type='s' name='"DLS_INTERFACE_CREATOR"'"
381 " direction='in'/>"
382 " <arg type='s' name='"DLS_INTERFACE_GENRE"'"
383 " direction='in'/>"
384 " <arg type='s' name='"DLS_INTERFACE_DESCRIPTION"'"
385 " direction='in'/>"
386 " <arg type='ao' name='"DLS_INTERFACE_PLAYLIST_ITEMS"'"
387 " direction='in'/>"
388 " <arg type='u' name='"DLS_INTERFACE_UPLOAD_ID"'"
389 " direction='out'/>"
390 " <arg type='o' name='"DLS_INTERFACE_PATH"'"
391 " direction='out'/>"
392 " </method>"
393 " <property type='s' name='"DLS_INTERFACE_PROP_LOCATION"'"
394 " access='read'/>"
395 " <property type='s' name='"DLS_INTERFACE_PROP_UDN"'"
396 " access='read'/>"
397 " <property type='s' name='"DLS_INTERFACE_PROP_DEVICE_TYPE"'"
398 " access='read'/>"
399 " <property type='s' name='"DLS_INTERFACE_PROP_FRIENDLY_NAME"'"
400 " access='read'/>"
401 " <property type='s' name='"DLS_INTERFACE_PROP_MANUFACTURER"'"
402 " access='read'/>"
403 " <property type='s' name='"
404 DLS_INTERFACE_PROP_MANUFACTURER_URL"'"
405 " access='read'/>"
406 " <property type='s' name='"
407 DLS_INTERFACE_PROP_MODEL_DESCRIPTION"'"
408 " access='read'/>"
409 " <property type='s' name='"DLS_INTERFACE_PROP_MODEL_NAME"'"
410 " access='read'/>"
411 " <property type='s' name='"DLS_INTERFACE_PROP_MODEL_NUMBER"'"
412 " access='read'/>"
413 " <property type='s' name='"DLS_INTERFACE_PROP_MODEL_URL"'"
414 " access='read'/>"
415 " <property type='s' name='"DLS_INTERFACE_PROP_SERIAL_NUMBER"'"
416 " access='read'/>"
417 " <property type='s' name='"DLS_INTERFACE_PROP_PRESENTATION_URL"'"
418 " access='read'/>"
419 " <property type='s' name='"DLS_INTERFACE_PROP_ICON_URL"'"
420 " access='read'/>"
421 " <property type='a{sv}'name='"
422 DLS_INTERFACE_PROP_SV_DLNA_CAPABILITIES"'"
423 " access='read'/>"
424 " <property type='as' name='"
425 DLS_INTERFACE_PROP_SV_SEARCH_CAPABILITIES"'"
426 " access='read'/>"
427 " <property type='as' name='"
428 DLS_INTERFACE_PROP_SV_SORT_CAPABILITIES"'"
429 " access='read'/>"
430 " <property type='as' name='"
431 DLS_INTERFACE_PROP_SV_SORT_EXT_CAPABILITIES"'"
432 " access='read'/>"
433 " <property type='a(ssao)' name='"
434 DLS_INTERFACE_PROP_SV_FEATURE_LIST"'"
435 " access='read'/>"
436 " <property type='u' name='"
437 DLS_INTERFACE_PROP_ESV_SYSTEM_UPDATE_ID"'"
438 " access='read'/>"
439 " <property type='s' name='"
440 DLS_INTERFACE_PROP_SV_SERVICE_RESET_TOKEN"'"
441 " access='read'/>"
442 " <signal name='"DLS_INTERFACE_ESV_CONTAINER_UPDATE_IDS"'>"
443 " <arg type='a(ou)' name='"DLS_INTERFACE_CONTAINER_PATHS_ID"'/>"
444 " </signal>"
445 " <signal name='"DLS_INTERFACE_ESV_LAST_CHANGE"'>"
446 " <arg type='a(sv)' name='"
447 DLS_INTERFACE_LAST_CHANGE_STATE_EVENT"'/>"
448 " </signal>"
449 " <signal name='"DLS_INTERFACE_UPLOAD_UPDATE"'>"
450 " <arg type='u' name='"DLS_INTERFACE_UPLOAD_ID"'/>"
451 " <arg type='s' name='"DLS_INTERFACE_UPLOAD_STATUS"'/>"
452 " <arg type='t' name='"DLS_INTERFACE_LENGTH"'/>"
453 " <arg type='t' name='"DLS_INTERFACE_TOTAL"'/>"
454 " </signal>"
455 " </interface>"
456 "</node>";
457
458 const dleyna_connector_t *dls_server_get_connector(void)
459 {
460 return g_context.connector;
461 }
462
463 dleyna_task_processor_t *dls_server_get_task_processor(void)
464 {
465 return g_context.processor;
466 }
467
468 static void prv_sync_task_complete(dls_task_t *task)
469 {
470 dls_task_complete(task);
471 dleyna_task_queue_task_completed(task->atom.queue_id);
472 }
473
474 static void prv_process_sync_task(dls_task_t *task)
475 {
476 dls_client_t *client;
477 const gchar *client_name;
478
479 switch (task->type) {
480 case DLS_TASK_GET_VERSION:
481 prv_sync_task_complete(task);
482 break;
483 case DLS_TASK_GET_SERVERS:
484 task->result = dls_upnp_get_server_ids(g_context.upnp);
485 prv_sync_task_complete(task);
486 break;
487 case DLS_TASK_SET_PROTOCOL_INFO:
488 client_name = dleyna_task_queue_get_source(task->atom.queue_id);
489 client = g_hash_table_lookup(g_context.watchers, client_name);
490 if (client) {
491 g_free(client->protocol_info);
492 if (task->ut.protocol_info.protocol_info[0]) {
493 client->protocol_info =
494 task->ut.protocol_info.protocol_info;
495 task->ut.protocol_info.protocol_info = NULL;
496 } else {
497 client->protocol_info = NULL;
498 }
499 }
500 prv_sync_task_complete(task);
501 break;
502 case DLS_TASK_SET_PREFER_LOCAL_ADDRESSES:
503 client_name = dleyna_task_queue_get_source(task->atom.queue_id);
504 client = g_hash_table_lookup(g_context.watchers, client_name);
505 if (client) {
506 client->prefer_local_addresses =
507 task->ut.prefer_local_addresses.prefer;
508 }
509 prv_sync_task_complete(task);
510 break;
511 case DLS_TASK_GET_UPLOAD_STATUS:
512 dls_upnp_get_upload_status(g_context.upnp, task);
513 dleyna_task_queue_task_completed(task->atom.queue_id);
514 break;
515 case DLS_TASK_GET_UPLOAD_IDS:
516 dls_upnp_get_upload_ids(g_context.upnp, task);
517 dleyna_task_queue_task_completed(task->atom.queue_id);
518 break;
519 case DLS_TASK_CANCEL_UPLOAD:
520 dls_upnp_cancel_upload(g_context.upnp, task);
521 dleyna_task_queue_task_completed(task->atom.queue_id);
522 break;
523 default:
524 break;
525 }
526 }
527
528 static void prv_async_task_complete(dls_task_t *task, GError *error)
529 {
530 DLEYNA_LOG_DEBUG("Enter");
531
532 if (error) {
533 dls_task_fail(task, error);
534 g_error_free(error);
535 } else {
536 dls_task_complete(task);
537 }
538
539 dleyna_task_queue_task_completed(task->atom.queue_id);
540
541 DLEYNA_LOG_DEBUG("Exit");
542 }
543
544 static void prv_process_async_task(dls_task_t *task)
545 {
546 dls_async_task_t *async_task = (dls_async_task_t *)task;
547 dls_client_t *client;
548 const gchar *client_name;
549
550 DLEYNA_LOG_DEBUG("Enter");
551
552 async_task->cancellable = g_cancellable_new();
553 client_name = dleyna_task_queue_get_source(task->atom.queue_id);
554 client = g_hash_table_lookup(g_context.watchers, client_name);
555
556 switch (task->type) {
557 case DLS_TASK_GET_CHILDREN:
558 dls_upnp_get_children(g_context.upnp, client, task,
559 prv_async_task_complete);
560 break;
561 case DLS_TASK_GET_PROP:
562 dls_upnp_get_prop(g_context.upnp, client, task,
563 prv_async_task_complete);
564 break;
565 case DLS_TASK_GET_ALL_PROPS:
566 dls_upnp_get_all_props(g_context.upnp, client, task,
567 prv_async_task_complete);
568 break;
569 case DLS_TASK_SEARCH:
570 dls_upnp_search(g_context.upnp, client, task,
571 prv_async_task_complete);
572 break;
573 case DLS_TASK_GET_RESOURCE:
574 dls_upnp_get_resource(g_context.upnp, client, task,
575 prv_async_task_complete);
576 break;
577 case DLS_TASK_UPLOAD_TO_ANY:
578 dls_upnp_upload_to_any(g_context.upnp, client, task,
579 prv_async_task_complete);
580 break;
581 case DLS_TASK_UPLOAD:
582 dls_upnp_upload(g_context.upnp, client, task,
583 prv_async_task_complete);
584 break;
585 case DLS_TASK_DELETE_OBJECT:
586 dls_upnp_delete_object(g_context.upnp, client, task,
587 prv_async_task_complete);
588 break;
589 case DLS_TASK_CREATE_CONTAINER:
590 dls_upnp_create_container(g_context.upnp, client, task,
591 prv_async_task_complete);
592 break;
593 case DLS_TASK_CREATE_CONTAINER_IN_ANY:
594 dls_upnp_create_container_in_any(g_context.upnp, client, task,
595 prv_async_task_complete);
596 break;
597 case DLS_TASK_UPDATE_OBJECT:
598 dls_upnp_update_object(g_context.upnp, client, task,
599 prv_async_task_complete);
600 break;
601 case DLS_TASK_CREATE_PLAYLIST:
602 dls_upnp_create_playlist(g_context.upnp, client, task,
603 prv_async_task_complete);
604 break;
605 case DLS_TASK_CREATE_PLAYLIST_IN_ANY:
606 dls_upnp_create_playlist_in_any(g_context.upnp, client, task,
607 prv_async_task_complete);
608 break;
609 default:
610 break;
611 }
612
613 DLEYNA_LOG_DEBUG("Exit");
614 }
615
616 static void prv_process_task(dleyna_task_atom_t *task, gpointer user_data)
617 {
618 dls_task_t *client_task = (dls_task_t *)task;
619
620 if (client_task->synchronous)
621 prv_process_sync_task(client_task);
622 else
623 prv_process_async_task(client_task);
624 }
625
626 static void prv_cancel_task(dleyna_task_atom_t *task, gpointer user_data)
627 {
628 dls_task_cancel((dls_task_t *)task);
629 }
630
631 static void prv_delete_task(dleyna_task_atom_t *task, gpointer user_data)
632 {
633 dls_task_delete((dls_task_t *)task);
634 }
635
636 static void prv_method_call(dleyna_connector_id_t conn,
637 const gchar *sender,
638 const gchar *object,
639 const gchar *interface,
640 const gchar *method,
641 GVariant *parameters,
642 dleyna_connector_msg_id_t invocation);
643
644 static void prv_object_method_call(dleyna_connector_id_t conn,
645 const gchar *sender,
646 const gchar *object,
647 const gchar *interface,
648 const gchar *method,
649 GVariant *parameters,
650 dleyna_connector_msg_id_t invocation);
651
652 static void prv_item_method_call(dleyna_connector_id_t conn,
653 const gchar *sender,
654 const gchar *object,
655 const gchar *interface,
656 const gchar *method,
657 GVariant *parameters,
658 dleyna_connector_msg_id_t invocation);
659
660 static void prv_con_method_call(dleyna_connector_id_t conn,
661 const gchar *sender,
662 const gchar *object,
663 const gchar *interface,
664 const gchar *method,
665 GVariant *parameters,
666 dleyna_connector_msg_id_t invocation);
667
668 static void prv_props_method_call(dleyna_connector_id_t conn,
669 const gchar *sender,
670 const gchar *object,
671 const gchar *interface,
672 const gchar *method,
673 GVariant *parameters,
674 dleyna_connector_msg_id_t invocation);
675
676 static void prv_device_method_call(dleyna_connector_id_t conn,
677 const gchar *sender,
678 const gchar *object,
679 const gchar *interface,
680 const gchar *method,
681 GVariant *parameters,
682 dleyna_connector_msg_id_t invocation);
683
684 static const dleyna_connector_dispatch_cb_t g_root_vtables[1] = {
685 prv_method_call
686 };
687
688 static const dleyna_connector_dispatch_cb_t
689 g_server_vtables[DLS_INTERFACE_INFO_MAX] = {
690 /* MUST be in the exact same order as g_dls_server_introspection */
691 prv_props_method_call,
692 prv_object_method_call,
693 prv_con_method_call,
694 prv_item_method_call,
695 prv_device_method_call
696 };
697
698 static void prv_remove_client(const gchar *name)
699 {
700 dleyna_task_processor_remove_queues_for_source(g_context.processor,
701 name);
702
703 (void) g_hash_table_remove(g_context.watchers, name);
704
705 if (g_hash_table_size(g_context.watchers) == 0)
706 if (!dleyna_settings_is_never_quit(g_context.settings))
707 dleyna_task_processor_set_quitting(g_context.processor);
708 }
709
710 static void prv_lost_client(const gchar *name)
711 {
712 DLEYNA_LOG_DEBUG("Lost Client %s", name);
713
714 prv_remove_client(name);
715 }
716
717 static void prv_add_task(dls_task_t *task, const gchar *source,
718 const gchar *sink)
719 {
720 dls_client_t *client;
721 const dleyna_task_queue_key_t *queue_id;
722
723 if (!g_hash_table_lookup(g_context.watchers, source)) {
724 client = g_new0(dls_client_t, 1);
725 client->prefer_local_addresses = TRUE;
726 g_context.connector->watch_client(source);
727 g_hash_table_insert(g_context.watchers, g_strdup(source),
728 client);
729 }
730
731 queue_id = dleyna_task_processor_lookup_queue(g_context.processor,
732 source, sink);
733 if (!queue_id)
734 queue_id = dleyna_task_processor_add_queue(
735 g_context.processor,
736 source,
737 sink,
738 DLEYNA_TASK_QUEUE_FLAG_AUTO_START,
739 prv_process_task,
740 prv_cancel_task,
741 prv_delete_task);
742
743 dleyna_task_queue_add_task(queue_id, &task->atom);
744 }
745
746 static void prv_method_call(dleyna_connector_id_t conn,
747 const gchar *sender, const gchar *object,
748 const gchar *interface,
749 const gchar *method, GVariant *parameters,
750 dleyna_connector_msg_id_t invocation)
751 {
752 dls_task_t *task;
753
754 if (!strcmp(method, DLS_INTERFACE_RELEASE)) {
755 prv_remove_client(sender);
756 g_context.connector->return_response(invocation, NULL);
757 } else if (!strcmp(method, DLS_INTERFACE_GET_VERSION)) {
758 task = dls_task_get_version_new(invocation);
759 prv_add_task(task, sender, DLS_SERVER_SINK);
760 } else if (!strcmp(method, DLS_INTERFACE_GET_SERVERS)) {
761 task = dls_task_get_servers_new(invocation);
762 prv_add_task(task, sender, DLS_SERVER_SINK);
763 } else if (!strcmp(method, DLS_INTERFACE_SET_PROTOCOL_INFO)) {
764 task = dls_task_set_protocol_info_new(invocation,
765 parameters);
766 prv_add_task(task, sender, DLS_SERVER_SINK);
767 } else if (!strcmp(method, DLS_INTERFACE_PREFER_LOCAL_ADDRESSES)) {
768 task = dls_task_prefer_local_addresses_new(invocation,
769 parameters);
770 prv_add_task(task, sender, DLS_SERVER_SINK);
771 }
772 }
773
774 gboolean dls_server_get_object_info(const gchar *object_path,
775 gchar **root_path,
776 gchar **object_id,
777 dls_device_t **device,
778 GError **error)
779 {
780 if (!dls_path_get_path_and_id(object_path, root_path, object_id,
781 error)) {
782 DLEYNA_LOG_WARNING("Bad object %s", object_path);
783
784 goto on_error;
785 }
786
787 *device = dls_device_from_path(*root_path,
788 dls_upnp_get_server_udn_map(g_context.upnp));
789
790 if (*device == NULL) {
791 DLEYNA_LOG_WARNING("Cannot locate device for %s", *root_path);
792
793 *error = g_error_new(DLEYNA_SERVER_ERROR,
794 DLEYNA_ERROR_OBJECT_NOT_FOUND,
795 "Cannot locate device corresponding to"
796 " the specified path");
797
798 g_free(*root_path);
799 g_free(*object_id);
800
801 goto on_error;
802 }
803
804 return TRUE;
805
806 on_error:
807
808 return FALSE;
809 }
810
811 static const gchar *prv_get_device_id(const gchar *object, GError **error)
812 {
813 dls_device_t *device;
814 gchar *root_path;
815 gchar *id;
816
817 if (!dls_server_get_object_info(object, &root_path, &id, &device,
818 error))
819 goto on_error;
820
821 g_free(id);
822 g_free(root_path);
823
824 return device->path;
825
826 on_error:
827
828 return NULL;
829 }
830
831 static void prv_object_method_call(dleyna_connector_id_t conn,
832 const gchar *sender, const gchar *object,
833 const gchar *interface,
834 const gchar *method, GVariant *parameters,
835 dleyna_connector_msg_id_t invocation)
836 {
837 dls_task_t *task;
838 GError *error = NULL;
839
840 if (!strcmp(method, DLS_INTERFACE_DELETE))
841 task = dls_task_delete_new(invocation, object, &error);
842 else if (!strcmp(method, DLS_INTERFACE_UPDATE))
843 task = dls_task_update_new(invocation, object,
844 parameters, &error);
845 else
846 goto finished;
847
848 if (!task) {
849 g_context.connector->return_error(invocation, error);
850 g_error_free(error);
851
852 goto finished;
853 }
854
855 prv_add_task(task, sender, task->target.device->path);
856
857 finished:
858
859 return;
860 }
861
862 static void prv_item_method_call(dleyna_connector_id_t conn,
863 const gchar *sender, const gchar *object,
864 const gchar *interface,
865 const gchar *method, GVariant *parameters,
866 dleyna_connector_msg_id_t invocation)
867 {
868 dls_task_t *task;
869 GError *error = NULL;
870
871 if (!strcmp(method, DLS_INTERFACE_GET_COMPATIBLE_RESOURCE)) {
872 task = dls_task_get_resource_new(invocation, object,
873 parameters, &error);
874
875 if (!task) {
876 g_context.connector->return_error(invocation, error);
877 g_error_free(error);
878
879 goto finished;
880 }
881
882 prv_add_task(task, sender, task->target.device->path);
883 }
884
885 finished:
886
887 return;
888 }
889
890
891 static void prv_con_method_call(dleyna_connector_id_t conn,
892 const gchar *sender,
893 const gchar *object,
894 const gchar *interface,
895 const gchar *method,
896 GVariant *parameters,
897 dleyna_connector_msg_id_t invocation)
898 {
899 dls_task_t *task;
900 GError *error = NULL;
901
902 if (!strcmp(method, DLS_INTERFACE_LIST_CHILDREN))
903 task = dls_task_get_children_new(invocation, object,
904 parameters, TRUE,
905 TRUE, &error);
906 else if (!strcmp(method, DLS_INTERFACE_LIST_CHILDREN_EX))
907 task = dls_task_get_children_ex_new(invocation, object,
908 parameters, TRUE,
909 TRUE, &error);
910 else if (!strcmp(method, DLS_INTERFACE_LIST_ITEMS))
911 task = dls_task_get_children_new(invocation, object,
912 parameters, TRUE,
913 FALSE, &error);
914 else if (!strcmp(method, DLS_INTERFACE_LIST_ITEMS_EX))
915 task = dls_task_get_children_ex_new(invocation, object,
916 parameters, TRUE,
917 FALSE, &error);
918 else if (!strcmp(method, DLS_INTERFACE_LIST_CONTAINERS))
919 task = dls_task_get_children_new(invocation, object,
920 parameters, FALSE,
921 TRUE, &error);
922 else if (!strcmp(method, DLS_INTERFACE_LIST_CONTAINERS_EX))
923 task = dls_task_get_children_ex_new(invocation, object,
924 parameters, FALSE,
925 TRUE, &error);
926 else if (!strcmp(method, DLS_INTERFACE_SEARCH_OBJECTS))
927 task = dls_task_search_new(invocation, object,
928 parameters, &error);
929 else if (!strcmp(method, DLS_INTERFACE_SEARCH_OBJECTS_EX))
930 task = dls_task_search_ex_new(invocation, object,
931 parameters, &error);
932 else if (!strcmp(method, DLS_INTERFACE_UPLOAD))
933 task = dls_task_upload_new(invocation, object,
934 parameters, &error);
935 else if (!strcmp(method, DLS_INTERFACE_CREATE_CONTAINER))
936 task = dls_task_create_container_new_generic(invocation,
937 DLS_TASK_CREATE_CONTAINER,
938 object, parameters, &error);
939 else if (!strcmp(method, DLS_INTERFACE_CREATE_PLAYLIST))
940 task = dls_task_create_playlist_new(invocation,
941 DLS_TASK_CREATE_PLAYLIST,
942 object, parameters, &error);
943 else
944 goto finished;
945
946 if (!task) {
947 g_context.connector->return_error(invocation, error);
948 g_error_free(error);
949
950 goto finished;
951 }
952
953 prv_add_task(task, sender, task->target.device->path);
954
955 finished:
956
957 return;
958 }
959
960 static void prv_props_method_call(dleyna_connector_id_t conn,
961 const gchar *sender,
962 const gchar *object,
963 const gchar *interface,
964 const gchar *method,
965 GVariant *parameters,
966 dleyna_connector_msg_id_t invocation)
967 {
968 dls_task_t *task;
969 GError *error = NULL;
970
971 if (!strcmp(method, DLS_INTERFACE_GET_ALL))
972 task = dls_task_get_props_new(invocation, object,
973 parameters, &error);
974 else if (!strcmp(method, DLS_INTERFACE_GET))
975 task = dls_task_get_prop_new(invocation, object,
976 parameters, &error);
977 else
978 goto finished;
979
980 if (!task) {
981 g_context.connector->return_error(invocation, error);
982 g_error_free(error);
983
984 goto finished;
985 }
986
987 prv_add_task(task, sender, task->target.device->path);
988
989 finished:
990
991 return;
992 }
993
994 static void prv_device_method_call(dleyna_connector_id_t conn,
995 const gchar *sender, const gchar *object,
996 const gchar *interface,
997 const gchar *method, GVariant *parameters,
998 dleyna_connector_msg_id_t invocation)
999 {
1000 dls_task_t *task;
1001 GError *error = NULL;
1002 const gchar *device_id;
1003 const dleyna_task_queue_key_t *queue_id;
1004
1005 if (!strcmp(method, DLS_INTERFACE_UPLOAD_TO_ANY)) {
1006 task = dls_task_upload_to_any_new(invocation,
1007 object, parameters, &error);
1008 } else if (!strcmp(method, DLS_INTERFACE_CREATE_CONTAINER_IN_ANY)) {
1009 task = dls_task_create_container_new_generic(
1010 invocation,
1011 DLS_TASK_CREATE_CONTAINER_IN_ANY,
1012 object, parameters, &error);
1013 } else if (!strcmp(method, DLS_INTERFACE_GET_UPLOAD_STATUS)) {
1014 task = dls_task_get_upload_status_new(invocation,
1015 object, parameters,
1016 &error);
1017 } else if (!strcmp(method, DLS_INTERFACE_GET_UPLOAD_IDS)) {
1018 task = dls_task_get_upload_ids_new(invocation, object,
1019 &error);
1020 } else if (!strcmp(method, DLS_INTERFACE_CANCEL_UPLOAD)) {
1021 task = dls_task_cancel_upload_new(invocation, object,
1022 parameters, &error);
1023 } else if (!strcmp(method, DLS_INTERFACE_CREATE_PLAYLIST_TO_ANY)) {
1024 task = dls_task_create_playlist_new(
1025 invocation,
1026 DLS_TASK_CREATE_PLAYLIST_IN_ANY,
1027 object, parameters, &error);
1028 } else if (!strcmp(method, DLS_INTERFACE_CANCEL)) {
1029 task = NULL;
1030
1031 device_id = prv_get_device_id(object, &error);
1032 if (!device_id)
1033 goto on_error;
1034
1035 queue_id = dleyna_task_processor_lookup_queue(
1036 g_context.processor,
1037 sender,
1038 device_id);
1039 if (queue_id)
1040 dleyna_task_processor_cancel_queue(queue_id);
1041
1042 g_context.connector->return_response(invocation, NULL);
1043
1044 goto finished;
1045 } else {
1046 goto finished;
1047 }
1048
1049 on_error:
1050
1051 if (!task) {
1052 g_context.connector->return_error(invocation, error);
1053 g_error_free(error);
1054
1055 goto finished;
1056 }
1057
1058 prv_add_task(task, sender, task->target.device->path);
1059
1060 finished:
1061
1062 return;
1063 }
1064
1065 static void prv_found_media_server(const gchar *path, void *user_data)
1066 {
1067 (void) g_context.connector->notify(g_context.connection,
1068 DLEYNA_SERVER_OBJECT,
1069 DLEYNA_SERVER_INTERFACE_MANAGER,
1070 DLS_INTERFACE_FOUND_SERVER,
1071 g_variant_new("(o)", path),
1072 NULL);
1073 }
1074
1075 static void prv_lost_media_server(const gchar *path, void *user_data)
1076 {
1077 (void) g_context.connector->notify(g_context.connection,
1078 DLEYNA_SERVER_OBJECT,
1079 DLEYNA_SERVER_INTERFACE_MANAGER,
1080 DLS_INTERFACE_LOST_SERVER,
1081 g_variant_new("(o)", path),
1082 NULL);
1083
1084 dleyna_task_processor_remove_queues_for_sink(g_context.processor, path);
1085 }
1086
1087 static void prv_unregister_client(gpointer user_data)
1088 {
1089 dls_client_t *client = user_data;
1090
1091 if (client) {
1092 g_free(client->protocol_info);
1093 g_free(client);
1094 }
1095 }
1096
1097 dls_upnp_t *dls_server_get_upnp(void)
1098 {
1099 return g_context.upnp;
1100 }
1101
1102 static gboolean prv_control_point_start_service(
1103 dleyna_connector_id_t connection)
1104 {
1105 gboolean retval = TRUE;
1106
1107 g_context.connection = connection;
1108
1109 g_context.dls_id = g_context.connector->publish_object(
1110 connection,
1111 DLEYNA_SERVER_OBJECT,
1112 TRUE,
1113 0,
1114 g_root_vtables);
1115
1116 if (!g_context.dls_id) {
1117 retval = FALSE;
1118 goto out;
1119 } else {
1120 g_context.upnp = dls_upnp_new(connection,
1121 g_server_vtables,
1122 prv_found_media_server,
1123 prv_lost_media_server,
1124 NULL);
1125 }
1126
1127 out:
1128
1129 return retval;
1130 }
1131
1132 static void prv_control_point_stop_service(void)
1133 {
1134 dls_upnp_unsubscribe(g_context.upnp);
1135
1136 dls_upnp_delete(g_context.upnp);
1137
1138 if (g_context.connection) {
1139 if (g_context.dls_id)
1140 g_context.connector->unpublish_object(
1141 g_context.connection,
1142 g_context.dls_id);
1143 }
1144 }
1145
1146 static void prv_control_point_initialize(const dleyna_connector_t *connector,
1147 dleyna_task_processor_t *processor,
1148 dleyna_settings_t *settings)
1149 {
1150 memset(&g_context, 0, sizeof(g_context));
1151
1152 g_context.connector = connector;
1153 g_context.processor = processor;
1154 g_context.settings = settings;
1155
1156 g_context.connector->set_client_lost_cb(prv_lost_client);
1157
1158 g_context.watchers = g_hash_table_new_full(g_str_hash, g_str_equal,
1159 g_free, prv_unregister_client);
1160
1161 g_set_prgname(DLS_PRG_NAME);
1162 }
1163
1164 static void prv_control_point_free(void)
1165 {
1166 if (g_context.watchers)
1167 g_hash_table_unref(g_context.watchers);
1168 }
1169
1170 static const gchar *prv_control_point_server_name(void)
1171 {
1172 return DLEYNA_SERVER_NAME;
1173 }
1174
1175 static const gchar *prv_control_point_server_introspection(void)
1176 {
1177 return g_server_introspection;
1178 }
1179
1180 static const gchar *prv_control_point_root_introspection(void)
1181 {
1182 return g_root_introspection;
1183 }
1184
1185 static const dleyna_control_point_t g_control_point = {
1186 prv_control_point_initialize,
1187 prv_control_point_free,
1188 prv_control_point_server_name,
1189 prv_control_point_server_introspection,
1190 prv_control_point_root_introspection,
1191 prv_control_point_start_service,
1192 prv_control_point_stop_service
1193 };
1194
1195 const dleyna_control_point_t *dleyna_control_point_get_server(void)
1196 {
1197 return &g_control_point;
1198 }
+0
-47
lib/server.h less more
0 /*
1 * dLeyna
2 *
3 * Copyright (C) 2013 Intel Corporation. All rights reserved.
4 *
5 * This program is free software; you can redistribute it and/or modify it
6 * under the terms and conditions of the GNU Lesser General Public License,
7 * version 2.1, as published by the Free Software Foundation.
8 *
9 * This program is distributed in the hope it will be useful, but WITHOUT
10 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
11 * FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License
12 * for more details.
13 *
14 * You should have received a copy of the GNU Lesser General Public License
15 * along with this program; if not, write to the Free Software Foundation, Inc.,
16 * 51 Franklin St - Fifth Floor, Boston, MA 02110-1301 USA.
17 *
18 * Regis Merlino <regis.merlino@intel.com>
19 *
20 */
21
22 #ifndef DLS_SERVER_H__
23 #define DLS_SERVER_H__
24
25 #include <libdleyna/core/connector.h>
26 #include <libdleyna/core/task-processor.h>
27
28 #define DLS_SERVER_SINK "dleyna-server"
29
30 typedef struct dls_device_t_ dls_device_t;
31 typedef struct dls_device_context_t_ dls_device_context_t;
32 typedef struct dls_upnp_t_ dls_upnp_t;
33
34 gboolean dls_server_get_object_info(const gchar *object_path,
35 gchar **root_path,
36 gchar **object_id,
37 dls_device_t **device,
38 GError **error);
39
40 dls_upnp_t *dls_server_get_upnp(void);
41
42 dleyna_task_processor_t *dls_server_get_task_processor(void);
43
44 const dleyna_connector_t *dls_server_get_connector(void);
45
46 #endif /* DLS_SERVER_H__ */
+0
-98
lib/sort.c less more
0 /*
1 * dLeyna
2 *
3 * Copyright (C) 2012-2013 Intel Corporation. All rights reserved.
4 *
5 * This program is free software; you can redistribute it and/or modify it
6 * under the terms and conditions of the GNU Lesser General Public License,
7 * version 2.1, as published by the Free Software Foundation.
8 *
9 * This program is distributed in the hope it will be useful, but WITHOUT
10 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
11 * FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License
12 * for more details.
13 *
14 * You should have received a copy of the GNU Lesser General Public License
15 * along with this program; if not, write to the Free Software Foundation, Inc.,
16 * 51 Franklin St - Fifth Floor, Boston, MA 02110-1301 USA.
17 *
18 * Mark Ryan <mark.d.ryan@intel.com>
19 *
20 */
21
22 #include <string.h>
23
24 #include "props.h"
25 #include "sort.h"
26
27 gchar *dls_sort_translate_sort_string(GHashTable *filter_map,
28 const gchar *sort_string)
29 {
30 GRegex *reg;
31 gchar *retval = NULL;
32 GMatchInfo *match_info = NULL;
33 gchar *prop = NULL;
34 gchar *op = NULL;
35 dls_prop_map_t *prop_map;
36 GString *str;
37
38 if (!g_regex_match_simple(
39 "^((\\+|\\-)([^,\\+\\-]+))?(,(\\+|\\-)([^,\\+\\-]+))*$",
40 sort_string, 0, 0))
41 goto no_free;
42
43 reg = g_regex_new("(\\+|\\-)(\\w+)", 0, 0, NULL);
44 str = g_string_new("");
45
46 g_regex_match(reg, sort_string, 0, &match_info);
47 while (g_match_info_matches(match_info)) {
48 op = g_match_info_fetch(match_info, 1);
49 if (!op)
50 goto on_error;
51
52 prop = g_match_info_fetch(match_info, 2);
53 if (!prop)
54 goto on_error;
55
56 prop_map = g_hash_table_lookup(filter_map, prop);
57 if (!prop_map)
58 goto on_error;
59
60 if (!prop_map->searchable)
61 goto on_error;
62
63 g_string_append_printf(str, "%s%s,", op,
64 prop_map->upnp_prop_name);
65
66 g_free(prop);
67 g_free(op);
68
69 prop = NULL;
70 op = NULL;
71
72 g_match_info_next(match_info, NULL);
73 }
74
75 if (str->len > 0)
76 str = g_string_truncate(str, str->len - 1);
77 retval = g_string_free(str, FALSE);
78
79 str = NULL;
80
81 on_error:
82
83 g_free(prop);
84 g_free(op);
85
86 if (match_info)
87 g_match_info_free(match_info);
88
89 if (str)
90 g_string_free(str, TRUE);
91
92 g_regex_unref(reg);
93
94 no_free:
95
96 return retval;
97 }
+0
-31
lib/sort.h less more
0 /*
1 * dLeyna
2 *
3 * Copyright (C) 2012-2013 Intel Corporation. All rights reserved.
4 *
5 * This program is free software; you can redistribute it and/or modify it
6 * under the terms and conditions of the GNU Lesser General Public License,
7 * version 2.1, as published by the Free Software Foundation.
8 *
9 * This program is distributed in the hope it will be useful, but WITHOUT
10 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
11 * FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License
12 * for more details.
13 *
14 * You should have received a copy of the GNU Lesser General Public License
15 * along with this program; if not, write to the Free Software Foundation, Inc.,
16 * 51 Franklin St - Fifth Floor, Boston, MA 02110-1301 USA.
17 *
18 * Mark Ryan <mark.d.ryan@intel.com>
19 *
20 */
21
22 #ifndef DLS_SORT_H__
23 #define DLS_SORT_H__
24
25 #include <glib.h>
26
27 gchar *dls_sort_translate_sort_string(GHashTable *filter_map,
28 const gchar *sort_string);
29
30 #endif /* DLS_SORT_H__ */
+0
-618
lib/task.c less more
0 /*
1 * dLeyna
2 *
3 * Copyright (C) 2012-2013 Intel Corporation. All rights reserved.
4 *
5 * This program is free software; you can redistribute it and/or modify it
6 * under the terms and conditions of the GNU Lesser General Public License,
7 * version 2.1, as published by the Free Software Foundation.
8 *
9 * This program is distributed in the hope it will be useful, but WITHOUT
10 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
11 * FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License
12 * for more details.
13 *
14 * You should have received a copy of the GNU Lesser General Public License
15 * along with this program; if not, write to the Free Software Foundation, Inc.,
16 * 51 Franklin St - Fifth Floor, Boston, MA 02110-1301 USA.
17 *
18 * Mark Ryan <mark.d.ryan@intel.com>
19 *
20 */
21
22 #include <libdleyna/core/error.h>
23
24 #include "async.h"
25
26 dls_task_t *dls_task_get_version_new(dleyna_connector_msg_id_t invocation)
27 {
28 dls_task_t *task = g_new0(dls_task_t, 1);
29
30 task->type = DLS_TASK_GET_VERSION;
31 task->invocation = invocation;
32 task->result_format = "(@s)";
33 task->result = g_variant_ref_sink(g_variant_new_string(VERSION));
34 task->synchronous = TRUE;
35
36 return task;
37 }
38
39 dls_task_t *dls_task_get_servers_new(dleyna_connector_msg_id_t invocation)
40 {
41 dls_task_t *task = g_new0(dls_task_t, 1);
42
43 task->type = DLS_TASK_GET_SERVERS;
44 task->invocation = invocation;
45 task->result_format = "(@ao)";
46 task->synchronous = TRUE;
47
48 return task;
49 }
50
51 static void prv_delete(dls_task_t *task)
52 {
53 if (!task->synchronous)
54 dls_async_task_delete((dls_async_task_t *)task);
55
56 switch (task->type) {
57 case DLS_TASK_GET_CHILDREN:
58 if (task->ut.get_children.filter)
59 g_variant_unref(task->ut.get_children.filter);
60 g_free(task->ut.get_children.sort_by);
61 break;
62 case DLS_TASK_GET_ALL_PROPS:
63 g_free(task->ut.get_props.interface_name);
64 break;
65 case DLS_TASK_GET_PROP:
66 g_free(task->ut.get_prop.interface_name);
67 g_free(task->ut.get_prop.prop_name);
68 break;
69 case DLS_TASK_SEARCH:
70 g_free(task->ut.search.query);
71 if (task->ut.search.filter)
72 g_variant_unref(task->ut.search.filter);
73 g_free(task->ut.search.sort_by);
74 break;
75 case DLS_TASK_GET_RESOURCE:
76 if (task->ut.resource.filter)
77 g_variant_unref(task->ut.resource.filter);
78 g_free(task->ut.resource.protocol_info);
79 break;
80 case DLS_TASK_SET_PROTOCOL_INFO:
81 if (task->ut.protocol_info.protocol_info)
82 g_free(task->ut.protocol_info.protocol_info);
83 break;
84 case DLS_TASK_UPLOAD_TO_ANY:
85 case DLS_TASK_UPLOAD:
86 g_free(task->ut.upload.display_name);
87 g_free(task->ut.upload.file_path);
88 break;
89 case DLS_TASK_CREATE_CONTAINER:
90 case DLS_TASK_CREATE_CONTAINER_IN_ANY:
91 g_free(task->ut.create_container.display_name);
92 g_free(task->ut.create_container.type);
93 g_variant_unref(task->ut.create_container.child_types);
94 break;
95 case DLS_TASK_UPDATE_OBJECT:
96 if (task->ut.update.to_add_update)
97 g_variant_unref(task->ut.update.to_add_update);
98 if (task->ut.update.to_delete)
99 g_variant_unref(task->ut.update.to_delete);
100 break;
101 case DLS_TASK_CREATE_PLAYLIST:
102 case DLS_TASK_CREATE_PLAYLIST_IN_ANY:
103 g_free(task->ut.playlist.title);
104 g_free(task->ut.playlist.creator);
105 g_free(task->ut.playlist.genre);
106 g_free(task->ut.playlist.desc);
107 if (task->ut.playlist.item_path)
108 g_variant_unref(task->ut.playlist.item_path);
109 break;
110 default:
111 break;
112 }
113
114 g_free(task->target.path);
115 g_free(task->target.root_path);
116 g_free(task->target.id);
117
118 if (task->result)
119 g_variant_unref(task->result);
120
121 g_free(task);
122 }
123
124 static gboolean prv_set_task_target_info(dls_task_t *task, const gchar *path,
125 GError **error)
126 {
127 task->target.path = g_strdup(path);
128 g_strstrip(task->target.path);
129
130 return dls_server_get_object_info(path, &task->target.root_path,
131 &task->target.id,
132 &task->target.device, error);
133 }
134
135 static dls_task_t *prv_m2spec_task_new(dls_task_type_t type,
136 dleyna_connector_msg_id_t invocation,
137 const gchar *path,
138 const gchar *result_format,
139 GError **error,
140 gboolean synchronous)
141 {
142 dls_task_t *task;
143
144 if (synchronous) {
145 task = g_new0(dls_task_t, 1);
146 task->synchronous = TRUE;
147 } else {
148 task = (dls_task_t *)g_new0(dls_async_task_t, 1);
149 }
150
151 if (!prv_set_task_target_info(task, path, error)) {
152 prv_delete(task);
153 task = NULL;
154
155 goto finished;
156 }
157
158 task->type = type;
159 task->invocation = invocation;
160 task->result_format = result_format;
161
162 finished:
163
164 return task;
165 }
166
167 dls_task_t *dls_task_get_children_new(dleyna_connector_msg_id_t invocation,
168 const gchar *path, GVariant *parameters,
169 gboolean items, gboolean containers,
170 GError **error)
171 {
172 dls_task_t *task;
173
174 task = prv_m2spec_task_new(DLS_TASK_GET_CHILDREN, invocation, path,
175 "(@aa{sv})", error, FALSE);
176 if (!task)
177 goto finished;
178
179 task->ut.get_children.containers = containers;
180 task->ut.get_children.items = items;
181
182 g_variant_get(parameters, "(uu@as)",
183 &task->ut.get_children.start,
184 &task->ut.get_children.count,
185 &task->ut.get_children.filter);
186
187 task->ut.get_children.sort_by = g_strdup("");
188
189 finished:
190
191 return task;
192 }
193
194 dls_task_t *dls_task_get_children_ex_new(dleyna_connector_msg_id_t invocation,
195 const gchar *path,
196 GVariant *parameters, gboolean items,
197 gboolean containers,
198 GError **error)
199 {
200 dls_task_t *task;
201
202 task = prv_m2spec_task_new(DLS_TASK_GET_CHILDREN, invocation, path,
203 "(@aa{sv})", error, FALSE);
204 if (!task)
205 goto finished;
206
207 task->ut.get_children.containers = containers;
208 task->ut.get_children.items = items;
209
210 g_variant_get(parameters, "(uu@ass)",
211 &task->ut.get_children.start,
212 &task->ut.get_children.count,
213 &task->ut.get_children.filter,
214 &task->ut.get_children.sort_by);
215
216 finished:
217
218 return task;
219 }
220
221 dls_task_t *dls_task_get_prop_new(dleyna_connector_msg_id_t invocation,
222 const gchar *path, GVariant *parameters,
223 GError **error)
224 {
225 dls_task_t *task;
226
227 task = prv_m2spec_task_new(DLS_TASK_GET_PROP, invocation, path, "(v)",
228 error, FALSE);
229 if (!task)
230 goto finished;
231
232 g_variant_get(parameters, "(ss)", &task->ut.get_prop.interface_name,
233 &task->ut.get_prop.prop_name);
234
235 g_strstrip(task->ut.get_prop.interface_name);
236 g_strstrip(task->ut.get_prop.prop_name);
237
238 finished:
239
240 return task;
241 }
242
243 dls_task_t *dls_task_get_props_new(dleyna_connector_msg_id_t invocation,
244 const gchar *path, GVariant *parameters,
245 GError **error)
246 {
247 dls_task_t *task;
248
249 task = prv_m2spec_task_new(DLS_TASK_GET_ALL_PROPS, invocation, path,
250 "(@a{sv})", error, FALSE);
251 if (!task)
252 goto finished;
253
254 g_variant_get(parameters, "(s)", &task->ut.get_props.interface_name);
255 g_strstrip(task->ut.get_props.interface_name);
256
257 finished:
258
259 return task;
260 }
261
262 dls_task_t *dls_task_search_new(dleyna_connector_msg_id_t invocation,
263 const gchar *path, GVariant *parameters,
264 GError **error)
265 {
266 dls_task_t *task;
267
268 task = prv_m2spec_task_new(DLS_TASK_SEARCH, invocation, path,
269 "(@aa{sv})", error, FALSE);
270 if (!task)
271 goto finished;
272
273 g_variant_get(parameters, "(suu@as)", &task->ut.search.query,
274 &task->ut.search.start, &task->ut.search.count,
275 &task->ut.search.filter);
276
277 task->ut.search.sort_by = g_strdup("");
278
279 finished:
280 return task;
281 }
282
283 dls_task_t *dls_task_search_ex_new(dleyna_connector_msg_id_t invocation,
284 const gchar *path, GVariant *parameters,
285 GError **error)
286 {
287 dls_task_t *task;
288
289 task = prv_m2spec_task_new(DLS_TASK_SEARCH, invocation, path,
290 "(@aa{sv}u)", error, FALSE);
291 if (!task)
292 goto finished;
293
294 g_variant_get(parameters, "(suu@ass)", &task->ut.search.query,
295 &task->ut.search.start, &task->ut.search.count,
296 &task->ut.search.filter, &task->ut.search.sort_by);
297
298 task->multiple_retvals = TRUE;
299
300 finished:
301
302 return task;
303 }
304
305 dls_task_t *dls_task_get_resource_new(dleyna_connector_msg_id_t invocation,
306 const gchar *path, GVariant *parameters,
307 GError **error)
308 {
309 dls_task_t *task;
310
311 task = prv_m2spec_task_new(DLS_TASK_GET_RESOURCE, invocation, path,
312 "(@a{sv})", error, FALSE);
313 if (!task)
314 goto finished;
315
316 g_variant_get(parameters, "(s@as)",
317 &task->ut.resource.protocol_info,
318 &task->ut.resource.filter);
319
320 finished:
321
322 return task;
323 }
324
325 dls_task_t *dls_task_set_protocol_info_new(dleyna_connector_msg_id_t invocation,
326 GVariant *parameters)
327 {
328 dls_task_t *task = g_new0(dls_task_t, 1);
329
330 task->type = DLS_TASK_SET_PROTOCOL_INFO;
331 task->invocation = invocation;
332 task->synchronous = TRUE;
333 g_variant_get(parameters, "(s)", &task->ut.protocol_info.protocol_info);
334
335 return task;
336 }
337
338 static dls_task_t *prv_upload_new_generic(dls_task_type_t type,
339 dleyna_connector_msg_id_t invocation,
340 const gchar *path,
341 GVariant *parameters,
342 GError **error)
343 {
344 dls_task_t *task;
345
346 task = prv_m2spec_task_new(type, invocation, path,
347 "(uo)", error, FALSE);
348 if (!task)
349 goto finished;
350
351 g_variant_get(parameters, "(ss)", &task->ut.upload.display_name,
352 &task->ut.upload.file_path);
353 g_strstrip(task->ut.upload.file_path);
354 task->multiple_retvals = TRUE;
355
356 finished:
357
358 return task;
359 }
360
361 dls_task_t *dls_task_prefer_local_addresses_new(
362 dleyna_connector_msg_id_t invocation,
363 GVariant *parameters)
364 {
365 dls_task_t *task = g_new0(dls_task_t, 1);
366
367 task->type = DLS_TASK_SET_PREFER_LOCAL_ADDRESSES;
368 task->invocation = invocation;
369 task->synchronous = TRUE;
370 g_variant_get(parameters, "(b)",
371 &task->ut.prefer_local_addresses.prefer);
372
373 return task;
374 }
375
376 dls_task_t *dls_task_upload_to_any_new(dleyna_connector_msg_id_t invocation,
377 const gchar *path, GVariant *parameters,
378 GError **error)
379 {
380 return prv_upload_new_generic(DLS_TASK_UPLOAD_TO_ANY, invocation,
381 path, parameters, error);
382 }
383
384 dls_task_t *dls_task_upload_new(dleyna_connector_msg_id_t invocation,
385 const gchar *path, GVariant *parameters,
386 GError **error)
387 {
388 return prv_upload_new_generic(DLS_TASK_UPLOAD, invocation,
389 path, parameters, error);
390 }
391
392 dls_task_t *dls_task_get_upload_status_new(dleyna_connector_msg_id_t invocation,
393 const gchar *path,
394 GVariant *parameters,
395 GError **error)
396 {
397 dls_task_t *task;
398
399 task = prv_m2spec_task_new(DLS_TASK_GET_UPLOAD_STATUS, invocation, path,
400 "(stt)", error, TRUE);
401 if (!task)
402 goto finished;
403
404 g_variant_get(parameters, "(u)",
405 &task->ut.upload_action.upload_id);
406 task->multiple_retvals = TRUE;
407
408 finished:
409
410 return task;
411 }
412
413 dls_task_t *dls_task_get_upload_ids_new(dleyna_connector_msg_id_t invocation,
414 const gchar *path,
415 GError **error)
416 {
417 dls_task_t *task;
418
419 task = prv_m2spec_task_new(DLS_TASK_GET_UPLOAD_IDS, invocation, path,
420 "(@au)", error, TRUE);
421 if (!task)
422 goto finished;
423
424 finished:
425
426 return task;
427 }
428
429 dls_task_t *dls_task_cancel_upload_new(dleyna_connector_msg_id_t invocation,
430 const gchar *path,
431 GVariant *parameters,
432 GError **error)
433 {
434 dls_task_t *task;
435
436 task = prv_m2spec_task_new(DLS_TASK_CANCEL_UPLOAD, invocation, path,
437 NULL, error, TRUE);
438 if (!task)
439 goto finished;
440
441 g_variant_get(parameters, "(u)",
442 &task->ut.upload_action.upload_id);
443
444 finished:
445
446 return task;
447 }
448
449 dls_task_t *dls_task_delete_new(dleyna_connector_msg_id_t invocation,
450 const gchar *path,
451 GError **error)
452 {
453 dls_task_t *task;
454
455 task = prv_m2spec_task_new(DLS_TASK_DELETE_OBJECT, invocation,
456 path, NULL, error, FALSE);
457 return task;
458 }
459
460 dls_task_t *dls_task_create_container_new_generic(
461 dleyna_connector_msg_id_t invocation,
462 dls_task_type_t type,
463 const gchar *path,
464 GVariant *parameters,
465 GError **error)
466 {
467 dls_task_t *task;
468
469 task = prv_m2spec_task_new(type, invocation, path,
470 "(@o)", error, FALSE);
471 if (!task)
472 goto finished;
473
474 g_variant_get(parameters, "(ss@as)",
475 &task->ut.create_container.display_name,
476 &task->ut.create_container.type,
477 &task->ut.create_container.child_types);
478
479 finished:
480
481 return task;
482 }
483
484 dls_task_t *dls_task_create_playlist_new(dleyna_connector_msg_id_t invocation,
485 dls_task_type_t type,
486 const gchar *path,
487 GVariant *parameters,
488 GError **error)
489 {
490 dls_task_t *task;
491
492 task = prv_m2spec_task_new(type, invocation, path,
493 "(uo)", error, FALSE);
494 if (!task)
495 goto finished;
496
497 g_variant_get(parameters, "(ssss@ao)",
498 &task->ut.playlist.title,
499 &task->ut.playlist.creator,
500 &task->ut.playlist.genre,
501 &task->ut.playlist.desc,
502 &task->ut.playlist.item_path);
503
504 task->multiple_retvals = TRUE;
505
506 finished:
507
508 return task;
509
510 }
511
512 dls_task_t *dls_task_update_new(dleyna_connector_msg_id_t invocation,
513 const gchar *path, GVariant *parameters,
514 GError **error)
515 {
516 dls_task_t *task;
517
518 task = prv_m2spec_task_new(DLS_TASK_UPDATE_OBJECT, invocation, path,
519 NULL, error, FALSE);
520 if (!task)
521 goto finished;
522
523 g_variant_get(parameters, "(@a{sv}@as)",
524 &task->ut.update.to_add_update,
525 &task->ut.update.to_delete);
526
527 finished:
528
529 return task;
530 }
531
532 void dls_task_complete(dls_task_t *task)
533 {
534 GVariant *variant = NULL;
535
536 if (!task)
537 goto finished;
538
539 if (task->invocation) {
540 if (task->result_format) {
541 if (task->multiple_retvals)
542 variant = task->result;
543 else
544 variant = g_variant_new(task->result_format,
545 task->result);
546 }
547 dls_server_get_connector()->return_response(task->invocation,
548 variant);
549 task->invocation = NULL;
550 }
551
552 finished:
553
554 return;
555 }
556
557 void dls_task_fail(dls_task_t *task, GError *error)
558 {
559 if (!task)
560 goto finished;
561
562 if (task->invocation) {
563 dls_server_get_connector()->return_error(task->invocation,
564 error);
565 task->invocation = NULL;
566 }
567
568 finished:
569
570 return;
571 }
572
573 void dls_task_cancel(dls_task_t *task)
574 {
575 GError *error;
576
577 if (!task)
578 goto finished;
579
580 if (task->invocation) {
581 error = g_error_new(DLEYNA_SERVER_ERROR, DLEYNA_ERROR_CANCELLED,
582 "Operation cancelled.");
583 dls_server_get_connector()->return_error(task->invocation,
584 error);
585 task->invocation = NULL;
586 g_error_free(error);
587 }
588
589 if (!task->synchronous)
590 dls_async_task_cancel((dls_async_task_t *)task);
591
592 finished:
593
594 return;
595 }
596
597 void dls_task_delete(dls_task_t *task)
598 {
599 GError *error;
600
601 if (!task)
602 goto finished;
603
604 if (task->invocation) {
605 error = g_error_new(DLEYNA_SERVER_ERROR, DLEYNA_ERROR_DIED,
606 "Unable to complete command.");
607 dls_server_get_connector()->return_error(task->invocation,
608 error);
609 g_error_free(error);
610 }
611
612 prv_delete(task);
613
614 finished:
615
616 return;
617 }
+0
-267
lib/task.h less more
0 /*
1 * dLeyna
2 *
3 * Copyright (C) 2012-2013 Intel Corporation. All rights reserved.
4 *
5 * This program is free software; you can redistribute it and/or modify it
6 * under the terms and conditions of the GNU Lesser General Public License,
7 * version 2.1, as published by the Free Software Foundation.
8 *
9 * This program is distributed in the hope it will be useful, but WITHOUT
10 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
11 * FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License
12 * for more details.
13 *
14 * You should have received a copy of the GNU Lesser General Public License
15 * along with this program; if not, write to the Free Software Foundation, Inc.,
16 * 51 Franklin St - Fifth Floor, Boston, MA 02110-1301 USA.
17 *
18 * Mark Ryan <mark.d.ryan@intel.com>
19 *
20 */
21
22 #ifndef DLS_TASK_H__
23 #define DLS_TASK_H__
24
25 #include <gio/gio.h>
26 #include <glib.h>
27
28 #include <libdleyna/core/connector.h>
29 #include <libdleyna/core/task-atom.h>
30
31 #include "server.h"
32
33 enum dls_task_type_t_ {
34 DLS_TASK_GET_VERSION,
35 DLS_TASK_GET_SERVERS,
36 DLS_TASK_GET_CHILDREN,
37 DLS_TASK_GET_ALL_PROPS,
38 DLS_TASK_GET_PROP,
39 DLS_TASK_SEARCH,
40 DLS_TASK_GET_RESOURCE,
41 DLS_TASK_SET_PREFER_LOCAL_ADDRESSES,
42 DLS_TASK_SET_PROTOCOL_INFO,
43 DLS_TASK_UPLOAD_TO_ANY,
44 DLS_TASK_UPLOAD,
45 DLS_TASK_GET_UPLOAD_STATUS,
46 DLS_TASK_GET_UPLOAD_IDS,
47 DLS_TASK_CANCEL_UPLOAD,
48 DLS_TASK_DELETE_OBJECT,
49 DLS_TASK_CREATE_CONTAINER,
50 DLS_TASK_CREATE_CONTAINER_IN_ANY,
51 DLS_TASK_UPDATE_OBJECT,
52 DLS_TASK_CREATE_PLAYLIST,
53 DLS_TASK_CREATE_PLAYLIST_IN_ANY
54 };
55 typedef enum dls_task_type_t_ dls_task_type_t;
56
57 typedef void (*dls_cancel_task_t)(void *handle);
58
59 typedef struct dls_task_get_children_t_ dls_task_get_children_t;
60 struct dls_task_get_children_t_ {
61 gboolean containers;
62 gboolean items;
63 guint start;
64 guint count;
65 GVariant *filter;
66 gchar *sort_by;
67 };
68
69 typedef struct dls_task_get_props_t_ dls_task_get_props_t;
70 struct dls_task_get_props_t_ {
71 gchar *interface_name;
72 };
73
74 typedef struct dls_task_get_prop_t_ dls_task_get_prop_t;
75 struct dls_task_get_prop_t_ {
76 gchar *prop_name;
77 gchar *interface_name;
78 };
79
80 typedef struct dls_task_search_t_ dls_task_search_t;
81 struct dls_task_search_t_ {
82 gchar *query;
83 guint start;
84 guint count;
85 gchar *sort_by;
86 GVariant *filter;
87 };
88
89 typedef struct dls_task_get_resource_t_ dls_task_get_resource_t;
90 struct dls_task_get_resource_t_ {
91 gchar *protocol_info;
92 GVariant *filter;
93 };
94
95 typedef struct dls_task_set_prefer_local_addresses_t_
96 dls_task_set_prefer_local_addresses_t;
97 struct dls_task_set_prefer_local_addresses_t_ {
98 gboolean prefer;
99 };
100
101 typedef struct dls_task_set_protocol_info_t_ dls_task_set_protocol_info_t;
102 struct dls_task_set_protocol_info_t_ {
103 gchar *protocol_info;
104 };
105
106 typedef struct dls_task_upload_t_ dls_task_upload_t;
107 struct dls_task_upload_t_ {
108 gchar *display_name;
109 gchar *file_path;
110 };
111
112 typedef struct dls_task_upload_action_t_ dls_task_upload_action_t;
113 struct dls_task_upload_action_t_ {
114 guint upload_id;
115 };
116
117 typedef struct dls_task_create_container_t_ dls_task_create_container_t;
118 struct dls_task_create_container_t_ {
119 gchar *display_name;
120 gchar *type;
121 GVariant *child_types;
122 };
123
124 typedef struct dls_task_update_t_ dls_task_update_t;
125 struct dls_task_update_t_ {
126 GVariant *to_add_update;
127 GVariant *to_delete;
128 };
129
130 typedef struct dls_task_create_playlist_t_ dls_task_create_playlist_t;
131 struct dls_task_create_playlist_t_ {
132 gchar *title;
133 gchar *creator;
134 gchar *genre;
135 gchar *desc;
136 GVariant *item_path;
137 };
138
139 typedef struct dls_task_target_info_t_ dls_task_target_info_t;
140 struct dls_task_target_info_t_ {
141 gchar *path;
142 gchar *root_path;
143 gchar *id;
144 dls_device_t *device;
145 };
146
147 typedef struct dls_task_t_ dls_task_t;
148 struct dls_task_t_ {
149 dleyna_task_atom_t atom; /* pseudo inheritance - MUST be first field */
150 dls_task_type_t type;
151 dls_task_target_info_t target;
152 const gchar *result_format;
153 GVariant *result;
154 dleyna_connector_msg_id_t invocation;
155 gboolean synchronous;
156 gboolean multiple_retvals;
157 union {
158 dls_task_get_children_t get_children;
159 dls_task_get_props_t get_props;
160 dls_task_get_prop_t get_prop;
161 dls_task_search_t search;
162 dls_task_get_resource_t resource;
163 dls_task_set_prefer_local_addresses_t prefer_local_addresses;
164 dls_task_set_protocol_info_t protocol_info;
165 dls_task_upload_t upload;
166 dls_task_upload_action_t upload_action;
167 dls_task_create_container_t create_container;
168 dls_task_update_t update;
169 dls_task_create_playlist_t playlist;
170 } ut;
171 };
172
173 dls_task_t *dls_task_get_version_new(dleyna_connector_msg_id_t invocation);
174
175 dls_task_t *dls_task_get_servers_new(dleyna_connector_msg_id_t invocation);
176
177 dls_task_t *dls_task_get_children_new(dleyna_connector_msg_id_t invocation,
178 const gchar *path, GVariant *parameters,
179 gboolean items, gboolean containers,
180 GError **error);
181
182 dls_task_t *dls_task_get_children_ex_new(dleyna_connector_msg_id_t invocation,
183 const gchar *path,
184 GVariant *parameters, gboolean items,
185 gboolean containers,
186 GError **error);
187
188 dls_task_t *dls_task_get_prop_new(dleyna_connector_msg_id_t invocation,
189 const gchar *path, GVariant *parameters,
190 GError **error);
191
192 dls_task_t *dls_task_get_props_new(dleyna_connector_msg_id_t invocation,
193 const gchar *path, GVariant *parameters,
194 GError **error);
195
196 dls_task_t *dls_task_search_new(dleyna_connector_msg_id_t invocation,
197 const gchar *path, GVariant *parameters,
198 GError **error);
199
200 dls_task_t *dls_task_search_ex_new(dleyna_connector_msg_id_t invocation,
201 const gchar *path, GVariant *parameters,
202 GError **error);
203
204 dls_task_t *dls_task_get_resource_new(dleyna_connector_msg_id_t invocation,
205 const gchar *path, GVariant *parameters,
206 GError **error);
207
208 dls_task_t *dls_task_set_protocol_info_new(dleyna_connector_msg_id_t invocation,
209 GVariant *parameters);
210
211 dls_task_t *dls_task_prefer_local_addresses_new(
212 dleyna_connector_msg_id_t invocation,
213 GVariant *parameters);
214
215 dls_task_t *dls_task_upload_to_any_new(dleyna_connector_msg_id_t invocation,
216 const gchar *path, GVariant *parameters,
217 GError **error);
218
219 dls_task_t *dls_task_upload_new(dleyna_connector_msg_id_t invocation,
220 const gchar *path, GVariant *parameters,
221 GError **error);
222
223 dls_task_t *dls_task_get_upload_status_new(dleyna_connector_msg_id_t invocation,
224 const gchar *path,
225 GVariant *parameters,
226 GError **error);
227
228 dls_task_t *dls_task_get_upload_ids_new(dleyna_connector_msg_id_t invocation,
229 const gchar *path,
230 GError **error);
231
232 dls_task_t *dls_task_cancel_upload_new(dleyna_connector_msg_id_t invocation,
233 const gchar *path,
234 GVariant *parameters,
235 GError **error);
236
237 dls_task_t *dls_task_delete_new(dleyna_connector_msg_id_t invocation,
238 const gchar *path,
239 GError **error);
240
241 dls_task_t *dls_task_create_container_new_generic(
242 dleyna_connector_msg_id_t invocation,
243 dls_task_type_t type,
244 const gchar *path,
245 GVariant *parameters,
246 GError **error);
247
248 dls_task_t *dls_task_create_playlist_new(dleyna_connector_msg_id_t invocation,
249 dls_task_type_t type,
250 const gchar *path,
251 GVariant *parameters,
252 GError **error);
253
254 dls_task_t *dls_task_update_new(dleyna_connector_msg_id_t invocation,
255 const gchar *path, GVariant *parameters,
256 GError **error);
257
258 void dls_task_cancel(dls_task_t *task);
259
260 void dls_task_complete(dls_task_t *task);
261
262 void dls_task_fail(dls_task_t *task, GError *error);
263
264 void dls_task_delete(dls_task_t *task);
265
266 #endif /* DLS_TASK_H__ */
+0
-1110
lib/upnp.c less more
0 /*
1 * dLeyna
2 *
3 * Copyright (C) 2012-2013 Intel Corporation. All rights reserved.
4 *
5 * This program is free software; you can redistribute it and/or modify it
6 * under the terms and conditions of the GNU Lesser General Public License,
7 * version 2.1, as published by the Free Software Foundation.
8 *
9 * This program is distributed in the hope it will be useful, but WITHOUT
10 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
11 * FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License
12 * for more details.
13 *
14 * You should have received a copy of the GNU Lesser General Public License
15 * along with this program; if not, write to the Free Software Foundation, Inc.,
16 * 51 Franklin St - Fifth Floor, Boston, MA 02110-1301 USA.
17 *
18 * Mark Ryan <mark.d.ryan@intel.com>
19 *
20 */
21
22 #include <string.h>
23
24 #include <libgssdp/gssdp-resource-browser.h>
25 #include <libgupnp/gupnp-control-point.h>
26 #include <libgupnp/gupnp-error.h>
27
28 #include <libdleyna/core/error.h>
29 #include <libdleyna/core/log.h>
30 #include <libdleyna/core/service-task.h>
31
32 #include "async.h"
33 #include "device.h"
34 #include "interface.h"
35 #include "path.h"
36 #include "search.h"
37 #include "sort.h"
38 #include "upnp.h"
39
40 struct dls_upnp_t_ {
41 dleyna_connector_id_t connection;
42 const dleyna_connector_dispatch_cb_t *interface_info;
43 GHashTable *filter_map;
44 GHashTable *property_map;
45 dls_upnp_callback_t found_server;
46 dls_upnp_callback_t lost_server;
47 GUPnPContextManager *context_manager;
48 void *user_data;
49 GHashTable *server_udn_map;
50 GHashTable *server_uc_map;
51 guint counter;
52 };
53
54 /* Private structure used in service task */
55 typedef struct prv_device_new_ct_t_ prv_device_new_ct_t;
56 struct prv_device_new_ct_t_ {
57 dls_upnp_t *upnp;
58 char *udn;
59 dls_device_t *device;
60 const dleyna_task_queue_key_t *queue_id;
61 };
62
63 static void prv_device_new_free(prv_device_new_ct_t *priv_t)
64 {
65 if (priv_t) {
66 g_free(priv_t->udn);
67 g_free(priv_t);
68 }
69 }
70
71 static void prv_device_chain_end(gboolean cancelled, gpointer data)
72 {
73 dls_device_t *device;
74 prv_device_new_ct_t *priv_t = (prv_device_new_ct_t *)data;
75
76 DLEYNA_LOG_DEBUG("Enter");
77
78 device = priv_t->device;
79
80 if (cancelled)
81 goto on_clear;
82
83 DLEYNA_LOG_DEBUG("Notify new server available: %s", device->path);
84 g_hash_table_insert(priv_t->upnp->server_udn_map, g_strdup(priv_t->udn),
85 device);
86 priv_t->upnp->found_server(device->path, priv_t->upnp->user_data);
87
88 on_clear:
89
90 g_hash_table_remove(priv_t->upnp->server_uc_map, priv_t->udn);
91 prv_device_new_free(priv_t);
92
93 if (cancelled)
94 dls_device_delete(device);
95
96 DLEYNA_LOG_DEBUG_NL();
97 }
98
99 static void prv_server_available_cb(GUPnPControlPoint *cp,
100 GUPnPDeviceProxy *proxy,
101 gpointer user_data)
102 {
103 dls_upnp_t *upnp = user_data;
104 const char *udn;
105 dls_device_t *device;
106 const gchar *ip_address;
107 dls_device_context_t *context;
108 const dleyna_task_queue_key_t *queue_id;
109 unsigned int i;
110 prv_device_new_ct_t *priv_t;
111
112 udn = gupnp_device_info_get_udn((GUPnPDeviceInfo *)proxy);
113 if (!udn)
114 goto on_error;
115
116 ip_address = gupnp_context_get_host_ip(
117 gupnp_control_point_get_context(cp));
118
119 DLEYNA_LOG_DEBUG("UDN %s", udn);
120 DLEYNA_LOG_DEBUG("IP Address %s", ip_address);
121
122 device = g_hash_table_lookup(upnp->server_udn_map, udn);
123
124 if (!device) {
125 priv_t = g_hash_table_lookup(upnp->server_uc_map, udn);
126
127 if (priv_t)
128 device = priv_t->device;
129 }
130
131 if (!device) {
132 DLEYNA_LOG_DEBUG("Device not found. Adding");
133 DLEYNA_LOG_DEBUG_NL();
134
135 priv_t = g_new0(prv_device_new_ct_t, 1);
136
137 queue_id = dleyna_task_processor_add_queue(
138 dls_server_get_task_processor(),
139 dleyna_service_task_create_source(),
140 DLS_SERVER_SINK,
141 DLEYNA_TASK_QUEUE_FLAG_AUTO_REMOVE,
142 dleyna_service_task_process_cb,
143 dleyna_service_task_cancel_cb,
144 dleyna_service_task_delete_cb);
145 dleyna_task_queue_set_finally(queue_id, prv_device_chain_end);
146 dleyna_task_queue_set_user_data(queue_id, priv_t);
147
148 device = dls_device_new(upnp->connection, proxy, ip_address,
149 upnp->interface_info,
150 upnp->property_map, upnp->counter,
151 queue_id);
152
153 upnp->counter++;
154
155 priv_t->upnp = upnp;
156 priv_t->udn = g_strdup(udn);
157 priv_t->queue_id = queue_id;
158 priv_t->device = device;
159
160 g_hash_table_insert(upnp->server_uc_map, g_strdup(udn), priv_t);
161 } else {
162 DLEYNA_LOG_DEBUG("Device Found");
163
164 for (i = 0; i < device->contexts->len; ++i) {
165 context = g_ptr_array_index(device->contexts, i);
166 if (!strcmp(context->ip_address, ip_address))
167 break;
168 }
169
170 if (i == device->contexts->len) {
171 DLEYNA_LOG_DEBUG("Adding Context");
172 (void) dls_device_append_new_context(device, ip_address,
173 proxy);
174 }
175
176 DLEYNA_LOG_DEBUG_NL();
177 }
178
179 on_error:
180
181 return;
182 }
183
184 static gboolean prv_subscribe_to_contents_change(gpointer user_data)
185 {
186 dls_device_t *device = user_data;
187
188 device->timeout_id = 0;
189 dls_device_subscribe_to_contents_change(device);
190
191 return FALSE;
192 }
193
194 static void prv_server_unavailable_cb(GUPnPControlPoint *cp,
195 GUPnPDeviceProxy *proxy,
196 gpointer user_data)
197 {
198 dls_upnp_t *upnp = user_data;
199 const char *udn;
200 dls_device_t *device;
201 const gchar *ip_address;
202 unsigned int i;
203 dls_device_context_t *context;
204 gboolean subscribed;
205 gboolean under_construction = FALSE;
206 prv_device_new_ct_t *priv_t;
207
208 DLEYNA_LOG_DEBUG("Enter");
209
210 udn = gupnp_device_info_get_udn((GUPnPDeviceInfo *)proxy);
211 if (!udn)
212 goto on_error;
213
214 ip_address = gupnp_context_get_host_ip(
215 gupnp_control_point_get_context(cp));
216
217 DLEYNA_LOG_DEBUG("UDN %s", udn);
218 DLEYNA_LOG_DEBUG("IP Address %s", ip_address);
219
220 device = g_hash_table_lookup(upnp->server_udn_map, udn);
221
222 if (!device) {
223 priv_t = g_hash_table_lookup(upnp->server_uc_map, udn);
224
225 if (priv_t) {
226 device = priv_t->device;
227 under_construction = TRUE;
228 }
229 }
230
231 if (!device) {
232 DLEYNA_LOG_WARNING("Device not found. Ignoring");
233 goto on_error;
234 }
235
236 for (i = 0; i < device->contexts->len; ++i) {
237 context = g_ptr_array_index(device->contexts, i);
238 if (!strcmp(context->ip_address, ip_address))
239 break;
240 }
241
242 if (i >= device->contexts->len)
243 goto on_error;
244
245 subscribed = context->subscribed;
246
247 (void) g_ptr_array_remove_index(device->contexts, i);
248
249 if (device->contexts->len == 0) {
250 if (!under_construction) {
251 DLEYNA_LOG_DEBUG("Last Context lost. Delete device");
252 upnp->lost_server(device->path, upnp->user_data);
253 g_hash_table_remove(upnp->server_udn_map, udn);
254 } else {
255 DLEYNA_LOG_WARNING(
256 "Device under construction. Cancelling");
257
258 dleyna_task_processor_cancel_queue(priv_t->queue_id);
259 }
260 } else if (subscribed && !device->timeout_id) {
261 DLEYNA_LOG_DEBUG("Subscribe on new context");
262
263 device->timeout_id = g_timeout_add_seconds(1,
264 prv_subscribe_to_contents_change,
265 device);
266 }
267
268 on_error:
269
270 DLEYNA_LOG_DEBUG("Exit");
271 DLEYNA_LOG_DEBUG_NL();
272
273 return;
274 }
275
276 static void prv_on_context_available(GUPnPContextManager *context_manager,
277 GUPnPContext *context,
278 gpointer user_data)
279 {
280 dls_upnp_t *upnp = user_data;
281 GUPnPControlPoint *cp;
282
283 cp = gupnp_control_point_new(
284 context,
285 "urn:schemas-upnp-org:device:MediaServer:1");
286
287 g_signal_connect(cp, "device-proxy-available",
288 G_CALLBACK(prv_server_available_cb), upnp);
289
290 g_signal_connect(cp, "device-proxy-unavailable",
291 G_CALLBACK(prv_server_unavailable_cb), upnp);
292
293 gssdp_resource_browser_set_active(GSSDP_RESOURCE_BROWSER(cp), TRUE);
294 gupnp_context_manager_manage_control_point(upnp->context_manager, cp);
295 g_object_unref(cp);
296 }
297
298 dls_upnp_t *dls_upnp_new(dleyna_connector_id_t connection,
299 const dleyna_connector_dispatch_cb_t *dispatch_table,
300 dls_upnp_callback_t found_server,
301 dls_upnp_callback_t lost_server,
302 void *user_data)
303 {
304 dls_upnp_t *upnp = g_new0(dls_upnp_t, 1);
305
306 upnp->connection = connection;
307 upnp->interface_info = dispatch_table;
308 upnp->user_data = user_data;
309 upnp->found_server = found_server;
310 upnp->lost_server = lost_server;
311
312 upnp->server_udn_map = g_hash_table_new_full(g_str_hash, g_str_equal,
313 g_free,
314 dls_device_delete);
315
316 upnp->server_uc_map = g_hash_table_new_full(g_str_hash, g_str_equal,
317 g_free, NULL);
318
319 dls_prop_maps_new(&upnp->property_map, &upnp->filter_map);
320
321 upnp->context_manager = gupnp_context_manager_create(0);
322
323 g_signal_connect(upnp->context_manager, "context-available",
324 G_CALLBACK(prv_on_context_available),
325 upnp);
326
327 return upnp;
328 }
329
330 void dls_upnp_delete(dls_upnp_t *upnp)
331 {
332 if (upnp) {
333 g_object_unref(upnp->context_manager);
334 g_hash_table_unref(upnp->property_map);
335 g_hash_table_unref(upnp->filter_map);
336 g_hash_table_unref(upnp->server_udn_map);
337 g_hash_table_unref(upnp->server_uc_map);
338 g_free(upnp);
339 }
340 }
341
342 GVariant *dls_upnp_get_server_ids(dls_upnp_t *upnp)
343 {
344 GVariantBuilder vb;
345 GHashTableIter iter;
346 gpointer value;
347 dls_device_t *device;
348 GVariant *retval;
349
350 DLEYNA_LOG_DEBUG("Enter");
351
352 g_variant_builder_init(&vb, G_VARIANT_TYPE("ao"));
353
354 g_hash_table_iter_init(&iter, upnp->server_udn_map);
355 while (g_hash_table_iter_next(&iter, NULL, &value)) {
356 device = value;
357 DLEYNA_LOG_DEBUG("Have device %s", device->path);
358 g_variant_builder_add(&vb, "o", device->path);
359 }
360
361 retval = g_variant_ref_sink(g_variant_builder_end(&vb));
362
363 DLEYNA_LOG_DEBUG("Exit");
364
365 return retval;
366 }
367
368 GHashTable *dls_upnp_get_server_udn_map(dls_upnp_t *upnp)
369 {
370 return upnp->server_udn_map;
371 }
372
373 void dls_upnp_get_children(dls_upnp_t *upnp, dls_client_t *client,
374 dls_task_t *task,
375 dls_upnp_task_complete_t cb)
376 {
377 dls_async_task_t *cb_data = (dls_async_task_t *)task;
378 dls_async_bas_t *cb_task_data;
379 gchar *upnp_filter = NULL;
380 gchar *sort_by = NULL;
381
382 DLEYNA_LOG_DEBUG("Enter");
383
384 DLEYNA_LOG_DEBUG("Path: %s", task->target.path);
385 DLEYNA_LOG_DEBUG("Start: %u", task->ut.get_children.start);
386 DLEYNA_LOG_DEBUG("Count: %u", task->ut.get_children.count);
387
388 cb_data->cb = cb;
389 cb_task_data = &cb_data->ut.bas;
390
391 cb_task_data->filter_mask =
392 dls_props_parse_filter(upnp->filter_map,
393 task->ut.get_children.filter,
394 &upnp_filter);
395
396 DLEYNA_LOG_DEBUG("Filter Mask 0x%"G_GUINT64_FORMAT"x",
397 cb_task_data->filter_mask);
398
399 sort_by = dls_sort_translate_sort_string(upnp->filter_map,
400 task->ut.get_children.sort_by);
401 if (!sort_by) {
402 DLEYNA_LOG_WARNING("Invalid Sort Criteria");
403
404 cb_data->error = g_error_new(DLEYNA_SERVER_ERROR,
405 DLEYNA_ERROR_BAD_QUERY,
406 "Sort Criteria are not valid");
407 goto on_error;
408 }
409
410 DLEYNA_LOG_DEBUG("Sort By %s", sort_by);
411
412 cb_task_data->protocol_info = client->protocol_info;
413
414 dls_device_get_children(client, task, upnp_filter, sort_by);
415
416 on_error:
417
418 if (!cb_data->action)
419 (void) g_idle_add(dls_async_task_complete, cb_data);
420
421 g_free(sort_by);
422 g_free(upnp_filter);
423
424 DLEYNA_LOG_DEBUG("Exit with %s", !cb_data->action ? "FAIL" : "SUCCESS");
425 }
426
427 void dls_upnp_get_all_props(dls_upnp_t *upnp, dls_client_t *client,
428 dls_task_t *task,
429 dls_upnp_task_complete_t cb)
430 {
431 gboolean root_object;
432 dls_async_task_t *cb_data = (dls_async_task_t *)task;
433 dls_async_get_all_t *cb_task_data;
434
435 DLEYNA_LOG_DEBUG("Enter");
436
437 DLEYNA_LOG_DEBUG("Path: %s", task->target.path);
438 DLEYNA_LOG_DEBUG("Interface %s", task->ut.get_prop.interface_name);
439
440 cb_data->cb = cb;
441 cb_task_data = &cb_data->ut.get_all;
442
443 root_object = task->target.id[0] == '0' && task->target.id[1] == 0;
444
445 DLEYNA_LOG_DEBUG("Root Object = %d", root_object);
446
447 cb_task_data->protocol_info = client->protocol_info;
448
449 dls_device_get_all_props(client, task, root_object);
450
451 DLEYNA_LOG_DEBUG("Exit with SUCCESS");
452 }
453
454 void dls_upnp_get_prop(dls_upnp_t *upnp, dls_client_t *client,
455 dls_task_t *task,
456 dls_upnp_task_complete_t cb)
457 {
458 gboolean root_object;
459 dls_async_task_t *cb_data = (dls_async_task_t *)task;
460 dls_async_get_prop_t *cb_task_data;
461 dls_prop_map_t *prop_map;
462 dls_task_get_prop_t *task_data;
463
464 DLEYNA_LOG_DEBUG("Enter");
465
466 DLEYNA_LOG_DEBUG("Path: %s", task->target.path);
467 DLEYNA_LOG_DEBUG("Interface %s", task->ut.get_prop.interface_name);
468 DLEYNA_LOG_DEBUG("Prop.%s", task->ut.get_prop.prop_name);
469
470 task_data = &task->ut.get_prop;
471 cb_data->cb = cb;
472 cb_task_data = &cb_data->ut.get_prop;
473
474 root_object = task->target.id[0] == '0' && task->target.id[1] == 0;
475
476 DLEYNA_LOG_DEBUG("Root Object = %d", root_object);
477
478 cb_task_data->protocol_info = client->protocol_info;
479 prop_map = g_hash_table_lookup(upnp->filter_map, task_data->prop_name);
480
481 dls_device_get_prop(client, task, prop_map, root_object);
482
483 DLEYNA_LOG_DEBUG("Exit with SUCCESS");
484 }
485
486 void dls_upnp_search(dls_upnp_t *upnp, dls_client_t *client,
487 dls_task_t *task,
488 dls_upnp_task_complete_t cb)
489 {
490 gchar *upnp_filter = NULL;
491 gchar *upnp_query = NULL;
492 gchar *sort_by = NULL;
493 dls_async_task_t *cb_data = (dls_async_task_t *)task;
494 dls_async_bas_t *cb_task_data;
495
496 DLEYNA_LOG_DEBUG("Enter");
497
498 DLEYNA_LOG_DEBUG("Path: %s", task->target.path);
499 DLEYNA_LOG_DEBUG("Query: %s", task->ut.search.query);
500 DLEYNA_LOG_DEBUG("Start: %u", task->ut.search.start);
501 DLEYNA_LOG_DEBUG("Count: %u", task->ut.search.count);
502
503 cb_data->cb = cb;
504 cb_task_data = &cb_data->ut.bas;
505
506 cb_task_data->filter_mask =
507 dls_props_parse_filter(upnp->filter_map,
508 task->ut.search.filter, &upnp_filter);
509
510 DLEYNA_LOG_DEBUG("Filter Mask 0x%"G_GUINT64_FORMAT"x",
511 cb_task_data->filter_mask);
512
513 upnp_query = dls_search_translate_search_string(upnp->filter_map,
514 task->ut.search.query);
515 if (!upnp_query) {
516 DLEYNA_LOG_WARNING("Query string is not valid:%s",
517 task->ut.search.query);
518
519 cb_data->error = g_error_new(DLEYNA_SERVER_ERROR,
520 DLEYNA_ERROR_BAD_QUERY,
521 "Query string is not valid.");
522 goto on_error;
523 }
524
525 DLEYNA_LOG_DEBUG("UPnP Query %s", upnp_query);
526
527 sort_by = dls_sort_translate_sort_string(upnp->filter_map,
528 task->ut.search.sort_by);
529 if (!sort_by) {
530 DLEYNA_LOG_WARNING("Invalid Sort Criteria");
531
532 cb_data->error = g_error_new(DLEYNA_SERVER_ERROR,
533 DLEYNA_ERROR_BAD_QUERY,
534 "Sort Criteria are not valid");
535 goto on_error;
536 }
537
538 DLEYNA_LOG_DEBUG("Sort By %s", sort_by);
539
540 cb_task_data->protocol_info = client->protocol_info;
541
542 dls_device_search(client, task, upnp_filter, upnp_query, sort_by);
543 on_error:
544
545 if (!cb_data->action)
546 (void) g_idle_add(dls_async_task_complete, cb_data);
547
548 g_free(sort_by);
549 g_free(upnp_query);
550 g_free(upnp_filter);
551
552 DLEYNA_LOG_DEBUG("Exit with %s", !cb_data->action ? "FAIL" : "SUCCESS");
553 }
554
555 void dls_upnp_get_resource(dls_upnp_t *upnp, dls_client_t *client,
556 dls_task_t *task,
557 dls_upnp_task_complete_t cb)
558 {
559 dls_async_task_t *cb_data = (dls_async_task_t *)task;
560 dls_async_get_all_t *cb_task_data;
561 gchar *upnp_filter = NULL;
562
563 DLEYNA_LOG_DEBUG("Enter");
564
565 DLEYNA_LOG_DEBUG("Protocol Info: %s ", task->ut.resource.protocol_info);
566
567 cb_data->cb = cb;
568 cb_task_data = &cb_data->ut.get_all;
569
570 DLEYNA_LOG_DEBUG("Root Path %s Id %s", task->target.root_path,
571 task->target.id);
572
573 cb_task_data->filter_mask =
574 dls_props_parse_filter(upnp->filter_map,
575 task->ut.resource.filter, &upnp_filter);
576
577 DLEYNA_LOG_DEBUG("Filter Mask 0x%"G_GUINT64_FORMAT"x",
578 cb_task_data->filter_mask);
579
580 dls_device_get_resource(client, task, upnp_filter);
581
582 DLEYNA_LOG_DEBUG("Exit");
583 }
584
585 static gboolean prv_compute_mime_and_class(dls_task_t *task,
586 dls_async_upload_t *cb_task_data,
587 GError **error)
588 {
589 gchar *content_type = NULL;
590
591 if (!g_file_test(task->ut.upload.file_path,
592 G_FILE_TEST_IS_REGULAR | G_FILE_TEST_EXISTS)) {
593
594 DLEYNA_LOG_WARNING(
595 "File %s does not exist or is not a regular file",
596 task->ut.upload.file_path);
597
598 *error = g_error_new(DLEYNA_SERVER_ERROR,
599 DLEYNA_ERROR_OBJECT_NOT_FOUND,
600 "File %s does not exist or is not a regular file",
601 task->ut.upload.file_path);
602 goto on_error;
603 }
604
605 content_type = g_content_type_guess(task->ut.upload.file_path, NULL, 0,
606 NULL);
607
608 if (!content_type) {
609
610 DLEYNA_LOG_WARNING("Unable to determine Content Type for %s",
611 task->ut.upload.file_path);
612
613 *error = g_error_new(DLEYNA_SERVER_ERROR, DLEYNA_ERROR_BAD_MIME,
614 "Unable to determine Content Type for %s",
615 task->ut.upload.file_path);
616 goto on_error;
617 }
618
619 cb_task_data->mime_type = g_content_type_get_mime_type(content_type);
620 g_free(content_type);
621
622 if (!cb_task_data->mime_type) {
623
624 DLEYNA_LOG_WARNING("Unable to determine MIME Type for %s",
625 task->ut.upload.file_path);
626
627 *error = g_error_new(DLEYNA_SERVER_ERROR, DLEYNA_ERROR_BAD_MIME,
628 "Unable to determine MIME Type for %s",
629 task->ut.upload.file_path);
630 goto on_error;
631 }
632
633 if (g_content_type_is_a(cb_task_data->mime_type, "image/*")) {
634 cb_task_data->object_class = "object.item.imageItem";
635 } else if (g_content_type_is_a(cb_task_data->mime_type, "audio/*")) {
636 cb_task_data->object_class = "object.item.audioItem";
637 } else if (g_content_type_is_a(cb_task_data->mime_type, "video/*")) {
638 cb_task_data->object_class = "object.item.videoItem";
639 } else {
640
641 DLEYNA_LOG_WARNING("Unsupported MIME Type %s",
642 cb_task_data->mime_type);
643
644 *error = g_error_new(DLEYNA_SERVER_ERROR, DLEYNA_ERROR_BAD_MIME,
645 "Unsupported MIME Type %s",
646 cb_task_data->mime_type);
647 goto on_error;
648 }
649
650 return TRUE;
651
652 on_error:
653
654 return FALSE;
655 }
656
657 void dls_upnp_upload_to_any(dls_upnp_t *upnp, dls_client_t *client,
658 dls_task_t *task,
659 dls_upnp_task_complete_t cb)
660 {
661 dls_async_task_t *cb_data = (dls_async_task_t *)task;
662 dls_async_upload_t *cb_task_data;
663
664 DLEYNA_LOG_DEBUG("Enter");
665
666 cb_data->cb = cb;
667 cb_task_data = &cb_data->ut.upload;
668
669 DLEYNA_LOG_DEBUG("Root Path %s Id %s", task->target.root_path,
670 task->target.id);
671
672 if (strcmp(task->target.id, "0")) {
673 DLEYNA_LOG_WARNING("Bad path %s", task->target.path);
674
675 cb_data->error =
676 g_error_new(DLEYNA_SERVER_ERROR, DLEYNA_ERROR_BAD_PATH,
677 "UploadToAnyContainer must be executed on a root path");
678 goto on_error;
679 }
680
681 if (!prv_compute_mime_and_class(task, cb_task_data, &cb_data->error))
682 goto on_error;
683
684 DLEYNA_LOG_DEBUG("MIME Type %s", cb_task_data->mime_type);
685 DLEYNA_LOG_DEBUG("Object class %s", cb_task_data->object_class);
686
687 dls_device_upload(client, task, "DLNA.ORG_AnyContainer");
688
689 on_error:
690
691 if (!cb_data->action)
692 (void) g_idle_add(dls_async_task_complete, cb_data);
693
694 DLEYNA_LOG_DEBUG("Exit");
695 }
696
697 void dls_upnp_upload(dls_upnp_t *upnp, dls_client_t *client, dls_task_t *task,
698 dls_upnp_task_complete_t cb)
699 {
700 dls_async_task_t *cb_data = (dls_async_task_t *)task;
701 dls_async_upload_t *cb_task_data;
702
703 DLEYNA_LOG_DEBUG("Enter");
704
705 cb_data->cb = cb;
706 cb_task_data = &cb_data->ut.upload;
707
708 if (!prv_compute_mime_and_class(task, cb_task_data, &cb_data->error))
709 goto on_error;
710
711 DLEYNA_LOG_DEBUG("MIME Type %s", cb_task_data->mime_type);
712 DLEYNA_LOG_DEBUG("Object class %s", cb_task_data->object_class);
713
714 dls_device_upload(client, task, task->target.id);
715
716 on_error:
717
718 if (!cb_data->action)
719 (void) g_idle_add(dls_async_task_complete, cb_data);
720
721 DLEYNA_LOG_DEBUG("Exit");
722 }
723
724 void dls_upnp_get_upload_status(dls_upnp_t *upnp, dls_task_t *task)
725 {
726 GError *error = NULL;
727
728 DLEYNA_LOG_DEBUG("Enter");
729
730 DLEYNA_LOG_DEBUG("Root Path %s Id %s", task->target.root_path,
731 task->target.id);
732
733 if (strcmp(task->target.id, "0")) {
734 DLEYNA_LOG_WARNING("Bad path %s", task->target.path);
735
736 error = g_error_new(DLEYNA_SERVER_ERROR, DLEYNA_ERROR_BAD_PATH,
737 "GetUploadStatus must be executed on a root path");
738 goto on_error;
739 }
740
741 (void) dls_device_get_upload_status(task, &error);
742
743 on_error:
744
745 if (error) {
746 dls_task_fail(task, error);
747 g_error_free(error);
748 } else {
749 dls_task_complete(task);
750 }
751
752 DLEYNA_LOG_DEBUG("Exit");
753 }
754
755 void dls_upnp_get_upload_ids(dls_upnp_t *upnp, dls_task_t *task)
756 {
757 GError *error = NULL;
758
759 DLEYNA_LOG_DEBUG("Enter");
760
761 DLEYNA_LOG_DEBUG("Root Path %s Id %s", task->target.root_path,
762 task->target.id);
763
764 if (strcmp(task->target.id, "0")) {
765 DLEYNA_LOG_WARNING("Bad path %s", task->target.path);
766
767 error = g_error_new(DLEYNA_SERVER_ERROR, DLEYNA_ERROR_BAD_PATH,
768 "GetUploadIDs must be executed on a root path");
769 goto on_error;
770 }
771
772 dls_device_get_upload_ids(task);
773
774 on_error:
775
776 if (error) {
777 dls_task_fail(task, error);
778 g_error_free(error);
779 } else {
780 dls_task_complete(task);
781 }
782
783 DLEYNA_LOG_DEBUG("Exit");
784 }
785
786 void dls_upnp_cancel_upload(dls_upnp_t *upnp, dls_task_t *task)
787 {
788 GError *error = NULL;
789
790 DLEYNA_LOG_DEBUG("Enter");
791
792 DLEYNA_LOG_DEBUG("Root Path %s Id %s", task->target.root_path,
793 task->target.id);
794
795 if (strcmp(task->target.id, "0")) {
796 DLEYNA_LOG_WARNING("Bad path %s", task->target.path);
797
798 error = g_error_new(DLEYNA_SERVER_ERROR, DLEYNA_ERROR_BAD_PATH,
799 "CancelUpload must be executed on a root path");
800 goto on_error;
801 }
802
803 (void) dls_device_cancel_upload(task, &error);
804
805 on_error:
806
807 if (error) {
808 dls_task_fail(task, error);
809 g_error_free(error);
810 } else {
811 dls_task_complete(task);
812 }
813
814 DLEYNA_LOG_DEBUG("Exit");
815 }
816
817 void dls_upnp_delete_object(dls_upnp_t *upnp, dls_client_t *client,
818 dls_task_t *task,
819 dls_upnp_task_complete_t cb)
820 {
821 dls_async_task_t *cb_data = (dls_async_task_t *)task;
822
823 DLEYNA_LOG_DEBUG("Enter");
824
825 cb_data->cb = cb;
826
827 DLEYNA_LOG_DEBUG("Root Path %s Id %s", task->target.root_path,
828 task->target.id);
829
830 dls_device_delete_object(client, task);
831
832 DLEYNA_LOG_DEBUG("Exit");
833 }
834
835 void dls_upnp_create_container(dls_upnp_t *upnp, dls_client_t *client,
836 dls_task_t *task,
837 dls_upnp_task_complete_t cb)
838 {
839 dls_async_task_t *cb_data = (dls_async_task_t *)task;
840
841 DLEYNA_LOG_DEBUG("Enter");
842
843 cb_data->cb = cb;
844
845 DLEYNA_LOG_DEBUG("Root Path %s Id %s", task->target.root_path,
846 task->target.id);
847
848 dls_device_create_container(client, task, task->target.id);
849
850 DLEYNA_LOG_DEBUG("Exit");
851 }
852
853 void dls_upnp_create_container_in_any(dls_upnp_t *upnp, dls_client_t *client,
854 dls_task_t *task,
855 dls_upnp_task_complete_t cb)
856 {
857 dls_async_task_t *cb_data = (dls_async_task_t *)task;
858
859 DLEYNA_LOG_DEBUG("Enter");
860
861 cb_data->cb = cb;
862
863 DLEYNA_LOG_DEBUG("Root Path %s Id %s", task->target.root_path,
864 task->target.id);
865
866 if (strcmp(task->target.id, "0")) {
867 DLEYNA_LOG_WARNING("Bad path %s", task->target.path);
868
869 cb_data->error =
870 g_error_new(DLEYNA_SERVER_ERROR, DLEYNA_ERROR_BAD_PATH,
871 "CreateContainerInAnyContainer must be executed on a root path");
872 goto on_error;
873 }
874
875 dls_device_create_container(client, task, "DLNA.ORG_AnyContainer");
876
877 on_error:
878
879 if (!cb_data->action)
880 (void) g_idle_add(dls_async_task_complete, cb_data);
881
882 DLEYNA_LOG_DEBUG("Exit");
883 }
884
885 void dls_upnp_update_object(dls_upnp_t *upnp, dls_client_t *client,
886 dls_task_t *task,
887 dls_upnp_task_complete_t cb)
888 {
889 dls_async_task_t *cb_data = (dls_async_task_t *)task;
890 dls_async_update_t *cb_task_data;
891 dls_upnp_prop_mask mask;
892 gchar *upnp_filter = NULL;
893 dls_task_update_t *task_data;
894
895 DLEYNA_LOG_DEBUG("Enter");
896
897 cb_data->cb = cb;
898 cb_task_data = &cb_data->ut.update;
899 task_data = &task->ut.update;
900
901 DLEYNA_LOG_DEBUG("Root Path %s Id %s", task->target.root_path,
902 task->target.id);
903
904 if (!dls_props_parse_update_filter(upnp->filter_map,
905 task_data->to_add_update,
906 task_data->to_delete,
907 &mask, &upnp_filter)) {
908 DLEYNA_LOG_WARNING("Invalid Parameter");
909
910 cb_data->error = g_error_new(DLEYNA_SERVER_ERROR,
911 DLEYNA_ERROR_OPERATION_FAILED,
912 "Invalid Parameter");
913 goto on_error;
914 }
915
916 cb_task_data->map = upnp->filter_map;
917
918 DLEYNA_LOG_DEBUG("Filter = %s", upnp_filter);
919 DLEYNA_LOG_DEBUG("Mask = 0x%"G_GUINT64_FORMAT"x", mask);
920
921 if (mask == 0) {
922 DLEYNA_LOG_WARNING("Empty Parameters");
923
924 cb_data->error = g_error_new(DLEYNA_SERVER_ERROR,
925 DLEYNA_ERROR_OPERATION_FAILED,
926 "Empty Parameters");
927
928 goto on_error;
929 }
930
931 dls_device_update_object(client, task, upnp_filter);
932
933 on_error:
934
935 g_free(upnp_filter);
936
937 if (!cb_data->action)
938 (void) g_idle_add(dls_async_task_complete, cb_data);
939
940 DLEYNA_LOG_DEBUG("Exit");
941 }
942
943 void dls_upnp_create_playlist(dls_upnp_t *upnp, dls_client_t *client,
944 dls_task_t *task,
945 dls_upnp_task_complete_t cb)
946 {
947 dls_async_task_t *cb_data = (dls_async_task_t *)task;
948 dls_task_create_playlist_t *task_data;
949
950 DLEYNA_LOG_DEBUG("Enter");
951
952 cb_data->cb = cb;
953 task_data = &task->ut.playlist;
954
955 DLEYNA_LOG_DEBUG("Root Path: %s - Id: %s", task->target.root_path,
956 task->target.id);
957
958 if (!task_data->title || !*task_data->title)
959 goto on_param_error;
960
961 if (!g_variant_n_children(task_data->item_path))
962 goto on_param_error;
963
964 DLEYNA_LOG_DEBUG_NL();
965 DLEYNA_LOG_DEBUG("Title = %s", task_data->title);
966 DLEYNA_LOG_DEBUG("Creator = %s", task_data->creator);
967 DLEYNA_LOG_DEBUG("Genre = %s", task_data->genre);
968 DLEYNA_LOG_DEBUG("Desc = %s", task_data->desc);
969 DLEYNA_LOG_DEBUG_NL();
970
971 dls_device_playlist_upload(client, task, task->target.id);
972
973 DLEYNA_LOG_DEBUG("Exit");
974
975 return;
976
977 on_param_error:
978
979 DLEYNA_LOG_WARNING("Invalid Parameter");
980
981 cb_data->error = g_error_new(DLEYNA_SERVER_ERROR,
982 DLEYNA_ERROR_OPERATION_FAILED,
983 "Invalid Parameter");
984
985 (void) g_idle_add(dls_async_task_complete, cb_data);
986
987 DLEYNA_LOG_DEBUG("Exit failure");
988 }
989
990 void dls_upnp_create_playlist_in_any(dls_upnp_t *upnp, dls_client_t *client,
991 dls_task_t *task,
992 dls_upnp_task_complete_t cb)
993 {
994 dls_async_task_t *cb_data = (dls_async_task_t *)task;
995 dls_task_create_playlist_t *task_data;
996
997 DLEYNA_LOG_DEBUG("Enter");
998
999 cb_data->cb = cb;
1000 task_data = &task->ut.playlist;
1001
1002 DLEYNA_LOG_DEBUG("Root Path: %s - Id: %s", task->target.root_path,
1003 task->target.id);
1004
1005 if (strcmp(task->target.id, "0")) {
1006 DLEYNA_LOG_WARNING("Bad path %s", task->target.path);
1007
1008 cb_data->error = g_error_new(DLEYNA_SERVER_ERROR,
1009 DLEYNA_ERROR_BAD_PATH,
1010 "CreatePlayListInAny must be executed on a root path");
1011
1012 goto on_error;
1013 }
1014
1015 if (!task_data->title || !*task_data->title)
1016 goto on_param_error;
1017
1018 if (!g_variant_n_children(task_data->item_path))
1019 goto on_param_error;
1020
1021 DLEYNA_LOG_DEBUG_NL();
1022 DLEYNA_LOG_DEBUG("Title = %s", task_data->title);
1023 DLEYNA_LOG_DEBUG("Creator = %s", task_data->creator);
1024 DLEYNA_LOG_DEBUG("Genre = %s", task_data->genre);
1025 DLEYNA_LOG_DEBUG("Desc = %s", task_data->desc);
1026 DLEYNA_LOG_DEBUG_NL();
1027
1028 dls_device_playlist_upload(client, task, "DLNA.ORG_AnyContainer");
1029
1030 DLEYNA_LOG_DEBUG("Exit");
1031
1032 return;
1033
1034 on_param_error:
1035
1036 DLEYNA_LOG_WARNING("Invalid Parameter");
1037
1038 cb_data->error = g_error_new(DLEYNA_SERVER_ERROR,
1039 DLEYNA_ERROR_OPERATION_FAILED,
1040 "Invalid Parameter");
1041 on_error:
1042
1043 (void) g_idle_add(dls_async_task_complete, cb_data);
1044
1045 DLEYNA_LOG_DEBUG("Exit failure");
1046 }
1047
1048 void dls_upnp_unsubscribe(dls_upnp_t *upnp)
1049 {
1050 GHashTableIter iter;
1051 gpointer value;
1052 dls_device_t *device;
1053
1054 DLEYNA_LOG_DEBUG("Enter");
1055
1056 g_hash_table_iter_init(&iter, upnp->server_udn_map);
1057 while (g_hash_table_iter_next(&iter, NULL, &value)) {
1058 device = value;
1059 dls_device_unsubscribe(device);
1060 }
1061
1062 DLEYNA_LOG_DEBUG("Exit");
1063 }
1064
1065 static gboolean prv_device_uc_find(gpointer key, gpointer value,
1066 gpointer user_data)
1067 {
1068 prv_device_new_ct_t *priv_t = (prv_device_new_ct_t *)value;
1069
1070 return (priv_t->device == user_data) ? TRUE : FALSE;
1071 }
1072
1073 static gboolean prv_device_find(gpointer key, gpointer value,
1074 gpointer user_data)
1075 {
1076 return (value == user_data) ? TRUE : FALSE;
1077 }
1078
1079 gboolean dls_upnp_device_context_exist(dls_device_t *device,
1080 dls_device_context_t *context)
1081 {
1082 gpointer result;
1083 guint i;
1084 gboolean found = FALSE;
1085 dls_upnp_t *upnp = dls_server_get_upnp();
1086
1087 if (upnp == NULL)
1088 goto on_exit;
1089
1090 /* Check if the device still exist */
1091 result = g_hash_table_find(upnp->server_udn_map, prv_device_find,
1092 device);
1093
1094 if (result == NULL)
1095 if (g_hash_table_find(upnp->server_uc_map, prv_device_uc_find,
1096 device) == NULL)
1097 goto on_exit;
1098
1099 /* Search if the context still exist in the device */
1100 for (i = 0; i < device->contexts->len; ++i) {
1101 if (g_ptr_array_index(device->contexts, i) == context) {
1102 found = TRUE;
1103 break;
1104 }
1105 }
1106
1107 on_exit:
1108 return found;
1109 }
+0
-109
lib/upnp.h less more
0 /*
1 * dLeyna
2 *
3 * Copyright (C) 2012-2013 Intel Corporation. All rights reserved.
4 *
5 * This program is free software; you can redistribute it and/or modify it
6 * under the terms and conditions of the GNU Lesser General Public License,
7 * version 2.1, as published by the Free Software Foundation.
8 *
9 * This program is distributed in the hope it will be useful, but WITHOUT
10 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
11 * FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License
12 * for more details.
13 *
14 * You should have received a copy of the GNU Lesser General Public License
15 * along with this program; if not, write to the Free Software Foundation, Inc.,
16 * 51 Franklin St - Fifth Floor, Boston, MA 02110-1301 USA.
17 *
18 * Mark Ryan <mark.d.ryan@intel.com>
19 *
20 */
21
22 #ifndef DLS_UPNP_H__
23 #define DLS_UPNP_H__
24
25 #include <libdleyna/core/connector.h>
26
27 #include "client.h"
28 #include "async.h"
29
30 typedef void (*dls_upnp_callback_t)(const gchar *path, void *user_data);
31 typedef void (*dls_upnp_task_complete_t)(dls_task_t *task, GError *error);
32
33 dls_upnp_t *dls_upnp_new(dleyna_connector_id_t connection,
34 const dleyna_connector_dispatch_cb_t *dispatch_table,
35 dls_upnp_callback_t found_server,
36 dls_upnp_callback_t lost_server,
37 void *user_data);
38
39 void dls_upnp_delete(dls_upnp_t *upnp);
40
41 GVariant *dls_upnp_get_server_ids(dls_upnp_t *upnp);
42
43 GHashTable *dls_upnp_get_server_udn_map(dls_upnp_t *upnp);
44
45 void dls_upnp_get_children(dls_upnp_t *upnp, dls_client_t *client,
46 dls_task_t *task,
47 dls_upnp_task_complete_t cb);
48
49 void dls_upnp_get_all_props(dls_upnp_t *upnp, dls_client_t *client,
50 dls_task_t *task,
51 dls_upnp_task_complete_t cb);
52
53 void dls_upnp_get_prop(dls_upnp_t *upnp, dls_client_t *client,
54 dls_task_t *task,
55 dls_upnp_task_complete_t cb);
56
57 void dls_upnp_search(dls_upnp_t *upnp, dls_client_t *client,
58 dls_task_t *task,
59 dls_upnp_task_complete_t cb);
60
61 void dls_upnp_get_resource(dls_upnp_t *upnp, dls_client_t *client,
62 dls_task_t *task,
63 dls_upnp_task_complete_t cb);
64
65 void dls_upnp_upload_to_any(dls_upnp_t *upnp, dls_client_t *client,
66 dls_task_t *task,
67 dls_upnp_task_complete_t cb);
68
69 void dls_upnp_upload(dls_upnp_t *upnp, dls_client_t *client,
70 dls_task_t *task,
71 dls_upnp_task_complete_t cb);
72
73 void dls_upnp_get_upload_status(dls_upnp_t *upnp, dls_task_t *task);
74
75 void dls_upnp_get_upload_ids(dls_upnp_t *upnp, dls_task_t *task);
76
77 void dls_upnp_cancel_upload(dls_upnp_t *upnp, dls_task_t *task);
78
79 void dls_upnp_delete_object(dls_upnp_t *upnp, dls_client_t *client,
80 dls_task_t *task,
81 dls_upnp_task_complete_t cb);
82
83 void dls_upnp_create_container(dls_upnp_t *upnp, dls_client_t *client,
84 dls_task_t *task,
85 dls_upnp_task_complete_t cb);
86
87 void dls_upnp_create_container_in_any(dls_upnp_t *upnp, dls_client_t *client,
88 dls_task_t *task,
89 dls_upnp_task_complete_t cb);
90
91 void dls_upnp_update_object(dls_upnp_t *upnp, dls_client_t *client,
92 dls_task_t *task,
93 dls_upnp_task_complete_t cb);
94
95 void dls_upnp_create_playlist(dls_upnp_t *upnp, dls_client_t *client,
96 dls_task_t *task,
97 dls_upnp_task_complete_t cb);
98
99 void dls_upnp_create_playlist_in_any(dls_upnp_t *upnp, dls_client_t *client,
100 dls_task_t *task,
101 dls_upnp_task_complete_t cb);
102
103 void dls_upnp_unsubscribe(dls_upnp_t *upnp);
104
105 gboolean dls_upnp_device_context_exist(dls_device_t *device,
106 dls_device_context_t *context);
107
108 #endif /* DLS_UPNP_H__ */
0 libdleyna_serverincdir = $(includedir)/dleyna-1.0/libdleyna/server
1
2 DLEYNA_SERVER_VERSION = 1:0:0
3
4 AM_CFLAGS = $(GLIB_CFLAGS) \
5 $(GIO_CFLAGS) \
6 $(DLEYNA_CORE_CFLAGS) \
7 $(GSSDP_CFLAGS) \
8 $(GUPNP_CFLAGS) \
9 $(GUPNPAV_CFLAGS) \
10 $(GUPNPDLNA_CFLAGS) \
11 $(SOUP_CFLAGS) \
12 -include config.h
13
14 ACLOCAL_AMFLAGS = -I m4 ${ACLOCAL_FLAGS}
15
16 lib_LTLIBRARIES = libdleyna-server-1.0.la
17
18 libdleyna_serverinc_HEADERS = control-point-server.h
19
20 libdleyna_server_1_0_la_LDFLAGS = -version-info $(DLEYNA_SERVER_VERSION) \
21 -no-undefined
22
23 libdleyna_server_1_0_la_SOURCES = $(libdleyna_serverinc_HEADERS) \
24 server.c \
25 async.c \
26 device.c \
27 path.c \
28 props.c \
29 search.c \
30 sort.c \
31 task.c \
32 upnp.c
33
34 libdleyna_server_1_0_la_LIBADD = $(GLIB_LIBS) \
35 $(GIO_LIBS) \
36 $(DLEYNA_CORE_LIBS) \
37 $(GSSDP_LIBS) \
38 $(GUPNP_LIBS) \
39 $(GUPNPAV_LIBS) \
40 $(GUPNPDLNA_LIBS) \
41 $(SOUP_LIBS)
42
43 MAINTAINERCLEANFILES = Makefile.in \
44 aclocal.m4 \
45 configure \
46 config.h.in \
47 config.h.in~ \
48 build-aux/depcomp \
49 build-aux/compile \
50 build-aux/missing \
51 build-aux/install-sh
52
53 sysconf_DATA = dleyna-server-service.conf
54
55 pkgconfigdir = $(libdir)/pkgconfig
56 pkgconfig_DATA = dleyna-server-1.0.pc
57
58 EXTRA_DIST = $(sysconf_DATA)
59 CLEANFILES = $(pkgconfig_DATA) dleyna-server-service.conf
60 DISTCLEANFILES = $(pkgconfig_DATA) dleyna-server-service.conf
61
62 maintainer-clean-local:
63 rm -rf build-aux
0 /*
1 * dLeyna
2 *
3 * Copyright (C) 2012-2013 Intel Corporation. All rights reserved.
4 *
5 * This program is free software; you can redistribute it and/or modify it
6 * under the terms and conditions of the GNU Lesser General Public License,
7 * version 2.1, as published by the Free Software Foundation.
8 *
9 * This program is distributed in the hope it will be useful, but WITHOUT
10 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
11 * FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License
12 * for more details.
13 *
14 * You should have received a copy of the GNU Lesser General Public License
15 * along with this program; if not, write to the Free Software Foundation, Inc.,
16 * 51 Franklin St - Fifth Floor, Boston, MA 02110-1301 USA.
17 *
18 * Mark Ryan <mark.d.ryan@intel.com>
19 *
20 */
21
22 #include <libdleyna/core/error.h>
23 #include <libdleyna/core/log.h>
24
25 #include "async.h"
26 #include "server.h"
27
28 void dls_async_task_delete(dls_async_task_t *cb_data)
29 {
30 switch (cb_data->task.type) {
31 case DLS_TASK_GET_CHILDREN:
32 case DLS_TASK_SEARCH:
33 if (cb_data->ut.bas.vbs)
34 g_ptr_array_unref(cb_data->ut.bas.vbs);
35 break;
36 case DLS_TASK_GET_ALL_PROPS:
37 case DLS_TASK_GET_RESOURCE:
38 if (cb_data->ut.get_all.vb)
39 g_variant_builder_unref(cb_data->ut.get_all.vb);
40 break;
41 case DLS_TASK_UPLOAD_TO_ANY:
42 case DLS_TASK_UPLOAD:
43 g_free(cb_data->ut.upload.mime_type);
44 break;
45 case DLS_TASK_UPDATE_OBJECT:
46 g_free(cb_data->ut.update.current_tag_value);
47 g_free(cb_data->ut.update.new_tag_value);
48 break;
49 case DLS_TASK_CREATE_PLAYLIST:
50 case DLS_TASK_CREATE_PLAYLIST_IN_ANY:
51 g_free(cb_data->ut.playlist.didl);
52 if (cb_data->ut.playlist.collection)
53 g_object_unref(cb_data->ut.playlist.collection);
54 break;
55 default:
56 break;
57 }
58
59 if (cb_data->cancellable)
60 g_object_unref(cb_data->cancellable);
61 }
62
63 gboolean dls_async_task_complete(gpointer user_data)
64 {
65 dls_async_task_t *cb_data = user_data;
66
67 DLEYNA_LOG_DEBUG("Enter. Error %p", (void *)cb_data->error);
68 DLEYNA_LOG_DEBUG_NL();
69
70 if (cb_data->proxy != NULL)
71 g_object_remove_weak_pointer((G_OBJECT(cb_data->proxy)),
72 (gpointer *)&cb_data->proxy);
73
74 cb_data->cb(&cb_data->task, cb_data->error);
75
76 return FALSE;
77 }
78
79 void dls_async_task_cancelled_cb(GCancellable *cancellable, gpointer user_data)
80 {
81 dls_async_task_t *cb_data = user_data;
82
83 if (cb_data->proxy != NULL)
84 gupnp_service_proxy_cancel_action(cb_data->proxy,
85 cb_data->action);
86
87 if (!cb_data->error)
88 cb_data->error = g_error_new(DLEYNA_SERVER_ERROR,
89 DLEYNA_ERROR_CANCELLED,
90 "Operation cancelled.");
91 (void) g_idle_add(dls_async_task_complete, cb_data);
92 }
93
94 void dls_async_task_cancel(dls_async_task_t *cb_data)
95 {
96 if (cb_data->cancellable)
97 g_cancellable_cancel(cb_data->cancellable);
98 }
0 /*
1 * dLeyna
2 *
3 * Copyright (C) 2012-2013 Intel Corporation. All rights reserved.
4 *
5 * This program is free software; you can redistribute it and/or modify it
6 * under the terms and conditions of the GNU Lesser General Public License,
7 * version 2.1, as published by the Free Software Foundation.
8 *
9 * This program is distributed in the hope it will be useful, but WITHOUT
10 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
11 * FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License
12 * for more details.
13 *
14 * You should have received a copy of the GNU Lesser General Public License
15 * along with this program; if not, write to the Free Software Foundation, Inc.,
16 * 51 Franklin St - Fifth Floor, Boston, MA 02110-1301 USA.
17 *
18 * Mark Ryan <mark.d.ryan@intel.com>
19 *
20 */
21
22 #ifndef DLS_ASYNC_H__
23 #define DLS_ASYNC_H__
24
25 #include <libgupnp/gupnp-control-point.h>
26 #include <libgupnp-av/gupnp-media-collection.h>
27
28 #include <libdleyna/core/task-atom.h>
29
30 #include "server.h"
31 #include "task.h"
32 #include "upnp.h"
33
34 typedef struct dls_async_task_t_ dls_async_task_t;
35 typedef guint64 dls_upnp_prop_mask;
36
37 typedef void (*dls_async_cb_t)(dls_async_task_t *cb_data);
38
39 typedef struct dls_async_bas_t_ dls_async_bas_t;
40 struct dls_async_bas_t_ {
41 dls_upnp_prop_mask filter_mask;
42 GPtrArray *vbs;
43 const gchar *protocol_info;
44 gboolean need_child_count;
45 guint retrieved;
46 guint max_count;
47 dls_async_cb_t get_children_cb;
48 };
49
50 typedef struct dls_async_get_prop_t_ dls_async_get_prop_t;
51 struct dls_async_get_prop_t_ {
52 GCallback prop_func;
53 const gchar *protocol_info;
54 };
55
56 typedef struct dls_async_get_all_t_ dls_async_get_all_t;
57 struct dls_async_get_all_t_ {
58 GCallback prop_func;
59 GVariantBuilder *vb;
60 dls_upnp_prop_mask filter_mask;
61 const gchar *protocol_info;
62 gboolean need_child_count;
63 gboolean device_object;
64 };
65
66 typedef struct dls_async_upload_t_ dls_async_upload_t;
67 struct dls_async_upload_t_ {
68 const gchar *object_class;
69 gchar *mime_type;
70 };
71
72 typedef struct dls_async_update_t_ dls_async_update_t;
73 struct dls_async_update_t_ {
74 gchar *current_tag_value;
75 gchar *new_tag_value;
76 GHashTable *map;
77 };
78
79 typedef struct dls_async_playlist_t_ dls_async_playlist_t;
80 struct dls_async_playlist_t_ {
81 const dleyna_task_queue_key_t *queue_id;
82 GUPnPMediaCollection *collection;
83 gchar *didl;
84 };
85
86 struct dls_async_task_t_ {
87 dls_task_t task; /* pseudo inheritance - MUST be first field */
88 dls_upnp_task_complete_t cb;
89 GError *error;
90 GUPnPServiceProxyAction *action;
91 GUPnPServiceProxy *proxy;
92 GCancellable *cancellable;
93 gulong cancel_id;
94 union {
95 dls_async_bas_t bas;
96 dls_async_get_prop_t get_prop;
97 dls_async_get_all_t get_all;
98 dls_async_upload_t upload;
99 dls_async_update_t update;
100 dls_async_playlist_t playlist;
101 } ut;
102 };
103
104 void dls_async_task_delete(dls_async_task_t *cb_data);
105
106 gboolean dls_async_task_complete(gpointer user_data);
107
108 void dls_async_task_cancelled_cb(GCancellable *cancellable, gpointer user_data);
109
110 void dls_async_task_cancel(dls_async_task_t *cb_data);
111
112 #endif /* DLS_ASYNC_H__ */
0 /*
1 * dLeyna
2 *
3 * Copyright (C) 2012-2013 Intel Corporation. All rights reserved.
4 *
5 * This program is free software; you can redistribute it and/or modify it
6 * under the terms and conditions of the GNU Lesser General Public License,
7 * version 2.1, as published by the Free Software Foundation.
8 *
9 * This program is distributed in the hope it will be useful, but WITHOUT
10 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
11 * FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License
12 * for more details.
13 *
14 * You should have received a copy of the GNU Lesser General Public License
15 * along with this program; if not, write to the Free Software Foundation, Inc.,
16 * 51 Franklin St - Fifth Floor, Boston, MA 02110-1301 USA.
17 *
18 * Regis Merlino <regis.merlino@intel.com>
19 *
20 */
21
22 #ifndef DLS_CLIENT_H__
23 #define DLS_CLIENT_H__
24
25 #include <glib.h>
26
27 typedef struct dls_client_t_ dls_client_t;
28 struct dls_client_t_ {
29 gchar *protocol_info;
30 gboolean prefer_local_addresses;
31 };
32
33 #endif /* DLS_CLIENT_H__ */
0 /*
1 * dLeyna
2 *
3 * Copyright (C) 2013 Intel Corporation. All rights reserved.
4 *
5 * This program is free software; you can redistribute it and/or modify it
6 * under the terms and conditions of the GNU Lesser General Public License,
7 * version 2.1, as published by the Free Software Foundation.
8 *
9 * This program is distributed in the hope it will be useful, but WITHOUT
10 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
11 * FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License
12 * for more details.
13 *
14 * You should have received a copy of the GNU Lesser General Public License
15 * along with this program; if not, write to the Free Software Foundation, Inc.,
16 * 51 Franklin St - Fifth Floor, Boston, MA 02110-1301 USA.
17 *
18 * Regis Merlino <regis.merlino@intel.com>
19 *
20 */
21
22 #ifndef DLEYNA_CONTROL_POINT_SERVER_H__
23 #define DLEYNA_CONTROL_POINT_SERVER_H__
24
25 #include <libdleyna/core/control-point.h>
26
27 const dleyna_control_point_t *dleyna_control_point_get_server(void);
28
29 #endif /* DLEYNA_CONTROL_POINT_SERVER_H__ */
0 /*
1 * dLeyna
2 *
3 * Copyright (C) 2012-2013 Intel Corporation. All rights reserved.
4 *
5 * This program is free software; you can redistribute it and/or modify it
6 * under the terms and conditions of the GNU Lesser General Public License,
7 * version 2.1, as published by the Free Software Foundation.
8 *
9 * This program is distributed in the hope it will be useful, but WITHOUT
10 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
11 * FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License
12 * for more details.
13 *
14 * You should have received a copy of the GNU Lesser General Public License
15 * along with this program; if not, write to the Free Software Foundation, Inc.,
16 * 51 Franklin St - Fifth Floor, Boston, MA 02110-1301 USA.
17 *
18 * Mark Ryan <mark.d.ryan@intel.com>
19 *
20 */
21
22 #include <string.h>
23 #include <libgupnp/gupnp-error.h>
24 #include <libgupnp-dlna/gupnp-dlna-profile.h>
25 #include <libgupnp-dlna/gupnp-dlna-profile-guesser.h>
26 #include <libgupnp-av/gupnp-media-collection.h>
27 #include <libgupnp-av/gupnp-cds-last-change-parser.h>
28 #include <libsoup/soup.h>
29
30 #include <libdleyna/core/error.h>
31 #include <libdleyna/core/log.h>
32 #include <libdleyna/core/service-task.h>
33
34 #include "device.h"
35 #include "interface.h"
36 #include "path.h"
37 #include "server.h"
38
39 #define DLS_SYSTEM_UPDATE_VAR "SystemUpdateID"
40 #define DLS_CONTAINER_UPDATE_VAR "ContainerUpdateIDs"
41 #define DLS_LAST_CHANGE_VAR "LastChange"
42 #define DLS_DMS_DEVICE_TYPE "urn:schemas-upnp-org:device:MediaServer:"
43
44 #define DLS_UPLOAD_STATUS_IN_PROGRESS "IN_PROGRESS"
45 #define DLS_UPLOAD_STATUS_CANCELLED "CANCELLED"
46 #define DLS_UPLOAD_STATUS_ERROR "ERROR"
47 #define DLS_UPLOAD_STATUS_COMPLETED "COMPLETED"
48
49 typedef gboolean(*dls_device_count_cb_t)(dls_async_task_t *cb_data,
50 gint count);
51
52 typedef struct dls_device_count_data_t_ dls_device_count_data_t;
53 struct dls_device_count_data_t_ {
54 dls_device_count_cb_t cb;
55 dls_async_task_t *cb_data;
56 };
57
58 typedef struct dls_device_object_builder_t_ dls_device_object_builder_t;
59 struct dls_device_object_builder_t_ {
60 GVariantBuilder *vb;
61 gchar *id;
62 gboolean needs_child_count;
63 };
64
65 typedef struct dls_device_upload_t_ dls_device_upload_t;
66 struct dls_device_upload_t_ {
67 SoupSession *soup_session;
68 SoupMessage *msg;
69 GMappedFile *mapped_file;
70 gchar *body;
71 gsize body_length;
72 const gchar *status;
73 guint64 bytes_uploaded;
74 guint64 bytes_to_upload;
75 };
76
77 typedef struct dls_device_upload_job_t_ dls_device_upload_job_t;
78 struct dls_device_upload_job_t_ {
79 gint upload_id;
80 dls_device_t *device;
81 guint remove_idle;
82 };
83
84 /* Private structure used in chain task */
85 typedef struct prv_new_device_ct_t_ prv_new_device_ct_t;
86 struct prv_new_device_ct_t_ {
87 dls_device_t *dev;
88 dleyna_connector_id_t connection;
89 const dleyna_connector_dispatch_cb_t *vtable;
90 GHashTable *property_map;
91 };
92
93 typedef struct prv_new_playlist_ct_t_ prv_new_playlist_ct_t;
94 struct prv_new_playlist_ct_t_ {
95 dls_async_task_t *cb_data;
96 gchar *id;
97 gchar *parent_id;
98 };
99
100 static void prv_get_child_count(dls_async_task_t *cb_data,
101 dls_device_count_cb_t cb, const gchar *id);
102 static void prv_retrieve_child_count_for_list(dls_async_task_t *cb_data);
103 static void prv_container_update_cb(GUPnPServiceProxy *proxy,
104 const char *variable,
105 GValue *value,
106 gpointer user_data);
107 static void prv_system_update_cb(GUPnPServiceProxy *proxy,
108 const char *variable,
109 GValue *value,
110 gpointer user_data);
111 static void prv_last_change_cb(GUPnPServiceProxy *proxy,
112 const char *variable,
113 GValue *value,
114 gpointer user_data);
115 static void prv_upload_delete(gpointer up);
116 static void prv_upload_job_delete(gpointer up);
117 static void prv_get_sr_token_for_props(GUPnPServiceProxy *proxy,
118 const dls_device_t *device,
119 dls_async_task_t *cb_data);
120
121 static void prv_object_builder_delete(void *dob)
122 {
123 dls_device_object_builder_t *builder = dob;
124
125 if (builder) {
126 if (builder->vb)
127 g_variant_builder_unref(builder->vb);
128
129 g_free(builder->id);
130 g_free(builder);
131 }
132 }
133
134 static void prv_count_data_new(dls_async_task_t *cb_data,
135 dls_device_count_cb_t cb,
136 dls_device_count_data_t **count_data)
137 {
138 dls_device_count_data_t *cd;
139
140 cd = g_new(dls_device_count_data_t, 1);
141 cd->cb = cb;
142 cd->cb_data = cb_data;
143
144 *count_data = cd;
145 }
146
147 static void prv_context_unsubscribe(dls_device_context_t *ctx)
148 {
149 if (ctx->timeout_id) {
150 (void) g_source_remove(ctx->timeout_id);
151 ctx->timeout_id = 0;
152 }
153
154 if (ctx->subscribed) {
155 gupnp_service_proxy_remove_notify(
156 ctx->service_proxy,
157 DLS_SYSTEM_UPDATE_VAR,
158 prv_system_update_cb,
159 ctx->device);
160 gupnp_service_proxy_remove_notify(
161 ctx->service_proxy,
162 DLS_CONTAINER_UPDATE_VAR,
163 prv_container_update_cb,
164 ctx->device);
165 gupnp_service_proxy_remove_notify(
166 ctx->service_proxy,
167 DLS_LAST_CHANGE_VAR,
168 prv_last_change_cb,
169 ctx->device);
170
171 gupnp_service_proxy_set_subscribed(ctx->service_proxy,
172 FALSE);
173
174 ctx->subscribed = FALSE;
175 }
176 }
177
178 static void prv_context_delete(gpointer context)
179 {
180 dls_device_context_t *ctx = context;
181
182 if (ctx) {
183 prv_context_unsubscribe(ctx);
184
185 if (ctx->device_proxy)
186 g_object_unref(ctx->device_proxy);
187
188 if (ctx->service_proxy)
189 g_object_unref(ctx->service_proxy);
190
191 g_free(ctx->ip_address);
192 g_free(ctx);
193 }
194 }
195
196 static void prv_context_new(const gchar *ip_address,
197 GUPnPDeviceProxy *proxy,
198 dls_device_t *device,
199 dls_device_context_t **context)
200 {
201 const gchar *service_type =
202 "urn:schemas-upnp-org:service:ContentDirectory";
203 dls_device_context_t *ctx = g_new(dls_device_context_t, 1);
204
205 ctx->ip_address = g_strdup(ip_address);
206 ctx->device_proxy = proxy;
207 ctx->device = device;
208 g_object_ref(proxy);
209 ctx->service_proxy = (GUPnPServiceProxy *)
210 gupnp_device_info_get_service((GUPnPDeviceInfo *)proxy,
211 service_type);
212 ctx->subscribed = FALSE;
213 ctx->timeout_id = 0;
214
215 *context = ctx;
216 }
217
218 void dls_device_delete(void *device)
219 {
220 dls_device_t *dev = device;
221
222 if (dev) {
223 dev->shutting_down = TRUE;
224 g_hash_table_unref(dev->upload_jobs);
225 g_hash_table_unref(dev->uploads);
226
227 if (dev->timeout_id)
228 (void) g_source_remove(dev->timeout_id);
229
230 if (dev->id)
231 (void) dls_server_get_connector()->unpublish_subtree(
232 dev->connection, dev->id);
233
234 g_ptr_array_unref(dev->contexts);
235 g_free(dev->path);
236 g_variant_unref(dev->search_caps);
237 g_variant_unref(dev->sort_caps);
238 g_variant_unref(dev->sort_ext_caps);
239 g_variant_unref(dev->feature_list);
240 g_free(dev);
241 }
242 }
243
244 void dls_device_unsubscribe(void *device)
245 {
246 unsigned int i;
247 dls_device_t *dev = device;
248 dls_device_context_t *context;
249
250 if (dev) {
251 for (i = 0; i < dev->contexts->len; ++i) {
252 context = g_ptr_array_index(dev->contexts, i);
253 prv_context_unsubscribe(context);
254 }
255 }
256 }
257
258 static void prv_last_change_decode(GUPnPCDSLastChangeEntry *entry,
259 GVariantBuilder *array,
260 const char *root_path)
261 {
262 GUPnPCDSLastChangeEvent event;
263 GVariant *state;
264 const char *object_id;
265 const char *parent_id;
266 const char *mclass;
267 const char *media_class;
268 char *key[] = {"ADD", "DEL", "MOD", "DONE"};
269 char *parent_path;
270 char *path = NULL;
271 gboolean sub_update;
272 guint32 update_id;
273
274 object_id = gupnp_cds_last_change_entry_get_object_id(entry);
275 if (!object_id)
276 goto on_error;
277
278 sub_update = gupnp_cds_last_change_entry_is_subtree_update(entry);
279 update_id = gupnp_cds_last_change_entry_get_update_id(entry);
280 path = dls_path_from_id(root_path, object_id);
281 event = gupnp_cds_last_change_entry_get_event(entry);
282
283 switch (event) {
284 case GUPNP_CDS_LAST_CHANGE_EVENT_OBJECT_ADDED:
285 parent_id = gupnp_cds_last_change_entry_get_parent_id(entry);
286 if (!parent_id)
287 goto on_error;
288
289 mclass = gupnp_cds_last_change_entry_get_class(entry);
290 if (!mclass)
291 goto on_error;
292
293 media_class = dls_props_upnp_class_to_media_spec(mclass);
294 if (!media_class)
295 goto on_error;
296
297 parent_path = dls_path_from_id(root_path, parent_id);
298 state = g_variant_new("(oubos)", path, update_id, sub_update,
299 parent_path, media_class);
300 g_free(parent_path);
301 break;
302 case GUPNP_CDS_LAST_CHANGE_EVENT_OBJECT_REMOVED:
303 case GUPNP_CDS_LAST_CHANGE_EVENT_OBJECT_MODIFIED:
304 state = g_variant_new("(oub)", path, update_id, sub_update);
305 break;
306 case GUPNP_CDS_LAST_CHANGE_EVENT_ST_DONE:
307 state = g_variant_new("(ou)", path, update_id);
308 break;
309 case GUPNP_CDS_LAST_CHANGE_EVENT_INVALID:
310 default:
311 goto on_error;
312 break;
313 }
314
315 g_variant_builder_add(array, "(sv)", key[event - 1], state);
316
317 on_error:
318
319 g_free(path);
320 }
321
322 static void prv_last_change_cb(GUPnPServiceProxy *proxy,
323 const char *variable,
324 GValue *value,
325 gpointer user_data)
326 {
327 const gchar *last_change;
328 GVariantBuilder array;
329 GVariant *val;
330 dls_device_t *device = user_data;
331 GUPnPCDSLastChangeParser *parser;
332 GList *list;
333 GList *next;
334 GError *error = NULL;
335
336 last_change = g_value_get_string(value);
337
338 DLEYNA_LOG_DEBUG_NL();
339 DLEYNA_LOG_DEBUG("LastChange XML: %s", last_change);
340 DLEYNA_LOG_DEBUG_NL();
341
342 parser = gupnp_cds_last_change_parser_new();
343 list = gupnp_cds_last_change_parser_parse(parser, last_change, &error);
344
345 if (error != NULL) {
346 DLEYNA_LOG_WARNING(
347 "gupnp_cds_last_change_parser_parse parsing failed: %s",
348 error->message);
349 goto on_error;
350 }
351
352 g_variant_builder_init(&array, G_VARIANT_TYPE("a(sv)"));
353 next = list;
354 while (next) {
355 prv_last_change_decode(next->data, &array, device->path);
356 gupnp_cds_last_change_entry_unref(next->data);
357 next = g_list_next(next);
358 }
359
360 val = g_variant_new("(@a(sv))", g_variant_builder_end(&array));
361
362 (void) dls_server_get_connector()->notify(device->connection,
363 device->path,
364 DLEYNA_SERVER_INTERFACE_MEDIA_DEVICE,
365 DLS_INTERFACE_ESV_LAST_CHANGE,
366 val,
367 NULL);
368
369 on_error:
370
371 g_list_free(list);
372 g_object_unref(parser);
373
374 if (error != NULL)
375 g_error_free(error);
376 }
377
378 static void prv_build_container_update_array(const gchar *root_path,
379 const gchar *value,
380 GVariantBuilder *builder)
381 {
382 gchar **str_array;
383 int pos = 0;
384 gchar *path;
385 guint id;
386
387 str_array = g_strsplit(value, ",", 0);
388
389 DLEYNA_LOG_DEBUG_NL();
390
391 while (str_array[pos] && str_array[pos + 1]) {
392 path = dls_path_from_id(root_path, str_array[pos++]);
393 id = atoi(str_array[pos++]);
394 g_variant_builder_add(builder, "(ou)", path, id);
395 DLEYNA_LOG_DEBUG("@Id [%s] - Path [%s] - id[%d]",
396 str_array[pos-2], path, id);
397 g_free(path);
398 }
399
400 DLEYNA_LOG_DEBUG_NL();
401
402 g_strfreev(str_array);
403 }
404
405 static void prv_container_update_cb(GUPnPServiceProxy *proxy,
406 const char *variable,
407 GValue *value,
408 gpointer user_data)
409 {
410 dls_device_t *device = user_data;
411 GVariantBuilder array;
412
413 g_variant_builder_init(&array, G_VARIANT_TYPE("a(ou)"));
414
415 prv_build_container_update_array(device->path,
416 g_value_get_string(value),
417 &array);
418
419 (void) dls_server_get_connector()->notify(
420 device->connection,
421 device->path,
422 DLEYNA_SERVER_INTERFACE_MEDIA_DEVICE,
423 DLS_INTERFACE_ESV_CONTAINER_UPDATE_IDS,
424 g_variant_new("(@a(ou))",
425 g_variant_builder_end(&array)),
426 NULL);
427 }
428
429 static void prv_system_update_cb(GUPnPServiceProxy *proxy,
430 const char *variable,
431 GValue *value,
432 gpointer user_data)
433 {
434 GVariantBuilder *array;
435 GVariant *val;
436 dls_device_t *device = user_data;
437 guint suid = g_value_get_uint(value);
438
439 DLEYNA_LOG_DEBUG("System Update %u", suid);
440
441 device->system_update_id = suid;
442
443 array = g_variant_builder_new(G_VARIANT_TYPE("a{sv}"));
444 g_variant_builder_add(array, "{sv}",
445 DLS_INTERFACE_PROP_ESV_SYSTEM_UPDATE_ID,
446 g_variant_new_uint32(suid));
447 val = g_variant_new("(s@a{sv}as)", DLEYNA_SERVER_INTERFACE_MEDIA_DEVICE,
448 g_variant_builder_end(array),
449 NULL);
450
451 (void) dls_server_get_connector()->notify(device->connection,
452 device->path,
453 DLS_INTERFACE_PROPERTIES,
454 DLS_INTERFACE_PROPERTIES_CHANGED,
455 val,
456 NULL);
457
458 g_variant_builder_unref(array);
459 }
460
461 static gboolean prv_re_enable_subscription(gpointer user_data)
462 {
463 dls_device_context_t *context = user_data;
464
465 context->timeout_id = 0;
466
467 return FALSE;
468 }
469
470 static void prv_subscription_lost_cb(GUPnPServiceProxy *proxy,
471 const GError *reason,
472 gpointer user_data)
473 {
474 dls_device_context_t *context = user_data;
475
476 if (!context->timeout_id) {
477 gupnp_service_proxy_set_subscribed(context->service_proxy,
478 TRUE);
479 context->timeout_id = g_timeout_add_seconds(
480 10,
481 prv_re_enable_subscription,
482 context);
483 } else {
484 g_source_remove(context->timeout_id);
485 gupnp_service_proxy_remove_notify(context->service_proxy,
486 DLS_SYSTEM_UPDATE_VAR,
487 prv_system_update_cb,
488 context->device);
489 gupnp_service_proxy_remove_notify(context->service_proxy,
490 DLS_CONTAINER_UPDATE_VAR,
491 prv_container_update_cb,
492 context->device);
493 gupnp_service_proxy_remove_notify(context->service_proxy,
494 DLS_LAST_CHANGE_VAR,
495 prv_last_change_cb,
496 context->device);
497
498 context->timeout_id = 0;
499 context->subscribed = FALSE;
500 }
501 }
502
503 void dls_device_subscribe_to_contents_change(dls_device_t *device)
504 {
505 dls_device_context_t *context;
506
507 context = dls_device_get_context(device, NULL);
508
509 DLEYNA_LOG_DEBUG("Subscribe for events on context: %s",
510 context->ip_address);
511
512 gupnp_service_proxy_add_notify(context->service_proxy,
513 DLS_SYSTEM_UPDATE_VAR,
514 G_TYPE_UINT,
515 prv_system_update_cb,
516 device);
517
518 gupnp_service_proxy_add_notify(context->service_proxy,
519 DLS_CONTAINER_UPDATE_VAR,
520 G_TYPE_STRING,
521 prv_container_update_cb,
522 device);
523
524 gupnp_service_proxy_add_notify(context->service_proxy,
525 DLS_LAST_CHANGE_VAR,
526 G_TYPE_STRING,
527 prv_last_change_cb,
528 device);
529
530 context->subscribed = TRUE;
531 gupnp_service_proxy_set_subscribed(context->service_proxy, TRUE);
532
533 g_signal_connect(context->service_proxy,
534 "subscription-lost",
535 G_CALLBACK(prv_subscription_lost_cb),
536 context);
537 }
538
539 static void prv_feature_list_add_feature(gchar *root_path,
540 GUPnPFeature *feature,
541 GVariantBuilder *vb)
542 {
543 GVariantBuilder vbo;
544 GVariant *var_obj;
545 const char *name;
546 const char *version;
547 const char *obj_id;
548 gchar **obj;
549 gchar **saved;
550 gchar *path;
551
552 name = gupnp_feature_get_name(feature);
553 version = gupnp_feature_get_version(feature);
554 obj_id = gupnp_feature_get_object_ids(feature);
555
556 g_variant_builder_init(&vbo, G_VARIANT_TYPE("ao"));
557
558 if (obj_id != NULL && *obj_id) {
559 obj = g_strsplit(obj_id, ",", 0);
560 saved = obj;
561
562 while (obj && *obj) {
563 path = dls_path_from_id(root_path, *obj);
564 g_variant_builder_add(&vbo, "o", path);
565 g_free(path);
566 obj++;
567 }
568
569 g_strfreev(saved);
570 }
571
572 var_obj = g_variant_builder_end(&vbo);
573 g_variant_builder_add(vb, "(ss@ao)", name, version, var_obj);
574 }
575
576 static void prv_get_feature_list_analyze(dls_device_t *device, gchar *result)
577 {
578 GUPnPFeatureListParser *parser;
579 GUPnPFeature *feature;
580 GList *list;
581 GList *item;
582 GError *error = NULL;
583 GVariantBuilder vb;
584 #if DLEYNA_LOG_LEVEL & DLEYNA_LOG_LEVEL_DEBUG
585 char *str;
586 #endif
587 parser = gupnp_feature_list_parser_new();
588 list = gupnp_feature_list_parser_parse_text(parser, result, &error);
589
590 if (error != NULL) {
591 DLEYNA_LOG_WARNING("GetFeatureList parsing failed: %s",
592 error->message);
593 goto on_exit;
594 }
595
596 g_variant_builder_init(&vb, G_VARIANT_TYPE("a(ssao)"));
597 item = list;
598
599 while (item != NULL) {
600 feature = (GUPnPFeature *)item->data;
601 prv_feature_list_add_feature(device->path, feature, &vb);
602 g_object_unref(feature);
603 item = g_list_next(item);
604 }
605
606 device->feature_list = g_variant_ref_sink(g_variant_builder_end(&vb));
607
608 #if DLEYNA_LOG_LEVEL & DLEYNA_LOG_LEVEL_DEBUG
609 str = g_variant_print(device->feature_list, FALSE);
610 DLEYNA_LOG_DEBUG("%s = %s", DLS_INTERFACE_PROP_SV_FEATURE_LIST, str);
611 g_free(str);
612 #endif
613
614 on_exit:
615 g_list_free(list);
616 g_object_unref(parser);
617
618 if (error != NULL)
619 g_error_free(error);
620 }
621
622 static void prv_get_feature_list_cb(GUPnPServiceProxy *proxy,
623 GUPnPServiceProxyAction *action,
624 gpointer user_data)
625 {
626 gchar *result = NULL;
627 GError *error = NULL;
628 prv_new_device_ct_t *priv_t = (prv_new_device_ct_t *)user_data;
629
630 if (!gupnp_service_proxy_end_action(proxy, action, &error,
631 "FeatureList", G_TYPE_STRING,
632 &result, NULL)) {
633 DLEYNA_LOG_WARNING("GetFeatureList operation failed: %s",
634 error->message);
635 goto on_error;
636 }
637
638 DLEYNA_LOG_DEBUG("GetFeatureList result: %s", result);
639
640 prv_get_feature_list_analyze(priv_t->dev, result);
641
642 on_error:
643 if (error != NULL)
644 g_error_free(error);
645
646 g_free(result);
647 }
648
649 static GUPnPServiceProxyAction *prv_get_feature_list(
650 dleyna_service_task_t *task,
651 GUPnPServiceProxy *proxy,
652 gboolean *failed)
653 {
654 *failed = FALSE;
655
656 return gupnp_service_proxy_begin_action(
657 proxy, "GetFeatureList",
658 dleyna_service_task_begin_action_cb,
659 task, NULL);
660 }
661
662 static void prv_get_sort_ext_capabilities_analyze(dls_device_t *device,
663 gchar *result)
664 {
665 gchar **caps;
666 gchar **saved;
667 #if DLEYNA_LOG_LEVEL & DLEYNA_LOG_LEVEL_DEBUG
668 gchar *props;
669 #endif
670 GVariantBuilder sort_ext_caps_vb;
671
672 g_variant_builder_init(&sort_ext_caps_vb, G_VARIANT_TYPE("as"));
673
674 caps = g_strsplit(result, ",", 0);
675 saved = caps;
676
677 while (caps && *caps) {
678 g_variant_builder_add(&sort_ext_caps_vb, "s", *caps);
679 caps++;
680 }
681
682 g_strfreev(saved);
683
684 device->sort_ext_caps = g_variant_ref_sink(g_variant_builder_end(
685 &sort_ext_caps_vb));
686
687 #if DLEYNA_LOG_LEVEL & DLEYNA_LOG_LEVEL_DEBUG
688 props = g_variant_print(device->sort_ext_caps, FALSE);
689 DLEYNA_LOG_DEBUG("%s = %s",
690 DLS_INTERFACE_PROP_SV_SORT_EXT_CAPABILITIES, props);
691 g_free(props);
692 #endif
693 }
694
695 static void prv_get_sort_ext_capabilities_cb(GUPnPServiceProxy *proxy,
696 GUPnPServiceProxyAction *action,
697 gpointer user_data)
698 {
699 gchar *result = NULL;
700 GError *error = NULL;
701 prv_new_device_ct_t *priv_t = (prv_new_device_ct_t *)user_data;
702
703 if (!gupnp_service_proxy_end_action(proxy, action, &error,
704 "SortExtensionCaps",
705 G_TYPE_STRING, &result, NULL)) {
706 DLEYNA_LOG_WARNING(
707 "GetSortExtensionCapabilities operation failed: %s",
708 error->message);
709 goto on_error;
710 }
711
712 DLEYNA_LOG_DEBUG("GetSortExtensionCapabilities result: %s", result);
713
714 prv_get_sort_ext_capabilities_analyze(priv_t->dev, result);
715
716 on_error:
717
718 if (error)
719 g_error_free(error);
720
721 g_free(result);
722 }
723
724 static GUPnPServiceProxyAction *prv_get_sort_ext_capabilities(
725 dleyna_service_task_t *task,
726 GUPnPServiceProxy *proxy,
727 gboolean *failed)
728 {
729 *failed = FALSE;
730
731 return gupnp_service_proxy_begin_action(
732 proxy,
733 "GetSortExtensionCapabilities",
734 dleyna_service_task_begin_action_cb,
735 task, NULL);
736 }
737
738 static void prv_get_capabilities_analyze(GHashTable *property_map,
739 gchar *result,
740 GVariant **variant)
741 {
742 gchar **caps;
743 gchar **saved;
744 gchar *prop_name;
745 GVariantBuilder caps_vb;
746
747 g_variant_builder_init(&caps_vb, G_VARIANT_TYPE("as"));
748
749 caps = g_strsplit(result, ",", 0);
750 saved = caps;
751
752 while (caps && *caps) {
753 prop_name = g_hash_table_lookup(property_map, *caps);
754
755 if (prop_name)
756 g_variant_builder_add(&caps_vb, "s", prop_name);
757
758 caps++;
759 }
760
761 g_strfreev(saved);
762
763 *variant = g_variant_ref_sink(g_variant_builder_end(&caps_vb));
764
765 #if DLEYNA_LOG_LEVEL & DLEYNA_LOG_LEVEL_DEBUG
766 prop_name = g_variant_print(*variant, FALSE);
767 DLEYNA_LOG_DEBUG("%s = %s", " Variant", prop_name);
768 g_free(prop_name);
769 #endif
770 }
771
772 static void prv_get_sort_capabilities_cb(GUPnPServiceProxy *proxy,
773 GUPnPServiceProxyAction *action,
774 gpointer user_data)
775 {
776 gchar *result = NULL;
777 GError *error = NULL;
778 prv_new_device_ct_t *priv_t = (prv_new_device_ct_t *)user_data;
779
780 if (!gupnp_service_proxy_end_action(proxy, action, &error, "SortCaps",
781 G_TYPE_STRING, &result, NULL)) {
782 DLEYNA_LOG_WARNING("GetSortCapabilities operation failed: %s",
783 error->message);
784 goto on_error;
785 }
786
787 DLEYNA_LOG_DEBUG("GetSortCapabilities result: %s", result);
788
789 prv_get_capabilities_analyze(priv_t->property_map, result,
790 &priv_t->dev->sort_caps);
791
792 on_error:
793
794 if (error)
795 g_error_free(error);
796
797 g_free(result);
798 }
799
800 static GUPnPServiceProxyAction *prv_get_sort_capabilities(
801 dleyna_service_task_t *task,
802 GUPnPServiceProxy *proxy,
803 gboolean *failed)
804 {
805 *failed = FALSE;
806
807 return gupnp_service_proxy_begin_action(
808 proxy,
809 "GetSortCapabilities",
810 dleyna_service_task_begin_action_cb,
811 task, NULL);
812 }
813
814 static void prv_get_search_capabilities_cb(GUPnPServiceProxy *proxy,
815 GUPnPServiceProxyAction *action,
816 gpointer user_data)
817 {
818 gchar *result = NULL;
819 GError *error = NULL;
820 prv_new_device_ct_t *priv_t = (prv_new_device_ct_t *)user_data;
821
822 if (!gupnp_service_proxy_end_action(proxy, action, &error, "SearchCaps",
823 G_TYPE_STRING, &result, NULL)) {
824 DLEYNA_LOG_WARNING("GetSearchCapabilities operation failed: %s",
825 error->message);
826 goto on_error;
827 }
828
829 DLEYNA_LOG_DEBUG("GetSearchCapabilities result: %s", result);
830
831 prv_get_capabilities_analyze(priv_t->property_map, result,
832 &priv_t->dev->search_caps);
833
834 on_error:
835
836 if (error)
837 g_error_free(error);
838
839 g_free(result);
840 }
841
842 static GUPnPServiceProxyAction *prv_get_search_capabilities(
843 dleyna_service_task_t *task,
844 GUPnPServiceProxy *proxy,
845 gboolean *failed)
846 {
847 *failed = FALSE;
848
849 return gupnp_service_proxy_begin_action(
850 proxy, "GetSearchCapabilities",
851 dleyna_service_task_begin_action_cb,
852 task, NULL);
853 }
854
855 static GUPnPServiceProxyAction *prv_subscribe(dleyna_service_task_t *task,
856 GUPnPServiceProxy *proxy,
857 gboolean *failed)
858 {
859 dls_device_t *device;
860
861 device = (dls_device_t *)dleyna_service_task_get_user_data(task);
862 dls_device_subscribe_to_contents_change(device);
863
864 *failed = FALSE;
865
866 return NULL;
867 }
868
869 static gboolean prv_subtree_interface_filter(const gchar *object_path,
870 const gchar *node,
871 const gchar *interface)
872 {
873 gboolean root_object = FALSE;
874 const gchar *slash;
875 gboolean retval = TRUE;
876
877 /* All objects in the hierarchy support the same interface. Strictly
878 speaking this is not correct as it will allow ListChildren to be
879 executed on a mediaitem object. However, returning the correct
880 interface here would be too inefficient. We would need to either
881 cache the type of all objects encountered so far or issue a UPnP
882 request here to determine the objects type. Best to let the client
883 call ListChildren on a item. This will lead to an error when we
884 execute the UPnP command and we can return an error then.
885
886 We do know however that the root objects are containers. Therefore
887 we can remove the MediaItem2 interface from the root containers. We
888 also know that only the root objects suport the MediaDevice
889 interface.
890 */
891
892 if (dls_path_get_non_root_id(object_path, &slash))
893 root_object = !slash;
894
895 if (root_object && !strcmp(interface, DLS_INTERFACE_MEDIA_ITEM))
896 retval = FALSE;
897 else if (!root_object && !strcmp(interface,
898 DLEYNA_SERVER_INTERFACE_MEDIA_DEVICE))
899 retval = FALSE;
900
901 return retval;
902 }
903
904 static GUPnPServiceProxyAction *prv_declare(dleyna_service_task_t *task,
905 GUPnPServiceProxy *proxy,
906 gboolean *failed)
907 {
908 guint id;
909 dls_device_t *device;
910 prv_new_device_ct_t *priv_t;
911
912 priv_t = (prv_new_device_ct_t *)dleyna_service_task_get_user_data(task);
913 device = priv_t->dev;
914
915 id = dls_server_get_connector()->publish_subtree(priv_t->connection,
916 device->path,
917 priv_t->vtable,
918 DLS_INTERFACE_INFO_MAX,
919 prv_subtree_interface_filter);
920 if (id) {
921 device->id = id;
922
923 device->uploads = g_hash_table_new_full(
924 g_int_hash,
925 g_int_equal,
926 g_free,
927 prv_upload_delete);
928 device->upload_jobs = g_hash_table_new_full(
929 g_int_hash,
930 g_int_equal,
931 g_free,
932 prv_upload_job_delete);
933
934 } else {
935 DLEYNA_LOG_WARNING("dleyna_connector_publish_subtree FAILED");
936 }
937
938 *failed = (!id);
939
940 return NULL;
941 }
942
943 dls_device_t *dls_device_new(
944 dleyna_connector_id_t connection,
945 GUPnPDeviceProxy *proxy,
946 const gchar *ip_address,
947 const dleyna_connector_dispatch_cb_t *dispatch_table,
948 GHashTable *property_map,
949 guint counter,
950 const dleyna_task_queue_key_t *queue_id)
951 {
952 dls_device_t *dev;
953 prv_new_device_ct_t *priv_t;
954 gchar *new_path;
955 dls_device_context_t *context;
956 GUPnPServiceProxy *s_proxy;
957
958 DLEYNA_LOG_DEBUG("New Device on %s", ip_address);
959
960 new_path = g_strdup_printf("%s/%u", DLEYNA_SERVER_PATH, counter);
961 DLEYNA_LOG_DEBUG("Server Path %s", new_path);
962
963 dev = g_new0(dls_device_t, 1);
964 priv_t = g_new0(prv_new_device_ct_t, 1);
965
966 dev->connection = connection;
967 dev->contexts = g_ptr_array_new_with_free_func(prv_context_delete);
968 dev->path = new_path;
969
970 priv_t->dev = dev;
971 priv_t->connection = connection;
972 priv_t->vtable = dispatch_table;
973 priv_t->property_map = property_map;
974
975 context = dls_device_append_new_context(dev, ip_address, proxy);
976 s_proxy = context->service_proxy;
977
978 dleyna_service_task_add(queue_id, prv_get_search_capabilities,
979 s_proxy,
980 prv_get_search_capabilities_cb, NULL, priv_t);
981
982 dleyna_service_task_add(queue_id, prv_get_sort_capabilities,
983 s_proxy,
984 prv_get_sort_capabilities_cb, NULL, priv_t);
985
986 dleyna_service_task_add(queue_id, prv_get_sort_ext_capabilities,
987 s_proxy,
988 prv_get_sort_ext_capabilities_cb, NULL, priv_t);
989
990 dleyna_service_task_add(queue_id, prv_get_feature_list, s_proxy,
991 prv_get_feature_list_cb, NULL, priv_t);
992
993 dleyna_service_task_add(queue_id, prv_subscribe, s_proxy,
994 NULL, NULL, dev);
995
996 dleyna_service_task_add(queue_id, prv_declare, s_proxy,
997 NULL, g_free, priv_t);
998
999 dleyna_task_queue_start(queue_id);
1000
1001 return dev;
1002 }
1003
1004 dls_device_context_t *dls_device_append_new_context(dls_device_t *device,
1005 const gchar *ip_address,
1006 GUPnPDeviceProxy *proxy)
1007 {
1008 dls_device_context_t *context;
1009
1010 prv_context_new(ip_address, proxy, device, &context);
1011 g_ptr_array_add(device->contexts, context);
1012
1013 return context;
1014 }
1015
1016 dls_device_t *dls_device_from_path(const gchar *path, GHashTable *device_list)
1017 {
1018 GHashTableIter iter;
1019 gpointer value;
1020 dls_device_t *device;
1021 dls_device_t *retval = NULL;
1022
1023 g_hash_table_iter_init(&iter, device_list);
1024
1025 while (g_hash_table_iter_next(&iter, NULL, &value)) {
1026 device = value;
1027
1028 if (!strcmp(device->path, path)) {
1029 retval = device;
1030 break;
1031 }
1032 }
1033
1034 return retval;
1035 }
1036
1037 dls_device_context_t *dls_device_get_context(const dls_device_t *device,
1038 dls_client_t *client)
1039 {
1040 dls_device_context_t *context;
1041 unsigned int i;
1042 const char ip4_local_prefix[] = "127.0.0.";
1043 gboolean prefer_local;
1044 gboolean is_local;
1045
1046 prefer_local = (client && client->prefer_local_addresses);
1047
1048 for (i = 0; i < device->contexts->len; ++i) {
1049 context = g_ptr_array_index(device->contexts, i);
1050
1051 is_local = (!strncmp(context->ip_address, ip4_local_prefix,
1052 sizeof(ip4_local_prefix) - 1) ||
1053 !strcmp(context->ip_address, "::1") ||
1054 !strcmp(context->ip_address, "0:0:0:0:0:0:0:1"));
1055
1056 if (prefer_local == is_local)
1057 break;
1058 }
1059
1060 if (i == device->contexts->len)
1061 context = g_ptr_array_index(device->contexts, 0);
1062
1063 return context;
1064 }
1065
1066 static void prv_found_child(GUPnPDIDLLiteParser *parser,
1067 GUPnPDIDLLiteObject *object,
1068 gpointer user_data)
1069 {
1070 dls_async_task_t *cb_data = user_data;
1071 dls_task_t *task = &cb_data->task;
1072 dls_task_get_children_t *task_data = &task->ut.get_children;
1073 dls_async_bas_t *cb_task_data = &cb_data->ut.bas;
1074 dls_device_object_builder_t *builder;
1075 gboolean have_child_count;
1076
1077 DLEYNA_LOG_DEBUG("Enter");
1078
1079 builder = g_new0(dls_device_object_builder_t, 1);
1080
1081 if (GUPNP_IS_DIDL_LITE_CONTAINER(object)) {
1082 if (!task_data->containers)
1083 goto on_error;
1084 } else {
1085 if (!task_data->items)
1086 goto on_error;
1087 }
1088
1089 builder->vb = g_variant_builder_new(G_VARIANT_TYPE("a{sv}"));
1090
1091 if (!dls_props_add_object(builder->vb, object, task->target.root_path,
1092 task->target.path, cb_task_data->filter_mask))
1093 goto on_error;
1094
1095 if (GUPNP_IS_DIDL_LITE_CONTAINER(object)) {
1096 dls_props_add_container(builder->vb,
1097 (GUPnPDIDLLiteContainer *)object,
1098 cb_task_data->filter_mask,
1099 &have_child_count);
1100
1101 if (!have_child_count && (cb_task_data->filter_mask &
1102 DLS_UPNP_MASK_PROP_CHILD_COUNT)) {
1103 builder->needs_child_count = TRUE;
1104 builder->id = g_strdup(
1105 gupnp_didl_lite_object_get_id(object));
1106 cb_task_data->need_child_count = TRUE;
1107 }
1108 } else {
1109 dls_props_add_item(builder->vb, object,
1110 task->target.root_path,
1111 cb_task_data->filter_mask,
1112 cb_task_data->protocol_info);
1113 }
1114
1115 g_ptr_array_add(cb_task_data->vbs, builder);
1116
1117 DLEYNA_LOG_DEBUG("Exit with SUCCESS");
1118
1119 return;
1120
1121 on_error:
1122
1123 prv_object_builder_delete(builder);
1124
1125 DLEYNA_LOG_DEBUG("Exit with FAIL");
1126 }
1127
1128 static GVariant *prv_children_result_to_variant(dls_async_task_t *cb_data)
1129 {
1130 guint i;
1131 dls_device_object_builder_t *builder;
1132 dls_async_bas_t *cb_task_data = &cb_data->ut.bas;
1133 GVariantBuilder vb;
1134
1135 g_variant_builder_init(&vb, G_VARIANT_TYPE("aa{sv}"));
1136
1137 for (i = 0; i < cb_task_data->vbs->len; ++i) {
1138 builder = g_ptr_array_index(cb_task_data->vbs, i);
1139 g_variant_builder_add(&vb, "@a{sv}",
1140 g_variant_builder_end(builder->vb));
1141 }
1142
1143 return g_variant_builder_end(&vb);
1144 }
1145
1146 static void prv_get_search_ex_result(dls_async_task_t *cb_data)
1147 {
1148 GVariant *out_params[2];
1149 dls_async_bas_t *cb_task_data = &cb_data->ut.bas;
1150
1151 out_params[0] = prv_children_result_to_variant(cb_data);
1152 out_params[1] = g_variant_new_uint32(cb_task_data->max_count);
1153
1154 cb_data->task.result = g_variant_ref_sink(
1155 g_variant_new_tuple(out_params, 2));
1156 }
1157
1158 static void prv_get_children_result(dls_async_task_t *cb_data)
1159 {
1160 GVariant *retval = prv_children_result_to_variant(cb_data);
1161 cb_data->task.result = g_variant_ref_sink(retval);
1162 }
1163
1164 static gboolean prv_child_count_for_list_cb(dls_async_task_t *cb_data,
1165 gint count)
1166 {
1167 dls_async_bas_t *cb_task_data = &cb_data->ut.bas;
1168 dls_device_object_builder_t *builder;
1169
1170 builder = g_ptr_array_index(cb_task_data->vbs, cb_task_data->retrieved);
1171 dls_props_add_child_count(builder->vb, count);
1172 cb_task_data->retrieved++;
1173 prv_retrieve_child_count_for_list(cb_data);
1174
1175 return cb_task_data->retrieved >= cb_task_data->vbs->len;
1176 }
1177
1178 static void prv_retrieve_child_count_for_list(dls_async_task_t *cb_data)
1179 {
1180 dls_async_bas_t *cb_task_data = &cb_data->ut.bas;
1181 dls_device_object_builder_t *builder;
1182 guint i;
1183
1184 for (i = cb_task_data->retrieved; i < cb_task_data->vbs->len; ++i) {
1185 builder = g_ptr_array_index(cb_task_data->vbs, i);
1186
1187 if (builder->needs_child_count)
1188 break;
1189 }
1190
1191 cb_task_data->retrieved = i;
1192
1193 if (i < cb_task_data->vbs->len)
1194 prv_get_child_count(cb_data, prv_child_count_for_list_cb,
1195 builder->id);
1196 else
1197 cb_task_data->get_children_cb(cb_data);
1198 }
1199
1200 static void prv_get_children_cb(GUPnPServiceProxy *proxy,
1201 GUPnPServiceProxyAction *action,
1202 gpointer user_data)
1203 {
1204 gchar *result = NULL;
1205 GUPnPDIDLLiteParser *parser = NULL;
1206 GError *upnp_error = NULL;
1207 dls_async_task_t *cb_data = user_data;
1208 dls_async_bas_t *cb_task_data = &cb_data->ut.bas;
1209
1210 DLEYNA_LOG_DEBUG("Enter");
1211
1212 if (!gupnp_service_proxy_end_action(cb_data->proxy, cb_data->action,
1213 &upnp_error,
1214 "Result", G_TYPE_STRING,
1215 &result, NULL)) {
1216 DLEYNA_LOG_WARNING("Browse operation failed: %s",
1217 upnp_error->message);
1218
1219 cb_data->error = g_error_new(DLEYNA_SERVER_ERROR,
1220 DLEYNA_ERROR_OPERATION_FAILED,
1221 "Browse operation failed: %s",
1222 upnp_error->message);
1223 goto on_error;
1224 }
1225
1226 DLEYNA_LOG_DEBUG("GetChildren result: %s", result);
1227
1228 parser = gupnp_didl_lite_parser_new();
1229
1230 g_signal_connect(parser, "object-available" ,
1231 G_CALLBACK(prv_found_child), cb_data);
1232
1233 cb_task_data->vbs = g_ptr_array_new_with_free_func(
1234 prv_object_builder_delete);
1235
1236 if (!gupnp_didl_lite_parser_parse_didl(parser, result, &upnp_error) &&
1237 upnp_error->code != GUPNP_XML_ERROR_EMPTY_NODE) {
1238 DLEYNA_LOG_WARNING("Unable to parse results of browse: %s",
1239 upnp_error->message);
1240
1241 cb_data->error = g_error_new(DLEYNA_SERVER_ERROR,
1242 DLEYNA_ERROR_OPERATION_FAILED,
1243 "Unable to parse results of browse: %s",
1244 upnp_error->message);
1245 goto on_error;
1246 }
1247
1248 if (cb_task_data->need_child_count) {
1249 DLEYNA_LOG_DEBUG("Need to retrieve ChildCounts");
1250
1251 cb_task_data->get_children_cb = prv_get_children_result;
1252 prv_retrieve_child_count_for_list(cb_data);
1253 goto no_complete;
1254 } else {
1255 prv_get_children_result(cb_data);
1256 }
1257
1258 on_error:
1259
1260 (void) g_idle_add(dls_async_task_complete, cb_data);
1261 g_cancellable_disconnect(cb_data->cancellable, cb_data->cancel_id);
1262
1263 no_complete:
1264
1265 if (upnp_error)
1266 g_error_free(upnp_error);
1267
1268 if (parser)
1269 g_object_unref(parser);
1270
1271 g_free(result);
1272
1273 DLEYNA_LOG_DEBUG("Exit");
1274 }
1275
1276 void dls_device_get_children(dls_client_t *client,
1277 dls_task_t *task,
1278 const gchar *upnp_filter, const gchar *sort_by)
1279 {
1280 dls_async_task_t *cb_data = (dls_async_task_t *)task;
1281 dls_device_context_t *context;
1282
1283 DLEYNA_LOG_DEBUG("Enter");
1284
1285 context = dls_device_get_context(task->target.device, client);
1286
1287 cb_data->action =
1288 gupnp_service_proxy_begin_action(context->service_proxy,
1289 "Browse",
1290 prv_get_children_cb,
1291 cb_data,
1292 "ObjectID", G_TYPE_STRING,
1293 task->target.id,
1294
1295 "BrowseFlag", G_TYPE_STRING,
1296 "BrowseDirectChildren",
1297
1298 "Filter", G_TYPE_STRING,
1299 upnp_filter,
1300
1301 "StartingIndex", G_TYPE_INT,
1302 task->ut.get_children.start,
1303 "RequestedCount", G_TYPE_INT,
1304 task->ut.get_children.count,
1305 "SortCriteria", G_TYPE_STRING,
1306 sort_by,
1307 NULL);
1308
1309 cb_data->proxy = context->service_proxy;
1310
1311 g_object_add_weak_pointer((G_OBJECT(context->service_proxy)),
1312 (gpointer *)&cb_data->proxy);
1313
1314 cb_data->cancel_id = g_cancellable_connect(
1315 cb_data->cancellable,
1316 G_CALLBACK(dls_async_task_cancelled_cb),
1317 cb_data, NULL);
1318
1319 DLEYNA_LOG_DEBUG("Exit");
1320 }
1321
1322 static void prv_get_item(GUPnPDIDLLiteParser *parser,
1323 GUPnPDIDLLiteObject *object,
1324 gpointer user_data)
1325 {
1326 dls_async_task_t *cb_data = user_data;
1327 dls_async_get_all_t *cb_task_data = &cb_data->ut.get_all;
1328
1329 if (!GUPNP_IS_DIDL_LITE_CONTAINER(object))
1330 dls_props_add_item(cb_task_data->vb, object,
1331 cb_data->task.target.root_path,
1332 DLS_UPNP_MASK_ALL_PROPS,
1333 cb_task_data->protocol_info);
1334 else
1335 cb_data->error = g_error_new(DLEYNA_SERVER_ERROR,
1336 DLEYNA_ERROR_UNKNOWN_INTERFACE,
1337 "Interface not supported on container.");
1338 }
1339
1340 static void prv_get_container(GUPnPDIDLLiteParser *parser,
1341 GUPnPDIDLLiteObject *object,
1342 gpointer user_data)
1343 {
1344 dls_async_task_t *cb_data = user_data;
1345 dls_async_get_all_t *cb_task_data = &cb_data->ut.get_all;
1346 gboolean have_child_count;
1347
1348 if (GUPNP_IS_DIDL_LITE_CONTAINER(object)) {
1349 dls_props_add_container(cb_task_data->vb,
1350 (GUPnPDIDLLiteContainer *)object,
1351 DLS_UPNP_MASK_ALL_PROPS,
1352 &have_child_count);
1353 if (!have_child_count)
1354 cb_task_data->need_child_count = TRUE;
1355 } else {
1356 cb_data->error = g_error_new(DLEYNA_SERVER_ERROR,
1357 DLEYNA_ERROR_UNKNOWN_INTERFACE,
1358 "Interface not supported on item.");
1359 }
1360 }
1361
1362 static void prv_get_object(GUPnPDIDLLiteParser *parser,
1363 GUPnPDIDLLiteObject *object,
1364 gpointer user_data)
1365 {
1366 dls_async_task_t *cb_data = user_data;
1367 dls_async_get_all_t *cb_task_data = &cb_data->ut.get_all;
1368 const char *id;
1369 const char *parent_path;
1370 gchar *path = NULL;
1371
1372 id = gupnp_didl_lite_object_get_parent_id(object);
1373
1374 if (!id || !strcmp(id, "-1") || !strcmp(id, "")) {
1375 parent_path = cb_data->task.target.root_path;
1376 } else {
1377 path = dls_path_from_id(cb_data->task.target.root_path, id);
1378 parent_path = path;
1379 }
1380
1381 if (!dls_props_add_object(cb_task_data->vb, object,
1382 cb_data->task.target.root_path,
1383 parent_path, DLS_UPNP_MASK_ALL_PROPS))
1384 cb_data->error = g_error_new(DLEYNA_SERVER_ERROR,
1385 DLEYNA_ERROR_BAD_RESULT,
1386 "Unable to retrieve mandatory object properties");
1387 g_free(path);
1388 }
1389
1390 static void prv_get_all(GUPnPDIDLLiteParser *parser,
1391 GUPnPDIDLLiteObject *object,
1392 gpointer user_data)
1393 {
1394 dls_async_task_t *cb_data = user_data;
1395 dls_async_get_all_t *cb_task_data = &cb_data->ut.get_all;
1396 gboolean have_child_count;
1397
1398 prv_get_object(parser, object, user_data);
1399
1400 if (!cb_data->error) {
1401 if (GUPNP_IS_DIDL_LITE_CONTAINER(object)) {
1402 dls_props_add_container(
1403 cb_task_data->vb,
1404 (GUPnPDIDLLiteContainer *)
1405 object, DLS_UPNP_MASK_ALL_PROPS,
1406 &have_child_count);
1407 if (!have_child_count)
1408 cb_task_data->need_child_count = TRUE;
1409 } else {
1410 dls_props_add_item(cb_task_data->vb,
1411 object,
1412 cb_data->task.target.root_path,
1413 DLS_UPNP_MASK_ALL_PROPS,
1414 cb_task_data->protocol_info);
1415 }
1416 }
1417 }
1418
1419 static gboolean prv_subscribed(const dls_device_t *device)
1420 {
1421 dls_device_context_t *context;
1422 unsigned int i;
1423 gboolean subscribed = FALSE;
1424
1425 for (i = 0; i < device->contexts->len; ++i) {
1426 context = g_ptr_array_index(device->contexts, i);
1427 if (context->subscribed) {
1428 subscribed = TRUE;
1429 break;
1430 }
1431 }
1432
1433 return subscribed;
1434 }
1435
1436 static void prv_system_update_id_for_prop_cb(GUPnPServiceProxy *proxy,
1437 GUPnPServiceProxyAction *action,
1438 gpointer user_data)
1439 {
1440 GError *upnp_error = NULL;
1441 guint id;
1442 dls_async_task_t *cb_data = user_data;
1443
1444 DLEYNA_LOG_DEBUG("Enter");
1445
1446 if (!gupnp_service_proxy_end_action(proxy, action, &upnp_error,
1447 "Id", G_TYPE_UINT,
1448 &id,
1449 NULL)) {
1450 DLEYNA_LOG_WARNING("Unable to retrieve ServiceUpdateID: %s %s",
1451 g_quark_to_string(upnp_error->domain),
1452 upnp_error->message);
1453
1454 cb_data->error = g_error_new(
1455 DLEYNA_SERVER_ERROR,
1456 DLEYNA_ERROR_OPERATION_FAILED,
1457 "Unable to retrieve ServiceUpdateID: %s",
1458 upnp_error->message);
1459
1460 goto on_complete;
1461 }
1462
1463 cb_data->task.result = g_variant_ref_sink(g_variant_new_uint32(id));
1464
1465 on_complete:
1466
1467 (void) g_idle_add(dls_async_task_complete, cb_data);
1468 g_cancellable_disconnect(cb_data->cancellable, cb_data->cancel_id);
1469
1470 if (upnp_error)
1471 g_error_free(upnp_error);
1472
1473 DLEYNA_LOG_DEBUG("Exit");
1474 }
1475
1476 static void prv_get_system_update_id_for_prop(GUPnPServiceProxy *proxy,
1477 const dls_device_t *device,
1478 dls_async_task_t *cb_data)
1479 {
1480 guint suid;
1481
1482 DLEYNA_LOG_DEBUG("Enter");
1483
1484 if (prv_subscribed(device)) {
1485 suid = device->system_update_id;
1486
1487 cb_data->task.result = g_variant_ref_sink(
1488 g_variant_new_uint32(suid));
1489
1490 (void) g_idle_add(dls_async_task_complete, cb_data);
1491
1492 goto on_complete;
1493 }
1494
1495 gupnp_service_proxy_begin_action(proxy, "GetSystemUpdateID",
1496 prv_system_update_id_for_prop_cb,
1497 cb_data,
1498 NULL);
1499
1500 cb_data->proxy = proxy;
1501
1502 g_object_add_weak_pointer((G_OBJECT(proxy)),
1503 (gpointer *)&cb_data->proxy);
1504
1505 cb_data->cancel_id = g_cancellable_connect(
1506 cb_data->cancellable,
1507 G_CALLBACK(dls_async_task_cancelled_cb),
1508 cb_data, NULL);
1509
1510 on_complete:
1511
1512 DLEYNA_LOG_DEBUG("Exit");
1513 }
1514
1515 static void prv_system_update_id_for_props_cb(GUPnPServiceProxy *proxy,
1516 GUPnPServiceProxyAction *action,
1517 gpointer user_data)
1518 {
1519 GError *upnp_error = NULL;
1520 guint id;
1521 dls_async_task_t *cb_data = user_data;
1522 dls_async_get_all_t *cb_task_data = &cb_data->ut.get_all;
1523
1524 DLEYNA_LOG_DEBUG("Enter");
1525
1526 if (!gupnp_service_proxy_end_action(proxy, action, &upnp_error,
1527 "Id", G_TYPE_UINT,
1528 &id,
1529 NULL)) {
1530 DLEYNA_LOG_WARNING("Unable to retrieve ServiceUpdateID: %s %s",
1531 g_quark_to_string(upnp_error->domain),
1532 upnp_error->message);
1533
1534 cb_data->error = g_error_new(
1535 DLEYNA_SERVER_ERROR,
1536 DLEYNA_ERROR_OPERATION_FAILED,
1537 "Unable to retrieve ServiceUpdateID: %s",
1538 upnp_error->message);
1539
1540 goto on_complete;
1541 }
1542
1543 g_variant_builder_add(cb_task_data->vb, "{sv}",
1544 DLS_SYSTEM_UPDATE_VAR,
1545 g_variant_new_uint32(id));
1546
1547 cb_data->task.result = g_variant_ref_sink(g_variant_builder_end(
1548 cb_task_data->vb));
1549
1550 on_complete:
1551
1552 if (!cb_data->error)
1553 prv_get_sr_token_for_props(proxy, cb_data->task.target.device,
1554 cb_data);
1555 else {
1556 (void) g_idle_add(dls_async_task_complete, cb_data);
1557 g_cancellable_disconnect(cb_data->cancellable,
1558 cb_data->cancel_id);
1559 }
1560
1561 if (upnp_error)
1562 g_error_free(upnp_error);
1563
1564 DLEYNA_LOG_DEBUG("Exit");
1565 }
1566
1567 static void prv_get_system_update_id_for_props(GUPnPServiceProxy *proxy,
1568 const dls_device_t *device,
1569 dls_async_task_t *cb_data)
1570 {
1571 dls_async_get_all_t *cb_task_data;
1572 guint suid;
1573
1574 DLEYNA_LOG_DEBUG("Enter");
1575
1576 if (prv_subscribed(device)) {
1577 suid = device->system_update_id;
1578
1579 cb_task_data = &cb_data->ut.get_all;
1580
1581 g_variant_builder_add(cb_task_data->vb, "{sv}",
1582 DLS_SYSTEM_UPDATE_VAR,
1583 g_variant_new_uint32(suid));
1584
1585 prv_get_sr_token_for_props(proxy, device, cb_data);
1586
1587 goto on_complete;
1588 }
1589
1590 gupnp_service_proxy_begin_action(proxy, "GetSystemUpdateID",
1591 prv_system_update_id_for_props_cb,
1592 cb_data,
1593 NULL);
1594
1595 cb_data->proxy = proxy;
1596
1597 g_object_add_weak_pointer((G_OBJECT(proxy)),
1598 (gpointer *)&cb_data->proxy);
1599
1600 cb_data->cancel_id = g_cancellable_connect(
1601 cb_data->cancellable,
1602 G_CALLBACK(dls_async_task_cancelled_cb),
1603 cb_data, NULL);
1604
1605 on_complete:
1606
1607 DLEYNA_LOG_DEBUG("Exit");
1608 }
1609
1610 static int prv_get_media_server_version(const dls_device_t *device)
1611 {
1612 dls_device_context_t *context;
1613 const char *device_type;
1614 const char *version;
1615
1616 context = dls_device_get_context(device, NULL);
1617 device_type = gupnp_device_info_get_device_type((GUPnPDeviceInfo *)
1618 context->device_proxy);
1619
1620 if (!g_str_has_prefix(device_type, DLS_DMS_DEVICE_TYPE))
1621 goto on_error;
1622
1623 version = device_type + sizeof(DLS_DMS_DEVICE_TYPE) - 1;
1624
1625 return atoi(version);
1626
1627 on_error:
1628
1629 return -1;
1630 }
1631
1632 static void prv_service_reset_for_prop_cb(GUPnPServiceProxy *proxy,
1633 GUPnPServiceProxyAction *action,
1634 gpointer user_data)
1635 {
1636 GError *upnp_error = NULL;
1637 gchar *token = NULL;
1638 dls_async_task_t *cb_data = user_data;
1639
1640 DLEYNA_LOG_DEBUG("Enter");
1641
1642 if (!gupnp_service_proxy_end_action(proxy, action, &upnp_error,
1643 "ResetToken", G_TYPE_STRING,
1644 &token,
1645 NULL)) {
1646 DLEYNA_LOG_WARNING(
1647 "Unable to retrieve ServiceResetToken: %s %s",
1648 g_quark_to_string(upnp_error->domain),
1649 upnp_error->message);
1650
1651
1652 cb_data->error = g_error_new(DLEYNA_SERVER_ERROR,
1653 DLEYNA_ERROR_OPERATION_FAILED,
1654 "GetServiceResetToken failed: %s",
1655 upnp_error->message);
1656
1657 goto on_complete;
1658 }
1659
1660 cb_data->task.result = g_variant_ref_sink(g_variant_new_string(token));
1661
1662 DLEYNA_LOG_DEBUG("Service Reset %s", token);
1663
1664 g_free(token);
1665
1666 on_complete:
1667
1668 (void) g_idle_add(dls_async_task_complete, cb_data);
1669 g_cancellable_disconnect(cb_data->cancellable, cb_data->cancel_id);
1670
1671 if (upnp_error)
1672 g_error_free(upnp_error);
1673
1674 DLEYNA_LOG_DEBUG("Exit");
1675 }
1676
1677 static void prv_get_sr_token_for_prop(GUPnPServiceProxy *proxy,
1678 const dls_device_t *device,
1679 dls_async_task_t *cb_data)
1680 {
1681 DLEYNA_LOG_DEBUG("Enter");
1682
1683 if (prv_get_media_server_version(device) < 3) {
1684 cb_data->error = g_error_new(DLEYNA_SERVER_ERROR,
1685 DLEYNA_ERROR_UNKNOWN_PROPERTY,
1686 "Unknown property");
1687
1688 (void) g_idle_add(dls_async_task_complete, cb_data);
1689
1690 goto on_error;
1691 }
1692
1693 gupnp_service_proxy_begin_action(proxy, "GetServiceResetToken",
1694 prv_service_reset_for_prop_cb,
1695 cb_data,
1696 NULL);
1697
1698 cb_data->proxy = proxy;
1699
1700 g_object_add_weak_pointer((G_OBJECT(proxy)),
1701 (gpointer *)&cb_data->proxy);
1702
1703 cb_data->cancel_id = g_cancellable_connect(
1704 cb_data->cancellable,
1705 G_CALLBACK(dls_async_task_cancelled_cb),
1706 cb_data, NULL);
1707
1708 on_error:
1709
1710 DLEYNA_LOG_DEBUG("Exit");
1711 }
1712
1713 static void prv_service_reset_for_props_cb(GUPnPServiceProxy *proxy,
1714 GUPnPServiceProxyAction *action,
1715 gpointer user_data)
1716 {
1717 GError *upnp_error = NULL;
1718 gchar *token = NULL;
1719 dls_async_task_t *cb_data = user_data;
1720 dls_async_get_all_t *cb_task_data;
1721
1722 DLEYNA_LOG_DEBUG("Enter");
1723
1724 if (!gupnp_service_proxy_end_action(proxy, action, &upnp_error,
1725 "ResetToken", G_TYPE_STRING,
1726 &token,
1727 NULL)) {
1728 DLEYNA_LOG_WARNING(
1729 "Unable to retrieve ServiceResetToken: %s %s",
1730 g_quark_to_string(upnp_error->domain),
1731 upnp_error->message);
1732
1733 cb_data->error = g_error_new(DLEYNA_SERVER_ERROR,
1734 DLEYNA_ERROR_OPERATION_FAILED,
1735 "GetServiceResetToken failed: %s",
1736 upnp_error->message);
1737
1738 goto on_complete;
1739 }
1740
1741 cb_task_data = &cb_data->ut.get_all;
1742 g_variant_builder_add(cb_task_data->vb, "{sv}",
1743 DLS_INTERFACE_PROP_SV_SERVICE_RESET_TOKEN,
1744 g_variant_new_string(token));
1745
1746 cb_data->task.result = g_variant_ref_sink(g_variant_builder_end(
1747 cb_task_data->vb));
1748
1749 DLEYNA_LOG_DEBUG("Service Reset %s", token);
1750
1751 g_free(token);
1752
1753 on_complete:
1754
1755 (void) g_idle_add(dls_async_task_complete, cb_data);
1756 g_cancellable_disconnect(cb_data->cancellable, cb_data->cancel_id);
1757
1758 if (upnp_error)
1759 g_error_free(upnp_error);
1760
1761 DLEYNA_LOG_DEBUG("Exit");
1762 }
1763
1764 static void prv_get_sr_token_for_props(GUPnPServiceProxy *proxy,
1765 const dls_device_t *device,
1766 dls_async_task_t *cb_data)
1767 {
1768 dls_async_get_all_t *cb_task_data;
1769
1770 DLEYNA_LOG_DEBUG("Enter");
1771
1772 if (prv_get_media_server_version(device) < 3) {
1773 cb_task_data = &cb_data->ut.get_all;
1774
1775 cb_data->task.result = g_variant_ref_sink(g_variant_builder_end(
1776 cb_task_data->vb));
1777
1778 goto on_complete; /* No error here, just skip the property */
1779 }
1780
1781 gupnp_service_proxy_begin_action(proxy, "GetServiceResetToken",
1782 prv_service_reset_for_props_cb,
1783 cb_data,
1784 NULL);
1785
1786 cb_data->proxy = proxy;
1787
1788 g_object_add_weak_pointer((G_OBJECT(proxy)),
1789 (gpointer *)&cb_data->proxy);
1790
1791 cb_data->cancel_id = g_cancellable_connect(
1792 cb_data->cancellable,
1793 G_CALLBACK(dls_async_task_cancelled_cb),
1794 cb_data, NULL);
1795
1796 DLEYNA_LOG_DEBUG("Exit");
1797
1798 return;
1799
1800 on_complete:
1801
1802 (void) g_idle_add(dls_async_task_complete, cb_data);
1803
1804 DLEYNA_LOG_DEBUG("Exit");
1805 }
1806
1807 static gboolean prv_get_all_child_count_cb(dls_async_task_t *cb_data,
1808 gint count)
1809 {
1810 dls_async_get_all_t *cb_task_data = &cb_data->ut.get_all;
1811
1812 dls_props_add_child_count(cb_task_data->vb, count);
1813 if (cb_task_data->device_object)
1814 prv_get_system_update_id_for_props(cb_data->proxy,
1815 cb_data->task.target.device,
1816 cb_data);
1817 else
1818 cb_data->task.result = g_variant_ref_sink(g_variant_builder_end(
1819 cb_task_data->vb));
1820
1821 return !cb_task_data->device_object;
1822 }
1823
1824 static void prv_get_all_ms2spec_props_cb(GUPnPServiceProxy *proxy,
1825 GUPnPServiceProxyAction *action,
1826 gpointer user_data)
1827 {
1828 GError *upnp_error = NULL;
1829 gchar *result = NULL;
1830 GUPnPDIDLLiteParser *parser = NULL;
1831 dls_async_task_t *cb_data = user_data;
1832 dls_async_get_all_t *cb_task_data = &cb_data->ut.get_all;
1833
1834 DLEYNA_LOG_DEBUG("Enter");
1835
1836 if (!gupnp_service_proxy_end_action(cb_data->proxy, cb_data->action,
1837 &upnp_error,
1838 "Result", G_TYPE_STRING,
1839 &result, NULL)) {
1840 DLEYNA_LOG_WARNING("Browse operation failed: %s",
1841 upnp_error->message);
1842
1843 cb_data->error = g_error_new(DLEYNA_SERVER_ERROR,
1844 DLEYNA_ERROR_OPERATION_FAILED,
1845 "Browse operation failed: %s",
1846 upnp_error->message);
1847 goto on_error;
1848 }
1849
1850 DLEYNA_LOG_DEBUG("GetMS2SpecProps result: %s", result);
1851
1852 parser = gupnp_didl_lite_parser_new();
1853
1854 g_signal_connect(parser, "object-available" , cb_task_data->prop_func,
1855 cb_data);
1856
1857 if (!gupnp_didl_lite_parser_parse_didl(parser, result, &upnp_error)) {
1858 if (upnp_error->code == GUPNP_XML_ERROR_EMPTY_NODE) {
1859 DLEYNA_LOG_WARNING("Property not defined for object");
1860
1861 cb_data->error =
1862 g_error_new(DLEYNA_SERVER_ERROR,
1863 DLEYNA_ERROR_UNKNOWN_PROPERTY,
1864 "Property not defined for object");
1865 } else {
1866 DLEYNA_LOG_WARNING(
1867 "Unable to parse results of browse: %s",
1868 upnp_error->message);
1869
1870 cb_data->error =
1871 g_error_new(DLEYNA_SERVER_ERROR,
1872 DLEYNA_ERROR_OPERATION_FAILED,
1873 "Unable to parse results of browse: %s",
1874 upnp_error->message);
1875 }
1876 goto on_error;
1877 }
1878
1879 if (cb_data->error)
1880 goto on_error;
1881
1882 if (cb_task_data->need_child_count) {
1883 DLEYNA_LOG_DEBUG("Need Child Count");
1884
1885 prv_get_child_count(cb_data, prv_get_all_child_count_cb,
1886 cb_data->task.target.id);
1887
1888 goto no_complete;
1889 } else if (cb_data->task.type == DLS_TASK_GET_ALL_PROPS &&
1890 cb_task_data->device_object) {
1891 prv_get_system_update_id_for_props(proxy,
1892 cb_data->task.target.device,
1893 cb_data);
1894
1895 goto no_complete;
1896 } else {
1897 cb_data->task.result = g_variant_ref_sink(g_variant_builder_end(
1898 cb_task_data->vb));
1899 }
1900
1901 on_error:
1902
1903 (void) g_idle_add(dls_async_task_complete, cb_data);
1904 g_cancellable_disconnect(cb_data->cancellable, cb_data->cancel_id);
1905
1906 no_complete:
1907
1908 if (upnp_error)
1909 g_error_free(upnp_error);
1910
1911 if (parser)
1912 g_object_unref(parser);
1913
1914 g_free(result);
1915
1916 DLEYNA_LOG_DEBUG("Exit");
1917 }
1918
1919 static void prv_get_all_ms2spec_props(dls_device_context_t *context,
1920 dls_async_task_t *cb_data)
1921 {
1922 dls_async_get_all_t *cb_task_data = &cb_data->ut.get_all;
1923 dls_task_t *task = &cb_data->task;
1924 dls_task_get_props_t *task_data = &task->ut.get_props;
1925
1926 DLEYNA_LOG_DEBUG("Enter called");
1927
1928 if (!strcmp(DLS_INTERFACE_MEDIA_CONTAINER, task_data->interface_name)) {
1929 cb_task_data->prop_func = G_CALLBACK(prv_get_container);
1930 } else if (!strcmp(DLS_INTERFACE_MEDIA_ITEM,
1931 task_data->interface_name)) {
1932 cb_task_data->prop_func = G_CALLBACK(prv_get_item);
1933 } else if (!strcmp(DLS_INTERFACE_MEDIA_OBJECT,
1934 task_data->interface_name)) {
1935 cb_task_data->prop_func = G_CALLBACK(prv_get_object);
1936 } else if (!strcmp("", task_data->interface_name)) {
1937 cb_task_data->prop_func = G_CALLBACK(prv_get_all);
1938 } else {
1939 DLEYNA_LOG_WARNING("Interface is unknown.");
1940
1941 cb_data->error =
1942 g_error_new(DLEYNA_SERVER_ERROR,
1943 DLEYNA_ERROR_UNKNOWN_INTERFACE,
1944 "Interface is unknown.");
1945 goto on_error;
1946 }
1947
1948 cb_data->action = gupnp_service_proxy_begin_action(
1949 context->service_proxy, "Browse",
1950 prv_get_all_ms2spec_props_cb, cb_data,
1951 "ObjectID", G_TYPE_STRING, task->target.id,
1952 "BrowseFlag", G_TYPE_STRING, "BrowseMetadata",
1953 "Filter", G_TYPE_STRING, "*",
1954 "StartingIndex", G_TYPE_INT, 0,
1955 "RequestedCount", G_TYPE_INT, 0,
1956 "SortCriteria", G_TYPE_STRING,
1957 "", NULL);
1958
1959 cb_data->proxy = context->service_proxy;
1960
1961 g_object_add_weak_pointer((G_OBJECT(context->service_proxy)),
1962 (gpointer *)&cb_data->proxy);
1963
1964 cb_data->cancel_id = g_cancellable_connect(
1965 cb_data->cancellable,
1966 G_CALLBACK(dls_async_task_cancelled_cb),
1967 cb_data, NULL);
1968
1969 DLEYNA_LOG_DEBUG("Exit with SUCCESS");
1970
1971 return;
1972
1973 on_error:
1974
1975 (void) g_idle_add(dls_async_task_complete, cb_data);
1976
1977 DLEYNA_LOG_DEBUG("Exit with FAIL");
1978
1979 return;
1980 }
1981
1982 void dls_device_get_all_props(dls_client_t *client,
1983 dls_task_t *task,
1984 gboolean root_object)
1985 {
1986 dls_async_task_t *cb_data = (dls_async_task_t *)task;
1987 dls_async_get_all_t *cb_task_data;
1988 dls_task_get_props_t *task_data = &task->ut.get_props;
1989 dls_device_context_t *context;
1990
1991 DLEYNA_LOG_DEBUG("Enter");
1992
1993 context = dls_device_get_context(task->target.device, client);
1994 cb_task_data = &cb_data->ut.get_all;
1995
1996 cb_task_data->vb = g_variant_builder_new(G_VARIANT_TYPE("a{sv}"));
1997 cb_task_data->device_object = root_object;
1998
1999 if (!strcmp(task_data->interface_name,
2000 DLEYNA_SERVER_INTERFACE_MEDIA_DEVICE)) {
2001 if (root_object) {
2002 dls_props_add_device(
2003 (GUPnPDeviceInfo *)context->device_proxy,
2004 task->target.device,
2005 cb_task_data->vb);
2006
2007 prv_get_system_update_id_for_props(
2008 context->service_proxy,
2009 task->target.device,
2010 cb_data);
2011 } else {
2012 cb_data->error =
2013 g_error_new(DLEYNA_SERVER_ERROR,
2014 DLEYNA_ERROR_UNKNOWN_INTERFACE,
2015 "Interface is only valid on root objects.");
2016
2017 (void) g_idle_add(dls_async_task_complete, cb_data);
2018 }
2019
2020 } else if (strcmp(task_data->interface_name, "")) {
2021 cb_task_data->device_object = FALSE;
2022 prv_get_all_ms2spec_props(context, cb_data);
2023 } else {
2024 if (root_object)
2025 dls_props_add_device(
2026 (GUPnPDeviceInfo *)context->device_proxy,
2027 task->target.device,
2028 cb_task_data->vb);
2029
2030 prv_get_all_ms2spec_props(context, cb_data);
2031 }
2032
2033 DLEYNA_LOG_DEBUG("Exit");
2034 }
2035
2036 static void prv_get_object_property(GUPnPDIDLLiteParser *parser,
2037 GUPnPDIDLLiteObject *object,
2038 gpointer user_data)
2039 {
2040 dls_async_task_t *cb_data = user_data;
2041 dls_task_t *task = &cb_data->task;
2042 dls_task_get_prop_t *task_data = &task->ut.get_prop;
2043
2044 if (cb_data->task.result)
2045 goto on_error;
2046
2047 cb_data->task.result = dls_props_get_object_prop(task_data->prop_name,
2048 task->target.root_path,
2049 object);
2050
2051 on_error:
2052
2053 return;
2054 }
2055
2056 static void prv_get_item_property(GUPnPDIDLLiteParser *parser,
2057 GUPnPDIDLLiteObject *object,
2058 gpointer user_data)
2059 {
2060 dls_async_task_t *cb_data = user_data;
2061 dls_task_t *task = &cb_data->task;
2062 dls_task_get_prop_t *task_data = &task->ut.get_prop;
2063 dls_async_get_prop_t *cb_task_data = &cb_data->ut.get_prop;
2064
2065 if (cb_data->task.result)
2066 goto on_error;
2067
2068 cb_data->task.result = dls_props_get_item_prop(
2069 task_data->prop_name,
2070 task->target.root_path,
2071 object,
2072 cb_task_data->protocol_info);
2073
2074 on_error:
2075
2076 return;
2077 }
2078
2079 static void prv_get_container_property(GUPnPDIDLLiteParser *parser,
2080 GUPnPDIDLLiteObject *object,
2081 gpointer user_data)
2082 {
2083 dls_async_task_t *cb_data = user_data;
2084 dls_task_t *task = &cb_data->task;
2085 dls_task_get_prop_t *task_data = &task->ut.get_prop;
2086
2087 if (cb_data->task.result)
2088 goto on_error;
2089
2090 cb_data->task.result = dls_props_get_container_prop(
2091 task_data->prop_name,
2092 object);
2093
2094 on_error:
2095
2096 return;
2097 }
2098
2099 static void prv_get_all_property(GUPnPDIDLLiteParser *parser,
2100 GUPnPDIDLLiteObject *object,
2101 gpointer user_data)
2102 {
2103 dls_async_task_t *cb_data = user_data;
2104
2105 prv_get_object_property(parser, object, user_data);
2106
2107 if (cb_data->task.result)
2108 goto on_error;
2109
2110 if (GUPNP_IS_DIDL_LITE_CONTAINER(object))
2111 prv_get_container_property(parser, object, user_data);
2112 else
2113 prv_get_item_property(parser, object, user_data);
2114
2115 on_error:
2116
2117 return;
2118 }
2119
2120 static gboolean prv_get_child_count_cb(dls_async_task_t *cb_data,
2121 gint count)
2122 {
2123 DLEYNA_LOG_DEBUG("Enter");
2124
2125 DLEYNA_LOG_DEBUG("Count %d", count);
2126
2127 cb_data->task.result = g_variant_ref_sink(
2128 g_variant_new_uint32((guint) count));
2129
2130 DLEYNA_LOG_DEBUG("Exit");
2131
2132 return TRUE;
2133 }
2134
2135 static void prv_count_children_cb(GUPnPServiceProxy *proxy,
2136 GUPnPServiceProxyAction *action,
2137 gpointer user_data)
2138 {
2139 dls_device_count_data_t *count_data = user_data;
2140 dls_async_task_t *cb_data = count_data->cb_data;
2141 GError *upnp_error = NULL;
2142 gint count;
2143 gboolean complete = FALSE;
2144
2145 DLEYNA_LOG_DEBUG("Enter");
2146
2147 if (!gupnp_service_proxy_end_action(cb_data->proxy, cb_data->action,
2148 &upnp_error,
2149 "TotalMatches", G_TYPE_INT,
2150 &count,
2151 NULL)) {
2152 DLEYNA_LOG_WARNING("Browse operation failed: %s",
2153 upnp_error->message);
2154
2155 cb_data->error = g_error_new(DLEYNA_SERVER_ERROR,
2156 DLEYNA_ERROR_OPERATION_FAILED,
2157 "Browse operation failed: %s",
2158 upnp_error->message);
2159 goto on_error;
2160 }
2161
2162 complete = count_data->cb(cb_data, count);
2163
2164 on_error:
2165
2166 g_free(user_data);
2167
2168 if (cb_data->error || complete) {
2169 (void) g_idle_add(dls_async_task_complete, cb_data);
2170 g_cancellable_disconnect(cb_data->cancellable,
2171 cb_data->cancel_id);
2172 }
2173
2174 if (upnp_error)
2175 g_error_free(upnp_error);
2176
2177 DLEYNA_LOG_DEBUG("Exit");
2178 }
2179
2180 static void prv_get_child_count(dls_async_task_t *cb_data,
2181 dls_device_count_cb_t cb, const gchar *id)
2182 {
2183 dls_device_count_data_t *count_data;
2184
2185 DLEYNA_LOG_DEBUG("Enter");
2186
2187 prv_count_data_new(cb_data, cb, &count_data);
2188 cb_data->action =
2189 gupnp_service_proxy_begin_action(cb_data->proxy,
2190 "Browse",
2191 prv_count_children_cb,
2192 count_data,
2193 "ObjectID", G_TYPE_STRING, id,
2194
2195 "BrowseFlag", G_TYPE_STRING,
2196 "BrowseDirectChildren",
2197
2198 "Filter", G_TYPE_STRING, "",
2199
2200 "StartingIndex", G_TYPE_INT,
2201 0,
2202
2203 "RequestedCount", G_TYPE_INT,
2204 1,
2205
2206 "SortCriteria", G_TYPE_STRING,
2207 "",
2208
2209 NULL);
2210
2211 DLEYNA_LOG_DEBUG("Exit with SUCCESS");
2212 }
2213
2214 static void prv_get_ms2spec_prop_cb(GUPnPServiceProxy *proxy,
2215 GUPnPServiceProxyAction *action,
2216 gpointer user_data)
2217 {
2218 GError *upnp_error = NULL;
2219 gchar *result = NULL;
2220 GUPnPDIDLLiteParser *parser = NULL;
2221 dls_async_task_t *cb_data = user_data;
2222 dls_async_get_prop_t *cb_task_data = &cb_data->ut.get_prop;
2223 dls_task_get_prop_t *task_data = &cb_data->task.ut.get_prop;
2224
2225 DLEYNA_LOG_DEBUG("Enter");
2226
2227 if (!gupnp_service_proxy_end_action(cb_data->proxy, cb_data->action,
2228 &upnp_error,
2229 "Result", G_TYPE_STRING,
2230 &result, NULL)) {
2231 DLEYNA_LOG_WARNING("Browse operation failed: %s",
2232 upnp_error->message);
2233
2234 cb_data->error = g_error_new(DLEYNA_SERVER_ERROR,
2235 DLEYNA_ERROR_OPERATION_FAILED,
2236 "Browse operation failed: %s",
2237 upnp_error->message);
2238 goto on_error;
2239 }
2240
2241 DLEYNA_LOG_DEBUG("GetMS2SpecProp result: %s", result);
2242
2243 parser = gupnp_didl_lite_parser_new();
2244
2245 g_signal_connect(parser, "object-available" , cb_task_data->prop_func,
2246 cb_data);
2247
2248 if (!gupnp_didl_lite_parser_parse_didl(parser, result, &upnp_error)) {
2249 if (upnp_error->code == GUPNP_XML_ERROR_EMPTY_NODE) {
2250 DLEYNA_LOG_WARNING("Property not defined for object");
2251
2252 cb_data->error =
2253 g_error_new(DLEYNA_SERVER_ERROR,
2254 DLEYNA_ERROR_UNKNOWN_PROPERTY,
2255 "Property not defined for object");
2256 } else {
2257 DLEYNA_LOG_WARNING(
2258 "Unable to parse results of browse: %s",
2259 upnp_error->message);
2260
2261 cb_data->error =
2262 g_error_new(DLEYNA_SERVER_ERROR,
2263 DLEYNA_ERROR_OPERATION_FAILED,
2264 "Unable to parse results of browse: %s",
2265 upnp_error->message);
2266 }
2267 goto on_error;
2268 }
2269
2270 if (!cb_data->task.result) {
2271 DLEYNA_LOG_WARNING("Property not defined for object");
2272
2273 cb_data->error = g_error_new(DLEYNA_SERVER_ERROR,
2274 DLEYNA_ERROR_UNKNOWN_PROPERTY,
2275 "Property not defined for object");
2276 }
2277
2278 on_error:
2279
2280 if (cb_data->error && !strcmp(task_data->prop_name,
2281 DLS_INTERFACE_PROP_CHILD_COUNT)) {
2282 DLEYNA_LOG_DEBUG("ChildCount not supported by server");
2283
2284 g_error_free(cb_data->error);
2285 cb_data->error = NULL;
2286 prv_get_child_count(cb_data, prv_get_child_count_cb,
2287 cb_data->task.target.id);
2288 } else {
2289 (void) g_idle_add(dls_async_task_complete, cb_data);
2290 g_cancellable_disconnect(cb_data->cancellable,
2291 cb_data->cancel_id);
2292 }
2293
2294 if (upnp_error)
2295 g_error_free(upnp_error);
2296
2297 if (parser)
2298 g_object_unref(parser);
2299
2300 g_free(result);
2301
2302 DLEYNA_LOG_DEBUG("Exit");
2303 }
2304
2305 static void prv_get_ms2spec_prop(dls_device_context_t *context,
2306 dls_prop_map_t *prop_map,
2307 dls_task_get_prop_t *task_data,
2308 dls_async_task_t *cb_data)
2309 {
2310 dls_async_get_prop_t *cb_task_data;
2311 const gchar *filter;
2312
2313 DLEYNA_LOG_DEBUG("Enter");
2314
2315 cb_task_data = &cb_data->ut.get_prop;
2316
2317 if (!prop_map) {
2318 cb_data->error = g_error_new(DLEYNA_SERVER_ERROR,
2319 DLEYNA_ERROR_UNKNOWN_PROPERTY,
2320 "Unknown property");
2321 goto on_error;
2322 }
2323
2324 filter = prop_map->filter ? prop_map->upnp_prop_name : "";
2325
2326 if (!strcmp(DLS_INTERFACE_MEDIA_CONTAINER, task_data->interface_name)) {
2327 cb_task_data->prop_func =
2328 G_CALLBACK(prv_get_container_property);
2329 } else if (!strcmp(DLS_INTERFACE_MEDIA_ITEM,
2330 task_data->interface_name)) {
2331 cb_task_data->prop_func = G_CALLBACK(prv_get_item_property);
2332 } else if (!strcmp(DLS_INTERFACE_MEDIA_OBJECT,
2333 task_data->interface_name)) {
2334 cb_task_data->prop_func = G_CALLBACK(prv_get_object_property);
2335 } else if (!strcmp("", task_data->interface_name)) {
2336 cb_task_data->prop_func = G_CALLBACK(prv_get_all_property);
2337 } else {
2338 DLEYNA_LOG_WARNING("Interface is unknown.%s",
2339 task_data->interface_name);
2340
2341 cb_data->error = g_error_new(DLEYNA_SERVER_ERROR,
2342 DLEYNA_ERROR_UNKNOWN_INTERFACE,
2343 "Interface is unknown.");
2344 goto on_error;
2345 }
2346
2347 cb_data->action = gupnp_service_proxy_begin_action(
2348 context->service_proxy, "Browse",
2349 prv_get_ms2spec_prop_cb,
2350 cb_data,
2351 "ObjectID", G_TYPE_STRING, cb_data->task.target.id,
2352 "BrowseFlag", G_TYPE_STRING,
2353 "BrowseMetadata",
2354 "Filter", G_TYPE_STRING, filter,
2355 "StartingIndex", G_TYPE_INT, 0,
2356 "RequestedCount", G_TYPE_INT, 0,
2357 "SortCriteria", G_TYPE_STRING,
2358 "",
2359 NULL);
2360
2361 cb_data->proxy = context->service_proxy;
2362
2363 g_object_add_weak_pointer((G_OBJECT(context->service_proxy)),
2364 (gpointer *)&cb_data->proxy);
2365
2366 cb_data->cancel_id = g_cancellable_connect(
2367 cb_data->cancellable,
2368 G_CALLBACK(dls_async_task_cancelled_cb),
2369 cb_data, NULL);
2370
2371 DLEYNA_LOG_DEBUG("Exit with SUCCESS");
2372
2373 return;
2374
2375 on_error:
2376
2377 (void) g_idle_add(dls_async_task_complete, cb_data);
2378
2379 DLEYNA_LOG_DEBUG("Exit with FAIL");
2380
2381 return;
2382 }
2383
2384 void dls_device_get_prop(dls_client_t *client,
2385 dls_task_t *task,
2386 dls_prop_map_t *prop_map, gboolean root_object)
2387 {
2388 dls_async_task_t *cb_data = (dls_async_task_t *)task;
2389 dls_task_get_prop_t *task_data = &task->ut.get_prop;
2390 dls_device_context_t *context;
2391 gboolean complete = FALSE;
2392
2393 DLEYNA_LOG_DEBUG("Enter");
2394
2395 context = dls_device_get_context(task->target.device, client);
2396
2397 if (!strcmp(task_data->interface_name,
2398 DLEYNA_SERVER_INTERFACE_MEDIA_DEVICE)) {
2399 if (root_object) {
2400 if (!strcmp(
2401 task_data->prop_name,
2402 DLS_INTERFACE_PROP_ESV_SYSTEM_UPDATE_ID)) {
2403 prv_get_system_update_id_for_prop(
2404 context->service_proxy,
2405 task->target.device,
2406 cb_data);
2407 } else if (!strcmp(
2408 task_data->prop_name,
2409 DLS_INTERFACE_PROP_SV_SERVICE_RESET_TOKEN)) {
2410 prv_get_sr_token_for_prop(
2411 context->service_proxy,
2412 task->target.device,
2413 cb_data);
2414 } else {
2415 cb_data->task.result =
2416 dls_props_get_device_prop(
2417 (GUPnPDeviceInfo *)
2418 context->device_proxy,
2419 task->target.device,
2420 task_data->prop_name);
2421
2422 if (!cb_data->task.result)
2423 cb_data->error = g_error_new(
2424 DLEYNA_SERVER_ERROR,
2425 DLEYNA_ERROR_UNKNOWN_PROPERTY,
2426 "Unknown property");
2427
2428 (void) g_idle_add(dls_async_task_complete,
2429 cb_data);
2430 }
2431
2432 } else {
2433 cb_data->error =
2434 g_error_new(DLEYNA_SERVER_ERROR,
2435 DLEYNA_ERROR_UNKNOWN_INTERFACE,
2436 "Interface is unknown.");
2437
2438 (void) g_idle_add(dls_async_task_complete, cb_data);
2439 }
2440
2441 } else if (strcmp(task_data->interface_name, "")) {
2442 prv_get_ms2spec_prop(context, prop_map, &task->ut.get_prop,
2443 cb_data);
2444 } else {
2445 if (root_object) {
2446 if (!strcmp(
2447 task_data->prop_name,
2448 DLS_INTERFACE_PROP_ESV_SYSTEM_UPDATE_ID)) {
2449 prv_get_system_update_id_for_prop(
2450 context->service_proxy,
2451 task->target.device,
2452 cb_data);
2453 complete = TRUE;
2454 } else if (!strcmp(
2455 task_data->prop_name,
2456 DLS_INTERFACE_PROP_SV_SERVICE_RESET_TOKEN)) {
2457 prv_get_sr_token_for_prop(
2458 context->service_proxy,
2459 task->target.device,
2460 cb_data);
2461 complete = TRUE;
2462 } else {
2463 cb_data->task.result =
2464 dls_props_get_device_prop(
2465 (GUPnPDeviceInfo *)
2466 context->device_proxy,
2467 task->target.device,
2468 task_data->prop_name);
2469 if (cb_data->task.result) {
2470 (void) g_idle_add(
2471 dls_async_task_complete,
2472 cb_data);
2473 complete = TRUE;
2474 }
2475 }
2476 }
2477
2478 if (!complete)
2479 prv_get_ms2spec_prop(context, prop_map,
2480 &task->ut.get_prop,
2481 cb_data);
2482 }
2483
2484 DLEYNA_LOG_DEBUG("Exit");
2485 }
2486
2487 static void prv_found_target(GUPnPDIDLLiteParser *parser,
2488 GUPnPDIDLLiteObject *object,
2489 gpointer user_data)
2490 {
2491 dls_async_task_t *cb_data = user_data;
2492 dls_async_bas_t *cb_task_data = &cb_data->ut.bas;
2493 const char *id;
2494 const char *parent_path;
2495 gchar *path = NULL;
2496 gboolean have_child_count;
2497 dls_device_object_builder_t *builder;
2498
2499 DLEYNA_LOG_DEBUG("Enter");
2500
2501 builder = g_new0(dls_device_object_builder_t, 1);
2502
2503 id = gupnp_didl_lite_object_get_parent_id(object);
2504
2505 if (!id || !strcmp(id, "-1") || !strcmp(id, "")) {
2506 parent_path = cb_data->task.target.root_path;
2507 } else {
2508 path = dls_path_from_id(cb_data->task.target.root_path, id);
2509 parent_path = path;
2510 }
2511
2512 builder->vb = g_variant_builder_new(G_VARIANT_TYPE("a{sv}"));
2513
2514 if (!dls_props_add_object(builder->vb, object,
2515 cb_data->task.target.root_path,
2516 parent_path, cb_task_data->filter_mask))
2517 goto on_error;
2518
2519 if (GUPNP_IS_DIDL_LITE_CONTAINER(object)) {
2520 dls_props_add_container(builder->vb,
2521 (GUPnPDIDLLiteContainer *)object,
2522 cb_task_data->filter_mask,
2523 &have_child_count);
2524
2525 if (!have_child_count && (cb_task_data->filter_mask &
2526 DLS_UPNP_MASK_PROP_CHILD_COUNT)) {
2527 builder->needs_child_count = TRUE;
2528 builder->id = g_strdup(
2529 gupnp_didl_lite_object_get_id(object));
2530 cb_task_data->need_child_count = TRUE;
2531 }
2532 } else {
2533 dls_props_add_item(builder->vb,
2534 object,
2535 cb_data->task.target.root_path,
2536 cb_task_data->filter_mask,
2537 cb_task_data->protocol_info);
2538 }
2539
2540 g_ptr_array_add(cb_task_data->vbs, builder);
2541 g_free(path);
2542
2543 DLEYNA_LOG_DEBUG("Exit with SUCCESS");
2544
2545 return;
2546
2547 on_error:
2548
2549 g_free(path);
2550 prv_object_builder_delete(builder);
2551
2552 DLEYNA_LOG_DEBUG("Exit with FAIL");
2553 }
2554
2555 static void prv_search_cb(GUPnPServiceProxy *proxy,
2556 GUPnPServiceProxyAction *action,
2557 gpointer user_data)
2558 {
2559 gchar *result = NULL;
2560 GUPnPDIDLLiteParser *parser = NULL;
2561 GError *upnp_error = NULL;
2562 dls_async_task_t *cb_data = user_data;
2563 dls_async_bas_t *cb_task_data = &cb_data->ut.bas;
2564
2565 DLEYNA_LOG_DEBUG("Enter");
2566
2567 if (!gupnp_service_proxy_end_action(cb_data->proxy, cb_data->action,
2568 &upnp_error,
2569 "Result", G_TYPE_STRING,
2570 &result,
2571 "TotalMatches", G_TYPE_INT,
2572 &cb_task_data->max_count,
2573 NULL)) {
2574
2575 DLEYNA_LOG_WARNING("Search operation failed %s",
2576 upnp_error->message);
2577
2578 cb_data->error = g_error_new(DLEYNA_SERVER_ERROR,
2579 DLEYNA_ERROR_OPERATION_FAILED,
2580 "Search operation failed: %s",
2581 upnp_error->message);
2582 goto on_error;
2583 }
2584
2585 parser = gupnp_didl_lite_parser_new();
2586
2587 cb_task_data->vbs = g_ptr_array_new_with_free_func(
2588 prv_object_builder_delete);
2589
2590 g_signal_connect(parser, "object-available" ,
2591 G_CALLBACK(prv_found_target), cb_data);
2592
2593 DLEYNA_LOG_DEBUG("Server Search result: %s", result);
2594
2595 if (!gupnp_didl_lite_parser_parse_didl(parser, result, &upnp_error) &&
2596 upnp_error->code != GUPNP_XML_ERROR_EMPTY_NODE) {
2597 DLEYNA_LOG_WARNING("Unable to parse results of search: %s",
2598 upnp_error->message);
2599
2600 cb_data->error = g_error_new(DLEYNA_SERVER_ERROR,
2601 DLEYNA_ERROR_OPERATION_FAILED,
2602 "Unable to parse results of search: %s",
2603 upnp_error->message);
2604 goto on_error;
2605 }
2606
2607 if (cb_task_data->need_child_count) {
2608 DLEYNA_LOG_DEBUG("Need to retrieve child count");
2609
2610 if (cb_data->task.multiple_retvals)
2611 cb_task_data->get_children_cb =
2612 prv_get_search_ex_result;
2613 else
2614 cb_task_data->get_children_cb = prv_get_children_result;
2615 prv_retrieve_child_count_for_list(cb_data);
2616 goto no_complete;
2617 } else {
2618 if (cb_data->task.multiple_retvals)
2619 prv_get_search_ex_result(cb_data);
2620 else
2621 prv_get_children_result(cb_data);
2622 }
2623
2624 on_error:
2625
2626 (void) g_idle_add(dls_async_task_complete, cb_data);
2627 g_cancellable_disconnect(cb_data->cancellable, cb_data->cancel_id);
2628
2629 no_complete:
2630
2631 if (parser)
2632 g_object_unref(parser);
2633
2634 g_free(result);
2635
2636 if (upnp_error)
2637 g_error_free(upnp_error);
2638
2639 DLEYNA_LOG_DEBUG("Exit");
2640 }
2641
2642 void dls_device_search(dls_client_t *client,
2643 dls_task_t *task,
2644 const gchar *upnp_filter, const gchar *upnp_query,
2645 const gchar *sort_by)
2646 {
2647 dls_async_task_t *cb_data = (dls_async_task_t *)task;
2648 dls_device_context_t *context;
2649
2650 DLEYNA_LOG_DEBUG("Enter");
2651
2652 context = dls_device_get_context(task->target.device, client);
2653
2654 cb_data->action = gupnp_service_proxy_begin_action(
2655 context->service_proxy, "Search",
2656 prv_search_cb,
2657 cb_data,
2658 "ContainerID", G_TYPE_STRING, task->target.id,
2659 "SearchCriteria", G_TYPE_STRING, upnp_query,
2660 "Filter", G_TYPE_STRING, upnp_filter,
2661 "StartingIndex", G_TYPE_INT, task->ut.search.start,
2662 "RequestedCount", G_TYPE_INT, task->ut.search.count,
2663 "SortCriteria", G_TYPE_STRING, sort_by,
2664 NULL);
2665
2666 cb_data->proxy = context->service_proxy;
2667
2668 g_object_add_weak_pointer((G_OBJECT(context->service_proxy)),
2669 (gpointer *)&cb_data->proxy);
2670
2671 cb_data->cancel_id = g_cancellable_connect(
2672 cb_data->cancellable,
2673 G_CALLBACK(dls_async_task_cancelled_cb),
2674 cb_data, NULL);
2675
2676 DLEYNA_LOG_DEBUG("Exit");
2677 }
2678
2679 static void prv_get_resource(GUPnPDIDLLiteParser *parser,
2680 GUPnPDIDLLiteObject *object,
2681 gpointer user_data)
2682 {
2683 dls_async_task_t *cb_data = user_data;
2684 dls_task_t *task = &cb_data->task;
2685 dls_task_get_resource_t *task_data = &task->ut.resource;
2686 dls_async_get_all_t *cb_task_data = &cb_data->ut.get_all;
2687
2688 DLEYNA_LOG_DEBUG("Enter");
2689
2690 dls_props_add_resource(cb_task_data->vb, object,
2691 cb_task_data->filter_mask,
2692 task_data->protocol_info);
2693 }
2694
2695 void dls_device_get_resource(dls_client_t *client,
2696 dls_task_t *task,
2697 const gchar *upnp_filter)
2698 {
2699 dls_async_task_t *cb_data = (dls_async_task_t *)task;
2700 dls_async_get_all_t *cb_task_data;
2701 dls_device_context_t *context;
2702
2703 context = dls_device_get_context(task->target.device, client);
2704 cb_task_data = &cb_data->ut.get_all;
2705
2706 cb_task_data->vb = g_variant_builder_new(G_VARIANT_TYPE("a{sv}"));
2707 cb_task_data->prop_func = G_CALLBACK(prv_get_resource);
2708 cb_task_data->device_object = FALSE;
2709
2710 cb_data->action = gupnp_service_proxy_begin_action(
2711 context->service_proxy, "Browse",
2712 prv_get_all_ms2spec_props_cb, cb_data,
2713 "ObjectID", G_TYPE_STRING, task->target.id,
2714 "BrowseFlag", G_TYPE_STRING, "BrowseMetadata",
2715 "Filter", G_TYPE_STRING, upnp_filter,
2716 "StartingIndex", G_TYPE_INT, 0,
2717 "RequestedCount", G_TYPE_INT, 0,
2718 "SortCriteria", G_TYPE_STRING,
2719 "", NULL);
2720
2721 cb_data->proxy = context->service_proxy;
2722
2723 g_object_add_weak_pointer((G_OBJECT(context->service_proxy)),
2724 (gpointer *)&cb_data->proxy);
2725
2726 cb_data->cancel_id = g_cancellable_connect(
2727 cb_data->cancellable,
2728 G_CALLBACK(dls_async_task_cancelled_cb),
2729 cb_data, NULL);
2730
2731 DLEYNA_LOG_DEBUG("Exit");
2732 }
2733
2734 static gchar *prv_create_new_container_didl(const gchar *parent_id,
2735 dls_task_t *task)
2736 {
2737 GUPnPDIDLLiteWriter *writer;
2738 GUPnPDIDLLiteObject *item;
2739 GUPnPDIDLLiteContainer *container;
2740 gchar *retval;
2741 GVariantIter iter;
2742 GVariant *child_type;
2743 const gchar *actual_type;
2744
2745 writer = gupnp_didl_lite_writer_new(NULL);
2746 item = GUPNP_DIDL_LITE_OBJECT(
2747 gupnp_didl_lite_writer_add_container(writer));
2748 container = GUPNP_DIDL_LITE_CONTAINER(item);
2749
2750 gupnp_didl_lite_object_set_id(item, "");
2751 gupnp_didl_lite_object_set_title(
2752 item,
2753 task->ut.create_container.display_name);
2754 gupnp_didl_lite_object_set_parent_id(item, parent_id);
2755 actual_type = dls_props_media_spec_to_upnp_class(
2756 task->ut.create_container.type);
2757 gupnp_didl_lite_object_set_upnp_class(item, actual_type);
2758 gupnp_didl_lite_object_set_restricted(item, FALSE);
2759 gupnp_didl_lite_object_set_dlna_managed(item, GUPNP_OCM_FLAGS_UPLOAD);
2760
2761 g_variant_iter_init(&iter, task->ut.create_container.child_types);
2762 while ((child_type = g_variant_iter_next_value(&iter))) {
2763 actual_type = dls_props_media_spec_to_upnp_class(
2764 g_variant_get_string(child_type, NULL));
2765 if (actual_type != NULL)
2766 gupnp_didl_lite_container_add_create_class(container,
2767 actual_type);
2768 g_variant_unref(child_type);
2769 }
2770
2771 retval = gupnp_didl_lite_writer_get_string(writer);
2772
2773 g_object_unref(item);
2774 g_object_unref(writer);
2775
2776 return retval;
2777 }
2778
2779 static const gchar *prv_get_dlna_profile_name(const gchar *filename)
2780 {
2781 gchar *uri;
2782 GError *error = NULL;
2783 const gchar *profile_name = NULL;
2784 GUPnPDLNAProfile *profile;
2785 GUPnPDLNAProfileGuesser *guesser;
2786 gboolean relaxed_mode = TRUE;
2787 gboolean extended_mode = TRUE;
2788
2789 guesser = gupnp_dlna_profile_guesser_new(relaxed_mode, extended_mode);
2790
2791 uri = g_filename_to_uri(filename, NULL, &error);
2792 if (uri == NULL) {
2793 DLEYNA_LOG_WARNING("Unable to convert filename: %s", filename);
2794
2795 if (error) {
2796 DLEYNA_LOG_WARNING("Error: %s", error->message);
2797
2798 g_error_free(error);
2799 }
2800
2801 goto on_error;
2802 }
2803
2804 profile = gupnp_dlna_profile_guesser_guess_profile_sync(guesser,
2805 uri,
2806 5000,
2807 NULL,
2808 &error);
2809 if (profile == NULL) {
2810 DLEYNA_LOG_WARNING("Unable to guess profile for URI: %s", uri);
2811
2812 if (error) {
2813 DLEYNA_LOG_WARNING("Error: %s", error->message);
2814
2815 g_error_free(error);
2816 }
2817
2818 goto on_error;
2819 }
2820
2821 profile_name = gupnp_dlna_profile_get_name(profile);
2822
2823 on_error:
2824 g_object_unref(guesser);
2825
2826 g_free(uri);
2827
2828 return profile_name;
2829 }
2830
2831 static gchar *prv_create_upload_didl(const gchar *parent_id, dls_task_t *task,
2832 const gchar *object_class,
2833 const gchar *mime_type)
2834 {
2835 GUPnPDIDLLiteWriter *writer;
2836 GUPnPDIDLLiteObject *item;
2837 gchar *retval;
2838 GUPnPProtocolInfo *protocol_info;
2839 GUPnPDIDLLiteResource *res;
2840 const gchar *profile;
2841
2842 writer = gupnp_didl_lite_writer_new(NULL);
2843 item = GUPNP_DIDL_LITE_OBJECT(gupnp_didl_lite_writer_add_item(writer));
2844
2845 gupnp_didl_lite_object_set_id(item, "");
2846 gupnp_didl_lite_object_set_title(item, task->ut.upload.display_name);
2847 gupnp_didl_lite_object_set_parent_id(item, parent_id);
2848 gupnp_didl_lite_object_set_upnp_class(item, object_class);
2849 gupnp_didl_lite_object_set_restricted(item, FALSE);
2850
2851 protocol_info = gupnp_protocol_info_new();
2852 gupnp_protocol_info_set_mime_type(protocol_info, mime_type);
2853 gupnp_protocol_info_set_protocol(protocol_info, "*");
2854 gupnp_protocol_info_set_network(protocol_info, "*");
2855
2856 profile = prv_get_dlna_profile_name(task->ut.upload.file_path);
2857 if (profile != NULL)
2858 gupnp_protocol_info_set_dlna_profile(protocol_info, profile);
2859
2860 res = gupnp_didl_lite_object_add_resource(item);
2861 gupnp_didl_lite_resource_set_protocol_info(res, protocol_info);
2862
2863 retval = gupnp_didl_lite_writer_get_string(writer);
2864
2865 g_object_unref(res);
2866 g_object_unref(protocol_info);
2867 g_object_unref(item);
2868 g_object_unref(writer);
2869
2870 return retval;
2871 }
2872
2873 static void prv_extract_import_uri(GUPnPDIDLLiteParser *parser,
2874 GUPnPDIDLLiteObject *object,
2875 gpointer user_data)
2876 {
2877 gchar **import_uri = user_data;
2878 GList *resources;
2879 GList *ptr;
2880 GUPnPDIDLLiteResource *res;
2881 const gchar *uri;
2882
2883 if (!*import_uri) {
2884 resources = gupnp_didl_lite_object_get_resources(object);
2885 ptr = resources;
2886 while (ptr) {
2887 res = ptr->data;
2888 if (!*import_uri) {
2889 uri = gupnp_didl_lite_resource_get_import_uri(
2890 res);
2891 if (uri)
2892 *import_uri = g_strdup(uri);
2893 }
2894 g_object_unref(res);
2895 ptr = ptr->next;
2896 }
2897
2898 g_list_free(resources);
2899 }
2900 }
2901
2902 static void prv_upload_delete_cb(GUPnPServiceProxy *proxy,
2903 GUPnPServiceProxyAction *action,
2904 gpointer user_data)
2905 {
2906 dls_async_task_t *cb_data = user_data;
2907
2908 DLEYNA_LOG_DEBUG("Enter");
2909
2910 (void) gupnp_service_proxy_end_action(cb_data->proxy, cb_data->action,
2911 NULL, NULL);
2912 (void) g_idle_add(dls_async_task_complete, cb_data);
2913 g_cancellable_disconnect(cb_data->cancellable, cb_data->cancel_id);
2914
2915 DLEYNA_LOG_DEBUG("Exit");
2916 }
2917
2918 static void prv_upload_job_delete(gpointer up_job)
2919 {
2920 dls_device_upload_job_t *upload_job = up_job;
2921
2922 if (up_job) {
2923 if (upload_job->remove_idle)
2924 (void) g_source_remove(upload_job->remove_idle);
2925
2926 g_free(upload_job);
2927 }
2928 }
2929
2930 static gboolean prv_remove_update_job(gpointer user_data)
2931 {
2932 dls_device_upload_job_t *upload_job = user_data;
2933 dls_device_upload_t *upload;
2934
2935 upload = g_hash_table_lookup(upload_job->device->uploads,
2936 &upload_job->upload_id);
2937 if (upload) {
2938 g_hash_table_remove(upload_job->device->uploads,
2939 &upload_job->upload_id);
2940
2941 DLEYNA_LOG_DEBUG("Removing Upload Object: %d",
2942 upload_job->upload_id);
2943 }
2944
2945 upload_job->remove_idle = 0;
2946 g_hash_table_remove(upload_job->device->upload_jobs,
2947 &upload_job->upload_id);
2948
2949 return FALSE;
2950 }
2951
2952 static void prv_generate_upload_update(dls_device_upload_job_t *upload_job,
2953 dls_device_upload_t *upload)
2954 {
2955 GVariant *args;
2956
2957 args = g_variant_new("(ustt)", upload_job->upload_id, upload->status,
2958 upload->bytes_uploaded, upload->bytes_to_upload);
2959
2960 DLEYNA_LOG_DEBUG(
2961 "Emitting: %s (%u %s %"G_GUINT64_FORMAT" %"G_GUINT64_FORMAT")"
2962 " on %s",
2963 DLS_INTERFACE_UPLOAD_UPDATE, upload_job->upload_id,
2964 upload->status, upload->bytes_uploaded,
2965 upload->bytes_to_upload, upload_job->device->path);
2966
2967 (void) dls_server_get_connector()->notify(
2968 upload_job->device->connection,
2969 upload_job->device->path,
2970 DLEYNA_SERVER_INTERFACE_MEDIA_DEVICE,
2971 DLS_INTERFACE_UPLOAD_UPDATE,
2972 args,
2973 NULL);
2974 }
2975
2976 static void prv_post_finished(SoupSession *session, SoupMessage *msg,
2977 gpointer user_data)
2978 {
2979 dls_device_upload_job_t *upload_job = user_data;
2980 dls_device_upload_t *upload;
2981 gint *upload_id;
2982
2983 DLEYNA_LOG_DEBUG("Enter");
2984
2985 DLEYNA_LOG_DEBUG("Upload %u finished. Code %u Message %s",
2986 upload_job->upload_id, msg->status_code,
2987 msg->reason_phrase);
2988
2989 /* This is clumsy but we need to distinguish between two cases:
2990 1. We cancel because the process is exitting.
2991 2. We cancel because a client has called CancelUpload.
2992
2993 We could use custom SOUP error messages to distinguish the cases
2994 but device->shutting_down seemed less hacky.
2995
2996 We need this check as if we are shutting down it is
2997 dangerous to manipulate uploads as we are being called from its
2998 destructor.
2999 */
3000
3001 if (upload_job->device->shutting_down) {
3002 DLEYNA_LOG_DEBUG("Device shutting down. Cancelling Upload");
3003 goto on_error;
3004 }
3005
3006 upload = g_hash_table_lookup(upload_job->device->uploads,
3007 &upload_job->upload_id);
3008 if (upload) {
3009 upload_job->remove_idle =
3010 g_timeout_add(30000, prv_remove_update_job, user_data);
3011
3012 if (SOUP_STATUS_IS_SUCCESSFUL(msg->status_code)) {
3013 upload->status = DLS_UPLOAD_STATUS_COMPLETED;
3014 upload->bytes_uploaded = upload->bytes_to_upload;
3015 } else if (msg->status_code == SOUP_STATUS_CANCELLED) {
3016 upload->status = DLS_UPLOAD_STATUS_CANCELLED;
3017 } else {
3018 upload->status = DLS_UPLOAD_STATUS_ERROR;
3019 }
3020
3021 DLEYNA_LOG_DEBUG("Upload Status: %s", upload->status);
3022
3023 prv_generate_upload_update(upload_job, upload);
3024
3025 g_object_unref(upload->msg);
3026 upload->msg = NULL;
3027
3028 g_object_unref(upload->soup_session);
3029 upload->soup_session = NULL;
3030
3031 g_mapped_file_unref(upload->mapped_file);
3032 upload->mapped_file = NULL;
3033
3034 g_free(upload->body);
3035 upload->body = NULL;
3036
3037 upload_id = g_new(gint, 1);
3038 *upload_id = upload_job->upload_id;
3039
3040 g_hash_table_insert(upload_job->device->upload_jobs, upload_id,
3041 upload_job);
3042
3043 upload_job = NULL;
3044 }
3045
3046 on_error:
3047
3048 prv_upload_job_delete(upload_job);
3049
3050 DLEYNA_LOG_DEBUG("Exit");
3051 }
3052
3053 static void prv_upload_delete(gpointer up)
3054 {
3055 dls_device_upload_t *upload = up;
3056
3057 DLEYNA_LOG_DEBUG("Enter");
3058
3059 if (upload) {
3060 if (upload->msg) {
3061 soup_session_cancel_message(upload->soup_session,
3062 upload->msg,
3063 SOUP_STATUS_CANCELLED);
3064 g_object_unref(upload->msg);
3065 }
3066
3067 if (upload->soup_session)
3068 g_object_unref(upload->soup_session);
3069
3070 if (upload->mapped_file)
3071 g_mapped_file_unref(upload->mapped_file);
3072 else if (upload->body)
3073 g_free(upload->body);
3074
3075 g_free(upload);
3076 }
3077
3078 DLEYNA_LOG_DEBUG("Exit");
3079 }
3080
3081 static void prv_post_bytes_written(SoupMessage *msg, SoupBuffer *chunk,
3082 gpointer user_data)
3083 {
3084 dls_device_upload_t *upload = user_data;
3085
3086 upload->bytes_uploaded += chunk->length;
3087 if (upload->bytes_uploaded > upload->bytes_to_upload)
3088 upload->bytes_uploaded = upload->bytes_to_upload;
3089 }
3090
3091 static dls_device_upload_t *prv_upload_data_new(const gchar *file_path,
3092 gchar *body,
3093 gsize body_length,
3094 const gchar *import_uri,
3095 const gchar *mime_type,
3096 GError **error)
3097 {
3098 dls_device_upload_t *upload = NULL;
3099 GMappedFile *mapped_file = NULL;
3100 gchar *up_body = body;
3101 gsize up_body_length = body_length;
3102
3103 DLEYNA_LOG_DEBUG("Enter");
3104
3105 if (file_path) {
3106 mapped_file = g_mapped_file_new(file_path, FALSE, NULL);
3107 if (!mapped_file) {
3108 DLEYNA_LOG_WARNING("Unable to map %s into memory",
3109 file_path);
3110
3111 *error = g_error_new(DLEYNA_SERVER_ERROR,
3112 DLEYNA_ERROR_IO,
3113 "Unable to map %s into memory",
3114 file_path);
3115 goto on_error;
3116 }
3117
3118 up_body = g_mapped_file_get_contents(mapped_file);
3119 up_body_length = g_mapped_file_get_length(mapped_file);
3120 }
3121
3122 upload = g_new0(dls_device_upload_t, 1);
3123
3124 upload->soup_session = soup_session_async_new();
3125 upload->msg = soup_message_new("POST", import_uri);
3126 upload->mapped_file = mapped_file;
3127 upload->body = body;
3128 upload->body_length = body_length;
3129
3130 if (!upload->msg) {
3131 DLEYNA_LOG_WARNING("Invalid URI %s", import_uri);
3132
3133 *error = g_error_new(DLEYNA_SERVER_ERROR,
3134 DLEYNA_ERROR_BAD_RESULT,
3135 "Invalid URI %s", import_uri);
3136 goto on_error;
3137 }
3138
3139 upload->status = DLS_UPLOAD_STATUS_IN_PROGRESS;
3140 upload->bytes_to_upload = up_body_length;
3141
3142 soup_message_headers_set_expectations(upload->msg->request_headers,
3143 SOUP_EXPECTATION_CONTINUE);
3144
3145 soup_message_set_request(upload->msg, mime_type, SOUP_MEMORY_STATIC,
3146 up_body, up_body_length);
3147 g_signal_connect(upload->msg, "wrote-body-data",
3148 G_CALLBACK(prv_post_bytes_written), upload);
3149
3150 DLEYNA_LOG_DEBUG("Exit with Success");
3151
3152 return upload;
3153
3154 on_error:
3155
3156 prv_upload_delete(upload);
3157
3158 DLEYNA_LOG_WARNING("Exit with Fail");
3159
3160 return NULL;
3161 }
3162
3163 static void prv_create_container_cb(GUPnPServiceProxy *proxy,
3164 GUPnPServiceProxyAction *action,
3165 gpointer user_data)
3166 {
3167 dls_async_task_t *cb_data = user_data;
3168 GError *upnp_error = NULL;
3169 gchar *result = NULL;
3170 gchar *object_id = NULL;
3171 gchar *object_path;
3172
3173 DLEYNA_LOG_DEBUG("Enter");
3174
3175 if (!gupnp_service_proxy_end_action(cb_data->proxy, cb_data->action,
3176 &upnp_error,
3177 "ObjectID", G_TYPE_STRING,
3178 &object_id,
3179 "Result", G_TYPE_STRING,
3180 &result,
3181 NULL)) {
3182 DLEYNA_LOG_WARNING("Create Object operation failed: %s",
3183 upnp_error->message);
3184
3185 cb_data->error = g_error_new(DLEYNA_SERVER_ERROR,
3186 DLEYNA_ERROR_OPERATION_FAILED,
3187 "Create Object operation failed: %s",
3188 upnp_error->message);
3189 goto on_error;
3190 }
3191
3192 object_path = dls_path_from_id(cb_data->task.target.root_path,
3193 object_id);
3194 cb_data->task.result = g_variant_ref_sink(g_variant_new_object_path(
3195 object_path));
3196 g_free(object_path);
3197
3198 on_error:
3199
3200 (void) g_idle_add(dls_async_task_complete, cb_data);
3201 g_cancellable_disconnect(cb_data->cancellable, cb_data->cancel_id);
3202
3203 if (object_id)
3204 g_free(object_id);
3205
3206 if (result)
3207 g_free(result);
3208
3209 if (upnp_error)
3210 g_error_free(upnp_error);
3211
3212 DLEYNA_LOG_DEBUG("Exit");
3213 }
3214
3215 static void prv_generic_upload_cb(dls_async_task_t *cb_data,
3216 char *file_path,
3217 gchar *body,
3218 gsize body_length,
3219 const gchar *mime_type)
3220 {
3221 gchar *object_id = NULL;
3222 gchar *result = NULL;
3223 gchar *import_uri = NULL;
3224 gchar *object_path;
3225 GError *error = NULL;
3226 gboolean delete_needed = FALSE;
3227 gint *upload_id;
3228 GUPnPDIDLLiteParser *parser = NULL;
3229 GVariant *out_p[2];
3230 dls_device_upload_t *upload;
3231 dls_device_upload_job_t *upload_job;
3232
3233 DLEYNA_LOG_DEBUG("Enter");
3234
3235 if (!gupnp_service_proxy_end_action(cb_data->proxy, cb_data->action,
3236 &error,
3237 "ObjectID", G_TYPE_STRING,
3238 &object_id,
3239 "Result", G_TYPE_STRING,
3240 &result,
3241 NULL)) {
3242 DLEYNA_LOG_WARNING("Create Object operation failed: %s",
3243 error->message);
3244
3245 cb_data->error = g_error_new(DLEYNA_SERVER_ERROR,
3246 DLEYNA_ERROR_OPERATION_FAILED,
3247 "Create Object operation "
3248 " failed: %s",
3249 error->message);
3250 goto on_error;
3251 }
3252
3253 DLEYNA_LOG_DEBUG_NL();
3254 DLEYNA_LOG_DEBUG("Create Object Result: %s", result);
3255 DLEYNA_LOG_DEBUG_NL();
3256
3257 delete_needed = TRUE;
3258
3259 parser = gupnp_didl_lite_parser_new();
3260
3261 g_signal_connect(parser, "object-available" ,
3262 G_CALLBACK(prv_extract_import_uri), &import_uri);
3263
3264 if (!gupnp_didl_lite_parser_parse_didl(parser, result, &error) &&
3265 error->code != GUPNP_XML_ERROR_EMPTY_NODE) {
3266
3267 DLEYNA_LOG_WARNING(
3268 "Unable to parse results of CreateObject: %s",
3269 error->message);
3270
3271 cb_data->error = g_error_new(DLEYNA_SERVER_ERROR,
3272 DLEYNA_ERROR_OPERATION_FAILED,
3273 "Unable to parse results of CreateObject: %s",
3274 error->message);
3275 goto on_error;
3276 }
3277
3278 if (!import_uri) {
3279 DLEYNA_LOG_WARNING("Missing Import URI");
3280
3281 cb_data->error = g_error_new(DLEYNA_SERVER_ERROR,
3282 DLEYNA_ERROR_OPERATION_FAILED,
3283 "Missing Import URI");
3284 goto on_error;
3285 }
3286
3287 DLEYNA_LOG_DEBUG("Import URI %s", import_uri);
3288
3289 upload = prv_upload_data_new(file_path, body, body_length,
3290 import_uri, mime_type, &cb_data->error);
3291
3292 if (!upload)
3293 goto on_error;
3294
3295 upload_job = g_new0(dls_device_upload_job_t, 1);
3296 upload_job->device = cb_data->task.target.device;
3297 upload_job->upload_id = (gint) cb_data->task.target.device->upload_id;
3298
3299 soup_session_queue_message(upload->soup_session, upload->msg,
3300 prv_post_finished, upload_job);
3301 g_object_ref(upload->msg);
3302
3303 upload_id = g_new(gint, 1);
3304 *upload_id = upload_job->upload_id;
3305 g_hash_table_insert(cb_data->task.target.device->uploads, upload_id,
3306 upload);
3307
3308 object_path = dls_path_from_id(cb_data->task.target.root_path,
3309 object_id);
3310
3311 DLEYNA_LOG_DEBUG("Upload ID %u", *upload_id);
3312 DLEYNA_LOG_DEBUG("Object ID %s", object_id);
3313 DLEYNA_LOG_DEBUG("Object Path %s", object_path);
3314
3315 out_p[0] = g_variant_new_uint32(*upload_id);
3316 out_p[1] = g_variant_new_object_path(object_path);
3317 cb_data->task.result = g_variant_ref_sink(g_variant_new_tuple(out_p,
3318 2));
3319
3320 ++cb_data->task.target.device->upload_id;
3321 if (cb_data->task.target.device->upload_id > G_MAXINT)
3322 cb_data->task.target.device->upload_id = 0;
3323
3324 g_free(object_path);
3325
3326 on_error:
3327
3328 if (cb_data->error && delete_needed) {
3329 DLEYNA_LOG_WARNING(
3330 "Upload failed deleting created object with id %s",
3331 object_id);
3332
3333 cb_data->action = gupnp_service_proxy_begin_action(
3334 cb_data->proxy, "DestroyObject",
3335 prv_upload_delete_cb, cb_data,
3336 "ObjectID", G_TYPE_STRING, object_id,
3337 NULL);
3338 } else {
3339 (void) g_idle_add(dls_async_task_complete, cb_data);
3340 g_cancellable_disconnect(cb_data->cancellable,
3341 cb_data->cancel_id);
3342 }
3343
3344 g_free(object_id);
3345 g_free(import_uri);
3346
3347 if (parser)
3348 g_object_unref(parser);
3349
3350 g_free(result);
3351
3352 if (error)
3353 g_error_free(error);
3354
3355 DLEYNA_LOG_DEBUG("Exit");
3356 }
3357
3358 static void prv_create_object_upload_cb(GUPnPServiceProxy *proxy,
3359 GUPnPServiceProxyAction *action,
3360 gpointer user_data)
3361 {
3362 dls_async_task_t *cb_data = user_data;
3363
3364 prv_generic_upload_cb(cb_data,
3365 cb_data->task.ut.upload.file_path,
3366 NULL, 0,
3367 cb_data->ut.upload.mime_type);
3368 }
3369
3370 void dls_device_upload(dls_client_t *client,
3371 dls_task_t *task, const gchar *parent_id)
3372 {
3373 dls_async_task_t *cb_data = (dls_async_task_t *)task;
3374 dls_device_context_t *context;
3375 gchar *didl;
3376 dls_async_upload_t *cb_task_data;
3377
3378 DLEYNA_LOG_DEBUG("Enter");
3379 DLEYNA_LOG_DEBUG("Uploading file to %s", parent_id);
3380
3381 context = dls_device_get_context(task->target.device, client);
3382 cb_task_data = &cb_data->ut.upload;
3383
3384 didl = prv_create_upload_didl(parent_id, task,
3385 cb_task_data->object_class,
3386 cb_task_data->mime_type);
3387
3388 DLEYNA_LOG_DEBUG_NL();
3389 DLEYNA_LOG_DEBUG("DIDL: %s", didl);
3390 DLEYNA_LOG_DEBUG_NL();
3391
3392 cb_data->action = gupnp_service_proxy_begin_action(
3393 context->service_proxy, "CreateObject",
3394 prv_create_object_upload_cb, cb_data,
3395 "ContainerID", G_TYPE_STRING, parent_id,
3396 "Elements", G_TYPE_STRING, didl,
3397 NULL);
3398
3399 cb_data->proxy = context->service_proxy;
3400
3401 g_object_add_weak_pointer((G_OBJECT(context->service_proxy)),
3402 (gpointer *)&cb_data->proxy);
3403
3404 cb_data->cancel_id = g_cancellable_connect(
3405 cb_data->cancellable,
3406 G_CALLBACK(dls_async_task_cancelled_cb),
3407 cb_data, NULL);
3408
3409 g_free(didl);
3410
3411 DLEYNA_LOG_DEBUG("Exit");
3412 }
3413
3414 gboolean dls_device_get_upload_status(dls_task_t *task, GError **error)
3415 {
3416 dls_device_upload_t *upload;
3417 gboolean retval = FALSE;
3418 GVariant *out_params[3];
3419 guint upload_id;
3420
3421 DLEYNA_LOG_DEBUG("Enter");
3422
3423 upload_id = task->ut.upload_action.upload_id;
3424
3425 upload = g_hash_table_lookup(task->target.device->uploads, &upload_id);
3426 if (!upload) {
3427 *error = g_error_new(DLEYNA_SERVER_ERROR,
3428 DLEYNA_ERROR_OBJECT_NOT_FOUND,
3429 "Unknown Upload ID %u ", upload_id);
3430 goto on_error;
3431 }
3432
3433 out_params[0] = g_variant_new_string(upload->status);
3434 out_params[1] = g_variant_new_uint64(upload->bytes_uploaded);
3435 out_params[2] = g_variant_new_uint64(upload->bytes_to_upload);
3436
3437 DLEYNA_LOG_DEBUG(
3438 "Upload ( %s %"G_GUINT64_FORMAT" %"G_GUINT64_FORMAT" )",
3439 upload->status, upload->bytes_uploaded,
3440 upload->bytes_to_upload);
3441
3442 task->result = g_variant_ref_sink(g_variant_new_tuple(out_params, 3));
3443
3444 retval = TRUE;
3445
3446 on_error:
3447
3448 DLEYNA_LOG_DEBUG("Exit");
3449
3450 return retval;
3451 }
3452
3453 void dls_device_get_upload_ids(dls_task_t *task)
3454 {
3455 GVariantBuilder vb;
3456 GHashTableIter iter;
3457 gpointer key;
3458
3459 DLEYNA_LOG_DEBUG("Enter");
3460
3461 g_variant_builder_init(&vb, G_VARIANT_TYPE("au"));
3462
3463 g_hash_table_iter_init(&iter, task->target.device->uploads);
3464 while (g_hash_table_iter_next(&iter, &key, NULL))
3465 g_variant_builder_add(&vb, "u", (guint32) (*((gint *)key)));
3466
3467 task->result = g_variant_ref_sink(g_variant_builder_end(&vb));
3468
3469 DLEYNA_LOG_DEBUG("Exit");
3470 }
3471
3472 gboolean dls_device_cancel_upload(dls_task_t *task, GError **error)
3473 {
3474 dls_device_upload_t *upload;
3475 gboolean retval = FALSE;
3476 guint upload_id;
3477
3478 DLEYNA_LOG_DEBUG("Enter");
3479
3480 upload_id = task->ut.upload_action.upload_id;
3481
3482 upload = g_hash_table_lookup(task->target.device->uploads, &upload_id);
3483 if (!upload) {
3484 *error = g_error_new(DLEYNA_SERVER_ERROR,
3485 DLEYNA_ERROR_OBJECT_NOT_FOUND,
3486 "Unknown Upload ID %u ", upload_id);
3487 goto on_error;
3488 }
3489
3490 if (upload->msg) {
3491 soup_session_cancel_message(upload->soup_session, upload->msg,
3492 SOUP_STATUS_CANCELLED);
3493 DLEYNA_LOG_DEBUG("Cancelling Upload %u ", upload_id);
3494 }
3495
3496 retval = TRUE;
3497
3498 on_error:
3499
3500 DLEYNA_LOG_DEBUG("Exit");
3501
3502 return retval;
3503 }
3504
3505 static void prv_destroy_object_cb(GUPnPServiceProxy *proxy,
3506 GUPnPServiceProxyAction *action,
3507 gpointer user_data)
3508 {
3509 GError *upnp_error = NULL;
3510 dls_async_task_t *cb_data = user_data;
3511
3512 DLEYNA_LOG_DEBUG("Enter");
3513
3514 if (!gupnp_service_proxy_end_action(cb_data->proxy, cb_data->action,
3515 &upnp_error,
3516 NULL)) {
3517 DLEYNA_LOG_WARNING("Destroy Object operation failed: %s",
3518 upnp_error->message);
3519
3520 cb_data->error = g_error_new(DLEYNA_SERVER_ERROR,
3521 DLEYNA_ERROR_OPERATION_FAILED,
3522 "Destroy Object operation failed: %s",
3523 upnp_error->message);
3524 }
3525
3526 (void) g_idle_add(dls_async_task_complete, cb_data);
3527 g_cancellable_disconnect(cb_data->cancellable, cb_data->cancel_id);
3528
3529 if (upnp_error)
3530 g_error_free(upnp_error);
3531
3532 DLEYNA_LOG_DEBUG("Exit");
3533 }
3534
3535 void dls_device_delete_object(dls_client_t *client,
3536 dls_task_t *task)
3537 {
3538 dls_async_task_t *cb_data = (dls_async_task_t *)task;
3539 dls_device_context_t *context;
3540
3541 DLEYNA_LOG_DEBUG("Enter");
3542
3543 context = dls_device_get_context(task->target.device, client);
3544
3545 cb_data->action = gupnp_service_proxy_begin_action(
3546 context->service_proxy, "DestroyObject",
3547 prv_destroy_object_cb, cb_data,
3548 "ObjectID", G_TYPE_STRING, task->target.id,
3549 NULL);
3550
3551 cb_data->proxy = context->service_proxy;
3552
3553 g_object_add_weak_pointer((G_OBJECT(context->service_proxy)),
3554 (gpointer *)&cb_data->proxy);
3555
3556 cb_data->cancel_id = g_cancellable_connect(cb_data->cancellable,
3557 G_CALLBACK(dls_async_task_cancelled_cb),
3558 cb_data, NULL);
3559
3560 DLEYNA_LOG_DEBUG("Exit");
3561 }
3562
3563 void dls_device_create_container(dls_client_t *client,
3564 dls_task_t *task,
3565 const gchar *parent_id)
3566 {
3567 dls_async_task_t *cb_data = (dls_async_task_t *)task;
3568 dls_device_context_t *context;
3569 gchar *didl;
3570
3571 DLEYNA_LOG_DEBUG("Enter");
3572
3573 context = dls_device_get_context(task->target.device, client);
3574
3575 didl = prv_create_new_container_didl(parent_id, task);
3576
3577 DLEYNA_LOG_DEBUG("DIDL: %s", didl);
3578
3579 cb_data->action = gupnp_service_proxy_begin_action(
3580 context->service_proxy, "CreateObject",
3581 prv_create_container_cb, cb_data,
3582 "ContainerID", G_TYPE_STRING, parent_id,
3583 "Elements", G_TYPE_STRING, didl,
3584 NULL);
3585
3586 cb_data->proxy = context->service_proxy;
3587
3588 g_object_add_weak_pointer((G_OBJECT(context->service_proxy)),
3589 (gpointer *)&cb_data->proxy);
3590
3591 cb_data->cancel_id = g_cancellable_connect(
3592 cb_data->cancellable,
3593 G_CALLBACK(dls_async_task_cancelled_cb),
3594 cb_data, NULL);
3595
3596 g_free(didl);
3597
3598 DLEYNA_LOG_DEBUG("Exit");
3599 }
3600
3601 static void prv_update_object_update_cb(GUPnPServiceProxy *proxy,
3602 GUPnPServiceProxyAction *action,
3603 gpointer user_data)
3604 {
3605 GError *upnp_error = NULL;
3606 dls_async_task_t *cb_data = user_data;
3607
3608 DLEYNA_LOG_DEBUG("Enter");
3609
3610 if (!gupnp_service_proxy_end_action(cb_data->proxy, cb_data->action,
3611 &upnp_error,
3612 NULL)) {
3613 DLEYNA_LOG_WARNING("Update Object operation failed: %s",
3614 upnp_error->message);
3615
3616 cb_data->error = g_error_new(DLEYNA_SERVER_ERROR,
3617 DLEYNA_ERROR_OPERATION_FAILED,
3618 "Update Object operation "
3619 " failed: %s",
3620 upnp_error->message);
3621 }
3622
3623 (void) g_idle_add(dls_async_task_complete, cb_data);
3624 g_cancellable_disconnect(cb_data->cancellable, cb_data->cancel_id);
3625
3626 if (upnp_error)
3627 g_error_free(upnp_error);
3628
3629 DLEYNA_LOG_DEBUG("Exit");
3630 }
3631
3632 static gchar *prv_get_current_xml_fragment(GUPnPDIDLLiteObject *object,
3633 dls_upnp_prop_mask mask)
3634 {
3635 gchar *retval = NULL;
3636
3637 if (mask & DLS_UPNP_MASK_PROP_DISPLAY_NAME)
3638 retval = gupnp_didl_lite_object_get_title_xml_string(object);
3639 else if (mask & DLS_UPNP_MASK_PROP_ALBUM)
3640 retval = gupnp_didl_lite_object_get_album_xml_string(object);
3641 else if (mask & DLS_UPNP_MASK_PROP_DATE)
3642 retval = gupnp_didl_lite_object_get_date_xml_string(object);
3643 else if (mask & DLS_UPNP_MASK_PROP_TYPE)
3644 retval = gupnp_didl_lite_object_get_upnp_class_xml_string(
3645 object);
3646 else if (mask & DLS_UPNP_MASK_PROP_TRACK_NUMBER)
3647 retval = gupnp_didl_lite_object_get_track_number_xml_string(
3648 object);
3649 else if (mask & DLS_UPNP_MASK_PROP_ARTISTS)
3650 retval = gupnp_didl_lite_object_get_artists_xml_string(object);
3651
3652 return retval;
3653 }
3654
3655 static gchar *prv_get_new_xml_fragment(GUPnPDIDLLiteObject *object,
3656 dls_upnp_prop_mask mask,
3657 GVariant *value)
3658 {
3659 GUPnPDIDLLiteContributor *artist;
3660 const gchar *artist_name;
3661 const gchar *upnp_class;
3662 GVariantIter viter;
3663 gchar *retval = NULL;
3664
3665 if (mask & DLS_UPNP_MASK_PROP_DISPLAY_NAME) {
3666 gupnp_didl_lite_object_set_title(
3667 object,
3668 g_variant_get_string(value, NULL));
3669
3670 retval = gupnp_didl_lite_object_get_title_xml_string(object);
3671 } else if (mask & DLS_UPNP_MASK_PROP_ALBUM) {
3672 gupnp_didl_lite_object_set_album(
3673 object,
3674 g_variant_get_string(value, NULL));
3675
3676 retval = gupnp_didl_lite_object_get_album_xml_string(object);
3677 } else if (mask & DLS_UPNP_MASK_PROP_DATE) {
3678 gupnp_didl_lite_object_set_date(
3679 object,
3680 g_variant_get_string(value, NULL));
3681
3682 retval = gupnp_didl_lite_object_get_date_xml_string(object);
3683 } else if (mask & DLS_UPNP_MASK_PROP_TYPE) {
3684 upnp_class = dls_props_media_spec_to_upnp_class(
3685 g_variant_get_string(value, NULL));
3686
3687 gupnp_didl_lite_object_set_upnp_class(object, upnp_class);
3688
3689 retval = gupnp_didl_lite_object_get_upnp_class_xml_string(
3690 object);
3691 } else if (mask & DLS_UPNP_MASK_PROP_TRACK_NUMBER) {
3692 gupnp_didl_lite_object_set_track_number(
3693 object,
3694 g_variant_get_int32(value));
3695
3696 retval = gupnp_didl_lite_object_get_track_number_xml_string(
3697 object);
3698 } else if (mask & DLS_UPNP_MASK_PROP_ARTISTS) {
3699 gupnp_didl_lite_object_unset_artists(object);
3700
3701 (void) g_variant_iter_init(&viter, value);
3702
3703 while (g_variant_iter_next(&viter, "&s", &artist_name)) {
3704 artist = gupnp_didl_lite_object_add_artist(object);
3705
3706 gupnp_didl_lite_contributor_set_name(artist,
3707 artist_name);
3708 }
3709
3710 retval = gupnp_didl_lite_object_get_artists_xml_string(object);
3711 }
3712
3713 return retval;
3714 }
3715
3716 static void prv_get_xml_fragments(GUPnPDIDLLiteParser *parser,
3717 GUPnPDIDLLiteObject *object,
3718 gpointer user_data)
3719 {
3720 GString *current_str;
3721 GString *new_str;
3722 gchar *frag1;
3723 gchar *frag2;
3724 GVariantIter viter;
3725 const gchar *prop;
3726 GVariant *value;
3727 dls_prop_map_t *prop_map;
3728 GUPnPDIDLLiteWriter *writer;
3729 GUPnPDIDLLiteObject *scratch_object;
3730 gboolean first = TRUE;
3731 dls_async_task_t *cb_data = user_data;
3732 dls_async_update_t *cb_task_data = &cb_data->ut.update;
3733 dls_task_t *task = &cb_data->task;
3734 dls_task_update_t *task_data = &task->ut.update;
3735
3736 DLEYNA_LOG_DEBUG("Enter");
3737
3738 current_str = g_string_new("");
3739 new_str = g_string_new("");
3740
3741 writer = gupnp_didl_lite_writer_new(NULL);
3742 if (GUPNP_IS_DIDL_LITE_CONTAINER(object))
3743 scratch_object = GUPNP_DIDL_LITE_OBJECT(
3744 gupnp_didl_lite_writer_add_container(writer));
3745 else
3746 scratch_object = GUPNP_DIDL_LITE_OBJECT(
3747 gupnp_didl_lite_writer_add_item(writer));
3748
3749 (void) g_variant_iter_init(&viter, task_data->to_add_update);
3750
3751 while (g_variant_iter_next(&viter, "{&sv}", &prop, &value)) {
3752
3753 DLEYNA_LOG_DEBUG("to_add_update = %s", prop);
3754
3755 prop_map = g_hash_table_lookup(cb_task_data->map, prop);
3756
3757 frag1 = prv_get_current_xml_fragment(object, prop_map->type);
3758 frag2 = prv_get_new_xml_fragment(scratch_object, prop_map->type,
3759 value);
3760
3761 if (!frag2) {
3762 DLEYNA_LOG_DEBUG("Unable to set %s. Skipping", prop);
3763
3764 g_free(frag1);
3765 continue;
3766 }
3767
3768 if (!first) {
3769 g_string_append(current_str, ",");
3770 g_string_append(new_str, ",");
3771 } else {
3772 first = FALSE;
3773 }
3774
3775 if (frag1) {
3776 g_string_append(current_str, (const gchar *)frag1);
3777 g_free(frag1);
3778 }
3779
3780 g_string_append(new_str, (const gchar *)frag2);
3781 g_free(frag2);
3782 }
3783
3784 (void) g_variant_iter_init(&viter, task_data->to_delete);
3785
3786 while (g_variant_iter_next(&viter, "&s", &prop)) {
3787 DLEYNA_LOG_DEBUG("to_delete = %s", prop);
3788
3789 prop_map = g_hash_table_lookup(cb_task_data->map, prop);
3790
3791 frag1 = prv_get_current_xml_fragment(object, prop_map->type);
3792 if (!frag1)
3793 continue;
3794
3795 if (!first)
3796 g_string_append(current_str, ",");
3797 else
3798 first = FALSE;
3799
3800 g_string_append(current_str, (const gchar *)frag1);
3801 g_free(frag1);
3802 }
3803
3804 cb_task_data->current_tag_value = g_string_free(current_str, FALSE);
3805 DLEYNA_LOG_DEBUG("current_tag_value = %s",
3806 cb_task_data->current_tag_value);
3807
3808 cb_task_data->new_tag_value = g_string_free(new_str, FALSE);
3809 DLEYNA_LOG_DEBUG("new_tag_value = %s", cb_task_data->new_tag_value);
3810
3811 g_object_unref(scratch_object);
3812 g_object_unref(writer);
3813
3814 DLEYNA_LOG_DEBUG("Exit");
3815 }
3816
3817 static void prv_update_object_browse_cb(GUPnPServiceProxy *proxy,
3818 GUPnPServiceProxyAction *action,
3819 gpointer user_data)
3820 {
3821 GError *upnp_error = NULL;
3822 dls_async_task_t *cb_data = user_data;
3823 dls_async_update_t *cb_task_data = &cb_data->ut.update;
3824 GUPnPDIDLLiteParser *parser = NULL;
3825 gchar *result = NULL;
3826
3827 DLEYNA_LOG_DEBUG("Enter");
3828
3829 if (!gupnp_service_proxy_end_action(cb_data->proxy, cb_data->action,
3830 &upnp_error,
3831 "Result", G_TYPE_STRING,
3832 &result, NULL)) {
3833 DLEYNA_LOG_WARNING("Browse Object operation failed: %s",
3834 upnp_error->message);
3835
3836 cb_data->error = g_error_new(DLEYNA_SERVER_ERROR,
3837 DLEYNA_ERROR_OPERATION_FAILED,
3838 "Browse operation failed: %s",
3839 upnp_error->message);
3840 goto on_error;
3841 }
3842
3843 DLEYNA_LOG_DEBUG("dls_device_update_ex_object result: %s", result);
3844
3845 parser = gupnp_didl_lite_parser_new();
3846
3847 g_signal_connect(parser, "object-available",
3848 G_CALLBACK(prv_get_xml_fragments),
3849 cb_data);
3850
3851 if (!gupnp_didl_lite_parser_parse_didl(parser, result, &upnp_error)) {
3852 if (upnp_error->code == GUPNP_XML_ERROR_EMPTY_NODE) {
3853 DLEYNA_LOG_WARNING("Property not defined for object");
3854
3855 cb_data->error =
3856 g_error_new(DLEYNA_SERVER_ERROR,
3857 DLEYNA_ERROR_UNKNOWN_PROPERTY,
3858 "Property not defined for object");
3859 } else {
3860 DLEYNA_LOG_WARNING(
3861 "Unable to parse results of browse: %s",
3862 upnp_error->message);
3863
3864 cb_data->error =
3865 g_error_new(DLEYNA_SERVER_ERROR,
3866 DLEYNA_ERROR_OPERATION_FAILED,
3867 "Unable to parse results of browse: %s",
3868 upnp_error->message);
3869 }
3870
3871 goto on_error;
3872 }
3873
3874 cb_data->action = gupnp_service_proxy_begin_action(
3875 cb_data->proxy, "UpdateObject",
3876 prv_update_object_update_cb, cb_data,
3877 "ObjectID", G_TYPE_STRING, cb_data->task.target.id,
3878 "CurrentTagValue", G_TYPE_STRING,
3879 cb_task_data->current_tag_value,
3880 "NewTagValue", G_TYPE_STRING, cb_task_data->new_tag_value,
3881 NULL);
3882
3883 goto no_complete;
3884
3885 on_error:
3886
3887 (void) g_idle_add(dls_async_task_complete, cb_data);
3888 g_cancellable_disconnect(cb_data->cancellable, cb_data->cancel_id);
3889
3890 no_complete:
3891
3892 if (parser)
3893 g_object_unref(parser);
3894
3895 g_free(result);
3896
3897 if (upnp_error)
3898 g_error_free(upnp_error);
3899
3900 DLEYNA_LOG_DEBUG("Exit");
3901 }
3902
3903 void dls_device_update_object(dls_client_t *client,
3904 dls_task_t *task,
3905 const gchar *upnp_filter)
3906 {
3907 dls_async_task_t *cb_data = (dls_async_task_t *)task;
3908 dls_device_context_t *context;
3909
3910 DLEYNA_LOG_DEBUG("Enter");
3911
3912 context = dls_device_get_context(task->target.device, client);
3913
3914 cb_data->action = gupnp_service_proxy_begin_action(
3915 context->service_proxy, "Browse",
3916 prv_update_object_browse_cb, cb_data,
3917 "ObjectID", G_TYPE_STRING, task->target.id,
3918 "BrowseFlag", G_TYPE_STRING, "BrowseMetadata",
3919 "Filter", G_TYPE_STRING, upnp_filter,
3920 "StartingIndex", G_TYPE_INT, 0,
3921 "RequestedCount", G_TYPE_INT, 0,
3922 "SortCriteria", G_TYPE_STRING,
3923 "", NULL);
3924
3925 cb_data->proxy = context->service_proxy;
3926
3927 g_object_add_weak_pointer((G_OBJECT(context->service_proxy)),
3928 (gpointer *)&cb_data->proxy);
3929
3930 cb_data->cancel_id = g_cancellable_connect(cb_data->cancellable,
3931 G_CALLBACK(dls_async_task_cancelled_cb),
3932 cb_data, NULL);
3933
3934 DLEYNA_LOG_DEBUG("Exit");
3935 }
3936
3937 static void prv_didls_free(gpointer data)
3938 {
3939 prv_new_playlist_ct_t *priv_t = (prv_new_playlist_ct_t *)data;
3940
3941 if (priv_t) {
3942 g_free(priv_t->id);
3943 g_free(priv_t->parent_id);
3944 g_free(priv_t);
3945 }
3946 }
3947
3948 static void prv_playlist_upload_cb(GUPnPServiceProxy *proxy,
3949 GUPnPServiceProxyAction *action,
3950 gpointer user_data)
3951 {
3952 dls_async_task_t *cb_data = user_data;
3953 gchar *didls;
3954
3955 didls = gupnp_media_collection_get_string(
3956 cb_data->ut.playlist.collection);
3957
3958 DLEYNA_LOG_DEBUG_NL();
3959 DLEYNA_LOG_DEBUG("Collection: %s", didls);
3960 DLEYNA_LOG_DEBUG_NL();
3961
3962 prv_generic_upload_cb(cb_data, NULL, didls, strlen(didls), "text/xml");
3963 }
3964
3965 static void prv_create_didls_item_parse_object(GUPnPDIDLLiteParser *parser,
3966 GUPnPDIDLLiteObject *object,
3967 gpointer user_data)
3968 {
3969 const char *class;
3970 const char *title;
3971 const char *artist;
3972 const char *album;
3973 const char *uri = NULL;
3974 GList *resources;
3975 GUPnPDIDLLiteObject *item_obj;
3976 GUPnPDIDLLiteItem *item;
3977 GUPnPDIDLLiteResource *res;
3978 GUPnPMediaCollection *collection = user_data;
3979
3980 if (GUPNP_IS_DIDL_LITE_CONTAINER(object))
3981 goto exit;
3982
3983 class = gupnp_didl_lite_object_get_upnp_class(object);
3984 title = gupnp_didl_lite_object_get_title(object);
3985 artist = gupnp_didl_lite_object_get_artist(object);
3986 album = gupnp_didl_lite_object_get_album(object);
3987 resources = gupnp_didl_lite_object_get_resources(object);
3988
3989 if (resources != NULL) {
3990 if (resources->data != NULL)
3991 uri = gupnp_didl_lite_resource_get_uri(resources->data);
3992
3993 g_list_free_full(resources, g_object_unref);
3994 }
3995
3996 DLEYNA_LOG_DEBUG("Create DIDL_S Item");
3997 DLEYNA_LOG_DEBUG("title: %s", title);
3998 DLEYNA_LOG_DEBUG("class: %s", class);
3999 DLEYNA_LOG_DEBUG("Artist: %s", artist);
4000 DLEYNA_LOG_DEBUG("album: %s", album);
4001 DLEYNA_LOG_DEBUG("URI: %s", uri);
4002 DLEYNA_LOG_DEBUG_NL();
4003
4004 item = gupnp_media_collection_add_item(collection);
4005 item_obj = GUPNP_DIDL_LITE_OBJECT(item);
4006
4007 if (title && *title)
4008 gupnp_didl_lite_object_set_title(item_obj, title);
4009
4010 if (class && *class)
4011 gupnp_didl_lite_object_set_upnp_class(item_obj, class);
4012
4013 if (artist && *artist)
4014 gupnp_didl_lite_object_set_artist(item_obj, artist);
4015
4016 if (album && *album)
4017 gupnp_didl_lite_object_set_album(item_obj, album);
4018
4019 if (uri && *uri) {
4020 res = gupnp_didl_lite_object_add_resource(item_obj);
4021 gupnp_didl_lite_resource_set_uri(res, uri);
4022 g_object_unref(res);
4023 }
4024
4025 g_object_unref(item);
4026
4027 exit:
4028 return;
4029 }
4030
4031 static void prv_create_didls_item_browse_cb(GUPnPServiceProxy *proxy,
4032 GUPnPServiceProxyAction *action,
4033 gpointer user_data)
4034 {
4035 GError *error = NULL;
4036 prv_new_playlist_ct_t *priv_t = user_data;
4037 dls_async_task_t *cb_data = priv_t->cb_data;
4038 GUPnPDIDLLiteParser *parser = NULL;
4039 gchar *result = NULL;
4040
4041 if (!gupnp_service_proxy_end_action(proxy, action, &error,
4042 "Result", G_TYPE_STRING,
4043 &result, NULL)) {
4044 DLEYNA_LOG_WARNING("Browse Object operation failed: %s",
4045 error->message);
4046 DLEYNA_LOG_DEBUG_NL();
4047
4048 cb_data->error = g_error_new(DLEYNA_SERVER_ERROR,
4049 DLEYNA_ERROR_OPERATION_FAILED,
4050 "Browse operation failed: %s",
4051 error->message);
4052 goto on_exit;
4053 }
4054
4055 DLEYNA_LOG_DEBUG_NL();
4056 DLEYNA_LOG_DEBUG("Result: %s", result);
4057 DLEYNA_LOG_DEBUG_NL();
4058
4059 parser = gupnp_didl_lite_parser_new();
4060
4061 g_signal_connect(parser, "object-available",
4062 G_CALLBACK(prv_create_didls_item_parse_object),
4063 cb_data->ut.playlist.collection);
4064
4065 if (gupnp_didl_lite_parser_parse_didl(parser, result, &error))
4066 goto on_exit;
4067
4068 if (error->code == GUPNP_XML_ERROR_EMPTY_NODE) {
4069 DLEYNA_LOG_WARNING("Property not defined for object");
4070
4071 cb_data->error = g_error_new(DLEYNA_SERVER_ERROR,
4072 DLEYNA_ERROR_UNKNOWN_PROPERTY,
4073 "Property not defined for object");
4074 } else {
4075 DLEYNA_LOG_WARNING("Unable to parse results of browse: %s",
4076 error->message);
4077
4078 cb_data->error = g_error_new(DLEYNA_SERVER_ERROR,
4079 DLEYNA_ERROR_OPERATION_FAILED,
4080 "Unable to parse results of browse: %s",
4081 error->message);
4082 }
4083
4084 on_exit:
4085
4086 if (cb_data->error != NULL)
4087 dleyna_task_processor_cancel_queue(
4088 cb_data->ut.playlist.queue_id);
4089
4090 if (parser)
4091 g_object_unref(parser);
4092
4093 if (error)
4094 g_error_free(error);
4095
4096 g_free(result);
4097 }
4098
4099 static GUPnPServiceProxyAction *prv_create_didls_item_browse(
4100 dleyna_service_task_t *task,
4101 GUPnPServiceProxy *proxy,
4102 gboolean *failed)
4103 {
4104 prv_new_playlist_ct_t *priv_t;
4105
4106 priv_t = (prv_new_playlist_ct_t *)dleyna_service_task_get_user_data(
4107 task);
4108 *failed = FALSE;
4109
4110 DLEYNA_LOG_DEBUG("Browse for ID: %s", priv_t->id);
4111
4112 return gupnp_service_proxy_begin_action(
4113 proxy, "Browse",
4114 dleyna_service_task_begin_action_cb, task,
4115 "ObjectID", G_TYPE_STRING, priv_t->id,
4116 "BrowseFlag", G_TYPE_STRING, "BrowseMetadata",
4117 "Filter", G_TYPE_STRING, "upnp:artist,upnp:album,res",
4118 "StartingIndex", G_TYPE_INT, 0,
4119 "RequestedCount", G_TYPE_INT, 1,
4120 "SortCriteria", G_TYPE_STRING, "", NULL);
4121 }
4122
4123 static gboolean prv_create_chain_didls_items(dls_task_t *task,
4124 GUPnPServiceProxy *proxy,
4125 dls_async_task_t *cb_data)
4126 {
4127 gchar *root_path = NULL;
4128 gchar *path;
4129 gchar *id = NULL;
4130 prv_new_playlist_ct_t *priv_t;
4131 dls_async_playlist_t *a_playlist = &cb_data->ut.playlist;
4132 GVariantIter iter;
4133
4134 DLEYNA_LOG_DEBUG_NL();
4135
4136 a_playlist->collection = gupnp_media_collection_new();
4137 gupnp_media_collection_set_title(a_playlist->collection,
4138 task->ut.playlist.title);
4139 gupnp_media_collection_set_author(a_playlist->collection,
4140 task->ut.playlist.creator);
4141
4142 g_variant_iter_init(&iter, task->ut.playlist.item_path);
4143
4144 while (g_variant_iter_next(&iter, "&o", &path)) {
4145 if (!dls_path_get_path_and_id(path, &root_path, &id, NULL)) {
4146 DLEYNA_LOG_DEBUG("Can't get id for path %s", path);
4147 cb_data->error = g_error_new(
4148 DLEYNA_SERVER_ERROR,
4149 DLEYNA_ERROR_OBJECT_NOT_FOUND,
4150 "Unable to find object for path: %s",
4151 path);
4152 goto on_error;
4153 }
4154
4155 DLEYNA_LOG_DEBUG("Create Task: @id: %s - Root: %s",
4156 id, root_path);
4157
4158 g_free(root_path);
4159
4160 priv_t = g_new0(prv_new_playlist_ct_t, 1);
4161 priv_t->cb_data = cb_data;
4162 priv_t->id = id;
4163
4164 dleyna_service_task_add(a_playlist->queue_id,
4165 prv_create_didls_item_browse,
4166 proxy,
4167 prv_create_didls_item_browse_cb,
4168 prv_didls_free, priv_t);
4169 }
4170
4171 DLEYNA_LOG_DEBUG_NL();
4172 return TRUE;
4173
4174 on_error:
4175
4176 return FALSE;
4177 }
4178
4179 static void prv_create_playlist_object(dls_task_create_playlist_t *t_playlist,
4180 dls_async_playlist_t *a_playlist,
4181 char *parent_id)
4182 {
4183 GUPnPDIDLLiteWriter *writer;
4184 GUPnPDIDLLiteObject *item;
4185 GUPnPProtocolInfo *protocol_info;
4186 GUPnPDIDLLiteResource *res;
4187 GUPnPDIDLLiteContributor *creator;
4188 GUPnPDIDLLiteContributor *author;
4189 GTimeVal time_v;
4190 gchar *time_c;
4191
4192 writer = gupnp_didl_lite_writer_new(NULL);
4193 item = GUPNP_DIDL_LITE_OBJECT(gupnp_didl_lite_writer_add_item(writer));
4194
4195 gupnp_didl_lite_object_set_id(item, "");
4196 gupnp_didl_lite_object_set_title(item, t_playlist->title);
4197 gupnp_didl_lite_object_set_genre(item, t_playlist->genre);
4198 gupnp_didl_lite_object_set_description(item, t_playlist->desc);
4199
4200 creator = gupnp_didl_lite_object_add_creator(item);
4201 author = gupnp_didl_lite_object_add_author(item);
4202 gupnp_didl_lite_contributor_set_name(creator, t_playlist->creator);
4203 gupnp_didl_lite_contributor_set_name(author, t_playlist->creator);
4204
4205 gupnp_didl_lite_object_set_parent_id(item, parent_id);
4206 gupnp_didl_lite_object_set_upnp_class(item, "object.item.playlistItem");
4207 gupnp_didl_lite_object_set_restricted(item, FALSE);
4208
4209 protocol_info = gupnp_protocol_info_new();
4210 gupnp_protocol_info_set_mime_type(protocol_info, "text/xml");
4211 gupnp_protocol_info_set_protocol(protocol_info, "*");
4212 gupnp_protocol_info_set_network(protocol_info, "*");
4213 gupnp_protocol_info_set_dlna_profile(protocol_info, "DIDL_S");
4214
4215 res = gupnp_didl_lite_object_add_resource(item);
4216 gupnp_didl_lite_resource_set_protocol_info(res, protocol_info);
4217
4218 g_get_current_time(&time_v);
4219 time_c = g_time_val_to_iso8601(&time_v);
4220 gupnp_didl_lite_object_set_date(item, time_c);
4221
4222 /* TODO: Need to compute DLNA Profile */
4223
4224 a_playlist->didl = gupnp_didl_lite_writer_get_string(writer);
4225
4226 DLEYNA_LOG_DEBUG("Playlist object %s created", t_playlist->title);
4227
4228 g_object_unref(res);
4229 g_object_unref(protocol_info);
4230 g_object_unref(creator);
4231 g_object_unref(author);
4232 g_object_unref(item);
4233 g_object_unref(writer);
4234 g_free(time_c);
4235 }
4236
4237 static void prv_create_didls_chain_end(gboolean cancelled, gpointer data)
4238 {
4239 prv_new_playlist_ct_t *priv_t = data;
4240 dls_async_task_t *cb_data = priv_t->cb_data;
4241 dls_async_playlist_t *a_playlist;
4242 dls_task_create_playlist_t *t_playlist;
4243
4244 DLEYNA_LOG_DEBUG("Enter");
4245
4246 if (cb_data->cancel_id) {
4247 if (!g_cancellable_is_cancelled(cb_data->cancellable))
4248 g_cancellable_disconnect(cb_data->cancellable,
4249 cb_data->cancel_id);
4250 cb_data->cancel_id = 0;
4251 }
4252
4253 if (cancelled)
4254 goto on_clear;
4255
4256 t_playlist = &cb_data->task.ut.playlist;
4257 a_playlist = &cb_data->ut.playlist;
4258 prv_create_playlist_object(t_playlist, a_playlist, priv_t->parent_id);
4259
4260 DLEYNA_LOG_DEBUG("Creating object");
4261 cb_data->action = gupnp_service_proxy_begin_action(
4262 cb_data->proxy,
4263 "CreateObject",
4264 prv_playlist_upload_cb, cb_data,
4265 "ContainerID", G_TYPE_STRING, priv_t->parent_id,
4266 "Elements", G_TYPE_STRING, a_playlist->didl,
4267 NULL);
4268
4269 cb_data->cancel_id = g_cancellable_connect(
4270 cb_data->cancellable,
4271 G_CALLBACK(dls_async_task_cancelled_cb),
4272 cb_data, NULL);
4273 on_clear:
4274
4275 if (cancelled) {
4276 if (!cb_data->error)
4277 cb_data->error = g_error_new(DLEYNA_SERVER_ERROR,
4278 DLEYNA_ERROR_CANCELLED,
4279 "Operation cancelled.");
4280 (void) g_idle_add(dls_async_task_complete, cb_data);
4281 }
4282
4283 prv_didls_free(priv_t);
4284
4285 cb_data->ut.playlist.queue_id = NULL;
4286
4287 DLEYNA_LOG_DEBUG("Exit");
4288 }
4289
4290 static void prv_create_chain_cancelled(GCancellable *cancellable,
4291 gpointer user_data)
4292 {
4293 dls_async_task_t *cb_data = user_data;
4294 const dleyna_task_queue_key_t *queue_id = cb_data->ut.playlist.queue_id;
4295
4296 DLEYNA_LOG_DEBUG("Enter");
4297
4298 dleyna_task_processor_cancel_queue(queue_id);
4299 }
4300
4301 void dls_device_playlist_upload(dls_client_t *client,
4302 dls_task_t *task,
4303 const gchar *parent_id)
4304 {
4305 dls_async_task_t *cb_data = (dls_async_task_t *)task;
4306 dls_device_context_t *context;
4307 prv_new_playlist_ct_t *priv_t;
4308 const dleyna_task_queue_key_t *queue_id;
4309
4310 DLEYNA_LOG_DEBUG("Enter");
4311 DLEYNA_LOG_DEBUG("Uploading playlist to %s", parent_id);
4312
4313 priv_t = g_new0(prv_new_playlist_ct_t, 1);
4314 priv_t->cb_data = cb_data;
4315 priv_t->parent_id = g_strdup(parent_id);
4316
4317 queue_id = dleyna_task_processor_add_queue(
4318 dls_server_get_task_processor(),
4319 dleyna_service_task_create_source(),
4320 DLS_SERVER_SINK,
4321 DLEYNA_TASK_QUEUE_FLAG_AUTO_REMOVE,
4322 dleyna_service_task_process_cb,
4323 dleyna_service_task_cancel_cb,
4324 dleyna_service_task_delete_cb);
4325 dleyna_task_queue_set_finally(queue_id, prv_create_didls_chain_end);
4326 dleyna_task_queue_set_user_data(queue_id, priv_t);
4327
4328 context = dls_device_get_context(task->target.device, client);
4329
4330 cb_data->proxy = context->service_proxy;
4331 cb_data->ut.playlist.queue_id = queue_id;
4332
4333 g_object_add_weak_pointer((G_OBJECT(context->service_proxy)),
4334 (gpointer *)&cb_data->proxy);
4335
4336 if (prv_create_chain_didls_items(task, cb_data->proxy, cb_data)) {
4337 cb_data->cancel_id = g_cancellable_connect(
4338 cb_data->cancellable,
4339 G_CALLBACK(prv_create_chain_cancelled),
4340 cb_data, NULL);
4341 dleyna_task_queue_start(queue_id);
4342 } else {
4343 (void) g_idle_add(dls_async_task_complete, cb_data);
4344 }
4345
4346 DLEYNA_LOG_DEBUG("Exit");
4347 }
0 /*
1 * dLeyna
2 *
3 * Copyright (C) 2012-2013 Intel Corporation. All rights reserved.
4 *
5 * This program is free software; you can redistribute it and/or modify it
6 * under the terms and conditions of the GNU Lesser General Public License,
7 * version 2.1, as published by the Free Software Foundation.
8 *
9 * This program is distributed in the hope it will be useful, but WITHOUT
10 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
11 * FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License
12 * for more details.
13 *
14 * You should have received a copy of the GNU Lesser General Public License
15 * along with this program; if not, write to the Free Software Foundation, Inc.,
16 * 51 Franklin St - Fifth Floor, Boston, MA 02110-1301 USA.
17 *
18 * Mark Ryan <mark.d.ryan@intel.com>
19 *
20 */
21
22 #ifndef DLS_DEVICE_H__
23 #define DLS_DEVICE_H__
24
25 #include <libgupnp/gupnp-control-point.h>
26
27 #include <libdleyna/core/connector.h>
28 #include <libdleyna/core/task-processor.h>
29
30 #include "async.h"
31 #include "client.h"
32 #include "props.h"
33
34 struct dls_device_context_t_ {
35 gchar *ip_address;
36 GUPnPDeviceProxy *device_proxy;
37 GUPnPServiceProxy *service_proxy;
38 dls_device_t *device;
39 gboolean subscribed;
40 guint timeout_id;
41 };
42
43 struct dls_device_t_ {
44 dleyna_connector_id_t connection;
45 guint id;
46 gchar *path;
47 GPtrArray *contexts;
48 guint timeout_id;
49 GHashTable *uploads;
50 GHashTable *upload_jobs;
51 guint upload_id;
52 guint system_update_id;
53 GVariant *search_caps;
54 GVariant *sort_caps;
55 GVariant *sort_ext_caps;
56 GVariant *feature_list;
57 gboolean shutting_down;
58 };
59
60 dls_device_context_t *dls_device_append_new_context(dls_device_t *device,
61 const gchar *ip_address,
62 GUPnPDeviceProxy *proxy);
63 void dls_device_delete(void *device);
64
65 void dls_device_unsubscribe(void *device);
66
67 dls_device_t *dls_device_new(
68 dleyna_connector_id_t connection,
69 GUPnPDeviceProxy *proxy,
70 const gchar *ip_address,
71 const dleyna_connector_dispatch_cb_t *dispatch_table,
72 GHashTable *filter_map,
73 guint counter,
74 const dleyna_task_queue_key_t *queue_id);
75
76 dls_device_t *dls_device_from_path(const gchar *path, GHashTable *device_list);
77
78 dls_device_context_t *dls_device_get_context(const dls_device_t *device,
79 dls_client_t *client);
80
81 void dls_device_get_children(dls_client_t *client,
82 dls_task_t *task,
83 const gchar *upnp_filter, const gchar *sort_by);
84
85 void dls_device_get_all_props(dls_client_t *client,
86 dls_task_t *task,
87 gboolean root_object);
88
89 void dls_device_get_prop(dls_client_t *client,
90 dls_task_t *task,
91 dls_prop_map_t *prop_map, gboolean root_object);
92
93 void dls_device_search(dls_client_t *client,
94 dls_task_t *task,
95 const gchar *upnp_filter, const gchar *upnp_query,
96 const gchar *sort_by);
97
98 void dls_device_get_resource(dls_client_t *client,
99 dls_task_t *task,
100 const gchar *upnp_filter);
101
102 void dls_device_subscribe_to_contents_change(dls_device_t *device);
103
104 void dls_device_upload(dls_client_t *client,
105 dls_task_t *task, const gchar *parent_id);
106
107 gboolean dls_device_get_upload_status(dls_task_t *task, GError **error);
108
109 gboolean dls_device_cancel_upload(dls_task_t *task, GError **error);
110
111 void dls_device_get_upload_ids(dls_task_t *task);
112
113 void dls_device_delete_object(dls_client_t *client,
114 dls_task_t *task);
115
116 void dls_device_create_container(dls_client_t *client,
117 dls_task_t *task,
118 const gchar *parent_id);
119
120 void dls_device_update_object(dls_client_t *client,
121 dls_task_t *task,
122 const gchar *upnp_filter);
123
124 void dls_device_playlist_upload(dls_client_t *client,
125 dls_task_t *task,
126 const gchar *parent_id);
127
128 #endif /* DLS_DEVICE_H__ */
0 prefix=@prefix@
1 exec_prefix=@exec_prefix@
2 libexecdir=@libexecdir@
3 includedir=${prefix}/include
4 libdir=@libdir@
5
6 Name: @PACKAGE@
7 Description: UPnP & DLNA server library
8 Libs: -L${libdir} -ldleyna-server-1.0
9 Requires.private: glib-2.0 gio-2.0 gupnp-1.0 gupnp-av-1.0 dleyna-core-1.0
10 Version: @VERSION@
0 # Configuration file for dleyna-server
1 #
2 #
3 #
4 # General configuration options
5 [general]
6
7 # true: Service always stay in memory running
8 # false: Service quit when the last client disconnects.
9 never-quit=@never_quit@
10
11 # IPC connector name
12 connector-name=@with_connector_name@
13
14 # Log configuration options
15 [log]
16
17 # Define the logging output method. 3 technologies are defined:
18 #
19 # 0=Syslog
20 # 1=GLib
21 # 2=File
22 log-type=@with_log_type@
23
24 # Comma-separated list of logging level.
25 # Log levels are: 1=critical, 2=error, 3=warning, 4=message, 5=info, 6=debug
26 #
27 # Allowed values for log-levels are
28 # 0 = disabled
29 # 7 = default (=1,2,5)
30 # 8 = all (=1,2,3,4,5,6)
31 # 1,..,6 = a comma separated list of log level
32 #
33 # IMPORTANT: This log level is a subset of the log level defined at compile time
34 # You can't enable levels disabled at compile time
35 # level=8 means all level flags defined at compile time.
36 log-level=@with_log_level@
0 /*
1 * dLeyna
2 *
3 * Copyright (C) 2012-2013 Intel Corporation. All rights reserved.
4 *
5 * This program is free software; you can redistribute it and/or modify it
6 * under the terms and conditions of the GNU Lesser General Public License,
7 * version 2.1, as published by the Free Software Foundation.
8 *
9 * This program is distributed in the hope it will be useful, but WITHOUT
10 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
11 * FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License
12 * for more details.
13 *
14 * You should have received a copy of the GNU Lesser General Public License
15 * along with this program; if not, write to the Free Software Foundation, Inc.,
16 * 51 Franklin St - Fifth Floor, Boston, MA 02110-1301 USA.
17 *
18 * Regis Merlino <regis.merlino@intel.com>
19 *
20 */
21
22 #ifndef DLEYNA_SERVER_INTERFACE_H__
23 #define DLEYNA_SERVER_INTERFACE_H__
24
25 enum dls_interface_type_ {
26 DLS_INTERFACE_INFO_PROPERTIES,
27 DLS_INTERFACE_INFO_OBJECT,
28 DLS_INTERFACE_INFO_CONTAINER,
29 DLS_INTERFACE_INFO_ITEM,
30 DLS_INTERFACE_INFO_DEVICE,
31 DLS_INTERFACE_INFO_MAX
32 };
33
34 #define DLS_INTERFACE_PROPERTIES "org.freedesktop.DBus.Properties"
35 #define DLS_INTERFACE_MEDIA_CONTAINER "org.gnome.UPnP.MediaContainer2"
36 #define DLS_INTERFACE_MEDIA_OBJECT "org.gnome.UPnP.MediaObject2"
37 #define DLS_INTERFACE_MEDIA_ITEM "org.gnome.UPnP.MediaItem2"
38
39 /* Object Properties */
40 #define DLS_INTERFACE_PROP_PATH "Path"
41 #define DLS_INTERFACE_PROP_PARENT "Parent"
42 #define DLS_INTERFACE_PROP_RESTRICTED "Restricted"
43 #define DLS_INTERFACE_PROP_DISPLAY_NAME "DisplayName"
44 #define DLS_INTERFACE_PROP_TYPE "Type"
45 #define DLS_INTERFACE_PROP_CREATOR "Creator"
46 #define DLS_INTERFACE_PROP_DLNA_MANAGED "DLNAManaged"
47 #define DLS_INTERFACE_PROP_OBJECT_UPDATE_ID "ObjectUpdateID"
48
49 /* Item Properties */
50 #define DLS_INTERFACE_PROP_REFPATH "RefPath"
51 #define DLS_INTERFACE_PROP_ARTIST "Artist"
52 #define DLS_INTERFACE_PROP_ARTISTS "Artists"
53 #define DLS_INTERFACE_PROP_ALBUM "Album"
54 #define DLS_INTERFACE_PROP_DATE "Date"
55 #define DLS_INTERFACE_PROP_GENRE "Genre"
56 #define DLS_INTERFACE_PROP_TRACK_NUMBER "TrackNumber"
57 #define DLS_INTERFACE_PROP_ALBUM_ART_URL "AlbumArtURL"
58 #define DLS_INTERFACE_PROP_RESOURCES "Resources"
59
60 /* Container Properties */
61 #define DLS_INTERFACE_PROP_SEARCHABLE "Searchable"
62 #define DLS_INTERFACE_PROP_CHILD_COUNT "ChildCount"
63 #define DLS_INTERFACE_PROP_CREATE_CLASSES "CreateClasses"
64 #define DLS_INTERFACE_PROP_CONTAINER_UPDATE_ID "ContainerUpdateID"
65 #define DLS_INTERFACE_PROP_TOTAL_DELETED_CHILD_COUNT "TotalDeletedChildCount"
66
67 /* Device Properties */
68 #define DLS_INTERFACE_PROP_LOCATION "Location"
69 #define DLS_INTERFACE_PROP_UDN "UDN"
70 #define DLS_INTERFACE_PROP_DEVICE_TYPE "DeviceType"
71 #define DLS_INTERFACE_PROP_FRIENDLY_NAME "FriendlyName"
72 #define DLS_INTERFACE_PROP_MANUFACTURER "Manufacturer"
73 #define DLS_INTERFACE_PROP_MANUFACTURER_URL "ManufacturerUrl"
74 #define DLS_INTERFACE_PROP_MODEL_DESCRIPTION "ModelDescription"
75 #define DLS_INTERFACE_PROP_MODEL_NAME "ModelName"
76 #define DLS_INTERFACE_PROP_MODEL_NUMBER "ModelNumber"
77 #define DLS_INTERFACE_PROP_MODEL_URL "ModelURL"
78 #define DLS_INTERFACE_PROP_SERIAL_NUMBER "SerialNumber"
79 #define DLS_INTERFACE_PROP_PRESENTATION_URL "PresentationURL"
80 #define DLS_INTERFACE_PROP_ICON_URL "IconURL"
81 #define DLS_INTERFACE_PROP_SV_DLNA_CAPABILITIES "DLNACaps"
82 #define DLS_INTERFACE_PROP_SV_SEARCH_CAPABILITIES "SearchCaps"
83 #define DLS_INTERFACE_PROP_SV_SORT_CAPABILITIES "SortCaps"
84 #define DLS_INTERFACE_PROP_SV_SORT_EXT_CAPABILITIES "SortExtCaps"
85 #define DLS_INTERFACE_PROP_SV_FEATURE_LIST "FeatureList"
86 #define DLS_INTERFACE_PROP_SV_SERVICE_RESET_TOKEN "ServiceResetToken"
87
88 /* Resources Properties */
89 #define DLS_INTERFACE_PROP_MIME_TYPE "MIMEType"
90 #define DLS_INTERFACE_PROP_DLNA_PROFILE "DLNAProfile"
91 #define DLS_INTERFACE_PROP_SIZE "Size"
92 #define DLS_INTERFACE_PROP_DURATION "Duration"
93 #define DLS_INTERFACE_PROP_BITRATE "Bitrate"
94 #define DLS_INTERFACE_PROP_SAMPLE_RATE "SampleRate"
95 #define DLS_INTERFACE_PROP_BITS_PER_SAMPLE "BitsPerSample"
96 #define DLS_INTERFACE_PROP_WIDTH "Width"
97 #define DLS_INTERFACE_PROP_HEIGHT "Height"
98 #define DLS_INTERFACE_PROP_COLOR_DEPTH "ColorDepth"
99 #define DLS_INTERFACE_PROP_URLS "URLs"
100 #define DLS_INTERFACE_PROP_URL "URL"
101 #define DLS_INTERFACE_PROP_UPDATE_COUNT "UpdateCount"
102
103 /* Evented State Variable Properties */
104 #define DLS_INTERFACE_PROP_ESV_SYSTEM_UPDATE_ID "SystemUpdateID"
105
106 #define DLS_INTERFACE_GET_VERSION "GetVersion"
107 #define DLS_INTERFACE_GET_SERVERS "GetServers"
108 #define DLS_INTERFACE_RELEASE "Release"
109 #define DLS_INTERFACE_SET_PROTOCOL_INFO "SetProtocolInfo"
110 #define DLS_INTERFACE_PREFER_LOCAL_ADDRESSES "PreferLocalAddresses"
111
112 #define DLS_INTERFACE_FOUND_SERVER "FoundServer"
113 #define DLS_INTERFACE_LOST_SERVER "LostServer"
114
115 #define DLS_INTERFACE_LIST_CHILDREN "ListChildren"
116 #define DLS_INTERFACE_LIST_CHILDREN_EX "ListChildrenEx"
117 #define DLS_INTERFACE_LIST_ITEMS "ListItems"
118 #define DLS_INTERFACE_LIST_ITEMS_EX "ListItemsEx"
119 #define DLS_INTERFACE_LIST_CONTAINERS "ListContainers"
120 #define DLS_INTERFACE_LIST_CONTAINERS_EX "ListContainersEx"
121 #define DLS_INTERFACE_SEARCH_OBJECTS "SearchObjects"
122 #define DLS_INTERFACE_SEARCH_OBJECTS_EX "SearchObjectsEx"
123 #define DLS_INTERFACE_UPDATE "Update"
124
125 #define DLS_INTERFACE_GET_COMPATIBLE_RESOURCE "GetCompatibleResource"
126
127 #define DLS_INTERFACE_GET "Get"
128 #define DLS_INTERFACE_GET_ALL "GetAll"
129 #define DLS_INTERFACE_INTERFACE_NAME "InterfaceName"
130 #define DLS_INTERFACE_PROPERTY_NAME "PropertyName"
131 #define DLS_INTERFACE_PROPERTIES_VALUE "Properties"
132 #define DLS_INTERFACE_VALUE "value"
133 #define DLS_INTERFACE_CHILD_TYPES "ChildTypes"
134
135 #define DLS_INTERFACE_VERSION "Version"
136 #define DLS_INTERFACE_SERVERS "Servers"
137
138 #define DLS_INTERFACE_CRITERIA "Criteria"
139 #define DLS_INTERFACE_DICT "Dictionary"
140 #define DLS_INTERFACE_PATH "Path"
141 #define DLS_INTERFACE_QUERY "Query"
142 #define DLS_INTERFACE_PROTOCOL_INFO "ProtocolInfo"
143 #define DLS_INTERFACE_PREFER "Prefer"
144
145 #define DLS_INTERFACE_OFFSET "Offset"
146 #define DLS_INTERFACE_MAX "Max"
147 #define DLS_INTERFACE_FILTER "Filter"
148 #define DLS_INTERFACE_CHILDREN "Children"
149 #define DLS_INTERFACE_SORT_BY "SortBy"
150 #define DLS_INTERFACE_TOTAL_ITEMS "TotalItems"
151
152 #define DLS_INTERFACE_PROPERTIES_CHANGED "PropertiesChanged"
153 #define DLS_INTERFACE_CHANGED_PROPERTIES "ChangedProperties"
154 #define DLS_INTERFACE_INVALIDATED_PROPERTIES "InvalidatedProperties"
155 #define DLS_INTERFACE_ESV_CONTAINER_UPDATE_IDS "ContainerUpdateIDs"
156 #define DLS_INTERFACE_CONTAINER_PATHS_ID "ContainerPathsIDs"
157 #define DLS_INTERFACE_ESV_LAST_CHANGE "LastChange"
158 #define DLS_INTERFACE_LAST_CHANGE_STATE_EVENT "StateEvent"
159
160 #define DLS_INTERFACE_DELETE "Delete"
161
162 #define DLS_INTERFACE_CREATE_CONTAINER "CreateContainer"
163 #define DLS_INTERFACE_CREATE_CONTAINER_IN_ANY "CreateContainerInAnyContainer"
164
165 #define DLS_INTERFACE_UPLOAD "Upload"
166 #define DLS_INTERFACE_UPLOAD_TO_ANY "UploadToAnyContainer"
167 #define DLS_INTERFACE_GET_UPLOAD_STATUS "GetUploadStatus"
168 #define DLS_INTERFACE_GET_UPLOAD_IDS "GetUploadIDs"
169 #define DLS_INTERFACE_CANCEL_UPLOAD "CancelUpload"
170 #define DLS_INTERFACE_TOTAL "Total"
171 #define DLS_INTERFACE_LENGTH "Length"
172 #define DLS_INTERFACE_FILE_PATH "FilePath"
173 #define DLS_INTERFACE_UPLOAD_ID "UploadId"
174 #define DLS_INTERFACE_UPLOAD_IDS "UploadIDs"
175 #define DLS_INTERFACE_UPLOAD_STATUS "UploadStatus"
176 #define DLS_INTERFACE_UPLOAD_UPDATE "UploadUpdate"
177 #define DLS_INTERFACE_TO_ADD_UPDATE "ToAddUpdate"
178 #define DLS_INTERFACE_TO_DELETE "ToDelete"
179 #define DLS_INTERFACE_CANCEL "Cancel"
180
181 #define DLS_INTERFACE_CREATE_PLAYLIST "CreatePlaylist"
182 #define DLS_INTERFACE_CREATE_PLAYLIST_TO_ANY "CreatePlaylistInAnyContainer"
183 #define DLS_INTERFACE_TITLE "Title"
184 #define DLS_INTERFACE_CREATOR "Creator"
185 #define DLS_INTERFACE_GENRE "Genre"
186 #define DLS_INTERFACE_DESCRIPTION "Description"
187 #define DLS_INTERFACE_PLAYLIST_ITEMS "PlaylistItems"
188
189
190 #endif /* DLEYNA_SERVER_INTERFACE_H__ */
0 /*
1 * dLeyna
2 *
3 * Copyright (C) 2012-2013 Intel Corporation. All rights reserved.
4 *
5 * This program is free software; you can redistribute it and/or modify it
6 * under the terms and conditions of the GNU Lesser General Public License,
7 * version 2.1, as published by the Free Software Foundation.
8 *
9 * This program is distributed in the hope it will be useful, but WITHOUT
10 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
11 * FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License
12 * for more details.
13 *
14 * You should have received a copy of the GNU Lesser General Public License
15 * along with this program; if not, write to the Free Software Foundation, Inc.,
16 * 51 Franklin St - Fifth Floor, Boston, MA 02110-1301 USA.
17 *
18 * Mark Ryan <mark.d.ryan@intel.com>
19 *
20 */
21
22 #include <stdio.h>
23 #include <string.h>
24
25 #include <libdleyna/core/error.h>
26
27 #include "path.h"
28 #include "server.h"
29
30 gboolean dls_path_get_non_root_id(const gchar *object_path,
31 const gchar **slash_before_id)
32 {
33 gboolean retval = FALSE;
34 unsigned int offset = strlen(DLEYNA_SERVER_PATH) + 1;
35
36 if (!g_str_has_prefix(object_path, DLEYNA_SERVER_PATH "/"))
37 goto on_error;
38
39 if (!object_path[offset])
40 goto on_error;
41
42 *slash_before_id = strchr(&object_path[offset], '/');
43 retval = TRUE;
44
45 on_error:
46
47 return retval;
48 }
49
50 static gchar *prv_object_name_to_id(const gchar *object_name)
51 {
52 gchar *retval = NULL;
53 unsigned int object_len = strlen(object_name);
54 unsigned int i;
55 gint hex;
56 gchar byte;
57
58 if (object_len & 1)
59 goto on_error;
60
61 retval = g_malloc((object_len >> 1) + 1);
62
63 for (i = 0; i < object_len; i += 2) {
64 hex = g_ascii_xdigit_value(object_name[i]);
65
66 if (hex == -1)
67 goto on_error;
68
69 byte = hex << 4;
70 hex = g_ascii_xdigit_value(object_name[i + 1]);
71
72 if (hex == -1)
73 goto on_error;
74
75 byte |= hex;
76 retval[i >> 1] = byte;
77 }
78 retval[i >> 1] = 0;
79
80 return retval;
81
82 on_error:
83
84 g_free(retval);
85
86 return NULL;
87 }
88
89 gboolean dls_path_get_path_and_id(const gchar *object_path, gchar **root_path,
90 gchar **id, GError **error)
91 {
92 const gchar *slash;
93 gchar *coded_id;
94
95 if (!dls_path_get_non_root_id(object_path, &slash))
96 goto on_error;
97
98 if (!slash) {
99 *root_path = g_strdup(object_path);
100 *id = g_strdup("0");
101 } else {
102 if (!slash[1])
103 goto on_error;
104
105 coded_id = prv_object_name_to_id(slash + 1);
106
107 if (!coded_id)
108 goto on_error;
109
110 *root_path = g_strndup(object_path, slash - object_path);
111 *id = coded_id;
112 }
113
114 return TRUE;
115
116 on_error:
117 if (error)
118 *error = g_error_new(DLEYNA_SERVER_ERROR, DLEYNA_ERROR_BAD_PATH,
119 "object path is badly formed.");
120
121 return FALSE;
122 }
123
124 static gchar *prv_id_to_object_name(const gchar *id)
125 {
126 gchar *retval;
127 unsigned int i;
128 unsigned int data_len = strlen(id);
129
130 retval = g_malloc((data_len << 1) + 1);
131 retval[0] = 0;
132
133 for (i = 0; i < data_len; i++)
134 sprintf(&retval[i << 1], "%0x", (guint8) id[i]);
135
136 return retval;
137 }
138
139 gchar *dls_path_from_id(const gchar *root_path, const gchar *id)
140 {
141 gchar *coded_id;
142 gchar *path;
143
144 if (!strcmp(id, "0")) {
145 path = g_strdup(root_path);
146 } else {
147 coded_id = prv_id_to_object_name(id);
148 path = g_strdup_printf("%s/%s", root_path, coded_id);
149 g_free(coded_id);
150 }
151
152 return path;
153 }
0 /*
1 * dLeyna
2 *
3 * Copyright (C) 2012-2013 Intel Corporation. All rights reserved.
4 *
5 * This program is free software; you can redistribute it and/or modify it
6 * under the terms and conditions of the GNU Lesser General Public License,
7 * version 2.1, as published by the Free Software Foundation.
8 *
9 * This program is distributed in the hope it will be useful, but WITHOUT
10 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
11 * FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License
12 * for more details.
13 *
14 * You should have received a copy of the GNU Lesser General Public License
15 * along with this program; if not, write to the Free Software Foundation, Inc.,
16 * 51 Franklin St - Fifth Floor, Boston, MA 02110-1301 USA.
17 *
18 * Mark Ryan <mark.d.ryan@intel.com>
19 *
20 */
21
22 #ifndef DLS_PATH_H__
23 #define DLS_PATH_H__
24
25 #include <glib.h>
26
27 gboolean dls_path_get_non_root_id(const gchar *object_path,
28 const gchar **slash_before_id);
29
30 gboolean dls_path_get_path_and_id(const gchar *object_path, gchar **root_path,
31 gchar **id, GError **error);
32
33 gchar *dls_path_from_id(const gchar *root_path, const gchar *id);
34
35 #endif /* DLS_PATH_H__ */
0 /*
1 * dLeyna
2 *
3 * Copyright (C) 2012-2013 Intel Corporation. All rights reserved.
4 *
5 * This program is free software; you can redistribute it and/or modify it
6 * under the terms and conditions of the GNU Lesser General Public License,
7 * version 2.1, as published by the Free Software Foundation.
8 *
9 * This program is distributed in the hope it will be useful, but WITHOUT
10 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
11 * FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License
12 * for more details.
13 *
14 * You should have received a copy of the GNU Lesser General Public License
15 * along with this program; if not, write to the Free Software Foundation, Inc.,
16 * 51 Franklin St - Fifth Floor, Boston, MA 02110-1301 USA.
17 *
18 * Mark Ryan <mark.d.ryan@intel.com>
19 *
20 */
21
22 #include <string.h>
23 #include <libgupnp-av/gupnp-didl-lite-contributor.h>
24
25 #include <libdleyna/core/log.h>
26
27 #include "device.h"
28 #include "interface.h"
29 #include "path.h"
30 #include "props.h"
31
32 static const gchar gUPnPContainer[] = "object.container";
33 static const gchar gUPnPAlbum[] = "object.container.album";
34 static const gchar gUPnPPerson[] = "object.container.person";
35 static const gchar gUPnPGenre[] = "object.container.genre";
36 static const gchar gUPnPAudioItem[] = "object.item.audioItem";
37 static const gchar gUPnPVideoItem[] = "object.item.videoItem";
38 static const gchar gUPnPImageItem[] = "object.item.imageItem";
39 static const gchar gUPnPPlaylistItem[] = "object.item.playlistItem";
40 static const gchar gUPnPItem[] = "object.item";
41
42 static const unsigned int gUPnPContainerLen =
43 (sizeof(gUPnPContainer) / sizeof(gchar)) - 1;
44 static const unsigned int gUPnPAlbumLen =
45 (sizeof(gUPnPAlbum) / sizeof(gchar)) - 1;
46 static const unsigned int gUPnPPersonLen =
47 (sizeof(gUPnPPerson) / sizeof(gchar)) - 1;
48 static const unsigned int gUPnPGenreLen =
49 (sizeof(gUPnPGenre) / sizeof(gchar)) - 1;
50 static const unsigned int gUPnPAudioItemLen =
51 (sizeof(gUPnPAudioItem) / sizeof(gchar)) - 1;
52 static const unsigned int gUPnPVideoItemLen =
53 (sizeof(gUPnPVideoItem) / sizeof(gchar)) - 1;
54 static const unsigned int gUPnPImageItemLen =
55 (sizeof(gUPnPImageItem) / sizeof(gchar)) - 1;
56 static const unsigned int gUPnPPlaylistItemLen =
57 (sizeof(gUPnPPlaylistItem) / sizeof(gchar)) - 1;
58 static const unsigned int gUPnPItemLen =
59 (sizeof(gUPnPItem) / sizeof(gchar)) - 1;
60
61 static const gchar gUPnPPhotoAlbum[] = "object.container.album.photoAlbum";
62 static const gchar gUPnPMusicAlbum[] = "object.container.album.musicAlbum";
63 static const gchar gUPnPMusicArtist[] = "object.container.person.musicArtist";
64 static const gchar gUPnPMovieGenre[] = "object.container.genre.movieGenre";
65 static const gchar gUPnPMusicGenre[] = "object.container.genre.musicGenre";
66 static const gchar gUPnPMusicTrack[] = "object.item.audioItem.musicTrack";
67 static const gchar gUPnPAudioBroadcast[] =
68 "object.item.audioItem.audioBroadcast";
69 static const gchar gUPnPAudioBook[] = "object.item.audioItem.audioBook";
70 static const gchar gUPnPMovie[] = "object.item.videoItem.movie";
71 static const gchar gUPnPMusicVideoClip[] =
72 "object.item.videoItem.musicVideoClip";
73 static const gchar gUPnPVideoBroadcast[] =
74 "object.item.videoItem.videoBroadcast";
75 static const gchar gUPnPPhoto[] = "object.item.imageItem.photo";
76
77 static const gchar gMediaSpec2Container[] = "container";
78 static const gchar gMediaSpec2Album[] = "album";
79 static const gchar gMediaSpec2AlbumPhoto[] = "album.photo";
80 static const gchar gMediaSpec2AlbumMusic[] = "album.music";
81 static const gchar gMediaSpec2Person[] = "person";
82 static const gchar gMediaSpec2PersonMusicArtist[] = "person.musicartist";
83 static const gchar gMediaSpec2Genre[] = "genre";
84 static const gchar gMediaSpec2GenreMovie[] = "genre.movie";
85 static const gchar gMediaSpec2GenreMusic[] = "genre.music";
86 static const gchar gMediaSpec2AudioMusic[] = "audio.music";
87 static const gchar gMediaSpec2AudioBroadcast[] = "audio.broadcast";
88 static const gchar gMediaSpec2AudioBook[] = "audio.book";
89 static const gchar gMediaSpec2Audio[] = "audio";
90 static const gchar gMediaSpec2VideoMovie[] = "video.movie";
91 static const gchar gMediaSpec2VideoMusicClip[] = "video.musicclip";
92 static const gchar gMediaSpec2VideoBroadcast[] = "video.broadcast";
93 static const gchar gMediaSpec2Video[] = "video";
94 static const gchar gMediaSpec2ImagePhoto[] = "image.photo";
95 static const gchar gMediaSpec2Image[] = "image";
96 static const gchar gMediaSpec2Playlist[] = "playlist";
97 static const gchar gMediaSpec2Item[] = "item";
98
99 static dls_prop_map_t *prv_prop_map_new(const gchar *prop_name,
100 dls_upnp_prop_mask type,
101 gboolean filter,
102 gboolean searchable,
103 gboolean updateable)
104 {
105 dls_prop_map_t *retval = g_new(dls_prop_map_t, 1);
106 retval->upnp_prop_name = prop_name;
107 retval->type = type;
108 retval->filter = filter;
109 retval->searchable = searchable;
110 retval->updateable = updateable;
111 return retval;
112 }
113
114 void dls_prop_maps_new(GHashTable **property_map, GHashTable **filter_map)
115 {
116 dls_prop_map_t *prop_t;
117 GHashTable *p_map;
118 GHashTable *f_map;
119
120 p_map = g_hash_table_new_full(g_str_hash, g_str_equal, NULL, NULL);
121 f_map = g_hash_table_new_full(g_str_hash, g_str_equal, NULL, g_free);
122
123 /* @childCount */
124 prop_t = prv_prop_map_new("@childCount",
125 DLS_UPNP_MASK_PROP_CHILD_COUNT,
126 TRUE, TRUE, FALSE);
127 g_hash_table_insert(f_map, DLS_INTERFACE_PROP_CHILD_COUNT, prop_t);
128 g_hash_table_insert(p_map, "@childCount",
129 DLS_INTERFACE_PROP_CHILD_COUNT);
130
131 /* @id */
132 prop_t = prv_prop_map_new("@id",
133 DLS_UPNP_MASK_PROP_PATH,
134 FALSE, TRUE, FALSE);
135 g_hash_table_insert(f_map, DLS_INTERFACE_PROP_PATH, prop_t);
136 g_hash_table_insert(p_map, "@id", DLS_INTERFACE_PROP_PATH);
137
138 /* @parentID */
139 prop_t = prv_prop_map_new("@parentID",
140 DLS_UPNP_MASK_PROP_PARENT,
141 FALSE, TRUE, FALSE);
142 g_hash_table_insert(f_map, DLS_INTERFACE_PROP_PARENT, prop_t);
143 g_hash_table_insert(p_map, "@parentID", DLS_INTERFACE_PROP_PARENT);
144
145 /* @refID */
146 prop_t = prv_prop_map_new("@refID",
147 DLS_UPNP_MASK_PROP_REFPATH,
148 TRUE, TRUE, FALSE);
149 g_hash_table_insert(f_map, DLS_INTERFACE_PROP_REFPATH, prop_t);
150 g_hash_table_insert(p_map, "@refID", DLS_INTERFACE_PROP_REFPATH);
151
152 /* @restricted */
153 prop_t = prv_prop_map_new("@restricted",
154 DLS_UPNP_MASK_PROP_RESTRICTED,
155 TRUE, TRUE, FALSE);
156 g_hash_table_insert(f_map, DLS_INTERFACE_PROP_RESTRICTED, prop_t);
157 g_hash_table_insert(p_map, "@restricted",
158 DLS_INTERFACE_PROP_RESTRICTED);
159
160 /* @searchable */
161 prop_t = prv_prop_map_new("@searchable",
162 DLS_UPNP_MASK_PROP_SEARCHABLE,
163 TRUE, TRUE, FALSE);
164 g_hash_table_insert(f_map, DLS_INTERFACE_PROP_SEARCHABLE, prop_t);
165 g_hash_table_insert(p_map, "@searchable",
166 DLS_INTERFACE_PROP_SEARCHABLE);
167
168 /* dc:creator */
169 prop_t = prv_prop_map_new("dc:creator",
170 DLS_UPNP_MASK_PROP_CREATOR,
171 TRUE, TRUE, FALSE);
172 g_hash_table_insert(f_map, DLS_INTERFACE_PROP_CREATOR, prop_t);
173 g_hash_table_insert(p_map, "dc:creator", DLS_INTERFACE_PROP_CREATOR);
174
175 /* dc:date */
176 prop_t = prv_prop_map_new("dc:date",
177 DLS_UPNP_MASK_PROP_DATE,
178 TRUE, TRUE, TRUE);
179 g_hash_table_insert(f_map, DLS_INTERFACE_PROP_DATE, prop_t);
180 g_hash_table_insert(p_map, "dc:date", DLS_INTERFACE_PROP_DATE);
181
182 /* dc:title */
183 prop_t = prv_prop_map_new("dc:title",
184 DLS_UPNP_MASK_PROP_DISPLAY_NAME,
185 FALSE, TRUE, TRUE);
186 g_hash_table_insert(f_map, DLS_INTERFACE_PROP_DISPLAY_NAME, prop_t);
187 g_hash_table_insert(p_map, "dc:title", DLS_INTERFACE_PROP_DISPLAY_NAME);
188
189 /* dlna:dlnaManaged */
190 prop_t = prv_prop_map_new("dlna:dlnaManaged",
191 DLS_UPNP_MASK_PROP_DLNA_MANAGED,
192 TRUE, FALSE, FALSE);
193 g_hash_table_insert(f_map, DLS_INTERFACE_PROP_DLNA_MANAGED, prop_t);
194 g_hash_table_insert(p_map, "dlna:dlnaManaged",
195 DLS_INTERFACE_PROP_DLNA_MANAGED);
196
197 /* res */
198 /* res - RES */
199 prop_t = prv_prop_map_new("res",
200 DLS_UPNP_MASK_PROP_RESOURCES,
201 TRUE, FALSE, FALSE);
202 g_hash_table_insert(f_map, DLS_INTERFACE_PROP_RESOURCES, prop_t);
203
204 /* res - URL */
205 prop_t = prv_prop_map_new("res",
206 DLS_UPNP_MASK_PROP_URL,
207 TRUE, FALSE, FALSE);
208 g_hash_table_insert(f_map, DLS_INTERFACE_PROP_URL, prop_t);
209
210 /* res - URLS */
211 prop_t = prv_prop_map_new("res",
212 DLS_UPNP_MASK_PROP_URLS,
213 TRUE, FALSE, FALSE);
214 g_hash_table_insert(f_map, DLS_INTERFACE_PROP_URLS, prop_t);
215
216 /* res@bitrate */
217 prop_t = prv_prop_map_new("res@bitrate",
218 DLS_UPNP_MASK_PROP_BITRATE,
219 TRUE, TRUE, FALSE);
220 g_hash_table_insert(f_map, DLS_INTERFACE_PROP_BITRATE, prop_t);
221 g_hash_table_insert(p_map, "res@bitrate", DLS_INTERFACE_PROP_BITRATE);
222
223 /* res@bitsPerSample */
224 prop_t = prv_prop_map_new("res@bitsPerSample",
225 DLS_UPNP_MASK_PROP_BITS_PER_SAMPLE,
226 TRUE, TRUE, FALSE);
227 g_hash_table_insert(f_map, DLS_INTERFACE_PROP_BITS_PER_SAMPLE, prop_t);
228 g_hash_table_insert(p_map, "res@bitsPerSample",
229 DLS_INTERFACE_PROP_BITS_PER_SAMPLE);
230
231 /* res@colorDepth */
232 prop_t = prv_prop_map_new("res@colorDepth",
233 DLS_UPNP_MASK_PROP_COLOR_DEPTH,
234 TRUE, TRUE, FALSE);
235 g_hash_table_insert(f_map, DLS_INTERFACE_PROP_COLOR_DEPTH, prop_t);
236 g_hash_table_insert(p_map, "res@colorDepth",
237 DLS_INTERFACE_PROP_COLOR_DEPTH);
238
239 /* res@duration */
240 prop_t = prv_prop_map_new("res@duration",
241 DLS_UPNP_MASK_PROP_DURATION,
242 TRUE, TRUE, FALSE);
243 g_hash_table_insert(f_map, DLS_INTERFACE_PROP_DURATION, prop_t);
244 g_hash_table_insert(p_map, "res@duration",
245 DLS_INTERFACE_PROP_DURATION);
246
247 /* res@protocolInfo */
248 /* res@protocolInfo - DLNA PROFILE*/
249 prop_t = prv_prop_map_new("res@protocolInfo",
250 DLS_UPNP_MASK_PROP_DLNA_PROFILE,
251 TRUE, FALSE, FALSE);
252 g_hash_table_insert(f_map, DLS_INTERFACE_PROP_DLNA_PROFILE, prop_t);
253
254 /* res@protocolInfo - MIME TYPES*/
255 prop_t = prv_prop_map_new("res@protocolInfo",
256 DLS_UPNP_MASK_PROP_MIME_TYPE,
257 TRUE, FALSE, FALSE);
258 g_hash_table_insert(f_map, DLS_INTERFACE_PROP_MIME_TYPE, prop_t);
259
260 /* res@resolution */
261 /* res@resolution - HEIGH */
262 prop_t = prv_prop_map_new("res@resolution",
263 DLS_UPNP_MASK_PROP_HEIGHT,
264 TRUE, FALSE, FALSE);
265 g_hash_table_insert(f_map, DLS_INTERFACE_PROP_HEIGHT, prop_t);
266
267 /* res@resolution - WIDTH */
268 prop_t = prv_prop_map_new("res@resolution",
269 DLS_UPNP_MASK_PROP_WIDTH,
270 TRUE, FALSE, FALSE);
271 g_hash_table_insert(f_map, DLS_INTERFACE_PROP_WIDTH, prop_t);
272
273 /* res@sampleFrequency */
274 prop_t = prv_prop_map_new("res@sampleFrequency",
275 DLS_UPNP_MASK_PROP_SAMPLE_RATE,
276 TRUE, TRUE, FALSE);
277 g_hash_table_insert(f_map, DLS_INTERFACE_PROP_SAMPLE_RATE, prop_t);
278 g_hash_table_insert(p_map, "res@sampleFrequency",
279 DLS_INTERFACE_PROP_SAMPLE_RATE);
280
281 /* res@size */
282 prop_t = prv_prop_map_new("res@size",
283 DLS_UPNP_MASK_PROP_SIZE,
284 TRUE, TRUE, FALSE);
285 g_hash_table_insert(f_map, DLS_INTERFACE_PROP_SIZE, prop_t);
286 g_hash_table_insert(p_map, "res@size", DLS_INTERFACE_PROP_SIZE);
287
288 /* res@updateCount */
289 prop_t = prv_prop_map_new("res@updateCount",
290 DLS_UPNP_MASK_PROP_UPDATE_COUNT,
291 TRUE, TRUE, FALSE);
292 g_hash_table_insert(f_map, DLS_INTERFACE_PROP_UPDATE_COUNT, prop_t);
293 g_hash_table_insert(p_map, "res@updateCount",
294 DLS_INTERFACE_PROP_UPDATE_COUNT);
295
296 /* upnp:album */
297 prop_t = prv_prop_map_new("upnp:album",
298 DLS_UPNP_MASK_PROP_ALBUM,
299 TRUE, TRUE, TRUE);
300 g_hash_table_insert(f_map, DLS_INTERFACE_PROP_ALBUM, prop_t);
301 g_hash_table_insert(p_map, "upnp:album", DLS_INTERFACE_PROP_ALBUM);
302
303 /* upnp:albumArtURI */
304 prop_t = prv_prop_map_new("upnp:albumArtURI",
305 DLS_UPNP_MASK_PROP_ALBUM_ART_URL,
306 TRUE, TRUE, FALSE);
307 g_hash_table_insert(f_map, DLS_INTERFACE_PROP_ALBUM_ART_URL, prop_t);
308 g_hash_table_insert(p_map, "upnp:albumArtURI",
309 DLS_INTERFACE_PROP_ALBUM_ART_URL);
310
311 /* upnp:artist */
312 /* upnp:artist - ARTIST*/
313 prop_t = prv_prop_map_new("upnp:artist",
314 DLS_UPNP_MASK_PROP_ARTIST,
315 TRUE, TRUE, FALSE);
316 g_hash_table_insert(f_map, DLS_INTERFACE_PROP_ARTIST, prop_t);
317 g_hash_table_insert(p_map, "upnp:artist", DLS_INTERFACE_PROP_ARTIST);
318
319 /* upnp:artist - ARTISTS*/
320 prop_t = prv_prop_map_new("upnp:artist",
321 DLS_UPNP_MASK_PROP_ARTISTS,
322 TRUE, FALSE, TRUE);
323 g_hash_table_insert(f_map, DLS_INTERFACE_PROP_ARTISTS, prop_t);
324
325 /* upnp:class */
326 prop_t = prv_prop_map_new("upnp:class",
327 DLS_UPNP_MASK_PROP_TYPE,
328 FALSE, TRUE, TRUE);
329 g_hash_table_insert(f_map, DLS_INTERFACE_PROP_TYPE, prop_t);
330 g_hash_table_insert(p_map, "upnp:class", DLS_INTERFACE_PROP_TYPE);
331
332 /* upnp:containerUpdateID */
333 prop_t = prv_prop_map_new("upnp:containerUpdateID",
334 DLS_UPNP_MASK_PROP_CONTAINER_UPDATE_ID,
335 TRUE, TRUE, FALSE);
336 g_hash_table_insert(f_map, DLS_INTERFACE_PROP_CONTAINER_UPDATE_ID,
337 prop_t);
338 g_hash_table_insert(p_map, "upnp:containerUpdateID",
339 DLS_INTERFACE_PROP_CONTAINER_UPDATE_ID);
340
341 /* upnp:createClass */
342 prop_t = prv_prop_map_new("upnp:createClass",
343 DLS_UPNP_MASK_PROP_CREATE_CLASSES,
344 TRUE, FALSE, FALSE);
345 g_hash_table_insert(f_map, DLS_INTERFACE_PROP_CREATE_CLASSES, prop_t);
346
347 /* upnp:genre */
348 prop_t = prv_prop_map_new("upnp:genre",
349 DLS_UPNP_MASK_PROP_GENRE,
350 TRUE, TRUE, FALSE);
351 g_hash_table_insert(f_map, DLS_INTERFACE_PROP_GENRE, prop_t);
352 g_hash_table_insert(p_map, "upnp:genre", DLS_INTERFACE_PROP_GENRE);
353
354 /* upnp:objectUpdateID */
355 prop_t = prv_prop_map_new("upnp:objectUpdateID",
356 DLS_UPNP_MASK_PROP_OBJECT_UPDATE_ID,
357 TRUE, TRUE, FALSE);
358 g_hash_table_insert(f_map, DLS_INTERFACE_PROP_OBJECT_UPDATE_ID, prop_t);
359 g_hash_table_insert(p_map, "upnp:objectUpdateID",
360 DLS_INTERFACE_PROP_OBJECT_UPDATE_ID);
361
362 /* upnp:originalTrackNumber */
363 prop_t = prv_prop_map_new("upnp:originalTrackNumber",
364 DLS_UPNP_MASK_PROP_TRACK_NUMBER,
365 TRUE, TRUE, TRUE);
366 g_hash_table_insert(f_map, DLS_INTERFACE_PROP_TRACK_NUMBER, prop_t);
367 g_hash_table_insert(p_map, "upnp:originalTrackNumber",
368 DLS_INTERFACE_PROP_TRACK_NUMBER);
369
370 /* upnp:totalDeletedChildCount */
371 prop_t = prv_prop_map_new("upnp:totalDeletedChildCount",
372 DLS_UPNP_MASK_PROP_TOTAL_DELETED_CHILD_COUNT,
373 TRUE, TRUE, FALSE);
374 g_hash_table_insert(f_map, DLS_INTERFACE_PROP_TOTAL_DELETED_CHILD_COUNT,
375 prop_t);
376 g_hash_table_insert(p_map, "upnp:totalDeletedChildCount",
377 DLS_INTERFACE_PROP_TOTAL_DELETED_CHILD_COUNT);
378
379 *filter_map = f_map;
380 *property_map = p_map;
381 }
382
383 static gchar *prv_compute_upnp_filter(GHashTable *upnp_props)
384 {
385 gpointer key;
386 GString *str;
387 GHashTableIter iter;
388
389 str = g_string_new("");
390 g_hash_table_iter_init(&iter, upnp_props);
391 if (g_hash_table_iter_next(&iter, &key, NULL)) {
392 g_string_append(str, (const gchar *)key);
393 while (g_hash_table_iter_next(&iter, &key, NULL)) {
394 g_string_append(str, ",");
395 g_string_append(str, (const gchar *)key);
396 }
397 }
398
399 return g_string_free(str, FALSE);
400 }
401
402 static dls_upnp_prop_mask prv_parse_filter_list(GHashTable *filter_map,
403 GVariant *filter,
404 gchar **upnp_filter)
405 {
406 GVariantIter viter;
407 const gchar *prop;
408 dls_prop_map_t *prop_map;
409 GHashTable *upnp_props;
410 dls_upnp_prop_mask mask = 0;
411
412 upnp_props = g_hash_table_new_full(g_str_hash, g_str_equal,
413 NULL, NULL);
414 (void) g_variant_iter_init(&viter, filter);
415
416 while (g_variant_iter_next(&viter, "&s", &prop)) {
417 prop_map = g_hash_table_lookup(filter_map, prop);
418 if (!prop_map)
419 continue;
420
421 mask |= prop_map->type;
422
423 if (!prop_map->filter)
424 continue;
425
426 g_hash_table_insert(upnp_props,
427 (gpointer) prop_map->upnp_prop_name, NULL);
428 }
429
430 *upnp_filter = prv_compute_upnp_filter(upnp_props);
431 g_hash_table_unref(upnp_props);
432
433 return mask;
434 }
435
436 dls_upnp_prop_mask dls_props_parse_filter(GHashTable *filter_map,
437 GVariant *filter,
438 gchar **upnp_filter)
439 {
440 gchar *str;
441 gboolean parse_filter = TRUE;
442 dls_upnp_prop_mask mask;
443
444 if (g_variant_n_children(filter) == 1) {
445 g_variant_get_child(filter, 0, "&s", &str);
446 if (!strcmp(str, "*"))
447 parse_filter = FALSE;
448 }
449
450 if (parse_filter) {
451 mask = prv_parse_filter_list(filter_map, filter, upnp_filter);
452 } else {
453 mask = DLS_UPNP_MASK_ALL_PROPS;
454 *upnp_filter = g_strdup("*");
455 }
456
457 return mask;
458 }
459
460 gboolean dls_props_parse_update_filter(GHashTable *filter_map,
461 GVariant *to_add_update,
462 GVariant *to_delete,
463 dls_upnp_prop_mask *mask,
464 gchar **upnp_filter)
465 {
466 GVariantIter viter;
467 const gchar *prop;
468 GVariant *value;
469 dls_prop_map_t *prop_map;
470 GHashTable *upnp_props;
471 gboolean retval = FALSE;
472
473 *mask = 0;
474
475 upnp_props = g_hash_table_new_full(g_str_hash, g_str_equal,
476 NULL, NULL);
477
478 (void) g_variant_iter_init(&viter, to_add_update);
479
480 while (g_variant_iter_next(&viter, "{&sv}", &prop, &value)) {
481 DLEYNA_LOG_DEBUG("to_add_update = %s", prop);
482
483 prop_map = g_hash_table_lookup(filter_map, prop);
484
485 if ((!prop_map) || (!prop_map->updateable))
486 goto on_error;
487
488 *mask |= prop_map->type;
489
490 if (!prop_map->filter)
491 continue;
492
493 g_hash_table_insert(upnp_props,
494 (gpointer) prop_map->upnp_prop_name, NULL);
495 }
496
497 (void) g_variant_iter_init(&viter, to_delete);
498
499 while (g_variant_iter_next(&viter, "&s", &prop)) {
500 DLEYNA_LOG_DEBUG("to_delete = %s", prop);
501
502 prop_map = g_hash_table_lookup(filter_map, prop);
503
504 if ((!prop_map) || (!prop_map->updateable) ||
505 (*mask & prop_map->type) != 0)
506 goto on_error;
507
508 *mask |= prop_map->type;
509
510 if (!prop_map->filter)
511 continue;
512
513 g_hash_table_insert(upnp_props,
514 (gpointer) prop_map->upnp_prop_name, NULL);
515 }
516
517 *upnp_filter = prv_compute_upnp_filter(upnp_props);
518
519 retval = TRUE;
520
521 on_error:
522
523 g_hash_table_unref(upnp_props);
524
525 return retval;
526 }
527
528 static void prv_add_string_prop(GVariantBuilder *vb, const gchar *key,
529 const gchar *value)
530 {
531 if (value) {
532 DLEYNA_LOG_DEBUG("Prop %s = %s", key, value);
533
534 g_variant_builder_add(vb, "{sv}", key,
535 g_variant_new_string(value));
536 }
537 }
538
539 static void prv_add_strv_prop(GVariantBuilder *vb, const gchar *key,
540 const gchar **value, unsigned int len)
541 {
542 if (len > 0)
543 g_variant_builder_add(vb, "{sv}", key,
544 g_variant_new_strv(value, len));
545 }
546
547 static void prv_add_path_prop(GVariantBuilder *vb, const gchar *key,
548 const gchar *value)
549 {
550 if (value) {
551 DLEYNA_LOG_DEBUG("Prop %s = %s", key, value);
552
553 g_variant_builder_add(vb, "{sv}", key,
554 g_variant_new_object_path(value));
555 }
556 }
557
558 static void prv_add_uint_prop(GVariantBuilder *vb, const gchar *key,
559 unsigned int value)
560 {
561 DLEYNA_LOG_DEBUG("Prop %s = %u", key, value);
562
563 g_variant_builder_add(vb, "{sv}", key, g_variant_new_uint32(value));
564 }
565
566 static void prv_add_int_prop(GVariantBuilder *vb, const gchar *key,
567 int value)
568 {
569 if (value != -1)
570 g_variant_builder_add(vb, "{sv}", key,
571 g_variant_new_int32(value));
572 }
573
574 static void prv_add_variant_prop(GVariantBuilder *vb, const gchar *key,
575 GVariant *prop)
576 {
577 if (prop)
578 g_variant_builder_add(vb, "{sv}", key, prop);
579 }
580
581 void dls_props_add_child_count(GVariantBuilder *item_vb, gint value)
582 {
583 prv_add_int_prop(item_vb, DLS_INTERFACE_PROP_CHILD_COUNT, value);
584 }
585
586 static void prv_add_bool_prop(GVariantBuilder *vb, const gchar *key,
587 gboolean value)
588 {
589 DLEYNA_LOG_DEBUG("Prop %s = %u", key, value);
590
591 g_variant_builder_add(vb, "{sv}", key, g_variant_new_boolean(value));
592 }
593
594 static void prv_add_int64_prop(GVariantBuilder *vb, const gchar *key,
595 gint64 value)
596 {
597 if (value != -1) {
598 DLEYNA_LOG_DEBUG("Prop %s = %"G_GINT64_FORMAT, key, value);
599
600 g_variant_builder_add(vb, "{sv}", key,
601 g_variant_new_int64(value));
602 }
603 }
604
605 static void prv_add_list_dlna_str(gpointer data, gpointer user_data)
606 {
607 GVariantBuilder *vb = (GVariantBuilder *)user_data;
608 gchar *cap_str = (gchar *)data;
609 gchar *str;
610 int value = 0;
611
612 if (g_str_has_prefix(cap_str, "srs-rt-retention-period-")) {
613 str = cap_str + strlen("srs-rt-retention-period-");
614 cap_str = "srs-rt-retention-period";
615
616 if (*str) {
617 if (!g_strcmp0(str, "infinity"))
618 value = -1;
619 else
620 value = atoi(str);
621 }
622 }
623
624 prv_add_uint_prop(vb, cap_str, value);
625 }
626
627 static GVariant *prv_add_list_dlna_prop(GList *list)
628 {
629 GVariantBuilder vb;
630
631 g_variant_builder_init(&vb, G_VARIANT_TYPE("a{sv}"));
632
633 g_list_foreach(list, prv_add_list_dlna_str, &vb);
634
635 return g_variant_builder_end(&vb);
636 }
637
638 static void prv_add_list_artists_str(gpointer data, gpointer user_data)
639 {
640 GVariantBuilder *vb = (GVariantBuilder *)user_data;
641 GUPnPDIDLLiteContributor *contributor = data;
642 const char *str;
643
644 str = gupnp_didl_lite_contributor_get_name(contributor);
645 g_variant_builder_add(vb, "s", str);
646 }
647
648 static GVariant *prv_get_artists_prop(GList *list)
649 {
650 GVariantBuilder vb;
651
652 g_variant_builder_init(&vb, G_VARIANT_TYPE("as"));
653 g_list_foreach(list, prv_add_list_artists_str, &vb);
654
655 return g_variant_builder_end(&vb);
656 }
657
658 void dls_props_add_device(GUPnPDeviceInfo *proxy,
659 const dls_device_t *device,
660 GVariantBuilder *vb)
661 {
662 gchar *str;
663 GList *list;
664 GVariant *dlna_caps;
665
666 prv_add_string_prop(vb, DLS_INTERFACE_PROP_LOCATION,
667 gupnp_device_info_get_location(proxy));
668
669 prv_add_string_prop(vb, DLS_INTERFACE_PROP_UDN,
670 gupnp_device_info_get_udn(proxy));
671
672 prv_add_string_prop(vb, DLS_INTERFACE_PROP_DEVICE_TYPE,
673 gupnp_device_info_get_device_type(proxy));
674
675 str = gupnp_device_info_get_friendly_name(proxy);
676 prv_add_string_prop(vb, DLS_INTERFACE_PROP_FRIENDLY_NAME, str);
677 g_free(str);
678
679 str = gupnp_device_info_get_manufacturer(proxy);
680 prv_add_string_prop(vb, DLS_INTERFACE_PROP_MANUFACTURER, str);
681 g_free(str);
682
683 str = gupnp_device_info_get_manufacturer_url(proxy);
684 prv_add_string_prop(vb, DLS_INTERFACE_PROP_MANUFACTURER_URL, str);
685 g_free(str);
686
687 str = gupnp_device_info_get_model_description(proxy);
688 prv_add_string_prop(vb, DLS_INTERFACE_PROP_MODEL_DESCRIPTION, str);
689 g_free(str);
690
691 str = gupnp_device_info_get_model_name(proxy);
692 prv_add_string_prop(vb, DLS_INTERFACE_PROP_MODEL_NAME, str);
693 g_free(str);
694
695 str = gupnp_device_info_get_model_number(proxy);
696 prv_add_string_prop(vb, DLS_INTERFACE_PROP_MODEL_NUMBER, str);
697 g_free(str);
698
699 str = gupnp_device_info_get_model_url(proxy);
700 prv_add_string_prop(vb, DLS_INTERFACE_PROP_MODEL_URL, str);
701 g_free(str);
702
703 str = gupnp_device_info_get_serial_number(proxy);
704 prv_add_string_prop(vb, DLS_INTERFACE_PROP_SERIAL_NUMBER, str);
705 g_free(str);
706
707 str = gupnp_device_info_get_presentation_url(proxy);
708 prv_add_string_prop(vb, DLS_INTERFACE_PROP_PRESENTATION_URL, str);
709 g_free(str);
710
711 str = gupnp_device_info_get_icon_url(proxy, NULL, -1, -1, -1, FALSE,
712 NULL, NULL, NULL, NULL);
713 prv_add_string_prop(vb, DLS_INTERFACE_PROP_ICON_URL, str);
714 g_free(str);
715
716 list = gupnp_device_info_list_dlna_capabilities(proxy);
717 if (list != NULL) {
718 dlna_caps = prv_add_list_dlna_prop(list);
719 g_variant_builder_add(vb, "{sv}",
720 DLS_INTERFACE_PROP_SV_DLNA_CAPABILITIES,
721 dlna_caps);
722 g_list_free_full(list, g_free);
723 }
724
725 if (device->search_caps != NULL)
726 g_variant_builder_add(vb, "{sv}",
727 DLS_INTERFACE_PROP_SV_SEARCH_CAPABILITIES,
728 device->search_caps);
729
730 if (device->sort_caps != NULL)
731 g_variant_builder_add(vb, "{sv}",
732 DLS_INTERFACE_PROP_SV_SORT_CAPABILITIES,
733 device->sort_caps);
734
735 if (device->sort_ext_caps != NULL)
736 g_variant_builder_add(
737 vb, "{sv}",
738 DLS_INTERFACE_PROP_SV_SORT_EXT_CAPABILITIES,
739 device->sort_ext_caps);
740
741 if (device->feature_list != NULL)
742 g_variant_builder_add(vb, "{sv}",
743 DLS_INTERFACE_PROP_SV_FEATURE_LIST,
744 device->feature_list);
745 }
746
747 GVariant *dls_props_get_device_prop(GUPnPDeviceInfo *proxy,
748 const dls_device_t *device,
749 const gchar *prop)
750 {
751 GVariant *dlna_caps = NULL;
752 GVariant *retval = NULL;
753 const gchar *str = NULL;
754 gchar *copy = NULL;
755 GList *list;
756
757 if (!strcmp(DLS_INTERFACE_PROP_LOCATION, prop)) {
758 str = gupnp_device_info_get_location(proxy);
759 } else if (!strcmp(DLS_INTERFACE_PROP_UDN, prop)) {
760 str = gupnp_device_info_get_udn(proxy);
761 } else if (!strcmp(DLS_INTERFACE_PROP_DEVICE_TYPE, prop)) {
762 str = gupnp_device_info_get_device_type(proxy);
763 } else if (!strcmp(DLS_INTERFACE_PROP_FRIENDLY_NAME, prop)) {
764 copy = gupnp_device_info_get_friendly_name(proxy);
765 str = copy;
766 } else if (!strcmp(DLS_INTERFACE_PROP_MANUFACTURER, prop)) {
767 copy = gupnp_device_info_get_manufacturer(proxy);
768 str = copy;
769 } else if (!strcmp(DLS_INTERFACE_PROP_MANUFACTURER_URL, prop)) {
770 copy = gupnp_device_info_get_manufacturer_url(proxy);
771 str = copy;
772 } else if (!strcmp(DLS_INTERFACE_PROP_MODEL_DESCRIPTION, prop)) {
773 copy = gupnp_device_info_get_model_description(proxy);
774 str = copy;
775 } else if (!strcmp(DLS_INTERFACE_PROP_MODEL_NAME, prop)) {
776 copy = gupnp_device_info_get_model_name(proxy);
777 str = copy;
778 } else if (!strcmp(DLS_INTERFACE_PROP_MODEL_NUMBER, prop)) {
779 copy = gupnp_device_info_get_model_number(proxy);
780 str = copy;
781 } else if (!strcmp(DLS_INTERFACE_PROP_MODEL_URL, prop)) {
782 copy = gupnp_device_info_get_model_url(proxy);
783 str = copy;
784 } else if (!strcmp(DLS_INTERFACE_PROP_SERIAL_NUMBER, prop)) {
785 copy = gupnp_device_info_get_serial_number(proxy);
786 str = copy;
787 } else if (!strcmp(DLS_INTERFACE_PROP_PRESENTATION_URL, prop)) {
788 copy = gupnp_device_info_get_presentation_url(proxy);
789 str = copy;
790 } else if (!strcmp(DLS_INTERFACE_PROP_ICON_URL, prop)) {
791 copy = gupnp_device_info_get_icon_url(proxy, NULL,
792 -1, -1, -1, FALSE,
793 NULL, NULL, NULL, NULL);
794 str = copy;
795 } else if (!strcmp(DLS_INTERFACE_PROP_SV_DLNA_CAPABILITIES, prop)) {
796 list = gupnp_device_info_list_dlna_capabilities(proxy);
797 if (list != NULL) {
798 dlna_caps = prv_add_list_dlna_prop(list);
799 g_list_free_full(list, g_free);
800 retval = g_variant_ref_sink(dlna_caps);
801
802 #if DLEYNA_LOG_LEVEL & DLEYNA_LOG_LEVEL_DEBUG
803 copy = g_variant_print(dlna_caps, FALSE);
804 DLEYNA_LOG_DEBUG("Prop %s = %s", prop, copy);
805 #endif
806 }
807 } else if (!strcmp(DLS_INTERFACE_PROP_SV_SEARCH_CAPABILITIES, prop)) {
808 if (device->search_caps != NULL) {
809 retval = g_variant_ref(device->search_caps);
810
811 #if DLEYNA_LOG_LEVEL & DLEYNA_LOG_LEVEL_DEBUG
812 copy = g_variant_print(device->search_caps, FALSE);
813 DLEYNA_LOG_DEBUG("Prop %s = %s", prop, copy);
814 #endif
815 }
816 } else if (!strcmp(DLS_INTERFACE_PROP_SV_SORT_CAPABILITIES, prop)) {
817 if (device->sort_caps != NULL) {
818 retval = g_variant_ref(device->sort_caps);
819
820 #if DLEYNA_LOG_LEVEL & DLEYNA_LOG_LEVEL_DEBUG
821 copy = g_variant_print(device->sort_caps, FALSE);
822 DLEYNA_LOG_DEBUG("Prop %s = %s", prop, copy);
823 #endif
824 }
825 } else if (!strcmp(DLS_INTERFACE_PROP_SV_SORT_EXT_CAPABILITIES, prop)) {
826 if (device->sort_ext_caps != NULL) {
827 retval = g_variant_ref(device->sort_ext_caps);
828
829 #if DLEYNA_LOG_LEVEL & DLEYNA_LOG_LEVEL_DEBUG
830 copy = g_variant_print(device->sort_ext_caps, FALSE);
831 DLEYNA_LOG_DEBUG("Prop %s = %s", prop, copy);
832 #endif
833 }
834 } else if (!strcmp(DLS_INTERFACE_PROP_SV_FEATURE_LIST, prop)) {
835 if (device->feature_list != NULL) {
836 retval = g_variant_ref(device->feature_list);
837
838 #if DLEYNA_LOG_LEVEL & DLEYNA_LOG_LEVEL_DEBUG
839 copy = g_variant_print(device->feature_list, FALSE);
840 DLEYNA_LOG_DEBUG("Prop %s = %s", prop, copy);
841 #endif
842 }
843 }
844
845 if (!retval) {
846 if (str) {
847 DLEYNA_LOG_DEBUG("Prop %s = %s", prop, str);
848
849 retval = g_variant_ref_sink(g_variant_new_string(str));
850 }
851 #if DLEYNA_LOG_LEVEL & DLEYNA_LOG_LEVEL_WARNING
852 else
853 DLEYNA_LOG_WARNING("Property %s not defined", prop);
854 #endif
855 }
856
857 g_free(copy);
858
859 return retval;
860 }
861
862 static GUPnPDIDLLiteResource *prv_match_resource(GUPnPDIDLLiteResource *res,
863 gchar **pi_str_array)
864 {
865 GUPnPDIDLLiteResource *retval = NULL;
866 GUPnPProtocolInfo *res_pi;
867 GUPnPProtocolInfo *pi;
868 unsigned int i;
869 gboolean match;
870
871 if (!pi_str_array) {
872 retval = res;
873 goto done;
874 }
875
876 res_pi = gupnp_didl_lite_resource_get_protocol_info(res);
877 if (!res_pi)
878 goto done;
879
880 for (i = 0; pi_str_array[i]; ++i) {
881 pi = gupnp_protocol_info_new_from_string(pi_str_array[i],
882 NULL);
883 if (!pi)
884 continue;
885 match = gupnp_protocol_info_is_compatible(pi, res_pi);
886 g_object_unref(pi);
887 if (match) {
888 retval = res;
889 break;
890 }
891 }
892 done:
893
894 return retval;
895 }
896
897 static GUPnPDIDLLiteResource *prv_get_matching_resource
898 (GUPnPDIDLLiteObject *object, const gchar *protocol_info)
899 {
900 GUPnPDIDLLiteResource *retval = NULL;
901 GUPnPDIDLLiteResource *res;
902 GList *resources;
903 GList *ptr;
904 gchar **pi_str_array = NULL;
905
906 if (protocol_info)
907 pi_str_array = g_strsplit(protocol_info, ",", 0);
908
909 resources = gupnp_didl_lite_object_get_resources(object);
910 ptr = resources;
911
912 while (ptr) {
913 res = ptr->data;
914 if (!retval) {
915 retval = prv_match_resource(res, pi_str_array);
916 if (!retval)
917 g_object_unref(res);
918 } else {
919 g_object_unref(res);
920 }
921
922 ptr = ptr->next;
923 }
924
925 g_list_free(resources);
926 if (pi_str_array)
927 g_strfreev(pi_str_array);
928
929 return retval;
930 }
931
932 static void prv_parse_resources(GVariantBuilder *item_vb,
933 GUPnPDIDLLiteResource *res,
934 dls_upnp_prop_mask filter_mask)
935 {
936 GUPnPProtocolInfo *protocol_info;
937 int int_val;
938 gint64 int64_val;
939 const char *str_val;
940 guint uint_val;
941
942 if (filter_mask & DLS_UPNP_MASK_PROP_SIZE) {
943 int64_val = gupnp_didl_lite_resource_get_size64(res);
944 prv_add_int64_prop(item_vb, DLS_INTERFACE_PROP_SIZE, int64_val);
945 }
946
947 if (filter_mask & DLS_UPNP_MASK_PROP_BITRATE) {
948 int_val = gupnp_didl_lite_resource_get_bitrate(res);
949 prv_add_int_prop(item_vb, DLS_INTERFACE_PROP_BITRATE, int_val);
950 }
951
952 if (filter_mask & DLS_UPNP_MASK_PROP_SAMPLE_RATE) {
953 int_val = gupnp_didl_lite_resource_get_sample_freq(res);
954 prv_add_int_prop(item_vb, DLS_INTERFACE_PROP_SAMPLE_RATE,
955 int_val);
956 }
957
958 if (filter_mask & DLS_UPNP_MASK_PROP_BITS_PER_SAMPLE) {
959 int_val = gupnp_didl_lite_resource_get_bits_per_sample(res);
960 prv_add_int_prop(item_vb, DLS_INTERFACE_PROP_BITS_PER_SAMPLE,
961 int_val);
962 }
963
964 if (filter_mask & DLS_UPNP_MASK_PROP_DURATION) {
965 int_val = (int) gupnp_didl_lite_resource_get_duration(res);
966 prv_add_int_prop(item_vb, DLS_INTERFACE_PROP_DURATION, int_val);
967 }
968
969 if (filter_mask & DLS_UPNP_MASK_PROP_WIDTH) {
970 int_val = (int) gupnp_didl_lite_resource_get_width(res);
971 prv_add_int_prop(item_vb, DLS_INTERFACE_PROP_WIDTH, int_val);
972 }
973
974 if (filter_mask & DLS_UPNP_MASK_PROP_HEIGHT) {
975 int_val = (int) gupnp_didl_lite_resource_get_height(res);
976 prv_add_int_prop(item_vb, DLS_INTERFACE_PROP_HEIGHT, int_val);
977 }
978
979 if (filter_mask & DLS_UPNP_MASK_PROP_COLOR_DEPTH) {
980 int_val = (int) gupnp_didl_lite_resource_get_color_depth(res);
981 prv_add_int_prop(item_vb, DLS_INTERFACE_PROP_COLOR_DEPTH,
982 int_val);
983 }
984
985 if (filter_mask & DLS_UPNP_MASK_PROP_UPDATE_COUNT) {
986 uint_val = gupnp_didl_lite_resource_get_update_count(res);
987 prv_add_uint_prop(item_vb, DLS_INTERFACE_PROP_UPDATE_COUNT,
988 uint_val);
989 }
990
991 protocol_info = gupnp_didl_lite_resource_get_protocol_info(res);
992
993 if (filter_mask & DLS_UPNP_MASK_PROP_DLNA_PROFILE) {
994 str_val = gupnp_protocol_info_get_dlna_profile(protocol_info);
995 prv_add_string_prop(item_vb, DLS_INTERFACE_PROP_DLNA_PROFILE,
996 str_val);
997 }
998
999 if (filter_mask & DLS_UPNP_MASK_PROP_MIME_TYPE) {
1000 str_val = gupnp_protocol_info_get_mime_type(protocol_info);
1001 prv_add_string_prop(item_vb, DLS_INTERFACE_PROP_MIME_TYPE,
1002 str_val);
1003 }
1004 }
1005
1006 static GVariant *prv_compute_create_classes(GUPnPDIDLLiteContainer *container)
1007 {
1008 GVariantBuilder create_classes_vb;
1009 GList *create_classes;
1010 GList *ptr;
1011 GUPnPDIDLLiteCreateClass *create_class;
1012 const char *content;
1013 gboolean inc_derived;
1014
1015 g_variant_builder_init(&create_classes_vb, G_VARIANT_TYPE("a(sb)"));
1016
1017 create_classes = gupnp_didl_lite_container_get_create_classes_full(
1018 container);
1019 ptr = create_classes;
1020 while (ptr) {
1021 create_class = ptr->data;
1022 content = gupnp_didl_lite_create_class_get_content(
1023 create_class);
1024 inc_derived = gupnp_didl_lite_create_class_get_include_derived(
1025 create_class);
1026 g_variant_builder_add(&create_classes_vb,
1027 "(sb)", content, inc_derived);
1028 g_object_unref(ptr->data);
1029 ptr = g_list_next(ptr);
1030 }
1031 g_list_free(create_classes);
1032
1033 return g_variant_builder_end(&create_classes_vb);
1034 }
1035
1036 const gchar *dls_props_media_spec_to_upnp_class(const gchar *m2spec_class)
1037 {
1038 const gchar *retval = NULL;
1039
1040 if (!strcmp(m2spec_class, gMediaSpec2AlbumPhoto))
1041 retval = gUPnPPhotoAlbum;
1042 else if (!strcmp(m2spec_class, gMediaSpec2AlbumMusic))
1043 retval = gUPnPMusicAlbum;
1044 else if (!strcmp(m2spec_class, gMediaSpec2Album))
1045 retval = gUPnPAlbum;
1046 else if (!strcmp(m2spec_class, gMediaSpec2PersonMusicArtist))
1047 retval = gUPnPMusicArtist;
1048 else if (!strcmp(m2spec_class, gMediaSpec2Person))
1049 retval = gUPnPPerson;
1050 else if (!strcmp(m2spec_class, gMediaSpec2GenreMovie))
1051 retval = gUPnPMovieGenre;
1052 else if (!strcmp(m2spec_class, gMediaSpec2GenreMusic))
1053 retval = gUPnPMusicGenre;
1054 else if (!strcmp(m2spec_class, gMediaSpec2Genre))
1055 retval = gUPnPGenre;
1056 else if (!strcmp(m2spec_class, gMediaSpec2Container))
1057 retval = gUPnPContainer;
1058 else if (!strcmp(m2spec_class, gMediaSpec2AudioMusic))
1059 retval = gUPnPMusicTrack;
1060 else if (!strcmp(m2spec_class, gMediaSpec2AudioBroadcast))
1061 retval = gUPnPAudioBroadcast;
1062 else if (!strcmp(m2spec_class, gMediaSpec2AudioBook))
1063 retval = gUPnPAudioBook;
1064 else if (!strcmp(m2spec_class, gMediaSpec2Audio))
1065 retval = gUPnPAudioItem;
1066 else if (!strcmp(m2spec_class, gMediaSpec2VideoMovie))
1067 retval = gUPnPMovie;
1068 else if (!strcmp(m2spec_class, gMediaSpec2VideoMusicClip))
1069 retval = gUPnPMusicVideoClip;
1070 else if (!strcmp(m2spec_class, gMediaSpec2VideoBroadcast))
1071 retval = gUPnPVideoBroadcast;
1072 else if (!strcmp(m2spec_class, gMediaSpec2Video))
1073 retval = gUPnPVideoItem;
1074 else if (!strcmp(m2spec_class, gMediaSpec2ImagePhoto))
1075 retval = gUPnPPhoto;
1076 else if (!strcmp(m2spec_class, gMediaSpec2Image))
1077 retval = gUPnPImageItem;
1078 else if (!strcmp(m2spec_class, gMediaSpec2Playlist))
1079 retval = gUPnPPlaylistItem;
1080 else if (!strcmp(m2spec_class, gMediaSpec2Item))
1081 retval = gUPnPItem;
1082
1083 return retval;
1084 }
1085
1086 const gchar *dls_props_upnp_class_to_media_spec(const gchar *upnp_class)
1087 {
1088 const gchar *retval = NULL;
1089 const gchar *ptr;
1090
1091 if (!strncmp(upnp_class, gUPnPAlbum, gUPnPAlbumLen)) {
1092 ptr = upnp_class + gUPnPAlbumLen;
1093 if (!strcmp(ptr, ".photoAlbum"))
1094 retval = gMediaSpec2AlbumPhoto;
1095 else if (!strcmp(ptr, ".musicAlbum"))
1096 retval = gMediaSpec2AlbumMusic;
1097 else
1098 retval = gMediaSpec2Album;
1099 } else if (!strncmp(upnp_class, gUPnPPerson, gUPnPPersonLen)) {
1100 ptr = upnp_class + gUPnPPersonLen;
1101 if (!strcmp(ptr, ".musicArtist"))
1102 retval = gMediaSpec2PersonMusicArtist;
1103 else
1104 retval = gMediaSpec2Person;
1105 } else if (!strncmp(upnp_class, gUPnPGenre, gUPnPGenreLen)) {
1106 ptr = upnp_class + gUPnPGenreLen;
1107 if (!strcmp(ptr, ".movieGenre"))
1108 retval = gMediaSpec2GenreMovie;
1109 else if (!strcmp(ptr, ".musicGenre"))
1110 retval = gMediaSpec2GenreMusic;
1111 else
1112 retval = gMediaSpec2Genre;
1113 } else if (!strncmp(upnp_class, gUPnPContainer, gUPnPContainerLen)) {
1114 ptr = upnp_class + gUPnPContainerLen;
1115 if (!*ptr || *ptr == '.')
1116 retval = gMediaSpec2Container;
1117 } else if (!strncmp(upnp_class, gUPnPAudioItem, gUPnPAudioItemLen)) {
1118 ptr = upnp_class + gUPnPAudioItemLen;
1119 if (!strcmp(ptr, ".musicTrack"))
1120 retval = gMediaSpec2AudioMusic;
1121 else if (!strcmp(ptr, ".audioBroadcast"))
1122 retval = gMediaSpec2AudioBroadcast;
1123 else if (!strcmp(ptr, ".audioBook"))
1124 retval = gMediaSpec2AudioBook;
1125 else
1126 retval = gMediaSpec2Audio;
1127 } else if (!strncmp(upnp_class, gUPnPVideoItem, gUPnPVideoItemLen)) {
1128 ptr = upnp_class + gUPnPVideoItemLen;
1129 if (!strcmp(ptr, ".movie"))
1130 retval = gMediaSpec2VideoMovie;
1131 else if (!strcmp(ptr, ".musicVideoClip"))
1132 retval = gMediaSpec2VideoMusicClip;
1133 else if (!strcmp(ptr, ".videoBroadcast"))
1134 retval = gMediaSpec2VideoBroadcast;
1135 else
1136 retval = gMediaSpec2Video;
1137 } else if (!strncmp(upnp_class, gUPnPImageItem, gUPnPImageItemLen)) {
1138 ptr = upnp_class + gUPnPImageItemLen;
1139 if (!strcmp(ptr, ".photo"))
1140 retval = gMediaSpec2ImagePhoto;
1141 else
1142 retval = gMediaSpec2Image;
1143 } else if (!strncmp(upnp_class, gUPnPPlaylistItem,
1144 gUPnPPlaylistItemLen)) {
1145 retval = gMediaSpec2Playlist;
1146 } else if (!strncmp(upnp_class, gUPnPItem, gUPnPItemLen)) {
1147 ptr = upnp_class + gUPnPItemLen;
1148 if (!*ptr || *ptr == '.')
1149 retval = gMediaSpec2Item;
1150 }
1151
1152 return retval;
1153 }
1154
1155 static GVariant *prv_props_get_dlna_managed_dict(GUPnPOCMFlags flags)
1156 {
1157 GVariantBuilder builder;
1158 gboolean managed;
1159
1160 g_variant_builder_init(&builder, G_VARIANT_TYPE("a{sb}"));
1161
1162 managed = (flags & GUPNP_OCM_FLAGS_UPLOAD);
1163 g_variant_builder_add(&builder, "{sb}", "Upload", managed);
1164
1165 managed = (flags & GUPNP_OCM_FLAGS_CREATE_CONTAINER);
1166 g_variant_builder_add(&builder, "{sb}", "CreateContainer", managed);
1167
1168 managed = (flags & GUPNP_OCM_FLAGS_DESTROYABLE);
1169 g_variant_builder_add(&builder, "{sb}", "Delete", managed);
1170
1171 managed = (flags & GUPNP_OCM_FLAGS_UPLOAD_DESTROYABLE);
1172 g_variant_builder_add(&builder, "{sb}", "UploadDelete", managed);
1173
1174 managed = (flags & GUPNP_OCM_FLAGS_CHANGE_METADATA);
1175 g_variant_builder_add(&builder, "{sb}", "ChangeMeta", managed);
1176
1177 return g_variant_builder_end(&builder);
1178 }
1179
1180 gboolean dls_props_add_object(GVariantBuilder *item_vb,
1181 GUPnPDIDLLiteObject *object,
1182 const char *root_path,
1183 const gchar *parent_path,
1184 dls_upnp_prop_mask filter_mask)
1185 {
1186 gchar *path = NULL;
1187 const char *id;
1188 const char *title;
1189 const char *creator;
1190 const char *upnp_class;
1191 const char *media_spec_type;
1192 gboolean retval = FALSE;
1193 gboolean rest;
1194 GUPnPOCMFlags flags;
1195 guint uint_val;
1196
1197 id = gupnp_didl_lite_object_get_id(object);
1198 if (!id)
1199 goto on_error;
1200
1201 upnp_class = gupnp_didl_lite_object_get_upnp_class(object);
1202 media_spec_type = dls_props_upnp_class_to_media_spec(upnp_class);
1203
1204 if (!media_spec_type)
1205 goto on_error;
1206
1207 title = gupnp_didl_lite_object_get_title(object);
1208 creator = gupnp_didl_lite_object_get_creator(object);
1209 rest = gupnp_didl_lite_object_get_restricted(object);
1210 path = dls_path_from_id(root_path, id);
1211
1212 if (filter_mask & DLS_UPNP_MASK_PROP_DISPLAY_NAME)
1213 prv_add_string_prop(item_vb, DLS_INTERFACE_PROP_DISPLAY_NAME,
1214 title);
1215
1216 if (filter_mask & DLS_UPNP_MASK_PROP_CREATOR)
1217 prv_add_string_prop(item_vb, DLS_INTERFACE_PROP_CREATOR,
1218 creator);
1219
1220 if (filter_mask & DLS_UPNP_MASK_PROP_PATH)
1221 prv_add_path_prop(item_vb, DLS_INTERFACE_PROP_PATH, path);
1222
1223 if (filter_mask & DLS_UPNP_MASK_PROP_PARENT)
1224 prv_add_path_prop(item_vb, DLS_INTERFACE_PROP_PARENT,
1225 parent_path);
1226
1227 if (filter_mask & DLS_UPNP_MASK_PROP_TYPE)
1228 prv_add_string_prop(item_vb, DLS_INTERFACE_PROP_TYPE,
1229 media_spec_type);
1230
1231 if (filter_mask & DLS_UPNP_MASK_PROP_RESTRICTED)
1232 prv_add_bool_prop(item_vb, DLS_INTERFACE_PROP_RESTRICTED, rest);
1233
1234 if (filter_mask & DLS_UPNP_MASK_PROP_DLNA_MANAGED) {
1235 flags = gupnp_didl_lite_object_get_dlna_managed(object);
1236 prv_add_variant_prop(item_vb,
1237 DLS_INTERFACE_PROP_DLNA_MANAGED,
1238 prv_props_get_dlna_managed_dict(flags));
1239 }
1240
1241 if (filter_mask & DLS_UPNP_MASK_PROP_OBJECT_UPDATE_ID) {
1242 uint_val = gupnp_didl_lite_object_get_update_id(object);
1243 prv_add_uint_prop(item_vb, DLS_INTERFACE_PROP_OBJECT_UPDATE_ID,
1244 uint_val);
1245 }
1246
1247 retval = TRUE;
1248
1249 on_error:
1250
1251 g_free(path);
1252
1253 return retval;
1254 }
1255
1256 void dls_props_add_container(GVariantBuilder *item_vb,
1257 GUPnPDIDLLiteContainer *object,
1258 dls_upnp_prop_mask filter_mask,
1259 gboolean *have_child_count)
1260 {
1261 int child_count;
1262 gboolean searchable;
1263 guint uint_val;
1264
1265 *have_child_count = FALSE;
1266 if (filter_mask & DLS_UPNP_MASK_PROP_CHILD_COUNT) {
1267 child_count = gupnp_didl_lite_container_get_child_count(object);
1268 if (child_count >= 0) {
1269 prv_add_uint_prop(item_vb,
1270 DLS_INTERFACE_PROP_CHILD_COUNT,
1271 (unsigned int) child_count);
1272 *have_child_count = TRUE;
1273 }
1274 }
1275
1276 if (filter_mask & DLS_UPNP_MASK_PROP_SEARCHABLE) {
1277 searchable = gupnp_didl_lite_container_get_searchable(object);
1278 prv_add_bool_prop(item_vb, DLS_INTERFACE_PROP_SEARCHABLE,
1279 searchable);
1280 }
1281
1282 if (filter_mask & DLS_UPNP_MASK_PROP_CREATE_CLASSES)
1283 prv_add_variant_prop(item_vb,
1284 DLS_INTERFACE_PROP_CREATE_CLASSES,
1285 prv_compute_create_classes(object));
1286
1287 if (filter_mask & DLS_UPNP_MASK_PROP_CONTAINER_UPDATE_ID) {
1288 uint_val = gupnp_didl_lite_container_get_container_update_id(
1289 object);
1290 prv_add_uint_prop(item_vb,
1291 DLS_INTERFACE_PROP_CONTAINER_UPDATE_ID,
1292 uint_val);
1293 }
1294
1295 if (filter_mask & DLS_UPNP_MASK_PROP_TOTAL_DELETED_CHILD_COUNT) {
1296 uint_val =
1297 gupnp_didl_lite_container_get_total_deleted_child_count(object);
1298 prv_add_uint_prop(item_vb,
1299 DLS_INTERFACE_PROP_TOTAL_DELETED_CHILD_COUNT,
1300 uint_val);
1301 }
1302 }
1303
1304 static GVariant *prv_compute_resources(GUPnPDIDLLiteObject *object,
1305 dls_upnp_prop_mask filter_mask)
1306 {
1307 GUPnPDIDLLiteResource *res = NULL;
1308 GList *resources;
1309 GList *ptr;
1310 GVariantBuilder *res_array_vb;
1311 GVariantBuilder *res_vb;
1312 const char *str_val;
1313 GVariant *retval;
1314
1315 res_array_vb = g_variant_builder_new(G_VARIANT_TYPE("aa{sv}"));
1316
1317 resources = gupnp_didl_lite_object_get_resources(object);
1318 ptr = resources;
1319
1320 while (ptr) {
1321 res = ptr->data;
1322 res_vb = g_variant_builder_new(G_VARIANT_TYPE("a{sv}"));
1323 if (filter_mask & DLS_UPNP_MASK_PROP_URL) {
1324 str_val = gupnp_didl_lite_resource_get_uri(res);
1325 if (str_val)
1326 prv_add_string_prop(res_vb,
1327 DLS_INTERFACE_PROP_URL,
1328 str_val);
1329 }
1330 prv_parse_resources(res_vb, res, filter_mask);
1331 g_variant_builder_add(res_array_vb, "@a{sv}",
1332 g_variant_builder_end(res_vb));
1333 g_variant_builder_unref(res_vb);
1334 g_object_unref(ptr->data);
1335 ptr = g_list_next(ptr);
1336 }
1337 retval = g_variant_builder_end(res_array_vb);
1338 g_variant_builder_unref(res_array_vb);
1339
1340 g_list_free(resources);
1341
1342 return retval;
1343 }
1344
1345 static void prv_add_resources(GVariantBuilder *item_vb,
1346 GUPnPDIDLLiteObject *object,
1347 dls_upnp_prop_mask filter_mask)
1348 {
1349 GVariant *val;
1350
1351 val = prv_compute_resources(object, filter_mask);
1352 g_variant_builder_add(item_vb, "{sv}", DLS_INTERFACE_PROP_RESOURCES,
1353 val);
1354 }
1355
1356 void dls_props_add_item(GVariantBuilder *item_vb,
1357 GUPnPDIDLLiteObject *object,
1358 const gchar *root_path,
1359 dls_upnp_prop_mask filter_mask,
1360 const gchar *protocol_info)
1361 {
1362 int track_number;
1363 GUPnPDIDLLiteResource *res;
1364 const char *str_val;
1365 char *path;
1366 GList *list;
1367
1368 if (filter_mask & DLS_UPNP_MASK_PROP_ARTIST)
1369 prv_add_string_prop(item_vb, DLS_INTERFACE_PROP_ARTIST,
1370 gupnp_didl_lite_object_get_artist(object));
1371
1372 if (filter_mask & DLS_UPNP_MASK_PROP_ARTISTS) {
1373 list = gupnp_didl_lite_object_get_artists(object);
1374 prv_add_variant_prop(item_vb, DLS_INTERFACE_PROP_ARTISTS,
1375 prv_get_artists_prop(list));
1376 g_list_free_full(list, g_object_unref);
1377 }
1378
1379 if (filter_mask & DLS_UPNP_MASK_PROP_ALBUM)
1380 prv_add_string_prop(item_vb, DLS_INTERFACE_PROP_ALBUM,
1381 gupnp_didl_lite_object_get_album(object));
1382
1383 if (filter_mask & DLS_UPNP_MASK_PROP_DATE)
1384 prv_add_string_prop(item_vb, DLS_INTERFACE_PROP_DATE,
1385 gupnp_didl_lite_object_get_date(object));
1386
1387 if (filter_mask & DLS_UPNP_MASK_PROP_GENRE)
1388 prv_add_string_prop(item_vb, DLS_INTERFACE_PROP_GENRE,
1389 gupnp_didl_lite_object_get_genre(object));
1390
1391 if (filter_mask & DLS_UPNP_MASK_PROP_TRACK_NUMBER) {
1392 track_number = gupnp_didl_lite_object_get_track_number(object);
1393 if (track_number >= 0)
1394 prv_add_int_prop(item_vb,
1395 DLS_INTERFACE_PROP_TRACK_NUMBER,
1396 track_number);
1397 }
1398
1399 if (filter_mask & DLS_UPNP_MASK_PROP_ALBUM_ART_URL)
1400 prv_add_string_prop(item_vb, DLS_INTERFACE_PROP_ALBUM_ART_URL,
1401 gupnp_didl_lite_object_get_album_art(
1402 object));
1403
1404 if (filter_mask & DLS_UPNP_MASK_PROP_REFPATH) {
1405 str_val = gupnp_didl_lite_item_get_ref_id(
1406 GUPNP_DIDL_LITE_ITEM(object));
1407 if (str_val != NULL) {
1408 path = dls_path_from_id(root_path, str_val);
1409 prv_add_path_prop(item_vb, DLS_INTERFACE_PROP_REFPATH,
1410 path);
1411 g_free(path);
1412 }
1413 }
1414
1415 res = prv_get_matching_resource(object, protocol_info);
1416 if (res) {
1417 if (filter_mask & DLS_UPNP_MASK_PROP_URLS) {
1418 str_val = gupnp_didl_lite_resource_get_uri(res);
1419 if (str_val)
1420 prv_add_strv_prop(item_vb,
1421 DLS_INTERFACE_PROP_URLS,
1422 &str_val, 1);
1423 }
1424 prv_parse_resources(item_vb, res, filter_mask);
1425 g_object_unref(res);
1426 }
1427
1428 if (filter_mask & DLS_UPNP_MASK_PROP_RESOURCES)
1429 prv_add_resources(item_vb, object, filter_mask);
1430 }
1431
1432 void dls_props_add_resource(GVariantBuilder *item_vb,
1433 GUPnPDIDLLiteObject *object,
1434 dls_upnp_prop_mask filter_mask,
1435 const gchar *protocol_info)
1436 {
1437 GUPnPDIDLLiteResource *res;
1438 const char *str_val;
1439
1440 res = prv_get_matching_resource(object, protocol_info);
1441 if (res) {
1442 if (filter_mask & DLS_UPNP_MASK_PROP_URL) {
1443 str_val = gupnp_didl_lite_resource_get_uri(res);
1444 if (str_val)
1445 prv_add_string_prop(item_vb,
1446 DLS_INTERFACE_PROP_URL,
1447 str_val);
1448 }
1449 prv_parse_resources(item_vb, res, filter_mask);
1450 g_object_unref(res);
1451 }
1452 }
1453
1454
1455 static GVariant *prv_get_resource_property(const gchar *prop,
1456 GUPnPDIDLLiteResource *res)
1457 {
1458 int int_val;
1459 gint64 int64_val;
1460 const char *str_val;
1461 GVariant *retval = NULL;
1462 GUPnPProtocolInfo *protocol_info;
1463
1464 if (!strcmp(prop, DLS_INTERFACE_PROP_DLNA_PROFILE)) {
1465 protocol_info = gupnp_didl_lite_resource_get_protocol_info(res);
1466 if (!protocol_info)
1467 goto on_error;
1468 str_val = gupnp_protocol_info_get_dlna_profile(protocol_info);
1469 if (!str_val)
1470 goto on_error;
1471 retval = g_variant_ref_sink(g_variant_new_string(str_val));
1472 } else if (!strcmp(prop, DLS_INTERFACE_PROP_MIME_TYPE)) {
1473 protocol_info = gupnp_didl_lite_resource_get_protocol_info(res);
1474 if (!protocol_info)
1475 goto on_error;
1476 str_val = gupnp_protocol_info_get_mime_type(protocol_info);
1477 if (!str_val)
1478 goto on_error;
1479 retval = g_variant_ref_sink(g_variant_new_string(str_val));
1480 } else if (!strcmp(prop, DLS_INTERFACE_PROP_SIZE)) {
1481 int64_val = gupnp_didl_lite_resource_get_size64(res);
1482 if (int64_val == -1)
1483 goto on_error;
1484 retval = g_variant_ref_sink(g_variant_new_int64(int64_val));
1485 } else if (!strcmp(prop, DLS_INTERFACE_PROP_DURATION)) {
1486 int_val = (int) gupnp_didl_lite_resource_get_duration(res);
1487 if (int_val == -1)
1488 goto on_error;
1489 retval = g_variant_ref_sink(g_variant_new_int32(int_val));
1490 } else if (!strcmp(prop, DLS_INTERFACE_PROP_BITRATE)) {
1491 int_val = gupnp_didl_lite_resource_get_bitrate(res);
1492 if (int_val == -1)
1493 goto on_error;
1494 retval = g_variant_ref_sink(g_variant_new_int32(int_val));
1495 } else if (!strcmp(prop, DLS_INTERFACE_PROP_SAMPLE_RATE)) {
1496 int_val = gupnp_didl_lite_resource_get_sample_freq(res);
1497 if (int_val == -1)
1498 goto on_error;
1499 retval = g_variant_ref_sink(g_variant_new_int32(int_val));
1500 } else if (!strcmp(prop, DLS_INTERFACE_PROP_BITS_PER_SAMPLE)) {
1501 int_val = gupnp_didl_lite_resource_get_bits_per_sample(res);
1502 if (int_val == -1)
1503 goto on_error;
1504 retval = g_variant_ref_sink(g_variant_new_int32(int_val));
1505 } else if (!strcmp(prop, DLS_INTERFACE_PROP_WIDTH)) {
1506 int_val = (int) gupnp_didl_lite_resource_get_width(res);
1507 if (int_val == -1)
1508 goto on_error;
1509 retval = g_variant_ref_sink(g_variant_new_int32(int_val));
1510 } else if (!strcmp(prop, DLS_INTERFACE_PROP_HEIGHT)) {
1511 int_val = (int) gupnp_didl_lite_resource_get_height(res);
1512 if (int_val == -1)
1513 goto on_error;
1514 retval = g_variant_ref_sink(g_variant_new_int32(int_val));
1515 } else if (!strcmp(prop, DLS_INTERFACE_PROP_COLOR_DEPTH)) {
1516 int_val = (int) gupnp_didl_lite_resource_get_color_depth(res);
1517 if (int_val == -1)
1518 goto on_error;
1519 retval = g_variant_ref_sink(g_variant_new_int32(int_val));
1520 } else if (!strcmp(prop, DLS_INTERFACE_PROP_URLS)) {
1521 str_val = gupnp_didl_lite_resource_get_uri(res);
1522 if (str_val)
1523 retval = g_variant_new_strv(&str_val, 1);
1524 }
1525
1526 on_error:
1527
1528 return retval;
1529 }
1530
1531 GVariant *dls_props_get_object_prop(const gchar *prop, const gchar *root_path,
1532 GUPnPDIDLLiteObject *object)
1533 {
1534 const char *id;
1535 gchar *path;
1536 const char *upnp_class;
1537 const char *media_spec_type;
1538 const char *title;
1539 gboolean rest;
1540 GVariant *retval = NULL;
1541 GUPnPOCMFlags dlna_managed;
1542 guint uint_val;
1543
1544 if (!strcmp(prop, DLS_INTERFACE_PROP_PARENT)) {
1545 id = gupnp_didl_lite_object_get_parent_id(object);
1546 if (!id || !strcmp(id, "-1")) {
1547 DLEYNA_LOG_DEBUG("Prop %s = %s", prop, root_path);
1548
1549 retval = g_variant_ref_sink(g_variant_new_string(
1550 root_path));
1551 } else {
1552 path = dls_path_from_id(root_path, id);
1553
1554 DLEYNA_LOG_DEBUG("Prop %s = %s", prop, path);
1555
1556 retval = g_variant_ref_sink(g_variant_new_string(
1557 path));
1558 g_free(path);
1559 }
1560 } else if (!strcmp(prop, DLS_INTERFACE_PROP_PATH)) {
1561 id = gupnp_didl_lite_object_get_id(object);
1562 if (!id)
1563 goto on_error;
1564
1565 path = dls_path_from_id(root_path, id);
1566
1567 DLEYNA_LOG_DEBUG("Prop %s = %s", prop, path);
1568
1569 retval = g_variant_ref_sink(g_variant_new_string(path));
1570 g_free(path);
1571 } else if (!strcmp(prop, DLS_INTERFACE_PROP_TYPE)) {
1572 upnp_class = gupnp_didl_lite_object_get_upnp_class(object);
1573 media_spec_type =
1574 dls_props_upnp_class_to_media_spec(upnp_class);
1575 if (!media_spec_type)
1576 goto on_error;
1577
1578 DLEYNA_LOG_DEBUG("Prop %s = %s", prop, media_spec_type);
1579
1580 retval = g_variant_ref_sink(g_variant_new_string(
1581 media_spec_type));
1582 } else if (!strcmp(prop, DLS_INTERFACE_PROP_DISPLAY_NAME)) {
1583 title = gupnp_didl_lite_object_get_title(object);
1584 if (!title)
1585 goto on_error;
1586
1587 DLEYNA_LOG_DEBUG("Prop %s = %s", prop, title);
1588
1589 retval = g_variant_ref_sink(g_variant_new_string(title));
1590 } else if (!strcmp(prop, DLS_INTERFACE_PROP_CREATOR)) {
1591 title = gupnp_didl_lite_object_get_creator(object);
1592 if (!title)
1593 goto on_error;
1594
1595 DLEYNA_LOG_DEBUG("Prop %s = %s", prop, title);
1596
1597 retval = g_variant_ref_sink(g_variant_new_string(title));
1598 } else if (!strcmp(prop, DLS_INTERFACE_PROP_RESTRICTED)) {
1599 rest = gupnp_didl_lite_object_get_restricted(object);
1600
1601 DLEYNA_LOG_DEBUG("Prop %s = %d", prop, rest);
1602
1603 retval = g_variant_ref_sink(g_variant_new_boolean(rest));
1604 } else if (!strcmp(prop, DLS_INTERFACE_PROP_DLNA_MANAGED)) {
1605 dlna_managed = gupnp_didl_lite_object_get_dlna_managed(object);
1606
1607 DLEYNA_LOG_DEBUG("Prop %s = %0x", prop, dlna_managed);
1608
1609 retval = g_variant_ref_sink(
1610 prv_props_get_dlna_managed_dict(dlna_managed));
1611 } else if (!strcmp(prop, DLS_INTERFACE_PROP_OBJECT_UPDATE_ID)) {
1612 uint_val = gupnp_didl_lite_object_get_update_id(object);
1613
1614 DLEYNA_LOG_DEBUG("Prop %s = %u", prop, uint_val);
1615
1616 retval = g_variant_ref_sink(g_variant_new_uint32(uint_val));
1617 }
1618
1619 on_error:
1620
1621 return retval;
1622 }
1623
1624 GVariant *dls_props_get_item_prop(const gchar *prop, const gchar *root_path,
1625 GUPnPDIDLLiteObject *object,
1626 const gchar *protocol_info)
1627 {
1628 const gchar *str;
1629 gchar *path;
1630 gint track_number;
1631 GUPnPDIDLLiteResource *res;
1632 GVariant *retval = NULL;
1633 GList *list;
1634
1635 if (GUPNP_IS_DIDL_LITE_CONTAINER(object))
1636 goto on_error;
1637
1638 if (!strcmp(prop, DLS_INTERFACE_PROP_ARTIST)) {
1639 str = gupnp_didl_lite_object_get_artist(object);
1640 if (!str)
1641 goto on_error;
1642
1643 DLEYNA_LOG_DEBUG("Prop %s = %s", prop, str);
1644
1645 retval = g_variant_ref_sink(g_variant_new_string(str));
1646 } else if (!strcmp(prop, DLS_INTERFACE_PROP_ARTISTS)) {
1647 list = gupnp_didl_lite_object_get_artists(object);
1648 if (!list)
1649 goto on_error;
1650
1651 retval = g_variant_ref_sink(prv_get_artists_prop(list));
1652 g_list_free_full(list, g_object_unref);
1653
1654 #if DLEYNA_LOG_LEVEL & DLEYNA_LOG_LEVEL_DEBUG
1655 path = g_variant_print(retval, FALSE);
1656 DLEYNA_LOG_DEBUG("Prop %s = %s", prop, path);
1657 g_free(path);
1658 #endif
1659 } else if (!strcmp(prop, DLS_INTERFACE_PROP_ALBUM)) {
1660 str = gupnp_didl_lite_object_get_album(object);
1661 if (!str)
1662 goto on_error;
1663
1664 DLEYNA_LOG_DEBUG("Prop %s = %s", prop, str);
1665
1666 retval = g_variant_ref_sink(g_variant_new_string(str));
1667 } else if (!strcmp(prop, DLS_INTERFACE_PROP_DATE)) {
1668 str = gupnp_didl_lite_object_get_date(object);
1669 if (!str)
1670 goto on_error;
1671
1672 DLEYNA_LOG_DEBUG("Prop %s = %s", prop, str);
1673
1674 retval = g_variant_ref_sink(g_variant_new_string(str));
1675 } else if (!strcmp(prop, DLS_INTERFACE_PROP_GENRE)) {
1676 str = gupnp_didl_lite_object_get_genre(object);
1677 if (!str)
1678 goto on_error;
1679
1680 DLEYNA_LOG_DEBUG("Prop %s = %s", prop, str);
1681
1682 retval = g_variant_ref_sink(g_variant_new_string(str));
1683 } else if (!strcmp(prop, DLS_INTERFACE_PROP_TRACK_NUMBER)) {
1684 track_number = gupnp_didl_lite_object_get_track_number(object);
1685 if (track_number < 0)
1686 goto on_error;
1687
1688 DLEYNA_LOG_DEBUG("Prop %s = %d", prop, track_number);
1689
1690 retval = g_variant_ref_sink(
1691 g_variant_new_int32(track_number));
1692 } else if (!strcmp(prop, DLS_INTERFACE_PROP_ALBUM_ART_URL)) {
1693 str = gupnp_didl_lite_object_get_album_art(object);
1694 if (!str)
1695 goto on_error;
1696
1697 DLEYNA_LOG_DEBUG("Prop %s = %s", prop, str);
1698
1699 retval = g_variant_ref_sink(g_variant_new_string(str));
1700 } else if (!strcmp(prop, DLS_INTERFACE_PROP_REFPATH)) {
1701 str = gupnp_didl_lite_item_get_ref_id(
1702 GUPNP_DIDL_LITE_ITEM(object));
1703 if (!str)
1704 goto on_error;
1705
1706 DLEYNA_LOG_DEBUG("Prop %s = %s", prop, str);
1707
1708 path = dls_path_from_id(root_path, str);
1709 retval = g_variant_ref_sink(g_variant_new_string(path));
1710 g_free(path);
1711 } else if (!strcmp(prop, DLS_INTERFACE_PROP_RESOURCES)) {
1712 retval = g_variant_ref_sink(
1713 prv_compute_resources(object, DLS_UPNP_MASK_ALL_PROPS));
1714 } else {
1715 res = prv_get_matching_resource(object, protocol_info);
1716 if (!res)
1717 goto on_error;
1718
1719 retval = prv_get_resource_property(prop, res);
1720
1721 g_object_unref(res);
1722 }
1723
1724 on_error:
1725
1726 return retval;
1727 }
1728
1729 GVariant *dls_props_get_container_prop(const gchar *prop,
1730 GUPnPDIDLLiteObject *object)
1731 {
1732 gint child_count;
1733 gboolean searchable;
1734 GUPnPDIDLLiteContainer *container;
1735 GVariant *retval = NULL;
1736 guint uint_val;
1737 #if DLEYNA_LOG_LEVEL & DLEYNA_LOG_LEVEL_DEBUG
1738 gchar *create_classes;
1739 #endif
1740 if (!GUPNP_IS_DIDL_LITE_CONTAINER(object))
1741 goto on_error;
1742
1743 container = (GUPnPDIDLLiteContainer *)object;
1744 if (!strcmp(prop, DLS_INTERFACE_PROP_CHILD_COUNT)) {
1745 child_count =
1746 gupnp_didl_lite_container_get_child_count(container);
1747
1748 DLEYNA_LOG_DEBUG("Prop %s = %d", prop, child_count);
1749
1750 if (child_count >= 0) {
1751 retval = g_variant_new_uint32((guint) child_count);
1752 retval = g_variant_ref_sink(retval);
1753 }
1754 } else if (!strcmp(prop, DLS_INTERFACE_PROP_SEARCHABLE)) {
1755 searchable =
1756 gupnp_didl_lite_container_get_searchable(container);
1757
1758 DLEYNA_LOG_DEBUG("Prop %s = %d", prop, searchable);
1759
1760 retval = g_variant_ref_sink(
1761 g_variant_new_boolean(searchable));
1762 } else if (!strcmp(prop, DLS_INTERFACE_PROP_CREATE_CLASSES)) {
1763 retval = g_variant_ref_sink(
1764 prv_compute_create_classes(container));
1765 #if DLEYNA_LOG_LEVEL & DLEYNA_LOG_LEVEL_DEBUG
1766 create_classes = g_variant_print(retval, FALSE);
1767 DLEYNA_LOG_DEBUG("Prop %s = %s", prop, create_classes);
1768 g_free(create_classes);
1769 #endif
1770 } else if (!strcmp(prop, DLS_INTERFACE_PROP_CONTAINER_UPDATE_ID)) {
1771 uint_val = gupnp_didl_lite_container_get_container_update_id(
1772 container);
1773
1774 DLEYNA_LOG_DEBUG("Prop %s = %u", prop, uint_val);
1775
1776 retval = g_variant_ref_sink(g_variant_new_uint32(uint_val));
1777 } else if (!strcmp(prop,
1778 DLS_INTERFACE_PROP_TOTAL_DELETED_CHILD_COUNT)) {
1779 uint_val =
1780 gupnp_didl_lite_container_get_total_deleted_child_count(
1781 container);
1782
1783 DLEYNA_LOG_DEBUG("Prop %s = %u", prop, uint_val);
1784
1785 retval = g_variant_ref_sink(g_variant_new_uint32(uint_val));
1786 }
1787
1788 on_error:
1789
1790 return retval;
1791 }
0 /*
1 * dLeyna
2 *
3 * Copyright (C) 2012-2013 Intel Corporation. All rights reserved.
4 *
5 * This program is free software; you can redistribute it and/or modify it
6 * under the terms and conditions of the GNU Lesser General Public License,
7 * version 2.1, as published by the Free Software Foundation.
8 *
9 * This program is distributed in the hope it will be useful, but WITHOUT
10 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
11 * FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License
12 * for more details.
13 *
14 * You should have received a copy of the GNU Lesser General Public License
15 * along with this program; if not, write to the Free Software Foundation, Inc.,
16 * 51 Franklin St - Fifth Floor, Boston, MA 02110-1301 USA.
17 *
18 * Mark Ryan <mark.d.ryan@intel.com>
19 *
20 */
21
22 #ifndef DLS_PROPS_H__
23 #define DLS_PROPS_H__
24
25 #include <libgupnp-av/gupnp-av.h>
26 #include "async.h"
27
28 #define DLS_UPNP_MASK_PROP_PARENT (1LL << 0)
29 #define DLS_UPNP_MASK_PROP_TYPE (1LL << 1)
30 #define DLS_UPNP_MASK_PROP_PATH (1LL << 2)
31 #define DLS_UPNP_MASK_PROP_DISPLAY_NAME (1LL << 3)
32 #define DLS_UPNP_MASK_PROP_CHILD_COUNT (1LL << 4)
33 #define DLS_UPNP_MASK_PROP_SEARCHABLE (1LL << 5)
34 #define DLS_UPNP_MASK_PROP_URLS (1LL << 6)
35 #define DLS_UPNP_MASK_PROP_MIME_TYPE (1LL << 7)
36 #define DLS_UPNP_MASK_PROP_ARTIST (1LL << 8)
37 #define DLS_UPNP_MASK_PROP_ALBUM (1LL << 9)
38 #define DLS_UPNP_MASK_PROP_DATE (1LL << 10)
39 #define DLS_UPNP_MASK_PROP_GENRE (1LL << 11)
40 #define DLS_UPNP_MASK_PROP_DLNA_PROFILE (1LL << 12)
41 #define DLS_UPNP_MASK_PROP_TRACK_NUMBER (1LL << 13)
42 #define DLS_UPNP_MASK_PROP_SIZE (1LL << 14)
43 #define DLS_UPNP_MASK_PROP_DURATION (1LL << 15)
44 #define DLS_UPNP_MASK_PROP_BITRATE (1LL << 16)
45 #define DLS_UPNP_MASK_PROP_SAMPLE_RATE (1LL << 17)
46 #define DLS_UPNP_MASK_PROP_BITS_PER_SAMPLE (1LL << 18)
47 #define DLS_UPNP_MASK_PROP_WIDTH (1LL << 19)
48 #define DLS_UPNP_MASK_PROP_HEIGHT (1LL << 20)
49 #define DLS_UPNP_MASK_PROP_COLOR_DEPTH (1LL << 21)
50 #define DLS_UPNP_MASK_PROP_ALBUM_ART_URL (1LL << 22)
51 #define DLS_UPNP_MASK_PROP_RESOURCES (1LL << 23)
52 #define DLS_UPNP_MASK_PROP_URL (1LL << 24)
53 #define DLS_UPNP_MASK_PROP_REFPATH (1LL << 25)
54 #define DLS_UPNP_MASK_PROP_RESTRICTED (1LL << 26)
55 #define DLS_UPNP_MASK_PROP_DLNA_MANAGED (1LL << 27)
56 #define DLS_UPNP_MASK_PROP_CREATOR (1LL << 28)
57 #define DLS_UPNP_MASK_PROP_ARTISTS (1LL << 29)
58 #define DLS_UPNP_MASK_PROP_CREATE_CLASSES (1LL << 30)
59 #define DLS_UPNP_MASK_PROP_OBJECT_UPDATE_ID (1LL << 31)
60 #define DLS_UPNP_MASK_PROP_UPDATE_COUNT (1LL << 32)
61 #define DLS_UPNP_MASK_PROP_CONTAINER_UPDATE_ID (1LL << 33)
62 #define DLS_UPNP_MASK_PROP_TOTAL_DELETED_CHILD_COUNT (1LL << 34)
63
64 #define DLS_UPNP_MASK_ALL_PROPS 0xffffffffffffffff
65
66 typedef struct dls_prop_map_t_ dls_prop_map_t;
67 struct dls_prop_map_t_ {
68 const gchar *upnp_prop_name;
69 dls_upnp_prop_mask type;
70 gboolean filter;
71 gboolean searchable;
72 gboolean updateable;
73 };
74
75 void dls_prop_maps_new(GHashTable **property_map, GHashTable **filter_map);
76
77 dls_upnp_prop_mask dls_props_parse_filter(GHashTable *filter_map,
78 GVariant *filter,
79 gchar **upnp_filter);
80
81 gboolean dls_props_parse_update_filter(GHashTable *filter_map,
82 GVariant *to_add_update,
83 GVariant *to_delete,
84 dls_upnp_prop_mask *mask,
85 gchar **upnp_filter);
86
87 void dls_props_add_device(GUPnPDeviceInfo *proxy,
88 const dls_device_t *device,
89 GVariantBuilder *vb);
90
91 GVariant *dls_props_get_device_prop(GUPnPDeviceInfo *proxy,
92 const dls_device_t *device,
93 const gchar *prop);
94
95 gboolean dls_props_add_object(GVariantBuilder *item_vb,
96 GUPnPDIDLLiteObject *object,
97 const char *root_path,
98 const gchar *parent_path,
99 dls_upnp_prop_mask filter_mask);
100
101 GVariant *dls_props_get_object_prop(const gchar *prop, const gchar *root_path,
102 GUPnPDIDLLiteObject *object);
103
104 void dls_props_add_container(GVariantBuilder *item_vb,
105 GUPnPDIDLLiteContainer *object,
106 dls_upnp_prop_mask filter_mask,
107 gboolean *have_child_count);
108
109 void dls_props_add_child_count(GVariantBuilder *item_vb, gint value);
110
111 GVariant *dls_props_get_container_prop(const gchar *prop,
112 GUPnPDIDLLiteObject *object);
113
114 void dls_props_add_resource(GVariantBuilder *item_vb,
115 GUPnPDIDLLiteObject *object,
116 dls_upnp_prop_mask filter_mask,
117 const gchar *protocol_info);
118
119 void dls_props_add_item(GVariantBuilder *item_vb,
120 GUPnPDIDLLiteObject *object,
121 const gchar *root_path,
122 dls_upnp_prop_mask filter_mask,
123 const gchar *protocol_info);
124
125 GVariant *dls_props_get_item_prop(const gchar *prop, const gchar *root_path,
126 GUPnPDIDLLiteObject *object,
127 const gchar *protocol_info);
128
129 const gchar *dls_props_media_spec_to_upnp_class(const gchar *m2spec_class);
130
131 const gchar *dls_props_upnp_class_to_media_spec(const gchar *upnp_class);
132
133 #endif /* DLS_PROPS_H__ */
0 /*
1 * dLeyna
2 *
3 * Copyright (C) 2012-2013 Intel Corporation. All rights reserved.
4 *
5 * This program is free software; you can redistribute it and/or modify it
6 * under the terms and conditions of the GNU Lesser General Public License,
7 * version 2.1, as published by the Free Software Foundation.
8 *
9 * This program is distributed in the hope it will be useful, but WITHOUT
10 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
11 * FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License
12 * for more details.
13 *
14 * You should have received a copy of the GNU Lesser General Public License
15 * along with this program; if not, write to the Free Software Foundation, Inc.,
16 * 51 Franklin St - Fifth Floor, Boston, MA 02110-1301 USA.
17 *
18 * Mark Ryan <mark.d.ryan@intel.com>
19 *
20 */
21
22 #include <string.h>
23
24 #include "interface.h"
25 #include "path.h"
26 #include "props.h"
27 #include "search.h"
28
29 gchar *dls_search_translate_search_string(GHashTable *filter_map,
30 const gchar *search_string)
31 {
32 GRegex *reg;
33 gchar *retval = NULL;
34 GMatchInfo *match_info = NULL;
35 gchar *prop = NULL;
36 gchar *op = NULL;
37 gchar *value = NULL;
38 const gchar *translated_value;
39 dls_prop_map_t *prop_map;
40 GString *str;
41 gint start_pos;
42 gint end_pos;
43 gint old_end_pos = 0;
44 unsigned int skipped;
45 unsigned int search_string_len = strlen(search_string);
46 gchar *root_path;
47 gchar *id;
48
49 reg = g_regex_new("(\\w+)\\s+(=|!=|<|<=|>|>|contains|doesNotContain|"\
50 "derivedfrom|exists)\\s+"\
51 "(\"[^\"]*\"|true|false)",
52 0, 0, NULL);
53 str = g_string_new("");
54
55 g_regex_match(reg, search_string, 0, &match_info);
56 while (g_match_info_matches(match_info)) {
57 prop = g_match_info_fetch(match_info, 1);
58 if (!prop)
59 goto on_error;
60
61 op = g_match_info_fetch(match_info, 2);
62 if (!op)
63 goto on_error;
64
65 value = g_match_info_fetch(match_info, 3);
66 if (!value)
67 goto on_error;
68
69 /* Handle special cases where we need to translate
70 value as well as property name */
71
72 if (!strcmp(prop, DLS_INTERFACE_PROP_TYPE)) {
73 /* Skip the quotes */
74
75 value[strlen(value) - 1] = 0;
76 translated_value = dls_props_media_spec_to_upnp_class(
77 value + 1);
78 if (!translated_value)
79 goto on_error;
80 g_free(value);
81 value = g_strdup_printf("\"%s\"", translated_value);
82 } else if (!strcmp(prop, DLS_INTERFACE_PROP_PARENT) ||
83 !strcmp(prop, DLS_INTERFACE_PROP_PATH)) {
84 value[strlen(value) - 1] = 0;
85 if (!dls_path_get_path_and_id(value + 1, &root_path,
86 &id, NULL))
87 goto on_error;
88 g_free(root_path);
89 g_free(value);
90 value = g_strdup_printf("\"%s\"", id);
91 g_free(id);
92 }
93
94 prop_map = g_hash_table_lookup(filter_map, prop);
95 if (!prop_map)
96 goto on_error;
97
98 if (!prop_map->searchable)
99 goto on_error;
100
101 if (!g_match_info_fetch_pos(match_info, 0, &start_pos,
102 &end_pos))
103 goto on_error;
104
105 skipped = start_pos - old_end_pos;
106 if (skipped > 0)
107 g_string_append_len(str, &search_string[old_end_pos],
108 skipped);
109 g_string_append_printf(str, "%s %s %s",
110 prop_map->upnp_prop_name, op, value);
111 old_end_pos = end_pos;
112
113 g_free(value);
114 g_free(prop);
115 g_free(op);
116
117 value = NULL;
118 prop = NULL;
119 op = NULL;
120
121 g_match_info_next(match_info, NULL);
122 }
123
124 skipped = search_string_len - old_end_pos;
125 if (skipped > 0)
126 g_string_append_len(str, &search_string[old_end_pos],
127 skipped);
128
129 retval = g_string_free(str, FALSE);
130 str = NULL;
131
132 on_error:
133
134 g_free(value);
135 g_free(prop);
136 g_free(op);
137
138 if (match_info)
139 g_match_info_free(match_info);
140
141 if (str)
142 g_string_free(str, TRUE);
143
144 g_regex_unref(reg);
145
146 return retval;
147 }
0 /*
1 * dLeyna
2 *
3 * Copyright (C) 2012-2013 Intel Corporation. All rights reserved.
4 *
5 * This program is free software; you can redistribute it and/or modify it
6 * under the terms and conditions of the GNU Lesser General Public License,
7 * version 2.1, as published by the Free Software Foundation.
8 *
9 * This program is distributed in the hope it will be useful, but WITHOUT
10 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
11 * FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License
12 * for more details.
13 *
14 * You should have received a copy of the GNU Lesser General Public License
15 * along with this program; if not, write to the Free Software Foundation, Inc.,
16 * 51 Franklin St - Fifth Floor, Boston, MA 02110-1301 USA.
17 *
18 * Mark Ryan <mark.d.ryan@intel.com>
19 *
20 */
21
22 #ifndef DLS_SEARCH_H__
23 #define DLS_SEARCH_H__
24
25 #include <glib.h>
26
27 gchar *dls_search_translate_search_string(GHashTable *filter_map,
28 const gchar *search_string);
29
30 #endif /* DLS_PROPS_H__ */
0 /*
1 * dLeyna
2 *
3 * Copyright (C) 2013 Intel Corporation. All rights reserved.
4 *
5 * This program is free software; you can redistribute it and/or modify it
6 * under the terms and conditions of the GNU Lesser General Public License,
7 * version 2.1, as published by the Free Software Foundation.
8 *
9 * This program is distributed in the hope it will be useful, but WITHOUT
10 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
11 * FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License
12 * for more details.
13 *
14 * You should have received a copy of the GNU Lesser General Public License
15 * along with this program; if not, write to the Free Software Foundation, Inc.,
16 * 51 Franklin St - Fifth Floor, Boston, MA 02110-1301 USA.
17 *
18 * Mark Ryan <mark.d.ryan@intel.com>
19 * Regis Merlino <regis.merlino@intel.com>
20 *
21 */
22
23 #include <glib.h>
24 #include <string.h>
25
26 #include <libdleyna/core/connector.h>
27 #include <libdleyna/core/control-point.h>
28 #include <libdleyna/core/error.h>
29 #include <libdleyna/core/log.h>
30 #include <libdleyna/core/task-processor.h>
31
32 #include "async.h"
33 #include "client.h"
34 #include "control-point-server.h"
35 #include "device.h"
36 #include "interface.h"
37 #include "path.h"
38 #include "server.h"
39 #include "upnp.h"
40
41 #ifdef UA_PREFIX
42 #define DLS_PRG_NAME UA_PREFIX " dLeyna/" VERSION
43 #else
44 #define DLS_PRG_NAME "dLeyna/" VERSION
45 #endif
46
47
48 typedef struct dls_server_context_t_ dls_server_context_t;
49 struct dls_server_context_t_ {
50 dleyna_connector_id_t connection;
51 dleyna_task_processor_t *processor;
52 const dleyna_connector_t *connector;
53 dleyna_settings_t *settings;
54 guint dls_id;
55 GHashTable *watchers;
56 dls_upnp_t *upnp;
57 };
58
59 static dls_server_context_t g_context;
60
61 static const gchar g_root_introspection[] =
62 "<node>"
63 " <interface name='"DLEYNA_SERVER_INTERFACE_MANAGER"'>"
64 " <method name='"DLS_INTERFACE_GET_VERSION"'>"
65 " <arg type='s' name='"DLS_INTERFACE_VERSION"'"
66 " direction='out'/>"
67 " </method>"
68 " <method name='"DLS_INTERFACE_RELEASE"'>"
69 " </method>"
70 " <method name='"DLS_INTERFACE_GET_SERVERS"'>"
71 " <arg type='ao' name='"DLS_INTERFACE_SERVERS"'"
72 " direction='out'/>"
73 " </method>"
74 " <method name='"DLS_INTERFACE_SET_PROTOCOL_INFO"'>"
75 " <arg type='s' name='"DLS_INTERFACE_PROTOCOL_INFO"'"
76 " direction='in'/>"
77 " </method>"
78 " <method name='"DLS_INTERFACE_PREFER_LOCAL_ADDRESSES"'>"
79 " <arg type='b' name='"DLS_INTERFACE_PREFER"'"
80 " direction='in'/>"
81 " </method>"
82 " <signal name='"DLS_INTERFACE_FOUND_SERVER"'>"
83 " <arg type='o' name='"DLS_INTERFACE_PATH"'/>"
84 " </signal>"
85 " <signal name='"DLS_INTERFACE_LOST_SERVER"'>"
86 " <arg type='o' name='"DLS_INTERFACE_PATH"'/>"
87 " </signal>"
88 " </interface>"
89 "</node>";
90
91 static const gchar g_server_introspection[] =
92 "<node>"
93 " <interface name='"DLS_INTERFACE_PROPERTIES"'>"
94 " <method name='"DLS_INTERFACE_GET"'>"
95 " <arg type='s' name='"DLS_INTERFACE_INTERFACE_NAME"'"
96 " direction='in'/>"
97 " <arg type='s' name='"DLS_INTERFACE_PROPERTY_NAME"'"
98 " direction='in'/>"
99 " <arg type='v' name='"DLS_INTERFACE_VALUE"'"
100 " direction='out'/>"
101 " </method>"
102 " <method name='"DLS_INTERFACE_GET_ALL"'>"
103 " <arg type='s' name='"DLS_INTERFACE_INTERFACE_NAME"'"
104 " direction='in'/>"
105 " <arg type='a{sv}' name='"DLS_INTERFACE_PROPERTIES_VALUE"'"
106 " direction='out'/>"
107 " </method>"
108 " <signal name='"DLS_INTERFACE_PROPERTIES_CHANGED"'>"
109 " <arg type='s' name='"DLS_INTERFACE_INTERFACE_NAME"'/>"
110 " <arg type='a{sv}' name='"DLS_INTERFACE_CHANGED_PROPERTIES"'/>"
111 " <arg type='as' name='"
112 DLS_INTERFACE_INVALIDATED_PROPERTIES"'/>"
113 " </signal>"
114 " </interface>"
115 " <interface name='"DLS_INTERFACE_MEDIA_OBJECT"'>"
116 " <property type='o' name='"DLS_INTERFACE_PROP_PARENT"'"
117 " access='read'/>"
118 " <property type='s' name='"DLS_INTERFACE_PROP_TYPE"'"
119 " access='read'/>"
120 " <property type='o' name='"DLS_INTERFACE_PROP_PATH"'"
121 " access='read'/>"
122 " <property type='s' name='"DLS_INTERFACE_PROP_DISPLAY_NAME"'"
123 " access='read'/>"
124 " <property type='s' name='"DLS_INTERFACE_PROP_CREATOR"'"
125 " access='read'/>"
126 " <property type='b' name='"DLS_INTERFACE_PROP_RESTRICTED"'"
127 " access='read'/>"
128 " <property type='a{sb}' name='"DLS_INTERFACE_PROP_DLNA_MANAGED"'"
129 " access='read'/>"
130 " <property type='u' name='"DLS_INTERFACE_PROP_OBJECT_UPDATE_ID"'"
131 " access='read'/>"
132 " <method name='"DLS_INTERFACE_DELETE"'>"
133 " </method>"
134 " <method name='"DLS_INTERFACE_UPDATE"'>"
135 " <arg type='a{sv}' name='"DLS_INTERFACE_TO_ADD_UPDATE"'"
136 " direction='in'/>"
137 " <arg type='as' name='"DLS_INTERFACE_TO_DELETE"'"
138 " direction='in'/>"
139 " </method>"
140 " </interface>"
141 " <interface name='"DLS_INTERFACE_MEDIA_CONTAINER"'>"
142 " <method name='"DLS_INTERFACE_LIST_CHILDREN"'>"
143 " <arg type='u' name='"DLS_INTERFACE_OFFSET"'"
144 " direction='in'/>"
145 " <arg type='u' name='"DLS_INTERFACE_MAX"'"
146 " direction='in'/>"
147 " <arg type='as' name='"DLS_INTERFACE_FILTER"'"
148 " direction='in'/>"
149 " <arg type='aa{sv}' name='"DLS_INTERFACE_CHILDREN"'"
150 " direction='out'/>"
151 " </method>"
152 " <method name='"DLS_INTERFACE_LIST_CHILDREN_EX"'>"
153 " <arg type='u' name='"DLS_INTERFACE_OFFSET"'"
154 " direction='in'/>"
155 " <arg type='u' name='"DLS_INTERFACE_MAX"'"
156 " direction='in'/>"
157 " <arg type='as' name='"DLS_INTERFACE_FILTER"'"
158 " direction='in'/>"
159 " <arg type='s' name='"DLS_INTERFACE_SORT_BY"'"
160 " direction='in'/>"
161 " <arg type='aa{sv}' name='"DLS_INTERFACE_CHILDREN"'"
162 " direction='out'/>"
163 " </method>"
164 " <method name='"DLS_INTERFACE_LIST_CONTAINERS"'>"
165 " <arg type='u' name='"DLS_INTERFACE_OFFSET"'"
166 " direction='in'/>"
167 " <arg type='u' name='"DLS_INTERFACE_MAX"'"
168 " direction='in'/>"
169 " <arg type='as' name='"DLS_INTERFACE_FILTER"'"
170 " direction='in'/>"
171 " <arg type='aa{sv}' name='"DLS_INTERFACE_CHILDREN"'"
172 " direction='out'/>"
173 " </method>"
174 " <method name='"DLS_INTERFACE_LIST_CONTAINERS_EX"'>"
175 " <arg type='u' name='"DLS_INTERFACE_OFFSET"'"
176 " direction='in'/>"
177 " <arg type='u' name='"DLS_INTERFACE_MAX"'"
178 " direction='in'/>"
179 " <arg type='as' name='"DLS_INTERFACE_FILTER"'"
180 " direction='in'/>"
181 " <arg type='s' name='"DLS_INTERFACE_SORT_BY"'"
182 " direction='in'/>"
183 " <arg type='aa{sv}' name='"DLS_INTERFACE_CHILDREN"'"
184 " direction='out'/>"
185 " </method>"
186 " <method name='"DLS_INTERFACE_LIST_ITEMS"'>"
187 " <arg type='u' name='"DLS_INTERFACE_OFFSET"'"
188 " direction='in'/>"
189 " <arg type='u' name='"DLS_INTERFACE_MAX"'"
190 " direction='in'/>"
191 " <arg type='as' name='"DLS_INTERFACE_FILTER"'"
192 " direction='in'/>"
193 " <arg type='aa{sv}' name='"DLS_INTERFACE_CHILDREN"'"
194 " direction='out'/>"
195 " </method>"
196 " <method name='"DLS_INTERFACE_LIST_ITEMS_EX"'>"
197 " <arg type='u' name='"DLS_INTERFACE_OFFSET"'"
198 " direction='in'/>"
199 " <arg type='u' name='"DLS_INTERFACE_MAX"'"
200 " direction='in'/>"
201 " <arg type='as' name='"DLS_INTERFACE_FILTER"'"
202 " direction='in'/>"
203 " <arg type='s' name='"DLS_INTERFACE_SORT_BY"'"
204 " direction='in'/>"
205 " <arg type='aa{sv}' name='"DLS_INTERFACE_CHILDREN"'"
206 " direction='out'/>"
207 " </method>"
208 " <method name='"DLS_INTERFACE_SEARCH_OBJECTS"'>"
209 " <arg type='s' name='"DLS_INTERFACE_QUERY"'"
210 " direction='in'/>"
211 " <arg type='u' name='"DLS_INTERFACE_OFFSET"'"
212 " direction='in'/>"
213 " <arg type='u' name='"DLS_INTERFACE_MAX"'"
214 " direction='in'/>"
215 " <arg type='as' name='"DLS_INTERFACE_FILTER"'"
216 " direction='in'/>"
217 " <arg type='aa{sv}' name='"DLS_INTERFACE_CHILDREN"'"
218 " direction='out'/>"
219 " </method>"
220 " <method name='"DLS_INTERFACE_SEARCH_OBJECTS_EX"'>"
221 " <arg type='s' name='"DLS_INTERFACE_QUERY"'"
222 " direction='in'/>"
223 " <arg type='u' name='"DLS_INTERFACE_OFFSET"'"
224 " direction='in'/>"
225 " <arg type='u' name='"DLS_INTERFACE_MAX"'"
226 " direction='in'/>"
227 " <arg type='as' name='"DLS_INTERFACE_FILTER"'"
228 " direction='in'/>"
229 " <arg type='s' name='"DLS_INTERFACE_SORT_BY"'"
230 " direction='in'/>"
231 " <arg type='aa{sv}' name='"DLS_INTERFACE_CHILDREN"'"
232 " direction='out'/>"
233 " <arg type='u' name='"DLS_INTERFACE_TOTAL_ITEMS"'"
234 " direction='out'/>"
235 " </method>"
236 " <method name='"DLS_INTERFACE_UPLOAD"'>"
237 " <arg type='s' name='"DLS_INTERFACE_PROP_DISPLAY_NAME"'"
238 " direction='in'/>"
239 " <arg type='s' name='"DLS_INTERFACE_FILE_PATH"'"
240 " direction='in'/>"
241 " <arg type='u' name='"DLS_INTERFACE_UPLOAD_ID"'"
242 " direction='out'/>"
243 " <arg type='o' name='"DLS_INTERFACE_PATH"'"
244 " direction='out'/>"
245 " </method>"
246 " <method name='"DLS_INTERFACE_CREATE_CONTAINER"'>"
247 " <arg type='s' name='"DLS_INTERFACE_PROP_DISPLAY_NAME"'"
248 " direction='in'/>"
249 " <arg type='s' name='"DLS_INTERFACE_PROP_TYPE"'"
250 " direction='in'/>"
251 " <arg type='as' name='"DLS_INTERFACE_CHILD_TYPES"'"
252 " direction='in'/>"
253 " <arg type='o' name='"DLS_INTERFACE_PATH"'"
254 " direction='out'/>"
255 " </method>"
256 " <method name='"DLS_INTERFACE_CREATE_PLAYLIST"'>"
257 " <arg type='s' name='"DLS_INTERFACE_TITLE"'"
258 " direction='in'/>"
259 " <arg type='s' name='"DLS_INTERFACE_CREATOR"'"
260 " direction='in'/>"
261 " <arg type='s' name='"DLS_INTERFACE_GENRE"'"
262 " direction='in'/>"
263 " <arg type='s' name='"DLS_INTERFACE_DESCRIPTION"'"
264 " direction='in'/>"
265 " <arg type='ao' name='"DLS_INTERFACE_PLAYLIST_ITEMS"'"
266 " direction='in'/>"
267 " <arg type='u' name='"DLS_INTERFACE_UPLOAD_ID"'"
268 " direction='out'/>"
269 " <arg type='o' name='"DLS_INTERFACE_PATH"'"
270 " direction='out'/>"
271 " </method>"
272 " <property type='u' name='"DLS_INTERFACE_PROP_CHILD_COUNT"'"
273 " access='read'/>"
274 " <property type='b' name='"DLS_INTERFACE_PROP_SEARCHABLE"'"
275 " access='read'/>"
276 " <property type='a(sb)' name='"
277 DLS_INTERFACE_PROP_CREATE_CLASSES"'"
278 " access='read'/>"
279 " <property type='u' name='"
280 DLS_INTERFACE_PROP_CONTAINER_UPDATE_ID"'"
281 " access='read'/>"
282 " <property type='u' name='"
283 DLS_INTERFACE_PROP_TOTAL_DELETED_CHILD_COUNT"'"
284 " access='read'/>"
285 " </interface>"
286 " <interface name='"DLS_INTERFACE_MEDIA_ITEM"'>"
287 " <method name='"DLS_INTERFACE_GET_COMPATIBLE_RESOURCE"'>"
288 " <arg type='s' name='"DLS_INTERFACE_PROTOCOL_INFO"'"
289 " direction='in'/>"
290 " <arg type='as' name='"DLS_INTERFACE_FILTER"'"
291 " direction='in'/>"
292 " <arg type='a{sv}' name='"DLS_INTERFACE_PROPERTIES_VALUE"'"
293 " direction='out'/>"
294 " </method>"
295 " <property type='as' name='"DLS_INTERFACE_PROP_URLS"'"
296 " access='read'/>"
297 " <property type='s' name='"DLS_INTERFACE_PROP_MIME_TYPE"'"
298 " access='read'/>"
299 " <property type='s' name='"DLS_INTERFACE_PROP_ARTIST"'"
300 " access='read'/>"
301 " <property type='as' name='"DLS_INTERFACE_PROP_ARTISTS"'"
302 " access='read'/>"
303 " <property type='s' name='"DLS_INTERFACE_PROP_ALBUM"'"
304 " access='read'/>"
305 " <property type='s' name='"DLS_INTERFACE_PROP_DATE"'"
306 " access='read'/>"
307 " <property type='s' name='"DLS_INTERFACE_PROP_GENRE"'"
308 " access='read'/>"
309 " <property type='s' name='"DLS_INTERFACE_PROP_DLNA_PROFILE"'"
310 " access='read'/>"
311 " <property type='i' name='"DLS_INTERFACE_PROP_TRACK_NUMBER"'"
312 " access='read'/>"
313 " <property type='x' name='"DLS_INTERFACE_PROP_SIZE"'"
314 " access='read'/>"
315 " <property type='i' name='"DLS_INTERFACE_PROP_DURATION"'"
316 " access='read'/>"
317 " <property type='i' name='"DLS_INTERFACE_PROP_BITRATE"'"
318 " access='read'/>"
319 " <property type='i' name='"DLS_INTERFACE_PROP_SAMPLE_RATE"'"
320 " access='read'/>"
321 " <property type='i' name='"DLS_INTERFACE_PROP_BITS_PER_SAMPLE"'"
322 " access='read'/>"
323 " <property type='i' name='"DLS_INTERFACE_PROP_WIDTH"'"
324 " access='read'/>"
325 " <property type='i' name='"DLS_INTERFACE_PROP_HEIGHT"'"
326 " access='read'/>"
327 " <property type='i' name='"DLS_INTERFACE_PROP_COLOR_DEPTH"'"
328 " access='read'/>"
329 " <property type='s' name='"DLS_INTERFACE_PROP_ALBUM_ART_URL"'"
330 " access='read'/>"
331 " <property type='o' name='"DLS_INTERFACE_PROP_REFPATH"'"
332 " access='read'/>"
333 " <property type='aa{sv}' name='"DLS_INTERFACE_PROP_RESOURCES"'"
334 " access='read'/>"
335 " </interface>"
336 " <interface name='"DLEYNA_SERVER_INTERFACE_MEDIA_DEVICE"'>"
337 " <method name='"DLS_INTERFACE_UPLOAD_TO_ANY"'>"
338 " <arg type='s' name='"DLS_INTERFACE_PROP_DISPLAY_NAME"'"
339 " direction='in'/>"
340 " <arg type='s' name='"DLS_INTERFACE_FILE_PATH"'"
341 " direction='in'/>"
342 " <arg type='u' name='"DLS_INTERFACE_UPLOAD_ID"'"
343 " direction='out'/>"
344 " <arg type='o' name='"DLS_INTERFACE_PATH"'"
345 " direction='out'/>"
346 " </method>"
347 " <method name='"DLS_INTERFACE_GET_UPLOAD_STATUS"'>"
348 " <arg type='u' name='"DLS_INTERFACE_UPLOAD_ID"'"
349 " direction='in'/>"
350 " <arg type='s' name='"DLS_INTERFACE_UPLOAD_STATUS"'"
351 " direction='out'/>"
352 " <arg type='t' name='"DLS_INTERFACE_LENGTH"'"
353 " direction='out'/>"
354 " <arg type='t' name='"DLS_INTERFACE_TOTAL"'"
355 " direction='out'/>"
356 " </method>"
357 " <method name='"DLS_INTERFACE_GET_UPLOAD_IDS"'>"
358 " <arg type='au' name='"DLS_INTERFACE_TOTAL"'"
359 " direction='out'/>"
360 " </method>"
361 " <method name='"DLS_INTERFACE_CANCEL_UPLOAD"'>"
362 " <arg type='u' name='"DLS_INTERFACE_UPLOAD_ID"'"
363 " direction='in'/>"
364 " </method>"
365 " <method name='"DLS_INTERFACE_CREATE_CONTAINER_IN_ANY"'>"
366 " <arg type='s' name='"DLS_INTERFACE_PROP_DISPLAY_NAME"'"
367 " direction='in'/>"
368 " <arg type='s' name='"DLS_INTERFACE_PROP_TYPE"'"
369 " direction='in'/>"
370 " <arg type='as' name='"DLS_INTERFACE_CHILD_TYPES"'"
371 " direction='in'/>"
372 " <arg type='o' name='"DLS_INTERFACE_PATH"'"
373 " direction='out'/>"
374 " </method>"
375 " <method name='"DLS_INTERFACE_CANCEL"'>"
376 " </method>"
377 " <method name='"DLS_INTERFACE_CREATE_PLAYLIST_TO_ANY"'>"
378 " <arg type='s' name='"DLS_INTERFACE_TITLE"'"
379 " direction='in'/>"
380 " <arg type='s' name='"DLS_INTERFACE_CREATOR"'"
381 " direction='in'/>"
382 " <arg type='s' name='"DLS_INTERFACE_GENRE"'"
383 " direction='in'/>"
384 " <arg type='s' name='"DLS_INTERFACE_DESCRIPTION"'"
385 " direction='in'/>"
386 " <arg type='ao' name='"DLS_INTERFACE_PLAYLIST_ITEMS"'"
387 " direction='in'/>"
388 " <arg type='u' name='"DLS_INTERFACE_UPLOAD_ID"'"
389 " direction='out'/>"
390 " <arg type='o' name='"DLS_INTERFACE_PATH"'"
391 " direction='out'/>"
392 " </method>"
393 " <property type='s' name='"DLS_INTERFACE_PROP_LOCATION"'"
394 " access='read'/>"
395 " <property type='s' name='"DLS_INTERFACE_PROP_UDN"'"
396 " access='read'/>"
397 " <property type='s' name='"DLS_INTERFACE_PROP_DEVICE_TYPE"'"
398 " access='read'/>"
399 " <property type='s' name='"DLS_INTERFACE_PROP_FRIENDLY_NAME"'"
400 " access='read'/>"
401 " <property type='s' name='"DLS_INTERFACE_PROP_MANUFACTURER"'"
402 " access='read'/>"
403 " <property type='s' name='"
404 DLS_INTERFACE_PROP_MANUFACTURER_URL"'"
405 " access='read'/>"
406 " <property type='s' name='"
407 DLS_INTERFACE_PROP_MODEL_DESCRIPTION"'"
408 " access='read'/>"
409 " <property type='s' name='"DLS_INTERFACE_PROP_MODEL_NAME"'"
410 " access='read'/>"
411 " <property type='s' name='"DLS_INTERFACE_PROP_MODEL_NUMBER"'"
412 " access='read'/>"
413 " <property type='s' name='"DLS_INTERFACE_PROP_MODEL_URL"'"
414 " access='read'/>"
415 " <property type='s' name='"DLS_INTERFACE_PROP_SERIAL_NUMBER"'"
416 " access='read'/>"
417 " <property type='s' name='"DLS_INTERFACE_PROP_PRESENTATION_URL"'"
418 " access='read'/>"
419 " <property type='s' name='"DLS_INTERFACE_PROP_ICON_URL"'"
420 " access='read'/>"
421 " <property type='a{sv}'name='"
422 DLS_INTERFACE_PROP_SV_DLNA_CAPABILITIES"'"
423 " access='read'/>"
424 " <property type='as' name='"
425 DLS_INTERFACE_PROP_SV_SEARCH_CAPABILITIES"'"
426 " access='read'/>"
427 " <property type='as' name='"
428 DLS_INTERFACE_PROP_SV_SORT_CAPABILITIES"'"
429 " access='read'/>"
430 " <property type='as' name='"
431 DLS_INTERFACE_PROP_SV_SORT_EXT_CAPABILITIES"'"
432 " access='read'/>"
433 " <property type='a(ssao)' name='"
434 DLS_INTERFACE_PROP_SV_FEATURE_LIST"'"
435 " access='read'/>"
436 " <property type='u' name='"
437 DLS_INTERFACE_PROP_ESV_SYSTEM_UPDATE_ID"'"
438 " access='read'/>"
439 " <property type='s' name='"
440 DLS_INTERFACE_PROP_SV_SERVICE_RESET_TOKEN"'"
441 " access='read'/>"
442 " <signal name='"DLS_INTERFACE_ESV_CONTAINER_UPDATE_IDS"'>"
443 " <arg type='a(ou)' name='"DLS_INTERFACE_CONTAINER_PATHS_ID"'/>"
444 " </signal>"
445 " <signal name='"DLS_INTERFACE_ESV_LAST_CHANGE"'>"
446 " <arg type='a(sv)' name='"
447 DLS_INTERFACE_LAST_CHANGE_STATE_EVENT"'/>"
448 " </signal>"
449 " <signal name='"DLS_INTERFACE_UPLOAD_UPDATE"'>"
450 " <arg type='u' name='"DLS_INTERFACE_UPLOAD_ID"'/>"
451 " <arg type='s' name='"DLS_INTERFACE_UPLOAD_STATUS"'/>"
452 " <arg type='t' name='"DLS_INTERFACE_LENGTH"'/>"
453 " <arg type='t' name='"DLS_INTERFACE_TOTAL"'/>"
454 " </signal>"
455 " </interface>"
456 "</node>";
457
458 const dleyna_connector_t *dls_server_get_connector(void)
459 {
460 return g_context.connector;
461 }
462
463 dleyna_task_processor_t *dls_server_get_task_processor(void)
464 {
465 return g_context.processor;
466 }
467
468 static void prv_sync_task_complete(dls_task_t *task)
469 {
470 dls_task_complete(task);
471 dleyna_task_queue_task_completed(task->atom.queue_id);
472 }
473
474 static void prv_process_sync_task(dls_task_t *task)
475 {
476 dls_client_t *client;
477 const gchar *client_name;
478
479 switch (task->type) {
480 case DLS_TASK_GET_VERSION:
481 prv_sync_task_complete(task);
482 break;
483 case DLS_TASK_GET_SERVERS:
484 task->result = dls_upnp_get_server_ids(g_context.upnp);
485 prv_sync_task_complete(task);
486 break;
487 case DLS_TASK_SET_PROTOCOL_INFO:
488 client_name = dleyna_task_queue_get_source(task->atom.queue_id);
489 client = g_hash_table_lookup(g_context.watchers, client_name);
490 if (client) {
491 g_free(client->protocol_info);
492 if (task->ut.protocol_info.protocol_info[0]) {
493 client->protocol_info =
494 task->ut.protocol_info.protocol_info;
495 task->ut.protocol_info.protocol_info = NULL;
496 } else {
497 client->protocol_info = NULL;
498 }
499 }
500 prv_sync_task_complete(task);
501 break;
502 case DLS_TASK_SET_PREFER_LOCAL_ADDRESSES:
503 client_name = dleyna_task_queue_get_source(task->atom.queue_id);
504 client = g_hash_table_lookup(g_context.watchers, client_name);
505 if (client) {
506 client->prefer_local_addresses =
507 task->ut.prefer_local_addresses.prefer;
508 }
509 prv_sync_task_complete(task);
510 break;
511 case DLS_TASK_GET_UPLOAD_STATUS:
512 dls_upnp_get_upload_status(g_context.upnp, task);
513 dleyna_task_queue_task_completed(task->atom.queue_id);
514 break;
515 case DLS_TASK_GET_UPLOAD_IDS:
516 dls_upnp_get_upload_ids(g_context.upnp, task);
517 dleyna_task_queue_task_completed(task->atom.queue_id);
518 break;
519 case DLS_TASK_CANCEL_UPLOAD:
520 dls_upnp_cancel_upload(g_context.upnp, task);
521 dleyna_task_queue_task_completed(task->atom.queue_id);
522 break;
523 default:
524 break;
525 }
526 }
527
528 static void prv_async_task_complete(dls_task_t *task, GError *error)
529 {
530 DLEYNA_LOG_DEBUG("Enter");
531
532 if (error) {
533 dls_task_fail(task, error);
534 g_error_free(error);
535 } else {
536 dls_task_complete(task);
537 }
538
539 dleyna_task_queue_task_completed(task->atom.queue_id);
540
541 DLEYNA_LOG_DEBUG("Exit");
542 }
543
544 static void prv_process_async_task(dls_task_t *task)
545 {
546 dls_async_task_t *async_task = (dls_async_task_t *)task;
547 dls_client_t *client;
548 const gchar *client_name;
549
550 DLEYNA_LOG_DEBUG("Enter");
551
552 async_task->cancellable = g_cancellable_new();
553 client_name = dleyna_task_queue_get_source(task->atom.queue_id);
554 client = g_hash_table_lookup(g_context.watchers, client_name);
555
556 switch (task->type) {
557 case DLS_TASK_GET_CHILDREN:
558 dls_upnp_get_children(g_context.upnp, client, task,
559 prv_async_task_complete);
560 break;
561 case DLS_TASK_GET_PROP:
562 dls_upnp_get_prop(g_context.upnp, client, task,
563 prv_async_task_complete);
564 break;
565 case DLS_TASK_GET_ALL_PROPS:
566 dls_upnp_get_all_props(g_context.upnp, client, task,
567 prv_async_task_complete);
568 break;
569 case DLS_TASK_SEARCH:
570 dls_upnp_search(g_context.upnp, client, task,
571 prv_async_task_complete);
572 break;
573 case DLS_TASK_GET_RESOURCE:
574 dls_upnp_get_resource(g_context.upnp, client, task,
575 prv_async_task_complete);
576 break;
577 case DLS_TASK_UPLOAD_TO_ANY:
578 dls_upnp_upload_to_any(g_context.upnp, client, task,
579 prv_async_task_complete);
580 break;
581 case DLS_TASK_UPLOAD:
582 dls_upnp_upload(g_context.upnp, client, task,
583 prv_async_task_complete);
584 break;
585 case DLS_TASK_DELETE_OBJECT:
586 dls_upnp_delete_object(g_context.upnp, client, task,
587 prv_async_task_complete);
588 break;
589 case DLS_TASK_CREATE_CONTAINER:
590 dls_upnp_create_container(g_context.upnp, client, task,
591 prv_async_task_complete);
592 break;
593 case DLS_TASK_CREATE_CONTAINER_IN_ANY:
594 dls_upnp_create_container_in_any(g_context.upnp, client, task,
595 prv_async_task_complete);
596 break;
597 case DLS_TASK_UPDATE_OBJECT:
598 dls_upnp_update_object(g_context.upnp, client, task,
599 prv_async_task_complete);
600 break;
601 case DLS_TASK_CREATE_PLAYLIST:
602 dls_upnp_create_playlist(g_context.upnp, client, task,
603 prv_async_task_complete);
604 break;
605 case DLS_TASK_CREATE_PLAYLIST_IN_ANY:
606 dls_upnp_create_playlist_in_any(g_context.upnp, client, task,
607 prv_async_task_complete);
608 break;
609 default:
610 break;
611 }
612
613 DLEYNA_LOG_DEBUG("Exit");
614 }
615
616 static void prv_process_task(dleyna_task_atom_t *task, gpointer user_data)
617 {
618 dls_task_t *client_task = (dls_task_t *)task;
619
620 if (client_task->synchronous)
621 prv_process_sync_task(client_task);
622 else
623 prv_process_async_task(client_task);
624 }
625
626 static void prv_cancel_task(dleyna_task_atom_t *task, gpointer user_data)
627 {
628 dls_task_cancel((dls_task_t *)task);
629 }
630
631 static void prv_delete_task(dleyna_task_atom_t *task, gpointer user_data)
632 {
633 dls_task_delete((dls_task_t *)task);
634 }
635
636 static void prv_method_call(dleyna_connector_id_t conn,
637 const gchar *sender,
638 const gchar *object,
639 const gchar *interface,
640 const gchar *method,
641 GVariant *parameters,
642 dleyna_connector_msg_id_t invocation);
643
644 static void prv_object_method_call(dleyna_connector_id_t conn,
645 const gchar *sender,
646 const gchar *object,
647 const gchar *interface,
648 const gchar *method,
649 GVariant *parameters,
650 dleyna_connector_msg_id_t invocation);
651
652 static void prv_item_method_call(dleyna_connector_id_t conn,
653 const gchar *sender,
654 const gchar *object,
655 const gchar *interface,
656 const gchar *method,
657 GVariant *parameters,
658 dleyna_connector_msg_id_t invocation);
659
660 static void prv_con_method_call(dleyna_connector_id_t conn,
661 const gchar *sender,
662 const gchar *object,
663 const gchar *interface,
664 const gchar *method,
665 GVariant *parameters,
666 dleyna_connector_msg_id_t invocation);
667
668 static void prv_props_method_call(dleyna_connector_id_t conn,
669 const gchar *sender,
670 const gchar *object,
671 const gchar *interface,
672 const gchar *method,
673 GVariant *parameters,
674 dleyna_connector_msg_id_t invocation);
675
676 static void prv_device_method_call(dleyna_connector_id_t conn,
677 const gchar *sender,
678 const gchar *object,
679 const gchar *interface,
680 const gchar *method,
681 GVariant *parameters,
682 dleyna_connector_msg_id_t invocation);
683
684 static const dleyna_connector_dispatch_cb_t g_root_vtables[1] = {
685 prv_method_call
686 };
687
688 static const dleyna_connector_dispatch_cb_t
689 g_server_vtables[DLS_INTERFACE_INFO_MAX] = {
690 /* MUST be in the exact same order as g_dls_server_introspection */
691 prv_props_method_call,
692 prv_object_method_call,
693 prv_con_method_call,
694 prv_item_method_call,
695 prv_device_method_call
696 };
697
698 static void prv_remove_client(const gchar *name)
699 {
700 dleyna_task_processor_remove_queues_for_source(g_context.processor,
701 name);
702
703 (void) g_hash_table_remove(g_context.watchers, name);
704
705 if (g_hash_table_size(g_context.watchers) == 0)
706 if (!dleyna_settings_is_never_quit(g_context.settings))
707 dleyna_task_processor_set_quitting(g_context.processor);
708 }
709
710 static void prv_lost_client(const gchar *name)
711 {
712 DLEYNA_LOG_DEBUG("Lost Client %s", name);
713
714 prv_remove_client(name);
715 }
716
717 static void prv_add_task(dls_task_t *task, const gchar *source,
718 const gchar *sink)
719 {
720 dls_client_t *client;
721 const dleyna_task_queue_key_t *queue_id;
722
723 if (!g_hash_table_lookup(g_context.watchers, source)) {
724 client = g_new0(dls_client_t, 1);
725 client->prefer_local_addresses = TRUE;
726 g_context.connector->watch_client(source);
727 g_hash_table_insert(g_context.watchers, g_strdup(source),
728 client);
729 }
730
731 queue_id = dleyna_task_processor_lookup_queue(g_context.processor,
732 source, sink);
733 if (!queue_id)
734 queue_id = dleyna_task_processor_add_queue(
735 g_context.processor,
736 source,
737 sink,
738 DLEYNA_TASK_QUEUE_FLAG_AUTO_START,
739 prv_process_task,
740 prv_cancel_task,
741 prv_delete_task);
742
743 dleyna_task_queue_add_task(queue_id, &task->atom);
744 }
745
746 static void prv_method_call(dleyna_connector_id_t conn,
747 const gchar *sender, const gchar *object,
748 const gchar *interface,
749 const gchar *method, GVariant *parameters,
750 dleyna_connector_msg_id_t invocation)
751 {
752 dls_task_t *task;
753
754 if (!strcmp(method, DLS_INTERFACE_RELEASE)) {
755 prv_remove_client(sender);
756 g_context.connector->return_response(invocation, NULL);
757 } else if (!strcmp(method, DLS_INTERFACE_GET_VERSION)) {
758 task = dls_task_get_version_new(invocation);
759 prv_add_task(task, sender, DLS_SERVER_SINK);
760 } else if (!strcmp(method, DLS_INTERFACE_GET_SERVERS)) {
761 task = dls_task_get_servers_new(invocation);
762 prv_add_task(task, sender, DLS_SERVER_SINK);
763 } else if (!strcmp(method, DLS_INTERFACE_SET_PROTOCOL_INFO)) {
764 task = dls_task_set_protocol_info_new(invocation,
765 parameters);
766 prv_add_task(task, sender, DLS_SERVER_SINK);
767 } else if (!strcmp(method, DLS_INTERFACE_PREFER_LOCAL_ADDRESSES)) {
768 task = dls_task_prefer_local_addresses_new(invocation,
769 parameters);
770 prv_add_task(task, sender, DLS_SERVER_SINK);
771 }
772 }
773
774 gboolean dls_server_get_object_info(const gchar *object_path,
775 gchar **root_path,
776 gchar **object_id,
777 dls_device_t **device,
778 GError **error)
779 {
780 if (!dls_path_get_path_and_id(object_path, root_path, object_id,
781 error)) {
782 DLEYNA_LOG_WARNING("Bad object %s", object_path);
783
784 goto on_error;
785 }
786
787 *device = dls_device_from_path(*root_path,
788 dls_upnp_get_server_udn_map(g_context.upnp));
789
790 if (*device == NULL) {
791 DLEYNA_LOG_WARNING("Cannot locate device for %s", *root_path);
792
793 *error = g_error_new(DLEYNA_SERVER_ERROR,
794 DLEYNA_ERROR_OBJECT_NOT_FOUND,
795 "Cannot locate device corresponding to"
796 " the specified path");
797
798 g_free(*root_path);
799 g_free(*object_id);
800
801 goto on_error;
802 }
803
804 return TRUE;
805
806 on_error:
807
808 return FALSE;
809 }
810
811 static const gchar *prv_get_device_id(const gchar *object, GError **error)
812 {
813 dls_device_t *device;
814 gchar *root_path;
815 gchar *id;
816
817 if (!dls_server_get_object_info(object, &root_path, &id, &device,
818 error))
819 goto on_error;
820
821 g_free(id);
822 g_free(root_path);
823
824 return device->path;
825
826 on_error:
827
828 return NULL;
829 }
830
831 static void prv_object_method_call(dleyna_connector_id_t conn,
832 const gchar *sender, const gchar *object,
833 const gchar *interface,
834 const gchar *method, GVariant *parameters,
835 dleyna_connector_msg_id_t invocation)
836 {
837 dls_task_t *task;
838 GError *error = NULL;
839
840 if (!strcmp(method, DLS_INTERFACE_DELETE))
841 task = dls_task_delete_new(invocation, object, &error);
842 else if (!strcmp(method, DLS_INTERFACE_UPDATE))
843 task = dls_task_update_new(invocation, object,
844 parameters, &error);
845 else
846 goto finished;
847
848 if (!task) {
849 g_context.connector->return_error(invocation, error);
850 g_error_free(error);
851
852 goto finished;
853 }
854
855 prv_add_task(task, sender, task->target.device->path);
856
857 finished:
858
859 return;
860 }
861
862 static void prv_item_method_call(dleyna_connector_id_t conn,
863 const gchar *sender, const gchar *object,
864 const gchar *interface,
865 const gchar *method, GVariant *parameters,
866 dleyna_connector_msg_id_t invocation)
867 {
868 dls_task_t *task;
869 GError *error = NULL;
870
871 if (!strcmp(method, DLS_INTERFACE_GET_COMPATIBLE_RESOURCE)) {
872 task = dls_task_get_resource_new(invocation, object,
873 parameters, &error);
874
875 if (!task) {
876 g_context.connector->return_error(invocation, error);
877 g_error_free(error);
878
879 goto finished;
880 }
881
882 prv_add_task(task, sender, task->target.device->path);
883 }
884
885 finished:
886
887 return;
888 }
889
890
891 static void prv_con_method_call(dleyna_connector_id_t conn,
892 const gchar *sender,
893 const gchar *object,
894 const gchar *interface,
895 const gchar *method,
896 GVariant *parameters,
897 dleyna_connector_msg_id_t invocation)
898 {
899 dls_task_t *task;
900 GError *error = NULL;
901
902 if (!strcmp(method, DLS_INTERFACE_LIST_CHILDREN))
903 task = dls_task_get_children_new(invocation, object,
904 parameters, TRUE,
905 TRUE, &error);
906 else if (!strcmp(method, DLS_INTERFACE_LIST_CHILDREN_EX))
907 task = dls_task_get_children_ex_new(invocation, object,
908 parameters, TRUE,
909 TRUE, &error);
910 else if (!strcmp(method, DLS_INTERFACE_LIST_ITEMS))
911 task = dls_task_get_children_new(invocation, object,
912 parameters, TRUE,
913 FALSE, &error);
914 else if (!strcmp(method, DLS_INTERFACE_LIST_ITEMS_EX))
915 task = dls_task_get_children_ex_new(invocation, object,
916 parameters, TRUE,
917 FALSE, &error);
918 else if (!strcmp(method, DLS_INTERFACE_LIST_CONTAINERS))
919 task = dls_task_get_children_new(invocation, object,
920 parameters, FALSE,
921 TRUE, &error);
922 else if (!strcmp(method, DLS_INTERFACE_LIST_CONTAINERS_EX))
923 task = dls_task_get_children_ex_new(invocation, object,
924 parameters, FALSE,
925 TRUE, &error);
926 else if (!strcmp(method, DLS_INTERFACE_SEARCH_OBJECTS))
927 task = dls_task_search_new(invocation, object,
928 parameters, &error);
929 else if (!strcmp(method, DLS_INTERFACE_SEARCH_OBJECTS_EX))
930 task = dls_task_search_ex_new(invocation, object,
931 parameters, &error);
932 else if (!strcmp(method, DLS_INTERFACE_UPLOAD))
933 task = dls_task_upload_new(invocation, object,
934 parameters, &error);
935 else if (!strcmp(method, DLS_INTERFACE_CREATE_CONTAINER))
936 task = dls_task_create_container_new_generic(invocation,
937 DLS_TASK_CREATE_CONTAINER,
938 object, parameters, &error);
939 else if (!strcmp(method, DLS_INTERFACE_CREATE_PLAYLIST))
940 task = dls_task_create_playlist_new(invocation,
941 DLS_TASK_CREATE_PLAYLIST,
942 object, parameters, &error);
943 else
944 goto finished;
945
946 if (!task) {
947 g_context.connector->return_error(invocation, error);
948 g_error_free(error);
949
950 goto finished;
951 }
952
953 prv_add_task(task, sender, task->target.device->path);
954
955 finished:
956
957 return;
958 }
959
960 static void prv_props_method_call(dleyna_connector_id_t conn,
961 const gchar *sender,
962 const gchar *object,
963 const gchar *interface,
964 const gchar *method,
965 GVariant *parameters,
966 dleyna_connector_msg_id_t invocation)
967 {
968 dls_task_t *task;
969 GError *error = NULL;
970
971 if (!strcmp(method, DLS_INTERFACE_GET_ALL))
972 task = dls_task_get_props_new(invocation, object,
973 parameters, &error);
974 else if (!strcmp(method, DLS_INTERFACE_GET))
975 task = dls_task_get_prop_new(invocation, object,
976 parameters, &error);
977 else
978 goto finished;
979
980 if (!task) {
981 g_context.connector->return_error(invocation, error);
982 g_error_free(error);
983
984 goto finished;
985 }
986
987 prv_add_task(task, sender, task->target.device->path);
988
989 finished:
990
991 return;
992 }
993
994 static void prv_device_method_call(dleyna_connector_id_t conn,
995 const gchar *sender, const gchar *object,
996 const gchar *interface,
997 const gchar *method, GVariant *parameters,
998 dleyna_connector_msg_id_t invocation)
999 {
1000 dls_task_t *task;
1001 GError *error = NULL;
1002 const gchar *device_id;
1003 const dleyna_task_queue_key_t *queue_id;
1004
1005 if (!strcmp(method, DLS_INTERFACE_UPLOAD_TO_ANY)) {
1006 task = dls_task_upload_to_any_new(invocation,
1007 object, parameters, &error);
1008 } else if (!strcmp(method, DLS_INTERFACE_CREATE_CONTAINER_IN_ANY)) {
1009 task = dls_task_create_container_new_generic(
1010 invocation,
1011 DLS_TASK_CREATE_CONTAINER_IN_ANY,
1012 object, parameters, &error);
1013 } else if (!strcmp(method, DLS_INTERFACE_GET_UPLOAD_STATUS)) {
1014 task = dls_task_get_upload_status_new(invocation,
1015 object, parameters,
1016 &error);
1017 } else if (!strcmp(method, DLS_INTERFACE_GET_UPLOAD_IDS)) {
1018 task = dls_task_get_upload_ids_new(invocation, object,
1019 &error);
1020 } else if (!strcmp(method, DLS_INTERFACE_CANCEL_UPLOAD)) {
1021 task = dls_task_cancel_upload_new(invocation, object,
1022 parameters, &error);
1023 } else if (!strcmp(method, DLS_INTERFACE_CREATE_PLAYLIST_TO_ANY)) {
1024 task = dls_task_create_playlist_new(
1025 invocation,
1026 DLS_TASK_CREATE_PLAYLIST_IN_ANY,
1027 object, parameters, &error);
1028 } else if (!strcmp(method, DLS_INTERFACE_CANCEL)) {
1029 task = NULL;
1030
1031 device_id = prv_get_device_id(object, &error);
1032 if (!device_id)
1033 goto on_error;
1034
1035 queue_id = dleyna_task_processor_lookup_queue(
1036 g_context.processor,
1037 sender,
1038 device_id);
1039 if (queue_id)
1040 dleyna_task_processor_cancel_queue(queue_id);
1041
1042 g_context.connector->return_response(invocation, NULL);
1043
1044 goto finished;
1045 } else {
1046 goto finished;
1047 }
1048
1049 on_error:
1050
1051 if (!task) {
1052 g_context.connector->return_error(invocation, error);
1053 g_error_free(error);
1054
1055 goto finished;
1056 }
1057
1058 prv_add_task(task, sender, task->target.device->path);
1059
1060 finished:
1061
1062 return;
1063 }
1064
1065 static void prv_found_media_server(const gchar *path, void *user_data)
1066 {
1067 (void) g_context.connector->notify(g_context.connection,
1068 DLEYNA_SERVER_OBJECT,
1069 DLEYNA_SERVER_INTERFACE_MANAGER,
1070 DLS_INTERFACE_FOUND_SERVER,
1071 g_variant_new("(o)", path),
1072 NULL);
1073 }
1074
1075 static void prv_lost_media_server(const gchar *path, void *user_data)
1076 {
1077 (void) g_context.connector->notify(g_context.connection,
1078 DLEYNA_SERVER_OBJECT,
1079 DLEYNA_SERVER_INTERFACE_MANAGER,
1080 DLS_INTERFACE_LOST_SERVER,
1081 g_variant_new("(o)", path),
1082 NULL);
1083
1084 dleyna_task_processor_remove_queues_for_sink(g_context.processor, path);
1085 }
1086
1087 static void prv_unregister_client(gpointer user_data)
1088 {
1089 dls_client_t *client = user_data;
1090
1091 if (client) {
1092 g_free(client->protocol_info);
1093 g_free(client);
1094 }
1095 }
1096
1097 dls_upnp_t *dls_server_get_upnp(void)
1098 {
1099 return g_context.upnp;
1100 }
1101
1102 static gboolean prv_control_point_start_service(
1103 dleyna_connector_id_t connection)
1104 {
1105 gboolean retval = TRUE;
1106
1107 g_context.connection = connection;
1108
1109 g_context.dls_id = g_context.connector->publish_object(
1110 connection,
1111 DLEYNA_SERVER_OBJECT,
1112 TRUE,
1113 0,
1114 g_root_vtables);
1115
1116 if (!g_context.dls_id) {
1117 retval = FALSE;
1118 goto out;
1119 } else {
1120 g_context.upnp = dls_upnp_new(connection,
1121 g_server_vtables,
1122 prv_found_media_server,
1123 prv_lost_media_server,
1124 NULL);
1125 }
1126
1127 out:
1128
1129 return retval;
1130 }
1131
1132 static void prv_control_point_stop_service(void)
1133 {
1134 dls_upnp_unsubscribe(g_context.upnp);
1135
1136 dls_upnp_delete(g_context.upnp);
1137
1138 if (g_context.connection) {
1139 if (g_context.dls_id)
1140 g_context.connector->unpublish_object(
1141 g_context.connection,
1142 g_context.dls_id);
1143 }
1144 }
1145
1146 static void prv_control_point_initialize(const dleyna_connector_t *connector,
1147 dleyna_task_processor_t *processor,
1148 dleyna_settings_t *settings)
1149 {
1150 memset(&g_context, 0, sizeof(g_context));
1151
1152 g_context.connector = connector;
1153 g_context.processor = processor;
1154 g_context.settings = settings;
1155
1156 g_context.connector->set_client_lost_cb(prv_lost_client);
1157
1158 g_context.watchers = g_hash_table_new_full(g_str_hash, g_str_equal,
1159 g_free, prv_unregister_client);
1160
1161 g_set_prgname(DLS_PRG_NAME);
1162 }
1163
1164 static void prv_control_point_free(void)
1165 {
1166 if (g_context.watchers)
1167 g_hash_table_unref(g_context.watchers);
1168 }
1169
1170 static const gchar *prv_control_point_server_name(void)
1171 {
1172 return DLEYNA_SERVER_NAME;
1173 }
1174
1175 static const gchar *prv_control_point_server_introspection(void)
1176 {
1177 return g_server_introspection;
1178 }
1179
1180 static const gchar *prv_control_point_root_introspection(void)
1181 {
1182 return g_root_introspection;
1183 }
1184
1185 static const dleyna_control_point_t g_control_point = {
1186 prv_control_point_initialize,
1187 prv_control_point_free,
1188 prv_control_point_server_name,
1189 prv_control_point_server_introspection,
1190 prv_control_point_root_introspection,
1191 prv_control_point_start_service,
1192 prv_control_point_stop_service
1193 };
1194
1195 const dleyna_control_point_t *dleyna_control_point_get_server(void)
1196 {
1197 return &g_control_point;
1198 }
0 /*
1 * dLeyna
2 *
3 * Copyright (C) 2013 Intel Corporation. All rights reserved.
4 *
5 * This program is free software; you can redistribute it and/or modify it
6 * under the terms and conditions of the GNU Lesser General Public License,
7 * version 2.1, as published by the Free Software Foundation.
8 *
9 * This program is distributed in the hope it will be useful, but WITHOUT
10 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
11 * FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License
12 * for more details.
13 *
14 * You should have received a copy of the GNU Lesser General Public License
15 * along with this program; if not, write to the Free Software Foundation, Inc.,
16 * 51 Franklin St - Fifth Floor, Boston, MA 02110-1301 USA.
17 *
18 * Regis Merlino <regis.merlino@intel.com>
19 *
20 */
21
22 #ifndef DLS_SERVER_H__
23 #define DLS_SERVER_H__
24
25 #include <libdleyna/core/connector.h>
26 #include <libdleyna/core/task-processor.h>
27
28 #define DLS_SERVER_SINK "dleyna-server"
29
30 typedef struct dls_device_t_ dls_device_t;
31 typedef struct dls_device_context_t_ dls_device_context_t;
32 typedef struct dls_upnp_t_ dls_upnp_t;
33
34 gboolean dls_server_get_object_info(const gchar *object_path,
35 gchar **root_path,
36 gchar **object_id,
37 dls_device_t **device,
38 GError **error);
39
40 dls_upnp_t *dls_server_get_upnp(void);
41
42 dleyna_task_processor_t *dls_server_get_task_processor(void);
43
44 const dleyna_connector_t *dls_server_get_connector(void);
45
46 #endif /* DLS_SERVER_H__ */
0 /*
1 * dLeyna
2 *
3 * Copyright (C) 2012-2013 Intel Corporation. All rights reserved.
4 *
5 * This program is free software; you can redistribute it and/or modify it
6 * under the terms and conditions of the GNU Lesser General Public License,
7 * version 2.1, as published by the Free Software Foundation.
8 *
9 * This program is distributed in the hope it will be useful, but WITHOUT
10 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
11 * FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License
12 * for more details.
13 *
14 * You should have received a copy of the GNU Lesser General Public License
15 * along with this program; if not, write to the Free Software Foundation, Inc.,
16 * 51 Franklin St - Fifth Floor, Boston, MA 02110-1301 USA.
17 *
18 * Mark Ryan <mark.d.ryan@intel.com>
19 *
20 */
21
22 #include <string.h>
23
24 #include "props.h"
25 #include "sort.h"
26
27 gchar *dls_sort_translate_sort_string(GHashTable *filter_map,
28 const gchar *sort_string)
29 {
30 GRegex *reg;
31 gchar *retval = NULL;
32 GMatchInfo *match_info = NULL;
33 gchar *prop = NULL;
34 gchar *op = NULL;
35 dls_prop_map_t *prop_map;
36 GString *str;
37
38 if (!g_regex_match_simple(
39 "^((\\+|\\-)([^,\\+\\-]+))?(,(\\+|\\-)([^,\\+\\-]+))*$",
40 sort_string, 0, 0))
41 goto no_free;
42
43 reg = g_regex_new("(\\+|\\-)(\\w+)", 0, 0, NULL);
44 str = g_string_new("");
45
46 g_regex_match(reg, sort_string, 0, &match_info);
47 while (g_match_info_matches(match_info)) {
48 op = g_match_info_fetch(match_info, 1);
49 if (!op)
50 goto on_error;
51
52 prop = g_match_info_fetch(match_info, 2);
53 if (!prop)
54 goto on_error;
55
56 prop_map = g_hash_table_lookup(filter_map, prop);
57 if (!prop_map)
58 goto on_error;
59
60 if (!prop_map->searchable)
61 goto on_error;
62
63 g_string_append_printf(str, "%s%s,", op,
64 prop_map->upnp_prop_name);
65
66 g_free(prop);
67 g_free(op);
68
69 prop = NULL;
70 op = NULL;
71
72 g_match_info_next(match_info, NULL);
73 }
74
75 if (str->len > 0)
76 str = g_string_truncate(str, str->len - 1);
77 retval = g_string_free(str, FALSE);
78
79 str = NULL;
80
81 on_error:
82
83 g_free(prop);
84 g_free(op);
85
86 if (match_info)
87 g_match_info_free(match_info);
88
89 if (str)
90 g_string_free(str, TRUE);
91
92 g_regex_unref(reg);
93
94 no_free:
95
96 return retval;
97 }
0 /*
1 * dLeyna
2 *
3 * Copyright (C) 2012-2013 Intel Corporation. All rights reserved.
4 *
5 * This program is free software; you can redistribute it and/or modify it
6 * under the terms and conditions of the GNU Lesser General Public License,
7 * version 2.1, as published by the Free Software Foundation.
8 *
9 * This program is distributed in the hope it will be useful, but WITHOUT
10 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
11 * FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License
12 * for more details.
13 *
14 * You should have received a copy of the GNU Lesser General Public License
15 * along with this program; if not, write to the Free Software Foundation, Inc.,
16 * 51 Franklin St - Fifth Floor, Boston, MA 02110-1301 USA.
17 *
18 * Mark Ryan <mark.d.ryan@intel.com>
19 *
20 */
21
22 #ifndef DLS_SORT_H__
23 #define DLS_SORT_H__
24
25 #include <glib.h>
26
27 gchar *dls_sort_translate_sort_string(GHashTable *filter_map,
28 const gchar *sort_string);
29
30 #endif /* DLS_SORT_H__ */
0 /*
1 * dLeyna
2 *
3 * Copyright (C) 2012-2013 Intel Corporation. All rights reserved.
4 *
5 * This program is free software; you can redistribute it and/or modify it
6 * under the terms and conditions of the GNU Lesser General Public License,
7 * version 2.1, as published by the Free Software Foundation.
8 *
9 * This program is distributed in the hope it will be useful, but WITHOUT
10 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
11 * FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License
12 * for more details.
13 *
14 * You should have received a copy of the GNU Lesser General Public License
15 * along with this program; if not, write to the Free Software Foundation, Inc.,
16 * 51 Franklin St - Fifth Floor, Boston, MA 02110-1301 USA.
17 *
18 * Mark Ryan <mark.d.ryan@intel.com>
19 *
20 */
21
22 #include <libdleyna/core/error.h>
23
24 #include "async.h"
25
26 dls_task_t *dls_task_get_version_new(dleyna_connector_msg_id_t invocation)
27 {
28 dls_task_t *task = g_new0(dls_task_t, 1);
29
30 task->type = DLS_TASK_GET_VERSION;
31 task->invocation = invocation;
32 task->result_format = "(@s)";
33 task->result = g_variant_ref_sink(g_variant_new_string(VERSION));
34 task->synchronous = TRUE;
35
36 return task;
37 }
38
39 dls_task_t *dls_task_get_servers_new(dleyna_connector_msg_id_t invocation)
40 {
41 dls_task_t *task = g_new0(dls_task_t, 1);
42
43 task->type = DLS_TASK_GET_SERVERS;
44 task->invocation = invocation;
45 task->result_format = "(@ao)";
46 task->synchronous = TRUE;
47
48 return task;
49 }
50
51 static void prv_delete(dls_task_t *task)
52 {
53 if (!task->synchronous)
54 dls_async_task_delete((dls_async_task_t *)task);
55
56 switch (task->type) {
57 case DLS_TASK_GET_CHILDREN:
58 if (task->ut.get_children.filter)
59 g_variant_unref(task->ut.get_children.filter);
60 g_free(task->ut.get_children.sort_by);
61 break;
62 case DLS_TASK_GET_ALL_PROPS:
63 g_free(task->ut.get_props.interface_name);
64 break;
65 case DLS_TASK_GET_PROP:
66 g_free(task->ut.get_prop.interface_name);
67 g_free(task->ut.get_prop.prop_name);
68 break;
69 case DLS_TASK_SEARCH:
70 g_free(task->ut.search.query);
71 if (task->ut.search.filter)
72 g_variant_unref(task->ut.search.filter);
73 g_free(task->ut.search.sort_by);
74 break;
75 case DLS_TASK_GET_RESOURCE:
76 if (task->ut.resource.filter)
77 g_variant_unref(task->ut.resource.filter);
78 g_free(task->ut.resource.protocol_info);
79 break;
80 case DLS_TASK_SET_PROTOCOL_INFO:
81 if (task->ut.protocol_info.protocol_info)
82 g_free(task->ut.protocol_info.protocol_info);
83 break;
84 case DLS_TASK_UPLOAD_TO_ANY:
85 case DLS_TASK_UPLOAD:
86 g_free(task->ut.upload.display_name);
87 g_free(task->ut.upload.file_path);
88 break;
89 case DLS_TASK_CREATE_CONTAINER:
90 case DLS_TASK_CREATE_CONTAINER_IN_ANY:
91 g_free(task->ut.create_container.display_name);
92 g_free(task->ut.create_container.type);
93 g_variant_unref(task->ut.create_container.child_types);
94 break;
95 case DLS_TASK_UPDATE_OBJECT:
96 if (task->ut.update.to_add_update)
97 g_variant_unref(task->ut.update.to_add_update);
98 if (task->ut.update.to_delete)
99 g_variant_unref(task->ut.update.to_delete);
100 break;
101 case DLS_TASK_CREATE_PLAYLIST:
102 case DLS_TASK_CREATE_PLAYLIST_IN_ANY:
103 g_free(task->ut.playlist.title);
104 g_free(task->ut.playlist.creator);
105 g_free(task->ut.playlist.genre);
106 g_free(task->ut.playlist.desc);
107 if (task->ut.playlist.item_path)
108 g_variant_unref(task->ut.playlist.item_path);
109 break;
110 default:
111 break;
112 }
113
114 g_free(task->target.path);
115 g_free(task->target.root_path);
116 g_free(task->target.id);
117
118 if (task->result)
119 g_variant_unref(task->result);
120
121 g_free(task);
122 }
123
124 static gboolean prv_set_task_target_info(dls_task_t *task, const gchar *path,
125 GError **error)
126 {
127 task->target.path = g_strdup(path);
128 g_strstrip(task->target.path);
129
130 return dls_server_get_object_info(path, &task->target.root_path,
131 &task->target.id,
132 &task->target.device, error);
133 }
134
135 static dls_task_t *prv_m2spec_task_new(dls_task_type_t type,
136 dleyna_connector_msg_id_t invocation,
137 const gchar *path,
138 const gchar *result_format,
139 GError **error,
140 gboolean synchronous)
141 {
142 dls_task_t *task;
143
144 if (synchronous) {
145 task = g_new0(dls_task_t, 1);
146 task->synchronous = TRUE;
147 } else {
148 task = (dls_task_t *)g_new0(dls_async_task_t, 1);
149 }
150
151 if (!prv_set_task_target_info(task, path, error)) {
152 prv_delete(task);
153 task = NULL;
154
155 goto finished;
156 }
157
158 task->type = type;
159 task->invocation = invocation;
160 task->result_format = result_format;
161
162 finished:
163
164 return task;
165 }
166
167 dls_task_t *dls_task_get_children_new(dleyna_connector_msg_id_t invocation,
168 const gchar *path, GVariant *parameters,
169 gboolean items, gboolean containers,
170 GError **error)
171 {
172 dls_task_t *task;
173
174 task = prv_m2spec_task_new(DLS_TASK_GET_CHILDREN, invocation, path,
175 "(@aa{sv})", error, FALSE);
176 if (!task)
177 goto finished;
178
179 task->ut.get_children.containers = containers;
180 task->ut.get_children.items = items;
181
182 g_variant_get(parameters, "(uu@as)",
183 &task->ut.get_children.start,
184 &task->ut.get_children.count,
185 &task->ut.get_children.filter);
186
187 task->ut.get_children.sort_by = g_strdup("");
188
189 finished:
190
191 return task;
192 }
193
194 dls_task_t *dls_task_get_children_ex_new(dleyna_connector_msg_id_t invocation,
195 const gchar *path,
196 GVariant *parameters, gboolean items,
197 gboolean containers,
198 GError **error)
199 {
200 dls_task_t *task;
201
202 task = prv_m2spec_task_new(DLS_TASK_GET_CHILDREN, invocation, path,
203 "(@aa{sv})", error, FALSE);
204 if (!task)
205 goto finished;
206
207 task->ut.get_children.containers = containers;
208 task->ut.get_children.items = items;
209
210 g_variant_get(parameters, "(uu@ass)",
211 &task->ut.get_children.start,
212 &task->ut.get_children.count,
213 &task->ut.get_children.filter,
214 &task->ut.get_children.sort_by);
215
216 finished:
217
218 return task;
219 }
220
221 dls_task_t *dls_task_get_prop_new(dleyna_connector_msg_id_t invocation,
222 const gchar *path, GVariant *parameters,
223 GError **error)
224 {
225 dls_task_t *task;
226
227 task = prv_m2spec_task_new(DLS_TASK_GET_PROP, invocation, path, "(v)",
228 error, FALSE);
229 if (!task)
230 goto finished;
231
232 g_variant_get(parameters, "(ss)", &task->ut.get_prop.interface_name,
233 &task->ut.get_prop.prop_name);
234
235 g_strstrip(task->ut.get_prop.interface_name);
236 g_strstrip(task->ut.get_prop.prop_name);
237
238 finished:
239
240 return task;
241 }
242
243 dls_task_t *dls_task_get_props_new(dleyna_connector_msg_id_t invocation,
244 const gchar *path, GVariant *parameters,
245 GError **error)
246 {
247 dls_task_t *task;
248
249 task = prv_m2spec_task_new(DLS_TASK_GET_ALL_PROPS, invocation, path,
250 "(@a{sv})", error, FALSE);
251 if (!task)
252 goto finished;
253
254 g_variant_get(parameters, "(s)", &task->ut.get_props.interface_name);
255 g_strstrip(task->ut.get_props.interface_name);
256
257 finished:
258
259 return task;
260 }
261
262 dls_task_t *dls_task_search_new(dleyna_connector_msg_id_t invocation,
263 const gchar *path, GVariant *parameters,
264 GError **error)
265 {
266 dls_task_t *task;
267
268 task = prv_m2spec_task_new(DLS_TASK_SEARCH, invocation, path,
269 "(@aa{sv})", error, FALSE);
270 if (!task)
271 goto finished;
272
273 g_variant_get(parameters, "(suu@as)", &task->ut.search.query,
274 &task->ut.search.start, &task->ut.search.count,
275 &task->ut.search.filter);
276
277 task->ut.search.sort_by = g_strdup("");
278
279 finished:
280 return task;
281 }
282
283 dls_task_t *dls_task_search_ex_new(dleyna_connector_msg_id_t invocation,
284 const gchar *path, GVariant *parameters,
285 GError **error)
286 {
287 dls_task_t *task;
288
289 task = prv_m2spec_task_new(DLS_TASK_SEARCH, invocation, path,
290 "(@aa{sv}u)", error, FALSE);
291 if (!task)
292 goto finished;
293
294 g_variant_get(parameters, "(suu@ass)", &task->ut.search.query,
295 &task->ut.search.start, &task->ut.search.count,
296 &task->ut.search.filter, &task->ut.search.sort_by);
297
298 task->multiple_retvals = TRUE;
299
300 finished:
301
302 return task;
303 }
304
305 dls_task_t *dls_task_get_resource_new(dleyna_connector_msg_id_t invocation,
306 const gchar *path, GVariant *parameters,
307 GError **error)
308 {
309 dls_task_t *task;
310
311 task = prv_m2spec_task_new(DLS_TASK_GET_RESOURCE, invocation, path,
312 "(@a{sv})", error, FALSE);
313 if (!task)
314 goto finished;
315
316 g_variant_get(parameters, "(s@as)",
317 &task->ut.resource.protocol_info,
318 &task->ut.resource.filter);
319
320 finished:
321
322 return task;
323 }
324
325 dls_task_t *dls_task_set_protocol_info_new(dleyna_connector_msg_id_t invocation,
326 GVariant *parameters)
327 {
328 dls_task_t *task = g_new0(dls_task_t, 1);
329
330 task->type = DLS_TASK_SET_PROTOCOL_INFO;
331 task->invocation = invocation;
332 task->synchronous = TRUE;
333 g_variant_get(parameters, "(s)", &task->ut.protocol_info.protocol_info);
334
335 return task;
336 }
337
338 static dls_task_t *prv_upload_new_generic(dls_task_type_t type,
339 dleyna_connector_msg_id_t invocation,
340 const gchar *path,
341 GVariant *parameters,
342 GError **error)
343 {
344 dls_task_t *task;
345
346 task = prv_m2spec_task_new(type, invocation, path,
347 "(uo)", error, FALSE);
348 if (!task)
349 goto finished;
350
351 g_variant_get(parameters, "(ss)", &task->ut.upload.display_name,
352 &task->ut.upload.file_path);
353 g_strstrip(task->ut.upload.file_path);
354 task->multiple_retvals = TRUE;
355
356 finished:
357
358 return task;
359 }
360
361 dls_task_t *dls_task_prefer_local_addresses_new(
362 dleyna_connector_msg_id_t invocation,
363 GVariant *parameters)
364 {
365 dls_task_t *task = g_new0(dls_task_t, 1);
366
367 task->type = DLS_TASK_SET_PREFER_LOCAL_ADDRESSES;
368 task->invocation = invocation;
369 task->synchronous = TRUE;
370 g_variant_get(parameters, "(b)",
371 &task->ut.prefer_local_addresses.prefer);
372
373 return task;
374 }
375
376 dls_task_t *dls_task_upload_to_any_new(dleyna_connector_msg_id_t invocation,
377 const gchar *path, GVariant *parameters,
378 GError **error)
379 {
380 return prv_upload_new_generic(DLS_TASK_UPLOAD_TO_ANY, invocation,
381 path, parameters, error);
382 }
383
384 dls_task_t *dls_task_upload_new(dleyna_connector_msg_id_t invocation,
385 const gchar *path, GVariant *parameters,
386 GError **error)
387 {
388 return prv_upload_new_generic(DLS_TASK_UPLOAD, invocation,
389 path, parameters, error);
390 }
391
392 dls_task_t *dls_task_get_upload_status_new(dleyna_connector_msg_id_t invocation,
393 const gchar *path,
394 GVariant *parameters,
395 GError **error)
396 {
397 dls_task_t *task;
398
399 task = prv_m2spec_task_new(DLS_TASK_GET_UPLOAD_STATUS, invocation, path,
400 "(stt)", error, TRUE);
401 if (!task)
402 goto finished;
403
404 g_variant_get(parameters, "(u)",
405 &task->ut.upload_action.upload_id);
406 task->multiple_retvals = TRUE;
407
408 finished:
409
410 return task;
411 }
412
413 dls_task_t *dls_task_get_upload_ids_new(dleyna_connector_msg_id_t invocation,
414 const gchar *path,
415 GError **error)
416 {
417 dls_task_t *task;
418
419 task = prv_m2spec_task_new(DLS_TASK_GET_UPLOAD_IDS, invocation, path,
420 "(@au)", error, TRUE);
421 if (!task)
422 goto finished;
423
424 finished:
425
426 return task;
427 }
428
429 dls_task_t *dls_task_cancel_upload_new(dleyna_connector_msg_id_t invocation,
430 const gchar *path,
431 GVariant *parameters,
432 GError **error)
433 {
434 dls_task_t *task;
435
436 task = prv_m2spec_task_new(DLS_TASK_CANCEL_UPLOAD, invocation, path,
437 NULL, error, TRUE);
438 if (!task)
439 goto finished;
440
441 g_variant_get(parameters, "(u)",
442 &task->ut.upload_action.upload_id);
443
444 finished:
445
446 return task;
447 }
448
449 dls_task_t *dls_task_delete_new(dleyna_connector_msg_id_t invocation,
450 const gchar *path,
451 GError **error)
452 {
453 dls_task_t *task;
454
455 task = prv_m2spec_task_new(DLS_TASK_DELETE_OBJECT, invocation,
456 path, NULL, error, FALSE);
457 return task;
458 }
459
460 dls_task_t *dls_task_create_container_new_generic(
461 dleyna_connector_msg_id_t invocation,
462 dls_task_type_t type,
463 const gchar *path,
464 GVariant *parameters,
465 GError **error)
466 {
467 dls_task_t *task;
468
469 task = prv_m2spec_task_new(type, invocation, path,
470 "(@o)", error, FALSE);
471 if (!task)
472 goto finished;
473
474 g_variant_get(parameters, "(ss@as)",
475 &task->ut.create_container.display_name,
476 &task->ut.create_container.type,
477 &task->ut.create_container.child_types);
478
479 finished:
480
481 return task;
482 }
483
484 dls_task_t *dls_task_create_playlist_new(dleyna_connector_msg_id_t invocation,
485 dls_task_type_t type,
486 const gchar *path,
487 GVariant *parameters,
488 GError **error)
489 {
490 dls_task_t *task;
491
492 task = prv_m2spec_task_new(type, invocation, path,
493 "(uo)", error, FALSE);
494 if (!task)
495 goto finished;
496
497 g_variant_get(parameters, "(ssss@ao)",
498 &task->ut.playlist.title,
499 &task->ut.playlist.creator,
500 &task->ut.playlist.genre,
501 &task->ut.playlist.desc,
502 &task->ut.playlist.item_path);
503
504 task->multiple_retvals = TRUE;
505
506 finished:
507
508 return task;
509
510 }
511
512 dls_task_t *dls_task_update_new(dleyna_connector_msg_id_t invocation,
513 const gchar *path, GVariant *parameters,
514 GError **error)
515 {
516 dls_task_t *task;
517
518 task = prv_m2spec_task_new(DLS_TASK_UPDATE_OBJECT, invocation, path,
519 NULL, error, FALSE);
520 if (!task)
521 goto finished;
522
523 g_variant_get(parameters, "(@a{sv}@as)",
524 &task->ut.update.to_add_update,
525 &task->ut.update.to_delete);
526
527 finished:
528
529 return task;
530 }
531
532 void dls_task_complete(dls_task_t *task)
533 {
534 GVariant *variant = NULL;
535
536 if (!task)
537 goto finished;
538
539 if (task->invocation) {
540 if (task->result_format) {
541 if (task->multiple_retvals)
542 variant = task->result;
543 else
544 variant = g_variant_new(task->result_format,
545 task->result);
546 }
547 dls_server_get_connector()->return_response(task->invocation,
548 variant);
549 task->invocation = NULL;
550 }
551
552 finished:
553
554 return;
555 }
556
557 void dls_task_fail(dls_task_t *task, GError *error)
558 {
559 if (!task)
560 goto finished;
561
562 if (task->invocation) {
563 dls_server_get_connector()->return_error(task->invocation,
564 error);
565 task->invocation = NULL;
566 }
567
568 finished:
569
570 return;
571 }
572
573 void dls_task_cancel(dls_task_t *task)
574 {
575 GError *error;
576
577 if (!task)
578 goto finished;
579
580 if (task->invocation) {
581 error = g_error_new(DLEYNA_SERVER_ERROR, DLEYNA_ERROR_CANCELLED,
582 "Operation cancelled.");
583 dls_server_get_connector()->return_error(task->invocation,
584 error);
585 task->invocation = NULL;
586 g_error_free(error);
587 }
588
589 if (!task->synchronous)
590 dls_async_task_cancel((dls_async_task_t *)task);
591
592 finished:
593
594 return;
595 }
596
597 void dls_task_delete(dls_task_t *task)
598 {
599 GError *error;
600
601 if (!task)
602 goto finished;
603
604 if (task->invocation) {
605 error = g_error_new(DLEYNA_SERVER_ERROR, DLEYNA_ERROR_DIED,
606 "Unable to complete command.");
607 dls_server_get_connector()->return_error(task->invocation,
608 error);
609 g_error_free(error);
610 }
611
612 prv_delete(task);
613
614 finished:
615
616 return;
617 }
0 /*
1 * dLeyna
2 *
3 * Copyright (C) 2012-2013 Intel Corporation. All rights reserved.
4 *
5 * This program is free software; you can redistribute it and/or modify it
6 * under the terms and conditions of the GNU Lesser General Public License,
7 * version 2.1, as published by the Free Software Foundation.
8 *
9 * This program is distributed in the hope it will be useful, but WITHOUT
10 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
11 * FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License
12 * for more details.
13 *
14 * You should have received a copy of the GNU Lesser General Public License
15 * along with this program; if not, write to the Free Software Foundation, Inc.,
16 * 51 Franklin St - Fifth Floor, Boston, MA 02110-1301 USA.
17 *
18 * Mark Ryan <mark.d.ryan@intel.com>
19 *
20 */
21
22 #ifndef DLS_TASK_H__
23 #define DLS_TASK_H__
24
25 #include <gio/gio.h>
26 #include <glib.h>
27
28 #include <libdleyna/core/connector.h>
29 #include <libdleyna/core/task-atom.h>
30
31 #include "server.h"
32
33 enum dls_task_type_t_ {
34 DLS_TASK_GET_VERSION,
35 DLS_TASK_GET_SERVERS,
36 DLS_TASK_GET_CHILDREN,
37 DLS_TASK_GET_ALL_PROPS,
38 DLS_TASK_GET_PROP,
39 DLS_TASK_SEARCH,
40 DLS_TASK_GET_RESOURCE,
41 DLS_TASK_SET_PREFER_LOCAL_ADDRESSES,
42 DLS_TASK_SET_PROTOCOL_INFO,
43 DLS_TASK_UPLOAD_TO_ANY,
44 DLS_TASK_UPLOAD,
45 DLS_TASK_GET_UPLOAD_STATUS,
46 DLS_TASK_GET_UPLOAD_IDS,
47 DLS_TASK_CANCEL_UPLOAD,
48 DLS_TASK_DELETE_OBJECT,
49 DLS_TASK_CREATE_CONTAINER,
50 DLS_TASK_CREATE_CONTAINER_IN_ANY,
51 DLS_TASK_UPDATE_OBJECT,
52 DLS_TASK_CREATE_PLAYLIST,
53 DLS_TASK_CREATE_PLAYLIST_IN_ANY
54 };
55 typedef enum dls_task_type_t_ dls_task_type_t;
56
57 typedef void (*dls_cancel_task_t)(void *handle);
58
59 typedef struct dls_task_get_children_t_ dls_task_get_children_t;
60 struct dls_task_get_children_t_ {
61 gboolean containers;
62 gboolean items;
63 guint start;
64 guint count;
65 GVariant *filter;
66 gchar *sort_by;
67 };
68
69 typedef struct dls_task_get_props_t_ dls_task_get_props_t;
70 struct dls_task_get_props_t_ {
71 gchar *interface_name;
72 };
73
74 typedef struct dls_task_get_prop_t_ dls_task_get_prop_t;
75 struct dls_task_get_prop_t_ {
76 gchar *prop_name;
77 gchar *interface_name;
78 };
79
80 typedef struct dls_task_search_t_ dls_task_search_t;
81 struct dls_task_search_t_ {
82 gchar *query;
83 guint start;
84 guint count;
85 gchar *sort_by;
86 GVariant *filter;
87 };
88
89 typedef struct dls_task_get_resource_t_ dls_task_get_resource_t;
90 struct dls_task_get_resource_t_ {
91 gchar *protocol_info;
92 GVariant *filter;
93 };
94
95 typedef struct dls_task_set_prefer_local_addresses_t_
96 dls_task_set_prefer_local_addresses_t;
97 struct dls_task_set_prefer_local_addresses_t_ {
98 gboolean prefer;
99 };
100
101 typedef struct dls_task_set_protocol_info_t_ dls_task_set_protocol_info_t;
102 struct dls_task_set_protocol_info_t_ {
103 gchar *protocol_info;
104 };
105
106 typedef struct dls_task_upload_t_ dls_task_upload_t;
107 struct dls_task_upload_t_ {
108 gchar *display_name;
109 gchar *file_path;
110 };
111
112 typedef struct dls_task_upload_action_t_ dls_task_upload_action_t;
113 struct dls_task_upload_action_t_ {
114 guint upload_id;
115 };
116
117 typedef struct dls_task_create_container_t_ dls_task_create_container_t;
118 struct dls_task_create_container_t_ {
119 gchar *display_name;
120 gchar *type;
121 GVariant *child_types;
122 };
123
124 typedef struct dls_task_update_t_ dls_task_update_t;
125 struct dls_task_update_t_ {
126 GVariant *to_add_update;
127 GVariant *to_delete;
128 };
129
130 typedef struct dls_task_create_playlist_t_ dls_task_create_playlist_t;
131 struct dls_task_create_playlist_t_ {
132 gchar *title;
133 gchar *creator;
134 gchar *genre;
135 gchar *desc;
136 GVariant *item_path;
137 };
138
139 typedef struct dls_task_target_info_t_ dls_task_target_info_t;
140 struct dls_task_target_info_t_ {
141 gchar *path;
142 gchar *root_path;
143 gchar *id;
144 dls_device_t *device;
145 };
146
147 typedef struct dls_task_t_ dls_task_t;
148 struct dls_task_t_ {
149 dleyna_task_atom_t atom; /* pseudo inheritance - MUST be first field */
150 dls_task_type_t type;
151 dls_task_target_info_t target;
152 const gchar *result_format;
153 GVariant *result;
154 dleyna_connector_msg_id_t invocation;
155 gboolean synchronous;
156 gboolean multiple_retvals;
157 union {
158 dls_task_get_children_t get_children;
159 dls_task_get_props_t get_props;
160 dls_task_get_prop_t get_prop;
161 dls_task_search_t search;
162 dls_task_get_resource_t resource;
163 dls_task_set_prefer_local_addresses_t prefer_local_addresses;
164 dls_task_set_protocol_info_t protocol_info;
165 dls_task_upload_t upload;
166 dls_task_upload_action_t upload_action;
167 dls_task_create_container_t create_container;
168 dls_task_update_t update;
169 dls_task_create_playlist_t playlist;
170 } ut;
171 };
172
173 dls_task_t *dls_task_get_version_new(dleyna_connector_msg_id_t invocation);
174
175 dls_task_t *dls_task_get_servers_new(dleyna_connector_msg_id_t invocation);
176
177 dls_task_t *dls_task_get_children_new(dleyna_connector_msg_id_t invocation,
178 const gchar *path, GVariant *parameters,
179 gboolean items, gboolean containers,
180 GError **error);
181
182 dls_task_t *dls_task_get_children_ex_new(dleyna_connector_msg_id_t invocation,
183 const gchar *path,
184 GVariant *parameters, gboolean items,
185 gboolean containers,
186 GError **error);
187
188 dls_task_t *dls_task_get_prop_new(dleyna_connector_msg_id_t invocation,
189 const gchar *path, GVariant *parameters,
190 GError **error);
191
192 dls_task_t *dls_task_get_props_new(dleyna_connector_msg_id_t invocation,
193 const gchar *path, GVariant *parameters,
194 GError **error);
195
196 dls_task_t *dls_task_search_new(dleyna_connector_msg_id_t invocation,
197 const gchar *path, GVariant *parameters,
198 GError **error);
199
200 dls_task_t *dls_task_search_ex_new(dleyna_connector_msg_id_t invocation,
201 const gchar *path, GVariant *parameters,
202 GError **error);
203
204 dls_task_t *dls_task_get_resource_new(dleyna_connector_msg_id_t invocation,
205 const gchar *path, GVariant *parameters,
206 GError **error);
207
208 dls_task_t *dls_task_set_protocol_info_new(dleyna_connector_msg_id_t invocation,
209 GVariant *parameters);
210
211 dls_task_t *dls_task_prefer_local_addresses_new(
212 dleyna_connector_msg_id_t invocation,
213 GVariant *parameters);
214
215 dls_task_t *dls_task_upload_to_any_new(dleyna_connector_msg_id_t invocation,
216 const gchar *path, GVariant *parameters,
217 GError **error);
218
219 dls_task_t *dls_task_upload_new(dleyna_connector_msg_id_t invocation,
220 const gchar *path, GVariant *parameters,
221 GError **error);
222
223 dls_task_t *dls_task_get_upload_status_new(dleyna_connector_msg_id_t invocation,
224 const gchar *path,
225 GVariant *parameters,
226 GError **error);
227
228 dls_task_t *dls_task_get_upload_ids_new(dleyna_connector_msg_id_t invocation,
229 const gchar *path,
230 GError **error);
231
232 dls_task_t *dls_task_cancel_upload_new(dleyna_connector_msg_id_t invocation,
233 const gchar *path,
234 GVariant *parameters,
235 GError **error);
236
237 dls_task_t *dls_task_delete_new(dleyna_connector_msg_id_t invocation,
238 const gchar *path,
239 GError **error);
240
241 dls_task_t *dls_task_create_container_new_generic(
242 dleyna_connector_msg_id_t invocation,
243 dls_task_type_t type,
244 const gchar *path,
245 GVariant *parameters,
246 GError **error);
247
248 dls_task_t *dls_task_create_playlist_new(dleyna_connector_msg_id_t invocation,
249 dls_task_type_t type,
250 const gchar *path,
251 GVariant *parameters,
252 GError **error);
253
254 dls_task_t *dls_task_update_new(dleyna_connector_msg_id_t invocation,
255 const gchar *path, GVariant *parameters,
256 GError **error);
257
258 void dls_task_cancel(dls_task_t *task);
259
260 void dls_task_complete(dls_task_t *task);
261
262 void dls_task_fail(dls_task_t *task, GError *error);
263
264 void dls_task_delete(dls_task_t *task);
265
266 #endif /* DLS_TASK_H__ */
0 /*
1 * dLeyna
2 *
3 * Copyright (C) 2012-2013 Intel Corporation. All rights reserved.
4 *
5 * This program is free software; you can redistribute it and/or modify it
6 * under the terms and conditions of the GNU Lesser General Public License,
7 * version 2.1, as published by the Free Software Foundation.
8 *
9 * This program is distributed in the hope it will be useful, but WITHOUT
10 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
11 * FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License
12 * for more details.
13 *
14 * You should have received a copy of the GNU Lesser General Public License
15 * along with this program; if not, write to the Free Software Foundation, Inc.,
16 * 51 Franklin St - Fifth Floor, Boston, MA 02110-1301 USA.
17 *
18 * Mark Ryan <mark.d.ryan@intel.com>
19 *
20 */
21
22 #include <string.h>
23
24 #include <libgssdp/gssdp-resource-browser.h>
25 #include <libgupnp/gupnp-control-point.h>
26 #include <libgupnp/gupnp-error.h>
27
28 #include <libdleyna/core/error.h>
29 #include <libdleyna/core/log.h>
30 #include <libdleyna/core/service-task.h>
31
32 #include "async.h"
33 #include "device.h"
34 #include "interface.h"
35 #include "path.h"
36 #include "search.h"
37 #include "sort.h"
38 #include "upnp.h"
39
40 struct dls_upnp_t_ {
41 dleyna_connector_id_t connection;
42 const dleyna_connector_dispatch_cb_t *interface_info;
43 GHashTable *filter_map;
44 GHashTable *property_map;
45 dls_upnp_callback_t found_server;
46 dls_upnp_callback_t lost_server;
47 GUPnPContextManager *context_manager;
48 void *user_data;
49 GHashTable *server_udn_map;
50 GHashTable *server_uc_map;
51 guint counter;
52 };
53
54 /* Private structure used in service task */
55 typedef struct prv_device_new_ct_t_ prv_device_new_ct_t;
56 struct prv_device_new_ct_t_ {
57 dls_upnp_t *upnp;
58 char *udn;
59 dls_device_t *device;
60 const dleyna_task_queue_key_t *queue_id;
61 };
62
63 static void prv_device_new_free(prv_device_new_ct_t *priv_t)
64 {
65 if (priv_t) {
66 g_free(priv_t->udn);
67 g_free(priv_t);
68 }
69 }
70
71 static void prv_device_chain_end(gboolean cancelled, gpointer data)
72 {
73 dls_device_t *device;
74 prv_device_new_ct_t *priv_t = (prv_device_new_ct_t *)data;
75
76 DLEYNA_LOG_DEBUG("Enter");
77
78 device = priv_t->device;
79
80 if (cancelled)
81 goto on_clear;
82
83 DLEYNA_LOG_DEBUG("Notify new server available: %s", device->path);
84 g_hash_table_insert(priv_t->upnp->server_udn_map, g_strdup(priv_t->udn),
85 device);
86 priv_t->upnp->found_server(device->path, priv_t->upnp->user_data);
87
88 on_clear:
89
90 g_hash_table_remove(priv_t->upnp->server_uc_map, priv_t->udn);
91 prv_device_new_free(priv_t);
92
93 if (cancelled)
94 dls_device_delete(device);
95
96 DLEYNA_LOG_DEBUG_NL();
97 }
98
99 static void prv_server_available_cb(GUPnPControlPoint *cp,
100 GUPnPDeviceProxy *proxy,
101 gpointer user_data)
102 {
103 dls_upnp_t *upnp = user_data;
104 const char *udn;
105 dls_device_t *device;
106 const gchar *ip_address;
107 dls_device_context_t *context;
108 const dleyna_task_queue_key_t *queue_id;
109 unsigned int i;
110 prv_device_new_ct_t *priv_t;
111
112 udn = gupnp_device_info_get_udn((GUPnPDeviceInfo *)proxy);
113 if (!udn)
114 goto on_error;
115
116 ip_address = gupnp_context_get_host_ip(
117 gupnp_control_point_get_context(cp));
118
119 DLEYNA_LOG_DEBUG("UDN %s", udn);
120 DLEYNA_LOG_DEBUG("IP Address %s", ip_address);
121
122 device = g_hash_table_lookup(upnp->server_udn_map, udn);
123
124 if (!device) {
125 priv_t = g_hash_table_lookup(upnp->server_uc_map, udn);
126
127 if (priv_t)
128 device = priv_t->device;
129 }
130
131 if (!device) {
132 DLEYNA_LOG_DEBUG("Device not found. Adding");
133 DLEYNA_LOG_DEBUG_NL();
134
135 priv_t = g_new0(prv_device_new_ct_t, 1);
136
137 queue_id = dleyna_task_processor_add_queue(
138 dls_server_get_task_processor(),
139 dleyna_service_task_create_source(),
140 DLS_SERVER_SINK,
141 DLEYNA_TASK_QUEUE_FLAG_AUTO_REMOVE,
142 dleyna_service_task_process_cb,
143 dleyna_service_task_cancel_cb,
144 dleyna_service_task_delete_cb);
145 dleyna_task_queue_set_finally(queue_id, prv_device_chain_end);
146 dleyna_task_queue_set_user_data(queue_id, priv_t);
147
148 device = dls_device_new(upnp->connection, proxy, ip_address,
149 upnp->interface_info,
150 upnp->property_map, upnp->counter,
151 queue_id);
152
153 upnp->counter++;
154
155 priv_t->upnp = upnp;
156 priv_t->udn = g_strdup(udn);
157 priv_t->queue_id = queue_id;
158 priv_t->device = device;
159
160 g_hash_table_insert(upnp->server_uc_map, g_strdup(udn), priv_t);
161 } else {
162 DLEYNA_LOG_DEBUG("Device Found");
163
164 for (i = 0; i < device->contexts->len; ++i) {
165 context = g_ptr_array_index(device->contexts, i);
166 if (!strcmp(context->ip_address, ip_address))
167 break;
168 }
169
170 if (i == device->contexts->len) {
171 DLEYNA_LOG_DEBUG("Adding Context");
172 (void) dls_device_append_new_context(device, ip_address,
173 proxy);
174 }
175
176 DLEYNA_LOG_DEBUG_NL();
177 }
178
179 on_error:
180
181 return;
182 }
183
184 static gboolean prv_subscribe_to_contents_change(gpointer user_data)
185 {
186 dls_device_t *device = user_data;
187
188 device->timeout_id = 0;
189 dls_device_subscribe_to_contents_change(device);
190
191 return FALSE;
192 }
193
194 static void prv_server_unavailable_cb(GUPnPControlPoint *cp,
195 GUPnPDeviceProxy *proxy,
196 gpointer user_data)
197 {
198 dls_upnp_t *upnp = user_data;
199 const char *udn;
200 dls_device_t *device;
201 const gchar *ip_address;
202 unsigned int i;
203 dls_device_context_t *context;
204 gboolean subscribed;
205 gboolean under_construction = FALSE;
206 prv_device_new_ct_t *priv_t;
207
208 DLEYNA_LOG_DEBUG("Enter");
209
210 udn = gupnp_device_info_get_udn((GUPnPDeviceInfo *)proxy);
211 if (!udn)
212 goto on_error;
213
214 ip_address = gupnp_context_get_host_ip(
215 gupnp_control_point_get_context(cp));
216
217 DLEYNA_LOG_DEBUG("UDN %s", udn);
218 DLEYNA_LOG_DEBUG("IP Address %s", ip_address);
219
220 device = g_hash_table_lookup(upnp->server_udn_map, udn);
221
222 if (!device) {
223 priv_t = g_hash_table_lookup(upnp->server_uc_map, udn);
224
225 if (priv_t) {
226 device = priv_t->device;
227 under_construction = TRUE;
228 }
229 }
230
231 if (!device) {
232 DLEYNA_LOG_WARNING("Device not found. Ignoring");
233 goto on_error;
234 }
235
236 for (i = 0; i < device->contexts->len; ++i) {
237 context = g_ptr_array_index(device->contexts, i);
238 if (!strcmp(context->ip_address, ip_address))
239 break;
240 }
241
242 if (i >= device->contexts->len)
243 goto on_error;
244
245 subscribed = context->subscribed;
246
247 (void) g_ptr_array_remove_index(device->contexts, i);
248
249 if (device->contexts->len == 0) {
250 if (!under_construction) {
251 DLEYNA_LOG_DEBUG("Last Context lost. Delete device");
252 upnp->lost_server(device->path, upnp->user_data);
253 g_hash_table_remove(upnp->server_udn_map, udn);
254 } else {
255 DLEYNA_LOG_WARNING(
256 "Device under construction. Cancelling");
257
258 dleyna_task_processor_cancel_queue(priv_t->queue_id);
259 }
260 } else if (subscribed && !device->timeout_id) {
261 DLEYNA_LOG_DEBUG("Subscribe on new context");
262
263 device->timeout_id = g_timeout_add_seconds(1,
264 prv_subscribe_to_contents_change,
265 device);
266 }
267
268 on_error:
269
270 DLEYNA_LOG_DEBUG("Exit");
271 DLEYNA_LOG_DEBUG_NL();
272
273 return;
274 }
275
276 static void prv_on_context_available(GUPnPContextManager *context_manager,
277 GUPnPContext *context,
278 gpointer user_data)
279 {
280 dls_upnp_t *upnp = user_data;
281 GUPnPControlPoint *cp;
282
283 cp = gupnp_control_point_new(
284 context,
285 "urn:schemas-upnp-org:device:MediaServer:1");
286
287 g_signal_connect(cp, "device-proxy-available",
288 G_CALLBACK(prv_server_available_cb), upnp);
289
290 g_signal_connect(cp, "device-proxy-unavailable",
291 G_CALLBACK(prv_server_unavailable_cb), upnp);
292
293 gssdp_resource_browser_set_active(GSSDP_RESOURCE_BROWSER(cp), TRUE);
294 gupnp_context_manager_manage_control_point(upnp->context_manager, cp);
295 g_object_unref(cp);
296 }
297
298 dls_upnp_t *dls_upnp_new(dleyna_connector_id_t connection,
299 const dleyna_connector_dispatch_cb_t *dispatch_table,
300 dls_upnp_callback_t found_server,
301 dls_upnp_callback_t lost_server,
302 void *user_data)
303 {
304 dls_upnp_t *upnp = g_new0(dls_upnp_t, 1);
305
306 upnp->connection = connection;
307 upnp->interface_info = dispatch_table;
308 upnp->user_data = user_data;
309 upnp->found_server = found_server;
310 upnp->lost_server = lost_server;
311
312 upnp->server_udn_map = g_hash_table_new_full(g_str_hash, g_str_equal,
313 g_free,
314 dls_device_delete);
315
316 upnp->server_uc_map = g_hash_table_new_full(g_str_hash, g_str_equal,
317 g_free, NULL);
318
319 dls_prop_maps_new(&upnp->property_map, &upnp->filter_map);
320
321 upnp->context_manager = gupnp_context_manager_create(0);
322
323 g_signal_connect(upnp->context_manager, "context-available",
324 G_CALLBACK(prv_on_context_available),
325 upnp);
326
327 return upnp;
328 }
329
330 void dls_upnp_delete(dls_upnp_t *upnp)
331 {
332 if (upnp) {
333 g_object_unref(upnp->context_manager);
334 g_hash_table_unref(upnp->property_map);
335 g_hash_table_unref(upnp->filter_map);
336 g_hash_table_unref(upnp->server_udn_map);
337 g_hash_table_unref(upnp->server_uc_map);
338 g_free(upnp);
339 }
340 }
341
342 GVariant *dls_upnp_get_server_ids(dls_upnp_t *upnp)
343 {
344 GVariantBuilder vb;
345 GHashTableIter iter;
346 gpointer value;
347 dls_device_t *device;
348 GVariant *retval;
349
350 DLEYNA_LOG_DEBUG("Enter");
351
352 g_variant_builder_init(&vb, G_VARIANT_TYPE("ao"));
353
354 g_hash_table_iter_init(&iter, upnp->server_udn_map);
355 while (g_hash_table_iter_next(&iter, NULL, &value)) {
356 device = value;
357 DLEYNA_LOG_DEBUG("Have device %s", device->path);
358 g_variant_builder_add(&vb, "o", device->path);
359 }
360
361 retval = g_variant_ref_sink(g_variant_builder_end(&vb));
362
363 DLEYNA_LOG_DEBUG("Exit");
364
365 return retval;
366 }
367
368 GHashTable *dls_upnp_get_server_udn_map(dls_upnp_t *upnp)
369 {
370 return upnp->server_udn_map;
371 }
372
373 void dls_upnp_get_children(dls_upnp_t *upnp, dls_client_t *client,
374 dls_task_t *task,
375 dls_upnp_task_complete_t cb)
376 {
377 dls_async_task_t *cb_data = (dls_async_task_t *)task;
378 dls_async_bas_t *cb_task_data;
379 gchar *upnp_filter = NULL;
380 gchar *sort_by = NULL;
381
382 DLEYNA_LOG_DEBUG("Enter");
383
384 DLEYNA_LOG_DEBUG("Path: %s", task->target.path);
385 DLEYNA_LOG_DEBUG("Start: %u", task->ut.get_children.start);
386 DLEYNA_LOG_DEBUG("Count: %u", task->ut.get_children.count);
387
388 cb_data->cb = cb;
389 cb_task_data = &cb_data->ut.bas;
390
391 cb_task_data->filter_mask =
392 dls_props_parse_filter(upnp->filter_map,
393 task->ut.get_children.filter,
394 &upnp_filter);
395
396 DLEYNA_LOG_DEBUG("Filter Mask 0x%"G_GUINT64_FORMAT"x",
397 cb_task_data->filter_mask);
398
399 sort_by = dls_sort_translate_sort_string(upnp->filter_map,
400 task->ut.get_children.sort_by);
401 if (!sort_by) {
402 DLEYNA_LOG_WARNING("Invalid Sort Criteria");
403
404 cb_data->error = g_error_new(DLEYNA_SERVER_ERROR,
405 DLEYNA_ERROR_BAD_QUERY,
406 "Sort Criteria are not valid");
407 goto on_error;
408 }
409
410 DLEYNA_LOG_DEBUG("Sort By %s", sort_by);
411
412 cb_task_data->protocol_info = client->protocol_info;
413
414 dls_device_get_children(client, task, upnp_filter, sort_by);
415
416 on_error:
417
418 if (!cb_data->action)
419 (void) g_idle_add(dls_async_task_complete, cb_data);
420
421 g_free(sort_by);
422 g_free(upnp_filter);
423
424 DLEYNA_LOG_DEBUG("Exit with %s", !cb_data->action ? "FAIL" : "SUCCESS");
425 }
426
427 void dls_upnp_get_all_props(dls_upnp_t *upnp, dls_client_t *client,
428 dls_task_t *task,
429 dls_upnp_task_complete_t cb)
430 {
431 gboolean root_object;
432 dls_async_task_t *cb_data = (dls_async_task_t *)task;
433 dls_async_get_all_t *cb_task_data;
434
435 DLEYNA_LOG_DEBUG("Enter");
436
437 DLEYNA_LOG_DEBUG("Path: %s", task->target.path);
438 DLEYNA_LOG_DEBUG("Interface %s", task->ut.get_prop.interface_name);
439
440 cb_data->cb = cb;
441 cb_task_data = &cb_data->ut.get_all;
442
443 root_object = task->target.id[0] == '0' && task->target.id[1] == 0;
444
445 DLEYNA_LOG_DEBUG("Root Object = %d", root_object);
446
447 cb_task_data->protocol_info = client->protocol_info;
448
449 dls_device_get_all_props(client, task, root_object);
450
451 DLEYNA_LOG_DEBUG("Exit with SUCCESS");
452 }
453
454 void dls_upnp_get_prop(dls_upnp_t *upnp, dls_client_t *client,
455 dls_task_t *task,
456 dls_upnp_task_complete_t cb)
457 {
458 gboolean root_object;
459 dls_async_task_t *cb_data = (dls_async_task_t *)task;
460 dls_async_get_prop_t *cb_task_data;
461 dls_prop_map_t *prop_map;
462 dls_task_get_prop_t *task_data;
463
464 DLEYNA_LOG_DEBUG("Enter");
465
466 DLEYNA_LOG_DEBUG("Path: %s", task->target.path);
467 DLEYNA_LOG_DEBUG("Interface %s", task->ut.get_prop.interface_name);
468 DLEYNA_LOG_DEBUG("Prop.%s", task->ut.get_prop.prop_name);
469
470 task_data = &task->ut.get_prop;
471 cb_data->cb = cb;
472 cb_task_data = &cb_data->ut.get_prop;
473
474 root_object = task->target.id[0] == '0' && task->target.id[1] == 0;
475
476 DLEYNA_LOG_DEBUG("Root Object = %d", root_object);
477
478 cb_task_data->protocol_info = client->protocol_info;
479 prop_map = g_hash_table_lookup(upnp->filter_map, task_data->prop_name);
480
481 dls_device_get_prop(client, task, prop_map, root_object);
482
483 DLEYNA_LOG_DEBUG("Exit with SUCCESS");
484 }
485
486 void dls_upnp_search(dls_upnp_t *upnp, dls_client_t *client,
487 dls_task_t *task,
488 dls_upnp_task_complete_t cb)
489 {
490 gchar *upnp_filter = NULL;
491 gchar *upnp_query = NULL;
492 gchar *sort_by = NULL;
493 dls_async_task_t *cb_data = (dls_async_task_t *)task;
494 dls_async_bas_t *cb_task_data;
495
496 DLEYNA_LOG_DEBUG("Enter");
497
498 DLEYNA_LOG_DEBUG("Path: %s", task->target.path);
499 DLEYNA_LOG_DEBUG("Query: %s", task->ut.search.query);
500 DLEYNA_LOG_DEBUG("Start: %u", task->ut.search.start);
501 DLEYNA_LOG_DEBUG("Count: %u", task->ut.search.count);
502
503 cb_data->cb = cb;
504 cb_task_data = &cb_data->ut.bas;
505
506 cb_task_data->filter_mask =
507 dls_props_parse_filter(upnp->filter_map,
508 task->ut.search.filter, &upnp_filter);
509
510 DLEYNA_LOG_DEBUG("Filter Mask 0x%"G_GUINT64_FORMAT"x",
511 cb_task_data->filter_mask);
512
513 upnp_query = dls_search_translate_search_string(upnp->filter_map,
514 task->ut.search.query);
515 if (!upnp_query) {
516 DLEYNA_LOG_WARNING("Query string is not valid:%s",
517 task->ut.search.query);
518
519 cb_data->error = g_error_new(DLEYNA_SERVER_ERROR,
520 DLEYNA_ERROR_BAD_QUERY,
521 "Query string is not valid.");
522 goto on_error;
523 }
524
525 DLEYNA_LOG_DEBUG("UPnP Query %s", upnp_query);
526
527 sort_by = dls_sort_translate_sort_string(upnp->filter_map,
528 task->ut.search.sort_by);
529 if (!sort_by) {
530 DLEYNA_LOG_WARNING("Invalid Sort Criteria");
531
532 cb_data->error = g_error_new(DLEYNA_SERVER_ERROR,
533 DLEYNA_ERROR_BAD_QUERY,
534 "Sort Criteria are not valid");
535 goto on_error;
536 }
537
538 DLEYNA_LOG_DEBUG("Sort By %s", sort_by);
539
540 cb_task_data->protocol_info = client->protocol_info;
541
542 dls_device_search(client, task, upnp_filter, upnp_query, sort_by);
543 on_error:
544
545 if (!cb_data->action)
546 (void) g_idle_add(dls_async_task_complete, cb_data);
547
548 g_free(sort_by);
549 g_free(upnp_query);
550 g_free(upnp_filter);
551
552 DLEYNA_LOG_DEBUG("Exit with %s", !cb_data->action ? "FAIL" : "SUCCESS");
553 }
554
555 void dls_upnp_get_resource(dls_upnp_t *upnp, dls_client_t *client,
556 dls_task_t *task,
557 dls_upnp_task_complete_t cb)
558 {
559 dls_async_task_t *cb_data = (dls_async_task_t *)task;
560 dls_async_get_all_t *cb_task_data;
561 gchar *upnp_filter = NULL;
562
563 DLEYNA_LOG_DEBUG("Enter");
564
565 DLEYNA_LOG_DEBUG("Protocol Info: %s ", task->ut.resource.protocol_info);
566
567 cb_data->cb = cb;
568 cb_task_data = &cb_data->ut.get_all;
569
570 DLEYNA_LOG_DEBUG("Root Path %s Id %s", task->target.root_path,
571 task->target.id);
572
573 cb_task_data->filter_mask =
574 dls_props_parse_filter(upnp->filter_map,
575 task->ut.resource.filter, &upnp_filter);
576
577 DLEYNA_LOG_DEBUG("Filter Mask 0x%"G_GUINT64_FORMAT"x",
578 cb_task_data->filter_mask);
579
580 dls_device_get_resource(client, task, upnp_filter);
581
582 DLEYNA_LOG_DEBUG("Exit");
583 }
584
585 static gboolean prv_compute_mime_and_class(dls_task_t *task,
586 dls_async_upload_t *cb_task_data,
587 GError **error)
588 {
589 gchar *content_type = NULL;
590
591 if (!g_file_test(task->ut.upload.file_path,
592 G_FILE_TEST_IS_REGULAR | G_FILE_TEST_EXISTS)) {
593
594 DLEYNA_LOG_WARNING(
595 "File %s does not exist or is not a regular file",
596 task->ut.upload.file_path);
597
598 *error = g_error_new(DLEYNA_SERVER_ERROR,
599 DLEYNA_ERROR_OBJECT_NOT_FOUND,
600 "File %s does not exist or is not a regular file",
601 task->ut.upload.file_path);
602 goto on_error;
603 }
604
605 content_type = g_content_type_guess(task->ut.upload.file_path, NULL, 0,
606 NULL);
607
608 if (!content_type) {
609
610 DLEYNA_LOG_WARNING("Unable to determine Content Type for %s",
611 task->ut.upload.file_path);
612
613 *error = g_error_new(DLEYNA_SERVER_ERROR, DLEYNA_ERROR_BAD_MIME,
614 "Unable to determine Content Type for %s",
615 task->ut.upload.file_path);
616 goto on_error;
617 }
618
619 cb_task_data->mime_type = g_content_type_get_mime_type(content_type);
620 g_free(content_type);
621
622 if (!cb_task_data->mime_type) {
623
624 DLEYNA_LOG_WARNING("Unable to determine MIME Type for %s",
625 task->ut.upload.file_path);
626
627 *error = g_error_new(DLEYNA_SERVER_ERROR, DLEYNA_ERROR_BAD_MIME,
628 "Unable to determine MIME Type for %s",
629 task->ut.upload.file_path);
630 goto on_error;
631 }
632
633 if (g_content_type_is_a(cb_task_data->mime_type, "image/*")) {
634 cb_task_data->object_class = "object.item.imageItem";
635 } else if (g_content_type_is_a(cb_task_data->mime_type, "audio/*")) {
636 cb_task_data->object_class = "object.item.audioItem";
637 } else if (g_content_type_is_a(cb_task_data->mime_type, "video/*")) {
638 cb_task_data->object_class = "object.item.videoItem";
639 } else {
640
641 DLEYNA_LOG_WARNING("Unsupported MIME Type %s",
642 cb_task_data->mime_type);
643
644 *error = g_error_new(DLEYNA_SERVER_ERROR, DLEYNA_ERROR_BAD_MIME,
645 "Unsupported MIME Type %s",
646 cb_task_data->mime_type);
647 goto on_error;
648 }
649
650 return TRUE;
651
652 on_error:
653
654 return FALSE;
655 }
656
657 void dls_upnp_upload_to_any(dls_upnp_t *upnp, dls_client_t *client,
658 dls_task_t *task,
659 dls_upnp_task_complete_t cb)
660 {
661 dls_async_task_t *cb_data = (dls_async_task_t *)task;
662 dls_async_upload_t *cb_task_data;
663
664 DLEYNA_LOG_DEBUG("Enter");
665
666 cb_data->cb = cb;
667 cb_task_data = &cb_data->ut.upload;
668
669 DLEYNA_LOG_DEBUG("Root Path %s Id %s", task->target.root_path,
670 task->target.id);
671
672 if (strcmp(task->target.id, "0")) {
673 DLEYNA_LOG_WARNING("Bad path %s", task->target.path);
674
675 cb_data->error =
676 g_error_new(DLEYNA_SERVER_ERROR, DLEYNA_ERROR_BAD_PATH,
677 "UploadToAnyContainer must be executed on a root path");
678 goto on_error;
679 }
680
681 if (!prv_compute_mime_and_class(task, cb_task_data, &cb_data->error))
682 goto on_error;
683
684 DLEYNA_LOG_DEBUG("MIME Type %s", cb_task_data->mime_type);
685 DLEYNA_LOG_DEBUG("Object class %s", cb_task_data->object_class);
686
687 dls_device_upload(client, task, "DLNA.ORG_AnyContainer");
688
689 on_error:
690
691 if (!cb_data->action)
692 (void) g_idle_add(dls_async_task_complete, cb_data);
693
694 DLEYNA_LOG_DEBUG("Exit");
695 }
696
697 void dls_upnp_upload(dls_upnp_t *upnp, dls_client_t *client, dls_task_t *task,
698 dls_upnp_task_complete_t cb)
699 {
700 dls_async_task_t *cb_data = (dls_async_task_t *)task;
701 dls_async_upload_t *cb_task_data;
702
703 DLEYNA_LOG_DEBUG("Enter");
704
705 cb_data->cb = cb;
706 cb_task_data = &cb_data->ut.upload;
707
708 if (!prv_compute_mime_and_class(task, cb_task_data, &cb_data->error))
709 goto on_error;
710
711 DLEYNA_LOG_DEBUG("MIME Type %s", cb_task_data->mime_type);
712 DLEYNA_LOG_DEBUG("Object class %s", cb_task_data->object_class);
713
714 dls_device_upload(client, task, task->target.id);
715
716 on_error:
717
718 if (!cb_data->action)
719 (void) g_idle_add(dls_async_task_complete, cb_data);
720
721 DLEYNA_LOG_DEBUG("Exit");
722 }
723
724 void dls_upnp_get_upload_status(dls_upnp_t *upnp, dls_task_t *task)
725 {
726 GError *error = NULL;
727
728 DLEYNA_LOG_DEBUG("Enter");
729
730 DLEYNA_LOG_DEBUG("Root Path %s Id %s", task->target.root_path,
731 task->target.id);
732
733 if (strcmp(task->target.id, "0")) {
734 DLEYNA_LOG_WARNING("Bad path %s", task->target.path);
735
736 error = g_error_new(DLEYNA_SERVER_ERROR, DLEYNA_ERROR_BAD_PATH,
737 "GetUploadStatus must be executed on a root path");
738 goto on_error;
739 }
740
741 (void) dls_device_get_upload_status(task, &error);
742
743 on_error:
744
745 if (error) {
746 dls_task_fail(task, error);
747 g_error_free(error);
748 } else {
749 dls_task_complete(task);
750 }
751
752 DLEYNA_LOG_DEBUG("Exit");
753 }
754
755 void dls_upnp_get_upload_ids(dls_upnp_t *upnp, dls_task_t *task)
756 {
757 GError *error = NULL;
758
759 DLEYNA_LOG_DEBUG("Enter");
760
761 DLEYNA_LOG_DEBUG("Root Path %s Id %s", task->target.root_path,
762 task->target.id);
763
764 if (strcmp(task->target.id, "0")) {
765 DLEYNA_LOG_WARNING("Bad path %s", task->target.path);
766
767 error = g_error_new(DLEYNA_SERVER_ERROR, DLEYNA_ERROR_BAD_PATH,
768 "GetUploadIDs must be executed on a root path");
769 goto on_error;
770 }
771
772 dls_device_get_upload_ids(task);
773
774 on_error:
775
776 if (error) {
777 dls_task_fail(task, error);
778 g_error_free(error);
779 } else {
780 dls_task_complete(task);
781 }
782
783 DLEYNA_LOG_DEBUG("Exit");
784 }
785
786 void dls_upnp_cancel_upload(dls_upnp_t *upnp, dls_task_t *task)
787 {
788 GError *error = NULL;
789
790 DLEYNA_LOG_DEBUG("Enter");
791
792 DLEYNA_LOG_DEBUG("Root Path %s Id %s", task->target.root_path,
793 task->target.id);
794
795 if (strcmp(task->target.id, "0")) {
796 DLEYNA_LOG_WARNING("Bad path %s", task->target.path);
797
798 error = g_error_new(DLEYNA_SERVER_ERROR, DLEYNA_ERROR_BAD_PATH,
799 "CancelUpload must be executed on a root path");
800 goto on_error;
801 }
802
803 (void) dls_device_cancel_upload(task, &error);
804
805 on_error:
806
807 if (error) {
808 dls_task_fail(task, error);
809 g_error_free(error);
810 } else {
811 dls_task_complete(task);
812 }
813
814 DLEYNA_LOG_DEBUG("Exit");
815 }
816
817 void dls_upnp_delete_object(dls_upnp_t *upnp, dls_client_t *client,
818 dls_task_t *task,
819 dls_upnp_task_complete_t cb)
820 {
821 dls_async_task_t *cb_data = (dls_async_task_t *)task;
822
823 DLEYNA_LOG_DEBUG("Enter");
824
825 cb_data->cb = cb;
826
827 DLEYNA_LOG_DEBUG("Root Path %s Id %s", task->target.root_path,
828 task->target.id);
829
830 dls_device_delete_object(client, task);
831
832 DLEYNA_LOG_DEBUG("Exit");
833 }
834
835 void dls_upnp_create_container(dls_upnp_t *upnp, dls_client_t *client,
836 dls_task_t *task,
837 dls_upnp_task_complete_t cb)
838 {
839 dls_async_task_t *cb_data = (dls_async_task_t *)task;
840
841 DLEYNA_LOG_DEBUG("Enter");
842
843 cb_data->cb = cb;
844
845 DLEYNA_LOG_DEBUG("Root Path %s Id %s", task->target.root_path,
846 task->target.id);
847
848 dls_device_create_container(client, task, task->target.id);
849
850 DLEYNA_LOG_DEBUG("Exit");
851 }
852
853 void dls_upnp_create_container_in_any(dls_upnp_t *upnp, dls_client_t *client,
854 dls_task_t *task,
855 dls_upnp_task_complete_t cb)
856 {
857 dls_async_task_t *cb_data = (dls_async_task_t *)task;
858
859 DLEYNA_LOG_DEBUG("Enter");
860
861 cb_data->cb = cb;
862
863 DLEYNA_LOG_DEBUG("Root Path %s Id %s", task->target.root_path,
864 task->target.id);
865
866 if (strcmp(task->target.id, "0")) {
867 DLEYNA_LOG_WARNING("Bad path %s", task->target.path);
868
869 cb_data->error =
870 g_error_new(DLEYNA_SERVER_ERROR, DLEYNA_ERROR_BAD_PATH,
871 "CreateContainerInAnyContainer must be executed on a root path");
872 goto on_error;
873 }
874
875 dls_device_create_container(client, task, "DLNA.ORG_AnyContainer");
876
877 on_error:
878
879 if (!cb_data->action)
880 (void) g_idle_add(dls_async_task_complete, cb_data);
881
882 DLEYNA_LOG_DEBUG("Exit");
883 }
884
885 void dls_upnp_update_object(dls_upnp_t *upnp, dls_client_t *client,
886 dls_task_t *task,
887 dls_upnp_task_complete_t cb)
888 {
889 dls_async_task_t *cb_data = (dls_async_task_t *)task;
890 dls_async_update_t *cb_task_data;
891 dls_upnp_prop_mask mask;
892 gchar *upnp_filter = NULL;
893 dls_task_update_t *task_data;
894
895 DLEYNA_LOG_DEBUG("Enter");
896
897 cb_data->cb = cb;
898 cb_task_data = &cb_data->ut.update;
899 task_data = &task->ut.update;
900
901 DLEYNA_LOG_DEBUG("Root Path %s Id %s", task->target.root_path,
902 task->target.id);
903
904 if (!dls_props_parse_update_filter(upnp->filter_map,
905 task_data->to_add_update,
906 task_data->to_delete,
907 &mask, &upnp_filter)) {
908 DLEYNA_LOG_WARNING("Invalid Parameter");
909
910 cb_data->error = g_error_new(DLEYNA_SERVER_ERROR,
911 DLEYNA_ERROR_OPERATION_FAILED,
912 "Invalid Parameter");
913 goto on_error;
914 }
915
916 cb_task_data->map = upnp->filter_map;
917
918 DLEYNA_LOG_DEBUG("Filter = %s", upnp_filter);
919 DLEYNA_LOG_DEBUG("Mask = 0x%"G_GUINT64_FORMAT"x", mask);
920
921 if (mask == 0) {
922 DLEYNA_LOG_WARNING("Empty Parameters");
923
924 cb_data->error = g_error_new(DLEYNA_SERVER_ERROR,
925 DLEYNA_ERROR_OPERATION_FAILED,
926 "Empty Parameters");
927
928 goto on_error;
929 }
930
931 dls_device_update_object(client, task, upnp_filter);
932
933 on_error:
934
935 g_free(upnp_filter);
936
937 if (!cb_data->action)
938 (void) g_idle_add(dls_async_task_complete, cb_data);
939
940 DLEYNA_LOG_DEBUG("Exit");
941 }
942
943 void dls_upnp_create_playlist(dls_upnp_t *upnp, dls_client_t *client,
944 dls_task_t *task,
945 dls_upnp_task_complete_t cb)
946 {
947 dls_async_task_t *cb_data = (dls_async_task_t *)task;
948 dls_task_create_playlist_t *task_data;
949
950 DLEYNA_LOG_DEBUG("Enter");
951
952 cb_data->cb = cb;
953 task_data = &task->ut.playlist;
954
955 DLEYNA_LOG_DEBUG("Root Path: %s - Id: %s", task->target.root_path,
956 task->target.id);
957
958 if (!task_data->title || !*task_data->title)
959 goto on_param_error;
960
961 if (!g_variant_n_children(task_data->item_path))
962 goto on_param_error;
963
964 DLEYNA_LOG_DEBUG_NL();
965 DLEYNA_LOG_DEBUG("Title = %s", task_data->title);
966 DLEYNA_LOG_DEBUG("Creator = %s", task_data->creator);
967 DLEYNA_LOG_DEBUG("Genre = %s", task_data->genre);
968 DLEYNA_LOG_DEBUG("Desc = %s", task_data->desc);
969 DLEYNA_LOG_DEBUG_NL();
970
971 dls_device_playlist_upload(client, task, task->target.id);
972
973 DLEYNA_LOG_DEBUG("Exit");
974
975 return;
976
977 on_param_error:
978
979 DLEYNA_LOG_WARNING("Invalid Parameter");
980
981 cb_data->error = g_error_new(DLEYNA_SERVER_ERROR,
982 DLEYNA_ERROR_OPERATION_FAILED,
983 "Invalid Parameter");
984
985 (void) g_idle_add(dls_async_task_complete, cb_data);
986
987 DLEYNA_LOG_DEBUG("Exit failure");
988 }
989
990 void dls_upnp_create_playlist_in_any(dls_upnp_t *upnp, dls_client_t *client,
991 dls_task_t *task,
992 dls_upnp_task_complete_t cb)
993 {
994 dls_async_task_t *cb_data = (dls_async_task_t *)task;
995 dls_task_create_playlist_t *task_data;
996
997 DLEYNA_LOG_DEBUG("Enter");
998
999 cb_data->cb = cb;
1000 task_data = &task->ut.playlist;
1001
1002 DLEYNA_LOG_DEBUG("Root Path: %s - Id: %s", task->target.root_path,
1003 task->target.id);
1004
1005 if (strcmp(task->target.id, "0")) {
1006 DLEYNA_LOG_WARNING("Bad path %s", task->target.path);
1007
1008 cb_data->error = g_error_new(DLEYNA_SERVER_ERROR,
1009 DLEYNA_ERROR_BAD_PATH,
1010 "CreatePlayListInAny must be executed on a root path");
1011
1012 goto on_error;
1013 }
1014
1015 if (!task_data->title || !*task_data->title)
1016 goto on_param_error;
1017
1018 if (!g_variant_n_children(task_data->item_path))
1019 goto on_param_error;
1020
1021 DLEYNA_LOG_DEBUG_NL();
1022 DLEYNA_LOG_DEBUG("Title = %s", task_data->title);
1023 DLEYNA_LOG_DEBUG("Creator = %s", task_data->creator);
1024 DLEYNA_LOG_DEBUG("Genre = %s", task_data->genre);
1025 DLEYNA_LOG_DEBUG("Desc = %s", task_data->desc);
1026 DLEYNA_LOG_DEBUG_NL();
1027
1028 dls_device_playlist_upload(client, task, "DLNA.ORG_AnyContainer");
1029
1030 DLEYNA_LOG_DEBUG("Exit");
1031
1032 return;
1033
1034 on_param_error:
1035
1036 DLEYNA_LOG_WARNING("Invalid Parameter");
1037
1038 cb_data->error = g_error_new(DLEYNA_SERVER_ERROR,
1039 DLEYNA_ERROR_OPERATION_FAILED,
1040 "Invalid Parameter");
1041 on_error:
1042
1043 (void) g_idle_add(dls_async_task_complete, cb_data);
1044
1045 DLEYNA_LOG_DEBUG("Exit failure");
1046 }
1047
1048 void dls_upnp_unsubscribe(dls_upnp_t *upnp)
1049 {
1050 GHashTableIter iter;
1051 gpointer value;
1052 dls_device_t *device;
1053
1054 DLEYNA_LOG_DEBUG("Enter");
1055
1056 g_hash_table_iter_init(&iter, upnp->server_udn_map);
1057 while (g_hash_table_iter_next(&iter, NULL, &value)) {
1058 device = value;
1059 dls_device_unsubscribe(device);
1060 }
1061
1062 DLEYNA_LOG_DEBUG("Exit");
1063 }
1064
1065 static gboolean prv_device_uc_find(gpointer key, gpointer value,
1066 gpointer user_data)
1067 {
1068 prv_device_new_ct_t *priv_t = (prv_device_new_ct_t *)value;
1069
1070 return (priv_t->device == user_data) ? TRUE : FALSE;
1071 }
1072
1073 static gboolean prv_device_find(gpointer key, gpointer value,
1074 gpointer user_data)
1075 {
1076 return (value == user_data) ? TRUE : FALSE;
1077 }
1078
1079 gboolean dls_upnp_device_context_exist(dls_device_t *device,
1080 dls_device_context_t *context)
1081 {
1082 gpointer result;
1083 guint i;
1084 gboolean found = FALSE;
1085 dls_upnp_t *upnp = dls_server_get_upnp();
1086
1087 if (upnp == NULL)
1088 goto on_exit;
1089
1090 /* Check if the device still exist */
1091 result = g_hash_table_find(upnp->server_udn_map, prv_device_find,
1092 device);
1093
1094 if (result == NULL)
1095 if (g_hash_table_find(upnp->server_uc_map, prv_device_uc_find,
1096 device) == NULL)
1097 goto on_exit;
1098
1099 /* Search if the context still exist in the device */
1100 for (i = 0; i < device->contexts->len; ++i) {
1101 if (g_ptr_array_index(device->contexts, i) == context) {
1102 found = TRUE;
1103 break;
1104 }
1105 }
1106
1107 on_exit:
1108 return found;
1109 }
0 /*
1 * dLeyna
2 *
3 * Copyright (C) 2012-2013 Intel Corporation. All rights reserved.
4 *
5 * This program is free software; you can redistribute it and/or modify it
6 * under the terms and conditions of the GNU Lesser General Public License,
7 * version 2.1, as published by the Free Software Foundation.
8 *
9 * This program is distributed in the hope it will be useful, but WITHOUT
10 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
11 * FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License
12 * for more details.
13 *
14 * You should have received a copy of the GNU Lesser General Public License
15 * along with this program; if not, write to the Free Software Foundation, Inc.,
16 * 51 Franklin St - Fifth Floor, Boston, MA 02110-1301 USA.
17 *
18 * Mark Ryan <mark.d.ryan@intel.com>
19 *
20 */
21
22 #ifndef DLS_UPNP_H__
23 #define DLS_UPNP_H__
24
25 #include <libdleyna/core/connector.h>
26
27 #include "client.h"
28 #include "async.h"
29
30 typedef void (*dls_upnp_callback_t)(const gchar *path, void *user_data);
31 typedef void (*dls_upnp_task_complete_t)(dls_task_t *task, GError *error);
32
33 dls_upnp_t *dls_upnp_new(dleyna_connector_id_t connection,
34 const dleyna_connector_dispatch_cb_t *dispatch_table,
35 dls_upnp_callback_t found_server,
36 dls_upnp_callback_t lost_server,
37 void *user_data);
38
39 void dls_upnp_delete(dls_upnp_t *upnp);
40
41 GVariant *dls_upnp_get_server_ids(dls_upnp_t *upnp);
42
43 GHashTable *dls_upnp_get_server_udn_map(dls_upnp_t *upnp);
44
45 void dls_upnp_get_children(dls_upnp_t *upnp, dls_client_t *client,
46 dls_task_t *task,
47 dls_upnp_task_complete_t cb);
48
49 void dls_upnp_get_all_props(dls_upnp_t *upnp, dls_client_t *client,
50 dls_task_t *task,
51 dls_upnp_task_complete_t cb);
52
53 void dls_upnp_get_prop(dls_upnp_t *upnp, dls_client_t *client,
54 dls_task_t *task,
55 dls_upnp_task_complete_t cb);
56
57 void dls_upnp_search(dls_upnp_t *upnp, dls_client_t *client,
58 dls_task_t *task,
59 dls_upnp_task_complete_t cb);
60
61 void dls_upnp_get_resource(dls_upnp_t *upnp, dls_client_t *client,
62 dls_task_t *task,
63 dls_upnp_task_complete_t cb);
64
65 void dls_upnp_upload_to_any(dls_upnp_t *upnp, dls_client_t *client,
66 dls_task_t *task,
67 dls_upnp_task_complete_t cb);
68
69 void dls_upnp_upload(dls_upnp_t *upnp, dls_client_t *client,
70 dls_task_t *task,
71 dls_upnp_task_complete_t cb);
72
73 void dls_upnp_get_upload_status(dls_upnp_t *upnp, dls_task_t *task);
74
75 void dls_upnp_get_upload_ids(dls_upnp_t *upnp, dls_task_t *task);
76
77 void dls_upnp_cancel_upload(dls_upnp_t *upnp, dls_task_t *task);
78
79 void dls_upnp_delete_object(dls_upnp_t *upnp, dls_client_t *client,
80 dls_task_t *task,
81 dls_upnp_task_complete_t cb);
82
83 void dls_upnp_create_container(dls_upnp_t *upnp, dls_client_t *client,
84 dls_task_t *task,
85 dls_upnp_task_complete_t cb);
86
87 void dls_upnp_create_container_in_any(dls_upnp_t *upnp, dls_client_t *client,
88 dls_task_t *task,
89 dls_upnp_task_complete_t cb);
90
91 void dls_upnp_update_object(dls_upnp_t *upnp, dls_client_t *client,
92 dls_task_t *task,
93 dls_upnp_task_complete_t cb);
94
95 void dls_upnp_create_playlist(dls_upnp_t *upnp, dls_client_t *client,
96 dls_task_t *task,
97 dls_upnp_task_complete_t cb);
98
99 void dls_upnp_create_playlist_in_any(dls_upnp_t *upnp, dls_client_t *client,
100 dls_task_t *task,
101 dls_upnp_task_complete_t cb);
102
103 void dls_upnp_unsubscribe(dls_upnp_t *upnp);
104
105 gboolean dls_upnp_device_context_exist(dls_device_t *device,
106 dls_device_context_t *context);
107
108 #endif /* DLS_UPNP_H__ */
0 AM_CFLAGS = $(GLIB_CFLAGS) \
1 $(GIO_CFLAGS) \
2 $(DLEYNA_CORE_CFLAGS) \
3 -I$(top_builddir)/lib \
0 AM_CFLAGS = $(GLIB_CFLAGS) \
1 $(GIO_CFLAGS) \
2 $(DLEYNA_CORE_CFLAGS) \
3 -I$(top_builddir)/libdleyna/server \
44 -include config.h
55
66 ACLOCAL_AMFLAGS = -I m4 ${ACLOCAL_FLAGS}
99
1010 dleyna_server_service_SOURCES = daemon.c
1111
12 dleyna_server_service_LDADD = $(GLIB_LIBS) \
13 $(GIO_LIBS) \
14 $(DLEYNA_CORE_LIBS) \
15 $(top_builddir)/lib/libdleyna-server-1.0.la
12 dleyna_server_service_LDADD = $(GLIB_LIBS) \
13 $(GIO_LIBS) \
14 $(DLEYNA_CORE_LIBS) \
15 $(top_builddir)/libdleyna/server/libdleyna-server-1.0.la
1616
1717 dbussessiondir = @DBUS_SESSION_DIR@
1818 dbussession_DATA = com.intel.dleyna-server.service
2424 #include <sys/signalfd.h>
2525
2626 #include <libdleyna/core/main-loop.h>
27 #include <lib/control-point-server.h>
27 #include <libdleyna/server/control-point-server.h>
2828
2929 #define DLS_SERVER_SERVICE_NAME "dleyna-server-service"
3030