4 * Z39.50 API for the Email gateway - YAZ version
6 * $Log: zaccess-yaz.c,v $
7 * Revision 1.9 1995/05/03 10:15:43 quinn
8 * Fixed bug in the get_record loop - crashed when diagrec was received.
10 * Revision 1.8 1995/04/28 14:18:38 quinn
11 * *** empty log message ***
13 * Revision 1.7 1995/04/21 16:29:32 quinn
16 * Revision 1.6 1995/04/21 12:58:36 adam
19 * Revision 1.5 1995/04/20 15:25:32 quinn
22 * Revision 1.4 1995/04/19 16:02:28 adam
25 * Revision 1.3 1995/04/19 12:55:15 quinn
28 * Revision 1.2 1995/04/19 09:24:02 quinn
29 * Fixed bug in zass_open - variable initialized after use
31 * Revision 1.1 1995/04/17 11:26:53 quinn
32 * Added YAZ version of zaccess
37 * Interface to the YAZ Z39.50 toolkit.
56 struct zass /* Z-assoc */
58 COMSTACK ass; /* comstack association handle */
61 int fd; /* low-level socket (for select only) */
64 int preferredmessagesize;
69 static Z_APDU *get_apdu(struct zass *z, int *complete)
74 if ((res = cs_get(z->ass, &z->inbuf, &z->inbuflen)) <= 0)
76 gw_log(GW_LOG_WARN, ZASS_TYPE, "cs_get failed");
88 odr_setbuf(z->decode, z->inbuf, res);
89 if (!z_APDU(z->decode, &ap, 0))
91 gw_log(GW_LOG_WARN, ZASS_TYPE, "decode: %s",
92 odr_errlist[odr_geterror(z->decode)]);
102 static int send_apdu(struct zass *z, Z_APDU *a)
107 if (!z_APDU(z->encode, &a, 0))
109 gw_log(GW_LOG_FATAL, ZASS_TYPE, "encoding initreq");
112 buf = odr_getbuf(z->encode, &len);
113 if (cs_put(z->ass, buf, len) < 0)
115 gw_log(GW_LOG_FATAL, ZASS_TYPE, "cs_put");
118 odr_reset(z->encode); /* release odr_allocated structures */
122 static int send_initreq(struct zass *p, char *auth)
126 Odr_bitmask options, protocolVersion;
128 Z_IdAuthentication idauth;
130 a.which = Z_APDU_initRequest;
131 a.u.initRequest = &init;
132 init.referenceId = 0;
133 init.options = &options;
134 ODR_MASK_ZERO(&options);
135 ODR_MASK_SET(&options, Z_Options_search);
136 ODR_MASK_SET(&options, Z_Options_present);
137 ODR_MASK_SET(&options, Z_Options_delSet);
138 init.protocolVersion = &protocolVersion;
139 ODR_MASK_ZERO(&protocolVersion);
140 ODR_MASK_SET(&protocolVersion, Z_ProtocolVersion_1);
141 ODR_MASK_SET(&protocolVersion, Z_ProtocolVersion_2);
142 init.preferredMessageSize = &p->preferredmessagesize;
143 init.maximumRecordSize = &p->maxrecordsize;
147 init.idAuthentication = &idauth;
148 idauth.which = Z_IdAuthentication_open;
149 idauth.u.open = auth;
152 init.idAuthentication = 0;
153 init.implementationId = ZASS_ID;
154 sprintf(name, "%s (YAZ protocol layer)", ZASS_NAME);
155 init.implementationName = name;
156 init.implementationVersion = ZASS_VERSION;
157 init.userInformationField = 0;
158 if (send_apdu(p, &a) < 0)
163 int zass_fileno(ZASS a)
168 int zass_openresult(ZASS p, int *complete)
173 if (!(ap = get_apdu(p, complete)))
175 if (ap->which != Z_APDU_initResponse)
177 gw_log(GW_LOG_WARN, ZASS_TYPE, "bad APDU");
180 res = ap->u.initResponse;
183 gw_log(GW_LOG_WARN, ZASS_TYPE, "Negative result on initres");
186 p->preferredmessagesize = *res->preferredMessageSize;
187 p->maxrecordsize = *res->maximumRecordSize;
188 if (res->implementationId)
189 gw_log(GW_LOG_NOTICE, ZASS_TYPE, "imp. ID : %s",
190 res->implementationId);
191 if (res->implementationName)
192 gw_log(GW_LOG_NOTICE, ZASS_TYPE, "imp. ID : %s",
193 res->implementationName);
194 if (res->implementationVersion)
195 gw_log(GW_LOG_NOTICE, ZASS_TYPE, "imp. ID : %s",
196 res->implementationVersion);
197 gw_log(ZASS_DEBUG, ZASS_TYPE, "Initialized ok");
201 ZASS zass_open(char *host, int port, int *complete, char *auth)
207 if (!(p = malloc(sizeof(*p))))
209 gw_log(GW_LOG_FATAL|GW_LOG_ERRNO, ZASS_TYPE, "malloc");
219 if (!(p->encode = odr_createmem(ODR_ENCODE)) ||
220 !(p->decode = odr_createmem(ODR_DECODE)))
222 gw_log(GW_LOG_FATAL|GW_LOG_ERRNO, ZASS_TYPE, "odr_createmem");
225 p->maxrecordsize = ZASS_MAXRECORDSIZE;
226 p->preferredmessagesize = ZASS_PREFERREDMESSAGESIZE;
227 if (!(p->ass = cs_create(tcpip_type, 1, CS_Z3950)))
229 gw_log(GW_LOG_FATAL|GW_LOG_ERRNO, ZASS_TYPE, "cs_create");
234 sprintf(addstr, "%s:%d", host, port);
235 if (!(address = tcpip_strtoaddr(addstr)))
237 gw_log(GW_LOG_FATAL, ZASS_TYPE, "failed to resolve %s", addstr);
240 p->fd = cs_fileno(p->ass);
241 gw_log(ZASS_DEBUG, ZASS_TYPE, "Connecting to %s", addstr);
242 if (cs_connect(p->ass, address) < 0)
244 gw_log(GW_LOG_FATAL, ZASS_TYPE, "Failed to connect to %s", addstr);
247 gw_log(ZASS_DEBUG, ZASS_TYPE, "connected ok... initializing");
248 if (send_initreq(p, auth) < 0)
250 gw_log(GW_LOG_FATAL, ZASS_TYPE, "Failed to send init");
258 if (zass_openresult(p, complete) < 0)
260 return p; /* all done */
264 * Convert the egate (ccl2rpn) version of RPN to YAZ RPN.
266 static Z_RPNStructure *rpn2rpn(ODR o, struct ccl_rpn_node *q)
268 Z_RPNStructure *r = odr_malloc(o, sizeof(*r));
269 Z_AttributesPlusTerm *t;
270 struct ccl_rpn_attr *i;
272 static int op[] = { Z_Operator_and, Z_Operator_or, Z_Operator_and_not };
277 r->which = Z_RPNStructure_simple;
278 r->u.simple = odr_malloc(o, sizeof(Z_Operand));
279 r->u.simple->which = Z_Operand_APT;
280 r->u.simple->u.attributesPlusTerm = t = odr_malloc(o, sizeof(*t));
281 t->term = odr_malloc(o, sizeof(Z_Term));
282 t->term->which = Z_Term_general;
283 t->term->u.general = odr_malloc(o, sizeof(Odr_oct));
284 t->term->u.general->len = t->term->u.general->size =
286 t->term->u.general->buf = odr_malloc(o, t->term->u.general->size);
287 memcpy(t->term->u.general->buf, q->u.t.term,
288 t->term->u.general->len);
289 t->num_attributes = 0;
290 t->attributeList = odr_malloc(o, sizeof(Z_AttributeElement*) * 100);
291 for (i = q->u.t.attr_list; i && t->num_attributes < 100;
294 Z_AttributeElement *a;
296 t->attributeList[t->num_attributes++] = a =
297 odr_malloc(o, sizeof(*a));
298 a->attributeType = odr_malloc(o, sizeof(int));
299 *a->attributeType = i->type;
300 a->attributeValue = odr_malloc(o, sizeof(*a));
301 *a->attributeValue = i->value;
305 r->which = Z_RPNStructure_simple;
306 r->u.simple = odr_malloc(o, sizeof(Z_Operand));
307 r->u.simple->which = Z_Operand_resultSetId;
308 r->u.simple->u.resultSetId = odr_malloc(o, len =
309 strlen(q->u.setname) + 1);
310 memcpy(r->u.simple->u.resultSetId, q->u.setname, len);
312 case CCL_RPN_AND: case CCL_RPN_OR: case CCL_RPN_NOT:
313 r->which = Z_RPNStructure_complex;
314 r->u.complex = odr_malloc(o, sizeof(Z_Complex));
315 if (!(r->u.complex->s1 = rpn2rpn(o, q->u.p[0])) ||
316 !(r->u.complex->s2 = rpn2rpn(o, q->u.p[1])))
318 r->u.complex->operator = odr_malloc(o, sizeof(Z_Operator));
319 r->u.complex->operator->which = op[q->kind];
320 r->u.complex->operator->u.and = "";
323 gw_log(GW_LOG_FATAL, ZASS_TYPE, "Bad operator in RPN");
328 const struct zass_searchent *zass_searchresult(ZASS a, int *complete)
330 static struct zass_searchent r;
332 Z_SearchResponse *res;
334 if (!(apdu = get_apdu(a, complete)))
336 if (apdu->which != Z_APDU_searchResponse)
338 gw_log(GW_LOG_FATAL, ZASS_TYPE, "Expected searchresponse, got #%d",
342 res = apdu->u.searchResponse;
343 r.status = *res->searchStatus;
344 r.num = *res->resultCount;
345 r.status = res->resultSetStatus ? *res->resultSetStatus : 0;
350 if (res->records->which != Z_Records_NSD)
351 gw_log(GW_LOG_WARN, ZASS_TYPE, "Unexpected record types in SchR");
355 Z_DiagRec *dr = res->records->u.nonSurrogateDiagnostic;
357 if (!(id = oid_getentbyoid(dr->diagnosticSetId)) ||
358 id->class != CLASS_DIAGSET || id->value != VAL_BIB1)
359 gw_log(GW_LOG_WARN, ZASS_TYPE,
360 "Missing or unknown diagset - ignoring error!");
363 r.errcode = *dr->condition;
366 strncpy(r.errstring, dr->addinfo, 512);
367 r.errstring[511] = '\0';
375 const struct zass_searchent *zass_search(ZASS a, struct ccl_rpn_node *query,
376 char *resname, char *databases, int *complete)
383 int smallSetUpperBound = 0;
384 int largeSetLowerBound = 1;
385 int mediumSetPresentNumber = 0;
386 int replaceIndicator = 1;
389 apdu.which = Z_APDU_searchRequest;
390 apdu.u.searchRequest = &req;
392 req.smallSetUpperBound = &smallSetUpperBound;
393 req.largeSetLowerBound = &largeSetLowerBound;
394 req.mediumSetPresentNumber = &mediumSetPresentNumber;
395 req.replaceIndicator = &replaceIndicator;
396 req.resultSetName = resname;
397 req.num_databaseNames = 0;
398 req.databaseNames = datab;
399 while (*databases && req.num_databaseNames < 100)
404 while (*p && !isspace(*p))
413 req.databaseNames[req.num_databaseNames] = odr_malloc(a->encode,
414 (p - databases) + 1);
415 strcpy(req.databaseNames[req.num_databaseNames++], databases);
417 databases = p + more;
419 req.smallSetElementSetNames = 0;
420 req.mediumSetElementSetNames = 0;
421 req.preferredRecordSyntax = 0;
423 q.which = Z_Query_type_1;
425 bib1.proto = PROTO_Z3950;
426 bib1.class = CLASS_ATTSET;
427 bib1.value = VAL_BIB1;
428 rpnq.attributeSetId = oid_getoidbyent(&bib1);
432 if (!(rpnq.RPNStructure = rpn2rpn(a->encode, query)))
434 if (send_apdu(a, &apdu) < 0)
442 return zass_searchresult(a, complete);
446 * Triple indirection - that's kinda heavy. We'll fix it later.
447 * There are worse things around, though. Like ZDist.
449 void get_diagrec(zass_record ***p, Z_DiagRec *r)
451 **p = malloc(sizeof(***p));
453 (**p)->errcode = *r->condition;
456 strncpy((**p)->errstring, r->addinfo, 200);
457 (**p)->errstring[200] = 0;
460 (**p)->errstring[0] = '\0';
461 (**p)->which = ZASS_REC_DIAG;
465 void get_responserecords(zass_record ***p, Z_NamePlusRecordList *recs)
469 for (i = 0; i < recs->num_records; i++)
471 Z_NamePlusRecord *record;
473 record = recs->records[i];
474 if (record->which == Z_NamePlusRecord_surrogateDiagnostic)
475 get_diagrec(p, record->u.surrogateDiagnostic);
478 Z_DatabaseRecord *r = record->u.databaseRecord;
481 **p = malloc(sizeof(***p));
484 if (!(recform = oid_getentbyoid(r->direct_reference)) ||
485 recform->class != CLASS_RECSYN)
487 gw_log(GW_LOG_WARN, ZASS_TYPE, "Unknown or bad record syntax");
488 (**p)->which = ZASS_REC_UNKNOWN;
491 switch (recform->value)
493 case VAL_USMARC: (**p)->which = ZASS_REC_USMARC; break;
495 gw_log(GW_LOG_WARN, ZASS_TYPE, "Unknown recsyn");
496 (**p)->which = ZASS_REC_UNKNOWN;
498 if (r->which != ODR_EXTERNAL_octet)
500 gw_log(GW_LOG_WARN, ZASS_TYPE, "Record wasn't octet-aligned");
505 if (!((**p)->record = malloc(r->u.octet_aligned->len + 1)))
507 gw_log(GW_LOG_FATAL, ZASS_TYPE, "malloc");
510 memcpy((**p)->record, r->u.octet_aligned->buf,
511 r->u.octet_aligned->len);
512 (**p)->record[r->u.octet_aligned->len] = '\0';
513 gw_log(ZASS_DEBUG, ZASS_TYPE, "Got a record of %d bytes",
514 r->u.octet_aligned->len);
521 static void zass_records_free(zass_record *p)
525 static int send_present(ZASS a, char *name, int start, int num,
529 Z_PresentRequest req;
534 apdu.which = Z_APDU_presentRequest;
535 apdu.u.presentRequest = &req;
537 req.resultSetId = name;
538 req.resultSetStartPoint = &start;
539 req.numberOfRecordsRequested = #
540 req.elementSetNames = 0;
542 recsyn.proto = PROTO_Z3950;
543 recsyn.class = CLASS_RECSYN;
545 req.preferredRecordSyntax = oid_getoidbyent(&recsyn);
547 req.preferredRecordSyntax = 0;
549 return send_apdu(a, &apdu);
553 * Note that 1== first record.
554 * TODO: make this baby operate in nonblocking mode, too.
556 const struct zass_presentent *zass_present(ZASS a, char *resname, int start,
557 int num, int *complete)
559 static struct zass_presentent r;
560 zass_record **rec = &r.records;
567 zass_records_free(r.records);
573 Z_PresentResponse *res;
575 gw_log(ZASS_DEBUG, ZASS_TYPE,
576 "Fetching %d records from # %d", num - r.num, start);
577 if (send_present(a, resname, start, num - r.num, VAL_USMARC) < 0)
579 if (!(apdu = get_apdu(a, complete)))
585 if (apdu->which != Z_APDU_presentResponse)
587 gw_log(GW_LOG_FATAL, ZASS_TYPE, "Expected presentresponse, got #%d",
591 res = apdu->u.presentResponse;
592 r.presentstatus = *res->presentStatus;
593 r.num += *res->numberOfRecordsReturned;
594 if (*res->numberOfRecordsReturned == 0)
596 gw_log(GW_LOG_WARN, ZASS_TYPE, "Got 0 records from target");
599 r.nextpos = *res->nextResultSetPosition;
601 switch (res->records->which)
603 case Z_Records_DBOSD:
604 get_responserecords(&rec,
605 res->records->u.databaseOrSurDiagnostics);
608 get_diagrec(&rec, res->records->u.nonSurrogateDiagnostic);
611 gw_log(GW_LOG_WARN, ZASS_TYPE, "Bad tag in response rec.");
615 while (num - r.num && start);
619 const struct zass_presentent *zass_presentresult(ZASS a, int *complete)