e3555582bd5708a31e3ef028a11efbf2f124d566
[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.21  1995-05-02 08:53:19  quinn
8  * Trying in vain to fix comm with ISODE
9  *
10  * Revision 1.20  1995/04/20  15:13:00  quinn
11  * Cosmetic
12  *
13  * Revision 1.19  1995/04/18  08:15:34  quinn
14  * Added dynamic memory allocation on encoding (whew). Code is now somewhat
15  * neater. We'll make the same change for decoding one day.
16  *
17  * Revision 1.18  1995/04/17  11:28:25  quinn
18  * Smallish
19  *
20  * Revision 1.17  1995/04/10  10:23:36  quinn
21  * Some work to add scan and other things.
22  *
23  * Revision 1.16  1995/03/31  09:18:55  quinn
24  * Added logging.
25  *
26  * Revision 1.15  1995/03/30  14:03:23  quinn
27  * Added RFC1006 as separate library
28  *
29  * Revision 1.14  1995/03/30  12:18:17  quinn
30  * Fixed bug.
31  *
32  * Revision 1.13  1995/03/30  09:09:24  quinn
33  * Added state-handle and some support for asynchronous activities.
34  *
35  * Revision 1.12  1995/03/29  15:40:16  quinn
36  * Ongoing work. Statserv is now dynamic by default
37  *
38  * Revision 1.11  1995/03/28  09:16:21  quinn
39  * Added record packing to the search request
40  *
41  * Revision 1.10  1995/03/27  08:34:24  quinn
42  * Added dynamic server functionality.
43  * Released bindings to session.c (is now redundant)
44  *
45  * Revision 1.9  1995/03/22  15:01:26  quinn
46  * Adjusting record packing.
47  *
48  * Revision 1.8  1995/03/22  10:13:21  quinn
49  * Working on record packer
50  *
51  * Revision 1.7  1995/03/21  15:53:31  quinn
52  * Little changes.
53  *
54  * Revision 1.6  1995/03/21  12:30:09  quinn
55  * Beginning to add support for record packing.
56  *
57  * Revision 1.5  1995/03/17  10:44:13  quinn
58  * Added catch of null-string in makediagrec
59  *
60  * Revision 1.4  1995/03/17  10:18:08  quinn
61  * Added memory management.
62  *
63  * Revision 1.3  1995/03/16  17:42:39  quinn
64  * Little changes
65  *
66  * Revision 1.2  1995/03/16  13:29:01  quinn
67  * Partitioned server.
68  *
69  * Revision 1.1  1995/03/15  16:02:10  quinn
70  * Modded session.c to seshigh.c
71  *
72  */
73
74 #include <stdlib.h>
75 #include <stdio.h>
76 #include <unistd.h>
77 #include <assert.h>
78
79 #include <comstack.h>
80 #include <eventl.h>
81 #include <session.h>
82 #include <proto.h>
83 #include <oid.h>
84 #include <log.h>
85
86 #include <backend.h>
87
88 #define MAXRECORDSIZE 1024*1024*5 /* should be configurable, of course */
89
90 static int process_apdu(IOCHAN chan);
91 static int process_initRequest(IOCHAN client, Z_InitRequest *req);
92 static int process_searchRequest(IOCHAN client, Z_SearchRequest *req);
93 static int process_presentRequest(IOCHAN client, Z_PresentRequest *req);
94 static int process_scanRequest(IOCHAN client, Z_ScanRequest *req);
95
96 extern int dynamic;
97 extern char *apdufile;
98
99 static FILE *apduf = 0; /* for use in static mode */
100
101 /*
102  * Create a new association-handle.
103  *  channel  : iochannel for the current line.
104  *  link     : communications channel.
105  * Returns: 0 or a new association handle.
106  */
107 association *create_association(IOCHAN channel, COMSTACK link)
108 {
109     association *new;
110
111     if (!(new = malloc(sizeof(*new))))
112         return 0;
113     new->client_chan = channel;
114     new->client_link = link;
115     if (!(new->decode = odr_createmem(ODR_DECODE)) ||
116         !(new->encode = odr_createmem(ODR_ENCODE)))
117         return 0;
118     if (apdufile)
119     {
120         char filename[256];
121         FILE *f;
122
123         if (!(new->print = odr_createmem(ODR_PRINT)))
124             return 0;
125         if (*apdufile)
126         {
127             strcpy(filename, apdufile);
128             if (!dynamic)
129             {
130                 if (!apduf)
131                 {
132                     if (!(apduf = fopen(filename, "w")))
133                     {
134                         logf(LOG_WARN|LOG_ERRNO, "%s", filename);
135                         return 0;
136                     }
137                     setvbuf(apduf, 0, _IONBF, 0);
138                 }
139                 f = apduf;
140             }
141             else 
142             {
143                 sprintf(filename + strlen(filename), ".%d", getpid());
144                 if (!(f = fopen(filename, "w")))
145                 {
146                     logf(LOG_WARN|LOG_ERRNO, "%s", filename);
147                     return 0;
148                 }
149                 setvbuf(f, 0, _IONBF, 0);
150             }
151             odr_setprint(new->print, f);
152         }
153     }
154     else
155         new->print = 0;
156     new->input_buffer = 0;
157     new->input_buffer_len = 0;
158     new->backend = 0;
159     if (cs_getproto(link) == CS_Z3950)
160         new->proto = PROTO_Z3950;
161     else
162         new->proto = PROTO_SR;
163     return new;
164 }
165
166 /*
167  * Free association and release resources.
168  */
169 void destroy_association(association *h)
170 {
171     odr_destroy(h->decode);
172     odr_destroy(h->encode);
173     if (h->print)
174         odr_destroy(h->print);
175     if (h->input_buffer)
176         free(h->input_buffer);
177     if (h->backend)
178         bend_close(h->backend);
179     free(h);
180 }
181
182 /*
183  * process events on the association. Called when the event loop detects an
184  * event.
185  *  h : the I/O channel that has an outstanding event.
186  *  event : the current outstanding event.
187  */
188 void ir_session(IOCHAN h, int event)
189 {
190     int res;
191     association *assoc = iochan_getdata(h);
192     COMSTACK conn = assoc->client_link;
193
194     if (event == EVENT_INPUT)
195     {
196         assert(assoc && conn);
197         res = cs_get(conn, &assoc->input_buffer, &assoc->input_buffer_len);
198         switch (res)
199         {
200             case 0: case -1: /* connection closed by peer */
201                 logf(LOG_LOG, "Connection closed by client");
202                 cs_close(conn);
203                 destroy_association(assoc);
204                 iochan_destroy(h);
205                 return;
206             case 1:  /* incomplete read */
207                 return;
208             default: /* data! */
209                 assoc->input_apdu_len = res;
210                 if (process_apdu(h) < 0)
211                 {
212                     cs_close(conn);
213                     destroy_association(assoc);
214                     iochan_destroy(h);
215                 }
216                 else if (cs_more(conn)) /* arrange to be called again */
217                     iochan_setevent(h, EVENT_INPUT);
218         }
219     }
220     else if (event == EVENT_OUTPUT)
221     {
222         switch (res = cs_put(conn, assoc->encode_buffer, assoc->encoded_len))
223         {
224             case -1:
225                 logf(LOG_LOG, "Connection closed by client");
226                 cs_close(conn);
227                 destroy_association(assoc);
228                 iochan_destroy(h);
229             case 0: /* all sent */
230                 iochan_setflags(h, EVENT_INPUT | EVENT_EXCEPT); /* reset */
231                 break;
232             case 1: /* partial send */
233                 break; /* we'll get called again */
234         }
235     }
236     else if (event == EVENT_EXCEPT)
237     {
238         logf(LOG_LOG, "Exception on line");
239         cs_close(conn);
240         destroy_association(assoc);
241         iochan_destroy(h);
242     }
243 }
244
245 /*
246  * Process the current outstanding APDU.
247  */
248 static int process_apdu(IOCHAN chan)
249 {
250     Z_APDU *apdu;
251     int res;
252     association *assoc = iochan_getdata(chan);
253
254     odr_setbuf(assoc->decode, assoc->input_buffer, assoc->input_apdu_len);
255     if (!z_APDU(assoc->decode, &apdu, 0))
256     {
257         logf(LOG_WARN, "ODR error: %s",
258             odr_errlist[odr_geterror(assoc->decode)]);
259         return -1;
260     }
261     if (assoc->print && !z_APDU(assoc->print, &apdu, 0))
262     {
263         logf(LOG_WARN, "ODR print error: %s", 
264             odr_errlist[odr_geterror(assoc->print)]);
265         return -1;
266     }
267     switch (apdu->which)
268     {
269         case Z_APDU_initRequest:
270             res = process_initRequest(chan, apdu->u.initRequest); break;
271         case Z_APDU_searchRequest:
272             res = process_searchRequest(chan, apdu->u.searchRequest); break;
273         case Z_APDU_presentRequest:
274             res = process_presentRequest(chan, apdu->u.presentRequest); break;
275         case Z_APDU_scanRequest:
276             res = process_scanRequest(chan, apdu->u.scanRequest); break;
277         default:
278             logf(LOG_WARN, "Bad APDU");
279             return -1;
280     }
281     odr_reset(assoc->decode); /* release incoming APDU */
282     odr_reset(assoc->encode); /* release stuff alloced before encoding */
283     return res;
284 }
285
286 static int process_initRequest(IOCHAN client, Z_InitRequest *req)
287 {
288     Z_APDU apdu, *apdup;
289     Z_InitResponse resp;
290     bool_t result = 1;
291     association *assoc = iochan_getdata(client);
292     bend_initrequest binitreq;
293     bend_initresult *binitres;
294     Odr_bitmask options, protocolVersion;
295
296     logf(LOG_LOG, "Got initRequest");
297     if (req->implementationId)
298         logf(LOG_LOG, "Id:        %s", req->implementationId);
299     if (req->implementationName)
300         logf(LOG_LOG, "Name:      %s", req->implementationName);
301     if (req->implementationVersion)
302         logf(LOG_LOG, "Version:   %s", req->implementationVersion);
303
304     binitreq.configname = "default-config";
305     if (!(binitres = bend_init(&binitreq)) || binitres->errcode)
306     {
307         logf(LOG_WARN, "Negative response from backend");
308         return -1;
309     }
310
311     assoc->backend = binitres->handle;
312     apdup = &apdu;
313     apdu.which = Z_APDU_initResponse;
314     apdu.u.initResponse = &resp;
315     resp.referenceId = req->referenceId;
316     ODR_MASK_ZERO(&options);
317     if (ODR_MASK_GET(req->options, Z_Options_search))
318         ODR_MASK_SET(&options, Z_Options_search);
319     if (ODR_MASK_GET(req->options, Z_Options_present))
320         ODR_MASK_SET(&options, Z_Options_present);
321 #if 0
322     if (ODR_MASK_GET(req->options, Z_Options_delSet))
323         ODR_MASK_SET(&options, Z_Options_delSet);
324 #endif
325     if (ODR_MASK_GET(req->options, Z_Options_namedResultSets))
326         ODR_MASK_SET(&options, Z_Options_namedResultSets);
327     if (ODR_MASK_GET(req->options, Z_Options_scan))
328         ODR_MASK_SET(&options, Z_Options_scan);
329     resp.options = &options;
330     ODR_MASK_ZERO(&protocolVersion);
331     if (ODR_MASK_GET(req->protocolVersion, Z_ProtocolVersion_1))
332         ODR_MASK_SET(&protocolVersion, Z_ProtocolVersion_1);
333     if (ODR_MASK_GET(req->protocolVersion, Z_ProtocolVersion_2))
334         ODR_MASK_SET(&protocolVersion, Z_ProtocolVersion_2);
335     resp.protocolVersion = &protocolVersion;
336     assoc->maximumRecordSize = *req->maximumRecordSize;
337     if (assoc->maximumRecordSize > MAXRECORDSIZE)
338         assoc->maximumRecordSize = MAXRECORDSIZE;
339     assoc->preferredMessageSize = *req->preferredMessageSize;
340     if (assoc->preferredMessageSize > assoc->maximumRecordSize)
341         assoc->preferredMessageSize = assoc->maximumRecordSize;
342     resp.preferredMessageSize = &assoc->preferredMessageSize;
343     resp.maximumRecordSize = &assoc->maximumRecordSize;
344     resp.result = &result;
345     resp.implementationId = "YAZ";
346 #if 0
347     resp.implementationName = "Index Data/YAZ Generic Frontend Server";
348 #endif
349     resp.implementationName = "High Level API Server aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaabbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaabbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbdddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddduuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa";
350     resp.implementationVersion = "$Revision: 1.21 $";
351     resp.userInformationField = 0;
352     if (!z_APDU(assoc->encode, &apdup, 0))
353     {
354         logf(LOG_FATAL, "ODR error encoding initres: %s",
355             odr_errlist[odr_geterror(assoc->encode)]);
356         return -1;
357     }
358     assoc->encode_buffer = odr_getbuf(assoc->encode, &assoc->encoded_len);
359     odr_reset(assoc->encode);
360     iochan_setflags(client, EVENT_OUTPUT | EVENT_EXCEPT);
361     return 0;
362 }
363
364 static Z_Records *diagrec(oid_proto proto, int error, char *addinfo)
365 {
366     static Z_Records rec;
367     oident bib1;
368     static Z_DiagRec dr;
369     static int err;
370
371     bib1.proto = proto;
372     bib1.class = CLASS_DIAGSET;
373     bib1.value = VAL_BIB1;
374
375     logf(LOG_DEBUG, "Diagnostic: %d -- %s", error, addinfo ? addinfo :
376         "NULL");
377     err = error;
378     rec.which = Z_Records_NSD;
379     rec.u.nonSurrogateDiagnostic = &dr;
380     dr.diagnosticSetId = oid_getoidbyent(&bib1);
381     dr.condition = &err;
382     dr.addinfo = addinfo ? addinfo : "";
383     return &rec;
384 }
385
386 static Z_NamePlusRecord *surrogatediagrec(oid_proto proto, char *dbname,
387                                             int error, char *addinfo)
388 {
389     static Z_NamePlusRecord rec;
390     static Z_DiagRec dr;
391     static int err;
392     oident bib1;
393
394     bib1.proto = proto;
395     bib1.class = CLASS_DIAGSET;
396     bib1.value = VAL_BIB1;
397
398     logf(LOG_DEBUG, "SurrogateDiagnotic: %d -- %s", error, addinfo);
399     err = error;
400     rec.databaseName = dbname;
401     rec.which = Z_NamePlusRecord_surrogateDiagnostic;
402     rec.u.surrogateDiagnostic = &dr;
403     dr.diagnosticSetId = oid_getoidbyent(&bib1);
404     dr.condition = &err;
405     dr.addinfo = addinfo ? addinfo : "";
406     return &rec;
407 }
408
409 static Z_DiagRecs *diagrecs(oid_proto proto, int error, char *addinfo)
410 {
411     static Z_DiagRecs recs;
412     static Z_DiagRec *recp[1], rec;
413     static int err;
414     oident bib1;
415
416     logf(LOG_DEBUG, "DiagRecs: %d -- %s", error, addinfo);
417     bib1.proto = proto;
418     bib1.class = CLASS_DIAGSET;
419     bib1.value = VAL_BIB1;
420
421     err = error;
422     recs.num_diagRecs = 1;
423     recs.diagRecs = recp;
424     recp[0] = &rec;
425     rec.diagnosticSetId = oid_getoidbyent(&bib1);
426     rec.condition = &err;
427     rec.addinfo = addinfo ? addinfo : "";
428     return &recs;
429 }
430
431 #define MAX_RECORDS 256
432
433 static Z_Records *pack_records(association *a, char *setname, int start,
434                                 int *num, Z_ElementSetNames *esn,
435                                 int *next, int *pres)
436 {
437     int recno, total_length = 0, toget = *num;
438     static Z_Records records;
439     static Z_NamePlusRecordList reclist;
440     static Z_NamePlusRecord *list[MAX_RECORDS];
441     oident recform;
442     Odr_oid *oid;
443
444     records.which = Z_Records_DBOSD;
445     records.u.databaseOrSurDiagnostics = &reclist;
446     reclist.num_records = 0;
447     reclist.records = list;
448     *pres = Z_PRES_SUCCESS;
449     *num = 0;
450     *next = 0;
451
452     recform.proto = a->proto;
453     recform.class = CLASS_RECSYN;
454     recform.value = VAL_USMARC;
455     if (!(oid = odr_oiddup(a->encode, oid_getoidbyent(&recform))))
456         return 0;
457
458     logf(LOG_DEBUG, "Request to pack %d+%d", start, toget);
459     logf(LOG_DEBUG, "pms=%d, mrs=%d", a->preferredMessageSize,
460         a->maximumRecordSize);
461     for (recno = start; reclist.num_records < toget; recno++)
462     {
463         bend_fetchrequest freq;
464         bend_fetchresult *fres;
465         Z_NamePlusRecord *thisrec;
466         Z_DatabaseRecord *thisext;
467
468         if (reclist.num_records == MAX_RECORDS - 1)
469         {
470             *pres = Z_PRES_PARTIAL_2;
471             break;
472         }
473         freq.setname = setname;
474         freq.number = recno;
475         if (!(fres = bend_fetch(a->backend, &freq, 0)))
476         {
477             *pres = Z_PRES_FAILURE;
478             return diagrec(a->proto, 2, "Backend interface problem");
479         }
480         /* backend should be able to signal whether error is system-wide
481            or only pertaining to current record */
482         if (fres->errcode)
483         {
484             *pres = Z_PRES_FAILURE;
485             return diagrec(a->proto, fres->errcode, fres->errstring);
486         }
487         logf(LOG_DEBUG, "  fetched record, len=%d, total=%d",
488             fres->len, total_length);
489         if (fres->len + total_length > a->preferredMessageSize)
490         {
491             /* record is small enough, really */
492             if (fres->len <= a->preferredMessageSize)
493             {
494                 logf(LOG_DEBUG, "  Dropped last normal-sized record");
495                 *pres = Z_PRES_PARTIAL_2;
496                 break;
497             }
498             /* record can only be fetched by itself */
499             if (fres->len < a->maximumRecordSize)
500             {
501                 logf(LOG_DEBUG, "  Record > prefmsgsz");
502                 if (toget > 1)
503                 {
504                     logf(LOG_DEBUG, "  Dropped it");
505                     reclist.records[reclist.num_records] =
506                          surrogatediagrec(a->proto, fres->basename, 16, 0);
507                     reclist.num_records++;
508                     *pres = Z_PRES_PARTIAL_2;
509                     break;
510                 }
511             }
512             else /* too big entirely */
513             {
514                 logf(LOG_DEBUG, "Record > maxrcdsz");
515                 reclist.records[reclist.num_records] =
516                     surrogatediagrec(a->proto, fres->basename, 17, 0);
517                 reclist.num_records++;
518                 *pres = Z_PRES_PARTIAL_2;
519                 break;
520             }
521         }
522         if (!(thisrec = odr_malloc(a->encode, sizeof(*thisrec))))
523             return 0;
524         if (!(thisrec->databaseName = odr_malloc(a->encode,
525             strlen(fres->basename) + 1)))
526             return 0;
527         strcpy(thisrec->databaseName, fres->basename);
528         thisrec->which = Z_NamePlusRecord_databaseRecord;
529         if (!(thisrec->u.databaseRecord = thisext =  odr_malloc(a->encode,
530             sizeof(Z_DatabaseRecord))))
531             return 0;
532         thisext->direct_reference = oid; /* should be OID for current MARC */
533         thisext->indirect_reference = 0;
534         thisext->descriptor = 0;
535         thisext->which = ODR_EXTERNAL_octet;
536         if (!(thisext->u.octet_aligned = odr_malloc(a->encode,
537             sizeof(Odr_oct))))
538             return 0;
539         if (!(thisext->u.octet_aligned->buf = odr_malloc(a->encode, fres->len)))
540             return 0;
541         memcpy(thisext->u.octet_aligned->buf, fres->record, fres->len);
542         thisext->u.octet_aligned->len = thisext->u.octet_aligned->size =
543             fres->len;
544         reclist.records[reclist.num_records] = thisrec;
545         reclist.num_records++;
546         total_length += fres->len;
547         (*num)++;
548         *next = fres->last_in_set ? 0 : recno + 1;
549     }
550     return &records;
551 }
552
553 static int process_searchRequest(IOCHAN client, Z_SearchRequest *req)
554 {
555     Z_APDU apdu, *apdup;
556     Z_SearchResponse resp;
557     association *assoc = iochan_getdata(client);
558     int nulint = 0;
559     bool_t sr = 1;
560     bend_searchrequest bsrq;
561     bend_searchresult *bsrt;
562     oident *oent;
563     int next = 0;
564     static int none = Z_RES_NONE;
565
566     logf(LOG_LOG, "Got SearchRequest.");
567     apdup = &apdu;
568     apdu.which = Z_APDU_searchResponse;
569     apdu.u.searchResponse = &resp;
570     resp.referenceId = req->referenceId;
571
572     resp.records = 0;
573     if (req->query->which == Z_Query_type_1)
574     {
575         Z_RPNQuery *q = req->query->u.type_1;
576
577         if (!(oent = oid_getentbyoid(q->attributeSetId)) ||
578             oent->class != CLASS_ATTSET ||
579             oent->value != VAL_BIB1)
580         {
581             resp.records = diagrec(assoc->proto, 121, 0);
582             resp.resultCount = &nulint;
583             resp.numberOfRecordsReturned = &nulint;
584             resp.nextResultSetPosition = &nulint;
585             resp.searchStatus = &none;
586             resp.resultSetStatus = 0;
587             resp.presentStatus = 0;
588         }
589     }
590     if (!resp.records)
591     {
592         int toget;
593         Z_ElementSetNames *setnames;
594         int presst = 0;
595
596         bsrq.setname = req->resultSetName;
597         bsrq.replace_set = *req->replaceIndicator;
598         bsrq.num_bases = req->num_databaseNames;
599         bsrq.basenames = req->databaseNames;
600         bsrq.query = req->query;
601
602         if (!(bsrt = bend_search(assoc->backend, &bsrq, 0)))
603             return -1;
604         else if (bsrt->errcode)
605         {
606
607             resp.records = diagrec(assoc->proto, bsrt->errcode,
608                 bsrt->errstring);
609             resp.resultCount = &nulint;
610             resp.numberOfRecordsReturned = &nulint;
611             resp.nextResultSetPosition = &nulint;
612             resp.searchStatus = &nulint;
613             resp.resultSetStatus = &none;
614             resp.presentStatus = 0;
615         }
616         else
617         {
618             resp.records = 0;
619
620             resp.resultCount = &bsrt->hits;
621
622             /* how many records does the user agent want, then? */
623             if (bsrt->hits <= *req->smallSetUpperBound)
624             {
625                 toget = bsrt->hits;
626                 setnames = req->smallSetElementSetNames;
627             }
628             else if (bsrt->hits < *req->largeSetLowerBound)
629             {
630                 toget = *req->mediumSetPresentNumber;
631                 if (toget > bsrt->hits)
632                     toget = bsrt->hits;
633                 setnames = req->mediumSetElementSetNames;
634             }
635             else
636                 toget = 0;
637
638             if (toget && !resp.records)
639             {
640                 resp.records = pack_records(assoc, req->resultSetName, 1,
641                     &toget, setnames, &next, &presst);
642                 if (!resp.records)
643                     return -1;
644                 resp.numberOfRecordsReturned = &toget;
645                 resp.nextResultSetPosition = &next;
646                 resp.searchStatus = &sr;
647                 resp.resultSetStatus = 0;
648                 resp.presentStatus = &presst;
649             }
650             else
651             {
652                 resp.numberOfRecordsReturned = &nulint;
653                 resp.nextResultSetPosition = &next;
654                 resp.searchStatus = &sr;
655                 resp.resultSetStatus = 0;
656                 resp.presentStatus = 0;
657             }
658         }
659     }
660
661     if (!z_APDU(assoc->encode, &apdup, 0))
662     {
663         logf(LOG_FATAL, "ODR error encoding searchres: %s",
664             odr_errlist[odr_geterror(assoc->encode)]);
665         return -1;
666     }
667     assoc->encode_buffer = odr_getbuf(assoc->encode, &assoc->encoded_len);
668     odr_reset(assoc->encode);
669     iochan_setflags(client, EVENT_OUTPUT | EVENT_EXCEPT);
670     return 0;
671 }
672
673 static int process_presentRequest(IOCHAN client, Z_PresentRequest *req)
674 {
675     Z_APDU apdu, *apdup;
676     Z_PresentResponse resp;
677     association *assoc = iochan_getdata(client);
678     int presst, next, num;
679
680     logf(LOG_LOG, "Got PresentRequest.");
681     apdup = &apdu;
682     apdu.which = Z_APDU_presentResponse;
683     apdu.u.presentResponse = &resp;
684     resp.referenceId = req->referenceId;
685
686     num = *req->numberOfRecordsRequested;
687     resp.records = pack_records(assoc, req->resultSetId,
688         *req->resultSetStartPoint, &num, req->elementSetNames, &next, &presst);
689     if (!resp.records)
690         return -1;
691     resp.numberOfRecordsReturned = &num;
692     resp.presentStatus = &presst;
693     resp.nextResultSetPosition = &next;
694
695     if (!z_APDU(assoc->encode, &apdup, 0))
696     {
697         logf(LOG_FATAL, "ODR error encoding initres: %s",
698             odr_errlist[odr_geterror(assoc->encode)]);
699         return -1;
700     }
701     assoc->encode_buffer = odr_getbuf(assoc->encode, &assoc->encoded_len);
702     odr_reset(assoc->encode);
703     iochan_setflags(client, EVENT_OUTPUT | EVENT_EXCEPT);
704     return 0;
705 }
706
707 static int process_scanRequest(IOCHAN client, Z_ScanRequest *req)
708 {
709     association *assoc = iochan_getdata(client);
710     Z_APDU apdu, *apdup;
711     Z_ScanResponse res;
712     bend_scanrequest srq;
713     bend_scanresult *srs;
714     int scanStatus = Z_Scan_failure;
715     int numberOfEntriesReturned = 0;
716     oident *attent;
717     Z_ListEntries ents;
718 #define SCAN_MAX_ENTRIES 200
719     Z_Entry *tab[SCAN_MAX_ENTRIES];
720
721     apdup = &apdu;
722     apdu.which = Z_APDU_scanResponse;
723     apdu.u.scanResponse = &res;
724     res.referenceId = req->referenceId;
725     res.stepSize = 0;
726     res.scanStatus = &scanStatus;
727     res.numberOfEntriesReturned = &numberOfEntriesReturned;
728     res.positionOfTerm = 0;
729     res.entries = &ents;
730     ents.which = Z_ListEntries_nonSurrogateDiagnostics;
731     res.attributeSet = 0;
732
733     if (req->attributeSet && (!(attent = oid_getentbyoid(req->attributeSet)) ||
734         attent->class != CLASS_ATTSET || attent->value != VAL_BIB1))
735         ents.u.nonSurrogateDiagnostics = diagrecs(assoc->proto, 121, 0);
736     else if (req->stepSize && *req->stepSize > 0)
737         ents.u.nonSurrogateDiagnostics = diagrecs(assoc->proto, 205, 0);
738     else
739     {
740         srq.num_bases = req->num_databaseNames;
741         srq.basenames = req->databaseNames;
742         srq.num_entries = *req->numberOfTermsRequested;
743         srq.term = req->termListAndStartPoint;
744         srq.term_position = req->preferredPositionInResponse ?
745             *req->preferredPositionInResponse : 1;
746         if (!(srs = bend_scan(assoc->backend, &srq, 0)))
747             ents.u.nonSurrogateDiagnostics = diagrecs(assoc->proto, 2, 0);
748         else if (srs->errcode)
749             ents.u.nonSurrogateDiagnostics = diagrecs(assoc->proto,
750                 srs->errcode, srs->errstring);
751         else
752         {
753             int i;
754             static Z_Entries list;
755
756             if (srs->status == BEND_SCAN_PARTIAL)
757                 scanStatus = Z_Scan_partial_5;
758             else
759                 scanStatus = Z_Scan_success;
760             ents.which = Z_ListEntries_entries;
761             ents.u.entries = &list;
762             list.entries = tab;
763             for (i = 0; i < srs->num_entries; i++)
764             {
765                 Z_Entry *e;
766                 Z_TermInfo *t;
767                 Odr_oct *o;
768
769                 if (i >= SCAN_MAX_ENTRIES)
770                 {
771                     scanStatus = Z_Scan_partial_4;
772                     break;
773                 }
774                 list.entries[i] = e = odr_malloc(assoc->encode, sizeof(*e));
775                 e->which = Z_Entry_termInfo;
776                 e->u.termInfo = t = odr_malloc(assoc->encode, sizeof(*t));
777                 t->suggestedAttributes = 0;
778                 t->alternativeTerm = 0;
779                 t->byAttributes = 0;
780                 t->globalOccurrences = &srs->entries[i].occurrences;
781                 t->term = odr_malloc(assoc->encode, sizeof(*t->term));
782                 t->term->which = Z_Term_general;
783                 t->term->u.general = o = odr_malloc(assoc->encode,
784                     sizeof(Odr_oct));
785                 o->buf = odr_malloc(assoc->encode, o->len = o->size =
786                     strlen(srs->entries[i].term));
787                 memcpy(o->buf, srs->entries[i].term, o->len);
788             }
789             list.num_entries = i;
790             res.numberOfEntriesReturned = &list.num_entries;
791             res.positionOfTerm = &srs->term_position;
792         }
793     }
794     if (!z_APDU(assoc->encode, &apdup, 0))
795     {
796         logf(LOG_FATAL, "ODR error encoding scanres: %s",
797             odr_errlist[odr_geterror(assoc->encode)]);
798         return -1;
799     }
800 #if 0
801     odr_reset(assoc->print);
802     if (!z_APDU(assoc->print, &apdup, 0))
803     {
804         logf(LOG_FATAL, "ODR error priniting scanres: %s",
805             odr_errlist[odr_geterror(assoc->encode)]);
806         return -1;
807     }
808 #endif
809     assoc->encode_buffer = odr_getbuf(assoc->encode, &assoc->encoded_len);
810     odr_reset(assoc->encode);
811     iochan_setflags(client, EVENT_OUTPUT | EVENT_EXCEPT);
812     return 0;
813 }