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