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