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