2 * Copyright (c) 1995-2003, Index Data
3 * See the file LICENSE for details.
5 * $Id: seshigh.c,v 1.13 2004-01-05 09:34:42 adam Exp $
9 * Frontend server logic.
11 * This code receives incoming APDUs, and handles client requests by means
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
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.
31 #include <sys/types.h>
34 #define S_ISREG(x) (x & _S_IFREG)
44 #include <yaz/yconfig.h>
45 #include <yaz/xmalloc.h>
46 #include <yaz/comstack.h>
49 #include <yaz/proto.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>
61 #include <yaz/backend.h>
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,
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,
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,
82 static Z_APDU *process_segmentRequest (association *assoc, request *reqb);
84 static FILE *apduf = 0; /* for use in static mode */
85 static statserv_options_block *control_block = 0;
87 static Z_APDU *process_ESRequest(association *assoc, request *reqb, int *fd);
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.
95 association *create_association(IOCHAN channel, COMSTACK link)
100 control_block = statserv_getcontrol();
101 if (!(anew = (association *)xmalloc(sizeof(*anew))))
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)))
113 if (*control_block->apdufile)
118 strcpy(filename, control_block->apdufile);
119 if (!(anew->print = odr_createmem(ODR_PRINT)))
121 if (*control_block->apdufile == '@')
123 odr_setprint(anew->print, yaz_log_file());
125 else if (*control_block->apdufile != '-')
127 strcpy(filename, control_block->apdufile);
128 if (!control_block->dynamic)
132 if (!(apduf = fopen(filename, "w")))
134 yaz_log(LOG_WARN|LOG_ERRNO, "%s", filename);
137 setvbuf(apduf, 0, _IONBF, 0);
143 sprintf(filename + strlen(filename), ".%d", getpid());
144 if (!(f = fopen(filename, "w")))
146 yaz_log(LOG_WARN|LOG_ERRNO, "%s", filename);
149 setvbuf(f, 0, _IONBF, 0);
151 odr_setprint(anew->print, f);
156 anew->input_buffer = 0;
157 anew->input_buffer_len = 0;
159 anew->state = ASSOC_NEW;
160 request_initq(&anew->incoming);
161 request_initq(&anew->outgoing);
162 anew->proto = cs_getproto(link);
167 * Free association and release resources.
169 void destroy_association(association *h)
171 statserv_options_block *cb = statserv_getcontrol();
175 odr_destroy(h->decode);
176 odr_destroy(h->encode);
178 odr_destroy(h->print);
180 xfree(h->input_buffer);
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);
190 xmalloc_trav("session closed");
191 if (control_block && control_block->one_shot)
197 static void do_close_req(association *a, int reason, char *message,
201 Z_Close *cls = zget_Close(a->encode);
203 /* Purge request queue */
204 while (request_deq(&a->incoming));
205 while (request_deq(&a->outgoing));
208 yaz_log(LOG_LOG, "Sending Close PDU, reason=%d, message=%s",
209 reason, message ? message : "none");
210 apdu.which = Z_APDU_close;
212 *cls->closeReason = reason;
213 cls->diagnosticInformation = message;
214 process_z_response(a, req, &apdu);
215 iochan_settimeout(a->client_chan, 20);
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 */
223 a->state = ASSOC_DEAD;
226 static void do_close(association *a, int reason, char *message)
228 request *req = request_get(&a->outgoing);
229 do_close_req (a, reason, message, req);
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.
238 * h : the I/O channel that has an outstanding event.
239 * event : the current outstanding event.
241 void ir_session(IOCHAN h, int event)
244 association *assoc = (association *)iochan_getdata(h);
245 COMSTACK conn = assoc->client_link;
248 assert(h && conn && assoc);
249 if (event == EVENT_TIMEOUT)
251 if (assoc->state != ASSOC_UP)
253 yaz_log(LOG_LOG, "Final timeout - closing connection.");
255 destroy_association(assoc);
260 yaz_log(LOG_LOG, "Session idle too long. Sending close.");
261 do_close(assoc, Z_Close_lackOfActivity, 0);
265 if (event & assoc->cs_accept_mask)
267 yaz_log (LOG_DEBUG, "ir_session (accept)");
268 if (!cs_accept (conn))
270 yaz_log (LOG_LOG, "accept failed");
271 destroy_association(assoc);
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);
281 iochan_setflag (h, assoc->cs_accept_mask);
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);
291 if ((event & assoc->cs_get_mask) || (event & EVENT_WORK)) /* input */
293 if ((assoc->cs_put_mask & EVENT_INPUT) == 0 && (event & assoc->cs_get_mask))
295 yaz_log(LOG_DEBUG, "ir_session (input)");
296 /* We aren't speaking to this fellow */
297 if (assoc->state == ASSOC_DEAD)
299 yaz_log(LOG_LOG, "Connection closed - end of session");
301 destroy_association(assoc);
305 assoc->cs_get_mask = EVENT_INPUT;
306 if ((res = cs_get(conn, &assoc->input_buffer,
307 &assoc->input_buffer_len)) <= 0)
309 yaz_log(LOG_LOG, "Connection closed by client");
311 destroy_association(assoc);
315 else if (res == 1) /* incomplete read - wait for more */
317 if (conn->io_pending & CS_WANT_WRITE)
318 assoc->cs_get_mask |= EVENT_OUTPUT;
319 iochan_setflag(h, assoc->cs_get_mask);
322 if (cs_more(conn)) /* more stuff - call us again later, please */
323 iochan_setevent(h, EVENT_INPUT);
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))
335 yaz_log(LOG_LOG, "ODR error on incoming PDU: %s [element %s] "
337 odr_errmsg(odr_geterror(assoc->decode)),
338 odr_getelement(assoc->decode),
339 odr_offset(assoc->decode));
340 if (assoc->decode->error != OHTTP)
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");
349 Z_GDU *p = z_get_HTTP_Response(assoc->encode, 400);
350 assoc->state = ASSOC_DEAD;
351 process_gdu_response(assoc, req, p);
355 req->request_mem = odr_extract_mem(assoc->decode);
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);
363 request_enq(&assoc->incoming, req);
366 /* can we do something yet? */
367 req = request_head(&assoc->incoming);
368 if (req->state == REQUEST_IDLE)
370 request_deq(&assoc->incoming);
371 process_gdu_request(assoc, req);
374 if (event & assoc->cs_put_mask)
376 request *req = request_head(&assoc->outgoing);
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))
384 yaz_log(LOG_LOG, "Connection closed by client");
386 destroy_association(assoc);
389 case 0: /* all sent - release the request structure */
390 yaz_log(LOG_DEBUG, "Wrote PDU, %d bytes", req->len_response);
392 yaz_log(LOG_DEBUG, "HTTP out:\n%.*s", req->len_response,
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);
407 assoc->cs_put_mask = EVENT_OUTPUT;
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);
418 if (event & EVENT_EXCEPT)
420 yaz_log(LOG_LOG, "ir_session (exception)");
422 destroy_association(assoc);
427 static int process_z_request(association *assoc, request *req, char **msg);
429 static void assoc_init_reset(association *assoc)
432 assoc->init = (bend_initrequest *) xmalloc (sizeof(*assoc->init));
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;
451 assoc->init->charneg_request = NULL;
452 assoc->init->charneg_response = NULL;
454 assoc->init->decode = assoc->decode;
455 assoc->init->peer_name =
456 odr_strdup (assoc->encode, cs_addrstr(assoc->client_link));
459 static int srw_bend_init(association *assoc)
461 const char *encoding = "UTF-8";
463 bend_initresult *binitres;
464 statserv_options_block *cb = statserv_getcontrol();
466 assoc_init_reset(assoc);
468 assoc->maximumRecordSize = 3000000;
469 assoc->preferredMessageSize = 3000000;
471 ce = yaz_set_proposal_charneg(assoc->decode, &encoding, 1, 0, 0, 1);
472 assoc->init->charneg_request = ce->u.charNeg3;
474 if (!(binitres = (*cb->bend_init)(assoc->init)))
476 yaz_log(LOG_WARN, "Bad response from backend.");
479 assoc->backend = binitres->handle;
483 static int srw_bend_fetch(association *assoc, int pos,
484 Z_SRW_searchRetrieveRequest *srw_req,
485 Z_SRW_record *record)
488 ODR o = assoc->encode;
490 rr.setname = "default";
493 rr.request_format = VAL_TEXT_XML;
494 rr.request_format_raw = yaz_oidval_to_z3950oid(assoc->decode,
497 rr.comp = (Z_RecordComposition *)
498 odr_malloc(assoc->decode, sizeof(*rr.comp));
499 rr.comp->which = Z_RecordComp_complex;
500 rr.comp->u.complex = (Z_CompSpec *)
501 odr_malloc(assoc->decode, sizeof(Z_CompSpec));
502 rr.comp->u.complex->selectAlternativeSyntax = (bool_t *)
503 odr_malloc(assoc->encode, sizeof(bool_t));
504 *rr.comp->u.complex->selectAlternativeSyntax = 0;
505 rr.comp->u.complex->num_dbSpecific = 0;
506 rr.comp->u.complex->dbSpecific = 0;
507 rr.comp->u.complex->num_recordSyntax = 0;
508 rr.comp->u.complex->recordSyntax = 0;
510 rr.comp->u.complex->generic = (Z_Specification *)
511 odr_malloc(assoc->decode, sizeof(Z_Specification));
512 rr.comp->u.complex->generic->which = Z_Schema_uri;
513 rr.comp->u.complex->generic->schema.uri = srw_req->recordSchema;
514 rr.comp->u.complex->generic->elementSpec = 0;
516 rr.stream = assoc->encode;
517 rr.print = assoc->print;
523 rr.output_format = VAL_TEXT_XML;
524 rr.output_format_raw = 0;
527 rr.surrogate_flag = 0;
528 rr.schema = srw_req->recordSchema;
530 if (!assoc->init->bend_fetch)
533 (*assoc->init->bend_fetch)(assoc->backend, &rr);
537 record->recordData_buf = rr.record;
538 record->recordData_len = rr.len;
539 record->recordPosition = odr_intdup(o, pos);
541 record->recordSchema = odr_strdup(o, rr.schema);
543 record->recordSchema = 0;
548 static void srw_bend_search(association *assoc, request *req,
549 Z_SRW_searchRetrieveRequest *srw_req,
550 Z_SRW_searchRetrieveResponse *srw_res,
558 yaz_log(LOG_LOG, "Got SRW SearchRetrieveRequest");
559 yaz_log(LOG_DEBUG, "srw_bend_search");
562 yaz_log(LOG_DEBUG, "srw_bend_init");
563 if (!srw_bend_init(assoc))
565 srw_error = 3; /* assume Authentication error */
567 srw_res->num_diagnostics = 1;
568 srw_res->diagnostics = (Z_SRW_diagnostic *)
569 odr_malloc(assoc->encode, sizeof(*srw_res->diagnostics));
570 srw_res->diagnostics[0].code =
571 odr_intdup(assoc->encode, srw_error);
572 srw_res->diagnostics[0].details = 0;
577 rr.setname = "default";
580 rr.basenames = &srw_req->database;
583 rr.query = (Z_Query *) odr_malloc (assoc->decode, sizeof(*rr.query));
585 if (srw_req->query_type == Z_SRW_query_type_cql)
587 ext = (Z_External *) odr_malloc(assoc->decode, sizeof(*ext));
588 ext->direct_reference = odr_getoidbystr(assoc->decode,
589 "1.2.840.10003.16.2");
590 ext->indirect_reference = 0;
592 ext->which = Z_External_CQL;
593 ext->u.cql = srw_req->query.cql;
595 rr.query->which = Z_Query_type_104;
596 rr.query->u.type_104 = ext;
598 else if (srw_req->query_type == Z_SRW_query_type_pqf)
600 Z_RPNQuery *RPNquery;
601 YAZ_PQF_Parser pqf_parser;
603 pqf_parser = yaz_pqf_create ();
605 RPNquery = yaz_pqf_parse (pqf_parser, assoc->decode,
611 int code = yaz_pqf_error (pqf_parser, &pqf_msg, &off);
612 yaz_log(LOG_LOG, "%*s^\n", off+4, "");
613 yaz_log(LOG_LOG, "Bad PQF: %s (code %d)\n", pqf_msg, code);
618 rr.query->which = Z_Query_type_1;
619 rr.query->u.type_1 = RPNquery;
621 yaz_pqf_destroy (pqf_parser);
626 if (!srw_error && srw_req->sort_type != Z_SRW_sort_type_none)
629 if (!srw_error && !assoc->init->bend_search)
634 yaz_log(LOG_DEBUG, "srw_bend_search returned SRW error %d", srw_error);
635 srw_res->num_diagnostics = 1;
636 srw_res->diagnostics = (Z_SRW_diagnostic *)
637 odr_malloc(assoc->encode, sizeof(*srw_res->diagnostics));
638 srw_res->diagnostics[0].code =
639 odr_intdup(assoc->encode, srw_error);
640 srw_res->diagnostics[0].details = 0;
644 rr.stream = assoc->encode;
645 rr.decode = assoc->decode;
646 rr.print = assoc->print;
648 rr.association = assoc;
654 yaz_log_zquery(rr.query);
655 (assoc->init->bend_search)(assoc->backend, &rr);
656 srw_res->numberOfRecords = odr_intdup(assoc->encode, rr.hits);
659 yaz_log(LOG_DEBUG, "bend_search returned Bib-1 code %d", rr.errcode);
660 if (rr.errcode == 109) /* database unavailable */
665 srw_res->num_diagnostics = 1;
666 srw_res->diagnostics = (Z_SRW_diagnostic *)
667 odr_malloc(assoc->encode, sizeof(*srw_res->diagnostics));
668 srw_res->diagnostics[0].code =
669 odr_intdup(assoc->encode,
670 yaz_diag_bib1_to_srw (rr.errcode));
671 srw_res->diagnostics[0].details = rr.errstring;
672 yaz_log(LOG_DEBUG, "srw_bend_search returned SRW error %d",
673 *srw_res->diagnostics[0].code);
678 int number = srw_req->maximumRecords ? *srw_req->maximumRecords : 0;
679 int start = srw_req->startRecord ? *srw_req->startRecord : 1;
681 yaz_log(LOG_LOG, "Request to pack %d+%d out of %d",
682 start, number, rr.hits);
684 srw_res->numberOfRecords = odr_intdup(assoc->encode, rr.hits);
691 yaz_log(LOG_LOG, "Request out or range");
696 int packing = Z_SRW_recordPacking_string;
697 if (start + number > rr.hits)
698 number = rr.hits - start + 1;
699 if (srw_req->recordPacking &&
700 !strcmp(srw_req->recordPacking, "xml"))
701 packing = Z_SRW_recordPacking_XML;
702 srw_res->records = (Z_SRW_record *)
703 odr_malloc(assoc->encode,
704 number * sizeof(*srw_res->records));
705 for (i = 0; i<number; i++)
709 srw_res->records[j].recordPacking = packing;
710 srw_res->records[j].recordData_buf = 0;
711 yaz_log(LOG_DEBUG, "srw_bend_fetch %d", i+start);
712 errcode = srw_bend_fetch(assoc, i+start, srw_req,
713 srw_res->records + j);
716 srw_res->num_diagnostics = 1;
717 srw_res->diagnostics = (Z_SRW_diagnostic *)
718 odr_malloc(assoc->encode,
719 sizeof(*srw_res->diagnostics));
720 srw_res->diagnostics[0].code =
721 odr_intdup(assoc->encode,
722 yaz_diag_bib1_to_srw (errcode));
723 srw_res->diagnostics[0].details = rr.errstring;
726 if (srw_res->records[j].recordData_buf)
729 srw_res->num_records = j;
731 srw_res->records = 0;
737 static void srw_bend_explain(association *assoc, request *req,
738 Z_SRW_explainRequest *srw_req,
739 Z_SRW_explainResponse *srw_res,
742 yaz_log(LOG_LOG, "Got SRW ExplainRequest");
746 yaz_log(LOG_DEBUG, "srw_bend_init");
747 if (!srw_bend_init(assoc))
752 if (assoc->init && assoc->init->bend_explain)
756 rr.stream = assoc->encode;
757 rr.decode = assoc->decode;
758 rr.print = assoc->print;
760 rr.database = srw_req->database;
761 (*assoc->init->bend_explain)(assoc->backend, &rr);
764 int packing = Z_SRW_recordPacking_string;
765 if (srw_req->recordPacking &&
766 !strcmp(srw_req->recordPacking, "xml"))
767 packing = Z_SRW_recordPacking_XML;
768 srw_res->record.recordSchema = 0;
769 srw_res->record.recordPacking = packing;
770 srw_res->record.recordData_buf = rr.explain_buf;
771 srw_res->record.recordData_len = strlen(rr.explain_buf);
772 srw_res->record.recordPosition = 0;
778 static void process_http_request(association *assoc, request *req)
780 Z_HTTP_Request *hreq = req->gdu_request->u.HTTP_Request;
781 ODR o = assoc->encode;
784 Z_SOAP *soap_package = 0;
787 Z_HTTP_Response *hres;
790 r = yaz_srw_decode(hreq, &sr, &soap_package, assoc->decode, &charset);
791 if (r == 2) /* not taken */
792 r = yaz_sru_decode(hreq, &sr, &soap_package, assoc->decode, &charset);
793 if (r == 0) /* decode SRW/SRU OK .. */
796 if (sr->which == Z_SRW_searchRetrieve_request)
799 yaz_srw_get(assoc->encode, Z_SRW_searchRetrieve_response);
801 srw_bend_search(assoc, req, sr->u.request, res->u.response,
803 if (http_code == 200)
804 soap_package->u.generic->p = res;
806 else if (sr->which == Z_SRW_explain_request)
808 Z_SRW_PDU *res = yaz_srw_get(o, Z_SRW_explain_response);
809 srw_bend_explain(assoc, req, sr->u.explain_request,
810 res->u.explain_response, &http_code);
811 if (http_code == 200)
812 soap_package->u.generic->p = res;
817 z_soap_error(assoc->encode, soap_package,
818 "SOAP-ENV:Client", "Bad method", 0);
820 if (http_code == 200 || http_code == 500)
822 static Z_SOAP_Handler soap_handlers[3] = {
824 {"http://www.loc.gov/zing/srw/", 0,
825 (Z_SOAP_fun) yaz_srw_codec},
826 {"http://www.loc.gov/zing/srw/v1.0/", 0,
827 (Z_SOAP_fun) yaz_srw_codec},
833 p = z_get_HTTP_Response(o, 200);
834 hres = p->u.HTTP_Response;
835 ret = z_soap_codec_enc(assoc->encode, &soap_package,
836 &hres->content_buf, &hres->content_len,
837 soap_handlers, charset);
838 hres->code = http_code;
840 strcpy(ctype, "text/xml");
843 strcat(ctype, "; charset=");
844 strcat(ctype, charset);
846 z_HTTP_header_add(o, &hres->headers, "Content-Type", ctype);
849 p = z_get_HTTP_Response(o, http_code);
852 p = z_get_HTTP_Response(o, 500);
853 hres = p->u.HTTP_Response;
854 if (!strcmp(hreq->version, "1.0"))
856 const char *v = z_HTTP_header_lookup(hreq->headers, "Connection");
857 if (v && !strcmp(v, "Keep-Alive"))
861 hres->version = "1.0";
865 const char *v = z_HTTP_header_lookup(hreq->headers, "Connection");
866 if (v && !strcmp(v, "close"))
870 hres->version = "1.1";
874 z_HTTP_header_add(o, &hres->headers, "Connection", "close");
875 assoc->state = ASSOC_DEAD;
876 assoc->cs_get_mask = 0;
881 const char *alive = z_HTTP_header_lookup(hreq->headers, "Keep-Alive");
883 if (alive && isdigit(*alive))
887 if (t < 0 || t > 3600)
889 iochan_settimeout(assoc->client_chan,t);
890 z_HTTP_header_add(o, &hres->headers, "Connection", "Keep-Alive");
892 process_gdu_response(assoc, req, p);
895 static void process_gdu_request(association *assoc, request *req)
897 if (req->gdu_request->which == Z_GDU_Z3950)
900 req->apdu_request = req->gdu_request->u.z3950;
901 if (process_z_request(assoc, req, &msg) < 0)
902 do_close_req(assoc, Z_Close_systemProblem, msg, req);
904 else if (req->gdu_request->which == Z_GDU_HTTP_Request)
905 process_http_request(assoc, req);
908 do_close_req(assoc, Z_Close_systemProblem, "bad protocol packet", req);
913 * Initiate request processing.
915 static int process_z_request(association *assoc, request *req, char **msg)
921 *msg = "Unknown Error";
922 assert(req && req->state == REQUEST_IDLE);
923 if (req->apdu_request->which != Z_APDU_initRequest && !assoc->init)
925 *msg = "Missing InitRequest";
928 switch (req->apdu_request->which)
930 case Z_APDU_initRequest:
931 iochan_settimeout(assoc->client_chan,
932 statserv_getcontrol()->idle_timeout * 60);
933 res = process_initRequest(assoc, req); break;
934 case Z_APDU_searchRequest:
935 res = process_searchRequest(assoc, req, &fd); break;
936 case Z_APDU_presentRequest:
937 res = process_presentRequest(assoc, req, &fd); break;
938 case Z_APDU_scanRequest:
939 if (assoc->init->bend_scan)
940 res = process_scanRequest(assoc, req, &fd);
943 *msg = "Cannot handle Scan APDU";
947 case Z_APDU_extendedServicesRequest:
948 if (assoc->init->bend_esrequest)
949 res = process_ESRequest(assoc, req, &fd);
952 *msg = "Cannot handle Extended Services APDU";
956 case Z_APDU_sortRequest:
957 if (assoc->init->bend_sort)
958 res = process_sortRequest(assoc, req, &fd);
961 *msg = "Cannot handle Sort APDU";
966 process_close(assoc, req);
968 case Z_APDU_deleteResultSetRequest:
969 if (assoc->init->bend_delete)
970 res = process_deleteRequest(assoc, req, &fd);
973 *msg = "Cannot handle Delete APDU";
977 case Z_APDU_segmentRequest:
978 if (assoc->init->bend_segment)
980 res = process_segmentRequest (assoc, req);
984 *msg = "Cannot handle Segment APDU";
989 *msg = "Bad APDU received";
994 yaz_log(LOG_DEBUG, " result immediately available");
995 retval = process_z_response(assoc, req, res);
999 yaz_log(LOG_DEBUG, " result unavailble");
1002 else /* no result yet - one will be provided later */
1006 /* Set up an I/O handler for the fd supplied by the backend */
1008 yaz_log(LOG_DEBUG, " establishing handler for result");
1009 req->state = REQUEST_PENDING;
1010 if (!(chan = iochan_create(fd, backend_response, EVENT_INPUT)))
1012 iochan_setdata(chan, assoc);
1019 * Handle message from the backend.
1021 void backend_response(IOCHAN i, int event)
1023 association *assoc = (association *)iochan_getdata(i);
1024 request *req = request_head(&assoc->incoming);
1028 yaz_log(LOG_DEBUG, "backend_response");
1029 assert(assoc && req && req->state != REQUEST_IDLE);
1030 /* determine what it is we're waiting for */
1031 switch (req->apdu_request->which)
1033 case Z_APDU_searchRequest:
1034 res = response_searchRequest(assoc, req, 0, &fd); break;
1036 case Z_APDU_presentRequest:
1037 res = response_presentRequest(assoc, req, 0, &fd); break;
1038 case Z_APDU_scanRequest:
1039 res = response_scanRequest(assoc, req, 0, &fd); break;
1042 yaz_log(LOG_WARN, "Serious programmer's lapse or bug");
1045 if ((res && process_z_response(assoc, req, res) < 0) || fd < 0)
1047 yaz_log(LOG_LOG, "Fatal error when talking to backend");
1048 do_close(assoc, Z_Close_systemProblem, 0);
1052 else if (!res) /* no result yet - try again later */
1054 yaz_log(LOG_DEBUG, " no result yet");
1055 iochan_setfd(i, fd); /* in case fd has changed */
1060 * Encode response, and transfer the request structure to the outgoing queue.
1062 static int process_gdu_response(association *assoc, request *req, Z_GDU *res)
1064 odr_setbuf(assoc->encode, req->response, req->size_response, 1);
1068 if (!z_GDU(assoc->print, &res, 0, 0))
1069 yaz_log(LOG_WARN, "ODR print error: %s",
1070 odr_errmsg(odr_geterror(assoc->print)));
1071 odr_reset(assoc->print);
1073 if (!z_GDU(assoc->encode, &res, 0, 0))
1075 yaz_log(LOG_WARN, "ODR error when encoding PDU: %s [element %s]",
1076 odr_errmsg(odr_geterror(assoc->decode)),
1077 odr_getelement(assoc->decode));
1078 request_release(req);
1081 req->response = odr_getbuf(assoc->encode, &req->len_response,
1082 &req->size_response);
1083 odr_setbuf(assoc->encode, 0, 0, 0); /* don'txfree if we abort later */
1084 odr_reset(assoc->encode);
1085 req->state = REQUEST_IDLE;
1086 request_enq(&assoc->outgoing, req);
1087 /* turn the work over to the ir_session handler */
1088 iochan_setflag(assoc->client_chan, EVENT_OUTPUT);
1089 assoc->cs_put_mask = EVENT_OUTPUT;
1090 /* Is there more work to be done? give that to the input handler too */
1092 if (request_head(&assoc->incoming))
1094 yaz_log (LOG_DEBUG, "more work to be done");
1095 iochan_setevent(assoc->client_chan, EVENT_WORK);
1102 * Encode response, and transfer the request structure to the outgoing queue.
1104 static int process_z_response(association *assoc, request *req, Z_APDU *res)
1106 Z_GDU *gres = (Z_GDU *) odr_malloc(assoc->encode, sizeof(*res));
1107 gres->which = Z_GDU_Z3950;
1108 gres->u.z3950 = res;
1110 return process_gdu_response(assoc, req, gres);
1115 * Handle init request.
1116 * At the moment, we don't check the options
1117 * anywhere else in the code - we just try not to do anything that would
1118 * break a naive client. We'll toss 'em into the association block when
1119 * we need them there.
1121 static Z_APDU *process_initRequest(association *assoc, request *reqb)
1123 statserv_options_block *cb = statserv_getcontrol();
1124 Z_InitRequest *req = reqb->apdu_request->u.initRequest;
1125 Z_APDU *apdu = zget_APDU(assoc->encode, Z_APDU_initResponse);
1126 Z_InitResponse *resp = apdu->u.initResponse;
1127 bend_initresult *binitres;
1131 yaz_log(LOG_LOG, "Got initRequest");
1132 if (req->implementationId)
1133 yaz_log(LOG_LOG, "Id: %s", req->implementationId);
1134 if (req->implementationName)
1135 yaz_log(LOG_LOG, "Name: %s", req->implementationName);
1136 if (req->implementationVersion)
1137 yaz_log(LOG_LOG, "Version: %s", req->implementationVersion);
1139 assoc_init_reset(assoc);
1141 assoc->init->auth = req->idAuthentication;
1142 assoc->init->referenceId = req->referenceId;
1144 if (ODR_MASK_GET(req->options, Z_Options_negotiationModel))
1146 Z_CharSetandLanguageNegotiation *negotiation =
1147 yaz_get_charneg_record (req->otherInfo);
1148 if (negotiation->which == Z_CharSetandLanguageNegotiation_proposal)
1149 assoc->init->charneg_request = negotiation;
1152 if (!(binitres = (*cb->bend_init)(assoc->init)))
1154 yaz_log(LOG_WARN, "Bad response from backend.");
1158 assoc->backend = binitres->handle;
1159 if ((assoc->init->bend_sort))
1160 yaz_log (LOG_DEBUG, "Sort handler installed");
1161 if ((assoc->init->bend_search))
1162 yaz_log (LOG_DEBUG, "Search handler installed");
1163 if ((assoc->init->bend_present))
1164 yaz_log (LOG_DEBUG, "Present handler installed");
1165 if ((assoc->init->bend_esrequest))
1166 yaz_log (LOG_DEBUG, "ESRequest handler installed");
1167 if ((assoc->init->bend_delete))
1168 yaz_log (LOG_DEBUG, "Delete handler installed");
1169 if ((assoc->init->bend_scan))
1170 yaz_log (LOG_DEBUG, "Scan handler installed");
1171 if ((assoc->init->bend_segment))
1172 yaz_log (LOG_DEBUG, "Segment handler installed");
1174 resp->referenceId = req->referenceId;
1176 /* let's tell the client what we can do */
1177 if (ODR_MASK_GET(req->options, Z_Options_search))
1179 ODR_MASK_SET(resp->options, Z_Options_search);
1180 strcat(options, "srch");
1182 if (ODR_MASK_GET(req->options, Z_Options_present))
1184 ODR_MASK_SET(resp->options, Z_Options_present);
1185 strcat(options, " prst");
1187 if (ODR_MASK_GET(req->options, Z_Options_delSet) &&
1188 assoc->init->bend_delete)
1190 ODR_MASK_SET(resp->options, Z_Options_delSet);
1191 strcat(options, " del");
1193 if (ODR_MASK_GET(req->options, Z_Options_extendedServices) &&
1194 assoc->init->bend_esrequest)
1196 ODR_MASK_SET(resp->options, Z_Options_extendedServices);
1197 strcat (options, " extendedServices");
1199 if (ODR_MASK_GET(req->options, Z_Options_namedResultSets))
1201 ODR_MASK_SET(resp->options, Z_Options_namedResultSets);
1202 strcat(options, " namedresults");
1204 if (ODR_MASK_GET(req->options, Z_Options_scan) && assoc->init->bend_scan)
1206 ODR_MASK_SET(resp->options, Z_Options_scan);
1207 strcat(options, " scan");
1209 if (ODR_MASK_GET(req->options, Z_Options_concurrentOperations))
1211 ODR_MASK_SET(resp->options, Z_Options_concurrentOperations);
1212 strcat(options, " concurrop");
1214 if (ODR_MASK_GET(req->options, Z_Options_sort) && assoc->init->bend_sort)
1216 ODR_MASK_SET(resp->options, Z_Options_sort);
1217 strcat(options, " sort");
1220 if (ODR_MASK_GET(req->options, Z_Options_negotiationModel)
1221 && assoc->init->charneg_response)
1223 Z_OtherInformation **p;
1224 Z_OtherInformationUnit *p0;
1226 yaz_oi_APDU(apdu, &p);
1228 if ((p0=yaz_oi_update(p, assoc->encode, NULL, 0, 0))) {
1229 ODR_MASK_SET(resp->options, Z_Options_negotiationModel);
1231 p0->which = Z_OtherInfo_externallyDefinedInfo;
1232 p0->information.externallyDefinedInfo =
1233 assoc->init->charneg_response;
1235 ODR_MASK_SET(resp->options, Z_Options_negotiationModel);
1236 strcat(options, " negotiation");
1239 if (ODR_MASK_GET(req->protocolVersion, Z_ProtocolVersion_1))
1241 ODR_MASK_SET(resp->protocolVersion, Z_ProtocolVersion_1);
1242 assoc->version = 2; /* 1 & 2 are equivalent */
1244 if (ODR_MASK_GET(req->protocolVersion, Z_ProtocolVersion_2))
1246 ODR_MASK_SET(resp->protocolVersion, Z_ProtocolVersion_2);
1249 if (ODR_MASK_GET(req->protocolVersion, Z_ProtocolVersion_3))
1251 ODR_MASK_SET(resp->protocolVersion, Z_ProtocolVersion_3);
1255 yaz_log(LOG_LOG, "Negotiated to v%d: %s", assoc->version, options);
1256 assoc->maximumRecordSize = *req->maximumRecordSize;
1257 if (assoc->maximumRecordSize > control_block->maxrecordsize)
1258 assoc->maximumRecordSize = control_block->maxrecordsize;
1259 assoc->preferredMessageSize = *req->preferredMessageSize;
1260 if (assoc->preferredMessageSize > assoc->maximumRecordSize)
1261 assoc->preferredMessageSize = assoc->maximumRecordSize;
1263 resp->preferredMessageSize = &assoc->preferredMessageSize;
1264 resp->maximumRecordSize = &assoc->maximumRecordSize;
1266 resp->implementationId = odr_prepend(assoc->encode,
1267 assoc->init->implementation_id,
1268 resp->implementationId);
1270 resp->implementationName = odr_prepend(assoc->encode,
1271 assoc->init->implementation_name,
1272 odr_prepend(assoc->encode, "GFS", resp->implementationName));
1274 version = odr_strdup(assoc->encode, "$Revision: 1.13 $");
1275 if (strlen(version) > 10) /* check for unexpanded CVS strings */
1276 version[strlen(version)-2] = '\0';
1277 resp->implementationVersion = odr_prepend(assoc->encode,
1278 assoc->init->implementation_version,
1279 odr_prepend(assoc->encode, &version[11],
1280 resp->implementationVersion));
1282 if (binitres->errcode)
1284 yaz_log(LOG_LOG, "Connection rejected by backend.");
1286 assoc->state = ASSOC_DEAD;
1287 resp->userInformationField = init_diagnostics(assoc->encode,
1289 binitres->errstring);
1292 assoc->state = ASSOC_UP;
1297 * Diagnostic in default format, to be returned as either a surrogate
1298 * or non-surrogate diagnostic in the context of an open session, or
1299 * as User-information when an Init is refused.
1301 static Z_DefaultDiagFormat *justdiag(ODR odr, int error, char *addinfo)
1303 int *err = odr_intdup(odr, error);
1304 Z_DefaultDiagFormat *dr = (Z_DefaultDiagFormat *)
1305 odr_malloc (odr, sizeof(*dr));
1307 yaz_log(LOG_LOG, "[%d] %s%s%s", error, diagbib1_str(error),
1308 addinfo ? " -- " : "", addinfo ? addinfo : "");
1310 dr->diagnosticSetId =
1311 yaz_oidval_to_z3950oid (odr, CLASS_DIAGSET, VAL_BIB1);
1312 dr->condition = err;
1313 dr->which = Z_DefaultDiagFormat_v2Addinfo;
1314 dr->u.v2Addinfo = odr_strdup (odr, addinfo ? addinfo : "");
1319 * Set the specified `errcode' and `errstring' into a UserInfo-1
1320 * external to be returned to the client in accordance with Z35.90
1321 * Implementor Agreement 5 (Returning diagnostics in an InitResponse):
1322 * http://lcweb.loc.gov/z3950/agency/agree/initdiag.html
1324 static Z_External *init_diagnostics(ODR odr, int error, char *addinfo)
1328 Z_OtherInformation *u;
1329 Z_OtherInformationUnit *l;
1330 Z_DiagnosticFormat *d;
1331 Z_DiagnosticFormat_s *e;
1333 x = (Z_External*) odr_malloc(odr, sizeof *x);
1335 x->indirect_reference = 0;
1336 oid.proto = PROTO_Z3950;
1337 oid.oclass = CLASS_USERINFO;
1338 oid.value = VAL_USERINFO1;
1339 x->direct_reference = odr_oiddup(odr, oid_getoidbyent(&oid));
1340 x->which = Z_External_userInfo1;
1342 u = odr_malloc(odr, sizeof *u);
1344 u->num_elements = 1;
1345 u->list = (Z_OtherInformationUnit**) odr_malloc(odr, sizeof *u->list);
1346 u->list[0] = (Z_OtherInformationUnit*) odr_malloc(odr, sizeof *u->list[0]);
1349 l->which = Z_OtherInfo_externallyDefinedInfo;
1351 x2 = (Z_External*) odr_malloc(odr, sizeof *x);
1352 l->information.externallyDefinedInfo = x2;
1354 x2->indirect_reference = 0;
1355 oid.oclass = CLASS_DIAGSET;
1356 oid.value = VAL_DIAG1;
1357 x2->direct_reference = odr_oiddup(odr, oid_getoidbyent(&oid));
1358 x2->which = Z_External_diag1;
1360 d = (Z_DiagnosticFormat*) odr_malloc(odr, sizeof *d);
1363 d->elements = (Z_DiagnosticFormat_s**) odr_malloc (odr, sizeof *d->elements);
1364 d->elements[0] = (Z_DiagnosticFormat_s*) odr_malloc (odr, sizeof *d->elements[0]);
1367 e->which = Z_DiagnosticFormat_s_defaultDiagRec;
1368 e->u.defaultDiagRec = justdiag(odr, error, addinfo);
1373 * nonsurrogate diagnostic record.
1375 static Z_Records *diagrec(association *assoc, int error, char *addinfo)
1377 Z_Records *rec = (Z_Records *)
1378 odr_malloc (assoc->encode, sizeof(*rec));
1379 rec->which = Z_Records_NSD;
1380 rec->u.nonSurrogateDiagnostic = justdiag(assoc->encode, error, addinfo);
1385 * surrogate diagnostic.
1387 static Z_NamePlusRecord *surrogatediagrec(association *assoc, char *dbname,
1388 int error, char *addinfo)
1390 Z_NamePlusRecord *rec = (Z_NamePlusRecord *)
1391 odr_malloc (assoc->encode, sizeof(*rec));
1392 Z_DiagRec *drec = (Z_DiagRec *)odr_malloc (assoc->encode, sizeof(*drec));
1394 yaz_log(LOG_DEBUG, "SurrogateDiagnotic: %d -- %s", error, addinfo);
1395 rec->databaseName = dbname;
1396 rec->which = Z_NamePlusRecord_surrogateDiagnostic;
1397 rec->u.surrogateDiagnostic = drec;
1398 drec->which = Z_DiagRec_defaultFormat;
1399 drec->u.defaultFormat = justdiag(assoc->encode, error, addinfo);
1405 * multiple nonsurrogate diagnostics.
1407 static Z_DiagRecs *diagrecs(association *assoc, int error, char *addinfo)
1409 Z_DiagRecs *recs = (Z_DiagRecs *)odr_malloc (assoc->encode, sizeof(*recs));
1410 int *err = odr_intdup(assoc->encode, error);
1411 Z_DiagRec **recp = (Z_DiagRec **)odr_malloc (assoc->encode, sizeof(*recp));
1412 Z_DiagRec *drec = (Z_DiagRec *)odr_malloc (assoc->encode, sizeof(*drec));
1413 Z_DefaultDiagFormat *rec = (Z_DefaultDiagFormat *)
1414 odr_malloc (assoc->encode, sizeof(*rec));
1416 yaz_log(LOG_DEBUG, "DiagRecs: %d -- %s", error, addinfo ? addinfo : "");
1418 recs->num_diagRecs = 1;
1419 recs->diagRecs = recp;
1421 drec->which = Z_DiagRec_defaultFormat;
1422 drec->u.defaultFormat = rec;
1424 rec->diagnosticSetId =
1425 yaz_oidval_to_z3950oid (assoc->encode, CLASS_DIAGSET, VAL_BIB1);
1426 rec->condition = err;
1428 rec->which = Z_DefaultDiagFormat_v2Addinfo;
1429 rec->u.v2Addinfo = odr_strdup (assoc->encode, addinfo ? addinfo : "");
1433 static Z_Records *pack_records(association *a, char *setname, int start,
1434 int *num, Z_RecordComposition *comp,
1435 int *next, int *pres, oid_value format,
1436 Z_ReferenceId *referenceId,
1439 int recno, total_length = 0, toget = *num, dumped_records = 0;
1440 Z_Records *records =
1441 (Z_Records *) odr_malloc (a->encode, sizeof(*records));
1442 Z_NamePlusRecordList *reclist =
1443 (Z_NamePlusRecordList *) odr_malloc (a->encode, sizeof(*reclist));
1444 Z_NamePlusRecord **list =
1445 (Z_NamePlusRecord **) odr_malloc (a->encode, sizeof(*list) * toget);
1447 records->which = Z_Records_DBOSD;
1448 records->u.databaseOrSurDiagnostics = reclist;
1449 reclist->num_records = 0;
1450 reclist->records = list;
1451 *pres = Z_PRES_SUCCESS;
1455 yaz_log(LOG_LOG, "Request to pack %d+%d+%s", start, toget, setname);
1456 yaz_log(LOG_DEBUG, "pms=%d, mrs=%d", a->preferredMessageSize,
1457 a->maximumRecordSize);
1458 for (recno = start; reclist->num_records < toget; recno++)
1461 Z_NamePlusRecord *thisrec;
1462 int this_length = 0;
1464 * we get the number of bytes allocated on the stream before any
1465 * allocation done by the backend - this should give us a reasonable
1466 * idea of the total size of the data so far.
1468 total_length = odr_total(a->encode) - dumped_records;
1474 freq.last_in_set = 0;
1475 freq.setname = setname;
1476 freq.surrogate_flag = 0;
1477 freq.number = recno;
1479 freq.request_format = format;
1480 freq.request_format_raw = oid;
1481 freq.output_format = format;
1482 freq.output_format_raw = 0;
1483 freq.stream = a->encode;
1484 freq.print = a->print;
1485 freq.referenceId = referenceId;
1487 (*a->init->bend_fetch)(a->backend, &freq);
1488 /* backend should be able to signal whether error is system-wide
1489 or only pertaining to current record */
1492 if (!freq.surrogate_flag)
1495 *pres = Z_PRES_FAILURE;
1496 /* for 'present request out of range',
1497 set addinfo to record position if not set */
1498 if (freq.errcode == 13 && freq.errstring == 0)
1500 sprintf (s, "%d", recno);
1503 return diagrec(a, freq.errcode, freq.errstring);
1505 reclist->records[reclist->num_records] =
1506 surrogatediagrec(a, freq.basename, freq.errcode,
1508 reclist->num_records++;
1509 *next = freq.last_in_set ? 0 : recno + 1;
1513 this_length = freq.len;
1515 this_length = odr_total(a->encode) - total_length - dumped_records;
1516 yaz_log(LOG_DEBUG, " fetched record, len=%d, total=%d dumped=%d",
1517 this_length, total_length, dumped_records);
1518 if (this_length + total_length > a->preferredMessageSize)
1520 /* record is small enough, really */
1521 if (this_length <= a->preferredMessageSize && recno > start)
1523 yaz_log(LOG_DEBUG, " Dropped last normal-sized record");
1524 *pres = Z_PRES_PARTIAL_2;
1527 /* record can only be fetched by itself */
1528 if (this_length < a->maximumRecordSize)
1530 yaz_log(LOG_DEBUG, " Record > prefmsgsz");
1533 yaz_log(LOG_DEBUG, " Dropped it");
1534 reclist->records[reclist->num_records] =
1535 surrogatediagrec(a, freq.basename, 16, 0);
1536 reclist->num_records++;
1537 *next = freq.last_in_set ? 0 : recno + 1;
1538 dumped_records += this_length;
1542 else /* too big entirely */
1544 yaz_log(LOG_LOG, "Record > maxrcdsz this=%d max=%d", this_length, a->maximumRecordSize);
1545 reclist->records[reclist->num_records] =
1546 surrogatediagrec(a, freq.basename, 17, 0);
1547 reclist->num_records++;
1548 *next = freq.last_in_set ? 0 : recno + 1;
1549 dumped_records += this_length;
1554 if (!(thisrec = (Z_NamePlusRecord *)
1555 odr_malloc(a->encode, sizeof(*thisrec))))
1557 if (!(thisrec->databaseName = (char *)odr_malloc(a->encode,
1558 strlen(freq.basename) + 1)))
1560 strcpy(thisrec->databaseName, freq.basename);
1561 thisrec->which = Z_NamePlusRecord_databaseRecord;
1563 if (freq.output_format_raw)
1565 struct oident *ident = oid_getentbyoid(freq.output_format_raw);
1566 freq.output_format = ident->value;
1568 thisrec->u.databaseRecord = z_ext_record(a->encode, freq.output_format,
1569 freq.record, freq.len);
1570 if (!thisrec->u.databaseRecord)
1572 reclist->records[reclist->num_records] = thisrec;
1573 reclist->num_records++;
1574 *next = freq.last_in_set ? 0 : recno + 1;
1576 *num = reclist->num_records;
1580 static Z_APDU *process_searchRequest(association *assoc, request *reqb,
1583 Z_SearchRequest *req = reqb->apdu_request->u.searchRequest;
1584 bend_search_rr *bsrr =
1585 (bend_search_rr *)nmem_malloc (reqb->request_mem, sizeof(*bsrr));
1587 yaz_log(LOG_LOG, "Got SearchRequest.");
1589 bsrr->request = reqb;
1590 bsrr->association = assoc;
1591 bsrr->referenceId = req->referenceId;
1592 save_referenceId (reqb, bsrr->referenceId);
1594 yaz_log (LOG_LOG, "ResultSet '%s'", req->resultSetName);
1595 if (req->databaseNames)
1598 for (i = 0; i < req->num_databaseNames; i++)
1599 yaz_log (LOG_LOG, "Database '%s'", req->databaseNames[i]);
1601 yaz_log_zquery(req->query);
1603 if (assoc->init->bend_search)
1605 bsrr->setname = req->resultSetName;
1606 bsrr->replace_set = *req->replaceIndicator;
1607 bsrr->num_bases = req->num_databaseNames;
1608 bsrr->basenames = req->databaseNames;
1609 bsrr->query = req->query;
1610 bsrr->stream = assoc->encode;
1611 nmem_transfer(bsrr->stream->mem, reqb->request_mem);
1612 bsrr->decode = assoc->decode;
1613 bsrr->print = assoc->print;
1616 bsrr->errstring = NULL;
1617 bsrr->search_info = NULL;
1618 (assoc->init->bend_search)(assoc->backend, bsrr);
1622 return response_searchRequest(assoc, reqb, bsrr, fd);
1625 int bend_searchresponse(void *handle, bend_search_rr *bsrr) {return 0;}
1628 * Prepare a searchresponse based on the backend results. We probably want
1629 * to look at making the fetching of records nonblocking as well, but
1630 * so far, we'll keep things simple.
1631 * If bsrt is null, that means we're called in response to a communications
1632 * event, and we'll have to get the response for ourselves.
1634 static Z_APDU *response_searchRequest(association *assoc, request *reqb,
1635 bend_search_rr *bsrt, int *fd)
1637 Z_SearchRequest *req = reqb->apdu_request->u.searchRequest;
1638 Z_APDU *apdu = (Z_APDU *)odr_malloc (assoc->encode, sizeof(*apdu));
1639 Z_SearchResponse *resp = (Z_SearchResponse *)
1640 odr_malloc (assoc->encode, sizeof(*resp));
1641 int *nulint = odr_intdup (assoc->encode, 0);
1642 bool_t *sr = odr_intdup(assoc->encode, 1);
1643 int *next = odr_intdup(assoc->encode, 0);
1644 int *none = odr_intdup(assoc->encode, Z_RES_NONE);
1646 apdu->which = Z_APDU_searchResponse;
1647 apdu->u.searchResponse = resp;
1648 resp->referenceId = req->referenceId;
1649 resp->additionalSearchInfo = 0;
1650 resp->otherInfo = 0;
1652 if (!bsrt && !bend_searchresponse(assoc->backend, bsrt))
1654 yaz_log(LOG_FATAL, "Bad result from backend");
1657 else if (bsrt->errcode)
1659 resp->records = diagrec(assoc, bsrt->errcode, bsrt->errstring);
1660 resp->resultCount = nulint;
1661 resp->numberOfRecordsReturned = nulint;
1662 resp->nextResultSetPosition = nulint;
1663 resp->searchStatus = nulint;
1664 resp->resultSetStatus = none;
1665 resp->presentStatus = 0;
1669 int *toget = odr_intdup(assoc->encode, 0);
1670 int *presst = odr_intdup(assoc->encode, 0);
1671 Z_RecordComposition comp, *compp = 0;
1673 yaz_log (LOG_LOG, "resultCount: %d", bsrt->hits);
1676 resp->resultCount = &bsrt->hits;
1678 comp.which = Z_RecordComp_simple;
1679 /* how many records does the user agent want, then? */
1680 if (bsrt->hits <= *req->smallSetUpperBound)
1682 *toget = bsrt->hits;
1683 if ((comp.u.simple = req->smallSetElementSetNames))
1686 else if (bsrt->hits < *req->largeSetLowerBound)
1688 *toget = *req->mediumSetPresentNumber;
1689 if (*toget > bsrt->hits)
1690 *toget = bsrt->hits;
1691 if ((comp.u.simple = req->mediumSetElementSetNames))
1697 if (*toget && !resp->records)
1702 if (!(prefformat = oid_getentbyoid(req->preferredRecordSyntax)))
1705 form = prefformat->value;
1706 resp->records = pack_records(assoc, req->resultSetName, 1,
1707 toget, compp, next, presst, form, req->referenceId,
1708 req->preferredRecordSyntax);
1711 resp->numberOfRecordsReturned = toget;
1712 resp->nextResultSetPosition = next;
1713 resp->searchStatus = sr;
1714 resp->resultSetStatus = 0;
1715 resp->presentStatus = presst;
1719 if (*resp->resultCount)
1721 resp->numberOfRecordsReturned = nulint;
1722 resp->nextResultSetPosition = next;
1723 resp->searchStatus = sr;
1724 resp->resultSetStatus = 0;
1725 resp->presentStatus = 0;
1728 resp->additionalSearchInfo = bsrt->search_info;
1733 * Maybe we got a little over-friendly when we designed bend_fetch to
1734 * get only one record at a time. Some backends can optimise multiple-record
1735 * fetches, and at any rate, there is some overhead involved in
1736 * all that selecting and hopping around. Problem is, of course, that the
1737 * frontend can't know ahead of time how many records it'll need to
1738 * fill the negotiated PDU size. Annoying. Segmentation or not, Z/SR
1739 * is downright lousy as a bulk data transfer protocol.
1741 * To start with, we'll do the fetching of records from the backend
1742 * in one operation: To save some trips in and out of the event-handler,
1743 * and to simplify the interface to pack_records. At any rate, asynch
1744 * operation is more fun in operations that have an unpredictable execution
1745 * speed - which is normally more true for search than for present.
1747 static Z_APDU *process_presentRequest(association *assoc, request *reqb,
1750 Z_PresentRequest *req = reqb->apdu_request->u.presentRequest;
1754 Z_PresentResponse *resp;
1758 yaz_log(LOG_LOG, "Got PresentRequest.");
1760 if (!(prefformat = oid_getentbyoid(req->preferredRecordSyntax)))
1763 form = prefformat->value;
1764 resp = (Z_PresentResponse *)odr_malloc (assoc->encode, sizeof(*resp));
1766 resp->presentStatus = odr_intdup(assoc->encode, 0);
1767 if (assoc->init->bend_present)
1769 bend_present_rr *bprr = (bend_present_rr *)
1770 nmem_malloc (reqb->request_mem, sizeof(*bprr));
1771 bprr->setname = req->resultSetId;
1772 bprr->start = *req->resultSetStartPoint;
1773 bprr->number = *req->numberOfRecordsRequested;
1774 bprr->format = form;
1775 bprr->comp = req->recordComposition;
1776 bprr->referenceId = req->referenceId;
1777 bprr->stream = assoc->encode;
1778 bprr->print = assoc->print;
1779 bprr->request = reqb;
1780 bprr->association = assoc;
1782 bprr->errstring = NULL;
1783 (*assoc->init->bend_present)(assoc->backend, bprr);
1789 resp->records = diagrec(assoc, bprr->errcode, bprr->errstring);
1790 *resp->presentStatus = Z_PRES_FAILURE;
1793 apdu = (Z_APDU *)odr_malloc (assoc->encode, sizeof(*apdu));
1794 next = odr_intdup(assoc->encode, 0);
1795 num = odr_intdup(assoc->encode, 0);
1797 apdu->which = Z_APDU_presentResponse;
1798 apdu->u.presentResponse = resp;
1799 resp->referenceId = req->referenceId;
1800 resp->otherInfo = 0;
1804 *num = *req->numberOfRecordsRequested;
1806 pack_records(assoc, req->resultSetId, *req->resultSetStartPoint,
1807 num, req->recordComposition, next, resp->presentStatus,
1808 form, req->referenceId, req->preferredRecordSyntax);
1812 resp->numberOfRecordsReturned = num;
1813 resp->nextResultSetPosition = next;
1819 * Scan was implemented rather in a hurry, and with support for only the basic
1820 * elements of the service in the backend API. Suggestions are welcome.
1822 static Z_APDU *process_scanRequest(association *assoc, request *reqb, int *fd)
1824 Z_ScanRequest *req = reqb->apdu_request->u.scanRequest;
1825 Z_APDU *apdu = (Z_APDU *)odr_malloc (assoc->encode, sizeof(*apdu));
1826 Z_ScanResponse *res = (Z_ScanResponse *)
1827 odr_malloc (assoc->encode, sizeof(*res));
1828 int *scanStatus = odr_intdup(assoc->encode, Z_Scan_failure);
1829 int *numberOfEntriesReturned = odr_intdup(assoc->encode, 0);
1830 Z_ListEntries *ents = (Z_ListEntries *)
1831 odr_malloc (assoc->encode, sizeof(*ents));
1832 Z_DiagRecs *diagrecs_p = NULL;
1834 bend_scan_rr *bsrr = (bend_scan_rr *)
1835 odr_malloc (assoc->encode, sizeof(*bsrr));
1836 struct scan_entry *save_entries;
1838 yaz_log(LOG_LOG, "Got ScanRequest");
1840 apdu->which = Z_APDU_scanResponse;
1841 apdu->u.scanResponse = res;
1842 res->referenceId = req->referenceId;
1844 /* if step is absent, set it to 0 */
1845 res->stepSize = odr_intdup(assoc->encode, 0);
1847 *res->stepSize = *req->stepSize;
1849 res->scanStatus = scanStatus;
1850 res->numberOfEntriesReturned = numberOfEntriesReturned;
1851 res->positionOfTerm = 0;
1852 res->entries = ents;
1853 ents->num_entries = 0;
1854 ents->entries = NULL;
1855 ents->num_nonsurrogateDiagnostics = 0;
1856 ents->nonsurrogateDiagnostics = NULL;
1857 res->attributeSet = 0;
1860 if (req->databaseNames)
1863 for (i = 0; i < req->num_databaseNames; i++)
1864 yaz_log (LOG_LOG, "Database '%s'", req->databaseNames[i]);
1866 bsrr->num_bases = req->num_databaseNames;
1867 bsrr->basenames = req->databaseNames;
1868 bsrr->num_entries = *req->numberOfTermsRequested;
1869 bsrr->term = req->termListAndStartPoint;
1870 bsrr->referenceId = req->referenceId;
1871 bsrr->stream = assoc->encode;
1872 bsrr->print = assoc->print;
1873 bsrr->step_size = res->stepSize;
1875 /* Note that version 2.0 of YAZ and older did not set entries ..
1876 We do now. And when we do it's easier to extend the scan entry
1877 We know that if the scan handler did set entries, it will
1878 not know of new member display_term.
1880 if (bsrr->num_entries > 0)
1883 bsrr->entries = odr_malloc(assoc->decode, sizeof(*bsrr->entries) *
1885 for (i = 0; i<bsrr->num_entries; i++)
1887 bsrr->entries[i].term = 0;
1888 bsrr->entries[i].occurrences = 0;
1889 bsrr->entries[i].errcode = 0;
1890 bsrr->entries[i].errstring = 0;
1891 bsrr->entries[i].display_term = 0;
1894 save_entries = bsrr->entries; /* save it so we can compare later */
1896 if (req->attributeSet &&
1897 (attset = oid_getentbyoid(req->attributeSet)) &&
1898 (attset->oclass == CLASS_ATTSET || attset->oclass == CLASS_GENERAL))
1899 bsrr->attributeset = attset->value;
1901 bsrr->attributeset = VAL_NONE;
1902 log_scan_term (req->termListAndStartPoint, bsrr->attributeset);
1903 bsrr->term_position = req->preferredPositionInResponse ?
1904 *req->preferredPositionInResponse : 1;
1905 ((int (*)(void *, bend_scan_rr *))
1906 (*assoc->init->bend_scan))(assoc->backend, bsrr);
1908 diagrecs_p = diagrecs(assoc, bsrr->errcode, bsrr->errstring);
1912 Z_Entry **tab = (Z_Entry **)
1913 odr_malloc (assoc->encode, sizeof(*tab) * bsrr->num_entries);
1915 if (bsrr->status == BEND_SCAN_PARTIAL)
1916 *scanStatus = Z_Scan_partial_5;
1918 *scanStatus = Z_Scan_success;
1919 ents->entries = tab;
1920 ents->num_entries = bsrr->num_entries;
1921 res->numberOfEntriesReturned = &ents->num_entries;
1922 res->positionOfTerm = &bsrr->term_position;
1923 for (i = 0; i < bsrr->num_entries; i++)
1929 tab[i] = e = (Z_Entry *)odr_malloc(assoc->encode, sizeof(*e));
1930 if (bsrr->entries[i].occurrences >= 0)
1932 e->which = Z_Entry_termInfo;
1933 e->u.termInfo = t = (Z_TermInfo *)
1934 odr_malloc(assoc->encode, sizeof(*t));
1935 t->suggestedAttributes = 0;
1937 if (save_entries == bsrr->entries &&
1938 bsrr->entries[i].display_term)
1940 /* the entries was NOT set by the handler. So it's
1941 safe to test for new member display_term. It is
1944 t->displayTerm = odr_strdup(assoc->encode,
1945 bsrr->entries[i].display_term);
1947 t->alternativeTerm = 0;
1948 t->byAttributes = 0;
1949 t->otherTermInfo = 0;
1950 t->globalOccurrences = &bsrr->entries[i].occurrences;
1951 t->term = (Z_Term *)
1952 odr_malloc(assoc->encode, sizeof(*t->term));
1953 t->term->which = Z_Term_general;
1954 t->term->u.general = o =
1955 (Odr_oct *)odr_malloc(assoc->encode, sizeof(Odr_oct));
1956 o->buf = (unsigned char *)
1957 odr_malloc(assoc->encode, o->len = o->size =
1958 strlen(bsrr->entries[i].term));
1959 memcpy(o->buf, bsrr->entries[i].term, o->len);
1960 yaz_log(LOG_DEBUG, " term #%d: '%s' (%d)", i,
1961 bsrr->entries[i].term, bsrr->entries[i].occurrences);
1965 Z_DiagRecs *drecs = diagrecs (assoc,
1966 bsrr->entries[i].errcode,
1967 bsrr->entries[i].errstring);
1968 assert (drecs->num_diagRecs == 1);
1969 e->which = Z_Entry_surrogateDiagnostic;
1970 assert (drecs->diagRecs[0]);
1971 e->u.surrogateDiagnostic = drecs->diagRecs[0];
1977 ents->num_nonsurrogateDiagnostics = diagrecs_p->num_diagRecs;
1978 ents->nonsurrogateDiagnostics = diagrecs_p->diagRecs;
1983 static Z_APDU *process_sortRequest(association *assoc, request *reqb,
1986 Z_SortRequest *req = reqb->apdu_request->u.sortRequest;
1987 Z_SortResponse *res = (Z_SortResponse *)
1988 odr_malloc (assoc->encode, sizeof(*res));
1989 bend_sort_rr *bsrr = (bend_sort_rr *)
1990 odr_malloc (assoc->encode, sizeof(*bsrr));
1992 Z_APDU *apdu = (Z_APDU *)odr_malloc (assoc->encode, sizeof(*apdu));
1994 yaz_log(LOG_LOG, "Got SortRequest.");
1996 bsrr->num_input_setnames = req->num_inputResultSetNames;
1997 bsrr->input_setnames = req->inputResultSetNames;
1998 bsrr->referenceId = req->referenceId;
1999 bsrr->output_setname = req->sortedResultSetName;
2000 bsrr->sort_sequence = req->sortSequence;
2001 bsrr->stream = assoc->encode;
2002 bsrr->print = assoc->print;
2004 bsrr->sort_status = Z_SortStatus_failure;
2006 bsrr->errstring = 0;
2008 (*assoc->init->bend_sort)(assoc->backend, bsrr);
2010 res->referenceId = bsrr->referenceId;
2011 res->sortStatus = odr_intdup(assoc->encode, bsrr->sort_status);
2012 res->resultSetStatus = 0;
2015 Z_DiagRecs *dr = diagrecs (assoc, bsrr->errcode, bsrr->errstring);
2016 res->diagnostics = dr->diagRecs;
2017 res->num_diagnostics = dr->num_diagRecs;
2021 res->num_diagnostics = 0;
2022 res->diagnostics = 0;
2024 res->resultCount = 0;
2027 apdu->which = Z_APDU_sortResponse;
2028 apdu->u.sortResponse = res;
2032 static Z_APDU *process_deleteRequest(association *assoc, request *reqb,
2035 Z_DeleteResultSetRequest *req =
2036 reqb->apdu_request->u.deleteResultSetRequest;
2037 Z_DeleteResultSetResponse *res = (Z_DeleteResultSetResponse *)
2038 odr_malloc (assoc->encode, sizeof(*res));
2039 bend_delete_rr *bdrr = (bend_delete_rr *)
2040 odr_malloc (assoc->encode, sizeof(*bdrr));
2041 Z_APDU *apdu = (Z_APDU *)odr_malloc (assoc->encode, sizeof(*apdu));
2043 yaz_log(LOG_LOG, "Got DeleteRequest.");
2045 bdrr->num_setnames = req->num_resultSetList;
2046 bdrr->setnames = req->resultSetList;
2047 bdrr->stream = assoc->encode;
2048 bdrr->print = assoc->print;
2049 bdrr->function = *req->deleteFunction;
2050 bdrr->referenceId = req->referenceId;
2052 if (bdrr->num_setnames > 0)
2055 bdrr->statuses = (int*)
2056 odr_malloc(assoc->encode, sizeof(*bdrr->statuses) *
2057 bdrr->num_setnames);
2058 for (i = 0; i < bdrr->num_setnames; i++)
2059 bdrr->statuses[i] = 0;
2061 (*assoc->init->bend_delete)(assoc->backend, bdrr);
2063 res->referenceId = req->referenceId;
2065 res->deleteOperationStatus = odr_intdup(assoc->encode,bdrr->delete_status);
2067 res->deleteListStatuses = 0;
2068 if (bdrr->num_setnames > 0)
2071 res->deleteListStatuses = (Z_ListStatuses *)
2072 odr_malloc(assoc->encode, sizeof(*res->deleteListStatuses));
2073 res->deleteListStatuses->num = bdrr->num_setnames;
2074 res->deleteListStatuses->elements =
2076 odr_malloc (assoc->encode,
2077 sizeof(*res->deleteListStatuses->elements) *
2078 bdrr->num_setnames);
2079 for (i = 0; i<bdrr->num_setnames; i++)
2081 res->deleteListStatuses->elements[i] =
2083 odr_malloc (assoc->encode,
2084 sizeof(**res->deleteListStatuses->elements));
2085 res->deleteListStatuses->elements[i]->status = bdrr->statuses+i;
2086 res->deleteListStatuses->elements[i]->id =
2087 odr_strdup (assoc->encode, bdrr->setnames[i]);
2091 res->numberNotDeleted = 0;
2092 res->bulkStatuses = 0;
2093 res->deleteMessage = 0;
2096 apdu->which = Z_APDU_deleteResultSetResponse;
2097 apdu->u.deleteResultSetResponse = res;
2101 static void process_close(association *assoc, request *reqb)
2103 Z_Close *req = reqb->apdu_request->u.close;
2104 static char *reasons[] =
2111 "securityViolation",
2118 yaz_log(LOG_LOG, "Got Close, reason %s, message %s",
2119 reasons[*req->closeReason], req->diagnosticInformation ?
2120 req->diagnosticInformation : "NULL");
2121 if (assoc->version < 3) /* to make do_force respond with close */
2123 do_close_req(assoc, Z_Close_finished,
2124 "Association terminated by client", reqb);
2127 void save_referenceId (request *reqb, Z_ReferenceId *refid)
2131 reqb->len_refid = refid->len;
2132 reqb->refid = (char *)nmem_malloc (reqb->request_mem, refid->len);
2133 memcpy (reqb->refid, refid->buf, refid->len);
2137 reqb->len_refid = 0;
2142 void bend_request_send (bend_association a, bend_request req, Z_APDU *res)
2144 process_z_response (a, req, res);
2147 bend_request bend_request_mk (bend_association a)
2149 request *nreq = request_get (&a->outgoing);
2150 nreq->request_mem = nmem_create ();
2154 Z_ReferenceId *bend_request_getid (ODR odr, bend_request req)
2159 id = (Odr_oct *)odr_malloc (odr, sizeof(*odr));
2160 id->buf = (unsigned char *)odr_malloc (odr, req->len_refid);
2161 id->len = id->size = req->len_refid;
2162 memcpy (id->buf, req->refid, req->len_refid);
2166 void bend_request_destroy (bend_request *req)
2168 nmem_destroy((*req)->request_mem);
2169 request_release(*req);
2173 int bend_backend_respond (bend_association a, bend_request req)
2177 r = process_z_request (a, req, &msg);
2179 yaz_log (LOG_WARN, "%s", msg);
2183 void bend_request_setdata(bend_request r, void *p)
2188 void *bend_request_getdata(bend_request r)
2190 return r->clientData;
2193 static Z_APDU *process_segmentRequest (association *assoc, request *reqb)
2195 bend_segment_rr req;
2197 req.segment = reqb->apdu_request->u.segmentRequest;
2198 req.stream = assoc->encode;
2199 req.decode = assoc->decode;
2200 req.print = assoc->print;
2201 req.association = assoc;
2203 (*assoc->init->bend_segment)(assoc->backend, &req);
2208 static Z_APDU *process_ESRequest(association *assoc, request *reqb, int *fd)
2210 bend_esrequest_rr esrequest;
2212 Z_ExtendedServicesRequest *req =
2213 reqb->apdu_request->u.extendedServicesRequest;
2214 Z_APDU *apdu = zget_APDU(assoc->encode, Z_APDU_extendedServicesResponse);
2216 Z_ExtendedServicesResponse *resp = apdu->u.extendedServicesResponse;
2218 yaz_log(LOG_DEBUG,"inside Process esRequest");
2220 esrequest.esr = reqb->apdu_request->u.extendedServicesRequest;
2221 esrequest.stream = assoc->encode;
2222 esrequest.decode = assoc->decode;
2223 esrequest.print = assoc->print;
2224 esrequest.errcode = 0;
2225 esrequest.errstring = NULL;
2226 esrequest.request = reqb;
2227 esrequest.association = assoc;
2228 esrequest.taskPackage = 0;
2229 esrequest.referenceId = req->referenceId;
2231 (*assoc->init->bend_esrequest)(assoc->backend, &esrequest);
2233 /* If the response is being delayed, return NULL */
2234 if (esrequest.request == NULL)
2237 resp->referenceId = req->referenceId;
2239 if (esrequest.errcode == -1)
2241 /* Backend service indicates request will be processed */
2242 yaz_log(LOG_DEBUG,"Request could be processed...Accepted !");
2243 *resp->operationStatus = Z_ExtendedServicesResponse_accepted;
2245 else if (esrequest.errcode == 0)
2247 /* Backend service indicates request will be processed */
2248 yaz_log(LOG_DEBUG,"Request could be processed...Done !");
2249 *resp->operationStatus = Z_ExtendedServicesResponse_done;
2253 Z_DiagRecs *diagRecs = diagrecs (assoc, esrequest.errcode,
2254 esrequest.errstring);
2256 /* Backend indicates error, request will not be processed */
2257 yaz_log(LOG_DEBUG,"Request could not be processed...failure !");
2258 *resp->operationStatus = Z_ExtendedServicesResponse_failure;
2259 resp->num_diagnostics = diagRecs->num_diagRecs;
2260 resp->diagnostics = diagRecs->diagRecs;
2262 /* Do something with the members of bend_extendedservice */
2263 if (esrequest.taskPackage)
2264 resp->taskPackage = z_ext_record (assoc->encode, VAL_EXTENDED,
2265 (const char *) esrequest.taskPackage,
2267 yaz_log(LOG_DEBUG,"Send the result apdu");