First submission of renderer-service-upnp
[profile/ivi/renderer-service-upnp.git] / src / upnp.c
1 /*
2  * renderer-service-upnp
3  *
4  * Copyright (C) 2012 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 <libgupnp/gupnp-context-manager.h>
26 #include <libgupnp/gupnp-error.h>
27
28 #include "async.h"
29 #include "device.h"
30 #include "error.h"
31 #include "host-service.h"
32 #include "log.h"
33 #include "prop-defs.h"
34 #include "upnp.h"
35
36 struct rsu_upnp_t_ {
37         GDBusConnection *connection;
38         rsu_interface_info_t *interface_info;
39         rsu_upnp_callback_t found_server;
40         rsu_upnp_callback_t lost_server;
41         GUPnPContextManager *context_manager;
42         void *user_data;
43         GHashTable *server_udn_map;
44         guint counter;
45         rsu_host_service_t *host_service;
46 };
47
48 static void prv_server_available_cb(GUPnPControlPoint *cp,
49                                     GUPnPDeviceProxy *proxy,
50                                     gpointer user_data)
51 {
52         rsu_upnp_t *upnp = user_data;
53         const char *udn;
54         rsu_device_t *device;
55         const gchar *ip_address;
56         rsu_device_context_t *context;
57         unsigned int i;
58
59         udn = gupnp_device_info_get_udn((GUPnPDeviceInfo *) proxy);
60         if (!udn)
61                 goto on_error;
62
63         ip_address = gupnp_context_get_host_ip(
64                 gupnp_control_point_get_context(cp));
65
66         device = g_hash_table_lookup(upnp->server_udn_map, udn);
67
68         if (!device) {
69                 if (rsu_device_new(upnp->connection, proxy,
70                                    ip_address,
71                                    upnp->counter,
72                                    upnp->interface_info,
73                                    upnp->user_data,
74                                    &device)) {
75                         ++upnp->counter;
76                         g_hash_table_insert(upnp->server_udn_map, g_strdup(udn),
77                                             device);
78                         upnp->found_server(device->path, upnp->user_data);
79                 }
80         } else {
81                 for (i = 0; i < device->contexts->len; ++i) {
82                         context = g_ptr_array_index(device->contexts, i);
83
84                         if (!strcmp(context->ip_address, ip_address))
85                                 break;
86                 }
87
88                 if (i == device->contexts->len)
89                         rsu_device_append_new_context(device, ip_address,
90                                                       proxy);
91         }
92
93 on_error:
94
95         return;
96 }
97
98 static gboolean prv_subscribe_to_service_changes(gpointer user_data)
99 {
100         rsu_device_t *device = user_data;
101
102         device->timeout_id = 0;
103         rsu_device_subscribe_to_service_changes(device);
104
105         return FALSE;
106 }
107
108 static void prv_server_unavailable_cb(GUPnPControlPoint *cp,
109                                       GUPnPDeviceProxy *proxy,
110                                       gpointer user_data)
111 {
112         rsu_upnp_t *upnp = user_data;
113         const char *udn;
114         rsu_device_t *device;
115         const gchar *ip_address;
116         unsigned int i;
117         rsu_device_context_t *context;
118         gboolean subscribed;
119
120         udn = gupnp_device_info_get_udn((GUPnPDeviceInfo *) proxy);
121         if (!udn)
122                 goto on_error;
123
124         ip_address = gupnp_context_get_host_ip(
125                 gupnp_control_point_get_context(cp));
126
127         device = g_hash_table_lookup(upnp->server_udn_map, udn);
128         if (!device)
129                 goto on_error;
130
131         for (i = 0; i < device->contexts->len; ++i) {
132                 context = g_ptr_array_index(device->contexts, i);
133
134                 if (!strcmp(context->ip_address, ip_address))
135                         break;
136         }
137
138         if (i < device->contexts->len) {
139                 subscribed = (context->subscribed_av || context->subscribed_cm);
140
141                 (void) g_ptr_array_remove_index(device->contexts, i);
142
143                 if (device->contexts->len == 0) {
144                         RSU_LOG_DEBUG("Last Context lost. Delete device");
145
146                         if (device->current_task)
147                                 rsu_async_task_lost_object(
148                                         device->current_task);
149
150                         upnp->lost_server(device->path, upnp->user_data);
151                         g_hash_table_remove(upnp->server_udn_map, udn);
152                 } else if (subscribed && !device->timeout_id) {
153                         RSU_LOG_DEBUG("Subscribe on new context");
154
155                         device->timeout_id = g_timeout_add_seconds(1,
156                                         prv_subscribe_to_service_changes,
157                                         device);
158                 }
159         }
160
161 on_error:
162
163         return;
164 }
165
166 static void prv_on_context_available(GUPnPContextManager *context_manager,
167                                      GUPnPContext *context,
168                                      gpointer user_data)
169 {
170         rsu_upnp_t *upnp = user_data;
171         GUPnPControlPoint *cp;
172
173         cp = gupnp_control_point_new(
174                 context,
175                 "urn:schemas-upnp-org:device:MediaRenderer:1");
176
177         g_signal_connect(cp, "device-proxy-available",
178                          G_CALLBACK(prv_server_available_cb), upnp);
179
180         g_signal_connect(cp, "device-proxy-unavailable",
181                          G_CALLBACK(prv_server_unavailable_cb), upnp);
182
183         gssdp_resource_browser_set_active(GSSDP_RESOURCE_BROWSER(cp), TRUE);
184         gupnp_context_manager_manage_control_point(upnp->context_manager, cp);
185         g_object_unref(cp);
186 }
187
188 rsu_upnp_t *rsu_upnp_new(GDBusConnection *connection,
189                          rsu_interface_info_t *interface_info,
190                          rsu_upnp_callback_t found_server,
191                          rsu_upnp_callback_t lost_server,
192                          void *user_data)
193 {
194         rsu_upnp_t *upnp = g_new0(rsu_upnp_t, 1);
195
196         upnp->connection = connection;
197         upnp->interface_info = interface_info;
198         upnp->user_data = user_data;
199         upnp->found_server = found_server;
200         upnp->lost_server = lost_server;
201
202         upnp->server_udn_map = g_hash_table_new_full(g_str_hash, g_str_equal,
203                                                      g_free,
204                                                      rsu_device_delete);
205         upnp->context_manager = gupnp_context_manager_create(0);
206
207         g_signal_connect(upnp->context_manager, "context-available",
208                          G_CALLBACK(prv_on_context_available),
209                          upnp);
210
211         rsu_host_service_new(&upnp->host_service);
212
213         return upnp;
214 }
215
216 void rsu_upnp_delete(rsu_upnp_t *upnp)
217 {
218         if (upnp) {
219                 rsu_host_service_delete(upnp->host_service);
220                 g_object_unref(upnp->context_manager);
221                 g_hash_table_unref(upnp->server_udn_map);
222
223                 g_free(upnp->interface_info);
224                 g_free(upnp);
225         }
226 }
227
228 GVariant *rsu_upnp_get_server_ids(rsu_upnp_t *upnp)
229 {
230         GVariantBuilder vb;
231         GHashTableIter iter;
232         gpointer value;
233         rsu_device_t *device;
234
235         g_variant_builder_init(&vb, G_VARIANT_TYPE("as"));
236         g_hash_table_iter_init(&iter, upnp->server_udn_map);
237
238         while (g_hash_table_iter_next(&iter, NULL, &value)) {
239                 device = value;
240                 g_variant_builder_add(&vb, "s", device->path);
241         }
242
243         return g_variant_ref_sink(g_variant_builder_end(&vb));
244 }
245
246 void rsu_upnp_get_prop(rsu_upnp_t *upnp, rsu_task_t *task,
247                        GCancellable *cancellable,
248                        rsu_upnp_task_complete_t cb,
249                        void *user_data)
250 {
251         rsu_device_t *device;
252         rsu_async_cb_data_t *cb_data;
253
254         device = rsu_device_from_path(task->path, upnp->server_udn_map);
255
256         if (!device) {
257                 cb_data = rsu_async_cb_data_new(task, cb, user_data, NULL, NULL,
258                                                 NULL);
259                 cb_data->error = g_error_new(RSU_ERROR,
260                                              RSU_ERROR_OBJECT_NOT_FOUND,
261                                              "Cannot locate a device"
262                                              " for the specified "
263                                              "object");
264                 (void) g_idle_add(rsu_async_complete_task, cb_data);
265         } else {
266                 rsu_device_get_prop(device, task, cancellable, cb, user_data);
267         }
268 }
269
270 void rsu_upnp_get_all_props(rsu_upnp_t *upnp, rsu_task_t *task,
271                             GCancellable *cancellable,
272                             rsu_upnp_task_complete_t cb,
273                             void *user_data)
274 {
275         rsu_device_t *device;
276         rsu_async_cb_data_t *cb_data;
277
278         device = rsu_device_from_path(task->path, upnp->server_udn_map);
279
280         if (!device) {
281                 cb_data = rsu_async_cb_data_new(task, cb, user_data, NULL, NULL,
282                                                 NULL);
283                 cb_data->error = g_error_new(RSU_ERROR,
284                                              RSU_ERROR_OBJECT_NOT_FOUND,
285                                              "Cannot locate a device"
286                                              " for the specified "
287                                              "object");
288                 (void) g_idle_add(rsu_async_complete_task, cb_data);
289         } else {
290                 rsu_device_get_all_props(device, task, cancellable, cb,
291                                          user_data);
292         }
293 }
294
295 void rsu_upnp_play(rsu_upnp_t *upnp, rsu_task_t *task,
296                    GCancellable *cancellable,
297                    rsu_upnp_task_complete_t cb,
298                    void *user_data)
299 {
300         rsu_device_t *device;
301         rsu_async_cb_data_t *cb_data;
302
303         device = rsu_device_from_path(task->path, upnp->server_udn_map);
304
305         if (!device) {
306                 cb_data = rsu_async_cb_data_new(task, cb, user_data, NULL, NULL,
307                                                 NULL);
308                 cb_data->error = g_error_new(RSU_ERROR,
309                                              RSU_ERROR_OBJECT_NOT_FOUND,
310                                              "Cannot locate a device"
311                                              " for the specified "
312                                              "object");
313                 (void) g_idle_add(rsu_async_complete_task, cb_data);
314         } else {
315                 rsu_device_play(device, task, cancellable, cb,
316                                 user_data);
317         }
318 }
319
320 void rsu_upnp_pause(rsu_upnp_t *upnp, rsu_task_t *task,
321                     GCancellable *cancellable,
322                     rsu_upnp_task_complete_t cb,
323                     void *user_data)
324 {
325         rsu_device_t *device;
326         rsu_async_cb_data_t *cb_data;
327
328         device = rsu_device_from_path(task->path, upnp->server_udn_map);
329
330         if (!device) {
331                 cb_data = rsu_async_cb_data_new(task, cb, user_data, NULL, NULL,
332                                                 NULL);
333                 cb_data->error = g_error_new(RSU_ERROR,
334                                              RSU_ERROR_OBJECT_NOT_FOUND,
335                                              "Cannot locate a device"
336                                              " for the specified "
337                                              "object");
338                 (void) g_idle_add(rsu_async_complete_task, cb_data);
339         } else {
340                 rsu_device_pause(device, task, cancellable, cb,
341                                  user_data);
342         }
343 }
344
345 void rsu_upnp_play_pause(rsu_upnp_t *upnp, rsu_task_t *task,
346                          GCancellable *cancellable,
347                          rsu_upnp_task_complete_t cb,
348                          void *user_data)
349 {
350         rsu_device_t *device;
351         rsu_async_cb_data_t *cb_data;
352
353         device = rsu_device_from_path(task->path, upnp->server_udn_map);
354
355         if (!device) {
356                 cb_data = rsu_async_cb_data_new(task, cb, user_data, NULL, NULL,
357                                                 NULL);
358                 cb_data->error = g_error_new(RSU_ERROR,
359                                              RSU_ERROR_OBJECT_NOT_FOUND,
360                                              "Cannot locate a device"
361                                              " for the specified "
362                                              "object");
363                 (void) g_idle_add(rsu_async_complete_task, cb_data);
364         } else {
365                 rsu_device_play_pause(device, task, cancellable, cb,
366                                       user_data);
367         }
368 }
369
370 void rsu_upnp_stop(rsu_upnp_t *upnp, rsu_task_t *task,
371                    GCancellable *cancellable,
372                    rsu_upnp_task_complete_t cb,
373                    void *user_data)
374 {
375         rsu_device_t *device;
376         rsu_async_cb_data_t *cb_data;
377
378         device = rsu_device_from_path(task->path, upnp->server_udn_map);
379
380         if (!device) {
381                 cb_data = rsu_async_cb_data_new(task, cb, user_data, NULL, NULL,
382                                                 NULL);
383                 cb_data->error = g_error_new(RSU_ERROR,
384                                              RSU_ERROR_OBJECT_NOT_FOUND,
385                                              "Cannot locate a device"
386                                              " for the specified "
387                                              "object");
388                 (void) g_idle_add(rsu_async_complete_task, cb_data);
389         } else {
390                 rsu_device_stop(device, task, cancellable, cb,
391                                 user_data);
392         }
393 }
394
395 void rsu_upnp_next(rsu_upnp_t *upnp, rsu_task_t *task,
396                    GCancellable *cancellable,
397                    rsu_upnp_task_complete_t cb,
398                    void *user_data)
399 {
400         rsu_device_t *device;
401         rsu_async_cb_data_t *cb_data;
402
403         device = rsu_device_from_path(task->path, upnp->server_udn_map);
404
405         if (!device) {
406                 cb_data = rsu_async_cb_data_new(task, cb, user_data, NULL, NULL,
407                                                 NULL);
408                 cb_data->error = g_error_new(RSU_ERROR,
409                                              RSU_ERROR_OBJECT_NOT_FOUND,
410                                              "Cannot locate a device"
411                                              " for the specified "
412                                              "object");
413                 (void) g_idle_add(rsu_async_complete_task, cb_data);
414         } else {
415                 rsu_device_next(device, task, cancellable, cb,
416                                 user_data);
417         }
418 }
419
420 void rsu_upnp_previous(rsu_upnp_t *upnp, rsu_task_t *task,
421                        GCancellable *cancellable,
422                        rsu_upnp_task_complete_t cb,
423                        void *user_data)
424 {
425         rsu_device_t *device;
426         rsu_async_cb_data_t *cb_data;
427
428         device = rsu_device_from_path(task->path, upnp->server_udn_map);
429
430         if (!device) {
431                 cb_data = rsu_async_cb_data_new(task, cb, user_data, NULL, NULL,
432                                                 NULL);
433                 cb_data->error = g_error_new(RSU_ERROR,
434                                              RSU_ERROR_OBJECT_NOT_FOUND,
435                                              "Cannot locate a device"
436                                              " for the specified "
437                                              "object");
438                 (void) g_idle_add(rsu_async_complete_task, cb_data);
439         } else {
440                 rsu_device_previous(device, task, cancellable, cb,
441                                     user_data);
442         }
443 }
444
445 void rsu_upnp_open_uri(rsu_upnp_t *upnp, rsu_task_t *task,
446                        GCancellable *cancellable,
447                        rsu_upnp_task_complete_t cb,
448                        void *user_data)
449 {
450         rsu_device_t *device;
451         rsu_async_cb_data_t *cb_data;
452
453         device = rsu_device_from_path(task->path, upnp->server_udn_map);
454
455         if (!device) {
456                 cb_data = rsu_async_cb_data_new(task, cb, user_data, NULL, NULL,
457                                                 NULL);
458                 cb_data->error = g_error_new(RSU_ERROR,
459                                              RSU_ERROR_OBJECT_NOT_FOUND,
460                                              "Cannot locate a device"
461                                              " for the specified "
462                                              "object");
463                 (void) g_idle_add(rsu_async_complete_task, cb_data);
464         } else {
465                 rsu_device_open_uri(device, task, cancellable, cb,
466                                     user_data);
467         }
468 }
469
470 void rsu_upnp_seek(rsu_upnp_t *upnp, rsu_task_t *task,
471                    GCancellable *cancellable,
472                    rsu_upnp_task_complete_t cb,
473                    void *user_data)
474 {
475         rsu_device_t *device;
476         rsu_async_cb_data_t *cb_data;
477
478         device = rsu_device_from_path(task->path, upnp->server_udn_map);
479
480         if (!device) {
481                 cb_data = rsu_async_cb_data_new(task, cb, user_data, NULL, NULL,
482                                                 NULL);
483                 cb_data->error = g_error_new(RSU_ERROR,
484                                              RSU_ERROR_OBJECT_NOT_FOUND,
485                                              "Cannot locate a device"
486                                              " for the specified "
487                                              "object");
488                 (void) g_idle_add(rsu_async_complete_task, cb_data);
489         } else {
490                 rsu_device_seek(device, task, cancellable, cb, user_data);
491         }
492 }
493
494 void rsu_upnp_set_position(rsu_upnp_t *upnp, rsu_task_t *task,
495                            GCancellable *cancellable,
496                            rsu_upnp_task_complete_t cb,
497                            void *user_data)
498 {
499         rsu_device_t *device;
500         rsu_async_cb_data_t *cb_data;
501
502         device = rsu_device_from_path(task->path, upnp->server_udn_map);
503
504         if (!device) {
505                 cb_data = rsu_async_cb_data_new(task, cb, user_data, NULL, NULL,
506                                                 NULL);
507                 cb_data->error = g_error_new(RSU_ERROR,
508                                              RSU_ERROR_OBJECT_NOT_FOUND,
509                                              "Cannot locate a device"
510                                              " for the specified "
511                                              "object");
512                 (void) g_idle_add(rsu_async_complete_task, cb_data);
513         } else {
514                 rsu_device_set_position(device, task, cancellable, cb,
515                                         user_data);
516         }
517 }
518
519 void rsu_upnp_host_uri(rsu_upnp_t *upnp, rsu_task_t *task,
520                        GCancellable *cancellable,
521                        rsu_upnp_task_complete_t cb,
522                        void *user_data)
523 {
524         rsu_device_t *device;
525         rsu_async_cb_data_t *cb_data;
526
527         device = rsu_device_from_path(task->path, upnp->server_udn_map);
528
529         if (!device) {
530                 cb_data = rsu_async_cb_data_new(task, cb, user_data, NULL, NULL,
531                                                 NULL);
532                 cb_data->error = g_error_new(RSU_ERROR,
533                                              RSU_ERROR_OBJECT_NOT_FOUND,
534                                              "Cannot locate a device"
535                                              " for the specified "
536                                              "object");
537                 (void) g_idle_add(rsu_async_complete_task, cb_data);
538         } else {
539                 rsu_device_host_uri(device, task, upnp->host_service,
540                                     cancellable, cb, user_data);
541         }
542 }
543
544 void rsu_upnp_remove_uri(rsu_upnp_t *upnp, rsu_task_t *task,
545                          GCancellable *cancellable,
546                          rsu_upnp_task_complete_t cb,
547                          void *user_data)
548 {
549         rsu_device_t *device;
550         rsu_async_cb_data_t *cb_data;
551
552         device = rsu_device_from_path(task->path, upnp->server_udn_map);
553
554         if (!device) {
555                 cb_data = rsu_async_cb_data_new(task, cb, user_data, NULL, NULL,
556                                                 NULL);
557                 cb_data->error = g_error_new(RSU_ERROR,
558                                              RSU_ERROR_OBJECT_NOT_FOUND,
559                                              "Cannot locate a device"
560                                              " for the specified "
561                                              "object");
562                 (void) g_idle_add(rsu_async_complete_task, cb_data);
563         } else {
564                 rsu_device_remove_uri(device, task, upnp->host_service,
565                                       cancellable, cb, user_data);
566         }
567 }
568
569 void rsu_upnp_lost_client(rsu_upnp_t *upnp, const gchar *client_name)
570 {
571         rsu_host_service_lost_client(upnp->host_service, client_name);
572 }