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