YAZ  4.2.57
seshigh.c
Go to the documentation of this file.
1 /* This file is part of the YAZ toolkit.
2  * Copyright (C) 1995-2013 Index Data
3  * See the file LICENSE for details.
4  */
28 #if HAVE_CONFIG_H
29 #include <config.h>
30 #endif
31 
32 #include <limits.h>
33 #include <stdlib.h>
34 #include <stdio.h>
35 #include <assert.h>
36 
37 #if HAVE_SYS_TYPES_H
38 #include <sys/types.h>
39 #endif
40 #if HAVE_SYS_STAT_H
41 #include <sys/stat.h>
42 #endif
43 
44 #ifdef WIN32
45 #include <io.h>
46 #define S_ISREG(x) (x & _S_IFREG)
47 #include <process.h>
48 #endif
49 
50 #if HAVE_UNISTD_H
51 #include <unistd.h>
52 #endif
53 
54 #if YAZ_HAVE_XML2
55 #include <libxml/parser.h>
56 #include <libxml/tree.h>
57 #endif
58 
59 #include <yaz/xmalloc.h>
60 #include <yaz/comstack.h>
61 #include "eventl.h"
62 #include "session.h"
63 #include "mime.h"
64 #include <yaz/proto.h>
65 #include <yaz/oid_db.h>
66 #include <yaz/log.h>
67 #include <yaz/logrpn.h>
68 #include <yaz/querytowrbuf.h>
69 #include <yaz/statserv.h>
70 #include <yaz/diagbib1.h>
71 #include <yaz/charneg.h>
72 #include <yaz/otherinfo.h>
73 #include <yaz/yaz-util.h>
74 #include <yaz/pquery.h>
75 #include <yaz/oid_db.h>
76 
77 #include <yaz/srw.h>
78 #include <yaz/backend.h>
79 #include <yaz/yaz-ccl.h>
80 
81 static void process_gdu_request(association *assoc, request *req);
82 static int process_z_request(association *assoc, request *req, char **msg);
83 static int process_gdu_response(association *assoc, request *req, Z_GDU *res);
84 static int process_z_response(association *assoc, request *req, Z_APDU *res);
85 static Z_APDU *process_initRequest(association *assoc, request *reqb);
86 static Z_External *init_diagnostics(ODR odr, int errcode,
87  const char *errstring);
88 static Z_APDU *process_searchRequest(association *assoc, request *reqb);
89 static Z_APDU *response_searchRequest(association *assoc, request *reqb,
90  bend_search_rr *bsrr);
91 static Z_APDU *process_presentRequest(association *assoc, request *reqb);
92 static Z_APDU *process_scanRequest(association *assoc, request *reqb);
93 static Z_APDU *process_sortRequest(association *assoc, request *reqb);
94 static void process_close(association *assoc, request *reqb);
95 static Z_APDU *process_deleteRequest(association *assoc, request *reqb);
96 static Z_APDU *process_segmentRequest(association *assoc, request *reqb);
97 static Z_APDU *process_ESRequest(association *assoc, request *reqb);
98 
99 /* dynamic logging levels */
100 static int logbits_set = 0;
101 static int log_session = 0; /* one-line logs for session */
102 static int log_sessiondetail = 0; /* more detailed stuff */
103 static int log_request = 0; /* one-line logs for requests */
104 static int log_requestdetail = 0; /* more detailed stuff */
105 
107 static void get_logbits(void)
108 { /* needs to be called after parsing cmd-line args that can set loglevels!*/
109  if (!logbits_set)
110  {
111  logbits_set = 1;
112  log_session = yaz_log_module_level("session");
113  log_sessiondetail = yaz_log_module_level("sessiondetail");
114  log_request = yaz_log_module_level("request");
115  log_requestdetail = yaz_log_module_level("requestdetail");
116  }
117 }
118 
119 static void wr_diag(WRBUF w, int error, const char *addinfo)
120 {
121  wrbuf_printf(w, "ERROR %d+", error);
122  wrbuf_puts_replace_char(w, diagbib1_str(error), ' ', '_');
123  if (addinfo)
124  {
125  wrbuf_puts(w, "+");
126  wrbuf_puts_replace_char(w, addinfo, ' ', '_');
127  }
128  wrbuf_puts(w, " ");
129 }
130 
131 static int odr_int_to_int(Odr_int v)
132 {
133  if (v >= INT_MAX)
134  return INT_MAX;
135  else if (v <= INT_MIN)
136  return INT_MIN;
137  else
138  return (int) v;
139 }
140 
141 /*
142  * Create and initialize a new association-handle.
143  * channel : iochannel for the current line.
144  * link : communications channel.
145  * Returns: 0 or a new association handle.
146  */
148  const char *apdufile)
149 {
150  association *anew;
151 
152  if (!logbits_set)
153  get_logbits();
154  if (!(anew = (association *)xmalloc(sizeof(*anew))))
155  return 0;
156  anew->init = 0;
157  anew->version = 0;
158  anew->last_control = 0;
159  anew->client_chan = channel;
160  anew->client_link = link;
161  anew->cs_get_mask = 0;
162  anew->cs_put_mask = 0;
163  anew->cs_accept_mask = 0;
164  if (!(anew->decode = odr_createmem(ODR_DECODE)) ||
165  !(anew->encode = odr_createmem(ODR_ENCODE)))
166  return 0;
167  if (apdufile && *apdufile)
168  {
169  FILE *f;
170 
171  if (!(anew->print = odr_createmem(ODR_PRINT)))
172  return 0;
173  if (*apdufile == '@')
174  {
175  odr_setprint(anew->print, yaz_log_file());
176  }
177  else if (*apdufile != '-')
178  {
179  char filename[256];
180  sprintf(filename, "%.200s.%ld", apdufile, (long)getpid());
181  if (!(f = fopen(filename, "w")))
182  {
183  yaz_log(YLOG_WARN|YLOG_ERRNO, "%s", filename);
184  return 0;
185  }
186  setvbuf(f, 0, _IONBF, 0);
187  odr_setprint(anew->print, f);
188  }
189  }
190  else
191  anew->print = 0;
192  anew->input_buffer = 0;
193  anew->input_buffer_len = 0;
194  anew->backend = 0;
195  anew->state = ASSOC_NEW;
196  request_initq(&anew->incoming);
197  request_initq(&anew->outgoing);
198  anew->proto = cs_getproto(link);
199  anew->server = 0;
200  return anew;
201 }
202 
203 /*
204  * Free association and release resources.
205  */
207 {
209  request *req;
210 
211  xfree(h->init);
212  odr_destroy(h->decode);
213  odr_destroy(h->encode);
214  if (h->print)
215  odr_destroy(h->print);
216  if (h->input_buffer)
217  xfree(h->input_buffer);
218  if (h->backend)
219  (*cb->bend_close)(h->backend);
220  while ((req = request_deq(&h->incoming)))
221  request_release(req);
222  while ((req = request_deq(&h->outgoing)))
223  request_release(req);
224  request_delq(&h->incoming);
225  request_delq(&h->outgoing);
226  xfree(h);
227  xmalloc_trav("session closed");
228 }
229 
230 static void do_close_req(association *a, int reason, char *message,
231  request *req)
232 {
233  Z_APDU *apdu = zget_APDU(a->encode, Z_APDU_close);
234  Z_Close *cls = apdu->u.close;
235 
236  /* Purge request queue */
237  while (request_deq(&a->incoming));
238  while (request_deq(&a->outgoing));
239  if (a->version >= 3)
240  {
241  yaz_log(log_requestdetail, "Sending Close PDU, reason=%d, message=%s",
242  reason, message ? message : "none");
243  *cls->closeReason = reason;
244  cls->diagnosticInformation = message;
245  process_z_response(a, req, apdu);
247  }
248  else
249  {
250  request_release(req);
251  yaz_log(log_requestdetail, "v2 client. No Close PDU");
252  iochan_setevent(a->client_chan, EVENT_TIMEOUT); /* force imm close */
253  a->cs_put_mask = 0;
254  }
255  a->state = ASSOC_DEAD;
256 }
257 
258 static void do_close(association *a, int reason, char *message)
259 {
260  request *req = request_get(&a->outgoing);
261  do_close_req(a, reason, message, req);
262 }
263 
264 
265 int ir_read(IOCHAN h, int event)
266 {
267  association *assoc = (association *)iochan_getdata(h);
268  COMSTACK conn = assoc->client_link;
269  request *req;
270 
271  if ((assoc->cs_put_mask & EVENT_INPUT) == 0 && (event & assoc->cs_get_mask))
272  {
273  /* We aren't speaking to this fellow */
274  if (assoc->state == ASSOC_DEAD)
275  {
276  yaz_log(log_session, "Connection closed - end of session");
277  cs_close(conn);
278  destroy_association(assoc);
279  iochan_destroy(h);
280  return 0;
281  }
282  assoc->cs_get_mask = EVENT_INPUT;
283 
284  do
285  {
286  int res = cs_get(conn, &assoc->input_buffer,
287  &assoc->input_buffer_len);
288  if (res < 0 && cs_errno(conn) == CSBUFSIZE)
289  {
290  yaz_log(log_session, "Connection error: %s res=%d",
291  cs_errmsg(cs_errno(conn)), res);
292  req = request_get(&assoc->incoming); /* get a new request */
294  "Incoming package too large", req);
295  return 0;
296  }
297  else if (res <= 0)
298  {
299  assoc->state = ASSOC_DEAD;
300  yaz_log(log_session, "Connection closed by client");
301  return 0;
302  }
303  else if (res == 1) /* incomplete read - wait for more */
304  {
305  if (conn->io_pending & CS_WANT_WRITE)
306  assoc->cs_get_mask |= EVENT_OUTPUT;
307  iochan_setflag(h, assoc->cs_get_mask);
308  return 0;
309  }
310  /* we got a complete PDU. Let's decode it */
311  yaz_log(YLOG_DEBUG, "Got PDU, %d bytes: lead=%02X %02X %02X", res,
312  assoc->input_buffer[0] & 0xff,
313  assoc->input_buffer[1] & 0xff,
314  assoc->input_buffer[2] & 0xff);
315  req = request_get(&assoc->incoming); /* get a new request */
316  odr_reset(assoc->decode);
317  odr_setbuf(assoc->decode, assoc->input_buffer, res, 0);
318  if (!z_GDU(assoc->decode, &req->gdu_request, 0, 0))
319  {
320  yaz_log(YLOG_WARN, "ODR error on incoming PDU: %s [element %s] "
321  "[near byte %ld] ",
322  odr_errmsg(odr_geterror(assoc->decode)),
323  odr_getelement(assoc->decode),
324  (long) odr_offset(assoc->decode));
325  if (assoc->decode->error != OHTTP)
326  {
327  yaz_log(YLOG_WARN, "PDU dump:");
328  odr_dumpBER(yaz_log_file(), assoc->input_buffer, res);
329  request_release(req);
330  do_close(assoc, Z_Close_protocolError, "Malformed package");
331  }
332  else
333  {
334  Z_GDU *p = z_get_HTTP_Response(assoc->encode, 400);
335  assoc->state = ASSOC_DEAD;
336  process_gdu_response(assoc, req, p);
337  }
338  return 0;
339  }
340  req->request_mem = odr_extract_mem(assoc->decode);
341  if (assoc->print)
342  {
343  if (!z_GDU(assoc->print, &req->gdu_request, 0, 0))
344  yaz_log(YLOG_WARN, "ODR print error: %s",
345  odr_errmsg(odr_geterror(assoc->print)));
346  odr_reset(assoc->print);
347  }
348  request_enq(&assoc->incoming, req);
349  }
350  while (cs_more(conn));
351  }
352  return 1;
353 }
354 
355 /*
356  * This is where PDUs from the client are read and the further
357  * processing is initiated. Flow of control moves down through the
358  * various process_* functions below, until the encoded result comes back up
359  * to the output handler in here.
360  *
361  * h : the I/O channel that has an outstanding event.
362  * event : the current outstanding event.
363  */
364 void ir_session(IOCHAN h, int event)
365 {
366  int res;
367  association *assoc = (association *)iochan_getdata(h);
368  COMSTACK conn = assoc->client_link;
369  request *req;
370 
371  assert(h && conn && assoc);
372  if (event == EVENT_TIMEOUT)
373  {
374  if (assoc->state != ASSOC_UP)
375  {
376  yaz_log(log_session, "Timeout. Closing connection");
377  /* do we need to lod this at all */
378  cs_close(conn);
379  destroy_association(assoc);
380  iochan_destroy(h);
381  }
382  else
383  {
384  yaz_log(log_sessiondetail, "Timeout. Sending Z39.50 Close");
385  do_close(assoc, Z_Close_lackOfActivity, 0);
386  }
387  return;
388  }
389  if (event & assoc->cs_accept_mask)
390  {
391  if (!cs_accept(conn))
392  {
393  yaz_log(YLOG_WARN, "accept failed");
394  destroy_association(assoc);
395  iochan_destroy(h);
396  return;
397  }
399  if (conn->io_pending)
400  { /* cs_accept didn't complete */
401  assoc->cs_accept_mask =
402  ((conn->io_pending & CS_WANT_WRITE) ? EVENT_OUTPUT : 0) |
403  ((conn->io_pending & CS_WANT_READ) ? EVENT_INPUT : 0);
404 
405  iochan_setflag(h, assoc->cs_accept_mask);
406  }
407  else
408  { /* cs_accept completed. Prepare for reading (cs_get) */
409  assoc->cs_accept_mask = 0;
410  assoc->cs_get_mask = EVENT_INPUT;
411  iochan_setflag(h, assoc->cs_get_mask);
412  }
413  return;
414  }
415  if (event & assoc->cs_get_mask) /* input */
416  {
417  if (!ir_read(h, event))
418  return;
419  req = request_head(&assoc->incoming);
420  if (req->state == REQUEST_IDLE)
421  {
422  request_deq(&assoc->incoming);
423  process_gdu_request(assoc, req);
424  }
425  }
426  if (event & assoc->cs_put_mask)
427  {
428  request *req = request_head(&assoc->outgoing);
429 
430  assoc->cs_put_mask = 0;
431  yaz_log(YLOG_DEBUG, "ir_session (output)");
432  req->state = REQUEST_PENDING;
433  switch (res = cs_put(conn, req->response, req->len_response))
434  {
435  case -1:
436  yaz_log(log_sessiondetail, "Connection closed by client");
437  cs_close(conn);
438  destroy_association(assoc);
439  iochan_destroy(h);
440  break;
441  case 0: /* all sent - release the request structure */
442  yaz_log(YLOG_DEBUG, "Wrote PDU, %d bytes", req->len_response);
443 #if 0
444  yaz_log(YLOG_DEBUG, "HTTP out:\n%.*s", req->len_response,
445  req->response);
446 #endif
447  request_deq(&assoc->outgoing);
448  request_release(req);
449  if (!request_head(&assoc->outgoing))
450  { /* restore mask for cs_get operation ... */
452  iochan_setflag(h, assoc->cs_get_mask);
453  if (assoc->state == ASSOC_DEAD)
455  }
456  else
457  {
458  assoc->cs_put_mask = EVENT_OUTPUT;
459  }
460  break;
461  default:
462  if (conn->io_pending & CS_WANT_WRITE)
463  assoc->cs_put_mask |= EVENT_OUTPUT;
464  if (conn->io_pending & CS_WANT_READ)
465  assoc->cs_put_mask |= EVENT_INPUT;
466  iochan_setflag(h, assoc->cs_put_mask);
467  }
468  }
469  if (event & EVENT_EXCEPT)
470  {
471  yaz_log(YLOG_WARN, "ir_session (exception)");
472  cs_close(conn);
473  destroy_association(assoc);
474  iochan_destroy(h);
475  }
476 }
477 
478 static int process_z_request(association *assoc, request *req, char **msg);
479 
480 
481 static void assoc_init_reset(association *assoc)
482 {
483  xfree(assoc->init);
484  assoc->init = (bend_initrequest *) xmalloc(sizeof(*assoc->init));
485 
486  assoc->init->stream = assoc->encode;
487  assoc->init->print = assoc->print;
488  assoc->init->auth = 0;
489  assoc->init->referenceId = 0;
490  assoc->init->implementation_version = 0;
491  assoc->init->implementation_id = 0;
492  assoc->init->implementation_name = 0;
493  assoc->init->query_charset = 0;
494  assoc->init->records_in_same_charset = 0;
495  assoc->init->bend_sort = NULL;
496  assoc->init->bend_search = NULL;
497  assoc->init->bend_present = NULL;
498  assoc->init->bend_esrequest = NULL;
499  assoc->init->bend_delete = NULL;
500  assoc->init->bend_scan = NULL;
501  assoc->init->bend_segment = NULL;
502  assoc->init->bend_fetch = NULL;
503  assoc->init->bend_explain = NULL;
504  assoc->init->bend_srw_scan = NULL;
505  assoc->init->bend_srw_update = NULL;
506  assoc->init->named_result_sets = 0;
507 
508  assoc->init->charneg_request = NULL;
509  assoc->init->charneg_response = NULL;
510 
511  assoc->init->decode = assoc->decode;
512  assoc->init->peer_name =
513  odr_strdup(assoc->encode, cs_addrstr(assoc->client_link));
514 
515  yaz_log(log_requestdetail, "peer %s", assoc->init->peer_name);
516 }
517 
518 static int srw_bend_init(association *assoc, Z_SRW_diagnostic **d, int *num, Z_SRW_PDU *sr)
519 {
521  if (!assoc->init)
522  {
523  const char *encoding = "UTF-8";
524  Z_External *ce;
525  bend_initresult *binitres;
526 
527  yaz_log(log_requestdetail, "srw_bend_init config=%s", cb->configname);
528  assoc_init_reset(assoc);
529 
530  if (sr->username)
531  {
533  odr_malloc(assoc->decode, sizeof(*auth));
534  size_t len;
535 
536  len = strlen(sr->username) + 1;
537  if (sr->password)
538  len += strlen(sr->password) + 2;
539  yaz_log(log_requestdetail, "username=%s password-len=%ld",
540  sr->username, (long)
541  (sr->password ? strlen(sr->password) : 0));
543  auth->u.open = (char *) odr_malloc(assoc->decode, len);
544  strcpy(auth->u.open, sr->username);
545  if (sr->password && *sr->password)
546  {
547  strcat(auth->u.open, "/");
548  strcat(auth->u.open, sr->password);
549  }
550  assoc->init->auth = auth;
551  }
552 
553 #if 1
554  ce = yaz_set_proposal_charneg(assoc->decode, &encoding, 1, 0, 0, 1);
555  assoc->init->charneg_request = ce->u.charNeg3;
556 #endif
557  assoc->backend = 0;
558  if (!(binitres = (*cb->bend_init)(assoc->init)))
559  {
560  assoc->state = ASSOC_DEAD;
561  yaz_add_srw_diagnostic(assoc->encode, d, num,
563  return 0;
564  }
565  assoc->backend = binitres->handle;
566  assoc->init->auth = 0;
567  if (binitres->errcode)
568  {
569  int srw_code = yaz_diag_bib1_to_srw(binitres->errcode);
570  assoc->state = ASSOC_DEAD;
571  yaz_add_srw_diagnostic(assoc->encode, d, num, srw_code,
572  binitres->errstring);
573  return 0;
574  }
575  return 1;
576  }
577  return 1;
578 }
579 
580 static int retrieve_fetch(association *assoc, bend_fetch_rr *rr)
581 {
582 #if YAZ_HAVE_XML2
583  yaz_record_conv_t rc = 0;
584  const char *match_schema = 0;
585  Odr_oid *match_syntax = 0;
586 
587  if (assoc->server)
588  {
589  int r;
590  const char *input_schema = yaz_get_esn(rr->comp);
591  Odr_oid *input_syntax_raw = rr->request_format;
592 
593  const char *backend_schema = 0;
594  Odr_oid *backend_syntax = 0;
595 
597  input_schema,
598  input_syntax_raw,
599  &match_schema,
600  &match_syntax,
601  &rc,
602  &backend_schema,
603  &backend_syntax);
604  if (r == -1) /* error ? */
605  {
606  const char *details = yaz_retrieval_get_error(
607  assoc->server->retrieval);
608 
610  if (details)
611  rr->errstring = odr_strdup(rr->stream, details);
612  return -1;
613  }
614  else if (r == 1 || r == 3)
615  {
616  const char *details = input_schema;
617  rr->errcode =
619  if (details)
620  rr->errstring = odr_strdup(rr->stream, details);
621  return -1;
622  }
623  else if (r == 2)
624  {
626  if (input_syntax_raw)
627  {
628  char oidbuf[OID_STR_MAX];
629  oid_oid_to_dotstring(input_syntax_raw, oidbuf);
630  rr->errstring = odr_strdup(rr->stream, oidbuf);
631  }
632  return -1;
633  }
634  if (backend_schema)
635  {
636  yaz_set_esn(&rr->comp, backend_schema, odr_getmem(rr->stream));
637  }
638  if (backend_syntax)
639  rr->request_format = backend_syntax;
640  }
641  (*assoc->init->bend_fetch)(assoc->backend, rr);
642  if (rc && rr->record && rr->errcode == 0)
643  { /* post conversion must take place .. */
644  WRBUF output_record = wrbuf_alloc();
645  int r = 1;
646  const char *details = 0;
647  if (rr->len > 0)
648  {
649  r = yaz_record_conv_record(rc, rr->record, rr->len, output_record);
650  if (r)
651  details = yaz_record_conv_get_error(rc);
652  }
653  else if (rr->len == -1 && rr->output_format &&
655  {
657  rc, (Z_OPACRecord *) rr->record, output_record);
658  if (r)
659  details = yaz_record_conv_get_error(rc);
660  }
661  if (r == 0 && match_syntax &&
662  !oid_oidcmp(match_syntax, yaz_oid_recsyn_opac))
663  {
665  Z_OPACRecord *opac = 0;
666  if (yaz_xml_to_opac(mt, wrbuf_buf(output_record),
667  wrbuf_len(output_record),
668  &opac, 0 /* iconv */, rr->stream->mem, 0)
669  && opac)
670  {
671  rr->len = -1;
672  rr->record = (char *) opac;
673  }
674  else
675  {
676  details = "XML to OPAC conversion failed";
677  r = 1;
678  }
679  yaz_marc_destroy(mt);
680  }
681  else if (r == 0)
682  {
683  rr->len = wrbuf_len(output_record);
684  rr->record = (char *) odr_malloc(rr->stream, rr->len);
685  memcpy(rr->record, wrbuf_buf(output_record), rr->len);
686  }
687  if (r)
688  {
690  if (details)
691  rr->errstring = odr_strdup(rr->stream, details);
692  }
693  wrbuf_destroy(output_record);
694  }
695  if (match_syntax)
696  rr->output_format = match_syntax;
697  if (match_schema)
698  rr->schema = odr_strdup(rr->stream, match_schema);
699 #else
700  (*assoc->init->bend_fetch)(assoc->backend, rr);
701 #endif
702  return 0;
703 }
704 
705 static int srw_bend_fetch(association *assoc, int pos,
707  Z_SRW_record *record,
708  const char **addinfo, int *last_in_set)
709 {
710  bend_fetch_rr rr;
711  ODR o = assoc->encode;
712 
713  rr.setname = "default";
714  rr.number = pos;
715  rr.referenceId = 0;
717 
718  rr.comp = (Z_RecordComposition *)
719  odr_malloc(assoc->decode, sizeof(*rr.comp));
721  rr.comp->u.complex = (Z_CompSpec *)
722  odr_malloc(assoc->decode, sizeof(Z_CompSpec));
724  odr_malloc(assoc->encode, sizeof(bool_t));
726  rr.comp->u.complex->num_dbSpecific = 0;
727  rr.comp->u.complex->dbSpecific = 0;
728  rr.comp->u.complex->num_recordSyntax = 0;
729  rr.comp->u.complex->recordSyntax = 0;
730 
732  odr_malloc(assoc->decode, sizeof(Z_Specification));
733 
734  /* schema uri = recordSchema (or NULL if recordSchema is not given) */
736  rr.comp->u.complex->generic->schema.uri = srw_req->recordSchema;
737 
738  /* ESN = recordSchema if recordSchema is present */
739  rr.comp->u.complex->generic->elementSpec = 0;
740  if (srw_req->recordSchema)
741  {
743  (Z_ElementSpec *) odr_malloc(assoc->encode, sizeof(Z_ElementSpec));
747  srw_req->recordSchema;
748  }
749 
750  rr.stream = assoc->encode;
751  rr.print = assoc->print;
752 
753  rr.basename = 0;
754  rr.len = 0;
755  rr.record = 0;
756  rr.last_in_set = 0;
757  rr.errcode = 0;
758  rr.errstring = 0;
759  rr.surrogate_flag = 0;
760  rr.schema = srw_req->recordSchema;
761 
762  if (!assoc->init->bend_fetch)
763  return 1;
764 
765  retrieve_fetch(assoc, &rr);
766 
767  *last_in_set = rr.last_in_set;
768 
769  if (rr.errcode && rr.surrogate_flag)
770  {
771  int code = yaz_diag_bib1_to_srw(rr.errcode);
772  yaz_mk_sru_surrogate(o, record, pos, code, rr.errstring);
773  return 0;
774  }
775  else if (rr.len >= 0)
776  {
777  record->recordData_buf = rr.record;
778  record->recordData_len = rr.len;
779  record->recordPosition = odr_intdup(o, pos);
780  record->recordSchema = odr_strdup_null(
781  o, rr.schema ? rr.schema : srw_req->recordSchema);
782  }
783  if (rr.errcode)
784  {
785  *addinfo = rr.errstring;
786  return rr.errcode;
787  }
788  return 0;
789 }
790 
791 static int cql2pqf(ODR odr, const char *cql, cql_transform_t ct,
792  Z_Query *query_result, char **sortkeys_p)
793 {
794  /* have a CQL query and CQL to PQF transform .. */
796  int r;
797  int srw_errcode = 0;
798  const char *add = 0;
799  WRBUF rpn_buf = wrbuf_alloc();
800 
801  *sortkeys_p = 0;
802  r = cql_parser_string(cp, cql);
803  if (r)
804  {
805  srw_errcode = YAZ_SRW_QUERY_SYNTAX_ERROR;
806  }
807  if (!r)
808  {
809  struct cql_node *cn = cql_parser_result(cp);
810 
811  /* Syntax OK */
812  r = cql_transform(ct, cn, wrbuf_vp_puts, rpn_buf);
813  if (r)
814  srw_errcode = cql_transform_error(ct, &add);
815  else
816  {
817  char out[100];
818  int r = cql_sortby_to_sortkeys_buf(cn, out, sizeof(out)-1);
819 
820  if (r == 0)
821  {
822  if (*out)
823  yaz_log(log_requestdetail, "srw_sortKeys '%s'", out);
824  *sortkeys_p = odr_strdup(odr, out);
825  }
826  else
827  {
828  yaz_log(log_requestdetail, "failed to create srw_sortKeys");
829  srw_errcode = YAZ_SRW_UNSUPP_SORT_TYPE;
830  }
831  }
832  }
833  if (!r)
834  {
835  /* Syntax & transform OK. */
836  /* Convert PQF string to Z39.50 to RPN query struct */
838  Z_RPNQuery *rpnquery = yaz_pqf_parse(pp, odr, wrbuf_cstr(rpn_buf));
839  if (!rpnquery)
840  {
841  size_t off;
842  const char *pqf_msg;
843  int code = yaz_pqf_error(pp, &pqf_msg, &off);
844  yaz_log(YLOG_WARN, "PQF Parser Error %s (code %d)",
845  pqf_msg, code);
846  srw_errcode = YAZ_SRW_QUERY_SYNTAX_ERROR;
847  }
848  else
849  {
850  query_result->which = Z_Query_type_1;
851  query_result->u.type_1 = rpnquery;
852  }
853  yaz_pqf_destroy(pp);
854  }
855  cql_parser_destroy(cp);
856  wrbuf_destroy(rpn_buf);
857  return srw_errcode;
858 }
859 
860 static int cql2pqf_scan(ODR odr, const char *cql, cql_transform_t ct,
861  Z_AttributesPlusTerm *result)
862 {
863  Z_Query query;
864  Z_RPNQuery *rpn;
865  char *sortkeys = 0;
866  int srw_error = cql2pqf(odr, cql, ct, &query, &sortkeys);
867  if (srw_error)
868  return srw_error;
869  if (query.which != Z_Query_type_1 && query.which != Z_Query_type_101)
870  return YAZ_SRW_QUERY_SYNTAX_ERROR; /* bad query type */
871  rpn = query.u.type_1;
872  if (!rpn->RPNStructure)
873  return YAZ_SRW_QUERY_SYNTAX_ERROR; /* must be structure */
875  return YAZ_SRW_QUERY_SYNTAX_ERROR; /* must be simple */
876  if (rpn->RPNStructure->u.simple->which != Z_Operand_APT)
877  return YAZ_SRW_QUERY_SYNTAX_ERROR; /* must be be attributes + term */
878  memcpy(result, rpn->RPNStructure->u.simple->u.attributesPlusTerm,
879  sizeof(*result));
880  return 0;
881 }
882 
883 
884 static int ccl2pqf(ODR odr, const Odr_oct *ccl, CCL_bibset bibset,
885  bend_search_rr *bsrr)
886 {
887  char *ccl0;
888  struct ccl_rpn_node *node;
889  int errcode, pos;
890 
891  ccl0 = odr_strdupn(odr, (char*) ccl->buf, ccl->len);
892  if ((node = ccl_find_str(bibset, ccl0, &errcode, &pos)) == 0)
893  {
894  bsrr->errstring = (char*) ccl_err_msg(errcode);
895  return YAZ_SRW_QUERY_SYNTAX_ERROR; /* Query syntax error */
896  }
897 
898  bsrr->query->which = Z_Query_type_1;
899  bsrr->query->u.type_1 = ccl_rpn_query(odr, node);
900  return 0;
901 }
902 
903 static void srw_bend_search(association *assoc,
904  Z_SRW_PDU *sr,
905  Z_SRW_PDU *res,
906  int *http_code)
907 {
908  Z_SRW_searchRetrieveResponse *srw_res = res->u.response;
909  int srw_error = 0;
910  Z_External *ext;
911  Z_SRW_searchRetrieveRequest *srw_req = sr->u.request;
912 
913  *http_code = 200;
914  yaz_log(log_requestdetail, "Got SRW SearchRetrieveRequest");
915  srw_bend_init(assoc, &srw_res->diagnostics, &srw_res->num_diagnostics, sr);
916  if (srw_res->num_diagnostics == 0 && assoc->init)
917  {
918  bend_search_rr rr;
919  rr.setname = "default";
920  rr.replace_set = 1;
921  rr.num_bases = 1;
922  rr.basenames = &srw_req->database;
923  rr.referenceId = 0;
924  rr.srw_sortKeys = 0;
925  rr.srw_setname = 0;
926  rr.srw_setnameIdleTime = 0;
927  rr.estimated_hit_count = 0;
928  rr.partial_resultset = 0;
929  rr.query = (Z_Query *) odr_malloc(assoc->decode, sizeof(*rr.query));
930  rr.query->u.type_1 = 0;
931  rr.extra_args = sr->extra_args;
932  rr.extra_response_data = 0;
933  rr.present_number = srw_req->maximumRecords ?
934  *srw_req->maximumRecords : 0;
935 
936  if (srw_req->query_type == Z_SRW_query_type_cql)
937  {
938  if (assoc->server && assoc->server->cql_transform)
939  {
940  int srw_errcode = cql2pqf(assoc->encode, srw_req->query.cql,
941  assoc->server->cql_transform,
942  rr.query,
943  &rr.srw_sortKeys);
944 
945  if (srw_errcode)
946  {
948  &srw_res->diagnostics,
949  &srw_res->num_diagnostics,
950  srw_errcode, 0);
951  }
952  }
953  else
954  {
955  /* CQL query to backend. Wrap it - Z39.50 style */
956  ext = (Z_External *) odr_malloc(assoc->decode, sizeof(*ext));
958  "1.2.840.10003.16.2");
959  ext->indirect_reference = 0;
960  ext->descriptor = 0;
961  ext->which = Z_External_CQL;
962  ext->u.cql = srw_req->query.cql;
963 
965  rr.query->u.type_104 = ext;
966  }
967  }
968  else if (srw_req->query_type == Z_SRW_query_type_pqf)
969  {
970  Z_RPNQuery *RPNquery;
971  YAZ_PQF_Parser pqf_parser;
972 
973  pqf_parser = yaz_pqf_create();
974 
975  RPNquery = yaz_pqf_parse(pqf_parser, assoc->decode,
976  srw_req->query.pqf);
977  if (!RPNquery)
978  {
979  const char *pqf_msg;
980  size_t off;
981  int code = yaz_pqf_error(pqf_parser, &pqf_msg, &off);
982  yaz_log(log_requestdetail, "Parse error %d %s near offset %ld",
983  code, pqf_msg, (long) off);
984  srw_error = YAZ_SRW_QUERY_SYNTAX_ERROR;
985  }
986 
987  rr.query->which = Z_Query_type_1;
988  rr.query->u.type_1 = RPNquery;
989 
990  yaz_pqf_destroy(pqf_parser);
991  }
992  else
993  {
994  yaz_add_srw_diagnostic(assoc->encode, &srw_res->diagnostics,
995  &srw_res->num_diagnostics,
997  }
998  if (rr.query->u.type_1)
999  {
1000  rr.stream = assoc->encode;
1001  rr.decode = assoc->decode;
1002  rr.print = assoc->print;
1003  if (srw_req->sort.sortKeys)
1004  rr.srw_sortKeys = odr_strdup(assoc->encode,
1005  srw_req->sort.sortKeys);
1006  rr.association = assoc;
1007  rr.hits = 0;
1008  rr.errcode = 0;
1009  rr.errstring = 0;
1010  rr.search_info = 0;
1011  rr.search_input = 0;
1013 
1014  (assoc->init->bend_search)(assoc->backend, &rr);
1015  if (rr.errcode)
1016  {
1018  {
1019  *http_code = 404;
1020  }
1021  else
1022  {
1023  srw_error = yaz_diag_bib1_to_srw(rr.errcode);
1025  &srw_res->diagnostics,
1026  &srw_res->num_diagnostics,
1027  srw_error, rr.errstring);
1028  }
1029  }
1030  else
1031  {
1032  int number = srw_req->maximumRecords ?
1033  odr_int_to_int(*srw_req->maximumRecords) : 0;
1034  int start = srw_req->startRecord ?
1035  odr_int_to_int(*srw_req->startRecord) : 1;
1036 
1037  yaz_log(log_requestdetail, "Request to pack %d+%d out of "
1039  start, number, rr.hits);
1040 
1041  srw_res->numberOfRecords = odr_intdup(assoc->encode, rr.hits);
1042  if (rr.srw_setname)
1043  {
1044  srw_res->resultSetId =
1045  odr_strdup(assoc->encode, rr.srw_setname );
1046  srw_res->resultSetIdleTime =
1047  odr_intdup(assoc->encode, *rr.srw_setnameIdleTime );
1048  }
1049 
1050  if (start > rr.hits || start < 1)
1051  {
1052  /* if hits<=0 and start=1 we don't return a diagnostic */
1053  if (start != 1)
1055  assoc->encode,
1056  &srw_res->diagnostics, &srw_res->num_diagnostics,
1058  }
1059  else if (number > 0)
1060  {
1061  int i;
1062  int ok = 1;
1063  if (start + number > rr.hits)
1064  number = odr_int_to_int(rr.hits) - start + 1;
1065 
1066  /* Call bend_present if defined */
1067  if (assoc->init->bend_present)
1068  {
1069  bend_present_rr *bprr = (bend_present_rr*)
1070  odr_malloc(assoc->decode, sizeof(*bprr));
1071  bprr->setname = "default";
1072  bprr->start = start;
1073  bprr->number = number;
1074  if (srw_req->recordSchema)
1075  {
1076  bprr->comp = (Z_RecordComposition *) odr_malloc(assoc->decode,
1077  sizeof(*bprr->comp));
1078  bprr->comp->which = Z_RecordComp_simple;
1079  bprr->comp->u.simple = (Z_ElementSetNames *)
1080  odr_malloc(assoc->decode, sizeof(Z_ElementSetNames));
1082  bprr->comp->u.simple->u.generic = srw_req->recordSchema;
1083  }
1084  else
1085  {
1086  bprr->comp = 0;
1087  }
1088  bprr->stream = assoc->encode;
1089  bprr->referenceId = 0;
1090  bprr->print = assoc->print;
1091  bprr->association = assoc;
1092  bprr->errcode = 0;
1093  bprr->errstring = NULL;
1094  (*assoc->init->bend_present)(assoc->backend, bprr);
1095 
1096  if (bprr->errcode)
1097  {
1098  srw_error = yaz_diag_bib1_to_srw(bprr->errcode);
1100  &srw_res->diagnostics,
1101  &srw_res->num_diagnostics,
1102  srw_error, bprr->errstring);
1103  ok = 0;
1104  }
1105  }
1106 
1107  if (ok)
1108  {
1109  int j = 0;
1110  int packing = Z_SRW_recordPacking_string;
1111  if (srw_req->recordPacking)
1112  {
1113  packing =
1115  if (packing == -1)
1116  packing = Z_SRW_recordPacking_string;
1117  }
1118  srw_res->records = (Z_SRW_record *)
1119  odr_malloc(assoc->encode,
1120  number * sizeof(*srw_res->records));
1121 
1122  srw_res->extra_records = (Z_SRW_extra_record **)
1123  odr_malloc(assoc->encode,
1124  number*sizeof(*srw_res->extra_records));
1125 
1126  for (i = 0; i<number; i++)
1127  {
1128  int errcode;
1129  int last_in_set = 0;
1130  const char *addinfo = 0;
1131 
1132  srw_res->records[j].recordPacking = packing;
1133  srw_res->records[j].recordData_buf = 0;
1134  srw_res->extra_records[j] = 0;
1135  yaz_log(YLOG_DEBUG, "srw_bend_fetch %d", i+start);
1136  errcode = srw_bend_fetch(assoc, i+start, srw_req,
1137  srw_res->records + j,
1138  &addinfo, &last_in_set);
1139  if (errcode)
1140  {
1142  &srw_res->diagnostics,
1143  &srw_res->num_diagnostics,
1144  yaz_diag_bib1_to_srw(errcode),
1145  addinfo);
1146 
1147  break;
1148  }
1149  if (srw_res->records[j].recordData_buf)
1150  j++;
1151  if (last_in_set)
1152  break;
1153  }
1154  srw_res->num_records = j;
1155  if (!j)
1156  srw_res->records = 0;
1157  }
1158  }
1159  if (rr.extra_response_data)
1160  {
1162  res->extraResponseData_len = strlen(rr.extra_response_data);
1163  }
1165  {
1167  assoc->encode,
1168  &srw_res->diagnostics,
1169  &srw_res->num_diagnostics,
1171  0);
1172  }
1173  }
1174  }
1175  }
1176  if (log_request)
1177  {
1178  const char *querystr = "?";
1179  const char *querytype = "?";
1180  WRBUF wr = wrbuf_alloc();
1181 
1182  switch (srw_req->query_type)
1183  {
1184  case Z_SRW_query_type_cql:
1185  querytype = "CQL";
1186  querystr = srw_req->query.cql;
1187  break;
1188  case Z_SRW_query_type_pqf:
1189  querytype = "PQF";
1190  querystr = srw_req->query.pqf;
1191  break;
1192  }
1193  wrbuf_printf(wr, "SRWSearch %s ", srw_req->database);
1194  if (srw_res->num_diagnostics)
1195  wrbuf_printf(wr, "ERROR %s", srw_res->diagnostics[0].uri);
1196  else if (*http_code != 200)
1197  wrbuf_printf(wr, "ERROR info:http/%d", *http_code);
1198  else if (srw_res->numberOfRecords)
1199  {
1200  wrbuf_printf(wr, "OK " ODR_INT_PRINTF,
1201  (srw_res->numberOfRecords ?
1202  *srw_res->numberOfRecords : 0));
1203  }
1204  wrbuf_printf(wr, " %s " ODR_INT_PRINTF "+%d",
1205  (srw_res->resultSetId ?
1206  srw_res->resultSetId : "-"),
1207  (srw_req->startRecord ? *srw_req->startRecord : 1),
1208  srw_res->num_records);
1209  yaz_log(log_request, "%s %s: %s", wrbuf_cstr(wr), querytype, querystr);
1210  wrbuf_destroy(wr);
1211  }
1212 }
1213 
1215 {
1216 #if YAZ_HAVE_XML2
1217  xmlNodePtr ptr = (xmlNode *) rr->server_node_ptr;
1218  if (!ptr)
1219  return 0;
1220  for (ptr = ptr->children; ptr; ptr = ptr->next)
1221  {
1222  if (ptr->type != XML_ELEMENT_NODE)
1223  continue;
1224  if (!strcmp((const char *) ptr->name, "explain"))
1225  {
1226  int len;
1227  xmlDocPtr doc = xmlNewDoc(BAD_CAST "1.0");
1228  xmlChar *buf_out;
1229  char *content;
1230 
1231  ptr = xmlCopyNode(ptr, 1);
1232 
1233  xmlDocSetRootElement(doc, ptr);
1234 
1235  xmlDocDumpMemory(doc, &buf_out, &len);
1236  content = (char*) odr_malloc(rr->stream, 1+len);
1237  memcpy(content, buf_out, len);
1238  content[len] = '\0';
1239 
1240  xmlFree(buf_out);
1241  xmlFreeDoc(doc);
1242  rr->explain_buf = content;
1243  return 0;
1244  }
1245  }
1246 #endif
1247  return 0;
1248 }
1249 
1250 static void srw_bend_explain(association *assoc,
1251  Z_SRW_PDU *sr,
1252  Z_SRW_explainResponse *srw_res,
1253  int *http_code)
1254 {
1255  Z_SRW_explainRequest *srw_req = sr->u.explain_request;
1256  yaz_log(log_requestdetail, "Got SRW ExplainRequest");
1257  *http_code = 404;
1258  srw_bend_init(assoc, &srw_res->diagnostics, &srw_res->num_diagnostics, sr);
1259  if (assoc->init)
1260  {
1261  bend_explain_rr rr;
1262 
1263  rr.stream = assoc->encode;
1264  rr.decode = assoc->decode;
1265  rr.print = assoc->print;
1266  rr.explain_buf = 0;
1267  rr.database = srw_req->database;
1268  if (assoc->server)
1269  rr.server_node_ptr = assoc->server->server_node_ptr;
1270  else
1271  rr.server_node_ptr = 0;
1272  rr.schema = "http://explain.z3950.org/dtd/2.0/";
1273  if (assoc->init->bend_explain)
1274  (*assoc->init->bend_explain)(assoc->backend, &rr);
1275  else
1277 
1278  if (rr.explain_buf)
1279  {
1280  int packing = Z_SRW_recordPacking_string;
1281  if (srw_req->recordPacking)
1282  {
1283  packing =
1285  if (packing == -1)
1286  packing = Z_SRW_recordPacking_string;
1287  }
1288  srw_res->record.recordSchema = rr.schema;
1289  srw_res->record.recordPacking = packing;
1290  srw_res->record.recordData_buf = rr.explain_buf;
1291  srw_res->record.recordData_len = strlen(rr.explain_buf);
1292  srw_res->record.recordPosition = 0;
1293  *http_code = 200;
1294  }
1295  }
1296 }
1297 
1298 static void srw_bend_scan(association *assoc,
1299  Z_SRW_PDU *sr,
1300  Z_SRW_PDU *res,
1301  int *http_code)
1302 {
1303  Z_SRW_scanRequest *srw_req = sr->u.scan_request;
1304  Z_SRW_scanResponse *srw_res = res->u.scan_response;
1305  yaz_log(log_requestdetail, "Got SRW ScanRequest");
1306 
1307  *http_code = 200;
1308  srw_bend_init(assoc, &srw_res->diagnostics, &srw_res->num_diagnostics, sr);
1309  if (srw_res->num_diagnostics == 0 && assoc->init)
1310  {
1311  int step_size = 0;
1312  struct scan_entry *save_entries;
1313 
1314  bend_scan_rr *bsrr = (bend_scan_rr *)
1315  odr_malloc(assoc->encode, sizeof(*bsrr));
1316  bsrr->num_bases = 1;
1317  bsrr->basenames = &srw_req->database;
1318 
1319  bsrr->num_entries = srw_req->maximumTerms ?
1320  odr_int_to_int(*srw_req->maximumTerms) : 10;
1321  bsrr->term_position = srw_req->responsePosition ?
1322  odr_int_to_int(*srw_req->responsePosition) : 1;
1323 
1324  bsrr->errcode = 0;
1325  bsrr->errstring = 0;
1326  bsrr->referenceId = 0;
1327  bsrr->stream = assoc->encode;
1328  bsrr->print = assoc->print;
1329  bsrr->step_size = &step_size;
1330  bsrr->entries = 0;
1331  bsrr->setname = 0;
1332  bsrr->extra_args = sr->extra_args;
1333  bsrr->extra_response_data = 0;
1334 
1335  if (bsrr->num_entries > 0)
1336  {
1337  int i;
1338  bsrr->entries = (struct scan_entry *)
1339  odr_malloc(assoc->decode, sizeof(*bsrr->entries) *
1340  bsrr->num_entries);
1341  for (i = 0; i<bsrr->num_entries; i++)
1342  {
1343  bsrr->entries[i].term = 0;
1344  bsrr->entries[i].occurrences = 0;
1345  bsrr->entries[i].errcode = 0;
1346  bsrr->entries[i].errstring = 0;
1347  bsrr->entries[i].display_term = 0;
1348  }
1349  }
1350  save_entries = bsrr->entries; /* save it so we can compare later */
1351 
1352  if (srw_req->query_type == Z_SRW_query_type_pqf &&
1353  assoc->init->bend_scan)
1354  {
1355  YAZ_PQF_Parser pqf_parser = yaz_pqf_create();
1356 
1357  bsrr->term = yaz_pqf_scan(pqf_parser, assoc->decode,
1358  &bsrr->attributeset,
1359  srw_req->scanClause.pqf);
1360  yaz_pqf_destroy(pqf_parser);
1361  bsrr->scanClause = 0;
1362  ((int (*)(void *, bend_scan_rr *))
1363  (*assoc->init->bend_scan))(assoc->backend, bsrr);
1364  }
1365  else if (srw_req->query_type == Z_SRW_query_type_cql
1366  && assoc->init->bend_scan && assoc->server
1367  && assoc->server->cql_transform)
1368  {
1369  int srw_error;
1370  bsrr->scanClause = 0;
1371  bsrr->attributeset = 0;
1372  bsrr->term = (Z_AttributesPlusTerm *)
1373  odr_malloc(assoc->decode, sizeof(*bsrr->term));
1374  srw_error = cql2pqf_scan(assoc->encode,
1375  srw_req->scanClause.cql,
1376  assoc->server->cql_transform,
1377  bsrr->term);
1378  if (srw_error)
1379  yaz_add_srw_diagnostic(assoc->encode, &srw_res->diagnostics,
1380  &srw_res->num_diagnostics,
1381  srw_error, 0);
1382  else
1383  {
1384  ((int (*)(void *, bend_scan_rr *))
1385  (*assoc->init->bend_scan))(assoc->backend, bsrr);
1386  }
1387  }
1388  else if (srw_req->query_type == Z_SRW_query_type_cql
1389  && assoc->init->bend_srw_scan)
1390  {
1391  bsrr->term = 0;
1392  bsrr->attributeset = 0;
1393  bsrr->scanClause = srw_req->scanClause.cql;
1394  ((int (*)(void *, bend_scan_rr *))
1395  (*assoc->init->bend_srw_scan))(assoc->backend, bsrr);
1396  }
1397  else
1398  {
1399  yaz_add_srw_diagnostic(assoc->encode, &srw_res->diagnostics,
1400  &srw_res->num_diagnostics,
1401  YAZ_SRW_UNSUPP_OPERATION, "scan");
1402  }
1403  if (bsrr->extra_response_data)
1404  {
1406  res->extraResponseData_len = strlen(bsrr->extra_response_data);
1407  }
1408  if (bsrr->errcode)
1409  {
1410  int srw_error;
1412  {
1413  *http_code = 404;
1414  return;
1415  }
1416  srw_error = yaz_diag_bib1_to_srw(bsrr->errcode);
1417 
1418  yaz_add_srw_diagnostic(assoc->encode, &srw_res->diagnostics,
1419  &srw_res->num_diagnostics,
1420  srw_error, bsrr->errstring);
1421  }
1422  else if (srw_res->num_diagnostics == 0 && bsrr->num_entries)
1423  {
1424  int i;
1425  srw_res->terms = (Z_SRW_scanTerm*)
1426  odr_malloc(assoc->encode, sizeof(*srw_res->terms) *
1427  bsrr->num_entries);
1428 
1429  srw_res->num_terms = bsrr->num_entries;
1430  for (i = 0; i<bsrr->num_entries; i++)
1431  {
1432  Z_SRW_scanTerm *t = srw_res->terms + i;
1433  t->value = odr_strdup(assoc->encode, bsrr->entries[i].term);
1434  t->numberOfRecords =
1435  odr_intdup(assoc->encode, bsrr->entries[i].occurrences);
1436  t->displayTerm = 0;
1437  if (save_entries == bsrr->entries &&
1438  bsrr->entries[i].display_term)
1439  {
1440  /* the entries was _not_ set by the handler. So it's
1441  safe to test for new member display_term. It is
1442  NULL'ed by us.
1443  */
1444  t->displayTerm = odr_strdup(assoc->encode,
1445  bsrr->entries[i].display_term);
1446  }
1447  t->whereInList = 0;
1448  }
1449  }
1450  }
1451  if (log_request)
1452  {
1453  WRBUF wr = wrbuf_alloc();
1454  const char *querytype = 0;
1455  const char *querystr = 0;
1456 
1457  switch(srw_req->query_type)
1458  {
1459  case Z_SRW_query_type_pqf:
1460  querytype = "PQF";
1461  querystr = srw_req->scanClause.pqf;
1462  break;
1463  case Z_SRW_query_type_cql:
1464  querytype = "CQL";
1465  querystr = srw_req->scanClause.cql;
1466  break;
1467  default:
1468  querytype = "UNKNOWN";
1469  querystr = "";
1470  }
1471 
1472  wrbuf_printf(wr, "SRWScan %s ", srw_req->database);
1473 
1474  if (srw_res->num_diagnostics)
1475  wrbuf_printf(wr, "ERROR %s - ", srw_res->diagnostics[0].uri);
1476  else if (srw_res->num_terms)
1477  wrbuf_printf(wr, "OK %d - ", srw_res->num_terms);
1478  else
1479  wrbuf_printf(wr, "OK - - ");
1480 
1482  (srw_req->responsePosition ?
1483  *srw_req->responsePosition : 1),
1484  (srw_req->maximumTerms ?
1485  *srw_req->maximumTerms : 1));
1486  /* there is no step size in SRU/W ??? */
1487  wrbuf_printf(wr, "%s: %s ", querytype, querystr);
1488  yaz_log(log_request, "%s ", wrbuf_cstr(wr) );
1489  wrbuf_destroy(wr);
1490  }
1491 
1492 }
1493 
1494 static void srw_bend_update(association *assoc,
1495  Z_SRW_PDU *sr,
1496  Z_SRW_updateResponse *srw_res,
1497  int *http_code)
1498 {
1499  Z_SRW_updateRequest *srw_req = sr->u.update_request;
1500  yaz_log(log_session, "SRWUpdate action=%s", srw_req->operation);
1501  yaz_log(YLOG_DEBUG, "num_diag = %d", srw_res->num_diagnostics );
1502  *http_code = 404;
1503  srw_bend_init(assoc, &srw_res->diagnostics, &srw_res->num_diagnostics, sr);
1504  if (assoc->init)
1505  {
1506  bend_update_rr rr;
1507  Z_SRW_extra_record *extra = srw_req->extra_record;
1508 
1509  rr.stream = assoc->encode;
1510  rr.print = assoc->print;
1511  rr.num_bases = 1;
1512  rr.basenames = &srw_req->database;
1513  rr.operation = srw_req->operation;
1514  rr.operation_status = "failed";
1515  rr.record_id = 0;
1516  rr.record_versions = 0;
1517  rr.num_versions = 0;
1518  rr.record_packing = "string";
1519  rr.record_schema = 0;
1520  rr.record_data = 0;
1521  rr.extra_record_data = 0;
1522  rr.extra_request_data = 0;
1523  rr.extra_response_data = 0;
1524  rr.uri = 0;
1525  rr.message = 0;
1526  rr.details = 0;
1527 
1528  *http_code = 200;
1529  if (rr.operation == 0)
1530  {
1532  assoc->encode, &srw_res->diagnostics,
1533  &srw_res->num_diagnostics,
1535  "action" );
1536  return;
1537  }
1538  yaz_log(YLOG_DEBUG, "basename = %s", rr.basenames[0] );
1539  yaz_log(YLOG_DEBUG, "Operation = %s", rr.operation );
1540  if (!strcmp( rr.operation, "delete"))
1541  {
1542  if (srw_req->record && !srw_req->record->recordSchema)
1543  {
1545  assoc->encode,
1546  srw_req->record->recordSchema);
1547  }
1548  if (srw_req->record)
1549  {
1550  rr.record_data = odr_strdupn(
1551  assoc->encode,
1552  srw_req->record->recordData_buf,
1553  srw_req->record->recordData_len );
1554  }
1555  if (extra && extra->extraRecordData_len)
1556  {
1558  assoc->encode,
1559  extra->extraRecordData_buf,
1560  extra->extraRecordData_len );
1561  }
1562  if (srw_req->recordId)
1563  rr.record_id = srw_req->recordId;
1564  else if (extra && extra->recordIdentifier)
1565  rr.record_id = extra->recordIdentifier;
1566  }
1567  else if (!strcmp(rr.operation, "replace"))
1568  {
1569  if (srw_req->recordId)
1570  rr.record_id = srw_req->recordId;
1571  else if (extra && extra->recordIdentifier)
1572  rr.record_id = extra->recordIdentifier;
1573  else
1574  {
1576  assoc->encode, &srw_res->diagnostics,
1577  &srw_res->num_diagnostics,
1579  "recordIdentifier");
1580  }
1581  if (!srw_req->record)
1582  {
1584  assoc->encode, &srw_res->diagnostics,
1585  &srw_res->num_diagnostics,
1587  "record");
1588  }
1589  else
1590  {
1591  if (srw_req->record->recordSchema)
1593  assoc->encode, srw_req->record->recordSchema);
1594  if (srw_req->record->recordData_len )
1595  {
1596  rr.record_data = odr_strdupn(assoc->encode,
1597  srw_req->record->recordData_buf,
1598  srw_req->record->recordData_len );
1599  }
1600  else
1601  {
1603  assoc->encode, &srw_res->diagnostics,
1604  &srw_res->num_diagnostics,
1606  "recordData" );
1607  }
1608  }
1609  if (extra && extra->extraRecordData_len)
1610  {
1612  assoc->encode,
1613  extra->extraRecordData_buf,
1614  extra->extraRecordData_len );
1615  }
1616  }
1617  else if (!strcmp(rr.operation, "insert"))
1618  {
1619  if (srw_req->recordId)
1620  rr.record_id = srw_req->recordId;
1621  else if (extra)
1622  rr.record_id = extra->recordIdentifier;
1623 
1624  if (srw_req->record)
1625  {
1626  if (srw_req->record->recordSchema)
1628  assoc->encode, srw_req->record->recordSchema);
1629 
1630  if (srw_req->record->recordData_len)
1631  rr.record_data = odr_strdupn(
1632  assoc->encode,
1633  srw_req->record->recordData_buf,
1634  srw_req->record->recordData_len );
1635  }
1636  if (extra && extra->extraRecordData_len)
1637  {
1639  assoc->encode,
1640  extra->extraRecordData_buf,
1641  extra->extraRecordData_len );
1642  }
1643  }
1644  else
1646  &srw_res->num_diagnostics,
1648  rr.operation );
1649 
1650  if (srw_req->record)
1651  {
1652  const char *pack_str =
1654  if (pack_str)
1655  rr.record_packing = odr_strdup(assoc->encode, pack_str);
1656  }
1657 
1658  if (srw_req->num_recordVersions)
1659  {
1660  rr.record_versions = srw_req->recordVersions;
1661  rr.num_versions = srw_req->num_recordVersions;
1662  }
1663  if (srw_req->extraRequestData_len)
1664  {
1666  srw_req->extraRequestData_buf,
1667  srw_req->extraRequestData_len );
1668  }
1669  if (srw_res->num_diagnostics == 0)
1670  {
1671  if ( assoc->init->bend_srw_update)
1672  (*assoc->init->bend_srw_update)(assoc->backend, &rr);
1673  else
1675  assoc->encode, &srw_res->diagnostics,
1676  &srw_res->num_diagnostics,
1678  "No Update backend handler");
1679  }
1680 
1681  if (rr.uri)
1683  &srw_res->diagnostics,
1684  &srw_res->num_diagnostics,
1685  rr.uri,
1686  rr.message,
1687  rr.details);
1688  srw_res->recordId = rr.record_id;
1689  srw_res->operationStatus = rr.operation_status;
1690  srw_res->recordVersions = rr.record_versions;
1691  srw_res->num_recordVersions = rr.num_versions;
1692  if (srw_res->extraResponseData_len)
1693  {
1695  srw_res->extraResponseData_len = strlen(rr.extra_response_data);
1696  }
1697  if (srw_res->num_diagnostics == 0 && rr.record_data)
1698  {
1699  srw_res->record = yaz_srw_get_record(assoc->encode);
1700  srw_res->record->recordSchema = rr.record_schema;
1701  if (rr.record_packing)
1702  {
1703  int pack = yaz_srw_str_to_pack(rr.record_packing);
1704 
1705  if (pack == -1)
1706  {
1708  yaz_log(YLOG_WARN, "Back packing %s from backend",
1709  rr.record_packing);
1710  }
1711  srw_res->record->recordPacking = pack;
1712  }
1713  srw_res->record->recordData_buf = rr.record_data;
1714  srw_res->record->recordData_len = strlen(rr.record_data);
1715  if (rr.extra_record_data)
1716  {
1717  Z_SRW_extra_record *ex =
1719  srw_res->extra_record = ex;
1721  ex->extraRecordData_len = strlen(rr.extra_record_data);
1722  }
1723  }
1724  }
1725 }
1726 
1727 /* check if path is OK (1); BAD (0) */
1728 static int check_path(const char *path)
1729 {
1730  if (*path != '/')
1731  return 0;
1732  if (strstr(path, ".."))
1733  return 0;
1734  return 1;
1735 }
1736 
1737 static char *read_file(const char *fname, ODR o, size_t *sz)
1738 {
1739  char *buf;
1740  FILE *inf = fopen(fname, "rb");
1741  if (!inf)
1742  return 0;
1743 
1744  fseek(inf, 0L, SEEK_END);
1745  *sz = ftell(inf);
1746  rewind(inf);
1747  buf = (char *) odr_malloc(o, *sz);
1748  if (fread(buf, 1, *sz, inf) != *sz)
1749  yaz_log(YLOG_WARN|YLOG_ERRNO, "short read %s", fname);
1750  fclose(inf);
1751  return buf;
1752 }
1753 
1754 static void process_http_request(association *assoc, request *req)
1755 {
1756  Z_HTTP_Request *hreq = req->gdu_request->u.HTTP_Request;
1757  ODR o = assoc->encode;
1758  int r = 2; /* 2=NOT TAKEN, 1=TAKEN, 0=SOAP TAKEN */
1759  Z_SRW_PDU *sr = 0;
1760  Z_SOAP *soap_package = 0;
1761  Z_GDU *p = 0;
1762  char *charset = 0;
1763  Z_HTTP_Response *hres = 0;
1764  int keepalive = 1;
1765  const char *stylesheet = 0; /* for now .. set later */
1766  Z_SRW_diagnostic *diagnostic = 0;
1767  int num_diagnostic = 0;
1768  const char *host = z_HTTP_header_lookup(hreq->headers, "Host");
1769 
1770  yaz_log(log_request, "%s %s HTTP/%s", hreq->method, hreq->path, hreq->version);
1771  if (!control_association(assoc, host, 0))
1772  {
1773  p = z_get_HTTP_Response(o, 404);
1774  r = 1;
1775  }
1776  if (r == 2 && assoc->server && assoc->server->docpath
1777  && hreq->path[0] == '/'
1778  &&
1779  /* check if path is a proper prefix of documentroot */
1780  strncmp(hreq->path+1, assoc->server->docpath,
1781  strlen(assoc->server->docpath))
1782  == 0)
1783  {
1784  if (!check_path(hreq->path))
1785  {
1786  yaz_log(YLOG_LOG, "File %s access forbidden", hreq->path+1);
1787  p = z_get_HTTP_Response(o, 404);
1788  }
1789  else
1790  {
1791  size_t content_size = 0;
1792  char *content_buf = read_file(hreq->path+1, o, &content_size);
1793  if (!content_buf)
1794  {
1795  yaz_log(YLOG_LOG, "File %s not found", hreq->path+1);
1796  p = z_get_HTTP_Response(o, 404);
1797  }
1798  else
1799  {
1800  const char *ctype = 0;
1802 
1803  yaz_mime_types_add(types, "xsl", "application/xml");
1804  yaz_mime_types_add(types, "xml", "application/xml");
1805  yaz_mime_types_add(types, "css", "text/css");
1806  yaz_mime_types_add(types, "html", "text/html");
1807  yaz_mime_types_add(types, "htm", "text/html");
1808  yaz_mime_types_add(types, "txt", "text/plain");
1809  yaz_mime_types_add(types, "js", "application/x-javascript");
1810 
1811  yaz_mime_types_add(types, "gif", "image/gif");
1812  yaz_mime_types_add(types, "png", "image/png");
1813  yaz_mime_types_add(types, "jpg", "image/jpeg");
1814  yaz_mime_types_add(types, "jpeg", "image/jpeg");
1815 
1816  ctype = yaz_mime_lookup_fname(types, hreq->path);
1817  if (!ctype)
1818  {
1819  yaz_log(YLOG_LOG, "No mime type for %s", hreq->path+1);
1820  p = z_get_HTTP_Response(o, 404);
1821  }
1822  else
1823  {
1824  p = z_get_HTTP_Response(o, 200);
1825  hres = p->u.HTTP_Response;
1826  hres->content_buf = content_buf;
1827  hres->content_len = content_size;
1828  z_HTTP_header_add(o, &hres->headers, "Content-Type", ctype);
1829  }
1830  yaz_mime_types_destroy(types);
1831  }
1832  }
1833  r = 1;
1834  }
1835 
1836  if (r == 2)
1837  {
1838  r = yaz_srw_decode(hreq, &sr, &soap_package, assoc->decode, &charset);
1839  yaz_log(YLOG_DEBUG, "yaz_srw_decode returned %d", r);
1840  }
1841  if (r == 2) /* not taken */
1842  {
1843  r = yaz_sru_decode(hreq, &sr, &soap_package, assoc->decode, &charset,
1844  &diagnostic, &num_diagnostic);
1845  yaz_log(YLOG_DEBUG, "yaz_sru_decode returned %d", r);
1846  }
1847  if (r == 0) /* decode SRW/SRU OK .. */
1848  {
1849  int http_code = 200;
1851  {
1852  Z_SRW_PDU *res =
1854  sr->srw_version);
1855  stylesheet = sr->u.request->stylesheet;
1856  if (num_diagnostic)
1857  {
1858  res->u.response->diagnostics = diagnostic;
1859  res->u.response->num_diagnostics = num_diagnostic;
1860  }
1861  else
1862  {
1863  srw_bend_search(assoc, sr, res, &http_code);
1864  }
1865  if (http_code == 200)
1866  soap_package->u.generic->p = res;
1867  }
1868  else if (sr->which == Z_SRW_explain_request)
1869  {
1871  sr->srw_version);
1872  stylesheet = sr->u.explain_request->stylesheet;
1873  if (num_diagnostic)
1874  {
1875  res->u.explain_response->diagnostics = diagnostic;
1876  res->u.explain_response->num_diagnostics = num_diagnostic;
1877  }
1878  srw_bend_explain(assoc, sr, res->u.explain_response, &http_code);
1879  if (http_code == 200)
1880  soap_package->u.generic->p = res;
1881  }
1882  else if (sr->which == Z_SRW_scan_request)
1883  {
1885  sr->srw_version);
1886  stylesheet = sr->u.scan_request->stylesheet;
1887  if (num_diagnostic)
1888  {
1889  res->u.scan_response->diagnostics = diagnostic;
1890  res->u.scan_response->num_diagnostics = num_diagnostic;
1891  }
1892  srw_bend_scan(assoc, sr, res, &http_code);
1893  if (http_code == 200)
1894  soap_package->u.generic->p = res;
1895  }
1896  else if (sr->which == Z_SRW_update_request)
1897  {
1899  sr->srw_version);
1900  yaz_log(YLOG_DEBUG, "handling SRW UpdateRequest");
1901  if (num_diagnostic)
1902  {
1903  res->u.update_response->diagnostics = diagnostic;
1904  res->u.update_response->num_diagnostics = num_diagnostic;
1905  }
1906  yaz_log(YLOG_DEBUG, "num_diag = %d", res->u.update_response->num_diagnostics );
1907  srw_bend_update(assoc, sr, res->u.update_response, &http_code);
1908  if (http_code == 200)
1909  soap_package->u.generic->p = res;
1910  }
1911  else
1912  {
1913  yaz_log(log_request, "SOAP ERROR");
1914  /* FIXME - what error, what query */
1915  http_code = 500;
1916  z_soap_error(assoc->encode, soap_package,
1917  "SOAP-ENV:Client", "Bad method", 0);
1918  }
1919  if (http_code == 200 || http_code == 500)
1920  {
1921  static Z_SOAP_Handler soap_handlers[4] = {
1922 #if YAZ_HAVE_XML2
1926 #endif
1927  {0, 0, 0}
1928  };
1929  char ctype[80];
1930  p = z_get_HTTP_Response(o, 200);
1931  hres = p->u.HTTP_Response;
1932 
1933  if (!stylesheet && assoc->server)
1934  stylesheet = assoc->server->stylesheet;
1935 
1936  /* empty stylesheet means NO stylesheet */
1937  if (stylesheet && *stylesheet == '\0')
1938  stylesheet = 0;
1939 
1940  z_soap_codec_enc_xsl(assoc->encode, &soap_package,
1941  &hres->content_buf, &hres->content_len,
1942  soap_handlers, charset, stylesheet);
1943  hres->code = http_code;
1944 
1945  strcpy(ctype, "text/xml");
1946  if (charset && strlen(charset) < sizeof(ctype)-30)
1947  {
1948  strcat(ctype, "; charset=");
1949  strcat(ctype, charset);
1950  }
1951  z_HTTP_header_add(o, &hres->headers, "Content-Type", ctype);
1952  }
1953  else
1954  p = z_get_HTTP_Response(o, http_code);
1955  }
1956 
1957  if (p == 0)
1958  p = z_get_HTTP_Response(o, 500);
1959  hres = p->u.HTTP_Response;
1960  if (!strcmp(hreq->version, "1.0"))
1961  {
1962  const char *v = z_HTTP_header_lookup(hreq->headers, "Connection");
1963  if (v && !strcmp(v, "Keep-Alive"))
1964  keepalive = 1;
1965  else
1966  keepalive = 0;
1967  hres->version = "1.0";
1968  }
1969  else
1970  {
1971  const char *v = z_HTTP_header_lookup(hreq->headers, "Connection");
1972  if (v && !strcmp(v, "close"))
1973  keepalive = 0;
1974  else
1975  keepalive = 1;
1976  hres->version = "1.1";
1977  }
1978  if (!keepalive || !assoc->last_control->keepalive)
1979  {
1980  z_HTTP_header_add(o, &hres->headers, "Connection", "close");
1981  assoc->state = ASSOC_DEAD;
1982  assoc->cs_get_mask = 0;
1983  }
1984  else
1985  {
1986  int t;
1987  const char *alive = z_HTTP_header_lookup(hreq->headers, "Keep-Alive");
1988 
1989  if (alive && yaz_isdigit(*(const unsigned char *) alive))
1990  t = atoi(alive);
1991  else
1992  t = 15;
1993  if (t < 0 || t > 3600)
1994  t = 3600;
1995  iochan_settimeout(assoc->client_chan,t);
1996  z_HTTP_header_add(o, &hres->headers, "Connection", "Keep-Alive");
1997  }
1998  process_gdu_response(assoc, req, p);
1999 }
2000 
2001 static void process_gdu_request(association *assoc, request *req)
2002 {
2003  if (req->gdu_request->which == Z_GDU_Z3950)
2004  {
2005  char *msg = 0;
2006  req->apdu_request = req->gdu_request->u.z3950;
2007  if (process_z_request(assoc, req, &msg) < 0)
2008  do_close_req(assoc, Z_Close_systemProblem, msg, req);
2009  }
2010  else if (req->gdu_request->which == Z_GDU_HTTP_Request)
2011  process_http_request(assoc, req);
2012  else
2013  {
2014  do_close_req(assoc, Z_Close_systemProblem, "bad protocol packet", req);
2015  }
2016 }
2017 
2018 /*
2019  * Initiate request processing.
2020  */
2021 static int process_z_request(association *assoc, request *req, char **msg)
2022 {
2023  Z_APDU *res;
2024  int retval;
2025 
2026  *msg = "Unknown Error";
2027  assert(req && req->state == REQUEST_IDLE);
2028  if (req->apdu_request->which != Z_APDU_initRequest && !assoc->init)
2029  {
2030  *msg = "Missing InitRequest";
2031  return -1;
2032  }
2033  switch (req->apdu_request->which)
2034  {
2035  case Z_APDU_initRequest:
2036  res = process_initRequest(assoc, req); break;
2037  case Z_APDU_searchRequest:
2038  res = process_searchRequest(assoc, req); break;
2039  case Z_APDU_presentRequest:
2040  res = process_presentRequest(assoc, req); break;
2041  case Z_APDU_scanRequest:
2042  if (assoc->init->bend_scan)
2043  res = process_scanRequest(assoc, req);
2044  else
2045  {
2046  *msg = "Cannot handle Scan APDU";
2047  return -1;
2048  }
2049  break;
2051  if (assoc->init->bend_esrequest)
2052  res = process_ESRequest(assoc, req);
2053  else
2054  {
2055  *msg = "Cannot handle Extended Services APDU";
2056  return -1;
2057  }
2058  break;
2059  case Z_APDU_sortRequest:
2060  if (assoc->init->bend_sort)
2061  res = process_sortRequest(assoc, req);
2062  else
2063  {
2064  *msg = "Cannot handle Sort APDU";
2065  return -1;
2066  }
2067  break;
2068  case Z_APDU_close:
2069  process_close(assoc, req);
2070  return 0;
2072  if (assoc->init->bend_delete)
2073  res = process_deleteRequest(assoc, req);
2074  else
2075  {
2076  *msg = "Cannot handle Delete APDU";
2077  return -1;
2078  }
2079  break;
2080  case Z_APDU_segmentRequest:
2081  if (assoc->init->bend_segment)
2082  {
2083  res = process_segmentRequest(assoc, req);
2084  }
2085  else
2086  {
2087  *msg = "Cannot handle Segment APDU";
2088  return -1;
2089  }
2090  break;
2092  return 0;
2093  default:
2094  *msg = "Bad APDU received";
2095  return -1;
2096  }
2097  if (res)
2098  {
2099  yaz_log(YLOG_DEBUG, " result immediately available");
2100  retval = process_z_response(assoc, req, res);
2101  }
2102  else
2103  {
2104  yaz_log(YLOG_DEBUG, " result unavailable");
2105  retval = -1;
2106  }
2107  return retval;
2108 }
2109 
2110 /*
2111  * Encode response, and transfer the request structure to the outgoing queue.
2112  */
2113 static int process_gdu_response(association *assoc, request *req, Z_GDU *res)
2114 {
2115  odr_setbuf(assoc->encode, req->response, req->size_response, 1);
2116 
2117  if (assoc->print)
2118  {
2119  if (!z_GDU(assoc->print, &res, 0, 0))
2120  yaz_log(YLOG_WARN, "ODR print error: %s",
2121  odr_errmsg(odr_geterror(assoc->print)));
2122  odr_reset(assoc->print);
2123  }
2124  if (!z_GDU(assoc->encode, &res, 0, 0))
2125  {
2126  yaz_log(YLOG_WARN, "ODR error when encoding PDU: %s [element %s]",
2127  odr_errmsg(odr_geterror(assoc->decode)),
2128  odr_getelement(assoc->decode));
2129  return -1;
2130  }
2131  req->response = odr_getbuf(assoc->encode, &req->len_response,
2132  &req->size_response);
2133  odr_setbuf(assoc->encode, 0, 0, 0); /* don'txfree if we abort later */
2134  odr_reset(assoc->encode);
2135  req->state = REQUEST_IDLE;
2136  request_enq(&assoc->outgoing, req);
2137  /* turn the work over to the ir_session handler */
2139  assoc->cs_put_mask = EVENT_OUTPUT;
2140  /* Is there more work to be done? give that to the input handler too */
2141  for (;;)
2142  {
2143  req = request_head(&assoc->incoming);
2144  if (req && req->state == REQUEST_IDLE)
2145  {
2146  request_deq(&assoc->incoming);
2147  process_gdu_request(assoc, req);
2148  }
2149  else
2150  break;
2151  }
2152  return 0;
2153 }
2154 
2155 /*
2156  * Encode response, and transfer the request structure to the outgoing queue.
2157  */
2158 static int process_z_response(association *assoc, request *req, Z_APDU *res)
2159 {
2160  Z_GDU *gres = (Z_GDU *) odr_malloc(assoc->encode, sizeof(*gres));
2161  gres->which = Z_GDU_Z3950;
2162  gres->u.z3950 = res;
2163 
2164  return process_gdu_response(assoc, req, gres);
2165 }
2166 
2167 static char *get_vhost(Z_OtherInformation *otherInfo)
2168 {
2169  return yaz_oi_get_string_oid(&otherInfo, yaz_oid_userinfo_proxy, 1, 0);
2170 }
2171 
2172 /*
2173  * Handle init request.
2174  * At the moment, we don't check the options
2175  * anywhere else in the code - we just try not to do anything that would
2176  * break a naive client. We'll toss 'em into the association block when
2177  * we need them there.
2178  */
2180 {
2181  Z_InitRequest *req = reqb->apdu_request->u.initRequest;
2182  Z_APDU *apdu = zget_APDU(assoc->encode, Z_APDU_initResponse);
2183  Z_InitResponse *resp = apdu->u.initResponse;
2184  bend_initresult *binitres;
2185  char options[140];
2186  statserv_options_block *cb = 0; /* by default no control for backend */
2187 
2188  if (control_association(assoc, get_vhost(req->otherInfo), 1))
2189  cb = statserv_getcontrol(); /* got control block for backend */
2190 
2191  if (cb && assoc->backend)
2192  (*cb->bend_close)(assoc->backend);
2193 
2194  yaz_log(log_requestdetail, "Got initRequest");
2195  if (req->implementationId)
2196  yaz_log(log_requestdetail, "Id: %s",
2197  req->implementationId);
2198  if (req->implementationName)
2199  yaz_log(log_requestdetail, "Name: %s",
2200  req->implementationName);
2201  if (req->implementationVersion)
2202  yaz_log(log_requestdetail, "Version: %s",
2203  req->implementationVersion);
2204 
2205  assoc_init_reset(assoc);
2206 
2207  assoc->init->auth = req->idAuthentication;
2208  assoc->init->referenceId = req->referenceId;
2209 
2211  {
2212  Z_CharSetandLanguageNegotiation *negotiation =
2214  if (negotiation &&
2216  assoc->init->charneg_request = negotiation;
2217  }
2218 
2219  /* by default named_result_sets is 0 .. Enable it if client asks for it. */
2221  assoc->init->named_result_sets = 1;
2222 
2223  assoc->backend = 0;
2224  if (cb)
2225  {
2226  if (req->implementationVersion)
2227  yaz_log(log_requestdetail, "Config: %s",
2228  cb->configname);
2229 
2231 
2232  /* we have a backend control block, so call that init function */
2233  if (!(binitres = (*cb->bend_init)(assoc->init)))
2234  {
2235  yaz_log(YLOG_WARN, "Bad response from backend.");
2236  return 0;
2237  }
2238  assoc->backend = binitres->handle;
2239  }
2240  else
2241  {
2242  /* no backend. return error */
2243  binitres = (bend_initresult *)
2244  odr_malloc(assoc->encode, sizeof(*binitres));
2245  binitres->errstring = 0;
2247  iochan_settimeout(assoc->client_chan, 10);
2248  }
2249  if ((assoc->init->bend_sort))
2250  yaz_log(YLOG_DEBUG, "Sort handler installed");
2251  if ((assoc->init->bend_search))
2252  yaz_log(YLOG_DEBUG, "Search handler installed");
2253  if ((assoc->init->bend_present))
2254  yaz_log(YLOG_DEBUG, "Present handler installed");
2255  if ((assoc->init->bend_esrequest))
2256  yaz_log(YLOG_DEBUG, "ESRequest handler installed");
2257  if ((assoc->init->bend_delete))
2258  yaz_log(YLOG_DEBUG, "Delete handler installed");
2259  if ((assoc->init->bend_scan))
2260  yaz_log(YLOG_DEBUG, "Scan handler installed");
2261  if ((assoc->init->bend_segment))
2262  yaz_log(YLOG_DEBUG, "Segment handler installed");
2263 
2264  resp->referenceId = req->referenceId;
2265  *options = '\0';
2266  /* let's tell the client what we can do */
2268  {
2270  strcat(options, "srch");
2271  }
2273  {
2275  strcat(options, " prst");
2276  }
2277  if (ODR_MASK_GET(req->options, Z_Options_delSet) &&
2278  assoc->init->bend_delete)
2279  {
2281  strcat(options, " del");
2282  }
2284  assoc->init->bend_esrequest)
2285  {
2287  strcat(options, " extendedServices");
2288  }
2290  && assoc->init->named_result_sets)
2291  {
2293  strcat(options, " namedresults");
2294  }
2295  if (ODR_MASK_GET(req->options, Z_Options_scan) && assoc->init->bend_scan)
2296  {
2298  strcat(options, " scan");
2299  }
2301  {
2303  strcat(options, " concurrop");
2304  }
2305  if (ODR_MASK_GET(req->options, Z_Options_sort) && assoc->init->bend_sort)
2306  {
2308  strcat(options, " sort");
2309  }
2310 
2312  {
2314 
2315  if (!assoc->init->charneg_response)
2316  {
2317  if (assoc->init->query_charset)
2318  {
2320  assoc->encode, assoc->init->query_charset, 0,
2321  assoc->init->records_in_same_charset);
2322  }
2323  else
2324  {
2325  yaz_log(YLOG_WARN, "default query_charset not defined by backend");
2326  }
2327  }
2328  if (assoc->init->charneg_response
2329  && (p0=yaz_oi_update(&resp->otherInfo, assoc->encode, NULL, 0, 0)))
2330  {
2333  assoc->init->charneg_response;
2335  strcat(options, " negotiation");
2336  }
2337  }
2340 
2342  {
2344  assoc->version = 1; /* 1 & 2 are equivalent */
2345  }
2347  {
2349  assoc->version = 2;
2350  }
2352  {
2354  assoc->version = 3;
2355  }
2356 
2357  yaz_log(log_requestdetail, "Negotiated to v%d: %s", assoc->version, options);
2358 
2359  if (*req->maximumRecordSize < assoc->maximumRecordSize)
2361 
2362  if (*req->preferredMessageSize < assoc->preferredMessageSize)
2364 
2365  resp->preferredMessageSize =
2366  odr_intdup(assoc->encode, assoc->preferredMessageSize);
2367  resp->maximumRecordSize =
2368  odr_intdup(assoc->encode, assoc->maximumRecordSize);
2369 
2370  resp->implementationId = odr_prepend(assoc->encode,
2371  assoc->init->implementation_id,
2372  resp->implementationId);
2373 
2374  resp->implementationVersion = odr_prepend(assoc->encode,
2375  assoc->init->implementation_version,
2376  resp->implementationVersion);
2377 
2378  resp->implementationName = odr_prepend(assoc->encode,
2379  assoc->init->implementation_name,
2380  odr_prepend(assoc->encode, "GFS", resp->implementationName));
2381 
2382  if (binitres->errcode)
2383  {
2384  assoc->state = ASSOC_DEAD;
2385  resp->userInformationField =
2386  init_diagnostics(assoc->encode, binitres->errcode,
2387  binitres->errstring);
2388  *resp->result = 0;
2389  }
2390  else
2391  assoc->state = ASSOC_UP;
2392 
2393  if (log_request)
2394  {
2395  if (!req->idAuthentication)
2396  yaz_log(log_request, "Auth none");
2398  {
2399  const char *open = req->idAuthentication->u.open;
2400  const char *slash = strchr(open, '/');
2401  int len;
2402  if (slash)
2403  len = slash - open;
2404  else
2405  len = strlen(open);
2406  yaz_log(log_request, "Auth open %.*s", len, open);
2407  }
2409  {
2410  const char *user = req->idAuthentication->u.idPass->userId;
2411  const char *group = req->idAuthentication->u.idPass->groupId;
2412  yaz_log(log_request, "Auth idPass %s %s",
2413  user ? user : "-", group ? group : "-");
2414  }
2415  else if (req->idAuthentication->which
2417  {
2418  yaz_log(log_request, "Auth anonymous");
2419  }
2420  else
2421  {
2422  yaz_log(log_request, "Auth other");
2423  }
2424  }
2425  if (log_request)
2426  {
2427  WRBUF wr = wrbuf_alloc();
2428  wrbuf_printf(wr, "Init ");
2429  if (binitres->errcode)
2430  wrbuf_printf(wr, "ERROR %d", binitres->errcode);
2431  else
2432  wrbuf_printf(wr, "OK -");
2433  wrbuf_printf(wr, " ID:%s Name:%s Version:%s",
2434  (req->implementationId ? req->implementationId :"-"),
2435  (req->implementationName ?
2436  req->implementationName : "-"),
2437  (req->implementationVersion ?
2438  req->implementationVersion : "-")
2439  );
2440  yaz_log(log_request, "%s", wrbuf_cstr(wr));
2441  wrbuf_destroy(wr);
2442  }
2443  return apdu;
2444 }
2445 
2446 /*
2447  * Set the specified `errcode' and `errstring' into a UserInfo-1
2448  * external to be returned to the client in accordance with Z35.90
2449  * Implementor Agreement 5 (Returning diagnostics in an InitResponse):
2450  * http://lcweb.loc.gov/z3950/agency/agree/initdiag.html
2451  */
2452 static Z_External *init_diagnostics(ODR odr, int error, const char *addinfo)
2453 {
2454  yaz_log(log_requestdetail, "[%d] %s%s%s", error, diagbib1_str(error),
2455  addinfo ? " -- " : "", addinfo ? addinfo : "");
2456  return zget_init_diagnostics(odr, error, addinfo);
2457 }
2458 
2459 /*
2460  * nonsurrogate diagnostic record.
2461  */
2462 static Z_Records *diagrec(association *assoc, int error, char *addinfo)
2463 {
2464  Z_Records *rec = (Z_Records *) odr_malloc(assoc->encode, sizeof(*rec));
2465 
2466  yaz_log(log_requestdetail, "[%d] %s%s%s", error, diagbib1_str(error),
2467  addinfo ? " -- " : "", addinfo ? addinfo : "");
2468 
2469  rec->which = Z_Records_NSD;
2471  error, addinfo);
2472  return rec;
2473 }
2474 
2475 /*
2476  * surrogate diagnostic.
2477  */
2479  const char *dbname,
2480  int error, const char *addinfo)
2481 {
2482  yaz_log(log_requestdetail, "[%d] %s%s%s", error, diagbib1_str(error),
2483  addinfo ? " -- " : "", addinfo ? addinfo : "");
2484  return zget_surrogateDiagRec(assoc->encode, dbname, error, addinfo);
2485 }
2486 
2487 static Z_Records *pack_records(association *a, char *setname, Odr_int start,
2488  Odr_int *num, Z_RecordComposition *comp,
2489  Odr_int *next, Odr_int *pres,
2490  Z_ReferenceId *referenceId,
2491  Odr_oid *oid, int *errcode)
2492 {
2493  int recno, total_length = 0, dumped_records = 0;
2494  int toget = odr_int_to_int(*num);
2495  Z_Records *records =
2496  (Z_Records *) odr_malloc(a->encode, sizeof(*records));
2497  Z_NamePlusRecordList *reclist =
2498  (Z_NamePlusRecordList *) odr_malloc(a->encode, sizeof(*reclist));
2499 
2500  records->which = Z_Records_DBOSD;
2501  records->u.databaseOrSurDiagnostics = reclist;
2502  reclist->num_records = 0;
2503 
2504  if (toget < 0)
2506  else if (toget == 0)
2507  reclist->records = odr_nullval();
2508  else
2509  reclist->records = (Z_NamePlusRecord **)
2510  odr_malloc(a->encode, sizeof(*reclist->records) * toget);
2511 
2512  *pres = Z_PresentStatus_success;
2513  *num = 0;
2514  *next = 0;
2515 
2516  yaz_log(log_requestdetail, "Request to pack " ODR_INT_PRINTF "+%d %s", start, toget, setname);
2517  yaz_log(log_requestdetail, "pms=%d, mrs=%d", a->preferredMessageSize,
2518  a->maximumRecordSize);
2519  for (recno = odr_int_to_int(start); reclist->num_records < toget; recno++)
2520  {
2521  bend_fetch_rr freq;
2522  Z_NamePlusRecord *thisrec;
2523  int this_length = 0;
2524  /*
2525  * we get the number of bytes allocated on the stream before any
2526  * allocation done by the backend - this should give us a reasonable
2527  * idea of the total size of the data so far.
2528  */
2529  total_length = odr_total(a->encode) - dumped_records;
2530  freq.errcode = 0;
2531  freq.errstring = 0;
2532  freq.basename = 0;
2533  freq.len = 0;
2534  freq.record = 0;
2535  freq.last_in_set = 0;
2536  freq.setname = setname;
2537  freq.surrogate_flag = 0;
2538  freq.number = recno;
2539  freq.comp = comp;
2540  freq.request_format = oid;
2541  freq.output_format = 0;
2542  freq.stream = a->encode;
2543  freq.print = a->print;
2544  freq.referenceId = referenceId;
2545  freq.schema = 0;
2546 
2547  retrieve_fetch(a, &freq);
2548 
2549  *next = freq.last_in_set ? 0 : recno + 1;
2550 
2551  if (freq.errcode)
2552  {
2553  if (!freq.surrogate_flag) /* non-surrogate diagnostic i.e. global */
2554  {
2555  char s[20];
2556  *pres = Z_PresentStatus_failure;
2557  /* for 'present request out of range',
2558  set addinfo to record position if not set */
2560  freq.errstring == 0)
2561  {
2562  sprintf(s, "%d", recno);
2563  freq.errstring = s;
2564  }
2565  if (errcode)
2566  *errcode = freq.errcode;
2567  return diagrec(a, freq.errcode, freq.errstring);
2568  }
2569  reclist->records[reclist->num_records] =
2570  surrogatediagrec(a, freq.basename, freq.errcode,
2571  freq.errstring);
2572  reclist->num_records++;
2573  continue;
2574  }
2575  if (freq.record == 0) /* no error and no record ? */
2576  {
2577  *pres = Z_PresentStatus_partial_4;
2578  *next = 0; /* signal end-of-set and stop */
2579  break;
2580  }
2581  if (freq.len >= 0)
2582  this_length = freq.len;
2583  else
2584  this_length = odr_total(a->encode) - total_length - dumped_records;
2585  yaz_log(YLOG_DEBUG, " fetched record, len=%d, total=%d dumped=%d",
2586  this_length, total_length, dumped_records);
2587  if (a->preferredMessageSize > 0 &&
2588  this_length + total_length > a->preferredMessageSize)
2589  {
2590  /* record is small enough, really */
2591  if (this_length <= a->preferredMessageSize && recno > start)
2592  {
2593  yaz_log(log_requestdetail, " Dropped last normal-sized record");
2594  *pres = Z_PresentStatus_partial_2;
2595  if (*next > 0)
2596  (*next)--;
2597  break;
2598  }
2599  /* record can only be fetched by itself */
2600  if (this_length < a->maximumRecordSize)
2601  {
2602  yaz_log(log_requestdetail, " Record > prefmsgsz");
2603  if (toget > 1)
2604  {
2605  yaz_log(YLOG_DEBUG, " Dropped it");
2606  reclist->records[reclist->num_records] =
2608  a, freq.basename,
2610  reclist->num_records++;
2611  dumped_records += this_length;
2612  continue;
2613  }
2614  }
2615  else /* too big entirely */
2616  {
2617  yaz_log(log_requestdetail, "Record > maxrcdsz "
2618  "this=%d max=%d",
2619  this_length, a->maximumRecordSize);
2620  reclist->records[reclist->num_records] =
2622  a, freq.basename,
2624  reclist->num_records++;
2625  dumped_records += this_length;
2626  continue;
2627  }
2628  }
2629 
2630  if (!(thisrec = (Z_NamePlusRecord *)
2631  odr_malloc(a->encode, sizeof(*thisrec))))
2632  return 0;
2633  thisrec->databaseName = odr_strdup_null(a->encode, freq.basename);
2635 
2636  if (!freq.output_format)
2637  {
2638  yaz_log(YLOG_WARN, "bend_fetch output_format not set");
2639  return 0;
2640  }
2641  thisrec->u.databaseRecord = z_ext_record_oid(
2642  a->encode, freq.output_format, freq.record, freq.len);
2643  if (!thisrec->u.databaseRecord)
2644  return 0;
2645  reclist->records[reclist->num_records] = thisrec;
2646  reclist->num_records++;
2647  if (freq.last_in_set)
2648  break;
2649  }
2650  *num = reclist->num_records;
2651  return records;
2652 }
2653 
2655 {
2657  bend_search_rr *bsrr =
2658  (bend_search_rr *)nmem_malloc(reqb->request_mem, sizeof(*bsrr));
2659 
2660  yaz_log(log_requestdetail, "Got SearchRequest.");
2661  bsrr->association = assoc;
2662  bsrr->referenceId = req->referenceId;
2663  bsrr->srw_sortKeys = 0;
2664  bsrr->srw_setname = 0;
2665  bsrr->srw_setnameIdleTime = 0;
2666  bsrr->estimated_hit_count = 0;
2667  bsrr->partial_resultset = 0;
2668  bsrr->extra_args = 0;
2669  bsrr->extra_response_data = 0;
2670 
2671  yaz_log(log_requestdetail, "ResultSet '%s'", req->resultSetName);
2672  if (req->databaseNames)
2673  {
2674  int i;
2675  for (i = 0; i < req->num_databaseNames; i++)
2676  yaz_log(log_requestdetail, "Database '%s'", req->databaseNames[i]);
2677  }
2678 
2680 
2681  if (assoc->init->bend_search)
2682  {
2683  bsrr->setname = req->resultSetName;
2684  bsrr->replace_set = *req->replaceIndicator;
2685  bsrr->num_bases = req->num_databaseNames;
2686  bsrr->basenames = req->databaseNames;
2687  bsrr->query = req->query;
2688  bsrr->stream = assoc->encode;
2689  nmem_transfer(odr_getmem(bsrr->stream), reqb->request_mem);
2690  bsrr->decode = assoc->decode;
2691  bsrr->print = assoc->print;
2692  bsrr->hits = 0;
2693  bsrr->errcode = 0;
2694  bsrr->errstring = NULL;
2695  bsrr->search_info = NULL;
2696  bsrr->search_input = req->otherInfo;
2697  bsrr->present_number = *req->mediumSetPresentNumber;
2698 
2699  if (assoc->server && assoc->server->cql_transform
2700  && req->query->which == Z_Query_type_104
2701  && req->query->u.type_104->which == Z_External_CQL)
2702  {
2703  /* have a CQL query and a CQL to PQF transform .. */
2704  int srw_errcode =
2705  cql2pqf(bsrr->stream, req->query->u.type_104->u.cql,
2706  assoc->server->cql_transform, bsrr->query,
2707  &bsrr->srw_sortKeys);
2708  if (srw_errcode)
2709  bsrr->errcode = yaz_diag_srw_to_bib1(srw_errcode);
2710  }
2711 
2712  if (assoc->server && assoc->server->ccl_transform
2713  && req->query->which == Z_Query_type_2) /*CCL*/
2714  {
2715  /* have a CCL query and a CCL to PQF transform .. */
2716  int srw_errcode =
2717  ccl2pqf(bsrr->stream, req->query->u.type_2,
2718  assoc->server->ccl_transform, bsrr);
2719  if (srw_errcode)
2720  bsrr->errcode = yaz_diag_srw_to_bib1(srw_errcode);
2721  }
2722 
2723  if (!bsrr->errcode)
2724  (assoc->init->bend_search)(assoc->backend, bsrr);
2725  }
2726  else
2727  {
2728  /* FIXME - make a diagnostic for it */
2729  yaz_log(YLOG_WARN,"Search not supported ?!?!");
2730  }
2731  return response_searchRequest(assoc, reqb, bsrr);
2732 }
2733 
2734 /*
2735  * Prepare a searchresponse based on the backend results. We probably want
2736  * to look at making the fetching of records nonblocking as well, but
2737  * so far, we'll keep things simple.
2738  * If bsrt is null, that means we're called in response to a communications
2739  * event, and we'll have to get the response for ourselves.
2740  */
2742  bend_search_rr *bsrt)
2743 {
2745  Z_APDU *apdu = (Z_APDU *)odr_malloc(assoc->encode, sizeof(*apdu));
2747  odr_malloc(assoc->encode, sizeof(*resp));
2748  Odr_int *nulint = odr_intdup(assoc->encode, 0);
2749  Odr_int *next = odr_intdup(assoc->encode, 0);
2751  Odr_int returnedrecs = 0;
2752 
2753  apdu->which = Z_APDU_searchResponse;
2754  apdu->u.searchResponse = resp;
2755  resp->referenceId = req->referenceId;
2756  resp->additionalSearchInfo = 0;
2757  resp->otherInfo = 0;
2758  if (!bsrt)
2759  {
2760  yaz_log(YLOG_FATAL, "Bad result from backend");
2761  return 0;
2762  }
2763  else if (bsrt->errcode)
2764  {
2765  resp->records = diagrec(assoc, bsrt->errcode, bsrt->errstring);
2766  resp->resultCount = nulint;
2767  resp->numberOfRecordsReturned = nulint;
2768  resp->nextResultSetPosition = nulint;
2769  resp->searchStatus = odr_booldup(assoc->encode, 0);
2770  resp->resultSetStatus = none;
2771  resp->presentStatus = 0;
2772  }
2773  else
2774  {
2775  bool_t *sr = odr_booldup(assoc->encode, 1);
2776  Odr_int *toget = odr_intdup(assoc->encode, 0);
2777  Z_RecordComposition comp, *compp = 0;
2778 
2779  yaz_log(log_requestdetail, "resultCount: " ODR_INT_PRINTF, bsrt->hits);
2780 
2781  resp->records = 0;
2782  resp->resultCount = &bsrt->hits;
2783 
2784  comp.which = Z_RecordComp_simple;
2785  /* how many records does the user agent want, then? */
2786  if (bsrt->hits < 0)
2787  *toget = 0;
2788  else if (bsrt->hits <= *req->smallSetUpperBound)
2789  {
2790  *toget = bsrt->hits;
2791  if ((comp.u.simple = req->smallSetElementSetNames))
2792  compp = &comp;
2793  }
2794  else if (bsrt->hits < *req->largeSetLowerBound)
2795  {
2796  *toget = *req->mediumSetPresentNumber;
2797  if (*toget > bsrt->hits)
2798  *toget = bsrt->hits;
2799  if ((comp.u.simple = req->mediumSetElementSetNames))
2800  compp = &comp;
2801  }
2802  else
2803  *toget = 0;
2804 
2805  if (*toget && !resp->records)
2806  {
2807  Odr_int *presst = odr_intdup(assoc->encode, 0);
2808  /* Call bend_present if defined */
2809  if (assoc->init->bend_present)
2810  {
2811  bend_present_rr *bprr = (bend_present_rr *)
2812  nmem_malloc(reqb->request_mem, sizeof(*bprr));
2813  bprr->setname = req->resultSetName;
2814  bprr->start = 1;
2815  bprr->number = odr_int_to_int(*toget);
2816  bprr->format = req->preferredRecordSyntax;
2817  bprr->comp = compp;
2818  bprr->referenceId = req->referenceId;
2819  bprr->stream = assoc->encode;
2820  bprr->print = assoc->print;
2821  bprr->association = assoc;
2822  bprr->errcode = 0;
2823  bprr->errstring = NULL;
2824  (*assoc->init->bend_present)(assoc->backend, bprr);
2825 
2826  if (bprr->errcode)
2827  {
2828  resp->records = diagrec(assoc, bprr->errcode, bprr->errstring);
2830  }
2831  }
2832 
2833  if (!resp->records)
2834  resp->records = pack_records(
2835  assoc, req->resultSetName, 1,
2836  toget, compp, next, presst, req->referenceId,
2837  req->preferredRecordSyntax, NULL);
2838  if (!resp->records)
2839  return 0;
2840  resp->numberOfRecordsReturned = toget;
2841  returnedrecs = *toget;
2842  resp->presentStatus = presst;
2843  }
2844  else
2845  {
2846  if (*resp->resultCount)
2847  *next = 1;
2848  resp->numberOfRecordsReturned = nulint;
2849  resp->presentStatus = 0;
2850  }
2851  resp->nextResultSetPosition = next;
2852  resp->searchStatus = sr;
2853  resp->resultSetStatus = 0;
2854  if (bsrt->estimated_hit_count)
2855  {
2856  resp->resultSetStatus = odr_intdup(assoc->encode,
2858  }
2859  else if (bsrt->partial_resultset)
2860  {
2861  resp->resultSetStatus = odr_intdup(assoc->encode,
2863  }
2864  }
2865  resp->additionalSearchInfo = bsrt->search_info;
2866 
2867  if (log_request)
2868  {
2869  int i;
2870  WRBUF wr = wrbuf_alloc();
2871 
2872  for (i = 0 ; i < req->num_databaseNames; i++)
2873  {
2874  if (i)
2875  wrbuf_printf(wr, "+");
2876  wrbuf_puts(wr, req->databaseNames[i]);
2877  }
2878  wrbuf_printf(wr, " ");
2879 
2880  if (bsrt->errcode)
2881  wrbuf_printf(wr, "ERROR %d", bsrt->errcode);
2882  else
2883  wrbuf_printf(wr, "OK " ODR_INT_PRINTF, bsrt->hits);
2884  wrbuf_printf(wr, " %s 1+" ODR_INT_PRINTF " ",
2885  req->resultSetName, returnedrecs);
2886  yaz_query_to_wrbuf(wr, req->query);
2887 
2888  yaz_log(log_request, "Search %s", wrbuf_cstr(wr));
2889  wrbuf_destroy(wr);
2890  }
2891  return apdu;
2892 }
2893 
2894 /*
2895  * Maybe we got a little over-friendly when we designed bend_fetch to
2896  * get only one record at a time. Some backends can optimise multiple-record
2897  * fetches, and at any rate, there is some overhead involved in
2898  * all that selecting and hopping around. Problem is, of course, that the
2899  * frontend can't know ahead of time how many records it'll need to
2900  * fill the negotiated PDU size. Annoying. Segmentation or not, Z/SR
2901  * is downright lousy as a bulk data transfer protocol.
2902  *
2903  * To start with, we'll do the fetching of records from the backend
2904  * in one operation: To save some trips in and out of the event-handler,
2905  * and to simplify the interface to pack_records. At any rate, asynch
2906  * operation is more fun in operations that have an unpredictable execution
2907  * speed - which is normally more true for search than for present.
2908  */
2910 {
2912  Z_APDU *apdu;
2913  Z_PresentResponse *resp;
2914  Odr_int *next;
2915  Odr_int *num;
2916  int errcode = 0;
2917 
2918  yaz_log(log_requestdetail, "Got PresentRequest.");
2919 
2920  resp = (Z_PresentResponse *)odr_malloc(assoc->encode, sizeof(*resp));
2921  resp->records = 0;
2922  resp->presentStatus = odr_intdup(assoc->encode, 0);
2923  if (assoc->init->bend_present)
2924  {
2925  bend_present_rr *bprr = (bend_present_rr *)
2926  nmem_malloc(reqb->request_mem, sizeof(*bprr));
2927  bprr->setname = req->resultSetId;
2928  bprr->start = odr_int_to_int(*req->resultSetStartPoint);
2930  bprr->format = req->preferredRecordSyntax;
2931  bprr->comp = req->recordComposition;
2932  bprr->referenceId = req->referenceId;
2933  bprr->stream = assoc->encode;
2934  bprr->print = assoc->print;
2935  bprr->association = assoc;
2936  bprr->errcode = 0;
2937  bprr->errstring = NULL;
2938  (*assoc->init->bend_present)(assoc->backend, bprr);
2939 
2940  if (bprr->errcode)
2941  {
2942  resp->records = diagrec(assoc, bprr->errcode, bprr->errstring);
2944  errcode = bprr->errcode;
2945  }
2946  }
2947  apdu = (Z_APDU *)odr_malloc(assoc->encode, sizeof(*apdu));
2948  next = odr_intdup(assoc->encode, 0);
2949  num = odr_intdup(assoc->encode, 0);
2950 
2951  apdu->which = Z_APDU_presentResponse;
2952  apdu->u.presentResponse = resp;
2953  resp->referenceId = req->referenceId;
2954  resp->otherInfo = 0;
2955 
2956  if (!resp->records)
2957  {
2958  *num = *req->numberOfRecordsRequested;
2959  resp->records =
2960  pack_records(assoc, req->resultSetId, *req->resultSetStartPoint,
2961  num, req->recordComposition, next,
2962  resp->presentStatus,
2964  &errcode);
2965  }
2966  if (log_request)
2967  {
2968  WRBUF wr = wrbuf_alloc();
2969  wrbuf_printf(wr, "Present ");
2970 
2971  if (*resp->presentStatus == Z_PresentStatus_failure)
2972  wrbuf_printf(wr, "ERROR %d ", errcode);
2973  else if (*resp->presentStatus == Z_PresentStatus_success)
2974  wrbuf_printf(wr, "OK - ");
2975  else
2976  wrbuf_printf(wr, "Partial " ODR_INT_PRINTF " - ",
2977  *resp->presentStatus);
2978 
2979  wrbuf_printf(wr, " %s " ODR_INT_PRINTF "+" ODR_INT_PRINTF " ",
2980  req->resultSetId, *req->resultSetStartPoint,
2981  *req->numberOfRecordsRequested);
2982  yaz_log(log_request, "%s", wrbuf_cstr(wr) );
2983  wrbuf_destroy(wr);
2984  }
2985  if (!resp->records)
2986  return 0;
2987  resp->numberOfRecordsReturned = num;
2988  resp->nextResultSetPosition = next;
2989 
2990  return apdu;
2991 }
2992 
2993 /*
2994  * Scan was implemented rather in a hurry, and with support for only the basic
2995  * elements of the service in the backend API. Suggestions are welcome.
2996  */
2998 {
2999  Z_ScanRequest *req = reqb->apdu_request->u.scanRequest;
3000  Z_APDU *apdu = (Z_APDU *)odr_malloc(assoc->encode, sizeof(*apdu));
3001  Z_ScanResponse *res = (Z_ScanResponse *)
3002  odr_malloc(assoc->encode, sizeof(*res));
3003  Odr_int *scanStatus = odr_intdup(assoc->encode, Z_Scan_failure);
3004  Odr_int *numberOfEntriesReturned = odr_intdup(assoc->encode, 0);
3005  Z_ListEntries *ents = (Z_ListEntries *)
3006  odr_malloc(assoc->encode, sizeof(*ents));
3007  Z_DiagRecs *diagrecs_p = NULL;
3008  bend_scan_rr *bsrr = (bend_scan_rr *)
3009  odr_malloc(assoc->encode, sizeof(*bsrr));
3010  struct scan_entry *save_entries;
3011  int step_size = 0;
3012 
3013  yaz_log(log_requestdetail, "Got ScanRequest");
3014 
3015  apdu->which = Z_APDU_scanResponse;
3016  apdu->u.scanResponse = res;
3017  res->referenceId = req->referenceId;
3018 
3019  /* if step is absent, set it to 0 */
3020  if (req->stepSize)
3021  step_size = odr_int_to_int(*req->stepSize);
3022 
3023  res->stepSize = 0;
3024  res->scanStatus = scanStatus;
3025  res->numberOfEntriesReturned = numberOfEntriesReturned;
3026  res->positionOfTerm = 0;
3027  res->entries = ents;
3028  ents->num_entries = 0;
3029  ents->entries = NULL;
3030  ents->num_nonsurrogateDiagnostics = 0;
3031  ents->nonsurrogateDiagnostics = NULL;
3032  res->attributeSet = 0;
3033  res->otherInfo = 0;
3034 
3035  if (req->databaseNames)
3036  {
3037  int i;
3038  for (i = 0; i < req->num_databaseNames; i++)
3039  yaz_log(log_requestdetail, "Database '%s'", req->databaseNames[i]);
3040  }
3041  bsrr->scanClause = 0;
3042  bsrr->errcode = 0;
3043  bsrr->errstring = 0;
3044  bsrr->num_bases = req->num_databaseNames;
3045  bsrr->basenames = req->databaseNames;
3047  bsrr->term = req->termListAndStartPoint;
3048  bsrr->referenceId = req->referenceId;
3049  bsrr->stream = assoc->encode;
3050  bsrr->print = assoc->print;
3051  bsrr->step_size = &step_size;
3052  bsrr->setname = yaz_oi_get_string_oid(&req->otherInfo,
3054  bsrr->entries = 0;
3055  bsrr->extra_args = 0;
3056  bsrr->extra_response_data = 0;
3057  /* For YAZ 2.0 and earlier it was the backend handler that
3058  initialized entries (member display_term did not exist)
3059  YAZ 2.0 and later sets 'entries' and initialize all members
3060  including 'display_term'. If YAZ 2.0 or later sees that
3061  entries was modified - we assume that it is an old handler and
3062  that 'display_term' is _not_ set.
3063  */
3064  if (bsrr->num_entries > 0)
3065  {
3066  int i;
3067  bsrr->entries = (struct scan_entry *)
3068  odr_malloc(assoc->decode, sizeof(*bsrr->entries) *
3069  bsrr->num_entries);
3070  for (i = 0; i<bsrr->num_entries; i++)
3071  {
3072  bsrr->entries[i].term = 0;
3073  bsrr->entries[i].occurrences = 0;
3074  bsrr->entries[i].errcode = 0;
3075  bsrr->entries[i].errstring = 0;
3076  bsrr->entries[i].display_term = 0;
3077  }
3078  }
3079  save_entries = bsrr->entries; /* save it so we can compare later */
3080 
3081  bsrr->attributeset = req->attributeSet;
3083  bsrr->attributeset);
3086 
3087  ((int (*)(void *, bend_scan_rr *))
3088  (*assoc->init->bend_scan))(assoc->backend, bsrr);
3089 
3090  if (bsrr->errcode)
3091  diagrecs_p = zget_DiagRecs(assoc->encode,
3092  bsrr->errcode, bsrr->errstring);
3093  else
3094  {
3095  int i;
3096  Z_Entry **tab = (Z_Entry **)
3097  odr_malloc(assoc->encode, sizeof(*tab) * bsrr->num_entries);
3098 
3099  if (bsrr->status == BEND_SCAN_PARTIAL)
3100  *scanStatus = Z_Scan_partial_5;
3101  else
3102  *scanStatus = Z_Scan_success;
3103  res->stepSize = odr_intdup(assoc->encode, step_size);
3104  ents->entries = tab;
3105  ents->num_entries = bsrr->num_entries;
3107  ents->num_entries);
3108  res->positionOfTerm = odr_intdup(assoc->encode, bsrr->term_position);
3109  for (i = 0; i < bsrr->num_entries; i++)
3110  {
3111  Z_Entry *e;
3112  Z_TermInfo *t;
3113  Odr_oct *o;
3114 
3115  tab[i] = e = (Z_Entry *)odr_malloc(assoc->encode, sizeof(*e));
3116  if (bsrr->entries[i].occurrences >= 0)
3117  {
3118  e->which = Z_Entry_termInfo;
3119  e->u.termInfo = t = (Z_TermInfo *)
3120  odr_malloc(assoc->encode, sizeof(*t));
3121  t->suggestedAttributes = 0;
3122  t->displayTerm = 0;
3123  if (save_entries == bsrr->entries &&
3124  bsrr->entries[i].display_term)
3125  {
3126  /* the entries was _not_ set by the handler. So it's
3127  safe to test for new member display_term. It is
3128  NULL'ed by us.
3129  */
3130  t->displayTerm = odr_strdup(assoc->encode,
3131  bsrr->entries[i].display_term);
3132  }
3133  t->alternativeTerm = 0;
3134  t->byAttributes = 0;
3135  t->otherTermInfo = 0;
3136  t->globalOccurrences = &bsrr->entries[i].occurrences;
3137  t->term = (Z_Term *)
3138  odr_malloc(assoc->encode, sizeof(*t->term));
3139  t->term->which = Z_Term_general;
3140  t->term->u.general = o =
3141  (Odr_oct *)odr_malloc(assoc->encode, sizeof(Odr_oct));
3142  o->buf = (unsigned char *)
3143  odr_malloc(assoc->encode, o->len = o->size =
3144  strlen(bsrr->entries[i].term));
3145  memcpy(o->buf, bsrr->entries[i].term, o->len);
3146  yaz_log(YLOG_DEBUG, " term #%d: '%s' (" ODR_INT_PRINTF ")", i,
3147  bsrr->entries[i].term, bsrr->entries[i].occurrences);
3148  }
3149  else
3150  {
3151  Z_DiagRecs *drecs = zget_DiagRecs(assoc->encode,
3152  bsrr->entries[i].errcode,
3153  bsrr->entries[i].errstring);
3154  assert(drecs->num_diagRecs == 1);
3156  assert(drecs->diagRecs[0]);
3157  e->u.surrogateDiagnostic = drecs->diagRecs[0];
3158  }
3159  }
3160  }
3161  if (diagrecs_p)
3162  {
3163  ents->num_nonsurrogateDiagnostics = diagrecs_p->num_diagRecs;
3164  ents->nonsurrogateDiagnostics = diagrecs_p->diagRecs;
3165  }
3166  if (log_request)
3167  {
3168  int i;
3169  WRBUF wr = wrbuf_alloc();
3170  wrbuf_printf(wr, "Scan ");
3171  for (i = 0 ; i < req->num_databaseNames; i++)
3172  {
3173  if (i)
3174  wrbuf_printf(wr, "+");
3175  wrbuf_puts(wr, req->databaseNames[i]);
3176  }
3177 
3178  wrbuf_printf(wr, " ");
3179 
3180  if (bsrr->errcode)
3181  wr_diag(wr, bsrr->errcode, bsrr->errstring);
3182  else
3183  wrbuf_printf(wr, "OK");
3184 
3185  wrbuf_printf(wr, " " ODR_INT_PRINTF " - " ODR_INT_PRINTF "+"
3188  *res->numberOfEntriesReturned : 0,
3190  *req->preferredPositionInResponse : 1),
3191  *req->numberOfTermsRequested,
3192  (res->stepSize ? *res->stepSize : 1));
3193 
3194  if (bsrr->setname)
3195  wrbuf_printf(wr, "+%s", bsrr->setname);
3196 
3197  wrbuf_printf(wr, " ");
3199  bsrr->attributeset);
3200  yaz_log(log_request, "%s", wrbuf_cstr(wr) );
3201  wrbuf_destroy(wr);
3202  }
3203  return apdu;
3204 }
3205 
3207 {
3208  int i;
3209  Z_SortRequest *req = reqb->apdu_request->u.sortRequest;
3210  Z_SortResponse *res = (Z_SortResponse *)
3211  odr_malloc(assoc->encode, sizeof(*res));
3212  bend_sort_rr *bsrr = (bend_sort_rr *)
3213  odr_malloc(assoc->encode, sizeof(*bsrr));
3214 
3215  Z_APDU *apdu = (Z_APDU *)odr_malloc(assoc->encode, sizeof(*apdu));
3216 
3217  yaz_log(log_requestdetail, "Got SortRequest.");
3218 
3220  for (i=0;i<req->num_inputResultSetNames;i++)
3221  yaz_log(log_requestdetail, "Input resultset: '%s'",
3222  req->inputResultSetNames[i]);
3223  bsrr->input_setnames = req->inputResultSetNames;
3224  bsrr->referenceId = req->referenceId;
3225  bsrr->output_setname = req->sortedResultSetName;
3226  yaz_log(log_requestdetail, "Output resultset: '%s'",
3227  req->sortedResultSetName);
3228  bsrr->sort_sequence = req->sortSequence;
3229  /*FIXME - dump those sequences too */
3230  bsrr->stream = assoc->encode;
3231  bsrr->print = assoc->print;
3232 
3234  bsrr->errcode = 0;
3235  bsrr->errstring = 0;
3236 
3237  (*assoc->init->bend_sort)(assoc->backend, bsrr);
3238 
3239  res->referenceId = bsrr->referenceId;
3240  res->sortStatus = odr_intdup(assoc->encode, bsrr->sort_status);
3241  res->resultSetStatus = 0;
3242  if (bsrr->errcode)
3243  {
3244  Z_DiagRecs *dr = zget_DiagRecs(assoc->encode,
3245  bsrr->errcode, bsrr->errstring);
3246  res->diagnostics = dr->diagRecs;
3247  res->num_diagnostics = dr->num_diagRecs;
3248  }
3249  else
3250  {
3251  res->num_diagnostics = 0;
3252  res->diagnostics = 0;
3253  }
3254  res->resultCount = 0;
3255  res->otherInfo = 0;
3256 
3257  apdu->which = Z_APDU_sortResponse;
3258  apdu->u.sortResponse = res;
3259  if (log_request)
3260  {
3261  WRBUF wr = wrbuf_alloc();
3262  wrbuf_printf(wr, "Sort ");
3263  if (bsrr->errcode)
3264  wrbuf_printf(wr, " ERROR %d", bsrr->errcode);
3265  else
3266  wrbuf_printf(wr, "OK -");
3267  wrbuf_printf(wr, " (");
3268  for (i = 0; i<req->num_inputResultSetNames; i++)
3269  {
3270  if (i)
3271  wrbuf_printf(wr, "+");
3272  wrbuf_puts(wr, req->inputResultSetNames[i]);
3273  }
3274  wrbuf_printf(wr, ")->%s ",req->sortedResultSetName);
3275 
3276  yaz_log(log_request, "%s", wrbuf_cstr(wr) );
3277  wrbuf_destroy(wr);
3278  }
3279  return apdu;
3280 }
3281 
3283 {
3284  int i;
3288  odr_malloc(assoc->encode, sizeof(*res));
3289  bend_delete_rr *bdrr = (bend_delete_rr *)
3290  odr_malloc(assoc->encode, sizeof(*bdrr));
3291  Z_APDU *apdu = (Z_APDU *)odr_malloc(assoc->encode, sizeof(*apdu));
3292 
3293  yaz_log(log_requestdetail, "Got DeleteRequest.");
3294 
3295  bdrr->num_setnames = req->num_resultSetList;
3296  bdrr->setnames = req->resultSetList;
3297  for (i = 0; i<req->num_resultSetList; i++)
3298  yaz_log(log_requestdetail, "resultset: '%s'",
3299  req->resultSetList[i]);
3300  bdrr->stream = assoc->encode;
3301  bdrr->print = assoc->print;
3302  bdrr->function = odr_int_to_int(*req->deleteFunction);
3303  bdrr->referenceId = req->referenceId;
3304  bdrr->statuses = 0;
3305  if (bdrr->num_setnames > 0)
3306  {
3307  bdrr->statuses = (int*)
3308  odr_malloc(assoc->encode, sizeof(*bdrr->statuses) *
3309  bdrr->num_setnames);
3310  for (i = 0; i < bdrr->num_setnames; i++)
3311  bdrr->statuses[i] = 0;
3312  }
3313  (*assoc->init->bend_delete)(assoc->backend, bdrr);
3314 
3315  res->referenceId = req->referenceId;
3316 
3318 
3319  res->deleteListStatuses = 0;
3320  if (bdrr->num_setnames > 0)
3321  {
3322  int i;
3324  odr_malloc(assoc->encode, sizeof(*res->deleteListStatuses));
3325  res->deleteListStatuses->num = bdrr->num_setnames;
3327  (Z_ListStatus **)
3328  odr_malloc(assoc->encode,
3329  sizeof(*res->deleteListStatuses->elements) *
3330  bdrr->num_setnames);
3331  for (i = 0; i<bdrr->num_setnames; i++)
3332  {
3333  res->deleteListStatuses->elements[i] =
3334  (Z_ListStatus *)
3335  odr_malloc(assoc->encode,
3336  sizeof(**res->deleteListStatuses->elements));
3337  res->deleteListStatuses->elements[i]->status =
3338  odr_intdup(assoc->encode, bdrr->statuses[i]);
3339  res->deleteListStatuses->elements[i]->id =
3340  odr_strdup(assoc->encode, bdrr->setnames[i]);
3341  }
3342  }
3343  res->numberNotDeleted = 0;
3344  res->bulkStatuses = 0;
3345  res->deleteMessage = 0;
3346  res->otherInfo = 0;
3347 
3349  apdu->u.deleteResultSetResponse = res;
3350  if (log_request)
3351  {
3352  WRBUF wr = wrbuf_alloc();
3353  wrbuf_printf(wr, "Delete ");
3354  if (bdrr->delete_status)
3355  wrbuf_printf(wr, "ERROR %d", bdrr->delete_status);
3356  else
3357  wrbuf_printf(wr, "OK -");
3358  for (i = 0; i<req->num_resultSetList; i++)
3359  wrbuf_printf(wr, " %s ", req->resultSetList[i]);
3360  yaz_log(log_request, "%s", wrbuf_cstr(wr) );
3361  wrbuf_destroy(wr);
3362  }
3363  return apdu;
3364 }
3365 
3366 static void process_close(association *assoc, request *reqb)
3367 {
3368  Z_Close *req = reqb->apdu_request->u.close;
3369  static char *reasons[] =
3370  {
3371  "finished",
3372  "shutdown",
3373  "systemProblem",
3374  "costLimit",
3375  "resources",
3376  "securityViolation",
3377  "protocolError",
3378  "lackOfActivity",
3379  "peerAbort",
3380  "unspecified"
3381  };
3382 
3383  yaz_log(log_requestdetail, "Got Close, reason %s, message %s",
3384  reasons[*req->closeReason], req->diagnosticInformation ?
3385  req->diagnosticInformation : "NULL");
3386  if (assoc->version < 3) /* to make do_force respond with close */
3387  assoc->version = 3;
3389  "Association terminated by client", reqb);
3390  yaz_log(log_request,"Close OK");
3391 }
3392 
3394 {
3395  bend_segment_rr req;
3396 
3397  req.segment = reqb->apdu_request->u.segmentRequest;
3398  req.stream = assoc->encode;
3399  req.decode = assoc->decode;
3400  req.print = assoc->print;
3401  req.association = assoc;
3402 
3403  (*assoc->init->bend_segment)(assoc->backend, &req);
3404 
3405  return 0;
3406 }
3407 
3409 {
3410  bend_esrequest_rr esrequest;
3411  const char *ext_name = "unknown";
3412 
3416 
3418 
3419  esrequest.esr = reqb->apdu_request->u.extendedServicesRequest;
3420  esrequest.stream = assoc->encode;
3421  esrequest.decode = assoc->decode;
3422  esrequest.print = assoc->print;
3423  esrequest.errcode = 0;
3424  esrequest.errstring = NULL;
3425  esrequest.association = assoc;
3426  esrequest.taskPackage = 0;
3427  esrequest.referenceId = req->referenceId;
3428 
3429  if (esrequest.esr && esrequest.esr->taskSpecificParameters)
3430  {
3431  switch(esrequest.esr->taskSpecificParameters->which)
3432  {
3433  case Z_External_itemOrder:
3434  ext_name = "ItemOrder"; break;
3435  case Z_External_update:
3436  ext_name = "Update"; break;
3437  case Z_External_update0:
3438  ext_name = "Update0"; break;
3439  case Z_External_ESAdmin:
3440  ext_name = "Admin"; break;
3441 
3442  }
3443  }
3444 
3445  (*assoc->init->bend_esrequest)(assoc->backend, &esrequest);
3446 
3447  resp->referenceId = req->referenceId;
3448 
3449  if (esrequest.errcode == -1)
3450  {
3451  /* Backend service indicates request will be processed */
3452  yaz_log(log_request, "Extended Service: %s (accepted)", ext_name);
3454  }
3455  else if (esrequest.errcode == 0)
3456  {
3457  /* Backend service indicates request will be processed */
3458  yaz_log(log_request, "Extended Service: %s (done)", ext_name);
3460  }
3461  else
3462  {
3463  Z_DiagRecs *diagRecs =
3464  zget_DiagRecs(assoc->encode, esrequest.errcode,
3465  esrequest.errstring);
3466  /* Backend indicates error, request will not be processed */
3467  yaz_log(log_request, "Extended Service: %s (failed)", ext_name);
3469  resp->num_diagnostics = diagRecs->num_diagRecs;
3470  resp->diagnostics = diagRecs->diagRecs;
3471  if (log_request)
3472  {
3473  WRBUF wr = wrbuf_alloc();
3474  wrbuf_diags(wr, resp->num_diagnostics, resp->diagnostics);
3475  yaz_log(log_request, "EsRequest %s", wrbuf_cstr(wr) );
3476  wrbuf_destroy(wr);
3477  }
3478 
3479  }
3480  /* Do something with the members of bend_extendedservice */
3481  if (esrequest.taskPackage)
3482  {
3483  resp->taskPackage = z_ext_record_oid(
3485  (const char *) esrequest.taskPackage, -1);
3486  }
3487  yaz_log(YLOG_DEBUG,"Send the result apdu");
3488  return apdu;
3489 }
3490 
3492 {
3493  if (assoc->state == ASSOC_DEAD)
3494  return 0; /* already marked as dead. Don't check I/O chan anymore */
3495 
3496  return iochan_is_alive(assoc->client_chan);
3497 }
3498 
3499 
3500 /*
3501  * Local variables:
3502  * c-basic-offset: 4
3503  * c-file-style: "Stroustrup"
3504  * indent-tabs-mode: nil
3505  * End:
3506  * vim: shiftwidth=4 tabstop=8 expandtab
3507  */
3508