QOF  0.7.5
qsf-xml.c
1 /***************************************************************************
2  * qsf-xml.c
3  *
4  * Fri Nov 26 19:29:47 2004
5  * Copyright 2004,2005,2006 Neil Williams <linux@codehelp.co.uk>
6  *
7  ****************************************************************************/
8 /*
9  * This program is free software; you can redistribute it and/or modify
10  * it under the terms of the GNU General Public License as published by
11  * the Free Software Foundation; either version 2 of the License, or
12  * (at your option) any later version.
13  *
14  * This program is distributed in the hope that it will be useful,
15  * but WITHOUT ANY WARRANTY; without even the implied warranty of
16  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
17  * GNU General Public License for more details.
18  *
19  * You should have received a copy of the GNU General Public License
20  * along with this program; if not, write to the Free Software
21  * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
22  */
23 
24 #include "config.h"
25 #include <glib.h>
26 #include <libxml/xmlversion.h>
27 #include <libxml/xmlmemory.h>
28 #include <libxml/tree.h>
29 #include <libxml/parser.h>
30 #include <libxml/xmlschemas.h>
31 #include "qof.h"
32 #include "qof-backend-qsf.h"
33 #include "qsf-dir.h"
34 #include "qsf-xml.h"
35 
36 static QofLogModule log_module = QOF_MOD_QSF;
37 
38 gint
39 qsf_compare_tag_strings (const xmlChar * node_name, gchar * tag_name)
40 {
41  return xmlStrcmp (node_name, (const xmlChar *) tag_name);
42 }
43 
44 gint
45 qsf_strings_equal (const xmlChar * node_name, gchar * tag_name)
46 {
47  if (0 == qsf_compare_tag_strings (node_name, tag_name))
48  {
49  return 1;
50  }
51  return 0;
52 }
53 
54 gint
55 qsf_is_element (xmlNodePtr a, xmlNsPtr ns, gchar * c)
56 {
57  g_return_val_if_fail (a != NULL, 0);
58  g_return_val_if_fail (ns != NULL, 0);
59  g_return_val_if_fail (c != NULL, 0);
60  if ((a->ns == ns) && (a->type == XML_ELEMENT_NODE) &&
61  qsf_strings_equal (a->name, c))
62  {
63  return 1;
64  }
65  return 0;
66 }
67 
68 gint
69 qsf_check_tag (QsfParam * params, gchar * qof_type)
70 {
71  return qsf_is_element (params->child_node, params->qsf_ns, qof_type);
72 }
73 
74 gboolean
75 qsf_is_valid (const gchar * schema_dir, const gchar * schema_filename,
76  xmlDocPtr doc)
77 {
78  xmlSchemaParserCtxtPtr qsf_schema_file;
79  xmlSchemaPtr qsf_schema;
80  xmlSchemaValidCtxtPtr qsf_context;
81  gchar *schema_path;
82  gint result;
83 
84  g_return_val_if_fail (doc || schema_filename, FALSE);
85  schema_path = g_strdup_printf ("%s/%s", schema_dir, schema_filename);
86  qsf_schema_file = xmlSchemaNewParserCtxt (schema_path);
87  qsf_schema = xmlSchemaParse (qsf_schema_file);
88  qsf_context = xmlSchemaNewValidCtxt (qsf_schema);
89  result = xmlSchemaValidateDoc (qsf_context, doc);
90  xmlSchemaFreeParserCtxt (qsf_schema_file);
91  xmlSchemaFreeValidCtxt (qsf_context);
92  xmlSchemaFree (qsf_schema);
93  g_free (schema_path);
94  if (result == 0)
95  {
96  return TRUE;
97  }
98  return FALSE;
99 }
100 
101 void
102 qsf_valid_foreach (xmlNodePtr parent, QsfValidCB cb,
103  struct QsfNodeIterate *qsfiter, QsfValidator * valid)
104 {
105  xmlNodePtr cur_node;
106 
107  qsfiter->v_fcn = &cb;
108  for (cur_node = parent->children; cur_node != NULL;
109  cur_node = cur_node->next)
110  {
111  cb (cur_node, qsfiter->ns, valid);
112  }
113 }
114 
115 void
116 qsf_node_foreach (xmlNodePtr parent, QsfNodeCB cb,
117  struct QsfNodeIterate *qsfiter, QsfParam * params)
118 {
119  xmlNodePtr cur_node;
120 
121  if (!parent)
122  return;
123  g_return_if_fail (params);
124  g_return_if_fail (qsfiter->ns);
125  qsfiter->fcn = &cb;
126  for (cur_node = parent->children; cur_node != NULL;
127  cur_node = cur_node->next)
128  {
129  cb (cur_node, qsfiter->ns, params);
130  }
131 }
132 
133 void
134 qsf_object_validation_handler (xmlNodePtr child, xmlNsPtr ns,
135  QsfValidator * valid)
136 {
137  xmlNodePtr cur_node;
138  xmlChar *object_declaration;
139  guint count;
140  QsfStatus type;
141  gboolean is_registered;
142 
143  count = 0;
144  type = QSF_NO_OBJECT;
145  is_registered = FALSE;
146  for (cur_node = child->children; cur_node != NULL;
147  cur_node = cur_node->next)
148  {
149  if (qsf_is_element (cur_node, ns, QSF_OBJECT_TAG))
150  {
151  object_declaration =
152  xmlGetProp (cur_node, BAD_CAST QSF_OBJECT_TYPE);
153  is_registered = qof_class_is_registered (object_declaration);
154  if (is_registered)
155  {
156  type = QSF_REGISTERED_OBJECT;
157  }
158  else
159  {
160  type = QSF_DEFINED_OBJECT;
161  }
162  xmlFree (object_declaration);
163  count = g_hash_table_size (valid->object_table);
164  g_hash_table_insert (valid->object_table, object_declaration,
165  GINT_TO_POINTER (type));
166  /* if insert was successful - i.e. object is unique so far */
167  if (g_hash_table_size (valid->object_table) > count)
168  {
169  valid->valid_object_count++;
170  if (is_registered)
171  {
172  valid->qof_registered_count++;
173  }
174  }
175  }
176  }
177 }
178 
179 gboolean
180 is_our_qsf_object (const gchar * path)
181 {
182  xmlDocPtr doc;
183  struct QsfNodeIterate qsfiter;
184  xmlNodePtr object_root;
185  QsfValidator valid;
186  gint table_count;
187 
188  g_return_val_if_fail ((path != NULL), FALSE);
189  doc = xmlParseFile (path);
190  if (doc == NULL)
191  {
192  return FALSE;
193  }
194  if (TRUE != qsf_is_valid (QSF_SCHEMA_DIR, QSF_OBJECT_SCHEMA, doc))
195  {
196  PINFO (" validation failed %s %s %s", QSF_SCHEMA_DIR,
197  QSF_OBJECT_SCHEMA, path);
198  return FALSE;
199  }
200  object_root = xmlDocGetRootElement (doc);
201  /* check that all objects in the file are already registered in QOF */
202  valid.object_table = g_hash_table_new (g_str_hash, g_str_equal);
203  valid.qof_registered_count = 0;
204  valid.valid_object_count = 0;
205  qsfiter.ns = object_root->ns;
206  qsf_valid_foreach (object_root, qsf_object_validation_handler,
207  &qsfiter, &valid);
208  table_count = g_hash_table_size (valid.object_table);
209  g_hash_table_destroy (valid.object_table);
210  xmlFreeDoc (doc);
211  if (table_count == valid.qof_registered_count)
212  {
213  return TRUE;
214  }
215  return FALSE;
216 }
217 
218 gboolean
219 is_qsf_object (const gchar * path)
220 {
221  xmlDocPtr doc;
222 
223  g_return_val_if_fail ((path != NULL), FALSE);
224  if (path == NULL)
225  {
226  return FALSE;
227  }
228  doc = xmlParseFile (path);
229  if (doc == NULL)
230  {
231  return FALSE;
232  }
233  if (TRUE != qsf_is_valid (QSF_SCHEMA_DIR, QSF_OBJECT_SCHEMA, doc))
234  {
235  return FALSE;
236  }
237  /* Note cannot test against a map here, so if the file is valid QSF,
238  accept it and work out the details later. */
239  return TRUE;
240 }
241 
242 gboolean
243 is_our_qsf_object_be (QsfParam * params)
244 {
245  xmlDocPtr doc;
246  struct QsfNodeIterate qsfiter;
247  xmlNodePtr object_root;
248  QsfValidator valid;
249  gint table_count;
250 
251  g_return_val_if_fail ((params != NULL), FALSE);
252  if (params->filepath == NULL)
253  {
254  qof_error_set_be (params->be, qof_error_register
255  (_("The QSF XML file '%s' could not be found."), TRUE));
256  return FALSE;
257  }
258  if (params->file_type != QSF_UNDEF)
259  {
260  return FALSE;
261  }
262  doc = xmlParseFile (params->filepath);
263  if (doc == NULL)
264  {
265  qof_error_set_be (params->be, qof_error_register
266  (_("There was an error parsing the file '%s'."), TRUE));
267  return FALSE;
268  }
269  if (TRUE != qsf_is_valid (QSF_SCHEMA_DIR, QSF_OBJECT_SCHEMA, doc))
270  {
271  qof_error_set_be (params->be, qof_error_register
272  (_("Invalid QSF Object file! The QSF object file '%s' "
273  " failed to validate against the QSF object schema. "
274  "The XML structure of the file is either not well-formed "
275  "or the file contains illegal data."), TRUE));
276  xmlFreeDoc (doc);
277  return FALSE;
278  }
279  params->file_type = IS_QSF_OBJ;
280  object_root = xmlDocGetRootElement (doc);
281  xmlFreeDoc (doc);
282  valid.object_table = g_hash_table_new (g_str_hash, g_str_equal);
283  valid.qof_registered_count = 0;
284  qsfiter.ns = object_root->ns;
285  qsf_valid_foreach (object_root, qsf_object_validation_handler,
286  &qsfiter, &valid);
287  table_count = g_hash_table_size (valid.object_table);
288  if (table_count == valid.qof_registered_count)
289  {
290  g_hash_table_destroy (valid.object_table);
291  return TRUE;
292  }
293  g_hash_table_destroy (valid.object_table);
294  qof_error_set_be (params->be, params->err_nomap);
295  return FALSE;
296 }
297 
298 gboolean
299 is_qsf_object_be (QsfParam * params)
300 {
301  gboolean result;
302  xmlDocPtr doc;
303  GList *maps;
304  gchar *path;
305 
306  g_return_val_if_fail ((params != NULL), FALSE);
307  path = g_strdup (params->filepath);
308  if (path == NULL)
309  {
310  qof_error_set_be (params->be, qof_error_register
311  (_("The QSF XML file '%s' could not be found."), TRUE));
312  return FALSE;
313  }
314  /* skip validation if is_our_qsf_object has already been called. */
315 /* if (ERR_QSF_INVALID_OBJ == qof_backend_get_error (params->be))
316  {
317  return FALSE;
318  }*/
319  if (params->file_type == QSF_UNDEF)
320  {
321  doc = xmlParseFile (path);
322  if (doc == NULL)
323  {
324  qof_error_set_be (params->be, qof_error_register
325  (_("There was an error parsing the file '%s'."), TRUE));
326  return FALSE;
327  }
328  if (TRUE != qsf_is_valid (QSF_SCHEMA_DIR, QSF_OBJECT_SCHEMA, doc))
329  {
330  qof_error_set_be (params->be, qof_error_register
331  (_("Invalid QSF Object file! The QSF object file '%s' "
332  " failed to validate against the QSF object schema. "
333  "The XML structure of the file is either not well-formed "
334  "or the file contains illegal data."), TRUE));
335  return FALSE;
336  }
337  }
338  result = FALSE;
339  /* retrieve list of maps from config frame. */
340  for (maps = params->map_files; maps; maps = maps->next)
341  {
342  QofErrorId err;
343  result = is_qsf_object_with_map_be (maps->data, params);
344  err = qof_error_check_be (params->be);
345  if ((err == QOF_SUCCESS) && result)
346  {
347  params->map_path = maps->data;
348  PINFO ("map chosen = %s", params->map_path);
349  break;
350  }
351  }
352  return result;
353 }
354 
355 static void
356 qsf_supported_data_types (gpointer type, gpointer user_data)
357 {
358  QsfParam *params;
359 
360  g_return_if_fail (user_data != NULL);
361  g_return_if_fail (type != NULL);
362  params = (QsfParam *) user_data;
363  if (qsf_is_element (params->param_node, params->qsf_ns,
364  (gchar *) type))
365  {
366  g_hash_table_insert (params->qsf_parameter_hash,
367  xmlGetProp (params->param_node,
368  BAD_CAST QSF_OBJECT_TYPE), params->param_node);
369  }
370 }
371 
372 static void
373 qsf_parameter_handler (xmlNodePtr child, xmlNsPtr qsf_ns,
374  QsfParam * params)
375 {
376  /* spurious */
377  if (!qsf_ns)
378  return;
379  params->param_node = child;
380  g_slist_foreach (params->supported_types, qsf_supported_data_types,
381  params);
382 }
383 
384 void
385 qsf_object_node_handler (xmlNodePtr child, xmlNsPtr qsf_ns,
386  QsfParam * params)
387 {
388  struct QsfNodeIterate qsfiter;
389  QsfObject *object_set;
390  gchar *tail, *object_count_s;
391  gint64 c;
392 
393  g_return_if_fail (child != NULL);
394  g_return_if_fail (qsf_ns != NULL);
395  params->qsf_ns = qsf_ns;
396  if (qsf_is_element (child, qsf_ns, QSF_OBJECT_TAG))
397  {
398  params->qsf_parameter_hash = NULL;
399  c = 0;
400  object_set = g_new (QsfObject, 1);
401  params->object_set = object_set;
402  object_set->object_count = 0;
403  object_set->parameters =
404  g_hash_table_new (g_str_hash, g_str_equal);
405  object_set->object_type = ((gchar *) xmlGetProp (child,
406  BAD_CAST QSF_OBJECT_TYPE));
407  object_count_s = ((gchar *) xmlGetProp (child,
408  BAD_CAST QSF_OBJECT_COUNT));
409  if (object_count_s)
410  {
411  c = (gint64) strtol (object_count_s, &tail, 0);
412  object_set->object_count = (gint) c;
413  g_free (object_count_s);
414  }
415  params->qsf_object_list =
416  g_list_prepend (params->qsf_object_list, object_set);
417  qsfiter.ns = qsf_ns;
418  params->qsf_parameter_hash = object_set->parameters;
419  qsf_node_foreach (child, qsf_parameter_handler, &qsfiter, params);
420  }
421 }
422 
423 void
424 qsf_book_node_handler (xmlNodePtr child, xmlNsPtr ns, QsfParam * params)
425 {
426  gchar *book_count_s, *tail;
427  gint book_count;
428  xmlNodePtr child_node;
429  struct QsfNodeIterate qsfiter;
430  gchar *buffer;
431  GUID book_guid;
432 
433  g_return_if_fail (child);
434  g_return_if_fail (params);
435  ENTER (" child=%s", child->name);
436  if (qsf_is_element (child, ns, QSF_BOOK_TAG))
437  {
438  book_count_s =
439  (gchar *) xmlGetProp (child, BAD_CAST QSF_BOOK_COUNT);
440  if (book_count_s)
441  {
442  book_count = (gint) strtol (book_count_s, &tail, 0);
443  /* More than one book not currently supported. */
444  g_free (book_count_s);
445  g_return_if_fail (book_count == 1);
446  }
447  qsfiter.ns = ns;
448  child_node = child->children->next;
449  if (qsf_is_element (child_node, ns, QSF_BOOK_GUID))
450  {
451  DEBUG (" trying to set book GUID");
452  buffer = BAD_CAST xmlNodeGetContent (child_node);
453  g_return_if_fail (TRUE == string_to_guid (buffer, &book_guid));
454  qof_entity_set_guid ((QofEntity *) params->book, &book_guid);
455  xmlNewChild (params->output_node, params->qsf_ns,
456  BAD_CAST QSF_BOOK_GUID, BAD_CAST buffer);
457  xmlFree (buffer);
458  }
459  qsf_node_foreach (child, qsf_object_node_handler, &qsfiter, params);
460  }
461  LEAVE (" ");
462 }