4 * Z39.50 API for the Email gateway - YAZ version
6 * $Log: zaccess-yaz.c,v $
7 * Revision 1.1 1995/04/17 11:26:53 quinn
8 * Added YAZ version of zaccess
14 * Interface to the Z39.50 toolkit.
33 struct zass /* Z-assoc */
35 COMSTACK ass; /* comstack association handle */
38 int fd; /* low-level socket (for select only) */
40 int preferredmessagesize;
41 char *outbuf; /* intermediary buffer */
46 static Z_APDU *get_apdu(struct zass *z)
51 if ((res = cs_get(z->ass, &z->inbuf, &z->inbuflen)) <= 0)
53 gw_log(GW_LOG_WARN, ZASS_TYPE, "cs_get failed");
57 odr_setbuf(z->decode, z->inbuf, res);
58 if (!z_APDU(z->decode, &ap, 0))
60 gw_log(GW_LOG_WARN, ZASS_TYPE, "decode: %s",
61 odr_errlist[odr_geterror(z->decode)]);
67 static int send_apdu(struct zass *z, Z_APDU *a)
72 if (!z_APDU(z->encode, &a, 0))
74 gw_log(GW_LOG_FATAL, ZASS_TYPE, "encoding initreq");
77 buf = odr_getbuf(z->encode, &len);
78 if (cs_put(z->ass, buf, len) < 0)
80 gw_log(GW_LOG_FATAL, ZASS_TYPE, "cs_put");
87 static int send_initreq(struct zass *p)
91 Odr_bitmask options, protocolVersion;
94 a.which = Z_APDU_initRequest;
95 a.u.initRequest = &init;
97 init.options = &options;
98 ODR_MASK_ZERO(&options);
99 ODR_MASK_SET(&options, Z_Options_search);
100 ODR_MASK_SET(&options, Z_Options_present);
101 ODR_MASK_SET(&options, Z_Options_delSet);
102 init.protocolVersion = &protocolVersion;
103 ODR_MASK_ZERO(&protocolVersion);
104 ODR_MASK_SET(&protocolVersion, Z_ProtocolVersion_1);
105 ODR_MASK_SET(&protocolVersion, Z_ProtocolVersion_2);
106 init.preferredMessageSize = &p->preferredmessagesize;
107 init.maximumRecordSize = &p->maxrecordsize;
108 init.idAuthentication = 0;
109 init.implementationId = ZASS_ID;
110 sprintf(name, "%s (YAZ protocol layer)", ZASS_NAME);
111 init.implementationName = name;
112 init.implementationVersion = ZASS_VERSION;
113 init.userInformationField = 0;
114 if (send_apdu(p, &a) < 0)
119 static int receive_initres(struct zass *p)
124 if (!(ap = get_apdu(p)))
126 if (ap->which != Z_APDU_initResponse)
128 gw_log(GW_LOG_WARN, ZASS_TYPE, "bad APDU");
131 res = ap->u.initResponse;
134 gw_log(GW_LOG_WARN, ZASS_TYPE, "Negative result on initres");
137 p->preferredmessagesize = *res->preferredMessageSize;
138 p->maxrecordsize = *res->maximumRecordSize;
139 if (res->implementationId)
140 gw_log(GW_LOG_NOTICE, ZASS_TYPE, "imp. ID : %s",
141 res->implementationId);
142 if (res->implementationName)
143 gw_log(GW_LOG_NOTICE, ZASS_TYPE, "imp. ID : %s",
144 res->implementationName);
145 if (res->implementationVersion)
146 gw_log(GW_LOG_NOTICE, ZASS_TYPE, "imp. ID : %s",
147 res->implementationVersion);
148 gw_log(ZASS_DEBUG, ZASS_TYPE, "Initialized ok");
152 ZASS zass_open(char *host, int port)
158 if (!(p = malloc(sizeof(*p))))
160 gw_log(GW_LOG_FATAL|GW_LOG_ERRNO, ZASS_TYPE, "malloc");
163 if (!(p->encode = odr_createmem(ODR_ENCODE)) ||
164 !(p->decode = odr_createmem(ODR_DECODE)))
166 gw_log(GW_LOG_FATAL|GW_LOG_ERRNO, ZASS_TYPE, "odr_createmem");
169 p->maxrecordsize = ZASS_MAXRECORDSIZE;
170 p->preferredmessagesize = ZASS_PREFERREDMESSAGESIZE;
171 if (!(p->outbuf = malloc(p->maxrecordsize + 1024)))
173 gw_log(GW_LOG_FATAL|GW_LOG_ERRNO, ZASS_TYPE, "malloc");
176 odr_setbuf(p->encode, p->outbuf, p->maxrecordsize + 1024);
177 if (!(p->ass = cs_create(tcpip_type, 1, CS_Z3950)))
179 gw_log(GW_LOG_FATAL|GW_LOG_ERRNO, ZASS_TYPE, "cs_create");
182 sprintf(addstr, "%s:%d", host, port);
183 if (!(address = tcpip_strtoaddr(addstr)))
185 gw_log(GW_LOG_FATAL, ZASS_TYPE, "failed to resolve %s", addstr);
188 p->fd = cs_fileno(p->ass);
189 gw_log(ZASS_DEBUG, ZASS_TYPE, "Connecting to %s", addstr);
190 if (cs_connect(p->ass, address) < 0)
192 gw_log(GW_LOG_FATAL, ZASS_TYPE, "Failed to connect to %s", addstr);
195 gw_log(ZASS_DEBUG, ZASS_TYPE, "connected ok");
196 if (send_initreq(p) < 0 || receive_initres(p) < 0)
198 gw_log(GW_LOG_FATAL, ZASS_TYPE, "Failed to initialize");
201 gw_log(ZASS_DEBUG, ZASS_TYPE, "Sent init request");
207 static Z_RPNStructure *rpn2rpn(ODR o, struct ccl_rpn_node *q)
209 Z_RPNStructure *r = odr_malloc(o, sizeof(*r));
210 Z_AttributesPlusTerm *t;
211 struct ccl_rpn_attr *i;
213 static int op[] = { Z_Operator_and, Z_Operator_or, Z_Operator_and_not };
218 r->which = Z_RPNStructure_simple;
219 r->u.simple = odr_malloc(o, sizeof(Z_Operand));
220 r->u.simple->which = Z_Operand_APT;
221 r->u.simple->u.attributesPlusTerm = t = odr_malloc(o, sizeof(*t));
222 t->term = odr_malloc(o, sizeof(Z_Term));
223 t->term->which = Z_Term_general;
224 t->term->u.general = odr_malloc(o, sizeof(Odr_oct));
225 t->term->u.general->len = t->term->u.general->size =
227 t->term->u.general->buf = odr_malloc(o, t->term->u.general->size);
228 memcpy(t->term->u.general->buf, q->u.t.term,
229 t->term->u.general->len);
230 t->num_attributes = 0;
231 t->attributeList = odr_malloc(o, sizeof(Z_AttributeElement*) * 100);
232 for (i = q->u.t.attr_list; i && t->num_attributes < 100;
235 Z_AttributeElement *a;
237 t->attributeList[t->num_attributes++] = a =
238 odr_malloc(o, sizeof(*a));
239 a->attributeType = odr_malloc(o, sizeof(int));
240 *a->attributeType = i->type;
241 a->attributeValue = odr_malloc(o, sizeof(*a));
242 *a->attributeValue = i->value;
246 r->which = Z_RPNStructure_simple;
247 r->u.simple = odr_malloc(o, sizeof(Z_Operand));
248 r->u.simple->which = Z_Operand_resultSetId;
249 r->u.simple->u.resultSetId = odr_malloc(o, len =
250 strlen(q->u.setname));
251 memcpy(r->u.simple->u.resultSetId, q->u.setname, len);
253 case CCL_RPN_AND: case CCL_RPN_OR: case CCL_RPN_NOT:
254 r->which = Z_RPNStructure_complex;
255 r->u.complex = odr_malloc(o, sizeof(Z_Complex));
256 if (!(r->u.complex->s1 = rpn2rpn(o, q->u.p[0])) ||
257 !(r->u.complex->s2 = rpn2rpn(o, q->u.p[1])))
259 r->u.complex->operator = odr_malloc(o, sizeof(Z_Operator));
260 r->u.complex->operator->which = op[q->kind];
261 r->u.complex->operator->u.and = "";
264 gw_log(GW_LOG_FATAL, ZASS_TYPE, "Bad operator in RPN");
269 static const struct zass_searchent *search_result(ZASS a)
271 static struct zass_searchent r;
273 Z_SearchResponse *res;
275 if (!(apdu = get_apdu(a)))
277 if (apdu->which != Z_APDU_searchResponse)
279 gw_log(GW_LOG_FATAL, ZASS_TYPE, "Expected searchresponse, got #%d",
283 res = apdu->u.searchResponse;
284 r.status = *res->searchStatus;
285 r.num = *res->resultCount;
286 r.status = res->resultSetStatus ? *res->resultSetStatus : 0;
291 if (res->records->which != Z_Records_NSD)
292 gw_log(GW_LOG_WARN, ZASS_TYPE, "Unexpected record types in SchR");
296 Z_DiagRec *dr = res->records->u.nonSurrogateDiagnostic;
298 if (!(id = oid_getentbyoid(dr->diagnosticSetId)) ||
299 id->class != CLASS_DIAGSET || id->value != VAL_BIB1)
300 gw_log(GW_LOG_WARN, ZASS_TYPE,
301 "Missing or unknown diagset - ignoring error!");
304 r.errcode = *dr->condition;
307 strncpy(r.errstring, dr->addinfo, 512);
308 r.errstring[511] = '\0';
316 const struct zass_searchent *zass_search(ZASS a, struct ccl_rpn_node *query,
317 char *resname, char *databases)
324 int smallSetUpperBound = 0;
325 int largeSetLowerBound = 1;
326 int mediumSetPresentNumber = 0;
327 int replaceIndicator = 1;
330 apdu.which = Z_APDU_searchRequest;
331 apdu.u.searchRequest = &req;
333 req.smallSetUpperBound = &smallSetUpperBound;
334 req.largeSetLowerBound = &largeSetLowerBound;
335 req.mediumSetPresentNumber = &mediumSetPresentNumber;
336 req.replaceIndicator = &replaceIndicator;
337 req.resultSetName = resname;
338 req.num_databaseNames = 0;
339 req.databaseNames = datab;
340 while (*databases && req.num_databaseNames < 100)
345 while (*p && !isspace(*p))
354 req.databaseNames[req.num_databaseNames] = odr_malloc(a->encode,
355 (p - databases) + 1);
356 strcpy(req.databaseNames[req.num_databaseNames++], databases);
358 databases = p + more;
360 req.smallSetElementSetNames = 0;
361 req.mediumSetElementSetNames = 0;
362 req.preferredRecordSyntax = 0;
364 q.which = Z_Query_type_1;
366 bib1.proto = PROTO_Z3950;
367 bib1.class = CLASS_ATTSET;
368 bib1.value = VAL_BIB1;
369 rpnq.attributeSetId = oid_getoidbyent(&bib1);
370 if (!(rpnq.RPNStructure = rpn2rpn(a->encode, query)))
372 if (send_apdu(a, &apdu) < 0)
375 return search_result(a);
379 * Triple indirection - that's kinda heavy. We'll fix it later.
380 * There are worse things around, though. Like ZDist.
382 void get_diagrec(zass_record ***p, Z_DiagRec *r)
384 **p = malloc(sizeof(***p));
386 (**p)->errcode = *r->condition;
389 strncpy((**p)->errstring, r->addinfo, 200);
390 (**p)->errstring[200] = 0;
393 (**p)->errstring[0] = '\0';
394 (**p)->which = ZASS_REC_DIAG;
398 void get_responserecords(zass_record ***p, Z_NamePlusRecordList *recs)
402 for (i = 0; i < recs->num_records; i++)
404 Z_NamePlusRecord *record;
406 record = recs->records[i];
407 if (record->which == Z_NamePlusRecord_surrogateDiagnostic)
408 get_diagrec(p, record->u.surrogateDiagnostic);
411 Z_DatabaseRecord *r = record->u.databaseRecord;
414 **p = malloc(sizeof(***p));
417 if (!(recform = oid_getentbyoid(r->direct_reference)) ||
418 recform->class != CLASS_RECSYN)
420 gw_log(GW_LOG_WARN, ZASS_TYPE, "Unknown or bad record syntax");
421 (**p)->which = ZASS_REC_UNKNOWN;
424 switch (recform->value)
426 case VAL_USMARC: (**p)->which = ZASS_REC_USMARC; break;
428 gw_log(GW_LOG_WARN, ZASS_TYPE, "Unknown recsyn");
429 (**p)->which = ZASS_REC_UNKNOWN;
431 if (r->which != ODR_EXTERNAL_octet)
433 gw_log(GW_LOG_WARN, ZASS_TYPE, "Record wasn't octet-aligned");
438 if (!((**p)->record = malloc(r->u.octet_aligned->len + 1)))
440 gw_log(GW_LOG_FATAL, ZASS_TYPE, "malloc");
443 memcpy((**p)->record, r->u.octet_aligned->buf,
444 r->u.octet_aligned->len);
445 (**p)->record[r->u.octet_aligned->len] = '\0';
446 gw_log(ZASS_DEBUG, ZASS_TYPE, "Got a record of %d bytes",
447 r->u.octet_aligned->len);
454 static void zass_records_free(zass_record *p)
458 static int send_present(ZASS a, char *name, int start, int num,
462 Z_PresentRequest req;
465 apdu.which = Z_APDU_presentRequest;
466 apdu.u.presentRequest = &req;
468 req.resultSetId = name;
469 req.resultSetStartPoint = &start;
470 req.numberOfRecordsRequested = #
471 req.elementSetNames = 0;
472 recsyn.proto = PROTO_Z3950;
473 recsyn.class = CLASS_RECSYN;
475 req.preferredRecordSyntax = oid_getoidbyent(&recsyn);
476 return send_apdu(a, &apdu);
480 * Note that 1== first record.
482 const struct zass_presentent *zass_present(ZASS a, char *resname, int start,
485 static struct zass_presentent r;
486 zass_record **rec = &r.records;
491 zass_records_free(r.records);
497 Z_PresentResponse *res;
499 gw_log(ZASS_DEBUG, ZASS_TYPE,
500 "Fetching %d records from # %d", num - r.num, start);
501 if (send_present(a, resname, start, num - r.num, VAL_USMARC) < 0)
503 if (!(apdu = get_apdu(a)))
505 if (apdu->which != Z_APDU_presentResponse)
507 gw_log(GW_LOG_FATAL, ZASS_TYPE, "Expected presentresponse, got #%d",
511 res = apdu->u.presentResponse;
512 r.presentstatus = *res->presentStatus;
513 r.num += *res->numberOfRecordsReturned;
514 if (*res->numberOfRecordsReturned == 0)
516 gw_log(GW_LOG_WARN, ZASS_TYPE, "Got 0 records from target");
519 r.nextpos = *res->nextResultSetPosition;
521 switch (res->records->which)
523 case Z_Records_DBOSD:
524 get_responserecords(&rec,
525 res->records->u.databaseOrSurDiagnostics);
528 get_diagrec(&rec, res->records->u.nonSurrogateDiagnostic);
531 gw_log(GW_LOG_WARN, ZASS_TYPE, "Bad tag in response rec.");
535 while (num - r.num && start);