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