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