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