1 /* This file is part of the YAZ toolkit.
2 * Copyright (C) 1995-2010 Index Data
3 * See the file LICENSE for details.
7 * \brief Implements ZOOM C interface.
15 #include <yaz/yaz-util.h>
16 #include <yaz/xmalloc.h>
17 #include <yaz/otherinfo.h>
19 #include <yaz/marcdisp.h>
20 #include <yaz/diagbib1.h>
21 #include <yaz/charneg.h>
22 #include <yaz/query-charset.h>
23 #include <yaz/copy_types.h>
24 #include <yaz/snprintf.h>
25 #include <yaz/facet.h>
27 #include <yaz/shptr.h>
33 static int log_api0 = 0;
34 static int log_details0 = 0;
36 static void resultset_destroy(ZOOM_resultset r);
37 static zoom_ret do_write_ex(ZOOM_connection c, char *buf_out, int len_out);
39 ZOOM_API(const char *) ZOOM_get_event_str(int event)
41 static const char *ar[] = {
57 static void initlog(void)
59 static int log_level_initialized = 0;
60 if (!log_level_initialized)
62 log_api0 = yaz_log_module_level("zoom");
63 log_details0 = yaz_log_module_level("zoomdetails");
64 log_level_initialized = 1;
68 ZOOM_Event ZOOM_Event_create(int kind)
70 ZOOM_Event event = (ZOOM_Event) xmalloc(sizeof(*event));
77 static void ZOOM_Event_destroy(ZOOM_Event event)
82 void ZOOM_connection_put_event(ZOOM_connection c, ZOOM_Event event)
86 c->m_queue_back->prev = event;
87 assert(c->m_queue_front);
91 assert(!c->m_queue_front);
92 c->m_queue_front = event;
94 event->next = c->m_queue_back;
96 c->m_queue_back = event;
99 static ZOOM_Event ZOOM_connection_get_event(ZOOM_connection c)
101 ZOOM_Event event = c->m_queue_front;
104 c->last_event = ZOOM_EVENT_NONE;
107 assert(c->m_queue_back);
108 c->m_queue_front = event->prev;
109 if (c->m_queue_front)
111 assert(c->m_queue_back);
112 c->m_queue_front->next = 0;
116 c->last_event = event->kind;
120 static void ZOOM_connection_remove_events(ZOOM_connection c)
123 while ((event = ZOOM_connection_get_event(c)))
124 ZOOM_Event_destroy(event);
127 ZOOM_API(int) ZOOM_connection_peek_event(ZOOM_connection c)
129 ZOOM_Event event = c->m_queue_front;
131 return event ? event->kind : ZOOM_EVENT_NONE;
134 void ZOOM_connection_remove_tasks(ZOOM_connection c);
136 void ZOOM_set_dset_error(ZOOM_connection c, int error,
138 const char *addinfo, const char *addinfo2)
145 if (!c->diagset || strcmp(dset, c->diagset))
148 c->diagset = xstrdup(dset);
149 /* remove integer part from SRW diagset .. */
150 if ((cp = strrchr(c->diagset, '/')))
153 if (addinfo && addinfo2)
155 c->addinfo = (char*) xmalloc(strlen(addinfo) + strlen(addinfo2) + 2);
156 strcpy(c->addinfo, addinfo);
157 strcat(c->addinfo, addinfo2);
160 c->addinfo = xstrdup(addinfo);
161 if (error != ZOOM_ERROR_NONE)
163 yaz_log(c->log_api, "%p set_dset_error %s %s:%d %s %s",
164 c, c->host_port ? c->host_port : "<>", dset, error,
165 addinfo ? addinfo : "",
166 addinfo2 ? addinfo2 : "");
167 ZOOM_connection_remove_tasks(c);
171 int ZOOM_uri_to_code(const char *uri)
175 if ((cp = strrchr(uri, '/')))
182 static void set_HTTP_error(ZOOM_connection c, int error,
183 const char *addinfo, const char *addinfo2)
185 ZOOM_set_dset_error(c, error, "HTTP", addinfo, addinfo2);
188 static void set_SRU_error(ZOOM_connection c, Z_SRW_diagnostic *d)
190 const char *uri = d->uri;
192 ZOOM_set_dset_error(c, ZOOM_uri_to_code(uri), uri, d->details, 0);
198 void ZOOM_set_error(ZOOM_connection c, int error, const char *addinfo)
200 ZOOM_set_dset_error(c, error, "ZOOM", addinfo, 0);
203 static void clear_error(ZOOM_connection c)
206 * If an error is tied to an operation then it's ok to clear: for
207 * example, a diagnostic returned from a search is cleared by a
208 * subsequent search. However, problems such as Connection Lost
209 * or Init Refused are not cleared, because they are not
210 * recoverable: doing another search doesn't help.
213 ZOOM_connection_remove_events(c);
216 case ZOOM_ERROR_CONNECT:
217 case ZOOM_ERROR_MEMORY:
218 case ZOOM_ERROR_DECODE:
219 case ZOOM_ERROR_CONNECTION_LOST:
220 case ZOOM_ERROR_INIT:
221 case ZOOM_ERROR_INTERNAL:
222 case ZOOM_ERROR_UNSUPPORTED_PROTOCOL:
225 ZOOM_set_error(c, ZOOM_ERROR_NONE, 0);
229 void ZOOM_connection_show_task(ZOOM_task task)
233 case ZOOM_TASK_SEARCH:
234 yaz_log(YLOG_LOG, "search p=%p", task);
236 case ZOOM_TASK_RETRIEVE:
237 yaz_log(YLOG_LOG, "retrieve p=%p", task);
239 case ZOOM_TASK_CONNECT:
240 yaz_log(YLOG_LOG, "connect p=%p", task);
243 yaz_log(YLOG_LOG, "scan p=%p", task);
248 void ZOOM_connection_show_tasks(ZOOM_connection c)
251 yaz_log(YLOG_LOG, "connection p=%p tasks", c);
252 for (task = c->tasks; task; task = task->next)
253 ZOOM_connection_show_task(task);
256 ZOOM_task ZOOM_connection_add_task(ZOOM_connection c, int which)
258 ZOOM_task *taskp = &c->tasks;
260 taskp = &(*taskp)->next;
261 *taskp = (ZOOM_task) xmalloc(sizeof(**taskp));
262 (*taskp)->running = 0;
263 (*taskp)->which = which;
269 ZOOM_API(int) ZOOM_connection_is_idle(ZOOM_connection c)
271 return c->tasks ? 0 : 1;
274 ZOOM_task ZOOM_connection_insert_task(ZOOM_connection c, int which)
276 ZOOM_task task = (ZOOM_task) xmalloc(sizeof(*task));
278 task->next = c->tasks;
287 void ZOOM_connection_remove_task(ZOOM_connection c)
289 ZOOM_task task = c->tasks;
293 c->tasks = task->next;
296 case ZOOM_TASK_SEARCH:
297 resultset_destroy(task->u.search.resultset);
298 xfree(task->u.search.syntax);
299 xfree(task->u.search.elementSetName);
301 case ZOOM_TASK_RETRIEVE:
302 resultset_destroy(task->u.retrieve.resultset);
303 xfree(task->u.retrieve.syntax);
304 xfree(task->u.retrieve.elementSetName);
306 case ZOOM_TASK_CONNECT:
309 ZOOM_scanset_destroy(task->u.scan.scan);
311 case ZOOM_TASK_PACKAGE:
312 ZOOM_package_destroy(task->u.package);
315 resultset_destroy(task->u.sort.resultset);
316 ZOOM_query_destroy(task->u.sort.q);
325 ZOOM_Event event = ZOOM_Event_create(ZOOM_EVENT_END);
326 ZOOM_connection_put_event(c, event);
331 void ZOOM_connection_remove_tasks(ZOOM_connection c)
334 ZOOM_connection_remove_task(c);
338 ZOOM_API(ZOOM_connection)
339 ZOOM_connection_create(ZOOM_options options)
341 ZOOM_connection c = (ZOOM_connection) xmalloc(sizeof(*c));
345 c->log_api = log_api0;
346 c->log_details = log_details0;
348 yaz_log(c->log_api, "%p ZOOM_connection_create", c);
350 c->proto = PROTO_Z3950;
352 ZOOM_connection_set_mask(c, 0);
354 c->state = STATE_IDLE;
357 ZOOM_set_error(c, ZOOM_ERROR_NONE, 0);
364 c->options = ZOOM_options_create_with_parent(options);
370 c->charset = c->lang = 0;
381 c->maximum_record_size = 0;
382 c->preferred_message_size = 0;
384 c->odr_in = odr_createmem(ODR_DECODE);
385 c->odr_out = odr_createmem(ODR_ENCODE);
389 c->support_named_resultsets = 0;
390 c->last_event = ZOOM_EVENT_NONE;
392 c->m_queue_front = 0;
401 /* set database names. Take local databases (if set); otherwise
402 take databases given in ZURL (if set); otherwise use Default */
403 char **ZOOM_connection_get_databases(ZOOM_connection con, ZOOM_options options,
406 char **databaseNames;
407 const char *cp = ZOOM_options_get(options, "databaseName");
409 if ((!cp || !*cp) && con->host_port)
411 if (strncmp(con->host_port, "unix:", 5) == 0)
412 cp = strchr(con->host_port+5, ':');
414 cp = strchr(con->host_port, '/');
420 nmem_strsplit(odr_getmem(odr), "+", cp, &databaseNames, num);
421 return databaseNames;
424 ZOOM_API(ZOOM_connection)
425 ZOOM_connection_new(const char *host, int portnum)
427 ZOOM_connection c = ZOOM_connection_create(0);
429 ZOOM_connection_connect(c, host, portnum);
433 static zoom_sru_mode get_sru_mode_from_string(const char *s)
436 return zoom_sru_soap;
437 if (!yaz_matchstr(s, "soap"))
438 return zoom_sru_soap;
439 else if (!yaz_matchstr(s, "get"))
441 else if (!yaz_matchstr(s, "post"))
442 return zoom_sru_post;
443 return zoom_sru_error;
447 ZOOM_connection_connect(ZOOM_connection c,
448 const char *host, int portnum)
455 yaz_log(c->log_api, "%p ZOOM_connection_connect host=%s portnum=%d",
456 c, host ? host : "null", portnum);
458 ZOOM_set_error(c, ZOOM_ERROR_NONE, 0);
459 ZOOM_connection_remove_tasks(c);
463 odr_setprint(c->odr_print, 0); /* prevent destroy from fclose'ing */
464 odr_destroy(c->odr_print);
466 if (ZOOM_options_get_bool(c->options, "apdulog", 0))
468 c->odr_print = odr_createmem(ODR_PRINT);
469 odr_setprint(c->odr_print, yaz_log_file());
476 yaz_log(c->log_details, "%p ZOOM_connection_connect reconnect ok", c);
480 yaz_log(c->log_details, "%p ZOOM_connection_connect connect", c);
483 val = ZOOM_options_get(c->options, "proxy");
486 yaz_log(c->log_details, "%p ZOOM_connection_connect proxy=%s", c, val);
487 c->proxy = xstrdup(val);
492 val = ZOOM_options_get(c->options, "charset");
495 yaz_log(c->log_details, "%p ZOOM_connection_connect charset=%s", c, val);
496 c->charset = xstrdup(val);
500 val = ZOOM_options_get(c->options, "lang");
503 yaz_log(c->log_details, "%p ZOOM_connection_connect lang=%s", c, val);
504 c->lang = xstrdup(val);
515 sprintf(hostn, "%.80s:%d", host, portnum);
516 c->host_port = xstrdup(hostn);
519 c->host_port = xstrdup(host);
524 * If the "<scheme>:" part of the host string is preceded by one
525 * or more comma-separated <name>=<value> pairs, these are taken
526 * to be options to be set on the connection object. Among other
527 * applications, this facility can be used to embed authentication
529 * user=admin,password=secret,tcp:localhost:9999
531 char *remainder = c->host_port;
532 char *pcolon = strchr(remainder, ':');
535 while ((pcomma = strchr(remainder, ',')) != 0 &&
536 (pcolon == 0 || pcomma < pcolon)) {
538 if ((pequals = strchr(remainder, '=')) != 0) {
540 /*printf("# setting '%s'='%s'\n", remainder, pequals+1);*/
541 ZOOM_connection_option_set(c, remainder, pequals+1);
543 remainder = pcomma+1;
546 if (remainder != c->host_port) {
548 c->host_port = xstrdup(remainder);
549 /*printf("# reset hp='%s'\n", remainder);*/
553 val = ZOOM_options_get(c->options, "sru");
554 c->sru_mode = get_sru_mode_from_string(val);
556 xfree(c->sru_version);
557 val = ZOOM_options_get(c->options, "sru_version");
558 c->sru_version = xstrdup(val ? val : "1.2");
560 ZOOM_options_set(c->options, "host", c->host_port);
562 xfree(c->cookie_out);
564 val = ZOOM_options_get(c->options, "cookie");
567 yaz_log(c->log_details, "%p ZOOM_connection_connect cookie=%s", c, val);
568 c->cookie_out = xstrdup(val);
573 val = ZOOM_options_get(c->options, "clientIP");
576 yaz_log(c->log_details, "%p ZOOM_connection_connect clientIP=%s",
578 c->client_IP = xstrdup(val);
583 val = ZOOM_options_get(c->options, "group");
585 c->group = xstrdup(val);
589 val = ZOOM_options_get(c->options, "user");
591 c->user = xstrdup(val);
595 val = ZOOM_options_get(c->options, "password");
597 val = ZOOM_options_get(c->options, "pass");
600 c->password = xstrdup(val);
602 c->maximum_record_size =
603 ZOOM_options_get_int(c->options, "maximumRecordSize", 1024*1024);
604 c->preferred_message_size =
605 ZOOM_options_get_int(c->options, "preferredMessageSize", 1024*1024);
607 c->async = ZOOM_options_get_bool(c->options, "async", 0);
608 yaz_log(c->log_details, "%p ZOOM_connection_connect async=%d", c, c->async);
610 task = ZOOM_connection_add_task(c, ZOOM_TASK_CONNECT);
614 while (ZOOM_event(1, &c))
619 ZOOM_API(void) ZOOM_resultset_release(ZOOM_resultset r)
621 #if ZOOM_RESULT_LISTS
625 /* remove ourselves from the resultsets in connection */
626 ZOOM_resultset *rp = &r->connection->resultsets;
629 assert(*rp); /* we must be in this list!! */
631 { /* OK, we're here - take us out of it */
643 ZOOM_connection_destroy(ZOOM_connection c)
645 #if ZOOM_RESULT_LISTS
646 ZOOM_resultsets list;
652 yaz_log(c->log_api, "%p ZOOM_connection_destroy", c);
656 #if ZOOM_RESULT_LISTS
657 // Remove the connection's usage of resultsets
658 list = c->resultsets;
660 ZOOM_resultsets removed = list;
661 ZOOM_resultset_destroy(list->resultset);
666 for (r = c->resultsets; r; r = r->next)
673 odr_destroy(c->odr_in);
674 odr_destroy(c->odr_out);
677 odr_setprint(c->odr_print, 0); /* prevent destroy from fclose'ing */
678 odr_destroy(c->odr_print);
680 ZOOM_options_destroy(c->options);
681 ZOOM_connection_remove_tasks(c);
682 ZOOM_connection_remove_events(c);
688 xfree(c->cookie_out);
694 xfree(c->sru_version);
698 void ZOOM_resultset_addref(ZOOM_resultset r)
702 yaz_mutex_enter(r->mutex);
704 yaz_log(log_details0, "%p ZOOM_resultset_addref count=%d",
706 yaz_mutex_leave(r->mutex);
710 ZOOM_resultset ZOOM_resultset_create(void)
713 ZOOM_resultset r = (ZOOM_resultset) xmalloc(sizeof(*r));
717 yaz_log(log_details0, "%p ZOOM_resultset_create", r);
720 r->odr = odr_createmem(ODR_ENCODE);
725 for (i = 0; i<RECORD_HASH_SIZE; i++)
726 r->record_hash[i] = 0;
730 r->databaseNames = 0;
731 r->num_databaseNames = 0;
736 yaz_mutex_create(&r->mutex);
739 WRBUF w = wrbuf_alloc();
740 YAZ_SHPTR_INIT(r->record_wrbuf, w);
746 ZOOM_API(ZOOM_resultset)
747 ZOOM_connection_search_pqf(ZOOM_connection c, const char *q)
750 ZOOM_query s = ZOOM_query_create();
752 ZOOM_query_prefix(s, q);
754 r = ZOOM_connection_search(c, s);
755 ZOOM_query_destroy(s);
759 ZOOM_API(ZOOM_resultset)
760 ZOOM_connection_search(ZOOM_connection c, ZOOM_query q)
762 ZOOM_resultset r = ZOOM_resultset_create();
766 const char *syntax, *elementSetName;
767 #if ZOOM_RESULT_LISTS
771 yaz_log(c->log_api, "%p ZOOM_connection_search set %p query %p", c, r, q);
772 r->r_sort_spec = ZOOM_query_get_sortspec(q);
775 r->options = ZOOM_options_create_with_parent(c->options);
777 start = ZOOM_options_get_int(r->options, "start", 0);
778 count = ZOOM_options_get_int(r->options, "count", 0);
780 /* If "presentChunk" is defined use that; otherwise "step" */
781 const char *cp = ZOOM_options_get(r->options, "presentChunk");
782 r->step = ZOOM_options_get_int(r->options,
783 (cp != 0 ? "presentChunk": "step"), 0);
785 r->piggyback = ZOOM_options_get_bool(r->options, "piggyback", 1);
786 cp = ZOOM_options_get(r->options, "setname");
788 r->setname = xstrdup(cp);
789 cp = ZOOM_options_get(r->options, "schema");
791 r->schema = xstrdup(cp);
793 r->databaseNames = ZOOM_connection_get_databases(c, c->options, &r->num_databaseNames,
798 #if ZOOM_RESULT_LISTS
799 yaz_log(log_details, "%p ZOOM_connection_search: Adding new resultset (%p) to resultsets (%p) ", c, r, c->resultsets);
800 set = xmalloc(sizeof(*set));
801 ZOOM_resultset_addref(r);
803 set->next = c->resultsets;
806 r->next = c->resultsets;
809 if (c->host_port && c->proto == PROTO_HTTP)
813 yaz_log(c->log_details, "ZOOM_connection_search: no comstack");
814 ZOOM_connection_add_task(c, ZOOM_TASK_CONNECT);
818 yaz_log(c->log_details, "ZOOM_connection_search: reconnect");
823 task = ZOOM_connection_add_task(c, ZOOM_TASK_SEARCH);
824 task->u.search.resultset = r;
825 task->u.search.start = start;
826 task->u.search.count = count;
827 task->u.search.recv_search_fired = 0;
829 syntax = ZOOM_options_get(r->options, "preferredRecordSyntax");
830 task->u.search.syntax = syntax ? xstrdup(syntax) : 0;
831 elementSetName = ZOOM_options_get(r->options, "elementSetName");
832 task->u.search.elementSetName = elementSetName
833 ? xstrdup(elementSetName) : 0;
835 ZOOM_resultset_addref(r);
837 ZOOM_query_addref(q);
841 while (ZOOM_event(1, &c))
848 ZOOM_resultset_sort(ZOOM_resultset r,
849 const char *sort_type, const char *sort_spec)
851 (void) ZOOM_resultset_sort1(r, sort_type, sort_spec);
855 ZOOM_resultset_sort1(ZOOM_resultset r,
856 const char *sort_type, const char *sort_spec)
858 ZOOM_connection c = r->connection;
862 newq = ZOOM_query_create();
863 if (ZOOM_query_sortby(newq, sort_spec) < 0)
866 yaz_log(c->log_api, "%p ZOOM_resultset_sort r=%p sort_type=%s sort_spec=%s",
867 r, r, sort_type, sort_spec);
871 if (c->host_port && c->proto == PROTO_HTTP)
875 yaz_log(c->log_details, "%p ZOOM_resultset_sort: no comstack", r);
876 ZOOM_connection_add_task(c, ZOOM_TASK_CONNECT);
880 yaz_log(c->log_details, "%p ZOOM_resultset_sort: prepare reconnect",
886 ZOOM_resultset_cache_reset(r);
887 task = ZOOM_connection_add_task(c, ZOOM_TASK_SORT);
888 task->u.sort.resultset = r;
889 task->u.sort.q = newq;
891 ZOOM_resultset_addref(r);
895 while (ZOOM_event(1, &c))
903 ZOOM_resultset_destroy(ZOOM_resultset r)
905 resultset_destroy(r);
908 static void resultset_destroy(ZOOM_resultset r)
912 yaz_mutex_enter(r->mutex);
914 yaz_log(log_details0, "%p ZOOM_resultset_destroy r=%p count=%d",
916 if (r->refcount == 0)
918 yaz_mutex_leave(r->mutex);
920 yaz_log(log_details0, "%p ZOOM_connection resultset_destroy: Deleting resultset (%p) ", r->connection, r);
921 ZOOM_resultset_cache_reset(r);
922 ZOOM_resultset_release(r);
923 ZOOM_query_destroy(r->query);
924 ZOOM_options_destroy(r->options);
928 yaz_mutex_destroy(&r->mutex);
930 YAZ_SHPTR_DEC(r->record_wrbuf, wrbuf_destroy);
935 yaz_mutex_leave(r->mutex);
939 ZOOM_resultset_size(ZOOM_resultset r)
944 int ZOOM_test_reconnect(ZOOM_connection c)
948 if (!c->reconnect_ok)
950 ZOOM_connection_close(c);
952 c->tasks->running = 0;
953 ZOOM_connection_insert_task(c, ZOOM_TASK_CONNECT);
955 event = ZOOM_Event_create(ZOOM_EVENT_CONNECT);
956 ZOOM_connection_put_event(c, event);
961 static void ZOOM_resultset_retrieve(ZOOM_resultset r,
962 int force_sync, int start, int count)
967 const char *syntax, *elementSetName;
971 yaz_log(log_details0, "%p ZOOM_resultset_retrieve force_sync=%d start=%d"
972 " count=%d", r, force_sync, start, count);
977 if (c->host_port && c->proto == PROTO_HTTP)
981 yaz_log(log_details0, "%p ZOOM_resultset_retrieve: no comstack", r);
982 ZOOM_connection_add_task(c, ZOOM_TASK_CONNECT);
986 yaz_log(log_details0, "%p ZOOM_resultset_retrieve: prepare "
991 task = ZOOM_connection_add_task(c, ZOOM_TASK_RETRIEVE);
992 task->u.retrieve.resultset = r;
993 task->u.retrieve.start = start;
994 task->u.retrieve.count = count;
996 syntax = ZOOM_options_get(r->options, "preferredRecordSyntax");
997 task->u.retrieve.syntax = syntax ? xstrdup(syntax) : 0;
998 elementSetName = ZOOM_options_get(r->options, "elementSetName");
999 task->u.retrieve.elementSetName = elementSetName
1000 ? xstrdup(elementSetName) : 0;
1002 cp = ZOOM_options_get(r->options, "schema");
1005 if (!r->schema || strcmp(r->schema, cp))
1008 r->schema = xstrdup(cp);
1012 ZOOM_resultset_addref(r);
1014 if (!r->connection->async || force_sync)
1015 while (r->connection && ZOOM_event(1, &r->connection))
1020 ZOOM_resultset_records(ZOOM_resultset r, ZOOM_record *recs,
1021 size_t start, size_t count)
1023 int force_present = 0;
1027 yaz_log(log_api0, "%p ZOOM_resultset_records r=%p start=%ld count=%ld",
1028 r, r, (long) start, (long) count);
1031 ZOOM_resultset_retrieve(r, force_present, start, count);
1035 for (i = 0; i< count; i++)
1036 recs[i] = ZOOM_resultset_record_immediate(r, i+start);
1041 ZOOM_resultset_facets_size(ZOOM_resultset r) {
1042 return r->num_facets;
1045 ZOOM_API(ZOOM_facet_field)
1046 ZOOM_resultset_get_facet_field(ZOOM_resultset r, const char *name) {
1047 int num = r->num_facets;
1048 ZOOM_facet_field *facets = r->facets;
1050 for (index = 0; index < num; index++) {
1051 if (!strcmp(facets[index]->facet_name, name)) {
1052 return facets[index];
1059 ZOOM_API(ZOOM_facet_field *)
1060 ZOOM_resultset_facets(ZOOM_resultset r)
1065 ZOOM_API(const char**)
1066 ZOOM_resultset_facet_names(ZOOM_resultset r)
1068 return (const char **) r->facets_names;
1071 ZOOM_API(const char*)
1072 ZOOM_facet_field_name(ZOOM_facet_field field)
1074 return field->facet_name;
1078 ZOOM_facet_field_term_count(ZOOM_facet_field field)
1080 return field->num_terms;
1083 ZOOM_API(const char*)
1084 ZOOM_facet_field_get_term(ZOOM_facet_field field, size_t idx, int *freq) {
1085 *freq = field->facet_terms[idx].frequency;
1086 return field->facet_terms[idx].term;
1090 static void get_cert(ZOOM_connection c)
1095 if (cs_get_peer_certificate_x509(c->cs, &cert_buf, &cert_len))
1097 ZOOM_connection_option_setl(c, "sslPeerCert",
1098 cert_buf, cert_len);
1103 static zoom_ret do_connect_host(ZOOM_connection c,
1104 const char *effective_host,
1105 const char *logical_url);
1107 static zoom_ret do_connect(ZOOM_connection c)
1109 const char *effective_host;
1112 effective_host = c->proxy;
1114 effective_host = c->host_port;
1115 return do_connect_host(c, effective_host, c->host_port);
1118 static zoom_ret do_connect_host(ZOOM_connection c, const char *effective_host,
1119 const char *logical_url)
1123 yaz_log(c->log_details, "%p do_connect effective_host=%s", c, effective_host);
1127 c->cs = cs_create_host(effective_host, 0, &add);
1129 if (c->cs && c->cs->protocol == PROTO_HTTP)
1136 c->proto = PROTO_HTTP;
1137 cs_get_host_args(logical_url, &db);
1140 c->path = xmalloc(strlen(db) * 3 + 2);
1141 yaz_encode_sru_dbpath_buf(c->path, db);
1144 ZOOM_set_error(c, ZOOM_ERROR_UNSUPPORTED_PROTOCOL, "SRW");
1145 ZOOM_connection_close(c);
1146 return zoom_complete;
1151 int ret = cs_connect(c->cs, add);
1154 ZOOM_Event event = ZOOM_Event_create(ZOOM_EVENT_CONNECT);
1155 ZOOM_connection_put_event(c, event);
1157 if (c->proto == PROTO_Z3950)
1158 ZOOM_connection_Z3950_send_init(c);
1161 /* no init request for SRW .. */
1162 assert(c->tasks->which == ZOOM_TASK_CONNECT);
1163 ZOOM_connection_remove_task(c);
1164 ZOOM_connection_set_mask(c, 0);
1165 ZOOM_connection_exec_task(c);
1167 c->state = STATE_ESTABLISHED;
1168 return zoom_pending;
1172 int mask = ZOOM_SELECT_EXCEPT;
1173 if (c->cs->io_pending & CS_WANT_WRITE)
1174 mask += ZOOM_SELECT_WRITE;
1175 if (c->cs->io_pending & CS_WANT_READ)
1176 mask += ZOOM_SELECT_READ;
1177 ZOOM_connection_set_mask(c, mask);
1178 c->state = STATE_CONNECTING;
1179 return zoom_pending;
1182 c->state = STATE_IDLE;
1183 ZOOM_set_error(c, ZOOM_ERROR_CONNECT, logical_url);
1184 return zoom_complete;
1187 /* returns 1 if PDU was sent OK (still pending )
1188 0 if PDU was not sent OK (nothing to wait for)
1192 static zoom_ret send_srw(ZOOM_connection c, Z_SRW_PDU *sr)
1196 const char *database = ZOOM_options_get(c->options, "databaseName");
1197 char *fdatabase = 0;
1200 fdatabase = yaz_encode_sru_dbpath_odr(c->odr_out, database);
1201 gdu = z_get_HTTP_Request_host_path(c->odr_out, c->host_port,
1202 fdatabase ? fdatabase : c->path);
1204 if (c->sru_mode == zoom_sru_get)
1206 yaz_sru_get_encode(gdu->u.HTTP_Request, sr, c->odr_out, c->charset);
1208 else if (c->sru_mode == zoom_sru_post)
1210 yaz_sru_post_encode(gdu->u.HTTP_Request, sr, c->odr_out, c->charset);
1212 else if (c->sru_mode == zoom_sru_soap)
1214 yaz_sru_soap_encode(gdu->u.HTTP_Request, sr, c->odr_out, c->charset);
1216 if (!z_GDU(c->odr_out, &gdu, 0, 0))
1217 return zoom_complete;
1219 z_GDU(c->odr_print, &gdu, 0, 0);
1220 c->buf_out = odr_getbuf(c->odr_out, &c->len_out, 0);
1222 event = ZOOM_Event_create(ZOOM_EVENT_SEND_APDU);
1223 ZOOM_connection_put_event(c, event);
1224 odr_reset(c->odr_out);
1225 return ZOOM_send_buf(c);
1230 static Z_SRW_PDU *ZOOM_srw_get_pdu(ZOOM_connection c, int type)
1232 Z_SRW_PDU *sr = yaz_srw_get_pdu(c->odr_out, type, c->sru_version);
1233 sr->username = c->user;
1234 sr->password = c->password;
1240 static zoom_ret ZOOM_connection_srw_send_search(ZOOM_connection c)
1244 ZOOM_resultset resultset = 0;
1246 const char *option_val = 0;
1249 if (c->error) /* don't continue on error */
1250 return zoom_complete;
1252 switch(c->tasks->which)
1254 case ZOOM_TASK_SEARCH:
1255 resultset = c->tasks->u.search.resultset;
1256 if (!resultset->setname)
1257 resultset->setname = xstrdup("default");
1258 ZOOM_options_set(resultset->options, "setname", resultset->setname);
1259 start = &c->tasks->u.search.start;
1260 count = &c->tasks->u.search.count;
1262 case ZOOM_TASK_RETRIEVE:
1263 resultset = c->tasks->u.retrieve.resultset;
1265 start = &c->tasks->u.retrieve.start;
1266 count = &c->tasks->u.retrieve.count;
1268 if (*start >= resultset->size)
1269 return zoom_complete;
1270 if (*start + *count > resultset->size)
1271 *count = resultset->size - *start;
1273 for (i = 0; i < *count; i++)
1276 ZOOM_record_cache_lookup(resultset, i + *start,
1277 c->tasks->u.retrieve.syntax,
1278 c->tasks->u.retrieve.elementSetName);
1283 ZOOM_Event event = ZOOM_Event_create(ZOOM_EVENT_RECV_RECORD);
1284 ZOOM_connection_put_event(c, event);
1291 return zoom_complete;
1294 return zoom_complete;
1296 assert(resultset->query);
1298 sr = ZOOM_srw_get_pdu(c, Z_SRW_searchRetrieve_request);
1299 z_query = ZOOM_query_get_Z_Query(resultset->query);
1301 if (z_query->which == Z_Query_type_104
1302 && z_query->u.type_104->which == Z_External_CQL)
1304 sr->u.request->query_type = Z_SRW_query_type_cql;
1305 sr->u.request->query.cql = z_query->u.type_104->u.cql;
1307 else if (z_query->which == Z_Query_type_1 && z_query->u.type_1)
1309 sr->u.request->query_type = Z_SRW_query_type_pqf;
1310 sr->u.request->query.pqf =
1311 ZOOM_query_get_query_string(resultset->query);
1315 ZOOM_set_error(c, ZOOM_ERROR_UNSUPPORTED_QUERY, 0);
1316 return zoom_complete;
1318 sr->u.request->startRecord = odr_intdup(c->odr_out, *start + 1);
1319 sr->u.request->maximumRecords = odr_intdup(
1320 c->odr_out, (resultset->step > 0 && resultset->step < *count) ?
1321 resultset->step : *count);
1322 sr->u.request->recordSchema = resultset->schema;
1324 option_val = ZOOM_resultset_option_get(resultset, "recordPacking");
1326 sr->u.request->recordPacking = odr_strdup(c->odr_out, option_val);
1328 option_val = ZOOM_resultset_option_get(resultset, "extraArgs");
1329 yaz_encode_sru_extra(sr, c->odr_out, option_val);
1330 return send_srw(c, sr);
1333 static zoom_ret ZOOM_connection_srw_send_search(ZOOM_connection c)
1335 return zoom_complete;
1339 ZOOM_API(ZOOM_record)
1340 ZOOM_resultset_record_immediate(ZOOM_resultset s,size_t pos)
1342 const char *syntax =
1343 ZOOM_options_get(s->options, "preferredRecordSyntax");
1344 const char *elementSetName =
1345 ZOOM_options_get(s->options, "elementSetName");
1347 return ZOOM_record_cache_lookup(s, pos, syntax, elementSetName);
1350 ZOOM_API(ZOOM_record)
1351 ZOOM_resultset_record(ZOOM_resultset r, size_t pos)
1353 ZOOM_record rec = ZOOM_resultset_record_immediate(r, pos);
1358 * MIKE: I think force_sync should always be zero, but I don't
1359 * want to make this change until I get the go-ahead from
1360 * Adam, in case something depends on the old synchronous
1364 if (getenv("ZOOM_RECORD_NO_FORCE_SYNC")) force_sync = 0;
1365 ZOOM_resultset_retrieve(r, force_sync, pos, 1);
1366 rec = ZOOM_resultset_record_immediate(r, pos);
1371 static yaz_iconv_t iconv_create_charset(const char *record_charset)
1378 strcpy(to, "UTF-8");
1379 if (record_charset && *record_charset)
1381 /* Use "from,to" or just "from" */
1382 const char *cp = strchr(record_charset, ',');
1383 size_t clen = strlen(record_charset);
1386 strncpy( to, cp+1, sizeof(to)-1);
1387 to[sizeof(to)-1] = '\0';
1388 clen = cp - record_charset;
1390 if (clen > sizeof(from)-1)
1391 clen = sizeof(from)-1;
1394 strncpy(from, record_charset, clen);
1398 cd = yaz_iconv_open(to, from);
1402 static const char *return_marc_record(WRBUF wrbuf,
1405 const char *buf, int sz,
1406 const char *record_charset)
1408 yaz_iconv_t cd = iconv_create_charset(record_charset);
1409 yaz_marc_t mt = yaz_marc_create();
1410 const char *ret_string = 0;
1413 yaz_marc_iconv(mt, cd);
1414 yaz_marc_xml(mt, marc_type);
1415 if (yaz_marc_decode_wrbuf(mt, buf, sz, wrbuf) > 0)
1418 *len = wrbuf_len(wrbuf);
1419 ret_string = wrbuf_cstr(wrbuf);
1421 yaz_marc_destroy(mt);
1423 yaz_iconv_close(cd);
1427 static const char *return_opac_record(WRBUF wrbuf,
1430 Z_OPACRecord *opac_rec,
1431 const char *record_charset)
1433 yaz_iconv_t cd = iconv_create_charset(record_charset);
1434 yaz_marc_t mt = yaz_marc_create();
1437 yaz_marc_iconv(mt, cd);
1438 yaz_marc_xml(mt, marc_type);
1440 yaz_opac_decode_wrbuf(mt, opac_rec, wrbuf);
1441 yaz_marc_destroy(mt);
1444 yaz_iconv_close(cd);
1446 *len = wrbuf_len(wrbuf);
1447 return wrbuf_cstr(wrbuf);
1450 static const char *return_string_record(WRBUF wrbuf,
1452 const char *buf, int sz,
1453 const char *record_charset)
1455 yaz_iconv_t cd = iconv_create_charset(record_charset);
1459 wrbuf_iconv_write(wrbuf, cd, buf, sz);
1460 wrbuf_iconv_reset(wrbuf, cd);
1462 buf = wrbuf_cstr(wrbuf);
1463 sz = wrbuf_len(wrbuf);
1464 yaz_iconv_close(cd);
1471 static const char *return_record_wrbuf(WRBUF wrbuf, int *len,
1472 Z_NamePlusRecord *npr,
1473 int marctype, const char *charset)
1475 Z_External *r = (Z_External *) npr->u.databaseRecord;
1476 const Odr_oid *oid = r->direct_reference;
1478 wrbuf_rewind(wrbuf);
1479 /* render bibliographic record .. */
1480 if (r->which == Z_External_OPAC)
1482 return return_opac_record(wrbuf, marctype, len,
1483 r->u.opac, charset);
1485 if (r->which == Z_External_sutrs)
1486 return return_string_record(wrbuf, len,
1487 (char*) r->u.sutrs->buf,
1490 else if (r->which == Z_External_octet)
1492 if (yaz_oid_is_iso2709(oid))
1494 const char *ret_buf = return_marc_record(
1495 wrbuf, marctype, len,
1496 (const char *) r->u.octet_aligned->buf,
1497 r->u.octet_aligned->len,
1501 /* bad ISO2709. Return fail unless raw (ISO2709) is wanted */
1502 if (marctype != YAZ_MARC_ISO2709)
1505 return return_string_record(wrbuf, len,
1506 (const char *) r->u.octet_aligned->buf,
1507 r->u.octet_aligned->len,
1510 else if (r->which == Z_External_grs1)
1512 yaz_display_grs1(wrbuf, r->u.grs1, 0);
1513 return return_string_record(wrbuf, len,
1521 static const char *get_record_format(WRBUF wrbuf, int *len,
1522 Z_NamePlusRecord *npr,
1523 int marctype, const char *charset,
1526 const char *res = return_record_wrbuf(wrbuf, len, npr, marctype, charset);
1528 if (*format == '1' && len)
1530 /* try to XML format res */
1532 xmlKeepBlanksDefault(0); /* get get xmlDocFormatMemory to work! */
1533 doc = xmlParseMemory(res, *len);
1538 xmlDocDumpFormatMemory(doc, &xml_mem, &xml_size, 1);
1539 wrbuf_rewind(wrbuf);
1540 wrbuf_write(wrbuf, (const char *) xml_mem, xml_size);
1543 res = wrbuf_cstr(wrbuf);
1544 *len = wrbuf_len(wrbuf);
1552 const char *ZOOM_npr_format(Z_NamePlusRecord *npr, const char *schema,
1554 const char *type_spec, int *len)
1560 const char *cp = type_spec;
1562 for (i = 0; cp[i] && cp[i] != ';' && cp[i] != ' ' && i < sizeof(type)-1;
1570 while (cp[i] == ' ')
1575 while (cp[i] == ' ')
1577 if (!strncmp(cp + i, "charset=", 8))
1580 i = i + 8; /* skip charset= */
1581 for (j = 0; cp[i] && cp[i] != ';' && cp[i] != ' '; i++)
1583 if (j < sizeof(charset)-1)
1584 charset[j++] = cp[i];
1588 else if (!strncmp(cp + i, "format=", 7))
1592 for (j = 0; cp[i] && cp[i] != ';' && cp[i] != ' '; i++)
1594 if (j < sizeof(format)-1)
1595 format[j++] = cp[i];
1600 if (!strcmp(type, "database"))
1603 *len = (npr->databaseName ? strlen(npr->databaseName) : 0);
1604 return npr->databaseName;
1606 else if (!strcmp(type, "schema"))
1609 *len = schema ? strlen(schema) : 0;
1612 else if (!strcmp(type, "syntax"))
1614 const char *desc = 0;
1615 if (npr->which == Z_NamePlusRecord_databaseRecord)
1617 Z_External *r = (Z_External *) npr->u.databaseRecord;
1618 desc = yaz_oid_to_string(yaz_oid_std(), r->direct_reference, 0);
1623 *len = strlen(desc);
1626 if (npr->which != Z_NamePlusRecord_databaseRecord)
1629 /* from now on - we have a database record .. */
1630 if (!strcmp(type, "render"))
1632 return get_record_format(wrbuf, len, npr, YAZ_MARC_LINE, charset, format);
1634 else if (!strcmp(type, "xml"))
1636 return get_record_format(wrbuf, len, npr, YAZ_MARC_MARCXML, charset,
1639 else if (!strcmp(type, "txml"))
1641 return get_record_format(wrbuf, len, npr, YAZ_MARC_TURBOMARC, charset,
1644 else if (!strcmp(type, "raw"))
1646 return get_record_format(wrbuf, len, npr, YAZ_MARC_ISO2709, charset,
1649 else if (!strcmp(type, "ext"))
1652 return (const char *) npr->u.databaseRecord;
1654 else if (!strcmp(type, "opac"))
1656 if (npr->u.databaseRecord->which == Z_External_OPAC)
1657 return get_record_format(wrbuf, len, npr, YAZ_MARC_MARCXML, charset,
1663 ZOOM_API(ZOOM_scanset)
1664 ZOOM_connection_scan(ZOOM_connection c, const char *start)
1667 ZOOM_query q = ZOOM_query_create();
1669 ZOOM_query_prefix(q, start);
1671 s = ZOOM_connection_scan1(c, q);
1672 ZOOM_query_destroy(q);
1677 ZOOM_API(ZOOM_scanset)
1678 ZOOM_connection_scan1(ZOOM_connection c, ZOOM_query q)
1680 ZOOM_scanset scan = 0;
1681 Z_Query *z_query = ZOOM_query_get_Z_Query(q);
1685 scan = (ZOOM_scanset) xmalloc(sizeof(*scan));
1686 scan->connection = c;
1687 scan->odr = odr_createmem(ODR_DECODE);
1688 scan->options = ZOOM_options_create_with_parent(c->options);
1690 scan->scan_response = 0;
1691 scan->srw_scan_response = 0;
1694 ZOOM_query_addref(q);
1695 scan->databaseNames = ZOOM_connection_get_databases(c, c->options,
1696 &scan->num_databaseNames,
1701 ZOOM_task task = ZOOM_connection_add_task(c, ZOOM_TASK_SCAN);
1702 task->u.scan.scan = scan;
1707 while (ZOOM_event(1, &c))
1715 ZOOM_scanset_destroy(ZOOM_scanset scan)
1720 if (scan->refcount == 0)
1722 ZOOM_query_destroy(scan->query);
1724 odr_destroy(scan->odr);
1726 ZOOM_options_destroy(scan->options);
1731 static zoom_ret send_package(ZOOM_connection c)
1735 yaz_log(c->log_details, "%p send_package", c);
1737 return zoom_complete;
1738 assert (c->tasks->which == ZOOM_TASK_PACKAGE);
1740 event = ZOOM_Event_create(ZOOM_EVENT_SEND_APDU);
1741 ZOOM_connection_put_event(c, event);
1743 c->buf_out = c->tasks->u.package->buf_out;
1744 c->len_out = c->tasks->u.package->len_out;
1746 return ZOOM_send_buf(c);
1750 static zoom_ret ZOOM_connection_srw_send_scan(ZOOM_connection c)
1754 const char *option_val = 0;
1758 return zoom_complete;
1759 assert (c->tasks->which == ZOOM_TASK_SCAN);
1760 scan = c->tasks->u.scan.scan;
1762 sr = ZOOM_srw_get_pdu(c, Z_SRW_scan_request);
1764 z_query = ZOOM_query_get_Z_Query(scan->query);
1765 /* SRU scan can only carry CQL and PQF */
1766 if (z_query->which == Z_Query_type_104)
1768 sr->u.scan_request->query_type = Z_SRW_query_type_cql;
1769 sr->u.scan_request->scanClause.cql =
1770 ZOOM_query_get_query_string(scan->query);
1772 else if (z_query->which == Z_Query_type_1
1773 || z_query->which == Z_Query_type_101)
1775 sr->u.scan_request->query_type = Z_SRW_query_type_pqf;
1776 sr->u.scan_request->scanClause.pqf =
1777 ZOOM_query_get_query_string(scan->query);
1781 ZOOM_set_error(c, ZOOM_ERROR_UNSUPPORTED_QUERY, 0);
1782 return zoom_complete;
1785 sr->u.scan_request->maximumTerms = odr_intdup(
1786 c->odr_out, ZOOM_options_get_int(scan->options, "number", 10));
1788 sr->u.scan_request->responsePosition = odr_intdup(
1789 c->odr_out, ZOOM_options_get_int(scan->options, "position", 1));
1791 option_val = ZOOM_options_get(scan->options, "extraArgs");
1792 yaz_encode_sru_extra(sr, c->odr_out, option_val);
1793 return send_srw(c, sr);
1796 static zoom_ret ZOOM_connection_srw_send_scan(ZOOM_connection c)
1798 return zoom_complete;
1804 ZOOM_scanset_size(ZOOM_scanset scan)
1809 if (scan->scan_response && scan->scan_response->entries)
1810 return scan->scan_response->entries->num_entries;
1811 else if (scan->srw_scan_response)
1812 return scan->srw_scan_response->num_terms;
1816 static void ZOOM_scanset_term_x(ZOOM_scanset scan, size_t pos,
1818 const char **value_term, size_t *value_len,
1819 const char **disp_term, size_t *disp_len)
1821 size_t noent = ZOOM_scanset_size(scan);
1832 if (scan->scan_response)
1834 Z_ScanResponse *res = scan->scan_response;
1835 if (res->entries->entries[pos]->which == Z_Entry_termInfo)
1837 Z_TermInfo *t = res->entries->entries[pos]->u.termInfo;
1839 *value_term = (const char *) t->term->u.general->buf;
1840 *value_len = t->term->u.general->len;
1843 *disp_term = t->displayTerm;
1844 *disp_len = strlen(*disp_term);
1846 else if (t->term->which == Z_Term_general)
1848 *disp_term = (const char *) t->term->u.general->buf;
1849 *disp_len = t->term->u.general->len;
1851 *occ = t->globalOccurrences ? *t->globalOccurrences : 0;
1854 if (scan->srw_scan_response)
1856 Z_SRW_scanResponse *res = scan->srw_scan_response;
1857 Z_SRW_scanTerm *t = res->terms + pos;
1860 *value_term = t->value;
1861 *value_len = strlen(*value_term);
1864 *disp_term = t->displayTerm;
1866 *disp_term = t->value;
1867 *disp_len = strlen(*disp_term);
1868 *occ = t->numberOfRecords ? *t->numberOfRecords : 0;
1873 ZOOM_API(const char *)
1874 ZOOM_scanset_term(ZOOM_scanset scan, size_t pos,
1875 size_t *occ, size_t *len)
1877 const char *value_term = 0;
1878 size_t value_len = 0;
1879 const char *disp_term = 0;
1880 size_t disp_len = 0;
1882 ZOOM_scanset_term_x(scan, pos, occ, &value_term, &value_len,
1883 &disp_term, &disp_len);
1889 ZOOM_API(const char *)
1890 ZOOM_scanset_display_term(ZOOM_scanset scan, size_t pos,
1891 size_t *occ, size_t *len)
1893 const char *value_term = 0;
1894 size_t value_len = 0;
1895 const char *disp_term = 0;
1896 size_t disp_len = 0;
1898 ZOOM_scanset_term_x(scan, pos, occ, &value_term, &value_len,
1899 &disp_term, &disp_len);
1905 ZOOM_API(const char *)
1906 ZOOM_scanset_option_get(ZOOM_scanset scan, const char *key)
1908 return ZOOM_options_get(scan->options, key);
1912 ZOOM_scanset_option_set(ZOOM_scanset scan, const char *key,
1915 ZOOM_options_set(scan->options, key, val);
1919 ZOOM_API(ZOOM_package)
1920 ZOOM_connection_package(ZOOM_connection c, ZOOM_options options)
1922 ZOOM_package p = (ZOOM_package) xmalloc(sizeof(*p));
1925 p->odr_out = odr_createmem(ODR_ENCODE);
1926 p->options = ZOOM_options_create_with_parent2(options, c->options);
1934 ZOOM_package_destroy(ZOOM_package p)
1939 if (p->refcount == 0)
1941 odr_destroy(p->odr_out);
1944 ZOOM_options_destroy(p->options);
1949 ZOOM_API(const char *)
1950 ZOOM_package_option_get(ZOOM_package p, const char *key)
1952 return ZOOM_options_get(p->options, key);
1955 ZOOM_API(const char *)
1956 ZOOM_package_option_getl(ZOOM_package p, const char *key, int *lenp)
1958 return ZOOM_options_getl(p->options, key, lenp);
1962 ZOOM_package_option_set(ZOOM_package p, const char *key,
1965 ZOOM_options_set(p->options, key, val);
1969 ZOOM_package_option_setl(ZOOM_package p, const char *key,
1970 const char *val, int len)
1972 ZOOM_options_setl(p->options, key, val, len);
1976 ZOOM_connection_exec_task(ZOOM_connection c)
1978 ZOOM_task task = c->tasks;
1979 zoom_ret ret = zoom_complete;
1983 yaz_log(c->log_details, "%p ZOOM_connection_exec_task type=%d run=%d",
1984 c, task->which, task->running);
1985 if (c->error != ZOOM_ERROR_NONE)
1987 yaz_log(c->log_details, "%p ZOOM_connection_exec_task "
1988 "removing tasks because of error = %d", c, c->error);
1989 ZOOM_connection_remove_tasks(c);
1994 yaz_log(c->log_details, "%p ZOOM_connection_exec_task "
1995 "task already running", c);
1999 ret = zoom_complete;
2000 if (c->cs || task->which == ZOOM_TASK_CONNECT)
2002 switch (task->which)
2004 case ZOOM_TASK_SEARCH:
2005 if (c->proto == PROTO_HTTP)
2006 ret = ZOOM_connection_srw_send_search(c);
2008 ret = ZOOM_connection_Z3950_send_search(c);
2010 case ZOOM_TASK_RETRIEVE:
2011 if (c->proto == PROTO_HTTP)
2012 ret = ZOOM_connection_srw_send_search(c);
2014 ret = send_Z3950_present(c);
2016 case ZOOM_TASK_CONNECT:
2017 ret = do_connect(c);
2019 case ZOOM_TASK_SCAN:
2020 if (c->proto == PROTO_HTTP)
2021 ret = ZOOM_connection_srw_send_scan(c);
2023 ret = ZOOM_connection_Z3950_send_scan(c);
2025 case ZOOM_TASK_PACKAGE:
2026 ret = send_package(c);
2028 case ZOOM_TASK_SORT:
2029 c->tasks->u.sort.resultset->r_sort_spec =
2030 ZOOM_query_get_sortspec(c->tasks->u.sort.q);
2031 ret = send_Z3950_sort(c, c->tasks->u.sort.resultset);
2037 yaz_log(c->log_details, "%p ZOOM_connection_exec_task "
2038 "remove tasks because no connection exist", c);
2039 ZOOM_connection_remove_tasks(c);
2041 if (ret == zoom_complete)
2043 yaz_log(c->log_details, "%p ZOOM_connection_exec_task "
2044 "task removed (complete)", c);
2045 ZOOM_connection_remove_task(c);
2048 yaz_log(c->log_details, "%p ZOOM_connection_exec_task "
2054 static zoom_ret handle_srw_response(ZOOM_connection c,
2055 Z_SRW_searchRetrieveResponse *res)
2057 ZOOM_resultset resultset = 0;
2062 const char *syntax, *elementSetName;
2065 return zoom_complete;
2067 switch(c->tasks->which)
2069 case ZOOM_TASK_SEARCH:
2070 resultset = c->tasks->u.search.resultset;
2071 start = &c->tasks->u.search.start;
2072 count = &c->tasks->u.search.count;
2073 syntax = c->tasks->u.search.syntax;
2074 elementSetName = c->tasks->u.search.elementSetName;
2076 if (!c->tasks->u.search.recv_search_fired)
2078 event = ZOOM_Event_create(ZOOM_EVENT_RECV_SEARCH);
2079 ZOOM_connection_put_event(c, event);
2080 c->tasks->u.search.recv_search_fired = 1;
2083 case ZOOM_TASK_RETRIEVE:
2084 resultset = c->tasks->u.retrieve.resultset;
2085 start = &c->tasks->u.retrieve.start;
2086 count = &c->tasks->u.retrieve.count;
2087 syntax = c->tasks->u.retrieve.syntax;
2088 elementSetName = c->tasks->u.retrieve.elementSetName;
2091 return zoom_complete;
2094 resultset->size = 0;
2096 if (res->resultSetId)
2097 ZOOM_resultset_option_set(resultset, "resultSetId", res->resultSetId);
2099 yaz_log(c->log_details, "%p handle_srw_response got SRW response OK", c);
2101 if (res->num_diagnostics > 0)
2103 set_SRU_error(c, &res->diagnostics[0]);
2107 if (res->numberOfRecords)
2108 resultset->size = *res->numberOfRecords;
2109 for (i = 0; i<res->num_records; i++)
2112 Z_SRW_record *sru_rec;
2113 Z_SRW_diagnostic *diag = 0;
2116 Z_NamePlusRecord *npr = (Z_NamePlusRecord *)
2117 odr_malloc(c->odr_in, sizeof(Z_NamePlusRecord));
2119 if (res->records[i].recordPosition &&
2120 *res->records[i].recordPosition > 0)
2121 pos = *res->records[i].recordPosition - 1;
2125 sru_rec = &res->records[i];
2127 npr->databaseName = 0;
2128 npr->which = Z_NamePlusRecord_databaseRecord;
2129 npr->u.databaseRecord = (Z_External *)
2130 odr_malloc(c->odr_in, sizeof(Z_External));
2131 npr->u.databaseRecord->descriptor = 0;
2132 npr->u.databaseRecord->direct_reference =
2133 odr_oiddup(c->odr_in, yaz_oid_recsyn_xml);
2134 npr->u.databaseRecord->which = Z_External_octet;
2136 npr->u.databaseRecord->u.octet_aligned = (Odr_oct *)
2137 odr_malloc(c->odr_in, sizeof(Odr_oct));
2138 npr->u.databaseRecord->u.octet_aligned->buf = (unsigned char*)
2139 sru_rec->recordData_buf;
2140 npr->u.databaseRecord->u.octet_aligned->len =
2141 npr->u.databaseRecord->u.octet_aligned->size =
2142 sru_rec->recordData_len;
2144 if (sru_rec->recordSchema
2145 && !strcmp(sru_rec->recordSchema,
2146 "info:srw/schema/1/diagnostics-v1.1"))
2148 sru_decode_surrogate_diagnostics(sru_rec->recordData_buf,
2149 sru_rec->recordData_len,
2153 ZOOM_record_cache_add(resultset, npr, pos, syntax, elementSetName,
2154 sru_rec->recordSchema, diag);
2158 if (*count + *start > resultset->size)
2159 *count = resultset->size - *start;
2163 nmem = odr_extract_mem(c->odr_in);
2164 nmem_transfer(odr_getmem(resultset->odr), nmem);
2168 return ZOOM_connection_srw_send_search(c);
2170 return zoom_complete;
2175 static void handle_srw_scan_response(ZOOM_connection c,
2176 Z_SRW_scanResponse *res)
2178 NMEM nmem = odr_extract_mem(c->odr_in);
2181 if (!c->tasks || c->tasks->which != ZOOM_TASK_SCAN)
2183 scan = c->tasks->u.scan.scan;
2185 if (res->num_diagnostics > 0)
2186 set_SRU_error(c, &res->diagnostics[0]);
2188 scan->scan_response = 0;
2189 scan->srw_scan_response = res;
2190 nmem_transfer(odr_getmem(scan->odr), nmem);
2192 ZOOM_options_set_int(scan->options, "number", res->num_terms);
2198 static Z_GDU *get_HTTP_Request_url(ODR odr, const char *url)
2200 Z_GDU *p = z_get_HTTP_Request(odr);
2201 const char *host = url;
2202 const char *cp0 = strstr(host, "://");
2203 const char *cp1 = 0;
2209 cp1 = strchr(cp0, '/');
2211 cp1 = cp0 + strlen(cp0);
2215 char *h = (char*) odr_malloc(odr, cp1 - cp0 + 1);
2216 memcpy (h, cp0, cp1 - cp0);
2218 z_HTTP_header_add(odr, &p->u.HTTP_Request->headers, "Host", h);
2220 p->u.HTTP_Request->path = odr_strdup(odr, *cp1 ? cp1 : "/");
2224 static zoom_ret send_SRW_redirect(ZOOM_connection c, const char *uri,
2225 Z_HTTP_Response *cookie_hres)
2227 struct Z_HTTP_Header *h;
2228 Z_GDU *gdu = get_HTTP_Request_url(c->odr_out, uri);
2229 char *combined_cookies;
2230 int combined_cookies_len = 0;
2232 gdu->u.HTTP_Request->method = odr_strdup(c->odr_out, "GET");
2233 z_HTTP_header_add(c->odr_out, &gdu->u.HTTP_Request->headers, "Accept",
2236 for (h = cookie_hres->headers; h; h = h->next)
2238 if (!strcmp(h->name, "Set-Cookie"))
2242 if (!(cp = strchr(h->value, ';')))
2243 cp = h->value + strlen(h->value);
2244 if (cp - h->value >= 1) {
2245 combined_cookies = xrealloc(combined_cookies, combined_cookies_len + cp - h->value + 3);
2246 memcpy(combined_cookies+combined_cookies_len, h->value, cp - h->value);
2247 combined_cookies[combined_cookies_len + cp - h->value] = '\0';
2248 strcat(combined_cookies,"; ");
2249 combined_cookies_len = strlen(combined_cookies);
2254 if (combined_cookies_len)
2256 z_HTTP_header_add(c->odr_out, &gdu->u.HTTP_Request->headers,
2257 "Cookie", combined_cookies);
2258 xfree(combined_cookies);
2261 if (c->user && c->password)
2263 z_HTTP_header_add_basic_auth(c->odr_out, &gdu->u.HTTP_Request->headers,
2264 c->user, c->password);
2266 if (!z_GDU(c->odr_out, &gdu, 0, 0))
2267 return zoom_complete;
2269 z_GDU(c->odr_print, &gdu, 0, 0);
2270 c->buf_out = odr_getbuf(c->odr_out, &c->len_out, 0);
2272 odr_reset(c->odr_out);
2273 return ZOOM_send_buf(c);
2276 static void handle_http(ZOOM_connection c, Z_HTTP_Response *hres)
2278 zoom_ret cret = zoom_complete;
2280 const char *addinfo = 0;
2281 const char *connection_head = z_HTTP_header_lookup(hres->headers,
2283 const char *location;
2285 ZOOM_connection_set_mask(c, 0);
2286 yaz_log(c->log_details, "%p handle_http", c);
2288 if ((hres->code == 301 || hres->code == 302) && c->sru_mode == zoom_sru_get
2289 && (location = z_HTTP_header_lookup(hres->headers, "Location")))
2292 if (c->no_redirects > 10)
2294 set_HTTP_error(c, hres->code, 0, 0);
2295 c->no_redirects = 0;
2296 ZOOM_connection_close(c);
2300 /* since redirect may change host we just reconnect. A smarter
2301 implementation might check whether it's the same server */
2302 do_connect_host(c, location, 0);
2303 send_SRW_redirect(c, location, hres);
2304 /* we're OK for now. Operation is not really complete */
2306 cret = zoom_pending;
2310 { /* not redirect (normal response) */
2311 if (!yaz_srw_check_content_type(hres))
2312 addinfo = "content-type";
2315 Z_SOAP *soap_package = 0;
2317 Z_SOAP_Handler soap_handlers[2] = {
2318 {YAZ_XMLNS_SRU_v1_1, 0, (Z_SOAP_fun) yaz_srw_codec},
2321 ret = z_soap_codec(o, &soap_package,
2322 &hres->content_buf, &hres->content_len,
2324 if (!ret && soap_package->which == Z_SOAP_generic &&
2325 soap_package->u.generic->no == 0)
2327 Z_SRW_PDU *sr = (Z_SRW_PDU*) soap_package->u.generic->p;
2329 ZOOM_options_set(c->options, "sru_version", sr->srw_version);
2330 ZOOM_options_setl(c->options, "sru_extra_response_data",
2331 sr->extraResponseData_buf, sr->extraResponseData_len);
2332 if (sr->which == Z_SRW_searchRetrieve_response)
2333 cret = handle_srw_response(c, sr->u.response);
2334 else if (sr->which == Z_SRW_scan_response)
2335 handle_srw_scan_response(c, sr->u.scan_response);
2339 else if (!ret && (soap_package->which == Z_SOAP_fault
2340 || soap_package->which == Z_SOAP_error))
2342 set_HTTP_error(c, hres->code,
2343 soap_package->u.fault->fault_code,
2344 soap_package->u.fault->fault_string);
2351 if (c->no_redirects) /* end of redirect. change hosts again */
2352 ZOOM_connection_close(c);
2354 c->no_redirects = 0;
2358 if (hres->code != 200)
2359 set_HTTP_error(c, hres->code, 0, 0);
2361 ZOOM_set_error(c, ZOOM_ERROR_DECODE, addinfo);
2362 ZOOM_connection_close(c);
2364 if (cret == zoom_complete)
2366 yaz_log(YLOG_LOG, "removing tasks in handle_http");
2367 ZOOM_connection_remove_task(c);
2371 if (!strcmp(hres->version, "1.0"))
2373 /* HTTP 1.0: only if Keep-Alive we stay alive.. */
2374 if (!connection_head || strcmp(connection_head, "Keep-Alive"))
2379 /* HTTP 1.1: only if no close we stay alive.. */
2380 if (connection_head && !strcmp(connection_head, "close"))
2385 ZOOM_connection_close(c);
2388 c->tasks->running = 0;
2389 ZOOM_connection_insert_task(c, ZOOM_TASK_CONNECT);
2390 c->reconnect_ok = 0;
2397 static int do_read(ZOOM_connection c)
2402 event = ZOOM_Event_create(ZOOM_EVENT_RECV_DATA);
2403 ZOOM_connection_put_event(c, event);
2405 r = cs_get(c->cs, &c->buf_in, &c->len_in);
2406 more = cs_more(c->cs);
2407 yaz_log(c->log_details, "%p do_read len=%d more=%d", c, r, more);
2412 if (!ZOOM_test_reconnect(c))
2414 ZOOM_set_error(c, ZOOM_ERROR_CONNECTION_LOST, c->host_port);
2415 ZOOM_connection_close(c);
2423 odr_reset(c->odr_in);
2424 odr_setbuf(c->odr_in, c->buf_in, r, 0);
2425 event = ZOOM_Event_create(ZOOM_EVENT_RECV_APDU);
2426 ZOOM_connection_put_event(c, event);
2428 if (!z_GDU(c->odr_in, &gdu, 0, 0))
2431 int err = odr_geterrorx(c->odr_in, &x);
2433 const char *element = odr_getelement(c->odr_in);
2434 yaz_snprintf(msg, sizeof(msg),
2435 "ODR code %d:%d element=%s offset=%d",
2436 err, x, element ? element : "<unknown>",
2437 odr_offset(c->odr_in));
2438 ZOOM_set_error(c, ZOOM_ERROR_DECODE, msg);
2441 FILE *ber_file = yaz_log_file();
2443 odr_dumpBER(ber_file, c->buf_in, r);
2445 ZOOM_connection_close(c);
2450 z_GDU(c->odr_print, &gdu, 0, 0);
2451 if (gdu->which == Z_GDU_Z3950)
2452 ZOOM_handle_Z3950_apdu(c, gdu->u.z3950);
2453 else if (gdu->which == Z_GDU_HTTP_Response)
2456 handle_http(c, gdu->u.HTTP_Response);
2458 ZOOM_set_error(c, ZOOM_ERROR_DECODE, 0);
2459 ZOOM_connection_close(c);
2463 c->reconnect_ok = 0;
2468 static zoom_ret do_write_ex(ZOOM_connection c, char *buf_out, int len_out)
2473 event = ZOOM_Event_create(ZOOM_EVENT_SEND_DATA);
2474 ZOOM_connection_put_event(c, event);
2476 yaz_log(c->log_details, "%p do_write_ex len=%d", c, len_out);
2477 if ((r = cs_put(c->cs, buf_out, len_out)) < 0)
2479 yaz_log(c->log_details, "%p do_write_ex write failed", c);
2480 if (ZOOM_test_reconnect(c))
2482 return zoom_pending;
2484 if (c->state == STATE_CONNECTING)
2485 ZOOM_set_error(c, ZOOM_ERROR_CONNECT, c->host_port);
2487 ZOOM_set_error(c, ZOOM_ERROR_CONNECTION_LOST, c->host_port);
2488 ZOOM_connection_close(c);
2489 return zoom_complete;
2493 int mask = ZOOM_SELECT_EXCEPT;
2494 if (c->cs->io_pending & CS_WANT_WRITE)
2495 mask += ZOOM_SELECT_WRITE;
2496 if (c->cs->io_pending & CS_WANT_READ)
2497 mask += ZOOM_SELECT_READ;
2498 ZOOM_connection_set_mask(c, mask);
2499 yaz_log(c->log_details, "%p do_write_ex write incomplete mask=%d",
2504 ZOOM_connection_set_mask(c, ZOOM_SELECT_READ|ZOOM_SELECT_EXCEPT);
2505 yaz_log(c->log_details, "%p do_write_ex write complete mask=%d",
2508 return zoom_pending;
2511 zoom_ret ZOOM_send_buf(ZOOM_connection c)
2513 return do_write_ex(c, c->buf_out, c->len_out);
2517 ZOOM_API(const char *)
2518 ZOOM_connection_option_get(ZOOM_connection c, const char *key)
2520 return ZOOM_options_get(c->options, key);
2523 ZOOM_API(const char *)
2524 ZOOM_connection_option_getl(ZOOM_connection c, const char *key, int *lenp)
2526 return ZOOM_options_getl(c->options, key, lenp);
2530 ZOOM_connection_option_set(ZOOM_connection c, const char *key,
2533 ZOOM_options_set(c->options, key, val);
2537 ZOOM_connection_option_setl(ZOOM_connection c, const char *key,
2538 const char *val, int len)
2540 ZOOM_options_setl(c->options, key, val, len);
2543 ZOOM_API(const char *)
2544 ZOOM_resultset_option_get(ZOOM_resultset r, const char *key)
2546 return ZOOM_options_get(r->options, key);
2550 ZOOM_resultset_option_set(ZOOM_resultset r, const char *key,
2553 ZOOM_options_set(r->options, key, val);
2558 ZOOM_connection_errcode(ZOOM_connection c)
2560 return ZOOM_connection_error(c, 0, 0);
2563 ZOOM_API(const char *)
2564 ZOOM_connection_errmsg(ZOOM_connection c)
2567 ZOOM_connection_error(c, &msg, 0);
2571 ZOOM_API(const char *)
2572 ZOOM_connection_addinfo(ZOOM_connection c)
2574 const char *addinfo;
2575 ZOOM_connection_error(c, 0, &addinfo);
2579 ZOOM_API(const char *)
2580 ZOOM_connection_diagset(ZOOM_connection c)
2582 const char *diagset;
2583 ZOOM_connection_error_x(c, 0, 0, &diagset);
2587 ZOOM_API(const char *)
2588 ZOOM_diag_str(int error)
2592 case ZOOM_ERROR_NONE:
2594 case ZOOM_ERROR_CONNECT:
2595 return "Connect failed";
2596 case ZOOM_ERROR_MEMORY:
2597 return "Out of memory";
2598 case ZOOM_ERROR_ENCODE:
2599 return "Encoding failed";
2600 case ZOOM_ERROR_DECODE:
2601 return "Decoding failed";
2602 case ZOOM_ERROR_CONNECTION_LOST:
2603 return "Connection lost";
2604 case ZOOM_ERROR_INIT:
2605 return "Init rejected";
2606 case ZOOM_ERROR_INTERNAL:
2607 return "Internal failure";
2608 case ZOOM_ERROR_TIMEOUT:
2610 case ZOOM_ERROR_UNSUPPORTED_PROTOCOL:
2611 return "Unsupported protocol";
2612 case ZOOM_ERROR_UNSUPPORTED_QUERY:
2613 return "Unsupported query type";
2614 case ZOOM_ERROR_INVALID_QUERY:
2615 return "Invalid query";
2616 case ZOOM_ERROR_CQL_PARSE:
2617 return "CQL parsing error";
2618 case ZOOM_ERROR_CQL_TRANSFORM:
2619 return "CQL transformation error";
2620 case ZOOM_ERROR_CCL_CONFIG:
2621 return "CCL configuration error";
2622 case ZOOM_ERROR_CCL_PARSE:
2623 return "CCL parsing error";
2625 return diagbib1_str(error);
2630 ZOOM_connection_error_x(ZOOM_connection c, const char **cp,
2631 const char **addinfo, const char **diagset)
2633 int error = c->error;
2636 if (!c->diagset || !strcmp(c->diagset, "ZOOM"))
2637 *cp = ZOOM_diag_str(error);
2638 else if (!strcmp(c->diagset, "HTTP"))
2639 *cp = z_HTTP_errmsg(c->error);
2640 else if (!strcmp(c->diagset, "Bib-1"))
2641 *cp = ZOOM_diag_str(error);
2642 else if (!strcmp(c->diagset, "info:srw/diagnostic/1"))
2643 *cp = yaz_diag_srw_str(c->error);
2645 *cp = "Unknown error and diagnostic set";
2648 *addinfo = c->addinfo ? c->addinfo : "";
2650 *diagset = c->diagset ? c->diagset : "";
2655 ZOOM_connection_error(ZOOM_connection c, const char **cp,
2656 const char **addinfo)
2658 return ZOOM_connection_error_x(c, cp, addinfo, 0);
2661 static void ZOOM_connection_do_io(ZOOM_connection c, int mask)
2663 ZOOM_Event event = 0;
2664 int r = cs_look(c->cs);
2665 yaz_log(c->log_details, "%p ZOOM_connection_do_io mask=%d cs_look=%d",
2670 event = ZOOM_Event_create(ZOOM_EVENT_CONNECT);
2671 ZOOM_set_error(c, ZOOM_ERROR_CONNECT, c->host_port);
2672 ZOOM_connection_close(c);
2673 ZOOM_connection_put_event(c, event);
2675 else if (r == CS_CONNECT)
2677 int ret = ret = cs_rcvconnect(c->cs);
2678 yaz_log(c->log_details, "%p ZOOM_connection_do_io "
2679 "cs_rcvconnect returned %d", c, ret);
2682 int mask = ZOOM_SELECT_EXCEPT;
2683 if (c->cs->io_pending & CS_WANT_WRITE)
2684 mask += ZOOM_SELECT_WRITE;
2685 if (c->cs->io_pending & CS_WANT_READ)
2686 mask += ZOOM_SELECT_READ;
2687 ZOOM_connection_set_mask(c, mask);
2688 event = ZOOM_Event_create(ZOOM_EVENT_NONE);
2689 ZOOM_connection_put_event(c, event);
2693 event = ZOOM_Event_create(ZOOM_EVENT_CONNECT);
2694 ZOOM_connection_put_event(c, event);
2696 if (c->proto == PROTO_Z3950)
2697 ZOOM_connection_Z3950_send_init(c);
2700 /* no init request for SRW .. */
2701 assert(c->tasks->which == ZOOM_TASK_CONNECT);
2702 ZOOM_connection_remove_task(c);
2703 ZOOM_connection_set_mask(c, 0);
2704 ZOOM_connection_exec_task(c);
2706 c->state = STATE_ESTABLISHED;
2710 ZOOM_set_error(c, ZOOM_ERROR_CONNECT, c->host_port);
2711 ZOOM_connection_close(c);
2716 if (mask & ZOOM_SELECT_EXCEPT)
2718 if (!ZOOM_test_reconnect(c))
2720 ZOOM_set_error(c, ZOOM_ERROR_CONNECTION_LOST, c->host_port);
2721 ZOOM_connection_close(c);
2725 if (mask & ZOOM_SELECT_READ)
2727 if (c->cs && (mask & ZOOM_SELECT_WRITE))
2733 ZOOM_connection_last_event(ZOOM_connection cs)
2736 return ZOOM_EVENT_NONE;
2737 return cs->last_event;
2741 ZOOM_API(int) ZOOM_connection_fire_event_timeout(ZOOM_connection c)
2745 ZOOM_Event event = ZOOM_Event_create(ZOOM_EVENT_TIMEOUT);
2746 /* timeout and this connection was waiting */
2747 ZOOM_set_error(c, ZOOM_ERROR_TIMEOUT, 0);
2748 ZOOM_connection_close(c);
2749 ZOOM_connection_put_event(c, event);
2755 ZOOM_connection_process(ZOOM_connection c)
2761 event = ZOOM_connection_get_event(c);
2764 ZOOM_Event_destroy(event);
2767 ZOOM_connection_exec_task(c);
2768 event = ZOOM_connection_get_event(c);
2771 ZOOM_Event_destroy(event);
2778 ZOOM_event_nonblock(int no, ZOOM_connection *cs)
2782 yaz_log(log_details0, "ZOOM_process_event(no=%d,cs=%p)", no, cs);
2784 for (i = 0; i<no; i++)
2786 ZOOM_connection c = cs[i];
2788 if (c && ZOOM_connection_process(c))
2794 ZOOM_API(int) ZOOM_connection_fire_event_socket(ZOOM_connection c, int mask)
2796 if (c->mask && mask)
2797 ZOOM_connection_do_io(c, mask);
2801 ZOOM_API(int) ZOOM_connection_get_socket(ZOOM_connection c)
2804 return cs_fileno(c->cs);
2808 ZOOM_API(int) ZOOM_connection_set_mask(ZOOM_connection c, int mask)
2816 ZOOM_API(int) ZOOM_connection_get_mask(ZOOM_connection c)
2823 ZOOM_API(int) ZOOM_connection_get_timeout(ZOOM_connection c)
2825 return ZOOM_options_get_int(c->options, "timeout", 30);
2828 ZOOM_API(void) ZOOM_connection_close(ZOOM_connection c)
2833 ZOOM_connection_set_mask(c, 0);
2834 c->state = STATE_IDLE;
2840 * c-file-style: "Stroustrup"
2841 * indent-tabs-mode: nil
2843 * vim: shiftwidth=4 tabstop=8 expandtab