QOF  0.7.5
qsf-backend.c
1 /*******************************************************************
2  * qsf-backend.c
3  *
4  * Sat Jan 1 15:07:14 2005
5  * Copyright 2005, 2006 Neil Williams
6  * linux@codehelp.co.uk
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 <errno.h>
26 #include <sys/stat.h>
27 #include <glib.h>
28 #include <libxml/xmlmemory.h>
29 #include <libxml/tree.h>
30 #include <libxml/parser.h>
31 #include <libxml/xmlschemas.h>
32 #include "qof.h"
33 #include "qofobject-p.h"
34 #include "qof-backend-qsf.h"
35 #include "qsf-xml.h"
36 #include "qsf-dir.h"
37 
38 #define QSF_TYPE_BINARY "binary"
39 #define QSF_TYPE_GLIST "glist"
40 #define QSF_TYPE_FRAME "frame"
41 
42 static QofLogModule log_module = QOF_MOD_QSF;
43 
44 static void qsf_object_commitCB (gpointer key, gpointer value,
45  gpointer data);
46 
47 struct QSFBackend_s
48 {
49  QofBackend be;
50  QsfParam *params;
51  gchar *fullpath;
52 };
53 
54 typedef struct QSFBackend_s QSFBackend;
55 
56 static void
57 option_cb (QofBackendOption * option, gpointer data)
58 {
59  QsfParam *params;
60 
61  params = (QsfParam *) data;
62  g_return_if_fail (params);
63  if (0 == safe_strcmp (QSF_COMPRESS, option->option_name))
64  {
65  params->use_gz_level = (*(gint64 *) option->value);
66  PINFO (" compression=%" G_GINT64_FORMAT, params->use_gz_level);
67  }
68  if (0 == safe_strcmp (QSF_MAP_FILES, option->option_name))
69  {
70  params->map_files = g_list_copy ((GList *) option->value);
71  }
72  if (0 == safe_strcmp (QSF_ENCODING, option->option_name))
73  {
74  params->encoding = g_strdup (option->value);
75  PINFO (" encoding=%s", params->encoding);
76  }
77  if (0 == safe_strcmp (QSF_DATE_CONVERT, option->option_name))
78  {
79  params->convert = (*(double *) option->value);
80  if (params->convert > 0)
81  PINFO (" converting date into time on file write.");
82  }
83 }
84 
85 static void
86 qsf_load_config (QofBackend * be, KvpFrame * config)
87 {
88  QSFBackend *qsf_be;
89  QsfParam *params;
90 
91  ENTER (" ");
92  qsf_be = (QSFBackend *) be;
93  g_return_if_fail (qsf_be->params);
94  params = qsf_be->params;
95  qof_backend_option_foreach (config, option_cb, params);
96  LEAVE (" ");
97 }
98 
99 static KvpFrame *
100 qsf_get_config (QofBackend * be)
101 {
102  QofBackendOption *option;
103  QSFBackend *qsf_be;
104  QsfParam *params;
105 
106  if (!be)
107  {
108  return NULL;
109  }
110  ENTER (" ");
111  qsf_be = (QSFBackend *) be;
112  g_return_val_if_fail (qsf_be->params, NULL);
113  params = qsf_be->params;
115  option = g_new0 (QofBackendOption, 1);
116  option->option_name = QSF_COMPRESS;
117  option->description =
118  _("Level of compression to use: 0 for none, 9 for highest.");
119  option->tooltip =
120  _("QOF can compress QSF XML files using gzip. "
121  "Note that compression is not used when outputting to STDOUT.");
122  option->type = KVP_TYPE_GINT64;
123  /* GINT_TO_POINTER can only be used for 32bit values. */
124  option->value = (gpointer) & params->use_gz_level;
125  qof_backend_prepare_option (be, option);
126  g_free (option);
127  option = g_new0 (QofBackendOption, 1);
128  option->option_name = QSF_MAP_FILES;
129  option->description =
130  _("List of QSF map files to use for this session.");
131  option->tooltip =
132  _("QOF can convert objects within QSF XML files "
133  "using a map of the changes required.");
134  option->type = KVP_TYPE_GLIST;
135  option->value = (gpointer) params->map_files;
136  qof_backend_prepare_option (be, option);
137  g_free (option);
138  option = g_new0 (QofBackendOption, 1);
139  option->option_name = QSF_ENCODING;
140  option->description =
141  _("Encoding string to use when writing the XML file.");
142  option->tooltip =
143  _("QSF defaults to UTF-8. Other encodings are supported by "
144  "passing the encoding string in this option.");
145  option->type = KVP_TYPE_STRING;
146  option->value = (gpointer) params->encoding;
147  qof_backend_prepare_option (be, option);
148  g_free (option);
149  option = g_new0 (QofBackendOption, 1);
150  option->option_name = QSF_DATE_CONVERT;
151  option->description =
152  _("Convert deprecated date values to time values.");
153  option->tooltip =
154  _("Applications that support the new QOF time format "
155  "need to enable this option to convert older date values into time. "
156  "Applications that still use date should not set this option "
157  "until time values are supported.");
158  option->type = KVP_TYPE_GINT64;
159  option->value = &params->convert;
160  qof_backend_prepare_option (be, option);
161  g_free (option);
162  LEAVE (" ");
163  return qof_backend_complete_frame (be);
164 }
165 
166 GList **
167 qsf_map_prepare_list (GList ** maps)
168 {
169  /* Add new map filenames here. */
171  *maps = g_list_prepend (*maps, "pilot-qsf-GnuCashInvoice.xml");
172  *maps = g_list_prepend (*maps, "pilot-qsf-gncCustomer.xml");
173  return maps;
174 }
175 
176 static void
177 qsf_param_init (QsfParam * params)
178 {
179  gchar *qsf_time_string;
180  gchar *qsf_enquiry_date;
181  gchar *qsf_time_now;
182  gchar *qsf_time_precision;
183 
184  g_return_if_fail (params != NULL);
185  params->count = 0;
186  params->convert = 1;
187  params->use_gz_level = 0;
188  params->supported_types = NULL;
189  params->file_type = QSF_UNDEF;
190  params->qsf_ns = NULL;
191  params->output_doc = NULL;
192  params->output_node = NULL;
193  params->lister = NULL;
194  params->full_kvp_path = NULL;
195  params->map_ns = NULL;
196  params->map_files = NULL;
197  params->map_path = NULL;
198  params->encoding = "UTF-8";
199  params->qsf_object_list = NULL;
200  params->qsf_parameter_hash =
201  g_hash_table_new (g_str_hash, g_str_equal);
202  params->qsf_default_hash = g_hash_table_new (g_str_hash, g_str_equal);
203  params->qsf_define_hash = g_hash_table_new (g_str_hash, g_str_equal);
204  params->qsf_calculate_hash =
205  g_hash_table_new (g_str_hash, g_str_equal);
206  params->referenceList = NULL;
207  params->supported_types =
208  g_slist_append (params->supported_types, QOF_TYPE_STRING);
209  params->supported_types =
210  g_slist_append (params->supported_types, QOF_TYPE_GUID);
211  params->supported_types =
212  g_slist_append (params->supported_types, QOF_TYPE_BOOLEAN);
213  params->supported_types =
214  g_slist_append (params->supported_types, QOF_TYPE_NUMERIC);
215 #ifndef QOF_DISABLE_DEPRECATED
216  /* Support read if built with deprecated code included.
217  Support write only if convert option is not enabled. */
218  params->supported_types =
219  g_slist_append (params->supported_types, QOF_TYPE_DATE);
220 #endif
221  params->supported_types =
222  g_slist_append (params->supported_types, QOF_TYPE_TIME);
223  params->supported_types =
224  g_slist_append (params->supported_types, QOF_TYPE_INT32);
225  params->supported_types =
226  g_slist_append (params->supported_types, QOF_TYPE_INT64);
227  params->supported_types =
228  g_slist_append (params->supported_types, QOF_TYPE_DOUBLE);
229  params->supported_types =
230  g_slist_append (params->supported_types, QOF_TYPE_CHAR);
231  params->supported_types =
232  g_slist_append (params->supported_types, QOF_TYPE_KVP);
233  params->supported_types =
234  g_slist_append (params->supported_types, QOF_TYPE_COLLECT);
235  params->supported_types =
236  g_slist_append (params->supported_types, QOF_TYPE_CHOICE);
237  qsf_time_precision = "%j";
238  qsf_enquiry_date = qof_time_stamp_now ();
239  qsf_time_string = qof_date_print (qof_date_get_current(),
241  qsf_time_now = qof_time_stamp_now ();
242 
243  g_hash_table_insert (params->qsf_default_hash, "qsf_enquiry_date",
244  qsf_enquiry_date);
245  g_hash_table_insert (params->qsf_default_hash, "qsf_time_now",
247  g_hash_table_insert (params->qsf_default_hash, "qsf_time_string",
248  qsf_time_string);
249  /* default map files */
250  params->map_files = *qsf_map_prepare_list (&params->map_files);
251  params->err_nomap = qof_error_register
252  (_("The selected QSF Object file '%s' requires a "
253  "map but it was not provided."), TRUE);
254  params->err_overflow = qof_error_register
255  (_("When converting XML strings into numbers, an "
256  "overflow has been detected. The QSF object file "
257  "'%s' contains invalid data in a field that is "
258  "meant to hold a number."), TRUE);
259 }
260 
261 static gboolean
262 qsf_determine_file_type (const gchar * path)
263 {
264  struct stat sbuf;
265 
266  if (!path)
267  return TRUE;
268  if (0 == safe_strcmp (path, QOF_STDOUT))
269  return TRUE;
270  if (stat (path, &sbuf) < 0)
271  {
272  /* in case the error is that the file does not exist */
273  FILE * f;
274  f = fopen (path, "a+");
275  if (f)
276  {
277  fclose (f);
278  return TRUE;
279  }
280  return FALSE;
281  }
282  if (sbuf.st_size == 0)
283  return TRUE;
284  if (is_our_qsf_object (path))
285  return TRUE;
286  else if (is_qsf_object (path))
287  return TRUE;
288  else if (is_qsf_map (path))
289  return TRUE;
290  return FALSE;
291 }
292 
293 static void
294 qsf_session_begin (QofBackend * be, QofSession * session,
295  const gchar * book_path, gboolean ignore_lock,
296  gboolean create_if_nonexistent)
297 {
298  QSFBackend *qsf_be;
299  gchar *p, *path;
300 
301  PINFO (" ignore_lock=%d create_if_nonexistent=%d", ignore_lock,
302  create_if_nonexistent);
303  g_return_if_fail (be != NULL);
304  g_return_if_fail (session);
305  be->fullpath = g_strdup (book_path);
306  qsf_be = (QSFBackend *) be;
307  g_return_if_fail (qsf_be->params != NULL);
308  qsf_be->fullpath = NULL;
309  if (book_path == NULL)
310  {
311  /* allow use of stdout */
312  qof_error_set_be (be, QOF_SUCCESS);
313  return;
314  }
315  p = strchr (book_path, ':');
316  if (p)
317  {
318  path = g_strdup (book_path);
319  if (!g_ascii_strncasecmp (path, "file:", 5))
320  {
321  p = g_new0 (gchar, strlen (path) - 5 + 1);
322  strcpy (p, path + 5);
323  }
324  qsf_be->fullpath = g_strdup (p);
325  g_free (path);
326  }
327  else
328  qsf_be->fullpath = g_strdup (book_path);
329  if (create_if_nonexistent)
330  {
331  FILE *f;
332 
333  f = fopen (qsf_be->fullpath, "a+");
334  if (f)
335  fclose (f);
336  else
337  {
338  qof_error_set_be (be, qof_error_register
339  (_("could not write to '%s'. "
340  "That database may be on a read-only file system, "
341  "or you may not have write permission for the "
342  "directory.\n"), TRUE));
343  return;
344  }
345  }
346  qof_error_set_be (be, QOF_SUCCESS);
347 }
348 
349 static void
350 qsf_free_params (QsfParam * params)
351 {
352  g_hash_table_destroy (params->qsf_calculate_hash);
353  g_hash_table_destroy (params->qsf_default_hash);
354  if (params->referenceList)
355  g_list_free (params->referenceList);
356  g_slist_free (params->supported_types);
357  if (params->map_ns)
358  xmlFreeNs (params->map_ns);
359  if (params->output_doc)
360  xmlFreeDoc (params->output_doc);
361 }
362 
363 static void
364 qsf_session_end (QofBackend * be)
365 {
366  QSFBackend *qsf_be;
367 
368  qsf_be = (QSFBackend *) be;
369  g_return_if_fail (qsf_be != NULL);
370  qsf_free_params (qsf_be->params);
371  g_free (qsf_be->fullpath);
372  qsf_be->fullpath = NULL;
373  xmlCleanupParser ();
374 }
375 
376 static void
377 qsf_destroy_backend (QofBackend * be)
378 {
379  g_free (be);
380 }
381 
382 static void
383 ent_ref_cb (QofEntity * ent, gpointer user_data)
384 {
385  QsfParam *params;
386  QofEntityReference *ref;
387  void (*reference_setter) (QofEntity *, QofEntity *);
388  QofEntity *reference;
389  QofCollection *coll;
390  QofIdType type;
391 
392  params = (QsfParam *) user_data;
393  g_return_if_fail (params);
394  while (params->referenceList)
395  {
396  ref = (QofEntityReference *) params->referenceList->data;
397  if (qof_object_is_choice (ent->e_type))
398  type = ref->choice_type;
399  else
400  type = ref->type;
401  coll = qof_book_get_collection (params->book, type);
402  reference = qof_collection_lookup_entity (coll, ref->ref_guid);
403  reference_setter =
404  (void (*)(QofEntity *, QofEntity *)) ref->param->param_setfcn;
405  if (reference_setter != NULL)
406  {
407  qof_util_param_edit ((QofInstance *) ent, ref->param);
408  qof_util_param_edit ((QofInstance *) reference, ref->param);
409  reference_setter (ent, reference);
410  qof_util_param_commit ((QofInstance *) ent, ref->param);
411  qof_util_param_commit ((QofInstance *) reference, ref->param);
412  }
413  params->referenceList = g_list_next (params->referenceList);
414  }
415 }
416 
417 static void
418 insert_ref_cb (QofObject * obj, gpointer user_data)
419 {
420  QsfParam *params;
421 
422  params = (QsfParam *) user_data;
423  g_return_if_fail (params);
424  qof_object_foreach (obj->e_type, params->book, ent_ref_cb, params);
425 }
426 
427 /*================================================
428  Load QofEntity into QofBook from XML in memory
429 ==================================================*/
430 
431 static gboolean
432 qsfdoc_to_qofbook (QsfParam * params)
433 {
434  QofInstance *inst;
435  struct QsfNodeIterate qiter;
436  QofBook *book;
437  GList *object_list;
438  xmlNodePtr qsf_root;
439  xmlNsPtr qsf_ns;
440 
441  g_return_val_if_fail (params != NULL, FALSE);
442  g_return_val_if_fail (params->input_doc != NULL, FALSE);
443  g_return_val_if_fail (params->book != NULL, FALSE);
444  g_return_val_if_fail (params->file_type == OUR_QSF_OBJ, FALSE);
445  qsf_root = xmlDocGetRootElement (params->input_doc);
446  if (!qsf_root)
447  return FALSE;
448  qsf_ns = qsf_root->ns;
449  qiter.ns = qsf_ns;
450  book = params->book;
451  params->referenceList =
452  (GList *) qof_book_get_data (book, ENTITYREFERENCE);
453  qsf_node_foreach (qsf_root, qsf_book_node_handler, &qiter, params);
454  object_list = g_list_copy (params->qsf_object_list);
455  while (object_list != NULL)
456  {
457  params->object_set = object_list->data;
458  object_list = g_list_next (object_list);
459  params->qsf_parameter_hash = params->object_set->parameters;
460  if (!qof_class_is_registered (params->object_set->object_type))
461  continue;
462  inst =
464  object_type, book);
465  g_return_val_if_fail (inst != NULL, FALSE);
466  params->qsf_ent = &inst->entity;
467  g_hash_table_foreach (params->qsf_parameter_hash,
468  qsf_object_commitCB, params);
469  }
470  qof_object_foreach_type (insert_ref_cb, params);
472  return TRUE;
473 }
474 
475 /* QofBackend routine to load from file - needs a map.
476 */
477 static gboolean
478 load_qsf_object (QofBook * book, const gchar * fullpath,
479  QsfParam * params)
480 {
481  xmlNodePtr qsf_root, map_root;
482  xmlDocPtr mapDoc, foreign_doc;
483  gchar *map_path, *map_file;
484 
485  map_file = params->map_path;
486  mapDoc = NULL;
487  /* use selected map */
488  if (!map_file)
489  {
490  qof_error_set_be (params->be, params->err_nomap);
491  return FALSE;
492  }
493  foreign_doc = xmlParseFile (fullpath);
494  if (foreign_doc == NULL)
495  {
496  qof_error_set_be (params->be, qof_error_register
497  (_("There was an error parsing the file '%s'.\n"), TRUE));
498  return FALSE;
499  }
500  qsf_root = NULL;
501  qsf_root = xmlDocGetRootElement (foreign_doc);
502  params->qsf_ns = qsf_root->ns;
503  params->book = book;
504  map_path = g_strdup_printf ("%s/%s", QSF_SCHEMA_DIR, map_file);
505  if (!map_path)
506  {
507  qof_error_set_be (params->be, params->err_nomap);
508  return FALSE;
509  }
510  mapDoc = xmlParseFile (map_path);
511  if (!mapDoc)
512  {
513  qof_error_set_be (params->be, params->err_nomap);
514  return FALSE;
515  }
516  map_root = xmlDocGetRootElement (mapDoc);
517  params->map_ns = map_root->ns;
518  params->input_doc = qsf_object_convert (mapDoc, qsf_root, params);
519  qsfdoc_to_qofbook (params);
520  return TRUE;
521 }
522 
523 static gboolean
524 load_our_qsf_object (const gchar * fullpath, QsfParam * params)
525 {
526  xmlNodePtr qsf_root;
527 
528  params->input_doc = xmlParseFile (fullpath);
529  if (params->input_doc == NULL)
530  {
531  qof_error_set_be (params->be, qof_error_register
532  (_("There was an error parsing the file '%s'."), TRUE));
533  return FALSE;
534  }
535  qsf_root = NULL;
536  qsf_root = xmlDocGetRootElement (params->input_doc);
537  params->qsf_ns = qsf_root->ns;
538  return qsfdoc_to_qofbook (params);
539 }
540 
541 /* Determine the type of QSF and load it into the QofBook
542 
543 - is_our_qsf_object, OUR_QSF_OBJ, QSF object file using only QOF objects known
544  to the calling process. No map is required.
545 - is_qsf_object, IS_QSF_OBJ, QSF object file that may or may not have a QSF map
546  to convert external objects. This temporary type will be set to HAVE_QSF_MAP
547  if a suitable map exists, or an error value returned: ERR_QSF_NO_MAP,
548  ERR_QSF_BAD_MAP or ERR_QSF_WRONG_MAP. This allows the calling process to inform
549  the user that the QSF itself is valid but a suitable map cannot be found.
550 - is_qsf_map, IS_QSF_MAP, QSF map file. In the backend, this generates
551  ERR_QSF_MAP_NOT_OBJ but it can be used internally when processing maps to
552  match a QSF object.
553 
554 returns NULL on error, otherwise a pointer to the QofBook. Use
555 the qof_book_merge API to merge the new data into the current
556 QofBook.
557 */
558 static void
559 qsf_file_type (QofBackend * be, QofBook * book)
560 {
561  QSFBackend *qsf_be;
562  QofErrorId parse_err;
563  QsfParam *params;
564  FILE *f;
565  gchar *path;
566  gboolean result;
567 
568  g_return_if_fail (be != NULL);
569  g_return_if_fail (book != NULL);
570  qsf_be = (QSFBackend *) be;
571  g_return_if_fail (qsf_be != NULL);
572  g_return_if_fail (qsf_be->fullpath != NULL);
573  g_return_if_fail (qsf_be->params != NULL);
574  parse_err = qof_error_register
575  (_("There was an error parsing the file '%s'."), TRUE);
576  params = qsf_be->params;
577  params->book = book;
578  DEBUG (" qsf_be->fullpath=%s", qsf_be->fullpath);
579  path = g_strdup (qsf_be->fullpath);
580  f = fopen (path, "r");
581  if (!f)
582  qof_error_set_be (be, qof_error_register
583  (_("There was an error reading the file '%s'."), TRUE));
584  else
585  fclose (f);
586  params->filepath = g_strdup (path);
587  result = is_our_qsf_object_be (params);
588  if (result)
589  {
590  params->file_type = OUR_QSF_OBJ;
591  result = load_our_qsf_object (path, params);
592  if (!result)
593  qof_error_set_be (be, parse_err);
594  return;
595  }
596  else if (is_qsf_object_be (params))
597  {
598  params->file_type = IS_QSF_OBJ;
599  result = load_qsf_object (book, path, params);
600  if (!result)
601  qof_error_set_be (be, parse_err);
602  return;
603  }
604  if (qof_error_check_be (be) == params->err_nomap)
605  {
606  /* usable QSF object but no map available */
607  params->file_type = IS_QSF_OBJ;
608  result = TRUE;
609  }
610  if (result == FALSE)
611  {
612  if (is_qsf_map_be (params))
613  {
614  params->file_type = IS_QSF_MAP;
615  qof_error_set_be (be, qof_error_register
616  (_("The selected file '%s' is a QSF map and cannot "
617  "be opened as a QSF object."), TRUE));
618  }
619  }
620 }
621 
622 static void
623 qsf_object_sequence (QofParam * qof_param, gpointer data)
624 {
625  QsfParam *params;
626  GSList *checklist, *result;
627 
628  g_return_if_fail (data != NULL);
629  params = (QsfParam *) data;
630  result = NULL;
631  checklist = NULL;
632  params->knowntype = FALSE;
633  checklist = g_slist_copy (params->supported_types);
634  for (result = checklist; result != NULL; result = result->next)
635  {
636  if (0 ==
637  safe_strcmp ((QofIdType) result->data,
638  qof_param->param_type))
639  params->knowntype = TRUE;
640  }
641  g_slist_free (checklist);
642  if (0 == safe_strcmp (qof_param->param_type, params->qof_type))
643  {
644  params->qsf_sequence =
645  g_slist_append (params->qsf_sequence, qof_param);
646  params->knowntype = TRUE;
647  }
648  /* handle params->qof_type = QOF_TYPE_GUID and qof_param->param_type != known type */
649  if (0 == safe_strcmp (params->qof_type, QOF_TYPE_GUID)
650  && (params->knowntype == FALSE))
651  {
652  params->qsf_sequence =
653  g_slist_append (params->qsf_sequence, qof_param);
654  params->knowntype = TRUE;
655  }
656 }
657 
658 /* receives each entry from supported_types in sequence
659  type = qof data type from supported list
660  user_data = params. Holds object type
661 */
662 static void
663 qsf_supported_parameters (gpointer type, gpointer user_data)
664 {
665  QsfParam *params;
666 
667  g_return_if_fail (user_data != NULL);
668  params = (QsfParam *) user_data;
669  params->qof_type = (QofIdType) type;
670  params->knowntype = FALSE;
671  qof_class_param_foreach (params->qof_obj_type, qsf_object_sequence,
672  params);
673 }
674 
675 static KvpValueType
676 qsf_to_kvp_helper (const char *type_string)
677 {
678  if (0 == safe_strcmp (QOF_TYPE_INT64, type_string))
679  return KVP_TYPE_GINT64;
680  if (0 == safe_strcmp (QOF_TYPE_DOUBLE, type_string))
681  return KVP_TYPE_DOUBLE;
682  if (0 == safe_strcmp (QOF_TYPE_NUMERIC, type_string))
683  return KVP_TYPE_NUMERIC;
684  if (0 == safe_strcmp (QOF_TYPE_STRING, type_string))
685  return KVP_TYPE_STRING;
686  if (0 == safe_strcmp (QOF_TYPE_GUID, type_string))
687  return KVP_TYPE_GUID;
688 #ifndef QOF_DISABLE_DEPRECATED
689  if (0 == safe_strcmp (QOF_TYPE_DATE, type_string))
690  return KVP_TYPE_TIMESPEC;
691 #endif
692  if (0 == safe_strcmp (QOF_TYPE_TIME, type_string))
693  return KVP_TYPE_TIME;
694  if (0 == safe_strcmp (QSF_TYPE_BINARY, type_string))
695  return KVP_TYPE_BINARY;
696  if (0 == safe_strcmp (QSF_TYPE_GLIST, type_string))
697  return KVP_TYPE_GLIST;
698  if (0 == safe_strcmp (QSF_TYPE_FRAME, type_string))
699  return KVP_TYPE_FRAME;
700  return 0;
701 }
702 
703 static QofIdTypeConst
705 {
706  switch (n)
707  {
708  case KVP_TYPE_GINT64:
709  {
710  return QOF_TYPE_INT64;
711  break;
712  }
713  case KVP_TYPE_DOUBLE:
714  {
715  return QOF_TYPE_DOUBLE;
716  break;
717  }
718  case KVP_TYPE_NUMERIC:
719  {
720  return QOF_TYPE_NUMERIC;
721  break;
722  }
723  case KVP_TYPE_STRING:
724  {
725  return QOF_TYPE_STRING;
726  break;
727  }
728  case KVP_TYPE_GUID:
729  {
730  return QOF_TYPE_GUID;
731  break;
732  }
733 #ifndef QOF_DISABLE_DEPRECATED
734  case KVP_TYPE_TIMESPEC:
735  {
736  return QOF_TYPE_DATE;
737  break;
738  }
739 #endif
740  case KVP_TYPE_BOOLEAN :
741  {
742  return QOF_TYPE_BOOLEAN;
743  break;
744  }
745  case KVP_TYPE_TIME :
746  {
747  return QOF_TYPE_TIME;
748  break;
749  }
750  case KVP_TYPE_BINARY:
751  {
752  return QSF_TYPE_BINARY;
753  break;
754  }
755  case KVP_TYPE_GLIST:
756  {
757  return QSF_TYPE_GLIST;
758  break;
759  }
760  case KVP_TYPE_FRAME:
761  {
762  return QSF_TYPE_FRAME;
763  break;
764  }
765  default:
766  {
767  return NULL;
768  }
769  }
770 }
771 
772 
773 static void
774 qsf_from_kvp_helper (const gchar * path, KvpValue * content,
775  gpointer data)
776 {
777  QsfParam *params;
778  QofParam *qof_param;
779  xmlNodePtr node;
780  KvpValueType n;
781  gchar *full_path;
782 
783  params = (QsfParam *) data;
784  qof_param = params->qof_param;
785  full_path = NULL;
786  g_return_if_fail (params && path && content);
787  n = kvp_value_get_type (content);
788  switch (n)
789  {
790  case KVP_TYPE_GINT64:
791  case KVP_TYPE_DOUBLE:
792  case KVP_TYPE_NUMERIC:
793  case KVP_TYPE_STRING:
794  case KVP_TYPE_GUID:
795  case KVP_TYPE_TIME :
796  case KVP_TYPE_BOOLEAN :
797 #ifndef QOF_DISABLE_DEPRECATED
798  case KVP_TYPE_TIMESPEC:
799 #endif
800  case KVP_TYPE_BINARY:
801  case KVP_TYPE_GLIST:
802  {
803  node =
804  xmlAddChild (params->output_node,
805  xmlNewNode (params->qsf_ns,
806  BAD_CAST qof_param->param_type));
807  xmlNodeAddContent (node,
808  BAD_CAST kvp_value_to_bare_string (content));
809  xmlNewProp (node, BAD_CAST QSF_OBJECT_TYPE,
810  BAD_CAST qof_param->param_name);
811  full_path =
812  g_strconcat (params->full_kvp_path, "/", path, NULL);
813  xmlNewProp (node, BAD_CAST QSF_OBJECT_KVP, BAD_CAST full_path);
814  xmlNewProp (node, BAD_CAST QSF_OBJECT_VALUE,
815  BAD_CAST kvp_value_to_qof_type_helper (n));
816  break;
817  }
818  case KVP_TYPE_FRAME:
819  {
820  if (!params->full_kvp_path)
821  params->full_kvp_path = g_strdup (path);
822  else
823  params->full_kvp_path = g_strconcat (params->full_kvp_path,
824  "/", path, NULL);
826  qsf_from_kvp_helper, params);
827  g_free (params->full_kvp_path);
828  params->full_kvp_path = NULL;
829  break;
830  }
831  default:
832  {
833  PERR (" unsupported value = %d", kvp_value_get_type (content));
834  break;
835  }
836  }
837 }
838 
839 static void
840 qsf_from_coll_cb (QofEntity * ent, gpointer user_data)
841 {
842  QsfParam *params;
843  QofParam *qof_param;
844  xmlNodePtr node;
845  gchar qsf_guid[GUID_ENCODING_LENGTH + 1];
846 
847  params = (QsfParam *) user_data;
848  if (!ent || !params)
849  return;
850  qof_param = params->qof_param;
851  guid_to_string_buff (qof_entity_get_guid (ent), qsf_guid);
852  node = xmlAddChild (params->output_node, xmlNewNode (params->qsf_ns,
853  BAD_CAST qof_param->param_type));
854  xmlNodeAddContent (node, BAD_CAST qsf_guid);
855  xmlNewProp (node, BAD_CAST QSF_OBJECT_TYPE,
856  BAD_CAST qof_param->param_name);
857 }
858 
859 /******* reference handling ***********/
860 
861 static gint
862 qof_reference_list_cb (gconstpointer a, gconstpointer b)
863 {
864  const QofEntityReference *aa;
865  const QofEntityReference *bb;
866 
867  aa = (QofEntityReference *) a;
868  bb = (QofEntityReference *) b;
869  if (aa == NULL)
870  return 1;
871  g_return_val_if_fail ((bb != NULL), 1);
872  g_return_val_if_fail ((aa->type != NULL), 1);
873  if ((0 == guid_compare (bb->ent_guid, aa->ent_guid))
874  && (0 == safe_strcmp (bb->type, aa->type))
875  && (0 == safe_strcmp (bb->param->param_name,
876  aa->param->param_name)))
877  return 0;
878  return 1;
879 }
880 
881 static QofEntityReference *
882 qof_reference_lookup (GList * referenceList, QofEntityReference * find)
883 {
884  GList *single_ref;
885  QofEntityReference *ent_ref;
886 
887  if (referenceList == NULL)
888  return NULL;
889  g_return_val_if_fail (find != NULL, NULL);
890  single_ref = NULL;
891  ent_ref = NULL;
892  single_ref =
893  g_list_find_custom (referenceList, find, qof_reference_list_cb);
894  if (single_ref == NULL)
895  return ent_ref;
896  ent_ref = (QofEntityReference *) single_ref->data;
897  g_list_free (single_ref);
898  return ent_ref;
899 }
900 
901 static void
902 reference_list_lookup (gpointer data, gpointer user_data)
903 {
904  QofEntity *ent;
905  QofParam *ref_param;
906  QofEntityReference *reference, *starter;
907  QsfParam *params;
908  const GUID *guid;
909  xmlNodePtr node, object_node;
910  xmlNsPtr ns;
911  GList *copy_list;
912  gchar qsf_guid[GUID_ENCODING_LENGTH + 1], *ref_name;
913 
914  params = (QsfParam *) user_data;
915  ref_param = (QofParam *) data;
916  object_node = params->output_node;
917  ent = params->qsf_ent;
918  ns = params->qsf_ns;
919  starter = g_new0 (QofEntityReference, 1);
920  starter->ent_guid = qof_entity_get_guid (ent);
921  starter->type = g_strdup (ent->e_type);
922  starter->param = ref_param;
923  starter->ref_guid = NULL;
924  copy_list = g_list_copy (params->referenceList);
925  reference = qof_reference_lookup (copy_list, starter);
926  g_free (starter);
927  if (reference != NULL)
928  {
929  if ((ref_param->param_getfcn == NULL)
930  || (ref_param->param_setfcn == NULL))
931  return;
932  ref_name = g_strdup (reference->param->param_name);
933  node =
934  xmlAddChild (object_node,
935  xmlNewNode (ns, BAD_CAST QOF_TYPE_GUID));
936  guid_to_string_buff (reference->ref_guid, qsf_guid);
937  xmlNodeAddContent (node, BAD_CAST qsf_guid);
938  xmlNewProp (node, BAD_CAST QSF_OBJECT_TYPE, BAD_CAST ref_name);
939  g_free (ref_name);
940  }
941  else
942  {
943  ent = (QofEntity *) ref_param->param_getfcn (ent, ref_param);
944  if (!ent)
945  return;
946  if ((0 == safe_strcmp (ref_param->param_type, QOF_TYPE_COLLECT)) ||
947  (0 == safe_strcmp (ref_param->param_type, QOF_TYPE_CHOICE)))
948  return;
949  node =
950  xmlAddChild (object_node,
951  xmlNewNode (ns, BAD_CAST QOF_TYPE_GUID));
952  guid = qof_entity_get_guid (ent);
953  guid_to_string_buff (guid, qsf_guid);
954  xmlNodeAddContent (node, BAD_CAST qsf_guid);
955  xmlNewProp (node, BAD_CAST QSF_OBJECT_TYPE,
956  BAD_CAST ref_param->param_name);
957  }
958 }
959 
960 /*=====================================
961  Convert QofEntity to QSF XML node
962 qof_param holds the parameter sequence.
963 =======================================*/
964 static void
965 qsf_entity_foreach (QofEntity * ent, gpointer data)
966 {
967  QsfParam *params;
968  GSList *param_list, *supported;
969  GList *ref;
970  xmlNodePtr node, object_node;
971  xmlNsPtr ns;
972  gchar *string_buffer;
973  QofParam *qof_param;
974  QofEntity *choice_ent;
975  KvpFrame *qsf_kvp;
976  QofCollection *qsf_coll;
977  gint param_count;
978  gboolean own_guid;
979  const GUID *cm_guid;
980  gchar cm_sa[GUID_ENCODING_LENGTH + 1];
981 
982  g_return_if_fail (data != NULL);
983  params = (QsfParam *) data;
984  param_count = ++params->count;
985  ns = params->qsf_ns;
986  qsf_kvp = NULL;
987  own_guid = FALSE;
988  choice_ent = NULL;
989  object_node = xmlNewChild (params->book_node, params->qsf_ns,
990  BAD_CAST QSF_OBJECT_TAG, NULL);
991  xmlNewProp (object_node, BAD_CAST QSF_OBJECT_TYPE,
992  BAD_CAST ent->e_type);
993  string_buffer = g_strdup_printf ("%i", param_count);
994  xmlNewProp (object_node, BAD_CAST QSF_OBJECT_COUNT,
995  BAD_CAST string_buffer);
996  g_free (string_buffer);
997  param_list = g_slist_copy (params->qsf_sequence);
998  while (param_list != NULL)
999  {
1000  qof_param = (QofParam *) param_list->data;
1001  g_return_if_fail (qof_param != NULL);
1002  if (0 == safe_strcmp (qof_param->param_type, QOF_TYPE_GUID))
1003  {
1004  if (!own_guid)
1005  {
1006  cm_guid = qof_entity_get_guid (ent);
1007  node = xmlAddChild (object_node, xmlNewNode (ns, BAD_CAST
1008  QOF_TYPE_GUID));
1009  guid_to_string_buff (cm_guid, cm_sa);
1010  string_buffer = g_strdup (cm_sa);
1011  xmlNodeAddContent (node, BAD_CAST string_buffer);
1012  xmlNewProp (node, BAD_CAST QSF_OBJECT_TYPE, BAD_CAST
1013  QOF_PARAM_GUID);
1014  g_free (string_buffer);
1015  own_guid = TRUE;
1016  }
1017  params->qsf_ent = ent;
1018  params->output_node = object_node;
1019  ref = qof_class_get_referenceList (ent->e_type);
1020  if (ref != NULL)
1021  g_list_foreach (ref, reference_list_lookup, params);
1022  }
1023  if (0 == safe_strcmp (qof_param->param_type, QOF_TYPE_COLLECT))
1024  {
1025  qsf_coll = qof_param->param_getfcn (ent, qof_param);
1026  if (qsf_coll)
1027  {
1028  params->qof_param = qof_param;
1029  params->output_node = object_node;
1030  if (qof_collection_count (qsf_coll) > 0)
1031  qof_collection_foreach (qsf_coll, qsf_from_coll_cb,
1032  params);
1033  }
1034  param_list = g_slist_next (param_list);
1035  continue;
1036  }
1037  if (0 == safe_strcmp (qof_param->param_type, QOF_TYPE_CHOICE))
1038  {
1040  choice_ent =
1041  (QofEntity *) qof_param->param_getfcn (ent, qof_param);
1042  if (!choice_ent)
1043  {
1044  param_list = g_slist_next (param_list);
1045  continue;
1046  }
1047  node = xmlAddChild (object_node, xmlNewNode (ns, BAD_CAST
1048  qof_param->param_type));
1049  cm_guid = qof_entity_get_guid (choice_ent);
1050  guid_to_string_buff (cm_guid, cm_sa);
1051  string_buffer = g_strdup (cm_sa);
1052  xmlNodeAddContent (node, BAD_CAST string_buffer);
1053  xmlNewProp (node, BAD_CAST QSF_OBJECT_TYPE, BAD_CAST
1054  qof_param->param_name);
1055  xmlNewProp (node, BAD_CAST "name",
1056  BAD_CAST choice_ent->e_type);
1057  g_free (string_buffer);
1058  param_list = g_slist_next (param_list);
1059  continue;
1060  }
1061  if (0 == safe_strcmp (qof_param->param_type, QOF_TYPE_KVP))
1062  {
1063  qsf_kvp =
1064  (KvpFrame *) qof_param->param_getfcn (ent, qof_param);
1065  if (kvp_frame_is_empty (qsf_kvp))
1066  return;
1067  params->qof_param = qof_param;
1068  params->output_node = object_node;
1069  kvp_frame_for_each_slot (qsf_kvp, qsf_from_kvp_helper, params);
1070  }
1071  if ((qof_param->param_setfcn != NULL)
1072  && (qof_param->param_getfcn != NULL))
1073  {
1074  for (supported = g_slist_copy (params->supported_types);
1075  supported != NULL; supported = g_slist_next (supported))
1076  {
1077  if (0 == safe_strcmp ((const gchar *) supported->data,
1078  (const gchar *) qof_param->param_type))
1079  {
1080  node = xmlAddChild (object_node,
1081  xmlNewNode (ns, BAD_CAST qof_param->param_type));
1082  string_buffer =
1084  (qof_param, ent));
1085  xmlNodeAddContent (node, BAD_CAST string_buffer);
1086  xmlNewProp (node, BAD_CAST QSF_OBJECT_TYPE, BAD_CAST
1087  qof_param->param_name);
1088  g_free (string_buffer);
1089  }
1090  }
1091  }
1092  param_list = g_slist_next (param_list);
1093  }
1094 }
1095 
1096 static void
1097 qsf_foreach_obj_type (QofObject * qsf_obj, gpointer data)
1098 {
1099  QsfParam *params;
1100  QofBook *book;
1101  GSList *support;
1102 
1103  g_return_if_fail (data != NULL);
1104  params = (QsfParam *) data;
1105  /* Skip unsupported objects */
1106  if ((qsf_obj->create == NULL) || (qsf_obj->foreach == NULL))
1107  {
1108  PINFO (" qsf_obj QOF support failed %s", qsf_obj->e_type);
1109  return;
1110  }
1111  params->qof_obj_type = qsf_obj->e_type;
1112  params->qsf_sequence = NULL;
1113  book = params->book;
1114  support = g_slist_copy (params->supported_types);
1115  g_slist_foreach (support, qsf_supported_parameters, params);
1116  qof_object_foreach (qsf_obj->e_type, book, qsf_entity_foreach, params);
1117 }
1118 
1119 /*=====================================================
1120  Take a QofBook and prepare a QSF XML doc in memory
1121 =======================================================*/
1122 /* QSF only uses one QofBook per file - count may be removed later. */
1123 static xmlDocPtr
1124 qofbook_to_qsf (QofBook * book, QsfParam * params)
1125 {
1126  xmlNodePtr top_node, node;
1127  xmlDocPtr doc;
1128  gchar buffer[GUID_ENCODING_LENGTH + 1];
1129  const GUID *book_guid;
1130 
1131  g_return_val_if_fail (book != NULL, NULL);
1132  params->book = book;
1133  params->referenceList =
1134  g_list_copy ((GList *) qof_book_get_data (book,
1135  ENTITYREFERENCE));
1136  doc = xmlNewDoc (BAD_CAST QSF_XML_VERSION);
1137  top_node = xmlNewNode (NULL, BAD_CAST QSF_ROOT_TAG);
1138  xmlDocSetRootElement (doc, top_node);
1139  xmlSetNs (top_node, xmlNewNs (top_node, BAD_CAST QSF_DEFAULT_NS,
1140  NULL));
1141  params->qsf_ns = top_node->ns;
1142  node =
1143  xmlNewChild (top_node, params->qsf_ns, BAD_CAST QSF_BOOK_TAG,
1144  NULL);
1145  params->book_node = node;
1146  xmlNewProp (node, BAD_CAST QSF_BOOK_COUNT, BAD_CAST "1");
1147  book_guid = qof_entity_get_guid ((QofEntity*)book);
1148  guid_to_string_buff (book_guid, buffer);
1149  xmlNewChild (params->book_node, params->qsf_ns,
1150  BAD_CAST QSF_BOOK_GUID, BAD_CAST buffer);
1151  params->output_doc = doc;
1152  params->book_node = node;
1153  qof_object_foreach_type (qsf_foreach_obj_type, params);
1154  return params->output_doc;
1155 }
1156 
1157 static void
1158 write_qsf_from_book (const gchar *path, QofBook * book,
1159  QsfParam * params)
1160 {
1161  xmlDocPtr qsf_doc;
1162  gint write_result;
1163  QofBackend *be;
1164 
1165  be = qof_book_get_backend (book);
1166  qsf_doc = qofbook_to_qsf (book, params);
1167  write_result = 0;
1168  PINFO (" use_gz_level=%" G_GINT64_FORMAT " encoding=%s",
1169  params->use_gz_level, params->encoding);
1170  if ((params->use_gz_level > 0) && (params->use_gz_level <= 9))
1171  xmlSetDocCompressMode (qsf_doc, params->use_gz_level);
1172  g_return_if_fail (qsf_is_valid
1173  (QSF_SCHEMA_DIR, QSF_OBJECT_SCHEMA, qsf_doc) == TRUE);
1174  write_result =
1175  xmlSaveFormatFileEnc (path, qsf_doc, params->encoding, 1);
1176  if (write_result < 0)
1177  {
1178  qof_error_set_be (be, qof_error_register
1179  (_("Could not write to '%s'. Check that you have "
1180  "permission to write to this file and that there is "
1181  "sufficient space to create it."), TRUE));
1182  return;
1183  }
1184  qof_object_mark_clean (book);
1185 }
1186 
1187 static void
1188 write_qsf_to_stdout (QofBook * book, QsfParam * params)
1189 {
1190  xmlDocPtr qsf_doc;
1191 
1192  qsf_doc = qofbook_to_qsf (book, params);
1193  g_return_if_fail (qsf_is_valid
1194  (QSF_SCHEMA_DIR, QSF_OBJECT_SCHEMA, qsf_doc) == TRUE);
1195  PINFO (" use_gz_level=%" G_GINT64_FORMAT " encoding=%s",
1196  params->use_gz_level, params->encoding);
1197  xmlSaveFormatFileEnc ("-", qsf_doc, params->encoding, 1);
1198  fprintf (stdout, "\n");
1199  qof_object_mark_clean (book);
1200 }
1201 
1202 static void
1203 qsf_write_file (QofBackend * be, QofBook * book)
1204 {
1205  QSFBackend *qsf_be;
1206  QsfParam *params;
1207  gchar *path;
1208 
1209  qsf_be = (QSFBackend *) be;
1210  params = qsf_be->params;
1211  /* if fullpath is blank, book_id was set to QOF_STDOUT */
1212  if (!qsf_be->fullpath || (*qsf_be->fullpath == '\0'))
1213  {
1214  write_qsf_to_stdout (book, params);
1215  return;
1216  }
1217  path = strdup (qsf_be->fullpath);
1218  write_qsf_from_book (path, book, params);
1219  g_free (path);
1220 }
1221 
1222 KvpValue *
1223 string_to_kvp_value (const gchar * content, KvpValueType type)
1224 {
1225  gchar *tail;
1226  gint64 cm_i64;
1227  gdouble cm_double;
1228  QofNumeric cm_numeric;
1229  GUID *cm_guid;
1230 #ifndef QOF_DISABLE_DEPRECATED
1231  struct tm kvp_time;
1232  time_t kvp_time_t;
1233  Timespec cm_date;
1234 #endif
1235 
1236  switch (type)
1237  {
1238  case KVP_TYPE_GINT64:
1239  {
1240  errno = 0;
1241  cm_i64 = strtoll (content, &tail, 0);
1242  if (errno == 0)
1243  {
1244  return kvp_value_new_gint64 (cm_i64);
1245  }
1246  break;
1247  }
1248  case KVP_TYPE_DOUBLE:
1249  {
1250  errno = 0;
1251  cm_double = strtod (content, &tail);
1252  if (errno == 0)
1253  return kvp_value_new_double (cm_double);
1254  break;
1255  }
1256  case KVP_TYPE_NUMERIC:
1257  {
1258  qof_numeric_from_string (content, &cm_numeric);
1259  return kvp_value_new_numeric (cm_numeric);
1260  break;
1261  }
1262  case KVP_TYPE_STRING:
1263  {
1264  return kvp_value_new_string (content);
1265  break;
1266  }
1267  case KVP_TYPE_GUID:
1268  {
1269  cm_guid = g_new0 (GUID, 1);
1270  if (TRUE == string_to_guid (content, cm_guid))
1271  return kvp_value_new_guid (cm_guid);
1272  break;
1273  }
1274  case KVP_TYPE_TIME :
1275  {
1276  QofDate *qd;
1277  QofTime *qt;
1278  KvpValue *retval;
1279 
1280  qd = qof_date_parse (content, QOF_DATE_FORMAT_UTC);
1281  if(qd)
1282  {
1283  qt = qof_date_to_qtime (qd);
1284  retval = kvp_value_new_time (qt);
1285  qof_date_free (qd);
1286  qof_time_free (qt);
1287  return retval;
1288  }
1289  else
1290  PERR (" failed to parse date");
1291  }
1292 #ifndef QOF_DISABLE_DEPRECATED
1293  case KVP_TYPE_TIMESPEC:
1294  {
1295  strptime (content, QSF_XSD_TIME, &kvp_time);
1296  kvp_time_t = mktime (&kvp_time);
1297  timespecFromTime_t (&cm_date, kvp_time_t);
1298  return kvp_value_new_timespec (cm_date);
1299  break;
1300  }
1301 #endif
1302  case KVP_TYPE_BOOLEAN :
1303  {
1304  gboolean val;
1305  val = qof_util_bool_to_int (content);
1306  return kvp_value_new_boolean (val);
1307  }
1308  case KVP_TYPE_BINARY:
1309 // return kvp_value_new_binary(value->value.binary.data,
1310 // value->value.binary.datasize);
1311  break;
1312  case KVP_TYPE_GLIST:
1313 // return kvp_value_new_glist(value->value.list);
1314  break;
1315  case KVP_TYPE_FRAME:
1316 // return kvp_value_new_frame(value->value.frame);
1317  break;
1318  }
1319  return NULL;
1320 }
1321 
1322 /* ======================================================
1323  Commit XML data from file to QofEntity in a QofBook
1324 ========================================================= */
1325 void
1326 qsf_object_commitCB (gpointer key, gpointer value, gpointer data)
1327 {
1328  QsfParam *params;
1329  QsfObject *object_set;
1330  xmlNodePtr node;
1331  QofEntityReference *reference;
1332  QofEntity *qsf_ent;
1333  QofBook *targetBook;
1334  const gchar *qof_type, *parameter_name;
1335  QofIdType obj_type, reference_type;
1336  gchar *tail;
1337  /* cm_ prefix used for variables that hold the data to commit */
1338  QofNumeric cm_numeric;
1339  gdouble cm_double;
1340  gboolean cm_boolean;
1341  gint32 cm_i32;
1342  gint64 cm_i64;
1343  gchar cm_char, (*char_getter) (xmlNodePtr);
1344  GUID *cm_guid;
1345  KvpFrame *cm_kvp;
1346  KvpValue *cm_value;
1347  KvpValueType cm_type;
1348  QofSetterFunc cm_setter;
1349  const QofParam *cm_param;
1350  void (*string_setter) (QofEntity *, const gchar *);
1351  void (*time_setter) (QofEntity *, QofTime *);
1352  void (*numeric_setter) (QofEntity *, QofNumeric);
1353  void (*double_setter) (QofEntity *, gdouble);
1354  void (*boolean_setter) (QofEntity *, gboolean);
1355  void (*i32_setter) (QofEntity *, gint32);
1356  void (*i64_setter) (QofEntity *, gint64);
1357  void (*char_setter) (QofEntity *, gchar);
1358 
1359  g_return_if_fail (data && value && key);
1360  params = (QsfParam *) data;
1361  node = (xmlNodePtr) value;
1362  parameter_name = (const gchar *) key;
1363  qof_type = (gchar *) node->name;
1364  qsf_ent = params->qsf_ent;
1365  targetBook = params->book;
1366  obj_type =
1367  (gchar *) xmlGetProp (node->parent, BAD_CAST QSF_OBJECT_TYPE);
1368  if (0 == safe_strcasecmp (obj_type, parameter_name))
1369  {
1370  return;
1371  }
1372  cm_setter = qof_class_get_parameter_setter (obj_type, parameter_name);
1373  cm_param = qof_class_get_parameter (obj_type, parameter_name);
1374  object_set = params->object_set;
1375  if (safe_strcmp (qof_type, QOF_TYPE_STRING) == 0)
1376  {
1377  string_setter = (void (*)(QofEntity *, const gchar *)) cm_setter;
1378  if (string_setter != NULL)
1379  {
1380  qof_util_param_edit ((QofInstance *) qsf_ent, cm_param);
1381  string_setter (qsf_ent, (gchar *) xmlNodeGetContent (node));
1382  qof_util_param_commit ((QofInstance *) qsf_ent, cm_param);
1383  }
1384  }
1385 #ifndef QOF_DISABLE_DEPRECATED
1386  /* use convert here to read "date" */
1387  if ((params->convert != 0) &&
1388  ((safe_strcmp (qof_type, QOF_TYPE_DATE) == 0) ||
1389  (safe_strcmp (qof_type, QOF_TYPE_TIME) == 0)))
1390  {
1391  /* just reading the same value from a different tag */
1392 #else
1393  if (safe_strcmp (qof_type, QOF_TYPE_TIME) == 0)
1394  {
1395 #endif
1396  time_setter = (void (*)(QofEntity *, QofTime*)) cm_setter;
1397  if (time_setter != NULL)
1398  {
1399  QofDate *qd;
1400  QofTime *qt;
1401 
1402  qd = qof_date_parse (
1403  (const gchar*) xmlNodeGetContent (node),
1405  if(qd)
1406  {
1407  qt = qof_date_to_qtime (qd);
1408  qof_util_param_edit ((QofInstance *) qsf_ent, cm_param);
1409  time_setter (qsf_ent, qt);
1410  qof_util_param_commit ((QofInstance *) qsf_ent, cm_param);
1411  qof_date_free (qd);
1412  }
1413  else
1414  PERR (" failed to parse date string");
1415  }
1416  }
1417 #ifndef QOF_DISABLE_DEPRECATED
1418  if ((params->convert == 0) &&
1419  (safe_strcmp (qof_type, QOF_TYPE_DATE) == 0))
1420  {
1421  void (*date_setter) (QofEntity *, Timespec);
1422  struct tm qsf_time;
1423  time_t qsf_time_t;
1424  Timespec cm_date;
1425  const gchar *timechk;
1426 
1427  memset (&qsf_time, '\0', sizeof (qsf_time));
1428  cm_date.tv_nsec = 0;
1429  cm_date.tv_sec = 0;
1430  date_setter = (void (*)(QofEntity *, Timespec)) cm_setter;
1431  timechk = NULL;
1432  timechk =
1433  strptime ((char *) xmlNodeGetContent (node), QSF_XSD_TIME,
1434  &qsf_time);
1435  g_return_if_fail (timechk != NULL);
1436  qsf_time_t = mktime (&qsf_time);
1437  if (qsf_time_t != -3600)
1438  {
1439  timespecFromTime_t (&cm_date, qsf_time_t);
1440  if (date_setter != NULL)
1441  {
1442  qof_util_param_edit ((QofInstance *) qsf_ent, cm_param);
1443  date_setter (qsf_ent, cm_date);
1444  qof_util_param_commit ((QofInstance *) qsf_ent, cm_param);
1445  }
1446  }
1447  }
1448 #endif
1449  if ((safe_strcmp (qof_type, QOF_TYPE_NUMERIC) == 0) ||
1450  (safe_strcmp (qof_type, QOF_TYPE_DEBCRED) == 0))
1451  {
1452  gchar *tmp;
1453  numeric_setter = (void (*)(QofEntity *, QofNumeric)) cm_setter;
1454  tmp = (char *) xmlNodeGetContent (node);
1455  qof_numeric_from_string (tmp, &cm_numeric);
1456  g_free (tmp);
1457  if (numeric_setter != NULL)
1458  {
1459  qof_util_param_edit ((QofInstance *) qsf_ent, cm_param);
1460  numeric_setter (qsf_ent, cm_numeric);
1461  qof_util_param_commit ((QofInstance *) qsf_ent, cm_param);
1462  }
1463  }
1464  if (safe_strcmp (qof_type, QOF_TYPE_GUID) == 0)
1465  {
1466  cm_guid = g_new0 (GUID, 1);
1467  if (TRUE !=
1468  string_to_guid ((gchar *) xmlNodeGetContent (node), cm_guid))
1469  {
1470  qof_error_set_be (params->be, qof_error_register(
1471  _("The selected QSF object file '%s' contains one or "
1472  "more invalid GUIDs. The file cannot be processed - "
1473  "please check the source of the file and try again."),
1474  TRUE));
1475  PINFO (" string to guid conversion failed for %s:%s:%s",
1476  xmlNodeGetContent (node), obj_type, qof_type);
1477  return;
1478  }
1479  reference_type =
1480  (gchar *) xmlGetProp (node, BAD_CAST QSF_OBJECT_TYPE);
1481  if (0 == safe_strcmp (QOF_PARAM_GUID, reference_type))
1482  {
1483  qof_util_param_edit ((QofInstance *) qsf_ent, cm_param);
1484  qof_entity_set_guid (qsf_ent, cm_guid);
1485  qof_util_param_commit ((QofInstance *) qsf_ent, cm_param);
1486  }
1487  else
1488  {
1489  reference = qof_entity_get_reference_from (qsf_ent, cm_param);
1490  if (reference)
1491  {
1492  params->referenceList =
1493  g_list_append (params->referenceList, reference);
1494  }
1495  }
1496  }
1497  if (safe_strcmp (qof_type, QOF_TYPE_INT32) == 0)
1498  {
1499  errno = 0;
1500  cm_i32 =
1501  (gint32) strtol ((char *) xmlNodeGetContent (node), &tail, 0);
1502  if (errno == 0)
1503  {
1504  i32_setter = (void (*)(QofEntity *, gint32)) cm_setter;
1505  if (i32_setter != NULL)
1506  {
1507  qof_util_param_edit ((QofInstance *) qsf_ent, cm_param);
1508  i32_setter (qsf_ent, cm_i32);
1509  qof_util_param_commit ((QofInstance *) qsf_ent, cm_param);
1510  }
1511  }
1512  else
1513  qof_error_set_be (params->be, params->err_overflow);
1514  }
1515  if (safe_strcmp (qof_type, QOF_TYPE_INT64) == 0)
1516  {
1517  errno = 0;
1518  cm_i64 = strtoll ((gchar *) xmlNodeGetContent (node), &tail, 0);
1519  if (errno == 0)
1520  {
1521  i64_setter = (void (*)(QofEntity *, gint64)) cm_setter;
1522  if (i64_setter != NULL)
1523  {
1524  qof_util_param_edit ((QofInstance *) qsf_ent, cm_param);
1525  i64_setter (qsf_ent, cm_i64);
1526  qof_util_param_commit ((QofInstance *) qsf_ent, cm_param);
1527  }
1528  }
1529  else
1530  qof_error_set_be (params->be, params->err_overflow);
1531  }
1532  if (safe_strcmp (qof_type, QOF_TYPE_DOUBLE) == 0)
1533  {
1534  errno = 0;
1535  cm_double = strtod ((gchar *) xmlNodeGetContent (node), &tail);
1536  if (errno == 0)
1537  {
1538  double_setter = (void (*)(QofEntity *, gdouble)) cm_setter;
1539  if (double_setter != NULL)
1540  {
1541  qof_util_param_edit ((QofInstance *) qsf_ent, cm_param);
1542  double_setter (qsf_ent, cm_double);
1543  qof_util_param_commit ((QofInstance *) qsf_ent, cm_param);
1544  }
1545  }
1546  }
1547  if (safe_strcmp (qof_type, QOF_TYPE_BOOLEAN) == 0)
1548  {
1549  if (0 == safe_strcasecmp ((gchar *) xmlNodeGetContent (node),
1551  cm_boolean = TRUE;
1552  else
1553  cm_boolean = FALSE;
1554  boolean_setter = (void (*)(QofEntity *, gboolean)) cm_setter;
1555  if (boolean_setter != NULL)
1556  {
1557  qof_util_param_edit ((QofInstance *) qsf_ent, cm_param);
1558  boolean_setter (qsf_ent, cm_boolean);
1559  qof_util_param_commit ((QofInstance *) qsf_ent, cm_param);
1560  }
1561  }
1562  if (safe_strcmp (qof_type, QOF_TYPE_KVP) == 0)
1563  {
1564  cm_type =
1565  qsf_to_kvp_helper ((gchar *)
1566  xmlGetProp (node, BAD_CAST QSF_OBJECT_VALUE));
1567  if (!cm_type)
1568  return;
1569  qof_util_param_edit ((QofInstance *) qsf_ent, cm_param);
1570  cm_value =
1571  string_to_kvp_value ((gchar *) xmlNodeGetContent (node),
1572  cm_type);
1573  cm_kvp = (KvpFrame *) cm_param->param_getfcn (qsf_ent, cm_param);
1574  cm_kvp = kvp_frame_set_value (cm_kvp, (gchar *) xmlGetProp (node,
1575  BAD_CAST QSF_OBJECT_KVP), cm_value);
1576  qof_util_param_commit ((QofInstance *) qsf_ent, cm_param);
1577  g_free (cm_value);
1578  }
1579  if (safe_strcmp (qof_type, QOF_TYPE_COLLECT) == 0)
1580  {
1581  QofCollection *qsf_coll;
1582  QofIdType type;
1583  QofEntityReference *reference;
1584  QofParam *copy_param;
1585  /* retrieve the *type* of the collection, ignore any contents. */
1586  qsf_coll = cm_param->param_getfcn (qsf_ent, cm_param);
1587  type = qof_collection_get_type (qsf_coll);
1588  cm_guid = g_new0 (GUID, 1);
1589  if (TRUE !=
1590  string_to_guid ((gchar *) xmlNodeGetContent (node), cm_guid))
1591  {
1592  qof_error_set_be (params->be, (qof_error_register(
1593  _("The selected QSF object file '%s' contains one or "
1594  "more invalid 'collect' values. The file cannot be processed - "
1595  "please check the source of the file and try again."),
1596  TRUE)));
1597  PINFO (" string to guid collect failed for %s",
1598  xmlNodeGetContent (node));
1599  return;
1600  }
1601  /* create a QofEntityReference with this type and GUID.
1602  there is only one entity each time.
1603  cm_guid contains the GUID of the reference.
1604  type is the type of the reference. */
1605  reference = g_new0 (QofEntityReference, 1);
1606  reference->type = g_strdup (qsf_ent->e_type);
1607  reference->ref_guid = cm_guid;
1608  reference->ent_guid = &qsf_ent->guid;
1609  copy_param = g_new0 (QofParam, 1);
1610  copy_param->param_name = g_strdup (cm_param->param_name);
1611  copy_param->param_type = g_strdup (cm_param->param_type);
1612  reference->param = copy_param;
1613  params->referenceList =
1614  g_list_append (params->referenceList, reference);
1615  }
1616  if (safe_strcmp (qof_type, QOF_TYPE_CHAR) == 0)
1617  {
1618  char_getter = (gchar (*)(xmlNodePtr)) xmlNodeGetContent;
1619  cm_char = char_getter (node);
1620  char_setter = (void (*)(QofEntity *, gchar)) cm_setter;
1621  if (char_setter != NULL)
1622  {
1623  qof_util_param_edit ((QofInstance *) qsf_ent, cm_param);
1624  char_setter (qsf_ent, cm_char);
1625  qof_util_param_commit ((QofInstance *) qsf_ent, cm_param);
1626  }
1627  }
1628 }
1629 
1630 static QofBackend *
1631 qsf_backend_new (void)
1632 {
1633  QSFBackend *qsf_be;
1634  QofBackend *be;
1635 
1636  qsf_be = g_new0 (QSFBackend, 1);
1637  be = (QofBackend *) qsf_be;
1638  qof_backend_init (be);
1639  qsf_be->params = g_new0 (QsfParam, 1);
1640  qsf_be->params->be = be;
1641  qsf_param_init (qsf_be->params);
1642  qsf_be->be.session_begin = qsf_session_begin;
1643 
1644  be->session_end = qsf_session_end;
1645  be->destroy_backend = qsf_destroy_backend;
1646  be->load = qsf_file_type;
1647  be->save_may_clobber_data = NULL;
1648  /* The QSF backend will always load and save the entire QSF XML file. */
1649  be->begin = NULL;
1650  be->commit = NULL;
1651  be->rollback = NULL;
1652  /* QSF uses the built-in SQL, not a dedicated SQL server. */
1653  be->compile_query = NULL;
1654  be->free_query = NULL;
1655  be->run_query = NULL;
1656  be->counter = NULL;
1657  /* The QSF backend is not multi-user. */
1658  be->events_pending = NULL;
1659  be->process_events = NULL;
1660 
1661  be->sync = qsf_write_file;
1662  /* use for maps, later. */
1663  be->load_config = qsf_load_config;
1664  be->get_config = qsf_get_config;
1665 
1666  qsf_be->fullpath = NULL;
1667  return be;
1668 }
1669 
1670 /* The QOF method of loading each backend.
1671 QSF is loaded as a GModule using the QOF method - QofBackendProvider.
1672 */
1673 static void
1674 qsf_provider_free (QofBackendProvider * prov)
1675 {
1676  prov->provider_name = NULL;
1677  prov->access_method = NULL;
1678  g_free (prov);
1679 }
1680 
1681 void
1683 {
1684  QofBackendProvider *prov;
1685 
1686  bindtextdomain (PACKAGE, LOCALE_DIR);
1687  prov = g_new0 (QofBackendProvider, 1);
1688  prov->provider_name = "QSF Backend Version 0.4";
1689  prov->access_method = "file";
1690  prov->partial_book_supported = TRUE;
1691  prov->backend_new = qsf_backend_new;
1692  prov->check_data_type = qsf_determine_file_type;
1693  prov->provider_free = qsf_provider_free;
1695 }