Update media-service-upnp to version 0.3.0 ( ca17a69 )
[profile/ivi/media-service-upnp.git] / src / media-service-upnp.c
1 /*
2  * media-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 <signal.h>
24 #include <stdbool.h>
25 #include <stdint.h>
26 #include <stdio.h>
27 #include <stdlib.h>
28 #include <string.h>
29 #include <syslog.h>
30 #include <sys/signalfd.h>
31
32 #include "client.h"
33 #include "interface.h"
34 #include "log.h"
35 #include "settings.h"
36 #include "task.h"
37 #include "upnp.h"
38
39 typedef struct msu_context_t_ msu_context_t;
40 struct msu_context_t_ {
41         bool error;
42         guint msu_id;
43         guint sig_id;
44         guint idle_id;
45         guint owner_id;
46         GDBusNodeInfo *root_node_info;
47         GDBusNodeInfo *server_node_info;
48         GMainLoop *main_loop;
49         GDBusConnection *connection;
50         gboolean quitting;
51         GPtrArray *tasks;
52         GHashTable *watchers;
53         GCancellable *cancellable;
54         msu_task_t *current_task;
55         msu_upnp_t *upnp;
56         msu_settings_context_t *settings;
57 };
58
59 static msu_context_t g_context;
60
61 static const gchar g_msu_root_introspection[] =
62         "<node>"
63         "  <interface name='"MSU_INTERFACE_MANAGER"'>"
64         "    <method name='"MSU_INTERFACE_GET_VERSION"'>"
65         "      <arg type='s' name='"MSU_INTERFACE_VERSION"'"
66         "           direction='out'/>"
67         "    </method>"
68         "    <method name='"MSU_INTERFACE_RELEASE"'>"
69         "    </method>"
70         "    <method name='"MSU_INTERFACE_GET_SERVERS"'>"
71         "      <arg type='ao' name='"MSU_INTERFACE_SERVERS"'"
72         "           direction='out'/>"
73         "    </method>"
74         "    <method name='"MSU_INTERFACE_SET_PROTOCOL_INFO"'>"
75         "      <arg type='s' name='"MSU_INTERFACE_PROTOCOL_INFO"'"
76         "           direction='in'/>"
77         "    </method>"
78         "    <method name='"MSU_INTERFACE_PREFER_LOCAL_ADDRESSES"'>"
79         "      <arg type='b' name='"MSU_INTERFACE_PREFER"'"
80         "           direction='in'/>"
81         "    </method>"
82         "    <signal name='"MSU_INTERFACE_FOUND_SERVER"'>"
83         "      <arg type='o' name='"MSU_INTERFACE_PATH"'/>"
84         "    </signal>"
85         "    <signal name='"MSU_INTERFACE_LOST_SERVER"'>"
86         "      <arg type='o' name='"MSU_INTERFACE_PATH"'/>"
87         "    </signal>"
88         "  </interface>"
89         "</node>";
90
91 static const gchar g_msu_server_introspection[] =
92         "<node>"
93         "  <interface name='"MSU_INTERFACE_PROPERTIES"'>"
94         "    <method name='"MSU_INTERFACE_GET"'>"
95         "      <arg type='s' name='"MSU_INTERFACE_INTERFACE_NAME"'"
96         "           direction='in'/>"
97         "      <arg type='s' name='"MSU_INTERFACE_PROPERTY_NAME"'"
98         "           direction='in'/>"
99         "      <arg type='v' name='"MSU_INTERFACE_VALUE"'"
100         "           direction='out'/>"
101         "    </method>"
102         "    <method name='"MSU_INTERFACE_GET_ALL"'>"
103         "      <arg type='s' name='"MSU_INTERFACE_INTERFACE_NAME"'"
104         "           direction='in'/>"
105         "      <arg type='a{sv}' name='"MSU_INTERFACE_PROPERTIES_VALUE"'"
106         "           direction='out'/>"
107         "    </method>"
108         "    <signal name='"MSU_INTERFACE_PROPERTIES_CHANGED"'>"
109         "      <arg type='s' name='"MSU_INTERFACE_INTERFACE_NAME"'/>"
110         "      <arg type='a{sv}' name='"MSU_INTERFACE_CHANGED_PROPERTIES"'/>"
111         "      <arg type='as' name='"MSU_INTERFACE_INVALIDATED_PROPERTIES"'/>"
112         "    </signal>"
113         "  </interface>"
114         "  <interface name='"MSU_INTERFACE_MEDIA_OBJECT"'>"
115         "    <property type='o' name='"MSU_INTERFACE_PROP_PARENT"'"
116         "       access='read'/>"
117         "    <property type='s' name='"MSU_INTERFACE_PROP_TYPE"'"
118         "       access='read'/>"
119         "    <property type='o' name='"MSU_INTERFACE_PROP_PATH"'"
120         "       access='read'/>"
121         "    <property type='s' name='"MSU_INTERFACE_PROP_DISPLAY_NAME"'"
122         "       access='read'/>"
123         "    <property type='s' name='"MSU_INTERFACE_PROP_CREATOR"'"
124         "       access='read'/>"
125         "    <property type='b' name='"MSU_INTERFACE_PROP_RESTRICTED"'"
126         "       access='read'/>"
127         "    <property type='a{sb}' name='"MSU_INTERFACE_PROP_DLNA_MANAGED"'"
128         "       access='read'/>"
129         "    <method name='"MSU_INTERFACE_DELETE"'>"
130         "    </method>"
131         "    <method name='"MSU_INTERFACE_UPDATE"'>"
132         "      <arg type='a{sv}' name='"MSU_INTERFACE_TO_ADD_UPDATE"'"
133         "           direction='in'/>"
134         "      <arg type='as' name='"MSU_INTERFACE_TO_DELETE"'"
135         "           direction='in'/>"
136         "    </method>"
137         "  </interface>"
138         "  <interface name='"MSU_INTERFACE_MEDIA_CONTAINER"'>"
139         "    <method name='"MSU_INTERFACE_LIST_CHILDREN"'>"
140         "      <arg type='u' name='"MSU_INTERFACE_OFFSET"'"
141         "           direction='in'/>"
142         "      <arg type='u' name='"MSU_INTERFACE_MAX"'"
143         "           direction='in'/>"
144         "      <arg type='as' name='"MSU_INTERFACE_FILTER"'"
145         "           direction='in'/>"
146         "      <arg type='aa{sv}' name='"MSU_INTERFACE_CHILDREN"'"
147         "           direction='out'/>"
148         "    </method>"
149         "    <method name='"MSU_INTERFACE_LIST_CHILDREN_EX"'>"
150         "      <arg type='u' name='"MSU_INTERFACE_OFFSET"'"
151         "           direction='in'/>"
152         "      <arg type='u' name='"MSU_INTERFACE_MAX"'"
153         "           direction='in'/>"
154         "      <arg type='as' name='"MSU_INTERFACE_FILTER"'"
155         "           direction='in'/>"
156         "      <arg type='s' name='"MSU_INTERFACE_SORT_BY"'"
157         "           direction='in'/>"
158         "      <arg type='aa{sv}' name='"MSU_INTERFACE_CHILDREN"'"
159         "           direction='out'/>"
160         "    </method>"
161         "    <method name='"MSU_INTERFACE_LIST_CONTAINERS"'>"
162         "      <arg type='u' name='"MSU_INTERFACE_OFFSET"'"
163         "           direction='in'/>"
164         "      <arg type='u' name='"MSU_INTERFACE_MAX"'"
165         "           direction='in'/>"
166         "      <arg type='as' name='"MSU_INTERFACE_FILTER"'"
167         "           direction='in'/>"
168         "      <arg type='aa{sv}' name='"MSU_INTERFACE_CHILDREN"'"
169         "           direction='out'/>"
170         "    </method>"
171         "    <method name='"MSU_INTERFACE_LIST_CONTAINERS_EX"'>"
172         "      <arg type='u' name='"MSU_INTERFACE_OFFSET"'"
173         "           direction='in'/>"
174         "      <arg type='u' name='"MSU_INTERFACE_MAX"'"
175         "           direction='in'/>"
176         "      <arg type='as' name='"MSU_INTERFACE_FILTER"'"
177         "           direction='in'/>"
178         "      <arg type='s' name='"MSU_INTERFACE_SORT_BY"'"
179         "           direction='in'/>"
180         "      <arg type='aa{sv}' name='"MSU_INTERFACE_CHILDREN"'"
181         "           direction='out'/>"
182         "    </method>"
183         "    <method name='"MSU_INTERFACE_LIST_ITEMS"'>"
184         "      <arg type='u' name='"MSU_INTERFACE_OFFSET"'"
185         "           direction='in'/>"
186         "      <arg type='u' name='"MSU_INTERFACE_MAX"'"
187         "           direction='in'/>"
188         "      <arg type='as' name='"MSU_INTERFACE_FILTER"'"
189         "           direction='in'/>"
190         "      <arg type='aa{sv}' name='"MSU_INTERFACE_CHILDREN"'"
191         "           direction='out'/>"
192         "    </method>"
193         "    <method name='"MSU_INTERFACE_LIST_ITEMS_EX"'>"
194         "      <arg type='u' name='"MSU_INTERFACE_OFFSET"'"
195         "           direction='in'/>"
196         "      <arg type='u' name='"MSU_INTERFACE_MAX"'"
197         "           direction='in'/>"
198         "      <arg type='as' name='"MSU_INTERFACE_FILTER"'"
199         "           direction='in'/>"
200         "      <arg type='s' name='"MSU_INTERFACE_SORT_BY"'"
201         "           direction='in'/>"
202         "      <arg type='aa{sv}' name='"MSU_INTERFACE_CHILDREN"'"
203         "           direction='out'/>"
204         "    </method>"
205         "    <method name='"MSU_INTERFACE_SEARCH_OBJECTS"'>"
206         "      <arg type='s' name='"MSU_INTERFACE_QUERY"'"
207         "           direction='in'/>"
208         "      <arg type='u' name='"MSU_INTERFACE_OFFSET"'"
209         "           direction='in'/>"
210         "      <arg type='u' name='"MSU_INTERFACE_MAX"'"
211         "           direction='in'/>"
212         "      <arg type='as' name='"MSU_INTERFACE_FILTER"'"
213         "           direction='in'/>"
214         "      <arg type='aa{sv}' name='"MSU_INTERFACE_CHILDREN"'"
215         "           direction='out'/>"
216         "    </method>"
217         "    <method name='"MSU_INTERFACE_SEARCH_OBJECTS_EX"'>"
218         "      <arg type='s' name='"MSU_INTERFACE_QUERY"'"
219         "           direction='in'/>"
220         "      <arg type='u' name='"MSU_INTERFACE_OFFSET"'"
221         "           direction='in'/>"
222         "      <arg type='u' name='"MSU_INTERFACE_MAX"'"
223         "           direction='in'/>"
224         "      <arg type='as' name='"MSU_INTERFACE_FILTER"'"
225         "           direction='in'/>"
226         "      <arg type='s' name='"MSU_INTERFACE_SORT_BY"'"
227         "           direction='in'/>"
228         "      <arg type='aa{sv}' name='"MSU_INTERFACE_CHILDREN"'"
229         "           direction='out'/>"
230         "      <arg type='u' name='"MSU_INTERFACE_TOTAL_ITEMS"'"
231         "           direction='out'/>"
232         "    </method>"
233         "    <method name='"MSU_INTERFACE_UPLOAD"'>"
234         "      <arg type='s' name='"MSU_INTERFACE_PROP_DISPLAY_NAME"'"
235         "           direction='in'/>"
236         "      <arg type='s' name='"MSU_INTERFACE_FILE_PATH"'"
237         "           direction='in'/>"
238         "      <arg type='u' name='"MSU_INTERFACE_UPLOAD_ID"'"
239         "           direction='out'/>"
240         "      <arg type='o' name='"MSU_INTERFACE_PATH"'"
241         "           direction='out'/>"
242         "    </method>"
243         "    <method name='"MSU_INTERFACE_CREATE_CONTAINER"'>"
244         "      <arg type='s' name='"MSU_INTERFACE_PROP_DISPLAY_NAME"'"
245         "           direction='in'/>"
246         "      <arg type='s' name='"MSU_INTERFACE_PROP_TYPE"'"
247         "           direction='in'/>"
248         "      <arg type='as' name='"MSU_INTERFACE_CHILD_TYPES"'"
249         "           direction='in'/>"
250         "      <arg type='o' name='"MSU_INTERFACE_PATH"'"
251         "           direction='out'/>"
252         "    </method>"
253         "    <property type='u' name='"MSU_INTERFACE_PROP_CHILD_COUNT"'"
254         "       access='read'/>"
255         "    <property type='b' name='"MSU_INTERFACE_PROP_SEARCHABLE"'"
256         "       access='read'/>"
257         "    <property type='a(sb)' name='"MSU_INTERFACE_PROP_CREATE_CLASSES"'"
258         "       access='read'/>"
259         "  </interface>"
260         "  <interface name='"MSU_INTERFACE_MEDIA_ITEM"'>"
261         "    <method name='"MSU_INTERFACE_GET_COMPATIBLE_RESOURCE"'>"
262         "      <arg type='s' name='"MSU_INTERFACE_PROTOCOL_INFO"'"
263         "           direction='in'/>"
264         "      <arg type='as' name='"MSU_INTERFACE_FILTER"'"
265         "           direction='in'/>"
266         "      <arg type='a{sv}' name='"MSU_INTERFACE_PROPERTIES_VALUE"'"
267         "           direction='out'/>"
268         "    </method>"
269         "    <property type='as' name='"MSU_INTERFACE_PROP_URLS"'"
270         "       access='read'/>"
271         "    <property type='s' name='"MSU_INTERFACE_PROP_MIME_TYPE"'"
272         "       access='read'/>"
273         "    <property type='s' name='"MSU_INTERFACE_PROP_ARTIST"'"
274         "       access='read'/>"
275         "    <property type='as' name='"MSU_INTERFACE_PROP_ARTISTS"'"
276         "       access='read'/>"
277         "    <property type='s' name='"MSU_INTERFACE_PROP_ALBUM"'"
278         "       access='read'/>"
279         "    <property type='s' name='"MSU_INTERFACE_PROP_DATE"'"
280         "       access='read'/>"
281         "    <property type='s' name='"MSU_INTERFACE_PROP_GENRE"'"
282         "       access='read'/>"
283         "    <property type='s' name='"MSU_INTERFACE_PROP_DLNA_PROFILE"'"
284         "       access='read'/>"
285         "    <property type='i' name='"MSU_INTERFACE_PROP_TRACK_NUMBER"'"
286         "       access='read'/>"
287         "    <property type='x' name='"MSU_INTERFACE_PROP_SIZE"'"
288         "       access='read'/>"
289         "    <property type='i' name='"MSU_INTERFACE_PROP_DURATION"'"
290         "       access='read'/>"
291         "    <property type='i' name='"MSU_INTERFACE_PROP_BITRATE"'"
292         "       access='read'/>"
293         "    <property type='i' name='"MSU_INTERFACE_PROP_SAMPLE_RATE"'"
294         "       access='read'/>"
295         "    <property type='i' name='"MSU_INTERFACE_PROP_BITS_PER_SAMPLE"'"
296         "       access='read'/>"
297         "    <property type='i' name='"MSU_INTERFACE_PROP_WIDTH"'"
298         "       access='read'/>"
299         "    <property type='i' name='"MSU_INTERFACE_PROP_HEIGHT"'"
300         "       access='read'/>"
301         "    <property type='i' name='"MSU_INTERFACE_PROP_COLOR_DEPTH"'"
302         "       access='read'/>"
303         "    <property type='s' name='"MSU_INTERFACE_PROP_ALBUM_ART_URL"'"
304         "       access='read'/>"
305         "    <property type='o' name='"MSU_INTERFACE_PROP_REFPATH"'"
306         "       access='read'/>"
307         "    <property type='aa{sv}' name='"MSU_INTERFACE_PROP_RESOURCES"'"
308         "       access='read'/>"
309         "  </interface>"
310         "  <interface name='"MSU_INTERFACE_MEDIA_DEVICE"'>"
311         "    <method name='"MSU_INTERFACE_UPLOAD_TO_ANY"'>"
312         "      <arg type='s' name='"MSU_INTERFACE_PROP_DISPLAY_NAME"'"
313         "           direction='in'/>"
314         "      <arg type='s' name='"MSU_INTERFACE_FILE_PATH"'"
315         "           direction='in'/>"
316         "      <arg type='u' name='"MSU_INTERFACE_UPLOAD_ID"'"
317         "           direction='out'/>"
318         "      <arg type='o' name='"MSU_INTERFACE_PATH"'"
319         "           direction='out'/>"
320         "    </method>"
321         "    <method name='"MSU_INTERFACE_GET_UPLOAD_STATUS"'>"
322         "      <arg type='u' name='"MSU_INTERFACE_UPLOAD_ID"'"
323         "           direction='in'/>"
324         "      <arg type='s' name='"MSU_INTERFACE_UPLOAD_STATUS"'"
325         "           direction='out'/>"
326         "      <arg type='t' name='"MSU_INTERFACE_LENGTH"'"
327         "           direction='out'/>"
328         "      <arg type='t' name='"MSU_INTERFACE_TOTAL"'"
329         "           direction='out'/>"
330         "    </method>"
331         "    <method name='"MSU_INTERFACE_GET_UPLOAD_IDS"'>"
332         "      <arg type='au' name='"MSU_INTERFACE_TOTAL"'"
333         "           direction='out'/>"
334         "    </method>"
335         "    <method name='"MSU_INTERFACE_CANCEL_UPLOAD"'>"
336         "      <arg type='u' name='"MSU_INTERFACE_UPLOAD_ID"'"
337         "           direction='in'/>"
338         "    </method>"
339         "    <method name='"MSU_INTERFACE_CREATE_CONTAINER_IN_ANY"'>"
340         "      <arg type='s' name='"MSU_INTERFACE_PROP_DISPLAY_NAME"'"
341         "           direction='in'/>"
342         "      <arg type='s' name='"MSU_INTERFACE_PROP_TYPE"'"
343         "           direction='in'/>"
344         "      <arg type='as' name='"MSU_INTERFACE_CHILD_TYPES"'"
345         "           direction='in'/>"
346         "      <arg type='o' name='"MSU_INTERFACE_PATH"'"
347         "           direction='out'/>"
348         "    </method>"
349         "    <property type='s' name='"MSU_INTERFACE_PROP_LOCATION"'"
350         "       access='read'/>"
351         "    <property type='s' name='"MSU_INTERFACE_PROP_UDN"'"
352         "       access='read'/>"
353         "    <property type='s' name='"MSU_INTERFACE_PROP_DEVICE_TYPE"'"
354         "       access='read'/>"
355         "    <property type='s' name='"MSU_INTERFACE_PROP_FRIENDLY_NAME"'"
356         "       access='read'/>"
357         "    <property type='s' name='"MSU_INTERFACE_PROP_MANUFACTURER"'"
358         "       access='read'/>"
359         "    <property type='s' name='"MSU_INTERFACE_PROP_MANUFACTURER_URL"'"
360         "       access='read'/>"
361         "    <property type='s' name='"MSU_INTERFACE_PROP_MODEL_DESCRIPTION"'"
362         "       access='read'/>"
363         "    <property type='s' name='"MSU_INTERFACE_PROP_MODEL_NAME"'"
364         "       access='read'/>"
365         "    <property type='s' name='"MSU_INTERFACE_PROP_MODEL_NUMBER"'"
366         "       access='read'/>"
367         "    <property type='s' name='"MSU_INTERFACE_PROP_MODEL_URL"'"
368         "       access='read'/>"
369         "    <property type='s' name='"MSU_INTERFACE_PROP_SERIAL_NUMBER"'"
370         "       access='read'/>"
371         "    <property type='s' name='"MSU_INTERFACE_PROP_PRESENTATION_URL"'"
372         "       access='read'/>"
373         "    <property type='s' name='"MSU_INTERFACE_PROP_ICON_URL"'"
374         "       access='read'/>"
375         "    <property type='a{sv}'name='"
376         MSU_INTERFACE_PROP_SV_DLNA_CAPABILITIES"'"
377         "       access='read'/>"
378         "    <property type='as' name='"
379         MSU_INTERFACE_PROP_SV_SEARCH_CAPABILITIES"'"
380         "       access='read'/>"
381         "    <property type='as' name='"
382         MSU_INTERFACE_PROP_SV_SORT_CAPABILITIES"'"
383         "       access='read'/>"
384         "    <property type='as' name='"
385         MSU_INTERFACE_PROP_SV_SORT_EXT_CAPABILITIES"'"
386         "       access='read'/>"
387         "    <property type='a(ssao)' name='"
388         MSU_INTERFACE_PROP_SV_FEATURE_LIST"'"
389         "       access='read'/>"
390         "    <property type='u' name='"
391         MSU_INTERFACE_PROP_ESV_SYSTEM_UPDATE_ID"'"
392         "       access='read'/>"
393         "    <property type='s' name='"
394         MSU_INTERFACE_PROP_ESV_SERVICE_RESET_TOKEN"'"
395         "       access='read'/>"
396         "    <signal name='"MSU_INTERFACE_CONTAINER_UPDATE"'>"
397         "      <arg type='ao' name='"MSU_INTERFACE_CONTAINER_PATHS"'/>"
398         "    </signal>"
399         "    <signal name='"MSU_INTERFACE_UPLOAD_UPDATE"'>"
400         "      <arg type='u' name='"MSU_INTERFACE_UPLOAD_ID"'/>"
401         "      <arg type='s' name='"MSU_INTERFACE_UPLOAD_STATUS"'/>"
402         "      <arg type='t' name='"MSU_INTERFACE_LENGTH"'/>"
403         "      <arg type='t' name='"MSU_INTERFACE_TOTAL"'/>"
404         "    </signal>"
405         "  </interface>"
406         "</node>";
407
408 static gboolean prv_process_task(gpointer user_data);
409
410 static void prv_free_msu_task_cb(gpointer data, gpointer user_data)
411 {
412         msu_task_delete(data);
413 }
414
415 static void prv_process_sync_task(msu_task_t *task)
416 {
417         const gchar *client_name;
418         msu_client_t *client;
419
420         g_context.current_task = task;
421
422         switch (task->type) {
423         case MSU_TASK_GET_VERSION:
424                 msu_task_complete_and_delete(task);
425                 break;
426         case MSU_TASK_GET_SERVERS:
427                 task->result = msu_upnp_get_server_ids(g_context.upnp);
428                 msu_task_complete_and_delete(task);
429                 break;
430         case MSU_TASK_SET_PROTOCOL_INFO:
431                 client_name =
432                         g_dbus_method_invocation_get_sender(task->invocation);
433                 client = g_hash_table_lookup(g_context.watchers, client_name);
434                 if (client) {
435                         g_free(client->protocol_info);
436                         if (task->ut.protocol_info.protocol_info[0]) {
437                                 client->protocol_info =
438                                         task->ut.protocol_info.protocol_info;
439                                 task->ut.protocol_info.protocol_info = NULL;
440                         } else {
441                                 client->protocol_info = NULL;
442                         }
443                 }
444                 msu_task_complete_and_delete(task);
445                 break;
446         case MSU_TASK_SET_PREFER_LOCAL_ADDRESSES:
447                 client_name =
448                         g_dbus_method_invocation_get_sender(task->invocation);
449                 client = g_hash_table_lookup(g_context.watchers, client_name);
450                 if (client) {
451                         client->prefer_local_addresses =
452                                         task->ut.prefer_local_addresses.prefer;
453                 }
454                 msu_task_complete_and_delete(task);
455                 break;
456         case MSU_TASK_GET_UPLOAD_STATUS:
457                 msu_upnp_get_upload_status(g_context.upnp, task);
458                 break;
459         case MSU_TASK_GET_UPLOAD_IDS:
460                 msu_upnp_get_upload_ids(g_context.upnp, task);
461                 break;
462         case MSU_TASK_CANCEL_UPLOAD:
463                 msu_upnp_cancel_upload(g_context.upnp, task);
464                 break;
465         default:
466                 break;
467         }
468
469         g_context.current_task = NULL;
470 }
471
472 static void prv_async_task_complete(msu_task_t *task, GVariant *result,
473                                     GError *error)
474 {
475         MSU_LOG_DEBUG("Enter");
476
477         g_object_unref(g_context.cancellable);
478         g_context.cancellable = NULL;
479         g_context.current_task = NULL;
480
481         if (error) {
482                 msu_task_fail_and_delete(task, error);
483                 g_error_free(error);
484         } else {
485                 task->result = result;
486                 msu_task_complete_and_delete(task);
487         }
488
489         if (g_context.quitting)
490                 g_main_loop_quit(g_context.main_loop);
491         else if (g_context.tasks->len > 0)
492                 g_context.idle_id = g_idle_add(prv_process_task, NULL);
493
494         MSU_LOG_DEBUG("Exit");
495 }
496
497 static void prv_process_async_task(msu_task_t *task)
498 {
499         const gchar *client_name;
500         msu_client_t *client;
501
502         MSU_LOG_DEBUG("Enter");
503
504         g_context.cancellable = g_cancellable_new();
505         g_context.current_task = task;
506         client_name =
507                 g_dbus_method_invocation_get_sender(task->invocation);
508         client = g_hash_table_lookup(g_context.watchers, client_name);
509
510         switch (task->type) {
511         case MSU_TASK_GET_CHILDREN:
512                 msu_upnp_get_children(g_context.upnp, client, task,
513                                       g_context.cancellable,
514                                       prv_async_task_complete);
515                 break;
516         case MSU_TASK_GET_PROP:
517                 msu_upnp_get_prop(g_context.upnp, client, task,
518                                   g_context.cancellable,
519                                   prv_async_task_complete);
520                 break;
521         case MSU_TASK_GET_ALL_PROPS:
522                 msu_upnp_get_all_props(g_context.upnp, client, task,
523                                        g_context.cancellable,
524                                        prv_async_task_complete);
525                 break;
526         case MSU_TASK_SEARCH:
527                 msu_upnp_search(g_context.upnp, client, task,
528                                 g_context.cancellable,
529                                 prv_async_task_complete);
530                 break;
531         case MSU_TASK_GET_RESOURCE:
532                 msu_upnp_get_resource(g_context.upnp, client, task,
533                                       g_context.cancellable,
534                                       prv_async_task_complete);
535                 break;
536         case MSU_TASK_UPLOAD_TO_ANY:
537                 msu_upnp_upload_to_any(g_context.upnp, client, task,
538                                        g_context.cancellable,
539                                        prv_async_task_complete);
540                 break;
541         case MSU_TASK_UPLOAD:
542                 msu_upnp_upload(g_context.upnp, client, task,
543                                 g_context.cancellable,
544                                 prv_async_task_complete);
545                 break;
546         case MSU_TASK_DELETE_OBJECT:
547                 msu_upnp_delete_object(g_context.upnp, client, task,
548                                        g_context.cancellable,
549                                        prv_async_task_complete);
550                 break;
551         case MSU_TASK_CREATE_CONTAINER:
552                 msu_upnp_create_container(g_context.upnp, client, task,
553                                           g_context.cancellable,
554                                           prv_async_task_complete);
555                 break;
556         case MSU_TASK_CREATE_CONTAINER_IN_ANY:
557                 msu_upnp_create_container_in_any(g_context.upnp, client, task,
558                                                  g_context.cancellable,
559                                                  prv_async_task_complete);
560                 break;
561         case MSU_TASK_UPDATE_OBJECT:
562                 msu_upnp_update_object(g_context.upnp, client, task,
563                                        g_context.cancellable,
564                                        prv_async_task_complete);
565                 break;
566         default:
567                 break;
568         }
569
570         MSU_LOG_DEBUG("Exit");
571 }
572
573 static gboolean prv_process_task(gpointer user_data)
574 {
575         msu_task_t *task;
576         gboolean retval = FALSE;
577
578         if (g_context.tasks->len > 0) {
579                 task = g_ptr_array_index(g_context.tasks, 0);
580                 if (task->synchronous) {
581                         prv_process_sync_task(task);
582                         retval = TRUE;
583                 } else {
584                         prv_process_async_task(task);
585                         g_context.idle_id = 0;
586                 }
587                 g_ptr_array_remove_index(g_context.tasks, 0);
588         } else {
589                 g_context.idle_id = 0;
590         }
591
592         return retval;
593 }
594
595 static void prv_msu_method_call(GDBusConnection *conn,
596                                 const gchar *sender,
597                                 const gchar *object,
598                                 const gchar *interface,
599                                 const gchar *method,
600                                 GVariant *parameters,
601                                 GDBusMethodInvocation *invocation,
602                                 gpointer user_data);
603
604 static void prv_object_method_call(GDBusConnection *conn,
605                                    const gchar *sender,
606                                    const gchar *object,
607                                    const gchar *interface,
608                                    const gchar *method,
609                                    GVariant *parameters,
610                                    GDBusMethodInvocation *invocation,
611                                    gpointer user_data);
612
613 static void prv_item_method_call(GDBusConnection *conn,
614                                  const gchar *sender,
615                                  const gchar *object,
616                                  const gchar *interface,
617                                  const gchar *method,
618                                  GVariant *parameters,
619                                  GDBusMethodInvocation *invocation,
620                                  gpointer user_data);
621
622 static void prv_con_method_call(GDBusConnection *conn,
623                                 const gchar *sender,
624                                 const gchar *object,
625                                 const gchar *interface,
626                                 const gchar *method,
627                                 GVariant *parameters,
628                                 GDBusMethodInvocation *invocation,
629                                 gpointer user_data);
630
631 static void prv_props_method_call(GDBusConnection *conn,
632                                   const gchar *sender,
633                                   const gchar *object,
634                                   const gchar *interface,
635                                   const gchar *method,
636                                   GVariant *parameters,
637                                   GDBusMethodInvocation *invocation,
638                                   gpointer user_data);
639
640 static void prv_device_method_call(GDBusConnection *conn,
641                                    const gchar *sender,
642                                    const gchar *object,
643                                    const gchar *interface,
644                                    const gchar *method,
645                                    GVariant *parameters,
646                                    GDBusMethodInvocation *invocation,
647                                    gpointer user_data);
648
649 static const GDBusInterfaceVTable g_msu_vtable = {
650         prv_msu_method_call,
651         NULL,
652         NULL
653 };
654
655 static const GDBusInterfaceVTable g_object_vtable = {
656         prv_object_method_call,
657         NULL,
658         NULL
659 };
660
661 static const GDBusInterfaceVTable g_item_vtable = {
662         prv_item_method_call,
663         NULL,
664         NULL
665 };
666
667 static const GDBusInterfaceVTable g_ms_vtable = {
668         prv_con_method_call,
669         NULL,
670         NULL
671 };
672
673 static const GDBusInterfaceVTable g_props_vtable = {
674         prv_props_method_call,
675         NULL,
676         NULL
677 };
678
679 static const GDBusInterfaceVTable g_device_vtable = {
680         prv_device_method_call,
681         NULL,
682         NULL
683 };
684
685 static const GDBusInterfaceVTable *g_server_vtables[MSU_INTERFACE_INFO_MAX] = {
686         &g_props_vtable,
687         &g_object_vtable,
688         &g_ms_vtable,
689         &g_item_vtable,
690         &g_device_vtable
691 };
692
693 static void prv_msu_context_init(void)
694 {
695         memset(&g_context, 0, sizeof(g_context));
696 }
697
698 static void prv_msu_context_free(void)
699 {
700         msu_upnp_delete(g_context.upnp);
701
702         if (g_context.watchers)
703                 g_hash_table_unref(g_context.watchers);
704
705         if (g_context.tasks) {
706                 g_ptr_array_foreach(g_context.tasks, prv_free_msu_task_cb,
707                                     NULL);
708                 g_ptr_array_unref(g_context.tasks);
709         }
710
711         if (g_context.idle_id)
712                 (void) g_source_remove(g_context.idle_id);
713
714         if (g_context.sig_id)
715                 (void) g_source_remove(g_context.sig_id);
716
717         if (g_context.connection) {
718                 if (g_context.msu_id)
719                         g_dbus_connection_unregister_object(
720                                                         g_context.connection,
721                                                         g_context.msu_id);
722         }
723
724         if (g_context.owner_id)
725                 g_bus_unown_name(g_context.owner_id);
726
727         if (g_context.connection)
728                 g_object_unref(g_context.connection);
729
730         if (g_context.main_loop)
731                 g_main_loop_unref(g_context.main_loop);
732
733         if (g_context.server_node_info)
734                 g_dbus_node_info_unref(g_context.server_node_info);
735
736         if (g_context.root_node_info)
737                 g_dbus_node_info_unref(g_context.root_node_info);
738
739         if (g_context.settings)
740                 msu_settings_delete(g_context.settings);
741 }
742
743 static void prv_quit(void)
744 {
745         if (g_context.cancellable) {
746                 g_cancellable_cancel(g_context.cancellable);
747                 g_context.quitting = TRUE;
748         } else {
749                 g_main_loop_quit(g_context.main_loop);
750         }
751 }
752
753 static void prv_remove_client(const gchar *name)
754 {
755         const gchar *client_name;
756         msu_task_t *task;
757         guint pos;
758
759         if (g_context.cancellable) {
760                 client_name = g_dbus_method_invocation_get_sender(
761                                         g_context.current_task->invocation);
762                 if (!strcmp(client_name, name)) {
763                         MSU_LOG_DEBUG("Cancelling current task, type is %d",
764                                       g_context.current_task->type);
765
766                         g_cancellable_cancel(g_context.cancellable);
767                 }
768         }
769
770         pos = 0;
771         while (pos < g_context.tasks->len) {
772                 task = (msu_task_t *) g_ptr_array_index(g_context.tasks, pos);
773
774                 client_name = g_dbus_method_invocation_get_sender(
775                                                         task->invocation);
776
777                 if (strcmp(client_name, name)) {
778                         pos++;
779                         continue;
780                 }
781
782                 MSU_LOG_DEBUG("Removing task type %d from array", task->type);
783
784                 (void) g_ptr_array_remove_index(g_context.tasks, pos);
785                 msu_task_cancel_and_delete(task);
786         }
787
788         (void) g_hash_table_remove(g_context.watchers, name);
789
790         if (g_hash_table_size(g_context.watchers) == 0)
791                 if (!msu_settings_is_never_quit(g_context.settings))
792                         prv_quit();
793 }
794
795 static void prv_lost_client(GDBusConnection *connection, const gchar *name,
796                             gpointer user_data)
797 {
798         MSU_LOG_DEBUG("Lost Client %s", name);
799
800         prv_remove_client(name);
801 }
802
803 static void prv_add_task(msu_task_t *task)
804 {
805         const gchar *client_name;
806         msu_client_t *client;
807
808         client_name = g_dbus_method_invocation_get_sender(task->invocation);
809
810         if (!g_hash_table_lookup(g_context.watchers, client_name)) {
811                 client = g_new0(msu_client_t, 1);
812                 client->prefer_local_addresses = TRUE;
813                 client->id = g_bus_watch_name(G_BUS_TYPE_SESSION, client_name,
814                                               G_BUS_NAME_WATCHER_FLAGS_NONE,
815                                               NULL, prv_lost_client, NULL,
816                                               NULL);
817                 g_hash_table_insert(g_context.watchers, g_strdup(client_name),
818                                     client);
819         }
820
821         if (!g_context.cancellable && !g_context.idle_id)
822                 g_context.idle_id = g_idle_add(prv_process_task, NULL);
823
824         g_ptr_array_add(g_context.tasks, task);
825 }
826
827 static void prv_msu_method_call(GDBusConnection *conn,
828                                 const gchar *sender, const gchar *object,
829                                 const gchar *interface,
830                                 const gchar *method, GVariant *parameters,
831                                 GDBusMethodInvocation *invocation,
832                                 gpointer user_data)
833 {
834         const gchar *client_name;
835         msu_task_t *task;
836
837         if (!strcmp(method, MSU_INTERFACE_RELEASE)) {
838                 client_name = g_dbus_method_invocation_get_sender(invocation);
839                 prv_remove_client(client_name);
840                 g_dbus_method_invocation_return_value(invocation, NULL);
841         } else if (!strcmp(method, MSU_INTERFACE_GET_VERSION)) {
842                 task = msu_task_get_version_new(invocation);
843                 prv_add_task(task);
844         } else if (!strcmp(method, MSU_INTERFACE_GET_SERVERS)) {
845                 task = msu_task_get_servers_new(invocation);
846                 prv_add_task(task);
847         } else if (!strcmp(method, MSU_INTERFACE_SET_PROTOCOL_INFO)) {
848                 task = msu_task_set_protocol_info_new(invocation, parameters);
849                 prv_add_task(task);
850         } else if (!strcmp(method, MSU_INTERFACE_PREFER_LOCAL_ADDRESSES)) {
851                 task = msu_task_prefer_local_addresses_new(invocation,
852                                                            parameters);
853                 prv_add_task(task);
854         }
855 }
856
857 static void prv_object_method_call(GDBusConnection *conn,
858                                    const gchar *sender, const gchar *object,
859                                    const gchar *interface,
860                                    const gchar *method, GVariant *parameters,
861                                    GDBusMethodInvocation *invocation,
862                                    gpointer user_data)
863 {
864         msu_task_t *task;
865
866         if (!strcmp(method, MSU_INTERFACE_DELETE)) {
867                 task = msu_task_delete_new(invocation, object);
868         } else if (!strcmp(method, MSU_INTERFACE_UPDATE))
869                 task = msu_task_update_new(invocation, object, parameters);
870         else
871                 goto finished;
872
873         prv_add_task(task);
874
875 finished:
876
877         return;
878 }
879
880 static void prv_item_method_call(GDBusConnection *conn,
881                                  const gchar *sender, const gchar *object,
882                                  const gchar *interface,
883                                  const gchar *method, GVariant *parameters,
884                                  GDBusMethodInvocation *invocation,
885                                  gpointer user_data)
886 {
887         msu_task_t *task;
888
889         if (!strcmp(method, MSU_INTERFACE_GET_COMPATIBLE_RESOURCE)) {
890                 task = msu_task_get_resource_new(invocation, object,
891                                                  parameters);
892                 prv_add_task(task);
893         }
894 }
895
896
897 static void prv_con_method_call(GDBusConnection *conn,
898                                 const gchar *sender,
899                                 const gchar *object,
900                                 const gchar *interface,
901                                 const gchar *method,
902                                 GVariant *parameters,
903                                 GDBusMethodInvocation *invocation,
904                                 gpointer user_data)
905 {
906         msu_task_t *task;
907
908         if (!strcmp(method, MSU_INTERFACE_LIST_CHILDREN))
909                 task = msu_task_get_children_new(invocation, object,
910                                                  parameters, TRUE, TRUE);
911         else if (!strcmp(method, MSU_INTERFACE_LIST_CHILDREN_EX))
912                 task = msu_task_get_children_ex_new(invocation, object,
913                                                     parameters, TRUE, TRUE);
914         else if (!strcmp(method, MSU_INTERFACE_LIST_ITEMS))
915                 task = msu_task_get_children_new(invocation, object,
916                                                  parameters, TRUE, FALSE);
917         else if (!strcmp(method, MSU_INTERFACE_LIST_ITEMS_EX))
918                 task = msu_task_get_children_ex_new(invocation, object,
919                                                     parameters, TRUE, FALSE);
920         else if (!strcmp(method, MSU_INTERFACE_LIST_CONTAINERS))
921                 task = msu_task_get_children_new(invocation, object,
922                                                  parameters, FALSE, TRUE);
923         else if (!strcmp(method, MSU_INTERFACE_LIST_CONTAINERS_EX))
924                 task = msu_task_get_children_ex_new(invocation, object,
925                                                     parameters, FALSE, TRUE);
926         else if (!strcmp(method, MSU_INTERFACE_SEARCH_OBJECTS))
927                 task = msu_task_search_new(invocation, object,
928                                            parameters);
929         else if (!strcmp(method, MSU_INTERFACE_SEARCH_OBJECTS_EX))
930                 task = msu_task_search_ex_new(invocation, object,
931                                               parameters);
932         else if (!strcmp(method, MSU_INTERFACE_UPLOAD))
933                 task = msu_task_upload_new(invocation, object, parameters);
934         else if (!strcmp(method, MSU_INTERFACE_CREATE_CONTAINER))
935                 task = msu_task_create_container_new_generic(invocation,
936                                                 MSU_TASK_CREATE_CONTAINER,
937                                                 object, parameters);
938         else
939                 goto finished;
940
941         prv_add_task(task);
942
943 finished:
944
945         return;
946 }
947
948 static void prv_props_method_call(GDBusConnection *conn,
949                                   const gchar *sender,
950                                   const gchar *object,
951                                   const gchar *interface,
952                                   const gchar *method,
953                                   GVariant *parameters,
954                                   GDBusMethodInvocation *invocation,
955                                   gpointer user_data)
956 {
957         msu_task_t *task;
958
959         if (!strcmp(method, MSU_INTERFACE_GET_ALL))
960                 task = msu_task_get_props_new(invocation, object, parameters);
961         else if (!strcmp(method, MSU_INTERFACE_GET))
962                 task = msu_task_get_prop_new(invocation, object, parameters);
963         else
964                 goto finished;
965
966         prv_add_task(task);
967
968 finished:
969
970         return;
971 }
972
973 static void prv_device_method_call(GDBusConnection *conn,
974                                    const gchar *sender, const gchar *object,
975                                    const gchar *interface,
976                                    const gchar *method, GVariant *parameters,
977                                    GDBusMethodInvocation *invocation,
978                                    gpointer user_data)
979 {
980         msu_task_t *task;
981
982         if (!strcmp(method, MSU_INTERFACE_UPLOAD_TO_ANY)) {
983                 task = msu_task_upload_to_any_new(invocation, object,
984                                                   parameters);
985                 prv_add_task(task);
986         } else if (!strcmp(method, MSU_INTERFACE_CREATE_CONTAINER_IN_ANY)) {
987                 task = msu_task_create_container_new_generic(invocation,
988                                         MSU_TASK_CREATE_CONTAINER_IN_ANY,
989                                         object, parameters);
990                 prv_add_task(task);
991         } else if (!strcmp(method, MSU_INTERFACE_GET_UPLOAD_STATUS)) {
992                 task = msu_task_get_upload_status_new(invocation, object,
993                                                       parameters);
994                 prv_add_task(task);
995         } else if (!strcmp(method, MSU_INTERFACE_GET_UPLOAD_IDS)) {
996                 task = msu_task_get_upload_ids_new(invocation, object);
997                 prv_add_task(task);
998         } else if (!strcmp(method, MSU_INTERFACE_CANCEL_UPLOAD)) {
999                 task = msu_task_cancel_upload_new(invocation, object,
1000                                                   parameters);
1001                 prv_add_task(task);
1002         }
1003 }
1004
1005 static void prv_found_media_server(const gchar *path, void *user_data)
1006 {
1007         (void) g_dbus_connection_emit_signal(g_context.connection,
1008                                              NULL,
1009                                              MSU_OBJECT,
1010                                              MSU_INTERFACE_MANAGER,
1011                                              MSU_INTERFACE_FOUND_SERVER,
1012                                              g_variant_new("(o)", path),
1013                                              NULL);
1014 }
1015
1016 static void prv_lost_media_server(const gchar *path, void *user_data)
1017 {
1018         (void) g_dbus_connection_emit_signal(g_context.connection,
1019                                              NULL,
1020                                              MSU_OBJECT,
1021                                              MSU_INTERFACE_MANAGER,
1022                                              MSU_INTERFACE_LOST_SERVER,
1023                                              g_variant_new("(o)", path),
1024                                              NULL);
1025 }
1026
1027 static void prv_bus_acquired(GDBusConnection *connection, const gchar *name,
1028                              gpointer user_data)
1029 {
1030         msu_interface_info_t *info;
1031         unsigned int i;
1032
1033         g_context.connection = connection;
1034
1035         g_context.msu_id =
1036                 g_dbus_connection_register_object(connection, MSU_OBJECT,
1037                                                   g_context.root_node_info->
1038                                                   interfaces[0],
1039                                                   &g_msu_vtable,
1040                                                   user_data, NULL, NULL);
1041
1042         if (!g_context.msu_id) {
1043                 g_context.error = true;
1044                 g_main_loop_quit(g_context.main_loop);
1045         } else {
1046                 info = g_new(msu_interface_info_t, MSU_INTERFACE_INFO_MAX);
1047                 for (i = 0; i < MSU_INTERFACE_INFO_MAX; ++i) {
1048                         info[i].interface =
1049                                 g_context.server_node_info->interfaces[i];
1050                         info[i].vtable = g_server_vtables[i];
1051                 }
1052                 g_context.upnp = msu_upnp_new(connection, info,
1053                                             prv_found_media_server,
1054                                             prv_lost_media_server,
1055                                             user_data);
1056         }
1057 }
1058
1059 static void prv_name_lost(GDBusConnection *connection, const gchar *name,
1060                           gpointer user_data)
1061 {
1062         g_context.connection = NULL;
1063
1064         prv_quit();
1065 }
1066
1067 static gboolean prv_quit_handler(GIOChannel *source, GIOCondition condition,
1068                                  gpointer user_data)
1069 {
1070         prv_quit();
1071         g_context.sig_id = 0;
1072
1073         return FALSE;
1074 }
1075
1076 static bool prv_init_signal_handler(sigset_t mask)
1077 {
1078         bool retval = false;
1079         int fd = -1;
1080         GIOChannel *channel = NULL;
1081
1082         fd = signalfd(-1, &mask, SFD_NONBLOCK);
1083         if (fd == -1)
1084                 goto on_error;
1085
1086         channel = g_io_channel_unix_new(fd);
1087         g_io_channel_set_close_on_unref(channel, TRUE);
1088
1089         if (g_io_channel_set_flags(channel, G_IO_FLAG_NONBLOCK, NULL) !=
1090             G_IO_STATUS_NORMAL)
1091                 goto on_error;
1092
1093         if (g_io_channel_set_encoding(channel, NULL, NULL) !=
1094             G_IO_STATUS_NORMAL)
1095                 goto on_error;
1096
1097         g_context.sig_id = g_io_add_watch(channel, G_IO_IN | G_IO_PRI,
1098                                          prv_quit_handler,
1099                                          NULL);
1100
1101         retval = true;
1102
1103 on_error:
1104
1105         if (channel)
1106                 g_io_channel_unref(channel);
1107
1108         return retval;
1109 }
1110
1111 static void prv_unregister_client(gpointer user_data)
1112 {
1113         msu_client_t *client = user_data;
1114
1115         if (client) {
1116                 g_bus_unwatch_name(client->id);
1117                 g_free(client->protocol_info);
1118                 g_free(client);
1119         }
1120 }
1121
1122 int main(int argc, char *argv[])
1123 {
1124         sigset_t mask;
1125         int retval = 1;
1126
1127         prv_msu_context_init();
1128
1129         sigemptyset(&mask);
1130         sigaddset(&mask, SIGTERM);
1131         sigaddset(&mask, SIGINT);
1132
1133         if (sigprocmask(SIG_BLOCK, &mask, NULL) == -1)
1134                 goto on_error;
1135
1136         g_type_init();
1137
1138         msu_log_init(argv[0]);
1139         msu_settings_new(&g_context.settings);
1140
1141         g_context.root_node_info =
1142                 g_dbus_node_info_new_for_xml(g_msu_root_introspection, NULL);
1143         if (!g_context.root_node_info)
1144                 goto on_error;
1145
1146         g_context.server_node_info =
1147                 g_dbus_node_info_new_for_xml(g_msu_server_introspection, NULL);
1148         if (!g_context.server_node_info)
1149                 goto on_error;
1150
1151         g_context.main_loop = g_main_loop_new(NULL, FALSE);
1152
1153         g_context.owner_id = g_bus_own_name(G_BUS_TYPE_SESSION,
1154                                           MSU_SERVER_NAME,
1155                                           G_BUS_NAME_OWNER_FLAGS_NONE,
1156                                           prv_bus_acquired, NULL,
1157                                           prv_name_lost, NULL, NULL);
1158
1159         g_context.tasks = g_ptr_array_new();
1160
1161         g_context.watchers = g_hash_table_new_full(g_str_hash, g_str_equal,
1162                                                  g_free, prv_unregister_client);
1163
1164         if (!prv_init_signal_handler(mask))
1165                 goto on_error;
1166
1167         g_main_loop_run(g_context.main_loop);
1168         if (g_context.error)
1169                 goto on_error;
1170
1171         retval = 0;
1172
1173 on_error:
1174
1175         prv_msu_context_free();
1176
1177         msu_log_finalize();
1178
1179         return retval;
1180 }
1181