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