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