mirror of
https://github.com/Gnucash/gnucash.git
synced 2025-02-25 18:55:30 -06:00
get the hooks into the backend lined up right
git-svn-id: svn+ssh://svn.gnucash.org/repo/gnucash/trunk@3503 57a11ea4-9604-0410-9ed3-97b8803252fd
This commit is contained in:
parent
3dd1feacde
commit
9d14f77759
@ -26,7 +26,7 @@
|
||||
|
||||
|
||||
typedef enum {
|
||||
ERR_BACKEND_NONE = 0,
|
||||
ERR_BACKEND_NO_ERR = 0,
|
||||
ERR_BACKEND_MISC,
|
||||
|
||||
/* fileio errors */
|
||||
@ -38,6 +38,7 @@ typedef enum {
|
||||
ERR_BFILEIO_ALLOC,
|
||||
|
||||
/* network errors */
|
||||
ERR_NETIO_NO_CONNECTION,
|
||||
ERR_NETIO_SHORT_READ,
|
||||
ERR_NETIO_WRONG_CONTENT_TYPE,
|
||||
ERR_NETIO_NOT_GNCXML
|
||||
@ -47,6 +48,22 @@ typedef enum {
|
||||
typedef struct _backend Backend;
|
||||
|
||||
/*
|
||||
* The book_begin() routine gives the backend a second initialization
|
||||
* opportunity. It is suggested that the backend check that
|
||||
* the URL is syntactically correct, and that it is actually
|
||||
* reachable. This is probably(?) a good time to initialize
|
||||
* the actual network connection.
|
||||
*
|
||||
* The book_load() routine should return at least an account tree,
|
||||
* and all currencies. It does not have to return any transactions
|
||||
* whatsoever, as these are obtained at a later stage when a user
|
||||
* opens a register, resulting in a query being sent to the backend.
|
||||
*
|
||||
* (Its OK to send over transactinos at this point, but one should
|
||||
* be careful of the network load; also, its possible that whatever
|
||||
* is sent is not what the user wanted anyway, which is why its
|
||||
* better to wait for the query).
|
||||
*
|
||||
* The trans_commit_edit() routine takes two transaction arguments:
|
||||
* the first is the proposed new transaction; the second is the
|
||||
* 'original' transaction. The second argument is here for
|
||||
@ -74,6 +91,20 @@ typedef struct _backend Backend;
|
||||
* cache of the split data. This will allow the gnucash client to
|
||||
* continue functioning even when disconnected from the server:
|
||||
* this is because it will have its local cache of data to work from.
|
||||
*
|
||||
* The sync() routine synchronizes the engine contents to the backend.
|
||||
* This is done by using version numbers (hack alert -- the engine
|
||||
* does not currently contain version numbers).
|
||||
* If the engine contents are newer than what's in the backend, the
|
||||
* data is stored to the backend. If the engine contents are older,
|
||||
* then the engine contents are updated.
|
||||
*
|
||||
* Note that this sync operation is only meant to apply to the
|
||||
* current contents of the engine. This routine is not intended
|
||||
* to be used to fetch account/transaction data from the backend.
|
||||
* (It might pull new splits from the backend, if this is what is
|
||||
* needed to update an existing transaction. It might pull new
|
||||
* currencies (??))
|
||||
*
|
||||
* The last_err member indicates the last error that occured.
|
||||
* It should probably be implemented as an array (actually,
|
||||
@ -82,8 +113,9 @@ typedef struct _backend Backend;
|
||||
|
||||
struct _backend
|
||||
{
|
||||
AccountGroup * (*book_load) (GNCBook *, const char * book_id);
|
||||
void (*book_end) (GNCBook *);
|
||||
void (*book_begin) (GNCBook *, const char *book_id);
|
||||
AccountGroup * (*book_load) (Backend *);
|
||||
void (*book_end) (Backend *);
|
||||
int (*account_begin_edit) (Backend *, Account *, int defer);
|
||||
int (*account_commit_edit) (Backend *, Account *);
|
||||
int (*trans_begin_edit) (Backend *, Transaction *);
|
||||
@ -91,6 +123,7 @@ struct _backend
|
||||
int (*trans_rollback_edit) (Backend *, Transaction *);
|
||||
|
||||
void (*run_query) (Backend *, Query *);
|
||||
void (*sync) (Backend *, AccountGroup *);
|
||||
|
||||
GNCBackendError last_err;
|
||||
};
|
||||
|
@ -25,9 +25,10 @@
|
||||
* Rudimentary implmentation right now; good enough for a demo,
|
||||
* but that's all.
|
||||
*
|
||||
* HACK ALRT -- this should be moved into its own sbdirectory
|
||||
* HACK ALERT -- this should be moved into its own subdirectory
|
||||
* Mostly so that the engine build doesn't require libghttp
|
||||
* as a dependency.
|
||||
* as a dependency. In general, backends should be dynamically
|
||||
* loaded ...
|
||||
*/
|
||||
|
||||
|
||||
@ -152,33 +153,46 @@ setup_request (XMLBackend *be)
|
||||
}
|
||||
}
|
||||
|
||||
/* ==================================================================== */
|
||||
/* Initialize the connection ... ?? */
|
||||
/* validate the URL for syntactic correctness ?? */
|
||||
/* maybe check to see if the server is reachable? */
|
||||
|
||||
static void
|
||||
xmlbeBookBegin (GNCBook *book, const char *url)
|
||||
{
|
||||
XMLBackend *be;
|
||||
if (!book) return;
|
||||
ENTER ("url is %s", url);
|
||||
|
||||
be = (XMLBackend *) xaccGNCBookGetBackend (book);
|
||||
|
||||
/* hack alert -- we store this first url as the url for inital
|
||||
* contact & * for sending queries to
|
||||
* this should be made customizable, I suppose ???? */
|
||||
be->query_url = g_strdup (url);
|
||||
|
||||
LEAVE(" ");
|
||||
}
|
||||
|
||||
/* ==================================================================== */
|
||||
/* Load a set of accounts and currencies from the indicated URL. */
|
||||
|
||||
static AccountGroup *
|
||||
xmlbeBookLoad (GNCBook *book, const char *url)
|
||||
xmlbeBookLoad (Backend *bend)
|
||||
{
|
||||
XMLBackend *be;
|
||||
XMLBackend *be = (XMLBackend *) bend;
|
||||
AccountGroup *grp;
|
||||
ghttp_request *request;
|
||||
const char *bufp;
|
||||
int len;
|
||||
|
||||
if (!book) return NULL;
|
||||
|
||||
ENTER ("url is %s", url);
|
||||
|
||||
be = (XMLBackend *) xaccGNCBookGetBackend (book);
|
||||
|
||||
/* hack alert -- we store this first url as some bogus url
|
||||
* for sending queries to
|
||||
* this should be made customizable, I suppose ???? */
|
||||
be->query_url = g_strdup (url);
|
||||
if (!be) return NULL;
|
||||
|
||||
/* build up a request for the URL */
|
||||
setup_request (be);
|
||||
request = be->request;
|
||||
ghttp_set_uri (request, (char *) url);
|
||||
ghttp_set_uri (request, be->query_url);
|
||||
ghttp_set_type (request, ghttp_type_get);
|
||||
ghttp_prepare (request);
|
||||
ghttp_process (request);
|
||||
@ -188,7 +202,7 @@ xmlbeBookLoad (GNCBook *book, const char *url)
|
||||
if (0 >= len) return NULL;
|
||||
|
||||
bufp = ghttp_get_body(request);
|
||||
grp = gncxml_read_from_buf (bufp, len);
|
||||
grp = gncxml_read_from_buf ((char *)bufp, len);
|
||||
|
||||
LEAVE(" ");
|
||||
return grp;
|
||||
@ -267,6 +281,7 @@ xmlendNew (void)
|
||||
be = (XMLBackend *) malloc (sizeof (XMLBackend));
|
||||
|
||||
/* generic backend handlers */
|
||||
be->be.book_begin = NULL;
|
||||
be->be.book_load = xmlbeBookLoad;
|
||||
be->be.book_end = xmlbeBookEnd;
|
||||
|
||||
@ -276,8 +291,9 @@ xmlendNew (void)
|
||||
be->be.trans_commit_edit = NULL;
|
||||
be->be.trans_rollback_edit = NULL;
|
||||
be->be.run_query = xmlbeRunQuery;
|
||||
be->be.sync = NULL;
|
||||
|
||||
be->be.last_err = ERR_BACKEND_NONE;
|
||||
be->be.last_err = ERR_BACKEND_NO_ERR;
|
||||
|
||||
be->request = ghttp_request_new();
|
||||
be->auth_cookie = NULL;
|
||||
|
@ -324,47 +324,56 @@ gnc_book_begin (GNCBook *book, const char * book_id, gboolean ignore_lock)
|
||||
|
||||
/* -------------------------------------------------- */
|
||||
if ((!strncmp(book_id, "http://", 7)) ||
|
||||
(!strncmp(book_id, "https://", 8)))
|
||||
(!strncmp(book_id, "https://", 8)) ||
|
||||
(!strncmp(book_id, "postgres://", 11)))
|
||||
{
|
||||
/* Store the sessionid URL */
|
||||
book->book_id = g_strdup (book_id);
|
||||
|
||||
/* create the backend */
|
||||
book->backend = xmlendNew();
|
||||
|
||||
/* not sure what else should happen here ... should we check to see
|
||||
* if the URL is reachable ?? Should we login the user ??
|
||||
/* by definition, URL's are fully resolved */
|
||||
/* hack alert -- the only reason we need to set 'fullpath' is
|
||||
* because the file save gui dialogues check for fully resolved
|
||||
* paths
|
||||
*/
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
/* -------------------------------------------------- */
|
||||
if (!strncmp(book_id, "postgres://", 11))
|
||||
{
|
||||
/* Store the sessionid URL */
|
||||
/* we expect this to be in the format
|
||||
* postgres://some.hostname.com/databasename.pql
|
||||
* or maybe
|
||||
* postgres://localhost/databasename.pql
|
||||
* or one can specify the postgres socket port number explicitly:
|
||||
* postgres://some.hostname.com:5432/databasename.pql
|
||||
*/
|
||||
book->book_id = g_strdup (book_id);
|
||||
book->fullpath = g_strdup (book_id);
|
||||
|
||||
/* load different backend based on URL. We should probably
|
||||
* dynamically load these based on some config file ... */
|
||||
if ((!strncmp(book_id, "http://", 7)) ||
|
||||
(!strncmp(book_id, "https://", 8)))
|
||||
{
|
||||
/* create the backend */
|
||||
book->backend = xmlendNew();
|
||||
} else
|
||||
if (!strncmp(book_id, "postgres://", 11))
|
||||
{
|
||||
/* #define SQLHACK */
|
||||
#ifdef SQLHACK
|
||||
{
|
||||
extern Backend * pgendNew (void);
|
||||
book->backend = pgendNew ();
|
||||
#else
|
||||
g_free(book->fullpath);
|
||||
book->fullpath = NULL;
|
||||
book->errtype = ENOSYS;
|
||||
return FALSE;
|
||||
#endif
|
||||
}
|
||||
|
||||
/* if there's a begin method, call that. */
|
||||
if (book->backend->book_begin)
|
||||
{
|
||||
(book->backend->book_begin)(book, book->book_id);
|
||||
/* hack alert we should check for errors here ... */
|
||||
|
||||
/* not sure what else should happen here ... should we check to see
|
||||
* if the URL is reachable ?? Should we login the user ??
|
||||
*/
|
||||
|
||||
}
|
||||
return TRUE;
|
||||
#else
|
||||
book->errtype = ENOSYS;
|
||||
return FALSE;
|
||||
#endif
|
||||
}
|
||||
/* -------------------------------------------------- */
|
||||
|
||||
/* -------------------------------------------------- */
|
||||
|
||||
/* otherwise, lets just assume its a file. */
|
||||
rc = gnc_book_begin_file (book, book_id, ignore_lock);
|
||||
@ -437,7 +446,7 @@ gnc_book_load (GNCBook *book)
|
||||
* The GUI will need to do a query for that.
|
||||
*/
|
||||
if (be && be->book_load) {
|
||||
book->topgroup = (be->book_load) (book, book->book_id);
|
||||
book->topgroup = (be->book_load) (be);
|
||||
}
|
||||
|
||||
xaccGroupSetBackend (book->topgroup, be);
|
||||
@ -480,8 +489,22 @@ gnc_book_save_may_clobber_data (GNCBook *book)
|
||||
void
|
||||
gnc_book_save (GNCBook *book)
|
||||
{
|
||||
Backend *be;
|
||||
if (!book) return;
|
||||
|
||||
/* if there is a backend, and the backend is reachablele
|
||||
* (i.e. we can communicate with it), then synchronize with
|
||||
* the backend. If we cannot contact the backend (e.g.
|
||||
* because we've gone offline, the network has crashed, etc.)
|
||||
* then give the user the option to save to disk.
|
||||
*/
|
||||
be = book->backend;
|
||||
if (be && be->sync && book->topgroup)
|
||||
{
|
||||
(be->sync)(be, book->topgroup);
|
||||
if (ERR_BACKEND_NO_ERR == be->last_err) return;
|
||||
}
|
||||
|
||||
/* if the fullpath doesn't exist, either the user failed to initialize,
|
||||
* or the lockfile was never obtained. Either way, we can't write. */
|
||||
book->errtype = 0;
|
||||
@ -510,6 +533,12 @@ gnc_book_end (GNCBook *book)
|
||||
{
|
||||
if (!book) return;
|
||||
|
||||
/* close down the backend first */
|
||||
if (book->backend && book->backend->book_end)
|
||||
{
|
||||
(book->backend->book_end)(book->backend);
|
||||
}
|
||||
|
||||
book->errtype = 0;
|
||||
book->last_file_err = ERR_FILEIO_NONE;
|
||||
|
||||
@ -542,6 +571,9 @@ gnc_book_destroy (GNCBook *book)
|
||||
|
||||
gnc_book_end (book);
|
||||
|
||||
/* destroy the backend */
|
||||
if (book->backend) g_free(book->backend);
|
||||
|
||||
xaccFreeAccountGroup (book->topgroup);
|
||||
book->topgroup = NULL;
|
||||
|
||||
|
@ -67,8 +67,9 @@ void gnc_book_destroy (GNCBook *book);
|
||||
|
||||
/* The gnc_book_begin () method begins a new book. It takes as an argument
|
||||
* the book id. The book id must be a string in the form of a URI/URL.
|
||||
* In the current implementation, only one type of URI is supported, and
|
||||
* that is the file URI: anything of the form
|
||||
* In the current implementation, only the file URI type is fully
|
||||
* supported, (with others in various stages of prototyping).
|
||||
* A file URI is of the form
|
||||
* "file:/home/somewhere/somedir/file.xac"
|
||||
* The path part must be a valid path. The file-part must be a valid
|
||||
* xacc/gnucash-format file. Paths may be relative or absolute. If the
|
||||
|
Loading…
Reference in New Issue
Block a user