Line data Source code
1 : /********************************************************************\
2 : * qofsession.cpp -- session access (connection to backend) *
3 : * *
4 : * This program is free software; you can redistribute it and/or *
5 : * modify it under the terms of the GNU General Public License as *
6 : * published by the Free Software Foundation; either version 2 of *
7 : * the License, or (at your option) any later version. *
8 : * *
9 : * This program is distributed in the hope that it will be useful, *
10 : * but WITHOUT ANY WARRANTY; without even the implied warranty of *
11 : * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the *
12 : * GNU General Public License for more details. *
13 : * *
14 : * You should have received a copy of the GNU General Public License*
15 : * along with this program; if not, contact: *
16 : * *
17 : * Free Software Foundation Voice: +1-617-542-5942 *
18 : * 51 Franklin Street, Fifth Floor Fax: +1-617-542-2652 *
19 : * Boston, MA 02110-1301, USA gnu@gnu.org *
20 : \********************************************************************/
21 :
22 : /**
23 : * @file qofsession.cpp
24 : * @brief Encapsulate a connection to a storage backend.
25 : *
26 : * HISTORY:
27 : * Created by Linas Vepstas December 1998
28 :
29 : @author Copyright (c) 1998-2004 Linas Vepstas <linas@linas.org>
30 : @author Copyright (c) 2000 Dave Peticolas
31 : @author Copyright (c) 2005 Neil Williams <linux@codehelp.co.uk>
32 : @author Copyright (c) 2016 Aaron Laws
33 : */
34 : #include <glib.h>
35 :
36 : #include <config.h>
37 :
38 : #include <stdlib.h>
39 : #include <sys/types.h>
40 : #include <sys/stat.h>
41 : #ifdef HAVE_UNISTD_H
42 : # include <unistd.h>
43 : #else
44 : # ifdef __GNUC__
45 : # warning "<unistd.h> required."
46 : # endif
47 : #endif
48 :
49 : #include "qof.h"
50 : #include "qofobject-p.h"
51 :
52 : static QofLogModule log_module = QOF_MOD_SESSION;
53 :
54 : #include "qofbook-p.hpp"
55 : #include "qof-backend.hpp"
56 : #include "qofsession.hpp"
57 : #include "gnc-backend-prov.hpp"
58 : #include "gnc-uri-utils.h"
59 :
60 : #include <vector>
61 : #include <boost/algorithm/string.hpp>
62 : #include <vector>
63 : #include <algorithm>
64 : #include <string>
65 : #include <sstream>
66 :
67 : using ProviderVec = std::vector<QofBackendProvider_ptr>;
68 : static ProviderVec s_providers;
69 : static const std::string empty_string{};
70 : /*
71 : * These getters are used in tests to reach static vars from outside
72 : * They should be removed when no longer needed
73 : */
74 :
75 : ProviderVec& get_providers (void );
76 : bool get_providers_initialized (void );
77 :
78 : ProviderVec&
79 0 : get_providers (void)
80 : {
81 0 : return s_providers;
82 : }
83 :
84 : bool
85 0 : get_providers_initialized (void)
86 : {
87 0 : return !s_providers.empty();
88 : }
89 :
90 : void
91 62 : qof_backend_register_provider (QofBackendProvider_ptr&& prov)
92 : {
93 62 : s_providers.emplace_back(std::move(prov));
94 62 : }
95 :
96 : void
97 0 : qof_backend_unregister_all_providers ()
98 : {
99 0 : s_providers.clear ();
100 0 : }
101 :
102 : /* Called from C so we have to keep the GList for now. */
103 : GList*
104 6 : qof_backend_get_registered_access_method_list(void)
105 : {
106 6 : GList* list = NULL;
107 6 : std::for_each(s_providers.begin(), s_providers.end(),
108 0 : [&list](QofBackendProvider_ptr& provider) {
109 0 : gpointer method = reinterpret_cast<gpointer>(const_cast<char*>(provider->access_method));
110 0 : list = g_list_prepend(list, method);
111 0 : });
112 6 : return list;
113 : }
114 :
115 : /* QofSessionImpl */
116 : /* ====================================================================== */
117 : /* Constructor/Destructor ----------------------------------*/
118 :
119 290 : QofSessionImpl::QofSessionImpl (QofBook* book) noexcept
120 290 : : m_backend {},
121 290 : m_book {book},
122 290 : m_uri {},
123 290 : m_saving {false},
124 290 : m_last_err {},
125 290 : m_error_message {}
126 : {
127 290 : }
128 :
129 165 : QofSessionImpl::~QofSessionImpl () noexcept
130 : {
131 165 : ENTER ("sess=%p uri=%s", this, m_uri.c_str ());
132 165 : end ();
133 165 : destroy_backend ();
134 165 : qof_book_set_backend (m_book, nullptr);
135 165 : qof_book_destroy (m_book);
136 165 : m_book = nullptr;
137 165 : LEAVE ("sess=%p", this);
138 165 : }
139 :
140 : void
141 165 : qof_session_destroy (QofSession * session)
142 : {
143 165 : delete session;
144 165 : }
145 :
146 : QofSession *
147 290 : qof_session_new (QofBook* book)
148 : {
149 290 : return new QofSessionImpl(book);
150 : }
151 :
152 : void
153 212 : QofSessionImpl::destroy_backend () noexcept
154 : {
155 212 : if (m_backend)
156 : {
157 30 : clear_error ();
158 30 : delete m_backend;
159 30 : m_backend = nullptr;
160 30 : qof_book_set_backend (m_book, nullptr);
161 : }
162 212 : }
163 :
164 : /* ====================================================================== */
165 :
166 : void
167 47 : QofSessionImpl::load_backend (std::string access_method) noexcept
168 : {
169 47 : std::ostringstream s;
170 47 : s << " list=" << s_providers.size();
171 47 : ENTER ("%s", s.str().c_str());
172 136 : for (auto const & prov : s_providers)
173 : {
174 88 : if (!boost::iequals (access_method, prov->access_method))
175 : {
176 39 : PINFO ("The provider providers access_method, %s, but we're loading for access_method, %s. Skipping.",
177 : prov->access_method, access_method.c_str ());
178 39 : continue;
179 : }
180 49 : PINFO (" Selected provider %s", prov->provider_name);
181 : // Only do a type check when trying to open an existing file
182 : // When saving over an existing file the contents of the original file don't matter
183 49 : if (!m_creating && !prov->type_check (m_uri.c_str ()))
184 : {
185 3 : PINFO("Provider, %s, reported not being usable for book, %s.",
186 : prov->provider_name, m_uri.c_str ());
187 3 : continue;
188 : }
189 46 : m_backend = prov->create_backend();
190 46 : LEAVE (" ");
191 46 : return;
192 : }
193 1 : std::string msg {"failed to get_backend using access method \"" + access_method + "\""};
194 1 : push_error (ERR_BACKEND_NO_HANDLER, msg);
195 1 : LEAVE (" ");
196 47 : }
197 :
198 : void
199 30 : QofSessionImpl::load (QofPercentageFunc percentage_func) noexcept
200 : {
201 : /* We must have an empty book to load into or bad things will happen. */
202 30 : g_return_if_fail(m_book && qof_book_empty(m_book));
203 :
204 29 : if (!m_uri.size ()) return;
205 29 : ENTER ("sess=%p uri=%s", this, m_uri.c_str ());
206 :
207 : /* At this point, we should are supposed to have a valid book
208 : * id and a lock on the file. */
209 29 : clear_error ();
210 :
211 : /* This code should be sufficient to initialize *any* backend,
212 : * whether http, postgres, or anything else that might come along.
213 : * Basically, the idea is that by now, a backend has already been
214 : * created & set up. At this point, we only need to get the
215 : * top-level account group out of the backend, and that is a
216 : * generic, backend-independent operation.
217 : */
218 29 : qof_book_set_backend (m_book, m_backend);
219 :
220 : /* Starting the session should result in a bunch of accounts
221 : * and currencies being downloaded, but probably no transactions;
222 : * The GUI will need to do a query for that.
223 : */
224 29 : if (m_backend)
225 : {
226 29 : m_backend->set_percentage(percentage_func);
227 29 : m_backend->load (m_book, LOAD_TYPE_INITIAL_LOAD);
228 29 : push_error (m_backend->get_error(), {});
229 : }
230 :
231 29 : auto err = get_error ();
232 29 : if ((err != ERR_BACKEND_NO_ERR) &&
233 2 : (err != ERR_FILEIO_FILE_TOO_OLD) &&
234 2 : (err != ERR_FILEIO_NO_ENCODING) &&
235 2 : (err != ERR_FILEIO_FILE_UPGRADE) &&
236 1 : (err != ERR_SQL_DB_TOO_OLD) &&
237 : (err != ERR_SQL_DB_TOO_NEW))
238 : {
239 : // Something failed, delete and restore new ones.
240 0 : destroy_backend();
241 0 : qof_book_destroy (m_book);
242 0 : m_book = qof_book_new();
243 0 : LEAVE ("error from backend %d", get_error ());
244 0 : return;
245 : }
246 :
247 29 : LEAVE ("sess = %p, uri=%s", this, m_uri.c_str ());
248 : }
249 :
250 : void
251 47 : QofSessionImpl::begin (const char* new_uri, SessionOpenMode mode) noexcept
252 : {
253 :
254 :
255 47 : ENTER (" sess=%p mode=%d, URI=%s", this, mode, new_uri);
256 47 : clear_error ();
257 : /* Check to see if this session is already open */
258 47 : if (m_uri.size ())
259 : {
260 0 : if (ERR_BACKEND_NO_ERR != get_error ())
261 0 : push_error (ERR_BACKEND_LOCKED, {});
262 0 : LEAVE("push error book is already open ");
263 0 : return;
264 : }
265 :
266 : /* seriously invalid */
267 47 : if (!new_uri)
268 : {
269 0 : if (ERR_BACKEND_NO_ERR != get_error ())
270 0 : push_error (ERR_BACKEND_BAD_URL, {});
271 0 : LEAVE("push error missing new_uri");
272 0 : return;
273 : }
274 :
275 47 : char * scheme {g_uri_parse_scheme (new_uri)};
276 47 : char * filename {nullptr};
277 47 : if (g_strcmp0 (scheme, "file") == 0)
278 40 : filename = gnc_uri_get_path(new_uri);
279 7 : else if (!scheme)
280 0 : filename = g_strdup (new_uri);
281 :
282 47 : if (filename && g_file_test (filename, G_FILE_TEST_IS_DIR))
283 : {
284 0 : if (ERR_BACKEND_NO_ERR == get_error ())
285 0 : push_error (ERR_BACKEND_BAD_URL, {});
286 0 : g_free (filename);
287 0 : g_free (scheme);
288 0 : LEAVE("Can't open a directory");
289 0 : return;
290 : }
291 : /* destroy the old backend */
292 47 : destroy_backend ();
293 : /* Store the session URL */
294 47 : m_uri = new_uri;
295 47 : m_creating = mode == SESSION_NEW_STORE || mode == SESSION_NEW_OVERWRITE;
296 47 : if (filename)
297 80 : load_backend ("file");
298 : else /* access method found, load appropriate backend */
299 14 : load_backend (scheme);
300 47 : g_free (filename);
301 47 : g_free (scheme);
302 :
303 : /* No backend was found. That's bad. */
304 47 : if (m_backend == nullptr)
305 : {
306 1 : m_uri = {};
307 1 : if (ERR_BACKEND_NO_ERR == get_error ())
308 0 : push_error (ERR_BACKEND_BAD_URL, {});
309 1 : LEAVE (" BAD: no backend: sess=%p book-id=%s",
310 : this, new_uri);
311 1 : return;
312 : }
313 :
314 : /* If there's a begin method, call that. */
315 46 : m_backend->session_begin(this, m_uri.c_str(), mode);
316 46 : PINFO ("Done running session_begin on backend");
317 46 : QofBackendError const err {m_backend->get_error()};
318 46 : auto msg (m_backend->get_message());
319 46 : if (err != ERR_BACKEND_NO_ERR)
320 : {
321 2 : m_uri = {};
322 2 : push_error (err, msg);
323 2 : LEAVE (" backend error %d %s", err, msg.empty() ? "(null)" : msg.c_str());
324 2 : return;
325 : }
326 44 : if (!msg.empty())
327 : {
328 0 : PWARN("%s", msg.c_str());
329 : }
330 :
331 44 : LEAVE (" sess=%p book-id=%s", this, new_uri);
332 46 : }
333 :
334 : void
335 263 : QofSessionImpl::end () noexcept
336 : {
337 263 : ENTER ("sess=%p uri=%s", this, m_uri.c_str ());
338 263 : if (m_backend != nullptr)
339 74 : m_backend->session_end();
340 263 : clear_error ();
341 263 : m_uri.clear();
342 263 : LEAVE ("sess=%p uri=%s", this, m_uri.c_str ());
343 263 : }
344 :
345 : /* error handling functions --------------------------------*/
346 :
347 : void
348 382 : QofSessionImpl::clear_error () noexcept
349 : {
350 382 : m_last_err = ERR_BACKEND_NO_ERR;
351 382 : m_error_message = {};
352 :
353 : /* pop the stack on the backend as well. */
354 382 : if (auto backend = qof_book_get_backend (m_book))
355 : {
356 79 : QofBackendError err = ERR_BACKEND_NO_ERR;
357 : do
358 79 : err = backend->get_error();
359 79 : while (err != ERR_BACKEND_NO_ERR);
360 : }
361 382 : }
362 :
363 : void
364 33 : QofSessionImpl::push_error (QofBackendError const err, std::string message) noexcept
365 : {
366 33 : m_last_err = err;
367 33 : m_error_message = message;
368 33 : }
369 :
370 : QofBackendError
371 149 : QofSessionImpl::get_error () noexcept
372 : {
373 : /* if we have a local error, return that. */
374 149 : if (m_last_err != ERR_BACKEND_NO_ERR)
375 9 : return m_last_err;
376 140 : auto qof_be = qof_book_get_backend (m_book);
377 140 : if (qof_be == nullptr) return ERR_BACKEND_NO_ERR;
378 :
379 73 : m_last_err = qof_be->get_error();
380 73 : return m_last_err;
381 : }
382 :
383 : const std::string&
384 0 : QofSessionImpl::get_error_message () const noexcept
385 : {
386 0 : return m_error_message;
387 : }
388 :
389 : QofBackendError
390 4 : QofSessionImpl::pop_error () noexcept
391 : {
392 4 : QofBackendError err {get_error ()};
393 4 : clear_error ();
394 4 : return err;
395 : }
396 :
397 : /* Accessors (getters/setters) -----------------------------*/
398 :
399 : QofBook *
400 13720 : QofSessionImpl::get_book () const noexcept
401 : {
402 13720 : if (!m_book) return nullptr;
403 13720 : if (qof_book_is_open (m_book))
404 13720 : return m_book;
405 0 : return nullptr;
406 : }
407 :
408 : QofBackend *
409 3 : QofSession::get_backend () const noexcept
410 : {
411 3 : return m_backend;
412 : }
413 :
414 : const std::string&
415 0 : QofSessionImpl::get_file_path () const noexcept
416 : {
417 0 : auto backend = qof_book_get_backend (m_book);
418 0 : if (!backend) return empty_string;
419 0 : return backend->get_uri();
420 : }
421 :
422 : std::string const &
423 0 : QofSessionImpl::get_uri () const noexcept
424 : {
425 0 : return m_uri;
426 : }
427 :
428 : bool
429 0 : QofSessionImpl::is_saving () const noexcept
430 : {
431 0 : return m_saving;
432 : }
433 :
434 : /* Manipulators (save, load, etc.) -------------------------*/
435 :
436 : void
437 16 : QofSessionImpl::save (QofPercentageFunc percentage_func) noexcept
438 : {
439 16 : if (!qof_book_session_not_saved (m_book)) //Clean book, nothing to do.
440 7 : return;
441 9 : m_saving = true;
442 9 : ENTER ("sess=%p uri=%s", this, m_uri.c_str ());
443 :
444 : /* If there is a backend, the book is dirty, and the backend is reachable
445 : * (i.e. we can communicate with it), then synchronize with the backend. If
446 : * we cannot contact the backend (e.g. because we've gone offline, the
447 : * network has crashed, etc.) then raise an error so that the controlling
448 : * dialog can offer the user a chance to save in a different way.
449 : */
450 9 : if (m_backend)
451 : {
452 : /* if invoked as SaveAs(), then backend not yet set */
453 9 : if (qof_book_get_backend (m_book) != m_backend)
454 9 : qof_book_set_backend (m_book, m_backend);
455 9 : m_backend->set_percentage(percentage_func);
456 9 : m_backend->sync(m_book);
457 9 : auto err = m_backend->get_error();
458 9 : if (err != ERR_BACKEND_NO_ERR)
459 : {
460 0 : push_error (err, {});
461 0 : m_saving = false;
462 0 : return;
463 : }
464 : /* If we got to here, then the backend saved everything
465 : * just fine, and we are done. So return. */
466 9 : clear_error ();
467 9 : LEAVE("Success");
468 : }
469 : else
470 : {
471 0 : push_error (ERR_BACKEND_NO_HANDLER, "failed to load backend");
472 0 : LEAVE("error -- No backend!");
473 : }
474 9 : m_saving = false;
475 : }
476 :
477 : void
478 1 : QofSessionImpl::safe_save (QofPercentageFunc percentage_func) noexcept
479 : {
480 1 : if (!(m_backend && m_book)) return;
481 1 : if (qof_book_get_backend (m_book) != m_backend)
482 0 : qof_book_set_backend (m_book, m_backend);
483 1 : m_backend->set_percentage(percentage_func);
484 1 : m_backend->safe_sync(get_book ());
485 1 : auto err = m_backend->get_error();
486 1 : auto msg = m_backend->get_message();
487 1 : if (err != ERR_BACKEND_NO_ERR)
488 : {
489 0 : m_uri = "";
490 0 : push_error (err, msg);
491 : }
492 1 : }
493 :
494 : void
495 1 : QofSessionImpl::ensure_all_data_loaded () noexcept
496 : {
497 1 : if (!(m_backend && m_book)) return;
498 1 : if (qof_book_get_backend (m_book) != m_backend)
499 0 : qof_book_set_backend (m_book, m_backend);
500 1 : m_backend->load(m_book, LOAD_TYPE_LOAD_ALL);
501 1 : push_error (m_backend->get_error(), {});
502 : }
503 :
504 : void
505 9 : QofSessionImpl::swap_books (QofSessionImpl & other) noexcept
506 : {
507 9 : ENTER ("sess1=%p sess2=%p", this, &other);
508 : // don't swap (that is, double-swap) read_only flags
509 9 : if (m_book && other.m_book)
510 4 : qof_book_swap_books_readonly (m_book, other.m_book);
511 9 : std::swap (m_book, other.m_book);
512 9 : auto mybackend = qof_book_get_backend (m_book);
513 9 : qof_book_set_backend (m_book, qof_book_get_backend (other.m_book));
514 9 : qof_book_set_backend (other.m_book, mybackend);
515 9 : LEAVE (" ");
516 9 : }
517 :
518 : bool
519 0 : QofSessionImpl::events_pending () const noexcept
520 : {
521 0 : return false;
522 : }
523 :
524 : bool
525 0 : QofSessionImpl::process_events () const noexcept
526 : {
527 0 : return false;
528 : }
529 :
530 : /* XXX This exports the list of accounts to a file. It does not
531 : * export any transactions. It's a place-holder until full
532 : * book-closing is implemented.
533 : */
534 : bool
535 0 : QofSessionImpl::export_session (QofSessionImpl & real_session,
536 : QofPercentageFunc percentage_func) noexcept
537 : {
538 0 : auto real_book = real_session.get_book ();
539 0 : ENTER ("tmp_session=%p real_session=%p book=%p uri=%s",
540 : this, &real_session, real_book, m_uri.c_str ());
541 :
542 : /* There must be a backend or else. (It should always be the file
543 : * backend too.)
544 : */
545 0 : if (!m_backend) return false;
546 :
547 0 : m_backend->set_percentage(percentage_func);
548 :
549 0 : m_backend->export_coa(real_book);
550 0 : auto err = m_backend->get_error();
551 0 : if (err != ERR_BACKEND_NO_ERR)
552 0 : return false;
553 0 : return true;
554 : }
555 :
556 : /* C Wrapper Functions */
557 : /* ====================================================================== */
558 :
559 : const char *
560 0 : qof_session_get_error_message (const QofSession * session)
561 : {
562 0 : if (!session) return "";
563 0 : return session->get_error_message ().c_str ();
564 : }
565 :
566 : QofBackendError
567 4 : qof_session_pop_error (QofSession * session)
568 : {
569 4 : if (!session) return ERR_BACKEND_NO_BACKEND;
570 4 : return session->pop_error ();
571 : }
572 :
573 : QofBook *
574 13719 : qof_session_get_book (const QofSession *session)
575 : {
576 13719 : if (!session) return NULL;
577 13719 : return session->get_book ();
578 : }
579 :
580 : const char *
581 0 : qof_session_get_file_path (const QofSession *session)
582 : {
583 0 : if (!session) return nullptr;
584 0 : auto& path{session->get_file_path()};
585 0 : return path.empty() ? nullptr : path.c_str ();
586 : }
587 :
588 : void
589 1 : qof_session_ensure_all_data_loaded (QofSession *session)
590 : {
591 1 : if (session == nullptr) return;
592 1 : return session->ensure_all_data_loaded ();
593 : }
594 :
595 : const char *
596 0 : qof_session_get_url (const QofSession *session)
597 : {
598 0 : if (!session) return NULL;
599 0 : return session->get_uri ().c_str ();
600 : }
601 :
602 : QofBackend *
603 3 : qof_session_get_backend (const QofSession *session)
604 : {
605 3 : if (!session) return NULL;
606 3 : return session->get_backend ();
607 : }
608 :
609 : void
610 47 : qof_session_begin (QofSession *session, const char * uri, SessionOpenMode mode)
611 : {
612 47 : if (!session) return;
613 47 : session->begin(uri, mode);
614 : }
615 :
616 : void
617 30 : qof_session_load (QofSession *session,
618 : QofPercentageFunc percentage_func)
619 : {
620 30 : if (!session) return;
621 30 : session->load (percentage_func);
622 : }
623 :
624 : void
625 16 : qof_session_save (QofSession *session,
626 : QofPercentageFunc percentage_func)
627 : {
628 16 : if (!session) return;
629 16 : session->save (percentage_func);
630 : }
631 :
632 : void
633 1 : qof_session_safe_save(QofSession *session, QofPercentageFunc percentage_func)
634 : {
635 1 : if (!session) return;
636 1 : session->safe_save (percentage_func);
637 : }
638 :
639 : gboolean
640 0 : qof_session_save_in_progress(const QofSession *session)
641 : {
642 0 : if (!session) return false;
643 0 : return session->is_saving ();
644 : }
645 :
646 : void
647 98 : qof_session_end (QofSession *session)
648 : {
649 98 : if (!session) return;
650 98 : session->end ();
651 : }
652 :
653 : void
654 9 : qof_session_swap_data (QofSession *session_1, QofSession *session_2)
655 : {
656 9 : if (session_1 == session_2) return;
657 9 : if (!session_1 || !session_2) return;
658 9 : session_1->swap_books (*session_2);
659 : }
660 :
661 : gboolean
662 0 : qof_session_events_pending (const QofSession *session)
663 : {
664 0 : if (!session) return false;
665 0 : return session->events_pending ();
666 : }
667 :
668 : gboolean
669 0 : qof_session_process_events (QofSession *session)
670 : {
671 0 : if (!session) return FALSE;
672 0 : return session->process_events ();
673 : }
674 :
675 : gboolean
676 0 : qof_session_export (QofSession *tmp_session,
677 : QofSession *real_session,
678 : QofPercentageFunc percentage_func)
679 : {
680 0 : if ((!tmp_session) || (!real_session)) return FALSE;
681 0 : return tmp_session->export_session (*real_session, percentage_func);
682 : }
683 :
684 : /* ================= Static function access for testing ================= */
685 :
686 : void init_static_qofsession_pointers (void);
687 :
688 0 : void qof_session_load_backend (QofSession * session, const char * access_method)
689 : {
690 0 : session->load_backend (access_method);
691 0 : }
692 :
693 : static void
694 0 : qof_session_clear_error (QofSession * session)
695 : {
696 0 : session->clear_error ();
697 0 : }
698 :
699 : static void
700 0 : qof_session_destroy_backend (QofSession * session)
701 : {
702 0 : session->destroy_backend ();
703 0 : }
704 :
705 0 : void qof_session_set_uri (QofSession * session, char const * uri)
706 : {
707 0 : if (!uri)
708 0 : session->m_uri = "";
709 : else
710 0 : session->m_uri = uri;
711 0 : }
712 :
713 : void (*p_qof_session_load_backend) (QofSession *, const char * access_method);
714 : void (*p_qof_session_clear_error) (QofSession *);
715 : void (*p_qof_session_destroy_backend) (QofSession *);
716 : void (*p_qof_session_set_uri) (QofSession *, char const * uri);
717 :
718 : void
719 0 : init_static_qofsession_pointers (void)
720 : {
721 0 : p_qof_session_load_backend = &qof_session_load_backend;
722 0 : p_qof_session_clear_error = &qof_session_clear_error;
723 0 : p_qof_session_destroy_backend = &qof_session_destroy_backend;
724 0 : p_qof_session_set_uri = &qof_session_set_uri;
725 0 : }
726 :
727 : QofBackendError
728 115 : qof_session_get_error (QofSession * session)
729 : {
730 115 : if (!session) return ERR_BACKEND_NO_BACKEND;
731 115 : return session->get_error();
732 : }
|