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