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