2dbd63b1ad12a5978e8a09ad1962790ade51558f
[profile/ivi/dLeyna.git] / dleyna-server / libdleyna / server / props.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 #include <libgupnp-av/gupnp-didl-lite-contributor.h>
25
26 #include <libdleyna/core/log.h>
27
28 #include "device.h"
29 #include "interface.h"
30 #include "path.h"
31 #include "props.h"
32
33 static const gchar gUPnPContainer[] = "object.container";
34 static const gchar gUPnPAlbum[] = "object.container.album";
35 static const gchar gUPnPPerson[] = "object.container.person";
36 static const gchar gUPnPGenre[] = "object.container.genre";
37 static const gchar gUPnPAudioItem[] = "object.item.audioItem";
38 static const gchar gUPnPVideoItem[] = "object.item.videoItem";
39 static const gchar gUPnPImageItem[] = "object.item.imageItem";
40 static const gchar gUPnPPlaylistItem[] = "object.item.playlistItem";
41 static const gchar gUPnPItem[] = "object.item";
42
43 static const unsigned int gUPnPContainerLen =
44         (sizeof(gUPnPContainer) / sizeof(gchar)) - 1;
45 static const unsigned int gUPnPAlbumLen =
46         (sizeof(gUPnPAlbum) / sizeof(gchar)) - 1;
47 static const unsigned int gUPnPPersonLen =
48         (sizeof(gUPnPPerson) / sizeof(gchar)) - 1;
49 static const unsigned int gUPnPGenreLen =
50         (sizeof(gUPnPGenre) / sizeof(gchar)) - 1;
51 static const unsigned int gUPnPAudioItemLen =
52         (sizeof(gUPnPAudioItem) / sizeof(gchar)) - 1;
53 static const unsigned int gUPnPVideoItemLen =
54         (sizeof(gUPnPVideoItem) / sizeof(gchar)) - 1;
55 static const unsigned int gUPnPImageItemLen =
56         (sizeof(gUPnPImageItem) / sizeof(gchar)) - 1;
57 static const unsigned int gUPnPPlaylistItemLen =
58         (sizeof(gUPnPPlaylistItem) / sizeof(gchar)) - 1;
59 static const unsigned int gUPnPItemLen =
60         (sizeof(gUPnPItem) / sizeof(gchar)) - 1;
61
62 static const gchar gUPnPPhotoAlbum[] = "object.container.album.photoAlbum";
63 static const gchar gUPnPMusicAlbum[] = "object.container.album.musicAlbum";
64 static const gchar gUPnPMusicArtist[] = "object.container.person.musicArtist";
65 static const gchar gUPnPMovieGenre[] = "object.container.genre.movieGenre";
66 static const gchar gUPnPMusicGenre[] = "object.container.genre.musicGenre";
67 static const gchar gUPnPMusicTrack[] = "object.item.audioItem.musicTrack";
68 static const gchar gUPnPAudioBroadcast[] =
69         "object.item.audioItem.audioBroadcast";
70 static const gchar gUPnPAudioBook[] = "object.item.audioItem.audioBook";
71 static const gchar gUPnPMovie[] = "object.item.videoItem.movie";
72 static const gchar gUPnPMusicVideoClip[] =
73         "object.item.videoItem.musicVideoClip";
74 static const gchar gUPnPVideoBroadcast[] =
75         "object.item.videoItem.videoBroadcast";
76 static const gchar gUPnPPhoto[] = "object.item.imageItem.photo";
77
78 static const gchar gMediaSpec2Container[] = "container";
79 static const gchar gMediaSpec2Album[] = "album";
80 static const gchar gMediaSpec2AlbumPhoto[] = "album.photo";
81 static const gchar gMediaSpec2AlbumMusic[] = "album.music";
82 static const gchar gMediaSpec2Person[] = "person";
83 static const gchar gMediaSpec2PersonMusicArtist[] = "person.musicartist";
84 static const gchar gMediaSpec2Genre[] = "genre";
85 static const gchar gMediaSpec2GenreMovie[] = "genre.movie";
86 static const gchar gMediaSpec2GenreMusic[] = "genre.music";
87 static const gchar gMediaSpec2AudioMusic[] = "audio.music";
88 static const gchar gMediaSpec2AudioBroadcast[] = "audio.broadcast";
89 static const gchar gMediaSpec2AudioBook[] = "audio.book";
90 static const gchar gMediaSpec2Audio[] = "audio";
91 static const gchar gMediaSpec2VideoMovie[] = "video.movie";
92 static const gchar gMediaSpec2VideoMusicClip[] = "video.musicclip";
93 static const gchar gMediaSpec2VideoBroadcast[] = "video.broadcast";
94 static const gchar gMediaSpec2Video[] = "video";
95 static const gchar gMediaSpec2ImagePhoto[] = "image.photo";
96 static const gchar gMediaSpec2Image[] = "image";
97 static const gchar gMediaSpec2Playlist[] = "playlist";
98 static const gchar gMediaSpec2Item[] = "item";
99
100 static dls_prop_map_t *prv_prop_map_new(const gchar *prop_name,
101                                             dls_upnp_prop_mask type,
102                                             gboolean filter,
103                                             gboolean searchable,
104                                             gboolean updateable)
105 {
106         dls_prop_map_t *retval = g_new(dls_prop_map_t, 1);
107         retval->upnp_prop_name = prop_name;
108         retval->type = type;
109         retval->filter = filter;
110         retval->searchable = searchable;
111         retval->updateable = updateable;
112         return retval;
113 }
114
115 void dls_prop_maps_new(GHashTable **property_map, GHashTable **filter_map)
116 {
117         dls_prop_map_t *prop_t;
118         GHashTable *p_map;
119         GHashTable *f_map;
120
121         p_map = g_hash_table_new_full(g_str_hash, g_str_equal, NULL, NULL);
122         f_map = g_hash_table_new_full(g_str_hash, g_str_equal, NULL, g_free);
123
124         /* @childCount */
125         prop_t = prv_prop_map_new("@childCount",
126                                         DLS_UPNP_MASK_PROP_CHILD_COUNT,
127                                         TRUE, TRUE, FALSE);
128         g_hash_table_insert(f_map, DLS_INTERFACE_PROP_CHILD_COUNT, prop_t);
129         g_hash_table_insert(p_map, "@childCount",
130                             DLS_INTERFACE_PROP_CHILD_COUNT);
131
132         /* @id */
133         prop_t = prv_prop_map_new("@id",
134                                         DLS_UPNP_MASK_PROP_PATH,
135                                         FALSE, TRUE, FALSE);
136         g_hash_table_insert(f_map, DLS_INTERFACE_PROP_PATH, prop_t);
137         g_hash_table_insert(p_map, "@id", DLS_INTERFACE_PROP_PATH);
138
139         /* @parentID */
140         prop_t = prv_prop_map_new("@parentID",
141                                         DLS_UPNP_MASK_PROP_PARENT,
142                                         FALSE, TRUE, FALSE);
143         g_hash_table_insert(f_map, DLS_INTERFACE_PROP_PARENT, prop_t);
144         g_hash_table_insert(p_map, "@parentID", DLS_INTERFACE_PROP_PARENT);
145
146         /* @refID */
147         prop_t = prv_prop_map_new("@refID",
148                                         DLS_UPNP_MASK_PROP_REFPATH,
149                                         TRUE, TRUE, FALSE);
150         g_hash_table_insert(f_map, DLS_INTERFACE_PROP_REFPATH, prop_t);
151         g_hash_table_insert(p_map, "@refID", DLS_INTERFACE_PROP_REFPATH);
152
153         /* @restricted */
154         prop_t = prv_prop_map_new("@restricted",
155                                         DLS_UPNP_MASK_PROP_RESTRICTED,
156                                         TRUE, TRUE, FALSE);
157         g_hash_table_insert(f_map, DLS_INTERFACE_PROP_RESTRICTED, prop_t);
158         g_hash_table_insert(p_map, "@restricted",
159                             DLS_INTERFACE_PROP_RESTRICTED);
160
161         /* @searchable */
162         prop_t = prv_prop_map_new("@searchable",
163                                         DLS_UPNP_MASK_PROP_SEARCHABLE,
164                                         TRUE, TRUE, FALSE);
165         g_hash_table_insert(f_map, DLS_INTERFACE_PROP_SEARCHABLE, prop_t);
166         g_hash_table_insert(p_map, "@searchable",
167                             DLS_INTERFACE_PROP_SEARCHABLE);
168
169         /* dc:creator */
170         prop_t = prv_prop_map_new("dc:creator",
171                                         DLS_UPNP_MASK_PROP_CREATOR,
172                                         TRUE, TRUE, FALSE);
173         g_hash_table_insert(f_map, DLS_INTERFACE_PROP_CREATOR, prop_t);
174         g_hash_table_insert(p_map, "dc:creator", DLS_INTERFACE_PROP_CREATOR);
175
176         /* dc:date */
177         prop_t = prv_prop_map_new("dc:date",
178                                         DLS_UPNP_MASK_PROP_DATE,
179                                         TRUE, TRUE, TRUE);
180         g_hash_table_insert(f_map, DLS_INTERFACE_PROP_DATE, prop_t);
181         g_hash_table_insert(p_map, "dc:date", DLS_INTERFACE_PROP_DATE);
182
183         /* dc:title */
184         prop_t = prv_prop_map_new("dc:title",
185                                         DLS_UPNP_MASK_PROP_DISPLAY_NAME,
186                                         FALSE, TRUE, TRUE);
187         g_hash_table_insert(f_map, DLS_INTERFACE_PROP_DISPLAY_NAME, prop_t);
188         g_hash_table_insert(p_map, "dc:title", DLS_INTERFACE_PROP_DISPLAY_NAME);
189
190         /* dlna:dlnaManaged */
191         prop_t = prv_prop_map_new("dlna:dlnaManaged",
192                                         DLS_UPNP_MASK_PROP_DLNA_MANAGED,
193                                         TRUE, FALSE, FALSE);
194         g_hash_table_insert(f_map, DLS_INTERFACE_PROP_DLNA_MANAGED, prop_t);
195         g_hash_table_insert(p_map, "dlna:dlnaManaged",
196                             DLS_INTERFACE_PROP_DLNA_MANAGED);
197
198         /* res */
199         /* res - RES */
200         prop_t = prv_prop_map_new("res",
201                                         DLS_UPNP_MASK_PROP_RESOURCES,
202                                         TRUE, FALSE, FALSE);
203         g_hash_table_insert(f_map, DLS_INTERFACE_PROP_RESOURCES, prop_t);
204
205         /* res - URL */
206         prop_t = prv_prop_map_new("res",
207                                         DLS_UPNP_MASK_PROP_URL,
208                                         TRUE, FALSE, FALSE);
209         g_hash_table_insert(f_map, DLS_INTERFACE_PROP_URL, prop_t);
210
211         /* res - URLS */
212         prop_t = prv_prop_map_new("res",
213                                         DLS_UPNP_MASK_PROP_URLS,
214                                         TRUE, FALSE, FALSE);
215         g_hash_table_insert(f_map, DLS_INTERFACE_PROP_URLS, prop_t);
216
217         /* res@bitrate */
218         prop_t = prv_prop_map_new("res@bitrate",
219                                         DLS_UPNP_MASK_PROP_BITRATE,
220                                         TRUE, TRUE, FALSE);
221         g_hash_table_insert(f_map, DLS_INTERFACE_PROP_BITRATE, prop_t);
222         g_hash_table_insert(p_map, "res@bitrate", DLS_INTERFACE_PROP_BITRATE);
223
224         /* res@bitsPerSample */
225         prop_t = prv_prop_map_new("res@bitsPerSample",
226                                         DLS_UPNP_MASK_PROP_BITS_PER_SAMPLE,
227                                         TRUE, TRUE, FALSE);
228         g_hash_table_insert(f_map, DLS_INTERFACE_PROP_BITS_PER_SAMPLE, prop_t);
229         g_hash_table_insert(p_map, "res@bitsPerSample",
230                             DLS_INTERFACE_PROP_BITS_PER_SAMPLE);
231
232         /* res@colorDepth */
233         prop_t = prv_prop_map_new("res@colorDepth",
234                                         DLS_UPNP_MASK_PROP_COLOR_DEPTH,
235                                         TRUE, TRUE, FALSE);
236         g_hash_table_insert(f_map, DLS_INTERFACE_PROP_COLOR_DEPTH, prop_t);
237         g_hash_table_insert(p_map, "res@colorDepth",
238                             DLS_INTERFACE_PROP_COLOR_DEPTH);
239
240         /* res@duration */
241         prop_t = prv_prop_map_new("res@duration",
242                                         DLS_UPNP_MASK_PROP_DURATION,
243                                         TRUE, TRUE, FALSE);
244         g_hash_table_insert(f_map, DLS_INTERFACE_PROP_DURATION, prop_t);
245         g_hash_table_insert(p_map, "res@duration",
246                             DLS_INTERFACE_PROP_DURATION);
247
248         /* res@protocolInfo */
249         /* res@protocolInfo - DLNA PROFILE*/
250         prop_t = prv_prop_map_new("res@protocolInfo",
251                                        DLS_UPNP_MASK_PROP_DLNA_PROFILE,
252                                        TRUE, FALSE, FALSE);
253         g_hash_table_insert(f_map, DLS_INTERFACE_PROP_DLNA_PROFILE, prop_t);
254
255         /* res@protocolInfo - MIME TYPES*/
256         prop_t = prv_prop_map_new("res@protocolInfo",
257                                         DLS_UPNP_MASK_PROP_MIME_TYPE,
258                                         TRUE, FALSE, FALSE);
259         g_hash_table_insert(f_map, DLS_INTERFACE_PROP_MIME_TYPE, prop_t);
260
261         /* res@resolution */
262         /* res@resolution - HEIGH */
263         prop_t = prv_prop_map_new("res@resolution",
264                                         DLS_UPNP_MASK_PROP_HEIGHT,
265                                         TRUE, FALSE, FALSE);
266         g_hash_table_insert(f_map, DLS_INTERFACE_PROP_HEIGHT, prop_t);
267
268         /* res@resolution - WIDTH */
269         prop_t = prv_prop_map_new("res@resolution",
270                                         DLS_UPNP_MASK_PROP_WIDTH,
271                                         TRUE, FALSE, FALSE);
272         g_hash_table_insert(f_map, DLS_INTERFACE_PROP_WIDTH, prop_t);
273
274         /* res@sampleFrequency */
275         prop_t = prv_prop_map_new("res@sampleFrequency",
276                                         DLS_UPNP_MASK_PROP_SAMPLE_RATE,
277                                         TRUE, TRUE, FALSE);
278         g_hash_table_insert(f_map, DLS_INTERFACE_PROP_SAMPLE_RATE, prop_t);
279         g_hash_table_insert(p_map, "res@sampleFrequency",
280                             DLS_INTERFACE_PROP_SAMPLE_RATE);
281
282         /* res@size */
283         prop_t = prv_prop_map_new("res@size",
284                                         DLS_UPNP_MASK_PROP_SIZE,
285                                        TRUE, TRUE, FALSE);
286         g_hash_table_insert(f_map, DLS_INTERFACE_PROP_SIZE, prop_t);
287         g_hash_table_insert(p_map, "res@size", DLS_INTERFACE_PROP_SIZE);
288
289         /* res@updateCount */
290         prop_t = prv_prop_map_new("res@updateCount",
291                                       DLS_UPNP_MASK_PROP_UPDATE_COUNT,
292                                       TRUE, TRUE, FALSE);
293         g_hash_table_insert(f_map, DLS_INTERFACE_PROP_UPDATE_COUNT, prop_t);
294         g_hash_table_insert(p_map, "res@updateCount",
295                             DLS_INTERFACE_PROP_UPDATE_COUNT);
296
297         /* upnp:album */
298         prop_t = prv_prop_map_new("upnp:album",
299                                         DLS_UPNP_MASK_PROP_ALBUM,
300                                        TRUE, TRUE, TRUE);
301         g_hash_table_insert(f_map, DLS_INTERFACE_PROP_ALBUM, prop_t);
302         g_hash_table_insert(p_map, "upnp:album", DLS_INTERFACE_PROP_ALBUM);
303
304         /* upnp:albumArtURI */
305         prop_t = prv_prop_map_new("upnp:albumArtURI",
306                                         DLS_UPNP_MASK_PROP_ALBUM_ART_URL,
307                                         TRUE, TRUE, FALSE);
308         g_hash_table_insert(f_map, DLS_INTERFACE_PROP_ALBUM_ART_URL, prop_t);
309         g_hash_table_insert(p_map, "upnp:albumArtURI",
310                             DLS_INTERFACE_PROP_ALBUM_ART_URL);
311
312         /* upnp:artist */
313         /* upnp:artist - ARTIST*/
314         prop_t = prv_prop_map_new("upnp:artist",
315                                         DLS_UPNP_MASK_PROP_ARTIST,
316                                         TRUE, TRUE, FALSE);
317         g_hash_table_insert(f_map, DLS_INTERFACE_PROP_ARTIST, prop_t);
318         g_hash_table_insert(p_map, "upnp:artist", DLS_INTERFACE_PROP_ARTIST);
319
320         /* upnp:artist - ARTISTS*/
321         prop_t = prv_prop_map_new("upnp:artist",
322                                         DLS_UPNP_MASK_PROP_ARTISTS,
323                                         TRUE, FALSE, TRUE);
324         g_hash_table_insert(f_map, DLS_INTERFACE_PROP_ARTISTS, prop_t);
325
326         /* upnp:class */
327         prop_t = prv_prop_map_new("upnp:class",
328                                         DLS_UPNP_MASK_PROP_TYPE,
329                                         FALSE, TRUE, TRUE);
330         g_hash_table_insert(f_map, DLS_INTERFACE_PROP_TYPE, prop_t);
331         g_hash_table_insert(p_map, "upnp:class", DLS_INTERFACE_PROP_TYPE);
332
333         /* upnp:containerUpdateID */
334         prop_t = prv_prop_map_new("upnp:containerUpdateID",
335                                       DLS_UPNP_MASK_PROP_CONTAINER_UPDATE_ID,
336                                       TRUE, TRUE, FALSE);
337         g_hash_table_insert(f_map, DLS_INTERFACE_PROP_CONTAINER_UPDATE_ID,
338                             prop_t);
339         g_hash_table_insert(p_map, "upnp:containerUpdateID",
340                             DLS_INTERFACE_PROP_CONTAINER_UPDATE_ID);
341
342         /* upnp:createClass */
343         prop_t = prv_prop_map_new("upnp:createClass",
344                                         DLS_UPNP_MASK_PROP_CREATE_CLASSES,
345                                         TRUE, FALSE, FALSE);
346         g_hash_table_insert(f_map, DLS_INTERFACE_PROP_CREATE_CLASSES, prop_t);
347
348         /* upnp:genre */
349         prop_t = prv_prop_map_new("upnp:genre",
350                                         DLS_UPNP_MASK_PROP_GENRE,
351                                         TRUE, TRUE, FALSE);
352         g_hash_table_insert(f_map, DLS_INTERFACE_PROP_GENRE, prop_t);
353         g_hash_table_insert(p_map, "upnp:genre", DLS_INTERFACE_PROP_GENRE);
354
355         /* upnp:objectUpdateID */
356         prop_t = prv_prop_map_new("upnp:objectUpdateID",
357                                       DLS_UPNP_MASK_PROP_OBJECT_UPDATE_ID,
358                                       TRUE, TRUE, FALSE);
359         g_hash_table_insert(f_map, DLS_INTERFACE_PROP_OBJECT_UPDATE_ID, prop_t);
360         g_hash_table_insert(p_map, "upnp:objectUpdateID",
361                             DLS_INTERFACE_PROP_OBJECT_UPDATE_ID);
362
363         /* upnp:originalTrackNumber */
364         prop_t = prv_prop_map_new("upnp:originalTrackNumber",
365                                         DLS_UPNP_MASK_PROP_TRACK_NUMBER,
366                                         TRUE, TRUE, TRUE);
367         g_hash_table_insert(f_map, DLS_INTERFACE_PROP_TRACK_NUMBER, prop_t);
368         g_hash_table_insert(p_map, "upnp:originalTrackNumber",
369                             DLS_INTERFACE_PROP_TRACK_NUMBER);
370
371         /* upnp:totalDeletedChildCount */
372         prop_t = prv_prop_map_new("upnp:totalDeletedChildCount",
373                                 DLS_UPNP_MASK_PROP_TOTAL_DELETED_CHILD_COUNT,
374                                 TRUE, TRUE, FALSE);
375         g_hash_table_insert(f_map, DLS_INTERFACE_PROP_TOTAL_DELETED_CHILD_COUNT,
376                             prop_t);
377         g_hash_table_insert(p_map, "upnp:totalDeletedChildCount",
378                             DLS_INTERFACE_PROP_TOTAL_DELETED_CHILD_COUNT);
379
380         *filter_map = f_map;
381         *property_map = p_map;
382 }
383
384 static gchar *prv_compute_upnp_filter(GHashTable *upnp_props)
385 {
386         gpointer key;
387         GString *str;
388         GHashTableIter iter;
389
390         str = g_string_new("");
391         g_hash_table_iter_init(&iter, upnp_props);
392         if (g_hash_table_iter_next(&iter, &key, NULL)) {
393                 g_string_append(str, (const gchar *)key);
394                 while (g_hash_table_iter_next(&iter, &key, NULL)) {
395                         g_string_append(str, ",");
396                         g_string_append(str, (const gchar *)key);
397                 }
398         }
399
400         return g_string_free(str, FALSE);
401 }
402
403 static dls_upnp_prop_mask prv_parse_filter_list(GHashTable *filter_map,
404                                                 GVariant *filter,
405                                                 gchar **upnp_filter)
406 {
407         GVariantIter viter;
408         const gchar *prop;
409         dls_prop_map_t *prop_map;
410         GHashTable *upnp_props;
411         dls_upnp_prop_mask mask = 0;
412
413         upnp_props = g_hash_table_new_full(g_str_hash, g_str_equal,
414                                            NULL, NULL);
415         (void) g_variant_iter_init(&viter, filter);
416
417         while (g_variant_iter_next(&viter, "&s", &prop)) {
418                 prop_map = g_hash_table_lookup(filter_map, prop);
419                 if (!prop_map)
420                         continue;
421
422                 mask |= prop_map->type;
423
424                 if (!prop_map->filter)
425                         continue;
426
427                 g_hash_table_insert(upnp_props,
428                                     (gpointer) prop_map->upnp_prop_name, NULL);
429         }
430
431         *upnp_filter = prv_compute_upnp_filter(upnp_props);
432         g_hash_table_unref(upnp_props);
433
434         return mask;
435 }
436
437 dls_upnp_prop_mask dls_props_parse_filter(GHashTable *filter_map,
438                                           GVariant *filter,
439                                           gchar **upnp_filter)
440 {
441         gchar *str;
442         gboolean parse_filter = TRUE;
443         dls_upnp_prop_mask mask;
444
445         if (g_variant_n_children(filter) == 1) {
446                 g_variant_get_child(filter, 0, "&s", &str);
447                 if (!strcmp(str, "*"))
448                         parse_filter = FALSE;
449         }
450
451         if (parse_filter) {
452                 mask = prv_parse_filter_list(filter_map, filter, upnp_filter);
453         } else {
454                 mask = DLS_UPNP_MASK_ALL_PROPS;
455                 *upnp_filter = g_strdup("*");
456         }
457
458         return mask;
459 }
460
461 gboolean dls_props_parse_update_filter(GHashTable *filter_map,
462                                        GVariant *to_add_update,
463                                        GVariant *to_delete,
464                                        dls_upnp_prop_mask *mask,
465                                        gchar **upnp_filter)
466 {
467         GVariantIter viter;
468         const gchar *prop;
469         GVariant *value;
470         dls_prop_map_t *prop_map;
471         GHashTable *upnp_props;
472         gboolean retval = FALSE;
473
474         *mask = 0;
475
476         upnp_props = g_hash_table_new_full(g_str_hash, g_str_equal,
477                                            NULL, NULL);
478
479         (void) g_variant_iter_init(&viter, to_add_update);
480
481         while (g_variant_iter_next(&viter, "{&sv}", &prop, &value)) {
482                 DLEYNA_LOG_DEBUG("to_add_update = %s", prop);
483
484                 prop_map = g_hash_table_lookup(filter_map, prop);
485
486                 if ((!prop_map) || (!prop_map->updateable))
487                         goto on_error;
488
489                 *mask |= prop_map->type;
490
491                 if (!prop_map->filter)
492                         continue;
493
494                 g_hash_table_insert(upnp_props,
495                                     (gpointer) prop_map->upnp_prop_name, NULL);
496         }
497
498         (void) g_variant_iter_init(&viter, to_delete);
499
500         while (g_variant_iter_next(&viter, "&s", &prop)) {
501                 DLEYNA_LOG_DEBUG("to_delete = %s", prop);
502
503                 prop_map = g_hash_table_lookup(filter_map, prop);
504
505                 if ((!prop_map) || (!prop_map->updateable) ||
506                     (*mask & prop_map->type) != 0)
507                         goto on_error;
508
509                 *mask |= prop_map->type;
510
511                 if (!prop_map->filter)
512                         continue;
513
514                 g_hash_table_insert(upnp_props,
515                                     (gpointer) prop_map->upnp_prop_name, NULL);
516         }
517
518         *upnp_filter = prv_compute_upnp_filter(upnp_props);
519
520         retval = TRUE;
521
522 on_error:
523
524         g_hash_table_unref(upnp_props);
525
526         return retval;
527 }
528
529 static void prv_add_string_prop(GVariantBuilder *vb, const gchar *key,
530                                 const gchar *value)
531 {
532         if (value) {
533                 DLEYNA_LOG_DEBUG("Prop %s = %s", key, value);
534
535                 g_variant_builder_add(vb, "{sv}", key,
536                                       g_variant_new_string(value));
537         }
538 }
539
540 static void prv_add_strv_prop(GVariantBuilder *vb, const gchar *key,
541                               const gchar **value, unsigned int len)
542 {
543         if (len > 0)
544                 g_variant_builder_add(vb, "{sv}", key,
545                                       g_variant_new_strv(value, len));
546 }
547
548 static void prv_add_path_prop(GVariantBuilder *vb, const gchar *key,
549                               const gchar *value)
550 {
551         if (value) {
552                 DLEYNA_LOG_DEBUG("Prop %s = %s", key, value);
553
554                 g_variant_builder_add(vb, "{sv}", key,
555                                       g_variant_new_object_path(value));
556         }
557 }
558
559 static void prv_add_uint_prop(GVariantBuilder *vb, const gchar *key,
560                               unsigned int value)
561 {
562         DLEYNA_LOG_DEBUG("Prop %s = %u", key, value);
563
564         g_variant_builder_add(vb, "{sv}", key, g_variant_new_uint32(value));
565 }
566
567 static void prv_add_int_prop(GVariantBuilder *vb, const gchar *key,
568                              int value)
569 {
570         if (value != -1)
571                 g_variant_builder_add(vb, "{sv}", key,
572                                       g_variant_new_int32(value));
573 }
574
575 static void prv_add_variant_prop(GVariantBuilder *vb, const gchar *key,
576                                  GVariant *prop)
577 {
578         if (prop)
579                 g_variant_builder_add(vb, "{sv}", key, prop);
580 }
581
582 void dls_props_add_child_count(GVariantBuilder *item_vb, gint value)
583 {
584         prv_add_int_prop(item_vb, DLS_INTERFACE_PROP_CHILD_COUNT, value);
585 }
586
587 static void prv_add_bool_prop(GVariantBuilder *vb, const gchar *key,
588                               gboolean value)
589 {
590         DLEYNA_LOG_DEBUG("Prop %s = %u", key, value);
591
592         g_variant_builder_add(vb, "{sv}", key, g_variant_new_boolean(value));
593 }
594
595 static void prv_add_int64_prop(GVariantBuilder *vb, const gchar *key,
596                                gint64 value)
597 {
598         if (value != -1) {
599                 DLEYNA_LOG_DEBUG("Prop %s = %"G_GINT64_FORMAT, key, value);
600
601                 g_variant_builder_add(vb, "{sv}", key,
602                                       g_variant_new_int64(value));
603         }
604 }
605
606 static void prv_add_list_dlna_str(gpointer data, gpointer user_data)
607 {
608         GVariantBuilder *vb = (GVariantBuilder *)user_data;
609         gchar *cap_str = (gchar *)data;
610         gchar *str;
611         int value = 0;
612
613         if (g_str_has_prefix(cap_str, "srs-rt-retention-period-")) {
614                 str = cap_str + strlen("srs-rt-retention-period-");
615                 cap_str = "srs-rt-retention-period";
616
617                 if (*str) {
618                         if (!g_strcmp0(str, "infinity"))
619                                 value = -1;
620                         else
621                                 value = atoi(str);
622                 }
623         }
624
625         prv_add_uint_prop(vb, cap_str, value);
626 }
627
628 static GVariant *prv_add_list_dlna_prop(GList *list)
629 {
630         GVariantBuilder vb;
631
632         g_variant_builder_init(&vb, G_VARIANT_TYPE("a{sv}"));
633
634         g_list_foreach(list, prv_add_list_dlna_str, &vb);
635
636         return g_variant_builder_end(&vb);
637 }
638
639 static void prv_add_list_artists_str(gpointer data, gpointer user_data)
640 {
641         GVariantBuilder *vb = (GVariantBuilder *)user_data;
642         GUPnPDIDLLiteContributor *contributor = data;
643         const char *str;
644
645         str = gupnp_didl_lite_contributor_get_name(contributor);
646         g_variant_builder_add(vb, "s", str);
647 }
648
649 static GVariant *prv_get_artists_prop(GList *list)
650 {
651         GVariantBuilder vb;
652
653         g_variant_builder_init(&vb, G_VARIANT_TYPE("as"));
654         g_list_foreach(list, prv_add_list_artists_str, &vb);
655
656         return g_variant_builder_end(&vb);
657 }
658
659 void dls_props_add_device(GUPnPDeviceInfo *proxy,
660                           const dls_device_t *device,
661                           GVariantBuilder *vb)
662 {
663         gchar *str;
664         GList *list;
665         GVariant *dlna_caps;
666
667         prv_add_string_prop(vb, DLS_INTERFACE_PROP_LOCATION,
668                             gupnp_device_info_get_location(proxy));
669
670         prv_add_string_prop(vb, DLS_INTERFACE_PROP_UDN,
671                             gupnp_device_info_get_udn(proxy));
672
673         prv_add_string_prop(vb, DLS_INTERFACE_PROP_DEVICE_TYPE,
674                             gupnp_device_info_get_device_type(proxy));
675
676         str = gupnp_device_info_get_friendly_name(proxy);
677         prv_add_string_prop(vb, DLS_INTERFACE_PROP_FRIENDLY_NAME, str);
678         g_free(str);
679
680         str = gupnp_device_info_get_manufacturer(proxy);
681         prv_add_string_prop(vb, DLS_INTERFACE_PROP_MANUFACTURER, str);
682         g_free(str);
683
684         str = gupnp_device_info_get_manufacturer_url(proxy);
685         prv_add_string_prop(vb, DLS_INTERFACE_PROP_MANUFACTURER_URL, str);
686         g_free(str);
687
688         str = gupnp_device_info_get_model_description(proxy);
689         prv_add_string_prop(vb, DLS_INTERFACE_PROP_MODEL_DESCRIPTION, str);
690         g_free(str);
691
692         str = gupnp_device_info_get_model_name(proxy);
693         prv_add_string_prop(vb, DLS_INTERFACE_PROP_MODEL_NAME, str);
694         g_free(str);
695
696         str = gupnp_device_info_get_model_number(proxy);
697         prv_add_string_prop(vb, DLS_INTERFACE_PROP_MODEL_NUMBER, str);
698         g_free(str);
699
700         str = gupnp_device_info_get_model_url(proxy);
701         prv_add_string_prop(vb, DLS_INTERFACE_PROP_MODEL_URL, str);
702         g_free(str);
703
704         str = gupnp_device_info_get_serial_number(proxy);
705         prv_add_string_prop(vb, DLS_INTERFACE_PROP_SERIAL_NUMBER, str);
706         g_free(str);
707
708         str = gupnp_device_info_get_presentation_url(proxy);
709         prv_add_string_prop(vb, DLS_INTERFACE_PROP_PRESENTATION_URL, str);
710         g_free(str);
711
712         str = gupnp_device_info_get_icon_url(proxy, NULL, -1, -1, -1, FALSE,
713                                              NULL, NULL, NULL, NULL);
714         prv_add_string_prop(vb, DLS_INTERFACE_PROP_ICON_URL, str);
715         g_free(str);
716
717         list = gupnp_device_info_list_dlna_capabilities(proxy);
718         if (list != NULL) {
719                 dlna_caps = prv_add_list_dlna_prop(list);
720                 g_variant_builder_add(vb, "{sv}",
721                                       DLS_INTERFACE_PROP_SV_DLNA_CAPABILITIES,
722                                       dlna_caps);
723                 g_list_free_full(list, g_free);
724         }
725
726         if (device->search_caps != NULL)
727                 g_variant_builder_add(vb, "{sv}",
728                                       DLS_INTERFACE_PROP_SV_SEARCH_CAPABILITIES,
729                                       device->search_caps);
730
731         if (device->sort_caps != NULL)
732                 g_variant_builder_add(vb, "{sv}",
733                                       DLS_INTERFACE_PROP_SV_SORT_CAPABILITIES,
734                                       device->sort_caps);
735
736         if (device->sort_ext_caps != NULL)
737                 g_variant_builder_add(
738                                 vb, "{sv}",
739                                 DLS_INTERFACE_PROP_SV_SORT_EXT_CAPABILITIES,
740                                 device->sort_ext_caps);
741
742         if (device->feature_list != NULL)
743                 g_variant_builder_add(vb, "{sv}",
744                                       DLS_INTERFACE_PROP_SV_FEATURE_LIST,
745                                       device->feature_list);
746 }
747
748 GVariant *dls_props_get_device_prop(GUPnPDeviceInfo *proxy,
749                                     const dls_device_t *device,
750                                     const gchar *prop)
751 {
752         GVariant *dlna_caps = NULL;
753         GVariant *retval = NULL;
754         const gchar *str = NULL;
755         gchar *copy = NULL;
756         GList *list;
757
758         if (!strcmp(DLS_INTERFACE_PROP_LOCATION, prop)) {
759                 str = gupnp_device_info_get_location(proxy);
760         } else if (!strcmp(DLS_INTERFACE_PROP_UDN, prop)) {
761                 str = gupnp_device_info_get_udn(proxy);
762         } else if (!strcmp(DLS_INTERFACE_PROP_DEVICE_TYPE, prop)) {
763                 str = gupnp_device_info_get_device_type(proxy);
764         } else if (!strcmp(DLS_INTERFACE_PROP_FRIENDLY_NAME, prop)) {
765                 copy = gupnp_device_info_get_friendly_name(proxy);
766                 str = copy;
767         } else if (!strcmp(DLS_INTERFACE_PROP_MANUFACTURER, prop)) {
768                 copy = gupnp_device_info_get_manufacturer(proxy);
769                 str = copy;
770         } else if (!strcmp(DLS_INTERFACE_PROP_MANUFACTURER_URL, prop)) {
771                 copy = gupnp_device_info_get_manufacturer_url(proxy);
772                 str = copy;
773         } else if (!strcmp(DLS_INTERFACE_PROP_MODEL_DESCRIPTION, prop)) {
774                 copy = gupnp_device_info_get_model_description(proxy);
775                 str = copy;
776         } else if (!strcmp(DLS_INTERFACE_PROP_MODEL_NAME, prop)) {
777                 copy = gupnp_device_info_get_model_name(proxy);
778                 str = copy;
779         } else if (!strcmp(DLS_INTERFACE_PROP_MODEL_NUMBER, prop)) {
780                 copy = gupnp_device_info_get_model_number(proxy);
781                 str = copy;
782         } else if (!strcmp(DLS_INTERFACE_PROP_MODEL_URL, prop)) {
783                 copy = gupnp_device_info_get_model_url(proxy);
784                 str = copy;
785         } else if (!strcmp(DLS_INTERFACE_PROP_SERIAL_NUMBER, prop)) {
786                 copy = gupnp_device_info_get_serial_number(proxy);
787                 str = copy;
788         } else if (!strcmp(DLS_INTERFACE_PROP_PRESENTATION_URL, prop)) {
789                 copy = gupnp_device_info_get_presentation_url(proxy);
790                 str = copy;
791         } else if (!strcmp(DLS_INTERFACE_PROP_ICON_URL, prop)) {
792                 copy = gupnp_device_info_get_icon_url(proxy, NULL,
793                                                       -1, -1, -1, FALSE,
794                                                       NULL, NULL, NULL, NULL);
795                 str = copy;
796         } else if (!strcmp(DLS_INTERFACE_PROP_SV_DLNA_CAPABILITIES, prop)) {
797                 list = gupnp_device_info_list_dlna_capabilities(proxy);
798                 if (list != NULL) {
799                         dlna_caps = prv_add_list_dlna_prop(list);
800                         g_list_free_full(list, g_free);
801                         retval = g_variant_ref_sink(dlna_caps);
802
803 #if DLEYNA_LOG_LEVEL & DLEYNA_LOG_LEVEL_DEBUG
804                         copy = g_variant_print(dlna_caps, FALSE);
805                         DLEYNA_LOG_DEBUG("Prop %s = %s", prop, copy);
806 #endif
807                 }
808         } else if (!strcmp(DLS_INTERFACE_PROP_SV_SEARCH_CAPABILITIES, prop)) {
809                 if (device->search_caps != NULL) {
810                         retval = g_variant_ref(device->search_caps);
811
812 #if DLEYNA_LOG_LEVEL & DLEYNA_LOG_LEVEL_DEBUG
813                         copy = g_variant_print(device->search_caps, FALSE);
814                         DLEYNA_LOG_DEBUG("Prop %s = %s", prop, copy);
815 #endif
816                 }
817         } else if (!strcmp(DLS_INTERFACE_PROP_SV_SORT_CAPABILITIES, prop)) {
818                 if (device->sort_caps != NULL) {
819                         retval = g_variant_ref(device->sort_caps);
820
821 #if DLEYNA_LOG_LEVEL & DLEYNA_LOG_LEVEL_DEBUG
822                         copy = g_variant_print(device->sort_caps, FALSE);
823                         DLEYNA_LOG_DEBUG("Prop %s = %s", prop, copy);
824 #endif
825                 }
826         } else if (!strcmp(DLS_INTERFACE_PROP_SV_SORT_EXT_CAPABILITIES, prop)) {
827                 if (device->sort_ext_caps != NULL) {
828                         retval = g_variant_ref(device->sort_ext_caps);
829
830 #if DLEYNA_LOG_LEVEL & DLEYNA_LOG_LEVEL_DEBUG
831                         copy = g_variant_print(device->sort_ext_caps, FALSE);
832                         DLEYNA_LOG_DEBUG("Prop %s = %s", prop, copy);
833 #endif
834                 }
835         } else if (!strcmp(DLS_INTERFACE_PROP_SV_FEATURE_LIST, prop)) {
836                 if (device->feature_list != NULL) {
837                         retval = g_variant_ref(device->feature_list);
838
839 #if DLEYNA_LOG_LEVEL & DLEYNA_LOG_LEVEL_DEBUG
840                         copy = g_variant_print(device->feature_list, FALSE);
841                         DLEYNA_LOG_DEBUG("Prop %s = %s", prop, copy);
842 #endif
843                 }
844         }
845
846         if (!retval) {
847                 if (str) {
848                         DLEYNA_LOG_DEBUG("Prop %s = %s", prop, str);
849
850                         retval = g_variant_ref_sink(g_variant_new_string(str));
851                 }
852 #if DLEYNA_LOG_LEVEL & DLEYNA_LOG_LEVEL_WARNING
853                 else
854                         DLEYNA_LOG_WARNING("Property %s not defined", prop);
855 #endif
856         }
857
858         g_free(copy);
859
860         return retval;
861 }
862
863 static GUPnPDIDLLiteResource *prv_match_resource(GUPnPDIDLLiteResource *res,
864                                                  gchar **pi_str_array)
865 {
866         GUPnPDIDLLiteResource *retval = NULL;
867         GUPnPProtocolInfo *res_pi;
868         GUPnPProtocolInfo *pi;
869         unsigned int i;
870         gboolean match;
871
872         if (!pi_str_array) {
873                 retval = res;
874                 goto done;
875         }
876
877         res_pi = gupnp_didl_lite_resource_get_protocol_info(res);
878         if (!res_pi)
879                 goto done;
880
881         for (i = 0; pi_str_array[i]; ++i) {
882                 pi = gupnp_protocol_info_new_from_string(pi_str_array[i],
883                         NULL);
884                 if (!pi)
885                         continue;
886                 match = gupnp_protocol_info_is_compatible(pi, res_pi);
887                 g_object_unref(pi);
888                 if (match) {
889                         retval = res;
890                         break;
891                 }
892         }
893 done:
894
895         return retval;
896 }
897
898 static GUPnPDIDLLiteResource *prv_get_matching_resource
899         (GUPnPDIDLLiteObject *object, const gchar *protocol_info)
900 {
901         GUPnPDIDLLiteResource *retval = NULL;
902         GUPnPDIDLLiteResource *res;
903         GList *resources;
904         GList *ptr;
905         gchar **pi_str_array = NULL;
906
907         if (protocol_info)
908                 pi_str_array = g_strsplit(protocol_info, ",", 0);
909
910         resources = gupnp_didl_lite_object_get_resources(object);
911         ptr = resources;
912
913         while (ptr) {
914                 res = ptr->data;
915                 if (!retval) {
916                         retval = prv_match_resource(res, pi_str_array);
917                         if (!retval)
918                                 g_object_unref(res);
919                 } else {
920                         g_object_unref(res);
921                 }
922
923                 ptr = ptr->next;
924         }
925
926         g_list_free(resources);
927         if (pi_str_array)
928                 g_strfreev(pi_str_array);
929
930         return retval;
931 }
932
933 static void prv_parse_resources(GVariantBuilder *item_vb,
934                                 GUPnPDIDLLiteResource *res,
935                                 dls_upnp_prop_mask filter_mask)
936 {
937         GUPnPProtocolInfo *protocol_info;
938         int int_val;
939         gint64 int64_val;
940         const char *str_val;
941         guint uint_val;
942
943         if (filter_mask & DLS_UPNP_MASK_PROP_SIZE) {
944                 int64_val = gupnp_didl_lite_resource_get_size64(res);
945                 prv_add_int64_prop(item_vb, DLS_INTERFACE_PROP_SIZE, int64_val);
946         }
947
948         if (filter_mask & DLS_UPNP_MASK_PROP_BITRATE) {
949                 int_val = gupnp_didl_lite_resource_get_bitrate(res);
950                 prv_add_int_prop(item_vb, DLS_INTERFACE_PROP_BITRATE, int_val);
951         }
952
953         if (filter_mask & DLS_UPNP_MASK_PROP_SAMPLE_RATE) {
954                 int_val = gupnp_didl_lite_resource_get_sample_freq(res);
955                 prv_add_int_prop(item_vb, DLS_INTERFACE_PROP_SAMPLE_RATE,
956                                  int_val);
957         }
958
959         if (filter_mask & DLS_UPNP_MASK_PROP_BITS_PER_SAMPLE) {
960                 int_val = gupnp_didl_lite_resource_get_bits_per_sample(res);
961                 prv_add_int_prop(item_vb, DLS_INTERFACE_PROP_BITS_PER_SAMPLE,
962                                  int_val);
963         }
964
965         if (filter_mask & DLS_UPNP_MASK_PROP_DURATION) {
966                 int_val = (int) gupnp_didl_lite_resource_get_duration(res);
967                 prv_add_int_prop(item_vb, DLS_INTERFACE_PROP_DURATION, int_val);
968         }
969
970         if (filter_mask & DLS_UPNP_MASK_PROP_WIDTH) {
971                 int_val = (int) gupnp_didl_lite_resource_get_width(res);
972                 prv_add_int_prop(item_vb, DLS_INTERFACE_PROP_WIDTH, int_val);
973         }
974
975         if (filter_mask & DLS_UPNP_MASK_PROP_HEIGHT) {
976                 int_val = (int) gupnp_didl_lite_resource_get_height(res);
977                 prv_add_int_prop(item_vb, DLS_INTERFACE_PROP_HEIGHT, int_val);
978         }
979
980         if (filter_mask & DLS_UPNP_MASK_PROP_COLOR_DEPTH) {
981                 int_val = (int) gupnp_didl_lite_resource_get_color_depth(res);
982                 prv_add_int_prop(item_vb, DLS_INTERFACE_PROP_COLOR_DEPTH,
983                                  int_val);
984         }
985
986         if (filter_mask & DLS_UPNP_MASK_PROP_UPDATE_COUNT) {
987                 uint_val = gupnp_didl_lite_resource_get_update_count(res);
988                 prv_add_uint_prop(item_vb, DLS_INTERFACE_PROP_UPDATE_COUNT,
989                                   uint_val);
990         }
991
992         protocol_info = gupnp_didl_lite_resource_get_protocol_info(res);
993
994         if (filter_mask & DLS_UPNP_MASK_PROP_DLNA_PROFILE) {
995                 str_val = gupnp_protocol_info_get_dlna_profile(protocol_info);
996                 prv_add_string_prop(item_vb, DLS_INTERFACE_PROP_DLNA_PROFILE,
997                                     str_val);
998         }
999
1000         if (filter_mask & DLS_UPNP_MASK_PROP_MIME_TYPE) {
1001                 str_val = gupnp_protocol_info_get_mime_type(protocol_info);
1002                 prv_add_string_prop(item_vb, DLS_INTERFACE_PROP_MIME_TYPE,
1003                                     str_val);
1004         }
1005 }
1006
1007 static GVariant *prv_compute_create_classes(GUPnPDIDLLiteContainer *container)
1008 {
1009         GVariantBuilder create_classes_vb;
1010         GList *create_classes;
1011         GList *ptr;
1012         GUPnPDIDLLiteCreateClass *create_class;
1013         const char *content;
1014         gboolean inc_derived;
1015
1016         g_variant_builder_init(&create_classes_vb, G_VARIANT_TYPE("a(sb)"));
1017
1018         create_classes = gupnp_didl_lite_container_get_create_classes_full(
1019                                                                  container);
1020         ptr = create_classes;
1021         while (ptr) {
1022                 create_class = ptr->data;
1023                 content = gupnp_didl_lite_create_class_get_content(
1024                                                                  create_class);
1025                 inc_derived = gupnp_didl_lite_create_class_get_include_derived(
1026                                                                  create_class);
1027                 g_variant_builder_add(&create_classes_vb,
1028                                       "(sb)", content, inc_derived);
1029                 g_object_unref(ptr->data);
1030                 ptr = g_list_next(ptr);
1031         }
1032         g_list_free(create_classes);
1033
1034         return g_variant_builder_end(&create_classes_vb);
1035 }
1036
1037 const gchar *dls_props_media_spec_to_upnp_class(const gchar *m2spec_class)
1038 {
1039         const gchar *retval = NULL;
1040
1041         if (!strcmp(m2spec_class, gMediaSpec2AlbumPhoto))
1042                 retval = gUPnPPhotoAlbum;
1043         else if (!strcmp(m2spec_class, gMediaSpec2AlbumMusic))
1044                 retval = gUPnPMusicAlbum;
1045         else if (!strcmp(m2spec_class, gMediaSpec2Album))
1046                 retval = gUPnPAlbum;
1047         else if (!strcmp(m2spec_class, gMediaSpec2PersonMusicArtist))
1048                 retval = gUPnPMusicArtist;
1049         else if (!strcmp(m2spec_class, gMediaSpec2Person))
1050                 retval = gUPnPPerson;
1051         else if (!strcmp(m2spec_class, gMediaSpec2GenreMovie))
1052                 retval = gUPnPMovieGenre;
1053         else if (!strcmp(m2spec_class, gMediaSpec2GenreMusic))
1054                 retval = gUPnPMusicGenre;
1055         else if (!strcmp(m2spec_class, gMediaSpec2Genre))
1056                 retval = gUPnPGenre;
1057         else if (!strcmp(m2spec_class, gMediaSpec2Container))
1058                 retval = gUPnPContainer;
1059         else if (!strcmp(m2spec_class, gMediaSpec2AudioMusic))
1060                 retval = gUPnPMusicTrack;
1061         else if (!strcmp(m2spec_class, gMediaSpec2AudioBroadcast))
1062                 retval = gUPnPAudioBroadcast;
1063         else if (!strcmp(m2spec_class, gMediaSpec2AudioBook))
1064                 retval = gUPnPAudioBook;
1065         else if (!strcmp(m2spec_class, gMediaSpec2Audio))
1066                 retval = gUPnPAudioItem;
1067         else if (!strcmp(m2spec_class, gMediaSpec2VideoMovie))
1068                 retval = gUPnPMovie;
1069         else if (!strcmp(m2spec_class, gMediaSpec2VideoMusicClip))
1070                 retval = gUPnPMusicVideoClip;
1071         else if (!strcmp(m2spec_class, gMediaSpec2VideoBroadcast))
1072                 retval = gUPnPVideoBroadcast;
1073         else if (!strcmp(m2spec_class, gMediaSpec2Video))
1074                 retval = gUPnPVideoItem;
1075         else if (!strcmp(m2spec_class, gMediaSpec2ImagePhoto))
1076                 retval = gUPnPPhoto;
1077         else if (!strcmp(m2spec_class, gMediaSpec2Image))
1078                 retval = gUPnPImageItem;
1079         else if (!strcmp(m2spec_class, gMediaSpec2Playlist))
1080                 retval = gUPnPPlaylistItem;
1081         else if (!strcmp(m2spec_class, gMediaSpec2Item))
1082                 retval = gUPnPItem;
1083
1084         return retval;
1085 }
1086
1087 const gchar *dls_props_upnp_class_to_media_spec(const gchar *upnp_class)
1088 {
1089         const gchar *retval = NULL;
1090         const gchar *ptr;
1091
1092         if (!strncmp(upnp_class, gUPnPAlbum, gUPnPAlbumLen)) {
1093                 ptr = upnp_class + gUPnPAlbumLen;
1094                 if (!strcmp(ptr, ".photoAlbum"))
1095                         retval = gMediaSpec2AlbumPhoto;
1096                 else if (!strcmp(ptr, ".musicAlbum"))
1097                         retval = gMediaSpec2AlbumMusic;
1098                 else
1099                         retval = gMediaSpec2Album;
1100         } else if (!strncmp(upnp_class, gUPnPPerson, gUPnPPersonLen)) {
1101                 ptr = upnp_class + gUPnPPersonLen;
1102                 if (!strcmp(ptr, ".musicArtist"))
1103                         retval = gMediaSpec2PersonMusicArtist;
1104                 else
1105                         retval = gMediaSpec2Person;
1106         } else if (!strncmp(upnp_class, gUPnPGenre, gUPnPGenreLen)) {
1107                 ptr = upnp_class + gUPnPGenreLen;
1108                 if (!strcmp(ptr, ".movieGenre"))
1109                         retval = gMediaSpec2GenreMovie;
1110                 else if (!strcmp(ptr, ".musicGenre"))
1111                         retval = gMediaSpec2GenreMusic;
1112                 else
1113                         retval = gMediaSpec2Genre;
1114         } else if (!strncmp(upnp_class, gUPnPContainer, gUPnPContainerLen)) {
1115                 ptr = upnp_class + gUPnPContainerLen;
1116                 if (!*ptr || *ptr == '.')
1117                         retval = gMediaSpec2Container;
1118         } else if (!strncmp(upnp_class, gUPnPAudioItem, gUPnPAudioItemLen)) {
1119                 ptr = upnp_class + gUPnPAudioItemLen;
1120                 if (!strcmp(ptr, ".musicTrack"))
1121                         retval = gMediaSpec2AudioMusic;
1122                 else if (!strcmp(ptr, ".audioBroadcast"))
1123                         retval = gMediaSpec2AudioBroadcast;
1124                 else if (!strcmp(ptr, ".audioBook"))
1125                         retval = gMediaSpec2AudioBook;
1126                 else
1127                         retval = gMediaSpec2Audio;
1128         } else if (!strncmp(upnp_class, gUPnPVideoItem, gUPnPVideoItemLen)) {
1129                 ptr = upnp_class + gUPnPVideoItemLen;
1130                 if (!strcmp(ptr, ".movie"))
1131                         retval = gMediaSpec2VideoMovie;
1132                 else if (!strcmp(ptr, ".musicVideoClip"))
1133                         retval = gMediaSpec2VideoMusicClip;
1134                 else if (!strcmp(ptr, ".videoBroadcast"))
1135                         retval = gMediaSpec2VideoBroadcast;
1136                 else
1137                         retval = gMediaSpec2Video;
1138         }  else if (!strncmp(upnp_class, gUPnPImageItem, gUPnPImageItemLen)) {
1139                 ptr = upnp_class + gUPnPImageItemLen;
1140                 if (!strcmp(ptr, ".photo"))
1141                         retval = gMediaSpec2ImagePhoto;
1142                 else
1143                         retval = gMediaSpec2Image;
1144         }  else if (!strncmp(upnp_class, gUPnPPlaylistItem,
1145                              gUPnPPlaylistItemLen)) {
1146                 retval = gMediaSpec2Playlist;
1147         } else if (!strncmp(upnp_class, gUPnPItem, gUPnPItemLen)) {
1148                 ptr = upnp_class + gUPnPItemLen;
1149                 if (!*ptr || *ptr == '.')
1150                         retval = gMediaSpec2Item;
1151         }
1152
1153         return retval;
1154 }
1155
1156 static GVariant *prv_props_get_dlna_managed_dict(GUPnPOCMFlags flags)
1157 {
1158         GVariantBuilder builder;
1159         gboolean managed;
1160
1161         g_variant_builder_init(&builder, G_VARIANT_TYPE("a{sb}"));
1162
1163         managed = (flags & GUPNP_OCM_FLAGS_UPLOAD);
1164         g_variant_builder_add(&builder, "{sb}", "Upload", managed);
1165
1166         managed = (flags & GUPNP_OCM_FLAGS_CREATE_CONTAINER);
1167         g_variant_builder_add(&builder, "{sb}", "CreateContainer", managed);
1168
1169         managed = (flags & GUPNP_OCM_FLAGS_DESTROYABLE);
1170         g_variant_builder_add(&builder, "{sb}", "Delete", managed);
1171
1172         managed = (flags & GUPNP_OCM_FLAGS_UPLOAD_DESTROYABLE);
1173         g_variant_builder_add(&builder, "{sb}", "UploadDelete", managed);
1174
1175         managed = (flags & GUPNP_OCM_FLAGS_CHANGE_METADATA);
1176         g_variant_builder_add(&builder, "{sb}", "ChangeMeta", managed);
1177
1178         return g_variant_builder_end(&builder);
1179 }
1180
1181 gboolean dls_props_add_object(GVariantBuilder *item_vb,
1182                               GUPnPDIDLLiteObject *object,
1183                               const char *root_path,
1184                               const gchar *parent_path,
1185                               dls_upnp_prop_mask filter_mask)
1186 {
1187         gchar *path = NULL;
1188         const char *id;
1189         const char *title;
1190         const char *creator;
1191         const char *upnp_class;
1192         const char *media_spec_type;
1193         gboolean retval = FALSE;
1194         gboolean rest;
1195         GUPnPOCMFlags flags;
1196         guint uint_val;
1197
1198         id = gupnp_didl_lite_object_get_id(object);
1199         if (!id)
1200                 goto on_error;
1201
1202         upnp_class = gupnp_didl_lite_object_get_upnp_class(object);
1203         media_spec_type = dls_props_upnp_class_to_media_spec(upnp_class);
1204
1205         if (!media_spec_type)
1206                 goto on_error;
1207
1208         title = gupnp_didl_lite_object_get_title(object);
1209         creator = gupnp_didl_lite_object_get_creator(object);
1210         rest = gupnp_didl_lite_object_get_restricted(object);
1211         path = dls_path_from_id(root_path, id);
1212
1213         if (filter_mask & DLS_UPNP_MASK_PROP_DISPLAY_NAME)
1214                 prv_add_string_prop(item_vb, DLS_INTERFACE_PROP_DISPLAY_NAME,
1215                                     title);
1216
1217         if (filter_mask & DLS_UPNP_MASK_PROP_CREATOR)
1218                 prv_add_string_prop(item_vb, DLS_INTERFACE_PROP_CREATOR,
1219                                     creator);
1220
1221         if (filter_mask & DLS_UPNP_MASK_PROP_PATH)
1222                 prv_add_path_prop(item_vb, DLS_INTERFACE_PROP_PATH, path);
1223
1224         if (filter_mask & DLS_UPNP_MASK_PROP_PARENT)
1225                 prv_add_path_prop(item_vb, DLS_INTERFACE_PROP_PARENT,
1226                                   parent_path);
1227
1228         if (filter_mask & DLS_UPNP_MASK_PROP_TYPE)
1229                 prv_add_string_prop(item_vb, DLS_INTERFACE_PROP_TYPE,
1230                                     media_spec_type);
1231
1232         if (filter_mask & DLS_UPNP_MASK_PROP_RESTRICTED)
1233                 prv_add_bool_prop(item_vb, DLS_INTERFACE_PROP_RESTRICTED, rest);
1234
1235         if (filter_mask & DLS_UPNP_MASK_PROP_DLNA_MANAGED) {
1236                 flags = gupnp_didl_lite_object_get_dlna_managed(object);
1237                 prv_add_variant_prop(item_vb,
1238                                      DLS_INTERFACE_PROP_DLNA_MANAGED,
1239                                      prv_props_get_dlna_managed_dict(flags));
1240         }
1241
1242         if (filter_mask & DLS_UPNP_MASK_PROP_OBJECT_UPDATE_ID) {
1243                 uint_val = gupnp_didl_lite_object_get_update_id(object);
1244                 prv_add_uint_prop(item_vb, DLS_INTERFACE_PROP_OBJECT_UPDATE_ID,
1245                                   uint_val);
1246         }
1247
1248         retval = TRUE;
1249
1250 on_error:
1251
1252         g_free(path);
1253
1254         return retval;
1255 }
1256
1257 void dls_props_add_container(GVariantBuilder *item_vb,
1258                              GUPnPDIDLLiteContainer *object,
1259                              dls_upnp_prop_mask filter_mask,
1260                              gboolean *have_child_count)
1261 {
1262         int child_count;
1263         gboolean searchable;
1264         guint uint_val;
1265
1266         *have_child_count = FALSE;
1267         if (filter_mask & DLS_UPNP_MASK_PROP_CHILD_COUNT) {
1268                 child_count = gupnp_didl_lite_container_get_child_count(object);
1269                 if (child_count >= 0) {
1270                         prv_add_uint_prop(item_vb,
1271                                           DLS_INTERFACE_PROP_CHILD_COUNT,
1272                                           (unsigned int) child_count);
1273                         *have_child_count = TRUE;
1274                 }
1275         }
1276
1277         if (filter_mask & DLS_UPNP_MASK_PROP_SEARCHABLE) {
1278                 searchable = gupnp_didl_lite_container_get_searchable(object);
1279                 prv_add_bool_prop(item_vb, DLS_INTERFACE_PROP_SEARCHABLE,
1280                                   searchable);
1281         }
1282
1283         if (filter_mask & DLS_UPNP_MASK_PROP_CREATE_CLASSES)
1284                 prv_add_variant_prop(item_vb,
1285                                      DLS_INTERFACE_PROP_CREATE_CLASSES,
1286                                      prv_compute_create_classes(object));
1287
1288         if (filter_mask & DLS_UPNP_MASK_PROP_CONTAINER_UPDATE_ID) {
1289                 uint_val = gupnp_didl_lite_container_get_container_update_id(
1290                                                                         object);
1291                 prv_add_uint_prop(item_vb,
1292                                   DLS_INTERFACE_PROP_CONTAINER_UPDATE_ID,
1293                                   uint_val);
1294         }
1295
1296         if (filter_mask & DLS_UPNP_MASK_PROP_TOTAL_DELETED_CHILD_COUNT) {
1297                 uint_val =
1298                 gupnp_didl_lite_container_get_total_deleted_child_count(object);
1299                 prv_add_uint_prop(item_vb,
1300                                   DLS_INTERFACE_PROP_TOTAL_DELETED_CHILD_COUNT,
1301                                   uint_val);
1302         }
1303 }
1304
1305 static GVariant *prv_compute_resources(GUPnPDIDLLiteObject *object,
1306                                        dls_upnp_prop_mask filter_mask)
1307 {
1308         GUPnPDIDLLiteResource *res = NULL;
1309         GList *resources;
1310         GList *ptr;
1311         GVariantBuilder *res_array_vb;
1312         GVariantBuilder *res_vb;
1313         const char *str_val;
1314         GVariant *retval;
1315
1316         res_array_vb = g_variant_builder_new(G_VARIANT_TYPE("aa{sv}"));
1317
1318         resources = gupnp_didl_lite_object_get_resources(object);
1319         ptr = resources;
1320
1321         while (ptr) {
1322                 res = ptr->data;
1323                 res_vb = g_variant_builder_new(G_VARIANT_TYPE("a{sv}"));
1324                 if (filter_mask & DLS_UPNP_MASK_PROP_URL) {
1325                         str_val = gupnp_didl_lite_resource_get_uri(res);
1326                         if (str_val)
1327                                 prv_add_string_prop(res_vb,
1328                                                     DLS_INTERFACE_PROP_URL,
1329                                                     str_val);
1330                 }
1331                 prv_parse_resources(res_vb, res, filter_mask);
1332                 g_variant_builder_add(res_array_vb, "@a{sv}",
1333                                       g_variant_builder_end(res_vb));
1334                 g_variant_builder_unref(res_vb);
1335                 g_object_unref(ptr->data);
1336                 ptr = g_list_next(ptr);
1337         }
1338         retval = g_variant_builder_end(res_array_vb);
1339         g_variant_builder_unref(res_array_vb);
1340
1341         g_list_free(resources);
1342
1343         return retval;
1344 }
1345
1346 static void prv_add_resources(GVariantBuilder *item_vb,
1347                               GUPnPDIDLLiteObject *object,
1348                               dls_upnp_prop_mask filter_mask)
1349 {
1350         GVariant *val;
1351
1352         val = prv_compute_resources(object, filter_mask);
1353         g_variant_builder_add(item_vb, "{sv}", DLS_INTERFACE_PROP_RESOURCES,
1354                               val);
1355 }
1356
1357 void dls_props_add_item(GVariantBuilder *item_vb,
1358                         GUPnPDIDLLiteObject *object,
1359                         const gchar *root_path,
1360                         dls_upnp_prop_mask filter_mask,
1361                         const gchar *protocol_info)
1362 {
1363         int track_number;
1364         GUPnPDIDLLiteResource *res;
1365         const char *str_val;
1366         char *path;
1367         GList *list;
1368
1369         if (filter_mask & DLS_UPNP_MASK_PROP_ARTIST)
1370                 prv_add_string_prop(item_vb, DLS_INTERFACE_PROP_ARTIST,
1371                                     gupnp_didl_lite_object_get_artist(object));
1372
1373         if (filter_mask & DLS_UPNP_MASK_PROP_ARTISTS) {
1374                 list = gupnp_didl_lite_object_get_artists(object);
1375                 prv_add_variant_prop(item_vb, DLS_INTERFACE_PROP_ARTISTS,
1376                                      prv_get_artists_prop(list));
1377                 g_list_free_full(list, g_object_unref);
1378         }
1379
1380         if (filter_mask & DLS_UPNP_MASK_PROP_ALBUM)
1381                 prv_add_string_prop(item_vb, DLS_INTERFACE_PROP_ALBUM,
1382                                     gupnp_didl_lite_object_get_album(object));
1383
1384         if (filter_mask & DLS_UPNP_MASK_PROP_DATE)
1385                 prv_add_string_prop(item_vb, DLS_INTERFACE_PROP_DATE,
1386                                     gupnp_didl_lite_object_get_date(object));
1387
1388         if (filter_mask & DLS_UPNP_MASK_PROP_GENRE)
1389                 prv_add_string_prop(item_vb, DLS_INTERFACE_PROP_GENRE,
1390                                     gupnp_didl_lite_object_get_genre(object));
1391
1392         if (filter_mask & DLS_UPNP_MASK_PROP_TRACK_NUMBER) {
1393                 track_number = gupnp_didl_lite_object_get_track_number(object);
1394                 if (track_number >= 0)
1395                         prv_add_int_prop(item_vb,
1396                                          DLS_INTERFACE_PROP_TRACK_NUMBER,
1397                                          track_number);
1398         }
1399
1400         if (filter_mask & DLS_UPNP_MASK_PROP_ALBUM_ART_URL)
1401                 prv_add_string_prop(item_vb, DLS_INTERFACE_PROP_ALBUM_ART_URL,
1402                                     gupnp_didl_lite_object_get_album_art(
1403                                             object));
1404
1405         if (filter_mask & DLS_UPNP_MASK_PROP_REFPATH) {
1406                 str_val = gupnp_didl_lite_item_get_ref_id(
1407                                                 GUPNP_DIDL_LITE_ITEM(object));
1408                 if (str_val != NULL) {
1409                         path = dls_path_from_id(root_path, str_val);
1410                         prv_add_path_prop(item_vb, DLS_INTERFACE_PROP_REFPATH,
1411                                           path);
1412                         g_free(path);
1413                 }
1414         }
1415
1416         res = prv_get_matching_resource(object, protocol_info);
1417         if (res) {
1418                 if (filter_mask & DLS_UPNP_MASK_PROP_URLS) {
1419                         str_val = gupnp_didl_lite_resource_get_uri(res);
1420                         if (str_val)
1421                                 prv_add_strv_prop(item_vb,
1422                                                   DLS_INTERFACE_PROP_URLS,
1423                                                   &str_val, 1);
1424                 }
1425                 prv_parse_resources(item_vb, res, filter_mask);
1426                 g_object_unref(res);
1427         }
1428
1429         if (filter_mask & DLS_UPNP_MASK_PROP_RESOURCES)
1430                 prv_add_resources(item_vb, object, filter_mask);
1431 }
1432
1433 void dls_props_add_resource(GVariantBuilder *item_vb,
1434                             GUPnPDIDLLiteObject *object,
1435                             dls_upnp_prop_mask filter_mask,
1436                             const gchar *protocol_info)
1437 {
1438         GUPnPDIDLLiteResource *res;
1439         const char *str_val;
1440
1441         res = prv_get_matching_resource(object, protocol_info);
1442         if (res) {
1443                 if (filter_mask & DLS_UPNP_MASK_PROP_URL) {
1444                         str_val = gupnp_didl_lite_resource_get_uri(res);
1445                         if (str_val)
1446                                 prv_add_string_prop(item_vb,
1447                                                     DLS_INTERFACE_PROP_URL,
1448                                                     str_val);
1449                 }
1450                 prv_parse_resources(item_vb, res, filter_mask);
1451                 g_object_unref(res);
1452         }
1453 }
1454
1455
1456 static GVariant *prv_get_resource_property(const gchar *prop,
1457                                            GUPnPDIDLLiteResource *res)
1458 {
1459         int int_val;
1460         gint64 int64_val;
1461         const char *str_val;
1462         GVariant *retval = NULL;
1463         GUPnPProtocolInfo *protocol_info;
1464
1465         if (!strcmp(prop, DLS_INTERFACE_PROP_DLNA_PROFILE)) {
1466                 protocol_info = gupnp_didl_lite_resource_get_protocol_info(res);
1467                 if (!protocol_info)
1468                         goto on_error;
1469                 str_val = gupnp_protocol_info_get_dlna_profile(protocol_info);
1470                 if (!str_val)
1471                         goto on_error;
1472                 retval = g_variant_ref_sink(g_variant_new_string(str_val));
1473         } else if (!strcmp(prop, DLS_INTERFACE_PROP_MIME_TYPE)) {
1474                 protocol_info = gupnp_didl_lite_resource_get_protocol_info(res);
1475                 if (!protocol_info)
1476                         goto on_error;
1477                 str_val = gupnp_protocol_info_get_mime_type(protocol_info);
1478                 if (!str_val)
1479                         goto on_error;
1480                 retval = g_variant_ref_sink(g_variant_new_string(str_val));
1481         } else if (!strcmp(prop, DLS_INTERFACE_PROP_SIZE)) {
1482                 int64_val = gupnp_didl_lite_resource_get_size64(res);
1483                 if (int64_val == -1)
1484                         goto on_error;
1485                 retval = g_variant_ref_sink(g_variant_new_int64(int64_val));
1486         } else if (!strcmp(prop, DLS_INTERFACE_PROP_DURATION)) {
1487                 int_val = (int) gupnp_didl_lite_resource_get_duration(res);
1488                 if (int_val == -1)
1489                         goto on_error;
1490                 retval = g_variant_ref_sink(g_variant_new_int32(int_val));
1491         } else if (!strcmp(prop, DLS_INTERFACE_PROP_BITRATE)) {
1492                 int_val = gupnp_didl_lite_resource_get_bitrate(res);
1493                 if (int_val == -1)
1494                         goto on_error;
1495                 retval = g_variant_ref_sink(g_variant_new_int32(int_val));
1496         } else if (!strcmp(prop, DLS_INTERFACE_PROP_SAMPLE_RATE)) {
1497                 int_val = gupnp_didl_lite_resource_get_sample_freq(res);
1498                 if (int_val == -1)
1499                         goto on_error;
1500                 retval = g_variant_ref_sink(g_variant_new_int32(int_val));
1501         } else if (!strcmp(prop, DLS_INTERFACE_PROP_BITS_PER_SAMPLE)) {
1502                 int_val = gupnp_didl_lite_resource_get_bits_per_sample(res);
1503                 if (int_val == -1)
1504                         goto on_error;
1505                 retval = g_variant_ref_sink(g_variant_new_int32(int_val));
1506         } else if (!strcmp(prop, DLS_INTERFACE_PROP_WIDTH)) {
1507                 int_val = (int) gupnp_didl_lite_resource_get_width(res);
1508                 if (int_val == -1)
1509                         goto on_error;
1510                 retval = g_variant_ref_sink(g_variant_new_int32(int_val));
1511         } else if (!strcmp(prop, DLS_INTERFACE_PROP_HEIGHT)) {
1512                 int_val = (int) gupnp_didl_lite_resource_get_height(res);
1513                 if (int_val == -1)
1514                         goto on_error;
1515                 retval = g_variant_ref_sink(g_variant_new_int32(int_val));
1516         } else if (!strcmp(prop, DLS_INTERFACE_PROP_COLOR_DEPTH)) {
1517                 int_val = (int) gupnp_didl_lite_resource_get_color_depth(res);
1518                 if (int_val == -1)
1519                         goto on_error;
1520                 retval = g_variant_ref_sink(g_variant_new_int32(int_val));
1521         } else if (!strcmp(prop, DLS_INTERFACE_PROP_URLS)) {
1522                 str_val = gupnp_didl_lite_resource_get_uri(res);
1523                 if (str_val)
1524                         retval = g_variant_new_strv(&str_val, 1);
1525         }
1526
1527 on_error:
1528
1529         return retval;
1530 }
1531
1532 GVariant *dls_props_get_object_prop(const gchar *prop, const gchar *root_path,
1533                                     GUPnPDIDLLiteObject *object)
1534 {
1535         const char *id;
1536         gchar *path;
1537         const char *upnp_class;
1538         const char *media_spec_type;
1539         const char *title;
1540         gboolean rest;
1541         GVariant *retval = NULL;
1542         GUPnPOCMFlags dlna_managed;
1543         guint uint_val;
1544
1545         if (!strcmp(prop, DLS_INTERFACE_PROP_PARENT)) {
1546                 id = gupnp_didl_lite_object_get_parent_id(object);
1547                 if (!id || !strcmp(id, "-1")) {
1548                         DLEYNA_LOG_DEBUG("Prop %s = %s", prop, root_path);
1549
1550                         retval = g_variant_ref_sink(g_variant_new_string(
1551                                                             root_path));
1552                 } else {
1553                         path = dls_path_from_id(root_path, id);
1554
1555                         DLEYNA_LOG_DEBUG("Prop %s = %s", prop, path);
1556
1557                         retval = g_variant_ref_sink(g_variant_new_string(
1558                                                             path));
1559                         g_free(path);
1560                 }
1561         } else if (!strcmp(prop, DLS_INTERFACE_PROP_PATH)) {
1562                 id = gupnp_didl_lite_object_get_id(object);
1563                 if (!id)
1564                         goto on_error;
1565
1566                 path = dls_path_from_id(root_path, id);
1567
1568                 DLEYNA_LOG_DEBUG("Prop %s = %s", prop, path);
1569
1570                 retval = g_variant_ref_sink(g_variant_new_string(path));
1571                 g_free(path);
1572         } else if (!strcmp(prop, DLS_INTERFACE_PROP_TYPE)) {
1573                 upnp_class = gupnp_didl_lite_object_get_upnp_class(object);
1574                 media_spec_type =
1575                         dls_props_upnp_class_to_media_spec(upnp_class);
1576                 if (!media_spec_type)
1577                         goto on_error;
1578
1579                 DLEYNA_LOG_DEBUG("Prop %s = %s", prop, media_spec_type);
1580
1581                 retval = g_variant_ref_sink(g_variant_new_string(
1582                                                     media_spec_type));
1583         } else if (!strcmp(prop, DLS_INTERFACE_PROP_DISPLAY_NAME)) {
1584                 title = gupnp_didl_lite_object_get_title(object);
1585                 if (!title)
1586                         goto on_error;
1587
1588                 DLEYNA_LOG_DEBUG("Prop %s = %s", prop, title);
1589
1590                 retval = g_variant_ref_sink(g_variant_new_string(title));
1591         } else if (!strcmp(prop, DLS_INTERFACE_PROP_CREATOR)) {
1592                 title = gupnp_didl_lite_object_get_creator(object);
1593                 if (!title)
1594                         goto on_error;
1595
1596                 DLEYNA_LOG_DEBUG("Prop %s = %s", prop, title);
1597
1598                 retval = g_variant_ref_sink(g_variant_new_string(title));
1599         } else if (!strcmp(prop, DLS_INTERFACE_PROP_RESTRICTED)) {
1600                 rest = gupnp_didl_lite_object_get_restricted(object);
1601
1602                 DLEYNA_LOG_DEBUG("Prop %s = %d", prop, rest);
1603
1604                 retval = g_variant_ref_sink(g_variant_new_boolean(rest));
1605         } else if (!strcmp(prop, DLS_INTERFACE_PROP_DLNA_MANAGED)) {
1606                 dlna_managed = gupnp_didl_lite_object_get_dlna_managed(object);
1607
1608                 DLEYNA_LOG_DEBUG("Prop %s = %0x", prop, dlna_managed);
1609
1610                 retval = g_variant_ref_sink(
1611                                 prv_props_get_dlna_managed_dict(dlna_managed));
1612         } else if (!strcmp(prop, DLS_INTERFACE_PROP_OBJECT_UPDATE_ID)) {
1613                 uint_val = gupnp_didl_lite_object_get_update_id(object);
1614
1615                 DLEYNA_LOG_DEBUG("Prop %s = %u", prop, uint_val);
1616
1617                 retval = g_variant_ref_sink(g_variant_new_uint32(uint_val));
1618         }
1619
1620 on_error:
1621
1622         return retval;
1623 }
1624
1625 GVariant *dls_props_get_item_prop(const gchar *prop, const gchar *root_path,
1626                                   GUPnPDIDLLiteObject *object,
1627                                   const gchar *protocol_info)
1628 {
1629         const gchar *str;
1630         gchar *path;
1631         gint track_number;
1632         GUPnPDIDLLiteResource *res;
1633         GVariant *retval = NULL;
1634         GList *list;
1635
1636         if (GUPNP_IS_DIDL_LITE_CONTAINER(object))
1637                 goto on_error;
1638
1639         if (!strcmp(prop, DLS_INTERFACE_PROP_ARTIST)) {
1640                 str = gupnp_didl_lite_object_get_artist(object);
1641                 if (!str)
1642                         goto on_error;
1643
1644                 DLEYNA_LOG_DEBUG("Prop %s = %s", prop, str);
1645
1646                 retval = g_variant_ref_sink(g_variant_new_string(str));
1647         } else if (!strcmp(prop, DLS_INTERFACE_PROP_ARTISTS)) {
1648                 list = gupnp_didl_lite_object_get_artists(object);
1649                 if (!list)
1650                         goto on_error;
1651
1652                 retval = g_variant_ref_sink(prv_get_artists_prop(list));
1653                 g_list_free_full(list, g_object_unref);
1654
1655 #if DLEYNA_LOG_LEVEL & DLEYNA_LOG_LEVEL_DEBUG
1656                 path = g_variant_print(retval, FALSE);
1657                 DLEYNA_LOG_DEBUG("Prop %s = %s", prop, path);
1658                 g_free(path);
1659 #endif
1660         } else if (!strcmp(prop, DLS_INTERFACE_PROP_ALBUM)) {
1661                 str = gupnp_didl_lite_object_get_album(object);
1662                 if (!str)
1663                         goto on_error;
1664
1665                 DLEYNA_LOG_DEBUG("Prop %s = %s", prop, str);
1666
1667                 retval = g_variant_ref_sink(g_variant_new_string(str));
1668         } else if (!strcmp(prop, DLS_INTERFACE_PROP_DATE)) {
1669                 str = gupnp_didl_lite_object_get_date(object);
1670                 if (!str)
1671                         goto on_error;
1672
1673                 DLEYNA_LOG_DEBUG("Prop %s = %s", prop, str);
1674
1675                 retval = g_variant_ref_sink(g_variant_new_string(str));
1676         } else if (!strcmp(prop, DLS_INTERFACE_PROP_GENRE)) {
1677                 str = gupnp_didl_lite_object_get_genre(object);
1678                 if (!str)
1679                         goto on_error;
1680
1681                 DLEYNA_LOG_DEBUG("Prop %s = %s", prop, str);
1682
1683                 retval = g_variant_ref_sink(g_variant_new_string(str));
1684         } else if (!strcmp(prop, DLS_INTERFACE_PROP_TRACK_NUMBER)) {
1685                 track_number = gupnp_didl_lite_object_get_track_number(object);
1686                 if (track_number < 0)
1687                         goto on_error;
1688
1689                 DLEYNA_LOG_DEBUG("Prop %s = %d", prop, track_number);
1690
1691                 retval = g_variant_ref_sink(
1692                         g_variant_new_int32(track_number));
1693         } else if (!strcmp(prop, DLS_INTERFACE_PROP_ALBUM_ART_URL)) {
1694                 str = gupnp_didl_lite_object_get_album_art(object);
1695                 if (!str)
1696                         goto on_error;
1697
1698                 DLEYNA_LOG_DEBUG("Prop %s = %s", prop, str);
1699
1700                 retval = g_variant_ref_sink(g_variant_new_string(str));
1701         } else if (!strcmp(prop, DLS_INTERFACE_PROP_REFPATH)) {
1702                 str = gupnp_didl_lite_item_get_ref_id(
1703                                         GUPNP_DIDL_LITE_ITEM(object));
1704                 if (!str)
1705                         goto on_error;
1706
1707                 DLEYNA_LOG_DEBUG("Prop %s = %s", prop, str);
1708
1709                 path = dls_path_from_id(root_path, str);
1710                 retval = g_variant_ref_sink(g_variant_new_string(path));
1711                 g_free(path);
1712         } else if (!strcmp(prop, DLS_INTERFACE_PROP_RESOURCES)) {
1713                 retval = g_variant_ref_sink(
1714                         prv_compute_resources(object, DLS_UPNP_MASK_ALL_PROPS));
1715         } else {
1716                 res = prv_get_matching_resource(object, protocol_info);
1717                 if (!res)
1718                         goto on_error;
1719
1720                 retval = prv_get_resource_property(prop, res);
1721
1722                 g_object_unref(res);
1723         }
1724
1725 on_error:
1726
1727         return retval;
1728 }
1729
1730 GVariant *dls_props_get_container_prop(const gchar *prop,
1731                                        GUPnPDIDLLiteObject *object)
1732 {
1733         gint child_count;
1734         gboolean searchable;
1735         GUPnPDIDLLiteContainer *container;
1736         GVariant *retval = NULL;
1737         guint uint_val;
1738 #if DLEYNA_LOG_LEVEL & DLEYNA_LOG_LEVEL_DEBUG
1739         gchar *create_classes;
1740 #endif
1741         if (!GUPNP_IS_DIDL_LITE_CONTAINER(object))
1742                 goto on_error;
1743
1744         container = (GUPnPDIDLLiteContainer *)object;
1745         if (!strcmp(prop, DLS_INTERFACE_PROP_CHILD_COUNT)) {
1746                 child_count =
1747                         gupnp_didl_lite_container_get_child_count(container);
1748
1749                 DLEYNA_LOG_DEBUG("Prop %s = %d", prop, child_count);
1750
1751                 if (child_count >= 0) {
1752                         retval = g_variant_new_uint32((guint) child_count);
1753                         retval = g_variant_ref_sink(retval);
1754                 }
1755         } else if (!strcmp(prop, DLS_INTERFACE_PROP_SEARCHABLE)) {
1756                 searchable =
1757                         gupnp_didl_lite_container_get_searchable(container);
1758
1759                 DLEYNA_LOG_DEBUG("Prop %s = %d", prop, searchable);
1760
1761                 retval = g_variant_ref_sink(
1762                         g_variant_new_boolean(searchable));
1763         } else if (!strcmp(prop, DLS_INTERFACE_PROP_CREATE_CLASSES)) {
1764                 retval = g_variant_ref_sink(
1765                         prv_compute_create_classes(container));
1766 #if DLEYNA_LOG_LEVEL & DLEYNA_LOG_LEVEL_DEBUG
1767                 create_classes = g_variant_print(retval, FALSE);
1768                 DLEYNA_LOG_DEBUG("Prop %s = %s", prop, create_classes);
1769                 g_free(create_classes);
1770 #endif
1771         } else if (!strcmp(prop, DLS_INTERFACE_PROP_CONTAINER_UPDATE_ID)) {
1772                 uint_val = gupnp_didl_lite_container_get_container_update_id(
1773                                                                 container);
1774
1775                 DLEYNA_LOG_DEBUG("Prop %s = %u", prop, uint_val);
1776
1777                 retval = g_variant_ref_sink(g_variant_new_uint32(uint_val));
1778         } else if (!strcmp(prop,
1779                                 DLS_INTERFACE_PROP_TOTAL_DELETED_CHILD_COUNT)) {
1780                 uint_val =
1781                         gupnp_didl_lite_container_get_total_deleted_child_count(
1782                                                                 container);
1783
1784                 DLEYNA_LOG_DEBUG("Prop %s = %u", prop, uint_val);
1785
1786                 retval = g_variant_ref_sink(g_variant_new_uint32(uint_val));
1787         }
1788
1789 on_error:
1790
1791         return retval;
1792 }