d51d3851a7f29ad31607a05e51d91bb7f844cfae
[profile/ivi/GUPnP-AV.git] / libgupnp-av / gupnp-cds-last-change-parser.c
1 /*
2  * Copyright (C) 2012 Intel Corporation.
3  *
4  * Authors: Jens Georg <jensg@openismus.com>
5  *
6  * This library is free software; you can redistribute it and/or
7  * modify it under the terms of the GNU Library General Public
8  * License as published by the Free Software Foundation; either
9  * version 2 of the License, or (at your option) any later version.
10  *
11  * This library is distributed in the hope that it will be useful,
12  * but WITHOUT ANY WARRANTY; without even the implied warranty of
13  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
14  * Library General Public License for more details.
15  *
16  * You should have received a copy of the GNU Library General Public
17  * License along with this library; if not, write to the
18  * Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
19  * Boston, MA 02110-1301, USA.
20  */
21
22 /**
23  * SECTION:gupnp-cds-last-change-parser
24  * @short_description: LastChange parser for the format used in
25  * CDS:3
26  *
27  * #GUPnPCDSLastChangeParser parses XML strings from
28  * CDS's LastChange state variable.
29  *
30  */
31
32 #include <libgupnp/gupnp-error.h>
33
34 #include "xml-util.h"
35 #include "gupnp-cds-last-change-parser.h"
36
37 /**
38  * GUPnPCDSLastChangeEntry:
39  *
40  * Opaque struct which contains information about the event.
41  **/
42 struct _GUPnPCDSLastChangeEntry {
43         int ref_count;
44         GUPnPCDSLastChangeEvent event;
45         char *object_id;
46         char *parent_id;
47         char *class;
48         guint32 update_id;
49         gboolean is_subtree_update;
50 };
51
52 G_DEFINE_TYPE (GUPnPCDSLastChangeParser,
53                gupnp_cds_last_change_parser,
54                G_TYPE_OBJECT)
55
56 G_DEFINE_BOXED_TYPE (GUPnPCDSLastChangeEntry,
57                      gupnp_cds_last_change_entry,
58                      gupnp_cds_last_change_entry_ref,
59                      gupnp_cds_last_change_entry_unref);
60
61 static void
62 gupnp_cds_last_change_parser_init (GUPnPCDSLastChangeParser *parser)
63 {
64 }
65
66 static void
67 gupnp_cds_last_change_parser_class_init (GUPnPCDSLastChangeParserClass *klass)
68 {
69 }
70
71 /**
72  * gupnp_cds_last_change_parser_new:
73  *
74  * Create a new #GUPnPCDSLastChangeParser.
75  *
76  * This parser is able to parse LastChange as defined in the
77  * ContentDirectory:3 specification.
78  *
79  * Returns:(transfer full): A new instance of #GUPnPCDSLastChangeParser.
80  **/
81 GUPnPCDSLastChangeParser *
82 gupnp_cds_last_change_parser_new (void)
83 {
84         return g_object_new (GUPNP_TYPE_CDS_LAST_CHANGE_PARSER,
85                              NULL);
86 }
87
88 /**
89  * gupnp_cds_last_change_parser_parse:
90  * @parser: #GUPnPCDSLastChangeParser
91  * @last_change: XML string to parse
92  * @error: Return value for parser error or %NULL to ingore
93  *
94  * Parse a LastChange XML document in the flavor defined by the
95  * ContentDirectory:3 specification.
96  *
97  * Returns: (element-type GUPnPCDSLastChangeEntry)(transfer full):
98  * List of #GUPnPCDSLastChangeEntry<!-- -->s
99  **/
100 GList *
101 gupnp_cds_last_change_parser_parse (GUPnPCDSLastChangeParser *parser,
102                                     const char               *last_change,
103                                     GError                  **error)
104
105 {
106         xmlDoc *doc;
107         xmlNode *state_event, *it;
108         GList *result = NULL;
109         GUPnPCDSLastChangeEntry *entry;
110
111         g_return_val_if_fail (GUPNP_IS_CDS_LAST_CHANGE_PARSER (parser),
112                               NULL);
113
114         doc = xmlParseDoc ((const xmlChar *) last_change);
115         if (doc == NULL) {
116                 g_set_error (error,
117                              GUPNP_XML_ERROR,
118                              GUPNP_XML_ERROR_PARSE,
119                              "Could not parse LastChange XML");
120
121                 goto out;
122         }
123
124         state_event = xml_util_get_element ((xmlNode *) doc,
125                                             "StateEvent",
126                                             NULL);
127         if (state_event == NULL) {
128                 g_set_error (error,
129                              GUPNP_XML_ERROR,
130                              GUPNP_XML_ERROR_PARSE,
131                              "Missing StateEvent node");
132
133                 goto out;
134         }
135
136         for (it = state_event->children; it != NULL; it = it->next) {
137                 if (it->type == XML_TEXT_NODE)
138                         continue;
139                 else if (g_ascii_strcasecmp ((const char *) it->name,
140                                              "objAdd") == 0) {
141                         const char *tmp;
142
143                         entry = g_slice_new0 (GUPnPCDSLastChangeEntry);
144                         entry->ref_count = 1;
145                         entry->event = GUPNP_CDS_LAST_CHANGE_EVENT_OBJECT_ADDED;
146
147                         tmp = xml_util_get_attribute_content (it, "objID");
148                         entry->object_id = g_strdup (tmp);
149
150                         tmp = xml_util_get_attribute_content (it,
151                                                               "objParentID");
152                         entry->parent_id = g_strdup (tmp);
153
154                         tmp = xml_util_get_attribute_content (it, "objClass");
155                         entry->class = g_strdup (tmp);
156
157                         entry->update_id = (guint32) xml_util_get_uint_attribute
158                                         (it,
159                                          "updateID",
160                                          0);
161                         entry->is_subtree_update =
162                                         xml_util_get_boolean_attribute
163                                                 (it,
164                                                  "stUpdate");
165                 } else if (g_ascii_strcasecmp ((const char *) it->name,
166                                                "objMod") == 0) {
167                         const char *tmp;
168
169                         entry = g_slice_new0 (GUPnPCDSLastChangeEntry);
170                         entry->ref_count = 1;
171                         entry->event = GUPNP_CDS_LAST_CHANGE_EVENT_OBJECT_MODIFIED;
172
173                         tmp = xml_util_get_attribute_content (it, "objID");
174                         entry->object_id = g_strdup (tmp);
175
176                         entry->update_id = (guint32) xml_util_get_uint_attribute
177                                         (it,
178                                          "updateID",
179                                          0);
180                         entry->is_subtree_update =
181                                         xml_util_get_boolean_attribute
182                                                 (it,
183                                                  "stUpdate");
184                 } else if (g_ascii_strcasecmp ((const char *) it->name,
185                                                "objDel") == 0) {
186                         const char *tmp;
187
188                         entry = g_slice_new0 (GUPnPCDSLastChangeEntry);
189                         entry->ref_count = 1;
190                         entry->event = GUPNP_CDS_LAST_CHANGE_EVENT_OBJECT_REMOVED;
191
192                         tmp = xml_util_get_attribute_content (it, "objID");
193                         entry->object_id = g_strdup (tmp);
194
195                         entry->update_id = (guint32) xml_util_get_uint_attribute
196                                         (it,
197                                          "updateID",
198                                          0);
199                         entry->is_subtree_update =
200                                         xml_util_get_boolean_attribute
201                                                 (it,
202                                                  "stUpdate");
203                 } else if (g_ascii_strcasecmp ((const char *) it->name,
204                                                "stDone") == 0) {
205                         const char *tmp;
206
207                         entry = g_slice_new0 (GUPnPCDSLastChangeEntry);
208                         entry->ref_count = 1;
209                         entry->event = GUPNP_CDS_LAST_CHANGE_EVENT_ST_DONE;
210
211                         tmp = xml_util_get_attribute_content (it, "objID");
212                         entry->object_id = g_strdup (tmp);
213
214                         entry->update_id = (guint32) xml_util_get_uint_attribute
215                                         (it,
216                                          "updateID",
217                                          0);
218                 } else {
219                         g_warning ("Skipping invalid LastChange entry: %s",
220                                    (const char *) it->name);
221                         continue;
222                 }
223
224                 result = g_list_prepend (result, entry);
225         }
226
227         result = g_list_reverse (result);
228 out:
229         if (doc != NULL) {
230                 xmlFreeDoc (doc);
231         }
232
233         return result;
234
235 }
236
237 /**
238  * gupnp_cds_last_change_entry_ref:
239  * @entry: A #GUPnPCDSLastChangeEntry
240  *
241  * Increase reference count of a #GUPnPCDSLastChangeEntry.
242  *
243  * Returns:(transfer full): The object passed in @entry.
244  **/
245 GUPnPCDSLastChangeEntry *
246 gupnp_cds_last_change_entry_ref (GUPnPCDSLastChangeEntry *entry)
247 {
248         g_return_val_if_fail (entry != NULL, NULL);
249         g_return_val_if_fail (entry->ref_count > 0, NULL);
250
251         g_atomic_int_inc (&entry->ref_count);
252
253         return entry;
254 }
255
256 /**
257  * gupnp_cds_last_change_entry_unref:
258  * @entry: A #GUPnPCDSLastChangeEntry
259  *
260  * Decrease reference count of a #GUPnPCDSLastChangeEntry. If the reference
261  * count drops to 0, @entry is freed.
262  **/
263 void
264 gupnp_cds_last_change_entry_unref (GUPnPCDSLastChangeEntry *entry)
265 {
266         g_return_if_fail (entry != NULL);
267         g_return_if_fail (entry->ref_count > 0);
268
269         if (g_atomic_int_dec_and_test (&entry->ref_count)) {
270                 g_free (entry->class);
271                 g_free (entry->object_id);
272                 g_free (entry->parent_id);
273
274                 g_slice_free (GUPnPCDSLastChangeEntry, entry);
275         }
276 }
277
278 /**
279  * gupnp_cds_last_change_entry_get_event:
280  * @entry: A #GUPnPCDSLastChangeEntry
281  *
282  * Get the type of the last change entry as defined in
283  * #GUPnPCDSLastChangeEvent.
284  *
285  * Returns: An event from the #GUPnPCDSLastChangeEvent or
286  * %GUPNP_CDS_LAST_CHANGE_EVENT_INVALID if the entry is not valid.
287  **/
288 GUPnPCDSLastChangeEvent
289 gupnp_cds_last_change_entry_get_event (GUPnPCDSLastChangeEntry *entry)
290 {
291         g_return_val_if_fail (entry != NULL,
292                               GUPNP_CDS_LAST_CHANGE_EVENT_INVALID);
293
294         return entry->event;
295 }
296
297 /**
298  * gupnp_cds_last_change_entry_get_object_id:
299  * @entry: A #GUPnPCDSLastChangeEntry
300  *
301  * Get the ID of the object in this change entry.
302  *
303  * Returns: (transfer none): The id of the object of this entry.
304  **/
305 const char *
306 gupnp_cds_last_change_entry_get_object_id (GUPnPCDSLastChangeEntry *entry)
307 {
308         g_return_val_if_fail (entry != NULL, NULL);
309
310         return entry->object_id;
311 }
312
313 /**
314  * gupnp_cds_last_change_entry_get_parent_id:
315  * @entry: A #GUPnPCDSLastChangeEntry
316  *
317  * Get the parent object id of the object in this change entry. This is only
318  * valid if gupnp_cds_last_change_entry_get_event() returns
319  * %GUPNP_CDS_LAST_CHANGE_EVENT_OBJECT_ADDED.
320  *
321  * Returns: (transfer none): The id of the object's parent of this entry.
322  **/
323 const char *
324 gupnp_cds_last_change_entry_get_parent_id (GUPnPCDSLastChangeEntry *entry)
325 {
326         g_return_val_if_fail (entry != NULL, NULL);
327
328         return entry->parent_id;
329 }
330
331 /**
332  * gupnp_cds_last_change_entry_get_class:
333  * @entry: A #GUPnPCDSLastChangeEntry
334  *
335  * Get the class of the object in this change entry. This is only
336  * valid if gupnp_cds_last_change_entry_get_event() returns
337  * %GUPNP_CDS_LAST_CHANGE_EVENT_OBJECT_ADDED.
338  *
339  * Returns: (transfer none): The upnp class of the object of this entry.
340  **/
341 const char *
342 gupnp_cds_last_change_entry_get_class (GUPnPCDSLastChangeEntry *entry)
343 {
344         g_return_val_if_fail (entry != NULL, NULL);
345
346         return entry->class;
347 }
348
349 /**
350  * gupnp_cds_last_change_entry_is_subtree_update:
351  * @entry: A #GUPnPCDSLastChangeEntry
352  *
353  * Returns whether this entry is part of a subtree update.
354  *
355  * Returns: %TRUE, if the entry is part of a subtree update, %FALSE otherwise.
356  **/
357 gboolean
358 gupnp_cds_last_change_entry_is_subtree_update (GUPnPCDSLastChangeEntry *entry)
359 {
360         g_return_val_if_fail (entry != NULL, FALSE);
361
362         return entry->is_subtree_update;
363 }
364
365 /**
366  * gupnp_cds_last_change_entry_get_update_id:
367  * @entry: A #GUPnPCDSLastChangeEntry
368  *
369  * Get the update id of the last change entry.
370  *
371  * Returns: update id of the entry or 0 if the entry is not valid.
372  **/
373 guint32
374 gupnp_cds_last_change_entry_get_update_id (GUPnPCDSLastChangeEntry *entry)
375 {
376         g_return_val_if_fail (entry != NULL, 0);
377
378         return entry->update_id;
379 }