Refactor record cache to separate source
[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/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>
26
27 #include <yaz/shptr.h>
28
29 #if SHPTR
30 YAZ_SHPTR_TYPE(WRBUF)
31 #endif
32
33 static int log_api0 = 0;
34 static int log_details0 = 0;
35
36 static void resultset_destroy(ZOOM_resultset r);
37 static zoom_ret do_write_ex(ZOOM_connection c, char *buf_out, int len_out);
38
39 ZOOM_API(const char *) ZOOM_get_event_str(int event)
40 {
41     static const char *ar[] = {
42         "NONE",
43         "CONNECT",
44         "SEND_DATA",
45         "RECV_DATA",
46         "TIMEOUT",
47         "UNKNOWN",
48         "SEND_APDU",
49         "RECV_APDU",
50         "RECV_RECORD",
51         "RECV_SEARCH",
52         "END"
53     };
54     return ar[event];
55 }
56
57 static void initlog(void)
58 {
59     static int log_level_initialized = 0;
60     if (!log_level_initialized)
61     {
62         log_api0 = yaz_log_module_level("zoom");
63         log_details0 = yaz_log_module_level("zoomdetails");
64         log_level_initialized = 1;
65     }
66 }
67
68 ZOOM_Event ZOOM_Event_create(int kind)
69 {
70     ZOOM_Event event = (ZOOM_Event) xmalloc(sizeof(*event));
71     event->kind = kind;
72     event->next = 0;
73     event->prev = 0;
74     return event;
75 }
76
77 static void ZOOM_Event_destroy(ZOOM_Event event)
78 {
79     xfree(event);
80 }
81
82 void ZOOM_connection_put_event(ZOOM_connection c, ZOOM_Event event)
83 {
84     if (c->m_queue_back)
85     {
86         c->m_queue_back->prev = event;
87         assert(c->m_queue_front);
88     }
89     else
90     {
91         assert(!c->m_queue_front);
92         c->m_queue_front = event;
93     }
94     event->next = c->m_queue_back;
95     event->prev = 0;
96     c->m_queue_back = event;
97 }
98
99 static ZOOM_Event ZOOM_connection_get_event(ZOOM_connection c)
100 {
101     ZOOM_Event event = c->m_queue_front;
102     if (!event)
103     {
104         c->last_event = ZOOM_EVENT_NONE;
105         return 0;
106     }
107     assert(c->m_queue_back);
108     c->m_queue_front = event->prev;
109     if (c->m_queue_front)
110     {
111         assert(c->m_queue_back);
112         c->m_queue_front->next = 0;
113     }
114     else
115         c->m_queue_back = 0;
116     c->last_event = event->kind;
117     return event;
118 }
119
120 static void ZOOM_connection_remove_events(ZOOM_connection c)
121 {
122     ZOOM_Event event;
123     while ((event = ZOOM_connection_get_event(c)))
124         ZOOM_Event_destroy(event);
125 }
126
127 ZOOM_API(int) ZOOM_connection_peek_event(ZOOM_connection c)
128 {
129     ZOOM_Event event = c->m_queue_front;
130
131     return event ? event->kind : ZOOM_EVENT_NONE;
132 }
133
134 void ZOOM_connection_remove_tasks(ZOOM_connection c);
135
136 void ZOOM_set_dset_error(ZOOM_connection c, int error,
137                          const char *dset,
138                          const char *addinfo, const char *addinfo2)
139 {
140     char *cp;
141
142     xfree(c->addinfo);
143     c->addinfo = 0;
144     c->error = error;
145     if (!c->diagset || strcmp(dset, c->diagset))
146     {
147         xfree(c->diagset);
148         c->diagset = xstrdup(dset);
149         /* remove integer part from SRW diagset .. */
150         if ((cp = strrchr(c->diagset, '/')))
151             *cp = '\0';
152     }
153     if (addinfo && addinfo2)
154     {
155         c->addinfo = (char*) xmalloc(strlen(addinfo) + strlen(addinfo2) + 2);
156         strcpy(c->addinfo, addinfo);
157         strcat(c->addinfo, addinfo2);
158     }
159     else if (addinfo)
160         c->addinfo = xstrdup(addinfo);
161     if (error != ZOOM_ERROR_NONE)
162     {
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);
168     }
169 }
170
171 int ZOOM_uri_to_code(const char *uri)
172 {
173     int code = 0;       
174     const char *cp;
175     if ((cp = strrchr(uri, '/')))
176         code = atoi(cp+1);
177     return code;
178 }
179
180
181 #if YAZ_HAVE_XML2
182 static void set_HTTP_error(ZOOM_connection c, int error,
183                            const char *addinfo, const char *addinfo2)
184 {
185     ZOOM_set_dset_error(c, error, "HTTP", addinfo, addinfo2);
186 }
187
188 static void set_SRU_error(ZOOM_connection c, Z_SRW_diagnostic *d)
189 {
190     const char *uri = d->uri;
191     if (uri)
192         ZOOM_set_dset_error(c, ZOOM_uri_to_code(uri), uri, d->details, 0);
193 }
194
195 #endif
196
197
198 void ZOOM_set_error(ZOOM_connection c, int error, const char *addinfo)
199 {
200     ZOOM_set_dset_error(c, error, "ZOOM", addinfo, 0);
201 }
202
203 static void clear_error(ZOOM_connection c)
204 {
205     /*
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.
211      */
212
213     ZOOM_connection_remove_events(c);
214     switch (c->error)
215     {
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:
223         break;
224     default:
225         ZOOM_set_error(c, ZOOM_ERROR_NONE, 0);
226     }
227 }
228
229 void ZOOM_connection_show_task(ZOOM_task task)
230 {
231     switch(task->which)
232     {
233     case ZOOM_TASK_SEARCH:
234         yaz_log(YLOG_LOG, "search p=%p", task);
235         break;
236     case ZOOM_TASK_RETRIEVE:
237         yaz_log(YLOG_LOG, "retrieve p=%p", task);
238         break;
239     case ZOOM_TASK_CONNECT:
240         yaz_log(YLOG_LOG, "connect p=%p", task);
241         break;
242     case ZOOM_TASK_SCAN:
243         yaz_log(YLOG_LOG, "scan p=%p", task);
244         break;
245     }
246 }
247
248 void ZOOM_connection_show_tasks(ZOOM_connection c)
249 {
250     ZOOM_task task;
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);
254 }
255
256 ZOOM_task ZOOM_connection_add_task(ZOOM_connection c, int which)
257 {
258     ZOOM_task *taskp = &c->tasks;
259     while (*taskp)
260         taskp = &(*taskp)->next;
261     *taskp = (ZOOM_task) xmalloc(sizeof(**taskp));
262     (*taskp)->running = 0;
263     (*taskp)->which = which;
264     (*taskp)->next = 0;
265     clear_error(c);
266     return *taskp;
267 }
268
269 ZOOM_API(int) ZOOM_connection_is_idle(ZOOM_connection c)
270 {
271     return c->tasks ? 0 : 1;
272 }
273
274 ZOOM_task ZOOM_connection_insert_task(ZOOM_connection c, int which)
275 {
276     ZOOM_task task = (ZOOM_task) xmalloc(sizeof(*task));
277
278     task->next = c->tasks;
279     c->tasks = task;
280
281     task->running = 0;
282     task->which = which;
283     clear_error(c);
284     return task;
285 }
286
287 void ZOOM_connection_remove_task(ZOOM_connection c)
288 {
289     ZOOM_task task = c->tasks;
290
291     if (task)
292     {
293         c->tasks = task->next;
294         switch (task->which)
295         {
296         case ZOOM_TASK_SEARCH:
297             resultset_destroy(task->u.search.resultset);
298             xfree(task->u.search.syntax);
299             xfree(task->u.search.elementSetName);
300             break;
301         case ZOOM_TASK_RETRIEVE:
302             resultset_destroy(task->u.retrieve.resultset);
303             xfree(task->u.retrieve.syntax);
304             xfree(task->u.retrieve.elementSetName);
305             break;
306         case ZOOM_TASK_CONNECT:
307             break;
308         case ZOOM_TASK_SCAN:
309             ZOOM_scanset_destroy(task->u.scan.scan);
310             break;
311         case ZOOM_TASK_PACKAGE:
312             ZOOM_package_destroy(task->u.package);
313             break;
314         case ZOOM_TASK_SORT:
315             resultset_destroy(task->u.sort.resultset);
316             ZOOM_query_destroy(task->u.sort.q);
317             break;
318         default:
319             assert(0);
320         }
321         xfree(task);
322
323         if (!c->tasks)
324         {
325             ZOOM_Event event = ZOOM_Event_create(ZOOM_EVENT_END);
326             ZOOM_connection_put_event(c, event);
327         }
328     }
329 }
330
331 void ZOOM_connection_remove_tasks(ZOOM_connection c)
332 {
333     while (c->tasks)
334         ZOOM_connection_remove_task(c);
335 }
336
337
338 ZOOM_API(ZOOM_connection)
339     ZOOM_connection_create(ZOOM_options options)
340 {
341     ZOOM_connection c = (ZOOM_connection) xmalloc(sizeof(*c));
342
343     initlog();
344
345     c->log_api = log_api0;
346     c->log_details = log_details0;
347
348     yaz_log(c->log_api, "%p ZOOM_connection_create", c);
349
350     c->proto = PROTO_Z3950;
351     c->cs = 0;
352     ZOOM_connection_set_mask(c, 0);
353     c->reconnect_ok = 0;
354     c->state = STATE_IDLE;
355     c->addinfo = 0;
356     c->diagset = 0;
357     ZOOM_set_error(c, ZOOM_ERROR_NONE, 0);
358     c->buf_in = 0;
359     c->len_in = 0;
360     c->buf_out = 0;
361     c->len_out = 0;
362     c->resultsets = 0;
363
364     c->options = ZOOM_options_create_with_parent(options);
365
366     c->host_port = 0;
367     c->path = 0;
368     c->proxy = 0;
369     
370     c->charset = c->lang = 0;
371
372     c->cookie_out = 0;
373     c->cookie_in = 0;
374     c->client_IP = 0;
375     c->tasks = 0;
376
377     c->user = 0;
378     c->group = 0;
379     c->password = 0;
380
381     c->maximum_record_size = 0;
382     c->preferred_message_size = 0;
383
384     c->odr_in = odr_createmem(ODR_DECODE);
385     c->odr_out = odr_createmem(ODR_ENCODE);
386     c->odr_print = 0;
387
388     c->async = 0;
389     c->support_named_resultsets = 0;
390     c->last_event = ZOOM_EVENT_NONE;
391
392     c->m_queue_front = 0;
393     c->m_queue_back = 0;
394
395     c->sru_version = 0;
396     c->no_redirects = 0;
397     return c;
398 }
399
400
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,
404                                      int *num, ODR odr)
405 {
406     char **databaseNames;
407     const char *cp = ZOOM_options_get(options, "databaseName");
408     
409     if ((!cp || !*cp) && con->host_port)
410     {
411         if (strncmp(con->host_port, "unix:", 5) == 0)
412             cp = strchr(con->host_port+5, ':');
413         else
414             cp = strchr(con->host_port, '/');
415         if (cp)
416             cp++;
417     }
418     if (!cp)
419         cp = "Default";
420     nmem_strsplit(odr_getmem(odr), "+", cp,  &databaseNames, num);
421     return databaseNames;
422 }
423
424 ZOOM_API(ZOOM_connection)
425     ZOOM_connection_new(const char *host, int portnum)
426 {
427     ZOOM_connection c = ZOOM_connection_create(0);
428
429     ZOOM_connection_connect(c, host, portnum);
430     return c;
431 }
432
433 static zoom_sru_mode get_sru_mode_from_string(const char *s)
434 {
435     if (!s || !*s)
436         return zoom_sru_soap;
437     if (!yaz_matchstr(s, "soap"))
438         return zoom_sru_soap;
439     else if (!yaz_matchstr(s, "get"))
440         return zoom_sru_get;
441     else if (!yaz_matchstr(s, "post"))
442         return zoom_sru_post;
443     return zoom_sru_error;
444 }
445
446 ZOOM_API(void)
447     ZOOM_connection_connect(ZOOM_connection c,
448                             const char *host, int portnum)
449 {
450     const char *val;
451     ZOOM_task task;
452
453     initlog();
454
455     yaz_log(c->log_api, "%p ZOOM_connection_connect host=%s portnum=%d",
456             c, host ? host : "null", portnum);
457
458     ZOOM_set_error(c, ZOOM_ERROR_NONE, 0);
459     ZOOM_connection_remove_tasks(c);
460
461     if (c->odr_print)
462     {
463         odr_setprint(c->odr_print, 0); /* prevent destroy from fclose'ing */
464         odr_destroy(c->odr_print);
465     }
466     if (ZOOM_options_get_bool(c->options, "apdulog", 0))
467     {
468         c->odr_print = odr_createmem(ODR_PRINT);
469         odr_setprint(c->odr_print, yaz_log_file());
470     }
471     else
472         c->odr_print = 0;
473
474     if (c->cs)
475     {
476         yaz_log(c->log_details, "%p ZOOM_connection_connect reconnect ok", c);
477         c->reconnect_ok = 1;
478         return;
479     }
480     yaz_log(c->log_details, "%p ZOOM_connection_connect connect", c);
481     xfree(c->proxy);
482     c->proxy = 0;
483     val = ZOOM_options_get(c->options, "proxy");
484     if (val && *val)
485     {
486         yaz_log(c->log_details, "%p ZOOM_connection_connect proxy=%s", c, val);
487         c->proxy = xstrdup(val);
488     }
489
490     xfree(c->charset);
491     c->charset = 0;
492     val = ZOOM_options_get(c->options, "charset");
493     if (val && *val)
494     {
495         yaz_log(c->log_details, "%p ZOOM_connection_connect charset=%s", c, val);
496         c->charset = xstrdup(val);
497     }
498
499     xfree(c->lang);
500     val = ZOOM_options_get(c->options, "lang");
501     if (val && *val)
502     {
503         yaz_log(c->log_details, "%p ZOOM_connection_connect lang=%s", c, val);
504         c->lang = xstrdup(val);
505     }
506     else
507         c->lang = 0;
508
509     if (host)
510     {
511         xfree(c->host_port);
512         if (portnum)
513         {
514             char hostn[128];
515             sprintf(hostn, "%.80s:%d", host, portnum);
516             c->host_port = xstrdup(hostn);
517         }
518         else
519             c->host_port = xstrdup(host);
520     }        
521
522     {
523         /*
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
528          * in a host string:
529          *          user=admin,password=secret,tcp:localhost:9999
530          */
531         char *remainder = c->host_port;
532         char *pcolon = strchr(remainder, ':');
533         char *pcomma;
534         char *pequals;
535         while ((pcomma = strchr(remainder, ',')) != 0 &&
536                (pcolon == 0 || pcomma < pcolon)) {
537             *pcomma = '\0';
538             if ((pequals = strchr(remainder, '=')) != 0) {
539                 *pequals = '\0';
540                 /*printf("# setting '%s'='%s'\n", remainder, pequals+1);*/
541                 ZOOM_connection_option_set(c, remainder, pequals+1);
542             }
543             remainder = pcomma+1;
544         }
545
546         if (remainder != c->host_port) {
547             xfree(c->host_port);
548             c->host_port = xstrdup(remainder);
549             /*printf("# reset hp='%s'\n", remainder);*/
550         }
551     }
552
553     val = ZOOM_options_get(c->options, "sru");
554     c->sru_mode = get_sru_mode_from_string(val);
555
556     xfree(c->sru_version);
557     val = ZOOM_options_get(c->options, "sru_version");
558     c->sru_version = xstrdup(val ? val : "1.2");
559
560     ZOOM_options_set(c->options, "host", c->host_port);
561
562     xfree(c->cookie_out);
563     c->cookie_out = 0;
564     val = ZOOM_options_get(c->options, "cookie");
565     if (val && *val)
566     { 
567         yaz_log(c->log_details, "%p ZOOM_connection_connect cookie=%s", c, val);
568         c->cookie_out = xstrdup(val);
569     }
570
571     xfree(c->client_IP);
572     c->client_IP = 0;
573     val = ZOOM_options_get(c->options, "clientIP");
574     if (val && *val)
575     {
576         yaz_log(c->log_details, "%p ZOOM_connection_connect clientIP=%s",
577                 c, val);
578         c->client_IP = xstrdup(val);
579     }
580
581     xfree(c->group);
582     c->group = 0;
583     val = ZOOM_options_get(c->options, "group");
584     if (val && *val)
585         c->group = xstrdup(val);
586
587     xfree(c->user);
588     c->user = 0;
589     val = ZOOM_options_get(c->options, "user");
590     if (val && *val)
591         c->user = xstrdup(val);
592
593     xfree(c->password);
594     c->password = 0;
595     val = ZOOM_options_get(c->options, "password");
596     if (!val)
597         val = ZOOM_options_get(c->options, "pass");
598
599     if (val && *val)
600         c->password = xstrdup(val);
601     
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);
606
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);
609  
610     task = ZOOM_connection_add_task(c, ZOOM_TASK_CONNECT);
611
612     if (!c->async)
613     {
614         while (ZOOM_event(1, &c))
615             ;
616     }
617 }
618
619 ZOOM_API(void) ZOOM_resultset_release(ZOOM_resultset r)
620 {
621 #if ZOOM_RESULT_LISTS
622 #else
623     if (r->connection)
624     {
625         /* remove ourselves from the resultsets in connection */
626         ZOOM_resultset *rp = &r->connection->resultsets;
627         while (1)
628         {
629             assert(*rp);   /* we must be in this list!! */
630             if (*rp == r)
631             {   /* OK, we're here - take us out of it */
632                 *rp = (*rp)->next;
633                 break;
634             }
635             rp = &(*rp)->next;
636         }
637         r->connection = 0;
638     }
639 #endif
640 }
641
642 ZOOM_API(void)
643     ZOOM_connection_destroy(ZOOM_connection c)
644 {
645 #if ZOOM_RESULT_LISTS
646     ZOOM_resultsets list;
647 #else
648     ZOOM_resultset r;
649 #endif
650     if (!c)
651         return;
652     yaz_log(c->log_api, "%p ZOOM_connection_destroy", c);
653     if (c->cs)
654         cs_close(c->cs);
655
656 #if ZOOM_RESULT_LISTS
657     // Remove the connection's usage of resultsets
658     list = c->resultsets;
659     while (list) {
660         ZOOM_resultsets removed = list;
661         ZOOM_resultset_destroy(list->resultset);
662         list = list->next;
663         xfree(removed);
664     }
665 #else
666     for (r = c->resultsets; r; r = r->next)
667         r->connection = 0;
668 #endif
669
670     xfree(c->buf_in);
671     xfree(c->addinfo);
672     xfree(c->diagset);
673     odr_destroy(c->odr_in);
674     odr_destroy(c->odr_out);
675     if (c->odr_print)
676     {
677         odr_setprint(c->odr_print, 0); /* prevent destroy from fclose'ing */
678         odr_destroy(c->odr_print);
679     }
680     ZOOM_options_destroy(c->options);
681     ZOOM_connection_remove_tasks(c);
682     ZOOM_connection_remove_events(c);
683     xfree(c->host_port);
684     xfree(c->path);
685     xfree(c->proxy);
686     xfree(c->charset);
687     xfree(c->lang);
688     xfree(c->cookie_out);
689     xfree(c->cookie_in);
690     xfree(c->client_IP);
691     xfree(c->user);
692     xfree(c->group);
693     xfree(c->password);
694     xfree(c->sru_version);
695     xfree(c);
696 }
697
698 void ZOOM_resultset_addref(ZOOM_resultset r)
699 {
700     if (r)
701     {
702         yaz_mutex_enter(r->mutex);
703         (r->refcount)++;
704         yaz_log(log_details0, "%p ZOOM_resultset_addref count=%d",
705                 r, r->refcount);
706         yaz_mutex_leave(r->mutex);
707     }
708 }
709
710 ZOOM_resultset ZOOM_resultset_create(void)
711 {
712     int i;
713     ZOOM_resultset r = (ZOOM_resultset) xmalloc(sizeof(*r));
714
715     initlog();
716
717     yaz_log(log_details0, "%p ZOOM_resultset_create", r);
718     r->refcount = 1;
719     r->size = 0;
720     r->odr = odr_createmem(ODR_ENCODE);
721     r->piggyback = 1;
722     r->setname = 0;
723     r->schema = 0;
724     r->step = 0;
725     for (i = 0; i<RECORD_HASH_SIZE; i++)
726         r->record_hash[i] = 0;
727     r->r_sort_spec = 0;
728     r->query = 0;
729     r->connection = 0;
730     r->databaseNames = 0;
731     r->num_databaseNames = 0;
732     r->facets = 0;
733     r->num_facets = 0;
734     r->facets_names = 0;
735     r->mutex = 0;
736     yaz_mutex_create(&r->mutex);
737 #if SHPTR
738     {
739         WRBUF w = wrbuf_alloc();
740         YAZ_SHPTR_INIT(r->record_wrbuf, w);
741     }
742 #endif
743     return r;
744 }
745
746 ZOOM_API(ZOOM_resultset)
747     ZOOM_connection_search_pqf(ZOOM_connection c, const char *q)
748 {
749     ZOOM_resultset r;
750     ZOOM_query s = ZOOM_query_create();
751
752     ZOOM_query_prefix(s, q);
753
754     r = ZOOM_connection_search(c, s);
755     ZOOM_query_destroy(s);
756     return r;
757 }
758
759 ZOOM_API(ZOOM_resultset)
760     ZOOM_connection_search(ZOOM_connection c, ZOOM_query q)
761 {
762     ZOOM_resultset r = ZOOM_resultset_create();
763     ZOOM_task task;
764     const char *cp;
765     int start, count;
766     const char *syntax, *elementSetName;
767 #if ZOOM_RESULT_LISTS
768     ZOOM_resultsets set;
769 #endif
770
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);
773     r->query = q;
774
775     r->options = ZOOM_options_create_with_parent(c->options);
776     
777     start = ZOOM_options_get_int(r->options, "start", 0);
778     count = ZOOM_options_get_int(r->options, "count", 0);
779     {
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);
784     }
785     r->piggyback = ZOOM_options_get_bool(r->options, "piggyback", 1);
786     cp = ZOOM_options_get(r->options, "setname");
787     if (cp)
788         r->setname = xstrdup(cp);
789     cp = ZOOM_options_get(r->options, "schema");
790     if (cp)
791         r->schema = xstrdup(cp);
792
793     r->databaseNames = ZOOM_connection_get_databases(c, c->options, &r->num_databaseNames,
794                                          r->odr);
795     
796     r->connection = c;
797
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);
802     set->resultset = r;
803     set->next = c->resultsets;
804     c->resultsets = set;
805 #else
806     r->next = c->resultsets;
807     c->resultsets = r;
808 #endif
809     if (c->host_port && c->proto == PROTO_HTTP)
810     {
811         if (!c->cs)
812         {
813             yaz_log(c->log_details, "ZOOM_connection_search: no comstack");
814             ZOOM_connection_add_task(c, ZOOM_TASK_CONNECT);
815         }
816         else
817         {
818             yaz_log(c->log_details, "ZOOM_connection_search: reconnect");
819             c->reconnect_ok = 1;
820         }
821     }
822
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;
828
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;
834    
835     ZOOM_resultset_addref(r);
836
837     ZOOM_query_addref(q);
838
839     if (!c->async)
840     {
841         while (ZOOM_event(1, &c))
842             ;
843     }
844     return r;
845 }
846
847 ZOOM_API(void)
848     ZOOM_resultset_sort(ZOOM_resultset r,
849                          const char *sort_type, const char *sort_spec)
850 {
851     (void) ZOOM_resultset_sort1(r, sort_type, sort_spec);
852 }
853
854 ZOOM_API(int)
855     ZOOM_resultset_sort1(ZOOM_resultset r,
856                          const char *sort_type, const char *sort_spec)
857 {
858     ZOOM_connection c = r->connection;
859     ZOOM_task task;
860     ZOOM_query newq;
861
862     newq = ZOOM_query_create();
863     if (ZOOM_query_sortby(newq, sort_spec) < 0)
864         return -1;
865
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);
868     if (!c)
869         return 0;
870
871     if (c->host_port && c->proto == PROTO_HTTP)
872     {
873         if (!c->cs)
874         {
875             yaz_log(c->log_details, "%p ZOOM_resultset_sort: no comstack", r);
876             ZOOM_connection_add_task(c, ZOOM_TASK_CONNECT);
877         }
878         else
879         {
880             yaz_log(c->log_details, "%p ZOOM_resultset_sort: prepare reconnect",
881                     r);
882             c->reconnect_ok = 1;
883         }
884     }
885     
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;
890
891     ZOOM_resultset_addref(r);  
892
893     if (!c->async)
894     {
895         while (ZOOM_event(1, &c))
896             ;
897     }
898
899     return 0;
900 }
901
902 ZOOM_API(void)
903     ZOOM_resultset_destroy(ZOOM_resultset r)
904 {
905     resultset_destroy(r);
906 }
907
908 static void resultset_destroy(ZOOM_resultset r)
909 {
910     if (!r)
911         return;
912     yaz_mutex_enter(r->mutex);
913     (r->refcount)--;
914     yaz_log(log_details0, "%p ZOOM_resultset_destroy r=%p count=%d",
915             r, r, r->refcount);
916     if (r->refcount == 0)
917     {
918         yaz_mutex_leave(r->mutex);
919
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);
925         odr_destroy(r->odr);
926         xfree(r->setname);
927         xfree(r->schema);
928         yaz_mutex_destroy(&r->mutex);
929 #if SHPTR
930         YAZ_SHPTR_DEC(r->record_wrbuf, wrbuf_destroy);
931 #endif
932         xfree(r);
933     }
934     else
935         yaz_mutex_leave(r->mutex);
936 }
937
938 ZOOM_API(size_t)
939     ZOOM_resultset_size(ZOOM_resultset r)
940 {
941     return r->size;
942 }
943
944 int ZOOM_test_reconnect(ZOOM_connection c)
945 {
946     ZOOM_Event event;
947
948     if (!c->reconnect_ok)
949         return 0;
950     ZOOM_connection_close(c);
951     c->reconnect_ok = 0;
952     c->tasks->running = 0;
953     ZOOM_connection_insert_task(c, ZOOM_TASK_CONNECT);
954
955     event = ZOOM_Event_create(ZOOM_EVENT_CONNECT);
956     ZOOM_connection_put_event(c, event);
957
958     return 1;
959 }
960
961 static void ZOOM_resultset_retrieve(ZOOM_resultset r,
962                                     int force_sync, int start, int count)
963 {
964     ZOOM_task task;
965     ZOOM_connection c;
966     const char *cp;
967     const char *syntax, *elementSetName;
968
969     if (!r)
970         return;
971     yaz_log(log_details0, "%p ZOOM_resultset_retrieve force_sync=%d start=%d"
972             " count=%d", r, force_sync, start, count);
973     c = r->connection;
974     if (!c)
975         return;
976
977     if (c->host_port && c->proto == PROTO_HTTP)
978     {
979         if (!c->cs)
980         {
981             yaz_log(log_details0, "%p ZOOM_resultset_retrieve: no comstack", r);
982             ZOOM_connection_add_task(c, ZOOM_TASK_CONNECT);
983         }
984         else
985         {
986             yaz_log(log_details0, "%p ZOOM_resultset_retrieve: prepare "
987                     "reconnect", r);
988             c->reconnect_ok = 1;
989         }
990     }
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;
995
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;
1001
1002     cp = ZOOM_options_get(r->options, "schema");
1003     if (cp)
1004     {
1005         if (!r->schema || strcmp(r->schema, cp))
1006         {
1007             xfree(r->schema);
1008             r->schema = xstrdup(cp);
1009         }
1010     }
1011
1012     ZOOM_resultset_addref(r);
1013
1014     if (!r->connection->async || force_sync)
1015         while (r->connection && ZOOM_event(1, &r->connection))
1016             ;
1017 }
1018
1019 ZOOM_API(void)
1020     ZOOM_resultset_records(ZOOM_resultset r, ZOOM_record *recs,
1021                            size_t start, size_t count)
1022 {
1023     int force_present = 0;
1024
1025     if (!r)
1026         return ;
1027     yaz_log(log_api0, "%p ZOOM_resultset_records r=%p start=%ld count=%ld",
1028             r, r, (long) start, (long) count);
1029     if (count && recs)
1030         force_present = 1;
1031     ZOOM_resultset_retrieve(r, force_present, start, count);
1032     if (force_present)
1033     {
1034         size_t i;
1035         for (i = 0; i< count; i++)
1036             recs[i] = ZOOM_resultset_record_immediate(r, i+start);
1037     }
1038 }
1039
1040 ZOOM_API(size_t)
1041     ZOOM_resultset_facets_size(ZOOM_resultset r) {
1042     return r->num_facets;
1043 }
1044
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;
1049     int index;
1050     for (index = 0; index < num; index++) {
1051         if (!strcmp(facets[index]->facet_name, name)) {
1052             return facets[index];
1053         }
1054     }
1055     return 0;
1056 }
1057
1058
1059 ZOOM_API(ZOOM_facet_field *)
1060     ZOOM_resultset_facets(ZOOM_resultset r)
1061 {
1062     return r->facets;
1063 }
1064
1065 ZOOM_API(const char**)
1066     ZOOM_resultset_facet_names(ZOOM_resultset r)
1067 {
1068     return (const char **) r->facets_names;
1069 }
1070
1071 ZOOM_API(const char*)
1072     ZOOM_facet_field_name(ZOOM_facet_field field)
1073 {
1074     return field->facet_name;
1075 }
1076
1077 ZOOM_API(size_t)
1078     ZOOM_facet_field_term_count(ZOOM_facet_field field)
1079 {
1080     return field->num_terms;
1081 }
1082
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;
1087 }
1088
1089
1090 static void get_cert(ZOOM_connection c)
1091 {
1092     char *cert_buf;
1093     int cert_len;
1094     
1095     if (cs_get_peer_certificate_x509(c->cs, &cert_buf, &cert_len))
1096     {
1097         ZOOM_connection_option_setl(c, "sslPeerCert",
1098                                     cert_buf, cert_len);
1099         xfree(cert_buf);
1100     }
1101 }
1102
1103 static zoom_ret do_connect_host(ZOOM_connection c,
1104                                 const char *effective_host,
1105                                 const char *logical_url);
1106
1107 static zoom_ret do_connect(ZOOM_connection c)
1108 {
1109     const char *effective_host;
1110
1111     if (c->proxy)
1112         effective_host = c->proxy;
1113     else
1114         effective_host = c->host_port;
1115     return do_connect_host(c, effective_host, c->host_port);
1116 }
1117
1118 static zoom_ret do_connect_host(ZOOM_connection c, const char *effective_host,
1119     const char *logical_url)
1120 {
1121     void *add;
1122
1123     yaz_log(c->log_details, "%p do_connect effective_host=%s", c, effective_host);
1124
1125     if (c->cs)
1126         cs_close(c->cs);
1127     c->cs = cs_create_host(effective_host, 0, &add);
1128
1129     if (c->cs && c->cs->protocol == PROTO_HTTP)
1130     {
1131 #if YAZ_HAVE_XML2
1132         if (logical_url)
1133         {
1134             const char *db = 0;
1135             
1136             c->proto = PROTO_HTTP;
1137             cs_get_host_args(logical_url, &db);
1138             xfree(c->path);
1139             
1140             c->path = xmalloc(strlen(db) * 3 + 2);
1141             yaz_encode_sru_dbpath_buf(c->path, db);
1142         }
1143 #else
1144         ZOOM_set_error(c, ZOOM_ERROR_UNSUPPORTED_PROTOCOL, "SRW");
1145         ZOOM_connection_close(c);
1146         return zoom_complete;
1147 #endif
1148     }
1149     if (c->cs)
1150     {
1151         int ret = cs_connect(c->cs, add);
1152         if (ret == 0)
1153         {
1154             ZOOM_Event event = ZOOM_Event_create(ZOOM_EVENT_CONNECT);
1155             ZOOM_connection_put_event(c, event);
1156             get_cert(c);
1157             if (c->proto == PROTO_Z3950)
1158                 ZOOM_connection_Z3950_send_init(c);
1159             else
1160             {
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);
1166             }
1167             c->state = STATE_ESTABLISHED;
1168             return zoom_pending;
1169         }
1170         else if (ret > 0)
1171         {
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;
1180         }
1181     }
1182     c->state = STATE_IDLE;
1183     ZOOM_set_error(c, ZOOM_ERROR_CONNECT, logical_url);
1184     return zoom_complete;
1185 }
1186
1187 /* returns 1 if PDU was sent OK (still pending )
1188    0 if PDU was not sent OK (nothing to wait for) 
1189 */
1190
1191 #if YAZ_HAVE_XML2
1192 static zoom_ret send_srw(ZOOM_connection c, Z_SRW_PDU *sr)
1193 {
1194     Z_GDU *gdu;
1195     ZOOM_Event event;
1196     const char *database =  ZOOM_options_get(c->options, "databaseName");
1197     char *fdatabase = 0;
1198     
1199     if (database)
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);
1203
1204     if (c->sru_mode == zoom_sru_get)
1205     {
1206         yaz_sru_get_encode(gdu->u.HTTP_Request, sr, c->odr_out, c->charset);
1207     }
1208     else if (c->sru_mode == zoom_sru_post)
1209     {
1210         yaz_sru_post_encode(gdu->u.HTTP_Request, sr, c->odr_out, c->charset);
1211     }
1212     else if (c->sru_mode == zoom_sru_soap)
1213     {
1214         yaz_sru_soap_encode(gdu->u.HTTP_Request, sr, c->odr_out, c->charset);
1215     }
1216     if (!z_GDU(c->odr_out, &gdu, 0, 0))
1217         return zoom_complete;
1218     if (c->odr_print)
1219         z_GDU(c->odr_print, &gdu, 0, 0);
1220     c->buf_out = odr_getbuf(c->odr_out, &c->len_out, 0);
1221         
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);
1226 }
1227 #endif
1228
1229 #if YAZ_HAVE_XML2
1230 static Z_SRW_PDU *ZOOM_srw_get_pdu(ZOOM_connection c, int type)
1231 {
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;
1235     return sr;
1236 }
1237 #endif
1238
1239 #if YAZ_HAVE_XML2
1240 static zoom_ret ZOOM_connection_srw_send_search(ZOOM_connection c)
1241 {
1242     int i;
1243     int *start, *count;
1244     ZOOM_resultset resultset = 0;
1245     Z_SRW_PDU *sr = 0;
1246     const char *option_val = 0;
1247     Z_Query *z_query;
1248
1249     if (c->error)                  /* don't continue on error */
1250         return zoom_complete;
1251     assert(c->tasks);
1252     switch(c->tasks->which)
1253     {
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;
1261         break;
1262     case ZOOM_TASK_RETRIEVE:
1263         resultset = c->tasks->u.retrieve.resultset;
1264
1265         start = &c->tasks->u.retrieve.start;
1266         count = &c->tasks->u.retrieve.count;
1267
1268         if (*start >= resultset->size)
1269             return zoom_complete;
1270         if (*start + *count > resultset->size)
1271             *count = resultset->size - *start;
1272
1273         for (i = 0; i < *count; i++)
1274         {
1275             ZOOM_record rec =
1276                 ZOOM_record_cache_lookup(resultset, i + *start,
1277                                          c->tasks->u.retrieve.syntax,
1278                                          c->tasks->u.retrieve.elementSetName);
1279             if (!rec)
1280                 break;
1281             else
1282             {
1283                 ZOOM_Event event = ZOOM_Event_create(ZOOM_EVENT_RECV_RECORD);
1284                 ZOOM_connection_put_event(c, event);
1285             }
1286         }
1287         *start += i;
1288         *count -= i;
1289
1290         if (*count == 0)
1291             return zoom_complete;
1292         break;
1293     default:
1294         return zoom_complete;
1295     }
1296     assert(resultset->query);
1297         
1298     sr = ZOOM_srw_get_pdu(c, Z_SRW_searchRetrieve_request);
1299     z_query = ZOOM_query_get_Z_Query(resultset->query);
1300
1301     if (z_query->which == Z_Query_type_104
1302         && z_query->u.type_104->which == Z_External_CQL)
1303     {
1304         sr->u.request->query_type = Z_SRW_query_type_cql;
1305         sr->u.request->query.cql = z_query->u.type_104->u.cql;
1306     }
1307     else if (z_query->which == Z_Query_type_1 && z_query->u.type_1)
1308     {
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);
1312     }
1313     else
1314     {
1315         ZOOM_set_error(c, ZOOM_ERROR_UNSUPPORTED_QUERY, 0);
1316         return zoom_complete;
1317     }
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;
1323     
1324     option_val = ZOOM_resultset_option_get(resultset, "recordPacking");
1325     if (option_val)
1326         sr->u.request->recordPacking = odr_strdup(c->odr_out, option_val);
1327
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);
1331 }
1332 #else
1333 static zoom_ret ZOOM_connection_srw_send_search(ZOOM_connection c)
1334 {
1335     return zoom_complete;
1336 }
1337 #endif
1338
1339 ZOOM_API(ZOOM_record)
1340     ZOOM_resultset_record_immediate(ZOOM_resultset s,size_t pos)
1341 {
1342     const char *syntax =
1343         ZOOM_options_get(s->options, "preferredRecordSyntax"); 
1344     const char *elementSetName =
1345         ZOOM_options_get(s->options, "elementSetName");
1346
1347     return ZOOM_record_cache_lookup(s, pos, syntax, elementSetName);
1348 }
1349
1350 ZOOM_API(ZOOM_record)
1351     ZOOM_resultset_record(ZOOM_resultset r, size_t pos)
1352 {
1353     ZOOM_record rec = ZOOM_resultset_record_immediate(r, pos);
1354
1355     if (!rec)
1356     {
1357         /*
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
1361          * behaviour.
1362          */
1363         int force_sync = 1;
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);
1367     }
1368     return rec;
1369 }
1370
1371 static yaz_iconv_t iconv_create_charset(const char *record_charset)
1372 {
1373     char to[40];
1374     char from[40];
1375     yaz_iconv_t cd = 0;
1376
1377     *from = '\0';
1378     strcpy(to, "UTF-8");
1379     if (record_charset && *record_charset)
1380     {
1381         /* Use "from,to" or just "from" */
1382         const char *cp = strchr(record_charset, ',');
1383         size_t clen = strlen(record_charset);
1384         if (cp && cp[1])
1385         {
1386             strncpy( to, cp+1, sizeof(to)-1);
1387             to[sizeof(to)-1] = '\0';
1388             clen = cp - record_charset;
1389         }
1390         if (clen > sizeof(from)-1)
1391             clen = sizeof(from)-1;
1392         
1393         if (clen)
1394             strncpy(from, record_charset, clen);
1395         from[clen] = '\0';
1396     }
1397     if (*from && *to)
1398         cd = yaz_iconv_open(to, from);
1399     return cd;
1400 }
1401
1402 static const char *return_marc_record(WRBUF wrbuf,
1403                                       int marc_type,
1404                                       int *len,
1405                                       const char *buf, int sz,
1406                                       const char *record_charset)
1407 {
1408     yaz_iconv_t cd = iconv_create_charset(record_charset);
1409     yaz_marc_t mt = yaz_marc_create();
1410     const char *ret_string = 0;
1411
1412     if (cd)
1413         yaz_marc_iconv(mt, cd);
1414     yaz_marc_xml(mt, marc_type);
1415     if (yaz_marc_decode_wrbuf(mt, buf, sz, wrbuf) > 0)
1416     {
1417         if (len)
1418             *len = wrbuf_len(wrbuf);
1419         ret_string = wrbuf_cstr(wrbuf);
1420     }
1421     yaz_marc_destroy(mt);
1422     if (cd)
1423         yaz_iconv_close(cd);
1424     return ret_string;
1425 }
1426
1427 static const char *return_opac_record(WRBUF wrbuf,
1428                                       int marc_type,
1429                                       int *len,
1430                                       Z_OPACRecord *opac_rec,
1431                                       const char *record_charset)
1432 {
1433     yaz_iconv_t cd = iconv_create_charset(record_charset);
1434     yaz_marc_t mt = yaz_marc_create();
1435
1436     if (cd)
1437         yaz_marc_iconv(mt, cd);
1438     yaz_marc_xml(mt, marc_type);
1439
1440     yaz_opac_decode_wrbuf(mt, opac_rec, wrbuf);
1441     yaz_marc_destroy(mt);
1442
1443     if (cd)
1444         yaz_iconv_close(cd);
1445     if (len)
1446         *len = wrbuf_len(wrbuf);
1447     return wrbuf_cstr(wrbuf);
1448 }
1449
1450 static const char *return_string_record(WRBUF wrbuf,
1451                                         int *len,
1452                                         const char *buf, int sz,
1453                                         const char *record_charset)
1454 {
1455     yaz_iconv_t cd = iconv_create_charset(record_charset);
1456
1457     if (cd)
1458     {
1459         wrbuf_iconv_write(wrbuf, cd, buf, sz);
1460         wrbuf_iconv_reset(wrbuf, cd);
1461
1462         buf = wrbuf_cstr(wrbuf);
1463         sz = wrbuf_len(wrbuf);
1464         yaz_iconv_close(cd);
1465     }
1466     if (len)
1467         *len = sz;
1468     return buf;
1469 }
1470
1471 static const char *return_record_wrbuf(WRBUF wrbuf, int *len,
1472                                        Z_NamePlusRecord *npr,
1473                                        int marctype, const char *charset)
1474 {
1475     Z_External *r = (Z_External *) npr->u.databaseRecord;
1476     const Odr_oid *oid = r->direct_reference;
1477
1478     wrbuf_rewind(wrbuf);
1479     /* render bibliographic record .. */
1480     if (r->which == Z_External_OPAC)
1481     {
1482         return return_opac_record(wrbuf, marctype, len,
1483                                   r->u.opac, charset);
1484     }
1485     if (r->which == Z_External_sutrs)
1486         return return_string_record(wrbuf, len,
1487                                     (char*) r->u.sutrs->buf,
1488                                     r->u.sutrs->len,
1489                                     charset);
1490     else if (r->which == Z_External_octet)
1491     {
1492         if (yaz_oid_is_iso2709(oid))
1493         {
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,
1498                 charset);
1499             if (ret_buf)
1500                 return ret_buf;
1501             /* bad ISO2709. Return fail unless raw (ISO2709) is wanted */
1502             if (marctype != YAZ_MARC_ISO2709)
1503                 return 0;
1504         }
1505         return return_string_record(wrbuf, len,
1506                                     (const char *) r->u.octet_aligned->buf,
1507                                     r->u.octet_aligned->len,
1508                                     charset);
1509     }
1510     else if (r->which == Z_External_grs1)
1511     {
1512         yaz_display_grs1(wrbuf, r->u.grs1, 0);
1513         return return_string_record(wrbuf, len,
1514                                     wrbuf_buf(wrbuf),
1515                                     wrbuf_len(wrbuf),
1516                                     charset);
1517     }
1518     return 0;
1519 }
1520     
1521 static const char *get_record_format(WRBUF wrbuf, int *len,
1522                                      Z_NamePlusRecord *npr,
1523                                      int marctype, const char *charset,
1524                                      const char *format)
1525 {
1526     const char *res = return_record_wrbuf(wrbuf, len, npr, marctype, charset);
1527 #if YAZ_HAVE_XML2
1528     if (*format == '1' && len)
1529     {
1530         /* try to XML format res */
1531         xmlDocPtr doc;
1532         xmlKeepBlanksDefault(0); /* get get xmlDocFormatMemory to work! */
1533         doc = xmlParseMemory(res, *len);
1534         if (doc)
1535         {
1536             xmlChar *xml_mem;
1537             int xml_size;
1538             xmlDocDumpFormatMemory(doc, &xml_mem, &xml_size, 1);
1539             wrbuf_rewind(wrbuf);
1540             wrbuf_write(wrbuf, (const char *) xml_mem, xml_size);
1541             xmlFree(xml_mem);
1542             xmlFreeDoc(doc);
1543             res = wrbuf_cstr(wrbuf);
1544             *len = wrbuf_len(wrbuf);
1545         } 
1546     }
1547 #endif
1548     return res;
1549 }
1550
1551
1552 const char *ZOOM_npr_format(Z_NamePlusRecord *npr, const char *schema,
1553                             WRBUF wrbuf,
1554                             const char *type_spec, int *len)
1555 {
1556     size_t i;
1557     char type[40];
1558     char charset[40];
1559     char format[3];
1560     const char *cp = type_spec;
1561
1562     for (i = 0; cp[i] && cp[i] != ';' && cp[i] != ' ' && i < sizeof(type)-1;
1563          i++)
1564         type[i] = cp[i];
1565     type[i] = '\0';
1566     charset[0] = '\0';
1567     format[0] = '\0';
1568     while (1)
1569     {
1570         while (cp[i] == ' ')
1571             i++;
1572         if (cp[i] != ';')
1573             break;
1574         i++;
1575         while (cp[i] == ' ')
1576             i++;
1577         if (!strncmp(cp + i, "charset=", 8))
1578         {
1579             size_t j = 0;
1580             i = i + 8; /* skip charset= */
1581             for (j = 0; cp[i] && cp[i] != ';' && cp[i] != ' '; i++)
1582             {
1583                 if (j < sizeof(charset)-1)
1584                     charset[j++] = cp[i];
1585             }
1586             charset[j] = '\0';
1587         }
1588         else if (!strncmp(cp + i, "format=", 7))
1589         {
1590             size_t j = 0; 
1591             i = i + 7;
1592             for (j = 0; cp[i] && cp[i] != ';' && cp[i] != ' '; i++)
1593             {
1594                 if (j < sizeof(format)-1)
1595                     format[j++] = cp[i];
1596             }
1597             format[j] = '\0';
1598         } 
1599     }
1600     if (!strcmp(type, "database"))
1601     {
1602         if (len)
1603             *len = (npr->databaseName ? strlen(npr->databaseName) : 0);
1604         return npr->databaseName;
1605     }
1606     else if (!strcmp(type, "schema"))
1607     {
1608         if (len)
1609             *len = schema ? strlen(schema) : 0;
1610         return schema;
1611     }
1612     else if (!strcmp(type, "syntax"))
1613     {
1614         const char *desc = 0;   
1615         if (npr->which == Z_NamePlusRecord_databaseRecord)
1616         {
1617             Z_External *r = (Z_External *) npr->u.databaseRecord;
1618             desc = yaz_oid_to_string(yaz_oid_std(), r->direct_reference, 0);
1619         }
1620         if (!desc)
1621             desc = "none";
1622         if (len)
1623             *len = strlen(desc);
1624         return desc;
1625     }
1626     if (npr->which != Z_NamePlusRecord_databaseRecord)
1627         return 0;
1628
1629     /* from now on - we have a database record .. */
1630     if (!strcmp(type, "render"))
1631     {
1632         return get_record_format(wrbuf, len, npr, YAZ_MARC_LINE, charset, format);
1633     }
1634     else if (!strcmp(type, "xml"))
1635     {
1636         return get_record_format(wrbuf, len, npr, YAZ_MARC_MARCXML, charset,
1637                                  format);
1638     }
1639     else if (!strcmp(type, "txml"))
1640     {
1641         return get_record_format(wrbuf, len, npr, YAZ_MARC_TURBOMARC, charset,
1642                                  format);
1643     }
1644     else if (!strcmp(type, "raw"))
1645     {
1646         return get_record_format(wrbuf, len, npr, YAZ_MARC_ISO2709, charset,
1647             format);
1648     }
1649     else if (!strcmp(type, "ext"))
1650     {
1651         if (len) *len = -1;
1652         return (const char *) npr->u.databaseRecord;
1653     }
1654     else if (!strcmp(type, "opac"))
1655     {
1656         if (npr->u.databaseRecord->which == Z_External_OPAC)
1657             return get_record_format(wrbuf, len, npr, YAZ_MARC_MARCXML, charset,
1658                                      format);
1659     }
1660     return 0;
1661 }
1662
1663 ZOOM_API(ZOOM_scanset)
1664     ZOOM_connection_scan(ZOOM_connection c, const char *start)
1665 {
1666     ZOOM_scanset s;
1667     ZOOM_query q = ZOOM_query_create();
1668
1669     ZOOM_query_prefix(q, start);
1670
1671     s = ZOOM_connection_scan1(c, q);
1672     ZOOM_query_destroy(q);
1673     return s;
1674
1675 }
1676
1677 ZOOM_API(ZOOM_scanset)
1678     ZOOM_connection_scan1(ZOOM_connection c, ZOOM_query q)
1679 {
1680     ZOOM_scanset scan = 0;
1681     Z_Query *z_query = ZOOM_query_get_Z_Query(q);
1682
1683     if (!z_query)
1684         return 0;
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);
1689     scan->refcount = 1;
1690     scan->scan_response = 0;
1691     scan->srw_scan_response = 0;
1692
1693     scan->query = q;
1694     ZOOM_query_addref(q);
1695     scan->databaseNames = ZOOM_connection_get_databases(c, c->options,
1696                                             &scan->num_databaseNames,
1697                                             scan->odr);
1698
1699     if (1)
1700     {
1701         ZOOM_task task = ZOOM_connection_add_task(c, ZOOM_TASK_SCAN);
1702         task->u.scan.scan = scan;
1703         
1704         (scan->refcount)++;
1705         if (!c->async)
1706         {
1707             while (ZOOM_event(1, &c))
1708                 ;
1709         }
1710     }
1711     return scan;
1712 }
1713
1714 ZOOM_API(void)
1715     ZOOM_scanset_destroy(ZOOM_scanset scan)
1716 {
1717     if (!scan)
1718         return;
1719     (scan->refcount)--;
1720     if (scan->refcount == 0)
1721     {
1722         ZOOM_query_destroy(scan->query);
1723
1724         odr_destroy(scan->odr);
1725         
1726         ZOOM_options_destroy(scan->options);
1727         xfree(scan);
1728     }
1729 }
1730
1731 static zoom_ret send_package(ZOOM_connection c)
1732 {
1733     ZOOM_Event event;
1734
1735     yaz_log(c->log_details, "%p send_package", c);
1736     if (!c->tasks)
1737         return zoom_complete;
1738     assert (c->tasks->which == ZOOM_TASK_PACKAGE);
1739     
1740     event = ZOOM_Event_create(ZOOM_EVENT_SEND_APDU);
1741     ZOOM_connection_put_event(c, event);
1742     
1743     c->buf_out = c->tasks->u.package->buf_out;
1744     c->len_out = c->tasks->u.package->len_out;
1745
1746     return ZOOM_send_buf(c);
1747 }
1748
1749 #if YAZ_HAVE_XML2
1750 static zoom_ret ZOOM_connection_srw_send_scan(ZOOM_connection c)
1751 {
1752     ZOOM_scanset scan;
1753     Z_SRW_PDU *sr = 0;
1754     const char *option_val = 0;
1755     Z_Query *z_query;
1756
1757     if (!c->tasks)
1758         return zoom_complete;
1759     assert (c->tasks->which == ZOOM_TASK_SCAN);
1760     scan = c->tasks->u.scan.scan;
1761         
1762     sr = ZOOM_srw_get_pdu(c, Z_SRW_scan_request);
1763
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)
1767     {
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);
1771     }
1772     else if (z_query->which == Z_Query_type_1
1773              || z_query->which == Z_Query_type_101)
1774     {
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);
1778     }
1779     else
1780     {
1781         ZOOM_set_error(c, ZOOM_ERROR_UNSUPPORTED_QUERY, 0);
1782         return zoom_complete;
1783     }
1784
1785     sr->u.scan_request->maximumTerms = odr_intdup(
1786         c->odr_out, ZOOM_options_get_int(scan->options, "number", 10));
1787     
1788     sr->u.scan_request->responsePosition = odr_intdup(
1789         c->odr_out, ZOOM_options_get_int(scan->options, "position", 1));
1790     
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);
1794 }
1795 #else
1796 static zoom_ret ZOOM_connection_srw_send_scan(ZOOM_connection c)
1797 {
1798     return zoom_complete;
1799 }
1800 #endif
1801
1802
1803 ZOOM_API(size_t)
1804     ZOOM_scanset_size(ZOOM_scanset scan)
1805 {
1806     if (!scan)
1807         return 0;
1808
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;
1813     return 0;
1814 }
1815
1816 static void ZOOM_scanset_term_x(ZOOM_scanset scan, size_t pos,
1817                                 size_t *occ,
1818                                 const char **value_term, size_t *value_len,
1819                                 const char **disp_term, size_t *disp_len)
1820 {
1821     size_t noent = ZOOM_scanset_size(scan);
1822     
1823     *value_term = 0;
1824     *value_len = 0;
1825
1826     *disp_term = 0;
1827     *disp_len = 0;
1828
1829     *occ = 0;
1830     if (pos >= noent)
1831         return;
1832     if (scan->scan_response)
1833     {
1834         Z_ScanResponse *res = scan->scan_response;
1835         if (res->entries->entries[pos]->which == Z_Entry_termInfo)
1836         {
1837             Z_TermInfo *t = res->entries->entries[pos]->u.termInfo;
1838             
1839             *value_term = (const char *) t->term->u.general->buf;
1840             *value_len = t->term->u.general->len;
1841             if (t->displayTerm)
1842             {
1843                 *disp_term = t->displayTerm;
1844                 *disp_len = strlen(*disp_term);
1845             }
1846             else if (t->term->which == Z_Term_general)
1847             {
1848                 *disp_term = (const char *) t->term->u.general->buf;
1849                 *disp_len = t->term->u.general->len;
1850             }
1851             *occ = t->globalOccurrences ? *t->globalOccurrences : 0;
1852         }
1853     }
1854     if (scan->srw_scan_response)
1855     {
1856         Z_SRW_scanResponse *res = scan->srw_scan_response;
1857         Z_SRW_scanTerm *t = res->terms + pos;
1858         if (t)
1859         {
1860             *value_term = t->value;
1861             *value_len = strlen(*value_term);
1862
1863             if (t->displayTerm)
1864                 *disp_term = t->displayTerm;
1865             else
1866                 *disp_term = t->value;
1867             *disp_len = strlen(*disp_term);
1868             *occ = t->numberOfRecords ? *t->numberOfRecords : 0;
1869         }
1870     }
1871 }
1872
1873 ZOOM_API(const char *)
1874     ZOOM_scanset_term(ZOOM_scanset scan, size_t pos,
1875                       size_t *occ, size_t *len)
1876 {
1877     const char *value_term = 0;
1878     size_t value_len = 0;
1879     const char *disp_term = 0;
1880     size_t disp_len = 0;
1881
1882     ZOOM_scanset_term_x(scan, pos, occ, &value_term, &value_len,
1883                         &disp_term, &disp_len);
1884     
1885     *len = value_len;
1886     return value_term;
1887 }
1888
1889 ZOOM_API(const char *)
1890     ZOOM_scanset_display_term(ZOOM_scanset scan, size_t pos,
1891                               size_t *occ, size_t *len)
1892 {
1893     const char *value_term = 0;
1894     size_t value_len = 0;
1895     const char *disp_term = 0;
1896     size_t disp_len = 0;
1897
1898     ZOOM_scanset_term_x(scan, pos, occ, &value_term, &value_len,
1899                         &disp_term, &disp_len);
1900     
1901     *len = disp_len;
1902     return disp_term;
1903 }
1904
1905 ZOOM_API(const char *)
1906     ZOOM_scanset_option_get(ZOOM_scanset scan, const char *key)
1907 {
1908     return ZOOM_options_get(scan->options, key);
1909 }
1910
1911 ZOOM_API(void)
1912     ZOOM_scanset_option_set(ZOOM_scanset scan, const char *key,
1913                             const char *val)
1914 {
1915     ZOOM_options_set(scan->options, key, val);
1916 }
1917
1918
1919 ZOOM_API(ZOOM_package)
1920     ZOOM_connection_package(ZOOM_connection c, ZOOM_options options)
1921 {
1922     ZOOM_package p = (ZOOM_package) xmalloc(sizeof(*p));
1923
1924     p->connection = c;
1925     p->odr_out = odr_createmem(ODR_ENCODE);
1926     p->options = ZOOM_options_create_with_parent2(options, c->options);
1927     p->refcount = 1;
1928     p->buf_out = 0;
1929     p->len_out = 0;
1930     return p;
1931 }
1932
1933 ZOOM_API(void)
1934     ZOOM_package_destroy(ZOOM_package p)
1935 {
1936     if (!p)
1937         return;
1938     (p->refcount)--;
1939     if (p->refcount == 0)
1940     {
1941         odr_destroy(p->odr_out);
1942         xfree(p->buf_out);
1943         
1944         ZOOM_options_destroy(p->options);
1945         xfree(p);
1946     }
1947 }
1948
1949 ZOOM_API(const char *)
1950     ZOOM_package_option_get(ZOOM_package p, const char *key)
1951 {
1952     return ZOOM_options_get(p->options, key);
1953 }
1954
1955 ZOOM_API(const char *)
1956     ZOOM_package_option_getl(ZOOM_package p, const char *key, int *lenp)
1957 {
1958     return ZOOM_options_getl(p->options, key, lenp);
1959 }
1960
1961 ZOOM_API(void)
1962     ZOOM_package_option_set(ZOOM_package p, const char *key,
1963                             const char *val)
1964 {
1965     ZOOM_options_set(p->options, key, val);
1966 }
1967
1968 ZOOM_API(void)
1969     ZOOM_package_option_setl(ZOOM_package p, const char *key,
1970                              const char *val, int len)
1971 {
1972     ZOOM_options_setl(p->options, key, val, len);
1973 }
1974
1975 ZOOM_API(int)
1976     ZOOM_connection_exec_task(ZOOM_connection c)
1977 {
1978     ZOOM_task task = c->tasks;
1979     zoom_ret ret = zoom_complete;
1980
1981     if (!task)
1982         return 0;
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)
1986     {
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);
1990         return 0;
1991     }
1992     if (task->running)
1993     {
1994         yaz_log(c->log_details, "%p ZOOM_connection_exec_task "
1995                 "task already running", c);
1996         return 0;
1997     }
1998     task->running = 1;
1999     ret = zoom_complete;
2000     if (c->cs || task->which == ZOOM_TASK_CONNECT)
2001     {
2002         switch (task->which)
2003         {
2004         case ZOOM_TASK_SEARCH:
2005             if (c->proto == PROTO_HTTP)
2006                 ret = ZOOM_connection_srw_send_search(c);
2007             else
2008                 ret = ZOOM_connection_Z3950_send_search(c);
2009             break;
2010         case ZOOM_TASK_RETRIEVE:
2011             if (c->proto == PROTO_HTTP)
2012                 ret = ZOOM_connection_srw_send_search(c);
2013             else
2014                 ret = send_Z3950_present(c);
2015             break;
2016         case ZOOM_TASK_CONNECT:
2017             ret = do_connect(c);
2018             break;
2019         case ZOOM_TASK_SCAN:
2020             if (c->proto == PROTO_HTTP)
2021                 ret = ZOOM_connection_srw_send_scan(c);
2022             else
2023                 ret = ZOOM_connection_Z3950_send_scan(c);
2024             break;
2025         case ZOOM_TASK_PACKAGE:
2026             ret = send_package(c);
2027             break;
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);
2032             break;
2033         }
2034     }
2035     else
2036     {
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);
2040     }
2041     if (ret == zoom_complete)
2042     {
2043         yaz_log(c->log_details, "%p ZOOM_connection_exec_task "
2044                 "task removed (complete)", c);
2045         ZOOM_connection_remove_task(c);
2046         return 0;
2047     }
2048     yaz_log(c->log_details, "%p ZOOM_connection_exec_task "
2049             "task pending", c);
2050     return 1;
2051 }
2052
2053 #if YAZ_HAVE_XML2
2054 static zoom_ret handle_srw_response(ZOOM_connection c,
2055                                     Z_SRW_searchRetrieveResponse *res)
2056 {
2057     ZOOM_resultset resultset = 0;
2058     int i;
2059     NMEM nmem;
2060     ZOOM_Event event;
2061     int *start, *count;
2062     const char *syntax, *elementSetName;
2063
2064     if (!c->tasks)
2065         return zoom_complete;
2066
2067     switch(c->tasks->which)
2068     {
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;        
2075
2076         if (!c->tasks->u.search.recv_search_fired)
2077         {
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;
2081         }
2082         break;
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;
2089         break;
2090     default:
2091         return zoom_complete;
2092     }
2093
2094     resultset->size = 0;
2095
2096     if (res->resultSetId)
2097         ZOOM_resultset_option_set(resultset, "resultSetId", res->resultSetId);
2098
2099     yaz_log(c->log_details, "%p handle_srw_response got SRW response OK", c);
2100
2101     if (res->num_diagnostics > 0)
2102     {
2103         set_SRU_error(c, &res->diagnostics[0]);
2104     }
2105     else
2106     {
2107         if (res->numberOfRecords)
2108             resultset->size = *res->numberOfRecords;
2109         for (i = 0; i<res->num_records; i++)
2110         {
2111             int pos;
2112             Z_SRW_record *sru_rec;
2113             Z_SRW_diagnostic *diag = 0;
2114             int num_diag;
2115             
2116             Z_NamePlusRecord *npr = (Z_NamePlusRecord *)
2117                 odr_malloc(c->odr_in, sizeof(Z_NamePlusRecord));
2118             
2119             if (res->records[i].recordPosition && 
2120                 *res->records[i].recordPosition > 0)
2121                 pos = *res->records[i].recordPosition - 1;
2122             else
2123                 pos = *start + i;
2124             
2125             sru_rec = &res->records[i];
2126             
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;
2135             
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;
2143             
2144             if (sru_rec->recordSchema 
2145                 && !strcmp(sru_rec->recordSchema,
2146                            "info:srw/schema/1/diagnostics-v1.1"))
2147             {
2148                 sru_decode_surrogate_diagnostics(sru_rec->recordData_buf,
2149                                                  sru_rec->recordData_len,
2150                                                  &diag, &num_diag,
2151                                                  resultset->odr);
2152             }
2153             ZOOM_record_cache_add(resultset, npr, pos, syntax, elementSetName,
2154                                   sru_rec->recordSchema, diag);
2155         }
2156         *count -= i;
2157         *start += i;
2158         if (*count + *start > resultset->size)
2159             *count = resultset->size - *start;
2160         if (*count < 0)
2161             *count = 0;
2162         
2163         nmem = odr_extract_mem(c->odr_in);
2164         nmem_transfer(odr_getmem(resultset->odr), nmem);
2165         nmem_destroy(nmem);
2166
2167         if (*count > 0)
2168             return ZOOM_connection_srw_send_search(c);
2169     }
2170     return zoom_complete;
2171 }
2172 #endif
2173
2174 #if YAZ_HAVE_XML2
2175 static void handle_srw_scan_response(ZOOM_connection c,
2176                                      Z_SRW_scanResponse *res)
2177 {
2178     NMEM nmem = odr_extract_mem(c->odr_in);
2179     ZOOM_scanset scan;
2180
2181     if (!c->tasks || c->tasks->which != ZOOM_TASK_SCAN)
2182         return;
2183     scan = c->tasks->u.scan.scan;
2184
2185     if (res->num_diagnostics > 0)
2186         set_SRU_error(c, &res->diagnostics[0]);
2187
2188     scan->scan_response = 0;
2189     scan->srw_scan_response = res;
2190     nmem_transfer(odr_getmem(scan->odr), nmem);
2191
2192     ZOOM_options_set_int(scan->options, "number", res->num_terms);
2193     nmem_destroy(nmem);
2194 }
2195 #endif
2196
2197 #if YAZ_HAVE_XML2
2198 static Z_GDU *get_HTTP_Request_url(ODR odr, const char *url)
2199 {
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;
2204     if (cp0)
2205         cp0 = cp0+3;
2206     else
2207         cp0 = host;
2208     
2209     cp1 = strchr(cp0, '/');
2210     if (!cp1)
2211         cp1 = cp0 + strlen(cp0);
2212     
2213     if (cp0 && cp1)
2214     {
2215         char *h = (char*) odr_malloc(odr, cp1 - cp0 + 1);
2216         memcpy (h, cp0, cp1 - cp0);
2217         h[cp1-cp0] = '\0';
2218         z_HTTP_header_add(odr, &p->u.HTTP_Request->headers, "Host", h);
2219     }
2220     p->u.HTTP_Request->path = odr_strdup(odr, *cp1 ? cp1 : "/");
2221     return p;
2222 }
2223
2224 static zoom_ret send_SRW_redirect(ZOOM_connection c, const char *uri,
2225                                   Z_HTTP_Response *cookie_hres)
2226 {
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;
2231
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",
2234                       "text/xml");
2235
2236     for (h = cookie_hres->headers; h; h = h->next)
2237     {
2238         if (!strcmp(h->name, "Set-Cookie"))
2239         {
2240             char *cp;
2241
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);
2250             }
2251         }
2252     }
2253
2254     if (combined_cookies_len)
2255     {
2256         z_HTTP_header_add(c->odr_out, &gdu->u.HTTP_Request->headers,
2257                           "Cookie", combined_cookies);
2258         xfree(combined_cookies);
2259     }
2260
2261     if (c->user && c->password)
2262     {
2263         z_HTTP_header_add_basic_auth(c->odr_out, &gdu->u.HTTP_Request->headers,
2264                                      c->user, c->password);
2265     }
2266     if (!z_GDU(c->odr_out, &gdu, 0, 0))
2267         return zoom_complete;
2268     if (c->odr_print)
2269         z_GDU(c->odr_print, &gdu, 0, 0);
2270     c->buf_out = odr_getbuf(c->odr_out, &c->len_out, 0);
2271
2272     odr_reset(c->odr_out);
2273     return ZOOM_send_buf(c);
2274 }
2275
2276 static void handle_http(ZOOM_connection c, Z_HTTP_Response *hres)
2277 {
2278     zoom_ret cret = zoom_complete;
2279     int ret = -1;
2280     const char *addinfo = 0;
2281     const char *connection_head = z_HTTP_header_lookup(hres->headers,
2282                                                        "Connection");
2283     const char *location;
2284
2285     ZOOM_connection_set_mask(c, 0);
2286     yaz_log(c->log_details, "%p handle_http", c);
2287     
2288     if ((hres->code == 301 || hres->code == 302) && c->sru_mode == zoom_sru_get
2289         && (location = z_HTTP_header_lookup(hres->headers, "Location")))
2290     {
2291         c->no_redirects++;
2292         if (c->no_redirects > 10)
2293         {
2294             set_HTTP_error(c, hres->code, 0, 0);
2295             c->no_redirects = 0;
2296             ZOOM_connection_close(c);
2297         }
2298         else
2299         {
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 */
2305             ret = 0;
2306             cret = zoom_pending;
2307         }
2308     }
2309     else 
2310     {   /* not redirect (normal response) */
2311         if (!yaz_srw_check_content_type(hres))
2312             addinfo = "content-type";
2313         else
2314         {
2315             Z_SOAP *soap_package = 0;
2316             ODR o = c->odr_in;
2317             Z_SOAP_Handler soap_handlers[2] = {
2318                 {YAZ_XMLNS_SRU_v1_1, 0, (Z_SOAP_fun) yaz_srw_codec},
2319                 {0, 0, 0}
2320             };
2321             ret = z_soap_codec(o, &soap_package,
2322                                &hres->content_buf, &hres->content_len,
2323                                soap_handlers);
2324             if (!ret && soap_package->which == Z_SOAP_generic &&
2325                 soap_package->u.generic->no == 0)
2326             {
2327                 Z_SRW_PDU *sr = (Z_SRW_PDU*) soap_package->u.generic->p;
2328                 
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);
2336                 else
2337                     ret = -1;
2338             }
2339             else if (!ret && (soap_package->which == Z_SOAP_fault
2340                               || soap_package->which == Z_SOAP_error))
2341             {
2342                 set_HTTP_error(c, hres->code,
2343                                soap_package->u.fault->fault_code,
2344                                soap_package->u.fault->fault_string);
2345             }
2346             else
2347                 ret = -1;
2348         }   
2349         if (ret == 0)
2350         {
2351             if (c->no_redirects) /* end of redirect. change hosts again */
2352                 ZOOM_connection_close(c);
2353         }
2354         c->no_redirects = 0;
2355     }
2356     if (ret)
2357     {
2358         if (hres->code != 200)
2359             set_HTTP_error(c, hres->code, 0, 0);
2360         else
2361             ZOOM_set_error(c, ZOOM_ERROR_DECODE, addinfo);
2362         ZOOM_connection_close(c);
2363     }
2364     if (cret == zoom_complete)
2365     {
2366         yaz_log(YLOG_LOG, "removing tasks in handle_http");
2367         ZOOM_connection_remove_task(c);
2368     }
2369     {
2370         int must_close = 0;
2371         if (!strcmp(hres->version, "1.0"))
2372         {
2373             /* HTTP 1.0: only if Keep-Alive we stay alive.. */
2374             if (!connection_head || strcmp(connection_head, "Keep-Alive"))
2375                 must_close = 1;
2376         }
2377         else
2378         {
2379             /* HTTP 1.1: only if no close we stay alive.. */
2380             if (connection_head && !strcmp(connection_head, "close"))
2381                 must_close = 1;
2382         }
2383         if (must_close)
2384         {
2385             ZOOM_connection_close(c);
2386             if (c->tasks)
2387             {
2388                 c->tasks->running = 0;
2389                 ZOOM_connection_insert_task(c, ZOOM_TASK_CONNECT);
2390                 c->reconnect_ok = 0;
2391             }
2392         }
2393     }
2394 }
2395 #endif
2396
2397 static int do_read(ZOOM_connection c)
2398 {
2399     int r, more;
2400     ZOOM_Event event;
2401     
2402     event = ZOOM_Event_create(ZOOM_EVENT_RECV_DATA);
2403     ZOOM_connection_put_event(c, event);
2404     
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);
2408     if (r == 1)
2409         return 0;
2410     if (r <= 0)
2411     {
2412         if (!ZOOM_test_reconnect(c))
2413         {
2414             ZOOM_set_error(c, ZOOM_ERROR_CONNECTION_LOST, c->host_port);
2415             ZOOM_connection_close(c);
2416         }
2417     }
2418     else
2419     {
2420         Z_GDU *gdu;
2421         ZOOM_Event event;
2422
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);
2427
2428         if (!z_GDU(c->odr_in, &gdu, 0, 0))
2429         {
2430             int x;
2431             int err = odr_geterrorx(c->odr_in, &x);
2432             char msg[100];
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);
2439             if (c->log_api)
2440             {
2441                 FILE *ber_file = yaz_log_file();
2442                 if (ber_file)
2443                     odr_dumpBER(ber_file, c->buf_in, r);
2444             }
2445             ZOOM_connection_close(c);
2446         }
2447         else
2448         {
2449             if (c->odr_print)
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)
2454             {
2455 #if YAZ_HAVE_XML2
2456                 handle_http(c, gdu->u.HTTP_Response);
2457 #else
2458                 ZOOM_set_error(c, ZOOM_ERROR_DECODE, 0);
2459                 ZOOM_connection_close(c);
2460 #endif
2461             }
2462         }
2463         c->reconnect_ok = 0;
2464     }
2465     return 1;
2466 }
2467
2468 static zoom_ret do_write_ex(ZOOM_connection c, char *buf_out, int len_out)
2469 {
2470     int r;
2471     ZOOM_Event event;
2472     
2473     event = ZOOM_Event_create(ZOOM_EVENT_SEND_DATA);
2474     ZOOM_connection_put_event(c, event);
2475
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)
2478     {
2479         yaz_log(c->log_details, "%p do_write_ex write failed", c);
2480         if (ZOOM_test_reconnect(c))
2481         {
2482             return zoom_pending;
2483         }
2484         if (c->state == STATE_CONNECTING)
2485             ZOOM_set_error(c, ZOOM_ERROR_CONNECT, c->host_port);
2486         else
2487             ZOOM_set_error(c, ZOOM_ERROR_CONNECTION_LOST, c->host_port);
2488         ZOOM_connection_close(c);
2489         return zoom_complete;
2490     }
2491     else if (r == 1)
2492     {    
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",
2500                 c, c->mask);
2501     }
2502     else
2503     {
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",
2506                 c, c->mask);
2507     }
2508     return zoom_pending;
2509 }
2510
2511 zoom_ret ZOOM_send_buf(ZOOM_connection c)
2512 {
2513     return do_write_ex(c, c->buf_out, c->len_out);
2514 }
2515
2516
2517 ZOOM_API(const char *)
2518     ZOOM_connection_option_get(ZOOM_connection c, const char *key)
2519 {
2520     return ZOOM_options_get(c->options, key);
2521 }
2522
2523 ZOOM_API(const char *)
2524     ZOOM_connection_option_getl(ZOOM_connection c, const char *key, int *lenp)
2525 {
2526     return ZOOM_options_getl(c->options, key, lenp);
2527 }
2528
2529 ZOOM_API(void)
2530     ZOOM_connection_option_set(ZOOM_connection c, const char *key,
2531                                const char *val)
2532 {
2533     ZOOM_options_set(c->options, key, val);
2534 }
2535
2536 ZOOM_API(void)
2537     ZOOM_connection_option_setl(ZOOM_connection c, const char *key,
2538                                 const char *val, int len)
2539 {
2540     ZOOM_options_setl(c->options, key, val, len);
2541 }
2542
2543 ZOOM_API(const char *)
2544     ZOOM_resultset_option_get(ZOOM_resultset r, const char *key)
2545 {
2546     return ZOOM_options_get(r->options, key);
2547 }
2548
2549 ZOOM_API(void)
2550     ZOOM_resultset_option_set(ZOOM_resultset r, const char *key,
2551                               const char *val)
2552 {
2553     ZOOM_options_set(r->options, key, val);
2554 }
2555
2556
2557 ZOOM_API(int)
2558     ZOOM_connection_errcode(ZOOM_connection c)
2559 {
2560     return ZOOM_connection_error(c, 0, 0);
2561 }
2562
2563 ZOOM_API(const char *)
2564     ZOOM_connection_errmsg(ZOOM_connection c)
2565 {
2566     const char *msg;
2567     ZOOM_connection_error(c, &msg, 0);
2568     return msg;
2569 }
2570
2571 ZOOM_API(const char *)
2572     ZOOM_connection_addinfo(ZOOM_connection c)
2573 {
2574     const char *addinfo;
2575     ZOOM_connection_error(c, 0, &addinfo);
2576     return addinfo;
2577 }
2578
2579 ZOOM_API(const char *)
2580     ZOOM_connection_diagset(ZOOM_connection c)
2581 {
2582     const char *diagset;
2583     ZOOM_connection_error_x(c, 0, 0, &diagset);
2584     return diagset;
2585 }
2586
2587 ZOOM_API(const char *)
2588     ZOOM_diag_str(int error)
2589 {
2590     switch (error)
2591     {
2592     case ZOOM_ERROR_NONE:
2593         return "No error";
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:
2609         return "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";
2624     default:
2625         return diagbib1_str(error);
2626     }
2627 }
2628
2629 ZOOM_API(int)
2630     ZOOM_connection_error_x(ZOOM_connection c, const char **cp,
2631                             const char **addinfo, const char **diagset)
2632 {
2633     int error = c->error;
2634     if (cp)
2635     {
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);
2644         else
2645             *cp = "Unknown error and diagnostic set";
2646     }
2647     if (addinfo)
2648         *addinfo = c->addinfo ? c->addinfo : "";
2649     if (diagset)
2650         *diagset = c->diagset ? c->diagset : "";
2651     return c->error;
2652 }
2653
2654 ZOOM_API(int)
2655     ZOOM_connection_error(ZOOM_connection c, const char **cp,
2656                           const char **addinfo)
2657 {
2658     return ZOOM_connection_error_x(c, cp, addinfo, 0);
2659 }
2660
2661 static void ZOOM_connection_do_io(ZOOM_connection c, int mask)
2662 {
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",
2666             c, mask, r);
2667     
2668     if (r == CS_NONE)
2669     {
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);
2674     }
2675     else if (r == CS_CONNECT)
2676     {
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);
2680         if (ret == 1)
2681         {
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);
2690         }
2691         else if (ret == 0)
2692         {
2693             event = ZOOM_Event_create(ZOOM_EVENT_CONNECT);
2694             ZOOM_connection_put_event(c, event);
2695             get_cert(c);
2696             if (c->proto == PROTO_Z3950)
2697                 ZOOM_connection_Z3950_send_init(c);
2698             else
2699             {
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);
2705             }
2706             c->state = STATE_ESTABLISHED;
2707         }
2708         else
2709         {
2710             ZOOM_set_error(c, ZOOM_ERROR_CONNECT, c->host_port);
2711             ZOOM_connection_close(c);
2712         }
2713     }
2714     else
2715     {
2716         if (mask & ZOOM_SELECT_EXCEPT)
2717         {
2718             if (!ZOOM_test_reconnect(c))
2719             {
2720                 ZOOM_set_error(c, ZOOM_ERROR_CONNECTION_LOST, c->host_port);
2721                 ZOOM_connection_close(c);
2722             }
2723             return;
2724         }
2725         if (mask & ZOOM_SELECT_READ)
2726             do_read(c);
2727         if (c->cs && (mask & ZOOM_SELECT_WRITE))
2728             ZOOM_send_buf(c);
2729     }
2730 }
2731
2732 ZOOM_API(int)
2733     ZOOM_connection_last_event(ZOOM_connection cs)
2734 {
2735     if (!cs)
2736         return ZOOM_EVENT_NONE;
2737     return cs->last_event;
2738 }
2739
2740
2741 ZOOM_API(int) ZOOM_connection_fire_event_timeout(ZOOM_connection c)
2742 {
2743     if (c->mask)
2744     {
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);
2750     }
2751     return 0;
2752 }
2753
2754 ZOOM_API(int)
2755     ZOOM_connection_process(ZOOM_connection c)
2756 {
2757     ZOOM_Event event;
2758     if (!c)
2759         return 0;
2760
2761     event = ZOOM_connection_get_event(c);
2762     if (event)
2763     {
2764         ZOOM_Event_destroy(event);
2765         return 1;
2766     }
2767     ZOOM_connection_exec_task(c);
2768     event = ZOOM_connection_get_event(c);
2769     if (event)
2770     {
2771         ZOOM_Event_destroy(event);
2772         return 1;
2773     }
2774     return 0;
2775 }
2776
2777 ZOOM_API(int)
2778     ZOOM_event_nonblock(int no, ZOOM_connection *cs)
2779 {
2780     int i;
2781
2782     yaz_log(log_details0, "ZOOM_process_event(no=%d,cs=%p)", no, cs);
2783     
2784     for (i = 0; i<no; i++)
2785     {
2786         ZOOM_connection c = cs[i];
2787
2788         if (c && ZOOM_connection_process(c))
2789             return i+1;
2790     }
2791     return 0;
2792 }
2793
2794 ZOOM_API(int) ZOOM_connection_fire_event_socket(ZOOM_connection c, int mask)
2795 {
2796     if (c->mask && mask)
2797         ZOOM_connection_do_io(c, mask);
2798     return 0;
2799 }
2800
2801 ZOOM_API(int) ZOOM_connection_get_socket(ZOOM_connection c)
2802 {
2803     if (c->cs)
2804         return cs_fileno(c->cs);
2805     return -1;
2806 }
2807
2808 ZOOM_API(int) ZOOM_connection_set_mask(ZOOM_connection c, int mask)
2809 {
2810     c->mask = mask;
2811     if (!c->cs)
2812         return -1; 
2813     return 0;
2814 }
2815
2816 ZOOM_API(int) ZOOM_connection_get_mask(ZOOM_connection c)
2817 {
2818     if (c->cs)
2819         return c->mask;
2820     return 0;
2821 }
2822
2823 ZOOM_API(int) ZOOM_connection_get_timeout(ZOOM_connection c)
2824 {
2825     return ZOOM_options_get_int(c->options, "timeout", 30);
2826 }
2827
2828 ZOOM_API(void) ZOOM_connection_close(ZOOM_connection c)
2829 {
2830     if (c->cs)
2831         cs_close(c->cs);
2832     c->cs = 0;
2833     ZOOM_connection_set_mask(c, 0);
2834     c->state = STATE_IDLE;
2835 }
2836
2837 /*
2838  * Local variables:
2839  * c-basic-offset: 4
2840  * c-file-style: "Stroustrup"
2841  * indent-tabs-mode: nil
2842  * End:
2843  * vim: shiftwidth=4 tabstop=8 expandtab
2844  */
2845