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