2 * Copyright (C) 1994, Index Data I/S
4 * Sebastian Hammer, Adam Dickmeiss
7 * Revision 1.11 1995-03-28 09:16:21 quinn
8 * Added record packing to the search request
10 * Revision 1.10 1995/03/27 08:34:24 quinn
11 * Added dynamic server functionality.
12 * Released bindings to session.c (is now redundant)
14 * Revision 1.9 1995/03/22 15:01:26 quinn
15 * Adjusting record packing.
17 * Revision 1.8 1995/03/22 10:13:21 quinn
18 * Working on record packer
20 * Revision 1.7 1995/03/21 15:53:31 quinn
23 * Revision 1.6 1995/03/21 12:30:09 quinn
24 * Beginning to add support for record packing.
26 * Revision 1.5 1995/03/17 10:44:13 quinn
27 * Added catch of null-string in makediagrec
29 * Revision 1.4 1995/03/17 10:18:08 quinn
30 * Added memory management.
32 * Revision 1.3 1995/03/16 17:42:39 quinn
35 * Revision 1.2 1995/03/16 13:29:01 quinn
38 * Revision 1.1 1995/03/15 16:02:10 quinn
39 * Modded session.c to seshigh.c
56 #define ENCODE_BUFFER_SIZE 10000
58 static int process_apdu(IOCHAN chan);
59 static int process_initRequest(IOCHAN client, Z_InitRequest *req);
60 static int process_searchRequest(IOCHAN client, Z_SearchRequest *req);
61 static int process_presentRequest(IOCHAN client, Z_PresentRequest *req);
63 association *create_association(IOCHAN channel, COMSTACK link)
67 if (!(new = malloc(sizeof(*new))))
69 new->client_chan = channel;
70 new->client_link = link;
71 if (!(new->decode = odr_createmem(ODR_DECODE)) ||
72 !(new->encode = odr_createmem(ODR_ENCODE)))
74 if (!(new->encode_buffer = malloc(ENCODE_BUFFER_SIZE)))
76 odr_setbuf(new->encode, new->encode_buffer, ENCODE_BUFFER_SIZE);
77 new->state = ASSOC_UNINIT;
78 new->input_buffer = 0;
79 new->input_buffer_len = 0;
80 if (cs_getproto(link) == CS_Z3950)
81 new->proto = PROTO_Z3950;
83 new->proto = PROTO_SR;
87 void destroy_association(association *h)
89 odr_destroy(h->decode);
90 odr_destroy(h->encode);
91 free(h->encode_buffer);
93 free(h->input_buffer);
97 void ir_session(IOCHAN h, int event)
100 association *assoc = iochan_getdata(h);
101 COMSTACK conn = assoc->client_link;
103 if (event == EVENT_INPUT)
105 assert(assoc && conn);
106 res = cs_get(conn, &assoc->input_buffer, &assoc->input_buffer_len);
109 case 0: case -1: /* connection closed by peer */
110 fprintf(stderr, "Closed connection\n");
112 destroy_association(assoc);
115 case 1: /* incomplete read */
118 assoc->input_apdu_len = res;
119 if (process_apdu(h) < 0)
121 fprintf(stderr, "Operation failed\n");
123 destroy_association(assoc);
126 else if (cs_more(conn)) /* arrange to be called again */
127 iochan_setevent(h, EVENT_INPUT);
130 else if (event == EVENT_OUTPUT)
132 switch (res = cs_put(conn, assoc->encode_buffer, assoc->encoded_len))
135 fprintf(stderr, "Closed connection\n");
137 destroy_association(assoc);
139 case 0: /* all sent */
140 iochan_setflags(h, EVENT_INPUT | EVENT_EXCEPT); /* reset */
142 case 1: /* partial send */
143 break; /* we'll get called again */
146 else if (event == EVENT_EXCEPT)
148 fprintf(stderr, "Exception on line\n");
150 destroy_association(assoc);
155 static int process_apdu(IOCHAN chan)
159 association *assoc = iochan_getdata(chan);
161 odr_setbuf(assoc->decode, assoc->input_buffer, assoc->input_apdu_len);
162 if (!z_APDU(assoc->decode, &apdu, 0))
164 odr_perror(assoc->decode, "Incoming APDU");
169 case Z_APDU_initRequest:
170 res = process_initRequest(chan, apdu->u.initRequest); break;
171 case Z_APDU_searchRequest:
172 res = process_searchRequest(chan, apdu->u.searchRequest); break;
173 case Z_APDU_presentRequest:
174 res = process_presentRequest(chan, apdu->u.presentRequest); break;
176 fprintf(stderr, "Bad APDU\n");
179 odr_reset(assoc->decode); /* release incopming APDU */
180 odr_reset(assoc->encode); /* release stuff alloced before encoding */
184 static int process_initRequest(IOCHAN client, Z_InitRequest *req)
189 association *assoc = iochan_getdata(client);
190 bend_initrequest binitreq;
191 bend_initresult *binitres;
193 fprintf(stderr, "Got initRequest.\n");
194 if (req->implementationId)
195 fprintf(stderr, "Id: %s\n", req->implementationId);
196 if (req->implementationName)
197 fprintf(stderr, "Name: %s\n", req->implementationName);
198 if (req->implementationVersion)
199 fprintf(stderr, "Version: %s\n", req->implementationVersion);
201 binitreq.configname = "default-config";
202 if (!(binitres = bend_init(&binitreq)) || binitres->errcode)
204 fprintf(stderr, "Bad response from backend\n");
209 apdu.which = Z_APDU_initResponse;
210 apdu.u.initResponse = &resp;
211 resp.referenceId = req->referenceId;
212 resp.options = req->options; /* should check these */
213 resp.protocolVersion = req->protocolVersion;
214 assoc->maximumRecordSize = *req->maximumRecordSize;
216 * This is not so hot. The big todo for ODR is dynamic memory allocation
219 if (assoc->maximumRecordSize > ENCODE_BUFFER_SIZE - 1000)
220 assoc->maximumRecordSize = ENCODE_BUFFER_SIZE - 1000;
221 assoc->preferredMessageSize = *req->preferredMessageSize;
222 if (assoc->preferredMessageSize > assoc->maximumRecordSize)
223 assoc->preferredMessageSize = assoc->maximumRecordSize;
224 resp.preferredMessageSize = &assoc->preferredMessageSize;
225 resp.maximumRecordSize = &assoc->maximumRecordSize;
226 resp.result = &result;
227 resp.implementationId = "YAZ";
228 resp.implementationName = "Index Data/YAZ Generic Frontend Server";
229 resp.implementationVersion = "$Revision: 1.11 $";
230 resp.userInformationField = 0;
231 if (!z_APDU(assoc->encode, &apdup, 0))
233 odr_perror(assoc->encode, "Encode initres");
236 odr_getbuf(assoc->encode, &assoc->encoded_len);
237 odr_reset(assoc->encode);
238 iochan_setflags(client, EVENT_OUTPUT | EVENT_EXCEPT);
242 static Z_Records *diagrec(oid_proto proto, int error, char *addinfo)
244 static Z_Records rec;
250 bib1.class = CLASS_DIAGSET;
251 bib1.value = VAL_BIB1;
253 fprintf(stderr, "Diagnostic: %d -- %s\n", error, addinfo ? addinfo :
256 rec.which = Z_Records_NSD;
257 rec.u.nonSurrogateDiagnostic = &dr;
258 dr.diagnosticSetId = oid_getoidbyent(&bib1);
260 dr.addinfo = addinfo ? addinfo : "";
264 static Z_NamePlusRecord *surrogatediagrec(oid_proto proto, char *dbname,
265 int error, char *addinfo)
267 static Z_NamePlusRecord rec;
273 bib1.class = CLASS_DIAGSET;
274 bib1.value = VAL_BIB1;
276 fprintf(stderr, "SurrogateDiagnotic: %d -- %s\n", error, addinfo);
278 rec.databaseName = dbname;
279 rec.which = Z_NamePlusRecord_surrogateDiagnostic;
280 rec.u.surrogateDiagnostic = &dr;
281 dr.diagnosticSetId = oid_getoidbyent(&bib1);
283 dr.addinfo = addinfo ? addinfo : "";
287 #define MAX_RECORDS 256
289 static Z_Records *pack_records(association *a, char *setname, int start,
290 int *num, Z_ElementSetNames *esn,
291 int *next, int *pres)
293 int recno, total_length = 0, toget = *num;
294 static Z_Records records;
295 static Z_NamePlusRecordList reclist;
296 static Z_NamePlusRecord *list[MAX_RECORDS];
300 records.which = Z_Records_DBOSD;
301 records.u.databaseOrSurDiagnostics = &reclist;
302 reclist.num_records = 0;
303 reclist.records = list;
304 *pres = Z_PRES_SUCCESS;
308 recform.proto = a->proto;
309 recform.class = CLASS_RECSYN;
310 recform.value = VAL_USMARC;
311 if (!(oid = odr_oiddup(a->encode, oid_getoidbyent(&recform))))
314 fprintf(stderr, "Request to pack %d+%d\n", start, toget);
315 fprintf(stderr, "pms=%d, mrs=%d\n", a->preferredMessageSize,
316 a->maximumRecordSize);
317 for (recno = start; reclist.num_records < toget; recno++)
319 bend_fetchrequest freq;
320 bend_fetchresult *fres;
321 Z_NamePlusRecord *thisrec;
322 Z_DatabaseRecord *thisext;
324 if (reclist.num_records == MAX_RECORDS - 1)
326 *pres = Z_PRES_PARTIAL_2;
329 freq.setname = setname;
331 if (!(fres = bend_fetch(&freq)))
333 *pres = Z_PRES_FAILURE;
334 return diagrec(a->proto, 2, "Backend interface problem");
336 /* backend should be able to signal whether error is system-wide
337 or only pertaining to current record */
340 *pres = Z_PRES_FAILURE;
341 return diagrec(a->proto, fres->errcode, fres->errstring);
343 fprintf(stderr, " Got record, len=%d, total=%d\n",
344 fres->len, total_length);
345 if (fres->len + total_length > a->preferredMessageSize)
347 fprintf(stderr, " In drop-zone\n");
348 /* record is small enough, really */
349 if (fres->len <= a->preferredMessageSize)
351 fprintf(stderr, " Dropped last normal-sized record\n");
352 *pres = Z_PRES_PARTIAL_2;
355 /* record can only be fetched by itself */
356 if (fres->len < a->maximumRecordSize)
358 fprintf(stderr, " Record > prefmsgsz\n");
361 fprintf(stderr, " Dropped it\n");
362 reclist.records[reclist.num_records] =
363 surrogatediagrec(a->proto, fres->basename, 16, 0);
364 reclist.num_records++;
365 *pres = Z_PRES_PARTIAL_2;
369 else /* too big entirely */
371 fprintf(stderr, "Record > maxrcdsz\n");
372 reclist.records[reclist.num_records] =
373 surrogatediagrec(a->proto, fres->basename, 17, 0);
374 reclist.num_records++;
375 *pres = Z_PRES_PARTIAL_2;
379 if (!(thisrec = odr_malloc(a->encode, sizeof(*thisrec))))
381 if (!(thisrec->databaseName = odr_malloc(a->encode,
382 strlen(fres->basename) + 1)))
384 strcpy(thisrec->databaseName, fres->basename);
385 thisrec->which = Z_NamePlusRecord_databaseRecord;
386 if (!(thisrec->u.databaseRecord = thisext = odr_malloc(a->encode,
387 sizeof(Z_DatabaseRecord))))
389 thisext->direct_reference = oid; /* should be OID for current MARC */
390 thisext->indirect_reference = 0;
391 thisext->descriptor = 0;
392 thisext->which = ODR_EXTERNAL_octet;
393 if (!(thisext->u.octet_aligned = odr_malloc(a->encode,
396 if (!(thisext->u.octet_aligned->buf = odr_malloc(a->encode, fres->len)))
398 memcpy(thisext->u.octet_aligned->buf, fres->record, fres->len);
399 thisext->u.octet_aligned->len = thisext->u.octet_aligned->size =
401 reclist.records[reclist.num_records] = thisrec;
402 reclist.num_records++;
403 total_length += fres->len;
405 *next = fres->last_in_set ? 0 : recno + 1;
410 static int process_searchRequest(IOCHAN client, Z_SearchRequest *req)
413 Z_SearchResponse resp;
414 association *assoc = iochan_getdata(client);
417 bend_searchrequest bsrq;
418 bend_searchresult *bsrt;
422 fprintf(stderr, "Got SearchRequest.\n");
424 apdu.which = Z_APDU_searchResponse;
425 apdu.u.searchResponse = &resp;
426 resp.referenceId = req->referenceId;
429 if (req->query->which == Z_Query_type_1)
431 Z_RPNQuery *q = req->query->u.type_1;
433 if (!(oent = oid_getentbyoid(q->attributeSetId)) ||
434 oent->class != CLASS_ATTSET ||
435 oent->value != VAL_BIB1)
436 resp.records = diagrec(assoc->proto, 121, 0);
441 Z_ElementSetNames *setnames;
444 bsrq.setname = req->resultSetName;
445 bsrq.replace_set = *req->replaceIndicator;
446 bsrq.num_bases = req->num_databaseNames;
447 bsrq.basenames = req->databaseNames;
448 bsrq.query = req->query;
450 if (!(bsrt = bend_search(&bsrq)))
452 else if (bsrt->errcode)
453 resp.records = diagrec(assoc->proto, bsrt->errcode,
458 resp.resultCount = &bsrt->hits;
460 /* how many records does the user agent want, then? */
461 if (bsrt->hits <= *req->smallSetUpperBound)
464 setnames = req->smallSetElementSetNames;
466 else if (bsrt->hits < *req->largeSetLowerBound)
468 toget = *req->mediumSetPresentNumber;
469 setnames = req->mediumSetElementSetNames;
476 resp.records = pack_records(assoc, req->resultSetName, 1, &toget,
477 setnames, &next, &presst);
480 resp.numberOfRecordsReturned = &toget;
481 resp.nextResultSetPosition = &next;
482 resp.searchStatus = &sr;
483 resp.resultSetStatus = &sr;
484 resp.presentStatus = &presst;
489 resp.numberOfRecordsReturned = &nulint;
490 resp.nextResultSetPosition = &nulint;
491 resp.searchStatus = &sr;
492 resp.resultSetStatus = &sr;
493 resp.presentStatus = 0;
498 resp.resultCount = &nulint;
499 resp.numberOfRecordsReturned = &nulint;
500 resp.nextResultSetPosition = &nulint;
501 resp.searchStatus = &nulint;
502 resp.resultSetStatus = 0;
505 if (!z_APDU(assoc->encode, &apdup, 0))
507 odr_perror(assoc->encode, "Encode searchres");
510 odr_getbuf(assoc->encode, &assoc->encoded_len);
511 odr_reset(assoc->encode);
512 iochan_setflags(client, EVENT_OUTPUT | EVENT_EXCEPT);
516 static int process_presentRequest(IOCHAN client, Z_PresentRequest *req)
519 Z_PresentResponse resp;
520 association *assoc = iochan_getdata(client);
521 int presst, next, num;
523 fprintf(stderr, "Got PresentRequest.\n");
525 apdu.which = Z_APDU_presentResponse;
526 apdu.u.presentResponse = &resp;
527 resp.referenceId = req->referenceId;
529 num = *req->numberOfRecordsRequested;
530 resp.records = pack_records(assoc, req->resultSetId,
531 *req->resultSetStartPoint, &num, req->elementSetNames, &next, &presst);
534 resp.numberOfRecordsReturned = #
535 resp.presentStatus = &presst;
536 resp.nextResultSetPosition = &next;
538 if (!z_APDU(assoc->encode, &apdup, 0))
540 odr_perror(assoc->encode, "Encode presentres");
543 odr_getbuf(assoc->encode, &assoc->encoded_len);
544 odr_reset(assoc->encode);
545 iochan_setflags(client, EVENT_OUTPUT | EVENT_EXCEPT);