4 * Z39.50 API for the Email gateway - YAZ version
6 * $Log: zaccess-yaz.c,v $
7 * Revision 1.4 1995/04/19 16:02:28 adam
10 * Revision 1.3 1995/04/19 12:55:15 quinn
13 * Revision 1.2 1995/04/19 09:24:02 quinn
14 * Fixed bug in zass_open - variable initialized after use
16 * Revision 1.1 1995/04/17 11:26:53 quinn
17 * Added YAZ version of zaccess
23 * Interface to the Z39.50 toolkit.
42 struct zass /* Z-assoc */
44 COMSTACK ass; /* comstack association handle */
47 int fd; /* low-level socket (for select only) */
49 int preferredmessagesize;
50 char *outbuf; /* intermediary buffer */
55 static Z_APDU *get_apdu(struct zass *z)
60 if ((res = cs_get(z->ass, &z->inbuf, &z->inbuflen)) <= 0)
62 gw_log(GW_LOG_WARN, ZASS_TYPE, "cs_get failed");
66 odr_setbuf(z->decode, z->inbuf, res);
67 if (!z_APDU(z->decode, &ap, 0))
69 gw_log(GW_LOG_WARN, ZASS_TYPE, "decode: %s",
70 odr_errlist[odr_geterror(z->decode)]);
76 static int send_apdu(struct zass *z, Z_APDU *a)
81 if (!z_APDU(z->encode, &a, 0))
83 gw_log(GW_LOG_FATAL, ZASS_TYPE, "encoding initreq");
86 buf = odr_getbuf(z->encode, &len);
87 if (cs_put(z->ass, buf, len) < 0)
89 gw_log(GW_LOG_FATAL, ZASS_TYPE, "cs_put");
96 static int send_initreq(struct zass *p, char *auth)
100 Odr_bitmask options, protocolVersion;
102 Z_IdAuthentication idauth;
104 a.which = Z_APDU_initRequest;
105 a.u.initRequest = &init;
106 init.referenceId = 0;
107 init.options = &options;
108 ODR_MASK_ZERO(&options);
109 ODR_MASK_SET(&options, Z_Options_search);
110 ODR_MASK_SET(&options, Z_Options_present);
111 ODR_MASK_SET(&options, Z_Options_delSet);
112 init.protocolVersion = &protocolVersion;
113 ODR_MASK_ZERO(&protocolVersion);
114 ODR_MASK_SET(&protocolVersion, Z_ProtocolVersion_1);
115 ODR_MASK_SET(&protocolVersion, Z_ProtocolVersion_2);
116 init.preferredMessageSize = &p->preferredmessagesize;
117 init.maximumRecordSize = &p->maxrecordsize;
119 init.idAuthentication = 0;
122 init.idAuthentication = &idauth;
123 idauth.which = Z_IdAuthentication_open;
124 idauth.u.open = auth;
126 init.implementationId = ZASS_ID;
127 sprintf(name, "%s (YAZ protocol layer)", ZASS_NAME);
128 init.implementationName = name;
129 init.implementationVersion = ZASS_VERSION;
130 init.userInformationField = 0;
131 if (send_apdu(p, &a) < 0)
136 static int receive_initres(struct zass *p)
141 if (!(ap = get_apdu(p)))
143 if (ap->which != Z_APDU_initResponse)
145 gw_log(GW_LOG_WARN, ZASS_TYPE, "bad APDU");
148 res = ap->u.initResponse;
151 gw_log(GW_LOG_WARN, ZASS_TYPE, "Negative result on initres");
154 p->preferredmessagesize = *res->preferredMessageSize;
155 p->maxrecordsize = *res->maximumRecordSize;
156 if (res->implementationId)
157 gw_log(GW_LOG_NOTICE, ZASS_TYPE, "imp. ID : %s",
158 res->implementationId);
159 if (res->implementationName)
160 gw_log(GW_LOG_NOTICE, ZASS_TYPE, "imp. ID : %s",
161 res->implementationName);
162 if (res->implementationVersion)
163 gw_log(GW_LOG_NOTICE, ZASS_TYPE, "imp. ID : %s",
164 res->implementationVersion);
165 gw_log(ZASS_DEBUG, ZASS_TYPE, "Initialized ok");
169 ZASS zass_open(char *host, int port, char *auth)
175 if (!(p = malloc(sizeof(*p))))
177 gw_log(GW_LOG_FATAL|GW_LOG_ERRNO, ZASS_TYPE, "malloc");
180 if (!(p->encode = odr_createmem(ODR_ENCODE)) ||
181 !(p->decode = odr_createmem(ODR_DECODE)))
183 gw_log(GW_LOG_FATAL|GW_LOG_ERRNO, ZASS_TYPE, "odr_createmem");
186 p->maxrecordsize = ZASS_MAXRECORDSIZE;
187 p->preferredmessagesize = ZASS_PREFERREDMESSAGESIZE;
188 if (!(p->outbuf = malloc(p->maxrecordsize + 1024)))
190 gw_log(GW_LOG_FATAL|GW_LOG_ERRNO, ZASS_TYPE, "malloc");
193 odr_setbuf(p->encode, p->outbuf, p->maxrecordsize + 1024);
194 if (!(p->ass = cs_create(tcpip_type, 1, CS_Z3950)))
196 gw_log(GW_LOG_FATAL|GW_LOG_ERRNO, ZASS_TYPE, "cs_create");
199 sprintf(addstr, "%s:%d", host, port);
200 if (!(address = tcpip_strtoaddr(addstr)))
202 gw_log(GW_LOG_FATAL, ZASS_TYPE, "failed to resolve %s", addstr);
205 p->fd = cs_fileno(p->ass);
206 gw_log(ZASS_DEBUG, ZASS_TYPE, "Connecting to %s", addstr);
207 if (cs_connect(p->ass, address) < 0)
209 gw_log(GW_LOG_FATAL, ZASS_TYPE, "Failed to connect to %s", addstr);
212 gw_log(ZASS_DEBUG, ZASS_TYPE, "connected ok");
215 if (send_initreq(p, auth) < 0 || receive_initres(p) < 0)
217 gw_log(GW_LOG_FATAL, ZASS_TYPE, "Failed to initialize");
220 gw_log(ZASS_DEBUG, ZASS_TYPE, "Sent init request");
224 static Z_RPNStructure *rpn2rpn(ODR o, struct ccl_rpn_node *q)
226 Z_RPNStructure *r = odr_malloc(o, sizeof(*r));
227 Z_AttributesPlusTerm *t;
228 struct ccl_rpn_attr *i;
230 static int op[] = { Z_Operator_and, Z_Operator_or, Z_Operator_and_not };
235 r->which = Z_RPNStructure_simple;
236 r->u.simple = odr_malloc(o, sizeof(Z_Operand));
237 r->u.simple->which = Z_Operand_APT;
238 r->u.simple->u.attributesPlusTerm = t = odr_malloc(o, sizeof(*t));
239 t->term = odr_malloc(o, sizeof(Z_Term));
240 t->term->which = Z_Term_general;
241 t->term->u.general = odr_malloc(o, sizeof(Odr_oct));
242 t->term->u.general->len = t->term->u.general->size =
244 t->term->u.general->buf = odr_malloc(o, t->term->u.general->size);
245 memcpy(t->term->u.general->buf, q->u.t.term,
246 t->term->u.general->len);
247 t->num_attributes = 0;
248 t->attributeList = odr_malloc(o, sizeof(Z_AttributeElement*) * 100);
249 for (i = q->u.t.attr_list; i && t->num_attributes < 100;
252 Z_AttributeElement *a;
254 t->attributeList[t->num_attributes++] = a =
255 odr_malloc(o, sizeof(*a));
256 a->attributeType = odr_malloc(o, sizeof(int));
257 *a->attributeType = i->type;
258 a->attributeValue = odr_malloc(o, sizeof(*a));
259 *a->attributeValue = i->value;
263 r->which = Z_RPNStructure_simple;
264 r->u.simple = odr_malloc(o, sizeof(Z_Operand));
265 r->u.simple->which = Z_Operand_resultSetId;
266 r->u.simple->u.resultSetId = odr_malloc(o, len =
267 strlen(q->u.setname));
268 memcpy(r->u.simple->u.resultSetId, q->u.setname, len);
270 case CCL_RPN_AND: case CCL_RPN_OR: case CCL_RPN_NOT:
271 r->which = Z_RPNStructure_complex;
272 r->u.complex = odr_malloc(o, sizeof(Z_Complex));
273 if (!(r->u.complex->s1 = rpn2rpn(o, q->u.p[0])) ||
274 !(r->u.complex->s2 = rpn2rpn(o, q->u.p[1])))
276 r->u.complex->operator = odr_malloc(o, sizeof(Z_Operator));
277 r->u.complex->operator->which = op[q->kind];
278 r->u.complex->operator->u.and = "";
281 gw_log(GW_LOG_FATAL, ZASS_TYPE, "Bad operator in RPN");
286 static const struct zass_searchent *search_result(ZASS a)
288 static struct zass_searchent r;
290 Z_SearchResponse *res;
292 if (!(apdu = get_apdu(a)))
294 if (apdu->which != Z_APDU_searchResponse)
296 gw_log(GW_LOG_FATAL, ZASS_TYPE, "Expected searchresponse, got #%d",
300 res = apdu->u.searchResponse;
301 r.status = *res->searchStatus;
302 r.num = *res->resultCount;
303 r.status = res->resultSetStatus ? *res->resultSetStatus : 0;
308 if (res->records->which != Z_Records_NSD)
309 gw_log(GW_LOG_WARN, ZASS_TYPE, "Unexpected record types in SchR");
313 Z_DiagRec *dr = res->records->u.nonSurrogateDiagnostic;
315 if (!(id = oid_getentbyoid(dr->diagnosticSetId)) ||
316 id->class != CLASS_DIAGSET || id->value != VAL_BIB1)
317 gw_log(GW_LOG_WARN, ZASS_TYPE,
318 "Missing or unknown diagset - ignoring error!");
321 r.errcode = *dr->condition;
324 strncpy(r.errstring, dr->addinfo, 512);
325 r.errstring[511] = '\0';
333 const struct zass_searchent *zass_search(ZASS a, struct ccl_rpn_node *query,
334 char *resname, char *databases)
341 int smallSetUpperBound = 0;
342 int largeSetLowerBound = 1;
343 int mediumSetPresentNumber = 0;
344 int replaceIndicator = 1;
347 apdu.which = Z_APDU_searchRequest;
348 apdu.u.searchRequest = &req;
350 req.smallSetUpperBound = &smallSetUpperBound;
351 req.largeSetLowerBound = &largeSetLowerBound;
352 req.mediumSetPresentNumber = &mediumSetPresentNumber;
353 req.replaceIndicator = &replaceIndicator;
354 req.resultSetName = resname;
355 req.num_databaseNames = 0;
356 req.databaseNames = datab;
357 while (*databases && req.num_databaseNames < 100)
362 while (*p && !isspace(*p))
371 req.databaseNames[req.num_databaseNames] = odr_malloc(a->encode,
372 (p - databases) + 1);
373 strcpy(req.databaseNames[req.num_databaseNames++], databases);
375 databases = p + more;
377 req.smallSetElementSetNames = 0;
378 req.mediumSetElementSetNames = 0;
379 req.preferredRecordSyntax = 0;
381 q.which = Z_Query_type_1;
383 bib1.proto = PROTO_Z3950;
384 bib1.class = CLASS_ATTSET;
385 bib1.value = VAL_BIB1;
386 rpnq.attributeSetId = oid_getoidbyent(&bib1);
387 if (!(rpnq.RPNStructure = rpn2rpn(a->encode, query)))
389 if (send_apdu(a, &apdu) < 0)
392 return search_result(a);
396 * Triple indirection - that's kinda heavy. We'll fix it later.
397 * There are worse things around, though. Like ZDist.
399 void get_diagrec(zass_record ***p, Z_DiagRec *r)
401 **p = malloc(sizeof(***p));
403 (**p)->errcode = *r->condition;
406 strncpy((**p)->errstring, r->addinfo, 200);
407 (**p)->errstring[200] = 0;
410 (**p)->errstring[0] = '\0';
411 (**p)->which = ZASS_REC_DIAG;
415 void get_responserecords(zass_record ***p, Z_NamePlusRecordList *recs)
419 for (i = 0; i < recs->num_records; i++)
421 Z_NamePlusRecord *record;
423 record = recs->records[i];
424 if (record->which == Z_NamePlusRecord_surrogateDiagnostic)
425 get_diagrec(p, record->u.surrogateDiagnostic);
428 Z_DatabaseRecord *r = record->u.databaseRecord;
431 **p = malloc(sizeof(***p));
434 if (!(recform = oid_getentbyoid(r->direct_reference)) ||
435 recform->class != CLASS_RECSYN)
437 gw_log(GW_LOG_WARN, ZASS_TYPE, "Unknown or bad record syntax");
438 (**p)->which = ZASS_REC_UNKNOWN;
441 switch (recform->value)
443 case VAL_USMARC: (**p)->which = ZASS_REC_USMARC; break;
445 gw_log(GW_LOG_WARN, ZASS_TYPE, "Unknown recsyn");
446 (**p)->which = ZASS_REC_UNKNOWN;
448 if (r->which != ODR_EXTERNAL_octet)
450 gw_log(GW_LOG_WARN, ZASS_TYPE, "Record wasn't octet-aligned");
455 if (!((**p)->record = malloc(r->u.octet_aligned->len + 1)))
457 gw_log(GW_LOG_FATAL, ZASS_TYPE, "malloc");
460 memcpy((**p)->record, r->u.octet_aligned->buf,
461 r->u.octet_aligned->len);
462 (**p)->record[r->u.octet_aligned->len] = '\0';
463 gw_log(ZASS_DEBUG, ZASS_TYPE, "Got a record of %d bytes",
464 r->u.octet_aligned->len);
471 static void zass_records_free(zass_record *p)
475 static int send_present(ZASS a, char *name, int start, int num,
479 Z_PresentRequest req;
482 apdu.which = Z_APDU_presentRequest;
483 apdu.u.presentRequest = &req;
485 req.resultSetId = name;
486 req.resultSetStartPoint = &start;
487 req.numberOfRecordsRequested = #
488 req.elementSetNames = 0;
490 recsyn.proto = PROTO_Z3950;
491 recsyn.class = CLASS_RECSYN;
493 req.preferredRecordSyntax = oid_getoidbyent(&recsyn);
495 req.preferredRecordSyntax = 0;
497 return send_apdu(a, &apdu);
501 * Note that 1== first record.
503 const struct zass_presentent *zass_present(ZASS a, char *resname, int start,
506 static struct zass_presentent r;
507 zass_record **rec = &r.records;
512 zass_records_free(r.records);
518 Z_PresentResponse *res;
520 gw_log(ZASS_DEBUG, ZASS_TYPE,
521 "Fetching %d records from # %d", num - r.num, start);
522 if (send_present(a, resname, start, num - r.num, VAL_USMARC) < 0)
524 if (!(apdu = get_apdu(a)))
526 if (apdu->which != Z_APDU_presentResponse)
528 gw_log(GW_LOG_FATAL, ZASS_TYPE, "Expected presentresponse, got #%d",
532 res = apdu->u.presentResponse;
533 r.presentstatus = *res->presentStatus;
534 r.num += *res->numberOfRecordsReturned;
535 if (*res->numberOfRecordsReturned == 0)
537 gw_log(GW_LOG_WARN, ZASS_TYPE, "Got 0 records from target");
540 r.nextpos = *res->nextResultSetPosition;
542 switch (res->records->which)
544 case Z_Records_DBOSD:
545 get_responserecords(&rec,
546 res->records->u.databaseOrSurDiagnostics);
549 get_diagrec(&rec, res->records->u.nonSurrogateDiagnostic);
552 gw_log(GW_LOG_WARN, ZASS_TYPE, "Bad tag in response rec.");
556 while (num - r.num && start);