Added utilities z_ext_record_{xml,sutrs,usmarc}.
[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.124 2007-04-13 09:55:41 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         const int *oid = yaz_string_to_oid(
1211             yaz_oid_std(), CLASS_USERINFO, OID_STR_COOKIE);
1212         Z_OtherInformation **oi;
1213         yaz_oi_APDU(a, &oi);
1214         yaz_oi_set_string_oid(oi, out, oid, 1, c->cookie_out);
1215     }
1216     if (c->client_IP)
1217     {
1218         const int *oid = yaz_string_to_oid(
1219             yaz_oid_std(), CLASS_USERINFO, OID_STR_CLIENT_IP);
1220         Z_OtherInformation **oi;
1221         yaz_oi_APDU(a, &oi);
1222         yaz_oi_set_string_oid(oi, out, oid, 1, c->client_IP);
1223     }
1224     otherInfo_attach(c, a, out);
1225     if (!z_APDU(out, &a, 0, 0))
1226     {
1227         FILE *outf = fopen("/tmp/apdu.txt", "a");
1228         if (a && outf)
1229         {
1230             ODR odr_pr = odr_createmem(ODR_PRINT);
1231             fprintf(outf, "a=%p\n", a);
1232             odr_setprint(odr_pr, outf);
1233             z_APDU(odr_pr, &a, 0, 0);
1234             odr_destroy(odr_pr);
1235         }
1236         yaz_log(log_api, "%p encoding_APDU: encoding failed", c);
1237         set_ZOOM_error(c, ZOOM_ERROR_ENCODE, 0);
1238         odr_reset(out);
1239         return -1;
1240     }
1241     yaz_log(log_details, "%p encoding_APDU encoding OK", c);
1242     return 0;
1243 }
1244
1245 static zoom_ret send_APDU(ZOOM_connection c, Z_APDU *a)
1246 {
1247     ZOOM_Event event;
1248     assert(a);
1249     if (encode_APDU(c, a, c->odr_out))
1250         return zoom_complete;
1251     yaz_log(log_details, "%p send APDU type=%d", c, a->which);
1252     c->buf_out = odr_getbuf(c->odr_out, &c->len_out, 0);
1253     event = ZOOM_Event_create(ZOOM_EVENT_SEND_APDU);
1254     ZOOM_connection_put_event(c, event);
1255     odr_reset(c->odr_out);
1256     return do_write(c);
1257 }
1258
1259 /* returns 1 if PDU was sent OK (still pending )
1260    0 if PDU was not sent OK (nothing to wait for) 
1261 */
1262
1263 static zoom_ret ZOOM_connection_send_init(ZOOM_connection c)
1264 {
1265     Z_APDU *apdu = zget_APDU(c->odr_out, Z_APDU_initRequest);
1266     Z_InitRequest *ireq = apdu->u.initRequest;
1267     Z_IdAuthentication *auth = (Z_IdAuthentication *)
1268         odr_malloc(c->odr_out, sizeof(*auth));
1269     const char *auth_groupId = ZOOM_options_get(c->options, "group");
1270     const char *auth_userId = ZOOM_options_get(c->options, "user");
1271     const char *auth_password = ZOOM_options_get(c->options, "password");
1272     char *version;
1273
1274     /* support the pass for backwards compatibility */
1275     if (!auth_password)
1276         auth_password = ZOOM_options_get(c->options, "pass");
1277         
1278     ODR_MASK_SET(ireq->options, Z_Options_search);
1279     ODR_MASK_SET(ireq->options, Z_Options_present);
1280     ODR_MASK_SET(ireq->options, Z_Options_scan);
1281     ODR_MASK_SET(ireq->options, Z_Options_sort);
1282     ODR_MASK_SET(ireq->options, Z_Options_extendedServices);
1283     ODR_MASK_SET(ireq->options, Z_Options_namedResultSets);
1284     
1285     ODR_MASK_SET(ireq->protocolVersion, Z_ProtocolVersion_1);
1286     ODR_MASK_SET(ireq->protocolVersion, Z_ProtocolVersion_2);
1287     ODR_MASK_SET(ireq->protocolVersion, Z_ProtocolVersion_3);
1288     
1289     /* Index Data's Z39.50 Implementor Id is 81 */
1290     ireq->implementationId =
1291         odr_prepend(c->odr_out,
1292                     ZOOM_options_get(c->options, "implementationId"),
1293                     odr_prepend(c->odr_out, "81", ireq->implementationId));
1294     
1295     ireq->implementationName = 
1296         odr_prepend(c->odr_out,
1297                     ZOOM_options_get(c->options, "implementationName"),
1298                     odr_prepend(c->odr_out, "ZOOM-C",
1299                                 ireq->implementationName));
1300     
1301     version = odr_strdup(c->odr_out, "$Revision: 1.124 $");
1302     if (strlen(version) > 10)   /* check for unexpanded CVS strings */
1303         version[strlen(version)-2] = '\0';
1304     ireq->implementationVersion = 
1305         odr_prepend(c->odr_out,
1306                     ZOOM_options_get(c->options, "implementationVersion"),
1307                     odr_prepend(c->odr_out, &version[11],
1308                                 ireq->implementationVersion));
1309     
1310     *ireq->maximumRecordSize =
1311         ZOOM_options_get_int(c->options, "maximumRecordSize", 1024*1024);
1312     *ireq->preferredMessageSize =
1313         ZOOM_options_get_int(c->options, "preferredMessageSize", 1024*1024);
1314     
1315     if (auth_groupId || auth_password)
1316     {
1317         Z_IdPass *pass = (Z_IdPass *) odr_malloc(c->odr_out, sizeof(*pass));
1318         int i = 0;
1319         pass->groupId = 0;
1320         if (auth_groupId && *auth_groupId)
1321         {
1322             pass->groupId = (char *)
1323                 odr_malloc(c->odr_out, strlen(auth_groupId)+1);
1324             strcpy(pass->groupId, auth_groupId);
1325             i++;
1326         }
1327         pass->userId = 0;
1328         if (auth_userId && *auth_userId)
1329         {
1330             pass->userId = (char *)
1331                 odr_malloc(c->odr_out, strlen(auth_userId)+1);
1332             strcpy(pass->userId, auth_userId);
1333             i++;
1334         }
1335         pass->password = 0;
1336         if (auth_password && *auth_password)
1337         {
1338             pass->password = (char *)
1339                 odr_malloc(c->odr_out, strlen(auth_password)+1);
1340             strcpy(pass->password, auth_password);
1341             i++;
1342         }
1343         if (i)
1344         {
1345             auth->which = Z_IdAuthentication_idPass;
1346             auth->u.idPass = pass;
1347             ireq->idAuthentication = auth;
1348         }
1349     }
1350     else if (auth_userId)
1351     {
1352         auth->which = Z_IdAuthentication_open;
1353         auth->u.open = (char *)
1354             odr_malloc(c->odr_out, strlen(auth_userId)+1);
1355         strcpy(auth->u.open, auth_userId);
1356         ireq->idAuthentication = auth;
1357     }
1358     if (c->proxy)
1359     {
1360         const int *oid = yaz_string_to_oid(
1361             yaz_oid_std(), CLASS_USERINFO, OID_STR_CLIENT_IP);
1362         yaz_oi_set_string_oid(&ireq->otherInfo, c->odr_out,
1363                               oid, 1, c->host_port);
1364     }
1365     if (c->charset || c->lang)
1366     {
1367         Z_OtherInformation **oi;
1368         Z_OtherInformationUnit *oi_unit;
1369         
1370         yaz_oi_APDU(apdu, &oi);
1371         
1372         if ((oi_unit = yaz_oi_update(oi, c->odr_out, NULL, 0, 0)))
1373         {
1374             ODR_MASK_SET(ireq->options, Z_Options_negotiationModel);
1375             oi_unit->which = Z_OtherInfo_externallyDefinedInfo;
1376             oi_unit->information.externallyDefinedInfo =
1377                 yaz_set_proposal_charneg_list(c->odr_out, " ",
1378                                               c->charset, c->lang, 1);
1379         }
1380     }
1381     assert(apdu);
1382     return send_APDU(c, apdu);
1383 }
1384
1385 #if YAZ_HAVE_XML2
1386 static zoom_ret send_srw(ZOOM_connection c, Z_SRW_PDU *sr)
1387 {
1388     Z_GDU *gdu;
1389     ZOOM_Event event;
1390
1391     gdu = z_get_HTTP_Request_host_path(c->odr_out, c->host_port, c->path);
1392
1393     if (c->sru_mode == zoom_sru_get)
1394     {
1395         yaz_sru_get_encode(gdu->u.HTTP_Request, sr, c->odr_out, c->charset);
1396     }
1397     else if (c->sru_mode == zoom_sru_post)
1398     {
1399         yaz_sru_post_encode(gdu->u.HTTP_Request, sr, c->odr_out, c->charset);
1400     }
1401     else if (c->sru_mode == zoom_sru_soap)
1402     {
1403         yaz_sru_soap_encode(gdu->u.HTTP_Request, sr, c->odr_out, c->charset);
1404     }
1405     if (!z_GDU(c->odr_out, &gdu, 0, 0))
1406         return zoom_complete;
1407     c->buf_out = odr_getbuf(c->odr_out, &c->len_out, 0);
1408         
1409     event = ZOOM_Event_create(ZOOM_EVENT_SEND_APDU);
1410     ZOOM_connection_put_event(c, event);
1411     odr_reset(c->odr_out);
1412     return do_write(c);
1413 }
1414 #endif
1415
1416 #if YAZ_HAVE_XML2
1417 static zoom_ret ZOOM_connection_srw_send_search(ZOOM_connection c)
1418 {
1419     int i;
1420     int *start, *count;
1421     ZOOM_resultset resultset = 0;
1422     Z_SRW_PDU *sr = 0;
1423     const char *option_val = 0;
1424
1425     if (c->error)                  /* don't continue on error */
1426         return zoom_complete;
1427     assert(c->tasks);
1428     switch(c->tasks->which)
1429     {
1430     case ZOOM_TASK_SEARCH:
1431         resultset = c->tasks->u.search.resultset;
1432         resultset->setname = xstrdup("default");
1433         ZOOM_options_set(resultset->options, "setname", resultset->setname);
1434         start = &c->tasks->u.search.start;
1435         count = &c->tasks->u.search.count;
1436         break;
1437     case ZOOM_TASK_RETRIEVE:
1438         resultset = c->tasks->u.retrieve.resultset;
1439
1440         start = &c->tasks->u.retrieve.start;
1441         count = &c->tasks->u.retrieve.count;
1442         
1443         if (*start >= resultset->size)
1444             return zoom_complete;
1445         if (*start + *count > resultset->size)
1446             *count = resultset->size - *start;
1447
1448         for (i = 0; i < *count; i++)
1449         {
1450             ZOOM_record rec =
1451                 record_cache_lookup(resultset, i + *start,
1452                                     c->tasks->u.retrieve.syntax,
1453                                     c->tasks->u.retrieve.elementSetName);
1454             if (!rec)
1455                 break;
1456             else
1457             {
1458                 ZOOM_Event event = ZOOM_Event_create(ZOOM_EVENT_RECV_RECORD);
1459                 ZOOM_connection_put_event(c, event);
1460             }
1461         }
1462         *start += i;
1463         *count -= i;
1464
1465         if (*count == 0)
1466             return zoom_complete;
1467         break;
1468     default:
1469         return zoom_complete;
1470     }
1471     assert(resultset->query);
1472         
1473     sr = yaz_srw_get(c->odr_out, Z_SRW_searchRetrieve_request);
1474
1475     if (resultset->query->z_query->which == Z_Query_type_104
1476         && resultset->query->z_query->u.type_104->which == Z_External_CQL)
1477     {
1478         sr->u.request->query_type = Z_SRW_query_type_cql;
1479         sr->u.request->query.cql =resultset->query->z_query->u.type_104->u.cql;
1480     }
1481     else if (resultset->query->z_query->which == Z_Query_type_1 &&
1482              resultset->query->z_query->u.type_1)
1483     {
1484         sr->u.request->query_type = Z_SRW_query_type_pqf;
1485         sr->u.request->query.pqf = resultset->query->query_string;
1486     }
1487     else
1488     {
1489         set_ZOOM_error(c, ZOOM_ERROR_UNSUPPORTED_QUERY, 0);
1490         return zoom_complete;
1491     }
1492     sr->u.request->startRecord = odr_intdup(c->odr_out, *start + 1);
1493     sr->u.request->maximumRecords = odr_intdup(
1494         c->odr_out, resultset->step>0 ? resultset->step : *count);
1495     sr->u.request->recordSchema = resultset->schema;
1496     
1497     option_val = ZOOM_resultset_option_get(resultset, "recordPacking");
1498     if (option_val)
1499         sr->u.request->recordPacking = odr_strdup(c->odr_out, option_val);
1500
1501     option_val = ZOOM_resultset_option_get(resultset, "extraArgs");
1502     if (option_val)
1503         sr->extra_args = odr_strdup(c->odr_out, option_val);
1504     return send_srw(c, sr);
1505 }
1506 #else
1507 static zoom_ret ZOOM_connection_srw_send_search(ZOOM_connection c)
1508 {
1509     return zoom_complete;
1510 }
1511 #endif
1512
1513 static zoom_ret ZOOM_connection_send_search(ZOOM_connection c)
1514 {
1515     ZOOM_resultset r;
1516     int lslb, ssub, mspn;
1517     const char *syntax;
1518     Z_APDU *apdu = zget_APDU(c->odr_out, Z_APDU_searchRequest);
1519     Z_SearchRequest *search_req = apdu->u.searchRequest;
1520     const char *elementSetName;
1521     const char *smallSetElementSetName;
1522     const char *mediumSetElementSetName;
1523
1524     assert(c->tasks);
1525     assert(c->tasks->which == ZOOM_TASK_SEARCH);
1526
1527     r = c->tasks->u.search.resultset;
1528
1529     yaz_log(log_details, "%p ZOOM_connection_send_search set=%p", c, r);
1530
1531     elementSetName =
1532         ZOOM_options_get(r->options, "elementSetName");
1533     smallSetElementSetName  =
1534         ZOOM_options_get(r->options, "smallSetElementSetName");
1535     mediumSetElementSetName =
1536         ZOOM_options_get(r->options, "mediumSetElementSetName");
1537
1538     if (!smallSetElementSetName)
1539         smallSetElementSetName = elementSetName;
1540
1541     if (!mediumSetElementSetName)
1542         mediumSetElementSetName = elementSetName;
1543
1544     assert(r);
1545     assert(r->query);
1546
1547     /* prepare query for the search request */
1548     search_req->query = r->query->z_query;
1549     if (!search_req->query)
1550     {
1551         set_ZOOM_error(c, ZOOM_ERROR_INVALID_QUERY, 0);
1552         return zoom_complete;
1553     }
1554
1555     search_req->databaseNames = r->databaseNames;
1556     search_req->num_databaseNames = r->num_databaseNames;
1557
1558     /* get syntax (no need to provide unless piggyback is in effect) */
1559     syntax = c->tasks->u.search.syntax;
1560
1561     lslb = ZOOM_options_get_int(r->options, "largeSetLowerBound", -1);
1562     ssub = ZOOM_options_get_int(r->options, "smallSetUpperBound", -1);
1563     mspn = ZOOM_options_get_int(r->options, "mediumSetPresentNumber", -1);
1564     if (lslb != -1 && ssub != -1 && mspn != -1)
1565     {
1566         /* So're a Z39.50 expert? Let's hope you don't do sort */
1567         *search_req->largeSetLowerBound = lslb;
1568         *search_req->smallSetUpperBound = ssub;
1569         *search_req->mediumSetPresentNumber = mspn;
1570     }
1571     else if (c->tasks->u.search.start == 0 && c->tasks->u.search.count > 0
1572              && r->piggyback && !r->r_sort_spec && !r->schema)
1573     {
1574         /* Regular piggyback - do it unless we're going to do sort */
1575         *search_req->largeSetLowerBound = 2000000000;
1576         *search_req->smallSetUpperBound = 1;
1577         *search_req->mediumSetPresentNumber = 
1578             r->step>0 ? r->step : c->tasks->u.search.count;
1579     }
1580     else
1581     {
1582         /* non-piggyback. Need not provide elementsets or syntaxes .. */
1583         smallSetElementSetName = 0;
1584         mediumSetElementSetName = 0;
1585         syntax = 0;
1586     }
1587     if (smallSetElementSetName && *smallSetElementSetName)
1588     {
1589         Z_ElementSetNames *esn = (Z_ElementSetNames *)
1590             odr_malloc(c->odr_out, sizeof(*esn));
1591         
1592         esn->which = Z_ElementSetNames_generic;
1593         esn->u.generic = odr_strdup(c->odr_out, smallSetElementSetName);
1594         search_req->smallSetElementSetNames = esn;
1595     }
1596     if (mediumSetElementSetName && *mediumSetElementSetName)
1597     {
1598         Z_ElementSetNames *esn =(Z_ElementSetNames *)
1599             odr_malloc(c->odr_out, sizeof(*esn));
1600         
1601         esn->which = Z_ElementSetNames_generic;
1602         esn->u.generic = odr_strdup(c->odr_out, mediumSetElementSetName);
1603         search_req->mediumSetElementSetNames = esn;
1604     }
1605     if (syntax)
1606         search_req->preferredRecordSyntax =
1607             zoom_yaz_str_to_z3950oid(c, CLASS_RECSYN, syntax);
1608     
1609     if (!r->setname)
1610     {
1611         if (c->support_named_resultsets)
1612         {
1613             char setname[14];
1614             int ord;
1615             /* find the lowest unused ordinal so that we re-use
1616                result sets on the server. */
1617             for (ord = 1; ; ord++)
1618             {
1619                 ZOOM_resultset rp;
1620                 sprintf(setname, "%d", ord);
1621                 for (rp = c->resultsets; rp; rp = rp->next)
1622                     if (rp->setname && !strcmp(rp->setname, setname))
1623                         break;
1624                 if (!rp)
1625                     break;
1626             }
1627             r->setname = xstrdup(setname);
1628             yaz_log(log_details, "%p ZOOM_connection_send_search: allocating "
1629                     "set %s", c, r->setname);
1630         }
1631         else
1632         {
1633             yaz_log(log_details, "%p ZOOM_connection_send_search: using "
1634                     "default set", c);
1635             r->setname = xstrdup("default");
1636         }
1637         ZOOM_options_set(r->options, "setname", r->setname);
1638     }
1639     search_req->resultSetName = odr_strdup(c->odr_out, r->setname);
1640     return send_APDU(c, apdu);
1641 }
1642
1643 static void response_default_diag(ZOOM_connection c, Z_DefaultDiagFormat *r)
1644 {
1645     int oclass;
1646     char *addinfo = 0;
1647
1648     switch (r->which)
1649     {
1650     case Z_DefaultDiagFormat_v2Addinfo:
1651         addinfo = r->u.v2Addinfo;
1652         break;
1653     case Z_DefaultDiagFormat_v3Addinfo:
1654         addinfo = r->u.v3Addinfo;
1655         break;
1656     }
1657     xfree(c->addinfo);
1658     c->addinfo = 0;
1659     set_dset_error(c, *r->condition,
1660                    yaz_oid_to_string(yaz_oid_std(), 
1661                                      r->diagnosticSetId, &oclass),
1662                    addinfo, 0);
1663 }
1664
1665 static void response_diag(ZOOM_connection c, Z_DiagRec *p)
1666 {
1667     if (p->which != Z_DiagRec_defaultFormat)
1668         set_ZOOM_error(c, ZOOM_ERROR_DECODE, 0);
1669     else
1670         response_default_diag(c, p->u.defaultFormat);
1671 }
1672
1673 ZOOM_API(ZOOM_record)
1674     ZOOM_record_clone(ZOOM_record srec)
1675 {
1676     char *buf;
1677     int size;
1678     ODR odr_enc;
1679     ZOOM_record nrec;
1680
1681     odr_enc = odr_createmem(ODR_ENCODE);
1682     if (!z_NamePlusRecord(odr_enc, &srec->npr, 0, 0))
1683         return 0;
1684     buf = odr_getbuf(odr_enc, &size, 0);
1685     
1686     nrec = (ZOOM_record) xmalloc(sizeof(*nrec));
1687     nrec->odr = odr_createmem(ODR_DECODE);
1688     nrec->wrbuf_marc = 0;
1689     nrec->wrbuf_iconv = 0;
1690     nrec->wrbuf_opac = 0;
1691     odr_setbuf(nrec->odr, buf, size, 0);
1692     z_NamePlusRecord(nrec->odr, &nrec->npr, 0, 0);
1693     
1694     odr_destroy(odr_enc);
1695     return nrec;
1696 }
1697
1698 ZOOM_API(ZOOM_record)
1699     ZOOM_resultset_record_immediate(ZOOM_resultset s,size_t pos)
1700 {
1701     const char *syntax =
1702         ZOOM_options_get(s->options, "preferredRecordSyntax"); 
1703     const char *elementSetName =
1704         ZOOM_options_get(s->options, "elementSetName");
1705
1706     return record_cache_lookup(s, pos, syntax, elementSetName);
1707 }
1708
1709 ZOOM_API(ZOOM_record)
1710     ZOOM_resultset_record(ZOOM_resultset r, size_t pos)
1711 {
1712     ZOOM_record rec = ZOOM_resultset_record_immediate(r, pos);
1713
1714     if (!rec)
1715     {
1716         /*
1717          * MIKE: I think force_sync should always be zero, but I don't
1718          * want to make this change until I get the go-ahead from
1719          * Adam, in case something depends on the old synchronous
1720          * behaviour.
1721          */
1722         int force_sync = 1;
1723         if (getenv("ZOOM_RECORD_NO_FORCE_SYNC")) force_sync = 0;
1724         ZOOM_resultset_retrieve(r, force_sync, pos, 1);
1725         rec = ZOOM_resultset_record_immediate(r, pos);
1726     }
1727     return rec;
1728 }
1729
1730 ZOOM_API(void)
1731     ZOOM_record_destroy(ZOOM_record rec)
1732 {
1733     if (!rec)
1734         return;
1735     if (rec->wrbuf_marc)
1736         wrbuf_destroy(rec->wrbuf_marc);
1737     if (rec->wrbuf_iconv)
1738         wrbuf_destroy(rec->wrbuf_iconv);
1739     if (rec->wrbuf_opac)
1740         wrbuf_destroy(rec->wrbuf_opac);
1741     odr_destroy(rec->odr);
1742     xfree(rec);
1743 }
1744
1745 static const char *marc_iconv_return(ZOOM_record rec, int marc_type,
1746                                      int *len,
1747                                      const char *buf, int sz,
1748                                      const char *record_charset)
1749 {
1750     char to[40];
1751     char from[40];
1752     yaz_iconv_t cd = 0;
1753     yaz_marc_t mt = yaz_marc_create();
1754
1755     *from = '\0';
1756     strcpy(to, "UTF-8");
1757     if (record_charset && *record_charset)
1758     {
1759         /* Use "from,to" or just "from" */
1760         const char *cp = strchr(record_charset, ',');
1761         int clen = strlen(record_charset);
1762         if (cp && cp[1])
1763         {
1764             strncpy( to, cp+1, sizeof(to)-1);
1765             to[sizeof(to)-1] = '\0';
1766             clen = cp - record_charset;
1767         }
1768         if (clen > sizeof(from)-1)
1769             clen = sizeof(from)-1;
1770         
1771         if (clen)
1772             strncpy(from, record_charset, clen);
1773         from[clen] = '\0';
1774     }
1775
1776     if (*from && *to)
1777     {
1778         cd = yaz_iconv_open(to, from);
1779         yaz_marc_iconv(mt, cd);
1780     }
1781
1782     yaz_marc_xml(mt, marc_type);
1783     if (!rec->wrbuf_marc)
1784         rec->wrbuf_marc = wrbuf_alloc();
1785     wrbuf_rewind(rec->wrbuf_marc);
1786     if (yaz_marc_decode_wrbuf(mt, buf, sz, rec->wrbuf_marc) > 0)
1787     {
1788         yaz_marc_destroy(mt);
1789         if (cd)
1790             yaz_iconv_close(cd);
1791         if (len)
1792             *len = wrbuf_len(rec->wrbuf_marc);
1793         return wrbuf_cstr(rec->wrbuf_marc);
1794     }
1795     yaz_marc_destroy(mt);
1796     if (cd)
1797         yaz_iconv_close(cd);
1798     return 0;
1799 }
1800
1801 static const char *record_iconv_return(ZOOM_record rec, int *len,
1802                                        const char *buf, int sz,
1803                                        const char *record_charset)
1804 {
1805     char to[40];
1806     char from[40];
1807     yaz_iconv_t cd = 0;
1808
1809     *from = '\0';
1810     strcpy(to, "UTF-8");
1811
1812     if (record_charset && *record_charset)
1813     {
1814         /* Use "from,to" or just "from" */
1815         const char *cp = strchr(record_charset, ',');
1816         int clen = strlen(record_charset);
1817         if (cp && cp[1])
1818         {
1819             strncpy( to, cp+1, sizeof(to)-1);
1820             to[sizeof(to)-1] = '\0';
1821             clen = cp - record_charset;
1822         }
1823         if (clen > sizeof(from)-1)
1824             clen = sizeof(from)-1;
1825         
1826         if (clen)
1827             strncpy(from, record_charset, clen);
1828         from[clen] = '\0';
1829     }
1830
1831     if (*from && *to && (cd = yaz_iconv_open(to, from)))
1832     {
1833         if (!rec->wrbuf_iconv)
1834             rec->wrbuf_iconv = wrbuf_alloc();
1835
1836         wrbuf_rewind(rec->wrbuf_iconv);
1837
1838         wrbuf_iconv_write(rec->wrbuf_iconv, cd, buf, sz);
1839         wrbuf_iconv_reset(rec->wrbuf_iconv, cd);
1840
1841         buf = wrbuf_cstr(rec->wrbuf_iconv);
1842         sz = wrbuf_len(rec->wrbuf_iconv);
1843         yaz_iconv_close(cd);
1844     }
1845     if (len)
1846         *len = sz;
1847     return buf;
1848 }
1849
1850 ZOOM_API(int)
1851     ZOOM_record_error(ZOOM_record rec, const char **cp,
1852                       const char **addinfo, const char **diagset)
1853 {
1854     Z_NamePlusRecord *npr;
1855     
1856     if (!rec)
1857         return 0;
1858     npr = rec->npr;
1859     if (npr && npr->which == Z_NamePlusRecord_surrogateDiagnostic)
1860     {
1861         Z_DiagRec *diag_rec = npr->u.surrogateDiagnostic;
1862         int error = YAZ_BIB1_UNSPECIFIED_ERROR;
1863         const char *add = 0;
1864
1865         if (diag_rec->which == Z_DiagRec_defaultFormat)
1866         {
1867             Z_DefaultDiagFormat *ddf = diag_rec->u.defaultFormat;
1868             int oclass;
1869     
1870             error = *ddf->condition;
1871             switch (ddf->which)
1872             {
1873             case Z_DefaultDiagFormat_v2Addinfo:
1874                 add = ddf->u.v2Addinfo;
1875                 break;
1876             case Z_DefaultDiagFormat_v3Addinfo:
1877                 add = ddf->u.v3Addinfo;
1878                 break;
1879             }
1880             if (diagset)
1881                 *diagset =
1882                     yaz_oid_to_string(yaz_oid_std(),
1883                                       ddf->diagnosticSetId, &oclass);
1884         }
1885         else
1886         {
1887             if (diagset)
1888                 *diagset = "Bib-1";
1889         }
1890         if (addinfo)
1891             *addinfo = add ? add : "";
1892         if (cp)
1893             *cp = diagbib1_str(error);
1894         return error;
1895     }
1896     return 0;
1897 }
1898
1899 ZOOM_API(const char *)
1900     ZOOM_record_get(ZOOM_record rec, const char *type_spec, int *len)
1901 {
1902     char type[40];
1903     char charset[40];
1904     char xpath[512];
1905     const char *cp;
1906     int i;
1907     Z_NamePlusRecord *npr;
1908     
1909     if (len)
1910         *len = 0; /* default return */
1911         
1912     if (!rec)
1913         return 0;
1914     npr = rec->npr;
1915     if (!npr)
1916         return 0;
1917
1918     cp = type_spec;
1919     for (i = 0; cp[i] && i < sizeof(type)-1; i++)
1920     {
1921         if (cp[i] == ';' || cp[i] == ' ')
1922             break;
1923         type[i] = cp[i];
1924     }
1925     type[i] = '\0';
1926     charset[0] = '\0';
1927     while (type_spec[i] == ';')
1928     {
1929         i++;
1930         while (type_spec[i] == ' ')
1931             i++;
1932         if (!strncmp(type_spec+i, "charset=", 8))
1933         {
1934             int j = 0;
1935             i = i + 8; /* skip charset= */
1936             for (j = 0; type_spec[i]  && j < sizeof(charset)-1; i++, j++)
1937             {
1938                 if (type_spec[i] == ';' || type_spec[i] == ' ')
1939                     break;
1940                 charset[j] = cp[i];
1941             }
1942             charset[j] = '\0';
1943         }
1944         else if (!strncmp(type_spec+i, "xpath=", 6))
1945         {
1946             int j = 0; 
1947             i = i + 6;
1948             for (j = 0; type_spec[i] && j < sizeof(xpath)-1; i++, j++)
1949                 xpath[j] = cp[i];
1950             xpath[j] = '\0';
1951         } 
1952         while (type_spec[i] == ' ')
1953             i++;
1954     }
1955     if (!strcmp(type, "database"))
1956     {
1957         if (len)
1958             *len = (npr->databaseName ? strlen(npr->databaseName) : 0);
1959         return npr->databaseName;
1960     }
1961     else if (!strcmp(type, "syntax"))
1962     {
1963         const char *desc = 0;   
1964         if (npr->which == Z_NamePlusRecord_databaseRecord)
1965         {
1966             Z_External *r = (Z_External *) npr->u.databaseRecord;
1967             desc = yaz_oid_to_string(yaz_oid_std(), r->direct_reference, 0);
1968         }
1969         if (!desc)
1970             desc = "none";
1971         if (len)
1972             *len = strlen(desc);
1973         return desc;
1974     }
1975     if (npr->which != Z_NamePlusRecord_databaseRecord)
1976         return 0;
1977
1978     /* from now on - we have a database record .. */
1979     if (!strcmp(type, "render"))
1980     {
1981         Z_External *r = (Z_External *) npr->u.databaseRecord;
1982         const int *oid = r->direct_reference;
1983
1984         /* render bibliographic record .. */
1985         if (r->which == Z_External_OPAC)
1986         {
1987             r = r->u.opac->bibliographicRecord;
1988             if (!r)
1989                 return 0;
1990             oid = r->direct_reference;
1991         }
1992         if (r->which == Z_External_sutrs)
1993             return record_iconv_return(rec, len,
1994                                        (char*) r->u.sutrs->buf,
1995                                        r->u.sutrs->len,
1996                                        charset);
1997         else if (r->which == Z_External_octet)
1998         {
1999             if (yaz_oid_is_iso2709(oid))
2000             {
2001                 const char *ret_buf = marc_iconv_return(
2002                     rec, YAZ_MARC_LINE, len,
2003                     (const char *) r->u.octet_aligned->buf,
2004                     r->u.octet_aligned->len,
2005                     charset);
2006                 if (ret_buf)
2007                     return ret_buf;
2008             }
2009             return record_iconv_return(rec, len,
2010                                        (const char *) r->u.octet_aligned->buf,
2011                                        r->u.octet_aligned->len,
2012                                        charset);
2013         }
2014         else if (r->which == Z_External_grs1)
2015         {
2016             if (!rec->wrbuf_marc)
2017                 rec->wrbuf_marc = wrbuf_alloc();
2018             wrbuf_rewind(rec->wrbuf_marc);
2019             yaz_display_grs1(rec->wrbuf_marc, r->u.grs1, 0);
2020             return record_iconv_return(rec, len,
2021                                        wrbuf_buf(rec->wrbuf_marc),
2022                                        wrbuf_len(rec->wrbuf_marc),
2023                                        charset);
2024         }
2025         return 0;
2026     }
2027     else if (!strcmp(type, "xml"))
2028     {
2029         Z_External *r = (Z_External *) npr->u.databaseRecord;
2030         const int *oid = r->direct_reference;
2031
2032         /* render bibliographic record .. */
2033         if (r->which == Z_External_OPAC)
2034         {
2035             r = r->u.opac->bibliographicRecord;
2036             if (!r)
2037                 return 0;
2038             oid = r->direct_reference;
2039         }
2040         
2041         if (r->which == Z_External_sutrs)
2042             return record_iconv_return(rec, len,
2043                                        (const char *) r->u.sutrs->buf,
2044                                        r->u.sutrs->len,
2045                                        charset);
2046         else if (r->which == Z_External_octet)
2047         {
2048             int marc_decode_type = YAZ_MARC_MARCXML;
2049             if (yaz_oid_is_iso2709(oid))
2050             {
2051                 const char *ret_buf = marc_iconv_return(
2052                     rec, marc_decode_type, len,
2053                     (const char *) r->u.octet_aligned->buf,
2054                     r->u.octet_aligned->len,
2055                     charset);
2056                 if (ret_buf)
2057                     return ret_buf;
2058             }
2059             return record_iconv_return(rec, len,
2060                                        (const char *) r->u.octet_aligned->buf,
2061                                        r->u.octet_aligned->len,
2062                                        charset);
2063         }
2064         else if (r->which == Z_External_grs1)
2065         {
2066             if (len) *len = 5;
2067             return "GRS-1";
2068         }
2069         return 0;
2070     }
2071     else if (!strcmp(type, "raw"))
2072     {
2073         Z_External *r = (Z_External *) npr->u.databaseRecord;
2074         
2075         if (r->which == Z_External_sutrs)
2076         {
2077             if (len) *len = r->u.sutrs->len;
2078             return (const char *) r->u.sutrs->buf;
2079         }
2080         else if (r->which == Z_External_octet)
2081         {
2082             if (len) *len = r->u.octet_aligned->len;
2083             return (const char *) r->u.octet_aligned->buf;
2084         }
2085         else /* grs-1, explain, OPAC, ... */
2086         {
2087             if (len) *len = -1;
2088             return (const char *) npr->u.databaseRecord;
2089         }
2090         return 0;
2091     }
2092     else if (!strcmp (type, "ext"))
2093     {
2094         if (len) *len = -1;
2095         return (const char *) npr->u.databaseRecord;
2096     }
2097     else if (!strcmp (type, "opac"))
2098              
2099     {
2100         Z_External *r = (Z_External *) npr->u.databaseRecord;
2101         if (r->which == Z_External_OPAC)
2102         {
2103             if (!rec->wrbuf_opac)
2104                 rec->wrbuf_opac = wrbuf_alloc();
2105             wrbuf_rewind(rec->wrbuf_opac);
2106             yaz_display_OPAC(rec->wrbuf_opac, r->u.opac, 0);
2107             return record_iconv_return(rec, len,
2108                                        wrbuf_buf(rec->wrbuf_opac),
2109                                        wrbuf_len(rec->wrbuf_opac),
2110                                        charset);
2111         }
2112     }
2113     return 0;
2114 }
2115
2116 static int strcmp_null(const char *v1, const char *v2)
2117 {
2118     if (!v1 && !v2)
2119         return 0;
2120     if (!v1 || !v2)
2121         return -1;
2122     return strcmp(v1, v2);
2123 }
2124
2125 static size_t record_hash(int pos)
2126 {
2127     if (pos < 0)
2128         pos = 0;
2129     return pos % RECORD_HASH_SIZE;
2130 }
2131
2132 static void record_cache_add(ZOOM_resultset r, Z_NamePlusRecord *npr, 
2133                              int pos,
2134                              const char *syntax, const char *elementSetName)
2135 {
2136     ZOOM_record_cache rc;
2137     
2138     ZOOM_Event event = ZOOM_Event_create(ZOOM_EVENT_RECV_RECORD);
2139     ZOOM_connection_put_event(r->connection, event);
2140
2141     for (rc = r->record_hash[record_hash(pos)]; rc; rc = rc->next)
2142     {
2143         if (pos == rc->pos)
2144         {
2145             if (strcmp_null(r->schema, rc->schema))
2146                 continue;
2147             if (strcmp_null(elementSetName,rc->elementSetName))
2148                 continue;
2149             if (strcmp_null(syntax, rc->syntax))
2150                 continue;
2151             /* not destroying rc->npr (it's handled by nmem )*/
2152             rc->rec.npr = npr;
2153             /* keeping wrbuf_marc too */
2154             return;
2155         }
2156     }
2157     rc = (ZOOM_record_cache) odr_malloc(r->odr, sizeof(*rc));
2158     rc->rec.npr = npr; 
2159     rc->rec.odr = 0;
2160     rc->rec.wrbuf_marc = 0;
2161     rc->rec.wrbuf_iconv = 0;
2162     rc->rec.wrbuf_opac = 0;
2163     if (elementSetName)
2164         rc->elementSetName = odr_strdup(r->odr, elementSetName);
2165     else
2166         rc->elementSetName = 0;
2167
2168     if (syntax)
2169         rc->syntax = odr_strdup(r->odr, syntax);
2170     else
2171         rc->syntax = 0;
2172
2173     if (r->schema)
2174         rc->schema = odr_strdup(r->odr, r->schema);
2175     else
2176         rc->schema = 0;
2177
2178     rc->pos = pos;
2179     rc->next = r->record_hash[record_hash(pos)];
2180     r->record_hash[record_hash(pos)] = rc;
2181 }
2182
2183 static ZOOM_record record_cache_lookup(ZOOM_resultset r, int pos,
2184                                        const char *syntax,
2185                                        const char *elementSetName)
2186 {
2187     ZOOM_record_cache rc;
2188     
2189     for (rc = r->record_hash[record_hash(pos)]; rc; rc = rc->next)
2190     {
2191         if (pos == rc->pos)
2192         {
2193             if (strcmp_null(r->schema, rc->schema))
2194                 continue;
2195             if (strcmp_null(elementSetName,rc->elementSetName))
2196                 continue;
2197             if (strcmp_null(syntax, rc->syntax))
2198                 continue;
2199             return &rc->rec;
2200         }
2201     }
2202     return 0;
2203 }
2204                                              
2205 static void handle_records(ZOOM_connection c, Z_Records *sr,
2206                            int present_phase)
2207 {
2208     ZOOM_resultset resultset;
2209     int *start, *count;
2210     const char *syntax = 0, *elementSetName = 0;
2211
2212     if (!c->tasks)
2213         return ;
2214     switch (c->tasks->which)
2215     {
2216     case ZOOM_TASK_SEARCH:
2217         resultset = c->tasks->u.search.resultset;
2218         start = &c->tasks->u.search.start;
2219         count = &c->tasks->u.search.count;
2220         syntax = c->tasks->u.search.syntax;
2221         elementSetName = c->tasks->u.search.elementSetName;
2222         break;
2223     case ZOOM_TASK_RETRIEVE:
2224         resultset = c->tasks->u.retrieve.resultset;        
2225         start = &c->tasks->u.retrieve.start;
2226         count = &c->tasks->u.retrieve.count;
2227         syntax = c->tasks->u.retrieve.syntax;
2228         elementSetName = c->tasks->u.retrieve.elementSetName;
2229         break;
2230     default:
2231         return;
2232     }
2233     if (sr && sr->which == Z_Records_NSD)
2234         response_default_diag(c, sr->u.nonSurrogateDiagnostic);
2235     else if (sr && sr->which == Z_Records_multipleNSD)
2236     {
2237         if (sr->u.multipleNonSurDiagnostics->num_diagRecs >= 1)
2238             response_diag(c, sr->u.multipleNonSurDiagnostics->diagRecs[0]);
2239         else
2240             set_ZOOM_error(c, ZOOM_ERROR_DECODE, 0);
2241     }
2242     else 
2243     {
2244         if (*count + *start > resultset->size)
2245             *count = resultset->size - *start;
2246         if (*count < 0)
2247             *count = 0;
2248         if (sr && sr->which == Z_Records_DBOSD)
2249         {
2250             int i;
2251             NMEM nmem = odr_extract_mem(c->odr_in);
2252             Z_NamePlusRecordList *p =
2253                 sr->u.databaseOrSurDiagnostics;
2254             for (i = 0; i<p->num_records; i++)
2255             {
2256                 record_cache_add(resultset, p->records[i], i + *start,
2257                                  syntax, elementSetName);
2258             }
2259             *count -= i;
2260             if (*count < 0)
2261                 *count = 0;
2262             *start += i;
2263             yaz_log(log_details, 
2264                     "handle_records resultset=%p start=%d count=%d",
2265                     resultset, *start, *count);
2266
2267             /* transfer our response to search_nmem .. we need it later */
2268             nmem_transfer(odr_getmem(resultset->odr), nmem);
2269             nmem_destroy(nmem);
2270             if (present_phase && p->num_records == 0)
2271             {
2272                 /* present response and we didn't get any records! */
2273                 Z_NamePlusRecord *myrec = 
2274                     zget_surrogateDiagRec(resultset->odr, 0, 14, 0);
2275                 record_cache_add(resultset, myrec, *start,
2276                                  syntax, elementSetName);
2277             }
2278         }
2279         else if (present_phase)
2280         {
2281             /* present response and we didn't get any records! */
2282             Z_NamePlusRecord *myrec = 
2283                 zget_surrogateDiagRec(resultset->odr, 0, 14, 0);
2284             record_cache_add(resultset, myrec, *start, syntax, elementSetName);
2285         }
2286     }
2287 }
2288
2289 static void handle_present_response(ZOOM_connection c, Z_PresentResponse *pr)
2290 {
2291     handle_records(c, pr->records, 1);
2292 }
2293
2294 static void handle_queryExpressionTerm(ZOOM_options opt, const char *name,
2295                                        Z_Term *term)
2296 {
2297     switch (term->which)
2298     {
2299     case Z_Term_general:
2300         ZOOM_options_setl(opt, name,
2301                           (const char *)(term->u.general->buf), 
2302                           term->u.general->len);
2303         break;
2304     case Z_Term_characterString:
2305         ZOOM_options_set(opt, name, term->u.characterString);
2306         break;
2307     case Z_Term_numeric:
2308         ZOOM_options_set_int(opt, name, *term->u.numeric);
2309         break;
2310     }
2311 }
2312
2313 static void handle_queryExpression(ZOOM_options opt, const char *name,
2314                                    Z_QueryExpression *exp)
2315 {
2316     char opt_name[80];
2317     
2318     switch (exp->which)
2319     {
2320     case Z_QueryExpression_term:
2321         if (exp->u.term && exp->u.term->queryTerm)
2322         {
2323             sprintf(opt_name, "%s.term", name);
2324             handle_queryExpressionTerm(opt, opt_name, exp->u.term->queryTerm);
2325         }
2326         break;
2327     case Z_QueryExpression_query:
2328         break;
2329     }
2330 }
2331
2332 static void handle_searchResult(ZOOM_connection c, ZOOM_resultset resultset,
2333                                 Z_OtherInformation *o)
2334 {
2335     int i;
2336     for (i = 0; o && i < o->num_elements; i++)
2337     {
2338         if (o->list[i]->which == Z_OtherInfo_externallyDefinedInfo)
2339         {
2340             Z_External *ext = o->list[i]->information.externallyDefinedInfo;
2341             
2342             if (ext->which == Z_External_searchResult1)
2343             {
2344                 int j;
2345                 Z_SearchInfoReport *sr = ext->u.searchResult1;
2346                 
2347                 if (sr->num)
2348                     ZOOM_options_set_int(
2349                         resultset->options, "searchresult.size", sr->num);
2350
2351                 for (j = 0; j < sr->num; j++)
2352                 {
2353                     Z_SearchInfoReport_s *ent =
2354                         ext->u.searchResult1->elements[j];
2355                     char pref[80];
2356                     
2357                     sprintf(pref, "searchresult.%d", j);
2358
2359                     if (ent->subqueryId)
2360                     {
2361                         char opt_name[80];
2362                         sprintf(opt_name, "%s.id", pref);
2363                         ZOOM_options_set(resultset->options, opt_name,
2364                                          ent->subqueryId);
2365                     }
2366                     if (ent->subqueryExpression)
2367                     {
2368                         char opt_name[80];
2369                         sprintf(opt_name, "%s.subquery", pref);
2370                         handle_queryExpression(resultset->options, opt_name,
2371                                                ent->subqueryExpression);
2372                     }
2373                     if (ent->subqueryInterpretation)
2374                     {
2375                         char opt_name[80];
2376                         sprintf(opt_name, "%s.interpretation", pref);
2377                         handle_queryExpression(resultset->options, opt_name,
2378                                                ent->subqueryInterpretation);
2379                     }
2380                     if (ent->subqueryRecommendation)
2381                     {
2382                         char opt_name[80];
2383                         sprintf(opt_name, "%s.recommendation", pref);
2384                         handle_queryExpression(resultset->options, opt_name,
2385                                                ent->subqueryRecommendation);
2386                     }
2387                     if (ent->subqueryCount)
2388                     {
2389                         char opt_name[80];
2390                         sprintf(opt_name, "%s.count", pref);
2391                         ZOOM_options_set_int(resultset->options, opt_name,
2392                                              *ent->subqueryCount);
2393                     }                                             
2394                 }
2395             }
2396         }
2397     }
2398 }
2399
2400 static void handle_search_response(ZOOM_connection c, Z_SearchResponse *sr)
2401 {
2402     ZOOM_resultset resultset;
2403     ZOOM_Event event;
2404
2405     if (!c->tasks || c->tasks->which != ZOOM_TASK_SEARCH)
2406         return ;
2407
2408     event = ZOOM_Event_create(ZOOM_EVENT_RECV_SEARCH);
2409     ZOOM_connection_put_event(c, event);
2410
2411     resultset = c->tasks->u.search.resultset;
2412
2413     if (sr->resultSetStatus)
2414     {
2415         ZOOM_options_set_int(resultset->options, "resultSetStatus",
2416                              *sr->resultSetStatus);
2417     }
2418     if (sr->presentStatus)
2419     {
2420         ZOOM_options_set_int(resultset->options, "presentStatus",
2421                              *sr->presentStatus);
2422     }
2423     handle_searchResult(c, resultset, sr->additionalSearchInfo);
2424
2425     resultset->size = *sr->resultCount;
2426     handle_records(c, sr->records, 0);
2427 }
2428
2429 static void sort_response(ZOOM_connection c, Z_SortResponse *res)
2430 {
2431     if (res->diagnostics && res->num_diagnostics > 0)
2432         response_diag(c, res->diagnostics[0]);
2433 }
2434
2435 static int scan_response(ZOOM_connection c, Z_ScanResponse *res)
2436 {
2437     NMEM nmem = odr_extract_mem(c->odr_in);
2438     ZOOM_scanset scan;
2439
2440     if (!c->tasks || c->tasks->which != ZOOM_TASK_SCAN)
2441         return 0;
2442     scan = c->tasks->u.scan.scan;
2443
2444     if (res->entries && res->entries->nonsurrogateDiagnostics)
2445         response_diag(c, res->entries->nonsurrogateDiagnostics[0]);
2446     scan->scan_response = res;
2447     nmem_transfer(odr_getmem(scan->odr), nmem);
2448     if (res->stepSize)
2449         ZOOM_options_set_int(scan->options, "stepSize", *res->stepSize);
2450     if (res->positionOfTerm)
2451         ZOOM_options_set_int(scan->options, "position", *res->positionOfTerm);
2452     if (res->scanStatus)
2453         ZOOM_options_set_int(scan->options, "scanStatus", *res->scanStatus);
2454     if (res->numberOfEntriesReturned)
2455         ZOOM_options_set_int(scan->options, "number",
2456                              *res->numberOfEntriesReturned);
2457     nmem_destroy(nmem);
2458     return 1;
2459 }
2460
2461 static zoom_ret send_sort(ZOOM_connection c,
2462                           ZOOM_resultset resultset)
2463 {
2464     if (c->error)
2465         resultset->r_sort_spec = 0;
2466     if (resultset->r_sort_spec)
2467     {
2468         Z_APDU *apdu = zget_APDU(c->odr_out, Z_APDU_sortRequest);
2469         Z_SortRequest *req = apdu->u.sortRequest;
2470         
2471         req->num_inputResultSetNames = 1;
2472         req->inputResultSetNames = (Z_InternationalString **)
2473             odr_malloc(c->odr_out, sizeof(*req->inputResultSetNames));
2474         req->inputResultSetNames[0] =
2475             odr_strdup(c->odr_out, resultset->setname);
2476         req->sortedResultSetName = odr_strdup(c->odr_out, resultset->setname);
2477         req->sortSequence = resultset->r_sort_spec;
2478         resultset->r_sort_spec = 0;
2479         return send_APDU(c, apdu);
2480     }
2481     return zoom_complete;
2482 }
2483
2484 static zoom_ret send_present(ZOOM_connection c)
2485 {
2486     Z_APDU *apdu = 0;
2487     Z_PresentRequest *req = 0;
2488     int i = 0;
2489     const char *syntax = 0;
2490     const char *elementSetName = 0;
2491     ZOOM_resultset  resultset;
2492     int *start, *count;
2493
2494     if (!c->tasks)
2495     {
2496         yaz_log(log_details, "%p send_present no tasks", c);
2497         return zoom_complete;
2498     }
2499     
2500     switch (c->tasks->which)
2501     {
2502     case ZOOM_TASK_SEARCH:
2503         resultset = c->tasks->u.search.resultset;
2504         start = &c->tasks->u.search.start;
2505         count = &c->tasks->u.search.count;
2506         syntax = c->tasks->u.search.syntax;
2507         elementSetName = c->tasks->u.search.elementSetName;
2508         break;
2509     case ZOOM_TASK_RETRIEVE:
2510         resultset = c->tasks->u.retrieve.resultset;
2511         start = &c->tasks->u.retrieve.start;
2512         count = &c->tasks->u.retrieve.count;
2513         syntax = c->tasks->u.retrieve.syntax;
2514         elementSetName = c->tasks->u.retrieve.elementSetName;
2515         break;
2516     default:
2517         return zoom_complete;
2518     }
2519     yaz_log(log_details, "%p send_present start=%d count=%d",
2520             c, *start, *count);
2521
2522     if (*start < 0 || *count < 0 || *start + *count > resultset->size)
2523     {
2524         set_dset_error(c, YAZ_BIB1_PRESENT_REQUEST_OUT_OF_RANGE, "Bib-1",
2525                        "", 0);
2526     }
2527     if (c->error)                  /* don't continue on error */
2528         return zoom_complete;
2529     yaz_log(log_details, "send_present resultset=%p start=%d count=%d",
2530             resultset, *start, *count);
2531
2532     for (i = 0; i < *count; i++)
2533     {
2534         ZOOM_record rec =
2535             record_cache_lookup(resultset, i + *start, syntax, elementSetName);
2536         if (!rec)
2537             break;
2538         else
2539         {
2540             ZOOM_Event event = ZOOM_Event_create(ZOOM_EVENT_RECV_RECORD);
2541             ZOOM_connection_put_event(c, event);
2542         }
2543     }
2544     *start += i;
2545     *count -= i;
2546
2547     if (*count == 0)
2548     {
2549         yaz_log(log_details, "%p send_present skip=%d no more to fetch", c, i);
2550         return zoom_complete;
2551     }
2552
2553     apdu = zget_APDU(c->odr_out, Z_APDU_presentRequest);
2554     req = apdu->u.presentRequest;
2555
2556     if (i)
2557         yaz_log(log_details, "%p send_present skip=%d", c, i);
2558
2559     *req->resultSetStartPoint = *start + 1;
2560     *req->numberOfRecordsRequested = resultset->step>0 ?
2561         resultset->step : *count;
2562     if (*req->numberOfRecordsRequested + *start > resultset->size)
2563         *req->numberOfRecordsRequested = resultset->size - *start;
2564     assert(*req->numberOfRecordsRequested > 0);
2565
2566     if (syntax && *syntax)
2567         req->preferredRecordSyntax =
2568             zoom_yaz_str_to_z3950oid(c, CLASS_RECSYN, syntax);
2569
2570     if (resultset->schema && *resultset->schema)
2571     {
2572         Z_RecordComposition *compo = (Z_RecordComposition *)
2573             odr_malloc(c->odr_out, sizeof(*compo));
2574
2575         req->recordComposition = compo;
2576         compo->which = Z_RecordComp_complex;
2577         compo->u.complex = (Z_CompSpec *)
2578             odr_malloc(c->odr_out, sizeof(*compo->u.complex));
2579         compo->u.complex->selectAlternativeSyntax = (bool_t *) 
2580             odr_malloc(c->odr_out, sizeof(bool_t));
2581         *compo->u.complex->selectAlternativeSyntax = 0;
2582
2583         compo->u.complex->generic = (Z_Specification *)
2584             odr_malloc(c->odr_out, sizeof(*compo->u.complex->generic));
2585
2586         compo->u.complex->generic->which = Z_Schema_oid;
2587         compo->u.complex->generic->schema.oid = (Odr_oid *)
2588             zoom_yaz_str_to_z3950oid (c, CLASS_SCHEMA, resultset->schema);
2589
2590         if (!compo->u.complex->generic->schema.oid)
2591         {
2592             /* OID wasn't a schema! Try record syntax instead. */
2593
2594             compo->u.complex->generic->schema.oid = (Odr_oid *)
2595                 zoom_yaz_str_to_z3950oid (c, CLASS_RECSYN, resultset->schema);
2596         }
2597         if (elementSetName && *elementSetName)
2598         {
2599             compo->u.complex->generic->elementSpec = (Z_ElementSpec *)
2600                 odr_malloc(c->odr_out, sizeof(Z_ElementSpec));
2601             compo->u.complex->generic->elementSpec->which =
2602                 Z_ElementSpec_elementSetName;
2603             compo->u.complex->generic->elementSpec->u.elementSetName =
2604                 odr_strdup(c->odr_out, elementSetName);
2605         }
2606         else
2607             compo->u.complex->generic->elementSpec = 0;
2608         compo->u.complex->num_dbSpecific = 0;
2609         compo->u.complex->dbSpecific = 0;
2610         compo->u.complex->num_recordSyntax = 0;
2611         compo->u.complex->recordSyntax = 0;
2612     }
2613     else if (elementSetName && *elementSetName)
2614     {
2615         Z_ElementSetNames *esn = (Z_ElementSetNames *)
2616             odr_malloc(c->odr_out, sizeof(*esn));
2617         Z_RecordComposition *compo = (Z_RecordComposition *)
2618             odr_malloc(c->odr_out, sizeof(*compo));
2619         
2620         esn->which = Z_ElementSetNames_generic;
2621         esn->u.generic = odr_strdup(c->odr_out, elementSetName);
2622         compo->which = Z_RecordComp_simple;
2623         compo->u.simple = esn;
2624         req->recordComposition = compo;
2625     }
2626     req->resultSetId = odr_strdup(c->odr_out, resultset->setname);
2627     return send_APDU(c, apdu);
2628 }
2629
2630 ZOOM_API(ZOOM_scanset)
2631     ZOOM_connection_scan(ZOOM_connection c, const char *start)
2632 {
2633     ZOOM_scanset s;
2634     ZOOM_query q = ZOOM_query_create();
2635
2636     ZOOM_query_prefix(q, start);
2637
2638     s = ZOOM_connection_scan1(c, q);
2639     ZOOM_query_destroy(q);
2640     return s;
2641
2642 }
2643
2644 ZOOM_API(ZOOM_scanset)
2645     ZOOM_connection_scan1(ZOOM_connection c, ZOOM_query q)
2646 {
2647     char *start;
2648     char *freeme = 0;
2649     ZOOM_scanset scan = 0;
2650
2651     /*
2652      * We need to check the query-type, so we can recognise CQL and
2653      * CCL and compile them into a form that we can use here.  The
2654      * ZOOM_query structure has no explicit `type' member, but
2655      * inspection of the ZOOM_query_prefix() and ZOOM_query_cql()
2656      * functions shows how the structure is set up in each case.
2657      */
2658     if (!q->z_query)
2659         return 0;
2660     else if (q->z_query->which == Z_Query_type_1) 
2661     {
2662         yaz_log(log_api, "%p ZOOM_connection_scan1 q=%p PQF '%s'",
2663                 c, q, q->query_string);
2664         start = q->query_string;
2665     } 
2666     else if (q->z_query->which == Z_Query_type_104)
2667     {
2668         yaz_log(log_api, "%p ZOOM_connection_scan1 q=%p CQL '%s'",
2669                 c, q, q->query_string);
2670         start = freeme = cql2pqf(c, q->query_string);
2671         if (start == 0)
2672             return 0;
2673     } 
2674     else
2675     {
2676         yaz_log(YLOG_FATAL, "%p ZOOM_connection_scan1 q=%p unknown type '%s'",
2677                 c, q, q->query_string);
2678         abort();
2679     }
2680     
2681     scan = (ZOOM_scanset) xmalloc(sizeof(*scan));
2682     scan->connection = c;
2683     scan->odr = odr_createmem(ODR_DECODE);
2684     scan->options = ZOOM_options_create_with_parent(c->options);
2685     scan->refcount = 1;
2686     scan->scan_response = 0;
2687     scan->termListAndStartPoint =
2688         p_query_scan(scan->odr, PROTO_Z3950, &scan->attributeSet, start);
2689     xfree(freeme);
2690
2691     scan->databaseNames = set_DatabaseNames(c, c->options,
2692                                             &scan->num_databaseNames,
2693                                             scan->odr);
2694     if (scan->termListAndStartPoint != 0)
2695     {
2696         ZOOM_task task = ZOOM_connection_add_task(c, ZOOM_TASK_SCAN);
2697         task->u.scan.scan = scan;
2698         
2699         (scan->refcount)++;
2700         if (!c->async)
2701         {
2702             while (ZOOM_event(1, &c))
2703                 ;
2704         }
2705     }
2706     return scan;
2707 }
2708
2709 ZOOM_API(void)
2710     ZOOM_scanset_destroy(ZOOM_scanset scan)
2711 {
2712     if (!scan)
2713         return;
2714     (scan->refcount)--;
2715     if (scan->refcount == 0)
2716     {
2717         odr_destroy(scan->odr);
2718         
2719         ZOOM_options_destroy(scan->options);
2720         xfree(scan);
2721     }
2722 }
2723
2724 static zoom_ret send_package(ZOOM_connection c)
2725 {
2726     ZOOM_Event event;
2727
2728     yaz_log(log_details, "%p send_package", c);
2729     if (!c->tasks)
2730         return zoom_complete;
2731     assert (c->tasks->which == ZOOM_TASK_PACKAGE);
2732     
2733     event = ZOOM_Event_create(ZOOM_EVENT_SEND_APDU);
2734     ZOOM_connection_put_event(c, event);
2735     
2736     c->buf_out = c->tasks->u.package->buf_out;
2737     c->len_out = c->tasks->u.package->len_out;
2738
2739     return do_write(c);
2740 }
2741
2742 static zoom_ret send_scan(ZOOM_connection c)
2743 {
2744     ZOOM_scanset scan;
2745     Z_APDU *apdu = zget_APDU(c->odr_out, Z_APDU_scanRequest);
2746     Z_ScanRequest *req = apdu->u.scanRequest;
2747
2748     yaz_log(log_details, "%p send_scan", c);
2749     if (!c->tasks)
2750         return zoom_complete;
2751     assert (c->tasks->which == ZOOM_TASK_SCAN);
2752     scan = c->tasks->u.scan.scan;
2753
2754     req->termListAndStartPoint = scan->termListAndStartPoint;
2755     req->attributeSet = scan->attributeSet;
2756
2757     *req->numberOfTermsRequested =
2758         ZOOM_options_get_int(scan->options, "number", 10);
2759
2760     req->preferredPositionInResponse =
2761         odr_intdup(c->odr_out,
2762                    ZOOM_options_get_int(scan->options, "position", 1));
2763
2764     req->stepSize =
2765         odr_intdup(c->odr_out,
2766                    ZOOM_options_get_int(scan->options, "stepSize", 0));
2767     
2768     req->databaseNames = scan->databaseNames;
2769     req->num_databaseNames = scan->num_databaseNames;
2770
2771     return send_APDU(c, apdu);
2772 }
2773
2774 ZOOM_API(size_t)
2775     ZOOM_scanset_size(ZOOM_scanset scan)
2776 {
2777     if (!scan || !scan->scan_response || !scan->scan_response->entries)
2778         return 0;
2779     return scan->scan_response->entries->num_entries;
2780 }
2781
2782 ZOOM_API(const char *)
2783     ZOOM_scanset_term(ZOOM_scanset scan, size_t pos,
2784                       int *occ, int *len)
2785 {
2786     const char *term = 0;
2787     size_t noent = ZOOM_scanset_size(scan);
2788     Z_ScanResponse *res = scan->scan_response;
2789     
2790     *len = 0;
2791     *occ = 0;
2792     if (pos >= noent)
2793         return 0;
2794     if (res->entries->entries[pos]->which == Z_Entry_termInfo)
2795     {
2796         Z_TermInfo *t = res->entries->entries[pos]->u.termInfo;
2797         
2798         if (t->term->which == Z_Term_general)
2799         {
2800             term = (const char *) t->term->u.general->buf;
2801             *len = t->term->u.general->len;
2802         }
2803         *occ = t->globalOccurrences ? *t->globalOccurrences : 0;
2804     }
2805     return term;
2806 }
2807
2808 ZOOM_API(const char *)
2809     ZOOM_scanset_display_term(ZOOM_scanset scan, size_t pos,
2810                               int *occ, int *len)
2811 {
2812     const char *term = 0;
2813     size_t noent = ZOOM_scanset_size(scan);
2814     Z_ScanResponse *res = scan->scan_response;
2815     
2816     *len = 0;
2817     *occ = 0;
2818     if (pos >= noent)
2819         return 0;
2820     if (res->entries->entries[pos]->which == Z_Entry_termInfo)
2821     {
2822         Z_TermInfo *t = res->entries->entries[pos]->u.termInfo;
2823
2824         if (t->displayTerm)
2825         {
2826             term = t->displayTerm;
2827             *len = strlen(term);
2828         }
2829         else if (t->term->which == Z_Term_general)
2830         {
2831             term = (const char *) t->term->u.general->buf;
2832             *len = t->term->u.general->len;
2833         }
2834         *occ = t->globalOccurrences ? *t->globalOccurrences : 0;
2835     }
2836     return term;
2837 }
2838
2839 ZOOM_API(const char *)
2840     ZOOM_scanset_option_get(ZOOM_scanset scan, const char *key)
2841 {
2842     return ZOOM_options_get(scan->options, key);
2843 }
2844
2845 ZOOM_API(void)
2846     ZOOM_scanset_option_set(ZOOM_scanset scan, const char *key,
2847                             const char *val)
2848 {
2849     ZOOM_options_set(scan->options, key, val);
2850 }
2851
2852 static Z_APDU *create_es_package(ZOOM_package p, const char *type)
2853 {
2854     const char *str;
2855     Z_APDU *apdu = zget_APDU(p->odr_out, Z_APDU_extendedServicesRequest);
2856     Z_ExtendedServicesRequest *req = apdu->u.extendedServicesRequest;
2857     
2858     *req->function = Z_ExtendedServicesRequest_create;
2859     
2860     str = ZOOM_options_get(p->options, "package-name");
2861     if (str && *str)
2862         req->packageName = odr_strdup(p->odr_out, str);
2863     
2864     str = ZOOM_options_get(p->options, "user-id");
2865     if (str)
2866         req->userId = odr_strdup(p->odr_out, str);
2867     
2868     req->packageType = yaz_string_to_oid_odr(yaz_oid_std(), CLASS_EXTSERV,
2869                                              type, p->odr_out);
2870
2871     str = ZOOM_options_get(p->options, "function");
2872     if (str)
2873     {
2874         if (!strcmp (str, "create"))
2875             *req->function = 1;
2876         if (!strcmp (str, "delete"))
2877             *req->function = 2;
2878         if (!strcmp (str, "modify"))
2879             *req->function = 3;
2880     }
2881     return apdu;
2882 }
2883
2884 static const char *ill_array_lookup(void *clientData, const char *idx)
2885 {
2886     ZOOM_package p = (ZOOM_package) clientData;
2887     return ZOOM_options_get(p->options, idx+4);
2888 }
2889
2890 static Z_External *encode_ill_request(ZOOM_package p)
2891 {
2892     ODR out = p->odr_out;
2893     ILL_Request *req;
2894     Z_External *r = 0;
2895     struct ill_get_ctl ctl;
2896         
2897     ctl.odr = p->odr_out;
2898     ctl.clientData = p;
2899     ctl.f = ill_array_lookup;
2900         
2901     req = ill_get_ILLRequest(&ctl, "ill", 0);
2902         
2903     if (!ill_Request(out, &req, 0, 0))
2904     {
2905         int ill_request_size;
2906         char *ill_request_buf = odr_getbuf(out, &ill_request_size, 0);
2907         if (ill_request_buf)
2908             odr_setbuf(out, ill_request_buf, ill_request_size, 1);
2909         return 0;
2910     }
2911     else
2912     {
2913         int illRequest_size = 0;
2914         char *illRequest_buf = odr_getbuf(out, &illRequest_size, 0);
2915                 
2916         r = (Z_External *) odr_malloc(out, sizeof(*r));
2917         r->direct_reference = yaz_string_to_oid_odr(yaz_oid_std(),
2918                                                     CLASS_GENERAL,
2919                                                     OID_STR_ILL_1,
2920                                                     out);
2921         r->indirect_reference = 0;
2922         r->descriptor = 0;
2923         r->which = Z_External_single;
2924                 
2925         r->u.single_ASN1_type =
2926             odr_create_Odr_oct(out,
2927                                (unsigned char *)illRequest_buf,
2928                                illRequest_size);
2929     }
2930     return r;
2931 }
2932
2933 static Z_ItemOrder *encode_item_order(ZOOM_package p)
2934 {
2935     Z_ItemOrder *req = (Z_ItemOrder *) odr_malloc(p->odr_out, sizeof(*req));
2936     const char *str;
2937     
2938     req->which = Z_IOItemOrder_esRequest;
2939     req->u.esRequest = (Z_IORequest *) 
2940         odr_malloc(p->odr_out,sizeof(Z_IORequest));
2941
2942     /* to keep part ... */
2943     req->u.esRequest->toKeep = (Z_IOOriginPartToKeep *)
2944         odr_malloc(p->odr_out,sizeof(Z_IOOriginPartToKeep));
2945     req->u.esRequest->toKeep->supplDescription = 0;
2946     req->u.esRequest->toKeep->contact = (Z_IOContact *)
2947         odr_malloc(p->odr_out, sizeof(*req->u.esRequest->toKeep->contact));
2948         
2949     str = ZOOM_options_get(p->options, "contact-name");
2950     req->u.esRequest->toKeep->contact->name = str ?
2951         odr_strdup(p->odr_out, str) : 0;
2952         
2953     str = ZOOM_options_get(p->options, "contact-phone");
2954     req->u.esRequest->toKeep->contact->phone = str ?
2955         odr_strdup(p->odr_out, str) : 0;
2956         
2957     str = ZOOM_options_get(p->options, "contact-email");
2958     req->u.esRequest->toKeep->contact->email = str ?
2959         odr_strdup(p->odr_out, str) : 0;
2960         
2961     req->u.esRequest->toKeep->addlBilling = 0;
2962         
2963     /* not to keep part ... */
2964     req->u.esRequest->notToKeep = (Z_IOOriginPartNotToKeep *)
2965         odr_malloc(p->odr_out,sizeof(Z_IOOriginPartNotToKeep));
2966         
2967     str = ZOOM_options_get(p->options, "itemorder-setname");
2968     if (!str)
2969         str = "default";
2970
2971     if (!*str) 
2972         req->u.esRequest->notToKeep->resultSetItem = 0;
2973     else
2974     {
2975         req->u.esRequest->notToKeep->resultSetItem = (Z_IOResultSetItem *)
2976             odr_malloc(p->odr_out, sizeof(Z_IOResultSetItem));
2977
2978         req->u.esRequest->notToKeep->resultSetItem->resultSetId =
2979             odr_strdup(p->odr_out, str);
2980         req->u.esRequest->notToKeep->resultSetItem->item =
2981             (int *) odr_malloc(p->odr_out, sizeof(int));
2982         
2983         str = ZOOM_options_get(p->options, "itemorder-item");
2984         *req->u.esRequest->notToKeep->resultSetItem->item =
2985             (str ? atoi(str) : 1);
2986     }
2987
2988     str = ZOOM_options_get(p->options, "doc");
2989     if (str)
2990     {
2991         req->u.esRequest->notToKeep->itemRequest =
2992             z_ext_record_xml(p->odr_out, str, strlen(str));
2993     }
2994     else
2995         req->u.esRequest->notToKeep->itemRequest = encode_ill_request(p);
2996     
2997     return req;
2998 }
2999
3000 Z_APDU *create_admin_package(ZOOM_package p, int type, 
3001                              Z_ESAdminOriginPartToKeep **toKeepP,
3002                              Z_ESAdminOriginPartNotToKeep **notToKeepP)
3003 {
3004     Z_APDU *apdu = create_es_package(p, OID_STR_ADMIN);
3005     if (apdu)
3006     {
3007         Z_ESAdminOriginPartToKeep  *toKeep;
3008         Z_ESAdminOriginPartNotToKeep  *notToKeep;
3009         Z_External *r = (Z_External *) odr_malloc(p->odr_out, sizeof(*r));
3010         const char *first_db = "Default";
3011         int num_db;
3012         char **db = set_DatabaseNames(p->connection, p->options, &num_db,
3013                                       p->odr_out);
3014         if (num_db > 0)
3015             first_db = db[0];
3016             
3017         r->direct_reference =
3018             yaz_string_to_oid_odr(yaz_oid_std(),
3019                                   CLASS_EXTSERV, OID_STR_ADMIN, p->odr_out);
3020         r->descriptor = 0;
3021         r->indirect_reference = 0;
3022         r->which = Z_External_ESAdmin;
3023         
3024         r->u.adminService = (Z_Admin *)
3025             odr_malloc(p->odr_out, sizeof(*r->u.adminService));
3026         r->u.adminService->which = Z_Admin_esRequest;
3027         r->u.adminService->u.esRequest = (Z_AdminEsRequest *)
3028             odr_malloc(p->odr_out, sizeof(*r->u.adminService->u.esRequest));
3029         
3030         toKeep = r->u.adminService->u.esRequest->toKeep =
3031             (Z_ESAdminOriginPartToKeep *) 
3032             odr_malloc(p->odr_out, sizeof(*r->u.adminService->u.esRequest->toKeep));
3033         toKeep->which = type;
3034         toKeep->databaseName = odr_strdup(p->odr_out, first_db);
3035         toKeep->u.create = odr_nullval();
3036         apdu->u.extendedServicesRequest->taskSpecificParameters = r;
3037         
3038         r->u.adminService->u.esRequest->notToKeep = notToKeep =
3039             (Z_ESAdminOriginPartNotToKeep *)
3040             odr_malloc(p->odr_out,
3041                        sizeof(*r->u.adminService->u.esRequest->notToKeep));
3042         notToKeep->which = Z_ESAdminOriginPartNotToKeep_recordsWillFollow;
3043         notToKeep->u.recordsWillFollow = odr_nullval();
3044         if (toKeepP)
3045             *toKeepP = toKeep;
3046         if (notToKeepP)
3047             *notToKeepP = notToKeep;
3048     }
3049     return apdu;
3050 }
3051
3052 static Z_APDU *create_xmlupdate_package(ZOOM_package p)
3053 {
3054     Z_APDU *apdu = create_es_package(p, OID_STR_XMLES);
3055     Z_ExtendedServicesRequest *req = apdu->u.extendedServicesRequest;
3056     Z_External *ext = (Z_External *) odr_malloc(p->odr_out, sizeof(*ext));
3057     const char *doc = ZOOM_options_get(p->options, "doc");
3058
3059     if (!doc)
3060         doc = "";
3061
3062     req->taskSpecificParameters = ext;
3063     ext->direct_reference = req->packageType;
3064     ext->descriptor = 0;
3065     ext->indirect_reference = 0;
3066     
3067     ext->which = Z_External_octet;
3068     ext->u.single_ASN1_type =
3069         odr_create_Odr_oct(p->odr_out, (const unsigned char *) doc,
3070                            strlen(doc));
3071     return apdu;
3072 }
3073
3074 static Z_APDU *create_update_package(ZOOM_package p)
3075 {
3076     Z_APDU *apdu = 0;
3077     const char *first_db = "Default";
3078     int num_db;
3079     char **db = set_DatabaseNames(p->connection, p->options, &num_db, p->odr_out);
3080     const char *action = ZOOM_options_get(p->options, "action");
3081     const char *recordIdOpaque = ZOOM_options_get(p->options, "recordIdOpaque");
3082     const char *recordIdNumber = ZOOM_options_get(p->options, "recordIdNumber");
3083     const char *record_buf = ZOOM_options_get(p->options, "record");
3084     const char *syntax_str = ZOOM_options_get(p->options, "syntax");
3085     int action_no = -1;
3086     int *syntax_oid = 0;
3087
3088     if (!syntax_str)
3089         syntax_str = "xml";
3090     if (!record_buf)
3091     {
3092         record_buf = "void";
3093         syntax_str = "SUTRS";
3094     }
3095
3096     if (syntax_str)
3097     {
3098         syntax_oid = yaz_string_to_oid_odr(yaz_oid_std(),
3099                                            CLASS_RECSYN, syntax_str,
3100                                            p->odr_out);
3101     }
3102     if (!syntax_oid)
3103         return 0;
3104
3105     if (num_db > 0)
3106         first_db = db[0];
3107     
3108     if (!action)
3109         action = "specialUpdate";
3110     
3111     if (!strcmp(action, "recordInsert"))
3112         action_no = Z_IUOriginPartToKeep_recordInsert;
3113     else if (!strcmp(action, "recordReplace"))
3114         action_no = Z_IUOriginPartToKeep_recordReplace;
3115     else if (!strcmp(action, "recordDelete"))
3116         action_no = Z_IUOriginPartToKeep_recordDelete;
3117     else if (!strcmp(action, "elementUpdate"))
3118         action_no = Z_IUOriginPartToKeep_elementUpdate;
3119     else if (!strcmp(action, "specialUpdate"))
3120         action_no = Z_IUOriginPartToKeep_specialUpdate;
3121     else
3122         return 0;
3123
3124     apdu = create_es_package(p, OID_STR_EXT_UPDATE);
3125     if (apdu)
3126     {
3127         Z_IUOriginPartToKeep *toKeep;
3128         Z_IUSuppliedRecords *notToKeep;
3129         Z_External *r = (Z_External *)
3130             odr_malloc(p->odr_out, sizeof(*r));
3131         
3132         apdu->u.extendedServicesRequest->taskSpecificParameters = r;
3133
3134         
3135         r->direct_reference =
3136             yaz_string_to_oid_odr(yaz_oid_std(), CLASS_EXTSERV,
3137                                   OID_STR_EXT_UPDATE, p->odr_out);
3138         r->descriptor = 0;
3139         r->which = Z_External_update;
3140         r->indirect_reference = 0;
3141         r->u.update = (Z_IUUpdate *)
3142             odr_malloc(p->odr_out, sizeof(*r->u.update));
3143         
3144         r->u.update->which = Z_IUUpdate_esRequest;
3145         r->u.update->u.esRequest = (Z_IUUpdateEsRequest *)
3146             odr_malloc(p->odr_out, sizeof(*r->u.update->u.esRequest));
3147         toKeep = r->u.update->u.esRequest->toKeep = 
3148             (Z_IUOriginPartToKeep *)
3149             odr_malloc(p->odr_out, sizeof(*toKeep));
3150         
3151         toKeep->databaseName = odr_strdup(p->odr_out, first_db);
3152         toKeep->schema = 0;
3153         toKeep->elementSetName = 0;
3154         toKeep->actionQualifier = 0;
3155         toKeep->action = odr_intdup(p->odr_out, action_no);
3156         
3157         notToKeep = r->u.update->u.esRequest->notToKeep = 
3158             (Z_IUSuppliedRecords *)
3159             odr_malloc(p->odr_out, sizeof(*notToKeep));
3160         notToKeep->num = 1;
3161         notToKeep->elements = (Z_IUSuppliedRecords_elem **)
3162             odr_malloc(p->odr_out, sizeof(*notToKeep->elements));
3163         notToKeep->elements[0] = (Z_IUSuppliedRecords_elem *)
3164             odr_malloc(p->odr_out, sizeof(**notToKeep->elements));
3165         notToKeep->elements[0]->which = Z_IUSuppliedRecords_elem_opaque;
3166         if (recordIdOpaque)
3167         {
3168             notToKeep->elements[0]->u.opaque = 
3169                 odr_create_Odr_oct(p->odr_out,
3170                                    (const unsigned char *) recordIdOpaque,
3171                                    strlen(recordIdOpaque));
3172         }
3173         else if (recordIdNumber)
3174         {
3175             notToKeep->elements[0]->which = Z_IUSuppliedRecords_elem_number;
3176             
3177             notToKeep->elements[0]->u.number =
3178                 odr_intdup(p->odr_out, atoi(recordIdNumber));
3179         }
3180         else
3181             notToKeep->elements[0]->u.opaque = 0;
3182         notToKeep->elements[0]->supplementalId = 0;
3183         notToKeep->elements[0]->correlationInfo = 0;
3184         notToKeep->elements[0]->record =
3185             z_ext_record_oid(p->odr_out, syntax_oid,
3186                              record_buf, strlen(record_buf));
3187     }
3188     if (0 && apdu)
3189     {
3190         ODR print = odr_createmem(ODR_PRINT);
3191
3192         z_APDU(print, &apdu, 0, 0);
3193         odr_destroy(print);
3194     }
3195     return apdu;
3196 }
3197
3198 ZOOM_API(void)
3199     ZOOM_package_send(ZOOM_package p, const char *type)
3200 {
3201     Z_APDU *apdu = 0;
3202     ZOOM_connection c;
3203     if (!p)
3204         return;
3205     c = p->connection;
3206     odr_reset(p->odr_out);
3207     xfree(p->buf_out);
3208     p->buf_out = 0;
3209     if (!strcmp(type, "itemorder"))
3210     {
3211         apdu = create_es_package(p, OID_STR_ITEMORDER);
3212         if (apdu)
3213         {
3214             Z_External *r = (Z_External *) odr_malloc(p->odr_out, sizeof(*r));
3215             
3216             r->direct_reference =
3217                 yaz_string_to_oid_odr(yaz_oid_std(), CLASS_EXTSERV,
3218                                       OID_STR_ITEMORDER, p->odr_out);
3219             r->descriptor = 0;
3220             r->which = Z_External_itemOrder;
3221             r->indirect_reference = 0;
3222             r->u.itemOrder = encode_item_order(p);
3223
3224             apdu->u.extendedServicesRequest->taskSpecificParameters = r;
3225         }
3226     }
3227     else if (!strcmp(type, "create"))  /* create database */
3228     {
3229         apdu = create_admin_package(p, Z_ESAdminOriginPartToKeep_create,
3230                                     0, 0);
3231     }   
3232     else if (!strcmp(type, "drop"))  /* drop database */
3233     {
3234         apdu = create_admin_package(p, Z_ESAdminOriginPartToKeep_drop,
3235                                     0, 0);
3236     }
3237     else if (!strcmp(type, "commit"))  /* commit changes */
3238     {
3239         apdu = create_admin_package(p, Z_ESAdminOriginPartToKeep_commit,
3240                                     0, 0);
3241     }
3242     else if (!strcmp(type, "update")) /* update record(s) */
3243     {
3244         apdu = create_update_package(p);
3245     }
3246     else if (!strcmp(type, "xmlupdate"))
3247     {
3248         apdu = create_xmlupdate_package(p);
3249     }
3250     if (apdu)
3251     {
3252         if (encode_APDU(p->connection, apdu, p->odr_out) == 0)
3253         {
3254             char *buf;
3255
3256             ZOOM_task task = ZOOM_connection_add_task(c, ZOOM_TASK_PACKAGE);
3257             task->u.package = p;
3258             buf = odr_getbuf(p->odr_out, &p->len_out, 0);
3259             p->buf_out = (char *) xmalloc(p->len_out);
3260             memcpy(p->buf_out, buf, p->len_out);
3261             
3262             (p->refcount)++;
3263             if (!c->async)
3264             {
3265                 while (ZOOM_event(1, &c))
3266                     ;
3267             }
3268         }
3269     }
3270 }
3271
3272 ZOOM_API(ZOOM_package)
3273     ZOOM_connection_package(ZOOM_connection c, ZOOM_options options)
3274 {
3275     ZOOM_package p = (ZOOM_package) xmalloc(sizeof(*p));
3276
3277     p->connection = c;
3278     p->odr_out = odr_createmem(ODR_ENCODE);
3279     p->options = ZOOM_options_create_with_parent2(options, c->options);
3280     p->refcount = 1;
3281     p->buf_out = 0;
3282     p->len_out = 0;
3283     return p;
3284 }
3285
3286 ZOOM_API(void)
3287     ZOOM_package_destroy(ZOOM_package p)
3288 {
3289     if (!p)
3290         return;
3291     (p->refcount)--;
3292     if (p->refcount == 0)
3293     {
3294         odr_destroy(p->odr_out);
3295         xfree(p->buf_out);
3296         
3297         ZOOM_options_destroy(p->options);
3298         xfree(p);
3299     }
3300 }
3301
3302 ZOOM_API(const char *)
3303     ZOOM_package_option_get(ZOOM_package p, const char *key)
3304 {
3305     return ZOOM_options_get(p->options, key);
3306 }
3307
3308
3309 ZOOM_API(void)
3310     ZOOM_package_option_set(ZOOM_package p, const char *key,
3311                             const char *val)
3312 {
3313     ZOOM_options_set(p->options, key, val);
3314 }
3315
3316 static int ZOOM_connection_exec_task(ZOOM_connection c)
3317 {
3318     ZOOM_task task = c->tasks;
3319     zoom_ret ret = zoom_complete;
3320
3321     if (!task)
3322         return 0;
3323     yaz_log(log_details, "%p ZOOM_connection_exec_task type=%d run=%d",
3324             c, task->which, task->running);
3325     if (c->error != ZOOM_ERROR_NONE)
3326     {
3327         yaz_log(log_details, "%p ZOOM_connection_exec_task "
3328                 "removing tasks because of error = %d", c, c->error);
3329         ZOOM_connection_remove_tasks(c);
3330         return 0;
3331     }
3332     if (task->running)
3333     {
3334         yaz_log(log_details, "%p ZOOM_connection_exec_task "
3335                 "task already running", c);
3336         return 0;
3337     }
3338     task->running = 1;
3339     ret = zoom_complete;
3340     if (c->cs || task->which == ZOOM_TASK_CONNECT)
3341     {
3342         switch (task->which)
3343         {
3344         case ZOOM_TASK_SEARCH:
3345             if (c->proto == PROTO_HTTP)
3346                 ret = ZOOM_connection_srw_send_search(c);
3347             else
3348                 ret = ZOOM_connection_send_search(c);
3349             break;
3350         case ZOOM_TASK_RETRIEVE:
3351             if (c->proto == PROTO_HTTP)
3352                 ret = ZOOM_connection_srw_send_search(c);
3353             else
3354                 ret = send_present(c);
3355             break;
3356         case ZOOM_TASK_CONNECT:
3357             ret = do_connect(c);
3358             break;
3359         case ZOOM_TASK_SCAN:
3360             ret = send_scan(c);
3361             break;
3362         case ZOOM_TASK_PACKAGE:
3363             ret = send_package(c);
3364             break;
3365         case ZOOM_TASK_SORT:
3366             c->tasks->u.sort.resultset->r_sort_spec = 
3367                 c->tasks->u.sort.q->sort_spec;
3368             ret = send_sort(c, c->tasks->u.sort.resultset);
3369             break;
3370         }
3371     }
3372     else
3373     {
3374         yaz_log(log_details, "%p ZOOM_connection_exec_task "
3375                 "remove tasks because no connection exist", c);
3376         ZOOM_connection_remove_tasks(c);
3377     }
3378     if (ret == zoom_complete)
3379     {
3380         yaz_log(log_details, "%p ZOOM_connection_exec_task "
3381                 "task removed (complete)", c);
3382         ZOOM_connection_remove_task(c);
3383         return 0;
3384     }
3385     yaz_log(log_details, "%p ZOOM_connection_exec_task "
3386             "task pending", c);
3387     return 1;
3388 }
3389
3390 static zoom_ret send_sort_present(ZOOM_connection c)
3391 {
3392     zoom_ret r = zoom_complete;
3393
3394     if (c->tasks && c->tasks->which == ZOOM_TASK_SEARCH)
3395         r = send_sort(c, c->tasks->u.search.resultset);
3396     if (r == zoom_complete)
3397         r = send_present(c);
3398     return r;
3399 }
3400
3401 static int es_response(ZOOM_connection c,
3402                        Z_ExtendedServicesResponse *res)
3403 {
3404     if (!c->tasks || c->tasks->which != ZOOM_TASK_PACKAGE)
3405         return 0;
3406     if (res->diagnostics && res->num_diagnostics > 0)
3407         response_diag(c, res->diagnostics[0]);
3408     if (res->taskPackage &&
3409         res->taskPackage->which == Z_External_extendedService)
3410     {
3411         Z_TaskPackage *taskPackage = res->taskPackage->u.extendedService;
3412         Odr_oct *id = taskPackage->targetReference;
3413         
3414         if (id)
3415             ZOOM_options_setl(c->tasks->u.package->options,
3416                               "targetReference", (char*) id->buf, id->len);
3417     }
3418     if (res->taskPackage && 
3419         res->taskPackage->which == Z_External_octet)
3420     {
3421         Odr_oct *doc = res->taskPackage->u.octet_aligned;
3422         ZOOM_options_setl(c->tasks->u.package->options,
3423                           "xmlUpdateDoc", (char*) doc->buf, doc->len);
3424     }
3425     return 1;
3426 }
3427
3428 static void interpret_init_diag(ZOOM_connection c,
3429                                 Z_DiagnosticFormat *diag)
3430 {
3431     if (diag->num > 0)
3432     {
3433         Z_DiagnosticFormat_s *ds = diag->elements[0];
3434         if (ds->which == Z_DiagnosticFormat_s_defaultDiagRec)
3435             response_default_diag(c, ds->u.defaultDiagRec);
3436     }
3437 }
3438
3439
3440 static void interpret_otherinformation_field(ZOOM_connection c,
3441                                              Z_OtherInformation *ui)
3442 {
3443     int i;
3444     for (i = 0; i < ui->num_elements; i++)
3445     {
3446         Z_OtherInformationUnit *unit = ui->list[i];
3447         if (unit->which == Z_OtherInfo_externallyDefinedInfo &&
3448             unit->information.externallyDefinedInfo &&
3449             unit->information.externallyDefinedInfo->which ==
3450             Z_External_diag1) 
3451         {
3452             interpret_init_diag(c, unit->information.externallyDefinedInfo->u.diag1);
3453         } 
3454     }
3455 }
3456
3457
3458 static void set_init_option(const char *name, void *clientData) {
3459     ZOOM_connection c = clientData;
3460     char buf[80];
3461
3462     sprintf(buf, "init_opt_%.70s", name);
3463     ZOOM_connection_option_set(c, buf, "1");
3464 }
3465
3466
3467 static void recv_apdu(ZOOM_connection c, Z_APDU *apdu)
3468 {
3469     Z_InitResponse *initrs;
3470     
3471     ZOOM_connection_set_mask(c, 0);
3472     yaz_log(log_details, "%p recv_apdu apdu->which=%d", c, apdu->which);
3473     switch(apdu->which)
3474     {
3475     case Z_APDU_initResponse:
3476         yaz_log(log_api, "%p recv_apd: Received Init response", c);
3477         initrs = apdu->u.initResponse;
3478         ZOOM_connection_option_set(c, "serverImplementationId",
3479                                    initrs->implementationId ?
3480                                    initrs->implementationId : "");
3481         ZOOM_connection_option_set(c, "serverImplementationName",
3482                                    initrs->implementationName ?
3483                                    initrs->implementationName : "");
3484         ZOOM_connection_option_set(c, "serverImplementationVersion",
3485                                    initrs->implementationVersion ?
3486                                    initrs->implementationVersion : "");
3487         /* Set the three old options too, for old applications */
3488         ZOOM_connection_option_set(c, "targetImplementationId",
3489                                    initrs->implementationId ?
3490                                    initrs->implementationId : "");
3491         ZOOM_connection_option_set(c, "targetImplementationName",
3492                                    initrs->implementationName ?
3493                                    initrs->implementationName : "");
3494         ZOOM_connection_option_set(c, "targetImplementationVersion",
3495                                    initrs->implementationVersion ?
3496                                    initrs->implementationVersion : "");
3497
3498         /* Make initrs->options available as ZOOM-level options */
3499         yaz_init_opt_decode(initrs->options, set_init_option, (void*) c);
3500
3501         if (!*initrs->result)
3502         {
3503             Z_External *uif = initrs->userInformationField;
3504
3505             set_ZOOM_error(c, ZOOM_ERROR_INIT, 0); /* default error */
3506
3507             if (uif && uif->which == Z_External_userInfo1)
3508                 interpret_otherinformation_field(c, uif->u.userInfo1);
3509         }
3510         else
3511         {
3512             const int *oid = yaz_string_to_oid(yaz_oid_std(),
3513                                                CLASS_USERINFO,
3514                                                OID_STR_COOKIE);
3515             char *cookie =
3516                 yaz_oi_get_string_oid(&apdu->u.initResponse->otherInfo,
3517                                       oid, 1, 0);
3518             xfree(c->cookie_in);
3519             c->cookie_in = 0;
3520             if (cookie)
3521                 c->cookie_in = xstrdup(cookie);
3522             if (ODR_MASK_GET(initrs->options, Z_Options_namedResultSets) &&
3523                 ODR_MASK_GET(initrs->protocolVersion, Z_ProtocolVersion_3))
3524                 c->support_named_resultsets = 1;
3525             if (c->tasks)
3526             {
3527                 assert(c->tasks->which == ZOOM_TASK_CONNECT);
3528                 ZOOM_connection_remove_task(c);
3529             }
3530             ZOOM_connection_exec_task(c);
3531         }
3532         if (ODR_MASK_GET(initrs->options, Z_Options_negotiationModel))
3533         {
3534             NMEM tmpmem = nmem_create();
3535             Z_CharSetandLanguageNegotiation *p =
3536                 yaz_get_charneg_record(initrs->otherInfo);
3537             
3538             if (p)
3539             {
3540                 char *charset = NULL, *lang = NULL;
3541                 int sel;
3542                 
3543                 yaz_get_response_charneg(tmpmem, p, &charset, &lang, &sel);
3544                 yaz_log(log_details, "%p recv_apdu target accepted: "
3545                         "charset %s, language %s, select %d",
3546                         c,
3547                         charset ? charset : "none", lang ? lang : "none", sel);
3548                 if (charset)
3549                     ZOOM_connection_option_set(c, "negotiation-charset",
3550                                                charset);
3551                 if (lang)
3552                     ZOOM_connection_option_set(c, "negotiation-lang",
3553                                                lang);
3554
3555                 ZOOM_connection_option_set(
3556                     c,  "negotiation-charset-in-effect-for-records",
3557                     (sel != 0) ? "1" : "0");
3558                 nmem_destroy(tmpmem);
3559             }
3560         }       
3561         break;
3562     case Z_APDU_searchResponse:
3563         yaz_log(log_api, "%p recv_apdu Search response", c);
3564         handle_search_response(c, apdu->u.searchResponse);
3565         if (send_sort_present(c) == zoom_complete)
3566             ZOOM_connection_remove_task(c);
3567         break;
3568     case Z_APDU_presentResponse:
3569         yaz_log(log_api, "%p recv_apdu Present response", c);
3570         handle_present_response(c, apdu->u.presentResponse);
3571         if (send_present(c) == zoom_complete)
3572             ZOOM_connection_remove_task(c);
3573         break;
3574     case Z_APDU_sortResponse:
3575         yaz_log(log_api, "%p recv_apdu Sort response", c);
3576         sort_response(c, apdu->u.sortResponse);
3577         if (send_present(c) == zoom_complete)
3578             ZOOM_connection_remove_task(c);
3579         break;
3580     case Z_APDU_scanResponse:
3581         yaz_log(log_api, "%p recv_apdu Scan response", c);
3582         scan_response(c, apdu->u.scanResponse);
3583         ZOOM_connection_remove_task(c);
3584         break;
3585     case Z_APDU_extendedServicesResponse:
3586         yaz_log(log_api, "%p recv_apdu Extended Services response", c);
3587         es_response(c, apdu->u.extendedServicesResponse);
3588         ZOOM_connection_remove_task(c);
3589         break;
3590     case Z_APDU_close:
3591         yaz_log(log_api, "%p recv_apdu Close PDU", c);
3592         if (!ZOOM_test_reconnect(c))
3593         {
3594             set_ZOOM_error(c, ZOOM_ERROR_CONNECTION_LOST, c->host_port);
3595             do_close(c);
3596         }
3597         break;
3598     default:
3599         yaz_log(log_api, "%p Received unknown PDU", c);
3600         set_ZOOM_error(c, ZOOM_ERROR_DECODE, 0);
3601         do_close(c);
3602     }
3603 }
3604
3605 #if YAZ_HAVE_XML2
3606 static void handle_srw_response(ZOOM_connection c,
3607                                 Z_SRW_searchRetrieveResponse *res)
3608 {
3609     ZOOM_resultset resultset = 0;
3610     int i;
3611     NMEM nmem;
3612     ZOOM_Event event;
3613     int *start;
3614     const char *syntax, *elementSetName;
3615
3616     if (!c->tasks)
3617         return;
3618
3619     switch(c->tasks->which)
3620     {
3621     case ZOOM_TASK_SEARCH:
3622         resultset = c->tasks->u.search.resultset;
3623         start = &c->tasks->u.search.start;
3624         syntax = c->tasks->u.search.syntax;
3625         elementSetName = c->tasks->u.search.elementSetName;        
3626         break;
3627     case ZOOM_TASK_RETRIEVE:
3628         resultset = c->tasks->u.retrieve.resultset;
3629         start = &c->tasks->u.retrieve.start;
3630         syntax = c->tasks->u.retrieve.syntax;
3631         elementSetName = c->tasks->u.retrieve.elementSetName;
3632         break;
3633     default:
3634         return;
3635     }
3636     event = ZOOM_Event_create(ZOOM_EVENT_RECV_SEARCH);
3637     ZOOM_connection_put_event(c, event);
3638
3639     resultset->size = 0;
3640
3641     yaz_log(log_details, "%p handle_srw_response got SRW response OK", c);
3642     
3643     if (res->numberOfRecords)
3644         resultset->size = *res->numberOfRecords;
3645
3646     for (i = 0; i<res->num_records; i++)
3647     {
3648         int pos;
3649
3650         Z_NamePlusRecord *npr = (Z_NamePlusRecord *)
3651             odr_malloc(c->odr_in, sizeof(Z_NamePlusRecord));
3652
3653         if (res->records[i].recordPosition && 
3654             *res->records[i].recordPosition > 0)
3655             pos = *res->records[i].recordPosition - 1;
3656         else
3657             pos = *start + i;
3658         
3659         npr->databaseName = 0;
3660         npr->which = Z_NamePlusRecord_databaseRecord;
3661         npr->u.databaseRecord = (Z_External *)
3662             odr_malloc(c->odr_in, sizeof(Z_External));
3663         npr->u.databaseRecord->descriptor = 0;
3664         npr->u.databaseRecord->direct_reference =
3665             yaz_string_to_oid_odr(yaz_oid_std(), CLASS_RECSYN, OID_STR_XML,
3666                                   c->odr_in);
3667         npr->u.databaseRecord->which = Z_External_octet;
3668
3669         npr->u.databaseRecord->u.octet_aligned = (Odr_oct *)
3670             odr_malloc(c->odr_in, sizeof(Odr_oct));
3671         npr->u.databaseRecord->u.octet_aligned->buf = (unsigned char*)
3672             res->records[i].recordData_buf;
3673         npr->u.databaseRecord->u.octet_aligned->len = 
3674             npr->u.databaseRecord->u.octet_aligned->size = 
3675             res->records[i].recordData_len;
3676         record_cache_add(resultset, npr, pos, syntax, elementSetName);
3677     }
3678     if (res->num_diagnostics > 0)
3679     {
3680         const char *uri = res->diagnostics[0].uri;
3681         if (uri)
3682         {
3683             int code = 0;       
3684             const char *cp;
3685             if ((cp = strrchr(uri, '/')))
3686                 code = atoi(cp+1);
3687             set_dset_error(c, code, uri,
3688                            res->diagnostics[0].details, 0);
3689         }
3690     }
3691     nmem = odr_extract_mem(c->odr_in);
3692     nmem_transfer(odr_getmem(resultset->odr), nmem);
3693     nmem_destroy(nmem);
3694 }
3695 #endif
3696
3697 #if YAZ_HAVE_XML2
3698 static void handle_http(ZOOM_connection c, Z_HTTP_Response *hres)
3699 {
3700     int ret = -1;
3701     const char *content_type = z_HTTP_header_lookup(hres->headers,
3702                                                     "Content-Type");
3703     const char *connection_head = z_HTTP_header_lookup(hres->headers,
3704                                                        "Connection");
3705     ZOOM_connection_set_mask(c, 0);
3706     yaz_log(log_details, "%p handle_http", c);
3707
3708     if (content_type && !yaz_strcmp_del("text/xml", content_type, "; "))
3709     {
3710         Z_SOAP *soap_package = 0;
3711         ODR o = c->odr_in;
3712         Z_SOAP_Handler soap_handlers[2] = {
3713             {YAZ_XMLNS_SRU_v1_1, 0, (Z_SOAP_fun) yaz_srw_codec},
3714             {0, 0, 0}
3715         };
3716         ret = z_soap_codec(o, &soap_package,
3717                            &hres->content_buf, &hres->content_len,
3718                            soap_handlers);
3719         if (!ret && soap_package->which == Z_SOAP_generic &&
3720             soap_package->u.generic->no == 0)
3721         {
3722             Z_SRW_PDU *sr = (Z_SRW_PDU*) soap_package->u.generic->p;
3723             if (sr->which == Z_SRW_searchRetrieve_response)
3724                 handle_srw_response(c, sr->u.response);
3725             else
3726                 ret = -1;
3727         }
3728         else if (!ret && (soap_package->which == Z_SOAP_fault
3729                           || soap_package->which == Z_SOAP_error))
3730         {
3731             set_HTTP_error(c, hres->code,
3732                            soap_package->u.fault->fault_code,
3733                            soap_package->u.fault->fault_string);
3734         }
3735         else
3736             ret = -1;
3737     }
3738     if (ret)
3739     {
3740         if (hres->code != 200)
3741             set_HTTP_error(c, hres->code, 0, 0);
3742         else
3743             set_ZOOM_error(c, ZOOM_ERROR_DECODE, 0);
3744         do_close(c);
3745     }
3746     ZOOM_connection_remove_task(c);
3747     if (!strcmp(hres->version, "1.0"))
3748     {
3749         /* HTTP 1.0: only if Keep-Alive we stay alive.. */
3750         if (!connection_head || strcmp(connection_head, "Keep-Alive"))
3751             do_close(c);
3752     }
3753     else 
3754     {
3755         /* HTTP 1.1: only if no close we stay alive .. */
3756         if (connection_head && !strcmp(connection_head, "close"))
3757             do_close(c);
3758     }
3759 }
3760 #endif
3761
3762 static int do_read(ZOOM_connection c)
3763 {
3764     int r, more;
3765     ZOOM_Event event;
3766     
3767     event = ZOOM_Event_create(ZOOM_EVENT_RECV_DATA);
3768     ZOOM_connection_put_event(c, event);
3769     
3770     r = cs_get(c->cs, &c->buf_in, &c->len_in);
3771     more = cs_more(c->cs);
3772     yaz_log(log_details, "%p do_read len=%d more=%d", c, r, more);
3773     if (r == 1)
3774         return 0;
3775     if (r <= 0)
3776     {
3777         if (!ZOOM_test_reconnect(c))
3778         {
3779             set_ZOOM_error(c, ZOOM_ERROR_CONNECTION_LOST, c->host_port);
3780             do_close(c);
3781         }
3782     }
3783     else
3784     {
3785         Z_GDU *gdu;
3786         ZOOM_Event event;
3787
3788         odr_reset(c->odr_in);
3789         odr_setbuf(c->odr_in, c->buf_in, r, 0);
3790         event = ZOOM_Event_create(ZOOM_EVENT_RECV_APDU);
3791         ZOOM_connection_put_event(c, event);
3792
3793         if (!z_GDU(c->odr_in, &gdu, 0, 0))
3794         {
3795             int x;
3796             int err = odr_geterrorx(c->odr_in, &x);
3797             char msg[60];
3798             const char *element = odr_getelement(c->odr_in);
3799             sprintf(msg, "ODR code %d:%d element=%-20s",
3800                     err, x, element ? element : "<unknown>");
3801             set_ZOOM_error(c, ZOOM_ERROR_DECODE, msg);
3802             do_close(c);
3803         }
3804         else if (gdu->which == Z_GDU_Z3950)
3805             recv_apdu(c, gdu->u.z3950);
3806         else if (gdu->which == Z_GDU_HTTP_Response)
3807         {
3808 #if YAZ_HAVE_XML2
3809             handle_http(c, gdu->u.HTTP_Response);
3810 #else
3811             set_ZOOM_error(c, ZOOM_ERROR_DECODE, 0);
3812             do_close(c);
3813 #endif
3814         }
3815         c->reconnect_ok = 0;
3816     }
3817     return 1;
3818 }
3819
3820 static zoom_ret do_write_ex(ZOOM_connection c, char *buf_out, int len_out)
3821 {
3822     int r;
3823     ZOOM_Event event;
3824     
3825     event = ZOOM_Event_create(ZOOM_EVENT_SEND_DATA);
3826     ZOOM_connection_put_event(c, event);
3827
3828     yaz_log(log_details, "%p do_write_ex len=%d", c, len_out);
3829     if ((r = cs_put(c->cs, buf_out, len_out)) < 0)
3830     {
3831         yaz_log(log_details, "%p do_write_ex write failed", c);
3832         if (ZOOM_test_reconnect(c))
3833         {
3834             return zoom_pending;
3835         }
3836         if (c->state == STATE_CONNECTING)
3837             set_ZOOM_error(c, ZOOM_ERROR_CONNECT, c->host_port);
3838         else
3839             set_ZOOM_error(c, ZOOM_ERROR_CONNECTION_LOST, c->host_port);
3840         do_close(c);
3841         return zoom_complete;
3842     }
3843     else if (r == 1)
3844     {    
3845         int mask = ZOOM_SELECT_EXCEPT;
3846         if (c->cs->io_pending & CS_WANT_WRITE)
3847             mask += ZOOM_SELECT_WRITE;
3848         if (c->cs->io_pending & CS_WANT_READ)
3849             mask += ZOOM_SELECT_READ;
3850         ZOOM_connection_set_mask(c, mask);
3851         yaz_log(log_details, "%p do_write_ex write incomplete mask=%d",
3852                 c, c->mask);
3853     }
3854     else
3855     {
3856         ZOOM_connection_set_mask(c, ZOOM_SELECT_READ|ZOOM_SELECT_EXCEPT);
3857         yaz_log(log_details, "%p do_write_ex write complete mask=%d",
3858                 c, c->mask);
3859     }
3860     return zoom_pending;
3861 }
3862
3863 static zoom_ret do_write(ZOOM_connection c)
3864 {
3865     return do_write_ex(c, c->buf_out, c->len_out);
3866 }
3867
3868
3869 ZOOM_API(const char *)
3870     ZOOM_connection_option_get(ZOOM_connection c, const char *key)
3871 {
3872     return ZOOM_options_get(c->options, key);
3873 }
3874
3875 ZOOM_API(const char *)
3876     ZOOM_connection_option_getl(ZOOM_connection c, const char *key, int *lenp)
3877 {
3878     return ZOOM_options_getl(c->options, key, lenp);
3879 }
3880
3881 ZOOM_API(void)
3882     ZOOM_connection_option_set(ZOOM_connection c, const char *key,
3883                                const char *val)
3884 {
3885     ZOOM_options_set(c->options, key, val);
3886 }
3887
3888 ZOOM_API(void)
3889     ZOOM_connection_option_setl(ZOOM_connection c, const char *key,
3890                                 const char *val, int len)
3891 {
3892     ZOOM_options_setl(c->options, key, val, len);
3893 }
3894
3895 ZOOM_API(const char *)
3896     ZOOM_resultset_option_get(ZOOM_resultset r, const char *key)
3897 {
3898     return ZOOM_options_get(r->options, key);
3899 }
3900
3901 ZOOM_API(void)
3902     ZOOM_resultset_option_set(ZOOM_resultset r, const char *key,
3903                               const char *val)
3904 {
3905     ZOOM_options_set(r->options, key, val);
3906 }
3907
3908
3909 ZOOM_API(int)
3910     ZOOM_connection_errcode(ZOOM_connection c)
3911 {
3912     return ZOOM_connection_error(c, 0, 0);
3913 }
3914
3915 ZOOM_API(const char *)
3916     ZOOM_connection_errmsg(ZOOM_connection c)
3917 {
3918     const char *msg;
3919     ZOOM_connection_error(c, &msg, 0);
3920     return msg;
3921 }
3922
3923 ZOOM_API(const char *)
3924     ZOOM_connection_addinfo(ZOOM_connection c)
3925 {
3926     const char *addinfo;
3927     ZOOM_connection_error(c, 0, &addinfo);
3928     return addinfo;
3929 }
3930
3931 ZOOM_API(const char *)
3932     ZOOM_connection_diagset(ZOOM_connection c)
3933 {
3934     const char *diagset;
3935     ZOOM_connection_error_x(c, 0, 0, &diagset);
3936     return diagset;
3937 }
3938
3939 ZOOM_API(const char *)
3940     ZOOM_diag_str(int error)
3941 {
3942     switch (error)
3943     {
3944     case ZOOM_ERROR_NONE:
3945         return "No error";
3946     case ZOOM_ERROR_CONNECT:
3947         return "Connect failed";
3948     case ZOOM_ERROR_MEMORY:
3949         return "Out of memory";
3950     case ZOOM_ERROR_ENCODE:
3951         return "Encoding failed";
3952     case ZOOM_ERROR_DECODE:
3953         return "Decoding failed";
3954     case ZOOM_ERROR_CONNECTION_LOST:
3955         return "Connection lost";
3956     case ZOOM_ERROR_INIT:
3957         return "Init rejected";
3958     case ZOOM_ERROR_INTERNAL:
3959         return "Internal failure";
3960     case ZOOM_ERROR_TIMEOUT:
3961         return "Timeout";
3962     case ZOOM_ERROR_UNSUPPORTED_PROTOCOL:
3963         return "Unsupported protocol";
3964     case ZOOM_ERROR_UNSUPPORTED_QUERY:
3965         return "Unsupported query type";
3966     case ZOOM_ERROR_INVALID_QUERY:
3967         return "Invalid query";
3968     case ZOOM_ERROR_CQL_PARSE:
3969         return "CQL parsing error";
3970     case ZOOM_ERROR_CQL_TRANSFORM:
3971         return "CQL transformation error";
3972     case ZOOM_ERROR_CCL_CONFIG:
3973         return "CCL configuration error";
3974     case ZOOM_ERROR_CCL_PARSE:
3975         return "CCL parsing error";
3976     default:
3977         return diagbib1_str(error);
3978     }
3979 }
3980
3981 ZOOM_API(int)
3982     ZOOM_connection_error_x(ZOOM_connection c, const char **cp,
3983                             const char **addinfo, const char **diagset)
3984 {
3985     int error = c->error;
3986     if (cp)
3987     {
3988         if (!c->diagset || !strcmp(c->diagset, "ZOOM"))
3989             *cp = ZOOM_diag_str(error);
3990         else if (!strcmp(c->diagset, "HTTP"))
3991             *cp = z_HTTP_errmsg(c->error);
3992         else if (!strcmp(c->diagset, "Bib-1"))
3993             *cp = ZOOM_diag_str(error);
3994         else if (!strcmp(c->diagset, "info:srw/diagnostic/1"))
3995             *cp = yaz_diag_srw_str(c->error);
3996         else
3997             *cp = "Unknown error and diagnostic set";
3998     }
3999     if (addinfo)
4000         *addinfo = c->addinfo ? c->addinfo : "";
4001     if (diagset)
4002         *diagset = c->diagset ? c->diagset : "";
4003     return c->error;
4004 }
4005
4006 ZOOM_API(int)
4007     ZOOM_connection_error(ZOOM_connection c, const char **cp,
4008                           const char **addinfo)
4009 {
4010     return ZOOM_connection_error_x(c, cp, addinfo, 0);
4011 }
4012
4013 static void ZOOM_connection_do_io(ZOOM_connection c, int mask)
4014 {
4015     ZOOM_Event event = 0;
4016     int r = cs_look(c->cs);
4017     yaz_log(log_details, "%p ZOOM_connection_do_io mask=%d cs_look=%d",
4018             c, mask, r);
4019     
4020     if (r == CS_NONE)
4021     {
4022         event = ZOOM_Event_create(ZOOM_EVENT_CONNECT);
4023         set_ZOOM_error(c, ZOOM_ERROR_CONNECT, c->host_port);
4024         do_close(c);
4025         ZOOM_connection_put_event(c, event);
4026     }
4027     else if (r == CS_CONNECT)
4028     {
4029         int ret = ret = cs_rcvconnect(c->cs);
4030         yaz_log(log_details, "%p ZOOM_connection_do_io "
4031                 "cs_rcvconnect returned %d", c, ret);
4032         if (ret == 1)
4033         {
4034             int mask = ZOOM_SELECT_EXCEPT;
4035             if (c->cs->io_pending & CS_WANT_WRITE)
4036                 mask += ZOOM_SELECT_WRITE;
4037             if (c->cs->io_pending & CS_WANT_READ)
4038                 mask += ZOOM_SELECT_READ;
4039             ZOOM_connection_set_mask(c, mask);
4040         }
4041         else if (ret == 0)
4042         {
4043             event = ZOOM_Event_create(ZOOM_EVENT_CONNECT);
4044             ZOOM_connection_put_event(c, event);
4045             get_cert(c);
4046             if (c->proto == PROTO_Z3950)
4047                 ZOOM_connection_send_init(c);
4048             else
4049             {
4050                 /* no init request for SRW .. */
4051                 assert(c->tasks->which == ZOOM_TASK_CONNECT);
4052                 ZOOM_connection_remove_task(c);
4053                 ZOOM_connection_set_mask(c, 0);
4054                 ZOOM_connection_exec_task(c);
4055             }
4056             c->state = STATE_ESTABLISHED;
4057         }
4058         else
4059         {
4060             set_ZOOM_error(c, ZOOM_ERROR_CONNECT, c->host_port);
4061             do_close(c);
4062         }
4063     }
4064     else
4065     {
4066         if (mask & ZOOM_SELECT_EXCEPT)
4067         {
4068             if (!ZOOM_test_reconnect(c))
4069             {
4070                 set_ZOOM_error(c, ZOOM_ERROR_CONNECTION_LOST, c->host_port);
4071                 do_close(c);
4072             }
4073             return;
4074         }
4075         if (mask & ZOOM_SELECT_READ)
4076             do_read(c);
4077         if (c->cs && (mask & ZOOM_SELECT_WRITE))
4078             do_write(c);
4079     }
4080 }
4081
4082 ZOOM_API(int)
4083     ZOOM_connection_last_event(ZOOM_connection cs)
4084 {
4085     if (!cs)
4086         return ZOOM_EVENT_NONE;
4087     return cs->last_event;
4088 }
4089
4090
4091 static void cql2pqf_wrbuf_puts(const char *buf, void *client_data)
4092 {
4093     WRBUF wrbuf = client_data;
4094     wrbuf_puts(wrbuf, buf);
4095 }
4096
4097 /*
4098  * Returns an xmalloc()d string containing RPN that corresponds to the
4099  * CQL passed in.  On error, sets the Connection object's error state
4100  * and returns a null pointer.
4101  * ### We could cache CQL parser and/or transformer in Connection.
4102  */
4103 static char *cql2pqf(ZOOM_connection c, const char *cql)
4104 {
4105     CQL_parser parser;
4106     int error;
4107     const char *cqlfile;
4108     cql_transform_t trans;
4109     char *result = 0;
4110
4111     parser = cql_parser_create();
4112     if ((error = cql_parser_string(parser, cql)) != 0) {
4113         cql_parser_destroy(parser);
4114         set_ZOOM_error(c, ZOOM_ERROR_CQL_PARSE, cql);
4115         return 0;
4116     }
4117
4118     cqlfile = ZOOM_connection_option_get(c, "cqlfile");
4119     if (cqlfile == 0) 
4120     {
4121         set_ZOOM_error(c, ZOOM_ERROR_CQL_TRANSFORM, "no CQL transform file");
4122     }
4123     else if ((trans = cql_transform_open_fname(cqlfile)) == 0) 
4124     {
4125         char buf[512];        
4126         sprintf(buf, "can't open CQL transform file '%.200s': %.200s",
4127                 cqlfile, strerror(errno));
4128         set_ZOOM_error(c, ZOOM_ERROR_CQL_TRANSFORM, buf);
4129     }
4130     else 
4131     {
4132         WRBUF wrbuf_result = wrbuf_alloc();
4133         error = cql_transform(trans, cql_parser_result(parser),
4134                               cql2pqf_wrbuf_puts, wrbuf_result);
4135         if (error != 0) {
4136             char buf[512];
4137             const char *addinfo;
4138             error = cql_transform_error(trans, &addinfo);
4139             sprintf(buf, "%.200s (addinfo=%.200s)", 
4140                     cql_strerror(error), addinfo);
4141             set_ZOOM_error(c, ZOOM_ERROR_CQL_TRANSFORM, buf);
4142         }
4143         else
4144         {
4145             result = xstrdup(wrbuf_cstr(wrbuf_result));
4146         }
4147         cql_transform_close(trans);
4148         wrbuf_destroy(wrbuf_result);
4149     }
4150     cql_parser_destroy(parser);
4151     return result;
4152 }
4153
4154 ZOOM_API(int) ZOOM_connection_fire_event_timeout(ZOOM_connection c)
4155 {
4156     if (c->mask)
4157     {
4158         ZOOM_Event event = ZOOM_Event_create(ZOOM_EVENT_TIMEOUT);
4159         /* timeout and this connection was waiting */
4160         set_ZOOM_error(c, ZOOM_ERROR_TIMEOUT, 0);
4161         do_close(c);
4162         ZOOM_connection_put_event(c, event);
4163     }
4164     return 0;
4165 }
4166
4167 ZOOM_API(int)
4168     ZOOM_connection_process(ZOOM_connection c)
4169 {
4170     ZOOM_Event event;
4171     if (!c)
4172         return 0;
4173
4174     event = ZOOM_connection_get_event(c);
4175     if (event)
4176     {
4177         ZOOM_Event_destroy(event);
4178         return 1;
4179     }
4180     ZOOM_connection_exec_task(c);
4181     event = ZOOM_connection_get_event(c);
4182     if (event)
4183     {
4184         ZOOM_Event_destroy(event);
4185         return 1;
4186     }
4187     return 0;
4188 }
4189
4190 ZOOM_API(int)
4191     ZOOM_event_nonblock(int no, ZOOM_connection *cs)
4192 {
4193     int i;
4194
4195     yaz_log(log_details, "ZOOM_process_event(no=%d,cs=%p)", no, cs);
4196     
4197     for (i = 0; i<no; i++)
4198     {
4199         ZOOM_connection c = cs[i];
4200
4201         if (c && ZOOM_connection_process(c))
4202             return i+1;
4203     }
4204     return 0;
4205 }
4206
4207 ZOOM_API(int) ZOOM_connection_fire_event_socket(ZOOM_connection c, int mask)
4208 {
4209     if (c->mask && mask)
4210         ZOOM_connection_do_io(c, mask);
4211     return 0;
4212 }
4213
4214 ZOOM_API(int) ZOOM_connection_get_socket(ZOOM_connection c)
4215 {
4216     if (c->cs)
4217         return cs_fileno(c->cs);
4218     return -1;
4219 }
4220
4221 ZOOM_API(int) ZOOM_connection_set_mask(ZOOM_connection c, int mask)
4222 {
4223     c->mask = mask;
4224     if (!c->cs)
4225         return -1; 
4226     return 0;
4227 }
4228
4229 ZOOM_API(int) ZOOM_connection_get_mask(ZOOM_connection c)
4230 {
4231     if (c->cs)
4232         return c->mask;
4233     return 0;
4234 }
4235
4236 ZOOM_API(int) ZOOM_connection_get_timeout(ZOOM_connection c)
4237 {
4238     return ZOOM_options_get_int(c->options, "timeout", 30);
4239 }
4240
4241 /*
4242  * Local variables:
4243  * c-basic-offset: 4
4244  * indent-tabs-mode: nil
4245  * End:
4246  * vim: shiftwidth=4 tabstop=8 expandtab
4247  */
4248