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