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