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