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