Protocol behavior sections. record_get raw returning Z External *.
[yaz-moved-to-github.git] / zoom / zoom-c.c
1 /*
2  * $Id: zoom-c.c,v 1.4 2001-11-11 22:25:25 adam Exp $
3  *
4  * ZOOM layer for C, connections, result sets, queries.
5  */
6 #include <assert.h>
7 #include <yaz/xmalloc.h>
8 #include <yaz/otherinfo.h>
9 #include <yaz/log.h>
10 #include <yaz/pquery.h>
11 #include <yaz/diagbib1.h>
12
13 #include "zoom-p.h"
14
15 #define USE_POLL 0
16
17 #if USE_POLL
18 #include <sys/poll.h>
19 #endif
20
21 static Z3950_record record_cache_lookup (Z3950_resultset r,
22                                          int pos,
23                                          const char *elementSetName);
24
25 static void clear_error (Z3950_connection c)
26 {
27     c->error = Z3950_ERROR_NONE;
28     xfree (c->addinfo);
29     c->addinfo = 0;
30 }
31
32 Z3950_connection Z3950_connection_create (Z3950_options options)
33 {
34     Z3950_connection c = xmalloc (sizeof(*c));
35
36     c->event_pending = 0;
37     c->cs = 0;
38     c->mask = 0;
39     c->state = STATE_IDLE;
40     c->error = Z3950_ERROR_NONE;
41     c->addinfo = 0;
42     c->buf_in = 0;
43     c->len_in = 0;
44     c->buf_out = 0;
45     c->len_out = 0;
46     c->resultsets = 0;
47
48     c->options = Z3950_options_create_with_parent(options);
49
50     c->host_port = 0;
51     c->proxy = 0;
52
53     c->cookie_out = 0;
54     c->cookie_in = 0;
55     c->tasks = 0;
56
57     c->odr_in = odr_createmem (ODR_DECODE);
58     c->odr_out = odr_createmem (ODR_ENCODE);
59
60     c->async = 0;
61     return c;
62 }
63
64 /* set database names. Take local databases (if set); otherwise
65    take databases given in ZURL (if set); otherwise use Default */
66 static char **set_DatabaseNames (Z3950_connection con, int *num)
67 {
68     char **databaseNames;
69     const char *c;
70     int no = 2;
71     const char *cp = Z3950_options_get (con->options, "databaseName");
72     
73     if (!cp || !*cp)
74     {
75         cp = strchr (con->host_port, '/');
76         if (cp)
77             cp++;
78         }
79     if (cp)
80     {
81         c = cp;
82         while ((c = strchr(c, '+')))
83         {
84             c++;
85             no++;
86         }
87     }
88     else
89         cp = "Default";
90     databaseNames = odr_malloc (con->odr_out, no * sizeof(*databaseNames));
91     no = 0;
92     while (*cp)
93     {
94         c = strchr (cp, '+');
95         if (!c)
96             c = cp + strlen(cp);
97         else if (c == cp)
98         {
99             cp++;
100             continue;
101         }
102         /* cp ptr to first char of db name, c is char
103            following db name */
104         databaseNames[no] = odr_malloc (con->odr_out, 1+c-cp);
105         memcpy (databaseNames[no], cp, c-cp);
106         databaseNames[no++][c-cp] = '\0';
107         cp = c;
108         if (*cp)
109             cp++;
110     }
111     databaseNames[no] = NULL;
112     *num = no;
113     return databaseNames;
114 }
115
116 Z3950_connection Z3950_connection_new (const char *host, int portnum)
117 {
118     Z3950_connection c = Z3950_connection_create (0);
119
120     Z3950_connection_connect (c, host, portnum);
121     return c;
122 }
123
124 void Z3950_connection_connect(Z3950_connection c,
125                               const char *host, int portnum)
126 {
127     const char *val;
128
129     val = Z3950_options_get (c->options, "proxy");
130     if (val && *val)
131         c->proxy = xstrdup (val);
132     else
133         c->proxy = 0;
134
135     if (portnum)
136     {
137         char hostn[128];
138         sprintf (hostn, "%.80s:%d", host, portnum);
139         c->host_port = xstrdup(hostn);
140     }
141     else
142         c->host_port = xstrdup(host);
143
144     c->async = Z3950_options_get_bool (c->options, "async", 0);
145     
146     if (!c->async)
147     {
148         while (Z3950_event (1, &c))
149             ;
150     }
151 }
152
153 Z3950_query Z3950_query_create(void)
154 {
155     Z3950_query s = xmalloc (sizeof(*s));
156
157     s->refcount = 1;
158     s->query = 0;
159     s->sort_spec = 0;
160     s->odr = odr_createmem (ODR_ENCODE);
161
162     return s;
163 }
164
165 const char *Z3950_connection_host (Z3950_connection c)
166 {
167     return c->host_port;
168 }
169
170 void Z3950_query_destroy(Z3950_query s)
171 {
172     if (!s)
173         return;
174
175     (s->refcount)--;
176     yaz_log (LOG_DEBUG, "Z3950_query_destroy count=%d", s->refcount);
177     if (s->refcount == 0)
178     {
179         odr_destroy (s->odr);
180         xfree (s);
181     }
182 }
183
184 int Z3950_query_prefix(Z3950_query s, const char *str)
185 {
186     s->query = odr_malloc (s->odr, sizeof(*s->query));
187     s->query->which = Z_Query_type_1;
188     s->query->u.type_1 =  p_query_rpn(s->odr, PROTO_Z3950, str);
189     if (!s->query->u.type_1)
190         return -1;
191     return 0;
192 }
193
194 int Z3950_query_sortby(Z3950_query s, const char *criteria)
195 {
196     s->sort_spec = yaz_sort_spec (s->odr, criteria);
197     if (!s->sort_spec)
198         return -1;
199     return 0;
200 }
201
202 static int do_write(Z3950_connection c);
203
204
205 Z3950_task Z3950_connection_add_task (Z3950_connection c, int which)
206 {
207     Z3950_task *taskp = &c->tasks;
208     while (*taskp)
209         taskp = &(*taskp)->next;
210     *taskp = xmalloc (sizeof(**taskp));
211     (*taskp)->running = 0;
212     (*taskp)->which = which;
213     (*taskp)->u.resultset = 0;  /* one null pointer there at least */
214     (*taskp)->next = 0;
215     clear_error (c);
216     return *taskp;
217 }
218
219 void Z3950_connection_remove_task (Z3950_connection c)
220 {
221     Z3950_task task = c->tasks;
222
223     if (task)
224     {
225         c->tasks = task->next;
226         switch (task->which)
227         {
228         case Z3950_TASK_SEARCH:
229             Z3950_resultset_destroy (task->u.resultset);
230             break;
231         case Z3950_TASK_RETRIEVE:
232             Z3950_resultset_destroy (task->u.resultset);
233             break;
234         default:
235             assert (0);
236         }
237         xfree (task);
238     }
239 }
240
241 void Z3950_connection_remove_tasks (Z3950_connection c)
242 {
243     while (c->tasks)
244         Z3950_connection_remove_task(c);
245 }
246
247 void Z3950_connection_destroy(Z3950_connection c)
248 {
249     Z3950_resultset r;
250     if (!c)
251         return;
252     if (c->cs)
253         cs_close (c->cs);
254     for (r = c->resultsets; r; r = r->next)
255         r->connection = 0;
256
257     xfree (c->buf_in);
258     xfree (c->addinfo);
259     odr_destroy (c->odr_in);
260     odr_destroy (c->odr_out);
261     Z3950_options_destroy (c->options);
262     Z3950_connection_remove_tasks (c);
263     xfree (c->host_port);
264     xfree (c);
265 }
266
267 void Z3950_resultset_addref (Z3950_resultset r)
268 {
269     if (r)
270         (r->refcount)++;
271 }
272 Z3950_resultset Z3950_resultset_create ()
273 {
274     Z3950_resultset r = xmalloc (sizeof(*r));
275
276     r->refcount = 1;
277     r->size = 0;
278     r->odr = odr_createmem (ODR_ENCODE);
279     r->start = 0;
280     r->piggyback = 1;
281     r->count = 0;
282     r->record_cache = 0;
283     r->r_sort_spec = 0;
284     r->r_query = 0;
285     r->search = 0;
286     r->connection = 0;
287     r->next = 0;
288     return r;
289 }
290
291 Z3950_resultset Z3950_connection_search_pqf(Z3950_connection c, const char *q)
292 {
293     Z3950_resultset r;
294     Z3950_query s = Z3950_query_create();
295
296     Z3950_query_prefix (s, q);
297
298     r = Z3950_connection_search (c, s);
299     Z3950_query_destroy (s);
300     return r;
301 }
302
303 Z3950_resultset Z3950_connection_search(Z3950_connection c, Z3950_query q)
304 {
305     Z3950_resultset r = Z3950_resultset_create ();
306     Z3950_task task;
307
308     r->r_sort_spec = q->sort_spec;
309     r->r_query = q->query;
310     r->search = q;
311
312     r->options = Z3950_options_create_with_parent(c->options);
313
314     r->start = Z3950_options_get_int(r->options, "start", 0);
315     r->count = Z3950_options_get_int(r->options, "count", 0);
316     r->piggyback = Z3950_options_get_bool (r->options, "piggyback", 1);
317     r->connection = c;
318
319     r->next = c->resultsets;
320     c->resultsets = r;
321
322     task = Z3950_connection_add_task (c, Z3950_TASK_SEARCH);
323     task->u.resultset = r;
324     Z3950_resultset_addref (r);  
325
326     (q->refcount)++;
327
328     if (!c->async)
329     {
330         while (Z3950_event (1, &c))
331             ;
332     }
333     return r;
334 }
335
336 void Z3950_resultset_destroy(Z3950_resultset r)
337 {
338     if (!r)
339         return;
340     (r->refcount)--;
341     yaz_log (LOG_DEBUG, "destroy r = %p count=%d", r, r->refcount);
342     if (r->refcount == 0)
343     {
344         if (r->connection)
345         {
346             /* remove ourselves from the resultsets in connection */
347             Z3950_resultset *rp = &r->connection->resultsets;
348             while (1)
349             {
350                 assert (*rp);   /* we must be in this list!! */
351                 if (*rp == r)
352                 {   /* OK, we're here - take us out of it */
353                     *rp = (*rp)->next;
354                     break;
355                 }
356                 rp = &(*rp)->next;
357             }
358         }
359         Z3950_query_destroy (r->search);
360         Z3950_options_destroy (r->options);
361         odr_destroy (r->odr);
362         xfree (r);
363     }
364 }
365
366 int Z3950_resultset_size (Z3950_resultset r)
367 {
368     return r->size;
369 }
370
371 static void do_close (Z3950_connection c)
372 {
373     if (c->cs)
374         cs_close(c->cs);
375     c->cs = 0;
376     c->mask = 0;
377     c->state = STATE_IDLE;
378 }
379
380 static void Z3950_resultset_retrieve (Z3950_resultset r,
381                                       int force_sync, int start, int count)
382 {
383     Z3950_task task;
384     Z3950_connection c;
385
386     if (!r)
387         return;
388     c = r->connection;
389     if (!c)
390         return;
391     task = Z3950_connection_add_task (c, Z3950_TASK_RETRIEVE);
392     task->u.resultset = r;
393     Z3950_resultset_addref (r);
394
395     r->start = start;
396     r->count = count;
397
398     if (!r->connection->async || force_sync)
399         while (r->connection && Z3950_event (1, &r->connection))
400             ;
401 }
402
403 void Z3950_resultset_records (Z3950_resultset r, Z3950_record *recs,
404                               size_t start, size_t count)
405 {
406     int force_present = 0;
407
408     if (!r)
409         return ;
410     if (count && recs)
411         force_present = 1;
412     Z3950_resultset_retrieve (r, force_present, start, count);
413     if (force_present)
414     {
415         size_t i;
416         for (i = 0; i< count; i++)
417             recs[i] = Z3950_resultset_record_immediate (r, i+start);
418     }
419 }
420
421 static void do_connect (Z3950_connection c)
422 {
423     void *add;
424     const char *effective_host;
425
426     if (c->proxy)
427         effective_host = c->proxy;
428     else
429         effective_host = c->host_port;
430
431     yaz_log (LOG_DEBUG, "do_connect host=%s", effective_host);
432
433     assert (!c->cs);
434     c->cs = cs_create_host (effective_host, 0, &add);
435
436     if (c->cs)
437     {
438         int ret = cs_connect (c->cs, add);
439         yaz_log (LOG_DEBUG, "cs_connect returned %d", ret);
440         if (ret >= 0)
441         {
442             c->state = STATE_CONNECTING; 
443             c->mask = Z3950_SELECT_READ | Z3950_SELECT_WRITE | 
444                 Z3950_SELECT_EXCEPT;
445             return;
446         }
447     }
448     c->event_pending = 1;
449     c->state = STATE_IDLE;
450     c->error = Z3950_ERROR_CONNECT;
451 }
452
453 int z3950_connection_socket(Z3950_connection c)
454 {
455     if (c->cs)
456         return cs_fileno(c->cs);
457     return -1;
458 }
459
460 int z3950_connection_mask(Z3950_connection c)
461 {
462     if (c->cs)
463         return c->mask;
464     return 0;
465 }
466
467 static int encode_APDU(Z3950_connection c, Z_APDU *a, ODR out)
468 {
469     char str[120];
470
471     assert (a);
472     sprintf (str, "send_APDU t=%p type=%d", c, a->which);
473     if (c->cookie_out)
474     {
475         Z_OtherInformation **oi;
476         yaz_oi_APDU(a, &oi);
477         yaz_oi_set_string_oidval(oi, out, VAL_COOKIE, 1, c->cookie_out);
478     }
479     if (!z_APDU(out, &a, 0, 0))
480     {
481         FILE *outf = fopen("/tmp/apdu.txt", "w");
482         if (outf)
483         {
484             ODR odr_pr = odr_createmem(ODR_PRINT);
485             fprintf (outf, "a=%p\n", a);
486             odr_setprint(odr_pr, outf);
487             z_APDU(odr_pr, &a, 0, 0);
488             odr_destroy(odr_pr);
489             fclose (outf);
490         }
491         c->error = Z3950_ERROR_ENCODE;
492         do_close (c);
493         return -1;
494     }
495     return 0;
496 }
497
498 static int send_APDU (Z3950_connection c, Z_APDU *a)
499 {
500     assert (a);
501     if (encode_APDU(c, a, c->odr_out))
502         return -1;
503     c->buf_out = odr_getbuf(c->odr_out, &c->len_out, 0);
504     odr_reset(c->odr_out);
505     do_write (c);
506     return 0;   
507 }
508
509 static int Z3950_connection_send_init (Z3950_connection c)
510 {
511     const char *impname;
512     Z_APDU *apdu = zget_APDU(c->odr_out, Z_APDU_initRequest);
513     Z_InitRequest *ireq = apdu->u.initRequest;
514     Z_IdAuthentication *auth = odr_malloc(c->odr_out, sizeof(*auth));
515     const char *auth_groupId = Z3950_options_get (c->options, "group");
516     const char *auth_userId = Z3950_options_get (c->options, "user");
517     const char *auth_password = Z3950_options_get (c->options, "pass");
518     
519     ODR_MASK_SET(ireq->options, Z_Options_search);
520     ODR_MASK_SET(ireq->options, Z_Options_present);
521     ODR_MASK_SET(ireq->options, Z_Options_scan);
522     ODR_MASK_SET(ireq->options, Z_Options_sort);
523 #if 0
524     ODR_MASK_SET(ireq->options, Z_Options_extendedServices);
525     ODR_MASK_SET(ireq->options, Z_Options_namedResultSets);
526 #endif
527     
528     ODR_MASK_SET(ireq->protocolVersion, Z_ProtocolVersion_1);
529     ODR_MASK_SET(ireq->protocolVersion, Z_ProtocolVersion_2);
530     ODR_MASK_SET(ireq->protocolVersion, Z_ProtocolVersion_3);
531     
532     impname = Z3950_options_get (c->options, "implementationName");
533     ireq->implementationName =
534         odr_malloc (c->odr_out, 15 + (impname ? strlen(impname) : 0));
535     strcpy (ireq->implementationName, "");
536     if (impname)
537     {
538         strcat (ireq->implementationName, impname);
539         strcat (ireq->implementationName, "/");
540     }                                          
541     strcat (ireq->implementationName, "ZOOM-C/YAZ");
542     
543     *ireq->maximumRecordSize =
544         Z3950_options_get_int (c->options, "maximumRecordSize", 1024*1024);
545     *ireq->preferredMessageSize =
546         Z3950_options_get_int (c->options, "preferredMessageSize", 1024*1024);
547     
548     if (auth_groupId || auth_password)
549     {
550         Z_IdPass *pass = odr_malloc(c->odr_out, sizeof(*pass));
551         int i = 0;
552         pass->groupId = 0;
553         if (auth_groupId && *auth_groupId)
554         {
555             pass->groupId = odr_malloc(c->odr_out, strlen(auth_groupId)+1);
556             strcpy(pass->groupId, auth_groupId);
557             i++;
558         }
559         pass->userId = 0;
560         if (auth_userId && *auth_userId)
561         {
562             pass->userId = odr_malloc(c->odr_out, strlen(auth_userId)+1);
563             strcpy(pass->userId, auth_userId);
564             i++;
565         }
566         pass->password = 0;
567         if (auth_password && *auth_password)
568         {
569             pass->password = odr_malloc(c->odr_out, strlen(auth_password)+1);
570             strcpy(pass->password, auth_password);
571             i++;
572         }
573         if (i)
574         {
575             auth->which = Z_IdAuthentication_idPass;
576             auth->u.idPass = pass;
577             ireq->idAuthentication = auth;
578         }
579     }
580     else if (auth_userId)
581     {
582         auth->which = Z_IdAuthentication_open;
583         auth->u.open = odr_malloc(c->odr_out, strlen(auth_userId)+1);
584         strcpy(auth->u.open, auth_userId);
585         ireq->idAuthentication = auth;
586     }
587     if (c->proxy)
588         yaz_oi_set_string_oidval(&ireq->otherInfo, c->odr_out,
589                                  VAL_PROXY, 1, c->host_port);
590     assert (apdu);
591     send_APDU (c, apdu);
592     
593     return 0;
594 }
595
596 static int Z3950_connection_send_search (Z3950_connection c)
597 {
598     Z3950_resultset r;
599     int lslb, ssub, mspn;
600     const char *syntax;
601     Z_APDU *apdu = zget_APDU(c->odr_out, Z_APDU_searchRequest);
602     Z_SearchRequest *search_req = apdu->u.searchRequest;
603     const char *elementSetName;
604     const char *smallSetElementSetName;
605     const char *mediumSetElementSetName;
606
607     assert (c->tasks);
608     assert (c->tasks->which == Z3950_TASK_SEARCH);
609
610     r = c->tasks->u.resultset;
611
612     elementSetName =
613         Z3950_options_get (r->options, "elementSetName");
614     smallSetElementSetName  =
615         Z3950_options_get (r->options, "smallSetElementSetName");
616     mediumSetElementSetName =
617         Z3950_options_get (r->options, "mediumSetElementSetName");
618
619     if (!smallSetElementSetName)
620         smallSetElementSetName = elementSetName;
621
622     if (!mediumSetElementSetName)
623         mediumSetElementSetName = elementSetName;
624
625     assert (r);
626     assert (r->r_query);
627
628     /* prepare query for the search request */
629     search_req->query = r->r_query;
630
631     search_req->databaseNames =
632         set_DatabaseNames (c, &search_req->num_databaseNames);
633
634     /* get syntax (no need to provide unless piggyback is in effect) */
635     syntax = Z3950_options_get (r->options, "preferredRecordSyntax");
636
637     lslb = Z3950_options_get_int (r->options, "largeSetLowerBound", -1);
638     ssub = Z3950_options_get_int (r->options, "smallSetUpperBound", -1);
639     mspn = Z3950_options_get_int (r->options, "mediumSetPresentNumber", -1);
640     if (lslb != -1 && ssub != -1 && mspn != -1)
641     {
642         /* So're a Z39.50 expert? Let's hope you don't do sort */
643         *search_req->largeSetLowerBound = lslb;
644         *search_req->smallSetUpperBound = ssub;
645         *search_req->mediumSetPresentNumber = mspn;
646     }
647     else if (r->start == 0 && r->count > 0
648              && r->piggyback && !r->r_sort_spec)
649     {
650         /* Regular piggyback - do it unless we're going to do sort */
651         *search_req->largeSetLowerBound = 2000000000;
652         *search_req->smallSetUpperBound = r->count;
653         *search_req->mediumSetPresentNumber = r->count;
654         smallSetElementSetName = 0;  /* no need to provide this */
655     }
656     else
657     {
658         /* non-piggyback. Need not provide elementsets or syntaxes .. */
659         smallSetElementSetName = 0;
660         mediumSetElementSetName = 0;
661         syntax = 0;
662     }
663     if (smallSetElementSetName && *smallSetElementSetName)
664     {
665         Z_ElementSetNames *esn = odr_malloc (c->odr_out, sizeof(*esn));
666         
667         esn->which = Z_ElementSetNames_generic;
668         esn->u.generic = odr_strdup (c->odr_out, smallSetElementSetName);
669         search_req->smallSetElementSetNames = esn;
670     }
671     if (mediumSetElementSetName && *mediumSetElementSetName)
672     {
673         Z_ElementSetNames *esn = odr_malloc (c->odr_out, sizeof(*esn));
674         
675         esn->which = Z_ElementSetNames_generic;
676         esn->u.generic = odr_strdup (c->odr_out, mediumSetElementSetName);
677         search_req->mediumSetElementSetNames = esn;
678     }
679     if (syntax)
680         search_req->preferredRecordSyntax =
681             yaz_str_to_z3950oid (c->odr_out, CLASS_RECSYN, syntax);
682
683     /* send search request */
684     send_APDU (c, apdu);
685     r->r_query = 0;
686     return 1;
687 }
688
689 static void response_diag (Z3950_connection c, Z_DiagRec *p)
690 {
691     Z_DefaultDiagFormat *r;
692     char *addinfo = 0;
693     
694     xfree (c->addinfo);
695     c->addinfo = 0;
696     if (p->which != Z_DiagRec_defaultFormat)
697     {
698         c->error = Z3950_ERROR_DECODE;
699         return;
700     }
701     r = p->u.defaultFormat;
702     switch (r->which)
703     {
704     case Z_DefaultDiagFormat_v2Addinfo:
705         addinfo = r->u.v2Addinfo;
706         break;
707     case Z_DefaultDiagFormat_v3Addinfo:
708         addinfo = r->u.v3Addinfo;
709         break;
710     }
711     if (addinfo)
712         c->addinfo = xstrdup (addinfo);
713     c->error = *r->condition;
714 }
715
716 Z3950_record Z3950_record_dup (Z3950_record srec)
717 {
718     char *buf;
719     int size;
720     ODR odr_enc;
721     Z3950_record nrec;
722
723     odr_enc = odr_createmem(ODR_ENCODE);
724     if (!z_NamePlusRecord (odr_enc, &srec->npr, 0, 0))
725         return 0;
726     buf = odr_getbuf (odr_enc, &size, 0);
727     
728     nrec = xmalloc (sizeof(*nrec));
729     nrec->odr = odr_createmem(ODR_DECODE);
730     nrec->wrbuf_marc = 0;
731     odr_setbuf (nrec->odr, buf, size, 0);
732     z_NamePlusRecord (nrec->odr, &nrec->npr, 0, 0);
733     
734     odr_destroy (odr_enc);
735     return nrec;
736 }
737
738 Z3950_record Z3950_resultset_record_immediate (Z3950_resultset s,size_t pos)
739 {
740     Z3950_record rec = record_cache_lookup (s, pos, 0);
741     if (!rec)
742         return 0;
743     return Z3950_record_dup (rec);
744 }
745
746 Z3950_record Z3950_resultset_record (Z3950_resultset r, size_t pos)
747 {
748     Z3950_resultset_retrieve (r, 1, pos, 1);
749     return Z3950_resultset_record_immediate (r, pos);
750 }
751
752 void Z3950_record_destroy (Z3950_record rec)
753 {
754     if (!rec)
755         return;
756     if (rec->wrbuf_marc)
757         wrbuf_free (rec->wrbuf_marc, 1);
758     odr_destroy (rec->odr);
759     xfree (rec);
760 }
761
762 void *Z3950_record_get (Z3950_record rec, const char *type, size_t *len)
763 {
764     Z_NamePlusRecord *npr;
765     if (!rec)
766         return 0;
767     npr = rec->npr;
768     if (!npr)
769         return 0;
770     if (!strcmp (type, "database"))
771     {
772         return npr->databaseName;
773     }
774     else if (!strcmp (type, "syntax"))
775     {
776         if (npr->which == Z_NamePlusRecord_databaseRecord)
777         {
778             Z_External *r = (Z_External *) npr->u.databaseRecord;
779             oident *ent = oid_getentbyoid(r->direct_reference);
780             if (ent)
781                 return ent->desc;
782         }
783         return "none";
784     }
785     else if (!strcmp (type, "render"))
786     {
787         if (npr->which == Z_NamePlusRecord_databaseRecord)
788         {
789             Z_External *r = (Z_External *) npr->u.databaseRecord;
790             oident *ent = oid_getentbyoid(r->direct_reference);
791             
792             if (r->which == Z_External_sutrs)
793             {
794                 *len = r->u.sutrs->len;
795                 return r->u.sutrs->buf;
796             }
797             else if (r->which == Z_External_octet)
798             {
799                 switch (ent->value)
800                 {
801                 case VAL_SOIF:
802                 case VAL_HTML:
803                 case VAL_SUTRS:
804                     break;
805                 case VAL_TEXT_XML:
806                 case VAL_APPLICATION_XML:
807                     break;
808                 default:
809                     if (!rec->wrbuf_marc)
810                         rec->wrbuf_marc = wrbuf_alloc();
811                     if (marc_display_wrbuf (r->u.octet_aligned->buf,
812                                         rec->wrbuf_marc, 0,
813                                             r->u.octet_aligned->len) > 0)
814                     {
815                         *len = wrbuf_len(rec->wrbuf_marc);
816                         return wrbuf_buf(rec->wrbuf_marc);
817                     }
818                 }
819                 *len = r->u.octet_aligned->len;
820                 return r->u.octet_aligned->buf;
821             }
822             else if (r->which == Z_External_grs1)
823             {
824                 *len = 5;
825                 return "GRS-1";
826             }
827         }
828         return 0;
829     }
830     else if (!strcmp (type, "raw"))
831     {
832         if (npr->which == Z_NamePlusRecord_databaseRecord)
833         {
834             *len = -1;
835             return (Z_External *) npr->u.databaseRecord;
836         }
837         return 0;
838     }
839     return 0;
840 }
841
842 void *Z3950_resultset_get (Z3950_resultset s, size_t pos, const char *type,
843                            size_t *len)
844 {
845     Z3950_record rec = record_cache_lookup (s, pos, 0);
846     return Z3950_record_get (rec, type, len);
847 }
848
849 static void record_cache_add (Z3950_resultset r,
850                               Z_NamePlusRecord *npr,
851                               int pos,
852                               const char *elementSetName)
853 {
854     Z3950_record_cache rc;
855
856     for (rc = r->record_cache; rc; rc = rc->next)
857     {
858         if (pos == rc->pos)
859         {
860             if ((!elementSetName && !rc->elementSetName)
861                 || (elementSetName && rc->elementSetName &&
862                     !strcmp (elementSetName, rc->elementSetName)))
863             {
864                 /* not destroying rc->npr (it's handled by nmem )*/
865                 rc->rec.npr = npr;
866                 /* keeping wrbuf_marc too */
867                 return;
868             }
869         }
870
871     }
872     rc = odr_malloc (r->odr, sizeof(*rc));
873     rc->rec.npr = npr; 
874     rc->rec.odr = 0;
875     rc->rec.wrbuf_marc = 0;
876     if (elementSetName)
877         rc->elementSetName = odr_strdup (r->odr, elementSetName);
878     else
879         rc->elementSetName = 0;
880     rc->pos = pos;
881     rc->next = r->record_cache;
882     r->record_cache = rc;
883 }
884
885 static Z3950_record record_cache_lookup (Z3950_resultset r,
886                                          int pos,
887                                          const char *elementSetName)
888 {
889     Z3950_record_cache rc;
890
891     for (rc = r->record_cache; rc; rc = rc->next)
892     {
893         if (pos == rc->pos)
894         {
895             if ((!elementSetName && !rc->elementSetName)
896                 || (elementSetName && rc->elementSetName &&
897                     !strcmp (elementSetName, rc->elementSetName)))
898                 return &rc->rec;
899         }
900     }
901     return 0;
902 }
903                                              
904 static void handle_records (Z3950_connection c, Z_Records *sr,
905                             int present_phase)
906 {
907     Z3950_resultset resultset;
908
909     if (!c->tasks)
910         return ;
911     if (c->tasks->which != Z3950_TASK_SEARCH &&
912         c->tasks->which != Z3950_TASK_RETRIEVE)
913         return ;
914     
915     resultset = c->tasks->u.resultset;
916
917     if (sr && sr->which == Z_Records_NSD)
918     {
919         Z_DiagRec dr, *dr_p = &dr;
920         dr.which = Z_DiagRec_defaultFormat;
921         dr.u.defaultFormat = sr->u.nonSurrogateDiagnostic;
922         
923         response_diag (c, dr_p);
924     }
925     else if (sr && sr->which == Z_Records_multipleNSD)
926     {
927         if (sr->u.multipleNonSurDiagnostics->num_diagRecs >= 1)
928             response_diag(c, sr->u.multipleNonSurDiagnostics->diagRecs[0]);
929         else
930             c->error = Z3950_ERROR_DECODE;
931     }
932     else 
933     {
934         if (resultset->count + resultset->start > resultset->size)
935             resultset->count = resultset->size - resultset->start;
936         if (resultset->count < 0)
937             resultset->count = 0;
938         if (sr && sr->which == Z_Records_DBOSD)
939         {
940             int i;
941             NMEM nmem = odr_extract_mem (c->odr_in);
942             Z_NamePlusRecordList *p =
943                 sr->u.databaseOrSurDiagnostics;
944             for (i = 0; i<p->num_records; i++)
945             {
946                 record_cache_add (resultset, p->records[i],
947                                   i+ resultset->start, 0);
948             }
949             /* transfer our response to search_nmem .. we need it later */
950             nmem_transfer (resultset->odr->mem, nmem);
951             nmem_destroy (nmem);
952             if (present_phase && p->num_records == 0)
953             {
954                 /* present response and we didn't get any records! */
955                 c->error = Z3950_ERROR_DECODE;
956             }
957         }
958         else if (present_phase)
959         {
960             /* present response and we didn't get any records! */
961             c->error = Z3950_ERROR_DECODE;
962         }
963     }
964 }
965
966 static void handle_present_response (Z3950_connection c, Z_PresentResponse *pr)
967 {
968     handle_records (c, pr->records, 1);
969 }
970
971 static void handle_search_response (Z3950_connection c, Z_SearchResponse *sr)
972 {
973     Z3950_resultset resultset;
974
975     yaz_log (LOG_DEBUG, "got search response");
976
977     if (!c->tasks || c->tasks->which != Z3950_TASK_SEARCH)
978         return ;
979
980     resultset = c->tasks->u.resultset;
981
982     resultset->size = *sr->resultCount;
983     handle_records (c, sr->records, 0);
984 }
985
986 static void sort_response (Z3950_connection c, Z_SortResponse *res)
987 {
988     if (res->diagnostics && res->num_diagnostics > 0)
989         response_diag (c, res->diagnostics[0]);
990 }
991
992 static int send_sort (Z3950_connection c)
993 {
994     Z3950_resultset  resultset;
995
996     if (!c->tasks || c->tasks->which != Z3950_TASK_SEARCH)
997         return 0;
998
999     resultset = c->tasks->u.resultset;
1000
1001     if (c->error)
1002     {
1003         resultset->r_sort_spec = 0;
1004         return 0;
1005     }
1006     if (resultset->r_sort_spec)
1007     {
1008         Z_APDU *apdu = zget_APDU(c->odr_out, Z_APDU_sortRequest);
1009         Z_SortRequest *req = apdu->u.sortRequest;
1010         
1011         req->num_inputResultSetNames = 1;
1012         req->inputResultSetNames = (Z_InternationalString **)
1013             odr_malloc (c->odr_out, sizeof(*req->inputResultSetNames));
1014         req->inputResultSetNames[0] = odr_strdup (c->odr_out, "default");
1015         req->sortedResultSetName = odr_strdup (c->odr_out, "default");
1016         req->sortSequence = resultset->r_sort_spec;
1017         resultset->r_sort_spec = 0;
1018         send_APDU (c, apdu);
1019         return 1;
1020     }
1021     return 0;
1022 }
1023
1024 static int send_present (Z3950_connection c)
1025 {
1026     Z_APDU *apdu = zget_APDU(c->odr_out, Z_APDU_presentRequest);
1027     Z_PresentRequest *req = apdu->u.presentRequest;
1028     int i = 0;
1029     const char *syntax = 
1030         Z3950_options_get (c->options, "preferredRecordSyntax");
1031     const char *element =
1032         Z3950_options_get (c->options, "elementSetName");
1033     Z3950_resultset  resultset;
1034
1035     if (!c->tasks)
1036         return 0;
1037     if (c->tasks->which != Z3950_TASK_SEARCH && 
1038         c->tasks->which != Z3950_TASK_RETRIEVE)
1039         return 0;
1040
1041     resultset = c->tasks->u.resultset;
1042     
1043     if (c->error)                  /* don't continue on error */
1044         return 0;
1045     if (resultset->start < 0)
1046         return 0;
1047     for (i = 0; i<resultset->count; i++)
1048     {
1049         Z3950_record rec =
1050             record_cache_lookup (resultset, i + resultset->start, 0);
1051         if (!rec)
1052             break;
1053     }
1054     if (i == resultset->count)
1055         return 0;
1056
1057     resultset->start += i;
1058     resultset->count -= i;
1059     *req->resultSetStartPoint = resultset->start + 1;
1060     *req->numberOfRecordsRequested = resultset->count;
1061     assert (*req->numberOfRecordsRequested > 0);
1062
1063     if (syntax && *syntax)
1064         req->preferredRecordSyntax =
1065             yaz_str_to_z3950oid (c->odr_out, CLASS_RECSYN, syntax);
1066
1067     if (element && *element)
1068     {
1069         Z_ElementSetNames *esn = odr_malloc (c->odr_out, sizeof(*esn));
1070         Z_RecordComposition *compo = odr_malloc (c->odr_out, sizeof(*compo));
1071         
1072         esn->which = Z_ElementSetNames_generic;
1073         esn->u.generic = odr_strdup (c->odr_out, element);
1074         compo->which = Z_RecordComp_simple;
1075         compo->u.simple = esn;
1076         req->recordComposition = compo;
1077     }
1078     send_APDU (c, apdu);
1079     return 1;
1080 }
1081
1082 static int Z3950_connection_exec_task (Z3950_connection c)
1083 {
1084     Z3950_task task = c->tasks;
1085
1086     yaz_log (LOG_DEBUG, "Z3950_connection_exec_task");
1087     if (!task)
1088         return 0;
1089     if (c->error != Z3950_ERROR_NONE || !c->cs)
1090     {
1091         Z3950_connection_remove_tasks (c);
1092         return 0;
1093     }
1094     yaz_log (LOG_DEBUG, "Z3950_connection_exec_task type=%d", task->which);
1095     if (task->running)
1096         return 0;
1097     task->running = 1;
1098     switch (task->which)
1099     {
1100     case Z3950_TASK_SEARCH:
1101         /* see if search hasn't been sent yet. */
1102         if (Z3950_connection_send_search (c))
1103             return 1;
1104         break;
1105     case Z3950_TASK_RETRIEVE:
1106         if (send_present (c))
1107             return 1;
1108         break;
1109     }
1110     Z3950_connection_remove_task (c);
1111     return 0;
1112 }
1113
1114 static int send_sort_present (Z3950_connection c)
1115 {
1116     int r = send_sort (c);
1117     if (!r)
1118         r = send_present (c);
1119     return r;
1120 }
1121
1122 static void handle_apdu (Z3950_connection c, Z_APDU *apdu)
1123 {
1124     Z_InitResponse *initrs;
1125     
1126     yaz_log (LOG_DEBUG, "hande_apdu type=%d", apdu->which);
1127     c->mask = 0;
1128     switch(apdu->which)
1129     {
1130     case Z_APDU_initResponse:
1131         initrs = apdu->u.initResponse;
1132         if (!*initrs->result)
1133         {
1134             c->error = Z3950_ERROR_INIT;
1135         }
1136         else
1137         {
1138             char *cookie =
1139                 yaz_oi_get_string_oidval (&apdu->u.initResponse->otherInfo,
1140                                           VAL_COOKIE, 1, 0);
1141             xfree (c->cookie_in);
1142             c->cookie_in = 0;
1143             if (cookie)
1144                 c->cookie_in = xstrdup(cookie);
1145             Z3950_connection_exec_task (c);
1146         }
1147         break;
1148     case Z_APDU_searchResponse:
1149         handle_search_response (c, apdu->u.searchResponse);
1150         if (!send_sort_present (c))
1151             Z3950_connection_remove_task (c);
1152         break;
1153     case Z_APDU_presentResponse:
1154         handle_present_response (c, apdu->u.presentResponse);
1155         if (!send_present (c))
1156             Z3950_connection_remove_task (c);
1157         break;
1158     case Z_APDU_sortResponse:
1159         sort_response (c, apdu->u.sortResponse);
1160         if (!send_present (c))
1161             Z3950_connection_remove_task (c);
1162     }
1163 }
1164
1165 static int do_read (Z3950_connection c)
1166 {
1167     int r;
1168     Z_APDU *apdu;
1169     
1170     r = cs_get (c->cs, &c->buf_in, &c->len_in);
1171     if (r == 1)
1172         return 0;
1173     if (r <= 0)
1174     {
1175         c->error= Z3950_ERROR_CONNECTION_LOST;
1176         do_close (c);
1177     }
1178     else
1179     {
1180         odr_reset (c->odr_in);
1181         odr_setbuf (c->odr_in, c->buf_in, r, 0);
1182         if (!z_APDU (c->odr_in, &apdu, 0, 0))
1183         {
1184             c->error = Z3950_ERROR_DECODE;
1185             do_close (c);
1186         }
1187         else
1188         {
1189             handle_apdu (c, apdu);
1190         }
1191     }
1192     return 1;
1193 }
1194
1195 static int do_write_ex (Z3950_connection c, char *buf_out, int len_out)
1196 {
1197     int r;
1198     
1199     if ((r=cs_put (c->cs, buf_out, len_out)) < 0)
1200     {
1201         if (c->state == STATE_CONNECTING)
1202             c->error = Z3950_ERROR_CONNECT;
1203         else
1204             c->error = Z3950_ERROR_CONNECTION_LOST;
1205         do_close (c);
1206         return 1;
1207     }
1208     else if (r == 1)
1209     {
1210         c->state = STATE_ESTABLISHED;
1211         c->mask = Z3950_SELECT_READ|Z3950_SELECT_WRITE|Z3950_SELECT_EXCEPT;
1212     }
1213     else
1214     {
1215         c->state = STATE_ESTABLISHED;
1216         c->mask = Z3950_SELECT_READ|Z3950_SELECT_EXCEPT;
1217     }
1218     return 0;
1219 }
1220
1221 static int do_write(Z3950_connection c)
1222 {
1223     return do_write_ex (c, c->buf_out, c->len_out);
1224 }
1225
1226 const char *Z3950_connection_option (Z3950_connection c, const char *key,
1227                                      const char *val)
1228 {
1229     const char *old_val = Z3950_options_get (c->options, key);
1230     if (val)
1231     {
1232         Z3950_options_set (c->options, key, val);
1233     }
1234     return old_val;
1235 }
1236
1237 const char *Z3950_resultset_option (Z3950_resultset r, const char *key,
1238                                     const char *val)
1239 {
1240     const char *old_val = Z3950_options_get (r->options, key);
1241     if (val)
1242     {
1243         Z3950_options_set (r->options, key, val);
1244     }
1245     return old_val;
1246 }
1247
1248
1249 int Z3950_connection_errcode (Z3950_connection c)
1250 {
1251     return Z3950_connection_error (c, 0, 0);
1252 }
1253
1254 const char *Z3950_connection_errmsg (Z3950_connection c)
1255 {
1256     const char *msg;
1257     Z3950_connection_error (c, &msg, 0);
1258     return msg;
1259 }
1260
1261 const char *Z3950_connection_addinfo (Z3950_connection c)
1262 {
1263     const char *addinfo;
1264     Z3950_connection_error (c, 0, &addinfo);
1265     return addinfo;
1266 }
1267
1268 int Z3950_connection_error (Z3950_connection c, const char **cp,
1269                             const char **addinfo)
1270 {
1271     int error = c->error;
1272     if (cp)
1273     {
1274         switch (error)
1275         {
1276         case Z3950_ERROR_NONE:
1277             *cp = "No error"; break;
1278         case Z3950_ERROR_CONNECT:
1279             *cp = "Connect failed"; break;
1280         case Z3950_ERROR_MEMORY:
1281             *cp = "Out of memory"; break;
1282         case Z3950_ERROR_ENCODE:
1283             *cp = "Encoding failed"; break;
1284         case Z3950_ERROR_DECODE:
1285             *cp = "Decoding failed"; break;
1286         case Z3950_ERROR_CONNECTION_LOST:
1287             *cp = "Connection lost"; break;
1288         case Z3950_ERROR_INIT:
1289             *cp = "Init rejected"; break;
1290         case Z3950_ERROR_INTERNAL:
1291             *cp = "Internal failure"; break;
1292         case Z3950_ERROR_TIMEOUT:
1293             *cp = "Timeout"; break;
1294         default:
1295             *cp = diagbib1_str (error);
1296         }
1297     }
1298     if (addinfo)
1299     {
1300         if (c->addinfo)
1301             *addinfo = c->addinfo;
1302         else
1303             *addinfo = "";
1304     }
1305     return c->error;
1306 }
1307
1308 int Z3950_connection_do_io(Z3950_connection c, int mask)
1309 {
1310 #if 0
1311     int r = cs_look(c->cs);
1312     yaz_log (LOG_LOG, "Z3950_connection_do_io c=%p mask=%d cs_look=%d",
1313              c, mask, r);
1314     
1315     if (r == CS_NONE)
1316     {
1317         c->error = Z3950_ERROR_CONNECT;
1318         do_close (c);
1319     }
1320     else if (r == CS_CONNECT)
1321     {
1322         yaz_log (LOG_LOG, "calling rcvconnect");
1323         if (cs_rcvconnect (c->cs) < 0)
1324         {
1325             c->error = Z3950_ERROR_CONNECT;
1326             do_close (c);
1327         }
1328         else
1329             Z3950_connection_send_init (c);
1330     }
1331     else
1332     {
1333         if (mask & Z3950_SELECT_READ)
1334             do_read (c);
1335         if (c->cs && (mask & Z3950_SELECT_WRITE))
1336             do_write (c);
1337     }   
1338 #else
1339     yaz_log (LOG_DEBUG, "Z3950_connection_do_io c=%p mask=%d", c, mask);
1340     if (c->state == STATE_CONNECTING)
1341     {
1342         if (mask & Z3950_SELECT_WRITE)
1343             Z3950_connection_send_init (c);
1344         else
1345         {
1346             c->error = Z3950_ERROR_CONNECT;
1347             do_close (c);
1348         }
1349     }
1350     else if (c->state == STATE_ESTABLISHED)
1351     {
1352         if (mask & Z3950_SELECT_READ)
1353             do_read (c);
1354         if (c->cs && (mask & Z3950_SELECT_WRITE))
1355             do_write (c);
1356     }
1357     else
1358     {
1359         c->error = Z3950_ERROR_INTERNAL;
1360         do_close (c);
1361     }
1362 #endif
1363     c->event_pending = 1;
1364     return 1;
1365 }
1366
1367 int Z3950_event (int no, Z3950_connection *cs)
1368 {
1369 #if USE_POLL
1370     struct pollfd pollfds[1024];
1371     Z3950_connection poll_cs[1024];
1372 #else
1373     struct timeval tv;
1374     fd_set input, output, except;
1375 #endif
1376     int i, r, nfds;
1377     int max_fd = 0;
1378
1379     for (i = 0; i<no; i++)
1380     {
1381         Z3950_connection c = cs[i];
1382         if (c && c->event_pending)
1383         {
1384             c->event_pending = 0;
1385             return i+1;
1386         }
1387     }
1388
1389 #if USE_POLL
1390
1391 #else
1392     tv.tv_sec = 15;
1393     tv.tv_usec = 0;
1394     
1395     FD_ZERO (&input);
1396     FD_ZERO (&output);
1397     FD_ZERO (&except);
1398 #endif
1399     nfds = 0;
1400     for (i = 0; i<no; i++)
1401     {
1402         Z3950_connection c = cs[i];
1403         int fd, mask;
1404         
1405         if (!c)
1406             continue;
1407         fd = z3950_connection_socket(c);
1408         mask = z3950_connection_mask(c);
1409
1410         if (fd == -1)
1411             continue;
1412         if (max_fd < fd)
1413             max_fd = fd;
1414
1415 #if USE_POLL
1416         if (mask)
1417         {
1418             short poll_events = 0;
1419
1420             if (mask & Z3950_SELECT_READ)
1421                 poll_events += POLLIN;
1422             if (mask & Z3950_SELECT_WRITE)
1423                 poll_events += POLLOUT;
1424             if (mask & Z3950_SELECT_EXCEPT)
1425                 poll_events += POLLERR;
1426             pollfds[nfds].fd = fd;
1427             pollfds[nfds].events = poll_events;
1428             pollfds[nfds].revents = 0;
1429             poll_cs[nfds] = c;
1430             nfds++;
1431         }
1432 #else
1433         if (mask & Z3950_SELECT_READ)
1434         {
1435             FD_SET (fd, &input);
1436             nfds++;
1437         }
1438         if (mask & Z3950_SELECT_WRITE)
1439         {
1440             FD_SET (fd, &output);
1441             nfds++;
1442         }
1443         if (mask & Z3950_SELECT_EXCEPT)
1444         {
1445             FD_SET (fd, &except);
1446             nfds++;
1447         }
1448 #endif
1449     }
1450     if (!nfds)
1451     {
1452         for (i = 0; i<no; i++)
1453         {
1454             Z3950_connection c = cs[i];
1455             if (!c)
1456                 continue;
1457             if (!c->cs && c->host_port && c->error == Z3950_ERROR_NONE)
1458             {
1459                 do_connect (c);
1460                 return i+1;
1461             }
1462             else
1463             {
1464                 if (Z3950_connection_exec_task (c))
1465                     return i+1;
1466             }
1467         }
1468         yaz_log (LOG_DEBUG, "no more events");
1469         return 0;
1470     }
1471 #if USE_POLL
1472     yaz_log (LOG_LOG, "poll start");
1473     r = poll (pollfds, nfds, 15000);
1474     yaz_log (LOG_LOG, "poll stop, returned r=%d", r);
1475     for (i = 0; i<nfds; i++)
1476     {
1477         Z3950_connection c = poll_cs[i];
1478         if (r && c->mask)
1479         {
1480             int mask = 0;
1481             if (pollfds[i].revents & POLLIN)
1482                 mask += Z3950_SELECT_READ;
1483             if (pollfds[i].revents & POLLOUT)
1484                 mask += Z3950_SELECT_WRITE;
1485             if (pollfds[i].revents & POLLERR)
1486                 mask += Z3950_SELECT_EXCEPT;
1487             if (mask)
1488                 Z3950_connection_do_io(c, mask);
1489         }
1490         else if (r == 0 && c->mask)
1491         {
1492             /* timeout and this connection was waiting */
1493             c->error = Z3950_ERROR_TIMEOUT;
1494             c->event_pending = 1;
1495         }
1496     }
1497 #else
1498     yaz_log (LOG_DEBUG, "select start");
1499     r = select (max_fd+1, &input, &output, &except, &tv);
1500     yaz_log (LOG_DEBUG, "select stop, returned r=%d", r);
1501     for (i = 0; i<no; i++)
1502     {
1503         Z3950_connection c = cs[i];
1504         int fd, mask;
1505
1506         if (!c)
1507             continue;
1508         fd = z3950_connection_socket(c);
1509         mask = 0;
1510         if (r && c->mask)
1511         {
1512             /* no timeout and real socket */
1513             if (FD_ISSET(fd, &input))
1514                 mask += Z3950_SELECT_READ;
1515             if (FD_ISSET(fd, &output))
1516                 mask += Z3950_SELECT_WRITE;
1517             if (FD_ISSET(fd, &except))
1518                 mask += Z3950_SELECT_EXCEPT;
1519             if (mask)
1520                 Z3950_connection_do_io(c, mask);
1521         }
1522         if (r == 0 && c->mask)
1523         {
1524             /* timeout and this connection was waiting */
1525             c->error = Z3950_ERROR_TIMEOUT;
1526             c->event_pending = 1;
1527         }
1528     }
1529 #endif
1530
1531     for (i = 0; i<no; i++)
1532     {
1533         Z3950_connection c = cs[i];
1534         if (!c)
1535             continue;
1536         if (c->event_pending)
1537         {
1538             c->event_pending = 0;
1539             return i+1;
1540         }
1541     }
1542     return 0;
1543 }