Fixed bug in zass_open - variable initialized after use
[egate.git] / zlayer / zaccess-yaz.c
1 /*
2  * Europagate, 1995
3  *
4  * Z39.50 API for the Email gateway - YAZ version
5  *
6  * $Log: zaccess-yaz.c,v $
7  * Revision 1.2  1995/04/19 09:24:02  quinn
8  * Fixed bug in zass_open - variable initialized after use
9  *
10  * Revision 1.1  1995/04/17  11:26:53  quinn
11  * Added YAZ version of zaccess
12  *
13  *
14  */
15
16 /*
17  * Interface to the Z39.50 toolkit.
18  */
19
20 #include <stdlib.h>
21 #include <assert.h>
22 #include <ctype.h>
23
24 #include <gw-log.h>
25 #include <proto.h>
26 #include <comstack.h>
27 #include <tcpip.h>
28 #ifdef USE_XTIMOSI
29 #include <xmosi.h>
30 #endif
31 #include <oid.h>
32
33 #include <ccl.h>
34 #include <zaccess.h>
35
36 struct zass    /* Z-assoc */
37 {
38     COMSTACK ass;              /* comstack association handle */
39     ODR encode;
40     ODR decode;
41     int fd;                         /* low-level socket (for select only) */
42     int maxrecordsize;
43     int preferredmessagesize;
44     char *outbuf;                      /* intermediary buffer */
45     char *inbuf;
46     int inbuflen;
47 };
48
49 static Z_APDU *get_apdu(struct zass *z)
50 {
51     int res;
52     Z_APDU *ap;
53
54     if ((res = cs_get(z->ass, &z->inbuf, &z->inbuflen)) <= 0)
55     {
56         gw_log(GW_LOG_WARN, ZASS_TYPE, "cs_get failed");
57         return 0;
58     }
59     odr_reset(z->decode);
60     odr_setbuf(z->decode, z->inbuf, res);
61     if (!z_APDU(z->decode, &ap, 0))
62     {
63         gw_log(GW_LOG_WARN, ZASS_TYPE, "decode: %s",
64             odr_errlist[odr_geterror(z->decode)]);
65         return 0;
66     }
67     return ap;
68 }
69
70 static int send_apdu(struct zass *z, Z_APDU *a)
71 {
72     char *buf;
73     int len;
74
75     if (!z_APDU(z->encode, &a, 0))
76     {
77         gw_log(GW_LOG_FATAL, ZASS_TYPE, "encoding initreq");
78         return -1;
79     }
80     buf = odr_getbuf(z->encode, &len);
81     if (cs_put(z->ass, buf, len) < 0)
82     {
83         gw_log(GW_LOG_FATAL, ZASS_TYPE, "cs_put");
84         return -1;
85     }
86     odr_reset(z->encode);
87     return 0;
88 }
89
90 static int send_initreq(struct zass *p)
91 {
92     Z_APDU a;
93     Z_InitRequest init;
94     Odr_bitmask options, protocolVersion;
95     char name[512];
96
97     a.which = Z_APDU_initRequest;
98     a.u.initRequest = &init;
99     init.referenceId = 0;
100     init.options = &options;
101     ODR_MASK_ZERO(&options);
102     ODR_MASK_SET(&options, Z_Options_search);
103     ODR_MASK_SET(&options, Z_Options_present);
104     ODR_MASK_SET(&options, Z_Options_delSet);
105     init.protocolVersion = &protocolVersion;
106     ODR_MASK_ZERO(&protocolVersion);
107     ODR_MASK_SET(&protocolVersion, Z_ProtocolVersion_1);
108     ODR_MASK_SET(&protocolVersion, Z_ProtocolVersion_2);
109     init.preferredMessageSize = &p->preferredmessagesize;
110     init.maximumRecordSize = &p->maxrecordsize;
111     init.idAuthentication = 0;
112     init.implementationId = ZASS_ID;
113     sprintf(name, "%s (YAZ protocol layer)", ZASS_NAME);
114     init.implementationName = name;
115     init.implementationVersion = ZASS_VERSION;
116     init.userInformationField = 0;
117     if (send_apdu(p, &a) < 0)
118         return -1;
119     return 0;
120 }
121
122 static int receive_initres(struct zass *p)
123 {
124     Z_APDU *ap;
125     Z_InitResponse *res;
126
127     if (!(ap = get_apdu(p)))
128         return -1;
129     if (ap->which != Z_APDU_initResponse)
130     {
131         gw_log(GW_LOG_WARN, ZASS_TYPE, "bad APDU");
132         return -1;
133     }
134     res = ap->u.initResponse;
135     if (!*res->result)
136     {
137         gw_log(GW_LOG_WARN, ZASS_TYPE, "Negative result on initres");
138         return -1;
139     }
140     p->preferredmessagesize = *res->preferredMessageSize;
141     p->maxrecordsize = *res->maximumRecordSize;
142     if (res->implementationId)
143         gw_log(GW_LOG_NOTICE, ZASS_TYPE, "imp. ID  : %s",
144             res->implementationId);
145     if (res->implementationName)
146         gw_log(GW_LOG_NOTICE, ZASS_TYPE, "imp. ID  : %s",
147             res->implementationName);
148     if (res->implementationVersion)
149         gw_log(GW_LOG_NOTICE, ZASS_TYPE, "imp. ID  : %s",
150             res->implementationVersion);
151     gw_log(ZASS_DEBUG, ZASS_TYPE, "Initialized ok");
152     return 0;
153 }
154
155 ZASS zass_open(char *host, int port)
156 {
157     struct zass *p;
158     char addstr[512];
159     void *address;
160
161     if (!(p = malloc(sizeof(*p))))
162     {
163         gw_log(GW_LOG_FATAL|GW_LOG_ERRNO, ZASS_TYPE, "malloc");
164         return 0;
165     }
166     if (!(p->encode = odr_createmem(ODR_ENCODE)) ||
167         !(p->decode = odr_createmem(ODR_DECODE)))
168     {
169         gw_log(GW_LOG_FATAL|GW_LOG_ERRNO, ZASS_TYPE, "odr_createmem");
170         return 0;
171     }
172     p->maxrecordsize = ZASS_MAXRECORDSIZE;
173     p->preferredmessagesize = ZASS_PREFERREDMESSAGESIZE;
174     if (!(p->outbuf = malloc(p->maxrecordsize + 1024)))
175     {
176         gw_log(GW_LOG_FATAL|GW_LOG_ERRNO, ZASS_TYPE, "malloc");
177         return 0;
178     }
179     odr_setbuf(p->encode, p->outbuf, p->maxrecordsize + 1024);
180     if (!(p->ass = cs_create(tcpip_type, 1, CS_Z3950)))
181     {
182         gw_log(GW_LOG_FATAL|GW_LOG_ERRNO, ZASS_TYPE, "cs_create");
183         return 0;
184     }
185     sprintf(addstr, "%s:%d", host, port);
186     if (!(address = tcpip_strtoaddr(addstr)))
187     {
188         gw_log(GW_LOG_FATAL, ZASS_TYPE, "failed to resolve %s", addstr);
189         return 0;
190     }
191     p->fd = cs_fileno(p->ass);
192     gw_log(ZASS_DEBUG, ZASS_TYPE, "Connecting to %s", addstr);
193     if (cs_connect(p->ass, address) < 0)
194     {
195         gw_log(GW_LOG_FATAL, ZASS_TYPE, "Failed to connect to %s", addstr);
196         return 0;
197     }
198     gw_log(ZASS_DEBUG, ZASS_TYPE, "connected ok");
199     p->inbuf = 0;
200     p->inbuflen = 0;
201     if (send_initreq(p) < 0 || receive_initres(p) < 0)
202     {
203         gw_log(GW_LOG_FATAL, ZASS_TYPE, "Failed to initialize");
204         return 0;
205     }
206     gw_log(ZASS_DEBUG, ZASS_TYPE, "Sent init request");
207     return p;
208 }
209
210 static Z_RPNStructure *rpn2rpn(ODR o, struct ccl_rpn_node *q)
211 {
212     Z_RPNStructure *r = odr_malloc(o, sizeof(*r));
213     Z_AttributesPlusTerm *t;
214     struct ccl_rpn_attr *i;
215     int len;
216     static int op[] = { Z_Operator_and, Z_Operator_or, Z_Operator_and_not };
217
218     switch (q->kind)
219     {
220         case CCL_RPN_TERM:
221             r->which = Z_RPNStructure_simple;
222             r->u.simple = odr_malloc(o, sizeof(Z_Operand));
223             r->u.simple->which = Z_Operand_APT;
224             r->u.simple->u.attributesPlusTerm = t = odr_malloc(o, sizeof(*t));
225             t->term = odr_malloc(o, sizeof(Z_Term));
226             t->term->which = Z_Term_general;
227             t->term->u.general = odr_malloc(o, sizeof(Odr_oct));
228             t->term->u.general->len = t->term->u.general->size =
229                 strlen(q->u.t.term);
230             t->term->u.general->buf = odr_malloc(o, t->term->u.general->size);
231             memcpy(t->term->u.general->buf, q->u.t.term,
232                 t->term->u.general->len);
233             t->num_attributes = 0;
234             t->attributeList = odr_malloc(o, sizeof(Z_AttributeElement*) * 100);
235             for (i = q->u.t.attr_list; i && t->num_attributes < 100;
236                 i = i->next)
237             {
238                 Z_AttributeElement *a;
239                 
240                 t->attributeList[t->num_attributes++] = a =
241                     odr_malloc(o, sizeof(*a));
242                 a->attributeType = odr_malloc(o, sizeof(int));
243                 *a->attributeType = i->type;
244                 a->attributeValue = odr_malloc(o, sizeof(*a));
245                 *a->attributeValue = i->value;
246             }
247             return r;
248         case CCL_RPN_SET:
249             r->which = Z_RPNStructure_simple;
250             r->u.simple = odr_malloc(o, sizeof(Z_Operand));
251             r->u.simple->which = Z_Operand_resultSetId;
252             r->u.simple->u.resultSetId = odr_malloc(o, len =
253                 strlen(q->u.setname));
254             memcpy(r->u.simple->u.resultSetId, q->u.setname, len);
255             return r;
256         case CCL_RPN_AND: case CCL_RPN_OR: case CCL_RPN_NOT:
257             r->which = Z_RPNStructure_complex;
258             r->u.complex = odr_malloc(o, sizeof(Z_Complex));
259             if (!(r->u.complex->s1 = rpn2rpn(o, q->u.p[0])) ||
260                 !(r->u.complex->s2 = rpn2rpn(o, q->u.p[1])))
261                     return 0;
262             r->u.complex->operator = odr_malloc(o, sizeof(Z_Operator));
263             r->u.complex->operator->which = op[q->kind];
264             r->u.complex->operator->u.and = "";
265             return r;
266         default:
267             gw_log(GW_LOG_FATAL, ZASS_TYPE, "Bad operator in RPN");
268             return 0;
269     }
270 }
271
272 static const struct zass_searchent *search_result(ZASS a)
273 {
274     static struct zass_searchent r;
275     Z_APDU *apdu;
276     Z_SearchResponse *res;
277
278     if (!(apdu = get_apdu(a)))
279         return 0;
280     if (apdu->which != Z_APDU_searchResponse)
281     {
282         gw_log(GW_LOG_FATAL, ZASS_TYPE, "Expected searchresponse, got #%d",
283             apdu->which);
284         return 0;
285     }
286     res = apdu->u.searchResponse;
287     r.status = *res->searchStatus;
288     r.num = *res->resultCount;
289     r.status = res->resultSetStatus ? *res->resultSetStatus : 0;
290     r.errcode = -1;
291     *r.errstring = '\0';
292     if (res->records)
293     {
294         if (res->records->which != Z_Records_NSD)
295             gw_log(GW_LOG_WARN, ZASS_TYPE, "Unexpected record types in SchR");
296         else
297         {
298             oident *id;
299             Z_DiagRec *dr = res->records->u.nonSurrogateDiagnostic;
300
301             if (!(id = oid_getentbyoid(dr->diagnosticSetId)) ||
302                 id->class != CLASS_DIAGSET || id->value != VAL_BIB1)
303                     gw_log(GW_LOG_WARN, ZASS_TYPE,
304                         "Missing or unknown diagset - ignoring error!");
305             else
306             {
307                 r.errcode = *dr->condition;
308                 if (dr->addinfo)
309                 {
310                     strncpy(r.errstring, dr->addinfo, 512);
311                     r.errstring[511] = '\0';
312                 }
313             }
314         }
315     }
316     return &r;
317 }
318
319 const struct zass_searchent *zass_search(ZASS a, struct ccl_rpn_node *query,
320     char *resname, char *databases)
321 {
322     Z_Query q;
323     Z_RPNQuery rpnq;
324     Z_APDU apdu;
325     Z_SearchRequest req;
326     oident bib1;
327     int smallSetUpperBound = 0;
328     int largeSetLowerBound = 1;
329     int mediumSetPresentNumber = 0;
330     int replaceIndicator = 1;
331     char *datab[100];
332
333     apdu.which = Z_APDU_searchRequest;
334     apdu.u.searchRequest = &req;
335     req.referenceId = 0;
336     req.smallSetUpperBound = &smallSetUpperBound;
337     req.largeSetLowerBound = &largeSetLowerBound;
338     req.mediumSetPresentNumber = &mediumSetPresentNumber;
339     req.replaceIndicator = &replaceIndicator;
340     req.resultSetName = resname;
341     req.num_databaseNames = 0;
342     req.databaseNames = datab;
343     while (*databases && req.num_databaseNames < 100)
344     {
345         char *p = databases;
346         int more;
347
348         while (*p && !isspace(*p))
349             p++;
350         if (isspace(*p))
351             more = 1;
352         else
353             more = 0;
354         *p = '\0';
355         if (p - databases)
356         {
357             req.databaseNames[req.num_databaseNames] = odr_malloc(a->encode,
358                 (p - databases) + 1);
359             strcpy(req.databaseNames[req.num_databaseNames++], databases);
360         }
361         databases = p + more;
362     }
363     req.smallSetElementSetNames = 0;
364     req.mediumSetElementSetNames = 0;
365     req.preferredRecordSyntax = 0;
366     req.query = &q;
367     q.which = Z_Query_type_1;
368     q.u.type_1 = &rpnq;
369     bib1.proto = PROTO_Z3950;
370     bib1.class = CLASS_ATTSET;
371     bib1.value = VAL_BIB1;
372     rpnq.attributeSetId = oid_getoidbyent(&bib1);
373     if (!(rpnq.RPNStructure = rpn2rpn(a->encode, query)))
374         return 0;
375     if (send_apdu(a, &apdu) < 0)
376         return 0;
377
378     return search_result(a);
379 }
380
381 /*
382  * Triple indirection - that's kinda heavy. We'll fix it later.
383  * There are worse things around, though. Like ZDist.
384  */
385 void get_diagrec(zass_record ***p, Z_DiagRec *r)
386 {
387     **p = malloc(sizeof(***p));
388     (**p)->next = 0;
389     (**p)->errcode = *r->condition;
390     if (r->addinfo)
391     {
392         strncpy((**p)->errstring, r->addinfo, 200);
393         (**p)->errstring[200] = 0;
394     }
395     else
396         (**p)->errstring[0] = '\0';
397     (**p)->which = ZASS_REC_DIAG;
398     *p = &(**p)->next;
399 }
400
401 void get_responserecords(zass_record ***p, Z_NamePlusRecordList *recs)
402 {
403     int i;
404
405     for (i = 0; i < recs->num_records; i++)
406     {
407         Z_NamePlusRecord *record;
408
409         record = recs->records[i];
410         if (record->which == Z_NamePlusRecord_surrogateDiagnostic)
411             get_diagrec(p, record->u.surrogateDiagnostic);
412         else
413         {
414             Z_DatabaseRecord *r = record->u.databaseRecord;
415             oident *recform;
416
417             **p = malloc(sizeof(***p));
418             (**p)->next = 0;
419
420             if (!(recform = oid_getentbyoid(r->direct_reference)) ||
421                  recform->class != CLASS_RECSYN)
422             {
423                 gw_log(GW_LOG_WARN, ZASS_TYPE, "Unknown or bad record syntax");
424                 (**p)->which = ZASS_REC_UNKNOWN;
425             }
426             else
427                 switch (recform->value)
428                 {
429                     case VAL_USMARC: (**p)->which = ZASS_REC_USMARC; break;
430                     default:
431                         gw_log(GW_LOG_WARN, ZASS_TYPE, "Unknown recsyn");
432                         (**p)->which = ZASS_REC_UNKNOWN;
433                 }
434             if (r->which != ODR_EXTERNAL_octet)
435             {
436                 gw_log(GW_LOG_WARN, ZASS_TYPE, "Record wasn't octet-aligned");
437                 (**p)->record = 0;
438             }
439             else
440             {
441                 if (!((**p)->record = malloc(r->u.octet_aligned->len + 1)))
442                 {
443                     gw_log(GW_LOG_FATAL, ZASS_TYPE, "malloc");
444                     return;
445                 }
446                 memcpy((**p)->record, r->u.octet_aligned->buf,
447                     r->u.octet_aligned->len);
448                 (**p)->record[r->u.octet_aligned->len] = '\0';
449                 gw_log(ZASS_DEBUG, ZASS_TYPE, "Got a record of %d bytes",
450                     r->u.octet_aligned->len);
451             }
452         }
453         (*p) = &(**p)->next;
454     }
455 }
456
457 static void zass_records_free(zass_record *p)
458 {
459 }
460
461 static int send_present(ZASS a, char *name, int start, int num,
462     enum oid_value form)
463 {
464     Z_APDU apdu;
465     Z_PresentRequest req;
466     oident recsyn;
467
468     apdu.which = Z_APDU_presentRequest;
469     apdu.u.presentRequest = &req;
470     req.referenceId = 0;
471     req.resultSetId = name;
472     req.resultSetStartPoint = &start;
473     req.numberOfRecordsRequested = &num;
474     req.elementSetNames = 0;
475     recsyn.proto = PROTO_Z3950;
476     recsyn.class = CLASS_RECSYN;
477     recsyn.value = form;
478     req.preferredRecordSyntax = oid_getoidbyent(&recsyn);
479     return send_apdu(a, &apdu);
480 }
481
482 /*
483  * Note that 1== first record.
484  */
485 const struct zass_presentent *zass_present(ZASS a, char *resname, int start,
486     int num)
487 {
488     static struct zass_presentent r;
489     zass_record **rec = &r.records;
490
491     r.num = 0;
492     if (r.records)
493     {
494         zass_records_free(r.records);
495         r.records = 0;
496     }
497     do
498     {
499         Z_APDU *apdu;
500         Z_PresentResponse *res;
501
502         gw_log(ZASS_DEBUG, ZASS_TYPE,
503             "Fetching %d records from # %d", num - r.num, start);
504         if (send_present(a, resname, start, num - r.num, VAL_USMARC) < 0)
505             return 0;
506         if (!(apdu = get_apdu(a)))
507             return 0;
508         if (apdu->which != Z_APDU_presentResponse)
509         {
510             gw_log(GW_LOG_FATAL, ZASS_TYPE, "Expected presentresponse, got #%d",
511                 apdu->which);
512             return 0;
513         }
514         res = apdu->u.presentResponse;
515         r.presentstatus = *res->presentStatus;
516         r.num += *res->numberOfRecordsReturned;
517         if (*res->numberOfRecordsReturned == 0)
518         {
519             gw_log(GW_LOG_WARN, ZASS_TYPE, "Got 0 records from target");
520             return 0;
521         }
522         r.nextpos = *res->nextResultSetPosition;
523         start = r.nextpos;
524         switch (res->records->which)
525         {
526             case Z_Records_DBOSD:
527                 get_responserecords(&rec,
528                     res->records->u.databaseOrSurDiagnostics);
529                 break;
530             case Z_Records_NSD:
531                 get_diagrec(&rec, res->records->u.nonSurrogateDiagnostic);
532                 break;
533             default:
534                 gw_log(GW_LOG_WARN, ZASS_TYPE, "Bad tag in response rec.");
535                 return 0;
536         }
537     }
538     while (num - r.num && start);
539     return &r;
540 }