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