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