4 * Z39.50 API for the Email gateway
7 * Revision 1.16 1995/04/20 15:25:34 quinn
10 * Revision 1.15 1995/04/17 11:26:55 quinn
11 * Added YAZ version of zaccess
13 * Revision 1.14 1995/02/23 08:32:26 adam
16 * Revision 1.12 1995/02/20 20:35:37 quinn
17 * Pull present status from presresp.
19 * Revision 1.11 1995/02/20 18:58:05 quinn
20 * Added hack for record in ANY
22 * Revision 1.10 1995/02/20 18:19:30 quinn
23 * More relaxed about record types.
25 * Revision 1.9 1995/02/17 15:17:51 quinn
28 * Revision 1.8 1995/02/17 14:48:41 quinn
29 * 'nother bug in present
31 * Revision 1.7 1995/02/17 14:42:21 quinn
32 * Trivial bug in fetch-loop.
34 * Revision 1.6 1995/02/17 14:41:22 quinn
37 * Revision 1.5 1995/02/17 13:58:01 quinn
38 * First kick at present handling
40 * Revision 1.4 1995/02/16 15:33:45 quinn
41 * Fixed bug in KWAQS generator
43 * Revision 1.3 1995/02/16 15:20:45 quinn
44 * Added initialization of response from search
46 * Revision 1.2 1995/02/16 15:14:53 quinn
47 * Fixed KWAQS-generator
49 * Revision 1.1 1995/02/16 14:47:55 quinn
55 * Interface to the Z39.50 toolkit. Primary function is to hide Zdist, or
56 * whatever lower-layer we decide to use later. The decision to add a
57 * layer atop the toolkit was twofold: It vastly simplifies the
58 * implementation of the protocol persistence, and it hides Zdist. The
59 * latter is useful after Zdist has gone and changed their fine API after
60 * we went through all the trouble of documenting it in our Design. Don't
61 * want that to happen again.
63 * For the time being at least, we'll have these routines hang (or err) if
64 * they get a WOULDBLOCK on a write. That seems safe since, under normal
65 * circumstances, the network buffers should always be able to absorb
66 * the small request packages.
81 struct zass /* Z-assoc */
83 NETBOXPROFILE *ass; /* ZDIST association handle */
84 int fd; /* low-level socket (for select only) */
86 int preferredmessagesize;
87 char *buf; /* intermediary buffer */
90 int rpn2kwaqs(struct ccl_rpn_node *q, char **p)
92 struct ccl_rpn_attr *i;
93 static char *ops[] = {"and", "or", "not"};
99 strcpy(*p, q->u.t.term);
100 (*p) += strlen(q->u.t.term);
101 if (q->u.t.attr_list)
105 for (i = q->u.t.attr_list; i; i = i->next)
107 sprintf(*p, "%d,%d%s", i->type, i->value, i->next ?
116 gw_log(GW_LOG_FATAL, ZASS_TYPE, "KWAQS Doesn't support set refs");
118 case CCL_RPN_AND: case CCL_RPN_OR: case CCL_RPN_NOT:
119 strcpy(*p, ops[q->kind]);
120 *p += strlen(ops[q->kind]);
123 if (rpn2kwaqs(q->u.p[0], p) < 0)
127 if (rpn2kwaqs(q->u.p[1], p) < 0)
133 gw_log(GW_LOG_FATAL, ZASS_TYPE, "Unknown RPN node");
138 int zass_openresult(ZASS p, int *complete)
143 if ((len = zutil_GetBERFromNet(p->ass, (unsigned char*)p->buf,
144 p->maxrecordsize)) <= 0)
146 gw_log(GW_LOG_FATAL, ZASS_TYPE, "Failed to receive initresponse");
149 ires = (PINITRESPONSE) zutil_CreateFromData((unsigned char*)p->buf, len);
150 if (InitResponse_GetTag(ires) != INITRESPONSE_TAG)
152 gw_log(GW_LOG_FATAL, ZASS_TYPE, "Expected initresponse from target");
155 gw_log(ZASS_DEBUG, ZASS_TYPE, "Got initresponse");
156 if (!InitResponse_GetResult(ires))
158 gw_log(GW_LOG_FATAL, ZASS_TYPE, "Access to target denied.");
161 gw_log(ZASS_DEBUG, ZASS_TYPE, "Connected OK");
162 p->preferredmessagesize = InitResponse_GetPreferredMessageSize(ires);
163 p->maxrecordsize = InitResponse_GetExceptionalRecordSize(ires);
164 InitResponse_Destroy(ires);
169 ZASS zass_open(char *host, int port, int *complete)
177 if (!(p = malloc(sizeof(*p))))
179 gw_log(GW_LOG_FATAL, ZASS_TYPE, "memory alloc failed");
182 p->maxrecordsize = ZASS_MAXRECORDSIZE;
183 p->preferredmessagesize = ZASS_PREFERREDMESSAGESIZE;
184 if (!(p->buf = malloc(ZASS_MAXRECORDSIZE + 100)))
186 gw_log(GW_LOG_FATAL, ZASS_TYPE, "alloc zass-buffer");
189 if (!(p->ass = NEWSTRUCT(NETBOXPROFILE)))
191 gw_log(GW_LOG_FATAL, ZASS_TYPE, "memory alloc failed");
194 p->ass->TimeOutSec = 120;
195 p->ass->TimeOutUSec = 0;
196 strcpy(p->ass->HostName, host);
199 if (netbox_Open(p->ass) != 1)
201 gw_log(GW_LOG_WARN, ZASS_TYPE, "netbox_Open failed");
204 gw_log(ZASS_DEBUG, ZASS_TYPE, "Opened connection to %s:%d",
205 p->ass->HostName, p->ass->Port);
206 sprintf(name, "%s (ZDIST protocol layer)", ZASS_NAME);
207 ireq = InitRequest_CreateInitAllASCII(0, "yy", "yy", p->maxrecordsize,
208 p->preferredmessagesize, ZASS_ID, name, ZASS_VERSION, 0);
211 gw_log(GW_LOG_FATAL, "ZASS_TYPE", "failed to create initrequest");
214 zutil_GetBEREncodedBuffer(ireq, (unsigned char*)p->buf, &len,
218 gw_log(GW_LOG_FATAL, ZASS_TYPE, "Failed to encode initrequest");
221 InitRequest_Destroy(ireq);
222 if (netbox_SendBuffer(p->ass, p->buf, len) != len)
224 gw_log(GW_LOG_FATAL, ZASS_TYPE, "Failed to send initrequest");
227 gw_log(ZASS_DEBUG, ZASS_TYPE, "Sent initrequest.");
229 if (zass_openresult(p, complete) < 0 && (!complete || *complete))
236 const struct zass_searchent *zass_search(ZASS a, struct ccl_rpn_node *query,
237 char *resname, char *databases)
239 static struct zass_searchent r;
241 DATA_DIR *pdu, *record;
245 if (rpn2kwaqs(query, &p) < 0)
247 gw_log(GW_LOG_FATAL, ZASS_TYPE, "Failed to encode query");
250 gw_log(ZASS_DEBUG, ZASS_TYPE, "Query: KWAQS: '%s'", kwaqs);
251 pdu = SearchRequest_CreateInitAllASCII(0, 0, 2, 0, 1, resname, databases,
252 0, 0, 0, kwaqs, BIB1_OID);
255 gw_log(GW_LOG_FATAL, "ZASS_TYPE", "failed to create searchrequest");
258 zutil_GetBEREncodedBuffer(pdu, (unsigned char*)a->buf, &len,
262 gw_log(GW_LOG_FATAL, ZASS_TYPE, "Failed to encode initrequest");
265 SearchRequest_Destroy(pdu);
266 if (netbox_SendBuffer(a->ass, a->buf, len) != len)
268 gw_log(GW_LOG_FATAL, ZASS_TYPE, "Failed to send initrequest");
271 gw_log(ZASS_DEBUG, ZASS_TYPE, "Sent searchrequest.");
272 if ((len = zutil_GetBERFromNet(a->ass, (unsigned char*)a->buf,
273 a->maxrecordsize)) <= 0)
275 gw_log(GW_LOG_FATAL, ZASS_TYPE, "Failed to receive searchresponse");
278 pdu = zutil_CreateFromData((unsigned char*)a->buf, len);
279 if (zutil_GetTag(pdu) != SEARCHRESPONSE_TAG)
281 gw_log(GW_LOG_FATAL, ZASS_TYPE, "Expected serchresponse from target");
284 gw_log(ZASS_DEBUG, ZASS_TYPE, "Got searchresponse");
285 r.status = SearchResponse_GetSearchStatus(pdu);
286 r.num = SearchResponse_GetResultCount(pdu);
287 r.status = SearchResponse_GetResultSetStatus(pdu);
290 if ((record = SearchResponse_GetRecords(pdu)))
292 if (zutil_GetTag(record) == NONSURROGATEDIAGNOSTIC_TAG)
296 r.errcode = zutil_GetTaggedInt(record, ASN1_INTEGER);
297 if ((ad = zutil_GetTaggedObject(record, ASN1_VISIBLESTRING)))
301 if ((s = OctetString_GetASCIIString(ad)))
303 strcpy(r.errstring, s);
309 gw_log(GW_LOG_WARN, ZASS_TYPE, "Got real record in SRCHRESP");
311 SearchResponse_Destroy(pdu);
317 * Triple indirection - that's kinda heavy. We'll fix it later.
318 * There are worse things around, though. Like ZDist.
320 void get_diagrec(zass_record ***p, DATA_DIR *rec)
324 **p = malloc(sizeof(***p));
326 (**p)->errcode = zutil_GetTaggedInt(rec, ASN1_INTEGER);
327 if ((ad = zutil_GetTaggedObject(rec, ASN1_VISIBLESTRING)))
331 if ((s = OctetString_GetASCIIString(ad)))
333 strcpy((**p)->errstring, s);
337 (**p)->which = ZASS_REC_DIAG;
341 void get_responserecords(zass_record ***p, DATA_DIR *rec)
343 int num, recsyntaxlen, i;
344 DATA_DIR *record, *retrec, *align;
346 POBJECTIDENTIFIER oid;
349 num = ResponseRecords_GetCount(rec);
350 for (i = 1; i <= num; i++)
352 record = ResponseRecords_GetRecord(rec, i);
355 gw_log(GW_LOG_WARN, ZASS_TYPE, "Failed to get record.");
358 retrec = NamePlusRecord_GetRetrievalRecord(record);
361 /* check if it's a diagrec */
362 if (record->ptr.child->fldid == 2)
363 get_diagrec(p, record->ptr.child);
366 gw_log(GW_LOG_WARN, ZASS_TYPE, "Illegal record.");
370 ext = RetrievalRecord_GetExternal(retrec);
373 gw_log(GW_LOG_WARN, ZASS_TYPE, "No external in record");
376 oid = External_GetDirectReference(ext);
379 gw_log(GW_LOG_WARN, ZASS_TYPE, "Unknown record type.");
382 recsyntaxlen = DirectReference_GetLength(oid);
383 memcpy(recsyntax, DirectReference_GetData(oid), recsyntaxlen);
384 recsyntax[recsyntaxlen] = '\0';
385 **p = malloc(sizeof(***p));
387 if (!strcmp(recsyntax, USMARC_OID))
388 (**p)->which = ZASS_REC_USMARC;
391 gw_log(GW_LOG_WARN, ZASS_TYPE, "ZLAYER only knows USMARC at this point.");
392 gw_log(GW_LOG_WARN, ZASS_TYPE, "Type was '%d'", (**p)->which);
394 align = External_GetEncodingAligned(ext);
397 gw_log(GW_LOG_WARN, ZASS_TYPE, "Record wasn't octet-aligned");
398 align = External_GetEncodingSingle(ext);
401 gw_log(GW_LOG_WARN, ZASS_TYPE, "Record wasn't ANY");
404 align = align->ptr.child;
406 if (!((**p)->record = malloc(align->count + 1)))
408 gw_log(GW_LOG_FATAL, ZASS_TYPE, "malloc");
411 memcpy((**p)->record, align->ptr.data, align->count);
412 (**p)->record[align->count] = '\0';
413 gw_log(ZASS_DEBUG, ZASS_TYPE, "Got a record of %d bytes",
420 static void zass_records_free(zass_record *p)
425 * Note that 1== first record.
427 const struct zass_presentent *zass_present(ZASS a, char *resname, int start,
430 static struct zass_presentent r = {0, 0, 0, 0};
431 zass_record **rec = &r.records;
438 zass_records_free(r.records);
443 gw_log(ZASS_DEBUG, ZASS_TYPE, "Fetching %d records from # %d", num - r.num,
445 pdu = PresentRequest_CreateInitAllASCII(0, resname, start, num - r.num, "F",
449 gw_log(GW_LOG_FATAL, "ZASS_TYPE", "failed to create presentrequest");
452 zutil_GetBEREncodedBuffer(pdu, (unsigned char*)a->buf, &len,
456 gw_log(GW_LOG_FATAL, ZASS_TYPE, "Failed to encode presentrequest");
459 PresentRequest_Destroy(pdu);
460 if (netbox_SendBuffer(a->ass, a->buf, len) != len)
462 gw_log(GW_LOG_FATAL, ZASS_TYPE, "Failed to send presentrequest");
465 gw_log(ZASS_DEBUG, ZASS_TYPE, "Sent presentrequest.");
466 if ((len = zutil_GetBERFromNet(a->ass, (unsigned char*)a->buf,
467 a->maxrecordsize)) <= 0)
469 gw_log(GW_LOG_FATAL, ZASS_TYPE, "Failed to receive presentresponse");
472 pdu = zutil_CreateFromData((unsigned char*)a->buf, len);
473 if (zutil_GetTag(pdu) != PRESENTRESPONSE_TAG)
475 gw_log(GW_LOG_FATAL, ZASS_TYPE, "Expected presentresponse from target");
478 gw_log(ZASS_DEBUG, ZASS_TYPE, "Got presentresponse");
479 r.num += PresentResponse_GetNumberOfRecordsReturned(pdu);
480 r.presentstatus = PresentResponse_GetPresentStatus(pdu);
483 gw_log(GW_LOG_WARN, ZASS_TYPE, "Got 0 records from target.");
486 r.nextpos = PresentResponse_GetNextResultSetPosition(pdu);
488 switch(PresentResponse_GetRecordType(pdu))
490 case RESPONSERECORDS_TAG:
491 get_responserecords(&rec, PresentResponse_GetResponseRecords(pdu));
493 case NONSURROGATEDIAGNOSTIC_TAG:
494 get_diagrec(&rec, PresentResponse_GetNonSurrogateDiagnostic(pdu));
497 gw_log(GW_LOG_WARN, ZASS_TYPE, "Bad tag in response rec.");
499 PresentResponse_Destroy(pdu);
501 while (num - r.num && start);