Use odr_prepend() to assemble "implementation*" strings.
[yaz-moved-to-github.git] / src / seshigh.c
1 /*
2  * Copyright (c) 1995-2003, Index Data
3  * See the file LICENSE for details.
4  *
5  * $Id: seshigh.c,v 1.3 2003-11-26 22:47:42 mike Exp $
6  */
7
8 /*
9  * Frontend server logic.
10  *
11  * This code receives incoming APDUs, and handles client requests by means
12  * of the backend API.
13  *
14  * Some of the code is getting quite involved, compared to simpler servers -
15  * primarily because it is asynchronous both in the communication with
16  * the user and the backend. We think the complexity will pay off in
17  * the form of greater flexibility when more asynchronous facilities
18  * are implemented.
19  *
20  * Memory management has become somewhat involved. In the simple case, where
21  * only one PDU is pending at a time, it will simply reuse the same memory,
22  * once it has found its working size. When we enable multiple concurrent
23  * operations, perhaps even with multiple parallel calls to the backend, it
24  * will maintain a pool of buffers for encoding and decoding, trying to
25  * minimize memory allocation/deallocation during normal operation.
26  *
27  */
28
29 #include <stdlib.h>
30 #include <stdio.h>
31 #include <sys/types.h>
32 #ifdef WIN32
33 #include <io.h>
34 #define S_ISREG(x) (x & _S_IFREG)
35 #include <process.h>
36 #include <sys/stat.h>
37 #else
38 #include <sys/stat.h>
39 #include <unistd.h>
40 #endif
41 #include <assert.h>
42 #include <ctype.h>
43
44 #include <yaz/yconfig.h>
45 #include <yaz/xmalloc.h>
46 #include <yaz/comstack.h>
47 #include "eventl.h"
48 #include "session.h"
49 #include <yaz/proto.h>
50 #include <yaz/oid.h>
51 #include <yaz/log.h>
52 #include <yaz/logrpn.h>
53 #include <yaz/statserv.h>
54 #include <yaz/diagbib1.h>
55 #include <yaz/charneg.h>
56 #include <yaz/otherinfo.h>
57 #include <yaz/yaz-util.h>
58 #include <yaz/pquery.h>
59
60 #include <yaz/srw.h>
61 #include <yaz/backend.h>
62
63 static void process_gdu_request(association *assoc, request *req);
64 static int process_z_request(association *assoc, request *req, char **msg);
65 void backend_response(IOCHAN i, int event);
66 static int process_gdu_response(association *assoc, request *req, Z_GDU *res);
67 static int process_z_response(association *assoc, request *req, Z_APDU *res);
68 static Z_APDU *process_initRequest(association *assoc, request *reqb);
69 static Z_External *init_diagnostics(ODR odr, int errcode, char *errstring);
70 static Z_APDU *process_searchRequest(association *assoc, request *reqb,
71     int *fd);
72 static Z_APDU *response_searchRequest(association *assoc, request *reqb,
73     bend_search_rr *bsrr, int *fd);
74 static Z_APDU *process_presentRequest(association *assoc, request *reqb,
75     int *fd);
76 static Z_APDU *process_scanRequest(association *assoc, request *reqb, int *fd);
77 static Z_APDU *process_sortRequest(association *assoc, request *reqb, int *fd);
78 static void process_close(association *assoc, request *reqb);
79 void save_referenceId (request *reqb, Z_ReferenceId *refid);
80 static Z_APDU *process_deleteRequest(association *assoc, request *reqb,
81     int *fd);
82 static Z_APDU *process_segmentRequest (association *assoc, request *reqb);
83
84 static FILE *apduf = 0; /* for use in static mode */
85 static statserv_options_block *control_block = 0;
86
87 static Z_APDU *process_ESRequest(association *assoc, request *reqb, int *fd);
88
89 /*
90  * Create and initialize a new association-handle.
91  *  channel  : iochannel for the current line.
92  *  link     : communications channel.
93  * Returns: 0 or a new association handle.
94  */
95 association *create_association(IOCHAN channel, COMSTACK link)
96 {
97     association *anew;
98
99     if (!control_block)
100         control_block = statserv_getcontrol();
101     if (!(anew = (association *)xmalloc(sizeof(*anew))))
102         return 0;
103     anew->init = 0;
104     anew->version = 0;
105     anew->client_chan = channel;
106     anew->client_link = link;
107     anew->cs_get_mask = 0;
108     anew->cs_put_mask = 0;
109     anew->cs_accept_mask = 0;
110     if (!(anew->decode = odr_createmem(ODR_DECODE)) ||
111         !(anew->encode = odr_createmem(ODR_ENCODE)))
112         return 0;
113     if (*control_block->apdufile)
114     {
115         char filename[256];
116         FILE *f;
117
118         strcpy(filename, control_block->apdufile);
119         if (!(anew->print = odr_createmem(ODR_PRINT)))
120             return 0;
121         if (*control_block->apdufile == '@')
122         {
123             odr_setprint(anew->print, yaz_log_file());
124         }       
125         else if (*control_block->apdufile != '-')
126         {
127             strcpy(filename, control_block->apdufile);
128             if (!control_block->dynamic)
129             {
130                 if (!apduf)
131                 {
132                     if (!(apduf = fopen(filename, "w")))
133                     {
134                         yaz_log(LOG_WARN|LOG_ERRNO, "%s", filename);
135                         return 0;
136                     }
137                     setvbuf(apduf, 0, _IONBF, 0);
138                 }
139                 f = apduf;
140             }
141             else 
142             {
143                 sprintf(filename + strlen(filename), ".%d", getpid());
144                 if (!(f = fopen(filename, "w")))
145                 {
146                     yaz_log(LOG_WARN|LOG_ERRNO, "%s", filename);
147                     return 0;
148                 }
149                 setvbuf(f, 0, _IONBF, 0);
150             }
151             odr_setprint(anew->print, f);
152         }
153     }
154     else
155         anew->print = 0;
156     anew->input_buffer = 0;
157     anew->input_buffer_len = 0;
158     anew->backend = 0;
159     anew->state = ASSOC_NEW;
160     request_initq(&anew->incoming);
161     request_initq(&anew->outgoing);
162     anew->proto = cs_getproto(link);
163     return anew;
164 }
165
166 /*
167  * Free association and release resources.
168  */
169 void destroy_association(association *h)
170 {
171     statserv_options_block *cb = statserv_getcontrol();
172     request *req;
173
174     xfree(h->init);
175     odr_destroy(h->decode);
176     odr_destroy(h->encode);
177     if (h->print)
178         odr_destroy(h->print);
179     if (h->input_buffer)
180     xfree(h->input_buffer);
181     if (h->backend)
182         (*cb->bend_close)(h->backend);
183     while ((req = request_deq(&h->incoming)))
184         request_release(req);
185     while ((req = request_deq(&h->outgoing)))
186         request_release(req);
187     request_delq(&h->incoming);
188     request_delq(&h->outgoing);
189     xfree(h);
190     xmalloc_trav("session closed");
191     if (control_block && control_block->one_shot)
192     {
193         exit (0);
194     }
195 }
196
197 static void do_close_req(association *a, int reason, char *message,
198                          request *req)
199 {
200     Z_APDU apdu;
201     Z_Close *cls = zget_Close(a->encode);
202     
203     /* Purge request queue */
204     while (request_deq(&a->incoming));
205     while (request_deq(&a->outgoing));
206     if (a->version >= 3)
207     {
208         yaz_log(LOG_LOG, "Sending Close PDU, reason=%d, message=%s",
209             reason, message ? message : "none");
210         apdu.which = Z_APDU_close;
211         apdu.u.close = cls;
212         *cls->closeReason = reason;
213         cls->diagnosticInformation = message;
214         process_z_response(a, req, &apdu);
215         iochan_settimeout(a->client_chan, 20);
216     }
217     else
218     {
219         request_release(req);
220         yaz_log(LOG_DEBUG, "v2 client. No Close PDU");
221         iochan_setevent(a->client_chan, EVENT_TIMEOUT); /* force imm close */
222     }
223     a->state = ASSOC_DEAD;
224 }
225
226 static void do_close(association *a, int reason, char *message)
227 {
228     request *req = request_get(&a->outgoing);
229     do_close_req (a, reason, message, req);
230 }
231
232 /*
233  * This is where PDUs from the client are read and the further
234  * processing is initiated. Flow of control moves down through the
235  * various process_* functions below, until the encoded result comes back up
236  * to the output handler in here.
237  * 
238  *  h     : the I/O channel that has an outstanding event.
239  *  event : the current outstanding event.
240  */
241 void ir_session(IOCHAN h, int event)
242 {
243     int res;
244     association *assoc = (association *)iochan_getdata(h);
245     COMSTACK conn = assoc->client_link;
246     request *req;
247
248     assert(h && conn && assoc);
249     if (event == EVENT_TIMEOUT)
250     {
251         if (assoc->state != ASSOC_UP)
252         {
253             yaz_log(LOG_LOG, "Final timeout - closing connection.");
254             cs_close(conn);
255             destroy_association(assoc);
256             iochan_destroy(h);
257         }
258         else
259         {
260             yaz_log(LOG_LOG, "Session idle too long. Sending close.");
261             do_close(assoc, Z_Close_lackOfActivity, 0);
262         }
263         return;
264     }
265     if (event & assoc->cs_accept_mask)
266     {
267         yaz_log (LOG_DEBUG, "ir_session (accept)");
268         if (!cs_accept (conn))
269         {
270             yaz_log (LOG_LOG, "accept failed");
271             destroy_association(assoc);
272             iochan_destroy(h);
273         }
274         iochan_clearflag (h, EVENT_OUTPUT|EVENT_OUTPUT);
275         if (conn->io_pending) 
276         {   /* cs_accept didn't complete */
277             assoc->cs_accept_mask = 
278                 ((conn->io_pending & CS_WANT_WRITE) ? EVENT_OUTPUT : 0) |
279                 ((conn->io_pending & CS_WANT_READ) ? EVENT_INPUT : 0);
280
281             iochan_setflag (h, assoc->cs_accept_mask);
282         }
283         else
284         {   /* cs_accept completed. Prepare for reading (cs_get) */
285             assoc->cs_accept_mask = 0;
286             assoc->cs_get_mask = EVENT_INPUT;
287             iochan_setflag (h, assoc->cs_get_mask);
288         }
289         return;
290     }
291     if ((event & assoc->cs_get_mask) || (event & EVENT_WORK)) /* input */
292     {
293         if ((assoc->cs_put_mask & EVENT_INPUT) == 0 && (event & assoc->cs_get_mask))
294         {
295             yaz_log(LOG_DEBUG, "ir_session (input)");
296             /* We aren't speaking to this fellow */
297             if (assoc->state == ASSOC_DEAD)
298             {
299                 yaz_log(LOG_LOG, "Connection closed - end of session");
300                 cs_close(conn);
301                 destroy_association(assoc);
302                 iochan_destroy(h);
303                 return;
304             }
305             assoc->cs_get_mask = EVENT_INPUT;
306             if ((res = cs_get(conn, &assoc->input_buffer,
307                 &assoc->input_buffer_len)) <= 0)
308             {
309                 yaz_log(LOG_LOG, "Connection closed by client");
310                 cs_close(conn);
311                 destroy_association(assoc);
312                 iochan_destroy(h);
313                 return;
314             }
315             else if (res == 1) /* incomplete read - wait for more  */
316             {
317                 if (conn->io_pending & CS_WANT_WRITE)
318                     assoc->cs_get_mask |= EVENT_OUTPUT;
319                 iochan_setflag(h, assoc->cs_get_mask);
320                 return;
321             }
322             if (cs_more(conn)) /* more stuff - call us again later, please */
323                 iochan_setevent(h, EVENT_INPUT);
324                 
325             /* we got a complete PDU. Let's decode it */
326             yaz_log(LOG_DEBUG, "Got PDU, %d bytes: lead=%02X %02X %02X", res,
327                             assoc->input_buffer[0] & 0xff,
328                             assoc->input_buffer[1] & 0xff,
329                             assoc->input_buffer[2] & 0xff);
330             req = request_get(&assoc->incoming); /* get a new request */
331             odr_reset(assoc->decode);
332             odr_setbuf(assoc->decode, assoc->input_buffer, res, 0);
333             if (!z_GDU(assoc->decode, &req->gdu_request, 0, 0))
334             {
335                 yaz_log(LOG_LOG, "ODR error on incoming PDU: %s [element %s] "
336                         "[near byte %d] ",
337                         odr_errmsg(odr_geterror(assoc->decode)),
338                         odr_getelement(assoc->decode),
339                         odr_offset(assoc->decode));
340                 if (assoc->decode->error != OHTTP)
341                 {
342                     yaz_log(LOG_LOG, "PDU dump:");
343                     odr_dumpBER(yaz_log_file(), assoc->input_buffer, res);
344                     request_release(req);
345                     do_close(assoc, Z_Close_protocolError,"Malformed package");
346                 }
347                 else
348                 {
349                     Z_GDU *p = z_get_HTTP_Response(assoc->encode, 400);
350                     assoc->state = ASSOC_DEAD;
351                     process_gdu_response(assoc, req, p);
352                 }
353                 return;
354             }
355             req->request_mem = odr_extract_mem(assoc->decode);
356             if (assoc->print && !z_GDU(assoc->print, &req->gdu_request, 0, 0))
357             {
358                 yaz_log(LOG_WARN, "ODR print error: %s", 
359                     odr_errmsg(odr_geterror(assoc->print)));
360                 odr_reset(assoc->print);
361             }
362             request_enq(&assoc->incoming, req);
363         }
364
365         /* can we do something yet? */
366         req = request_head(&assoc->incoming);
367         if (req->state == REQUEST_IDLE)
368         {
369             request_deq(&assoc->incoming);
370             process_gdu_request(assoc, req);
371         }
372     }
373     if (event & assoc->cs_put_mask)
374     {
375         request *req = request_head(&assoc->outgoing);
376
377         assoc->cs_put_mask = 0;
378         yaz_log(LOG_DEBUG, "ir_session (output)");
379         req->state = REQUEST_PENDING;
380         switch (res = cs_put(conn, req->response, req->len_response))
381         {
382         case -1:
383             yaz_log(LOG_LOG, "Connection closed by client");
384             cs_close(conn);
385             destroy_association(assoc);
386             iochan_destroy(h);
387             break;
388         case 0: /* all sent - release the request structure */
389             yaz_log(LOG_DEBUG, "Wrote PDU, %d bytes", req->len_response);
390 #if 0
391             yaz_log(LOG_DEBUG, "HTTP out:\n%.*s", req->len_response,
392                     req->response);
393 #endif
394             nmem_destroy(req->request_mem);
395             request_deq(&assoc->outgoing);
396             request_release(req);
397             if (!request_head(&assoc->outgoing))
398             {   /* restore mask for cs_get operation ... */
399                 iochan_clearflag(h, EVENT_OUTPUT|EVENT_INPUT);
400                 iochan_setflag(h, assoc->cs_get_mask);
401                 if (assoc->state == ASSOC_DEAD)
402                     iochan_setevent(assoc->client_chan, EVENT_TIMEOUT);
403             }
404             else
405             {
406                 assoc->cs_put_mask = EVENT_OUTPUT;
407             }
408             break;
409         default:
410             if (conn->io_pending & CS_WANT_WRITE)
411                 assoc->cs_put_mask |= EVENT_OUTPUT;
412             if (conn->io_pending & CS_WANT_READ)
413                 assoc->cs_put_mask |= EVENT_INPUT;
414             iochan_setflag(h, assoc->cs_put_mask);
415         }
416     }
417     if (event & EVENT_EXCEPT)
418     {
419         yaz_log(LOG_LOG, "ir_session (exception)");
420         cs_close(conn);
421         destroy_association(assoc);
422         iochan_destroy(h);
423     }
424 }
425
426 static int process_z_request(association *assoc, request *req, char **msg);
427
428 static void assoc_init_reset(association *assoc)
429 {
430     xfree (assoc->init);
431     assoc->init = (bend_initrequest *) xmalloc (sizeof(*assoc->init));
432
433     assoc->init->stream = assoc->encode;
434     assoc->init->print = assoc->print;
435     assoc->init->auth = 0;
436     assoc->init->referenceId = 0;
437     assoc->init->implementation_version = 0;
438     assoc->init->implementation_id = 0;
439     assoc->init->implementation_name = 0;
440     assoc->init->bend_sort = NULL;
441     assoc->init->bend_search = NULL;
442     assoc->init->bend_present = NULL;
443     assoc->init->bend_esrequest = NULL;
444     assoc->init->bend_delete = NULL;
445     assoc->init->bend_scan = NULL;
446     assoc->init->bend_segment = NULL;
447     assoc->init->bend_fetch = NULL;
448     assoc->init->bend_explain = NULL;
449
450     assoc->init->charneg_request = NULL;
451     assoc->init->charneg_response = NULL;
452
453     assoc->init->decode = assoc->decode;
454     assoc->init->peer_name = 
455         odr_strdup (assoc->encode, cs_addrstr(assoc->client_link));
456 }
457
458 static int srw_bend_init(association *assoc)
459 {
460     const char *encoding = "UTF-8";
461     Z_External *ce;
462     bend_initresult *binitres;
463     statserv_options_block *cb = statserv_getcontrol();
464     
465     assoc_init_reset(assoc);
466
467     assoc->maximumRecordSize = 3000000;
468     assoc->preferredMessageSize = 3000000;
469 #if 1
470     ce = yaz_set_proposal_charneg(assoc->decode, &encoding, 1, 0, 0, 1);
471     assoc->init->charneg_request = ce->u.charNeg3;
472 #endif
473     if (!(binitres = (*cb->bend_init)(assoc->init)))
474     {
475         yaz_log(LOG_WARN, "Bad response from backend.");
476         return 0;
477     }
478     assoc->backend = binitres->handle;
479     return 1;
480 }
481
482 static int srw_bend_fetch(association *assoc, int pos,
483                           Z_SRW_searchRetrieveRequest *srw_req,
484                           Z_SRW_record *record)
485 {
486     bend_fetch_rr rr;
487     ODR o = assoc->encode;
488
489     rr.setname = "default";
490     rr.number = pos;
491     rr.referenceId = 0;
492     rr.request_format = VAL_TEXT_XML;
493     rr.request_format_raw = yaz_oidval_to_z3950oid(assoc->decode,
494                                                    CLASS_TRANSYN,
495                                                    VAL_TEXT_XML);
496     rr.comp = (Z_RecordComposition *)
497             odr_malloc(assoc->decode, sizeof(*rr.comp));
498     rr.comp->which = Z_RecordComp_complex;
499     rr.comp->u.complex = (Z_CompSpec *)
500             odr_malloc(assoc->decode, sizeof(Z_CompSpec));
501     rr.comp->u.complex->selectAlternativeSyntax = (bool_t *)
502         odr_malloc(assoc->encode, sizeof(bool_t));
503     *rr.comp->u.complex->selectAlternativeSyntax = 0;    
504     rr.comp->u.complex->num_dbSpecific = 0;
505     rr.comp->u.complex->dbSpecific = 0;
506     rr.comp->u.complex->num_recordSyntax = 0; 
507     rr.comp->u.complex->recordSyntax = 0;
508
509     rr.comp->u.complex->generic = (Z_Specification *) 
510             odr_malloc(assoc->decode, sizeof(Z_Specification));
511     rr.comp->u.complex->generic->which = Z_Schema_uri;
512     rr.comp->u.complex->generic->schema.uri = srw_req->recordSchema;
513     rr.comp->u.complex->generic->elementSpec = 0;
514     
515     rr.stream = assoc->encode;
516     rr.print = assoc->print;
517
518     rr.basename = 0;
519     rr.len = 0;
520     rr.record = 0;
521     rr.last_in_set = 0;
522     rr.output_format = VAL_TEXT_XML;
523     rr.output_format_raw = 0;
524     rr.errcode = 0;
525     rr.errstring = 0;
526     rr.surrogate_flag = 0;
527     rr.schema = srw_req->recordSchema;
528
529     if (!assoc->init->bend_fetch)
530         return 1;
531
532     (*assoc->init->bend_fetch)(assoc->backend, &rr);
533
534     if (rr.len >= 0)
535     {
536         record->recordData_buf = rr.record;
537         record->recordData_len = rr.len;
538         record->recordPosition = odr_intdup(o, pos);
539         if (rr.schema)
540             record->recordSchema = odr_strdup(o, rr.schema);
541         else
542             record->recordSchema = 0;
543     }
544     return rr.errcode;
545 }
546
547 static void srw_bend_search(association *assoc, request *req,
548                             Z_SRW_searchRetrieveRequest *srw_req,
549                             Z_SRW_searchRetrieveResponse *srw_res)
550 {
551     int srw_error = 0;
552     bend_search_rr rr;
553     Z_External *ext;
554     
555     yaz_log(LOG_LOG, "Got SRW SearchRetrieveRequest");
556     yaz_log(LOG_DEBUG, "srw_bend_search");
557     if (!assoc->init)
558     {
559         yaz_log(LOG_DEBUG, "srw_bend_init");
560         if (!srw_bend_init(assoc))
561         {
562             srw_error = 3;  /* assume Authentication error */
563
564             srw_res->num_diagnostics = 1;
565             srw_res->diagnostics = (Z_SRW_diagnostic *)
566                 odr_malloc(assoc->encode, sizeof(*srw_res->diagnostics));
567             srw_res->diagnostics[0].code = 
568                 odr_intdup(assoc->encode, srw_error);
569             srw_res->diagnostics[0].details = 0;
570             return;
571         }
572     }
573     
574     rr.setname = "default";
575     rr.replace_set = 1;
576     rr.num_bases = 1;
577     rr.basenames = &srw_req->database;
578     rr.referenceId = 0;
579
580     rr.query = (Z_Query *) odr_malloc (assoc->decode, sizeof(*rr.query));
581
582     if (srw_req->query_type == Z_SRW_query_type_cql)
583     {
584         ext = (Z_External *) odr_malloc(assoc->decode, sizeof(*ext));
585         ext->direct_reference = odr_getoidbystr(assoc->decode, 
586                                                 "1.2.840.10003.16.2");
587         ext->indirect_reference = 0;
588         ext->descriptor = 0;
589         ext->which = Z_External_CQL;
590         ext->u.cql = srw_req->query.cql;
591
592         rr.query->which = Z_Query_type_104;
593         rr.query->u.type_104 =  ext;
594     }
595     else if (srw_req->query_type == Z_SRW_query_type_pqf)
596     {
597         Z_RPNQuery *RPNquery;
598         YAZ_PQF_Parser pqf_parser;
599
600         pqf_parser = yaz_pqf_create ();
601
602         RPNquery = yaz_pqf_parse (pqf_parser, assoc->decode,
603                                   srw_req->query.pqf);
604         if (!RPNquery)
605         {
606             const char *pqf_msg;
607             size_t off;
608             int code = yaz_pqf_error (pqf_parser, &pqf_msg, &off);
609             yaz_log(LOG_LOG, "%*s^\n", off+4, "");
610             yaz_log(LOG_LOG, "Bad PQF: %s (code %d)\n", pqf_msg, code);
611             
612             srw_error = 10;
613         }
614
615         rr.query->which = Z_Query_type_1;
616         rr.query->u.type_1 =  RPNquery;
617
618         yaz_pqf_destroy (pqf_parser);
619     }
620     else
621         srw_error = 11;
622
623     if (!srw_error && srw_req->sort_type != Z_SRW_sort_type_none)
624         srw_error = 80;
625
626     if (!srw_error && !assoc->init->bend_search)
627         srw_error = 1;
628
629     if (srw_error)
630     {
631         yaz_log(LOG_DEBUG, "srw_bend_search returned SRW error %d", srw_error);
632         srw_res->num_diagnostics = 1;
633         srw_res->diagnostics = (Z_SRW_diagnostic *)
634             odr_malloc(assoc->encode, sizeof(*srw_res->diagnostics));
635         srw_res->diagnostics[0].code = 
636             odr_intdup(assoc->encode, srw_error);
637         srw_res->diagnostics[0].details = 0;
638         return;
639     }
640     
641     rr.stream = assoc->encode;
642     rr.decode = assoc->decode;
643     rr.print = assoc->print;
644     rr.request = req;
645     rr.association = assoc;
646     rr.fd = 0;
647     rr.hits = 0;
648     rr.errcode = 0;
649     rr.errstring = 0;
650     rr.search_info = 0;
651     yaz_log_zquery(rr.query);
652     (assoc->init->bend_search)(assoc->backend, &rr);
653     srw_res->numberOfRecords = odr_intdup(assoc->encode, rr.hits);
654     if (rr.errcode)
655     {
656         yaz_log(LOG_DEBUG, "bend_search returned Bib-1 code %d", rr.errcode);
657         srw_res->num_diagnostics = 1;
658         srw_res->diagnostics = (Z_SRW_diagnostic *)
659             odr_malloc(assoc->encode, sizeof(*srw_res->diagnostics));
660         srw_res->diagnostics[0].code = 
661             odr_intdup(assoc->encode, 
662                        yaz_diag_bib1_to_srw (rr.errcode));
663         srw_res->diagnostics[0].details = rr.errstring;
664         yaz_log(LOG_DEBUG, "srw_bend_search returned SRW error %d",
665                 *srw_res->diagnostics[0].code);
666                 
667     }
668     else
669     {
670         int number = srw_req->maximumRecords ? *srw_req->maximumRecords : 0;
671         int start = srw_req->startRecord ? *srw_req->startRecord : 1;
672
673         yaz_log(LOG_LOG, "Request to pack %d+%d out of %d",
674                 start, number, rr.hits);
675
676         srw_res->numberOfRecords = odr_intdup(assoc->encode, rr.hits);
677         if (number > 0)
678         {
679             int i;
680
681             if (start > rr.hits)
682             {
683                 yaz_log(LOG_LOG, "Request out or range");
684             }
685             else
686             {
687                 int j = 0;
688                 int packing = Z_SRW_recordPacking_string;
689                 if (start + number > rr.hits)
690                     number = rr.hits - start + 1;
691                 if (srw_req->recordPacking && 
692                     !strcmp(srw_req->recordPacking, "xml"))
693                     packing = Z_SRW_recordPacking_XML;
694                 srw_res->records = (Z_SRW_record *)
695                     odr_malloc(assoc->encode,
696                                number * sizeof(*srw_res->records));
697                 for (i = 0; i<number; i++)
698                 {
699                     int errcode;
700                     
701                     srw_res->records[j].recordPacking = packing;
702                     srw_res->records[j].recordData_buf = 0;
703                     yaz_log(LOG_DEBUG, "srw_bend_fetch %d", i+start);
704                     errcode = srw_bend_fetch(assoc, i+start, srw_req,
705                                              srw_res->records + j);
706                     if (errcode)
707                     {
708                         srw_res->num_diagnostics = 1;
709                         srw_res->diagnostics = (Z_SRW_diagnostic *)
710                             odr_malloc(assoc->encode, 
711                                        sizeof(*srw_res->diagnostics));
712                         srw_res->diagnostics[0].code = 
713                             odr_intdup(assoc->encode, 
714                                        yaz_diag_bib1_to_srw (errcode));
715                         srw_res->diagnostics[0].details = rr.errstring;
716                         break;
717                     }
718                     if (srw_res->records[j].recordData_buf)
719                         j++;
720                 }
721                 srw_res->num_records = j;
722                 if (!j)
723                     srw_res->records = 0;
724             }
725         }
726     }
727 }
728
729
730 static void srw_bend_explain(association *assoc, request *req,
731                              Z_SRW_explainRequest *srw_req,
732                              Z_SRW_explainResponse *srw_res)
733 {
734     yaz_log(LOG_LOG, "Got SRW ExplainRequest");
735     if (!assoc->init)
736     {
737         yaz_log(LOG_DEBUG, "srw_bend_init");
738         if (!srw_bend_init(assoc))
739             return;
740     }
741     if (assoc->init && assoc->init->bend_explain)
742     {
743         bend_explain_rr rr;
744
745         rr.stream = assoc->encode;
746         rr.decode = assoc->decode;
747         rr.print = assoc->print;
748         rr.explain_buf = 0;
749         (*assoc->init->bend_explain)(assoc->backend, &rr);
750         if (rr.explain_buf)
751         {
752             srw_res->explainData_buf = rr.explain_buf;
753             srw_res->explainData_len = strlen(rr.explain_buf);
754         }
755     }
756 }
757
758 static int hex_digit (int ch)
759 {
760     if (ch >= '0' && ch <= '9')
761         return ch - '0';
762     else if (ch >= 'a' && ch <= 'f')
763         return ch - 'a'+10;
764     else if (ch >= 'A' && ch <= 'F')
765         return ch - 'A'+10;
766     return 0;
767 }
768
769 static char *uri_val(const char *path, const char *name, ODR o)
770 {
771     size_t nlen = strlen(name);
772     if (*path != '?')
773         return 0;
774     path++;
775     while (path && *path)
776     {
777         const char *p1 = strchr(path, '=');
778         if (!p1)
779             break;
780         if ((size_t)(p1 - path) == nlen && !memcmp(path, name, nlen))
781         {
782             size_t i = 0;
783             char *ret;
784             
785             path = p1 + 1;
786             p1 = strchr(path, '&');
787             if (!p1)
788                 p1 = strlen(path) + path;
789             ret = odr_malloc(o, p1 - path + 1);
790             while (*path && *path != '&')
791             {
792                 if (*path == '+')
793                 {
794                     ret[i++] = ' ';
795                     path++;
796                 }
797                 else if (*path == '%' && path[1] && path[2])
798                 {
799                     ret[i++] = hex_digit (path[1])*16 + hex_digit (path[2]);
800                     path = path + 3;
801                 }
802                 else
803                     ret[i++] = *path++;
804             }
805             ret[i] = '\0';
806             return ret;
807         }
808         path = strchr(p1, '&');
809         if (path)
810             path++;
811     }
812     return 0;
813 }
814
815 void uri_val_int(const char *path, const char *name, ODR o, int **intp)
816 {
817     const char *v = uri_val(path, name, o);
818     if (v)
819         *intp = odr_intdup(o, atoi(v));
820 }
821
822 static void process_http_request(association *assoc, request *req)
823 {
824     Z_HTTP_Request *hreq = req->gdu_request->u.HTTP_Request;
825     ODR o = assoc->encode;
826     Z_GDU *p = 0;
827     Z_HTTP_Response *hres = 0;
828     int keepalive = 1;
829
830     if (!strcmp(hreq->method, "GET"))
831     {
832         char *db = "Default";
833         const char *p0 = hreq->path, *p1;
834 #if HAVE_XML2
835         int ret = -1;
836         char *charset = 0;
837         Z_SOAP *soap_package = 0;
838         static Z_SOAP_Handler soap_handlers[2] = {
839             {"http://www.loc.gov/zing/srw/v1.0/", 0,
840              (Z_SOAP_fun) yaz_srw_codec},
841             {0, 0, 0}
842         };
843 #endif
844         
845         if (*p0 == '/')
846             p0++;
847         p1 = strchr(p0, '?');
848         if (!p1)
849             p1 = p0 + strlen(p0);
850         if (p1 != p0)
851         {
852             db = odr_malloc(assoc->decode, p1 - p0 + 1);
853             memcpy (db, p0, p1 - p0);
854             db[p1 - p0] = '\0';
855         }
856 #if HAVE_XML2
857         if (p1 && *p1 == '?' && p1[1])
858         {
859             Z_SRW_PDU *res = yaz_srw_get(o, Z_SRW_searchRetrieve_response);
860             Z_SRW_PDU *sr = yaz_srw_get(o, Z_SRW_searchRetrieve_request);
861             char *query = uri_val(p1, "query", o);
862             char *pQuery = uri_val(p1, "pQuery", o);
863             char *sortKeys = uri_val(p1, "sortKeys", o);
864             
865             if (query)
866             {
867                 sr->u.request->query_type = Z_SRW_query_type_cql;
868                 sr->u.request->query.cql = query;
869             }
870             if (pQuery)
871             {
872                 sr->u.request->query_type = Z_SRW_query_type_pqf;
873                 sr->u.request->query.pqf = pQuery;
874             }
875             if (sortKeys)
876             {
877                 sr->u.request->sort_type = Z_SRW_sort_type_sort;
878                 sr->u.request->sort.sortKeys = sortKeys;
879             }
880             sr->u.request->recordSchema = uri_val(p1, "recordSchema", o);
881             sr->u.request->recordPacking = uri_val(p1, "recordPacking", o);
882             if (!sr->u.request->recordPacking)
883                 sr->u.request->recordPacking = "xml";
884             uri_val_int(p1, "maximumRecords", o, 
885                         &sr->u.request->maximumRecords);
886             uri_val_int(p1, "startRecord", o,
887                         &sr->u.request->startRecord);
888             if (sr->u.request->startRecord)
889                 yaz_log(LOG_LOG, "startRecord=%d", *sr->u.request->startRecord);
890             sr->u.request->database = db;
891             srw_bend_search(assoc, req, sr->u.request, res->u.response);
892             
893             soap_package = odr_malloc(o, sizeof(*soap_package));
894             soap_package->which = Z_SOAP_generic;
895
896             soap_package->u.generic =
897                 odr_malloc(o, sizeof(*soap_package->u.generic));
898
899             soap_package->u.generic->p = res;
900             soap_package->u.generic->ns = soap_handlers[0].ns;
901             soap_package->u.generic->no = 0;
902             
903             soap_package->ns = "SRU";
904
905             p = z_get_HTTP_Response(o, 200);
906             hres = p->u.HTTP_Response;
907
908             ret = z_soap_codec_enc(assoc->encode, &soap_package,
909                                    &hres->content_buf, &hres->content_len,
910                                    soap_handlers, charset);
911             if (!charset)
912                 z_HTTP_header_add(o, &hres->headers, "Content-Type", "text/xml");
913             else
914             {
915                 char ctype[60];
916                 strcpy(ctype, "text/xml; charset=");
917                 strcat(ctype, charset);
918                 z_HTTP_header_add(o, &hres->headers, "Content-Type", ctype);
919             }
920
921         }
922         else
923         {
924             Z_SRW_PDU *res = yaz_srw_get(o, Z_SRW_explain_response);
925             Z_SRW_PDU *sr = yaz_srw_get(o, Z_SRW_explain_request);
926
927             srw_bend_explain(assoc, req, sr->u.explain_request,
928                             res->u.explain_response);
929
930             if (res->u.explain_response->explainData_buf)
931             {
932                 soap_package = odr_malloc(o, sizeof(*soap_package));
933                 soap_package->which = Z_SOAP_generic;
934                 
935                 soap_package->u.generic =
936                     odr_malloc(o, sizeof(*soap_package->u.generic));
937                 
938                 soap_package->u.generic->p = res;
939                 soap_package->u.generic->ns = soap_handlers[0].ns;
940                 soap_package->u.generic->no = 0;
941                 
942                 soap_package->ns = "SRU";
943                 
944                 p = z_get_HTTP_Response(o, 200);
945                 hres = p->u.HTTP_Response;
946                 
947                 ret = z_soap_codec_enc(assoc->encode, &soap_package,
948                                        &hres->content_buf, &hres->content_len,
949                                        soap_handlers, charset);
950                 if (!charset)
951                     z_HTTP_header_add(o, &hres->headers, "Content-Type", "text/xml");
952                 else
953                 {
954                     char ctype[60];
955                     strcpy(ctype, "text/xml; charset=");
956                     strcat(ctype, charset);
957                     z_HTTP_header_add(o, &hres->headers, "Content-Type",
958                                       ctype);
959                 }
960             }
961         }
962 #endif
963 #ifdef DOCDIR
964         if (strlen(hreq->path) >= 5 && strlen(hreq->path) < 80 &&
965                          !memcmp(hreq->path, "/doc/", 5))
966         {
967             FILE *f;
968             char fpath[120];
969
970             strcpy(fpath, DOCDIR);
971             strcat(fpath, hreq->path+4);
972             f = fopen(fpath, "rb");
973             if (f) {
974                 struct stat sbuf;
975                 if (fstat(fileno(f), &sbuf) || !S_ISREG(sbuf.st_mode))
976                 {
977                     fclose(f);
978                     f = 0;
979                 }
980             }
981             if (f)
982             {
983                 long sz;
984                 fseek(f, 0L, SEEK_END);
985                 sz = ftell(f);
986                 if (sz >= 0 && sz < 500000)
987                 {
988                     const char *ctype = "application/octet-stream";
989                     const char *cp;
990
991                     p = z_get_HTTP_Response(o, 200);
992                     hres = p->u.HTTP_Response;
993                     hres->content_buf = (char *) odr_malloc(o, sz + 1);
994                     hres->content_len = sz;
995                     fseek(f, 0L, SEEK_SET);
996                     fread(hres->content_buf, 1, sz, f);
997                     if ((cp = strrchr(fpath, '.'))) {
998                         cp++;
999                         if (!strcmp(cp, "png"))
1000                             ctype = "image/png";
1001                         else if (!strcmp(cp, "gif"))
1002                             ctype = "image/gif";
1003                         else if (!strcmp(cp, "xml"))
1004                             ctype = "text/xml";
1005                         else if (!strcmp(cp, "html"))
1006                             ctype = "text/html";
1007                     }
1008                     z_HTTP_header_add(o, &hres->headers, "Content-Type", ctype);
1009                 }
1010                 fclose(f);
1011             }
1012         }
1013 #endif
1014
1015 #if 0
1016         if (!strcmp(hreq->path, "/")) 
1017         {
1018 #ifdef DOCDIR
1019             struct stat sbuf;
1020 #endif
1021             const char *doclink = "";
1022             p = z_get_HTTP_Response(o, 200);
1023             hres = p->u.HTTP_Response;
1024             hres->content_buf = (char *) odr_malloc(o, 400);
1025 #ifdef DOCDIR
1026             if (stat(DOCDIR "/yaz.html", &sbuf) == 0 && S_ISREG(sbuf.st_mode))
1027                 doclink = "<P><A HREF=\"/doc/yaz.html\">Documentation</A></P>";
1028 #endif
1029             sprintf (hres->content_buf, 
1030                      "<!DOCTYPE HTML PUBLIC \"-//IETF//DTD HTML 2.0//EN\">\n"
1031                      "<HTML>\n"
1032                      " <HEAD>\n"
1033                      "  <TITLE>YAZ " YAZ_VERSION "</TITLE>\n"
1034                      " </HEAD>\n"
1035                      " <BODY>\n"
1036                      "  <P><A HREF=\"http://www.indexdata.dk/yaz/\">YAZ</A> " 
1037                      YAZ_VERSION "</P>\n"
1038                      "%s"
1039                      " </BODY>\n"
1040                      "</HTML>\n", doclink);
1041             hres->content_len = strlen(hres->content_buf);
1042             z_HTTP_header_add(o, &hres->headers, "Content-Type", "text/html");
1043         }
1044 #endif
1045
1046         if (!p)
1047         {
1048             p = z_get_HTTP_Response(o, 404);
1049         }
1050     }
1051     else if (!strcmp(hreq->method, "POST"))
1052     {
1053         const char *content_type = z_HTTP_header_lookup(hreq->headers,
1054                                                         "Content-Type");
1055         if (content_type && !yaz_strcmp_del("text/xml", content_type, "; "))
1056         {
1057             Z_SOAP *soap_package = 0;
1058             int ret = -1;
1059             int http_code = 500;
1060             const char *charset_p = 0;
1061             char *charset = 0;
1062
1063             static Z_SOAP_Handler soap_handlers[2] = {
1064 #if HAVE_XML2
1065                 {"http://www.loc.gov/zing/srw/v1.0/", 0,
1066                  (Z_SOAP_fun) yaz_srw_codec},
1067 #endif
1068                 {0, 0, 0}
1069             };
1070             if ((charset_p = strstr(content_type, "; charset=")))
1071             {
1072                 int i = 0;
1073                 charset_p += 10;
1074                 while (i < 20 && charset_p[i] &&
1075                        !strchr("; \n\r", charset_p[i]))
1076                     i++;
1077                 charset = odr_malloc(assoc->encode, i+1);
1078                 memcpy(charset, charset_p, i);
1079                 charset[i] = '\0';
1080                 yaz_log(LOG_LOG, "SOAP encoding %s", charset);
1081             }
1082             ret = z_soap_codec(assoc->decode, &soap_package, 
1083                                &hreq->content_buf, &hreq->content_len,
1084                                soap_handlers);
1085 #if HAVE_XML2
1086             if (!ret && soap_package->which == Z_SOAP_generic &&
1087                 soap_package->u.generic->no == 0)
1088             {
1089                 /* SRW package */
1090                 Z_SRW_PDU *sr = soap_package->u.generic->p;
1091                 
1092                 if (sr->which == Z_SRW_searchRetrieve_request)
1093                 {
1094                     Z_SRW_PDU *res =
1095                         yaz_srw_get(assoc->encode,
1096                                     Z_SRW_searchRetrieve_response);
1097
1098                     if (!sr->u.request->database)
1099                     {
1100                         const char *p0 = hreq->path, *p1;
1101                         if (*p0 == '/')
1102                             p0++;
1103                         p1 = strchr(p0, '?');
1104                         if (!p1)
1105                             p1 = p0 + strlen(p0);
1106                         if (p1 != p0)
1107                         {
1108                             sr->u.request->database =
1109                                 odr_malloc(assoc->decode, p1 - p0 + 1);
1110                             memcpy (sr->u.request->database, p0, p1 - p0);
1111                             sr->u.request->database[p1 - p0] = '\0';
1112                         }
1113                         else
1114                             sr->u.request->database = "Default";
1115                     }
1116                     srw_bend_search(assoc, req, sr->u.request,
1117                                     res->u.response);
1118                     
1119                     soap_package->u.generic->p = res;
1120                     http_code = 200;
1121                 }
1122                 else if (sr->which == Z_SRW_explain_request)
1123                 {
1124                     Z_SRW_PDU *res =
1125                         yaz_srw_get(assoc->encode, Z_SRW_explain_response);
1126
1127                     srw_bend_explain(assoc, req, sr->u.explain_request,
1128                                      res->u.explain_response);
1129                     if (!res->u.explain_response->explainData_buf)
1130                     {
1131                         z_soap_error(assoc->encode, soap_package,
1132                                      "SOAP-ENV:Client", "Explain Not Supported", 0);
1133                     }
1134                     else
1135                     {
1136                         soap_package->u.generic->p = res;
1137                         http_code = 200;
1138                     }
1139                 }
1140                 else
1141                 {
1142                     z_soap_error(assoc->encode, soap_package,
1143                                  "SOAP-ENV:Client", "Bad method", 0); 
1144                 }
1145             }
1146 #endif
1147             p = z_get_HTTP_Response(o, 200);
1148             hres = p->u.HTTP_Response;
1149             ret = z_soap_codec_enc(assoc->encode, &soap_package,
1150                                    &hres->content_buf, &hres->content_len,
1151                                    soap_handlers, charset);
1152             hres->code = http_code;
1153             if (!charset)
1154                 z_HTTP_header_add(o, &hres->headers, "Content-Type", "text/xml");
1155             else
1156             {
1157                 char ctype[60];
1158                 strcpy(ctype, "text/xml; charset=");
1159                 strcat(ctype, charset);
1160                 z_HTTP_header_add(o, &hres->headers, "Content-Type", ctype);
1161             }
1162         }
1163         if (!p) /* still no response ? */
1164             p = z_get_HTTP_Response(o, 500);
1165     }
1166     else
1167     {
1168         p = z_get_HTTP_Response(o, 405);
1169         hres = p->u.HTTP_Response;
1170
1171         z_HTTP_header_add(o, &hres->headers, "Allow", "GET, POST");
1172     }
1173     hres = p->u.HTTP_Response;
1174     if (!strcmp(hreq->version, "1.0")) 
1175     {
1176         const char *v = z_HTTP_header_lookup(hreq->headers, "Connection");
1177         if (v && !strcmp(v, "Keep-Alive"))
1178             keepalive = 1;
1179         else
1180             keepalive = 0;
1181         hres->version = "1.0";
1182     }
1183     else
1184     {
1185         const char *v = z_HTTP_header_lookup(hreq->headers, "Connection");
1186         if (v && !strcmp(v, "close"))
1187             keepalive = 0;
1188         else
1189             keepalive = 1;
1190         hres->version = "1.1";
1191     }
1192     if (!keepalive)
1193     {
1194         z_HTTP_header_add(o, &hres->headers, "Connection", "close");
1195         assoc->state = ASSOC_DEAD;
1196     }
1197     else
1198     {
1199         int t;
1200         const char *alive = z_HTTP_header_lookup(hreq->headers, "Keep-Alive");
1201
1202         if (alive && isdigit(*alive))
1203             t = atoi(alive);
1204         else
1205             t = 15;
1206         if (t < 0 || t > 3600)
1207             t = 3600;
1208         iochan_settimeout(assoc->client_chan,t);
1209         z_HTTP_header_add(o, &hres->headers, "Connection", "Keep-Alive");
1210     }
1211     process_gdu_response(assoc, req, p);
1212 }
1213
1214 static void process_gdu_request(association *assoc, request *req)
1215 {
1216     if (req->gdu_request->which == Z_GDU_Z3950)
1217     {
1218         char *msg = 0;
1219         req->apdu_request = req->gdu_request->u.z3950;
1220         if (process_z_request(assoc, req, &msg) < 0)
1221             do_close_req(assoc, Z_Close_systemProblem, msg, req);
1222     }
1223     else if (req->gdu_request->which == Z_GDU_HTTP_Request)
1224         process_http_request(assoc, req);
1225     else
1226     {
1227         do_close_req(assoc, Z_Close_systemProblem, "bad protocol packet", req);
1228     }
1229 }
1230
1231 /*
1232  * Initiate request processing.
1233  */
1234 static int process_z_request(association *assoc, request *req, char **msg)
1235 {
1236     int fd = -1;
1237     Z_APDU *res;
1238     int retval;
1239     
1240     *msg = "Unknown Error";
1241     assert(req && req->state == REQUEST_IDLE);
1242     if (req->apdu_request->which != Z_APDU_initRequest && !assoc->init)
1243     {
1244         *msg = "Missing InitRequest";
1245         return -1;
1246     }
1247     switch (req->apdu_request->which)
1248     {
1249     case Z_APDU_initRequest:
1250         iochan_settimeout(assoc->client_chan,
1251                           statserv_getcontrol()->idle_timeout * 60);
1252         res = process_initRequest(assoc, req); break;
1253     case Z_APDU_searchRequest:
1254         res = process_searchRequest(assoc, req, &fd); break;
1255     case Z_APDU_presentRequest:
1256         res = process_presentRequest(assoc, req, &fd); break;
1257     case Z_APDU_scanRequest:
1258         if (assoc->init->bend_scan)
1259             res = process_scanRequest(assoc, req, &fd);
1260         else
1261         {
1262             *msg = "Cannot handle Scan APDU";
1263             return -1;
1264         }
1265         break;
1266     case Z_APDU_extendedServicesRequest:
1267         if (assoc->init->bend_esrequest)
1268             res = process_ESRequest(assoc, req, &fd);
1269         else
1270         {
1271             *msg = "Cannot handle Extended Services APDU";
1272             return -1;
1273         }
1274         break;
1275     case Z_APDU_sortRequest:
1276         if (assoc->init->bend_sort)
1277             res = process_sortRequest(assoc, req, &fd);
1278         else
1279         {
1280             *msg = "Cannot handle Sort APDU";
1281             return -1;
1282         }
1283         break;
1284     case Z_APDU_close:
1285         process_close(assoc, req);
1286         return 0;
1287     case Z_APDU_deleteResultSetRequest:
1288         if (assoc->init->bend_delete)
1289             res = process_deleteRequest(assoc, req, &fd);
1290         else
1291         {
1292             *msg = "Cannot handle Delete APDU";
1293             return -1;
1294         }
1295         break;
1296     case Z_APDU_segmentRequest:
1297         if (assoc->init->bend_segment)
1298         {
1299             res = process_segmentRequest (assoc, req);
1300         }
1301         else
1302         {
1303             *msg = "Cannot handle Segment APDU";
1304             return -1;
1305         }
1306         break;
1307     default:
1308         *msg = "Bad APDU received";
1309         return -1;
1310     }
1311     if (res)
1312     {
1313         yaz_log(LOG_DEBUG, "  result immediately available");
1314         retval = process_z_response(assoc, req, res);
1315     }
1316     else if (fd < 0)
1317     {
1318         yaz_log(LOG_DEBUG, "  result unavailble");
1319         retval = 0;
1320     }
1321     else /* no result yet - one will be provided later */
1322     {
1323         IOCHAN chan;
1324
1325         /* Set up an I/O handler for the fd supplied by the backend */
1326
1327         yaz_log(LOG_DEBUG, "   establishing handler for result");
1328         req->state = REQUEST_PENDING;
1329         if (!(chan = iochan_create(fd, backend_response, EVENT_INPUT)))
1330             abort();
1331         iochan_setdata(chan, assoc);
1332         retval = 0;
1333     }
1334     return retval;
1335 }
1336
1337 /*
1338  * Handle message from the backend.
1339  */
1340 void backend_response(IOCHAN i, int event)
1341 {
1342     association *assoc = (association *)iochan_getdata(i);
1343     request *req = request_head(&assoc->incoming);
1344     Z_APDU *res;
1345     int fd;
1346
1347     yaz_log(LOG_DEBUG, "backend_response");
1348     assert(assoc && req && req->state != REQUEST_IDLE);
1349     /* determine what it is we're waiting for */
1350     switch (req->apdu_request->which)
1351     {
1352         case Z_APDU_searchRequest:
1353             res = response_searchRequest(assoc, req, 0, &fd); break;
1354 #if 0
1355         case Z_APDU_presentRequest:
1356             res = response_presentRequest(assoc, req, 0, &fd); break;
1357         case Z_APDU_scanRequest:
1358             res = response_scanRequest(assoc, req, 0, &fd); break;
1359 #endif
1360         default:
1361             yaz_log(LOG_WARN, "Serious programmer's lapse or bug");
1362             abort();
1363     }
1364     if ((res && process_z_response(assoc, req, res) < 0) || fd < 0)
1365     {
1366         yaz_log(LOG_LOG, "Fatal error when talking to backend");
1367         do_close(assoc, Z_Close_systemProblem, 0);
1368         iochan_destroy(i);
1369         return;
1370     }
1371     else if (!res) /* no result yet - try again later */
1372     {
1373         yaz_log(LOG_DEBUG, "   no result yet");
1374         iochan_setfd(i, fd); /* in case fd has changed */
1375     }
1376 }
1377
1378 /*
1379  * Encode response, and transfer the request structure to the outgoing queue.
1380  */
1381 static int process_gdu_response(association *assoc, request *req, Z_GDU *res)
1382 {
1383     odr_setbuf(assoc->encode, req->response, req->size_response, 1);
1384
1385     if (assoc->print && !z_GDU(assoc->print, &res, 0, 0))
1386     {
1387         yaz_log(LOG_WARN, "ODR print error: %s", 
1388             odr_errmsg(odr_geterror(assoc->print)));
1389         odr_reset(assoc->print);
1390     }
1391     if (!z_GDU(assoc->encode, &res, 0, 0))
1392     {
1393         yaz_log(LOG_WARN, "ODR error when encoding PDU: %s [element %s]",
1394                 odr_errmsg(odr_geterror(assoc->decode)),
1395                 odr_getelement(assoc->decode));
1396         request_release(req);
1397         return -1;
1398     }
1399     req->response = odr_getbuf(assoc->encode, &req->len_response,
1400         &req->size_response);
1401     odr_setbuf(assoc->encode, 0, 0, 0); /* don'txfree if we abort later */
1402     odr_reset(assoc->encode);
1403     req->state = REQUEST_IDLE;
1404     request_enq(&assoc->outgoing, req);
1405     /* turn the work over to the ir_session handler */
1406     iochan_setflag(assoc->client_chan, EVENT_OUTPUT);
1407     assoc->cs_put_mask = EVENT_OUTPUT;
1408     /* Is there more work to be done? give that to the input handler too */
1409 #if 1
1410     if (request_head(&assoc->incoming))
1411     {
1412         yaz_log (LOG_DEBUG, "more work to be done");
1413         iochan_setevent(assoc->client_chan, EVENT_WORK);
1414     }
1415 #endif
1416     return 0;
1417 }
1418
1419 /*
1420  * Encode response, and transfer the request structure to the outgoing queue.
1421  */
1422 static int process_z_response(association *assoc, request *req, Z_APDU *res)
1423 {
1424     Z_GDU *gres = (Z_GDU *) odr_malloc(assoc->encode, sizeof(*res));
1425     gres->which = Z_GDU_Z3950;
1426     gres->u.z3950 = res;
1427
1428     return process_gdu_response(assoc, req, gres);
1429 }
1430
1431
1432 /*
1433  * Handle init request.
1434  * At the moment, we don't check the options
1435  * anywhere else in the code - we just try not to do anything that would
1436  * break a naive client. We'll toss 'em into the association block when
1437  * we need them there.
1438  */
1439 static Z_APDU *process_initRequest(association *assoc, request *reqb)
1440 {
1441     statserv_options_block *cb = statserv_getcontrol();
1442     Z_InitRequest *req = reqb->apdu_request->u.initRequest;
1443     Z_APDU *apdu = zget_APDU(assoc->encode, Z_APDU_initResponse);
1444     Z_InitResponse *resp = apdu->u.initResponse;
1445     bend_initresult *binitres;
1446     char *version;
1447     char options[140];
1448
1449     yaz_log(LOG_LOG, "Got initRequest");
1450     if (req->implementationId)
1451         yaz_log(LOG_LOG, "Id:        %s", req->implementationId);
1452     if (req->implementationName)
1453         yaz_log(LOG_LOG, "Name:      %s", req->implementationName);
1454     if (req->implementationVersion)
1455         yaz_log(LOG_LOG, "Version:   %s", req->implementationVersion);
1456
1457     assoc_init_reset(assoc);
1458
1459     assoc->init->auth = req->idAuthentication;
1460     assoc->init->referenceId = req->referenceId;
1461
1462     if (ODR_MASK_GET(req->options, Z_Options_negotiationModel))
1463     {
1464         Z_CharSetandLanguageNegotiation *negotiation =
1465             yaz_get_charneg_record (req->otherInfo);
1466         if (negotiation->which == Z_CharSetandLanguageNegotiation_proposal)
1467             assoc->init->charneg_request = negotiation;
1468     }
1469     
1470     if (!(binitres = (*cb->bend_init)(assoc->init)))
1471     {
1472         yaz_log(LOG_WARN, "Bad response from backend.");
1473         return 0;
1474     }
1475
1476     assoc->backend = binitres->handle;
1477     if ((assoc->init->bend_sort))
1478         yaz_log (LOG_DEBUG, "Sort handler installed");
1479     if ((assoc->init->bend_search))
1480         yaz_log (LOG_DEBUG, "Search handler installed");
1481     if ((assoc->init->bend_present))
1482         yaz_log (LOG_DEBUG, "Present handler installed");   
1483     if ((assoc->init->bend_esrequest))
1484         yaz_log (LOG_DEBUG, "ESRequest handler installed");   
1485     if ((assoc->init->bend_delete))
1486         yaz_log (LOG_DEBUG, "Delete handler installed");   
1487     if ((assoc->init->bend_scan))
1488         yaz_log (LOG_DEBUG, "Scan handler installed");   
1489     if ((assoc->init->bend_segment))
1490         yaz_log (LOG_DEBUG, "Segment handler installed");   
1491     
1492     resp->referenceId = req->referenceId;
1493     *options = '\0';
1494     /* let's tell the client what we can do */
1495     if (ODR_MASK_GET(req->options, Z_Options_search))
1496     {
1497         ODR_MASK_SET(resp->options, Z_Options_search);
1498         strcat(options, "srch");
1499     }
1500     if (ODR_MASK_GET(req->options, Z_Options_present))
1501     {
1502         ODR_MASK_SET(resp->options, Z_Options_present);
1503         strcat(options, " prst");
1504     }
1505     if (ODR_MASK_GET(req->options, Z_Options_delSet) &&
1506         assoc->init->bend_delete)
1507     {
1508         ODR_MASK_SET(resp->options, Z_Options_delSet);
1509         strcat(options, " del");
1510     }
1511     if (ODR_MASK_GET(req->options, Z_Options_extendedServices) &&
1512         assoc->init->bend_esrequest)
1513     {
1514         ODR_MASK_SET(resp->options, Z_Options_extendedServices);
1515         strcat (options, " extendedServices");
1516     }
1517     if (ODR_MASK_GET(req->options, Z_Options_namedResultSets))
1518     {
1519         ODR_MASK_SET(resp->options, Z_Options_namedResultSets);
1520         strcat(options, " namedresults");
1521     }
1522     if (ODR_MASK_GET(req->options, Z_Options_scan) && assoc->init->bend_scan)
1523     {
1524         ODR_MASK_SET(resp->options, Z_Options_scan);
1525         strcat(options, " scan");
1526     }
1527     if (ODR_MASK_GET(req->options, Z_Options_concurrentOperations))
1528     {
1529         ODR_MASK_SET(resp->options, Z_Options_concurrentOperations);
1530         strcat(options, " concurrop");
1531     }
1532     if (ODR_MASK_GET(req->options, Z_Options_sort) && assoc->init->bend_sort)
1533     {
1534         ODR_MASK_SET(resp->options, Z_Options_sort);
1535         strcat(options, " sort");
1536     }
1537
1538     if (ODR_MASK_GET(req->options, Z_Options_negotiationModel)
1539         && assoc->init->charneg_response)
1540     {
1541         Z_OtherInformation **p;
1542         Z_OtherInformationUnit *p0;
1543         
1544         yaz_oi_APDU(apdu, &p);
1545         
1546         if ((p0=yaz_oi_update(p, assoc->encode, NULL, 0, 0))) {
1547             ODR_MASK_SET(resp->options, Z_Options_negotiationModel);
1548             
1549             p0->which = Z_OtherInfo_externallyDefinedInfo;
1550             p0->information.externallyDefinedInfo =
1551                 assoc->init->charneg_response;
1552         }
1553         ODR_MASK_SET(resp->options, Z_Options_negotiationModel);
1554         strcat(options, " negotiation");
1555     }
1556
1557     if (ODR_MASK_GET(req->protocolVersion, Z_ProtocolVersion_1))
1558     {
1559         ODR_MASK_SET(resp->protocolVersion, Z_ProtocolVersion_1);
1560         assoc->version = 2; /* 1 & 2 are equivalent */
1561     }
1562     if (ODR_MASK_GET(req->protocolVersion, Z_ProtocolVersion_2))
1563     {
1564         ODR_MASK_SET(resp->protocolVersion, Z_ProtocolVersion_2);
1565         assoc->version = 2;
1566     }
1567     if (ODR_MASK_GET(req->protocolVersion, Z_ProtocolVersion_3))
1568     {
1569         ODR_MASK_SET(resp->protocolVersion, Z_ProtocolVersion_3);
1570         assoc->version = 3;
1571     }
1572
1573     yaz_log(LOG_LOG, "Negotiated to v%d: %s", assoc->version, options);
1574     assoc->maximumRecordSize = *req->maximumRecordSize;
1575     if (assoc->maximumRecordSize > control_block->maxrecordsize)
1576         assoc->maximumRecordSize = control_block->maxrecordsize;
1577     assoc->preferredMessageSize = *req->preferredMessageSize;
1578     if (assoc->preferredMessageSize > assoc->maximumRecordSize)
1579         assoc->preferredMessageSize = assoc->maximumRecordSize;
1580
1581     resp->preferredMessageSize = &assoc->preferredMessageSize;
1582     resp->maximumRecordSize = &assoc->maximumRecordSize;
1583
1584     resp->implementationId = odr_prepend(assoc->encode,
1585                 assoc->init->implementation_id,
1586                 resp->implementationId);
1587
1588     resp->implementationName = odr_prepend(assoc->encode,
1589                 assoc->init->implementation_name,
1590                 odr_prepend(assoc->encode, "GFS", resp->implementationName));
1591
1592     version = odr_strdup(assoc->encode, "$Revision: 1.3 $");
1593     if (strlen(version) > 10)   /* check for unexpanded CVS strings */
1594         version[strlen(version)-2] = '\0';
1595     resp->implementationVersion = odr_prepend(assoc->encode,
1596                 assoc->init->implementation_version,
1597                 odr_prepend(assoc->encode, &version[11],
1598                             resp->implementationVersion));
1599
1600     if (binitres->errcode)
1601     {
1602         yaz_log(LOG_LOG, "Connection rejected by backend.");
1603         *resp->result = 0;
1604         assoc->state = ASSOC_DEAD;
1605         resp->userInformationField = init_diagnostics(assoc->encode,
1606                                                       binitres->errcode,
1607                                                       binitres->errstring);
1608     }
1609     else
1610         assoc->state = ASSOC_UP;
1611     return apdu;
1612 }
1613
1614 /*
1615  * Diagnostic in default format, to be returned as either a surrogate
1616  * or non-surrogate diagnostic in the context of an open session, or
1617  * as User-information when an Init is refused.
1618  */
1619 static Z_DefaultDiagFormat *justdiag(ODR odr, int error, char *addinfo)
1620 {
1621     int *err = odr_intdup(odr, error);
1622     Z_DefaultDiagFormat *dr = (Z_DefaultDiagFormat *)
1623         odr_malloc (odr, sizeof(*dr));
1624
1625     yaz_log(LOG_LOG, "[%d] %s%s%s", error, diagbib1_str(error),
1626         addinfo ? " -- " : "", addinfo ? addinfo : "");
1627
1628     dr->diagnosticSetId =
1629         yaz_oidval_to_z3950oid (odr, CLASS_DIAGSET, VAL_BIB1);
1630     dr->condition = err;
1631     dr->which = Z_DefaultDiagFormat_v2Addinfo;
1632     dr->u.v2Addinfo = odr_strdup (odr, addinfo ? addinfo : "");
1633     return dr;
1634 }
1635
1636 /*
1637  * Set the specified `errcode' and `errstring' into a UserInfo-1
1638  * external to be returned to the client in accordance with Z35.90
1639  * Implementor Agreement 5 (Returning diagnostics in an InitResponse):
1640  *      http://lcweb.loc.gov/z3950/agency/agree/initdiag.html
1641  */
1642 static Z_External *init_diagnostics(ODR odr, int error, char *addinfo)
1643 {
1644     Z_External *x, *x2;
1645     oident oid;
1646     Z_OtherInformation *u;
1647     Z_OtherInformationUnit *l;
1648     Z_DiagnosticFormat *d;
1649     Z_DiagnosticFormat_s *e;
1650
1651     x = (Z_External*) odr_malloc(odr, sizeof *x);
1652     x->descriptor = 0;
1653     x->indirect_reference = 0;  
1654     oid.proto = PROTO_Z3950;
1655     oid.oclass = CLASS_USERINFO;
1656     oid.value = VAL_USERINFO1;
1657     x->direct_reference = odr_oiddup(odr, oid_getoidbyent(&oid));
1658     x->which = Z_External_userInfo1;
1659
1660     u = odr_malloc(odr, sizeof *u);
1661     x->u.userInfo1 = u;
1662     u->num_elements = 1;
1663     u->list = (Z_OtherInformationUnit**) odr_malloc(odr, sizeof *u->list);
1664     u->list[0] = (Z_OtherInformationUnit*) odr_malloc(odr, sizeof *u->list[0]);
1665     l = u->list[0];
1666     l->category = 0;
1667     l->which = Z_OtherInfo_externallyDefinedInfo;
1668
1669     x2 = (Z_External*) odr_malloc(odr, sizeof *x);
1670     l->information.externallyDefinedInfo = x2;
1671     x2->descriptor = 0;
1672     x2->indirect_reference = 0;
1673     oid.oclass = CLASS_DIAGSET;
1674     oid.value = VAL_DIAG1;
1675     x2->direct_reference = odr_oiddup(odr, oid_getoidbyent(&oid));
1676     x2->which = Z_External_diag1;
1677
1678     d = (Z_DiagnosticFormat*) odr_malloc(odr, sizeof *d);
1679     x2->u.diag1 = d;
1680     d->num = 1;
1681     d->elements = (Z_DiagnosticFormat_s**) odr_malloc (odr, sizeof *d->elements);
1682     d->elements[0] = (Z_DiagnosticFormat_s*) odr_malloc (odr, sizeof *d->elements[0]);
1683     e = d->elements[0];
1684
1685     e->which = Z_DiagnosticFormat_s_defaultDiagRec;
1686     e->u.defaultDiagRec = justdiag(odr, error, addinfo);
1687     return x;
1688 }
1689
1690 /*
1691  * nonsurrogate diagnostic record.
1692  */
1693 static Z_Records *diagrec(association *assoc, int error, char *addinfo)
1694 {
1695     Z_Records *rec = (Z_Records *)
1696         odr_malloc (assoc->encode, sizeof(*rec));
1697     rec->which = Z_Records_NSD;
1698     rec->u.nonSurrogateDiagnostic = justdiag(assoc->encode, error, addinfo);
1699     return rec;
1700 }
1701
1702 /*
1703  * surrogate diagnostic.
1704  */
1705 static Z_NamePlusRecord *surrogatediagrec(association *assoc, char *dbname,
1706                                           int error, char *addinfo)
1707 {
1708     Z_NamePlusRecord *rec = (Z_NamePlusRecord *)
1709         odr_malloc (assoc->encode, sizeof(*rec));
1710     Z_DiagRec *drec = (Z_DiagRec *)odr_malloc (assoc->encode, sizeof(*drec));
1711     
1712     yaz_log(LOG_DEBUG, "SurrogateDiagnotic: %d -- %s", error, addinfo);
1713     rec->databaseName = dbname;
1714     rec->which = Z_NamePlusRecord_surrogateDiagnostic;
1715     rec->u.surrogateDiagnostic = drec;
1716     drec->which = Z_DiagRec_defaultFormat;
1717     drec->u.defaultFormat = justdiag(assoc->encode, error, addinfo);
1718
1719     return rec;
1720 }
1721
1722 /*
1723  * multiple nonsurrogate diagnostics.
1724  */
1725 static Z_DiagRecs *diagrecs(association *assoc, int error, char *addinfo)
1726 {
1727     Z_DiagRecs *recs = (Z_DiagRecs *)odr_malloc (assoc->encode, sizeof(*recs));
1728     int *err = odr_intdup(assoc->encode, error);
1729     Z_DiagRec **recp = (Z_DiagRec **)odr_malloc (assoc->encode, sizeof(*recp));
1730     Z_DiagRec *drec = (Z_DiagRec *)odr_malloc (assoc->encode, sizeof(*drec));
1731     Z_DefaultDiagFormat *rec = (Z_DefaultDiagFormat *)
1732         odr_malloc (assoc->encode, sizeof(*rec));
1733
1734     yaz_log(LOG_DEBUG, "DiagRecs: %d -- %s", error, addinfo ? addinfo : "");
1735
1736     recs->num_diagRecs = 1;
1737     recs->diagRecs = recp;
1738     recp[0] = drec;
1739     drec->which = Z_DiagRec_defaultFormat;
1740     drec->u.defaultFormat = rec;
1741
1742     rec->diagnosticSetId =
1743         yaz_oidval_to_z3950oid (assoc->encode, CLASS_DIAGSET, VAL_BIB1);
1744     rec->condition = err;
1745
1746     rec->which = Z_DefaultDiagFormat_v2Addinfo;
1747     rec->u.v2Addinfo = odr_strdup (assoc->encode, addinfo ? addinfo : "");
1748     return recs;
1749 }
1750
1751 static Z_Records *pack_records(association *a, char *setname, int start,
1752                                int *num, Z_RecordComposition *comp,
1753                                int *next, int *pres, oid_value format,
1754                                Z_ReferenceId *referenceId,
1755                                int *oid)
1756 {
1757     int recno, total_length = 0, toget = *num, dumped_records = 0;
1758     Z_Records *records =
1759         (Z_Records *) odr_malloc (a->encode, sizeof(*records));
1760     Z_NamePlusRecordList *reclist =
1761         (Z_NamePlusRecordList *) odr_malloc (a->encode, sizeof(*reclist));
1762     Z_NamePlusRecord **list =
1763         (Z_NamePlusRecord **) odr_malloc (a->encode, sizeof(*list) * toget);
1764
1765     records->which = Z_Records_DBOSD;
1766     records->u.databaseOrSurDiagnostics = reclist;
1767     reclist->num_records = 0;
1768     reclist->records = list;
1769     *pres = Z_PRES_SUCCESS;
1770     *num = 0;
1771     *next = 0;
1772
1773     yaz_log(LOG_LOG, "Request to pack %d+%d+%s", start, toget, setname);
1774     yaz_log(LOG_DEBUG, "pms=%d, mrs=%d", a->preferredMessageSize,
1775         a->maximumRecordSize);
1776     for (recno = start; reclist->num_records < toget; recno++)
1777     {
1778         bend_fetch_rr freq;
1779         Z_NamePlusRecord *thisrec;
1780         int this_length = 0;
1781         /*
1782          * we get the number of bytes allocated on the stream before any
1783          * allocation done by the backend - this should give us a reasonable
1784          * idea of the total size of the data so far.
1785          */
1786         total_length = odr_total(a->encode) - dumped_records;
1787         freq.errcode = 0;
1788         freq.errstring = 0;
1789         freq.basename = 0;
1790         freq.len = 0;
1791         freq.record = 0;
1792         freq.last_in_set = 0;
1793         freq.setname = setname;
1794         freq.surrogate_flag = 0;
1795         freq.number = recno;
1796         freq.comp = comp;
1797         freq.request_format = format;
1798         freq.request_format_raw = oid;
1799         freq.output_format = format;
1800         freq.output_format_raw = 0;
1801         freq.stream = a->encode;
1802         freq.print = a->print;
1803         freq.referenceId = referenceId;
1804         freq.schema = 0;
1805         (*a->init->bend_fetch)(a->backend, &freq);
1806         /* backend should be able to signal whether error is system-wide
1807            or only pertaining to current record */
1808         if (freq.errcode)
1809         {
1810             if (!freq.surrogate_flag)
1811             {
1812                 char s[20];
1813                 *pres = Z_PRES_FAILURE;
1814                 /* for 'present request out of range',
1815                    set addinfo to record position if not set */
1816                 if (freq.errcode == 13 && freq.errstring == 0)
1817                 {
1818                     sprintf (s, "%d", recno);
1819                     freq.errstring = s;
1820                 }
1821                 return diagrec(a, freq.errcode, freq.errstring);
1822             }
1823             reclist->records[reclist->num_records] =
1824                 surrogatediagrec(a, freq.basename, freq.errcode,
1825                                  freq.errstring);
1826             reclist->num_records++;
1827             *next = freq.last_in_set ? 0 : recno + 1;
1828             continue;
1829         }
1830         if (freq.len >= 0)
1831             this_length = freq.len;
1832         else
1833             this_length = odr_total(a->encode) - total_length;
1834         yaz_log(LOG_DEBUG, "  fetched record, len=%d, total=%d",
1835             this_length, total_length);
1836         if (this_length + total_length > a->preferredMessageSize)
1837         {
1838             /* record is small enough, really */
1839             if (this_length <= a->preferredMessageSize)
1840             {
1841                 yaz_log(LOG_DEBUG, "  Dropped last normal-sized record");
1842                 *pres = Z_PRES_PARTIAL_2;
1843                 break;
1844             }
1845             /* record can only be fetched by itself */
1846             if (this_length < a->maximumRecordSize)
1847             {
1848                 yaz_log(LOG_DEBUG, "  Record > prefmsgsz");
1849                 if (toget > 1)
1850                 {
1851                     yaz_log(LOG_DEBUG, "  Dropped it");
1852                     reclist->records[reclist->num_records] =
1853                          surrogatediagrec(a, freq.basename, 16, 0);
1854                     reclist->num_records++;
1855                     *next = freq.last_in_set ? 0 : recno + 1;
1856                     dumped_records += this_length;
1857                     continue;
1858                 }
1859             }
1860             else /* too big entirely */
1861             {
1862                 yaz_log(LOG_LOG, "Record > maxrcdsz this=%d max=%d", this_length, a->maximumRecordSize);
1863                 reclist->records[reclist->num_records] =
1864                     surrogatediagrec(a, freq.basename, 17, 0);
1865                 reclist->num_records++;
1866                 *next = freq.last_in_set ? 0 : recno + 1;
1867                 dumped_records += this_length;
1868                 continue;
1869             }
1870         }
1871
1872         if (!(thisrec = (Z_NamePlusRecord *)
1873               odr_malloc(a->encode, sizeof(*thisrec))))
1874             return 0;
1875         if (!(thisrec->databaseName = (char *)odr_malloc(a->encode,
1876             strlen(freq.basename) + 1)))
1877             return 0;
1878         strcpy(thisrec->databaseName, freq.basename);
1879         thisrec->which = Z_NamePlusRecord_databaseRecord;
1880
1881         if (freq.output_format_raw)
1882         {
1883             struct oident *ident = oid_getentbyoid(freq.output_format_raw);
1884             freq.output_format = ident->value;
1885         }
1886         thisrec->u.databaseRecord = z_ext_record(a->encode, freq.output_format,
1887                                                  freq.record, freq.len);
1888         if (!thisrec->u.databaseRecord)
1889             return 0;
1890         reclist->records[reclist->num_records] = thisrec;
1891         reclist->num_records++;
1892         *next = freq.last_in_set ? 0 : recno + 1;
1893     }
1894     *num = reclist->num_records;
1895     return records;
1896 }
1897
1898 static Z_APDU *process_searchRequest(association *assoc, request *reqb,
1899     int *fd)
1900 {
1901     Z_SearchRequest *req = reqb->apdu_request->u.searchRequest;
1902     bend_search_rr *bsrr = 
1903         (bend_search_rr *)nmem_malloc (reqb->request_mem, sizeof(*bsrr));
1904     
1905     yaz_log(LOG_LOG, "Got SearchRequest.");
1906     bsrr->fd = fd;
1907     bsrr->request = reqb;
1908     bsrr->association = assoc;
1909     bsrr->referenceId = req->referenceId;
1910     save_referenceId (reqb, bsrr->referenceId);
1911
1912     yaz_log (LOG_LOG, "ResultSet '%s'", req->resultSetName);
1913     if (req->databaseNames)
1914     {
1915         int i;
1916         for (i = 0; i < req->num_databaseNames; i++)
1917             yaz_log (LOG_LOG, "Database '%s'", req->databaseNames[i]);
1918     }
1919     yaz_log_zquery(req->query);
1920
1921     if (assoc->init->bend_search)
1922     {
1923         bsrr->setname = req->resultSetName;
1924         bsrr->replace_set = *req->replaceIndicator;
1925         bsrr->num_bases = req->num_databaseNames;
1926         bsrr->basenames = req->databaseNames;
1927         bsrr->query = req->query;
1928         bsrr->stream = assoc->encode;
1929         bsrr->decode = assoc->decode;
1930         bsrr->print = assoc->print;
1931         bsrr->errcode = 0;
1932         bsrr->hits = 0;
1933         bsrr->errstring = NULL;
1934         bsrr->search_info = NULL;
1935         (assoc->init->bend_search)(assoc->backend, bsrr);
1936         if (!bsrr->request)
1937             return 0;
1938     }
1939     return response_searchRequest(assoc, reqb, bsrr, fd);
1940 }
1941
1942 int bend_searchresponse(void *handle, bend_search_rr *bsrr) {return 0;}
1943
1944 /*
1945  * Prepare a searchresponse based on the backend results. We probably want
1946  * to look at making the fetching of records nonblocking as well, but
1947  * so far, we'll keep things simple.
1948  * If bsrt is null, that means we're called in response to a communications
1949  * event, and we'll have to get the response for ourselves.
1950  */
1951 static Z_APDU *response_searchRequest(association *assoc, request *reqb,
1952     bend_search_rr *bsrt, int *fd)
1953 {
1954     Z_SearchRequest *req = reqb->apdu_request->u.searchRequest;
1955     Z_APDU *apdu = (Z_APDU *)odr_malloc (assoc->encode, sizeof(*apdu));
1956     Z_SearchResponse *resp = (Z_SearchResponse *)
1957         odr_malloc (assoc->encode, sizeof(*resp));
1958     int *nulint = odr_intdup (assoc->encode, 0);
1959     bool_t *sr = odr_intdup(assoc->encode, 1);
1960     int *next = odr_intdup(assoc->encode, 0);
1961     int *none = odr_intdup(assoc->encode, Z_RES_NONE);
1962
1963     apdu->which = Z_APDU_searchResponse;
1964     apdu->u.searchResponse = resp;
1965     resp->referenceId = req->referenceId;
1966     resp->additionalSearchInfo = 0;
1967     resp->otherInfo = 0;
1968     *fd = -1;
1969     if (!bsrt && !bend_searchresponse(assoc->backend, bsrt))
1970     {
1971         yaz_log(LOG_FATAL, "Bad result from backend");
1972         return 0;
1973     }
1974     else if (bsrt->errcode)
1975     {
1976         resp->records = diagrec(assoc, bsrt->errcode, bsrt->errstring);
1977         resp->resultCount = nulint;
1978         resp->numberOfRecordsReturned = nulint;
1979         resp->nextResultSetPosition = nulint;
1980         resp->searchStatus = nulint;
1981         resp->resultSetStatus = none;
1982         resp->presentStatus = 0;
1983     }
1984     else
1985     {
1986         int *toget = odr_intdup(assoc->encode, 0);
1987         int *presst = odr_intdup(assoc->encode, 0);
1988         Z_RecordComposition comp, *compp = 0;
1989
1990         yaz_log (LOG_LOG, "resultCount: %d", bsrt->hits);
1991
1992         resp->records = 0;
1993         resp->resultCount = &bsrt->hits;
1994
1995         comp.which = Z_RecordComp_simple;
1996         /* how many records does the user agent want, then? */
1997         if (bsrt->hits <= *req->smallSetUpperBound)
1998         {
1999             *toget = bsrt->hits;
2000             if ((comp.u.simple = req->smallSetElementSetNames))
2001                 compp = &comp;
2002         }
2003         else if (bsrt->hits < *req->largeSetLowerBound)
2004         {
2005             *toget = *req->mediumSetPresentNumber;
2006             if (*toget > bsrt->hits)
2007                 *toget = bsrt->hits;
2008             if ((comp.u.simple = req->mediumSetElementSetNames))
2009                 compp = &comp;
2010         }
2011         else
2012             *toget = 0;
2013
2014         if (*toget && !resp->records)
2015         {
2016             oident *prefformat;
2017             oid_value form;
2018
2019             if (!(prefformat = oid_getentbyoid(req->preferredRecordSyntax)))
2020                 form = VAL_NONE;
2021             else
2022                 form = prefformat->value;
2023             resp->records = pack_records(assoc, req->resultSetName, 1,
2024                 toget, compp, next, presst, form, req->referenceId,
2025                                          req->preferredRecordSyntax);
2026             if (!resp->records)
2027                 return 0;
2028             resp->numberOfRecordsReturned = toget;
2029             resp->nextResultSetPosition = next;
2030             resp->searchStatus = sr;
2031             resp->resultSetStatus = 0;
2032             resp->presentStatus = presst;
2033         }
2034         else
2035         {
2036             if (*resp->resultCount)
2037                 *next = 1;
2038             resp->numberOfRecordsReturned = nulint;
2039             resp->nextResultSetPosition = next;
2040             resp->searchStatus = sr;
2041             resp->resultSetStatus = 0;
2042             resp->presentStatus = 0;
2043         }
2044     }
2045     resp->additionalSearchInfo = bsrt->search_info;
2046     return apdu;
2047 }
2048
2049 /*
2050  * Maybe we got a little over-friendly when we designed bend_fetch to
2051  * get only one record at a time. Some backends can optimise multiple-record
2052  * fetches, and at any rate, there is some overhead involved in
2053  * all that selecting and hopping around. Problem is, of course, that the
2054  * frontend can't know ahead of time how many records it'll need to
2055  * fill the negotiated PDU size. Annoying. Segmentation or not, Z/SR
2056  * is downright lousy as a bulk data transfer protocol.
2057  *
2058  * To start with, we'll do the fetching of records from the backend
2059  * in one operation: To save some trips in and out of the event-handler,
2060  * and to simplify the interface to pack_records. At any rate, asynch
2061  * operation is more fun in operations that have an unpredictable execution
2062  * speed - which is normally more true for search than for present.
2063  */
2064 static Z_APDU *process_presentRequest(association *assoc, request *reqb,
2065                                       int *fd)
2066 {
2067     Z_PresentRequest *req = reqb->apdu_request->u.presentRequest;
2068     oident *prefformat;
2069     oid_value form;
2070     Z_APDU *apdu;
2071     Z_PresentResponse *resp;
2072     int *next;
2073     int *num;
2074
2075     yaz_log(LOG_LOG, "Got PresentRequest.");
2076
2077     if (!(prefformat = oid_getentbyoid(req->preferredRecordSyntax)))
2078         form = VAL_NONE;
2079     else
2080         form = prefformat->value;
2081     resp = (Z_PresentResponse *)odr_malloc (assoc->encode, sizeof(*resp));
2082     resp->records = 0;
2083     resp->presentStatus = odr_intdup(assoc->encode, 0);
2084     if (assoc->init->bend_present)
2085     {
2086         bend_present_rr *bprr = (bend_present_rr *)
2087             nmem_malloc (reqb->request_mem, sizeof(*bprr));
2088         bprr->setname = req->resultSetId;
2089         bprr->start = *req->resultSetStartPoint;
2090         bprr->number = *req->numberOfRecordsRequested;
2091         bprr->format = form;
2092         bprr->comp = req->recordComposition;
2093         bprr->referenceId = req->referenceId;
2094         bprr->stream = assoc->encode;
2095         bprr->print = assoc->print;
2096         bprr->request = reqb;
2097         bprr->association = assoc;
2098         bprr->errcode = 0;
2099         bprr->errstring = NULL;
2100         (*assoc->init->bend_present)(assoc->backend, bprr);
2101         
2102         if (!bprr->request)
2103             return 0;
2104         if (bprr->errcode)
2105         {
2106             resp->records = diagrec(assoc, bprr->errcode, bprr->errstring);
2107             *resp->presentStatus = Z_PRES_FAILURE;
2108         }
2109     }
2110     apdu = (Z_APDU *)odr_malloc (assoc->encode, sizeof(*apdu));
2111     next = odr_intdup(assoc->encode, 0);
2112     num = odr_intdup(assoc->encode, 0);
2113     
2114     apdu->which = Z_APDU_presentResponse;
2115     apdu->u.presentResponse = resp;
2116     resp->referenceId = req->referenceId;
2117     resp->otherInfo = 0;
2118     
2119     if (!resp->records)
2120     {
2121         *num = *req->numberOfRecordsRequested;
2122         resp->records =
2123             pack_records(assoc, req->resultSetId, *req->resultSetStartPoint,
2124                      num, req->recordComposition, next, resp->presentStatus,
2125                          form, req->referenceId, req->preferredRecordSyntax);
2126     }
2127     if (!resp->records)
2128         return 0;
2129     resp->numberOfRecordsReturned = num;
2130     resp->nextResultSetPosition = next;
2131     
2132     return apdu;
2133 }
2134
2135 /*
2136  * Scan was implemented rather in a hurry, and with support for only the basic
2137  * elements of the service in the backend API. Suggestions are welcome.
2138  */
2139 static Z_APDU *process_scanRequest(association *assoc, request *reqb, int *fd)
2140 {
2141     Z_ScanRequest *req = reqb->apdu_request->u.scanRequest;
2142     Z_APDU *apdu = (Z_APDU *)odr_malloc (assoc->encode, sizeof(*apdu));
2143     Z_ScanResponse *res = (Z_ScanResponse *)
2144         odr_malloc (assoc->encode, sizeof(*res));
2145     int *scanStatus = odr_intdup(assoc->encode, Z_Scan_failure);
2146     int *numberOfEntriesReturned = odr_intdup(assoc->encode, 0);
2147     Z_ListEntries *ents = (Z_ListEntries *)
2148         odr_malloc (assoc->encode, sizeof(*ents));
2149     Z_DiagRecs *diagrecs_p = NULL;
2150     oident *attset;
2151     bend_scan_rr *bsrr = (bend_scan_rr *)
2152         odr_malloc (assoc->encode, sizeof(*bsrr));
2153     struct scan_entry *save_entries;
2154
2155     yaz_log(LOG_LOG, "Got ScanRequest");
2156
2157     apdu->which = Z_APDU_scanResponse;
2158     apdu->u.scanResponse = res;
2159     res->referenceId = req->referenceId;
2160
2161     /* if step is absent, set it to 0 */
2162     res->stepSize = odr_intdup(assoc->encode, 0);
2163     if (req->stepSize)
2164         *res->stepSize = *req->stepSize;
2165
2166     res->scanStatus = scanStatus;
2167     res->numberOfEntriesReturned = numberOfEntriesReturned;
2168     res->positionOfTerm = 0;
2169     res->entries = ents;
2170     ents->num_entries = 0;
2171     ents->entries = NULL;
2172     ents->num_nonsurrogateDiagnostics = 0;
2173     ents->nonsurrogateDiagnostics = NULL;
2174     res->attributeSet = 0;
2175     res->otherInfo = 0;
2176
2177     if (req->databaseNames)
2178     {
2179         int i;
2180         for (i = 0; i < req->num_databaseNames; i++)
2181             yaz_log (LOG_LOG, "Database '%s'", req->databaseNames[i]);
2182     }
2183     bsrr->num_bases = req->num_databaseNames;
2184     bsrr->basenames = req->databaseNames;
2185     bsrr->num_entries = *req->numberOfTermsRequested;
2186     bsrr->term = req->termListAndStartPoint;
2187     bsrr->referenceId = req->referenceId;
2188     bsrr->stream = assoc->encode;
2189     bsrr->print = assoc->print;
2190     bsrr->step_size = res->stepSize;
2191     bsrr->entries = 0;
2192     /* Note that version 2.0 of YAZ and older did not set entries .. 
2193        We do now. And when we do it's easier to extend the scan entry 
2194        We know that if the scan handler did set entries, it will
2195        not know of new member display_term.
2196     */
2197     if (bsrr->num_entries > 0) 
2198     {
2199         int i;
2200         bsrr->entries = odr_malloc(assoc->decode, sizeof(*bsrr->entries) *
2201                                    bsrr->num_entries);
2202         for (i = 0; i<bsrr->num_entries; i++)
2203         {
2204             bsrr->entries[i].term = 0;
2205             bsrr->entries[i].occurrences = 0;
2206             bsrr->entries[i].errcode = 0;
2207             bsrr->entries[i].errstring = 0;
2208             bsrr->entries[i].display_term = 0;
2209         }
2210     }
2211     save_entries = bsrr->entries;  /* save it so we can compare later */
2212
2213     if (req->attributeSet &&
2214         (attset = oid_getentbyoid(req->attributeSet)) &&
2215         (attset->oclass == CLASS_ATTSET || attset->oclass == CLASS_GENERAL))
2216         bsrr->attributeset = attset->value;
2217     else
2218         bsrr->attributeset = VAL_NONE;
2219     log_scan_term (req->termListAndStartPoint, bsrr->attributeset);
2220     bsrr->term_position = req->preferredPositionInResponse ?
2221         *req->preferredPositionInResponse : 1;
2222     ((int (*)(void *, bend_scan_rr *))
2223      (*assoc->init->bend_scan))(assoc->backend, bsrr);
2224     if (bsrr->errcode)
2225         diagrecs_p = diagrecs(assoc, bsrr->errcode, bsrr->errstring);
2226     else
2227     {
2228         int i;
2229         Z_Entry **tab = (Z_Entry **)
2230             odr_malloc (assoc->encode, sizeof(*tab) * bsrr->num_entries);
2231         
2232         if (bsrr->status == BEND_SCAN_PARTIAL)
2233             *scanStatus = Z_Scan_partial_5;
2234         else
2235             *scanStatus = Z_Scan_success;
2236         ents->entries = tab;
2237         ents->num_entries = bsrr->num_entries;
2238         res->numberOfEntriesReturned = &ents->num_entries;          
2239         res->positionOfTerm = &bsrr->term_position;
2240         for (i = 0; i < bsrr->num_entries; i++)
2241         {
2242             Z_Entry *e;
2243             Z_TermInfo *t;
2244             Odr_oct *o;
2245             
2246             tab[i] = e = (Z_Entry *)odr_malloc(assoc->encode, sizeof(*e));
2247             if (bsrr->entries[i].occurrences >= 0)
2248             {
2249                 e->which = Z_Entry_termInfo;
2250                 e->u.termInfo = t = (Z_TermInfo *)
2251                     odr_malloc(assoc->encode, sizeof(*t));
2252                 t->suggestedAttributes = 0;
2253                 t->displayTerm = 0;
2254                 if (save_entries == bsrr->entries && 
2255                     bsrr->entries[i].display_term)
2256                 {
2257                     /* the entries was NOT set by the handler. So it's
2258                        safe to test for new member display_term. It is
2259                        NULL'ed by us.
2260                     */
2261                     t->displayTerm = odr_strdup(assoc->encode,
2262                                                 bsrr->entries[i].display_term);
2263                 }
2264                 t->alternativeTerm = 0;
2265                 t->byAttributes = 0;
2266                 t->otherTermInfo = 0;
2267                 t->globalOccurrences = &bsrr->entries[i].occurrences;
2268                 t->term = (Z_Term *)
2269                     odr_malloc(assoc->encode, sizeof(*t->term));
2270                 t->term->which = Z_Term_general;
2271                 t->term->u.general = o =
2272                     (Odr_oct *)odr_malloc(assoc->encode, sizeof(Odr_oct));
2273                 o->buf = (unsigned char *)
2274                     odr_malloc(assoc->encode, o->len = o->size =
2275                                strlen(bsrr->entries[i].term));
2276                 memcpy(o->buf, bsrr->entries[i].term, o->len);
2277                 yaz_log(LOG_DEBUG, "  term #%d: '%s' (%d)", i,
2278                          bsrr->entries[i].term, bsrr->entries[i].occurrences);
2279             }
2280             else
2281             {
2282                 Z_DiagRecs *drecs = diagrecs (assoc,
2283                                               bsrr->entries[i].errcode,
2284                                               bsrr->entries[i].errstring);
2285                 assert (drecs->num_diagRecs == 1);
2286                 e->which = Z_Entry_surrogateDiagnostic;
2287                 assert (drecs->diagRecs[0]);
2288                 e->u.surrogateDiagnostic = drecs->diagRecs[0];
2289             }
2290         }
2291     }
2292     if (diagrecs_p)
2293     {
2294         ents->num_nonsurrogateDiagnostics = diagrecs_p->num_diagRecs;
2295         ents->nonsurrogateDiagnostics = diagrecs_p->diagRecs;
2296     }
2297     return apdu;
2298 }
2299
2300 static Z_APDU *process_sortRequest(association *assoc, request *reqb,
2301     int *fd)
2302 {
2303     Z_SortRequest *req = reqb->apdu_request->u.sortRequest;
2304     Z_SortResponse *res = (Z_SortResponse *)
2305         odr_malloc (assoc->encode, sizeof(*res));
2306     bend_sort_rr *bsrr = (bend_sort_rr *)
2307         odr_malloc (assoc->encode, sizeof(*bsrr));
2308
2309     Z_APDU *apdu = (Z_APDU *)odr_malloc (assoc->encode, sizeof(*apdu));
2310
2311     yaz_log(LOG_LOG, "Got SortRequest.");
2312
2313     bsrr->num_input_setnames = req->num_inputResultSetNames;
2314     bsrr->input_setnames = req->inputResultSetNames;
2315     bsrr->referenceId = req->referenceId;
2316     bsrr->output_setname = req->sortedResultSetName;
2317     bsrr->sort_sequence = req->sortSequence;
2318     bsrr->stream = assoc->encode;
2319     bsrr->print = assoc->print;
2320
2321     bsrr->sort_status = Z_SortStatus_failure;
2322     bsrr->errcode = 0;
2323     bsrr->errstring = 0;
2324     
2325     (*assoc->init->bend_sort)(assoc->backend, bsrr);
2326     
2327     res->referenceId = bsrr->referenceId;
2328     res->sortStatus = odr_intdup(assoc->encode, bsrr->sort_status);
2329     res->resultSetStatus = 0;
2330     if (bsrr->errcode)
2331     {
2332         Z_DiagRecs *dr = diagrecs (assoc, bsrr->errcode, bsrr->errstring);
2333         res->diagnostics = dr->diagRecs;
2334         res->num_diagnostics = dr->num_diagRecs;
2335     }
2336     else
2337     {
2338         res->num_diagnostics = 0;
2339         res->diagnostics = 0;
2340     }
2341     res->resultCount = 0;
2342     res->otherInfo = 0;
2343
2344     apdu->which = Z_APDU_sortResponse;
2345     apdu->u.sortResponse = res;
2346     return apdu;
2347 }
2348
2349 static Z_APDU *process_deleteRequest(association *assoc, request *reqb,
2350     int *fd)
2351 {
2352     Z_DeleteResultSetRequest *req =
2353         reqb->apdu_request->u.deleteResultSetRequest;
2354     Z_DeleteResultSetResponse *res = (Z_DeleteResultSetResponse *)
2355         odr_malloc (assoc->encode, sizeof(*res));
2356     bend_delete_rr *bdrr = (bend_delete_rr *)
2357         odr_malloc (assoc->encode, sizeof(*bdrr));
2358     Z_APDU *apdu = (Z_APDU *)odr_malloc (assoc->encode, sizeof(*apdu));
2359
2360     yaz_log(LOG_LOG, "Got DeleteRequest.");
2361
2362     bdrr->num_setnames = req->num_resultSetList;
2363     bdrr->setnames = req->resultSetList;
2364     bdrr->stream = assoc->encode;
2365     bdrr->print = assoc->print;
2366     bdrr->function = *req->deleteFunction;
2367     bdrr->referenceId = req->referenceId;
2368     bdrr->statuses = 0;
2369     if (bdrr->num_setnames > 0)
2370     {
2371         int i;
2372         bdrr->statuses = (int*) 
2373             odr_malloc(assoc->encode, sizeof(*bdrr->statuses) *
2374                        bdrr->num_setnames);
2375         for (i = 0; i < bdrr->num_setnames; i++)
2376             bdrr->statuses[i] = 0;
2377     }
2378     (*assoc->init->bend_delete)(assoc->backend, bdrr);
2379     
2380     res->referenceId = req->referenceId;
2381
2382     res->deleteOperationStatus = odr_intdup(assoc->encode,bdrr->delete_status);
2383
2384     res->deleteListStatuses = 0;
2385     if (bdrr->num_setnames > 0)
2386     {
2387         int i;
2388         res->deleteListStatuses = (Z_ListStatuses *)
2389             odr_malloc(assoc->encode, sizeof(*res->deleteListStatuses));
2390         res->deleteListStatuses->num = bdrr->num_setnames;
2391         res->deleteListStatuses->elements =
2392             (Z_ListStatus **)
2393             odr_malloc (assoc->encode, 
2394                         sizeof(*res->deleteListStatuses->elements) *
2395                         bdrr->num_setnames);
2396         for (i = 0; i<bdrr->num_setnames; i++)
2397         {
2398             res->deleteListStatuses->elements[i] =
2399                 (Z_ListStatus *)
2400                 odr_malloc (assoc->encode,
2401                             sizeof(**res->deleteListStatuses->elements));
2402             res->deleteListStatuses->elements[i]->status = bdrr->statuses+i;
2403             res->deleteListStatuses->elements[i]->id =
2404                 odr_strdup (assoc->encode, bdrr->setnames[i]);
2405             
2406         }
2407     }
2408     res->numberNotDeleted = 0;
2409     res->bulkStatuses = 0;
2410     res->deleteMessage = 0;
2411     res->otherInfo = 0;
2412
2413     apdu->which = Z_APDU_deleteResultSetResponse;
2414     apdu->u.deleteResultSetResponse = res;
2415     return apdu;
2416 }
2417
2418 static void process_close(association *assoc, request *reqb)
2419 {
2420     Z_Close *req = reqb->apdu_request->u.close;
2421     static char *reasons[] =
2422     {
2423         "finished",
2424         "shutdown",
2425         "systemProblem",
2426         "costLimit",
2427         "resources",
2428         "securityViolation",
2429         "protocolError",
2430         "lackOfActivity",
2431         "peerAbort",
2432         "unspecified"
2433     };
2434
2435     yaz_log(LOG_LOG, "Got Close, reason %s, message %s",
2436         reasons[*req->closeReason], req->diagnosticInformation ?
2437         req->diagnosticInformation : "NULL");
2438     if (assoc->version < 3) /* to make do_force respond with close */
2439         assoc->version = 3;
2440     do_close_req(assoc, Z_Close_finished,
2441                  "Association terminated by client", reqb);
2442 }
2443
2444 void save_referenceId (request *reqb, Z_ReferenceId *refid)
2445 {
2446     if (refid)
2447     {
2448         reqb->len_refid = refid->len;
2449         reqb->refid = (char *)nmem_malloc (reqb->request_mem, refid->len);
2450         memcpy (reqb->refid, refid->buf, refid->len);
2451     }
2452     else
2453     {
2454         reqb->len_refid = 0;
2455         reqb->refid = NULL;
2456     }
2457 }
2458
2459 void bend_request_send (bend_association a, bend_request req, Z_APDU *res)
2460 {
2461     process_z_response (a, req, res);
2462 }
2463
2464 bend_request bend_request_mk (bend_association a)
2465 {
2466     request *nreq = request_get (&a->outgoing);
2467     nreq->request_mem = nmem_create ();
2468     return nreq;
2469 }
2470
2471 Z_ReferenceId *bend_request_getid (ODR odr, bend_request req)
2472 {
2473     Z_ReferenceId *id;
2474     if (!req->refid)
2475         return 0;
2476     id = (Odr_oct *)odr_malloc (odr, sizeof(*odr));
2477     id->buf = (unsigned char *)odr_malloc (odr, req->len_refid);
2478     id->len = id->size = req->len_refid;
2479     memcpy (id->buf, req->refid, req->len_refid);
2480     return id;
2481 }
2482
2483 void bend_request_destroy (bend_request *req)
2484 {
2485     nmem_destroy((*req)->request_mem);
2486     request_release(*req);
2487     *req = NULL;
2488 }
2489
2490 int bend_backend_respond (bend_association a, bend_request req)
2491 {
2492     char *msg;
2493     int r;
2494     r = process_z_request (a, req, &msg);
2495     if (r < 0)
2496         yaz_log (LOG_WARN, "%s", msg);
2497     return r;
2498 }
2499
2500 void bend_request_setdata(bend_request r, void *p)
2501 {
2502     r->clientData = p;
2503 }
2504
2505 void *bend_request_getdata(bend_request r)
2506 {
2507     return r->clientData;
2508 }
2509
2510 static Z_APDU *process_segmentRequest (association *assoc, request *reqb)
2511 {
2512     bend_segment_rr req;
2513
2514     req.segment = reqb->apdu_request->u.segmentRequest;
2515     req.stream = assoc->encode;
2516     req.decode = assoc->decode;
2517     req.print = assoc->print;
2518     req.association = assoc;
2519     
2520     (*assoc->init->bend_segment)(assoc->backend, &req);
2521
2522     return 0;
2523 }
2524
2525 static Z_APDU *process_ESRequest(association *assoc, request *reqb, int *fd)
2526 {
2527     bend_esrequest_rr esrequest;
2528
2529     Z_ExtendedServicesRequest *req =
2530         reqb->apdu_request->u.extendedServicesRequest;
2531     Z_APDU *apdu = zget_APDU(assoc->encode, Z_APDU_extendedServicesResponse);
2532
2533     Z_ExtendedServicesResponse *resp = apdu->u.extendedServicesResponse;
2534
2535     yaz_log(LOG_DEBUG,"inside Process esRequest");
2536
2537     esrequest.esr = reqb->apdu_request->u.extendedServicesRequest;
2538     esrequest.stream = assoc->encode;
2539     esrequest.decode = assoc->decode;
2540     esrequest.print = assoc->print;
2541     esrequest.errcode = 0;
2542     esrequest.errstring = NULL;
2543     esrequest.request = reqb;
2544     esrequest.association = assoc;
2545     esrequest.taskPackage = 0;
2546     esrequest.referenceId = req->referenceId;
2547     
2548     (*assoc->init->bend_esrequest)(assoc->backend, &esrequest);
2549     
2550     /* If the response is being delayed, return NULL */
2551     if (esrequest.request == NULL)
2552         return(NULL);
2553
2554     resp->referenceId = req->referenceId;
2555
2556     if (esrequest.errcode == -1)
2557     {
2558         /* Backend service indicates request will be processed */
2559         yaz_log(LOG_DEBUG,"Request could be processed...Accepted !");
2560         *resp->operationStatus = Z_ExtendedServicesResponse_accepted;
2561     }
2562     else if (esrequest.errcode == 0)
2563     {
2564         /* Backend service indicates request will be processed */
2565         yaz_log(LOG_DEBUG,"Request could be processed...Done !");
2566         *resp->operationStatus = Z_ExtendedServicesResponse_done;
2567     }
2568     else
2569     {
2570         Z_DiagRecs *diagRecs = diagrecs (assoc, esrequest.errcode,
2571                                          esrequest.errstring);
2572
2573         /* Backend indicates error, request will not be processed */
2574         yaz_log(LOG_DEBUG,"Request could not be processed...failure !");
2575         *resp->operationStatus = Z_ExtendedServicesResponse_failure;
2576         resp->num_diagnostics = diagRecs->num_diagRecs;
2577         resp->diagnostics = diagRecs->diagRecs;
2578     }
2579     /* Do something with the members of bend_extendedservice */
2580     if (esrequest.taskPackage)
2581         resp->taskPackage = z_ext_record (assoc->encode, VAL_EXTENDED,
2582                                          (const char *)  esrequest.taskPackage,
2583                                           -1);
2584     yaz_log(LOG_DEBUG,"Send the result apdu");
2585     return apdu;
2586 }
2587