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