Fixed bug #1507: Command record=..&id=.. should block if it does not exist.
[pazpar2-moved-to-github.git] / src / logic.c
index eae862b..e8c2e88 100644 (file)
@@ -1,4 +1,4 @@
-/* $Id: logic.c,v 1.54 2007-07-16 17:01:46 adam Exp $
+/* $Id: logic.c,v 1.64 2007-09-05 08:40:12 adam Exp $
    Copyright (c) 2006-2007, Index Data.
 
 This file is part of Pazpar2.
@@ -46,6 +46,7 @@ Free Software Foundation, 59 Temple Place - Suite 330, Boston, MA
 #include <yaz/query-charset.h>
 #include <yaz/querytowrbuf.h>
 #include <yaz/oid_db.h>
+#include <yaz/snprintf.h>
 
 #if HAVE_CONFIG_H
 #include "cconfig.h"
@@ -92,10 +93,11 @@ struct parameters global_parameters =
     100,
     MAX_CHUNK,
     0,
-    0
+    0,
+    180,
+    30
 };
 
-
 // Recursively traverse query structure to extract terms.
 void pull_terms(NMEM nmem, struct ccl_rpn_node *n, char **termlist, int *num)
 {
@@ -189,7 +191,18 @@ xmlDoc *record_to_xml(struct session_database *sdb, Z_External *rec)
             rdoc = xmlParseMemory((char*) wrbuf_buf(wrbuf_opac),
                                   wrbuf_len(wrbuf_opac));
             if (!rdoc)
+            {
                 yaz_log(YLOG_WARN, "Unable to parse OPAC XML");
+                /* Was used to debug bug #1348 */
+#if 0
+                FILE *f = fopen("/tmp/opac.xml.txt", "wb");
+                if (f)
+                {
+                    fwrite(wrbuf_buf(wrbuf_opac), 1, wrbuf_len(wrbuf_opac), f);
+                    fclose(f);
+                }
+#endif
+            }
             wrbuf_destroy(wrbuf_opac);
         }
     }
@@ -221,7 +234,6 @@ xmlDoc *record_to_xml(struct session_database *sdb, Z_External *rec)
                 return 0;
             }
             
-            yaz_marc_write_using_libxml2(sdb->yaz_marc, 1);
             if (yaz_marc_write_xml(sdb->yaz_marc, &res,
                                    "http://www.loc.gov/MARC21/slim", 0, 0) < 0)
             {
@@ -260,18 +272,88 @@ xmlDoc *record_to_xml(struct session_database *sdb, Z_External *rec)
     return rdoc;
 }
 
