Changed include/yaz/diagbib1.h and added include/yaz/diagsrw.h with
[yaz-moved-to-github.git] / src / seshigh.c
index 64eeae1..88dc833 100644 (file)
@@ -2,7 +2,7 @@
  * Copyright (C) 1995-2005, Index Data ApS
  * See the file LICENSE for details.
  *
- * $Id: seshigh.c,v 1.46 2005-01-19 09:18:08 adam Exp $
+ * $Id: seshigh.c,v 1.53 2005-04-22 08:27:58 adam Exp $
  */
 /**
  * \file seshigh.c
 #include <unistd.h>
 #endif
 
+#if HAVE_XML2
+#include <libxml/parser.h>
+#include <libxml/tree.h>
+#endif
+
 #include <yaz/yconfig.h>
 #include <yaz/xmalloc.h>
 #include <yaz/comstack.h>
@@ -91,9 +96,6 @@ static Z_APDU *process_deleteRequest(association *assoc, request *reqb,
     int *fd);
 static Z_APDU *process_segmentRequest (association *assoc, request *reqb);
 
-static FILE *apduf = 0; /* for use in static mode */
-static statserv_options_block *control_block = 0;
-
 static Z_APDU *process_ESRequest(association *assoc, request *reqb, int *fd);
 
 /* dynamic logging levels */
@@ -107,10 +109,10 @@ static void get_logbits()
 { /* needs to be called after parsing cmd-line args that can set loglevels!*/
     if (!logbits_set)
     {
-        logbits_set=1;
-        log_session=yaz_log_module_level("session"); 
-        log_request=yaz_log_module_level("request"); 
-        log_requestdetail=yaz_log_module_level("requestdetail"); 
+        logbits_set = 1;
+        log_session = yaz_log_module_level("session"); 
+        log_request = yaz_log_module_level("request"); 
+        log_requestdetail = yaz_log_module_level("requestdetail"); 
     }
 }
 
@@ -128,18 +130,18 @@ static void wr_diag(WRBUF w, int error, const char *addinfo)
  *  link     : communications channel.
  * Returns: 0 or a new association handle.
  */
-association *create_association(IOCHAN channel, COMSTACK link)
+association *create_association(IOCHAN channel, COMSTACK link,
+                               const char *apdufile)
 {
     association *anew;
 
     if (!logbits_set)
         get_logbits();
-    if (!control_block)
-        control_block = statserv_getcontrol();
     if (!(anew = (association *)xmalloc(sizeof(*anew))))
         return 0;
     anew->init = 0;
     anew->version = 0;
+    anew->last_control = 0;
     anew->client_chan = channel;
     anew->client_link = link;
     anew->cs_get_mask = 0;
@@ -148,44 +150,26 @@ association *create_association(IOCHAN channel, COMSTACK link)
     if (!(anew->decode = odr_createmem(ODR_DECODE)) ||
         !(anew->encode = odr_createmem(ODR_ENCODE)))
         return 0;
-    if (*control_block->apdufile)
+    if (apdufile && *apdufile)
     {
-        char filename[256];
         FILE *f;
 
-        strcpy(filename, control_block->apdufile);
         if (!(anew->print = odr_createmem(ODR_PRINT)))
             return 0;
-        if (*control_block->apdufile == '@')
+        if (*apdufile == '@')
         {
             odr_setprint(anew->print, yaz_log_file());
         }       
-        else if (*control_block->apdufile != '-')
+        else if (*apdufile != '-')
         {
-            strcpy(filename, control_block->apdufile);
-            if (!control_block->dynamic)
-            {
-                if (!apduf)
-                {
-                    if (!(apduf = fopen(filename, "w")))
-                    {
-                        yaz_log(YLOG_WARN|YLOG_ERRNO, "can't open apdu dump %s", filename);
-                        return 0;
-                    }
-                    setvbuf(apduf, 0, _IONBF, 0);
-                }
-                f = apduf;
-            }
-            else 
-            {
-                sprintf(filename + strlen(filename), ".%ld", (long)getpid());
-                if (!(f = fopen(filename, "w")))
-                {
-                    yaz_log(YLOG_WARN|YLOG_ERRNO, "%s", filename);
-                    return 0;
-                }
-                setvbuf(f, 0, _IONBF, 0);
-            }
+           char filename[256];
+           sprintf(filename, "%.200s.%ld", apdufile, (long)getpid());
+           if (!(f = fopen(filename, "w")))
+           {
+               yaz_log(YLOG_WARN|YLOG_ERRNO, "%s", filename);
+               return 0;
+           }
+           setvbuf(f, 0, _IONBF, 0);
             odr_setprint(anew->print, f);
         }
     }
@@ -198,6 +182,8 @@ association *create_association(IOCHAN channel, COMSTACK link)
     request_initq(&anew->incoming);
     request_initq(&anew->outgoing);
     anew->proto = cs_getproto(link);
+    anew->cql_transform = 0;
+    anew->server_node_ptr = 0;
     return anew;
 }
 
