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