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