@@ -226,7 +212,7 @@ void destroy_association(association *h)
     request_delq(&h->outgoing);
     xfree(h);
     xmalloc_trav("session closed");
-    if (control_block && control_block->one_shot)
+    if (cb && cb->one_shot)
     {
         exit (0);
     }
@@ -464,6 +450,7 @@ void ir_session(IOCHAN h, int event)
 
 static int process_z_request(association *assoc, request *req, char **msg);
 
+
 static void assoc_init_reset(association *assoc)
 {
     xfree (assoc->init);
@@ -493,37 +480,45 @@ static void assoc_init_reset(association *assoc)
     assoc->init->decode = assoc->decode;
     assoc->init->peer_name = 
         odr_strdup (assoc->encode, cs_addrstr(assoc->client_link));
+
+    yaz_log(log_requestdetail, "peer %s", assoc->init->peer_name);
 }
 
 static int srw_bend_init(association *assoc, Z_SRW_diagnostic **d, int *num)
 {
-    const char *encoding = "UTF-8";
-    Z_External *ce;
-    bend_initresult *binitres;
     statserv_options_block *cb = statserv_getcontrol();
-    
-    assoc_init_reset(assoc);
+    if (!assoc->init)
+    {
+       const char *encoding = "UTF-8";
+       Z_External *ce;
+       bend_initresult *binitres;
 
-    assoc->maximumRecordSize = 3000000;
-    assoc->preferredMessageSize = 3000000;
+       yaz_log(YLOG_LOG, "srw_bend_init config=%s", cb->configname);
+       assoc_init_reset(assoc);
+       
+       assoc->maximumRecordSize = 3000000;
+       assoc->preferredMessageSize = 3000000;
 #if 1
-    ce = yaz_set_proposal_charneg(assoc->decode, &encoding, 1, 0, 0, 1);
-    assoc->init->charneg_request = ce->u.charNeg3;
+       ce = yaz_set_proposal_charneg(assoc->decode, &encoding, 1, 0, 0, 1);
+       assoc->init->charneg_request = ce->u.charNeg3;
 #endif
-    assoc->backend = 0;
-    if (!(binitres = (*cb->bend_init)(assoc->init)))
-    {
-        assoc->state = ASSOC_DEAD;
-       yaz_add_srw_diagnostic(assoc->encode, d, num, 3, 0);
-        return 0;
-    }
-    assoc->backend = binitres->handle;
-    if (binitres->errcode)
-    {
-        assoc->state = ASSOC_DEAD;
-       yaz_add_srw_diagnostic(assoc->encode, d, num, binitres->errcode,
-                              binitres->errstring);
-       return 0;
+       assoc->backend = 0;
+       if (!(binitres = (*cb->bend_init)(assoc->init)))
+       {
+           assoc->state = ASSOC_DEAD;
+           yaz_add_srw_diagnostic(assoc->encode, d, num,
+                           YAZ_SRW_AUTHENTICATION_ERROR, 0);
+           return 0;
+       }
+       assoc->backend = binitres->handle;
+       if (binitres->errcode)
+       {
+           assoc->state = ASSOC_DEAD;
+           yaz_add_srw_diagnostic(assoc->encode, d, num, binitres->errcode,
+                                  binitres->errstring);
+           return 0;
+       }
+       return 1;
     }
     return 1;
 }
@@ -634,6 +629,79 @@ static int srw_bend_fetch(association *assoc, int pos,
     return rr.errcode;
 }
 
+static int cql2pqf(ODR odr, const char *cql, cql_transform_t ct,
+                  Z_Query *query_result)
+{
+    /* have a CQL query and  CQL to PQF transform .. */
+    CQL_parser cp = cql_parser_create();
+    int r;
+    int srw_errcode = 0;
+    const char *add = 0;
+    char rpn_buf[512];
+           
+    r = cql_parser_string(cp, cql);
+    if (r)
+    {
+       /* CQL syntax error */
+       srw_errcode = 10; 
+    }
+    if (!r)
+    {
+       /* Syntax OK */
+       r = cql_transform_buf(ct,
+                             cql_parser_result(cp),
+                             rpn_buf, sizeof(rpn_buf)-1);
+       if (r)
+           srw_errcode  = cql_transform_error(ct, &add);
+    }
+    if (!r)
+    {
+       /* Syntax & transform OK. */
+       /* Convert PQF string to Z39.50 to RPN query struct */
+       YAZ_PQF_Parser pp = yaz_pqf_create();
+       Z_RPNQuery *rpnquery = yaz_pqf_parse(pp, odr, rpn_buf);
+       if (!rpnquery)
+       {
+           size_t off;
+           const char *pqf_msg;
+           int code = yaz_pqf_error(pp, &pqf_msg, &off);
+           yaz_log(YLOG_WARN, "PQF Parser Error %s (code %d)",
+                   pqf_msg, code);
+           srw_errcode = 10;
+       }
+       else
+       {
+           query_result->which = Z_Query_type_1;
+           query_result->u.type_1 = rpnquery;
+       }
+       yaz_pqf_destroy(pp);
+    }
+    cql_parser_destroy(cp);
+    return srw_errcode;
+}
+
+static int cql2pqf_scan(ODR odr, const char *cql, cql_transform_t ct,
+                       Z_AttributesPlusTerm *result)
+{
+    Z_Query query;
+    Z_RPNQuery *rpn;
+    int srw_error = cql2pqf(odr, cql, ct, &query);
+    if (srw_error)
+       return srw_error;
+    if (query.which != Z_Query_type_1 && query.which != Z_Query_type_101)
+       return 10; /* bad query type */
+    rpn = query.u.type_1;
+    if (!rpn->RPNStructure) 
+       return 10; /* must be structure */
+    if (rpn->RPNStructure->which != Z_RPNStructure_simple)
+       return 10; /* must be simple */
+    if (rpn->RPNStructure->u.simple->which != Z_Operand_APT)
+       return 10; /* must be attributes plus term node .. */
+    memcpy(result, rpn->RPNStructure->u.simple->u.attributesPlusTerm,
+          sizeof(*result));
+    return 0;
+}
+                  
 static void srw_bend_search(association *assoc, request *req,
                             Z_SRW_searchRetrieveRequest *srw_req,
                             Z_SRW_searchRetrieveResponse *srw_res,
@@ -644,11 +712,11 @@ static void srw_bend_search(association *assoc, request *req,
     
     *http_code = 200;
     yaz_log(log_requestdetail, "Got SRW SearchRetrieveRequest");
-    if (!assoc->init)
-        srw_bend_init(assoc, &srw_res->diagnostics, &srw_res->num_diagnostics);
+    srw_bend_init(assoc, &srw_res->diagnostics, &srw_res->num_diagnostics);
     if (srw_req->sort_type != Z_SRW_sort_type_none)
        yaz_add_srw_diagnostic(assoc->encode, &srw_res->diagnostics,
-                              &srw_res->num_diagnostics, 80, 0);
+                              &srw_res->num_diagnostics,
+                              YAZ_SRW_SORT_UNSUPP, 0);
     else if (srw_res->num_diagnostics == 0 && assoc->init)
     {
        bend_search_rr rr;
@@ -659,19 +727,36 @@ static void srw_bend_search(association *assoc, request *req,
        rr.referenceId = 0;
        
        rr.query = (Z_Query *) odr_malloc (assoc->decode, sizeof(*rr.query));
+       rr.query->u.type_1 = 0;
        
        if (srw_req->query_type == Z_SRW_query_type_cql)
        {
-           ext = (Z_External *) odr_malloc(assoc->decode, sizeof(*ext));
-           ext->direct_reference = odr_getoidbystr(assoc->decode, 
-                                                   "1.2.840.10003.16.2");
-           ext->indirect_reference = 0;
-           ext->descriptor = 0;
-           ext->which = Z_External_CQL;
-           ext->u.cql = srw_req->query.cql;
-           
-           rr.query->which = Z_Query_type_104;
-           rr.query->u.type_104 =  ext;
+           if (assoc->cql_transform)
+           {
+               int srw_errcode = cql2pqf(assoc->encode, srw_req->query.cql,
+                                         assoc->cql_transform, rr.query);
+               if (srw_errcode)
+               {
+                   yaz_add_srw_diagnostic(assoc->encode,
+                                          &srw_res->diagnostics,
+                                          &srw_res->num_diagnostics,
+                                          srw_errcode, 0);
+               }
+           }
+           else
+           {
+               /* CQL query to backend. Wrap it - Z39.50 style */
+               ext = (Z_External *) odr_malloc(assoc->decode, sizeof(*ext));
+               ext->direct_reference = odr_getoidbystr(assoc->decode, 
+                                                       "1.2.840.10003.16.2");
+               ext->indirect_reference = 0;
+               ext->descriptor = 0;
+               ext->which = Z_External_CQL;
+               ext->u.cql = srw_req->query.cql;
+               
+               rr.query->which = Z_Query_type_104;
+               rr.query->u.type_104 =  ext;
+           }
        }
        else if (srw_req->query_type == Z_SRW_query_type_pqf)
        {
@@ -689,7 +774,7 @@ static void srw_bend_search(association *assoc, request *req,
                int code = yaz_pqf_error (pqf_parser, &pqf_msg, &off);
                yaz_log(log_requestdetail, "Parse error %d %s near offset %d",
                        code, pqf_msg, off);
-               srw_error = 10;
+               srw_error = YAZ_SRW_QUERY_SYNTAX_ERROR;
            }
            
            rr.query->which = Z_Query_type_1;
@@ -699,9 +784,9 @@ static void srw_bend_search(association *assoc, request *req,
        }
        else
        {
-           rr.query->u.type_1 = 0;
            yaz_add_srw_diagnostic(assoc->encode, &srw_res->diagnostics,
-                                  &srw_res->num_diagnostics, 11, 0);
+                                  &srw_res->num_diagnostics,
+                                  YAZ_SRW_UNSUPP_QUERY_TYPE, 0);
        }
        if (rr.query->u.type_1)
        {
@@ -720,7 +805,7 @@ static void srw_bend_search(association *assoc, request *req,
            (assoc->init->bend_search)(assoc->backend, &rr);
            if (rr.errcode)
            {
-               if (rr.errcode == 109) /* database unavailable */
+               if (rr.errcode == YAZ_BIB1_DATABASE_UNAVAILABLE)
                {
                    *http_code = 404;
                }
@@ -750,7 +835,7 @@ static void srw_bend_search(association *assoc, request *req,
                    {
                        yaz_add_srw_diagnostic(assoc->encode, &srw_res->diagnostics,
                                               &srw_res->num_diagnostics,
-                                              61, 0);
+                                              YAZ_SRW_FIRST_RECORD_POSITION_OUT_OF_RANGE, 0);
                    }
                    else
                    {
@@ -832,6 +917,42 @@ static void srw_bend_search(association *assoc, request *req,
     }
 }
 
+static char *srw_bend_explain_default(void *handle, bend_explain_rr *rr)
+{
+#if HAVE_XML2
+    xmlNodePtr ptr = rr->server_node_ptr;
+    if (!ptr)
+       return 0;
+    for (ptr = ptr->children; ptr; ptr = ptr->next)
+    {
+       if (ptr->type != XML_ELEMENT_NODE)
+           continue;
+       if (!strcmp((const char *) ptr->name, "explain"))
+       {
+           int len;
+           xmlDocPtr doc = xmlNewDoc("1.0");
+           xmlChar *buf_out;
+           char *content;
+
+           ptr = xmlCopyNode(ptr, 1);
+        
+           xmlDocSetRootElement(doc, ptr);
+           
+           xmlDocDumpMemory(doc, &buf_out, &len);
+           content = (char*) odr_malloc(rr->stream, 1+len);
+           memcpy(content, buf_out, len);
+           content[len] = '\0';
+           
+           xmlFree(buf_out);
+           xmlFreeDoc(doc);
+           rr->explain_buf = content;
+           return 0;
+       }
+    }
+#endif
+    return 0;
+}
+
 static void srw_bend_explain(association *assoc, request *req,
                              Z_SRW_explainRequest *srw_req,
                              Z_SRW_explainResponse *srw_res,
@@ -839,32 +960,36 @@ static void srw_bend_explain(association *assoc, request *req,
 {
     yaz_log(log_requestdetail, "Got SRW ExplainRequest");
     *http_code = 404;
-    if (!assoc->init)
-        srw_bend_init(assoc, &srw_res->diagnostics, &srw_res->num_diagnostics);
-    if (assoc->init && assoc->init->bend_explain)
-    {
-        bend_explain_rr rr;
-
-        rr.stream = assoc->encode;
-        rr.decode = assoc->decode;
-        rr.print = assoc->print;
-        rr.explain_buf = 0;
-        rr.database = srw_req->database;
-        rr.schema = "http://explain.z3950.org/dtd/2.0/";
-        (*assoc->init->bend_explain)(assoc->backend, &rr);
-        if (rr.explain_buf)
-        {
-            int packing = Z_SRW_recordPacking_string;
-            if (srw_req->recordPacking && 
-                !strcmp(srw_req->recordPacking, "xml"))
-                packing = Z_SRW_recordPacking_XML;
-            srw_res->record.recordSchema = rr.schema;
-            srw_res->record.recordPacking = packing;
-            srw_res->record.recordData_buf = rr.explain_buf;
-            srw_res->record.recordData_len = strlen(rr.explain_buf);
-            srw_res->record.recordPosition = 0;
-            *http_code = 200;
-        }
+    srw_bend_init(assoc, &srw_res->diagnostics, &srw_res->num_diagnostics);
+    if (assoc->init)
+    {
+       bend_explain_rr rr;
+       
+       rr.stream = assoc->encode;
+       rr.decode = assoc->decode;
+       rr.print = assoc->print;
+       rr.explain_buf = 0;
+       rr.database = srw_req->database;
+       rr.server_node_ptr = assoc->server_node_ptr;
+       rr.schema = "http://explain.z3950.org/dtd/2.0/";
+       if (assoc->init->bend_explain)
+           (*assoc->init->bend_explain)(assoc->backend, &rr);
+       else
+           srw_bend_explain_default(assoc->backend, &rr);
+
+       if (rr.explain_buf)
+       {
+           int packing = Z_SRW_recordPacking_string;
+           if (srw_req->recordPacking && 
+               !strcmp(srw_req->recordPacking, "xml"))
+               packing = Z_SRW_recordPacking_XML;
+           srw_res->record.recordSchema = rr.schema;
+           srw_res->record.recordPacking = packing;
+           srw_res->record.recordData_buf = rr.explain_buf;
+           srw_res->record.recordData_len = strlen(rr.explain_buf);
+           srw_res->record.recordPosition = 0;
+           *http_code = 200;
+       }
     }
 }
 
@@ -876,8 +1001,7 @@ static void srw_bend_scan(association *assoc, request *req,
     yaz_log(log_requestdetail, "Got SRW ScanRequest");
 
     *http_code = 200;
-    if (!assoc->init)
-        srw_bend_init(assoc, &srw_res->diagnostics, &srw_res->num_diagnostics);
+    srw_bend_init(assoc, &srw_res->diagnostics, &srw_res->num_diagnostics);
     if (srw_res->num_diagnostics == 0 && assoc->init)
     {
        struct scan_entry *save_entries;
@@ -941,21 +1065,45 @@ static void srw_bend_scan(association *assoc, request *req,
        else if (srw_req->query_type == Z_SRW_query_type_cql
                 && assoc->init->bend_srw_scan)
        {
-           bsrr->term = 0;
-           bsrr->attributeset = VAL_NONE;
-           bsrr->scanClause = srw_req->scanClause.cql;
-           ((int (*)(void *, bend_scan_rr *))
-            (*assoc->init->bend_srw_scan))(assoc->backend, bsrr);
+           if (assoc->cql_transform)
+           {
+               int srw_error;
+               bsrr->scanClause = 0;
+               bsrr->attributeset = VAL_NONE;
+               bsrr->term = odr_malloc(assoc->decode, sizeof(*bsrr->term));
+               srw_error = cql2pqf_scan(assoc->encode,
+                                            srw_req->scanClause.cql,
+                                            assoc->cql_transform,
+                                            bsrr->term);
+               if (srw_error)
+                   yaz_add_srw_diagnostic(assoc->encode, &srw_res->diagnostics,
+                                          &srw_res->num_diagnostics,
+                                          srw_error, 0);
+               else
+               {
+                   ((int (*)(void *, bend_scan_rr *))
+                    (*assoc->init->bend_scan))(assoc->backend, bsrr);
+               }
+           }
+           else
+           {
+               bsrr->term = 0;
+               bsrr->attributeset = VAL_NONE;
+               bsrr->scanClause = srw_req->scanClause.cql;
+               ((int (*)(void *, bend_scan_rr *))
+                (*assoc->init->bend_srw_scan))(assoc->backend, bsrr);
+           }
        }
        else
        {
            yaz_add_srw_diagnostic(assoc->encode, &srw_res->diagnostics,
-                                  &srw_res->num_diagnostics, 4, "scan");
+                                  &srw_res->num_diagnostics,
+                                  YAZ_SRW_UNSUPP_OPERATION, "scan");
        }
        if (bsrr->errcode)
        {
            int srw_error;
-           if (bsrr->errcode == 109) /* database unavailable */
+           if (bsrr->errcode == YAZ_BIB1_DATABASE_UNAVAILABLE)
            {
                *http_code = 404;
                return;
@@ -1046,8 +1194,14 @@ static void process_http_request(association *assoc, request *req)
     char *stylesheet = 0;
     Z_SRW_diagnostic *diagnostic = 0;
     int num_diagnostic = 0;
+    const char *host = z_HTTP_header_lookup(hreq->headers, "Host");
 
-    if (!strcmp(hreq->path, "/test")) 
+    if (!control_association(assoc, host, 0))
+    {
+       p = z_get_HTTP_Response(o, 404);
+       r = 1;
+    }
+    if (r == 2 && !strcmp(hreq->path, "/test")) 
     {   
         p = z_get_HTTP_Response(o, 200);
         hres = p->u.HTTP_Response;
@@ -1236,8 +1390,6 @@ static int process_z_request(association *assoc, request *req, char **msg)
     switch (req->apdu_request->which)
     {
     case Z_APDU_initRequest:
-        iochan_settimeout(assoc->client_chan,
-                          statserv_getcontrol()->idle_timeout * 60);
         res = process_initRequest(assoc, req); break;
     case Z_APDU_searchRequest:
         res = process_searchRequest(assoc, req, &fd); break;
@@ -1317,7 +1469,7 @@ static int process_z_request(association *assoc, request *req, char **msg)
 
         yaz_log(YLOG_DEBUG, "   establishing handler for result");
         req->state = REQUEST_PENDING;
-        if (!(chan = iochan_create(fd, backend_response, EVENT_INPUT)))
+        if (!(chan = iochan_create(fd, backend_response, EVENT_INPUT, 0)))
             abort();
         iochan_setdata(chan, assoc);
         retval = 0;
@@ -1419,6 +1571,10 @@ static int process_z_response(association *assoc, request *req, Z_APDU *res)
     return process_gdu_response(assoc, req, gres);
 }
 
+static char *get_vhost(Z_OtherInformation *otherInfo)
+{
+    return yaz_oi_get_string_oidval(&otherInfo, VAL_PROXY, 1, 0);
+}
 
 /*
  * Handle init request.
@@ -1429,22 +1585,28 @@ static int process_z_response(association *assoc, request *req, Z_APDU *res)
  */
 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 *version;
     char options[140];
+    statserv_options_block *cb = 0;  /* by default no control for backend */
 
+    if (control_association(assoc, get_vhost(req->otherInfo), 1))
+       cb = statserv_getcontrol();  /* got control block for backend */
+    
     yaz_log(log_requestdetail, "Got initRequest");
     if (req->implementationId)
-        yaz_log(log_requestdetail, "Id:        %s", req->implementationId);
+        yaz_log(log_requestdetail, "Id:        %s",
+               req->implementationId);
     if (req->implementationName)
-        yaz_log(log_requestdetail, "Name:      %s", req->implementationName);
+        yaz_log(log_requestdetail, "Name:      %s",
+               req->implementationName);
     if (req->implementationVersion)
-        yaz_log(log_requestdetail, "Version:   %s", req->implementationVersion);
-
+        yaz_log(log_requestdetail, "Version:   %s",
+               req->implementationVersion);
+    
     assoc_init_reset(assoc);
 
     assoc->init->auth = req->idAuthentication;
@@ -1460,27 +1622,44 @@ static Z_APDU *process_initRequest(association *assoc, request *reqb)
     }
     
     assoc->backend = 0;
-    if (!(binitres = (*cb->bend_init)(assoc->init)))
+    if (cb)
     {
-        yaz_log(YLOG_WARN, "Bad response from backend.");
-        return 0;
+       if (req->implementationVersion)
+           yaz_log(log_requestdetail, "Config:    %s",
+                   cb->configname);
+    
+        iochan_settimeout(assoc->client_chan, cb->idle_timeout * 60);
+       
+       /* we have a backend control block, so call that init function */
+       if (!(binitres = (*cb->bend_init)(assoc->init)))
+       {
+           yaz_log(YLOG_WARN, "Bad response from backend.");
+           return 0;
+       }
+       assoc->backend = binitres->handle;
+    }
+    else
+    {
+       /* no backend. return error */
+       binitres = odr_malloc(assoc->encode, sizeof(*binitres));
+       binitres->errstring = 0;
+       binitres->errcode = YAZ_BIB1_PERMANENT_SYSTEM_ERROR;
+       iochan_settimeout(assoc->client_chan, 10);
     }
-
-    assoc->backend = binitres->handle;
     if ((assoc->init->bend_sort))
-        yaz_log (YLOG_DEBUG, "Sort handler installed");
+       yaz_log (YLOG_DEBUG, "Sort handler installed");
     if ((assoc->init->bend_search))
-        yaz_log (YLOG_DEBUG, "Search handler installed");
+       yaz_log (YLOG_DEBUG, "Search handler installed");
     if ((assoc->init->bend_present))
-        yaz_log (YLOG_DEBUG, "Present handler installed");   
+       yaz_log (YLOG_DEBUG, "Present handler installed");   
     if ((assoc->init->bend_esrequest))
-        yaz_log (YLOG_DEBUG, "ESRequest handler installed");   
+       yaz_log (YLOG_DEBUG, "ESRequest handler installed");   
     if ((assoc->init->bend_delete))
-        yaz_log (YLOG_DEBUG, "Delete handler installed");   
+       yaz_log (YLOG_DEBUG, "Delete handler installed");   
     if ((assoc->init->bend_scan))
-        yaz_log (YLOG_DEBUG, "Scan handler installed");   
+       yaz_log (YLOG_DEBUG, "Scan handler installed");   
     if ((assoc->init->bend_segment))
-        yaz_log (YLOG_DEBUG, "Segment handler installed");   
+       yaz_log (YLOG_DEBUG, "Segment handler installed");   
     
     resp->referenceId = req->referenceId;
     *options = '\0';
@@ -1567,8 +1746,9 @@ static Z_APDU *process_initRequest(association *assoc, request *reqb)
 
     yaz_log(log_requestdetail, "Negotiated to v%d: %s", assoc->version, options);
     assoc->maximumRecordSize = *req->maximumRecordSize;
-    if (assoc->maximumRecordSize > control_block->maxrecordsize)
-        assoc->maximumRecordSize = control_block->maxrecordsize;
+
+    if (cb && assoc->maximumRecordSize > cb->maxrecordsize)
+        assoc->maximumRecordSize = cb->maxrecordsize;
     assoc->preferredMessageSize = *req->preferredMessageSize;
     if (assoc->preferredMessageSize > assoc->maximumRecordSize)
         assoc->preferredMessageSize = assoc->maximumRecordSize;
@@ -1584,7 +1764,7 @@ static Z_APDU *process_initRequest(association *assoc, request *reqb)
                 assoc->init->implementation_name,
                 odr_prepend(assoc->encode, "GFS", resp->implementationName));
 
-    version = odr_strdup(assoc->encode, "$Revision: 1.46 $");
+    version = odr_strdup(assoc->encode, "$Revision: 1.53 $");
     if (strlen(version) > 10)   /* check for unexpanded CVS strings */
         version[strlen(version)-2] = '\0';
     resp->implementationVersion = odr_prepend(assoc->encode,
@@ -1727,7 +1907,8 @@ static Z_Records *pack_records(association *a, char *setname, int start,
                 *pres = Z_PresentStatus_failure;
                 /* for 'present request out of range',
                    set addinfo to record position if not set */
-                if (freq.errcode == 13 && freq.errstring == 0)
+                if (freq.errcode == YAZ_BIB1_PRESENT_REQUEST_OUT_OF_RANGE  && 
+                               freq.errstring == 0)
                 {
                     sprintf (s, "%d", recno);
                     freq.errstring = s;
@@ -1790,10 +1971,10 @@ static Z_Records *pack_records(association *a, char *setname, int start,
         if (!(thisrec = (Z_NamePlusRecord *)
               odr_malloc(a->encode, sizeof(*thisrec))))
             return 0;
-        if (!(thisrec->databaseName = (char *)odr_malloc(a->encode,
-            strlen(freq.basename) + 1)))
-            return 0;
-        strcpy(thisrec->databaseName, freq.basename);
+       if (freq.basename)
+           thisrec->databaseName = odr_strdup(a->encode, freq.basename);
+       else
+           thisrec->databaseName = 0;
         thisrec->which = Z_NamePlusRecord_databaseRecord;
 
         if (freq.output_format_raw)
@@ -1848,11 +2029,24 @@ static Z_APDU *process_searchRequest(association *assoc, request *reqb,
         nmem_transfer(bsrr->stream->mem, reqb->request_mem);
         bsrr->decode = assoc->decode;
         bsrr->print = assoc->print;
-        bsrr->errcode = 0;
         bsrr->hits = 0;
+        bsrr->errcode = 0;
         bsrr->errstring = NULL;
         bsrr->search_info = NULL;
-        (assoc->init->bend_search)(assoc->backend, bsrr);
+
+       if (assoc->cql_transform &&
+           req->query->which == Z_Query_type_104 &&
+           req->query->u.type_104->which == Z_External_CQL)
+       {
+           /* have a CQL query and a CQL to PQF transform .. */
+           int srw_errcode = 
+               cql2pqf(bsrr->stream, req->query->u.type_104->u.cql,
+                       assoc->cql_transform, bsrr->query);
+           if (srw_errcode)
+               bsrr->errcode = yaz_diag_srw_to_bib1(srw_errcode);
+       }
+       if (!bsrr->errcode)
+           (assoc->init->bend_search)(assoc->backend, bsrr);
         if (!bsrr->request)  /* backend not ready with the search response */
             return 0;  /* should not be used any more */
     }
@@ -2616,7 +2810,7 @@ static Z_APDU *process_ESRequest(association *assoc, request *reqb, int *fd)
         resp->diagnostics = diagRecs->diagRecs;
         if (log_request)
         {
-            WRBUF wr=wrbuf_alloc();
+            WRBUF wr = wrbuf_alloc();
             wrbuf_diags(wr, resp->num_diagnostics, resp->diagnostics);
             yaz_log(log_request, "EsRequest %s", wrbuf_buf(wr) );
             wrbuf_free(wr, 1);