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