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