summaryrefslogtreecommitdiff
path: root/src/common/xml.c
diff options
context:
space:
mode:
Diffstat (limited to 'src/common/xml.c')
-rw-r--r--src/common/xml.c809
1 files changed, 809 insertions, 0 deletions
diff --git a/src/common/xml.c b/src/common/xml.c
new file mode 100644
index 0000000..b89b4f9
--- /dev/null
+++ b/src/common/xml.c
@@ -0,0 +1,809 @@
+
+/* Firebox Tools - Outils de configurations pour le WM Firebox
+ * xml.c - lecture ou écriture de documents XML
+ *
+ * Copyright (C) 2006-2007 Cyrille Bagard
+ *
+ * This file is part of Firebox Tools.
+ *
+ * Firebox Tools is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * Firebox Tools is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+
+
+#include "xml.h"
+
+
+#include <stdarg.h>
+#include <string.h>
+
+
+#include "extstr.h"
+
+
+#ifdef DEBUG
+# define XML_LOG fprintf
+#else
+# define XML_LOG if (FALSE) fprintf
+#endif
+
+
+
+/******************************************************************************
+* *
+* Paramètres : filename = nom du fichier à ouvrir. *
+* xdoc = structure XML chargée. [OUT] *
+* context = contexte à utiliser pour les recherches. [OUT] *
+* *
+* Description : Crée un nouveau fichier XML. *
+* *
+* Retour : true si l'opération a pu s'effectuer, false sinon. *
+* *
+* Remarques : - *
+* *
+******************************************************************************/
+
+bool create_new_xml_file(xmlDocPtr *xdoc, xmlXPathContextPtr *context)
+{
+ *xdoc = xmlNewDoc(BAD_CAST "1.0");
+
+ if (*xdoc == NULL)
+ return false;
+
+ *context = xmlXPathNewContext(*xdoc);
+
+ if (*context == NULL)
+ {
+ xmlFreeDoc(*xdoc);
+ return false;
+ }
+
+ return true;
+
+}
+
+
+/******************************************************************************
+* *
+* Paramètres : xdoc = structure XML chargée. *
+* filename = nom du fichier à remplir. *
+* *
+* Description : Sauvegarde une structure XML dans un fichier. *
+* *
+* Retour : true si l'opération a pu s'effectuer, false sinon. *
+* *
+* Remarques : - *
+* *
+******************************************************************************/
+
+bool save_xml_file(xmlDocPtr xdoc, const char *filename)
+{
+ int ret; /* Bilan de l'appel */
+
+ ret = xmlSaveFormatFileEnc(filename, xdoc, "UTF-8", 1);
+
+ return (ret != -1);
+
+}
+
+
+/******************************************************************************
+* *
+* Paramètres : xdoc = structure XML chargée à supprimer. *
+* context = contexte utilisé pour les recherches. *
+* *
+* Description : Ferme une structure XML. *
+* *
+* Retour : - *
+* *
+* Remarques : - *
+* *
+******************************************************************************/
+
+void close_xml_file(xmlDocPtr xdoc, xmlXPathContextPtr context)
+{
+ xmlXPathFreeContext(context);
+ xmlFreeDoc(xdoc);
+
+ xmlCleanupParser();
+
+}
+
+
+
+/* ---------------------------------------------------------------------------------- */
+/* OPERATIONS DE LECTURE D'UN FICHIER XML */
+/* ---------------------------------------------------------------------------------- */
+
+
+/******************************************************************************
+* *
+* Paramètres : filename = nom du fichier à ouvrir. *
+* xdoc = structure XML chargée. [OUT] *
+* xpathCtx = contexte à utiliser pour les recherches. [OUT] *
+* *
+* Description : Ouvre un fichier XML de façon encadrée. *
+* *
+* Retour : true si l'opération a pu s'effectuer, false sinon. *
+* *
+* Remarques : - *
+* *
+******************************************************************************/
+
+gboolean open_xml_file(const char *filename, xmlDoc **xdoc, xmlXPathContextPtr *xpathCtx)
+{
+ *xdoc = xmlParseFile(filename);
+
+ if (*xdoc == NULL)
+ {
+ XML_LOG(stderr, "Can not parse the XML file '%s'\n", filename);
+ return FALSE;
+ }
+
+ *xpathCtx = xmlXPathNewContext(*xdoc);
+
+ if (*xpathCtx == NULL)
+ {
+ XML_LOG(stderr, "Unable to create new XPath context\n");
+ xmlFreeDoc(*xdoc);
+ return FALSE;
+ }
+
+ return TRUE;
+
+}
+
+
+/******************************************************************************
+* *
+* Paramètres : xpathCtx = contexte à utiliser pour les recherches. *
+* path = chemin d'accès au noeud visé. *
+* *
+* Description : Obtient de façon encadrée l'accès à un noeud défini. *
+* *
+* Retour : Adresse de l'accès trouvé. *
+* *
+* Remarques : - *
+* *
+******************************************************************************/
+
+xmlXPathObjectPtr get_node_xpath_object(xmlXPathContextPtr xpathCtx, const char *path)
+{
+ xmlXPathObjectPtr result; /* Noeud XML à renvoyer */
+
+ result = xmlXPathEvalExpression(BAD_CAST path, xpathCtx);
+
+ if (result == NULL)
+ {
+ XML_LOG(stderr, "Unable to evaluate xpath expression '%s'\n", path);
+ return NULL;
+ }
+
+ if (result->nodesetval == NULL)
+ {
+ XML_LOG(stderr, "Node '%s' not found\n", path);
+ xmlXPathFreeObject(result);
+ return NULL;
+ }
+
+ return result;
+
+}
+
+
+/******************************************************************************
+* *
+* Paramètres : node = noeud dont une propriété est à lire. *
+* *
+* Description : Obtient une valeur placée entre <...> et </...>. *
+* *
+* Retour : Valeur sous forme de chaîne de caractères ou NULL. *
+* *
+* Remarques : - *
+* *
+******************************************************************************/
+
+char *qck_get_node_text_value(xmlNodePtr node)
+{
+ char *result; /* Valeur en question renvoyée */
+
+ result = NULL;
+
+ if (node != NULL)
+ if (node->children != NULL)
+ if (node->children->content != NULL)
+ result = strdup((char *)node->children->content);
+
+ if (result == NULL) XML_LOG(stderr, "No text value for node '%s'\n", node->name);
+
+ return result;
+
+}
+
+
+/******************************************************************************
+* *
+* Paramètres : xpathCtx = contexte à utiliser pour les recherches. *
+* path = chemin d'accès au noeud visé. *
+* *
+* Description : Obtient une valeur placée entre <...> et </...>. *
+* *
+* Retour : Valeur sous forme de chaîne de caractères ou NULL. *
+* *
+* Remarques : - *
+* *
+******************************************************************************/
+
+char *get_node_text_value(xmlXPathContextPtr xpathCtx, const char *path)
+{
+ char *result; /* Valeur en question renvoyée */
+ xmlXPathObjectPtr xpathObj; /* Point de départ XML */
+
+ result = NULL;
+
+ xpathObj = get_node_xpath_object(xpathCtx, path);
+ if (xpathObj == NULL) return NULL;
+
+ if (xpathObj->nodesetval->nodeNr > 0)
+ result = qck_get_node_text_value(xpathObj->nodesetval->nodeTab[0]);
+
+ xmlXPathFreeObject(xpathObj);
+
+ return result;
+
+}
+
+
+/******************************************************************************
+* *
+* Paramètres : node = noeud dont une propriété est à lire. *
+* name = nom de la propriété à lire. *
+* *
+* Description : Obtient la valeur d'une propriété d'un élément. *
+* *
+* Retour : Valeur sous forme de chaîne de caractères ou NULL. *
+* *
+* Remarques : - *
+* *
+******************************************************************************/
+
+char *qck_get_node_prop_value(xmlNodePtr node, const char *name)
+{
+ char *result; /* Valeur en question renvoyée */
+ xmlAttrPtr attrib; /* Liste d'attributs présents */
+
+ result = NULL;
+
+ if (node == NULL) return NULL;
+
+ /* Lecture de la valeur */
+
+ for (attrib = node->properties; attrib != NULL && result == NULL; attrib = attrib->next)
+ if (xmlStrEqual(attrib->name, BAD_CAST name)) result = strdup((char *)attrib->children->content);
+
+ return result;
+
+}
+
+
+/******************************************************************************
+* *
+* Paramètres : xpathCtx = contexte à utiliser pour les recherches. *
+* path = chemin d'accès au noeud à traiter. *
+* name = nom de la propriété à lire. *
+* *
+* Description : Obtient la valeur d'une propriété d'un élément. *
+* *
+* Retour : Valeur sous forme de chaîne de caractères ou NULL. *
+* *
+* Remarques : - *
+* *
+******************************************************************************/
+
+char *get_node_prop_value(xmlXPathContextPtr xpathCtx, const char *path, const char *name)
+{
+ char *result; /* Valeur en question renvoyée */
+ xmlXPathObjectPtr xpathObj; /* Point de départ XML */
+
+ result = NULL;
+
+ xpathObj = get_node_xpath_object(xpathCtx, path);
+ if (xpathObj == NULL) return NULL;
+
+ if (xpathObj->nodesetval->nodeNr > 0)
+ result = qck_get_node_prop_value(xpathObj->nodesetval->nodeTab[0], name);
+
+ xmlXPathFreeObject(xpathObj);
+
+ return result;
+
+}
+
+
+/******************************************************************************
+* *
+* Paramètres : node = noeud de texte avec un lien avec le document XML. *
+* *
+* Description : Construit un chemin d'accès complet selon le fichier XML. *
+* *
+* Retour : Valeur à libérer de la mémoire après usage ou NULL. *
+* *
+* Remarques : - *
+* *
+******************************************************************************/
+
+char *qck_build_filename_with_doc_url(xmlNodePtr node)
+{
+ char *result; /* Construction à retourner */
+ char *text; /* Valeur du texte lu */
+ char *last; /* Point de remplacement */
+
+ result = NULL;
+
+ text = qck_get_node_text_value(node);
+
+ if (text != NULL)
+ {
+ result = (char *)calloc(xmlStrlen(node->doc->URL) + strlen(text) + 1, sizeof(char));
+
+ strcpy(result, (const char *)node->doc->URL);
+
+ last = strrchr(result, '/');
+ last++;
+
+ strcpy(last, text);
+ free(text);
+
+ }
+
+ return result;
+
+}
+
+
+/******************************************************************************
+* *
+* Paramètres : xpathCtx = contexte à utiliser pour les recherches. *
+* path = chemin d'accès au noeud à traiter. *
+* *
+* Description : Construit un chemin d'accès complet selon le fichier XML. *
+* *
+* Retour : Valeur sous forme de chaîne de caractères ou NULL. *
+* *
+* Remarques : - *
+* *
+******************************************************************************/
+
+char *build_filename_with_doc_url(xmlXPathContextPtr xpathCtx, const char *path)
+{
+ char *result; /* Valeur en question renvoyée */
+ xmlXPathObjectPtr xpathObj; /* Point de départ XML */
+
+ result = NULL;
+
+ xpathObj = get_node_xpath_object(xpathCtx, path);
+ if (xpathObj == NULL) return NULL;
+
+ if (xpathObj->nodesetval->nodeNr > 0)
+ result = qck_build_filename_with_doc_url(xpathObj->nodesetval->nodeTab[0]);
+
+ xmlXPathFreeObject(xpathObj);
+
+ return result;
+
+}
+
+
+
+/* ---------------------------------------------------------------------------------- */
+/* OPERATIONS D'ECRITURE D'UN FICHIER XML */
+/* ---------------------------------------------------------------------------------- */
+
+
+/******************************************************************************
+* *
+* Paramètres : filename = nom du fichier à ouvrir. *
+* *
+* Description : Amorce l'écriture d'un nouveau fichier XML. *
+* *
+* Retour : Rédacteur mis en place ou NULL en cas d'erreur. *
+* *
+* Remarques : - *
+* *
+******************************************************************************/
+
+xmlTextWriterPtr start_writing_xml_file(const char *filename)
+{
+ xmlTextWriterPtr result; /* Moyen à retourner */
+ int retval; /* Bilan d'une opération */
+
+ result = xmlNewTextWriterFilename(filename, 0);
+
+ if (result == NULL)
+ {
+ XML_LOG(stderr, "Error creating the xml writer\n");
+ return NULL;
+ }
+
+ retval = xmlTextWriterStartDocument(result, NULL, "UTF-8", "yes");
+ if (retval < 0)
+ {
+ XML_LOG(stderr, "Error at xmlTextWriterStartDocument\n");
+ xmlFreeTextWriter(result);
+ return NULL;
+ }
+
+ retval = xmlTextWriterSetIndent(result, 1);
+ if (retval < 0)
+ {
+ XML_LOG(stderr, "Error setting indentation\n");
+ xmlFreeTextWriter(result);
+ return NULL;
+ }
+
+ retval = xmlTextWriterSetIndentString(result, BAD_CAST "\t");
+ if (retval < 0)
+ {
+ XML_LOG(stderr, "Error setting indentation string\n");
+ xmlFreeTextWriter(result);
+ return NULL;
+ }
+
+ return result;
+
+}
+
+
+/******************************************************************************
+* *
+* Paramètres : writer = rédacteur dédié à l'écriture. *
+* *
+* Description : Met fin à l'écriture d'un nouveau fichier XML. *
+* *
+* Retour : Bilan de l'opération : true ou false. *
+* *
+* Remarques : Ferme au besoin toutes les balises encore ouvertes. *
+* *
+******************************************************************************/
+
+bool end_writing_xml_file(xmlTextWriterPtr writer)
+{
+ int retval; /* Bilan de l'opération */
+
+ retval = xmlTextWriterEndDocument(writer);
+ if (retval < 0)
+ {
+ XML_LOG(stderr, "Error at xmlTextWriterEndDocument\n");
+ return false;
+ }
+
+ xmlFreeTextWriter(writer);
+
+ return true;
+
+}
+
+
+/******************************************************************************
+* *
+* Paramètres : writer = rédacteur dédié à l'écriture. *
+* name = nom de la balise à écrire. *
+* *
+* Description : Ecrit une balise et ne la referme pas. *
+* *
+* Retour : Bilan de l'opération : true ou false. *
+* *
+* Remarques : - *
+* *
+******************************************************************************/
+
+bool open_xml_element(xmlTextWriterPtr writer, const char *name)
+{
+ int retval; /* Bilan de l'opération */
+
+ retval = xmlTextWriterStartElement(writer, BAD_CAST name);
+
+ if (retval < 0)
+ XML_LOG(stderr, "Error at xmlTextWriterWriteFormatElement\n");
+
+ return (retval >= 0);
+
+}
+
+
+/******************************************************************************
+* *
+* Paramètres : writer = rédacteur dédié à l'écriture. *
+* *
+* Description : Ferme la dernière balise laissée ouverte. *
+* *
+* Retour : Bilan de l'opération : true ou false. *
+* *
+* Remarques : - *
+* *
+******************************************************************************/
+
+bool close_xml_element(xmlTextWriterPtr writer)
+{
+ int retval; /* Bilan de l'opération */
+
+ retval = xmlTextWriterEndElement(writer);
+
+ if (retval < 0)
+ XML_LOG(stderr, "Error at xmlTextWriterWriteFormatElement\n");
+
+ return (retval >= 0);
+
+}
+
+
+/******************************************************************************
+* *
+* Paramètres : writer = rédacteur dédié à l'écriture. *
+* name = nom de la balise à écrire. *
+* format = format de la chaîne à traiter. *
+* ... = informations à inscrire. *
+* *
+* Description : Ecrit une balise avec un contenu textuel. *
+* *
+* Retour : Bilan de l'opération : true ou false. *
+* *
+* Remarques : - *
+* *
+******************************************************************************/
+
+bool write_xml_element_with_content(xmlTextWriterPtr writer, const char *name, const char *format, ...)
+{
+ va_list ap; /* Liste d'arguments variable */
+ int retval; /* Bilan de l'opération */
+
+ va_start(ap, format);
+
+ retval = xmlTextWriterWriteVFormatElement(writer, BAD_CAST name, format, ap);
+
+ if (retval < 0)
+ XML_LOG(stderr, "Error at xmlTextWriterWriteFormatElement\n");
+
+ va_end(ap);
+
+ return (retval >= 0);
+
+}
+
+
+/******************************************************************************
+* *
+* Paramètres : writer = rédacteur dédié à l'écriture. *
+* name = nom de l'attribut à écrire. *
+* format = format de la chaîne à traiter. *
+* ... = informations à inscrire. *
+* *
+* Description : Ecrit un attribut avec un contenu textuel. *
+* *
+* Retour : Bilan de l'opération : true ou false. *
+* *
+* Remarques : - *
+* *
+******************************************************************************/
+
+bool write_xml_attribute(xmlTextWriterPtr writer, const char *name, const char *format, ...)
+{
+ va_list ap; /* Liste d'arguments variable */
+ int retval; /* Bilan de l'opération */
+
+ va_start(ap, format);
+
+ retval = xmlTextWriterWriteVFormatAttribute(writer, BAD_CAST name, format, ap);
+
+ if (retval < 0)
+ XML_LOG(stderr, "Error at xmlTextWriterWriteFormatElement\n");
+
+ va_end(ap);
+
+ return (retval >= 0);
+
+}
+
+
+/******************************************************************************
+* *
+* Paramètres : writer = rédacteur dédié à l'écriture. *
+* format = format de la chaîne à traiter. *
+* ... = informations à inscrire. *
+* *
+* Description : Ecrit un contenu textuel. *
+* *
+* Retour : Bilan de l'opération : true ou false. *
+* *
+* Remarques : - *
+* *
+******************************************************************************/
+
+bool write_xml_content(xmlTextWriterPtr writer, const char *format, ...)
+{
+ va_list ap; /* Liste d'arguments variable */
+ int retval; /* Bilan de l'opération */
+
+ va_start(ap, format);
+
+ retval = xmlTextWriterWriteVFormatString(writer, format, ap);
+
+ if (retval < 0)
+ XML_LOG(stderr, "Error at xmlTextWriterWriteFormatElement\n");
+
+ va_end(ap);
+
+ return (retval >= 0);
+
+}
+
+
+
+/* ---------------------------------------------------------------------------------- */
+/* OPERATIONS D'ECRITURE D'UN FICHIER XML */
+/* ---------------------------------------------------------------------------------- */
+
+
+
+/******************************************************************************
+* *
+* Paramètres : context = contexte à utiliser pour les recherches. *
+* path = chemin d'accès au noeud visé. *
+* *
+* Description : Fournit le premier noeud correspondant à un chemin XPath. *
+* *
+* Retour : Adresse du noeud trouvé ou NULL en cas d'échec. *
+* *
+* Remarques : - *
+* *
+******************************************************************************/
+
+xmlNodePtr get_node_from_xpath(xmlXPathContextPtr context, const char *path)
+{
+ xmlNodePtr result; /* Noeud trouvé à renvoyer */
+ xmlXPathObjectPtr xobject; /* Point de départ XML */
+
+ result = NULL;
+
+ xobject = get_node_xpath_object(context, path);
+ if (xobject == NULL) return NULL;
+
+ if (xobject->nodesetval->nodeNr > 0)
+ result = xobject->nodesetval->nodeTab[0];
+
+ xmlXPathFreeObject(xobject);
+
+ return result;
+
+}
+
+
+/******************************************************************************
+* *
+* Paramètres : xdoc = structure XML chargée. *
+* context = contexte à utiliser pour les recherches. *
+* path = chemin d'accès au noeud visé. *
+* *
+* Description : S'assure qu'un noeud donné est bien présent dans le document.*
+* *
+* Retour : Noeud en question ou NULL en cas d'échec à la création. *
+* *
+* Remarques : - *
+* *
+******************************************************************************/
+
+xmlNodePtr ensure_node_exist(xmlDocPtr xdoc, xmlXPathContextPtr context, const char *path)
+{
+ xmlNodePtr result; /* Noeud à retourner */
+ char **levels; /* Niveaux dans le chemin */
+ size_t levels_count; /* Nombre de ces niveaux */
+ xmlNodePtr last; /* Dernier noeud valide */
+ size_t i; /* Boucle de parcours #1 */
+ char *iter_path; /* Chamin d'accès pour le test */
+ size_t j; /* Boucle de parcours #2 */
+ xmlNodePtr iter; /* Test d'accès à un noeud */
+ char *cond; /* Marque de condition ('[') */
+
+ result = get_node_from_xpath(context, path);
+
+ if (result == NULL)
+ {
+ levels = strtoka(path, "/", &levels_count);
+
+ /* Recherche la racine valide la plus haute */
+
+ last = xmlDocGetRootElement(xdoc);
+
+ for (i = 0; i < levels_count && last != NULL; i++)
+ {
+ iter_path = strdup("");
+
+ for (j = 0; j <= i; j++)
+ {
+ iter_path = stradd(iter_path, "/");
+ iter_path = stradd(iter_path, levels[j]);
+ }
+
+ iter = get_node_from_xpath(context, iter_path);
+
+ if (iter == NULL) break;
+ else last = iter;
+
+ }
+
+ /* Inscription des noeuds restants */
+
+ if (last == NULL)
+ {
+ last = xmlNewDocNode(xdoc, NULL, BAD_CAST levels[i++], NULL);
+ xmlDocSetRootElement(xdoc, last);
+
+ if (i == levels_count)
+ result = last;
+
+ }
+
+ for ( ; i < levels_count && last != NULL; i++)
+ {
+ cond = strchr(levels[i], '[');
+ if (cond != NULL) *cond = '\0';
+
+ result = xmlNewDocNode(xdoc, NULL, BAD_CAST levels[i], NULL);
+ result = xmlAddChild(last, result);
+ last = result;
+ }
+
+ /* Libération de la mémoire */
+
+ for (i = 0; i < levels_count; i++)
+ free(levels[i]);
+
+ free(levels);
+
+ }
+
+ return result;
+
+}
+
+
+/******************************************************************************
+* *
+* Paramètres : xdoc = structure XML chargée. *
+* context = contexte à utiliser pour les recherches. *
+* path = chemin d'accès au noeud visé. *
+* content = texte à inscrire en contenu. *
+* *
+* Description : S'assure qu'un noeud donné est bien présent dans le document.*
+* *
+* Retour : true en cas de succès, false sinon. *
+* *
+* Remarques : - *
+* *
+******************************************************************************/
+
+bool add_content_to_node(xmlDocPtr xdoc, xmlXPathContextPtr context, const char *path, const char *content)
+{
+ xmlNodePtr node; /* Noeud à modifier */
+
+ if (content == NULL) return true;
+
+ node = ensure_node_exist(xdoc, context, path);
+ if (node == NULL) return false;
+
+ xmlNodeSetContent(node, content);
+
+ return true;
+
+}