Throw out the 'user=pass%hoststring' syntax that I added a few days
[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.55 2005-12-12 12:02:04 mike 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, effective_host);
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.55 $");
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                           term->u.general->buf, term->u.general->len);
2088         break;
2089     case Z_Term_characterString:
2090         ZOOM_options_set(opt, name, term->u.characterString);
2091         break;
2092     case Z_Term_numeric:
2093         ZOOM_options_set_int(opt, name, *term->u.numeric);
2094         break;
2095     }
2096 }
2097
2098 static void handle_queryExpression(ZOOM_options opt, const char *name,
2099                                    Z_QueryExpression *exp)
2100 {
2101     char opt_name[80];
2102     
2103     switch (exp->which)
2104     {
2105     case Z_QueryExpression_term:
2106         if (exp->u.term && exp->u.term->queryTerm)
2107         {
2108             sprintf(opt_name, "%s.term", name);
2109             handle_queryExpressionTerm(opt, opt_name, exp->u.term->queryTerm);
2110         }
2111         break;
2112     case Z_QueryExpression_query:
2113         break;
2114     }
2115 }
2116
2117 static void handle_searchResult(ZOOM_connection c, ZOOM_resultset resultset,
2118                                 Z_OtherInformation *o)
2119 {
2120     int i;
2121     for (i = 0; o && i < o->num_elements; i++)
2122     {
2123         if (o->list[i]->which == Z_OtherInfo_externallyDefinedInfo)
2124         {
2125             Z_External *ext = o->list[i]->information.externallyDefinedInfo;
2126             
2127             if (ext->which == Z_External_searchResult1)
2128             {
2129                 int j;
2130                 Z_SearchInfoReport *sr = ext->u.searchResult1;
2131                 
2132                 if (sr->num)
2133                     ZOOM_options_set_int(
2134                         resultset->options, "searchresult.size", sr->num);
2135
2136                 for (j = 0; j < sr->num; j++)
2137                 {
2138                     Z_SearchInfoReport_s *ent =
2139                         ext->u.searchResult1->elements[j];
2140                     char pref[80];
2141                     
2142                     sprintf(pref, "searchresult.%d", j);
2143
2144                     if (ent->subqueryId)
2145                     {
2146                         char opt_name[80];
2147                         sprintf(opt_name, "%s.id", pref);
2148                         ZOOM_options_set(resultset->options, opt_name,
2149                                          ent->subqueryId);
2150                     }
2151                     if (ent->subqueryExpression)
2152                     {
2153                         char opt_name[80];
2154                         sprintf(opt_name, "%s.subquery", pref);
2155                         handle_queryExpression(resultset->options, opt_name,
2156                                                ent->subqueryExpression);
2157                     }
2158                     if (ent->subqueryInterpretation)
2159                     {
2160                         char opt_name[80];
2161                         sprintf(opt_name, "%s.interpretation", pref);
2162                         handle_queryExpression(resultset->options, opt_name,
2163                                                ent->subqueryInterpretation);
2164                     }
2165                     if (ent->subqueryRecommendation)
2166                     {
2167                         char opt_name[80];
2168                         sprintf(opt_name, "%s.recommendation", pref);
2169                         handle_queryExpression(resultset->options, opt_name,
2170                                                ent->subqueryRecommendation);
2171                     }
2172                     if (ent->subqueryCount)
2173                     {
2174                         char opt_name[80];
2175                         sprintf(opt_name, "%s.count", pref);
2176                         ZOOM_options_set_int(resultset->options, opt_name,
2177                                              *ent->subqueryCount);
2178                     }                                             
2179                 }
2180             }
2181         }
2182     }
2183 }
2184
2185 static void handle_search_response (ZOOM_connection c, Z_SearchResponse *sr)
2186 {
2187     ZOOM_resultset resultset;
2188     ZOOM_Event event;
2189
2190     if (!c->tasks || c->tasks->which != ZOOM_TASK_SEARCH)
2191         return ;
2192
2193     event = ZOOM_Event_create(ZOOM_EVENT_RECV_SEARCH);
2194     ZOOM_connection_put_event(c, event);
2195
2196     resultset = c->tasks->u.search.resultset;
2197
2198     handle_searchResult(c, resultset, sr->additionalSearchInfo);
2199
2200     resultset->size = *sr->resultCount;
2201     handle_records (c, sr->records, 0);
2202 }
2203
2204 static void sort_response (ZOOM_connection c, Z_SortResponse *res)
2205 {
2206     if (res->diagnostics && res->num_diagnostics > 0)
2207         response_diag (c, res->diagnostics[0]);
2208 }
2209
2210 static int scan_response (ZOOM_connection c, Z_ScanResponse *res)
2211 {
2212     NMEM nmem = odr_extract_mem (c->odr_in);
2213     ZOOM_scanset scan;
2214
2215     if (!c->tasks || c->tasks->which != ZOOM_TASK_SCAN)
2216         return 0;
2217     scan = c->tasks->u.scan.scan;
2218
2219     if (res->entries && res->entries->nonsurrogateDiagnostics)
2220         response_diag(c, res->entries->nonsurrogateDiagnostics[0]);
2221     scan->scan_response = res;
2222     nmem_transfer (scan->odr->mem, nmem);
2223     if (res->stepSize)
2224         ZOOM_options_set_int (scan->options, "stepSize", *res->stepSize);
2225     if (res->positionOfTerm)
2226         ZOOM_options_set_int (scan->options, "position", *res->positionOfTerm);
2227     if (res->scanStatus)
2228         ZOOM_options_set_int (scan->options, "scanStatus", *res->scanStatus);
2229     if (res->numberOfEntriesReturned)
2230         ZOOM_options_set_int (scan->options, "number",
2231                               *res->numberOfEntriesReturned);
2232     nmem_destroy (nmem);
2233     return 1;
2234 }
2235
2236 static zoom_ret send_sort (ZOOM_connection c,
2237                            ZOOM_resultset resultset)
2238 {
2239     if (c->error)
2240         resultset->r_sort_spec = 0;
2241     if (resultset->r_sort_spec)
2242     {
2243         Z_APDU *apdu = zget_APDU(c->odr_out, Z_APDU_sortRequest);
2244         Z_SortRequest *req = apdu->u.sortRequest;
2245         
2246         req->num_inputResultSetNames = 1;
2247         req->inputResultSetNames = (Z_InternationalString **)
2248             odr_malloc (c->odr_out, sizeof(*req->inputResultSetNames));
2249         req->inputResultSetNames[0] =
2250             odr_strdup (c->odr_out, resultset->setname);
2251         req->sortedResultSetName = odr_strdup (c->odr_out, resultset->setname);
2252         req->sortSequence = resultset->r_sort_spec;
2253         resultset->r_sort_spec = 0;
2254         return send_APDU (c, apdu);
2255     }
2256     return zoom_complete;
2257 }
2258
2259 static zoom_ret send_present(ZOOM_connection c)
2260 {
2261     Z_APDU *apdu = 0;
2262     Z_PresentRequest *req = 0;
2263     int i = 0;
2264     const char *syntax = 0;
2265     const char *elementSetName = 0;
2266     ZOOM_resultset  resultset;
2267
2268     if (!c->tasks)
2269     {
2270         yaz_log(log_details, "%p send_present no tasks", c);
2271         return zoom_complete;
2272     }
2273     switch (c->tasks->which)
2274     {
2275     case ZOOM_TASK_SEARCH:
2276         resultset = c->tasks->u.search.resultset;
2277         break;
2278     case ZOOM_TASK_RETRIEVE:
2279         resultset = c->tasks->u.retrieve.resultset;
2280         resultset->start = c->tasks->u.retrieve.start;
2281         resultset->count = c->tasks->u.retrieve.count;
2282
2283         if (resultset->start >= resultset->size)
2284         {
2285             yaz_log(log_details, "%p send_present start=%d >= size=%d",
2286                     c, resultset->start, resultset->size);
2287             return zoom_complete;
2288         }
2289         if (resultset->start + resultset->count > resultset->size)
2290             resultset->count = resultset->size - resultset->start;
2291         break;
2292     default:
2293         return zoom_complete;
2294     }
2295     yaz_log(log_details, "%p send_present start=%d count=%d",
2296             c, resultset->start, resultset->count);
2297
2298     syntax = ZOOM_resultset_option_get (resultset, "preferredRecordSyntax");
2299     elementSetName = ZOOM_resultset_option_get (resultset, "elementSetName");
2300
2301     if (c->error)                  /* don't continue on error */
2302         return zoom_complete;
2303     if (resultset->start < 0)
2304         return zoom_complete;
2305     for (i = 0; i<resultset->count; i++)
2306     {
2307         ZOOM_record rec =
2308             record_cache_lookup (resultset, i + resultset->start);
2309         if (!rec)
2310             break;
2311     }
2312     if (i == resultset->count)
2313     {
2314         yaz_log(log_details, "%p send_present skip=%d no more to fetch", c, i);
2315         return zoom_complete;
2316     }
2317
2318     apdu = zget_APDU(c->odr_out, Z_APDU_presentRequest);
2319     req = apdu->u.presentRequest;
2320
2321     resultset->start += i;
2322     resultset->count -= i;
2323
2324     if (i)
2325         yaz_log(log_details, "%p send_present skip=%d", c, i);
2326
2327     *req->resultSetStartPoint = resultset->start + 1;
2328     *req->numberOfRecordsRequested = resultset->step>0 ?
2329         resultset->step : resultset->count;
2330     assert (*req->numberOfRecordsRequested > 0);
2331
2332
2333     if (syntax && *syntax)
2334         req->preferredRecordSyntax =
2335             yaz_str_to_z3950oid (c->odr_out, CLASS_RECSYN, syntax);
2336
2337     if (resultset->schema && *resultset->schema)
2338     {
2339         Z_RecordComposition *compo = (Z_RecordComposition *)
2340             odr_malloc (c->odr_out, sizeof(*compo));
2341
2342         req->recordComposition = compo;
2343         compo->which = Z_RecordComp_complex;
2344         compo->u.complex = (Z_CompSpec *)
2345             odr_malloc(c->odr_out, sizeof(*compo->u.complex));
2346         compo->u.complex->selectAlternativeSyntax = (bool_t *) 
2347             odr_malloc(c->odr_out, sizeof(bool_t));
2348         *compo->u.complex->selectAlternativeSyntax = 0;
2349
2350         compo->u.complex->generic = (Z_Specification *)
2351             odr_malloc(c->odr_out, sizeof(*compo->u.complex->generic));
2352
2353         compo->u.complex->generic->which = Z_Schema_oid;
2354         compo->u.complex->generic->schema.oid = (Odr_oid *)
2355             yaz_str_to_z3950oid (c->odr_out, CLASS_SCHEMA, resultset->schema);
2356
2357         if (!compo->u.complex->generic->schema.oid)
2358         {
2359             /* OID wasn't a schema! Try record syntax instead. */
2360
2361             compo->u.complex->generic->schema.oid = (Odr_oid *)
2362                 yaz_str_to_z3950oid (c->odr_out, CLASS_RECSYN, resultset->schema);
2363         }
2364         if (elementSetName && *elementSetName)
2365         {
2366             compo->u.complex->generic->elementSpec = (Z_ElementSpec *)
2367                 odr_malloc(c->odr_out, sizeof(Z_ElementSpec));
2368             compo->u.complex->generic->elementSpec->which =
2369                 Z_ElementSpec_elementSetName;
2370             compo->u.complex->generic->elementSpec->u.elementSetName =
2371                 odr_strdup (c->odr_out, elementSetName);
2372         }
2373         else
2374             compo->u.complex->generic->elementSpec = 0;
2375         compo->u.complex->num_dbSpecific = 0;
2376         compo->u.complex->dbSpecific = 0;
2377         compo->u.complex->num_recordSyntax = 0;
2378         compo->u.complex->recordSyntax = 0;
2379     }
2380     else if (elementSetName && *elementSetName)
2381     {
2382         Z_ElementSetNames *esn = (Z_ElementSetNames *)
2383             odr_malloc (c->odr_out, sizeof(*esn));
2384         Z_RecordComposition *compo = (Z_RecordComposition *)
2385             odr_malloc (c->odr_out, sizeof(*compo));
2386         
2387         esn->which = Z_ElementSetNames_generic;
2388         esn->u.generic = odr_strdup (c->odr_out, elementSetName);
2389         compo->which = Z_RecordComp_simple;
2390         compo->u.simple = esn;
2391         req->recordComposition = compo;
2392     }
2393     req->resultSetId = odr_strdup(c->odr_out, resultset->setname);
2394     return send_APDU (c, apdu);
2395 }
2396
2397 ZOOM_API(ZOOM_scanset)
2398 ZOOM_connection_scan (ZOOM_connection c, const char *start)
2399 {
2400     ZOOM_scanset scan = (ZOOM_scanset) xmalloc (sizeof(*scan));
2401
2402     scan->connection = c;
2403     scan->odr = odr_createmem (ODR_DECODE);
2404     scan->options = ZOOM_options_create_with_parent (c->options);
2405     scan->refcount = 1;
2406     scan->scan_response = 0;
2407
2408     if ((scan->termListAndStartPoint =
2409          p_query_scan(scan->odr, PROTO_Z3950, &scan->attributeSet,
2410                       start)))
2411     {
2412         ZOOM_task task = ZOOM_connection_add_task (c, ZOOM_TASK_SCAN);
2413         task->u.scan.scan = scan;
2414         
2415         (scan->refcount)++;
2416         if (!c->async)
2417         {
2418             while (ZOOM_event (1, &c))
2419                 ;
2420         }
2421     }
2422     return scan;
2423 }
2424
2425 ZOOM_API(void)
2426 ZOOM_scanset_destroy (ZOOM_scanset scan)
2427 {
2428     if (!scan)
2429         return;
2430     (scan->refcount)--;
2431     if (scan->refcount == 0)
2432     {
2433         odr_destroy (scan->odr);
2434         
2435         ZOOM_options_destroy (scan->options);
2436         xfree (scan);
2437     }
2438 }
2439
2440 static zoom_ret send_package (ZOOM_connection c)
2441 {
2442     ZOOM_Event event;
2443
2444     yaz_log(log_details, "%p send_package", c);
2445     if (!c->tasks)
2446         return zoom_complete;
2447     assert (c->tasks->which == ZOOM_TASK_PACKAGE);
2448     
2449     event = ZOOM_Event_create (ZOOM_EVENT_SEND_APDU);
2450     ZOOM_connection_put_event (c, event);
2451     
2452     return do_write_ex (c, c->tasks->u.package->buf_out,
2453                         c->tasks->u.package->len_out);
2454 }
2455
2456 static zoom_ret send_scan (ZOOM_connection c)
2457 {
2458     ZOOM_scanset scan;
2459     Z_APDU *apdu = zget_APDU(c->odr_out, Z_APDU_scanRequest);
2460     Z_ScanRequest *req = apdu->u.scanRequest;
2461
2462     yaz_log(log_details, "%p send_scan", c);
2463     if (!c->tasks)
2464         return zoom_complete;
2465     assert (c->tasks->which == ZOOM_TASK_SCAN);
2466     scan = c->tasks->u.scan.scan;
2467
2468     req->termListAndStartPoint = scan->termListAndStartPoint;
2469     req->attributeSet = scan->attributeSet;
2470
2471     *req->numberOfTermsRequested =
2472         ZOOM_options_get_int(scan->options, "number", 10);
2473
2474     req->preferredPositionInResponse =
2475         odr_intdup (c->odr_out,
2476                     ZOOM_options_get_int(scan->options, "position", 1));
2477
2478     req->stepSize =
2479         odr_intdup (c->odr_out,
2480                     ZOOM_options_get_int(scan->options, "stepSize", 0));
2481     
2482     req->databaseNames = set_DatabaseNames (c, scan->options, 
2483                                             &req->num_databaseNames);
2484
2485     return send_APDU (c, apdu);
2486 }
2487
2488 ZOOM_API(size_t)
2489 ZOOM_scanset_size (ZOOM_scanset scan)
2490 {
2491     if (!scan || !scan->scan_response || !scan->scan_response->entries)
2492         return 0;
2493     return scan->scan_response->entries->num_entries;
2494 }
2495
2496 ZOOM_API(const char *)
2497 ZOOM_scanset_term (ZOOM_scanset scan, size_t pos,
2498                    int *occ, int *len)
2499 {
2500     const char *term = 0;
2501     size_t noent = ZOOM_scanset_size (scan);
2502     Z_ScanResponse *res = scan->scan_response;
2503     
2504     *len = 0;
2505     *occ = 0;
2506     if (pos >= noent)
2507         return 0;
2508     if (res->entries->entries[pos]->which == Z_Entry_termInfo)
2509     {
2510         Z_TermInfo *t = res->entries->entries[pos]->u.termInfo;
2511         
2512         if (t->term->which == Z_Term_general)
2513         {
2514             term = (const char *) t->term->u.general->buf;
2515             *len = t->term->u.general->len;
2516         }
2517         *occ = t->globalOccurrences ? *t->globalOccurrences : 0;
2518     }
2519     return term;
2520 }
2521
2522 ZOOM_API(const char *)
2523 ZOOM_scanset_display_term (ZOOM_scanset scan, size_t pos,
2524                            int *occ, int *len)
2525 {
2526     const char *term = 0;
2527     size_t noent = ZOOM_scanset_size (scan);
2528     Z_ScanResponse *res = scan->scan_response;
2529     
2530     *len = 0;
2531     *occ = 0;
2532     if (pos >= noent)
2533         return 0;
2534     if (res->entries->entries[pos]->which == Z_Entry_termInfo)
2535     {
2536         Z_TermInfo *t = res->entries->entries[pos]->u.termInfo;
2537
2538         if (t->displayTerm)
2539         {
2540             term = t->displayTerm;
2541             *len = strlen(term);
2542         }
2543         else if (t->term->which == Z_Term_general)
2544         {
2545             term = (const char *) t->term->u.general->buf;
2546             *len = t->term->u.general->len;
2547         }
2548         *occ = t->globalOccurrences ? *t->globalOccurrences : 0;
2549     }
2550     return term;
2551 }
2552
2553 ZOOM_API(const char *)
2554 ZOOM_scanset_option_get (ZOOM_scanset scan, const char *key)
2555 {
2556     return ZOOM_options_get (scan->options, key);
2557 }
2558
2559 ZOOM_API(void)
2560 ZOOM_scanset_option_set (ZOOM_scanset scan, const char *key,
2561                               const char *val)
2562 {
2563     ZOOM_options_set (scan->options, key, val);
2564 }
2565
2566 static Z_APDU *create_es_package (ZOOM_package p, int type)
2567 {
2568     const char *str;
2569     Z_APDU *apdu = zget_APDU(p->odr_out, Z_APDU_extendedServicesRequest);
2570     Z_ExtendedServicesRequest *req = apdu->u.extendedServicesRequest;
2571     
2572     *req->function = Z_ExtendedServicesRequest_create;
2573     
2574     str = ZOOM_options_get(p->options, "package-name");
2575     if (str && *str)
2576         req->packageName = nmem_strdup (p->odr_out->mem, str);
2577     
2578     str = ZOOM_options_get(p->options, "user-id");
2579     if (str)
2580         req->userId = nmem_strdup (p->odr_out->mem, str);
2581     
2582     req->packageType = yaz_oidval_to_z3950oid(p->odr_out, CLASS_EXTSERV,
2583                                               type);
2584
2585     str = ZOOM_options_get(p->options, "function");
2586     if (str)
2587     {
2588         if (!strcmp (str, "create"))
2589             *req->function = 1;
2590         if (!strcmp (str, "delete"))
2591             *req->function = 2;
2592         if (!strcmp (str, "modify"))
2593             *req->function = 3;
2594     }
2595     return apdu;
2596 }
2597
2598 static const char *ill_array_lookup (void *clientData, const char *idx)
2599 {
2600     ZOOM_package p = (ZOOM_package) clientData;
2601     return ZOOM_options_get (p->options, idx+4);
2602 }
2603
2604 static Z_External *encode_ill_request (ZOOM_package p)
2605 {
2606     ODR out = p->odr_out;
2607     ILL_Request *req;
2608     Z_External *r = 0;
2609     struct ill_get_ctl ctl;
2610         
2611     ctl.odr = p->odr_out;
2612     ctl.clientData = p;
2613     ctl.f = ill_array_lookup;
2614         
2615     req = ill_get_ILLRequest(&ctl, "ill", 0);
2616         
2617     if (!ill_Request (out, &req, 0, 0))
2618     {
2619         int ill_request_size;
2620         char *ill_request_buf = odr_getbuf (out, &ill_request_size, 0);
2621         if (ill_request_buf)
2622             odr_setbuf (out, ill_request_buf, ill_request_size, 1);
2623         return 0;
2624     }
2625     else
2626     {
2627         oident oid;
2628         int illRequest_size = 0;
2629         char *illRequest_buf = odr_getbuf (out, &illRequest_size, 0);
2630                 
2631         oid.proto = PROTO_GENERAL;
2632         oid.oclass = CLASS_GENERAL;
2633         oid.value = VAL_ISO_ILL_1;
2634                 
2635         r = (Z_External *) odr_malloc (out, sizeof(*r));
2636         r->direct_reference = odr_oiddup(out,oid_getoidbyent(&oid)); 
2637         r->indirect_reference = 0;
2638         r->descriptor = 0;
2639         r->which = Z_External_single;
2640                 
2641         r->u.single_ASN1_type = (Odr_oct *)
2642             odr_malloc (out, sizeof(*r->u.single_ASN1_type));
2643         r->u.single_ASN1_type->buf = (unsigned char*)
2644             odr_malloc (out, illRequest_size);
2645         r->u.single_ASN1_type->len = illRequest_size;
2646         r->u.single_ASN1_type->size = illRequest_size;
2647         memcpy (r->u.single_ASN1_type->buf, illRequest_buf, illRequest_size);
2648     }
2649     return r;
2650 }
2651
2652 static Z_ItemOrder *encode_item_order(ZOOM_package p)
2653 {
2654     Z_ItemOrder *req = (Z_ItemOrder *) odr_malloc (p->odr_out, sizeof(*req));
2655     const char *str;
2656     
2657     req->which = Z_IOItemOrder_esRequest;
2658     req->u.esRequest = (Z_IORequest *) 
2659         odr_malloc(p->odr_out,sizeof(Z_IORequest));
2660
2661     /* to keep part ... */
2662     req->u.esRequest->toKeep = (Z_IOOriginPartToKeep *)
2663         odr_malloc(p->odr_out,sizeof(Z_IOOriginPartToKeep));
2664     req->u.esRequest->toKeep->supplDescription = 0;
2665     req->u.esRequest->toKeep->contact = (Z_IOContact *)
2666         odr_malloc (p->odr_out, sizeof(*req->u.esRequest->toKeep->contact));
2667         
2668     str = ZOOM_options_get(p->options, "contact-name");
2669     req->u.esRequest->toKeep->contact->name = str ?
2670         nmem_strdup (p->odr_out->mem, str) : 0;
2671         
2672     str = ZOOM_options_get(p->options, "contact-phone");
2673     req->u.esRequest->toKeep->contact->phone = str ?
2674         nmem_strdup (p->odr_out->mem, str) : 0;
2675         
2676     str = ZOOM_options_get(p->options, "contact-email");
2677     req->u.esRequest->toKeep->contact->email = str ?
2678         nmem_strdup (p->odr_out->mem, str) : 0;
2679         
2680     req->u.esRequest->toKeep->addlBilling = 0;
2681         
2682     /* not to keep part ... */
2683     req->u.esRequest->notToKeep = (Z_IOOriginPartNotToKeep *)
2684         odr_malloc(p->odr_out,sizeof(Z_IOOriginPartNotToKeep));
2685         
2686     str = ZOOM_options_get(p->options, "itemorder-setname");
2687     if (!str)
2688         str = "default";
2689
2690     if (!*str) 
2691         req->u.esRequest->notToKeep->resultSetItem = 0;
2692     else
2693     {
2694         req->u.esRequest->notToKeep->resultSetItem = (Z_IOResultSetItem *)
2695            odr_malloc(p->odr_out, sizeof(Z_IOResultSetItem));
2696
2697         req->u.esRequest->notToKeep->resultSetItem->resultSetId =
2698            nmem_strdup (p->odr_out->mem, str);
2699         req->u.esRequest->notToKeep->resultSetItem->item =
2700             (int *) odr_malloc(p->odr_out, sizeof(int));
2701         
2702         str = ZOOM_options_get(p->options, "itemorder-item");
2703         *req->u.esRequest->notToKeep->resultSetItem->item =
2704             (str ? atoi(str) : 1);
2705     }
2706     req->u.esRequest->notToKeep->itemRequest = encode_ill_request(p);
2707     
2708     return req;
2709 }
2710
2711 Z_APDU *create_admin_package(ZOOM_package p, int type, 
2712                              Z_ESAdminOriginPartToKeep **toKeepP,
2713                              Z_ESAdminOriginPartNotToKeep **notToKeepP)
2714 {
2715     Z_APDU *apdu = create_es_package (p, VAL_ADMINSERVICE);
2716     if (apdu)
2717     {
2718         Z_ESAdminOriginPartToKeep  *toKeep;
2719         Z_ESAdminOriginPartNotToKeep  *notToKeep;
2720         Z_External *r = (Z_External *) odr_malloc (p->odr_out, sizeof(*r));
2721         const char *first_db = "Default";
2722         int num_db;
2723         char **db = set_DatabaseNames(p->connection, p->options, &num_db);
2724         if (num_db > 0)
2725             first_db = db[0];
2726             
2727         r->direct_reference =
2728             yaz_oidval_to_z3950oid(p->odr_out, CLASS_EXTSERV,
2729                                    VAL_ADMINSERVICE);
2730         r->descriptor = 0;
2731         r->indirect_reference = 0;
2732         r->which = Z_External_ESAdmin;
2733         
2734         r->u.adminService = (Z_Admin *)
2735             odr_malloc(p->odr_out, sizeof(*r->u.adminService));
2736         r->u.adminService->which = Z_Admin_esRequest;
2737         r->u.adminService->u.esRequest = (Z_AdminEsRequest *)
2738             odr_malloc(p->odr_out, sizeof(*r->u.adminService->u.esRequest));
2739         
2740         toKeep = r->u.adminService->u.esRequest->toKeep =
2741             (Z_ESAdminOriginPartToKeep *) 
2742             odr_malloc(p->odr_out, sizeof(*r->u.adminService->u.esRequest->toKeep));
2743         toKeep->which = type;
2744         toKeep->databaseName = odr_strdup(p->odr_out, first_db);
2745         toKeep->u.create = odr_nullval();
2746         apdu->u.extendedServicesRequest->taskSpecificParameters = r;
2747         
2748         r->u.adminService->u.esRequest->notToKeep = notToKeep =
2749             (Z_ESAdminOriginPartNotToKeep *)
2750             odr_malloc(p->odr_out,
2751                        sizeof(*r->u.adminService->u.esRequest->notToKeep));
2752         notToKeep->which = Z_ESAdminOriginPartNotToKeep_recordsWillFollow;
2753         notToKeep->u.recordsWillFollow = odr_nullval();
2754         if (toKeepP)
2755             *toKeepP = toKeep;
2756         if (notToKeepP)
2757             *notToKeepP = notToKeep;
2758     }
2759     return apdu;
2760 }
2761
2762 static Z_APDU *create_xmlupdate_package(ZOOM_package p)
2763 {
2764     Z_APDU *apdu = create_es_package(p, VAL_XMLES);
2765     Z_ExtendedServicesRequest *req = apdu->u.extendedServicesRequest;
2766     Z_External *ext = (Z_External *) odr_malloc(p->odr_out, sizeof(*ext));
2767     const char *doc = ZOOM_options_get(p->options, "doc");
2768
2769     req->taskSpecificParameters = ext;
2770     ext->direct_reference = req->packageType;
2771     ext->descriptor = 0;
2772     ext->indirect_reference = 0;
2773     
2774     ext->which = Z_External_octet;
2775     ext->u.single_ASN1_type = (Odr_oct *)
2776         odr_malloc (p->odr_out, sizeof(Odr_oct));
2777
2778     if (!doc)
2779         doc = "";
2780     ext->u.single_ASN1_type->buf = (unsigned char*)
2781         odr_strdup(p->odr_out, doc);
2782     ext->u.single_ASN1_type->size = ext->u.single_ASN1_type->len = strlen(doc);
2783     return apdu;
2784 }
2785
2786 static Z_APDU *create_update_package(ZOOM_package p)
2787 {
2788     Z_APDU *apdu = 0;
2789     const char *first_db = "Default";
2790     int num_db;
2791     char **db = set_DatabaseNames(p->connection, p->options, &num_db);
2792     const char *action = ZOOM_options_get(p->options, "action");
2793     const char *recordIdOpaque = ZOOM_options_get(p->options, "recordIdOpaque");
2794     const char *recordIdNumber = ZOOM_options_get(p->options, "recordIdNumber");
2795     const char *record_buf = ZOOM_options_get(p->options, "record");
2796     const char *syntax_str = ZOOM_options_get(p->options, "syntax");
2797     int syntax_oid = VAL_NONE;
2798     int action_no = -1;
2799     
2800     if (syntax_str)
2801         syntax_oid = oid_getvalbyname(syntax_str);
2802     if (!record_buf)
2803     {
2804         record_buf = "void";
2805         syntax_oid = VAL_SUTRS;
2806     }
2807     if (syntax_oid == VAL_NONE)
2808         syntax_oid = VAL_TEXT_XML;
2809     
2810     if (num_db > 0)
2811         first_db = db[0];
2812     
2813     if (!action)
2814         action = "specialUpdate";
2815     
2816     if (!strcmp(action, "recordInsert"))
2817         action_no = Z_IUOriginPartToKeep_recordInsert;
2818     else if (!strcmp(action, "recordReplace"))
2819         action_no = Z_IUOriginPartToKeep_recordReplace;
2820     else if (!strcmp(action, "recordDelete"))
2821         action_no = Z_IUOriginPartToKeep_recordDelete;
2822     else if (!strcmp(action, "elementUpdate"))
2823         action_no = Z_IUOriginPartToKeep_elementUpdate;
2824     else if (!strcmp(action, "specialUpdate"))
2825         action_no = Z_IUOriginPartToKeep_specialUpdate;
2826     else
2827         return 0;
2828
2829     apdu = create_es_package (p, VAL_DBUPDATE);
2830     if (apdu)
2831     {
2832         Z_IUOriginPartToKeep *toKeep;
2833         Z_IUSuppliedRecords *notToKeep;
2834         Z_External *r = (Z_External *)
2835             odr_malloc (p->odr_out, sizeof(*r));
2836         
2837         apdu->u.extendedServicesRequest->taskSpecificParameters = r;
2838         
2839         r->direct_reference =
2840             yaz_oidval_to_z3950oid(p->odr_out, CLASS_EXTSERV,
2841                                    VAL_DBUPDATE);
2842         r->descriptor = 0;
2843         r->which = Z_External_update;
2844         r->indirect_reference = 0;
2845         r->u.update = (Z_IUUpdate *)
2846             odr_malloc(p->odr_out, sizeof(*r->u.update));
2847         
2848         r->u.update->which = Z_IUUpdate_esRequest;
2849         r->u.update->u.esRequest = (Z_IUUpdateEsRequest *)
2850             odr_malloc(p->odr_out, sizeof(*r->u.update->u.esRequest));
2851         toKeep = r->u.update->u.esRequest->toKeep = 
2852             (Z_IUOriginPartToKeep *)
2853             odr_malloc(p->odr_out, sizeof(*toKeep));
2854         
2855         toKeep->databaseName = odr_strdup(p->odr_out, first_db);
2856         toKeep->schema = 0;
2857         toKeep->elementSetName = 0;
2858         toKeep->actionQualifier = 0;
2859         toKeep->action = odr_intdup(p->odr_out, action_no);
2860         
2861         notToKeep = r->u.update->u.esRequest->notToKeep = 
2862             (Z_IUSuppliedRecords *)
2863             odr_malloc(p->odr_out, sizeof(*notToKeep));
2864         notToKeep->num = 1;
2865         notToKeep->elements = (Z_IUSuppliedRecords_elem **)
2866             odr_malloc(p->odr_out, sizeof(*notToKeep->elements));
2867         notToKeep->elements[0] = (Z_IUSuppliedRecords_elem *)
2868             odr_malloc(p->odr_out, sizeof(**notToKeep->elements));
2869         notToKeep->elements[0]->which = Z_IUSuppliedRecords_elem_opaque;
2870         if (recordIdOpaque)
2871         {
2872             notToKeep->elements[0]->u.opaque = (Odr_oct *)
2873                 odr_malloc (p->odr_out, sizeof(Odr_oct));
2874             notToKeep->elements[0]->u.opaque->size =
2875                 notToKeep->elements[0]->u.opaque->len = strlen(recordIdOpaque);
2876             notToKeep->elements[0]->u.opaque->buf = (unsigned char*)
2877                 odr_strdup(p->odr_out, recordIdOpaque);
2878         }
2879         else if (recordIdNumber)
2880         {
2881             notToKeep->elements[0]->which = Z_IUSuppliedRecords_elem_number;
2882             
2883             notToKeep->elements[0]->u.number =
2884                 odr_intdup(p->odr_out, atoi(recordIdNumber));
2885         }
2886         else
2887             notToKeep->elements[0]->u.opaque = 0;
2888         notToKeep->elements[0]->supplementalId = 0;
2889         notToKeep->elements[0]->correlationInfo = 0;
2890         notToKeep->elements[0]->record =
2891             z_ext_record(p->odr_out, syntax_oid,
2892                          record_buf, strlen(record_buf));
2893     }
2894     if (0 && apdu)
2895     {
2896        ODR print = odr_createmem(ODR_PRINT);
2897
2898        z_APDU(print, &apdu, 0, 0);
2899        odr_destroy(print);
2900     }
2901     return apdu;
2902 }
2903
2904 ZOOM_API(void)
2905     ZOOM_package_send (ZOOM_package p, const char *type)
2906 {
2907     Z_APDU *apdu = 0;
2908     ZOOM_connection c;
2909     if (!p)
2910         return;
2911     c = p->connection;
2912     odr_reset (p->odr_out);
2913     xfree (p->buf_out);
2914     p->buf_out = 0;
2915     if (!strcmp(type, "itemorder"))
2916     {
2917         apdu = create_es_package (p, VAL_ITEMORDER);
2918         if (apdu)
2919         {
2920             Z_External *r = (Z_External *) odr_malloc (p->odr_out, sizeof(*r));
2921             
2922             r->direct_reference =
2923                 yaz_oidval_to_z3950oid(p->odr_out, CLASS_EXTSERV,
2924                                        VAL_ITEMORDER);
2925             r->descriptor = 0;
2926             r->which = Z_External_itemOrder;
2927             r->indirect_reference = 0;
2928             r->u.itemOrder = encode_item_order (p);
2929
2930             apdu->u.extendedServicesRequest->taskSpecificParameters = r;
2931         }
2932     }
2933     else if (!strcmp(type, "create"))  /* create database */
2934     {
2935         apdu = create_admin_package(p, Z_ESAdminOriginPartToKeep_create,
2936                                     0, 0);
2937     }   
2938     else if (!strcmp(type, "drop"))  /* drop database */
2939     {
2940         apdu = create_admin_package(p, Z_ESAdminOriginPartToKeep_drop,
2941                                     0, 0);
2942     }
2943     else if (!strcmp(type, "commit"))  /* commit changes */
2944     {
2945         apdu = create_admin_package(p, Z_ESAdminOriginPartToKeep_commit,
2946                                     0, 0);
2947     }
2948     else if (!strcmp(type, "update")) /* update record(s) */
2949     {
2950         apdu = create_update_package(p);
2951     }
2952     else if (!strcmp(type, "xmlupdate"))
2953     {
2954         apdu = create_xmlupdate_package(p);
2955     }
2956     if (apdu)
2957     {
2958         if (encode_APDU(p->connection, apdu, p->odr_out) == 0)
2959         {
2960             char *buf;
2961
2962             ZOOM_task task = ZOOM_connection_add_task (c, ZOOM_TASK_PACKAGE);
2963             task->u.package = p;
2964             buf = odr_getbuf(p->odr_out, &p->len_out, 0);
2965             p->buf_out = (char *) xmalloc (p->len_out);
2966             memcpy (p->buf_out, buf, p->len_out);
2967             
2968             (p->refcount)++;
2969             if (!c->async)
2970             {
2971                 while (ZOOM_event (1, &c))
2972                     ;
2973             }
2974         }
2975     }
2976 }
2977
2978 ZOOM_API(ZOOM_package)
2979     ZOOM_connection_package (ZOOM_connection c, ZOOM_options options)
2980 {
2981     ZOOM_package p = (ZOOM_package) xmalloc (sizeof(*p));
2982
2983     p->connection = c;
2984     p->odr_out = odr_createmem (ODR_ENCODE);
2985     p->options = ZOOM_options_create_with_parent2 (options, c->options);
2986     p->refcount = 1;
2987     p->buf_out = 0;
2988     p->len_out = 0;
2989     return p;
2990 }
2991
2992 ZOOM_API(void)
2993     ZOOM_package_destroy(ZOOM_package p)
2994 {
2995     if (!p)
2996         return;
2997     (p->refcount)--;
2998     if (p->refcount == 0)
2999     {
3000         odr_destroy (p->odr_out);
3001         xfree (p->buf_out);
3002         
3003         ZOOM_options_destroy (p->options);
3004         xfree (p);
3005     }
3006 }
3007
3008 ZOOM_API(const char *)
3009 ZOOM_package_option_get (ZOOM_package p, const char *key)
3010 {
3011     return ZOOM_options_get (p->options, key);
3012 }
3013
3014
3015 ZOOM_API(void)
3016 ZOOM_package_option_set (ZOOM_package p, const char *key,
3017                               const char *val)
3018 {
3019     ZOOM_options_set (p->options, key, val);
3020 }
3021
3022 static int ZOOM_connection_exec_task (ZOOM_connection c)
3023 {
3024     ZOOM_task task = c->tasks;
3025     zoom_ret ret = zoom_complete;
3026
3027     if (!task)
3028         return 0;
3029     yaz_log(log_details, "%p ZOOM_connection_exec_task type=%d run=%d",
3030              c, task->which, task->running);
3031     if (c->error != ZOOM_ERROR_NONE)
3032     {
3033         yaz_log(log_details, "%p ZOOM_connection_exec_task "
3034                 "removing tasks because of error = %d", c, c->error);
3035         ZOOM_connection_remove_tasks (c);
3036         return 0;
3037     }
3038     if (task->running)
3039     {
3040         yaz_log(log_details, "%p ZOOM_connection_exec_task "
3041                  "task already running", c);
3042         return 0;
3043     }
3044     task->running = 1;
3045     ret = zoom_complete;
3046     if (c->cs || task->which == ZOOM_TASK_CONNECT)
3047     {
3048         switch (task->which)
3049         {
3050         case ZOOM_TASK_SEARCH:
3051             if (c->proto == PROTO_HTTP)
3052                 ret = ZOOM_connection_srw_send_search(c);
3053             else
3054                 ret = ZOOM_connection_send_search(c);
3055             break;
3056         case ZOOM_TASK_RETRIEVE:
3057             if (c->proto == PROTO_HTTP)
3058                 ret = ZOOM_connection_srw_send_search(c);
3059             else
3060                 ret = send_present (c);
3061             break;
3062         case ZOOM_TASK_CONNECT:
3063             ret = do_connect(c);
3064             break;
3065         case ZOOM_TASK_SCAN:
3066             ret = send_scan(c);
3067             break;
3068         case ZOOM_TASK_PACKAGE:
3069             ret = send_package(c);
3070             break;
3071         case ZOOM_TASK_SORT:
3072             c->tasks->u.sort.resultset->r_sort_spec = 
3073                 c->tasks->u.sort.q->sort_spec;
3074             ret = send_sort(c, c->tasks->u.sort.resultset);
3075             break;
3076         }
3077     }
3078     else
3079     {
3080         yaz_log(log_details, "%p ZOOM_connection_exec_task "
3081                 "remove tasks because no connection exist", c);
3082         ZOOM_connection_remove_tasks (c);
3083     }
3084     if (ret == zoom_complete)
3085     {
3086         yaz_log(log_details, "%p ZOOM_connection_exec_task "
3087                  "task removed (complete)", c);
3088         ZOOM_connection_remove_task (c);
3089         return 0;
3090     }
3091     yaz_log(log_details, "%p ZOOM_connection_exec_task "
3092             "task pending", c);
3093     return 1;
3094 }
3095
3096 static zoom_ret send_sort_present (ZOOM_connection c)
3097 {
3098     zoom_ret r = zoom_complete;
3099
3100     if (c->tasks && c->tasks->which == ZOOM_TASK_SEARCH)
3101         r = send_sort (c, c->tasks->u.search.resultset);
3102     if (r == zoom_complete)
3103         r = send_present (c);
3104     return r;
3105 }
3106
3107 static int es_response (ZOOM_connection c,
3108                         Z_ExtendedServicesResponse *res)
3109 {
3110     if (!c->tasks || c->tasks->which != ZOOM_TASK_PACKAGE)
3111         return 0;
3112     if (res->diagnostics && res->num_diagnostics > 0)
3113         response_diag(c, res->diagnostics[0]);
3114     if (res->taskPackage &&
3115         res->taskPackage->which == Z_External_extendedService)
3116     {
3117         Z_TaskPackage *taskPackage = res->taskPackage->u.extendedService;
3118         Odr_oct *id = taskPackage->targetReference;
3119         
3120         if (id)
3121             ZOOM_options_setl (c->tasks->u.package->options,
3122                                "targetReference", (char*) id->buf, id->len);
3123     }
3124     if (res->taskPackage && 
3125         res->taskPackage->which == Z_External_octet)
3126     {
3127         Odr_oct *doc = res->taskPackage->u.octet_aligned;
3128         ZOOM_options_setl (c->tasks->u.package->options,
3129                            "xmlUpdateDoc", (char*) doc->buf, doc->len);
3130     }
3131     return 1;
3132 }
3133
3134 static void interpret_init_diag(ZOOM_connection c,
3135                                 Z_DiagnosticFormat *diag)
3136 {
3137     if (diag->num > 0)
3138     {
3139         Z_DiagnosticFormat_s *ds = diag->elements[0];
3140         if (ds->which == Z_DiagnosticFormat_s_defaultDiagRec)
3141             response_default_diag(c, ds->u.defaultDiagRec);
3142     }
3143 }
3144
3145
3146 static void interpret_otherinformation_field(ZOOM_connection c,
3147                                              Z_OtherInformation *ui)
3148 {
3149     int i;
3150     for (i = 0; i < ui->num_elements; i++)
3151     {
3152         Z_OtherInformationUnit *unit = ui->list[i];
3153         if (unit->which == Z_OtherInfo_externallyDefinedInfo &&
3154             unit->information.externallyDefinedInfo &&
3155             unit->information.externallyDefinedInfo->which ==
3156             Z_External_diag1) 
3157         {
3158             interpret_init_diag(c, unit->information.externallyDefinedInfo->u.diag1);
3159         } 
3160     }
3161 }
3162
3163 static void recv_apdu (ZOOM_connection c, Z_APDU *apdu)
3164 {
3165     Z_InitResponse *initrs;
3166     
3167     c->mask = 0;
3168     yaz_log(log_details, "%p recv_apdu apdu->which=%d", c, apdu->which);
3169     switch(apdu->which)
3170     {
3171     case Z_APDU_initResponse:
3172         yaz_log(log_api, "%p recv_apd: Received Init response", c);
3173         initrs = apdu->u.initResponse;
3174         ZOOM_connection_option_set(c, "serverImplementationId",
3175                                    initrs->implementationId ?
3176                                    initrs->implementationId : "");
3177         ZOOM_connection_option_set(c, "serverImplementationName",
3178                                    initrs->implementationName ?
3179                                    initrs->implementationName : "");
3180         ZOOM_connection_option_set(c, "serverImplementationVersion",
3181                                    initrs->implementationVersion ?
3182                                    initrs->implementationVersion : "");
3183         /* Set the three old options too, for old applications */
3184         ZOOM_connection_option_set(c, "targetImplementationId",
3185                                    initrs->implementationId ?
3186                                    initrs->implementationId : "");
3187         ZOOM_connection_option_set(c, "targetImplementationName",
3188                                    initrs->implementationName ?
3189                                    initrs->implementationName : "");
3190         ZOOM_connection_option_set(c, "targetImplementationVersion",
3191                                    initrs->implementationVersion ?
3192                                    initrs->implementationVersion : "");
3193         if (!*initrs->result)
3194         {
3195             Z_External *uif = initrs->userInformationField;
3196
3197             set_ZOOM_error(c, ZOOM_ERROR_INIT, 0); /* default error */
3198
3199             if (uif && uif->which == Z_External_userInfo1)
3200                 interpret_otherinformation_field(c, uif->u.userInfo1);
3201         }
3202         else
3203         {
3204             char *cookie =
3205                 yaz_oi_get_string_oidval (&apdu->u.initResponse->otherInfo,
3206                                           VAL_COOKIE, 1, 0);
3207             xfree (c->cookie_in);
3208             c->cookie_in = 0;
3209             if (cookie)
3210                 c->cookie_in = xstrdup(cookie);
3211             if (ODR_MASK_GET(initrs->options, Z_Options_namedResultSets) &&
3212                 ODR_MASK_GET(initrs->protocolVersion, Z_ProtocolVersion_3))
3213                 c->support_named_resultsets = 1;
3214             if (c->tasks)
3215             {
3216                 assert (c->tasks->which == ZOOM_TASK_CONNECT);
3217                 ZOOM_connection_remove_task (c);
3218             }
3219             ZOOM_connection_exec_task (c);
3220         }
3221         if (ODR_MASK_GET(initrs->options, Z_Options_negotiationModel))
3222         {
3223             NMEM tmpmem = nmem_create();
3224             Z_CharSetandLanguageNegotiation *p =
3225                 yaz_get_charneg_record(initrs->otherInfo);
3226             
3227             if (p)
3228             {
3229                 char *charset = NULL, *lang = NULL;
3230                 int sel;
3231                 
3232                 yaz_get_response_charneg(tmpmem, p, &charset, &lang, &sel);
3233                 yaz_log(log_details, "%p recv_apdu target accepted: "
3234                         "charset %s, language %s, select %d",
3235                         c,
3236                         charset ? charset : "none", lang ? lang : "none", sel);
3237                 if (charset)
3238                     ZOOM_connection_option_set (c, "negotiation-charset",
3239                                                 charset);
3240                 if (lang)
3241                     ZOOM_connection_option_set (c, "negotiation-lang",
3242                                                 lang);
3243
3244                 ZOOM_connection_option_set (
3245                     c,  "negotiation-charset-in-effect-for-records",
3246                     (sel != 0) ? "1" : "0");
3247                 nmem_destroy(tmpmem);
3248             }
3249         }       
3250         break;
3251     case Z_APDU_searchResponse:
3252         yaz_log(log_api, "%p recv_apdu Search response", c);
3253         handle_search_response (c, apdu->u.searchResponse);
3254         if (send_sort_present (c) == zoom_complete)
3255             ZOOM_connection_remove_task (c);
3256         break;
3257     case Z_APDU_presentResponse:
3258         yaz_log(log_api, "%p recv_apdu Present response", c);
3259         handle_present_response (c, apdu->u.presentResponse);
3260         if (send_present (c) == zoom_complete)
3261             ZOOM_connection_remove_task (c);
3262         break;
3263     case Z_APDU_sortResponse:
3264         yaz_log(log_api, "%p recv_apdu Sort response", c);
3265         sort_response (c, apdu->u.sortResponse);
3266         if (send_present (c) == zoom_complete)
3267             ZOOM_connection_remove_task (c);
3268         break;
3269     case Z_APDU_scanResponse:
3270         yaz_log(log_api, "%p recv_apdu Scan response", c);
3271         scan_response (c, apdu->u.scanResponse);
3272         ZOOM_connection_remove_task (c);
3273         break;
3274     case Z_APDU_extendedServicesResponse:
3275         yaz_log(log_api, "%p recv_apdu Extended Services response", c);
3276         es_response (c, apdu->u.extendedServicesResponse);
3277         ZOOM_connection_remove_task (c);
3278         break;
3279     case Z_APDU_close:
3280         yaz_log(log_api, "%p recv_apdu Close PDU", c);
3281         if (c->reconnect_ok)
3282         {
3283             do_close(c);
3284             c->tasks->running = 0;
3285             ZOOM_connection_insert_task (c, ZOOM_TASK_CONNECT);
3286         }
3287         else
3288         {
3289             set_ZOOM_error(c, ZOOM_ERROR_CONNECTION_LOST, 0);
3290             do_close(c);
3291         }
3292         break;
3293     default:
3294         yaz_log(log_api, "%p Received unknown PDU", c);
3295         set_ZOOM_error(c, ZOOM_ERROR_DECODE, 0);
3296         do_close(c);
3297     }
3298 }
3299
3300 #if HAVE_XML2
3301 static void handle_srw_response(ZOOM_connection c,
3302                                 Z_SRW_searchRetrieveResponse *res)
3303 {
3304     ZOOM_resultset resultset = 0;
3305     int i;
3306     NMEM nmem;
3307     ZOOM_Event event;
3308
3309     if (!c->tasks)
3310         return;
3311
3312     if (c->tasks->which == ZOOM_TASK_SEARCH)
3313         resultset = c->tasks->u.search.resultset;
3314     else if (c->tasks->which == ZOOM_TASK_RETRIEVE)
3315         resultset = c->tasks->u.retrieve.resultset;
3316     else
3317         return ;
3318
3319     event = ZOOM_Event_create(ZOOM_EVENT_RECV_SEARCH);
3320     ZOOM_connection_put_event(c, event);
3321
3322     resultset->size = 0;
3323
3324     yaz_log(log_details, "%p handle_srw_response got SRW response OK", c);
3325     
3326     if (res->numberOfRecords)
3327         resultset->size = *res->numberOfRecords;
3328
3329     for (i = 0; i<res->num_records; i++)
3330     {
3331         int pos;
3332
3333         Z_NamePlusRecord *npr = (Z_NamePlusRecord *)
3334             odr_malloc(c->odr_in, sizeof(Z_NamePlusRecord));
3335
3336         if (res->records[i].recordPosition && 
3337             *res->records[i].recordPosition > 0)
3338             pos = *res->records[i].recordPosition - 1;
3339         else
3340             pos = resultset->start + i;
3341         
3342         npr->databaseName = 0;
3343         npr->which = Z_NamePlusRecord_databaseRecord;
3344         npr->u.databaseRecord = (Z_External *)
3345             odr_malloc(c->odr_in, sizeof(Z_External));
3346         npr->u.databaseRecord->descriptor = 0;
3347         npr->u.databaseRecord->direct_reference =
3348             yaz_oidval_to_z3950oid(c->odr_in, CLASS_RECSYN, VAL_TEXT_XML);
3349         npr->u.databaseRecord->which = Z_External_octet;
3350         npr->u.databaseRecord->u.octet_aligned = (Odr_oct *)
3351             odr_malloc(c->odr_in, sizeof(Odr_oct));
3352         npr->u.databaseRecord->u.octet_aligned->buf = (unsigned char*)
3353             res->records[i].recordData_buf;
3354         npr->u.databaseRecord->u.octet_aligned->len = 
3355             npr->u.databaseRecord->u.octet_aligned->size = 
3356             res->records[i].recordData_len;
3357         record_cache_add (resultset, npr, pos);
3358     }
3359     if (res->num_diagnostics > 0)
3360     {
3361         const char *uri = res->diagnostics[0].uri;
3362         if (uri)
3363         {
3364             int code = 0;       
3365             const char *cp;
3366             if ((cp = strrchr(uri, '/')))
3367                 code = atoi(cp+1);
3368             set_dset_error(c, code, uri,
3369                            res->diagnostics[0].details, 0);
3370         }
3371     }
3372     nmem = odr_extract_mem(c->odr_in);
3373     nmem_transfer(resultset->odr->mem, nmem);
3374     nmem_destroy(nmem);
3375 }
3376 #endif
3377
3378 #if HAVE_XML2
3379 static void handle_http(ZOOM_connection c, Z_HTTP_Response *hres)
3380 {
3381     int ret = -1;
3382     const char *content_type = z_HTTP_header_lookup(hres->headers,
3383                                                     "Content-Type");
3384     const char *connection_head = z_HTTP_header_lookup(hres->headers,
3385                                                        "Connection");
3386     c->mask = 0;
3387     yaz_log(log_details, "%p handle_http", c);
3388
3389     if (content_type && !yaz_strcmp_del("text/xml", content_type, "; "))
3390     {
3391         Z_SOAP *soap_package = 0;
3392         ODR o = c->odr_in;
3393         Z_SOAP_Handler soap_handlers[2] = {
3394             {"http://www.loc.gov/zing/srw/", 0,
3395              (Z_SOAP_fun) yaz_srw_codec},
3396             {0, 0, 0}
3397         };
3398         ret = z_soap_codec(o, &soap_package,
3399                            &hres->content_buf, &hres->content_len,
3400                            soap_handlers);
3401         if (!ret && soap_package->which == Z_SOAP_generic &&
3402             soap_package->u.generic->no == 0)
3403         {
3404             Z_SRW_PDU *sr = (Z_SRW_PDU*) soap_package->u.generic->p;
3405             if (sr->which == Z_SRW_searchRetrieve_response)
3406                 handle_srw_response(c, sr->u.response);
3407             else
3408                 ret = -1;
3409         }
3410         else if (!ret && (soap_package->which == Z_SOAP_fault
3411                           || soap_package->which == Z_SOAP_error))
3412         {
3413             set_HTTP_error(c, hres->code,
3414                            soap_package->u.fault->fault_code,
3415                            soap_package->u.fault->fault_string);
3416         }
3417         else
3418             ret = -1;
3419     }
3420     if (ret)
3421     {
3422         if (hres->code != 200)
3423             set_HTTP_error(c, hres->code, 0, 0);
3424         else
3425             set_ZOOM_error(c, ZOOM_ERROR_DECODE, 0);
3426         do_close (c);
3427     }
3428     ZOOM_connection_remove_task(c);
3429     if (!strcmp(hres->version, "1.0"))
3430     {
3431         /* HTTP 1.0: only if Keep-Alive we stay alive.. */
3432         if (!connection_head || strcmp(connection_head, "Keep-Alive"))
3433             do_close(c);
3434     }
3435     else 
3436     {
3437         /* HTTP 1.1: only if no close we stay alive .. */
3438         if (connection_head && !strcmp(connection_head, "close"))
3439             do_close(c);
3440     }
3441 }
3442 #endif
3443
3444 static int do_read (ZOOM_connection c)
3445 {
3446     int r, more;
3447     ZOOM_Event event;
3448     
3449     event = ZOOM_Event_create (ZOOM_EVENT_RECV_DATA);
3450     ZOOM_connection_put_event (c, event);
3451     
3452     r = cs_get (c->cs, &c->buf_in, &c->len_in);
3453     more = cs_more(c->cs);
3454     yaz_log(log_details, "%p do_read len=%d more=%d", c, r, more);
3455     if (r == 1)
3456         return 0;
3457     if (r <= 0)
3458     {
3459         if (c->reconnect_ok)
3460         {
3461             do_close (c);
3462             c->reconnect_ok = 0;
3463             yaz_log(log_details, "%p do_read reconnect read", c);
3464             c->tasks->running = 0;
3465             ZOOM_connection_insert_task (c, ZOOM_TASK_CONNECT);
3466         }
3467         else
3468         {
3469             set_ZOOM_error(c, ZOOM_ERROR_CONNECTION_LOST, 0);
3470             do_close (c);
3471         }
3472     }
3473     else
3474     {
3475         Z_GDU *gdu;
3476         ZOOM_Event event;
3477
3478         odr_reset (c->odr_in);
3479         odr_setbuf (c->odr_in, c->buf_in, r, 0);
3480         event = ZOOM_Event_create (ZOOM_EVENT_RECV_APDU);
3481         ZOOM_connection_put_event (c, event);
3482
3483         if (!z_GDU (c->odr_in, &gdu, 0, 0))
3484         {
3485             int x;
3486             int err = odr_geterrorx(c->odr_in, &x);
3487             char msg[60];
3488             const char *element = odr_getelement(c->odr_in);
3489             sprintf (msg, "ODR code %d:%d element=%-20s",
3490                      err, x, element ? element : "<unknown>");
3491             set_ZOOM_error(c, ZOOM_ERROR_DECODE, msg);
3492             do_close (c);
3493         }
3494         else if (gdu->which == Z_GDU_Z3950)
3495             recv_apdu (c, gdu->u.z3950);
3496         else if (gdu->which == Z_GDU_HTTP_Response)
3497         {
3498 #if HAVE_XML2
3499             handle_http (c, gdu->u.HTTP_Response);
3500 #else
3501             set_ZOOM_error(c, ZOOM_ERROR_DECODE, 0);
3502             do_close (c);
3503 #endif
3504         }
3505         c->reconnect_ok = 0;
3506     }
3507     return 1;
3508 }
3509
3510 static zoom_ret do_write_ex (ZOOM_connection c, char *buf_out, int len_out)
3511 {
3512     int r;
3513     ZOOM_Event event;
3514     
3515     event = ZOOM_Event_create(ZOOM_EVENT_SEND_DATA);
3516     ZOOM_connection_put_event (c, event);
3517
3518     yaz_log(log_details, "%p do_write_ex len=%d", c, len_out);
3519     if ((r = cs_put(c->cs, buf_out, len_out)) < 0)
3520     {
3521         yaz_log(log_details, "%p do_write_ex write failed", c);
3522         if (c->reconnect_ok)
3523         {
3524             do_close (c);
3525             c->reconnect_ok = 0;
3526             yaz_log(log_details, "%p do_write_ex reconnect write", c);
3527             c->tasks->running = 0;
3528             ZOOM_connection_insert_task (c, ZOOM_TASK_CONNECT);
3529             return zoom_pending;
3530         }
3531         if (c->state == STATE_CONNECTING)
3532             set_ZOOM_error(c, ZOOM_ERROR_CONNECT, 0);
3533         else
3534             set_ZOOM_error(c, ZOOM_ERROR_CONNECTION_LOST, 0);
3535         do_close (c);
3536         return zoom_complete;
3537     }
3538     else if (r == 1)
3539     {    
3540         c->mask = ZOOM_SELECT_EXCEPT;
3541         if (c->cs->io_pending & CS_WANT_WRITE)
3542             c->mask += ZOOM_SELECT_WRITE;
3543         if (c->cs->io_pending & CS_WANT_READ)
3544             c->mask += ZOOM_SELECT_READ;
3545         yaz_log(log_details, "%p do_write_ex write incomplete mask=%d",
3546                 c, c->mask);
3547     }
3548     else
3549     {
3550         c->mask = ZOOM_SELECT_READ|ZOOM_SELECT_EXCEPT;
3551         yaz_log(log_details, "%p do_write_ex write complete mask=%d",
3552                 c, c->mask);
3553     }
3554     return zoom_pending;
3555 }
3556
3557 static zoom_ret do_write(ZOOM_connection c)
3558 {
3559     return do_write_ex (c, c->buf_out, c->len_out);
3560 }
3561
3562
3563 ZOOM_API(const char *)
3564 ZOOM_connection_option_get (ZOOM_connection c, const char *key)
3565 {
3566     return ZOOM_options_get (c->options, key);
3567 }
3568
3569 ZOOM_API(const char *)
3570 ZOOM_connection_option_getl (ZOOM_connection c, const char *key, int *lenp)
3571 {
3572     return ZOOM_options_getl (c->options, key, lenp);
3573 }
3574
3575 ZOOM_API(void)
3576 ZOOM_connection_option_set (ZOOM_connection c, const char *key,
3577                             const char *val)
3578 {
3579     ZOOM_options_set (c->options, key, val);
3580 }
3581
3582 ZOOM_API(void)
3583 ZOOM_connection_option_setl (ZOOM_connection c, const char *key,
3584                              const char *val, int len)
3585 {
3586     ZOOM_options_setl (c->options, key, val, len);
3587 }
3588
3589 ZOOM_API(const char *)
3590 ZOOM_resultset_option_get (ZOOM_resultset r, const char *key)
3591 {
3592     return ZOOM_options_get (r->options, key);
3593 }
3594
3595 ZOOM_API(void)
3596 ZOOM_resultset_option_set (ZOOM_resultset r, const char *key,
3597                                   const char *val)
3598 {
3599     ZOOM_options_set (r->options, key, val);
3600 }
3601
3602
3603 ZOOM_API(int)
3604 ZOOM_connection_errcode (ZOOM_connection c)
3605 {
3606     return ZOOM_connection_error (c, 0, 0);
3607 }
3608
3609 ZOOM_API(const char *)
3610 ZOOM_connection_errmsg (ZOOM_connection c)
3611 {
3612     const char *msg;
3613     ZOOM_connection_error (c, &msg, 0);
3614     return msg;
3615 }
3616
3617 ZOOM_API(const char *)
3618 ZOOM_connection_addinfo (ZOOM_connection c)
3619 {
3620     const char *addinfo;
3621     ZOOM_connection_error (c, 0, &addinfo);
3622     return addinfo;
3623 }
3624
3625 ZOOM_API(const char *)
3626 ZOOM_connection_diagset (ZOOM_connection c)
3627 {
3628     const char *diagset;
3629     ZOOM_connection_error_x (c, 0, 0, &diagset);
3630     return diagset;
3631 }
3632
3633 ZOOM_API(const char *)
3634 ZOOM_diag_str (int error)
3635 {
3636     switch (error)
3637     {
3638     case ZOOM_ERROR_NONE:
3639         return "No error";
3640     case ZOOM_ERROR_CONNECT:
3641         return "Connect failed";
3642     case ZOOM_ERROR_MEMORY:
3643         return "Out of memory";
3644     case ZOOM_ERROR_ENCODE:
3645         return "Encoding failed";
3646     case ZOOM_ERROR_DECODE:
3647         return "Decoding failed";
3648     case ZOOM_ERROR_CONNECTION_LOST:
3649         return "Connection lost";
3650     case ZOOM_ERROR_INIT:
3651         return "Init rejected";
3652     case ZOOM_ERROR_INTERNAL:
3653         return "Internal failure";
3654     case ZOOM_ERROR_TIMEOUT:
3655         return "Timeout";
3656     case ZOOM_ERROR_UNSUPPORTED_PROTOCOL:
3657         return "Unsupported protocol";
3658     case ZOOM_ERROR_UNSUPPORTED_QUERY:
3659         return "Unsupported query type";
3660     case ZOOM_ERROR_INVALID_QUERY:
3661         return "Invalid query";
3662     default:
3663         return diagbib1_str (error);
3664     }
3665 }
3666
3667 ZOOM_API(int)
3668 ZOOM_connection_error_x (ZOOM_connection c, const char **cp,
3669                          const char **addinfo, const char **diagset)
3670 {
3671     int error = c->error;
3672     if (cp)
3673     {
3674         if (!c->diagset || !strcmp(c->diagset, "ZOOM"))
3675             *cp = ZOOM_diag_str(error);
3676         else if (!strcmp(c->diagset, "HTTP"))
3677             *cp = z_HTTP_errmsg(c->error);
3678         else if (!strcmp(c->diagset, "Bib-1"))
3679             *cp = ZOOM_diag_str(error);
3680         else if (!strcmp(c->diagset, "info:srw/diagnostic/1"))
3681             *cp = yaz_diag_srw_str(c->error);
3682         else
3683             *cp = "Unknown error and diagnostic set";
3684     }
3685     if (addinfo)
3686         *addinfo = c->addinfo ? c->addinfo : "";
3687     if (diagset)
3688         *diagset = c->diagset ? c->diagset : "";
3689     return c->error;
3690 }
3691
3692 ZOOM_API(int)
3693 ZOOM_connection_error (ZOOM_connection c, const char **cp,
3694                        const char **addinfo)
3695 {
3696     return ZOOM_connection_error_x(c, cp, addinfo, 0);
3697 }
3698
3699 static int ZOOM_connection_do_io(ZOOM_connection c, int mask)
3700 {
3701     ZOOM_Event event = 0;
3702     int r = cs_look(c->cs);
3703     yaz_log(log_details, "%p ZOOM_connection_do_io mask=%d cs_look=%d",
3704              c, mask, r);
3705     
3706     if (r == CS_NONE)
3707     {
3708         event = ZOOM_Event_create (ZOOM_EVENT_CONNECT);
3709         set_ZOOM_error(c, ZOOM_ERROR_CONNECT, 0);
3710         do_close (c);
3711         ZOOM_connection_put_event (c, event);
3712     }
3713     else if (r == CS_CONNECT)
3714     {
3715         int ret;
3716         event = ZOOM_Event_create (ZOOM_EVENT_CONNECT);
3717
3718         ret = cs_rcvconnect (c->cs);
3719         yaz_log(log_details, "%p ZOOM_connection_do_io "
3720                 "cs_rcvconnect returned %d", c, ret);
3721         if (ret == 1)
3722         {
3723             c->mask = ZOOM_SELECT_EXCEPT;
3724             if (c->cs->io_pending & CS_WANT_WRITE)
3725                 c->mask += ZOOM_SELECT_WRITE;
3726             if (c->cs->io_pending & CS_WANT_READ)
3727                 c->mask += ZOOM_SELECT_READ;
3728             ZOOM_connection_put_event (c, event);
3729         }
3730         else if (ret == 0)
3731         {
3732             ZOOM_connection_put_event (c, event);
3733             get_cert(c);
3734             if (c->proto == PROTO_Z3950)
3735                 ZOOM_connection_send_init(c);
3736             else
3737             {
3738                 /* no init request for SRW .. */
3739                 assert (c->tasks->which == ZOOM_TASK_CONNECT);
3740                 ZOOM_connection_remove_task (c);
3741                 c->mask = 0;
3742                 ZOOM_connection_exec_task (c);
3743             }
3744             c->state = STATE_ESTABLISHED;
3745         }
3746         else
3747         {
3748             set_ZOOM_error(c, ZOOM_ERROR_CONNECT, 0);
3749             do_close (c);
3750             ZOOM_connection_put_event (c, event);
3751         }
3752     }
3753     else
3754     {
3755         if (mask & ZOOM_SELECT_READ)
3756             do_read (c);
3757         if (c->cs && (mask & ZOOM_SELECT_WRITE))
3758             do_write (c);
3759     }
3760     return 1;
3761 }
3762
3763 ZOOM_API(int)
3764 ZOOM_connection_last_event(ZOOM_connection cs)
3765 {
3766     if (!cs)
3767         return ZOOM_EVENT_NONE;
3768     return cs->last_event;
3769 }
3770
3771 ZOOM_API(int)
3772 ZOOM_event (int no, ZOOM_connection *cs)
3773 {
3774     int timeout = 30;      /* default timeout in seconds */
3775     int timeout_set = 0;   /* whether it was overriden at all */
3776 #if HAVE_SYS_POLL_H
3777     struct pollfd pollfds[1024];
3778     ZOOM_connection poll_cs[1024];
3779 #else
3780     struct timeval tv;
3781     fd_set input, output, except;
3782 #endif
3783     int i, r, nfds;
3784     int max_fd = 0;
3785
3786     for (i = 0; i<no; i++)
3787     {
3788         ZOOM_connection c = cs[i];
3789         ZOOM_Event event;
3790         if (c && (event = ZOOM_connection_get_event(c)))
3791         {
3792             ZOOM_Event_destroy (event);
3793             return i+1;
3794         }
3795     }
3796     for (i = 0; i<no; i++)
3797     {
3798         ZOOM_connection c = cs[i];
3799         ZOOM_Event event;
3800         if (c && ZOOM_connection_exec_task (c))
3801         {
3802             if ((event = ZOOM_connection_get_event(c)))
3803             {
3804                 ZOOM_Event_destroy (event);
3805                 return i+1;
3806             }
3807         }
3808     }
3809 #if HAVE_SYS_POLL_H
3810
3811 #else
3812     FD_ZERO (&input);
3813     FD_ZERO (&output);
3814     FD_ZERO (&except);
3815 #endif
3816     nfds = 0;
3817     for (i = 0; i<no; i++)
3818     {
3819         ZOOM_connection c = cs[i];
3820         int fd, mask;
3821         int this_timeout;
3822         
3823         if (!c)
3824             continue;
3825         fd = z3950_connection_socket(c);
3826         mask = z3950_connection_mask(c);
3827
3828         if (fd == -1)
3829             continue;
3830         if (max_fd < fd)
3831             max_fd = fd;
3832         
3833         /* -1 is used for indefinite timeout (no timeout), so -2 here. */
3834         this_timeout = ZOOM_options_get_int (c->options, "timeout", -2);
3835         if (this_timeout != -2)
3836         {
3837             /* ensure the minimum timeout is used */
3838             if (!timeout_set)
3839                 timeout = this_timeout;
3840             else if (this_timeout != -1 && this_timeout < timeout)
3841                 timeout = this_timeout;
3842             timeout_set = 1;
3843         }               
3844 #if HAVE_SYS_POLL_H
3845         if (mask)
3846         {
3847             short poll_events = 0;
3848
3849             if (mask & ZOOM_SELECT_READ)
3850                 poll_events += POLLIN;
3851             if (mask & ZOOM_SELECT_WRITE)
3852                 poll_events += POLLOUT;
3853             if (mask & ZOOM_SELECT_EXCEPT)
3854                 poll_events += POLLERR;
3855             pollfds[nfds].fd = fd;
3856             pollfds[nfds].events = poll_events;
3857             pollfds[nfds].revents = 0;
3858             poll_cs[nfds] = c;
3859             nfds++;
3860         }
3861 #else
3862         if (mask & ZOOM_SELECT_READ)
3863         {
3864             FD_SET (fd, &input);
3865             nfds++;
3866         }
3867         if (mask & ZOOM_SELECT_WRITE)
3868         {
3869             FD_SET (fd, &output);
3870             nfds++;
3871         }
3872         if (mask & ZOOM_SELECT_EXCEPT)
3873         {
3874             FD_SET (fd, &except);
3875             nfds++;
3876         }
3877 #endif
3878     }
3879     if (!nfds)
3880         return 0;
3881
3882 #if HAVE_SYS_POLL_H
3883     r = poll (pollfds, nfds, (timeout == -1 ? -1 : timeout * 1000));
3884     for (i = 0; i<nfds; i++)
3885     {
3886         ZOOM_connection c = poll_cs[i];
3887         if (r && c->mask)
3888         {
3889             int mask = 0;
3890             if (pollfds[i].revents & POLLIN)
3891                 mask += ZOOM_SELECT_READ;
3892             if (pollfds[i].revents & POLLOUT)
3893                 mask += ZOOM_SELECT_WRITE;
3894             if (pollfds[i].revents & POLLERR)
3895                 mask += ZOOM_SELECT_EXCEPT;
3896             if (mask)
3897                 ZOOM_connection_do_io(c, mask);
3898         }
3899         else if (r == 0 && c->mask)
3900         {
3901             ZOOM_Event event = ZOOM_Event_create(ZOOM_EVENT_TIMEOUT);
3902             /* timeout and this connection was waiting */
3903             set_ZOOM_error(c, ZOOM_ERROR_TIMEOUT, 0);
3904             do_close (c);
3905             ZOOM_connection_put_event(c, event);
3906         }
3907     }
3908 #else
3909     tv.tv_sec = timeout;
3910     tv.tv_usec = 0;
3911     r = select (max_fd+1, &input, &output, &except, (timeout == -1 ? 0 : &tv));
3912     for (i = 0; i<no; i++)
3913     {
3914         ZOOM_connection c = cs[i];
3915         int fd, mask;
3916
3917         if (!c)
3918             continue;
3919         fd = z3950_connection_socket(c);
3920         mask = 0;
3921         if (r && c->mask)
3922         {
3923             /* no timeout and real socket */
3924             if (FD_ISSET(fd, &input))
3925                 mask += ZOOM_SELECT_READ;
3926             if (FD_ISSET(fd, &output))
3927                 mask += ZOOM_SELECT_WRITE;
3928             if (FD_ISSET(fd, &except))
3929                 mask += ZOOM_SELECT_EXCEPT;
3930             if (mask)
3931                 ZOOM_connection_do_io(c, mask);
3932         }
3933         if (r == 0 && c->mask)
3934         {
3935             ZOOM_Event event = ZOOM_Event_create(ZOOM_EVENT_TIMEOUT);
3936             /* timeout and this connection was waiting */
3937             set_ZOOM_error(c, ZOOM_ERROR_TIMEOUT, 0);
3938             do_close (c);
3939             ZOOM_connection_put_event(c, event);
3940         }
3941     }
3942 #endif
3943     for (i = 0; i<no; i++)
3944     {
3945         ZOOM_connection c = cs[i];
3946         ZOOM_Event event;
3947         if (c && (event = ZOOM_connection_get_event(c)))
3948         {
3949             ZOOM_Event_destroy (event);
3950             return i+1;
3951         }
3952     }
3953     return 0;
3954 }
3955 /*
3956  * Local variables:
3957  * c-basic-offset: 4
3958  * indent-tabs-mode: nil
3959  * End:
3960  * vim: shiftwidth=4 tabstop=8 expandtab
3961  */
3962