Implemented delete result set service to server API.
[yaz-moved-to-github.git] / server / seshigh.c
1 /*
2  * Copyright (c) 1995-1998, Index Data
3  * See the file LICENSE for details.
4  * Sebastian Hammer, Adam Dickmeiss
5  *
6  * $Log: seshigh.c,v $
7  * Revision 1.77  1998-07-20 12:38:42  adam
8  * Implemented delete result set service to server API.
9  *
10  * Revision 1.76  1998/05/27 16:57:07  adam
11  * Support for surrogate diagnostic records added for bend_fetch.
12  *
13  * Revision 1.75  1998/05/18 10:13:07  adam
14  * Fixed call to es_request handler - extra argument was passed.
15  *
16  * Revision 1.74  1998/03/31 15:13:20  adam
17  * Development towards compiled ASN.1.
18  *
19  * Revision 1.73  1998/03/31 11:07:45  adam
20  * Furhter work on UNIverse resource report.
21  * Added Extended Services handling in frontend server.
22  *
23  * Revision 1.72  1998/02/11 11:53:35  adam
24  * Changed code so that it compiles as C++.
25  *
26  * Revision 1.71  1998/02/10 11:03:57  adam
27  * Added support for extended handlers in backend server interface.
28  *
29  * Revision 1.70  1998/01/29 13:15:35  adam
30  * Implemented sort for the backend interface.
31  *
32  * Revision 1.69  1997/09/30 11:48:12  adam
33  * Fixed bug introduced by previous commit.
34  *
35  * Revision 1.68  1997/09/29 13:18:59  adam
36  * Added function, oid_ent_to_oid, to replace the function
37  * oid_getoidbyent, which is not thread safe.
38  *
39  * Revision 1.67  1997/09/17 12:10:40  adam
40  * YAZ version 1.4.
41  *
42  * Revision 1.66  1997/09/05 15:26:44  adam
43  * Added ODR encode in search and scen bend request structures.
44  * Fixed a few enums that caused trouble with C++.
45  *
46  * Revision 1.65  1997/09/01 08:53:01  adam
47  * New windows NT/95 port using MSV5.0. The test server 'ztest' was
48  * moved a separate directory. MSV5.0 project server.dsp created.
49  * As an option, the server can now operate as an NT service.
50  *
51  * Revision 1.64  1997/04/30 08:52:11  quinn
52  * Null
53  *
54  * Revision 1.63  1996/10/11  11:57:26  quinn
55  * Smallish
56  *
57  * Revision 1.62  1996/07/06  19:58:35  quinn
58  * System headerfiles gathered in yconfig
59  *
60  * Revision 1.61  1996/06/10  08:56:16  quinn
61  * Work on Summary.
62  *
63  * Revision 1.60  1996/05/30  11:03:10  quinn
64  * Fixed NextresultSetPosition bug fixed.
65  *
66  * Revision 1.59  1996/05/14  09:26:46  quinn
67  * Added attribute set to scan backend
68  *
69  * Revision 1.58  1996/02/20  12:53:04  quinn
70  * Chanes to SCAN
71  *
72  * Revision 1.57  1996/01/02  08:57:47  quinn
73  * Changed enums in the ASN.1 .h files to #defines. Changed oident.class to oclass
74  *
75  * Revision 1.56  1995/12/14  11:09:57  quinn
76  * Work on Explain
77  *
78  * Revision 1.55  1995/11/08  17:41:37  quinn
79  * Smallish.
80  *
81  * Revision 1.54  1995/11/08  15:11:29  quinn
82  * Log of close transmit.
83  *
84  * Revision 1.53  1995/11/01  13:54:58  quinn
85  * Minor adjustments
86  *
87  * Revision 1.52  1995/11/01  12:19:13  quinn
88  * Second attempt to fix same bug.
89  *
90  * Revision 1.50  1995/10/25  16:58:32  quinn
91  * Simple.
92  *
93  * Revision 1.49  1995/10/16  13:51:53  quinn
94  * Changes to provide Especs to the backend.
95  *
96  * Revision 1.48  1995/10/06  08:51:20  quinn
97  * Added Write-buffer.
98  *
99  * Revision 1.47  1995/08/29  14:24:16  quinn
100  * Added second half of close-handshake
101  *
102  * Revision 1.46  1995/08/29  11:17:58  quinn
103  * Added code to receive close
104  *
105  * Revision 1.45  1995/08/21  09:11:00  quinn
106  * Smallish fixes to suppport new formats.
107  *
108  * Revision 1.44  1995/08/17  12:45:25  quinn
109  * Fixed minor problems with GRS-1. Added support in c&s.
110  *
111  * Revision 1.43  1995/08/15  12:00:31  quinn
112  * Updated External
113  *
114  * Revision 1.42  1995/08/15  11:16:50  quinn
115  *
116  * Revision 1.41  1995/08/02  10:23:06  quinn
117  * Smallish
118  *
119  * Revision 1.40  1995/07/31  14:34:26  quinn
120  * Fixed bug in process_searchResponse (numberOfRecordsReturned).
121  *
122  * Revision 1.39  1995/06/27  13:21:00  quinn
123  * SUTRS support
124  *
125  * Revision 1.38  1995/06/19  12:39:11  quinn
126  * Fixed bug in timeout code. Added BER dumper.
127  *
128  * Revision 1.37  1995/06/16  13:16:14  quinn
129  * Fixed Defaultdiagformat.
130  *
131  * Revision 1.36  1995/06/16  10:31:36  quinn
132  * Added session timeout.
133  *
134  * Revision 1.35  1995/06/15  07:45:14  quinn
135  * Moving to v3.
136  *
137  * Revision 1.34  1995/06/14  15:26:46  quinn
138  * *** empty log message ***
139  *
140  * Revision 1.33  1995/06/06  14:57:05  quinn
141  * Better diagnostics.
142  *
143  * Revision 1.32  1995/06/06  08:41:44  quinn
144  * Better diagnostics.
145  *
146  * Revision 1.31  1995/06/06  08:15:37  quinn
147  * Cosmetic.
148  *
149  * Revision 1.30  1995/06/05  10:53:32  quinn
150  * Added a better SCAN.
151  *
152  * Revision 1.29  1995/06/01  11:25:03  quinn
153  * Smallish.
154  *
155  * Revision 1.28  1995/06/01  11:21:01  quinn
156  * Attempting to fix a bug in pack-records. replaced break with continue
157  * for large records, according to standard.
158  *
159  * Revision 1.27  1995/05/29  08:12:06  quinn
160  * Moved oid to util
161  *
162  * Revision 1.26  1995/05/18  13:02:12  quinn
163  * Smallish.
164  *
165  * Revision 1.25  1995/05/17  08:42:26  quinn
166  * Transfer auth info to backend. Allow backend to reject init gracefully.
167  *
168  * Revision 1.24  1995/05/16  08:51:04  quinn
169  * License, documentation, and memory fixes
170  *
171  * Revision 1.23  1995/05/15  13:25:10  quinn
172  * Fixed memory bug.
173  *
174  * Revision 1.22  1995/05/15  11:56:39  quinn
175  * Asynchronous facilities. Restructuring of seshigh code.
176  *
177  * Revision 1.21  1995/05/02  08:53:19  quinn
178  * Trying in vain to fix comm with ISODE
179  *
180  * Revision 1.20  1995/04/20  15:13:00  quinn
181  * Cosmetic
182  *
183  * Revision 1.19  1995/04/18  08:15:34  quinn
184  * Added dynamic memory allocation on encoding (whew). Code is now somewhat
185  * neater. We'll make the same change for decoding one day.
186  *
187  * Revision 1.18  1995/04/17  11:28:25  quinn
188  * Smallish
189  *
190  * Revision 1.17  1995/04/10  10:23:36  quinn
191  * Some work to add scan and other things.
192  *
193  * Revision 1.16  1995/03/31  09:18:55  quinn
194  * Added logging.
195  *
196  * Revision 1.15  1995/03/30  14:03:23  quinn
197  * Added RFC1006 as separate library
198  *
199  * Revision 1.14  1995/03/30  12:18:17  quinn
200  * Fixed bug.
201  *
202  * Revision 1.13  1995/03/30  09:09:24  quinn
203  * Added state-handle and some support for asynchronous activities.
204  *
205  * Revision 1.12  1995/03/29  15:40:16  quinn
206  * Ongoing work. Statserv is now dynamic by default
207  *
208  * Revision 1.11  1995/03/28  09:16:21  quinn
209  * Added record packing to the search request
210  *
211  * Revision 1.10  1995/03/27  08:34:24  quinn
212  * Added dynamic server functionality.
213  * Released bindings to session.c (is now redundant)
214  *
215  * Revision 1.9  1995/03/22  15:01:26  quinn
216  * Adjusting record packing.
217  *
218  * Revision 1.8  1995/03/22  10:13:21  quinn
219  * Working on record packer
220  *
221  * Revision 1.7  1995/03/21  15:53:31  quinn
222  * Little changes.
223  *
224  * Revision 1.6  1995/03/21  12:30:09  quinn
225  * Beginning to add support for record packing.
226  *
227  * Revision 1.5  1995/03/17  10:44:13  quinn
228  * Added catch of null-string in makediagrec
229  *
230  * Revision 1.4  1995/03/17  10:18:08  quinn
231  * Added memory management.
232  *
233  * Revision 1.3  1995/03/16  17:42:39  quinn
234  * Little changes
235  *
236  * Revision 1.2  1995/03/16  13:29:01  quinn
237  * Partitioned server.
238  *
239  * Revision 1.1  1995/03/15  16:02:10  quinn
240  * Modded session.c to seshigh.c
241  *
242  */
243
244 /*
245  * Frontend server logic.
246  *
247  * This code receives incoming APDUs, and handles client requests by means
248  * of the backend API.
249  *
250  * Some of the code is getting quite involved, compared to simpler servers -
251  * primarily because it is asynchronous both in the communication with
252  * the user and the backend. We think the complexity will pay off in
253  * the form of greater flexibility when more asynchronous facilities
254  * are implemented.
255  *
256  * Memory management has become somewhat involved. In the simple case, where
257  * only one PDU is pending at a time, it will simply reuse the same memory,
258  * once it has found its working size. When we enable multiple concurrent
259  * operations, perhaps even with multiple parallel calls to the backend, it
260  * will maintain a pool of buffers for encoding and decoding, trying to
261  * minimize memory allocation/deallocation during normal operation.
262  *
263  * TODOs include (and will be done in order of public interest):
264  * 
265  * Support for EXPLAIN - provide simple meta-database system.
266  * Support for access control.
267  * Support for resource control.
268  * Support for extended services - primarily Item Order.
269  * Rest of Z39.50-1994
270  *
271  */
272
273 #include <yconfig.h>
274 #include <stdlib.h>
275 #include <stdio.h>
276 #ifdef WINDOWS
277 #include <process.h>
278 #else
279 #include <unistd.h>
280 #endif
281 #include <assert.h>
282
283 #include <xmalloc.h>
284 #include <comstack.h>
285 #include "eventl.h"
286 #include "session.h"
287 #include <proto.h>
288 #include <oid.h>
289 #include <log.h>
290 #include <statserv.h>
291
292 #include <backend.h>
293
294 static int process_request(association *assoc, request *req);
295 void backend_response(IOCHAN i, int event);
296 static int process_response(association *assoc, request *req, Z_APDU *res);
297 static Z_APDU *process_initRequest(association *assoc, request *reqb);
298 static Z_APDU *process_searchRequest(association *assoc, request *reqb,
299     int *fd);
300 static Z_APDU *response_searchRequest(association *assoc, request *reqb,
301     bend_search_rr *bsrr, int *fd);
302 static Z_APDU *process_presentRequest(association *assoc, request *reqb,
303     int *fd);
304 static Z_APDU *process_scanRequest(association *assoc, request *reqb, int *fd);
305 static Z_APDU *process_sortRequest(association *assoc, request *reqb, int *fd);
306 static void process_close(association *assoc, request *reqb);
307 void save_referenceId (request *reqb, Z_ReferenceId *refid);
308 static Z_APDU *process_deleteRequest(association *assoc, request *reqb,
309     int *fd);
310
311 static FILE *apduf = 0; /* for use in static mode */
312 static statserv_options_block *control_block = 0;
313
314 /* Chas: Added in from DALI */
315 static Z_APDU *process_ESRequest(association *assoc, request *reqb, int *fd);
316 /* Chas: End of addition from DALI */
317
318 /*
319  * Create and initialize a new association-handle.
320  *  channel  : iochannel for the current line.
321  *  link     : communications channel.
322  * Returns: 0 or a new association handle.
323  */
324 association *create_association(IOCHAN channel, COMSTACK link)
325 {
326     association *anew;
327
328     if (!control_block)
329         control_block = statserv_getcontrol();
330     if (!(anew = (association *)xmalloc(sizeof(*anew))))
331         return 0;
332     anew->client_chan = channel;
333     anew->client_link = link;
334     if (!(anew->decode = odr_createmem(ODR_DECODE)) ||
335         !(anew->encode = odr_createmem(ODR_ENCODE)))
336         return 0;
337     if (*control_block->apdufile)
338     {
339         char filename[256];
340         FILE *f;
341
342         strcpy(filename, control_block->apdufile);
343         if (!(anew->print = odr_createmem(ODR_PRINT)))
344             return 0;
345         if (*control_block->apdufile != '-')
346         {
347             strcpy(filename, control_block->apdufile);
348             if (!control_block->dynamic)
349             {
350                 if (!apduf)
351                 {
352                     if (!(apduf = fopen(filename, "w")))
353                     {
354                         logf(LOG_WARN|LOG_ERRNO, "%s", filename);
355                         return 0;
356                     }
357                     setvbuf(apduf, 0, _IONBF, 0);
358                 }
359                 f = apduf;
360             }
361             else 
362             {
363                 sprintf(filename + strlen(filename), ".%d", getpid());
364                 if (!(f = fopen(filename, "w")))
365                 {
366                     logf(LOG_WARN|LOG_ERRNO, "%s", filename);
367                     return 0;
368                 }
369                 setvbuf(f, 0, _IONBF, 0);
370             }
371             odr_setprint(anew->print, f);
372         }
373     }
374     else
375         anew->print = 0;
376     anew->input_buffer = 0;
377     anew->input_buffer_len = 0;
378     anew->backend = 0;
379     anew->state = ASSOC_NEW;
380     request_initq(&anew->incoming);
381     request_initq(&anew->outgoing);
382     anew->proto = cs_getproto(link);
383     return anew;
384 }
385
386 /*
387  * Free association and release resources.
388  */
389 void destroy_association(association *h)
390 {
391     odr_destroy(h->decode);
392     odr_destroy(h->encode);
393     if (h->print)
394         odr_destroy(h->print);
395     if (h->input_buffer)
396     xfree(h->input_buffer);
397     if (h->backend)
398         bend_close(h->backend);
399     while (request_deq(&h->incoming));
400     while (request_deq(&h->outgoing));
401     request_delq(&h->incoming);
402     request_delq(&h->outgoing);
403     xfree(h);
404 }
405
406 static void do_close_req(association *a, int reason, char *message,
407                          request *req)
408 {
409     Z_APDU apdu;
410     Z_Close *cls = zget_Close(a->encode);
411     
412     /* Purge request queue */
413     while (request_deq(&a->incoming));
414     while (request_deq(&a->outgoing));
415     if (a->version >= 3)
416     {
417         logf(LOG_LOG, "Sending Close PDU, reason=%d, message=%s",
418             reason, message ? message : "none");
419         apdu.which = Z_APDU_close;
420         apdu.u.close = cls;
421         *cls->closeReason = reason;
422         cls->diagnosticInformation = message;
423         process_response(a, req, &apdu);
424         iochan_settimeout(a->client_chan, 60);
425     }
426     else
427     {
428         logf(LOG_DEBUG, "v2 client. No Close PDU");
429         iochan_setevent(a->client_chan, EVENT_TIMEOUT); /* force imm close */
430     }
431     a->state = ASSOC_DEAD;
432 }
433
434 static void do_close(association *a, int reason, char *message)
435 {
436     do_close_req (a, reason, message, request_get(&a->outgoing));
437 }
438
439 /*
440  * This is where PDUs from the client are read and the further
441  * processing is initiated. Flow of control moves down through the
442  * various process_* functions below, until the encoded result comes back up
443  * to the output handler in here.
444  * 
445  *  h     : the I/O channel that has an outstanding event.
446  *  event : the current outstanding event.
447  */
448 void ir_session(IOCHAN h, int event)
449 {
450     int res;
451     association *assoc = (association *)iochan_getdata(h);
452     COMSTACK conn = assoc->client_link;
453     request *req;
454
455     assert(h && conn && assoc);
456     if (event == EVENT_TIMEOUT)
457     {
458         if (assoc->state != ASSOC_UP)
459         {
460             logf(LOG_LOG, "Final timeout - closing connection.");
461             cs_close(conn);
462             destroy_association(assoc);
463             iochan_destroy(h);
464         }
465         else
466         {
467             logf(LOG_LOG, "Session idle too long. Sending close.");
468             do_close(assoc, Z_Close_lackOfActivity, 0);
469         }
470         return;
471     }
472     if (event & EVENT_INPUT || event & EVENT_WORK) /* input */
473     {
474         if (event & EVENT_INPUT)
475         {
476             logf(LOG_DEBUG, "ir_session (input)");
477             assert(assoc && conn);
478             /* We aren't speaking to this fellow */
479             if (assoc->state == ASSOC_DEAD)
480             {
481                 logf(LOG_LOG, "Closed connection after reject");
482                 cs_close(conn);
483                 destroy_association(assoc);
484                 iochan_destroy(h);
485                 return;
486             }
487             if ((res = cs_get(conn, &assoc->input_buffer,
488                 &assoc->input_buffer_len)) <= 0)
489             {
490                 logf(LOG_LOG, "Connection closed by client");
491                 cs_close(conn);
492                 destroy_association(assoc);
493                 iochan_destroy(h);
494                 return;
495             }
496             else if (res == 1) /* incomplete read - wait for more  */
497                 return;
498             if (cs_more(conn)) /* more stuff - call us again later, please */
499                 iochan_setevent(h, EVENT_INPUT);
500                 
501             /* we got a complete PDU. Let's decode it */
502             logf(LOG_DEBUG, "Got PDU, %d bytes", res);
503             req = request_get(&assoc->incoming); /* get a new request structure */
504             odr_reset(assoc->decode);
505             odr_setbuf(assoc->decode, assoc->input_buffer, res, 0);
506             if (!z_APDU(assoc->decode, &req->request, 0))
507             {
508                 logf(LOG_LOG, "ODR error on incoming PDU: %s [near byte %d] ",
509                     odr_errmsg(odr_geterror(assoc->decode)),
510                     odr_offset(assoc->decode));
511                 logf(LOG_LOG, "PDU dump:");
512                 odr_dumpBER(log_file(), assoc->input_buffer, res);
513                 do_close(assoc, Z_Close_protocolError, "Malformed package");
514                 return;
515             }
516             req->request_mem = odr_extract_mem(assoc->decode);
517             if (assoc->print && !z_APDU(assoc->print, &req->request, 0))
518             {
519                 logf(LOG_WARN, "ODR print error: %s", 
520                     odr_errmsg(odr_geterror(assoc->print)));
521                 odr_reset(assoc->print);
522             }
523             request_enq(&assoc->incoming, req);
524         }
525
526         /* can we do something yet? */
527         req = request_head(&assoc->incoming);
528         if (req->state == REQUEST_IDLE)
529         {
530             request_deq(&assoc->incoming);
531             if (process_request(assoc, req) < 0)
532                 do_close_req(assoc, Z_Close_systemProblem, "Unknown error",
533                              req);
534         }
535     }
536     if (event & EVENT_OUTPUT)
537     {
538         request *req = request_head(&assoc->outgoing);
539
540         logf(LOG_DEBUG, "ir_session (output)");
541         req->state = REQUEST_PENDING;
542         switch (res = cs_put(conn, req->response, req->len_response))
543         {
544             case -1:
545                 logf(LOG_LOG, "Connection closed by client");
546                 cs_close(conn);
547                 destroy_association(assoc);
548                 iochan_destroy(h);
549                 break;
550             case 0: /* all sent - release the request structure */
551                 logf(LOG_DEBUG, "Wrote PDU, %d bytes", req->len_response);
552                 nmem_destroy(req->request_mem);
553                 request_deq(&assoc->outgoing);
554                 request_release(req);
555                 if (!request_head(&assoc->outgoing))
556                     iochan_clearflag(h, EVENT_OUTPUT);
557                 break;
558             /* value of 1 -- partial send -- is simply ignored */
559         }
560     }
561     if (event & EVENT_EXCEPT)
562     {
563         logf(LOG_DEBUG, "ir_session (exception)");
564         cs_close(conn);
565         destroy_association(assoc);
566         iochan_destroy(h);
567     }
568 }
569
570 /*
571  * Initiate request processing.
572  */
573 static int process_request(association *assoc, request *req)
574 {
575     int fd = -1;
576     Z_APDU *res;
577     int retval;
578
579     assert(req && req->state == REQUEST_IDLE);
580     switch (req->request->which)
581     {
582         case Z_APDU_initRequest:
583             res = process_initRequest(assoc, req); break;
584         case Z_APDU_searchRequest:
585             res = process_searchRequest(assoc, req, &fd); break;
586         case Z_APDU_presentRequest:
587             res = process_presentRequest(assoc, req, &fd); break;
588         case Z_APDU_scanRequest:
589             res = process_scanRequest(assoc, req, &fd); break;
590         case Z_APDU_extendedServicesRequest:
591             if (assoc->bend_esrequest)
592                 res = process_ESRequest(assoc, req, &fd);
593             else
594             {
595                 logf(LOG_WARN, "Cannot handle EXTENDED SERVICES APDU");
596                 return -1;
597             }
598             break;
599         case Z_APDU_sortRequest:
600             if (assoc->bend_sort)
601                 res = process_sortRequest(assoc, req, &fd);
602             else
603             {
604                 logf(LOG_WARN, "Cannot handle SORT APDU");
605                 return -1;
606             }
607             break;
608         case Z_APDU_close:
609             process_close(assoc, req); return 0;
610         case Z_APDU_deleteResultSetRequest:
611             if (assoc->bend_delete)
612                 res = process_deleteRequest(assoc, req, &fd);
613             else
614             {
615                 logf (LOG_WARN, "Cannot handle Delete APDU");
616                 return -1;
617             }
618             break;
619         default:
620             logf(LOG_WARN, "Bad APDU received");
621             return -1;
622     }
623     if (res)
624     {
625         logf(LOG_DEBUG, "  result immediately available");
626         retval = process_response(assoc, req, res);
627     }
628     else if (fd < 0)
629     {
630         logf(LOG_DEBUG, "  result unavailble");
631         retval = 0;
632     }
633     else /* no result yet - one will be provided later */
634     {
635         IOCHAN chan;
636
637         /* Set up an I/O handler for the fd supplied by the backend */
638
639         logf(LOG_DEBUG, "   establishing handler for result");
640         req->state = REQUEST_PENDING;
641         if (!(chan = iochan_create(fd, backend_response, EVENT_INPUT)))
642             abort();
643         iochan_setdata(chan, assoc);
644         retval = 0;
645     }
646     return retval;
647 }
648
649 /*
650  * Handle message from the backend.
651  */
652 void backend_response(IOCHAN i, int event)
653 {
654     association *assoc = (association *)iochan_getdata(i);
655     request *req = request_head(&assoc->incoming);
656     Z_APDU *res;
657     int fd;
658
659     logf(LOG_DEBUG, "backend_response");
660     assert(assoc && req && req->state != REQUEST_IDLE);
661     /* determine what it is we're waiting for */
662     switch (req->request->which)
663     {
664         case Z_APDU_searchRequest:
665             res = response_searchRequest(assoc, req, 0, &fd); break;
666 #if 0
667         case Z_APDU_presentRequest:
668             res = response_presentRequest(assoc, req, 0, &fd); break;
669         case Z_APDU_scanRequest:
670             res = response_scanRequest(assoc, req, 0, &fd); break;
671 #endif
672         default:
673             logf(LOG_WARN, "Serious programmer's lapse or bug");
674             abort();
675     }
676     if ((res && process_response(assoc, req, res) < 0) || fd < 0)
677     {
678         logf(LOG_LOG, "Fatal error when talking to backend");
679         do_close(assoc, Z_Close_systemProblem, 0);
680         iochan_destroy(i);
681         return;
682     }
683     else if (!res) /* no result yet - try again later */
684     {
685         logf(LOG_DEBUG, "   no result yet");
686         iochan_setfd(i, fd); /* in case fd has changed */
687     }
688 }
689
690 /*
691  * Encode response, and transfer the request structure to the outgoing queue.
692  */
693 static int process_response(association *assoc, request *req, Z_APDU *res)
694 {
695     odr_setbuf(assoc->encode, req->response, req->size_response, 1);
696     if (!z_APDU(assoc->encode, &res, 0))
697     {
698         logf(LOG_WARN, "ODR error when encoding response: %s",
699             odr_errmsg(odr_geterror(assoc->decode)));
700         odr_reset(assoc->encode);
701         return -1;
702     }
703     req->response = odr_getbuf(assoc->encode, &req->len_response,
704         &req->size_response);
705     odr_setbuf(assoc->encode, 0, 0, 0); /* don'txfree if we abort later */
706     if (assoc->print && !z_APDU(assoc->print, &res, 0))
707     {
708         logf(LOG_WARN, "ODR print error: %s", 
709             odr_errmsg(odr_geterror(assoc->print)));
710         odr_reset(assoc->print);
711     }
712     odr_reset(assoc->encode);
713     req->state = REQUEST_IDLE;
714     request_enq(&assoc->outgoing, req);
715     /* turn the work over to the ir_session handler */
716     iochan_setflag(assoc->client_chan, EVENT_OUTPUT);
717     /* Is there more work to be done? give that to the input handler too */
718 #if 1
719     if (request_head(&assoc->incoming))
720     {
721         logf (LOG_DEBUG, "more work to be done");
722         iochan_setevent(assoc->client_chan, EVENT_WORK);
723     }
724 #endif
725     return 0;
726 }
727
728 /*
729  * Handle init request.
730  * At the moment, we don't check the options
731  * anywhere else in the code - we just try not to do anything that would
732  * break a naive client. We'll toss 'em into the association block when
733  * we need them there.
734  */
735 static Z_APDU *process_initRequest(association *assoc, request *reqb)
736 {
737     Z_InitRequest *req = reqb->request->u.initRequest;
738     Z_APDU *apdu = zget_APDU(assoc->encode, Z_APDU_initResponse);
739     Z_InitResponse *resp = apdu->u.initResponse;
740     bend_initrequest binitreq;
741     bend_initresult *binitres;
742     char options[100];
743
744     logf(LOG_LOG, "Got initRequest");
745     if (req->implementationId)
746         logf(LOG_LOG, "Id:        %s", req->implementationId);
747     if (req->implementationName)
748         logf(LOG_LOG, "Name:      %s", req->implementationName);
749     if (req->implementationVersion)
750         logf(LOG_LOG, "Version:   %s", req->implementationVersion);
751
752     binitreq.stream = assoc->encode;
753     binitreq.configname = "default-config";
754     binitreq.auth = req->idAuthentication;
755     binitreq.bend_sort = NULL;
756     binitreq.bend_search = NULL;
757     binitreq.bend_present = NULL;
758     binitreq.bend_esrequest = NULL;
759     binitreq.bend_delete = NULL;
760     if (!(binitres = bend_init(&binitreq)))
761     {
762         logf(LOG_WARN, "Bad response from backend.");
763         return 0;
764     }
765
766     assoc->backend = binitres->handle;
767     if ((assoc->bend_sort = (int (*)())binitreq.bend_sort))
768         logf (LOG_DEBUG, "Sort handler installed");
769     if ((assoc->bend_search = (int (*)())binitreq.bend_search))
770         logf (LOG_DEBUG, "Search handler installed");
771     if ((assoc->bend_present = (int (*)())binitreq.bend_present))
772         logf (LOG_DEBUG, "Present handler installed");   
773     if ((assoc->bend_esrequest = (int (*)())binitreq.bend_esrequest))
774         logf (LOG_DEBUG, "ESRequest handler installed");   
775     if ((assoc->bend_delete = (int (*)())binitreq.bend_delete))
776         logf (LOG_DEBUG, "Delete handler installed");   
777     
778     resp->referenceId = req->referenceId;
779     *options = '\0';
780     /* let's tell the client what we can do */
781     if (ODR_MASK_GET(req->options, Z_Options_search))
782     {
783         ODR_MASK_SET(resp->options, Z_Options_search);
784         strcat(options, "srch");
785     }
786     if (ODR_MASK_GET(req->options, Z_Options_present))
787     {
788         ODR_MASK_SET(resp->options, Z_Options_present);
789         strcat(options, " prst");
790     }
791     if (ODR_MASK_GET(req->options, Z_Options_delSet) && binitreq.bend_delete)
792     {
793         ODR_MASK_SET(resp->options, Z_Options_delSet);
794         strcat(options, " del");
795     }
796     if (ODR_MASK_GET(req->options, Z_Options_extendedServices) &&
797         binitreq.bend_esrequest)
798     {
799         ODR_MASK_SET(resp->options, Z_Options_extendedServices);
800         strcat (options, " extendedServices");
801     }
802     if (ODR_MASK_GET(req->options, Z_Options_namedResultSets))
803     {
804         ODR_MASK_SET(resp->options, Z_Options_namedResultSets);
805         strcat(options, " namedresults");
806     }
807     if (ODR_MASK_GET(req->options, Z_Options_scan))
808     {
809         ODR_MASK_SET(resp->options, Z_Options_scan);
810         strcat(options, " scan");
811     }
812     if (ODR_MASK_GET(req->options, Z_Options_concurrentOperations))
813     {
814         ODR_MASK_SET(resp->options, Z_Options_concurrentOperations);
815         strcat(options, " concurop");
816     }
817     if (ODR_MASK_GET(req->options, Z_Options_sort && binitreq.bend_sort))
818     {
819         ODR_MASK_SET(resp->options, Z_Options_sort);
820         strcat(options, " sort");
821     }
822     if (ODR_MASK_GET(req->protocolVersion, Z_ProtocolVersion_1))
823     {
824         ODR_MASK_SET(resp->protocolVersion, Z_ProtocolVersion_1);
825         assoc->version = 2; /* 1 & 2 are equivalent */
826     }
827     if (ODR_MASK_GET(req->protocolVersion, Z_ProtocolVersion_2))
828     {
829         ODR_MASK_SET(resp->protocolVersion, Z_ProtocolVersion_2);
830         assoc->version = 2;
831     }
832     if (ODR_MASK_GET(req->protocolVersion, Z_ProtocolVersion_3))
833     {
834         ODR_MASK_SET(resp->protocolVersion, Z_ProtocolVersion_3);
835         assoc->version = 3;
836     }
837     logf(LOG_LOG, "Negotiated to v%d: %s", assoc->version, options);
838     assoc->maximumRecordSize = *req->maximumRecordSize;
839     if (assoc->maximumRecordSize > control_block->maxrecordsize)
840         assoc->maximumRecordSize = control_block->maxrecordsize;
841     assoc->preferredMessageSize = *req->preferredMessageSize;
842     if (assoc->preferredMessageSize > assoc->maximumRecordSize)
843         assoc->preferredMessageSize = assoc->maximumRecordSize;
844     resp->preferredMessageSize = &assoc->preferredMessageSize;
845     resp->maximumRecordSize = &assoc->maximumRecordSize;
846     resp->implementationName = "Index Data/YAZ Generic Frontend Server";
847     if (binitres->errcode)
848     {
849         logf(LOG_LOG, "Connection rejected by backend.");
850         *resp->result = 0;
851         assoc->state = ASSOC_DEAD;
852     }
853     else
854         assoc->state = ASSOC_UP;
855     return apdu;
856 }
857
858 /*
859  * These functions should be merged.
860  */
861
862 static void set_addinfo (Z_DefaultDiagFormat *dr, char *addinfo)
863 {
864 #if ASN_COMPILED
865     dr->which = Z_DefaultDiagFormat_v2Addinfo;
866     dr->u.v2Addinfo = addinfo ? addinfo : "";
867 #else
868     dr->which = Z_DiagForm_v2AddInfo;
869     dr->addinfo = addinfo ? addinfo : "";
870 #endif
871 }
872
873 /*
874  * nonsurrogate diagnostic record.
875  */
876 static Z_Records *diagrec(association *assoc, int error, char *addinfo)
877 {
878     int oid[OID_SIZE];
879     Z_Records *rec = (Z_Records *)odr_malloc (assoc->encode, sizeof(*rec));
880     oident bib1;
881     int *err = (int *)odr_malloc (assoc->encode, sizeof(*err));
882     Z_DiagRec *drec = (Z_DiagRec *)odr_malloc (assoc->encode, sizeof(*drec));
883     Z_DefaultDiagFormat *dr = (Z_DefaultDiagFormat *)odr_malloc (assoc->encode, sizeof(*dr));
884
885     bib1.proto = assoc->proto;
886     bib1.oclass = CLASS_DIAGSET;
887     bib1.value = VAL_BIB1;
888
889     logf(LOG_DEBUG, "Diagnostic: %d -- %s", error, addinfo ? addinfo :
890         "NULL");
891     *err = error;
892     rec->which = Z_Records_NSD;
893 #if ASN_COMPILED
894     rec->u.nonSurrogateDiagnostic = dr;
895 #else
896 #ifdef Z_95
897     rec->u.nonSurrogateDiagnostic = drec;
898     drec->which = Z_DiagRec_defaultFormat;
899     drec->u.defaultFormat = dr;
900 #else
901     rec->u.nonSurrogateDiagnostic = dr;
902 #endif
903 #endif
904     dr->diagnosticSetId = odr_oiddup (assoc->encode,
905                                       oid_ent_to_oid(&bib1, oid));
906     dr->condition = err;
907     set_addinfo (dr, addinfo);
908     return rec;
909 }
910
911 /*
912  * surrogate diagnostic.
913  */
914 static Z_NamePlusRecord *surrogatediagrec(association *assoc, char *dbname,
915                                           int error, char *addinfo)
916 {
917     int oid[OID_SIZE];
918     Z_NamePlusRecord *rec = (Z_NamePlusRecord *)odr_malloc (assoc->encode, sizeof(*rec));
919     int *err = (int *)odr_malloc (assoc->encode, sizeof(*err));
920     oident bib1;
921     Z_DiagRec *drec = (Z_DiagRec *)odr_malloc (assoc->encode, sizeof(*drec));
922     Z_DefaultDiagFormat *dr = (Z_DefaultDiagFormat *)odr_malloc (assoc->encode, sizeof(*dr));
923
924     bib1.proto = assoc->proto;
925     bib1.oclass = CLASS_DIAGSET;
926     bib1.value = VAL_BIB1;
927
928     logf(LOG_DEBUG, "SurrogateDiagnotic: %d -- %s", error, addinfo);
929     *err = error;
930     rec->databaseName = dbname;
931     rec->which = Z_NamePlusRecord_surrogateDiagnostic;
932     rec->u.surrogateDiagnostic = drec;
933     drec->which = Z_DiagRec_defaultFormat;
934     drec->u.defaultFormat = dr;
935     dr->diagnosticSetId = odr_oiddup (assoc->encode,
936                                       oid_ent_to_oid(&bib1, oid));
937     dr->condition = err;
938     set_addinfo (dr, addinfo);
939
940     return rec;
941 }
942
943 /*
944  * multiple nonsurrogate diagnostics.
945  */
946 static Z_DiagRecs *diagrecs(association *assoc, int error, char *addinfo)
947 {
948     int oid[OID_SIZE];
949     Z_DiagRecs *recs = (Z_DiagRecs *)odr_malloc (assoc->encode, sizeof(*recs));
950     int *err = (int *)odr_malloc (assoc->encode, sizeof(*err));
951     oident bib1;
952     Z_DiagRec **recp = (Z_DiagRec **)odr_malloc (assoc->encode, sizeof(*recp));
953     Z_DiagRec *drec = (Z_DiagRec *)odr_malloc (assoc->encode, sizeof(*drec));
954     Z_DefaultDiagFormat *rec = (Z_DefaultDiagFormat *)odr_malloc (assoc->encode, sizeof(*rec));
955
956     logf(LOG_DEBUG, "DiagRecs: %d -- %s", error, addinfo);
957     bib1.proto = assoc->proto;
958     bib1.oclass = CLASS_DIAGSET;
959     bib1.value = VAL_BIB1;
960
961     *err = error;
962     recs->num_diagRecs = 1;
963     recs->diagRecs = recp;
964     recp[0] = drec;
965     drec->which = Z_DiagRec_defaultFormat;
966     drec->u.defaultFormat = rec;
967
968     rec->diagnosticSetId = odr_oiddup (assoc->encode,
969                                       oid_ent_to_oid(&bib1, oid));
970     rec->condition = err;
971
972 #ifdef ASN_COMPILED
973     rec->which = Z_DefaultDiagFormat_v2Addinfo;
974     rec->u.v2Addinfo = addinfo ? addinfo : "";
975 #else
976     rec->which = Z_DiagForm_v2AddInfo;
977     rec->addinfo = addinfo ? addinfo : "";
978 #endif
979     return recs;
980 }
981
982 static Z_Records *pack_records(association *a, char *setname, int start,
983                                 int *num, Z_RecordComposition *comp,
984                                 int *next, int *pres, oid_value format)
985 {
986     int oid[OID_SIZE];
987     int recno, total_length = 0, toget = *num, dumped_records = 0;
988     Z_Records *records = (Z_Records *)odr_malloc (a->encode, sizeof(*records));
989     Z_NamePlusRecordList *reclist = (Z_NamePlusRecordList *)odr_malloc (a->encode, sizeof(*reclist));
990     Z_NamePlusRecord **list = (Z_NamePlusRecord **)odr_malloc (a->encode, sizeof(*list) * toget);
991     oident recform;
992
993     records->which = Z_Records_DBOSD;
994     records->u.databaseOrSurDiagnostics = reclist;
995     reclist->num_records = 0;
996     reclist->records = list;
997     *pres = Z_PRES_SUCCESS;
998     *num = 0;
999     *next = 0;
1000
1001     logf(LOG_DEBUG, "Request to pack %d+%d", start, toget);
1002     logf(LOG_DEBUG, "pms=%d, mrs=%d", a->preferredMessageSize,
1003         a->maximumRecordSize);
1004     for (recno = start; reclist->num_records < toget; recno++)
1005     {
1006         bend_fetchrequest freq;
1007         bend_fetchresult *fres;
1008         Z_NamePlusRecord *thisrec;
1009         Z_DatabaseRecord *thisext;
1010         int this_length = 0;
1011
1012         /*
1013          * we get the number of bytes allocated on the stream before any
1014          * allocation done by the backend - this should give us a reasonable
1015          * idea of the total size of the data so far.
1016          */
1017         total_length = odr_total(a->encode) - dumped_records;
1018         freq.setname = setname;
1019         freq.number = recno;
1020         freq.comp = comp;
1021         freq.format = format;
1022         freq.stream = a->encode;
1023         freq.surrogate_flag = 0;
1024         if (!(fres = bend_fetch(a->backend, &freq, 0)))
1025         {
1026             *pres = Z_PRES_FAILURE;
1027             return diagrec(a, 2, "Backend interface problem");
1028         }
1029         /* backend should be able to signal whether error is system-wide
1030            or only pertaining to current record */
1031         if (fres->errcode)
1032         {
1033             if (!freq.surrogate_flag)
1034             {
1035                 *pres = Z_PRES_FAILURE;
1036                 return diagrec(a, fres->errcode, fres->errstring);
1037             }
1038             reclist->records[reclist->num_records] =
1039                 surrogatediagrec(a, fres->basename, fres->errcode,
1040                                  fres->errstring);
1041             reclist->num_records++;
1042             *next = fres->last_in_set ? 0 : recno + 1;
1043             continue;
1044         }
1045         if (fres->len >= 0)
1046             this_length = fres->len;
1047         else
1048             this_length = odr_total(a->encode) - total_length;
1049         logf(LOG_DEBUG, "  fetched record, len=%d, total=%d",
1050             this_length, total_length);
1051         if (this_length + total_length > a->preferredMessageSize)
1052         {
1053             /* record is small enough, really */
1054             if (this_length <= a->preferredMessageSize)
1055             {
1056                 logf(LOG_DEBUG, "  Dropped last normal-sized record");
1057                 *pres = Z_PRES_PARTIAL_2;
1058                 break;
1059             }
1060             /* record can only be fetched by itself */
1061             if (this_length < a->maximumRecordSize)
1062             {
1063                 logf(LOG_DEBUG, "  Record > prefmsgsz");
1064                 if (toget > 1)
1065                 {
1066                     logf(LOG_DEBUG, "  Dropped it");
1067                     reclist->records[reclist->num_records] =
1068                          surrogatediagrec(a, fres->basename, 16, 0);
1069                     reclist->num_records++;
1070                     *next = fres->last_in_set ? 0 : recno + 1;
1071                     dumped_records += this_length;
1072                     continue;
1073                 }
1074             }
1075             else /* too big entirely */
1076             {
1077                 logf(LOG_DEBUG, "Record > maxrcdsz");
1078                 reclist->records[reclist->num_records] =
1079                     surrogatediagrec(a, fres->basename, 17, 0);
1080                 reclist->num_records++;
1081                 *next = fres->last_in_set ? 0 : recno + 1;
1082                 dumped_records += this_length;
1083                 continue;
1084             }
1085         }
1086
1087         if (!(thisrec = (Z_NamePlusRecord *)odr_malloc(a->encode, sizeof(*thisrec))))
1088             return 0;
1089         if (!(thisrec->databaseName = (char *)odr_malloc(a->encode,
1090             strlen(fres->basename) + 1)))
1091             return 0;
1092         strcpy(thisrec->databaseName, fres->basename);
1093         thisrec->which = Z_NamePlusRecord_databaseRecord;
1094         if (!(thisrec->u.databaseRecord = thisext = (Z_External *)odr_malloc(a->encode,
1095             sizeof(Z_DatabaseRecord))))
1096             return 0;
1097         recform.proto = a->proto;
1098         recform.oclass = CLASS_RECSYN;
1099         recform.value = fres->format;
1100         thisext->direct_reference = odr_oiddup(a->encode,
1101                                                oid_ent_to_oid(&recform, oid));
1102         thisext->indirect_reference = 0;
1103         thisext->descriptor = 0;
1104         if (fres->len < 0) /* Structured data */
1105         {
1106             switch (fres->format)
1107             {
1108                 case VAL_SUTRS: thisext->which = Z_External_sutrs; break;
1109                 case VAL_GRS1: thisext->which = Z_External_grs1; break;
1110                 case VAL_EXPLAIN: thisext->which = Z_External_explainRecord;
1111                     break;
1112                 case VAL_SUMMARY: thisext->which = Z_External_summary; break;
1113                 case VAL_OPAC: thisext->which = Z_External_OPAC; break;
1114
1115                 default:
1116                     logf(LOG_FATAL, "Unknown structured format from backend.");
1117                     return 0;
1118             }
1119
1120             /*
1121              * We cheat on the pointers here. Obviously, the record field
1122              * of the backend-fetch structure should have been a union for
1123              * correctness, but we're stuck with this for backwards
1124              * compatibility.
1125              */
1126             thisext->u.grs1 = (Z_GenericRecord*) fres->record;
1127         }
1128         else if (fres->format == VAL_SUTRS) /* SUTRS is a single-ASN.1-type */
1129         {
1130 #if 0
1131             Z_SUTRS *sutrs = (Z_SUTRS *)odr_malloc(a->encode, 1+fres->len);
1132             
1133             memcpy(sutrs, fres->record, fres->len);
1134             sutrs[fres->len] = '\0';
1135 #else
1136             Odr_oct *sutrs = (Odr_oct *)odr_malloc(a->encode, sizeof(*sutrs));
1137
1138             thisext->which = Z_External_sutrs;
1139             thisext->u.sutrs = sutrs;
1140             sutrs->buf = (unsigned char *)odr_malloc(a->encode, fres->len);
1141             sutrs->len = sutrs->size = fres->len;
1142             memcpy(sutrs->buf, fres->record, fres->len);
1143 #endif
1144         }
1145         else /* octet-aligned record. */
1146         {
1147             thisext->which = Z_External_octet;
1148             if (!(thisext->u.octet_aligned = (Odr_oct *)odr_malloc(a->encode,
1149                 sizeof(Odr_oct))))
1150                 return 0;
1151             if (!(thisext->u.octet_aligned->buf = (unsigned char *)odr_malloc(a->encode,
1152                 fres->len)))
1153                 return 0;
1154             memcpy(thisext->u.octet_aligned->buf, fres->record, fres->len);
1155             thisext->u.octet_aligned->len = thisext->u.octet_aligned->size =
1156                 fres->len;
1157         }
1158         reclist->records[reclist->num_records] = thisrec;
1159         reclist->num_records++;
1160         *next = fres->last_in_set ? 0 : recno + 1;
1161     }
1162     *num = reclist->num_records;
1163     return records;
1164 }
1165
1166 static Z_APDU *process_searchRequest(association *assoc, request *reqb,
1167     int *fd)
1168 {
1169     Z_SearchRequest *req = reqb->request->u.searchRequest;
1170     bend_search_rr *bsrr = (bend_search_rr *)nmem_malloc (reqb->request_mem, sizeof(*bsrr));
1171
1172     logf(LOG_LOG, "Got SearchRequest.");
1173     save_referenceId (reqb, req->referenceId);
1174     /* store ref id in request */
1175     bsrr->fd = fd;
1176     bsrr->request = reqb;
1177     bsrr->association = assoc;
1178     if (assoc->bend_search)
1179     {
1180         bsrr->setname = req->resultSetName;
1181         bsrr->replace_set = *req->replaceIndicator;
1182         bsrr->num_bases = req->num_databaseNames;
1183         bsrr->basenames = req->databaseNames;
1184         bsrr->query = req->query;
1185         bsrr->stream = assoc->encode;
1186         bsrr->errcode = 0;
1187         bsrr->hits = 0;
1188         bsrr->errstring = NULL;
1189         ((int (*)(void *, bend_search_rr *))(assoc->bend_search))(assoc->backend, bsrr);
1190         if (!bsrr->request)
1191             return 0;
1192     }
1193     else
1194     {
1195         bend_searchrequest bsrq;
1196         bend_searchresult *bsrt;
1197
1198         bsrq.setname = req->resultSetName;
1199         bsrq.replace_set = *req->replaceIndicator;
1200         bsrq.num_bases = req->num_databaseNames;
1201         bsrq.basenames = req->databaseNames;
1202         bsrq.query = req->query;
1203         bsrq.stream = assoc->encode;
1204         if (!(bsrt = bend_search(assoc->backend, &bsrq, fd)))
1205             return 0;
1206         bsrr->hits = bsrt->hits;
1207         bsrr->errcode = bsrt->errcode;
1208         bsrr->errstring = bsrt->errstring;
1209     }
1210     return response_searchRequest(assoc, reqb, bsrr, fd);
1211 }
1212
1213 int bend_searchresponse(void *handle, bend_search_rr *bsrr) {return 0;}
1214
1215 /*
1216  * Prepare a searchresponse based on the backend results. We probably want
1217  * to look at making the fetching of records nonblocking as well, but
1218  * so far, we'll keep things simple.
1219  * If bsrt is null, that means we're called in response to a communications
1220  * event, and we'll have to get the response for ourselves.
1221  */
1222 static Z_APDU *response_searchRequest(association *assoc, request *reqb,
1223     bend_search_rr *bsrt, int *fd)
1224 {
1225     Z_SearchRequest *req = reqb->request->u.searchRequest;
1226     Z_APDU *apdu = (Z_APDU *)odr_malloc (assoc->encode, sizeof(*apdu));
1227     Z_SearchResponse *resp = (Z_SearchResponse *)odr_malloc (assoc->encode, sizeof(*resp));
1228     int *nulint = (int *)odr_malloc (assoc->encode, sizeof(*nulint));
1229     bool_t *sr = (bool_t *)odr_malloc (assoc->encode, sizeof(*sr));
1230     int *next = (int *)odr_malloc (assoc->encode, sizeof(*next));
1231     int *none = (int *)odr_malloc (assoc->encode, sizeof(*none));
1232
1233     *nulint = 0;
1234     *sr = 1;
1235     *next = 0;
1236     *none = Z_RES_NONE;
1237
1238     apdu->which = Z_APDU_searchResponse;
1239     apdu->u.searchResponse = resp;
1240     resp->referenceId = req->referenceId;
1241     resp->additionalSearchInfo = 0;
1242     resp->otherInfo = 0;
1243     *fd = -1;
1244     if (!bsrt && !bend_searchresponse(assoc->backend, bsrt))
1245     {
1246         logf(LOG_FATAL, "Bad result from backend");
1247         return 0;
1248     }
1249     else if (bsrt->errcode)
1250     {
1251         resp->records = diagrec(assoc, bsrt->errcode, bsrt->errstring);
1252         resp->resultCount = nulint;
1253         resp->numberOfRecordsReturned = nulint;
1254         resp->nextResultSetPosition = nulint;
1255         resp->searchStatus = nulint;
1256         resp->resultSetStatus = none;
1257         resp->presentStatus = 0;
1258     }
1259     else
1260     {
1261         int *toget = (int *)odr_malloc (assoc->encode, sizeof(*toget));
1262         int *presst = (int *)odr_malloc (assoc->encode, sizeof(*presst));
1263         Z_RecordComposition comp, *compp = 0;
1264
1265         *toget = 0;
1266         *presst = 0;
1267         resp->records = 0;
1268         resp->resultCount = &bsrt->hits;
1269
1270         comp.which = Z_RecordComp_simple;
1271         /* how many records does the user agent want, then? */
1272         if (bsrt->hits <= *req->smallSetUpperBound)
1273         {
1274             *toget = bsrt->hits;
1275             if ((comp.u.simple = req->smallSetElementSetNames))
1276                 compp = &comp;
1277         }
1278         else if (bsrt->hits < *req->largeSetLowerBound)
1279         {
1280             *toget = *req->mediumSetPresentNumber;
1281             if (*toget > bsrt->hits)
1282                 *toget = bsrt->hits;
1283             if ((comp.u.simple = req->mediumSetElementSetNames))
1284                 compp = &comp;
1285         }
1286         else
1287             *toget = 0;
1288
1289         if (*toget && !resp->records)
1290         {
1291             oident *prefformat;
1292             oid_value form;
1293
1294             if (!(prefformat = oid_getentbyoid(req->preferredRecordSyntax)) ||
1295                 prefformat->oclass != CLASS_RECSYN)
1296                 form = VAL_NONE;
1297             else
1298                 form = prefformat->value;
1299             resp->records = pack_records(assoc, req->resultSetName, 1,
1300                 toget, compp, next, presst, form);
1301             if (!resp->records)
1302                 return 0;
1303             resp->numberOfRecordsReturned = toget;
1304             resp->nextResultSetPosition = next;
1305             resp->searchStatus = sr;
1306             resp->resultSetStatus = 0;
1307             resp->presentStatus = presst;
1308         }
1309         else
1310         {
1311             if (*resp->resultCount)
1312                 *next = 1;
1313             resp->numberOfRecordsReturned = nulint;
1314             resp->nextResultSetPosition = next;
1315             resp->searchStatus = sr;
1316             resp->resultSetStatus = 0;
1317             resp->presentStatus = 0;
1318         }
1319     }
1320     return apdu;
1321 }
1322
1323 /*
1324  * Maybe we got a little over-friendly when we designed bend_fetch to
1325  * get only one record at a time. Some backends can optimise multiple-record
1326  * fetches, and at any rate, there is some overhead involved in
1327  * all that selecting and hopping around. Problem is, of course, that the
1328  * frontend can't know ahead of time how many records it'll need to
1329  * fill the negotiated PDU size. Annoying. Segmentation or not, Z/SR
1330  * is downright lousy as a bulk data transfer protocol.
1331  *
1332  * To start with, we'll do the fetching of records from the backend
1333  * in one operation: To save some trips in and out of the event-handler,
1334  * and to simplify the interface to pack_records. At any rate, asynch
1335  * operation is more fun in operations that have an unpredictable execution
1336  * speed - which is normally more true for search than for present.
1337  */
1338 static Z_APDU *process_presentRequest(association *assoc, request *reqb,
1339     int *fd)
1340 {
1341     Z_PresentRequest *req = reqb->request->u.presentRequest;
1342     oident *prefformat;
1343     oid_value form;
1344     Z_APDU *apdu;
1345     Z_PresentResponse *resp;
1346     int *presst;
1347     int *next;
1348     int *num;
1349
1350     logf(LOG_LOG, "Got PresentRequest.");
1351
1352     if (!(prefformat = oid_getentbyoid(req->preferredRecordSyntax)) ||
1353         prefformat->oclass != CLASS_RECSYN)
1354         form = VAL_NONE;
1355     else
1356         form = prefformat->value;
1357     if (assoc->bend_present)
1358     {
1359         bend_present_rr *bprr = (bend_present_rr *)nmem_malloc (reqb->request_mem, sizeof(*bprr));
1360         bprr->setname = req->resultSetId;
1361         bprr->start = *req->resultSetStartPoint;
1362         bprr->number = *req->numberOfRecordsRequested;
1363         bprr->format = form;
1364         bprr->comp = req->recordComposition;
1365         bprr->stream = assoc->encode;
1366         bprr->request = reqb;
1367         bprr->association = assoc;
1368         bprr->errcode = 0;
1369         bprr->errstring = NULL;
1370         ((int (*)(void *, bend_present_rr *))(*assoc->bend_present))(assoc->backend, bprr);
1371
1372         if (!bprr->request)
1373             return 0;
1374     }
1375     apdu = (Z_APDU *)odr_malloc (assoc->encode, sizeof(*apdu));
1376     resp = (Z_PresentResponse *)odr_malloc (assoc->encode, sizeof(*resp));
1377     presst = (int *)odr_malloc (assoc->encode, sizeof(*presst));
1378     next = (int *)odr_malloc (assoc->encode, sizeof(*next));
1379     num = (int *)odr_malloc (assoc->encode, sizeof(*num));
1380     *presst = 0;
1381     *next = 0;
1382     *num = *req->numberOfRecordsRequested;
1383     
1384     apdu->which = Z_APDU_presentResponse;
1385     apdu->u.presentResponse = resp;
1386     resp->referenceId = req->referenceId;
1387     resp->otherInfo = 0;
1388     
1389     resp->records =
1390         pack_records(assoc, req->resultSetId, *req->resultSetStartPoint,
1391                      num, req->recordComposition, next, presst, form);
1392     if (!resp->records)
1393         return 0;
1394     resp->numberOfRecordsReturned = num;
1395     resp->presentStatus = presst;
1396     resp->nextResultSetPosition = next;
1397     
1398     return apdu;
1399 }
1400
1401 /*
1402  * Scan was implemented rather in a hurry, and with support for only the basic
1403  * elements of the service in the backend API. Suggestions are welcome.
1404  */
1405 static Z_APDU *process_scanRequest(association *assoc, request *reqb, int *fd)
1406 {
1407     Z_ScanRequest *req = reqb->request->u.scanRequest;
1408     Z_APDU *apdu = (Z_APDU *)odr_malloc (assoc->encode, sizeof(*apdu));
1409     Z_ScanResponse *res = (Z_ScanResponse *)odr_malloc (assoc->encode, sizeof(*res));
1410     int *scanStatus = (int *)odr_malloc (assoc->encode, sizeof(*scanStatus));
1411     int *numberOfEntriesReturned =
1412          (int *)odr_malloc (assoc->encode, sizeof(*numberOfEntriesReturned));
1413     Z_ListEntries *ents = (Z_ListEntries *)odr_malloc (assoc->encode, sizeof(*ents));
1414     Z_DiagRecs *diagrecs_p = NULL;
1415     oident *attent;
1416     bend_scanrequest srq;
1417     bend_scanresult *srs;
1418     oident *attset;
1419
1420     logf(LOG_LOG, "Got ScanRequest");
1421     *scanStatus = Z_Scan_failure;
1422     *numberOfEntriesReturned = 0;
1423
1424     apdu->which = Z_APDU_scanResponse;
1425     apdu->u.scanResponse = res;
1426     res->referenceId = req->referenceId;
1427     res->stepSize = 0;
1428     res->scanStatus = scanStatus;
1429     res->numberOfEntriesReturned = numberOfEntriesReturned;
1430     res->positionOfTerm = 0;
1431     res->entries = ents;
1432 #if ASN_COMPILED
1433     ents->num_entries = 0;
1434     ents->entries = NULL;
1435     ents->num_nonsurrogateDiagnostics = 0;
1436     ents->nonsurrogateDiagnostics = NULL;
1437 #else
1438     ents->which = Z_ListEntries_entries;
1439 #endif
1440     res->attributeSet = 0;
1441     res->otherInfo = 0;
1442
1443     if (req->attributeSet && (!(attent = oid_getentbyoid(req->attributeSet)) ||
1444                               attent->oclass != CLASS_ATTSET
1445                               || attent->value != VAL_BIB1))
1446         diagrecs_p = diagrecs(assoc, 121, 0);
1447     else if (req->stepSize && *req->stepSize > 0)
1448         diagrecs_p = diagrecs(assoc, 205, 0);
1449     else
1450     {
1451         if (req->termListAndStartPoint->term->which == Z_Term_general)
1452             logf(LOG_DEBUG, " term: '%.*s'",
1453                  req->termListAndStartPoint->term->u.general->len,
1454                  req->termListAndStartPoint->term->u.general->buf);
1455         srq.num_bases = req->num_databaseNames;
1456         srq.basenames = req->databaseNames;
1457         srq.num_entries = *req->numberOfTermsRequested;
1458         srq.term = req->termListAndStartPoint;
1459         srq.stream = assoc->encode;
1460         if (!(attset = oid_getentbyoid(req->attributeSet)) ||
1461             attset->oclass != CLASS_RECSYN)
1462             srq.attributeset = VAL_NONE;
1463         else
1464             srq.attributeset = attset->value;
1465         srq.term_position = req->preferredPositionInResponse ?
1466             *req->preferredPositionInResponse : 1;
1467         if (!(srs = bend_scan(assoc->backend, &srq, 0)))
1468             diagrecs_p = diagrecs(assoc, 2, 0);
1469         else if (srs->errcode)
1470             diagrecs_p = diagrecs(assoc, srs->errcode, srs->errstring);
1471         else
1472         {
1473             int i;
1474 #ifdef ASN_COMPILED
1475 #else
1476             Z_Entries *list = (Z_Entries *)
1477                 odr_malloc (assoc->encode, sizeof(*list));
1478 #endif
1479             Z_Entry **tab = (Z_Entry **)
1480                 odr_malloc (assoc->encode, sizeof(*tab) * srs->num_entries);
1481             
1482             if (srs->status == BEND_SCAN_PARTIAL)
1483                 *scanStatus = Z_Scan_partial_5;
1484             else
1485                 *scanStatus = Z_Scan_success;
1486 #ifdef ASN_COMPILED
1487             ents->entries = tab;
1488             ents->num_entries = srs->num_entries;
1489             res->numberOfEntriesReturned = &ents->num_entries;      
1490 #else
1491             ents->u.entries = list;
1492             list->entries = tab;
1493             list->num_entries = srs->num_entries;
1494             res->numberOfEntriesReturned = &list->num_entries;
1495 #endif
1496             res->positionOfTerm = &srs->term_position;
1497             for (i = 0; i < srs->num_entries; i++)
1498             {
1499                 Z_Entry *e;
1500                 Z_TermInfo *t;
1501                 Odr_oct *o;
1502                 
1503                 tab[i] = e = (Z_Entry *)odr_malloc(assoc->encode, sizeof(*e));
1504                 e->which = Z_Entry_termInfo;
1505                 e->u.termInfo = t = (Z_TermInfo *)odr_malloc(assoc->encode, sizeof(*t));
1506                 t->suggestedAttributes = 0;
1507                 t->displayTerm = 0;
1508                 t->alternativeTerm = 0;
1509                 t->byAttributes = 0;
1510                 t->otherTermInfo = 0;
1511                 t->globalOccurrences = &srs->entries[i].occurrences;
1512                 t->term = (Z_Term *)odr_malloc(assoc->encode, sizeof(*t->term));
1513                 t->term->which = Z_Term_general;
1514                 t->term->u.general = o = (Odr_oct *)odr_malloc(assoc->encode,
1515                     sizeof(Odr_oct));
1516                 o->buf = (unsigned char *)odr_malloc(assoc->encode, o->len = o->size =
1517                     strlen(srs->entries[i].term));
1518                 memcpy(o->buf, srs->entries[i].term, o->len);
1519                 logf(LOG_DEBUG, "  term #%d: '%s' (%d)", i,
1520                     srs->entries[i].term, srs->entries[i].occurrences);
1521             }
1522         }
1523     }
1524     if (diagrecs_p)
1525     {
1526 #ifdef ASN_COMPILED
1527         ents->num_nonsurrogateDiagnostics = diagrecs_p->num_diagRecs;
1528         ents->nonsurrogateDiagnostics = diagrecs_p->diagRecs;
1529 #else
1530         ents->u.nonSurrogateDiagnostics = diagrecs_p;
1531         ents->which = Z_ListEntries_nonSurrogateDiagnostics;
1532 #endif
1533     }
1534     return apdu;
1535 }
1536
1537 static Z_APDU *process_sortRequest(association *assoc, request *reqb,
1538     int *fd)
1539 {
1540     Z_SortRequest *req = reqb->request->u.sortRequest;
1541     Z_SortResponse *res = (Z_SortResponse *)odr_malloc (assoc->encode, sizeof(*res));
1542     bend_sort_rr *bsrr = (bend_sort_rr *)odr_malloc (assoc->encode, sizeof(*bsrr));
1543
1544     Z_APDU *apdu = (Z_APDU *)odr_malloc (assoc->encode, sizeof(*apdu));
1545
1546     logf(LOG_LOG, "Got SortRequest.");
1547
1548 #ifdef ASN_COMPILED
1549     bsrr->num_input_setnames = req->num_inputResultSetNames;
1550     bsrr->input_setnames = req->inputResultSetNames;
1551 #else
1552     bsrr->num_input_setnames = req->inputResultSetNames->num_strings;
1553     bsrr->input_setnames = req->inputResultSetNames->strings;
1554 #endif
1555     bsrr->output_setname = req->sortedResultSetName;
1556     bsrr->sort_sequence = req->sortSequence;
1557     bsrr->stream = assoc->encode;
1558
1559     bsrr->sort_status = Z_SortStatus_failure;
1560     bsrr->errcode = 0;
1561     bsrr->errstring = 0;
1562
1563     ((int (*)(void *, bend_sort_rr *))(*assoc->bend_sort))(assoc->backend, bsrr);
1564
1565     res->referenceId = req->referenceId;
1566     res->sortStatus = (int *)odr_malloc (assoc->encode, sizeof(*res->sortStatus));
1567     *res->sortStatus = bsrr->sort_status;
1568     res->resultSetStatus = 0;
1569     if (bsrr->errcode)
1570     {
1571         Z_DiagRecs *dr = diagrecs (assoc, bsrr->errcode, bsrr->errstring);
1572 #ifdef ASN_COMPILED
1573         res->diagnostics = dr->diagRecs;
1574         res->num_diagnostics = dr->num_diagRecs;
1575 #else
1576         res->diagnostics = dr;
1577 #endif
1578     }
1579     else
1580     {
1581 #ifdef ASN_COMPILED
1582         res->num_diagnostics = 0;
1583 #endif
1584         res->diagnostics = 0;
1585     }
1586     res->otherInfo = 0;
1587
1588     apdu->which = Z_APDU_sortResponse;
1589     apdu->u.sortResponse = res;
1590     return apdu;
1591 }
1592
1593 static Z_APDU *process_deleteRequest(association *assoc, request *reqb,
1594     int *fd)
1595 {
1596     Z_DeleteResultSetRequest *req = reqb->request->u.deleteResultSetRequest;
1597     Z_DeleteResultSetResponse *res = (Z_DeleteResultSetResponse *)
1598         odr_malloc (assoc->encode, sizeof(*res));
1599     bend_delete_rr *bdrr = (bend_delete_rr *)
1600         odr_malloc (assoc->encode, sizeof(*bdrr));
1601     Z_APDU *apdu = (Z_APDU *)odr_malloc (assoc->encode, sizeof(*apdu));
1602
1603     logf(LOG_LOG, "Got DeleteRequest.");
1604
1605     bdrr->num_setnames = req->num_ids;
1606     bdrr->setnames = req->resultSetList;
1607     bdrr->stream = assoc->encode;
1608     bdrr->function = *req->deleteFunction;
1609
1610     ((int (*)(void *, bend_delete_rr *))
1611      (*assoc->bend_delete))(assoc->backend, bdrr);
1612     
1613     res->referenceId = req->referenceId;
1614
1615     res->deleteOperationStatus = (int *)
1616         odr_malloc (assoc->encode, sizeof(*res->deleteOperationStatus));
1617     *res->deleteOperationStatus = bdrr->delete_status;
1618
1619     res->num_statuses = 0;
1620     res->deleteListStatuses = 0;
1621     res->numberNotDeleted = 0;
1622     res->num_bulkStatuses = 0;
1623     res->bulkStatuses = 0;
1624     res->deleteMessage = 0;
1625     res->otherInfo = 0;
1626
1627     apdu->which = Z_APDU_deleteResultSetResponse;
1628     apdu->u.deleteResultSetResponse = res;
1629     return apdu;
1630 }
1631
1632 static void process_close(association *assoc, request *reqb)
1633 {
1634     Z_Close *req = reqb->request->u.close;
1635     static char *reasons[] =
1636     {
1637         "finished",
1638         "shutdown",
1639         "systemProblem",
1640         "costLimit",
1641         "resources",
1642         "securityViolation",
1643         "protocolError",
1644         "lackOfActivity",
1645         "peerAbort",
1646         "unspecified"
1647     };
1648
1649     logf(LOG_LOG, "Got Close, reason %s, message %s",
1650         reasons[*req->closeReason], req->diagnosticInformation ?
1651         req->diagnosticInformation : "NULL");
1652     if (assoc->version < 3) /* to make do_force respond with close */
1653         assoc->version = 3;
1654     do_close_req(assoc, Z_Close_finished, "Association terminated by client",
1655                  reqb);
1656 }
1657
1658 void save_referenceId (request *reqb, Z_ReferenceId *refid)
1659 {
1660     if (refid)
1661     {
1662         reqb->len_refid = refid->len;
1663         reqb->refid = (char *)nmem_malloc (reqb->request_mem, refid->len);
1664         memcpy (reqb->refid, refid->buf, refid->len);
1665     }
1666     else
1667     {
1668         reqb->len_refid = 0;
1669         reqb->refid = NULL;
1670     }
1671 }
1672
1673 void bend_request_send (bend_association a, bend_request req, Z_APDU *res)
1674 {
1675     process_response (a, req, res);
1676 }
1677
1678 bend_request bend_request_mk (bend_association a)
1679 {
1680     request *nreq = request_get (&a->outgoing);
1681     nreq->request_mem = nmem_create ();
1682     return nreq;
1683 }
1684
1685 Z_ReferenceId *bend_request_getid (ODR odr, bend_request req)
1686 {
1687     Z_ReferenceId *id;
1688     if (!req->refid)
1689         return 0;
1690     id = (Odr_oct *)odr_malloc (odr, sizeof(*odr));
1691     id->buf = (unsigned char *)odr_malloc (odr, req->len_refid);
1692     id->len = id->size = req->len_refid;
1693     memcpy (id->buf, req->refid, req->len_refid);
1694     return id;
1695 }
1696
1697 void bend_request_destroy (bend_request *req)
1698 {
1699     nmem_destroy((*req)->request_mem);
1700     request_release(*req);
1701     *req = NULL;
1702 }
1703
1704 int bend_backend_respond (bend_association a, bend_request req)
1705 {
1706     return process_request (a, req);
1707 }
1708
1709 void bend_request_setdata(bend_request r, void *p)
1710 {
1711     r->clientData = p;
1712 }
1713
1714 void *bend_request_getdata(bend_request r)
1715 {
1716     return r->clientData;
1717 }
1718
1719 /* Chas: Added in from DALI */
1720 static Z_APDU *process_ESRequest(association *assoc, request *reqb, int *fd)
1721 {
1722     bend_esrequest_rr esrequest;
1723
1724     Z_ExtendedServicesRequest *req = reqb->request->u.extendedServicesRequest;
1725     Z_APDU *apdu = zget_APDU(assoc->encode, Z_APDU_extendedServicesResponse);
1726
1727     Z_ExtendedServicesResponse *resp = apdu->u.extendedServicesResponse;
1728
1729     logf(LOG_DEBUG,"inside Process esRequest");
1730
1731     esrequest.esr = reqb->request->u.extendedServicesRequest;
1732     esrequest.stream = assoc->encode;
1733     esrequest.errcode = 0;
1734     esrequest.errstring = NULL;
1735     esrequest.request = reqb;
1736     esrequest.association = assoc;
1737     
1738     ((int (*)(void *, bend_esrequest_rr *))(*assoc->bend_esrequest))(assoc->backend,
1739                                                                    &esrequest);
1740     
1741     /* If the response is being delayed, return NULL */
1742     if (esrequest.request == NULL)
1743         return(NULL);
1744
1745     resp->referenceId = req->referenceId;
1746
1747     if ( esrequest.errcode == 0 )
1748     {
1749         /* Backend service indicates request will be processed */
1750         logf(LOG_DEBUG,"Request will be processed...Good !");
1751         *resp->operationStatus = Z_ExtendedServicesResponse_done;
1752     }
1753     else
1754     {
1755         /* Backend indicates error, request will not be processed */
1756         logf(LOG_DEBUG,"Request will not be processed...BAD !");
1757         *resp->operationStatus = Z_ExtendedServicesResponse_failure;
1758     }
1759     /* Do something with the members of bend_extendedservice */
1760
1761     logf(LOG_DEBUG,"Send the result apdu");
1762
1763     return apdu;
1764 }
1765
1766 /* Chas: End of addition from DALI */