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