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