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