277050ab5825a62193748b153aecf4643fc31fe3
[yaz-moved-to-github.git] / server / seshigh.c
1 /*
2  * Copyright (c) 1995-2000, Index Data
3  * See the file LICENSE for details.
4  *
5  * $Log: seshigh.c,v $
6  * Revision 1.110  2000-10-02 13:05:32  adam
7  * Fixed bug introduced by previous commit.
8  *
9  * Revision 1.109  2000/10/02 11:07:44  adam
10  * Added peer_name member for bend_init handler. Changed the YAZ
11  * client so that tcp: can be avoided in target spec.
12  *
13  * Revision 1.108  2000/09/04 08:58:15  adam
14  * Added prefix yaz_ for most logging utility functions.
15  *
16  * Revision 1.107  2000/08/31 10:20:12  adam
17  * Added member request_format and output_format for backend fetch method.
18  *
19  * Revision 1.106  2000/08/31 09:51:25  adam
20  * Added record_syntax member for fetch method (raw OID).
21  *
22  * Revision 1.105  2000/07/06 10:38:47  adam
23  * Enhanced option --enable-tcpd.
24  *
25  * Revision 1.104  2000/04/05 07:39:55  adam
26  * Added shared library support (libtool).
27  *
28  * Revision 1.103  2000/03/20 19:06:25  adam
29  * Added Segment request for fronend server. Work on admin for client.
30  *
31  * Revision 1.102  2000/03/15 12:59:49  adam
32  * Added handle member to statserv_control.
33  *
34  * Revision 1.101  2000/01/12 14:36:07  adam
35  * Added printing stream (ODR) for backend functions.
36  *
37  * Revision 1.100  1999/12/16 23:36:19  adam
38  * Implemented ILL protocol. Minor updates ASN.1 compiler.
39  *
40  * Revision 1.99  1999/11/30 13:47:12  adam
41  * Improved installation. Moved header files to include/yaz.
42  *
43  * Revision 1.98  1999/11/29 15:12:27  adam
44  * Changed the way implementationName - and version is set.
45  *
46  * Revision 1.96  1999/11/04 14:58:44  adam
47  * Added status elements for backend delete result set handler.
48  * Updated delete result result set command for client.
49  *
50  * Revision 1.95  1999/10/11 10:01:24  adam
51  * Implemented bend_sort_rr handler for frontend server.
52  *
53  * Revision 1.94  1999/08/27 09:40:32  adam
54  * Renamed logf function to yaz_log. Removed VC++ project files.
55  *
56  * Revision 1.93  1999/07/06 12:17:15  adam
57  * Added option -1 that runs server once (for profiling purposes).
58  *
59  * Revision 1.92  1999/06/17 10:54:45  adam
60  * Added facility to specify implementation version - and name
61  * for server.
62  *
63  * Revision 1.91  1999/06/01 14:29:12  adam
64  * Work on Extended Services.
65  *
66  * Revision 1.90  1999/05/27 13:02:20  adam
67  * Assigned OID for old DB Update (VAL_DBUPDATE0).
68  *
69  * Revision 1.89  1999/05/26 15:24:26  adam
70  * Fixed minor bugs regarding DB Update (introduced by previous commit).
71  *
72  * Revision 1.88  1999/04/20 09:56:48  adam
73  * Added 'name' paramter to encoder/decoder routines (typedef Odr_fun).
74  * Modified all encoders/decoders to reflect this change.
75  *
76  * Revision 1.87  1999/03/31 11:18:25  adam
77  * Implemented odr_strdup. Added Reference ID to backend server API.
78  *
79  * Revision 1.86  1999/02/02 13:57:38  adam
80  * Uses preprocessor define WIN32 instead of WINDOWS to build code
81  * for Microsoft WIN32.
82  *
83  * Revision 1.85  1998/11/17 09:52:59  adam
84  * Fixed minor bug (introduced by previous commit).
85  *
86  * Revision 1.84  1998/11/16 16:02:32  adam
87  * Added loggin utilies, log_rpn_query and log_scan_term. These used
88  * to be part of Zebra.
89  *
90  * Revision 1.83  1998/11/03 10:09:36  adam
91  * Fixed bug regarding YC.
92  *
93  * Revision 1.82  1998/10/20 14:00:30  quinn
94  * Fixed Scan
95  *
96  * Revision 1.81  1998/10/13 16:12:24  adam
97  * Added support for Surrogate Diagnostics for Scan Term entries.
98  *
99  * Revision 1.80  1998/09/02 12:41:53  adam
100  * Added decode stream in bend search structures.
101  *
102  * Revision 1.79  1998/08/19 16:10:08  adam
103  * Changed som member names of DeleteResultSetRequest/Response.
104  *
105  * Revision 1.78  1998/08/03 10:23:55  adam
106  * Fixed bug regarding Options for Sort.
107  *
108  * Revision 1.77  1998/07/20 12:38:42  adam
109  * Implemented delete result set service to server API.
110  *
111  * Revision 1.76  1998/05/27 16:57:07  adam
112  * Support for surrogate diagnostic records added for bend_fetch.
113  *
114  * Revision 1.75  1998/05/18 10:13:07  adam
115  * Fixed call to es_request handler - extra argument was passed.
116  *
117  * Revision 1.74  1998/03/31 15:13:20  adam
118  * Development towards compiled ASN.1.
119  *
120  * Revision 1.73  1998/03/31 11:07:45  adam
121  * Furhter work on UNIverse resource report.
122  * Added Extended Services handling in frontend server.
123  *
124  * Revision 1.72  1998/02/11 11:53:35  adam
125  * Changed code so that it compiles as C++.
126  *
127  * Revision 1.71  1998/02/10 11:03:57  adam
128  * Added support for extended handlers in backend server interface.
129  *
130  * Revision 1.70  1998/01/29 13:15:35  adam
131  * Implemented sort for the backend interface.
132  *
133  * Revision 1.69  1997/09/30 11:48:12  adam
134  * Fixed bug introduced by previous commit.
135  *
136  * Revision 1.68  1997/09/29 13:18:59  adam
137  * Added function, oid_ent_to_oid, to replace the function
138  * oid_getoidbyent, which is not thread safe.
139  *
140  * Revision 1.67  1997/09/17 12:10:40  adam
141  * YAZ version 1.4.
142  *
143  * Revision 1.66  1997/09/05 15:26:44  adam
144  * Added ODR encode in search and scen bend request structures.
145  * Fixed a few enums that caused trouble with C++.
146  *
147  * Revision 1.65  1997/09/01 08:53:01  adam
148  * New windows NT/95 port using MSV5.0. The test server 'ztest' was
149  * moved a separate directory. MSV5.0 project server.dsp created.
150  * As an option, the server can now operate as an NT service.
151  *
152  * Revision 1.64  1997/04/30 08:52:11  quinn
153  * Null
154  *
155  * Revision 1.63  1996/10/11  11:57:26  quinn
156  * Smallish
157  *
158  * Revision 1.62  1996/07/06  19:58:35  quinn
159  * System headerfiles gathered in yconfig
160  *
161  * Revision 1.61  1996/06/10  08:56:16  quinn
162  * Work on Summary.
163  *
164  * Revision 1.60  1996/05/30  11:03:10  quinn
165  * Fixed NextresultSetPosition bug fixed.
166  *
167  * Revision 1.59  1996/05/14  09:26:46  quinn
168  * Added attribute set to scan backend
169  *
170  * Revision 1.58  1996/02/20  12:53:04  quinn
171  * Chanes to SCAN
172  *
173  * Revision 1.57  1996/01/02  08:57:47  quinn
174  * Changed enums in the ASN.1 .h files to #defines. Changed oident.class to oclass
175  *
176  * Revision 1.56  1995/12/14  11:09:57  quinn
177  * Work on Explain
178  *
179  * Revision 1.55  1995/11/08  17:41:37  quinn
180  * Smallish.
181  *
182  * Revision 1.54  1995/11/08  15:11:29  quinn
183  * Log of close transmit.
184  *
185  * Revision 1.53  1995/11/01  13:54:58  quinn
186  * Minor adjustments
187  *
188  * Revision 1.52  1995/11/01  12:19:13  quinn
189  * Second attempt to fix same bug.
190  *
191  * Revision 1.50  1995/10/25  16:58:32  quinn
192  * Simple.
193  *
194  * Revision 1.49  1995/10/16  13:51:53  quinn
195  * Changes to provide Especs to the backend.
196  *
197  * Revision 1.48  1995/10/06  08:51:20  quinn
198  * Added Write-buffer.
199  *
200  * Revision 1.47  1995/08/29  14:24:16  quinn
201  * Added second half of close-handshake
202  *
203  * Revision 1.46  1995/08/29  11:17:58  quinn
204  * Added code to receive close
205  *
206  * Revision 1.45  1995/08/21  09:11:00  quinn
207  * Smallish fixes to suppport new formats.
208  *
209  * Revision 1.44  1995/08/17  12:45:25  quinn
210  * Fixed minor problems with GRS-1. Added support in c&s.
211  *
212  * Revision 1.43  1995/08/15  12:00:31  quinn
213  * Updated External
214  *
215  * Revision 1.42  1995/08/15  11:16:50  quinn
216  *
217  * Revision 1.41  1995/08/02  10:23:06  quinn
218  * Smallish
219  *
220  * Revision 1.40  1995/07/31  14:34:26  quinn
221  * Fixed bug in process_searchResponse (numberOfRecordsReturned).
222  *
223  * Revision 1.39  1995/06/27  13:21:00  quinn
224  * SUTRS support
225  *
226  * Revision 1.38  1995/06/19  12:39:11  quinn
227  * Fixed bug in timeout code. Added BER dumper.
228  *
229  * Revision 1.37  1995/06/16  13:16:14  quinn
230  * Fixed Defaultdiagformat.
231  *
232  * Revision 1.36  1995/06/16  10:31:36  quinn
233  * Added session timeout.
234  *
235  * Revision 1.35  1995/06/15  07:45:14  quinn
236  * Moving to v3.
237  *
238  * Revision 1.34  1995/06/14  15:26:46  quinn
239  * *** empty log message ***
240  *
241  * Revision 1.33  1995/06/06  14:57:05  quinn
242  * Better diagnostics.
243  *
244  * Revision 1.32  1995/06/06  08:41:44  quinn
245  * Better diagnostics.
246  *
247  * Revision 1.31  1995/06/06  08:15:37  quinn
248  * Cosmetic.
249  *
250  * Revision 1.30  1995/06/05  10:53:32  quinn
251  * Added a better SCAN.
252  *
253  * Revision 1.29  1995/06/01  11:25:03  quinn
254  * Smallish.
255  *
256  * Revision 1.28  1995/06/01  11:21:01  quinn
257  * Attempting to fix a bug in pack-records. replaced break with continue
258  * for large records, according to standard.
259  *
260  * Revision 1.27  1995/05/29  08:12:06  quinn
261  * Moved oid to util
262  *
263  * Revision 1.26  1995/05/18  13:02:12  quinn
264  * Smallish.
265  *
266  * Revision 1.25  1995/05/17  08:42:26  quinn
267  * Transfer auth info to backend. Allow backend to reject init gracefully.
268  *
269  * Revision 1.24  1995/05/16  08:51:04  quinn
270  * License, documentation, and memory fixes
271  *
272  * Revision 1.23  1995/05/15  13:25:10  quinn
273  * Fixed memory bug.
274  *
275  * Revision 1.22  1995/05/15  11:56:39  quinn
276  * Asynchronous facilities. Restructuring of seshigh code.
277  *
278  * Revision 1.21  1995/05/02  08:53:19  quinn
279  * Trying in vain to fix comm with ISODE
280  *
281  * Revision 1.20  1995/04/20  15:13:00  quinn
282  * Cosmetic
283  *
284  * Revision 1.19  1995/04/18  08:15:34  quinn
285  * Added dynamic memory allocation on encoding (whew). Code is now somewhat
286  * neater. We'll make the same change for decoding one day.
287  *
288  * Revision 1.18  1995/04/17  11:28:25  quinn
289  * Smallish
290  *
291  * Revision 1.17  1995/04/10  10:23:36  quinn
292  * Some work to add scan and other things.
293  *
294  * Revision 1.16  1995/03/31  09:18:55  quinn
295  * Added logging.
296  *
297  * Revision 1.15  1995/03/30  14:03:23  quinn
298  * Added RFC1006 as separate library
299  *
300  * Revision 1.14  1995/03/30  12:18:17  quinn
301  * Fixed bug.
302  *
303  * Revision 1.13  1995/03/30  09:09:24  quinn
304  * Added state-handle and some support for asynchronous activities.
305  *
306  * Revision 1.12  1995/03/29  15:40:16  quinn
307  * Ongoing work. Statserv is now dynamic by default
308  *
309  * Revision 1.11  1995/03/28  09:16:21  quinn
310  * Added record packing to the search request
311  *
312  * Revision 1.10  1995/03/27  08:34:24  quinn
313  * Added dynamic server functionality.
314  * Released bindings to session.c (is now redundant)
315  *
316  * Revision 1.9  1995/03/22  15:01:26  quinn
317  * Adjusting record packing.
318  *
319  * Revision 1.8  1995/03/22  10:13:21  quinn
320  * Working on record packer
321  *
322  * Revision 1.7  1995/03/21  15:53:31  quinn
323  * Little changes.
324  *
325  * Revision 1.6  1995/03/21  12:30:09  quinn
326  * Beginning to add support for record packing.
327  *
328  * Revision 1.5  1995/03/17  10:44:13  quinn
329  * Added catch of null-string in makediagrec
330  *
331  * Revision 1.4  1995/03/17  10:18:08  quinn
332  * Added memory management.
333  *
334  * Revision 1.3  1995/03/16  17:42:39  quinn
335  * Little changes
336  *
337  * Revision 1.2  1995/03/16  13:29:01  quinn
338  * Partitioned server.
339  *
340  * Revision 1.1  1995/03/15  16:02:10  quinn
341  * Modded session.c to seshigh.c
342  *
343  */
344
345 /*
346  * Frontend server logic.
347  *
348  * This code receives incoming APDUs, and handles client requests by means
349  * of the backend API.
350  *
351  * Some of the code is getting quite involved, compared to simpler servers -
352  * primarily because it is asynchronous both in the communication with
353  * the user and the backend. We think the complexity will pay off in
354  * the form of greater flexibility when more asynchronous facilities
355  * are implemented.
356  *
357  * Memory management has become somewhat involved. In the simple case, where
358  * only one PDU is pending at a time, it will simply reuse the same memory,
359  * once it has found its working size. When we enable multiple concurrent
360  * operations, perhaps even with multiple parallel calls to the backend, it
361  * will maintain a pool of buffers for encoding and decoding, trying to
362  * minimize memory allocation/deallocation during normal operation.
363  *
364  * TODOs include (and will be done in order of public interest):
365  * 
366  * Support for EXPLAIN - provide simple meta-database system.
367  * Support for access control.
368  * Support for resource control.
369  * Support for extended services - primarily Item Order.
370  * Rest of Z39.50-1994
371  *
372  */
373
374 #include <stdlib.h>
375 #include <stdio.h>
376 #ifdef WIN32
377 #include <process.h>
378 #else
379 #include <unistd.h>
380 #endif
381 #include <assert.h>
382
383 #include <yaz/yconfig.h>
384 #include <yaz/xmalloc.h>
385 #include <yaz/comstack.h>
386 #include "eventl.h"
387 #include "session.h"
388 #include <yaz/proto.h>
389 #include <yaz/oid.h>
390 #include <yaz/log.h>
391 #include <yaz/logrpn.h>
392 #include <yaz/statserv.h>
393
394 #include <yaz/backend.h>
395
396 static int process_request(association *assoc, request *req, char **msg);
397 void backend_response(IOCHAN i, int event);
398 static int process_response(association *assoc, request *req, Z_APDU *res);
399 static Z_APDU *process_initRequest(association *assoc, request *reqb);
400 static Z_APDU *process_searchRequest(association *assoc, request *reqb,
401     int *fd);
402 static Z_APDU *response_searchRequest(association *assoc, request *reqb,
403     bend_search_rr *bsrr, int *fd);
404 static Z_APDU *process_presentRequest(association *assoc, request *reqb,
405     int *fd);
406 static Z_APDU *process_scanRequest(association *assoc, request *reqb, int *fd);
407 static Z_APDU *process_sortRequest(association *assoc, request *reqb, int *fd);
408 static void process_close(association *assoc, request *reqb);
409 void save_referenceId (request *reqb, Z_ReferenceId *refid);
410 static Z_APDU *process_deleteRequest(association *assoc, request *reqb,
411     int *fd);
412 static Z_APDU *process_segmentRequest (association *assoc, request *reqb);
413
414 static FILE *apduf = 0; /* for use in static mode */
415 static statserv_options_block *control_block = 0;
416
417 /* Chas: Added in from DALI */
418 static Z_APDU *process_ESRequest(association *assoc, request *reqb, int *fd);
419 /* Chas: End of addition from DALI */
420
421 /*
422  * Create and initialize a new association-handle.
423  *  channel  : iochannel for the current line.
424  *  link     : communications channel.
425  * Returns: 0 or a new association handle.
426  */
427 association *create_association(IOCHAN channel, COMSTACK link)
428 {
429     association *anew;
430
431     if (!control_block)
432         control_block = statserv_getcontrol();
433     if (!(anew = (association *)xmalloc(sizeof(*anew))))
434         return 0;
435     anew->init = 0;
436     anew->client_chan = channel;
437     anew->client_link = link;
438     if (!(anew->decode = odr_createmem(ODR_DECODE)) ||
439         !(anew->encode = odr_createmem(ODR_ENCODE)))
440         return 0;
441     if (*control_block->apdufile)
442     {
443         char filename[256];
444         FILE *f;
445
446         strcpy(filename, control_block->apdufile);
447         if (!(anew->print = odr_createmem(ODR_PRINT)))
448             return 0;
449         if (*control_block->apdufile != '-')
450         {
451             strcpy(filename, control_block->apdufile);
452             if (!control_block->dynamic)
453             {
454                 if (!apduf)
455                 {
456                     if (!(apduf = fopen(filename, "w")))
457                     {
458                         yaz_log(LOG_WARN|LOG_ERRNO, "%s", filename);
459                         return 0;
460                     }
461                     setvbuf(apduf, 0, _IONBF, 0);
462                 }
463                 f = apduf;
464             }
465             else 
466             {
467                 sprintf(filename + strlen(filename), ".%d", getpid());
468                 if (!(f = fopen(filename, "w")))
469                 {
470                     yaz_log(LOG_WARN|LOG_ERRNO, "%s", filename);
471                     return 0;
472                 }
473                 setvbuf(f, 0, _IONBF, 0);
474             }
475             odr_setprint(anew->print, f);
476         }
477     }
478     else
479         anew->print = 0;
480     anew->input_buffer = 0;
481     anew->input_buffer_len = 0;
482     anew->backend = 0;
483     anew->state = ASSOC_NEW;
484     request_initq(&anew->incoming);
485     request_initq(&anew->outgoing);
486     anew->proto = cs_getproto(link);
487     return anew;
488 }
489
490 /*
491  * Free association and release resources.
492  */
493 void destroy_association(association *h)
494 {
495     statserv_options_block *cb = statserv_getcontrol();
496
497     xfree(h->init);
498     odr_destroy(h->decode);
499     odr_destroy(h->encode);
500     if (h->print)
501         odr_destroy(h->print);
502     if (h->input_buffer)
503     xfree(h->input_buffer);
504     if (h->backend)
505         (*cb->bend_close)(h->backend);
506     while (request_deq(&h->incoming));
507     while (request_deq(&h->outgoing));
508     request_delq(&h->incoming);
509     request_delq(&h->outgoing);
510     xfree(h);
511     if (control_block && control_block->one_shot)
512         exit (0);
513 }
514
515 static void do_close_req(association *a, int reason, char *message,
516                          request *req)
517 {
518     Z_APDU apdu;
519     Z_Close *cls = zget_Close(a->encode);
520     
521     /* Purge request queue */
522     while (request_deq(&a->incoming));
523     while (request_deq(&a->outgoing));
524     if (a->version >= 3)
525     {
526         yaz_log(LOG_LOG, "Sending Close PDU, reason=%d, message=%s",
527             reason, message ? message : "none");
528         apdu.which = Z_APDU_close;
529         apdu.u.close = cls;
530         *cls->closeReason = reason;
531         cls->diagnosticInformation = message;
532         process_response(a, req, &apdu);
533         iochan_settimeout(a->client_chan, 60);
534     }
535     else
536     {
537         yaz_log(LOG_DEBUG, "v2 client. No Close PDU");
538         iochan_setevent(a->client_chan, EVENT_TIMEOUT); /* force imm close */
539     }
540     a->state = ASSOC_DEAD;
541 }
542
543 static void do_close(association *a, int reason, char *message)
544 {
545     do_close_req (a, reason, message, request_get(&a->outgoing));
546 }
547
548 /*
549  * This is where PDUs from the client are read and the further
550  * processing is initiated. Flow of control moves down through the
551  * various process_* functions below, until the encoded result comes back up
552  * to the output handler in here.
553  * 
554  *  h     : the I/O channel that has an outstanding event.
555  *  event : the current outstanding event.
556  */
557 void ir_session(IOCHAN h, int event)
558 {
559     int res;
560     association *assoc = (association *)iochan_getdata(h);
561     COMSTACK conn = assoc->client_link;
562     request *req;
563
564     assert(h && conn && assoc);
565     if (event == EVENT_TIMEOUT)
566     {
567         if (assoc->state != ASSOC_UP)
568         {
569             yaz_log(LOG_LOG, "Final timeout - closing connection.");
570             cs_close(conn);
571             destroy_association(assoc);
572             iochan_destroy(h);
573         }
574         else
575         {
576             yaz_log(LOG_LOG, "Session idle too long. Sending close.");
577             do_close(assoc, Z_Close_lackOfActivity, 0);
578         }
579         return;
580     }
581     if (event & EVENT_INPUT || event & EVENT_WORK) /* input */
582     {
583         if (event & EVENT_INPUT)
584         {
585             yaz_log(LOG_DEBUG, "ir_session (input)");
586             assert(assoc && conn);
587             /* We aren't speaking to this fellow */
588             if (assoc->state == ASSOC_DEAD)
589             {
590                 yaz_log(LOG_LOG, "Closed connection after reject");
591                 cs_close(conn);
592                 destroy_association(assoc);
593                 iochan_destroy(h);
594                 return;
595             }
596             if ((res = cs_get(conn, &assoc->input_buffer,
597                 &assoc->input_buffer_len)) <= 0)
598             {
599                 yaz_log(LOG_LOG, "Connection closed by client");
600                 cs_close(conn);
601                 destroy_association(assoc);
602                 iochan_destroy(h);
603                 return;
604             }
605             else if (res == 1) /* incomplete read - wait for more  */
606                 return;
607             if (cs_more(conn)) /* more stuff - call us again later, please */
608                 iochan_setevent(h, EVENT_INPUT);
609                 
610             /* we got a complete PDU. Let's decode it */
611             yaz_log(LOG_DEBUG, "Got PDU, %d bytes", res);
612             req = request_get(&assoc->incoming); /* get a new request structure */
613             odr_reset(assoc->decode);
614             odr_setbuf(assoc->decode, assoc->input_buffer, res, 0);
615             if (!z_APDU(assoc->decode, &req->request, 0, 0))
616             {
617                 yaz_log(LOG_LOG, "ODR error on incoming PDU: %s [near byte %d] ",
618                     odr_errmsg(odr_geterror(assoc->decode)),
619                     odr_offset(assoc->decode));
620                 yaz_log(LOG_LOG, "PDU dump:");
621                 odr_dumpBER(yaz_log_file(), assoc->input_buffer, res);
622                 do_close(assoc, Z_Close_protocolError, "Malformed package");
623                 return;
624             }
625             req->request_mem = odr_extract_mem(assoc->decode);
626             if (assoc->print && !z_APDU(assoc->print, &req->request, 0, 0))
627             {
628                 yaz_log(LOG_WARN, "ODR print error: %s", 
629                     odr_errmsg(odr_geterror(assoc->print)));
630                 odr_reset(assoc->print);
631             }
632             request_enq(&assoc->incoming, req);
633         }
634
635         /* can we do something yet? */
636         req = request_head(&assoc->incoming);
637         if (req->state == REQUEST_IDLE)
638         {
639             char *msg;
640             request_deq(&assoc->incoming);
641             if (process_request(assoc, req, &msg) < 0)
642                 do_close_req(assoc, Z_Close_systemProblem, msg, req);
643         }
644     }
645     if (event & EVENT_OUTPUT)
646     {
647         request *req = request_head(&assoc->outgoing);
648
649         yaz_log(LOG_DEBUG, "ir_session (output)");
650         req->state = REQUEST_PENDING;
651         switch (res = cs_put(conn, req->response, req->len_response))
652         {
653             case -1:
654                 yaz_log(LOG_LOG, "Connection closed by client");
655                 cs_close(conn);
656                 destroy_association(assoc);
657                 iochan_destroy(h);
658                 break;
659             case 0: /* all sent - release the request structure */
660                 yaz_log(LOG_DEBUG, "Wrote PDU, %d bytes", req->len_response);
661                 nmem_destroy(req->request_mem);
662                 request_deq(&assoc->outgoing);
663                 request_release(req);
664                 if (!request_head(&assoc->outgoing))
665                     iochan_clearflag(h, EVENT_OUTPUT);
666                 break;
667             /* value of 1 -- partial send -- is simply ignored */
668         }
669     }
670     if (event & EVENT_EXCEPT)
671     {
672         yaz_log(LOG_DEBUG, "ir_session (exception)");
673         cs_close(conn);
674         destroy_association(assoc);
675         iochan_destroy(h);
676     }
677 }
678
679 /*
680  * Initiate request processing.
681  */
682 static int process_request(association *assoc, request *req, char **msg)
683 {
684     int fd = -1;
685     Z_APDU *res;
686     int retval;
687     
688     *msg = "Unknown Error";
689     assert(req && req->state == REQUEST_IDLE);
690     if (req->request->which != Z_APDU_initRequest && !assoc->init)
691     {
692         *msg = "Missing InitRequest";
693         return -1;
694     }
695     switch (req->request->which)
696     {
697     case Z_APDU_initRequest:
698         res = process_initRequest(assoc, req); break;
699     case Z_APDU_searchRequest:
700         res = process_searchRequest(assoc, req, &fd); break;
701     case Z_APDU_presentRequest:
702         res = process_presentRequest(assoc, req, &fd); break;
703     case Z_APDU_scanRequest:
704         if (assoc->init->bend_scan)
705             res = process_scanRequest(assoc, req, &fd);
706         else
707         {
708             *msg = "Cannot handle Scan APDU";
709             return -1;
710         }
711         break;
712     case Z_APDU_extendedServicesRequest:
713         if (assoc->init->bend_esrequest)
714             res = process_ESRequest(assoc, req, &fd);
715         else
716         {
717             *msg = "Cannot handle Extended Services APDU";
718             return -1;
719         }
720         break;
721     case Z_APDU_sortRequest:
722         if (assoc->init->bend_sort)
723             res = process_sortRequest(assoc, req, &fd);
724         else
725         {
726             *msg = "Cannot handle Sort APDU";
727             return -1;
728         }
729         break;
730     case Z_APDU_close:
731         process_close(assoc, req);
732         return 0;
733     case Z_APDU_deleteResultSetRequest:
734         if (assoc->init->bend_delete)
735             res = process_deleteRequest(assoc, req, &fd);
736         else
737         {
738             *msg = "Cannot handle Delete APDU";
739             return -1;
740         }
741         break;
742     case Z_APDU_segmentRequest:
743         if (assoc->init->bend_segment)
744         {
745             res = process_segmentRequest (assoc, req);
746         }
747         else
748         {
749             *msg = "Cannot handle Segment APDU";
750             return -1;
751         }
752         break;
753     default:
754         *msg = "Bad APDU received";
755         return -1;
756     }
757     if (res)
758     {
759         yaz_log(LOG_DEBUG, "  result immediately available");
760         retval = process_response(assoc, req, res);
761     }
762     else if (fd < 0)
763     {
764         yaz_log(LOG_DEBUG, "  result unavailble");
765         retval = 0;
766     }
767     else /* no result yet - one will be provided later */
768     {
769         IOCHAN chan;
770
771         /* Set up an I/O handler for the fd supplied by the backend */
772
773         yaz_log(LOG_DEBUG, "   establishing handler for result");
774         req->state = REQUEST_PENDING;
775         if (!(chan = iochan_create(fd, backend_response, EVENT_INPUT)))
776             abort();
777         iochan_setdata(chan, assoc);
778         retval = 0;
779     }
780     return retval;
781 }
782
783 /*
784  * Handle message from the backend.
785  */
786 void backend_response(IOCHAN i, int event)
787 {
788     association *assoc = (association *)iochan_getdata(i);
789     request *req = request_head(&assoc->incoming);
790     Z_APDU *res;
791     int fd;
792
793     yaz_log(LOG_DEBUG, "backend_response");
794     assert(assoc && req && req->state != REQUEST_IDLE);
795     /* determine what it is we're waiting for */
796     switch (req->request->which)
797     {
798         case Z_APDU_searchRequest:
799             res = response_searchRequest(assoc, req, 0, &fd); break;
800 #if 0
801         case Z_APDU_presentRequest:
802             res = response_presentRequest(assoc, req, 0, &fd); break;
803         case Z_APDU_scanRequest:
804             res = response_scanRequest(assoc, req, 0, &fd); break;
805 #endif
806         default:
807             yaz_log(LOG_WARN, "Serious programmer's lapse or bug");
808             abort();
809     }
810     if ((res && process_response(assoc, req, res) < 0) || fd < 0)
811     {
812         yaz_log(LOG_LOG, "Fatal error when talking to backend");
813         do_close(assoc, Z_Close_systemProblem, 0);
814         iochan_destroy(i);
815         return;
816     }
817     else if (!res) /* no result yet - try again later */
818     {
819         yaz_log(LOG_DEBUG, "   no result yet");
820         iochan_setfd(i, fd); /* in case fd has changed */
821     }
822 }
823
824 /*
825  * Encode response, and transfer the request structure to the outgoing queue.
826  */
827 static int process_response(association *assoc, request *req, Z_APDU *res)
828 {
829     odr_setbuf(assoc->encode, req->response, req->size_response, 1);
830     if (!z_APDU(assoc->encode, &res, 0, 0))
831     {
832         yaz_log(LOG_WARN, "ODR error when encoding response: %s",
833             odr_errmsg(odr_geterror(assoc->decode)));
834         odr_reset(assoc->encode);
835         return -1;
836     }
837     req->response = odr_getbuf(assoc->encode, &req->len_response,
838         &req->size_response);
839     odr_setbuf(assoc->encode, 0, 0, 0); /* don'txfree if we abort later */
840     if (assoc->print && !z_APDU(assoc->print, &res, 0, 0))
841     {
842         yaz_log(LOG_WARN, "ODR print error: %s", 
843             odr_errmsg(odr_geterror(assoc->print)));
844         odr_reset(assoc->print);
845     }
846     odr_reset(assoc->encode);
847     req->state = REQUEST_IDLE;
848     request_enq(&assoc->outgoing, req);
849     /* turn the work over to the ir_session handler */
850     iochan_setflag(assoc->client_chan, EVENT_OUTPUT);
851     /* Is there more work to be done? give that to the input handler too */
852 #if 1
853     if (request_head(&assoc->incoming))
854     {
855         yaz_log (LOG_DEBUG, "more work to be done");
856         iochan_setevent(assoc->client_chan, EVENT_WORK);
857     }
858 #endif
859     return 0;
860 }
861
862 /*
863  * Handle init request.
864  * At the moment, we don't check the options
865  * anywhere else in the code - we just try not to do anything that would
866  * break a naive client. We'll toss 'em into the association block when
867  * we need them there.
868  */
869 static Z_APDU *process_initRequest(association *assoc, request *reqb)
870 {
871     statserv_options_block *cb = statserv_getcontrol();
872     Z_InitRequest *req = reqb->request->u.initRequest;
873     Z_APDU *apdu = zget_APDU(assoc->encode, Z_APDU_initResponse);
874     Z_InitResponse *resp = apdu->u.initResponse;
875     bend_initresult *binitres;
876     char options[100];
877
878     xfree (assoc->init);
879     assoc->init = xmalloc (sizeof(*assoc->init));
880
881     yaz_log(LOG_LOG, "Got initRequest");
882     if (req->implementationId)
883         yaz_log(LOG_LOG, "Id:        %s", req->implementationId);
884     if (req->implementationName)
885         yaz_log(LOG_LOG, "Name:      %s", req->implementationName);
886     if (req->implementationVersion)
887         yaz_log(LOG_LOG, "Version:   %s", req->implementationVersion);
888
889     assoc->init->stream = assoc->encode;
890     assoc->init->print = assoc->print;
891     assoc->init->auth = req->idAuthentication;
892     assoc->init->referenceId = req->referenceId;
893     assoc->init->implementation_version = 0;
894     assoc->init->implementation_name = 0;
895     assoc->init->bend_sort = NULL;
896     assoc->init->bend_search = NULL;
897     assoc->init->bend_present = NULL;
898     assoc->init->bend_esrequest = NULL;
899     assoc->init->bend_delete = NULL;
900     assoc->init->bend_scan = NULL;
901     assoc->init->bend_segment = NULL;
902     assoc->init->bend_fetch = NULL;
903     
904     assoc->init->peer_name =
905         odr_strdup (assoc->encode, cs_addrstr(assoc->client_link));
906     if (!(binitres = (*cb->bend_init)(assoc->init)))
907     {
908         yaz_log(LOG_WARN, "Bad response from backend.");
909         return 0;
910     }
911
912     assoc->backend = binitres->handle;
913     if ((assoc->init->bend_sort))
914         yaz_log (LOG_DEBUG, "Sort handler installed");
915     if ((assoc->init->bend_search))
916         yaz_log (LOG_DEBUG, "Search handler installed");
917     if ((assoc->init->bend_present))
918         yaz_log (LOG_DEBUG, "Present handler installed");   
919     if ((assoc->init->bend_esrequest))
920         yaz_log (LOG_DEBUG, "ESRequest handler installed");   
921     if ((assoc->init->bend_delete))
922         yaz_log (LOG_DEBUG, "Delete handler installed");   
923     if ((assoc->init->bend_scan))
924         yaz_log (LOG_DEBUG, "Scan handler installed");   
925     if ((assoc->init->bend_segment))
926         yaz_log (LOG_DEBUG, "Segment handler installed");   
927     
928     resp->referenceId = req->referenceId;
929     *options = '\0';
930     /* let's tell the client what we can do */
931     if (ODR_MASK_GET(req->options, Z_Options_search))
932     {
933         ODR_MASK_SET(resp->options, Z_Options_search);
934         strcat(options, "srch");
935     }
936     if (ODR_MASK_GET(req->options, Z_Options_present))
937     {
938         ODR_MASK_SET(resp->options, Z_Options_present);
939         strcat(options, " prst");
940     }
941     if (ODR_MASK_GET(req->options, Z_Options_delSet) &&
942         assoc->init->bend_delete)
943     {
944         ODR_MASK_SET(resp->options, Z_Options_delSet);
945         strcat(options, " del");
946     }
947     if (ODR_MASK_GET(req->options, Z_Options_extendedServices) &&
948         assoc->init->bend_esrequest)
949     {
950         ODR_MASK_SET(resp->options, Z_Options_extendedServices);
951         strcat (options, " extendedServices");
952     }
953     if (ODR_MASK_GET(req->options, Z_Options_namedResultSets))
954     {
955         ODR_MASK_SET(resp->options, Z_Options_namedResultSets);
956         strcat(options, " namedresults");
957     }
958     if (ODR_MASK_GET(req->options, Z_Options_scan) && assoc->init->bend_scan)
959     {
960         ODR_MASK_SET(resp->options, Z_Options_scan);
961         strcat(options, " scan");
962     }
963     if (ODR_MASK_GET(req->options, Z_Options_concurrentOperations))
964     {
965         ODR_MASK_SET(resp->options, Z_Options_concurrentOperations);
966         strcat(options, " concurop");
967     }
968     if (ODR_MASK_GET(req->options, Z_Options_sort) && assoc->init->bend_sort)
969     {
970         ODR_MASK_SET(resp->options, Z_Options_sort);
971         strcat(options, " sort");
972     }
973     if (ODR_MASK_GET(req->protocolVersion, Z_ProtocolVersion_1))
974     {
975         ODR_MASK_SET(resp->protocolVersion, Z_ProtocolVersion_1);
976         assoc->version = 2; /* 1 & 2 are equivalent */
977     }
978     if (ODR_MASK_GET(req->protocolVersion, Z_ProtocolVersion_2))
979     {
980         ODR_MASK_SET(resp->protocolVersion, Z_ProtocolVersion_2);
981         assoc->version = 2;
982     }
983     if (ODR_MASK_GET(req->protocolVersion, Z_ProtocolVersion_3))
984     {
985         ODR_MASK_SET(resp->protocolVersion, Z_ProtocolVersion_3);
986         assoc->version = 3;
987     }
988     yaz_log(LOG_LOG, "Negotiated to v%d: %s", assoc->version, options);
989     assoc->maximumRecordSize = *req->maximumRecordSize;
990     if (assoc->maximumRecordSize > control_block->maxrecordsize)
991         assoc->maximumRecordSize = control_block->maxrecordsize;
992     assoc->preferredMessageSize = *req->preferredMessageSize;
993     if (assoc->preferredMessageSize > assoc->maximumRecordSize)
994         assoc->preferredMessageSize = assoc->maximumRecordSize;
995     resp->preferredMessageSize = &assoc->preferredMessageSize;
996     resp->maximumRecordSize = &assoc->maximumRecordSize;
997
998     resp->implementationName = "GFS";
999
1000     if (assoc->init->implementation_name)
1001     {
1002         char *nv = (char *)
1003             odr_malloc (assoc->encode,
1004                         strlen(assoc->init->implementation_name) + 10 + 
1005                                strlen(resp->implementationName));
1006         sprintf (nv, "%s / %s",
1007                  resp->implementationName, assoc->init->implementation_name);
1008         resp->implementationName = nv;
1009     }
1010     if (assoc->init->implementation_version)
1011     {
1012         char *nv = (char *)
1013             odr_malloc (assoc->encode,
1014                         strlen(assoc->init->implementation_version) + 10 + 
1015                                strlen(resp->implementationVersion));
1016         sprintf (nv, "YAZ %s / %s",
1017                  resp->implementationVersion,
1018                  assoc->init->implementation_version);
1019         resp->implementationVersion = nv;
1020     }
1021
1022     if (binitres->errcode)
1023     {
1024         yaz_log(LOG_LOG, "Connection rejected by backend.");
1025         *resp->result = 0;
1026         assoc->state = ASSOC_DEAD;
1027     }
1028     else
1029         assoc->state = ASSOC_UP;
1030     return apdu;
1031 }
1032
1033 /*
1034  * These functions should be merged.
1035  */
1036
1037 static void set_addinfo (Z_DefaultDiagFormat *dr, char *addinfo)
1038 {
1039 #if ASN_COMPILED
1040     dr->which = Z_DefaultDiagFormat_v2Addinfo;
1041     dr->u.v2Addinfo = addinfo ? addinfo : "";
1042 #else
1043     dr->which = Z_DiagForm_v2AddInfo;
1044     dr->addinfo = addinfo ? addinfo : "";
1045 #endif
1046 }
1047
1048 /*
1049  * nonsurrogate diagnostic record.
1050  */
1051 static Z_Records *diagrec(association *assoc, int error, char *addinfo)
1052 {
1053     int oid[OID_SIZE];
1054     Z_Records *rec = (Z_Records *)
1055         odr_malloc (assoc->encode, sizeof(*rec));
1056     oident bib1;
1057     int *err = (int *)
1058         odr_malloc (assoc->encode, sizeof(*err));
1059     Z_DiagRec *drec = (Z_DiagRec *)
1060         odr_malloc (assoc->encode, sizeof(*drec));
1061     Z_DefaultDiagFormat *dr = (Z_DefaultDiagFormat *)
1062         odr_malloc (assoc->encode, sizeof(*dr));
1063
1064     bib1.proto = assoc->proto;
1065     bib1.oclass = CLASS_DIAGSET;
1066     bib1.value = VAL_BIB1;
1067
1068     yaz_log(LOG_DEBUG, "Diagnostic: %d -- %s", error, addinfo ? addinfo :
1069         "NULL");
1070     *err = error;
1071     rec->which = Z_Records_NSD;
1072 #if ASN_COMPILED
1073     rec->u.nonSurrogateDiagnostic = dr;
1074 #else
1075     rec->u.nonSurrogateDiagnostic = drec;
1076     drec->which = Z_DiagRec_defaultFormat;
1077     drec->u.defaultFormat = dr;
1078 #endif
1079     dr->diagnosticSetId =
1080         odr_oiddup (assoc->encode, oid_ent_to_oid(&bib1, oid));
1081     dr->condition = err;
1082     set_addinfo (dr, addinfo);
1083     return rec;
1084 }
1085
1086 /*
1087  * surrogate diagnostic.
1088  */
1089 static Z_NamePlusRecord *surrogatediagrec(association *assoc, char *dbname,
1090                                           int error, char *addinfo)
1091 {
1092     int oid[OID_SIZE];
1093     Z_NamePlusRecord *rec = (Z_NamePlusRecord *)
1094         odr_malloc (assoc->encode, sizeof(*rec));
1095     int *err = (int *)odr_malloc (assoc->encode, sizeof(*err));
1096     oident bib1;
1097     Z_DiagRec *drec = (Z_DiagRec *)odr_malloc (assoc->encode, sizeof(*drec));
1098     Z_DefaultDiagFormat *dr = (Z_DefaultDiagFormat *)
1099         odr_malloc (assoc->encode, sizeof(*dr));
1100     
1101     bib1.proto = assoc->proto;
1102     bib1.oclass = CLASS_DIAGSET;
1103     bib1.value = VAL_BIB1;
1104
1105     yaz_log(LOG_DEBUG, "SurrogateDiagnotic: %d -- %s", error, addinfo);
1106     *err = error;
1107     rec->databaseName = dbname;
1108     rec->which = Z_NamePlusRecord_surrogateDiagnostic;
1109     rec->u.surrogateDiagnostic = drec;
1110     drec->which = Z_DiagRec_defaultFormat;
1111     drec->u.defaultFormat = dr;
1112     dr->diagnosticSetId = odr_oiddup (assoc->encode,
1113                                       oid_ent_to_oid(&bib1, oid));
1114     dr->condition = err;
1115     set_addinfo (dr, addinfo);
1116
1117     return rec;
1118 }
1119
1120 /*
1121  * multiple nonsurrogate diagnostics.
1122  */
1123 static Z_DiagRecs *diagrecs(association *assoc, int error, char *addinfo)
1124 {
1125     int oid[OID_SIZE];
1126     Z_DiagRecs *recs = (Z_DiagRecs *)odr_malloc (assoc->encode, sizeof(*recs));
1127     int *err = (int *)odr_malloc (assoc->encode, sizeof(*err));
1128     oident bib1;
1129     Z_DiagRec **recp = (Z_DiagRec **)odr_malloc (assoc->encode, sizeof(*recp));
1130     Z_DiagRec *drec = (Z_DiagRec *)odr_malloc (assoc->encode, sizeof(*drec));
1131     Z_DefaultDiagFormat *rec = (Z_DefaultDiagFormat *)odr_malloc (assoc->encode, sizeof(*rec));
1132
1133     yaz_log(LOG_DEBUG, "DiagRecs: %d -- %s", error, addinfo ? addinfo : "");
1134     bib1.proto = assoc->proto;
1135     bib1.oclass = CLASS_DIAGSET;
1136     bib1.value = VAL_BIB1;
1137
1138     *err = error;
1139     recs->num_diagRecs = 1;
1140     recs->diagRecs = recp;
1141     recp[0] = drec;
1142     drec->which = Z_DiagRec_defaultFormat;
1143     drec->u.defaultFormat = rec;
1144
1145     rec->diagnosticSetId = odr_oiddup (assoc->encode,
1146                                       oid_ent_to_oid(&bib1, oid));
1147     rec->condition = err;
1148
1149 #ifdef ASN_COMPILED
1150     rec->which = Z_DefaultDiagFormat_v2Addinfo;
1151     rec->u.v2Addinfo = addinfo ? addinfo : "";
1152 #else
1153     rec->which = Z_DiagForm_v2AddInfo;
1154     rec->addinfo = addinfo ? addinfo : "";
1155 #endif
1156     return recs;
1157 }
1158
1159 static Z_Records *pack_records(association *a, char *setname, int start,
1160                                int *num, Z_RecordComposition *comp,
1161                                int *next, int *pres, oid_value format,
1162                                Z_ReferenceId *referenceId,
1163                                int *oid)
1164 {
1165     int recno, total_length = 0, toget = *num, dumped_records = 0;
1166     Z_Records *records =
1167         (Z_Records *) odr_malloc (a->encode, sizeof(*records));
1168     Z_NamePlusRecordList *reclist =
1169         (Z_NamePlusRecordList *) odr_malloc (a->encode, sizeof(*reclist));
1170     Z_NamePlusRecord **list =
1171         (Z_NamePlusRecord **) odr_malloc (a->encode, sizeof(*list) * toget);
1172
1173     records->which = Z_Records_DBOSD;
1174     records->u.databaseOrSurDiagnostics = reclist;
1175     reclist->num_records = 0;
1176     reclist->records = list;
1177     *pres = Z_PRES_SUCCESS;
1178     *num = 0;
1179     *next = 0;
1180
1181     yaz_log(LOG_LOG, "Request to pack %d+%d", start, toget);
1182     yaz_log(LOG_DEBUG, "pms=%d, mrs=%d", a->preferredMessageSize,
1183         a->maximumRecordSize);
1184     for (recno = start; reclist->num_records < toget; recno++)
1185     {
1186         bend_fetch_rr freq;
1187         Z_NamePlusRecord *thisrec;
1188         int this_length = 0;
1189         /*
1190          * we get the number of bytes allocated on the stream before any
1191          * allocation done by the backend - this should give us a reasonable
1192          * idea of the total size of the data so far.
1193          */
1194         total_length = odr_total(a->encode) - dumped_records;
1195         freq.errcode = 0;
1196         freq.errstring = 0;
1197         freq.basename = 0;
1198         freq.len = 0;
1199         freq.record = 0;
1200         freq.last_in_set = 0;
1201         freq.setname = setname;
1202         freq.surrogate_flag = 0;
1203         freq.number = recno;
1204         freq.comp = comp;
1205         freq.request_format = format;
1206         freq.request_format_raw = oid;
1207         freq.output_format = format;
1208         freq.output_format_raw = 0;
1209         freq.stream = a->encode;
1210         freq.print = a->print;
1211         freq.surrogate_flag = 0;
1212         freq.referenceId = referenceId;
1213         (*a->init->bend_fetch)(a->backend, &freq);
1214         /* backend should be able to signal whether error is system-wide
1215            or only pertaining to current record */
1216         if (freq.errcode)
1217         {
1218             if (!freq.surrogate_flag)
1219             {
1220                 *pres = Z_PRES_FAILURE;
1221                 return diagrec(a, freq.errcode, freq.errstring);
1222             }
1223             reclist->records[reclist->num_records] =
1224                 surrogatediagrec(a, freq.basename, freq.errcode,
1225                                  freq.errstring);
1226             reclist->num_records++;
1227             *next = freq.last_in_set ? 0 : recno + 1;
1228             continue;
1229         }
1230         if (freq.len >= 0)
1231             this_length = freq.len;
1232         else
1233             this_length = odr_total(a->encode) - total_length;
1234         yaz_log(LOG_DEBUG, "  fetched record, len=%d, total=%d",
1235             this_length, total_length);
1236         if (this_length + total_length > a->preferredMessageSize)
1237         {
1238             /* record is small enough, really */
1239             if (this_length <= a->preferredMessageSize)
1240             {
1241                 yaz_log(LOG_DEBUG, "  Dropped last normal-sized record");
1242                 *pres = Z_PRES_PARTIAL_2;
1243                 break;
1244             }
1245             /* record can only be fetched by itself */
1246             if (this_length < a->maximumRecordSize)
1247             {
1248                 yaz_log(LOG_DEBUG, "  Record > prefmsgsz");
1249                 if (toget > 1)
1250                 {
1251                     yaz_log(LOG_DEBUG, "  Dropped it");
1252                     reclist->records[reclist->num_records] =
1253                          surrogatediagrec(a, freq.basename, 16, 0);
1254                     reclist->num_records++;
1255                     *next = freq.last_in_set ? 0 : recno + 1;
1256                     dumped_records += this_length;
1257                     continue;
1258                 }
1259             }
1260             else /* too big entirely */
1261             {
1262                 yaz_log(LOG_DEBUG, "Record > maxrcdsz");
1263                 reclist->records[reclist->num_records] =
1264                     surrogatediagrec(a, freq.basename, 17, 0);
1265                 reclist->num_records++;
1266                 *next = freq.last_in_set ? 0 : recno + 1;
1267                 dumped_records += this_length;
1268                 continue;
1269             }
1270         }
1271
1272         if (!(thisrec = (Z_NamePlusRecord *)
1273               odr_malloc(a->encode, sizeof(*thisrec))))
1274             return 0;
1275         if (!(thisrec->databaseName = (char *)odr_malloc(a->encode,
1276             strlen(freq.basename) + 1)))
1277             return 0;
1278         strcpy(thisrec->databaseName, freq.basename);
1279         thisrec->which = Z_NamePlusRecord_databaseRecord;
1280
1281         if (freq.output_format_raw)
1282         {
1283             struct oident *ident = oid_getentbyoid(freq.output_format_raw);
1284             freq.output_format = ident->value;
1285         }
1286         thisrec->u.databaseRecord = z_ext_record(a->encode, freq.output_format,
1287                                                  freq.record, freq.len);
1288         if (!thisrec->u.databaseRecord)
1289             return 0;
1290         reclist->records[reclist->num_records] = thisrec;
1291         reclist->num_records++;
1292         *next = freq.last_in_set ? 0 : recno + 1;
1293     }
1294     *num = reclist->num_records;
1295     return records;
1296 }
1297
1298 static Z_APDU *process_searchRequest(association *assoc, request *reqb,
1299     int *fd)
1300 {
1301     Z_SearchRequest *req = reqb->request->u.searchRequest;
1302     bend_search_rr *bsrr = 
1303         (bend_search_rr *)nmem_malloc (reqb->request_mem, sizeof(*bsrr));
1304     
1305     yaz_log(LOG_LOG, "Got SearchRequest.");
1306     bsrr->fd = fd;
1307     bsrr->request = reqb;
1308     bsrr->association = assoc;
1309     bsrr->referenceId = req->referenceId;
1310     save_referenceId (reqb, bsrr->referenceId);
1311
1312     yaz_log (LOG_LOG, "ResultSet '%s'", req->resultSetName);
1313     if (req->databaseNames)
1314     {
1315         int i;
1316         for (i = 0; i < req->num_databaseNames; i++)
1317             yaz_log (LOG_LOG, "Database '%s'", req->databaseNames[i]);
1318     }
1319     switch (req->query->which)
1320     {
1321     case Z_Query_type_1: case Z_Query_type_101:
1322         log_rpn_query (req->query->u.type_1);
1323     }
1324     if (assoc->init->bend_search)
1325     {
1326         bsrr->setname = req->resultSetName;
1327         bsrr->replace_set = *req->replaceIndicator;
1328         bsrr->num_bases = req->num_databaseNames;
1329         bsrr->basenames = req->databaseNames;
1330         bsrr->query = req->query;
1331         bsrr->stream = assoc->encode;
1332         bsrr->decode = assoc->decode;
1333         bsrr->print = assoc->print;
1334         bsrr->errcode = 0;
1335         bsrr->hits = 0;
1336         bsrr->errstring = NULL;
1337         (assoc->init->bend_search)(assoc->backend, bsrr);
1338         if (!bsrr->request)
1339             return 0;
1340     }
1341 #if 0
1342     else
1343     {
1344         bend_searchrequest bsrq;
1345         bend_searchresult *bsrt;
1346
1347         bsrq.setname = req->resultSetName;
1348         bsrq.replace_set = *req->replaceIndicator;
1349         bsrq.num_bases = req->num_databaseNames;
1350         bsrq.basenames = req->databaseNames;
1351         bsrq.query = req->query;
1352         bsrq.referenceId = req->referenceId;
1353         bsrq.stream = assoc->encode;
1354         bsrq.decode = assoc->decode;
1355         bsrq.print = assoc->print;
1356         if (!(bsrt = bend_search (assoc->backend, &bsrq, fd)))
1357             return 0;
1358         bsrr->hits = bsrt->hits;
1359         bsrr->errcode = bsrt->errcode;
1360         bsrr->errstring = bsrt->errstring;
1361     }
1362 #endif
1363     return response_searchRequest(assoc, reqb, bsrr, fd);
1364 }
1365
1366 int bend_searchresponse(void *handle, bend_search_rr *bsrr) {return 0;}
1367
1368 /*
1369  * Prepare a searchresponse based on the backend results. We probably want
1370  * to look at making the fetching of records nonblocking as well, but
1371  * so far, we'll keep things simple.
1372  * If bsrt is null, that means we're called in response to a communications
1373  * event, and we'll have to get the response for ourselves.
1374  */
1375 static Z_APDU *response_searchRequest(association *assoc, request *reqb,
1376     bend_search_rr *bsrt, int *fd)
1377 {
1378     Z_SearchRequest *req = reqb->request->u.searchRequest;
1379     Z_APDU *apdu = (Z_APDU *)odr_malloc (assoc->encode, sizeof(*apdu));
1380     Z_SearchResponse *resp = (Z_SearchResponse *)
1381         odr_malloc (assoc->encode, sizeof(*resp));
1382     int *nulint = (int *)odr_malloc (assoc->encode, sizeof(*nulint));
1383     bool_t *sr = (bool_t *)odr_malloc (assoc->encode, sizeof(*sr));
1384     int *next = (int *)odr_malloc (assoc->encode, sizeof(*next));
1385     int *none = (int *)odr_malloc (assoc->encode, sizeof(*none));
1386
1387     *nulint = 0;
1388     *sr = 1;
1389     *next = 0;
1390     *none = Z_RES_NONE;
1391
1392     apdu->which = Z_APDU_searchResponse;
1393     apdu->u.searchResponse = resp;
1394     resp->referenceId = req->referenceId;
1395     resp->additionalSearchInfo = 0;
1396     resp->otherInfo = 0;
1397     *fd = -1;
1398     if (!bsrt && !bend_searchresponse(assoc->backend, bsrt))
1399     {
1400         yaz_log(LOG_FATAL, "Bad result from backend");
1401         return 0;
1402     }
1403     else if (bsrt->errcode)
1404     {
1405         resp->records = diagrec(assoc, bsrt->errcode, bsrt->errstring);
1406         resp->resultCount = nulint;
1407         resp->numberOfRecordsReturned = nulint;
1408         resp->nextResultSetPosition = nulint;
1409         resp->searchStatus = nulint;
1410         resp->resultSetStatus = none;
1411         resp->presentStatus = 0;
1412     }
1413     else
1414     {
1415         int *toget = (int *)odr_malloc (assoc->encode, sizeof(*toget));
1416         int *presst = (int *)odr_malloc (assoc->encode, sizeof(*presst));
1417         Z_RecordComposition comp, *compp = 0;
1418
1419         *toget = 0;
1420         *presst = 0;
1421         resp->records = 0;
1422         resp->resultCount = &bsrt->hits;
1423
1424         comp.which = Z_RecordComp_simple;
1425         /* how many records does the user agent want, then? */
1426         if (bsrt->hits <= *req->smallSetUpperBound)
1427         {
1428             *toget = bsrt->hits;
1429             if ((comp.u.simple = req->smallSetElementSetNames))
1430                 compp = &comp;
1431         }
1432         else if (bsrt->hits < *req->largeSetLowerBound)
1433         {
1434             *toget = *req->mediumSetPresentNumber;
1435             if (*toget > bsrt->hits)
1436                 *toget = bsrt->hits;
1437             if ((comp.u.simple = req->mediumSetElementSetNames))
1438                 compp = &comp;
1439         }
1440         else
1441             *toget = 0;
1442
1443         if (*toget && !resp->records)
1444         {
1445             oident *prefformat;
1446             oid_value form;
1447
1448             if (!(prefformat = oid_getentbyoid(req->preferredRecordSyntax)) ||
1449                 prefformat->oclass != CLASS_RECSYN)
1450                 form = VAL_NONE;
1451             else
1452                 form = prefformat->value;
1453             resp->records = pack_records(assoc, req->resultSetName, 1,
1454                 toget, compp, next, presst, form, req->referenceId,
1455                                          req->preferredRecordSyntax);
1456             if (!resp->records)
1457                 return 0;
1458             resp->numberOfRecordsReturned = toget;
1459             resp->nextResultSetPosition = next;
1460             resp->searchStatus = sr;
1461             resp->resultSetStatus = 0;
1462             resp->presentStatus = presst;
1463         }
1464         else
1465         {
1466             if (*resp->resultCount)
1467                 *next = 1;
1468             resp->numberOfRecordsReturned = nulint;
1469             resp->nextResultSetPosition = next;
1470             resp->searchStatus = sr;
1471             resp->resultSetStatus = 0;
1472             resp->presentStatus = 0;
1473         }
1474     }
1475     return apdu;
1476 }
1477
1478 /*
1479  * Maybe we got a little over-friendly when we designed bend_fetch to
1480  * get only one record at a time. Some backends can optimise multiple-record
1481  * fetches, and at any rate, there is some overhead involved in
1482  * all that selecting and hopping around. Problem is, of course, that the
1483  * frontend can't know ahead of time how many records it'll need to
1484  * fill the negotiated PDU size. Annoying. Segmentation or not, Z/SR
1485  * is downright lousy as a bulk data transfer protocol.
1486  *
1487  * To start with, we'll do the fetching of records from the backend
1488  * in one operation: To save some trips in and out of the event-handler,
1489  * and to simplify the interface to pack_records. At any rate, asynch
1490  * operation is more fun in operations that have an unpredictable execution
1491  * speed - which is normally more true for search than for present.
1492  */
1493 static Z_APDU *process_presentRequest(association *assoc, request *reqb,
1494                                       int *fd)
1495 {
1496     Z_PresentRequest *req = reqb->request->u.presentRequest;
1497     oident *prefformat;
1498     oid_value form;
1499     Z_APDU *apdu;
1500     Z_PresentResponse *resp;
1501     int *presst;
1502     int *next;
1503     int *num;
1504
1505     yaz_log(LOG_LOG, "Got PresentRequest.");
1506
1507     if (!(prefformat = oid_getentbyoid(req->preferredRecordSyntax)) ||
1508         prefformat->oclass != CLASS_RECSYN)
1509         form = VAL_NONE;
1510     else
1511         form = prefformat->value;
1512     if (assoc->init->bend_present)
1513     {
1514         bend_present_rr *bprr = (bend_present_rr *)
1515             nmem_malloc (reqb->request_mem, sizeof(*bprr));
1516         bprr->setname = req->resultSetId;
1517         bprr->start = *req->resultSetStartPoint;
1518         bprr->number = *req->numberOfRecordsRequested;
1519         bprr->format = form;
1520         bprr->comp = req->recordComposition;
1521         bprr->referenceId = req->referenceId;
1522         bprr->stream = assoc->encode;
1523         bprr->print = assoc->print;
1524         bprr->request = reqb;
1525         bprr->association = assoc;
1526         bprr->errcode = 0;
1527         bprr->errstring = NULL;
1528         (*assoc->init->bend_present)(assoc->backend, bprr);
1529         
1530         if (!bprr->request)
1531             return 0;
1532     }
1533     apdu = (Z_APDU *)odr_malloc (assoc->encode, sizeof(*apdu));
1534     resp = (Z_PresentResponse *)odr_malloc (assoc->encode, sizeof(*resp));
1535     presst = (int *)odr_malloc (assoc->encode, sizeof(*presst));
1536     next = (int *)odr_malloc (assoc->encode, sizeof(*next));
1537     num = (int *)odr_malloc (assoc->encode, sizeof(*num));
1538     *presst = 0;
1539     *next = 0;
1540     *num = *req->numberOfRecordsRequested;
1541     
1542     apdu->which = Z_APDU_presentResponse;
1543     apdu->u.presentResponse = resp;
1544     resp->referenceId = req->referenceId;
1545     resp->otherInfo = 0;
1546     
1547     resp->records =
1548         pack_records(assoc, req->resultSetId, *req->resultSetStartPoint,
1549                      num, req->recordComposition, next, presst, form,
1550                      req->referenceId, req->preferredRecordSyntax);
1551     if (!resp->records)
1552         return 0;
1553     resp->numberOfRecordsReturned = num;
1554     resp->presentStatus = presst;
1555     resp->nextResultSetPosition = next;
1556     
1557     return apdu;
1558 }
1559
1560 #if 0
1561 static int bend_default_scan (void *handle, bend_scan_rr *rr)
1562 {
1563     bend_scanrequest srq;
1564     bend_scanresult *srs;
1565
1566     srq.num_bases = rr->num_bases;
1567     srq.basenames = rr->basenames;
1568     srq.attributeset = rr->attributeset;
1569     srq.referenceId = rr->referenceId;
1570     srq.term = rr->term;
1571     srq.term_position = rr->term_position;
1572     srq.num_entries = rr->num_entries;
1573     srq.stream = rr->stream;
1574     srq.print = rr->print;
1575     
1576     srs = bend_scan(handle, &srq, 0);
1577
1578     rr->term_position = srs->term_position;
1579     rr->num_entries = srs->num_entries;
1580     rr->entries = srs->entries;
1581     rr->status = srs->status;
1582     rr->errcode = srs->errcode;
1583     rr->errstring = srs->errstring;
1584     return 0;
1585 }
1586 #endif
1587
1588 /*
1589  * Scan was implemented rather in a hurry, and with support for only the basic
1590  * elements of the service in the backend API. Suggestions are welcome.
1591  */
1592 static Z_APDU *process_scanRequest(association *assoc, request *reqb, int *fd)
1593 {
1594     Z_ScanRequest *req = reqb->request->u.scanRequest;
1595     Z_APDU *apdu = (Z_APDU *)odr_malloc (assoc->encode, sizeof(*apdu));
1596     Z_ScanResponse *res = (Z_ScanResponse *)
1597         odr_malloc (assoc->encode, sizeof(*res));
1598     int *scanStatus = (int *)
1599         odr_malloc (assoc->encode, sizeof(*scanStatus));
1600     int *numberOfEntriesReturned = (int *)
1601         odr_malloc (assoc->encode, sizeof(*numberOfEntriesReturned));
1602     Z_ListEntries *ents = (Z_ListEntries *)
1603         odr_malloc (assoc->encode, sizeof(*ents));
1604     Z_DiagRecs *diagrecs_p = NULL;
1605     oident *attent;
1606     oident *attset;
1607
1608     yaz_log(LOG_LOG, "Got ScanRequest");
1609     *scanStatus = Z_Scan_failure;
1610     *numberOfEntriesReturned = 0;
1611
1612     apdu->which = Z_APDU_scanResponse;
1613     apdu->u.scanResponse = res;
1614     res->referenceId = req->referenceId;
1615     res->stepSize = 0;
1616     res->scanStatus = scanStatus;
1617     res->numberOfEntriesReturned = numberOfEntriesReturned;
1618     res->positionOfTerm = 0;
1619     res->entries = ents;
1620     ents->num_entries = 0;
1621     ents->entries = NULL;
1622     ents->num_nonsurrogateDiagnostics = 0;
1623     ents->nonsurrogateDiagnostics = NULL;
1624     res->attributeSet = 0;
1625     res->otherInfo = 0;
1626
1627     if (req->attributeSet && (!(attent = oid_getentbyoid(req->attributeSet)) ||
1628                               attent->oclass != CLASS_ATTSET
1629                               || attent->value != VAL_BIB1))
1630         diagrecs_p = diagrecs(assoc, 121, 0);
1631     else if (req->stepSize && *req->stepSize > 0)
1632         diagrecs_p = diagrecs(assoc, 205, 0);
1633     else
1634     {
1635         bend_scan_rr *bsrr = (bend_scan_rr *)
1636             odr_malloc (assoc->encode, sizeof(*bsrr));
1637         if (req->databaseNames)
1638         {
1639             int i;
1640             for (i = 0; i < req->num_databaseNames; i++)
1641                 yaz_log (LOG_LOG, "Database '%s'", req->databaseNames[i]);
1642         }
1643         bsrr->num_bases = req->num_databaseNames;
1644         bsrr->basenames = req->databaseNames;
1645         bsrr->num_entries = *req->numberOfTermsRequested;
1646         bsrr->term = req->termListAndStartPoint;
1647         bsrr->referenceId = req->referenceId;
1648         bsrr->stream = assoc->encode;
1649         bsrr->print = assoc->print;
1650         if (!(attset = oid_getentbyoid(req->attributeSet)) ||
1651             attset->oclass != CLASS_RECSYN)
1652             bsrr->attributeset = VAL_NONE;
1653         else
1654             bsrr->attributeset = attset->value;
1655         log_scan_term (req->termListAndStartPoint, attset->value);
1656         bsrr->term_position = req->preferredPositionInResponse ?
1657             *req->preferredPositionInResponse : 1;
1658         ((int (*)(void *, bend_scan_rr *))
1659          (*assoc->init->bend_scan))(assoc->backend, bsrr);
1660         if (bsrr->errcode)
1661             diagrecs_p = diagrecs(assoc, bsrr->errcode, bsrr->errstring);
1662         else
1663         {
1664             int i;
1665             Z_Entry **tab = (Z_Entry **)
1666                 odr_malloc (assoc->encode, sizeof(*tab) * bsrr->num_entries);
1667             
1668             if (bsrr->status == BEND_SCAN_PARTIAL)
1669                 *scanStatus = Z_Scan_partial_5;
1670             else
1671                 *scanStatus = Z_Scan_success;
1672             ents->entries = tab;
1673             ents->num_entries = bsrr->num_entries;
1674             res->numberOfEntriesReturned = &ents->num_entries;      
1675             res->positionOfTerm = &bsrr->term_position;
1676             for (i = 0; i < bsrr->num_entries; i++)
1677             {
1678                 Z_Entry *e;
1679                 Z_TermInfo *t;
1680                 Odr_oct *o;
1681                 
1682                 tab[i] = e = (Z_Entry *)odr_malloc(assoc->encode, sizeof(*e));
1683                 if (bsrr->entries[i].occurrences >= 0)
1684                 {
1685                     e->which = Z_Entry_termInfo;
1686                     e->u.termInfo = t = (Z_TermInfo *)
1687                         odr_malloc(assoc->encode, sizeof(*t));
1688                     t->suggestedAttributes = 0;
1689                     t->displayTerm = 0;
1690                     t->alternativeTerm = 0;
1691                     t->byAttributes = 0;
1692                     t->otherTermInfo = 0;
1693                     t->globalOccurrences = &bsrr->entries[i].occurrences;
1694                     t->term = (Z_Term *)
1695                         odr_malloc(assoc->encode, sizeof(*t->term));
1696                     t->term->which = Z_Term_general;
1697                     t->term->u.general = o =
1698                         (Odr_oct *)odr_malloc(assoc->encode, sizeof(Odr_oct));
1699                     o->buf = (unsigned char *)
1700                         odr_malloc(assoc->encode, o->len = o->size =
1701                                    strlen(bsrr->entries[i].term));
1702                     memcpy(o->buf, bsrr->entries[i].term, o->len);
1703                     yaz_log(LOG_DEBUG, "  term #%d: '%s' (%d)", i,
1704                          bsrr->entries[i].term, bsrr->entries[i].occurrences);
1705                 }
1706                 else
1707                 {
1708                     Z_DiagRecs *drecs = diagrecs (assoc,
1709                                                   bsrr->entries[i].errcode,
1710                                                   bsrr->entries[i].errstring);
1711                     assert (drecs->num_diagRecs == 1);
1712                     e->which = Z_Entry_surrogateDiagnostic;
1713                     assert (drecs->diagRecs[0]);
1714                     e->u.surrogateDiagnostic = drecs->diagRecs[0];
1715                 }
1716             }
1717         }
1718     }
1719     if (diagrecs_p)
1720     {
1721         ents->num_nonsurrogateDiagnostics = diagrecs_p->num_diagRecs;
1722         ents->nonsurrogateDiagnostics = diagrecs_p->diagRecs;
1723     }
1724     return apdu;
1725 }
1726
1727 static Z_APDU *process_sortRequest(association *assoc, request *reqb,
1728     int *fd)
1729 {
1730     Z_SortRequest *req = reqb->request->u.sortRequest;
1731     Z_SortResponse *res = (Z_SortResponse *)
1732         odr_malloc (assoc->encode, sizeof(*res));
1733     bend_sort_rr *bsrr = (bend_sort_rr *)
1734         odr_malloc (assoc->encode, sizeof(*bsrr));
1735
1736     Z_APDU *apdu = (Z_APDU *)odr_malloc (assoc->encode, sizeof(*apdu));
1737
1738     yaz_log(LOG_LOG, "Got SortRequest.");
1739
1740 #ifdef ASN_COMPILED
1741     bsrr->num_input_setnames = req->num_inputResultSetNames;
1742     bsrr->input_setnames = req->inputResultSetNames;
1743 #else
1744     bsrr->num_input_setnames = req->inputResultSetNames->num_strings;
1745     bsrr->input_setnames = req->inputResultSetNames->strings;
1746 #endif
1747     bsrr->referenceId = req->referenceId;
1748     bsrr->output_setname = req->sortedResultSetName;
1749     bsrr->sort_sequence = req->sortSequence;
1750     bsrr->stream = assoc->encode;
1751     bsrr->print = assoc->print;
1752
1753     bsrr->sort_status = Z_SortStatus_failure;
1754     bsrr->errcode = 0;
1755     bsrr->errstring = 0;
1756     
1757     (*assoc->init->bend_sort)(assoc->backend, bsrr);
1758     
1759     res->referenceId = bsrr->referenceId;
1760     res->sortStatus = (int *)
1761         odr_malloc (assoc->encode, sizeof(*res->sortStatus));
1762     *res->sortStatus = bsrr->sort_status;
1763     res->resultSetStatus = 0;
1764     if (bsrr->errcode)
1765     {
1766         Z_DiagRecs *dr = diagrecs (assoc, bsrr->errcode, bsrr->errstring);
1767 #ifdef ASN_COMPILED
1768         res->diagnostics = dr->diagRecs;
1769         res->num_diagnostics = dr->num_diagRecs;
1770 #else
1771         res->diagnostics = dr;
1772 #endif
1773     }
1774     else
1775     {
1776 #ifdef ASN_COMPILED
1777         res->num_diagnostics = 0;
1778 #endif
1779         res->diagnostics = 0;
1780     }
1781     res->otherInfo = 0;
1782
1783     apdu->which = Z_APDU_sortResponse;
1784     apdu->u.sortResponse = res;
1785     return apdu;
1786 }
1787
1788 static Z_APDU *process_deleteRequest(association *assoc, request *reqb,
1789     int *fd)
1790 {
1791     Z_DeleteResultSetRequest *req = reqb->request->u.deleteResultSetRequest;
1792     Z_DeleteResultSetResponse *res = (Z_DeleteResultSetResponse *)
1793         odr_malloc (assoc->encode, sizeof(*res));
1794     bend_delete_rr *bdrr = (bend_delete_rr *)
1795         odr_malloc (assoc->encode, sizeof(*bdrr));
1796     Z_APDU *apdu = (Z_APDU *)odr_malloc (assoc->encode, sizeof(*apdu));
1797
1798     yaz_log(LOG_LOG, "Got DeleteRequest.");
1799
1800     bdrr->num_setnames = req->num_resultSetList;
1801     bdrr->setnames = req->resultSetList;
1802     bdrr->stream = assoc->encode;
1803     bdrr->print = assoc->print;
1804     bdrr->function = *req->deleteFunction;
1805     bdrr->referenceId = req->referenceId;
1806     bdrr->statuses = 0;
1807     if (bdrr->num_setnames > 0)
1808     {
1809         int i;
1810         bdrr->statuses = odr_malloc(assoc->encode, sizeof(*bdrr->statuses) *
1811                                     bdrr->num_setnames);
1812         for (i = 0; i < bdrr->num_setnames; i++)
1813             bdrr->statuses[i] = 0;
1814     }
1815     (*assoc->init->bend_delete)(assoc->backend, bdrr);
1816     
1817     res->referenceId = req->referenceId;
1818
1819     res->deleteOperationStatus = (int *)
1820         odr_malloc (assoc->encode, sizeof(*res->deleteOperationStatus));
1821     *res->deleteOperationStatus = bdrr->delete_status;
1822
1823     res->deleteListStatuses = 0;
1824     if (bdrr->num_setnames > 0)
1825     {
1826         int i;
1827         res->deleteListStatuses = odr_malloc(assoc->encode,
1828                                              sizeof(*res->deleteListStatuses));
1829         res->deleteListStatuses->num = bdrr->num_setnames;
1830         res->deleteListStatuses->elements =
1831             odr_malloc (assoc->encode,
1832                         sizeof(*res->deleteListStatuses->elements) *
1833                         bdrr->num_setnames);
1834         for (i = 0; i<bdrr->num_setnames; i++)
1835         {
1836             res->deleteListStatuses->elements[i] =
1837                 odr_malloc (assoc->encode,
1838                             sizeof(**res->deleteListStatuses->elements));
1839             res->deleteListStatuses->elements[i]->status = bdrr->statuses+i;
1840             res->deleteListStatuses->elements[i]->id =
1841                 odr_strdup (assoc->encode, bdrr->setnames[i]);
1842             
1843         }
1844     }
1845     res->numberNotDeleted = 0;
1846     res->bulkStatuses = 0;
1847     res->deleteMessage = 0;
1848     res->otherInfo = 0;
1849
1850     apdu->which = Z_APDU_deleteResultSetResponse;
1851     apdu->u.deleteResultSetResponse = res;
1852     return apdu;
1853 }
1854
1855 static void process_close(association *assoc, request *reqb)
1856 {
1857     Z_Close *req = reqb->request->u.close;
1858     static char *reasons[] =
1859     {
1860         "finished",
1861         "shutdown",
1862         "systemProblem",
1863         "costLimit",
1864         "resources",
1865         "securityViolation",
1866         "protocolError",
1867         "lackOfActivity",
1868         "peerAbort",
1869         "unspecified"
1870     };
1871
1872     yaz_log(LOG_LOG, "Got Close, reason %s, message %s",
1873         reasons[*req->closeReason], req->diagnosticInformation ?
1874         req->diagnosticInformation : "NULL");
1875     if (assoc->version < 3) /* to make do_force respond with close */
1876         assoc->version = 3;
1877     do_close_req(assoc, Z_Close_finished,
1878                  "Association terminated by client", reqb);
1879 }
1880
1881 void save_referenceId (request *reqb, Z_ReferenceId *refid)
1882 {
1883     if (refid)
1884     {
1885         reqb->len_refid = refid->len;
1886         reqb->refid = (char *)nmem_malloc (reqb->request_mem, refid->len);
1887         memcpy (reqb->refid, refid->buf, refid->len);
1888     }
1889     else
1890     {
1891         reqb->len_refid = 0;
1892         reqb->refid = NULL;
1893     }
1894 }
1895
1896 void bend_request_send (bend_association a, bend_request req, Z_APDU *res)
1897 {
1898     process_response (a, req, res);
1899 }
1900
1901 bend_request bend_request_mk (bend_association a)
1902 {
1903     request *nreq = request_get (&a->outgoing);
1904     nreq->request_mem = nmem_create ();
1905     return nreq;
1906 }
1907
1908 Z_ReferenceId *bend_request_getid (ODR odr, bend_request req)
1909 {
1910     Z_ReferenceId *id;
1911     if (!req->refid)
1912         return 0;
1913     id = (Odr_oct *)odr_malloc (odr, sizeof(*odr));
1914     id->buf = (unsigned char *)odr_malloc (odr, req->len_refid);
1915     id->len = id->size = req->len_refid;
1916     memcpy (id->buf, req->refid, req->len_refid);
1917     return id;
1918 }
1919
1920 void bend_request_destroy (bend_request *req)
1921 {
1922     nmem_destroy((*req)->request_mem);
1923     request_release(*req);
1924     *req = NULL;
1925 }
1926
1927 int bend_backend_respond (bend_association a, bend_request req)
1928 {
1929     char *msg;
1930     int r;
1931     r = process_request (a, req, &msg);
1932     if (r < 0)
1933         logf (LOG_WARN, "%s", msg);
1934     return r;
1935 }
1936
1937 void bend_request_setdata(bend_request r, void *p)
1938 {
1939     r->clientData = p;
1940 }
1941
1942 void *bend_request_getdata(bend_request r)
1943 {
1944     return r->clientData;
1945 }
1946
1947 static Z_APDU *process_segmentRequest (association *assoc, request *reqb)
1948 {
1949     bend_segment_rr request;
1950
1951     request.segment = reqb->request->u.segmentRequest;
1952     request.stream = assoc->encode;
1953     request.decode = assoc->decode;
1954     request.print = assoc->print;
1955     request.association = assoc;
1956     
1957     (*assoc->init->bend_segment)(assoc->backend, &request);
1958
1959     return 0;
1960 }
1961
1962 static Z_APDU *process_ESRequest(association *assoc, request *reqb, int *fd)
1963 {
1964     bend_esrequest_rr esrequest;
1965
1966     Z_ExtendedServicesRequest *req = reqb->request->u.extendedServicesRequest;
1967     Z_APDU *apdu = zget_APDU(assoc->encode, Z_APDU_extendedServicesResponse);
1968
1969     Z_ExtendedServicesResponse *resp = apdu->u.extendedServicesResponse;
1970
1971     yaz_log(LOG_DEBUG,"inside Process esRequest");
1972
1973     esrequest.esr = reqb->request->u.extendedServicesRequest;
1974     esrequest.stream = assoc->encode;
1975     esrequest.decode = assoc->decode;
1976     esrequest.print = assoc->print;
1977     esrequest.errcode = 0;
1978     esrequest.errstring = NULL;
1979     esrequest.request = reqb;
1980     esrequest.association = assoc;
1981     esrequest.referenceId = req->referenceId;
1982     
1983     (*assoc->init->bend_esrequest)(assoc->backend, &esrequest);
1984     
1985     /* If the response is being delayed, return NULL */
1986     if (esrequest.request == NULL)
1987         return(NULL);
1988
1989     resp->referenceId = req->referenceId;
1990
1991     if (esrequest.errcode == -1)
1992     {
1993         /* Backend service indicates request will be processed */
1994         yaz_log(LOG_DEBUG,"Request could be processed...Accepted !");
1995         *resp->operationStatus = Z_ExtendedServicesResponse_accepted;
1996     }
1997     else if (esrequest.errcode == 0)
1998     {
1999         /* Backend service indicates request will be processed */
2000         yaz_log(LOG_DEBUG,"Request could be processed...Done !");
2001         *resp->operationStatus = Z_ExtendedServicesResponse_done;
2002     }
2003     else
2004     {
2005         Z_DiagRecs *diagRecs = diagrecs (assoc, esrequest.errcode,
2006                                          esrequest.errstring);
2007
2008         /* Backend indicates error, request will not be processed */
2009         yaz_log(LOG_DEBUG,"Request could not be processed...failure !");
2010         *resp->operationStatus = Z_ExtendedServicesResponse_failure;
2011         resp->num_diagnostics = diagRecs->num_diagRecs;
2012         resp->diagnostics = diagRecs->diagRecs;
2013     }
2014     /* Do something with the members of bend_extendedservice */
2015
2016     yaz_log(LOG_DEBUG,"Send the result apdu");
2017     return apdu;
2018 }