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