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