QOF 0.8.2
|
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 #include <glib.h> 00042 #include <libintl.h> 00043 #include "qof.h" 00044 #include "qoferror-p.h" 00045 #include "qofbackend-p.h" 00046 #include "qofbook-p.h" 00047 #include "qofsession-p.h" 00048 #include "qofobject-p.h" 00049 00050 #define _(String) dgettext (GETTEXT_PACKAGE, String) 00051 00052 static GHookList *session_closed_hooks = NULL; 00053 static QofLogModule log_module = QOF_MOD_SESSION; 00054 static GSList *provider_list = NULL; 00055 00056 /* ============================================================= */ 00057 00058 void 00059 qof_backend_register_provider (QofBackendProvider * prov) 00060 { 00061 provider_list = g_slist_prepend (provider_list, prov); 00062 } 00063 00064 /* =========================================================== */ 00065 00066 /* hook routines */ 00067 00068 void 00069 qof_session_add_close_hook (GFunc fn, gpointer data) 00070 { 00071 GHook *hook; 00072 00073 if (session_closed_hooks == NULL) 00074 { 00075 session_closed_hooks = malloc (sizeof (GHookList)); /* LEAKED */ 00076 g_hook_list_init (session_closed_hooks, sizeof (GHook)); 00077 } 00078 00079 hook = g_hook_alloc (session_closed_hooks); 00080 if (!hook) 00081 return; 00082 00083 hook->func = (GHookFunc) fn; 00084 hook->data = data; 00085 g_hook_append (session_closed_hooks, hook); 00086 } 00087 00088 void 00089 qof_session_call_close_hooks (QofSession * session) 00090 { 00091 GHook *hook; 00092 GFunc fn; 00093 00094 if (session_closed_hooks == NULL) 00095 return; 00096 00097 hook = g_hook_first_valid (session_closed_hooks, FALSE); 00098 while (hook) 00099 { 00100 fn = (GFunc) hook->func; 00101 fn (session, hook->data); 00102 hook = g_hook_next_valid (session_closed_hooks, hook, FALSE); 00103 } 00104 } 00105 00106 /* =============================================================== */ 00107 00108 static void 00109 qof_session_init (QofSession * session) 00110 { 00111 if (!session) 00112 return; 00113 00114 session->books = g_list_append (NULL, qof_book_new ()); 00115 session->book_id = NULL; 00116 session->backend = NULL; 00117 qof_error_init (); 00118 } 00119 00120 QofSession * 00121 qof_session_new (void) 00122 { 00123 QofSession *session = g_new0 (QofSession, 1); 00124 qof_session_init (session); 00125 return session; 00126 } 00127 00128 QofBook * 00129 qof_session_get_book (QofSession * session) 00130 { 00131 GList *node; 00132 if (!session) 00133 return NULL; 00134 00135 for (node = session->books; node; node = node->next) 00136 { 00137 QofBook *book = node->data; 00138 if ('y' == book->book_open) 00139 return book; 00140 } 00141 return NULL; 00142 } 00143 00144 void 00145 qof_session_add_book (QofSession * session, QofBook * addbook) 00146 { 00147 GList *node; 00148 if (!session) 00149 return; 00150 00151 ENTER (" sess=%p book=%p", session, addbook); 00152 00153 /* See if this book is already there ... */ 00154 for (node = session->books; node; node = node->next) 00155 { 00156 QofBook *book = node->data; 00157 if (addbook == book) 00158 return; 00159 } 00160 00161 if ('y' == addbook->book_open) 00162 { 00163 /* hack alert -- someone should free all the books in the list, 00164 * but it should probably not be us ... since the books backends 00165 * should be shutdown first, etc */ 00166 /* XXX this should probably be an error XXX */ 00167 g_list_free (session->books); 00168 session->books = g_list_append (NULL, addbook); 00169 } 00170 else 00171 { 00172 /* XXX Need to tell the backend to add a book as well */ 00173 session->books = g_list_append (session->books, addbook); 00174 } 00175 00176 qof_book_set_backend (addbook, session->backend); 00177 LEAVE (" "); 00178 } 00179 00180 QofBackend * 00181 qof_session_get_backend (QofSession * session) 00182 { 00183 if (!session) 00184 return NULL; 00185 return session->backend; 00186 } 00187 00188 const gchar * 00189 qof_session_get_file_path (QofSession * session) 00190 { 00191 if (!session) 00192 return NULL; 00193 if (!session->backend) 00194 return NULL; 00195 return session->backend->fullpath; 00196 } 00197 00198 const gchar * 00199 qof_session_get_url (QofSession * session) 00200 { 00201 if (!session) 00202 return NULL; 00203 return session->book_id; 00204 } 00205 00206 /* =============================================================== */ 00207 00208 typedef struct qof_entity_copy_data 00209 { 00210 QofEntity *from; 00211 QofEntity *to; 00212 QofParam *param; 00213 GList *referenceList; 00214 GSList *param_list; 00215 QofSession *new_session; 00216 gboolean error; 00217 } QofEntityCopyData; 00218 00219 static void 00220 qof_book_set_partial (QofBook * book) 00221 { 00222 gboolean partial; 00223 00224 partial = 00225 (gboolean) 00226 GPOINTER_TO_INT (qof_book_get_data (book, PARTIAL_QOFBOOK)); 00227 if (!partial) 00228 { 00229 qof_book_set_data (book, PARTIAL_QOFBOOK, GINT_TO_POINTER (TRUE)); 00230 } 00231 } 00232 00233 void 00234 qof_session_update_reference_list (QofSession * session, 00235 QofEntityReference * reference) 00236 { 00237 QofBook *book; 00238 GList *book_ref_list; 00239 00240 book = qof_session_get_book (session); 00241 book_ref_list = (GList *) qof_book_get_data (book, ENTITYREFERENCE); 00242 book_ref_list = g_list_append (book_ref_list, reference); 00243 qof_book_set_data (book, ENTITYREFERENCE, book_ref_list); 00244 qof_book_set_partial (book); 00245 } 00246 00247 static void 00248 qof_entity_param_cb (QofParam * param, gpointer data) 00249 { 00250 QofEntityCopyData *qecd; 00251 00252 g_return_if_fail (data != NULL); 00253 qecd = (QofEntityCopyData *) data; 00254 g_return_if_fail (param != NULL); 00255 /* KVP doesn't need a set routine to be copied. */ 00256 if (0 == safe_strcmp (param->param_type, QOF_TYPE_KVP)) 00257 { 00258 qecd->param_list = g_slist_prepend (qecd->param_list, param); 00259 return; 00260 } 00261 if ((param->param_getfcn != NULL) && (param->param_setfcn != NULL)) 00262 { 00263 qecd->param_list = g_slist_prepend (qecd->param_list, param); 00264 } 00265 } 00266 00267 static void 00268 col_ref_cb (QofEntity * ref_ent, gpointer user_data) 00269 { 00270 QofEntityReference *ref; 00271 QofEntityCopyData *qecd; 00272 QofEntity *ent; 00273 const GUID *cm_guid; 00274 gchar cm_sa[GUID_ENCODING_LENGTH + 1]; 00275 gchar *cm_string; 00276 00277 qecd = (QofEntityCopyData *) user_data; 00278 ent = qecd->from; 00279 ref = g_new0 (QofEntityReference, 1); 00280 ref->type = ent->e_type; 00281 ref->ref_guid = g_new (GUID, 1); 00282 ref->ent_guid = &ent->guid; 00283 ref->param = qof_class_get_parameter (ent->e_type, 00284 qecd->param->param_name); 00285 cm_guid = qof_entity_get_guid (ref_ent); 00286 guid_to_string_buff (cm_guid, cm_sa); 00287 cm_string = g_strdup (cm_sa); 00288 if (TRUE == string_to_guid (cm_string, ref->ref_guid)) 00289 { 00290 g_free (cm_string); 00291 qof_session_update_reference_list (qecd->new_session, ref); 00292 } 00293 } 00294 00295 static void 00296 qof_entity_foreach_copy (gpointer data, gpointer user_data) 00297 { 00298 QofEntity *importEnt, *targetEnt /*, *referenceEnt */ ; 00299 QofEntityCopyData *context; 00300 QofEntityReference *reference; 00301 gboolean registered_type; 00302 /* cm_ prefix used for variables that hold the data to commit */ 00303 QofParam *cm_param; 00304 gchar *cm_string, *cm_char; 00305 const GUID *cm_guid; 00306 KvpFrame *cm_kvp; 00307 QofCollection *cm_col; 00308 /* function pointers and variables for parameter getters that don't use pointers normally */ 00309 QofNumeric cm_numeric, (*numeric_getter) (QofEntity *, QofParam *); 00310 gdouble cm_double, (*double_getter) (QofEntity *, QofParam *); 00311 gboolean cm_boolean, (*boolean_getter) (QofEntity *, QofParam *); 00312 gint32 cm_i32, (*int32_getter) (QofEntity *, QofParam *); 00313 gint64 cm_i64, (*int64_getter) (QofEntity *, QofParam *); 00314 /* function pointers to the parameter setters */ 00315 void (*string_setter) (QofEntity *, const gchar *); 00316 void (*numeric_setter) (QofEntity *, QofNumeric); 00317 void (*guid_setter) (QofEntity *, const GUID *); 00318 void (*double_setter) (QofEntity *, gdouble); 00319 void (*boolean_setter) (QofEntity *, gboolean); 00320 void (*i32_setter) (QofEntity *, gint32); 00321 void (*i64_setter) (QofEntity *, gint64); 00322 void (*char_setter) (QofEntity *, gchar *); 00323 void (*kvp_frame_setter) (QofEntity *, KvpFrame *); 00324 00325 g_return_if_fail (user_data != NULL); 00326 context = (QofEntityCopyData *) user_data; 00327 importEnt = context->from; 00328 targetEnt = context->to; 00329 registered_type = FALSE; 00330 cm_param = (QofParam *) data; 00331 g_return_if_fail (cm_param != NULL); 00332 context->param = cm_param; 00333 if (safe_strcmp (cm_param->param_type, QOF_TYPE_STRING) == 0) 00334 { 00335 cm_string = (gchar *) cm_param->param_getfcn (importEnt, cm_param); 00336 if (cm_string) 00337 { 00338 string_setter = 00339 (void (*)(QofEntity *, 00340 const char *)) cm_param->param_setfcn; 00341 if (string_setter != NULL) 00342 { 00343 qof_util_param_edit ((QofInstance *) targetEnt, cm_param); 00344 string_setter (targetEnt, cm_string); 00345 qof_util_param_commit ((QofInstance *) targetEnt, cm_param); 00346 } 00347 } 00348 registered_type = TRUE; 00349 } 00350 if (safe_strcmp (cm_param->param_type, QOF_TYPE_TIME) == 0) 00351 { 00352 QofTime *qt; 00353 void (*time_setter) (QofEntity *, QofTime *); 00354 00355 qt = cm_param->param_getfcn (importEnt, cm_param); 00356 time_setter = 00357 (void (*)(QofEntity *, QofTime*))cm_param->param_setfcn; 00358 if (time_setter != NULL) 00359 { 00360 qof_util_param_edit ((QofInstance *) targetEnt, cm_param); 00361 time_setter (targetEnt, qt); 00362 qof_util_param_commit ((QofInstance *) targetEnt, cm_param); 00363 } 00364 registered_type = TRUE; 00365 } 00366 if ((safe_strcmp (cm_param->param_type, QOF_TYPE_NUMERIC) == 0) || 00367 (safe_strcmp (cm_param->param_type, QOF_TYPE_DEBCRED) == 0)) 00368 { 00369 numeric_getter = 00370 (QofNumeric (*)(QofEntity *, 00371 QofParam *)) cm_param->param_getfcn; 00372 cm_numeric = numeric_getter (importEnt, cm_param); 00373 numeric_setter = 00374 (void (*)(QofEntity *, QofNumeric)) cm_param->param_setfcn; 00375 if (numeric_setter != NULL) 00376 { 00377 qof_util_param_edit ((QofInstance *) targetEnt, cm_param); 00378 numeric_setter (targetEnt, cm_numeric); 00379 qof_util_param_commit ((QofInstance *) targetEnt, cm_param); 00380 } 00381 registered_type = TRUE; 00382 } 00383 if (safe_strcmp (cm_param->param_type, QOF_TYPE_GUID) == 0) 00384 { 00385 cm_guid = 00386 (const GUID *) cm_param->param_getfcn (importEnt, cm_param); 00387 guid_setter = 00388 (void (*)(QofEntity *, const GUID *)) cm_param->param_setfcn; 00389 if (guid_setter != NULL) 00390 { 00391 qof_util_param_edit ((QofInstance *) targetEnt, cm_param); 00392 guid_setter (targetEnt, cm_guid); 00393 qof_util_param_commit ((QofInstance *) targetEnt, cm_param); 00394 } 00395 registered_type = TRUE; 00396 } 00397 if (safe_strcmp (cm_param->param_type, QOF_TYPE_INT32) == 0) 00398 { 00399 int32_getter = 00400 (gint32 (*)(QofEntity *, QofParam *)) cm_param->param_getfcn; 00401 cm_i32 = int32_getter (importEnt, cm_param); 00402 i32_setter = 00403 (void (*)(QofEntity *, gint32)) cm_param->param_setfcn; 00404 if (i32_setter != NULL) 00405 { 00406 qof_util_param_edit ((QofInstance *) targetEnt, cm_param); 00407 i32_setter (targetEnt, cm_i32); 00408 qof_util_param_commit ((QofInstance *) targetEnt, cm_param); 00409 } 00410 registered_type = TRUE; 00411 } 00412 if (safe_strcmp (cm_param->param_type, QOF_TYPE_INT64) == 0) 00413 { 00414 int64_getter = 00415 (gint64 (*)(QofEntity *, QofParam *)) cm_param->param_getfcn; 00416 cm_i64 = int64_getter (importEnt, cm_param); 00417 i64_setter = 00418 (void (*)(QofEntity *, gint64)) cm_param->param_setfcn; 00419 if (i64_setter != NULL) 00420 { 00421 qof_util_param_edit ((QofInstance *) targetEnt, cm_param); 00422 i64_setter (targetEnt, cm_i64); 00423 qof_util_param_commit ((QofInstance *) targetEnt, cm_param); 00424 } 00425 registered_type = TRUE; 00426 } 00427 if (safe_strcmp (cm_param->param_type, QOF_TYPE_DOUBLE) == 0) 00428 { 00429 double_getter = 00430 (gdouble (*)(QofEntity *, QofParam *)) cm_param->param_getfcn; 00431 cm_double = double_getter (importEnt, cm_param); 00432 double_setter = 00433 (void (*)(QofEntity *, gdouble)) cm_param->param_setfcn; 00434 if (double_setter != NULL) 00435 { 00436 qof_util_param_edit ((QofInstance *) targetEnt, cm_param); 00437 double_setter (targetEnt, cm_double); 00438 qof_util_param_commit ((QofInstance *) targetEnt, cm_param); 00439 } 00440 registered_type = TRUE; 00441 } 00442 if (safe_strcmp (cm_param->param_type, QOF_TYPE_BOOLEAN) == 0) 00443 { 00444 boolean_getter = 00445 (gboolean (*)(QofEntity *, QofParam *)) cm_param->param_getfcn; 00446 cm_boolean = boolean_getter (importEnt, cm_param); 00447 boolean_setter = 00448 (void (*)(QofEntity *, gboolean)) cm_param->param_setfcn; 00449 if (boolean_setter != NULL) 00450 { 00451 qof_util_param_edit ((QofInstance *) targetEnt, cm_param); 00452 boolean_setter (targetEnt, cm_boolean); 00453 qof_util_param_commit ((QofInstance *) targetEnt, cm_param); 00454 } 00455 registered_type = TRUE; 00456 } 00457 if (safe_strcmp (cm_param->param_type, QOF_TYPE_KVP) == 0) 00458 { 00459 cm_kvp = (KvpFrame *) cm_param->param_getfcn (importEnt, cm_param); 00460 kvp_frame_setter = 00461 (void (*)(QofEntity *, KvpFrame *)) cm_param->param_setfcn; 00462 if (kvp_frame_setter != NULL) 00463 { 00464 qof_util_param_edit ((QofInstance *) targetEnt, cm_param); 00465 kvp_frame_setter (targetEnt, cm_kvp); 00466 qof_util_param_commit ((QofInstance *) targetEnt, cm_param); 00467 } 00468 else 00469 { 00470 QofInstance *target_inst; 00471 00472 target_inst = (QofInstance *) targetEnt; 00473 kvp_frame_delete (target_inst->kvp_data); 00474 target_inst->kvp_data = kvp_frame_copy (cm_kvp); 00475 } 00476 registered_type = TRUE; 00477 } 00478 if (safe_strcmp (cm_param->param_type, QOF_TYPE_CHAR) == 0) 00479 { 00480 cm_char = (gchar *) cm_param->param_getfcn (importEnt, cm_param); 00481 char_setter = 00482 (void (*)(QofEntity *, char *)) cm_param->param_setfcn; 00483 if (char_setter != NULL) 00484 { 00485 qof_util_param_edit ((QofInstance *) targetEnt, cm_param); 00486 char_setter (targetEnt, cm_char); 00487 qof_util_param_commit ((QofInstance *) targetEnt, cm_param); 00488 } 00489 registered_type = TRUE; 00490 } 00491 if (safe_strcmp (cm_param->param_type, QOF_TYPE_COLLECT) == 0) 00492 { 00493 cm_col = 00494 (QofCollection *) cm_param->param_getfcn (importEnt, cm_param); 00495 if (cm_col) 00496 { 00497 /* create one reference for each member of the collection. */ 00498 qof_collection_foreach (cm_col, col_ref_cb, context); 00499 } 00500 registered_type = TRUE; 00501 } 00502 if (registered_type == FALSE) 00503 { 00504 /* referenceEnt = (QofEntity*)cm_param->param_getfcn(importEnt, cm_param); 00505 if(!referenceEnt) { return; } 00506 if(!referenceEnt->e_type) { return; }*/ 00507 reference = qof_entity_get_reference_from (importEnt, cm_param); 00508 if (reference) 00509 { 00510 qof_session_update_reference_list (context->new_session, 00511 reference); 00512 } 00513 } 00514 } 00515 00516 static gboolean 00517 qof_entity_guid_match (QofSession * new_session, QofEntity * original) 00518 { 00519 QofEntity *copy; 00520 const GUID *g; 00521 QofIdTypeConst type; 00522 QofBook *targetBook; 00523 QofCollection *coll; 00524 00525 copy = NULL; 00526 g_return_val_if_fail (original != NULL, FALSE); 00527 targetBook = qof_session_get_book (new_session); 00528 g_return_val_if_fail (targetBook != NULL, FALSE); 00529 g = qof_entity_get_guid (original); 00530 type = g_strdup (original->e_type); 00531 coll = qof_book_get_collection (targetBook, type); 00532 copy = qof_collection_lookup_entity (coll, g); 00533 if (copy) 00534 { 00535 return TRUE; 00536 } 00537 return FALSE; 00538 } 00539 00540 static void 00541 qof_entity_list_foreach (gpointer data, gpointer user_data) 00542 { 00543 QofEntityCopyData *qecd; 00544 QofEntity *original; 00545 QofInstance *inst; 00546 QofBook *book; 00547 const GUID *g; 00548 00549 g_return_if_fail (data != NULL); 00550 original = (QofEntity *) data; 00551 g_return_if_fail (user_data != NULL); 00552 qecd = (QofEntityCopyData *) user_data; 00553 if (qof_entity_guid_match (qecd->new_session, original)) 00554 { 00555 return; 00556 } 00557 qecd->from = original; 00558 if (!qof_object_compliance (original->e_type, FALSE)) 00559 { 00560 qecd->error = TRUE; 00561 return; 00562 } 00563 book = qof_session_get_book (qecd->new_session); 00564 inst = 00565 (QofInstance *) qof_object_new_instance (original->e_type, book); 00566 if (!inst) 00567 { 00568 PERR (" failed to create new entity type=%s.", original->e_type); 00569 qecd->error = TRUE; 00570 return; 00571 } 00572 qecd->to = &inst->entity; 00573 g = qof_entity_get_guid (original); 00574 qof_entity_set_guid (qecd->to, g); 00575 if (qecd->param_list != NULL) 00576 { 00577 g_slist_free (qecd->param_list); 00578 qecd->param_list = NULL; 00579 } 00580 qof_class_param_foreach (original->e_type, qof_entity_param_cb, qecd); 00581 g_slist_foreach (qecd->param_list, qof_entity_foreach_copy, qecd); 00582 } 00583 00584 static void 00585 qof_entity_coll_foreach (QofEntity * original, gpointer user_data) 00586 { 00587 QofEntityCopyData *qecd; 00588 const GUID *g; 00589 QofBook *targetBook; 00590 QofCollection *coll; 00591 QofEntity *copy; 00592 00593 g_return_if_fail (user_data != NULL); 00594 copy = NULL; 00595 qecd = (QofEntityCopyData *) user_data; 00596 targetBook = qof_session_get_book (qecd->new_session); 00597 g = qof_entity_get_guid (original); 00598 coll = qof_book_get_collection (targetBook, original->e_type); 00599 copy = qof_collection_lookup_entity (coll, g); 00600 if (copy) 00601 { 00602 qecd->error = TRUE; 00603 } 00604 } 00605 00606 static void 00607 qof_entity_coll_copy (QofEntity * original, gpointer user_data) 00608 { 00609 QofEntityCopyData *qecd; 00610 QofBook *book; 00611 QofInstance *inst; 00612 const GUID *g; 00613 00614 g_return_if_fail (user_data != NULL); 00615 qecd = (QofEntityCopyData *) user_data; 00616 book = qof_session_get_book (qecd->new_session); 00617 if (!qof_object_compliance (original->e_type, TRUE)) 00618 { 00619 return; 00620 } 00621 inst = 00622 (QofInstance *) qof_object_new_instance (original->e_type, book); 00623 qecd->to = &inst->entity; 00624 qecd->from = original; 00625 g = qof_entity_get_guid (original); 00626 qof_entity_set_guid (qecd->to, g); 00627 g_slist_foreach (qecd->param_list, qof_entity_foreach_copy, qecd); 00628 } 00629 00630 gboolean 00631 qof_entity_copy_to_session (QofSession * new_session, QofEntity * original) 00632 { 00633 QofEntityCopyData qecd; 00634 QofInstance *inst; 00635 QofBook *book; 00636 00637 if (!new_session || !original) 00638 return FALSE; 00639 if (qof_entity_guid_match (new_session, original)) 00640 return FALSE; 00641 if (!qof_object_compliance (original->e_type, TRUE)) 00642 return FALSE; 00643 qof_event_suspend (); 00644 qecd.param_list = NULL; 00645 book = qof_session_get_book (new_session); 00646 qecd.new_session = new_session; 00647 qof_book_set_partial (book); 00648 inst = 00649 (QofInstance *) qof_object_new_instance (original->e_type, book); 00650 qecd.to = &inst->entity; 00651 qecd.from = original; 00652 qof_entity_set_guid (qecd.to, qof_entity_get_guid (original)); 00653 qof_class_param_foreach (original->e_type, qof_entity_param_cb, &qecd); 00654 if (g_slist_length (qecd.param_list) == 0) 00655 return FALSE; 00656 g_slist_foreach (qecd.param_list, qof_entity_foreach_copy, &qecd); 00657 g_slist_free (qecd.param_list); 00658 qof_event_resume (); 00659 return TRUE; 00660 } 00661 00662 gboolean 00663 qof_entity_copy_list (QofSession * new_session, GList * entity_list) 00664 { 00665 QofEntityCopyData *qecd; 00666 00667 if (!new_session || !entity_list) 00668 return FALSE; 00669 ENTER (" list=%d", g_list_length (entity_list)); 00670 qecd = g_new0 (QofEntityCopyData, 1); 00671 qof_event_suspend (); 00672 qecd->param_list = NULL; 00673 qecd->new_session = new_session; 00674 qof_book_set_partial (qof_session_get_book (new_session)); 00675 g_list_foreach (entity_list, qof_entity_list_foreach, qecd); 00676 qof_event_resume (); 00677 if (qecd->error) 00678 PWARN (" some/all entities in the list could not be copied."); 00679 g_free (qecd); 00680 LEAVE (" "); 00681 return TRUE; 00682 } 00683 00684 gboolean 00685 qof_entity_copy_coll (QofSession * new_session, 00686 QofCollection * entity_coll) 00687 { 00688 QofEntityCopyData qecd; 00689 00690 g_return_val_if_fail (new_session, FALSE); 00691 if (!entity_coll) 00692 { 00693 return FALSE; 00694 } 00695 qof_event_suspend (); 00696 qecd.param_list = NULL; 00697 qecd.new_session = new_session; 00698 qof_book_set_partial (qof_session_get_book (qecd.new_session)); 00699 qof_collection_foreach (entity_coll, qof_entity_coll_foreach, &qecd); 00700 qof_class_param_foreach (qof_collection_get_type (entity_coll), 00701 qof_entity_param_cb, &qecd); 00702 qof_collection_foreach (entity_coll, qof_entity_coll_copy, &qecd); 00703 if (qecd.param_list != NULL) 00704 { 00705 g_slist_free (qecd.param_list); 00706 } 00707 qof_event_resume (); 00708 return TRUE; 00709 } 00710 00711 struct recurse_s 00712 { 00713 QofSession *session; 00714 gboolean success; 00715 GList *ref_list; 00716 GList *ent_list; 00717 }; 00718 00719 static void 00720 recurse_collection_cb (QofEntity * ent, gpointer user_data) 00721 { 00722 struct recurse_s *store; 00723 00724 if (user_data == NULL) 00725 { 00726 return; 00727 } 00728 store = (struct recurse_s *) user_data; 00729 if (!ent || !store) 00730 { 00731 return; 00732 } 00733 store->success = qof_entity_copy_to_session (store->session, ent); 00734 if (store->success) 00735 { 00736 store->ent_list = g_list_append (store->ent_list, ent); 00737 } 00738 } 00739 00740 static void 00741 recurse_ent_cb (QofEntity * ent, gpointer user_data) 00742 { 00743 GList *ref_list, *i, *j, *ent_list, *child_list; 00744 QofParam *ref_param; 00745 QofEntity *ref_ent, *child_ent; 00746 QofSession *session; 00747 struct recurse_s *store; 00748 gboolean success; 00749 00750 if (user_data == NULL) 00751 { 00752 return; 00753 } 00754 store = (struct recurse_s *) user_data; 00755 session = store->session; 00756 success = store->success; 00757 ref_list = NULL; 00758 child_ent = NULL; 00759 ref_list = g_list_copy (store->ref_list); 00760 if ((!session) || (!ent)) 00761 { 00762 return; 00763 } 00764 ent_list = NULL; 00765 child_list = NULL; 00766 i = NULL; 00767 j = NULL; 00768 for (i = ref_list; i != NULL; i = i->next) 00769 { 00770 if (i->data == NULL) 00771 { 00772 continue; 00773 } 00774 ref_param = (QofParam *) i->data; 00775 if (ref_param->param_name == NULL) 00776 { 00777 continue; 00778 } 00779 if (0 == safe_strcmp (ref_param->param_type, QOF_TYPE_COLLECT)) 00780 { 00781 QofCollection *col; 00782 00783 col = ref_param->param_getfcn (ent, ref_param); 00784 if (col) 00785 { 00786 qof_collection_foreach (col, recurse_collection_cb, store); 00787 } 00788 continue; 00789 } 00790 ref_ent = (QofEntity *) ref_param->param_getfcn (ent, ref_param); 00791 if ((ref_ent) && (ref_ent->e_type)) 00792 { 00793 store->success = qof_entity_copy_to_session (session, ref_ent); 00794 if (store->success) 00795 { 00796 ent_list = g_list_append (ent_list, ref_ent); 00797 } 00798 } 00799 } 00800 for (i = ent_list; i != NULL; i = i->next) 00801 { 00802 if (i->data == NULL) 00803 { 00804 continue; 00805 } 00806 child_ent = (QofEntity *) i->data; 00807 if (child_ent == NULL) 00808 { 00809 continue; 00810 } 00811 ref_list = qof_class_get_referenceList (child_ent->e_type); 00812 for (j = ref_list; j != NULL; j = j->next) 00813 { 00814 if (j->data == NULL) 00815 { 00816 continue; 00817 } 00818 ref_param = (QofParam *) j->data; 00819 ref_ent = ref_param->param_getfcn (child_ent, ref_param); 00820 if (ref_ent != NULL) 00821 { 00822 success = qof_entity_copy_to_session (session, ref_ent); 00823 if (success) 00824 { 00825 child_list = g_list_append (child_list, ref_ent); 00826 } 00827 } 00828 } 00829 } 00830 for (i = child_list; i != NULL; i = i->next) 00831 { 00832 if (i->data == NULL) 00833 { 00834 continue; 00835 } 00836 ref_ent = (QofEntity *) i->data; 00837 if (ref_ent == NULL) 00838 { 00839 continue; 00840 } 00841 ref_list = qof_class_get_referenceList (ref_ent->e_type); 00842 for (j = ref_list; j != NULL; j = j->next) 00843 { 00844 if (j->data == NULL) 00845 { 00846 continue; 00847 } 00848 ref_param = (QofParam *) j->data; 00849 child_ent = ref_param->param_getfcn (ref_ent, ref_param); 00850 if (child_ent != NULL) 00851 { 00852 qof_entity_copy_to_session (session, child_ent); 00853 } 00854 } 00855 } 00856 } 00857 00858 gboolean 00859 qof_entity_copy_coll_r (QofSession * new_session, QofCollection * coll) 00860 { 00861 struct recurse_s store; 00862 gboolean success; 00863 00864 if ((!new_session) || (!coll)) 00865 { 00866 return FALSE; 00867 } 00868 store.session = new_session; 00869 success = TRUE; 00870 store.success = success; 00871 store.ent_list = NULL; 00872 store.ref_list = 00873 qof_class_get_referenceList (qof_collection_get_type (coll)); 00874 success = qof_entity_copy_coll (new_session, coll); 00875 if (success) 00876 { 00877 qof_collection_foreach (coll, recurse_ent_cb, &store); 00878 } 00879 return success; 00880 } 00881 00882 gboolean 00883 qof_entity_copy_one_r (QofSession * new_session, QofEntity * ent) 00884 { 00885 struct recurse_s store; 00886 QofCollection *coll; 00887 gboolean success; 00888 00889 if ((!new_session) || (!ent)) 00890 { 00891 return FALSE; 00892 } 00893 store.session = new_session; 00894 success = TRUE; 00895 store.success = success; 00896 store.ref_list = qof_class_get_referenceList (ent->e_type); 00897 success = qof_entity_copy_to_session (new_session, ent); 00898 if (success == TRUE) 00899 { 00900 coll = 00901 qof_book_get_collection (qof_session_get_book (new_session), 00902 ent->e_type); 00903 if (coll) 00904 { 00905 qof_collection_foreach (coll, recurse_ent_cb, &store); 00906 } 00907 } 00908 return success; 00909 } 00910 00911 00912 /* ============================================================== */ 00913 00917 struct backend_providers 00918 { 00919 const gchar *libdir; 00920 const gchar *filename; 00921 const gchar *init_fcn; 00922 }; 00923 00924 /* All available QOF backends need to be described here 00925 and the last entry must be three NULL's. 00926 Remember: Use the libdir from the current build environment 00927 and use JUST the module name without .so - .so is not portable! */ 00928 struct backend_providers backend_list[] = { 00929 {QOF_LIB_DIR, QSF_BACKEND_LIB, QSF_MODULE_INIT}, 00930 {QOF_LIB_DIR, "libqof-backend-sqlite", "qof_sqlite_provider_init"}, 00931 #ifdef HAVE_GDA 00932 {QOF_LIB_DIR, "libqof-backend-gda", "qof_gda_provider_init"}, 00933 #endif 00934 #ifdef HAVE_ESTRON 00935 {QOF_LIB_DIR, "libqof_backend_estron", "dwiend_provider_init"}, 00936 #endif 00937 {NULL, NULL, NULL} 00938 }; 00939 00940 static void 00941 qof_session_load_backend (QofSession * session, gchar *access_method) 00942 { 00943 GSList *p; 00944 GList *node; 00945 QofBackendProvider *prov; 00946 QofBook *book; 00947 gint num; 00948 gboolean prov_type; 00949 gboolean (*type_check) (const gchar *); 00950 00951 ENTER (" list=%d", g_slist_length (provider_list)); 00952 prov_type = FALSE; 00953 if (NULL == provider_list) 00954 { 00955 for (num = 0; backend_list[num].filename != NULL; num++) 00956 { 00957 if (!qof_load_backend_library (backend_list[num].libdir, 00958 backend_list[num].filename, 00959 backend_list[num].init_fcn)) 00960 { 00961 PWARN (" failed to load %s from %s using %s", 00962 backend_list[num].filename, backend_list[num].libdir, 00963 backend_list[num].init_fcn); 00964 } 00965 } 00966 } 00967 p = g_slist_copy (provider_list); 00968 while (p != NULL) 00969 { 00970 prov = p->data; 00971 /* Does this provider handle the desired access method? */ 00972 if (0 == strcasecmp (access_method, prov->access_method)) 00973 { 00974 /* More than one backend could provide this 00975 access method, check file type compatibility. */ 00976 type_check = 00977 (gboolean (*)(const gchar *)) prov->check_data_type; 00978 prov_type = (type_check) (session->book_id); 00979 if (!prov_type) 00980 { 00981 PINFO (" %s not usable", prov->provider_name); 00982 p = p->next; 00983 continue; 00984 } 00985 PINFO (" selected %s", prov->provider_name); 00986 if (NULL == prov->backend_new) 00987 { 00988 p = p->next; 00989 continue; 00990 } 00991 /* Use the providers creation callback */ 00992 session->backend = (*(prov->backend_new)) (); 00993 session->backend->provider = prov; 00994 /* Tell the books about the backend that they'll be using. */ 00995 for (node = session->books; node; node = node->next) 00996 { 00997 book = node->data; 00998 qof_book_set_backend (book, session->backend); 00999 } 01000 LEAVE (" "); 01001 return; 01002 } 01003 p = p->next; 01004 } 01005 LEAVE (" "); 01006 } 01007 01008 /* =============================================================== */ 01009 01010 static void 01011 qof_session_destroy_backend (QofSession * session) 01012 { 01013 g_return_if_fail (session); 01014 01015 if (session->backend) 01016 { 01017 /* Then destroy the backend */ 01018 if (session->backend->destroy_backend) 01019 { 01020 session->backend->destroy_backend (session->backend); 01021 } 01022 else 01023 { 01024 g_free (session->backend); 01025 } 01026 } 01027 01028 session->backend = NULL; 01029 } 01030 01031 void 01032 qof_session_begin (QofSession * session, const gchar *book_id, 01033 gboolean ignore_lock, gboolean create_if_nonexistent) 01034 { 01035 gchar *p, *access_method; 01036 01037 if (!session) 01038 return; 01039 01040 ENTER (" sess=%p ignore_lock=%d, book-id=%s", 01041 session, ignore_lock, book_id ? book_id : "(null)"); 01042 01043 /* Clear the error condition of previous errors */ 01044 qof_error_clear (session); 01045 01046 /* Check to see if this session is already open */ 01047 if (session->book_id) 01048 { 01049 qof_error_set (session, qof_error_register 01050 (_("This book appears to be open already."), FALSE)); 01051 LEAVE (" push error book is already open "); 01052 return; 01053 } 01054 01055 if (!book_id) 01056 { 01057 LEAVE (" using stdout"); 01058 return; 01059 } 01060 01061 /* Store the session URL */ 01062 session->book_id = g_strdup (book_id); 01063 01064 /* destroy the old backend */ 01065 qof_session_destroy_backend (session); 01066 01067 /* Look for something of the form of "file:/", "http://" or 01068 * "postgres://". Everything before the colon is the access 01069 * method. Load the first backend found for that access method. 01070 */ 01071 p = strchr (book_id, ':'); 01072 if (p) 01073 { 01074 access_method = g_strdup (book_id); 01075 p = strchr (access_method, ':'); 01076 *p = 0; 01077 qof_session_load_backend (session, access_method); 01078 g_free (access_method); 01079 } 01080 else 01081 { 01082 /* If no colon found, assume it must be a file-path */ 01083 qof_session_load_backend (session, "file"); 01084 } 01085 01086 /* No backend was found. That's bad. */ 01087 if (NULL == session->backend) 01088 { 01089 gchar * msg; 01090 01091 msg = g_strdup_printf (_("Unable to locate a " 01092 "suitable backend for '%s' - please check " 01093 "you have specified an access method " 01094 "like file: or sqlite:"), book_id); 01095 qof_error_set (session, qof_error_register 01096 (msg, FALSE)); 01097 DEBUG (" msg=%s", msg); 01098 LEAVE (" BAD: no backend: sess=%p book-id=%s", 01099 session, book_id ? book_id : "(null)"); 01100 g_free (msg); 01101 return; 01102 } 01103 01104 /* If there's a begin method, call that. */ 01105 if (session->backend->session_begin) 01106 { 01107 (session->backend->session_begin) (session->backend, session, 01108 session->book_id, ignore_lock, create_if_nonexistent); 01109 PINFO (" Done running session_begin on backend"); 01110 if (qof_error_check(session) != QOF_SUCCESS) 01111 { 01112 g_free (session->book_id); 01113 session->book_id = NULL; 01114 LEAVE (" backend error "); 01115 return; 01116 } 01117 } 01118 qof_error_clear (session); 01119 LEAVE (" sess=%p book-id=%s", session, book_id ? book_id : "(null)"); 01120 } 01121 01122 /* ============================================================== */ 01123 01124 void 01125 qof_session_load (QofSession * session, QofPercentageFunc percentage_func) 01126 { 01127 QofBook *newbook, *ob; 01128 QofBookList *oldbooks, *node; 01129 QofBackend *be; 01130 01131 if (!session) 01132 return; 01133 if ((!session->book_id) || 01134 (0 == safe_strcasecmp(session->book_id, QOF_STDOUT))) 01135 return; 01136 01137 ENTER (" sess=%p book_id=%s", session, session->book_id 01138 ? session->book_id : "(null)"); 01139 01140 /* At this point, we should are supposed to have a valid book 01141 * id and a lock on the file. */ 01142 01143 oldbooks = session->books; 01144 01145 /* XXX why are we creating a book here? I think the books 01146 * need to be handled by the backend ... especially since 01147 * the backend may need to load multiple books ... XXX. FIXME. 01148 */ 01149 newbook = qof_book_new (); 01150 session->books = g_list_append (NULL, newbook); 01151 PINFO (" new book=%p", newbook); 01152 01153 qof_error_clear (session); 01154 01155 /* This code should be sufficient to initialize *any* backend, 01156 * whether http, postgres, or anything else that might come along. 01157 * Basically, the idea is that by now, a backend has already been 01158 * created & set up. At this point, we only need to get the 01159 * top-level account group out of the backend, and that is a 01160 * generic, backend-independent operation. 01161 */ 01162 be = session->backend; 01163 qof_book_set_backend (newbook, be); 01164 01165 /* Starting the session should result in a bunch of accounts 01166 * and currencies being downloaded, but probably no transactions; 01167 * The GUI will need to do a query for that. 01168 */ 01169 if (be) 01170 { 01171 be->percentage = percentage_func; 01172 01173 if (be->load) 01174 { 01175 be->load (be, newbook); 01176 } 01177 } 01178 01179 if (qof_error_check(session) != QOF_SUCCESS) 01180 { 01181 /* Something broke, put back the old stuff */ 01182 qof_book_set_backend (newbook, NULL); 01183 qof_book_destroy (newbook); 01184 g_list_free (session->books); 01185 session->books = oldbooks; 01186 g_free (session->book_id); 01187 session->book_id = NULL; 01188 LEAVE (" error from backend "); 01189 return; 01190 } 01191 01192 for (node = oldbooks; node; node = node->next) 01193 { 01194 ob = node->data; 01195 qof_book_set_backend (ob, NULL); 01196 qof_book_destroy (ob); 01197 } 01198 g_list_free (oldbooks); 01199 01200 LEAVE (" sess = %p, book_id=%s", session, session->book_id 01201 ? session->book_id : "(null)"); 01202 } 01203 01204 /* ============================================================= */ 01205 01206 gboolean 01207 qof_session_save_may_clobber_data (QofSession * session) 01208 { 01209 if (!session) 01210 return FALSE; 01211 if (!session->backend) 01212 return FALSE; 01213 if (!session->backend->save_may_clobber_data) 01214 return FALSE; 01215 01216 return (*(session->backend->save_may_clobber_data)) (session->backend); 01217 } 01218 01219 void 01220 qof_session_save (QofSession * session, 01221 QofPercentageFunc percentage_func) 01222 { 01223 GList *node; 01224 QofBackend *be; 01225 gboolean partial, change_backend; 01226 QofBackendProvider *prov; 01227 GSList *p; 01228 QofBook *book, *abook; 01229 gint num; 01230 gchar *msg, *book_id; 01231 01232 if (!session) 01233 return; 01234 ENTER (" sess=%p book_id=%s", 01235 session, session->book_id ? session->book_id : "(null)"); 01236 /* Partial book handling. */ 01237 book = qof_session_get_book (session); 01238 partial = 01239 (gboolean) 01240 GPOINTER_TO_INT (qof_book_get_data (book, PARTIAL_QOFBOOK)); 01241 change_backend = FALSE; 01242 msg = g_strdup_printf (" "); 01243 book_id = g_strdup (session->book_id); 01244 if (partial == TRUE) 01245 { 01246 if (session->backend && session->backend->provider) 01247 { 01248 prov = session->backend->provider; 01249 if (TRUE == prov->partial_book_supported) 01250 { 01251 /* if current backend supports partial, leave alone. */ 01252 change_backend = FALSE; 01253 } 01254 else 01255 { 01256 change_backend = TRUE; 01257 } 01258 } 01259 /* If provider is undefined, assume partial not supported. */ 01260 else 01261 { 01262 change_backend = TRUE; 01263 } 01264 } 01265 if (change_backend == TRUE) 01266 { 01267 qof_session_destroy_backend (session); 01268 if (NULL == provider_list) 01269 { 01270 for (num = 0; backend_list[num].filename != NULL; num++) 01271 { 01272 qof_load_backend_library (backend_list[num].libdir, 01273 backend_list[num].filename, 01274 backend_list[num].init_fcn); 01275 } 01276 } 01277 p = g_slist_copy (provider_list); 01278 while (p != NULL) 01279 { 01280 prov = p->data; 01281 if (TRUE == prov->partial_book_supported) 01282 { 01284 /* if((TRUE == prov->partial_book_supported) && 01285 (0 == strcasecmp (access_method, prov->access_method))) 01286 { */ 01287 if (NULL == prov->backend_new) 01288 continue; 01289 /* Use the providers creation callback */ 01290 session->backend = (*(prov->backend_new)) (); 01291 session->backend->provider = prov; 01292 if (session->backend->session_begin) 01293 { 01294 /* Call begin - backend has been changed, 01295 so make sure a file can be written, 01296 use ignore_lock and create_if_nonexistent */ 01297 g_free (session->book_id); 01298 session->book_id = NULL; 01299 (session->backend->session_begin) (session->backend, 01300 session, book_id, TRUE, TRUE); 01301 PINFO 01302 (" Done running session_begin on changed backend"); 01303 if (qof_error_check (session) != QOF_SUCCESS) 01304 { 01305 g_free (session->book_id); 01306 session->book_id = NULL; 01307 LEAVE (" changed backend error"); 01308 return; 01309 } 01310 } 01311 /* Tell the books about the backend that they'll be using. */ 01312 for (node = session->books; node; node = node->next) 01313 { 01314 book = node->data; 01315 qof_book_set_backend (book, session->backend); 01316 } 01317 p = NULL; 01318 } 01319 if (p) 01320 { 01321 p = p->next; 01322 } 01323 } 01324 if (!session->backend) 01325 { 01326 msg = g_strdup_printf (" failed to load backend"); 01327 qof_error_set (session, qof_error_register 01328 (_("Failed to load backend, no suitable handler."), 01329 FALSE)); 01330 return; 01331 } 01332 } 01333 /* If there is a backend, and the backend is reachable 01334 * (i.e. we can communicate with it), then synchronize with 01335 * the backend. If we cannot contact the backend (e.g. 01336 * because we've gone offline, the network has crashed, etc.) 01337 * then give the user the option to save to the local disk. 01338 * 01339 * hack alert -- FIXME -- XXX the code below no longer 01340 * does what the words above say. This needs fixing. 01341 */ 01342 be = session->backend; 01343 if (be) 01344 { 01345 for (node = session->books; node; node = node->next) 01346 { 01347 abook = node->data; 01348 /* if invoked as SaveAs(), then backend not yet set */ 01349 qof_book_set_backend (abook, be); 01350 be->percentage = percentage_func; 01351 if (be->sync) 01352 (be->sync) (be, abook); 01353 } 01354 /* If we got to here, then the backend saved everything 01355 * just fine, and we are done. So return. */ 01356 /* Return the book_id to previous value. */ 01357 qof_error_clear (session); 01358 LEAVE (" Success"); 01359 return; 01360 } 01361 else 01362 { 01363 msg = g_strdup_printf (" failed to load backend"); 01364 qof_error_set (session, qof_error_register 01365 (_("Failed to load backend, no suitable handler."), 01366 FALSE)); 01367 } 01368 LEAVE (" error -- No backend!"); 01369 } 01370 01371 /* ============================================================= */ 01372 01373 void 01374 qof_session_end (QofSession * session) 01375 { 01376 if (!session) 01377 return; 01378 01379 ENTER (" sess=%p book_id=%s", session, session->book_id 01380 ? session->book_id : "(null)"); 01381 01382 /* close down the backend first */ 01383 if (session->backend && session->backend->session_end) 01384 { 01385 (session->backend->session_end) (session->backend); 01386 } 01387 01388 qof_error_clear (session); 01389 01390 g_free (session->book_id); 01391 session->book_id = NULL; 01392 01393 LEAVE (" sess=%p book_id=%s", session, session->book_id 01394 ? session->book_id : "(null)"); 01395 } 01396 01397 void 01398 qof_session_destroy (QofSession * session) 01399 { 01400 GList *node; 01401 if (!session) 01402 return; 01403 01404 ENTER (" sess=%p book_id=%s", session, session->book_id 01405 ? session->book_id : "(null)"); 01406 01407 qof_session_end (session); 01408 01409 /* destroy the backend */ 01410 qof_session_destroy_backend (session); 01411 01412 for (node = session->books; node; node = node->next) 01413 { 01414 QofBook *book = node->data; 01415 qof_book_set_backend (book, NULL); 01416 qof_book_destroy (book); 01417 } 01418 01419 session->books = NULL; 01420 g_free (session); 01421 qof_error_close (); 01422 01423 LEAVE (" sess=%p", session); 01424 } 01425 01426 /* ============================================================= */ 01427 01428 void 01429 qof_session_swap_data (QofSession * session_1, QofSession * session_2) 01430 { 01431 GList *books_1, *books_2, *node; 01432 01433 if (session_1 == session_2) 01434 return; 01435 if (!session_1 || !session_2) 01436 return; 01437 01438 ENTER (" sess1=%p sess2=%p", session_1, session_2); 01439 01440 books_1 = session_1->books; 01441 books_2 = session_2->books; 01442 01443 session_1->books = books_2; 01444 session_2->books = books_1; 01445 01446 for (node = books_1; node; node = node->next) 01447 { 01448 QofBook *book_1 = node->data; 01449 qof_book_set_backend (book_1, session_2->backend); 01450 } 01451 for (node = books_2; node; node = node->next) 01452 { 01453 QofBook *book_2 = node->data; 01454 qof_book_set_backend (book_2, session_1->backend); 01455 } 01456 01457 LEAVE (" "); 01458 } 01459 01460 /* ============================================================= */ 01461 01462 gboolean 01463 qof_session_events_pending (QofSession * session) 01464 { 01465 if (!session) 01466 return FALSE; 01467 if (!session->backend) 01468 return FALSE; 01469 if (!session->backend->events_pending) 01470 return FALSE; 01471 01472 return session->backend->events_pending (session->backend); 01473 } 01474 01475 gboolean 01476 qof_session_process_events (QofSession * session) 01477 { 01478 if (!session) 01479 return FALSE; 01480 if (!session->backend) 01481 return FALSE; 01482 if (!session->backend->process_events) 01483 return FALSE; 01484 01485 return session->backend->process_events (session->backend); 01486 } 01487 01488 /* ============== END OF FILE ========================== */