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