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