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