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