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