mirror of
https://github.com/Gnucash/gnucash.git
synced 2025-02-25 18:55:30 -06:00
changethe XML network IO to formally to be a backend plugin.
git-svn-id: svn+ssh://svn.gnucash.org/repo/gnucash/trunk@3431 57a11ea4-9604-0410-9ed3-97b8803252fd
This commit is contained in:
parent
4c8b980884
commit
2f4edaef66
@ -16,6 +16,7 @@
|
||||
|
||||
#include "Account.h"
|
||||
#include "Group.h"
|
||||
#include "Query.h"
|
||||
#include "Transaction.h"
|
||||
#include "gnc-book.h"
|
||||
|
||||
@ -32,13 +33,15 @@ typedef struct _backend Backend;
|
||||
|
||||
struct _backend
|
||||
{
|
||||
AccountGroup * (*book_begin) (GNCBook *, const char * book_id);
|
||||
AccountGroup * (*book_load) (GNCBook *, const char * book_id);
|
||||
int (*book_end) (GNCBook *);
|
||||
int (*account_begin_edit) (Backend *, Account *, int defer);
|
||||
int (*account_commit_edit) (Backend *, Account *);
|
||||
int (*trans_begin_edit) (Backend *, Transaction *);
|
||||
int (*trans_commit_edit) (Backend *, Transaction *new, Transaction *orig);
|
||||
int (*trans_rollback_edit) (Backend *, Transaction *);
|
||||
|
||||
int (*run_query) (Backend *, Query *);
|
||||
};
|
||||
|
||||
/*
|
||||
|
@ -22,8 +22,11 @@
|
||||
\********************************************************************/
|
||||
|
||||
/*
|
||||
* ultra super reudimentary right now
|
||||
* this will be very very different in final verisn
|
||||
* ultra super rudimentary right now
|
||||
|
||||
* HACK ALRT -- this should be moved into its own sbdirectory
|
||||
* Mostly so that the engine build doesn't require libghttp
|
||||
* as a dependency.
|
||||
*/
|
||||
|
||||
|
||||
@ -33,25 +36,50 @@
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
|
||||
#include "BackendP.h"
|
||||
#include "NetIO.h"
|
||||
#include "gnc-book.h"
|
||||
#include "gnc-engine-util.h"
|
||||
#include "io-gncxml.h"
|
||||
|
||||
static short module = MOD_IO;
|
||||
|
||||
/* ==================================================================== */
|
||||
typedef struct _xmlend XMLBackend;
|
||||
|
||||
AccountGroup *
|
||||
xaccRecvAccountGroup (char *url)
|
||||
struct _xmlend {
|
||||
Backend be;
|
||||
|
||||
ghttp_request *request;
|
||||
char * query_url;
|
||||
};
|
||||
|
||||
Backend *xmlendNew (void);
|
||||
|
||||
|
||||
/* ==================================================================== */
|
||||
/* Load a set of accounts and currencies from the indicated URL. */
|
||||
|
||||
static AccountGroup *
|
||||
xmlbeBookLoad (GNCBook *book, const char *url)
|
||||
{
|
||||
XMLBackend *be;
|
||||
AccountGroup *grp;
|
||||
ghttp_request *request;
|
||||
char *bufp;
|
||||
int len;
|
||||
|
||||
if (!book) return NULL;
|
||||
|
||||
ENTER ("url is %s\n", url);
|
||||
request = ghttp_request_new();
|
||||
ghttp_set_uri (request, url);
|
||||
|
||||
be = (XMLBackend *) xaccGNCBookGetBackend (book);
|
||||
|
||||
/* hack alert -- some bogus url for sending queries to */
|
||||
/* this should be made customizable, I suppose ???? */
|
||||
be->query_url = g_strdup (url);
|
||||
|
||||
request = be->request;
|
||||
ghttp_set_uri (request, (char *) url);
|
||||
ghttp_set_type (request, ghttp_type_get);
|
||||
ghttp_set_header (request, http_hdr_Connection, "close");
|
||||
ghttp_set_sync (request, ghttp_sync);
|
||||
@ -71,18 +99,110 @@ xaccRecvAccountGroup (char *url)
|
||||
}
|
||||
else
|
||||
{
|
||||
char * errstr = ghttp_get_error (request);
|
||||
char * reason = ghttp_reason_phrase (request);
|
||||
const char * errstr = ghttp_get_error (request);
|
||||
const char * reason = ghttp_reason_phrase (request);
|
||||
PERR ("connection failed: %s %s\n", errstr, reason);
|
||||
|
||||
ghttp_request_destroy(request);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
ghttp_request_destroy(request);
|
||||
|
||||
LEAVE("\n");
|
||||
return NULL;
|
||||
}
|
||||
|
||||
/* ==================================================================== */
|
||||
|
||||
static int
|
||||
xmlbeRunQuery (Backend *b, Query *q)
|
||||
{
|
||||
XMLBackend *be = (XMLBackend *) b;
|
||||
ghttp_request *request;
|
||||
char *bufp;
|
||||
int len;
|
||||
|
||||
if (!be || !q) return 999;
|
||||
|
||||
/* set up a new http request, of type POST */
|
||||
request = ghttp_request_new();
|
||||
ghttp_set_uri (request, be->query_url);
|
||||
ghttp_set_type (request, ghttp_type_post);
|
||||
ghttp_set_header (request, http_hdr_Connection, "close");
|
||||
ghttp_set_sync (request, ghttp_sync);
|
||||
ghttp_clean (request);
|
||||
|
||||
/* convert the query to XML */
|
||||
gncxml_write_query_to_buf (q, &bufp, &len);
|
||||
|
||||
/* put the XML into the request body */
|
||||
ghttp_set_body (request, bufp, len);
|
||||
|
||||
/* send it off the the webserver, wait for the reply */
|
||||
ghttp_prepare (request);
|
||||
ghttp_process (request);
|
||||
|
||||
/* free the query xml */
|
||||
free (bufp);
|
||||
|
||||
len = ghttp_get_body_len(request);
|
||||
|
||||
if (0 < len)
|
||||
{
|
||||
bufp = ghttp_get_body(request);
|
||||
PINFO ("reply length=%d\n", len);
|
||||
DEBUG ("%s\n", bufp);
|
||||
|
||||
/* we got back a list of splits, these need to be merged in */
|
||||
// grp = gncxml_read_from_buf (bufp, len);
|
||||
return 0;
|
||||
}
|
||||
else
|
||||
{
|
||||
const char * errstr = ghttp_get_error (request);
|
||||
const char * reason = ghttp_reason_phrase (request);
|
||||
PERR ("connection failed: %s %s\n", errstr, reason);
|
||||
|
||||
return 444;
|
||||
}
|
||||
|
||||
|
||||
LEAVE("\n");
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* ==================================================================== */
|
||||
#if 0
|
||||
|
||||
xmlbeBookEnd ()
|
||||
{
|
||||
ghttp_request_destroy (be->request);
|
||||
g_free (be->query_url);
|
||||
}
|
||||
#endif
|
||||
|
||||
/* ==================================================================== */
|
||||
|
||||
Backend *
|
||||
xmlendNew (void)
|
||||
{
|
||||
XMLBackend *be;
|
||||
be = (XMLBackend *) malloc (sizeof (XMLBackend));
|
||||
|
||||
/* generic backend handlers */
|
||||
be->be.book_load = xmlbeBookLoad;
|
||||
be->be.book_end = NULL;
|
||||
|
||||
be->be.account_begin_edit = NULL;
|
||||
be->be.account_commit_edit = NULL;
|
||||
be->be.trans_begin_edit = NULL;
|
||||
be->be.trans_commit_edit = NULL;
|
||||
be->be.trans_rollback_edit = NULL;
|
||||
be->be.run_query = xmlbeRunQuery;
|
||||
|
||||
be->request = ghttp_request_new();
|
||||
be->query_url = NULL;
|
||||
|
||||
return (Backend *) be;
|
||||
}
|
||||
|
||||
|
||||
/* ============================== END OF FILE ======================== */
|
||||
|
@ -25,8 +25,8 @@
|
||||
#ifndef __XACC_NET_IO_H__
|
||||
#define __XACC_NET_IO_H__
|
||||
|
||||
#include "Group.h"
|
||||
#include "BackendP.h"
|
||||
|
||||
AccountGroup * xaccRecvAccountGroup (char *url);
|
||||
Backend * xmlendNew (void);
|
||||
|
||||
#endif /* __XACC_NET_IO_H__ */
|
||||
|
@ -22,23 +22,24 @@
|
||||
|
||||
#include <ctype.h>
|
||||
#include <glib.h>
|
||||
#include <sys/types.h>
|
||||
#include <math.h>
|
||||
#include <string.h>
|
||||
#include <regex.h>
|
||||
#include <sys/time.h>
|
||||
#include <sys/types.h>
|
||||
#include <unistd.h>
|
||||
#include <string.h>
|
||||
#include <math.h>
|
||||
|
||||
#include <assert.h>
|
||||
|
||||
#include "gnc-common.h"
|
||||
#include "gnc-engine-util.h"
|
||||
#include "gnc-numeric.h"
|
||||
#include "TransactionP.h"
|
||||
#include "Transaction.h"
|
||||
#include "Account.h"
|
||||
#include "BackendP.h"
|
||||
#include "Group.h"
|
||||
#include "Query.h"
|
||||
#include "Transaction.h"
|
||||
#include "TransactionP.h"
|
||||
|
||||
static short module = MOD_QUERY;
|
||||
|
||||
@ -955,12 +956,14 @@ xaccQueryGetSplits(Query * q) {
|
||||
GList * all_accts, * node;
|
||||
Account * current;
|
||||
QueryTerm * qt;
|
||||
Backend * be;
|
||||
|
||||
int total_splits_checked = 0;
|
||||
int split_count = 0;
|
||||
int acct_ok;
|
||||
|
||||
if (!q) return NULL;
|
||||
ENTER("query=%p", q);
|
||||
|
||||
/* tmp hack alert */
|
||||
q->changed = 1;
|
||||
@ -982,6 +985,12 @@ xaccQueryGetSplits(Query * q) {
|
||||
/* prepare the terms for processing */
|
||||
xaccQueryCompileTerms (q);
|
||||
|
||||
/* if there is a backend, query the backend, let it fetch the data */
|
||||
be = xaccGroupGetBackend (q->acct_group);
|
||||
if (be && be->run_query) {
|
||||
(be->run_query) (be, q);
|
||||
}
|
||||
|
||||
/* iterate over accounts */
|
||||
all_accts = xaccGroupGetSubAccounts (q->acct_group);
|
||||
|
||||
@ -2165,6 +2174,15 @@ xaccQuerySetGroup(Query * q, AccountGroup * g) {
|
||||
}
|
||||
|
||||
|
||||
/*******************************************************************
|
||||
* xaccQueryGetGroup
|
||||
*******************************************************************/
|
||||
AccountGroup *
|
||||
xaccQueryGetGroup(Query * q) {
|
||||
return (q->acct_group);
|
||||
}
|
||||
|
||||
|
||||
/*******************************************************************
|
||||
* xaccQueryGetEarliestDateFound
|
||||
*******************************************************************/
|
||||
|
@ -206,20 +206,23 @@ typedef struct {
|
||||
* basic Query API
|
||||
*******************************************************************/
|
||||
|
||||
Query * xaccMallocQuery(void);
|
||||
void xaccFreeQuery(Query *);
|
||||
Query * xaccQueryCopy(Query *q);
|
||||
void xaccQuerySetGroup(Query * q, AccountGroup * group);
|
||||
Query * xaccQueryInvert(Query * q1);
|
||||
Query * xaccQueryMerge(Query * q1, Query * q2, QueryOp op);
|
||||
void xaccQueryClear(Query * q);
|
||||
void xaccQueryPurgeTerms(Query * q, pd_type_t type);
|
||||
Query * xaccMallocQuery(void);
|
||||
void xaccFreeQuery(Query *);
|
||||
Query * xaccQueryCopy(Query *q);
|
||||
void xaccQuerySetGroup(Query * q, AccountGroup * group);
|
||||
AccountGroup *xaccQueryGetGroup(Query * q);
|
||||
|
||||
int xaccQueryHasTerms(Query * q);
|
||||
gboolean xaccQueryHasTermType(Query * q, pd_type_t type);
|
||||
GList * xaccQueryGetTerms(Query * q);
|
||||
Query * xaccQueryInvert(Query * q1);
|
||||
Query * xaccQueryMerge(Query * q1, Query * q2, QueryOp op);
|
||||
void xaccQueryClear(Query * q);
|
||||
|
||||
GList * xaccQueryGetSplits(Query * q);
|
||||
void xaccQueryPurgeTerms(Query * q, pd_type_t type);
|
||||
int xaccQueryHasTerms(Query * q);
|
||||
gboolean xaccQueryHasTermType(Query * q, pd_type_t type);
|
||||
GList * xaccQueryGetTerms(Query * q);
|
||||
|
||||
/* after the query has been set up, call this to run the query */
|
||||
GList * xaccQueryGetSplits(Query * q);
|
||||
|
||||
/* handy for debugging */
|
||||
void xaccQueryPrint(Query *q);
|
||||
|
@ -83,7 +83,8 @@ struct _gnc_book
|
||||
int lockfd;
|
||||
|
||||
/* ---------------------------------------------------- */
|
||||
/* this struct member applies only for SQL i/o */
|
||||
/* This struct member applies for network and SQL i/o */
|
||||
/* It is not currently used for file i/o, but maybe it should be ?? */
|
||||
Backend *backend;
|
||||
};
|
||||
|
||||
@ -176,39 +177,6 @@ gnc_book_get_file_path (GNCBook *book)
|
||||
|
||||
/* ============================================================== */
|
||||
|
||||
#if 0
|
||||
static AccountGroup *
|
||||
xaccSessionBeginSQL (Session *sess, const char * dbname)
|
||||
{
|
||||
Backend *be = NULL;
|
||||
AccountGroup *grp = NULL;
|
||||
|
||||
if (!sess) return NULL;
|
||||
|
||||
/* #define SQLHACK */
|
||||
#ifdef SQLHACK
|
||||
{
|
||||
/* for testing the sql, just a hack, remove later ... */
|
||||
extern Backend * pgendNew (void);
|
||||
be = pgendNew ();
|
||||
}
|
||||
#endif
|
||||
|
||||
sess->backend = be;
|
||||
|
||||
if (be && be->session_begin) {
|
||||
grp = (be->session_begin) (sess, dbname);
|
||||
}
|
||||
/* comment out until testing done, else clobber file ...*/
|
||||
/* sess->topgroup = grp; */
|
||||
xaccGroupSetBackend (sess->topgroup, be);
|
||||
|
||||
return (sess->topgroup);
|
||||
}
|
||||
#endif
|
||||
|
||||
/* ============================================================== */
|
||||
|
||||
static gboolean
|
||||
gnc_book_get_file_lock (GNCBook *book)
|
||||
{
|
||||
@ -318,21 +286,6 @@ gnc_book_begin_file (GNCBook *book, const char * filefrag,
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
/* ============================================================== */
|
||||
/* URL mostly a noop --- maybe this would be a login dialog ??? */
|
||||
|
||||
static gboolean
|
||||
gnc_book_begin_http (GNCBook *book, const char * pathfrag)
|
||||
{
|
||||
ENTER ("pathfrag=%s\n", pathfrag);
|
||||
|
||||
/* Store the sessionid URL */
|
||||
book->book_id = g_strdup (pathfrag);
|
||||
|
||||
LEAVE ("\n");
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
/* ============================================================== */
|
||||
|
||||
gboolean
|
||||
@ -369,24 +322,45 @@ gnc_book_begin (GNCBook *book, const char * book_id, gboolean ignore_lock)
|
||||
return rc;
|
||||
}
|
||||
|
||||
if (!strncmp(book_id, "http://", 7))
|
||||
/* -------------------------------------------------- */
|
||||
if ((!strncmp(book_id, "http://", 7)) ||
|
||||
(!strncmp(book_id, "https://", 8)))
|
||||
{
|
||||
rc = gnc_book_begin_http (book, book_id);
|
||||
return rc;
|
||||
}
|
||||
|
||||
if (!strncmp(book_id, "https://", 8))
|
||||
{
|
||||
rc = gnc_book_begin_http (book, book_id);
|
||||
return rc;
|
||||
/* 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 ??
|
||||
*/
|
||||
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);
|
||||
|
||||
/* #define SQLHACK */
|
||||
#ifdef SQLHACK
|
||||
extern Backend * pgendNew (void);
|
||||
book->backend = pgendNew ();
|
||||
#endif
|
||||
|
||||
book->errtype = ENOSYS;
|
||||
return FALSE;
|
||||
}
|
||||
/* -------------------------------------------------- */
|
||||
|
||||
|
||||
/* otherwise, lets just assume its a file. */
|
||||
@ -436,14 +410,34 @@ gnc_book_load (GNCBook *book)
|
||||
LEAVE("\n");
|
||||
return TRUE;
|
||||
}
|
||||
else if (strncmp(book->book_id, "http://", 7) == 0)
|
||||
|
||||
else if ((strncmp(book->book_id, "http://", 7) == 0) ||
|
||||
(strncmp(book->book_id, "https://", 8) == 0) ||
|
||||
(strncmp(book->book_id, "postgres://", 11) == 0))
|
||||
{
|
||||
/* This code should be sufficient to initiliaze *any* backend,
|
||||
* whether http, postgres, or anything else that might come along.
|
||||
* Basically, the idea is that by now, a backend has already been
|
||||
* created & set up. At this point, we only need to get the
|
||||
* top-level account group out of the backend, and that is a
|
||||
* generic, backend-independent operation.
|
||||
*/
|
||||
Backend *be = book->backend;
|
||||
xaccFreeAccountGroup (book->topgroup);
|
||||
book->topgroup = NULL;
|
||||
|
||||
book->errtype = 0;
|
||||
book->last_file_err = ERR_FILEIO_NONE;
|
||||
book->topgroup = xaccRecvAccountGroup (book->book_id);
|
||||
|
||||
/* starting the session should result in a bunch of accounts
|
||||
* and currencies being downloaded, but probably no transactions;
|
||||
* The GUI will need to do a query for that.
|
||||
*/
|
||||
if (be && be->book_load) {
|
||||
book->topgroup = (be->book_load) (book, book->book_id);
|
||||
}
|
||||
|
||||
xaccGroupSetBackend (book->topgroup, be);
|
||||
|
||||
if (!book->topgroup || (book->last_file_err != ERR_FILEIO_NONE))
|
||||
{
|
||||
@ -454,16 +448,6 @@ gnc_book_load (GNCBook *book)
|
||||
LEAVE("\n");
|
||||
return TRUE;
|
||||
}
|
||||
else if (strncmp(book->book_id, "postgres://", 11) == 0)
|
||||
{
|
||||
#ifdef SQLHACK
|
||||
/* for testing the sql, just a hack, remove later ... */
|
||||
/* this should never ever appear here ... */
|
||||
xaccSessionBeginSQL (sess, book->book_id);
|
||||
#endif
|
||||
book->errtype = ENOSYS;
|
||||
return FALSE;
|
||||
}
|
||||
else
|
||||
{
|
||||
book->errtype = ENOSYS;
|
||||
|
@ -904,14 +904,15 @@ pgendNew (void)
|
||||
be = (PGBackend *) malloc (sizeof (PGBackend));
|
||||
|
||||
/* generic backend handlers */
|
||||
be->be.session_begin = pgend_session_begin;
|
||||
be->be.session_end = NULL;
|
||||
be->be.book_load = pgend_session_begin;
|
||||
be->be.book_end = NULL;
|
||||
|
||||
be->be.account_begin_edit = NULL;
|
||||
be->be.account_commit_edit = NULL;
|
||||
be->be.trans_begin_edit = NULL;
|
||||
be->be.trans_commit_edit = pgend_trans_commit_edit;
|
||||
be->be.trans_rollback_edit= NULL;
|
||||
be->be.run_query= NULL;
|
||||
|
||||
/* postgres specific data */
|
||||
be->dbName = NULL;
|
||||
|
@ -1,7 +1,7 @@
|
||||
/*
|
||||
* PostgressBackend.h
|
||||
* PostgresBackend.h
|
||||
*
|
||||
* Implements the callbacks for the postgress backend.
|
||||
* Implements the callbacks for the postgres backend.
|
||||
*
|
||||
*/
|
||||
|
||||
|
@ -1,9 +1,9 @@
|
||||
|
||||
This directory contains very broken, very experimental code
|
||||
for sql support. It doesn't work. The instructions bwlow are for
|
||||
for sql support. It doesn't work. The instructions below are for
|
||||
developers who want to hack this code.
|
||||
|
||||
1) Install postgresl server, client and devel packages.
|
||||
1) Install postgresql server, client and devel packages.
|
||||
2) if installed from redhat, then running /etc/rc.d/init.d/postgresql
|
||||
will setup and initialize all first-time setup & config.
|
||||
3) as root, su - postgres then run 'createuser' to add your user id
|
||||
@ -17,7 +17,14 @@ developers who want to hack this code.
|
||||
|
||||
Development Notes
|
||||
-----------------
|
||||
Just about everything done in this demo is wrong.
|
||||
There has got to be a far more elegant way of 'converting' c structs
|
||||
to sql and back. Because basically, that's all tat this code wants
|
||||
to do: just convert the engine structs to/from corresponding records
|
||||
in an sql database.
|
||||
|
||||
This code only implements writing to the DB, not reading. Its mostly
|
||||
cut-n-paste code that does the same thing over & over again.
|
||||
|
||||
--
|
||||
|
||||
|
||||
|
Loading…
Reference in New Issue
Block a user