Added YAZ version of zaccess
[egate.git] / zlayer / zaccess.c
1 /*
2  * Europagate, 1995
3  *
4  * Z39.50 API for the Email gateway
5  *
6  * $Log: zaccess.c,v $
7  * Revision 1.15  1995/04/17 11:26:55  quinn
8  * Added YAZ version of zaccess
9  *
10  * Revision 1.14  1995/02/23  08:32:26  adam
11  * Changed header.
12  *
13  * Revision 1.12  1995/02/20  20:35:37  quinn
14  * Pull present status from presresp.
15  *
16  * Revision 1.11  1995/02/20  18:58:05  quinn
17  * Added hack for record in ANY
18  *
19  * Revision 1.10  1995/02/20  18:19:30  quinn
20  * More relaxed about record types.
21  *
22  * Revision 1.9  1995/02/17  15:17:51  quinn
23  * Bug fix
24  *
25  * Revision 1.8  1995/02/17  14:48:41  quinn
26  * 'nother bug in present
27  *
28  * Revision 1.7  1995/02/17  14:42:21  quinn
29  * Trivial bug in fetch-loop.
30  *
31  * Revision 1.6  1995/02/17  14:41:22  quinn
32  * Debugging.
33  *
34  * Revision 1.5  1995/02/17  13:58:01  quinn
35  * First kick at present handling
36  *
37  * Revision 1.4  1995/02/16  15:33:45  quinn
38  * Fixed bug in KWAQS generator
39  *
40  * Revision 1.3  1995/02/16  15:20:45  quinn
41  * Added initialization of response from search
42  *
43  * Revision 1.2  1995/02/16  15:14:53  quinn
44  * Fixed KWAQS-generator
45  *
46  * Revision 1.1  1995/02/16  14:47:55  quinn
47  * First kick.
48  *
49  */
50
51 /*
52  * Interface to the Z39.50 toolkit.
53  */
54
55 #include <stdlib.h>
56 #include <assert.h>
57
58 #include <z3950.h>
59 #include <z3950sup.h>
60 #include <zutil.h>
61
62 #include <gw-log.h>
63
64 #include <ccl.h>
65 #include <zaccess.h>
66
67 struct zass    /* Z-assoc */
68 {
69     NETBOXPROFILE *ass;              /* ZDIST association handle */
70     int fd;                         /* low-level socket (for select only) */
71     int maxrecordsize;
72     int preferredmessagesize;
73     char *buf;                      /* intermediary buffer */
74 };
75
76 int rpn2kwaqs(struct ccl_rpn_node *q, char **p)
77 {
78     struct ccl_rpn_attr *i;
79     static char *ops[] = {"and", "or", "not"};
80
81     assert(!CCL_RPN_AND);
82     switch (q->kind)
83     {
84         case CCL_RPN_TERM:
85             strcpy(*p, q->u.t.term);
86             (*p) += strlen(q->u.t.term);
87             if (q->u.t.attr_list)
88             {
89                 strcat(*p, "[");
90                 (*p)++;
91                 for (i = q->u.t.attr_list; i; i = i->next)
92                 {
93                     sprintf(*p, "%d,%d%s", i->type, i->value, i->next ?
94                         "," : "");
95                     *p += strlen(*p);
96                 }
97                 strcat(*p, "]");
98                 (*p)++;
99             }
100             return 0;
101         case CCL_RPN_SET:
102             gw_log(GW_LOG_FATAL, ZASS_TYPE, "KWAQS Doesn't support set refs");
103             return -1;
104         case CCL_RPN_AND: case CCL_RPN_OR: case CCL_RPN_NOT:
105             strcpy(*p, ops[q->kind]);
106             *p += strlen(ops[q->kind]);
107             strcat(*p, "(");
108             (*p)++;
109             if (rpn2kwaqs(q->u.p[0], p) < 0)
110                 return -1;
111             strcat(*p, ",");
112             (*p)++;
113             if (rpn2kwaqs(q->u.p[1], p) < 0)
114                 return -1;
115             strcat(*p, ")");
116             (*p)++;
117             return 0;
118         default:
119             gw_log(GW_LOG_FATAL, ZASS_TYPE, "Unknown RPN node");
120             return -1;
121     }
122 }
123
124 ZASS zass_open(char *host, int port)
125 {
126     struct zass *p;
127     PINITREQUEST ireq;
128     PINITRESPONSE ires;
129     int len;
130     char name[512];
131
132     if (!(p = malloc(sizeof(*p))))
133     {
134         gw_log(GW_LOG_FATAL, ZASS_TYPE, "memory alloc failed");
135         return 0;
136     }
137     p->maxrecordsize = ZASS_MAXRECORDSIZE;
138     p->preferredmessagesize = ZASS_PREFERREDMESSAGESIZE;
139     if (!(p->buf = malloc(ZASS_MAXRECORDSIZE + 100)))
140     {
141         gw_log(GW_LOG_FATAL, ZASS_TYPE, "alloc zass-buffer");
142         return 0;
143     }
144     if (!(p->ass = NEWSTRUCT(NETBOXPROFILE)))
145     {
146         gw_log(GW_LOG_FATAL, ZASS_TYPE, "memory alloc failed");
147         return 0;
148     }
149     p->ass->TimeOutSec = 120;
150     p->ass->TimeOutUSec = 0;
151     strcpy(p->ass->HostName, host);
152     p->ass->Port = port;
153
154     if (netbox_Open(p->ass) != 1)
155     {
156         gw_log(GW_LOG_WARN, ZASS_TYPE, "netbox_Open failed");
157         return 0;
158     }
159     gw_log(ZASS_DEBUG, ZASS_TYPE, "Opened connection to %s:%d", p->ass->HostName,
160         p->ass->Port);
161     sprintf(name, "%s (ZDIST protocol layer)", ZASS_NAME);
162     ireq = InitRequest_CreateInitAllASCII(0, "yy", "yy", p->maxrecordsize,
163         p->preferredmessagesize, ZASS_ID, name, ZASS_VERSION, 0);
164     if (!ireq)
165     {
166         gw_log(GW_LOG_FATAL, "ZASS_TYPE", "failed to create initrequest");
167         return 0;
168     }
169     zutil_GetBEREncodedBuffer(ireq, (unsigned char*)p->buf, &len,
170         p->maxrecordsize);
171     if (len <= 0)
172     {
173         gw_log(GW_LOG_FATAL, ZASS_TYPE, "Failed to encode initrequest");
174         return 0;
175     }
176     InitRequest_Destroy(ireq);
177     if (netbox_SendBuffer(p->ass, p->buf, len) != len)
178     {
179         gw_log(GW_LOG_FATAL, ZASS_TYPE, "Failed to send initrequest");
180         return 0;
181     }
182     gw_log(ZASS_DEBUG, ZASS_TYPE, "Sent initrequest.");
183     if ((len = zutil_GetBERFromNet(p->ass, (unsigned char*)p->buf,
184         p->maxrecordsize)) <= 0)
185     {
186         gw_log(GW_LOG_FATAL, ZASS_TYPE, "Failed to receive initresponse");
187         return 0;
188     }
189     ires = (PINITRESPONSE) zutil_CreateFromData((unsigned char*)p->buf, len);
190     if (InitResponse_GetTag(ires) != INITRESPONSE_TAG)
191     {
192         gw_log(GW_LOG_FATAL, ZASS_TYPE, "Expected initresponse from target");
193         return 0;
194     }
195     gw_log(ZASS_DEBUG, ZASS_TYPE, "Got initresponse");
196     if (!InitResponse_GetResult(ires))
197     {
198         gw_log(GW_LOG_FATAL, ZASS_TYPE, "Access to target denied.");
199         return 0;
200     }
201     gw_log(ZASS_DEBUG, ZASS_TYPE, "Connected OK");
202     p->preferredmessagesize = InitResponse_GetPreferredMessageSize(ires);
203     p->maxrecordsize = InitResponse_GetExceptionalRecordSize(ires);
204     InitResponse_Destroy(ires);
205     return p;
206 }
207
208 const struct zass_searchent *zass_search(ZASS a, struct ccl_rpn_node *query,
209     char *resname, char *databases)
210 {
211     static struct zass_searchent r;
212     char kwaqs[512], *p;
213     DATA_DIR *pdu, *record;
214     int len;
215
216     p = kwaqs;
217     if (rpn2kwaqs(query, &p) < 0)
218     {
219         gw_log(GW_LOG_FATAL, ZASS_TYPE, "Failed to encode query");
220         return 0;
221     }
222     gw_log(ZASS_DEBUG, ZASS_TYPE, "Query: KWAQS: '%s'", kwaqs);
223     pdu = SearchRequest_CreateInitAllASCII(0, 0, 2, 0, 1, resname, databases,
224         0, 0, 0, kwaqs, BIB1_OID);
225     if (!pdu)
226     {
227         gw_log(GW_LOG_FATAL, "ZASS_TYPE", "failed to create searchrequest");
228         return 0;
229     }
230     zutil_GetBEREncodedBuffer(pdu, (unsigned char*)a->buf, &len,
231         a->maxrecordsize);
232     if (len <= 0)
233     {
234         gw_log(GW_LOG_FATAL, ZASS_TYPE, "Failed to encode initrequest");
235         return 0;
236     }
237     SearchRequest_Destroy(pdu);
238     if (netbox_SendBuffer(a->ass, a->buf, len) != len)
239     {
240         gw_log(GW_LOG_FATAL, ZASS_TYPE, "Failed to send initrequest");
241         return 0;
242     }
243     gw_log(ZASS_DEBUG, ZASS_TYPE, "Sent searchrequest.");
244     if ((len = zutil_GetBERFromNet(a->ass, (unsigned char*)a->buf,
245         a->maxrecordsize)) <= 0)
246     {
247         gw_log(GW_LOG_FATAL, ZASS_TYPE, "Failed to receive searchresponse");
248         return 0;
249     }
250     pdu = zutil_CreateFromData((unsigned char*)a->buf, len);
251     if (zutil_GetTag(pdu) != SEARCHRESPONSE_TAG)
252     {
253         gw_log(GW_LOG_FATAL, ZASS_TYPE, "Expected serchresponse from target");
254         return 0;
255     }
256     gw_log(ZASS_DEBUG, ZASS_TYPE, "Got searchresponse");
257     r.status = SearchResponse_GetSearchStatus(pdu);
258     r.num = SearchResponse_GetResultCount(pdu);
259     r.status = SearchResponse_GetResultSetStatus(pdu);
260     r.errcode = -1;
261     *r.errstring = '\0';
262     if ((record = SearchResponse_GetRecords(pdu)))
263     {
264         if (zutil_GetTag(record) == NONSURROGATEDIAGNOSTIC_TAG)
265         {
266             DATA_DIR *ad;
267
268             r.errcode = zutil_GetTaggedInt(record, ASN1_INTEGER);
269             if ((ad = zutil_GetTaggedObject(record, ASN1_VISIBLESTRING)))
270             {
271                 char *s;
272
273                 if ((s = OctetString_GetASCIIString(ad)))
274                 {
275                     strcpy(r.errstring, s);
276                     FREE(s);
277                 }
278             }
279         }
280         else
281             gw_log(GW_LOG_WARN, ZASS_TYPE, "Got real record in SRCHRESP");
282     }
283     SearchResponse_Destroy(pdu);
284
285     return &r;
286 }
287
288 /*
289  * Triple indirection - that's kinda heavy. We'll fix it later.
290  * There are worse things around, though. Like ZDist.
291  */
292 void get_diagrec(zass_record ***p, DATA_DIR *rec)
293 {
294     DATA_DIR *ad;
295
296     **p = malloc(sizeof(***p));
297     (**p)->next = 0;
298     (**p)->errcode = zutil_GetTaggedInt(rec, ASN1_INTEGER);
299     if ((ad = zutil_GetTaggedObject(rec, ASN1_VISIBLESTRING)))
300     {
301         char *s;
302
303         if ((s = OctetString_GetASCIIString(ad)))
304         {
305             strcpy((**p)->errstring, s);
306             FREE(s);
307         }
308     }
309     (**p)->which = ZASS_REC_DIAG;
310     *p = &(**p)->next;
311 }
312
313 void get_responserecords(zass_record ***p, DATA_DIR *rec)
314 {
315     int num, recsyntaxlen, i;
316     DATA_DIR *record, *retrec, *align;
317     PEXTERNAL ext;
318     POBJECTIDENTIFIER oid;
319     char recsyntax[256];
320
321     num = ResponseRecords_GetCount(rec);
322     for (i = 1; i <= num; i++)
323     {
324         record = ResponseRecords_GetRecord(rec, i);
325         if (!record)
326         {
327             gw_log(GW_LOG_WARN, ZASS_TYPE, "Failed to get record.");
328             return;
329         }
330         retrec = NamePlusRecord_GetRetrievalRecord(record);
331         if (!retrec)
332         {
333             /* check if it's a diagrec */
334             if (record->ptr.child->fldid == 2)
335                 get_diagrec(p, record->ptr.child);
336             else
337             {
338                 gw_log(GW_LOG_WARN, ZASS_TYPE, "Illegal record.");
339                 return;
340             }
341         }
342         ext = RetrievalRecord_GetExternal(retrec);
343         if (!ext)
344         {
345             gw_log(GW_LOG_WARN, ZASS_TYPE, "No external in record");
346             return;
347         }
348         oid = External_GetDirectReference(ext);
349         if (!oid)
350         {
351             gw_log(GW_LOG_WARN, ZASS_TYPE, "Unknown record type.");
352             return;
353         }
354         recsyntaxlen = DirectReference_GetLength(oid);
355         memcpy(recsyntax, DirectReference_GetData(oid), recsyntaxlen);
356         recsyntax[recsyntaxlen] = '\0';
357         **p = malloc(sizeof(***p));
358         (**p)->next = 0;
359         if (!strcmp(recsyntax, USMARC_OID))
360             (**p)->which = ZASS_REC_USMARC;
361         else
362         {
363             gw_log(GW_LOG_WARN, ZASS_TYPE, "ZLAYER only knows USMARC at this point.");
364             gw_log(GW_LOG_WARN, ZASS_TYPE, "Type was '%d'", (**p)->which);
365         }
366         align = External_GetEncodingAligned(ext);
367         if (!align)
368         {
369             gw_log(GW_LOG_WARN, ZASS_TYPE, "Record wasn't octet-aligned");
370             align = External_GetEncodingSingle(ext);
371             if (!align)
372             {
373                 gw_log(GW_LOG_WARN, ZASS_TYPE, "Record wasn't ANY");
374                 return;
375             }
376             align = align->ptr.child;
377         }
378         if (!((**p)->record = malloc(align->count + 1)))
379         {
380             gw_log(GW_LOG_FATAL, ZASS_TYPE, "malloc");
381             return;
382         }
383         memcpy((**p)->record, align->ptr.data, align->count);
384         (**p)->record[align->count] = '\0';
385         gw_log(ZASS_DEBUG, ZASS_TYPE, "Got a record of %d bytes",
386             align->count);
387
388         (*p) = &(**p)->next;
389     }
390 }
391
392 static void zass_records_free(zass_record *p)
393 {
394 }
395
396 /*
397  * Note that 1== first record.
398  */
399 const struct zass_presentent *zass_present(ZASS a, char *resname, int start,
400     int num)
401 {
402     static struct zass_presentent r = {0, 0, 0, 0};
403     zass_record **rec = &r.records;
404     DATA_DIR *pdu;
405     int len;
406
407     r.num = 0;
408     if (r.records)
409     {
410         zass_records_free(r.records);
411         r.records = 0;
412     }
413     do
414     {
415         gw_log(ZASS_DEBUG, ZASS_TYPE, "Fetching %d records from # %d", num - r.num,
416             start);
417         pdu = PresentRequest_CreateInitAllASCII(0, resname, start, num - r.num, "F",
418             USMARC_OID);
419         if (!pdu)
420         {
421             gw_log(GW_LOG_FATAL, "ZASS_TYPE", "failed to create presentrequest");
422             return 0;
423         }
424         zutil_GetBEREncodedBuffer(pdu, (unsigned char*)a->buf, &len,
425             a->maxrecordsize);
426         if (len <= 0)
427         {
428             gw_log(GW_LOG_FATAL, ZASS_TYPE, "Failed to encode presentrequest");
429             return 0;
430         }
431         PresentRequest_Destroy(pdu);
432         if (netbox_SendBuffer(a->ass, a->buf, len) != len)
433         {
434             gw_log(GW_LOG_FATAL, ZASS_TYPE, "Failed to send presentrequest");
435             return 0;
436         }
437         gw_log(ZASS_DEBUG, ZASS_TYPE, "Sent presentrequest.");
438         if ((len = zutil_GetBERFromNet(a->ass, (unsigned char*)a->buf,
439             a->maxrecordsize)) <= 0)
440         {
441             gw_log(GW_LOG_FATAL, ZASS_TYPE, "Failed to receive presentresponse");
442             return 0;
443         }
444         pdu = zutil_CreateFromData((unsigned char*)a->buf, len);
445         if (zutil_GetTag(pdu) != PRESENTRESPONSE_TAG)
446         {
447             gw_log(GW_LOG_FATAL, ZASS_TYPE, "Expected presentresponse from target");
448             return 0;
449         }
450         gw_log(ZASS_DEBUG, ZASS_TYPE, "Got presentresponse");
451         r.num += PresentResponse_GetNumberOfRecordsReturned(pdu);
452         r.presentstatus = PresentResponse_GetPresentStatus(pdu);
453         if (r.num == 0)
454         {
455             gw_log(GW_LOG_WARN, ZASS_TYPE, "Got 0 records from target.");
456             return 0;
457         }
458         r.nextpos = PresentResponse_GetNextResultSetPosition(pdu);
459         start = r.nextpos;
460         switch(PresentResponse_GetRecordType(pdu))
461         {
462             case RESPONSERECORDS_TAG:
463                 get_responserecords(&rec, PresentResponse_GetResponseRecords(pdu));
464                 break;
465             case NONSURROGATEDIAGNOSTIC_TAG:
466                 get_diagrec(&rec, PresentResponse_GetNonSurrogateDiagnostic(pdu));
467                 break;
468             default:
469                 gw_log(GW_LOG_WARN, ZASS_TYPE, "Bad tag in response rec.");
470         }
471         PresentResponse_Destroy(pdu);
472     }
473     while (num - r.num && start);
474     *rec = 0;
475         
476     return &r;
477 }