improvement: path limit in URL works without full result display
[webpac] / openisis / lstb.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: lstb.c,v 1.41 2003/06/15 15:57:43 mawag Exp $
25         implementation of client side session and communication functions.
26 */
27
28 #include <errno.h>
29 #include <limits.h> /* PATH_MAX */
30 #include <stdarg.h>
31 #include <stdio.h>
32 #include <string.h>
33
34 #include "openisis.h"
35 #include "loi.h"
36 #include "lcli.h"
37 #include "ldb.h"
38 #include "ldsp.h"
39 #include "lfdt.h"
40 #include "luti.h"
41
42 #ifdef WIN32
43 #define snprintf _snprintf
44 #define vsnprintf _vsnprintf
45 #endif
46
47 #define LSTB_STHOSTLEN   32
48
49 #define LSTB_MAXDBS      255  /* according to OPENISIS_DBIDMSK */
50 #define LSTB_MAXSTUBS    255  /* according to OPENISIS_SCIDMSK */
51
52 #define LSTB_INCRDB      8
53 #define LSTB_INCRSTUB    8
54
55 /* ************************************************************
56  * meta
57 */
58
59 /* stub id 0      has idx -1 */
60 /* stub id 0x0100 has idx  0 */
61
62 #define SC_X2I(s)  ( ( (1 + (s)) << 8 ) & OPENISIS_SCIDMSK )
63
64 #define SC_I2X(s)  ( ( ((s) & OPENISIS_SCIDMSK) >> 8 ) - 1 )
65
66 #define DB_X2I(d,s)  ( ((s) & OPENISIS_SCIDMSK) | ((d) & OPENISIS_DBIDMSK) )
67
68 #define DB_I2X(d)    ( (d) & OPENISIS_DBIDMSK )
69
70 #define DB_IDX_OK(st,idx)   \
71         (0 <= (idx) && (st)->head.ndbs > (idx) && * (st)->head.dbs[idx]->name)
72
73 /* ************************************************************
74  * client view of db
75 */
76
77 typedef struct {
78         Db head;
79 } CDb;
80
81 static void cdbCtor (CDb *that,
82         const char *name, int dbid, int tms, Fdt *fdt, Rec *cfg
83 ) {
84         strncpy (that->head.name, name, DBNLEN - 1) [DBNLEN - 1] = 0;
85         that->head.dbid = dbid;
86         that->head.tms  = tms;
87         that->head.fdt  = fdt;
88         that->head.cfg  = cfg;
89 }
90
91 static void cdbDtor (CDb *that) {
92         if (that->head.fdt) {
93                 fFree (that->head.fdt);
94         }
95         if (that->head.cfg) {
96                 mFree (that->head.cfg);
97         }
98         memset (that, 0, sizeof (CDb));
99 }
100
101 /* ************************************************************
102  * client session
103 */
104
105 typedef struct OpenIsisStb {
106         Schema    head;
107         char      host[LSTB_STHOSTLEN];
108         int       port;
109         int       srq;   /* rqs serial no */
110         Rec      *rqs;   /* last request */
111         Rec      *rsp;   /* last response */
112         Db       *rdb;   /* db of embedded rsp recs */
113         CDb      *dbase; /* db array of non-local schema */
114         LutiLT    hsh;
115         CliChnl   chn;
116         OpenIsisStbRqsProc  *rqsp;
117         OpenIsisStbDelProc  *delp;
118         void     *cld;   /* client_data of rqsp */
119         /*MMM deprecated*/
120         OpenIsisRspCb       *actcb;
121         void                *actcld;
122         OpenIsisStubCbData   cbdta;
123 } OpenIsisStb;
124
125 Stub stub0 = 0;
126
127 static Stub  RmtStubs = 0;
128 static int   NumStubs = 0;
129
130 static LutiLT StubLT = 0;
131
132 #define STBSIZE  (sizeof(Stb))
133
134 static int stbLocalRqs (Stub that, Rec *rqs);
135 static int stbSyncRqs (Stub that, Rec *rqs);
136
137 static void stbClear (Stub that) {
138         memset (that, 0, STBSIZE);
139         that->chn.sd = -1;
140 }
141
142 static int stbCtor (Stub that,
143         const char *name, int scid, Rec *cfg,
144         OpenIsisStbRqsProc *rqsp, OpenIsisStbDelProc *delp, void *cld,
145         OpenIsisStubCbData *cbd
146 ) {
147         strncpy (that->head.name, name, SCNLEN - 1) [SCNLEN - 1] = 0;
148         that->head.scid = scid;
149         if (scid) {
150                 if (! rString (cfg, OPENISIS_SC_HOST, 0, that->host, LSTB_STHOSTLEN)) {
151                         return 0;
152                 }
153                 that->port = rInt (cfg, OPENISIS_SC_PORT, 0, 0);
154                 if (0 >= that->port) {
155                         return 0;
156                 }
157                 if (rqsp) {
158                         that->rqsp = rqsp;
159                 }
160                 else {
161                         that->rqsp = &stbSyncRqs;
162                 }
163         }
164         else {
165                 that->rqsp = &stbLocalRqs;
166         }
167         that->head.cfg =
168                 rSet (cfg, RCHG | RDIS, OPENISIS_SC_NAME, that->head.name, 0);
169         that->delp = delp;
170         that->cld  = cld;
171         that->hsh  = luti_ltnew ();
172         if (cbd) {
173                 memcpy (&that->cbdta, cbd, sizeof (OpenIsisStubCbData));
174         }
175         luti_ltadd (StubLT, that->head.name, scid);
176         return !0;
177 }
178
179 /* return idx or -err */
180 static int cdbNewIdx (Stub that) {
181         int     ndb = that->head.ndbs;
182         int     rt, j;
183         for (j = 0; ndb > j; ++j) {
184                 if (! * that->dbase[j].head.name) {
185                         return j;
186                 }
187         }
188         rt = luti_ptrincr (&that->dbase, &ndb,
189                 LSTB_INCRDB, sizeof (CDb), LSTB_MAXDBS);
190         if (0 > rt) {
191                 return sMsg (ERR_NOMEM, "%s: cdbNewIdx: out of memory",
192                         that->head.name);
193         }
194         rt = luti_ptrincr (&that->head.dbs, &that->head.ndbs,
195                 LSTB_INCRDB, sizeof (CDb*), LSTB_MAXDBS);
196         if (0 > rt) {
197                 return sMsg (ERR_NOMEM, "%s: cdbNewIdx: out of memory",
198                         that->head.name);
199         }
200         for (j = that->head.ndbs; 0 <= --j; ) {
201                 that->head.dbs[j] = (Db*) (that->dbase + j);
202         }
203         return rt;
204 }
205
206 /* return bool newly allocated or -err */
207 static int cdbNewIdx0 (Stub that, int dbid) {
208         int  rt;
209         if (dbid < that->head.ndbs) {
210                 if (that->head.dbs[dbid]) {
211                         return 0;
212                 }
213                 return 1;
214         }
215         rt = luti_ptrincr (&that->head.dbs, &that->head.ndbs,
216                 1 + dbid - that->head.ndbs, sizeof (CDb*), -1);
217         if (0 > rt) {
218                 return sMsg (ERR_NOMEM, "%s: cdbNewIdx0(%d): out of memory",
219                         that->head.name, dbid);
220         }
221         return 1;
222 }
223
224 static int stbNewIdx () {
225         int   j;
226         for (j = 0; NumStubs > j; ++j) {
227                 if (! * RmtStubs[j].head.name) {
228                         return j;
229                 }
230         }
231         j = luti_ptrincr (&RmtStubs, &NumStubs,
232                 LSTB_INCRSTUB, STBSIZE, LSTB_MAXSTUBS);
233         /* stbClear (RmtStubs + j) done in nOpen */
234         return j;
235 }
236
237 static Stub stbById (int sid) {
238         int idx;
239         if (0 > sid || ! stub0) {
240                 return 0;
241         }
242         if (0 == sid) {
243                 return stub0;
244         }
245         idx = SC_I2X (sid);
246         if (0 > idx) {
247                 log_msg (LOG_ERROR, "stbById: illegal id %x", sid);
248                 return 0;
249         }
250         if (NumStubs <= idx) {
251                 return 0;
252         }
253         if (! * RmtStubs[idx].head.name) {
254                 return 0;
255         }
256         return RmtStubs + idx;
257 }
258
259 static void stbDtor (Stub that) {
260         luti_ltrmv (StubLT, that->head.name);
261         if (that->cbdta.delcb) {
262                 if (that->actcld) {
263                         (*that->cbdta.delcb) (
264                                 that->cbdta.delcld, that, that->actcld);
265                 }
266                 if (that->cbdta.dfltcld) {
267                         (*that->cbdta.delcb) (
268                                 that->cbdta.delcld, that, that->cbdta.dfltcld);
269                 }
270                 (*that->cbdta.delcb) (that->cbdta.delcld, that, 0);
271         }
272         if (that->dbase) {
273                 int j;
274                 for (j = that->head.ndbs; 0 <= --j;  ) {
275                         if (* that->dbase[j].head.name) {
276                                 cdbDtor (that->dbase + j);
277                         }
278                 }
279                 mFree (that->dbase);
280         }
281         if (that->head.dbs) {
282                 mFree (that->head.dbs);
283         }
284         if (that->head.cfg) {
285                 mFree (that->head.cfg);
286         }
287         luti_ltdel (that->hsh);
288         nClean (that);
289         stbClear (that);
290 }
291
292 static int stbDbidFromName (Stub that, const char *dbn) {
293         int id;
294         if (! dbn) {
295                 return -1;
296         }
297         id = luti_ltget (that->hsh, dbn);
298         return id;
299 }
300
301 static int stbDbIdxFromName (Stub that, const char *dbn) {
302         int idx = stbDbidFromName (that, dbn);
303         if (0 > idx) {
304                 return -1;
305         }
306         idx = DB_I2X (idx);
307         if (! DB_IDX_OK (that, idx)) {
308                 return -1;
309         }
310         return idx;
311 }
312
313 static int stbOpenDb (Stub that) {
314         char  dbn[DBNLEN];
315         Fdt  *fdt;
316         Rec  *cfg;
317         int   idx, dbid, tms;
318         int   newdb = 0;
319
320         if (! rString (that->rsp, OPENISIS_COM_DBN, 0, dbn, DBNLEN)) {
321                 return sMsg (ERR_IDIOT, "%s: stbOpenDb: missing db name in rsp",
322                         that->head.name);
323         }
324         idx = stbDbIdxFromName (that, dbn);
325
326         if (0 > idx) {
327                 idx = cdbNewIdx (that);
328                 if (0 > idx) {
329                         return idx;
330                 }
331                 newdb = !0;
332         }
333         else {
334                 cdbDtor (that->dbase + idx);
335         }
336
337         fdt = fRec2Fdt (that->rsp);
338         cfg = luti_unwrap (that->rsp, 0, OPENISIS_COM_CFG, -1);
339         tms = rInt (that->rsp, OPENISIS_COM_TMS, 0, 0);
340         dbid = DB_X2I (idx, that->head.scid);
341
342         cdbCtor (that->dbase + idx, dbn, dbid, tms, fdt, cfg);
343
344         if (newdb) {
345                 luti_ltadd (that->hsh, dbn, dbid);
346         }
347         return 0;
348 }
349
350 static int stbOpenDb0 (Stub that) {
351         Db  *dbh;
352         int  newdb = 0;
353         int  dbid  = rInt (that->rsp, OPENISIS_RSP_DBID, -1, 0);
354         if (0 > dbid) {
355                 return sMsg (ERR_IDIOT, "stbOpenDb0: missing dbid in rsp");
356         }
357
358         newdb = cdbNewIdx0 (that, dbid);
359         if (0 > newdb) {
360                 return newdb;
361         }
362
363         dbh = ldb_getdb (dbid);
364         if (! dbh) {
365                 return sMsg (ERR_TRASH, "stbOpenDb0: no such db %d", dbid);
366         }
367         log_msg (LOG_INFO, "stbOpenDb0 %d %s", dbid, dbh->name);
368
369         that->head.dbs[dbid] = dbh;
370         if (newdb) {
371                 luti_ltadd (that->hsh, dbh->name, dbid);
372         }
373         return 0;
374 }
375
376 static int stbCloseDb (Stub that) {
377         char  dbn[DBNLEN];
378         CDb  *db;
379         int   dbid;
380         if (! rString (that->rsp, OPENISIS_COM_DBN, 0, dbn, sizeof (dbn))) {
381                 return sMsg (ERR_IDIOT, "%s: stbCloseDb: missing db name in rsp",
382                         that->head.name);
383         }
384         dbid = stbDbIdxFromName (that, dbn);
385         if (0 > dbid) {
386                 return sMsg (ERR_TRASH, "%s: stbCloseDb: illegal db name <%s> in rsp",
387                         that->head.name, dbn);
388         }
389         db = that->dbase + dbid;
390         luti_ltrmv (that->hsh, db->head.name);
391         cdbDtor (db);
392         return 0;
393 }
394
395 static int stbCloseDb0 (Stub that) {
396         char   dbn[DBNLEN];
397         int    dbid = rInt (that->rsp, OPENISIS_RSP_DBID, -1, 0);
398         if (0 > dbid) {
399                 return sMsg (ERR_IDIOT, "stbCloseDb0: missing dbid in rsp");
400         }
401         if (that->head.ndbs <= dbid) {
402                 return sMsg (ERR_TRASH, "stbCloseDb0: illegal dbid %d(%d) in rsp",
403                         dbid, that->head.ndbs);
404         }
405         if (! rString (that->rsp, OPENISIS_COM_DBN, 0, dbn, DBNLEN)) {
406                 return sMsg (ERR_IDIOT, "stbCloseDb0: missing db name in rsp\n");
407         }
408         log_msg (LOG_INFO, "stbCloseDb0 %d %s", dbid, dbn);
409         luti_ltrmv (that->hsh, dbn);
410         that->head.dbs[dbid] = 0;
411         return 0;
412 }
413
414 /*      (local) response callback
415 */
416 static int stbRspCb (Rec **rsp, void *cld) {
417         Stub      that;
418         Rec      *rsp2;
419         int       sid, ser, err, err2, err3;
420
421         (void)cld;
422         sid = rInt (*rsp, OPENISIS_COM_SID, -1, 0);
423         ser = rInt (*rsp, OPENISIS_COM_SER, -1, 0);
424         err = rInt (*rsp, OPENISIS_RSP_ERR, -1, 0);
425
426         that = stbById (sid);
427         if (! that) {
428                 return sMsg (ERR_TRASH, "illegal stub id %d in stbRspCb\n", sid);
429         }
430         if (ser != that->srq) {
431                 return sMsg (ERR_TRASH, "%s: rqs serial mismatch %d != %d",
432                         that->head.name, ser, that->srq);
433         }
434         if (! that->rqs) {
435                 return sMsg (ERR_TRASH, "%s/%d: response without request",
436                         that->head.name, sid);
437         }
438         if (that->rsp) {
439                 sMsg (ERR_TRASH, "%s: multiple responses",
440                         that->head.name);
441                 mFree (that->rsp);
442         }
443         that->rdb = 0;
444         that->rsp = *rsp;
445         *rsp = 0;
446
447         err2 = err3 = 0;
448
449         if (0 == err) {
450                 int rtyp = rInt (that->rqs, OPENISIS_RQS_TYPE, -1, 0);
451                 switch (rtyp) {
452
453                 case OPENISIS_RQST_LSDB:
454                 case OPENISIS_RQST_EVAL:
455                         break;
456
457                 case OPENISIS_RQST_CLOS:
458                         if (sid) {
459                                 err2 = sMsg (ERR_IDIOT, "%s: db close on remote stub",
460                                         that->head.name);
461                         }
462                         else {
463                                 err2 = stbCloseDb0 (that);
464                         }
465                         break;
466                 
467                 case OPENISIS_RQST_OPEN:
468                         if (sid) {
469                                 err2 = sMsg (ERR_IDIOT, "%s: db open on remote stub",
470                                         that->head.name);
471                                 break;
472                         }
473                         err2 = stbOpenDb0 (that);
474                         goto setdb;
475                 
476                 case OPENISIS_RQST_MNT:
477                         if (0 == sid) {
478                                 err2 = sMsg (ERR_IDIOT, "db mount on local stub");
479                                 break;
480                         }
481                         err2 = stbOpenDb (that);
482                         goto setdb;
483
484                 default:
485                 setdb: {
486                         char dbn[DBNLEN];
487                         int rdid, rx;
488                         if (! rString (that->rsp, OPENISIS_COM_DBN, 0, dbn, sizeof(dbn))) {
489                                 if (sid) {
490                                         err2 = sMsg (ERR_IDIOT,
491                                                 "%s: stbRspCb: missing db name in rsp",
492                                                 that->head.name);
493                                 }
494                                 else {
495                                         err2 = sMsg (ERR_IDIOT,
496                                                 "stbRspCb: missing db name in rsp");
497                                 }
498                         }
499                         else {
500                                 rdid = stbDbidFromName (that, dbn);
501                                 if (0 > rdid) {
502                                         if (sid) {
503                                                 err2 = sMsg (ERR_TRASH,
504                                                         "%s: stbRspCb: illegal db name <%s> in rsp",
505                                                         that->head.name, dbn);
506                                         }
507                                         else {
508                                                 err2 = sMsg (ERR_TRASH,
509                                                         "stbRspCb: illegal db name <%s> in rsp", dbn);
510                                         }
511                                 }
512                                 else {
513                                         /* see nDbById */
514                                         rx = DB_I2X (rdid);
515                                         if (! DB_IDX_OK (that, rx)) {
516                                                 if (sid) {
517                                                         err2 = sMsg (ERR_TRASH,
518                                                                 "%s: stbRspCb: illegal db id %d %d <%s>",
519                                                                 that->head.name, rdid, rx, dbn);
520                                                 }
521                                                 else {
522                                                         err2 = sMsg (ERR_TRASH,
523                                                                 "stbRspCb: illegal db id %d %d <%s>",
524                                                                 rdid, rx, dbn);
525                                                 }
526                                         }
527                                         else {
528                                                 that->rdb = that->head.dbs[rx];
529                                         }
530                                 }
531                         }
532                 }
533                 } /* switch */
534         }
535         else { /* err */
536                 int tms;
537                 tms = rInt (that->rsp, OPENISIS_COM_TMS, 0, 0);
538                 if (tms && sid) { /* remounted */
539                         err2 = stbCloseDb (that);
540                 }
541         }
542
543         rsp2 = rAddI (that->rsp, OPENISIS_RSP_CERR, err2, !0);
544         if (rsp2) {
545                 that->rsp = rsp2;
546         }
547
548         if (that->actcb) {
549                 err3 = (*that->actcb) (that->actcld,
550                         that, that->rsp, that->rdb);
551                 if (that->actcld && that->cbdta.delcb) {
552                         (*that->cbdta.delcb) (
553                                 that->cbdta.delcld, that, that->actcld);
554                 }
555                 that->actcb = 0;
556                 that->actcld = 0;
557         }
558         else if (that->cbdta.dfltcb) {
559                 err3 = (*that->cbdta.dfltcb) (that->cbdta.dfltcld,
560                         that, that->rsp, that->rdb);
561         }
562
563         sMsg (LOG_VERBOSE, "stbRspCb(%s): %d/%d err = %x,%x,%x",
564                 that->head.name, sid, ser, err, err2, err3);
565
566         return err3;
567 }
568
569 static int stbLocalRqs (Stub that, Rec *rqs) {
570         (void)that;
571         return ldspProcess (rqs, 0, &stbRspCb, 0);
572 }
573
574 static int stbSyncRqs (Stub that, Rec *rqs) {
575         Rec *rsp;
576         int  rt;
577         if (0 > that->chn.sd) {
578                 rt = cliConnect (&that->chn, that->host, that->port);
579                 if (0 > rt) {
580                         return rt;
581                 }
582         }
583         rt = cliWrite (&that->chn, rqs);
584         if (0 > rt) {
585                 return rt;
586         }
587         rsp = cliRead (&that->chn);
588         if (! rsp) {
589                 return sMsg (ERR_IO, "stbSyncRqs: read error %d - %s", 
590                         that->chn.err, strerror (that->chn.err));
591         }
592         rt = stbRspCb (&rsp, 0);
593         if (rsp) {
594                 mFree (rsp);
595         }
596         return rt;
597 }
598
599 /* ************************************************************
600         package functions
601 */
602
603 /* ************************************************************
604         public functions
605 */
606
607 Stub nInit (
608         int argc, const char **argv, OpenIsisStubCbData *dta
609 ) {
610         Rec *cfg = 0;
611
612         if (stub0) {
613                 return stub0;
614         }
615
616         StubLT = luti_ltnew ();
617         stub0 = mAlloc (STBSIZE);
618         stbClear (stub0);
619
620         if (0 < argc) {
621                 char  buf[PATH_MAX];
622                 char *logn;
623                 int   logl;
624
625                 cfg = rSet (0, RFDT | RARGV | RIGN | argc, openIsisFdtSyspar, argv);
626
627                 logn = rString (cfg, OPENISIS_SLOGF, 0, buf, sizeof(buf));
628                 logl = rInt (cfg, OPENISIS_SLOGV, -1, 0);
629                 if (logn || 0 <= logl) {
630                         cLog (logl, logn);
631                 }
632         }
633
634         stbCtor (stub0, "", 0, cfg, 0, 0, 0, dta);
635         return stub0;
636 }
637
638 Stub nOpen (
639         const char *name, int argc, const char *argv[], OpenIsisStubCbData *dta
640 ) {
641         OpenIsisStb buf;
642         Stub  that;
643         Rec  *cfg   =  0;
644         int   idx, sid;
645
646         if (! stub0 || ! name || ! *name) {
647                 return 0;
648         }
649
650         idx = luti_ltget (StubLT, name);
651         if (0 <= idx) {
652                 idx = SC_I2X (idx);
653                 if (0 > idx || NumStubs <= idx || ! * RmtStubs[idx].head.name) {
654                         log_msg (LOG_ERROR, "nOpen: illegal idx %d(%d) for %s",
655                                 idx, NumStubs, name);
656                         return 0;
657                 }
658                 return RmtStubs + idx;
659         }
660
661         idx = stbNewIdx ();
662         if (0 > idx) {
663                 return 0;
664         }
665         if (0 < argc) {
666                 cfg = rSet (0, RFDT | RARGV | RIGN | argc, openIsisFdtScheme, argv);
667         }
668         sid = SC_X2I (idx);
669         stbClear (&buf);
670         if (! stbCtor (&buf, name, sid, cfg, 0, 0, 0, dta)) {
671                 if (cfg) {
672                         mFree (cfg);
673                 }
674                 return 0;
675         }
676
677         that = RmtStubs + idx;
678         memcpy (that, &buf, STBSIZE);
679         return that;
680 }
681
682 void nClose (Stub that) {
683         if (that && *that->head.name) {
684                 stbDtor (that);
685         }
686 }
687
688 void nDeinit () {
689         if (stub0) {
690                 int j;
691                 if (RmtStubs) {
692                         for (j = NumStubs; 0 <= --j;  ) {
693                                 if (* RmtStubs[j].head.name) {
694                                         stbDtor (RmtStubs + j);
695                                 }
696                         }
697                         mFree (RmtStubs);
698                         RmtStubs = 0;
699                         NumStubs = 0;
700                 }
701                 for (j = stub0->head.ndbs; 0 <= --j;  ) {
702                         if (stub0->head.dbs[j]) {
703                                 cDClose (stub0->head.dbs[j]->dbid);
704                         }
705                 }
706                 stbDtor (stub0);
707                 mFree (stub0);
708                 stub0 = 0;
709                 luti_ltdel (StubLT);
710                 StubLT = 0;
711         }
712 }
713
714 Schema* nSchema (Stub that) {
715         int idx;
716         if (that && ( stub0 == that || ( *that->head.name &&
717                 0 <= (idx = SC_I2X (that->head.scid)) && NumStubs > idx
718         ))) {
719                 return &that->head;
720         }
721         return 0;
722 }
723
724 void nClean (Stub that) {
725         if (that->rqs) {
726                 mFree (that->rqs);
727                 that->rqs = 0;
728         }
729         if (that->rsp) {
730                 mFree (that->rsp);
731                 that->rsp = 0;
732         }
733         that->rdb = 0;
734 }
735
736 int nSend (Stub that,
737         Rec *rqs, OpenIsisRspCb *actcb, void *actcld, int dup
738 ) {
739         char  buf[DBNLEN];
740         char  sid[16];
741         char  ser[16];
742         char  tms[16];
743         char *dbl, *dbn;
744         int   dbid, rtyp, setn;
745
746         if (!(that && rqs)) {
747                 return sMsg (ERR_IDIOT, "nSend: null pointer %x %x",
748                         (int)that, (int)rqs);
749         }
750         if (stub0 != that && ! *that->head.name) {
751                 return sMsg (ERR_IDIOT, "nSend: invalid stub");
752         }
753         setn = !0;
754         rtyp = rInt (rqs, OPENISIS_RQS_TYPE, -1, 0);
755         switch (rtyp) {
756         case -1:
757                 return sMsg (ERR_IDIOT, "nSend(%s): request without type",
758                         that->head.name);
759         case OPENISIS_RQST_LSDB:
760         case OPENISIS_RQST_EVAL:
761                 setn = 0;
762                 break;
763         case OPENISIS_RQST_OPEN:
764         case OPENISIS_RQST_CLOS:
765                 if (stub0 != that) {
766                         return sMsg (ERR_IDIOT, "nSend(%s): request %d denied",
767                                 that->head.name, rtyp);
768                 }
769                 break;
770         case OPENISIS_RQST_MNT:
771                 if (stub0 == that) {
772                         return sMsg (ERR_IDIOT, "nSend: local mount denied");
773                 }
774                 rqs = rSet (rqs, RFDT | RDIS | RCHG, openIsisFdtRqs,
775                         OPENISIS_COM_TMS, "0", 0);
776                 if (! rqs) {
777                         return sMsg (ERR_NOMEM, "nSend(%s): out of memory",
778                                 that->head.name);
779                 }
780                 break;
781         }
782
783         dbid = -1;
784         dbn = 0;
785         dbl = that->rdb ? that->rdb->name : 0;
786         nClean (that);
787
788         i2a (sid, that->head.scid);
789         if (0 >= ++that->srq) {
790                 that->srq = 1;
791                 log_msg (LOG_INFO, "stub %s: srq loop over 0", that->head.name);
792         }
793         i2a (ser, that->srq);
794
795         if (setn) {
796                 if ((dbn = rString (rqs, OPENISIS_COM_DBN, 0, buf, DBNLEN))) {
797                         setn = 0;
798                 }
799                 else {
800                         if (dbl) {
801                                 dbn = dbl;
802                         }
803                         else {
804                                 dbn = rString (that->head.cfg,
805                                         OPENISIS_SC_DFLTDB, 0, buf, DBNLEN);
806                         }
807                 }
808                 if (! dbn) {
809                         return sMsg (ERR_IDIOT, "nSend(%s): request without db name",
810                                 that->head.name);
811                 }
812                 if (0 > rInt (rqs, OPENISIS_COM_TMS, -1, 0)) {
813                         dbid = stbDbIdxFromName (that, dbn);
814                         if (0 <= dbid) {
815                                 i2a (tms, that->head.dbs[dbid]->tms);
816                         }
817                 }
818         }
819
820         if (dup) {
821                 rqs = rDup (rqs, 0, 0);
822                 if (! rqs) {
823                         return -1;
824                 }
825         }
826
827         rqs = rSet (rqs, RFDT | RDIS | RCHG, openIsisFdtRqs,
828                 OPENISIS_COM_SID, sid, OPENISIS_COM_SER, ser,
829                 0 <= dbid ? OPENISIS_COM_TMS : 0, tms, 0);
830         if (! rqs) {
831                 return sMsg (ERR_NOMEM, "nSend(%s): out of memory",
832                         that->head.name);
833         }
834         if (setn) {
835                 rqs = rSet (rqs, RFDT | RDIS | RCHG, openIsisFdtRqs,
836                         OPENISIS_COM_DBN, dbn, 0);
837                 if (! rqs) {
838                         return sMsg (ERR_NOMEM, "nSend(%s): out of memory",
839                                 that->head.name);
840                 }
841         }
842
843         that->rqs = rqs;
844
845         if (that->actcld && that->cbdta.delcb) {
846                 /* ooups */
847                 (*that->cbdta.delcb) (
848                         that->cbdta.delcld, that, that->actcld);
849         }
850         if (actcb) {
851                 that->actcb = actcb;
852                 that->actcld = actcld;
853         }
854         else {
855                 that->actcb = 0;
856                 that->actcld = 0;
857         }
858
859         return (*that->rqsp) (that, that->rqs);
860 }
861
862 Rec* nRecv (Stub that, Db **db) {
863         if (that && (
864                 stub0 == that || *that->head.name
865         )) {
866                 if (db) {
867                         *db = that->rdb;
868                 }
869                 return that->rsp;
870         }
871         return 0;
872 }
873
874 Db* nDbByName (Stub that, const char *dbname) {
875         if (that && (
876                 stub0 == that || *that->head.name
877         )) {
878                 int dbid = stbDbIdxFromName (that, dbname);
879                 if (0 <= dbid) {
880                         return that->head.dbs[dbid];
881                 }
882         }
883         return 0;
884 }
885
886 Db* nDbById (int id) {
887         Stub that = stbById (id & OPENISIS_SCIDMSK);
888         if (! that) {
889                 return 0;
890         }
891         id = DB_I2X (id);
892         if (! DB_IDX_OK (that, id)) {
893                 return 0;
894         }
895         return that->head.dbs[id];
896 }
897
898 #define FREEPTR(p) if (p) { mFree (p); p = 0; }
899 #define FREEROW    if (rowids) { FREEPTR(*rowids) }
900
901 int nResult (
902         Stub that, int **rowids, Rec ***recs, Db **db, int *tot
903 ) {
904         Field     *F;
905         Rec      **R;
906         int       *I;
907         int        numt, numr, err, pos, j;
908
909         if (rowids) {
910                 *rowids = 0;
911         }
912         if (recs) {
913                 *recs = 0;
914         }
915         if (db) {
916                 *db = 0;
917         }
918         if (tot) {
919                 *tot = 0;
920         }
921         if (! that) {
922                 return sMsg (ERR_IDIOT, "nGetResult: null pointer");
923         }
924         if (! that->rsp) {
925                 return sMsg (ERR_IDIOT, "nGetResult(%s): no response",
926                         that->head.name);
927         }
928         err = rInt (that->rsp, OPENISIS_RSP_ERR, 0, 0);
929         if (err) {
930                 return sMsg (ERR_IDIOT, "nGetResult(%s): response error %d",
931                         that->head.name, err);
932         }
933         numr = rInt (that->rsp, OPENISIS_RSP_NUMR, -1, 0);
934         numt = rInt (that->rsp, OPENISIS_RSP_NUMT, -1, 0);
935         if (0 > numr || 0 > numt) {
936                 return sMsg (ERR_IDIOT, "nGetResult(%s): no record count in response",
937                         that->head.name);
938         }
939         if (numr) {
940                 if (rowids) {
941                         *rowids = (int*) mAlloc (numr * sizeof (int));
942                         if (! *rowids) {
943                                 return sMsg (ERR_NOMEM, "nGetResult(%s): out of memory",
944                                         that->head.name);
945                         }
946                         for (I = *rowids, j = pos = 0; numr > j; ++I, ++j) {
947                                 *I = rInt (that->rsp, OPENISIS_COM_ROW, -1, &pos);
948                                 if (0 > *I) {
949                                         FREEROW;
950                                         return sMsg (ERR_TRASH,
951                                                 "nGetResult(%s): missing rowid %d(%d)",
952                                                 that->head.name, j, numr);
953                                 }
954                         }
955                 }
956
957                 if (recs) {
958                         *recs = (Rec**) mAlloc (numr * sizeof (Rec*));
959                         if (! *recs) {
960                                 FREEROW;
961                                 return sMsg (ERR_NOMEM, "nGetResult(%s): out of memory",
962                                         that->head.name);
963                         }
964                         memset (*recs, 0, numr * sizeof (Rec*));
965                         R = *recs;
966                         I = rowids ? *rowids : 0;
967                         j = pos = 0;
968                         F = rGet (that->rsp, OPENISIS_COM_REC, &pos);
969                         if (! F) {
970                                 sMsg (LOG_WARN,
971                                         "nGetResult(%s): no records in response",
972                                         that->head.name);
973                                 FREEPTR (*recs);
974                         }
975                         else {
976                                 --pos;
977                                 while (numr > j) {
978                                         *R = luti_unwrap (that->rsp, &pos, OPENISIS_COM_REC,
979                                                 that->rdb ? that->rdb->dbid : -1);
980                                         if (! *R) {
981                                                 FREEROW;
982                                                 luti_free ((void**)*recs, j);
983                                                 *recs = 0;
984                                                 return sMsg (ERR_NOMEM,
985                                                         "nGetResult(%s): out of memory", that->head.name);
986                                         }
987                                         if (I) {
988                                                 (*R)->rowid = *I;
989                                                 ++I;
990                                         }
991                                         ++R;
992                                         ++j;
993                                 }
994                         }
995                 } /* recs */
996
997         } /* numr */
998
999         if (db) {
1000                 *db = that->rdb;
1001         }
1002         if (tot) {
1003                 *tot = numt;
1004         }
1005
1006         return numr;
1007 }
1008