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