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