436de77d4b1fb843338c3c99d475c59bb6a28f6c
[yaz-moved-to-github.git] / server / seshigh.c
1 /*
2  * Copyright (C) 1994, Index Data I/S 
3  * All rights reserved.
4  * Sebastian Hammer, Adam Dickmeiss
5  *
6  * $Log: seshigh.c,v $
7  * Revision 1.16  1995-03-31 09:18:55  quinn
8  * Added logging.
9  *
10  * Revision 1.15  1995/03/30  14:03:23  quinn
11  * Added RFC1006 as separate library
12  *
13  * Revision 1.14  1995/03/30  12:18:17  quinn
14  * Fixed bug.
15  *
16  * Revision 1.13  1995/03/30  09:09:24  quinn
17  * Added state-handle and some support for asynchronous activities.
18  *
19  * Revision 1.12  1995/03/29  15:40:16  quinn
20  * Ongoing work. Statserv is now dynamic by default
21  *
22  * Revision 1.11  1995/03/28  09:16:21  quinn
23  * Added record packing to the search request
24  *
25  * Revision 1.10  1995/03/27  08:34:24  quinn
26  * Added dynamic server functionality.
27  * Released bindings to session.c (is now redundant)
28  *
29  * Revision 1.9  1995/03/22  15:01:26  quinn
30  * Adjusting record packing.
31  *
32  * Revision 1.8  1995/03/22  10:13:21  quinn
33  * Working on record packer
34  *
35  * Revision 1.7  1995/03/21  15:53:31  quinn
36  * Little changes.
37  *
38  * Revision 1.6  1995/03/21  12:30:09  quinn
39  * Beginning to add support for record packing.
40  *
41  * Revision 1.5  1995/03/17  10:44:13  quinn
42  * Added catch of null-string in makediagrec
43  *
44  * Revision 1.4  1995/03/17  10:18:08  quinn
45  * Added memory management.
46  *
47  * Revision 1.3  1995/03/16  17:42:39  quinn
48  * Little changes
49  *
50  * Revision 1.2  1995/03/16  13:29:01  quinn
51  * Partitioned server.
52  *
53  * Revision 1.1  1995/03/15  16:02:10  quinn
54  * Modded session.c to seshigh.c
55  *
56  */
57
58 #include <stdlib.h>
59 #include <assert.h>
60
61 #include <comstack.h>
62 #include <eventl.h>
63 #include <session.h>
64 #include <proto.h>
65 #include <oid.h>
66 #include <iso2709.h>
67 #include <log.h>
68
69 #include <backend.h>
70
71 #define ENCODE_BUFFER_SIZE 10000
72
73 static int process_apdu(IOCHAN chan);
74 static int process_initRequest(IOCHAN client, Z_InitRequest *req);
75 static int process_searchRequest(IOCHAN client, Z_SearchRequest *req);
76 static int process_presentRequest(IOCHAN client, Z_PresentRequest *req);
77
78 association *create_association(IOCHAN channel, COMSTACK link)
79 {
80     association *new;
81
82     if (!(new = malloc(sizeof(*new))))
83         return 0;
84     new->client_chan = channel;
85     new->client_link = link;
86     if (!(new->decode = odr_createmem(ODR_DECODE)) ||
87         !(new->encode = odr_createmem(ODR_ENCODE)))
88         return 0;
89     if (!(new->encode_buffer = malloc(ENCODE_BUFFER_SIZE)))
90         return 0;
91     odr_setbuf(new->encode, new->encode_buffer, ENCODE_BUFFER_SIZE);
92     new->state = ASSOC_UNINIT;
93     new->input_buffer = 0;
94     new->input_buffer_len = 0;
95     new->backend = 0;
96     if (cs_getproto(link) == CS_Z3950)
97         new->proto = PROTO_Z3950;
98     else
99         new->proto = PROTO_SR;
100     return new;
101 }
102
103 void destroy_association(association *h)
104 {
105     odr_destroy(h->decode);
106     odr_destroy(h->encode);
107     free(h->encode_buffer);
108     if (h->input_buffer)
109         free(h->input_buffer);
110     if (h->backend)
111         bend_close(h->backend);
112     free(h);
113 }
114
115 void ir_session(IOCHAN h, int event)
116 {
117     int res;
118     association *assoc = iochan_getdata(h);
119     COMSTACK conn = assoc->client_link;
120
121     if (event == EVENT_INPUT)
122     {
123         assert(assoc && conn);
124         res = cs_get(conn, &assoc->input_buffer, &assoc->input_buffer_len);
125         switch (res)
126         {
127             case 0: case -1: /* connection closed by peer */
128                 logf(LOG_LOG, "Connection closed by client");
129                 cs_close(conn);
130                 destroy_association(assoc);
131                 iochan_destroy(h);
132                 return;
133             case 1:  /* incomplete read */
134                 return;
135             default: /* data! */
136                 assoc->input_apdu_len = res;
137                 if (process_apdu(h) < 0)
138                 {
139                     cs_close(conn);
140                     destroy_association(assoc);
141                     iochan_destroy(h);
142                 }
143                 else if (cs_more(conn)) /* arrange to be called again */
144                     iochan_setevent(h, EVENT_INPUT);
145         }
146     }
147     else if (event == EVENT_OUTPUT)
148     {
149         switch (res = cs_put(conn, assoc->encode_buffer, assoc->encoded_len))
150         {
151             case -1:
152                 logf(LOG_LOG, "Connection closed by client");
153                 cs_close(conn);
154                 destroy_association(assoc);
155                 iochan_destroy(h);
156             case 0: /* all sent */
157                 iochan_setflags(h, EVENT_INPUT | EVENT_EXCEPT); /* reset */
158                 break;
159             case 1: /* partial send */
160                 break; /* we'll get called again */
161         }
162     }
163     else if (event == EVENT_EXCEPT)
164     {
165         logf(LOG_LOG, "Exception on line");
166         cs_close(conn);
167         destroy_association(assoc);
168         iochan_destroy(h);
169     }
170 }
171
172 static int process_apdu(IOCHAN chan)
173 {
174     Z_APDU *apdu;
175     int res;
176     association *assoc = iochan_getdata(chan);
177
178     odr_setbuf(assoc->decode, assoc->input_buffer, assoc->input_apdu_len);
179     if (!z_APDU(assoc->decode, &apdu, 0))
180     {
181         logf(LOG_WARN, "ODR error: %s",
182             odr_errlist[odr_geterror(assoc->decode)]);
183         return -1;
184     }
185     switch (apdu->which)
186     {
187         case Z_APDU_initRequest:
188             res = process_initRequest(chan, apdu->u.initRequest); break;
189         case Z_APDU_searchRequest:
190             res = process_searchRequest(chan, apdu->u.searchRequest); break;
191         case Z_APDU_presentRequest:
192             res = process_presentRequest(chan, apdu->u.presentRequest); break;
193         default:
194             logf(LOG_WARN, "Bad APDU");
195             return -1;
196     }
197     odr_reset(assoc->decode); /* release incopming APDU */
198     odr_reset(assoc->encode); /* release stuff alloced before encoding */
199     return res;
200 }
201
202 static int process_initRequest(IOCHAN client, Z_InitRequest *req)
203 {
204     Z_APDU apdu, *apdup;
205     Z_InitResponse resp;
206     bool_t result = 1;
207     association *assoc = iochan_getdata(client);
208     bend_initrequest binitreq;
209     bend_initresult *binitres;
210     Odr_bitmask options, protocolVersion;
211
212     logf(LOG_LOG, "Got initRequest");
213     if (req->implementationId)
214         logf(LOG_LOG, "Id:        %s", req->implementationId);
215     if (req->implementationName)
216         logf(LOG_LOG, "Name:      %s", req->implementationName);
217     if (req->implementationVersion)
218         logf(LOG_LOG, "Version:   %s", req->implementationVersion);
219
220     binitreq.configname = "default-config";
221     if (!(binitres = bend_init(&binitreq)) || binitres->errcode)
222     {
223         logf(LOG_WARN, "Bad response from backend");
224         return -1;
225     }
226
227     assoc->backend = binitres->handle;
228     apdup = &apdu;
229     apdu.which = Z_APDU_initResponse;
230     apdu.u.initResponse = &resp;
231     resp.referenceId = req->referenceId;
232     ODR_MASK_ZERO(&options);
233     if (ODR_MASK_GET(req->options, Z_Options_search))
234         ODR_MASK_SET(&options, Z_Options_search);
235     if (ODR_MASK_GET(req->options, Z_Options_present))
236         ODR_MASK_SET(&options, Z_Options_present);
237     if (ODR_MASK_GET(req->options, Z_Options_delSet))
238         ODR_MASK_SET(&options, Z_Options_delSet);
239     if (ODR_MASK_GET(req->options, Z_Options_namedResultSets))
240         ODR_MASK_SET(&options, Z_Options_namedResultSets);
241     resp.options = &options;
242     ODR_MASK_ZERO(&protocolVersion);
243     if (ODR_MASK_GET(req->protocolVersion, Z_ProtocolVersion_1))
244         ODR_MASK_SET(&protocolVersion, Z_ProtocolVersion_1);
245     if (ODR_MASK_GET(req->protocolVersion, Z_ProtocolVersion_2))
246         ODR_MASK_SET(&protocolVersion, Z_ProtocolVersion_2);
247     resp.protocolVersion = &protocolVersion;
248     assoc->maximumRecordSize = *req->maximumRecordSize;
249     /*
250      * This is not so hot. The big todo for ODR is dynamic memory allocation
251      * on encoding.
252      */
253     if (assoc->maximumRecordSize > ENCODE_BUFFER_SIZE - 1000)
254         assoc->maximumRecordSize = ENCODE_BUFFER_SIZE - 1000;
255     assoc->preferredMessageSize = *req->preferredMessageSize;
256     if (assoc->preferredMessageSize > assoc->maximumRecordSize)
257         assoc->preferredMessageSize = assoc->maximumRecordSize;
258     resp.preferredMessageSize = &assoc->preferredMessageSize;
259     resp.maximumRecordSize = &assoc->maximumRecordSize;
260     resp.result = &result;
261     resp.implementationId = "YAZ";
262     resp.implementationName = "Index Data/YAZ Generic Frontend Server";
263     resp.implementationVersion = "$Revision: 1.16 $";
264     resp.userInformationField = 0;
265     if (!z_APDU(assoc->encode, &apdup, 0))
266     {
267         logf(LOG_FATAL, "ODR error encoding initres: %s",
268             odr_errlist[odr_geterror(assoc->encode)]);
269         return -1;
270     }
271     odr_getbuf(assoc->encode, &assoc->encoded_len);
272     odr_reset(assoc->encode);
273     iochan_setflags(client, EVENT_OUTPUT | EVENT_EXCEPT);
274     return 0;
275 }
276
277 static Z_Records *diagrec(oid_proto proto, int error, char *addinfo)
278 {
279     static Z_Records rec;
280     oident bib1;
281     static Z_DiagRec dr;
282     static int err;
283
284     bib1.proto = proto;
285     bib1.class = CLASS_DIAGSET;
286     bib1.value = VAL_BIB1;
287
288     logf(LOG_DEBUG, "Diagnostic: %d -- %s", error, addinfo ? addinfo :
289         "NULL");
290     err = error;
291     rec.which = Z_Records_NSD;
292     rec.u.nonSurrogateDiagnostic = &dr;
293     dr.diagnosticSetId = oid_getoidbyent(&bib1);
294     dr.condition = &err;
295     dr.addinfo = addinfo ? addinfo : "";
296     return &rec;
297 }
298
299 static Z_NamePlusRecord *surrogatediagrec(oid_proto proto, char *dbname,
300                                             int error, char *addinfo)
301 {
302     static Z_NamePlusRecord rec;
303     static Z_DiagRec dr;
304     static int err;
305     oident bib1;
306
307     bib1.proto = proto;
308     bib1.class = CLASS_DIAGSET;
309     bib1.value = VAL_BIB1;
310
311     logf(LOG_DEBUG, "SurrogateDiagnotic: %d -- %s", error, addinfo);
312     err = error;
313     rec.databaseName = dbname;
314     rec.which = Z_NamePlusRecord_surrogateDiagnostic;
315     rec.u.surrogateDiagnostic = &dr;
316     dr.diagnosticSetId = oid_getoidbyent(&bib1);
317     dr.condition = &err;
318     dr.addinfo = addinfo ? addinfo : "";
319     return &rec;
320 }
321
322 #define MAX_RECORDS 256
323
324 static Z_Records *pack_records(association *a, char *setname, int start,
325                                 int *num, Z_ElementSetNames *esn,
326                                 int *next, int *pres)
327 {
328     int recno, total_length = 0, toget = *num;
329     static Z_Records records;
330     static Z_NamePlusRecordList reclist;
331     static Z_NamePlusRecord *list[MAX_RECORDS];
332     oident recform;
333     Odr_oid *oid;
334
335     records.which = Z_Records_DBOSD;
336     records.u.databaseOrSurDiagnostics = &reclist;
337     reclist.num_records = 0;
338     reclist.records = list;
339     *pres = Z_PRES_SUCCESS;
340     *num = 0;
341     *next = 0;
342
343     recform.proto = a->proto;
344     recform.class = CLASS_RECSYN;
345     recform.value = VAL_USMARC;
346     if (!(oid = odr_oiddup(a->encode, oid_getoidbyent(&recform))))
347         return 0;
348
349     logf(LOG_DEBUG, "Request to pack %d+%d", start, toget);
350     logf(LOG_DEBUG, "pms=%d, mrs=%d", a->preferredMessageSize,
351         a->maximumRecordSize);
352     for (recno = start; reclist.num_records < toget; recno++)
353     {
354         bend_fetchrequest freq;
355         bend_fetchresult *fres;
356         Z_NamePlusRecord *thisrec;
357         Z_DatabaseRecord *thisext;
358
359         if (reclist.num_records == MAX_RECORDS - 1)
360         {
361             *pres = Z_PRES_PARTIAL_2;
362             break;
363         }
364         freq.setname = setname;
365         freq.number = recno;
366         if (!(fres = bend_fetch(a->backend, &freq, 0)))
367         {
368             *pres = Z_PRES_FAILURE;
369             return diagrec(a->proto, 2, "Backend interface problem");
370         }
371         /* backend should be able to signal whether error is system-wide
372            or only pertaining to current record */
373         if (fres->errcode)
374         {
375             *pres = Z_PRES_FAILURE;
376             return diagrec(a->proto, fres->errcode, fres->errstring);
377         }
378         logf(LOG_DEBUG, "  Got record, len=%d, total=%d",
379             fres->len, total_length);
380         if (fres->len + total_length > a->preferredMessageSize)
381         {
382             /* record is small enough, really */
383             if (fres->len <= a->preferredMessageSize)
384             {
385                 logf(LOG_DEBUG, "  Dropped last normal-sized record");
386                 *pres = Z_PRES_PARTIAL_2;
387                 break;
388             }
389             /* record can only be fetched by itself */
390             if (fres->len < a->maximumRecordSize)
391             {
392                 logf(LOG_DEBUG, "  Record > prefmsgsz");
393                 if (toget > 1)
394                 {
395                     logf(LOG_DEBUG, "  Dropped it");
396                     reclist.records[reclist.num_records] =
397                          surrogatediagrec(a->proto, fres->basename, 16, 0);
398                     reclist.num_records++;
399                     *pres = Z_PRES_PARTIAL_2;
400                     break;
401                 }
402             }
403             else /* too big entirely */
404             {
405                 logf(LOG_DEBUG, "Record > maxrcdsz");
406                 reclist.records[reclist.num_records] =
407                     surrogatediagrec(a->proto, fres->basename, 17, 0);
408                 reclist.num_records++;
409                 *pres = Z_PRES_PARTIAL_2;
410                 break;
411             }
412         }
413         if (!(thisrec = odr_malloc(a->encode, sizeof(*thisrec))))
414             return 0;
415         if (!(thisrec->databaseName = odr_malloc(a->encode,
416             strlen(fres->basename) + 1)))
417             return 0;
418         strcpy(thisrec->databaseName, fres->basename);
419         thisrec->which = Z_NamePlusRecord_databaseRecord;
420         if (!(thisrec->u.databaseRecord = thisext =  odr_malloc(a->encode,
421             sizeof(Z_DatabaseRecord))))
422             return 0;
423         thisext->direct_reference = oid; /* should be OID for current MARC */
424         thisext->indirect_reference = 0;
425         thisext->descriptor = 0;
426         thisext->which = ODR_EXTERNAL_octet;
427         if (!(thisext->u.octet_aligned = odr_malloc(a->encode,
428             sizeof(Odr_oct))))
429             return 0;
430         if (!(thisext->u.octet_aligned->buf = odr_malloc(a->encode, fres->len)))
431             return 0;
432         memcpy(thisext->u.octet_aligned->buf, fres->record, fres->len);
433         thisext->u.octet_aligned->len = thisext->u.octet_aligned->size =
434             fres->len;
435         reclist.records[reclist.num_records] = thisrec;
436         reclist.num_records++;
437         total_length += fres->len;
438         (*num)++;
439         *next = fres->last_in_set ? 0 : recno + 1;
440     }
441     return &records;
442 }
443
444 static int process_searchRequest(IOCHAN client, Z_SearchRequest *req)
445 {
446     Z_APDU apdu, *apdup;
447     Z_SearchResponse resp;
448     association *assoc = iochan_getdata(client);
449     int nulint = 0;
450     bool_t sr = 1;
451     bend_searchrequest bsrq;
452     bend_searchresult *bsrt;
453     oident *oent;
454     int next = 0;
455     static int none = Z_RES_NONE;
456
457     logf(LOG_LOG, "Got SearchRequest.");
458     apdup = &apdu;
459     apdu.which = Z_APDU_searchResponse;
460     apdu.u.searchResponse = &resp;
461     resp.referenceId = req->referenceId;
462
463     resp.records = 0;
464     if (req->query->which == Z_Query_type_1)
465     {
466         Z_RPNQuery *q = req->query->u.type_1;
467
468         if (!(oent = oid_getentbyoid(q->attributeSetId)) ||
469             oent->class != CLASS_ATTSET ||
470             oent->value != VAL_BIB1)
471         {
472             resp.records = diagrec(assoc->proto, 121, 0);
473             resp.resultCount = &nulint;
474             resp.numberOfRecordsReturned = &nulint;
475             resp.nextResultSetPosition = &nulint;
476             resp.searchStatus = &none;
477             resp.resultSetStatus = 0;
478             resp.presentStatus = 0;
479         }
480     }
481     if (!resp.records)
482     {
483         int toget;
484         Z_ElementSetNames *setnames;
485         int presst = 0;
486
487         bsrq.setname = req->resultSetName;
488         bsrq.replace_set = *req->replaceIndicator;
489         bsrq.num_bases = req->num_databaseNames;
490         bsrq.basenames = req->databaseNames;
491         bsrq.query = req->query;
492
493         if (!(bsrt = bend_search(assoc->backend, &bsrq, 0)))
494             return -1;
495         else if (bsrt->errcode)
496         {
497
498             resp.records = diagrec(assoc->proto, bsrt->errcode,
499                 bsrt->errstring);
500             resp.resultCount = &nulint;
501             resp.numberOfRecordsReturned = &nulint;
502             resp.nextResultSetPosition = &nulint;
503             resp.searchStatus = &nulint;
504             resp.resultSetStatus = &none;
505             resp.presentStatus = 0;
506         }
507         else
508         {
509             resp.records = 0;
510
511             resp.resultCount = &bsrt->hits;
512
513             /* how many records does the user agent want, then? */
514             if (bsrt->hits <= *req->smallSetUpperBound)
515             {
516                 toget = bsrt->hits;
517                 setnames = req->smallSetElementSetNames;
518             }
519             else if (bsrt->hits < *req->largeSetLowerBound)
520             {
521                 toget = *req->mediumSetPresentNumber;
522                 setnames = req->mediumSetElementSetNames;
523             }
524             else
525                 toget = 0;
526
527             if (toget && !resp.records)
528             {
529                 resp.records = pack_records(assoc, req->resultSetName, 1,
530                     &toget, setnames, &next, &presst);
531                 if (!resp.records)
532                     return -1;
533                 resp.numberOfRecordsReturned = &toget;
534                 resp.nextResultSetPosition = &next;
535                 resp.searchStatus = &sr;
536                 resp.resultSetStatus = 0;
537                 resp.presentStatus = &presst;
538             }
539             else
540             {
541                 resp.numberOfRecordsReturned = &nulint;
542                 resp.nextResultSetPosition = &next;
543                 resp.searchStatus = &sr;
544                 resp.resultSetStatus = 0;
545                 resp.presentStatus = 0;
546             }
547         }
548     }
549
550     if (!z_APDU(assoc->encode, &apdup, 0))
551     {
552         logf(LOG_FATAL, "ODR error encoding searchres: %s",
553             odr_errlist[odr_geterror(assoc->encode)]);
554         return -1;
555     }
556     odr_getbuf(assoc->encode, &assoc->encoded_len);
557     odr_reset(assoc->encode);
558     iochan_setflags(client, EVENT_OUTPUT | EVENT_EXCEPT);
559     return 0;
560 }
561
562 static int process_presentRequest(IOCHAN client, Z_PresentRequest *req)
563 {
564     Z_APDU apdu, *apdup;
565     Z_PresentResponse resp;
566     association *assoc = iochan_getdata(client);
567     int presst, next, num;
568
569     logf(LOG_LOG, "Got PresentRequest.");
570     apdup = &apdu;
571     apdu.which = Z_APDU_presentResponse;
572     apdu.u.presentResponse = &resp;
573     resp.referenceId = req->referenceId;
574
575     num = *req->numberOfRecordsRequested;
576     resp.records = pack_records(assoc, req->resultSetId,
577         *req->resultSetStartPoint, &num, req->elementSetNames, &next, &presst);
578     if (!resp.records)
579         return -1;
580     resp.numberOfRecordsReturned = &num;
581     resp.presentStatus = &presst;
582     resp.nextResultSetPosition = &next;
583
584     if (!z_APDU(assoc->encode, &apdup, 0))
585     {
586         logf(LOG_FATAL, "ODR error encoding initres: %s",
587             odr_errlist[odr_geterror(assoc->encode)]);
588         return -1;
589     }
590     odr_getbuf(assoc->encode, &assoc->encoded_len);
591     odr_reset(assoc->encode);
592     iochan_setflags(client, EVENT_OUTPUT | EVENT_EXCEPT);
593     return 0;
594 }