4 * Z39.50 API for the Email gateway - YAZ version
6 * $Log: zaccess-yaz.c,v $
7 * Revision 1.2 1995/04/19 09:24:02 quinn
8 * Fixed bug in zass_open - variable initialized after use
10 * Revision 1.1 1995/04/17 11:26:53 quinn
11 * Added YAZ version of zaccess
17 * Interface to the Z39.50 toolkit.
36 struct zass /* Z-assoc */
38 COMSTACK ass; /* comstack association handle */
41 int fd; /* low-level socket (for select only) */
43 int preferredmessagesize;
44 char *outbuf; /* intermediary buffer */
49 static Z_APDU *get_apdu(struct zass *z)
54 if ((res = cs_get(z->ass, &z->inbuf, &z->inbuflen)) <= 0)
56 gw_log(GW_LOG_WARN, ZASS_TYPE, "cs_get failed");
60 odr_setbuf(z->decode, z->inbuf, res);
61 if (!z_APDU(z->decode, &ap, 0))
63 gw_log(GW_LOG_WARN, ZASS_TYPE, "decode: %s",
64 odr_errlist[odr_geterror(z->decode)]);
70 static int send_apdu(struct zass *z, Z_APDU *a)
75 if (!z_APDU(z->encode, &a, 0))
77 gw_log(GW_LOG_FATAL, ZASS_TYPE, "encoding initreq");
80 buf = odr_getbuf(z->encode, &len);
81 if (cs_put(z->ass, buf, len) < 0)
83 gw_log(GW_LOG_FATAL, ZASS_TYPE, "cs_put");
90 static int send_initreq(struct zass *p)
94 Odr_bitmask options, protocolVersion;
97 a.which = Z_APDU_initRequest;
98 a.u.initRequest = &init;
100 init.options = &options;
101 ODR_MASK_ZERO(&options);
102 ODR_MASK_SET(&options, Z_Options_search);
103 ODR_MASK_SET(&options, Z_Options_present);
104 ODR_MASK_SET(&options, Z_Options_delSet);
105 init.protocolVersion = &protocolVersion;
106 ODR_MASK_ZERO(&protocolVersion);
107 ODR_MASK_SET(&protocolVersion, Z_ProtocolVersion_1);
108 ODR_MASK_SET(&protocolVersion, Z_ProtocolVersion_2);
109 init.preferredMessageSize = &p->preferredmessagesize;
110 init.maximumRecordSize = &p->maxrecordsize;
111 init.idAuthentication = 0;
112 init.implementationId = ZASS_ID;
113 sprintf(name, "%s (YAZ protocol layer)", ZASS_NAME);
114 init.implementationName = name;
115 init.implementationVersion = ZASS_VERSION;
116 init.userInformationField = 0;
117 if (send_apdu(p, &a) < 0)
122 static int receive_initres(struct zass *p)
127 if (!(ap = get_apdu(p)))
129 if (ap->which != Z_APDU_initResponse)
131 gw_log(GW_LOG_WARN, ZASS_TYPE, "bad APDU");
134 res = ap->u.initResponse;
137 gw_log(GW_LOG_WARN, ZASS_TYPE, "Negative result on initres");
140 p->preferredmessagesize = *res->preferredMessageSize;
141 p->maxrecordsize = *res->maximumRecordSize;
142 if (res->implementationId)
143 gw_log(GW_LOG_NOTICE, ZASS_TYPE, "imp. ID : %s",
144 res->implementationId);
145 if (res->implementationName)
146 gw_log(GW_LOG_NOTICE, ZASS_TYPE, "imp. ID : %s",
147 res->implementationName);
148 if (res->implementationVersion)
149 gw_log(GW_LOG_NOTICE, ZASS_TYPE, "imp. ID : %s",
150 res->implementationVersion);
151 gw_log(ZASS_DEBUG, ZASS_TYPE, "Initialized ok");
155 ZASS zass_open(char *host, int port)
161 if (!(p = malloc(sizeof(*p))))
163 gw_log(GW_LOG_FATAL|GW_LOG_ERRNO, ZASS_TYPE, "malloc");
166 if (!(p->encode = odr_createmem(ODR_ENCODE)) ||
167 !(p->decode = odr_createmem(ODR_DECODE)))
169 gw_log(GW_LOG_FATAL|GW_LOG_ERRNO, ZASS_TYPE, "odr_createmem");
172 p->maxrecordsize = ZASS_MAXRECORDSIZE;
173 p->preferredmessagesize = ZASS_PREFERREDMESSAGESIZE;
174 if (!(p->outbuf = malloc(p->maxrecordsize + 1024)))
176 gw_log(GW_LOG_FATAL|GW_LOG_ERRNO, ZASS_TYPE, "malloc");
179 odr_setbuf(p->encode, p->outbuf, p->maxrecordsize + 1024);
180 if (!(p->ass = cs_create(tcpip_type, 1, CS_Z3950)))
182 gw_log(GW_LOG_FATAL|GW_LOG_ERRNO, ZASS_TYPE, "cs_create");
185 sprintf(addstr, "%s:%d", host, port);
186 if (!(address = tcpip_strtoaddr(addstr)))
188 gw_log(GW_LOG_FATAL, ZASS_TYPE, "failed to resolve %s", addstr);
191 p->fd = cs_fileno(p->ass);
192 gw_log(ZASS_DEBUG, ZASS_TYPE, "Connecting to %s", addstr);
193 if (cs_connect(p->ass, address) < 0)
195 gw_log(GW_LOG_FATAL, ZASS_TYPE, "Failed to connect to %s", addstr);
198 gw_log(ZASS_DEBUG, ZASS_TYPE, "connected ok");
201 if (send_initreq(p) < 0 || receive_initres(p) < 0)
203 gw_log(GW_LOG_FATAL, ZASS_TYPE, "Failed to initialize");
206 gw_log(ZASS_DEBUG, ZASS_TYPE, "Sent init request");
210 static Z_RPNStructure *rpn2rpn(ODR o, struct ccl_rpn_node *q)
212 Z_RPNStructure *r = odr_malloc(o, sizeof(*r));
213 Z_AttributesPlusTerm *t;
214 struct ccl_rpn_attr *i;
216 static int op[] = { Z_Operator_and, Z_Operator_or, Z_Operator_and_not };
221 r->which = Z_RPNStructure_simple;
222 r->u.simple = odr_malloc(o, sizeof(Z_Operand));
223 r->u.simple->which = Z_Operand_APT;
224 r->u.simple->u.attributesPlusTerm = t = odr_malloc(o, sizeof(*t));
225 t->term = odr_malloc(o, sizeof(Z_Term));
226 t->term->which = Z_Term_general;
227 t->term->u.general = odr_malloc(o, sizeof(Odr_oct));
228 t->term->u.general->len = t->term->u.general->size =
230 t->term->u.general->buf = odr_malloc(o, t->term->u.general->size);
231 memcpy(t->term->u.general->buf, q->u.t.term,
232 t->term->u.general->len);
233 t->num_attributes = 0;
234 t->attributeList = odr_malloc(o, sizeof(Z_AttributeElement*) * 100);
235 for (i = q->u.t.attr_list; i && t->num_attributes < 100;
238 Z_AttributeElement *a;
240 t->attributeList[t->num_attributes++] = a =
241 odr_malloc(o, sizeof(*a));
242 a->attributeType = odr_malloc(o, sizeof(int));
243 *a->attributeType = i->type;
244 a->attributeValue = odr_malloc(o, sizeof(*a));
245 *a->attributeValue = i->value;
249 r->which = Z_RPNStructure_simple;
250 r->u.simple = odr_malloc(o, sizeof(Z_Operand));
251 r->u.simple->which = Z_Operand_resultSetId;
252 r->u.simple->u.resultSetId = odr_malloc(o, len =
253 strlen(q->u.setname));
254 memcpy(r->u.simple->u.resultSetId, q->u.setname, len);
256 case CCL_RPN_AND: case CCL_RPN_OR: case CCL_RPN_NOT:
257 r->which = Z_RPNStructure_complex;
258 r->u.complex = odr_malloc(o, sizeof(Z_Complex));
259 if (!(r->u.complex->s1 = rpn2rpn(o, q->u.p[0])) ||
260 !(r->u.complex->s2 = rpn2rpn(o, q->u.p[1])))
262 r->u.complex->operator = odr_malloc(o, sizeof(Z_Operator));
263 r->u.complex->operator->which = op[q->kind];
264 r->u.complex->operator->u.and = "";
267 gw_log(GW_LOG_FATAL, ZASS_TYPE, "Bad operator in RPN");
272 static const struct zass_searchent *search_result(ZASS a)
274 static struct zass_searchent r;
276 Z_SearchResponse *res;
278 if (!(apdu = get_apdu(a)))
280 if (apdu->which != Z_APDU_searchResponse)
282 gw_log(GW_LOG_FATAL, ZASS_TYPE, "Expected searchresponse, got #%d",
286 res = apdu->u.searchResponse;
287 r.status = *res->searchStatus;
288 r.num = *res->resultCount;
289 r.status = res->resultSetStatus ? *res->resultSetStatus : 0;
294 if (res->records->which != Z_Records_NSD)
295 gw_log(GW_LOG_WARN, ZASS_TYPE, "Unexpected record types in SchR");
299 Z_DiagRec *dr = res->records->u.nonSurrogateDiagnostic;
301 if (!(id = oid_getentbyoid(dr->diagnosticSetId)) ||
302 id->class != CLASS_DIAGSET || id->value != VAL_BIB1)
303 gw_log(GW_LOG_WARN, ZASS_TYPE,
304 "Missing or unknown diagset - ignoring error!");
307 r.errcode = *dr->condition;
310 strncpy(r.errstring, dr->addinfo, 512);
311 r.errstring[511] = '\0';
319 const struct zass_searchent *zass_search(ZASS a, struct ccl_rpn_node *query,
320 char *resname, char *databases)
327 int smallSetUpperBound = 0;
328 int largeSetLowerBound = 1;
329 int mediumSetPresentNumber = 0;
330 int replaceIndicator = 1;
333 apdu.which = Z_APDU_searchRequest;
334 apdu.u.searchRequest = &req;
336 req.smallSetUpperBound = &smallSetUpperBound;
337 req.largeSetLowerBound = &largeSetLowerBound;
338 req.mediumSetPresentNumber = &mediumSetPresentNumber;
339 req.replaceIndicator = &replaceIndicator;
340 req.resultSetName = resname;
341 req.num_databaseNames = 0;
342 req.databaseNames = datab;
343 while (*databases && req.num_databaseNames < 100)
348 while (*p && !isspace(*p))
357 req.databaseNames[req.num_databaseNames] = odr_malloc(a->encode,
358 (p - databases) + 1);
359 strcpy(req.databaseNames[req.num_databaseNames++], databases);
361 databases = p + more;
363 req.smallSetElementSetNames = 0;
364 req.mediumSetElementSetNames = 0;
365 req.preferredRecordSyntax = 0;
367 q.which = Z_Query_type_1;
369 bib1.proto = PROTO_Z3950;
370 bib1.class = CLASS_ATTSET;
371 bib1.value = VAL_BIB1;
372 rpnq.attributeSetId = oid_getoidbyent(&bib1);
373 if (!(rpnq.RPNStructure = rpn2rpn(a->encode, query)))
375 if (send_apdu(a, &apdu) < 0)
378 return search_result(a);
382 * Triple indirection - that's kinda heavy. We'll fix it later.
383 * There are worse things around, though. Like ZDist.
385 void get_diagrec(zass_record ***p, Z_DiagRec *r)
387 **p = malloc(sizeof(***p));
389 (**p)->errcode = *r->condition;
392 strncpy((**p)->errstring, r->addinfo, 200);
393 (**p)->errstring[200] = 0;
396 (**p)->errstring[0] = '\0';
397 (**p)->which = ZASS_REC_DIAG;
401 void get_responserecords(zass_record ***p, Z_NamePlusRecordList *recs)
405 for (i = 0; i < recs->num_records; i++)
407 Z_NamePlusRecord *record;
409 record = recs->records[i];
410 if (record->which == Z_NamePlusRecord_surrogateDiagnostic)
411 get_diagrec(p, record->u.surrogateDiagnostic);
414 Z_DatabaseRecord *r = record->u.databaseRecord;
417 **p = malloc(sizeof(***p));
420 if (!(recform = oid_getentbyoid(r->direct_reference)) ||
421 recform->class != CLASS_RECSYN)
423 gw_log(GW_LOG_WARN, ZASS_TYPE, "Unknown or bad record syntax");
424 (**p)->which = ZASS_REC_UNKNOWN;
427 switch (recform->value)
429 case VAL_USMARC: (**p)->which = ZASS_REC_USMARC; break;
431 gw_log(GW_LOG_WARN, ZASS_TYPE, "Unknown recsyn");
432 (**p)->which = ZASS_REC_UNKNOWN;
434 if (r->which != ODR_EXTERNAL_octet)
436 gw_log(GW_LOG_WARN, ZASS_TYPE, "Record wasn't octet-aligned");
441 if (!((**p)->record = malloc(r->u.octet_aligned->len + 1)))
443 gw_log(GW_LOG_FATAL, ZASS_TYPE, "malloc");
446 memcpy((**p)->record, r->u.octet_aligned->buf,
447 r->u.octet_aligned->len);
448 (**p)->record[r->u.octet_aligned->len] = '\0';
449 gw_log(ZASS_DEBUG, ZASS_TYPE, "Got a record of %d bytes",
450 r->u.octet_aligned->len);
457 static void zass_records_free(zass_record *p)
461 static int send_present(ZASS a, char *name, int start, int num,
465 Z_PresentRequest req;
468 apdu.which = Z_APDU_presentRequest;
469 apdu.u.presentRequest = &req;
471 req.resultSetId = name;
472 req.resultSetStartPoint = &start;
473 req.numberOfRecordsRequested = #
474 req.elementSetNames = 0;
475 recsyn.proto = PROTO_Z3950;
476 recsyn.class = CLASS_RECSYN;
478 req.preferredRecordSyntax = oid_getoidbyent(&recsyn);
479 return send_apdu(a, &apdu);
483 * Note that 1== first record.
485 const struct zass_presentent *zass_present(ZASS a, char *resname, int start,
488 static struct zass_presentent r;
489 zass_record **rec = &r.records;
494 zass_records_free(r.records);
500 Z_PresentResponse *res;
502 gw_log(ZASS_DEBUG, ZASS_TYPE,
503 "Fetching %d records from # %d", num - r.num, start);
504 if (send_present(a, resname, start, num - r.num, VAL_USMARC) < 0)
506 if (!(apdu = get_apdu(a)))
508 if (apdu->which != Z_APDU_presentResponse)
510 gw_log(GW_LOG_FATAL, ZASS_TYPE, "Expected presentresponse, got #%d",
514 res = apdu->u.presentResponse;
515 r.presentstatus = *res->presentStatus;
516 r.num += *res->numberOfRecordsReturned;
517 if (*res->numberOfRecordsReturned == 0)
519 gw_log(GW_LOG_WARN, ZASS_TYPE, "Got 0 records from target");
522 r.nextpos = *res->nextResultSetPosition;
524 switch (res->records->which)
526 case Z_Records_DBOSD:
527 get_responserecords(&rec,
528 res->records->u.databaseOrSurDiagnostics);
531 get_diagrec(&rec, res->records->u.nonSurrogateDiagnostic);
534 gw_log(GW_LOG_WARN, ZASS_TYPE, "Bad tag in response rec.");
538 while (num - r.num && start);