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