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
50 * Revision 1.6 2001/02/26 14:32:36 adam
51 * Updated for YAZ 1.7. HTML output tidy up. Added LOC target.
53 * Revision 1.5 1996/01/03 08:58:12 adam
54 * Updated to use new version of Yaz (some names changed to avoid C++ conflict).
56 * Revision 1.4 1995/07/28 10:51:34 adam
57 * Bug fix: multiple databases weren't specified correctly.
59 * Revision 1.3 1995/07/20 08:18:15 adam
60 * Bug fix: Multiple databases are separated by commas and NOT by space.
62 * Revision 1.2 1995/07/05 11:08:33 adam
63 * Bug fix in init request.
65 * Revision 1.1 1995/07/03 08:21:31 adam
66 * Yaz layer moved to new sub directory. zaccess aligned with new
69 * Revision 1.10 1995/05/16 09:41:46 adam
70 * LICENSE. Uses new versions of odr_{set,get}buf.
72 * Revision 1.9 1995/05/03 10:15:43 quinn
73 * Fixed bug in the get_record loop - crashed when diagrec was received.
75 * Revision 1.8 1995/04/28 14:18:38 quinn
76 * *** empty log message ***
78 * Revision 1.7 1995/04/21 16:29:32 quinn
81 * Revision 1.6 1995/04/21 12:58:36 adam
84 * Revision 1.5 1995/04/20 15:25:32 quinn
87 * Revision 1.4 1995/04/19 16:02:28 adam
90 * Revision 1.3 1995/04/19 12:55:15 quinn
93 * Revision 1.2 1995/04/19 09:24:02 quinn
94 * Fixed bug in zass_open - variable initialized after use
96 * Revision 1.1 1995/04/17 11:26:53 quinn
97 * Added YAZ version of zaccess
102 * Interface to the YAZ Z39.50 toolkit.
110 #include <yaz/proto.h>
111 #include <yaz/comstack.h>
112 #include <yaz/tcpip.h>
121 struct zass /* Z-assoc */
123 COMSTACK ass; /* comstack association handle */
126 int fd; /* low-level socket (for select only) */
129 int preferredmessagesize;
134 static Z_APDU *get_apdu(struct zass *z, int *complete)
139 if ((res = cs_get(z->ass, &z->inbuf, &z->inbuflen)) <= 0)
141 gw_log(GW_LOG_WARN, ZASS_TYPE, "cs_get failed");
152 odr_reset(z->decode);
153 odr_setbuf(z->decode, z->inbuf, res, 0);
154 if (!z_APDU(z->decode, &ap, 0, 0))
156 gw_log(GW_LOG_WARN, ZASS_TYPE, "decode: %s",
157 odr_errlist[odr_geterror(z->decode)]);
167 static int send_apdu(struct zass *z, Z_APDU *a)
172 if (!z_APDU(z->encode, &a, 0, 0))
174 gw_log(GW_LOG_FATAL, ZASS_TYPE, "encoding initreq");
177 buf = odr_getbuf(z->encode, &len, NULL);
178 if (cs_put(z->ass, buf, len) < 0)
180 gw_log(GW_LOG_FATAL, ZASS_TYPE, "cs_put");
183 odr_reset(z->encode); /* release odr_allocated structures */
187 static int send_initreq(struct zass *p, char *auth)
192 Z_IdAuthentication idauth;
194 apdu = zget_APDU (p->encode, Z_APDU_initRequest);
195 req = apdu->u.initRequest;
196 req->preferredMessageSize = &p->preferredmessagesize;
197 req->maximumRecordSize = &p->maxrecordsize;
199 ODR_MASK_SET(req->options, Z_Options_search);
200 ODR_MASK_SET(req->options, Z_Options_present);
201 ODR_MASK_SET(req->options, Z_Options_namedResultSets);
203 ODR_MASK_SET(req->protocolVersion, Z_ProtocolVersion_1);
204 ODR_MASK_SET(req->protocolVersion, Z_ProtocolVersion_2);
208 req->idAuthentication = &idauth;
209 idauth.which = Z_IdAuthentication_open;
210 idauth.u.open = auth;
213 req->idAuthentication = 0;
214 sprintf(name, "%s (YAZ protocol layer)", ZASS_NAME);
215 req->implementationName = name;
216 req->implementationVersion = ZASS_VERSION;
217 if (send_apdu(p, apdu) < 0)
222 int zass_fileno(ZASS a)
227 int zass_openresult(ZASS p, int *complete)
232 if (!(ap = get_apdu(p, complete)))
234 if (ap->which != Z_APDU_initResponse)
236 gw_log(GW_LOG_WARN, ZASS_TYPE, "bad APDU");
239 res = ap->u.initResponse;
242 gw_log(GW_LOG_WARN, ZASS_TYPE, "Negative result on initres");
245 p->preferredmessagesize = *res->preferredMessageSize;
246 p->maxrecordsize = *res->maximumRecordSize;
247 if (res->implementationId)
248 gw_log(GW_LOG_NOTICE, ZASS_TYPE, "imp. ID : %s",
249 res->implementationId);
250 if (res->implementationName)
251 gw_log(GW_LOG_NOTICE, ZASS_TYPE, "imp. ID : %s",
252 res->implementationName);
253 if (res->implementationVersion)
254 gw_log(GW_LOG_NOTICE, ZASS_TYPE, "imp. ID : %s",
255 res->implementationVersion);
256 gw_log(ZASS_DEBUG, ZASS_TYPE, "Initialized ok");
260 ZASS zass_open(char *host, int port, int *complete, char *auth)
266 if (!(p = malloc(sizeof(*p))))
268 gw_log(GW_LOG_FATAL|GW_LOG_ERRNO, ZASS_TYPE, "malloc");
278 if (!(p->encode = odr_createmem(ODR_ENCODE)) ||
279 !(p->decode = odr_createmem(ODR_DECODE)))
281 gw_log(GW_LOG_FATAL|GW_LOG_ERRNO, ZASS_TYPE, "odr_createmem");
284 p->maxrecordsize = ZASS_MAXRECORDSIZE;
285 p->preferredmessagesize = ZASS_PREFERREDMESSAGESIZE;
286 if (!(p->ass = cs_create(tcpip_type, 1, CS_Z3950)))
288 gw_log(GW_LOG_FATAL|GW_LOG_ERRNO, ZASS_TYPE, "cs_create");
293 sprintf(addstr, "%s:%d", host, port);
294 if (!(address = tcpip_strtoaddr(addstr)))
296 gw_log(GW_LOG_FATAL, ZASS_TYPE, "failed to resolve %s", addstr);
299 p->fd = cs_fileno(p->ass);
300 gw_log(ZASS_DEBUG, ZASS_TYPE, "Connecting to %s", addstr);
301 if (cs_connect(p->ass, address) < 0)
303 gw_log(GW_LOG_FATAL, ZASS_TYPE, "Failed to connect to %s", addstr);
306 gw_log(ZASS_DEBUG, ZASS_TYPE, "connected ok... initializing");
307 if (send_initreq(p, auth) < 0)
309 gw_log(GW_LOG_FATAL, ZASS_TYPE, "Failed to send init");
317 if (zass_openresult(p, complete) < 0)
319 return p; /* all done */
323 * Convert the egate (ccl2rpn) version of RPN to YAZ RPN.
325 static Z_RPNStructure *rpn2rpn(ODR o, struct ccl_rpn_node *q)
327 Z_RPNStructure *r = odr_malloc(o, sizeof(*r));
328 Z_AttributesPlusTerm *t;
329 struct ccl_rpn_attr *i;
331 static int op[] = { Z_Operator_and, Z_Operator_or, Z_Operator_and_not };
332 Z_AttributeElement **elements;
333 int num_attributes = 0;
338 r->which = Z_RPNStructure_simple;
339 r->u.simple = odr_malloc(o, sizeof(Z_Operand));
340 r->u.simple->which = Z_Operand_APT;
341 r->u.simple->u.attributesPlusTerm = t = odr_malloc(o, sizeof(*t));
342 t->term = odr_malloc(o, sizeof(Z_Term));
343 t->term->which = Z_Term_general;
344 t->term->u.general = odr_malloc(o, sizeof(Odr_oct));
345 t->term->u.general->len = t->term->u.general->size =
347 t->term->u.general->buf = odr_malloc(o, t->term->u.general->size);
348 memcpy(t->term->u.general->buf, q->u.t.term,
349 t->term->u.general->len);
350 elements = odr_malloc(o, sizeof(Z_AttributeElement*) * 100);
352 t->attributes = (Z_AttributeList *)
353 odr_malloc (o, sizeof(*t->attributes));
354 t->attributes->attributes = elements;
356 t->attributeList = elements;
358 for (i = q->u.t.attr_list; i && num_attributes < 100; i = i->next)
360 Z_AttributeElement *a;
362 elements[num_attributes++] = a =
363 odr_malloc(o, sizeof(*a));
364 a->attributeType = odr_malloc(o, sizeof(int));
365 *a->attributeType = i->type;
367 a->which = Z_AttributeValue_numeric;
368 a->value.numeric = odr_malloc(o, sizeof(*a->value.numeric));
369 *a->value.numeric = i->value;
372 t->attributes->num_attributes = num_attributes;
374 t->num_attributes = num_attributes;
378 r->which = Z_RPNStructure_simple;
379 r->u.simple = odr_malloc(o, sizeof(Z_Operand));
380 r->u.simple->which = Z_Operand_resultSetId;
381 r->u.simple->u.resultSetId = odr_malloc(o, len =
382 strlen(q->u.setname) + 1);
383 memcpy(r->u.simple->u.resultSetId, q->u.setname, len);
385 case CCL_RPN_AND: case CCL_RPN_OR: case CCL_RPN_NOT:
386 r->which = Z_RPNStructure_complex;
387 r->u.complex = odr_malloc(o, sizeof(Z_Complex));
388 if (!(r->u.complex->s1 = rpn2rpn(o, q->u.p[0])) ||
389 !(r->u.complex->s2 = rpn2rpn(o, q->u.p[1])))
391 r->u.complex->roperator = odr_malloc(o, sizeof(Z_Operator));
392 r->u.complex->roperator->which = op[q->kind];
393 r->u.complex->roperator->u.op_and = "";
396 gw_log(GW_LOG_FATAL, ZASS_TYPE, "Bad operator in RPN");
401 const struct zass_searchent *zass_searchresult(ZASS a, int *complete)
403 static struct zass_searchent r;
405 Z_SearchResponse *res;
407 if (!(apdu = get_apdu(a, complete)))
409 if (apdu->which != Z_APDU_searchResponse)
411 gw_log(GW_LOG_FATAL, ZASS_TYPE, "Expected searchresponse, got #%d",
415 res = apdu->u.searchResponse;
416 r.status = *res->searchStatus;
417 r.num = *res->resultCount;
418 r.status = res->resultSetStatus ? *res->resultSetStatus : 0;
423 if (res->records->which != Z_Records_NSD)
424 gw_log(GW_LOG_WARN, ZASS_TYPE, "Unexpected record types in SchR");
430 Z_DiagRec *dr = &dr_info;
431 dr_info.which = Z_DiagRec_defaultFormat;
432 dr_info.u.defaultFormat = res->records->u.nonSurrogateDiagnostic;
434 Z_DiagRec *dr = res->records->u.nonSurrogateDiagnostic;
436 if (dr->which != Z_DiagRec_defaultFormat ||
437 !(id = oid_getentbyoid
438 (dr->u.defaultFormat->diagnosticSetId)) ||
439 id->oclass != CLASS_DIAGSET ||
440 id->value != VAL_BIB1)
441 gw_log(GW_LOG_WARN, ZASS_TYPE,
442 "Missing or unknown diagset - ignoring error!");
445 r.errcode = *dr->u.defaultFormat->condition;
447 switch (dr->u.defaultFormat->which)
449 case Z_DefaultDiagFormat_v2Addinfo:
450 strncpy(r.errstring, dr->u.defaultFormat->u.v2Addinfo,
452 r.errstring[511] = '\0';
454 case Z_DefaultDiagFormat_v3Addinfo:
455 strncpy(r.errstring, dr->u.defaultFormat->u.v3Addinfo,
457 r.errstring[511] = '\0';
461 if (dr->u.defaultFormat->addinfo)
463 strncpy(r.errstring, dr->u.defaultFormat->addinfo, 512);
464 r.errstring[511] = '\0';
473 const struct zass_searchent *zass_search(ZASS a, struct ccl_rpn_node *query,
474 char *resname, char *databases, int *complete)
479 Z_SearchRequest *req;
481 int smallSetUpperBound = 0;
482 int largeSetLowerBound = 1;
483 int mediumSetPresentNumber = 0;
484 int replaceIndicator = 1;
487 apdu = zget_APDU (a->encode, Z_APDU_searchRequest);
488 req = apdu->u.searchRequest;
490 req->smallSetUpperBound = &smallSetUpperBound;
491 req->largeSetLowerBound = &largeSetLowerBound;
492 req->mediumSetPresentNumber = &mediumSetPresentNumber;
493 req->replaceIndicator = &replaceIndicator;
494 req->resultSetName = resname;
495 req->num_databaseNames = 0;
496 req->databaseNames = datab;
497 while (*databases && req->num_databaseNames < 100)
502 while (*p && !isspace(*p) && *p != ',')
510 req->databaseNames[req->num_databaseNames] = odr_malloc(a->encode,
511 (p - databases) + 1);
512 memcpy(req->databaseNames[req->num_databaseNames], databases,
514 req->databaseNames[req->num_databaseNames++][p-databases] = '\0';
516 databases = p + more;
519 q.which = Z_Query_type_1;
521 bib1.proto = PROTO_Z3950;
522 bib1.oclass = CLASS_ATTSET;
523 bib1.value = VAL_BIB1;
524 rpnq.attributeSetId = oid_getoidbyent(&bib1);
528 if (!(rpnq.RPNStructure = rpn2rpn(a->encode, query)))
530 if (send_apdu(a, apdu) < 0)
538 return zass_searchresult(a, complete);
542 * Triple indirection - that's kinda heavy. We'll fix it later.
543 * There are worse things around, though. Like ZDist.
545 void get_diagrecs(zass_record ***p, Z_DiagRec **r, int num)
547 **p = malloc(sizeof(***p));
550 (**p)->errstring[0] = '\0';
551 if ((*r)->which == Z_DiagRec_defaultFormat)
553 (**p)->errcode = *(*r)->u.defaultFormat->condition;
555 switch ((*r)->u.defaultFormat->which)
557 case Z_DefaultDiagFormat_v2Addinfo:
558 strncpy((**p)->errstring, (*r)->u.defaultFormat->u.v2Addinfo, 200);
559 (**p)->errstring[200] = 0;
561 case Z_DefaultDiagFormat_v3Addinfo:
562 strncpy((**p)->errstring, (*r)->u.defaultFormat->u.v3Addinfo, 200);
563 (**p)->errstring[200] = 0;
567 if ((*r)->u.defaultFormat->addinfo)
569 strncpy((**p)->errstring, (*r)->u.defaultFormat->addinfo, 200);
570 (**p)->errstring[200] = 0;
576 (**p)->which = ZASS_REC_DIAG;
580 void get_responserecords(zass_record ***p, Z_NamePlusRecordList *recs)
584 for (i = 0; i < recs->num_records; i++)
586 Z_NamePlusRecord *record;
588 record = recs->records[i];
589 if (record->which == Z_NamePlusRecord_surrogateDiagnostic)
590 get_diagrecs(p, &record->u.surrogateDiagnostic, 1);
593 Z_DatabaseRecord *r = record->u.databaseRecord;
596 **p = malloc(sizeof(***p));
599 if (!(recform = oid_getentbyoid(r->direct_reference)) ||
600 recform->oclass != CLASS_RECSYN)
602 gw_log(GW_LOG_WARN, ZASS_TYPE, "Unknown or bad record syntax");
603 (**p)->which = ZASS_REC_UNKNOWN;
606 switch (recform->value)
608 case VAL_USMARC: (**p)->which = ZASS_REC_USMARC; break;
609 case VAL_DANMARC: (**p)->which = ZASS_REC_DANMARC; break;
611 gw_log(GW_LOG_WARN, ZASS_TYPE, "Unknown recsyn");
612 (**p)->which = ZASS_REC_UNKNOWN;
614 if (r->which != ODR_EXTERNAL_octet)
616 gw_log(GW_LOG_WARN, ZASS_TYPE, "Record wasn't octet-aligned");
621 if (!((**p)->record = malloc(r->u.octet_aligned->len + 1)))
623 gw_log(GW_LOG_FATAL, ZASS_TYPE, "malloc");
626 memcpy((**p)->record, r->u.octet_aligned->buf,
627 r->u.octet_aligned->len);
628 (**p)->record[r->u.octet_aligned->len] = '\0';
629 gw_log(ZASS_DEBUG, ZASS_TYPE, "Got a record of %d bytes",
630 r->u.octet_aligned->len);
637 static void zass_records_free(zass_record *p)
641 static int send_present(ZASS a, char *name, int start, int num,
645 Z_PresentRequest *req;
650 apdu = zget_APDU (a->encode, Z_APDU_presentRequest);
651 req = apdu->u.presentRequest;
653 req->resultSetId = name;
654 req->resultSetStartPoint = &start;
655 req->numberOfRecordsRequested = #
657 recsyn.proto = PROTO_Z3950;
658 recsyn.oclass = CLASS_RECSYN;
660 req->preferredRecordSyntax = oid_getoidbyent(&recsyn);
662 req->preferredRecordSyntax = 0;
664 return send_apdu(a, apdu);
668 * Note that 1== first record.
669 * TODO: make this baby operate in nonblocking mode, too.
671 const struct zass_presentent *zass_present(ZASS a, char *resname, int start,
672 int num, int *complete)
674 static struct zass_presentent r;
675 zass_record **rec = &r.records;
682 zass_records_free(r.records);
688 Z_PresentResponse *res;
690 Z_DiagRec dr, *dr_p = &dr;
693 gw_log(ZASS_DEBUG, ZASS_TYPE,
694 "Fetching %d records from # %d", num - r.num, start);
695 if (send_present(a, resname, start, num - r.num, VAL_USMARC) < 0)
697 if (!(apdu = get_apdu(a, complete)))
703 if (apdu->which != Z_APDU_presentResponse)
705 gw_log(GW_LOG_FATAL, ZASS_TYPE, "Expected presentresponse, got #%d",
709 res = apdu->u.presentResponse;
710 r.presentstatus = *res->presentStatus;
711 r.num += *res->numberOfRecordsReturned;
712 if (*res->numberOfRecordsReturned == 0)
714 gw_log(GW_LOG_WARN, ZASS_TYPE, "Got 0 records from target");
717 r.nextpos = *res->nextResultSetPosition;
719 switch (res->records->which)
721 case Z_Records_DBOSD:
722 get_responserecords(&rec,
723 res->records->u.databaseOrSurDiagnostics);
727 dr.which = Z_DiagRec_defaultFormat;
728 dr.u.defaultFormat = res->records->u.nonSurrogateDiagnostic;
729 get_diagrecs(&rec, &dr_p, 1);
731 get_diagrecs(&rec, &res->records->u.nonSurrogateDiagnostic, 1);
735 gw_log(GW_LOG_WARN, ZASS_TYPE, "Bad tag in response rec.");
739 while (num - r.num && start);
743 const struct zass_presentent *zass_presentresult(ZASS a, int *complete)