From f6164c93ea8b575c43586bd08b34122b37f5f7af Mon Sep 17 00:00:00 2001 From: Sebastian Hammer Date: Mon, 17 Apr 1995 11:26:52 +0000 Subject: [PATCH] Added YAZ version of zaccess --- zlayer/Makefile | 19 +- zlayer/zaccess-yaz.c | 537 ++++++++++++++++++++++++++++++++++++++++++++++++++ zlayer/zaccess.c | 11 +- 3 files changed, 558 insertions(+), 9 deletions(-) create mode 100644 zlayer/zaccess-yaz.c diff --git a/zlayer/Makefile b/zlayer/Makefile index e1dbc4d..44566aa 100644 --- a/zlayer/Makefile +++ b/zlayer/Makefile @@ -2,7 +2,10 @@ # Europagate, 1995 # # $Log: Makefile,v $ -# Revision 1.8 1995/04/17 09:36:44 adam +# Revision 1.9 1995/04/17 11:26:52 quinn +# Added YAZ version of zaccess +# +# Revision 1.8 1995/04/17 09:36:44 adam # Minor changes in makefile. # # Revision 1.7 1995/03/27 12:52:25 adam @@ -25,16 +28,20 @@ # SHELL=/bin/sh -ZDEFS=-DLOW_TO_HIGH -Dfar= -ZPRE=/home/proj/zdist/zdist102b1-1/libz3950 -ZINC=-I$(ZPRE) -ZLIB=$(ZPRE)/libz3950.a +#ZDEFS=-DLOW_TO_HIGH -Dfar= +#ZPRE=/home/proj/zdist/zdist102b1-1/libz3950 +#ZINC=-I$(ZPRE) +#ZLIB=$(ZPRE)/libz3950.a + +ZINC=-I../../yaz/include +ZDEFS= # -DUSE_XTIMOSI +ZLIB=../../yaz/lib/libyaz.a INCLUDE=-I. -I../include $(ZINC) #CFLAGS=-g -Wall -pedantic -ansi TPROG1=test LIB=../lib/libzass.a -PO=zaccess.o +PO=zaccess-yaz.o CPP=$(CC) -E DEFS=$(INCLUDE) diff --git a/zlayer/zaccess-yaz.c b/zlayer/zaccess-yaz.c new file mode 100644 index 0000000..f8615d9 --- /dev/null +++ b/zlayer/zaccess-yaz.c @@ -0,0 +1,537 @@ +/* + * Europagate, 1995 + * + * Z39.50 API for the Email gateway - YAZ version + * + * $Log: zaccess-yaz.c,v $ + * Revision 1.1 1995/04/17 11:26:53 quinn + * Added YAZ version of zaccess + * + * + */ + +/* + * Interface to the 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 maxrecordsize; + int preferredmessagesize; + char *outbuf; /* intermediary buffer */ + char *inbuf; + int inbuflen; +}; + +static Z_APDU *get_apdu(struct zass *z) +{ + 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"); + return 0; + } + odr_reset(z->decode); + odr_setbuf(z->decode, z->inbuf, res); + if (!z_APDU(z->decode, &ap, 0)) + { + gw_log(GW_LOG_WARN, ZASS_TYPE, "decode: %s", + odr_errlist[odr_geterror(z->decode)]); + return 0; + } + 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); + if (cs_put(z->ass, buf, len) < 0) + { + gw_log(GW_LOG_FATAL, ZASS_TYPE, "cs_put"); + return -1; + } + odr_reset(z->encode); + return 0; +} + +static int send_initreq(struct zass *p) +{ + Z_APDU a; + Z_InitRequest init; + Odr_bitmask options, protocolVersion; + char name[512]; + + a.which = Z_APDU_initRequest; + a.u.initRequest = &init; + init.referenceId = 0; + init.options = &options; + ODR_MASK_ZERO(&options); + ODR_MASK_SET(&options, Z_Options_search); + ODR_MASK_SET(&options, Z_Options_present); + ODR_MASK_SET(&options, Z_Options_delSet); + init.protocolVersion = &protocolVersion; + ODR_MASK_ZERO(&protocolVersion); + ODR_MASK_SET(&protocolVersion, Z_ProtocolVersion_1); + ODR_MASK_SET(&protocolVersion, Z_ProtocolVersion_2); + init.preferredMessageSize = &p->preferredmessagesize; + init.maximumRecordSize = &p->maxrecordsize; + init.idAuthentication = 0; + init.implementationId = ZASS_ID; + sprintf(name, "%s (YAZ protocol layer)", ZASS_NAME); + init.implementationName = name; + init.implementationVersion = ZASS_VERSION; + init.userInformationField = 0; + if (send_apdu(p, &a) < 0) + return -1; + return 0; +} + +static int receive_initres(struct zass *p) +{ + Z_APDU *ap; + Z_InitResponse *res; + + if (!(ap = get_apdu(p))) + 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) +{ + 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 (!(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->outbuf = malloc(p->maxrecordsize + 1024))) + { + gw_log(GW_LOG_FATAL|GW_LOG_ERRNO, ZASS_TYPE, "malloc"); + return 0; + } + odr_setbuf(p->encode, p->outbuf, p->maxrecordsize + 1024); + if (!(p->ass = cs_create(tcpip_type, 1, CS_Z3950))) + { + gw_log(GW_LOG_FATAL|GW_LOG_ERRNO, ZASS_TYPE, "cs_create"); + return 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"); + if (send_initreq(p) < 0 || receive_initres(p) < 0) + { + gw_log(GW_LOG_FATAL, ZASS_TYPE, "Failed to initialize"); + return 0; + } + gw_log(ZASS_DEBUG, ZASS_TYPE, "Sent init request"); + p->inbuf = 0; + p->inbuflen = 0; + return p; +} + +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; + a->attributeValue = odr_malloc(o, sizeof(*a)); + *a->attributeValue = i->value; + } + 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)); + 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; + } +} + +static const struct zass_searchent *search_result(ZASS a) +{ + static struct zass_searchent r; + Z_APDU *apdu; + Z_SearchResponse *res; + + if (!(apdu = get_apdu(a))) + 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; + + 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'; + } + } + } + } + return &r; +} + +const struct zass_searchent *zass_search(ZASS a, struct ccl_rpn_node *query, + char *resname, char *databases) +{ + 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.which = Z_APDU_searchRequest; + apdu.u.searchRequest = &req; + req.referenceId = 0; + 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.smallSetElementSetNames = 0; + req.mediumSetElementSetNames = 0; + req.preferredRecordSyntax = 0; + 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 (!(rpnq.RPNStructure = rpn2rpn(a->encode, query))) + return 0; + if (send_apdu(a, &apdu) < 0) + return 0; + + return search_result(a); +} + +/* + * 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; + (**p)->errcode = *r->condition; + if (r->addinfo) + { + strncpy((**p)->errstring, r->addinfo, 200); + (**p)->errstring[200] = 0; + } + else + (**p)->errstring[0] = '\0'; + (**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; + oident recsyn; + + apdu.which = Z_APDU_presentRequest; + apdu.u.presentRequest = &req; + req.referenceId = 0; + req.resultSetId = name; + req.resultSetStartPoint = &start; + req.numberOfRecordsRequested = # + req.elementSetNames = 0; + recsyn.proto = PROTO_Z3950; + recsyn.class = CLASS_RECSYN; + recsyn.value = form; + req.preferredRecordSyntax = oid_getoidbyent(&recsyn); + return send_apdu(a, &apdu); +} + +/* + * Note that 1== first record. + */ +const struct zass_presentent *zass_present(ZASS a, char *resname, int start, + int num) +{ + static struct zass_presentent r; + zass_record **rec = &r.records; + + 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))) + 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; +} diff --git a/zlayer/zaccess.c b/zlayer/zaccess.c index 3cbc5ca..0420161 100644 --- a/zlayer/zaccess.c +++ b/zlayer/zaccess.c @@ -4,7 +4,10 @@ * Z39.50 API for the Email gateway * * $Log: zaccess.c,v $ - * Revision 1.14 1995/02/23 08:32:26 adam + * Revision 1.15 1995/04/17 11:26:55 quinn + * Added YAZ version of zaccess + * + * Revision 1.14 1995/02/23 08:32:26 adam * Changed header. * * Revision 1.12 1995/02/20 20:35:37 quinn @@ -124,6 +127,7 @@ ZASS zass_open(char *host, int port) PINITREQUEST ireq; PINITRESPONSE ires; int len; + char name[512]; if (!(p = malloc(sizeof(*p)))) { @@ -154,8 +158,9 @@ ZASS zass_open(char *host, int port) } gw_log(ZASS_DEBUG, ZASS_TYPE, "Opened connection to %s:%d", p->ass->HostName, p->ass->Port); + sprintf(name, "%s (ZDIST protocol layer)", ZASS_NAME); ireq = InitRequest_CreateInitAllASCII(0, "yy", "yy", p->maxrecordsize, - p->preferredmessagesize, ZASS_ID, ZASS_NAME, ZASS_VERSION, 0); + p->preferredmessagesize, ZASS_ID, name, ZASS_VERSION, 0); if (!ireq) { gw_log(GW_LOG_FATAL, "ZASS_TYPE", "failed to create initrequest"); @@ -465,7 +470,7 @@ const struct zass_presentent *zass_present(ZASS a, char *resname, int start, } PresentResponse_Destroy(pdu); } - while (num - r.num); + while (num - r.num && start); *rec = 0; return &r; -- 1.7.10.4