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