1 /*
2  * libvirt-gconfig-object.c: base object for XML configuration
3  *
4  * Copyright (C) 2008 Daniel P. Berrange
5  * Copyright (C) 2010-2011 Red Hat, Inc.
6  *
7  * This library is free software; you can redistribute it and/or
8  * modify it under the terms of the GNU Lesser General Public
9  * License as published by the Free Software Foundation; either
10  * version 2.1 of the License, or (at your option) any later version.
11  *
12  * This library is distributed in the hope that it will be useful,
13  * but WITHOUT ANY WARRANTY; without even the implied warranty of
14  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
15  * Lesser General Public License for more details.
16  *
17  * You should have received a copy of the GNU Lesser General Public
18  * License along with this library. If not, see
19  * <http://www.gnu.org/licenses/>.
20  *
21  * Author: Daniel P. Berrange <berrange@redhat.com>
22  */
23 
24 #include <config.h>
25 
26 #include <string.h>
27 
28 #include <libxml/relaxng.h>
29 #include <glib/gi18n-lib.h>
30 
31 #include "libvirt-gconfig/libvirt-gconfig.h"
32 #include "libvirt-gconfig/libvirt-gconfig-private.h"
33 
34 #define GVIR_CONFIG_OBJECT_GET_PRIVATE(obj)                         \
35         (G_TYPE_INSTANCE_GET_PRIVATE((obj), GVIR_CONFIG_TYPE_OBJECT, GVirConfigObjectPrivate))
36 
37 struct _GVirConfigObjectPrivate
38 {
39     gchar *schema;
40 
41     GVirConfigXmlDoc *doc;
42     xmlNodePtr node;
43 };
44 
45 G_DEFINE_TYPE_WITH_PRIVATE(GVirConfigObject, gvir_config_object, G_TYPE_OBJECT);
46 
47 enum {
48     PROP_0,
49     PROP_SCHEMA,
50     PROP_NODE,
51     PROP_DOC
52 };
53 
54 
gvir_xml_generic_error_nop(void * userData G_GNUC_UNUSED,const char * msg G_GNUC_UNUSED,...)55 static void gvir_xml_generic_error_nop(void *userData G_GNUC_UNUSED,
56                                        const char *msg G_GNUC_UNUSED,
57                                        ...)
58 {
59 }
60 
gvir_xml_structured_error_nop(void * userData G_GNUC_UNUSED,xmlErrorPtr error G_GNUC_UNUSED)61 static void gvir_xml_structured_error_nop(void *userData G_GNUC_UNUSED,
62                                           xmlErrorPtr error G_GNUC_UNUSED)
63 {
64 }
65 
66 
gvir_config_object_get_property(GObject * object,guint prop_id,GValue * value,GParamSpec * pspec)67 static void gvir_config_object_get_property(GObject *object,
68                                             guint prop_id,
69                                             GValue *value,
70                                             GParamSpec *pspec)
71 {
72     GVirConfigObject *obj = GVIR_CONFIG_OBJECT(object);
73     GVirConfigObjectPrivate *priv = obj->priv;
74 
75     switch (prop_id) {
76     case PROP_SCHEMA:
77         g_value_set_string(value, priv->schema);
78         break;
79 
80     case PROP_NODE:
81         g_value_set_pointer(value, gvir_config_object_get_xml_node(obj));
82         break;
83 
84     case PROP_DOC:
85         g_value_set_object(value, obj->priv->doc);
86         break;
87 
88     default:
89         G_OBJECT_WARN_INVALID_PROPERTY_ID(object, prop_id, pspec);
90     }
91 }
92 
gvir_config_object_set_property(GObject * object,guint prop_id,const GValue * value,GParamSpec * pspec)93 static void gvir_config_object_set_property(GObject *object,
94                                             guint prop_id,
95                                             const GValue *value,
96                                             GParamSpec *pspec)
97 {
98     GVirConfigObject *obj = GVIR_CONFIG_OBJECT(object);
99     GVirConfigObjectPrivate *priv = obj->priv;
100 
101     switch (prop_id) {
102     case PROP_SCHEMA:
103         g_free(priv->schema);
104         priv->schema = g_value_dup_string(value);
105         break;
106 
107     case PROP_NODE:
108         priv->node =g_value_get_pointer(value);
109         break;
110 
111     case PROP_DOC:
112         obj->priv->doc = g_value_dup_object(value);
113         break;
114 
115     default:
116         G_OBJECT_WARN_INVALID_PROPERTY_ID(object, prop_id, pspec);
117     }
118 }
119 
120 
gvir_config_object_finalize(GObject * object)121 static void gvir_config_object_finalize(GObject *object)
122 {
123     GVirConfigObject *gvir_object = GVIR_CONFIG_OBJECT(object);
124     GVirConfigObjectPrivate *priv = gvir_object->priv;
125 
126     g_free(priv->schema);
127 
128     if (priv->doc != NULL) {
129         g_object_unref(G_OBJECT(priv->doc));
130         priv->node = NULL; /* node belongs to doc, make sure not to free it */
131     }
132     if (priv->node != NULL) {
133         g_assert(priv->node->doc == NULL);
134         xmlFreeNode(priv->node);
135     }
136 
137     G_OBJECT_CLASS(gvir_config_object_parent_class)->finalize(object);
138 }
139 
140 
gvir_config_object_class_init(GVirConfigObjectClass * klass)141 static void gvir_config_object_class_init(GVirConfigObjectClass *klass)
142 {
143     GObjectClass *object_class = G_OBJECT_CLASS(klass);
144 
145     object_class->finalize = gvir_config_object_finalize;
146     object_class->get_property = gvir_config_object_get_property;
147     object_class->set_property = gvir_config_object_set_property;
148 
149     g_object_class_install_property(object_class,
150                                     PROP_SCHEMA,
151                                     g_param_spec_string("schema",
152                                                         "Schema",
153                                                         "The doc RNG schema",
154                                                         NULL,
155                                                         G_PARAM_READABLE |
156                                                         G_PARAM_WRITABLE |
157                                                         G_PARAM_CONSTRUCT_ONLY |
158                                                         G_PARAM_STATIC_STRINGS));
159 
160     g_object_class_install_property(object_class,
161                                     PROP_NODE,
162                                     g_param_spec_pointer("node",
163                                                         "XML Node",
164                                                         "The XML node this config object corresponds to",
165                                                         G_PARAM_READWRITE |
166                                                         G_PARAM_CONSTRUCT_ONLY |
167                                                         G_PARAM_STATIC_STRINGS));
168 
169     g_object_class_install_property(object_class,
170                                     PROP_DOC,
171                                     g_param_spec_object("doc",
172                                                         "XML Doc",
173                                                         "The XML doc this config object corresponds to",
174                                                         GVIR_CONFIG_TYPE_XML_DOC,
175                                                         G_PARAM_READWRITE |
176                                                         G_PARAM_CONSTRUCT_ONLY |
177                                                         G_PARAM_STATIC_STRINGS));
178 }
179 
180 
gvir_config_object_init(GVirConfigObject * object)181 static void gvir_config_object_init(GVirConfigObject *object)
182 {
183     object->priv = GVIR_CONFIG_OBJECT_GET_PRIVATE(object);
184 }
185 
gvir_config_object_validate(GVirConfigObject * config,GError ** err)186 void gvir_config_object_validate(GVirConfigObject *config,
187                                  GError **err)
188 {
189     GVirConfigObjectPrivate *priv;
190     xmlRelaxNGParserCtxtPtr rngParser = NULL;
191     xmlRelaxNGPtr rng = NULL;
192     xmlRelaxNGValidCtxtPtr rngValid = NULL;
193 
194     g_return_if_fail(GVIR_CONFIG_IS_OBJECT(config));
195     g_return_if_fail(err == NULL || *err == NULL);
196 
197     priv = config->priv;
198 
199     xmlSetGenericErrorFunc(NULL, gvir_xml_generic_error_nop);
200     xmlSetStructuredErrorFunc(NULL, gvir_xml_structured_error_nop);
201 
202     if (!priv->node) {
203         gvir_config_set_error_literal(err,
204                                       GVIR_CONFIG_OBJECT_ERROR,
205                                       0,
206                                       _("No XML document associated with this config object"));
207         return;
208     }
209 
210     if (!priv->schema) {
211         gvir_config_set_error_literal(err,
212                                       GVIR_CONFIG_OBJECT_ERROR,
213                                       0,
214                                       _("No XML schema associated with this config object"));
215         return;
216     }
217 
218     rngParser = xmlRelaxNGNewParserCtxt(priv->schema);
219     if (!rngParser) {
220         gvir_config_set_error(err,
221                               GVIR_CONFIG_OBJECT_ERROR,
222                               0,
223                               _("Unable to create RNG parser for %s"),
224                               priv->schema);
225         return;
226     }
227 
228     rng = xmlRelaxNGParse(rngParser);
229     if (!rng) {
230         gvir_config_set_error(err,
231                               GVIR_CONFIG_OBJECT_ERROR,
232                               0,
233                               _("Unable to parse RNG %s"),
234                               priv->schema);
235         xmlRelaxNGFreeParserCtxt(rngParser);
236         return;
237     }
238     xmlRelaxNGFreeParserCtxt(rngParser);
239 
240     rngValid = xmlRelaxNGNewValidCtxt(rng);
241     if (!rngValid) {
242         gvir_config_set_error(err,
243                               GVIR_CONFIG_OBJECT_ERROR,
244                               0,
245                               _("Unable to create RNG validation context %s"),
246                               priv->schema);
247         xmlRelaxNGFree(rng);
248         return;
249     }
250 
251     if (xmlRelaxNGValidateDoc(rngValid, priv->node->doc) != 0) {
252         gvir_config_set_error_literal(err,
253                                       GVIR_CONFIG_OBJECT_ERROR,
254                                       0,
255                                       _("Unable to validate doc"));
256         xmlRelaxNGFreeValidCtxt(rngValid);
257         xmlRelaxNGFree(rng);
258         return;
259     }
260 
261     xmlRelaxNGFreeValidCtxt(rngValid);
262     xmlRelaxNGFree(rng);
263 }
264 
gvir_config_object_to_xml(GVirConfigObject * config)265 gchar *gvir_config_object_to_xml(GVirConfigObject *config)
266 {
267     g_return_val_if_fail(GVIR_CONFIG_IS_OBJECT(config), NULL);
268 
269     return gvir_config_xml_node_to_string(config->priv->node);
270 }
271 
gvir_config_object_get_schema(GVirConfigObject * config)272 const gchar *gvir_config_object_get_schema(GVirConfigObject *config)
273 {
274     g_return_val_if_fail(GVIR_CONFIG_IS_OBJECT(config), NULL);
275 
276     return config->priv->schema;
277 }
278 
279 
280 G_GNUC_INTERNAL GVirConfigXmlDoc *
gvir_config_object_get_xml_doc(GVirConfigObject * config)281 gvir_config_object_get_xml_doc(GVirConfigObject *config)
282 {
283     return config->priv->doc;
284 }
285 
286 
287 /* FIXME: will we always have one xmlNode per GConfig object? */
288 /* FIXME: need to return the right node from subclasses */
289 /* NB: the xmlNodePtr must not be freed by the caller */
290 G_GNUC_INTERNAL xmlNodePtr
gvir_config_object_get_xml_node(GVirConfigObject * config)291 gvir_config_object_get_xml_node(GVirConfigObject *config)
292 {
293     return config->priv->node;
294 }
295 
296 G_GNUC_INTERNAL const char *
gvir_config_object_get_node_content(GVirConfigObject * object,const char * node_name)297 gvir_config_object_get_node_content(GVirConfigObject *object,
298                                     const char *node_name)
299 {
300     xmlNodePtr node;
301 
302     node = gvir_config_object_get_xml_node(GVIR_CONFIG_OBJECT(object));
303     if (node == NULL)
304         return NULL;
305 
306     return gvir_config_xml_get_child_element_content(node, node_name);
307 }
308 
309 G_GNUC_INTERNAL const char *
gvir_config_object_get_attribute(GVirConfigObject * object,const char * node_name,const char * attr_name)310 gvir_config_object_get_attribute(GVirConfigObject *object,
311                                  const char *node_name,
312                                  const char *attr_name)
313 {
314     xmlNodePtr node;
315 
316     g_return_val_if_fail(attr_name != NULL, NULL);
317 
318     node = gvir_config_object_get_xml_node(GVIR_CONFIG_OBJECT(object));
319     if (node == NULL)
320         return NULL;
321 
322     if (node_name != NULL) {
323         node = gvir_config_xml_get_element(node, node_name, NULL);
324         if (node == NULL)
325             return NULL;
326     }
327 
328     return gvir_config_xml_get_attribute_content(node, attr_name);
329 }
330 
331 static xmlNodePtr
gvir_config_object_set_child_internal(GVirConfigObject * object,xmlNodePtr child,gboolean overwrite)332 gvir_config_object_set_child_internal(GVirConfigObject *object,
333                                       xmlNodePtr child,
334                                       gboolean overwrite)
335 {
336     xmlNodePtr parent_node;
337     xmlNodePtr old_node;
338 
339     parent_node = gvir_config_object_get_xml_node(GVIR_CONFIG_OBJECT(object));
340     g_return_val_if_fail (parent_node != NULL, NULL);
341 
342     old_node = gvir_config_xml_get_element(parent_node, child->name, NULL);
343     /* FIXME: should we make sure there are no multiple occurrences
344      * of this node?
345      */
346     if (old_node) {
347         if (overwrite) {
348             old_node = xmlReplaceNode(old_node, child);
349             xmlFreeNode(old_node);
350         } else {
351             return old_node;
352         }
353     } else {
354         xmlAddChild(parent_node, child);
355     }
356 
357     return NULL;
358 }
359 
360 G_GNUC_INTERNAL void
gvir_config_object_set_child(GVirConfigObject * object,xmlNodePtr child)361 gvir_config_object_set_child(GVirConfigObject *object, xmlNodePtr child)
362 {
363     gvir_config_object_set_child_internal(object, child, TRUE);
364 }
365 
366 G_GNUC_INTERNAL void
gvir_config_object_foreach_child(GVirConfigObject * object,const char * parent_name,GVirConfigXmlNodeIterator iter_func,gpointer opaque)367 gvir_config_object_foreach_child(GVirConfigObject *object,
368                                  const char *parent_name,
369                                  GVirConfigXmlNodeIterator iter_func,
370                                  gpointer opaque)
371 {
372     xmlNodePtr root_node;
373     xmlNodePtr node;
374 
375     g_return_if_fail(GVIR_CONFIG_IS_OBJECT(object));
376 
377     root_node = gvir_config_object_get_xml_node(object);
378     g_return_if_fail(root_node != NULL);
379 
380     node = gvir_config_xml_get_element(root_node, parent_name, NULL);
381     if (node == NULL)
382         return;
383 
384     gvir_config_xml_foreach_child(node, iter_func, opaque);
385 }
386 
387 G_GNUC_INTERNAL GVirConfigObject *
gvir_config_object_add_child(GVirConfigObject * object,const char * child_name)388 gvir_config_object_add_child(GVirConfigObject *object,
389                              const char *child_name)
390 {
391     xmlNodePtr new_node;
392     xmlNodePtr old_node;
393 
394     g_return_val_if_fail(GVIR_CONFIG_IS_OBJECT(object), NULL);
395     g_return_val_if_fail(child_name != NULL, NULL);
396 
397     new_node = xmlNewDocNode(NULL, NULL, (xmlChar *)child_name, NULL);
398     old_node = gvir_config_object_set_child_internal(object, new_node,
399                                                      FALSE);
400     if (old_node != NULL) {
401         xmlFreeNode(new_node);
402         return GVIR_CONFIG_OBJECT(g_object_new(GVIR_CONFIG_TYPE_OBJECT,
403                                                "doc", object->priv->doc,
404                                                "node", old_node,
405                                                NULL));
406     }
407 
408     return GVIR_CONFIG_OBJECT(g_object_new(GVIR_CONFIG_TYPE_OBJECT,
409                                            "doc", object->priv->doc,
410                                            "node", new_node,
411                                            NULL));
412 }
413 
414 G_GNUC_INTERNAL void
gvir_config_object_add_child_with_attribute(GVirConfigObject * object,const char * child_name,const char * attr_name,const char * attr_value)415 gvir_config_object_add_child_with_attribute(GVirConfigObject *object,
416                                             const char *child_name,
417                                             const char *attr_name,
418                                             const char *attr_value)
419 {
420     GVirConfigObject *child;
421 
422     child = gvir_config_object_add_child(object, child_name);
423     gvir_config_object_set_attribute(child, attr_name, attr_value, NULL);
424     g_object_unref(G_OBJECT(child));
425 }
426 
427 
gvir_config_object_add_child_with_attribute_enum(GVirConfigObject * object,const char * child_name,const char * attr_name,GType attr_type,unsigned int attr_value)428 void gvir_config_object_add_child_with_attribute_enum(GVirConfigObject *object,
429                                                       const char *child_name,
430                                                       const char *attr_name,
431                                                       GType attr_type,
432                                                       unsigned int attr_value)
433 {
434     GVirConfigObject *child;
435 
436     child = gvir_config_object_add_child(object, child_name);
437     gvir_config_object_set_attribute_with_type(child, attr_name, attr_type, attr_value, NULL);
438     g_object_unref(G_OBJECT(child));
439 }
440 
441 
442 G_GNUC_INTERNAL GVirConfigObject *
gvir_config_object_replace_child(GVirConfigObject * object,const char * child_name)443 gvir_config_object_replace_child(GVirConfigObject *object,
444                                  const char *child_name)
445 {
446     xmlNodePtr new_node;
447 
448     g_return_val_if_fail(GVIR_CONFIG_IS_OBJECT(object), NULL);
449     g_return_val_if_fail(child_name != NULL, NULL);
450 
451     new_node = xmlNewDocNode(NULL, NULL, (xmlChar *)child_name, NULL);
452     gvir_config_object_set_child_internal(object, new_node, TRUE);
453 
454     return GVIR_CONFIG_OBJECT(g_object_new(GVIR_CONFIG_TYPE_OBJECT,
455                                            "doc", object->priv->doc,
456                                            "node", new_node,
457                                            NULL));
458 }
459 
460 G_GNUC_INTERNAL void
gvir_config_object_replace_child_with_attribute(GVirConfigObject * object,const char * child_name,const char * attr_name,const char * attr_value)461 gvir_config_object_replace_child_with_attribute(GVirConfigObject *object,
462                                                 const char *child_name,
463                                                 const char *attr_name,
464                                                 const char *attr_value)
465 {
466     GVirConfigObject *child;
467 
468     child = gvir_config_object_replace_child(object, child_name);
469     gvir_config_object_set_attribute(child, attr_name, attr_value, NULL);
470     g_object_unref(G_OBJECT(child));
471 }
472 
473 G_GNUC_INTERNAL void
gvir_config_object_replace_child_with_attribute_enum(GVirConfigObject * object,const char * child_name,const char * attr_name,GType attr_type,unsigned int attr_value)474 gvir_config_object_replace_child_with_attribute_enum(GVirConfigObject *object,
475                                                      const char *child_name,
476                                                      const char *attr_name,
477                                                      GType attr_type,
478                                                      unsigned int attr_value)
479 {
480     GVirConfigObject *child;
481 
482     child = gvir_config_object_replace_child(object, child_name);
483     gvir_config_object_set_attribute_with_type(child, attr_name, attr_type, attr_value, NULL);
484     g_object_unref(G_OBJECT(child));
485 }
486 
487 struct NodeMatch {
488     const char *name;
489     const char *ns;
490 };
491 
maybe_unlink_node(xmlNodePtr node,void * opaque)492 static gboolean maybe_unlink_node(xmlNodePtr node, void *opaque)
493 {
494     gboolean dounlink = TRUE;
495     struct NodeMatch *match = (struct NodeMatch *)opaque;
496 
497     if (match->ns != NULL) {
498         dounlink = dounlink && (g_strcmp0(match->ns, (char *)node->ns->href) == 0);
499     }
500 
501     if (match->name != NULL) {
502         dounlink = dounlink && (g_strcmp0(match->name, (char *)node->name) == 0);
503     }
504     if (dounlink) {
505         xmlUnlinkNode(node);
506         xmlFreeNode(node);
507     }
508 
509     return dounlink;
510 }
511 
remove_oneshot(xmlNodePtr node,gpointer opaque)512 static gboolean remove_oneshot(xmlNodePtr node, gpointer opaque)
513 {
514     return !maybe_unlink_node(node, opaque);
515 }
516 
517 G_GNUC_INTERNAL void
gvir_config_object_delete_child(GVirConfigObject * object,const char * child_name,const char * ns_href)518 gvir_config_object_delete_child(GVirConfigObject *object,
519                                 const char *child_name,
520                                 const char *ns_href)
521 {
522     struct NodeMatch match;
523 
524     g_return_if_fail(GVIR_CONFIG_IS_OBJECT(object));
525 
526     match.name = child_name;
527     match.ns = ns_href;
528     gvir_config_object_foreach_child(object, NULL, remove_oneshot, &match);
529 }
530 
remove_always(xmlNodePtr node,gpointer opaque)531 static gboolean remove_always(xmlNodePtr node, gpointer opaque)
532 {
533     maybe_unlink_node(node, opaque);
534 
535     return TRUE;
536 }
537 
538 G_GNUC_INTERNAL void
gvir_config_object_delete_children(GVirConfigObject * object,const char * child_name,const char * ns_href)539 gvir_config_object_delete_children(GVirConfigObject *object,
540                                    const char *child_name,
541                                    const char *ns_href)
542 {
543     struct NodeMatch match;
544 
545     g_return_if_fail(GVIR_CONFIG_IS_OBJECT(object));
546 
547     match.name = child_name;
548     match.ns = ns_href;
549 
550     gvir_config_object_foreach_child(object, NULL, remove_always, &match);
551 }
552 
553 G_GNUC_INTERNAL void
gvir_config_object_set_node_content(GVirConfigObject * object,const char * node_name,const char * value)554 gvir_config_object_set_node_content(GVirConfigObject *object,
555                                     const char *node_name,
556                                     const char *value)
557 {
558     xmlChar *encoded_data;
559     GVirConfigObject *node;
560 
561     g_return_if_fail(GVIR_CONFIG_IS_OBJECT(object));
562 
563     if (value == NULL) {
564         gvir_config_object_delete_child(object, node_name, NULL);
565 
566         return;
567     }
568 
569     if (node_name != NULL) {
570         node = gvir_config_object_replace_child(object, node_name);
571         g_return_if_fail(node != NULL);
572     } else {
573         node = g_object_ref(object);
574     }
575     encoded_data = xmlEncodeEntitiesReentrant(node->priv->node->doc,
576                                               (xmlChar *)value);
577     xmlNodeSetContent(node->priv->node, encoded_data);
578     xmlFree(encoded_data);
579     g_object_unref(G_OBJECT(node));
580 }
581 
582 G_GNUC_INTERNAL void
gvir_config_object_set_node_content_uint64(GVirConfigObject * object,const char * node_name,guint64 value)583 gvir_config_object_set_node_content_uint64(GVirConfigObject *object,
584                                            const char *node_name,
585                                            guint64 value)
586 {
587     char *str;
588 
589     g_return_if_fail(GVIR_CONFIG_IS_OBJECT(object));
590 
591     str = g_strdup_printf("%"G_GUINT64_FORMAT, value);
592     gvir_config_object_set_node_content(object, node_name, str);
593     g_free(str);
594 }
595 
596 /* FIXME: how to notify of errors/node not found? */
597 G_GNUC_INTERNAL guint64
gvir_config_object_get_node_content_uint64(GVirConfigObject * object,const char * node_name)598 gvir_config_object_get_node_content_uint64(GVirConfigObject *object,
599                                            const char *node_name)
600 {
601     xmlNodePtr node;
602     const char *str;
603     guint64 value;
604 
605     node = gvir_config_object_get_xml_node(GVIR_CONFIG_OBJECT(object));
606     if (node == NULL)
607         return 0;
608 
609     str = gvir_config_xml_get_child_element_content(node, node_name);
610     if (!str)
611         return 0;
612 
613     value = g_ascii_strtoull(str, NULL, 0);
614 
615     return value;
616 }
617 
618 G_GNUC_INTERNAL gint
gvir_config_object_get_node_content_genum(GVirConfigObject * object,const char * node_name,GType enum_type,gint default_value)619 gvir_config_object_get_node_content_genum(GVirConfigObject *object,
620                                           const char *node_name,
621                                           GType enum_type,
622                                           gint default_value)
623 {
624     xmlNodePtr node;
625     const char *str;
626     gint value;
627 
628     node = gvir_config_object_get_xml_node(GVIR_CONFIG_OBJECT(object));
629     if (node == NULL)
630         return default_value;
631 
632     str = gvir_config_xml_get_child_element_content(node, node_name);
633     if (!str)
634         return default_value;
635 
636     value = gvir_config_genum_get_value(enum_type, str, default_value);
637 
638     return value;
639 }
640 
641 G_GNUC_INTERNAL gint
gvir_config_object_get_attribute_genum(GVirConfigObject * object,const char * node_name,const char * attr_name,GType enum_type,gint default_value)642 gvir_config_object_get_attribute_genum(GVirConfigObject *object,
643                                        const char *node_name,
644                                        const char *attr_name,
645                                        GType enum_type,
646                                        gint default_value)
647 {
648     xmlNodePtr node;
649     const char *attr_val;
650     gint value;
651 
652     g_return_val_if_fail(attr_name != NULL, default_value);
653 
654     node = gvir_config_object_get_xml_node(GVIR_CONFIG_OBJECT(object));
655     if (node == NULL)
656         return default_value;
657 
658     if (node_name != NULL) {
659         node = gvir_config_xml_get_element(node, node_name, NULL);
660         if (node == NULL)
661             return default_value;
662     }
663 
664     attr_val = gvir_config_xml_get_attribute_content(node, attr_name);
665     if (attr_val == NULL)
666         return default_value;
667 
668     value = gvir_config_genum_get_value(enum_type, attr_val,
669                                         default_value);
670 
671     return value;
672 }
673 
674 G_GNUC_INTERNAL guint64
gvir_config_object_get_attribute_uint64(GVirConfigObject * object,const char * node_name,const char * attr_name,guint64 default_value)675 gvir_config_object_get_attribute_uint64(GVirConfigObject *object,
676                                         const char *node_name,
677                                         const char *attr_name,
678                                         guint64 default_value)
679 {
680     const char *str;
681 
682     str = gvir_config_object_get_attribute(object, node_name, attr_name);
683     if (str == NULL)
684         return default_value;
685 
686     return g_ascii_strtoull(str, NULL, 0);
687 }
688 
689 
690 G_GNUC_INTERNAL gboolean
gvir_config_object_get_attribute_boolean(GVirConfigObject * object,const char * node_name,const char * attr_name,gboolean default_value)691 gvir_config_object_get_attribute_boolean(GVirConfigObject *object,
692                                          const char *node_name,
693                                          const char *attr_name,
694                                          gboolean default_value)
695 {
696     const char *str;
697 
698     str = gvir_config_object_get_attribute(object, node_name, attr_name);
699     if (g_strcmp0(str, "yes") == 0) {
700         return TRUE;
701     } else if (g_strcmp0(str, "no") == 0) {
702         return FALSE;
703     } else {
704         return default_value;
705     }
706 }
707 
708 
gvir_config_object_new_from_xml(GType type,const char * root_name,const char * schema,const gchar * xml,GError ** error)709 GVirConfigObject *gvir_config_object_new_from_xml(GType type,
710                                                   const char *root_name,
711                                                   const char *schema,
712                                                   const gchar *xml,
713                                                   GError **error)
714 {
715     GVirConfigObject *object;
716     GVirConfigXmlDoc *doc;
717     xmlNodePtr node;
718     GError *tmp_error = NULL;
719 
720     node = gvir_config_xml_parse(xml, root_name, &tmp_error);
721     if (tmp_error != NULL) {
722         g_propagate_error(error, tmp_error);
723         return NULL;
724     }
725     doc = gvir_config_xml_doc_new(node->doc);
726     object = GVIR_CONFIG_OBJECT(g_object_new(type,
727                                              "doc", doc,
728                                              "node", node,
729                                              "schema", schema,
730                                              NULL));
731     g_object_unref(G_OBJECT(doc));
732 
733     return object;
734 }
735 
736 G_GNUC_INTERNAL GVirConfigObject *
gvir_config_object_new_from_tree(GType type,GVirConfigXmlDoc * doc,const char * schema,xmlNodePtr tree)737 gvir_config_object_new_from_tree(GType type, GVirConfigXmlDoc *doc,
738                                  const char *schema, xmlNodePtr tree)
739 {
740     g_return_val_if_fail(g_type_is_a(type, GVIR_CONFIG_TYPE_OBJECT), NULL);
741     g_return_val_if_fail(GVIR_CONFIG_IS_XML_DOC(doc), NULL);
742     g_return_val_if_fail(tree != NULL, NULL);
743 
744     return GVIR_CONFIG_OBJECT(g_object_new(type,
745                                            "doc", doc,
746                                            "node", tree,
747                                            "schema", schema,
748                                            NULL));
749 }
750 
gvir_config_object_new(GType type,const char * root_name,const char * schema)751 GVirConfigObject *gvir_config_object_new(GType type,
752                                          const char *root_name,
753                                          const char *schema)
754 {
755     GVirConfigObject *object;
756     GVirConfigXmlDoc *doc;
757     xmlDocPtr xml_doc;
758     xmlNodePtr node;
759 
760     doc = gvir_config_xml_doc_new(NULL);
761     g_object_get(G_OBJECT(doc), "doc", &xml_doc, NULL);
762     g_assert(xml_doc != NULL);
763     node = xmlNewDocNode(xml_doc, NULL, (xmlChar *)root_name, NULL);
764     xmlDocSetRootElement(xml_doc, node);
765     object = GVIR_CONFIG_OBJECT(g_object_new(type,
766                                              "doc", doc,
767                                              "node", node,
768                                              "schema", schema,
769                                              NULL));
770 
771     g_object_unref(G_OBJECT(doc));
772 
773     return object;
774 }
775 
776 G_GNUC_INTERNAL void
gvir_config_object_set_attribute(GVirConfigObject * object,...)777 gvir_config_object_set_attribute(GVirConfigObject *object, ...)
778 {
779     xmlDocPtr doc;
780     va_list args;
781 
782     g_return_if_fail(GVIR_CONFIG_IS_OBJECT(object));
783 
784     g_object_get(G_OBJECT(object->priv->doc), "doc", &doc, NULL);
785     va_start(args, object);
786     while (TRUE) {
787         const char *name;
788         const char *value;
789 
790         name = va_arg(args, const char *);
791         if (name == NULL) {
792             break;
793         }
794         gvir_config_object_remove_attribute(object, name);
795         value = va_arg(args, const char *);
796         if (value == NULL) {
797             g_warn_if_reached();
798             break;
799         }
800         xmlNewProp(object->priv->node, (xmlChar *)name, (xmlChar *)value);
801     }
802     va_end(args);
803 }
804 
805 G_GNUC_INTERNAL void
gvir_config_object_set_attribute_with_type(GVirConfigObject * object,...)806 gvir_config_object_set_attribute_with_type(GVirConfigObject *object, ...)
807 {
808     va_list args;
809 
810     g_return_if_fail(GVIR_CONFIG_IS_OBJECT(object));
811 
812     va_start(args, object);
813     while (TRUE) {
814         const char *name;
815         GType attr_type;
816         char *str;
817 
818 
819         name = va_arg(args, const char *);
820         if (name == NULL) {
821             break;
822         }
823         gvir_config_object_remove_attribute(object, name);
824 
825         attr_type = va_arg(args, GType);
826         if (G_TYPE_IS_ENUM(attr_type)) {
827             int val;
828             const char *enum_str;
829             val = va_arg(args, int);
830             enum_str = gvir_config_genum_get_nick(attr_type, val);
831             if (enum_str != NULL) {
832                 str = g_strdup(enum_str);
833             } else {
834                 str = NULL;
835             }
836         } else switch (attr_type) {
837             case G_TYPE_UINT64: {
838                 guint64 val;
839                 val = va_arg(args, guint64);
840                 str = g_strdup_printf("%"G_GUINT64_FORMAT, val);
841                 break;
842             }
843             case G_TYPE_UINT: {
844                 guint val;
845                 val = va_arg(args, guint);
846                 str = g_strdup_printf("%u", val);
847                 break;
848             }
849             case G_TYPE_INT: {
850                 gint val;
851                 val = va_arg(args, gint);
852                 str = g_strdup_printf("%d", val);
853                 break;
854             }
855             case G_TYPE_STRING:
856                 str = va_arg(args, char *);
857                 xmlNewProp(object->priv->node, (xmlChar *)name, (xmlChar *)str);
858                 str = NULL;
859                 break;
860             case G_TYPE_BOOLEAN: {
861                 gboolean val;
862                 val = va_arg(args, gboolean);
863                 str = g_strdup_printf("%s", val?"yes":"no");
864                 break;
865             }
866             default:
867                 g_warning("Unhandled type: %s", g_type_name(attr_type));
868                 g_assert_not_reached();
869         }
870 
871         if (str != NULL) {
872             xmlNewProp(object->priv->node, (xmlChar *)name, (xmlChar *)str);
873             g_free(str);
874         }
875     }
876     va_end(args);
877 }
878 
879 static void
gvir_config_object_attach(GVirConfigObject * parent,GVirConfigObject * child,gboolean replace)880 gvir_config_object_attach(GVirConfigObject *parent, GVirConfigObject *child, gboolean replace)
881 {
882     g_return_if_fail(GVIR_CONFIG_IS_OBJECT(parent));
883     g_return_if_fail(GVIR_CONFIG_IS_OBJECT(child));
884 
885     if (replace) {
886         gvir_config_object_delete_children(parent,
887                                            (char *)child->priv->node->name,
888                                            NULL);
889     }
890     xmlUnlinkNode(child->priv->node);
891     xmlAddChild(parent->priv->node, child->priv->node);
892     if (child->priv->doc != NULL) {
893         g_object_unref(G_OBJECT(child->priv->doc));
894         child->priv->doc = NULL;
895     }
896     if (parent->priv->doc != NULL) {
897         child->priv->doc = g_object_ref(parent->priv->doc);
898     }
899 }
900 
901 G_GNUC_INTERNAL void
gvir_config_object_attach_replace(GVirConfigObject * parent,const char * child_name,GVirConfigObject * child)902 gvir_config_object_attach_replace(GVirConfigObject *parent,
903                                   const char *child_name,
904                                   GVirConfigObject *child)
905 {
906     g_return_if_fail(child_name != NULL);
907 
908     if (child == NULL)
909         gvir_config_object_delete_children(parent, child_name, NULL);
910     else
911         gvir_config_object_attach(parent, child, TRUE);
912 }
913 
914 G_GNUC_INTERNAL void
gvir_config_object_attach_add(GVirConfigObject * parent,GVirConfigObject * child)915 gvir_config_object_attach_add(GVirConfigObject *parent, GVirConfigObject *child)
916 {
917     gvir_config_object_attach(parent, child, FALSE);
918 }
919 
920 G_GNUC_INTERNAL void
gvir_config_object_remove_attribute(GVirConfigObject * object,const char * attr_name)921 gvir_config_object_remove_attribute(GVirConfigObject *object,
922                                     const char *attr_name)
923 {
924     int status;
925 
926     do {
927         status = xmlUnsetProp(object->priv->node, (xmlChar *)attr_name);
928     } while (status == 0);
929 }
930 
931 static gboolean
gvir_config_object_set_xmlnode_namespace(xmlNodePtr node,const char * ns,const char * ns_uri)932 gvir_config_object_set_xmlnode_namespace(xmlNodePtr node, const char *ns,
933                                          const char *ns_uri)
934 {
935     xmlNsPtr namespace;
936 
937     namespace = xmlNewNs(node, (xmlChar *)ns_uri, (xmlChar *)ns);
938     if (namespace == NULL)
939         return FALSE;
940 
941     xmlSetNs(node, namespace);
942     return TRUE;
943 }
944 
945 static gboolean
gvir_config_object_set_namespace_recursively(xmlNodePtr node,const char * ns,const char * ns_uri)946 gvir_config_object_set_namespace_recursively(xmlNodePtr node,
947                                              const char *ns,
948                                              const char *ns_uri)
949 {
950     xmlNodePtr n;
951 
952     for (n = node; n != NULL; n = n->next) {
953         if (n->type == XML_ELEMENT_NODE) {
954             if (!gvir_config_object_set_xmlnode_namespace(n, ns, ns_uri))
955                 return FALSE;
956         }
957 
958         if (!gvir_config_object_set_namespace_recursively(n->children, ns, NULL))
959             return FALSE;
960     }
961 
962     return TRUE;
963 }
964 
965 G_GNUC_INTERNAL gboolean
gvir_config_object_set_namespace(GVirConfigObject * object,const char * ns,const char * ns_uri,gboolean ns_children)966 gvir_config_object_set_namespace(GVirConfigObject *object, const char *ns,
967                                  const char *ns_uri, gboolean ns_children)
968 {
969     g_return_val_if_fail(GVIR_CONFIG_IS_OBJECT(object), FALSE);
970     g_return_val_if_fail(ns != NULL, FALSE);
971     g_return_val_if_fail(ns_uri != NULL, FALSE);
972 
973     if (!ns_children) {
974         return gvir_config_object_set_xmlnode_namespace(object->priv->node,
975                                                         ns, ns_uri);
976     }
977 
978     return gvir_config_object_set_namespace_recursively(object->priv->node,
979                                                         ns, ns_uri);
980 }
981 
982 G_GNUC_INTERNAL GVirConfigObject *
gvir_config_object_get_child_with_type(GVirConfigObject * object,const gchar * child_name,GType child_type)983 gvir_config_object_get_child_with_type(GVirConfigObject *object,
984                                        const gchar *child_name,
985                                        GType child_type)
986 {
987     xmlNodePtr node;
988 
989     g_return_val_if_fail(GVIR_CONFIG_IS_OBJECT(object), NULL);
990     g_return_val_if_fail(child_name != NULL, NULL);
991 
992     node = gvir_config_xml_get_element(object->priv->node, child_name, NULL);
993     if (node == NULL)
994         return NULL;
995 
996     return gvir_config_object_new_from_tree(child_type,
997                                             object->priv->doc,
998                                             object->priv->schema,
999                                             node);
1000 }
1001 
1002 G_GNUC_INTERNAL GVirConfigObject *
gvir_config_object_get_child(GVirConfigObject * object,const gchar * child_name)1003 gvir_config_object_get_child(GVirConfigObject *object,
1004                              const gchar *child_name)
1005 {
1006     return gvir_config_object_get_child_with_type(object,
1007                                                   child_name,
1008                                                   GVIR_CONFIG_TYPE_OBJECT);
1009 }
1010 
1011 G_GNUC_INTERNAL gboolean
gvir_config_object_has_child(GVirConfigObject * object,const gchar * child_name)1012 gvir_config_object_has_child(GVirConfigObject *object, const gchar *child_name)
1013 {
1014     xmlNodePtr node;
1015 
1016     g_return_val_if_fail(GVIR_CONFIG_IS_OBJECT(object), FALSE);
1017     g_return_val_if_fail(child_name != NULL, FALSE);
1018 
1019     node = gvir_config_xml_get_element(object->priv->node, child_name, NULL);
1020 
1021     return (node != NULL);
1022 }
1023