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