fixed parsing for science direct html with more than one <a href=> per one <tr>
[webpac] / openisis / ldsp.c
1 /*
2         openisis - an open implementation of the CDS/ISIS database
3         Version 0.8.x (patchlevel see file Version)
4         Copyright (C) 2001-2003 by Erik Grziwotz, erik@openisis.org
5
6         This library is free software; you can redistribute it and/or
7         modify it under the terms of the GNU Lesser General Public
8         License as published by the Free Software Foundation; either
9         version 2.1 of the License, or (at your option) any later version.
10
11         This library is distributed in the hope that it will be useful,
12         but WITHOUT ANY WARRANTY; without even the implied warranty of
13         MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
14         Lesser General Public License for more details.
15
16         You should have received a copy of the GNU Lesser General Public
17         License along with this library; if not, write to the Free Software
18         Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
19
20         see README for more information
21 EOH */
22
23 /*
24         $Id: ldsp.c,v 1.32 2003/06/15 15:57:43 mawag Exp $
25         server side request processing.
26 */
27
28 #include <errno.h>
29 #include <limits.h> /* PATH_MAX */
30 #include <stdarg.h>
31 #include <stdio.h>  /* vsnprintf */
32 #include <string.h>
33
34 #include "openisis.h"
35 #include "loi.h"
36 #include "ldb.h"
37 #include "ldsp.h"
38 #include "lfdt.h"
39 /* #include "lsv.h" */
40 #include "luti.h"
41
42 #ifdef WIN32
43 #define vsnprintf _vsnprintf
44 #endif
45
46 #define ISID   0
47 #define ISER   1
48 #define ITMS   2
49 #define SSTS   3
50
51 #define RSPVAR \
52         Db  *dbh = 0; \
53         Rec *rsp = 0; \
54         int sdbid;
55
56 #define RSPADDR \
57         if (! rspa) { rspa = &rsp; }
58
59 #define RSPHEAD \
60         *rspa = rAddI (*rspa, OPENISIS_COM_SID, sst[ISID], !0); \
61         *rspa = rAddI (*rspa, OPENISIS_COM_SER, sst[ISER], !0);
62
63 #define RSPNOERR \
64         *rspa = rAddI (*rspa, OPENISIS_RSP_ERR, 0, !0);
65
66 #define RSPDB \
67         RADDS (*rspa, OPENISIS_COM_DBN, dbn, !0);
68
69 #define RSPFREE  mFree (rsp);
70
71 #define GETDB(rname)                                           \
72         sdbid = ldspGetDb (&dbh, sst, dbn);                        \
73         if (0 > sdbid) {                                           \
74                 int rt = _RqsError (sst, ERR_INVAL, rspa, cb, \
75                         cld, rname, sst[ITMS] ?                            \
76                         "db %s remounted" : "db %s not open", dbn);        \
77                 return rt;                                             \
78         }
79
80 #define RSPFDT \
81         if (dbh->fdt) { \
82                 *rspa = openIsisFFdt2Rec (dbh->fdt, *rspa, 0); \
83         } \
84         if (dbh->cfg) { \
85                 *rspa = luti_wrap (*rspa, dbh->cfg, OPENISIS_COM_CFG); \
86         }
87
88 #define DELIVER \
89         if (*rspa) { \
90                 int rt = 0; \
91                 if (cb) { \
92                         rt = (*cb) (rspa, cld); \
93                         mFree (*rspa); \
94                 } \
95                 return rt; \
96         } \
97         return sMsg (ERR_NOMEM, "ldsp: deliver: rsp == 0");
98
99 #define UTF8REC(rec,inv,rname) \
100         if (openIsisEnc2Utf8 && dbh->cfg) { \
101                 char ebuf[128]; \
102                 char *enc = rString (dbh->cfg, OPENISIS_DENC, 0, ebuf, sizeof(ebuf)); \
103                 if (enc && *enc && strcmp (enc, "utf-8")) { \
104                         ldspUtf8Rec (&rec, enc, inv); \
105                 } \
106         }
107
108 /* ************************************************************
109 */
110
111 static int ldspGetDb (Db **db, int sst[], const char *dbn) {
112         Db *dbh = nDbByName (stub0, dbn);
113         if (! dbh) {
114                 sst[ITMS] = 0;
115                 return -1;
116         }
117         if (sst[ITMS]) {
118                 if (-1 == sst[ITMS] || /* dont want default of rInt call */
119                         (dbh->tms && dbh->tms != sst[ITMS])
120                 ) {
121                         sst[ITMS] = dbh->tms;
122                         return -1;
123                 }
124         }
125         if (db) {
126                 *db = dbh;
127         }
128         return dbh->dbid;
129 }
130
131 static int ldspUtf8Rec (Rec **rec, const char *enc, int invert) {
132         char    buf[4096];
133         const char *dst;
134         Rec    *res  =  0;
135         Field  *F, *E;
136         if (*rec && (*rec)->len) {
137                 E = (*rec)->field + (*rec)->len;
138                 for (F = (*rec)->field; E > F; ++F) {
139                         if (0 == F->len || (1 == F->len && ! *(F->val))) {
140                                 RADDS (res, F->tag, "", !0);
141                         }
142                         else {
143                                 dst = (*openIsisEnc2Utf8) (enc,
144                                         F->val, F->len, buf, sizeof(buf), invert);
145                                 if (! dst) {
146                                         if (res) {
147                                                 mFree (res);
148                                         }
149                                         return sMsg (ERR_TRASH,
150                                                 "ldsp: cannot convert %s %s utf8: %.*s",
151                                                 enc, (invert ? "from" : "to"), F->len, F->val);
152                                 }
153                                 RADDS (res, F->tag, dst, !0);
154                                 if (dst != buf && dst != F->val) {
155                                         (*openIsisEnc2Utf8) (0, dst, 0, 0, 0, 0);
156                                 }
157                         }
158                         if (! res) {
159                                 return sMsg (ERR_NOMEM, "ldsp: convert %s %s utf8: res == 0",
160                                         enc, (invert ? "from" : "to"));
161                         }
162                 }
163                 res->rowid = (*rec)->rowid;
164                 res->dbid = (*rec)->dbid;
165                 mFree (*rec);
166                 *rec = res;
167         }
168         return 0;
169 }
170
171 static int _RqsError (
172         int sst[], int err, Rec **rspa, LdspDlvCb *cb,
173         void *cld, const char *rname, const char *fmt, ...
174 ) {
175         Rec *rsp = 0;
176         char msg[OPENISIS_ERRMSGLEN];
177         va_list ap;
178         RSPADDR
179         RSPHEAD
180         sMsg (LOG_VERBOSE, "ldsp: _RqsError %s %x from %d/%d",
181                 rname, err, sst[ISID], sst[ISER]);
182         if (sst[ITMS] && -1 != sst[ITMS]) {
183                 *rspa = rAddI (*rspa, OPENISIS_COM_TMS, sst[ITMS], !0);
184         }
185         *rspa = rAddI (*rspa, OPENISIS_RSP_ERR, err, !0);
186         if (0 != fmt && 0 != *fmt) {
187                 va_start (ap, fmt);
188                 vsnprintf (msg, OPENISIS_ERRMSGLEN - 1, fmt, ap);
189                 msg[OPENISIS_ERRMSGLEN - 1] = 0;
190                 va_end (ap);
191                 RADDS (*rspa, OPENISIS_RSP_MSG, msg, !0);
192                 sMsg (LOG_WARN, "ldsp: err on rqs %s: %d(%x) - %s",
193                         rname, err, err, msg);
194         }
195         else {
196                 sMsg (LOG_WARN, "ldsp: err on rqs %s: %d(%x)",
197                         rname, err, err);
198         }
199         DELIVER
200 }
201
202 static int _RqsLsDb (
203         int sst[], Rec **rspa, LdspDlvCb *cb, void *cld
204 ) {
205         Rec     *rsp = 0;
206         Schema  *sc;
207         char    *n;
208         int      j;
209         sc = nSchema (stub0);
210         if (! sc) {
211                 j = _RqsError (sst, ERR_BADF, rspa, cb, cld,
212                         "lsdb", "local schema not initialized");
213                 return j;
214         }
215         RSPADDR
216         RSPHEAD
217         RSPNOERR
218         sMsg (LOG_VERBOSE, "ldsp: _RqsLsDb from %d/%d", sst[ISID], sst[ISER]);
219         for (j = 0; sc->ndbs > j; ++j) {
220                 if (*(n = sc->dbs[j]->name)) {
221                         RADDS (*rspa, OPENISIS_COM_DBN, n, !0);
222                 }
223         }
224         DELIVER
225 }
226
227 static int _RqsEval (
228         int sst[], Rec *cmd, Rec **rspa, LdspDlvCb *cb, void *cld
229 ) {
230         Rec  *res = 0;
231         Rec  *rsp = 0;
232         int   j;
233         if (! openIsisEval) {
234                 j = _RqsError (sst, ERR_IDIOT, rspa, cb, cld,
235                         "eval", "server has no eval function");
236                 mFree (cmd);
237                 return j;
238         }
239         j = (*openIsisEval) (cmd, &res);
240         mFree (cmd);
241         RSPADDR
242         RSPHEAD
243         *rspa = rAddI (*rspa, OPENISIS_RSP_ERR, j, !0);
244         if (res) {
245                 *rspa = rAddI (*rspa, OPENISIS_RSP_NUMT, 1, !0);
246                 *rspa = rAddI (*rspa, OPENISIS_RSP_NUMR, 1, !0);
247                 *rspa = rAddI (*rspa, OPENISIS_COM_ROW, 0, !0);
248                 *rspa = luti_wrap (*rspa, res, OPENISIS_COM_REC);
249                 mFree (res);
250         }
251         else {
252                 *rspa = rAddI (*rspa, OPENISIS_RSP_NUMT, 0, !0);
253                 *rspa = rAddI (*rspa, OPENISIS_RSP_NUMR, 0, !0);
254         }
255         DELIVER
256 }
257
258 static int _RqsOpen (
259         int sst[], const char *dbn, Rec *dbpar, Fdt *fdt,
260         Rec **rspa, LdspDlvCb *cb, void *cld
261 ) {
262 #define STRERRNOLEN  128
263         RSPVAR
264         Schema *sc;
265         char    msg[STRERRNOLEN];
266         sst[ITMS] = 0;
267         sc = nSchema (stub0);
268         if (! sc) {
269                 int rt = _RqsError (sst, ERR_BADF, rspa, cb, cld,
270                         "open", "%s: local schema not initialized", dbn);
271                 mFree (dbpar);
272                 return rt;
273         }
274         sMsg (LOG_VERBOSE, "ldsp: _RqsOpen %s from %d/%d",
275                 dbn, sst[ISID], sst[ISER]);
276         errno = 0;
277         dbh = cDOpen (dbn, dbpar, sc->cfg, fdt);
278         if (! dbh) {
279                 int rt;
280                 strncpy (msg, strerror (errno), STRERRNOLEN - 1)
281                         [STRERRNOLEN - 1] = 0;
282                 rt = _RqsError (sst, ERR_BADF, rspa, cb, cld,
283                         "open", "%s: %s", dbn, msg);
284                 mFree (dbpar);
285                 return rt;
286         }
287         sdbid = dbh->dbid;
288         dbn = dbh->name;
289         RSPADDR
290         RSPHEAD
291         RSPNOERR
292         RSPDB
293         *rspa = rAddI (*rspa, OPENISIS_RSP_DBID, sdbid, !0);
294         *rspa = rAddI (*rspa, OPENISIS_COM_TMS, dbh->tms, !0);
295         mFree (dbpar);
296         DELIVER
297 }
298
299 static int _RqsClose (
300         int sst[], const char *dbn, Rec **rspa, LdspDlvCb *cb, void *cld
301 ) {
302         RSPVAR
303         int err;
304         GETDB("close")
305         sMsg (LOG_VERBOSE, "ldsp: _RqsClose %s from %d/%d",
306                 dbn, sst[ISID], sst[ISER]);
307         err = cDClose (sdbid);
308         RSPADDR
309         RSPHEAD
310         *rspa = rAddI (*rspa, OPENISIS_RSP_DBID, sdbid, !0);
311         *rspa = rAddI (*rspa, OPENISIS_RSP_ERR, err, !0);
312         RSPDB
313         DELIVER
314 }
315
316 static int _RqsMount (
317         int sst[], const char *dbn, Rec **rspa, LdspDlvCb *cb, void *cld
318 ) {
319         RSPVAR
320         GETDB("mount")
321         RSPADDR
322         RSPHEAD
323         sMsg (LOG_VERBOSE, "ldsp: _RqsMount %s from %d/%d",
324                 dbn, sst[ISID], sst[ISER]);
325         RSPNOERR
326         RSPDB
327         RSPFDT
328         DELIVER
329 }
330
331 static int _RqsMaxrow (
332         int sst[], const char *dbn, int flags,
333         Rec **rspa, LdspDlvCb *cb, void *cld
334 ) {
335         RSPVAR
336         int maxrow;
337         GETDB("maxrow")
338         maxrow = dMaxId (sdbid);
339         if (0 > maxrow) {
340                 int rt = _RqsError (sst, ERR_TRASH, rspa, cb, cld, "maxrow",
341                         "no maxrow for %s", dbn);
342                 RSPFREE
343                 return rt;
344         }
345         RSPADDR
346         RSPHEAD
347         sMsg (LOG_VERBOSE, "ldsp: _RqsMaxrow %s from %d/%d",
348                 dbn, sst[ISID], sst[ISER]);
349         RSPNOERR
350         RSPDB
351         if (OPENISIS_RQSF_MNT & flags) {
352                 RSPFDT
353         }
354         *rspa = rAddI (*rspa, OPENISIS_COM_ROW, maxrow, !0);
355         DELIVER
356 }
357
358 static int _RqsQuery (
359         int sst[], const char *dbn, const char *key, int mode, int skip,
360         int size, int flags, Rec **rspa, LdspDlvCb *cb, void *cld
361 ) {
362         RSPVAR
363         Set         set;
364         Rec        *rec;
365         int        lrt;
366         int         j;
367         int         rdr = OPENISIS_RQSF_QRR & flags;
368         GETDB("query")
369         sMsg (LOG_VERBOSE, "ldsp: _RqsQuery %s from %d/%d",
370                 dbn, sst[ISID], sst[ISER]);
371         set.len = 0; /* input */
372         lrt = dQuery (&set, sdbid, key, mode, skip);
373         if (0 > lrt) {
374                 int rt = _RqsError (sst, ERR_TRASH, rspa, cb, cld, "query",
375                         "%d,%s = %d", sdbid, key, lrt);
376                 return rt;
377         }
378         RSPADDR
379         RSPHEAD
380         RSPNOERR
381         RSPDB
382         if (OPENISIS_RQSF_MNT & flags) {
383                 RSPFDT
384         }
385         *rspa = rAddI (*rspa, OPENISIS_RSP_NUMT, lrt, !0);
386         if (0 < size && lrt > size) {
387                 lrt = size;
388         }
389         *rspa = rAddI (*rspa, OPENISIS_RSP_NUMR, lrt, !0);
390         for (j = 0; lrt > j; ++j) {
391                 *rspa = rAddI (*rspa, OPENISIS_COM_ROW, set.id[j], !0);
392         }
393         if (rdr) {
394                 for (j = 0; *rspa && lrt > j; ++j) {
395                         rec = dRead (sdbid, set.id[j]);
396                         if (rec) {
397                                 UTF8REC (rec, 0, "query")
398                         }
399                         else {
400                                 sMsg (ERR_TRASH | LOG_WARN,
401                                         "db %s: non-existent mfn %d for key %s",
402                                         dbn, set.id[j], key);
403                                 rec = rDup (0, 0, 0);
404                         }
405                         *rspa = luti_wrap (*rspa, rec, OPENISIS_COM_REC);
406                         mFree (rec);
407                 }
408         }
409         DELIVER
410 }
411
412 static int _RqsRead (
413         int sst[], const char *dbn, int row, int flags,
414         Rec **rspa, LdspDlvCb *cb, void *cld
415 ) {
416         RSPVAR
417         Rec *rec;
418         GETDB("read")
419         RSPADDR
420         RSPHEAD
421         sMsg (LOG_VERBOSE, "ldsp: _RqsRead %s %d from %d/%d",
422                 dbn, row, sst[ISID], sst[ISER]);
423         RSPNOERR
424         RSPDB
425         if (OPENISIS_RQSF_MNT & flags) {
426                 RSPFDT
427         }
428         rec = dRead (sdbid, row);
429         if (rec) {
430                 *rspa = rAddI (*rspa, OPENISIS_RSP_NUMT, 1, !0);
431                 *rspa = rAddI (*rspa, OPENISIS_RSP_NUMR, 1, !0);
432                 *rspa = rAddI (*rspa, OPENISIS_COM_ROW, row, !0);
433                 UTF8REC (rec, 0, "read")
434                 *rspa = luti_wrap (*rspa, rec, OPENISIS_COM_REC);
435                 mFree (rec);
436         }
437         else {
438                 *rspa = rAddI (*rspa, OPENISIS_RSP_NUMT, 0, !0);
439                 *rspa = rAddI (*rspa, OPENISIS_RSP_NUMR, 0, !0);
440         }
441         DELIVER
442 }
443
444 static int _RqsUpdate (
445         int sst[], const char *dbn, int row, Rec *rec, Rec *idx, int flags,
446         Rec **rspa, LdspDlvCb *cb, void *cld
447 ) {
448         RSPVAR
449         int rtw;
450         GETDB("update")
451         sMsg (LOG_VERBOSE, "ldsp: _RqsUpdate %s %d from %d/%d",
452                 dbn, row, sst[ISID], sst[ISER]);
453         if (rec) {
454                 rec->rowid = row;
455                 rec->dbid = sdbid;
456                 UTF8REC (rec, !0, "update")
457         }
458         rtw = dWritex (sdbid, rec, idx);
459         if (rtw) {
460                 rtw = _RqsError (sst, ERR_TRASH, rspa, cb, cld,
461                         "update", "write error %d", rtw);
462                 mFree (rec);
463                 mFree (idx);
464                 return rtw;
465         }
466         RSPADDR
467         RSPHEAD
468         RSPNOERR
469         RSPDB
470         if (OPENISIS_RQSF_MNT & flags) {
471                 RSPFDT
472         }
473         if (rec) {
474                 row = rec->rowid;
475                 mFree (rec);
476         }
477         mFree (idx);
478         *rspa = rAddI (*rspa, OPENISIS_COM_ROW, row, !0);
479         DELIVER
480 }
481
482 static int _RqsDelete (
483         int sst[], const char *dbn, int row, int flags,
484         Rec **rspa, LdspDlvCb *cb, void *cld
485 ) {
486         (void)flags;
487         sMsg (LOG_VERBOSE, "ldsp: _RqsDelete %s %d from %d/%d",
488                 dbn, row, sst[ISID], sst[ISER]);
489         return _RqsError (sst, ERR_TRASH, rspa, cb, cld, "delete",
490                 "request not implemented yet");
491 }
492
493 /* ************************************************************
494         package functions
495 */
496
497 int ldspProcess (Rec *rqs, Rec **rspa, LdspDlvCb *cb, void *cld) {
498         char   buf[PATH_MAX];
499         char  *dbn  =  0;
500         int    sst[SSTS];
501         int    rtyp;
502         int    flags  =  0;
503
504         if (0 == rqs) {
505                 return sMsg (ERR_IDIOT, "ldspProcess: no request");
506         }
507         if (0 == rspa && 0 == cb) {
508                 return sMsg (ERR_IDIOT, "ldspProcess: no callback");
509         }
510
511         rtyp      = rInt (rqs, OPENISIS_RQS_TYPE, -1, 0);
512         sst[ISID] = rInt (rqs, OPENISIS_COM_SID, -1, 0);
513         sst[ISER] = rInt (rqs, OPENISIS_COM_SER, -1, 0);
514         sst[ITMS] = rInt (rqs, OPENISIS_COM_TMS, -1, 0);
515
516         if (OPENISIS_RQST_LSDB != rtyp &&
517                 OPENISIS_RQST_EVAL != rtyp
518         ) {
519                 dbn = rString (rqs, OPENISIS_COM_DBN, 0, buf, sizeof(buf));
520                 if (!dbn) {
521                         i2a (buf, rtyp);
522                         return _RqsError (sst, ERR_INVAL, rspa, cb, cld, buf,
523                                 "no db name given");
524                 }
525                 flags = rInt (rqs, OPENISIS_RQS_FLG, 0, 0);
526         }
527
528         switch (rtyp) {
529
530         case OPENISIS_RQST_LSDB:
531                 return _RqsLsDb (sst, rspa, cb, cld);
532
533         case OPENISIS_RQST_OPEN: {
534                 Rec *dbpar = luti_unwrap (rqs, 0, OPENISIS_COM_CFG, -1);
535                 Fdt *fdt   = fRec2Fdt (rqs);
536                 return _RqsOpen (sst, dbn, dbpar, fdt, rspa, cb, cld);
537         }
538
539         case OPENISIS_RQST_CLOS:
540                 return _RqsClose (sst, dbn, rspa, cb, cld);
541
542         case OPENISIS_RQST_MNT:
543                 return _RqsMount (sst, dbn, rspa, cb, cld);
544
545         case OPENISIS_RQST_MROW:
546                 return _RqsMaxrow (sst, dbn, flags, rspa, cb, cld);
547
548         case OPENISIS_RQST_QRY: {
549                 char  kybuf[OPENISIS_QRY_KEYLEN];
550                 char *key;
551                 int  mode, skip, size;
552                 key  = rString (rqs, OPENISIS_RQS_KEY, 0, kybuf, OPENISIS_QRY_KEYLEN);
553                 mode = rInt (rqs, OPENISIS_RQS_QMOD, -1, 0);
554                 skip = rInt (rqs, OPENISIS_RQS_SKIP, 0, 0);
555                 size = rInt (rqs, OPENISIS_RQS_SIZE, -1, 0);
556                 if (0 > mode || 0 == key) {
557                         return _RqsError (sst, ERR_INVAL, rspa, cb, cld, "query",
558                                 "missing mode or key");
559                 }
560                 return _RqsQuery (sst, dbn, key,
561                         mode, skip, size, flags, rspa, cb, cld);
562         }
563
564         case OPENISIS_RQST_READ: {
565                 int rowid;
566                 rowid = rInt (rqs, OPENISIS_COM_ROW, -1, 0);
567                 if (0 > rowid) {
568                         return _RqsError (sst, ERR_INVAL, rspa, cb, cld, "read",
569                                 "missing rowid");
570                 }
571                 return _RqsRead (sst, dbn, rowid, flags, rspa, cb, cld);
572         }
573
574         case OPENISIS_RQST_INS:
575         case OPENISIS_RQST_UPD: {
576                 Rec  *rec, *idx;
577                 int   rowid = 0;
578                 int   ins = rtyp == OPENISIS_RQST_INS;
579                 rec = luti_unwrap (rqs, 0, OPENISIS_COM_REC, -1);
580                 idx = luti_unwrap (rqs, 0, OPENISIS_RQS_IDX, -1);
581                 if (!(rec || idx)) {
582                         return _RqsError (sst, ERR_INVAL, rspa, cb, cld,
583                                 "update", "missing record or index");
584                 }
585                 rowid = rInt (rqs, OPENISIS_COM_ROW, 0, 0);
586                 if (! ins && 0 >= rowid) {
587                         return _RqsError (sst, ERR_INVAL, rspa, cb, cld,
588                                 "update", "missing rowid");
589                 }
590                 return _RqsUpdate (sst, dbn, rowid, rec, idx, flags, rspa, cb, cld);
591         }
592
593         case OPENISIS_RQST_DEL: {
594                 int rowid;
595                 rowid = rInt (rqs, OPENISIS_COM_ROW, -1, 0);
596                 if (0 > rowid) {
597                         return _RqsError (sst, ERR_INVAL, rspa, cb, cld, "delete",
598                                 "missing rowid");
599                 }
600                 return _RqsDelete (sst, dbn, rowid, flags, rspa, cb, cld);
601         }
602
603         case OPENISIS_RQST_EVAL: {
604                 Rec  *rec;
605                 rec = luti_unwrap (rqs, 0, OPENISIS_COM_REC, -1);
606                 if (! rec) {
607                         return _RqsError (sst, ERR_INVAL, rspa, cb, cld,
608                                 "eval", "missing command record");
609                 }
610                 return _RqsEval (sst, rec, rspa, cb, cld);
611         }
612
613         } /* switch */
614
615         i2a (buf, rtyp);
616         return _RqsError (sst, ERR_INVAL, rspa, cb, cld, buf,
617                 "unrecognized request type");
618 } /* ldspProcess */
619