Added explain facility for frontend server XML config. Renamed
[yaz-moved-to-github.git] / src / seshigh.c
index 70581d4..c812921 100644 (file)
@@ -2,7 +2,7 @@
  * Copyright (C) 1995-2005, Index Data ApS
  * See the file LICENSE for details.
  *
- * $Id: seshigh.c,v 1.47 2005-02-01 14:46:47 adam Exp $
+ * $Id: seshigh.c,v 1.49 2005-03-03 23:16:20 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>
@@ -177,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;
 }
 
@@ -475,8 +482,6 @@ static void assoc_init_reset(association *assoc)
         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)
@@ -623,6 +628,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,
@@ -647,19 +725,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)
        {
@@ -687,7 +782,6 @@ 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);
        }
@@ -820,6 +914,39 @@ static void srw_bend_search(association *assoc, request *req,
     }
 }
 
+static char *srw_bend_explain_default(void *handle, bend_explain_rr *rr)
+{
+    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;
+           ptr = xmlCopyNode(ptr, 1);
+
+           xmlDocPtr doc = xmlNewDoc((const xmlChar *) "1.0");
+        
+           xmlDocSetRootElement(doc, ptr);
+           
+           xmlChar *buf_out;
+           xmlDocDumpMemory(doc, &buf_out, &len);
+           char *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;
+       }
+    }
+    return 0;
+}
+
 static void srw_bend_explain(association *assoc, request *req,
                              Z_SRW_explainRequest *srw_req,
                              Z_SRW_explainResponse *srw_res,
@@ -828,30 +955,35 @@ static void srw_bend_explain(association *assoc, request *req,
     yaz_log(log_requestdetail, "Got SRW ExplainRequest");
     *http_code = 404;
     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;
-        }
+    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;
+       }
     }
 }
 
@@ -927,11 +1059,33 @@ 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)
+           {
+               bsrr->scanClause = 0;
+               bsrr->attributeset = VAL_NONE;
+               bsrr->term = odr_malloc(assoc->decode, sizeof(*bsrr->term));
+               int 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
        {
@@ -1602,7 +1756,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.47 $");
+    version = odr_strdup(assoc->encode, "$Revision: 1.49 $");
     if (strlen(version) > 10)   /* check for unexpanded CVS strings */
         version[strlen(version)-2] = '\0';
     resp->implementationVersion = odr_prepend(assoc->encode,
@@ -1866,11 +2020,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 */
     }