00001
00002
00003
00004
00005
00006
00007
00008
00009
00010
00011
00012
00013
00014
00015
00016
00017
00018
00019
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 "qof.h"
00043 #include "qofbackend-p.h"
00044 #include "qofbook-p.h"
00045 #include "qofsession-p.h"
00046 #include "qofobject-p.h"
00047
00048 static GHookList *session_closed_hooks = NULL;
00049 static QofLogModule log_module = QOF_MOD_SESSION;
00050 static GSList *provider_list = NULL;
00051
00052
00053
00054 void
00055 qof_backend_register_provider (QofBackendProvider * prov)
00056 {
00057 provider_list = g_slist_prepend (provider_list, prov);
00058 }
00059
00060
00061
00062
00063
00064 void
00065 qof_session_add_close_hook (GFunc fn, gpointer data)
00066 {
00067 GHook *hook;
00068
00069 if (session_closed_hooks == NULL)
00070 {
00071 session_closed_hooks = malloc (sizeof (GHookList));
00072 g_hook_list_init (session_closed_hooks, sizeof (GHook));
00073 }
00074
00075 hook = g_hook_alloc (session_closed_hooks);
00076 if (!hook)
00077 return;
00078
00079 hook->func = (GHookFunc) fn;
00080 hook->data = data;
00081 g_hook_append (session_closed_hooks, hook);
00082 }
00083
00084 void
00085 qof_session_call_close_hooks (QofSession * session)
00086 {
00087 GHook *hook;
00088 GFunc fn;
00089
00090 if (session_closed_hooks == NULL)
00091 return;
00092
00093 hook = g_hook_first_valid (session_closed_hooks, FALSE);
00094 while (hook)
00095 {
00096 fn = (GFunc) hook->func;
00097 fn (session, hook->data);
00098 hook = g_hook_next_valid (session_closed_hooks, hook, FALSE);
00099 }
00100 }
00101
00102
00103
00104
00105 static void
00106 qof_session_clear_error (QofSession * session)
00107 {
00108 QofBackendError err;
00109
00110 session->last_err = ERR_BACKEND_NO_ERR;
00111 g_free (session->error_message);
00112 session->error_message = NULL;
00113
00114
00115 if (session->backend)
00116 {
00117 do
00118 {
00119 err = qof_backend_get_error (session->backend);
00120 }
00121 while (ERR_BACKEND_NO_ERR != err);
00122 }
00123 }
00124
00125 void
00126 qof_session_push_error (QofSession * session, QofBackendError err,
00127 const gchar *message)
00128 {
00129 if (!session)
00130 return;
00131
00132 g_free (session->error_message);
00133
00134 session->last_err = err;
00135 session->error_message = g_strdup (message);
00136 }
00137
00138 QofBackendError
00139 qof_session_get_error (QofSession * session)
00140 {
00141 QofBackendError err;
00142
00143 if (!session)
00144 return ERR_BACKEND_NO_BACKEND;
00145
00146
00147 if (ERR_BACKEND_NO_ERR != session->last_err)
00148 {
00149 return session->last_err;
00150 }
00151
00152
00153 if (!session->backend)
00154 return ERR_BACKEND_NO_ERR;
00155
00156 err = qof_backend_get_error (session->backend);
00157 session->last_err = err;
00158 return err;
00159 }
00160
00161 static const gchar *
00162 get_default_error_message (QofBackendError err
00163 __attribute__ ((unused)))
00164 {
00165 return "";
00166 }
00167
00168 const gchar *
00169 qof_session_get_error_message (QofSession * session)
00170 {
00171 if (!session)
00172 return "";
00173 if (!session->error_message)
00174 return get_default_error_message (session->last_err);
00175 return session->error_message;
00176 }
00177
00178 QofBackendError
00179 qof_session_pop_error (QofSession * session)
00180 {
00181 QofBackendError err;
00182
00183 if (!session)
00184 return ERR_BACKEND_NO_BACKEND;
00185
00186 err = qof_session_get_error (session);
00187 qof_session_clear_error (session);
00188
00189 return err;
00190 }
00191
00192
00193
00194 static void
00195 qof_session_init (QofSession * session)
00196 {
00197 if (!session)
00198 return;
00199
00200 session->entity.e_type = QOF_ID_SESSION;
00201 session->books = g_list_append (NULL, qof_book_new ());
00202 session->book_id = NULL;
00203 session->backend = NULL;
00204
00205 qof_session_clear_error (session);
00206 }
00207
00208 QofSession *
00209 qof_session_new (void)
00210 {
00211 QofSession *session = g_new0 (QofSession, 1);
00212 qof_session_init (session);
00213 return session;
00214 }
00215
00216 QofBook *
00217 qof_session_get_book (QofSession * session)
00218 {
00219 GList *node;
00220 if (!session)
00221 return NULL;
00222
00223 for (node = session->books; node; node = node->next)
00224 {
00225 QofBook *book = node->data;
00226 if ('y' == book->book_open)
00227 return book;
00228 }
00229 return NULL;
00230 }
00231
00232 void
00233 qof_session_add_book (QofSession * session, QofBook * addbook)
00234 {
00235 GList *node;
00236 if (!session)
00237 return;
00238
00239 ENTER (" sess=%p book=%p", session, addbook);
00240
00241
00242 for (node = session->books; node; node = node->next)
00243 {
00244 QofBook *book = node->data;
00245 if (addbook == book)
00246 return;
00247 }
00248
00249 if ('y' == addbook->book_open)
00250 {
00251
00252
00253
00254
00255 g_list_free (session->books);
00256 session->books = g_list_append (NULL, addbook);
00257 }
00258 else
00259 {
00260
00261 session->books = g_list_append (session->books, addbook);
00262 }
00263
00264 qof_book_set_backend (addbook, session->backend);
00265 LEAVE (" ");
00266 }
00267
00268 QofBackend *
00269 qof_session_get_backend (QofSession * session)
00270 {
00271 if (!session)
00272 return NULL;
00273 return session->backend;
00274 }
00275
00276 const gchar *
00277 qof_session_get_file_path (QofSession * session)
00278 {
00279 if (!session)
00280 return NULL;
00281 if (!session->backend)
00282 return NULL;
00283 return session->backend->fullpath;
00284 }
00285
00286 const gchar *
00287 qof_session_get_url (QofSession * session)
00288 {
00289 if (!session)
00290 return NULL;
00291 return session->book_id;
00292 }
00293
00294
00295
00296 typedef struct qof_entity_copy_data
00297 {
00298 QofEntity *from;
00299 QofEntity *to;
00300 QofParam *param;
00301 GList *referenceList;
00302 GSList *param_list;
00303 QofSession *new_session;
00304 gboolean error;
00305 } QofEntityCopyData;
00306
00307 static void
00308 qof_book_set_partial (QofBook * book)
00309 {
00310 gboolean partial;
00311
00312 partial =
00313 (gboolean)
00314 GPOINTER_TO_INT (qof_book_get_data (book, PARTIAL_QOFBOOK));
00315 if (!partial)
00316 {
00317 qof_book_set_data (book, PARTIAL_QOFBOOK, GINT_TO_POINTER (TRUE));
00318 }
00319 }
00320
00321 void
00322 qof_session_update_reference_list (QofSession * session,
00323 QofEntityReference * reference)
00324 {
00325 QofBook *book;
00326 GList *book_ref_list;
00327
00328 book = qof_session_get_book (session);
00329 book_ref_list = (GList *) qof_book_get_data (book, ENTITYREFERENCE);
00330 book_ref_list = g_list_append (book_ref_list, reference);
00331 qof_book_set_data (book, ENTITYREFERENCE, book_ref_list);
00332 qof_book_set_partial (book);
00333 }
00334
00335 static void
00336 qof_entity_param_cb (QofParam * param, gpointer data)
00337 {
00338 QofEntityCopyData *qecd;
00339
00340 g_return_if_fail (data != NULL);
00341 qecd = (QofEntityCopyData *) data;
00342 g_return_if_fail (param != NULL);
00343
00344 if (0 == safe_strcmp (param->param_type, QOF_TYPE_KVP))
00345 {
00346 qecd->param_list = g_slist_prepend (qecd->param_list, param);
00347 return;
00348 }
00349 if ((param->param_getfcn != NULL) && (param->param_setfcn != NULL))
00350 {
00351 qecd->param_list = g_slist_prepend (qecd->param_list, param);
00352 }
00353 }
00354
00355 static void
00356 col_ref_cb (QofEntity * ref_ent, gpointer user_data)
00357 {
00358 QofEntityReference *ref;
00359 QofEntityCopyData *qecd;
00360 QofEntity *ent;
00361 const GUID *cm_guid;
00362 gchar cm_sa[GUID_ENCODING_LENGTH + 1];
00363 gchar *cm_string;
00364
00365 qecd = (QofEntityCopyData *) user_data;
00366 ent = qecd->from;
00367 ref = g_new0 (QofEntityReference, 1);
00368 ref->type = ent->e_type;
00369 ref->ref_guid = g_new (GUID, 1);
00370 ref->ent_guid = &ent->guid;
00371 ref->param = qof_class_get_parameter (ent->e_type,
00372 qecd->param->param_name);
00373 cm_guid = qof_entity_get_guid (ref_ent);
00374 guid_to_string_buff (cm_guid, cm_sa);
00375 cm_string = g_strdup (cm_sa);
00376 if (TRUE == string_to_guid (cm_string, ref->ref_guid))
00377 {
00378 g_free (cm_string);
00379 qof_session_update_reference_list (qecd->new_session, ref);
00380 }
00381 }
00382
00383 static void
00384 qof_entity_foreach_copy (gpointer data, gpointer user_data)
00385 {
00386 QofEntity *importEnt, *targetEnt ;
00387 QofEntityCopyData *context;
00388 QofEntityReference *reference;
00389 gboolean registered_type;
00390
00391 QofParam *cm_param;
00392 gchar *cm_string, *cm_char;
00393 const GUID *cm_guid;
00394 KvpFrame *cm_kvp;
00395 QofCollection *cm_col;
00396
00397 QofNumeric cm_numeric, (*numeric_getter) (QofEntity *, QofParam *);
00398 gdouble cm_double, (*double_getter) (QofEntity *, QofParam *);
00399 gboolean cm_boolean, (*boolean_getter) (QofEntity *, QofParam *);
00400 gint32 cm_i32, (*int32_getter) (QofEntity *, QofParam *);
00401 gint64 cm_i64, (*int64_getter) (QofEntity *, QofParam *);
00402
00403 void (*string_setter) (QofEntity *, const gchar *);
00404 void (*numeric_setter) (QofEntity *, QofNumeric);
00405 void (*guid_setter) (QofEntity *, const GUID *);
00406 void (*double_setter) (QofEntity *, gdouble);
00407 void (*boolean_setter) (QofEntity *, gboolean);
00408 void (*i32_setter) (QofEntity *, gint32);
00409 void (*i64_setter) (QofEntity *, gint64);
00410 void (*char_setter) (QofEntity *, gchar *);
00411 void (*kvp_frame_setter) (QofEntity *, KvpFrame *);
00412
00413 g_return_if_fail (user_data != NULL);
00414 context = (QofEntityCopyData *) user_data;
00415 importEnt = context->from;
00416 targetEnt = context->to;
00417 registered_type = FALSE;
00418 cm_param = (QofParam *) data;
00419 g_return_if_fail (cm_param != NULL);
00420 context->param = cm_param;
00421 if (safe_strcmp (cm_param->param_type, QOF_TYPE_STRING) == 0)
00422 {
00423 cm_string = (gchar *) cm_param->param_getfcn (importEnt, cm_param);
00424 if (cm_string)
00425 {
00426 string_setter =
00427 (void (*)(QofEntity *,
00428 const char *)) cm_param->param_setfcn;
00429 if (string_setter != NULL)
00430 {
00431 qof_util_param_edit ((QofInstance *) targetEnt, cm_param);
00432 string_setter (targetEnt, cm_string);
00433 qof_util_param_commit ((QofInstance *) targetEnt, cm_param);
00434 }
00435 }
00436 registered_type = TRUE;
00437 }
00438 if (safe_strcmp (cm_param->param_type, QOF_TYPE_TIME) == 0)
00439 {
00440 QofTime *qt;
00441 void (*time_setter) (QofEntity *, QofTime *);
00442
00443 qt = cm_param->param_getfcn (importEnt, cm_param);
00444 time_setter =
00445 (void (*)(QofEntity *, QofTime*))cm_param->param_setfcn;
00446 if (time_setter != NULL)
00447 {
00448 qof_util_param_edit ((QofInstance *) targetEnt, cm_param);
00449 time_setter (targetEnt, qt);
00450 qof_util_param_commit ((QofInstance *) targetEnt, cm_param);
00451 }
00452 registered_type = TRUE;
00453 }
00454 #ifndef QOF_DISABLE_DEPRECATED
00455 if (safe_strcmp (cm_param->param_type, QOF_TYPE_DATE) == 0)
00456 {
00457 Timespec cm_date, (*date_getter) (QofEntity *, QofParam *);
00458 void (*date_setter) (QofEntity *, Timespec);
00459
00460 cm_date.tv_nsec = 0;
00461 cm_date.tv_sec = 0;
00462 date_getter =
00463 (Timespec (*)(QofEntity *, QofParam *)) cm_param->param_getfcn;
00464 cm_date = date_getter (importEnt, cm_param);
00465 date_setter =
00466 (void (*)(QofEntity *, Timespec)) cm_param->param_setfcn;
00467 if (date_setter != NULL)
00468 {
00469 qof_util_param_edit ((QofInstance *) targetEnt, cm_param);
00470 date_setter (targetEnt, cm_date);
00471 qof_util_param_commit ((QofInstance *) targetEnt, cm_param);
00472 }
00473 registered_type = TRUE;
00474 }
00475 #endif
00476 if ((safe_strcmp (cm_param->param_type, QOF_TYPE_NUMERIC) == 0) ||
00477 (safe_strcmp (cm_param->param_type, QOF_TYPE_DEBCRED) == 0))
00478 {
00479 numeric_getter =
00480 (QofNumeric (*)(QofEntity *,
00481 QofParam *)) cm_param->param_getfcn;
00482 cm_numeric = numeric_getter (importEnt, cm_param);
00483 numeric_setter =
00484 (void (*)(QofEntity *, QofNumeric)) cm_param->param_setfcn;
00485 if (numeric_setter != NULL)
00486 {
00487 qof_util_param_edit ((QofInstance *) targetEnt, cm_param);
00488 numeric_setter (targetEnt, cm_numeric);
00489 qof_util_param_commit ((QofInstance *) targetEnt, cm_param);
00490 }
00491 registered_type = TRUE;
00492 }
00493 if (safe_strcmp (cm_param->param_type, QOF_TYPE_GUID) == 0)
00494 {
00495 cm_guid =
00496 (const GUID *) cm_param->param_getfcn (importEnt, cm_param);
00497 guid_setter =
00498 (void (*)(QofEntity *, const GUID *)) cm_param->param_setfcn;
00499 if (guid_setter != NULL)
00500 {
00501 qof_util_param_edit ((QofInstance *) targetEnt, cm_param);
00502 guid_setter (targetEnt, cm_guid);
00503 qof_util_param_commit ((QofInstance *) targetEnt, cm_param);
00504 }
00505 registered_type = TRUE;
00506 }
00507 if (safe_strcmp (cm_param->param_type, QOF_TYPE_INT32) == 0)
00508 {
00509 int32_getter =
00510 (gint32 (*)(QofEntity *, QofParam *)) cm_param->param_getfcn;
00511 cm_i32 = int32_getter (importEnt, cm_param);
00512 i32_setter =
00513 (void (*)(QofEntity *, gint32)) cm_param->param_setfcn;
00514 if (i32_setter != NULL)
00515 {
00516 qof_util_param_edit ((QofInstance *) targetEnt, cm_param);
00517 i32_setter (targetEnt, cm_i32);
00518 qof_util_param_commit ((QofInstance *) targetEnt, cm_param);
00519 }
00520 registered_type = TRUE;
00521 }
00522 if (safe_strcmp (cm_param->param_type, QOF_TYPE_INT64) == 0)
00523 {
00524 int64_getter =
00525 (gint64 (*)(QofEntity *, QofParam *)) cm_param->param_getfcn;
00526 cm_i64 = int64_getter (importEnt, cm_param);
00527 i64_setter =
00528 (void (*)(QofEntity *, gint64)) cm_param->param_setfcn;
00529 if (i64_setter != NULL)
00530 {
00531 qof_util_param_edit ((QofInstance *) targetEnt, cm_param);
00532 i64_setter (targetEnt, cm_i64);
00533 qof_util_param_commit ((QofInstance *) targetEnt, cm_param);
00534 }
00535 registered_type = TRUE;
00536 }
00537 if (safe_strcmp (cm_param->param_type, QOF_TYPE_DOUBLE) == 0)
00538 {
00539 double_getter =
00540 (gdouble (*)(QofEntity *, QofParam *)) cm_param->param_getfcn;
00541 cm_double = double_getter (importEnt, cm_param);
00542 double_setter =
00543 (void (*)(QofEntity *, gdouble)) cm_param->param_setfcn;
00544 if (double_setter != NULL)
00545 {
00546 qof_util_param_edit ((QofInstance *) targetEnt, cm_param);
00547 double_setter (targetEnt, cm_double);
00548 qof_util_param_commit ((QofInstance *) targetEnt, cm_param);
00549 }
00550 registered_type = TRUE;
00551 }
00552 if (safe_strcmp (cm_param->param_type, QOF_TYPE_BOOLEAN) == 0)
00553 {
00554 boolean_getter =
00555 (gboolean (*)(QofEntity *, QofParam *)) cm_param->param_getfcn;
00556 cm_boolean = boolean_getter (importEnt, cm_param);
00557 boolean_setter =
00558 (void (*)(QofEntity *, gboolean)) cm_param->param_setfcn;
00559 if (boolean_setter != NULL)
00560 {
00561 qof_util_param_edit ((QofInstance *) targetEnt, cm_param);
00562 boolean_setter (targetEnt, cm_boolean);
00563 qof_util_param_commit ((QofInstance *) targetEnt, cm_param);
00564 }
00565 registered_type = TRUE;
00566 }
00567 if (safe_strcmp (cm_param->param_type, QOF_TYPE_KVP) == 0)
00568 {
00569 cm_kvp = (KvpFrame *) cm_param->param_getfcn (importEnt, cm_param);
00570 kvp_frame_setter =
00571 (void (*)(QofEntity *, KvpFrame *)) cm_param->param_setfcn;
00572 if (kvp_frame_setter != NULL)
00573 {
00574 qof_util_param_edit ((QofInstance *) targetEnt, cm_param);
00575 kvp_frame_setter (targetEnt, cm_kvp);
00576 qof_util_param_commit ((QofInstance *) targetEnt, cm_param);
00577 }
00578 else
00579 {
00580 QofInstance *target_inst;
00581
00582 target_inst = (QofInstance *) targetEnt;
00583 kvp_frame_delete (target_inst->kvp_data);
00584 target_inst->kvp_data = kvp_frame_copy (cm_kvp);
00585 }
00586 registered_type = TRUE;
00587 }
00588 if (safe_strcmp (cm_param->param_type, QOF_TYPE_CHAR) == 0)
00589 {
00590 cm_char = (gchar *) cm_param->param_getfcn (importEnt, cm_param);
00591 char_setter =
00592 (void (*)(QofEntity *, char *)) cm_param->param_setfcn;
00593 if (char_setter != NULL)
00594 {
00595 qof_util_param_edit ((QofInstance *) targetEnt, cm_param);
00596 char_setter (targetEnt, cm_char);
00597 qof_util_param_commit ((QofInstance *) targetEnt, cm_param);
00598 }
00599 registered_type = TRUE;
00600 }
00601 if (safe_strcmp (cm_param->param_type, QOF_TYPE_COLLECT) == 0)
00602 {
00603 cm_col =
00604 (QofCollection *) cm_param->param_getfcn (importEnt, cm_param);
00605 if (cm_col)
00606 {
00607
00608 qof_collection_foreach (cm_col, col_ref_cb, context);
00609 }
00610 registered_type = TRUE;
00611 }
00612 if (registered_type == FALSE)
00613 {
00614
00615
00616
00617 reference = qof_entity_get_reference_from (importEnt, cm_param);
00618 if (reference)
00619 {
00620 qof_session_update_reference_list (context->new_session,
00621 reference);
00622 }
00623 }
00624 }
00625
00626 static gboolean
00627 qof_entity_guid_match (QofSession * new_session, QofEntity * original)
00628 {
00629 QofEntity *copy;
00630 const GUID *g;
00631 QofIdTypeConst type;
00632 QofBook *targetBook;
00633 QofCollection *coll;
00634
00635 copy = NULL;
00636 g_return_val_if_fail (original != NULL, FALSE);
00637 targetBook = qof_session_get_book (new_session);
00638 g_return_val_if_fail (targetBook != NULL, FALSE);
00639 g = qof_entity_get_guid (original);
00640 type = g_strdup (original->e_type);
00641 coll = qof_book_get_collection (targetBook, type);
00642 copy = qof_collection_lookup_entity (coll, g);
00643 if (copy)
00644 {
00645 return TRUE;
00646 }
00647 return FALSE;
00648 }
00649
00650 static void
00651 qof_entity_list_foreach (gpointer data, gpointer user_data)
00652 {
00653 QofEntityCopyData *qecd;
00654 QofEntity *original;
00655 QofInstance *inst;
00656 QofBook *book;
00657 const GUID *g;
00658
00659 g_return_if_fail (data != NULL);
00660 original = (QofEntity *) data;
00661 g_return_if_fail (user_data != NULL);
00662 qecd = (QofEntityCopyData *) user_data;
00663 if (qof_entity_guid_match (qecd->new_session, original))
00664 {
00665 return;
00666 }
00667 qecd->from = original;
00668 if (!qof_object_compliance (original->e_type, FALSE))
00669 {
00670 qecd->error = TRUE;
00671 return;
00672 }
00673 book = qof_session_get_book (qecd->new_session);
00674 inst =
00675 (QofInstance *) qof_object_new_instance (original->e_type, book);
00676 if (!inst)
00677 {
00678 PERR (" failed to create new entity type=%s.", original->e_type);
00679 qecd->error = TRUE;
00680 return;
00681 }
00682 qecd->to = &inst->entity;
00683 g = qof_entity_get_guid (original);
00684 qof_entity_set_guid (qecd->to, g);
00685 if (qecd->param_list != NULL)
00686 {
00687 g_slist_free (qecd->param_list);
00688 qecd->param_list = NULL;
00689 }
00690 qof_class_param_foreach (original->e_type, qof_entity_param_cb, qecd);
00691 g_slist_foreach (qecd->param_list, qof_entity_foreach_copy, qecd);
00692 }
00693
00694 static void
00695 qof_entity_coll_foreach (QofEntity * original, gpointer user_data)
00696 {
00697 QofEntityCopyData *qecd;
00698 const GUID *g;
00699 QofBook *targetBook;
00700 QofCollection *coll;
00701 QofEntity *copy;
00702
00703 g_return_if_fail (user_data != NULL);
00704 copy = NULL;
00705 qecd = (QofEntityCopyData *) user_data;
00706 targetBook = qof_session_get_book (qecd->new_session);
00707 g = qof_entity_get_guid (original);
00708 coll = qof_book_get_collection (targetBook, original->e_type);
00709 copy = qof_collection_lookup_entity (coll, g);
00710 if (copy)
00711 {
00712 qecd->error = TRUE;
00713 }
00714 }
00715
00716 static void
00717 qof_entity_coll_copy (QofEntity * original, gpointer user_data)
00718 {
00719 QofEntityCopyData *qecd;
00720 QofBook *book;
00721 QofInstance *inst;
00722 const GUID *g;
00723
00724 g_return_if_fail (user_data != NULL);
00725 qecd = (QofEntityCopyData *) user_data;
00726 book = qof_session_get_book (qecd->new_session);
00727 if (!qof_object_compliance (original->e_type, TRUE))
00728 {
00729 return;
00730 }
00731 inst =
00732 (QofInstance *) qof_object_new_instance (original->e_type, book);
00733 qecd->to = &inst->entity;
00734 qecd->from = original;
00735 g = qof_entity_get_guid (original);
00736 qof_entity_set_guid (qecd->to, g);
00737 g_slist_foreach (qecd->param_list, qof_entity_foreach_copy, qecd);
00738 }
00739
00740 gboolean
00741 qof_entity_copy_to_session (QofSession * new_session, QofEntity * original)
00742 {
00743 QofEntityCopyData qecd;
00744 QofInstance *inst;
00745 QofBook *book;
00746
00747 if (!new_session || !original)
00748 return FALSE;
00749 if (qof_entity_guid_match (new_session, original))
00750 return FALSE;
00751 if (!qof_object_compliance (original->e_type, TRUE))
00752 return FALSE;
00753 qof_event_suspend ();
00754 qecd.param_list = NULL;
00755 book = qof_session_get_book (new_session);
00756 qecd.new_session = new_session;
00757 qof_book_set_partial (book);
00758 inst =
00759 (QofInstance *) qof_object_new_instance (original->e_type, book);
00760 qecd.to = &inst->entity;
00761 qecd.from = original;
00762 qof_entity_set_guid (qecd.to, qof_entity_get_guid (original));
00763 qof_class_param_foreach (original->e_type, qof_entity_param_cb, &qecd);
00764 if (g_slist_length (qecd.param_list) == 0)
00765 return FALSE;
00766 g_slist_foreach (qecd.param_list, qof_entity_foreach_copy, &qecd);
00767 g_slist_free (qecd.param_list);
00768 qof_event_resume ();
00769 return TRUE;
00770 }
00771
00772 gboolean
00773 qof_entity_copy_list (QofSession * new_session, GList * entity_list)
00774 {
00775 QofEntityCopyData *qecd;
00776
00777 if (!new_session || !entity_list)
00778 {
00779 return FALSE;
00780 }
00781 ENTER (" list=%d", g_list_length (entity_list));
00782 qecd = g_new0 (QofEntityCopyData, 1);
00783 qof_event_suspend ();
00784 qecd->param_list = NULL;
00785 qecd->new_session = new_session;
00786 qof_book_set_partial (qof_session_get_book (new_session));
00787 g_list_foreach (entity_list, qof_entity_list_foreach, qecd);
00788 qof_event_resume ();
00789 if (qecd->error)
00790 {
00791 PWARN (" some/all entities in the list could not be copied.");
00792 }
00793 g_free (qecd);
00794 LEAVE (" ");
00795 return TRUE;
00796 }
00797
00798 gboolean
00799 qof_entity_copy_coll (QofSession * new_session,
00800 QofCollection * entity_coll)
00801 {
00802 QofEntityCopyData qecd;
00803
00804 g_return_val_if_fail (new_session, FALSE);
00805 if (!entity_coll)
00806 {
00807 return FALSE;
00808 }
00809 qof_event_suspend ();
00810 qecd.param_list = NULL;
00811 qecd.new_session = new_session;
00812 qof_book_set_partial (qof_session_get_book (qecd.new_session));
00813 qof_collection_foreach (entity_coll, qof_entity_coll_foreach, &qecd);
00814 qof_class_param_foreach (qof_collection_get_type (entity_coll),
00815 qof_entity_param_cb, &qecd);
00816 qof_collection_foreach (entity_coll, qof_entity_coll_copy, &qecd);
00817 if (qecd.param_list != NULL)
00818 {
00819 g_slist_free (qecd.param_list);
00820 }
00821 qof_event_resume ();
00822 return TRUE;
00823 }
00824
00825 struct recurse_s
00826 {
00827 QofSession *session;
00828 gboolean success;
00829 GList *ref_list;
00830 GList *ent_list;
00831 };
00832
00833 static void
00834 recurse_collection_cb (QofEntity * ent, gpointer user_data)
00835 {
00836 struct recurse_s *store;
00837
00838 if (user_data == NULL)
00839 {
00840 return;
00841 }
00842 store = (struct recurse_s *) user_data;
00843 if (!ent || !store)
00844 {
00845 return;
00846 }
00847 store->success = qof_entity_copy_to_session (store->session, ent);
00848 if (store->success)
00849 {
00850 store->ent_list = g_list_append (store->ent_list, ent);
00851 }
00852 }
00853
00854 static void
00855 recurse_ent_cb (QofEntity * ent, gpointer user_data)
00856 {
00857 GList *ref_list, *i, *j, *ent_list, *child_list;
00858 QofParam *ref_param;
00859 QofEntity *ref_ent, *child_ent;
00860 QofSession *session;
00861 struct recurse_s *store;
00862 gboolean success;
00863
00864 if (user_data == NULL)
00865 {
00866 return;
00867 }
00868 store = (struct recurse_s *) user_data;
00869 session = store->session;
00870 success = store->success;
00871 ref_list = NULL;
00872 child_ent = NULL;
00873 ref_list = g_list_copy (store->ref_list);
00874 if ((!session) || (!ent))
00875 {
00876 return;
00877 }
00878 ent_list = NULL;
00879 child_list = NULL;
00880 i = NULL;
00881 j = NULL;
00882 for (i = ref_list; i != NULL; i = i->next)
00883 {
00884 if (i->data == NULL)
00885 {
00886 continue;
00887 }
00888 ref_param = (QofParam *) i->data;
00889 if (ref_param->param_name == NULL)
00890 {
00891 continue;
00892 }
00893 if (0 == safe_strcmp (ref_param->param_type, QOF_TYPE_COLLECT))
00894 {
00895 QofCollection *col;
00896
00897 col = ref_param->param_getfcn (ent, ref_param);
00898 if (col)
00899 {
00900 qof_collection_foreach (col, recurse_collection_cb, store);
00901 }
00902 continue;
00903 }
00904 ref_ent = (QofEntity *) ref_param->param_getfcn (ent, ref_param);
00905 if ((ref_ent) && (ref_ent->e_type))
00906 {
00907 store->success = qof_entity_copy_to_session (session, ref_ent);
00908 if (store->success)
00909 {
00910 ent_list = g_list_append (ent_list, ref_ent);
00911 }
00912 }
00913 }
00914 for (i = ent_list; i != NULL; i = i->next)
00915 {
00916 if (i->data == NULL)
00917 {
00918 continue;
00919 }
00920 child_ent = (QofEntity *) i->data;
00921 if (child_ent == NULL)
00922 {
00923 continue;
00924 }
00925 ref_list = qof_class_get_referenceList (child_ent->e_type);
00926 for (j = ref_list; j != NULL; j = j->next)
00927 {
00928 if (j->data == NULL)
00929 {
00930 continue;
00931 }
00932 ref_param = (QofParam *) j->data;
00933 ref_ent = ref_param->param_getfcn (child_ent, ref_param);
00934 if (ref_ent != NULL)
00935 {
00936 success = qof_entity_copy_to_session (session, ref_ent);
00937 if (success)
00938 {
00939 child_list = g_list_append (child_list, ref_ent);
00940 }
00941 }
00942 }
00943 }
00944 for (i = child_list; i != NULL; i = i->next)
00945 {
00946 if (i->data == NULL)
00947 {
00948 continue;
00949 }
00950 ref_ent = (QofEntity *) i->data;
00951 if (ref_ent == NULL)
00952 {
00953 continue;
00954 }
00955 ref_list = qof_class_get_referenceList (ref_ent->e_type);
00956 for (j = ref_list; j != NULL; j = j->next)
00957 {
00958 if (j->data == NULL)
00959 {
00960 continue;
00961 }
00962 ref_param = (QofParam *) j->data;
00963 child_ent = ref_param->param_getfcn (ref_ent, ref_param);
00964 if (child_ent != NULL)
00965 {
00966 qof_entity_copy_to_session (session, child_ent);
00967 }
00968 }
00969 }
00970 }
00971
00972 gboolean
00973 qof_entity_copy_coll_r (QofSession * new_session, QofCollection * coll)
00974 {
00975 struct recurse_s store;
00976 gboolean success;
00977
00978 if ((!new_session) || (!coll))
00979 {
00980 return FALSE;
00981 }
00982 store.session = new_session;
00983 success = TRUE;
00984 store.success = success;
00985 store.ent_list = NULL;
00986 store.ref_list =
00987 qof_class_get_referenceList (qof_collection_get_type (coll));
00988 success = qof_entity_copy_coll (new_session, coll);
00989 if (success)
00990 {
00991 qof_collection_foreach (coll, recurse_ent_cb, &store);
00992 }
00993 return success;
00994 }
00995
00996 gboolean
00997 qof_entity_copy_one_r (QofSession * new_session, QofEntity * ent)
00998 {
00999 struct recurse_s store;
01000 QofCollection *coll;
01001 gboolean success;
01002
01003 if ((!new_session) || (!ent))
01004 {
01005 return FALSE;
01006 }
01007 store.session = new_session;
01008 success = TRUE;
01009 store.success = success;
01010 store.ref_list = qof_class_get_referenceList (ent->e_type);
01011 success = qof_entity_copy_to_session (new_session, ent);
01012 if (success == TRUE)
01013 {
01014 coll =
01015 qof_book_get_collection (qof_session_get_book (new_session),
01016 ent->e_type);
01017 if (coll)
01018 {
01019 qof_collection_foreach (coll, recurse_ent_cb, &store);
01020 }
01021 }
01022 return success;
01023 }
01024
01025
01026
01027
01031 struct backend_providers
01032 {
01033 const gchar *libdir;
01034 const gchar *filename;
01035 const gchar *init_fcn;
01036 };
01037
01038
01039
01040
01041
01042 struct backend_providers backend_list[] = {
01043 {QOF_LIB_DIR, QSF_BACKEND_LIB, QSF_MODULE_INIT},
01044 {QOF_LIB_DIR, "libqof-backend-sqlite", "qof_sqlite_provider_init"},
01045 #ifdef HAVE_DWI
01046 {QOF_LIB_DIR, "libqof_backend_dwi", "dwiend_provider_init"},
01047 #endif
01048 {NULL, NULL, NULL}
01049 };
01050
01051 static void
01052 qof_session_load_backend (QofSession * session, gchar *access_method)
01053 {
01054 GSList *p;
01055 GList *node;
01056 QofBackendProvider *prov;
01057 QofBook *book;
01058 gchar *msg;
01059 gint num;
01060 gboolean prov_type;
01061 gboolean (*type_check) (const char *);
01062
01063 ENTER (" list=%d", g_slist_length (provider_list));
01064 prov_type = FALSE;
01065 if (NULL == provider_list)
01066 {
01067 for (num = 0; backend_list[num].filename != NULL; num++)
01068 {
01069 if (!qof_load_backend_library (backend_list[num].libdir,
01070 backend_list[num].filename,
01071 backend_list[num].init_fcn))
01072 {
01073 PWARN (" failed to load %s from %s using %s",
01074 backend_list[num].filename, backend_list[num].libdir,
01075 backend_list[num].init_fcn);
01076 }
01077 }
01078 }
01079 p = g_slist_copy (provider_list);
01080 while (p != NULL)
01081 {
01082 prov = p->data;
01083
01084 if (0 == strcasecmp (access_method, prov->access_method))
01085 {
01086
01087
01088 type_check =
01089 (gboolean (*)(const gchar *)) prov->check_data_type;
01090 prov_type = (type_check) (session->book_id);
01091 if (!prov_type)
01092 {
01093 PINFO (" %s not usable", prov->provider_name);
01094 p = p->next;
01095 continue;
01096 }
01097 PINFO (" selected %s", prov->provider_name);
01098 if (NULL == prov->backend_new)
01099 {
01100 p = p->next;
01101 continue;
01102 }
01103
01104 session->backend = (*(prov->backend_new)) ();
01105 session->backend->provider = prov;
01106
01107 for (node = session->books; node; node = node->next)
01108 {
01109 book = node->data;
01110 qof_book_set_backend (book, session->backend);
01111 }
01112 LEAVE (" ");
01113 return;
01114 }
01115 p = p->next;
01116 }
01117 msg =
01118 g_strdup_printf ("failed to load '%s' using access_method",
01119 access_method);
01120 qof_session_push_error (session, ERR_BACKEND_NO_HANDLER, msg);
01121 LEAVE (" ");
01122 }
01123
01124
01125
01126 static void
01127 qof_session_destroy_backend (QofSession * session)
01128 {
01129 g_return_if_fail (session);
01130
01131 if (session->backend)
01132 {
01133
01134 char *msg = qof_backend_get_message (session->backend);
01135 g_free (msg);
01136
01137
01138 if (session->backend->destroy_backend)
01139 {
01140 session->backend->destroy_backend (session->backend);
01141 }
01142 else
01143 {
01144 g_free (session->backend);
01145 }
01146 }
01147
01148 session->backend = NULL;
01149 }
01150
01151 void
01152 qof_session_begin (QofSession * session, const gchar *book_id,
01153 gboolean ignore_lock, gboolean create_if_nonexistent)
01154 {
01155 gchar *p, *access_method, *msg;
01156 gint err;
01157
01158 if (!session)
01159 return;
01160
01161 ENTER (" sess=%p ignore_lock=%d, book-id=%s",
01162 session, ignore_lock, book_id ? book_id : "(null)");
01163
01164
01165 qof_session_clear_error (session);
01166
01167
01168 if (session->book_id)
01169 {
01170 qof_session_push_error (session, ERR_BACKEND_LOCKED, NULL);
01171 LEAVE (" push error book is already open ");
01172 return;
01173 }
01174
01175
01176 if (!book_id)
01177 {
01178 qof_session_push_error (session, ERR_BACKEND_BAD_URL, NULL);
01179 LEAVE (" push error missing book_id");
01180 return;
01181 }
01182
01183
01184 session->book_id = g_strdup (book_id);
01185
01186
01187 qof_session_destroy_backend (session);
01188
01189
01190
01191
01192
01193 p = strchr (book_id, ':');
01194 if (p)
01195 {
01196 access_method = g_strdup (book_id);
01197 p = strchr (access_method, ':');
01198 *p = 0;
01199 qof_session_load_backend (session, access_method);
01200 g_free (access_method);
01201 }
01202 else
01203 {
01204
01205 qof_session_load_backend (session, "file");
01206 }
01207
01208
01209 if (NULL == session->backend)
01210 {
01211 qof_session_push_error (session, ERR_BACKEND_BAD_URL, NULL);
01212 LEAVE (" BAD: no backend: sess=%p book-id=%s",
01213 session, book_id ? book_id : "(null)");
01214 return;
01215 }
01216
01217
01218 if (session->backend->session_begin)
01219 {
01220
01221 (session->backend->session_begin) (session->backend, session,
01222 session->book_id, ignore_lock, create_if_nonexistent);
01223 PINFO (" Done running session_begin on backend");
01224 err = qof_backend_get_error (session->backend);
01225 msg = qof_backend_get_message (session->backend);
01226 if (err != ERR_BACKEND_NO_ERR)
01227 {
01228 g_free (session->book_id);
01229 session->book_id = NULL;
01230 qof_session_push_error (session, err, msg);
01231 LEAVE (" backend error %d %s", err, msg);
01232 return;
01233 }
01234 if (msg != NULL)
01235 {
01236 PWARN (" %s", msg);
01237 g_free (msg);
01238 }
01239 }
01240
01241 LEAVE (" sess=%p book-id=%s", session, book_id ? book_id : "(null)");
01242 }
01243
01244
01245
01246 void
01247 qof_session_load (QofSession * session, QofPercentageFunc percentage_func)
01248 {
01249 QofBook *newbook, *ob;
01250 QofBookList *oldbooks, *node;
01251 QofBackend *be;
01252 QofBackendError err;
01253
01254 if (!session)
01255 return;
01256 if (!session->book_id)
01257 return;
01258
01259 ENTER (" sess=%p book_id=%s", session, session->book_id
01260 ? session->book_id : "(null)");
01261
01262
01263
01264
01265 oldbooks = session->books;
01266
01267
01268
01269
01270
01271 newbook = qof_book_new ();
01272 session->books = g_list_append (NULL, newbook);
01273 PINFO (" new book=%p", newbook);
01274
01275 qof_session_clear_error (session);
01276
01277
01278
01279
01280
01281
01282
01283
01284 be = session->backend;
01285 qof_book_set_backend (newbook, be);
01286
01287
01288
01289
01290
01291 if (be)
01292 {
01293 be->percentage = percentage_func;
01294
01295 if (be->load)
01296 {
01297 be->load (be, newbook);
01298 qof_session_push_error (session, qof_backend_get_error (be),
01299 NULL);
01300 }
01301 }
01302
01303
01304
01305
01306
01307
01308 err = qof_session_get_error (session);
01309 if ((err != ERR_BACKEND_NO_ERR) &&
01310 (err != ERR_FILEIO_FILE_TOO_OLD) &&
01311 (err != ERR_FILEIO_NO_ENCODING) && (err != ERR_SQL_DB_TOO_OLD))
01312 {
01313
01314 qof_book_set_backend (newbook, NULL);
01315 qof_book_destroy (newbook);
01316 g_list_free (session->books);
01317 session->books = oldbooks;
01318 LEAVE (" error from backend %d", qof_session_get_error (session));
01319 return;
01320 }
01321
01322 for (node = oldbooks; node; node = node->next)
01323 {
01324 ob = node->data;
01325 qof_book_set_backend (ob, NULL);
01326 qof_book_destroy (ob);
01327 }
01328 g_list_free (oldbooks);
01329
01330 LEAVE (" sess = %p, book_id=%s", session, session->book_id
01331 ? session->book_id : "(null)");
01332 }
01333
01334
01335
01336 gboolean
01337 qof_session_save_may_clobber_data (QofSession * session)
01338 {
01339 if (!session)
01340 return FALSE;
01341 if (!session->backend)
01342 return FALSE;
01343 if (!session->backend->save_may_clobber_data)
01344 return FALSE;
01345
01346 return (*(session->backend->save_may_clobber_data)) (session->backend);
01347 }
01348
01349 static gboolean
01350 save_error_handler (QofBackend * be, QofSession * session)
01351 {
01352 int err;
01353 err = qof_backend_get_error (be);
01354
01355 if (ERR_BACKEND_NO_ERR != err)
01356 {
01357 qof_session_push_error (session, err, NULL);
01358 return TRUE;
01359 }
01360 return FALSE;
01361 }
01362
01363 void
01364 qof_session_save (QofSession * session,
01365 QofPercentageFunc percentage_func)
01366 {
01367 GList *node;
01368 QofBackend *be;
01369 gboolean partial, change_backend;
01370 QofBackendProvider *prov;
01371 GSList *p;
01372 QofBook *book, *abook;
01373 gint err;
01374 gint num;
01375 gchar *msg, *book_id;
01376
01377 if (!session)
01378 return;
01379 ENTER (" sess=%p book_id=%s",
01380 session, session->book_id ? session->book_id : "(null)");
01381
01382 book = qof_session_get_book (session);
01383 partial =
01384 (gboolean)
01385 GPOINTER_TO_INT (qof_book_get_data (book, PARTIAL_QOFBOOK));
01386 change_backend = FALSE;
01387 msg = g_strdup_printf (" ");
01388 book_id = g_strdup (session->book_id);
01389 if (partial == TRUE)
01390 {
01391 if (session->backend && session->backend->provider)
01392 {
01393 prov = session->backend->provider;
01394 if (TRUE == prov->partial_book_supported)
01395 {
01396
01397 change_backend = FALSE;
01398 }
01399 else
01400 {
01401 change_backend = TRUE;
01402 }
01403 }
01404
01405 else
01406 {
01407 change_backend = TRUE;
01408 }
01409 }
01410 if (change_backend == TRUE)
01411 {
01412 qof_session_destroy_backend (session);
01413 if (NULL == provider_list)
01414 {
01415 for (num = 0; backend_list[num].filename != NULL; num++)
01416 {
01417 qof_load_backend_library (backend_list[num].libdir,
01418 backend_list[num].filename,
01419 backend_list[num].init_fcn);
01420 }
01421 }
01422 p = g_slist_copy (provider_list);
01423 while (p != NULL)
01424 {
01425 prov = p->data;
01426 if (TRUE == prov->partial_book_supported)
01427 {
01429
01430
01431
01432 if (NULL == prov->backend_new)
01433 continue;
01434
01435 session->backend = (*(prov->backend_new)) ();
01436 session->backend->provider = prov;
01437 if (session->backend->session_begin)
01438 {
01439
01440
01441
01442 g_free (session->book_id);
01443 session->book_id = NULL;
01444 (session->backend->session_begin) (session->backend,
01445 session, book_id, TRUE, TRUE);
01446 PINFO
01447 (" Done running session_begin on changed backend");
01448 err = qof_backend_get_error (session->backend);
01449 msg = qof_backend_get_message (session->backend);
01450 if (err != ERR_BACKEND_NO_ERR)
01451 {
01452 g_free (session->book_id);
01453 session->book_id = NULL;
01454 qof_session_push_error (session, err, msg);
01455 LEAVE (" changed backend error %d", err);
01456 return;
01457 }
01458 if (msg != NULL)
01459 {
01460 PWARN ("%s", msg);
01461 g_free (msg);
01462 }
01463 }
01464
01465 for (node = session->books; node; node = node->next)
01466 {
01467 book = node->data;
01468 qof_book_set_backend (book, session->backend);
01469 }
01470 p = NULL;
01471 }
01472 if (p)
01473 {
01474 p = p->next;
01475 }
01476 }
01477 if (!session->backend)
01478 {
01479 msg = g_strdup_printf (" failed to load backend");
01480 qof_session_push_error (session, ERR_BACKEND_NO_HANDLER, msg);
01481 return;
01482 }
01483 }
01484
01485
01486
01487
01488
01489
01490
01491
01492
01493 be = session->backend;
01494 if (be)
01495 {
01496 for (node = session->books; node; node = node->next)
01497 {
01498 abook = node->data;
01499
01500 qof_book_set_backend (abook, be);
01501 be->percentage = percentage_func;
01502 if (be->sync)
01503 {
01504 (be->sync) (be, abook);
01505 if (save_error_handler (be, session))
01506 return;
01507 }
01508 }
01509
01510
01511
01512 qof_session_clear_error (session);
01513 LEAVE (" Success");
01514 return;
01515 }
01516 else
01517 {
01518 msg = g_strdup_printf (" failed to load backend");
01519 qof_session_push_error (session, ERR_BACKEND_NO_HANDLER, msg);
01520 }
01521 LEAVE (" error -- No backend!");
01522 }
01523
01524
01525
01526 void
01527 qof_session_end (QofSession * session)
01528 {
01529 if (!session)
01530 return;
01531
01532 ENTER (" sess=%p book_id=%s", session, session->book_id
01533 ? session->book_id : "(null)");
01534
01535
01536 if (session->backend && session->backend->session_end)
01537 {
01538 (session->backend->session_end) (session->backend);
01539 }
01540
01541 qof_session_clear_error (session);
01542
01543 g_free (session->book_id);
01544 session->book_id = NULL;
01545
01546 LEAVE (" sess=%p book_id=%s", session, session->book_id
01547 ? session->book_id : "(null)");
01548 }
01549
01550 void
01551 qof_session_destroy (QofSession * session)
01552 {
01553 GList *node;
01554 if (!session)
01555 return;
01556
01557 ENTER (" sess=%p book_id=%s", session, session->book_id
01558 ? session->book_id : "(null)");
01559
01560 qof_session_end (session);
01561
01562
01563 qof_session_destroy_backend (session);
01564
01565 for (node = session->books; node; node = node->next)
01566 {
01567 QofBook *book = node->data;
01568 qof_book_set_backend (book, NULL);
01569 qof_book_destroy (book);
01570 }
01571
01572 session->books = NULL;
01573 #ifndef QOF_DISABLE_DEPRECATED
01574 if (session == qof_session_get_current_session())
01575 qof_session_clear_current_session();
01576 #endif
01577 g_free (session);
01578
01579 LEAVE (" sess=%p", session);
01580 }
01581
01582
01583
01584 void
01585 qof_session_swap_data (QofSession * session_1, QofSession * session_2)
01586 {
01587 GList *books_1, *books_2, *node;
01588
01589 if (session_1 == session_2)
01590 return;
01591 if (!session_1 || !session_2)
01592 return;
01593
01594 ENTER (" sess1=%p sess2=%p", session_1, session_2);
01595
01596 books_1 = session_1->books;
01597 books_2 = session_2->books;
01598
01599 session_1->books = books_2;
01600 session_2->books = books_1;
01601
01602 for (node = books_1; node; node = node->next)
01603 {
01604 QofBook *book_1 = node->data;
01605 qof_book_set_backend (book_1, session_2->backend);
01606 }
01607 for (node = books_2; node; node = node->next)
01608 {
01609 QofBook *book_2 = node->data;
01610 qof_book_set_backend (book_2, session_1->backend);
01611 }
01612
01613 LEAVE (" ");
01614 }
01615
01616
01617
01618 gboolean
01619 qof_session_events_pending (QofSession * session)
01620 {
01621 if (!session)
01622 return FALSE;
01623 if (!session->backend)
01624 return FALSE;
01625 if (!session->backend->events_pending)
01626 return FALSE;
01627
01628 return session->backend->events_pending (session->backend);
01629 }
01630
01631 gboolean
01632 qof_session_process_events (QofSession * session)
01633 {
01634 if (!session)
01635 return FALSE;
01636 if (!session->backend)
01637 return FALSE;
01638 if (!session->backend->process_events)
01639 return FALSE;
01640
01641 return session->backend->process_events (session->backend);
01642 }
01643
01644