fa3e7ee01d403de6b5d71c39a4d4dd287227651b
[yaz-moved-to-github.git] / src / zoom-c.c
1 /* This file is part of the YAZ toolkit.
2  * Copyright (C) 1995-2010 Index Data
3  * See the file LICENSE for details.
4  */
5 /**
6  * \file zoom-c.c
7  * \brief Implements ZOOM C interface.
8  */
9
10 #include <assert.h>
11 #include <string.h>
12 #include <errno.h>
13 #include "zoom-p.h"
14
15 #include <yaz/yaz-util.h>
16 #include <yaz/xmalloc.h>
17 #include <yaz/otherinfo.h>
18 #include <yaz/log.h>
19 #include <yaz/diagbib1.h>
20 #include <yaz/charneg.h>
21 #include <yaz/query-charset.h>
22 #include <yaz/snprintf.h>
23 #include <yaz/facet.h>
24
25 #include <yaz/shptr.h>
26
27 #if SHPTR
28 YAZ_SHPTR_TYPE(WRBUF)
29 #endif
30
31 static int log_api0 = 0;
32 static int log_details0 = 0;
33
34 static void resultset_destroy(ZOOM_resultset r);
35 static zoom_ret do_write_ex(ZOOM_connection c, char *buf_out, int len_out);
36
37 ZOOM_API(const char *) ZOOM_get_event_str(int event)
38 {
39     static const char *ar[] = {
40         "NONE",
41         "CONNECT",
42         "SEND_DATA",
43         "RECV_DATA",
44         "TIMEOUT",
45         "UNKNOWN",
46         "SEND_APDU",
47         "RECV_APDU",
48         "RECV_RECORD",
49         "RECV_SEARCH",
50         "END"
51     };
52     return ar[event];
53 }
54
55 static void initlog(void)
56 {
57     static int log_level_initialized = 0;
58     if (!log_level_initialized)
59     {
60         log_api0 = yaz_log_module_level("zoom");
61         log_details0 = yaz_log_module_level("zoomdetails");
62         log_level_initialized = 1;
63     }
64 }
65
66 ZOOM_Event ZOOM_Event_create(int kind)
67 {
68     ZOOM_Event event = (ZOOM_Event) xmalloc(sizeof(*event));
69     event->kind = kind;
70     event->next = 0;
71     event->prev = 0;
72     return event;
73 }
74
75 static void ZOOM_Event_destroy(ZOOM_Event event)
76 {
77     xfree(event);
78 }
79
80 void ZOOM_connection_put_event(ZOOM_connection c, ZOOM_Event event)
81 {
82     if (c->m_queue_back)
83     {
84         c->m_queue_back->prev = event;
85         assert(c->m_queue_front);
86     }
87     else
88     {
89         assert(!c->m_queue_front);
90         c->m_queue_front = event;
91     }
92     event->next = c->m_queue_back;
93     event->prev = 0;
94     c->m_queue_back = event;
95 }
96
97 static ZOOM_Event ZOOM_connection_get_event(ZOOM_connection c)
98 {
99     ZOOM_Event event = c->m_queue_front;
100     if (!event)
101     {
102         c->last_event = ZOOM_EVENT_NONE;
103         return 0;
104     }
105     assert(c->m_queue_back);
106     c->m_queue_front = event->prev;
107     if (c->m_queue_front)
108     {
109         assert(c->m_queue_back);
110         c->m_queue_front->next = 0;
111     }
112     else
113         c->m_queue_back = 0;
114     c->last_event = event->kind;
115     return event;
116 }
117
118 static void ZOOM_connection_remove_events(ZOOM_connection c)
119 {
120     ZOOM_Event event;
121     while ((event = ZOOM_connection_get_event(c)))
122         ZOOM_Event_destroy(event);
123 }
124
125 ZOOM_API(int) ZOOM_connection_peek_event(ZOOM_connection c)
126 {
127     ZOOM_Event event = c->m_queue_front;
128
129     return event ? event->kind : ZOOM_EVENT_NONE;
130 }
131
132 void ZOOM_connection_remove_tasks(ZOOM_connection c);
133
134 void ZOOM_set_dset_error(ZOOM_connection c, int error,
135                          const char *dset,
136                          const char *addinfo, const char *addinfo2)
137 {
138     char *cp;
139
140     xfree(c->addinfo);
141     c->addinfo = 0;
142     c->error = error;
143     if (!c->diagset || strcmp(dset, c->diagset))
144     {
145         xfree(c->diagset);
146         c->diagset = xstrdup(dset);
147         /* remove integer part from SRW diagset .. */
148         if ((cp = strrchr(c->diagset, '/')))
149             *cp = '\0';
150     }
151     if (addinfo && addinfo2)
152     {
153         c->addinfo = (char*) xmalloc(strlen(addinfo) + strlen(addinfo2) + 2);
154         strcpy(c->addinfo, addinfo);
155         strcat(c->addinfo, addinfo2);
156     }
157     else if (addinfo)
158         c->addinfo = xstrdup(addinfo);
159     if (error != ZOOM_ERROR_NONE)
160     {
161         yaz_log(c->log_api, "%p set_dset_error %s %s:%d %s %s",
162                 c, c->host_port ? c->host_port : "<>", dset, error,
163                 addinfo ? addinfo : "",
164                 addinfo2 ? addinfo2 : "");
165         ZOOM_connection_remove_tasks(c);
166     }
167 }
168
169 int ZOOM_uri_to_code(const char *uri)
170 {
171     int code = 0;       
172     const char *cp;
173     if ((cp = strrchr(uri, '/')))
174         code = atoi(cp+1);
175     return code;
176 }
177
178
179 #if YAZ_HAVE_XML2
180 static void set_HTTP_error(ZOOM_connection c, int error,
181                            const char *addinfo, const char *addinfo2)
182 {
183     ZOOM_set_dset_error(c, error, "HTTP", addinfo, addinfo2);
184 }
185
186 static void set_SRU_error(ZOOM_connection c, Z_SRW_diagnostic *d)
187 {
188     const char *uri = d->uri;
189     if (uri)
190         ZOOM_set_dset_error(c, ZOOM_uri_to_code(uri), uri, d->details, 0);
191 }
192
193 #endif
194
195
196 void ZOOM_set_error(ZOOM_connection c, int error, const char *addinfo)
197 {
198     ZOOM_set_dset_error(c, error, "ZOOM", addinfo, 0);
199 }
200
201 static void clear_error(ZOOM_connection c)
202 {
203     /*
204      * If an error is tied to an operation then it's ok to clear: for
205      * example, a diagnostic returned from a search is cleared by a
206      * subsequent search.  However, problems such as Connection Lost
207      * or Init Refused are not cleared, because they are not
208      * recoverable: doing another search doesn't help.
209      */
210
211     ZOOM_connection_remove_events(c);
212     switch (c->error)
213     {
214     case ZOOM_ERROR_CONNECT:
215     case ZOOM_ERROR_MEMORY:
216     case ZOOM_ERROR_DECODE:
217     case ZOOM_ERROR_CONNECTION_LOST:
218     case ZOOM_ERROR_INIT:
219     case ZOOM_ERROR_INTERNAL:
220     case ZOOM_ERROR_UNSUPPORTED_PROTOCOL:
221         break;
222     default:
223         ZOOM_set_error(c, ZOOM_ERROR_NONE, 0);
224     }
225 }
226
227 void ZOOM_connection_show_task(ZOOM_task task)
228 {
229     switch(task->which)
230     {
231     case ZOOM_TASK_SEARCH:
232         yaz_log(YLOG_LOG, "search p=%p", task);
233         break;
234     case ZOOM_TASK_RETRIEVE:
235         yaz_log(YLOG_LOG, "retrieve p=%p", task);
236         break;
237     case ZOOM_TASK_CONNECT:
238         yaz_log(YLOG_LOG, "connect p=%p", task);
239         break;
240     case ZOOM_TASK_SCAN:
241         yaz_log(YLOG_LOG, "scan p=%p", task);
242         break;
243     }
244 }
245
246 void ZOOM_connection_show_tasks(ZOOM_connection c)
247 {
248     ZOOM_task task;
249     yaz_log(YLOG_LOG, "connection p=%p tasks", c);
250     for (task = c->tasks; task; task = task->next)
251         ZOOM_connection_show_task(task);
252 }
253
254 ZOOM_task ZOOM_connection_add_task(ZOOM_connection c, int which)
255 {
256     ZOOM_task *taskp = &c->tasks;
257     while (*taskp)
258         taskp = &(*taskp)->next;
259     *taskp = (ZOOM_task) xmalloc(sizeof(**taskp));
260     (*taskp)->running = 0;
261     (*taskp)->which = which;
262     (*taskp)->next = 0;
263     clear_error(c);
264     return *taskp;
265 }
266
267 ZOOM_API(int) ZOOM_connection_is_idle(ZOOM_connection c)
268 {
269     return c->tasks ? 0 : 1;
270 }
271
272 ZOOM_task ZOOM_connection_insert_task(ZOOM_connection c, int which)
273 {
274     ZOOM_task task = (ZOOM_task) xmalloc(sizeof(*task));
275
276     task->next = c->tasks;
277     c->tasks = task;
278
279     task->running = 0;
280     task->which = which;
281     clear_error(c);
282     return task;
283 }
284
285 void ZOOM_connection_remove_task(ZOOM_connection c)
286 {
287     ZOOM_task task = c->tasks;
288
289     if (task)
290     {
291         c->tasks = task->next;
292         switch (task->which)
293         {
294         case ZOOM_TASK_SEARCH:
295             resultset_destroy(task->u.search.resultset);
296             xfree(task->u.search.syntax);
297             xfree(task->u.search.elementSetName);
298             break;
299         case ZOOM_TASK_RETRIEVE:
300             resultset_destroy(task->u.retrieve.resultset);
301             xfree(task->u.retrieve.syntax);
302             xfree(task->u.retrieve.elementSetName);
303             break;
304         case ZOOM_TASK_CONNECT:
305             break;
306         case ZOOM_TASK_SCAN:
307             ZOOM_scanset_destroy(task->u.scan.scan);
308             break;
309         case ZOOM_TASK_PACKAGE:
310             ZOOM_package_destroy(task->u.package);
311             break;
312         case ZOOM_TASK_SORT:
313             resultset_destroy(task->u.sort.resultset);
314             ZOOM_query_destroy(task->u.sort.q);
315             break;
316         default:
317             assert(0);
318         }
319         xfree(task);
320
321         if (!c->tasks)
322         {
323             ZOOM_Event event = ZOOM_Event_create(ZOOM_EVENT_END);
324             ZOOM_connection_put_event(c, event);
325         }
326     }
327 }
328
329 void ZOOM_connection_remove_tasks(ZOOM_connection c)
330 {
331     while (c->tasks)
332         ZOOM_connection_remove_task(c);
333 }
334
335
336 ZOOM_API(ZOOM_connection)
337     ZOOM_connection_create(ZOOM_options options)
338 {
339     ZOOM_connection c = (ZOOM_connection) xmalloc(sizeof(*c));
340
341     initlog();
342
343     c->log_api = log_api0;
344     c->log_details = log_details0;
345
346     yaz_log(c->log_api, "%p ZOOM_connection_create", c);
347
348     c->proto = PROTO_Z3950;
349     c->cs = 0;
350     ZOOM_connection_set_mask(c, 0);
351     c->reconnect_ok = 0;
352     c->state = STATE_IDLE;
353     c->addinfo = 0;
354     c->diagset = 0;
355     ZOOM_set_error(c, ZOOM_ERROR_NONE, 0);
356     c->buf_in = 0;
357     c->len_in = 0;
358     c->buf_out = 0;
359     c->len_out = 0;
360     c->resultsets = 0;
361
362     c->options = ZOOM_options_create_with_parent(options);
363
364     c->host_port = 0;
365     c->path = 0;
366     c->proxy = 0;
367     
368     c->charset = c->lang = 0;
369
370     c->cookie_out = 0;
371     c->cookie_in = 0;
372     c->client_IP = 0;
373     c->tasks = 0;
374
375     c->user = 0;
376     c->group = 0;
377     c->password = 0;
378
379     c->maximum_record_size = 0;
380     c->preferred_message_size = 0;
381
382     c->odr_in = odr_createmem(ODR_DECODE);
383     c->odr_out = odr_createmem(ODR_ENCODE);
384     c->odr_print = 0;
385
386     c->async = 0;
387     c->support_named_resultsets = 0;
388     c->last_event = ZOOM_EVENT_NONE;
389
390     c->m_queue_front = 0;
391     c->m_queue_back = 0;
392
393     c->sru_version = 0;
394     c->no_redirects = 0;
395     return c;
396 }
397
398
399 /* set database names. Take local databases (if set); otherwise
400    take databases given in ZURL (if set); otherwise use Default */
401 char **ZOOM_connection_get_databases(ZOOM_connection con, ZOOM_options options,
402                                      int *num, ODR odr)
403 {
404     char **databaseNames;
405     const char *cp = ZOOM_options_get(options, "databaseName");
406     
407     if ((!cp || !*cp) && con->host_port)
408     {
409         if (strncmp(con->host_port, "unix:", 5) == 0)
410             cp = strchr(con->host_port+5, ':');
411         else
412             cp = strchr(con->host_port, '/');
413         if (cp)
414             cp++;
415     }
416     if (!cp)
417         cp = "Default";
418     nmem_strsplit(odr_getmem(odr), "+", cp,  &databaseNames, num);
419     return databaseNames;
420 }
421
422 ZOOM_API(ZOOM_connection)
423     ZOOM_connection_new(const char *host, int portnum)
424 {
425     ZOOM_connection c = ZOOM_connection_create(0);
426
427     ZOOM_connection_connect(c, host, portnum);
428     return c;
429 }
430
431 static zoom_sru_mode get_sru_mode_from_string(const char *s)
432 {
433     if (!s || !*s)
434         return zoom_sru_soap;
435     if (!yaz_matchstr(s, "soap"))
436         return zoom_sru_soap;
437     else if (!yaz_matchstr(s, "get"))
438         return zoom_sru_get;
439     else if (!yaz_matchstr(s, "post"))
440         return zoom_sru_post;
441     return zoom_sru_error;
442 }
443
444 ZOOM_API(void)
445     ZOOM_connection_connect(ZOOM_connection c,
446                             const char *host, int portnum)
447 {
448     const char *val;
449     ZOOM_task task;
450
451     initlog();
452
453     yaz_log(c->log_api, "%p ZOOM_connection_connect host=%s portnum=%d",
454             c, host ? host : "null", portnum);
455
456     ZOOM_set_error(c, ZOOM_ERROR_NONE, 0);
457     ZOOM_connection_remove_tasks(c);
458
459     if (c->odr_print)
460     {
461         odr_setprint(c->odr_print, 0); /* prevent destroy from fclose'ing */
462         odr_destroy(c->odr_print);
463     }
464     if (ZOOM_options_get_bool(c->options, "apdulog", 0))
465     {
466         c->odr_print = odr_createmem(ODR_PRINT);
467         odr_setprint(c->odr_print, yaz_log_file());
468     }
469     else
470         c->odr_print = 0;
471
472     if (c->cs)
473     {
474         yaz_log(c->log_details, "%p ZOOM_connection_connect reconnect ok", c);
475         c->reconnect_ok = 1;
476         return;
477     }
478     yaz_log(c->log_details, "%p ZOOM_connection_connect connect", c);
479     xfree(c->proxy);
480     c->proxy = 0;
481     val = ZOOM_options_get(c->options, "proxy");
482     if (val && *val)
483     {
484         yaz_log(c->log_details, "%p ZOOM_connection_connect proxy=%s", c, val);
485         c->proxy = xstrdup(val);
486     }
487
488     xfree(c->charset);
489     c->charset = 0;
490     val = ZOOM_options_get(c->options, "charset");
491     if (val && *val)
492     {
493         yaz_log(c->log_details, "%p ZOOM_connection_connect charset=%s", c, val);
494         c->charset = xstrdup(val);
495     }
496
497     xfree(c->lang);
498     val = ZOOM_options_get(c->options, "lang");
499     if (val && *val)
500     {
501         yaz_log(c->log_details, "%p ZOOM_connection_connect lang=%s", c, val);
502         c->lang = xstrdup(val);
503     }
504     else
505         c->lang = 0;
506
507     if (host)
508     {
509         xfree(c->host_port);
510         if (portnum)
511         {
512             char hostn[128];
513             sprintf(hostn, "%.80s:%d", host, portnum);
514             c->host_port = xstrdup(hostn);
515         }
516         else
517             c->host_port = xstrdup(host);
518     }        
519
520     {
521         /*
522          * If the "<scheme>:" part of the host string is preceded by one
523          * or more comma-separated <name>=<value> pairs, these are taken
524          * to be options to be set on the connection object.  Among other
525          * applications, this facility can be used to embed authentication
526          * in a host string:
527          *          user=admin,password=secret,tcp:localhost:9999
528          */
529         char *remainder = c->host_port;
530         char *pcolon = strchr(remainder, ':');
531         char *pcomma;
532         char *pequals;
533         while ((pcomma = strchr(remainder, ',')) != 0 &&
534                (pcolon == 0 || pcomma < pcolon)) {
535             *pcomma = '\0';
536             if ((pequals = strchr(remainder, '=')) != 0) {
537                 *pequals = '\0';
538                 /*printf("# setting '%s'='%s'\n", remainder, pequals+1);*/
539                 ZOOM_connection_option_set(c, remainder, pequals+1);
540             }
541             remainder = pcomma+1;
542         }
543
544         if (remainder != c->host_port) {
545             xfree(c->host_port);
546             c->host_port = xstrdup(remainder);
547             /*printf("# reset hp='%s'\n", remainder);*/
548         }
549     }
550
551     val = ZOOM_options_get(c->options, "sru");
552     c->sru_mode = get_sru_mode_from_string(val);
553
554     xfree(c->sru_version);
555     val = ZOOM_options_get(c->options, "sru_version");
556     c->sru_version = xstrdup(val ? val : "1.2");
557
558     ZOOM_options_set(c->options, "host", c->host_port);
559
560     xfree(c->cookie_out);
561     c->cookie_out = 0;
562     val = ZOOM_options_get(c->options, "cookie");
563     if (val && *val)
564     { 
565         yaz_log(c->log_details, "%p ZOOM_connection_connect cookie=%s", c, val);
566         c->cookie_out = xstrdup(val);
567     }
568
569     xfree(c->client_IP);
570     c->client_IP = 0;
571     val = ZOOM_options_get(c->options, "clientIP");
572     if (val && *val)
573     {
574         yaz_log(c->log_details, "%p ZOOM_connection_connect clientIP=%s",
575                 c, val);
576         c->client_IP = xstrdup(val);
577     }
578
579     xfree(c->group);
580     c->group = 0;
581     val = ZOOM_options_get(c->options, "group");
582     if (val && *val)
583         c->group = xstrdup(val);
584
585     xfree(c->user);
586     c->user = 0;
587     val = ZOOM_options_get(c->options, "user");
588     if (val && *val)
589         c->user = xstrdup(val);
590
591     xfree(c->password);
592     c->password = 0;
593     val = ZOOM_options_get(c->options, "password");
594     if (!val)
595         val = ZOOM_options_get(c->options, "pass");
596
597     if (val && *val)
598         c->password = xstrdup(val);
599     
600     c->maximum_record_size =
601         ZOOM_options_get_int(c->options, "maximumRecordSize", 1024*1024);
602     c->preferred_message_size =
603         ZOOM_options_get_int(c->options, "preferredMessageSize", 1024*1024);
604
605     c->async = ZOOM_options_get_bool(c->options, "async", 0);
606     yaz_log(c->log_details, "%p ZOOM_connection_connect async=%d", c, c->async);
607  
608     task = ZOOM_connection_add_task(c, ZOOM_TASK_CONNECT);
609
610     if (!c->async)
611     {
612         while (ZOOM_event(1, &c))
613             ;
614     }
615 }
616
617 ZOOM_API(void) ZOOM_resultset_release(ZOOM_resultset r)
618 {
619 #if ZOOM_RESULT_LISTS
620 #else
621     if (r->connection)
622     {
623         /* remove ourselves from the resultsets in connection */
624         ZOOM_resultset *rp = &r->connection->resultsets;
625         while (1)
626         {
627             assert(*rp);   /* we must be in this list!! */
628             if (*rp == r)
629             {   /* OK, we're here - take us out of it */
630                 *rp = (*rp)->next;
631                 break;
632             }
633             rp = &(*rp)->next;
634         }
635         r->connection = 0;
636     }
637 #endif
638 }
639
640 ZOOM_API(void)
641     ZOOM_connection_destroy(ZOOM_connection c)
642 {
643 #if ZOOM_RESULT_LISTS
644     ZOOM_resultsets list;
645 #else
646     ZOOM_resultset r;
647 #endif
648     if (!c)
649         return;
650     yaz_log(c->log_api, "%p ZOOM_connection_destroy", c);
651     if (c->cs)
652         cs_close(c->cs);
653
654 #if ZOOM_RESULT_LISTS
655     // Remove the connection's usage of resultsets
656     list = c->resultsets;
657     while (list) {
658         ZOOM_resultsets removed = list;
659         ZOOM_resultset_destroy(list->resultset);
660         list = list->next;
661         xfree(removed);
662     }
663 #else
664     for (r = c->resultsets; r; r = r->next)
665         r->connection = 0;
666 #endif
667
668     xfree(c->buf_in);
669     xfree(c->addinfo);
670     xfree(c->diagset);
671     odr_destroy(c->odr_in);
672     odr_destroy(c->odr_out);
673     if (c->odr_print)
674     {
675         odr_setprint(c->odr_print, 0); /* prevent destroy from fclose'ing */
676         odr_destroy(c->odr_print);
677     }
678     ZOOM_options_destroy(c->options);
679     ZOOM_connection_remove_tasks(c);
680     ZOOM_connection_remove_events(c);
681     xfree(c->host_port);
682     xfree(c->path);
683     xfree(c->proxy);
684     xfree(c->charset);
685     xfree(c->lang);
686     xfree(c->cookie_out);
687     xfree(c->cookie_in);
688     xfree(c->client_IP);
689     xfree(c->user);
690     xfree(c->group);
691     xfree(c->password);
692     xfree(c->sru_version);
693     xfree(c);
694 }
695
696 void ZOOM_resultset_addref(ZOOM_resultset r)
697 {
698     if (r)
699     {
700         yaz_mutex_enter(r->mutex);
701         (r->refcount)++;
702         yaz_log(log_details0, "%p ZOOM_resultset_addref count=%d",
703                 r, r->refcount);
704         yaz_mutex_leave(r->mutex);
705     }
706 }
707
708 ZOOM_resultset ZOOM_resultset_create(void)
709 {
710     int i;
711     ZOOM_resultset r = (ZOOM_resultset) xmalloc(sizeof(*r));
712
713     initlog();
714
715     yaz_log(log_details0, "%p ZOOM_resultset_create", r);
716     r->refcount = 1;
717     r->size = 0;
718     r->odr = odr_createmem(ODR_ENCODE);
719     r->piggyback = 1;
720     r->setname = 0;
721     r->schema = 0;
722     r->step = 0;
723     for (i = 0; i<RECORD_HASH_SIZE; i++)
724         r->record_hash[i] = 0;
725     r->r_sort_spec = 0;
726     r->query = 0;
727     r->connection = 0;
728     r->databaseNames = 0;
729     r->num_databaseNames = 0;
730     r->facets = 0;
731     r->num_facets = 0;
732     r->facets_names = 0;
733     r->mutex = 0;
734     yaz_mutex_create(&r->mutex);
735 #if SHPTR
736     {
737         WRBUF w = wrbuf_alloc();
738         YAZ_SHPTR_INIT(r->record_wrbuf, w);
739     }
740 #endif
741     return r;
742 }
743
744 ZOOM_API(ZOOM_resultset)
745     ZOOM_connection_search_pqf(ZOOM_connection c, const char *q)
746 {
747     ZOOM_resultset r;
748     ZOOM_query s = ZOOM_query_create();
749
750     ZOOM_query_prefix(s, q);
751
752     r = ZOOM_connection_search(c, s);
753     ZOOM_query_destroy(s);
754     return r;
755 }
756
757 ZOOM_API(ZOOM_resultset)
758     ZOOM_connection_search(ZOOM_connection c, ZOOM_query q)
759 {
760     ZOOM_resultset r = ZOOM_resultset_create();
761     ZOOM_task task;
762     const char *cp;
763     int start, count;
764     const char *syntax, *elementSetName;
765 #if ZOOM_RESULT_LISTS
766     ZOOM_resultsets set;
767 #endif
768
769     yaz_log(c->log_api, "%p ZOOM_connection_search set %p query %p", c, r, q);
770     r->r_sort_spec = ZOOM_query_get_sortspec(q);
771     r->query = q;
772
773     r->options = ZOOM_options_create_with_parent(c->options);
774     
775     start = ZOOM_options_get_int(r->options, "start", 0);
776     count = ZOOM_options_get_int(r->options, "count", 0);
777     {
778         /* If "presentChunk" is defined use that; otherwise "step" */
779         const char *cp = ZOOM_options_get(r->options, "presentChunk");
780         r->step = ZOOM_options_get_int(r->options,
781                                        (cp != 0 ? "presentChunk": "step"), 0);
782     }
783     r->piggyback = ZOOM_options_get_bool(r->options, "piggyback", 1);
784     cp = ZOOM_options_get(r->options, "setname");
785     if (cp)
786         r->setname = xstrdup(cp);
787     cp = ZOOM_options_get(r->options, "schema");
788     if (cp)
789         r->schema = xstrdup(cp);
790
791     r->databaseNames = ZOOM_connection_get_databases(c, c->options, &r->num_databaseNames,
792                                          r->odr);
793     
794     r->connection = c;
795
796 #if ZOOM_RESULT_LISTS
797     yaz_log(log_details, "%p ZOOM_connection_search: Adding new resultset (%p) to resultsets (%p) ", c, r, c->resultsets);
798     set = xmalloc(sizeof(*set));
799     ZOOM_resultset_addref(r);
800     set->resultset = r;
801     set->next = c->resultsets;
802     c->resultsets = set;
803 #else
804     r->next = c->resultsets;
805     c->resultsets = r;
806 #endif
807     if (c->host_port && c->proto == PROTO_HTTP)
808     {
809         if (!c->cs)
810         {
811             yaz_log(c->log_details, "ZOOM_connection_search: no comstack");
812             ZOOM_connection_add_task(c, ZOOM_TASK_CONNECT);
813         }
814         else
815         {
816             yaz_log(c->log_details, "ZOOM_connection_search: reconnect");
817             c->reconnect_ok = 1;
818         }
819     }
820
821     task = ZOOM_connection_add_task(c, ZOOM_TASK_SEARCH);
822     task->u.search.resultset = r;
823     task->u.search.start = start;
824     task->u.search.count = count;
825     task->u.search.recv_search_fired = 0;
826
827     syntax = ZOOM_options_get(r->options, "preferredRecordSyntax"); 
828     task->u.search.syntax = syntax ? xstrdup(syntax) : 0;
829     elementSetName = ZOOM_options_get(r->options, "elementSetName");
830     task->u.search.elementSetName = elementSetName 
831         ? xstrdup(elementSetName) : 0;
832    
833     ZOOM_resultset_addref(r);
834
835     ZOOM_query_addref(q);
836
837     if (!c->async)
838     {
839         while (ZOOM_event(1, &c))
840             ;
841     }
842     return r;
843 }
844
845 ZOOM_API(void)
846     ZOOM_resultset_sort(ZOOM_resultset r,
847                          const char *sort_type, const char *sort_spec)
848 {
849     (void) ZOOM_resultset_sort1(r, sort_type, sort_spec);
850 }
851
852 ZOOM_API(int)
853     ZOOM_resultset_sort1(ZOOM_resultset r,
854                          const char *sort_type, const char *sort_spec)
855 {
856     ZOOM_connection c = r->connection;
857     ZOOM_task task;
858     ZOOM_query newq;
859
860     newq = ZOOM_query_create();
861     if (ZOOM_query_sortby(newq, sort_spec) < 0)
862         return -1;
863
864     yaz_log(c->log_api, "%p ZOOM_resultset_sort r=%p sort_type=%s sort_spec=%s",
865             r, r, sort_type, sort_spec);
866     if (!c)
867         return 0;
868
869     if (c->host_port && c->proto == PROTO_HTTP)
870     {
871         if (!c->cs)
872         {
873             yaz_log(c->log_details, "%p ZOOM_resultset_sort: no comstack", r);
874             ZOOM_connection_add_task(c, ZOOM_TASK_CONNECT);
875         }
876         else
877         {
878             yaz_log(c->log_details, "%p ZOOM_resultset_sort: prepare reconnect",
879                     r);
880             c->reconnect_ok = 1;
881         }
882     }
883     
884     ZOOM_resultset_cache_reset(r);
885     task = ZOOM_connection_add_task(c, ZOOM_TASK_SORT);
886     task->u.sort.resultset = r;
887     task->u.sort.q = newq;
888
889     ZOOM_resultset_addref(r);  
890
891     if (!c->async)
892     {
893         while (ZOOM_event(1, &c))
894             ;
895     }
896
897     return 0;
898 }
899
900 ZOOM_API(void)
901     ZOOM_resultset_destroy(ZOOM_resultset r)
902 {
903     resultset_destroy(r);
904 }
905
906 static void resultset_destroy(ZOOM_resultset r)
907 {
908     if (!r)
909         return;
910     yaz_mutex_enter(r->mutex);
911     (r->refcount)--;
912     yaz_log(log_details0, "%p ZOOM_resultset_destroy r=%p count=%d",
913             r, r, r->refcount);
914     if (r->refcount == 0)
915     {
916         yaz_mutex_leave(r->mutex);
917
918         yaz_log(log_details0, "%p ZOOM_connection resultset_destroy: Deleting resultset (%p) ", r->connection, r);
919         ZOOM_resultset_cache_reset(r);
920         ZOOM_resultset_release(r);
921         ZOOM_query_destroy(r->query);
922         ZOOM_options_destroy(r->options);
923         odr_destroy(r->odr);
924         xfree(r->setname);
925         xfree(r->schema);
926         yaz_mutex_destroy(&r->mutex);
927 #if SHPTR
928         YAZ_SHPTR_DEC(r->record_wrbuf, wrbuf_destroy);
929 #endif
930         xfree(r);
931     }
932     else
933         yaz_mutex_leave(r->mutex);
934 }
935
936 ZOOM_API(size_t)
937     ZOOM_resultset_size(ZOOM_resultset r)
938 {
939     return r->size;
940 }
941
942 int ZOOM_test_reconnect(ZOOM_connection c)
943 {
944     ZOOM_Event event;
945
946     if (!c->reconnect_ok)
947         return 0;
948     ZOOM_connection_close(c);
949     c->reconnect_ok = 0;
950     c->tasks->running = 0;
951     ZOOM_connection_insert_task(c, ZOOM_TASK_CONNECT);
952
953     event = ZOOM_Event_create(ZOOM_EVENT_CONNECT);
954     ZOOM_connection_put_event(c, event);
955
956     return 1;
957 }
958
959 static void ZOOM_resultset_retrieve(ZOOM_resultset r,
960                                     int force_sync, int start, int count)
961 {
962     ZOOM_task task;
963     ZOOM_connection c;
964     const char *cp;
965     const char *syntax, *elementSetName;
966
967     if (!r)
968         return;
969     yaz_log(log_details0, "%p ZOOM_resultset_retrieve force_sync=%d start=%d"
970             " count=%d", r, force_sync, start, count);
971     c = r->connection;
972     if (!c)
973         return;
974
975     if (c->host_port && c->proto == PROTO_HTTP)
976     {
977         if (!c->cs)
978         {
979             yaz_log(log_details0, "%p ZOOM_resultset_retrieve: no comstack", r);
980             ZOOM_connection_add_task(c, ZOOM_TASK_CONNECT);
981         }
982         else
983         {
984             yaz_log(log_details0, "%p ZOOM_resultset_retrieve: prepare "
985                     "reconnect", r);
986             c->reconnect_ok = 1;
987         }
988     }
989     task = ZOOM_connection_add_task(c, ZOOM_TASK_RETRIEVE);
990     task->u.retrieve.resultset = r;
991     task->u.retrieve.start = start;
992     task->u.retrieve.count = count;
993
994     syntax = ZOOM_options_get(r->options, "preferredRecordSyntax"); 
995     task->u.retrieve.syntax = syntax ? xstrdup(syntax) : 0;
996     elementSetName = ZOOM_options_get(r->options, "elementSetName");
997     task->u.retrieve.elementSetName = elementSetName 
998         ? xstrdup(elementSetName) : 0;
999
1000     cp = ZOOM_options_get(r->options, "schema");
1001     if (cp)
1002     {
1003         if (!r->schema || strcmp(r->schema, cp))
1004         {
1005             xfree(r->schema);
1006             r->schema = xstrdup(cp);
1007         }
1008     }
1009
1010     ZOOM_resultset_addref(r);
1011
1012     if (!r->connection->async || force_sync)
1013         while (r->connection && ZOOM_event(1, &r->connection))
1014             ;
1015 }
1016
1017 ZOOM_API(void)
1018     ZOOM_resultset_records(ZOOM_resultset r, ZOOM_record *recs,
1019                            size_t start, size_t count)
1020 {
1021     int force_present = 0;
1022
1023     if (!r)
1024         return ;
1025     yaz_log(log_api0, "%p ZOOM_resultset_records r=%p start=%ld count=%ld",
1026             r, r, (long) start, (long) count);
1027     if (count && recs)
1028         force_present = 1;
1029     ZOOM_resultset_retrieve(r, force_present, start, count);
1030     if (force_present)
1031     {
1032         size_t i;
1033         for (i = 0; i< count; i++)
1034             recs[i] = ZOOM_resultset_record_immediate(r, i+start);
1035     }
1036 }
1037
1038 ZOOM_API(size_t)
1039     ZOOM_resultset_facets_size(ZOOM_resultset r) {
1040     return r->num_facets;
1041 }
1042
1043 ZOOM_API(ZOOM_facet_field)
1044     ZOOM_resultset_get_facet_field(ZOOM_resultset r, const char *name) {
1045     int num = r->num_facets;
1046     ZOOM_facet_field *facets = r->facets;
1047     int index;
1048     for (index = 0; index < num; index++) {
1049         if (!strcmp(facets[index]->facet_name, name)) {
1050             return facets[index];
1051         }
1052     }
1053     return 0;
1054 }
1055
1056
1057 ZOOM_API(ZOOM_facet_field *)
1058     ZOOM_resultset_facets(ZOOM_resultset r)
1059 {
1060     return r->facets;
1061 }
1062
1063 ZOOM_API(const char**)
1064     ZOOM_resultset_facet_names(ZOOM_resultset r)
1065 {
1066     return (const char **) r->facets_names;
1067 }
1068
1069 ZOOM_API(const char*)
1070     ZOOM_facet_field_name(ZOOM_facet_field field)
1071 {
1072     return field->facet_name;
1073 }
1074
1075 ZOOM_API(size_t)
1076     ZOOM_facet_field_term_count(ZOOM_facet_field field)
1077 {
1078     return field->num_terms;
1079 }
1080
1081 ZOOM_API(const char*)
1082     ZOOM_facet_field_get_term(ZOOM_facet_field field, size_t idx, int *freq) {
1083     *freq = field->facet_terms[idx].frequency;
1084     return field->facet_terms[idx].term;
1085 }
1086
1087
1088 static void get_cert(ZOOM_connection c)
1089 {
1090     char *cert_buf;
1091     int cert_len;
1092     
1093     if (cs_get_peer_certificate_x509(c->cs, &cert_buf, &cert_len))
1094     {
1095         ZOOM_connection_option_setl(c, "sslPeerCert",
1096                                     cert_buf, cert_len);
1097         xfree(cert_buf);
1098     }
1099 }
1100
1101 static zoom_ret do_connect_host(ZOOM_connection c,
1102                                 const char *effective_host,
1103                                 const char *logical_url);
1104
1105 static zoom_ret do_connect(ZOOM_connection c)
1106 {
1107     const char *effective_host;
1108
1109     if (c->proxy)
1110         effective_host = c->proxy;
1111     else
1112         effective_host = c->host_port;
1113     return do_connect_host(c, effective_host, c->host_port);
1114 }
1115
1116 static zoom_ret do_connect_host(ZOOM_connection c, const char *effective_host,
1117     const char *logical_url)
1118 {
1119     void *add;
1120
1121     yaz_log(c->log_details, "%p do_connect effective_host=%s", c, effective_host);
1122
1123     if (c->cs)
1124         cs_close(c->cs);
1125     c->cs = cs_create_host(effective_host, 0, &add);
1126
1127     if (c->cs && c->cs->protocol == PROTO_HTTP)
1128     {
1129 #if YAZ_HAVE_XML2
1130         if (logical_url)
1131         {
1132             const char *db = 0;
1133             
1134             c->proto = PROTO_HTTP;
1135             cs_get_host_args(logical_url, &db);
1136             xfree(c->path);
1137             
1138             c->path = xmalloc(strlen(db) * 3 + 2);
1139             yaz_encode_sru_dbpath_buf(c->path, db);
1140         }
1141 #else
1142         ZOOM_set_error(c, ZOOM_ERROR_UNSUPPORTED_PROTOCOL, "SRW");
1143         ZOOM_connection_close(c);
1144         return zoom_complete;
1145 #endif
1146     }
1147     if (c->cs)
1148     {
1149         int ret = cs_connect(c->cs, add);
1150         if (ret == 0)
1151         {
1152             ZOOM_Event event = ZOOM_Event_create(ZOOM_EVENT_CONNECT);
1153             ZOOM_connection_put_event(c, event);
1154             get_cert(c);
1155             if (c->proto == PROTO_Z3950)
1156                 ZOOM_connection_Z3950_send_init(c);
1157             else
1158             {
1159                 /* no init request for SRW .. */
1160                 assert(c->tasks->which == ZOOM_TASK_CONNECT);
1161                 ZOOM_connection_remove_task(c);
1162                 ZOOM_connection_set_mask(c, 0);
1163                 ZOOM_connection_exec_task(c);
1164             }
1165             c->state = STATE_ESTABLISHED;
1166             return zoom_pending;
1167         }
1168         else if (ret > 0)
1169         {
1170             int mask = ZOOM_SELECT_EXCEPT;
1171             if (c->cs->io_pending & CS_WANT_WRITE)
1172                 mask += ZOOM_SELECT_WRITE;
1173             if (c->cs->io_pending & CS_WANT_READ)
1174                 mask += ZOOM_SELECT_READ;
1175             ZOOM_connection_set_mask(c, mask);
1176             c->state = STATE_CONNECTING; 
1177             return zoom_pending;
1178         }
1179     }
1180     c->state = STATE_IDLE;
1181     ZOOM_set_error(c, ZOOM_ERROR_CONNECT, logical_url);
1182     return zoom_complete;
1183 }
1184
1185 /* returns 1 if PDU was sent OK (still pending )
1186    0 if PDU was not sent OK (nothing to wait for) 
1187 */
1188
1189 #if YAZ_HAVE_XML2
1190 static zoom_ret send_srw(ZOOM_connection c, Z_SRW_PDU *sr)
1191 {
1192     Z_GDU *gdu;
1193     ZOOM_Event event;
1194     const char *database =  ZOOM_options_get(c->options, "databaseName");
1195     char *fdatabase = 0;
1196     
1197     if (database)
1198         fdatabase = yaz_encode_sru_dbpath_odr(c->odr_out, database);
1199     gdu = z_get_HTTP_Request_host_path(c->odr_out, c->host_port,
1200                                        fdatabase ? fdatabase : c->path);
1201
1202     if (c->sru_mode == zoom_sru_get)
1203     {
1204         yaz_sru_get_encode(gdu->u.HTTP_Request, sr, c->odr_out, c->charset);
1205     }
1206     else if (c->sru_mode == zoom_sru_post)
1207     {
1208         yaz_sru_post_encode(gdu->u.HTTP_Request, sr, c->odr_out, c->charset);
1209     }
1210     else if (c->sru_mode == zoom_sru_soap)
1211     {
1212         yaz_sru_soap_encode(gdu->u.HTTP_Request, sr, c->odr_out, c->charset);
1213     }
1214     if (!z_GDU(c->odr_out, &gdu, 0, 0))
1215         return zoom_complete;
1216     if (c->odr_print)
1217         z_GDU(c->odr_print, &gdu, 0, 0);
1218     c->buf_out = odr_getbuf(c->odr_out, &c->len_out, 0);
1219         
1220     event = ZOOM_Event_create(ZOOM_EVENT_SEND_APDU);
1221     ZOOM_connection_put_event(c, event);
1222     odr_reset(c->odr_out);
1223     return ZOOM_send_buf(c);
1224 }
1225 #endif
1226
1227 #if YAZ_HAVE_XML2
1228 static Z_SRW_PDU *ZOOM_srw_get_pdu(ZOOM_connection c, int type)
1229 {
1230     Z_SRW_PDU *sr = yaz_srw_get_pdu(c->odr_out, type, c->sru_version);
1231     sr->username = c->user;
1232     sr->password = c->password;
1233     return sr;
1234 }
1235 #endif
1236
1237 #if YAZ_HAVE_XML2
1238 static zoom_ret ZOOM_connection_srw_send_search(ZOOM_connection c)
1239 {
1240     int i;
1241     int *start, *count;
1242     ZOOM_resultset resultset = 0;
1243     Z_SRW_PDU *sr = 0;
1244     const char *option_val = 0;
1245     Z_Query *z_query;
1246
1247     if (c->error)                  /* don't continue on error */
1248         return zoom_complete;
1249     assert(c->tasks);
1250     switch(c->tasks->which)
1251     {
1252     case ZOOM_TASK_SEARCH:
1253         resultset = c->tasks->u.search.resultset;
1254         if (!resultset->setname)
1255             resultset->setname = xstrdup("default");
1256         ZOOM_options_set(resultset->options, "setname", resultset->setname);
1257         start = &c->tasks->u.search.start;
1258         count = &c->tasks->u.search.count;
1259         break;
1260     case ZOOM_TASK_RETRIEVE:
1261         resultset = c->tasks->u.retrieve.resultset;
1262
1263         start = &c->tasks->u.retrieve.start;
1264         count = &c->tasks->u.retrieve.count;
1265
1266         if (*start >= resultset->size)
1267             return zoom_complete;
1268         if (*start + *count > resultset->size)
1269             *count = resultset->size - *start;
1270
1271         for (i = 0; i < *count; i++)
1272         {
1273             ZOOM_record rec =
1274                 ZOOM_record_cache_lookup(resultset, i + *start,
1275                                          c->tasks->u.retrieve.syntax,
1276                                          c->tasks->u.retrieve.elementSetName);
1277             if (!rec)
1278                 break;
1279             else
1280             {
1281                 ZOOM_Event event = ZOOM_Event_create(ZOOM_EVENT_RECV_RECORD);
1282                 ZOOM_connection_put_event(c, event);
1283             }
1284         }
1285         *start += i;
1286         *count -= i;
1287
1288         if (*count == 0)
1289             return zoom_complete;
1290         break;
1291     default:
1292         return zoom_complete;
1293     }
1294     assert(resultset->query);
1295         
1296     sr = ZOOM_srw_get_pdu(c, Z_SRW_searchRetrieve_request);
1297     z_query = ZOOM_query_get_Z_Query(resultset->query);
1298
1299     if (z_query->which == Z_Query_type_104
1300         && z_query->u.type_104->which == Z_External_CQL)
1301     {
1302         sr->u.request->query_type = Z_SRW_query_type_cql;
1303         sr->u.request->query.cql = z_query->u.type_104->u.cql;
1304     }
1305     else if (z_query->which == Z_Query_type_1 && z_query->u.type_1)
1306     {
1307         sr->u.request->query_type = Z_SRW_query_type_pqf;
1308         sr->u.request->query.pqf =
1309             ZOOM_query_get_query_string(resultset->query);
1310     }
1311     else
1312     {
1313         ZOOM_set_error(c, ZOOM_ERROR_UNSUPPORTED_QUERY, 0);
1314         return zoom_complete;
1315     }
1316     sr->u.request->startRecord = odr_intdup(c->odr_out, *start + 1);
1317     sr->u.request->maximumRecords = odr_intdup(
1318         c->odr_out, (resultset->step > 0 && resultset->step < *count) ? 
1319         resultset->step : *count);
1320     sr->u.request->recordSchema = resultset->schema;
1321     
1322     option_val = ZOOM_resultset_option_get(resultset, "recordPacking");
1323     if (option_val)
1324         sr->u.request->recordPacking = odr_strdup(c->odr_out, option_val);
1325
1326     option_val = ZOOM_resultset_option_get(resultset, "extraArgs");
1327     yaz_encode_sru_extra(sr, c->odr_out, option_val);
1328     return send_srw(c, sr);
1329 }
1330 #else
1331 static zoom_ret ZOOM_connection_srw_send_search(ZOOM_connection c)
1332 {
1333     return zoom_complete;
1334 }
1335 #endif
1336
1337 ZOOM_API(ZOOM_record)
1338     ZOOM_resultset_record_immediate(ZOOM_resultset s,size_t pos)
1339 {
1340     const char *syntax =
1341         ZOOM_options_get(s->options, "preferredRecordSyntax"); 
1342     const char *elementSetName =
1343         ZOOM_options_get(s->options, "elementSetName");
1344
1345     return ZOOM_record_cache_lookup(s, pos, syntax, elementSetName);
1346 }
1347
1348 ZOOM_API(ZOOM_record)
1349     ZOOM_resultset_record(ZOOM_resultset r, size_t pos)
1350 {
1351     ZOOM_record rec = ZOOM_resultset_record_immediate(r, pos);
1352
1353     if (!rec)
1354     {
1355         /*
1356          * MIKE: I think force_sync should always be zero, but I don't
1357          * want to make this change until I get the go-ahead from
1358          * Adam, in case something depends on the old synchronous
1359          * behaviour.
1360          */
1361         int force_sync = 1;
1362         if (getenv("ZOOM_RECORD_NO_FORCE_SYNC")) force_sync = 0;
1363         ZOOM_resultset_retrieve(r, force_sync, pos, 1);
1364         rec = ZOOM_resultset_record_immediate(r, pos);
1365     }
1366     return rec;
1367 }
1368
1369 ZOOM_API(ZOOM_scanset)
1370     ZOOM_connection_scan(ZOOM_connection c, const char *start)
1371 {
1372     ZOOM_scanset s;
1373     ZOOM_query q = ZOOM_query_create();
1374
1375     ZOOM_query_prefix(q, start);
1376
1377     s = ZOOM_connection_scan1(c, q);
1378     ZOOM_query_destroy(q);
1379     return s;
1380
1381 }
1382
1383 ZOOM_API(ZOOM_scanset)
1384     ZOOM_connection_scan1(ZOOM_connection c, ZOOM_query q)
1385 {
1386     ZOOM_scanset scan = 0;
1387     Z_Query *z_query = ZOOM_query_get_Z_Query(q);
1388
1389     if (!z_query)
1390         return 0;
1391     scan = (ZOOM_scanset) xmalloc(sizeof(*scan));
1392     scan->connection = c;
1393     scan->odr = odr_createmem(ODR_DECODE);
1394     scan->options = ZOOM_options_create_with_parent(c->options);
1395     scan->refcount = 1;
1396     scan->scan_response = 0;
1397     scan->srw_scan_response = 0;
1398
1399     scan->query = q;
1400     ZOOM_query_addref(q);
1401     scan->databaseNames = ZOOM_connection_get_databases(c, c->options,
1402                                             &scan->num_databaseNames,
1403                                             scan->odr);
1404
1405     if (1)
1406     {
1407         ZOOM_task task = ZOOM_connection_add_task(c, ZOOM_TASK_SCAN);
1408         task->u.scan.scan = scan;
1409         
1410         (scan->refcount)++;
1411         if (!c->async)
1412         {
1413             while (ZOOM_event(1, &c))
1414                 ;
1415         }
1416     }
1417     return scan;
1418 }
1419
1420 ZOOM_API(void)
1421     ZOOM_scanset_destroy(ZOOM_scanset scan)
1422 {
1423     if (!scan)
1424         return;
1425     (scan->refcount)--;
1426     if (scan->refcount == 0)
1427     {
1428         ZOOM_query_destroy(scan->query);
1429
1430         odr_destroy(scan->odr);
1431         
1432         ZOOM_options_destroy(scan->options);
1433         xfree(scan);
1434     }
1435 }
1436
1437 static zoom_ret send_package(ZOOM_connection c)
1438 {
1439     ZOOM_Event event;
1440
1441     yaz_log(c->log_details, "%p send_package", c);
1442     if (!c->tasks)
1443         return zoom_complete;
1444     assert (c->tasks->which == ZOOM_TASK_PACKAGE);
1445     
1446     event = ZOOM_Event_create(ZOOM_EVENT_SEND_APDU);
1447     ZOOM_connection_put_event(c, event);
1448     
1449     c->buf_out = c->tasks->u.package->buf_out;
1450     c->len_out = c->tasks->u.package->len_out;
1451
1452     return ZOOM_send_buf(c);
1453 }
1454
1455 #if YAZ_HAVE_XML2
1456 static zoom_ret ZOOM_connection_srw_send_scan(ZOOM_connection c)
1457 {
1458     ZOOM_scanset scan;
1459     Z_SRW_PDU *sr = 0;
1460     const char *option_val = 0;
1461     Z_Query *z_query;
1462
1463     if (!c->tasks)
1464         return zoom_complete;
1465     assert (c->tasks->which == ZOOM_TASK_SCAN);
1466     scan = c->tasks->u.scan.scan;
1467         
1468     sr = ZOOM_srw_get_pdu(c, Z_SRW_scan_request);
1469
1470     z_query = ZOOM_query_get_Z_Query(scan->query);
1471     /* SRU scan can only carry CQL and PQF */
1472     if (z_query->which == Z_Query_type_104)
1473     {
1474         sr->u.scan_request->query_type = Z_SRW_query_type_cql;
1475         sr->u.scan_request->scanClause.cql =
1476             ZOOM_query_get_query_string(scan->query);
1477     }
1478     else if (z_query->which == Z_Query_type_1
1479              || z_query->which == Z_Query_type_101)
1480     {
1481         sr->u.scan_request->query_type = Z_SRW_query_type_pqf;
1482         sr->u.scan_request->scanClause.pqf =
1483             ZOOM_query_get_query_string(scan->query);
1484     }
1485     else
1486     {
1487         ZOOM_set_error(c, ZOOM_ERROR_UNSUPPORTED_QUERY, 0);
1488         return zoom_complete;
1489     }
1490
1491     sr->u.scan_request->maximumTerms = odr_intdup(
1492         c->odr_out, ZOOM_options_get_int(scan->options, "number", 10));
1493     
1494     sr->u.scan_request->responsePosition = odr_intdup(
1495         c->odr_out, ZOOM_options_get_int(scan->options, "position", 1));
1496     
1497     option_val = ZOOM_options_get(scan->options, "extraArgs");
1498     yaz_encode_sru_extra(sr, c->odr_out, option_val);
1499     return send_srw(c, sr);
1500 }
1501 #else
1502 static zoom_ret ZOOM_connection_srw_send_scan(ZOOM_connection c)
1503 {
1504     return zoom_complete;
1505 }
1506 #endif
1507
1508
1509 ZOOM_API(size_t)
1510     ZOOM_scanset_size(ZOOM_scanset scan)
1511 {
1512     if (!scan)
1513         return 0;
1514
1515     if (scan->scan_response && scan->scan_response->entries)
1516         return scan->scan_response->entries->num_entries;
1517     else if (scan->srw_scan_response)
1518         return scan->srw_scan_response->num_terms;
1519     return 0;
1520 }
1521
1522 static void ZOOM_scanset_term_x(ZOOM_scanset scan, size_t pos,
1523                                 size_t *occ,
1524                                 const char **value_term, size_t *value_len,
1525                                 const char **disp_term, size_t *disp_len)
1526 {
1527     size_t noent = ZOOM_scanset_size(scan);
1528     
1529     *value_term = 0;
1530     *value_len = 0;
1531
1532     *disp_term = 0;
1533     *disp_len = 0;
1534
1535     *occ = 0;
1536     if (pos >= noent)
1537         return;
1538     if (scan->scan_response)
1539     {
1540         Z_ScanResponse *res = scan->scan_response;
1541         if (res->entries->entries[pos]->which == Z_Entry_termInfo)
1542         {
1543             Z_TermInfo *t = res->entries->entries[pos]->u.termInfo;
1544             
1545             *value_term = (const char *) t->term->u.general->buf;
1546             *value_len = t->term->u.general->len;
1547             if (t->displayTerm)
1548             {
1549                 *disp_term = t->displayTerm;
1550                 *disp_len = strlen(*disp_term);
1551             }
1552             else if (t->term->which == Z_Term_general)
1553             {
1554                 *disp_term = (const char *) t->term->u.general->buf;
1555                 *disp_len = t->term->u.general->len;
1556             }
1557             *occ = t->globalOccurrences ? *t->globalOccurrences : 0;
1558         }
1559     }
1560     if (scan->srw_scan_response)
1561     {
1562         Z_SRW_scanResponse *res = scan->srw_scan_response;
1563         Z_SRW_scanTerm *t = res->terms + pos;
1564         if (t)
1565         {
1566             *value_term = t->value;
1567             *value_len = strlen(*value_term);
1568
1569             if (t->displayTerm)
1570                 *disp_term = t->displayTerm;
1571             else
1572                 *disp_term = t->value;
1573             *disp_len = strlen(*disp_term);
1574             *occ = t->numberOfRecords ? *t->numberOfRecords : 0;
1575         }
1576     }
1577 }
1578
1579 ZOOM_API(const char *)
1580     ZOOM_scanset_term(ZOOM_scanset scan, size_t pos,
1581                       size_t *occ, size_t *len)
1582 {
1583     const char *value_term = 0;
1584     size_t value_len = 0;
1585     const char *disp_term = 0;
1586     size_t disp_len = 0;
1587
1588     ZOOM_scanset_term_x(scan, pos, occ, &value_term, &value_len,
1589                         &disp_term, &disp_len);
1590     
1591     *len = value_len;
1592     return value_term;
1593 }
1594
1595 ZOOM_API(const char *)
1596     ZOOM_scanset_display_term(ZOOM_scanset scan, size_t pos,
1597                               size_t *occ, size_t *len)
1598 {
1599     const char *value_term = 0;
1600     size_t value_len = 0;
1601     const char *disp_term = 0;
1602     size_t disp_len = 0;
1603
1604     ZOOM_scanset_term_x(scan, pos, occ, &value_term, &value_len,
1605                         &disp_term, &disp_len);
1606     
1607     *len = disp_len;
1608     return disp_term;
1609 }
1610
1611 ZOOM_API(const char *)
1612     ZOOM_scanset_option_get(ZOOM_scanset scan, const char *key)
1613 {
1614     return ZOOM_options_get(scan->options, key);
1615 }
1616
1617 ZOOM_API(void)
1618     ZOOM_scanset_option_set(ZOOM_scanset scan, const char *key,
1619                             const char *val)
1620 {
1621     ZOOM_options_set(scan->options, key, val);
1622 }
1623
1624
1625 ZOOM_API(ZOOM_package)
1626     ZOOM_connection_package(ZOOM_connection c, ZOOM_options options)
1627 {
1628     ZOOM_package p = (ZOOM_package) xmalloc(sizeof(*p));
1629
1630     p->connection = c;
1631     p->odr_out = odr_createmem(ODR_ENCODE);
1632     p->options = ZOOM_options_create_with_parent2(options, c->options);
1633     p->refcount = 1;
1634     p->buf_out = 0;
1635     p->len_out = 0;
1636     return p;
1637 }
1638
1639 ZOOM_API(void)
1640     ZOOM_package_destroy(ZOOM_package p)
1641 {
1642     if (!p)
1643         return;
1644     (p->refcount)--;
1645     if (p->refcount == 0)
1646     {
1647         odr_destroy(p->odr_out);
1648         xfree(p->buf_out);
1649         
1650         ZOOM_options_destroy(p->options);
1651         xfree(p);
1652     }
1653 }
1654
1655 ZOOM_API(const char *)
1656     ZOOM_package_option_get(ZOOM_package p, const char *key)
1657 {
1658     return ZOOM_options_get(p->options, key);
1659 }
1660
1661 ZOOM_API(const char *)
1662     ZOOM_package_option_getl(ZOOM_package p, const char *key, int *lenp)
1663 {
1664     return ZOOM_options_getl(p->options, key, lenp);
1665 }
1666
1667 ZOOM_API(void)
1668     ZOOM_package_option_set(ZOOM_package p, const char *key,
1669                             const char *val)
1670 {
1671     ZOOM_options_set(p->options, key, val);
1672 }
1673
1674 ZOOM_API(void)
1675     ZOOM_package_option_setl(ZOOM_package p, const char *key,
1676                              const char *val, int len)
1677 {
1678     ZOOM_options_setl(p->options, key, val, len);
1679 }
1680
1681 ZOOM_API(int)
1682     ZOOM_connection_exec_task(ZOOM_connection c)
1683 {
1684     ZOOM_task task = c->tasks;
1685     zoom_ret ret = zoom_complete;
1686
1687     if (!task)
1688         return 0;
1689     yaz_log(c->log_details, "%p ZOOM_connection_exec_task type=%d run=%d",
1690             c, task->which, task->running);
1691     if (c->error != ZOOM_ERROR_NONE)
1692     {
1693         yaz_log(c->log_details, "%p ZOOM_connection_exec_task "
1694                 "removing tasks because of error = %d", c, c->error);
1695         ZOOM_connection_remove_tasks(c);
1696         return 0;
1697     }
1698     if (task->running)
1699     {
1700         yaz_log(c->log_details, "%p ZOOM_connection_exec_task "
1701                 "task already running", c);
1702         return 0;
1703     }
1704     task->running = 1;
1705     ret = zoom_complete;
1706     if (c->cs || task->which == ZOOM_TASK_CONNECT)
1707     {
1708         switch (task->which)
1709         {
1710         case ZOOM_TASK_SEARCH:
1711             if (c->proto == PROTO_HTTP)
1712                 ret = ZOOM_connection_srw_send_search(c);
1713             else
1714                 ret = ZOOM_connection_Z3950_send_search(c);
1715             break;
1716         case ZOOM_TASK_RETRIEVE:
1717             if (c->proto == PROTO_HTTP)
1718                 ret = ZOOM_connection_srw_send_search(c);
1719             else
1720                 ret = send_Z3950_present(c);
1721             break;
1722         case ZOOM_TASK_CONNECT:
1723             ret = do_connect(c);
1724             break;
1725         case ZOOM_TASK_SCAN:
1726             if (c->proto == PROTO_HTTP)
1727                 ret = ZOOM_connection_srw_send_scan(c);
1728             else
1729                 ret = ZOOM_connection_Z3950_send_scan(c);
1730             break;
1731         case ZOOM_TASK_PACKAGE:
1732             ret = send_package(c);
1733             break;
1734         case ZOOM_TASK_SORT:
1735             c->tasks->u.sort.resultset->r_sort_spec = 
1736                 ZOOM_query_get_sortspec(c->tasks->u.sort.q);
1737             ret = send_Z3950_sort(c, c->tasks->u.sort.resultset);
1738             break;
1739         }
1740     }
1741     else
1742     {
1743         yaz_log(c->log_details, "%p ZOOM_connection_exec_task "
1744                 "remove tasks because no connection exist", c);
1745         ZOOM_connection_remove_tasks(c);
1746     }
1747     if (ret == zoom_complete)
1748     {
1749         yaz_log(c->log_details, "%p ZOOM_connection_exec_task "
1750                 "task removed (complete)", c);
1751         ZOOM_connection_remove_task(c);
1752         return 0;
1753     }
1754     yaz_log(c->log_details, "%p ZOOM_connection_exec_task "
1755             "task pending", c);
1756     return 1;
1757 }
1758
1759 #if YAZ_HAVE_XML2
1760 static zoom_ret handle_srw_response(ZOOM_connection c,
1761                                     Z_SRW_searchRetrieveResponse *res)
1762 {
1763     ZOOM_resultset resultset = 0;
1764     int i;
1765     NMEM nmem;
1766     ZOOM_Event event;
1767     int *start, *count;
1768     const char *syntax, *elementSetName;
1769
1770     if (!c->tasks)
1771         return zoom_complete;
1772
1773     switch(c->tasks->which)
1774     {
1775     case ZOOM_TASK_SEARCH:
1776         resultset = c->tasks->u.search.resultset;
1777         start = &c->tasks->u.search.start;
1778         count = &c->tasks->u.search.count;
1779         syntax = c->tasks->u.search.syntax;
1780         elementSetName = c->tasks->u.search.elementSetName;        
1781
1782         if (!c->tasks->u.search.recv_search_fired)
1783         {
1784             event = ZOOM_Event_create(ZOOM_EVENT_RECV_SEARCH);
1785             ZOOM_connection_put_event(c, event);
1786             c->tasks->u.search.recv_search_fired = 1;
1787         }
1788         break;
1789     case ZOOM_TASK_RETRIEVE:
1790         resultset = c->tasks->u.retrieve.resultset;
1791         start = &c->tasks->u.retrieve.start;
1792         count = &c->tasks->u.retrieve.count;
1793         syntax = c->tasks->u.retrieve.syntax;
1794         elementSetName = c->tasks->u.retrieve.elementSetName;
1795         break;
1796     default:
1797         return zoom_complete;
1798     }
1799
1800     resultset->size = 0;
1801
1802     if (res->resultSetId)
1803         ZOOM_resultset_option_set(resultset, "resultSetId", res->resultSetId);
1804
1805     yaz_log(c->log_details, "%p handle_srw_response got SRW response OK", c);
1806
1807     if (res->num_diagnostics > 0)
1808     {
1809         set_SRU_error(c, &res->diagnostics[0]);
1810     }
1811     else
1812     {
1813         if (res->numberOfRecords)
1814             resultset->size = *res->numberOfRecords;
1815         for (i = 0; i<res->num_records; i++)
1816         {
1817             int pos;
1818             Z_SRW_record *sru_rec;
1819             Z_SRW_diagnostic *diag = 0;
1820             int num_diag;
1821             
1822             Z_NamePlusRecord *npr = (Z_NamePlusRecord *)
1823                 odr_malloc(c->odr_in, sizeof(Z_NamePlusRecord));
1824             
1825             if (res->records[i].recordPosition && 
1826                 *res->records[i].recordPosition > 0)
1827                 pos = *res->records[i].recordPosition - 1;
1828             else
1829                 pos = *start + i;
1830             
1831             sru_rec = &res->records[i];
1832             
1833             npr->databaseName = 0;
1834             npr->which = Z_NamePlusRecord_databaseRecord;
1835             npr->u.databaseRecord = (Z_External *)
1836                 odr_malloc(c->odr_in, sizeof(Z_External));
1837             npr->u.databaseRecord->descriptor = 0;
1838             npr->u.databaseRecord->direct_reference =
1839                 odr_oiddup(c->odr_in, yaz_oid_recsyn_xml);
1840             npr->u.databaseRecord->which = Z_External_octet;
1841             
1842             npr->u.databaseRecord->u.octet_aligned = (Odr_oct *)
1843                 odr_malloc(c->odr_in, sizeof(Odr_oct));
1844             npr->u.databaseRecord->u.octet_aligned->buf = (unsigned char*)
1845                 sru_rec->recordData_buf;
1846             npr->u.databaseRecord->u.octet_aligned->len = 
1847                 npr->u.databaseRecord->u.octet_aligned->size = 
1848                 sru_rec->recordData_len;
1849             
1850             if (sru_rec->recordSchema 
1851                 && !strcmp(sru_rec->recordSchema,
1852                            "info:srw/schema/1/diagnostics-v1.1"))
1853             {
1854                 sru_decode_surrogate_diagnostics(sru_rec->recordData_buf,
1855                                                  sru_rec->recordData_len,
1856                                                  &diag, &num_diag,
1857                                                  resultset->odr);
1858             }
1859             ZOOM_record_cache_add(resultset, npr, pos, syntax, elementSetName,
1860                                   sru_rec->recordSchema, diag);
1861         }
1862         *count -= i;
1863         *start += i;
1864         if (*count + *start > resultset->size)
1865             *count = resultset->size - *start;
1866         if (*count < 0)
1867             *count = 0;
1868         
1869         nmem = odr_extract_mem(c->odr_in);
1870         nmem_transfer(odr_getmem(resultset->odr), nmem);
1871         nmem_destroy(nmem);
1872
1873         if (*count > 0)
1874             return ZOOM_connection_srw_send_search(c);
1875     }
1876     return zoom_complete;
1877 }
1878 #endif
1879
1880 #if YAZ_HAVE_XML2
1881 static void handle_srw_scan_response(ZOOM_connection c,
1882                                      Z_SRW_scanResponse *res)
1883 {
1884     NMEM nmem = odr_extract_mem(c->odr_in);
1885     ZOOM_scanset scan;
1886
1887     if (!c->tasks || c->tasks->which != ZOOM_TASK_SCAN)
1888         return;
1889     scan = c->tasks->u.scan.scan;
1890
1891     if (res->num_diagnostics > 0)
1892         set_SRU_error(c, &res->diagnostics[0]);
1893
1894     scan->scan_response = 0;
1895     scan->srw_scan_response = res;
1896     nmem_transfer(odr_getmem(scan->odr), nmem);
1897
1898     ZOOM_options_set_int(scan->options, "number", res->num_terms);
1899     nmem_destroy(nmem);
1900 }
1901 #endif
1902
1903 #if YAZ_HAVE_XML2
1904 static Z_GDU *get_HTTP_Request_url(ODR odr, const char *url)
1905 {
1906     Z_GDU *p = z_get_HTTP_Request(odr);
1907     const char *host = url;
1908     const char *cp0 = strstr(host, "://");
1909     const char *cp1 = 0;
1910     if (cp0)
1911         cp0 = cp0+3;
1912     else
1913         cp0 = host;
1914     
1915     cp1 = strchr(cp0, '/');
1916     if (!cp1)
1917         cp1 = cp0 + strlen(cp0);
1918     
1919     if (cp0 && cp1)
1920     {
1921         char *h = (char*) odr_malloc(odr, cp1 - cp0 + 1);
1922         memcpy (h, cp0, cp1 - cp0);
1923         h[cp1-cp0] = '\0';
1924         z_HTTP_header_add(odr, &p->u.HTTP_Request->headers, "Host", h);
1925     }
1926     p->u.HTTP_Request->path = odr_strdup(odr, *cp1 ? cp1 : "/");
1927     return p;
1928 }
1929
1930 static zoom_ret send_SRW_redirect(ZOOM_connection c, const char *uri,
1931                                   Z_HTTP_Response *cookie_hres)
1932 {
1933     struct Z_HTTP_Header *h;
1934     Z_GDU *gdu = get_HTTP_Request_url(c->odr_out, uri);
1935     char *combined_cookies;
1936     int combined_cookies_len = 0;
1937
1938     gdu->u.HTTP_Request->method = odr_strdup(c->odr_out, "GET");
1939     z_HTTP_header_add(c->odr_out, &gdu->u.HTTP_Request->headers, "Accept",
1940                       "text/xml");
1941
1942     for (h = cookie_hres->headers; h; h = h->next)
1943     {
1944         if (!strcmp(h->name, "Set-Cookie"))
1945         {
1946             char *cp;
1947
1948             if (!(cp = strchr(h->value, ';')))
1949                 cp = h->value + strlen(h->value);
1950             if (cp - h->value >= 1) {
1951                 combined_cookies = xrealloc(combined_cookies, combined_cookies_len + cp - h->value + 3);
1952                 memcpy(combined_cookies+combined_cookies_len, h->value, cp - h->value);
1953                 combined_cookies[combined_cookies_len + cp - h->value] = '\0';
1954                 strcat(combined_cookies,"; ");
1955                 combined_cookies_len = strlen(combined_cookies);
1956             }
1957         }
1958     }
1959
1960     if (combined_cookies_len)
1961     {
1962         z_HTTP_header_add(c->odr_out, &gdu->u.HTTP_Request->headers,
1963                           "Cookie", combined_cookies);
1964         xfree(combined_cookies);
1965     }
1966
1967     if (c->user && c->password)
1968     {
1969         z_HTTP_header_add_basic_auth(c->odr_out, &gdu->u.HTTP_Request->headers,
1970                                      c->user, c->password);
1971     }
1972     if (!z_GDU(c->odr_out, &gdu, 0, 0))
1973         return zoom_complete;
1974     if (c->odr_print)
1975         z_GDU(c->odr_print, &gdu, 0, 0);
1976     c->buf_out = odr_getbuf(c->odr_out, &c->len_out, 0);
1977
1978     odr_reset(c->odr_out);
1979     return ZOOM_send_buf(c);
1980 }
1981
1982 static void handle_http(ZOOM_connection c, Z_HTTP_Response *hres)
1983 {
1984     zoom_ret cret = zoom_complete;
1985     int ret = -1;
1986     const char *addinfo = 0;
1987     const char *connection_head = z_HTTP_header_lookup(hres->headers,
1988                                                        "Connection");
1989     const char *location;
1990
1991     ZOOM_connection_set_mask(c, 0);
1992     yaz_log(c->log_details, "%p handle_http", c);
1993     
1994     if ((hres->code == 301 || hres->code == 302) && c->sru_mode == zoom_sru_get
1995         && (location = z_HTTP_header_lookup(hres->headers, "Location")))
1996     {
1997         c->no_redirects++;
1998         if (c->no_redirects > 10)
1999         {
2000             set_HTTP_error(c, hres->code, 0, 0);
2001             c->no_redirects = 0;
2002             ZOOM_connection_close(c);
2003         }
2004         else
2005         {
2006             /* since redirect may change host we just reconnect. A smarter
2007                implementation might check whether it's the same server */
2008             do_connect_host(c, location, 0);
2009             send_SRW_redirect(c, location, hres);
2010             /* we're OK for now. Operation is not really complete */
2011             ret = 0;
2012             cret = zoom_pending;
2013         }
2014     }
2015     else 
2016     {   /* not redirect (normal response) */
2017         if (!yaz_srw_check_content_type(hres))
2018             addinfo = "content-type";
2019         else
2020         {
2021             Z_SOAP *soap_package = 0;
2022             ODR o = c->odr_in;
2023             Z_SOAP_Handler soap_handlers[2] = {
2024                 {YAZ_XMLNS_SRU_v1_1, 0, (Z_SOAP_fun) yaz_srw_codec},
2025                 {0, 0, 0}
2026             };
2027             ret = z_soap_codec(o, &soap_package,
2028                                &hres->content_buf, &hres->content_len,
2029                                soap_handlers);
2030             if (!ret && soap_package->which == Z_SOAP_generic &&
2031                 soap_package->u.generic->no == 0)
2032             {
2033                 Z_SRW_PDU *sr = (Z_SRW_PDU*) soap_package->u.generic->p;
2034                 
2035                 ZOOM_options_set(c->options, "sru_version", sr->srw_version);
2036                 ZOOM_options_setl(c->options, "sru_extra_response_data",
2037                                   sr->extraResponseData_buf, sr->extraResponseData_len);
2038                 if (sr->which == Z_SRW_searchRetrieve_response)
2039                     cret = handle_srw_response(c, sr->u.response);
2040                 else if (sr->which == Z_SRW_scan_response)
2041                     handle_srw_scan_response(c, sr->u.scan_response);
2042                 else
2043                     ret = -1;
2044             }
2045             else if (!ret && (soap_package->which == Z_SOAP_fault
2046                               || soap_package->which == Z_SOAP_error))
2047             {
2048                 set_HTTP_error(c, hres->code,
2049                                soap_package->u.fault->fault_code,
2050                                soap_package->u.fault->fault_string);
2051             }
2052             else
2053                 ret = -1;
2054         }   
2055         if (ret == 0)
2056         {
2057             if (c->no_redirects) /* end of redirect. change hosts again */
2058                 ZOOM_connection_close(c);
2059         }
2060         c->no_redirects = 0;
2061     }
2062     if (ret)
2063     {
2064         if (hres->code != 200)
2065             set_HTTP_error(c, hres->code, 0, 0);
2066         else
2067             ZOOM_set_error(c, ZOOM_ERROR_DECODE, addinfo);
2068         ZOOM_connection_close(c);
2069     }
2070     if (cret == zoom_complete)
2071     {
2072         yaz_log(YLOG_LOG, "removing tasks in handle_http");
2073         ZOOM_connection_remove_task(c);
2074     }
2075     {
2076         int must_close = 0;
2077         if (!strcmp(hres->version, "1.0"))
2078         {
2079             /* HTTP 1.0: only if Keep-Alive we stay alive.. */
2080             if (!connection_head || strcmp(connection_head, "Keep-Alive"))
2081                 must_close = 1;
2082         }
2083         else
2084         {
2085             /* HTTP 1.1: only if no close we stay alive.. */
2086             if (connection_head && !strcmp(connection_head, "close"))
2087                 must_close = 1;
2088         }
2089         if (must_close)
2090         {
2091             ZOOM_connection_close(c);
2092             if (c->tasks)
2093             {
2094                 c->tasks->running = 0;
2095                 ZOOM_connection_insert_task(c, ZOOM_TASK_CONNECT);
2096                 c->reconnect_ok = 0;
2097             }
2098         }
2099     }
2100 }
2101 #endif
2102
2103 static int do_read(ZOOM_connection c)
2104 {
2105     int r, more;
2106     ZOOM_Event event;
2107     
2108     event = ZOOM_Event_create(ZOOM_EVENT_RECV_DATA);
2109     ZOOM_connection_put_event(c, event);
2110     
2111     r = cs_get(c->cs, &c->buf_in, &c->len_in);
2112     more = cs_more(c->cs);
2113     yaz_log(c->log_details, "%p do_read len=%d more=%d", c, r, more);
2114     if (r == 1)
2115         return 0;
2116     if (r <= 0)
2117     {
2118         if (!ZOOM_test_reconnect(c))
2119         {
2120             ZOOM_set_error(c, ZOOM_ERROR_CONNECTION_LOST, c->host_port);
2121             ZOOM_connection_close(c);
2122         }
2123     }
2124     else
2125     {
2126         Z_GDU *gdu;
2127         ZOOM_Event event;
2128
2129         odr_reset(c->odr_in);
2130         odr_setbuf(c->odr_in, c->buf_in, r, 0);
2131         event = ZOOM_Event_create(ZOOM_EVENT_RECV_APDU);
2132         ZOOM_connection_put_event(c, event);
2133
2134         if (!z_GDU(c->odr_in, &gdu, 0, 0))
2135         {
2136             int x;
2137             int err = odr_geterrorx(c->odr_in, &x);
2138             char msg[100];
2139             const char *element = odr_getelement(c->odr_in);
2140             yaz_snprintf(msg, sizeof(msg),
2141                     "ODR code %d:%d element=%s offset=%d",
2142                     err, x, element ? element : "<unknown>",
2143                     odr_offset(c->odr_in));
2144             ZOOM_set_error(c, ZOOM_ERROR_DECODE, msg);
2145             if (c->log_api)
2146             {
2147                 FILE *ber_file = yaz_log_file();
2148                 if (ber_file)
2149                     odr_dumpBER(ber_file, c->buf_in, r);
2150             }
2151             ZOOM_connection_close(c);
2152         }
2153         else
2154         {
2155             if (c->odr_print)
2156                 z_GDU(c->odr_print, &gdu, 0, 0);
2157             if (gdu->which == Z_GDU_Z3950)
2158                 ZOOM_handle_Z3950_apdu(c, gdu->u.z3950);
2159             else if (gdu->which == Z_GDU_HTTP_Response)
2160             {
2161 #if YAZ_HAVE_XML2
2162                 handle_http(c, gdu->u.HTTP_Response);
2163 #else
2164                 ZOOM_set_error(c, ZOOM_ERROR_DECODE, 0);
2165                 ZOOM_connection_close(c);
2166 #endif
2167             }
2168         }
2169         c->reconnect_ok = 0;
2170     }
2171     return 1;
2172 }
2173
2174 static zoom_ret do_write_ex(ZOOM_connection c, char *buf_out, int len_out)
2175 {
2176     int r;
2177     ZOOM_Event event;
2178     
2179     event = ZOOM_Event_create(ZOOM_EVENT_SEND_DATA);
2180     ZOOM_connection_put_event(c, event);
2181
2182     yaz_log(c->log_details, "%p do_write_ex len=%d", c, len_out);
2183     if ((r = cs_put(c->cs, buf_out, len_out)) < 0)
2184     {
2185         yaz_log(c->log_details, "%p do_write_ex write failed", c);
2186         if (ZOOM_test_reconnect(c))
2187         {
2188             return zoom_pending;
2189         }
2190         if (c->state == STATE_CONNECTING)
2191             ZOOM_set_error(c, ZOOM_ERROR_CONNECT, c->host_port);
2192         else
2193             ZOOM_set_error(c, ZOOM_ERROR_CONNECTION_LOST, c->host_port);
2194         ZOOM_connection_close(c);
2195         return zoom_complete;
2196     }
2197     else if (r == 1)
2198     {    
2199         int mask = ZOOM_SELECT_EXCEPT;
2200         if (c->cs->io_pending & CS_WANT_WRITE)
2201             mask += ZOOM_SELECT_WRITE;
2202         if (c->cs->io_pending & CS_WANT_READ)
2203             mask += ZOOM_SELECT_READ;
2204         ZOOM_connection_set_mask(c, mask);
2205         yaz_log(c->log_details, "%p do_write_ex write incomplete mask=%d",
2206                 c, c->mask);
2207     }
2208     else
2209     {
2210         ZOOM_connection_set_mask(c, ZOOM_SELECT_READ|ZOOM_SELECT_EXCEPT);
2211         yaz_log(c->log_details, "%p do_write_ex write complete mask=%d",
2212                 c, c->mask);
2213     }
2214     return zoom_pending;
2215 }
2216
2217 zoom_ret ZOOM_send_buf(ZOOM_connection c)
2218 {
2219     return do_write_ex(c, c->buf_out, c->len_out);
2220 }
2221
2222
2223 ZOOM_API(const char *)
2224     ZOOM_connection_option_get(ZOOM_connection c, const char *key)
2225 {
2226     return ZOOM_options_get(c->options, key);
2227 }
2228
2229 ZOOM_API(const char *)
2230     ZOOM_connection_option_getl(ZOOM_connection c, const char *key, int *lenp)
2231 {
2232     return ZOOM_options_getl(c->options, key, lenp);
2233 }
2234
2235 ZOOM_API(void)
2236     ZOOM_connection_option_set(ZOOM_connection c, const char *key,
2237                                const char *val)
2238 {
2239     ZOOM_options_set(c->options, key, val);
2240 }
2241
2242 ZOOM_API(void)
2243     ZOOM_connection_option_setl(ZOOM_connection c, const char *key,
2244                                 const char *val, int len)
2245 {
2246     ZOOM_options_setl(c->options, key, val, len);
2247 }
2248
2249 ZOOM_API(const char *)
2250     ZOOM_resultset_option_get(ZOOM_resultset r, const char *key)
2251 {
2252     return ZOOM_options_get(r->options, key);
2253 }
2254
2255 ZOOM_API(void)
2256     ZOOM_resultset_option_set(ZOOM_resultset r, const char *key,
2257                               const char *val)
2258 {
2259     ZOOM_options_set(r->options, key, val);
2260 }
2261
2262
2263 ZOOM_API(int)
2264     ZOOM_connection_errcode(ZOOM_connection c)
2265 {
2266     return ZOOM_connection_error(c, 0, 0);
2267 }
2268
2269 ZOOM_API(const char *)
2270     ZOOM_connection_errmsg(ZOOM_connection c)
2271 {
2272     const char *msg;
2273     ZOOM_connection_error(c, &msg, 0);
2274     return msg;
2275 }
2276
2277 ZOOM_API(const char *)
2278     ZOOM_connection_addinfo(ZOOM_connection c)
2279 {
2280     const char *addinfo;
2281     ZOOM_connection_error(c, 0, &addinfo);
2282     return addinfo;
2283 }
2284
2285 ZOOM_API(const char *)
2286     ZOOM_connection_diagset(ZOOM_connection c)
2287 {
2288     const char *diagset;
2289     ZOOM_connection_error_x(c, 0, 0, &diagset);
2290     return diagset;
2291 }
2292
2293 ZOOM_API(const char *)
2294     ZOOM_diag_str(int error)
2295 {
2296     switch (error)
2297     {
2298     case ZOOM_ERROR_NONE:
2299         return "No error";
2300     case ZOOM_ERROR_CONNECT:
2301         return "Connect failed";
2302     case ZOOM_ERROR_MEMORY:
2303         return "Out of memory";
2304     case ZOOM_ERROR_ENCODE:
2305         return "Encoding failed";
2306     case ZOOM_ERROR_DECODE:
2307         return "Decoding failed";
2308     case ZOOM_ERROR_CONNECTION_LOST:
2309         return "Connection lost";
2310     case ZOOM_ERROR_INIT:
2311         return "Init rejected";
2312     case ZOOM_ERROR_INTERNAL:
2313         return "Internal failure";
2314     case ZOOM_ERROR_TIMEOUT:
2315         return "Timeout";
2316     case ZOOM_ERROR_UNSUPPORTED_PROTOCOL:
2317         return "Unsupported protocol";
2318     case ZOOM_ERROR_UNSUPPORTED_QUERY:
2319         return "Unsupported query type";
2320     case ZOOM_ERROR_INVALID_QUERY:
2321         return "Invalid query";
2322     case ZOOM_ERROR_CQL_PARSE:
2323         return "CQL parsing error";
2324     case ZOOM_ERROR_CQL_TRANSFORM:
2325         return "CQL transformation error";
2326     case ZOOM_ERROR_CCL_CONFIG:
2327         return "CCL configuration error";
2328     case ZOOM_ERROR_CCL_PARSE:
2329         return "CCL parsing error";
2330     default:
2331         return diagbib1_str(error);
2332     }
2333 }
2334
2335 ZOOM_API(int)
2336     ZOOM_connection_error_x(ZOOM_connection c, const char **cp,
2337                             const char **addinfo, const char **diagset)
2338 {
2339     int error = c->error;
2340     if (cp)
2341     {
2342         if (!c->diagset || !strcmp(c->diagset, "ZOOM"))
2343             *cp = ZOOM_diag_str(error);
2344         else if (!strcmp(c->diagset, "HTTP"))
2345             *cp = z_HTTP_errmsg(c->error);
2346         else if (!strcmp(c->diagset, "Bib-1"))
2347             *cp = ZOOM_diag_str(error);
2348         else if (!strcmp(c->diagset, "info:srw/diagnostic/1"))
2349             *cp = yaz_diag_srw_str(c->error);
2350         else
2351             *cp = "Unknown error and diagnostic set";
2352     }
2353     if (addinfo)
2354         *addinfo = c->addinfo ? c->addinfo : "";
2355     if (diagset)
2356         *diagset = c->diagset ? c->diagset : "";
2357     return c->error;
2358 }
2359
2360 ZOOM_API(int)
2361     ZOOM_connection_error(ZOOM_connection c, const char **cp,
2362                           const char **addinfo)
2363 {
2364     return ZOOM_connection_error_x(c, cp, addinfo, 0);
2365 }
2366
2367 static void ZOOM_connection_do_io(ZOOM_connection c, int mask)
2368 {
2369     ZOOM_Event event = 0;
2370     int r = cs_look(c->cs);
2371     yaz_log(c->log_details, "%p ZOOM_connection_do_io mask=%d cs_look=%d",
2372             c, mask, r);
2373     
2374     if (r == CS_NONE)
2375     {
2376         event = ZOOM_Event_create(ZOOM_EVENT_CONNECT);
2377         ZOOM_set_error(c, ZOOM_ERROR_CONNECT, c->host_port);
2378         ZOOM_connection_close(c);
2379         ZOOM_connection_put_event(c, event);
2380     }
2381     else if (r == CS_CONNECT)
2382     {
2383         int ret = ret = cs_rcvconnect(c->cs);
2384         yaz_log(c->log_details, "%p ZOOM_connection_do_io "
2385                 "cs_rcvconnect returned %d", c, ret);
2386         if (ret == 1)
2387         {
2388             int mask = ZOOM_SELECT_EXCEPT;
2389             if (c->cs->io_pending & CS_WANT_WRITE)
2390                 mask += ZOOM_SELECT_WRITE;
2391             if (c->cs->io_pending & CS_WANT_READ)
2392                 mask += ZOOM_SELECT_READ;
2393             ZOOM_connection_set_mask(c, mask);
2394             event = ZOOM_Event_create(ZOOM_EVENT_NONE);
2395             ZOOM_connection_put_event(c, event);
2396         }
2397         else if (ret == 0)
2398         {
2399             event = ZOOM_Event_create(ZOOM_EVENT_CONNECT);
2400             ZOOM_connection_put_event(c, event);
2401             get_cert(c);
2402             if (c->proto == PROTO_Z3950)
2403                 ZOOM_connection_Z3950_send_init(c);
2404             else
2405             {
2406                 /* no init request for SRW .. */
2407                 assert(c->tasks->which == ZOOM_TASK_CONNECT);
2408                 ZOOM_connection_remove_task(c);
2409                 ZOOM_connection_set_mask(c, 0);
2410                 ZOOM_connection_exec_task(c);
2411             }
2412             c->state = STATE_ESTABLISHED;
2413         }
2414         else
2415         {
2416             ZOOM_set_error(c, ZOOM_ERROR_CONNECT, c->host_port);
2417             ZOOM_connection_close(c);
2418         }
2419     }
2420     else
2421     {
2422         if (mask & ZOOM_SELECT_EXCEPT)
2423         {
2424             if (!ZOOM_test_reconnect(c))
2425             {
2426                 ZOOM_set_error(c, ZOOM_ERROR_CONNECTION_LOST, c->host_port);
2427                 ZOOM_connection_close(c);
2428             }
2429             return;
2430         }
2431         if (mask & ZOOM_SELECT_READ)
2432             do_read(c);
2433         if (c->cs && (mask & ZOOM_SELECT_WRITE))
2434             ZOOM_send_buf(c);
2435     }
2436 }
2437
2438 ZOOM_API(int)
2439     ZOOM_connection_last_event(ZOOM_connection cs)
2440 {
2441     if (!cs)
2442         return ZOOM_EVENT_NONE;
2443     return cs->last_event;
2444 }
2445
2446
2447 ZOOM_API(int) ZOOM_connection_fire_event_timeout(ZOOM_connection c)
2448 {
2449     if (c->mask)
2450     {
2451         ZOOM_Event event = ZOOM_Event_create(ZOOM_EVENT_TIMEOUT);
2452         /* timeout and this connection was waiting */
2453         ZOOM_set_error(c, ZOOM_ERROR_TIMEOUT, 0);
2454         ZOOM_connection_close(c);
2455         ZOOM_connection_put_event(c, event);
2456     }
2457     return 0;
2458 }
2459
2460 ZOOM_API(int)
2461     ZOOM_connection_process(ZOOM_connection c)
2462 {
2463     ZOOM_Event event;
2464     if (!c)
2465         return 0;
2466
2467     event = ZOOM_connection_get_event(c);
2468     if (event)
2469     {
2470         ZOOM_Event_destroy(event);
2471         return 1;
2472     }
2473     ZOOM_connection_exec_task(c);
2474     event = ZOOM_connection_get_event(c);
2475     if (event)
2476     {
2477         ZOOM_Event_destroy(event);
2478         return 1;
2479     }
2480     return 0;
2481 }
2482
2483 ZOOM_API(int)
2484     ZOOM_event_nonblock(int no, ZOOM_connection *cs)
2485 {
2486     int i;
2487
2488     yaz_log(log_details0, "ZOOM_process_event(no=%d,cs=%p)", no, cs);
2489     
2490     for (i = 0; i<no; i++)
2491     {
2492         ZOOM_connection c = cs[i];
2493
2494         if (c && ZOOM_connection_process(c))
2495             return i+1;
2496     }
2497     return 0;
2498 }
2499
2500 ZOOM_API(int) ZOOM_connection_fire_event_socket(ZOOM_connection c, int mask)
2501 {
2502     if (c->mask && mask)
2503         ZOOM_connection_do_io(c, mask);
2504     return 0;
2505 }
2506
2507 ZOOM_API(int) ZOOM_connection_get_socket(ZOOM_connection c)
2508 {
2509     if (c->cs)
2510         return cs_fileno(c->cs);
2511     return -1;
2512 }
2513
2514 ZOOM_API(int) ZOOM_connection_set_mask(ZOOM_connection c, int mask)
2515 {
2516     c->mask = mask;
2517     if (!c->cs)
2518         return -1; 
2519     return 0;
2520 }
2521
2522 ZOOM_API(int) ZOOM_connection_get_mask(ZOOM_connection c)
2523 {
2524     if (c->cs)
2525         return c->mask;
2526     return 0;
2527 }
2528
2529 ZOOM_API(int) ZOOM_connection_get_timeout(ZOOM_connection c)
2530 {
2531     return ZOOM_options_get_int(c->options, "timeout", 30);
2532 }
2533
2534 ZOOM_API(void) ZOOM_connection_close(ZOOM_connection c)
2535 {
2536     if (c->cs)
2537         cs_close(c->cs);
2538     c->cs = 0;
2539     ZOOM_connection_set_mask(c, 0);
2540     c->state = STATE_IDLE;
2541 }
2542
2543 /*
2544  * Local variables:
2545  * c-basic-offset: 4
2546  * c-file-style: "Stroustrup"
2547  * indent-tabs-mode: nil
2548  * End:
2549  * vim: shiftwidth=4 tabstop=8 expandtab
2550  */
2551