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