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