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