For GFS: schema for bend_fetch; displayTerm for bend_scan.
[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.154 2003-04-24 13:30:32 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         srw_res->numberOfRecords = odr_intdup(assoc->encode, rr.hits);
662         if (srw_req->maximumRecords && *srw_req->maximumRecords > 0)
663         {
664             int number = *srw_req->maximumRecords;
665             int start = 1;
666             int i;
667
668             if (srw_req->startRecord)
669                 start = *srw_req->startRecord;
670
671             yaz_log(LOG_DEBUG, "srw_bend_search. start=%d max=%d",
672                     start, *srw_req->maximumRecords);
673
674             if (start <= rr.hits)
675             {
676                 int j = 0;
677                 int packing = Z_SRW_recordPacking_string;
678                 if (start + number > rr.hits)
679                     number = rr.hits - start + 1;
680                 if (srw_req->recordPacking && 
681                     !strcmp(srw_req->recordPacking, "xml"))
682                     packing = Z_SRW_recordPacking_XML;
683                 srw_res->records = (Z_SRW_record *)
684                     odr_malloc(assoc->encode,
685                                number * sizeof(*srw_res->records));
686                 for (i = 0; i<number; i++)
687                 {
688                     int errcode;
689                     
690                     srw_res->records[j].recordPacking = packing;
691                     srw_res->records[j].recordData_buf = 0;
692                     yaz_log(LOG_DEBUG, "srw_bend_fetch %d", i+start);
693                     errcode = srw_bend_fetch(assoc, i+start, srw_req,
694                                              srw_res->records + j);
695                     if (errcode)
696                     {
697                         srw_res->num_diagnostics = 1;
698                         srw_res->diagnostics = (Z_SRW_diagnostic *)
699                             odr_malloc(assoc->encode, 
700                                        sizeof(*srw_res->diagnostics));
701                         srw_res->diagnostics[0].code = 
702                             odr_intdup(assoc->encode, 
703                                        yaz_diag_bib1_to_srw (errcode));
704                         srw_res->diagnostics[0].details = rr.errstring;
705                         break;
706                     }
707                     if (srw_res->records[j].recordData_buf)
708                         j++;
709                 }
710                 srw_res->num_records = j;
711                 if (!j)
712                     srw_res->records = 0;
713             }
714         }
715     }
716 }
717
718
719 static void srw_bend_explain(association *assoc, request *req,
720                              Z_SRW_explainRequest *srw_req,
721                              Z_SRW_explainResponse *srw_res)
722 {
723     yaz_log(LOG_LOG, "Got SRW ExplainRequest");
724     if (!assoc->init)
725     {
726         yaz_log(LOG_DEBUG, "srw_bend_init");
727         if (!srw_bend_init(assoc))
728             return;
729     }
730     if (assoc->init && assoc->init->bend_explain)
731     {
732         bend_explain_rr rr;
733
734         rr.stream = assoc->encode;
735         rr.decode = assoc->decode;
736         rr.print = assoc->print;
737         rr.explain_buf = 0;
738         (*assoc->init->bend_explain)(assoc->backend, &rr);
739         if (rr.explain_buf)
740         {
741             srw_res->explainData_buf = rr.explain_buf;
742             srw_res->explainData_len = strlen(rr.explain_buf);
743         }
744     }
745 }
746
747 static int hex_digit (int ch)
748 {
749     if (ch >= '0' && ch <= '9')
750         return ch - '0';
751     else if (ch >= 'a' && ch <= 'f')
752         return ch - 'a'+10;
753     else if (ch >= 'A' && ch <= 'F')
754         return ch - 'A'+10;
755     return 0;
756 }
757
758 static char *uri_val(const char *path, const char *name, ODR o)
759 {
760     size_t nlen = strlen(name);
761     if (*path != '?')
762         return 0;
763     path++;
764     while (path && *path)
765     {
766         const char *p1 = strchr(path, '=');
767         if (!p1)
768             break;
769         if (p1 - path == nlen && !memcmp(path, name, nlen))
770         {
771             size_t i = 0;
772             char *ret;
773             
774             path = p1 + 1;
775             p1 = strchr(path, '&');
776             if (!p1)
777                 p1 = strlen(path) + path;
778             ret = odr_malloc(o, p1 - path + 1);
779             while (*path && *path != '&')
780             {
781                 if (*path == '+')
782                 {
783                     ret[i++] = ' ';
784                     path++;
785                 }
786                 else if (*path == '%' && path[1] && path[2])
787                 {
788                     ret[i++] = hex_digit (path[1])*16 + hex_digit (path[2]);
789                     path = path + 3;
790                 }
791                 else
792                     ret[i++] = *path++;
793             }
794             ret[i] = '\0';
795             return ret;
796         }
797         path = strchr(p1, '&');
798         if (path)
799             path++;
800     }
801     return 0;
802 }
803
804 void uri_val_int(const char *path, const char *name, ODR o, int **intp)
805 {
806     const char *v = uri_val(path, name, o);
807     if (v)
808         *intp = odr_intdup(o, atoi(v));
809 }
810
811 static void process_http_request(association *assoc, request *req)
812 {
813     Z_HTTP_Request *hreq = req->gdu_request->u.HTTP_Request;
814     ODR o = assoc->encode;
815     Z_GDU *p = 0;
816     Z_HTTP_Response *hres = 0;
817     int keepalive = 1;
818
819     if (!strcmp(hreq->method, "GET"))
820     {
821         char *charset = 0;
822         int ret = -1;
823         Z_SOAP *soap_package = 0;
824         char *db = "Default";
825         const char *p0 = hreq->path, *p1;
826         static Z_SOAP_Handler soap_handlers[2] = {
827 #if HAVE_XML2
828             {"http://www.loc.gov/zing/srw/v1.0/", 0,
829              (Z_SOAP_fun) yaz_srw_codec},
830 #endif
831             {0, 0, 0}
832         };
833         
834         if (*p0 == '/')
835             p0++;
836         p1 = strchr(p0, '?');
837         if (!p1)
838             p1 = p0 + strlen(p0);
839         if (p1 != p0)
840         {
841             db = odr_malloc(assoc->decode, p1 - p0 + 1);
842             memcpy (db, p0, p1 - p0);
843             db[p1 - p0] = '\0';
844         }
845 #if HAVE_XML2
846         if (p1 && *p1 == '?' && p1[1])
847         {
848             Z_SRW_PDU *res = yaz_srw_get(o, Z_SRW_searchRetrieve_response);
849             Z_SRW_PDU *sr = yaz_srw_get(o, Z_SRW_searchRetrieve_request);
850             char *query = uri_val(p1, "query", o);
851             char *pQuery = uri_val(p1, "pQuery", o);
852             char *sortKeys = uri_val(p1, "sortKeys", o);
853             
854             if (query)
855             {
856                 sr->u.request->query_type = Z_SRW_query_type_cql;
857                 sr->u.request->query.cql = query;
858             }
859             if (pQuery)
860             {
861                 sr->u.request->query_type = Z_SRW_query_type_pqf;
862                 sr->u.request->query.pqf = pQuery;
863             }
864             if (sortKeys)
865             {
866                 sr->u.request->sort_type = Z_SRW_sort_type_sort;
867                 sr->u.request->sort.sortKeys = sortKeys;
868             }
869             sr->u.request->recordSchema = uri_val(p1, "recordSchema", o);
870             sr->u.request->recordPacking = uri_val(p1, "recordPacking", o);
871             if (!sr->u.request->recordPacking)
872                 sr->u.request->recordPacking = "xml";
873             uri_val_int(p1, "maximumRecords", o, 
874                         &sr->u.request->maximumRecords);
875             uri_val_int(p1, "startRecord", o,
876                         &sr->u.request->startRecord);
877             if (sr->u.request->startRecord)
878                 yaz_log(LOG_LOG, "startRecord=%d", *sr->u.request->startRecord);
879             sr->u.request->database = db;
880             srw_bend_search(assoc, req, sr->u.request, res->u.response);
881             
882             soap_package = odr_malloc(o, sizeof(*soap_package));
883             soap_package->which = Z_SOAP_generic;
884
885             soap_package->u.generic =
886                 odr_malloc(o, sizeof(*soap_package->u.generic));
887
888             soap_package->u.generic->p = res;
889             soap_package->u.generic->ns = soap_handlers[0].ns;
890             soap_package->u.generic->no = 0;
891             
892             soap_package->ns = "SRU";
893
894             p = z_get_HTTP_Response(o, 200);
895             hres = p->u.HTTP_Response;
896
897             ret = z_soap_codec_enc(assoc->encode, &soap_package,
898                                    &hres->content_buf, &hres->content_len,
899                                    soap_handlers, charset);
900             if (!charset)
901                 z_HTTP_header_add(o, &hres->headers, "Content-Type", "text/xml");
902             else
903             {
904                 char ctype[60];
905                 strcpy(ctype, "text/xml; charset=");
906                 strcat(ctype, charset);
907                 z_HTTP_header_add(o, &hres->headers, "Content-Type", ctype);
908             }
909
910         }
911         else
912         {
913             Z_SRW_PDU *res = yaz_srw_get(o, Z_SRW_explain_response);
914             Z_SRW_PDU *sr = yaz_srw_get(o, Z_SRW_explain_request);
915
916             srw_bend_explain(assoc, req, sr->u.explain_request,
917                             res->u.explain_response);
918
919             if (res->u.explain_response->explainData_buf)
920             {
921                 soap_package = odr_malloc(o, sizeof(*soap_package));
922                 soap_package->which = Z_SOAP_generic;
923                 
924                 soap_package->u.generic =
925                     odr_malloc(o, sizeof(*soap_package->u.generic));
926                 
927                 soap_package->u.generic->p = res;
928                 soap_package->u.generic->ns = soap_handlers[0].ns;
929                 soap_package->u.generic->no = 0;
930                 
931                 soap_package->ns = "SRU";
932                 
933                 p = z_get_HTTP_Response(o, 200);
934                 hres = p->u.HTTP_Response;
935                 
936                 ret = z_soap_codec_enc(assoc->encode, &soap_package,
937                                        &hres->content_buf, &hres->content_len,
938                                        soap_handlers, charset);
939                 if (!charset)
940                     z_HTTP_header_add(o, &hres->headers, "Content-Type", "text/xml");
941                 else
942                 {
943                     char ctype[60];
944                     strcpy(ctype, "text/xml; charset=");
945                     strcat(ctype, charset);
946                     z_HTTP_header_add(o, &hres->headers, "Content-Type",
947                                       ctype);
948                 }
949             }
950         }
951 #endif
952 #ifdef DOCDIR
953         if (strlen(hreq->path) >= 5 && strlen(hreq->path) < 80 &&
954                          !memcmp(hreq->path, "/doc/", 5))
955         {
956             FILE *f;
957             char fpath[120];
958
959             strcpy(fpath, DOCDIR);
960             strcat(fpath, hreq->path+4);
961             f = fopen(fpath, "rb");
962             if (f) {
963                 struct stat sbuf;
964                 if (fstat(fileno(f), &sbuf) || !S_ISREG(sbuf.st_mode))
965                 {
966                     fclose(f);
967                     f = 0;
968                 }
969             }
970             if (f)
971             {
972                 long sz;
973                 fseek(f, 0L, SEEK_END);
974                 sz = ftell(f);
975                 if (sz >= 0 && sz < 500000)
976                 {
977                     const char *ctype = "application/octet-stream";
978                     const char *cp;
979
980                     p = z_get_HTTP_Response(o, 200);
981                     hres = p->u.HTTP_Response;
982                     hres->content_buf = (char *) odr_malloc(o, sz + 1);
983                     hres->content_len = sz;
984                     fseek(f, 0L, SEEK_SET);
985                     fread(hres->content_buf, 1, sz, f);
986                     if ((cp = strrchr(fpath, '.'))) {
987                         cp++;
988                         if (!strcmp(cp, "png"))
989                             ctype = "image/png";
990                         else if (!strcmp(cp, "gif"))
991                             ctype = "image/gif";
992                         else if (!strcmp(cp, "xml"))
993                             ctype = "text/xml";
994                         else if (!strcmp(cp, "html"))
995                             ctype = "text/html";
996                     }
997                     z_HTTP_header_add(o, &hres->headers, "Content-Type", ctype);
998                 }
999                 fclose(f);
1000             }
1001         }
1002 #endif
1003
1004 #if 0
1005         if (!strcmp(hreq->path, "/")) 
1006         {
1007 #ifdef DOCDIR
1008             struct stat sbuf;
1009 #endif
1010             const char *doclink = "";
1011             p = z_get_HTTP_Response(o, 200);
1012             hres = p->u.HTTP_Response;
1013             hres->content_buf = (char *) odr_malloc(o, 400);
1014 #ifdef DOCDIR
1015             if (stat(DOCDIR "/yaz.html", &sbuf) == 0 && S_ISREG(sbuf.st_mode))
1016                 doclink = "<P><A HREF=\"/doc/yaz.html\">Documentation</A></P>";
1017 #endif
1018             sprintf (hres->content_buf, 
1019                      "<!DOCTYPE HTML PUBLIC \"-//IETF//DTD HTML 2.0//EN\">\n"
1020                      "<HTML>\n"
1021                      " <HEAD>\n"
1022                      "  <TITLE>YAZ " YAZ_VERSION "</TITLE>\n"
1023                      " </HEAD>\n"
1024                      " <BODY>\n"
1025                      "  <P><A HREF=\"http://www.indexdata.dk/yaz/\">YAZ</A> " 
1026                      YAZ_VERSION "</P>\n"
1027                      "%s"
1028                      " </BODY>\n"
1029                      "</HTML>\n", doclink);
1030             hres->content_len = strlen(hres->content_buf);
1031             z_HTTP_header_add(o, &hres->headers, "Content-Type", "text/html");
1032         }
1033 #endif
1034
1035         if (!p)
1036         {
1037             p = z_get_HTTP_Response(o, 404);
1038         }
1039     }
1040     else if (!strcmp(hreq->method, "POST"))
1041     {
1042         const char *content_type = z_HTTP_header_lookup(hreq->headers,
1043                                                         "Content-Type");
1044         if (content_type && !yaz_strcmp_del("text/xml", content_type, "; "))
1045         {
1046             Z_SOAP *soap_package = 0;
1047             int ret = -1;
1048             int http_code = 500;
1049             const char *charset_p = 0;
1050             char *charset = 0;
1051
1052             static Z_SOAP_Handler soap_handlers[2] = {
1053 #if HAVE_XML2
1054                 {"http://www.loc.gov/zing/srw/v1.0/", 0,
1055                  (Z_SOAP_fun) yaz_srw_codec},
1056 #endif
1057                 {0, 0, 0}
1058             };
1059             if ((charset_p = strstr(content_type, "; charset=")))
1060             {
1061                 int i = 0;
1062                 charset_p += 10;
1063                 while (i < 20 && charset_p[i] &&
1064                        !strchr("; \n\r", charset_p[i]))
1065                     i++;
1066                 charset = odr_malloc(assoc->encode, i+1);
1067                 memcpy(charset, charset_p, i);
1068                 charset[i] = '\0';
1069                 yaz_log(LOG_LOG, "SOAP encoding %s", charset);
1070             }
1071             ret = z_soap_codec(assoc->decode, &soap_package, 
1072                                &hreq->content_buf, &hreq->content_len,
1073                                soap_handlers);
1074 #if HAVE_XML2
1075             if (!ret && soap_package->which == Z_SOAP_generic &&
1076                 soap_package->u.generic->no == 0)
1077             {
1078                 /* SRW package */
1079                 Z_SRW_PDU *sr = soap_package->u.generic->p;
1080                 
1081                 if (sr->which == Z_SRW_searchRetrieve_request)
1082                 {
1083                     Z_SRW_PDU *res =
1084                         yaz_srw_get(assoc->encode,
1085                                     Z_SRW_searchRetrieve_response);
1086
1087                     if (!sr->u.request->database)
1088                     {
1089                         const char *p0 = hreq->path, *p1;
1090                         if (*p0 == '/')
1091                             p0++;
1092                         p1 = strchr(p0, '?');
1093                         if (!p1)
1094                             p1 = p0 + strlen(p0);
1095                         if (p1 != p0)
1096                         {
1097                             sr->u.request->database =
1098                                 odr_malloc(assoc->decode, p1 - p0 + 1);
1099                             memcpy (sr->u.request->database, p0, p1 - p0);
1100                             sr->u.request->database[p1 - p0] = '\0';
1101                         }
1102                         else
1103                             sr->u.request->database = "Default";
1104                     }
1105                     srw_bend_search(assoc, req, sr->u.request,
1106                                     res->u.response);
1107                     
1108                     soap_package->u.generic->p = res;
1109                     http_code = 200;
1110                 }
1111                 else if (sr->which == Z_SRW_explain_request)
1112                 {
1113                     Z_SRW_PDU *res =
1114                         yaz_srw_get(assoc->encode, Z_SRW_explain_response);
1115
1116                     srw_bend_explain(assoc, req, sr->u.explain_request,
1117                                      res->u.explain_response);
1118                     if (!res->u.explain_response->explainData_buf)
1119                     {
1120                         z_soap_error(assoc->encode, soap_package,
1121                                      "SOAP-ENV:Client", "Explain Not Supported", 0);
1122                     }
1123                     else
1124                     {
1125                         soap_package->u.generic->p = res;
1126                         http_code = 200;
1127                     }
1128                 }
1129                 else
1130                 {
1131                     z_soap_error(assoc->encode, soap_package,
1132                                  "SOAP-ENV:Client", "Bad method", 0); 
1133                 }
1134             }
1135 #endif
1136             p = z_get_HTTP_Response(o, 200);
1137             hres = p->u.HTTP_Response;
1138             ret = z_soap_codec_enc(assoc->encode, &soap_package,
1139                                    &hres->content_buf, &hres->content_len,
1140                                    soap_handlers, charset);
1141             hres->code = http_code;
1142             if (!charset)
1143                 z_HTTP_header_add(o, &hres->headers, "Content-Type", "text/xml");
1144             else
1145             {
1146                 char ctype[60];
1147                 strcpy(ctype, "text/xml; charset=");
1148                 strcat(ctype, charset);
1149                 z_HTTP_header_add(o, &hres->headers, "Content-Type", ctype);
1150             }
1151         }
1152         if (!p) /* still no response ? */
1153             p = z_get_HTTP_Response(o, 500);
1154     }
1155     else
1156     {
1157         p = z_get_HTTP_Response(o, 405);
1158         hres = p->u.HTTP_Response;
1159
1160         z_HTTP_header_add(o, &hres->headers, "Allow", "GET, POST");
1161     }
1162     hres = p->u.HTTP_Response;
1163     if (!strcmp(hreq->version, "1.0")) 
1164     {
1165         const char *v = z_HTTP_header_lookup(hreq->headers, "Connection");
1166         if (v && !strcmp(v, "Keep-Alive"))
1167             keepalive = 1;
1168         else
1169             keepalive = 0;
1170         hres->version = "1.0";
1171     }
1172     else
1173     {
1174         const char *v = z_HTTP_header_lookup(hreq->headers, "Connection");
1175         if (v && !strcmp(v, "close"))
1176             keepalive = 0;
1177         else
1178             keepalive = 1;
1179         hres->version = "1.1";
1180     }
1181     if (!keepalive)
1182     {
1183         z_HTTP_header_add(o, &hres->headers, "Connection", "close");
1184         assoc->state = ASSOC_DEAD;
1185     }
1186     else
1187     {
1188         int t;
1189         const char *alive = z_HTTP_header_lookup(hreq->headers, "Keep-Alive");
1190
1191         if (alive && isdigit(*alive))
1192             t = atoi(alive);
1193         else
1194             t = 15;
1195         if (t < 0 || t > 3600)
1196             t = 3600;
1197         iochan_settimeout(assoc->client_chan,t);
1198         z_HTTP_header_add(o, &hres->headers, "Connection", "Keep-Alive");
1199     }
1200     process_gdu_response(assoc, req, p);
1201 }
1202
1203 static void process_gdu_request(association *assoc, request *req)
1204 {
1205     if (req->gdu_request->which == Z_GDU_Z3950)
1206     {
1207         char *msg = 0;
1208         req->apdu_request = req->gdu_request->u.z3950;
1209         if (process_z_request(assoc, req, &msg) < 0)
1210             do_close_req(assoc, Z_Close_systemProblem, msg, req);
1211     }
1212     else if (req->gdu_request->which == Z_GDU_HTTP_Request)
1213         process_http_request(assoc, req);
1214     else
1215     {
1216         do_close_req(assoc, Z_Close_systemProblem, "bad protocol packet", req);
1217     }
1218 }
1219
1220 /*
1221  * Initiate request processing.
1222  */
1223 static int process_z_request(association *assoc, request *req, char **msg)
1224 {
1225     int fd = -1;
1226     Z_APDU *res;
1227     int retval;
1228     
1229     *msg = "Unknown Error";
1230     assert(req && req->state == REQUEST_IDLE);
1231     if (req->apdu_request->which != Z_APDU_initRequest && !assoc->init)
1232     {
1233         *msg = "Missing InitRequest";
1234         return -1;
1235     }
1236     switch (req->apdu_request->which)
1237     {
1238     case Z_APDU_initRequest:
1239         iochan_settimeout(assoc->client_chan,
1240                           statserv_getcontrol()->idle_timeout * 60);
1241         res = process_initRequest(assoc, req); break;
1242     case Z_APDU_searchRequest:
1243         res = process_searchRequest(assoc, req, &fd); break;
1244     case Z_APDU_presentRequest:
1245         res = process_presentRequest(assoc, req, &fd); break;
1246     case Z_APDU_scanRequest:
1247         if (assoc->init->bend_scan)
1248             res = process_scanRequest(assoc, req, &fd);
1249         else
1250         {
1251             *msg = "Cannot handle Scan APDU";
1252             return -1;
1253         }
1254         break;
1255     case Z_APDU_extendedServicesRequest:
1256         if (assoc->init->bend_esrequest)
1257             res = process_ESRequest(assoc, req, &fd);
1258         else
1259         {
1260             *msg = "Cannot handle Extended Services APDU";
1261             return -1;
1262         }
1263         break;
1264     case Z_APDU_sortRequest:
1265         if (assoc->init->bend_sort)
1266             res = process_sortRequest(assoc, req, &fd);
1267         else
1268         {
1269             *msg = "Cannot handle Sort APDU";
1270             return -1;
1271         }
1272         break;
1273     case Z_APDU_close:
1274         process_close(assoc, req);
1275         return 0;
1276     case Z_APDU_deleteResultSetRequest:
1277         if (assoc->init->bend_delete)
1278             res = process_deleteRequest(assoc, req, &fd);
1279         else
1280         {
1281             *msg = "Cannot handle Delete APDU";
1282             return -1;
1283         }
1284         break;
1285     case Z_APDU_segmentRequest:
1286         if (assoc->init->bend_segment)
1287         {
1288             res = process_segmentRequest (assoc, req);
1289         }
1290         else
1291         {
1292             *msg = "Cannot handle Segment APDU";
1293             return -1;
1294         }
1295         break;
1296     default:
1297         *msg = "Bad APDU received";
1298         return -1;
1299     }
1300     if (res)
1301     {
1302         yaz_log(LOG_DEBUG, "  result immediately available");
1303         retval = process_z_response(assoc, req, res);
1304     }
1305     else if (fd < 0)
1306     {
1307         yaz_log(LOG_DEBUG, "  result unavailble");
1308         retval = 0;
1309     }
1310     else /* no result yet - one will be provided later */
1311     {
1312         IOCHAN chan;
1313
1314         /* Set up an I/O handler for the fd supplied by the backend */
1315
1316         yaz_log(LOG_DEBUG, "   establishing handler for result");
1317         req->state = REQUEST_PENDING;
1318         if (!(chan = iochan_create(fd, backend_response, EVENT_INPUT)))
1319             abort();
1320         iochan_setdata(chan, assoc);
1321         retval = 0;
1322     }
1323     return retval;
1324 }
1325
1326 /*
1327  * Handle message from the backend.
1328  */
1329 void backend_response(IOCHAN i, int event)
1330 {
1331     association *assoc = (association *)iochan_getdata(i);
1332     request *req = request_head(&assoc->incoming);
1333     Z_APDU *res;
1334     int fd;
1335
1336     yaz_log(LOG_DEBUG, "backend_response");
1337     assert(assoc && req && req->state != REQUEST_IDLE);
1338     /* determine what it is we're waiting for */
1339     switch (req->apdu_request->which)
1340     {
1341         case Z_APDU_searchRequest:
1342             res = response_searchRequest(assoc, req, 0, &fd); break;
1343 #if 0
1344         case Z_APDU_presentRequest:
1345             res = response_presentRequest(assoc, req, 0, &fd); break;
1346         case Z_APDU_scanRequest:
1347             res = response_scanRequest(assoc, req, 0, &fd); break;
1348 #endif
1349         default:
1350             yaz_log(LOG_WARN, "Serious programmer's lapse or bug");
1351             abort();
1352     }
1353     if ((res && process_z_response(assoc, req, res) < 0) || fd < 0)
1354     {
1355         yaz_log(LOG_LOG, "Fatal error when talking to backend");
1356         do_close(assoc, Z_Close_systemProblem, 0);
1357         iochan_destroy(i);
1358         return;
1359     }
1360     else if (!res) /* no result yet - try again later */
1361     {
1362         yaz_log(LOG_DEBUG, "   no result yet");
1363         iochan_setfd(i, fd); /* in case fd has changed */
1364     }
1365 }
1366
1367 /*
1368  * Encode response, and transfer the request structure to the outgoing queue.
1369  */
1370 static int process_gdu_response(association *assoc, request *req, Z_GDU *res)
1371 {
1372     odr_setbuf(assoc->encode, req->response, req->size_response, 1);
1373
1374     if (assoc->print && !z_GDU(assoc->print, &res, 0, 0))
1375     {
1376         yaz_log(LOG_WARN, "ODR print error: %s", 
1377             odr_errmsg(odr_geterror(assoc->print)));
1378         odr_reset(assoc->print);
1379     }
1380     if (!z_GDU(assoc->encode, &res, 0, 0))
1381     {
1382         yaz_log(LOG_WARN, "ODR error when encoding response: %s",
1383             odr_errmsg(odr_geterror(assoc->decode)));
1384         return -1;
1385     }
1386     req->response = odr_getbuf(assoc->encode, &req->len_response,
1387         &req->size_response);
1388     odr_setbuf(assoc->encode, 0, 0, 0); /* don'txfree if we abort later */
1389     odr_reset(assoc->encode);
1390     req->state = REQUEST_IDLE;
1391     request_enq(&assoc->outgoing, req);
1392     /* turn the work over to the ir_session handler */
1393     iochan_setflag(assoc->client_chan, EVENT_OUTPUT);
1394     assoc->cs_put_mask = EVENT_OUTPUT;
1395     /* Is there more work to be done? give that to the input handler too */
1396 #if 1
1397     if (request_head(&assoc->incoming))
1398     {
1399         yaz_log (LOG_DEBUG, "more work to be done");
1400         iochan_setevent(assoc->client_chan, EVENT_WORK);
1401     }
1402 #endif
1403     return 0;
1404 }
1405
1406 /*
1407  * Encode response, and transfer the request structure to the outgoing queue.
1408  */
1409 static int process_z_response(association *assoc, request *req, Z_APDU *res)
1410 {
1411     Z_GDU *gres = (Z_GDU *) odr_malloc(assoc->encode, sizeof(*res));
1412     gres->which = Z_GDU_Z3950;
1413     gres->u.z3950 = res;
1414
1415     return process_gdu_response(assoc, req, gres);
1416 }
1417
1418
1419 /*
1420  * Handle init request.
1421  * At the moment, we don't check the options
1422  * anywhere else in the code - we just try not to do anything that would
1423  * break a naive client. We'll toss 'em into the association block when
1424  * we need them there.
1425  */
1426 static Z_APDU *process_initRequest(association *assoc, request *reqb)
1427 {
1428     statserv_options_block *cb = statserv_getcontrol();
1429     Z_InitRequest *req = reqb->apdu_request->u.initRequest;
1430     Z_APDU *apdu = zget_APDU(assoc->encode, Z_APDU_initResponse);
1431     Z_InitResponse *resp = apdu->u.initResponse;
1432     bend_initresult *binitres;
1433
1434     char options[140];
1435
1436     yaz_log(LOG_LOG, "Got initRequest");
1437     if (req->implementationId)
1438         yaz_log(LOG_LOG, "Id:        %s", req->implementationId);
1439     if (req->implementationName)
1440         yaz_log(LOG_LOG, "Name:      %s", req->implementationName);
1441     if (req->implementationVersion)
1442         yaz_log(LOG_LOG, "Version:   %s", req->implementationVersion);
1443
1444     assoc_init_reset(assoc);
1445
1446     assoc->init->auth = req->idAuthentication;
1447     assoc->init->referenceId = req->referenceId;
1448
1449     if (ODR_MASK_GET(req->options, Z_Options_negotiationModel))
1450     {
1451         Z_CharSetandLanguageNegotiation *negotiation =
1452             yaz_get_charneg_record (req->otherInfo);
1453         if (negotiation->which == Z_CharSetandLanguageNegotiation_proposal)
1454             assoc->init->charneg_request = negotiation;
1455     }
1456     
1457     if (!(binitres = (*cb->bend_init)(assoc->init)))
1458     {
1459         yaz_log(LOG_WARN, "Bad response from backend.");
1460         return 0;
1461     }
1462
1463     assoc->backend = binitres->handle;
1464     if ((assoc->init->bend_sort))
1465         yaz_log (LOG_DEBUG, "Sort handler installed");
1466     if ((assoc->init->bend_search))
1467         yaz_log (LOG_DEBUG, "Search handler installed");
1468     if ((assoc->init->bend_present))
1469         yaz_log (LOG_DEBUG, "Present handler installed");   
1470     if ((assoc->init->bend_esrequest))
1471         yaz_log (LOG_DEBUG, "ESRequest handler installed");   
1472     if ((assoc->init->bend_delete))
1473         yaz_log (LOG_DEBUG, "Delete handler installed");   
1474     if ((assoc->init->bend_scan))
1475         yaz_log (LOG_DEBUG, "Scan handler installed");   
1476     if ((assoc->init->bend_segment))
1477         yaz_log (LOG_DEBUG, "Segment handler installed");   
1478     
1479     resp->referenceId = req->referenceId;
1480     *options = '\0';
1481     /* let's tell the client what we can do */
1482     if (ODR_MASK_GET(req->options, Z_Options_search))
1483     {
1484         ODR_MASK_SET(resp->options, Z_Options_search);
1485         strcat(options, "srch");
1486     }
1487     if (ODR_MASK_GET(req->options, Z_Options_present))
1488     {
1489         ODR_MASK_SET(resp->options, Z_Options_present);
1490         strcat(options, " prst");
1491     }
1492     if (ODR_MASK_GET(req->options, Z_Options_delSet) &&
1493         assoc->init->bend_delete)
1494     {
1495         ODR_MASK_SET(resp->options, Z_Options_delSet);
1496         strcat(options, " del");
1497     }
1498     if (ODR_MASK_GET(req->options, Z_Options_extendedServices) &&
1499         assoc->init->bend_esrequest)
1500     {
1501         ODR_MASK_SET(resp->options, Z_Options_extendedServices);
1502         strcat (options, " extendedServices");
1503     }
1504     if (ODR_MASK_GET(req->options, Z_Options_namedResultSets))
1505     {
1506         ODR_MASK_SET(resp->options, Z_Options_namedResultSets);
1507         strcat(options, " namedresults");
1508     }
1509     if (ODR_MASK_GET(req->options, Z_Options_scan) && assoc->init->bend_scan)
1510     {
1511         ODR_MASK_SET(resp->options, Z_Options_scan);
1512         strcat(options, " scan");
1513     }
1514     if (ODR_MASK_GET(req->options, Z_Options_concurrentOperations))
1515     {
1516         ODR_MASK_SET(resp->options, Z_Options_concurrentOperations);
1517         strcat(options, " concurrop");
1518     }
1519     if (ODR_MASK_GET(req->options, Z_Options_sort) && assoc->init->bend_sort)
1520     {
1521         ODR_MASK_SET(resp->options, Z_Options_sort);
1522         strcat(options, " sort");
1523     }
1524
1525     if (ODR_MASK_GET(req->options, Z_Options_negotiationModel)
1526         && assoc->init->charneg_response)
1527     {
1528         Z_OtherInformation **p;
1529         Z_OtherInformationUnit *p0;
1530         
1531         yaz_oi_APDU(apdu, &p);
1532         
1533         if ((p0=yaz_oi_update(p, assoc->encode, NULL, 0, 0))) {
1534             ODR_MASK_SET(resp->options, Z_Options_negotiationModel);
1535             
1536             p0->which = Z_OtherInfo_externallyDefinedInfo;
1537             p0->information.externallyDefinedInfo =
1538                 assoc->init->charneg_response;
1539         }
1540         ODR_MASK_SET(resp->options, Z_Options_negotiationModel);
1541         strcat(options, " negotiation");
1542     }
1543
1544     if (ODR_MASK_GET(req->protocolVersion, Z_ProtocolVersion_1))
1545     {
1546         ODR_MASK_SET(resp->protocolVersion, Z_ProtocolVersion_1);
1547         assoc->version = 2; /* 1 & 2 are equivalent */
1548     }
1549     if (ODR_MASK_GET(req->protocolVersion, Z_ProtocolVersion_2))
1550     {
1551         ODR_MASK_SET(resp->protocolVersion, Z_ProtocolVersion_2);
1552         assoc->version = 2;
1553     }
1554     if (ODR_MASK_GET(req->protocolVersion, Z_ProtocolVersion_3))
1555     {
1556         ODR_MASK_SET(resp->protocolVersion, Z_ProtocolVersion_3);
1557         assoc->version = 3;
1558     }
1559
1560     yaz_log(LOG_LOG, "Negotiated to v%d: %s", assoc->version, options);
1561     assoc->maximumRecordSize = *req->maximumRecordSize;
1562     if (assoc->maximumRecordSize > control_block->maxrecordsize)
1563         assoc->maximumRecordSize = control_block->maxrecordsize;
1564     assoc->preferredMessageSize = *req->preferredMessageSize;
1565     if (assoc->preferredMessageSize > assoc->maximumRecordSize)
1566         assoc->preferredMessageSize = assoc->maximumRecordSize;
1567
1568     resp->preferredMessageSize = &assoc->preferredMessageSize;
1569     resp->maximumRecordSize = &assoc->maximumRecordSize;
1570
1571     resp->implementationName = "GFS/YAZ";
1572
1573     if (assoc->init->implementation_id)
1574     {
1575         char *nv = (char *)
1576             odr_malloc (assoc->encode,
1577                         strlen(assoc->init->implementation_id) + 10 + 
1578                                strlen(resp->implementationId));
1579         sprintf (nv, "%s / %s",
1580                  resp->implementationId, assoc->init->implementation_id);
1581         resp->implementationId = nv;
1582     }
1583     if (assoc->init->implementation_name)
1584     {
1585         char *nv = (char *)
1586             odr_malloc (assoc->encode,
1587                         strlen(assoc->init->implementation_name) + 10 + 
1588                                strlen(resp->implementationName));
1589         sprintf (nv, "%s / %s",
1590                  resp->implementationName, assoc->init->implementation_name);
1591         resp->implementationName = nv;
1592     }
1593     if (assoc->init->implementation_version)
1594     {
1595         char *nv = (char *)
1596             odr_malloc (assoc->encode,
1597                         strlen(assoc->init->implementation_version) + 10 + 
1598                                strlen(resp->implementationVersion));
1599         sprintf (nv, "YAZ %s / %s",
1600                  resp->implementationVersion,
1601                  assoc->init->implementation_version);
1602         resp->implementationVersion = nv;
1603     }
1604
1605     if (binitres->errcode)
1606     {
1607         yaz_log(LOG_LOG, "Connection rejected by backend.");
1608         *resp->result = 0;
1609         assoc->state = ASSOC_DEAD;
1610     }
1611     else
1612         assoc->state = ASSOC_UP;
1613     return apdu;
1614 }
1615
1616 /*
1617  * These functions should be merged.
1618  */
1619
1620 static void set_addinfo (Z_DefaultDiagFormat *dr, char *addinfo, ODR odr)
1621 {
1622     dr->which = Z_DefaultDiagFormat_v2Addinfo;
1623     dr->u.v2Addinfo = odr_strdup (odr, addinfo ? addinfo : "");
1624 }
1625
1626 /*
1627  * nonsurrogate diagnostic record.
1628  */
1629 static Z_Records *diagrec(association *assoc, int error, char *addinfo)
1630 {
1631     Z_Records *rec = (Z_Records *)
1632         odr_malloc (assoc->encode, sizeof(*rec));
1633     int *err = odr_intdup(assoc->encode, error);
1634     Z_DiagRec *drec = (Z_DiagRec *)
1635         odr_malloc (assoc->encode, sizeof(*drec));
1636     Z_DefaultDiagFormat *dr = (Z_DefaultDiagFormat *)
1637         odr_malloc (assoc->encode, sizeof(*dr));
1638
1639     yaz_log(LOG_LOG, "[%d] %s %s%s", error, diagbib1_str(error),
1640         addinfo ? " -- " : "", addinfo ? addinfo : "");
1641     rec->which = Z_Records_NSD;
1642     rec->u.nonSurrogateDiagnostic = dr;
1643     dr->diagnosticSetId =
1644         yaz_oidval_to_z3950oid (assoc->encode, CLASS_DIAGSET, VAL_BIB1);
1645     dr->condition = err;
1646     set_addinfo (dr, addinfo, assoc->encode);
1647     return rec;
1648 }
1649
1650 /*
1651  * surrogate diagnostic.
1652  */
1653 static Z_NamePlusRecord *surrogatediagrec(association *assoc, char *dbname,
1654                                           int error, char *addinfo)
1655 {
1656     Z_NamePlusRecord *rec = (Z_NamePlusRecord *)
1657         odr_malloc (assoc->encode, sizeof(*rec));
1658     int *err = odr_intdup(assoc->encode, error);
1659     Z_DiagRec *drec = (Z_DiagRec *)odr_malloc (assoc->encode, sizeof(*drec));
1660     Z_DefaultDiagFormat *dr = (Z_DefaultDiagFormat *)
1661         odr_malloc (assoc->encode, sizeof(*dr));
1662     
1663     yaz_log(LOG_DEBUG, "SurrogateDiagnotic: %d -- %s", error, addinfo);
1664     rec->databaseName = dbname;
1665     rec->which = Z_NamePlusRecord_surrogateDiagnostic;
1666     rec->u.surrogateDiagnostic = drec;
1667     drec->which = Z_DiagRec_defaultFormat;
1668     drec->u.defaultFormat = dr;
1669     dr->diagnosticSetId =
1670         yaz_oidval_to_z3950oid (assoc->encode, CLASS_DIAGSET, VAL_BIB1);
1671     dr->condition = err;
1672     set_addinfo (dr, addinfo, assoc->encode);
1673
1674     return rec;
1675 }
1676
1677 /*
1678  * multiple nonsurrogate diagnostics.
1679  */
1680 static Z_DiagRecs *diagrecs(association *assoc, int error, char *addinfo)
1681 {
1682     Z_DiagRecs *recs = (Z_DiagRecs *)odr_malloc (assoc->encode, sizeof(*recs));
1683     int *err = odr_intdup(assoc->encode, error);
1684     Z_DiagRec **recp = (Z_DiagRec **)odr_malloc (assoc->encode, sizeof(*recp));
1685     Z_DiagRec *drec = (Z_DiagRec *)odr_malloc (assoc->encode, sizeof(*drec));
1686     Z_DefaultDiagFormat *rec = (Z_DefaultDiagFormat *)
1687         odr_malloc (assoc->encode, sizeof(*rec));
1688
1689     yaz_log(LOG_DEBUG, "DiagRecs: %d -- %s", error, addinfo ? addinfo : "");
1690
1691     recs->num_diagRecs = 1;
1692     recs->diagRecs = recp;
1693     recp[0] = drec;
1694     drec->which = Z_DiagRec_defaultFormat;
1695     drec->u.defaultFormat = rec;
1696
1697     rec->diagnosticSetId =
1698         yaz_oidval_to_z3950oid (assoc->encode, CLASS_DIAGSET, VAL_BIB1);
1699     rec->condition = err;
1700
1701     rec->which = Z_DefaultDiagFormat_v2Addinfo;
1702     rec->u.v2Addinfo = odr_strdup (assoc->encode, addinfo ? addinfo : "");
1703     return recs;
1704 }
1705
1706 static Z_Records *pack_records(association *a, char *setname, int start,
1707                                int *num, Z_RecordComposition *comp,
1708                                int *next, int *pres, oid_value format,
1709                                Z_ReferenceId *referenceId,
1710                                int *oid)
1711 {
1712     int recno, total_length = 0, toget = *num, dumped_records = 0;
1713     Z_Records *records =
1714         (Z_Records *) odr_malloc (a->encode, sizeof(*records));
1715     Z_NamePlusRecordList *reclist =
1716         (Z_NamePlusRecordList *) odr_malloc (a->encode, sizeof(*reclist));
1717     Z_NamePlusRecord **list =
1718         (Z_NamePlusRecord **) odr_malloc (a->encode, sizeof(*list) * toget);
1719
1720     records->which = Z_Records_DBOSD;
1721     records->u.databaseOrSurDiagnostics = reclist;
1722     reclist->num_records = 0;
1723     reclist->records = list;
1724     *pres = Z_PRES_SUCCESS;
1725     *num = 0;
1726     *next = 0;
1727
1728     yaz_log(LOG_LOG, "Request to pack %d+%d+%s", start, toget, setname);
1729     yaz_log(LOG_DEBUG, "pms=%d, mrs=%d", a->preferredMessageSize,
1730         a->maximumRecordSize);
1731     for (recno = start; reclist->num_records < toget; recno++)
1732     {
1733         bend_fetch_rr freq;
1734         Z_NamePlusRecord *thisrec;
1735         int this_length = 0;
1736         /*
1737          * we get the number of bytes allocated on the stream before any
1738          * allocation done by the backend - this should give us a reasonable
1739          * idea of the total size of the data so far.
1740          */
1741         total_length = odr_total(a->encode) - dumped_records;
1742         freq.errcode = 0;
1743         freq.errstring = 0;
1744         freq.basename = 0;
1745         freq.len = 0;
1746         freq.record = 0;
1747         freq.last_in_set = 0;
1748         freq.setname = setname;
1749         freq.surrogate_flag = 0;
1750         freq.number = recno;
1751         freq.comp = comp;
1752         freq.request_format = format;
1753         freq.request_format_raw = oid;
1754         freq.output_format = format;
1755         freq.output_format_raw = 0;
1756         freq.stream = a->encode;
1757         freq.print = a->print;
1758         freq.referenceId = referenceId;
1759         freq.schema = 0;
1760         (*a->init->bend_fetch)(a->backend, &freq);
1761         /* backend should be able to signal whether error is system-wide
1762            or only pertaining to current record */
1763         if (freq.errcode)
1764         {
1765             if (!freq.surrogate_flag)
1766             {
1767                 char s[20];
1768                 *pres = Z_PRES_FAILURE;
1769                 /* for 'present request out of range',
1770                    set addinfo to record position if not set */
1771                 if (freq.errcode == 13 && freq.errstring == 0)
1772                 {
1773                     sprintf (s, "%d", recno);
1774                     freq.errstring = s;
1775                 }
1776                 return diagrec(a, freq.errcode, freq.errstring);
1777             }
1778             reclist->records[reclist->num_records] =
1779                 surrogatediagrec(a, freq.basename, freq.errcode,
1780                                  freq.errstring);
1781             reclist->num_records++;
1782             *next = freq.last_in_set ? 0 : recno + 1;
1783             continue;
1784         }
1785         if (freq.len >= 0)
1786             this_length = freq.len;
1787         else
1788             this_length = odr_total(a->encode) - total_length;
1789         yaz_log(LOG_DEBUG, "  fetched record, len=%d, total=%d",
1790             this_length, total_length);
1791         if (this_length + total_length > a->preferredMessageSize)
1792         {
1793             /* record is small enough, really */
1794             if (this_length <= a->preferredMessageSize)
1795             {
1796                 yaz_log(LOG_DEBUG, "  Dropped last normal-sized record");
1797                 *pres = Z_PRES_PARTIAL_2;
1798                 break;
1799             }
1800             /* record can only be fetched by itself */
1801             if (this_length < a->maximumRecordSize)
1802             {
1803                 yaz_log(LOG_DEBUG, "  Record > prefmsgsz");
1804                 if (toget > 1)
1805                 {
1806                     yaz_log(LOG_DEBUG, "  Dropped it");
1807                     reclist->records[reclist->num_records] =
1808                          surrogatediagrec(a, freq.basename, 16, 0);
1809                     reclist->num_records++;
1810                     *next = freq.last_in_set ? 0 : recno + 1;
1811                     dumped_records += this_length;
1812                     continue;
1813                 }
1814             }
1815             else /* too big entirely */
1816             {
1817                 yaz_log(LOG_LOG, "Record > maxrcdsz this=%d max=%d", this_length, a->maximumRecordSize);
1818                 reclist->records[reclist->num_records] =
1819                     surrogatediagrec(a, freq.basename, 17, 0);
1820                 reclist->num_records++;
1821                 *next = freq.last_in_set ? 0 : recno + 1;
1822                 dumped_records += this_length;
1823                 continue;
1824             }
1825         }
1826
1827         if (!(thisrec = (Z_NamePlusRecord *)
1828               odr_malloc(a->encode, sizeof(*thisrec))))
1829             return 0;
1830         if (!(thisrec->databaseName = (char *)odr_malloc(a->encode,
1831             strlen(freq.basename) + 1)))
1832             return 0;
1833         strcpy(thisrec->databaseName, freq.basename);
1834         thisrec->which = Z_NamePlusRecord_databaseRecord;
1835
1836         if (freq.output_format_raw)
1837         {
1838             struct oident *ident = oid_getentbyoid(freq.output_format_raw);
1839             freq.output_format = ident->value;
1840         }
1841         thisrec->u.databaseRecord = z_ext_record(a->encode, freq.output_format,
1842                                                  freq.record, freq.len);
1843         if (!thisrec->u.databaseRecord)
1844             return 0;
1845         reclist->records[reclist->num_records] = thisrec;
1846         reclist->num_records++;
1847         *next = freq.last_in_set ? 0 : recno + 1;
1848     }
1849     *num = reclist->num_records;
1850     return records;
1851 }
1852
1853 static Z_APDU *process_searchRequest(association *assoc, request *reqb,
1854     int *fd)
1855 {
1856     Z_SearchRequest *req = reqb->apdu_request->u.searchRequest;
1857     bend_search_rr *bsrr = 
1858         (bend_search_rr *)nmem_malloc (reqb->request_mem, sizeof(*bsrr));
1859     
1860     yaz_log(LOG_LOG, "Got SearchRequest.");
1861     bsrr->fd = fd;
1862     bsrr->request = reqb;
1863     bsrr->association = assoc;
1864     bsrr->referenceId = req->referenceId;
1865     save_referenceId (reqb, bsrr->referenceId);
1866
1867     yaz_log (LOG_LOG, "ResultSet '%s'", req->resultSetName);
1868     if (req->databaseNames)
1869     {
1870         int i;
1871         for (i = 0; i < req->num_databaseNames; i++)
1872             yaz_log (LOG_LOG, "Database '%s'", req->databaseNames[i]);
1873     }
1874     yaz_log_zquery(req->query);
1875
1876     if (assoc->init->bend_search)
1877     {
1878         bsrr->setname = req->resultSetName;
1879         bsrr->replace_set = *req->replaceIndicator;
1880         bsrr->num_bases = req->num_databaseNames;
1881         bsrr->basenames = req->databaseNames;
1882         bsrr->query = req->query;
1883         bsrr->stream = assoc->encode;
1884         bsrr->decode = assoc->decode;
1885         bsrr->print = assoc->print;
1886         bsrr->errcode = 0;
1887         bsrr->hits = 0;
1888         bsrr->errstring = NULL;
1889         bsrr->search_info = NULL;
1890         (assoc->init->bend_search)(assoc->backend, bsrr);
1891         if (!bsrr->request)
1892             return 0;
1893     }
1894     return response_searchRequest(assoc, reqb, bsrr, fd);
1895 }
1896
1897 int bend_searchresponse(void *handle, bend_search_rr *bsrr) {return 0;}
1898
1899 /*
1900  * Prepare a searchresponse based on the backend results. We probably want
1901  * to look at making the fetching of records nonblocking as well, but
1902  * so far, we'll keep things simple.
1903  * If bsrt is null, that means we're called in response to a communications
1904  * event, and we'll have to get the response for ourselves.
1905  */
1906 static Z_APDU *response_searchRequest(association *assoc, request *reqb,
1907     bend_search_rr *bsrt, int *fd)
1908 {
1909     Z_SearchRequest *req = reqb->apdu_request->u.searchRequest;
1910     Z_APDU *apdu = (Z_APDU *)odr_malloc (assoc->encode, sizeof(*apdu));
1911     Z_SearchResponse *resp = (Z_SearchResponse *)
1912         odr_malloc (assoc->encode, sizeof(*resp));
1913     int *nulint = odr_intdup (assoc->encode, 0);
1914     bool_t *sr = odr_intdup(assoc->encode, 1);
1915     int *next = odr_intdup(assoc->encode, 0);
1916     int *none = odr_intdup(assoc->encode, Z_RES_NONE);
1917
1918     apdu->which = Z_APDU_searchResponse;
1919     apdu->u.searchResponse = resp;
1920     resp->referenceId = req->referenceId;
1921     resp->additionalSearchInfo = 0;
1922     resp->otherInfo = 0;
1923     *fd = -1;
1924     if (!bsrt && !bend_searchresponse(assoc->backend, bsrt))
1925     {
1926         yaz_log(LOG_FATAL, "Bad result from backend");
1927         return 0;
1928     }
1929     else if (bsrt->errcode)
1930     {
1931         resp->records = diagrec(assoc, bsrt->errcode, bsrt->errstring);
1932         resp->resultCount = nulint;
1933         resp->numberOfRecordsReturned = nulint;
1934         resp->nextResultSetPosition = nulint;
1935         resp->searchStatus = nulint;
1936         resp->resultSetStatus = none;
1937         resp->presentStatus = 0;
1938     }
1939     else
1940     {
1941         int *toget = odr_intdup(assoc->encode, 0);
1942         int *presst = odr_intdup(assoc->encode, 0);
1943         Z_RecordComposition comp, *compp = 0;
1944
1945         yaz_log (LOG_LOG, "resultCount: %d", bsrt->hits);
1946
1947         resp->records = 0;
1948         resp->resultCount = &bsrt->hits;
1949
1950         comp.which = Z_RecordComp_simple;
1951         /* how many records does the user agent want, then? */
1952         if (bsrt->hits <= *req->smallSetUpperBound)
1953         {
1954             *toget = bsrt->hits;
1955             if ((comp.u.simple = req->smallSetElementSetNames))
1956                 compp = &comp;
1957         }
1958         else if (bsrt->hits < *req->largeSetLowerBound)
1959         {
1960             *toget = *req->mediumSetPresentNumber;
1961             if (*toget > bsrt->hits)
1962                 *toget = bsrt->hits;
1963             if ((comp.u.simple = req->mediumSetElementSetNames))
1964                 compp = &comp;
1965         }
1966         else
1967             *toget = 0;
1968
1969         if (*toget && !resp->records)
1970         {
1971             oident *prefformat;
1972             oid_value form;
1973
1974             if (!(prefformat = oid_getentbyoid(req->preferredRecordSyntax)))
1975                 form = VAL_NONE;
1976             else
1977                 form = prefformat->value;
1978             resp->records = pack_records(assoc, req->resultSetName, 1,
1979                 toget, compp, next, presst, form, req->referenceId,
1980                                          req->preferredRecordSyntax);
1981             if (!resp->records)
1982                 return 0;
1983             resp->numberOfRecordsReturned = toget;
1984             resp->nextResultSetPosition = next;
1985             resp->searchStatus = sr;
1986             resp->resultSetStatus = 0;
1987             resp->presentStatus = presst;
1988         }
1989         else
1990         {
1991             if (*resp->resultCount)
1992                 *next = 1;
1993             resp->numberOfRecordsReturned = nulint;
1994             resp->nextResultSetPosition = next;
1995             resp->searchStatus = sr;
1996             resp->resultSetStatus = 0;
1997             resp->presentStatus = 0;
1998         }
1999     }
2000     resp->additionalSearchInfo = bsrt->search_info;
2001     return apdu;
2002 }
2003
2004 /*
2005  * Maybe we got a little over-friendly when we designed bend_fetch to
2006  * get only one record at a time. Some backends can optimise multiple-record
2007  * fetches, and at any rate, there is some overhead involved in
2008  * all that selecting and hopping around. Problem is, of course, that the
2009  * frontend can't know ahead of time how many records it'll need to
2010  * fill the negotiated PDU size. Annoying. Segmentation or not, Z/SR
2011  * is downright lousy as a bulk data transfer protocol.
2012  *
2013  * To start with, we'll do the fetching of records from the backend
2014  * in one operation: To save some trips in and out of the event-handler,
2015  * and to simplify the interface to pack_records. At any rate, asynch
2016  * operation is more fun in operations that have an unpredictable execution
2017  * speed - which is normally more true for search than for present.
2018  */
2019 static Z_APDU *process_presentRequest(association *assoc, request *reqb,
2020                                       int *fd)
2021 {
2022     Z_PresentRequest *req = reqb->apdu_request->u.presentRequest;
2023     oident *prefformat;
2024     oid_value form;
2025     Z_APDU *apdu;
2026     Z_PresentResponse *resp;
2027     int *next;
2028     int *num;
2029
2030     yaz_log(LOG_LOG, "Got PresentRequest.");
2031
2032     if (!(prefformat = oid_getentbyoid(req->preferredRecordSyntax)))
2033         form = VAL_NONE;
2034     else
2035         form = prefformat->value;
2036     resp = (Z_PresentResponse *)odr_malloc (assoc->encode, sizeof(*resp));
2037     resp->records = 0;
2038     resp->presentStatus = odr_intdup(assoc->encode, 0);
2039     if (assoc->init->bend_present)
2040     {
2041         bend_present_rr *bprr = (bend_present_rr *)
2042             nmem_malloc (reqb->request_mem, sizeof(*bprr));
2043         bprr->setname = req->resultSetId;
2044         bprr->start = *req->resultSetStartPoint;
2045         bprr->number = *req->numberOfRecordsRequested;
2046         bprr->format = form;
2047         bprr->comp = req->recordComposition;
2048         bprr->referenceId = req->referenceId;
2049         bprr->stream = assoc->encode;
2050         bprr->print = assoc->print;
2051         bprr->request = reqb;
2052         bprr->association = assoc;
2053         bprr->errcode = 0;
2054         bprr->errstring = NULL;
2055         (*assoc->init->bend_present)(assoc->backend, bprr);
2056         
2057         if (!bprr->request)
2058             return 0;
2059         if (bprr->errcode)
2060         {
2061             resp->records = diagrec(assoc, bprr->errcode, bprr->errstring);
2062             *resp->presentStatus = Z_PRES_FAILURE;
2063         }
2064     }
2065     apdu = (Z_APDU *)odr_malloc (assoc->encode, sizeof(*apdu));
2066     next = odr_intdup(assoc->encode, 0);
2067     num = odr_intdup(assoc->encode, 0);
2068     
2069     apdu->which = Z_APDU_presentResponse;
2070     apdu->u.presentResponse = resp;
2071     resp->referenceId = req->referenceId;
2072     resp->otherInfo = 0;
2073     
2074     if (!resp->records)
2075     {
2076         *num = *req->numberOfRecordsRequested;
2077         resp->records =
2078             pack_records(assoc, req->resultSetId, *req->resultSetStartPoint,
2079                      num, req->recordComposition, next, resp->presentStatus,
2080                          form, req->referenceId, req->preferredRecordSyntax);
2081     }
2082     if (!resp->records)
2083         return 0;
2084     resp->numberOfRecordsReturned = num;
2085     resp->nextResultSetPosition = next;
2086     
2087     return apdu;
2088 }
2089
2090 /*
2091  * Scan was implemented rather in a hurry, and with support for only the basic
2092  * elements of the service in the backend API. Suggestions are welcome.
2093  */
2094 static Z_APDU *process_scanRequest(association *assoc, request *reqb, int *fd)
2095 {
2096     Z_ScanRequest *req = reqb->apdu_request->u.scanRequest;
2097     Z_APDU *apdu = (Z_APDU *)odr_malloc (assoc->encode, sizeof(*apdu));
2098     Z_ScanResponse *res = (Z_ScanResponse *)
2099         odr_malloc (assoc->encode, sizeof(*res));
2100     int *scanStatus = odr_intdup(assoc->encode, Z_Scan_failure);
2101     int *numberOfEntriesReturned = odr_intdup(assoc->encode, 0);
2102     Z_ListEntries *ents = (Z_ListEntries *)
2103         odr_malloc (assoc->encode, sizeof(*ents));
2104     Z_DiagRecs *diagrecs_p = NULL;
2105     oident *attset;
2106     bend_scan_rr *bsrr = (bend_scan_rr *)
2107         odr_malloc (assoc->encode, sizeof(*bsrr));
2108     struct scan_entry *save_entries;
2109
2110     yaz_log(LOG_LOG, "Got ScanRequest");
2111
2112     apdu->which = Z_APDU_scanResponse;
2113     apdu->u.scanResponse = res;
2114     res->referenceId = req->referenceId;
2115
2116     /* if step is absent, set it to 0 */
2117     res->stepSize = odr_intdup(assoc->encode, 0);
2118     if (req->stepSize)
2119         *res->stepSize = *req->stepSize;
2120
2121     res->scanStatus = scanStatus;
2122     res->numberOfEntriesReturned = numberOfEntriesReturned;
2123     res->positionOfTerm = 0;
2124     res->entries = ents;
2125     ents->num_entries = 0;
2126     ents->entries = NULL;
2127     ents->num_nonsurrogateDiagnostics = 0;
2128     ents->nonsurrogateDiagnostics = NULL;
2129     res->attributeSet = 0;
2130     res->otherInfo = 0;
2131
2132     if (req->databaseNames)
2133     {
2134         int i;
2135         for (i = 0; i < req->num_databaseNames; i++)
2136             yaz_log (LOG_LOG, "Database '%s'", req->databaseNames[i]);
2137     }
2138     bsrr->num_bases = req->num_databaseNames;
2139     bsrr->basenames = req->databaseNames;
2140     bsrr->num_entries = *req->numberOfTermsRequested;
2141     bsrr->term = req->termListAndStartPoint;
2142     bsrr->referenceId = req->referenceId;
2143     bsrr->stream = assoc->encode;
2144     bsrr->print = assoc->print;
2145     bsrr->step_size = res->stepSize;
2146     bsrr->entries = 0;
2147     /* Note that version 2.0 of YAZ and older did not set entries .. 
2148        We do now. And when we do it's easier to extend the scan entry 
2149        We know that if the scan handler did set entries, it will
2150        not know of new member display_term.
2151     */
2152     if (bsrr->num_entries > 0) 
2153     {
2154         int i;
2155         bsrr->entries = odr_malloc(assoc->decode, sizeof(*bsrr->entries) *
2156                                    bsrr->num_entries);
2157         for (i = 0; i<bsrr->num_entries; i++)
2158         {
2159             bsrr->entries[i].term = 0;
2160             bsrr->entries[i].occurrences = 0;
2161             bsrr->entries[i].errcode = 0;
2162             bsrr->entries[i].errstring = 0;
2163             bsrr->entries[i].display_term = 0;
2164         }
2165     }
2166     save_entries = bsrr->entries;  /* save it so we can compare later */
2167
2168     if (req->attributeSet &&
2169         (attset = oid_getentbyoid(req->attributeSet)) &&
2170         (attset->oclass == CLASS_ATTSET || attset->oclass == CLASS_GENERAL))
2171         bsrr->attributeset = attset->value;
2172     else
2173         bsrr->attributeset = VAL_NONE;
2174     log_scan_term (req->termListAndStartPoint, bsrr->attributeset);
2175     bsrr->term_position = req->preferredPositionInResponse ?
2176         *req->preferredPositionInResponse : 1;
2177     ((int (*)(void *, bend_scan_rr *))
2178      (*assoc->init->bend_scan))(assoc->backend, bsrr);
2179     if (bsrr->errcode)
2180         diagrecs_p = diagrecs(assoc, bsrr->errcode, bsrr->errstring);
2181     else
2182     {
2183         int i;
2184         Z_Entry **tab = (Z_Entry **)
2185             odr_malloc (assoc->encode, sizeof(*tab) * bsrr->num_entries);
2186         
2187         if (bsrr->status == BEND_SCAN_PARTIAL)
2188             *scanStatus = Z_Scan_partial_5;
2189         else
2190             *scanStatus = Z_Scan_success;
2191         ents->entries = tab;
2192         ents->num_entries = bsrr->num_entries;
2193         res->numberOfEntriesReturned = &ents->num_entries;          
2194         res->positionOfTerm = &bsrr->term_position;
2195         for (i = 0; i < bsrr->num_entries; i++)
2196         {
2197             Z_Entry *e;
2198             Z_TermInfo *t;
2199             Odr_oct *o;
2200             
2201             tab[i] = e = (Z_Entry *)odr_malloc(assoc->encode, sizeof(*e));
2202             if (bsrr->entries[i].occurrences >= 0)
2203             {
2204                 e->which = Z_Entry_termInfo;
2205                 e->u.termInfo = t = (Z_TermInfo *)
2206                     odr_malloc(assoc->encode, sizeof(*t));
2207                 t->suggestedAttributes = 0;
2208                 t->displayTerm = 0;
2209                 if (save_entries == bsrr->entries && 
2210                     bsrr->entries[i].display_term)
2211                 {
2212                     /* the entries was NOT set by the handler. So it's
2213                        safe to test for new member display_term. It is
2214                        NULL'ed by us.
2215                     */
2216                     t->displayTerm = odr_strdup(assoc->encode,
2217                                                 bsrr->entries[i].display_term);
2218                 }
2219                 t->alternativeTerm = 0;
2220                 t->byAttributes = 0;
2221                 t->otherTermInfo = 0;
2222                 t->globalOccurrences = &bsrr->entries[i].occurrences;
2223                 t->term = (Z_Term *)
2224                     odr_malloc(assoc->encode, sizeof(*t->term));
2225                 t->term->which = Z_Term_general;
2226                 t->term->u.general = o =
2227                     (Odr_oct *)odr_malloc(assoc->encode, sizeof(Odr_oct));
2228                 o->buf = (unsigned char *)
2229                     odr_malloc(assoc->encode, o->len = o->size =
2230                                strlen(bsrr->entries[i].term));
2231                 memcpy(o->buf, bsrr->entries[i].term, o->len);
2232                 yaz_log(LOG_DEBUG, "  term #%d: '%s' (%d)", i,
2233                          bsrr->entries[i].term, bsrr->entries[i].occurrences);
2234             }
2235             else
2236             {
2237                 Z_DiagRecs *drecs = diagrecs (assoc,
2238                                               bsrr->entries[i].errcode,
2239                                               bsrr->entries[i].errstring);
2240                 assert (drecs->num_diagRecs == 1);
2241                 e->which = Z_Entry_surrogateDiagnostic;
2242                 assert (drecs->diagRecs[0]);
2243                 e->u.surrogateDiagnostic = drecs->diagRecs[0];
2244             }
2245         }
2246     }
2247     if (diagrecs_p)
2248     {
2249         ents->num_nonsurrogateDiagnostics = diagrecs_p->num_diagRecs;
2250         ents->nonsurrogateDiagnostics = diagrecs_p->diagRecs;
2251     }
2252     return apdu;
2253 }
2254
2255 static Z_APDU *process_sortRequest(association *assoc, request *reqb,
2256     int *fd)
2257 {
2258     Z_SortRequest *req = reqb->apdu_request->u.sortRequest;
2259     Z_SortResponse *res = (Z_SortResponse *)
2260         odr_malloc (assoc->encode, sizeof(*res));
2261     bend_sort_rr *bsrr = (bend_sort_rr *)
2262         odr_malloc (assoc->encode, sizeof(*bsrr));
2263
2264     Z_APDU *apdu = (Z_APDU *)odr_malloc (assoc->encode, sizeof(*apdu));
2265
2266     yaz_log(LOG_LOG, "Got SortRequest.");
2267
2268     bsrr->num_input_setnames = req->num_inputResultSetNames;
2269     bsrr->input_setnames = req->inputResultSetNames;
2270     bsrr->referenceId = req->referenceId;
2271     bsrr->output_setname = req->sortedResultSetName;
2272     bsrr->sort_sequence = req->sortSequence;
2273     bsrr->stream = assoc->encode;
2274     bsrr->print = assoc->print;
2275
2276     bsrr->sort_status = Z_SortStatus_failure;
2277     bsrr->errcode = 0;
2278     bsrr->errstring = 0;
2279     
2280     (*assoc->init->bend_sort)(assoc->backend, bsrr);
2281     
2282     res->referenceId = bsrr->referenceId;
2283     res->sortStatus = odr_intdup(assoc->encode, bsrr->sort_status);
2284     res->resultSetStatus = 0;
2285     if (bsrr->errcode)
2286     {
2287         Z_DiagRecs *dr = diagrecs (assoc, bsrr->errcode, bsrr->errstring);
2288         res->diagnostics = dr->diagRecs;
2289         res->num_diagnostics = dr->num_diagRecs;
2290     }
2291     else
2292     {
2293         res->num_diagnostics = 0;
2294         res->diagnostics = 0;
2295     }
2296     res->otherInfo = 0;
2297
2298     apdu->which = Z_APDU_sortResponse;
2299     apdu->u.sortResponse = res;
2300     return apdu;
2301 }
2302
2303 static Z_APDU *process_deleteRequest(association *assoc, request *reqb,
2304     int *fd)
2305 {
2306     Z_DeleteResultSetRequest *req =
2307         reqb->apdu_request->u.deleteResultSetRequest;
2308     Z_DeleteResultSetResponse *res = (Z_DeleteResultSetResponse *)
2309         odr_malloc (assoc->encode, sizeof(*res));
2310     bend_delete_rr *bdrr = (bend_delete_rr *)
2311         odr_malloc (assoc->encode, sizeof(*bdrr));
2312     Z_APDU *apdu = (Z_APDU *)odr_malloc (assoc->encode, sizeof(*apdu));
2313
2314     yaz_log(LOG_LOG, "Got DeleteRequest.");
2315
2316     bdrr->num_setnames = req->num_resultSetList;
2317     bdrr->setnames = req->resultSetList;
2318     bdrr->stream = assoc->encode;
2319     bdrr->print = assoc->print;
2320     bdrr->function = *req->deleteFunction;
2321     bdrr->referenceId = req->referenceId;
2322     bdrr->statuses = 0;
2323     if (bdrr->num_setnames > 0)
2324     {
2325         int i;
2326         bdrr->statuses = (int*) 
2327             odr_malloc(assoc->encode, sizeof(*bdrr->statuses) *
2328                        bdrr->num_setnames);
2329         for (i = 0; i < bdrr->num_setnames; i++)
2330             bdrr->statuses[i] = 0;
2331     }
2332     (*assoc->init->bend_delete)(assoc->backend, bdrr);
2333     
2334     res->referenceId = req->referenceId;
2335
2336     res->deleteOperationStatus = odr_intdup(assoc->encode,bdrr->delete_status);
2337
2338     res->deleteListStatuses = 0;
2339     if (bdrr->num_setnames > 0)
2340     {
2341         int i;
2342         res->deleteListStatuses = (Z_ListStatuses *)
2343             odr_malloc(assoc->encode, sizeof(*res->deleteListStatuses));
2344         res->deleteListStatuses->num = bdrr->num_setnames;
2345         res->deleteListStatuses->elements =
2346             (Z_ListStatus **)
2347             odr_malloc (assoc->encode, 
2348                         sizeof(*res->deleteListStatuses->elements) *
2349                         bdrr->num_setnames);
2350         for (i = 0; i<bdrr->num_setnames; i++)
2351         {
2352             res->deleteListStatuses->elements[i] =
2353                 (Z_ListStatus *)
2354                 odr_malloc (assoc->encode,
2355                             sizeof(**res->deleteListStatuses->elements));
2356             res->deleteListStatuses->elements[i]->status = bdrr->statuses+i;
2357             res->deleteListStatuses->elements[i]->id =
2358                 odr_strdup (assoc->encode, bdrr->setnames[i]);
2359             
2360         }
2361     }
2362     res->numberNotDeleted = 0;
2363     res->bulkStatuses = 0;
2364     res->deleteMessage = 0;
2365     res->otherInfo = 0;
2366
2367     apdu->which = Z_APDU_deleteResultSetResponse;
2368     apdu->u.deleteResultSetResponse = res;
2369     return apdu;
2370 }
2371
2372 static void process_close(association *assoc, request *reqb)
2373 {
2374     Z_Close *req = reqb->apdu_request->u.close;
2375     static char *reasons[] =
2376     {
2377         "finished",
2378         "shutdown",
2379         "systemProblem",
2380         "costLimit",
2381         "resources",
2382         "securityViolation",
2383         "protocolError",
2384         "lackOfActivity",
2385         "peerAbort",
2386         "unspecified"
2387     };
2388
2389     yaz_log(LOG_LOG, "Got Close, reason %s, message %s",
2390         reasons[*req->closeReason], req->diagnosticInformation ?
2391         req->diagnosticInformation : "NULL");
2392     if (assoc->version < 3) /* to make do_force respond with close */
2393         assoc->version = 3;
2394     do_close_req(assoc, Z_Close_finished,
2395                  "Association terminated by client", reqb);
2396 }
2397
2398 void save_referenceId (request *reqb, Z_ReferenceId *refid)
2399 {
2400     if (refid)
2401     {
2402         reqb->len_refid = refid->len;
2403         reqb->refid = (char *)nmem_malloc (reqb->request_mem, refid->len);
2404         memcpy (reqb->refid, refid->buf, refid->len);
2405     }
2406     else
2407     {
2408         reqb->len_refid = 0;
2409         reqb->refid = NULL;
2410     }
2411 }
2412
2413 void bend_request_send (bend_association a, bend_request req, Z_APDU *res)
2414 {
2415     process_z_response (a, req, res);
2416 }
2417
2418 bend_request bend_request_mk (bend_association a)
2419 {
2420     request *nreq = request_get (&a->outgoing);
2421     nreq->request_mem = nmem_create ();
2422     return nreq;
2423 }
2424
2425 Z_ReferenceId *bend_request_getid (ODR odr, bend_request req)
2426 {
2427     Z_ReferenceId *id;
2428     if (!req->refid)
2429         return 0;
2430     id = (Odr_oct *)odr_malloc (odr, sizeof(*odr));
2431     id->buf = (unsigned char *)odr_malloc (odr, req->len_refid);
2432     id->len = id->size = req->len_refid;
2433     memcpy (id->buf, req->refid, req->len_refid);
2434     return id;
2435 }
2436
2437 void bend_request_destroy (bend_request *req)
2438 {
2439     nmem_destroy((*req)->request_mem);
2440     request_release(*req);
2441     *req = NULL;
2442 }
2443
2444 int bend_backend_respond (bend_association a, bend_request req)
2445 {
2446     char *msg;
2447     int r;
2448     r = process_z_request (a, req, &msg);
2449     if (r < 0)
2450         yaz_log (LOG_WARN, "%s", msg);
2451     return r;
2452 }
2453
2454 void bend_request_setdata(bend_request r, void *p)
2455 {
2456     r->clientData = p;
2457 }
2458
2459 void *bend_request_getdata(bend_request r)
2460 {
2461     return r->clientData;
2462 }
2463
2464 static Z_APDU *process_segmentRequest (association *assoc, request *reqb)
2465 {
2466     bend_segment_rr req;
2467
2468     req.segment = reqb->apdu_request->u.segmentRequest;
2469     req.stream = assoc->encode;
2470     req.decode = assoc->decode;
2471     req.print = assoc->print;
2472     req.association = assoc;
2473     
2474     (*assoc->init->bend_segment)(assoc->backend, &req);
2475
2476     return 0;
2477 }
2478
2479 static Z_APDU *process_ESRequest(association *assoc, request *reqb, int *fd)
2480 {
2481     bend_esrequest_rr esrequest;
2482
2483     Z_ExtendedServicesRequest *req =
2484         reqb->apdu_request->u.extendedServicesRequest;
2485     Z_APDU *apdu = zget_APDU(assoc->encode, Z_APDU_extendedServicesResponse);
2486
2487     Z_ExtendedServicesResponse *resp = apdu->u.extendedServicesResponse;
2488
2489     yaz_log(LOG_DEBUG,"inside Process esRequest");
2490
2491     esrequest.esr = reqb->apdu_request->u.extendedServicesRequest;
2492     esrequest.stream = assoc->encode;
2493     esrequest.decode = assoc->decode;
2494     esrequest.print = assoc->print;
2495     esrequest.errcode = 0;
2496     esrequest.errstring = NULL;
2497     esrequest.request = reqb;
2498     esrequest.association = assoc;
2499     esrequest.taskPackage = 0;
2500     esrequest.referenceId = req->referenceId;
2501     
2502     (*assoc->init->bend_esrequest)(assoc->backend, &esrequest);
2503     
2504     /* If the response is being delayed, return NULL */
2505     if (esrequest.request == NULL)
2506         return(NULL);
2507
2508     resp->referenceId = req->referenceId;
2509
2510     if (esrequest.errcode == -1)
2511     {
2512         /* Backend service indicates request will be processed */
2513         yaz_log(LOG_DEBUG,"Request could be processed...Accepted !");
2514         *resp->operationStatus = Z_ExtendedServicesResponse_accepted;
2515     }
2516     else if (esrequest.errcode == 0)
2517     {
2518         /* Backend service indicates request will be processed */
2519         yaz_log(LOG_DEBUG,"Request could be processed...Done !");
2520         *resp->operationStatus = Z_ExtendedServicesResponse_done;
2521     }
2522     else
2523     {
2524         Z_DiagRecs *diagRecs = diagrecs (assoc, esrequest.errcode,
2525                                          esrequest.errstring);
2526
2527         /* Backend indicates error, request will not be processed */
2528         yaz_log(LOG_DEBUG,"Request could not be processed...failure !");
2529         *resp->operationStatus = Z_ExtendedServicesResponse_failure;
2530         resp->num_diagnostics = diagRecs->num_diagRecs;
2531         resp->diagnostics = diagRecs->diagRecs;
2532     }
2533     /* Do something with the members of bend_extendedservice */
2534     if (esrequest.taskPackage)
2535         resp->taskPackage = z_ext_record (assoc->encode, VAL_EXTENDED,
2536                                          (const char *)  esrequest.taskPackage,
2537                                           -1);
2538     yaz_log(LOG_DEBUG,"Send the result apdu");
2539     return apdu;
2540 }
2541