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