reconnect bugfix (when receiving close PDU)
[yaz-moved-to-github.git] / zoom / zoom-c.c
1 /*
2  * $Id: zoom-c.c,v 1.34 2002-07-01 12:59:12 adam Exp $
3  *
4  * ZOOM layer for C, connections, result sets, queries.
5  */
6 #include <assert.h>
7 #include <yaz/xmalloc.h>
8 #include <yaz/otherinfo.h>
9 #include <yaz/log.h>
10 #include <yaz/pquery.h>
11 #include <yaz/diagbib1.h>
12 #include <yaz/charneg.h>
13 #include <yaz/ill.h>
14
15 #include "zoom-p.h"
16
17 #if HAVE_SYS_POLL_H
18 #include <sys/poll.h>
19 #endif
20
21 static int ZOOM_connection_send_init (ZOOM_connection c);
22 static int do_write_ex (ZOOM_connection c, char *buf_out, int len_out);
23
24 static ZOOM_Event ZOOM_Event_create (int kind)
25 {
26     ZOOM_Event event = (ZOOM_Event) xmalloc (sizeof(*event));
27     event->kind = kind;
28     event->next = 0;
29     event->prev = 0;
30     return event;
31 }
32
33 static void ZOOM_Event_destroy (ZOOM_Event event)
34 {
35     xfree (event);
36 }
37
38 static void ZOOM_connection_put_event (ZOOM_connection c, ZOOM_Event event)
39 {
40     if (c->m_queue_back)
41     {
42         c->m_queue_back->prev = event;
43         assert (c->m_queue_front);
44     }
45     else
46     {
47         assert (!c->m_queue_front);
48         c->m_queue_front = event;
49     }
50     event->next = c->m_queue_back;
51     event->prev = 0;
52     c->m_queue_back = event;
53 }
54
55 static ZOOM_Event ZOOM_connection_get_event(ZOOM_connection c)
56 {
57     ZOOM_Event event = c->m_queue_front;
58     if (!event)
59         return 0;
60     assert (c->m_queue_back);
61     c->m_queue_front = event->prev;
62     if (c->m_queue_front)
63     {
64         assert (c->m_queue_back);
65         c->m_queue_front->next = 0;
66     }
67     else
68         c->m_queue_back = 0;
69     c->last_event = event->kind;
70     return event;
71 }
72
73 static void clear_error (ZOOM_connection c)
74 {
75
76     switch (c->error)
77     {
78     case ZOOM_ERROR_CONNECT:
79     case ZOOM_ERROR_MEMORY:
80     case ZOOM_ERROR_DECODE:
81     case ZOOM_ERROR_CONNECTION_LOST:
82     case ZOOM_ERROR_INIT:
83     case ZOOM_ERROR_INTERNAL:
84         break;
85     default:
86         c->error = ZOOM_ERROR_NONE;
87         xfree (c->addinfo);
88         c->addinfo = 0;
89     }
90 }
91
92 ZOOM_task ZOOM_connection_add_task (ZOOM_connection c, int which)
93 {
94     ZOOM_task *taskp = &c->tasks;
95     while (*taskp)
96         taskp = &(*taskp)->next;
97     *taskp = (ZOOM_task) xmalloc (sizeof(**taskp));
98     (*taskp)->running = 0;
99     (*taskp)->which = which;
100     (*taskp)->next = 0;
101     clear_error (c);
102     return *taskp;
103 }
104
105 ZOOM_task ZOOM_connection_insert_task (ZOOM_connection c, int which)
106 {
107     ZOOM_task task = (ZOOM_task) xmalloc (sizeof(*task));
108
109     task->next = c->tasks;
110     c->tasks = task;
111
112     task->running = 0;
113     task->which = which;
114     clear_error (c);
115     return task;
116 }
117
118 void ZOOM_connection_remove_task (ZOOM_connection c)
119 {
120     ZOOM_task task = c->tasks;
121
122     if (task)
123     {
124         c->tasks = task->next;
125         switch (task->which)
126         {
127         case ZOOM_TASK_SEARCH:
128             ZOOM_resultset_destroy (task->u.search.resultset);
129             break;
130         case ZOOM_TASK_RETRIEVE:
131             ZOOM_resultset_destroy (task->u.retrieve.resultset);
132             break;
133         case ZOOM_TASK_CONNECT:
134             break;
135         case ZOOM_TASK_SCAN:
136             ZOOM_scanset_destroy (task->u.scan.scan);
137             break;
138         case ZOOM_TASK_PACKAGE:
139             ZOOM_package_destroy (task->u.package);
140             break;
141         default:
142             assert (0);
143         }
144         xfree (task);
145     }
146 }
147
148 void ZOOM_connection_remove_tasks (ZOOM_connection c)
149 {
150     while (c->tasks)
151         ZOOM_connection_remove_task(c);
152 }
153
154 static ZOOM_record record_cache_lookup (ZOOM_resultset r,
155                                          int pos,
156                                          const char *elementSetName);
157
158 ZOOM_API(ZOOM_connection)
159 ZOOM_connection_create (ZOOM_options options)
160 {
161     ZOOM_connection c = (ZOOM_connection) xmalloc (sizeof(*c));
162
163     c->cs = 0;
164     c->mask = 0;
165     c->reconnect_ok = 0;
166     c->state = STATE_IDLE;
167     c->error = ZOOM_ERROR_NONE;
168     c->addinfo = 0;
169     c->buf_in = 0;
170     c->len_in = 0;
171     c->buf_out = 0;
172     c->len_out = 0;
173     c->resultsets = 0;
174
175     c->options = ZOOM_options_create_with_parent(options);
176
177     c->host_port = 0;
178     c->proxy = 0;
179     
180     c->charset = c->lang = 0;
181
182     c->cookie_out = 0;
183     c->cookie_in = 0;
184     c->client_IP = 0;
185     c->tasks = 0;
186
187     c->odr_in = odr_createmem (ODR_DECODE);
188     c->odr_out = odr_createmem (ODR_ENCODE);
189
190     c->async = 0;
191     c->support_named_resultsets = 0;
192     c->last_event = ZOOM_EVENT_NONE;
193
194     c->m_queue_front = 0;
195     c->m_queue_back = 0;
196     return c;
197 }
198
199 /* set database names. Take local databases (if set); otherwise
200    take databases given in ZURL (if set); otherwise use Default */
201 static char **set_DatabaseNames (ZOOM_connection con, ZOOM_options options,
202                                  int *num)
203 {
204     char **databaseNames;
205     const char *c;
206     int no = 2;
207     const char *cp = ZOOM_options_get (options, "databaseName");
208     
209     if (!cp || !*cp)
210     {
211         cp = strchr (con->host_port, '/');
212         if (cp)
213             cp++;
214         }
215     if (cp)
216     {
217         c = cp;
218         while ((c = strchr(c, '+')))
219         {
220             c++;
221             no++;
222         }
223     }
224     else
225         cp = "Default";
226     databaseNames = (char**)
227         odr_malloc (con->odr_out, no * sizeof(*databaseNames));
228     no = 0;
229     while (*cp)
230     {
231         c = strchr (cp, '+');
232         if (!c)
233             c = cp + strlen(cp);
234         else if (c == cp)
235         {
236             cp++;
237             continue;
238         }
239         /* cp ptr to first char of db name, c is char
240            following db name */
241         databaseNames[no] = (char*) odr_malloc (con->odr_out, 1+c-cp);
242         memcpy (databaseNames[no], cp, c-cp);
243         databaseNames[no++][c-cp] = '\0';
244         cp = c;
245         if (*cp)
246             cp++;
247     }
248     databaseNames[no] = NULL;
249     *num = no;
250     return databaseNames;
251 }
252
253 ZOOM_API(ZOOM_connection)
254 ZOOM_connection_new (const char *host, int portnum)
255 {
256     ZOOM_connection c = ZOOM_connection_create (0);
257
258     ZOOM_connection_connect (c, host, portnum);
259     return c;
260 }
261
262 ZOOM_API(void)
263 ZOOM_connection_connect(ZOOM_connection c,
264                         const char *host, int portnum)
265 {
266     const char *val;
267     ZOOM_task task;
268
269     if (c->cs)
270     {
271         yaz_log (LOG_DEBUG, "reconnect");
272         c->reconnect_ok = 1;
273         return;
274     }
275     yaz_log(LOG_DEBUG, "connect");
276     xfree (c->proxy);
277     val = ZOOM_options_get (c->options, "proxy");
278     if (val && *val)
279         c->proxy = xstrdup (val);
280     else
281         c->proxy = 0;
282
283     xfree (c->charset);
284     val = ZOOM_options_get (c->options, "charset");
285     if (val && *val)
286         c->charset = xstrdup (val);
287     else
288         c->charset = 0;
289
290     xfree (c->lang);
291     val = ZOOM_options_get (c->options, "lang");
292     if (val && *val)
293         c->lang = xstrdup (val);
294     else
295         c->lang = 0;
296
297     xfree (c->host_port);
298     if (portnum)
299     {
300         char hostn[128];
301         sprintf (hostn, "%.80s:%d", host, portnum);
302         c->host_port = xstrdup(hostn);
303     }
304     else
305         c->host_port = xstrdup(host);
306
307     ZOOM_options_set(c->options, "host", c->host_port);
308
309     val = ZOOM_options_get (c->options, "cookie");
310     if (val && *val)
311         c->cookie_out = xstrdup (val);
312
313     val = ZOOM_options_get (c->options, "clientIP");
314     if (val && *val)
315         c->client_IP = xstrdup (val);
316
317     c->async = ZOOM_options_get_bool (c->options, "async", 0);
318  
319     c->error = ZOOM_ERROR_NONE;
320
321     task = ZOOM_connection_add_task (c, ZOOM_TASK_CONNECT);
322
323     if (!c->async)
324     {
325         while (ZOOM_event (1, &c))
326             ;
327     }
328 }
329
330 ZOOM_API(ZOOM_query)
331 ZOOM_query_create(void)
332 {
333     ZOOM_query s = (ZOOM_query) xmalloc (sizeof(*s));
334
335     s->refcount = 1;
336     s->query = 0;
337     s->sort_spec = 0;
338     s->odr = odr_createmem (ODR_ENCODE);
339
340     return s;
341 }
342
343 ZOOM_API(void)
344 ZOOM_query_destroy(ZOOM_query s)
345 {
346     if (!s)
347         return;
348
349     (s->refcount)--;
350     yaz_log (LOG_DEBUG, "ZOOM_query_destroy count=%d", s->refcount);
351     if (s->refcount == 0)
352     {
353         odr_destroy (s->odr);
354         xfree (s);
355     }
356 }
357
358 ZOOM_API(int)
359 ZOOM_query_prefix(ZOOM_query s, const char *str)
360 {
361     s->query = (Z_Query *) odr_malloc (s->odr, sizeof(*s->query));
362     s->query->which = Z_Query_type_1;
363     s->query->u.type_1 =  p_query_rpn(s->odr, PROTO_Z3950, str);
364     if (!s->query->u.type_1)
365         return -1;
366     return 0;
367 }
368
369 ZOOM_API(int)
370 ZOOM_query_sortby(ZOOM_query s, const char *criteria)
371 {
372     s->sort_spec = yaz_sort_spec (s->odr, criteria);
373     if (!s->sort_spec)
374         return -1;
375     return 0;
376 }
377
378 static int do_write(ZOOM_connection c);
379
380 ZOOM_API(void)
381 ZOOM_connection_destroy(ZOOM_connection c)
382 {
383     ZOOM_resultset r;
384     if (!c)
385         return;
386     if (c->cs)
387         cs_close (c->cs);
388     for (r = c->resultsets; r; r = r->next)
389         r->connection = 0;
390
391     xfree (c->buf_in);
392     xfree (c->addinfo);
393     odr_destroy (c->odr_in);
394     odr_destroy (c->odr_out);
395     ZOOM_options_destroy (c->options);
396     ZOOM_connection_remove_tasks (c);
397     xfree (c->host_port);
398     xfree (c->proxy);
399     xfree (c->charset);
400     xfree (c->lang);
401     xfree (c->cookie_out);
402     xfree (c->cookie_in);
403     xfree (c->client_IP);
404     xfree (c);
405 }
406
407 void ZOOM_resultset_addref (ZOOM_resultset r)
408 {
409     if (r)
410         (r->refcount)++;
411 }
412 ZOOM_resultset ZOOM_resultset_create ()
413 {
414     ZOOM_resultset r = (ZOOM_resultset) xmalloc (sizeof(*r));
415
416     r->refcount = 1;
417     r->size = 0;
418     r->odr = odr_createmem (ODR_ENCODE);
419     r->start = 0;
420     r->piggyback = 1;
421     r->setname = 0;
422     r->count = 0;
423     r->record_cache = 0;
424     r->r_sort_spec = 0;
425     r->r_query = 0;
426     r->search = 0;
427     r->connection = 0;
428     r->next = 0;
429     return r;
430 }
431
432 ZOOM_API(ZOOM_resultset)
433 ZOOM_connection_search_pqf(ZOOM_connection c, const char *q)
434 {
435     ZOOM_resultset r;
436     ZOOM_query s = ZOOM_query_create();
437
438     ZOOM_query_prefix (s, q);
439
440     r = ZOOM_connection_search (c, s);
441     ZOOM_query_destroy (s);
442     return r;
443 }
444
445 ZOOM_API(ZOOM_resultset)
446 ZOOM_connection_search(ZOOM_connection c, ZOOM_query q)
447 {
448     ZOOM_resultset r = ZOOM_resultset_create ();
449     ZOOM_task task;
450     const char *cp;
451
452     r->r_sort_spec = q->sort_spec;
453     r->r_query = q->query;
454     r->search = q;
455
456     r->options = ZOOM_options_create_with_parent(c->options);
457
458     r->start = ZOOM_options_get_int(r->options, "start", 0);
459     r->count = ZOOM_options_get_int(r->options, "count", 0);
460     r->piggyback = ZOOM_options_get_bool (r->options, "piggyback", 1);
461     cp = ZOOM_options_get (r->options, "setname");
462     if (cp)
463         r->setname = xstrdup (cp);
464     
465     r->connection = c;
466
467     r->next = c->resultsets;
468     c->resultsets = r;
469
470     task = ZOOM_connection_add_task (c, ZOOM_TASK_SEARCH);
471     task->u.search.resultset = r;
472     ZOOM_resultset_addref (r);  
473
474     (q->refcount)++;
475
476     if (!c->async)
477     {
478         while (ZOOM_event (1, &c))
479             ;
480     }
481     return r;
482 }
483
484 ZOOM_API(void)
485 ZOOM_resultset_destroy(ZOOM_resultset r)
486 {
487     if (!r)
488         return;
489     (r->refcount)--;
490     yaz_log (LOG_DEBUG, "destroy r = %p count=%d", r, r->refcount);
491     if (r->refcount == 0)
492     {
493         ZOOM_record_cache rc;
494
495         for (rc = r->record_cache; rc; rc = rc->next)
496             if (rc->rec.wrbuf_marc)
497                 wrbuf_free (rc->rec.wrbuf_marc, 1);
498         if (r->connection)
499         {
500             /* remove ourselves from the resultsets in connection */
501             ZOOM_resultset *rp = &r->connection->resultsets;
502             while (1)
503             {
504                 assert (*rp);   /* we must be in this list!! */
505                 if (*rp == r)
506                 {   /* OK, we're here - take us out of it */
507                     *rp = (*rp)->next;
508                     break;
509                 }
510                 rp = &(*rp)->next;
511             }
512         }
513         ZOOM_query_destroy (r->search);
514         ZOOM_options_destroy (r->options);
515         odr_destroy (r->odr);
516         xfree (r->setname);
517         xfree (r);
518     }
519 }
520
521 ZOOM_API(size_t)
522 ZOOM_resultset_size (ZOOM_resultset r)
523 {
524     return r->size;
525 }
526
527 static void do_close (ZOOM_connection c)
528 {
529     if (c->cs)
530         cs_close(c->cs);
531     c->cs = 0;
532     c->mask = 0;
533     c->state = STATE_IDLE;
534 }
535
536 static void ZOOM_resultset_retrieve (ZOOM_resultset r,
537                                      int force_sync, int start, int count)
538 {
539     ZOOM_task task;
540     ZOOM_connection c;
541
542     if (!r)
543         return;
544     c = r->connection;
545     if (!c)
546         return;
547     task = ZOOM_connection_add_task (c, ZOOM_TASK_RETRIEVE);
548     task->u.retrieve.resultset = r;
549     task->u.retrieve.start = start;
550     task->u.retrieve.count = count;
551
552     ZOOM_resultset_addref (r);
553
554     if (!r->connection->async || force_sync)
555         while (r->connection && ZOOM_event (1, &r->connection))
556             ;
557 }
558
559 ZOOM_API(void)
560 ZOOM_resultset_records (ZOOM_resultset r, ZOOM_record *recs,
561                         size_t start, size_t count)
562 {
563     int force_present = 0;
564
565     if (!r)
566         return ;
567     if (count && recs)
568         force_present = 1;
569     ZOOM_resultset_retrieve (r, force_present, start, count);
570     if (force_present)
571     {
572         size_t i;
573         for (i = 0; i< count; i++)
574             recs[i] = ZOOM_resultset_record_immediate (r, i+start);
575     }
576 }
577
578 static int do_connect (ZOOM_connection c)
579 {
580     void *add;
581     const char *effective_host;
582
583     if (c->proxy)
584         effective_host = c->proxy;
585     else
586         effective_host = c->host_port;
587
588     yaz_log (LOG_DEBUG, "do_connect host=%s", effective_host);
589
590     assert (!c->cs);
591     c->cs = cs_create_host (effective_host, 0, &add);
592
593     if (c->cs)
594     {
595         int ret = cs_connect (c->cs, add);
596         if (ret == 0)
597         {
598             ZOOM_Event event = ZOOM_Event_create(ZOOM_EVENT_CONNECT);
599             ZOOM_connection_put_event(c, event);
600             ZOOM_connection_send_init(c);
601             c->state = STATE_ESTABLISHED;
602             return 1;
603         }
604         else if (ret > 0)
605         {
606             c->state = STATE_CONNECTING; 
607             c->mask = ZOOM_SELECT_EXCEPT;
608             if (c->cs->io_pending & CS_WANT_WRITE)
609                 c->mask += ZOOM_SELECT_WRITE;
610             if (c->cs->io_pending & CS_WANT_READ)
611                 c->mask += ZOOM_SELECT_READ;
612             return 1;
613         }
614     }
615     c->state = STATE_IDLE;
616     c->error = ZOOM_ERROR_CONNECT;
617     return 0;
618 }
619
620 int z3950_connection_socket(ZOOM_connection c)
621 {
622     if (c->cs)
623         return cs_fileno(c->cs);
624     return -1;
625 }
626
627 int z3950_connection_mask(ZOOM_connection c)
628 {
629     if (c->cs)
630         return c->mask;
631     return 0;
632 }
633
634 static int encode_APDU(ZOOM_connection c, Z_APDU *a, ODR out)
635 {
636     assert (a);
637     if (c->cookie_out)
638     {
639         Z_OtherInformation **oi;
640         yaz_oi_APDU(a, &oi);
641         yaz_oi_set_string_oidval(oi, out, VAL_COOKIE, 1, c->cookie_out);
642     }
643     if (c->client_IP)
644     {
645         Z_OtherInformation **oi;
646         yaz_oi_APDU(a, &oi);
647         yaz_oi_set_string_oidval(oi, out, VAL_CLIENT_IP, 1, c->client_IP);
648     }
649     if (!z_APDU(out, &a, 0, 0))
650     {
651         FILE *outf = fopen("/tmp/apdu.txt", "a");
652         if (a && outf)
653         {
654             ODR odr_pr = odr_createmem(ODR_PRINT);
655             fprintf (outf, "a=%p\n", a);
656             odr_setprint(odr_pr, outf);
657             z_APDU(odr_pr, &a, 0, 0);
658             odr_destroy(odr_pr);
659         }
660         c->error = ZOOM_ERROR_ENCODE;
661         do_close (c);
662         return -1;
663     }
664     
665     return 0;
666 }
667
668 static int send_APDU (ZOOM_connection c, Z_APDU *a)
669 {
670     ZOOM_Event event;
671     assert (a);
672     if (encode_APDU(c, a, c->odr_out))
673         return -1;
674     c->buf_out = odr_getbuf(c->odr_out, &c->len_out, 0);
675     event = ZOOM_Event_create (ZOOM_EVENT_SEND_APDU);
676     ZOOM_connection_put_event (c, event);
677     odr_reset(c->odr_out);
678     do_write (c);
679     return 0;
680 }
681
682 static int ZOOM_connection_send_init (ZOOM_connection c)
683 {
684     const char *impname;
685     Z_APDU *apdu = zget_APDU(c->odr_out, Z_APDU_initRequest);
686     Z_InitRequest *ireq = apdu->u.initRequest;
687     Z_IdAuthentication *auth = (Z_IdAuthentication *)
688         odr_malloc(c->odr_out, sizeof(*auth));
689     const char *auth_groupId = ZOOM_options_get (c->options, "group");
690     const char *auth_userId = ZOOM_options_get (c->options, "user");
691     const char *auth_password = ZOOM_options_get (c->options, "pass");
692     
693     ODR_MASK_SET(ireq->options, Z_Options_search);
694     ODR_MASK_SET(ireq->options, Z_Options_present);
695     ODR_MASK_SET(ireq->options, Z_Options_scan);
696     ODR_MASK_SET(ireq->options, Z_Options_sort);
697     ODR_MASK_SET(ireq->options, Z_Options_extendedServices);
698     ODR_MASK_SET(ireq->options, Z_Options_namedResultSets);
699     
700     ODR_MASK_SET(ireq->protocolVersion, Z_ProtocolVersion_1);
701     ODR_MASK_SET(ireq->protocolVersion, Z_ProtocolVersion_2);
702     ODR_MASK_SET(ireq->protocolVersion, Z_ProtocolVersion_3);
703     
704     impname = ZOOM_options_get (c->options, "implementationName");
705     ireq->implementationName =
706         (char *) odr_malloc (c->odr_out, 15 + (impname ? strlen(impname) : 0));
707     strcpy (ireq->implementationName, "");
708     if (impname)
709     {
710         strcat (ireq->implementationName, impname);
711         strcat (ireq->implementationName, "/");
712     }                                          
713     strcat (ireq->implementationName, "ZOOM-C/YAZ");
714     
715     *ireq->maximumRecordSize =
716         ZOOM_options_get_int (c->options, "maximumRecordSize", 1024*1024);
717     *ireq->preferredMessageSize =
718         ZOOM_options_get_int (c->options, "preferredMessageSize", 1024*1024);
719     
720     if (auth_groupId || auth_password)
721     {
722         Z_IdPass *pass = (Z_IdPass *) odr_malloc(c->odr_out, sizeof(*pass));
723         int i = 0;
724         pass->groupId = 0;
725         if (auth_groupId && *auth_groupId)
726         {
727             pass->groupId = (char *)
728                 odr_malloc(c->odr_out, strlen(auth_groupId)+1);
729             strcpy(pass->groupId, auth_groupId);
730             i++;
731         }
732         pass->userId = 0;
733         if (auth_userId && *auth_userId)
734         {
735             pass->userId = (char *)
736                 odr_malloc(c->odr_out, strlen(auth_userId)+1);
737             strcpy(pass->userId, auth_userId);
738             i++;
739         }
740         pass->password = 0;
741         if (auth_password && *auth_password)
742         {
743             pass->password = (char *)
744                 odr_malloc(c->odr_out, strlen(auth_password)+1);
745             strcpy(pass->password, auth_password);
746             i++;
747         }
748         if (i)
749         {
750             auth->which = Z_IdAuthentication_idPass;
751             auth->u.idPass = pass;
752             ireq->idAuthentication = auth;
753         }
754     }
755     else if (auth_userId)
756     {
757         auth->which = Z_IdAuthentication_open;
758         auth->u.open = (char *)
759             odr_malloc(c->odr_out, strlen(auth_userId)+1);
760         strcpy(auth->u.open, auth_userId);
761         ireq->idAuthentication = auth;
762     }
763     if (c->proxy)
764         yaz_oi_set_string_oidval(&ireq->otherInfo, c->odr_out,
765                                  VAL_PROXY, 1, c->host_port);
766     if (c->charset||c->lang)
767     {
768         Z_OtherInformation **oi;
769         Z_OtherInformationUnit *oi_unit;
770         
771         yaz_oi_APDU(apdu, &oi);
772         
773         if ((oi_unit = yaz_oi_update(oi, c->odr_out, NULL, 0, 0)))
774         {
775                 ODR_MASK_SET(ireq->options, Z_Options_negotiationModel);
776                 
777                 oi_unit->which = Z_OtherInfo_externallyDefinedInfo;
778                 oi_unit->information.externallyDefinedInfo =
779                         yaz_set_proposal_charneg(c->odr_out,
780                                 (const char **)&c->charset, (c->charset) ? 1:0,
781                                 (const char **)&c->lang, (c->lang) ? 1:0, 1);
782         }
783     }
784     assert (apdu);
785     send_APDU (c, apdu);
786     
787     return 0;
788 }
789
790 static int ZOOM_connection_send_search (ZOOM_connection c)
791 {
792     ZOOM_resultset r;
793     int lslb, ssub, mspn;
794     const char *syntax;
795     Z_APDU *apdu = zget_APDU(c->odr_out, Z_APDU_searchRequest);
796     Z_SearchRequest *search_req = apdu->u.searchRequest;
797     const char *elementSetName;
798     const char *smallSetElementSetName;
799     const char *mediumSetElementSetName;
800     const char *schema;
801
802     assert (c->tasks);
803     assert (c->tasks->which == ZOOM_TASK_SEARCH);
804
805     r = c->tasks->u.search.resultset;
806
807     elementSetName =
808         ZOOM_options_get (r->options, "elementSetName");
809     smallSetElementSetName  =
810         ZOOM_options_get (r->options, "smallSetElementSetName");
811     mediumSetElementSetName =
812         ZOOM_options_get (r->options, "mediumSetElementSetName");
813     schema =
814         ZOOM_options_get (r->options, "schema");
815
816     if (!smallSetElementSetName)
817         smallSetElementSetName = elementSetName;
818
819     if (!mediumSetElementSetName)
820         mediumSetElementSetName = elementSetName;
821
822     assert (r);
823     assert (r->r_query);
824
825     /* prepare query for the search request */
826     search_req->query = r->r_query;
827
828     search_req->databaseNames =
829         set_DatabaseNames (c, r->options, &search_req->num_databaseNames);
830
831     /* get syntax (no need to provide unless piggyback is in effect) */
832     syntax = ZOOM_options_get (r->options, "preferredRecordSyntax");
833
834     lslb = ZOOM_options_get_int (r->options, "largeSetLowerBound", -1);
835     ssub = ZOOM_options_get_int (r->options, "smallSetUpperBound", -1);
836     mspn = ZOOM_options_get_int (r->options, "mediumSetPresentNumber", -1);
837     if (lslb != -1 && ssub != -1 && mspn != -1)
838     {
839         /* So're a Z39.50 expert? Let's hope you don't do sort */
840         *search_req->largeSetLowerBound = lslb;
841         *search_req->smallSetUpperBound = ssub;
842         *search_req->mediumSetPresentNumber = mspn;
843     }
844     else if (r->start == 0 && r->count > 0
845              && r->piggyback && !r->r_sort_spec && !schema)
846     {
847         /* Regular piggyback - do it unless we're going to do sort */
848         *search_req->largeSetLowerBound = 2000000000;
849         *search_req->smallSetUpperBound = r->count;
850         *search_req->mediumSetPresentNumber = r->count;
851         smallSetElementSetName = 0;  /* no need to provide this */
852     }
853     else
854     {
855         /* non-piggyback. Need not provide elementsets or syntaxes .. */
856         smallSetElementSetName = 0;
857         mediumSetElementSetName = 0;
858         syntax = 0;
859     }
860     if (smallSetElementSetName && *smallSetElementSetName)
861     {
862         Z_ElementSetNames *esn = (Z_ElementSetNames *)
863             odr_malloc (c->odr_out, sizeof(*esn));
864         
865         esn->which = Z_ElementSetNames_generic;
866         esn->u.generic = odr_strdup (c->odr_out, smallSetElementSetName);
867         search_req->smallSetElementSetNames = esn;
868     }
869     if (mediumSetElementSetName && *mediumSetElementSetName)
870     {
871         Z_ElementSetNames *esn = (Z_ElementSetNames *)
872             odr_malloc (c->odr_out, sizeof(*esn));
873         
874         esn->which = Z_ElementSetNames_generic;
875         esn->u.generic = odr_strdup (c->odr_out, mediumSetElementSetName);
876         search_req->mediumSetElementSetNames = esn;
877     }
878     if (syntax)
879         search_req->preferredRecordSyntax =
880             yaz_str_to_z3950oid (c->odr_out, CLASS_RECSYN, syntax);
881     
882     if (!r->setname)
883     {
884         if (c->support_named_resultsets)
885         {
886             char setname[14];
887             int ord;
888             /* find the lowest unused ordinal so that we re-use
889                result sets on the server. */
890             for (ord = 1; ; ord++)
891             {
892                 ZOOM_resultset rp;
893                 sprintf (setname, "%d", ord);
894                 for (rp = c->resultsets; rp; rp = rp->next)
895                     if (rp->setname && !strcmp (rp->setname, setname))
896                         break;
897                 if (!rp)
898                     break;
899             }
900             r->setname = xstrdup (setname);
901             yaz_log (LOG_DEBUG, "allocating %s", r->setname);
902         }
903         else
904             r->setname = xstrdup ("default");
905         ZOOM_options_set (r->options, "setname", r->setname);
906     }
907     search_req->resultSetName = odr_strdup(c->odr_out, r->setname);
908     /* send search request */
909     send_APDU (c, apdu);
910     return 1;
911 }
912
913 static void response_diag (ZOOM_connection c, Z_DiagRec *p)
914 {
915     Z_DefaultDiagFormat *r;
916     char *addinfo = 0;
917     
918     xfree (c->addinfo);
919     c->addinfo = 0;
920     if (p->which != Z_DiagRec_defaultFormat)
921     {
922         c->error = ZOOM_ERROR_DECODE;
923         return;
924     }
925     r = p->u.defaultFormat;
926     switch (r->which)
927     {
928     case Z_DefaultDiagFormat_v2Addinfo:
929         addinfo = r->u.v2Addinfo;
930         break;
931     case Z_DefaultDiagFormat_v3Addinfo:
932         addinfo = r->u.v3Addinfo;
933         break;
934     }
935     if (addinfo)
936         c->addinfo = xstrdup (addinfo);
937     c->error = *r->condition;
938 }
939
940 ZOOM_API(ZOOM_record)
941 ZOOM_record_clone (ZOOM_record srec)
942 {
943     char *buf;
944     int size;
945     ODR odr_enc;
946     ZOOM_record nrec;
947
948     odr_enc = odr_createmem(ODR_ENCODE);
949     if (!z_NamePlusRecord (odr_enc, &srec->npr, 0, 0))
950         return 0;
951     buf = odr_getbuf (odr_enc, &size, 0);
952     
953     nrec = (ZOOM_record) xmalloc (sizeof(*nrec));
954     nrec->odr = odr_createmem(ODR_DECODE);
955     nrec->wrbuf_marc = 0;
956     odr_setbuf (nrec->odr, buf, size, 0);
957     z_NamePlusRecord (nrec->odr, &nrec->npr, 0, 0);
958     
959     odr_destroy (odr_enc);
960     return nrec;
961 }
962
963 ZOOM_API(ZOOM_record)
964 ZOOM_resultset_record_immediate (ZOOM_resultset s,size_t pos)
965 {
966     return record_cache_lookup (s, pos, 0);
967 }
968
969 ZOOM_API(ZOOM_record)
970 ZOOM_resultset_record (ZOOM_resultset r, size_t pos)
971 {
972     ZOOM_resultset_retrieve (r, 1, pos, 1);
973     return ZOOM_resultset_record_immediate (r, pos);
974 }
975
976 ZOOM_API(void)
977 ZOOM_record_destroy (ZOOM_record rec)
978 {
979     if (!rec)
980         return;
981     if (rec->wrbuf_marc)
982         wrbuf_free (rec->wrbuf_marc, 1);
983     odr_destroy (rec->odr);
984     xfree (rec);
985 }
986
987 ZOOM_API(const char *)
988 ZOOM_record_get (ZOOM_record rec, const char *type, int *len)
989 {
990     Z_NamePlusRecord *npr;
991     
992     if (len)
993         *len = 0; /* default return */
994         
995     if (!rec)
996         return 0;
997     npr = rec->npr;
998     if (!npr)
999         return 0;
1000     if (!strcmp (type, "database"))
1001     {
1002         if (len)
1003             *len = strlen(npr->databaseName);
1004         return npr->databaseName;
1005     }
1006     else if (!strcmp (type, "syntax"))
1007     {
1008         if (npr->which == Z_NamePlusRecord_databaseRecord)
1009         {
1010             Z_External *r = (Z_External *) npr->u.databaseRecord;
1011             oident *ent = oid_getentbyoid(r->direct_reference);
1012             if (ent)
1013             {
1014                 if (len)
1015                     *len = strlen(ent->desc);
1016                 return ent->desc;
1017             }
1018         }
1019         return "none";
1020     }
1021     else if (!strcmp (type, "render") && 
1022              npr->which == Z_NamePlusRecord_databaseRecord)
1023     {
1024         Z_External *r = (Z_External *) npr->u.databaseRecord;
1025         oident *ent = oid_getentbyoid(r->direct_reference);
1026         
1027         if (r->which == Z_External_sutrs)
1028         {
1029             if (len) *len = r->u.sutrs->len;
1030             return (const char *) r->u.sutrs->buf;
1031         }
1032         else if (r->which == Z_External_octet)
1033         {
1034             switch (ent->value)
1035             {
1036             case VAL_SOIF:
1037             case VAL_HTML:
1038             case VAL_SUTRS:
1039                 break;
1040             case VAL_TEXT_XML:
1041             case VAL_APPLICATION_XML:
1042                 break;
1043             default:
1044                 if (!rec->wrbuf_marc)
1045                     rec->wrbuf_marc = wrbuf_alloc();
1046                 wrbuf_rewind (rec->wrbuf_marc);
1047                 if (yaz_marc_decode ((const char *)
1048                                      r->u.octet_aligned->buf,
1049                                      rec->wrbuf_marc, 0,
1050                                      r->u.octet_aligned->len,
1051                                      0) > 0)
1052                 {
1053                     if (len) *len = wrbuf_len(rec->wrbuf_marc);
1054                     return wrbuf_buf(rec->wrbuf_marc);
1055                 }
1056             }
1057             if (len) *len = r->u.octet_aligned->len;
1058             return (const char *) r->u.octet_aligned->buf;
1059         }
1060         else if (r->which == Z_External_grs1)
1061         {
1062             if (len) *len = 5;
1063             return "GRS-1";
1064         }
1065         return 0;
1066     }
1067     else if (!strcmp (type, "xml") && 
1068              npr->which == Z_NamePlusRecord_databaseRecord)
1069     {
1070         Z_External *r = (Z_External *) npr->u.databaseRecord;
1071         oident *ent = oid_getentbyoid(r->direct_reference);
1072         
1073         if (r->which == Z_External_sutrs)
1074         {
1075             if (len) *len = r->u.sutrs->len;
1076             return (const char *) r->u.sutrs->buf;
1077         }
1078         else if (r->which == Z_External_octet)
1079         {
1080             switch (ent->value)
1081             {
1082             case VAL_SOIF:
1083             case VAL_HTML:
1084             case VAL_SUTRS:
1085                 break;
1086             case VAL_TEXT_XML:
1087             case VAL_APPLICATION_XML:
1088                 break;
1089             default:
1090                 if (!rec->wrbuf_marc)
1091                     rec->wrbuf_marc = wrbuf_alloc();
1092                 wrbuf_rewind (rec->wrbuf_marc);
1093                 if (yaz_marc_decode ((const char *)
1094                                      r->u.octet_aligned->buf,
1095                                      rec->wrbuf_marc, 0,
1096                                      r->u.octet_aligned->len,
1097                                      1) > 0)
1098                 {
1099                     if (len) *len = wrbuf_len(rec->wrbuf_marc);
1100                     return wrbuf_buf(rec->wrbuf_marc);
1101                 }
1102             }
1103             if (len) *len = r->u.octet_aligned->len;
1104             return (const char *) r->u.octet_aligned->buf;
1105         }
1106         else if (r->which == Z_External_grs1)
1107         {
1108             if (len) *len = 5;
1109             return "GRS-1";
1110         }
1111         return 0;
1112     }
1113     else if (!strcmp (type, "raw"))
1114     {
1115         if (npr->which == Z_NamePlusRecord_databaseRecord)
1116             return (const char *) npr->u.databaseRecord;
1117         return 0;
1118     }
1119     return 0;
1120 }
1121
1122 static void record_cache_add (ZOOM_resultset r,
1123                               Z_NamePlusRecord *npr,
1124                               int pos,
1125                               const char *elementSetName)
1126 {
1127     ZOOM_record_cache rc;
1128
1129     for (rc = r->record_cache; rc; rc = rc->next)
1130     {
1131         if (pos == rc->pos)
1132         {
1133             if ((!elementSetName && !rc->elementSetName)
1134                 || (elementSetName && rc->elementSetName &&
1135                     !strcmp (elementSetName, rc->elementSetName)))
1136             {
1137                 /* not destroying rc->npr (it's handled by nmem )*/
1138                 rc->rec.npr = npr;
1139                 /* keeping wrbuf_marc too */
1140                 return;
1141             }
1142         }
1143     }
1144     rc = (ZOOM_record_cache) odr_malloc (r->odr, sizeof(*rc));
1145     rc->rec.npr = npr; 
1146     rc->rec.odr = 0;
1147     rc->rec.wrbuf_marc = 0;
1148     if (elementSetName)
1149         rc->elementSetName = odr_strdup (r->odr, elementSetName);
1150     else
1151         rc->elementSetName = 0;
1152     rc->pos = pos;
1153     rc->next = r->record_cache;
1154     r->record_cache = rc;
1155 }
1156
1157 static ZOOM_record record_cache_lookup (ZOOM_resultset r,
1158                                          int pos,
1159                                          const char *elementSetName)
1160 {
1161     ZOOM_record_cache rc;
1162
1163     for (rc = r->record_cache; rc; rc = rc->next)
1164     {
1165         if (pos == rc->pos)
1166         {
1167             if ((!elementSetName && !rc->elementSetName)
1168                 || (elementSetName && rc->elementSetName &&
1169                     !strcmp (elementSetName, rc->elementSetName)))
1170                 return &rc->rec;
1171         }
1172     }
1173     return 0;
1174 }
1175                                              
1176 static void handle_records (ZOOM_connection c, Z_Records *sr,
1177                             int present_phase)
1178 {
1179     ZOOM_resultset resultset;
1180
1181     if (!c->tasks)
1182         return ;
1183     switch (c->tasks->which)
1184     {
1185     case ZOOM_TASK_SEARCH:
1186         resultset = c->tasks->u.search.resultset;
1187         break;
1188     case ZOOM_TASK_RETRIEVE:
1189         resultset = c->tasks->u.retrieve.resultset;        
1190         break;
1191     default:
1192         return;
1193     }
1194     if (sr && sr->which == Z_Records_NSD)
1195     {
1196         Z_DiagRec dr, *dr_p = &dr;
1197         dr.which = Z_DiagRec_defaultFormat;
1198         dr.u.defaultFormat = sr->u.nonSurrogateDiagnostic;
1199         
1200         response_diag (c, dr_p);
1201     }
1202     else if (sr && sr->which == Z_Records_multipleNSD)
1203     {
1204         if (sr->u.multipleNonSurDiagnostics->num_diagRecs >= 1)
1205             response_diag(c, sr->u.multipleNonSurDiagnostics->diagRecs[0]);
1206         else
1207             c->error = ZOOM_ERROR_DECODE;
1208     }
1209     else 
1210     {
1211         if (resultset->count + resultset->start > resultset->size)
1212             resultset->count = resultset->size - resultset->start;
1213         if (resultset->count < 0)
1214             resultset->count = 0;
1215         if (sr && sr->which == Z_Records_DBOSD)
1216         {
1217             int i;
1218             NMEM nmem = odr_extract_mem (c->odr_in);
1219             Z_NamePlusRecordList *p =
1220                 sr->u.databaseOrSurDiagnostics;
1221             for (i = 0; i<p->num_records; i++)
1222             {
1223                 record_cache_add (resultset, p->records[i],
1224                                   i+ resultset->start, 0);
1225             }
1226             /* transfer our response to search_nmem .. we need it later */
1227             nmem_transfer (resultset->odr->mem, nmem);
1228             nmem_destroy (nmem);
1229             if (present_phase && p->num_records == 0)
1230             {
1231                 /* present response and we didn't get any records! */
1232                 c->error = ZOOM_ERROR_DECODE;
1233             }
1234         }
1235         else if (present_phase)
1236         {
1237             /* present response and we didn't get any records! */
1238             c->error = ZOOM_ERROR_DECODE;
1239         }
1240     }
1241 }
1242
1243 static void handle_present_response (ZOOM_connection c, Z_PresentResponse *pr)
1244 {
1245     handle_records (c, pr->records, 1);
1246 }
1247
1248 static void handle_search_response (ZOOM_connection c, Z_SearchResponse *sr)
1249 {
1250     ZOOM_resultset resultset;
1251
1252     yaz_log (LOG_DEBUG, "got search response");
1253
1254     if (!c->tasks || c->tasks->which != ZOOM_TASK_SEARCH)
1255         return ;
1256
1257     resultset = c->tasks->u.search.resultset;
1258
1259     resultset->size = *sr->resultCount;
1260     handle_records (c, sr->records, 0);
1261 }
1262
1263 static void sort_response (ZOOM_connection c, Z_SortResponse *res)
1264 {
1265     if (res->diagnostics && res->num_diagnostics > 0)
1266         response_diag (c, res->diagnostics[0]);
1267 }
1268
1269 static int scan_response (ZOOM_connection c, Z_ScanResponse *res)
1270 {
1271     NMEM nmem = odr_extract_mem (c->odr_in);
1272     ZOOM_scanset scan;
1273
1274     if (!c->tasks || c->tasks->which != ZOOM_TASK_SCAN)
1275         return 0;
1276     scan = c->tasks->u.scan.scan;
1277
1278     if (res->entries && res->entries->nonsurrogateDiagnostics)
1279         response_diag(c, res->entries->nonsurrogateDiagnostics[0]);
1280     scan->scan_response = res;
1281     nmem_transfer (scan->odr->mem, nmem);
1282     if (res->stepSize)
1283         ZOOM_options_set_int (scan->options, "stepSize", *res->stepSize);
1284     if (res->positionOfTerm)
1285         ZOOM_options_set_int (scan->options, "position", *res->positionOfTerm);
1286     if (res->scanStatus)
1287         ZOOM_options_set_int (scan->options, "scanStatus", *res->scanStatus);
1288     if (res->numberOfEntriesReturned)
1289         ZOOM_options_set_int (scan->options, "number",
1290                               *res->numberOfEntriesReturned);
1291     nmem_destroy (nmem);
1292     return 1;
1293 }
1294
1295 static int send_sort (ZOOM_connection c)
1296 {
1297     ZOOM_resultset  resultset;
1298
1299     if (!c->tasks || c->tasks->which != ZOOM_TASK_SEARCH)
1300         return 0;
1301
1302     resultset = c->tasks->u.search.resultset;
1303
1304     if (c->error)
1305     {
1306         resultset->r_sort_spec = 0;
1307         return 0;
1308     }
1309     if (resultset->r_sort_spec)
1310     {
1311         Z_APDU *apdu = zget_APDU(c->odr_out, Z_APDU_sortRequest);
1312         Z_SortRequest *req = apdu->u.sortRequest;
1313         
1314         req->num_inputResultSetNames = 1;
1315         req->inputResultSetNames = (Z_InternationalString **)
1316             odr_malloc (c->odr_out, sizeof(*req->inputResultSetNames));
1317         req->inputResultSetNames[0] =
1318             odr_strdup (c->odr_out, resultset->setname);
1319         req->sortedResultSetName = odr_strdup (c->odr_out, resultset->setname);
1320         req->sortSequence = resultset->r_sort_spec;
1321         resultset->r_sort_spec = 0;
1322         send_APDU (c, apdu);
1323         return 1;
1324     }
1325     return 0;
1326 }
1327
1328 static int send_present (ZOOM_connection c)
1329 {
1330     Z_APDU *apdu = zget_APDU(c->odr_out, Z_APDU_presentRequest);
1331     Z_PresentRequest *req = apdu->u.presentRequest;
1332     int i = 0;
1333     const char *syntax = 
1334         ZOOM_options_get (c->options, "preferredRecordSyntax");
1335     const char *element =
1336         ZOOM_options_get (c->options, "elementSetName");
1337     const char *schema =
1338         ZOOM_options_get (c->options, "schema");
1339     ZOOM_resultset  resultset;
1340
1341     if (!c->tasks)
1342         return 0;
1343
1344     switch (c->tasks->which)
1345     {
1346     case ZOOM_TASK_SEARCH:
1347         resultset = c->tasks->u.search.resultset;
1348         break;
1349     case ZOOM_TASK_RETRIEVE:
1350         resultset = c->tasks->u.retrieve.resultset;
1351         resultset->start = c->tasks->u.retrieve.start;
1352         resultset->count = c->tasks->u.retrieve.count;
1353
1354         if (resultset->start >= resultset->size)
1355             return 0;
1356         if (resultset->start + resultset->count > resultset->size)
1357             resultset->count = resultset->size - resultset->start;
1358         break;
1359     default:
1360         return 0;
1361     }
1362
1363     if (c->error)                  /* don't continue on error */
1364         return 0;
1365     if (resultset->start < 0)
1366         return 0;
1367     for (i = 0; i<resultset->count; i++)
1368     {
1369         ZOOM_record rec =
1370             record_cache_lookup (resultset, i + resultset->start, 0);
1371         if (!rec)
1372             break;
1373     }
1374     if (i == resultset->count)
1375         return 0;
1376
1377     resultset->start += i;
1378     resultset->count -= i;
1379     *req->resultSetStartPoint = resultset->start + 1;
1380     *req->numberOfRecordsRequested = resultset->count;
1381     assert (*req->numberOfRecordsRequested > 0);
1382
1383     if (syntax && *syntax)
1384         req->preferredRecordSyntax =
1385             yaz_str_to_z3950oid (c->odr_out, CLASS_RECSYN, syntax);
1386
1387     if (schema && *schema)
1388     {
1389         Z_RecordComposition *compo = (Z_RecordComposition *)
1390             odr_malloc (c->odr_out, sizeof(*compo));
1391
1392         req->recordComposition = compo;
1393         compo->which = Z_RecordComp_complex;
1394         compo->u.complex = (Z_CompSpec *)
1395             odr_malloc(c->odr_out, sizeof(*compo->u.complex));
1396         compo->u.complex->selectAlternativeSyntax = (bool_t *) 
1397             odr_malloc(c->odr_out, sizeof(bool_t));
1398         *compo->u.complex->selectAlternativeSyntax = 0;
1399
1400         compo->u.complex->generic = (Z_Specification *)
1401             odr_malloc(c->odr_out, sizeof(*compo->u.complex->generic));
1402
1403         compo->u.complex->generic->schema = (Odr_oid *)
1404             yaz_str_to_z3950oid (c->odr_out, CLASS_SCHEMA, schema);
1405
1406         if (!compo->u.complex->generic->schema)
1407         {
1408             /* OID wasn't a schema! Try record syntax instead. */
1409
1410             compo->u.complex->generic->schema = (Odr_oid *)
1411                 yaz_str_to_z3950oid (c->odr_out, CLASS_RECSYN, schema);
1412         }
1413         if (element && *element)
1414         {
1415             compo->u.complex->generic->elementSpec = (Z_ElementSpec *)
1416                 odr_malloc(c->odr_out, sizeof(Z_ElementSpec));
1417             compo->u.complex->generic->elementSpec->which =
1418                 Z_ElementSpec_elementSetName;
1419             compo->u.complex->generic->elementSpec->u.elementSetName =
1420                 odr_strdup (c->odr_out, element);
1421         }
1422         else
1423             compo->u.complex->generic->elementSpec = 0;
1424         compo->u.complex->num_dbSpecific = 0;
1425         compo->u.complex->dbSpecific = 0;
1426         compo->u.complex->num_recordSyntax = 0;
1427         compo->u.complex->recordSyntax = 0;
1428     }
1429     else if (element && *element)
1430     {
1431         Z_ElementSetNames *esn = (Z_ElementSetNames *)
1432             odr_malloc (c->odr_out, sizeof(*esn));
1433         Z_RecordComposition *compo = (Z_RecordComposition *)
1434             odr_malloc (c->odr_out, sizeof(*compo));
1435         
1436         esn->which = Z_ElementSetNames_generic;
1437         esn->u.generic = odr_strdup (c->odr_out, element);
1438         compo->which = Z_RecordComp_simple;
1439         compo->u.simple = esn;
1440         req->recordComposition = compo;
1441     }
1442     req->resultSetId = odr_strdup(c->odr_out, resultset->setname);
1443     send_APDU (c, apdu);
1444     return 1;
1445 }
1446
1447 ZOOM_API(ZOOM_scanset)
1448 ZOOM_connection_scan (ZOOM_connection c, const char *start)
1449 {
1450     ZOOM_scanset scan = (ZOOM_scanset) xmalloc (sizeof(*scan));
1451
1452     scan->connection = c;
1453     scan->odr = odr_createmem (ODR_DECODE);
1454     scan->options = ZOOM_options_create_with_parent (c->options);
1455     scan->refcount = 1;
1456     scan->scan_response = 0;
1457
1458     if ((scan->termListAndStartPoint =
1459          p_query_scan(scan->odr, PROTO_Z3950, &scan->attributeSet,
1460                       start)))
1461     {
1462         ZOOM_task task = ZOOM_connection_add_task (c, ZOOM_TASK_SCAN);
1463         task->u.scan.scan = scan;
1464         
1465         (scan->refcount)++;
1466         if (!c->async)
1467         {
1468             while (ZOOM_event (1, &c))
1469                 ;
1470         }
1471     }
1472     return scan;
1473 }
1474
1475 ZOOM_API(void)
1476 ZOOM_scanset_destroy (ZOOM_scanset scan)
1477 {
1478     if (!scan)
1479         return;
1480     (scan->refcount)--;
1481     if (scan->refcount == 0)
1482     {
1483         odr_destroy (scan->odr);
1484         
1485         ZOOM_options_destroy (scan->options);
1486         xfree (scan);
1487     }
1488 }
1489
1490 static int send_package (ZOOM_connection c)
1491 {
1492     ZOOM_Event event;
1493     if (!c->tasks)
1494         return 0;
1495     assert (c->tasks->which == ZOOM_TASK_PACKAGE);
1496
1497     event = ZOOM_Event_create (ZOOM_EVENT_SEND_APDU);
1498     ZOOM_connection_put_event (c, event);
1499
1500     do_write_ex (c, c->tasks->u.package->buf_out,
1501                  c->tasks->u.package->len_out);
1502     return 1;
1503 }
1504
1505 static int send_scan (ZOOM_connection c)
1506 {
1507     ZOOM_scanset scan;
1508     Z_APDU *apdu = zget_APDU(c->odr_out, Z_APDU_scanRequest);
1509     Z_ScanRequest *req = apdu->u.scanRequest;
1510     if (!c->tasks)
1511         return 0;
1512     assert (c->tasks->which == ZOOM_TASK_SCAN);
1513     scan = c->tasks->u.scan.scan;
1514
1515     req->termListAndStartPoint = scan->termListAndStartPoint;
1516     req->attributeSet = scan->attributeSet;
1517
1518     *req->numberOfTermsRequested =
1519         ZOOM_options_get_int(scan->options, "number", 10);
1520
1521     req->preferredPositionInResponse =
1522         odr_intdup (c->odr_out,
1523                     ZOOM_options_get_int(scan->options, "position", 1));
1524
1525     req->stepSize =
1526         odr_intdup (c->odr_out,
1527                     ZOOM_options_get_int(scan->options, "stepSize", 0));
1528     
1529     req->databaseNames = set_DatabaseNames (c, scan->options, 
1530                                             &req->num_databaseNames);
1531
1532     send_APDU (c, apdu);
1533
1534     return 1;
1535 }
1536
1537 ZOOM_API(size_t)
1538 ZOOM_scanset_size (ZOOM_scanset scan)
1539 {
1540     if (!scan || !scan->scan_response || !scan->scan_response->entries)
1541         return 0;
1542     return scan->scan_response->entries->num_entries;
1543 }
1544
1545 ZOOM_API(const char *)
1546 ZOOM_scanset_term (ZOOM_scanset scan, size_t pos,
1547                                int *occ, int *len)
1548 {
1549     const char *term = 0;
1550     size_t noent = ZOOM_scanset_size (scan);
1551     Z_ScanResponse *res = scan->scan_response;
1552     
1553     *len = 0;
1554     *occ = 0;
1555     if (pos >= noent)
1556         return 0;
1557     if (res->entries->entries[pos]->which == Z_Entry_termInfo)
1558     {
1559         Z_TermInfo *t = res->entries->entries[pos]->u.termInfo;
1560         
1561         if (t->term->which == Z_Term_general)
1562         {
1563             term = (const char *) t->term->u.general->buf;
1564             *len = t->term->u.general->len;
1565         }
1566         *occ = t->globalOccurrences ? *t->globalOccurrences : 0;
1567     }
1568     return term;
1569 }
1570
1571 ZOOM_API(const char *)
1572 ZOOM_scanset_option_get (ZOOM_scanset scan, const char *key)
1573 {
1574     return ZOOM_options_get (scan->options, key);
1575 }
1576
1577 ZOOM_API(void)
1578 ZOOM_scanset_option_set (ZOOM_scanset scan, const char *key,
1579                               const char *val)
1580 {
1581     ZOOM_options_set (scan->options, key, val);
1582 }
1583
1584
1585
1586 static Z_APDU *create_es_package (ZOOM_package p, int type)
1587 {
1588     const char *str;
1589     Z_APDU *apdu = zget_APDU(p->odr_out, Z_APDU_extendedServicesRequest);
1590     Z_ExtendedServicesRequest *req = apdu->u.extendedServicesRequest;
1591     
1592     *req->function = Z_ExtendedServicesRequest_create;
1593     
1594     str = ZOOM_options_get(p->options, "package-name");
1595     if (str && *str)
1596         req->packageName = nmem_strdup (p->odr_out->mem, str);
1597     
1598     str = ZOOM_options_get(p->options, "user-id");
1599     if (str)
1600         req->userId = nmem_strdup (p->odr_out->mem, str);
1601     
1602     req->packageType = yaz_oidval_to_z3950oid(p->odr_out, CLASS_EXTSERV,
1603                                               type);
1604
1605     str = ZOOM_options_get(p->options, "function");
1606     if (str)
1607     {
1608         if (!strcmp (str, "create"))
1609             *req->function = 1;
1610         if (!strcmp (str, "delete"))
1611             *req->function = 2;
1612         if (!strcmp (str, "modify"))
1613             *req->function = 3;
1614     }
1615     return apdu;
1616 }
1617
1618 static const char *ill_array_lookup (void *clientData, const char *idx)
1619 {
1620     ZOOM_package p = (ZOOM_package) clientData;
1621     return ZOOM_options_get (p->options, idx+4);
1622 }
1623
1624 static Z_External *encode_ill_request (ZOOM_package p)
1625 {
1626     ODR out = p->odr_out;
1627     ILL_Request *req;
1628     Z_External *r = 0;
1629     struct ill_get_ctl ctl;
1630         
1631     ctl.odr = p->odr_out;
1632     ctl.clientData = p;
1633     ctl.f = ill_array_lookup;
1634         
1635     req = ill_get_ILLRequest(&ctl, "ill", 0);
1636         
1637     if (!ill_Request (out, &req, 0, 0))
1638     {
1639         int ill_request_size;
1640         char *ill_request_buf = odr_getbuf (out, &ill_request_size, 0);
1641         if (ill_request_buf)
1642             odr_setbuf (out, ill_request_buf, ill_request_size, 1);
1643         return 0;
1644     }
1645     else
1646     {
1647         oident oid;
1648         int illRequest_size = 0;
1649         char *illRequest_buf = odr_getbuf (out, &illRequest_size, 0);
1650                 
1651         oid.proto = PROTO_GENERAL;
1652         oid.oclass = CLASS_GENERAL;
1653         oid.value = VAL_ISO_ILL_1;
1654                 
1655         r = (Z_External *) odr_malloc (out, sizeof(*r));
1656         r->direct_reference = odr_oiddup(out,oid_getoidbyent(&oid)); 
1657         r->indirect_reference = 0;
1658         r->descriptor = 0;
1659         r->which = Z_External_single;
1660                 
1661         r->u.single_ASN1_type = (Odr_oct *)
1662             odr_malloc (out, sizeof(*r->u.single_ASN1_type));
1663         r->u.single_ASN1_type->buf = odr_malloc (out, illRequest_size);
1664         r->u.single_ASN1_type->len = illRequest_size;
1665         r->u.single_ASN1_type->size = illRequest_size;
1666         memcpy (r->u.single_ASN1_type->buf, illRequest_buf, illRequest_size);
1667     }
1668     return r;
1669 }
1670
1671 static Z_ItemOrder *encode_item_order(ZOOM_package p)
1672 {
1673     Z_ItemOrder *req = odr_malloc (p->odr_out, sizeof(*req));
1674     const char *str;
1675     
1676     req->which=Z_IOItemOrder_esRequest;
1677     req->u.esRequest = (Z_IORequest *) 
1678         odr_malloc(p->odr_out,sizeof(Z_IORequest));
1679
1680     /* to keep part ... */
1681     req->u.esRequest->toKeep = (Z_IOOriginPartToKeep *)
1682         odr_malloc(p->odr_out,sizeof(Z_IOOriginPartToKeep));
1683     req->u.esRequest->toKeep->supplDescription = 0;
1684     req->u.esRequest->toKeep->contact =
1685         odr_malloc (p->odr_out, sizeof(*req->u.esRequest->toKeep->contact));
1686         
1687     str = ZOOM_options_get(p->options, "contact-name");
1688     req->u.esRequest->toKeep->contact->name = str ?
1689         nmem_strdup (p->odr_out->mem, str) : 0;
1690         
1691     str = ZOOM_options_get(p->options, "contact-phone");
1692     req->u.esRequest->toKeep->contact->phone = str ?
1693         nmem_strdup (p->odr_out->mem, str) : 0;
1694         
1695     str = ZOOM_options_get(p->options, "contact-email");
1696     req->u.esRequest->toKeep->contact->email = str ?
1697         nmem_strdup (p->odr_out->mem, str) : 0;
1698         
1699     req->u.esRequest->toKeep->addlBilling = 0;
1700         
1701     /* not to keep part ... */
1702     req->u.esRequest->notToKeep = (Z_IOOriginPartNotToKeep *)
1703         odr_malloc(p->odr_out,sizeof(Z_IOOriginPartNotToKeep));
1704         
1705     req->u.esRequest->notToKeep->resultSetItem = (Z_IOResultSetItem *)
1706         odr_malloc(p->odr_out, sizeof(Z_IOResultSetItem));
1707
1708     str = ZOOM_options_get(p->options, "itemorder-setname");
1709     if (!str)
1710         str = "default";
1711     req->u.esRequest->notToKeep->resultSetItem->resultSetId =
1712         nmem_strdup (p->odr_out->mem, str);
1713     req->u.esRequest->notToKeep->resultSetItem->item =
1714         (int *) odr_malloc(p->odr_out, sizeof(int));
1715         
1716     str = ZOOM_options_get(p->options, "itemorder-item");
1717     *req->u.esRequest->notToKeep->resultSetItem->item =
1718         (str ? atoi(str) : 1);
1719     
1720     req->u.esRequest->notToKeep->itemRequest = encode_ill_request(p);
1721     
1722     return req;
1723 }
1724
1725 ZOOM_API(void)
1726     ZOOM_package_send (ZOOM_package p, const char *type)
1727 {
1728     Z_APDU *apdu = 0;
1729     ZOOM_connection c;
1730     if (!p)
1731         return;
1732     c = p->connection;
1733     odr_reset (p->odr_out);
1734     xfree (p->buf_out);
1735     p->buf_out = 0;
1736     if (!strcmp(type, "itemorder"))
1737     {
1738         Z_External *r;
1739         apdu = create_es_package (p, VAL_ITEMORDER);
1740         if (apdu)
1741         {
1742             r = odr_malloc (p->odr_out, sizeof(*r));
1743             
1744             r->direct_reference =
1745                 yaz_oidval_to_z3950oid(p->odr_out, CLASS_EXTSERV,
1746                                        VAL_ITEMORDER);
1747             r->descriptor = 0;
1748             r->which = Z_External_itemOrder;
1749             r->indirect_reference = 0;
1750             r->u.itemOrder = encode_item_order (p);
1751
1752             apdu->u.extendedServicesRequest->taskSpecificParameters = r;
1753         }
1754     }
1755     if (apdu)
1756     {
1757         if (encode_APDU(p->connection, apdu, p->odr_out) == 0)
1758         {
1759             char *buf;
1760
1761             ZOOM_task task = ZOOM_connection_add_task (c, ZOOM_TASK_PACKAGE);
1762             task->u.package = p;
1763             buf = odr_getbuf(p->odr_out, &p->len_out, 0);
1764             p->buf_out = xmalloc (p->len_out);
1765             memcpy (p->buf_out, buf, p->len_out);
1766             
1767             (p->refcount)++;
1768             if (!c->async)
1769             {
1770                 while (ZOOM_event (1, &c))
1771                     ;
1772             }
1773         }
1774     }
1775 }
1776
1777 ZOOM_API(ZOOM_package)
1778     ZOOM_connection_package (ZOOM_connection c, ZOOM_options options)
1779 {
1780     ZOOM_package p = (ZOOM_package) xmalloc (sizeof(*p));
1781
1782     p->connection = c;
1783     p->odr_out = odr_createmem (ODR_ENCODE);
1784     p->options = ZOOM_options_create_with_parent2 (options, c->options);
1785     p->refcount = 1;
1786     p->buf_out = 0;
1787     p->len_out = 0;
1788     return p;
1789 }
1790
1791 ZOOM_API(void)
1792     ZOOM_package_destroy(ZOOM_package p)
1793 {
1794     if (!p)
1795         return;
1796     (p->refcount)--;
1797     if (p->refcount == 0)
1798     {
1799         odr_destroy (p->odr_out);
1800         xfree (p->buf_out);
1801         
1802         ZOOM_options_destroy (p->options);
1803         xfree (p);
1804     }
1805 }
1806
1807 ZOOM_API(const char *)
1808 ZOOM_package_option_get (ZOOM_package p, const char *key)
1809 {
1810     return ZOOM_options_get (p->options, key);
1811 }
1812
1813 ZOOM_API(void)
1814 ZOOM_package_option_set (ZOOM_package p, const char *key,
1815                               const char *val)
1816 {
1817     ZOOM_options_set (p->options, key, val);
1818 }
1819
1820 static int ZOOM_connection_exec_task (ZOOM_connection c)
1821 {
1822     ZOOM_task task = c->tasks;
1823
1824     yaz_log (LOG_DEBUG, "ZOOM_connection_exec_task");
1825     if (!task)
1826         return 0;
1827     if (c->error != ZOOM_ERROR_NONE ||
1828         (!c->cs && task->which != ZOOM_TASK_CONNECT))
1829     {
1830         ZOOM_connection_remove_tasks (c);
1831         return 0;
1832     }
1833     yaz_log (LOG_DEBUG, "ZOOM_connection_exec_task type=%d run=%d",
1834              task->which, task->running);
1835     if (task->running)
1836         return 0;
1837     task->running = 1;
1838     switch (task->which)
1839     {
1840     case ZOOM_TASK_SEARCH:
1841         /* see if search hasn't been sent yet. */
1842         if (ZOOM_connection_send_search (c))
1843             return 1;
1844         break;
1845     case ZOOM_TASK_RETRIEVE:
1846         if (send_present (c))
1847             return 1;
1848         break;
1849     case ZOOM_TASK_CONNECT:
1850         if (do_connect(c))
1851             return 1;
1852         break;
1853     case ZOOM_TASK_SCAN:
1854         if (send_scan(c))
1855             return 1;
1856         break;
1857     case ZOOM_TASK_PACKAGE:
1858         if (send_package(c))
1859             return 1;
1860     }
1861     ZOOM_connection_remove_task (c);
1862     return 0;
1863 }
1864
1865 static int send_sort_present (ZOOM_connection c)
1866 {
1867     int r = send_sort (c);
1868     if (!r)
1869         r = send_present (c);
1870     return r;
1871 }
1872
1873 static int es_response (ZOOM_connection c,
1874                         Z_ExtendedServicesResponse *res)
1875 {
1876     if (!c->tasks || c->tasks->which != ZOOM_TASK_PACKAGE)
1877         return 0;
1878     if (res->diagnostics && res->num_diagnostics > 0)
1879         response_diag(c, res->diagnostics[0]);
1880     if (res->taskPackage &&
1881         res->taskPackage->which == Z_External_extendedService)
1882     {
1883         Z_TaskPackage *taskPackage = res->taskPackage->u.extendedService;
1884         Odr_oct *id = taskPackage->targetReference;
1885         
1886         if (id)
1887             ZOOM_options_setl (c->tasks->u.package->options,
1888                                "targetReference", id->buf, id->len);
1889     }
1890     return 1;
1891 }
1892
1893
1894 static void handle_apdu (ZOOM_connection c, Z_APDU *apdu)
1895 {
1896     Z_InitResponse *initrs;
1897     
1898     c->mask = 0;
1899     yaz_log (LOG_DEBUG, "hande_apdu type=%d", apdu->which);
1900     switch(apdu->which)
1901     {
1902     case Z_APDU_initResponse:
1903         initrs = apdu->u.initResponse;
1904         if (!*initrs->result)
1905         {
1906             c->error = ZOOM_ERROR_INIT;
1907         }
1908         else
1909         {
1910             char *cookie =
1911                 yaz_oi_get_string_oidval (&apdu->u.initResponse->otherInfo,
1912                                           VAL_COOKIE, 1, 0);
1913             xfree (c->cookie_in);
1914             c->cookie_in = 0;
1915             if (cookie)
1916                 c->cookie_in = xstrdup(cookie);
1917             if (ODR_MASK_GET(initrs->options, Z_Options_namedResultSets) &&
1918                 ODR_MASK_GET(initrs->protocolVersion, Z_ProtocolVersion_3))
1919                 c->support_named_resultsets = 1;
1920             if (c->tasks)
1921             {
1922                 assert (c->tasks->which == ZOOM_TASK_CONNECT);
1923                 ZOOM_connection_remove_task (c);
1924             }
1925             ZOOM_connection_exec_task (c);
1926         }
1927         if (ODR_MASK_GET(initrs->options, Z_Options_negotiationModel))
1928         {
1929             NMEM tmpmem = nmem_create();
1930             Z_CharSetandLanguageNegotiation *p =
1931                 yaz_get_charneg_record(initrs->otherInfo);
1932             
1933             if (p)
1934             {
1935                 char *charset=NULL, *lang=NULL;
1936                 int selected;
1937                 
1938                 yaz_get_response_charneg(tmpmem, p, &charset, &lang, &selected);
1939                 yaz_log(LOG_DEBUG, "Target accepted: charset - %s,"
1940                         "language - %s, select - %d",
1941                         charset, lang, selected);
1942                 
1943                 nmem_destroy(tmpmem);
1944             }
1945         }       
1946         break;
1947     case Z_APDU_searchResponse:
1948         handle_search_response (c, apdu->u.searchResponse);
1949         if (!send_sort_present (c))
1950             ZOOM_connection_remove_task (c);
1951         break;
1952     case Z_APDU_presentResponse:
1953         handle_present_response (c, apdu->u.presentResponse);
1954         if (!send_present (c))
1955             ZOOM_connection_remove_task (c);
1956         break;
1957     case Z_APDU_sortResponse:
1958         sort_response (c, apdu->u.sortResponse);
1959         if (!send_present (c))
1960             ZOOM_connection_remove_task (c);
1961         break;
1962     case Z_APDU_scanResponse:
1963         scan_response (c, apdu->u.scanResponse);
1964         ZOOM_connection_remove_task (c);
1965         break;
1966     case Z_APDU_extendedServicesResponse:
1967         es_response (c, apdu->u.extendedServicesResponse);
1968         ZOOM_connection_remove_task (c);
1969         break;
1970     case Z_APDU_close:
1971         if (c->reconnect_ok)
1972         {
1973             do_close(c);
1974             c->tasks->running = 0;
1975             ZOOM_connection_insert_task (c, ZOOM_TASK_CONNECT);
1976         }
1977         else
1978         {
1979             c->error = ZOOM_ERROR_CONNECTION_LOST;
1980             do_close(c);
1981         }
1982         break;
1983     default:
1984         c->error = ZOOM_ERROR_DECODE;
1985         do_close(c);
1986     }
1987 }
1988
1989 static int do_read (ZOOM_connection c)
1990 {
1991     int r;
1992     Z_APDU *apdu;
1993     ZOOM_Event event;
1994     
1995     event = ZOOM_Event_create (ZOOM_EVENT_RECV_DATA);
1996     ZOOM_connection_put_event (c, event);
1997     
1998     yaz_log (LOG_DEBUG, "do_read len=%d", c->len_in);
1999
2000     r = cs_get (c->cs, &c->buf_in, &c->len_in);
2001     if (r == 1)
2002         return 0;
2003     if (r <= 0)
2004     {
2005         if (c->reconnect_ok)
2006         {
2007             do_close (c);
2008             c->reconnect_ok = 0;
2009             yaz_log (LOG_DEBUG, "reconnect read");
2010             c->tasks->running = 0;
2011             ZOOM_connection_insert_task (c, ZOOM_TASK_CONNECT);
2012         }
2013         else
2014         {
2015             c->error= ZOOM_ERROR_CONNECTION_LOST;
2016             do_close (c);
2017         }
2018     }
2019     else
2020     {
2021         ZOOM_Event event;
2022         odr_reset (c->odr_in);
2023         odr_setbuf (c->odr_in, c->buf_in, r, 0);
2024         event = ZOOM_Event_create (ZOOM_EVENT_RECV_APDU);
2025         ZOOM_connection_put_event (c, event);
2026         if (!z_APDU (c->odr_in, &apdu, 0, 0))
2027         {
2028             c->error = ZOOM_ERROR_DECODE;
2029             do_close (c);
2030         }
2031         else
2032             handle_apdu (c, apdu);
2033         c->reconnect_ok = 0;
2034     }
2035     return 1;
2036 }
2037
2038 static int do_write_ex (ZOOM_connection c, char *buf_out, int len_out)
2039 {
2040     int r;
2041     ZOOM_Event event;
2042     
2043     event = ZOOM_Event_create(ZOOM_EVENT_SEND_DATA);
2044     ZOOM_connection_put_event (c, event);
2045
2046     yaz_log (LOG_DEBUG, "do_write_ex len=%d", len_out);
2047     if ((r=cs_put (c->cs, buf_out, len_out)) < 0)
2048     {
2049         if (c->reconnect_ok)
2050         {
2051             do_close (c);
2052             c->reconnect_ok = 0;
2053             yaz_log (LOG_DEBUG, "reconnect write");
2054             c->tasks->running = 0;
2055             ZOOM_connection_insert_task (c, ZOOM_TASK_CONNECT);
2056             return 0;
2057         }
2058         if (c->state == STATE_CONNECTING)
2059             c->error = ZOOM_ERROR_CONNECT;
2060         else
2061             c->error = ZOOM_ERROR_CONNECTION_LOST;
2062         do_close (c);
2063         return 1;
2064     }
2065     else if (r == 1)
2066     {    
2067         c->mask = ZOOM_SELECT_EXCEPT;
2068         if (c->cs->io_pending & CS_WANT_WRITE)
2069             c->mask += ZOOM_SELECT_WRITE;
2070         if (c->cs->io_pending & CS_WANT_READ)
2071             c->mask += ZOOM_SELECT_READ;
2072         yaz_log (LOG_DEBUG, "do_write_ex 1 mask=%d", c->mask);
2073     }
2074     else
2075     {
2076         // c->reconnect_ok = 0;
2077         c->mask = ZOOM_SELECT_READ|ZOOM_SELECT_EXCEPT;
2078         yaz_log (LOG_DEBUG, "do_write_ex 2 mask=%d", c->mask);
2079     }
2080     return 0;
2081 }
2082
2083 static int do_write(ZOOM_connection c)
2084 {
2085     return do_write_ex (c, c->buf_out, c->len_out);
2086 }
2087
2088
2089 ZOOM_API(const char *)
2090 ZOOM_connection_option_get (ZOOM_connection c, const char *key)
2091 {
2092     return ZOOM_options_get (c->options, key);
2093 }
2094
2095 ZOOM_API(void)
2096 ZOOM_connection_option_set (ZOOM_connection c, const char *key,
2097                                   const char *val)
2098 {
2099     ZOOM_options_set (c->options, key, val);
2100 }
2101
2102 ZOOM_API(const char *)
2103 ZOOM_resultset_option_get (ZOOM_resultset r, const char *key)
2104 {
2105     return ZOOM_options_get (r->options, key);
2106 }
2107
2108 ZOOM_API(void)
2109 ZOOM_resultset_option_set (ZOOM_resultset r, const char *key,
2110                                   const char *val)
2111 {
2112     ZOOM_options_set (r->options, key, val);
2113 }
2114
2115
2116 ZOOM_API(int)
2117 ZOOM_connection_errcode (ZOOM_connection c)
2118 {
2119     return ZOOM_connection_error (c, 0, 0);
2120 }
2121
2122 ZOOM_API(const char *)
2123 ZOOM_connection_errmsg (ZOOM_connection c)
2124 {
2125     const char *msg;
2126     ZOOM_connection_error (c, &msg, 0);
2127     return msg;
2128 }
2129
2130 ZOOM_API(const char *)
2131 ZOOM_connection_addinfo (ZOOM_connection c)
2132 {
2133     const char *addinfo;
2134     ZOOM_connection_error (c, 0, &addinfo);
2135     return addinfo;
2136 }
2137
2138 ZOOM_API(int)
2139 ZOOM_connection_error (ZOOM_connection c, const char **cp,
2140                             const char **addinfo)
2141 {
2142     int error = c->error;
2143     if (cp)
2144     {
2145         switch (error)
2146         {
2147         case ZOOM_ERROR_NONE:
2148             *cp = "No error"; break;
2149         case ZOOM_ERROR_CONNECT:
2150             *cp = "Connect failed"; break;
2151         case ZOOM_ERROR_MEMORY:
2152             *cp = "Out of memory"; break;
2153         case ZOOM_ERROR_ENCODE:
2154             *cp = "Encoding failed"; break;
2155         case ZOOM_ERROR_DECODE:
2156             *cp = "Decoding failed"; break;
2157         case ZOOM_ERROR_CONNECTION_LOST:
2158             *cp = "Connection lost"; break;
2159         case ZOOM_ERROR_INIT:
2160             *cp = "Init rejected"; break;
2161         case ZOOM_ERROR_INTERNAL:
2162             *cp = "Internal failure"; break;
2163         case ZOOM_ERROR_TIMEOUT:
2164             *cp = "Timeout"; break;
2165         default:
2166             *cp = diagbib1_str (error);
2167         }
2168     }
2169     if (addinfo)
2170     {
2171         if (c->addinfo)
2172             *addinfo = c->addinfo;
2173         else
2174             *addinfo = "";
2175     }
2176     return c->error;
2177 }
2178
2179 static int ZOOM_connection_do_io(ZOOM_connection c, int mask)
2180 {
2181     ZOOM_Event event = 0;
2182     int r = cs_look(c->cs);
2183     yaz_log (LOG_DEBUG, "ZOOM_connection_do_io c=%p mask=%d cs_look=%d",
2184              c, mask, r);
2185     
2186     if (r == CS_NONE)
2187     {
2188         event = ZOOM_Event_create (ZOOM_EVENT_CONNECT);
2189         c->error = ZOOM_ERROR_CONNECT;
2190         do_close (c);
2191         ZOOM_connection_put_event (c, event);
2192     }
2193     else if (r == CS_CONNECT)
2194     {
2195         int ret;
2196         event = ZOOM_Event_create (ZOOM_EVENT_CONNECT);
2197
2198         ret = cs_rcvconnect (c->cs);
2199         yaz_log (LOG_DEBUG, "cs_rcvconnect returned %d", ret);
2200         if (ret == 1)
2201         {
2202             c->mask = ZOOM_SELECT_EXCEPT;
2203             if (c->cs->io_pending & CS_WANT_WRITE)
2204                 c->mask += ZOOM_SELECT_WRITE;
2205             if (c->cs->io_pending & CS_WANT_READ)
2206                 c->mask += ZOOM_SELECT_READ;
2207             ZOOM_connection_put_event (c, event);
2208         }
2209         else if (ret == 0)
2210         {
2211             ZOOM_connection_put_event (c, event);
2212             ZOOM_connection_send_init (c);
2213             c->state = STATE_ESTABLISHED;
2214         }
2215         else
2216         {
2217             c->error = ZOOM_ERROR_CONNECT;
2218             do_close (c);
2219             ZOOM_connection_put_event (c, event);
2220         }
2221     }
2222     else
2223     {
2224         if (mask & ZOOM_SELECT_READ)
2225             do_read (c);
2226         if (c->cs && (mask & ZOOM_SELECT_WRITE))
2227             do_write (c);
2228     }
2229     return 1;
2230 }
2231
2232 ZOOM_API(int)
2233 ZOOM_connection_last_event(ZOOM_connection cs)
2234 {
2235     if (!cs)
2236         return ZOOM_EVENT_NONE;
2237     return cs->last_event;
2238 }
2239
2240 ZOOM_API(int)
2241 ZOOM_event (int no, ZOOM_connection *cs)
2242 {
2243 #if HAVE_SYS_POLL_H
2244     struct pollfd pollfds[1024];
2245     ZOOM_connection poll_cs[1024];
2246 #else
2247     struct timeval tv;
2248     fd_set input, output, except;
2249 #endif
2250     int i, r, nfds;
2251     int max_fd = 0;
2252
2253     for (i = 0; i<no; i++)
2254     {
2255         ZOOM_connection c = cs[i];
2256         ZOOM_Event event;
2257         if (c && (event = ZOOM_connection_get_event(c)))
2258         {
2259             ZOOM_Event_destroy (event);
2260             return i+1;
2261         }
2262     }
2263     for (i = 0; i<no; i++)
2264     {
2265         ZOOM_connection c = cs[i];
2266         ZOOM_Event event;
2267         if (c && ZOOM_connection_exec_task (c))
2268         {
2269             if ((event = ZOOM_connection_get_event(c)))
2270             {
2271                 ZOOM_Event_destroy (event);
2272                 return i+1;
2273             }
2274         }
2275     }
2276 #if HAVE_SYS_POLL_H
2277
2278 #else
2279     tv.tv_sec = 15;
2280     tv.tv_usec = 0;
2281     
2282     FD_ZERO (&input);
2283     FD_ZERO (&output);
2284     FD_ZERO (&except);
2285 #endif
2286     nfds = 0;
2287     for (i = 0; i<no; i++)
2288     {
2289         ZOOM_connection c = cs[i];
2290         int fd, mask;
2291         
2292         if (!c)
2293             continue;
2294         fd = z3950_connection_socket(c);
2295         mask = z3950_connection_mask(c);
2296
2297         if (fd == -1)
2298             continue;
2299         if (max_fd < fd)
2300             max_fd = fd;
2301
2302 #if HAVE_SYS_POLL_H
2303         if (mask)
2304         {
2305             short poll_events = 0;
2306
2307             if (mask & ZOOM_SELECT_READ)
2308                 poll_events += POLLIN;
2309             if (mask & ZOOM_SELECT_WRITE)
2310                 poll_events += POLLOUT;
2311             if (mask & ZOOM_SELECT_EXCEPT)
2312                 poll_events += POLLERR;
2313             pollfds[nfds].fd = fd;
2314             pollfds[nfds].events = poll_events;
2315             pollfds[nfds].revents = 0;
2316             poll_cs[nfds] = c;
2317             nfds++;
2318         }
2319 #else
2320         if (mask & ZOOM_SELECT_READ)
2321         {
2322             FD_SET (fd, &input);
2323             nfds++;
2324         }
2325         if (mask & ZOOM_SELECT_WRITE)
2326         {
2327             FD_SET (fd, &output);
2328             nfds++;
2329         }
2330         if (mask & ZOOM_SELECT_EXCEPT)
2331         {
2332             FD_SET (fd, &except);
2333             nfds++;
2334         }
2335 #endif
2336     }
2337     if (!nfds)
2338         return 0;
2339 #if HAVE_SYS_POLL_H
2340     r = poll (pollfds, nfds, 15000);
2341     for (i = 0; i<nfds; i++)
2342     {
2343         ZOOM_connection c = poll_cs[i];
2344         if (r && c->mask)
2345         {
2346             int mask = 0;
2347             if (pollfds[i].revents & POLLIN)
2348                 mask += ZOOM_SELECT_READ;
2349             if (pollfds[i].revents & POLLOUT)
2350                 mask += ZOOM_SELECT_WRITE;
2351             if (pollfds[i].revents & POLLERR)
2352                 mask += ZOOM_SELECT_EXCEPT;
2353             if (mask)
2354                 ZOOM_connection_do_io(c, mask);
2355         }
2356         else if (r == 0 && c->mask)
2357         {
2358             ZOOM_Event event = ZOOM_Event_create(ZOOM_EVENT_TIMEOUT);
2359             /* timeout and this connection was waiting */
2360             c->error = ZOOM_ERROR_TIMEOUT;
2361             do_close (c);
2362             ZOOM_connection_put_event(c, event);
2363         }
2364     }
2365 #else
2366     yaz_log (LOG_DEBUG, "select start");
2367     r = select (max_fd+1, &input, &output, &except, &tv);
2368     yaz_log (LOG_DEBUG, "select stop, returned r=%d", r);
2369     for (i = 0; i<no; i++)
2370     {
2371         ZOOM_connection c = cs[i];
2372         int fd, mask;
2373
2374         if (!c)
2375             continue;
2376         fd = z3950_connection_socket(c);
2377         mask = 0;
2378         if (r && c->mask)
2379         {
2380             /* no timeout and real socket */
2381             if (FD_ISSET(fd, &input))
2382                 mask += ZOOM_SELECT_READ;
2383             if (FD_ISSET(fd, &output))
2384                 mask += ZOOM_SELECT_WRITE;
2385             if (FD_ISSET(fd, &except))
2386                 mask += ZOOM_SELECT_EXCEPT;
2387             if (mask)
2388                 ZOOM_connection_do_io(c, mask);
2389         }
2390         if (r == 0 && c->mask)
2391         {
2392             ZOOM_Event event = ZOOM_Event_create(ZOOM_EVENT_TIMEOUT);
2393             /* timeout and this connection was waiting */
2394             c->error = ZOOM_ERROR_TIMEOUT;
2395             do_close (c);
2396             yaz_log (LOG_DEBUG, "timeout");
2397             ZOOM_connection_put_event(c, event);
2398         }
2399     }
2400 #endif
2401     for (i = 0; i<no; i++)
2402     {
2403         ZOOM_connection c = cs[i];
2404         ZOOM_Event event;
2405         if (c && (event = ZOOM_connection_get_event(c)))
2406         {
2407             ZOOM_Event_destroy (event);
2408             return i+1;
2409         }
2410     }
2411     return 0;
2412 }