Improvements in the log system, especially on the server side. Now logging on
[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.34 2004-11-16 17:08:11 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(LOG_WARN|LOG_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(LOG_WARN|LOG_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(LOG_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 (LOG_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(LOG_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(LOG_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(LOG_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(LOG_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(LOG_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(LOG_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(LOG_DEBUG, "Wrote PDU, %d bytes", req->len_response);
414 #if 0
415             yaz_log(LOG_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(LOG_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(LOG_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(LOG_DEBUG, "srw_bend_search");
626     if (!assoc->init)
627     {
628         yaz_log(LOG_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(LOG_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(LOG_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(LOG_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(LOG_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(LOG_DEBUG, "  result immediately available");
1140         retval = process_z_response(assoc, req, res);
1141     }
1142     else if (fd < 0)
1143     {
1144         yaz_log(LOG_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(LOG_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(LOG_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(LOG_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(LOG_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(LOG_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(LOG_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(LOG_WARN, "ODR error when encoding PDU: %s [element %s]",
1221                 odr_errmsg(odr_geterror(assoc->decode)),
1222                 odr_getelement(assoc->decode));
1223         request_release(req);
1224         return -1;
1225     }
1226     req->response = odr_getbuf(assoc->encode, &req->len_response,
1227         &req->size_response);
1228     odr_setbuf(assoc->encode, 0, 0, 0); /* don'txfree if we abort later */
1229     odr_reset(assoc->encode);
1230     req->state = REQUEST_IDLE;
1231     request_enq(&assoc->outgoing, req);
1232     /* turn the work over to the ir_session handler */
1233     iochan_setflag(assoc->client_chan, EVENT_OUTPUT);
1234     assoc->cs_put_mask = EVENT_OUTPUT;
1235     /* Is there more work to be done? give that to the input handler too */
1236 #if 1
1237     if (request_head(&assoc->incoming))
1238     {
1239         yaz_log (LOG_DEBUG, "more work to be done");
1240         iochan_setevent(assoc->client_chan, EVENT_WORK);
1241     }
1242 #endif
1243     return 0;
1244 }
1245
1246 /*
1247  * Encode response, and transfer the request structure to the outgoing queue.
1248  */
1249 static int process_z_response(association *assoc, request *req, Z_APDU *res)
1250 {
1251     Z_GDU *gres = (Z_GDU *) odr_malloc(assoc->encode, sizeof(*res));
1252     gres->which = Z_GDU_Z3950;
1253     gres->u.z3950 = res;
1254
1255     return process_gdu_response(assoc, req, gres);
1256 }
1257
1258
1259 /*
1260  * Handle init request.
1261  * At the moment, we don't check the options
1262  * anywhere else in the code - we just try not to do anything that would
1263  * break a naive client. We'll toss 'em into the association block when
1264  * we need them there.
1265  */
1266 static Z_APDU *process_initRequest(association *assoc, request *reqb)
1267 {
1268     statserv_options_block *cb = statserv_getcontrol();
1269     Z_InitRequest *req = reqb->apdu_request->u.initRequest;
1270     Z_APDU *apdu = zget_APDU(assoc->encode, Z_APDU_initResponse);
1271     Z_InitResponse *resp = apdu->u.initResponse;
1272     bend_initresult *binitres;
1273     char *version;
1274     char options[140];
1275
1276     yaz_log(log_requestdetail, "Got initRequest");
1277     if (req->implementationId)
1278         yaz_log(log_requestdetail, "Id:        %s", req->implementationId);
1279     if (req->implementationName)
1280         yaz_log(log_requestdetail, "Name:      %s", req->implementationName);
1281     if (req->implementationVersion)
1282         yaz_log(log_requestdetail, "Version:   %s", req->implementationVersion);
1283
1284     assoc_init_reset(assoc);
1285
1286     assoc->init->auth = req->idAuthentication;
1287     assoc->init->referenceId = req->referenceId;
1288
1289     if (ODR_MASK_GET(req->options, Z_Options_negotiationModel))
1290     {
1291         Z_CharSetandLanguageNegotiation *negotiation =
1292             yaz_get_charneg_record (req->otherInfo);
1293         if (negotiation &&
1294             negotiation->which == Z_CharSetandLanguageNegotiation_proposal)
1295             assoc->init->charneg_request = negotiation;
1296     }
1297     
1298     assoc->backend = 0;
1299     if (!(binitres = (*cb->bend_init)(assoc->init)))
1300     {
1301         yaz_log(LOG_WARN, "Bad response from backend.");
1302         return 0;
1303     }
1304
1305     assoc->backend = binitres->handle;
1306     if ((assoc->init->bend_sort))
1307         yaz_log (LOG_DEBUG, "Sort handler installed");
1308     if ((assoc->init->bend_search))
1309         yaz_log (LOG_DEBUG, "Search handler installed");
1310     if ((assoc->init->bend_present))
1311         yaz_log (LOG_DEBUG, "Present handler installed");   
1312     if ((assoc->init->bend_esrequest))
1313         yaz_log (LOG_DEBUG, "ESRequest handler installed");   
1314     if ((assoc->init->bend_delete))
1315         yaz_log (LOG_DEBUG, "Delete handler installed");   
1316     if ((assoc->init->bend_scan))
1317         yaz_log (LOG_DEBUG, "Scan handler installed");   
1318     if ((assoc->init->bend_segment))
1319         yaz_log (LOG_DEBUG, "Segment handler installed");   
1320     
1321     resp->referenceId = req->referenceId;
1322     *options = '\0';
1323     /* let's tell the client what we can do */
1324     if (ODR_MASK_GET(req->options, Z_Options_search))
1325     {
1326         ODR_MASK_SET(resp->options, Z_Options_search);
1327         strcat(options, "srch");
1328     }
1329     if (ODR_MASK_GET(req->options, Z_Options_present))
1330     {
1331         ODR_MASK_SET(resp->options, Z_Options_present);
1332         strcat(options, " prst");
1333     }
1334     if (ODR_MASK_GET(req->options, Z_Options_delSet) &&
1335         assoc->init->bend_delete)
1336     {
1337         ODR_MASK_SET(resp->options, Z_Options_delSet);
1338         strcat(options, " del");
1339     }
1340     if (ODR_MASK_GET(req->options, Z_Options_extendedServices) &&
1341         assoc->init->bend_esrequest)
1342     {
1343         ODR_MASK_SET(resp->options, Z_Options_extendedServices);
1344         strcat (options, " extendedServices");
1345     }
1346     if (ODR_MASK_GET(req->options, Z_Options_namedResultSets))
1347     {
1348         ODR_MASK_SET(resp->options, Z_Options_namedResultSets);
1349         strcat(options, " namedresults");
1350     }
1351     if (ODR_MASK_GET(req->options, Z_Options_scan) && assoc->init->bend_scan)
1352     {
1353         ODR_MASK_SET(resp->options, Z_Options_scan);
1354         strcat(options, " scan");
1355     }
1356     if (ODR_MASK_GET(req->options, Z_Options_concurrentOperations))
1357     {
1358         ODR_MASK_SET(resp->options, Z_Options_concurrentOperations);
1359         strcat(options, " concurrop");
1360     }
1361     if (ODR_MASK_GET(req->options, Z_Options_sort) && assoc->init->bend_sort)
1362     {
1363         ODR_MASK_SET(resp->options, Z_Options_sort);
1364         strcat(options, " sort");
1365     }
1366
1367     if (ODR_MASK_GET(req->options, Z_Options_negotiationModel)
1368         && assoc->init->charneg_response)
1369     {
1370         Z_OtherInformation **p;
1371         Z_OtherInformationUnit *p0;
1372         
1373         yaz_oi_APDU(apdu, &p);
1374         
1375         if ((p0=yaz_oi_update(p, assoc->encode, NULL, 0, 0))) {
1376             ODR_MASK_SET(resp->options, Z_Options_negotiationModel);
1377             
1378             p0->which = Z_OtherInfo_externallyDefinedInfo;
1379             p0->information.externallyDefinedInfo =
1380                 assoc->init->charneg_response;
1381         }
1382         ODR_MASK_SET(resp->options, Z_Options_negotiationModel);
1383         strcat(options, " negotiation");
1384     }
1385         
1386     ODR_MASK_SET(resp->options, Z_Options_triggerResourceCtrl);
1387
1388     if (ODR_MASK_GET(req->protocolVersion, Z_ProtocolVersion_1))
1389     {
1390         ODR_MASK_SET(resp->protocolVersion, Z_ProtocolVersion_1);
1391         assoc->version = 1; /* 1 & 2 are equivalent */
1392     }
1393     if (ODR_MASK_GET(req->protocolVersion, Z_ProtocolVersion_2))
1394     {
1395         ODR_MASK_SET(resp->protocolVersion, Z_ProtocolVersion_2);
1396         assoc->version = 2;
1397     }
1398     if (ODR_MASK_GET(req->protocolVersion, Z_ProtocolVersion_3))
1399     {
1400         ODR_MASK_SET(resp->protocolVersion, Z_ProtocolVersion_3);
1401         assoc->version = 3;
1402     }
1403
1404     yaz_log(log_requestdetail, "Negotiated to v%d: %s", assoc->version, options);
1405     assoc->maximumRecordSize = *req->maximumRecordSize;
1406     if (assoc->maximumRecordSize > control_block->maxrecordsize)
1407         assoc->maximumRecordSize = control_block->maxrecordsize;
1408     assoc->preferredMessageSize = *req->preferredMessageSize;
1409     if (assoc->preferredMessageSize > assoc->maximumRecordSize)
1410         assoc->preferredMessageSize = assoc->maximumRecordSize;
1411
1412     resp->preferredMessageSize = &assoc->preferredMessageSize;
1413     resp->maximumRecordSize = &assoc->maximumRecordSize;
1414
1415     resp->implementationId = odr_prepend(assoc->encode,
1416                 assoc->init->implementation_id,
1417                 resp->implementationId);
1418
1419     resp->implementationName = odr_prepend(assoc->encode,
1420                 assoc->init->implementation_name,
1421                 odr_prepend(assoc->encode, "GFS", resp->implementationName));
1422
1423     version = odr_strdup(assoc->encode, "$Revision: 1.34 $");
1424     if (strlen(version) > 10)   /* check for unexpanded CVS strings */
1425         version[strlen(version)-2] = '\0';
1426     resp->implementationVersion = odr_prepend(assoc->encode,
1427                 assoc->init->implementation_version,
1428                 odr_prepend(assoc->encode, &version[11],
1429                             resp->implementationVersion));
1430
1431     if (binitres->errcode)
1432     {
1433         yaz_log(LOG_DEBUG, "Connection rejected by backend.");
1434         *resp->result = 0;
1435         assoc->state = ASSOC_DEAD;
1436         resp->userInformationField = init_diagnostics(assoc->encode,
1437                                                       binitres->errcode,
1438                                                       binitres->errstring);
1439         yaz_log(log_request,"Init from '%s' (%s) (ver %s) Error %d %s",
1440             req->implementationName ? req->implementationName :"??",
1441             req->implementationId ? req->implementationId :"?", 
1442             req->implementationVersion ? req->implementationVersion: "?",
1443             binitres->errcode,binitres->errstring );
1444     }
1445     else
1446     {
1447         assoc->state = ASSOC_UP;
1448         yaz_log(log_request,"Init from '%s' (%s) (ver %s) OK",
1449             req->implementationName ? req->implementationName :"??",
1450             req->implementationId ? req->implementationId :"?", 
1451             req->implementationVersion ? req->implementationVersion: "?");
1452     }
1453
1454     return apdu;
1455 }
1456
1457 /*
1458  * Diagnostic in default format, to be returned as either a surrogate
1459  * or non-surrogate diagnostic in the context of an open session, or
1460  * as User-information when an Init is refused.
1461  */
1462 static Z_DefaultDiagFormat *justdiag(ODR odr, int error, char *addinfo)
1463 {
1464     int *err = odr_intdup(odr, error);
1465     Z_DefaultDiagFormat *dr = (Z_DefaultDiagFormat *)
1466         odr_malloc (odr, sizeof(*dr));
1467
1468     yaz_log(log_requestdetail, "[%d] %s%s%s", error, diagbib1_str(error),
1469         addinfo ? " -- " : "", addinfo ? addinfo : "");
1470
1471     dr->diagnosticSetId =
1472         yaz_oidval_to_z3950oid (odr, CLASS_DIAGSET, VAL_BIB1);
1473     dr->condition = err;
1474     dr->which = Z_DefaultDiagFormat_v2Addinfo;
1475     dr->u.v2Addinfo = odr_strdup (odr, addinfo ? addinfo : "");
1476     return dr;
1477 }
1478
1479 /*
1480  * Set the specified `errcode' and `errstring' into a UserInfo-1
1481  * external to be returned to the client in accordance with Z35.90
1482  * Implementor Agreement 5 (Returning diagnostics in an InitResponse):
1483  *      http://lcweb.loc.gov/z3950/agency/agree/initdiag.html
1484  */
1485 static Z_External *init_diagnostics(ODR odr, int error, char *addinfo)
1486 {
1487     Z_External *x, *x2;
1488     oident oid;
1489     Z_OtherInformation *u;
1490     Z_OtherInformationUnit *l;
1491     Z_DiagnosticFormat *d;
1492     Z_DiagnosticFormat_s *e;
1493
1494     x = (Z_External*) odr_malloc(odr, sizeof *x);
1495     x->descriptor = 0;
1496     x->indirect_reference = 0;  
1497     oid.proto = PROTO_Z3950;
1498     oid.oclass = CLASS_USERINFO;
1499     oid.value = VAL_USERINFO1;
1500     x->direct_reference = odr_oiddup(odr, oid_getoidbyent(&oid));
1501     x->which = Z_External_userInfo1;
1502
1503     u = odr_malloc(odr, sizeof *u);
1504     x->u.userInfo1 = u;
1505     u->num_elements = 1;
1506     u->list = (Z_OtherInformationUnit**) odr_malloc(odr, sizeof *u->list);
1507     u->list[0] = (Z_OtherInformationUnit*) odr_malloc(odr, sizeof *u->list[0]);
1508     l = u->list[0];
1509     l->category = 0;
1510     l->which = Z_OtherInfo_externallyDefinedInfo;
1511
1512     x2 = (Z_External*) odr_malloc(odr, sizeof *x);
1513     l->information.externallyDefinedInfo = x2;
1514     x2->descriptor = 0;
1515     x2->indirect_reference = 0;
1516     oid.oclass = CLASS_DIAGSET;
1517     oid.value = VAL_DIAG1;
1518     x2->direct_reference = odr_oiddup(odr, oid_getoidbyent(&oid));
1519     x2->which = Z_External_diag1;
1520
1521     d = (Z_DiagnosticFormat*) odr_malloc(odr, sizeof *d);
1522     x2->u.diag1 = d;
1523     d->num = 1;
1524     d->elements = (Z_DiagnosticFormat_s**) odr_malloc (odr, sizeof *d->elements);
1525     d->elements[0] = (Z_DiagnosticFormat_s*) odr_malloc (odr, sizeof *d->elements[0]);
1526     e = d->elements[0];
1527
1528     e->which = Z_DiagnosticFormat_s_defaultDiagRec;
1529     e->u.defaultDiagRec = justdiag(odr, error, addinfo);
1530     e->message = 0;
1531     return x;
1532 }
1533
1534 /*
1535  * nonsurrogate diagnostic record.
1536  */
1537 static Z_Records *diagrec(association *assoc, int error, char *addinfo)
1538 {
1539     Z_Records *rec = (Z_Records *)
1540         odr_malloc (assoc->encode, sizeof(*rec));
1541     rec->which = Z_Records_NSD;
1542     rec->u.nonSurrogateDiagnostic = justdiag(assoc->encode, error, addinfo);
1543     return rec;
1544 }
1545
1546 /*
1547  * surrogate diagnostic.
1548  */
1549 static Z_NamePlusRecord *surrogatediagrec(association *assoc, char *dbname,
1550                                           int error, char *addinfo)
1551 {
1552     Z_NamePlusRecord *rec = (Z_NamePlusRecord *)
1553         odr_malloc (assoc->encode, sizeof(*rec));
1554     Z_DiagRec *drec = (Z_DiagRec *)odr_malloc (assoc->encode, sizeof(*drec));
1555     
1556     yaz_log(LOG_DEBUG, "SurrogateDiagnotic: %d -- %s", error, addinfo);
1557     rec->databaseName = dbname;
1558     rec->which = Z_NamePlusRecord_surrogateDiagnostic;
1559     rec->u.surrogateDiagnostic = drec;
1560     drec->which = Z_DiagRec_defaultFormat;
1561     drec->u.defaultFormat = justdiag(assoc->encode, error, addinfo);
1562
1563     return rec;
1564 }
1565
1566 /*
1567  * multiple nonsurrogate diagnostics.
1568  */
1569 static Z_DiagRecs *diagrecs(association *assoc, int error, char *addinfo)
1570 {
1571     Z_DiagRecs *recs = (Z_DiagRecs *)odr_malloc (assoc->encode, sizeof(*recs));
1572     int *err = odr_intdup(assoc->encode, error);
1573     Z_DiagRec **recp = (Z_DiagRec **)odr_malloc (assoc->encode, sizeof(*recp));
1574     Z_DiagRec *drec = (Z_DiagRec *)odr_malloc (assoc->encode, sizeof(*drec));
1575     Z_DefaultDiagFormat *rec = (Z_DefaultDiagFormat *)
1576         odr_malloc (assoc->encode, sizeof(*rec));
1577
1578     yaz_log(LOG_DEBUG, "DiagRecs: %d -- %s", error, addinfo ? addinfo : "");
1579
1580     recs->num_diagRecs = 1;
1581     recs->diagRecs = recp;
1582     recp[0] = drec;
1583     drec->which = Z_DiagRec_defaultFormat;
1584     drec->u.defaultFormat = rec;
1585
1586     rec->diagnosticSetId =
1587         yaz_oidval_to_z3950oid (assoc->encode, CLASS_DIAGSET, VAL_BIB1);
1588     rec->condition = err;
1589
1590     rec->which = Z_DefaultDiagFormat_v2Addinfo;
1591     rec->u.v2Addinfo = odr_strdup (assoc->encode, addinfo ? addinfo : "");
1592     return recs;
1593 }
1594
1595 static Z_Records *pack_records(association *a, char *setname, int start,
1596                                int *num, Z_RecordComposition *comp,
1597                                int *next, int *pres, oid_value format,
1598                                Z_ReferenceId *referenceId,
1599                                int *oid, int *errcode)
1600 {
1601     int recno, total_length = 0, toget = *num, dumped_records = 0;
1602     *errcode=0;
1603     Z_Records *records =
1604         (Z_Records *) odr_malloc (a->encode, sizeof(*records));
1605     Z_NamePlusRecordList *reclist =
1606         (Z_NamePlusRecordList *) odr_malloc (a->encode, sizeof(*reclist));
1607     Z_NamePlusRecord **list =
1608         (Z_NamePlusRecord **) odr_malloc (a->encode, sizeof(*list) * toget);
1609
1610     records->which = Z_Records_DBOSD;
1611     records->u.databaseOrSurDiagnostics = reclist;
1612     reclist->num_records = 0;
1613     reclist->records = list;
1614     *pres = Z_PresentStatus_success;
1615     *num = 0;
1616     *next = 0;
1617
1618     yaz_log(log_requestdetail, "Request to pack %d+%d %s", start, toget, setname);
1619     yaz_log(log_requestdetail, "pms=%d, mrs=%d", a->preferredMessageSize,
1620         a->maximumRecordSize);
1621     for (recno = start; reclist->num_records < toget; recno++)
1622     {
1623         bend_fetch_rr freq;
1624         Z_NamePlusRecord *thisrec;
1625         int this_length = 0;
1626         /*
1627          * we get the number of bytes allocated on the stream before any
1628          * allocation done by the backend - this should give us a reasonable
1629          * idea of the total size of the data so far.
1630          */
1631         total_length = odr_total(a->encode) - dumped_records;
1632         freq.errcode = 0;
1633         freq.errstring = 0;
1634         freq.basename = 0;
1635         freq.len = 0;
1636         freq.record = 0;
1637         freq.last_in_set = 0;
1638         freq.setname = setname;
1639         freq.surrogate_flag = 0;
1640         freq.number = recno;
1641         freq.comp = comp;
1642         freq.request_format = format;
1643         freq.request_format_raw = oid;
1644         freq.output_format = format;
1645         freq.output_format_raw = 0;
1646         freq.stream = a->encode;
1647         freq.print = a->print;
1648         freq.referenceId = referenceId;
1649         freq.schema = 0;
1650         (*a->init->bend_fetch)(a->backend, &freq);
1651         /* backend should be able to signal whether error is system-wide
1652            or only pertaining to current record */
1653         if (freq.errcode)
1654         {
1655             if (!freq.surrogate_flag)
1656             {
1657                 char s[20];
1658                 *pres = Z_PresentStatus_failure;
1659                 /* for 'present request out of range',
1660                    set addinfo to record position if not set */
1661                 if (freq.errcode == 13 && freq.errstring == 0)
1662                 {
1663                     sprintf (s, "%d", recno);
1664                     freq.errstring = s;
1665                 }
1666                 if (errcode)
1667                     *errcode=freq.errcode;
1668                 return diagrec(a, freq.errcode, freq.errstring);
1669             }
1670             reclist->records[reclist->num_records] =
1671                 surrogatediagrec(a, freq.basename, freq.errcode,
1672                                  freq.errstring);
1673             reclist->num_records++;
1674             *next = freq.last_in_set ? 0 : recno + 1;
1675             continue;
1676         }
1677         if (freq.len >= 0)
1678             this_length = freq.len;
1679         else
1680             this_length = odr_total(a->encode) - total_length - dumped_records;
1681         yaz_log(LOG_DEBUG, "  fetched record, len=%d, total=%d dumped=%d",
1682             this_length, total_length, dumped_records);
1683         if (a->preferredMessageSize > 0 &&
1684                 this_length + total_length > a->preferredMessageSize)
1685         {
1686             /* record is small enough, really */
1687             if (this_length <= a->preferredMessageSize && recno > start)
1688             {
1689                 yaz_log(log_requestdetail, "  Dropped last normal-sized record");
1690                 *pres = Z_PresentStatus_partial_2;
1691                 break;
1692             }
1693             /* record can only be fetched by itself */
1694             if (this_length < a->maximumRecordSize)
1695             {
1696                 yaz_log(log_requestdetail, "  Record > prefmsgsz");
1697                 if (toget > 1)
1698                 {
1699                     yaz_log(LOG_DEBUG, "  Dropped it");
1700                     reclist->records[reclist->num_records] =
1701                          surrogatediagrec(a, freq.basename, 16, 0);
1702                     reclist->num_records++;
1703                     *next = freq.last_in_set ? 0 : recno + 1;
1704                     dumped_records += this_length;
1705                     continue;
1706                 }
1707             }
1708             else /* too big entirely */
1709             {
1710                 yaz_log(log_requestdetail, "Record > maxrcdsz this=%d max=%d",
1711                         this_length, a->maximumRecordSize);
1712                 reclist->records[reclist->num_records] =
1713                     surrogatediagrec(a, freq.basename, 17, 0);
1714                 reclist->num_records++;
1715                 *next = freq.last_in_set ? 0 : recno + 1;
1716                 dumped_records += this_length;
1717                 continue;
1718             }
1719         }
1720
1721         if (!(thisrec = (Z_NamePlusRecord *)
1722               odr_malloc(a->encode, sizeof(*thisrec))))
1723             return 0;
1724         if (!(thisrec->databaseName = (char *)odr_malloc(a->encode,
1725             strlen(freq.basename) + 1)))
1726             return 0;
1727         strcpy(thisrec->databaseName, freq.basename);
1728         thisrec->which = Z_NamePlusRecord_databaseRecord;
1729
1730         if (freq.output_format_raw)
1731         {
1732             struct oident *ident = oid_getentbyoid(freq.output_format_raw);
1733             freq.output_format = ident->value;
1734         }
1735         thisrec->u.databaseRecord = z_ext_record(a->encode, freq.output_format,
1736                                                  freq.record, freq.len);
1737         if (!thisrec->u.databaseRecord)
1738             return 0;
1739         reclist->records[reclist->num_records] = thisrec;
1740         reclist->num_records++;
1741         *next = freq.last_in_set ? 0 : recno + 1;
1742     }
1743     *num = reclist->num_records;
1744     return records;
1745 }
1746
1747 static Z_APDU *process_searchRequest(association *assoc, request *reqb,
1748     int *fd)
1749 {
1750     Z_SearchRequest *req = reqb->apdu_request->u.searchRequest;
1751     bend_search_rr *bsrr = 
1752         (bend_search_rr *)nmem_malloc (reqb->request_mem, sizeof(*bsrr));
1753     
1754     yaz_log(log_requestdetail, "Got SearchRequest.");
1755     bsrr->fd = fd;
1756     bsrr->request = reqb;
1757     bsrr->association = assoc;
1758     bsrr->referenceId = req->referenceId;
1759     save_referenceId (reqb, bsrr->referenceId);
1760
1761     yaz_log (log_requestdetail, "ResultSet '%s'", req->resultSetName);
1762     if (req->databaseNames)
1763     {
1764         int i;
1765         for (i = 0; i < req->num_databaseNames; i++)
1766             yaz_log (log_requestdetail, "Database '%s'", req->databaseNames[i]);
1767     }
1768
1769     yaz_log_zquery_level(log_requestdetail,req->query);
1770
1771     if (assoc->init->bend_search)
1772     {
1773         bsrr->setname = req->resultSetName;
1774         bsrr->replace_set = *req->replaceIndicator;
1775         bsrr->num_bases = req->num_databaseNames;
1776         bsrr->basenames = req->databaseNames;
1777         bsrr->query = req->query;
1778         bsrr->stream = assoc->encode;
1779         nmem_transfer(bsrr->stream->mem, reqb->request_mem);
1780         bsrr->decode = assoc->decode;
1781         bsrr->print = assoc->print;
1782         bsrr->errcode = 0;
1783         bsrr->hits = 0;
1784         bsrr->errstring = NULL;
1785         bsrr->search_info = NULL;
1786         (assoc->init->bend_search)(assoc->backend, bsrr);
1787         if (!bsrr->request)  /* backend not ready with the search response */
1788             return 0;  /* should not be used any more */
1789     }
1790     else
1791     { 
1792         /* FIXME - make a diagnostic for it */
1793         yaz_log(LOG_WARN,"Search not supported ?!?!");
1794     }
1795     return response_searchRequest(assoc, reqb, bsrr, fd);
1796 }
1797
1798 int bend_searchresponse(void *handle, bend_search_rr *bsrr) {return 0;}
1799
1800 /*
1801  * Prepare a searchresponse based on the backend results. We probably want
1802  * to look at making the fetching of records nonblocking as well, but
1803  * so far, we'll keep things simple.
1804  * If bsrt is null, that means we're called in response to a communications
1805  * event, and we'll have to get the response for ourselves.
1806  */
1807 static Z_APDU *response_searchRequest(association *assoc, request *reqb,
1808     bend_search_rr *bsrt, int *fd)
1809 {
1810     Z_SearchRequest *req = reqb->apdu_request->u.searchRequest;
1811     Z_APDU *apdu = (Z_APDU *)odr_malloc (assoc->encode, sizeof(*apdu));
1812     Z_SearchResponse *resp = (Z_SearchResponse *)
1813         odr_malloc (assoc->encode, sizeof(*resp));
1814     int *nulint = odr_intdup (assoc->encode, 0);
1815     bool_t *sr = odr_intdup(assoc->encode, 1);
1816     int *next = odr_intdup(assoc->encode, 0);
1817     int *none = odr_intdup(assoc->encode, Z_SearchResponse_none);
1818     int returnedrecs=0;
1819
1820     apdu->which = Z_APDU_searchResponse;
1821     apdu->u.searchResponse = resp;
1822     resp->referenceId = req->referenceId;
1823     resp->additionalSearchInfo = 0;
1824     resp->otherInfo = 0;
1825     *fd = -1;
1826     if (!bsrt && !bend_searchresponse(assoc->backend, bsrt))
1827     {
1828         yaz_log(LOG_FATAL, "Bad result from backend");
1829         return 0;
1830     }
1831     else if (bsrt->errcode)
1832     {
1833         resp->records = diagrec(assoc, bsrt->errcode, bsrt->errstring);
1834         resp->resultCount = nulint;
1835         resp->numberOfRecordsReturned = nulint;
1836         resp->nextResultSetPosition = nulint;
1837         resp->searchStatus = nulint;
1838         resp->resultSetStatus = none;
1839         resp->presentStatus = 0;
1840     }
1841     else
1842     {
1843         int *toget = odr_intdup(assoc->encode, 0);
1844         int *presst = odr_intdup(assoc->encode, 0);
1845         Z_RecordComposition comp, *compp = 0;
1846
1847         yaz_log (log_requestdetail, "resultCount: %d", bsrt->hits);
1848
1849         resp->records = 0;
1850         resp->resultCount = &bsrt->hits;
1851
1852         comp.which = Z_RecordComp_simple;
1853         /* how many records does the user agent want, then? */
1854         if (bsrt->hits <= *req->smallSetUpperBound)
1855         {
1856             *toget = bsrt->hits;
1857             if ((comp.u.simple = req->smallSetElementSetNames))
1858                 compp = &comp;
1859         }
1860         else if (bsrt->hits < *req->largeSetLowerBound)
1861         {
1862             *toget = *req->mediumSetPresentNumber;
1863             if (*toget > bsrt->hits)
1864                 *toget = bsrt->hits;
1865             if ((comp.u.simple = req->mediumSetElementSetNames))
1866                 compp = &comp;
1867         }
1868         else
1869             *toget = 0;
1870
1871         if (*toget && !resp->records)
1872         {
1873             oident *prefformat;
1874             oid_value form;
1875
1876             if (!(prefformat = oid_getentbyoid(req->preferredRecordSyntax)))
1877                 form = VAL_NONE;
1878             else
1879                 form = prefformat->value;
1880             resp->records = pack_records(assoc, req->resultSetName, 1,
1881                 toget, compp, next, presst, form, req->referenceId,
1882                                req->preferredRecordSyntax, NULL);
1883             if (!resp->records)
1884                 return 0;
1885             resp->numberOfRecordsReturned = toget;
1886             returnedrecs=*toget;
1887             resp->nextResultSetPosition = next;
1888             resp->searchStatus = sr;
1889             resp->resultSetStatus = 0;
1890             resp->presentStatus = presst;
1891         }
1892         else
1893         {
1894             if (*resp->resultCount)
1895                 *next = 1;
1896             resp->numberOfRecordsReturned = nulint;
1897             resp->nextResultSetPosition = next;
1898             resp->searchStatus = sr;
1899             resp->resultSetStatus = 0;
1900             resp->presentStatus = 0;
1901         }
1902     }
1903     resp->additionalSearchInfo = bsrt->search_info;
1904
1905     if (log_request)
1906     {
1907         WRBUF wr=wrbuf_alloc();
1908         wrbuf_put_zquery(wr, req->query);
1909         if (bsrt->errcode)
1910             wrbuf_printf(wr," ERROR %d %s", bsrt->errcode, bsrt->errstring);
1911         else
1912         {
1913             wrbuf_printf(wr," OK:%d hits ",bsrt->hits);
1914             if (returnedrecs)
1915                 wrbuf_printf(wr," Returned %d records", returnedrecs);
1916         }
1917         yaz_log(log_request, "Search %s", wrbuf_buf(wr) );
1918         wrbuf_free(wr,1);
1919     }
1920     return apdu;
1921 }
1922
1923 /*
1924  * Maybe we got a little over-friendly when we designed bend_fetch to
1925  * get only one record at a time. Some backends can optimise multiple-record
1926  * fetches, and at any rate, there is some overhead involved in
1927  * all that selecting and hopping around. Problem is, of course, that the
1928  * frontend can't know ahead of time how many records it'll need to
1929  * fill the negotiated PDU size. Annoying. Segmentation or not, Z/SR
1930  * is downright lousy as a bulk data transfer protocol.
1931  *
1932  * To start with, we'll do the fetching of records from the backend
1933  * in one operation: To save some trips in and out of the event-handler,
1934  * and to simplify the interface to pack_records. At any rate, asynch
1935  * operation is more fun in operations that have an unpredictable execution
1936  * speed - which is normally more true for search than for present.
1937  */
1938 static Z_APDU *process_presentRequest(association *assoc, request *reqb,
1939                                       int *fd)
1940 {
1941     Z_PresentRequest *req = reqb->apdu_request->u.presentRequest;
1942     oident *prefformat;
1943     oid_value form;
1944     Z_APDU *apdu;
1945     Z_PresentResponse *resp;
1946     int *next;
1947     int *num;
1948     int errcode=0;
1949
1950     yaz_log(log_requestdetail, "Got PresentRequest.");
1951
1952     if (!(prefformat = oid_getentbyoid(req->preferredRecordSyntax)))
1953         form = VAL_NONE;
1954     else
1955         form = prefformat->value;
1956     resp = (Z_PresentResponse *)odr_malloc (assoc->encode, sizeof(*resp));
1957     resp->records = 0;
1958     resp->presentStatus = odr_intdup(assoc->encode, 0);
1959     if (assoc->init->bend_present)
1960     {
1961         bend_present_rr *bprr = (bend_present_rr *)
1962             nmem_malloc (reqb->request_mem, sizeof(*bprr));
1963         bprr->setname = req->resultSetId;
1964         bprr->start = *req->resultSetStartPoint;
1965         bprr->number = *req->numberOfRecordsRequested;
1966         bprr->format = form;
1967         bprr->comp = req->recordComposition;
1968         bprr->referenceId = req->referenceId;
1969         bprr->stream = assoc->encode;
1970         bprr->print = assoc->print;
1971         bprr->request = reqb;
1972         bprr->association = assoc;
1973         bprr->errcode = 0;
1974         bprr->errstring = NULL;
1975         (*assoc->init->bend_present)(assoc->backend, bprr);
1976         
1977         if (!bprr->request)
1978             return 0; /* should not happen */
1979         if (bprr->errcode)
1980         {
1981             resp->records = diagrec(assoc, bprr->errcode, bprr->errstring);
1982             *resp->presentStatus = Z_PresentStatus_failure;
1983             errcode=bprr->errcode;
1984         }
1985     }
1986     apdu = (Z_APDU *)odr_malloc (assoc->encode, sizeof(*apdu));
1987     next = odr_intdup(assoc->encode, 0);
1988     num = odr_intdup(assoc->encode, 0);
1989     
1990     apdu->which = Z_APDU_presentResponse;
1991     apdu->u.presentResponse = resp;
1992     resp->referenceId = req->referenceId;
1993     resp->otherInfo = 0;
1994     
1995     if (!resp->records)
1996     {
1997         *num = *req->numberOfRecordsRequested;
1998         resp->records =
1999             pack_records(assoc, req->resultSetId, *req->resultSetStartPoint,
2000                      num, req->recordComposition, next, resp->presentStatus,
2001                          form, req->referenceId, req->preferredRecordSyntax, 
2002                          &errcode);
2003     }
2004     if (!resp->records)
2005         return 0;
2006     resp->numberOfRecordsReturned = num;
2007     resp->nextResultSetPosition = next;
2008     if (log_request)
2009     {
2010         WRBUF wr=wrbuf_alloc();
2011         wrbuf_printf(wr, "Present: [%s] %d+%d ",
2012                 req->resultSetId, *req->resultSetStartPoint,
2013                 *req->numberOfRecordsRequested);
2014         if (*resp->presentStatus == Z_PresentStatus_failure)
2015         {
2016             wrbuf_printf(wr," ERROR %d ", errcode);
2017         }
2018         else if (*resp->presentStatus == Z_PresentStatus_success)
2019             wrbuf_printf(wr," OK %d records returned ", *num);
2020         else
2021             wrbuf_printf(wr," Partial (%d) OK %d records returned ", 
2022                     *resp->presentStatus, *num);
2023         yaz_log(log_request, "%s", wrbuf_buf(wr) );
2024         wrbuf_free(wr,1);
2025     }
2026     
2027     return apdu;
2028 }
2029
2030 /*
2031  * Scan was implemented rather in a hurry, and with support for only the basic
2032  * elements of the service in the backend API. Suggestions are welcome.
2033  */
2034 static Z_APDU *process_scanRequest(association *assoc, request *reqb, int *fd)
2035 {
2036     Z_ScanRequest *req = reqb->apdu_request->u.scanRequest;
2037     Z_APDU *apdu = (Z_APDU *)odr_malloc (assoc->encode, sizeof(*apdu));
2038     Z_ScanResponse *res = (Z_ScanResponse *)
2039         odr_malloc (assoc->encode, sizeof(*res));
2040     int *scanStatus = odr_intdup(assoc->encode, Z_Scan_failure);
2041     int *numberOfEntriesReturned = odr_intdup(assoc->encode, 0);
2042     Z_ListEntries *ents = (Z_ListEntries *)
2043         odr_malloc (assoc->encode, sizeof(*ents));
2044     Z_DiagRecs *diagrecs_p = NULL;
2045     oident *attset;
2046     bend_scan_rr *bsrr = (bend_scan_rr *)
2047         odr_malloc (assoc->encode, sizeof(*bsrr));
2048     struct scan_entry *save_entries;
2049
2050     yaz_log(log_requestdetail, "Got ScanRequest");
2051
2052     apdu->which = Z_APDU_scanResponse;
2053     apdu->u.scanResponse = res;
2054     res->referenceId = req->referenceId;
2055
2056     /* if step is absent, set it to 0 */
2057     res->stepSize = odr_intdup(assoc->encode, 0);
2058     if (req->stepSize)
2059         *res->stepSize = *req->stepSize;
2060
2061     res->scanStatus = scanStatus;
2062     res->numberOfEntriesReturned = numberOfEntriesReturned;
2063     res->positionOfTerm = 0;
2064     res->entries = ents;
2065     ents->num_entries = 0;
2066     ents->entries = NULL;
2067     ents->num_nonsurrogateDiagnostics = 0;
2068     ents->nonsurrogateDiagnostics = NULL;
2069     res->attributeSet = 0;
2070     res->otherInfo = 0;
2071
2072     if (req->databaseNames)
2073     {
2074         int i;
2075         for (i = 0; i < req->num_databaseNames; i++)
2076             yaz_log (log_requestdetail, "Database '%s'", req->databaseNames[i]);
2077     }
2078     yaz_log(log_requestdetail, "pos %d  step %d  entries %d",
2079             *req->preferredPositionInResponse, *res->stepSize, 
2080             *req->numberOfTermsRequested);
2081     bsrr->num_bases = req->num_databaseNames;
2082     bsrr->basenames = req->databaseNames;
2083     bsrr->num_entries = *req->numberOfTermsRequested;
2084     bsrr->term = req->termListAndStartPoint;
2085     bsrr->referenceId = req->referenceId;
2086     bsrr->stream = assoc->encode;
2087     bsrr->print = assoc->print;
2088     bsrr->step_size = res->stepSize;
2089     bsrr->entries = 0;
2090     /* Note that version 2.0 of YAZ and older did not set entries .. 
2091        We do now. And when we do it's easier to extend the scan entry 
2092        We know that if the scan handler did set entries, it will
2093        not know of new member display_term.
2094     */
2095     if (bsrr->num_entries > 0) 
2096     {
2097         int i;
2098         bsrr->entries = odr_malloc(assoc->decode, sizeof(*bsrr->entries) *
2099                                    bsrr->num_entries);
2100         for (i = 0; i<bsrr->num_entries; i++)
2101         {
2102             bsrr->entries[i].term = 0;
2103             bsrr->entries[i].occurrences = 0;
2104             bsrr->entries[i].errcode = 0;
2105             bsrr->entries[i].errstring = 0;
2106             bsrr->entries[i].display_term = 0;
2107         }
2108     }
2109     save_entries = bsrr->entries;  /* save it so we can compare later */
2110
2111     if (req->attributeSet &&
2112         (attset = oid_getentbyoid(req->attributeSet)) &&
2113         (attset->oclass == CLASS_ATTSET || attset->oclass == CLASS_GENERAL))
2114         bsrr->attributeset = attset->value;
2115     else
2116         bsrr->attributeset = VAL_NONE;
2117     log_scan_term_level (log_requestdetail, req->termListAndStartPoint, 
2118             bsrr->attributeset);
2119     bsrr->term_position = req->preferredPositionInResponse ?
2120         *req->preferredPositionInResponse : 1;
2121
2122     ((int (*)(void *, bend_scan_rr *))
2123      (*assoc->init->bend_scan))(assoc->backend, bsrr);
2124
2125     if (bsrr->errcode)
2126         diagrecs_p = diagrecs(assoc, bsrr->errcode, bsrr->errstring);
2127     else
2128     {
2129         int i;
2130         Z_Entry **tab = (Z_Entry **)
2131             odr_malloc (assoc->encode, sizeof(*tab) * bsrr->num_entries);
2132         
2133         if (bsrr->status == BEND_SCAN_PARTIAL)
2134             *scanStatus = Z_Scan_partial_5;
2135         else
2136             *scanStatus = Z_Scan_success;
2137         ents->entries = tab;
2138         ents->num_entries = bsrr->num_entries;
2139         res->numberOfEntriesReturned = &ents->num_entries;          
2140         res->positionOfTerm = &bsrr->term_position;
2141         for (i = 0; i < bsrr->num_entries; i++)
2142         {
2143             Z_Entry *e;
2144             Z_TermInfo *t;
2145             Odr_oct *o;
2146             
2147             tab[i] = e = (Z_Entry *)odr_malloc(assoc->encode, sizeof(*e));
2148             if (bsrr->entries[i].occurrences >= 0)
2149             {
2150                 e->which = Z_Entry_termInfo;
2151                 e->u.termInfo = t = (Z_TermInfo *)
2152                     odr_malloc(assoc->encode, sizeof(*t));
2153                 t->suggestedAttributes = 0;
2154                 t->displayTerm = 0;
2155                 if (save_entries == bsrr->entries && 
2156                     bsrr->entries[i].display_term)
2157                 {
2158                     /* the entries was NOT set by the handler. So it's
2159                        safe to test for new member display_term. It is
2160                        NULL'ed by us.
2161                     */
2162                     t->displayTerm = odr_strdup(assoc->encode,
2163                                                 bsrr->entries[i].display_term);
2164                 }
2165                 t->alternativeTerm = 0;
2166                 t->byAttributes = 0;
2167                 t->otherTermInfo = 0;
2168                 t->globalOccurrences = &bsrr->entries[i].occurrences;
2169                 t->term = (Z_Term *)
2170                     odr_malloc(assoc->encode, sizeof(*t->term));
2171                 t->term->which = Z_Term_general;
2172                 t->term->u.general = o =
2173                     (Odr_oct *)odr_malloc(assoc->encode, sizeof(Odr_oct));
2174                 o->buf = (unsigned char *)
2175                     odr_malloc(assoc->encode, o->len = o->size =
2176                                strlen(bsrr->entries[i].term));
2177                 memcpy(o->buf, bsrr->entries[i].term, o->len);
2178                 yaz_log(LOG_DEBUG, "  term #%d: '%s' (%d)", i,
2179                          bsrr->entries[i].term, bsrr->entries[i].occurrences);
2180             }
2181             else
2182             {
2183                 Z_DiagRecs *drecs = diagrecs (assoc,
2184                                               bsrr->entries[i].errcode,
2185                                               bsrr->entries[i].errstring);
2186                 assert (drecs->num_diagRecs == 1);
2187                 e->which = Z_Entry_surrogateDiagnostic;
2188                 assert (drecs->diagRecs[0]);
2189                 e->u.surrogateDiagnostic = drecs->diagRecs[0];
2190             }
2191         }
2192     }
2193     if (diagrecs_p)
2194     {
2195         ents->num_nonsurrogateDiagnostics = diagrecs_p->num_diagRecs;
2196         ents->nonsurrogateDiagnostics = diagrecs_p->diagRecs;
2197     }
2198     if (log_request)
2199     {
2200         WRBUF wr=wrbuf_alloc();
2201         wrbuf_printf(wr, "Scan  %d@%d ",
2202             *req->preferredPositionInResponse, 
2203             *req->numberOfTermsRequested);
2204         if (*res->stepSize)
2205             wrbuf_printf(wr, "(step %d) ",*res->stepSize);
2206         wrbuf_scan_term(wr, req->termListAndStartPoint, 
2207             bsrr->attributeset);
2208         
2209         if (*res->scanStatus == Z_Scan_success)
2210         {
2211             wrbuf_printf(wr," OK");
2212         }
2213         else 
2214             wrbuf_printf(wr," Error");
2215         yaz_log(log_request, "%s", wrbuf_buf(wr) );
2216         wrbuf_free(wr,1);
2217     }
2218     return apdu;
2219 }
2220
2221 static Z_APDU *process_sortRequest(association *assoc, request *reqb,
2222     int *fd)
2223 {
2224     int i;
2225     Z_SortRequest *req = reqb->apdu_request->u.sortRequest;
2226     Z_SortResponse *res = (Z_SortResponse *)
2227         odr_malloc (assoc->encode, sizeof(*res));
2228     bend_sort_rr *bsrr = (bend_sort_rr *)
2229         odr_malloc (assoc->encode, sizeof(*bsrr));
2230
2231     Z_APDU *apdu = (Z_APDU *)odr_malloc (assoc->encode, sizeof(*apdu));
2232
2233     yaz_log(log_requestdetail, "Got SortRequest.");
2234
2235     bsrr->num_input_setnames = req->num_inputResultSetNames;
2236     for (i=0;i<req->num_inputResultSetNames;i++)
2237         yaz_log(log_requestdetail, "Input resultset: '%s'",
2238                 req->inputResultSetNames[i]);
2239     bsrr->input_setnames = req->inputResultSetNames;
2240     bsrr->referenceId = req->referenceId;
2241     bsrr->output_setname = req->sortedResultSetName;
2242     yaz_log(log_requestdetail, "Output resultset: '%s'",
2243                 req->sortedResultSetName);
2244     bsrr->sort_sequence = req->sortSequence;
2245        /*FIXME - dump those sequences too */
2246     bsrr->stream = assoc->encode;
2247     bsrr->print = assoc->print;
2248
2249     bsrr->sort_status = Z_SortResponse_failure;
2250     bsrr->errcode = 0;
2251     bsrr->errstring = 0;
2252     
2253     (*assoc->init->bend_sort)(assoc->backend, bsrr);
2254     
2255     res->referenceId = bsrr->referenceId;
2256     res->sortStatus = odr_intdup(assoc->encode, bsrr->sort_status);
2257     res->resultSetStatus = 0;
2258     if (bsrr->errcode)
2259     {
2260         Z_DiagRecs *dr = diagrecs (assoc, bsrr->errcode, bsrr->errstring);
2261         res->diagnostics = dr->diagRecs;
2262         res->num_diagnostics = dr->num_diagRecs;
2263     }
2264     else
2265     {
2266         res->num_diagnostics = 0;
2267         res->diagnostics = 0;
2268     }
2269     res->resultCount = 0;
2270     res->otherInfo = 0;
2271
2272     apdu->which = Z_APDU_sortResponse;
2273     apdu->u.sortResponse = res;
2274     if (log_request)
2275     {
2276         WRBUF wr=wrbuf_alloc();
2277         wrbuf_printf(wr, "Sort (");
2278         for (i=0;i<req->num_inputResultSetNames;i++)
2279         {
2280             if (i)
2281                 wrbuf_printf(wr,",");
2282             wrbuf_printf(wr, req->inputResultSetNames[i]);
2283         }
2284         wrbuf_printf(wr,")->%s ",req->sortedResultSetName);
2285
2286         /* FIXME - dump also the sort sequence */
2287         if (bsrr->errcode)
2288             wrbuf_diags(wr, res->num_diagnostics, res->diagnostics);
2289         else
2290             wrbuf_printf(wr," OK");
2291         yaz_log(log_request, "%s", wrbuf_buf(wr) );
2292         wrbuf_free(wr,1);
2293     }
2294     return apdu;
2295 }
2296
2297 static Z_APDU *process_deleteRequest(association *assoc, request *reqb,
2298     int *fd)
2299 {
2300     int i;
2301     Z_DeleteResultSetRequest *req =
2302         reqb->apdu_request->u.deleteResultSetRequest;
2303     Z_DeleteResultSetResponse *res = (Z_DeleteResultSetResponse *)
2304         odr_malloc (assoc->encode, sizeof(*res));
2305     bend_delete_rr *bdrr = (bend_delete_rr *)
2306         odr_malloc (assoc->encode, sizeof(*bdrr));
2307     Z_APDU *apdu = (Z_APDU *)odr_malloc (assoc->encode, sizeof(*apdu));
2308
2309     yaz_log(log_requestdetail, "Got DeleteRequest.");
2310
2311     bdrr->num_setnames = req->num_resultSetList;
2312     bdrr->setnames = req->resultSetList;
2313     for (i=0;i<req->num_resultSetList;i++)
2314         yaz_log(log_requestdetail, "resultset: '%s'",
2315                 req->resultSetList[i]);
2316     bdrr->stream = assoc->encode;
2317     bdrr->print = assoc->print;
2318     bdrr->function = *req->deleteFunction;
2319     bdrr->referenceId = req->referenceId;
2320     bdrr->statuses = 0;
2321     if (bdrr->num_setnames > 0)
2322     {
2323         bdrr->statuses = (int*) 
2324             odr_malloc(assoc->encode, sizeof(*bdrr->statuses) *
2325                        bdrr->num_setnames);
2326         for (i = 0; i < bdrr->num_setnames; i++)
2327             bdrr->statuses[i] = 0;
2328     }
2329     (*assoc->init->bend_delete)(assoc->backend, bdrr);
2330     
2331     res->referenceId = req->referenceId;
2332
2333     res->deleteOperationStatus = odr_intdup(assoc->encode,bdrr->delete_status);
2334
2335     res->deleteListStatuses = 0;
2336     if (bdrr->num_setnames > 0)
2337     {
2338         int i;
2339         res->deleteListStatuses = (Z_ListStatuses *)
2340             odr_malloc(assoc->encode, sizeof(*res->deleteListStatuses));
2341         res->deleteListStatuses->num = bdrr->num_setnames;
2342         res->deleteListStatuses->elements =
2343             (Z_ListStatus **)
2344             odr_malloc (assoc->encode, 
2345                         sizeof(*res->deleteListStatuses->elements) *
2346                         bdrr->num_setnames);
2347         for (i = 0; i<bdrr->num_setnames; i++)
2348         {
2349             res->deleteListStatuses->elements[i] =
2350                 (Z_ListStatus *)
2351                 odr_malloc (assoc->encode,
2352                             sizeof(**res->deleteListStatuses->elements));
2353             res->deleteListStatuses->elements[i]->status = bdrr->statuses+i;
2354             res->deleteListStatuses->elements[i]->id =
2355                 odr_strdup (assoc->encode, bdrr->setnames[i]);
2356             
2357         }
2358     }
2359     res->numberNotDeleted = 0;
2360     res->bulkStatuses = 0;
2361     res->deleteMessage = 0;
2362     res->otherInfo = 0;
2363
2364     apdu->which = Z_APDU_deleteResultSetResponse;
2365     apdu->u.deleteResultSetResponse = res;
2366     if (log_request)
2367     {
2368         WRBUF wr=wrbuf_alloc();
2369         wrbuf_printf(wr, "Delete ");
2370         for (i=0;i<req->num_resultSetList;i++)
2371             wrbuf_printf(wr, " '%s' ", req->resultSetList[i]);
2372         if (bdrr->delete_status)
2373             wrbuf_printf(wr," ERROR %d", bdrr->delete_status);
2374         else
2375             wrbuf_printf(wr,"OK");
2376         yaz_log(log_request, "%s", wrbuf_buf(wr) );
2377         wrbuf_free(wr,1);
2378     }
2379     return apdu;
2380 }
2381
2382 static void process_close(association *assoc, request *reqb)
2383 {
2384     Z_Close *req = reqb->apdu_request->u.close;
2385     static char *reasons[] =
2386     {
2387         "finished",
2388         "shutdown",
2389         "systemProblem",
2390         "costLimit",
2391         "resources",
2392         "securityViolation",
2393         "protocolError",
2394         "lackOfActivity",
2395         "peerAbort",
2396         "unspecified"
2397     };
2398
2399     yaz_log(log_requestdetail, "Got Close, reason %s, message %s",
2400         reasons[*req->closeReason], req->diagnosticInformation ?
2401         req->diagnosticInformation : "NULL");
2402     if (assoc->version < 3) /* to make do_force respond with close */
2403         assoc->version = 3;
2404     do_close_req(assoc, Z_Close_finished,
2405                  "Association terminated by client", reqb);
2406     yaz_log(log_request,"Close OK");
2407 }
2408
2409 void save_referenceId (request *reqb, Z_ReferenceId *refid)
2410 {
2411     if (refid)
2412     {
2413         reqb->len_refid = refid->len;
2414         reqb->refid = (char *)nmem_malloc (reqb->request_mem, refid->len);
2415         memcpy (reqb->refid, refid->buf, refid->len);
2416     }
2417     else
2418     {
2419         reqb->len_refid = 0;
2420         reqb->refid = NULL;
2421     }
2422 }
2423
2424 void bend_request_send (bend_association a, bend_request req, Z_APDU *res)
2425 {
2426     process_z_response (a, req, res);
2427 }
2428
2429 bend_request bend_request_mk (bend_association a)
2430 {
2431     request *nreq = request_get (&a->outgoing);
2432     nreq->request_mem = nmem_create ();
2433     return nreq;
2434 }
2435
2436 Z_ReferenceId *bend_request_getid (ODR odr, bend_request req)
2437 {
2438     Z_ReferenceId *id;
2439     if (!req->refid)
2440         return 0;
2441     id = (Odr_oct *)odr_malloc (odr, sizeof(*odr));
2442     id->buf = (unsigned char *)odr_malloc (odr, req->len_refid);
2443     id->len = id->size = req->len_refid;
2444     memcpy (id->buf, req->refid, req->len_refid);
2445     return id;
2446 }
2447
2448 void bend_request_destroy (bend_request *req)
2449 {
2450     nmem_destroy((*req)->request_mem);
2451     request_release(*req);
2452     *req = NULL;
2453 }
2454
2455 int bend_backend_respond (bend_association a, bend_request req)
2456 {
2457     char *msg;
2458     int r;
2459     r = process_z_request (a, req, &msg);
2460     if (r < 0)
2461         yaz_log (LOG_WARN, "%s", msg);
2462     return r;
2463 }
2464
2465 void bend_request_setdata(bend_request r, void *p)
2466 {
2467     r->clientData = p;
2468 }
2469
2470 void *bend_request_getdata(bend_request r)
2471 {
2472     return r->clientData;
2473 }
2474
2475 static Z_APDU *process_segmentRequest (association *assoc, request *reqb)
2476 {
2477     bend_segment_rr req;
2478
2479     req.segment = reqb->apdu_request->u.segmentRequest;
2480     req.stream = assoc->encode;
2481     req.decode = assoc->decode;
2482     req.print = assoc->print;
2483     req.association = assoc;
2484     
2485     (*assoc->init->bend_segment)(assoc->backend, &req);
2486
2487     return 0;
2488 }
2489
2490 static Z_APDU *process_ESRequest(association *assoc, request *reqb, int *fd)
2491 {
2492     bend_esrequest_rr esrequest;
2493
2494     Z_ExtendedServicesRequest *req =
2495         reqb->apdu_request->u.extendedServicesRequest;
2496     Z_APDU *apdu = zget_APDU(assoc->encode, Z_APDU_extendedServicesResponse);
2497
2498     Z_ExtendedServicesResponse *resp = apdu->u.extendedServicesResponse;
2499
2500     yaz_log(log_requestdetail,"Got EsRequest");
2501
2502     esrequest.esr = reqb->apdu_request->u.extendedServicesRequest;
2503     esrequest.stream = assoc->encode;
2504     esrequest.decode = assoc->decode;
2505     esrequest.print = assoc->print;
2506     esrequest.errcode = 0;
2507     esrequest.errstring = NULL;
2508     esrequest.request = reqb;
2509     esrequest.association = assoc;
2510     esrequest.taskPackage = 0;
2511     esrequest.referenceId = req->referenceId;
2512     
2513     (*assoc->init->bend_esrequest)(assoc->backend, &esrequest);
2514     
2515     /* If the response is being delayed, return NULL */
2516     if (esrequest.request == NULL)
2517         return(NULL);
2518
2519     resp->referenceId = req->referenceId;
2520
2521     if (esrequest.errcode == -1)
2522     {
2523         /* Backend service indicates request will be processed */
2524         yaz_log(log_request,"EsRequest OK: Accepted !");
2525         *resp->operationStatus = Z_ExtendedServicesResponse_accepted;
2526     }
2527     else if (esrequest.errcode == 0)
2528     {
2529         /* Backend service indicates request will be processed */
2530         yaz_log(log_request,"EsRequest OK: Done !");
2531         *resp->operationStatus = Z_ExtendedServicesResponse_done;
2532     }
2533     else
2534     {
2535         Z_DiagRecs *diagRecs = diagrecs (assoc, esrequest.errcode,
2536                                          esrequest.errstring);
2537
2538         /* Backend indicates error, request will not be processed */
2539         yaz_log(LOG_DEBUG,"Request could not be processed...failure !");
2540         *resp->operationStatus = Z_ExtendedServicesResponse_failure;
2541         resp->num_diagnostics = diagRecs->num_diagRecs;
2542         resp->diagnostics = diagRecs->diagRecs;
2543         if (log_request)
2544         {
2545             WRBUF wr=wrbuf_alloc();
2546             wrbuf_diags(wr, resp->num_diagnostics, resp->diagnostics);
2547             yaz_log(log_request, "EsRequest %s", wrbuf_buf(wr) );
2548             wrbuf_free(wr,1);
2549         }
2550
2551     }
2552     /* Do something with the members of bend_extendedservice */
2553     if (esrequest.taskPackage)
2554         resp->taskPackage = z_ext_record (assoc->encode, VAL_EXTENDED,
2555                                          (const char *)  esrequest.taskPackage,
2556                                           -1);
2557     yaz_log(LOG_DEBUG,"Send the result apdu");
2558     return apdu;
2559 }
2560