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