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