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