QOF 0.8.4
|
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 ((gchar*)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 = (gchar*) 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 }