QOF  0.7.5
qofsession.c
Go to the documentation of this file.
1 /********************************************************************\
2  * qofsesssion.c -- session access (connection to backend) *
3  * *
4  * This program is free software; you can redistribute it and/or *
5  * modify it under the terms of the GNU General Public License as *
6  * published by the Free Software Foundation; either version 2 of *
7  * the License, or (at your option) any later version. *
8  * *
9  * This program is distributed in the hope that it will be useful, *
10  * but WITHOUT ANY WARRANTY; without even the implied warranty of *
11  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the *
12  * GNU General Public License for more details. *
13  * *
14  * You should have received a copy of the GNU General Public License*
15  * along with this program; if not, contact: *
16  * *
17  * Free Software Foundation Voice: +1-617-542-5942 *
18  * 51 Franklin Street, Fifth Floor Fax: +1-617-542-2652 *
19  * Boston, MA 02110-1301, USA gnu@gnu.org *
20 \********************************************************************/
21 
34 #include "config.h"
35 
36 #include <stdlib.h>
37 #include <string.h>
38 #include <sys/types.h>
39 #include <sys/stat.h>
40 #include <unistd.h>
41 #include <glib.h>
42 #include <libintl.h>
43 #include "qof.h"
44 #include "qoferror-p.h"
45 #include "qofbackend-p.h"
46 #include "qofbook-p.h"
47 #include "qofsession-p.h"
48 #include "qofobject-p.h"
49 
50 #define _(String) dgettext (GETTEXT_PACKAGE, String)
51 
52 static GHookList *session_closed_hooks = NULL;
53 static QofLogModule log_module = QOF_MOD_SESSION;
54 static GSList *provider_list = NULL;
55 
56 /* ============================================================= */
57 
58 void
60 {
61  provider_list = g_slist_prepend (provider_list, prov);
62 }
63 
64 /* =========================================================== */
65 
66 /* hook routines */
67 
68 void
69 qof_session_add_close_hook (GFunc fn, gpointer data)
70 {
71  GHook *hook;
72 
73  if (session_closed_hooks == NULL)
74  {
75  session_closed_hooks = malloc (sizeof (GHookList)); /* LEAKED */
76  g_hook_list_init (session_closed_hooks, sizeof (GHook));
77  }
78 
79  hook = g_hook_alloc (session_closed_hooks);
80  if (!hook)
81  return;
82 
83  hook->func = (GHookFunc) fn;
84  hook->data = data;
85  g_hook_append (session_closed_hooks, hook);
86 }
87 
88 void
90 {
91  GHook *hook;
92  GFunc fn;
93 
94  if (session_closed_hooks == NULL)
95  return;
96 
97  hook = g_hook_first_valid (session_closed_hooks, FALSE);
98  while (hook)
99  {
100  fn = (GFunc) hook->func;
101  fn (session, hook->data);
102  hook = g_hook_next_valid (session_closed_hooks, hook, FALSE);
103  }
104 }
105 
106 /* =============================================================== */
107 
108 static void
109 qof_session_init (QofSession * session)
110 {
111  if (!session)
112  return;
113 
114 #ifndef QOF_DISABLE_DEPRECATED
115  session->entity.e_type = QOF_ID_SESSION;
116 #endif
117  session->books = g_list_append (NULL, qof_book_new ());
118  session->book_id = NULL;
119  session->backend = NULL;
120  qof_error_init ();
121 }
122 
123 QofSession *
124 qof_session_new (void)
125 {
126  QofSession *session = g_new0 (QofSession, 1);
127  qof_session_init (session);
128  return session;
129 }
130 
131 QofBook *
132 qof_session_get_book (QofSession * session)
133 {
134  GList *node;
135  if (!session)
136  return NULL;
137 
138  for (node = session->books; node; node = node->next)
139  {
140  QofBook *book = node->data;
141  if ('y' == book->book_open)
142  return book;
143  }
144  return NULL;
145 }
146 
147 void
149 {
150  GList *node;
151  if (!session)
152  return;
153 
154  ENTER (" sess=%p book=%p", session, addbook);
155 
156  /* See if this book is already there ... */
157  for (node = session->books; node; node = node->next)
158  {
159  QofBook *book = node->data;
160  if (addbook == book)
161  return;
162  }
163 
164  if ('y' == addbook->book_open)
165  {
166  /* hack alert -- someone should free all the books in the list,
167  * but it should probably not be us ... since the books backends
168  * should be shutdown first, etc */
169 /* XXX this should probably be an error XXX */
170  g_list_free (session->books);
171  session->books = g_list_append (NULL, addbook);
172  }
173  else
174  {
175 /* XXX Need to tell the backend to add a book as well */
176  session->books = g_list_append (session->books, addbook);
177  }
178 
179  qof_book_set_backend (addbook, session->backend);
180  LEAVE (" ");
181 }
182 
183 QofBackend *
184 qof_session_get_backend (QofSession * session)
185 {
186  if (!session)
187  return NULL;
188  return session->backend;
189 }
190 
191 const gchar *
193 {
194  if (!session)
195  return NULL;
196  if (!session->backend)
197  return NULL;
198  return session->backend->fullpath;
199 }
200 
201 const gchar *
202 qof_session_get_url (QofSession * session)
203 {
204  if (!session)
205  return NULL;
206  return session->book_id;
207 }
208 
209 /* =============================================================== */
210 
211 typedef struct qof_entity_copy_data
212 {
213  QofEntity *from;
214  QofEntity *to;
215  QofParam *param;
216  GList *referenceList;
217  GSList *param_list;
218  QofSession *new_session;
219  gboolean error;
220 } QofEntityCopyData;
221 
222 static void
223 qof_book_set_partial (QofBook * book)
224 {
225  gboolean partial;
226 
227  partial =
228  (gboolean)
229  GPOINTER_TO_INT (qof_book_get_data (book, PARTIAL_QOFBOOK));
230  if (!partial)
231  {
232  qof_book_set_data (book, PARTIAL_QOFBOOK, GINT_TO_POINTER (TRUE));
233  }
234 }
235 
236 void
238  QofEntityReference * reference)
239 {
240  QofBook *book;
241  GList *book_ref_list;
242 
243  book = qof_session_get_book (session);
244  book_ref_list = (GList *) qof_book_get_data (book, ENTITYREFERENCE);
245  book_ref_list = g_list_append (book_ref_list, reference);
246  qof_book_set_data (book, ENTITYREFERENCE, book_ref_list);
247  qof_book_set_partial (book);
248 }
249 
250 static void
251 qof_entity_param_cb (QofParam * param, gpointer data)
252 {
253  QofEntityCopyData *qecd;
254 
255  g_return_if_fail (data != NULL);
256  qecd = (QofEntityCopyData *) data;
257  g_return_if_fail (param != NULL);
258  /* KVP doesn't need a set routine to be copied. */
259  if (0 == safe_strcmp (param->param_type, QOF_TYPE_KVP))
260  {
261  qecd->param_list = g_slist_prepend (qecd->param_list, param);
262  return;
263  }
264  if ((param->param_getfcn != NULL) && (param->param_setfcn != NULL))
265  {
266  qecd->param_list = g_slist_prepend (qecd->param_list, param);
267  }
268 }
269 
270 static void
271 col_ref_cb (QofEntity * ref_ent, gpointer user_data)
272 {
273  QofEntityReference *ref;
274  QofEntityCopyData *qecd;
275  QofEntity *ent;
276  const GUID *cm_guid;
277  gchar cm_sa[GUID_ENCODING_LENGTH + 1];
278  gchar *cm_string;
279 
280  qecd = (QofEntityCopyData *) user_data;
281  ent = qecd->from;
282  ref = g_new0 (QofEntityReference, 1);
283  ref->type = ent->e_type;
284  ref->ref_guid = g_new (GUID, 1);
285  ref->ent_guid = &ent->guid;
286  ref->param = qof_class_get_parameter (ent->e_type,
287  qecd->param->param_name);
288  cm_guid = qof_entity_get_guid (ref_ent);
289  guid_to_string_buff (cm_guid, cm_sa);
290  cm_string = g_strdup (cm_sa);
291  if (TRUE == string_to_guid (cm_string, ref->ref_guid))
292  {
293  g_free (cm_string);
294  qof_session_update_reference_list (qecd->new_session, ref);
295  }
296 }
297 
298 static void
299 qof_entity_foreach_copy (gpointer data, gpointer user_data)
300 {
301  QofEntity *importEnt, *targetEnt /*, *referenceEnt */ ;
302  QofEntityCopyData *context;
303  QofEntityReference *reference;
304  gboolean registered_type;
305  /* cm_ prefix used for variables that hold the data to commit */
306  QofParam *cm_param;
307  gchar *cm_string, *cm_char;
308  const GUID *cm_guid;
309  KvpFrame *cm_kvp;
310  QofCollection *cm_col;
311  /* function pointers and variables for parameter getters that don't use pointers normally */
312  QofNumeric cm_numeric, (*numeric_getter) (QofEntity *, QofParam *);
313  gdouble cm_double, (*double_getter) (QofEntity *, QofParam *);
314  gboolean cm_boolean, (*boolean_getter) (QofEntity *, QofParam *);
315  gint32 cm_i32, (*int32_getter) (QofEntity *, QofParam *);
316  gint64 cm_i64, (*int64_getter) (QofEntity *, QofParam *);
317  /* function pointers to the parameter setters */
318  void (*string_setter) (QofEntity *, const gchar *);
319  void (*numeric_setter) (QofEntity *, QofNumeric);
320  void (*guid_setter) (QofEntity *, const GUID *);
321  void (*double_setter) (QofEntity *, gdouble);
322  void (*boolean_setter) (QofEntity *, gboolean);
323  void (*i32_setter) (QofEntity *, gint32);
324  void (*i64_setter) (QofEntity *, gint64);
325  void (*char_setter) (QofEntity *, gchar *);
326  void (*kvp_frame_setter) (QofEntity *, KvpFrame *);
327 
328  g_return_if_fail (user_data != NULL);
329  context = (QofEntityCopyData *) user_data;
330  importEnt = context->from;
331  targetEnt = context->to;
332  registered_type = FALSE;
333  cm_param = (QofParam *) data;
334  g_return_if_fail (cm_param != NULL);
335  context->param = cm_param;
336  if (safe_strcmp (cm_param->param_type, QOF_TYPE_STRING) == 0)
337  {
338  cm_string = (gchar *) cm_param->param_getfcn (importEnt, cm_param);
339  if (cm_string)
340  {
341  string_setter =
342  (void (*)(QofEntity *,
343  const char *)) cm_param->param_setfcn;
344  if (string_setter != NULL)
345  {
346  qof_util_param_edit ((QofInstance *) targetEnt, cm_param);
347  string_setter (targetEnt, cm_string);
348  qof_util_param_commit ((QofInstance *) targetEnt, cm_param);
349  }
350  }
351  registered_type = TRUE;
352  }
353  if (safe_strcmp (cm_param->param_type, QOF_TYPE_TIME) == 0)
354  {
355  QofTime *qt;
356  void (*time_setter) (QofEntity *, QofTime *);
357 
358  qt = cm_param->param_getfcn (importEnt, cm_param);
359  time_setter =
360  (void (*)(QofEntity *, QofTime*))cm_param->param_setfcn;
361  if (time_setter != NULL)
362  {
363  qof_util_param_edit ((QofInstance *) targetEnt, cm_param);
364  time_setter (targetEnt, qt);
365  qof_util_param_commit ((QofInstance *) targetEnt, cm_param);
366  }
367  registered_type = TRUE;
368  }
369 #ifndef QOF_DISABLE_DEPRECATED
370  if (safe_strcmp (cm_param->param_type, QOF_TYPE_DATE) == 0)
371  {
372  Timespec cm_date, (*date_getter) (QofEntity *, QofParam *);
373  void (*date_setter) (QofEntity *, Timespec);
374 
375  cm_date.tv_nsec = 0;
376  cm_date.tv_sec = 0;
377  date_getter =
378  (Timespec (*)(QofEntity *, QofParam *)) cm_param->param_getfcn;
379  cm_date = date_getter (importEnt, cm_param);
380  date_setter =
381  (void (*)(QofEntity *, Timespec)) cm_param->param_setfcn;
382  if (date_setter != NULL)
383  {
384  qof_util_param_edit ((QofInstance *) targetEnt, cm_param);
385  date_setter (targetEnt, cm_date);
386  qof_util_param_commit ((QofInstance *) targetEnt, cm_param);
387  }
388  registered_type = TRUE;
389  }
390 #endif
391  if ((safe_strcmp (cm_param->param_type, QOF_TYPE_NUMERIC) == 0) ||
392  (safe_strcmp (cm_param->param_type, QOF_TYPE_DEBCRED) == 0))
393  {
394  numeric_getter =
395  (QofNumeric (*)(QofEntity *,
396  QofParam *)) cm_param->param_getfcn;
397  cm_numeric = numeric_getter (importEnt, cm_param);
398  numeric_setter =
399  (void (*)(QofEntity *, QofNumeric)) cm_param->param_setfcn;
400  if (numeric_setter != NULL)
401  {
402  qof_util_param_edit ((QofInstance *) targetEnt, cm_param);
403  numeric_setter (targetEnt, cm_numeric);
404  qof_util_param_commit ((QofInstance *) targetEnt, cm_param);
405  }
406  registered_type = TRUE;
407  }
408  if (safe_strcmp (cm_param->param_type, QOF_TYPE_GUID) == 0)
409  {
410  cm_guid =
411  (const GUID *) cm_param->param_getfcn (importEnt, cm_param);
412  guid_setter =
413  (void (*)(QofEntity *, const GUID *)) cm_param->param_setfcn;
414  if (guid_setter != NULL)
415  {
416  qof_util_param_edit ((QofInstance *) targetEnt, cm_param);
417  guid_setter (targetEnt, cm_guid);
418  qof_util_param_commit ((QofInstance *) targetEnt, cm_param);
419  }
420  registered_type = TRUE;
421  }
422  if (safe_strcmp (cm_param->param_type, QOF_TYPE_INT32) == 0)
423  {
424  int32_getter =
425  (gint32 (*)(QofEntity *, QofParam *)) cm_param->param_getfcn;
426  cm_i32 = int32_getter (importEnt, cm_param);
427  i32_setter =
428  (void (*)(QofEntity *, gint32)) cm_param->param_setfcn;
429  if (i32_setter != NULL)
430  {
431  qof_util_param_edit ((QofInstance *) targetEnt, cm_param);
432  i32_setter (targetEnt, cm_i32);
433  qof_util_param_commit ((QofInstance *) targetEnt, cm_param);
434  }
435  registered_type = TRUE;
436  }
437  if (safe_strcmp (cm_param->param_type, QOF_TYPE_INT64) == 0)
438  {
439  int64_getter =
440  (gint64 (*)(QofEntity *, QofParam *)) cm_param->param_getfcn;
441  cm_i64 = int64_getter (importEnt, cm_param);
442  i64_setter =
443  (void (*)(QofEntity *, gint64)) cm_param->param_setfcn;
444  if (i64_setter != NULL)
445  {
446  qof_util_param_edit ((QofInstance *) targetEnt, cm_param);
447  i64_setter (targetEnt, cm_i64);
448  qof_util_param_commit ((QofInstance *) targetEnt, cm_param);
449  }
450  registered_type = TRUE;
451  }
452  if (safe_strcmp (cm_param->param_type, QOF_TYPE_DOUBLE) == 0)
453  {
454  double_getter =
455  (gdouble (*)(QofEntity *, QofParam *)) cm_param->param_getfcn;
456  cm_double = double_getter (importEnt, cm_param);
457  double_setter =
458  (void (*)(QofEntity *, gdouble)) cm_param->param_setfcn;
459  if (double_setter != NULL)
460  {
461  qof_util_param_edit ((QofInstance *) targetEnt, cm_param);
462  double_setter (targetEnt, cm_double);
463  qof_util_param_commit ((QofInstance *) targetEnt, cm_param);
464  }
465  registered_type = TRUE;
466  }
467  if (safe_strcmp (cm_param->param_type, QOF_TYPE_BOOLEAN) == 0)
468  {
469  boolean_getter =
470  (gboolean (*)(QofEntity *, QofParam *)) cm_param->param_getfcn;
471  cm_boolean = boolean_getter (importEnt, cm_param);
472  boolean_setter =
473  (void (*)(QofEntity *, gboolean)) cm_param->param_setfcn;
474  if (boolean_setter != NULL)
475  {
476  qof_util_param_edit ((QofInstance *) targetEnt, cm_param);
477  boolean_setter (targetEnt, cm_boolean);
478  qof_util_param_commit ((QofInstance *) targetEnt, cm_param);
479  }
480  registered_type = TRUE;
481  }
482  if (safe_strcmp (cm_param->param_type, QOF_TYPE_KVP) == 0)
483  {
484  cm_kvp = (KvpFrame *) cm_param->param_getfcn (importEnt, cm_param);
485  kvp_frame_setter =
486  (void (*)(QofEntity *, KvpFrame *)) cm_param->param_setfcn;
487  if (kvp_frame_setter != NULL)
488  {
489  qof_util_param_edit ((QofInstance *) targetEnt, cm_param);
490  kvp_frame_setter (targetEnt, cm_kvp);
491  qof_util_param_commit ((QofInstance *) targetEnt, cm_param);
492  }
493  else
494  {
495  QofInstance *target_inst;
496 
497  target_inst = (QofInstance *) targetEnt;
498  kvp_frame_delete (target_inst->kvp_data);
499  target_inst->kvp_data = kvp_frame_copy (cm_kvp);
500  }
501  registered_type = TRUE;
502  }
503  if (safe_strcmp (cm_param->param_type, QOF_TYPE_CHAR) == 0)
504  {
505  cm_char = (gchar *) cm_param->param_getfcn (importEnt, cm_param);
506  char_setter =
507  (void (*)(QofEntity *, char *)) cm_param->param_setfcn;
508  if (char_setter != NULL)
509  {
510  qof_util_param_edit ((QofInstance *) targetEnt, cm_param);
511  char_setter (targetEnt, cm_char);
512  qof_util_param_commit ((QofInstance *) targetEnt, cm_param);
513  }
514  registered_type = TRUE;
515  }
516  if (safe_strcmp (cm_param->param_type, QOF_TYPE_COLLECT) == 0)
517  {
518  cm_col =
519  (QofCollection *) cm_param->param_getfcn (importEnt, cm_param);
520  if (cm_col)
521  {
522  /* create one reference for each member of the collection. */
523  qof_collection_foreach (cm_col, col_ref_cb, context);
524  }
525  registered_type = TRUE;
526  }
527  if (registered_type == FALSE)
528  {
529 /* referenceEnt = (QofEntity*)cm_param->param_getfcn(importEnt, cm_param);
530  if(!referenceEnt) { return; }
531  if(!referenceEnt->e_type) { return; }*/
532  reference = qof_entity_get_reference_from (importEnt, cm_param);
533  if (reference)
534  {
535  qof_session_update_reference_list (context->new_session,
536  reference);
537  }
538  }
539 }
540 
541 static gboolean
542 qof_entity_guid_match (QofSession * new_session, QofEntity * original)
543 {
544  QofEntity *copy;
545  const GUID *g;
546  QofIdTypeConst type;
547  QofBook *targetBook;
548  QofCollection *coll;
549 
550  copy = NULL;
551  g_return_val_if_fail (original != NULL, FALSE);
552  targetBook = qof_session_get_book (new_session);
553  g_return_val_if_fail (targetBook != NULL, FALSE);
554  g = qof_entity_get_guid (original);
555  type = g_strdup (original->e_type);
556  coll = qof_book_get_collection (targetBook, type);
557  copy = qof_collection_lookup_entity (coll, g);
558  if (copy)
559  {
560  return TRUE;
561  }
562  return FALSE;
563 }
564 
565 static void
566 qof_entity_list_foreach (gpointer data, gpointer user_data)
567 {
568  QofEntityCopyData *qecd;
569  QofEntity *original;
570  QofInstance *inst;
571  QofBook *book;
572  const GUID *g;
573 
574  g_return_if_fail (data != NULL);
575  original = (QofEntity *) data;
576  g_return_if_fail (user_data != NULL);
577  qecd = (QofEntityCopyData *) user_data;
578  if (qof_entity_guid_match (qecd->new_session, original))
579  {
580  return;
581  }
582  qecd->from = original;
583  if (!qof_object_compliance (original->e_type, FALSE))
584  {
585  qecd->error = TRUE;
586  return;
587  }
588  book = qof_session_get_book (qecd->new_session);
589  inst =
590  (QofInstance *) qof_object_new_instance (original->e_type, book);
591  if (!inst)
592  {
593  PERR (" failed to create new entity type=%s.", original->e_type);
594  qecd->error = TRUE;
595  return;
596  }
597  qecd->to = &inst->entity;
598  g = qof_entity_get_guid (original);
599  qof_entity_set_guid (qecd->to, g);
600  if (qecd->param_list != NULL)
601  {
602  g_slist_free (qecd->param_list);
603  qecd->param_list = NULL;
604  }
605  qof_class_param_foreach (original->e_type, qof_entity_param_cb, qecd);
606  g_slist_foreach (qecd->param_list, qof_entity_foreach_copy, qecd);
607 }
608 
609 static void
610 qof_entity_coll_foreach (QofEntity * original, gpointer user_data)
611 {
612  QofEntityCopyData *qecd;
613  const GUID *g;
614  QofBook *targetBook;
615  QofCollection *coll;
616  QofEntity *copy;
617 
618  g_return_if_fail (user_data != NULL);
619  copy = NULL;
620  qecd = (QofEntityCopyData *) user_data;
621  targetBook = qof_session_get_book (qecd->new_session);
622  g = qof_entity_get_guid (original);
623  coll = qof_book_get_collection (targetBook, original->e_type);
624  copy = qof_collection_lookup_entity (coll, g);
625  if (copy)
626  {
627  qecd->error = TRUE;
628  }
629 }
630 
631 static void
632 qof_entity_coll_copy (QofEntity * original, gpointer user_data)
633 {
634  QofEntityCopyData *qecd;
635  QofBook *book;
636  QofInstance *inst;
637  const GUID *g;
638 
639  g_return_if_fail (user_data != NULL);
640  qecd = (QofEntityCopyData *) user_data;
641  book = qof_session_get_book (qecd->new_session);
642  if (!qof_object_compliance (original->e_type, TRUE))
643  {
644  return;
645  }
646  inst =
647  (QofInstance *) qof_object_new_instance (original->e_type, book);
648  qecd->to = &inst->entity;
649  qecd->from = original;
650  g = qof_entity_get_guid (original);
651  qof_entity_set_guid (qecd->to, g);
652  g_slist_foreach (qecd->param_list, qof_entity_foreach_copy, qecd);
653 }
654 
655 gboolean
657 {
658  QofEntityCopyData qecd;
659  QofInstance *inst;
660  QofBook *book;
661 
662  if (!new_session || !original)
663  return FALSE;
664  if (qof_entity_guid_match (new_session, original))
665  return FALSE;
666  if (!qof_object_compliance (original->e_type, TRUE))
667  return FALSE;
669  qecd.param_list = NULL;
670  book = qof_session_get_book (new_session);
671  qecd.new_session = new_session;
672  qof_book_set_partial (book);
673  inst =
674  (QofInstance *) qof_object_new_instance (original->e_type, book);
675  qecd.to = &inst->entity;
676  qecd.from = original;
677  qof_entity_set_guid (qecd.to, qof_entity_get_guid (original));
678  qof_class_param_foreach (original->e_type, qof_entity_param_cb, &qecd);
679  if (g_slist_length (qecd.param_list) == 0)
680  return FALSE;
681  g_slist_foreach (qecd.param_list, qof_entity_foreach_copy, &qecd);
682  g_slist_free (qecd.param_list);
683  qof_event_resume ();
684  return TRUE;
685 }
686 
687 gboolean
688 qof_entity_copy_list (QofSession * new_session, GList * entity_list)
689 {
690  QofEntityCopyData *qecd;
691 
692  if (!new_session || !entity_list)
693  return FALSE;
694  ENTER (" list=%d", g_list_length (entity_list));
695  qecd = g_new0 (QofEntityCopyData, 1);
697  qecd->param_list = NULL;
698  qecd->new_session = new_session;
699  qof_book_set_partial (qof_session_get_book (new_session));
700  g_list_foreach (entity_list, qof_entity_list_foreach, qecd);
701  qof_event_resume ();
702  if (qecd->error)
703  PWARN (" some/all entities in the list could not be copied.");
704  g_free (qecd);
705  LEAVE (" ");
706  return TRUE;
707 }
708 
709 gboolean
711  QofCollection * entity_coll)
712 {
713  QofEntityCopyData qecd;
714 
715  g_return_val_if_fail (new_session, FALSE);
716  if (!entity_coll)
717  {
718  return FALSE;
719  }
721  qecd.param_list = NULL;
722  qecd.new_session = new_session;
723  qof_book_set_partial (qof_session_get_book (qecd.new_session));
724  qof_collection_foreach (entity_coll, qof_entity_coll_foreach, &qecd);
726  qof_entity_param_cb, &qecd);
727  qof_collection_foreach (entity_coll, qof_entity_coll_copy, &qecd);
728  if (qecd.param_list != NULL)
729  {
730  g_slist_free (qecd.param_list);
731  }
732  qof_event_resume ();
733  return TRUE;
734 }
735 
736 struct recurse_s
737 {
738  QofSession *session;
739  gboolean success;
740  GList *ref_list;
741  GList *ent_list;
742 };
743 
744 static void
745 recurse_collection_cb (QofEntity * ent, gpointer user_data)
746 {
747  struct recurse_s *store;
748 
749  if (user_data == NULL)
750  {
751  return;
752  }
753  store = (struct recurse_s *) user_data;
754  if (!ent || !store)
755  {
756  return;
757  }
758  store->success = qof_entity_copy_to_session (store->session, ent);
759  if (store->success)
760  {
761  store->ent_list = g_list_append (store->ent_list, ent);
762  }
763 }
764 
765 static void
766 recurse_ent_cb (QofEntity * ent, gpointer user_data)
767 {
768  GList *ref_list, *i, *j, *ent_list, *child_list;
769  QofParam *ref_param;
770  QofEntity *ref_ent, *child_ent;
771  QofSession *session;
772  struct recurse_s *store;
773  gboolean success;
774 
775  if (user_data == NULL)
776  {
777  return;
778  }
779  store = (struct recurse_s *) user_data;
780  session = store->session;
781  success = store->success;
782  ref_list = NULL;
783  child_ent = NULL;
784  ref_list = g_list_copy (store->ref_list);
785  if ((!session) || (!ent))
786  {
787  return;
788  }
789  ent_list = NULL;
790  child_list = NULL;
791  i = NULL;
792  j = NULL;
793  for (i = ref_list; i != NULL; i = i->next)
794  {
795  if (i->data == NULL)
796  {
797  continue;
798  }
799  ref_param = (QofParam *) i->data;
800  if (ref_param->param_name == NULL)
801  {
802  continue;
803  }
804  if (0 == safe_strcmp (ref_param->param_type, QOF_TYPE_COLLECT))
805  {
806  QofCollection *col;
807 
808  col = ref_param->param_getfcn (ent, ref_param);
809  if (col)
810  {
811  qof_collection_foreach (col, recurse_collection_cb, store);
812  }
813  continue;
814  }
815  ref_ent = (QofEntity *) ref_param->param_getfcn (ent, ref_param);
816  if ((ref_ent) && (ref_ent->e_type))
817  {
818  store->success = qof_entity_copy_to_session (session, ref_ent);
819  if (store->success)
820  {
821  ent_list = g_list_append (ent_list, ref_ent);
822  }
823  }
824  }
825  for (i = ent_list; i != NULL; i = i->next)
826  {
827  if (i->data == NULL)
828  {
829  continue;
830  }
831  child_ent = (QofEntity *) i->data;
832  if (child_ent == NULL)
833  {
834  continue;
835  }
836  ref_list = qof_class_get_referenceList (child_ent->e_type);
837  for (j = ref_list; j != NULL; j = j->next)
838  {
839  if (j->data == NULL)
840  {
841  continue;
842  }
843  ref_param = (QofParam *) j->data;
844  ref_ent = ref_param->param_getfcn (child_ent, ref_param);
845  if (ref_ent != NULL)
846  {
847  success = qof_entity_copy_to_session (session, ref_ent);
848  if (success)
849  {
850  child_list = g_list_append (child_list, ref_ent);
851  }
852  }
853  }
854  }
855  for (i = child_list; i != NULL; i = i->next)
856  {
857  if (i->data == NULL)
858  {
859  continue;
860  }
861  ref_ent = (QofEntity *) i->data;
862  if (ref_ent == NULL)
863  {
864  continue;
865  }
866  ref_list = qof_class_get_referenceList (ref_ent->e_type);
867  for (j = ref_list; j != NULL; j = j->next)
868  {
869  if (j->data == NULL)
870  {
871  continue;
872  }
873  ref_param = (QofParam *) j->data;
874  child_ent = ref_param->param_getfcn (ref_ent, ref_param);
875  if (child_ent != NULL)
876  {
877  qof_entity_copy_to_session (session, child_ent);
878  }
879  }
880  }
881 }
882 
883 gboolean
885 {
886  struct recurse_s store;
887  gboolean success;
888 
889  if ((!new_session) || (!coll))
890  {
891  return FALSE;
892  }
893  store.session = new_session;
894  success = TRUE;
895  store.success = success;
896  store.ent_list = NULL;
897  store.ref_list =
899  success = qof_entity_copy_coll (new_session, coll);
900  if (success)
901  {
902  qof_collection_foreach (coll, recurse_ent_cb, &store);
903  }
904  return success;
905 }
906 
907 gboolean
909 {
910  struct recurse_s store;
911  QofCollection *coll;
912  gboolean success;
913 
914  if ((!new_session) || (!ent))
915  {
916  return FALSE;
917  }
918  store.session = new_session;
919  success = TRUE;
920  store.success = success;
921  store.ref_list = qof_class_get_referenceList (ent->e_type);
922  success = qof_entity_copy_to_session (new_session, ent);
923  if (success == TRUE)
924  {
925  coll =
926  qof_book_get_collection (qof_session_get_book (new_session),
927  ent->e_type);
928  if (coll)
929  {
930  qof_collection_foreach (coll, recurse_ent_cb, &store);
931  }
932  }
933  return success;
934 }
935 
936 
937 /* ============================================================== */
938 
942 struct backend_providers
943 {
944  const gchar *libdir;
945  const gchar *filename;
946  const gchar *init_fcn;
947 };
948 
949 /* All available QOF backends need to be described here
950 and the last entry must be three NULL's.
951 Remember: Use the libdir from the current build environment
952 and use JUST the module name without .so - .so is not portable! */
953 struct backend_providers backend_list[] = {
954  {QOF_LIB_DIR, QSF_BACKEND_LIB, QSF_MODULE_INIT},
955  {QOF_LIB_DIR, "libqof-backend-sqlite", "qof_sqlite_provider_init"},
956 #ifdef HAVE_GDA
957  {QOF_LIB_DIR, "libqof-backend-gda", "qof_gda_provider_init"},
958 #endif
959 #ifdef HAVE_DWI
960  {QOF_LIB_DIR, "libqof_backend_dwi", "dwiend_provider_init"},
961 #endif
962  {NULL, NULL, NULL}
963 };
964 
965 static void
966 qof_session_load_backend (QofSession * session, gchar *access_method)
967 {
968  GSList *p;
969  GList *node;
970  QofBackendProvider *prov;
971  QofBook *book;
972  gint num;
973  gboolean prov_type;
974  gboolean (*type_check) (const gchar *);
975 
976  ENTER (" list=%d", g_slist_length (provider_list));
977  prov_type = FALSE;
978  if (NULL == provider_list)
979  {
980  for (num = 0; backend_list[num].filename != NULL; num++)
981  {
982  if (!qof_load_backend_library (backend_list[num].libdir,
983  backend_list[num].filename,
984  backend_list[num].init_fcn))
985  {
986  PWARN (" failed to load %s from %s using %s",
987  backend_list[num].filename, backend_list[num].libdir,
988  backend_list[num].init_fcn);
989  }
990  }
991  }
992  p = g_slist_copy (provider_list);
993  while (p != NULL)
994  {
995  prov = p->data;
996  /* Does this provider handle the desired access method? */
997  if (0 == strcasecmp (access_method, prov->access_method))
998  {
999  /* More than one backend could provide this
1000  access method, check file type compatibility. */
1001  type_check =
1002  (gboolean (*)(const gchar *)) prov->check_data_type;
1003  prov_type = (type_check) (session->book_id);
1004  if (!prov_type)
1005  {
1006  PINFO (" %s not usable", prov->provider_name);
1007  p = p->next;
1008  continue;
1009  }
1010  PINFO (" selected %s", prov->provider_name);
1011  if (NULL == prov->backend_new)
1012  {
1013  p = p->next;
1014  continue;
1015  }
1016  /* Use the providers creation callback */
1017  session->backend = (*(prov->backend_new)) ();
1018  session->backend->provider = prov;
1019  /* Tell the books about the backend that they'll be using. */
1020  for (node = session->books; node; node = node->next)
1021  {
1022  book = node->data;
1023  qof_book_set_backend (book, session->backend);
1024  }
1025  LEAVE (" ");
1026  return;
1027  }
1028  p = p->next;
1029  }
1030  LEAVE (" ");
1031 }
1032 
1033 /* =============================================================== */
1034 
1035 static void
1036 qof_session_destroy_backend (QofSession * session)
1037 {
1038  g_return_if_fail (session);
1039 
1040  if (session->backend)
1041  {
1042  /* Then destroy the backend */
1043  if (session->backend->destroy_backend)
1044  {
1045  session->backend->destroy_backend (session->backend);
1046  }
1047  else
1048  {
1049  g_free (session->backend);
1050  }
1051  }
1052 
1053  session->backend = NULL;
1054 }
1055 
1056 void
1057 qof_session_begin (QofSession * session, const gchar *book_id,
1058  gboolean ignore_lock, gboolean create_if_nonexistent)
1059 {
1060  gchar *p, *access_method;
1061 
1062  if (!session)
1063  return;
1064 
1065  ENTER (" sess=%p ignore_lock=%d, book-id=%s",
1066  session, ignore_lock, book_id ? book_id : "(null)");
1067 
1068  /* Clear the error condition of previous errors */
1069  qof_error_clear (session);
1070 
1071  /* Check to see if this session is already open */
1072  if (session->book_id)
1073  {
1075  (_("This book appears to be open already."), FALSE));
1076  LEAVE (" push error book is already open ");
1077  return;
1078  }
1079 
1080  if (!book_id)
1081  {
1082  LEAVE (" using stdout");
1083  return;
1084  }
1085 
1086  /* Store the session URL */
1087  session->book_id = g_strdup (book_id);
1088 
1089  /* destroy the old backend */
1090  qof_session_destroy_backend (session);
1091 
1092  /* Look for something of the form of "file:/", "http://" or
1093  * "postgres://". Everything before the colon is the access
1094  * method. Load the first backend found for that access method.
1095  */
1096  p = strchr (book_id, ':');
1097  if (p)
1098  {
1099  access_method = g_strdup (book_id);
1100  p = strchr (access_method, ':');
1101  *p = 0;
1102  qof_session_load_backend (session, access_method);
1103  g_free (access_method);
1104  }
1105  else
1106  {
1107  /* If no colon found, assume it must be a file-path */
1108  qof_session_load_backend (session, "file");
1109  }
1110 
1111  /* No backend was found. That's bad. */
1112  if (NULL == session->backend)
1113  {
1114  gchar * msg;
1115 
1116  msg = g_strdup_printf (_("Unable to locate a "
1117  "suitable backend for '%s' - please check "
1118  "you have specified an access method "
1119  "like file: or sqlite:"), book_id);
1121  (msg, FALSE));
1122  DEBUG (" msg=%s", msg);
1123  LEAVE (" BAD: no backend: sess=%p book-id=%s",
1124  session, book_id ? book_id : "(null)");
1125  g_free (msg);
1126  return;
1127  }
1128 
1129  /* If there's a begin method, call that. */
1130  if (session->backend->session_begin)
1131  {
1132  (session->backend->session_begin) (session->backend, session,
1133  session->book_id, ignore_lock, create_if_nonexistent);
1134  PINFO (" Done running session_begin on backend");
1135  if (qof_error_check(session) != QOF_SUCCESS)
1136  {
1137  g_free (session->book_id);
1138  session->book_id = NULL;
1139  LEAVE (" backend error ");
1140  return;
1141  }
1142  }
1143  qof_error_clear (session);
1144  LEAVE (" sess=%p book-id=%s", session, book_id ? book_id : "(null)");
1145 }
1146 
1147 /* ============================================================== */
1148 
1149 void
1150 qof_session_load (QofSession * session, QofPercentageFunc percentage_func)
1151 {
1152  QofBook *newbook, *ob;
1153  QofBookList *oldbooks, *node;
1154  QofBackend *be;
1155 
1156  if (!session)
1157  return;
1158  if ((!session->book_id) ||
1159  (0 == safe_strcasecmp(session->book_id, QOF_STDOUT)))
1160  return;
1161 
1162  ENTER (" sess=%p book_id=%s", session, session->book_id
1163  ? session->book_id : "(null)");
1164 
1165  /* At this point, we should are supposed to have a valid book
1166  * id and a lock on the file. */
1167 
1168  oldbooks = session->books;
1169 
1170  /* XXX why are we creating a book here? I think the books
1171  * need to be handled by the backend ... especially since
1172  * the backend may need to load multiple books ... XXX. FIXME.
1173  */
1174  newbook = qof_book_new ();
1175  session->books = g_list_append (NULL, newbook);
1176  PINFO (" new book=%p", newbook);
1177 
1178  qof_error_clear (session);
1179 
1180  /* This code should be sufficient to initialize *any* backend,
1181  * whether http, postgres, or anything else that might come along.
1182  * Basically, the idea is that by now, a backend has already been
1183  * created & set up. At this point, we only need to get the
1184  * top-level account group out of the backend, and that is a
1185  * generic, backend-independent operation.
1186  */
1187  be = session->backend;
1188  qof_book_set_backend (newbook, be);
1189 
1190  /* Starting the session should result in a bunch of accounts
1191  * and currencies being downloaded, but probably no transactions;
1192  * The GUI will need to do a query for that.
1193  */
1194  if (be)
1195  {
1196  be->percentage = percentage_func;
1197 
1198  if (be->load)
1199  {
1200  be->load (be, newbook);
1201  }
1202  }
1203 
1204  if (qof_error_check(session) != QOF_SUCCESS)
1205  {
1206  /* Something broke, put back the old stuff */
1207  qof_book_set_backend (newbook, NULL);
1208  qof_book_destroy (newbook);
1209  g_list_free (session->books);
1210  session->books = oldbooks;
1211  g_free (session->book_id);
1212  session->book_id = NULL;
1213  LEAVE (" error from backend ");
1214  return;
1215  }
1216 
1217  for (node = oldbooks; node; node = node->next)
1218  {
1219  ob = node->data;
1220  qof_book_set_backend (ob, NULL);
1221  qof_book_destroy (ob);
1222  }
1223  g_list_free (oldbooks);
1224 
1225  LEAVE (" sess = %p, book_id=%s", session, session->book_id
1226  ? session->book_id : "(null)");
1227 }
1228 
1229 /* ============================================================= */
1230 
1231 gboolean
1233 {
1234  if (!session)
1235  return FALSE;
1236  if (!session->backend)
1237  return FALSE;
1238  if (!session->backend->save_may_clobber_data)
1239  return FALSE;
1240 
1241  return (*(session->backend->save_may_clobber_data)) (session->backend);
1242 }
1243 
1244 void
1246  QofPercentageFunc percentage_func)
1247 {
1248  GList *node;
1249  QofBackend *be;
1250  gboolean partial, change_backend;
1251  QofBackendProvider *prov;
1252  GSList *p;
1253  QofBook *book, *abook;
1254  gint num;
1255  gchar *msg, *book_id;
1256 
1257  if (!session)
1258  return;
1259  ENTER (" sess=%p book_id=%s",
1260  session, session->book_id ? session->book_id : "(null)");
1261  /* Partial book handling. */
1262  book = qof_session_get_book (session);
1263  partial =
1264  (gboolean)
1265  GPOINTER_TO_INT (qof_book_get_data (book, PARTIAL_QOFBOOK));
1266  change_backend = FALSE;
1267  msg = g_strdup_printf (" ");
1268  book_id = g_strdup (session->book_id);
1269  if (partial == TRUE)
1270  {
1271  if (session->backend && session->backend->provider)
1272  {
1273  prov = session->backend->provider;
1274  if (TRUE == prov->partial_book_supported)
1275  {
1276  /* if current backend supports partial, leave alone. */
1277  change_backend = FALSE;
1278  }
1279  else
1280  {
1281  change_backend = TRUE;
1282  }
1283  }
1284  /* If provider is undefined, assume partial not supported. */
1285  else
1286  {
1287  change_backend = TRUE;
1288  }
1289  }
1290  if (change_backend == TRUE)
1291  {
1292  qof_session_destroy_backend (session);
1293  if (NULL == provider_list)
1294  {
1295  for (num = 0; backend_list[num].filename != NULL; num++)
1296  {
1297  qof_load_backend_library (backend_list[num].libdir,
1298  backend_list[num].filename,
1299  backend_list[num].init_fcn);
1300  }
1301  }
1302  p = g_slist_copy (provider_list);
1303  while (p != NULL)
1304  {
1305  prov = p->data;
1306  if (TRUE == prov->partial_book_supported)
1307  {
1309  /* if((TRUE == prov->partial_book_supported) &&
1310  (0 == strcasecmp (access_method, prov->access_method)))
1311  { */
1312  if (NULL == prov->backend_new)
1313  continue;
1314  /* Use the providers creation callback */
1315  session->backend = (*(prov->backend_new)) ();
1316  session->backend->provider = prov;
1317  if (session->backend->session_begin)
1318  {
1319  /* Call begin - backend has been changed,
1320  so make sure a file can be written,
1321  use ignore_lock and create_if_nonexistent */
1322  g_free (session->book_id);
1323  session->book_id = NULL;
1324  (session->backend->session_begin) (session->backend,
1325  session, book_id, TRUE, TRUE);
1326  PINFO
1327  (" Done running session_begin on changed backend");
1328  if (qof_error_check (session) != QOF_SUCCESS)
1329  {
1330  g_free (session->book_id);
1331  session->book_id = NULL;
1332  LEAVE (" changed backend error");
1333  return;
1334  }
1335  }
1336  /* Tell the books about the backend that they'll be using. */
1337  for (node = session->books; node; node = node->next)
1338  {
1339  book = node->data;
1340  qof_book_set_backend (book, session->backend);
1341  }
1342  p = NULL;
1343  }
1344  if (p)
1345  {
1346  p = p->next;
1347  }
1348  }
1349  if (!session->backend)
1350  {
1351  msg = g_strdup_printf (" failed to load backend");
1353  (_("Failed to load backend, no suitable handler."),
1354  FALSE));
1355  return;
1356  }
1357  }
1358  /* If there is a backend, and the backend is reachable
1359  * (i.e. we can communicate with it), then synchronize with
1360  * the backend. If we cannot contact the backend (e.g.
1361  * because we've gone offline, the network has crashed, etc.)
1362  * then give the user the option to save to the local disk.
1363  *
1364  * hack alert -- FIXME -- XXX the code below no longer
1365  * does what the words above say. This needs fixing.
1366  */
1367  be = session->backend;
1368  if (be)
1369  {
1370  for (node = session->books; node; node = node->next)
1371  {
1372  abook = node->data;
1373  /* if invoked as SaveAs(), then backend not yet set */
1374  qof_book_set_backend (abook, be);
1375  be->percentage = percentage_func;
1376  if (be->sync)
1377  (be->sync) (be, abook);
1378  }
1379  /* If we got to here, then the backend saved everything
1380  * just fine, and we are done. So return. */
1381  /* Return the book_id to previous value. */
1382  qof_error_clear (session);
1383  LEAVE (" Success");
1384  return;
1385  }
1386  else
1387  {
1388  msg = g_strdup_printf (" failed to load backend");
1390  (_("Failed to load backend, no suitable handler."),
1391  FALSE));
1392  }
1393  LEAVE (" error -- No backend!");
1394 }
1395 
1396 /* ============================================================= */
1397 
1398 void
1400 {
1401  if (!session)
1402  return;
1403 
1404  ENTER (" sess=%p book_id=%s", session, session->book_id
1405  ? session->book_id : "(null)");
1406 
1407  /* close down the backend first */
1408  if (session->backend && session->backend->session_end)
1409  {
1410  (session->backend->session_end) (session->backend);
1411  }
1412 
1413  qof_error_clear (session);
1414 
1415  g_free (session->book_id);
1416  session->book_id = NULL;
1417 
1418  LEAVE (" sess=%p book_id=%s", session, session->book_id
1419  ? session->book_id : "(null)");
1420 }
1421 
1422 void
1423 qof_session_destroy (QofSession * session)
1424 {
1425  GList *node;
1426  if (!session)
1427  return;
1428 
1429  ENTER (" sess=%p book_id=%s", session, session->book_id
1430  ? session->book_id : "(null)");
1431 
1432  qof_session_end (session);
1433 
1434  /* destroy the backend */
1435  qof_session_destroy_backend (session);
1436 
1437  for (node = session->books; node; node = node->next)
1438  {
1439  QofBook *book = node->data;
1440  qof_book_set_backend (book, NULL);
1441  qof_book_destroy (book);
1442  }
1443 
1444  session->books = NULL;
1445 #ifndef QOF_DISABLE_DEPRECATED
1446  if (session == qof_session_get_current_session())
1447  qof_session_clear_current_session();
1448 #endif
1449  g_free (session);
1450  qof_error_close ();
1451 
1452  LEAVE (" sess=%p", session);
1453 }
1454 
1455 /* ============================================================= */
1456 
1457 void
1458 qof_session_swap_data (QofSession * session_1, QofSession * session_2)
1459 {
1460  GList *books_1, *books_2, *node;
1461 
1462  if (session_1 == session_2)
1463  return;
1464  if (!session_1 || !session_2)
1465  return;
1466 
1467  ENTER (" sess1=%p sess2=%p", session_1, session_2);
1468 
1469  books_1 = session_1->books;
1470  books_2 = session_2->books;
1471 
1472  session_1->books = books_2;
1473  session_2->books = books_1;
1474 
1475  for (node = books_1; node; node = node->next)
1476  {
1477  QofBook *book_1 = node->data;
1478  qof_book_set_backend (book_1, session_2->backend);
1479  }
1480  for (node = books_2; node; node = node->next)
1481  {
1482  QofBook *book_2 = node->data;
1483  qof_book_set_backend (book_2, session_1->backend);
1484  }
1485 
1486  LEAVE (" ");
1487 }
1488 
1489 /* ============================================================= */
1490 
1491 gboolean
1493 {
1494  if (!session)
1495  return FALSE;
1496  if (!session->backend)
1497  return FALSE;
1498  if (!session->backend->events_pending)
1499  return FALSE;
1500 
1501  return session->backend->events_pending (session->backend);
1502 }
1503 
1504 gboolean
1506 {
1507  if (!session)
1508  return FALSE;
1509  if (!session->backend)
1510  return FALSE;
1511  if (!session->backend->process_events)
1512  return FALSE;
1513 
1514  return session->backend->process_events (session->backend);
1515 }
1516 
1517 /* ============== END OF FILE ========================== */