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