ae494dc4db01f9cfca5d2436cd011a880224e020
[yaz-moved-to-github.git] / src / seshigh.c
1 /*
2  * Copyright (C) 1995-2005, Index Data ApS
3  * See the file LICENSE for details.
4  *
5  * $Id: seshigh.c,v 1.92 2006-07-07 10:31:26 marc 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 <assert.h>
34 #include <ctype.h>
35
36 #if HAVE_SYS_TYPES_H
37 #include <sys/types.h>
38 #endif
39 #if HAVE_SYS_STAT_H
40 #include <sys/stat.h>
41 #endif
42
43 #ifdef WIN32
44 #include <io.h>
45 #define S_ISREG(x) (x & _S_IFREG)
46 #include <process.h>
47 #endif
48
49 #if HAVE_UNISTD_H
50 #include <unistd.h>
51 #endif
52
53 #if YAZ_HAVE_XML2
54 #include <libxml/parser.h>
55 #include <libxml/tree.h>
56 #endif
57
58 #include <yaz/yconfig.h>
59 #include <yaz/xmalloc.h>
60 #include <yaz/comstack.h>
61 #include "eventl.h"
62 #include "session.h"
63 #include "mime.h"
64 #include <yaz/proto.h>
65 #include <yaz/oid.h>
66 #include <yaz/log.h>
67 #include <yaz/logrpn.h>
68 #include <yaz/querytowrbuf.h>
69 #include <yaz/statserv.h>
70 #include <yaz/diagbib1.h>
71 #include <yaz/charneg.h>
72 #include <yaz/otherinfo.h>
73 #include <yaz/yaz-util.h>
74 #include <yaz/pquery.h>
75
76 #include <yaz/srw.h>
77 #include <yaz/backend.h>
78
79 static void process_gdu_request(association *assoc, request *req);
80 static int process_z_request(association *assoc, request *req, char **msg);
81 void backend_response(IOCHAN i, int event);
82 static int process_gdu_response(association *assoc, request *req, Z_GDU *res);
83 static int process_z_response(association *assoc, request *req, Z_APDU *res);
84 static Z_APDU *process_initRequest(association *assoc, request *reqb);
85 static Z_External *init_diagnostics(ODR odr, int errcode,
86                                     const char *errstring);
87 static Z_APDU *process_searchRequest(association *assoc, request *reqb,
88     int *fd);
89 static Z_APDU *response_searchRequest(association *assoc, request *reqb,
90     bend_search_rr *bsrr, int *fd);
91 static Z_APDU *process_presentRequest(association *assoc, request *reqb,
92     int *fd);
93 static Z_APDU *process_scanRequest(association *assoc, request *reqb, int *fd);
94 static Z_APDU *process_sortRequest(association *assoc, request *reqb, int *fd);
95 static void process_close(association *assoc, request *reqb);
96 void save_referenceId (request *reqb, Z_ReferenceId *refid);
97 static Z_APDU *process_deleteRequest(association *assoc, request *reqb,
98     int *fd);
99 static Z_APDU *process_segmentRequest (association *assoc, request *reqb);
100
101 static Z_APDU *process_ESRequest(association *assoc, request *reqb, int *fd);
102
103 /* dynamic logging levels */
104 static int logbits_set = 0;
105 static int log_session = 0; 
106 static int log_request = 0; /* one-line logs for requests */
107 static int log_requestdetail = 0;  /* more detailed stuff */
108
109 /** get_logbits sets global loglevel bits */
110 static void get_logbits()
111 { /* needs to be called after parsing cmd-line args that can set loglevels!*/
112     if (!logbits_set)
113     {
114         logbits_set = 1;
115         log_session = yaz_log_module_level("session"); 
116         log_request = yaz_log_module_level("request");
117         log_requestdetail = yaz_log_module_level("requestdetail"); 
118     }
119 }
120
121
122
123 static void wr_diag(WRBUF w, int error, const char *addinfo)
124 {
125     wrbuf_printf(w, "ERROR %d+", error);
126     wrbuf_puts_replace_char(w, diagbib1_str(error), ' ', '_');
127     if (addinfo){
128         wrbuf_puts(w, "+");
129         wrbuf_puts_replace_char(w, addinfo, ' ', '_');
130     }
131     
132     wrbuf_puts(w, " ");    
133 }
134
135
136 /*
137  * Create and initialize a new association-handle.
138  *  channel  : iochannel for the current line.
139  *  link     : communications channel.
140  * Returns: 0 or a new association handle.
141  */
142 association *create_association(IOCHAN channel, COMSTACK link,
143                                 const char *apdufile)
144 {
145     association *anew;
146
147     if (!logbits_set)
148         get_logbits();
149     if (!(anew = (association *)xmalloc(sizeof(*anew))))
150         return 0;
151     anew->init = 0;
152     anew->version = 0;
153     anew->last_control = 0;
154     anew->client_chan = channel;
155     anew->client_link = link;
156     anew->cs_get_mask = 0;
157     anew->cs_put_mask = 0;
158     anew->cs_accept_mask = 0;
159     if (!(anew->decode = odr_createmem(ODR_DECODE)) ||
160         !(anew->encode = odr_createmem(ODR_ENCODE)))
161         return 0;
162     if (apdufile && *apdufile)
163     {
164         FILE *f;
165
166         if (!(anew->print = odr_createmem(ODR_PRINT)))
167             return 0;
168         if (*apdufile == '@')
169         {
170             odr_setprint(anew->print, yaz_log_file());
171         }       
172         else if (*apdufile != '-')
173         {
174             char filename[256];
175             sprintf(filename, "%.200s.%ld", apdufile, (long)getpid());
176             if (!(f = fopen(filename, "w")))
177             {
178                 yaz_log(YLOG_WARN|YLOG_ERRNO, "%s", filename);
179                 return 0;
180             }
181             setvbuf(f, 0, _IONBF, 0);
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     anew->server = 0;
195     return anew;
196 }
197
198 /*
199  * Free association and release resources.
200  */
201 void destroy_association(association *h)
202 {
203     statserv_options_block *cb = statserv_getcontrol();
204     request *req;
205
206     xfree(h->init);
207     odr_destroy(h->decode);
208     odr_destroy(h->encode);
209     if (h->print)
210         odr_destroy(h->print);
211     if (h->input_buffer)
212     xfree(h->input_buffer);
213     if (h->backend)
214         (*cb->bend_close)(h->backend);
215     while ((req = request_deq(&h->incoming)))
216         request_release(req);
217     while ((req = request_deq(&h->outgoing)))
218         request_release(req);
219     request_delq(&h->incoming);
220     request_delq(&h->outgoing);
221     xfree(h);
222     xmalloc_trav("session closed");
223     if (cb && cb->one_shot)
224     {
225         exit (0);
226     }
227 }
228
229 static void do_close_req(association *a, int reason, char *message,
230                          request *req)
231 {
232     Z_APDU apdu;
233     Z_Close *cls = zget_Close(a->encode);
234     
235     /* Purge request queue */
236     while (request_deq(&a->incoming));
237     while (request_deq(&a->outgoing));
238     if (a->version >= 3)
239     {
240         yaz_log(log_requestdetail, "Sending Close PDU, reason=%d, message=%s",
241             reason, message ? message : "none");
242         apdu.which = Z_APDU_close;
243         apdu.u.close = cls;
244         *cls->closeReason = reason;
245         cls->diagnosticInformation = message;
246         process_z_response(a, req, &apdu);
247         iochan_settimeout(a->client_chan, 20);
248     }
249     else
250     {
251         request_release(req);
252         yaz_log(log_requestdetail, "v2 client. No Close PDU");
253         iochan_setevent(a->client_chan, EVENT_TIMEOUT); /* force imm close */
254         a->cs_put_mask = 0;
255     }
256     a->state = ASSOC_DEAD;
257 }
258
259 static void do_close(association *a, int reason, char *message)
260 {
261     request *req = request_get(&a->outgoing);
262     do_close_req (a, reason, message, req);
263 }
264
265 /*
266  * This is where PDUs from the client are read and the further
267  * processing is initiated. Flow of control moves down through the
268  * various process_* functions below, until the encoded result comes back up
269  * to the output handler in here.
270  * 
271  *  h     : the I/O channel that has an outstanding event.
272  *  event : the current outstanding event.
273  */
274 void ir_session(IOCHAN h, int event)
275 {
276     int res;
277     association *assoc = (association *)iochan_getdata(h);
278     COMSTACK conn = assoc->client_link;
279     request *req;
280
281     assert(h && conn && assoc);
282     if (event == EVENT_TIMEOUT)
283     {
284         if (assoc->state != ASSOC_UP)
285         {
286             yaz_log(YLOG_DEBUG, "Final timeout - closing connection.");
287             /* do we need to lod this at all */
288             cs_close(conn);
289             destroy_association(assoc);
290             iochan_destroy(h);
291         }
292         else
293         {
294             yaz_log(log_session, "Session idle too long. Sending close.");
295             do_close(assoc, Z_Close_lackOfActivity, 0);
296         }
297         return;
298     }
299     if (event & assoc->cs_accept_mask)
300     {
301         if (!cs_accept (conn))
302         {
303             yaz_log (YLOG_WARN, "accept failed");
304             destroy_association(assoc);
305             iochan_destroy(h);
306         }
307         iochan_clearflag (h, EVENT_OUTPUT);
308         if (conn->io_pending) 
309         {   /* cs_accept didn't complete */
310             assoc->cs_accept_mask = 
311                 ((conn->io_pending & CS_WANT_WRITE) ? EVENT_OUTPUT : 0) |
312                 ((conn->io_pending & CS_WANT_READ) ? EVENT_INPUT : 0);
313
314             iochan_setflag (h, assoc->cs_accept_mask);
315         }
316         else
317         {   /* cs_accept completed. Prepare for reading (cs_get) */
318             assoc->cs_accept_mask = 0;
319             assoc->cs_get_mask = EVENT_INPUT;
320             iochan_setflag (h, assoc->cs_get_mask);
321         }
322         return;
323     }
324     if ((event & assoc->cs_get_mask) || (event & EVENT_WORK)) /* input */
325     {
326         if ((assoc->cs_put_mask & EVENT_INPUT) == 0 && (event & assoc->cs_get_mask))
327         {
328             yaz_log(YLOG_DEBUG, "ir_session (input)");
329             /* We aren't speaking to this fellow */
330             if (assoc->state == ASSOC_DEAD)
331             {
332                 yaz_log(log_session, "Connection closed - end of session");
333                 cs_close(conn);
334                 destroy_association(assoc);
335                 iochan_destroy(h);
336                 return;
337             }
338             assoc->cs_get_mask = EVENT_INPUT;
339             if ((res = cs_get(conn, &assoc->input_buffer,
340                 &assoc->input_buffer_len)) <= 0)
341             {
342                 yaz_log(log_session, "Connection closed by client");
343                 cs_close(conn);
344                 destroy_association(assoc);
345                 iochan_destroy(h);
346                 return;
347             }
348             else if (res == 1) /* incomplete read - wait for more  */
349             {
350                 if (conn->io_pending & CS_WANT_WRITE)
351                     assoc->cs_get_mask |= EVENT_OUTPUT;
352                 iochan_setflag(h, assoc->cs_get_mask);
353                 return;
354             }
355             if (cs_more(conn)) /* more stuff - call us again later, please */
356                 iochan_setevent(h, EVENT_INPUT);
357                 
358             /* we got a complete PDU. Let's decode it */
359             yaz_log(YLOG_DEBUG, "Got PDU, %d bytes: lead=%02X %02X %02X", res,
360                             assoc->input_buffer[0] & 0xff,
361                             assoc->input_buffer[1] & 0xff,
362                             assoc->input_buffer[2] & 0xff);
363             req = request_get(&assoc->incoming); /* get a new request */
364             odr_reset(assoc->decode);
365             odr_setbuf(assoc->decode, assoc->input_buffer, res, 0);
366             if (!z_GDU(assoc->decode, &req->gdu_request, 0, 0))
367             {
368                 yaz_log(YLOG_WARN, "ODR error on incoming PDU: %s [element %s] "
369                         "[near byte %ld] ",
370                         odr_errmsg(odr_geterror(assoc->decode)),
371                         odr_getelement(assoc->decode),
372                         (long) odr_offset(assoc->decode));
373                 if (assoc->decode->error != OHTTP)
374                 {
375                     yaz_log(YLOG_WARN, "PDU dump:");
376                     odr_dumpBER(yaz_log_file(), assoc->input_buffer, res);
377                     request_release(req);
378                     do_close(assoc, Z_Close_protocolError,"Malformed package");
379                 }
380                 else
381                 {
382                     Z_GDU *p = z_get_HTTP_Response(assoc->encode, 400);
383                     assoc->state = ASSOC_DEAD;
384                     process_gdu_response(assoc, req, p);
385                 }
386                 return;
387             }
388             req->request_mem = odr_extract_mem(assoc->decode);
389             if (assoc->print) 
390             {
391                 if (!z_GDU(assoc->print, &req->gdu_request, 0, 0))
392                     yaz_log(YLOG_WARN, "ODR print error: %s", 
393                        odr_errmsg(odr_geterror(assoc->print)));
394                 odr_reset(assoc->print);
395             }
396             request_enq(&assoc->incoming, req);
397         }
398
399         /* can we do something yet? */
400         req = request_head(&assoc->incoming);
401         if (req->state == REQUEST_IDLE)
402         {
403             request_deq(&assoc->incoming);
404             process_gdu_request(assoc, req);
405         }
406     }
407     if (event & assoc->cs_put_mask)
408     {
409         request *req = request_head(&assoc->outgoing);
410
411         assoc->cs_put_mask = 0;
412         yaz_log(YLOG_DEBUG, "ir_session (output)");
413         req->state = REQUEST_PENDING;
414         switch (res = cs_put(conn, req->response, req->len_response))
415         {
416         case -1:
417             yaz_log(log_session, "Connection closed by client");
418             cs_close(conn);
419             destroy_association(assoc);
420             iochan_destroy(h);
421             break;
422         case 0: /* all sent - release the request structure */
423             yaz_log(YLOG_DEBUG, "Wrote PDU, %d bytes", req->len_response);
424 #if 0
425             yaz_log(YLOG_DEBUG, "HTTP out:\n%.*s", req->len_response,
426                     req->response);
427 #endif
428             nmem_destroy(req->request_mem);
429             request_deq(&assoc->outgoing);
430             request_release(req);
431             if (!request_head(&assoc->outgoing))
432             {   /* restore mask for cs_get operation ... */
433                 iochan_clearflag(h, EVENT_OUTPUT|EVENT_INPUT);
434                 iochan_setflag(h, assoc->cs_get_mask);
435                 if (assoc->state == ASSOC_DEAD)
436                     iochan_setevent(assoc->client_chan, EVENT_TIMEOUT);
437             }
438             else
439             {
440                 assoc->cs_put_mask = EVENT_OUTPUT;
441             }
442             break;
443         default:
444             if (conn->io_pending & CS_WANT_WRITE)
445                 assoc->cs_put_mask |= EVENT_OUTPUT;
446             if (conn->io_pending & CS_WANT_READ)
447                 assoc->cs_put_mask |= EVENT_INPUT;
448             iochan_setflag(h, assoc->cs_put_mask);
449         }
450     }
451     if (event & EVENT_EXCEPT)
452     {
453         yaz_log(YLOG_WARN, "ir_session (exception)");
454         cs_close(conn);
455         destroy_association(assoc);
456         iochan_destroy(h);
457     }
458 }
459
460 static int process_z_request(association *assoc, request *req, char **msg);
461
462
463 static void assoc_init_reset(association *assoc)
464 {
465     xfree (assoc->init);
466     assoc->init = (bend_initrequest *) xmalloc (sizeof(*assoc->init));
467
468     assoc->init->stream = assoc->encode;
469     assoc->init->print = assoc->print;
470     assoc->init->auth = 0;
471     assoc->init->referenceId = 0;
472     assoc->init->implementation_version = 0;
473     assoc->init->implementation_id = 0;
474     assoc->init->implementation_name = 0;
475     assoc->init->bend_sort = NULL;
476     assoc->init->bend_search = NULL;
477     assoc->init->bend_present = NULL;
478     assoc->init->bend_esrequest = NULL;
479     assoc->init->bend_delete = NULL;
480     assoc->init->bend_scan = NULL;
481     assoc->init->bend_segment = NULL;
482     assoc->init->bend_fetch = NULL;
483     assoc->init->bend_explain = NULL;
484     assoc->init->bend_srw_scan = NULL;
485     assoc->init->bend_srw_update = NULL;
486
487     assoc->init->charneg_request = NULL;
488     assoc->init->charneg_response = NULL;
489
490     assoc->init->decode = assoc->decode;
491     assoc->init->peer_name = 
492         odr_strdup (assoc->encode, cs_addrstr(assoc->client_link));
493
494     yaz_log(log_requestdetail, "peer %s", assoc->init->peer_name);
495 }
496
497 static int srw_bend_init(association *assoc, Z_SRW_diagnostic **d, int *num, Z_SRW_PDU *sr)
498 {
499     statserv_options_block *cb = statserv_getcontrol();
500     if (!assoc->init)
501     {
502         const char *encoding = "UTF-8";
503         Z_External *ce;
504         bend_initresult *binitres;
505
506         yaz_log(log_requestdetail, "srw_bend_init config=%s", cb->configname);
507         assoc_init_reset(assoc);
508         
509         assoc->maximumRecordSize = 3000000;
510         assoc->preferredMessageSize = 3000000;
511
512         if (sr->username)
513         {
514             Z_IdAuthentication *auth = odr_malloc(assoc->decode, sizeof(*auth));
515             int len;
516
517             len = strlen(sr->username) + 1;
518             if (sr->password) 
519                 len += strlen(sr->password) + 2;
520             auth->which = Z_IdAuthentication_open;
521             auth->u.open = odr_malloc(assoc->decode, len);
522             strcpy(auth->u.open, sr->username);
523             if (sr->password && *sr->password)
524             {
525                 strcat(auth->u.open, "/");
526                 strcat(auth->u.open, sr->password);
527             }
528             assoc->init->auth = auth;
529         }
530
531 #if 1
532         ce = yaz_set_proposal_charneg(assoc->decode, &encoding, 1, 0, 0, 1);
533         assoc->init->charneg_request = ce->u.charNeg3;
534 #endif
535         assoc->backend = 0;
536         if (!(binitres = (*cb->bend_init)(assoc->init)))
537         {
538             assoc->state = ASSOC_DEAD;
539             yaz_add_srw_diagnostic(assoc->encode, d, num,
540                             YAZ_SRW_AUTHENTICATION_ERROR, 0);
541             return 0;
542         }
543         assoc->backend = binitres->handle;
544         assoc->init->auth = 0;
545         if (binitres->errcode)
546         {
547             int srw_code = yaz_diag_bib1_to_srw(binitres->errcode);
548             assoc->state = ASSOC_DEAD;
549             yaz_add_srw_diagnostic(assoc->encode, d, num, srw_code,
550                                    binitres->errstring);
551             return 0;
552         }
553         return 1;
554     }
555     return 1;
556 }
557
558 static const char *get_esn(Z_RecordComposition *comp)
559 {
560     if (comp && comp->which == Z_RecordComp_complex)
561     {
562         if (comp->u.complex->generic 
563             && comp->u.complex->generic->elementSpec
564             && (comp->u.complex->generic->elementSpec->which == 
565                 Z_ElementSpec_elementSetName))
566             return comp->u.complex->generic->elementSpec->u.elementSetName;
567     }
568     else if (comp && comp->which == Z_RecordComp_simple &&
569              comp->u.simple->which == Z_ElementSetNames_generic)
570         return comp->u.simple->u.generic;
571     return 0;
572 }
573
574 static void set_esn(Z_RecordComposition **comp_p, const char *esn, NMEM nmem)
575 {
576     Z_RecordComposition *comp = nmem_malloc(nmem, sizeof(*comp));
577     
578     comp->which = Z_RecordComp_simple;
579     comp->u.simple = nmem_malloc(nmem, sizeof(*comp->u.simple));
580     comp->u.simple->which = Z_ElementSetNames_generic;
581     comp->u.simple->u.generic = nmem_strdup(nmem, esn);
582     *comp_p = comp;
583 }
584
585 static int retrieve_fetch(association *assoc, bend_fetch_rr *rr)
586 {
587 #if YAZ_HAVE_XML2
588     yaz_record_conv_t rc = 0;
589     const char *match_schema = 0;
590     int *match_syntax = 0;
591
592     if (assoc->server)
593     {
594         int r;
595         const char *input_schema = get_esn(rr->comp);
596         Odr_oid *input_syntax_raw = rr->request_format_raw;
597         
598         const char *backend_schema = 0;
599         Odr_oid *backend_syntax = 0;
600
601         r = yaz_retrieval_request(assoc->server->retrieval,
602                                   input_schema,
603                                   input_syntax_raw,
604                                   &match_schema,
605                                   &match_syntax,
606                                   &rc,
607                                   &backend_schema,
608                                   &backend_syntax);
609         if (r == -1) /* error ? */
610         {
611             const char *details = yaz_retrieval_get_error(
612                 assoc->server->retrieval);
613
614             rr->errcode = YAZ_BIB1_SYSTEM_ERROR_IN_PRESENTING_RECORDS;
615             if (details)
616                 rr->errstring = odr_strdup(rr->stream, details);
617             return -1;
618         }
619         else if (r == 1 || r == 3)
620         {
621             const char *details = input_schema;
622             rr->errcode =  YAZ_BIB1_ELEMENT_SET_NAMES_UNSUPP;
623             if (details)
624                 rr->errstring = odr_strdup(rr->stream, details);
625             return -1;
626         }
627         else if (r == 2)
628         {
629             rr->errcode = YAZ_BIB1_RECORD_SYNTAX_UNSUPP;
630             if (input_syntax_raw)
631             {
632                 char oidbuf[OID_STR_MAX];
633                 oid_to_dotstring(input_syntax_raw, oidbuf);
634                 rr->errstring = odr_strdup(rr->stream, oidbuf);
635             }
636             return -1;
637         }
638         if (backend_schema)
639         {
640             set_esn(&rr->comp, backend_schema, rr->stream->mem);
641         }
642         if (backend_syntax)
643         {
644             oident *oident_syntax = oid_getentbyoid(backend_syntax);
645
646             rr->request_format_raw = backend_syntax;
647             
648             if (oident_syntax)
649                 rr->request_format = oident_syntax->value;
650             else
651                 rr->request_format = VAL_NONE;
652         }
653     }
654     (*assoc->init->bend_fetch)(assoc->backend, rr);
655     if (rc && rr->record && rr->errcode == 0 && rr->len > 0)
656     {   /* post conversion must take place .. */
657         WRBUF output_record = wrbuf_alloc();
658         int r = yaz_record_conv_record(rc, rr->record, rr->len, output_record);
659         if (r)
660         {
661             const char *details = yaz_record_conv_get_error(rc);
662             rr->errcode = YAZ_BIB1_SYSTEM_ERROR_IN_PRESENTING_RECORDS;
663             if (details)
664                 rr->errstring = odr_strdup(rr->stream, details);
665         }
666         else
667         {
668             rr->len = wrbuf_len(output_record);
669             rr->record = odr_malloc(rr->stream, rr->len);
670             memcpy(rr->record, wrbuf_buf(output_record), rr->len);
671         }
672         wrbuf_free(output_record, 1);
673     }
674     if (match_syntax)
675     {
676         struct oident *oi = oid_getentbyoid(match_syntax);
677         rr->output_format = oi ? oi->value : VAL_NONE;
678         rr->output_format_raw = match_syntax;
679     }
680     if (match_schema)
681         rr->schema = odr_strdup(rr->stream, match_schema);
682     return 0;
683 #else
684     (*assoc->init->bend_fetch)(assoc->backend, rr);
685 #endif
686 }
687
688 static int srw_bend_fetch(association *assoc, int pos,
689                           Z_SRW_searchRetrieveRequest *srw_req,
690                           Z_SRW_record *record)
691 {
692     bend_fetch_rr rr;
693     ODR o = assoc->encode;
694
695     rr.setname = "default";
696     rr.number = pos;
697     rr.referenceId = 0;
698     rr.request_format = VAL_TEXT_XML;
699     rr.request_format_raw = yaz_oidval_to_z3950oid(assoc->decode,
700                                                    CLASS_RECSYN,
701                                                    VAL_TEXT_XML);
702     rr.comp = (Z_RecordComposition *)
703             odr_malloc(assoc->decode, sizeof(*rr.comp));
704     rr.comp->which = Z_RecordComp_complex;
705     rr.comp->u.complex = (Z_CompSpec *)
706             odr_malloc(assoc->decode, sizeof(Z_CompSpec));
707     rr.comp->u.complex->selectAlternativeSyntax = (bool_t *)
708         odr_malloc(assoc->encode, sizeof(bool_t));
709     *rr.comp->u.complex->selectAlternativeSyntax = 0;    
710     rr.comp->u.complex->num_dbSpecific = 0;
711     rr.comp->u.complex->dbSpecific = 0;
712     rr.comp->u.complex->num_recordSyntax = 0; 
713     rr.comp->u.complex->recordSyntax = 0;
714
715     rr.comp->u.complex->generic = (Z_Specification *) 
716             odr_malloc(assoc->decode, sizeof(Z_Specification));
717
718     /* schema uri = recordSchema (or NULL if recordSchema is not given) */
719     rr.comp->u.complex->generic->which = Z_Schema_uri;
720     rr.comp->u.complex->generic->schema.uri = srw_req->recordSchema;
721
722     /* ESN = recordSchema if recordSchema is present */
723     rr.comp->u.complex->generic->elementSpec = 0;
724     if (srw_req->recordSchema)
725     {
726         rr.comp->u.complex->generic->elementSpec = 
727             (Z_ElementSpec *) odr_malloc(assoc->encode, sizeof(Z_ElementSpec));
728         rr.comp->u.complex->generic->elementSpec->which = 
729             Z_ElementSpec_elementSetName;
730         rr.comp->u.complex->generic->elementSpec->u.elementSetName =
731             srw_req->recordSchema;
732     }
733     
734     rr.stream = assoc->encode;
735     rr.print = assoc->print;
736
737     rr.basename = 0;
738     rr.len = 0;
739     rr.record = 0;
740     rr.last_in_set = 0;
741     rr.errcode = 0;
742     rr.errstring = 0;
743     rr.surrogate_flag = 0;
744     rr.schema = srw_req->recordSchema;
745
746     if (!assoc->init->bend_fetch)
747         return 1;
748
749     retrieve_fetch(assoc, &rr);
750
751     if (rr.errcode && rr.surrogate_flag)
752     {
753         int code = yaz_diag_bib1_to_srw(rr.errcode);
754         const char *message = yaz_diag_srw_str(code);
755         int len = 200;
756         if (message)
757             len += strlen(message);
758         if (rr.errstring)
759             len += strlen(rr.errstring);
760
761         record->recordData_buf = odr_malloc(o, len);
762         
763         sprintf(record->recordData_buf, "<diagnostic "
764                 "xmlns=\"http://www.loc.gov/zing/srw/diagnostic/\">\n"
765                 " <uri>info:srw/diagnostic/1/%d</uri>\n", code);
766         if (rr.errstring)
767             sprintf(record->recordData_buf + strlen(record->recordData_buf),
768                     " <details>%s</details>\n", rr.errstring);
769         if (message)
770             sprintf(record->recordData_buf + strlen(record->recordData_buf),
771                     " <message>%s</message>\n", message);
772         sprintf(record->recordData_buf + strlen(record->recordData_buf),
773                 "</diagnostic>\n");
774         record->recordData_len = strlen(record->recordData_buf);
775         record->recordPosition = odr_intdup(o, pos);
776         record->recordSchema = "info:srw/schema/1/diagnostics-v1.1";
777         return 0;
778     }
779     else if (rr.len >= 0)
780     {
781         record->recordData_buf = rr.record;
782         record->recordData_len = rr.len;
783         record->recordPosition = odr_intdup(o, pos);
784         if (rr.schema)
785             record->recordSchema = odr_strdup(o, rr.schema);
786         else
787             record->recordSchema = 0;
788     }
789     return rr.errcode;
790 }
791
792 static int cql2pqf(ODR odr, const char *cql, cql_transform_t ct,
793                    Z_Query *query_result)
794 {
795     /* have a CQL query and  CQL to PQF transform .. */
796     CQL_parser cp = cql_parser_create();
797     int r;
798     int srw_errcode = 0;
799     const char *add = 0;
800     char rpn_buf[5120];
801             
802     r = cql_parser_string(cp, cql);
803     if (r)
804     {
805         /* CQL syntax error */
806         srw_errcode = 10; 
807     }
808     if (!r)
809     {
810         /* Syntax OK */
811         r = cql_transform_buf(ct,
812                               cql_parser_result(cp),
813                               rpn_buf, sizeof(rpn_buf)-1);
814         if (r)
815             srw_errcode  = cql_transform_error(ct, &add);
816     }
817     if (!r)
818     {
819         /* Syntax & transform OK. */
820         /* Convert PQF string to Z39.50 to RPN query struct */
821         YAZ_PQF_Parser pp = yaz_pqf_create();
822         Z_RPNQuery *rpnquery = yaz_pqf_parse(pp, odr, rpn_buf);
823         if (!rpnquery)
824         {
825             size_t off;
826             const char *pqf_msg;
827             int code = yaz_pqf_error(pp, &pqf_msg, &off);
828             yaz_log(YLOG_WARN, "PQF Parser Error %s (code %d)",
829                     pqf_msg, code);
830             srw_errcode = 10;
831         }
832         else
833         {
834             query_result->which = Z_Query_type_1;
835             query_result->u.type_1 = rpnquery;
836         }
837         yaz_pqf_destroy(pp);
838     }
839     cql_parser_destroy(cp);
840     return srw_errcode;
841 }
842
843 static int cql2pqf_scan(ODR odr, const char *cql, cql_transform_t ct,
844                         Z_AttributesPlusTerm *result)
845 {
846     Z_Query query;
847     Z_RPNQuery *rpn;
848     int srw_error = cql2pqf(odr, cql, ct, &query);
849     if (srw_error)
850         return srw_error;
851     if (query.which != Z_Query_type_1 && query.which != Z_Query_type_101)
852         return 10; /* bad query type */
853     rpn = query.u.type_1;
854     if (!rpn->RPNStructure) 
855         return 10; /* must be structure */
856     if (rpn->RPNStructure->which != Z_RPNStructure_simple)
857         return 10; /* must be simple */
858     if (rpn->RPNStructure->u.simple->which != Z_Operand_APT)
859         return 10; /* must be attributes plus term node .. */
860     memcpy(result, rpn->RPNStructure->u.simple->u.attributesPlusTerm,
861            sizeof(*result));
862     return 0;
863 }
864                    
865 static void srw_bend_search(association *assoc, request *req,
866                             Z_SRW_PDU *sr,
867                             Z_SRW_searchRetrieveResponse *srw_res,
868                             int *http_code)
869 {
870     int srw_error = 0;
871     Z_External *ext;
872     Z_SRW_searchRetrieveRequest *srw_req = sr->u.request;
873     
874     *http_code = 200;
875     yaz_log(log_requestdetail, "Got SRW SearchRetrieveRequest");
876     srw_bend_init(assoc, &srw_res->diagnostics, &srw_res->num_diagnostics, sr);
877     if (srw_res->num_diagnostics == 0 && assoc->init)
878     {
879         bend_search_rr rr;
880         rr.setname = "default";
881         rr.replace_set = 1;
882         rr.num_bases = 1;
883         rr.basenames = &srw_req->database;
884         rr.referenceId = 0;
885         rr.srw_sortKeys = 0;
886         rr.srw_setname = 0;
887         rr.srw_setnameIdleTime = 0;
888         rr.query = (Z_Query *) odr_malloc (assoc->decode, sizeof(*rr.query));
889         rr.query->u.type_1 = 0;
890         
891         if (srw_req->query_type == Z_SRW_query_type_cql)
892         {
893             if (assoc->server && assoc->server->cql_transform)
894             {
895                 int srw_errcode = cql2pqf(assoc->encode, srw_req->query.cql,
896                                           assoc->server->cql_transform,
897                                           rr.query);
898                 if (srw_errcode)
899                 {
900                     yaz_add_srw_diagnostic(assoc->encode,
901                                            &srw_res->diagnostics,
902                                            &srw_res->num_diagnostics,
903                                            srw_errcode, 0);
904                 }
905             }
906             else
907             {
908                 /* CQL query to backend. Wrap it - Z39.50 style */
909                 ext = (Z_External *) odr_malloc(assoc->decode, sizeof(*ext));
910                 ext->direct_reference = odr_getoidbystr(assoc->decode, 
911                                                         "1.2.840.10003.16.2");
912                 ext->indirect_reference = 0;
913                 ext->descriptor = 0;
914                 ext->which = Z_External_CQL;
915                 ext->u.cql = srw_req->query.cql;
916                 
917                 rr.query->which = Z_Query_type_104;
918                 rr.query->u.type_104 =  ext;
919             }
920         }
921         else if (srw_req->query_type == Z_SRW_query_type_pqf)
922         {
923             Z_RPNQuery *RPNquery;
924             YAZ_PQF_Parser pqf_parser;
925             
926             pqf_parser = yaz_pqf_create ();
927             
928             RPNquery = yaz_pqf_parse (pqf_parser, assoc->decode,
929                                       srw_req->query.pqf);
930             if (!RPNquery)
931             {
932                 const char *pqf_msg;
933                 size_t off;
934                 int code = yaz_pqf_error (pqf_parser, &pqf_msg, &off);
935                 yaz_log(log_requestdetail, "Parse error %d %s near offset %ld",
936                         code, pqf_msg, (long) off);
937                 srw_error = YAZ_SRW_QUERY_SYNTAX_ERROR;
938             }
939             
940             rr.query->which = Z_Query_type_1;
941             rr.query->u.type_1 =  RPNquery;
942             
943             yaz_pqf_destroy (pqf_parser);
944         }
945         else
946         {
947             yaz_add_srw_diagnostic(assoc->encode, &srw_res->diagnostics,
948                                    &srw_res->num_diagnostics,
949                                    YAZ_SRW_UNSUPP_QUERY_TYPE, 0);
950         }
951         if (rr.query->u.type_1)
952         {
953             rr.stream = assoc->encode;
954             rr.decode = assoc->decode;
955             rr.print = assoc->print;
956             rr.request = req;
957             if ( srw_req->sort.sortKeys )
958                 rr.srw_sortKeys = odr_strdup(assoc->encode, 
959                                              srw_req->sort.sortKeys );
960             rr.association = assoc;
961             rr.fd = 0;
962             rr.hits = 0;
963             rr.errcode = 0;
964             rr.errstring = 0;
965             rr.search_info = 0;
966             yaz_log_zquery_level(log_requestdetail,rr.query);
967             
968             (assoc->init->bend_search)(assoc->backend, &rr);
969             if (rr.errcode)
970             {
971                 if (rr.errcode == YAZ_BIB1_DATABASE_UNAVAILABLE)
972                 {
973                     *http_code = 404;
974                 }
975                 else
976                 {
977                     srw_error = yaz_diag_bib1_to_srw (rr.errcode);
978                     yaz_add_srw_diagnostic(assoc->encode,
979                                            &srw_res->diagnostics,
980                                            &srw_res->num_diagnostics,
981                                            srw_error, rr.errstring);
982                 }
983             }
984             else
985             {
986                 int number = srw_req->maximumRecords ? *srw_req->maximumRecords : 0;
987                 int start = srw_req->startRecord ? *srw_req->startRecord : 1;
988                 
989                 yaz_log(log_requestdetail, "Request to pack %d+%d out of %d",
990                         start, number, rr.hits);
991                 
992                 srw_res->numberOfRecords = odr_intdup(assoc->encode, rr.hits);
993                 if (rr.srw_setname)
994                 {
995                     srw_res->resultSetId =
996                         odr_strdup(assoc->encode, rr.srw_setname );
997                     srw_res->resultSetIdleTime =
998                         odr_intdup(assoc->encode, *rr.srw_setnameIdleTime );
999                 }
1000                 if (number > 0)
1001                 {
1002                     int i;
1003                     
1004                     if (start > rr.hits)
1005                     {
1006                         yaz_add_srw_diagnostic(assoc->encode, &srw_res->diagnostics,
1007                                                &srw_res->num_diagnostics,
1008                                                YAZ_SRW_FIRST_RECORD_POSITION_OUT_OF_RANGE, 0);
1009                     }
1010                     else
1011                     {
1012                         int ok = 1;
1013                         if (start + number > rr.hits)
1014                             number = rr.hits - start + 1;
1015
1016                         /* Call bend_present if defined */
1017                         if (assoc->init->bend_present)
1018                         {
1019                             bend_present_rr *bprr = (bend_present_rr*)
1020                                 odr_malloc (assoc->decode, sizeof(*bprr));
1021                             bprr->setname = "default";
1022                             bprr->start = start;
1023                             bprr->number = number;
1024                             bprr->format = VAL_TEXT_XML;
1025                             if (srw_req->recordSchema)
1026                             {
1027                                 bprr->comp = (Z_RecordComposition *) odr_malloc(assoc->decode,
1028                                         sizeof(*bprr->comp));
1029                                 bprr->comp->which = Z_RecordComp_simple;
1030                                 bprr->comp->u.simple = (Z_ElementSetNames *)
1031                                     odr_malloc(assoc->decode, sizeof(Z_ElementSetNames));
1032                                 bprr->comp->u.simple->which = Z_ElementSetNames_generic;
1033                                 bprr->comp->u.simple->u.generic = srw_req->recordSchema;
1034                             }
1035                             else
1036                             {
1037                                 bprr->comp = 0;
1038                             }
1039                             bprr->stream = assoc->encode;
1040                             bprr->referenceId = 0;
1041                             bprr->print = assoc->print;
1042                             bprr->request = req;
1043                             bprr->association = assoc;
1044                             bprr->errcode = 0;
1045                             bprr->errstring = NULL;
1046                             (*assoc->init->bend_present)(assoc->backend, bprr);
1047                             
1048                             if (!bprr->request)
1049                                 return;
1050                             if (bprr->errcode)
1051                             {
1052                                 srw_error = yaz_diag_bib1_to_srw (bprr->errcode);
1053                                 yaz_add_srw_diagnostic(assoc->encode,
1054                                                        &srw_res->diagnostics,
1055                                                        &srw_res->num_diagnostics,
1056                                                        srw_error, bprr->errstring);
1057                                 ok = 0;
1058                             }
1059                         }
1060
1061                         if (ok)
1062                         {
1063                             int j = 0;
1064                             int packing = Z_SRW_recordPacking_string;
1065                             if (srw_req->recordPacking){
1066                                 if (!strcmp(srw_req->recordPacking, "xml"))
1067                                     packing = Z_SRW_recordPacking_XML;
1068                                 if (!strcmp(srw_req->recordPacking, "url"))
1069                                     packing = Z_SRW_recordPacking_URL;
1070                             }
1071                             srw_res->records = (Z_SRW_record *)
1072                                 odr_malloc(assoc->encode,
1073                                            number * sizeof(*srw_res->records));
1074                             
1075                             srw_res->extra_records = (Z_SRW_extra_record **)
1076                                 odr_malloc(assoc->encode,
1077                                            number*sizeof(*srw_res->extra_records));
1078
1079                             for (i = 0; i<number; i++)
1080                             {
1081                                 int errcode;
1082                                 
1083                                 srw_res->records[j].recordPacking = packing;
1084                                 srw_res->records[j].recordData_buf = 0;
1085                                 srw_res->extra_records[j] = 0;
1086                                 yaz_log(YLOG_DEBUG, "srw_bend_fetch %d", i+start);
1087                                 errcode = srw_bend_fetch(assoc, i+start, srw_req,
1088                                                          srw_res->records + j);
1089                                 if (errcode)
1090                                 {
1091                                     yaz_add_srw_diagnostic(assoc->encode,
1092                                                            &srw_res->diagnostics,
1093                                                            &srw_res->num_diagnostics,
1094                                                            yaz_diag_bib1_to_srw (errcode),
1095                                                            rr.errstring);
1096                                     
1097                                     break;
1098                                 }
1099                                 if (srw_res->records[j].recordData_buf)
1100                                     j++;
1101                             }
1102                             srw_res->num_records = j;
1103                             if (!j)
1104                                 srw_res->records = 0;
1105                         }
1106                     }
1107                 }
1108             }
1109         }
1110     }
1111     if (log_request)
1112     {
1113         const char *querystr = "?";
1114         const char *querytype = "?";
1115         WRBUF wr = wrbuf_alloc();
1116
1117         switch (srw_req->query_type)
1118         {
1119         case Z_SRW_query_type_cql:
1120             querytype = "CQL";
1121             querystr = srw_req->query.cql;
1122             break;
1123         case Z_SRW_query_type_pqf:
1124             querytype = "PQF";
1125             querystr = srw_req->query.pqf;
1126             break;
1127         }
1128         wrbuf_printf(wr, "SRWSearch ");
1129         wrbuf_printf(wr, srw_req->database);
1130         wrbuf_printf(wr, " ");
1131         if (srw_res->num_diagnostics)
1132             wrbuf_printf(wr, "ERROR %s", srw_res->diagnostics[0].uri);
1133         else if (*http_code != 200)
1134             wrbuf_printf(wr, "ERROR info:http/%d", *http_code);
1135         else if (srw_res->numberOfRecords)
1136         {
1137             wrbuf_printf(wr, "OK %d",
1138                          (srw_res->numberOfRecords ?
1139                           *srw_res->numberOfRecords : 0));
1140         }
1141         wrbuf_printf(wr, " %s %d+%d", 
1142                      (srw_res->resultSetId ?
1143                       srw_res->resultSetId : "-"),
1144                      (srw_req->startRecord ? *srw_req->startRecord : 1), 
1145                      srw_res->num_records);
1146         yaz_log(log_request, "%s %s: %s", wrbuf_buf(wr), querytype, querystr);
1147         wrbuf_free(wr, 1);
1148     }
1149 }
1150
1151 static char *srw_bend_explain_default(void *handle, bend_explain_rr *rr)
1152 {
1153 #if YAZ_HAVE_XML2
1154     xmlNodePtr ptr = rr->server_node_ptr;
1155     if (!ptr)
1156         return 0;
1157     for (ptr = ptr->children; ptr; ptr = ptr->next)
1158     {
1159         if (ptr->type != XML_ELEMENT_NODE)
1160             continue;
1161         if (!strcmp((const char *) ptr->name, "explain"))
1162         {
1163             int len;
1164             xmlDocPtr doc = xmlNewDoc(BAD_CAST "1.0");
1165             xmlChar *buf_out;
1166             char *content;
1167
1168             ptr = xmlCopyNode(ptr, 1);
1169         
1170             xmlDocSetRootElement(doc, ptr);
1171             
1172             xmlDocDumpMemory(doc, &buf_out, &len);
1173             content = (char*) odr_malloc(rr->stream, 1+len);
1174             memcpy(content, buf_out, len);
1175             content[len] = '\0';
1176             
1177             xmlFree(buf_out);
1178             xmlFreeDoc(doc);
1179             rr->explain_buf = content;
1180             return 0;
1181         }
1182     }
1183 #endif
1184     return 0;
1185 }
1186
1187 static void srw_bend_explain(association *assoc, request *req,
1188                              Z_SRW_PDU *sr,
1189                              Z_SRW_explainResponse *srw_res,
1190                              int *http_code)
1191 {
1192     Z_SRW_explainRequest *srw_req = sr->u.explain_request;
1193     yaz_log(log_requestdetail, "Got SRW ExplainRequest");
1194     *http_code = 404;
1195     srw_bend_init(assoc, &srw_res->diagnostics, &srw_res->num_diagnostics, sr);
1196     if (assoc->init)
1197     {
1198         bend_explain_rr rr;
1199         
1200         rr.stream = assoc->encode;
1201         rr.decode = assoc->decode;
1202         rr.print = assoc->print;
1203         rr.explain_buf = 0;
1204         rr.database = srw_req->database;
1205         if (assoc->server)
1206             rr.server_node_ptr = assoc->server->server_node_ptr;
1207         else
1208             rr.server_node_ptr = 0;
1209         rr.schema = "http://explain.z3950.org/dtd/2.0/";
1210         if (assoc->init->bend_explain)
1211             (*assoc->init->bend_explain)(assoc->backend, &rr);
1212         else
1213             srw_bend_explain_default(assoc->backend, &rr);
1214
1215         if (rr.explain_buf)
1216         {
1217             int packing = Z_SRW_recordPacking_string;
1218             if (srw_req->recordPacking)
1219             {
1220                 if (!strcmp(srw_req->recordPacking, "xml"))
1221                     packing = Z_SRW_recordPacking_XML;
1222                 else if (!strcmp(srw_req->recordPacking, "url"))
1223                     packing = Z_SRW_recordPacking_URL;
1224             }
1225             srw_res->record.recordSchema = rr.schema;
1226             srw_res->record.recordPacking = packing;
1227             srw_res->record.recordData_buf = rr.explain_buf;
1228             srw_res->record.recordData_len = strlen(rr.explain_buf);
1229             srw_res->record.recordPosition = 0;
1230             *http_code = 200;
1231         }
1232     }
1233 }
1234
1235 static void srw_bend_scan(association *assoc, request *req,
1236                           Z_SRW_PDU *sr,
1237                           Z_SRW_scanResponse *srw_res,
1238                           int *http_code)
1239 {
1240     Z_SRW_scanRequest *srw_req = sr->u.scan_request;
1241     yaz_log(log_requestdetail, "Got SRW ScanRequest");
1242
1243     *http_code = 200;
1244     srw_bend_init(assoc, &srw_res->diagnostics, &srw_res->num_diagnostics, sr);
1245     if (srw_res->num_diagnostics == 0 && assoc->init)
1246     {
1247         struct scan_entry *save_entries;
1248
1249         bend_scan_rr *bsrr = (bend_scan_rr *)
1250             odr_malloc (assoc->encode, sizeof(*bsrr));
1251         bsrr->num_bases = 1;
1252         bsrr->basenames = &srw_req->database;
1253
1254         bsrr->num_entries = srw_req->maximumTerms ?
1255             *srw_req->maximumTerms : 10;
1256         bsrr->term_position = srw_req->responsePosition ?
1257             *srw_req->responsePosition : 1;
1258
1259         bsrr->errcode = 0;
1260         bsrr->errstring = 0;
1261         bsrr->referenceId = 0;
1262         bsrr->stream = assoc->encode;
1263         bsrr->print = assoc->print;
1264         bsrr->step_size = odr_intdup(assoc->decode, 0);
1265         bsrr->entries = 0;
1266
1267         if (bsrr->num_entries > 0) 
1268         {
1269             int i;
1270             bsrr->entries = odr_malloc(assoc->decode, sizeof(*bsrr->entries) *
1271                                        bsrr->num_entries);
1272             for (i = 0; i<bsrr->num_entries; i++)
1273             {
1274                 bsrr->entries[i].term = 0;
1275                 bsrr->entries[i].occurrences = 0;
1276                 bsrr->entries[i].errcode = 0;
1277                 bsrr->entries[i].errstring = 0;
1278                 bsrr->entries[i].display_term = 0;
1279             }
1280         }
1281         save_entries = bsrr->entries;  /* save it so we can compare later */
1282
1283         if (srw_req->query_type == Z_SRW_query_type_pqf &&
1284             assoc->init->bend_scan)
1285         {
1286             Odr_oid *scan_attributeSet = 0;
1287             oident *attset;
1288             YAZ_PQF_Parser pqf_parser = yaz_pqf_create();
1289             
1290             bsrr->term = yaz_pqf_scan(pqf_parser, assoc->decode,
1291                                       &scan_attributeSet, 
1292                                       srw_req->scanClause.pqf); 
1293             if (scan_attributeSet &&
1294                 (attset = oid_getentbyoid(scan_attributeSet)) &&
1295                 (attset->oclass == CLASS_ATTSET ||
1296                  attset->oclass == CLASS_GENERAL))
1297                 bsrr->attributeset = attset->value;
1298             else
1299                 bsrr->attributeset = VAL_NONE;
1300             yaz_pqf_destroy(pqf_parser);
1301             bsrr->scanClause = 0;
1302             ((int (*)(void *, bend_scan_rr *))
1303              (*assoc->init->bend_scan))(assoc->backend, bsrr);
1304         }
1305         else if (srw_req->query_type == Z_SRW_query_type_cql
1306                  && assoc->init->bend_scan && assoc->server
1307                  && assoc->server->cql_transform)
1308         {
1309             int srw_error;
1310             bsrr->scanClause = 0;
1311             bsrr->attributeset = VAL_NONE;
1312             bsrr->term = odr_malloc(assoc->decode, sizeof(*bsrr->term));
1313             srw_error = cql2pqf_scan(assoc->encode,
1314                                      srw_req->scanClause.cql,
1315                                      assoc->server->cql_transform,
1316                                      bsrr->term);
1317             if (srw_error)
1318                 yaz_add_srw_diagnostic(assoc->encode, &srw_res->diagnostics,
1319                                        &srw_res->num_diagnostics,
1320                                        srw_error, 0);
1321             else
1322             {
1323                 ((int (*)(void *, bend_scan_rr *))
1324                  (*assoc->init->bend_scan))(assoc->backend, bsrr);
1325             }
1326         }
1327         else if (srw_req->query_type == Z_SRW_query_type_cql
1328                  && assoc->init->bend_srw_scan)
1329         {
1330             bsrr->term = 0;
1331             bsrr->attributeset = VAL_NONE;
1332             bsrr->scanClause = srw_req->scanClause.cql;
1333             ((int (*)(void *, bend_scan_rr *))
1334              (*assoc->init->bend_srw_scan))(assoc->backend, bsrr);
1335         }
1336         else
1337         {
1338             yaz_add_srw_diagnostic(assoc->encode, &srw_res->diagnostics,
1339                                    &srw_res->num_diagnostics,
1340                                    YAZ_SRW_UNSUPP_OPERATION, "scan");
1341         }
1342         if (bsrr->errcode)
1343         {
1344             int srw_error;
1345             if (bsrr->errcode == YAZ_BIB1_DATABASE_UNAVAILABLE)
1346             {
1347                 *http_code = 404;
1348                 return;
1349             }
1350             srw_error = yaz_diag_bib1_to_srw (bsrr->errcode);
1351
1352             yaz_add_srw_diagnostic(assoc->encode, &srw_res->diagnostics,
1353                                    &srw_res->num_diagnostics,
1354                                    srw_error, bsrr->errstring);
1355         }
1356         else if (srw_res->num_diagnostics == 0 && bsrr->num_entries)
1357         {
1358             int i;
1359             srw_res->terms = (Z_SRW_scanTerm*)
1360                 odr_malloc(assoc->encode, sizeof(*srw_res->terms) *
1361                            bsrr->num_entries);
1362
1363             srw_res->num_terms =  bsrr->num_entries;
1364             for (i = 0; i<bsrr->num_entries; i++)
1365             {
1366                 Z_SRW_scanTerm *t = srw_res->terms + i;
1367                 t->value = odr_strdup(assoc->encode, bsrr->entries[i].term);
1368                 t->numberOfRecords =
1369                     odr_intdup(assoc->encode, bsrr->entries[i].occurrences);
1370                 t->displayTerm = 0;
1371                 if (save_entries == bsrr->entries && 
1372                     bsrr->entries[i].display_term)
1373                 {
1374                     /* the entries was _not_ set by the handler. So it's
1375                        safe to test for new member display_term. It is
1376                        NULL'ed by us.
1377                     */
1378                     t->displayTerm = odr_strdup(assoc->encode, 
1379                                                 bsrr->entries[i].display_term);
1380                 }
1381                 t->whereInList = 0;
1382             }
1383         }
1384     }
1385     if (log_request)
1386     {
1387         WRBUF wr = wrbuf_alloc();
1388         const char *querytype = 0;
1389         const char *querystr = 0;
1390
1391         switch(srw_req->query_type)
1392         {
1393         case Z_SRW_query_type_pqf:
1394             querytype = "PQF";
1395             querystr = srw_req->scanClause.pqf;
1396             break;
1397         case Z_SRW_query_type_cql:
1398             querytype = "CQL";
1399             querystr = srw_req->scanClause.cql;
1400             break;
1401         default:
1402             querytype = "Unknown";
1403             querystr = "";
1404         }
1405
1406         wrbuf_printf(wr, "SRWScan ");
1407         wrbuf_printf(wr, srw_req->database);
1408         wrbuf_printf(wr, " ");
1409
1410         if (srw_res->num_diagnostics)
1411             wrbuf_printf(wr, "ERROR %s - ", srw_res->diagnostics[0].uri);
1412         else
1413             wrbuf_printf(wr, "OK - - ");
1414
1415         wrbuf_printf(wr, "%d+%d+0 ",
1416                      (srw_req->responsePosition ? 
1417                       *srw_req->responsePosition : 1),
1418                      (srw_req->maximumTerms ?
1419                       *srw_req->maximumTerms : 1));
1420         /* there is no step size in SRU/W ??? */
1421         wrbuf_printf(wr, "%s: %s ", querytype, querystr);
1422         yaz_log(log_request, "%s ", wrbuf_buf(wr) );
1423         wrbuf_free(wr, 1);
1424     }
1425
1426 }
1427
1428 static void srw_bend_update(association *assoc, request *req,
1429                             Z_SRW_PDU *sr,
1430                             Z_SRW_updateResponse *srw_res,
1431                             int *http_code)
1432 {
1433     Z_SRW_updateRequest *srw_req = sr->u.update_request;
1434     yaz_log(YLOG_DEBUG, "Got SRW UpdateRequest");
1435     yaz_log(YLOG_DEBUG, "num_diag = %d", srw_res->num_diagnostics );
1436     *http_code = 404;
1437     srw_bend_init(assoc, &srw_res->diagnostics, &srw_res->num_diagnostics, sr);
1438     if (assoc->init)
1439     {
1440         bend_update_rr rr;
1441         
1442         rr.stream = assoc->encode;
1443         rr.print = assoc->print;
1444         rr.num_bases = 1;
1445         rr.basenames = &srw_req->database;
1446         rr.operation = srw_req->operation;
1447         rr.operation_status = "failed";
1448         rr.record_id = 0;
1449         rr.record_version = 0;
1450         rr.record_checksum = 0;
1451         rr.record_old_version = 0;
1452         rr.record_packing = "xml";
1453         rr.record_schema = 0;
1454         rr.record_data = 0;
1455         rr.request_extra_record = 0;
1456         rr.response_extra_record = 0;
1457         rr.extra_request_data = 0;
1458         rr.extra_response_data = 0;
1459         rr.uri = 0;
1460         rr.message = 0;
1461         rr.details = 0;
1462
1463         yaz_log(YLOG_DEBUG, "basename = %s", rr.basenames[0] );
1464         yaz_log(YLOG_DEBUG, "Operation = %s", rr.operation );
1465         if ( !strcmp( rr.operation, "delete" ) ){
1466             if ( !srw_req->recordId ){
1467                 yaz_add_srw_diagnostic(assoc->encode, &srw_res->diagnostics,
1468                                        &srw_res->num_diagnostics,
1469                                        7, "recordId" );
1470             }
1471             else {
1472                 rr.record_id = srw_req->recordId;
1473             }
1474             if (  !srw_req->recordVersion ){
1475                 yaz_add_srw_diagnostic(assoc->encode, &srw_res->diagnostics,
1476                                        &srw_res->num_diagnostics,
1477                                        7, "recordVersion" );
1478             }
1479             else {
1480                 rr.record_version = odr_strdup( assoc->encode,
1481                                                 srw_req->recordVersion );
1482                 
1483             }
1484             if ( srw_req->recordOldVersion ){
1485                 rr.record_old_version = odr_strdup(assoc->encode,
1486                                                    srw_req->recordOldVersion );
1487             }
1488             if ( srw_req->extraRequestData ){
1489                 rr.extra_request_data = odr_strdup(assoc->encode,
1490                                                    srw_req->extraRequestData );
1491             }
1492         }
1493         else if ( !strcmp( rr.operation, "replace" ) ){
1494             if ( !srw_req->recordId ){
1495                 yaz_add_srw_diagnostic(assoc->encode, &srw_res->diagnostics,
1496                                        &srw_res->num_diagnostics,
1497                                        7, "recordId" );
1498             }
1499             else {
1500                 rr.record_id = srw_req->recordId;
1501             }
1502             if ( srw_req->record.recordSchema == 0 ){
1503                 yaz_add_srw_diagnostic(assoc->encode, &srw_res->diagnostics,
1504                                        &srw_res->num_diagnostics,
1505                                        7, "recordSchema" );
1506             }
1507             else {
1508                 rr.record_schema = odr_strdup(assoc->encode,
1509                                               srw_req->record.recordSchema );
1510             }
1511             switch (srw_req->record.recordPacking)
1512             {
1513             case Z_SRW_recordPacking_string: 
1514                 rr.record_packing = "string";
1515                 break;
1516             case Z_SRW_recordPacking_XML: 
1517                 rr.record_packing = "xml";
1518                 break;
1519             case Z_SRW_recordPacking_URL: 
1520                 rr.record_packing = "url";
1521                 break;
1522             }
1523             if ( srw_req->record.recordData_len ){
1524                 rr.record_data = odr_strdupn(assoc->encode, 
1525                                              srw_req->record.recordData_buf,
1526                                              srw_req->record.recordData_len );
1527                 rr.request_extra_record = srw_req->extra_record;
1528             }
1529             else {
1530                 yaz_add_srw_diagnostic(assoc->encode, &srw_res->diagnostics,
1531                                        &srw_res->num_diagnostics,
1532                                        7, "recordData" );
1533             }
1534             if (srw_req->extraRequestData)
1535                 rr.extra_request_data = odr_strdup(assoc->encode,
1536                                                    srw_req->extraRequestData );
1537         }
1538         else if ( !strcmp( rr.operation, "insert" ) )
1539         {
1540             rr.record_id = srw_req->recordId; 
1541             if ( srw_req->record.recordSchema == 0 ){
1542                 yaz_add_srw_diagnostic(assoc->encode, &srw_res->diagnostics,
1543                                        &srw_res->num_diagnostics,
1544                                        7, "recordSchema" );
1545             }
1546             else {
1547                 rr.record_schema = odr_strdup(assoc->encode,
1548                                               srw_req->record.recordSchema);
1549             }
1550             switch (srw_req->record.recordPacking)
1551             {
1552             case Z_SRW_recordPacking_string: 
1553                 rr.record_packing = "string";
1554                 break;
1555             case Z_SRW_recordPacking_XML: 
1556                 rr.record_packing = "xml";
1557                 break;
1558             case Z_SRW_recordPacking_URL: 
1559                 rr.record_packing = "url";
1560                 break;
1561             }
1562             
1563             if (srw_req->record.recordData_len)
1564             {
1565                 rr.record_data = odr_strdupn(assoc->encode, 
1566                                              srw_req->record.recordData_buf,
1567                                              srw_req->record.recordData_len );
1568                 rr.request_extra_record = srw_req->extra_record;
1569             }
1570             else
1571                 yaz_add_srw_diagnostic(assoc->encode, &srw_res->diagnostics,
1572                                        &srw_res->num_diagnostics,
1573                                        7, "recordData" );
1574             if ( srw_req->extraRequestData )
1575                 rr.extra_request_data = odr_strdup(assoc->encode,
1576                                                    srw_req->extraRequestData );
1577         }
1578         if (srw_res->num_diagnostics == 0)
1579         {
1580             if ( assoc->init->bend_srw_update)
1581                 (*assoc->init->bend_srw_update)(assoc->backend, &rr);
1582             else {
1583                 yaz_log( YLOG_WARN, "Got No Update function!");
1584                 return;
1585             }
1586         }
1587
1588         if (rr.uri)
1589             yaz_add_srw_diagnostic_uri(assoc->encode,
1590                                        &srw_res->diagnostics,
1591                                        &srw_res->num_diagnostics,
1592                                        rr.uri, 
1593                                        rr.message,
1594                                        rr.details);
1595         srw_res->recordId = rr.record_id;
1596         srw_res->operationStatus = rr.operation_status;
1597         srw_res->recordVersion = rr.record_version;
1598         srw_res->recordChecksum = rr.record_checksum;
1599         srw_res->extraResponseData = rr.extra_response_data;
1600         srw_res->record.recordPosition = 0;
1601         if (srw_res->num_diagnostics == 0 && rr.record_data)
1602         {
1603             srw_res->record.recordSchema = rr.record_schema;
1604             srw_res->record.recordPacking = srw_req->record.recordPacking;
1605             srw_res->record.recordData_buf = rr.record_data;
1606             srw_res->record.recordData_len = strlen(rr.record_data);
1607             srw_res->extra_record = rr.response_extra_record;
1608                 
1609         }
1610         else
1611             srw_res->record.recordData_len = 0;
1612         *http_code = 200;
1613     }
1614 }
1615
1616 /* check if path is OK (1); BAD (0) */
1617 static int check_path(const char *path)
1618 {
1619     if (*path != '/')
1620         return 0;
1621     if (strstr(path, ".."))
1622         return 0;
1623     return 1;
1624 }
1625
1626 static char *read_file(const char *fname, ODR o, int *sz)
1627 {
1628     char *buf;
1629     FILE *inf = fopen(fname, "rb");
1630     if (!inf)
1631         return 0;
1632
1633     fseek(inf, 0L, SEEK_END);
1634     *sz = ftell(inf);
1635     rewind(inf);
1636     buf = odr_malloc(o, *sz);
1637     fread(buf, 1, *sz, inf);
1638     fclose(inf);
1639     return buf;     
1640 }
1641
1642 static void process_http_request(association *assoc, request *req)
1643 {
1644     Z_HTTP_Request *hreq = req->gdu_request->u.HTTP_Request;
1645     ODR o = assoc->encode;
1646     int r = 2;  /* 2=NOT TAKEN, 1=TAKEN, 0=SOAP TAKEN */
1647     Z_SRW_PDU *sr = 0;
1648     Z_SOAP *soap_package = 0;
1649     Z_GDU *p = 0;
1650     char *charset = 0;
1651     Z_HTTP_Response *hres = 0;
1652     int keepalive = 1;
1653     const char *stylesheet = 0; /* for now .. set later */
1654     Z_SRW_diagnostic *diagnostic = 0;
1655     int num_diagnostic = 0;
1656     const char *host = z_HTTP_header_lookup(hreq->headers, "Host");
1657
1658     if (!control_association(assoc, host, 0))
1659     {
1660         p = z_get_HTTP_Response(o, 404);
1661         r = 1;
1662     }
1663     if (r == 2 && assoc->server && assoc->server->docpath
1664         && hreq->path[0] == '/' 
1665         && 
1666         /* check if path is a proper prefix of documentroot */
1667         strncmp(hreq->path+1, assoc->server->docpath,
1668                 strlen(assoc->server->docpath))
1669         == 0)
1670     {   
1671         if (!check_path(hreq->path))
1672         {
1673             yaz_log(YLOG_LOG, "File %s access forbidden", hreq->path+1);
1674             p = z_get_HTTP_Response(o, 404);
1675         }
1676         else
1677         {
1678             int content_size = 0;
1679             char *content_buf = read_file(hreq->path+1, o, &content_size);
1680             if (!content_buf)
1681             {
1682                 yaz_log(YLOG_LOG, "File %s not found", hreq->path+1);
1683                 p = z_get_HTTP_Response(o, 404);
1684             }
1685             else
1686             {
1687                 const char *ctype = 0;
1688                 yaz_mime_types types = yaz_mime_types_create();
1689                 
1690                 yaz_mime_types_add(types, "xsl", "application/xml");
1691                 yaz_mime_types_add(types, "xml", "application/xml");
1692                 yaz_mime_types_add(types, "css", "text/css");
1693                 yaz_mime_types_add(types, "html", "text/html");
1694                 yaz_mime_types_add(types, "htm", "text/html");
1695                 yaz_mime_types_add(types, "txt", "text/plain");
1696                 yaz_mime_types_add(types, "js", "application/x-javascript");
1697                 
1698                 yaz_mime_types_add(types, "gif", "image/gif");
1699                 yaz_mime_types_add(types, "png", "image/png");
1700                 yaz_mime_types_add(types, "jpg", "image/jpeg");
1701                 yaz_mime_types_add(types, "jpeg", "image/jpeg");
1702                 
1703                 ctype = yaz_mime_lookup_fname(types, hreq->path);
1704                 if (!ctype)
1705                 {
1706                     yaz_log(YLOG_LOG, "No mime type for %s", hreq->path+1);
1707                     p = z_get_HTTP_Response(o, 404);
1708                 }
1709                 else
1710                 {
1711                     p = z_get_HTTP_Response(o, 200);
1712                     hres = p->u.HTTP_Response;
1713                     hres->content_buf = content_buf;
1714                     hres->content_len = content_size;
1715                     z_HTTP_header_add(o, &hres->headers, "Content-Type", ctype);
1716                 }
1717                 yaz_mime_types_destroy(types);
1718             }
1719         }
1720         r = 1;
1721     }
1722
1723     if (r == 2)
1724     {
1725         r = yaz_srw_decode(hreq, &sr, &soap_package, assoc->decode, &charset);
1726         yaz_log(YLOG_DEBUG, "yaz_srw_decode returned %d", r);
1727     }
1728     if (r == 2)  /* not taken */
1729     {
1730         r = yaz_sru_decode(hreq, &sr, &soap_package, assoc->decode, &charset,
1731                            &diagnostic, &num_diagnostic);
1732         yaz_log(YLOG_DEBUG, "yaz_sru_decode returned %d", r);
1733     }
1734     if (r == 0)  /* decode SRW/SRU OK .. */
1735     {
1736         int http_code = 200;
1737         if (sr->which == Z_SRW_searchRetrieve_request)
1738         {
1739             Z_SRW_PDU *res =
1740                 yaz_srw_get(assoc->encode, Z_SRW_searchRetrieve_response);
1741
1742             stylesheet = sr->u.request->stylesheet;
1743             if (num_diagnostic)
1744             {
1745                 res->u.response->diagnostics = diagnostic;
1746                 res->u.response->num_diagnostics = num_diagnostic;
1747             }
1748             else
1749             {
1750                 srw_bend_search(assoc, req, sr, res->u.response, 
1751                                 &http_code);
1752             }
1753             if (http_code == 200)
1754                 soap_package->u.generic->p = res;
1755         }
1756         else if (sr->which == Z_SRW_explain_request)
1757         {
1758             Z_SRW_PDU *res = yaz_srw_get(o, Z_SRW_explain_response);
1759             stylesheet = sr->u.explain_request->stylesheet;
1760             if (num_diagnostic)
1761             {   
1762                 res->u.explain_response->diagnostics = diagnostic;
1763                 res->u.explain_response->num_diagnostics = num_diagnostic;
1764             }
1765             srw_bend_explain(assoc, req, sr,
1766                              res->u.explain_response, &http_code);
1767             if (http_code == 200)
1768                 soap_package->u.generic->p = res;
1769         }
1770         else if (sr->which == Z_SRW_scan_request)
1771         {
1772             Z_SRW_PDU *res = yaz_srw_get(o, Z_SRW_scan_response);
1773             stylesheet = sr->u.scan_request->stylesheet;
1774             if (num_diagnostic)
1775             {   
1776                 res->u.scan_response->diagnostics = diagnostic;
1777                 res->u.scan_response->num_diagnostics = num_diagnostic;
1778             }
1779             srw_bend_scan(assoc, req, sr,
1780                           res->u.scan_response, &http_code);
1781             if (http_code == 200)
1782                 soap_package->u.generic->p = res;
1783         }
1784         else if (sr->which == Z_SRW_update_request)
1785         {
1786             Z_SRW_PDU *res = yaz_srw_get(o, Z_SRW_update_response);
1787             yaz_log(YLOG_DEBUG, "handling SRW UpdateRequest");
1788             if (num_diagnostic)
1789             {   
1790                 res->u.update_response->diagnostics = diagnostic;
1791                 res->u.update_response->num_diagnostics = num_diagnostic;
1792             }
1793             yaz_log(YLOG_DEBUG, "num_diag = %d", res->u.update_response->num_diagnostics );
1794             srw_bend_update(assoc, req, sr,
1795                             res->u.update_response, &http_code);
1796             if (http_code == 200)
1797                 soap_package->u.generic->p = res;
1798         }
1799         else
1800         {
1801             yaz_log(log_request, "SOAP ERROR"); 
1802             /* FIXME - what error, what query */
1803             http_code = 500;
1804             z_soap_error(assoc->encode, soap_package,
1805                          "SOAP-ENV:Client", "Bad method", 0); 
1806         }
1807         if (http_code == 200 || http_code == 500)
1808         {
1809             static Z_SOAP_Handler soap_handlers[4] = {
1810 #if YAZ_HAVE_XML2
1811                 {"http://www.loc.gov/zing/srw/", 0,
1812                  (Z_SOAP_fun) yaz_srw_codec},
1813                 {"http://www.loc.gov/zing/srw/v1.0/", 0,
1814                  (Z_SOAP_fun) yaz_srw_codec},
1815                 {"http://www.loc.gov/zing/srw/update/", 0,
1816                  (Z_SOAP_fun) yaz_ucp_codec},
1817 #endif
1818                 {0, 0, 0}
1819             };
1820             char ctype[60];
1821             int ret;
1822             p = z_get_HTTP_Response(o, 200);
1823             hres = p->u.HTTP_Response;
1824
1825             if (!stylesheet && assoc->server)
1826                 stylesheet = assoc->server->stylesheet;
1827
1828             /* empty stylesheet means NO stylesheet */
1829             if (stylesheet && *stylesheet == '\0')
1830                 stylesheet = 0;
1831
1832             ret = z_soap_codec_enc_xsl(assoc->encode, &soap_package,
1833                                        &hres->content_buf, &hres->content_len,
1834                                        soap_handlers, charset, stylesheet);
1835             hres->code = http_code;
1836
1837             strcpy(ctype, "text/xml");
1838             if (charset)
1839             {
1840                 strcat(ctype, "; charset=");
1841                 strcat(ctype, charset);
1842             }
1843             z_HTTP_header_add(o, &hres->headers, "Content-Type", ctype);
1844         }
1845         else
1846             p = z_get_HTTP_Response(o, http_code);
1847     }
1848
1849     if (p == 0)
1850         p = z_get_HTTP_Response(o, 500);
1851     hres = p->u.HTTP_Response;
1852     if (!strcmp(hreq->version, "1.0")) 
1853     {
1854         const char *v = z_HTTP_header_lookup(hreq->headers, "Connection");
1855         if (v && !strcmp(v, "Keep-Alive"))
1856             keepalive = 1;
1857         else
1858             keepalive = 0;
1859         hres->version = "1.0";
1860     }
1861     else
1862     {
1863         const char *v = z_HTTP_header_lookup(hreq->headers, "Connection");
1864         if (v && !strcmp(v, "close"))
1865             keepalive = 0;
1866         else
1867             keepalive = 1;
1868         hres->version = "1.1";
1869     }
1870     if (!keepalive)
1871     {
1872         z_HTTP_header_add(o, &hres->headers, "Connection", "close");
1873         assoc->state = ASSOC_DEAD;
1874         assoc->cs_get_mask = 0;
1875     }
1876     else
1877     {
1878         int t;
1879         const char *alive = z_HTTP_header_lookup(hreq->headers, "Keep-Alive");
1880
1881         if (alive && isdigit(*(const unsigned char *) alive))
1882             t = atoi(alive);
1883         else
1884             t = 15;
1885         if (t < 0 || t > 3600)
1886             t = 3600;
1887         iochan_settimeout(assoc->client_chan,t);
1888         z_HTTP_header_add(o, &hres->headers, "Connection", "Keep-Alive");
1889     }
1890     process_gdu_response(assoc, req, p);
1891 }
1892
1893 static void process_gdu_request(association *assoc, request *req)
1894 {
1895     if (req->gdu_request->which == Z_GDU_Z3950)
1896     {
1897         char *msg = 0;
1898         req->apdu_request = req->gdu_request->u.z3950;
1899         if (process_z_request(assoc, req, &msg) < 0)
1900             do_close_req(assoc, Z_Close_systemProblem, msg, req);
1901     }
1902     else if (req->gdu_request->which == Z_GDU_HTTP_Request)
1903         process_http_request(assoc, req);
1904     else
1905     {
1906         do_close_req(assoc, Z_Close_systemProblem, "bad protocol packet", req);
1907     }
1908 }
1909
1910 /*
1911  * Initiate request processing.
1912  */
1913 static int process_z_request(association *assoc, request *req, char **msg)
1914 {
1915     int fd = -1;
1916     Z_APDU *res;
1917     int retval;
1918     
1919     *msg = "Unknown Error";
1920     assert(req && req->state == REQUEST_IDLE);
1921     if (req->apdu_request->which != Z_APDU_initRequest && !assoc->init)
1922     {
1923         *msg = "Missing InitRequest";
1924         return -1;
1925     }
1926     switch (req->apdu_request->which)
1927     {
1928     case Z_APDU_initRequest:
1929         res = process_initRequest(assoc, req); break;
1930     case Z_APDU_searchRequest:
1931         res = process_searchRequest(assoc, req, &fd); break;
1932     case Z_APDU_presentRequest:
1933         res = process_presentRequest(assoc, req, &fd); break;
1934     case Z_APDU_scanRequest:
1935         if (assoc->init->bend_scan)
1936             res = process_scanRequest(assoc, req, &fd);
1937         else
1938         {
1939             *msg = "Cannot handle Scan APDU";
1940             return -1;
1941         }
1942         break;
1943     case Z_APDU_extendedServicesRequest:
1944         if (assoc->init->bend_esrequest)
1945             res = process_ESRequest(assoc, req, &fd);
1946         else
1947         {
1948             *msg = "Cannot handle Extended Services APDU";
1949             return -1;
1950         }
1951         break;
1952     case Z_APDU_sortRequest:
1953         if (assoc->init->bend_sort)
1954             res = process_sortRequest(assoc, req, &fd);
1955         else
1956         {
1957             *msg = "Cannot handle Sort APDU";
1958             return -1;
1959         }
1960         break;
1961     case Z_APDU_close:
1962         process_close(assoc, req);
1963         return 0;
1964     case Z_APDU_deleteResultSetRequest:
1965         if (assoc->init->bend_delete)
1966             res = process_deleteRequest(assoc, req, &fd);
1967         else
1968         {
1969             *msg = "Cannot handle Delete APDU";
1970             return -1;
1971         }
1972         break;
1973     case Z_APDU_segmentRequest:
1974         if (assoc->init->bend_segment)
1975         {
1976             res = process_segmentRequest (assoc, req);
1977         }
1978         else
1979         {
1980             *msg = "Cannot handle Segment APDU";
1981             return -1;
1982         }
1983         break;
1984     case Z_APDU_triggerResourceControlRequest:
1985         return 0;
1986     default:
1987         *msg = "Bad APDU received";
1988         return -1;
1989     }
1990     if (res)
1991     {
1992         yaz_log(YLOG_DEBUG, "  result immediately available");
1993         retval = process_z_response(assoc, req, res);
1994     }
1995     else if (fd < 0)
1996     {
1997         yaz_log(YLOG_DEBUG, "  result unavailble");
1998         retval = 0;
1999     }
2000     else /* no result yet - one will be provided later */
2001     {
2002         IOCHAN chan;
2003
2004         /* Set up an I/O handler for the fd supplied by the backend */
2005
2006         yaz_log(YLOG_DEBUG, "   establishing handler for result");
2007         req->state = REQUEST_PENDING;
2008         if (!(chan = iochan_create(fd, backend_response, EVENT_INPUT, 0)))
2009             abort();
2010         iochan_setdata(chan, assoc);
2011         retval = 0;
2012     }
2013     return retval;
2014 }
2015
2016 /*
2017  * Handle message from the backend.
2018  */
2019 void backend_response(IOCHAN i, int event)
2020 {
2021     association *assoc = (association *)iochan_getdata(i);
2022     request *req = request_head(&assoc->incoming);
2023     Z_APDU *res;
2024     int fd;
2025
2026     yaz_log(YLOG_DEBUG, "backend_response");
2027     assert(assoc && req && req->state != REQUEST_IDLE);
2028     /* determine what it is we're waiting for */
2029     switch (req->apdu_request->which)
2030     {
2031         case Z_APDU_searchRequest:
2032             res = response_searchRequest(assoc, req, 0, &fd); break;
2033 #if 0
2034         case Z_APDU_presentRequest:
2035             res = response_presentRequest(assoc, req, 0, &fd); break;
2036         case Z_APDU_scanRequest:
2037             res = response_scanRequest(assoc, req, 0, &fd); break;
2038 #endif
2039         default:
2040             yaz_log(YLOG_FATAL, "Serious programmer's lapse or bug");
2041             abort();
2042     }
2043     if ((res && process_z_response(assoc, req, res) < 0) || fd < 0)
2044     {
2045         yaz_log(YLOG_WARN, "Fatal error when talking to backend");
2046         do_close(assoc, Z_Close_systemProblem, 0);
2047         iochan_destroy(i);
2048         return;
2049     }
2050     else if (!res) /* no result yet - try again later */
2051     {
2052         yaz_log(YLOG_DEBUG, "   no result yet");
2053         iochan_setfd(i, fd); /* in case fd has changed */
2054     }
2055 }
2056
2057 /*
2058  * Encode response, and transfer the request structure to the outgoing queue.
2059  */
2060 static int process_gdu_response(association *assoc, request *req, Z_GDU *res)
2061 {
2062     odr_setbuf(assoc->encode, req->response, req->size_response, 1);
2063
2064     if (assoc->print)
2065     {
2066         if (!z_GDU(assoc->print, &res, 0, 0))
2067             yaz_log(YLOG_WARN, "ODR print error: %s", 
2068                 odr_errmsg(odr_geterror(assoc->print)));
2069         odr_reset(assoc->print);
2070     }
2071     if (!z_GDU(assoc->encode, &res, 0, 0))
2072     {
2073         yaz_log(YLOG_WARN, "ODR error when encoding PDU: %s [element %s]",
2074                 odr_errmsg(odr_geterror(assoc->decode)),
2075                 odr_getelement(assoc->decode));
2076         return -1;
2077     }
2078     req->response = odr_getbuf(assoc->encode, &req->len_response,
2079         &req->size_response);
2080     odr_setbuf(assoc->encode, 0, 0, 0); /* don'txfree if we abort later */
2081     odr_reset(assoc->encode);
2082     req->state = REQUEST_IDLE;
2083     request_enq(&assoc->outgoing, req);
2084     /* turn the work over to the ir_session handler */
2085     iochan_setflag(assoc->client_chan, EVENT_OUTPUT);
2086     assoc->cs_put_mask = EVENT_OUTPUT;
2087     /* Is there more work to be done? give that to the input handler too */
2088 #if 1
2089     if (request_head(&assoc->incoming))
2090     {
2091         yaz_log (YLOG_DEBUG, "more work to be done");
2092         iochan_setevent(assoc->client_chan, EVENT_WORK);
2093     }
2094 #endif
2095     return 0;
2096 }
2097
2098 /*
2099  * Encode response, and transfer the request structure to the outgoing queue.
2100  */
2101 static int process_z_response(association *assoc, request *req, Z_APDU *res)
2102 {
2103     Z_GDU *gres = (Z_GDU *) odr_malloc(assoc->encode, sizeof(*res));
2104     gres->which = Z_GDU_Z3950;
2105     gres->u.z3950 = res;
2106
2107     return process_gdu_response(assoc, req, gres);
2108 }
2109
2110 static char *get_vhost(Z_OtherInformation *otherInfo)
2111 {
2112     return yaz_oi_get_string_oidval(&otherInfo, VAL_PROXY, 1, 0);
2113 }
2114
2115 /*
2116  * Handle init request.
2117  * At the moment, we don't check the options
2118  * anywhere else in the code - we just try not to do anything that would
2119  * break a naive client. We'll toss 'em into the association block when
2120  * we need them there.
2121  */
2122 static Z_APDU *process_initRequest(association *assoc, request *reqb)
2123 {
2124     Z_InitRequest *req = reqb->apdu_request->u.initRequest;
2125     Z_APDU *apdu = zget_APDU(assoc->encode, Z_APDU_initResponse);
2126     Z_InitResponse *resp = apdu->u.initResponse;
2127     bend_initresult *binitres;
2128     char *version;
2129     char options[140];
2130     statserv_options_block *cb = 0;  /* by default no control for backend */
2131
2132     if (control_association(assoc, get_vhost(req->otherInfo), 1))
2133         cb = statserv_getcontrol();  /* got control block for backend */
2134
2135     if (cb && assoc->backend)
2136         (*cb->bend_close)(assoc->backend);
2137
2138     yaz_log(log_requestdetail, "Got initRequest");
2139     if (req->implementationId)
2140         yaz_log(log_requestdetail, "Id:        %s",
2141                 req->implementationId);
2142     if (req->implementationName)
2143         yaz_log(log_requestdetail, "Name:      %s",
2144                 req->implementationName);
2145     if (req->implementationVersion)
2146         yaz_log(log_requestdetail, "Version:   %s",
2147                 req->implementationVersion);
2148     
2149     assoc_init_reset(assoc);
2150
2151     assoc->init->auth = req->idAuthentication;
2152     assoc->init->referenceId = req->referenceId;
2153
2154     if (ODR_MASK_GET(req->options, Z_Options_negotiationModel))
2155     {
2156         Z_CharSetandLanguageNegotiation *negotiation =
2157             yaz_get_charneg_record (req->otherInfo);
2158         if (negotiation &&
2159             negotiation->which == Z_CharSetandLanguageNegotiation_proposal)
2160             assoc->init->charneg_request = negotiation;
2161     }
2162
2163     assoc->backend = 0;
2164     if (cb)
2165     {
2166         if (req->implementationVersion)
2167             yaz_log(log_requestdetail, "Config:    %s",
2168                     cb->configname);
2169     
2170         iochan_settimeout(assoc->client_chan, cb->idle_timeout * 60);
2171         
2172         /* we have a backend control block, so call that init function */
2173         if (!(binitres = (*cb->bend_init)(assoc->init)))
2174         {
2175             yaz_log(YLOG_WARN, "Bad response from backend.");
2176             return 0;
2177         }
2178         assoc->backend = binitres->handle;
2179     }
2180     else
2181     {
2182         /* no backend. return error */
2183         binitres = odr_malloc(assoc->encode, sizeof(*binitres));
2184         binitres->errstring = 0;
2185         binitres->errcode = YAZ_BIB1_PERMANENT_SYSTEM_ERROR;
2186         iochan_settimeout(assoc->client_chan, 10);
2187     }
2188     if ((assoc->init->bend_sort))
2189         yaz_log (YLOG_DEBUG, "Sort handler installed");
2190     if ((assoc->init->bend_search))
2191         yaz_log (YLOG_DEBUG, "Search handler installed");
2192     if ((assoc->init->bend_present))
2193         yaz_log (YLOG_DEBUG, "Present handler installed");   
2194     if ((assoc->init->bend_esrequest))
2195         yaz_log (YLOG_DEBUG, "ESRequest handler installed");   
2196     if ((assoc->init->bend_delete))
2197         yaz_log (YLOG_DEBUG, "Delete handler installed");   
2198     if ((assoc->init->bend_scan))
2199         yaz_log (YLOG_DEBUG, "Scan handler installed");   
2200     if ((assoc->init->bend_segment))
2201         yaz_log (YLOG_DEBUG, "Segment handler installed");   
2202     
2203     resp->referenceId = req->referenceId;
2204     *options = '\0';
2205     /* let's tell the client what we can do */
2206     if (ODR_MASK_GET(req->options, Z_Options_search))
2207     {
2208         ODR_MASK_SET(resp->options, Z_Options_search);
2209         strcat(options, "srch");
2210     }
2211     if (ODR_MASK_GET(req->options, Z_Options_present))
2212     {
2213         ODR_MASK_SET(resp->options, Z_Options_present);
2214         strcat(options, " prst");
2215     }
2216     if (ODR_MASK_GET(req->options, Z_Options_delSet) &&
2217         assoc->init->bend_delete)
2218     {
2219         ODR_MASK_SET(resp->options, Z_Options_delSet);
2220         strcat(options, " del");
2221     }
2222     if (ODR_MASK_GET(req->options, Z_Options_extendedServices) &&
2223         assoc->init->bend_esrequest)
2224     {
2225         ODR_MASK_SET(resp->options, Z_Options_extendedServices);
2226         strcat (options, " extendedServices");
2227     }
2228     if (ODR_MASK_GET(req->options, Z_Options_namedResultSets))
2229     {
2230         ODR_MASK_SET(resp->options, Z_Options_namedResultSets);
2231         strcat(options, " namedresults");
2232     }
2233     if (ODR_MASK_GET(req->options, Z_Options_scan) && assoc->init->bend_scan)
2234     {
2235         ODR_MASK_SET(resp->options, Z_Options_scan);
2236         strcat(options, " scan");
2237     }
2238     if (ODR_MASK_GET(req->options, Z_Options_concurrentOperations))
2239     {
2240         ODR_MASK_SET(resp->options, Z_Options_concurrentOperations);
2241         strcat(options, " concurrop");
2242     }
2243     if (ODR_MASK_GET(req->options, Z_Options_sort) && assoc->init->bend_sort)
2244     {
2245         ODR_MASK_SET(resp->options, Z_Options_sort);
2246         strcat(options, " sort");
2247     }
2248
2249     if (ODR_MASK_GET(req->options, Z_Options_negotiationModel)
2250         && assoc->init->charneg_response)
2251     {
2252         Z_OtherInformation **p;
2253         Z_OtherInformationUnit *p0;
2254         
2255         yaz_oi_APDU(apdu, &p);
2256         
2257         if ((p0=yaz_oi_update(p, assoc->encode, NULL, 0, 0))) {
2258             ODR_MASK_SET(resp->options, Z_Options_negotiationModel);
2259             
2260             p0->which = Z_OtherInfo_externallyDefinedInfo;
2261             p0->information.externallyDefinedInfo =
2262                 assoc->init->charneg_response;
2263         }
2264         ODR_MASK_SET(resp->options, Z_Options_negotiationModel);
2265         strcat(options, " negotiation");
2266     }
2267         
2268     if (ODR_MASK_GET(req->options, Z_Options_triggerResourceCtrl))
2269         ODR_MASK_SET(resp->options, Z_Options_triggerResourceCtrl);
2270
2271     if (ODR_MASK_GET(req->protocolVersion, Z_ProtocolVersion_1))
2272     {
2273         ODR_MASK_SET(resp->protocolVersion, Z_ProtocolVersion_1);
2274         assoc->version = 1; /* 1 & 2 are equivalent */
2275     }
2276     if (ODR_MASK_GET(req->protocolVersion, Z_ProtocolVersion_2))
2277     {
2278         ODR_MASK_SET(resp->protocolVersion, Z_ProtocolVersion_2);
2279         assoc->version = 2;
2280     }
2281     if (ODR_MASK_GET(req->protocolVersion, Z_ProtocolVersion_3))
2282     {
2283         ODR_MASK_SET(resp->protocolVersion, Z_ProtocolVersion_3);
2284         assoc->version = 3;
2285     }
2286
2287     yaz_log(log_requestdetail, "Negotiated to v%d: %s", assoc->version, options);
2288     assoc->maximumRecordSize = *req->maximumRecordSize;
2289
2290     if (cb && assoc->maximumRecordSize > cb->maxrecordsize)
2291         assoc->maximumRecordSize = cb->maxrecordsize;
2292     assoc->preferredMessageSize = *req->preferredMessageSize;
2293     if (assoc->preferredMessageSize > assoc->maximumRecordSize)
2294         assoc->preferredMessageSize = assoc->maximumRecordSize;
2295
2296     resp->preferredMessageSize = &assoc->preferredMessageSize;
2297     resp->maximumRecordSize = &assoc->maximumRecordSize;
2298
2299     resp->implementationId = odr_prepend(assoc->encode,
2300                 assoc->init->implementation_id,
2301                 resp->implementationId);
2302
2303     resp->implementationName = odr_prepend(assoc->encode,
2304                 assoc->init->implementation_name,
2305                 odr_prepend(assoc->encode, "GFS", resp->implementationName));
2306
2307     version = odr_strdup(assoc->encode, "$Revision: 1.92 $");
2308     if (strlen(version) > 10)   /* check for unexpanded CVS strings */
2309         version[strlen(version)-2] = '\0';
2310     resp->implementationVersion = odr_prepend(assoc->encode,
2311                 assoc->init->implementation_version,
2312                 odr_prepend(assoc->encode, &version[11],
2313                             resp->implementationVersion));
2314
2315     if (binitres->errcode)
2316     {
2317         assoc->state = ASSOC_DEAD;
2318         resp->userInformationField =
2319             init_diagnostics(assoc->encode, binitres->errcode,
2320                              binitres->errstring);
2321         *resp->result = 0;
2322     }
2323     if (log_request)
2324     {
2325         if (!req->idAuthentication)
2326             yaz_log(log_request, "Auth none");
2327         else if (req->idAuthentication->which == Z_IdAuthentication_open)
2328         {
2329             const char *open = req->idAuthentication->u.open;
2330             const char *slash = strchr(open, '/');
2331             int len;
2332             if (slash)
2333                 len = slash - open;
2334             else
2335                 len = strlen(open);
2336                 yaz_log(log_request, "Auth open %.*s", len, open);
2337         }
2338         else if (req->idAuthentication->which == Z_IdAuthentication_idPass)
2339         {
2340             const char *user = req->idAuthentication->u.idPass->userId;
2341             const char *group = req->idAuthentication->u.idPass->groupId;
2342             yaz_log(log_request, "Auth idPass %s %s",
2343                     user ? user : "-", group ? group : "-");
2344         }
2345         else if (req->idAuthentication->which 
2346                  == Z_IdAuthentication_anonymous)
2347         {
2348             yaz_log(log_request, "Auth anonymous");
2349         }
2350         else
2351         {
2352             yaz_log(log_request, "Auth other");
2353         }
2354     }
2355     if (log_request)
2356     {
2357         WRBUF wr = wrbuf_alloc();
2358         wrbuf_printf(wr, "Init ");
2359         if (binitres->errcode)
2360             wrbuf_printf(wr, "ERROR %d", binitres->errcode);
2361         else
2362             wrbuf_printf(wr, "OK -");
2363         wrbuf_printf(wr, " ID:%s Name:%s Version:%s",
2364                      (req->implementationId ? req->implementationId :"-"), 
2365                      (req->implementationName ?
2366                       req->implementationName : "-"),
2367                      (req->implementationVersion ?
2368                       req->implementationVersion : "-")
2369             );
2370         yaz_log(log_request, "%s", wrbuf_buf(wr));
2371         wrbuf_free(wr, 1);
2372     }
2373     return apdu;
2374 }
2375
2376 /*
2377  * Set the specified `errcode' and `errstring' into a UserInfo-1
2378  * external to be returned to the client in accordance with Z35.90
2379  * Implementor Agreement 5 (Returning diagnostics in an InitResponse):
2380  *      http://lcweb.loc.gov/z3950/agency/agree/initdiag.html
2381  */
2382 static Z_External *init_diagnostics(ODR odr, int error, const char *addinfo)
2383 {
2384     yaz_log(log_requestdetail, "[%d] %s%s%s", error, diagbib1_str(error),
2385         addinfo ? " -- " : "", addinfo ? addinfo : "");
2386     return zget_init_diagnostics(odr, error, addinfo);
2387 }
2388
2389 /*
2390  * nonsurrogate diagnostic record.
2391  */
2392 static Z_Records *diagrec(association *assoc, int error, char *addinfo)
2393 {
2394     Z_Records *rec = (Z_Records *) odr_malloc (assoc->encode, sizeof(*rec));
2395
2396     yaz_log(log_requestdetail, "[%d] %s%s%s", error, diagbib1_str(error),
2397             addinfo ? " -- " : "", addinfo ? addinfo : "");
2398
2399     rec->which = Z_Records_NSD;
2400     rec->u.nonSurrogateDiagnostic = zget_DefaultDiagFormat(assoc->encode,
2401                                                            error, addinfo);
2402     return rec;
2403 }
2404
2405 /*
2406  * surrogate diagnostic.
2407  */
2408 static Z_NamePlusRecord *surrogatediagrec(association *assoc, 
2409                                           const char *dbname,
2410                                           int error, const char *addinfo)
2411 {
2412     yaz_log(log_requestdetail, "[%d] %s%s%s", error, diagbib1_str(error),
2413             addinfo ? " -- " : "", addinfo ? addinfo : "");
2414     return zget_surrogateDiagRec(assoc->encode, dbname, error, addinfo);
2415 }
2416
2417 static Z_Records *pack_records(association *a, char *setname, int start,
2418                                int *num, Z_RecordComposition *comp,
2419                                int *next, int *pres, oid_value format,
2420                                Z_ReferenceId *referenceId,
2421                                int *oid, int *errcode)
2422 {
2423     int recno, total_length = 0, toget = *num, dumped_records = 0;
2424     Z_Records *records =
2425         (Z_Records *) odr_malloc (a->encode, sizeof(*records));
2426     Z_NamePlusRecordList *reclist =
2427         (Z_NamePlusRecordList *) odr_malloc (a->encode, sizeof(*reclist));
2428     Z_NamePlusRecord **list =
2429         (Z_NamePlusRecord **) odr_malloc (a->encode, sizeof(*list) * toget);
2430
2431     records->which = Z_Records_DBOSD;
2432     records->u.databaseOrSurDiagnostics = reclist;
2433     reclist->num_records = 0;
2434     reclist->records = list;
2435     *pres = Z_PresentStatus_success;
2436     *num = 0;
2437     *next = 0;
2438
2439     yaz_log(log_requestdetail, "Request to pack %d+%d %s", start, toget, setname);
2440     yaz_log(log_requestdetail, "pms=%d, mrs=%d", a->preferredMessageSize,
2441         a->maximumRecordSize);
2442     for (recno = start; reclist->num_records < toget; recno++)
2443     {
2444         bend_fetch_rr freq;
2445         Z_NamePlusRecord *thisrec;
2446         int this_length = 0;
2447         /*
2448          * we get the number of bytes allocated on the stream before any
2449          * allocation done by the backend - this should give us a reasonable
2450          * idea of the total size of the data so far.
2451          */
2452         total_length = odr_total(a->encode) - dumped_records;
2453         freq.errcode = 0;
2454         freq.errstring = 0;
2455         freq.basename = 0;
2456         freq.len = 0;
2457         freq.record = 0;
2458         freq.last_in_set = 0;
2459         freq.setname = setname;
2460         freq.surrogate_flag = 0;
2461         freq.number = recno;
2462         freq.comp = comp;
2463         freq.request_format = format;
2464         freq.request_format_raw = oid;
2465         freq.output_format = format;
2466         freq.output_format_raw = 0;
2467         freq.stream = a->encode;
2468         freq.print = a->print;
2469         freq.referenceId = referenceId;
2470         freq.schema = 0;
2471
2472         retrieve_fetch(a, &freq);
2473
2474         *next = freq.last_in_set ? 0 : recno + 1;
2475
2476         /* backend should be able to signal whether error is system-wide
2477            or only pertaining to current record */
2478         if (freq.errcode)
2479         {
2480             if (!freq.surrogate_flag)
2481             {
2482                 char s[20];
2483                 *pres = Z_PresentStatus_failure;
2484                 /* for 'present request out of range',
2485                    set addinfo to record position if not set */
2486                 if (freq.errcode == YAZ_BIB1_PRESENT_REQUEST_OUT_OF_RANGE  && 
2487                                 freq.errstring == 0)
2488                 {
2489                     sprintf (s, "%d", recno);
2490                     freq.errstring = s;
2491                 }
2492                 if (errcode)
2493                     *errcode = freq.errcode;
2494                 return diagrec(a, freq.errcode, freq.errstring);
2495             }
2496             reclist->records[reclist->num_records] =
2497                 surrogatediagrec(a, freq.basename, freq.errcode,
2498                                  freq.errstring);
2499             reclist->num_records++;
2500             continue;
2501         }
2502         if (freq.record == 0)  /* no error and no record ? */
2503         {
2504             *next = 0;   /* signal end-of-set and stop */
2505             break;
2506         }
2507         if (freq.len >= 0)
2508             this_length = freq.len;
2509         else
2510             this_length = odr_total(a->encode) - total_length - dumped_records;
2511         yaz_log(YLOG_DEBUG, "  fetched record, len=%d, total=%d dumped=%d",
2512             this_length, total_length, dumped_records);
2513         if (a->preferredMessageSize > 0 &&
2514                 this_length + total_length > a->preferredMessageSize)
2515         {
2516             /* record is small enough, really */
2517             if (this_length <= a->preferredMessageSize && recno > start)
2518             {
2519                 yaz_log(log_requestdetail, "  Dropped last normal-sized record");
2520                 *pres = Z_PresentStatus_partial_2;
2521                 break;
2522             }
2523             /* record can only be fetched by itself */
2524             if (this_length < a->maximumRecordSize)
2525             {
2526                 yaz_log(log_requestdetail, "  Record > prefmsgsz");
2527                 if (toget > 1)
2528                 {
2529                     yaz_log(YLOG_DEBUG, "  Dropped it");
2530                     reclist->records[reclist->num_records] =
2531                          surrogatediagrec(a, freq.basename, 16, 0);
2532                     reclist->num_records++;
2533                     dumped_records += this_length;
2534                     continue;
2535                 }
2536             }
2537             else /* too big entirely */
2538             {
2539                 yaz_log(log_requestdetail, "Record > maxrcdsz this=%d max=%d",
2540                         this_length, a->maximumRecordSize);
2541                 reclist->records[reclist->num_records] =
2542                     surrogatediagrec(a, freq.basename, 17, 0);
2543                 reclist->num_records++;
2544                 dumped_records += this_length;
2545                 continue;
2546             }
2547         }
2548
2549         if (!(thisrec = (Z_NamePlusRecord *)
2550               odr_malloc(a->encode, sizeof(*thisrec))))
2551             return 0;
2552         if (freq.basename)
2553             thisrec->databaseName = odr_strdup(a->encode, freq.basename);
2554         else
2555             thisrec->databaseName = 0;
2556         thisrec->which = Z_NamePlusRecord_databaseRecord;
2557
2558         if (freq.output_format_raw)
2559         {
2560             struct oident *ident = oid_getentbyoid(freq.output_format_raw);
2561             freq.output_format = ident->value;
2562         }
2563         thisrec->u.databaseRecord = z_ext_record(a->encode, freq.output_format,
2564                                                  freq.record, freq.len);
2565         if (!thisrec->u.databaseRecord)
2566             return 0;
2567         reclist->records[reclist->num_records] = thisrec;
2568         reclist->num_records++;
2569     }
2570     *num = reclist->num_records;
2571     return records;
2572 }
2573
2574 static Z_APDU *process_searchRequest(association *assoc, request *reqb,
2575     int *fd)
2576 {
2577     Z_SearchRequest *req = reqb->apdu_request->u.searchRequest;
2578     bend_search_rr *bsrr = 
2579         (bend_search_rr *)nmem_malloc (reqb->request_mem, sizeof(*bsrr));
2580     
2581     yaz_log(log_requestdetail, "Got SearchRequest.");
2582     bsrr->fd = fd;
2583     bsrr->request = reqb;
2584     bsrr->association = assoc;
2585     bsrr->referenceId = req->referenceId;
2586     save_referenceId (reqb, bsrr->referenceId);
2587     bsrr->srw_sortKeys = 0;
2588     bsrr->srw_setname = 0;
2589     bsrr->srw_setnameIdleTime = 0;
2590
2591     yaz_log (log_requestdetail, "ResultSet '%s'", req->resultSetName);
2592     if (req->databaseNames)
2593     {
2594         int i;
2595         for (i = 0; i < req->num_databaseNames; i++)
2596             yaz_log (log_requestdetail, "Database '%s'", req->databaseNames[i]);
2597     }
2598
2599     yaz_log_zquery_level(log_requestdetail,req->query);
2600
2601     if (assoc->init->bend_search)
2602     {
2603         bsrr->setname = req->resultSetName;
2604         bsrr->replace_set = *req->replaceIndicator;
2605         bsrr->num_bases = req->num_databaseNames;
2606         bsrr->basenames = req->databaseNames;
2607         bsrr->query = req->query;
2608         bsrr->stream = assoc->encode;
2609         nmem_transfer(bsrr->stream->mem, reqb->request_mem);
2610         bsrr->decode = assoc->decode;
2611         bsrr->print = assoc->print;
2612         bsrr->hits = 0;
2613         bsrr->errcode = 0;
2614         bsrr->errstring = NULL;
2615         bsrr->search_info = NULL;
2616
2617         if (assoc->server && assoc->server->cql_transform 
2618             && req->query->which == Z_Query_type_104
2619             && req->query->u.type_104->which == Z_External_CQL)
2620         {
2621             /* have a CQL query and a CQL to PQF transform .. */
2622             int srw_errcode = 
2623                 cql2pqf(bsrr->stream, req->query->u.type_104->u.cql,
2624                         assoc->server->cql_transform, bsrr->query);
2625             if (srw_errcode)
2626                 bsrr->errcode = yaz_diag_srw_to_bib1(srw_errcode);
2627         }
2628         if (!bsrr->errcode)
2629             (assoc->init->bend_search)(assoc->backend, bsrr);
2630         if (!bsrr->request)  /* backend not ready with the search response */
2631             return 0;  /* should not be used any more */
2632     }
2633     else
2634     { 
2635         /* FIXME - make a diagnostic for it */
2636         yaz_log(YLOG_WARN,"Search not supported ?!?!");
2637     }
2638     return response_searchRequest(assoc, reqb, bsrr, fd);
2639 }
2640
2641 int bend_searchresponse(void *handle, bend_search_rr *bsrr) {return 0;}
2642
2643 /*
2644  * Prepare a searchresponse based on the backend results. We probably want
2645  * to look at making the fetching of records nonblocking as well, but
2646  * so far, we'll keep things simple.
2647  * If bsrt is null, that means we're called in response to a communications
2648  * event, and we'll have to get the response for ourselves.
2649  */
2650 static Z_APDU *response_searchRequest(association *assoc, request *reqb,
2651     bend_search_rr *bsrt, int *fd)
2652 {
2653     Z_SearchRequest *req = reqb->apdu_request->u.searchRequest;
2654     Z_APDU *apdu = (Z_APDU *)odr_malloc (assoc->encode, sizeof(*apdu));
2655     Z_SearchResponse *resp = (Z_SearchResponse *)
2656         odr_malloc (assoc->encode, sizeof(*resp));
2657     int *nulint = odr_intdup (assoc->encode, 0);
2658     bool_t *sr = odr_intdup(assoc->encode, 1);
2659     int *next = odr_intdup(assoc->encode, 0);
2660     int *none = odr_intdup(assoc->encode, Z_SearchResponse_none);
2661     int returnedrecs=0;
2662
2663     apdu->which = Z_APDU_searchResponse;
2664     apdu->u.searchResponse = resp;
2665     resp->referenceId = req->referenceId;
2666     resp->additionalSearchInfo = 0;
2667     resp->otherInfo = 0;
2668     *fd = -1;
2669     if (!bsrt && !bend_searchresponse(assoc->backend, bsrt))
2670     {
2671         yaz_log(YLOG_FATAL, "Bad result from backend");
2672         return 0;
2673     }
2674     else if (bsrt->errcode)
2675     {
2676         resp->records = diagrec(assoc, bsrt->errcode, bsrt->errstring);
2677         resp->resultCount = nulint;
2678         resp->numberOfRecordsReturned = nulint;
2679         resp->nextResultSetPosition = nulint;
2680         resp->searchStatus = nulint;
2681         resp->resultSetStatus = none;
2682         resp->presentStatus = 0;
2683     }
2684     else
2685     {
2686         int *toget = odr_intdup(assoc->encode, 0);
2687         int *presst = odr_intdup(assoc->encode, 0);
2688         Z_RecordComposition comp, *compp = 0;
2689
2690         yaz_log (log_requestdetail, "resultCount: %d", bsrt->hits);
2691
2692         resp->records = 0;
2693         resp->resultCount = &bsrt->hits;
2694
2695         comp.which = Z_RecordComp_simple;
2696         /* how many records does the user agent want, then? */
2697         if (bsrt->hits <= *req->smallSetUpperBound)
2698         {
2699             *toget = bsrt->hits;
2700             if ((comp.u.simple = req->smallSetElementSetNames))
2701                 compp = &comp;
2702         }
2703         else if (bsrt->hits < *req->largeSetLowerBound)
2704         {
2705             *toget = *req->mediumSetPresentNumber;
2706             if (*toget > bsrt->hits)
2707                 *toget = bsrt->hits;
2708             if ((comp.u.simple = req->mediumSetElementSetNames))
2709                 compp = &comp;
2710         }
2711         else
2712             *toget = 0;
2713
2714         if (*toget && !resp->records)
2715         {
2716             oident *prefformat;
2717             oid_value form;
2718
2719             if (!(prefformat = oid_getentbyoid(req->preferredRecordSyntax)))
2720                 form = VAL_NONE;
2721             else
2722                 form = prefformat->value;
2723
2724             /* Call bend_present if defined */
2725             if (assoc->init->bend_present)
2726             {
2727                 bend_present_rr *bprr = (bend_present_rr *)
2728                     nmem_malloc (reqb->request_mem, sizeof(*bprr));
2729                 bprr->setname = req->resultSetName;
2730                 bprr->start = 1;
2731                 bprr->number = *toget;
2732                 bprr->format = form;
2733                 bprr->comp = compp;
2734                 bprr->referenceId = req->referenceId;
2735                 bprr->stream = assoc->encode;
2736                 bprr->print = assoc->print;
2737                 bprr->request = reqb;
2738                 bprr->association = assoc;
2739                 bprr->errcode = 0;
2740                 bprr->errstring = NULL;
2741                 (*assoc->init->bend_present)(assoc->backend, bprr);
2742
2743                 if (!bprr->request)
2744                     return 0;
2745                 if (bprr->errcode)
2746                 {
2747                     resp->records = diagrec(assoc, bprr->errcode, bprr->errstring);
2748                     *resp->presentStatus = Z_PresentStatus_failure;
2749                 }
2750             }
2751
2752             if (!resp->records)
2753                 resp->records = pack_records(assoc, req->resultSetName, 1,
2754                                              toget, compp, next, presst, form, req->referenceId,
2755                                              req->preferredRecordSyntax, NULL);
2756             if (!resp->records)
2757                 return 0;
2758             resp->numberOfRecordsReturned = toget;
2759             returnedrecs = *toget;
2760             resp->nextResultSetPosition = next;
2761             resp->searchStatus = sr;
2762             resp->resultSetStatus = 0;
2763             resp->presentStatus = presst;
2764         }
2765         else
2766         {
2767             if (*resp->resultCount)
2768                 *next = 1;
2769             resp->numberOfRecordsReturned = nulint;
2770             resp->nextResultSetPosition = next;
2771             resp->searchStatus = sr;
2772             resp->resultSetStatus = 0;
2773             resp->presentStatus = 0;
2774         }
2775     }
2776     resp->additionalSearchInfo = bsrt->search_info;
2777
2778     if (log_request)
2779     {
2780         int i;
2781         WRBUF wr = wrbuf_alloc();
2782
2783         for (i = 0 ; i < req->num_databaseNames; i++){
2784             if (i)
2785                 wrbuf_printf(wr, "+");
2786             wrbuf_printf(wr, req->databaseNames[i]);
2787         }
2788         wrbuf_printf(wr, " ");
2789         
2790         if (bsrt->errcode)
2791             wrbuf_printf(wr, "ERROR %d", bsrt->errcode);
2792         else
2793             wrbuf_printf(wr, "OK %d", bsrt->hits);
2794         wrbuf_printf(wr, " %s 1+%d ",
2795                      req->resultSetName, returnedrecs);
2796         yaz_query_to_wrbuf(wr, req->query);
2797         
2798         yaz_log(log_request, "Search %s", wrbuf_buf(wr));
2799         wrbuf_free(wr, 1);
2800     }
2801     return apdu;
2802 }
2803
2804 /*
2805  * Maybe we got a little over-friendly when we designed bend_fetch to
2806  * get only one record at a time. Some backends can optimise multiple-record
2807  * fetches, and at any rate, there is some overhead involved in
2808  * all that selecting and hopping around. Problem is, of course, that the
2809  * frontend can't know ahead of time how many records it'll need to
2810  * fill the negotiated PDU size. Annoying. Segmentation or not, Z/SR
2811  * is downright lousy as a bulk data transfer protocol.
2812  *
2813  * To start with, we'll do the fetching of records from the backend
2814  * in one operation: To save some trips in and out of the event-handler,
2815  * and to simplify the interface to pack_records. At any rate, asynch
2816  * operation is more fun in operations that have an unpredictable execution
2817  * speed - which is normally more true for search than for present.
2818  */
2819 static Z_APDU *process_presentRequest(association *assoc, request *reqb,
2820                                       int *fd)
2821 {
2822     Z_PresentRequest *req = reqb->apdu_request->u.presentRequest;
2823     oident *prefformat;
2824     oid_value form;
2825     Z_APDU *apdu;
2826     Z_PresentResponse *resp;
2827     int *next;
2828     int *num;
2829     int errcode = 0;
2830     const char *errstring = 0;
2831
2832     yaz_log(log_requestdetail, "Got PresentRequest.");
2833
2834     if (!(prefformat = oid_getentbyoid(req->preferredRecordSyntax)))
2835         form = VAL_NONE;
2836     else
2837         form = prefformat->value;
2838     resp = (Z_PresentResponse *)odr_malloc (assoc->encode, sizeof(*resp));
2839     resp->records = 0;
2840     resp->presentStatus = odr_intdup(assoc->encode, 0);
2841     if (assoc->init->bend_present)
2842     {
2843         bend_present_rr *bprr = (bend_present_rr *)
2844             nmem_malloc (reqb->request_mem, sizeof(*bprr));
2845         bprr->setname = req->resultSetId;
2846         bprr->start = *req->resultSetStartPoint;
2847         bprr->number = *req->numberOfRecordsRequested;
2848         bprr->format = form;
2849         bprr->comp = req->recordComposition;
2850         bprr->referenceId = req->referenceId;
2851         bprr->stream = assoc->encode;
2852         bprr->print = assoc->print;
2853         bprr->request = reqb;
2854         bprr->association = assoc;
2855         bprr->errcode = 0;
2856         bprr->errstring = NULL;
2857         (*assoc->init->bend_present)(assoc->backend, bprr);
2858         
2859         if (!bprr->request)
2860             return 0; /* should not happen */
2861         if (bprr->errcode)
2862         {
2863             resp->records = diagrec(assoc, bprr->errcode, bprr->errstring);
2864             *resp->presentStatus = Z_PresentStatus_failure;
2865             errcode = bprr->errcode;
2866             errstring = bprr->errstring;
2867         }
2868     }
2869     apdu = (Z_APDU *)odr_malloc (assoc->encode, sizeof(*apdu));
2870     next = odr_intdup(assoc->encode, 0);
2871     num = odr_intdup(assoc->encode, 0);
2872     
2873     apdu->which = Z_APDU_presentResponse;
2874     apdu->u.presentResponse = resp;
2875     resp->referenceId = req->referenceId;
2876     resp->otherInfo = 0;
2877     
2878     if (!resp->records)
2879     {
2880         *num = *req->numberOfRecordsRequested;
2881         resp->records =
2882             pack_records(assoc, req->resultSetId, *req->resultSetStartPoint,
2883                          num, req->recordComposition, next,
2884                          resp->presentStatus,
2885                          form, req->referenceId, req->preferredRecordSyntax, 
2886                          &errcode);
2887     }
2888     if (log_request)
2889     {
2890         WRBUF wr = wrbuf_alloc();
2891         wrbuf_printf(wr, "Present ");
2892
2893         if (*resp->presentStatus == Z_PresentStatus_failure)
2894             wrbuf_printf(wr, "ERROR %d ", errcode);
2895         else if (*resp->presentStatus == Z_PresentStatus_success)
2896             wrbuf_printf(wr, "OK -  ");
2897         else
2898             wrbuf_printf(wr, "Partial %d - ", *resp->presentStatus);
2899
2900         wrbuf_printf(wr, " %s %d+%d ",
2901                 req->resultSetId, *req->resultSetStartPoint,
2902                 *req->numberOfRecordsRequested);
2903         yaz_log(log_request, "%s", wrbuf_buf(wr) );
2904         wrbuf_free(wr, 1);
2905     }
2906     if (!resp->records)
2907         return 0;
2908     resp->numberOfRecordsReturned = num;
2909     resp->nextResultSetPosition = next;
2910     
2911     return apdu;
2912 }
2913
2914 /*
2915  * Scan was implemented rather in a hurry, and with support for only the basic
2916  * elements of the service in the backend API. Suggestions are welcome.
2917  */
2918 static Z_APDU *process_scanRequest(association *assoc, request *reqb, int *fd)
2919 {
2920     Z_ScanRequest *req = reqb->apdu_request->u.scanRequest;
2921     Z_APDU *apdu = (Z_APDU *)odr_malloc (assoc->encode, sizeof(*apdu));
2922     Z_ScanResponse *res = (Z_ScanResponse *)
2923         odr_malloc (assoc->encode, sizeof(*res));
2924     int *scanStatus = odr_intdup(assoc->encode, Z_Scan_failure);
2925     int *numberOfEntriesReturned = odr_intdup(assoc->encode, 0);
2926     Z_ListEntries *ents = (Z_ListEntries *)
2927         odr_malloc (assoc->encode, sizeof(*ents));
2928     Z_DiagRecs *diagrecs_p = NULL;
2929     oident *attset;
2930     bend_scan_rr *bsrr = (bend_scan_rr *)
2931         odr_malloc (assoc->encode, sizeof(*bsrr));
2932     struct scan_entry *save_entries;
2933
2934     yaz_log(log_requestdetail, "Got ScanRequest");
2935
2936     apdu->which = Z_APDU_scanResponse;
2937     apdu->u.scanResponse = res;
2938     res->referenceId = req->referenceId;
2939
2940     /* if step is absent, set it to 0 */
2941     res->stepSize = odr_intdup(assoc->encode, 0);
2942     if (req->stepSize)
2943         *res->stepSize = *req->stepSize;
2944
2945     res->scanStatus = scanStatus;
2946     res->numberOfEntriesReturned = numberOfEntriesReturned;
2947     res->positionOfTerm = 0;
2948     res->entries = ents;
2949     ents->num_entries = 0;
2950     ents->entries = NULL;
2951     ents->num_nonsurrogateDiagnostics = 0;
2952     ents->nonsurrogateDiagnostics = NULL;
2953     res->attributeSet = 0;
2954     res->otherInfo = 0;
2955
2956     if (req->databaseNames)
2957     {
2958         int i;
2959         for (i = 0; i < req->num_databaseNames; i++)
2960             yaz_log (log_requestdetail, "Database '%s'", req->databaseNames[i]);
2961     }
2962     bsrr->scanClause = 0;
2963     bsrr->errcode = 0;
2964     bsrr->errstring = 0;
2965     bsrr->num_bases = req->num_databaseNames;
2966     bsrr->basenames = req->databaseNames;
2967     bsrr->num_entries = *req->numberOfTermsRequested;
2968     bsrr->term = req->termListAndStartPoint;
2969     bsrr->referenceId = req->referenceId;
2970     bsrr->stream = assoc->encode;
2971     bsrr->print = assoc->print;
2972     bsrr->step_size = res->stepSize;
2973     bsrr->entries = 0;
2974     /* For YAZ 2.0 and earlier it was the backend handler that
2975        initialized entries (member display_term did not exist)
2976        YAZ 2.0 and later sets 'entries'  and initialize all members
2977        including 'display_term'. If YAZ 2.0 or later sees that
2978        entries was modified - we assume that it is an old handler and
2979        that 'display_term' is _not_ set.
2980     */
2981     if (bsrr->num_entries > 0) 
2982     {
2983         int i;
2984         bsrr->entries = odr_malloc(assoc->decode, sizeof(*bsrr->entries) *
2985                                    bsrr->num_entries);
2986         for (i = 0; i<bsrr->num_entries; i++)
2987         {
2988             bsrr->entries[i].term = 0;
2989             bsrr->entries[i].occurrences = 0;
2990             bsrr->entries[i].errcode = 0;
2991             bsrr->entries[i].errstring = 0;
2992             bsrr->entries[i].display_term = 0;
2993         }
2994     }
2995     save_entries = bsrr->entries;  /* save it so we can compare later */
2996
2997     if (req->attributeSet &&
2998         (attset = oid_getentbyoid(req->attributeSet)) &&
2999         (attset->oclass == CLASS_ATTSET || attset->oclass == CLASS_GENERAL))
3000         bsrr->attributeset = attset->value;
3001     else
3002         bsrr->attributeset = VAL_NONE;
3003     log_scan_term_level (log_requestdetail, req->termListAndStartPoint, 
3004             bsrr->attributeset);
3005     bsrr->term_position = req->preferredPositionInResponse ?
3006         *req->preferredPositionInResponse : 1;
3007
3008     ((int (*)(void *, bend_scan_rr *))
3009      (*assoc->init->bend_scan))(assoc->backend, bsrr);
3010
3011     if (bsrr->errcode)
3012         diagrecs_p = zget_DiagRecs(assoc->encode,
3013                                    bsrr->errcode, bsrr->errstring);
3014     else
3015     {
3016         int i;
3017         Z_Entry **tab = (Z_Entry **)
3018             odr_malloc (assoc->encode, sizeof(*tab) * bsrr->num_entries);
3019         
3020         if (bsrr->status == BEND_SCAN_PARTIAL)
3021             *scanStatus = Z_Scan_partial_5;
3022         else
3023             *scanStatus = Z_Scan_success;
3024         ents->entries = tab;
3025         ents->num_entries = bsrr->num_entries;
3026         res->numberOfEntriesReturned = &ents->num_entries;          
3027         res->positionOfTerm = &bsrr->term_position;
3028         for (i = 0; i < bsrr->num_entries; i++)
3029         {
3030             Z_Entry *e;
3031             Z_TermInfo *t;
3032             Odr_oct *o;
3033             
3034             tab[i] = e = (Z_Entry *)odr_malloc(assoc->encode, sizeof(*e));
3035             if (bsrr->entries[i].occurrences >= 0)
3036             {
3037                 e->which = Z_Entry_termInfo;
3038                 e->u.termInfo = t = (Z_TermInfo *)
3039                     odr_malloc(assoc->encode, sizeof(*t));
3040                 t->suggestedAttributes = 0;
3041                 t->displayTerm = 0;
3042                 if (save_entries == bsrr->entries && 
3043                     bsrr->entries[i].display_term)
3044                 {
3045                     /* the entries was _not_ set by the handler. So it's
3046                        safe to test for new member display_term. It is
3047                        NULL'ed by us.
3048                     */
3049                     t->displayTerm = odr_strdup(assoc->encode,
3050                                                 bsrr->entries[i].display_term);
3051                 }
3052                 t->alternativeTerm = 0;
3053                 t->byAttributes = 0;
3054                 t->otherTermInfo = 0;
3055                 t->globalOccurrences = &bsrr->entries[i].occurrences;
3056                 t->term = (Z_Term *)
3057                     odr_malloc(assoc->encode, sizeof(*t->term));
3058                 t->term->which = Z_Term_general;
3059                 t->term->u.general = o =
3060                     (Odr_oct *)odr_malloc(assoc->encode, sizeof(Odr_oct));
3061                 o->buf = (unsigned char *)
3062                     odr_malloc(assoc->encode, o->len = o->size =
3063                                strlen(bsrr->entries[i].term));
3064                 memcpy(o->buf, bsrr->entries[i].term, o->len);
3065                 yaz_log(YLOG_DEBUG, "  term #%d: '%s' (%d)", i,
3066                          bsrr->entries[i].term, bsrr->entries[i].occurrences);
3067             }
3068             else
3069             {
3070                 Z_DiagRecs *drecs = zget_DiagRecs(assoc->encode,
3071                                                   bsrr->entries[i].errcode,
3072                                                   bsrr->entries[i].errstring);
3073                 assert (drecs->num_diagRecs == 1);
3074                 e->which = Z_Entry_surrogateDiagnostic;
3075                 assert (drecs->diagRecs[0]);
3076                 e->u.surrogateDiagnostic = drecs->diagRecs[0];
3077             }
3078         }
3079     }
3080     if (diagrecs_p)
3081     {
3082         ents->num_nonsurrogateDiagnostics = diagrecs_p->num_diagRecs;
3083         ents->nonsurrogateDiagnostics = diagrecs_p->diagRecs;
3084     }
3085     if (log_request)
3086     {
3087         int i;
3088         WRBUF wr = wrbuf_alloc();
3089         wrbuf_printf(wr, "Scan ");
3090         for (i = 0 ; i < req->num_databaseNames; i++){
3091             if (i)
3092                 wrbuf_printf(wr, "+");
3093             wrbuf_printf(wr, req->databaseNames[i]);
3094         }
3095         wrbuf_printf(wr, " ");
3096         
3097         if (bsrr->errcode){
3098             wr_diag(wr, bsrr->errcode, bsrr->errstring);
3099             wrbuf_printf(wr, " ");
3100         }
3101         else if (*res->scanStatus == Z_Scan_success)
3102             wrbuf_printf(wr, "OK - - ");
3103         else
3104             wrbuf_printf(wr, "Partial - - ");
3105
3106         wrbuf_printf(wr, "%d+%d+%d ",
3107                      (req->preferredPositionInResponse ?
3108                       *req->preferredPositionInResponse : 1),
3109                      *req->numberOfTermsRequested,
3110                      (res->stepSize ? *res->stepSize : 1));
3111         /* TODO - make this print out RPN: or CQL: in the beginning!! */
3112         /* maybe wrbuf_printf(wr, "%s: %s ", querytype, querystr); 
3113            see line 1415 */
3114         yaz_scan_to_wrbuf(wr, req->termListAndStartPoint, 
3115                           bsrr->attributeset);
3116         yaz_log(log_request, "%s", wrbuf_buf(wr) );
3117         wrbuf_free(wr, 1);
3118     }
3119     return apdu;
3120 }
3121
3122 static Z_APDU *process_sortRequest(association *assoc, request *reqb,
3123     int *fd)
3124 {
3125     int i;
3126     Z_SortRequest *req = reqb->apdu_request->u.sortRequest;
3127     Z_SortResponse *res = (Z_SortResponse *)
3128         odr_malloc (assoc->encode, sizeof(*res));
3129     bend_sort_rr *bsrr = (bend_sort_rr *)
3130         odr_malloc (assoc->encode, sizeof(*bsrr));
3131
3132     Z_APDU *apdu = (Z_APDU *)odr_malloc (assoc->encode, sizeof(*apdu));
3133
3134     yaz_log(log_requestdetail, "Got SortRequest.");
3135
3136     bsrr->num_input_setnames = req->num_inputResultSetNames;
3137     for (i=0;i<req->num_inputResultSetNames;i++)
3138         yaz_log(log_requestdetail, "Input resultset: '%s'",
3139                 req->inputResultSetNames[i]);
3140     bsrr->input_setnames = req->inputResultSetNames;
3141     bsrr->referenceId = req->referenceId;
3142     bsrr->output_setname = req->sortedResultSetName;
3143     yaz_log(log_requestdetail, "Output resultset: '%s'",
3144                 req->sortedResultSetName);
3145     bsrr->sort_sequence = req->sortSequence;
3146        /*FIXME - dump those sequences too */
3147     bsrr->stream = assoc->encode;
3148     bsrr->print = assoc->print;
3149
3150     bsrr->sort_status = Z_SortResponse_failure;
3151     bsrr->errcode = 0;
3152     bsrr->errstring = 0;
3153     
3154     (*assoc->init->bend_sort)(assoc->backend, bsrr);
3155     
3156     res->referenceId = bsrr->referenceId;
3157     res->sortStatus = odr_intdup(assoc->encode, bsrr->sort_status);
3158     res->resultSetStatus = 0;
3159     if (bsrr->errcode)
3160     {
3161         Z_DiagRecs *dr = zget_DiagRecs(assoc->encode,
3162                                        bsrr->errcode, bsrr->errstring);
3163         res->diagnostics = dr->diagRecs;
3164         res->num_diagnostics = dr->num_diagRecs;
3165     }
3166     else
3167     {
3168         res->num_diagnostics = 0;
3169         res->diagnostics = 0;
3170     }
3171     res->resultCount = 0;
3172     res->otherInfo = 0;
3173
3174     apdu->which = Z_APDU_sortResponse;
3175     apdu->u.sortResponse = res;
3176     if (log_request)
3177     {
3178         WRBUF wr = wrbuf_alloc();
3179         wrbuf_printf(wr, "Sort ");
3180         if (bsrr->errcode)
3181             wrbuf_printf(wr, " ERROR %d", bsrr->errcode);
3182         else
3183             wrbuf_printf(wr,  "OK -");
3184         wrbuf_printf(wr, " (");
3185         for (i = 0; i<req->num_inputResultSetNames; i++)
3186         {
3187             if (i)
3188                 wrbuf_printf(wr, "+");
3189             wrbuf_printf(wr, req->inputResultSetNames[i]);
3190         }
3191         wrbuf_printf(wr, ")->%s ",req->sortedResultSetName);
3192
3193         yaz_log(log_request, "%s", wrbuf_buf(wr) );
3194         wrbuf_free(wr, 1);
3195     }
3196     return apdu;
3197 }
3198
3199 static Z_APDU *process_deleteRequest(association *assoc, request *reqb,
3200     int *fd)
3201 {
3202     int i;
3203     Z_DeleteResultSetRequest *req =
3204         reqb->apdu_request->u.deleteResultSetRequest;
3205     Z_DeleteResultSetResponse *res = (Z_DeleteResultSetResponse *)
3206         odr_malloc (assoc->encode, sizeof(*res));
3207     bend_delete_rr *bdrr = (bend_delete_rr *)
3208         odr_malloc (assoc->encode, sizeof(*bdrr));
3209     Z_APDU *apdu = (Z_APDU *)odr_malloc (assoc->encode, sizeof(*apdu));
3210
3211     yaz_log(log_requestdetail, "Got DeleteRequest.");
3212
3213     bdrr->num_setnames = req->num_resultSetList;
3214     bdrr->setnames = req->resultSetList;
3215     for (i = 0; i<req->num_resultSetList; i++)
3216         yaz_log(log_requestdetail, "resultset: '%s'",
3217                 req->resultSetList[i]);
3218     bdrr->stream = assoc->encode;
3219     bdrr->print = assoc->print;
3220     bdrr->function = *req->deleteFunction;
3221     bdrr->referenceId = req->referenceId;
3222     bdrr->statuses = 0;
3223     if (bdrr->num_setnames > 0)
3224     {
3225         bdrr->statuses = (int*) 
3226             odr_malloc(assoc->encode, sizeof(*bdrr->statuses) *
3227                        bdrr->num_setnames);
3228         for (i = 0; i < bdrr->num_setnames; i++)
3229             bdrr->statuses[i] = 0;
3230     }
3231     (*assoc->init->bend_delete)(assoc->backend, bdrr);
3232     
3233     res->referenceId = req->referenceId;
3234
3235     res->deleteOperationStatus = odr_intdup(assoc->encode,bdrr->delete_status);
3236
3237     res->deleteListStatuses = 0;
3238     if (bdrr->num_setnames > 0)
3239     {
3240         int i;
3241         res->deleteListStatuses = (Z_ListStatuses *)
3242             odr_malloc(assoc->encode, sizeof(*res->deleteListStatuses));
3243         res->deleteListStatuses->num = bdrr->num_setnames;
3244         res->deleteListStatuses->elements =
3245             (Z_ListStatus **)
3246             odr_malloc (assoc->encode, 
3247                         sizeof(*res->deleteListStatuses->elements) *
3248                         bdrr->num_setnames);
3249         for (i = 0; i<bdrr->num_setnames; i++)
3250         {
3251             res->deleteListStatuses->elements[i] =
3252                 (Z_ListStatus *)
3253                 odr_malloc (assoc->encode,
3254                             sizeof(**res->deleteListStatuses->elements));
3255             res->deleteListStatuses->elements[i]->status = bdrr->statuses+i;
3256             res->deleteListStatuses->elements[i]->id =
3257                 odr_strdup (assoc->encode, bdrr->setnames[i]);
3258         }
3259     }
3260     res->numberNotDeleted = 0;
3261     res->bulkStatuses = 0;
3262     res->deleteMessage = 0;
3263     res->otherInfo = 0;
3264
3265     apdu->which = Z_APDU_deleteResultSetResponse;
3266     apdu->u.deleteResultSetResponse = res;
3267     if (log_request)
3268     {
3269         WRBUF wr = wrbuf_alloc();
3270         wrbuf_printf(wr, "Delete ");
3271         if (bdrr->delete_status)
3272             wrbuf_printf(wr, "ERROR %d", bdrr->delete_status);
3273         else
3274             wrbuf_printf(wr, "OK -");
3275         for (i = 0; i<req->num_resultSetList; i++)
3276             wrbuf_printf(wr, " %s ", req->resultSetList[i]);
3277         yaz_log(log_request, "%s", wrbuf_buf(wr) );
3278         wrbuf_free(wr, 1);
3279     }
3280     return apdu;
3281 }
3282
3283 static void process_close(association *assoc, request *reqb)
3284 {
3285     Z_Close *req = reqb->apdu_request->u.close;
3286     static char *reasons[] =
3287     {
3288         "finished",
3289         "shutdown",
3290         "systemProblem",
3291         "costLimit",
3292         "resources",
3293         "securityViolation",
3294         "protocolError",
3295         "lackOfActivity",
3296         "peerAbort",
3297         "unspecified"
3298     };
3299
3300     yaz_log(log_requestdetail, "Got Close, reason %s, message %s",
3301         reasons[*req->closeReason], req->diagnosticInformation ?
3302         req->diagnosticInformation : "NULL");
3303     if (assoc->version < 3) /* to make do_force respond with close */
3304         assoc->version = 3;
3305     do_close_req(assoc, Z_Close_finished,
3306                  "Association terminated by client", reqb);
3307     yaz_log(log_request,"Close OK");
3308 }
3309
3310 void save_referenceId (request *reqb, Z_ReferenceId *refid)
3311 {
3312     if (refid)
3313     {
3314         reqb->len_refid = refid->len;
3315         reqb->refid = (char *)nmem_malloc (reqb->request_mem, refid->len);
3316         memcpy (reqb->refid, refid->buf, refid->len);
3317     }
3318     else
3319     {
3320         reqb->len_refid = 0;
3321         reqb->refid = NULL;
3322     }
3323 }
3324
3325 void bend_request_send (bend_association a, bend_request req, Z_APDU *res)
3326 {
3327     process_z_response (a, req, res);
3328 }
3329
3330 bend_request bend_request_mk (bend_association a)
3331 {
3332     request *nreq = request_get (&a->outgoing);
3333     nreq->request_mem = nmem_create ();
3334     return nreq;
3335 }
3336
3337 Z_ReferenceId *bend_request_getid (ODR odr, bend_request req)
3338 {
3339     Z_ReferenceId *id;
3340     if (!req->refid)
3341         return 0;
3342     id = (Odr_oct *)odr_malloc (odr, sizeof(*odr));
3343     id->buf = (unsigned char *)odr_malloc (odr, req->len_refid);
3344     id->len = id->size = req->len_refid;
3345     memcpy (id->buf, req->refid, req->len_refid);
3346     return id;
3347 }
3348
3349 void bend_request_destroy (bend_request *req)
3350 {
3351     nmem_destroy((*req)->request_mem);
3352     request_release(*req);
3353     *req = NULL;
3354 }
3355
3356 int bend_backend_respond (bend_association a, bend_request req)
3357 {
3358     char *msg;
3359     int r;
3360     r = process_z_request (a, req, &msg);
3361     if (r < 0)
3362         yaz_log (YLOG_WARN, "%s", msg);
3363     return r;
3364 }
3365
3366 void bend_request_setdata(bend_request r, void *p)
3367 {
3368     r->clientData = p;
3369 }
3370
3371 void *bend_request_getdata(bend_request r)
3372 {
3373     return r->clientData;
3374 }
3375
3376 static Z_APDU *process_segmentRequest (association *assoc, request *reqb)
3377 {
3378     bend_segment_rr req;
3379
3380     req.segment = reqb->apdu_request->u.segmentRequest;
3381     req.stream = assoc->encode;
3382     req.decode = assoc->decode;
3383     req.print = assoc->print;
3384     req.association = assoc;
3385     
3386     (*assoc->init->bend_segment)(assoc->backend, &req);
3387
3388     return 0;
3389 }
3390
3391 static Z_APDU *process_ESRequest(association *assoc, request *reqb, int *fd)
3392 {
3393     bend_esrequest_rr esrequest;
3394     const char *ext_name = "unknown";
3395
3396     Z_ExtendedServicesRequest *req =
3397         reqb->apdu_request->u.extendedServicesRequest;
3398     Z_APDU *apdu = zget_APDU(assoc->encode, Z_APDU_extendedServicesResponse);
3399
3400     Z_ExtendedServicesResponse *resp = apdu->u.extendedServicesResponse;
3401
3402     esrequest.esr = reqb->apdu_request->u.extendedServicesRequest;
3403     esrequest.stream = assoc->encode;
3404     esrequest.decode = assoc->decode;
3405     esrequest.print = assoc->print;
3406     esrequest.errcode = 0;
3407     esrequest.errstring = NULL;
3408     esrequest.request = reqb;
3409     esrequest.association = assoc;
3410     esrequest.taskPackage = 0;
3411     esrequest.referenceId = req->referenceId;
3412
3413     
3414     if (esrequest.esr && esrequest.esr->taskSpecificParameters)
3415     {
3416         switch(esrequest.esr->taskSpecificParameters->which)
3417         {
3418         case Z_External_itemOrder:
3419             ext_name = "ItemOrder"; break;
3420         case Z_External_update:
3421             ext_name = "Update"; break;
3422         case Z_External_update0:
3423             ext_name = "Update0"; break;
3424         case Z_External_ESAdmin:
3425             ext_name = "Admin"; break;
3426
3427         }
3428     }
3429
3430     (*assoc->init->bend_esrequest)(assoc->backend, &esrequest);
3431     
3432     /* If the response is being delayed, return NULL */
3433     if (esrequest.request == NULL)
3434         return(NULL);
3435
3436     resp->referenceId = req->referenceId;
3437
3438     if (esrequest.errcode == -1)
3439     {
3440         /* Backend service indicates request will be processed */
3441         yaz_log(log_request, "Extended Service: %s (accepted)", ext_name);
3442         *resp->operationStatus = Z_ExtendedServicesResponse_accepted;
3443     }
3444     else if (esrequest.errcode == 0)
3445     {
3446         /* Backend service indicates request will be processed */
3447         yaz_log(log_request, "Extended Service: %s (done)", ext_name);
3448         *resp->operationStatus = Z_ExtendedServicesResponse_done;
3449     }
3450     else
3451     {
3452         Z_DiagRecs *diagRecs =
3453             zget_DiagRecs(assoc->encode, esrequest.errcode,
3454                           esrequest.errstring);
3455         /* Backend indicates error, request will not be processed */
3456         yaz_log(log_request, "Extended Service: %s (failed)", ext_name);
3457         *resp->operationStatus = Z_ExtendedServicesResponse_failure;
3458         resp->num_diagnostics = diagRecs->num_diagRecs;
3459         resp->diagnostics = diagRecs->diagRecs;
3460         if (log_request)
3461         {
3462             WRBUF wr = wrbuf_alloc();
3463             wrbuf_diags(wr, resp->num_diagnostics, resp->diagnostics);
3464             yaz_log(log_request, "EsRequest %s", wrbuf_buf(wr) );
3465             wrbuf_free(wr, 1);
3466         }
3467
3468     }
3469     /* Do something with the members of bend_extendedservice */
3470     if (esrequest.taskPackage)
3471         resp->taskPackage = z_ext_record (assoc->encode, VAL_EXTENDED,
3472                                          (const char *)  esrequest.taskPackage,
3473                                           -1);
3474     yaz_log(YLOG_DEBUG,"Send the result apdu");
3475     return apdu;
3476 }
3477
3478 /*
3479  * Local variables:
3480  * c-basic-offset: 4
3481  * indent-tabs-mode: nil
3482  * End:
3483  * vim: shiftwidth=4 tabstop=8 expandtab
3484  */
3485