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