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