-xmlDoc *normalize_record(struct session_database *sdb, Z_External *rec)
+#define MAX_XSLT_ARGS 16
+
+// Add static values from session database settings if applicable
+static void insert_settings_parameters(struct session_database *sdb,
+        struct session *se, char **parms)
+{
+    struct conf_service *service = global_parameters.server->service;
+    int i;
+    int nparms = 0;
+    int offset = 0;
+
+    for (i = 0; i < service->num_metadata; i++)
+    {
+        struct conf_metadata *md = &service->metadata[i];
+        int setting;
+
+        if (md->setting == Metadata_setting_parameter &&
+                (setting = settings_offset(md->name)) > 0)
+        {
+            const char *val = session_setting_oneval(sdb, setting);
+            if (val && nparms < MAX_XSLT_ARGS)
+            {
+                char *buf;
+                int len = strlen(val);
+                buf = nmem_malloc(se->nmem, len + 3);
+                buf[0] = '\'';
+                strcpy(buf + 1, val);
+                buf[len+1] = '\'';
+                buf[len+2] = '\0';
+                parms[offset++] = md->name;
+                parms[offset++] = buf;
+                nparms++;
+            }
+        }
+    }
+    parms[offset] = 0;
+}
+
+// Add static values from session database settings if applicable
+static void insert_settings_values(struct session_database *sdb, xmlDoc *doc)
+{
+    struct conf_service *service = global_parameters.server->service;
+    int i;
+
+    for (i = 0; i < service->num_metadata; i++)
+    {
+        struct conf_metadata *md = &service->metadata[i];
+        int offset;
+
+        if (md->setting == Metadata_setting_postproc &&
+                (offset = settings_offset(md->name)) > 0)
+        {
+            const char *val = session_setting_oneval(sdb, offset);
+            if (val)
+            {
+                xmlNode *r = xmlDocGetRootElement(doc);
+                xmlNode *n = xmlNewTextChild(r, 0, (xmlChar *) "metadata",
+                        (xmlChar *) val);
+                xmlSetProp(n, (xmlChar *) "type", (xmlChar *) md->name);
+            }
+        }
+    }
+}
+
+xmlDoc *normalize_record(struct session_database *sdb, struct session *se,
+        Z_External *rec)
 {
     struct database_retrievalmap *m;
     xmlDoc *rdoc = record_to_xml(sdb, rec);
     if (rdoc)
     {
-        for (m = sdb->map; m; m = m->next){
+        for (m = sdb->map; m; m = m->next)
+        {
             xmlDoc *new = 0;
             
             {
                 xmlNodePtr root = 0;
-                new = xsltApplyStylesheet(m->stylesheet, rdoc, 0);
+                char *parms[MAX_XSLT_ARGS*2+1];
+
+                insert_settings_parameters(sdb, se, parms);
+
+                new = xsltApplyStylesheet(m->stylesheet, rdoc, (const char **) parms);
                 root= xmlDocGetRootElement(new);
                 if (!new || !root || !(root->children))
                 {
@@ -286,6 +368,9 @@ xmlDoc *normalize_record(struct session_database *sdb, Z_External *rec)
             xmlFreeDoc(rdoc);
             rdoc = new;
         }
+
+        insert_settings_values(sdb, rdoc);
+
         if (global_parameters.dump_records)
         {
             FILE *lf = yaz_log_file();
@@ -308,7 +393,7 @@ xmlDoc *normalize_record(struct session_database *sdb, Z_External *rec)
 
 // Retrieve first defined value for 'name' for given database.
 // Will be extended to take into account user associated with session
-char *session_setting_oneval(struct session_database *db, int offset)
+const char *session_setting_oneval(struct session_database *db, int offset)
 {
     if (!db->settings[offset])
         return "";
@@ -320,7 +405,7 @@ char *session_setting_oneval(struct session_database *db, int offset)
 // Initialize YAZ Map structures for MARC-based targets
 static int prepare_yazmarc(struct session_database *sdb)
 {
-    char *s;
+    const char *s;
 
     if (!sdb->settings)
     {
@@ -360,7 +445,7 @@ static int prepare_yazmarc(struct session_database *sdb)
 // setting. However, this is not a realistic use scenario.
 static int prepare_map(struct session *se, struct session_database *sdb)
 {
-   char *s;
+    const char *s;
 
     if (!sdb->settings)
     {
@@ -372,7 +457,30 @@ static int prepare_map(struct session *se, struct session_database *sdb)
         char **stylesheets;
         struct database_retrievalmap **m = &sdb->map;
         int num, i;
+        char auto_stylesheet[256];
 
+        if (!strcmp(s, "auto"))
+        {
+            const char *request_syntax = session_setting_oneval(
+                sdb, PZ_REQUESTSYNTAX);
+            if (request_syntax)
+            {
+                char *cp;
+                yaz_snprintf(auto_stylesheet, sizeof(auto_stylesheet),
+                             "%s.xsl", request_syntax);
+                for (cp = auto_stylesheet; *cp; cp++)
+                {
+                    /* deliberately only consider ASCII */
+                    if (*cp > 32 && *cp < 127)
+                        *cp = tolower(*cp);
+                }
+                s = auto_stylesheet;
+            }
+            else
+            {
+                yaz_log(YLOG_WARN, "No pz:requestsyntax for auto stylesheet");
+            }
+        }
         nmem_strsplit(se->session_nmem, ",", s, &stylesheets, &num);
         for (i = 0; i < num; i++)
         {
@@ -443,13 +551,21 @@ int session_set_watch(struct session *s, int what,
 
 void session_alert_watch(struct session *s, int what)
 {
-    if (!s->watchlist[what].fun)
-        return;
-    http_remove_observer(s->watchlist[what].obs);
-    (*s->watchlist[what].fun)(s->watchlist[what].data);
-    s->watchlist[what].fun = 0;
-    s->watchlist[what].data = 0;
-    s->watchlist[what].obs = 0;
+    if (s->watchlist[what].fun)
+    {
+        /* our watch is no longer associated with http_channel */
+        http_remove_observer(s->watchlist[what].obs);
+        session_watchfun fun = s->watchlist[what].fun;
+        void *data = s->watchlist[what].data;
+
+        /* reset watch before fun is invoked - in case fun wants to set
+           it again */
+        s->watchlist[what].fun = 0;
+        s->watchlist[what].data = 0;
+        s->watchlist[what].obs = 0;
+
+        fun(data);
+    }
 }
 
 //callback for grep_databases
@@ -539,23 +655,22 @@ enum pazpar2_error_code search(struct session *se,
 
     *addinfo = 0;
     nmem_reset(se->nmem);
+    se->relevance = 0;
+    se->total_records = se->total_hits = se->total_merged = 0;
+    se->reclist = 0;
+    se->num_termlists = 0;
     criteria = parse_filter(se->nmem, filter);
     se->requestid++;
     live_channels = select_targets(se, criteria);
     if (live_channels)
     {
         int maxrecs = live_channels * global_parameters.toget;
-        se->num_termlists = 0;
         se->reclist = reclist_create(se->nmem, maxrecs);
-        // This will be initialized in send_search()
-        se->total_records = se->total_hits = se->total_merged = 0;
         se->expected_maxrecs = maxrecs;
     }
     else
         return PAZPAR2_NO_TARGETS;
 
-    se->relevance = 0;
-
     for (cl = se->clients; cl; cl = client_next_in_session(cl))
     {
         if (prepare_session_database(se, client_get_database(cl)) < 0)
@@ -751,15 +866,21 @@ struct session *new_session(NMEM nmem)
     return session;
 }
 
-struct hitsbytarget *hitsbytarget(struct session *se, int *count)
+struct hitsbytarget *hitsbytarget(struct session *se, int *count, NMEM nmem)
 {
-    static struct hitsbytarget res[1000]; // FIXME MM
+    struct hitsbytarget *res = 0;
     struct client *cl;
+    size_t sz = 0;
 
+    for (cl = se->clients; cl; cl = client_next_in_session(cl))
+        sz++;
+
+    res = nmem_malloc(nmem, sizeof(*res) * sz);
     *count = 0;
     for (cl = se->clients; cl; cl = client_next_in_session(cl))
     {
-        char *name = session_setting_oneval(client_get_database(cl), PZ_NAME);
+        const char *name = session_setting_oneval(client_get_database(cl),
+                                                  PZ_NAME);
 
         res[*count].id = client_get_database(cl)->database->url;
         res[*count].name = *name ? name : "Unknown";
@@ -770,7 +891,6 @@ struct hitsbytarget *hitsbytarget(struct session *se, int *count)
         res[*count].connected  = client_get_connection(cl) ? 1 : 0;
         (*count)++;
     }
-
     return res;
 }
 
@@ -982,7 +1102,8 @@ static struct record_metadata *record_metadata_init(
 struct record *ingest_record(struct client *cl, Z_External *rec,
                              int record_no)
 {
-    xmlDoc *xdoc = normalize_record(client_get_database(cl), rec);
+    xmlDoc *xdoc = normalize_record(client_get_database(cl),
+        client_get_session(cl), rec);
     xmlNode *root, *n;
     struct record *record;
     struct record_cluster *cluster;
@@ -1050,7 +1171,7 @@ struct record *ingest_record(struct client *cl, Z_External *rec,
             type = xmlGetProp(n, (xmlChar *) "type");
             value = xmlNodeListGetString(xdoc, n->children, 1);
 
-            if (!type || !value)
+            if (!type || !value || !*value)
                 continue;
 
             md_field_id