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