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