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