record fetch method ext that returns YAZ Z_External
[yaz-moved-to-github.git] / zoom / zoom-c.c
1 /*
2  * $Id: zoom-c.c,v 1.38 2002-07-29 19:51:34 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         {
1155             Z_External *r = (Z_External *) npr->u.databaseRecord;
1156             
1157             if (r->which == Z_External_sutrs)
1158             {
1159                 if (len) *len = r->u.sutrs->len;
1160                 return (const char *) r->u.sutrs->buf;
1161             }
1162             else if (r->which == Z_External_octet)
1163             {
1164                 if (len) *len = r->u.octet_aligned->len;
1165                 return (const char *) r->u.octet_aligned->buf;
1166             }
1167             else /* grs-1, explain, ... */
1168             {
1169                 if (len) *len = -1;
1170                 return (const char *) npr->u.databaseRecord;
1171             }
1172         }
1173         return 0;
1174     }
1175     else if (!strcmp (type, "ext"))
1176     {
1177         if (npr->which == Z_NamePlusRecord_databaseRecord)
1178             return (const char *) npr->u.databaseRecord;
1179         return 0;
1180     }
1181     return 0;
1182 }
1183
1184 static void record_cache_add (ZOOM_resultset r,
1185                               Z_NamePlusRecord *npr,
1186                               int pos,
1187                               const char *elementSetName)
1188 {
1189     ZOOM_record_cache rc;
1190
1191     for (rc = r->record_cache; rc; rc = rc->next)
1192     {
1193         if (pos == rc->pos)
1194         {
1195             if ((!elementSetName && !rc->elementSetName)
1196                 || (elementSetName && rc->elementSetName &&
1197                     !strcmp (elementSetName, rc->elementSetName)))
1198             {
1199                 /* not destroying rc->npr (it's handled by nmem )*/
1200                 rc->rec.npr = npr;
1201                 /* keeping wrbuf_marc too */
1202                 return;
1203             }
1204         }
1205     }
1206     rc = (ZOOM_record_cache) odr_malloc (r->odr, sizeof(*rc));
1207     rc->rec.npr = npr; 
1208     rc->rec.odr = 0;
1209     rc->rec.wrbuf_marc = 0;
1210     if (elementSetName)
1211         rc->elementSetName = odr_strdup (r->odr, elementSetName);
1212     else
1213         rc->elementSetName = 0;
1214     rc->pos = pos;
1215     rc->next = r->record_cache;
1216     r->record_cache = rc;
1217 }
1218
1219 static ZOOM_record record_cache_lookup (ZOOM_resultset r,
1220                                          int pos,
1221                                          const char *elementSetName)
1222 {
1223     ZOOM_record_cache rc;
1224
1225     for (rc = r->record_cache; rc; rc = rc->next)
1226     {
1227         if (pos == rc->pos)
1228         {
1229             if ((!elementSetName && !rc->elementSetName)
1230                 || (elementSetName && rc->elementSetName &&
1231                     !strcmp (elementSetName, rc->elementSetName)))
1232                 return &rc->rec;
1233         }
1234     }
1235     return 0;
1236 }
1237                                              
1238 static void handle_records (ZOOM_connection c, Z_Records *sr,
1239                             int present_phase)
1240 {
1241     ZOOM_resultset resultset;
1242
1243     if (!c->tasks)
1244         return ;
1245     switch (c->tasks->which)
1246     {
1247     case ZOOM_TASK_SEARCH:
1248         resultset = c->tasks->u.search.resultset;
1249         break;
1250     case ZOOM_TASK_RETRIEVE:
1251         resultset = c->tasks->u.retrieve.resultset;        
1252         break;
1253     default:
1254         return;
1255     }
1256     if (sr && sr->which == Z_Records_NSD)
1257     {
1258         Z_DiagRec dr, *dr_p = &dr;
1259         dr.which = Z_DiagRec_defaultFormat;
1260         dr.u.defaultFormat = sr->u.nonSurrogateDiagnostic;
1261         
1262         response_diag (c, dr_p);
1263     }
1264     else if (sr && sr->which == Z_Records_multipleNSD)
1265     {
1266         if (sr->u.multipleNonSurDiagnostics->num_diagRecs >= 1)
1267             response_diag(c, sr->u.multipleNonSurDiagnostics->diagRecs[0]);
1268         else
1269             c->error = ZOOM_ERROR_DECODE;
1270     }
1271     else 
1272     {
1273         if (resultset->count + resultset->start > resultset->size)
1274             resultset->count = resultset->size - resultset->start;
1275         if (resultset->count < 0)
1276             resultset->count = 0;
1277         if (sr && sr->which == Z_Records_DBOSD)
1278         {
1279             int i;
1280             NMEM nmem = odr_extract_mem (c->odr_in);
1281             Z_NamePlusRecordList *p =
1282                 sr->u.databaseOrSurDiagnostics;
1283             for (i = 0; i<p->num_records; i++)
1284             {
1285                 record_cache_add (resultset, p->records[i],
1286                                   i+ resultset->start, 0);
1287             }
1288             /* transfer our response to search_nmem .. we need it later */
1289             nmem_transfer (resultset->odr->mem, nmem);
1290             nmem_destroy (nmem);
1291             if (present_phase && p->num_records == 0)
1292             {
1293                 /* present response and we didn't get any records! */
1294                 c->error = ZOOM_ERROR_DECODE;
1295             }
1296         }
1297         else if (present_phase)
1298         {
1299             /* present response and we didn't get any records! */
1300             c->error = ZOOM_ERROR_DECODE;
1301         }
1302     }
1303 }
1304
1305 static void handle_present_response (ZOOM_connection c, Z_PresentResponse *pr)
1306 {
1307     handle_records (c, pr->records, 1);
1308 }
1309
1310 static void handle_search_response (ZOOM_connection c, Z_SearchResponse *sr)
1311 {
1312     ZOOM_resultset resultset;
1313
1314     yaz_log (LOG_DEBUG, "got search response");
1315
1316     if (!c->tasks || c->tasks->which != ZOOM_TASK_SEARCH)
1317         return ;
1318
1319     resultset = c->tasks->u.search.resultset;
1320
1321     resultset->size = *sr->resultCount;
1322     handle_records (c, sr->records, 0);
1323 }
1324
1325 static void sort_response (ZOOM_connection c, Z_SortResponse *res)
1326 {
1327     if (res->diagnostics && res->num_diagnostics > 0)
1328         response_diag (c, res->diagnostics[0]);
1329 }
1330
1331 static int scan_response (ZOOM_connection c, Z_ScanResponse *res)
1332 {
1333     NMEM nmem = odr_extract_mem (c->odr_in);
1334     ZOOM_scanset scan;
1335
1336     if (!c->tasks || c->tasks->which != ZOOM_TASK_SCAN)
1337         return 0;
1338     scan = c->tasks->u.scan.scan;
1339
1340     if (res->entries && res->entries->nonsurrogateDiagnostics)
1341         response_diag(c, res->entries->nonsurrogateDiagnostics[0]);
1342     scan->scan_response = res;
1343     nmem_transfer (scan->odr->mem, nmem);
1344     if (res->stepSize)
1345         ZOOM_options_set_int (scan->options, "stepSize", *res->stepSize);
1346     if (res->positionOfTerm)
1347         ZOOM_options_set_int (scan->options, "position", *res->positionOfTerm);
1348     if (res->scanStatus)
1349         ZOOM_options_set_int (scan->options, "scanStatus", *res->scanStatus);
1350     if (res->numberOfEntriesReturned)
1351         ZOOM_options_set_int (scan->options, "number",
1352                               *res->numberOfEntriesReturned);
1353     nmem_destroy (nmem);
1354     return 1;
1355 }
1356
1357 static int send_sort (ZOOM_connection c)
1358 {
1359     ZOOM_resultset  resultset;
1360
1361     if (!c->tasks || c->tasks->which != ZOOM_TASK_SEARCH)
1362         return 0;
1363
1364     resultset = c->tasks->u.search.resultset;
1365
1366     if (c->error)
1367     {
1368         resultset->r_sort_spec = 0;
1369         return 0;
1370     }
1371     if (resultset->r_sort_spec)
1372     {
1373         Z_APDU *apdu = zget_APDU(c->odr_out, Z_APDU_sortRequest);
1374         Z_SortRequest *req = apdu->u.sortRequest;
1375         
1376         req->num_inputResultSetNames = 1;
1377         req->inputResultSetNames = (Z_InternationalString **)
1378             odr_malloc (c->odr_out, sizeof(*req->inputResultSetNames));
1379         req->inputResultSetNames[0] =
1380             odr_strdup (c->odr_out, resultset->setname);
1381         req->sortedResultSetName = odr_strdup (c->odr_out, resultset->setname);
1382         req->sortSequence = resultset->r_sort_spec;
1383         resultset->r_sort_spec = 0;
1384         send_APDU (c, apdu);
1385         return 1;
1386     }
1387     return 0;
1388 }
1389
1390 static int send_present (ZOOM_connection c)
1391 {
1392     Z_APDU *apdu = zget_APDU(c->odr_out, Z_APDU_presentRequest);
1393     Z_PresentRequest *req = apdu->u.presentRequest;
1394     int i = 0;
1395     const char *syntax = 
1396         ZOOM_options_get (c->options, "preferredRecordSyntax");
1397     const char *element =
1398         ZOOM_options_get (c->options, "elementSetName");
1399     const char *schema =
1400         ZOOM_options_get (c->options, "schema");
1401     ZOOM_resultset  resultset;
1402
1403     if (!c->tasks)
1404         return 0;
1405
1406     switch (c->tasks->which)
1407     {
1408     case ZOOM_TASK_SEARCH:
1409         resultset = c->tasks->u.search.resultset;
1410         break;
1411     case ZOOM_TASK_RETRIEVE:
1412         resultset = c->tasks->u.retrieve.resultset;
1413         resultset->start = c->tasks->u.retrieve.start;
1414         resultset->count = c->tasks->u.retrieve.count;
1415
1416         if (resultset->start >= resultset->size)
1417             return 0;
1418         if (resultset->start + resultset->count > resultset->size)
1419             resultset->count = resultset->size - resultset->start;
1420         break;
1421     default:
1422         return 0;
1423     }
1424
1425     if (c->error)                  /* don't continue on error */
1426         return 0;
1427     if (resultset->start < 0)
1428         return 0;
1429     for (i = 0; i<resultset->count; i++)
1430     {
1431         ZOOM_record rec =
1432             record_cache_lookup (resultset, i + resultset->start, 0);
1433         if (!rec)
1434             break;
1435     }
1436     if (i == resultset->count)
1437         return 0;
1438
1439     resultset->start += i;
1440     resultset->count -= i;
1441     *req->resultSetStartPoint = resultset->start + 1;
1442     *req->numberOfRecordsRequested = resultset->count;
1443     assert (*req->numberOfRecordsRequested > 0);
1444
1445     if (syntax && *syntax)
1446         req->preferredRecordSyntax =
1447             yaz_str_to_z3950oid (c->odr_out, CLASS_RECSYN, syntax);
1448
1449     if (schema && *schema)
1450     {
1451         Z_RecordComposition *compo = (Z_RecordComposition *)
1452             odr_malloc (c->odr_out, sizeof(*compo));
1453
1454         req->recordComposition = compo;
1455         compo->which = Z_RecordComp_complex;
1456         compo->u.complex = (Z_CompSpec *)
1457             odr_malloc(c->odr_out, sizeof(*compo->u.complex));
1458         compo->u.complex->selectAlternativeSyntax = (bool_t *) 
1459             odr_malloc(c->odr_out, sizeof(bool_t));
1460         *compo->u.complex->selectAlternativeSyntax = 0;
1461
1462         compo->u.complex->generic = (Z_Specification *)
1463             odr_malloc(c->odr_out, sizeof(*compo->u.complex->generic));
1464
1465         compo->u.complex->generic->schema = (Odr_oid *)
1466             yaz_str_to_z3950oid (c->odr_out, CLASS_SCHEMA, schema);
1467
1468         if (!compo->u.complex->generic->schema)
1469         {
1470             /* OID wasn't a schema! Try record syntax instead. */
1471
1472             compo->u.complex->generic->schema = (Odr_oid *)
1473                 yaz_str_to_z3950oid (c->odr_out, CLASS_RECSYN, schema);
1474         }
1475         if (element && *element)
1476         {
1477             compo->u.complex->generic->elementSpec = (Z_ElementSpec *)
1478                 odr_malloc(c->odr_out, sizeof(Z_ElementSpec));
1479             compo->u.complex->generic->elementSpec->which =
1480                 Z_ElementSpec_elementSetName;
1481             compo->u.complex->generic->elementSpec->u.elementSetName =
1482                 odr_strdup (c->odr_out, element);
1483         }
1484         else
1485             compo->u.complex->generic->elementSpec = 0;
1486         compo->u.complex->num_dbSpecific = 0;
1487         compo->u.complex->dbSpecific = 0;
1488         compo->u.complex->num_recordSyntax = 0;
1489         compo->u.complex->recordSyntax = 0;
1490     }
1491     else if (element && *element)
1492     {
1493         Z_ElementSetNames *esn = (Z_ElementSetNames *)
1494             odr_malloc (c->odr_out, sizeof(*esn));
1495         Z_RecordComposition *compo = (Z_RecordComposition *)
1496             odr_malloc (c->odr_out, sizeof(*compo));
1497         
1498         esn->which = Z_ElementSetNames_generic;
1499         esn->u.generic = odr_strdup (c->odr_out, element);
1500         compo->which = Z_RecordComp_simple;
1501         compo->u.simple = esn;
1502         req->recordComposition = compo;
1503     }
1504     req->resultSetId = odr_strdup(c->odr_out, resultset->setname);
1505     send_APDU (c, apdu);
1506     return 1;
1507 }
1508
1509 ZOOM_API(ZOOM_scanset)
1510 ZOOM_connection_scan (ZOOM_connection c, const char *start)
1511 {
1512     ZOOM_scanset scan = (ZOOM_scanset) xmalloc (sizeof(*scan));
1513
1514     scan->connection = c;
1515     scan->odr = odr_createmem (ODR_DECODE);
1516     scan->options = ZOOM_options_create_with_parent (c->options);
1517     scan->refcount = 1;
1518     scan->scan_response = 0;
1519
1520     if ((scan->termListAndStartPoint =
1521          p_query_scan(scan->odr, PROTO_Z3950, &scan->attributeSet,
1522                       start)))
1523     {
1524         ZOOM_task task = ZOOM_connection_add_task (c, ZOOM_TASK_SCAN);
1525         task->u.scan.scan = scan;
1526         
1527         (scan->refcount)++;
1528         if (!c->async)
1529         {
1530             while (ZOOM_event (1, &c))
1531                 ;
1532         }
1533     }
1534     return scan;
1535 }
1536
1537 ZOOM_API(void)
1538 ZOOM_scanset_destroy (ZOOM_scanset scan)
1539 {
1540     if (!scan)
1541         return;
1542     (scan->refcount)--;
1543     if (scan->refcount == 0)
1544     {
1545         odr_destroy (scan->odr);
1546         
1547         ZOOM_options_destroy (scan->options);
1548         xfree (scan);
1549     }
1550 }
1551
1552 static int send_package (ZOOM_connection c)
1553 {
1554     ZOOM_Event event;
1555     if (!c->tasks)
1556         return 0;
1557     assert (c->tasks->which == ZOOM_TASK_PACKAGE);
1558
1559     event = ZOOM_Event_create (ZOOM_EVENT_SEND_APDU);
1560     ZOOM_connection_put_event (c, event);
1561
1562     do_write_ex (c, c->tasks->u.package->buf_out,
1563                  c->tasks->u.package->len_out);
1564     return 1;
1565 }
1566
1567 static int send_scan (ZOOM_connection c)
1568 {
1569     ZOOM_scanset scan;
1570     Z_APDU *apdu = zget_APDU(c->odr_out, Z_APDU_scanRequest);
1571     Z_ScanRequest *req = apdu->u.scanRequest;
1572     if (!c->tasks)
1573         return 0;
1574     assert (c->tasks->which == ZOOM_TASK_SCAN);
1575     scan = c->tasks->u.scan.scan;
1576
1577     req->termListAndStartPoint = scan->termListAndStartPoint;
1578     req->attributeSet = scan->attributeSet;
1579
1580     *req->numberOfTermsRequested =
1581         ZOOM_options_get_int(scan->options, "number", 10);
1582
1583     req->preferredPositionInResponse =
1584         odr_intdup (c->odr_out,
1585                     ZOOM_options_get_int(scan->options, "position", 1));
1586
1587     req->stepSize =
1588         odr_intdup (c->odr_out,
1589                     ZOOM_options_get_int(scan->options, "stepSize", 0));
1590     
1591     req->databaseNames = set_DatabaseNames (c, scan->options, 
1592                                             &req->num_databaseNames);
1593
1594     send_APDU (c, apdu);
1595
1596     return 1;
1597 }
1598
1599 ZOOM_API(size_t)
1600 ZOOM_scanset_size (ZOOM_scanset scan)
1601 {
1602     if (!scan || !scan->scan_response || !scan->scan_response->entries)
1603         return 0;
1604     return scan->scan_response->entries->num_entries;
1605 }
1606
1607 ZOOM_API(const char *)
1608 ZOOM_scanset_term (ZOOM_scanset scan, size_t pos,
1609                                int *occ, int *len)
1610 {
1611     const char *term = 0;
1612     size_t noent = ZOOM_scanset_size (scan);
1613     Z_ScanResponse *res = scan->scan_response;
1614     
1615     *len = 0;
1616     *occ = 0;
1617     if (pos >= noent)
1618         return 0;
1619     if (res->entries->entries[pos]->which == Z_Entry_termInfo)
1620     {
1621         Z_TermInfo *t = res->entries->entries[pos]->u.termInfo;
1622         
1623         if (t->term->which == Z_Term_general)
1624         {
1625             term = (const char *) t->term->u.general->buf;
1626             *len = t->term->u.general->len;
1627         }
1628         *occ = t->globalOccurrences ? *t->globalOccurrences : 0;
1629     }
1630     return term;
1631 }
1632
1633 ZOOM_API(const char *)
1634 ZOOM_scanset_option_get (ZOOM_scanset scan, const char *key)
1635 {
1636     return ZOOM_options_get (scan->options, key);
1637 }
1638
1639 ZOOM_API(void)
1640 ZOOM_scanset_option_set (ZOOM_scanset scan, const char *key,
1641                               const char *val)
1642 {
1643     ZOOM_options_set (scan->options, key, val);
1644 }
1645
1646
1647
1648 static Z_APDU *create_es_package (ZOOM_package p, int type)
1649 {
1650     const char *str;
1651     Z_APDU *apdu = zget_APDU(p->odr_out, Z_APDU_extendedServicesRequest);
1652     Z_ExtendedServicesRequest *req = apdu->u.extendedServicesRequest;
1653     
1654     *req->function = Z_ExtendedServicesRequest_create;
1655     
1656     str = ZOOM_options_get(p->options, "package-name");
1657     if (str && *str)
1658         req->packageName = nmem_strdup (p->odr_out->mem, str);
1659     
1660     str = ZOOM_options_get(p->options, "user-id");
1661     if (str)
1662         req->userId = nmem_strdup (p->odr_out->mem, str);
1663     
1664     req->packageType = yaz_oidval_to_z3950oid(p->odr_out, CLASS_EXTSERV,
1665                                               type);
1666
1667     str = ZOOM_options_get(p->options, "function");
1668     if (str)
1669     {
1670         if (!strcmp (str, "create"))
1671             *req->function = 1;
1672         if (!strcmp (str, "delete"))
1673             *req->function = 2;
1674         if (!strcmp (str, "modify"))
1675             *req->function = 3;
1676     }
1677     return apdu;
1678 }
1679
1680 static const char *ill_array_lookup (void *clientData, const char *idx)
1681 {
1682     ZOOM_package p = (ZOOM_package) clientData;
1683     return ZOOM_options_get (p->options, idx+4);
1684 }
1685
1686 static Z_External *encode_ill_request (ZOOM_package p)
1687 {
1688     ODR out = p->odr_out;
1689     ILL_Request *req;
1690     Z_External *r = 0;
1691     struct ill_get_ctl ctl;
1692         
1693     ctl.odr = p->odr_out;
1694     ctl.clientData = p;
1695     ctl.f = ill_array_lookup;
1696         
1697     req = ill_get_ILLRequest(&ctl, "ill", 0);
1698         
1699     if (!ill_Request (out, &req, 0, 0))
1700     {
1701         int ill_request_size;
1702         char *ill_request_buf = odr_getbuf (out, &ill_request_size, 0);
1703         if (ill_request_buf)
1704             odr_setbuf (out, ill_request_buf, ill_request_size, 1);
1705         return 0;
1706     }
1707     else
1708     {
1709         oident oid;
1710         int illRequest_size = 0;
1711         char *illRequest_buf = odr_getbuf (out, &illRequest_size, 0);
1712                 
1713         oid.proto = PROTO_GENERAL;
1714         oid.oclass = CLASS_GENERAL;
1715         oid.value = VAL_ISO_ILL_1;
1716                 
1717         r = (Z_External *) odr_malloc (out, sizeof(*r));
1718         r->direct_reference = odr_oiddup(out,oid_getoidbyent(&oid)); 
1719         r->indirect_reference = 0;
1720         r->descriptor = 0;
1721         r->which = Z_External_single;
1722                 
1723         r->u.single_ASN1_type = (Odr_oct *)
1724             odr_malloc (out, sizeof(*r->u.single_ASN1_type));
1725         r->u.single_ASN1_type->buf = odr_malloc (out, illRequest_size);
1726         r->u.single_ASN1_type->len = illRequest_size;
1727         r->u.single_ASN1_type->size = illRequest_size;
1728         memcpy (r->u.single_ASN1_type->buf, illRequest_buf, illRequest_size);
1729     }
1730     return r;
1731 }
1732
1733 static Z_ItemOrder *encode_item_order(ZOOM_package p)
1734 {
1735     Z_ItemOrder *req = odr_malloc (p->odr_out, sizeof(*req));
1736     const char *str;
1737     
1738     req->which=Z_IOItemOrder_esRequest;
1739     req->u.esRequest = (Z_IORequest *) 
1740         odr_malloc(p->odr_out,sizeof(Z_IORequest));
1741
1742     /* to keep part ... */
1743     req->u.esRequest->toKeep = (Z_IOOriginPartToKeep *)
1744         odr_malloc(p->odr_out,sizeof(Z_IOOriginPartToKeep));
1745     req->u.esRequest->toKeep->supplDescription = 0;
1746     req->u.esRequest->toKeep->contact =
1747         odr_malloc (p->odr_out, sizeof(*req->u.esRequest->toKeep->contact));
1748         
1749     str = ZOOM_options_get(p->options, "contact-name");
1750     req->u.esRequest->toKeep->contact->name = str ?
1751         nmem_strdup (p->odr_out->mem, str) : 0;
1752         
1753     str = ZOOM_options_get(p->options, "contact-phone");
1754     req->u.esRequest->toKeep->contact->phone = str ?
1755         nmem_strdup (p->odr_out->mem, str) : 0;
1756         
1757     str = ZOOM_options_get(p->options, "contact-email");
1758     req->u.esRequest->toKeep->contact->email = str ?
1759         nmem_strdup (p->odr_out->mem, str) : 0;
1760         
1761     req->u.esRequest->toKeep->addlBilling = 0;
1762         
1763     /* not to keep part ... */
1764     req->u.esRequest->notToKeep = (Z_IOOriginPartNotToKeep *)
1765         odr_malloc(p->odr_out,sizeof(Z_IOOriginPartNotToKeep));
1766         
1767     req->u.esRequest->notToKeep->resultSetItem = (Z_IOResultSetItem *)
1768         odr_malloc(p->odr_out, sizeof(Z_IOResultSetItem));
1769
1770     str = ZOOM_options_get(p->options, "itemorder-setname");
1771     if (!str)
1772         str = "default";
1773     req->u.esRequest->notToKeep->resultSetItem->resultSetId =
1774         nmem_strdup (p->odr_out->mem, str);
1775     req->u.esRequest->notToKeep->resultSetItem->item =
1776         (int *) odr_malloc(p->odr_out, sizeof(int));
1777         
1778     str = ZOOM_options_get(p->options, "itemorder-item");
1779     *req->u.esRequest->notToKeep->resultSetItem->item =
1780         (str ? atoi(str) : 1);
1781     
1782     req->u.esRequest->notToKeep->itemRequest = encode_ill_request(p);
1783     
1784     return req;
1785 }
1786
1787 ZOOM_API(void)
1788     ZOOM_package_send (ZOOM_package p, const char *type)
1789 {
1790     Z_APDU *apdu = 0;
1791     ZOOM_connection c;
1792     if (!p)
1793         return;
1794     c = p->connection;
1795     odr_reset (p->odr_out);
1796     xfree (p->buf_out);
1797     p->buf_out = 0;
1798     if (!strcmp(type, "itemorder"))
1799     {
1800         Z_External *r;
1801         apdu = create_es_package (p, VAL_ITEMORDER);
1802         if (apdu)
1803         {
1804             r = odr_malloc (p->odr_out, sizeof(*r));
1805             
1806             r->direct_reference =
1807                 yaz_oidval_to_z3950oid(p->odr_out, CLASS_EXTSERV,
1808                                        VAL_ITEMORDER);
1809             r->descriptor = 0;
1810             r->which = Z_External_itemOrder;
1811             r->indirect_reference = 0;
1812             r->u.itemOrder = encode_item_order (p);
1813
1814             apdu->u.extendedServicesRequest->taskSpecificParameters = r;
1815         }
1816     }
1817     if (apdu)
1818     {
1819         if (encode_APDU(p->connection, apdu, p->odr_out) == 0)
1820         {
1821             char *buf;
1822
1823             ZOOM_task task = ZOOM_connection_add_task (c, ZOOM_TASK_PACKAGE);
1824             task->u.package = p;
1825             buf = odr_getbuf(p->odr_out, &p->len_out, 0);
1826             p->buf_out = xmalloc (p->len_out);
1827             memcpy (p->buf_out, buf, p->len_out);
1828             
1829             (p->refcount)++;
1830             if (!c->async)
1831             {
1832                 while (ZOOM_event (1, &c))
1833                     ;
1834             }
1835         }
1836     }
1837 }
1838
1839 ZOOM_API(ZOOM_package)
1840     ZOOM_connection_package (ZOOM_connection c, ZOOM_options options)
1841 {
1842     ZOOM_package p = (ZOOM_package) xmalloc (sizeof(*p));
1843
1844     p->connection = c;
1845     p->odr_out = odr_createmem (ODR_ENCODE);
1846     p->options = ZOOM_options_create_with_parent2 (options, c->options);
1847     p->refcount = 1;
1848     p->buf_out = 0;
1849     p->len_out = 0;
1850     return p;
1851 }
1852
1853 ZOOM_API(void)
1854     ZOOM_package_destroy(ZOOM_package p)
1855 {
1856     if (!p)
1857         return;
1858     (p->refcount)--;
1859     if (p->refcount == 0)
1860     {
1861         odr_destroy (p->odr_out);
1862         xfree (p->buf_out);
1863         
1864         ZOOM_options_destroy (p->options);
1865         xfree (p);
1866     }
1867 }
1868
1869 ZOOM_API(const char *)
1870 ZOOM_package_option_get (ZOOM_package p, const char *key)
1871 {
1872     return ZOOM_options_get (p->options, key);
1873 }
1874
1875 ZOOM_API(void)
1876 ZOOM_package_option_set (ZOOM_package p, const char *key,
1877                               const char *val)
1878 {
1879     ZOOM_options_set (p->options, key, val);
1880 }
1881
1882 static int ZOOM_connection_exec_task (ZOOM_connection c)
1883 {
1884     ZOOM_task task = c->tasks;
1885
1886     yaz_log (LOG_DEBUG, "ZOOM_connection_exec_task");
1887     if (!task)
1888         return 0;
1889     if (c->error != ZOOM_ERROR_NONE ||
1890         (!c->cs && task->which != ZOOM_TASK_CONNECT))
1891     {
1892         ZOOM_connection_remove_tasks (c);
1893         return 0;
1894     }
1895     yaz_log (LOG_DEBUG, "ZOOM_connection_exec_task type=%d run=%d",
1896              task->which, task->running);
1897     if (task->running)
1898         return 0;
1899     task->running = 1;
1900     switch (task->which)
1901     {
1902     case ZOOM_TASK_SEARCH:
1903         /* see if search hasn't been sent yet. */
1904         if (ZOOM_connection_send_search (c))
1905             return 1;
1906         break;
1907     case ZOOM_TASK_RETRIEVE:
1908         if (send_present (c))
1909             return 1;
1910         break;
1911     case ZOOM_TASK_CONNECT:
1912         if (do_connect(c))
1913             return 1;
1914         break;
1915     case ZOOM_TASK_SCAN:
1916         if (send_scan(c))
1917             return 1;
1918         break;
1919     case ZOOM_TASK_PACKAGE:
1920         if (send_package(c))
1921             return 1;
1922     }
1923     ZOOM_connection_remove_task (c);
1924     return 0;
1925 }
1926
1927 static int send_sort_present (ZOOM_connection c)
1928 {
1929     int r = send_sort (c);
1930     if (!r)
1931         r = send_present (c);
1932     return r;
1933 }
1934
1935 static int es_response (ZOOM_connection c,
1936                         Z_ExtendedServicesResponse *res)
1937 {
1938     if (!c->tasks || c->tasks->which != ZOOM_TASK_PACKAGE)
1939         return 0;
1940     if (res->diagnostics && res->num_diagnostics > 0)
1941         response_diag(c, res->diagnostics[0]);
1942     if (res->taskPackage &&
1943         res->taskPackage->which == Z_External_extendedService)
1944     {
1945         Z_TaskPackage *taskPackage = res->taskPackage->u.extendedService;
1946         Odr_oct *id = taskPackage->targetReference;
1947         
1948         if (id)
1949             ZOOM_options_setl (c->tasks->u.package->options,
1950                                "targetReference", id->buf, id->len);
1951     }
1952     return 1;
1953 }
1954
1955
1956 static void handle_apdu (ZOOM_connection c, Z_APDU *apdu)
1957 {
1958     Z_InitResponse *initrs;
1959     
1960     c->mask = 0;
1961     yaz_log (LOG_DEBUG, "hande_apdu type=%d", apdu->which);
1962     switch(apdu->which)
1963     {
1964     case Z_APDU_initResponse:
1965         initrs = apdu->u.initResponse;
1966         if (!*initrs->result)
1967         {
1968             c->error = ZOOM_ERROR_INIT;
1969         }
1970         else
1971         {
1972             char *cookie =
1973                 yaz_oi_get_string_oidval (&apdu->u.initResponse->otherInfo,
1974                                           VAL_COOKIE, 1, 0);
1975             xfree (c->cookie_in);
1976             c->cookie_in = 0;
1977             if (cookie)
1978                 c->cookie_in = xstrdup(cookie);
1979             if (ODR_MASK_GET(initrs->options, Z_Options_namedResultSets) &&
1980                 ODR_MASK_GET(initrs->protocolVersion, Z_ProtocolVersion_3))
1981                 c->support_named_resultsets = 1;
1982             if (c->tasks)
1983             {
1984                 assert (c->tasks->which == ZOOM_TASK_CONNECT);
1985                 ZOOM_connection_remove_task (c);
1986             }
1987             ZOOM_connection_exec_task (c);
1988         }
1989         if (ODR_MASK_GET(initrs->options, Z_Options_negotiationModel))
1990         {
1991             NMEM tmpmem = nmem_create();
1992             Z_CharSetandLanguageNegotiation *p =
1993                 yaz_get_charneg_record(initrs->otherInfo);
1994             
1995             if (p)
1996             {
1997                 char *charset=NULL, *lang=NULL;
1998                 int selected;
1999                 
2000                 yaz_get_response_charneg(tmpmem, p, &charset, &lang, &selected);
2001                 yaz_log(LOG_DEBUG, "Target accepted: charset - %s,"
2002                         "language - %s, select - %d",
2003                         charset, lang, selected);
2004                 
2005                 nmem_destroy(tmpmem);
2006             }
2007         }       
2008         break;
2009     case Z_APDU_searchResponse:
2010         handle_search_response (c, apdu->u.searchResponse);
2011         if (!send_sort_present (c))
2012             ZOOM_connection_remove_task (c);
2013         break;
2014     case Z_APDU_presentResponse:
2015         handle_present_response (c, apdu->u.presentResponse);
2016         if (!send_present (c))
2017             ZOOM_connection_remove_task (c);
2018         break;
2019     case Z_APDU_sortResponse:
2020         sort_response (c, apdu->u.sortResponse);
2021         if (!send_present (c))
2022             ZOOM_connection_remove_task (c);
2023         break;
2024     case Z_APDU_scanResponse:
2025         scan_response (c, apdu->u.scanResponse);
2026         ZOOM_connection_remove_task (c);
2027         break;
2028     case Z_APDU_extendedServicesResponse:
2029         es_response (c, apdu->u.extendedServicesResponse);
2030         ZOOM_connection_remove_task (c);
2031         break;
2032     case Z_APDU_close:
2033         if (c->reconnect_ok)
2034         {
2035             do_close(c);
2036             c->tasks->running = 0;
2037             ZOOM_connection_insert_task (c, ZOOM_TASK_CONNECT);
2038         }
2039         else
2040         {
2041             c->error = ZOOM_ERROR_CONNECTION_LOST;
2042             do_close(c);
2043         }
2044         break;
2045     default:
2046         c->error = ZOOM_ERROR_DECODE;
2047         do_close(c);
2048     }
2049 }
2050
2051 static int do_read (ZOOM_connection c)
2052 {
2053     int r;
2054     Z_APDU *apdu;
2055     ZOOM_Event event;
2056     
2057     event = ZOOM_Event_create (ZOOM_EVENT_RECV_DATA);
2058     ZOOM_connection_put_event (c, event);
2059     
2060     yaz_log (LOG_DEBUG, "do_read len=%d", c->len_in);
2061
2062     r = cs_get (c->cs, &c->buf_in, &c->len_in);
2063     if (r == 1)
2064         return 0;
2065     if (r <= 0)
2066     {
2067         if (c->reconnect_ok)
2068         {
2069             do_close (c);
2070             c->reconnect_ok = 0;
2071             yaz_log (LOG_DEBUG, "reconnect read");
2072             c->tasks->running = 0;
2073             ZOOM_connection_insert_task (c, ZOOM_TASK_CONNECT);
2074         }
2075         else
2076         {
2077             c->error= ZOOM_ERROR_CONNECTION_LOST;
2078             do_close (c);
2079         }
2080     }
2081     else
2082     {
2083         ZOOM_Event event;
2084         odr_reset (c->odr_in);
2085         odr_setbuf (c->odr_in, c->buf_in, r, 0);
2086         event = ZOOM_Event_create (ZOOM_EVENT_RECV_APDU);
2087         ZOOM_connection_put_event (c, event);
2088         if (!z_APDU (c->odr_in, &apdu, 0, 0))
2089         {
2090             c->error = ZOOM_ERROR_DECODE;
2091             do_close (c);
2092         }
2093         else
2094             handle_apdu (c, apdu);
2095         c->reconnect_ok = 0;
2096     }
2097     return 1;
2098 }
2099
2100 static int do_write_ex (ZOOM_connection c, char *buf_out, int len_out)
2101 {
2102     int r;
2103     ZOOM_Event event;
2104     
2105     event = ZOOM_Event_create(ZOOM_EVENT_SEND_DATA);
2106     ZOOM_connection_put_event (c, event);
2107
2108     yaz_log (LOG_DEBUG, "do_write_ex len=%d", len_out);
2109     if ((r=cs_put (c->cs, buf_out, len_out)) < 0)
2110     {
2111         if (c->reconnect_ok)
2112         {
2113             do_close (c);
2114             c->reconnect_ok = 0;
2115             yaz_log (LOG_DEBUG, "reconnect write");
2116             c->tasks->running = 0;
2117             ZOOM_connection_insert_task (c, ZOOM_TASK_CONNECT);
2118             return 0;
2119         }
2120         if (c->state == STATE_CONNECTING)
2121             c->error = ZOOM_ERROR_CONNECT;
2122         else
2123             c->error = ZOOM_ERROR_CONNECTION_LOST;
2124         do_close (c);
2125         return 1;
2126     }
2127     else if (r == 1)
2128     {    
2129         c->mask = ZOOM_SELECT_EXCEPT;
2130         if (c->cs->io_pending & CS_WANT_WRITE)
2131             c->mask += ZOOM_SELECT_WRITE;
2132         if (c->cs->io_pending & CS_WANT_READ)
2133             c->mask += ZOOM_SELECT_READ;
2134         yaz_log (LOG_DEBUG, "do_write_ex 1 mask=%d", c->mask);
2135     }
2136     else
2137     {
2138         // c->reconnect_ok = 0;
2139         c->mask = ZOOM_SELECT_READ|ZOOM_SELECT_EXCEPT;
2140         yaz_log (LOG_DEBUG, "do_write_ex 2 mask=%d", c->mask);
2141     }
2142     return 0;
2143 }
2144
2145 static int do_write(ZOOM_connection c)
2146 {
2147     return do_write_ex (c, c->buf_out, c->len_out);
2148 }
2149
2150
2151 ZOOM_API(const char *)
2152 ZOOM_connection_option_get (ZOOM_connection c, const char *key)
2153 {
2154     return ZOOM_options_get (c->options, key);
2155 }
2156
2157 ZOOM_API(void)
2158 ZOOM_connection_option_set (ZOOM_connection c, const char *key,
2159                                   const char *val)
2160 {
2161     ZOOM_options_set (c->options, key, val);
2162 }
2163
2164 ZOOM_API(const char *)
2165 ZOOM_resultset_option_get (ZOOM_resultset r, const char *key)
2166 {
2167     return ZOOM_options_get (r->options, key);
2168 }
2169
2170 ZOOM_API(void)
2171 ZOOM_resultset_option_set (ZOOM_resultset r, const char *key,
2172                                   const char *val)
2173 {
2174     ZOOM_options_set (r->options, key, val);
2175 }
2176
2177
2178 ZOOM_API(int)
2179 ZOOM_connection_errcode (ZOOM_connection c)
2180 {
2181     return ZOOM_connection_error (c, 0, 0);
2182 }
2183
2184 ZOOM_API(const char *)
2185 ZOOM_connection_errmsg (ZOOM_connection c)
2186 {
2187     const char *msg;
2188     ZOOM_connection_error (c, &msg, 0);
2189     return msg;
2190 }
2191
2192 ZOOM_API(const char *)
2193 ZOOM_connection_addinfo (ZOOM_connection c)
2194 {
2195     const char *addinfo;
2196     ZOOM_connection_error (c, 0, &addinfo);
2197     return addinfo;
2198 }
2199
2200 ZOOM_API(int)
2201 ZOOM_connection_error (ZOOM_connection c, const char **cp,
2202                             const char **addinfo)
2203 {
2204     int error = c->error;
2205     if (cp)
2206     {
2207         switch (error)
2208         {
2209         case ZOOM_ERROR_NONE:
2210             *cp = "No error"; break;
2211         case ZOOM_ERROR_CONNECT:
2212             *cp = "Connect failed"; break;
2213         case ZOOM_ERROR_MEMORY:
2214             *cp = "Out of memory"; break;
2215         case ZOOM_ERROR_ENCODE:
2216             *cp = "Encoding failed"; break;
2217         case ZOOM_ERROR_DECODE:
2218             *cp = "Decoding failed"; break;
2219         case ZOOM_ERROR_CONNECTION_LOST:
2220             *cp = "Connection lost"; break;
2221         case ZOOM_ERROR_INIT:
2222             *cp = "Init rejected"; break;
2223         case ZOOM_ERROR_INTERNAL:
2224             *cp = "Internal failure"; break;
2225         case ZOOM_ERROR_TIMEOUT:
2226             *cp = "Timeout"; break;
2227         default:
2228             *cp = diagbib1_str (error);
2229         }
2230     }
2231     if (addinfo)
2232     {
2233         if (c->addinfo)
2234             *addinfo = c->addinfo;
2235         else
2236             *addinfo = "";
2237     }
2238     return c->error;
2239 }
2240
2241 static int ZOOM_connection_do_io(ZOOM_connection c, int mask)
2242 {
2243     ZOOM_Event event = 0;
2244     int r = cs_look(c->cs);
2245     yaz_log (LOG_DEBUG, "ZOOM_connection_do_io c=%p mask=%d cs_look=%d",
2246              c, mask, r);
2247     
2248     if (r == CS_NONE)
2249     {
2250         event = ZOOM_Event_create (ZOOM_EVENT_CONNECT);
2251         c->error = ZOOM_ERROR_CONNECT;
2252         do_close (c);
2253         ZOOM_connection_put_event (c, event);
2254     }
2255     else if (r == CS_CONNECT)
2256     {
2257         int ret;
2258         event = ZOOM_Event_create (ZOOM_EVENT_CONNECT);
2259
2260         ret = cs_rcvconnect (c->cs);
2261         yaz_log (LOG_DEBUG, "cs_rcvconnect returned %d", ret);
2262         if (ret == 1)
2263         {
2264             c->mask = ZOOM_SELECT_EXCEPT;
2265             if (c->cs->io_pending & CS_WANT_WRITE)
2266                 c->mask += ZOOM_SELECT_WRITE;
2267             if (c->cs->io_pending & CS_WANT_READ)
2268                 c->mask += ZOOM_SELECT_READ;
2269             ZOOM_connection_put_event (c, event);
2270         }
2271         else if (ret == 0)
2272         {
2273             ZOOM_connection_put_event (c, event);
2274             ZOOM_connection_send_init (c);
2275             c->state = STATE_ESTABLISHED;
2276         }
2277         else
2278         {
2279             c->error = ZOOM_ERROR_CONNECT;
2280             do_close (c);
2281             ZOOM_connection_put_event (c, event);
2282         }
2283     }
2284     else
2285     {
2286         if (mask & ZOOM_SELECT_READ)
2287             do_read (c);
2288         if (c->cs && (mask & ZOOM_SELECT_WRITE))
2289             do_write (c);
2290     }
2291     return 1;
2292 }
2293
2294 ZOOM_API(int)
2295 ZOOM_connection_last_event(ZOOM_connection cs)
2296 {
2297     if (!cs)
2298         return ZOOM_EVENT_NONE;
2299     return cs->last_event;
2300 }
2301
2302 ZOOM_API(int)
2303 ZOOM_event (int no, ZOOM_connection *cs)
2304 {
2305 #if HAVE_SYS_POLL_H
2306     struct pollfd pollfds[1024];
2307     ZOOM_connection poll_cs[1024];
2308 #else
2309     struct timeval tv;
2310     fd_set input, output, except;
2311 #endif
2312     int i, r, nfds;
2313     int max_fd = 0;
2314
2315     for (i = 0; i<no; i++)
2316     {
2317         ZOOM_connection c = cs[i];
2318         ZOOM_Event event;
2319         if (c && (event = ZOOM_connection_get_event(c)))
2320         {
2321             ZOOM_Event_destroy (event);
2322             return i+1;
2323         }
2324     }
2325     for (i = 0; i<no; i++)
2326     {
2327         ZOOM_connection c = cs[i];
2328         ZOOM_Event event;
2329         if (c && ZOOM_connection_exec_task (c))
2330         {
2331             if ((event = ZOOM_connection_get_event(c)))
2332             {
2333                 ZOOM_Event_destroy (event);
2334                 return i+1;
2335             }
2336         }
2337     }
2338 #if HAVE_SYS_POLL_H
2339
2340 #else
2341     tv.tv_sec = 25;
2342     tv.tv_usec = 0;
2343     
2344     FD_ZERO (&input);
2345     FD_ZERO (&output);
2346     FD_ZERO (&except);
2347 #endif
2348     nfds = 0;
2349     for (i = 0; i<no; i++)
2350     {
2351         ZOOM_connection c = cs[i];
2352         int fd, mask;
2353         
2354         if (!c)
2355             continue;
2356         fd = z3950_connection_socket(c);
2357         mask = z3950_connection_mask(c);
2358
2359         if (fd == -1)
2360             continue;
2361         if (max_fd < fd)
2362             max_fd = fd;
2363
2364 #if HAVE_SYS_POLL_H
2365         if (mask)
2366         {
2367             short poll_events = 0;
2368
2369             if (mask & ZOOM_SELECT_READ)
2370                 poll_events += POLLIN;
2371             if (mask & ZOOM_SELECT_WRITE)
2372                 poll_events += POLLOUT;
2373             if (mask & ZOOM_SELECT_EXCEPT)
2374                 poll_events += POLLERR;
2375             pollfds[nfds].fd = fd;
2376             pollfds[nfds].events = poll_events;
2377             pollfds[nfds].revents = 0;
2378             poll_cs[nfds] = c;
2379             nfds++;
2380         }
2381 #else
2382         if (mask & ZOOM_SELECT_READ)
2383         {
2384             FD_SET (fd, &input);
2385             nfds++;
2386         }
2387         if (mask & ZOOM_SELECT_WRITE)
2388         {
2389             FD_SET (fd, &output);
2390             nfds++;
2391         }
2392         if (mask & ZOOM_SELECT_EXCEPT)
2393         {
2394             FD_SET (fd, &except);
2395             nfds++;
2396         }
2397 #endif
2398     }
2399     if (!nfds)
2400         return 0;
2401 #if HAVE_SYS_POLL_H
2402     r = poll (pollfds, nfds, 25000);
2403     for (i = 0; i<nfds; i++)
2404     {
2405         ZOOM_connection c = poll_cs[i];
2406         if (r && c->mask)
2407         {
2408             int mask = 0;
2409             if (pollfds[i].revents & POLLIN)
2410                 mask += ZOOM_SELECT_READ;
2411             if (pollfds[i].revents & POLLOUT)
2412                 mask += ZOOM_SELECT_WRITE;
2413             if (pollfds[i].revents & POLLERR)
2414                 mask += ZOOM_SELECT_EXCEPT;
2415             if (mask)
2416                 ZOOM_connection_do_io(c, mask);
2417         }
2418         else if (r == 0 && c->mask)
2419         {
2420             ZOOM_Event event = ZOOM_Event_create(ZOOM_EVENT_TIMEOUT);
2421             /* timeout and this connection was waiting */
2422             c->error = ZOOM_ERROR_TIMEOUT;
2423             do_close (c);
2424             ZOOM_connection_put_event(c, event);
2425         }
2426     }
2427 #else
2428     yaz_log (LOG_DEBUG, "select start");
2429     r = select (max_fd+1, &input, &output, &except, &tv);
2430     yaz_log (LOG_DEBUG, "select stop, returned r=%d", r);
2431     for (i = 0; i<no; i++)
2432     {
2433         ZOOM_connection c = cs[i];
2434         int fd, mask;
2435
2436         if (!c)
2437             continue;
2438         fd = z3950_connection_socket(c);
2439         mask = 0;
2440         if (r && c->mask)
2441         {
2442             /* no timeout and real socket */
2443             if (FD_ISSET(fd, &input))
2444                 mask += ZOOM_SELECT_READ;
2445             if (FD_ISSET(fd, &output))
2446                 mask += ZOOM_SELECT_WRITE;
2447             if (FD_ISSET(fd, &except))
2448                 mask += ZOOM_SELECT_EXCEPT;
2449             if (mask)
2450                 ZOOM_connection_do_io(c, mask);
2451         }
2452         if (r == 0 && c->mask)
2453         {
2454             ZOOM_Event event = ZOOM_Event_create(ZOOM_EVENT_TIMEOUT);
2455             /* timeout and this connection was waiting */
2456             c->error = ZOOM_ERROR_TIMEOUT;
2457             do_close (c);
2458             yaz_log (LOG_DEBUG, "timeout");
2459             ZOOM_connection_put_event(c, event);
2460         }
2461     }
2462 #endif
2463     for (i = 0; i<no; i++)
2464     {
2465         ZOOM_connection c = cs[i];
2466         ZOOM_Event event;
2467         if (c && (event = ZOOM_connection_get_event(c)))
2468         {
2469             ZOOM_Event_destroy (event);
2470             return i+1;
2471         }
2472     }
2473     return 0;
2474 }