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