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