YAZ  4.2.57
zoom-c.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  */
9 #if HAVE_CONFIG_H
10 #include <config.h>
11 #endif
12 
13 #include <assert.h>
14 #include <string.h>
15 #include <errno.h>
16 #include "zoom-p.h"
17 
18 #include <yaz/yaz-util.h>
19 #include <yaz/xmalloc.h>
20 #include <yaz/otherinfo.h>
21 #include <yaz/log.h>
22 #include <yaz/diagbib1.h>
23 #include <yaz/charneg.h>
24 #include <yaz/query-charset.h>
25 #include <yaz/snprintf.h>
26 #include <yaz/facet.h>
27 
28 #include <yaz/shptr.h>
29 
30 #if SHPTR
32 #endif
33 
34 static int log_api0 = 0;
35 static int log_details0 = 0;
36 
37 static void resultset_destroy(ZOOM_resultset r);
38 static zoom_ret do_write_ex(ZOOM_connection c, char *buf_out, int len_out);
39 
40 static void initlog(void)
41 {
42  static int log_level_initialized = 0;
43 
44  if (!log_level_initialized)
45  {
46  log_api0 = yaz_log_module_level("zoom");
47  log_details0 = yaz_log_module_level("zoomdetails");
48  log_level_initialized = 1;
49  }
50 }
51 
53 
55  const char *dset,
56  const char *addinfo, const char *addinfo2)
57 {
58  char *cp;
59 
60  xfree(c->addinfo);
61  c->addinfo = 0;
62  c->error = error;
63  if (!c->diagset || strcmp(dset, c->diagset))
64  {
65  xfree(c->diagset);
66  c->diagset = xstrdup(dset);
67  /* remove integer part from SRW diagset .. */
68  if ((cp = strrchr(c->diagset, '/')))
69  *cp = '\0';
70  }
71  if (addinfo && addinfo2)
72  {
73  c->addinfo = (char*) xmalloc(strlen(addinfo) + strlen(addinfo2) + 3);
74  strcpy(c->addinfo, addinfo);
75  strcat(c->addinfo, ": ");
76  strcat(c->addinfo, addinfo2);
77  }
78  else if (addinfo)
79  c->addinfo = xstrdup(addinfo);
80  if (error != ZOOM_ERROR_NONE)
81  {
82  yaz_log(c->log_api, "%p set_dset_error %s %s:%d %s %s",
83  c, c->host_port ? c->host_port : "<>", dset, error,
84  addinfo ? addinfo : "",
85  addinfo2 ? addinfo2 : "");
87  }
88 }
89 
90 int ZOOM_uri_to_code(const char *uri)
91 {
92  int code = 0;
93  const char *cp;
94  if ((cp = strrchr(uri, '/')))
95  code = atoi(cp+1);
96  return code;
97 }
98 
99 
100 
101 void ZOOM_set_error(ZOOM_connection c, int error, const char *addinfo)
102 {
103  ZOOM_set_dset_error(c, error, "ZOOM", addinfo, 0);
104 }
105 
107 {
108  /*
109  * If an error is tied to an operation then it's ok to clear: for
110  * example, a diagnostic returned from a search is cleared by a
111  * subsequent search. However, problems such as Connection Lost
112  * or Init Refused are not cleared, because they are not
113  * recoverable: doing another search doesn't help.
114  */
115 
117  switch (c->error)
118  {
119  case ZOOM_ERROR_CONNECT:
120  case ZOOM_ERROR_MEMORY:
121  case ZOOM_ERROR_DECODE:
123  case ZOOM_ERROR_INIT:
124  case ZOOM_ERROR_INTERNAL:
126  break;
127  default:
129  }
130 }
131 
133 {
134  switch(task->which)
135  {
136  case ZOOM_TASK_SEARCH:
137  yaz_log(YLOG_LOG, "search p=%p", task);
138  break;
139  case ZOOM_TASK_RETRIEVE:
140  yaz_log(YLOG_LOG, "retrieve p=%p", task);
141  break;
142  case ZOOM_TASK_CONNECT:
143  yaz_log(YLOG_LOG, "connect p=%p", task);
144  break;
145  case ZOOM_TASK_SCAN:
146  yaz_log(YLOG_LOG, "scan p=%p", task);
147  break;
148  }
149 }
150 
152 {
153  ZOOM_task task;
154  yaz_log(YLOG_LOG, "connection p=%p tasks", c);
155  for (task = c->tasks; task; task = task->next)
157 }
158 
160 {
161  ZOOM_task *taskp = &c->tasks;
162  while (*taskp)
163  taskp = &(*taskp)->next;
164  *taskp = (ZOOM_task) xmalloc(sizeof(**taskp));
165  (*taskp)->running = 0;
166  (*taskp)->which = which;
167  (*taskp)->next = 0;
168  clear_error(c);
169  return *taskp;
170 }
171 
173 {
174  return c->tasks ? 0 : 1;
175 }
176 
178 {
179  ZOOM_task task = (ZOOM_task) xmalloc(sizeof(*task));
180 
181  task->next = c->tasks;
182  c->tasks = task;
183 
184  task->running = 0;
185  task->which = which;
186  return task;
187 }
188 
190 {
191  ZOOM_task task = c->tasks;
192 
193  if (task)
194  {
195  c->tasks = task->next;
196  switch (task->which)
197  {
198  case ZOOM_TASK_SEARCH:
199  resultset_destroy(task->u.search.resultset);
200  xfree(task->u.search.syntax);
201  xfree(task->u.search.elementSetName);
202  break;
203  case ZOOM_TASK_RETRIEVE:
204  resultset_destroy(task->u.retrieve.resultset);
205  xfree(task->u.retrieve.syntax);
206  xfree(task->u.retrieve.elementSetName);
207  break;
208  case ZOOM_TASK_CONNECT:
209  break;
210  case ZOOM_TASK_SCAN:
211  ZOOM_scanset_destroy(task->u.scan.scan);
212  break;
213  case ZOOM_TASK_PACKAGE:
215  break;
216  case ZOOM_TASK_SORT:
217  resultset_destroy(task->u.sort.resultset);
218  ZOOM_query_destroy(task->u.sort.q);
219  break;
220  default:
221  assert(0);
222  }
223  xfree(task);
224 
225  if (!c->tasks)
226  {
228  ZOOM_connection_put_event(c, event);
229  }
230  }
231 }
232 
234 {
235  while (c->tasks)
237 }
238 
239 static void odr_wrbuf_write(ODR o, void *handle, int type,
240  const char *buf, int len)
241 {
242  WRBUF w = (WRBUF) handle;
243  wrbuf_write(w, buf, len);
244 }
245 
248 {
249  ZOOM_connection c = (ZOOM_connection) xmalloc(sizeof(*c));
250 
251  initlog();
252 
253  c->log_api = log_api0;
255 
256  yaz_log(c->log_api, "%p ZOOM_connection_create", c);
257 
258  c->proto = PROTO_Z3950;
259  c->cs = 0;
261  c->reconnect_ok = 0;
262  c->state = STATE_IDLE;
263  c->addinfo = 0;
264  c->diagset = 0;
266  c->buf_in = 0;
267  c->len_in = 0;
268  c->buf_out = 0;
269  c->len_out = 0;
270  c->resultsets = 0;
271 
273 
274  c->host_port = 0;
275  c->proxy = 0;
276  c->tproxy = 0;
277 
278  c->charset = c->lang = 0;
279 
280  c->cookie_out = 0;
281  c->cookie_in = 0;
282  c->client_IP = 0;
283  c->tasks = 0;
284 
285  c->user = 0;
286  c->group = 0;
287  c->password = 0;
288 
289  c->maximum_record_size = 0;
290  c->preferred_message_size = 0;
291 
294  c->odr_print = 0;
295  c->odr_save = 0;
296 
297  c->async = 0;
300 
301  c->m_queue_front = 0;
302  c->m_queue_back = 0;
303 
304  c->sru_version = 0;
305  c->no_redirects = 0;
306  c->saveAPDU_wrbuf = 0;
307  return c;
308 }
309 
310 ZOOM_API(void) ZOOM_connection_save_apdu_wrbuf(ZOOM_connection c, WRBUF w)
311 {
312  if (c->odr_save)
313  {
314  odr_destroy(c->odr_save);
315  c->odr_save = 0;
316  }
317  if (w)
318  {
319  c->odr_save = odr_createmem(ODR_PRINT);
320  odr_set_stream(c->odr_save, w, odr_wrbuf_write, 0);
321  }
322 }
323 
324 /* set database names. Take local databases (if set); otherwise
325  take databases given in ZURL (if set); otherwise use Default */
327  int *num, ODR odr)
328 {
329  char **databaseNames;
330  const char *cp = ZOOM_options_get(options, "databaseName");
331 
332  if ((!cp || !*cp) && con->host_port)
333  cs_get_host_args(con->host_port, &cp);
334  if (!cp || !*cp)
335  cp = "Default";
336  nmem_strsplit(odr_getmem(odr), "+", cp, &databaseNames, num);
337  return databaseNames;
338 }
339 
341  ZOOM_connection_new(const char *host, int portnum)
342 {
344 
345  ZOOM_connection_connect(c, host, portnum);
346  return c;
347 }
348 
350 {
351  if (!s || !*s)
352  return zoom_sru_soap;
353  if (!yaz_matchstr(s, "soap"))
354  return zoom_sru_soap;
355  else if (!yaz_matchstr(s, "get"))
356  return zoom_sru_get;
357  else if (!yaz_matchstr(s, "post"))
358  return zoom_sru_post;
359  else if (!yaz_matchstr(s, "solr"))
360  return zoom_sru_solr;
361  return zoom_sru_error;
362 }
363 
364 ZOOM_API(void)
366  const char *host, int portnum)
367 {
368  const char *val;
369 
370  initlog();
371 
372  yaz_log(c->log_api, "%p ZOOM_connection_connect host=%s portnum=%d",
373  c, host ? host : "null", portnum);
374 
377 
378  if (c->odr_print)
379  {
380  odr_setprint(c->odr_print, 0); /* prevent destroy from fclose'ing */
381  odr_destroy(c->odr_print);
382  }
383  if (ZOOM_options_get_bool(c->options, "apdulog", 0))
384  {
385  c->odr_print = odr_createmem(ODR_PRINT);
386  odr_setprint(c->odr_print, yaz_log_file());
387  }
388  else
389  c->odr_print = 0;
390 
391  if (c->cs)
392  {
393  yaz_log(c->log_details, "%p ZOOM_connection_connect reconnect ok", c);
394  c->reconnect_ok = 1;
395  return;
396  }
397  yaz_log(c->log_details, "%p ZOOM_connection_connect connect", c);
398  xfree(c->proxy);
399  c->proxy = 0;
400  val = ZOOM_options_get(c->options, "proxy");
401  if (val && *val)
402  {
403  yaz_log(c->log_details, "%p ZOOM_connection_connect proxy=%s", c, val);
404  c->proxy = xstrdup(val);
405  }
406 
407  xfree(c->tproxy);
408  c->tproxy = 0;
409  val = ZOOM_options_get(c->options, "tproxy");
410  if (val && *val)
411  {
412  yaz_log(c->log_details, "%p ZOOM_connection_connect tproxy=%s", c, val);
413  c->tproxy = xstrdup(val);
414  }
415 
416  xfree(c->charset);
417  c->charset = 0;
418  val = ZOOM_options_get(c->options, "charset");
419  if (val && *val)
420  {
421  yaz_log(c->log_details, "%p ZOOM_connection_connect charset=%s", c, val);
422  c->charset = xstrdup(val);
423  }
424 
425  xfree(c->lang);
426  val = ZOOM_options_get(c->options, "lang");
427  if (val && *val)
428  {
429  yaz_log(c->log_details, "%p ZOOM_connection_connect lang=%s", c, val);
430  c->lang = xstrdup(val);
431  }
432  else
433  c->lang = 0;
434 
435  if (host)
436  {
437  xfree(c->host_port);
438  if (portnum)
439  {
440  char hostn[128];
441  sprintf(hostn, "%.80s:%d", host, portnum);
442  c->host_port = xstrdup(hostn);
443  }
444  else
445  c->host_port = xstrdup(host);
446  }
447 
448  {
449  /*
450  * If the "<scheme>:" part of the host string is preceded by one
451  * or more comma-separated <name>=<value> pairs, these are taken
452  * to be options to be set on the connection object. Among other
453  * applications, this facility can be used to embed authentication
454  * in a host string:
455  * user=admin,password=secret,tcp:localhost:9999
456  */
457  char *remainder = c->host_port;
458  char *pcolon = strchr(remainder, ':');
459  char *pcomma;
460  char *pequals;
461  while ((pcomma = strchr(remainder, ',')) != 0 &&
462  (pcolon == 0 || pcomma < pcolon))
463  {
464  *pcomma = '\0';
465  if ((pequals = strchr(remainder, '=')) != 0)
466  {
467  *pequals = '\0';
468  ZOOM_connection_option_set(c, remainder, pequals+1);
469  }
470  remainder = pcomma+1;
471  }
472 
473  if (remainder != c->host_port)
474  {
475  remainder = xstrdup(remainder);
476  xfree(c->host_port);
477  c->host_port = remainder;
478  }
479  }
480 
481  val = ZOOM_options_get(c->options, "sru");
482  c->sru_mode = get_sru_mode_from_string(val);
483 
484  xfree(c->sru_version);
485  val = ZOOM_options_get(c->options, "sru_version");
486  c->sru_version = xstrdup(val ? val : "1.2");
487 
488  ZOOM_options_set(c->options, "host", c->host_port);
489 
490  xfree(c->cookie_out);
491  c->cookie_out = 0;
492  val = ZOOM_options_get(c->options, "cookie");
493  if (val && *val)
494  {
495  yaz_log(c->log_details, "%p ZOOM_connection_connect cookie=%s", c, val);
496  c->cookie_out = xstrdup(val);
497  }
498 
499  xfree(c->client_IP);
500  c->client_IP = 0;
501  val = ZOOM_options_get(c->options, "clientIP");
502  if (val && *val)
503  {
504  yaz_log(c->log_details, "%p ZOOM_connection_connect clientIP=%s",
505  c, val);
506  c->client_IP = xstrdup(val);
507  }
508 
509  xfree(c->group);
510  c->group = 0;
511  val = ZOOM_options_get(c->options, "group");
512  if (val && *val)
513  c->group = xstrdup(val);
514 
515  xfree(c->user);
516  c->user = 0;
517  val = ZOOM_options_get(c->options, "user");
518  if (val && *val)
519  c->user = xstrdup(val);
520 
521  xfree(c->password);
522  c->password = 0;
523  val = ZOOM_options_get(c->options, "password");
524  if (!val)
525  val = ZOOM_options_get(c->options, "pass");
526 
527  if (val && *val)
528  c->password = xstrdup(val);
529 
530  c->maximum_record_size =
531  ZOOM_options_get_int(c->options, "maximumRecordSize", 64*1024*1024);
532  c->preferred_message_size =
533  ZOOM_options_get_int(c->options, "preferredMessageSize", 64*1024*1024);
534 
535  c->async = ZOOM_options_get_bool(c->options, "async", 0);
536 
537  if (c->sru_mode == zoom_sru_error)
538  {
541  return;
542  }
543 
544  yaz_log(c->log_details, "%p ZOOM_connection_connect async=%d", c, c->async);
546 
547  if (!c->async)
548  {
549  while (ZOOM_event(1, &c))
550  ;
551  }
552 }
553 
555 {
556 #if ZOOM_RESULT_LISTS
557 #else
558  if (r->connection)
559  {
560  /* remove ourselves from the resultsets in connection */
562  while (1)
563  {
564  assert(*rp); /* we must be in this list!! */
565  if (*rp == r)
566  { /* OK, we're here - take us out of it */
567  *rp = (*rp)->next;
568  break;
569  }
570  rp = &(*rp)->next;
571  }
572  r->connection = 0;
573  }
574 #endif
575 }
576 
577 ZOOM_API(void)
579 {
580 #if ZOOM_RESULT_LISTS
581  ZOOM_resultsets list;
582 #else
583  ZOOM_resultset r;
584 #endif
585  if (!c)
586  return;
587  yaz_log(c->log_api, "%p ZOOM_connection_destroy", c);
588  if (c->cs)
589  cs_close(c->cs);
590 
591 #if ZOOM_RESULT_LISTS
592  /* Remove the connection's usage of resultsets */
593  list = c->resultsets;
594  while (list) {
595  ZOOM_resultsets removed = list;
596  ZOOM_resultset_destroy(list->resultset);
597  list = list->next;
598  xfree(removed);
599  }
600 #else
601  for (r = c->resultsets; r; r = r->next)
602  r->connection = 0;
603 #endif
604 
605  xfree(c->buf_in);
606  xfree(c->addinfo);
607  xfree(c->diagset);
608  odr_destroy(c->odr_in);
609  odr_destroy(c->odr_out);
610  if (c->odr_save)
611  odr_destroy(c->odr_save);
612  if (c->odr_print)
613  {
614  odr_setprint(c->odr_print, 0); /* prevent destroy from fclose'ing */
615  odr_destroy(c->odr_print);
616  }
617  ZOOM_options_destroy(c->options);
620  xfree(c->host_port);
621  xfree(c->proxy);
622  xfree(c->tproxy);
623  xfree(c->charset);
624  xfree(c->lang);
625  xfree(c->cookie_out);
626  xfree(c->cookie_in);
627  xfree(c->client_IP);
628  xfree(c->user);
629  xfree(c->group);
630  xfree(c->password);
631  xfree(c->sru_version);
632  wrbuf_destroy(c->saveAPDU_wrbuf);
633  xfree(c);
634 }
635 
637 {
638  if (r)
639  {
641  (r->refcount)++;
642  yaz_log(log_details0, "%p ZOOM_resultset_addref count=%d",
643  r, r->refcount);
645  }
646 }
647 
648 static int g_resultsets = 0;
650 
651 /* TODO We need to initialize this before running threaded:
652  * call resultset_use(0) */
653 
654 static int resultset_use(int delta) {
655  int resultset_count;
656  if (g_resultset_mutex == 0)
657  yaz_mutex_create(&g_resultset_mutex);
658  yaz_mutex_enter(g_resultset_mutex);
659  g_resultsets += delta;
660  resultset_count = g_resultsets;
661  yaz_mutex_leave(g_resultset_mutex);
662  return resultset_count;
663 }
664 
665 int resultsets_count(void) {
666  return resultset_use(0);
667 }
668 
670 {
671  int i;
672  ZOOM_resultset r = (ZOOM_resultset) xmalloc(sizeof(*r));
673 
674  initlog();
675 
676  yaz_log(log_details0, "%p ZOOM_resultset_create", r);
677  r->refcount = 1;
678  r->size = 0;
680  r->piggyback = 1;
681  r->setname = 0;
682  r->schema = 0;
683  r->step = 0;
684  for (i = 0; i<RECORD_HASH_SIZE; i++)
685  r->record_hash[i] = 0;
686  r->r_sort_spec = 0;
687  r->query = 0;
688  r->connection = 0;
689  r->databaseNames = 0;
690  r->num_databaseNames = 0;
691  r->facets = 0;
692  r->num_facets = 0;
693  r->facets_names = 0;
694  r->mutex = 0;
695  yaz_mutex_create(&r->mutex);
696 #if SHPTR
697  {
698  WRBUF w = wrbuf_alloc();
700  }
701 #endif
702  resultset_use(1);
703  return r;
704 }
705 
708 {
709  ZOOM_resultset r;
711 
712  ZOOM_query_prefix(s, q);
713 
714  r = ZOOM_connection_search(c, s);
716  return r;
717 }
718 
721 {
723  ZOOM_task task;
724  const char *cp;
725  int start, count;
726  const char *syntax, *elementSetName;
727 #if ZOOM_RESULT_LISTS
728  ZOOM_resultsets set;
729 #endif
730 
731  yaz_log(c->log_api, "%p ZOOM_connection_search set %p query %p", c, r, q);
733  r->query = q;
734 
735  r->options = ZOOM_options_create_with_parent(c->options);
736 
737  start = ZOOM_options_get_int(r->options, "start", 0);
738  count = ZOOM_options_get_int(r->options, "count", 0);
739  {
740  /* If "presentChunk" is defined use that; otherwise "step" */
741  const char *cp = ZOOM_options_get(r->options, "presentChunk");
743  (cp != 0 ? "presentChunk": "step"), 0);
744  }
745  r->piggyback = ZOOM_options_get_bool(r->options, "piggyback", 1);
746  cp = ZOOM_options_get(r->options, "setname");
747  if (cp)
748  r->setname = xstrdup(cp);
749  cp = ZOOM_options_get(r->options, "schema");
750  if (cp)
751  r->schema = xstrdup(cp);
752 
754  r->odr);
755 
756  r->connection = c;
757 
758 #if ZOOM_RESULT_LISTS
759  yaz_log(log_details, "%p ZOOM_connection_search: Adding new resultset (%p) to resultsets (%p) ", c, r, c->resultsets);
760  set = xmalloc(sizeof(*set));
762  set->resultset = r;
763  set->next = c->resultsets;
764  c->resultsets = set;
765 #else
766  r->next = c->resultsets;
767  c->resultsets = r;
768 #endif
769  if (c->host_port && c->proto == PROTO_HTTP)
770  {
771  if (!c->cs)
772  {
773  yaz_log(c->log_details, "ZOOM_connection_search: no comstack");
775  }
776  else
777  {
778  yaz_log(c->log_details, "ZOOM_connection_search: reconnect");
779  c->reconnect_ok = 1;
780  }
781  }
782 
784  task->u.search.resultset = r;
785  task->u.search.start = start;
786  task->u.search.count = count;
787  task->u.search.recv_search_fired = 0;
788 
789  syntax = ZOOM_options_get(r->options, "preferredRecordSyntax");
790  task->u.search.syntax = syntax ? xstrdup(syntax) : 0;
791  elementSetName = ZOOM_options_get(r->options, "elementSetName");
792  task->u.search.elementSetName = elementSetName
793  ? xstrdup(elementSetName) : 0;
794 
796 
798 
799  if (!c->async)
800  {
801  while (ZOOM_event(1, &c))
802  ;
803  }
804  return r;
805 }
806 
807 ZOOM_API(void)
809  const char *sort_type, const char *sort_spec)
810 {
811  (void) ZOOM_resultset_sort1(r, sort_type, sort_spec);
812 }
813 
814 ZOOM_API(int)
816  const char *sort_type, const char *sort_spec)
817 {
818  ZOOM_connection c = r->connection;
819  ZOOM_task task;
820  ZOOM_query newq;
821 
822  newq = ZOOM_query_create();
823  if (ZOOM_query_sortby(newq, sort_spec) < 0)
824  return -1;
825 
826  yaz_log(c->log_api, "%p ZOOM_resultset_sort r=%p sort_type=%s sort_spec=%s",
827  r, r, sort_type, sort_spec);
828  if (!c)
829  return 0;
830 
831  if (c->host_port && c->proto == PROTO_HTTP)
832  {
833  if (!c->cs)
834  {
835  yaz_log(c->log_details, "%p ZOOM_resultset_sort: no comstack", r);
837  }
838  else
839  {
840  yaz_log(c->log_details, "%p ZOOM_resultset_sort: prepare reconnect",
841  r);
842  c->reconnect_ok = 1;
843  }
844  }
845 
848  task->u.sort.resultset = r;
849  task->u.sort.q = newq;
850 
852 
853  if (!c->async)
854  {
855  while (ZOOM_event(1, &c))
856  ;
857  }
858 
859  return 0;
860 }
861 
862 ZOOM_API(void)
864 {
866 }
867 
869 {
870  if (!r)
871  return;
873  (r->refcount)--;
874  yaz_log(log_details0, "%p ZOOM_resultset_destroy r=%p count=%d",
875  r, r, r->refcount);
876  if (r->refcount == 0)
877  {
879 
880  yaz_log(log_details0, "%p ZOOM_connection resultset_destroy: Deleting resultset (%p) ", r->connection, r);
885  odr_destroy(r->odr);
886  xfree(r->setname);
887  xfree(r->schema);
889 #if SHPTR
891 #endif
892  resultset_use(-1);
893  xfree(r);
894  }
895  else
897 }
898 
899 ZOOM_API(size_t)
901 {
902  return r->size;
903 }
904 
906 {
907  ZOOM_Event event;
908 
909  if (!c->reconnect_ok)
910  return 0;
912  c->reconnect_ok = 0;
913  c->tasks->running = 0;
915 
917  ZOOM_connection_put_event(c, event);
918 
919  return 1;
920 }
921 
923  int force_sync, int start, int count)
924 {
925  ZOOM_task task;
926  ZOOM_connection c;
927  const char *cp;
928  const char *syntax, *elementSetName;
929 
930  if (!r)
931  return;
932  yaz_log(log_details0, "%p ZOOM_resultset_retrieve force_sync=%d start=%d"
933  " count=%d", r, force_sync, start, count);
934  c = r->connection;
935  if (!c)
936  return;
937 
938  if (c->host_port && c->proto == PROTO_HTTP)
939  {
940  if (!c->cs)
941  {
942  yaz_log(log_details0, "%p ZOOM_resultset_retrieve: no comstack", r);
944  }
945  else
946  {
947  yaz_log(log_details0, "%p ZOOM_resultset_retrieve: prepare "
948  "reconnect", r);
949  c->reconnect_ok = 1;
950  }
951  }
953  task->u.retrieve.resultset = r;
954  task->u.retrieve.start = start;
955  task->u.retrieve.count = count;
956 
957  syntax = ZOOM_options_get(r->options, "preferredRecordSyntax");
958  task->u.retrieve.syntax = syntax ? xstrdup(syntax) : 0;
959  elementSetName = ZOOM_options_get(r->options, "elementSetName");
960  task->u.retrieve.elementSetName = elementSetName
961  ? xstrdup(elementSetName) : 0;
962 
963  cp = ZOOM_options_get(r->options, "schema");
964  if (cp)
965  {
966  if (!r->schema || strcmp(r->schema, cp))
967  {
968  xfree(r->schema);
969  r->schema = xstrdup(cp);
970  }
971  }
972 
974 
975  if (!r->connection->async || force_sync)
976  while (r->connection && ZOOM_event(1, &r->connection))
977  ;
978 }
979 
980 ZOOM_API(void)
982  size_t start, size_t count)
983 {
984  int force_present = 0;
985 
986  if (!r)
987  return ;
988  yaz_log(log_api0, "%p ZOOM_resultset_records r=%p start=%ld count=%ld",
989  r, r, (long) start, (long) count);
990  if (count && recs)
991  force_present = 1;
992  ZOOM_resultset_retrieve(r, force_present, start, count);
993  if (force_present)
994  {
995  size_t i;
996  for (i = 0; i< count; i++)
997  recs[i] = ZOOM_resultset_record_immediate(r, i+start);
998  }
999 }
1000 
1001 ZOOM_API(size_t)
1003  return r->num_facets;
1004 }
1005 
1008  int num = r->num_facets;
1009  ZOOM_facet_field *facets = r->facets;
1010  int index;
1011  for (index = 0; index < num; index++) {
1012  if (!strcmp(facets[index]->facet_name, name)) {
1013  return facets[index];
1014  }
1015  }
1016  return 0;
1017 }
1018 
1021  int num = r->num_facets;
1022  ZOOM_facet_field *facets = r->facets;
1023  if (index >= 0 && index < num) {
1024  return facets[index];
1025  }
1026  return 0;
1027 }
1028 
1031 {
1032  return r->facets;
1033 }
1034 
1035 ZOOM_API(const char**)
1037 {
1038  return (const char **) r->facets_names;
1039 }
1040 
1041 ZOOM_API(const char*)
1043 {
1044  return field->facet_name;
1045 }
1046 
1047 ZOOM_API(size_t)
1049 {
1050  return field->num_terms;
1051 }
1052 
1053 ZOOM_API(const char*)
1054  ZOOM_facet_field_get_term(ZOOM_facet_field field, size_t idx, int *freq) {
1055  *freq = field->facet_terms[idx].frequency;
1056  return field->facet_terms[idx].term;
1057 }
1058 
1059 
1061 {
1062  char *cert_buf;
1063  int cert_len;
1064 
1065  if (cs_get_peer_certificate_x509(c->cs, &cert_buf, &cert_len))
1066  {
1067  ZOOM_connection_option_setl(c, "sslPeerCert",
1068  cert_buf, cert_len);
1069  xfree(cert_buf);
1070  }
1071 }
1072 
1074  const char *logical_url);
1075 
1077 {
1078  return do_connect_host(c, c->host_port);
1079 }
1080 
1081 static zoom_ret do_connect_host(ZOOM_connection c, const char *logical_url)
1082 {
1083  void *add;
1084 
1085  if (c->cs)
1086  cs_close(c->cs);
1087  c->cs = cs_create_host_proxy(logical_url, 0, &add,
1088  c->tproxy ? c->tproxy : c->proxy);
1089 
1090  if (c->cs && c->cs->protocol == PROTO_HTTP)
1091  {
1092 #if YAZ_HAVE_XML2
1093  c->proto = PROTO_HTTP;
1094 #else
1097  return zoom_complete;
1098 #endif
1099  }
1100  if (c->cs)
1101  {
1102  int ret = cs_connect(c->cs, add);
1103  if (ret == 0)
1104  {
1106  ZOOM_connection_put_event(c, event);
1107  get_cert(c);
1108  if (c->proto == PROTO_Z3950)
1110  else
1111  {
1112  /* no init request for SRW .. */
1113  assert(c->tasks->which == ZOOM_TASK_CONNECT);
1117  }
1118  c->state = STATE_ESTABLISHED;
1119  return zoom_pending;
1120  }
1121  else if (ret > 0)
1122  {
1123  int mask = ZOOM_SELECT_EXCEPT;
1124  if (c->cs->io_pending & CS_WANT_WRITE)
1125  mask += ZOOM_SELECT_WRITE;
1126  if (c->cs->io_pending & CS_WANT_READ)
1127  mask += ZOOM_SELECT_READ;
1128  ZOOM_connection_set_mask(c, mask);
1129  c->state = STATE_CONNECTING;
1130  return zoom_pending;
1131  }
1132  }
1133  c->state = STATE_IDLE;
1134  ZOOM_set_error(c, ZOOM_ERROR_CONNECT, logical_url);
1135  return zoom_complete;
1136 }
1137 
1138 /* returns 1 if PDU was sent OK (still pending )
1139  0 if PDU was not sent OK (nothing to wait for)
1140 */
1141 
1144 {
1145  const char *syntax =
1146  ZOOM_options_get(s->options, "preferredRecordSyntax");
1147  const char *elementSetName =
1148  ZOOM_options_get(s->options, "elementSetName");
1149 
1150  return ZOOM_record_cache_lookup(s, pos, syntax, elementSetName);
1151 }
1152 
1155 {
1157 
1158  if (!rec)
1159  {
1160  /*
1161  * MIKE: I think force_sync should always be zero, but I don't
1162  * want to make this change until I get the go-ahead from
1163  * Adam, in case something depends on the old synchronous
1164  * behaviour.
1165  */
1166  int force_sync = 1;
1167  if (getenv("ZOOM_RECORD_NO_FORCE_SYNC")) force_sync = 0;
1168  ZOOM_resultset_retrieve(r, force_sync, pos, 1);
1169  rec = ZOOM_resultset_record_immediate(r, pos);
1170  }
1171  return rec;
1172 }
1173 
1176 {
1177  ZOOM_scanset s;
1179 
1180  ZOOM_query_prefix(q, start);
1181 
1182  s = ZOOM_connection_scan1(c, q);
1183  ZOOM_query_destroy(q);
1184  return s;
1185 
1186 }
1187 
1190 {
1191  ZOOM_scanset scan = 0;
1192  Z_Query *z_query = ZOOM_query_get_Z_Query(q);
1193 
1194  if (!z_query)
1195  return 0;
1196  scan = (ZOOM_scanset) xmalloc(sizeof(*scan));
1197  scan->connection = c;
1198  scan->odr = odr_createmem(ODR_DECODE);
1199  scan->options = ZOOM_options_create_with_parent(c->options);
1200  scan->refcount = 1;
1201  scan->scan_response = 0;
1202  scan->srw_scan_response = 0;
1203 
1204  scan->query = q;
1205  ZOOM_query_addref(q);
1206  scan->databaseNames = ZOOM_connection_get_databases(c, c->options,
1207  &scan->num_databaseNames,
1208  scan->odr);
1209 
1210  if (1)
1211  {
1213  task->u.scan.scan = scan;
1214 
1215  (scan->refcount)++;
1216  if (!c->async)
1217  {
1218  while (ZOOM_event(1, &c))
1219  ;
1220  }
1221  }
1222  return scan;
1223 }
1224 
1225 ZOOM_API(void)
1227 {
1228  if (!scan)
1229  return;
1230  (scan->refcount)--;
1231  if (scan->refcount == 0)
1232  {
1233  ZOOM_query_destroy(scan->query);
1234 
1235  odr_destroy(scan->odr);
1236 
1237  ZOOM_options_destroy(scan->options);
1238  xfree(scan);
1239  }
1240 }
1241 
1243 {
1244  ZOOM_Event event;
1245 
1246  yaz_log(c->log_details, "%p send_package", c);
1247  if (!c->tasks)
1248  return zoom_complete;
1249  assert (c->tasks->which == ZOOM_TASK_PACKAGE);
1250 
1252  ZOOM_connection_put_event(c, event);
1253 
1254  c->buf_out = c->tasks->u.package->buf_out;
1255  c->len_out = c->tasks->u.package->len_out;
1256 
1257  return ZOOM_send_buf(c);
1258 }
1259 
1260 ZOOM_API(size_t)
1262 {
1263  if (!scan)
1264  return 0;
1265 
1266  if (scan->scan_response && scan->scan_response->entries)
1267  return scan->scan_response->entries->num_entries;
1268  else if (scan->srw_scan_response)
1269  return scan->srw_scan_response->num_terms;
1270  return 0;
1271 }
1272 
1273 static void ZOOM_scanset_term_x(ZOOM_scanset scan, size_t pos,
1274  size_t *occ,
1275  const char **value_term, size_t *value_len,
1276  const char **disp_term, size_t *disp_len)
1277 {
1278  size_t noent = ZOOM_scanset_size(scan);
1279 
1280  *value_term = 0;
1281  *value_len = 0;
1282 
1283  *disp_term = 0;
1284  *disp_len = 0;
1285 
1286  *occ = 0;
1287  if (pos >= noent)
1288  return;
1289  if (scan->scan_response)
1290  {
1291  Z_ScanResponse *res = scan->scan_response;
1292  if (res->entries->entries[pos]->which == Z_Entry_termInfo)
1293  {
1294  Z_TermInfo *t = res->entries->entries[pos]->u.termInfo;
1295 
1296  *value_term = (const char *) t->term->u.general->buf;
1297  *value_len = t->term->u.general->len;
1298  if (t->displayTerm)
1299  {
1300  *disp_term = t->displayTerm;
1301  *disp_len = strlen(*disp_term);
1302  }
1303  else if (t->term->which == Z_Term_general)
1304  {
1305  *disp_term = (const char *) t->term->u.general->buf;
1306  *disp_len = t->term->u.general->len;
1307  }
1308  *occ = t->globalOccurrences ? *t->globalOccurrences : 0;
1309  }
1310  }
1311  if (scan->srw_scan_response)
1312  {
1314  Z_SRW_scanTerm *t = res->terms + pos;
1315  if (t)
1316  {
1317  *value_term = t->value;
1318  *value_len = strlen(*value_term);
1319 
1320  if (t->displayTerm)
1321  *disp_term = t->displayTerm;
1322  else
1323  *disp_term = t->value;
1324  *disp_len = strlen(*disp_term);
1325  *occ = t->numberOfRecords ? *t->numberOfRecords : 0;
1326  }
1327  }
1328 }
1329 
1330 ZOOM_API(const char *)
1332  size_t *occ, size_t *len)
1333 {
1334  const char *value_term = 0;
1335  size_t value_len = 0;
1336  const char *disp_term = 0;
1337  size_t disp_len = 0;
1338 
1339  ZOOM_scanset_term_x(scan, pos, occ, &value_term, &value_len,
1340  &disp_term, &disp_len);
1341 
1342  *len = value_len;
1343  return value_term;
1344 }
1345 
1346 ZOOM_API(const char *)
1348  size_t *occ, size_t *len)
1349 {
1350  const char *value_term = 0;
1351  size_t value_len = 0;
1352  const char *disp_term = 0;
1353  size_t disp_len = 0;
1354 
1355  ZOOM_scanset_term_x(scan, pos, occ, &value_term, &value_len,
1356  &disp_term, &disp_len);
1357 
1358  *len = disp_len;
1359  return disp_term;
1360 }
1361 
1362 ZOOM_API(const char *)
1363  ZOOM_scanset_option_get(ZOOM_scanset scan, const char *key)
1364 {
1365  return ZOOM_options_get(scan->options, key);
1366 }
1367 
1368 ZOOM_API(void)
1369  ZOOM_scanset_option_set(ZOOM_scanset scan, const char *key,
1370  const char *val)
1371 {
1372  ZOOM_options_set(scan->options, key, val);
1373 }
1374 
1375 
1378 {
1379  ZOOM_package p = (ZOOM_package) xmalloc(sizeof(*p));
1380 
1381  p->connection = c;
1383  p->options = ZOOM_options_create_with_parent2(options, c->options);
1384  p->refcount = 1;
1385  p->buf_out = 0;
1386  p->len_out = 0;
1387  return p;
1388 }
1389 
1390 ZOOM_API(void)
1392 {
1393  if (!p)
1394  return;
1395  (p->refcount)--;
1396  if (p->refcount == 0)
1397  {
1398  odr_destroy(p->odr_out);
1399  xfree(p->buf_out);
1400 
1401  ZOOM_options_destroy(p->options);
1402  xfree(p);
1403  }
1404 }
1405 
1406 ZOOM_API(const char *)
1408 {
1409  return ZOOM_options_get(p->options, key);
1410 }
1411 
1412 ZOOM_API(const char *)
1413  ZOOM_package_option_getl(ZOOM_package p, const char *key, int *lenp)
1414 {
1415  return ZOOM_options_getl(p->options, key, lenp);
1416 }
1417 
1418 ZOOM_API(void)
1420  const char *val)
1421 {
1422  ZOOM_options_set(p->options, key, val);
1423 }
1424 
1425 ZOOM_API(void)
1427  const char *val, int len)
1428 {
1429  ZOOM_options_setl(p->options, key, val, len);
1430 }
1431 
1432 ZOOM_API(int)
1434 {
1435  ZOOM_task task = c->tasks;
1436  zoom_ret ret = zoom_complete;
1437 
1438  if (!task)
1439  return 0;
1440  yaz_log(c->log_details, "%p ZOOM_connection_exec_task type=%d run=%d",
1441  c, task->which, task->running);
1442  if (c->error != ZOOM_ERROR_NONE)
1443  {
1444  yaz_log(c->log_details, "%p ZOOM_connection_exec_task "
1445  "removing tasks because of error = %d", c, c->error);
1447  return 0;
1448  }
1449  if (task->running)
1450  {
1451  yaz_log(c->log_details, "%p ZOOM_connection_exec_task "
1452  "task already running", c);
1453  return 0;
1454  }
1455  task->running = 1;
1456  ret = zoom_complete;
1457  if (c->cs || task->which == ZOOM_TASK_CONNECT)
1458  {
1459  switch (task->which)
1460  {
1461  case ZOOM_TASK_SEARCH:
1462  if (c->proto == PROTO_HTTP)
1464  else
1466  break;
1467  case ZOOM_TASK_RETRIEVE:
1468  if (c->proto == PROTO_HTTP)
1470  else
1471  ret = send_Z3950_present(c);
1472  break;
1473  case ZOOM_TASK_CONNECT:
1474  ret = do_connect(c);
1475  break;
1476  case ZOOM_TASK_SCAN:
1477  if (c->proto == PROTO_HTTP)
1479  else
1481  break;
1482  case ZOOM_TASK_PACKAGE:
1483  ret = send_package(c);
1484  break;
1485  case ZOOM_TASK_SORT:
1486  c->tasks->u.sort.resultset->r_sort_spec =
1487  ZOOM_query_get_sortspec(c->tasks->u.sort.q);
1488  ret = send_Z3950_sort(c, c->tasks->u.sort.resultset);
1489  break;
1490  }
1491  }
1492  else
1493  {
1494  yaz_log(c->log_details, "%p ZOOM_connection_exec_task "
1495  "remove tasks because no connection exist", c);
1497  }
1498  if (ret == zoom_complete)
1499  {
1500  yaz_log(c->log_details, "%p ZOOM_connection_exec_task "
1501  "task removed (complete)", c);
1503  return 0;
1504  }
1505  yaz_log(c->log_details, "%p ZOOM_connection_exec_task "
1506  "task pending", c);
1507  return 1;
1508 }
1509 
1510 #if YAZ_HAVE_XML2
1511 
1512 static zoom_ret send_HTTP_redirect(ZOOM_connection c, const char *uri,
1513  Z_HTTP_Response *cookie_hres)
1514 {
1515  struct Z_HTTP_Header *h;
1516  char *combined_cookies = 0;
1517  int combined_cookies_len = 0;
1518  Z_GDU *gdu = z_get_HTTP_Request_uri(c->odr_out, uri, 0, c->proxy ? 1 : 0);
1519 
1520  gdu->u.HTTP_Request->method = odr_strdup(c->odr_out, "GET");
1521  z_HTTP_header_add(c->odr_out, &gdu->u.HTTP_Request->headers, "Accept",
1522  "text/xml");
1523 
1524  for (h = cookie_hres->headers; h; h = h->next)
1525  {
1526  if (!strcmp(h->name, "Set-Cookie"))
1527  {
1528  char *cp;
1529 
1530  if (!(cp = strchr(h->value, ';')))
1531  cp = h->value + strlen(h->value);
1532  if (cp - h->value >= 1) {
1533  combined_cookies = xrealloc(combined_cookies, combined_cookies_len + cp - h->value + 3);
1534  memcpy(combined_cookies+combined_cookies_len, h->value, cp - h->value);
1535  combined_cookies[combined_cookies_len + cp - h->value] = '\0';
1536  strcat(combined_cookies,"; ");
1537  combined_cookies_len = strlen(combined_cookies);
1538  }
1539  }
1540  }
1541 
1542  if (combined_cookies_len)
1543  {
1545  "Cookie", combined_cookies);
1546  xfree(combined_cookies);
1547  }
1548 
1549  if (c->user && c->password)
1550  {
1552  c->user, c->password);
1553  }
1554  return ZOOM_send_GDU(c, gdu);
1555 }
1556 
1558 {
1559  ZOOM_Event event;
1560 
1561  int r = z_GDU(c->odr_out, &gdu, 0, 0);
1562  if (!r)
1563  return zoom_complete;
1564  if (c->odr_print)
1565  z_GDU(c->odr_print, &gdu, 0, 0);
1566  if (c->odr_save)
1567  z_GDU(c->odr_save, &gdu, 0, 0);
1568  c->buf_out = odr_getbuf(c->odr_out, &c->len_out, 0);
1569  odr_reset(c->odr_out);
1570 
1572  ZOOM_connection_put_event(c, event);
1573 
1574  return ZOOM_send_buf(c);
1575 }
1576 
1577 #if YAZ_HAVE_XML2
1579  const char *addinfo, const char *addinfo2)
1580 {
1581  ZOOM_set_dset_error(c, error, "HTTP", addinfo, addinfo2);
1582 }
1583 #endif
1584 
1585 
1587 {
1588  zoom_ret cret = zoom_complete;
1589  int ret = -1;
1590  char *addinfo = 0;
1591  const char *connection_head = z_HTTP_header_lookup(hres->headers,
1592  "Connection");
1593  const char *location;
1594 
1596  yaz_log(c->log_details, "%p handle_http", c);
1597 
1598  if ((hres->code == 301 || hres->code == 302) && c->sru_mode == zoom_sru_get
1599  && (location = z_HTTP_header_lookup(hres->headers, "Location")))
1600  {
1601  c->no_redirects++;
1602  if (c->no_redirects > 10)
1603  {
1604  ZOOM_set_HTTP_error(c, hres->code, 0, 0);
1605  c->no_redirects = 0;
1607  }
1608  else
1609  {
1610  /* since redirect may change host we just reconnect. A smarter
1611  implementation might check whether it's the same server */
1612  do_connect_host(c, location);
1613  send_HTTP_redirect(c, location, hres);
1614  /* we're OK for now. Operation is not really complete */
1615  return;
1616  }
1617  }
1618  else
1619  {
1620  ret = ZOOM_handle_sru(c, hres, &cret, &addinfo);
1621  if (ret == 0)
1622  {
1623  if (c->no_redirects) /* end of redirect. change hosts again */
1625  }
1626  c->no_redirects = 0;
1627  }
1628  if (ret)
1629  {
1630  if (hres->code != 200)
1631  ZOOM_set_HTTP_error(c, hres->code, 0, 0);
1632  else
1633  {
1634  yaz_log(YLOG_LOG, "set error... addinfo=%s", addinfo ?
1635  addinfo : "NULL");
1636  ZOOM_set_error(c, ZOOM_ERROR_DECODE, addinfo);
1637  }
1639  }
1640  if (cret == zoom_complete)
1641  {
1642  yaz_log(c->log_details, "removing tasks in handle_http");
1644  }
1645  {
1646  int must_close = 0;
1647  if (!strcmp(hres->version, "1.0"))
1648  {
1649  /* HTTP 1.0: only if Keep-Alive we stay alive.. */
1650  if (!connection_head || strcmp(connection_head, "Keep-Alive"))
1651  must_close = 1;
1652  }
1653  else
1654  {
1655  /* HTTP 1.1: only if no close we stay alive.. */
1656  if (connection_head && !strcmp(connection_head, "close"))
1657  must_close = 1;
1658  }
1659  if (must_close)
1660  {
1662  if (c->tasks)
1663  {
1664  c->tasks->running = 0;
1666  c->reconnect_ok = 0;
1667  }
1668  }
1669  else
1670  c->reconnect_ok = 1; /* if the server closes anyway */
1671  }
1672 }
1673 #endif
1674 
1676 {
1677  int r, more;
1678  ZOOM_Event event;
1679 
1681  ZOOM_connection_put_event(c, event);
1682 
1683  r = cs_get(c->cs, &c->buf_in, &c->len_in);
1684  more = cs_more(c->cs);
1685  yaz_log(c->log_details, "%p do_read len=%d more=%d", c, r, more);
1686  if (r == 1)
1687  return 0;
1688  if (r <= 0)
1689  {
1690  if (!ZOOM_test_reconnect(c))
1691  {
1694  }
1695  }
1696  else
1697  {
1698  Z_GDU *gdu;
1699  ZOOM_Event event;
1700 
1701  odr_reset(c->odr_in);
1702  odr_setbuf(c->odr_in, c->buf_in, r, 0);
1704  ZOOM_connection_put_event(c, event);
1705 
1706  if (!z_GDU(c->odr_in, &gdu, 0, 0))
1707  {
1708  int x;
1709  int err = odr_geterrorx(c->odr_in, &x);
1710  char msg[100];
1711  const char *element = odr_getelement(c->odr_in);
1712  yaz_snprintf(msg, sizeof(msg),
1713  "ODR code %d:%d element=%s offset=%d",
1714  err, x, element ? element : "<unknown>",
1715  odr_offset(c->odr_in));
1717  if (c->log_api)
1718  {
1719  FILE *ber_file = yaz_log_file();
1720  if (ber_file)
1721  odr_dumpBER(ber_file, c->buf_in, r);
1722  }
1724  }
1725  else
1726  {
1727  if (c->odr_print)
1728  z_GDU(c->odr_print, &gdu, 0, 0);
1729  if (c->odr_save)
1730  z_GDU(c->odr_save, &gdu, 0, 0);
1731  if (gdu->which == Z_GDU_Z3950)
1732  ZOOM_handle_Z3950_apdu(c, gdu->u.z3950);
1733  else if (gdu->which == Z_GDU_HTTP_Response)
1734  {
1735 #if YAZ_HAVE_XML2
1736  handle_http(c, gdu->u.HTTP_Response);
1737 #else
1740 #endif
1741  }
1742  }
1743  }
1744  return 1;
1745 }
1746 
1747 static zoom_ret do_write_ex(ZOOM_connection c, char *buf_out, int len_out)
1748 {
1749  int r;
1750  ZOOM_Event event;
1751 
1753  ZOOM_connection_put_event(c, event);
1754 
1755  yaz_log(c->log_details, "%p do_write_ex len=%d", c, len_out);
1756  if ((r = cs_put(c->cs, buf_out, len_out)) < 0)
1757  {
1758  yaz_log(c->log_details, "%p do_write_ex write failed", c);
1759  if (ZOOM_test_reconnect(c))
1760  {
1761  return zoom_pending;
1762  }
1763  if (c->state == STATE_CONNECTING)
1765  else
1768  return zoom_complete;
1769  }
1770  else if (r == 1)
1771  {
1772  int mask = ZOOM_SELECT_EXCEPT;
1773  if (c->cs->io_pending & CS_WANT_WRITE)
1774  mask += ZOOM_SELECT_WRITE;
1775  if (c->cs->io_pending & CS_WANT_READ)
1776  mask += ZOOM_SELECT_READ;
1777  ZOOM_connection_set_mask(c, mask);
1778  yaz_log(c->log_details, "%p do_write_ex write incomplete mask=%d",
1779  c, c->mask);
1780  }
1781  else
1782  {
1784  yaz_log(c->log_details, "%p do_write_ex write complete mask=%d",
1785  c, c->mask);
1786  }
1787  return zoom_pending;
1788 }
1789 
1791 {
1792  return do_write_ex(c, c->buf_out, c->len_out);
1793 }
1794 
1795 
1796 ZOOM_API(const char *)
1798 {
1799  if (!strcmp(key, "APDU"))
1800  {
1801  return c->saveAPDU_wrbuf ? wrbuf_cstr(c->saveAPDU_wrbuf) : "";
1802  }
1803  else
1804  return ZOOM_options_get(c->options, key);
1805 }
1806 
1807 ZOOM_API(const char *)
1808  ZOOM_connection_option_getl(ZOOM_connection c, const char *key, int *lenp)
1809 {
1810  if (!strcmp(key, "APDU"))
1811  {
1812  if (c->saveAPDU_wrbuf)
1813  {
1814  *lenp = wrbuf_len(c->saveAPDU_wrbuf);
1815  return wrbuf_cstr(c->saveAPDU_wrbuf);
1816  }
1817  else
1818  {
1819  *lenp = 0;
1820  return "";
1821  }
1822  }
1823  else
1824  return ZOOM_options_getl(c->options, key, lenp);
1825 }
1826 
1827 ZOOM_API(void)
1829  const char *val)
1830 {
1831  if (!strcmp(key, "saveAPDU"))
1832  {
1833  if (val && strcmp(val, "0"))
1834  {
1835  if (!c->saveAPDU_wrbuf)
1836  c->saveAPDU_wrbuf = wrbuf_alloc();
1837  else
1838  wrbuf_rewind(c->saveAPDU_wrbuf);
1839  }
1840  else
1841  {
1842  wrbuf_destroy(c->saveAPDU_wrbuf);
1843  c->saveAPDU_wrbuf = 0;
1844  }
1845  ZOOM_connection_save_apdu_wrbuf(c, c->saveAPDU_wrbuf);
1846  }
1847  else
1848  ZOOM_options_set(c->options, key, val);
1849 }
1850 
1851 ZOOM_API(void)
1853  const char *val, int len)
1854 {
1855  ZOOM_options_setl(c->options, key, val, len);
1856 }
1857 
1858 ZOOM_API(const char *)
1860 {
1861  return ZOOM_options_get(r->options, key);
1862 }
1863 
1864 ZOOM_API(void)
1866  const char *val)
1867 {
1868  ZOOM_options_set(r->options, key, val);
1869 }
1870 
1871 
1872 ZOOM_API(int)
1874 {
1875  return ZOOM_connection_error(c, 0, 0);
1876 }
1877 
1878 ZOOM_API(const char *)
1880 {
1881  const char *msg;
1882  ZOOM_connection_error(c, &msg, 0);
1883  return msg;
1884 }
1885 
1886 ZOOM_API(const char *)
1888 {
1889  const char *addinfo;
1890  ZOOM_connection_error(c, 0, &addinfo);
1891  return addinfo;
1892 }
1893 
1894 ZOOM_API(const char *)
1896 {
1897  const char *diagset;
1898  ZOOM_connection_error_x(c, 0, 0, &diagset);
1899  return diagset;
1900 }
1901 
1902 ZOOM_API(const char *)
1903  ZOOM_diag_str(int error)
1904 {
1905  switch (error)
1906  {
1907  case ZOOM_ERROR_NONE:
1908  return "No error";
1909  case ZOOM_ERROR_CONNECT:
1910  return "Connect failed";
1911  case ZOOM_ERROR_MEMORY:
1912  return "Out of memory";
1913  case ZOOM_ERROR_ENCODE:
1914  return "Encoding failed";
1915  case ZOOM_ERROR_DECODE:
1916  return "Decoding failed";
1918  return "Connection lost";
1919  case ZOOM_ERROR_INIT:
1920  return "Init rejected";
1921  case ZOOM_ERROR_INTERNAL:
1922  return "Internal failure";
1923  case ZOOM_ERROR_TIMEOUT:
1924  return "Timeout";
1926  return "Unsupported protocol";
1928  return "Unsupported query type";
1930  return "Invalid query";
1931  case ZOOM_ERROR_CQL_PARSE:
1932  return "CQL parsing error";
1934  return "CQL transformation error";
1935  case ZOOM_ERROR_CCL_CONFIG:
1936  return "CCL configuration error";
1937  case ZOOM_ERROR_CCL_PARSE:
1938  return "CCL parsing error";
1940  return "Extended Service. invalid action";
1942  return "Extended Service. invalid version";
1944  return "Extended Service. invalid syntax";
1945  default:
1946  return diagbib1_str(error);
1947  }
1948 }
1949 
1950 ZOOM_API(int)
1952  const char **addinfo, const char **diagset)
1953 {
1954  int error = c->error;
1955  if (cp)
1956  {
1957  if (!c->diagset || !strcmp(c->diagset, "ZOOM"))
1958  *cp = ZOOM_diag_str(error);
1959  else if (!strcmp(c->diagset, "HTTP"))
1960  *cp = z_HTTP_errmsg(c->error);
1961  else if (!strcmp(c->diagset, "Bib-1"))
1962  *cp = ZOOM_diag_str(error);
1963  else if (!strcmp(c->diagset, "info:srw/diagnostic/1"))
1964  *cp = yaz_diag_srw_str(c->error);
1965  else
1966  *cp = "Unknown error and diagnostic set";
1967  }
1968  if (addinfo)
1969  *addinfo = c->addinfo ? c->addinfo : "";
1970  if (diagset)
1971  *diagset = c->diagset ? c->diagset : "";
1972  return c->error;
1973 }
1974 
1975 ZOOM_API(int)
1977  const char **addinfo)
1978 {
1979  return ZOOM_connection_error_x(c, cp, addinfo, 0);
1980 }
1981 
1983 {
1984  ZOOM_Event event = 0;
1985  int r = cs_look(c->cs);
1986  yaz_log(c->log_details, "%p ZOOM_connection_do_io mask=%d cs_look=%d",
1987  c, mask, r);
1988 
1989  if (r == CS_NONE)
1990  {
1994  ZOOM_connection_put_event(c, event);
1995  }
1996  else if (r == CS_CONNECT)
1997  {
1998  int ret = ret = cs_rcvconnect(c->cs);
1999  yaz_log(c->log_details, "%p ZOOM_connection_do_io "
2000  "cs_rcvconnect returned %d", c, ret);
2001  if (ret == 1)
2002  {
2003  int mask = ZOOM_SELECT_EXCEPT;
2004  if (c->cs->io_pending & CS_WANT_WRITE)
2005  mask += ZOOM_SELECT_WRITE;
2006  if (c->cs->io_pending & CS_WANT_READ)
2007  mask += ZOOM_SELECT_READ;
2008  ZOOM_connection_set_mask(c, mask);
2010  ZOOM_connection_put_event(c, event);
2011  }
2012  else if (ret == 0)
2013  {
2015  ZOOM_connection_put_event(c, event);
2016  get_cert(c);
2017  if (c->proto == PROTO_Z3950)
2019  else
2020  {
2021  /* no init request for SRW .. */
2022  assert(c->tasks->which == ZOOM_TASK_CONNECT);
2026  }
2027  c->state = STATE_ESTABLISHED;
2028  }
2029  else
2030  {
2033  }
2034  }
2035  else
2036  {
2037  if (mask & ZOOM_SELECT_EXCEPT)
2038  {
2039  if (!ZOOM_test_reconnect(c))
2040  {
2043  }
2044  return;
2045  }
2046  if (mask & ZOOM_SELECT_READ)
2047  do_read(c);
2048  if (c->cs && (mask & ZOOM_SELECT_WRITE))
2049  ZOOM_send_buf(c);
2050  }
2051 }
2052 
2053 ZOOM_API(int)
2055 {
2056  if (!cs)
2057  return ZOOM_EVENT_NONE;
2058  return cs->last_event;
2059 }
2060 
2061 
2063 {
2064  if (c->mask)
2065  {
2067  /* timeout and this connection was waiting */
2070  ZOOM_connection_put_event(c, event);
2071  }
2072  return 0;
2073 }
2074 
2075 ZOOM_API(int)
2077 {
2078  ZOOM_Event event;
2079  if (!c)
2080  return 0;
2081 
2082  event = ZOOM_connection_get_event(c);
2083  if (event)
2084  {
2085  ZOOM_Event_destroy(event);
2086  return 1;
2087  }
2089  event = ZOOM_connection_get_event(c);
2090  if (event)
2091  {
2092  ZOOM_Event_destroy(event);
2093  return 1;
2094  }
2095  return 0;
2096 }
2097 
2098 ZOOM_API(int)
2100 {
2101  int i;
2102 
2103  yaz_log(log_details0, "ZOOM_process_event(no=%d,cs=%p)", no, cs);
2104 
2105  for (i = 0; i<no; i++)
2106  {
2107  ZOOM_connection c = cs[i];
2108 
2109  if (c && ZOOM_connection_process(c))
2110  return i+1;
2111  }
2112  return 0;
2113 }
2114 
2116 {
2117  if (c->mask && mask)
2118  ZOOM_connection_do_io(c, mask);
2119  return 0;
2120 }
2121 
2123 {
2124  if (c->cs)
2125  return cs_fileno(c->cs);
2126  return -1;
2127 }
2128 
2130 {
2131  c->mask = mask;
2132  if (!c->cs)
2133  return -1;
2134  return 0;
2135 }
2136 
2138 {
2139  if (c->cs)
2140  return c->mask;
2141  return 0;
2142 }
2143 
2145 {
2146  return ZOOM_options_get_int(c->options, "timeout", 30);
2147 }
2148 
2150 {
2151  if (c->cs)
2152  cs_close(c->cs);
2153  c->cs = 0;
2155  c->state = STATE_IDLE;
2156 }
2157 
2158 /*
2159  * Local variables:
2160  * c-basic-offset: 4
2161  * c-file-style: "Stroustrup"
2162  * indent-tabs-mode: nil
2163  * End:
2164  * vim: shiftwidth=4 tabstop=8 expandtab
2165  */
2166