bc09552ee42f0af7b6a457947747c140e431370e
[egate.git] / zlayer / zaccess-yaz.c
1 /*
2  * Copyright (c) 1995, the EUROPAGATE consortium (see below).
3  *
4  * The EUROPAGATE consortium members are:
5  *
6  *    University College Dublin
7  *    Danmarks Teknologiske Videnscenter
8  *    An Chomhairle Leabharlanna
9  *    Consejo Superior de Investigaciones Cientificas
10  *
11  * Permission to use, copy, modify, distribute, and sell this software and
12  * its documentation, in whole or in part, for any purpose, is hereby granted,
13  * provided that:
14  *
15  * 1. This copyright and permission notice appear in all copies of the
16  * software and its documentation. Notices of copyright or attribution
17  * which appear at the beginning of any file must remain unchanged.
18  *
19  * 2. The names of EUROPAGATE or the project partners may not be used to
20  * endorse or promote products derived from this software without specific
21  * prior written permission.
22  *
23  * 3. Users of this software (implementors and gateway operators) agree to
24  * inform the EUROPAGATE consortium of their use of the software. This
25  * information will be used to evaluate the EUROPAGATE project and the
26  * software, and to plan further developments. The consortium may use
27  * the information in later publications.
28  * 
29  * 4. Users of this software agree to make their best efforts, when
30  * documenting their use of the software, to acknowledge the EUROPAGATE
31  * consortium, and the role played by the software in their work.
32  *
33  * THIS SOFTWARE IS PROVIDED "AS IS" AND WITHOUT WARRANTY OF ANY KIND,
34  * EXPRESS, IMPLIED, OR OTHERWISE, INCLUDING WITHOUT LIMITATION, ANY
35  * WARRANTY OF MERCHANTABILITY OR FITNESS FOR A PARTICULAR PURPOSE.
36  * IN NO EVENT SHALL THE EUROPAGATE CONSORTIUM OR ITS MEMBERS BE LIABLE
37  * FOR ANY SPECIAL, INCIDENTAL, INDIRECT OR CONSEQUENTIAL DAMAGES OF
38  * ANY KIND, OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA
39  * OR PROFITS, WHETHER OR NOT ADVISED OF THE POSSIBILITY OF DAMAGE, AND
40  * ON ANY THEORY OF LIABILITY, ARISING OUT OF OR IN CONNECTION WITH THE
41  * USE OR PERFORMANCE OF THIS SOFTWARE.
42  *
43  */
44 /*
45  * Europagate, 1995
46  *
47  * Z39.50 API for the Email gateway - YAZ version
48  *
49  * $Log: zaccess-yaz.c,v $
50  * Revision 1.10  1995/05/16 09:41:46  adam
51  * LICENSE. Uses new versions of odr_{set,get}buf.
52  *
53  * Revision 1.9  1995/05/03  10:15:43  quinn
54  * Fixed bug in the get_record loop - crashed when diagrec was received.
55  *
56  * Revision 1.8  1995/04/28  14:18:38  quinn
57  * *** empty log message ***
58  *
59  * Revision 1.7  1995/04/21  16:29:32  quinn
60  * Fixed bugs.
61  *
62  * Revision 1.6  1995/04/21  12:58:36  adam
63  * Bug fix.
64  *
65  * Revision 1.5  1995/04/20  15:25:32  quinn
66  * Asynch. API
67  *
68  * Revision 1.4  1995/04/19  16:02:28  adam
69  * Record type hack.
70  *
71  * Revision 1.3  1995/04/19  12:55:15  quinn
72  * Added auth.
73  *
74  * Revision 1.2  1995/04/19  09:24:02  quinn
75  * Fixed bug in zass_open - variable initialized after use
76  *
77  * Revision 1.1  1995/04/17  11:26:53  quinn
78  * Added YAZ version of zaccess
79  *
80  */
81
82 /*
83  * Interface to the YAZ Z39.50 toolkit.
84  */
85
86 #include <stdlib.h>
87 #include <assert.h>
88 #include <ctype.h>
89
90 #include <gw-log.h>
91 #include <proto.h>
92 #include <comstack.h>
93 #include <tcpip.h>
94 #ifdef USE_XTIMOSI
95 #include <xmosi.h>
96 #endif
97 #include <oid.h>
98
99 #include <ccl.h>
100 #include <zaccess.h>
101
102 struct zass    /* Z-assoc */
103 {
104     COMSTACK ass;              /* comstack association handle */
105     ODR encode;
106     ODR decode;
107     int fd;                         /* low-level socket (for select only) */
108     int nonblocking;
109     int maxrecordsize;
110     int preferredmessagesize;
111     char *inbuf;
112     int inbuflen;
113 };
114
115 static Z_APDU *get_apdu(struct zass *z, int *complete)
116 {
117     int res;
118     Z_APDU *ap;
119
120     if ((res = cs_get(z->ass, &z->inbuf, &z->inbuflen)) <= 0)
121     {
122         gw_log(GW_LOG_WARN, ZASS_TYPE, "cs_get failed");
123         if (complete)
124             *complete = 1;
125         return 0;
126     }
127     else if (res == 1)
128     {
129         if (complete)
130             *complete = 0;
131         return 0;
132     }
133     odr_reset(z->decode);
134     odr_setbuf(z->decode, z->inbuf, res, 0);
135     if (!z_APDU(z->decode, &ap, 0))
136     {
137         gw_log(GW_LOG_WARN, ZASS_TYPE, "decode: %s",
138             odr_errlist[odr_geterror(z->decode)]);
139         if (complete)
140             *complete = 0;
141         return 0;
142     }
143     if (complete)
144         *complete = 1;
145     return ap;
146 }
147
148 static int send_apdu(struct zass *z, Z_APDU *a)
149 {
150     char *buf;
151     int len;
152
153     if (!z_APDU(z->encode, &a, 0))
154     {
155         gw_log(GW_LOG_FATAL, ZASS_TYPE, "encoding initreq");
156         return -1;
157     }
158     buf = odr_getbuf(z->encode, &len, NULL);
159     if (cs_put(z->ass, buf, len) < 0)
160     {
161         gw_log(GW_LOG_FATAL, ZASS_TYPE, "cs_put");
162         return -1;
163     }
164     odr_reset(z->encode);  /* release odr_allocated structures */
165     return 0;
166 }
167
168 static int send_initreq(struct zass *p, char *auth)
169 {
170     Z_APDU a;
171     Z_InitRequest init;
172     Odr_bitmask options, protocolVersion;
173     char name[512];
174     Z_IdAuthentication idauth;
175
176     a.which = Z_APDU_initRequest;
177     a.u.initRequest = &init;
178     init.referenceId = 0;
179     init.options = &options;
180     ODR_MASK_ZERO(&options);
181     ODR_MASK_SET(&options, Z_Options_search);
182     ODR_MASK_SET(&options, Z_Options_present);
183     ODR_MASK_SET(&options, Z_Options_delSet);
184     init.protocolVersion = &protocolVersion;
185     ODR_MASK_ZERO(&protocolVersion);
186     ODR_MASK_SET(&protocolVersion, Z_ProtocolVersion_1);
187     ODR_MASK_SET(&protocolVersion, Z_ProtocolVersion_2);
188     init.preferredMessageSize = &p->preferredmessagesize;
189     init.maximumRecordSize = &p->maxrecordsize;
190
191     if (auth)
192     {
193         init.idAuthentication = &idauth;
194         idauth.which = Z_IdAuthentication_open;
195         idauth.u.open = auth;
196     }
197     else
198         init.idAuthentication = 0;
199     init.implementationId = ZASS_ID;
200     sprintf(name, "%s (YAZ protocol layer)", ZASS_NAME);
201     init.implementationName = name;
202     init.implementationVersion = ZASS_VERSION;
203     init.userInformationField = 0;
204     if (send_apdu(p, &a) < 0)
205         return -1;
206     return 0;
207 }
208
209 int zass_fileno(ZASS a)
210 {
211     return a->fd;
212 }
213
214 int zass_openresult(ZASS p, int *complete)
215 {
216     Z_APDU *ap;
217     Z_InitResponse *res;
218
219     if (!(ap = get_apdu(p, complete)))
220         return -1;
221     if (ap->which != Z_APDU_initResponse)
222     {
223         gw_log(GW_LOG_WARN, ZASS_TYPE, "bad APDU");
224         return -1;
225     }
226     res = ap->u.initResponse;
227     if (!*res->result)
228     {
229         gw_log(GW_LOG_WARN, ZASS_TYPE, "Negative result on initres");
230         return -1;
231     }
232     p->preferredmessagesize = *res->preferredMessageSize;
233     p->maxrecordsize = *res->maximumRecordSize;
234     if (res->implementationId)
235         gw_log(GW_LOG_NOTICE, ZASS_TYPE, "imp. ID  : %s",
236             res->implementationId);
237     if (res->implementationName)
238         gw_log(GW_LOG_NOTICE, ZASS_TYPE, "imp. ID  : %s",
239             res->implementationName);
240     if (res->implementationVersion)
241         gw_log(GW_LOG_NOTICE, ZASS_TYPE, "imp. ID  : %s",
242             res->implementationVersion);
243     gw_log(ZASS_DEBUG, ZASS_TYPE, "Initialized ok");
244     return 0;
245 }
246
247 ZASS zass_open(char *host, int port, int *complete, char *auth)
248 {
249     struct zass *p;
250     char addstr[512];
251     void *address;
252
253     if (!(p = malloc(sizeof(*p))))
254     {
255         gw_log(GW_LOG_FATAL|GW_LOG_ERRNO, ZASS_TYPE, "malloc");
256         return 0;
257     }
258     if (complete)
259     {
260         *complete = 1;
261         p->nonblocking = 1;
262     }
263     else
264         p->nonblocking = 0;
265     if (!(p->encode = odr_createmem(ODR_ENCODE)) ||
266         !(p->decode = odr_createmem(ODR_DECODE)))
267     {
268         gw_log(GW_LOG_FATAL|GW_LOG_ERRNO, ZASS_TYPE, "odr_createmem");
269         return 0;
270     }
271     p->maxrecordsize = ZASS_MAXRECORDSIZE;
272     p->preferredmessagesize = ZASS_PREFERREDMESSAGESIZE;
273     if (!(p->ass = cs_create(tcpip_type, 1, CS_Z3950)))
274     {
275         gw_log(GW_LOG_FATAL|GW_LOG_ERRNO, ZASS_TYPE, "cs_create");
276         return 0;
277     }
278     p->inbuf = 0;
279     p->inbuflen = 0;
280     sprintf(addstr, "%s:%d", host, port);
281     if (!(address = tcpip_strtoaddr(addstr)))
282     {
283         gw_log(GW_LOG_FATAL, ZASS_TYPE, "failed to resolve %s", addstr);
284         return 0;
285     }
286     p->fd = cs_fileno(p->ass);
287     gw_log(ZASS_DEBUG, ZASS_TYPE, "Connecting to %s", addstr);
288     if (cs_connect(p->ass, address) < 0)
289     {
290         gw_log(GW_LOG_FATAL, ZASS_TYPE, "Failed to connect to %s", addstr);
291         return 0;
292     }
293     gw_log(ZASS_DEBUG, ZASS_TYPE, "connected ok... initializing");
294     if (send_initreq(p, auth) < 0)
295     {
296         gw_log(GW_LOG_FATAL, ZASS_TYPE, "Failed to send init");
297         return 0;
298     }
299     if (p->nonblocking)
300     {
301         *complete = 0;
302         return p;
303     }
304     if (zass_openresult(p, complete) < 0)
305         return 0;
306     return p; /* all done */
307 }
308
309 /*
310  * Convert the egate (ccl2rpn) version of RPN to YAZ RPN.
311  */
312 static Z_RPNStructure *rpn2rpn(ODR o, struct ccl_rpn_node *q)
313 {
314     Z_RPNStructure *r = odr_malloc(o, sizeof(*r));
315     Z_AttributesPlusTerm *t;
316     struct ccl_rpn_attr *i;
317     int len;
318     static int op[] = { Z_Operator_and, Z_Operator_or, Z_Operator_and_not };
319
320     switch (q->kind)
321     {
322         case CCL_RPN_TERM:
323             r->which = Z_RPNStructure_simple;
324             r->u.simple = odr_malloc(o, sizeof(Z_Operand));
325             r->u.simple->which = Z_Operand_APT;
326             r->u.simple->u.attributesPlusTerm = t = odr_malloc(o, sizeof(*t));
327             t->term = odr_malloc(o, sizeof(Z_Term));
328             t->term->which = Z_Term_general;
329             t->term->u.general = odr_malloc(o, sizeof(Odr_oct));
330             t->term->u.general->len = t->term->u.general->size =
331                 strlen(q->u.t.term);
332             t->term->u.general->buf = odr_malloc(o, t->term->u.general->size);
333             memcpy(t->term->u.general->buf, q->u.t.term,
334                 t->term->u.general->len);
335             t->num_attributes = 0;
336             t->attributeList = odr_malloc(o, sizeof(Z_AttributeElement*) * 100);
337             for (i = q->u.t.attr_list; i && t->num_attributes < 100;
338                 i = i->next)
339             {
340                 Z_AttributeElement *a;
341                 
342                 t->attributeList[t->num_attributes++] = a =
343                     odr_malloc(o, sizeof(*a));
344                 a->attributeType = odr_malloc(o, sizeof(int));
345                 *a->attributeType = i->type;
346                 a->attributeValue = odr_malloc(o, sizeof(*a));
347                 *a->attributeValue = i->value;
348             }
349             return r;
350         case CCL_RPN_SET:
351             r->which = Z_RPNStructure_simple;
352             r->u.simple = odr_malloc(o, sizeof(Z_Operand));
353             r->u.simple->which = Z_Operand_resultSetId;
354             r->u.simple->u.resultSetId = odr_malloc(o, len =
355                 strlen(q->u.setname) + 1);
356             memcpy(r->u.simple->u.resultSetId, q->u.setname, len);
357             return r;
358         case CCL_RPN_AND: case CCL_RPN_OR: case CCL_RPN_NOT:
359             r->which = Z_RPNStructure_complex;
360             r->u.complex = odr_malloc(o, sizeof(Z_Complex));
361             if (!(r->u.complex->s1 = rpn2rpn(o, q->u.p[0])) ||
362                 !(r->u.complex->s2 = rpn2rpn(o, q->u.p[1])))
363                     return 0;
364             r->u.complex->operator = odr_malloc(o, sizeof(Z_Operator));
365             r->u.complex->operator->which = op[q->kind];
366             r->u.complex->operator->u.and = "";
367             return r;
368         default:
369             gw_log(GW_LOG_FATAL, ZASS_TYPE, "Bad operator in RPN");
370             return 0;
371     }
372 }
373
374 const struct zass_searchent *zass_searchresult(ZASS a, int *complete)
375 {
376     static struct zass_searchent r;
377     Z_APDU *apdu;
378     Z_SearchResponse *res;
379
380     if (!(apdu = get_apdu(a, complete)))
381         return 0;
382     if (apdu->which != Z_APDU_searchResponse)
383     {
384         gw_log(GW_LOG_FATAL, ZASS_TYPE, "Expected searchresponse, got #%d",
385             apdu->which);
386         return 0;
387     }
388     res = apdu->u.searchResponse;
389     r.status = *res->searchStatus;
390     r.num = *res->resultCount;
391     r.status = res->resultSetStatus ? *res->resultSetStatus : 0;
392     r.errcode = -1;
393     *r.errstring = '\0';
394     if (res->records)
395     {
396         if (res->records->which != Z_Records_NSD)
397             gw_log(GW_LOG_WARN, ZASS_TYPE, "Unexpected record types in SchR");
398         else
399         {
400             oident *id;
401             Z_DiagRec *dr = res->records->u.nonSurrogateDiagnostic;
402
403             if (!(id = oid_getentbyoid(dr->diagnosticSetId)) ||
404                 id->class != CLASS_DIAGSET || id->value != VAL_BIB1)
405                     gw_log(GW_LOG_WARN, ZASS_TYPE,
406                         "Missing or unknown diagset - ignoring error!");
407             else
408             {
409                 r.errcode = *dr->condition;
410                 if (dr->addinfo)
411                 {
412                     strncpy(r.errstring, dr->addinfo, 512);
413                     r.errstring[511] = '\0';
414                 }
415             }
416         }
417     }
418     return &r;
419 }
420
421 const struct zass_searchent *zass_search(ZASS a, struct ccl_rpn_node *query,
422     char *resname, char *databases, int *complete)
423 {
424     Z_Query q;
425     Z_RPNQuery rpnq;
426     Z_APDU apdu;
427     Z_SearchRequest req;
428     oident bib1;
429     int smallSetUpperBound = 0;
430     int largeSetLowerBound = 1;
431     int mediumSetPresentNumber = 0;
432     int replaceIndicator = 1;
433     char *datab[100];
434
435     apdu.which = Z_APDU_searchRequest;
436     apdu.u.searchRequest = &req;
437     req.referenceId = 0;
438     req.smallSetUpperBound = &smallSetUpperBound;
439     req.largeSetLowerBound = &largeSetLowerBound;
440     req.mediumSetPresentNumber = &mediumSetPresentNumber;
441     req.replaceIndicator = &replaceIndicator;
442     req.resultSetName = resname;
443     req.num_databaseNames = 0;
444     req.databaseNames = datab;
445     while (*databases && req.num_databaseNames < 100)
446     {
447         char *p = databases;
448         int more;
449
450         while (*p && !isspace(*p))
451             p++;
452         if (isspace(*p))
453             more = 1;
454         else
455             more = 0;
456         *p = '\0';
457         if (p - databases)
458         {
459             req.databaseNames[req.num_databaseNames] = odr_malloc(a->encode,
460                 (p - databases) + 1);
461             strcpy(req.databaseNames[req.num_databaseNames++], databases);
462         }
463         databases = p + more;
464     }
465     req.smallSetElementSetNames = 0;
466     req.mediumSetElementSetNames = 0;
467     req.preferredRecordSyntax = 0;
468     req.query = &q;
469     q.which = Z_Query_type_1;
470     q.u.type_1 = &rpnq;
471     bib1.proto = PROTO_Z3950;
472     bib1.class = CLASS_ATTSET;
473     bib1.value = VAL_BIB1;
474     rpnq.attributeSetId = oid_getoidbyent(&bib1);
475
476     if (complete)
477         *complete = 1;
478     if (!(rpnq.RPNStructure = rpn2rpn(a->encode, query)))
479         return 0;
480     if (send_apdu(a, &apdu) < 0)
481         return 0;
482     if (complete)
483     {
484         *complete = 0;
485         return 0;
486     }
487     else
488         return zass_searchresult(a, complete);
489 }
490
491 /*
492  * Triple indirection - that's kinda heavy. We'll fix it later.
493  * There are worse things around, though. Like ZDist.
494  */
495 void get_diagrec(zass_record ***p, Z_DiagRec *r)
496 {
497     **p = malloc(sizeof(***p));
498     (**p)->next = 0;
499     (**p)->errcode = *r->condition;
500     if (r->addinfo)
501     {
502         strncpy((**p)->errstring, r->addinfo, 200);
503         (**p)->errstring[200] = 0;
504     }
505     else
506         (**p)->errstring[0] = '\0';
507     (**p)->which = ZASS_REC_DIAG;
508     *p = &(**p)->next;
509 }
510
511 void get_responserecords(zass_record ***p, Z_NamePlusRecordList *recs)
512 {
513     int i;
514
515     for (i = 0; i < recs->num_records; i++)
516     {
517         Z_NamePlusRecord *record;
518
519         record = recs->records[i];
520         if (record->which == Z_NamePlusRecord_surrogateDiagnostic)
521             get_diagrec(p, record->u.surrogateDiagnostic);
522         else
523         {
524             Z_DatabaseRecord *r = record->u.databaseRecord;
525             oident *recform;
526
527             **p = malloc(sizeof(***p));
528             (**p)->next = 0;
529
530             if (!(recform = oid_getentbyoid(r->direct_reference)) ||
531                  recform->class != CLASS_RECSYN)
532             {
533                 gw_log(GW_LOG_WARN, ZASS_TYPE, "Unknown or bad record syntax");
534                 (**p)->which = ZASS_REC_UNKNOWN;
535             }
536             else
537                 switch (recform->value)
538                 {
539                     case VAL_USMARC: (**p)->which = ZASS_REC_USMARC; break;
540                     default:
541                         gw_log(GW_LOG_WARN, ZASS_TYPE, "Unknown recsyn");
542                         (**p)->which = ZASS_REC_UNKNOWN;
543                 }
544             if (r->which != ODR_EXTERNAL_octet)
545             {
546                 gw_log(GW_LOG_WARN, ZASS_TYPE, "Record wasn't octet-aligned");
547                 (**p)->record = 0;
548             }
549             else
550             {
551                 if (!((**p)->record = malloc(r->u.octet_aligned->len + 1)))
552                 {
553                     gw_log(GW_LOG_FATAL, ZASS_TYPE, "malloc");
554                     return;
555                 }
556                 memcpy((**p)->record, r->u.octet_aligned->buf,
557                     r->u.octet_aligned->len);
558                 (**p)->record[r->u.octet_aligned->len] = '\0';
559                 gw_log(ZASS_DEBUG, ZASS_TYPE, "Got a record of %d bytes",
560                     r->u.octet_aligned->len);
561             }
562             (*p) = &(**p)->next;
563         }
564     }
565 }
566
567 static void zass_records_free(zass_record *p)
568 {
569 }
570
571 static int send_present(ZASS a, char *name, int start, int num,
572     enum oid_value form)
573 {
574     Z_APDU apdu;
575     Z_PresentRequest req;
576 #if 0
577     oident recsyn;
578 #endif
579
580     apdu.which = Z_APDU_presentRequest;
581     apdu.u.presentRequest = &req;
582     req.referenceId = 0;
583     req.resultSetId = name;
584     req.resultSetStartPoint = &start;
585     req.numberOfRecordsRequested = &num;
586     req.elementSetNames = 0;
587 #if 0
588     recsyn.proto = PROTO_Z3950;
589     recsyn.class = CLASS_RECSYN;
590     recsyn.value = form;
591     req.preferredRecordSyntax = oid_getoidbyent(&recsyn);
592 #else
593     req.preferredRecordSyntax = 0;
594 #endif
595     return send_apdu(a, &apdu);
596 }
597
598 /*
599  * Note that 1== first record.
600  * TODO: make this baby operate in nonblocking mode, too.
601  */
602 const struct zass_presentent *zass_present(ZASS a, char *resname, int start,
603     int num, int *complete)
604 {
605     static struct zass_presentent r;
606     zass_record **rec = &r.records;
607
608     if (complete)
609         *complete = 1;
610     r.num = 0;
611     if (r.records)
612     {
613         zass_records_free(r.records);
614         r.records = 0;
615     }
616     do
617     {
618         Z_APDU *apdu;
619         Z_PresentResponse *res;
620
621         gw_log(ZASS_DEBUG, ZASS_TYPE,
622             "Fetching %d records from # %d", num - r.num, start);
623         if (send_present(a, resname, start, num - r.num, VAL_USMARC) < 0)
624             return 0;
625         if (!(apdu = get_apdu(a, complete)))
626         {
627             if (complete)
628                 *complete = 1;
629             return 0;
630         }
631         if (apdu->which != Z_APDU_presentResponse)
632         {
633             gw_log(GW_LOG_FATAL, ZASS_TYPE, "Expected presentresponse, got #%d",
634                 apdu->which);
635             return 0;
636         }
637         res = apdu->u.presentResponse;
638         r.presentstatus = *res->presentStatus;
639         r.num += *res->numberOfRecordsReturned;
640         if (*res->numberOfRecordsReturned == 0)
641         {
642             gw_log(GW_LOG_WARN, ZASS_TYPE, "Got 0 records from target");
643             return 0;
644         }
645         r.nextpos = *res->nextResultSetPosition;
646         start = r.nextpos;
647         switch (res->records->which)
648         {
649             case Z_Records_DBOSD:
650                 get_responserecords(&rec,
651                     res->records->u.databaseOrSurDiagnostics);
652                 break;
653             case Z_Records_NSD:
654                 get_diagrec(&rec, res->records->u.nonSurrogateDiagnostic);
655                 break;
656             default:
657                 gw_log(GW_LOG_WARN, ZASS_TYPE, "Bad tag in response rec.");
658                 return 0;
659         }
660     }
661     while (num - r.num && start);
662     return &r;
663 }
664
665 const struct zass_presentent *zass_presentresult(ZASS a, int *complete)
666 {
667     *complete = 1;
668     return 0;
669 }