2 * Copyright (c) 1995, the EUROPAGATE consortium (see below).
4 * The EUROPAGATE consortium members are:
6 * University College Dublin
7 * Danmarks Teknologiske Videnscenter
8 * An Chomhairle Leabharlanna
9 * Consejo Superior de Investigaciones Cientificas
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,
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.
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.
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.
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.
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.
47 * Z39.50 API for the Email gateway - YAZ version
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.
53 * Revision 1.9 1995/05/03 10:15:43 quinn
54 * Fixed bug in the get_record loop - crashed when diagrec was received.
56 * Revision 1.8 1995/04/28 14:18:38 quinn
57 * *** empty log message ***
59 * Revision 1.7 1995/04/21 16:29:32 quinn
62 * Revision 1.6 1995/04/21 12:58:36 adam
65 * Revision 1.5 1995/04/20 15:25:32 quinn
68 * Revision 1.4 1995/04/19 16:02:28 adam
71 * Revision 1.3 1995/04/19 12:55:15 quinn
74 * Revision 1.2 1995/04/19 09:24:02 quinn
75 * Fixed bug in zass_open - variable initialized after use
77 * Revision 1.1 1995/04/17 11:26:53 quinn
78 * Added YAZ version of zaccess
83 * Interface to the YAZ Z39.50 toolkit.
102 struct zass /* Z-assoc */
104 COMSTACK ass; /* comstack association handle */
107 int fd; /* low-level socket (for select only) */
110 int preferredmessagesize;
115 static Z_APDU *get_apdu(struct zass *z, int *complete)
120 if ((res = cs_get(z->ass, &z->inbuf, &z->inbuflen)) <= 0)
122 gw_log(GW_LOG_WARN, ZASS_TYPE, "cs_get failed");
133 odr_reset(z->decode);
134 odr_setbuf(z->decode, z->inbuf, res, 0);
135 if (!z_APDU(z->decode, &ap, 0))
137 gw_log(GW_LOG_WARN, ZASS_TYPE, "decode: %s",
138 odr_errlist[odr_geterror(z->decode)]);
148 static int send_apdu(struct zass *z, Z_APDU *a)
153 if (!z_APDU(z->encode, &a, 0))
155 gw_log(GW_LOG_FATAL, ZASS_TYPE, "encoding initreq");
158 buf = odr_getbuf(z->encode, &len, NULL);
159 if (cs_put(z->ass, buf, len) < 0)
161 gw_log(GW_LOG_FATAL, ZASS_TYPE, "cs_put");
164 odr_reset(z->encode); /* release odr_allocated structures */
168 static int send_initreq(struct zass *p, char *auth)
172 Odr_bitmask options, protocolVersion;
174 Z_IdAuthentication idauth;
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;
193 init.idAuthentication = &idauth;
194 idauth.which = Z_IdAuthentication_open;
195 idauth.u.open = auth;
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)
209 int zass_fileno(ZASS a)
214 int zass_openresult(ZASS p, int *complete)
219 if (!(ap = get_apdu(p, complete)))
221 if (ap->which != Z_APDU_initResponse)
223 gw_log(GW_LOG_WARN, ZASS_TYPE, "bad APDU");
226 res = ap->u.initResponse;
229 gw_log(GW_LOG_WARN, ZASS_TYPE, "Negative result on initres");
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");
247 ZASS zass_open(char *host, int port, int *complete, char *auth)
253 if (!(p = malloc(sizeof(*p))))
255 gw_log(GW_LOG_FATAL|GW_LOG_ERRNO, ZASS_TYPE, "malloc");
265 if (!(p->encode = odr_createmem(ODR_ENCODE)) ||
266 !(p->decode = odr_createmem(ODR_DECODE)))
268 gw_log(GW_LOG_FATAL|GW_LOG_ERRNO, ZASS_TYPE, "odr_createmem");
271 p->maxrecordsize = ZASS_MAXRECORDSIZE;
272 p->preferredmessagesize = ZASS_PREFERREDMESSAGESIZE;
273 if (!(p->ass = cs_create(tcpip_type, 1, CS_Z3950)))
275 gw_log(GW_LOG_FATAL|GW_LOG_ERRNO, ZASS_TYPE, "cs_create");
280 sprintf(addstr, "%s:%d", host, port);
281 if (!(address = tcpip_strtoaddr(addstr)))
283 gw_log(GW_LOG_FATAL, ZASS_TYPE, "failed to resolve %s", addstr);
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)
290 gw_log(GW_LOG_FATAL, ZASS_TYPE, "Failed to connect to %s", addstr);
293 gw_log(ZASS_DEBUG, ZASS_TYPE, "connected ok... initializing");
294 if (send_initreq(p, auth) < 0)
296 gw_log(GW_LOG_FATAL, ZASS_TYPE, "Failed to send init");
304 if (zass_openresult(p, complete) < 0)
306 return p; /* all done */
310 * Convert the egate (ccl2rpn) version of RPN to YAZ RPN.
312 static Z_RPNStructure *rpn2rpn(ODR o, struct ccl_rpn_node *q)
314 Z_RPNStructure *r = odr_malloc(o, sizeof(*r));
315 Z_AttributesPlusTerm *t;
316 struct ccl_rpn_attr *i;
318 static int op[] = { Z_Operator_and, Z_Operator_or, Z_Operator_and_not };
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 =
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;
340 Z_AttributeElement *a;
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;
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);
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])))
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 = "";
369 gw_log(GW_LOG_FATAL, ZASS_TYPE, "Bad operator in RPN");
374 const struct zass_searchent *zass_searchresult(ZASS a, int *complete)
376 static struct zass_searchent r;
378 Z_SearchResponse *res;
380 if (!(apdu = get_apdu(a, complete)))
382 if (apdu->which != Z_APDU_searchResponse)
384 gw_log(GW_LOG_FATAL, ZASS_TYPE, "Expected searchresponse, got #%d",
388 res = apdu->u.searchResponse;
389 r.status = *res->searchStatus;
390 r.num = *res->resultCount;
391 r.status = res->resultSetStatus ? *res->resultSetStatus : 0;
396 if (res->records->which != Z_Records_NSD)
397 gw_log(GW_LOG_WARN, ZASS_TYPE, "Unexpected record types in SchR");
401 Z_DiagRec *dr = res->records->u.nonSurrogateDiagnostic;
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!");
409 r.errcode = *dr->condition;
412 strncpy(r.errstring, dr->addinfo, 512);
413 r.errstring[511] = '\0';
421 const struct zass_searchent *zass_search(ZASS a, struct ccl_rpn_node *query,
422 char *resname, char *databases, int *complete)
429 int smallSetUpperBound = 0;
430 int largeSetLowerBound = 1;
431 int mediumSetPresentNumber = 0;
432 int replaceIndicator = 1;
435 apdu.which = Z_APDU_searchRequest;
436 apdu.u.searchRequest = &req;
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)
450 while (*p && !isspace(*p))
459 req.databaseNames[req.num_databaseNames] = odr_malloc(a->encode,
460 (p - databases) + 1);
461 strcpy(req.databaseNames[req.num_databaseNames++], databases);
463 databases = p + more;
465 req.smallSetElementSetNames = 0;
466 req.mediumSetElementSetNames = 0;
467 req.preferredRecordSyntax = 0;
469 q.which = Z_Query_type_1;
471 bib1.proto = PROTO_Z3950;
472 bib1.class = CLASS_ATTSET;
473 bib1.value = VAL_BIB1;
474 rpnq.attributeSetId = oid_getoidbyent(&bib1);
478 if (!(rpnq.RPNStructure = rpn2rpn(a->encode, query)))
480 if (send_apdu(a, &apdu) < 0)
488 return zass_searchresult(a, complete);
492 * Triple indirection - that's kinda heavy. We'll fix it later.
493 * There are worse things around, though. Like ZDist.
495 void get_diagrec(zass_record ***p, Z_DiagRec *r)
497 **p = malloc(sizeof(***p));
499 (**p)->errcode = *r->condition;
502 strncpy((**p)->errstring, r->addinfo, 200);
503 (**p)->errstring[200] = 0;
506 (**p)->errstring[0] = '\0';
507 (**p)->which = ZASS_REC_DIAG;
511 void get_responserecords(zass_record ***p, Z_NamePlusRecordList *recs)
515 for (i = 0; i < recs->num_records; i++)
517 Z_NamePlusRecord *record;
519 record = recs->records[i];
520 if (record->which == Z_NamePlusRecord_surrogateDiagnostic)
521 get_diagrec(p, record->u.surrogateDiagnostic);
524 Z_DatabaseRecord *r = record->u.databaseRecord;
527 **p = malloc(sizeof(***p));
530 if (!(recform = oid_getentbyoid(r->direct_reference)) ||
531 recform->class != CLASS_RECSYN)
533 gw_log(GW_LOG_WARN, ZASS_TYPE, "Unknown or bad record syntax");
534 (**p)->which = ZASS_REC_UNKNOWN;
537 switch (recform->value)
539 case VAL_USMARC: (**p)->which = ZASS_REC_USMARC; break;
541 gw_log(GW_LOG_WARN, ZASS_TYPE, "Unknown recsyn");
542 (**p)->which = ZASS_REC_UNKNOWN;
544 if (r->which != ODR_EXTERNAL_octet)
546 gw_log(GW_LOG_WARN, ZASS_TYPE, "Record wasn't octet-aligned");
551 if (!((**p)->record = malloc(r->u.octet_aligned->len + 1)))
553 gw_log(GW_LOG_FATAL, ZASS_TYPE, "malloc");
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);
567 static void zass_records_free(zass_record *p)
571 static int send_present(ZASS a, char *name, int start, int num,
575 Z_PresentRequest req;
580 apdu.which = Z_APDU_presentRequest;
581 apdu.u.presentRequest = &req;
583 req.resultSetId = name;
584 req.resultSetStartPoint = &start;
585 req.numberOfRecordsRequested = #
586 req.elementSetNames = 0;
588 recsyn.proto = PROTO_Z3950;
589 recsyn.class = CLASS_RECSYN;
591 req.preferredRecordSyntax = oid_getoidbyent(&recsyn);
593 req.preferredRecordSyntax = 0;
595 return send_apdu(a, &apdu);
599 * Note that 1== first record.
600 * TODO: make this baby operate in nonblocking mode, too.
602 const struct zass_presentent *zass_present(ZASS a, char *resname, int start,
603 int num, int *complete)
605 static struct zass_presentent r;
606 zass_record **rec = &r.records;
613 zass_records_free(r.records);
619 Z_PresentResponse *res;
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)
625 if (!(apdu = get_apdu(a, complete)))
631 if (apdu->which != Z_APDU_presentResponse)
633 gw_log(GW_LOG_FATAL, ZASS_TYPE, "Expected presentresponse, got #%d",
637 res = apdu->u.presentResponse;
638 r.presentstatus = *res->presentStatus;
639 r.num += *res->numberOfRecordsReturned;
640 if (*res->numberOfRecordsReturned == 0)
642 gw_log(GW_LOG_WARN, ZASS_TYPE, "Got 0 records from target");
645 r.nextpos = *res->nextResultSetPosition;
647 switch (res->records->which)
649 case Z_Records_DBOSD:
650 get_responserecords(&rec,
651 res->records->u.databaseOrSurDiagnostics);
654 get_diagrec(&rec, res->records->u.nonSurrogateDiagnostic);
657 gw_log(GW_LOG_WARN, ZASS_TYPE, "Bad tag in response rec.");
661 while (num - r.num && start);
665 const struct zass_presentent *zass_presentresult(ZASS a, int *complete)