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