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