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