qofsession.c

Go to the documentation of this file.
00001 /********************************************************************\
00002  * qofsesssion.c -- session access (connection to backend)          *
00003  *                                                                  *
00004  * This program is free software; you can redistribute it and/or    *
00005  * modify it under the terms of the GNU General Public License as   *
00006  * published by the Free Software Foundation; either version 2 of   *
00007  * the License, or (at your option) any later version.              *
00008  *                                                                  *
00009  * This program is distributed in the hope that it will be useful,  *
00010  * but WITHOUT ANY WARRANTY; without even the implied warranty of   *
00011  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the    *
00012  * GNU General Public License for more details.                     *
00013  *                                                                  *
00014  * You should have received a copy of the GNU General Public License*
00015  * along with this program; if not, contact:                        *
00016  *                                                                  *
00017  * Free Software Foundation           Voice:  +1-617-542-5942       *
00018  * 51 Franklin Street, Fifth Floor    Fax:    +1-617-542-2652       *
00019  * Boston, MA  02110-1301,  USA       gnu@gnu.org                   *
00020 \********************************************************************/
00021 
00034 #include "config.h"
00035 
00036 #include <stdlib.h>
00037 #include <string.h>
00038 #include <sys/types.h>
00039 #include <sys/stat.h>
00040 #include <unistd.h>
00041 
00042 #include <glib.h>
00043 #include "qof.h"
00044 #include "qofbackend-p.h"
00045 #include "qofbook-p.h"
00046 #include "qofsession-p.h"
00047 #include "qofobject-p.h"
00048 #include "qofla-dir.h" /* for QOF_LIB_DIR */
00049 
00051 static QofSession * current_session = NULL;
00052 
00053 static GHookList * session_closed_hooks = NULL;
00054 static QofLogModule log_module = QOF_MOD_SESSION;
00055 static GSList *provider_list = NULL;
00056 static gboolean qof_providers_initialized = FALSE;
00057 
00058 /* ====================================================================== */
00059 
00060 void
00061 qof_backend_register_provider (QofBackendProvider *prov)
00062 {
00063         provider_list = g_slist_append (provider_list, prov);
00064 }
00065 
00066 GList*
00067 qof_backend_get_registered_access_method_list(void)
00068 {
00069         GList* list = NULL;
00070         GSList* node;
00071 
00072         for( node = provider_list; node != NULL; node = node->next ) {
00073                 QofBackendProvider *prov = node->data;
00074                 list = g_list_append( list, (gchar*)prov->access_method );
00075         }
00076 
00077         return list;
00078 }
00079 
00080 /* ====================================================================== */
00081 
00082 /* hook routines */
00083 
00084 void
00085 qof_session_add_close_hook (GFunc fn, gpointer data)
00086 {
00087   GHook *hook;
00088 
00089   if (session_closed_hooks == NULL) {
00090       session_closed_hooks = malloc(sizeof(GHookList)); /* LEAKED */
00091     g_hook_list_init (session_closed_hooks, sizeof(GHook));
00092   }
00093 
00094   hook = g_hook_alloc(session_closed_hooks);
00095   if (!hook)
00096     return;
00097 
00098   hook->func = (GHookFunc)fn;
00099   hook->data = data;
00100   g_hook_append(session_closed_hooks, hook);
00101 }
00102 
00103 void
00104 qof_session_call_close_hooks (QofSession *session)
00105 {
00106   GHook *hook;
00107   GFunc fn;
00108 
00109   if (session_closed_hooks == NULL)
00110     return;
00111 
00112   hook = g_hook_first_valid (session_closed_hooks, FALSE);
00113   while (hook) {
00114     fn = (GFunc)hook->func;
00115     fn(session, hook->data);
00116     hook = g_hook_next_valid (session_closed_hooks, hook, FALSE);
00117   }
00118 }
00119 
00120 /* ====================================================================== */
00121 /* error handling routines */
00122 
00123 static void
00124 qof_session_clear_error (QofSession *session)
00125 {
00126   QofBackendError err;
00127 
00128   session->last_err = ERR_BACKEND_NO_ERR;
00129   g_free(session->error_message);
00130   session->error_message = NULL;
00131 
00132   /* pop the stack on the backend as well. */
00133   if (session->backend)
00134   {
00135     do
00136     {
00137        err = qof_backend_get_error (session->backend);
00138     } while (ERR_BACKEND_NO_ERR != err);
00139   }
00140 }
00141 
00142 void
00143 qof_session_push_error (QofSession *session, QofBackendError err,
00144                         const char *message)
00145 {
00146   if (!session) return;
00147 
00148   g_free (session->error_message);
00149 
00150   session->last_err = err;
00151   session->error_message = g_strdup (message);
00152 }
00153 
00154 QofBackendError
00155 qof_session_get_error (QofSession * session)
00156 {
00157   QofBackendError err;
00158 
00159   if (!session) return ERR_BACKEND_NO_BACKEND;
00160 
00161   /* if we have a local error, return that. */
00162   if (ERR_BACKEND_NO_ERR != session->last_err)
00163   {
00164     return session->last_err;
00165   }
00166 
00167   /* maybe we should return a no-backend error ??? */
00168   if (! session->backend) return ERR_BACKEND_NO_ERR;
00169 
00170   err = qof_backend_get_error (session->backend);
00171   session->last_err = err;
00172   return err;
00173 }
00174 
00175 static const char *
00176 get_default_error_message(QofBackendError err)
00177 {
00178     return "";
00179 }
00180 
00181 const char *
00182 qof_session_get_error_message(const QofSession *session)
00183 {
00184     if(!session) return "";
00185     if(!session->error_message)
00186       return get_default_error_message(session->last_err);
00187     return session->error_message;
00188 }
00189 
00190 QofBackendError
00191 qof_session_pop_error (QofSession * session)
00192 {
00193   QofBackendError err;
00194 
00195   if (!session) return ERR_BACKEND_NO_BACKEND;
00196 
00197   err = qof_session_get_error(session);
00198   qof_session_clear_error(session);
00199 
00200   return err;
00201 }
00202 
00203 /* ====================================================================== */
00204 
00205 static void
00206 qof_session_init (QofSession *session)
00207 {
00208   if (!session) return;
00209 
00210   session->entity.e_type = QOF_ID_SESSION;
00211   session->books = g_list_append (NULL, qof_book_new ());
00212   session->book_id = NULL;
00213   session->backend = NULL;
00214   session->lock = 1;
00215 
00216   qof_session_clear_error (session);
00217 }
00218 
00219 QofSession *
00220 qof_session_new (void)
00221 {
00222   QofSession *session = g_new0(QofSession, 1);
00223   qof_session_init(session);
00224   return session;
00225 }
00226 
00229 QofSession *
00230 qof_session_get_current_session (void)
00231 {
00232   if (!current_session)
00233   {
00234     qof_event_suspend ();
00235     current_session = qof_session_new ();
00236     qof_event_resume ();
00237   }
00238 
00239   return current_session;
00240 }
00241 
00244 void
00245 qof_session_set_current_session (QofSession *session)
00246 {
00247   current_session = session;
00248 }
00249 
00250 QofBook *
00251 qof_session_get_book (const QofSession *session)
00252 {
00253    GList *node;
00254    if (!session) return NULL;
00255 
00256    for (node=session->books; node; node=node->next)
00257    {
00258       QofBook *book = node->data;
00259       if ('y' == book->book_open) return book;
00260    }
00261    return NULL;
00262 }
00263 
00264 void
00265 qof_session_add_book (QofSession *session, QofBook *addbook)
00266 {
00267   GList *node;
00268   if (!session) return;
00269 
00270   ENTER (" sess=%p book=%p", session, addbook);
00271 
00272   /* See if this book is already there ... */
00273   for (node=session->books; node; node=node->next)
00274   {
00275      QofBook *book = node->data;
00276      if (addbook == book) return;
00277   }
00278 
00279   if ('y' == addbook->book_open)
00280   {
00281     /* hack alert -- someone should free all the books in the list,
00282      * but it should probably not be us ... since the books backends
00283      * should be shutdown first, etc */
00284 /* XXX this should probably be an error XXX */
00285     g_list_free (session->books);
00286     session->books = g_list_append (NULL, addbook);
00287   }
00288   else 
00289   {
00290 /* XXX Need to tell the backend to add a book as well */
00291     session->books = g_list_append (session->books, addbook);
00292   }
00293 
00294   qof_book_set_backend (addbook, session->backend);
00295   LEAVE (" ");
00296 }
00297 
00298 QofBackend * 
00299 qof_session_get_backend (const QofSession *session)
00300 {
00301    if (!session) return NULL;
00302    return session->backend;
00303 }
00304 
00305 const char *
00306 qof_session_get_file_path (const QofSession *session)
00307 {
00308    if (!session) return NULL;
00309    if (!session->backend) return NULL;
00310    return session->backend->fullpath;
00311 }
00312 
00313 const char *
00314 qof_session_get_url (const QofSession *session)
00315 {
00316    if (!session) return NULL;
00317    return session->book_id;
00318 }
00319 
00320 /* =============================================================== */
00321 
00322 typedef struct qof_instance_copy_data {
00323         QofInstance *from;
00324         QofInstance *to;
00325         QofParam  *param;
00326         GList  *referenceList;
00327         GSList *param_list;
00328         QofSession *new_session;
00329         gboolean error;
00330 }QofInstanceCopyData;
00331 
00332 static void
00333 qof_book_set_partial(QofBook *book)
00334 {
00335         gboolean partial;
00336 
00337         partial =
00338          (gboolean)GPOINTER_TO_INT(qof_book_get_data(book, PARTIAL_QOFBOOK));
00339         if(!partial) {
00340                 qof_book_set_data(book, PARTIAL_QOFBOOK, GINT_TO_POINTER(TRUE));
00341         }
00342 }
00343 
00344 void
00345 qof_session_update_reference_list(QofSession *session, QofInstanceReference *reference)
00346 {
00347         QofBook  *book;
00348         GList    *book_ref_list;
00349 
00350         book = qof_session_get_book(session);
00351         book_ref_list = (GList*)qof_book_get_data(book, ENTITYREFERENCE);
00352         book_ref_list = g_list_append(book_ref_list, reference);
00353         qof_book_set_data(book, ENTITYREFERENCE, book_ref_list);
00354         qof_book_set_partial(book);
00355 }
00356 
00357 static void
00358 qof_instance_param_cb(QofParam *param, gpointer data)
00359 {
00360         QofInstanceCopyData *qecd;
00361 
00362         g_return_if_fail(data != NULL);
00363         qecd = (QofInstanceCopyData*)data;
00364         g_return_if_fail(param != NULL);
00365         /* KVP doesn't need a set routine to be copied. */
00366         if(0 == safe_strcmp(param->param_type, QOF_TYPE_KVP)) {
00367                 qecd->param_list = g_slist_prepend(qecd->param_list, param);
00368                 return;
00369         }
00370         if((param->param_getfcn != NULL)&&(param->param_setfcn != NULL)) {
00371                         qecd->param_list = g_slist_prepend(qecd->param_list, param);
00372         }
00373 }
00374 
00375 static void
00376 col_ref_cb (QofInstance* ref_ent, gpointer user_data)
00377 {
00378         QofInstanceReference *ref;
00379         QofInstanceCopyData  *qecd;
00380         QofInstance *ent;
00381         const GUID   *cm_guid;
00382         char         cm_sa[GUID_ENCODING_LENGTH + 1];
00383         gchar        *cm_string;
00384 
00385     g_return_if_fail(user_data);
00386         qecd = (QofInstanceCopyData*)user_data;
00387         ent = qecd->from;
00388     g_return_if_fail(ent);
00389         ref = g_new0(QofInstanceReference, 1);
00390         ref->type = ent->e_type;
00391         ref->ref_guid = g_new(GUID, 1);
00392         ref->ent_guid = qof_instance_get_guid(ent);
00393         ref->param = qof_class_get_parameter(ent->e_type, 
00394                 qecd->param->param_name);
00395         cm_guid = qof_entity_get_guid(ref_ent);
00396         guid_to_string_buff(cm_guid, cm_sa);
00397         cm_string = g_strdup(cm_sa);
00398         if(TRUE == string_to_guid(cm_string, ref->ref_guid)) {
00399                 g_free(cm_string);
00400                 qof_session_update_reference_list(qecd->new_session, ref);
00401         }
00402 }
00403 
00404 static void
00405 qof_instance_foreach_copy(gpointer data, gpointer user_data)
00406 {
00407         QofInstance          *importEnt, *targetEnt/*, *referenceEnt*/;
00408         QofInstanceCopyData     *context;
00409         QofInstanceReference  *reference;
00410         gboolean                registered_type;
00411         /* cm_ prefix used for variables that hold the data to commit */
00412         QofParam                *cm_param;
00413         gchar                   *cm_string, *cm_char;
00414         const GUID              *cm_guid;
00415         KvpFrame                *cm_kvp;
00416         QofCollection *cm_col;
00417         /* function pointers and variables for parameter getters that don't use pointers normally */
00418         gnc_numeric     cm_numeric, (*numeric_getter)   (QofInstance*, QofParam*);
00419         double                  cm_double,      (*double_getter)        (QofInstance*, QofParam*);
00420         gboolean                cm_boolean, (*boolean_getter)   (QofInstance*, QofParam*);
00421         gint32                  cm_i32,         (*int32_getter)         (QofInstance*, QofParam*);
00422         gint64                  cm_i64,         (*int64_getter)         (QofInstance*, QofParam*);
00423         Timespec                cm_date,        (*date_getter)          (QofInstance*, QofParam*);
00424         /* function pointers to the parameter setters */
00425         void    (*string_setter)        (QofInstance*, const char*);
00426         void    (*date_setter)          (QofInstance*, Timespec);
00427         void    (*numeric_setter)       (QofInstance*, gnc_numeric);
00428         void    (*guid_setter)          (QofInstance*, const GUID*);
00429         void    (*double_setter)        (QofInstance*, double);
00430         void    (*boolean_setter)       (QofInstance*, gboolean);
00431         void    (*i32_setter)           (QofInstance*, gint32);
00432         void    (*i64_setter)           (QofInstance*, gint64);
00433         void    (*char_setter)          (QofInstance*, char*);
00434         void    (*kvp_frame_setter)     (QofInstance*, KvpFrame*);
00435         
00436         g_return_if_fail(user_data != NULL);
00437         context = (QofInstanceCopyData*) user_data;
00438         cm_date.tv_nsec = 0;
00439         cm_date.tv_sec =  0;
00440         importEnt = context->from;
00441         targetEnt = context->to;
00442         registered_type = FALSE;
00443         cm_param = (QofParam*) data;
00444         g_return_if_fail(cm_param != NULL);
00445         context->param = cm_param;
00446         if(safe_strcmp(cm_param->param_type, QOF_TYPE_STRING) == 0)  { 
00447                 cm_string = (gchar*)cm_param->param_getfcn(importEnt, cm_param);
00448                 if(cm_string) {
00449                 string_setter = (void(*)(QofInstance*, const char*))cm_param->param_setfcn;
00450                 if(string_setter != NULL) {     string_setter(targetEnt, cm_string); }
00451                 }
00452                 registered_type = TRUE;
00453         }
00454         if(safe_strcmp(cm_param->param_type, QOF_TYPE_DATE) == 0) { 
00455                 date_getter = (Timespec (*)(QofInstance*, QofParam*))cm_param->param_getfcn;
00456                 cm_date = date_getter(importEnt, cm_param);
00457                 date_setter = (void(*)(QofInstance*, Timespec))cm_param->param_setfcn;
00458                 if(date_setter != NULL) { date_setter(targetEnt, cm_date); }
00459                 registered_type = TRUE;
00460         }
00461         if((safe_strcmp(cm_param->param_type, QOF_TYPE_NUMERIC) == 0)  ||
00462         (safe_strcmp(cm_param->param_type, QOF_TYPE_DEBCRED) == 0)) { 
00463                 numeric_getter = (gnc_numeric (*)(QofInstance*, QofParam*))cm_param->param_getfcn;
00464                 cm_numeric = numeric_getter(importEnt, cm_param);
00465                 numeric_setter = (void(*)(QofInstance*, gnc_numeric))cm_param->param_setfcn;
00466                 if(numeric_setter != NULL) { numeric_setter(targetEnt, cm_numeric); }
00467                 registered_type = TRUE;
00468         }
00469         if(safe_strcmp(cm_param->param_type, QOF_TYPE_GUID) == 0) { 
00470                 cm_guid = (const GUID*)cm_param->param_getfcn(importEnt, cm_param);
00471                 guid_setter = (void(*)(QofInstance*, const GUID*))cm_param->param_setfcn;
00472                 if(guid_setter != NULL) { guid_setter(targetEnt, cm_guid); }
00473                 registered_type = TRUE;
00474         }
00475         if(safe_strcmp(cm_param->param_type, QOF_TYPE_INT32) == 0) { 
00476                 int32_getter = (gint32 (*)(QofInstance*, QofParam*)) cm_param->param_getfcn;
00477                 cm_i32 = int32_getter(importEnt, cm_param);
00478                 i32_setter = (void(*)(QofInstance*, gint32))cm_param->param_setfcn;
00479                 if(i32_setter != NULL) { i32_setter(targetEnt, cm_i32); }
00480                 registered_type = TRUE;
00481         }
00482         if(safe_strcmp(cm_param->param_type, QOF_TYPE_INT64) == 0) { 
00483                 int64_getter = (gint64 (*)(QofInstance*, QofParam*)) cm_param->param_getfcn;
00484                 cm_i64 = int64_getter(importEnt, cm_param);
00485                 i64_setter = (void(*)(QofInstance*, gint64))cm_param->param_setfcn;
00486                 if(i64_setter != NULL) { i64_setter(targetEnt, cm_i64); }
00487                 registered_type = TRUE;
00488         }
00489         if(safe_strcmp(cm_param->param_type, QOF_TYPE_DOUBLE) == 0) { 
00490                 double_getter = (double (*)(QofInstance*, QofParam*)) cm_param->param_getfcn;
00491                 cm_double = double_getter(importEnt, cm_param);
00492                 double_setter = (void(*)(QofInstance*, double))cm_param->param_setfcn;
00493                 if(double_setter != NULL) { double_setter(targetEnt, cm_double); }
00494                 registered_type = TRUE;
00495         }
00496         if(safe_strcmp(cm_param->param_type, QOF_TYPE_BOOLEAN) == 0){ 
00497                 boolean_getter = (gboolean (*)(QofInstance*, QofParam*)) cm_param->param_getfcn;
00498                 cm_boolean = boolean_getter(importEnt, cm_param);
00499                 boolean_setter = (void(*)(QofInstance*, gboolean))cm_param->param_setfcn;
00500                 if(boolean_setter != NULL) { boolean_setter(targetEnt, cm_boolean); }
00501                 registered_type = TRUE;
00502         }
00503         if(safe_strcmp(cm_param->param_type, QOF_TYPE_KVP) == 0) { 
00504                 cm_kvp = (KvpFrame*)cm_param->param_getfcn(importEnt,cm_param);
00505                 kvp_frame_setter = (void(*)(QofInstance*, KvpFrame*))cm_param->param_setfcn;
00506                 if(kvp_frame_setter != NULL) { kvp_frame_setter(targetEnt, cm_kvp); }
00507                 else
00508                 {
00509                         QofInstance *target_inst;
00510 
00511                         target_inst = (QofInstance*)targetEnt;
00512                         kvp_frame_delete(target_inst->kvp_data);
00513                         target_inst->kvp_data = kvp_frame_copy(cm_kvp);
00514                 }
00515                 registered_type = TRUE;
00516         }
00517         if(safe_strcmp(cm_param->param_type, QOF_TYPE_CHAR) == 0) { 
00518                 cm_char = (gchar*)cm_param->param_getfcn(importEnt,cm_param);
00519                 char_setter = (void(*)(QofInstance*, char*))cm_param->param_setfcn;
00520                 if(char_setter != NULL) { char_setter(targetEnt, cm_char); }
00521                 registered_type = TRUE;
00522         }
00523         if(safe_strcmp(cm_param->param_type, QOF_TYPE_COLLECT) == 0) {
00524                 cm_col = (QofCollection*)cm_param->param_getfcn(importEnt, cm_param);
00525                 if(cm_col)
00526                 {
00527                         /* create one reference for each member of the collection. */
00528                         qof_collection_foreach(cm_col, col_ref_cb, context);
00529                 }
00530                 registered_type = TRUE;
00531         }
00532         if(registered_type == FALSE) {
00533 /*              referenceEnt = QOF_INSTANCE(cm_param->param_getfcn(importEnt, cm_param));
00534                 if(!referenceEnt) { return; }
00535                 if(!referenceEnt->e_type) { return; }*/
00536                 reference = qof_instance_get_reference_from(importEnt, cm_param);
00537                 if(reference) {
00538                         qof_session_update_reference_list(context->new_session, reference);
00539                 }
00540         }
00541 }
00542 
00543 static gboolean
00544 qof_instance_guid_match(QofSession *new_session, QofInstance *original)
00545 {
00546         QofInstance *copy;
00547         const GUID *g;
00548         QofIdTypeConst type;
00549         QofBook *targetBook;
00550         QofCollection *coll;
00551         
00552         copy = NULL;
00553         g_return_val_if_fail(original != NULL, FALSE);
00554         targetBook = qof_session_get_book(new_session);
00555         g_return_val_if_fail(targetBook != NULL, FALSE);
00556         g = qof_instance_get_guid(original);
00557         type = g_strdup(original->e_type);
00558         coll = qof_book_get_collection(targetBook, type);
00559         copy = qof_collection_lookup_entity(coll, g);
00560         if(copy) { return TRUE; }
00561         return FALSE;   
00562 }
00563 
00564 static void
00565 qof_instance_list_foreach(gpointer data, gpointer user_data)
00566 {
00567         QofInstanceCopyData *qecd;
00568         QofInstance *original;
00569         QofInstance *inst;
00570         QofBook *book;
00571         const GUID *g;
00572         
00573         g_return_if_fail(data != NULL);
00574         original = QOF_INSTANCE(data);
00575         g_return_if_fail(user_data != NULL);
00576         qecd = (QofInstanceCopyData*)user_data;
00577         if(qof_instance_guid_match(qecd->new_session, original)) { return; }
00578         qecd->from = original;
00579         if(!qof_object_compliance(original->e_type, FALSE)) 
00580         {
00581                 qecd->error = TRUE;
00582                 return;
00583         }
00584         book = qof_session_get_book(qecd->new_session);
00585         inst = (QofInstance*)qof_object_new_instance(original->e_type, book);
00586         if(!inst) 
00587         { 
00588                 PERR (" failed to create new entity type=%s.", original->e_type);
00589                 qecd->error = TRUE;
00590                 return;
00591         }
00592         qecd->to = inst;
00593         g = qof_instance_get_guid(original);
00594         qof_instance_set_guid(qecd->to, g);
00595         if(qecd->param_list != NULL) { 
00596                 g_slist_free(qecd->param_list);
00597                 qecd->param_list = NULL;
00598         }
00599         qof_class_param_foreach(original->e_type, qof_instance_param_cb, qecd);
00600         qof_begin_edit(inst);
00601         g_slist_foreach(qecd->param_list, qof_instance_foreach_copy, qecd);
00602         qof_commit_edit(inst);
00603 }
00604 
00605 static void
00606 qof_instance_coll_foreach(QofInstance *original, gpointer user_data)
00607 {
00608         QofInstanceCopyData *qecd;
00609         const GUID *g;
00610         QofBook *targetBook;
00611         QofCollection *coll;
00612         QofInstance *copy;
00613 
00614         g_return_if_fail(original != NULL);
00615         g_return_if_fail(user_data != NULL);
00616         copy = NULL;
00617         qecd = (QofInstanceCopyData*)user_data;
00618         targetBook = qof_session_get_book(qecd->new_session);
00619         g = qof_instance_get_guid(original);
00620         coll = qof_book_get_collection(targetBook, original->e_type);
00621         copy = qof_collection_lookup_entity(coll, g);
00622         if(copy) { qecd->error = TRUE; }
00623 }
00624 
00625 static void
00626 qof_instance_coll_copy(QofInstance *original, gpointer user_data)
00627 {
00628         QofInstanceCopyData *qecd;
00629         QofBook *book;
00630         QofInstance *inst;
00631         const GUID *g;
00632 
00633         g_return_if_fail(original != NULL);
00634         g_return_if_fail(user_data != NULL);
00635         qecd = (QofInstanceCopyData*)user_data;
00636         book = qof_session_get_book(qecd->new_session);
00637         if(!qof_object_compliance(original->e_type, TRUE)) { return; }
00638         inst = (QofInstance*)qof_object_new_instance(original->e_type, book);
00639         qecd->to = inst;
00640         qecd->from = original;
00641         g = qof_instance_get_guid(original);
00642         qof_instance_set_guid(qecd->to, g);
00643         qof_begin_edit(inst);
00644         g_slist_foreach(qecd->param_list, qof_instance_foreach_copy, qecd);
00645         qof_commit_edit(inst);
00646 }
00647 
00648 gboolean 
00649 qof_instance_copy_to_session(QofSession* new_session, QofInstance* original)
00650 {
00651         QofInstanceCopyData qecd;
00652         QofInstance *inst;
00653         QofBook *book;
00654 
00655         if(!new_session || !original) { return FALSE; }
00656         if(qof_instance_guid_match(new_session, original)) { return FALSE; }
00657         if(!qof_object_compliance(original->e_type, TRUE)) { return FALSE; }
00658         qof_event_suspend();
00659         qecd.param_list = NULL;
00660         book = qof_session_get_book(new_session);
00661         qecd.new_session = new_session;
00662         qof_book_set_partial(book);
00663         inst = (QofInstance*)qof_object_new_instance(original->e_type, book);
00664         qecd.to = inst;
00665         qecd.from = original;
00666         qof_instance_set_guid(qecd.to, qof_instance_get_guid(original));
00667         qof_begin_edit(inst);
00668         qof_class_param_foreach(original->e_type, qof_instance_param_cb, &qecd);
00669         qof_commit_edit(inst);
00670         if(g_slist_length(qecd.param_list) == 0) { return FALSE; }
00671         g_slist_foreach(qecd.param_list, qof_instance_foreach_copy, &qecd);
00672         g_slist_free(qecd.param_list);
00673         qof_event_resume();
00674         return TRUE;
00675 }
00676 
00677 gboolean qof_instance_copy_list(QofSession *new_session, GList *entity_list)
00678 {
00679         QofInstanceCopyData *qecd;
00680 
00681         if(!new_session || !entity_list) { return FALSE; }
00682         ENTER (" list=%d", g_list_length(entity_list));
00683         qecd = g_new0(QofInstanceCopyData, 1);
00684         qof_event_suspend();
00685         qecd->param_list = NULL;
00686         qecd->new_session = new_session;
00687         qof_book_set_partial(qof_session_get_book(new_session));
00688         g_list_foreach(entity_list, qof_instance_list_foreach, qecd);
00689         qof_event_resume();
00690         if(qecd->error) 
00691         { 
00692                 PWARN (" some/all entities in the list could not be copied.");
00693         }
00694         g_free(qecd);
00695         LEAVE (" ");
00696         return TRUE;
00697 }
00698 
00699 gboolean 
00700 qof_instance_copy_coll(QofSession *new_session, QofCollection *entity_coll)
00701 {
00702         QofInstanceCopyData qecd;
00703 
00704         g_return_val_if_fail(new_session, FALSE);
00705         if(!entity_coll) { return FALSE; }
00706         qof_event_suspend();
00707         qecd.param_list = NULL;
00708         qecd.new_session = new_session;
00709         qof_book_set_partial(qof_session_get_book(qecd.new_session));
00710         qof_collection_foreach(entity_coll, qof_instance_coll_foreach, &qecd);
00711         qof_class_param_foreach(qof_collection_get_type(entity_coll), 
00712                 qof_instance_param_cb, &qecd);
00713         qof_collection_foreach(entity_coll, qof_instance_coll_copy, &qecd);
00714         if(qecd.param_list != NULL) { g_slist_free(qecd.param_list); }
00715         qof_event_resume();
00716         return TRUE;
00717 }
00718 
00719 struct recurse_s
00720 {
00721         QofSession *session;
00722         gboolean   success;
00723         GList      *ref_list;
00724         GList      *ent_list;
00725 };
00726 
00727 static void
00728 recurse_collection_cb (QofInstance *ent, gpointer user_data)
00729 {
00730         struct recurse_s *store;
00731 
00732         if(user_data == NULL) { return; }
00733         store = (struct recurse_s*)user_data;
00734         if(!ent || !store) { return; }
00735         store->success = qof_instance_copy_to_session(store->session, ent);
00736         if(store->success) {
00737         store->ent_list = g_list_append(store->ent_list, ent);
00738         }
00739 }
00740 
00741 static void
00742 recurse_ent_cb(QofInstance *ent, gpointer user_data)
00743 {
00744         GList      *ref_list, *i, *j, *ent_list, *child_list;
00745         QofParam   *ref_param;
00746         QofInstance  *ref_ent, *child_ent;
00747         QofSession *session;
00748         struct recurse_s *store;
00749         gboolean   success;
00750 
00751         if(user_data == NULL) { return; }
00752         store = (struct recurse_s*)user_data;
00753         session = store->session;
00754         success = store->success;
00755         ref_list = NULL;
00756         child_ent = NULL;
00757         ref_list = g_list_copy(store->ref_list);
00758         if((!session)||(!ent)) { return; }
00759         ent_list = NULL;
00760         child_list = NULL;
00761         i = NULL;
00762         j = NULL;
00763         for(i = ref_list; i != NULL; i=i->next)
00764         {
00765                 if(i->data == NULL) { continue; }
00766                 ref_param = (QofParam*)i->data;
00767                 if(ref_param->param_name == NULL) { continue; }
00768                 if(0 == safe_strcmp(ref_param->param_type, QOF_TYPE_COLLECT)) {
00769                         QofCollection *col;
00770 
00771                         col = ref_param->param_getfcn(ent, ref_param);
00772                         if(col) {
00773                         qof_collection_foreach(col, recurse_collection_cb, store);
00774                         }
00775                         continue;
00776                 }
00777                 ref_ent = QOF_INSTANCE(ref_param->param_getfcn(ent, ref_param));
00778                 if((ref_ent)&&(ref_ent->e_type))
00779                 {
00780                         store->success = qof_instance_copy_to_session(session, ref_ent);
00781                         if(store->success) { ent_list = g_list_append(ent_list, ref_ent); }
00782                 }
00783         }
00784         for(i = ent_list; i != NULL; i = i->next)
00785         {
00786                 if(i->data == NULL) { continue; }
00787                 child_ent = QOF_INSTANCE(i->data);
00788                 if(child_ent == NULL) { continue; }
00789                 ref_list = qof_class_get_referenceList(child_ent->e_type);
00790                 for(j = ref_list; j != NULL; j = j->next)
00791                 {
00792                         if(j->data == NULL) { continue; }
00793                         ref_param = (QofParam*)j->data;
00794                         ref_ent = ref_param->param_getfcn(child_ent, ref_param);
00795                         if(ref_ent != NULL)
00796                         {
00797                                 success = qof_instance_copy_to_session(session, ref_ent);
00798                                 if(success) { child_list = g_list_append(child_list, ref_ent); }
00799                         }
00800                 }
00801         }
00802         for(i = child_list; i != NULL; i = i->next)
00803         {
00804                 if(i->data == NULL) { continue; }
00805                 ref_ent = QOF_INSTANCE(i->data);
00806                 if(ref_ent == NULL) { continue; }
00807                 ref_list = qof_class_get_referenceList(ref_ent->e_type);
00808                 for(j = ref_list; j != NULL; j = j->next)
00809                 {
00810                         if(j->data == NULL) { continue; }
00811                         ref_param = (QofParam*)j->data;
00812                         child_ent = ref_param->param_getfcn(ref_ent, ref_param);
00813                         if(child_ent != NULL)
00814                         {
00815                                 qof_instance_copy_to_session(session, child_ent);
00816                         }
00817                 }
00818         }
00819 }
00820 
00821 gboolean
00822 qof_instance_copy_coll_r(QofSession *new_session, QofCollection *coll)
00823 {
00824         struct recurse_s store;
00825         gboolean success;
00826 
00827         if((!new_session)||(!coll)) { return FALSE; }
00828         store.session = new_session;
00829         success = TRUE;
00830         store.success = success;
00831         store.ent_list = NULL;
00832         store.ref_list = qof_class_get_referenceList(qof_collection_get_type(coll));
00833         success = qof_instance_copy_coll(new_session, coll);
00834         if(success){ qof_collection_foreach(coll, recurse_ent_cb, &store); }
00835         return success;
00836 }
00837 
00838 gboolean qof_instance_copy_one_r(QofSession *new_session, QofInstance *ent)
00839 {
00840         struct recurse_s store;
00841         QofCollection *coll;
00842         gboolean success;
00843 
00844         if((!new_session)||(!ent)) { return FALSE; }
00845         store.session = new_session;
00846         success = TRUE;
00847         store.success = success;
00848         store.ref_list = qof_class_get_referenceList(ent->e_type);
00849         success = qof_instance_copy_to_session(new_session, ent);
00850         if(success == TRUE) {
00851                 coll = qof_book_get_collection(qof_session_get_book(new_session), ent->e_type);
00852                 if(coll) { qof_collection_foreach(coll, recurse_ent_cb, &store); }
00853         }
00854         return success;
00855 }
00856 
00857 
00858 /* ====================================================================== */
00859 
00863 struct backend_providers
00864 {
00865         const char *libdir;
00866         const char *filename;
00867 };
00868 
00869 /* All available QOF backends need to be described here
00870 and the last entry must be two NULL's.
00871 Remember: Use the libdir from the current build environment
00872 and use JUST the module name without .so - .so is not portable! */
00873 struct backend_providers backend_list[] = {
00874         { QOF_LIB_DIR, QSF_BACKEND_LIB },
00875 #ifdef HAVE_DWI
00876         { QOF_LIB_DIR, "libqof_backend_dwi"},
00877 #endif
00878         { NULL, NULL }
00879 };
00880 
00881 static void
00882 qof_session_load_backend(QofSession * session, const char * access_method)
00883 {
00884         GSList *p;
00885         GList *node;
00886         QofBackendProvider *prov;
00887         QofBook *book;
00888         char *msg;
00889         gint num;
00890         gboolean prov_type;
00891         gboolean (*type_check) (const char*);
00892         gchar *libdir_from_env = NULL;
00893 
00894         ENTER (" list=%d, initted=%s", g_slist_length(provider_list),
00895                qof_providers_initialized ? "true" : "false");
00896         prov_type = FALSE;
00897         if (!qof_providers_initialized)
00898         {
00899                 libdir_from_env = g_strdup(g_getenv("QOF_LIB_DIR"));
00900                 for (num = 0; backend_list[num].filename != NULL; num++) {
00901                         if (libdir_from_env) {
00902                                 if (!(qof_load_backend_library(libdir_from_env,
00903                                                                                            backend_list[num].filename)
00904                                           || qof_load_backend_library(backend_list[num].libdir,
00905                                                                                                   backend_list[num].filename)))
00906                                 {
00907                                         PWARN (" failed to load %s from %s or %s",
00908                                                    backend_list[num].filename, libdir_from_env,
00909                                                    backend_list[num].libdir);
00910                                 }
00911                         } else {
00912                                 if (!qof_load_backend_library(backend_list[num].libdir,
00913                                                                                           backend_list[num].filename)) {
00914                                         PWARN (" failed to load %s from %s",
00915                                                    backend_list[num].filename, backend_list[num].libdir);
00916                                 }
00917                         }
00918                 }
00919                 g_free(libdir_from_env);
00920                 qof_providers_initialized = TRUE;
00921         }
00922         p = provider_list;
00923         while(p != NULL)
00924         {
00925                 prov = p->data;
00926                 /* Does this provider handle the desired access method? */
00927                 if (0 == strcasecmp (access_method, prov->access_method))
00928                 {
00929                         /* More than one backend could provide this
00930                         access method, check file type compatibility. */
00931                         type_check = (gboolean (*)(const char*)) prov->check_data_type;
00932                         if (type_check) {
00933                             prov_type = (type_check)(session->book_id);
00934                             if (!prov_type) {
00935                                 PINFO(" %s not usable", prov->provider_name);
00936                                 p = p->next;
00937                                 continue;
00938                             }
00939                         }
00940                         PINFO (" selected %s", prov->provider_name);
00941                         if (NULL == prov->backend_new) 
00942                         {
00943                                 p = p->next;
00944                                 continue;
00945                         }
00946                         /* Use the providers creation callback */
00947             session->backend = (*(prov->backend_new))();
00948                         session->backend->provider = prov;
00949                         /* Tell the books about the backend that they'll be using. */
00950                         for (node=session->books; node; node=node->next)
00951                         {
00952                                 book = node->data;
00953                                 qof_book_set_backend (book, session->backend);
00954                         }
00955                         LEAVE (" ");
00956                         return;
00957                 }
00958                 p = p->next;
00959         }
00960         msg = g_strdup_printf("failed to load '%s' using access_method", access_method);
00961         qof_session_push_error (session, ERR_BACKEND_NO_HANDLER, msg);
00962         LEAVE (" ");
00963 }
00964 
00965 /* ====================================================================== */
00966 
00967 static void
00968 qof_session_destroy_backend (QofSession *session)
00969 {
00970   g_return_if_fail (session);
00971 
00972   if (session->backend)
00973   {
00974     /* clear any error message */
00975     char * msg = qof_backend_get_message (session->backend);
00976     g_free (msg);
00977 
00978     /* Then destroy the backend */
00979     if (session->backend->destroy_backend)
00980     {
00981       session->backend->destroy_backend(session->backend);
00982     }
00983     else
00984     {
00985       g_free(session->backend);
00986     }
00987   }
00988 
00989   session->backend = NULL;
00990 }
00991 
00992 void
00993 qof_session_begin (QofSession *session, const char * book_id, 
00994                    gboolean ignore_lock, gboolean create_if_nonexistent)
00995 {
00996   char *p, *access_method, *msg;
00997   int err;
00998 
00999   if (!session) return;
01000 
01001   ENTER (" sess=%p ignore_lock=%d, book-id=%s", 
01002          session, ignore_lock,
01003          book_id ? book_id : "(null)");
01004 
01005   /* Clear the error condition of previous errors */
01006   qof_session_clear_error (session);
01007 
01008   /* Check to see if this session is already open */
01009   if (session->book_id)
01010   {
01011     qof_session_push_error (session, ERR_BACKEND_LOCKED, NULL);
01012     LEAVE("push error book is already open ");
01013     return;
01014   }
01015 
01016   /* seriously invalid */
01017   if (!book_id)
01018   {
01019     qof_session_push_error (session, ERR_BACKEND_BAD_URL, NULL);
01020     LEAVE("push error missing book_id");
01021     return;
01022   }
01023 
01024   /* destroy the old backend */
01025   qof_session_destroy_backend(session);
01026 
01027   /* Store the session URL  */
01028   session->book_id = g_strdup (book_id);
01029 
01030   /* Look for something of the form of "file:/", "http://" or 
01031    * "postgres://". Everything before the colon is the access 
01032    * method.  Load the first backend found for that access method.
01033    */
01034   p = strchr (book_id, ':');
01035   if (p)
01036   {
01037     access_method = g_strdup (book_id);
01038     p = strchr (access_method, ':');
01039     *p = '\0';
01040     qof_session_load_backend(session, access_method);
01041     g_free (access_method);
01042 #ifdef G_OS_WIN32
01043     if (NULL == session->backend)
01044     {
01045       /* Clear the error condition of previous errors */
01046       qof_session_clear_error (session);
01047 
01048       /* On windows, a colon can be part of a normal filename. So if
01049          no backend was found (which means the part before the colon
01050          wasn't an access method), fall back to the file backend. */
01051       qof_session_load_backend(session, "file"); 
01052     }
01053 #endif
01054   }
01055   else
01056   {
01057      /* If no colon found, assume it must be a file-path */
01058      qof_session_load_backend(session, "file"); 
01059   }
01060 
01061   /* No backend was found. That's bad. */
01062   if (NULL == session->backend)
01063   {
01064     g_free(session->book_id);
01065     session->book_id = NULL;
01066     qof_session_push_error (session, ERR_BACKEND_BAD_URL, NULL);
01067     LEAVE (" BAD: no backend: sess=%p book-id=%s", 
01068          session,  book_id ? book_id : "(null)");
01069     return;
01070   }
01071 
01072   /* If there's a begin method, call that. */
01073   if (session->backend->session_begin)
01074   {
01075       
01076       (session->backend->session_begin)(session->backend, session,
01077                                   session->book_id, ignore_lock,
01078                                   create_if_nonexistent);
01079