+ break;
+ case Z_APDU_segmentRequest:
+ if (assoc->init->bend_segment)
+ {
+ res = process_segmentRequest (assoc, req);
+ }
+ else
+ {
+ *msg = "Cannot handle Segment APDU";
+ return -1;
+ }
+ break;
+ default:
+ *msg = "Bad APDU received";
+ return -1;
+ }
+ if (res)
+ {
+ yaz_log(LOG_DEBUG, " result immediately available");
+ retval = process_z_response(assoc, req, res);
+ }
+ else if (fd < 0)
+ {
+ yaz_log(LOG_DEBUG, " result unavailble");
+ retval = 0;
+ }
+ else /* no result yet - one will be provided later */
+ {
+ IOCHAN chan;
+
+ /* Set up an I/O handler for the fd supplied by the backend */
+
+ yaz_log(LOG_DEBUG, " establishing handler for result");
+ req->state = REQUEST_PENDING;
+ if (!(chan = iochan_create(fd, backend_response, EVENT_INPUT)))
+ abort();
+ iochan_setdata(chan, assoc);
+ retval = 0;
+ }
+ return retval;
+}
+
+/*
+ * Handle message from the backend.
+ */
+void backend_response(IOCHAN i, int event)
+{
+ association *assoc = (association *)iochan_getdata(i);
+ request *req = request_head(&assoc->incoming);
+ Z_APDU *res;
+ int fd;
+
+ yaz_log(LOG_DEBUG, "backend_response");
+ assert(assoc && req && req->state != REQUEST_IDLE);
+ /* determine what it is we're waiting for */
+ switch (req->apdu_request->which)
+ {
+ case Z_APDU_searchRequest:
+ res = response_searchRequest(assoc, req, 0, &fd); break;
+#if 0
+ case Z_APDU_presentRequest:
+ res = response_presentRequest(assoc, req, 0, &fd); break;
+ case Z_APDU_scanRequest:
+ res = response_scanRequest(assoc, req, 0, &fd); break;
+#endif
+ default:
+ yaz_log(LOG_WARN, "Serious programmer's lapse or bug");
+ abort();
+ }
+ if ((res && process_z_response(assoc, req, res) < 0) || fd < 0)
+ {
+ yaz_log(LOG_LOG, "Fatal error when talking to backend");
+ do_close(assoc, Z_Close_systemProblem, 0);
+ iochan_destroy(i);
+ return;
+ }
+ else if (!res) /* no result yet - try again later */
+ {
+ yaz_log(LOG_DEBUG, " no result yet");
+ iochan_setfd(i, fd); /* in case fd has changed */
+ }
+}
+
+/*
+ * Encode response, and transfer the request structure to the outgoing queue.
+ */
+static int process_gdu_response(association *assoc, request *req, Z_GDU *res)
+{
+ odr_setbuf(assoc->encode, req->response, req->size_response, 1);
+
+ if (assoc->print && !z_GDU(assoc->print, &res, 0, 0))
+ {
+ yaz_log(LOG_WARN, "ODR print error: %s",
+ odr_errmsg(odr_geterror(assoc->print)));
+ odr_reset(assoc->print);
+ }
+ if (!z_GDU(assoc->encode, &res, 0, 0))
+ {
+ yaz_log(LOG_WARN, "ODR error when decoding PDU: %s [element %s]",
+ odr_errmsg(odr_geterror(assoc->decode)),
+ odr_getelement(assoc->decode));
+ request_release(req);
+ return -1;
+ }
+ req->response = odr_getbuf(assoc->encode, &req->len_response,
+ &req->size_response);
+ odr_setbuf(assoc->encode, 0, 0, 0); /* don'txfree if we abort later */
+ odr_reset(assoc->encode);
+ req->state = REQUEST_IDLE;
+ request_enq(&assoc->outgoing, req);
+ /* turn the work over to the ir_session handler */
+ iochan_setflag(assoc->client_chan, EVENT_OUTPUT);
+ assoc->cs_put_mask = EVENT_OUTPUT;
+ /* Is there more work to be done? give that to the input handler too */
+#if 1
+ if (request_head(&assoc->incoming))
+ {
+ yaz_log (LOG_DEBUG, "more work to be done");
+ iochan_setevent(assoc->client_chan, EVENT_WORK);
+ }
+#endif
+ return 0;
+}
+
+/*
+ * Encode response, and transfer the request structure to the outgoing queue.
+ */
+static int process_z_response(association *assoc, request *req, Z_APDU *res)
+{
+ Z_GDU *gres = (Z_GDU *) odr_malloc(assoc->encode, sizeof(*res));
+ gres->which = Z_GDU_Z3950;
+ gres->u.z3950 = res;
+
+ return process_gdu_response(assoc, req, gres);
+}
+
+
+/*
+ * Handle init request.
+ * At the moment, we don't check the options
+ * anywhere else in the code - we just try not to do anything that would
+ * break a naive client. We'll toss 'em into the association block when
+ * we need them there.
+ */
+static Z_APDU *process_initRequest(association *assoc, request *reqb)
+{
+ statserv_options_block *cb = statserv_getcontrol();
+ Z_InitRequest *req = reqb->apdu_request->u.initRequest;
+ Z_APDU *apdu = zget_APDU(assoc->encode, Z_APDU_initResponse);
+ Z_InitResponse *resp = apdu->u.initResponse;
+ bend_initresult *binitres;
+
+ char options[140];
+
+ yaz_log(LOG_LOG, "Got initRequest");
+ if (req->implementationId)
+ yaz_log(LOG_LOG, "Id: %s", req->implementationId);
+ if (req->implementationName)
+ yaz_log(LOG_LOG, "Name: %s", req->implementationName);
+ if (req->implementationVersion)
+ yaz_log(LOG_LOG, "Version: %s", req->implementationVersion);
+
+ assoc_init_reset(assoc);
+
+ assoc->init->auth = req->idAuthentication;
+ assoc->init->referenceId = req->referenceId;
+
+ if (ODR_MASK_GET(req->options, Z_Options_negotiationModel))
+ {
+ Z_CharSetandLanguageNegotiation *negotiation =
+ yaz_get_charneg_record (req->otherInfo);
+ if (negotiation->which == Z_CharSetandLanguageNegotiation_proposal)
+ assoc->init->charneg_request = negotiation;
+ }
+
+ if (!(binitres = (*cb->bend_init)(assoc->init)))
+ {
+ yaz_log(LOG_WARN, "Bad response from backend.");
+ return 0;
+ }
+
+ assoc->backend = binitres->handle;
+ if ((assoc->init->bend_sort))
+ yaz_log (LOG_DEBUG, "Sort handler installed");
+ if ((assoc->init->bend_search))
+ yaz_log (LOG_DEBUG, "Search handler installed");
+ if ((assoc->init->bend_present))
+ yaz_log (LOG_DEBUG, "Present handler installed");
+ if ((assoc->init->bend_esrequest))
+ yaz_log (LOG_DEBUG, "ESRequest handler installed");
+ if ((assoc->init->bend_delete))
+ yaz_log (LOG_DEBUG, "Delete handler installed");
+ if ((assoc->init->bend_scan))
+ yaz_log (LOG_DEBUG, "Scan handler installed");
+ if ((assoc->init->bend_segment))
+ yaz_log (LOG_DEBUG, "Segment handler installed");
+
+ resp->referenceId = req->referenceId;
+ *options = '\0';
+ /* let's tell the client what we can do */
+ if (ODR_MASK_GET(req->options, Z_Options_search))
+ {
+ ODR_MASK_SET(resp->options, Z_Options_search);
+ strcat(options, "srch");
+ }
+ if (ODR_MASK_GET(req->options, Z_Options_present))
+ {
+ ODR_MASK_SET(resp->options, Z_Options_present);
+ strcat(options, " prst");
+ }
+ if (ODR_MASK_GET(req->options, Z_Options_delSet) &&
+ assoc->init->bend_delete)
+ {
+ ODR_MASK_SET(resp->options, Z_Options_delSet);
+ strcat(options, " del");
+ }
+ if (ODR_MASK_GET(req->options, Z_Options_extendedServices) &&
+ assoc->init->bend_esrequest)
+ {
+ ODR_MASK_SET(resp->options, Z_Options_extendedServices);
+ strcat (options, " extendedServices");
+ }
+ if (ODR_MASK_GET(req->options, Z_Options_namedResultSets))
+ {
+ ODR_MASK_SET(resp->options, Z_Options_namedResultSets);
+ strcat(options, " namedresults");
+ }
+ if (ODR_MASK_GET(req->options, Z_Options_scan) && assoc->init->bend_scan)
+ {
+ ODR_MASK_SET(resp->options, Z_Options_scan);
+ strcat(options, " scan");
+ }
+ if (ODR_MASK_GET(req->options, Z_Options_concurrentOperations))
+ {
+ ODR_MASK_SET(resp->options, Z_Options_concurrentOperations);
+ strcat(options, " concurrop");
+ }
+ if (ODR_MASK_GET(req->options, Z_Options_sort) && assoc->init->bend_sort)
+ {
+ ODR_MASK_SET(resp->options, Z_Options_sort);
+ strcat(options, " sort");
+ }
+
+ if (ODR_MASK_GET(req->options, Z_Options_negotiationModel)
+ && assoc->init->charneg_response)
+ {
+ Z_OtherInformation **p;
+ Z_OtherInformationUnit *p0;
+
+ yaz_oi_APDU(apdu, &p);
+
+ if ((p0=yaz_oi_update(p, assoc->encode, NULL, 0, 0))) {
+ ODR_MASK_SET(resp->options, Z_Options_negotiationModel);
+
+ p0->which = Z_OtherInfo_externallyDefinedInfo;
+ p0->information.externallyDefinedInfo =
+ assoc->init->charneg_response;
+ }
+ ODR_MASK_SET(resp->options, Z_Options_negotiationModel);
+ strcat(options, " negotiation");
+ }
+
+ if (ODR_MASK_GET(req->protocolVersion, Z_ProtocolVersion_1))
+ {
+ ODR_MASK_SET(resp->protocolVersion, Z_ProtocolVersion_1);
+ assoc->version = 2; /* 1 & 2 are equivalent */
+ }
+ if (ODR_MASK_GET(req->protocolVersion, Z_ProtocolVersion_2))
+ {
+ ODR_MASK_SET(resp->protocolVersion, Z_ProtocolVersion_2);
+ assoc->version = 2;
+ }
+ if (ODR_MASK_GET(req->protocolVersion, Z_ProtocolVersion_3))
+ {
+ ODR_MASK_SET(resp->protocolVersion, Z_ProtocolVersion_3);
+ assoc->version = 3;
+ }
+
+ yaz_log(LOG_LOG, "Negotiated to v%d: %s", assoc->version, options);
+ assoc->maximumRecordSize = *req->maximumRecordSize;
+ if (assoc->maximumRecordSize > control_block->maxrecordsize)
+ assoc->maximumRecordSize = control_block->maxrecordsize;
+ assoc->preferredMessageSize = *req->preferredMessageSize;
+ if (assoc->preferredMessageSize > assoc->maximumRecordSize)
+ assoc->preferredMessageSize = assoc->maximumRecordSize;
+
+ resp->preferredMessageSize = &assoc->preferredMessageSize;
+ resp->maximumRecordSize = &assoc->maximumRecordSize;
+
+ resp->implementationName = "GFS/YAZ";
+
+ if (assoc->init->implementation_id)
+ {
+ char *nv = (char *)
+ odr_malloc (assoc->encode,
+ strlen(assoc->init->implementation_id) + 10 +
+ strlen(resp->implementationId));
+ sprintf (nv, "%s / %s",
+ resp->implementationId, assoc->init->implementation_id);
+ resp->implementationId = nv;
+ }
+ if (assoc->init->implementation_name)
+ {
+ char *nv = (char *)
+ odr_malloc (assoc->encode,
+ strlen(assoc->init->implementation_name) + 10 +
+ strlen(resp->implementationName));
+ sprintf (nv, "%s / %s",
+ resp->implementationName, assoc->init->implementation_name);
+ resp->implementationName = nv;
+ }
+ if (assoc->init->implementation_version)
+ {
+ char *nv = (char *)
+ odr_malloc (assoc->encode,
+ strlen(assoc->init->implementation_version) + 10 +
+ strlen(resp->implementationVersion));
+ sprintf (nv, "YAZ %s / %s",
+ resp->implementationVersion,
+ assoc->init->implementation_version);
+ resp->implementationVersion = nv;
+ }
+
+ if (binitres->errcode)
+ {
+ yaz_log(LOG_LOG, "Connection rejected by backend.");
+ *resp->result = 0;
+ assoc->state = ASSOC_DEAD;
+ resp->userInformationField = init_diagnostics(assoc->encode,
+ binitres->errcode,
+ binitres->errstring);
+ }
+ else
+ assoc->state = ASSOC_UP;
+ return apdu;
+}
+
+/*
+ * Diagnostic in default format, to be returned as either a surrogate
+ * or non-surrogate diagnostic in the context of an open session, or
+ * as User-information when an Init is refused.
+ */
+static Z_DefaultDiagFormat *justdiag(ODR odr, int error, char *addinfo)
+{
+ int *err = odr_intdup(odr, error);
+ Z_DefaultDiagFormat *dr = (Z_DefaultDiagFormat *)
+ odr_malloc (odr, sizeof(*dr));
+
+ yaz_log(LOG_LOG, "[%d] %s%s%s", error, diagbib1_str(error),
+ addinfo ? " -- " : "", addinfo ? addinfo : "");
+
+ dr->diagnosticSetId =
+ yaz_oidval_to_z3950oid (odr, CLASS_DIAGSET, VAL_BIB1);
+ dr->condition = err;
+ dr->which = Z_DefaultDiagFormat_v2Addinfo;
+ dr->u.v2Addinfo = odr_strdup (odr, addinfo ? addinfo : "");
+ return dr;
+}
+
+/*
+ * Set the specified `errcode' and `errstring' into a UserInfo-1
+ * external to be returned to the client in accordance with Z35.90
+ * Implementor Agreement 5 (Returning diagnostics in an InitResponse):
+ * http://lcweb.loc.gov/z3950/agency/agree/initdiag.html
+ */
+static Z_External *init_diagnostics(ODR odr, int error, char *addinfo)
+{
+ Z_External *x, *x2;
+ oident oid;
+ Z_OtherInformation *u;
+ Z_OtherInformationUnit *l;
+ Z_DiagnosticFormat *d;
+ Z_DiagnosticFormat_s *e;
+
+ x = (Z_External*) odr_malloc(odr, sizeof *x);
+ x->descriptor = 0;
+ x->indirect_reference = 0;
+ oid.proto = PROTO_Z3950;
+ oid.oclass = CLASS_USERINFO;
+ oid.value = VAL_USERINFO1;
+ x->direct_reference = odr_oiddup(odr, oid_getoidbyent(&oid));
+ x->which = Z_External_userInfo1;
+
+ u = odr_malloc(odr, sizeof *u);
+ x->u.userInfo1 = u;
+ u->num_elements = 1;
+ u->list = (Z_OtherInformationUnit**) odr_malloc(odr, sizeof *u->list);
+ u->list[0] = (Z_OtherInformationUnit*) odr_malloc(odr, sizeof *u->list[0]);
+ l = u->list[0];
+ l->category = 0;
+ l->which = Z_OtherInfo_externallyDefinedInfo;
+
+ x2 = (Z_External*) odr_malloc(odr, sizeof *x);
+ l->information.externallyDefinedInfo = x2;
+ x2->descriptor = 0;
+ x2->indirect_reference = 0;
+ oid.oclass = CLASS_DIAGSET;
+ oid.value = VAL_DIAG1;
+ x2->direct_reference = odr_oiddup(odr, oid_getoidbyent(&oid));
+ x2->which = Z_External_diag1;
+
+ d = (Z_DiagnosticFormat*) odr_malloc(odr, sizeof *d);
+ x2->u.diag1 = d;
+ d->num = 1;
+ d->elements = (Z_DiagnosticFormat_s**) odr_malloc (odr, sizeof *d->elements);
+ d->elements[0] = (Z_DiagnosticFormat_s*) odr_malloc (odr, sizeof *d->elements[0]);
+ e = d->elements[0];
+
+ e->which = Z_DiagnosticFormat_s_defaultDiagRec;
+ e->u.defaultDiagRec = justdiag(odr, error, addinfo);
+ return x;
+}
+
+/*
+ * nonsurrogate diagnostic record.
+ */
+static Z_Records *diagrec(association *assoc, int error, char *addinfo)
+{
+ Z_Records *rec = (Z_Records *)
+ odr_malloc (assoc->encode, sizeof(*rec));
+ rec->which = Z_Records_NSD;
+ rec->u.nonSurrogateDiagnostic = justdiag(assoc->encode, error, addinfo);
+ return rec;
+}
+
+/*
+ * surrogate diagnostic.
+ */
+static Z_NamePlusRecord *surrogatediagrec(association *assoc, char *dbname,
+ int error, char *addinfo)
+{
+ Z_NamePlusRecord *rec = (Z_NamePlusRecord *)
+ odr_malloc (assoc->encode, sizeof(*rec));
+ Z_DiagRec *drec = (Z_DiagRec *)odr_malloc (assoc->encode, sizeof(*drec));
+
+ yaz_log(LOG_DEBUG, "SurrogateDiagnotic: %d -- %s", error, addinfo);
+ rec->databaseName = dbname;
+ rec->which = Z_NamePlusRecord_surrogateDiagnostic;
+ rec->u.surrogateDiagnostic = drec;
+ drec->which = Z_DiagRec_defaultFormat;
+ drec->u.defaultFormat = justdiag(assoc->encode, error, addinfo);
+
+ return rec;
+}
+
+/*
+ * multiple nonsurrogate diagnostics.
+ */
+static Z_DiagRecs *diagrecs(association *assoc, int error, char *addinfo)
+{
+ Z_DiagRecs *recs = (Z_DiagRecs *)odr_malloc (assoc->encode, sizeof(*recs));
+ int *err = odr_intdup(assoc->encode, error);
+ Z_DiagRec **recp = (Z_DiagRec **)odr_malloc (assoc->encode, sizeof(*recp));
+ Z_DiagRec *drec = (Z_DiagRec *)odr_malloc (assoc->encode, sizeof(*drec));
+ Z_DefaultDiagFormat *rec = (Z_DefaultDiagFormat *)
+ odr_malloc (assoc->encode, sizeof(*rec));
+
+ yaz_log(LOG_DEBUG, "DiagRecs: %d -- %s", error, addinfo ? addinfo : "");
+
+ recs->num_diagRecs = 1;
+ recs->diagRecs = recp;
+ recp[0] = drec;
+ drec->which = Z_DiagRec_defaultFormat;
+ drec->u.defaultFormat = rec;
+
+ rec->diagnosticSetId =
+ yaz_oidval_to_z3950oid (assoc->encode, CLASS_DIAGSET, VAL_BIB1);
+ rec->condition = err;
+
+ rec->which = Z_DefaultDiagFormat_v2Addinfo;
+ rec->u.v2Addinfo = odr_strdup (assoc->encode, addinfo ? addinfo : "");
+ return recs;
+}
+
+static Z_Records *pack_records(association *a, char *setname, int start,
+ int *num, Z_RecordComposition *comp,
+ int *next, int *pres, oid_value format,
+ Z_ReferenceId *referenceId,
+ int *oid)
+{
+ int recno, total_length = 0, toget = *num, dumped_records = 0;
+ Z_Records *records =
+ (Z_Records *) odr_malloc (a->encode, sizeof(*records));
+ Z_NamePlusRecordList *reclist =
+ (Z_NamePlusRecordList *) odr_malloc (a->encode, sizeof(*reclist));
+ Z_NamePlusRecord **list =
+ (Z_NamePlusRecord **) odr_malloc (a->encode, sizeof(*list) * toget);
+
+ records->which = Z_Records_DBOSD;
+ records->u.databaseOrSurDiagnostics = reclist;
+ reclist->num_records = 0;
+ reclist->records = list;
+ *pres = Z_PRES_SUCCESS;
+ *num = 0;
+ *next = 0;
+
+ yaz_log(LOG_LOG, "Request to pack %d+%d+%s", start, toget, setname);
+ yaz_log(LOG_DEBUG, "pms=%d, mrs=%d", a->preferredMessageSize,
+ a->maximumRecordSize);
+ for (recno = start; reclist->num_records < toget; recno++)
+ {
+ bend_fetch_rr freq;
+ Z_NamePlusRecord *thisrec;
+ int this_length = 0;
+ /*
+ * we get the number of bytes allocated on the stream before any
+ * allocation done by the backend - this should give us a reasonable
+ * idea of the total size of the data so far.
+ */
+ total_length = odr_total(a->encode) - dumped_records;
+ freq.errcode = 0;
+ freq.errstring = 0;
+ freq.basename = 0;
+ freq.len = 0;
+ freq.record = 0;
+ freq.last_in_set = 0;
+ freq.setname = setname;
+ freq.surrogate_flag = 0;
+ freq.number = recno;
+ freq.comp = comp;
+ freq.request_format = format;
+ freq.request_format_raw = oid;
+ freq.output_format = format;
+ freq.output_format_raw = 0;
+ freq.stream = a->encode;
+ freq.print = a->print;
+ freq.referenceId = referenceId;
+ freq.schema = 0;
+ (*a->init->bend_fetch)(a->backend, &freq);