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