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