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