Update dleyna to 0.2.0 (4bcad24)
[profile/ivi/dLeyna.git] / dleyna-server / libdleyna / server / upnp.c
1 /*
2  * dLeyna
3  *
4  * Copyright (C) 2012-2013 Intel Corporation. All rights reserved.
5  *
6  * This program is free software; you can redistribute it and/or modify it
7  * under the terms and conditions of the GNU Lesser General Public License,
8  * version 2.1, as published by the Free Software Foundation.
9  *
10  * This program is distributed in the hope it will be useful, but WITHOUT
11  * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
12  * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU Lesser General Public License
13  * for more details.
14  *
15  * You should have received a copy of the GNU Lesser General Public License
16  * along with this program; if not, write to the Free Software Foundation, Inc.,
17  * 51 Franklin St - Fifth Floor, Boston, MA 02110-1301 USA.
18  *
19  * Mark Ryan <mark.d.ryan@intel.com>
20  *
21  */
22
23 #include <string.h>
24
25 #include <libgssdp/gssdp-resource-browser.h>
26 #include <libgupnp/gupnp-control-point.h>
27 #include <libgupnp/gupnp-error.h>
28
29 #include <libdleyna/core/error.h>
30 #include <libdleyna/core/log.h>
31 #include <libdleyna/core/service-task.h>
32
33 #include "async.h"
34 #include "device.h"
35 #include "interface.h"
36 #include "path.h"
37 #include "search.h"
38 #include "sort.h"
39 #include "upnp.h"
40
41 struct dls_upnp_t_ {
42         dleyna_connector_id_t connection;
43         const dleyna_connector_dispatch_cb_t *interface_info;
44         GHashTable *filter_map;
45         GHashTable *property_map;
46         dls_upnp_callback_t found_server;
47         dls_upnp_callback_t lost_server;
48         GUPnPContextManager *context_manager;
49         void *user_data;
50         GHashTable *server_udn_map;
51         GHashTable *server_uc_map;
52         guint counter;
53 };
54
55 /* Private structure used in service task */
56 typedef struct prv_device_new_ct_t_ prv_device_new_ct_t;
57 struct prv_device_new_ct_t_ {
58         dls_upnp_t *upnp;
59         char *udn;
60         gchar *ip_address;
61         dls_device_t *device;
62         const dleyna_task_queue_key_t *queue_id;
63 };
64
65 static void prv_device_new_free(prv_device_new_ct_t *priv_t)
66 {
67         if (priv_t) {
68                 g_free(priv_t->udn);
69                 g_free(priv_t->ip_address);
70                 g_free(priv_t);
71         }
72 }
73
74 static void prv_device_chain_end(gboolean cancelled, gpointer data)
75 {
76         dls_device_t *device;
77         prv_device_new_ct_t *priv_t = (prv_device_new_ct_t *)data;
78
79         DLEYNA_LOG_DEBUG("Enter");
80
81         device = priv_t->device;
82
83         if (cancelled)
84                 goto on_clear;
85
86         DLEYNA_LOG_DEBUG("Notify new server available: %s", device->path);
87         g_hash_table_insert(priv_t->upnp->server_udn_map, g_strdup(priv_t->udn),
88                             device);
89         priv_t->upnp->found_server(device->path, priv_t->upnp->user_data);
90
91 on_clear:
92
93         g_hash_table_remove(priv_t->upnp->server_uc_map, priv_t->udn);
94
95         if (cancelled)
96                 dls_device_delete(device);
97
98         prv_device_new_free(priv_t);
99
100         DLEYNA_LOG_DEBUG_NL();
101 }
102
103 static void prv_device_context_switch_end(gboolean cancelled, gpointer data)
104 {
105         prv_device_new_ct_t *priv_t = (prv_device_new_ct_t *)data;
106
107         DLEYNA_LOG_DEBUG("Enter");
108
109         g_hash_table_remove(priv_t->upnp->server_uc_map, priv_t->udn);
110         prv_device_new_free(priv_t);
111
112         DLEYNA_LOG_DEBUG("Exit");
113 }
114
115 static const dleyna_task_queue_key_t *prv_create_device_queue(
116                                                 prv_device_new_ct_t **priv_t)
117 {
118         const dleyna_task_queue_key_t *queue_id;
119
120         *priv_t = g_new0(prv_device_new_ct_t, 1);
121
122         queue_id = dleyna_task_processor_add_queue(
123                         dls_server_get_task_processor(),
124                         dleyna_service_task_create_source(),
125                         DLS_SERVER_SINK,
126                         DLEYNA_TASK_QUEUE_FLAG_AUTO_REMOVE,
127                         dleyna_service_task_process_cb,
128                         dleyna_service_task_cancel_cb,
129                         dleyna_service_task_delete_cb);
130         dleyna_task_queue_set_finally(queue_id, prv_device_chain_end);
131         dleyna_task_queue_set_user_data(queue_id, *priv_t);
132
133
134         return queue_id;
135 }
136
137 static void prv_update_device_context(prv_device_new_ct_t *priv_t,
138                                       dls_upnp_t *upnp, const char *udn,
139                                       dls_device_t *device,
140                                       const gchar *ip_address,
141                                       const dleyna_task_queue_key_t *queue_id)
142 {
143         priv_t->upnp = upnp;
144         priv_t->udn = g_strdup(udn);
145         priv_t->ip_address = g_strdup(ip_address);
146         priv_t->queue_id = queue_id;
147         priv_t->device = device;
148
149         g_hash_table_insert(upnp->server_uc_map, g_strdup(udn), priv_t);
150 }
151
152 static void prv_server_available_cb(GUPnPControlPoint *cp,
153                                     GUPnPDeviceProxy *proxy,
154                                     gpointer user_data)
155 {
156         dls_upnp_t *upnp = user_data;
157         const char *udn;
158         dls_device_t *device;
159         const gchar *ip_address;
160         dls_device_context_t *context;
161         const dleyna_task_queue_key_t *queue_id;
162         unsigned int i;
163         prv_device_new_ct_t *priv_t;
164
165         udn = gupnp_device_info_get_udn((GUPnPDeviceInfo *)proxy);
166         if (!udn)
167                 goto on_error;
168
169         ip_address = gupnp_context_get_host_ip(
170                 gupnp_control_point_get_context(cp));
171
172         DLEYNA_LOG_DEBUG("UDN %s", udn);
173         DLEYNA_LOG_DEBUG("IP Address %s", ip_address);
174
175         device = g_hash_table_lookup(upnp->server_udn_map, udn);
176
177         if (!device) {
178                 priv_t = g_hash_table_lookup(upnp->server_uc_map, udn);
179
180                 if (priv_t)
181                         device = priv_t->device;
182         }
183
184         if (!device) {
185                 DLEYNA_LOG_DEBUG("Device not found. Adding");
186                 DLEYNA_LOG_DEBUG_NL();
187
188                 queue_id = prv_create_device_queue(&priv_t);
189
190                 device = dls_device_new(upnp->connection, proxy, ip_address,
191                                         upnp->interface_info,
192                                         upnp->property_map, upnp->counter,
193                                         queue_id);
194
195                 prv_update_device_context(priv_t, upnp, udn, device, ip_address,
196                                           queue_id);
197
198                 upnp->counter++;
199         } else {
200                 DLEYNA_LOG_DEBUG("Device Found");
201
202                 for (i = 0; i < device->contexts->len; ++i) {
203                         context = g_ptr_array_index(device->contexts, i);
204                         if (!strcmp(context->ip_address, ip_address))
205                                 break;
206                 }
207
208                 if (i == device->contexts->len) {
209                         DLEYNA_LOG_DEBUG("Adding Context");
210                         (void) dls_device_append_new_context(device, ip_address,
211                                                              proxy);
212                 }
213
214                 DLEYNA_LOG_DEBUG_NL();
215         }
216
217 on_error:
218
219         return;
220 }
221
222 static gboolean prv_subscribe_to_contents_change(gpointer user_data)
223 {
224         dls_device_t *device = user_data;
225
226         device->timeout_id = 0;
227         dls_device_subscribe_to_contents_change(device);
228
229         return FALSE;
230 }
231
232 static void prv_server_unavailable_cb(GUPnPControlPoint *cp,
233                                       GUPnPDeviceProxy *proxy,
234                                       gpointer user_data)
235 {
236         dls_upnp_t *upnp = user_data;
237         const char *udn;
238         dls_device_t *device;
239         const gchar *ip_address;
240         unsigned int i;
241         dls_device_context_t *context;
242         gboolean subscribed;
243         gboolean construction_ctx = FALSE;
244         gboolean under_construction = FALSE;
245         prv_device_new_ct_t *priv_t;
246         const dleyna_task_queue_key_t *queue_id;
247
248         DLEYNA_LOG_DEBUG("Enter");
249
250         udn = gupnp_device_info_get_udn((GUPnPDeviceInfo *)proxy);
251         if (!udn)
252                 goto on_error;
253
254         ip_address = gupnp_context_get_host_ip(
255                 gupnp_control_point_get_context(cp));
256
257         DLEYNA_LOG_DEBUG("UDN %s", udn);
258         DLEYNA_LOG_DEBUG("IP Address %s", ip_address);
259
260         device = g_hash_table_lookup(upnp->server_udn_map, udn);
261
262         if (!device) {
263                 priv_t = g_hash_table_lookup(upnp->server_uc_map, udn);
264
265                 if (priv_t) {
266                         device = priv_t->device;
267                         under_construction = TRUE;
268                 }
269         }
270
271         if (!device) {
272                 DLEYNA_LOG_WARNING("Device not found. Ignoring");
273                 goto on_error;
274         }
275
276         for (i = 0; i < device->contexts->len; ++i) {
277                 context = g_ptr_array_index(device->contexts, i);
278                 if (!strcmp(context->ip_address, ip_address))
279                         break;
280         }
281
282         if (i >= device->contexts->len)
283                 goto on_error;
284
285         subscribed = context->subscribed;
286         if (under_construction)
287                 construction_ctx = !strcmp(context->ip_address,
288                                            priv_t->ip_address);
289
290         (void) g_ptr_array_remove_index(device->contexts, i);
291
292         if (device->contexts->len == 0) {
293                 if (!under_construction) {
294                         DLEYNA_LOG_DEBUG("Last Context lost. Delete device");
295                         upnp->lost_server(device->path, upnp->user_data);
296                         g_hash_table_remove(upnp->server_udn_map, udn);
297                 } else {
298                         DLEYNA_LOG_WARNING(
299                                 "Device under construction. Cancelling");
300
301                         dleyna_task_processor_cancel_queue(priv_t->queue_id);
302                 }
303         } else if (under_construction && construction_ctx) {
304                 DLEYNA_LOG_WARNING(
305                         "Device under construction. Switching context");
306
307                 /* Cancel previous contruction task chain */
308                 g_hash_table_remove(priv_t->upnp->server_uc_map, priv_t->udn);
309                 dleyna_task_queue_set_finally(priv_t->queue_id,
310                                               prv_device_context_switch_end);
311                 dleyna_task_processor_cancel_queue(priv_t->queue_id);
312
313                 /* Create a new construction task chain */
314                 context = dls_device_get_context(device, NULL);
315                 queue_id = prv_create_device_queue(&priv_t);
316                 prv_update_device_context(priv_t, upnp, udn, device,
317                                           context->ip_address, queue_id);
318
319                 /* Start tasks from current construction step */
320                 dls_device_construct(device,
321                                      context,
322                                      upnp->connection,
323                                      upnp->interface_info,
324                                      upnp->property_map,
325                                      queue_id);
326
327         } else if (subscribed && !device->timeout_id) {
328                 DLEYNA_LOG_DEBUG("Subscribe on new context");
329
330                 device->timeout_id = g_timeout_add_seconds(1,
331                                 prv_subscribe_to_contents_change,
332                                 device);
333         }
334
335 on_error:
336
337         DLEYNA_LOG_DEBUG("Exit");
338         DLEYNA_LOG_DEBUG_NL();
339
340         return;
341 }
342
343 static void prv_on_context_available(GUPnPContextManager *context_manager,
344                                      GUPnPContext *context,
345                                      gpointer user_data)
346 {
347         dls_upnp_t *upnp = user_data;
348         GUPnPControlPoint *cp;
349
350         cp = gupnp_control_point_new(
351                 context,
352                 "urn:schemas-upnp-org:device:MediaServer:1");
353
354         g_signal_connect(cp, "device-proxy-available",
355                          G_CALLBACK(prv_server_available_cb), upnp);
356
357         g_signal_connect(cp, "device-proxy-unavailable",
358                          G_CALLBACK(prv_server_unavailable_cb), upnp);
359
360         gssdp_resource_browser_set_active(GSSDP_RESOURCE_BROWSER(cp), TRUE);
361         gupnp_context_manager_manage_control_point(upnp->context_manager, cp);
362         g_object_unref(cp);
363 }
364
365 dls_upnp_t *dls_upnp_new(dleyna_connector_id_t connection,
366                          const dleyna_connector_dispatch_cb_t *dispatch_table,
367                          dls_upnp_callback_t found_server,
368                          dls_upnp_callback_t lost_server,
369                          void *user_data)
370 {
371         dls_upnp_t *upnp = g_new0(dls_upnp_t, 1);
372
373         upnp->connection = connection;
374         upnp->interface_info = dispatch_table;
375         upnp->user_data = user_data;
376         upnp->found_server = found_server;
377         upnp->lost_server = lost_server;
378
379         upnp->server_udn_map = g_hash_table_new_full(g_str_hash, g_str_equal,
380                                                      g_free,
381                                                      dls_device_delete);
382
383         upnp->server_uc_map = g_hash_table_new_full(g_str_hash, g_str_equal,
384                                                      g_free, NULL);
385
386         dls_prop_maps_new(&upnp->property_map, &upnp->filter_map);
387
388         upnp->context_manager = gupnp_context_manager_create(0);
389
390         g_signal_connect(upnp->context_manager, "context-available",
391                          G_CALLBACK(prv_on_context_available),
392                          upnp);
393
394         return upnp;
395 }
396
397 void dls_upnp_delete(dls_upnp_t *upnp)
398 {
399         if (upnp) {
400                 g_object_unref(upnp->context_manager);
401                 g_hash_table_unref(upnp->property_map);
402                 g_hash_table_unref(upnp->filter_map);
403                 g_hash_table_unref(upnp->server_udn_map);
404                 g_hash_table_unref(upnp->server_uc_map);
405                 g_free(upnp);
406         }
407 }
408
409 GVariant *dls_upnp_get_server_ids(dls_upnp_t *upnp)
410 {
411         GVariantBuilder vb;
412         GHashTableIter iter;
413         gpointer value;
414         dls_device_t *device;
415         GVariant *retval;
416
417         DLEYNA_LOG_DEBUG("Enter");
418
419         g_variant_builder_init(&vb, G_VARIANT_TYPE("ao"));
420
421         g_hash_table_iter_init(&iter, upnp->server_udn_map);
422         while (g_hash_table_iter_next(&iter, NULL, &value)) {
423                 device = value;
424                 DLEYNA_LOG_DEBUG("Have device %s", device->path);
425                 g_variant_builder_add(&vb, "o", device->path);
426         }
427
428         retval = g_variant_ref_sink(g_variant_builder_end(&vb));
429
430         DLEYNA_LOG_DEBUG("Exit");
431
432         return retval;
433 }
434
435 GHashTable *dls_upnp_get_server_udn_map(dls_upnp_t *upnp)
436 {
437         return upnp->server_udn_map;
438 }
439
440 void dls_upnp_get_children(dls_upnp_t *upnp, dls_client_t *client,
441                            dls_task_t *task,
442                            dls_upnp_task_complete_t cb)
443 {
444         dls_async_task_t *cb_data = (dls_async_task_t *)task;
445         dls_async_bas_t *cb_task_data;
446         gchar *upnp_filter = NULL;
447         gchar *sort_by = NULL;
448
449         DLEYNA_LOG_DEBUG("Enter");
450
451         DLEYNA_LOG_DEBUG("Path: %s", task->target.path);
452         DLEYNA_LOG_DEBUG("Start: %u", task->ut.get_children.start);
453         DLEYNA_LOG_DEBUG("Count: %u", task->ut.get_children.count);
454
455         cb_data->cb = cb;
456         cb_task_data = &cb_data->ut.bas;
457
458         cb_task_data->filter_mask =
459                 dls_props_parse_filter(upnp->filter_map,
460                                        task->ut.get_children.filter,
461                                        &upnp_filter);
462
463         DLEYNA_LOG_DEBUG("Filter Mask 0x%"G_GUINT64_FORMAT"x",
464                          cb_task_data->filter_mask);
465
466         sort_by = dls_sort_translate_sort_string(upnp->filter_map,
467                                                  task->ut.get_children.sort_by);
468         if (!sort_by) {
469                 DLEYNA_LOG_WARNING("Invalid Sort Criteria");
470
471                 cb_data->error = g_error_new(DLEYNA_SERVER_ERROR,
472                                              DLEYNA_ERROR_BAD_QUERY,
473                                              "Sort Criteria are not valid");
474                 goto on_error;
475         }
476
477         DLEYNA_LOG_DEBUG("Sort By %s", sort_by);
478
479         cb_task_data->protocol_info = client->protocol_info;
480
481         dls_device_get_children(client, task, upnp_filter, sort_by);
482
483 on_error:
484
485         if (!cb_data->action)
486                 (void) g_idle_add(dls_async_task_complete, cb_data);
487
488         g_free(sort_by);
489         g_free(upnp_filter);
490
491         DLEYNA_LOG_DEBUG("Exit with %s", !cb_data->action ? "FAIL" : "SUCCESS");
492 }
493
494 void dls_upnp_get_all_props(dls_upnp_t *upnp, dls_client_t *client,
495                             dls_task_t *task,
496                             dls_upnp_task_complete_t cb)
497 {
498         gboolean root_object;
499         dls_async_task_t *cb_data = (dls_async_task_t *)task;
500         dls_async_get_all_t *cb_task_data;
501
502         DLEYNA_LOG_DEBUG("Enter");
503
504         DLEYNA_LOG_DEBUG("Path: %s", task->target.path);
505         DLEYNA_LOG_DEBUG("Interface %s", task->ut.get_prop.interface_name);
506
507         cb_data->cb = cb;
508         cb_task_data = &cb_data->ut.get_all;
509
510         root_object = task->target.id[0] == '0' && task->target.id[1] == 0;
511
512         DLEYNA_LOG_DEBUG("Root Object = %d", root_object);
513
514         cb_task_data->protocol_info = client->protocol_info;
515
516         dls_device_get_all_props(client, task, root_object);
517
518         DLEYNA_LOG_DEBUG("Exit with SUCCESS");
519 }
520
521 void dls_upnp_get_prop(dls_upnp_t *upnp, dls_client_t *client,
522                        dls_task_t *task,
523                        dls_upnp_task_complete_t cb)
524 {
525         gboolean root_object;
526         dls_async_task_t *cb_data = (dls_async_task_t *)task;
527         dls_async_get_prop_t *cb_task_data;
528         dls_prop_map_t *prop_map;
529         dls_task_get_prop_t *task_data;
530
531         DLEYNA_LOG_DEBUG("Enter");
532
533         DLEYNA_LOG_DEBUG("Path: %s", task->target.path);
534         DLEYNA_LOG_DEBUG("Interface %s", task->ut.get_prop.interface_name);
535         DLEYNA_LOG_DEBUG("Prop.%s", task->ut.get_prop.prop_name);
536
537         task_data = &task->ut.get_prop;
538         cb_data->cb = cb;
539         cb_task_data = &cb_data->ut.get_prop;
540
541         root_object = task->target.id[0] == '0' && task->target.id[1] == 0;
542
543         DLEYNA_LOG_DEBUG("Root Object = %d", root_object);
544
545         cb_task_data->protocol_info = client->protocol_info;
546         prop_map = g_hash_table_lookup(upnp->filter_map, task_data->prop_name);
547
548         dls_device_get_prop(client, task, prop_map, root_object);
549
550         DLEYNA_LOG_DEBUG("Exit with SUCCESS");
551 }
552
553 void dls_upnp_search(dls_upnp_t *upnp, dls_client_t *client,
554                      dls_task_t *task,
555                      dls_upnp_task_complete_t cb)
556 {
557         gchar *upnp_filter = NULL;
558         gchar *upnp_query = NULL;
559         gchar *sort_by = NULL;
560         dls_async_task_t *cb_data = (dls_async_task_t *)task;
561         dls_async_bas_t *cb_task_data;
562
563         DLEYNA_LOG_DEBUG("Enter");
564
565         DLEYNA_LOG_DEBUG("Path: %s", task->target.path);
566         DLEYNA_LOG_DEBUG("Query: %s", task->ut.search.query);
567         DLEYNA_LOG_DEBUG("Start: %u", task->ut.search.start);
568         DLEYNA_LOG_DEBUG("Count: %u", task->ut.search.count);
569
570         cb_data->cb = cb;
571         cb_task_data = &cb_data->ut.bas;
572
573         cb_task_data->filter_mask =
574                 dls_props_parse_filter(upnp->filter_map,
575                                        task->ut.search.filter, &upnp_filter);
576
577         DLEYNA_LOG_DEBUG("Filter Mask 0x%"G_GUINT64_FORMAT"x",
578                          cb_task_data->filter_mask);
579
580         upnp_query = dls_search_translate_search_string(upnp->filter_map,
581                                                         task->ut.search.query);
582         if (!upnp_query) {
583                 DLEYNA_LOG_WARNING("Query string is not valid:%s",
584                                    task->ut.search.query);
585
586                 cb_data->error = g_error_new(DLEYNA_SERVER_ERROR,
587                                              DLEYNA_ERROR_BAD_QUERY,
588                                              "Query string is not valid.");
589                 goto on_error;
590         }
591
592         DLEYNA_LOG_DEBUG("UPnP Query %s", upnp_query);
593
594         sort_by = dls_sort_translate_sort_string(upnp->filter_map,
595                                                  task->ut.search.sort_by);
596         if (!sort_by) {
597                 DLEYNA_LOG_WARNING("Invalid Sort Criteria");
598
599                 cb_data->error = g_error_new(DLEYNA_SERVER_ERROR,
600                                              DLEYNA_ERROR_BAD_QUERY,
601                                              "Sort Criteria are not valid");
602                 goto on_error;
603         }
604
605         DLEYNA_LOG_DEBUG("Sort By %s", sort_by);
606
607         cb_task_data->protocol_info = client->protocol_info;
608
609         dls_device_search(client, task, upnp_filter, upnp_query, sort_by);
610 on_error:
611
612         if (!cb_data->action)
613                 (void) g_idle_add(dls_async_task_complete, cb_data);
614
615         g_free(sort_by);
616         g_free(upnp_query);
617         g_free(upnp_filter);
618
619         DLEYNA_LOG_DEBUG("Exit with %s", !cb_data->action ? "FAIL" : "SUCCESS");
620 }
621
622 void dls_upnp_get_resource(dls_upnp_t *upnp, dls_client_t *client,
623                            dls_task_t *task,
624                            dls_upnp_task_complete_t cb)
625 {
626         dls_async_task_t *cb_data = (dls_async_task_t *)task;
627         dls_async_get_all_t *cb_task_data;
628         gchar *upnp_filter = NULL;
629
630         DLEYNA_LOG_DEBUG("Enter");
631
632         DLEYNA_LOG_DEBUG("Protocol Info: %s ", task->ut.resource.protocol_info);
633
634         cb_data->cb = cb;
635         cb_task_data = &cb_data->ut.get_all;
636
637         DLEYNA_LOG_DEBUG("Root Path %s Id %s", task->target.root_path,
638                          task->target.id);
639
640         cb_task_data->filter_mask =
641                 dls_props_parse_filter(upnp->filter_map,
642                                        task->ut.resource.filter, &upnp_filter);
643
644         DLEYNA_LOG_DEBUG("Filter Mask 0x%"G_GUINT64_FORMAT"x",
645                          cb_task_data->filter_mask);
646
647         dls_device_get_resource(client, task, upnp_filter);
648
649         g_free(upnp_filter);
650
651         DLEYNA_LOG_DEBUG("Exit");
652 }
653
654 static gboolean prv_compute_mime_and_class(dls_task_t *task,
655                                            dls_async_upload_t *cb_task_data,
656                                            GError **error)
657 {
658         gchar *content_type = NULL;
659
660         if (!g_file_test(task->ut.upload.file_path,
661                          G_FILE_TEST_IS_REGULAR | G_FILE_TEST_EXISTS)) {
662                 DLEYNA_LOG_WARNING(
663                         "File %s does not exist or is not a regular file",
664                         task->ut.upload.file_path);
665
666                 *error = g_error_new(DLEYNA_SERVER_ERROR,
667                                      DLEYNA_ERROR_OBJECT_NOT_FOUND,
668                                      "File %s does not exist or is not a regular file",
669                                      task->ut.upload.file_path);
670                 goto on_error;
671         }
672
673         content_type = g_content_type_guess(task->ut.upload.file_path, NULL, 0,
674                                             NULL);
675
676         if (!content_type) {
677                 DLEYNA_LOG_WARNING("Unable to determine Content Type for %s",
678                                    task->ut.upload.file_path);
679
680                 *error = g_error_new(DLEYNA_SERVER_ERROR, DLEYNA_ERROR_BAD_MIME,
681                                      "Unable to determine Content Type for %s",
682                                      task->ut.upload.file_path);
683                 goto on_error;
684         }
685
686         cb_task_data->mime_type = g_content_type_get_mime_type(content_type);
687         g_free(content_type);
688
689         if (!cb_task_data->mime_type) {
690                 DLEYNA_LOG_WARNING("Unable to determine MIME Type for %s",
691                                    task->ut.upload.file_path);
692
693                 *error = g_error_new(DLEYNA_SERVER_ERROR, DLEYNA_ERROR_BAD_MIME,
694                                      "Unable to determine MIME Type for %s",
695                                      task->ut.upload.file_path);
696                 goto on_error;
697         }
698
699         if (g_content_type_is_a(cb_task_data->mime_type, "image/*")) {
700                 cb_task_data->object_class = "object.item.imageItem";
701         } else if (g_content_type_is_a(cb_task_data->mime_type, "audio/*")) {
702                 cb_task_data->object_class = "object.item.audioItem";
703         } else if (g_content_type_is_a(cb_task_data->mime_type, "video/*")) {
704                 cb_task_data->object_class = "object.item.videoItem";
705         } else {
706                 DLEYNA_LOG_WARNING("Unsupported MIME Type %s",
707                                    cb_task_data->mime_type);
708
709                 *error = g_error_new(DLEYNA_SERVER_ERROR, DLEYNA_ERROR_BAD_MIME,
710                                      "Unsupported MIME Type %s",
711                                      cb_task_data->mime_type);
712                 goto on_error;
713         }
714
715         return TRUE;
716
717 on_error:
718
719         return FALSE;
720 }
721
722 void dls_upnp_upload_to_any(dls_upnp_t *upnp, dls_client_t *client,
723                             dls_task_t *task,
724                             dls_upnp_task_complete_t cb)
725 {
726         dls_async_task_t *cb_data = (dls_async_task_t *)task;
727         dls_async_upload_t *cb_task_data;
728
729         DLEYNA_LOG_DEBUG("Enter");
730
731         cb_data->cb = cb;
732         cb_task_data = &cb_data->ut.upload;
733
734         DLEYNA_LOG_DEBUG("Root Path %s Id %s", task->target.root_path,
735                          task->target.id);
736
737         if (strcmp(task->target.id, "0")) {
738                 DLEYNA_LOG_WARNING("Bad path %s", task->target.path);
739
740                 cb_data->error =
741                         g_error_new(DLEYNA_SERVER_ERROR, DLEYNA_ERROR_BAD_PATH,
742                                     "UploadToAnyContainer must be executed on a root path");
743                 goto on_error;
744         }
745
746         if (!prv_compute_mime_and_class(task, cb_task_data, &cb_data->error))
747                 goto on_error;
748
749         DLEYNA_LOG_DEBUG("MIME Type %s", cb_task_data->mime_type);
750         DLEYNA_LOG_DEBUG("Object class %s", cb_task_data->object_class);
751
752         dls_device_upload(client, task, "DLNA.ORG_AnyContainer");
753
754 on_error:
755
756         if (!cb_data->action)
757                 (void) g_idle_add(dls_async_task_complete, cb_data);
758
759         DLEYNA_LOG_DEBUG("Exit");
760 }
761
762 void dls_upnp_upload(dls_upnp_t *upnp, dls_client_t *client, dls_task_t *task,
763                      dls_upnp_task_complete_t cb)
764 {
765         dls_async_task_t *cb_data = (dls_async_task_t *)task;
766         dls_async_upload_t *cb_task_data;
767
768         DLEYNA_LOG_DEBUG("Enter");
769
770         cb_data->cb = cb;
771         cb_task_data = &cb_data->ut.upload;
772
773         if (!prv_compute_mime_and_class(task, cb_task_data, &cb_data->error))
774                 goto on_error;
775
776         DLEYNA_LOG_DEBUG("MIME Type %s", cb_task_data->mime_type);
777         DLEYNA_LOG_DEBUG("Object class %s", cb_task_data->object_class);
778
779         dls_device_upload(client, task, task->target.id);
780
781 on_error:
782
783         if (!cb_data->action)
784                 (void) g_idle_add(dls_async_task_complete, cb_data);
785
786         DLEYNA_LOG_DEBUG("Exit");
787 }
788
789 void dls_upnp_get_upload_status(dls_upnp_t *upnp, dls_task_t *task)
790 {
791         GError *error = NULL;
792
793         DLEYNA_LOG_DEBUG("Enter");
794
795         DLEYNA_LOG_DEBUG("Root Path %s Id %s", task->target.root_path,
796                          task->target.id);
797
798         if (strcmp(task->target.id, "0")) {
799                 DLEYNA_LOG_WARNING("Bad path %s", task->target.path);
800
801                 error = g_error_new(DLEYNA_SERVER_ERROR, DLEYNA_ERROR_BAD_PATH,
802                                     "GetUploadStatus must be executed on a root path");
803                 goto on_error;
804         }
805
806         (void) dls_device_get_upload_status(task, &error);
807
808 on_error:
809
810         if (error) {
811                 dls_task_fail(task, error);
812                 g_error_free(error);
813         } else {
814                 dls_task_complete(task);
815         }
816
817         DLEYNA_LOG_DEBUG("Exit");
818 }
819
820 void dls_upnp_get_upload_ids(dls_upnp_t *upnp, dls_task_t *task)
821 {
822         GError *error = NULL;
823
824         DLEYNA_LOG_DEBUG("Enter");
825
826         DLEYNA_LOG_DEBUG("Root Path %s Id %s", task->target.root_path,
827                          task->target.id);
828
829         if (strcmp(task->target.id, "0")) {
830                 DLEYNA_LOG_WARNING("Bad path %s", task->target.path);
831
832                 error = g_error_new(DLEYNA_SERVER_ERROR, DLEYNA_ERROR_BAD_PATH,
833                                     "GetUploadIDs must be executed on a root path");
834                 goto on_error;
835         }
836
837          dls_device_get_upload_ids(task);
838
839 on_error:
840
841         if (error) {
842                 dls_task_fail(task, error);
843                 g_error_free(error);
844         } else {
845                 dls_task_complete(task);
846         }
847
848         DLEYNA_LOG_DEBUG("Exit");
849 }
850
851 void dls_upnp_cancel_upload(dls_upnp_t *upnp, dls_task_t *task)
852 {
853         GError *error = NULL;
854
855         DLEYNA_LOG_DEBUG("Enter");
856
857         DLEYNA_LOG_DEBUG("Root Path %s Id %s", task->target.root_path,
858                          task->target.id);
859
860         if (strcmp(task->target.id, "0")) {
861                 DLEYNA_LOG_WARNING("Bad path %s", task->target.path);
862
863                 error = g_error_new(DLEYNA_SERVER_ERROR, DLEYNA_ERROR_BAD_PATH,
864                                     "CancelUpload must be executed on a root path");
865                 goto on_error;
866         }
867
868         (void) dls_device_cancel_upload(task, &error);
869
870 on_error:
871
872         if (error) {
873                 dls_task_fail(task, error);
874                 g_error_free(error);
875         } else {
876                 dls_task_complete(task);
877         }
878
879         DLEYNA_LOG_DEBUG("Exit");
880 }
881
882 void dls_upnp_delete_object(dls_upnp_t *upnp, dls_client_t *client,
883                             dls_task_t *task,
884                             dls_upnp_task_complete_t cb)
885 {
886         dls_async_task_t *cb_data = (dls_async_task_t *)task;
887
888         DLEYNA_LOG_DEBUG("Enter");
889
890         cb_data->cb = cb;
891
892         DLEYNA_LOG_DEBUG("Root Path %s Id %s", task->target.root_path,
893                          task->target.id);
894
895         dls_device_delete_object(client, task);
896
897         DLEYNA_LOG_DEBUG("Exit");
898 }
899
900 void dls_upnp_create_container(dls_upnp_t *upnp, dls_client_t *client,
901                                dls_task_t *task,
902                                dls_upnp_task_complete_t cb)
903 {
904         dls_async_task_t *cb_data = (dls_async_task_t *)task;
905
906         DLEYNA_LOG_DEBUG("Enter");
907
908         cb_data->cb = cb;
909
910         DLEYNA_LOG_DEBUG("Root Path %s Id %s", task->target.root_path,
911                          task->target.id);
912
913         dls_device_create_container(client, task, task->target.id);
914
915         if (!cb_data->action)
916                 (void) g_idle_add(dls_async_task_complete, cb_data);
917
918         DLEYNA_LOG_DEBUG("Exit");
919 }
920
921 void dls_upnp_create_container_in_any(dls_upnp_t *upnp, dls_client_t *client,
922                                       dls_task_t *task,
923                                       dls_upnp_task_complete_t cb)
924 {
925         dls_async_task_t *cb_data = (dls_async_task_t *)task;
926
927         DLEYNA_LOG_DEBUG("Enter");
928
929         cb_data->cb = cb;
930
931         DLEYNA_LOG_DEBUG("Root Path %s Id %s", task->target.root_path,
932                          task->target.id);
933
934         if (strcmp(task->target.id, "0")) {
935                 DLEYNA_LOG_WARNING("Bad path %s", task->target.path);
936
937                 cb_data->error =
938                         g_error_new(DLEYNA_SERVER_ERROR, DLEYNA_ERROR_BAD_PATH,
939                                     "CreateContainerInAnyContainer must be executed on a root path");
940                 goto on_error;
941         }
942
943         dls_device_create_container(client, task, "DLNA.ORG_AnyContainer");
944
945 on_error:
946
947         if (!cb_data->action)
948                 (void) g_idle_add(dls_async_task_complete, cb_data);
949
950         DLEYNA_LOG_DEBUG("Exit");
951 }
952
953 void dls_upnp_update_object(dls_upnp_t *upnp, dls_client_t *client,
954                             dls_task_t *task,
955                             dls_upnp_task_complete_t cb)
956 {
957         dls_async_task_t *cb_data = (dls_async_task_t *)task;
958         dls_async_update_t *cb_task_data;
959         dls_upnp_prop_mask mask;
960         gchar *upnp_filter = NULL;
961         dls_task_update_t *task_data;
962
963         DLEYNA_LOG_DEBUG("Enter");
964
965         cb_data->cb = cb;
966         cb_task_data = &cb_data->ut.update;
967         task_data = &task->ut.update;
968
969         DLEYNA_LOG_DEBUG("Root Path %s Id %s", task->target.root_path,
970                          task->target.id);
971
972         if (!dls_props_parse_update_filter(upnp->filter_map,
973                                            task_data->to_add_update,
974                                            task_data->to_delete,
975                                            &mask, &upnp_filter)) {
976                 DLEYNA_LOG_WARNING("Invalid Parameter");
977
978                 cb_data->error = g_error_new(DLEYNA_SERVER_ERROR,
979                                              DLEYNA_ERROR_OPERATION_FAILED,
980                                              "Invalid Parameter");
981                 goto on_error;
982         }
983
984         cb_task_data->map = upnp->filter_map;
985
986         DLEYNA_LOG_DEBUG("Filter = %s", upnp_filter);
987         DLEYNA_LOG_DEBUG("Mask = 0x%"G_GUINT64_FORMAT"x", mask);
988
989         if (mask == 0) {
990                 DLEYNA_LOG_WARNING("Empty Parameters");
991
992                 cb_data->error = g_error_new(DLEYNA_SERVER_ERROR,
993                                              DLEYNA_ERROR_OPERATION_FAILED,
994                                              "Empty Parameters");
995
996                 goto on_error;
997         }
998
999         dls_device_update_object(client, task, upnp_filter);
1000
1001 on_error:
1002
1003         g_free(upnp_filter);
1004
1005         if (!cb_data->action)
1006                 (void) g_idle_add(dls_async_task_complete, cb_data);
1007
1008         DLEYNA_LOG_DEBUG("Exit");
1009 }
1010
1011 void dls_upnp_get_object_metadata(dls_upnp_t *upnp, dls_client_t *client,
1012                                   dls_task_t *task, dls_upnp_task_complete_t cb)
1013 {
1014         dls_async_task_t *cb_data = (dls_async_task_t *)task;
1015
1016         DLEYNA_LOG_DEBUG("Enter");
1017
1018         cb_data->cb = cb;
1019
1020         DLEYNA_LOG_DEBUG("Root Path %s Id %s", task->target.root_path,
1021                          task->target.id);
1022
1023         dls_device_get_object_metadata(client, task, task->target.id);
1024
1025         DLEYNA_LOG_DEBUG("Exit");
1026 }
1027
1028 void dls_upnp_create_reference(dls_upnp_t *upnp, dls_client_t *client,
1029                                dls_task_t *task,
1030                                dls_upnp_task_complete_t cb)
1031 {
1032         dls_async_task_t *cb_data = (dls_async_task_t *)task;
1033
1034         DLEYNA_LOG_DEBUG("Enter");
1035
1036         cb_data->cb = cb;
1037
1038         DLEYNA_LOG_DEBUG("Root Path: %s - Id: %s", task->target.root_path,
1039                          task->target.id);
1040
1041         dls_device_create_reference(client, task);
1042
1043         DLEYNA_LOG_DEBUG("Exit");
1044
1045         return;
1046 }
1047
1048 void dls_upnp_get_icon(dls_upnp_t *upnp, dls_client_t *client,
1049                        dls_task_t *task,
1050                        dls_upnp_task_complete_t cb)
1051 {
1052         dls_async_task_t *cb_data = (dls_async_task_t *)task;
1053
1054         DLEYNA_LOG_DEBUG("Enter");
1055
1056         cb_data->cb = cb;
1057
1058         dls_device_get_icon(client, task);
1059
1060         DLEYNA_LOG_DEBUG("Exit");
1061 }
1062
1063 void dls_upnp_unsubscribe(dls_upnp_t *upnp)
1064 {
1065         GHashTableIter iter;
1066         gpointer value;
1067         dls_device_t *device;
1068
1069         DLEYNA_LOG_DEBUG("Enter");
1070
1071         g_hash_table_iter_init(&iter, upnp->server_udn_map);
1072         while (g_hash_table_iter_next(&iter, NULL, &value)) {
1073                 device = value;
1074                 dls_device_unsubscribe(device);
1075         }
1076
1077         DLEYNA_LOG_DEBUG("Exit");
1078 }
1079
1080 static gboolean prv_device_uc_find(gpointer key, gpointer value,
1081                                    gpointer user_data)
1082 {
1083         prv_device_new_ct_t *priv_t = (prv_device_new_ct_t *)value;
1084
1085         return (priv_t->device == user_data) ? TRUE : FALSE;
1086 }
1087
1088 static gboolean prv_device_find(gpointer key, gpointer value,
1089                                 gpointer user_data)
1090 {
1091         return (value == user_data) ? TRUE : FALSE;
1092 }
1093
1094 gboolean dls_upnp_device_context_exist(dls_device_t *device,
1095                                        dls_device_context_t *context)
1096 {
1097         gpointer result;
1098         guint i;
1099         gboolean found = FALSE;
1100         dls_upnp_t *upnp = dls_server_get_upnp();
1101
1102         if (upnp == NULL)
1103                 goto on_exit;
1104
1105         /* Check if the device still exist */
1106         result = g_hash_table_find(upnp->server_udn_map, prv_device_find,
1107                                    device);
1108
1109         if (result == NULL)
1110                 if (g_hash_table_find(upnp->server_uc_map, prv_device_uc_find,
1111                                       device) == NULL)
1112                         goto on_exit;
1113
1114         /* Search if the context still exist in the device */
1115         for (i = 0; i < device->contexts->len; ++i) {
1116                 if (g_ptr_array_index(device->contexts, i) == context) {
1117                         found = TRUE;
1118                         break;
1119                 }
1120         }
1121
1122 on_exit:
1123         return found;
1124 }
1125
1126 void dls_upnp_rescan(dls_upnp_t *upnp)
1127 {
1128         DLEYNA_LOG_DEBUG("re-scanning control points");
1129
1130         gupnp_context_manager_rescan_control_points(upnp->context_manager);
1131 }