From 91d8877d6db53047e46b24f39ba95e7a01691755 Mon Sep 17 00:00:00 2001 From: Adam Dickmeiss Date: Mon, 3 Jul 1995 08:21:30 +0000 Subject: [PATCH] Yaz layer moved to new sub directory. zaccess aligned with new YAZ version (1.0b). --- zlayer-yaz/Makefile | 47 ++++ zlayer-yaz/zaccess.c | 697 ++++++++++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 744 insertions(+) create mode 100644 zlayer-yaz/Makefile create mode 100644 zlayer-yaz/zaccess.c diff --git a/zlayer-yaz/Makefile b/zlayer-yaz/Makefile new file mode 100644 index 0000000..f8c66e6 --- /dev/null +++ b/zlayer-yaz/Makefile @@ -0,0 +1,47 @@ +# Makefile for Email gateway Z39.50 interface +# Europagate, 1995 +# +# Makefile for the YAZ interface +# $Id: Makefile,v 1.1 1995/07/03 08:21:30 adam Exp $ +# +SHELL=/bin/sh + +ZINC=-I../../yaz/include +#ZDEFS=-DUSE_XTIMOSI +ZLIB=../../yaz/lib/libyaz.a + +INCLUDE=-I. -I../include $(ZINC) +LIB=../lib/libzass.a +PO=zaccess.o +CPP=$(CC) -E +DEFS=$(INCLUDE) + +all: $(LIB) + +$(LIB): $(PO) + rm -f $(LIB) + ar qc $(LIB) $(PO) + ranlib $(LIB) + +.c.o: + $(CC) -c $(DEFS) $(CFLAGS) $(ZDEFS) $< + +clean: + rm -f *.log *.[oa] core mon.out gmon.out errlist *~ + +depend: depend2 + +depend1: + sed '/^#Depend/q' Makefile.tmp + $(CPP) $(DEFS) -M *.c >>Makefile.tmp + mv -f Makefile.tmp Makefile + +depend2: + $(CPP) $(DEFS) -M *.c >.depend + +#GNU make style depend +ifeq (.depend,$(wildcard .depend)) +include .depend +endif + +#Depend --- DOT NOT DELETE THIS LINE diff --git a/zlayer-yaz/zaccess.c b/zlayer-yaz/zaccess.c new file mode 100644 index 0000000..f9e93ad --- /dev/null +++ b/zlayer-yaz/zaccess.c @@ -0,0 +1,697 @@ +/* + * Copyright (c) 1995, the EUROPAGATE consortium (see below). + * + * The EUROPAGATE consortium members are: + * + * University College Dublin + * Danmarks Teknologiske Videnscenter + * An Chomhairle Leabharlanna + * Consejo Superior de Investigaciones Cientificas + * + * Permission to use, copy, modify, distribute, and sell this software and + * its documentation, in whole or in part, for any purpose, is hereby granted, + * provided that: + * + * 1. This copyright and permission notice appear in all copies of the + * software and its documentation. Notices of copyright or attribution + * which appear at the beginning of any file must remain unchanged. + * + * 2. The names of EUROPAGATE or the project partners may not be used to + * endorse or promote products derived from this software without specific + * prior written permission. + * + * 3. Users of this software (implementors and gateway operators) agree to + * inform the EUROPAGATE consortium of their use of the software. This + * information will be used to evaluate the EUROPAGATE project and the + * software, and to plan further developments. The consortium may use + * the information in later publications. + * + * 4. Users of this software agree to make their best efforts, when + * documenting their use of the software, to acknowledge the EUROPAGATE + * consortium, and the role played by the software in their work. + * + * THIS SOFTWARE IS PROVIDED "AS IS" AND WITHOUT WARRANTY OF ANY KIND, + * EXPRESS, IMPLIED, OR OTHERWISE, INCLUDING WITHOUT LIMITATION, ANY + * WARRANTY OF MERCHANTABILITY OR FITNESS FOR A PARTICULAR PURPOSE. + * IN NO EVENT SHALL THE EUROPAGATE CONSORTIUM OR ITS MEMBERS BE LIABLE + * FOR ANY SPECIAL, INCIDENTAL, INDIRECT OR CONSEQUENTIAL DAMAGES OF + * ANY KIND, OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA + * OR PROFITS, WHETHER OR NOT ADVISED OF THE POSSIBILITY OF DAMAGE, AND + * ON ANY THEORY OF LIABILITY, ARISING OUT OF OR IN CONNECTION WITH THE + * USE OR PERFORMANCE OF THIS SOFTWARE. + * + */ +/* + * Europagate, 1995 + * + * Z39.50 API for the Email gateway - YAZ version + * + * $Log: zaccess.c,v $ + * Revision 1.1 1995/07/03 08:21:31 adam + * Yaz layer moved to new sub directory. zaccess aligned with new + * YAZ version (1.0b). + * + * Revision 1.10 1995/05/16 09:41:46 adam + * LICENSE. Uses new versions of odr_{set,get}buf. + * + * Revision 1.9 1995/05/03 10:15:43 quinn + * Fixed bug in the get_record loop - crashed when diagrec was received. + * + * Revision 1.8 1995/04/28 14:18:38 quinn + * *** empty log message *** + * + * Revision 1.7 1995/04/21 16:29:32 quinn + * Fixed bugs. + * + * Revision 1.6 1995/04/21 12:58:36 adam + * Bug fix. + * + * Revision 1.5 1995/04/20 15:25:32 quinn + * Asynch. API + * + * Revision 1.4 1995/04/19 16:02:28 adam + * Record type hack. + * + * Revision 1.3 1995/04/19 12:55:15 quinn + * Added auth. + * + * Revision 1.2 1995/04/19 09:24:02 quinn + * Fixed bug in zass_open - variable initialized after use + * + * Revision 1.1 1995/04/17 11:26:53 quinn + * Added YAZ version of zaccess + * + */ + +/* + * Interface to the YAZ Z39.50 toolkit. + */ + +#include +#include +#include + +#include +#include +#include +#include +#ifdef USE_XTIMOSI +#include +#endif +#include + +#include +#include + +struct zass /* Z-assoc */ +{ + COMSTACK ass; /* comstack association handle */ + ODR encode; + ODR decode; + int fd; /* low-level socket (for select only) */ + int nonblocking; + int maxrecordsize; + int preferredmessagesize; + char *inbuf; + int inbuflen; +}; + +static Z_APDU *get_apdu(struct zass *z, int *complete) +{ + int res; + Z_APDU *ap; + + if ((res = cs_get(z->ass, &z->inbuf, &z->inbuflen)) <= 0) + { + gw_log(GW_LOG_WARN, ZASS_TYPE, "cs_get failed"); + if (complete) + *complete = 1; + return 0; + } + else if (res == 1) + { + if (complete) + *complete = 0; + return 0; + } + odr_reset(z->decode); + odr_setbuf(z->decode, z->inbuf, res, 0); + if (!z_APDU(z->decode, &ap, 0)) + { + gw_log(GW_LOG_WARN, ZASS_TYPE, "decode: %s", + odr_errlist[odr_geterror(z->decode)]); + if (complete) + *complete = 0; + return 0; + } + if (complete) + *complete = 1; + return ap; +} + +static int send_apdu(struct zass *z, Z_APDU *a) +{ + char *buf; + int len; + + if (!z_APDU(z->encode, &a, 0)) + { + gw_log(GW_LOG_FATAL, ZASS_TYPE, "encoding initreq"); + return -1; + } + buf = odr_getbuf(z->encode, &len, NULL); + if (cs_put(z->ass, buf, len) < 0) + { + gw_log(GW_LOG_FATAL, ZASS_TYPE, "cs_put"); + return -1; + } + odr_reset(z->encode); /* release odr_allocated structures */ + return 0; +} + +static int send_initreq(struct zass *p, char *auth) +{ + Z_APDU *apdu; + Z_InitRequest *req; + char name[512]; + Z_IdAuthentication idauth; + + apdu = zget_APDU (p->encode, Z_APDU_initRequest); + req = apdu->u.initRequest; + req->preferredMessageSize = &p->preferredmessagesize; + req->maximumRecordSize = &p->maxrecordsize; + + if (auth) + { + req->idAuthentication = &idauth; + idauth.which = Z_IdAuthentication_open; + idauth.u.open = auth; + } + else + req->idAuthentication = 0; + sprintf(name, "%s (YAZ protocol layer)", ZASS_NAME); + req->implementationName = name; + req->implementationVersion = ZASS_VERSION; + if (send_apdu(p, apdu) < 0) + return -1; + return 0; +} + +int zass_fileno(ZASS a) +{ + return a->fd; +} + +int zass_openresult(ZASS p, int *complete) +{ + Z_APDU *ap; + Z_InitResponse *res; + + if (!(ap = get_apdu(p, complete))) + return -1; + if (ap->which != Z_APDU_initResponse) + { + gw_log(GW_LOG_WARN, ZASS_TYPE, "bad APDU"); + return -1; + } + res = ap->u.initResponse; + if (!*res->result) + { + gw_log(GW_LOG_WARN, ZASS_TYPE, "Negative result on initres"); + return -1; + } + p->preferredmessagesize = *res->preferredMessageSize; + p->maxrecordsize = *res->maximumRecordSize; + if (res->implementationId) + gw_log(GW_LOG_NOTICE, ZASS_TYPE, "imp. ID : %s", + res->implementationId); + if (res->implementationName) + gw_log(GW_LOG_NOTICE, ZASS_TYPE, "imp. ID : %s", + res->implementationName); + if (res->implementationVersion) + gw_log(GW_LOG_NOTICE, ZASS_TYPE, "imp. ID : %s", + res->implementationVersion); + gw_log(ZASS_DEBUG, ZASS_TYPE, "Initialized ok"); + return 0; +} + +ZASS zass_open(char *host, int port, int *complete, char *auth) +{ + struct zass *p; + char addstr[512]; + void *address; + + if (!(p = malloc(sizeof(*p)))) + { + gw_log(GW_LOG_FATAL|GW_LOG_ERRNO, ZASS_TYPE, "malloc"); + return 0; + } + if (complete) + { + *complete = 1; + p->nonblocking = 1; + } + else + p->nonblocking = 0; + if (!(p->encode = odr_createmem(ODR_ENCODE)) || + !(p->decode = odr_createmem(ODR_DECODE))) + { + gw_log(GW_LOG_FATAL|GW_LOG_ERRNO, ZASS_TYPE, "odr_createmem"); + return 0; + } + p->maxrecordsize = ZASS_MAXRECORDSIZE; + p->preferredmessagesize = ZASS_PREFERREDMESSAGESIZE; + if (!(p->ass = cs_create(tcpip_type, 1, CS_Z3950))) + { + gw_log(GW_LOG_FATAL|GW_LOG_ERRNO, ZASS_TYPE, "cs_create"); + return 0; + } + p->inbuf = 0; + p->inbuflen = 0; + sprintf(addstr, "%s:%d", host, port); + if (!(address = tcpip_strtoaddr(addstr))) + { + gw_log(GW_LOG_FATAL, ZASS_TYPE, "failed to resolve %s", addstr); + return 0; + } + p->fd = cs_fileno(p->ass); + gw_log(ZASS_DEBUG, ZASS_TYPE, "Connecting to %s", addstr); + if (cs_connect(p->ass, address) < 0) + { + gw_log(GW_LOG_FATAL, ZASS_TYPE, "Failed to connect to %s", addstr); + return 0; + } + gw_log(ZASS_DEBUG, ZASS_TYPE, "connected ok... initializing"); + if (send_initreq(p, auth) < 0) + { + gw_log(GW_LOG_FATAL, ZASS_TYPE, "Failed to send init"); + return 0; + } + if (p->nonblocking) + { + *complete = 0; + return p; + } + if (zass_openresult(p, complete) < 0) + return 0; + return p; /* all done */ +} + +/* + * Convert the egate (ccl2rpn) version of RPN to YAZ RPN. + */ +static Z_RPNStructure *rpn2rpn(ODR o, struct ccl_rpn_node *q) +{ + Z_RPNStructure *r = odr_malloc(o, sizeof(*r)); + Z_AttributesPlusTerm *t; + struct ccl_rpn_attr *i; + int len; + static int op[] = { Z_Operator_and, Z_Operator_or, Z_Operator_and_not }; + + switch (q->kind) + { + case CCL_RPN_TERM: + r->which = Z_RPNStructure_simple; + r->u.simple = odr_malloc(o, sizeof(Z_Operand)); + r->u.simple->which = Z_Operand_APT; + r->u.simple->u.attributesPlusTerm = t = odr_malloc(o, sizeof(*t)); + t->term = odr_malloc(o, sizeof(Z_Term)); + t->term->which = Z_Term_general; + t->term->u.general = odr_malloc(o, sizeof(Odr_oct)); + t->term->u.general->len = t->term->u.general->size = + strlen(q->u.t.term); + t->term->u.general->buf = odr_malloc(o, t->term->u.general->size); + memcpy(t->term->u.general->buf, q->u.t.term, + t->term->u.general->len); + t->num_attributes = 0; + t->attributeList = odr_malloc(o, sizeof(Z_AttributeElement*) * 100); + for (i = q->u.t.attr_list; i && t->num_attributes < 100; + i = i->next) + { + Z_AttributeElement *a; + + t->attributeList[t->num_attributes++] = a = + odr_malloc(o, sizeof(*a)); + a->attributeType = odr_malloc(o, sizeof(int)); + *a->attributeType = i->type; +#ifdef Z_95 + a->attributeSet = 0; + a->which = Z_AttributeValue_numeric; + a->value.numeric = odr_malloc(o, sizeof(*a->value.numeric)); + *a->value.numeric = i->value; +#else + a->attributeValue = odr_malloc(o, sizeof(*a->attributeValue)); + *a->attributeValue = i->value; +#endif + } + return r; + case CCL_RPN_SET: + r->which = Z_RPNStructure_simple; + r->u.simple = odr_malloc(o, sizeof(Z_Operand)); + r->u.simple->which = Z_Operand_resultSetId; + r->u.simple->u.resultSetId = odr_malloc(o, len = + strlen(q->u.setname) + 1); + memcpy(r->u.simple->u.resultSetId, q->u.setname, len); + return r; + case CCL_RPN_AND: case CCL_RPN_OR: case CCL_RPN_NOT: + r->which = Z_RPNStructure_complex; + r->u.complex = odr_malloc(o, sizeof(Z_Complex)); + if (!(r->u.complex->s1 = rpn2rpn(o, q->u.p[0])) || + !(r->u.complex->s2 = rpn2rpn(o, q->u.p[1]))) + return 0; + r->u.complex->operator = odr_malloc(o, sizeof(Z_Operator)); + r->u.complex->operator->which = op[q->kind]; + r->u.complex->operator->u.and = ""; + return r; + default: + gw_log(GW_LOG_FATAL, ZASS_TYPE, "Bad operator in RPN"); + return 0; + } +} + +const struct zass_searchent *zass_searchresult(ZASS a, int *complete) +{ + static struct zass_searchent r; + Z_APDU *apdu; + Z_SearchResponse *res; + + if (!(apdu = get_apdu(a, complete))) + return 0; + if (apdu->which != Z_APDU_searchResponse) + { + gw_log(GW_LOG_FATAL, ZASS_TYPE, "Expected searchresponse, got #%d", + apdu->which); + return 0; + } + res = apdu->u.searchResponse; + r.status = *res->searchStatus; + r.num = *res->resultCount; + r.status = res->resultSetStatus ? *res->resultSetStatus : 0; + r.errcode = -1; + *r.errstring = '\0'; + if (res->records) + { + if (res->records->which != Z_Records_NSD) + gw_log(GW_LOG_WARN, ZASS_TYPE, "Unexpected record types in SchR"); + else + { + oident *id; + Z_DiagRec *dr = res->records->u.nonSurrogateDiagnostic; + +#ifdef Z_95 + if (dr->which != Z_DiagRec_defaultFormat || + !(id = oid_getentbyoid + (dr->u.defaultFormat->diagnosticSetId)) || + id->class != CLASS_DIAGSET || + id->value != VAL_BIB1) + gw_log(GW_LOG_WARN, ZASS_TYPE, + "Missing or unknown diagset - ignoring error!"); + else + { + r.errcode = *dr->u.defaultFormat->condition; + if (dr->u.defaultFormat->addinfo) + { + strncpy(r.errstring, dr->u.defaultFormat->addinfo, 512); + r.errstring[511] = '\0'; + } + } +#else + if (!(id = oid_getentbyoid(dr->diagnosticSetId)) || + id->class != CLASS_DIAGSET || id->value != VAL_BIB1) + gw_log(GW_LOG_WARN, ZASS_TYPE, + "Missing or unknown diagset - ignoring error!"); + else + { + r.errcode = *dr->condition; + if (dr->addinfo) + { + strncpy(r.errstring, dr->addinfo, 512); + r.errstring[511] = '\0'; + } + } +#endif + } + } + return &r; +} + +const struct zass_searchent *zass_search(ZASS a, struct ccl_rpn_node *query, + char *resname, char *databases, int *complete) +{ + Z_Query q; + Z_RPNQuery rpnq; + Z_APDU *apdu; + Z_SearchRequest *req; + oident bib1; + int smallSetUpperBound = 0; + int largeSetLowerBound = 1; + int mediumSetPresentNumber = 0; + int replaceIndicator = 1; + char *datab[100]; + + apdu = zget_APDU (a->encode, Z_APDU_searchRequest); + req = apdu->u.searchRequest; + + req->smallSetUpperBound = &smallSetUpperBound; + req->largeSetLowerBound = &largeSetLowerBound; + req->mediumSetPresentNumber = &mediumSetPresentNumber; + req->replaceIndicator = &replaceIndicator; + req->resultSetName = resname; + req->num_databaseNames = 0; + req->databaseNames = datab; + while (*databases && req->num_databaseNames < 100) + { + char *p = databases; + int more; + + while (*p && !isspace(*p)) + p++; + if (isspace(*p)) + more = 1; + else + more = 0; + *p = '\0'; + if (p - databases) + { + req->databaseNames[req->num_databaseNames] = odr_malloc(a->encode, + (p - databases) + 1); + strcpy(req->databaseNames[req->num_databaseNames++], databases); + } + databases = p + more; + } + req->query = &q; + q.which = Z_Query_type_1; + q.u.type_1 = &rpnq; + bib1.proto = PROTO_Z3950; + bib1.class = CLASS_ATTSET; + bib1.value = VAL_BIB1; + rpnq.attributeSetId = oid_getoidbyent(&bib1); + + if (complete) + *complete = 1; + if (!(rpnq.RPNStructure = rpn2rpn(a->encode, query))) + return 0; + if (send_apdu(a, apdu) < 0) + return 0; + if (complete) + { + *complete = 0; + return 0; + } + else + return zass_searchresult(a, complete); +} + +/* + * Triple indirection - that's kinda heavy. We'll fix it later. + * There are worse things around, though. Like ZDist. + */ +void get_diagrec(zass_record ***p, Z_DiagRec *r) +{ + **p = malloc(sizeof(***p)); + (**p)->next = 0; +#ifdef Z_95 + (**p)->errstring[0] = '\0'; + if (r->which == Z_DiagRec_defaultFormat) + { + (**p)->errcode = *r->u.defaultFormat->condition; + if (r->u.defaultFormat->addinfo) + { + strncpy((**p)->errstring, r->u.defaultFormat->addinfo, 200); + (**p)->errstring[200] = 0; + } + } + else + (**p)->errcode = -1; +#else + (**p)->errcode = *r->condition; + if (r->addinfo) + { + strncpy((**p)->errstring, r->addinfo, 200); + (**p)->errstring[200] = 0; + } + else + (**p)->errstring[0] = '\0'; +#endif + (**p)->which = ZASS_REC_DIAG; + *p = &(**p)->next; +} + +void get_responserecords(zass_record ***p, Z_NamePlusRecordList *recs) +{ + int i; + + for (i = 0; i < recs->num_records; i++) + { + Z_NamePlusRecord *record; + + record = recs->records[i]; + if (record->which == Z_NamePlusRecord_surrogateDiagnostic) + get_diagrec(p, record->u.surrogateDiagnostic); + else + { + Z_DatabaseRecord *r = record->u.databaseRecord; + oident *recform; + + **p = malloc(sizeof(***p)); + (**p)->next = 0; + + if (!(recform = oid_getentbyoid(r->direct_reference)) || + recform->class != CLASS_RECSYN) + { + gw_log(GW_LOG_WARN, ZASS_TYPE, "Unknown or bad record syntax"); + (**p)->which = ZASS_REC_UNKNOWN; + } + else + switch (recform->value) + { + case VAL_USMARC: (**p)->which = ZASS_REC_USMARC; break; + default: + gw_log(GW_LOG_WARN, ZASS_TYPE, "Unknown recsyn"); + (**p)->which = ZASS_REC_UNKNOWN; + } + if (r->which != ODR_EXTERNAL_octet) + { + gw_log(GW_LOG_WARN, ZASS_TYPE, "Record wasn't octet-aligned"); + (**p)->record = 0; + } + else + { + if (!((**p)->record = malloc(r->u.octet_aligned->len + 1))) + { + gw_log(GW_LOG_FATAL, ZASS_TYPE, "malloc"); + return; + } + memcpy((**p)->record, r->u.octet_aligned->buf, + r->u.octet_aligned->len); + (**p)->record[r->u.octet_aligned->len] = '\0'; + gw_log(ZASS_DEBUG, ZASS_TYPE, "Got a record of %d bytes", + r->u.octet_aligned->len); + } + (*p) = &(**p)->next; + } + } +} + +static void zass_records_free(zass_record *p) +{ +} + +static int send_present(ZASS a, char *name, int start, int num, + enum oid_value form) +{ + Z_APDU *apdu; + Z_PresentRequest *req; +#if 0 + oident recsyn; +#endif + + apdu = zget_APDU (a->encode, Z_APDU_presentRequest); + req = apdu->u.presentRequest; + + req->resultSetId = name; + req->resultSetStartPoint = &start; + req->numberOfRecordsRequested = # +#if 0 + recsyn.proto = PROTO_Z3950; + recsyn.class = CLASS_RECSYN; + recsyn.value = form; + req->preferredRecordSyntax = oid_getoidbyent(&recsyn); +#else + req->preferredRecordSyntax = 0; +#endif + return send_apdu(a, apdu); +} + +/* + * Note that 1== first record. + * TODO: make this baby operate in nonblocking mode, too. + */ +const struct zass_presentent *zass_present(ZASS a, char *resname, int start, + int num, int *complete) +{ + static struct zass_presentent r; + zass_record **rec = &r.records; + + if (complete) + *complete = 1; + r.num = 0; + if (r.records) + { + zass_records_free(r.records); + r.records = 0; + } + do + { + Z_APDU *apdu; + Z_PresentResponse *res; + + gw_log(ZASS_DEBUG, ZASS_TYPE, + "Fetching %d records from # %d", num - r.num, start); + if (send_present(a, resname, start, num - r.num, VAL_USMARC) < 0) + return 0; + if (!(apdu = get_apdu(a, complete))) + { + if (complete) + *complete = 1; + return 0; + } + if (apdu->which != Z_APDU_presentResponse) + { + gw_log(GW_LOG_FATAL, ZASS_TYPE, "Expected presentresponse, got #%d", + apdu->which); + return 0; + } + res = apdu->u.presentResponse; + r.presentstatus = *res->presentStatus; + r.num += *res->numberOfRecordsReturned; + if (*res->numberOfRecordsReturned == 0) + { + gw_log(GW_LOG_WARN, ZASS_TYPE, "Got 0 records from target"); + return 0; + } + r.nextpos = *res->nextResultSetPosition; + start = r.nextpos; + switch (res->records->which) + { + case Z_Records_DBOSD: + get_responserecords(&rec, + res->records->u.databaseOrSurDiagnostics); + break; + case Z_Records_NSD: + get_diagrec(&rec, res->records->u.nonSurrogateDiagnostic); + break; + default: + gw_log(GW_LOG_WARN, ZASS_TYPE, "Bad tag in response rec."); + return 0; + } + } + while (num - r.num && start); + return &r; +} + +const struct zass_presentent *zass_presentresult(ZASS a, int *complete) +{ + *complete = 1; + return 0; +} -- 1.7.10.4