/*
openisis - an open implementation of the CDS/ISIS database
Version 0.8.x (patchlevel see file Version)
Copyright (C) 2001-2003 by Erik Grziwotz, erik@openisis.org
This library is free software; you can redistribute it and/or
modify it under the terms of the GNU Lesser General Public
License as published by the Free Software Foundation; either
version 2.1 of the License, or (at your option) any later version.
This library is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
Lesser General Public License for more details.
You should have received a copy of the GNU Lesser General Public
License along with this library; if not, write to the Free Software
Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
see README for more information
EOH */
/*
$Id: ldsp.c,v 1.32 2003/06/15 15:57:43 mawag Exp $
server side request processing.
*/
#include <errno.h>
#include <limits.h> /* PATH_MAX */
#include <stdarg.h>
#include <stdio.h> /* vsnprintf */
#include <string.h>
#include "openisis.h"
#include "loi.h"
#include "ldb.h"
#include "ldsp.h"
#include "lfdt.h"
/* #include "lsv.h" */
#include "luti.h"
#ifdef WIN32
#define vsnprintf _vsnprintf
#endif
#define ISID 0
#define ISER 1
#define ITMS 2
#define SSTS 3
#define RSPVAR \
Db *dbh = 0; \
Rec *rsp = 0; \
int sdbid;
#define RSPADDR \
if (! rspa) { rspa = &rsp; }
#define RSPHEAD \
*rspa = rAddI (*rspa, OPENISIS_COM_SID, sst[ISID], !0); \
*rspa = rAddI (*rspa, OPENISIS_COM_SER, sst[ISER], !0);
#define RSPNOERR \
*rspa = rAddI (*rspa, OPENISIS_RSP_ERR, 0, !0);
#define RSPDB \
RADDS (*rspa, OPENISIS_COM_DBN, dbn, !0);
#define RSPFREE mFree (rsp);
#define GETDB(rname) \
sdbid = ldspGetDb (&dbh, sst, dbn); \
if (0 > sdbid) { \
int rt = _RqsError (sst, ERR_INVAL, rspa, cb, \
cld, rname, sst[ITMS] ? \
"db %s remounted" : "db %s not open", dbn); \
return rt; \
}
#define RSPFDT \
if (dbh->fdt) { \
*rspa = openIsisFFdt2Rec (dbh->fdt, *rspa, 0); \
} \
if (dbh->cfg) { \
*rspa = luti_wrap (*rspa, dbh->cfg, OPENISIS_COM_CFG); \
}
#define DELIVER \
if (*rspa) { \
int rt = 0; \
if (cb) { \
rt = (*cb) (rspa, cld); \
mFree (*rspa); \
} \
return rt; \
} \
return sMsg (ERR_NOMEM, "ldsp: deliver: rsp == 0");
#define UTF8REC(rec,inv,rname) \
if (openIsisEnc2Utf8 && dbh->cfg) { \
char ebuf[128]; \
char *enc = rString (dbh->cfg, OPENISIS_DENC, 0, ebuf, sizeof(ebuf)); \
if (enc && *enc && strcmp (enc, "utf-8")) { \
ldspUtf8Rec (&rec, enc, inv); \
} \
}
/* ************************************************************
*/
static int ldspGetDb (Db **db, int sst[], const char *dbn) {
Db *dbh = nDbByName (stub0, dbn);
if (! dbh) {
sst[ITMS] = 0;
return -1;
}
if (sst[ITMS]) {
if (-1 == sst[ITMS] || /* dont want default of rInt call */
(dbh->tms && dbh->tms != sst[ITMS])
) {
sst[ITMS] = dbh->tms;
return -1;
}
}
if (db) {
*db = dbh;
}
return dbh->dbid;
}
static int ldspUtf8Rec (Rec **rec, const char *enc, int invert) {
char buf[4096];
const char *dst;
Rec *res = 0;
Field *F, *E;
if (*rec && (*rec)->len) {
E = (*rec)->field + (*rec)->len;
for (F = (*rec)->field; E > F; ++F) {
if (0 == F->len || (1 == F->len && ! *(F->val))) {
RADDS (res, F->tag, "", !0);
}
else {
dst = (*openIsisEnc2Utf8) (enc,
F->val, F->len, buf, sizeof(buf), invert);
if (! dst) {
if (res) {
mFree (res);
}
return sMsg (ERR_TRASH,
"ldsp: cannot convert %s %s utf8: %.*s",
enc, (invert ? "from" : "to"), F->len, F->val);
}
RADDS (res, F->tag, dst, !0);
if (dst != buf && dst != F->val) {
(*openIsisEnc2Utf8) (0, dst, 0, 0, 0, 0);
}
}
if (! res) {
return sMsg (ERR_NOMEM, "ldsp: convert %s %s utf8: res == 0",
enc, (invert ? "from" : "to"));
}
}
res->rowid = (*rec)->rowid;
res->dbid = (*rec)->dbid;
mFree (*rec);
*rec = res;
}
return 0;
}
static int _RqsError (
int sst[], int err, Rec **rspa, LdspDlvCb *cb,
void *cld, const char *rname, const char *fmt, ...
) {
Rec *rsp = 0;
char msg[OPENISIS_ERRMSGLEN];
va_list ap;
RSPADDR
RSPHEAD
sMsg (LOG_VERBOSE, "ldsp: _RqsError %s %x from %d/%d",
rname, err, sst[ISID], sst[ISER]);
if (sst[ITMS] && -1 != sst[ITMS]) {
*rspa = rAddI (*rspa, OPENISIS_COM_TMS, sst[ITMS], !0);
}
*rspa = rAddI (*rspa, OPENISIS_RSP_ERR, err, !0);
if (0 != fmt && 0 != *fmt) {
va_start (ap, fmt);
vsnprintf (msg, OPENISIS_ERRMSGLEN - 1, fmt, ap);
msg[OPENISIS_ERRMSGLEN - 1] = 0;
va_end (ap);
RADDS (*rspa, OPENISIS_RSP_MSG, msg, !0);
sMsg (LOG_WARN, "ldsp: err on rqs %s: %d(%x) - %s",
rname, err, err, msg);
}
else {
sMsg (LOG_WARN, "ldsp: err on rqs %s: %d(%x)",
rname, err, err);
}
DELIVER
}
static int _RqsLsDb (
int sst[], Rec **rspa, LdspDlvCb *cb, void *cld
) {
Rec *rsp = 0;
Schema *sc;
char *n;
int j;
sc = nSchema (stub0);
if (! sc) {
j = _RqsError (sst, ERR_BADF, rspa, cb, cld,
"lsdb", "local schema not initialized");
return j;
}
RSPADDR
RSPHEAD
RSPNOERR
sMsg (LOG_VERBOSE, "ldsp: _RqsLsDb from %d/%d", sst[ISID], sst[ISER]);
for (j = 0; sc->ndbs > j; ++j) {
if (*(n = sc->dbs[j]->name)) {
RADDS (*rspa, OPENISIS_COM_DBN, n, !0);
}
}
DELIVER
}
static int _RqsEval (
int sst[], Rec *cmd, Rec **rspa, LdspDlvCb *cb, void *cld
) {
Rec *res = 0;
Rec *rsp = 0;
int j;
if (! openIsisEval) {
j = _RqsError (sst, ERR_IDIOT, rspa, cb, cld,
"eval", "server has no eval function");
mFree (cmd);
return j;
}
j = (*openIsisEval) (cmd, &res);
mFree (cmd);
RSPADDR
RSPHEAD
*rspa = rAddI (*rspa, OPENISIS_RSP_ERR, j, !0);
if (res) {
*rspa = rAddI (*rspa, OPENISIS_RSP_NUMT, 1, !0);
*rspa = rAddI (*rspa, OPENISIS_RSP_NUMR, 1, !0);
*rspa = rAddI (*rspa, OPENISIS_COM_ROW, 0, !0);
*rspa = luti_wrap (*rspa, res, OPENISIS_COM_REC);
mFree (res);
}
else {
*rspa = rAddI (*rspa, OPENISIS_RSP_NUMT, 0, !0);
*rspa = rAddI (*rspa, OPENISIS_RSP_NUMR, 0, !0);
}
DELIVER
}
static int _RqsOpen (
int sst[], const char *dbn, Rec *dbpar, Fdt *fdt,
Rec **rspa, LdspDlvCb *cb, void *cld
) {
#define STRERRNOLEN 128
RSPVAR
Schema *sc;
char msg[STRERRNOLEN];
sst[ITMS] = 0;
sc = nSchema (stub0);
if (! sc) {
int rt = _RqsError (sst, ERR_BADF, rspa, cb, cld,
"open", "%s: local schema not initialized", dbn);
mFree (dbpar);
return rt;
}
sMsg (LOG_VERBOSE, "ldsp: _RqsOpen %s from %d/%d",
dbn, sst[ISID], sst[ISER]);
errno = 0;
dbh = cDOpen (dbn, dbpar, sc->cfg, fdt);
if (! dbh) {
int rt;
strncpy (msg, strerror (errno), STRERRNOLEN - 1)
[STRERRNOLEN - 1] = 0;
rt = _RqsError (sst, ERR_BADF, rspa, cb, cld,
"open", "%s: %s", dbn, msg);
mFree (dbpar);
return rt;
}
sdbid = dbh->dbid;
dbn = dbh->name;
RSPADDR
RSPHEAD
RSPNOERR
RSPDB
*rspa = rAddI (*rspa, OPENISIS_RSP_DBID, sdbid, !0);
*rspa = rAddI (*rspa, OPENISIS_COM_TMS, dbh->tms, !0);
mFree (dbpar);
DELIVER
}
static int _RqsClose (
int sst[], const char *dbn, Rec **rspa, LdspDlvCb *cb, void *cld
) {
RSPVAR
int err;
GETDB("close")
sMsg (LOG_VERBOSE, "ldsp: _RqsClose %s from %d/%d",
dbn, sst[ISID], sst[ISER]);
err = cDClose (sdbid);
RSPADDR
RSPHEAD
*rspa = rAddI (*rspa, OPENISIS_RSP_DBID, sdbid, !0);
*rspa = rAddI (*rspa, OPENISIS_RSP_ERR, err, !0);
RSPDB
DELIVER
}
static int _RqsMount (
int sst[], const char *dbn, Rec **rspa, LdspDlvCb *cb, void *cld
) {
RSPVAR
GETDB("mount")
RSPADDR
RSPHEAD
sMsg (LOG_VERBOSE, "ldsp: _RqsMount %s from %d/%d",
dbn, sst[ISID], sst[ISER]);
RSPNOERR
RSPDB
RSPFDT
DELIVER
}
static int _RqsMaxrow (
int sst[], const char *dbn, int flags,
Rec **rspa, LdspDlvCb *cb, void *cld
) {
RSPVAR
int maxrow;
GETDB("maxrow")
maxrow = dMaxId (sdbid);
if (0 > maxrow) {
int rt = _RqsError (sst, ERR_TRASH, rspa, cb, cld, "maxrow",
"no maxrow for %s", dbn);
RSPFREE
return rt;
}
RSPADDR
RSPHEAD
sMsg (LOG_VERBOSE, "ldsp: _RqsMaxrow %s from %d/%d",
dbn, sst[ISID], sst[ISER]);
RSPNOERR
RSPDB
if (OPENISIS_RQSF_MNT & flags) {
RSPFDT
}
*rspa = rAddI (*rspa, OPENISIS_COM_ROW, maxrow, !0);
DELIVER
}
static int _RqsQuery (
int sst[], const char *dbn, const char *key, int mode, int skip,
int size, int flags, Rec **rspa, LdspDlvCb *cb, void *cld
) {
RSPVAR
Set set;
Rec *rec;
int lrt;
int j;
int rdr = OPENISIS_RQSF_QRR & flags;
GETDB("query")
sMsg (LOG_VERBOSE, "ldsp: _RqsQuery %s from %d/%d",
dbn, sst[ISID], sst[ISER]);
set.len = 0; /* input */
lrt = dQuery (&set, sdbid, key, mode, skip);
if (0 > lrt) {
int rt = _RqsError (sst, ERR_TRASH, rspa, cb, cld, "query",
"%d,%s = %d", sdbid, key, lrt);
return rt;
}
RSPADDR
RSPHEAD
RSPNOERR
RSPDB
if (OPENISIS_RQSF_MNT & flags) {
RSPFDT
}
*rspa = rAddI (*rspa, OPENISIS_RSP_NUMT, lrt, !0);
if (0 < size && lrt > size) {
lrt = size;
}
*rspa = rAddI (*rspa, OPENISIS_RSP_NUMR, lrt, !0);
for (j = 0; lrt > j; ++j) {
*rspa = rAddI (*rspa, OPENISIS_COM_ROW, set.id[j], !0);
}
if (rdr) {
for (j = 0; *rspa && lrt > j; ++j) {
rec = dRead (sdbid, set.id[j]);
if (rec) {
UTF8REC (rec, 0, "query")
}
else {
sMsg (ERR_TRASH | LOG_WARN,
"db %s: non-existent mfn %d for key %s",
dbn, set.id[j], key);
rec = rDup (0, 0, 0);
}
*rspa = luti_wrap (*rspa, rec, OPENISIS_COM_REC);
mFree (rec);
}
}
DELIVER
}
static int _RqsRead (
int sst[], const char *dbn, int row, int flags,
Rec **rspa, LdspDlvCb *cb, void *cld
) {
RSPVAR
Rec *rec;
GETDB("read")
RSPADDR
RSPHEAD
sMsg (LOG_VERBOSE, "ldsp: _RqsRead %s %d from %d/%d",
dbn, row, sst[ISID], sst[ISER]);
RSPNOERR
RSPDB
if (OPENISIS_RQSF_MNT & flags) {
RSPFDT
}
rec = dRead (sdbid, row);
if (rec) {
*rspa = rAddI (*rspa, OPENISIS_RSP_NUMT, 1, !0);
*rspa = rAddI (*rspa, OPENISIS_RSP_NUMR, 1, !0);
*rspa = rAddI (*rspa, OPENISIS_COM_ROW, row, !0);
UTF8REC (rec, 0, "read")
*rspa = luti_wrap (*rspa, rec, OPENISIS_COM_REC);
mFree (rec);
}
else {
*rspa = rAddI (*rspa, OPENISIS_RSP_NUMT, 0, !0);
*rspa = rAddI (*rspa, OPENISIS_RSP_NUMR, 0, !0);
}
DELIVER
}
static int _RqsUpdate (
int sst[], const char *dbn, int row, Rec *rec, Rec *idx, int flags,
Rec **rspa, LdspDlvCb *cb, void *cld
) {
RSPVAR
int rtw;
GETDB("update")
sMsg (LOG_VERBOSE, "ldsp: _RqsUpdate %s %d from %d/%d",
dbn, row, sst[ISID], sst[ISER]);
if (rec) {
rec->rowid = row;
rec->dbid = sdbid;
UTF8REC (rec, !0, "update")
}
rtw = dWritex (sdbid, rec, idx);
if (rtw) {
rtw = _RqsError (sst, ERR_TRASH, rspa, cb, cld,
"update", "write error %d", rtw);
mFree (rec);
mFree (idx);
return rtw;
}
RSPADDR
RSPHEAD
RSPNOERR
RSPDB
if (OPENISIS_RQSF_MNT & flags) {
RSPFDT
}
if (rec) {
row = rec->rowid;
mFree (rec);
}
mFree (idx);
*rspa = rAddI (*rspa, OPENISIS_COM_ROW, row, !0);
DELIVER
}
static int _RqsDelete (
int sst[], const char *dbn, int row, int flags,
Rec **rspa, LdspDlvCb *cb, void *cld
) {
(void)flags;
sMsg (LOG_VERBOSE, "ldsp: _RqsDelete %s %d from %d/%d",
dbn, row, sst[ISID], sst[ISER]);
return _RqsError (sst, ERR_TRASH, rspa, cb, cld, "delete",
"request not implemented yet");
}
/* ************************************************************
package functions
*/
int ldspProcess (Rec *rqs, Rec **rspa, LdspDlvCb *cb, void *cld) {
char buf[PATH_MAX];
char *dbn = 0;
int sst[SSTS];
int rtyp;
int flags = 0;
if (0 == rqs) {
return sMsg (ERR_IDIOT, "ldspProcess: no request");
}
if (0 == rspa && 0 == cb) {
return sMsg (ERR_IDIOT, "ldspProcess: no callback");
}
rtyp = rInt (rqs, OPENISIS_RQS_TYPE, -1, 0);
sst[ISID] = rInt (rqs, OPENISIS_COM_SID, -1, 0);
sst[ISER] = rInt (rqs, OPENISIS_COM_SER, -1, 0);
sst[ITMS] = rInt (rqs, OPENISIS_COM_TMS, -1, 0);
if (OPENISIS_RQST_LSDB != rtyp &&
OPENISIS_RQST_EVAL != rtyp
) {
dbn = rString (rqs, OPENISIS_COM_DBN, 0, buf, sizeof(buf));
if (!dbn) {
i2a (buf, rtyp);
return _RqsError (sst, ERR_INVAL, rspa, cb, cld, buf,
"no db name given");
}
flags = rInt (rqs, OPENISIS_RQS_FLG, 0, 0);
}
switch (rtyp) {
case OPENISIS_RQST_LSDB:
return _RqsLsDb (sst, rspa, cb, cld);
case OPENISIS_RQST_OPEN: {
Rec *dbpar = luti_unwrap (rqs, 0, OPENISIS_COM_CFG, -1);
Fdt *fdt = fRec2Fdt (rqs);
return _RqsOpen (sst, dbn, dbpar, fdt, rspa, cb, cld);
}
case OPENISIS_RQST_CLOS:
return _RqsClose (sst, dbn, rspa, cb, cld);
case OPENISIS_RQST_MNT:
return _RqsMount (sst, dbn, rspa, cb, cld);
case OPENISIS_RQST_MROW:
return _RqsMaxrow (sst, dbn, flags, rspa, cb, cld);
case OPENISIS_RQST_QRY: {
char kybuf[OPENISIS_QRY_KEYLEN];
char *key;
int mode, skip, size;
key = rString (rqs, OPENISIS_RQS_KEY, 0, kybuf, OPENISIS_QRY_KEYLEN);
mode = rInt (rqs, OPENISIS_RQS_QMOD, -1, 0);
skip = rInt (rqs, OPENISIS_RQS_SKIP, 0, 0);
size = rInt (rqs, OPENISIS_RQS_SIZE, -1, 0);
if (0 > mode || 0 == key) {
return _RqsError (sst, ERR_INVAL, rspa, cb, cld, "query",
"missing mode or key");
}
return _RqsQuery (sst, dbn, key,
mode, skip, size, flags, rspa, cb, cld);
}
case OPENISIS_RQST_READ: {
int rowid;
rowid = rInt (rqs, OPENISIS_COM_ROW, -1, 0);
if (0 > rowid) {
return _RqsError (sst, ERR_INVAL, rspa, cb, cld, "read",
"missing rowid");
}
return _RqsRead (sst, dbn, rowid, flags, rspa, cb, cld);
}
case OPENISIS_RQST_INS:
case OPENISIS_RQST_UPD: {
Rec *rec, *idx;
int rowid = 0;
int ins = rtyp == OPENISIS_RQST_INS;
rec = luti_unwrap (rqs, 0, OPENISIS_COM_REC, -1);
idx = luti_unwrap (rqs, 0, OPENISIS_RQS_IDX, -1);
if (!(rec || idx)) {
return _RqsError (sst, ERR_INVAL, rspa, cb, cld,
"update", "missing record or index");
}
rowid = rInt (rqs, OPENISIS_COM_ROW, 0, 0);
if (! ins && 0 >= rowid) {
return _RqsError (sst, ERR_INVAL, rspa, cb, cld,
"update", "missing rowid");
}
return _RqsUpdate (sst, dbn, rowid, rec, idx, flags, rspa, cb, cld);
}
case OPENISIS_RQST_DEL: {
int rowid;
rowid = rInt (rqs, OPENISIS_COM_ROW, -1, 0);
if (0 > rowid) {
return _RqsError (sst, ERR_INVAL, rspa, cb, cld, "delete",
"missing rowid");
}
return _RqsDelete (sst, dbn, rowid, flags, rspa, cb, cld);
}
case OPENISIS_RQST_EVAL: {
Rec *rec;
rec = luti_unwrap (rqs, 0, OPENISIS_COM_REC, -1);
if (! rec) {
return _RqsError (sst, ERR_INVAL, rspa, cb, cld,
"eval", "missing command record");
}
return _RqsEval (sst, rec, rspa, cb, cld);
}
} /* switch */
i2a (buf, rtyp);
return _RqsError (sst, ERR_INVAL, rspa, cb, cld, buf,
"unrecognized request type");
} /* ldspProcess */