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