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