Generate built-in OIDs from oid.csv.
[yaz-moved-to-github.git] / src / zoom-c.c
1 /*
2  * Copyright (C) 1995-2007, Index Data ApS
3  * See the file LICENSE for details.
4  *
5  * $Id: zoom-c.c,v 1.125 2007-04-16 21:53:09 adam Exp $
6  */
7 /**
8  * \file zoom-c.c
9  * \brief Implements ZOOM C interface.
10  */
11
12 #include <assert.h>
13 #include <string.h>
14 #include <errno.h>
15 #include "zoom-p.h"
16
17 #include <yaz/yaz-util.h>
18 #include <yaz/xmalloc.h>
19 #include <yaz/otherinfo.h>
20 #include <yaz/log.h>
21 #include <yaz/pquery.h>
22 #include <yaz/marcdisp.h>
23 #include <yaz/diagbib1.h>
24 #include <yaz/charneg.h>
25 #include <yaz/ill.h>
26 #include <yaz/srw.h>
27 #include <yaz/cql.h>
28 #include <yaz/ccl.h>
29
30 static int log_api = 0;
31 static int log_details = 0;
32
33 typedef enum {
34     zoom_pending,
35     zoom_complete
36 } zoom_ret;
37
38 static void resultset_destroy(ZOOM_resultset r);
39 static zoom_ret ZOOM_connection_send_init(ZOOM_connection c);
40 static zoom_ret do_write_ex(ZOOM_connection c, char *buf_out, int len_out);
41 static char *cql2pqf(ZOOM_connection c, const char *cql);
42
43
44 /*
45  * This wrapper is just for logging failed lookups.  It would be nicer
46  * if it could cause failure when a lookup fails, but that's hard.
47  */
48 static int *zoom_yaz_str_to_z3950oid(ZOOM_connection c,
49                                      int oid_class, const char *str) {
50     int *res = yaz_string_to_oid_odr(yaz_oid_std(), oid_class, str,
51                                      c->odr_out);
52     if (res == 0)
53         yaz_log(YLOG_WARN, "%p OID lookup (%d, '%s') failed",
54                 c, (int) oid_class, str);
55     return res;
56 }
57
58
59 static void initlog(void)
60 {
61     static int log_level_initialized = 0;
62     if (!log_level_initialized)
63     {
64         log_api = yaz_log_module_level("zoom");
65         log_details = yaz_log_module_level("zoomdetails");
66         log_level_initialized = 1;
67     }
68 }
69
70 static ZOOM_Event ZOOM_Event_create(int kind)
71 {
72     ZOOM_Event event = (ZOOM_Event) xmalloc(sizeof(*event));
73     event->kind = kind;
74     event->next = 0;
75     event->prev = 0;
76     yaz_log(log_details, "ZOOM_Event_create(kind=%d)", kind);
77     return event;
78 }
79
80 static void ZOOM_Event_destroy(ZOOM_Event event)
81 {
82     xfree(event);
83 }
84
85 static void ZOOM_connection_put_event(ZOOM_connection c, ZOOM_Event event)
86 {
87     if (c->m_queue_back)
88     {
89         c->m_queue_back->prev = event;
90         assert(c->m_queue_front);
91     }
92     else
93     {
94         assert(!c->m_queue_front);
95         c->m_queue_front = event;
96     }
97     event->next = c->m_queue_back;
98     event->prev = 0;
99     c->m_queue_back = event;
100 }
101
102 static ZOOM_Event ZOOM_connection_get_event(ZOOM_connection c)
103 {
104     ZOOM_Event event = c->m_queue_front;
105     if (!event)
106     {
107         c->last_event = ZOOM_EVENT_NONE;
108         return 0;
109     }
110     assert(c->m_queue_back);
111     c->m_queue_front = event->prev;
112     if (c->m_queue_front)
113     {
114         assert(c->m_queue_back);
115         c->m_queue_front->next = 0;
116     }
117     else
118         c->m_queue_back = 0;
119     c->last_event = event->kind;
120     return event;
121 }
122
123 static void ZOOM_connection_remove_events(ZOOM_connection c)
124 {
125     ZOOM_Event event;
126     while ((event = ZOOM_connection_get_event(c)))
127         ZOOM_Event_destroy(event);
128 }
129
130 ZOOM_API(int) ZOOM_connection_peek_event(ZOOM_connection c)
131 {
132     ZOOM_Event event = c->m_queue_front;
133
134     return event ? event->kind : ZOOM_EVENT_NONE;
135 }
136
137 void ZOOM_connection_remove_tasks(ZOOM_connection c);
138
139 static void set_dset_error(ZOOM_connection c, int error,
140                            const char *dset,
141                            const char *addinfo, const char *addinfo2)
142 {
143     char *cp;
144
145     xfree(c->addinfo);
146     c->addinfo = 0;
147     c->error = error;
148     if (!c->diagset || strcmp(dset, c->diagset))
149     {
150         xfree(c->diagset);
151         c->diagset = xstrdup(dset);
152         /* remove integer part from SRW diagset .. */
153         if ((cp = strrchr(c->diagset, '/')))
154             *cp = '\0';
155     }
156     if (addinfo && addinfo2)
157     {
158         c->addinfo = (char*) xmalloc(strlen(addinfo) + strlen(addinfo2) + 2);
159         strcpy(c->addinfo, addinfo);
160         strcat(c->addinfo, addinfo2);
161     }
162     else if (addinfo)
163         c->addinfo = xstrdup(addinfo);
164     if (error != ZOOM_ERROR_NONE)
165     {
166         yaz_log(log_api, "%p set_dset_error %s %s:%d %s %s",
167                 c, c->host_port ? c->host_port : "<>", dset, error,
168                 addinfo ? addinfo : "",
169                 addinfo2 ? addinfo2 : "");
170         ZOOM_connection_remove_tasks(c);
171     }
172 }
173
174 #if YAZ_HAVE_XML2
175 static void set_HTTP_error(ZOOM_connection c, int error,
176                            const char *addinfo, const char *addinfo2)
177 {
178     set_dset_error(c, error, "HTTP", addinfo, addinfo2);
179 }
180 #endif
181
182
183 static void set_ZOOM_error(ZOOM_connection c, int error,
184                            const char *addinfo)
185 {
186     set_dset_error(c, error, "ZOOM", addinfo, 0);
187 }
188
189 static void clear_error(ZOOM_connection c)
190 {
191     /*
192      * If an error is tied to an operation then it's ok to clear: for
193      * example, a diagnostic returned from a search is cleared by a
194      * subsequent search.  However, problems such as Connection Lost
195      * or Init Refused are not cleared, because they are not
196      * recoverable: doing another search doesn't help.
197      */
198
199     ZOOM_connection_remove_events(c);
200     switch (c->error)
201     {
202     case ZOOM_ERROR_CONNECT:
203     case ZOOM_ERROR_MEMORY:
204     case ZOOM_ERROR_DECODE:
205     case ZOOM_ERROR_CONNECTION_LOST:
206     case ZOOM_ERROR_INIT:
207     case ZOOM_ERROR_INTERNAL:
208     case ZOOM_ERROR_UNSUPPORTED_PROTOCOL:
209         break;
210     default:
211         set_ZOOM_error(c, ZOOM_ERROR_NONE, 0);
212     }
213 }
214
215 void ZOOM_connection_show_task(ZOOM_task task)
216 {
217     switch(task->which)
218     {
219     case ZOOM_TASK_SEARCH:
220         yaz_log(YLOG_LOG, "search p=%p", task);
221         break;
222     case ZOOM_TASK_RETRIEVE:
223         yaz_log(YLOG_LOG, "retrieve p=%p", task);
224         break;
225     case ZOOM_TASK_CONNECT:
226         yaz_log(YLOG_LOG, "connect p=%p", task);
227         break;
228     case ZOOM_TASK_SCAN:
229         yaz_log(YLOG_LOG, "scant p=%p", task);
230         break;
231     }
232 }
233
234 void ZOOM_connection_show_tasks(ZOOM_connection c)
235 {
236     ZOOM_task task;
237     yaz_log(YLOG_LOG, "connection p=%p tasks", c);
238     for (task = c->tasks; task; task = task->next)
239         ZOOM_connection_show_task(task);
240 }
241
242 ZOOM_task ZOOM_connection_add_task(ZOOM_connection c, int which)
243 {
244     ZOOM_task *taskp = &c->tasks;
245     while (*taskp)
246         taskp = &(*taskp)->next;
247     *taskp = (ZOOM_task) xmalloc(sizeof(**taskp));
248     (*taskp)->running = 0;
249     (*taskp)->which = which;
250     (*taskp)->next = 0;
251     clear_error(c);
252     return *taskp;
253 }
254
255 ZOOM_API(int) ZOOM_connection_is_idle(ZOOM_connection c)
256 {
257     return c->tasks ? 0 : 1;
258 }
259
260 ZOOM_task ZOOM_connection_insert_task(ZOOM_connection c, int which)
261 {
262     ZOOM_task task = (ZOOM_task) xmalloc(sizeof(*task));
263
264     task->next = c->tasks;
265     c->tasks = task;
266
267     task->running = 0;
268     task->which = which;
269     clear_error(c);
270     return task;
271 }
272
273 void ZOOM_connection_remove_task(ZOOM_connection c)
274 {
275     ZOOM_task task = c->tasks;
276
277     if (task)
278     {
279         c->tasks = task->next;
280         switch (task->which)
281         {
282         case ZOOM_TASK_SEARCH:
283             resultset_destroy(task->u.search.resultset);
284             xfree(task->u.search.syntax);
285             xfree(task->u.search.elementSetName);
286             break;
287         case ZOOM_TASK_RETRIEVE:
288             resultset_destroy(task->u.retrieve.resultset);
289             xfree(task->u.retrieve.syntax);
290             xfree(task->u.retrieve.elementSetName);
291             break;
292         case ZOOM_TASK_CONNECT:
293             break;
294         case ZOOM_TASK_SCAN:
295             ZOOM_scanset_destroy(task->u.scan.scan);
296             break;
297         case ZOOM_TASK_PACKAGE:
298             ZOOM_package_destroy(task->u.package);
299             break;
300         case ZOOM_TASK_SORT:
301             resultset_destroy(task->u.sort.resultset);
302             ZOOM_query_destroy(task->u.sort.q);
303             break;
304         default:
305             assert(0);
306         }
307         xfree(task);
308
309         if (!c->tasks)
310         {
311             ZOOM_Event event = ZOOM_Event_create(ZOOM_EVENT_END);
312             ZOOM_connection_put_event(c, event);
313         }
314     }
315 }
316
317 static int ZOOM_connection_exec_task(ZOOM_connection c);
318
319 void ZOOM_connection_remove_tasks(ZOOM_connection c)
320 {
321     while (c->tasks)
322         ZOOM_connection_remove_task(c);
323 }
324
325 static ZOOM_record record_cache_lookup(ZOOM_resultset r, int pos,
326                                        const char *syntax,
327                                        const char *elementSetName);
328
329 ZOOM_API(ZOOM_connection)
330     ZOOM_connection_create(ZOOM_options options)
331 {
332     ZOOM_connection c = (ZOOM_connection) xmalloc(sizeof(*c));
333
334     initlog();
335
336     yaz_log(log_api, "%p ZOOM_connection_create", c);
337
338     c->proto = PROTO_Z3950;
339     c->cs = 0;
340     ZOOM_connection_set_mask(c, 0);
341     c->reconnect_ok = 0;
342     c->state = STATE_IDLE;
343     c->addinfo = 0;
344     c->diagset = 0;
345     set_ZOOM_error(c, ZOOM_ERROR_NONE, 0);
346     c->buf_in = 0;
347     c->len_in = 0;
348     c->buf_out = 0;
349     c->len_out = 0;
350     c->resultsets = 0;
351
352     c->options = ZOOM_options_create_with_parent(options);
353
354     c->host_port = 0;
355     c->path = 0;
356     c->proxy = 0;
357     
358     c->charset = c->lang = 0;
359
360     c->cookie_out = 0;
361     c->cookie_in = 0;
362     c->client_IP = 0;
363     c->tasks = 0;
364
365     c->odr_in = odr_createmem(ODR_DECODE);
366     c->odr_out = odr_createmem(ODR_ENCODE);
367
368     c->async = 0;
369     c->support_named_resultsets = 0;
370     c->last_event = ZOOM_EVENT_NONE;
371
372     c->m_queue_front = 0;
373     c->m_queue_back = 0;
374     return c;
375 }
376
377
378 /* set database names. Take local databases (if set); otherwise
379    take databases given in ZURL (if set); otherwise use Default */
380 static char **set_DatabaseNames(ZOOM_connection con, ZOOM_options options,
381                                 int *num, ODR odr)
382 {
383     char **databaseNames;
384     const char *cp = ZOOM_options_get(options, "databaseName");
385     
386     if ((!cp || !*cp) && con->host_port)
387     {
388         if (strncmp(con->host_port, "unix:", 5) == 0)
389             cp = strchr(con->host_port+5, ':');
390         else
391             cp = strchr(con->host_port, '/');
392         if (cp)
393             cp++;
394     }
395     if (!cp)
396         cp = "Default";
397     nmem_strsplit(odr_getmem(odr), "+", cp,  &databaseNames, num);
398     return databaseNames;
399 }
400
401 ZOOM_API(ZOOM_connection)
402     ZOOM_connection_new(const char *host, int portnum)
403 {
404     ZOOM_connection c = ZOOM_connection_create(0);
405
406     ZOOM_connection_connect(c, host, portnum);
407     return c;
408 }
409
410 static zoom_sru_mode get_sru_mode_from_string(const char *s)
411 {
412     if (!s || !*s)
413         return zoom_sru_soap;
414     if (!yaz_matchstr(s, "soap"))
415         return zoom_sru_soap;
416     else if (!yaz_matchstr(s, "get"))
417         return zoom_sru_get;
418     else if (!yaz_matchstr(s, "post"))
419         return zoom_sru_post;
420     return zoom_sru_error;
421 }
422
423 ZOOM_API(void)
424     ZOOM_connection_connect(ZOOM_connection c,
425                             const char *host, int portnum)
426 {
427     const char *val;
428     ZOOM_task task;
429
430     initlog();
431
432     yaz_log(log_api, "%p ZOOM_connection_connect host=%s portnum=%d",
433             c, host ? host : "null", portnum);
434
435     set_ZOOM_error(c, ZOOM_ERROR_NONE, 0);
436     ZOOM_connection_remove_tasks(c);
437
438     if (c->cs)
439     {
440         yaz_log(log_details, "%p ZOOM_connection_connect reconnect ok", c);
441         c->reconnect_ok = 1;
442         return;
443     }
444     yaz_log(log_details, "%p ZOOM_connection_connect connect", c);
445     xfree(c->proxy);
446     val = ZOOM_options_get(c->options, "proxy");
447     if (val && *val)
448     {
449         yaz_log(log_details, "%p ZOOM_connection_connect proxy=%s", c, val);
450         c->proxy = xstrdup(val);
451     }
452     else
453         c->proxy = 0;
454
455     xfree(c->charset);
456     val = ZOOM_options_get(c->options, "charset");
457     if (val && *val)
458     {
459         yaz_log(log_details, "%p ZOOM_connection_connect charset=%s", c, val);
460         c->charset = xstrdup(val);
461     }
462     else
463         c->charset = 0;
464
465     xfree(c->lang);
466     val = ZOOM_options_get(c->options, "lang");
467     if (val && *val)
468     {
469         yaz_log(log_details, "%p ZOOM_connection_connect lang=%s", c, val);
470         c->lang = xstrdup(val);
471     }
472     else
473         c->lang = 0;
474
475     if (host)
476     {
477         xfree(c->host_port);
478         if (portnum)
479         {
480             char hostn[128];
481             sprintf(hostn, "%.80s:%d", host, portnum);
482             c->host_port = xstrdup(hostn);
483         }
484         else
485             c->host_port = xstrdup(host);
486     }        
487
488     {
489         /*
490          * If the "<scheme>:" part of the host string is preceded by one
491          * or more comma-separated <name>=<value> pairs, these are taken
492          * to be options to be set on the connection object.  Among other
493          * applications, this facility can be used to embed authentication
494          * in a host string:
495          *          user=admin,password=secret,tcp:localhost:9999
496          */
497         char *remainder = c->host_port;
498         char *pcolon = strchr(remainder, ':');
499         char *pcomma;
500         char *pequals;
501         while ((pcomma = strchr(remainder, ',')) != 0 &&
502                (pcolon == 0 || pcomma < pcolon)) {
503             *pcomma = '\0';
504             if ((pequals = strchr(remainder, '=')) != 0) {
505                 *pequals = '\0';
506                 /*printf("# setting '%s'='%s'\n", remainder, pequals+1);*/
507                 ZOOM_connection_option_set(c, remainder, pequals+1);
508             }
509             remainder = pcomma+1;
510         }
511
512         if (remainder != c->host_port) {
513             xfree(c->host_port);
514             c->host_port = xstrdup(remainder);
515             /*printf("# reset hp='%s'\n", remainder);*/
516         }
517     }
518
519     val = ZOOM_options_get(c->options, "sru");
520     c->sru_mode = get_sru_mode_from_string(val);
521
522     ZOOM_options_set(c->options, "host", c->host_port);
523
524     val = ZOOM_options_get(c->options, "cookie");
525     if (val && *val)
526     { 
527         yaz_log(log_details, "%p ZOOM_connection_connect cookie=%s", c, val);
528         c->cookie_out = xstrdup(val);
529     }
530
531     val = ZOOM_options_get(c->options, "clientIP");
532     if (val && *val)
533     {
534         yaz_log(log_details, "%p ZOOM_connection_connect clientIP=%s",
535                 c, val);
536         c->client_IP = xstrdup(val);
537     }
538
539     c->async = ZOOM_options_get_bool(c->options, "async", 0);
540     yaz_log(log_details, "%p ZOOM_connection_connect async=%d", c, c->async);
541  
542     task = ZOOM_connection_add_task(c, ZOOM_TASK_CONNECT);
543
544     if (!c->async)
545     {
546         while (ZOOM_event(1, &c))
547             ;
548     }
549 }
550
551 ZOOM_API(ZOOM_query)
552     ZOOM_query_create(void)
553 {
554     ZOOM_query s = (ZOOM_query) xmalloc(sizeof(*s));
555
556     yaz_log(log_details, "%p ZOOM_query_create", s);
557     s->refcount = 1;
558     s->z_query = 0;
559     s->sort_spec = 0;
560     s->odr = odr_createmem(ODR_ENCODE);
561     s->query_string = 0;
562
563     return s;
564 }
565
566 ZOOM_API(void)
567     ZOOM_query_destroy(ZOOM_query s)
568 {
569     if (!s)
570         return;
571
572     (s->refcount)--;
573     yaz_log(log_details, "%p ZOOM_query_destroy count=%d", s, s->refcount);
574     if (s->refcount == 0)
575     {
576         odr_destroy(s->odr);
577         xfree(s);
578     }
579 }
580
581 ZOOM_API(int)
582     ZOOM_query_prefix(ZOOM_query s, const char *str)
583 {
584     s->query_string = odr_strdup(s->odr, str);
585     s->z_query = (Z_Query *) odr_malloc(s->odr, sizeof(*s->z_query));
586     s->z_query->which = Z_Query_type_1;
587     s->z_query->u.type_1 =  p_query_rpn(s->odr, str);
588     if (!s->z_query->u.type_1)
589     {
590         yaz_log(log_details, "%p ZOOM_query_prefix str=%s failed", s, str);
591         s->z_query = 0;
592         return -1;
593     }
594     yaz_log(log_details, "%p ZOOM_query_prefix str=%s", s, str);
595     return 0;
596 }
597
598 ZOOM_API(int)
599     ZOOM_query_cql(ZOOM_query s, const char *str)
600 {
601     Z_External *ext;
602
603     s->query_string = odr_strdup(s->odr, str);
604
605     ext = (Z_External *) odr_malloc(s->odr, sizeof(*ext));
606     ext->direct_reference = odr_getoidbystr(s->odr, "1.2.840.10003.16.2");
607     ext->indirect_reference = 0;
608     ext->descriptor = 0;
609     ext->which = Z_External_CQL;
610     ext->u.cql = s->query_string;
611     
612     s->z_query = (Z_Query *) odr_malloc(s->odr, sizeof(*s->z_query));
613     s->z_query->which = Z_Query_type_104;
614     s->z_query->u.type_104 =  ext;
615
616     yaz_log(log_details, "%p ZOOM_query_cql str=%s", s, str);
617
618     return 0;
619 }
620
621 /*
622  * Translate the CQL string client-side into RPN which is passed to
623  * the server.  This is useful for server's that don't themselves
624  * support CQL, for which ZOOM_query_cql() is useless.  `conn' is used
625  * only as a place to stash diagnostics if compilation fails; if this
626  * information is not needed, a null pointer may be used.
627  */
628 ZOOM_API(int)
629     ZOOM_query_cql2rpn(ZOOM_query s, const char *str, ZOOM_connection conn)
630 {
631     char *rpn;
632     int ret;
633     ZOOM_connection freeme = 0;
634
635     yaz_log(log_details, "%p ZOOM_query_cql2rpn str=%s conn=%p", s, str, conn);
636     if (conn == 0)
637         conn = freeme = ZOOM_connection_create(0);
638
639     rpn = cql2pqf(conn, str);
640     if (freeme != 0)
641         ZOOM_connection_destroy(freeme);
642     if (rpn == 0)
643         return -1;
644
645     ret = ZOOM_query_prefix(s, rpn);
646     xfree(rpn);
647     return ret;
648 }
649
650 /*
651  * Analogous in every way to ZOOM_query_cql2rpn(), except that there
652  * is no analogous ZOOM_query_ccl() that just sends uninterpreted CCL
653  * to the server, as the YAZ GFS doesn't know how to handle this.
654  */
655 ZOOM_API(int)
656     ZOOM_query_ccl2rpn(ZOOM_query s, const char *str, const char *config,
657                        int *ccl_error, const char **error_string,
658                        int *error_pos)
659 {
660     int ret;
661     struct ccl_rpn_node *rpn;
662     CCL_bibset bibset = ccl_qual_mk();
663
664     if (config)
665         ccl_qual_buf(bibset, config);
666
667     rpn = ccl_find_str(bibset, str, ccl_error, error_pos);
668     if (!rpn)
669     {
670         *error_string = ccl_err_msg(*ccl_error);
671         ret = -1;
672     }
673     else
674     {
675         WRBUF wr = wrbuf_alloc();
676         ccl_pquery(wr, rpn);
677         ccl_rpn_delete(rpn);
678         ret = ZOOM_query_prefix(s, wrbuf_cstr(wr));
679         wrbuf_destroy(wr);
680     }
681     ccl_qual_rm(&bibset);
682     return ret;
683 }
684
685 ZOOM_API(int)
686     ZOOM_query_sortby(ZOOM_query s, const char *criteria)
687 {
688     s->sort_spec = yaz_sort_spec(s->odr, criteria);
689     if (!s->sort_spec)
690     {
691         yaz_log(log_details, "%p ZOOM_query_sortby criteria=%s failed",
692                 s, criteria);
693         return -1;
694     }
695     yaz_log(log_details, "%p ZOOM_query_sortby criteria=%s", s, criteria);
696     return 0;
697 }
698
699 static zoom_ret do_write(ZOOM_connection c);
700
701 ZOOM_API(void)
702     ZOOM_connection_destroy(ZOOM_connection c)
703 {
704     ZOOM_resultset r;
705     if (!c)
706         return;
707     yaz_log(log_api, "%p ZOOM_connection_destroy", c);
708     if (c->cs)
709         cs_close(c->cs);
710     for (r = c->resultsets; r; r = r->next)
711         r->connection = 0;
712
713     xfree(c->buf_in);
714     xfree(c->addinfo);
715     xfree(c->diagset);
716     odr_destroy(c->odr_in);
717     odr_destroy(c->odr_out);
718     ZOOM_options_destroy(c->options);
719     ZOOM_connection_remove_tasks(c);
720     ZOOM_connection_remove_events(c);
721     xfree(c->host_port);
722     xfree(c->path);
723     xfree(c->proxy);
724     xfree(c->charset);
725     xfree(c->lang);
726     xfree(c->cookie_out);
727     xfree(c->cookie_in);
728     xfree(c->client_IP);
729     xfree(c);
730 }
731
732 void ZOOM_resultset_addref(ZOOM_resultset r)
733 {
734     if (r)
735     {
736         (r->refcount)++;
737         yaz_log(log_details, "%p ZOOM_resultset_addref count=%d",
738                 r, r->refcount);
739     }
740 }
741
742 ZOOM_resultset ZOOM_resultset_create(void)
743 {
744     int i;
745     ZOOM_resultset r = (ZOOM_resultset) xmalloc(sizeof(*r));
746
747     initlog();
748
749     yaz_log(log_details, "%p ZOOM_resultset_create", r);
750     r->refcount = 1;
751     r->size = 0;
752     r->odr = odr_createmem(ODR_ENCODE);
753     r->piggyback = 1;
754     r->setname = 0;
755     r->schema = 0;
756     r->step = 0;
757     for (i = 0; i<RECORD_HASH_SIZE; i++)
758         r->record_hash[i] = 0;
759     r->r_sort_spec = 0;
760     r->query = 0;
761     r->connection = 0;
762     r->next = 0;
763     r->databaseNames = 0;
764     r->num_databaseNames = 0;
765     return r;
766 }
767
768 ZOOM_API(ZOOM_resultset)
769     ZOOM_connection_search_pqf(ZOOM_connection c, const char *q)
770 {
771     ZOOM_resultset r;
772     ZOOM_query s = ZOOM_query_create();
773
774     ZOOM_query_prefix(s, q);
775
776     r = ZOOM_connection_search(c, s);
777     ZOOM_query_destroy(s);
778     return r;
779 }
780
781 ZOOM_API(ZOOM_resultset)
782     ZOOM_connection_search(ZOOM_connection c, ZOOM_query q)
783 {
784     ZOOM_resultset r = ZOOM_resultset_create();
785     ZOOM_task task;
786     const char *cp;
787     int start, count;
788     const char *syntax, *elementSetName;
789
790     yaz_log(log_api, "%p ZOOM_connection_search set %p query %p", c, r, q);
791     r->r_sort_spec = q->sort_spec;
792     r->query = q;
793
794     r->options = ZOOM_options_create_with_parent(c->options);
795
796     start = ZOOM_options_get_int(r->options, "start", 0);
797     count = ZOOM_options_get_int(r->options, "count", 0);
798     {
799         /* If "presentChunk" is defined use that; otherwise "step" */
800         const char *cp = ZOOM_options_get(r->options, "presentChunk");
801         r->step = ZOOM_options_get_int(r->options,
802                                        (cp != 0 ? "presentChunk": "step"), 0);
803     }
804     r->piggyback = ZOOM_options_get_bool(r->options, "piggyback", 1);
805     cp = ZOOM_options_get(r->options, "setname");
806     if (cp)
807         r->setname = xstrdup(cp);
808     cp = ZOOM_options_get(r->options, "schema");
809     if (cp)
810         r->schema = xstrdup(cp);
811
812     r->databaseNames = set_DatabaseNames(c, c->options, &r->num_databaseNames,
813                                          r->odr);
814     
815     r->connection = c;
816
817     r->next = c->resultsets;
818     c->resultsets = r;
819
820     if (c->host_port && c->proto == PROTO_HTTP)
821     {
822         if (!c->cs)
823         {
824             yaz_log(log_details, "ZOOM_connection_search: no comstack");
825             ZOOM_connection_add_task(c, ZOOM_TASK_CONNECT);
826         }
827         else
828         {
829             yaz_log(log_details, "ZOOM_connection_search: reconnect");
830             c->reconnect_ok = 1;
831         }
832     }
833
834     task = ZOOM_connection_add_task(c, ZOOM_TASK_SEARCH);
835     task->u.search.resultset = r;
836     task->u.search.start = start;
837     task->u.search.count = count;
838
839     syntax = ZOOM_options_get(r->options, "preferredRecordSyntax"); 
840     task->u.search.syntax = syntax ? xstrdup(syntax) : 0;
841     elementSetName = ZOOM_options_get(r->options, "elementSetName");
842     task->u.search.elementSetName = elementSetName 
843         ? xstrdup(elementSetName) : 0;
844    
845     ZOOM_resultset_addref(r);
846
847     (q->refcount)++;
848
849     if (!c->async)
850     {
851         while (ZOOM_event(1, &c))
852             ;
853     }
854     return r;
855 }
856
857 ZOOM_API(void)
858     ZOOM_resultset_sort(ZOOM_resultset r,
859                          const char *sort_type, const char *sort_spec)
860 {
861     (void) ZOOM_resultset_sort(r, sort_type, sort_spec);
862 }
863
864 ZOOM_API(int)
865     ZOOM_resultset_sort1(ZOOM_resultset r,
866                          const char *sort_type, const char *sort_spec)
867 {
868     ZOOM_connection c = r->connection;
869     ZOOM_task task;
870     ZOOM_query newq;
871
872     newq = ZOOM_query_create();
873     if (ZOOM_query_sortby(newq, sort_spec) < 0)
874         return -1;
875
876     yaz_log(log_api, "%p ZOOM_resultset_sort r=%p sort_type=%s sort_spec=%s",
877             r, r, sort_type, sort_spec);
878     if (!c)
879         return 0;
880
881     if (c->host_port && c->proto == PROTO_HTTP)
882     {
883         if (!c->cs)
884         {
885             yaz_log(log_details, "%p ZOOM_resultset_sort: no comstack", r);
886             ZOOM_connection_add_task(c, ZOOM_TASK_CONNECT);
887         }
888         else
889         {
890             yaz_log(log_details, "%p ZOOM_resultset_sort: prepare reconnect",
891                     r);
892             c->reconnect_ok = 1;
893         }
894     }
895     
896     ZOOM_resultset_cache_reset(r);
897     task = ZOOM_connection_add_task(c, ZOOM_TASK_SORT);
898     task->u.sort.resultset = r;
899     task->u.sort.q = newq;
900
901     ZOOM_resultset_addref(r);  
902
903     if (!c->async)
904     {
905         while (ZOOM_event(1, &c))
906             ;
907     }
908
909     return 0;
910 }
911
912 ZOOM_API(void)
913     ZOOM_resultset_cache_reset(ZOOM_resultset r)
914 {
915     int i;
916     for (i = 0; i<RECORD_HASH_SIZE; i++)
917     {
918         ZOOM_record_cache rc;
919         for (rc = r->record_hash[i]; rc; rc = rc->next)
920         {
921             if (rc->rec.wrbuf_marc)
922                 wrbuf_destroy(rc->rec.wrbuf_marc);
923             if (rc->rec.wrbuf_iconv)
924                 wrbuf_destroy(rc->rec.wrbuf_iconv);
925             if (rc->rec.wrbuf_opac)
926                 wrbuf_destroy(rc->rec.wrbuf_opac);
927         }
928         r->record_hash[i] = 0;
929     }
930 }
931
932 ZOOM_API(void)
933     ZOOM_resultset_destroy(ZOOM_resultset r)
934 {
935     resultset_destroy(r);
936 }
937
938 static void resultset_destroy(ZOOM_resultset r)
939 {
940     if (!r)
941         return;
942     (r->refcount)--;
943     yaz_log(log_details, "%p ZOOM_resultset_destroy r=%p count=%d",
944             r, r, r->refcount);
945     if (r->refcount == 0)
946     {
947         ZOOM_resultset_cache_reset(r);
948
949         if (r->connection)
950         {
951             /* remove ourselves from the resultsets in connection */
952             ZOOM_resultset *rp = &r->connection->resultsets;
953             while (1)
954             {
955                 assert(*rp);   /* we must be in this list!! */
956                 if (*rp == r)
957                 {   /* OK, we're here - take us out of it */
958                     *rp = (*rp)->next;
959                     break;
960                 }
961                 rp = &(*rp)->next;
962             }
963         }
964         ZOOM_query_destroy(r->query);
965         ZOOM_options_destroy(r->options);
966         odr_destroy(r->odr);
967         xfree(r->setname);
968         xfree(r->schema);
969         xfree(r);
970     }
971 }
972
973 ZOOM_API(size_t)
974     ZOOM_resultset_size(ZOOM_resultset r)
975 {
976     yaz_log(log_details, "ZOOM_resultset_size r=%p count=%d",
977             r, r->size);
978     return r->size;
979 }
980
981 static void do_close(ZOOM_connection c)
982 {
983     if (c->cs)
984         cs_close(c->cs);
985     c->cs = 0;
986     ZOOM_connection_set_mask(c, 0);
987     c->state = STATE_IDLE;
988 }
989
990 static int ZOOM_test_reconnect(ZOOM_connection c)
991 {
992     ZOOM_Event event;
993
994     if (!c->reconnect_ok)
995         return 0;
996     do_close(c);
997     c->reconnect_ok = 0;
998     c->tasks->running = 0;
999     ZOOM_connection_insert_task(c, ZOOM_TASK_CONNECT);
1000
1001     event = ZOOM_Event_create(ZOOM_EVENT_CONNECT);
1002     ZOOM_connection_put_event(c, event);
1003
1004     return 1;
1005 }
1006
1007 static void ZOOM_resultset_retrieve(ZOOM_resultset r,
1008                                     int force_sync, int start, int count)
1009 {
1010     ZOOM_task task;
1011     ZOOM_connection c;
1012     const char *cp;
1013     const char *syntax, *elementSetName;
1014
1015     if (!r)
1016         return;
1017     yaz_log(log_details, "%p ZOOM_resultset_retrieve force_sync=%d start=%d"
1018             " count=%d", r, force_sync, start, count);
1019     c = r->connection;
1020     if (!c)
1021         return;
1022
1023     if (c->host_port && c->proto == PROTO_HTTP)
1024     {
1025         if (!c->cs)
1026         {
1027             yaz_log(log_details, "%p ZOOM_resultset_retrieve: no comstack", r);
1028             ZOOM_connection_add_task(c, ZOOM_TASK_CONNECT);
1029         }
1030         else
1031         {
1032             yaz_log(log_details, "%p ZOOM_resultset_retrieve: prepare "
1033                     "reconnect", r);
1034             c->reconnect_ok = 1;
1035         }
1036     }
1037     task = ZOOM_connection_add_task(c, ZOOM_TASK_RETRIEVE);
1038     task->u.retrieve.resultset = r;
1039     task->u.retrieve.start = start;
1040     task->u.retrieve.count = count;
1041
1042     syntax = ZOOM_options_get(r->options, "preferredRecordSyntax"); 
1043     task->u.retrieve.syntax = syntax ? xstrdup(syntax) : 0;
1044     elementSetName = ZOOM_options_get(r->options, "elementSetName");
1045     task->u.retrieve.elementSetName = elementSetName 
1046         ? xstrdup(elementSetName) : 0;
1047
1048     cp = ZOOM_options_get(r->options, "schema");
1049     if (cp)
1050     {
1051         if (!r->schema || strcmp(r->schema, cp))
1052         {
1053             xfree(r->schema);
1054             r->schema = xstrdup(cp);
1055         }
1056     }
1057
1058     ZOOM_resultset_addref(r);
1059
1060     if (!r->connection->async || force_sync)
1061         while (r->connection && ZOOM_event(1, &r->connection))
1062             ;
1063 }
1064
1065 ZOOM_API(void)
1066     ZOOM_resultset_records(ZOOM_resultset r, ZOOM_record *recs,
1067                            size_t start, size_t count)
1068 {
1069     int force_present = 0;
1070
1071     if (!r)
1072         return ;
1073     yaz_log(log_api, "%p ZOOM_resultset_records r=%p start=%ld count=%ld",
1074             r, r, (long) start, (long) count);
1075     if (count && recs)
1076         force_present = 1;
1077     ZOOM_resultset_retrieve(r, force_present, start, count);
1078     if (force_present)
1079     {
1080         size_t i;
1081         for (i = 0; i< count; i++)
1082             recs[i] = ZOOM_resultset_record_immediate(r, i+start);
1083     }
1084 }
1085
1086 static void get_cert(ZOOM_connection c)
1087 {
1088     char *cert_buf;
1089     int cert_len;
1090     
1091     if (cs_get_peer_certificate_x509(c->cs, &cert_buf, &cert_len))
1092     {
1093         ZOOM_connection_option_setl(c, "sslPeerCert",
1094                                     cert_buf, cert_len);
1095         xfree(cert_buf);
1096     }
1097 }
1098
1099 static zoom_ret do_connect(ZOOM_connection c)
1100 {
1101     void *add;
1102     const char *effective_host;
1103
1104     if (c->proxy)
1105         effective_host = c->proxy;
1106     else
1107         effective_host = c->host_port;
1108
1109     yaz_log(log_details, "%p do_connect effective_host=%s", c, effective_host);
1110
1111     if (c->cs)
1112         cs_close(c->cs);
1113     c->cs = cs_create_host(effective_host, 0, &add);
1114
1115     if (c->cs && c->cs->protocol == PROTO_HTTP)
1116     {
1117 #if YAZ_HAVE_XML2
1118         const char *path = 0;
1119
1120         c->proto = PROTO_HTTP;
1121         cs_get_host_args(c->host_port, &path);
1122         xfree(c->path);
1123         c->path = (char*) xmalloc(strlen(path)+2);
1124         c->path[0] = '/';
1125         strcpy(c->path+1, path);
1126 #else
1127         set_ZOOM_error(c, ZOOM_ERROR_UNSUPPORTED_PROTOCOL, "SRW");
1128         do_close(c);
1129         return zoom_complete;
1130 #endif
1131     }
1132     if (c->cs)
1133     {
1134         int ret = cs_connect(c->cs, add);
1135         if (ret == 0)
1136         {
1137             ZOOM_Event event = ZOOM_Event_create(ZOOM_EVENT_CONNECT);
1138             ZOOM_connection_put_event(c, event);
1139             get_cert(c);
1140             if (c->proto == PROTO_Z3950)
1141                 ZOOM_connection_send_init(c);
1142             else
1143             {
1144                 /* no init request for SRW .. */
1145                 assert(c->tasks->which == ZOOM_TASK_CONNECT);
1146                 ZOOM_connection_remove_task(c);
1147                 ZOOM_connection_set_mask(c, 0);
1148                 ZOOM_connection_exec_task(c);
1149             }
1150             c->state = STATE_ESTABLISHED;
1151             return zoom_pending;
1152         }
1153         else if (ret > 0)
1154         {
1155             int mask = ZOOM_SELECT_EXCEPT;
1156             if (c->cs->io_pending & CS_WANT_WRITE)
1157                 mask += ZOOM_SELECT_WRITE;
1158             if (c->cs->io_pending & CS_WANT_READ)
1159                 mask += ZOOM_SELECT_READ;
1160             ZOOM_connection_set_mask(c, mask);
1161             c->state = STATE_CONNECTING; 
1162             return zoom_pending;
1163         }
1164     }
1165     c->state = STATE_IDLE;
1166     set_ZOOM_error(c, ZOOM_ERROR_CONNECT, c->host_port);
1167     return zoom_complete;
1168 }
1169
1170 static void otherInfo_attach(ZOOM_connection c, Z_APDU *a, ODR out)
1171 {
1172     int i;
1173     for (i = 0; i<200; i++)
1174     {
1175         size_t len;
1176         int *oid;
1177         Z_OtherInformation **oi;
1178         char buf[80];
1179         const char *val;
1180         const char *cp;
1181
1182         sprintf(buf, "otherInfo%d", i);
1183         val = ZOOM_options_get(c->options, buf);
1184         if (!val)
1185             break;
1186         cp = strchr(val, ':');
1187         if (!cp)
1188             continue;
1189         len = cp - val;
1190         if (len >= sizeof(buf))
1191             len = sizeof(buf)-1;
1192         memcpy(buf, val, len);
1193         buf[len] = '\0';
1194         
1195         oid = yaz_string_to_oid_odr(yaz_oid_std(), CLASS_USERINFO,
1196                                     buf, out);
1197         if (!oid)
1198             continue;
1199         
1200         yaz_oi_APDU(a, &oi);
1201         yaz_oi_set_string_oid(oi, out, oid, 1, cp+1);
1202     }
1203 }
1204
1205 static int encode_APDU(ZOOM_connection c, Z_APDU *a, ODR out)
1206 {
1207     assert(a);
1208     if (c->cookie_out)
1209     {
1210         Z_OtherInformation **oi;
1211         yaz_oi_APDU(a, &oi);
1212         yaz_oi_set_string_oid(oi, out, yaz_oid_userinfo_cookie, 
1213                               1, c->cookie_out);
1214     }
1215     if (c->client_IP)
1216     {
1217         Z_OtherInformation **oi;
1218         yaz_oi_APDU(a, &oi);
1219         yaz_oi_set_string_oid(oi, out, yaz_oid_userinfo_client_ip, 
1220                               1, c->client_IP);
1221     }
1222     otherInfo_attach(c, a, out);
1223     if (!z_APDU(out, &a, 0, 0))
1224     {
1225         FILE *outf = fopen("/tmp/apdu.txt", "a");
1226         if (a && outf)
1227         {
1228             ODR odr_pr = odr_createmem(ODR_PRINT);
1229             fprintf(outf, "a=%p\n", a);
1230             odr_setprint(odr_pr, outf);
1231             z_APDU(odr_pr, &a, 0, 0);
1232             odr_destroy(odr_pr);
1233         }
1234         yaz_log(log_api, "%p encoding_APDU: encoding failed", c);
1235         set_ZOOM_error(c, ZOOM_ERROR_ENCODE, 0);
1236         odr_reset(out);
1237         return -1;
1238     }
1239     yaz_log(log_details, "%p encoding_APDU encoding OK", c);
1240     return 0;
1241 }
1242
1243 static zoom_ret send_APDU(ZOOM_connection c, Z_APDU *a)
1244 {
1245     ZOOM_Event event;
1246     assert(a);
1247     if (encode_APDU(c, a, c->odr_out))
1248         return zoom_complete;
1249     yaz_log(log_details, "%p send APDU type=%d", c, a->which);
1250     c->buf_out = odr_getbuf(c->odr_out, &c->len_out, 0);
1251     event = ZOOM_Event_create(ZOOM_EVENT_SEND_APDU);
1252     ZOOM_connection_put_event(c, event);
1253     odr_reset(c->odr_out);
1254     return do_write(c);
1255 }
1256
1257 /* returns 1 if PDU was sent OK (still pending )
1258    0 if PDU was not sent OK (nothing to wait for) 
1259 */
1260
1261 static zoom_ret ZOOM_connection_send_init(ZOOM_connection c)
1262 {
1263     Z_APDU *apdu = zget_APDU(c->odr_out, Z_APDU_initRequest);
1264     Z_InitRequest *ireq = apdu->u.initRequest;
1265     Z_IdAuthentication *auth = (Z_IdAuthentication *)
1266         odr_malloc(c->odr_out, sizeof(*auth));
1267     const char *auth_groupId = ZOOM_options_get(c->options, "group");
1268     const char *auth_userId = ZOOM_options_get(c->options, "user");
1269     const char *auth_password = ZOOM_options_get(c->options, "password");
1270     char *version;
1271
1272     /* support the pass for backwards compatibility */
1273     if (!auth_password)
1274         auth_password = ZOOM_options_get(c->options, "pass");
1275         
1276     ODR_MASK_SET(ireq->options, Z_Options_search);
1277     ODR_MASK_SET(ireq->options, Z_Options_present);
1278     ODR_MASK_SET(ireq->options, Z_Options_scan);
1279     ODR_MASK_SET(ireq->options, Z_Options_sort);
1280     ODR_MASK_SET(ireq->options, Z_Options_extendedServices);
1281     ODR_MASK_SET(ireq->options, Z_Options_namedResultSets);
1282     
1283     ODR_MASK_SET(ireq->protocolVersion, Z_ProtocolVersion_1);
1284     ODR_MASK_SET(ireq->protocolVersion, Z_ProtocolVersion_2);
1285     ODR_MASK_SET(ireq->protocolVersion, Z_ProtocolVersion_3);
1286     
1287     /* Index Data's Z39.50 Implementor Id is 81 */
1288     ireq->implementationId =
1289         odr_prepend(c->odr_out,
1290                     ZOOM_options_get(c->options, "implementationId"),
1291                     odr_prepend(c->odr_out, "81", ireq->implementationId));
1292     
1293     ireq->implementationName = 
1294         odr_prepend(c->odr_out,
1295                     ZOOM_options_get(c->options, "implementationName"),
1296                     odr_prepend(c->odr_out, "ZOOM-C",
1297                                 ireq->implementationName));
1298     
1299     version = odr_strdup(c->odr_out, "$Revision: 1.125 $");
1300     if (strlen(version) > 10)   /* check for unexpanded CVS strings */
1301         version[strlen(version)-2] = '\0';
1302     ireq->implementationVersion = 
1303         odr_prepend(c->odr_out,
1304                     ZOOM_options_get(c->options, "implementationVersion"),
1305                     odr_prepend(c->odr_out, &version[11],
1306                                 ireq->implementationVersion));
1307     
1308     *ireq->maximumRecordSize =
1309         ZOOM_options_get_int(c->options, "maximumRecordSize", 1024*1024);
1310     *ireq->preferredMessageSize =
1311         ZOOM_options_get_int(c->options, "preferredMessageSize", 1024*1024);
1312     
1313     if (auth_groupId || auth_password)
1314     {
1315         Z_IdPass *pass = (Z_IdPass *) odr_malloc(c->odr_out, sizeof(*pass));
1316         int i = 0;
1317         pass->groupId = 0;
1318         if (auth_groupId && *auth_groupId)
1319         {
1320             pass->groupId = (char *)
1321                 odr_malloc(c->odr_out, strlen(auth_groupId)+1);
1322             strcpy(pass->groupId, auth_groupId);
1323             i++;
1324         }
1325         pass->userId = 0;
1326         if (auth_userId && *auth_userId)
1327         {
1328             pass->userId = (char *)
1329                 odr_malloc(c->odr_out, strlen(auth_userId)+1);
1330             strcpy(pass->userId, auth_userId);
1331             i++;
1332         }
1333         pass->password = 0;
1334         if (auth_password && *auth_password)
1335         {
1336             pass->password = (char *)
1337                 odr_malloc(c->odr_out, strlen(auth_password)+1);
1338             strcpy(pass->password, auth_password);
1339             i++;
1340         }
1341         if (i)
1342         {
1343             auth->which = Z_IdAuthentication_idPass;
1344             auth->u.idPass = pass;
1345             ireq->idAuthentication = auth;
1346         }
1347     }
1348     else if (auth_userId)
1349     {
1350         auth->which = Z_IdAuthentication_open;
1351         auth->u.open = (char *)
1352             odr_malloc(c->odr_out, strlen(auth_userId)+1);
1353         strcpy(auth->u.open, auth_userId);
1354         ireq->idAuthentication = auth;
1355     }
1356     if (c->proxy)
1357     {
1358         yaz_oi_set_string_oid(&ireq->otherInfo, c->odr_out,
1359                               yaz_oid_userinfo_proxy, 1, c->host_port);
1360     }
1361     if (c->charset || c->lang)
1362     {
1363         Z_OtherInformation **oi;
1364         Z_OtherInformationUnit *oi_unit;
1365         
1366         yaz_oi_APDU(apdu, &oi);
1367         
1368         if ((oi_unit = yaz_oi_update(oi, c->odr_out, NULL, 0, 0)))
1369         {
1370             ODR_MASK_SET(ireq->options, Z_Options_negotiationModel);
1371             oi_unit->which = Z_OtherInfo_externallyDefinedInfo;
1372             oi_unit->information.externallyDefinedInfo =
1373                 yaz_set_proposal_charneg_list(c->odr_out, " ",
1374                                               c->charset, c->lang, 1);
1375         }
1376     }
1377     assert(apdu);
1378     return send_APDU(c, apdu);
1379 }
1380
1381 #if YAZ_HAVE_XML2
1382 static zoom_ret send_srw(ZOOM_connection c, Z_SRW_PDU *sr)
1383 {
1384     Z_GDU *gdu;
1385     ZOOM_Event event;
1386
1387     gdu = z_get_HTTP_Request_host_path(c->odr_out, c->host_port, c->path);
1388
1389     if (c->sru_mode == zoom_sru_get)
1390     {
1391         yaz_sru_get_encode(gdu->u.HTTP_Request, sr, c->odr_out, c->charset);
1392     }
1393     else if (c->sru_mode == zoom_sru_post)
1394     {
1395         yaz_sru_post_encode(gdu->u.HTTP_Request, sr, c->odr_out, c->charset);
1396     }
1397     else if (c->sru_mode == zoom_sru_soap)
1398     {
1399         yaz_sru_soap_encode(gdu->u.HTTP_Request, sr, c->odr_out, c->charset);
1400     }
1401     if (!z_GDU(c->odr_out, &gdu, 0, 0))
1402         return zoom_complete;
1403     c->buf_out = odr_getbuf(c->odr_out, &c->len_out, 0);
1404         
1405     event = ZOOM_Event_create(ZOOM_EVENT_SEND_APDU);
1406     ZOOM_connection_put_event(c, event);
1407     odr_reset(c->odr_out);
1408     return do_write(c);
1409 }
1410 #endif
1411
1412 #if YAZ_HAVE_XML2
1413 static zoom_ret ZOOM_connection_srw_send_search(ZOOM_connection c)
1414 {
1415     int i;
1416     int *start, *count;
1417     ZOOM_resultset resultset = 0;
1418     Z_SRW_PDU *sr = 0;
1419     const char *option_val = 0;
1420
1421     if (c->error)                  /* don't continue on error */
1422         return zoom_complete;
1423     assert(c->tasks);
1424     switch(c->tasks->which)
1425     {
1426     case ZOOM_TASK_SEARCH:
1427         resultset = c->tasks->u.search.resultset;
1428         resultset->setname = xstrdup("default");
1429         ZOOM_options_set(resultset->options, "setname", resultset->setname);
1430         start = &c->tasks->u.search.start;
1431         count = &c->tasks->u.search.count;
1432         break;
1433     case ZOOM_TASK_RETRIEVE:
1434         resultset = c->tasks->u.retrieve.resultset;
1435
1436         start = &c->tasks->u.retrieve.start;
1437         count = &c->tasks->u.retrieve.count;
1438         
1439         if (*start >= resultset->size)
1440             return zoom_complete;
1441         if (*start + *count > resultset->size)
1442             *count = resultset->size - *start;
1443
1444         for (i = 0; i < *count; i++)
1445         {
1446             ZOOM_record rec =
1447                 record_cache_lookup(resultset, i + *start,
1448                                     c->tasks->u.retrieve.syntax,
1449                                     c->tasks->u.retrieve.elementSetName);
1450             if (!rec)
1451                 break;
1452             else
1453             {
1454                 ZOOM_Event event = ZOOM_Event_create(ZOOM_EVENT_RECV_RECORD);
1455                 ZOOM_connection_put_event(c, event);
1456             }
1457         }
1458         *start += i;
1459         *count -= i;
1460
1461         if (*count == 0)
1462             return zoom_complete;
1463         break;
1464     default:
1465         return zoom_complete;
1466     }
1467     assert(resultset->query);
1468         
1469     sr = yaz_srw_get(c->odr_out, Z_SRW_searchRetrieve_request);
1470
1471     if (resultset->query->z_query->which == Z_Query_type_104
1472         && resultset->query->z_query->u.type_104->which == Z_External_CQL)
1473     {
1474         sr->u.request->query_type = Z_SRW_query_type_cql;
1475         sr->u.request->query.cql =resultset->query->z_query->u.type_104->u.cql;
1476     }
1477     else if (resultset->query->z_query->which == Z_Query_type_1 &&
1478              resultset->query->z_query->u.type_1)
1479     {
1480         sr->u.request->query_type = Z_SRW_query_type_pqf;
1481         sr->u.request->query.pqf = resultset->query->query_string;
1482     }
1483     else
1484     {
1485         set_ZOOM_error(c, ZOOM_ERROR_UNSUPPORTED_QUERY, 0);
1486         return zoom_complete;
1487     }
1488     sr->u.request->startRecord = odr_intdup(c->odr_out, *start + 1);
1489     sr->u.request->maximumRecords = odr_intdup(
1490         c->odr_out, resultset->step>0 ? resultset->step : *count);
1491     sr->u.request->recordSchema = resultset->schema;
1492     
1493     option_val = ZOOM_resultset_option_get(resultset, "recordPacking");
1494     if (option_val)
1495         sr->u.request->recordPacking = odr_strdup(c->odr_out, option_val);
1496
1497     option_val = ZOOM_resultset_option_get(resultset, "extraArgs");
1498     if (option_val)
1499         sr->extra_args = odr_strdup(c->odr_out, option_val);
1500     return send_srw(c, sr);
1501 }
1502 #else
1503 static zoom_ret ZOOM_connection_srw_send_search(ZOOM_connection c)
1504 {
1505     return zoom_complete;
1506 }
1507 #endif
1508
1509 static zoom_ret ZOOM_connection_send_search(ZOOM_connection c)
1510 {
1511     ZOOM_resultset r;
1512     int lslb, ssub, mspn;
1513     const char *syntax;
1514     Z_APDU *apdu = zget_APDU(c->odr_out, Z_APDU_searchRequest);
1515     Z_SearchRequest *search_req = apdu->u.searchRequest;
1516     const char *elementSetName;
1517     const char *smallSetElementSetName;
1518     const char *mediumSetElementSetName;
1519
1520     assert(c->tasks);
1521     assert(c->tasks->which == ZOOM_TASK_SEARCH);
1522
1523     r = c->tasks->u.search.resultset;
1524
1525     yaz_log(log_details, "%p ZOOM_connection_send_search set=%p", c, r);
1526
1527     elementSetName =
1528         ZOOM_options_get(r->options, "elementSetName");
1529     smallSetElementSetName  =
1530         ZOOM_options_get(r->options, "smallSetElementSetName");
1531     mediumSetElementSetName =
1532         ZOOM_options_get(r->options, "mediumSetElementSetName");
1533
1534     if (!smallSetElementSetName)
1535         smallSetElementSetName = elementSetName;
1536
1537     if (!mediumSetElementSetName)
1538         mediumSetElementSetName = elementSetName;
1539
1540     assert(r);
1541     assert(r->query);
1542
1543     /* prepare query for the search request */
1544     search_req->query = r->query->z_query;
1545     if (!search_req->query)
1546     {
1547         set_ZOOM_error(c, ZOOM_ERROR_INVALID_QUERY, 0);
1548         return zoom_complete;
1549     }
1550
1551     search_req->databaseNames = r->databaseNames;
1552     search_req->num_databaseNames = r->num_databaseNames;
1553
1554     /* get syntax (no need to provide unless piggyback is in effect) */
1555     syntax = c->tasks->u.search.syntax;
1556
1557     lslb = ZOOM_options_get_int(r->options, "largeSetLowerBound", -1);
1558     ssub = ZOOM_options_get_int(r->options, "smallSetUpperBound", -1);
1559     mspn = ZOOM_options_get_int(r->options, "mediumSetPresentNumber", -1);
1560     if (lslb != -1 && ssub != -1 && mspn != -1)
1561     {
1562         /* So're a Z39.50 expert? Let's hope you don't do sort */
1563         *search_req->largeSetLowerBound = lslb;
1564         *search_req->smallSetUpperBound = ssub;
1565         *search_req->mediumSetPresentNumber = mspn;
1566     }
1567     else if (c->tasks->u.search.start == 0 && c->tasks->u.search.count > 0
1568              && r->piggyback && !r->r_sort_spec && !r->schema)
1569     {
1570         /* Regular piggyback - do it unless we're going to do sort */
1571         *search_req->largeSetLowerBound = 2000000000;
1572         *search_req->smallSetUpperBound = 1;
1573         *search_req->mediumSetPresentNumber = 
1574             r->step>0 ? r->step : c->tasks->u.search.count;
1575     }
1576     else
1577     {
1578         /* non-piggyback. Need not provide elementsets or syntaxes .. */
1579         smallSetElementSetName = 0;
1580         mediumSetElementSetName = 0;
1581         syntax = 0;
1582     }
1583     if (smallSetElementSetName && *smallSetElementSetName)
1584     {
1585         Z_ElementSetNames *esn = (Z_ElementSetNames *)
1586             odr_malloc(c->odr_out, sizeof(*esn));
1587         
1588         esn->which = Z_ElementSetNames_generic;
1589         esn->u.generic = odr_strdup(c->odr_out, smallSetElementSetName);
1590         search_req->smallSetElementSetNames = esn;
1591     }
1592     if (mediumSetElementSetName && *mediumSetElementSetName)
1593     {
1594         Z_ElementSetNames *esn =(Z_ElementSetNames *)
1595             odr_malloc(c->odr_out, sizeof(*esn));
1596         
1597         esn->which = Z_ElementSetNames_generic;
1598         esn->u.generic = odr_strdup(c->odr_out, mediumSetElementSetName);
1599         search_req->mediumSetElementSetNames = esn;
1600     }
1601     if (syntax)
1602         search_req->preferredRecordSyntax =
1603             zoom_yaz_str_to_z3950oid(c, CLASS_RECSYN, syntax);
1604     
1605     if (!r->setname)
1606     {
1607         if (c->support_named_resultsets)
1608         {
1609             char setname[14];
1610             int ord;
1611             /* find the lowest unused ordinal so that we re-use
1612                result sets on the server. */
1613             for (ord = 1; ; ord++)
1614             {
1615                 ZOOM_resultset rp;
1616                 sprintf(setname, "%d", ord);
1617                 for (rp = c->resultsets; rp; rp = rp->next)
1618                     if (rp->setname && !strcmp(rp->setname, setname))
1619                         break;
1620                 if (!rp)
1621                     break;
1622             }
1623             r->setname = xstrdup(setname);
1624             yaz_log(log_details, "%p ZOOM_connection_send_search: allocating "
1625                     "set %s", c, r->setname);
1626         }
1627         else
1628         {
1629             yaz_log(log_details, "%p ZOOM_connection_send_search: using "
1630                     "default set", c);
1631             r->setname = xstrdup("default");
1632         }
1633         ZOOM_options_set(r->options, "setname", r->setname);
1634     }
1635     search_req->resultSetName = odr_strdup(c->odr_out, r->setname);
1636     return send_APDU(c, apdu);
1637 }
1638
1639 static void response_default_diag(ZOOM_connection c, Z_DefaultDiagFormat *r)
1640 {
1641     int oclass;
1642     char *addinfo = 0;
1643
1644     switch (r->which)
1645     {
1646     case Z_DefaultDiagFormat_v2Addinfo:
1647         addinfo = r->u.v2Addinfo;
1648         break;
1649     case Z_DefaultDiagFormat_v3Addinfo:
1650         addinfo = r->u.v3Addinfo;
1651         break;
1652     }
1653     xfree(c->addinfo);
1654     c->addinfo = 0;
1655     set_dset_error(c, *r->condition,
1656                    yaz_oid_to_string(yaz_oid_std(), 
1657                                      r->diagnosticSetId, &oclass),
1658                    addinfo, 0);
1659 }
1660
1661 static void response_diag(ZOOM_connection c, Z_DiagRec *p)
1662 {
1663     if (p->which != Z_DiagRec_defaultFormat)
1664         set_ZOOM_error(c, ZOOM_ERROR_DECODE, 0);
1665     else
1666         response_default_diag(c, p->u.defaultFormat);
1667 }
1668
1669 ZOOM_API(ZOOM_record)
1670     ZOOM_record_clone(ZOOM_record srec)
1671 {
1672     char *buf;
1673     int size;
1674     ODR odr_enc;
1675     ZOOM_record nrec;
1676
1677     odr_enc = odr_createmem(ODR_ENCODE);
1678     if (!z_NamePlusRecord(odr_enc, &srec->npr, 0, 0))
1679         return 0;
1680     buf = odr_getbuf(odr_enc, &size, 0);
1681     
1682     nrec = (ZOOM_record) xmalloc(sizeof(*nrec));
1683     nrec->odr = odr_createmem(ODR_DECODE);
1684     nrec->wrbuf_marc = 0;
1685     nrec->wrbuf_iconv = 0;
1686     nrec->wrbuf_opac = 0;
1687     odr_setbuf(nrec->odr, buf, size, 0);
1688     z_NamePlusRecord(nrec->odr, &nrec->npr, 0, 0);
1689     
1690     odr_destroy(odr_enc);
1691     return nrec;
1692 }
1693
1694 ZOOM_API(ZOOM_record)
1695     ZOOM_resultset_record_immediate(ZOOM_resultset s,size_t pos)
1696 {
1697     const char *syntax =
1698         ZOOM_options_get(s->options, "preferredRecordSyntax"); 
1699     const char *elementSetName =
1700         ZOOM_options_get(s->options, "elementSetName");
1701
1702     return record_cache_lookup(s, pos, syntax, elementSetName);
1703 }
1704
1705 ZOOM_API(ZOOM_record)
1706     ZOOM_resultset_record(ZOOM_resultset r, size_t pos)
1707 {
1708     ZOOM_record rec = ZOOM_resultset_record_immediate(r, pos);
1709
1710     if (!rec)
1711     {
1712         /*
1713          * MIKE: I think force_sync should always be zero, but I don't
1714          * want to make this change until I get the go-ahead from
1715          * Adam, in case something depends on the old synchronous
1716          * behaviour.
1717          */
1718         int force_sync = 1;
1719         if (getenv("ZOOM_RECORD_NO_FORCE_SYNC")) force_sync = 0;
1720         ZOOM_resultset_retrieve(r, force_sync, pos, 1);
1721         rec = ZOOM_resultset_record_immediate(r, pos);
1722     }
1723     return rec;
1724 }
1725
1726 ZOOM_API(void)
1727     ZOOM_record_destroy(ZOOM_record rec)
1728 {
1729     if (!rec)
1730         return;
1731     if (rec->wrbuf_marc)
1732         wrbuf_destroy(rec->wrbuf_marc);
1733     if (rec->wrbuf_iconv)
1734         wrbuf_destroy(rec->wrbuf_iconv);
1735     if (rec->wrbuf_opac)
1736         wrbuf_destroy(rec->wrbuf_opac);
1737     odr_destroy(rec->odr);
1738     xfree(rec);
1739 }
1740
1741 static const char *marc_iconv_return(ZOOM_record rec, int marc_type,
1742                                      int *len,
1743                                      const char *buf, int sz,
1744                                      const char *record_charset)
1745 {
1746     char to[40];
1747     char from[40];
1748     yaz_iconv_t cd = 0;
1749     yaz_marc_t mt = yaz_marc_create();
1750
1751     *from = '\0';
1752     strcpy(to, "UTF-8");
1753     if (record_charset && *record_charset)
1754     {
1755         /* Use "from,to" or just "from" */
1756         const char *cp = strchr(record_charset, ',');
1757         int clen = strlen(record_charset);
1758         if (cp && cp[1])
1759         {
1760             strncpy( to, cp+1, sizeof(to)-1);
1761             to[sizeof(to)-1] = '\0';
1762             clen = cp - record_charset;
1763         }
1764         if (clen > sizeof(from)-1)
1765             clen = sizeof(from)-1;
1766         
1767         if (clen)
1768             strncpy(from, record_charset, clen);
1769         from[clen] = '\0';
1770     }
1771
1772     if (*from && *to)
1773     {
1774         cd = yaz_iconv_open(to, from);
1775         yaz_marc_iconv(mt, cd);
1776     }
1777
1778     yaz_marc_xml(mt, marc_type);
1779     if (!rec->wrbuf_marc)
1780         rec->wrbuf_marc = wrbuf_alloc();
1781     wrbuf_rewind(rec->wrbuf_marc);
1782     if (yaz_marc_decode_wrbuf(mt, buf, sz, rec->wrbuf_marc) > 0)
1783     {
1784         yaz_marc_destroy(mt);
1785         if (cd)
1786             yaz_iconv_close(cd);
1787         if (len)
1788             *len = wrbuf_len(rec->wrbuf_marc);
1789         return wrbuf_cstr(rec->wrbuf_marc);
1790     }
1791     yaz_marc_destroy(mt);
1792     if (cd)
1793         yaz_iconv_close(cd);
1794     return 0;
1795 }
1796
1797 static const char *record_iconv_return(ZOOM_record rec, int *len,
1798                                        const char *buf, int sz,
1799                                        const char *record_charset)
1800 {
1801     char to[40];
1802     char from[40];
1803     yaz_iconv_t cd = 0;
1804
1805     *from = '\0';
1806     strcpy(to, "UTF-8");
1807
1808     if (record_charset && *record_charset)
1809     {
1810         /* Use "from,to" or just "from" */
1811         const char *cp = strchr(record_charset, ',');
1812         int clen = strlen(record_charset);
1813         if (cp && cp[1])
1814         {
1815             strncpy( to, cp+1, sizeof(to)-1);
1816             to[sizeof(to)-1] = '\0';
1817             clen = cp - record_charset;
1818         }
1819         if (clen > sizeof(from)-1)
1820             clen = sizeof(from)-1;
1821         
1822         if (clen)
1823             strncpy(from, record_charset, clen);
1824         from[clen] = '\0';
1825     }
1826
1827     if (*from && *to && (cd = yaz_iconv_open(to, from)))
1828     {
1829         if (!rec->wrbuf_iconv)
1830             rec->wrbuf_iconv = wrbuf_alloc();
1831
1832         wrbuf_rewind(rec->wrbuf_iconv);
1833
1834         wrbuf_iconv_write(rec->wrbuf_iconv, cd, buf, sz);
1835         wrbuf_iconv_reset(rec->wrbuf_iconv, cd);
1836
1837         buf = wrbuf_cstr(rec->wrbuf_iconv);
1838         sz = wrbuf_len(rec->wrbuf_iconv);
1839         yaz_iconv_close(cd);
1840     }
1841     if (len)
1842         *len = sz;
1843     return buf;
1844 }
1845
1846 ZOOM_API(int)
1847     ZOOM_record_error(ZOOM_record rec, const char **cp,
1848                       const char **addinfo, const char **diagset)
1849 {
1850     Z_NamePlusRecord *npr;
1851     
1852     if (!rec)
1853         return 0;
1854     npr = rec->npr;
1855     if (npr && npr->which == Z_NamePlusRecord_surrogateDiagnostic)
1856     {
1857         Z_DiagRec *diag_rec = npr->u.surrogateDiagnostic;
1858         int error = YAZ_BIB1_UNSPECIFIED_ERROR;
1859         const char *add = 0;
1860
1861         if (diag_rec->which == Z_DiagRec_defaultFormat)
1862         {
1863             Z_DefaultDiagFormat *ddf = diag_rec->u.defaultFormat;
1864             int oclass;
1865     
1866             error = *ddf->condition;
1867             switch (ddf->which)
1868             {
1869             case Z_DefaultDiagFormat_v2Addinfo:
1870                 add = ddf->u.v2Addinfo;
1871                 break;
1872             case Z_DefaultDiagFormat_v3Addinfo:
1873                 add = ddf->u.v3Addinfo;
1874                 break;
1875             }
1876             if (diagset)
1877                 *diagset =
1878                     yaz_oid_to_string(yaz_oid_std(),
1879                                       ddf->diagnosticSetId, &oclass);
1880         }
1881         else
1882         {
1883             if (diagset)
1884                 *diagset = "Bib-1";
1885         }
1886         if (addinfo)
1887             *addinfo = add ? add : "";
1888         if (cp)
1889             *cp = diagbib1_str(error);
1890         return error;
1891     }
1892     return 0;
1893 }
1894
1895 ZOOM_API(const char *)
1896     ZOOM_record_get(ZOOM_record rec, const char *type_spec, int *len)
1897 {
1898     char type[40];
1899     char charset[40];
1900     char xpath[512];
1901     const char *cp;
1902     int i;
1903     Z_NamePlusRecord *npr;
1904     
1905     if (len)
1906         *len = 0; /* default return */
1907         
1908     if (!rec)
1909         return 0;
1910     npr = rec->npr;
1911     if (!npr)
1912         return 0;
1913
1914     cp = type_spec;
1915     for (i = 0; cp[i] && i < sizeof(type)-1; i++)
1916     {
1917         if (cp[i] == ';' || cp[i] == ' ')
1918             break;
1919         type[i] = cp[i];
1920     }
1921     type[i] = '\0';
1922     charset[0] = '\0';
1923     while (type_spec[i] == ';')
1924     {
1925         i++;
1926         while (type_spec[i] == ' ')
1927             i++;
1928         if (!strncmp(type_spec+i, "charset=", 8))
1929         {
1930             int j = 0;
1931             i = i + 8; /* skip charset= */
1932             for (j = 0; type_spec[i]  && j < sizeof(charset)-1; i++, j++)
1933             {
1934                 if (type_spec[i] == ';' || type_spec[i] == ' ')
1935                     break;
1936                 charset[j] = cp[i];
1937             }
1938             charset[j] = '\0';
1939         }
1940         else if (!strncmp(type_spec+i, "xpath=", 6))
1941         {
1942             int j = 0; 
1943             i = i + 6;
1944             for (j = 0; type_spec[i] && j < sizeof(xpath)-1; i++, j++)
1945                 xpath[j] = cp[i];
1946             xpath[j] = '\0';
1947         } 
1948         while (type_spec[i] == ' ')
1949             i++;
1950     }
1951     if (!strcmp(type, "database"))
1952     {
1953         if (len)
1954             *len = (npr->databaseName ? strlen(npr->databaseName) : 0);
1955         return npr->databaseName;
1956     }
1957     else if (!strcmp(type, "syntax"))
1958     {
1959         const char *desc = 0;   
1960         if (npr->which == Z_NamePlusRecord_databaseRecord)
1961         {
1962             Z_External *r = (Z_External *) npr->u.databaseRecord;
1963             desc = yaz_oid_to_string(yaz_oid_std(), r->direct_reference, 0);
1964         }
1965         if (!desc)
1966             desc = "none";
1967         if (len)
1968             *len = strlen(desc);
1969         return desc;
1970     }
1971     if (npr->which != Z_NamePlusRecord_databaseRecord)
1972         return 0;
1973
1974     /* from now on - we have a database record .. */
1975     if (!strcmp(type, "render"))
1976     {
1977         Z_External *r = (Z_External *) npr->u.databaseRecord;
1978         const int *oid = r->direct_reference;
1979
1980         /* render bibliographic record .. */
1981         if (r->which == Z_External_OPAC)
1982         {
1983             r = r->u.opac->bibliographicRecord;
1984             if (!r)
1985                 return 0;
1986             oid = r->direct_reference;
1987         }
1988         if (r->which == Z_External_sutrs)
1989             return record_iconv_return(rec, len,
1990                                        (char*) r->u.sutrs->buf,
1991                                        r->u.sutrs->len,
1992                                        charset);
1993         else if (r->which == Z_External_octet)
1994         {
1995             if (yaz_oid_is_iso2709(oid))
1996             {
1997                 const char *ret_buf = marc_iconv_return(
1998                     rec, YAZ_MARC_LINE, len,
1999                     (const char *) r->u.octet_aligned->buf,
2000                     r->u.octet_aligned->len,
2001                     charset);
2002                 if (ret_buf)
2003                     return ret_buf;
2004             }
2005             return record_iconv_return(rec, len,
2006                                        (const char *) r->u.octet_aligned->buf,
2007                                        r->u.octet_aligned->len,
2008                                        charset);
2009         }
2010         else if (r->which == Z_External_grs1)
2011         {
2012             if (!rec->wrbuf_marc)
2013                 rec->wrbuf_marc = wrbuf_alloc();
2014             wrbuf_rewind(rec->wrbuf_marc);
2015             yaz_display_grs1(rec->wrbuf_marc, r->u.grs1, 0);
2016             return record_iconv_return(rec, len,
2017                                        wrbuf_buf(rec->wrbuf_marc),
2018                                        wrbuf_len(rec->wrbuf_marc),
2019                                        charset);
2020         }
2021         return 0;
2022     }
2023     else if (!strcmp(type, "xml"))
2024     {
2025         Z_External *r = (Z_External *) npr->u.databaseRecord;
2026         const int *oid = r->direct_reference;
2027
2028         /* render bibliographic record .. */
2029         if (r->which == Z_External_OPAC)
2030         {
2031             r = r->u.opac->bibliographicRecord;
2032             if (!r)
2033                 return 0;
2034             oid = r->direct_reference;
2035         }
2036         
2037         if (r->which == Z_External_sutrs)
2038             return record_iconv_return(rec, len,
2039                                        (const char *) r->u.sutrs->buf,
2040                                        r->u.sutrs->len,
2041                                        charset);
2042         else if (r->which == Z_External_octet)
2043         {
2044             int marc_decode_type = YAZ_MARC_MARCXML;
2045             if (yaz_oid_is_iso2709(oid))
2046             {
2047                 const char *ret_buf = marc_iconv_return(
2048                     rec, marc_decode_type, len,
2049                     (const char *) r->u.octet_aligned->buf,
2050                     r->u.octet_aligned->len,
2051                     charset);
2052                 if (ret_buf)
2053                     return ret_buf;
2054             }
2055             return record_iconv_return(rec, len,
2056                                        (const char *) r->u.octet_aligned->buf,
2057                                        r->u.octet_aligned->len,
2058                                        charset);
2059         }
2060         else if (r->which == Z_External_grs1)
2061         {
2062             if (len) *len = 5;
2063             return "GRS-1";
2064         }
2065         return 0;
2066     }
2067     else if (!strcmp(type, "raw"))
2068     {
2069         Z_External *r = (Z_External *) npr->u.databaseRecord;
2070         
2071         if (r->which == Z_External_sutrs)
2072         {
2073             if (len) *len = r->u.sutrs->len;
2074             return (const char *) r->u.sutrs->buf;
2075         }
2076         else if (r->which == Z_External_octet)
2077         {
2078             if (len) *len = r->u.octet_aligned->len;
2079             return (const char *) r->u.octet_aligned->buf;
2080         }
2081         else /* grs-1, explain, OPAC, ... */
2082         {
2083             if (len) *len = -1;
2084             return (const char *) npr->u.databaseRecord;
2085         }
2086         return 0;
2087     }
2088     else if (!strcmp (type, "ext"))
2089     {
2090         if (len) *len = -1;
2091         return (const char *) npr->u.databaseRecord;
2092     }
2093     else if (!strcmp (type, "opac"))
2094              
2095     {
2096         Z_External *r = (Z_External *) npr->u.databaseRecord;
2097         if (r->which == Z_External_OPAC)
2098         {
2099             if (!rec->wrbuf_opac)
2100                 rec->wrbuf_opac = wrbuf_alloc();
2101             wrbuf_rewind(rec->wrbuf_opac);
2102             yaz_display_OPAC(rec->wrbuf_opac, r->u.opac, 0);
2103             return record_iconv_return(rec, len,
2104                                        wrbuf_buf(rec->wrbuf_opac),
2105                                        wrbuf_len(rec->wrbuf_opac),
2106                                        charset);
2107         }
2108     }
2109     return 0;
2110 }
2111
2112 static int strcmp_null(const char *v1, const char *v2)
2113 {
2114     if (!v1 && !v2)
2115         return 0;
2116     if (!v1 || !v2)
2117         return -1;
2118     return strcmp(v1, v2);
2119 }
2120
2121 static size_t record_hash(int pos)
2122 {
2123     if (pos < 0)
2124         pos = 0;
2125     return pos % RECORD_HASH_SIZE;
2126 }
2127
2128 static void record_cache_add(ZOOM_resultset r, Z_NamePlusRecord *npr, 
2129                              int pos,
2130                              const char *syntax, const char *elementSetName)
2131 {
2132     ZOOM_record_cache rc;
2133     
2134     ZOOM_Event event = ZOOM_Event_create(ZOOM_EVENT_RECV_RECORD);
2135     ZOOM_connection_put_event(r->connection, event);
2136
2137     for (rc = r->record_hash[record_hash(pos)]; rc; rc = rc->next)
2138     {
2139         if (pos == rc->pos)
2140         {
2141             if (strcmp_null(r->schema, rc->schema))
2142                 continue;
2143             if (strcmp_null(elementSetName,rc->elementSetName))
2144                 continue;
2145             if (strcmp_null(syntax, rc->syntax))
2146                 continue;
2147             /* not destroying rc->npr (it's handled by nmem )*/
2148             rc->rec.npr = npr;
2149             /* keeping wrbuf_marc too */
2150             return;
2151         }
2152     }
2153     rc = (ZOOM_record_cache) odr_malloc(r->odr, sizeof(*rc));
2154     rc->rec.npr = npr; 
2155     rc->rec.odr = 0;
2156     rc->rec.wrbuf_marc = 0;
2157     rc->rec.wrbuf_iconv = 0;
2158     rc->rec.wrbuf_opac = 0;
2159     if (elementSetName)
2160         rc->elementSetName = odr_strdup(r->odr, elementSetName);
2161     else
2162         rc->elementSetName = 0;
2163
2164     if (syntax)
2165         rc->syntax = odr_strdup(r->odr, syntax);
2166     else
2167         rc->syntax = 0;
2168
2169     if (r->schema)
2170         rc->schema = odr_strdup(r->odr, r->schema);
2171     else
2172         rc->schema = 0;
2173
2174     rc->pos = pos;
2175     rc->next = r->record_hash[record_hash(pos)];
2176     r->record_hash[record_hash(pos)] = rc;
2177 }
2178
2179 static ZOOM_record record_cache_lookup(ZOOM_resultset r, int pos,
2180                                        const char *syntax,
2181                                        const char *elementSetName)
2182 {
2183     ZOOM_record_cache rc;
2184     
2185     for (rc = r->record_hash[record_hash(pos)]; rc; rc = rc->next)
2186     {
2187         if (pos == rc->pos)
2188         {
2189             if (strcmp_null(r->schema, rc->schema))
2190                 continue;
2191             if (strcmp_null(elementSetName,rc->elementSetName))
2192                 continue;
2193             if (strcmp_null(syntax, rc->syntax))
2194                 continue;
2195             return &rc->rec;
2196         }
2197     }
2198     return 0;
2199 }
2200                                              
2201 static void handle_records(ZOOM_connection c, Z_Records *sr,
2202                            int present_phase)
2203 {
2204     ZOOM_resultset resultset;
2205     int *start, *count;
2206     const char *syntax = 0, *elementSetName = 0;
2207
2208     if (!c->tasks)
2209         return ;
2210     switch (c->tasks->which)
2211     {
2212     case ZOOM_TASK_SEARCH:
2213         resultset = c->tasks->u.search.resultset;
2214         start = &c->tasks->u.search.start;
2215         count = &c->tasks->u.search.count;
2216         syntax = c->tasks->u.search.syntax;
2217         elementSetName = c->tasks->u.search.elementSetName;
2218         break;
2219     case ZOOM_TASK_RETRIEVE:
2220         resultset = c->tasks->u.retrieve.resultset;        
2221         start = &c->tasks->u.retrieve.start;
2222         count = &c->tasks->u.retrieve.count;
2223         syntax = c->tasks->u.retrieve.syntax;
2224         elementSetName = c->tasks->u.retrieve.elementSetName;
2225         break;
2226     default:
2227         return;
2228     }
2229     if (sr && sr->which == Z_Records_NSD)
2230         response_default_diag(c, sr->u.nonSurrogateDiagnostic);
2231     else if (sr && sr->which == Z_Records_multipleNSD)
2232     {
2233         if (sr->u.multipleNonSurDiagnostics->num_diagRecs >= 1)
2234             response_diag(c, sr->u.multipleNonSurDiagnostics->diagRecs[0]);
2235         else
2236             set_ZOOM_error(c, ZOOM_ERROR_DECODE, 0);
2237     }
2238     else 
2239     {
2240         if (*count + *start > resultset->size)
2241             *count = resultset->size - *start;
2242         if (*count < 0)
2243             *count = 0;
2244         if (sr && sr->which == Z_Records_DBOSD)
2245         {
2246             int i;
2247             NMEM nmem = odr_extract_mem(c->odr_in);
2248             Z_NamePlusRecordList *p =
2249                 sr->u.databaseOrSurDiagnostics;
2250             for (i = 0; i<p->num_records; i++)
2251             {
2252                 record_cache_add(resultset, p->records[i], i + *start,
2253                                  syntax, elementSetName);
2254             }
2255             *count -= i;
2256             if (*count < 0)
2257                 *count = 0;
2258             *start += i;
2259             yaz_log(log_details, 
2260                     "handle_records resultset=%p start=%d count=%d",
2261                     resultset, *start, *count);
2262
2263             /* transfer our response to search_nmem .. we need it later */
2264             nmem_transfer(odr_getmem(resultset->odr), nmem);
2265             nmem_destroy(nmem);
2266             if (present_phase && p->num_records == 0)
2267             {
2268                 /* present response and we didn't get any records! */
2269                 Z_NamePlusRecord *myrec = 
2270                     zget_surrogateDiagRec(resultset->odr, 0, 14, 0);
2271                 record_cache_add(resultset, myrec, *start,
2272                                  syntax, elementSetName);
2273             }
2274         }
2275         else if (present_phase)
2276         {
2277             /* present response and we didn't get any records! */
2278             Z_NamePlusRecord *myrec = 
2279                 zget_surrogateDiagRec(resultset->odr, 0, 14, 0);
2280             record_cache_add(resultset, myrec, *start, syntax, elementSetName);
2281         }
2282     }
2283 }
2284
2285 static void handle_present_response(ZOOM_connection c, Z_PresentResponse *pr)
2286 {
2287     handle_records(c, pr->records, 1);
2288 }
2289
2290 static void handle_queryExpressionTerm(ZOOM_options opt, const char *name,
2291                                        Z_Term *term)
2292 {
2293     switch (term->which)
2294     {
2295     case Z_Term_general:
2296         ZOOM_options_setl(opt, name,
2297                           (const char *)(term->u.general->buf), 
2298                           term->u.general->len);
2299         break;
2300     case Z_Term_characterString:
2301         ZOOM_options_set(opt, name, term->u.characterString);
2302         break;
2303     case Z_Term_numeric:
2304         ZOOM_options_set_int(opt, name, *term->u.numeric);
2305         break;
2306     }
2307 }
2308
2309 static void handle_queryExpression(ZOOM_options opt, const char *name,
2310                                    Z_QueryExpression *exp)
2311 {
2312     char opt_name[80];
2313     
2314     switch (exp->which)
2315     {
2316     case Z_QueryExpression_term:
2317         if (exp->u.term && exp->u.term->queryTerm)
2318         {
2319             sprintf(opt_name, "%s.term", name);
2320             handle_queryExpressionTerm(opt, opt_name, exp->u.term->queryTerm);
2321         }
2322         break;
2323     case Z_QueryExpression_query:
2324         break;
2325     }
2326 }
2327
2328 static void handle_searchResult(ZOOM_connection c, ZOOM_resultset resultset,
2329                                 Z_OtherInformation *o)
2330 {
2331     int i;
2332     for (i = 0; o && i < o->num_elements; i++)
2333     {
2334         if (o->list[i]->which == Z_OtherInfo_externallyDefinedInfo)
2335         {
2336             Z_External *ext = o->list[i]->information.externallyDefinedInfo;
2337             
2338             if (ext->which == Z_External_searchResult1)
2339             {
2340                 int j;
2341                 Z_SearchInfoReport *sr = ext->u.searchResult1;
2342                 
2343                 if (sr->num)
2344                     ZOOM_options_set_int(
2345                         resultset->options, "searchresult.size", sr->num);
2346
2347                 for (j = 0; j < sr->num; j++)
2348                 {
2349                     Z_SearchInfoReport_s *ent =
2350                         ext->u.searchResult1->elements[j];
2351                     char pref[80];
2352                     
2353                     sprintf(pref, "searchresult.%d", j);
2354
2355                     if (ent->subqueryId)
2356                     {
2357                         char opt_name[80];
2358                         sprintf(opt_name, "%s.id", pref);
2359                         ZOOM_options_set(resultset->options, opt_name,
2360                                          ent->subqueryId);
2361                     }
2362                     if (ent->subqueryExpression)
2363                     {
2364                         char opt_name[80];
2365                         sprintf(opt_name, "%s.subquery", pref);
2366                         handle_queryExpression(resultset->options, opt_name,
2367                                                ent->subqueryExpression);
2368                     }
2369                     if (ent->subqueryInterpretation)
2370                     {
2371                         char opt_name[80];
2372                         sprintf(opt_name, "%s.interpretation", pref);
2373                         handle_queryExpression(resultset->options, opt_name,
2374                                                ent->subqueryInterpretation);
2375                     }
2376                     if (ent->subqueryRecommendation)
2377                     {
2378                         char opt_name[80];
2379                         sprintf(opt_name, "%s.recommendation", pref);
2380                         handle_queryExpression(resultset->options, opt_name,
2381                                                ent->subqueryRecommendation);
2382                     }
2383                     if (ent->subqueryCount)
2384                     {
2385                         char opt_name[80];
2386                         sprintf(opt_name, "%s.count", pref);
2387                         ZOOM_options_set_int(resultset->options, opt_name,
2388                                              *ent->subqueryCount);
2389                     }                                             
2390                 }
2391             }
2392         }
2393     }
2394 }
2395
2396 static void handle_search_response(ZOOM_connection c, Z_SearchResponse *sr)
2397 {
2398     ZOOM_resultset resultset;
2399     ZOOM_Event event;
2400
2401     if (!c->tasks || c->tasks->which != ZOOM_TASK_SEARCH)
2402         return ;
2403
2404     event = ZOOM_Event_create(ZOOM_EVENT_RECV_SEARCH);
2405     ZOOM_connection_put_event(c, event);
2406
2407     resultset = c->tasks->u.search.resultset;
2408
2409     if (sr->resultSetStatus)
2410     {
2411         ZOOM_options_set_int(resultset->options, "resultSetStatus",
2412                              *sr->resultSetStatus);
2413     }
2414     if (sr->presentStatus)
2415     {
2416         ZOOM_options_set_int(resultset->options, "presentStatus",
2417                              *sr->presentStatus);
2418     }
2419     handle_searchResult(c, resultset, sr->additionalSearchInfo);
2420
2421     resultset->size = *sr->resultCount;
2422     handle_records(c, sr->records, 0);
2423 }
2424
2425 static void sort_response(ZOOM_connection c, Z_SortResponse *res)
2426 {
2427     if (res->diagnostics && res->num_diagnostics > 0)
2428         response_diag(c, res->diagnostics[0]);
2429 }
2430
2431 static int scan_response(ZOOM_connection c, Z_ScanResponse *res)
2432 {
2433     NMEM nmem = odr_extract_mem(c->odr_in);
2434     ZOOM_scanset scan;
2435
2436     if (!c->tasks || c->tasks->which != ZOOM_TASK_SCAN)
2437         return 0;
2438     scan = c->tasks->u.scan.scan;
2439
2440     if (res->entries && res->entries->nonsurrogateDiagnostics)
2441         response_diag(c, res->entries->nonsurrogateDiagnostics[0]);
2442     scan->scan_response = res;
2443     nmem_transfer(odr_getmem(scan->odr), nmem);
2444     if (res->stepSize)
2445         ZOOM_options_set_int(scan->options, "stepSize", *res->stepSize);
2446     if (res->positionOfTerm)
2447         ZOOM_options_set_int(scan->options, "position", *res->positionOfTerm);
2448     if (res->scanStatus)
2449         ZOOM_options_set_int(scan->options, "scanStatus", *res->scanStatus);
2450     if (res->numberOfEntriesReturned)
2451         ZOOM_options_set_int(scan->options, "number",
2452                              *res->numberOfEntriesReturned);
2453     nmem_destroy(nmem);
2454     return 1;
2455 }
2456
2457 static zoom_ret send_sort(ZOOM_connection c,
2458                           ZOOM_resultset resultset)
2459 {
2460     if (c->error)
2461         resultset->r_sort_spec = 0;
2462     if (resultset->r_sort_spec)
2463     {
2464         Z_APDU *apdu = zget_APDU(c->odr_out, Z_APDU_sortRequest);
2465         Z_SortRequest *req = apdu->u.sortRequest;
2466         
2467         req->num_inputResultSetNames = 1;
2468         req->inputResultSetNames = (Z_InternationalString **)
2469             odr_malloc(c->odr_out, sizeof(*req->inputResultSetNames));
2470         req->inputResultSetNames[0] =
2471             odr_strdup(c->odr_out, resultset->setname);
2472         req->sortedResultSetName = odr_strdup(c->odr_out, resultset->setname);
2473         req->sortSequence = resultset->r_sort_spec;
2474         resultset->r_sort_spec = 0;
2475         return send_APDU(c, apdu);
2476     }
2477     return zoom_complete;
2478 }
2479
2480 static zoom_ret send_present(ZOOM_connection c)
2481 {
2482     Z_APDU *apdu = 0;
2483     Z_PresentRequest *req = 0;
2484     int i = 0;
2485     const char *syntax = 0;
2486     const char *elementSetName = 0;
2487     ZOOM_resultset  resultset;
2488     int *start, *count;
2489
2490     if (!c->tasks)
2491     {
2492         yaz_log(log_details, "%p send_present no tasks", c);
2493         return zoom_complete;
2494     }
2495     
2496     switch (c->tasks->which)
2497     {
2498     case ZOOM_TASK_SEARCH:
2499         resultset = c->tasks->u.search.resultset;
2500         start = &c->tasks->u.search.start;
2501         count = &c->tasks->u.search.count;
2502         syntax = c->tasks->u.search.syntax;
2503         elementSetName = c->tasks->u.search.elementSetName;
2504         break;
2505     case ZOOM_TASK_RETRIEVE:
2506         resultset = c->tasks->u.retrieve.resultset;
2507         start = &c->tasks->u.retrieve.start;
2508         count = &c->tasks->u.retrieve.count;
2509         syntax = c->tasks->u.retrieve.syntax;
2510         elementSetName = c->tasks->u.retrieve.elementSetName;
2511         break;
2512     default:
2513         return zoom_complete;
2514     }
2515     yaz_log(log_details, "%p send_present start=%d count=%d",
2516             c, *start, *count);
2517
2518     if (*start < 0 || *count < 0 || *start + *count > resultset->size)
2519     {
2520         set_dset_error(c, YAZ_BIB1_PRESENT_REQUEST_OUT_OF_RANGE, "Bib-1",
2521                        "", 0);
2522     }
2523     if (c->error)                  /* don't continue on error */
2524         return zoom_complete;
2525     yaz_log(log_details, "send_present resultset=%p start=%d count=%d",
2526             resultset, *start, *count);
2527
2528     for (i = 0; i < *count; i++)
2529     {
2530         ZOOM_record rec =
2531             record_cache_lookup(resultset, i + *start, syntax, elementSetName);
2532         if (!rec)
2533             break;
2534         else
2535         {
2536             ZOOM_Event event = ZOOM_Event_create(ZOOM_EVENT_RECV_RECORD);
2537             ZOOM_connection_put_event(c, event);
2538         }
2539     }
2540     *start += i;
2541     *count -= i;
2542
2543     if (*count == 0)
2544     {
2545         yaz_log(log_details, "%p send_present skip=%d no more to fetch", c, i);
2546         return zoom_complete;
2547     }
2548
2549     apdu = zget_APDU(c->odr_out, Z_APDU_presentRequest);
2550     req = apdu->u.presentRequest;
2551
2552     if (i)
2553         yaz_log(log_details, "%p send_present skip=%d", c, i);
2554
2555     *req->resultSetStartPoint = *start + 1;
2556     *req->numberOfRecordsRequested = resultset->step>0 ?
2557         resultset->step : *count;
2558     if (*req->numberOfRecordsRequested + *start > resultset->size)
2559         *req->numberOfRecordsRequested = resultset->size - *start;
2560     assert(*req->numberOfRecordsRequested > 0);
2561
2562     if (syntax && *syntax)
2563         req->preferredRecordSyntax =
2564             zoom_yaz_str_to_z3950oid(c, CLASS_RECSYN, syntax);
2565
2566     if (resultset->schema && *resultset->schema)
2567     {
2568         Z_RecordComposition *compo = (Z_RecordComposition *)
2569             odr_malloc(c->odr_out, sizeof(*compo));
2570
2571         req->recordComposition = compo;
2572         compo->which = Z_RecordComp_complex;
2573         compo->u.complex = (Z_CompSpec *)
2574             odr_malloc(c->odr_out, sizeof(*compo->u.complex));
2575         compo->u.complex->selectAlternativeSyntax = (bool_t *) 
2576             odr_malloc(c->odr_out, sizeof(bool_t));
2577         *compo->u.complex->selectAlternativeSyntax = 0;
2578
2579         compo->u.complex->generic = (Z_Specification *)
2580             odr_malloc(c->odr_out, sizeof(*compo->u.complex->generic));
2581
2582         compo->u.complex->generic->which = Z_Schema_oid;
2583         compo->u.complex->generic->schema.oid = (Odr_oid *)
2584             zoom_yaz_str_to_z3950oid (c, CLASS_SCHEMA, resultset->schema);
2585
2586         if (!compo->u.complex->generic->schema.oid)
2587         {
2588             /* OID wasn't a schema! Try record syntax instead. */
2589
2590             compo->u.complex->generic->schema.oid = (Odr_oid *)
2591                 zoom_yaz_str_to_z3950oid (c, CLASS_RECSYN, resultset->schema);
2592         }
2593         if (elementSetName && *elementSetName)
2594         {
2595             compo->u.complex->generic->elementSpec = (Z_ElementSpec *)
2596                 odr_malloc(c->odr_out, sizeof(Z_ElementSpec));
2597             compo->u.complex->generic->elementSpec->which =
2598                 Z_ElementSpec_elementSetName;
2599             compo->u.complex->generic->elementSpec->u.elementSetName =
2600                 odr_strdup(c->odr_out, elementSetName);
2601         }
2602         else
2603             compo->u.complex->generic->elementSpec = 0;
2604         compo->u.complex->num_dbSpecific = 0;
2605         compo->u.complex->dbSpecific = 0;
2606         compo->u.complex->num_recordSyntax = 0;
2607         compo->u.complex->recordSyntax = 0;
2608     }
2609     else if (elementSetName && *elementSetName)
2610     {
2611         Z_ElementSetNames *esn = (Z_ElementSetNames *)
2612             odr_malloc(c->odr_out, sizeof(*esn));
2613         Z_RecordComposition *compo = (Z_RecordComposition *)
2614             odr_malloc(c->odr_out, sizeof(*compo));
2615         
2616         esn->which = Z_ElementSetNames_generic;
2617         esn->u.generic = odr_strdup(c->odr_out, elementSetName);
2618         compo->which = Z_RecordComp_simple;
2619         compo->u.simple = esn;
2620         req->recordComposition = compo;
2621     }
2622     req->resultSetId = odr_strdup(c->odr_out, resultset->setname);
2623     return send_APDU(c, apdu);
2624 }
2625
2626 ZOOM_API(ZOOM_scanset)
2627     ZOOM_connection_scan(ZOOM_connection c, const char *start)
2628 {
2629     ZOOM_scanset s;
2630     ZOOM_query q = ZOOM_query_create();
2631
2632     ZOOM_query_prefix(q, start);
2633
2634     s = ZOOM_connection_scan1(c, q);
2635     ZOOM_query_destroy(q);
2636     return s;
2637
2638 }
2639
2640 ZOOM_API(ZOOM_scanset)
2641     ZOOM_connection_scan1(ZOOM_connection c, ZOOM_query q)
2642 {
2643     char *start;
2644     char *freeme = 0;
2645     ZOOM_scanset scan = 0;
2646
2647     /*
2648      * We need to check the query-type, so we can recognise CQL and
2649      * CCL and compile them into a form that we can use here.  The
2650      * ZOOM_query structure has no explicit `type' member, but
2651      * inspection of the ZOOM_query_prefix() and ZOOM_query_cql()
2652      * functions shows how the structure is set up in each case.
2653      */
2654     if (!q->z_query)
2655         return 0;
2656     else if (q->z_query->which == Z_Query_type_1) 
2657     {
2658         yaz_log(log_api, "%p ZOOM_connection_scan1 q=%p PQF '%s'",
2659                 c, q, q->query_string);
2660         start = q->query_string;
2661     } 
2662     else if (q->z_query->which == Z_Query_type_104)
2663     {
2664         yaz_log(log_api, "%p ZOOM_connection_scan1 q=%p CQL '%s'",
2665                 c, q, q->query_string);
2666         start = freeme = cql2pqf(c, q->query_string);
2667         if (start == 0)
2668             return 0;
2669     } 
2670     else
2671     {
2672         yaz_log(YLOG_FATAL, "%p ZOOM_connection_scan1 q=%p unknown type '%s'",
2673                 c, q, q->query_string);
2674         abort();
2675     }
2676     
2677     scan = (ZOOM_scanset) xmalloc(sizeof(*scan));
2678     scan->connection = c;
2679     scan->odr = odr_createmem(ODR_DECODE);
2680     scan->options = ZOOM_options_create_with_parent(c->options);
2681     scan->refcount = 1;
2682     scan->scan_response = 0;
2683     scan->termListAndStartPoint =
2684         p_query_scan(scan->odr, PROTO_Z3950, &scan->attributeSet, start);
2685     xfree(freeme);
2686
2687     scan->databaseNames = set_DatabaseNames(c, c->options,
2688                                             &scan->num_databaseNames,
2689                                             scan->odr);
2690     if (scan->termListAndStartPoint != 0)
2691     {
2692         ZOOM_task task = ZOOM_connection_add_task(c, ZOOM_TASK_SCAN);
2693         task->u.scan.scan = scan;
2694         
2695         (scan->refcount)++;
2696         if (!c->async)
2697         {
2698             while (ZOOM_event(1, &c))
2699                 ;
2700         }
2701     }
2702     return scan;
2703 }
2704
2705 ZOOM_API(void)
2706     ZOOM_scanset_destroy(ZOOM_scanset scan)
2707 {
2708     if (!scan)
2709         return;
2710     (scan->refcount)--;
2711     if (scan->refcount == 0)
2712     {
2713         odr_destroy(scan->odr);
2714         
2715         ZOOM_options_destroy(scan->options);
2716         xfree(scan);
2717     }
2718 }
2719
2720 static zoom_ret send_package(ZOOM_connection c)
2721 {
2722     ZOOM_Event event;
2723
2724     yaz_log(log_details, "%p send_package", c);
2725     if (!c->tasks)
2726         return zoom_complete;
2727     assert (c->tasks->which == ZOOM_TASK_PACKAGE);
2728     
2729     event = ZOOM_Event_create(ZOOM_EVENT_SEND_APDU);
2730     ZOOM_connection_put_event(c, event);
2731     
2732     c->buf_out = c->tasks->u.package->buf_out;
2733     c->len_out = c->tasks->u.package->len_out;
2734
2735     return do_write(c);
2736 }
2737
2738 static zoom_ret send_scan(ZOOM_connection c)
2739 {
2740     ZOOM_scanset scan;
2741     Z_APDU *apdu = zget_APDU(c->odr_out, Z_APDU_scanRequest);
2742     Z_ScanRequest *req = apdu->u.scanRequest;
2743
2744     yaz_log(log_details, "%p send_scan", c);
2745     if (!c->tasks)
2746         return zoom_complete;
2747     assert (c->tasks->which == ZOOM_TASK_SCAN);
2748     scan = c->tasks->u.scan.scan;
2749
2750     req->termListAndStartPoint = scan->termListAndStartPoint;
2751     req->attributeSet = scan->attributeSet;
2752
2753     *req->numberOfTermsRequested =
2754         ZOOM_options_get_int(scan->options, "number", 10);
2755
2756     req->preferredPositionInResponse =
2757         odr_intdup(c->odr_out,
2758                    ZOOM_options_get_int(scan->options, "position", 1));
2759
2760     req->stepSize =
2761         odr_intdup(c->odr_out,
2762                    ZOOM_options_get_int(scan->options, "stepSize", 0));
2763     
2764     req->databaseNames = scan->databaseNames;
2765     req->num_databaseNames = scan->num_databaseNames;
2766
2767     return send_APDU(c, apdu);
2768 }
2769
2770 ZOOM_API(size_t)
2771     ZOOM_scanset_size(ZOOM_scanset scan)
2772 {
2773     if (!scan || !scan->scan_response || !scan->scan_response->entries)
2774         return 0;
2775     return scan->scan_response->entries->num_entries;
2776 }
2777
2778 ZOOM_API(const char *)
2779     ZOOM_scanset_term(ZOOM_scanset scan, size_t pos,
2780                       int *occ, int *len)
2781 {
2782     const char *term = 0;
2783     size_t noent = ZOOM_scanset_size(scan);
2784     Z_ScanResponse *res = scan->scan_response;
2785     
2786     *len = 0;
2787     *occ = 0;
2788     if (pos >= noent)
2789         return 0;
2790     if (res->entries->entries[pos]->which == Z_Entry_termInfo)
2791     {
2792         Z_TermInfo *t = res->entries->entries[pos]->u.termInfo;
2793         
2794         if (t->term->which == Z_Term_general)
2795         {
2796             term = (const char *) t->term->u.general->buf;
2797             *len = t->term->u.general->len;
2798         }
2799         *occ = t->globalOccurrences ? *t->globalOccurrences : 0;
2800     }
2801     return term;
2802 }
2803
2804 ZOOM_API(const char *)
2805     ZOOM_scanset_display_term(ZOOM_scanset scan, size_t pos,
2806                               int *occ, int *len)
2807 {
2808     const char *term = 0;
2809     size_t noent = ZOOM_scanset_size(scan);
2810     Z_ScanResponse *res = scan->scan_response;
2811     
2812     *len = 0;
2813     *occ = 0;
2814     if (pos >= noent)
2815         return 0;
2816     if (res->entries->entries[pos]->which == Z_Entry_termInfo)
2817     {
2818         Z_TermInfo *t = res->entries->entries[pos]->u.termInfo;
2819
2820         if (t->displayTerm)
2821         {
2822             term = t->displayTerm;
2823             *len = strlen(term);
2824         }
2825         else if (t->term->which == Z_Term_general)
2826         {
2827             term = (const char *) t->term->u.general->buf;
2828             *len = t->term->u.general->len;
2829         }
2830         *occ = t->globalOccurrences ? *t->globalOccurrences : 0;
2831     }
2832     return term;
2833 }
2834
2835 ZOOM_API(const char *)
2836     ZOOM_scanset_option_get(ZOOM_scanset scan, const char *key)
2837 {
2838     return ZOOM_options_get(scan->options, key);
2839 }
2840
2841 ZOOM_API(void)
2842     ZOOM_scanset_option_set(ZOOM_scanset scan, const char *key,
2843                             const char *val)
2844 {
2845     ZOOM_options_set(scan->options, key, val);
2846 }
2847
2848 static Z_APDU *create_es_package(ZOOM_package p, const int *oid)
2849 {
2850     const char *str;
2851     Z_APDU *apdu = zget_APDU(p->odr_out, Z_APDU_extendedServicesRequest);
2852     Z_ExtendedServicesRequest *req = apdu->u.extendedServicesRequest;
2853     
2854     *req->function = Z_ExtendedServicesRequest_create;
2855     
2856     str = ZOOM_options_get(p->options, "package-name");
2857     if (str && *str)
2858         req->packageName = odr_strdup(p->odr_out, str);
2859     
2860     str = ZOOM_options_get(p->options, "user-id");
2861     if (str)
2862         req->userId = odr_strdup(p->odr_out, str);
2863     
2864     req->packageType = odr_oiddup(p->odr_out, oid);
2865
2866     str = ZOOM_options_get(p->options, "function");
2867     if (str)
2868     {
2869         if (!strcmp (str, "create"))
2870             *req->function = 1;
2871         if (!strcmp (str, "delete"))
2872             *req->function = 2;
2873         if (!strcmp (str, "modify"))
2874             *req->function = 3;
2875     }
2876     return apdu;
2877 }
2878
2879 static const char *ill_array_lookup(void *clientData, const char *idx)
2880 {
2881     ZOOM_package p = (ZOOM_package) clientData;
2882     return ZOOM_options_get(p->options, idx+4);
2883 }
2884
2885 static Z_External *encode_ill_request(ZOOM_package p)
2886 {
2887     ODR out = p->odr_out;
2888     ILL_Request *req;
2889     Z_External *r = 0;
2890     struct ill_get_ctl ctl;
2891         
2892     ctl.odr = p->odr_out;
2893     ctl.clientData = p;
2894     ctl.f = ill_array_lookup;
2895         
2896     req = ill_get_ILLRequest(&ctl, "ill", 0);
2897         
2898     if (!ill_Request(out, &req, 0, 0))
2899     {
2900         int ill_request_size;
2901         char *ill_request_buf = odr_getbuf(out, &ill_request_size, 0);
2902         if (ill_request_buf)
2903             odr_setbuf(out, ill_request_buf, ill_request_size, 1);
2904         return 0;
2905     }
2906     else
2907     {
2908         int illRequest_size = 0;
2909         char *illRequest_buf = odr_getbuf(out, &illRequest_size, 0);
2910                 
2911         r = (Z_External *) odr_malloc(out, sizeof(*r));
2912         r->direct_reference = odr_oiddup(out, yaz_oid_general_isoill_1);
2913         r->indirect_reference = 0;
2914         r->descriptor = 0;
2915         r->which = Z_External_single;
2916                 
2917         r->u.single_ASN1_type =
2918             odr_create_Odr_oct(out,
2919                                (unsigned char *)illRequest_buf,
2920                                illRequest_size);
2921     }
2922     return r;
2923 }
2924
2925 static Z_ItemOrder *encode_item_order(ZOOM_package p)
2926 {
2927     Z_ItemOrder *req = (Z_ItemOrder *) odr_malloc(p->odr_out, sizeof(*req));
2928     const char *str;
2929     
2930     req->which = Z_IOItemOrder_esRequest;
2931     req->u.esRequest = (Z_IORequest *) 
2932         odr_malloc(p->odr_out,sizeof(Z_IORequest));
2933
2934     /* to keep part ... */
2935     req->u.esRequest->toKeep = (Z_IOOriginPartToKeep *)
2936         odr_malloc(p->odr_out,sizeof(Z_IOOriginPartToKeep));
2937     req->u.esRequest->toKeep->supplDescription = 0;
2938     req->u.esRequest->toKeep->contact = (Z_IOContact *)
2939         odr_malloc(p->odr_out, sizeof(*req->u.esRequest->toKeep->contact));
2940         
2941     str = ZOOM_options_get(p->options, "contact-name");
2942     req->u.esRequest->toKeep->contact->name = str ?
2943         odr_strdup(p->odr_out, str) : 0;
2944         
2945     str = ZOOM_options_get(p->options, "contact-phone");
2946     req->u.esRequest->toKeep->contact->phone = str ?
2947         odr_strdup(p->odr_out, str) : 0;
2948         
2949     str = ZOOM_options_get(p->options, "contact-email");
2950     req->u.esRequest->toKeep->contact->email = str ?
2951         odr_strdup(p->odr_out, str) : 0;
2952         
2953     req->u.esRequest->toKeep->addlBilling = 0;
2954         
2955     /* not to keep part ... */
2956     req->u.esRequest->notToKeep = (Z_IOOriginPartNotToKeep *)
2957         odr_malloc(p->odr_out,sizeof(Z_IOOriginPartNotToKeep));
2958         
2959     str = ZOOM_options_get(p->options, "itemorder-setname");
2960     if (!str)
2961         str = "default";
2962
2963     if (!*str) 
2964         req->u.esRequest->notToKeep->resultSetItem = 0;
2965     else
2966     {
2967         req->u.esRequest->notToKeep->resultSetItem = (Z_IOResultSetItem *)
2968             odr_malloc(p->odr_out, sizeof(Z_IOResultSetItem));
2969
2970         req->u.esRequest->notToKeep->resultSetItem->resultSetId =
2971             odr_strdup(p->odr_out, str);
2972         req->u.esRequest->notToKeep->resultSetItem->item =
2973             (int *) odr_malloc(p->odr_out, sizeof(int));
2974         
2975         str = ZOOM_options_get(p->options, "itemorder-item");
2976         *req->u.esRequest->notToKeep->resultSetItem->item =
2977             (str ? atoi(str) : 1);
2978     }
2979
2980     str = ZOOM_options_get(p->options, "doc");
2981     if (str)
2982     {
2983         req->u.esRequest->notToKeep->itemRequest =
2984             z_ext_record_xml(p->odr_out, str, strlen(str));
2985     }
2986     else
2987         req->u.esRequest->notToKeep->itemRequest = encode_ill_request(p);
2988     
2989     return req;
2990 }
2991
2992 Z_APDU *create_admin_package(ZOOM_package p, int type, 
2993                              Z_ESAdminOriginPartToKeep **toKeepP,
2994                              Z_ESAdminOriginPartNotToKeep **notToKeepP)
2995 {
2996     Z_APDU *apdu = create_es_package(p, yaz_oid_extserv_admin);
2997     if (apdu)
2998     {
2999         Z_ESAdminOriginPartToKeep  *toKeep;
3000         Z_ESAdminOriginPartNotToKeep  *notToKeep;
3001         Z_External *r = (Z_External *) odr_malloc(p->odr_out, sizeof(*r));
3002         const char *first_db = "Default";
3003         int num_db;
3004         char **db = set_DatabaseNames(p->connection, p->options, &num_db,
3005                                       p->odr_out);
3006         if (num_db > 0)
3007             first_db = db[0];
3008             
3009         r->direct_reference = odr_oiddup(p->odr_out, yaz_oid_extserv_admin);
3010         r->descriptor = 0;
3011         r->indirect_reference = 0;
3012         r->which = Z_External_ESAdmin;
3013         
3014         r->u.adminService = (Z_Admin *)
3015             odr_malloc(p->odr_out, sizeof(*r->u.adminService));
3016         r->u.adminService->which = Z_Admin_esRequest;
3017         r->u.adminService->u.esRequest = (Z_AdminEsRequest *)
3018             odr_malloc(p->odr_out, sizeof(*r->u.adminService->u.esRequest));
3019         
3020         toKeep = r->u.adminService->u.esRequest->toKeep =
3021             (Z_ESAdminOriginPartToKeep *) 
3022             odr_malloc(p->odr_out, sizeof(*r->u.adminService->u.esRequest->toKeep));
3023         toKeep->which = type;
3024         toKeep->databaseName = odr_strdup(p->odr_out, first_db);
3025         toKeep->u.create = odr_nullval();
3026         apdu->u.extendedServicesRequest->taskSpecificParameters = r;
3027         
3028         r->u.adminService->u.esRequest->notToKeep = notToKeep =
3029             (Z_ESAdminOriginPartNotToKeep *)
3030             odr_malloc(p->odr_out,
3031                        sizeof(*r->u.adminService->u.esRequest->notToKeep));
3032         notToKeep->which = Z_ESAdminOriginPartNotToKeep_recordsWillFollow;
3033         notToKeep->u.recordsWillFollow = odr_nullval();
3034         if (toKeepP)
3035             *toKeepP = toKeep;
3036         if (notToKeepP)
3037             *notToKeepP = notToKeep;
3038     }
3039     return apdu;
3040 }
3041
3042 static Z_APDU *create_xmlupdate_package(ZOOM_package p)
3043 {
3044     Z_APDU *apdu = create_es_package(p, yaz_oid_extserv_xml_es);
3045     Z_ExtendedServicesRequest *req = apdu->u.extendedServicesRequest;
3046     Z_External *ext = (Z_External *) odr_malloc(p->odr_out, sizeof(*ext));
3047     const char *doc = ZOOM_options_get(p->options, "doc");
3048
3049     if (!doc)
3050         doc = "";
3051
3052     req->taskSpecificParameters = ext;
3053     ext->direct_reference = req->packageType;
3054     ext->descriptor = 0;
3055     ext->indirect_reference = 0;
3056     
3057     ext->which = Z_External_octet;
3058     ext->u.single_ASN1_type =
3059         odr_create_Odr_oct(p->odr_out, (const unsigned char *) doc,
3060                            strlen(doc));
3061     return apdu;
3062 }
3063
3064 static Z_APDU *create_update_package(ZOOM_package p)
3065 {
3066     Z_APDU *apdu = 0;
3067     const char *first_db = "Default";
3068     int num_db;
3069     char **db = set_DatabaseNames(p->connection, p->options, &num_db, p->odr_out);
3070     const char *action = ZOOM_options_get(p->options, "action");
3071     const char *recordIdOpaque = ZOOM_options_get(p->options, "recordIdOpaque");
3072     const char *recordIdNumber = ZOOM_options_get(p->options, "recordIdNumber");
3073     const char *record_buf = ZOOM_options_get(p->options, "record");
3074     const char *syntax_str = ZOOM_options_get(p->options, "syntax");
3075     int action_no = -1;
3076     int *syntax_oid = 0;
3077
3078     if (!syntax_str)
3079         syntax_str = "xml";
3080     if (!record_buf)
3081     {
3082         record_buf = "void";
3083         syntax_str = "SUTRS";
3084     }
3085
3086     if (syntax_str)
3087     {
3088         syntax_oid = yaz_string_to_oid_odr(yaz_oid_std(),
3089                                            CLASS_RECSYN, syntax_str,
3090                                            p->odr_out);
3091     }
3092     if (!syntax_oid)
3093         return 0;
3094
3095     if (num_db > 0)
3096         first_db = db[0];
3097     
3098     if (!action)
3099         action = "specialUpdate";
3100     
3101     if (!strcmp(action, "recordInsert"))
3102         action_no = Z_IUOriginPartToKeep_recordInsert;
3103     else if (!strcmp(action, "recordReplace"))
3104         action_no = Z_IUOriginPartToKeep_recordReplace;
3105     else if (!strcmp(action, "recordDelete"))
3106         action_no = Z_IUOriginPartToKeep_recordDelete;
3107     else if (!strcmp(action, "elementUpdate"))
3108         action_no = Z_IUOriginPartToKeep_elementUpdate;
3109     else if (!strcmp(action, "specialUpdate"))
3110         action_no = Z_IUOriginPartToKeep_specialUpdate;
3111     else
3112         return 0;
3113
3114     apdu = create_es_package(p, yaz_oid_extserv_database_update);
3115     if (apdu)
3116     {
3117         Z_IUOriginPartToKeep *toKeep;
3118         Z_IUSuppliedRecords *notToKeep;
3119         Z_External *r = (Z_External *)
3120             odr_malloc(p->odr_out, sizeof(*r));
3121         
3122         apdu->u.extendedServicesRequest->taskSpecificParameters = r;
3123
3124         
3125         r->direct_reference = odr_oiddup(p->odr_out, 
3126                                          yaz_oid_extserv_database_update);
3127         r->descriptor = 0;
3128         r->which = Z_External_update;
3129         r->indirect_reference = 0;
3130         r->u.update = (Z_IUUpdate *)
3131             odr_malloc(p->odr_out, sizeof(*r->u.update));
3132         
3133         r->u.update->which = Z_IUUpdate_esRequest;
3134         r->u.update->u.esRequest = (Z_IUUpdateEsRequest *)
3135             odr_malloc(p->odr_out, sizeof(*r->u.update->u.esRequest));
3136         toKeep = r->u.update->u.esRequest->toKeep = 
3137             (Z_IUOriginPartToKeep *)
3138             odr_malloc(p->odr_out, sizeof(*toKeep));
3139         
3140         toKeep->databaseName = odr_strdup(p->odr_out, first_db);
3141         toKeep->schema = 0;
3142         toKeep->elementSetName = 0;
3143         toKeep->actionQualifier = 0;
3144         toKeep->action = odr_intdup(p->odr_out, action_no);
3145         
3146         notToKeep = r->u.update->u.esRequest->notToKeep = 
3147             (Z_IUSuppliedRecords *)
3148             odr_malloc(p->odr_out, sizeof(*notToKeep));
3149         notToKeep->num = 1;
3150         notToKeep->elements = (Z_IUSuppliedRecords_elem **)
3151             odr_malloc(p->odr_out, sizeof(*notToKeep->elements));
3152         notToKeep->elements[0] = (Z_IUSuppliedRecords_elem *)
3153             odr_malloc(p->odr_out, sizeof(**notToKeep->elements));
3154         notToKeep->elements[0]->which = Z_IUSuppliedRecords_elem_opaque;
3155         if (recordIdOpaque)
3156         {
3157             notToKeep->elements[0]->u.opaque = 
3158                 odr_create_Odr_oct(p->odr_out,
3159                                    (const unsigned char *) recordIdOpaque,
3160                                    strlen(recordIdOpaque));
3161         }
3162         else if (recordIdNumber)
3163         {
3164             notToKeep->elements[0]->which = Z_IUSuppliedRecords_elem_number;
3165             
3166             notToKeep->elements[0]->u.number =
3167                 odr_intdup(p->odr_out, atoi(recordIdNumber));
3168         }
3169         else
3170             notToKeep->elements[0]->u.opaque = 0;
3171         notToKeep->elements[0]->supplementalId = 0;
3172         notToKeep->elements[0]->correlationInfo = 0;
3173         notToKeep->elements[0]->record =
3174             z_ext_record_oid(p->odr_out, syntax_oid,
3175                              record_buf, strlen(record_buf));
3176     }
3177     if (0 && apdu)
3178     {
3179         ODR print = odr_createmem(ODR_PRINT);
3180
3181         z_APDU(print, &apdu, 0, 0);
3182         odr_destroy(print);
3183     }
3184     return apdu;
3185 }
3186
3187 ZOOM_API(void)
3188     ZOOM_package_send(ZOOM_package p, const char *type)
3189 {
3190     Z_APDU *apdu = 0;
3191     ZOOM_connection c;
3192     if (!p)
3193         return;
3194     c = p->connection;
3195     odr_reset(p->odr_out);
3196     xfree(p->buf_out);
3197     p->buf_out = 0;
3198     if (!strcmp(type, "itemorder"))
3199     {
3200         apdu = create_es_package(p, yaz_oid_extserv_item_order);
3201         if (apdu)
3202         {
3203             Z_External *r = (Z_External *) odr_malloc(p->odr_out, sizeof(*r));
3204             
3205             r->direct_reference = 
3206                 odr_oiddup(p->odr_out, yaz_oid_extserv_item_order);
3207             r->descriptor = 0;
3208             r->which = Z_External_itemOrder;
3209             r->indirect_reference = 0;
3210             r->u.itemOrder = encode_item_order(p);
3211
3212             apdu->u.extendedServicesRequest->taskSpecificParameters = r;
3213         }
3214     }
3215     else if (!strcmp(type, "create"))  /* create database */
3216     {
3217         apdu = create_admin_package(p, Z_ESAdminOriginPartToKeep_create,
3218                                     0, 0);
3219     }   
3220     else if (!strcmp(type, "drop"))  /* drop database */
3221     {
3222         apdu = create_admin_package(p, Z_ESAdminOriginPartToKeep_drop,
3223                                     0, 0);
3224     }
3225     else if (!strcmp(type, "commit"))  /* commit changes */
3226     {
3227         apdu = create_admin_package(p, Z_ESAdminOriginPartToKeep_commit,
3228                                     0, 0);
3229     }
3230     else if (!strcmp(type, "update")) /* update record(s) */
3231     {
3232         apdu = create_update_package(p);
3233     }
3234     else if (!strcmp(type, "xmlupdate"))
3235     {
3236         apdu = create_xmlupdate_package(p);
3237     }
3238     if (apdu)
3239     {
3240         if (encode_APDU(p->connection, apdu, p->odr_out) == 0)
3241         {
3242             char *buf;
3243
3244             ZOOM_task task = ZOOM_connection_add_task(c, ZOOM_TASK_PACKAGE);
3245             task->u.package = p;
3246             buf = odr_getbuf(p->odr_out, &p->len_out, 0);
3247             p->buf_out = (char *) xmalloc(p->len_out);
3248             memcpy(p->buf_out, buf, p->len_out);
3249             
3250             (p->refcount)++;
3251             if (!c->async)
3252             {
3253                 while (ZOOM_event(1, &c))
3254                     ;
3255             }
3256         }
3257     }
3258 }
3259
3260 ZOOM_API(ZOOM_package)
3261     ZOOM_connection_package(ZOOM_connection c, ZOOM_options options)
3262 {
3263     ZOOM_package p = (ZOOM_package) xmalloc(sizeof(*p));
3264
3265     p->connection = c;
3266     p->odr_out = odr_createmem(ODR_ENCODE);
3267     p->options = ZOOM_options_create_with_parent2(options, c->options);
3268     p->refcount = 1;
3269     p->buf_out = 0;
3270     p->len_out = 0;
3271     return p;
3272 }
3273
3274 ZOOM_API(void)
3275     ZOOM_package_destroy(ZOOM_package p)
3276 {
3277     if (!p)
3278         return;
3279     (p->refcount)--;
3280     if (p->refcount == 0)
3281     {
3282         odr_destroy(p->odr_out);
3283         xfree(p->buf_out);
3284         
3285         ZOOM_options_destroy(p->options);
3286         xfree(p);
3287     }
3288 }
3289
3290 ZOOM_API(const char *)
3291     ZOOM_package_option_get(ZOOM_package p, const char *key)
3292 {
3293     return ZOOM_options_get(p->options, key);
3294 }
3295
3296
3297 ZOOM_API(void)
3298     ZOOM_package_option_set(ZOOM_package p, const char *key,
3299                             const char *val)
3300 {
3301     ZOOM_options_set(p->options, key, val);
3302 }
3303
3304 static int ZOOM_connection_exec_task(ZOOM_connection c)
3305 {
3306     ZOOM_task task = c->tasks;
3307     zoom_ret ret = zoom_complete;
3308
3309     if (!task)
3310         return 0;
3311     yaz_log(log_details, "%p ZOOM_connection_exec_task type=%d run=%d",
3312             c, task->which, task->running);
3313     if (c->error != ZOOM_ERROR_NONE)
3314     {
3315         yaz_log(log_details, "%p ZOOM_connection_exec_task "
3316                 "removing tasks because of error = %d", c, c->error);
3317         ZOOM_connection_remove_tasks(c);
3318         return 0;
3319     }
3320     if (task->running)
3321     {
3322         yaz_log(log_details, "%p ZOOM_connection_exec_task "
3323                 "task already running", c);
3324         return 0;
3325     }
3326     task->running = 1;
3327     ret = zoom_complete;
3328     if (c->cs || task->which == ZOOM_TASK_CONNECT)
3329     {
3330         switch (task->which)
3331         {
3332         case ZOOM_TASK_SEARCH:
3333             if (c->proto == PROTO_HTTP)
3334                 ret = ZOOM_connection_srw_send_search(c);
3335             else
3336                 ret = ZOOM_connection_send_search(c);
3337             break;
3338         case ZOOM_TASK_RETRIEVE:
3339             if (c->proto == PROTO_HTTP)
3340                 ret = ZOOM_connection_srw_send_search(c);
3341             else
3342                 ret = send_present(c);
3343             break;
3344         case ZOOM_TASK_CONNECT:
3345             ret = do_connect(c);
3346             break;
3347         case ZOOM_TASK_SCAN:
3348             ret = send_scan(c);
3349             break;
3350         case ZOOM_TASK_PACKAGE:
3351             ret = send_package(c);
3352             break;
3353         case ZOOM_TASK_SORT:
3354             c->tasks->u.sort.resultset->r_sort_spec = 
3355                 c->tasks->u.sort.q->sort_spec;
3356             ret = send_sort(c, c->tasks->u.sort.resultset);
3357             break;
3358         }
3359     }
3360     else
3361     {
3362         yaz_log(log_details, "%p ZOOM_connection_exec_task "
3363                 "remove tasks because no connection exist", c);
3364         ZOOM_connection_remove_tasks(c);
3365     }
3366     if (ret == zoom_complete)
3367     {
3368         yaz_log(log_details, "%p ZOOM_connection_exec_task "
3369                 "task removed (complete)", c);
3370         ZOOM_connection_remove_task(c);
3371         return 0;
3372     }
3373     yaz_log(log_details, "%p ZOOM_connection_exec_task "
3374             "task pending", c);
3375     return 1;
3376 }
3377
3378 static zoom_ret send_sort_present(ZOOM_connection c)
3379 {
3380     zoom_ret r = zoom_complete;
3381
3382     if (c->tasks && c->tasks->which == ZOOM_TASK_SEARCH)
3383         r = send_sort(c, c->tasks->u.search.resultset);
3384     if (r == zoom_complete)
3385         r = send_present(c);
3386     return r;
3387 }
3388
3389 static int es_response(ZOOM_connection c,
3390                        Z_ExtendedServicesResponse *res)
3391 {
3392     if (!c->tasks || c->tasks->which != ZOOM_TASK_PACKAGE)
3393         return 0;
3394     if (res->diagnostics && res->num_diagnostics > 0)
3395         response_diag(c, res->diagnostics[0]);
3396     if (res->taskPackage &&
3397         res->taskPackage->which == Z_External_extendedService)
3398     {
3399         Z_TaskPackage *taskPackage = res->taskPackage->u.extendedService;
3400         Odr_oct *id = taskPackage->targetReference;
3401         
3402         if (id)
3403             ZOOM_options_setl(c->tasks->u.package->options,
3404                               "targetReference", (char*) id->buf, id->len);
3405     }
3406     if (res->taskPackage && 
3407         res->taskPackage->which == Z_External_octet)
3408     {
3409         Odr_oct *doc = res->taskPackage->u.octet_aligned;
3410         ZOOM_options_setl(c->tasks->u.package->options,
3411                           "xmlUpdateDoc", (char*) doc->buf, doc->len);
3412     }
3413     return 1;
3414 }
3415
3416 static void interpret_init_diag(ZOOM_connection c,
3417                                 Z_DiagnosticFormat *diag)
3418 {
3419     if (diag->num > 0)
3420     {
3421         Z_DiagnosticFormat_s *ds = diag->elements[0];
3422         if (ds->which == Z_DiagnosticFormat_s_defaultDiagRec)
3423             response_default_diag(c, ds->u.defaultDiagRec);
3424     }
3425 }
3426
3427
3428 static void interpret_otherinformation_field(ZOOM_connection c,
3429                                              Z_OtherInformation *ui)
3430 {
3431     int i;
3432     for (i = 0; i < ui->num_elements; i++)
3433     {
3434         Z_OtherInformationUnit *unit = ui->list[i];
3435         if (unit->which == Z_OtherInfo_externallyDefinedInfo &&
3436             unit->information.externallyDefinedInfo &&
3437             unit->information.externallyDefinedInfo->which ==
3438             Z_External_diag1) 
3439         {
3440             interpret_init_diag(c, unit->information.externallyDefinedInfo->u.diag1);
3441         } 
3442     }
3443 }
3444
3445
3446 static void set_init_option(const char *name, void *clientData) {
3447     ZOOM_connection c = clientData;
3448     char buf[80];
3449
3450     sprintf(buf, "init_opt_%.70s", name);
3451     ZOOM_connection_option_set(c, buf, "1");
3452 }
3453
3454
3455 static void recv_apdu(ZOOM_connection c, Z_APDU *apdu)
3456 {
3457     Z_InitResponse *initrs;
3458     
3459     ZOOM_connection_set_mask(c, 0);
3460     yaz_log(log_details, "%p recv_apdu apdu->which=%d", c, apdu->which);
3461     switch(apdu->which)
3462     {
3463     case Z_APDU_initResponse:
3464         yaz_log(log_api, "%p recv_apd: Received Init response", c);
3465         initrs = apdu->u.initResponse;
3466         ZOOM_connection_option_set(c, "serverImplementationId",
3467                                    initrs->implementationId ?
3468                                    initrs->implementationId : "");
3469         ZOOM_connection_option_set(c, "serverImplementationName",
3470                                    initrs->implementationName ?
3471                                    initrs->implementationName : "");
3472         ZOOM_connection_option_set(c, "serverImplementationVersion",
3473                                    initrs->implementationVersion ?
3474                                    initrs->implementationVersion : "");
3475         /* Set the three old options too, for old applications */
3476         ZOOM_connection_option_set(c, "targetImplementationId",
3477                                    initrs->implementationId ?
3478                                    initrs->implementationId : "");
3479         ZOOM_connection_option_set(c, "targetImplementationName",
3480                                    initrs->implementationName ?
3481                                    initrs->implementationName : "");
3482         ZOOM_connection_option_set(c, "targetImplementationVersion",
3483                                    initrs->implementationVersion ?
3484                                    initrs->implementationVersion : "");
3485
3486         /* Make initrs->options available as ZOOM-level options */
3487         yaz_init_opt_decode(initrs->options, set_init_option, (void*) c);
3488
3489         if (!*initrs->result)
3490         {
3491             Z_External *uif = initrs->userInformationField;
3492
3493             set_ZOOM_error(c, ZOOM_ERROR_INIT, 0); /* default error */
3494
3495             if (uif && uif->which == Z_External_userInfo1)
3496                 interpret_otherinformation_field(c, uif->u.userInfo1);
3497         }
3498         else
3499         {
3500             char *cookie =
3501                 yaz_oi_get_string_oid(&apdu->u.initResponse->otherInfo,
3502                                       yaz_oid_userinfo_cookie, 1, 0);
3503             xfree(c->cookie_in);
3504             c->cookie_in = 0;
3505             if (cookie)
3506                 c->cookie_in = xstrdup(cookie);
3507             if (ODR_MASK_GET(initrs->options, Z_Options_namedResultSets) &&
3508                 ODR_MASK_GET(initrs->protocolVersion, Z_ProtocolVersion_3))
3509                 c->support_named_resultsets = 1;
3510             if (c->tasks)
3511             {
3512                 assert(c->tasks->which == ZOOM_TASK_CONNECT);
3513                 ZOOM_connection_remove_task(c);
3514             }
3515             ZOOM_connection_exec_task(c);
3516         }
3517         if (ODR_MASK_GET(initrs->options, Z_Options_negotiationModel))
3518         {
3519             NMEM tmpmem = nmem_create();
3520             Z_CharSetandLanguageNegotiation *p =
3521                 yaz_get_charneg_record(initrs->otherInfo);
3522             
3523             if (p)
3524             {
3525                 char *charset = NULL, *lang = NULL;
3526                 int sel;
3527                 
3528                 yaz_get_response_charneg(tmpmem, p, &charset, &lang, &sel);
3529                 yaz_log(log_details, "%p recv_apdu target accepted: "
3530                         "charset %s, language %s, select %d",
3531                         c,
3532                         charset ? charset : "none", lang ? lang : "none", sel);
3533                 if (charset)
3534                     ZOOM_connection_option_set(c, "negotiation-charset",
3535                                                charset);
3536                 if (lang)
3537                     ZOOM_connection_option_set(c, "negotiation-lang",
3538                                                lang);
3539
3540                 ZOOM_connection_option_set(
3541                     c,  "negotiation-charset-in-effect-for-records",
3542                     (sel != 0) ? "1" : "0");
3543                 nmem_destroy(tmpmem);
3544             }
3545         }       
3546         break;
3547     case Z_APDU_searchResponse:
3548         yaz_log(log_api, "%p recv_apdu Search response", c);
3549         handle_search_response(c, apdu->u.searchResponse);
3550         if (send_sort_present(c) == zoom_complete)
3551             ZOOM_connection_remove_task(c);
3552         break;
3553     case Z_APDU_presentResponse:
3554         yaz_log(log_api, "%p recv_apdu Present response", c);
3555         handle_present_response(c, apdu->u.presentResponse);
3556         if (send_present(c) == zoom_complete)
3557             ZOOM_connection_remove_task(c);
3558         break;
3559     case Z_APDU_sortResponse:
3560         yaz_log(log_api, "%p recv_apdu Sort response", c);
3561         sort_response(c, apdu->u.sortResponse);
3562         if (send_present(c) == zoom_complete)
3563             ZOOM_connection_remove_task(c);
3564         break;
3565     case Z_APDU_scanResponse:
3566         yaz_log(log_api, "%p recv_apdu Scan response", c);
3567         scan_response(c, apdu->u.scanResponse);
3568         ZOOM_connection_remove_task(c);
3569         break;
3570     case Z_APDU_extendedServicesResponse:
3571         yaz_log(log_api, "%p recv_apdu Extended Services response", c);
3572         es_response(c, apdu->u.extendedServicesResponse);
3573         ZOOM_connection_remove_task(c);
3574         break;
3575     case Z_APDU_close:
3576         yaz_log(log_api, "%p recv_apdu Close PDU", c);
3577         if (!ZOOM_test_reconnect(c))
3578         {
3579             set_ZOOM_error(c, ZOOM_ERROR_CONNECTION_LOST, c->host_port);
3580             do_close(c);
3581         }
3582         break;
3583     default:
3584         yaz_log(log_api, "%p Received unknown PDU", c);
3585         set_ZOOM_error(c, ZOOM_ERROR_DECODE, 0);
3586         do_close(c);
3587     }
3588 }
3589
3590 #if YAZ_HAVE_XML2
3591 static void handle_srw_response(ZOOM_connection c,
3592                                 Z_SRW_searchRetrieveResponse *res)
3593 {
3594     ZOOM_resultset resultset = 0;
3595     int i;
3596     NMEM nmem;
3597     ZOOM_Event event;
3598     int *start;
3599     const char *syntax, *elementSetName;
3600
3601     if (!c->tasks)
3602         return;
3603
3604     switch(c->tasks->which)
3605     {
3606     case ZOOM_TASK_SEARCH:
3607         resultset = c->tasks->u.search.resultset;
3608         start = &c->tasks->u.search.start;
3609         syntax = c->tasks->u.search.syntax;
3610         elementSetName = c->tasks->u.search.elementSetName;        
3611         break;
3612     case ZOOM_TASK_RETRIEVE:
3613         resultset = c->tasks->u.retrieve.resultset;
3614         start = &c->tasks->u.retrieve.start;
3615         syntax = c->tasks->u.retrieve.syntax;
3616         elementSetName = c->tasks->u.retrieve.elementSetName;
3617         break;
3618     default:
3619         return;
3620     }
3621     event = ZOOM_Event_create(ZOOM_EVENT_RECV_SEARCH);
3622     ZOOM_connection_put_event(c, event);
3623
3624     resultset->size = 0;
3625
3626     yaz_log(log_details, "%p handle_srw_response got SRW response OK", c);
3627     
3628     if (res->numberOfRecords)
3629         resultset->size = *res->numberOfRecords;
3630
3631     for (i = 0; i<res->num_records; i++)
3632     {
3633         int pos;
3634
3635         Z_NamePlusRecord *npr = (Z_NamePlusRecord *)
3636             odr_malloc(c->odr_in, sizeof(Z_NamePlusRecord));
3637
3638         if (res->records[i].recordPosition && 
3639             *res->records[i].recordPosition > 0)
3640             pos = *res->records[i].recordPosition - 1;
3641         else
3642             pos = *start + i;
3643         
3644         npr->databaseName = 0;
3645         npr->which = Z_NamePlusRecord_databaseRecord;
3646         npr->u.databaseRecord = (Z_External *)
3647             odr_malloc(c->odr_in, sizeof(Z_External));
3648         npr->u.databaseRecord->descriptor = 0;
3649         npr->u.databaseRecord->direct_reference =
3650             odr_oiddup(c->odr_in, yaz_oid_recsyn_xml);
3651         npr->u.databaseRecord->which = Z_External_octet;
3652
3653         npr->u.databaseRecord->u.octet_aligned = (Odr_oct *)
3654             odr_malloc(c->odr_in, sizeof(Odr_oct));
3655         npr->u.databaseRecord->u.octet_aligned->buf = (unsigned char*)
3656             res->records[i].recordData_buf;
3657         npr->u.databaseRecord->u.octet_aligned->len = 
3658             npr->u.databaseRecord->u.octet_aligned->size = 
3659             res->records[i].recordData_len;
3660         record_cache_add(resultset, npr, pos, syntax, elementSetName);
3661     }
3662     if (res->num_diagnostics > 0)
3663     {
3664         const char *uri = res->diagnostics[0].uri;
3665         if (uri)
3666         {
3667             int code = 0;       
3668             const char *cp;
3669             if ((cp = strrchr(uri, '/')))
3670                 code = atoi(cp+1);
3671             set_dset_error(c, code, uri,
3672                            res->diagnostics[0].details, 0);
3673         }
3674     }
3675     nmem = odr_extract_mem(c->odr_in);
3676     nmem_transfer(odr_getmem(resultset->odr), nmem);
3677     nmem_destroy(nmem);
3678 }
3679 #endif
3680
3681 #if YAZ_HAVE_XML2
3682 static void handle_http(ZOOM_connection c, Z_HTTP_Response *hres)
3683 {
3684     int ret = -1;
3685     const char *content_type = z_HTTP_header_lookup(hres->headers,
3686                                                     "Content-Type");
3687     const char *connection_head = z_HTTP_header_lookup(hres->headers,
3688                                                        "Connection");
3689     ZOOM_connection_set_mask(c, 0);
3690     yaz_log(log_details, "%p handle_http", c);
3691
3692     if (content_type && !yaz_strcmp_del("text/xml", content_type, "; "))
3693     {
3694         Z_SOAP *soap_package = 0;
3695         ODR o = c->odr_in;
3696         Z_SOAP_Handler soap_handlers[2] = {
3697             {YAZ_XMLNS_SRU_v1_1, 0, (Z_SOAP_fun) yaz_srw_codec},
3698             {0, 0, 0}
3699         };
3700         ret = z_soap_codec(o, &soap_package,
3701                            &hres->content_buf, &hres->content_len,
3702                            soap_handlers);
3703         if (!ret && soap_package->which == Z_SOAP_generic &&
3704             soap_package->u.generic->no == 0)
3705         {
3706             Z_SRW_PDU *sr = (Z_SRW_PDU*) soap_package->u.generic->p;
3707             if (sr->which == Z_SRW_searchRetrieve_response)
3708                 handle_srw_response(c, sr->u.response);
3709             else
3710                 ret = -1;
3711         }
3712         else if (!ret && (soap_package->which == Z_SOAP_fault
3713                           || soap_package->which == Z_SOAP_error))
3714         {
3715             set_HTTP_error(c, hres->code,
3716                            soap_package->u.fault->fault_code,
3717                            soap_package->u.fault->fault_string);
3718         }
3719         else
3720             ret = -1;
3721     }
3722     if (ret)
3723     {
3724         if (hres->code != 200)
3725             set_HTTP_error(c, hres->code, 0, 0);
3726         else
3727             set_ZOOM_error(c, ZOOM_ERROR_DECODE, 0);
3728         do_close(c);
3729     }
3730     ZOOM_connection_remove_task(c);
3731     if (!strcmp(hres->version, "1.0"))
3732     {
3733         /* HTTP 1.0: only if Keep-Alive we stay alive.. */
3734         if (!connection_head || strcmp(connection_head, "Keep-Alive"))
3735             do_close(c);
3736     }
3737     else 
3738     {
3739         /* HTTP 1.1: only if no close we stay alive .. */
3740         if (connection_head && !strcmp(connection_head, "close"))
3741             do_close(c);
3742     }
3743 }
3744 #endif
3745
3746 static int do_read(ZOOM_connection c)
3747 {
3748     int r, more;
3749     ZOOM_Event event;
3750     
3751     event = ZOOM_Event_create(ZOOM_EVENT_RECV_DATA);
3752     ZOOM_connection_put_event(c, event);
3753     
3754     r = cs_get(c->cs, &c->buf_in, &c->len_in);
3755     more = cs_more(c->cs);
3756     yaz_log(log_details, "%p do_read len=%d more=%d", c, r, more);
3757     if (r == 1)
3758         return 0;
3759     if (r <= 0)
3760     {
3761         if (!ZOOM_test_reconnect(c))
3762         {
3763             set_ZOOM_error(c, ZOOM_ERROR_CONNECTION_LOST, c->host_port);
3764             do_close(c);
3765         }
3766     }
3767     else
3768     {
3769         Z_GDU *gdu;
3770         ZOOM_Event event;
3771
3772         odr_reset(c->odr_in);
3773         odr_setbuf(c->odr_in, c->buf_in, r, 0);
3774         event = ZOOM_Event_create(ZOOM_EVENT_RECV_APDU);
3775         ZOOM_connection_put_event(c, event);
3776
3777         if (!z_GDU(c->odr_in, &gdu, 0, 0))
3778         {
3779             int x;
3780             int err = odr_geterrorx(c->odr_in, &x);
3781             char msg[60];
3782             const char *element = odr_getelement(c->odr_in);
3783             sprintf(msg, "ODR code %d:%d element=%-20s",
3784                     err, x, element ? element : "<unknown>");
3785             set_ZOOM_error(c, ZOOM_ERROR_DECODE, msg);
3786             do_close(c);
3787         }
3788         else if (gdu->which == Z_GDU_Z3950)
3789             recv_apdu(c, gdu->u.z3950);
3790         else if (gdu->which == Z_GDU_HTTP_Response)
3791         {
3792 #if YAZ_HAVE_XML2
3793             handle_http(c, gdu->u.HTTP_Response);
3794 #else
3795             set_ZOOM_error(c, ZOOM_ERROR_DECODE, 0);
3796             do_close(c);
3797 #endif
3798         }
3799         c->reconnect_ok = 0;
3800     }
3801     return 1;
3802 }
3803
3804 static zoom_ret do_write_ex(ZOOM_connection c, char *buf_out, int len_out)
3805 {
3806     int r;
3807     ZOOM_Event event;
3808     
3809     event = ZOOM_Event_create(ZOOM_EVENT_SEND_DATA);
3810     ZOOM_connection_put_event(c, event);
3811
3812     yaz_log(log_details, "%p do_write_ex len=%d", c, len_out);
3813     if ((r = cs_put(c->cs, buf_out, len_out)) < 0)
3814     {
3815         yaz_log(log_details, "%p do_write_ex write failed", c);
3816         if (ZOOM_test_reconnect(c))
3817         {
3818             return zoom_pending;
3819         }
3820         if (c->state == STATE_CONNECTING)
3821             set_ZOOM_error(c, ZOOM_ERROR_CONNECT, c->host_port);
3822         else
3823             set_ZOOM_error(c, ZOOM_ERROR_CONNECTION_LOST, c->host_port);
3824         do_close(c);
3825         return zoom_complete;
3826     }
3827     else if (r == 1)
3828     {    
3829         int mask = ZOOM_SELECT_EXCEPT;
3830         if (c->cs->io_pending & CS_WANT_WRITE)
3831             mask += ZOOM_SELECT_WRITE;
3832         if (c->cs->io_pending & CS_WANT_READ)
3833             mask += ZOOM_SELECT_READ;
3834         ZOOM_connection_set_mask(c, mask);
3835         yaz_log(log_details, "%p do_write_ex write incomplete mask=%d",
3836                 c, c->mask);
3837     }
3838     else
3839     {
3840         ZOOM_connection_set_mask(c, ZOOM_SELECT_READ|ZOOM_SELECT_EXCEPT);
3841         yaz_log(log_details, "%p do_write_ex write complete mask=%d",
3842                 c, c->mask);
3843     }
3844     return zoom_pending;
3845 }
3846
3847 static zoom_ret do_write(ZOOM_connection c)
3848 {
3849     return do_write_ex(c, c->buf_out, c->len_out);
3850 }
3851
3852
3853 ZOOM_API(const char *)
3854     ZOOM_connection_option_get(ZOOM_connection c, const char *key)
3855 {
3856     return ZOOM_options_get(c->options, key);
3857 }
3858
3859 ZOOM_API(const char *)
3860     ZOOM_connection_option_getl(ZOOM_connection c, const char *key, int *lenp)
3861 {
3862     return ZOOM_options_getl(c->options, key, lenp);
3863 }
3864
3865 ZOOM_API(void)
3866     ZOOM_connection_option_set(ZOOM_connection c, const char *key,
3867                                const char *val)
3868 {
3869     ZOOM_options_set(c->options, key, val);
3870 }
3871
3872 ZOOM_API(void)
3873     ZOOM_connection_option_setl(ZOOM_connection c, const char *key,
3874                                 const char *val, int len)
3875 {
3876     ZOOM_options_setl(c->options, key, val, len);
3877 }
3878
3879 ZOOM_API(const char *)
3880     ZOOM_resultset_option_get(ZOOM_resultset r, const char *key)
3881 {
3882     return ZOOM_options_get(r->options, key);
3883 }
3884
3885 ZOOM_API(void)
3886     ZOOM_resultset_option_set(ZOOM_resultset r, const char *key,
3887                               const char *val)
3888 {
3889     ZOOM_options_set(r->options, key, val);
3890 }
3891
3892
3893 ZOOM_API(int)
3894     ZOOM_connection_errcode(ZOOM_connection c)
3895 {
3896     return ZOOM_connection_error(c, 0, 0);
3897 }
3898
3899 ZOOM_API(const char *)
3900     ZOOM_connection_errmsg(ZOOM_connection c)
3901 {
3902     const char *msg;
3903     ZOOM_connection_error(c, &msg, 0);
3904     return msg;
3905 }
3906
3907 ZOOM_API(const char *)
3908     ZOOM_connection_addinfo(ZOOM_connection c)
3909 {
3910     const char *addinfo;
3911     ZOOM_connection_error(c, 0, &addinfo);
3912     return addinfo;
3913 }
3914
3915 ZOOM_API(const char *)
3916     ZOOM_connection_diagset(ZOOM_connection c)
3917 {
3918     const char *diagset;
3919     ZOOM_connection_error_x(c, 0, 0, &diagset);
3920     return diagset;
3921 }
3922
3923 ZOOM_API(const char *)
3924     ZOOM_diag_str(int error)
3925 {
3926     switch (error)
3927     {
3928     case ZOOM_ERROR_NONE:
3929         return "No error";
3930     case ZOOM_ERROR_CONNECT:
3931         return "Connect failed";
3932     case ZOOM_ERROR_MEMORY:
3933         return "Out of memory";
3934     case ZOOM_ERROR_ENCODE:
3935         return "Encoding failed";
3936     case ZOOM_ERROR_DECODE:
3937         return "Decoding failed";
3938     case ZOOM_ERROR_CONNECTION_LOST:
3939         return "Connection lost";
3940     case ZOOM_ERROR_INIT:
3941         return "Init rejected";
3942     case ZOOM_ERROR_INTERNAL:
3943         return "Internal failure";
3944     case ZOOM_ERROR_TIMEOUT:
3945         return "Timeout";
3946     case ZOOM_ERROR_UNSUPPORTED_PROTOCOL:
3947         return "Unsupported protocol";
3948     case ZOOM_ERROR_UNSUPPORTED_QUERY:
3949         return "Unsupported query type";
3950     case ZOOM_ERROR_INVALID_QUERY:
3951         return "Invalid query";
3952     case ZOOM_ERROR_CQL_PARSE:
3953         return "CQL parsing error";
3954     case ZOOM_ERROR_CQL_TRANSFORM:
3955         return "CQL transformation error";
3956     case ZOOM_ERROR_CCL_CONFIG:
3957         return "CCL configuration error";
3958     case ZOOM_ERROR_CCL_PARSE:
3959         return "CCL parsing error";
3960     default:
3961         return diagbib1_str(error);
3962     }
3963 }
3964
3965 ZOOM_API(int)
3966     ZOOM_connection_error_x(ZOOM_connection c, const char **cp,
3967                             const char **addinfo, const char **diagset)
3968 {
3969     int error = c->error;
3970     if (cp)
3971     {
3972         if (!c->diagset || !strcmp(c->diagset, "ZOOM"))
3973             *cp = ZOOM_diag_str(error);
3974         else if (!strcmp(c->diagset, "HTTP"))
3975             *cp = z_HTTP_errmsg(c->error);
3976         else if (!strcmp(c->diagset, "Bib-1"))
3977             *cp = ZOOM_diag_str(error);
3978         else if (!strcmp(c->diagset, "info:srw/diagnostic/1"))
3979             *cp = yaz_diag_srw_str(c->error);
3980         else
3981             *cp = "Unknown error and diagnostic set";
3982     }
3983     if (addinfo)
3984         *addinfo = c->addinfo ? c->addinfo : "";
3985     if (diagset)
3986         *diagset = c->diagset ? c->diagset : "";
3987     return c->error;
3988 }
3989
3990 ZOOM_API(int)
3991     ZOOM_connection_error(ZOOM_connection c, const char **cp,
3992                           const char **addinfo)
3993 {
3994     return ZOOM_connection_error_x(c, cp, addinfo, 0);
3995 }
3996
3997 static void ZOOM_connection_do_io(ZOOM_connection c, int mask)
3998 {
3999     ZOOM_Event event = 0;
4000     int r = cs_look(c->cs);
4001     yaz_log(log_details, "%p ZOOM_connection_do_io mask=%d cs_look=%d",
4002             c, mask, r);
4003     
4004     if (r == CS_NONE)
4005     {
4006         event = ZOOM_Event_create(ZOOM_EVENT_CONNECT);
4007         set_ZOOM_error(c, ZOOM_ERROR_CONNECT, c->host_port);
4008         do_close(c);
4009         ZOOM_connection_put_event(c, event);
4010     }
4011     else if (r == CS_CONNECT)
4012     {
4013         int ret = ret = cs_rcvconnect(c->cs);
4014         yaz_log(log_details, "%p ZOOM_connection_do_io "
4015                 "cs_rcvconnect returned %d", c, ret);
4016         if (ret == 1)
4017         {
4018             int mask = ZOOM_SELECT_EXCEPT;
4019             if (c->cs->io_pending & CS_WANT_WRITE)
4020                 mask += ZOOM_SELECT_WRITE;
4021             if (c->cs->io_pending & CS_WANT_READ)
4022                 mask += ZOOM_SELECT_READ;
4023             ZOOM_connection_set_mask(c, mask);
4024         }
4025         else if (ret == 0)
4026         {
4027             event = ZOOM_Event_create(ZOOM_EVENT_CONNECT);
4028             ZOOM_connection_put_event(c, event);
4029             get_cert(c);
4030             if (c->proto == PROTO_Z3950)
4031                 ZOOM_connection_send_init(c);
4032             else
4033             {
4034                 /* no init request for SRW .. */
4035                 assert(c->tasks->which == ZOOM_TASK_CONNECT);
4036                 ZOOM_connection_remove_task(c);
4037                 ZOOM_connection_set_mask(c, 0);
4038                 ZOOM_connection_exec_task(c);
4039             }
4040             c->state = STATE_ESTABLISHED;
4041         }
4042         else
4043         {
4044             set_ZOOM_error(c, ZOOM_ERROR_CONNECT, c->host_port);
4045             do_close(c);
4046         }
4047     }
4048     else
4049     {
4050         if (mask & ZOOM_SELECT_EXCEPT)
4051         {
4052             if (!ZOOM_test_reconnect(c))
4053             {
4054                 set_ZOOM_error(c, ZOOM_ERROR_CONNECTION_LOST, c->host_port);
4055                 do_close(c);
4056             }
4057             return;
4058         }
4059         if (mask & ZOOM_SELECT_READ)
4060             do_read(c);
4061         if (c->cs && (mask & ZOOM_SELECT_WRITE))
4062             do_write(c);
4063     }
4064 }
4065
4066 ZOOM_API(int)
4067     ZOOM_connection_last_event(ZOOM_connection cs)
4068 {
4069     if (!cs)
4070         return ZOOM_EVENT_NONE;
4071     return cs->last_event;
4072 }
4073
4074
4075 static void cql2pqf_wrbuf_puts(const char *buf, void *client_data)
4076 {
4077     WRBUF wrbuf = client_data;
4078     wrbuf_puts(wrbuf, buf);
4079 }
4080
4081 /*
4082  * Returns an xmalloc()d string containing RPN that corresponds to the
4083  * CQL passed in.  On error, sets the Connection object's error state
4084  * and returns a null pointer.
4085  * ### We could cache CQL parser and/or transformer in Connection.
4086  */
4087 static char *cql2pqf(ZOOM_connection c, const char *cql)
4088 {
4089     CQL_parser parser;
4090     int error;
4091     const char *cqlfile;
4092     cql_transform_t trans;
4093     char *result = 0;
4094
4095     parser = cql_parser_create();
4096     if ((error = cql_parser_string(parser, cql)) != 0) {
4097         cql_parser_destroy(parser);
4098         set_ZOOM_error(c, ZOOM_ERROR_CQL_PARSE, cql);
4099         return 0;
4100     }
4101
4102     cqlfile = ZOOM_connection_option_get(c, "cqlfile");
4103     if (cqlfile == 0) 
4104     {
4105         set_ZOOM_error(c, ZOOM_ERROR_CQL_TRANSFORM, "no CQL transform file");
4106     }
4107     else if ((trans = cql_transform_open_fname(cqlfile)) == 0) 
4108     {
4109         char buf[512];        
4110         sprintf(buf, "can't open CQL transform file '%.200s': %.200s",
4111                 cqlfile, strerror(errno));
4112         set_ZOOM_error(c, ZOOM_ERROR_CQL_TRANSFORM, buf);
4113     }
4114     else 
4115     {
4116         WRBUF wrbuf_result = wrbuf_alloc();
4117         error = cql_transform(trans, cql_parser_result(parser),
4118                               cql2pqf_wrbuf_puts, wrbuf_result);
4119         if (error != 0) {
4120             char buf[512];
4121             const char *addinfo;
4122             error = cql_transform_error(trans, &addinfo);
4123             sprintf(buf, "%.200s (addinfo=%.200s)", 
4124                     cql_strerror(error), addinfo);
4125             set_ZOOM_error(c, ZOOM_ERROR_CQL_TRANSFORM, buf);
4126         }
4127         else
4128         {
4129             result = xstrdup(wrbuf_cstr(wrbuf_result));
4130         }
4131         cql_transform_close(trans);
4132         wrbuf_destroy(wrbuf_result);
4133     }
4134     cql_parser_destroy(parser);
4135     return result;
4136 }
4137
4138 ZOOM_API(int) ZOOM_connection_fire_event_timeout(ZOOM_connection c)
4139 {
4140     if (c->mask)
4141     {
4142         ZOOM_Event event = ZOOM_Event_create(ZOOM_EVENT_TIMEOUT);
4143         /* timeout and this connection was waiting */
4144         set_ZOOM_error(c, ZOOM_ERROR_TIMEOUT, 0);
4145         do_close(c);
4146         ZOOM_connection_put_event(c, event);
4147     }
4148     return 0;
4149 }
4150
4151 ZOOM_API(int)
4152     ZOOM_connection_process(ZOOM_connection c)
4153 {
4154     ZOOM_Event event;
4155     if (!c)
4156         return 0;
4157
4158     event = ZOOM_connection_get_event(c);
4159     if (event)
4160     {
4161         ZOOM_Event_destroy(event);
4162         return 1;
4163     }
4164     ZOOM_connection_exec_task(c);
4165     event = ZOOM_connection_get_event(c);
4166     if (event)
4167     {
4168         ZOOM_Event_destroy(event);
4169         return 1;
4170     }
4171     return 0;
4172 }
4173
4174 ZOOM_API(int)
4175     ZOOM_event_nonblock(int no, ZOOM_connection *cs)
4176 {
4177     int i;
4178
4179     yaz_log(log_details, "ZOOM_process_event(no=%d,cs=%p)", no, cs);
4180     
4181     for (i = 0; i<no; i++)
4182     {
4183         ZOOM_connection c = cs[i];
4184
4185         if (c && ZOOM_connection_process(c))
4186             return i+1;
4187     }
4188     return 0;
4189 }
4190
4191 ZOOM_API(int) ZOOM_connection_fire_event_socket(ZOOM_connection c, int mask)
4192 {
4193     if (c->mask && mask)
4194         ZOOM_connection_do_io(c, mask);
4195     return 0;
4196 }
4197
4198 ZOOM_API(int) ZOOM_connection_get_socket(ZOOM_connection c)
4199 {
4200     if (c->cs)
4201         return cs_fileno(c->cs);
4202     return -1;
4203 }
4204
4205 ZOOM_API(int) ZOOM_connection_set_mask(ZOOM_connection c, int mask)
4206 {
4207     c->mask = mask;
4208     if (!c->cs)
4209         return -1; 
4210     return 0;
4211 }
4212
4213 ZOOM_API(int) ZOOM_connection_get_mask(ZOOM_connection c)
4214 {
4215     if (c->cs)
4216         return c->mask;
4217     return 0;
4218 }
4219
4220 ZOOM_API(int) ZOOM_connection_get_timeout(ZOOM_connection c)
4221 {
4222     return ZOOM_options_get_int(c->options, "timeout", 30);
4223 }
4224
4225 /*
4226  * Local variables:
4227  * c-basic-offset: 4
4228  * indent-tabs-mode: nil
4229  * End:
4230  * vim: shiftwidth=4 tabstop=8 expandtab
4231  */
4232