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