Fixed Makefile(s).
[yaz-moved-to-github.git] / server / seshigh.c
1 /*
2  * Copyright (c) 1995, Index Data
3  * See the file LICENSE for details.
4  * Sebastian Hammer, Adam Dickmeiss
5  *
6  * $Log: seshigh.c,v $
7  * Revision 1.69  1997-09-30 11:48:12  adam
8  * Fixed bug introduced by previous commit.
9  *
10  * Revision 1.68  1997/09/29 13:18:59  adam
11  * Added function, oid_ent_to_oid, to replace the function
12  * oid_getoidbyent, which is not thread safe.
13  *
14  * Revision 1.67  1997/09/17 12:10:40  adam
15  * YAZ version 1.4.
16  *
17  * Revision 1.66  1997/09/05 15:26:44  adam
18  * Added ODR encode in search and scen bend request structures.
19  * Fixed a few enums that caused trouble with C++.
20  *
21  * Revision 1.65  1997/09/01 08:53:01  adam
22  * New windows NT/95 port using MSV5.0. The test server 'ztest' was
23  * moved a separate directory. MSV5.0 project server.dsp created.
24  * As an option, the server can now operate as an NT service.
25  *
26  * Revision 1.64  1997/04/30 08:52:11  quinn
27  * Null
28  *
29  * Revision 1.63  1996/10/11  11:57:26  quinn
30  * Smallish
31  *
32  * Revision 1.62  1996/07/06  19:58:35  quinn
33  * System headerfiles gathered in yconfig
34  *
35  * Revision 1.61  1996/06/10  08:56:16  quinn
36  * Work on Summary.
37  *
38  * Revision 1.60  1996/05/30  11:03:10  quinn
39  * Fixed NextresultSetPosition bug fixed.
40  *
41  * Revision 1.59  1996/05/14  09:26:46  quinn
42  * Added attribute set to scan backend
43  *
44  * Revision 1.58  1996/02/20  12:53:04  quinn
45  * Chanes to SCAN
46  *
47  * Revision 1.57  1996/01/02  08:57:47  quinn
48  * Changed enums in the ASN.1 .h files to #defines. Changed oident.class to oclass
49  *
50  * Revision 1.56  1995/12/14  11:09:57  quinn
51  * Work on Explain
52  *
53  * Revision 1.55  1995/11/08  17:41:37  quinn
54  * Smallish.
55  *
56  * Revision 1.54  1995/11/08  15:11:29  quinn
57  * Log of close transmit.
58  *
59  * Revision 1.53  1995/11/01  13:54:58  quinn
60  * Minor adjustments
61  *
62  * Revision 1.52  1995/11/01  12:19:13  quinn
63  * Second attempt to fix same bug.
64  *
65  * Revision 1.50  1995/10/25  16:58:32  quinn
66  * Simple.
67  *
68  * Revision 1.49  1995/10/16  13:51:53  quinn
69  * Changes to provide Especs to the backend.
70  *
71  * Revision 1.48  1995/10/06  08:51:20  quinn
72  * Added Write-buffer.
73  *
74  * Revision 1.47  1995/08/29  14:24:16  quinn
75  * Added second half of close-handshake
76  *
77  * Revision 1.46  1995/08/29  11:17:58  quinn
78  * Added code to receive close
79  *
80  * Revision 1.45  1995/08/21  09:11:00  quinn
81  * Smallish fixes to suppport new formats.
82  *
83  * Revision 1.44  1995/08/17  12:45:25  quinn
84  * Fixed minor problems with GRS-1. Added support in c&s.
85  *
86  * Revision 1.43  1995/08/15  12:00:31  quinn
87  * Updated External
88  *
89  * Revision 1.42  1995/08/15  11:16:50  quinn
90  *
91  * Revision 1.41  1995/08/02  10:23:06  quinn
92  * Smallish
93  *
94  * Revision 1.40  1995/07/31  14:34:26  quinn
95  * Fixed bug in process_searchResponse (numberOfRecordsReturned).
96  *
97  * Revision 1.39  1995/06/27  13:21:00  quinn
98  * SUTRS support
99  *
100  * Revision 1.38  1995/06/19  12:39:11  quinn
101  * Fixed bug in timeout code. Added BER dumper.
102  *
103  * Revision 1.37  1995/06/16  13:16:14  quinn
104  * Fixed Defaultdiagformat.
105  *
106  * Revision 1.36  1995/06/16  10:31:36  quinn
107  * Added session timeout.
108  *
109  * Revision 1.35  1995/06/15  07:45:14  quinn
110  * Moving to v3.
111  *
112  * Revision 1.34  1995/06/14  15:26:46  quinn
113  * *** empty log message ***
114  *
115  * Revision 1.33  1995/06/06  14:57:05  quinn
116  * Better diagnostics.
117  *
118  * Revision 1.32  1995/06/06  08:41:44  quinn
119  * Better diagnostics.
120  *
121  * Revision 1.31  1995/06/06  08:15:37  quinn
122  * Cosmetic.
123  *
124  * Revision 1.30  1995/06/05  10:53:32  quinn
125  * Added a better SCAN.
126  *
127  * Revision 1.29  1995/06/01  11:25:03  quinn
128  * Smallish.
129  *
130  * Revision 1.28  1995/06/01  11:21:01  quinn
131  * Attempting to fix a bug in pack-records. replaced break with continue
132  * for large records, according to standard.
133  *
134  * Revision 1.27  1995/05/29  08:12:06  quinn
135  * Moved oid to util
136  *
137  * Revision 1.26  1995/05/18  13:02:12  quinn
138  * Smallish.
139  *
140  * Revision 1.25  1995/05/17  08:42:26  quinn
141  * Transfer auth info to backend. Allow backend to reject init gracefully.
142  *
143  * Revision 1.24  1995/05/16  08:51:04  quinn
144  * License, documentation, and memory fixes
145  *
146  * Revision 1.23  1995/05/15  13:25:10  quinn
147  * Fixed memory bug.
148  *
149  * Revision 1.22  1995/05/15  11:56:39  quinn
150  * Asynchronous facilities. Restructuring of seshigh code.
151  *
152  * Revision 1.21  1995/05/02  08:53:19  quinn
153  * Trying in vain to fix comm with ISODE
154  *
155  * Revision 1.20  1995/04/20  15:13:00  quinn
156  * Cosmetic
157  *
158  * Revision 1.19  1995/04/18  08:15:34  quinn
159  * Added dynamic memory allocation on encoding (whew). Code is now somewhat
160  * neater. We'll make the same change for decoding one day.
161  *
162  * Revision 1.18  1995/04/17  11:28:25  quinn
163  * Smallish
164  *
165  * Revision 1.17  1995/04/10  10:23:36  quinn
166  * Some work to add scan and other things.
167  *
168  * Revision 1.16  1995/03/31  09:18:55  quinn
169  * Added logging.
170  *
171  * Revision 1.15  1995/03/30  14:03:23  quinn
172  * Added RFC1006 as separate library
173  *
174  * Revision 1.14  1995/03/30  12:18:17  quinn
175  * Fixed bug.
176  *
177  * Revision 1.13  1995/03/30  09:09:24  quinn
178  * Added state-handle and some support for asynchronous activities.
179  *
180  * Revision 1.12  1995/03/29  15:40:16  quinn
181  * Ongoing work. Statserv is now dynamic by default
182  *
183  * Revision 1.11  1995/03/28  09:16:21  quinn
184  * Added record packing to the search request
185  *
186  * Revision 1.10  1995/03/27  08:34:24  quinn
187  * Added dynamic server functionality.
188  * Released bindings to session.c (is now redundant)
189  *
190  * Revision 1.9  1995/03/22  15:01:26  quinn
191  * Adjusting record packing.
192  *
193  * Revision 1.8  1995/03/22  10:13:21  quinn
194  * Working on record packer
195  *
196  * Revision 1.7  1995/03/21  15:53:31  quinn
197  * Little changes.
198  *
199  * Revision 1.6  1995/03/21  12:30:09  quinn
200  * Beginning to add support for record packing.
201  *
202  * Revision 1.5  1995/03/17  10:44:13  quinn
203  * Added catch of null-string in makediagrec
204  *
205  * Revision 1.4  1995/03/17  10:18:08  quinn
206  * Added memory management.
207  *
208  * Revision 1.3  1995/03/16  17:42:39  quinn
209  * Little changes
210  *
211  * Revision 1.2  1995/03/16  13:29:01  quinn
212  * Partitioned server.
213  *
214  * Revision 1.1  1995/03/15  16:02:10  quinn
215  * Modded session.c to seshigh.c
216  *
217  */
218
219 /*
220  * Frontend server logic.
221  *
222  * This code receives incoming APDUs, and handles client requests by means
223  * of the backend API.
224  *
225  * Some of the code is getting quite involved, compared to simpler servers -
226  * primarily because it is asynchronous both in the communication with
227  * the user and the backend. We think the complexity will pay off in
228  * the form of greater flexibility when more asynchronous facilities
229  * are implemented.
230  *
231  * Memory management has become somewhat involved. In the simple case, where
232  * only one PDU is pending at a time, it will simply reuse the same memory,
233  * once it has found its working size. When we enable multiple concurrent
234  * operations, perhaps even with multiple parallel calls to the backend, it
235  * will maintain a pool of buffers for encoding and decoding, trying to
236  * minimize memory allocation/deallocation during normal operation.
237  *
238  * TODOs include (and will be done in order of public interest):
239  * 
240  * Support for EXPLAIN - provide simple meta-database system.
241  * Support for access control.
242  * Support for resource control.
243  * Support for extended services - primarily Item Order.
244  * Rest of Z39.50-1994
245  *
246  */
247
248 #include <yconfig.h>
249 #include <stdlib.h>
250 #include <stdio.h>
251 #ifdef WINDOWS
252 #include <process.h>
253 #else
254 #include <unistd.h>
255 #endif
256 #include <assert.h>
257
258 #include <xmalloc.h>
259 #include <comstack.h>
260 #include "eventl.h"
261 #include "session.h"
262 #include <proto.h>
263 #include <oid.h>
264 #include <log.h>
265 #include <statserv.h>
266
267 #include <backend.h>
268
269 static int process_request(association *assoc);
270 void backend_response(IOCHAN i, int event);
271 static int process_response(association *assoc, request *req, Z_APDU *res);
272 static Z_APDU *process_initRequest(association *assoc, request *reqb);
273 static Z_APDU *process_searchRequest(association *assoc, request *reqb,
274     int *fd);
275 static Z_APDU *response_searchRequest(association *assoc, request *reqb,
276     bend_searchresult *bsrt, int *fd);
277 static Z_APDU *process_presentRequest(association *assoc, request *reqb,
278     int *fd);
279 static Z_APDU *process_scanRequest(association *assoc, request *reqb, int *fd);
280 static void process_close(association *assoc, request *reqb);
281
282 static FILE *apduf = 0; /* for use in static mode */
283 static statserv_options_block *control_block = 0;
284
285 /*
286  * Create and initialize a new association-handle.
287  *  channel  : iochannel for the current line.
288  *  link     : communications channel.
289  * Returns: 0 or a new association handle.
290  */
291 association *create_association(IOCHAN channel, COMSTACK link)
292 {
293     association *new;
294
295     if (!control_block)
296         control_block = statserv_getcontrol();
297     if (!(new = xmalloc(sizeof(*new))))
298         return 0;
299     new->client_chan = channel;
300     new->client_link = link;
301     if (!(new->decode = odr_createmem(ODR_DECODE)) ||
302         !(new->encode = odr_createmem(ODR_ENCODE)))
303         return 0;
304     if (*control_block->apdufile)
305     {
306         char filename[256];
307         FILE *f;
308
309         strcpy(filename, control_block->apdufile);
310         if (!(new->print = odr_createmem(ODR_PRINT)))
311             return 0;
312         if (*control_block->apdufile != '-')
313         {
314             strcpy(filename, control_block->apdufile);
315             if (!control_block->dynamic)
316             {
317                 if (!apduf)
318                 {
319                     if (!(apduf = fopen(filename, "w")))
320                     {
321                         logf(LOG_WARN|LOG_ERRNO, "%s", filename);
322                         return 0;
323                     }
324                     setvbuf(apduf, 0, _IONBF, 0);
325                 }
326                 f = apduf;
327             }
328             else 
329             {
330                 sprintf(filename + strlen(filename), ".%d", getpid());
331                 if (!(f = fopen(filename, "w")))
332                 {
333                     logf(LOG_WARN|LOG_ERRNO, "%s", filename);
334                     return 0;
335                 }
336                 setvbuf(f, 0, _IONBF, 0);
337             }
338             odr_setprint(new->print, f);
339         }
340     }
341     else
342         new->print = 0;
343     new->input_buffer = 0;
344     new->input_buffer_len = 0;
345     new->backend = 0;
346     new->state = ASSOC_NEW;
347     request_initq(&new->incoming);
348     request_initq(&new->outgoing);
349     new->proto = cs_getproto(link);
350     return new;
351 }
352
353 /*
354  * Free association and release resources.
355  */
356 void destroy_association(association *h)
357 {
358     odr_destroy(h->decode);
359     odr_destroy(h->encode);
360     if (h->print)
361         odr_destroy(h->print);
362     if (h->input_buffer)
363     xfree(h->input_buffer);
364     if (h->backend)
365         bend_close(h->backend);
366     while (request_deq(&h->incoming));
367     while (request_deq(&h->outgoing));
368     request_delq(&h->incoming);
369     request_delq(&h->outgoing);
370     xfree(h);
371 }
372
373 static void do_close(association *a, int reason, char *message)
374 {
375     Z_APDU apdu;
376     Z_Close *cls = zget_Close(a->encode);
377     request *req = request_get(&a->outgoing);
378
379     /* Purge request queue */
380     while (request_deq(&a->incoming));
381     while (request_deq(&a->outgoing));
382     if (a->version >= 3)
383     {
384         logf(LOG_LOG, "Sending Close PDU, reason=%d, message=%s",
385             reason, message ? message : "none");
386         apdu.which = Z_APDU_close;
387         apdu.u.close = cls;
388         *cls->closeReason = reason;
389         cls->diagnosticInformation = message;
390         process_response(a, req, &apdu);
391         iochan_settimeout(a->client_chan, 60);
392     }
393     else
394     {
395         logf(LOG_DEBUG, "v2 client. No Close PDU");
396         iochan_setevent(a->client_chan, EVENT_TIMEOUT); /* force imm close */
397     }
398     a->state = ASSOC_DEAD;
399 }
400
401 /*
402  * This is where PDUs from the client are read and the further
403  * processing is initiated. Flow of control moves down through the
404  * various process_* functions below, until the encoded result comes back up
405  * to the output handler in here.
406  * 
407  *  h     : the I/O channel that has an outstanding event.
408  *  event : the current outstanding event.
409  */
410 void ir_session(IOCHAN h, int event)
411 {
412     int res;
413     association *assoc = iochan_getdata(h);
414     COMSTACK conn = assoc->client_link;
415     request *req;
416
417     assert(h && conn && assoc);
418     if (event == EVENT_TIMEOUT)
419     {
420         if (assoc->state != ASSOC_UP)
421         {
422             logf(LOG_LOG, "Final timeout - closing connection.");
423             cs_close(conn);
424             destroy_association(assoc);
425             iochan_destroy(h);
426         }
427         else
428         {
429             logf(LOG_LOG, "Session idle too long. Sending close.");
430             do_close(assoc, Z_Close_lackOfActivity, 0);
431         }
432         return;
433     }
434     if (event & EVENT_INPUT || event & EVENT_WORK) /* input */
435     {
436         if (event & EVENT_INPUT)
437         {
438             logf(LOG_DEBUG, "ir_session (input)");
439             assert(assoc && conn);
440             /* We aren't speaking to this fellow */
441             if (assoc->state == ASSOC_DEAD)
442             {
443                 logf(LOG_LOG, "Closed connection after reject");
444                 cs_close(conn);
445                 destroy_association(assoc);
446                 iochan_destroy(h);
447                 return;
448             }
449             if ((res = cs_get(conn, &assoc->input_buffer,
450                 &assoc->input_buffer_len)) <= 0)
451             {
452                 logf(LOG_LOG, "Connection closed by client");
453                 cs_close(conn);
454                 destroy_association(assoc);
455                 iochan_destroy(h);
456                 return;
457             }
458             else if (res == 1) /* incomplete read - wait for more  */
459                 return;
460             if (cs_more(conn)) /* more stuff - call us again later, please */
461                 iochan_setevent(h, EVENT_INPUT);
462                 
463             /* we got a complete PDU. Let's decode it */
464             logf(LOG_DEBUG, "Got PDU, %d bytes", res);
465             req = request_get(&assoc->incoming); /* get a new request structure */
466             odr_reset(assoc->decode);
467             odr_setbuf(assoc->decode, assoc->input_buffer, res, 0);
468             if (!z_APDU(assoc->decode, &req->request, 0))
469             {
470                 logf(LOG_LOG, "ODR error on incoming PDU: %s [near byte %d] ",
471                     odr_errmsg(odr_geterror(assoc->decode)),
472                     odr_offset(assoc->decode));
473                 logf(LOG_LOG, "PDU dump:");
474                 odr_dumpBER(log_file(), assoc->input_buffer, res);
475                 do_close(assoc, Z_Close_protocolError, "Malformed package");
476                 return;
477             }
478             req->request_mem = odr_extract_mem(assoc->decode);
479             if (assoc->print && !z_APDU(assoc->print, &req->request, 0))
480             {
481                 logf(LOG_WARN, "ODR print error: %s", 
482                     odr_errmsg(odr_geterror(assoc->print)));
483                 odr_reset(assoc->print);
484             }
485             request_enq(&assoc->incoming, req);
486         }
487
488         /* can we do something yet? */
489         req = request_head(&assoc->incoming);
490         if (req->state == REQUEST_IDLE)
491             if (process_request(assoc) < 0)
492                 do_close(assoc, Z_Close_systemProblem, "Unknown error");
493     }
494     if (event & EVENT_OUTPUT)
495     {
496         request *req = request_head(&assoc->outgoing);
497
498         logf(LOG_DEBUG, "ir_session (output)");
499         req->state = REQUEST_PENDING;
500         switch (res = cs_put(conn, req->response, req->len_response))
501         {
502             case -1:
503                 logf(LOG_LOG, "Connection closed by client");
504                 cs_close(conn);
505                 destroy_association(assoc);
506                 iochan_destroy(h);
507                 break;
508             case 0: /* all sent - release the request structure */
509                 logf(LOG_DEBUG, "Wrote PDU, %d bytes", req->len_response);
510                 nmem_destroy(req->request_mem);
511                 request_deq(&assoc->outgoing);
512                 request_release(req);
513                 if (!request_head(&assoc->outgoing))
514                     iochan_clearflag(h, EVENT_OUTPUT);
515                 break;
516             /* value of 1 -- partial send -- is simply ignored */
517         }
518     }
519     if (event & EVENT_EXCEPT)
520     {
521         logf(LOG_DEBUG, "ir_session (exception)");
522         cs_close(conn);
523         destroy_association(assoc);
524         iochan_destroy(h);
525     }
526 }
527
528 /*
529  * Initiate request processing.
530  */
531 static int process_request(association *assoc)
532 {
533     request *req = request_head(&assoc->incoming);
534     int fd = -1;
535     Z_APDU *res;
536
537     logf(LOG_DEBUG, "process_request");
538     assert(req && req->state == REQUEST_IDLE);
539     switch (req->request->which)
540     {
541         case Z_APDU_initRequest:
542             res = process_initRequest(assoc, req); break;
543         case Z_APDU_searchRequest:
544             res = process_searchRequest(assoc, req, &fd); break;
545         case Z_APDU_presentRequest:
546             res = process_presentRequest(assoc, req, &fd); break;
547         case Z_APDU_scanRequest:
548             res = process_scanRequest(assoc, req, &fd); break;
549         case Z_APDU_close:
550             process_close(assoc, req); return 0;
551         default:
552             logf(LOG_WARN, "Bad APDU received");
553             return -1;
554     }
555     if (res)
556     {
557         logf(LOG_DEBUG, "  result immediately available");
558         return process_response(assoc, req, res);
559     }
560     else if (fd < 0)
561     {
562         logf(LOG_WARN, "   bad result");
563         return -1;
564     }
565     else /* no result yet - one will be provided later */
566     {
567         IOCHAN chan;
568
569         /* Set up an I/O handler for the fd supplied by the backend */
570
571         logf(LOG_DEBUG, "   establishing handler for result");
572         req->state = REQUEST_PENDING;
573         if (!(chan = iochan_create(fd, backend_response, EVENT_INPUT)))
574             abort();
575         iochan_setdata(chan, assoc);
576         return 0;
577     }
578 }
579
580 /*
581  * Handle message from the backend.
582  */
583 void backend_response(IOCHAN i, int event)
584 {
585     association *assoc = iochan_getdata(i);
586     request *req = request_head(&assoc->incoming);
587     Z_APDU *res;
588     int fd;
589
590     logf(LOG_DEBUG, "backend_response");
591     assert(assoc && req && req->state != REQUEST_IDLE);
592     /* determine what it is we're waiting for */
593     switch (req->request->which)
594     {
595         case Z_APDU_searchRequest:
596             res = response_searchRequest(assoc, req, 0, &fd); break;
597 #if 0
598         case Z_APDU_presentRequest:
599             res = response_presentRequest(assoc, req, 0, &fd); break;
600         case Z_APDU_scanRequest:
601             res = response_scanRequest(assoc, req, 0, &fd); break;
602 #endif
603         default:
604             logf(LOG_WARN, "Serious programmer's lapse or bug");
605             abort();
606     }
607     if ((res && process_response(assoc, req, res) < 0) || fd < 0)
608     {
609         logf(LOG_LOG, "Fatal error when talking to backend");
610         do_close(assoc, Z_Close_systemProblem, 0);
611         iochan_destroy(i);
612         return;
613     }
614     else if (!res) /* no result yet - try again later */
615     {
616         logf(LOG_DEBUG, "   no result yet");
617         iochan_setfd(i, fd); /* in case fd has changed */
618     }
619 }
620
621 /*
622  * Encode response, and transfer the request structure to the outgoing queue.
623  */
624 static int process_response(association *assoc, request *req, Z_APDU *res)
625 {
626     odr_setbuf(assoc->encode, req->response, req->size_response, 1);
627     if (!z_APDU(assoc->encode, &res, 0))
628     {
629         logf(LOG_WARN, "ODR error when encoding response: %s",
630             odr_errmsg(odr_geterror(assoc->decode)));
631         odr_reset(assoc->encode);
632         return -1;
633     }
634     req->response = odr_getbuf(assoc->encode, &req->len_response,
635         &req->size_response);
636     odr_setbuf(assoc->encode, 0, 0, 0); /* don'txfree if we abort later */
637     odr_reset(assoc->encode);
638     if (assoc->print && !z_APDU(assoc->print, &res, 0))
639     {
640         logf(LOG_WARN, "ODR print error: %s", 
641             odr_errmsg(odr_geterror(assoc->print)));
642         odr_reset(assoc->print);
643     }
644     /* change this when we make the backend reentrant */
645     if (req == request_head(&assoc->incoming))
646     {
647         req->state = REQUEST_IDLE;
648         request_deq(&assoc->incoming);
649     }
650     request_enq(&assoc->outgoing, req);
651     /* turn the work over to the ir_session handler */
652     iochan_setflag(assoc->client_chan, EVENT_OUTPUT);
653     /* Is there more work to be done? give that to the input handler too */
654     if (request_head(&assoc->incoming))
655         iochan_setevent(assoc->client_chan, EVENT_WORK);
656     return 0;
657 }
658
659 /*
660  * Handle init request.
661  * At the moment, we don't check the options
662  * anywhere else in the code - we just try not to do anything that would
663  * break a naive client. We'll toss 'em into the association block when
664  * we need them there.
665  */
666 static Z_APDU *process_initRequest(association *assoc, request *reqb)
667 {
668     Z_InitRequest *req = reqb->request->u.initRequest;
669     Z_APDU *apdu = zget_APDU(assoc->encode, Z_APDU_initResponse);
670     Z_InitResponse *resp = apdu->u.initResponse;
671     bend_initrequest binitreq;
672     bend_initresult *binitres;
673     char options[100];
674
675     logf(LOG_LOG, "Got initRequest");
676     if (req->implementationId)
677         logf(LOG_LOG, "Id:        %s", req->implementationId);
678     if (req->implementationName)
679         logf(LOG_LOG, "Name:      %s", req->implementationName);
680     if (req->implementationVersion)
681         logf(LOG_LOG, "Version:   %s", req->implementationVersion);
682
683     binitreq.stream = assoc->encode;
684     binitreq.configname = "default-config";
685     binitreq.auth = req->idAuthentication;
686     if (!(binitres = bend_init(&binitreq)))
687     {
688         logf(LOG_WARN, "Bad response from backend.");
689         return 0;
690     }
691
692     assoc->backend = binitres->handle;
693     resp->referenceId = req->referenceId;
694     *options = '\0';
695     /* let's tell the client what we can do */
696     if (ODR_MASK_GET(req->options, Z_Options_search))
697     {
698         ODR_MASK_SET(resp->options, Z_Options_search);
699         strcat(options, "srch");
700     }
701     if (ODR_MASK_GET(req->options, Z_Options_present))
702     {
703         ODR_MASK_SET(resp->options, Z_Options_present);
704         strcat(options, " prst");
705     }
706 #if 0
707     if (ODR_MASK_GET(req->options, Z_Options_delSet))
708     {
709         ODR_MASK_SET(&options, Z_Options_delSet);
710         strcat(options, " del");
711     }
712 #endif
713     if (ODR_MASK_GET(req->options, Z_Options_namedResultSets))
714     {
715         ODR_MASK_SET(resp->options, Z_Options_namedResultSets);
716         strcat(options, " namedresults");
717     }
718     if (ODR_MASK_GET(req->options, Z_Options_scan))
719     {
720         ODR_MASK_SET(resp->options, Z_Options_scan);
721         strcat(options, " scan");
722     }
723     if (ODR_MASK_GET(req->options, Z_Options_concurrentOperations))
724     {
725         ODR_MASK_SET(resp->options, Z_Options_concurrentOperations);
726         strcat(options, " concurop");
727     }
728
729     if (ODR_MASK_GET(req->protocolVersion, Z_ProtocolVersion_1))
730     {
731         ODR_MASK_SET(resp->protocolVersion, Z_ProtocolVersion_1);
732         assoc->version = 2; /* 1 & 2 are equivalent */
733     }
734     if (ODR_MASK_GET(req->protocolVersion, Z_ProtocolVersion_2))
735     {
736         ODR_MASK_SET(resp->protocolVersion, Z_ProtocolVersion_2);
737         assoc->version = 2;
738     }
739     if (ODR_MASK_GET(req->protocolVersion, Z_ProtocolVersion_3))
740     {
741         ODR_MASK_SET(resp->protocolVersion, Z_ProtocolVersion_3);
742         assoc->version = 3;
743     }
744     logf(LOG_LOG, "Negotiated to v%d: %s", assoc->version, options);
745     assoc->maximumRecordSize = *req->maximumRecordSize;
746     if (assoc->maximumRecordSize > control_block->maxrecordsize)
747         assoc->maximumRecordSize = control_block->maxrecordsize;
748     assoc->preferredMessageSize = *req->preferredMessageSize;
749     if (assoc->preferredMessageSize > assoc->maximumRecordSize)
750         assoc->preferredMessageSize = assoc->maximumRecordSize;
751     resp->preferredMessageSize = &assoc->preferredMessageSize;
752     resp->maximumRecordSize = &assoc->maximumRecordSize;
753     resp->implementationName = "Index Data/YAZ Generic Frontend Server";
754     if (binitres->errcode)
755     {
756         logf(LOG_LOG, "Connection rejected by backend.");
757         *resp->result = 0;
758         assoc->state = ASSOC_DEAD;
759     }
760     else
761         assoc->state = ASSOC_UP;
762     return apdu;
763 }
764
765 /*
766  * These functions should be merged.
767  */
768
769 /*
770  * nonsurrogate diagnostic record.
771  */
772 static Z_Records *diagrec(association *assoc, int error, char *addinfo)
773 {
774     int oid[OID_SIZE];
775     Z_Records *rec = odr_malloc (assoc->encode, sizeof(*rec));
776     oident bib1;
777     int *err = odr_malloc (assoc->encode, sizeof(*err));
778     Z_DiagRec *drec = odr_malloc (assoc->encode, sizeof(*drec));
779     Z_DefaultDiagFormat *dr = odr_malloc (assoc->encode, sizeof(*dr));
780
781     bib1.proto = assoc->proto;
782     bib1.oclass = CLASS_DIAGSET;
783     bib1.value = VAL_BIB1;
784
785     logf(LOG_DEBUG, "Diagnostic: %d -- %s", error, addinfo ? addinfo :
786         "NULL");
787     *err = error;
788     rec->which = Z_Records_NSD;
789 #ifdef Z_95
790     rec->u.nonSurrogateDiagnostic = drec;
791     drec->which = Z_DiagRec_defaultFormat;
792     drec->u.defaultFormat = dr;
793 #else
794     rec->u.nonSurrogateDiagnostic = dr;
795 #endif
796     dr->diagnosticSetId = odr_oiddup (assoc->encode,
797                                       oid_ent_to_oid(&bib1, oid));
798     dr->condition = err;
799     dr->which = Z_DiagForm_v2AddInfo;
800     dr->addinfo = addinfo ? addinfo : "";
801     return rec;
802 }
803
804 /*
805  * surrogate diagnostic.
806  */
807 static Z_NamePlusRecord *surrogatediagrec(association *assoc, char *dbname,
808                                             int error, char *addinfo)
809 {
810     int oid[OID_SIZE];
811     Z_NamePlusRecord *rec = odr_malloc (assoc->encode, sizeof(*rec));
812     int *err = odr_malloc (assoc->encode, sizeof(*err));
813     oident bib1;
814     Z_DiagRec *drec = odr_malloc (assoc->encode, sizeof(*drec));
815     Z_DefaultDiagFormat *dr = odr_malloc (assoc->encode, sizeof(*dr));
816
817     bib1.proto = assoc->proto;
818     bib1.oclass = CLASS_DIAGSET;
819     bib1.value = VAL_BIB1;
820
821     logf(LOG_DEBUG, "SurrogateDiagnotic: %d -- %s", error, addinfo);
822     *err = error;
823     rec->databaseName = dbname;
824     rec->which = Z_NamePlusRecord_surrogateDiagnostic;
825     rec->u.surrogateDiagnostic = drec;
826     drec->which = Z_DiagRec_defaultFormat;
827     drec->u.defaultFormat = dr;
828     dr->diagnosticSetId = odr_oiddup (assoc->encode,
829                                       oid_ent_to_oid(&bib1, oid));
830     dr->condition = err;
831     dr->which = Z_DiagForm_v2AddInfo;
832     dr->addinfo = addinfo ? addinfo : "";
833     return rec;
834 }
835
836 /*
837  * multiple nonsurrogate diagnostics.
838  */
839 static Z_DiagRecs *diagrecs(association *assoc, int error, char *addinfo)
840 {
841     int oid[OID_SIZE];
842     Z_DiagRecs *recs = odr_malloc (assoc->encode, sizeof(*recs));
843     int *err = odr_malloc (assoc->encode, sizeof(*err));
844     oident bib1;
845     Z_DiagRec **recp = odr_malloc (assoc->encode, sizeof(*recp));
846     Z_DiagRec *drec = odr_malloc (assoc->encode, sizeof(*drec));
847     Z_DefaultDiagFormat *rec = odr_malloc (assoc->encode, sizeof(*rec));
848
849     logf(LOG_DEBUG, "DiagRecs: %d -- %s", error, addinfo);
850     bib1.proto = assoc->proto;
851     bib1.oclass = CLASS_DIAGSET;
852     bib1.value = VAL_BIB1;
853
854     *err = error;
855     recs->num_diagRecs = 1;
856     recs->diagRecs = recp;
857     recp[0] = drec;
858     drec->which = Z_DiagRec_defaultFormat;
859     drec->u.defaultFormat = rec;
860
861     rec->diagnosticSetId = odr_oiddup (assoc->encode,
862                                       oid_ent_to_oid(&bib1, oid));
863     rec->condition = err;
864     rec->which = Z_DiagForm_v2AddInfo;
865     rec->addinfo = addinfo ? addinfo : "";
866     return recs;
867 }
868
869 static Z_Records *pack_records(association *a, char *setname, int start,
870                                 int *num, Z_RecordComposition *comp,
871                                 int *next, int *pres, oid_value format)
872 {
873     int oid[OID_SIZE];
874     int recno, total_length = 0, toget = *num, dumped_records = 0;
875     Z_Records *records = odr_malloc (a->encode, sizeof(*records));
876     Z_NamePlusRecordList *reclist = odr_malloc (a->encode, sizeof(*reclist));
877     Z_NamePlusRecord **list = odr_malloc (a->encode, sizeof(*list) * toget);
878     oident recform;
879
880     records->which = Z_Records_DBOSD;
881     records->u.databaseOrSurDiagnostics = reclist;
882     reclist->num_records = 0;
883     reclist->records = list;
884     *pres = Z_PRES_SUCCESS;
885     *num = 0;
886     *next = 0;
887
888     logf(LOG_DEBUG, "Request to pack %d+%d", start, toget);
889     logf(LOG_DEBUG, "pms=%d, mrs=%d", a->preferredMessageSize,
890         a->maximumRecordSize);
891     for (recno = start; reclist->num_records < toget; recno++)
892     {
893         bend_fetchrequest freq;
894         bend_fetchresult *fres;
895         Z_NamePlusRecord *thisrec;
896         Z_DatabaseRecord *thisext;
897         int this_length;
898
899         /*
900          * we get the number of bytes allocated on the stream before any
901          * allocation done by the backend - this should give us a reasonable
902          * idea of the total size of the data so far.
903          */
904         total_length = odr_total(a->encode) - dumped_records;
905         freq.setname = setname;
906         freq.number = recno;
907         freq.comp = comp;
908         freq.format = format;
909         freq.stream = a->encode;
910         if (!(fres = bend_fetch(a->backend, &freq, 0)))
911         {
912             *pres = Z_PRES_FAILURE;
913             return diagrec(a, 2, "Backend interface problem");
914         }
915         /* backend should be able to signal whether error is system-wide
916            or only pertaining to current record */
917         if (fres->errcode)
918         {
919             *pres = Z_PRES_FAILURE;
920             return diagrec(a, fres->errcode, fres->errstring);
921         }
922         if (fres->len >= 0)
923             this_length = fres->len;
924         else
925             this_length = odr_total(a->encode) - total_length;
926         logf(LOG_DEBUG, "  fetched record, len=%d, total=%d",
927             this_length, total_length);
928         if (this_length + total_length > a->preferredMessageSize)
929         {
930             /* record is small enough, really */
931             if (this_length <= a->preferredMessageSize)
932             {
933                 logf(LOG_DEBUG, "  Dropped last normal-sized record");
934                 *pres = Z_PRES_PARTIAL_2;
935                 break;
936             }
937             /* record can only be fetched by itself */
938             if (this_length < a->maximumRecordSize)
939             {
940                 logf(LOG_DEBUG, "  Record > prefmsgsz");
941                 if (toget > 1)
942                 {
943                     logf(LOG_DEBUG, "  Dropped it");
944                     reclist->records[reclist->num_records] =
945                          surrogatediagrec(a, fres->basename, 16, 0);
946                     reclist->num_records++;
947                     *next = fres->last_in_set ? 0 : recno + 1;
948                     dumped_records += this_length;
949                     continue;
950                 }
951             }
952             else /* too big entirely */
953             {
954                 logf(LOG_DEBUG, "Record > maxrcdsz");
955                 reclist->records[reclist->num_records] =
956                     surrogatediagrec(a, fres->basename, 17, 0);
957                 reclist->num_records++;
958                 *next = fres->last_in_set ? 0 : recno + 1;
959                 dumped_records += this_length;
960                 continue;
961             }
962         }
963         if (!(thisrec = odr_malloc(a->encode, sizeof(*thisrec))))
964             return 0;
965         if (!(thisrec->databaseName = odr_malloc(a->encode,
966             strlen(fres->basename) + 1)))
967             return 0;
968         strcpy(thisrec->databaseName, fres->basename);
969         thisrec->which = Z_NamePlusRecord_databaseRecord;
970         if (!(thisrec->u.databaseRecord = thisext = odr_malloc(a->encode,
971             sizeof(Z_DatabaseRecord))))
972             return 0;
973         recform.proto = a->proto;
974         recform.oclass = CLASS_RECSYN;
975         recform.value = fres->format;
976         thisext->direct_reference = odr_oiddup(a->encode,
977                                                oid_ent_to_oid(&recform, oid));
978         thisext->indirect_reference = 0;
979         thisext->descriptor = 0;
980         if (fres->len < 0) /* Structured data */
981         {
982             switch (fres->format)
983             {
984                 case VAL_SUTRS: thisext->which = Z_External_sutrs; break;
985                 case VAL_GRS1: thisext->which = Z_External_grs1; break;
986                 case VAL_EXPLAIN: thisext->which = Z_External_explainRecord;
987                     break;
988                 case VAL_SUMMARY: thisext->which = Z_External_summary; break;
989                 case VAL_OPAC: thisext->which = Z_External_OPAC; break;
990
991                 default:
992                     logf(LOG_FATAL, "Unknown structured format from backend.");
993                     return 0;
994             }
995
996             /*
997              * We cheat on the pointers here. Obviously, the record field
998              * of the backend-fetch structure should have been a union for
999              * correctness, but we're stuck with this for backwards
1000              * compatibility.
1001              */
1002             thisext->u.grs1 = (Z_GenericRecord*) fres->record;
1003         }
1004         else if (fres->format == VAL_SUTRS) /* SUTRS is a single-ASN.1-type */
1005         {
1006             Odr_oct *sutrs = odr_malloc(a->encode, sizeof(*sutrs));
1007
1008             thisext->which = Z_External_sutrs;
1009             thisext->u.sutrs = sutrs;
1010             sutrs->buf = odr_malloc(a->encode, fres->len);
1011             sutrs->len = sutrs->size = fres->len;
1012             memcpy(sutrs->buf, fres->record, fres->len);
1013         }
1014         else /* octet-aligned record. */
1015         {
1016             thisext->which = Z_External_octet;
1017             if (!(thisext->u.octet_aligned = odr_malloc(a->encode,
1018                 sizeof(Odr_oct))))
1019                 return 0;
1020             if (!(thisext->u.octet_aligned->buf = odr_malloc(a->encode,
1021                 fres->len)))
1022                 return 0;
1023             memcpy(thisext->u.octet_aligned->buf, fres->record, fres->len);
1024             thisext->u.octet_aligned->len = thisext->u.octet_aligned->size =
1025                 fres->len;
1026         }
1027         reclist->records[reclist->num_records] = thisrec;
1028         reclist->num_records++;
1029         *next = fres->last_in_set ? 0 : recno + 1;
1030     }
1031     *num = reclist->num_records;
1032     return records;
1033 }
1034
1035 static Z_APDU *process_searchRequest(association *assoc, request *reqb,
1036     int *fd)
1037 {
1038     Z_SearchRequest *req = reqb->request->u.searchRequest;
1039     bend_searchrequest bsrq;
1040     bend_searchresult *bsrt;
1041
1042     logf(LOG_LOG, "Got SearchRequest.");
1043
1044     bsrq.setname = req->resultSetName;
1045     bsrq.replace_set = *req->replaceIndicator;
1046     bsrq.num_bases = req->num_databaseNames;
1047     bsrq.basenames = req->databaseNames;
1048     bsrq.query = req->query;
1049     bsrq.stream = assoc->encode;
1050
1051     if (!(bsrt = bend_search(assoc->backend, &bsrq, fd)))
1052         return 0;
1053     return response_searchRequest(assoc, reqb, bsrt, fd);
1054 }
1055
1056 bend_searchresult *bend_searchresponse(void *handle) {return 0;}
1057
1058 /*
1059  * Prepare a searchresponse based on the backend results. We probably want
1060  * to look at making the fetching of records nonblocking as well, but
1061  * so far, we'll keep things simple.
1062  * If bsrt is null, that means we're called in response to a communications
1063  * event, and we'll have to get the response for ourselves.
1064  */
1065 static Z_APDU *response_searchRequest(association *assoc, request *reqb,
1066     bend_searchresult *bsrt, int *fd)
1067 {
1068     Z_SearchRequest *req = reqb->request->u.searchRequest;
1069     Z_APDU *apdu = odr_malloc (assoc->encode, sizeof(*apdu));
1070     Z_SearchResponse *resp = odr_malloc (assoc->encode, sizeof(*resp));
1071     int *nulint = odr_malloc (assoc->encode, sizeof(*nulint));
1072     bool_t *sr = odr_malloc (assoc->encode, sizeof(*sr));
1073     int *next = odr_malloc (assoc->encode, sizeof(*next));
1074     int *none = odr_malloc (assoc->encode, sizeof(*none));
1075
1076     *nulint = 0;
1077     *sr = 1;
1078     *next = 0;
1079     *none = Z_RES_NONE;
1080
1081     apdu->which = Z_APDU_searchResponse;
1082     apdu->u.searchResponse = resp;
1083     resp->referenceId = req->referenceId;
1084     resp->additionalSearchInfo = 0;
1085     resp->otherInfo = 0;
1086     *fd = -1;
1087     if (!bsrt && !(bsrt = bend_searchresponse(assoc->backend)))
1088     {
1089         logf(LOG_FATAL, "Bad result from backend");
1090         return 0;
1091     }
1092     else if (bsrt->errcode)
1093     {
1094         resp->records = diagrec(assoc, bsrt->errcode, bsrt->errstring);
1095         resp->resultCount = nulint;
1096         resp->numberOfRecordsReturned = nulint;
1097         resp->nextResultSetPosition = nulint;
1098         resp->searchStatus = nulint;
1099         resp->resultSetStatus = none;
1100         resp->presentStatus = 0;
1101     }
1102     else
1103     {
1104         int *toget = odr_malloc (assoc->encode, sizeof(*toget));
1105         int *presst = odr_malloc (assoc->encode, sizeof(*presst));
1106         Z_RecordComposition comp, *compp = 0;
1107
1108         *toget = 0;
1109         *presst = 0;
1110         resp->records = 0;
1111         resp->resultCount = &bsrt->hits;
1112
1113         comp.which = Z_RecordComp_simple;
1114         /* how many records does the user agent want, then? */
1115         if (bsrt->hits <= *req->smallSetUpperBound)
1116         {
1117             *toget = bsrt->hits;
1118             if ((comp.u.simple = req->smallSetElementSetNames))
1119                 compp = &comp;
1120         }
1121         else if (bsrt->hits < *req->largeSetLowerBound)
1122         {
1123             *toget = *req->mediumSetPresentNumber;
1124             if (*toget > bsrt->hits)
1125                 *toget = bsrt->hits;
1126             if ((comp.u.simple = req->mediumSetElementSetNames))
1127                 compp = &comp;
1128         }
1129         else
1130             *toget = 0;
1131
1132         if (*toget && !resp->records)
1133         {
1134             oident *prefformat;
1135             oid_value form;
1136
1137             if (!(prefformat = oid_getentbyoid(req->preferredRecordSyntax)) ||
1138                 prefformat->oclass != CLASS_RECSYN)
1139                 form = VAL_NONE;
1140             else
1141                 form = prefformat->value;
1142             resp->records = pack_records(assoc, req->resultSetName, 1,
1143                 toget, compp, next, presst, form);
1144             if (!resp->records)
1145                 return 0;
1146             resp->numberOfRecordsReturned = toget;
1147             resp->nextResultSetPosition = next;
1148             resp->searchStatus = sr;
1149             resp->resultSetStatus = 0;
1150             resp->presentStatus = presst;
1151         }
1152         else
1153         {
1154             if (*resp->resultCount)
1155                 *next = 1;
1156             resp->numberOfRecordsReturned = nulint;
1157             resp->nextResultSetPosition = next;
1158             resp->searchStatus = sr;
1159             resp->resultSetStatus = 0;
1160             resp->presentStatus = 0;
1161         }
1162     }
1163     return apdu;
1164 }
1165
1166 /*
1167  * Maybe we got a little over-friendly when we designed bend_fetch to
1168  * get only one record at a time. Some backends can optimise multiple-record
1169  * fetches, and at any rate, there is some overhead involved in
1170  * all that selecting and hopping around. Problem is, of course, that the
1171  * frontend can't know ahead of time how many records it'll need to
1172  * fill the negotiated PDU size. Annoying. Segmentation or not, Z/SR
1173  * is downright lousy as a bulk data transfer protocol.
1174  *
1175  * To start with, we'll do the fetching of records from the backend
1176  * in one operation: To save some trips in and out of the event-handler,
1177  * and to simplify the interface to pack_records. At any rate, asynch
1178  * operation is more fun in operations that have an unpredictable execution
1179  * speed - which is normally more true for search than for present.
1180  */
1181 static Z_APDU *process_presentRequest(association *assoc, request *reqb,
1182     int *fd)
1183 {
1184     Z_PresentRequest *req = reqb->request->u.presentRequest;
1185     Z_APDU *apdu = odr_malloc (assoc->encode, sizeof(*apdu));
1186     Z_PresentResponse *resp = odr_malloc (assoc->encode, sizeof(*resp));
1187     int *presst = odr_malloc (assoc->encode, sizeof(*presst));
1188     int *next = odr_malloc (assoc->encode, sizeof(*next));
1189     int *num = odr_malloc (assoc->encode, sizeof(*num));
1190     oident *prefformat;
1191     oid_value form;
1192
1193     logf(LOG_LOG, "Got PresentRequest.");
1194     *presst = 0;
1195     *next = 0;
1196     *num = 0;
1197
1198     apdu->which = Z_APDU_presentResponse;
1199     apdu->u.presentResponse = resp;
1200     resp->referenceId = req->referenceId;
1201     resp->otherInfo = 0;
1202
1203     if (!(prefformat = oid_getentbyoid(req->preferredRecordSyntax)) ||
1204         prefformat->oclass != CLASS_RECSYN)
1205         form = VAL_NONE;
1206     else
1207         form = prefformat->value;
1208     *num = *req->numberOfRecordsRequested;
1209     resp->records = pack_records(assoc, req->resultSetId,
1210         *req->resultSetStartPoint, num, req->recordComposition, next,
1211         presst, form);
1212     if (!resp->records)
1213         return 0;
1214     resp->numberOfRecordsReturned = num;
1215     resp->presentStatus = presst;
1216     resp->nextResultSetPosition = next;
1217
1218     return apdu;
1219 }
1220
1221 /*
1222  * Scan was implemented rather in a hurry, and with support for only the basic
1223  * elements of the service in the backend API. Suggestions are welcome.
1224  */
1225 static Z_APDU *process_scanRequest(association *assoc, request *reqb, int *fd)
1226 {
1227     Z_ScanRequest *req = reqb->request->u.scanRequest;
1228     Z_APDU *apdu = odr_malloc (assoc->encode, sizeof(*apdu));
1229     Z_ScanResponse *res = odr_malloc (assoc->encode, sizeof(*res));
1230     int *scanStatus = odr_malloc (assoc->encode, sizeof(*scanStatus));
1231     int *numberOfEntriesReturned =
1232          odr_malloc (assoc->encode, sizeof(*numberOfEntriesReturned));
1233     Z_ListEntries *ents = odr_malloc (assoc->encode, sizeof(*ents));
1234     oident *attent;
1235     bend_scanrequest srq;
1236     bend_scanresult *srs;
1237     oident *attset;
1238
1239     logf(LOG_LOG, "Got scanrequest");
1240     *scanStatus = Z_Scan_failure;
1241     *numberOfEntriesReturned = 0;
1242
1243     apdu->which = Z_APDU_scanResponse;
1244     apdu->u.scanResponse = res;
1245     res->referenceId = req->referenceId;
1246     res->stepSize = 0;
1247     res->scanStatus = scanStatus;
1248     res->numberOfEntriesReturned = numberOfEntriesReturned;
1249     res->positionOfTerm = 0;
1250     res->entries = ents;
1251     ents->which = Z_ListEntries_nonSurrogateDiagnostics;
1252     res->attributeSet = 0;
1253     res->otherInfo = 0;
1254
1255     if (req->attributeSet && (!(attent = oid_getentbyoid(req->attributeSet)) ||
1256         attent->oclass != CLASS_ATTSET || attent->value != VAL_BIB1))
1257         ents->u.nonSurrogateDiagnostics = diagrecs(assoc, 121, 0);
1258     else if (req->stepSize && *req->stepSize > 0)
1259         ents->u.nonSurrogateDiagnostics = diagrecs(assoc, 205, 0);
1260     else
1261     {
1262         if (req->termListAndStartPoint->term->which == Z_Term_general)
1263             logf(LOG_DEBUG, " term: '%.*s'",
1264                 req->termListAndStartPoint->term->u.general->len,
1265                 req->termListAndStartPoint->term->u.general->buf);
1266         srq.num_bases = req->num_databaseNames;
1267         srq.basenames = req->databaseNames;
1268         srq.num_entries = *req->numberOfTermsRequested;
1269         srq.term = req->termListAndStartPoint;
1270         srq.stream = assoc->encode;
1271         if (!(attset = oid_getentbyoid(req->attributeSet)) ||
1272             attset->oclass != CLASS_RECSYN)
1273             srq.attributeset = VAL_NONE;
1274         else
1275             srq.attributeset = attset->value;
1276         srq.term_position = req->preferredPositionInResponse ?
1277             *req->preferredPositionInResponse : 1;
1278         if (!(srs = bend_scan(assoc->backend, &srq, 0)))
1279             ents->u.nonSurrogateDiagnostics = diagrecs(assoc, 2, 0);
1280         else if (srs->errcode)
1281             ents->u.nonSurrogateDiagnostics = diagrecs(assoc,
1282                 srs->errcode, srs->errstring);
1283         else
1284         {
1285             int i;
1286             Z_Entries *list = odr_malloc (assoc->encode, sizeof(*list));
1287             Z_Entry **tab = odr_malloc (assoc->encode,
1288                                         sizeof(*tab) * srs->num_entries);
1289
1290             if (srs->status == BEND_SCAN_PARTIAL)
1291                 *scanStatus = Z_Scan_partial_5;
1292             else
1293                 *scanStatus = Z_Scan_success;
1294             ents->which = Z_ListEntries_entries;
1295             ents->u.entries = list;
1296             list->entries = tab;
1297             for (i = 0; i < srs->num_entries; i++)
1298             {
1299                 Z_Entry *e;
1300                 Z_TermInfo *t;
1301                 Odr_oct *o;
1302
1303                 list->entries[i] = e = odr_malloc(assoc->encode, sizeof(*e));
1304                 e->which = Z_Entry_termInfo;
1305                 e->u.termInfo = t = odr_malloc(assoc->encode, sizeof(*t));
1306                 t->suggestedAttributes = 0;
1307                 t->displayTerm = 0;
1308                 t->alternativeTerm = 0;
1309                 t->byAttributes = 0;
1310                 t->otherTermInfo = 0;
1311                 t->globalOccurrences = &srs->entries[i].occurrences;
1312                 t->term = odr_malloc(assoc->encode, sizeof(*t->term));
1313                 t->term->which = Z_Term_general;
1314                 t->term->u.general = o = odr_malloc(assoc->encode,
1315                     sizeof(Odr_oct));
1316                 o->buf = odr_malloc(assoc->encode, o->len = o->size =
1317                     strlen(srs->entries[i].term));
1318                 memcpy(o->buf, srs->entries[i].term, o->len);
1319                 logf(LOG_DEBUG, "  term #%d: '%s' (%d)", i,
1320                     srs->entries[i].term, srs->entries[i].occurrences);
1321             }
1322             list->num_entries = i;
1323             res->numberOfEntriesReturned = &list->num_entries;
1324             res->positionOfTerm = &srs->term_position;
1325         }
1326     }
1327
1328     return apdu;
1329 }
1330
1331 static void process_close(association *assoc, request *reqb)
1332 {
1333     Z_Close *req = reqb->request->u.close;
1334     static char *reasons[] =
1335     {
1336         "finished",
1337         "shutdown",
1338         "systemProblem",
1339         "costLimit",
1340         "resources",
1341         "securityViolation",
1342         "protocolError",
1343         "lackOfActivity",
1344         "peerAbort",
1345         "unspecified"
1346     };
1347
1348     logf(LOG_LOG, "Got close, reason %s, message %s",
1349         reasons[*req->closeReason], req->diagnosticInformation ?
1350         req->diagnosticInformation : "NULL");
1351     if (assoc->version < 3) /* to make do_force respond with close */
1352         assoc->version = 3;
1353     do_close(assoc, Z_Close_finished, "Association terminated by client");
1354 }