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