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