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