2 * Copyright (C) 1994, Index Data I/S
4 * Sebastian Hammer, Adam Dickmeiss
7 * Revision 1.18 1995-04-17 11:28:25 quinn
10 * Revision 1.17 1995/04/10 10:23:36 quinn
11 * Some work to add scan and other things.
13 * Revision 1.16 1995/03/31 09:18:55 quinn
16 * Revision 1.15 1995/03/30 14:03:23 quinn
17 * Added RFC1006 as separate library
19 * Revision 1.14 1995/03/30 12:18:17 quinn
22 * Revision 1.13 1995/03/30 09:09:24 quinn
23 * Added state-handle and some support for asynchronous activities.
25 * Revision 1.12 1995/03/29 15:40:16 quinn
26 * Ongoing work. Statserv is now dynamic by default
28 * Revision 1.11 1995/03/28 09:16:21 quinn
29 * Added record packing to the search request
31 * Revision 1.10 1995/03/27 08:34:24 quinn
32 * Added dynamic server functionality.
33 * Released bindings to session.c (is now redundant)
35 * Revision 1.9 1995/03/22 15:01:26 quinn
36 * Adjusting record packing.
38 * Revision 1.8 1995/03/22 10:13:21 quinn
39 * Working on record packer
41 * Revision 1.7 1995/03/21 15:53:31 quinn
44 * Revision 1.6 1995/03/21 12:30:09 quinn
45 * Beginning to add support for record packing.
47 * Revision 1.5 1995/03/17 10:44:13 quinn
48 * Added catch of null-string in makediagrec
50 * Revision 1.4 1995/03/17 10:18:08 quinn
51 * Added memory management.
53 * Revision 1.3 1995/03/16 17:42:39 quinn
56 * Revision 1.2 1995/03/16 13:29:01 quinn
59 * Revision 1.1 1995/03/15 16:02:10 quinn
60 * Modded session.c to seshigh.c
78 #define ENCODE_BUFFER_SIZE 10000
80 static int process_apdu(IOCHAN chan);
81 static int process_initRequest(IOCHAN client, Z_InitRequest *req);
82 static int process_searchRequest(IOCHAN client, Z_SearchRequest *req);
83 static int process_presentRequest(IOCHAN client, Z_PresentRequest *req);
84 static int process_scanRequest(IOCHAN client, Z_ScanRequest *req);
87 extern char *apdufile;
89 static FILE *apduf = 0; /* for use in static mode */
91 association *create_association(IOCHAN channel, COMSTACK link)
95 if (!(new = malloc(sizeof(*new))))
97 new->client_chan = channel;
98 new->client_link = link;
99 if (!(new->decode = odr_createmem(ODR_DECODE)) ||
100 !(new->encode = odr_createmem(ODR_ENCODE)))
107 if (!(new->print = odr_createmem(ODR_PRINT)))
111 strcpy(filename, apdufile);
116 if (!(apduf = fopen(filename, "w")))
118 logf(LOG_WARN|LOG_ERRNO, "%s", filename);
121 setvbuf(apduf, 0, _IONBF, 0);
127 sprintf(filename + strlen(filename), ".%d", getpid());
128 if (!(f = fopen(filename, "w")))
130 logf(LOG_WARN|LOG_ERRNO, "%s", filename);
133 setvbuf(f, 0, _IONBF, 0);
135 odr_setprint(new->print, f);
140 if (!(new->encode_buffer = malloc(ENCODE_BUFFER_SIZE)))
142 odr_setbuf(new->encode, new->encode_buffer, ENCODE_BUFFER_SIZE);
143 new->state = ASSOC_UNINIT;
144 new->input_buffer = 0;
145 new->input_buffer_len = 0;
147 if (cs_getproto(link) == CS_Z3950)
148 new->proto = PROTO_Z3950;
150 new->proto = PROTO_SR;
154 void destroy_association(association *h)
156 odr_destroy(h->decode);
157 odr_destroy(h->encode);
159 odr_destroy(h->print);
160 free(h->encode_buffer);
162 free(h->input_buffer);
164 bend_close(h->backend);
169 * process events on the association
171 void ir_session(IOCHAN h, int event)
174 association *assoc = iochan_getdata(h);
175 COMSTACK conn = assoc->client_link;
177 if (event == EVENT_INPUT)
179 assert(assoc && conn);
180 res = cs_get(conn, &assoc->input_buffer, &assoc->input_buffer_len);
183 case 0: case -1: /* connection closed by peer */
184 logf(LOG_LOG, "Connection closed by client");
186 destroy_association(assoc);
189 case 1: /* incomplete read */
192 assoc->input_apdu_len = res;
193 if (process_apdu(h) < 0)
196 destroy_association(assoc);
199 else if (cs_more(conn)) /* arrange to be called again */
200 iochan_setevent(h, EVENT_INPUT);
203 else if (event == EVENT_OUTPUT)
205 switch (res = cs_put(conn, assoc->encode_buffer, assoc->encoded_len))
208 logf(LOG_LOG, "Connection closed by client");
210 destroy_association(assoc);
212 case 0: /* all sent */
213 iochan_setflags(h, EVENT_INPUT | EVENT_EXCEPT); /* reset */
215 case 1: /* partial send */
216 break; /* we'll get called again */
219 else if (event == EVENT_EXCEPT)
221 logf(LOG_LOG, "Exception on line");
223 destroy_association(assoc);
228 static int process_apdu(IOCHAN chan)
232 association *assoc = iochan_getdata(chan);
234 odr_setbuf(assoc->decode, assoc->input_buffer, assoc->input_apdu_len);
235 if (!z_APDU(assoc->decode, &apdu, 0))
237 logf(LOG_WARN, "ODR error: %s",
238 odr_errlist[odr_geterror(assoc->decode)]);
241 if (assoc->print && !z_APDU(assoc->print, &apdu, 0))
243 logf(LOG_WARN, "ODR print error: %s",
244 odr_errlist[odr_geterror(assoc->print)]);
249 case Z_APDU_initRequest:
250 res = process_initRequest(chan, apdu->u.initRequest); break;
251 case Z_APDU_searchRequest:
252 res = process_searchRequest(chan, apdu->u.searchRequest); break;
253 case Z_APDU_presentRequest:
254 res = process_presentRequest(chan, apdu->u.presentRequest); break;
255 case Z_APDU_scanRequest:
256 res = process_scanRequest(chan, apdu->u.scanRequest); break;
258 logf(LOG_WARN, "Bad APDU");
261 odr_reset(assoc->decode); /* release incoming APDU */
262 odr_reset(assoc->encode); /* release stuff alloced before encoding */
266 static int process_initRequest(IOCHAN client, Z_InitRequest *req)
271 association *assoc = iochan_getdata(client);
272 bend_initrequest binitreq;
273 bend_initresult *binitres;
274 Odr_bitmask options, protocolVersion;
276 logf(LOG_LOG, "Got initRequest");
277 if (req->implementationId)
278 logf(LOG_LOG, "Id: %s", req->implementationId);
279 if (req->implementationName)
280 logf(LOG_LOG, "Name: %s", req->implementationName);
281 if (req->implementationVersion)
282 logf(LOG_LOG, "Version: %s", req->implementationVersion);
284 binitreq.configname = "default-config";
285 if (!(binitres = bend_init(&binitreq)) || binitres->errcode)
287 logf(LOG_WARN, "Negative response from backend");
291 assoc->backend = binitres->handle;
293 apdu.which = Z_APDU_initResponse;
294 apdu.u.initResponse = &resp;
295 resp.referenceId = req->referenceId;
296 ODR_MASK_ZERO(&options);
297 if (ODR_MASK_GET(req->options, Z_Options_search))
298 ODR_MASK_SET(&options, Z_Options_search);
299 if (ODR_MASK_GET(req->options, Z_Options_present))
300 ODR_MASK_SET(&options, Z_Options_present);
302 if (ODR_MASK_GET(req->options, Z_Options_delSet))
303 ODR_MASK_SET(&options, Z_Options_delSet);
305 if (ODR_MASK_GET(req->options, Z_Options_namedResultSets))
306 ODR_MASK_SET(&options, Z_Options_namedResultSets);
307 if (ODR_MASK_GET(req->options, Z_Options_scan))
308 ODR_MASK_SET(&options, Z_Options_scan);
309 resp.options = &options;
310 ODR_MASK_ZERO(&protocolVersion);
311 if (ODR_MASK_GET(req->protocolVersion, Z_ProtocolVersion_1))
312 ODR_MASK_SET(&protocolVersion, Z_ProtocolVersion_1);
313 if (ODR_MASK_GET(req->protocolVersion, Z_ProtocolVersion_2))
314 ODR_MASK_SET(&protocolVersion, Z_ProtocolVersion_2);
315 resp.protocolVersion = &protocolVersion;
316 assoc->maximumRecordSize = *req->maximumRecordSize;
318 * This is not so hot. The big todo for ODR is dynamic memory allocation
321 if (assoc->maximumRecordSize > ENCODE_BUFFER_SIZE - 1000)
322 assoc->maximumRecordSize = ENCODE_BUFFER_SIZE - 1000;
323 assoc->preferredMessageSize = *req->preferredMessageSize;
324 if (assoc->preferredMessageSize > assoc->maximumRecordSize)
325 assoc->preferredMessageSize = assoc->maximumRecordSize;
326 resp.preferredMessageSize = &assoc->preferredMessageSize;
327 resp.maximumRecordSize = &assoc->maximumRecordSize;
328 resp.result = &result;
329 resp.implementationId = "YAZ";
330 resp.implementationName = "Index Data/YAZ Generic Frontend Server";
331 resp.implementationVersion = "$Revision: 1.18 $";
332 resp.userInformationField = 0;
333 if (!z_APDU(assoc->encode, &apdup, 0))
335 logf(LOG_FATAL, "ODR error encoding initres: %s",
336 odr_errlist[odr_geterror(assoc->encode)]);
339 odr_getbuf(assoc->encode, &assoc->encoded_len);
340 odr_reset(assoc->encode);
341 iochan_setflags(client, EVENT_OUTPUT | EVENT_EXCEPT);
345 static Z_Records *diagrec(oid_proto proto, int error, char *addinfo)
347 static Z_Records rec;
353 bib1.class = CLASS_DIAGSET;
354 bib1.value = VAL_BIB1;
356 logf(LOG_DEBUG, "Diagnostic: %d -- %s", error, addinfo ? addinfo :
359 rec.which = Z_Records_NSD;
360 rec.u.nonSurrogateDiagnostic = &dr;
361 dr.diagnosticSetId = oid_getoidbyent(&bib1);
363 dr.addinfo = addinfo ? addinfo : "";
367 static Z_NamePlusRecord *surrogatediagrec(oid_proto proto, char *dbname,
368 int error, char *addinfo)
370 static Z_NamePlusRecord rec;
376 bib1.class = CLASS_DIAGSET;
377 bib1.value = VAL_BIB1;
379 logf(LOG_DEBUG, "SurrogateDiagnotic: %d -- %s", error, addinfo);
381 rec.databaseName = dbname;
382 rec.which = Z_NamePlusRecord_surrogateDiagnostic;
383 rec.u.surrogateDiagnostic = &dr;
384 dr.diagnosticSetId = oid_getoidbyent(&bib1);
386 dr.addinfo = addinfo ? addinfo : "";
390 static Z_DiagRecs *diagrecs(oid_proto proto, int error, char *addinfo)
392 static Z_DiagRecs recs;
393 static Z_DiagRec *recp[1], rec;
397 logf(LOG_DEBUG, "DiagRecs: %d -- %s", error, addinfo);
399 bib1.class = CLASS_DIAGSET;
400 bib1.value = VAL_BIB1;
403 recs.num_diagRecs = 1;
404 recs.diagRecs = recp;
406 rec.diagnosticSetId = oid_getoidbyent(&bib1);
407 rec.condition = &err;
408 rec.addinfo = addinfo ? addinfo : "";
412 #define MAX_RECORDS 256
414 static Z_Records *pack_records(association *a, char *setname, int start,
415 int *num, Z_ElementSetNames *esn,
416 int *next, int *pres)
418 int recno, total_length = 0, toget = *num;
419 static Z_Records records;
420 static Z_NamePlusRecordList reclist;
421 static Z_NamePlusRecord *list[MAX_RECORDS];
425 records.which = Z_Records_DBOSD;
426 records.u.databaseOrSurDiagnostics = &reclist;
427 reclist.num_records = 0;
428 reclist.records = list;
429 *pres = Z_PRES_SUCCESS;
433 recform.proto = a->proto;
434 recform.class = CLASS_RECSYN;
435 recform.value = VAL_USMARC;
436 if (!(oid = odr_oiddup(a->encode, oid_getoidbyent(&recform))))
439 logf(LOG_DEBUG, "Request to pack %d+%d", start, toget);
440 logf(LOG_DEBUG, "pms=%d, mrs=%d", a->preferredMessageSize,
441 a->maximumRecordSize);
442 for (recno = start; reclist.num_records < toget; recno++)
444 bend_fetchrequest freq;
445 bend_fetchresult *fres;
446 Z_NamePlusRecord *thisrec;
447 Z_DatabaseRecord *thisext;
449 if (reclist.num_records == MAX_RECORDS - 1)
451 *pres = Z_PRES_PARTIAL_2;
454 freq.setname = setname;
456 if (!(fres = bend_fetch(a->backend, &freq, 0)))
458 *pres = Z_PRES_FAILURE;
459 return diagrec(a->proto, 2, "Backend interface problem");
461 /* backend should be able to signal whether error is system-wide
462 or only pertaining to current record */
465 *pres = Z_PRES_FAILURE;
466 return diagrec(a->proto, fres->errcode, fres->errstring);
468 logf(LOG_DEBUG, " fetched record, len=%d, total=%d",
469 fres->len, total_length);
470 if (fres->len + total_length > a->preferredMessageSize)
472 /* record is small enough, really */
473 if (fres->len <= a->preferredMessageSize)
475 logf(LOG_DEBUG, " Dropped last normal-sized record");
476 *pres = Z_PRES_PARTIAL_2;
479 /* record can only be fetched by itself */
480 if (fres->len < a->maximumRecordSize)
482 logf(LOG_DEBUG, " Record > prefmsgsz");
485 logf(LOG_DEBUG, " Dropped it");
486 reclist.records[reclist.num_records] =
487 surrogatediagrec(a->proto, fres->basename, 16, 0);
488 reclist.num_records++;
489 *pres = Z_PRES_PARTIAL_2;
493 else /* too big entirely */
495 logf(LOG_DEBUG, "Record > maxrcdsz");
496 reclist.records[reclist.num_records] =
497 surrogatediagrec(a->proto, fres->basename, 17, 0);
498 reclist.num_records++;
499 *pres = Z_PRES_PARTIAL_2;
503 if (!(thisrec = odr_malloc(a->encode, sizeof(*thisrec))))
505 if (!(thisrec->databaseName = odr_malloc(a->encode,
506 strlen(fres->basename) + 1)))
508 strcpy(thisrec->databaseName, fres->basename);
509 thisrec->which = Z_NamePlusRecord_databaseRecord;
510 if (!(thisrec->u.databaseRecord = thisext = odr_malloc(a->encode,
511 sizeof(Z_DatabaseRecord))))
513 thisext->direct_reference = oid; /* should be OID for current MARC */
514 thisext->indirect_reference = 0;
515 thisext->descriptor = 0;
516 thisext->which = ODR_EXTERNAL_octet;
517 if (!(thisext->u.octet_aligned = odr_malloc(a->encode,
520 if (!(thisext->u.octet_aligned->buf = odr_malloc(a->encode, fres->len)))
522 memcpy(thisext->u.octet_aligned->buf, fres->record, fres->len);
523 thisext->u.octet_aligned->len = thisext->u.octet_aligned->size =
525 reclist.records[reclist.num_records] = thisrec;
526 reclist.num_records++;
527 total_length += fres->len;
529 *next = fres->last_in_set ? 0 : recno + 1;
534 static int process_searchRequest(IOCHAN client, Z_SearchRequest *req)
537 Z_SearchResponse resp;
538 association *assoc = iochan_getdata(client);
541 bend_searchrequest bsrq;
542 bend_searchresult *bsrt;
545 static int none = Z_RES_NONE;
547 logf(LOG_LOG, "Got SearchRequest.");
549 apdu.which = Z_APDU_searchResponse;
550 apdu.u.searchResponse = &resp;
551 resp.referenceId = req->referenceId;
554 if (req->query->which == Z_Query_type_1)
556 Z_RPNQuery *q = req->query->u.type_1;
558 if (!(oent = oid_getentbyoid(q->attributeSetId)) ||
559 oent->class != CLASS_ATTSET ||
560 oent->value != VAL_BIB1)
562 resp.records = diagrec(assoc->proto, 121, 0);
563 resp.resultCount = &nulint;
564 resp.numberOfRecordsReturned = &nulint;
565 resp.nextResultSetPosition = &nulint;
566 resp.searchStatus = &none;
567 resp.resultSetStatus = 0;
568 resp.presentStatus = 0;
574 Z_ElementSetNames *setnames;
577 bsrq.setname = req->resultSetName;
578 bsrq.replace_set = *req->replaceIndicator;
579 bsrq.num_bases = req->num_databaseNames;
580 bsrq.basenames = req->databaseNames;
581 bsrq.query = req->query;
583 if (!(bsrt = bend_search(assoc->backend, &bsrq, 0)))
585 else if (bsrt->errcode)
588 resp.records = diagrec(assoc->proto, bsrt->errcode,
590 resp.resultCount = &nulint;
591 resp.numberOfRecordsReturned = &nulint;
592 resp.nextResultSetPosition = &nulint;
593 resp.searchStatus = &nulint;
594 resp.resultSetStatus = &none;
595 resp.presentStatus = 0;
601 resp.resultCount = &bsrt->hits;
603 /* how many records does the user agent want, then? */
604 if (bsrt->hits <= *req->smallSetUpperBound)
607 setnames = req->smallSetElementSetNames;
609 else if (bsrt->hits < *req->largeSetLowerBound)
611 toget = *req->mediumSetPresentNumber;
612 if (toget > bsrt->hits)
614 setnames = req->mediumSetElementSetNames;
619 if (toget && !resp.records)
621 resp.records = pack_records(assoc, req->resultSetName, 1,
622 &toget, setnames, &next, &presst);
625 resp.numberOfRecordsReturned = &toget;
626 resp.nextResultSetPosition = &next;
627 resp.searchStatus = &sr;
628 resp.resultSetStatus = 0;
629 resp.presentStatus = &presst;
633 resp.numberOfRecordsReturned = &nulint;
634 resp.nextResultSetPosition = &next;
635 resp.searchStatus = &sr;
636 resp.resultSetStatus = 0;
637 resp.presentStatus = 0;
642 if (!z_APDU(assoc->encode, &apdup, 0))
644 logf(LOG_FATAL, "ODR error encoding searchres: %s",
645 odr_errlist[odr_geterror(assoc->encode)]);
648 odr_getbuf(assoc->encode, &assoc->encoded_len);
649 odr_reset(assoc->encode);
650 iochan_setflags(client, EVENT_OUTPUT | EVENT_EXCEPT);
654 static int process_presentRequest(IOCHAN client, Z_PresentRequest *req)
657 Z_PresentResponse resp;
658 association *assoc = iochan_getdata(client);
659 int presst, next, num;
661 logf(LOG_LOG, "Got PresentRequest.");
663 apdu.which = Z_APDU_presentResponse;
664 apdu.u.presentResponse = &resp;
665 resp.referenceId = req->referenceId;
667 num = *req->numberOfRecordsRequested;
668 resp.records = pack_records(assoc, req->resultSetId,
669 *req->resultSetStartPoint, &num, req->elementSetNames, &next, &presst);
672 resp.numberOfRecordsReturned = #
673 resp.presentStatus = &presst;
674 resp.nextResultSetPosition = &next;
676 if (!z_APDU(assoc->encode, &apdup, 0))
678 logf(LOG_FATAL, "ODR error encoding initres: %s",
679 odr_errlist[odr_geterror(assoc->encode)]);
682 odr_getbuf(assoc->encode, &assoc->encoded_len);
683 odr_reset(assoc->encode);
684 iochan_setflags(client, EVENT_OUTPUT | EVENT_EXCEPT);
688 static int process_scanRequest(IOCHAN client, Z_ScanRequest *req)
690 association *assoc = iochan_getdata(client);
693 bend_scanrequest srq;
694 bend_scanresult *srs;
695 int scanStatus = Z_Scan_failure;
696 int numberOfEntriesReturned = 0;
699 #define SCAN_MAX_ENTRIES 200
700 Z_Entry *tab[SCAN_MAX_ENTRIES];
703 apdu.which = Z_APDU_scanResponse;
704 apdu.u.scanResponse = &res;
705 res.referenceId = req->referenceId;
707 res.scanStatus = &scanStatus;
708 res.numberOfEntriesReturned = &numberOfEntriesReturned;
709 res.positionOfTerm = 0;
711 ents.which = Z_ListEntries_nonSurrogateDiagnostics;
712 res.attributeSet = 0;
714 if (req->attributeSet && (!(attent = oid_getentbyoid(req->attributeSet)) ||
715 attent->class != CLASS_ATTSET || attent->value != VAL_BIB1))
716 ents.u.nonSurrogateDiagnostics = diagrecs(assoc->proto, 121, 0);
717 else if (req->stepSize && *req->stepSize > 0)
718 ents.u.nonSurrogateDiagnostics = diagrecs(assoc->proto, 205, 0);
721 srq.num_bases = req->num_databaseNames;
722 srq.basenames = req->databaseNames;
723 srq.num_entries = *req->numberOfTermsRequested;
724 srq.term = req->termListAndStartPoint;
725 srq.term_position = req->preferredPositionInResponse ?
726 *req->preferredPositionInResponse : 1;
727 if (!(srs = bend_scan(assoc->backend, &srq, 0)))
728 ents.u.nonSurrogateDiagnostics = diagrecs(assoc->proto, 2, 0);
729 else if (srs->errcode)
730 ents.u.nonSurrogateDiagnostics = diagrecs(assoc->proto,
731 srs->errcode, srs->errstring);
735 static Z_Entries list;
737 if (srs->status == BEND_SCAN_PARTIAL)
738 scanStatus = Z_Scan_partial_5;
740 scanStatus = Z_Scan_success;
741 ents.which = Z_ListEntries_entries;
742 ents.u.entries = &list;
744 for (i = 0; i < srs->num_entries; i++)
750 if (i >= SCAN_MAX_ENTRIES)
752 scanStatus = Z_Scan_partial_4;
755 list.entries[i] = e = odr_malloc(assoc->encode, sizeof(*e));
756 e->which = Z_Entry_termInfo;
757 e->u.termInfo = t = odr_malloc(assoc->encode, sizeof(*t));
758 t->suggestedAttributes = 0;
759 t->alternativeTerm = 0;
761 t->globalOccurrences = &srs->entries[i].occurrences;
762 t->term = odr_malloc(assoc->encode, sizeof(*t->term));
763 t->term->which = Z_Term_general;
764 t->term->u.general = o = odr_malloc(assoc->encode,
766 o->buf = odr_malloc(assoc->encode, o->len = o->size =
767 strlen(srs->entries[i].term));
768 memcpy(o->buf, srs->entries[i].term, o->len);
770 list.num_entries = i;
771 res.numberOfEntriesReturned = &list.num_entries;
772 res.positionOfTerm = &srs->term_position;
775 if (!z_APDU(assoc->encode, &apdup, 0))
777 logf(LOG_FATAL, "ODR error encoding initres: %s",
778 odr_errlist[odr_geterror(assoc->encode)]);
781 odr_getbuf(assoc->encode, &assoc->encoded_len);
782 odr_reset(assoc->encode);
783 iochan_setflags(client, EVENT_OUTPUT | EVENT_EXCEPT);