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