Bug fix: multiple databases weren't specified correctly.
[egate.git] / zlayer-yaz / zaccess.c
1 /*
2  * Copyright (c) 1995, the EUROPAGATE consortium (see below).
3  *
4  * The EUROPAGATE consortium members are:
5  *
6  *    University College Dublin
7  *    Danmarks Teknologiske Videnscenter
8  *    An Chomhairle Leabharlanna
9  *    Consejo Superior de Investigaciones Cientificas
10  *
11  * Permission to use, copy, modify, distribute, and sell this software and
12  * its documentation, in whole or in part, for any purpose, is hereby granted,
13  * provided that:
14  *
15  * 1. This copyright and permission notice appear in all copies of the
16  * software and its documentation. Notices of copyright or attribution
17  * which appear at the beginning of any file must remain unchanged.
18  *
19  * 2. The names of EUROPAGATE or the project partners may not be used to
20  * endorse or promote products derived from this software without specific
21  * prior written permission.
22  *
23  * 3. Users of this software (implementors and gateway operators) agree to
24  * inform the EUROPAGATE consortium of their use of the software. This
25  * information will be used to evaluate the EUROPAGATE project and the
26  * software, and to plan further developments. The consortium may use
27  * the information in later publications.
28  * 
29  * 4. Users of this software agree to make their best efforts, when
30  * documenting their use of the software, to acknowledge the EUROPAGATE
31  * consortium, and the role played by the software in their work.
32  *
33  * THIS SOFTWARE IS PROVIDED "AS IS" AND WITHOUT WARRANTY OF ANY KIND,
34  * EXPRESS, IMPLIED, OR OTHERWISE, INCLUDING WITHOUT LIMITATION, ANY
35  * WARRANTY OF MERCHANTABILITY OR FITNESS FOR A PARTICULAR PURPOSE.
36  * IN NO EVENT SHALL THE EUROPAGATE CONSORTIUM OR ITS MEMBERS BE LIABLE
37  * FOR ANY SPECIAL, INCIDENTAL, INDIRECT OR CONSEQUENTIAL DAMAGES OF
38  * ANY KIND, OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA
39  * OR PROFITS, WHETHER OR NOT ADVISED OF THE POSSIBILITY OF DAMAGE, AND
40  * ON ANY THEORY OF LIABILITY, ARISING OUT OF OR IN CONNECTION WITH THE
41  * USE OR PERFORMANCE OF THIS SOFTWARE.
42  *
43  */
44 /*
45  * Europagate, 1995
46  *
47  * Z39.50 API for the Email gateway - YAZ version
48  *
49  * $Log: zaccess.c,v $
50  * Revision 1.4  1995/07/28 10:51:34  adam
51  * Bug fix: multiple databases weren't specified correctly.
52  *
53  * Revision 1.3  1995/07/20  08:18:15  adam
54  * Bug fix: Multiple databases are separated by commas and NOT by space.
55  *
56  * Revision 1.2  1995/07/05  11:08:33  adam
57  * Bug fix in init request.
58  *
59  * Revision 1.1  1995/07/03  08:21:31  adam
60  * Yaz layer moved to new sub directory. zaccess aligned with new
61  * YAZ version (1.0b).
62  *
63  * Revision 1.10  1995/05/16  09:41:46  adam
64  * LICENSE. Uses new versions of odr_{set,get}buf.
65  *
66  * Revision 1.9  1995/05/03  10:15:43  quinn
67  * Fixed bug in the get_record loop - crashed when diagrec was received.
68  *
69  * Revision 1.8  1995/04/28  14:18:38  quinn
70  * *** empty log message ***
71  *
72  * Revision 1.7  1995/04/21  16:29:32  quinn
73  * Fixed bugs.
74  *
75  * Revision 1.6  1995/04/21  12:58:36  adam
76  * Bug fix.
77  *
78  * Revision 1.5  1995/04/20  15:25:32  quinn
79  * Asynch. API
80  *
81  * Revision 1.4  1995/04/19  16:02:28  adam
82  * Record type hack.
83  *
84  * Revision 1.3  1995/04/19  12:55:15  quinn
85  * Added auth.
86  *
87  * Revision 1.2  1995/04/19  09:24:02  quinn
88  * Fixed bug in zass_open - variable initialized after use
89  *
90  * Revision 1.1  1995/04/17  11:26:53  quinn
91  * Added YAZ version of zaccess
92  *
93  */
94
95 /*
96  * Interface to the YAZ Z39.50 toolkit.
97  */
98
99 #include <stdlib.h>
100 #include <assert.h>
101 #include <ctype.h>
102
103 #include <gw-log.h>
104 #include <proto.h>
105 #include <comstack.h>
106 #include <tcpip.h>
107 #ifdef USE_XTIMOSI
108 #include <xmosi.h>
109 #endif
110 #include <oid.h>
111
112 #include <ccl.h>
113 #include <zaccess.h>
114
115 struct zass    /* Z-assoc */
116 {
117     COMSTACK ass;              /* comstack association handle */
118     ODR encode;
119     ODR decode;
120     int fd;                         /* low-level socket (for select only) */
121     int nonblocking;
122     int maxrecordsize;
123     int preferredmessagesize;
124     char *inbuf;
125     int inbuflen;
126 };
127
128 static Z_APDU *get_apdu(struct zass *z, int *complete)
129 {
130     int res;
131     Z_APDU *ap;
132
133     if ((res = cs_get(z->ass, &z->inbuf, &z->inbuflen)) <= 0)
134     {
135         gw_log(GW_LOG_WARN, ZASS_TYPE, "cs_get failed");
136         if (complete)
137             *complete = 1;
138         return 0;
139     }
140     else if (res == 1)
141     {
142         if (complete)
143             *complete = 0;
144         return 0;
145     }
146     odr_reset(z->decode);
147     odr_setbuf(z->decode, z->inbuf, res, 0);
148     if (!z_APDU(z->decode, &ap, 0))
149     {
150         gw_log(GW_LOG_WARN, ZASS_TYPE, "decode: %s",
151             odr_errlist[odr_geterror(z->decode)]);
152         if (complete)
153             *complete = 0;
154         return 0;
155     }
156     if (complete)
157         *complete = 1;
158     return ap;
159 }
160
161 static int send_apdu(struct zass *z, Z_APDU *a)
162 {
163     char *buf;
164     int len;
165
166     if (!z_APDU(z->encode, &a, 0))
167     {
168         gw_log(GW_LOG_FATAL, ZASS_TYPE, "encoding initreq");
169         return -1;
170     }
171     buf = odr_getbuf(z->encode, &len, NULL);
172     if (cs_put(z->ass, buf, len) < 0)
173     {
174         gw_log(GW_LOG_FATAL, ZASS_TYPE, "cs_put");
175         return -1;
176     }
177     odr_reset(z->encode);  /* release odr_allocated structures */
178     return 0;
179 }
180
181 static int send_initreq(struct zass *p, char *auth)
182 {
183     Z_APDU *apdu;
184     Z_InitRequest *req;
185     char name[512];
186     Z_IdAuthentication idauth;
187
188     apdu = zget_APDU (p->encode, Z_APDU_initRequest);
189     req = apdu->u.initRequest;
190     req->preferredMessageSize = &p->preferredmessagesize;
191     req->maximumRecordSize = &p->maxrecordsize;
192
193     ODR_MASK_SET(req->options, Z_Options_search);
194     ODR_MASK_SET(req->options, Z_Options_present);
195     ODR_MASK_SET(req->options, Z_Options_namedResultSets);
196
197     ODR_MASK_SET(req->protocolVersion, Z_ProtocolVersion_1);
198     ODR_MASK_SET(req->protocolVersion, Z_ProtocolVersion_2);
199
200     if (auth)
201     {
202         req->idAuthentication = &idauth;
203         idauth.which = Z_IdAuthentication_open;
204         idauth.u.open = auth;
205     }
206     else
207         req->idAuthentication = 0;
208     sprintf(name, "%s (YAZ protocol layer)", ZASS_NAME);
209     req->implementationName = name;
210     req->implementationVersion = ZASS_VERSION;
211     if (send_apdu(p, apdu) < 0)
212         return -1;
213     return 0;
214 }
215
216 int zass_fileno(ZASS a)
217 {
218     return a->fd;
219 }
220
221 int zass_openresult(ZASS p, int *complete)
222 {
223     Z_APDU *ap;
224     Z_InitResponse *res;
225
226     if (!(ap = get_apdu(p, complete)))
227         return -1;
228     if (ap->which != Z_APDU_initResponse)
229     {
230         gw_log(GW_LOG_WARN, ZASS_TYPE, "bad APDU");
231         return -1;
232     }
233     res = ap->u.initResponse;
234     if (!*res->result)
235     {
236         gw_log(GW_LOG_WARN, ZASS_TYPE, "Negative result on initres");
237         return -1;
238     }
239     p->preferredmessagesize = *res->preferredMessageSize;
240     p->maxrecordsize = *res->maximumRecordSize;
241     if (res->implementationId)
242         gw_log(GW_LOG_NOTICE, ZASS_TYPE, "imp. ID  : %s",
243             res->implementationId);
244     if (res->implementationName)
245         gw_log(GW_LOG_NOTICE, ZASS_TYPE, "imp. ID  : %s",
246             res->implementationName);
247     if (res->implementationVersion)
248         gw_log(GW_LOG_NOTICE, ZASS_TYPE, "imp. ID  : %s",
249             res->implementationVersion);
250     gw_log(ZASS_DEBUG, ZASS_TYPE, "Initialized ok");
251     return 0;
252 }
253
254 ZASS zass_open(char *host, int port, int *complete, char *auth)
255 {
256     struct zass *p;
257     char addstr[512];
258     void *address;
259
260     if (!(p = malloc(sizeof(*p))))
261     {
262         gw_log(GW_LOG_FATAL|GW_LOG_ERRNO, ZASS_TYPE, "malloc");
263         return 0;
264     }
265     if (complete)
266     {
267         *complete = 1;
268         p->nonblocking = 1;
269     }
270     else
271         p->nonblocking = 0;
272     if (!(p->encode = odr_createmem(ODR_ENCODE)) ||
273         !(p->decode = odr_createmem(ODR_DECODE)))
274     {
275         gw_log(GW_LOG_FATAL|GW_LOG_ERRNO, ZASS_TYPE, "odr_createmem");
276         return 0;
277     }
278     p->maxrecordsize = ZASS_MAXRECORDSIZE;
279     p->preferredmessagesize = ZASS_PREFERREDMESSAGESIZE;
280     if (!(p->ass = cs_create(tcpip_type, 1, CS_Z3950)))
281     {
282         gw_log(GW_LOG_FATAL|GW_LOG_ERRNO, ZASS_TYPE, "cs_create");
283         return 0;
284     }
285     p->inbuf = 0;
286     p->inbuflen = 0;
287     sprintf(addstr, "%s:%d", host, port);
288     if (!(address = tcpip_strtoaddr(addstr)))
289     {
290         gw_log(GW_LOG_FATAL, ZASS_TYPE, "failed to resolve %s", addstr);
291         return 0;
292     }
293     p->fd = cs_fileno(p->ass);
294     gw_log(ZASS_DEBUG, ZASS_TYPE, "Connecting to %s", addstr);
295     if (cs_connect(p->ass, address) < 0)
296     {
297         gw_log(GW_LOG_FATAL, ZASS_TYPE, "Failed to connect to %s", addstr);
298         return 0;
299     }
300     gw_log(ZASS_DEBUG, ZASS_TYPE, "connected ok... initializing");
301     if (send_initreq(p, auth) < 0)
302     {
303         gw_log(GW_LOG_FATAL, ZASS_TYPE, "Failed to send init");
304         return 0;
305     }
306     if (p->nonblocking)
307     {
308         *complete = 0;
309         return p;
310     }
311     if (zass_openresult(p, complete) < 0)
312         return 0;
313     return p; /* all done */
314 }
315
316 /*
317  * Convert the egate (ccl2rpn) version of RPN to YAZ RPN.
318  */
319 static Z_RPNStructure *rpn2rpn(ODR o, struct ccl_rpn_node *q)
320 {
321     Z_RPNStructure *r = odr_malloc(o, sizeof(*r));
322     Z_AttributesPlusTerm *t;
323     struct ccl_rpn_attr *i;
324     int len;
325     static int op[] = { Z_Operator_and, Z_Operator_or, Z_Operator_and_not };
326
327     switch (q->kind)
328     {
329         case CCL_RPN_TERM:
330             r->which = Z_RPNStructure_simple;
331             r->u.simple = odr_malloc(o, sizeof(Z_Operand));
332             r->u.simple->which = Z_Operand_APT;
333             r->u.simple->u.attributesPlusTerm = t = odr_malloc(o, sizeof(*t));
334             t->term = odr_malloc(o, sizeof(Z_Term));
335             t->term->which = Z_Term_general;
336             t->term->u.general = odr_malloc(o, sizeof(Odr_oct));
337             t->term->u.general->len = t->term->u.general->size =
338                 strlen(q->u.t.term);
339             t->term->u.general->buf = odr_malloc(o, t->term->u.general->size);
340             memcpy(t->term->u.general->buf, q->u.t.term,
341                 t->term->u.general->len);
342             t->num_attributes = 0;
343             t->attributeList = odr_malloc(o, sizeof(Z_AttributeElement*) * 100);
344             for (i = q->u.t.attr_list; i && t->num_attributes < 100;
345                 i = i->next)
346             {
347                 Z_AttributeElement *a;
348                 
349                 t->attributeList[t->num_attributes++] = a =
350                     odr_malloc(o, sizeof(*a));
351                 a->attributeType = odr_malloc(o, sizeof(int));
352                 *a->attributeType = i->type;
353 #ifdef Z_95
354                 a->attributeSet = 0;
355                 a->which = Z_AttributeValue_numeric;
356                 a->value.numeric = odr_malloc(o, sizeof(*a->value.numeric));
357                 *a->value.numeric = i->value;
358 #else
359                 a->attributeValue = odr_malloc(o, sizeof(*a->attributeValue));
360                 *a->attributeValue = i->value;
361 #endif
362             }
363             return r;
364         case CCL_RPN_SET:
365             r->which = Z_RPNStructure_simple;
366             r->u.simple = odr_malloc(o, sizeof(Z_Operand));
367             r->u.simple->which = Z_Operand_resultSetId;
368             r->u.simple->u.resultSetId = odr_malloc(o, len =
369                 strlen(q->u.setname) + 1);
370             memcpy(r->u.simple->u.resultSetId, q->u.setname, len);
371             return r;
372         case CCL_RPN_AND: case CCL_RPN_OR: case CCL_RPN_NOT:
373             r->which = Z_RPNStructure_complex;
374             r->u.complex = odr_malloc(o, sizeof(Z_Complex));
375             if (!(r->u.complex->s1 = rpn2rpn(o, q->u.p[0])) ||
376                 !(r->u.complex->s2 = rpn2rpn(o, q->u.p[1])))
377                     return 0;
378             r->u.complex->operator = odr_malloc(o, sizeof(Z_Operator));
379             r->u.complex->operator->which = op[q->kind];
380             r->u.complex->operator->u.and = "";
381             return r;
382         default:
383             gw_log(GW_LOG_FATAL, ZASS_TYPE, "Bad operator in RPN");
384             return 0;
385     }
386 }
387
388 const struct zass_searchent *zass_searchresult(ZASS a, int *complete)
389 {
390     static struct zass_searchent r;
391     Z_APDU *apdu;
392     Z_SearchResponse *res;
393
394     if (!(apdu = get_apdu(a, complete)))
395         return 0;
396     if (apdu->which != Z_APDU_searchResponse)
397     {
398         gw_log(GW_LOG_FATAL, ZASS_TYPE, "Expected searchresponse, got #%d",
399             apdu->which);
400         return 0;
401     }
402     res = apdu->u.searchResponse;
403     r.status = *res->searchStatus;
404     r.num = *res->resultCount;
405     r.status = res->resultSetStatus ? *res->resultSetStatus : 0;
406     r.errcode = -1;
407     *r.errstring = '\0';
408     if (res->records)
409     {
410         if (res->records->which != Z_Records_NSD)
411             gw_log(GW_LOG_WARN, ZASS_TYPE, "Unexpected record types in SchR");
412         else
413         {
414             oident *id;
415             Z_DiagRec *dr = res->records->u.nonSurrogateDiagnostic;
416
417 #ifdef Z_95
418             if (dr->which != Z_DiagRec_defaultFormat || 
419                 !(id = oid_getentbyoid 
420                        (dr->u.defaultFormat->diagnosticSetId)) ||
421                 id->class != CLASS_DIAGSET || 
422                 id->value != VAL_BIB1)
423                     gw_log(GW_LOG_WARN, ZASS_TYPE,
424                         "Missing or unknown diagset - ignoring error!");
425             else
426             {
427                 r.errcode = *dr->u.defaultFormat->condition;
428                 if (dr->u.defaultFormat->addinfo)
429                 {
430                     strncpy(r.errstring, dr->u.defaultFormat->addinfo, 512);
431                     r.errstring[511] = '\0';
432                 }
433             }
434 #else
435             if (!(id = oid_getentbyoid(dr->diagnosticSetId)) ||
436                 id->class != CLASS_DIAGSET || id->value != VAL_BIB1)
437                     gw_log(GW_LOG_WARN, ZASS_TYPE,
438                         "Missing or unknown diagset - ignoring error!");
439             else
440             {
441                 r.errcode = *dr->condition;
442                 if (dr->addinfo)
443                 {
444                     strncpy(r.errstring, dr->addinfo, 512);
445                     r.errstring[511] = '\0';
446                 }
447             }
448 #endif
449         }
450     }
451     return &r;
452 }
453
454 const struct zass_searchent *zass_search(ZASS a, struct ccl_rpn_node *query,
455     char *resname, char *databases, int *complete)
456 {
457     Z_Query q;
458     Z_RPNQuery rpnq;
459     Z_APDU *apdu;
460     Z_SearchRequest *req;
461     oident bib1;
462     int smallSetUpperBound = 0;
463     int largeSetLowerBound = 1;
464     int mediumSetPresentNumber = 0;
465     int replaceIndicator = 1;
466     char *datab[100];
467
468     apdu = zget_APDU (a->encode, Z_APDU_searchRequest);
469     req = apdu->u.searchRequest;
470
471     req->smallSetUpperBound = &smallSetUpperBound;
472     req->largeSetLowerBound = &largeSetLowerBound;
473     req->mediumSetPresentNumber = &mediumSetPresentNumber;
474     req->replaceIndicator = &replaceIndicator;
475     req->resultSetName = resname;
476     req->num_databaseNames = 0;
477     req->databaseNames = datab;
478     while (*databases && req->num_databaseNames < 100)
479     {
480         char *p = databases;
481         int more;
482
483         while (*p && !isspace(*p) && *p != ',')
484             p++;
485         if (*p)
486             more = 1;
487         else
488             more = 0;
489         if (p - databases)
490         {
491             req->databaseNames[req->num_databaseNames] = odr_malloc(a->encode,
492                 (p - databases) + 1);
493             memcpy(req->databaseNames[req->num_databaseNames], databases, 
494                    p-databases);
495             req->databaseNames[req->num_databaseNames++][p-databases] = '\0';
496         }
497         databases = p + more;
498     }
499     req->query = &q;
500     q.which = Z_Query_type_1;
501     q.u.type_1 = &rpnq;
502     bib1.proto = PROTO_Z3950;
503     bib1.class = CLASS_ATTSET;
504     bib1.value = VAL_BIB1;
505     rpnq.attributeSetId = oid_getoidbyent(&bib1);
506
507     if (complete)
508         *complete = 1;
509     if (!(rpnq.RPNStructure = rpn2rpn(a->encode, query)))
510         return 0;
511     if (send_apdu(a, apdu) < 0)
512         return 0;
513     if (complete)
514     {
515         *complete = 0;
516         return 0;
517     }
518     else
519         return zass_searchresult(a, complete);
520 }
521
522 /*
523  * Triple indirection - that's kinda heavy. We'll fix it later.
524  * There are worse things around, though. Like ZDist.
525  */
526 void get_diagrec(zass_record ***p, Z_DiagRec *r)
527 {
528     **p = malloc(sizeof(***p));
529     (**p)->next = 0;
530 #ifdef Z_95
531     (**p)->errstring[0] = '\0';
532     if (r->which == Z_DiagRec_defaultFormat)
533     {
534         (**p)->errcode = *r->u.defaultFormat->condition;
535         if (r->u.defaultFormat->addinfo)
536         {
537             strncpy((**p)->errstring, r->u.defaultFormat->addinfo, 200);
538             (**p)->errstring[200] = 0;
539         }
540     }
541     else
542         (**p)->errcode = -1;
543 #else
544     (**p)->errcode = *r->condition;
545     if (r->addinfo)
546     {
547         strncpy((**p)->errstring, r->addinfo, 200);
548         (**p)->errstring[200] = 0;
549     }
550     else
551         (**p)->errstring[0] = '\0';
552 #endif
553     (**p)->which = ZASS_REC_DIAG;
554     *p = &(**p)->next;
555 }
556
557 void get_responserecords(zass_record ***p, Z_NamePlusRecordList *recs)
558 {
559     int i;
560
561     for (i = 0; i < recs->num_records; i++)
562     {
563         Z_NamePlusRecord *record;
564
565         record = recs->records[i];
566         if (record->which == Z_NamePlusRecord_surrogateDiagnostic)
567             get_diagrec(p, record->u.surrogateDiagnostic);
568         else
569         {
570             Z_DatabaseRecord *r = record->u.databaseRecord;
571             oident *recform;
572
573             **p = malloc(sizeof(***p));
574             (**p)->next = 0;
575
576             if (!(recform = oid_getentbyoid(r->direct_reference)) ||
577                  recform->class != CLASS_RECSYN)
578             {
579                 gw_log(GW_LOG_WARN, ZASS_TYPE, "Unknown or bad record syntax");
580                 (**p)->which = ZASS_REC_UNKNOWN;
581             }
582             else
583                 switch (recform->value)
584                 {
585                     case VAL_USMARC: (**p)->which = ZASS_REC_USMARC; break;
586                     default:
587                         gw_log(GW_LOG_WARN, ZASS_TYPE, "Unknown recsyn");
588                         (**p)->which = ZASS_REC_UNKNOWN;
589                 }
590             if (r->which != ODR_EXTERNAL_octet)
591             {
592                 gw_log(GW_LOG_WARN, ZASS_TYPE, "Record wasn't octet-aligned");
593                 (**p)->record = 0;
594             }
595             else
596             {
597                 if (!((**p)->record = malloc(r->u.octet_aligned->len + 1)))
598                 {
599                     gw_log(GW_LOG_FATAL, ZASS_TYPE, "malloc");
600                     return;
601                 }
602                 memcpy((**p)->record, r->u.octet_aligned->buf,
603                     r->u.octet_aligned->len);
604                 (**p)->record[r->u.octet_aligned->len] = '\0';
605                 gw_log(ZASS_DEBUG, ZASS_TYPE, "Got a record of %d bytes",
606                     r->u.octet_aligned->len);
607             }
608             (*p) = &(**p)->next;
609         }
610     }
611 }
612
613 static void zass_records_free(zass_record *p)
614 {
615 }
616
617 static int send_present(ZASS a, char *name, int start, int num,
618     enum oid_value form)
619 {
620     Z_APDU *apdu;
621     Z_PresentRequest *req;
622 #if 0
623     oident recsyn;
624 #endif
625
626     apdu = zget_APDU (a->encode, Z_APDU_presentRequest);
627     req = apdu->u.presentRequest;
628
629     req->resultSetId = name;
630     req->resultSetStartPoint = &start;
631     req->numberOfRecordsRequested = &num;
632 #if 0
633     recsyn.proto = PROTO_Z3950;
634     recsyn.class = CLASS_RECSYN;
635     recsyn.value = form;
636     req->preferredRecordSyntax = oid_getoidbyent(&recsyn);
637 #else
638     req->preferredRecordSyntax = 0;
639 #endif
640     return send_apdu(a, apdu);
641 }
642
643 /*
644  * Note that 1== first record.
645  * TODO: make this baby operate in nonblocking mode, too.
646  */
647 const struct zass_presentent *zass_present(ZASS a, char *resname, int start,
648     int num, int *complete)
649 {
650     static struct zass_presentent r;
651     zass_record **rec = &r.records;
652
653     if (complete)
654         *complete = 1;
655     r.num = 0;
656     if (r.records)
657     {
658         zass_records_free(r.records);
659         r.records = 0;
660     }
661     do
662     {
663         Z_APDU *apdu;
664         Z_PresentResponse *res;
665
666         gw_log(ZASS_DEBUG, ZASS_TYPE,
667             "Fetching %d records from # %d", num - r.num, start);
668         if (send_present(a, resname, start, num - r.num, VAL_USMARC) < 0)
669             return 0;
670         if (!(apdu = get_apdu(a, complete)))
671         {
672             if (complete)
673                 *complete = 1;
674             return 0;
675         }
676         if (apdu->which != Z_APDU_presentResponse)
677         {
678             gw_log(GW_LOG_FATAL, ZASS_TYPE, "Expected presentresponse, got #%d",
679                 apdu->which);
680             return 0;
681         }
682         res = apdu->u.presentResponse;
683         r.presentstatus = *res->presentStatus;
684         r.num += *res->numberOfRecordsReturned;
685         if (*res->numberOfRecordsReturned == 0)
686         {
687             gw_log(GW_LOG_WARN, ZASS_TYPE, "Got 0 records from target");
688             return 0;
689         }
690         r.nextpos = *res->nextResultSetPosition;
691         start = r.nextpos;
692         switch (res->records->which)
693         {
694             case Z_Records_DBOSD:
695                 get_responserecords(&rec,
696                     res->records->u.databaseOrSurDiagnostics);
697                 break;
698             case Z_Records_NSD:
699                 get_diagrec(&rec, res->records->u.nonSurrogateDiagnostic);
700                 break;
701             default:
702                 gw_log(GW_LOG_WARN, ZASS_TYPE, "Bad tag in response rec.");
703                 return 0;
704         }
705     }
706     while (num - r.num && start);
707     return &r;
708 }
709
710 const struct zass_presentent *zass_presentresult(ZASS a, int *complete)
711 {
712     *complete = 1;
713     return 0;
714 }