QOF  0.8.0
qsf-xml.c
00001 /***************************************************************************
00002  *            qsf-xml.c
00003  *
00004  *  Fri Nov 26 19:29:47 2004
00005  *  Copyright  2004,2005,2006  Neil Williams  <linux@codehelp.co.uk>
00006  *
00007  ****************************************************************************/
00008 /*
00009  *  This program is free software; you can redistribute it and/or modify
00010  *  it under the terms of the GNU General Public License as published by
00011  *  the Free Software Foundation; either version 2 of the License, or
00012  *  (at your option) any later version.
00013  *
00014  *  This program is distributed in the hope that it will be useful,
00015  *  but WITHOUT ANY WARRANTY; without even the implied warranty of
00016  *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
00017  *  GNU General Public License for more details.
00018  *
00019  *  You should have received a copy of the GNU General Public License
00020  *  along with this program; if not, write to the Free Software
00021  *  Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
00022  */
00023 
00024 #include "config.h"
00025 #include <glib.h>
00026 #include <libxml/xmlversion.h>
00027 #include <libxml/xmlmemory.h>
00028 #include <libxml/tree.h>
00029 #include <libxml/parser.h>
00030 #include <libxml/xmlschemas.h>
00031 #include "qof.h"
00032 #include "qof-backend-qsf.h"
00033 #include "qsf-xml.h"
00034 
00035 static QofLogModule log_module = QOF_MOD_QSF;
00036 
00037 gint
00038 qsf_compare_tag_strings (const xmlChar * node_name, gchar * tag_name)
00039 {
00040     return xmlStrcmp (node_name, (const xmlChar *) tag_name);
00041 }
00042 
00043 gint
00044 qsf_strings_equal (const xmlChar * node_name, gchar * tag_name)
00045 {
00046     if (0 == qsf_compare_tag_strings (node_name, tag_name))
00047     {
00048         return 1;
00049     }
00050     return 0;
00051 }
00052 
00053 gint
00054 qsf_is_element (xmlNodePtr a, xmlNsPtr ns, gchar * c)
00055 {
00056     g_return_val_if_fail (a != NULL, 0);
00057     g_return_val_if_fail (ns != NULL, 0);
00058     g_return_val_if_fail (c != NULL, 0);
00059     if ((a->ns == ns) && (a->type == XML_ELEMENT_NODE) &&
00060         qsf_strings_equal (a->name, c))
00061     {
00062         return 1;
00063     }
00064     return 0;
00065 }
00066 
00067 gint
00068 qsf_check_tag (QsfParam * params, gchar * qof_type)
00069 {
00070     return qsf_is_element (params->child_node, params->qsf_ns, qof_type);
00071 }
00072 
00073 gboolean
00074 qsf_is_valid (const gchar * schema_dir, const gchar * schema_filename,
00075     xmlDocPtr doc)
00076 {
00077     xmlSchemaParserCtxtPtr qsf_schema_file;
00078     xmlSchemaPtr qsf_schema;
00079     xmlSchemaValidCtxtPtr qsf_context;
00080     gchar *schema_path;
00081     gint result;
00082 
00083     g_return_val_if_fail (doc || schema_filename, FALSE);
00084     schema_path = g_strdup_printf ("%s/%s", schema_dir, schema_filename);
00085     qsf_schema_file = xmlSchemaNewParserCtxt (schema_path);
00086     qsf_schema = xmlSchemaParse (qsf_schema_file);
00087     qsf_context = xmlSchemaNewValidCtxt (qsf_schema);
00088     result = xmlSchemaValidateDoc (qsf_context, doc);
00089     xmlSchemaFreeParserCtxt (qsf_schema_file);
00090     xmlSchemaFreeValidCtxt (qsf_context);
00091     xmlSchemaFree (qsf_schema);
00092     g_free (schema_path);
00093     if (result == 0)
00094     {
00095         return TRUE;
00096     }
00097     return FALSE;
00098 }
00099 
00100 void
00101 qsf_valid_foreach (xmlNodePtr parent, QsfValidCB cb,
00102     struct QsfNodeIterate *qsfiter, QsfValidator * valid)
00103 {
00104     xmlNodePtr cur_node;
00105 
00106     qsfiter->v_fcn = &cb;
00107     for (cur_node = parent->children; cur_node != NULL;
00108         cur_node = cur_node->next)
00109     {
00110         cb (cur_node, qsfiter->ns, valid);
00111     }
00112 }
00113 
00114 void
00115 qsf_node_foreach (xmlNodePtr parent, QsfNodeCB cb,
00116     struct QsfNodeIterate *qsfiter, QsfParam * params)
00117 {
00118     xmlNodePtr cur_node;
00119 
00120     if (!parent)
00121         return;
00122     g_return_if_fail (params);
00123     g_return_if_fail (qsfiter->ns);
00124     qsfiter->fcn = &cb;
00125     for (cur_node = parent->children; cur_node != NULL;
00126         cur_node = cur_node->next)
00127     {
00128         cb (cur_node, qsfiter->ns, params);
00129     }
00130 }
00131 
00132 void
00133 qsf_object_validation_handler (xmlNodePtr child, xmlNsPtr ns,
00134     QsfValidator * valid)
00135 {
00136     xmlNodePtr cur_node;
00137     xmlChar *object_declaration;
00138     guint count;
00139     QsfStatus type;
00140     gboolean is_registered;
00141 
00142     count = 0;
00143     type = QSF_NO_OBJECT;
00144     is_registered = FALSE;
00145     for (cur_node = child->children; cur_node != NULL;
00146         cur_node = cur_node->next)
00147     {
00148         if (qsf_is_element (cur_node, ns, QSF_OBJECT_TAG))
00149         {
00150             object_declaration =
00151                 xmlGetProp (cur_node, BAD_CAST QSF_OBJECT_TYPE);
00152             is_registered = qof_class_is_registered (object_declaration);
00153             if (is_registered)
00154             {
00155                 type = QSF_REGISTERED_OBJECT;
00156             }
00157             else
00158             {
00159                 type = QSF_DEFINED_OBJECT;
00160             }
00161             xmlFree (object_declaration);
00162             count = g_hash_table_size (valid->object_table);
00163             g_hash_table_insert (valid->object_table, object_declaration,
00164                 GINT_TO_POINTER (type));
00165             /* if insert was successful - i.e. object is unique so far */
00166             if (g_hash_table_size (valid->object_table) > count)
00167             {
00168                 valid->valid_object_count++;
00169                 if (is_registered)
00170                 {
00171                     valid->qof_registered_count++;
00172                 }
00173             }
00174         }
00175     }
00176 }
00177 
00178 gboolean
00179 is_our_qsf_object (const gchar * path)
00180 {
00181     xmlDocPtr doc;
00182     struct QsfNodeIterate qsfiter;
00183     xmlNodePtr object_root;
00184     QsfValidator valid;
00185     gint table_count;
00186 
00187     g_return_val_if_fail ((path != NULL), FALSE);
00188     doc = xmlParseFile (path);
00189     if (doc == NULL)
00190     {
00191         return FALSE;
00192     }
00193     if (TRUE != qsf_is_valid (QSF_SCHEMA_DIR, QSF_OBJECT_SCHEMA, doc))
00194     {
00195         PINFO (" validation failed %s %s %s", QSF_SCHEMA_DIR,
00196             QSF_OBJECT_SCHEMA, path);
00197         return FALSE;
00198     }
00199     object_root = xmlDocGetRootElement (doc);
00200     /* check that all objects in the file are already registered in QOF */
00201     valid.object_table = g_hash_table_new (g_str_hash, g_str_equal);
00202     valid.qof_registered_count = 0;
00203     valid.valid_object_count = 0;
00204     qsfiter.ns = object_root->ns;
00205     qsf_valid_foreach (object_root, qsf_object_validation_handler, 
00206         &qsfiter, &valid);
00207     table_count = g_hash_table_size (valid.object_table);
00208     g_hash_table_destroy (valid.object_table);
00209     xmlFreeDoc (doc);
00210     if (table_count == valid.qof_registered_count)
00211     {
00212         return TRUE;
00213     }
00214     return FALSE;
00215 }
00216 
00217 gboolean
00218 is_qsf_object (const gchar * path)
00219 {
00220     xmlDocPtr doc;
00221 
00222     g_return_val_if_fail ((path != NULL), FALSE);
00223     if (path == NULL)
00224     {
00225         return FALSE;
00226     }
00227     doc = xmlParseFile (path);
00228     if (doc == NULL)
00229     {
00230         return FALSE;
00231     }
00232     if (TRUE != qsf_is_valid (QSF_SCHEMA_DIR, QSF_OBJECT_SCHEMA, doc))
00233     {
00234         return FALSE;
00235     }
00236     /* Note cannot test against a map here, so if the file is valid QSF,
00237        accept it and work out the details later. */
00238     return TRUE;
00239 }
00240 
00241 gboolean
00242 is_our_qsf_object_be (QsfParam * params)
00243 {
00244     xmlDocPtr doc;
00245     struct QsfNodeIterate qsfiter;
00246     xmlNodePtr object_root;
00247     QsfValidator valid;
00248     gint table_count;
00249 
00250     g_return_val_if_fail ((params != NULL), FALSE);
00251     if (params->filepath == NULL)
00252     {
00253         qof_error_set_be (params->be, qof_error_register
00254         (_("The QSF XML file '%s' could not be found."), TRUE));
00255         return FALSE;
00256     }
00257     if (params->file_type != QSF_UNDEF)
00258     {
00259         return FALSE;
00260     }
00261     doc = xmlParseFile (params->filepath);
00262     if (doc == NULL)
00263     {
00264         qof_error_set_be (params->be, qof_error_register
00265         (_("There was an error parsing the file '%s'."), TRUE));
00266         return FALSE;
00267     }
00268     if (TRUE != qsf_is_valid (QSF_SCHEMA_DIR, QSF_OBJECT_SCHEMA, doc))
00269     {
00270         qof_error_set_be (params->be, qof_error_register
00271         (_("Invalid QSF Object file! The QSF object file '%s' "
00272         " failed to validate  against the QSF object schema. "
00273         "The XML structure of the file is either not well-formed "
00274         "or the file contains illegal data."), TRUE));
00275         xmlFreeDoc (doc);
00276         return FALSE;
00277     }
00278     params->file_type = IS_QSF_OBJ;
00279     object_root = xmlDocGetRootElement (doc);
00280     xmlFreeDoc (doc);
00281     valid.object_table = g_hash_table_new (g_str_hash, g_str_equal);
00282     valid.qof_registered_count = 0;
00283     qsfiter.ns = object_root->ns;
00284     qsf_valid_foreach (object_root, qsf_object_validation_handler, 
00285         &qsfiter, &valid);
00286     table_count = g_hash_table_size (valid.object_table);
00287     if (table_count == valid.qof_registered_count)
00288     {
00289         g_hash_table_destroy (valid.object_table);
00290         return TRUE;
00291     }
00292     g_hash_table_destroy (valid.object_table);
00293     qof_error_set_be (params->be, params->err_nomap);
00294     return FALSE;
00295 }
00296 
00297 gboolean
00298 is_qsf_object_be (QsfParam * params)
00299 {
00300     gboolean result;
00301     xmlDocPtr doc;
00302     GList *maps;
00303     gchar *path;
00304 
00305     g_return_val_if_fail ((params != NULL), FALSE);
00306     path = g_strdup (params->filepath);
00307     if (path == NULL)
00308     {
00309         qof_error_set_be (params->be, qof_error_register
00310         (_("The QSF XML file '%s' could not be found."), TRUE));
00311         return FALSE;
00312     }
00313     /* skip validation if is_our_qsf_object has already been called. */
00314 /*  if (ERR_QSF_INVALID_OBJ == qof_backend_get_error (params->be))
00315     {
00316         return FALSE;
00317     }*/
00318     if (params->file_type == QSF_UNDEF)
00319     {
00320         doc = xmlParseFile (path);
00321         if (doc == NULL)
00322         {
00323             qof_error_set_be (params->be, qof_error_register
00324             (_("There was an error parsing the file '%s'."), TRUE));
00325             return FALSE;
00326         }
00327         if (TRUE != qsf_is_valid (QSF_SCHEMA_DIR, QSF_OBJECT_SCHEMA, doc))
00328         {
00329             qof_error_set_be (params->be, qof_error_register
00330             (_("Invalid QSF Object file! The QSF object file '%s' "
00331             " failed to validate  against the QSF object schema. "
00332             "The XML structure of the file is either not well-formed "
00333             "or the file contains illegal data."), TRUE));
00334             return FALSE;
00335         }
00336     }
00337     result = FALSE;
00338     /* retrieve list of maps from config frame. */
00339     for (maps = params->map_files; maps; maps = maps->next)
00340     {
00341         QofErrorId err;
00342         result = is_qsf_object_with_map_be (maps->data, params);
00343         err = qof_error_check_be (params->be);
00344         if ((err == QOF_SUCCESS) && result)
00345         {
00346             params->map_path = maps->data;
00347             PINFO ("map chosen = %s", params->map_path);
00348             break;
00349         }
00350     }
00351     return result;
00352 }
00353 
00354 static void
00355 qsf_supported_data_types (gpointer type, gpointer user_data)
00356 {
00357     QsfParam *params;
00358 
00359     g_return_if_fail (user_data != NULL);
00360     g_return_if_fail (type != NULL);
00361     params = (QsfParam *) user_data;
00362     if (qsf_is_element (params->param_node, params->qsf_ns,
00363             (gchar *) type))
00364     {
00365         g_hash_table_insert (params->qsf_parameter_hash,
00366             xmlGetProp (params->param_node,
00367                 BAD_CAST QSF_OBJECT_TYPE), params->param_node);
00368     }
00369 }
00370 
00371 static void
00372 qsf_parameter_handler (xmlNodePtr child, xmlNsPtr qsf_ns,
00373     QsfParam * params)
00374 {
00375     /* spurious */
00376     if (!qsf_ns)
00377         return;
00378     params->param_node = child;
00379     g_slist_foreach (params->supported_types, qsf_supported_data_types,
00380         params);
00381 }
00382 
00383 void
00384 qsf_object_node_handler (xmlNodePtr child, xmlNsPtr qsf_ns,
00385     QsfParam * params)
00386 {
00387     struct QsfNodeIterate qsfiter;
00388     QsfObject *object_set;
00389     gchar *tail, *object_count_s;
00390     gint64 c;
00391 
00392     g_return_if_fail (child != NULL);
00393     g_return_if_fail (qsf_ns != NULL);
00394     params->qsf_ns = qsf_ns;
00395     if (qsf_is_element (child, qsf_ns, QSF_OBJECT_TAG))
00396     {
00397         params->qsf_parameter_hash = NULL;
00398         c = 0;
00399         object_set = g_new (QsfObject, 1);
00400         params->object_set = object_set;
00401         object_set->object_count = 0;
00402         object_set->parameters =
00403             g_hash_table_new (g_str_hash, g_str_equal);
00404         object_set->object_type = ((gchar *) xmlGetProp (child,
00405                 BAD_CAST QSF_OBJECT_TYPE));
00406         object_count_s = ((gchar *) xmlGetProp (child,
00407                 BAD_CAST QSF_OBJECT_COUNT));
00408         if (object_count_s)
00409         {
00410             c = (gint64) strtol (object_count_s, &tail, 0);
00411             object_set->object_count = (gint) c;
00412             g_free (object_count_s);
00413         }
00414         params->qsf_object_list =
00415             g_list_prepend (params->qsf_object_list, object_set);
00416         qsfiter.ns = qsf_ns;
00417         params->qsf_parameter_hash = object_set->parameters;
00418         qsf_node_foreach (child, qsf_parameter_handler, &qsfiter, params);
00419     }
00420 }
00421 
00422 void
00423 qsf_book_node_handler (xmlNodePtr child, xmlNsPtr ns, QsfParam * params)
00424 {
00425     gchar *book_count_s, *tail;
00426     gint book_count;
00427     xmlNodePtr child_node;
00428     struct QsfNodeIterate qsfiter;
00429     gchar *buffer;
00430     GUID book_guid;
00431 
00432     g_return_if_fail (child);
00433     g_return_if_fail (params);
00434     ENTER (" child=%s", child->name);
00435     if (qsf_is_element (child, ns, QSF_BOOK_TAG))
00436     {
00437         book_count_s =
00438             (gchar *) xmlGetProp (child, BAD_CAST QSF_BOOK_COUNT);
00439         if (book_count_s)
00440         {
00441             book_count = (gint) strtol (book_count_s, &tail, 0);
00442             /* More than one book not currently supported. */
00443             g_free (book_count_s);
00444             g_return_if_fail (book_count == 1);
00445         }
00446         qsfiter.ns = ns;
00447         child_node = child->children->next;
00448         if (qsf_is_element (child_node, ns, QSF_BOOK_GUID))
00449         {
00450             DEBUG (" trying to set book GUID");
00451             buffer = BAD_CAST xmlNodeGetContent (child_node);
00452             g_return_if_fail (TRUE == string_to_guid (buffer, &book_guid));
00453             qof_entity_set_guid ((QofEntity *) params->book, &book_guid);
00454             xmlNewChild (params->output_node, params->qsf_ns,
00455                 BAD_CAST QSF_BOOK_GUID, BAD_CAST buffer);
00456             xmlFree (buffer);
00457         }
00458         qsf_node_foreach (child, qsf_object_node_handler, &qsfiter, params);
00459     }
00460     LEAVE (" ");
00461 }