1da198ded4b4344f51b2019db308e9f55f0b454d
[yaz-moved-to-github.git] / zoom / zoom-c.c
1 /*
2  * $Id: zoom-c.c,v 1.15 2001-12-30 22:21:11 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
13 #include "zoom-p.h"
14
15 #if HAVE_SYS_POLL_H
16 #include <sys/poll.h>
17 #endif
18
19 static ZOOM_Event ZOOM_Event_create (int kind)
20 {
21     ZOOM_Event event = xmalloc (sizeof(*event));
22     event->kind = kind;
23     event->next = 0;
24     event->prev = 0;
25     return event;
26 }
27
28 static void ZOOM_Event_destroy (ZOOM_Event event)
29 {
30     xfree (event);
31 }
32
33 static void ZOOM_connection_put_event (ZOOM_connection c, ZOOM_Event event)
34 {
35     // put in back of queue
36     if (c->m_queue_back)
37     {
38         c->m_queue_back->prev = event;
39         assert (c->m_queue_front);
40     }
41     else
42     {
43         assert (!c->m_queue_front);
44         c->m_queue_front = event;
45     }
46     event->next = c->m_queue_back;
47     event->prev = 0;
48     c->m_queue_back = event;
49 }
50
51 static ZOOM_Event ZOOM_connection_get_event(ZOOM_connection c)
52 {
53     // get from front of queue
54     ZOOM_Event event = c->m_queue_front;
55     if (!event)
56         return 0;
57     assert (c->m_queue_back);
58     c->m_queue_front = event->prev;
59     if (c->m_queue_front)
60     {
61         assert (c->m_queue_back);
62         c->m_queue_front->next = 0;
63     }
64     else
65         c->m_queue_back = 0;
66     c->last_event = event->kind;
67     return event;
68 }
69
70 static void clear_error (ZOOM_connection c)
71 {
72     c->error = ZOOM_ERROR_NONE;
73     xfree (c->addinfo);
74     c->addinfo = 0;
75 }
76
77 ZOOM_task ZOOM_connection_add_task (ZOOM_connection c, int which)
78 {
79     ZOOM_task *taskp = &c->tasks;
80     while (*taskp)
81         taskp = &(*taskp)->next;
82     *taskp = xmalloc (sizeof(**taskp));
83     (*taskp)->running = 0;
84     (*taskp)->which = which;
85     (*taskp)->next = 0;
86     clear_error (c);
87     return *taskp;
88 }
89
90 void ZOOM_connection_remove_task (ZOOM_connection c)
91 {
92     ZOOM_task task = c->tasks;
93
94     if (task)
95     {
96         c->tasks = task->next;
97         switch (task->which)
98         {
99         case ZOOM_TASK_SEARCH:
100             ZOOM_resultset_destroy (task->u.search.resultset);
101             break;
102         case ZOOM_TASK_RETRIEVE:
103             ZOOM_resultset_destroy (task->u.retrieve.resultset);
104             break;
105         case ZOOM_TASK_CONNECT:
106             break;
107         case ZOOM_TASK_SCAN:
108             ZOOM_scanset_destroy (task->u.scan.scan);
109             break;
110         default:
111             assert (0);
112         }
113         xfree (task);
114     }
115 }
116
117 void ZOOM_connection_remove_tasks (ZOOM_connection c)
118 {
119     while (c->tasks)
120         ZOOM_connection_remove_task(c);
121 }
122
123 static ZOOM_record record_cache_lookup (ZOOM_resultset r,
124                                          int pos,
125                                          const char *elementSetName);
126
127 ZOOM_connection ZOOM_connection_create (ZOOM_options options)
128 {
129     ZOOM_connection c = xmalloc (sizeof(*c));
130
131     c->cs = 0;
132     c->mask = 0;
133     c->state = STATE_IDLE;
134     c->error = ZOOM_ERROR_NONE;
135     c->addinfo = 0;
136     c->buf_in = 0;
137     c->len_in = 0;
138     c->buf_out = 0;
139     c->len_out = 0;
140     c->resultsets = 0;
141
142     c->options = ZOOM_options_create_with_parent(options);
143
144     c->host_port = 0;
145     c->proxy = 0;
146
147     c->cookie_out = 0;
148     c->cookie_in = 0;
149     c->tasks = 0;
150
151     c->odr_in = odr_createmem (ODR_DECODE);
152     c->odr_out = odr_createmem (ODR_ENCODE);
153
154     c->async = 0;
155     c->support_named_resultsets = 0;
156     c->last_event = ZOOM_EVENT_NONE;
157
158     c->m_queue_front = 0;
159     c->m_queue_back = 0;
160     return c;
161 }
162
163 /* set database names. Take local databases (if set); otherwise
164    take databases given in ZURL (if set); otherwise use Default */
165 static char **set_DatabaseNames (ZOOM_connection con, ZOOM_options options,
166                                  int *num)
167 {
168     char **databaseNames;
169     const char *c;
170     int no = 2;
171     const char *cp = ZOOM_options_get (options, "databaseName");
172     
173     if (!cp || !*cp)
174     {
175         cp = strchr (con->host_port, '/');
176         if (cp)
177             cp++;
178         }
179     if (cp)
180     {
181         c = cp;
182         while ((c = strchr(c, '+')))
183         {
184             c++;
185             no++;
186         }
187     }
188     else
189         cp = "Default";
190     databaseNames = odr_malloc (con->odr_out, no * sizeof(*databaseNames));
191     no = 0;
192     while (*cp)
193     {
194         c = strchr (cp, '+');
195         if (!c)
196             c = cp + strlen(cp);
197         else if (c == cp)
198         {
199             cp++;
200             continue;
201         }
202         /* cp ptr to first char of db name, c is char
203            following db name */
204         databaseNames[no] = odr_malloc (con->odr_out, 1+c-cp);
205         memcpy (databaseNames[no], cp, c-cp);
206         databaseNames[no++][c-cp] = '\0';
207         cp = c;
208         if (*cp)
209             cp++;
210     }
211     databaseNames[no] = NULL;
212     *num = no;
213     return databaseNames;
214 }
215
216 ZOOM_connection ZOOM_connection_new (const char *host, int portnum)
217 {
218     ZOOM_connection c = ZOOM_connection_create (0);
219
220     ZOOM_connection_connect (c, host, portnum);
221     return c;
222 }
223
224 void ZOOM_connection_connect(ZOOM_connection c,
225                               const char *host, int portnum)
226 {
227     const char *val;
228     ZOOM_task task;
229
230     val = ZOOM_options_get (c->options, "proxy");
231     if (val && *val)
232         c->proxy = xstrdup (val);
233     else
234         c->proxy = 0;
235
236     if (portnum)
237     {
238         char hostn[128];
239         sprintf (hostn, "%.80s:%d", host, portnum);
240         c->host_port = xstrdup(hostn);
241     }
242     else
243         c->host_port = xstrdup(host);
244
245     ZOOM_options_set(c->options, "host", c->host_port);
246
247     c->async = ZOOM_options_get_bool (c->options, "async", 0);
248     
249     task = ZOOM_connection_add_task (c, ZOOM_TASK_CONNECT);
250
251     if (!c->async)
252     {
253         while (ZOOM_event (1, &c))
254             ;
255     }
256 }
257
258 ZOOM_query ZOOM_query_create(void)
259 {
260     ZOOM_query s = xmalloc (sizeof(*s));
261
262     s->refcount = 1;
263     s->query = 0;
264     s->sort_spec = 0;
265     s->odr = odr_createmem (ODR_ENCODE);
266
267     return s;
268 }
269
270 void ZOOM_query_destroy(ZOOM_query s)
271 {
272     if (!s)
273         return;
274
275     (s->refcount)--;
276     yaz_log (LOG_DEBUG, "ZOOM_query_destroy count=%d", s->refcount);
277     if (s->refcount == 0)
278     {
279         odr_destroy (s->odr);
280         xfree (s);
281     }
282 }
283
284 int ZOOM_query_prefix(ZOOM_query s, const char *str)
285 {
286     s->query = odr_malloc (s->odr, sizeof(*s->query));
287     s->query->which = Z_Query_type_1;
288     s->query->u.type_1 =  p_query_rpn(s->odr, PROTO_Z3950, str);
289     if (!s->query->u.type_1)
290         return -1;
291     return 0;
292 }
293
294 int ZOOM_query_sortby(ZOOM_query s, const char *criteria)
295 {
296     s->sort_spec = yaz_sort_spec (s->odr, criteria);
297     if (!s->sort_spec)
298         return -1;
299     return 0;
300 }
301
302 static int do_write(ZOOM_connection c);
303
304 void ZOOM_connection_destroy(ZOOM_connection c)
305 {
306     ZOOM_resultset r;
307     if (!c)
308         return;
309     if (c->cs)
310         cs_close (c->cs);
311     for (r = c->resultsets; r; r = r->next)
312         r->connection = 0;
313
314     xfree (c->buf_in);
315     xfree (c->addinfo);
316     odr_destroy (c->odr_in);
317     odr_destroy (c->odr_out);
318     ZOOM_options_destroy (c->options);
319     ZOOM_connection_remove_tasks (c);
320     xfree (c->host_port);
321     xfree (c);
322 }
323
324 void ZOOM_resultset_addref (ZOOM_resultset r)
325 {
326     if (r)
327         (r->refcount)++;
328 }
329 ZOOM_resultset ZOOM_resultset_create ()
330 {
331     ZOOM_resultset r = xmalloc (sizeof(*r));
332
333     r->refcount = 1;
334     r->size = 0;
335     r->odr = odr_createmem (ODR_ENCODE);
336     r->start = 0;
337     r->piggyback = 1;
338     r->setname = 0;
339     r->count = 0;
340     r->record_cache = 0;
341     r->r_sort_spec = 0;
342     r->r_query = 0;
343     r->search = 0;
344     r->connection = 0;
345     r->next = 0;
346     return r;
347 }
348
349 ZOOM_resultset ZOOM_connection_search_pqf(ZOOM_connection c, const char *q)
350 {
351     ZOOM_resultset r;
352     ZOOM_query s = ZOOM_query_create();
353
354     ZOOM_query_prefix (s, q);
355
356     r = ZOOM_connection_search (c, s);
357     ZOOM_query_destroy (s);
358     return r;
359 }
360
361 ZOOM_resultset ZOOM_connection_search(ZOOM_connection c, ZOOM_query q)
362 {
363     ZOOM_resultset r = ZOOM_resultset_create ();
364     ZOOM_task task;
365     const char *cp;
366
367     r->r_sort_spec = q->sort_spec;
368     r->r_query = q->query;
369     r->search = q;
370
371     r->options = ZOOM_options_create_with_parent(c->options);
372
373     r->start = ZOOM_options_get_int(r->options, "start", 0);
374     r->count = ZOOM_options_get_int(r->options, "count", 0);
375     r->piggyback = ZOOM_options_get_bool (r->options, "piggyback", 1);
376     cp = ZOOM_options_get (r->options, "setname");
377     if (cp)
378         r->setname = xstrdup (cp);
379     
380     r->connection = c;
381
382     r->next = c->resultsets;
383     c->resultsets = r;
384
385     task = ZOOM_connection_add_task (c, ZOOM_TASK_SEARCH);
386     task->u.search.resultset = r;
387     ZOOM_resultset_addref (r);  
388
389     (q->refcount)++;
390
391     if (!c->async)
392     {
393         while (ZOOM_event (1, &c))
394             ;
395     }
396     return r;
397 }
398
399 void ZOOM_resultset_destroy(ZOOM_resultset r)
400 {
401     if (!r)
402         return;
403     (r->refcount)--;
404     yaz_log (LOG_DEBUG, "destroy r = %p count=%d", r, r->refcount);
405     if (r->refcount == 0)
406     {
407         if (r->connection)
408         {
409             /* remove ourselves from the resultsets in connection */
410             ZOOM_resultset *rp = &r->connection->resultsets;
411             while (1)
412             {
413                 assert (*rp);   /* we must be in this list!! */
414                 if (*rp == r)
415                 {   /* OK, we're here - take us out of it */
416                     *rp = (*rp)->next;
417                     break;
418                 }
419                 rp = &(*rp)->next;
420             }
421         }
422         ZOOM_query_destroy (r->search);
423         ZOOM_options_destroy (r->options);
424         odr_destroy (r->odr);
425         xfree (r->setname);
426         xfree (r);
427     }
428 }
429
430 size_t ZOOM_resultset_size (ZOOM_resultset r)
431 {
432     return r->size;
433 }
434
435 static void do_close (ZOOM_connection c)
436 {
437     if (c->cs)
438         cs_close(c->cs);
439     c->cs = 0;
440     c->mask = 0;
441     c->state = STATE_IDLE;
442 }
443
444 static void ZOOM_resultset_retrieve (ZOOM_resultset r,
445                                       int force_sync, int start, int count)
446 {
447     ZOOM_task task;
448     ZOOM_connection c;
449     const char *cp;
450
451     if (!r)
452         return;
453     c = r->connection;
454     if (!c)
455         return;
456     task = ZOOM_connection_add_task (c, ZOOM_TASK_RETRIEVE);
457     task->u.retrieve.resultset = r;
458     task->u.retrieve.start = start;
459     task->u.retrieve.count = count;
460
461     ZOOM_resultset_addref (r);
462
463     if (!r->connection->async || force_sync)
464         while (r->connection && ZOOM_event (1, &r->connection))
465             ;
466 }
467
468 void ZOOM_resultset_records (ZOOM_resultset r, ZOOM_record *recs,
469                               size_t start, size_t count)
470 {
471     int force_present = 0;
472
473     if (!r)
474         return ;
475     if (count && recs)
476         force_present = 1;
477     ZOOM_resultset_retrieve (r, force_present, start, count);
478     if (force_present)
479     {
480         size_t i;
481         for (i = 0; i< count; i++)
482             recs[i] = ZOOM_resultset_record_immediate (r, i+start);
483     }
484 }
485
486 static int do_connect (ZOOM_connection c)
487 {
488     void *add;
489     const char *effective_host;
490
491     if (c->proxy)
492         effective_host = c->proxy;
493     else
494         effective_host = c->host_port;
495
496     yaz_log (LOG_DEBUG, "do_connect host=%s", effective_host);
497
498     assert (!c->cs);
499     c->cs = cs_create_host (effective_host, 0, &add);
500
501     if (c->cs)
502     {
503         int ret = cs_connect (c->cs, add);
504         yaz_log (LOG_DEBUG, "cs_connect returned %d", ret);
505         if (ret >= 0)
506         {
507             c->state = STATE_CONNECTING; 
508             c->mask = ZOOM_SELECT_READ | ZOOM_SELECT_WRITE | 
509                 ZOOM_SELECT_EXCEPT;
510             return 1;
511         }
512     }
513     c->state = STATE_IDLE;
514     c->error = ZOOM_ERROR_CONNECT;
515     return 0;
516 }
517
518 int z3950_connection_socket(ZOOM_connection c)
519 {
520     if (c->cs)
521         return cs_fileno(c->cs);
522     return -1;
523 }
524
525 int z3950_connection_mask(ZOOM_connection c)
526 {
527     if (c->cs)
528         return c->mask;
529     return 0;
530 }
531
532 static int encode_APDU(ZOOM_connection c, Z_APDU *a, ODR out)
533 {
534     char str[120];
535
536     assert (a);
537     sprintf (str, "send_APDU t=%p type=%d", c, a->which);
538     if (c->cookie_out)
539     {
540         Z_OtherInformation **oi;
541         yaz_oi_APDU(a, &oi);
542         yaz_oi_set_string_oidval(oi, out, VAL_COOKIE, 1, c->cookie_out);
543     }
544     if (!z_APDU(out, &a, 0, 0))
545     {
546         FILE *outf = fopen("/tmp/apdu.txt", "w");
547         if (outf)
548         {
549             ODR odr_pr = odr_createmem(ODR_PRINT);
550             fprintf (outf, "a=%p\n", a);
551             odr_setprint(odr_pr, outf);
552             z_APDU(odr_pr, &a, 0, 0);
553             odr_destroy(odr_pr);
554             fclose (outf);
555         }
556         c->error = ZOOM_ERROR_ENCODE;
557         do_close (c);
558         return -1;
559     }
560     
561     return 0;
562 }
563
564 static int send_APDU (ZOOM_connection c, Z_APDU *a)
565 {
566     ZOOM_Event event;
567     assert (a);
568     if (encode_APDU(c, a, c->odr_out))
569         return -1;
570     c->buf_out = odr_getbuf(c->odr_out, &c->len_out, 0);
571     event = ZOOM_Event_create (ZOOM_EVENT_SEND_APDU);
572     ZOOM_connection_put_event (c, event);
573     odr_reset(c->odr_out);
574     do_write (c);
575     return 0;   
576 }
577
578 static int ZOOM_connection_send_init (ZOOM_connection c)
579 {
580     const char *impname;
581     Z_APDU *apdu = zget_APDU(c->odr_out, Z_APDU_initRequest);
582     Z_InitRequest *ireq = apdu->u.initRequest;
583     Z_IdAuthentication *auth = odr_malloc(c->odr_out, sizeof(*auth));
584     const char *auth_groupId = ZOOM_options_get (c->options, "group");
585     const char *auth_userId = ZOOM_options_get (c->options, "user");
586     const char *auth_password = ZOOM_options_get (c->options, "pass");
587     
588     ODR_MASK_SET(ireq->options, Z_Options_search);
589     ODR_MASK_SET(ireq->options, Z_Options_present);
590     ODR_MASK_SET(ireq->options, Z_Options_scan);
591     ODR_MASK_SET(ireq->options, Z_Options_sort);
592     ODR_MASK_SET(ireq->options, Z_Options_extendedServices);
593     ODR_MASK_SET(ireq->options, Z_Options_namedResultSets);
594     
595     ODR_MASK_SET(ireq->protocolVersion, Z_ProtocolVersion_1);
596     ODR_MASK_SET(ireq->protocolVersion, Z_ProtocolVersion_2);
597     ODR_MASK_SET(ireq->protocolVersion, Z_ProtocolVersion_3);
598     
599     impname = ZOOM_options_get (c->options, "implementationName");
600     ireq->implementationName =
601         odr_malloc (c->odr_out, 15 + (impname ? strlen(impname) : 0));
602     strcpy (ireq->implementationName, "");
603     if (impname)
604     {
605         strcat (ireq->implementationName, impname);
606         strcat (ireq->implementationName, "/");
607     }                                          
608     strcat (ireq->implementationName, "ZOOM-C/YAZ");
609     
610     *ireq->maximumRecordSize =
611         ZOOM_options_get_int (c->options, "maximumRecordSize", 1024*1024);
612     *ireq->preferredMessageSize =
613         ZOOM_options_get_int (c->options, "preferredMessageSize", 1024*1024);
614     
615     if (auth_groupId || auth_password)
616     {
617         Z_IdPass *pass = odr_malloc(c->odr_out, sizeof(*pass));
618         int i = 0;
619         pass->groupId = 0;
620         if (auth_groupId && *auth_groupId)
621         {
622             pass->groupId = odr_malloc(c->odr_out, strlen(auth_groupId)+1);
623             strcpy(pass->groupId, auth_groupId);
624             i++;
625         }
626         pass->userId = 0;
627         if (auth_userId && *auth_userId)
628         {
629             pass->userId = odr_malloc(c->odr_out, strlen(auth_userId)+1);
630             strcpy(pass->userId, auth_userId);
631             i++;
632         }
633         pass->password = 0;
634         if (auth_password && *auth_password)
635         {
636             pass->password = odr_malloc(c->odr_out, strlen(auth_password)+1);
637             strcpy(pass->password, auth_password);
638             i++;
639         }
640         if (i)
641         {
642             auth->which = Z_IdAuthentication_idPass;
643             auth->u.idPass = pass;
644             ireq->idAuthentication = auth;
645         }
646     }
647     else if (auth_userId)
648     {
649         auth->which = Z_IdAuthentication_open;
650         auth->u.open = odr_malloc(c->odr_out, strlen(auth_userId)+1);
651         strcpy(auth->u.open, auth_userId);
652         ireq->idAuthentication = auth;
653     }
654     if (c->proxy)
655         yaz_oi_set_string_oidval(&ireq->otherInfo, c->odr_out,
656                                  VAL_PROXY, 1, c->host_port);
657     assert (apdu);
658     send_APDU (c, apdu);
659     
660     return 0;
661 }
662
663 static int ZOOM_connection_send_search (ZOOM_connection c)
664 {
665     ZOOM_resultset r;
666     int lslb, ssub, mspn;
667     const char *syntax;
668     Z_APDU *apdu = zget_APDU(c->odr_out, Z_APDU_searchRequest);
669     Z_SearchRequest *search_req = apdu->u.searchRequest;
670     const char *elementSetName;
671     const char *smallSetElementSetName;
672     const char *mediumSetElementSetName;
673     const char *schema;
674
675     assert (c->tasks);
676     assert (c->tasks->which == ZOOM_TASK_SEARCH);
677
678     r = c->tasks->u.search.resultset;
679
680     elementSetName =
681         ZOOM_options_get (r->options, "elementSetName");
682     smallSetElementSetName  =
683         ZOOM_options_get (r->options, "smallSetElementSetName");
684     mediumSetElementSetName =
685         ZOOM_options_get (r->options, "mediumSetElementSetName");
686     schema =
687         ZOOM_options_get (r->options, "schema");
688
689     if (!smallSetElementSetName)
690         smallSetElementSetName = elementSetName;
691
692     if (!mediumSetElementSetName)
693         mediumSetElementSetName = elementSetName;
694
695     assert (r);
696     assert (r->r_query);
697
698     /* prepare query for the search request */
699     search_req->query = r->r_query;
700
701     search_req->databaseNames =
702         set_DatabaseNames (c, r->options, &search_req->num_databaseNames);
703
704     /* get syntax (no need to provide unless piggyback is in effect) */
705     syntax = ZOOM_options_get (r->options, "preferredRecordSyntax");
706
707     lslb = ZOOM_options_get_int (r->options, "largeSetLowerBound", -1);
708     ssub = ZOOM_options_get_int (r->options, "smallSetUpperBound", -1);
709     mspn = ZOOM_options_get_int (r->options, "mediumSetPresentNumber", -1);
710     if (lslb != -1 && ssub != -1 && mspn != -1)
711     {
712         /* So're a Z39.50 expert? Let's hope you don't do sort */
713         *search_req->largeSetLowerBound = lslb;
714         *search_req->smallSetUpperBound = ssub;
715         *search_req->mediumSetPresentNumber = mspn;
716     }
717     else if (r->start == 0 && r->count > 0
718              && r->piggyback && !r->r_sort_spec && !schema)
719     {
720         /* Regular piggyback - do it unless we're going to do sort */
721         *search_req->largeSetLowerBound = 2000000000;
722         *search_req->smallSetUpperBound = r->count;
723         *search_req->mediumSetPresentNumber = r->count;
724         smallSetElementSetName = 0;  /* no need to provide this */
725     }
726     else
727     {
728         /* non-piggyback. Need not provide elementsets or syntaxes .. */
729         smallSetElementSetName = 0;
730         mediumSetElementSetName = 0;
731         syntax = 0;
732     }
733     if (smallSetElementSetName && *smallSetElementSetName)
734     {
735         Z_ElementSetNames *esn = odr_malloc (c->odr_out, sizeof(*esn));
736         
737         esn->which = Z_ElementSetNames_generic;
738         esn->u.generic = odr_strdup (c->odr_out, smallSetElementSetName);
739         search_req->smallSetElementSetNames = esn;
740     }
741     if (mediumSetElementSetName && *mediumSetElementSetName)
742     {
743         Z_ElementSetNames *esn = odr_malloc (c->odr_out, sizeof(*esn));
744         
745         esn->which = Z_ElementSetNames_generic;
746         esn->u.generic = odr_strdup (c->odr_out, mediumSetElementSetName);
747         search_req->mediumSetElementSetNames = esn;
748     }
749     if (syntax)
750         search_req->preferredRecordSyntax =
751             yaz_str_to_z3950oid (c->odr_out, CLASS_RECSYN, syntax);
752     
753     if (!r->setname)
754     {
755         if (c->support_named_resultsets)
756         {
757             char setname[14];
758             int ord;
759             /* find the lowest unused ordinal so that we re-use
760                result sets on the server. */
761             for (ord = 1; ; ord++)
762             {
763                 ZOOM_resultset rp;
764                 sprintf (setname, "%d", ord);
765                 for (rp = c->resultsets; rp; rp = rp->next)
766                     if (rp->setname && !strcmp (rp->setname, setname))
767                         break;
768                 if (!rp)
769                     break;
770             }
771             r->setname = xstrdup (setname);
772             yaz_log (LOG_DEBUG, "allocating %s", r->setname);
773         }
774         else
775             r->setname = xstrdup ("default");
776         ZOOM_options_set (r->options, "setname", r->setname);
777     }
778     search_req->resultSetName = odr_strdup(c->odr_out, r->setname);
779     /* send search request */
780     send_APDU (c, apdu);
781     r->r_query = 0;
782     return 1;
783 }
784
785 static void response_diag (ZOOM_connection c, Z_DiagRec *p)
786 {
787     Z_DefaultDiagFormat *r;
788     char *addinfo = 0;
789     
790     xfree (c->addinfo);
791     c->addinfo = 0;
792     if (p->which != Z_DiagRec_defaultFormat)
793     {
794         c->error = ZOOM_ERROR_DECODE;
795         return;
796     }
797     r = p->u.defaultFormat;
798     switch (r->which)
799     {
800     case Z_DefaultDiagFormat_v2Addinfo:
801         addinfo = r->u.v2Addinfo;
802         break;
803     case Z_DefaultDiagFormat_v3Addinfo:
804         addinfo = r->u.v3Addinfo;
805         break;
806     }
807     if (addinfo)
808         c->addinfo = xstrdup (addinfo);
809     c->error = *r->condition;
810 }
811
812 ZOOM_record ZOOM_record_clone (ZOOM_record srec)
813 {
814     char *buf;
815     int size;
816     ODR odr_enc;
817     ZOOM_record nrec;
818
819     odr_enc = odr_createmem(ODR_ENCODE);
820     if (!z_NamePlusRecord (odr_enc, &srec->npr, 0, 0))
821         return 0;
822     buf = odr_getbuf (odr_enc, &size, 0);
823     
824     nrec = xmalloc (sizeof(*nrec));
825     nrec->odr = odr_createmem(ODR_DECODE);
826     nrec->wrbuf_marc = 0;
827     odr_setbuf (nrec->odr, buf, size, 0);
828     z_NamePlusRecord (nrec->odr, &nrec->npr, 0, 0);
829     
830     odr_destroy (odr_enc);
831     return nrec;
832 }
833
834 ZOOM_record ZOOM_resultset_record_immediate (ZOOM_resultset s,size_t pos)
835 {
836     return record_cache_lookup (s, pos, 0);
837 }
838
839 ZOOM_record ZOOM_resultset_record (ZOOM_resultset r, size_t pos)
840 {
841     ZOOM_resultset_retrieve (r, 1, pos, 1);
842     return ZOOM_resultset_record_immediate (r, pos);
843 }
844
845 void ZOOM_record_destroy (ZOOM_record rec)
846 {
847     if (!rec)
848         return;
849     if (rec->wrbuf_marc)
850         wrbuf_free (rec->wrbuf_marc, 1);
851     odr_destroy (rec->odr);
852     xfree (rec);
853 }
854
855 void *ZOOM_record_get (ZOOM_record rec, const char *type, size_t *len)
856 {
857     Z_NamePlusRecord *npr;
858     if (!rec)
859         return 0;
860     npr = rec->npr;
861     if (!npr)
862         return 0;
863     if (!strcmp (type, "database"))
864     {
865         return npr->databaseName;
866     }
867     else if (!strcmp (type, "syntax"))
868     {
869         if (npr->which == Z_NamePlusRecord_databaseRecord)
870         {
871             Z_External *r = (Z_External *) npr->u.databaseRecord;
872             oident *ent = oid_getentbyoid(r->direct_reference);
873             if (ent)
874                 return ent->desc;
875         }
876         return "none";
877     }
878     else if (!strcmp (type, "render"))
879     {
880         if (npr->which == Z_NamePlusRecord_databaseRecord)
881         {
882             Z_External *r = (Z_External *) npr->u.databaseRecord;
883             oident *ent = oid_getentbyoid(r->direct_reference);
884             
885             if (r->which == Z_External_sutrs)
886             {
887                 *len = r->u.sutrs->len;
888                 return r->u.sutrs->buf;
889             }
890             else if (r->which == Z_External_octet)
891             {
892                 switch (ent->value)
893                 {
894                 case VAL_SOIF:
895                 case VAL_HTML:
896                 case VAL_SUTRS:
897                     break;
898                 case VAL_TEXT_XML:
899                 case VAL_APPLICATION_XML:
900                     break;
901                 default:
902                     if (!rec->wrbuf_marc)
903                         rec->wrbuf_marc = wrbuf_alloc();
904                     if (marc_display_wrbuf (r->u.octet_aligned->buf,
905                                         rec->wrbuf_marc, 0,
906                                             r->u.octet_aligned->len) > 0)
907                     {
908                         *len = wrbuf_len(rec->wrbuf_marc);
909                         return wrbuf_buf(rec->wrbuf_marc);
910                     }
911                 }
912                 *len = r->u.octet_aligned->len;
913                 return r->u.octet_aligned->buf;
914             }
915             else if (r->which == Z_External_grs1)
916             {
917                 *len = 5;
918                 return "GRS-1";
919             }
920         }
921         return 0;
922     }
923     else if (!strcmp (type, "raw"))
924     {
925         if (npr->which == Z_NamePlusRecord_databaseRecord)
926         {
927             *len = -1;
928             return (Z_External *) npr->u.databaseRecord;
929         }
930         return 0;
931     }
932     return 0;
933 }
934
935 static void record_cache_add (ZOOM_resultset r,
936                               Z_NamePlusRecord *npr,
937                               int pos,
938                               const char *elementSetName)
939 {
940     ZOOM_record_cache rc;
941
942     for (rc = r->record_cache; rc; rc = rc->next)
943     {
944         if (pos == rc->pos)
945         {
946             if ((!elementSetName && !rc->elementSetName)
947                 || (elementSetName && rc->elementSetName &&
948                     !strcmp (elementSetName, rc->elementSetName)))
949             {
950                 /* not destroying rc->npr (it's handled by nmem )*/
951                 rc->rec.npr = npr;
952                 /* keeping wrbuf_marc too */
953                 return;
954             }
955         }
956
957     }
958     rc = odr_malloc (r->odr, sizeof(*rc));
959     rc->rec.npr = npr; 
960     rc->rec.odr = 0;
961     rc->rec.wrbuf_marc = 0;
962     if (elementSetName)
963         rc->elementSetName = odr_strdup (r->odr, elementSetName);
964     else
965         rc->elementSetName = 0;
966     rc->pos = pos;
967     rc->next = r->record_cache;
968     r->record_cache = rc;
969 }
970
971 static ZOOM_record record_cache_lookup (ZOOM_resultset r,
972                                          int pos,
973                                          const char *elementSetName)
974 {
975     ZOOM_record_cache rc;
976
977     for (rc = r->record_cache; rc; rc = rc->next)
978     {
979         if (pos == rc->pos)
980         {
981             if ((!elementSetName && !rc->elementSetName)
982                 || (elementSetName && rc->elementSetName &&
983                     !strcmp (elementSetName, rc->elementSetName)))
984                 return &rc->rec;
985         }
986     }
987     return 0;
988 }
989                                              
990 static void handle_records (ZOOM_connection c, Z_Records *sr,
991                             int present_phase)
992 {
993     ZOOM_resultset resultset;
994
995     if (!c->tasks)
996         return ;
997     switch (c->tasks->which)
998     {
999     case ZOOM_TASK_SEARCH:
1000         resultset = c->tasks->u.search.resultset;
1001         break;
1002     case ZOOM_TASK_RETRIEVE:
1003         resultset = c->tasks->u.retrieve.resultset;        
1004         break;
1005     default:
1006         return;
1007     }
1008     if (sr && sr->which == Z_Records_NSD)
1009     {
1010         Z_DiagRec dr, *dr_p = &dr;
1011         dr.which = Z_DiagRec_defaultFormat;
1012         dr.u.defaultFormat = sr->u.nonSurrogateDiagnostic;
1013         
1014         response_diag (c, dr_p);
1015     }
1016     else if (sr && sr->which == Z_Records_multipleNSD)
1017     {
1018         if (sr->u.multipleNonSurDiagnostics->num_diagRecs >= 1)
1019             response_diag(c, sr->u.multipleNonSurDiagnostics->diagRecs[0]);
1020         else
1021             c->error = ZOOM_ERROR_DECODE;
1022     }
1023     else 
1024     {
1025         if (resultset->count + resultset->start > resultset->size)
1026             resultset->count = resultset->size - resultset->start;
1027         if (resultset->count < 0)
1028             resultset->count = 0;
1029         if (sr && sr->which == Z_Records_DBOSD)
1030         {
1031             int i;
1032             NMEM nmem = odr_extract_mem (c->odr_in);
1033             Z_NamePlusRecordList *p =
1034                 sr->u.databaseOrSurDiagnostics;
1035             for (i = 0; i<p->num_records; i++)
1036             {
1037                 record_cache_add (resultset, p->records[i],
1038                                   i+ resultset->start, 0);
1039             }
1040             /* transfer our response to search_nmem .. we need it later */
1041             nmem_transfer (resultset->odr->mem, nmem);
1042             nmem_destroy (nmem);
1043             if (present_phase && p->num_records == 0)
1044             {
1045                 /* present response and we didn't get any records! */
1046                 c->error = ZOOM_ERROR_DECODE;
1047             }
1048         }
1049         else if (present_phase)
1050         {
1051             /* present response and we didn't get any records! */
1052             c->error = ZOOM_ERROR_DECODE;
1053         }
1054     }
1055 }
1056
1057 static void handle_present_response (ZOOM_connection c, Z_PresentResponse *pr)
1058 {
1059     handle_records (c, pr->records, 1);
1060 }
1061
1062 static void handle_search_response (ZOOM_connection c, Z_SearchResponse *sr)
1063 {
1064     ZOOM_resultset resultset;
1065
1066     yaz_log (LOG_DEBUG, "got search response");
1067
1068     if (!c->tasks || c->tasks->which != ZOOM_TASK_SEARCH)
1069         return ;
1070
1071     resultset = c->tasks->u.search.resultset;
1072
1073     resultset->size = *sr->resultCount;
1074     handle_records (c, sr->records, 0);
1075 }
1076
1077 static void sort_response (ZOOM_connection c, Z_SortResponse *res)
1078 {
1079     if (res->diagnostics && res->num_diagnostics > 0)
1080         response_diag (c, res->diagnostics[0]);
1081 }
1082
1083 static int scan_response (ZOOM_connection c, Z_ScanResponse *res)
1084 {
1085     NMEM nmem = odr_extract_mem (c->odr_in);
1086     ZOOM_scanset scan;
1087
1088     if (!c->tasks || c->tasks->which != ZOOM_TASK_SCAN)
1089         return 0;
1090     scan = c->tasks->u.scan.scan;
1091
1092     if (res->entries && res->entries->nonsurrogateDiagnostics)
1093         response_diag(c, res->entries->nonsurrogateDiagnostics[0]);
1094     scan->scan_response = res;
1095     nmem_transfer (scan->odr->mem, nmem);
1096     nmem_destroy (nmem);
1097 }
1098
1099 static int send_sort (ZOOM_connection c)
1100 {
1101     ZOOM_resultset  resultset;
1102
1103     if (!c->tasks || c->tasks->which != ZOOM_TASK_SEARCH)
1104         return 0;
1105
1106     resultset = c->tasks->u.search.resultset;
1107
1108     if (c->error)
1109     {
1110         resultset->r_sort_spec = 0;
1111         return 0;
1112     }
1113     if (resultset->r_sort_spec)
1114     {
1115         Z_APDU *apdu = zget_APDU(c->odr_out, Z_APDU_sortRequest);
1116         Z_SortRequest *req = apdu->u.sortRequest;
1117         
1118         req->num_inputResultSetNames = 1;
1119         req->inputResultSetNames = (Z_InternationalString **)
1120             odr_malloc (c->odr_out, sizeof(*req->inputResultSetNames));
1121         req->inputResultSetNames[0] =
1122             odr_strdup (c->odr_out, resultset->setname);
1123         req->sortedResultSetName = odr_strdup (c->odr_out, resultset->setname);
1124         req->sortSequence = resultset->r_sort_spec;
1125         resultset->r_sort_spec = 0;
1126         send_APDU (c, apdu);
1127         return 1;
1128     }
1129     return 0;
1130 }
1131
1132 static int send_present (ZOOM_connection c)
1133 {
1134     Z_APDU *apdu = zget_APDU(c->odr_out, Z_APDU_presentRequest);
1135     Z_PresentRequest *req = apdu->u.presentRequest;
1136     int i = 0;
1137     const char *syntax = 
1138         ZOOM_options_get (c->options, "preferredRecordSyntax");
1139     const char *element =
1140         ZOOM_options_get (c->options, "elementSetName");
1141     const char *schema =
1142         ZOOM_options_get (c->options, "schema");
1143     ZOOM_resultset  resultset;
1144
1145     if (!c->tasks)
1146         return 0;
1147
1148     switch (c->tasks->which)
1149     {
1150     case ZOOM_TASK_SEARCH:
1151         resultset = c->tasks->u.search.resultset;
1152         break;
1153     case ZOOM_TASK_RETRIEVE:
1154         resultset = c->tasks->u.retrieve.resultset;
1155         resultset->start = c->tasks->u.retrieve.start;
1156         resultset->count = c->tasks->u.retrieve.count;
1157
1158         if (resultset->start >= resultset->size)
1159             return 0;
1160         if (resultset->start + resultset->count > resultset->size)
1161             resultset->count = resultset->size - resultset->start;
1162         break;
1163     default:
1164         return 0;
1165     }
1166
1167     if (c->error)                  /* don't continue on error */
1168         return 0;
1169     if (resultset->start < 0)
1170         return 0;
1171     for (i = 0; i<resultset->count; i++)
1172     {
1173         ZOOM_record rec =
1174             record_cache_lookup (resultset, i + resultset->start, 0);
1175         if (!rec)
1176             break;
1177     }
1178     if (i == resultset->count)
1179         return 0;
1180
1181     resultset->start += i;
1182     resultset->count -= i;
1183     *req->resultSetStartPoint = resultset->start + 1;
1184     *req->numberOfRecordsRequested = resultset->count;
1185     assert (*req->numberOfRecordsRequested > 0);
1186
1187     if (syntax && *syntax)
1188         req->preferredRecordSyntax =
1189             yaz_str_to_z3950oid (c->odr_out, CLASS_RECSYN, syntax);
1190
1191     if (schema && *schema)
1192     {
1193         Z_RecordComposition *compo = odr_malloc (c->odr_out, sizeof(*compo));
1194
1195         req->recordComposition = compo;
1196         compo->which = Z_RecordComp_complex;
1197         compo->u.complex = (Z_CompSpec *)
1198             odr_malloc(c->odr_out, sizeof(*compo->u.complex));
1199         compo->u.complex->selectAlternativeSyntax = (bool_t *) 
1200             odr_malloc(c->odr_out, sizeof(bool_t));
1201         *compo->u.complex->selectAlternativeSyntax = 0;
1202
1203         compo->u.complex->generic = (Z_Specification *)
1204             odr_malloc(c->odr_out, sizeof(*compo->u.complex->generic));
1205
1206         compo->u.complex->generic->schema = (Odr_oid *)
1207             yaz_str_to_z3950oid (c->odr_out, CLASS_SCHEMA, schema);
1208
1209         if (!compo->u.complex->generic->schema)
1210         {
1211             /* OID wasn't a schema! Try record syntax instead. */
1212
1213             compo->u.complex->generic->schema = (Odr_oid *)
1214                 yaz_str_to_z3950oid (c->odr_out, CLASS_RECSYN, schema);
1215         }
1216         if (element && *element)
1217         {
1218             compo->u.complex->generic->elementSpec = (Z_ElementSpec *)
1219                 odr_malloc(c->odr_out, sizeof(Z_ElementSpec));
1220             compo->u.complex->generic->elementSpec->which =
1221                 Z_ElementSpec_elementSetName;
1222             compo->u.complex->generic->elementSpec->u.elementSetName =
1223                 odr_strdup (c->odr_out, element);
1224         }
1225         else
1226             compo->u.complex->generic->elementSpec = 0;
1227         compo->u.complex->num_dbSpecific = 0;
1228         compo->u.complex->dbSpecific = 0;
1229         compo->u.complex->num_recordSyntax = 0;
1230         compo->u.complex->recordSyntax = 0;
1231     }
1232     else if (element && *element)
1233     {
1234         Z_ElementSetNames *esn = odr_malloc (c->odr_out, sizeof(*esn));
1235         Z_RecordComposition *compo = odr_malloc (c->odr_out, sizeof(*compo));
1236         
1237         esn->which = Z_ElementSetNames_generic;
1238         esn->u.generic = odr_strdup (c->odr_out, element);
1239         compo->which = Z_RecordComp_simple;
1240         compo->u.simple = esn;
1241         req->recordComposition = compo;
1242     }
1243     req->resultSetId = odr_strdup(c->odr_out, resultset->setname);
1244     send_APDU (c, apdu);
1245     return 1;
1246 }
1247
1248 ZOOM_scanset ZOOM_connection_scan (ZOOM_connection c, const char *start)
1249 {
1250     ZOOM_scanset scan = xmalloc (sizeof(*scan));
1251
1252     scan->connection = c;
1253     scan->odr = odr_createmem (ODR_DECODE);
1254     scan->options = ZOOM_options_create_with_parent (c->options);
1255     scan->refcount = 1;
1256     scan->scan_response = 0;
1257
1258     if ((scan->termListAndStartPoint =
1259          p_query_scan(scan->odr, PROTO_Z3950, &scan->attributeSet,
1260                       start)))
1261     {
1262         ZOOM_task task = ZOOM_connection_add_task (c, ZOOM_TASK_SCAN);
1263         task->u.scan.scan = scan;
1264         
1265         (scan->refcount)++;
1266         if (!c->async)
1267         {
1268             while (ZOOM_event (1, &c))
1269                 ;
1270         }
1271     }
1272     return scan;
1273 }
1274
1275 void ZOOM_scanset_destroy (ZOOM_scanset scan)
1276 {
1277     if (!scan)
1278         return;
1279     (scan->refcount)--;
1280     if (scan->refcount == 0)
1281     {
1282         odr_destroy (scan->odr);
1283         
1284         ZOOM_options_destroy (scan->options);
1285         xfree (scan);
1286     }
1287 }
1288
1289 int send_scan (ZOOM_connection c)
1290 {
1291     ZOOM_scanset scan;
1292     Z_APDU *apdu = zget_APDU(c->odr_out, Z_APDU_scanRequest);
1293     Z_ScanRequest *req = apdu->u.scanRequest;
1294     if (!c->tasks)
1295         return 0;
1296     assert (c->tasks->which == ZOOM_TASK_SCAN);
1297     scan = c->tasks->u.scan.scan;
1298
1299     req->termListAndStartPoint = scan->termListAndStartPoint;
1300     req->attributeSet = scan->attributeSet;
1301
1302     *req->numberOfTermsRequested =
1303         ZOOM_options_get_int(scan->options, "number", 10);
1304
1305     req->preferredPositionInResponse =
1306         odr_intdup (c->odr_out,
1307                     ZOOM_options_get_int(scan->options, "position", 1));
1308
1309     req->stepSize =
1310         odr_intdup (c->odr_out,
1311                     ZOOM_options_get_int(scan->options, "stepSize", 0));
1312     
1313     req->databaseNames = set_DatabaseNames (c, scan->options, 
1314                                             &req->num_databaseNames);
1315
1316     send_APDU (c, apdu);
1317
1318     return 1;
1319 }
1320
1321 size_t ZOOM_scanset_size (ZOOM_scanset scan)
1322 {
1323     if (!scan || !scan->scan_response || !scan->scan_response->entries)
1324         return 0;
1325     return scan->scan_response->entries->num_entries;
1326 }
1327
1328 const char *ZOOM_scanset_term (ZOOM_scanset scan, size_t i, int *occ, size_t *len)
1329 {
1330     const char *term = 0;
1331     size_t noent = ZOOM_scanset_size (scan);
1332     Z_ScanResponse *res = scan->scan_response;
1333     
1334     *len = 0;
1335     *occ = 0;
1336     if (i >= noent)
1337         return 0;
1338     if (res->entries->entries[i]->which == Z_Entry_termInfo)
1339     {
1340         Z_TermInfo *t = res->entries->entries[i]->u.termInfo;
1341         
1342         if (t->term->which == Z_Term_general)
1343         {
1344             term = t->term->u.general->buf;
1345             *len = t->term->u.general->len;
1346         }
1347         *occ = t->globalOccurrences ? *t->globalOccurrences : 0;
1348     }
1349     return term;
1350 }
1351
1352 static int ZOOM_connection_exec_task (ZOOM_connection c)
1353 {
1354     ZOOM_task task = c->tasks;
1355
1356     yaz_log (LOG_DEBUG, "ZOOM_connection_exec_task");
1357     if (!task)
1358         return 0;
1359     if (c->error != ZOOM_ERROR_NONE ||
1360         (!c->cs && task->which != ZOOM_TASK_CONNECT))
1361     {
1362         ZOOM_connection_remove_tasks (c);
1363         return 0;
1364     }
1365     yaz_log (LOG_DEBUG, "ZOOM_connection_exec_task type=%d", task->which);
1366     if (task->running)
1367         return 0;
1368     task->running = 1;
1369     switch (task->which)
1370     {
1371     case ZOOM_TASK_SEARCH:
1372         /* see if search hasn't been sent yet. */
1373         if (ZOOM_connection_send_search (c))
1374             return 1;
1375         break;
1376     case ZOOM_TASK_RETRIEVE:
1377         if (send_present (c))
1378             return 1;
1379         break;
1380     case ZOOM_TASK_CONNECT:
1381         if (do_connect(c))
1382             return 1;
1383         break;
1384     case ZOOM_TASK_SCAN:
1385         if (send_scan(c))
1386             return 1;
1387     }
1388     ZOOM_connection_remove_task (c);
1389     return 0;
1390 }
1391
1392 static int send_sort_present (ZOOM_connection c)
1393 {
1394     int r = send_sort (c);
1395     if (!r)
1396         r = send_present (c);
1397     return r;
1398 }
1399
1400 static void handle_apdu (ZOOM_connection c, Z_APDU *apdu)
1401 {
1402     Z_InitResponse *initrs;
1403     
1404     yaz_log (LOG_DEBUG, "hande_apdu type=%d", apdu->which);
1405     c->mask = 0;
1406     switch(apdu->which)
1407     {
1408     case Z_APDU_initResponse:
1409         initrs = apdu->u.initResponse;
1410         if (!*initrs->result)
1411         {
1412             c->error = ZOOM_ERROR_INIT;
1413         }
1414         else
1415         {
1416             char *cookie =
1417                 yaz_oi_get_string_oidval (&apdu->u.initResponse->otherInfo,
1418                                           VAL_COOKIE, 1, 0);
1419             xfree (c->cookie_in);
1420             c->cookie_in = 0;
1421             if (cookie)
1422                 c->cookie_in = xstrdup(cookie);
1423             if (ODR_MASK_GET(initrs->options, Z_Options_namedResultSets) &&
1424                 ODR_MASK_GET(initrs->protocolVersion, Z_ProtocolVersion_3))
1425                 c->support_named_resultsets = 1;
1426             if (c->tasks)
1427             {
1428                 assert (c->tasks->which == ZOOM_TASK_CONNECT);
1429                 ZOOM_connection_remove_task (c);
1430             }
1431             ZOOM_connection_exec_task (c);
1432         }
1433         break;
1434     case Z_APDU_searchResponse:
1435         handle_search_response (c, apdu->u.searchResponse);
1436         if (!send_sort_present (c))
1437             ZOOM_connection_remove_task (c);
1438         break;
1439     case Z_APDU_presentResponse:
1440         handle_present_response (c, apdu->u.presentResponse);
1441         if (!send_present (c))
1442             ZOOM_connection_remove_task (c);
1443         break;
1444     case Z_APDU_sortResponse:
1445         sort_response (c, apdu->u.sortResponse);
1446         if (!send_present (c))
1447             ZOOM_connection_remove_task (c);
1448         break;
1449     case Z_APDU_scanResponse:
1450         scan_response (c, apdu->u.scanResponse);
1451         ZOOM_connection_remove_task (c);
1452     }
1453 }
1454
1455 static int do_read (ZOOM_connection c)
1456 {
1457     int r;
1458     Z_APDU *apdu;
1459     ZOOM_Event event;
1460     
1461     event = ZOOM_Event_create (ZOOM_EVENT_RECV_DATA);
1462     ZOOM_connection_put_event (c, event);
1463     
1464     r = cs_get (c->cs, &c->buf_in, &c->len_in);
1465     if (r == 1)
1466         return 0;
1467     if (r <= 0)
1468     {
1469         c->error= ZOOM_ERROR_CONNECTION_LOST;
1470         do_close (c);
1471     }
1472     else
1473     {
1474         ZOOM_Event event;
1475         odr_reset (c->odr_in);
1476         odr_setbuf (c->odr_in, c->buf_in, r, 0);
1477         event = ZOOM_Event_create (ZOOM_EVENT_RECV_APDU);
1478         ZOOM_connection_put_event (c, event);
1479         if (!z_APDU (c->odr_in, &apdu, 0, 0))
1480         {
1481             c->error = ZOOM_ERROR_DECODE;
1482             do_close (c);
1483         }
1484         else
1485         {
1486             handle_apdu (c, apdu);
1487         }
1488     }
1489     return 1;
1490 }
1491
1492 static int do_write_ex (ZOOM_connection c, char *buf_out, int len_out)
1493 {
1494     int r;
1495     ZOOM_Event event;
1496     
1497     event = ZOOM_Event_create(ZOOM_EVENT_SEND_DATA);
1498     ZOOM_connection_put_event (c, event);
1499
1500     if ((r=cs_put (c->cs, buf_out, len_out)) < 0)
1501     {
1502         if (c->state == STATE_CONNECTING)
1503             c->error = ZOOM_ERROR_CONNECT;
1504         else
1505             c->error = ZOOM_ERROR_CONNECTION_LOST;
1506         do_close (c);
1507         return 1;
1508     }
1509     else if (r == 1)
1510     {
1511         c->state = STATE_ESTABLISHED;
1512         c->mask = ZOOM_SELECT_READ|ZOOM_SELECT_WRITE|ZOOM_SELECT_EXCEPT;
1513     }
1514     else
1515     {
1516         c->state = STATE_ESTABLISHED;
1517         c->mask = ZOOM_SELECT_READ|ZOOM_SELECT_EXCEPT;
1518     }
1519     return 0;
1520 }
1521
1522 static int do_write(ZOOM_connection c)
1523 {
1524     return do_write_ex (c, c->buf_out, c->len_out);
1525 }
1526
1527
1528 const char *ZOOM_connection_option_get (ZOOM_connection c, const char *key)
1529 {
1530     return ZOOM_options_get (c->options, key);
1531 }
1532
1533 void ZOOM_connection_option_set (ZOOM_connection c, const char *key,
1534                                   const char *val)
1535 {
1536     ZOOM_options_set (c->options, key, val);
1537 }
1538
1539 const char *ZOOM_resultset_option_get (ZOOM_resultset r, const char *key)
1540 {
1541     return ZOOM_options_get (r->options, key);
1542 }
1543
1544 void ZOOM_resultset_option_set (ZOOM_resultset r, const char *key,
1545                                   const char *val)
1546 {
1547     ZOOM_options_set (r->options, key, val);
1548 }
1549
1550
1551 int ZOOM_connection_errcode (ZOOM_connection c)
1552 {
1553     return ZOOM_connection_error (c, 0, 0);
1554 }
1555
1556 const char *ZOOM_connection_errmsg (ZOOM_connection c)
1557 {
1558     const char *msg;
1559     ZOOM_connection_error (c, &msg, 0);
1560     return msg;
1561 }
1562
1563 const char *ZOOM_connection_addinfo (ZOOM_connection c)
1564 {
1565     const char *addinfo;
1566     ZOOM_connection_error (c, 0, &addinfo);
1567     return addinfo;
1568 }
1569
1570 int ZOOM_connection_error (ZOOM_connection c, const char **cp,
1571                             const char **addinfo)
1572 {
1573     int error = c->error;
1574     if (cp)
1575     {
1576         switch (error)
1577         {
1578         case ZOOM_ERROR_NONE:
1579             *cp = "No error"; break;
1580         case ZOOM_ERROR_CONNECT:
1581             *cp = "Connect failed"; break;
1582         case ZOOM_ERROR_MEMORY:
1583             *cp = "Out of memory"; break;
1584         case ZOOM_ERROR_ENCODE:
1585             *cp = "Encoding failed"; break;
1586         case ZOOM_ERROR_DECODE:
1587             *cp = "Decoding failed"; break;
1588         case ZOOM_ERROR_CONNECTION_LOST:
1589             *cp = "Connection lost"; break;
1590         case ZOOM_ERROR_INIT:
1591             *cp = "Init rejected"; break;
1592         case ZOOM_ERROR_INTERNAL:
1593             *cp = "Internal failure"; break;
1594         case ZOOM_ERROR_TIMEOUT:
1595             *cp = "Timeout"; break;
1596         default:
1597             *cp = diagbib1_str (error);
1598         }
1599     }
1600     if (addinfo)
1601     {
1602         if (c->addinfo)
1603             *addinfo = c->addinfo;
1604         else
1605             *addinfo = "";
1606     }
1607     return c->error;
1608 }
1609
1610 int ZOOM_connection_do_io(ZOOM_connection c, int mask)
1611 {
1612     ZOOM_Event event = 0;
1613 #if 0
1614     int r = cs_look(c->cs);
1615     yaz_log (LOG_LOG, "ZOOM_connection_do_io c=%p mask=%d cs_look=%d",
1616              c, mask, r);
1617     
1618     if (r == CS_NONE)
1619     {
1620         event = ZOOM_Event_create (ZOOM_EVENT_IO_CONNECT);
1621         c->error = ZOOM_ERROR_CONNECT;
1622         do_close (c);
1623         ZOOM_connection_put_event (c, event);
1624     }
1625     else if (r == CS_CONNECT)
1626     {
1627         event = ZOOM_Event_create (ZOOM_EVENT_IO_CONNECT);
1628         yaz_log (LOG_LOG, "calling rcvconnect");
1629         if (cs_rcvconnect (c->cs) < 0)
1630         {
1631             c->error = ZOOM_ERROR_CONNECT;
1632             do_close (c);
1633             ZOOM_connection_put_event (c, event);
1634         }
1635         else
1636         {
1637             ZOOM_connection_put_event (c, event);
1638             ZOOM_connection_send_init (c);
1639         }
1640     }
1641     else
1642     {
1643         if (mask & ZOOM_SELECT_READ)
1644             do_read (c);
1645         if (c->cs && (mask & ZOOM_SELECT_WRITE))
1646             do_write (c);
1647     }   
1648 #else
1649     yaz_log (LOG_DEBUG, "ZOOM_connection_do_io c=%p mask=%d", c, mask);
1650     if (c->state == STATE_CONNECTING)
1651     {
1652         event = ZOOM_Event_create (ZOOM_EVENT_CONNECT);
1653         if (mask & ZOOM_SELECT_WRITE)
1654         {
1655             ZOOM_connection_put_event (c, event);
1656             ZOOM_connection_send_init (c);
1657         }
1658         else
1659         {
1660             c->error = ZOOM_ERROR_CONNECT;
1661             do_close (c);
1662             ZOOM_connection_put_event (c, event);
1663         }
1664     }
1665     else if (c->state == STATE_ESTABLISHED)
1666     {
1667         if (mask & ZOOM_SELECT_READ)
1668             do_read (c);
1669         if (c->cs && (mask & ZOOM_SELECT_WRITE))
1670             do_write (c);
1671     }
1672     else
1673     {
1674         event = ZOOM_Event_create (ZOOM_EVENT_UNKNOWN);
1675         ZOOM_connection_put_event (c, event);
1676         c->error = ZOOM_ERROR_INTERNAL;
1677         do_close (c);
1678     }
1679 #endif
1680     return 1;
1681 }
1682
1683 int ZOOM_connection_last_event(ZOOM_connection cs)
1684 {
1685     if (!cs)
1686         return ZOOM_EVENT_NONE;
1687     return cs->last_event;
1688 }
1689
1690 int ZOOM_event (int no, ZOOM_connection *cs)
1691 {
1692 #if HAVE_SYS_POLL_H
1693     struct pollfd pollfds[1024];
1694     ZOOM_connection poll_cs[1024];
1695 #else
1696     struct timeval tv;
1697     fd_set input, output, except;
1698 #endif
1699     int i, r, nfds;
1700     int max_fd = 0;
1701
1702     for (i = 0; i<no; i++)
1703     {
1704         ZOOM_connection c = cs[i];
1705         ZOOM_Event event;
1706         if (c && (event = ZOOM_connection_get_event(c)))
1707         {
1708             ZOOM_Event_destroy (event);
1709             return i+1;
1710         }
1711     }
1712     for (i = 0; i<no; i++)
1713     {
1714         ZOOM_connection c = cs[i];
1715         ZOOM_Event event;
1716         if (c && ZOOM_connection_exec_task (c))
1717         {
1718             if ((event = ZOOM_connection_get_event(c)))
1719             {
1720                 ZOOM_Event_destroy (event);
1721                 return i+1;
1722             }
1723         }
1724     }
1725 #if HAVE_SYS_POLL_H
1726
1727 #else
1728     tv.tv_sec = 15;
1729     tv.tv_usec = 0;
1730     
1731     FD_ZERO (&input);
1732     FD_ZERO (&output);
1733     FD_ZERO (&except);
1734 #endif
1735     nfds = 0;
1736     for (i = 0; i<no; i++)
1737     {
1738         ZOOM_connection c = cs[i];
1739         int fd, mask;
1740         
1741         if (!c)
1742             continue;
1743         fd = z3950_connection_socket(c);
1744         mask = z3950_connection_mask(c);
1745
1746         if (fd == -1)
1747             continue;
1748         if (max_fd < fd)
1749             max_fd = fd;
1750
1751 #if HAVE_SYS_POLL_H
1752         if (mask)
1753         {
1754             short poll_events = 0;
1755
1756             if (mask & ZOOM_SELECT_READ)
1757                 poll_events += POLLIN;
1758             if (mask & ZOOM_SELECT_WRITE)
1759                 poll_events += POLLOUT;
1760             if (mask & ZOOM_SELECT_EXCEPT)
1761                 poll_events += POLLERR;
1762             pollfds[nfds].fd = fd;
1763             pollfds[nfds].events = poll_events;
1764             pollfds[nfds].revents = 0;
1765             poll_cs[nfds] = c;
1766             nfds++;
1767         }
1768 #else
1769         if (mask & ZOOM_SELECT_READ)
1770         {
1771             FD_SET (fd, &input);
1772             nfds++;
1773         }
1774         if (mask & ZOOM_SELECT_WRITE)
1775         {
1776             FD_SET (fd, &output);
1777             nfds++;
1778         }
1779         if (mask & ZOOM_SELECT_EXCEPT)
1780         {
1781             FD_SET (fd, &except);
1782             nfds++;
1783         }
1784 #endif
1785     }
1786     if (!nfds)
1787         return 0;
1788 #if HAVE_SYS_POLL_H
1789     yaz_log (LOG_DEBUG, "poll start");
1790     r = poll (pollfds, nfds, 15000);
1791     yaz_log (LOG_DEBUG, "poll stop, returned r=%d", r);
1792     for (i = 0; i<nfds; i++)
1793     {
1794         ZOOM_connection c = poll_cs[i];
1795         if (r && c->mask)
1796         {
1797             int mask = 0;
1798             if (pollfds[i].revents & POLLIN)
1799                 mask += ZOOM_SELECT_READ;
1800             if (pollfds[i].revents & POLLOUT)
1801                 mask += ZOOM_SELECT_WRITE;
1802             if (pollfds[i].revents & POLLERR)
1803                 mask += ZOOM_SELECT_EXCEPT;
1804             if (mask)
1805                 ZOOM_connection_do_io(c, mask);
1806         }
1807         else if (r == 0 && c->mask)
1808         {
1809             ZOOM_Event event = ZOOM_Event_create(ZOOM_EVENT_TIMEOUT);
1810             /* timeout and this connection was waiting */
1811             c->error = ZOOM_ERROR_TIMEOUT;
1812             do_close (c);
1813             ZOOM_connection_put_event(c, event);
1814         }
1815     }
1816 #else
1817     yaz_log (LOG_DEBUG, "select start");
1818     r = select (max_fd+1, &input, &output, &except, &tv);
1819     yaz_log (LOG_DEBUG, "select stop, returned r=%d", r);
1820     for (i = 0; i<no; i++)
1821     {
1822         ZOOM_connection c = cs[i];
1823         int fd, mask;
1824
1825         if (!c)
1826             continue;
1827         fd = z3950_connection_socket(c);
1828         mask = 0;
1829         if (r && c->mask)
1830         {
1831             /* no timeout and real socket */
1832             if (FD_ISSET(fd, &input))
1833                 mask += ZOOM_SELECT_READ;
1834             if (FD_ISSET(fd, &output))
1835                 mask += ZOOM_SELECT_WRITE;
1836             if (FD_ISSET(fd, &except))
1837                 mask += ZOOM_SELECT_EXCEPT;
1838             if (mask)
1839                 ZOOM_connection_do_io(c, mask);
1840         }
1841         if (r == 0 && c->mask)
1842         {
1843             ZOOM_Event event = ZOOM_Event_create(ZOOM_EVENT_IO_TIMEOUT);
1844             /* timeout and this connection was waiting */
1845             c->error = ZOOM_ERROR_TIMEOUT;
1846             do_close (c);
1847             yaz_log (LOG_DEBUG, "timeout");
1848             ZOOM_connection_put_event(c, event);
1849         }
1850     }
1851 #endif
1852     for (i = 0; i<no; i++)
1853     {
1854         ZOOM_connection c = cs[i];
1855         ZOOM_Event event;
1856         if (c && (event = ZOOM_connection_get_event(c)))
1857         {
1858             ZOOM_Event_destroy (event);
1859             return i+1;
1860         }
1861     }
1862     return 0;
1863 }