Extended command 'record' so that XML representation of original record
authorAdam Dickmeiss <adam@indexdata.dk>
Fri, 15 Jun 2007 06:45:39 +0000 (06:45 +0000)
committerAdam Dickmeiss <adam@indexdata.dk>
Fri, 15 Jun 2007 06:45:39 +0000 (06:45 +0000)
can be returned. Bug #1172. If parameter 'offset' is given for command
'record' the original record is returned and offset is the record offset
within cluster (merged) record, where 0=first, 1=second, etc.. If no record
at offset is given diagnostic RECORD_FAIL is returned.  When offset
is given, the value of parameter 'syntax' and 'esn' specifies the
record syntax and element set respectively. If any of these are omitted
the present request also omits them. The code to convert from 'raw' to
XML is combined into one function record_to_xml. Note that OPAC records
are not converted to XML yet.

src/client.c
src/client.h
src/http_command.c
src/logic.c
src/pazpar2.h

index c4147fa..37598fe 100644 (file)
@@ -1,4 +1,4 @@
-/* $Id: client.c,v 1.8 2007-06-06 11:56:35 marc Exp $
+/* $Id: client.c,v 1.9 2007-06-15 06:45:39 adam Exp $
    Copyright (c) 2006-2007, Index Data.
 
 This file is part of Pazpar2.
@@ -45,9 +45,7 @@ Free Software Foundation, 59 Temple Place - Suite 330, Boston, MA
 #include <yaz/nmem.h>
 #include <yaz/query-charset.h>
 #include <yaz/querytowrbuf.h>
-#if YAZ_VERSIONL >= 0x020163
 #include <yaz/oid_db.h>
-#endif
 
 #if HAVE_CONFIG_H
 #include "cconfig.h"
@@ -78,9 +76,20 @@ struct client {
     int requestid;            // ID of current outstanding request
     int diagnostic;
     enum client_state state;
+    struct show_raw *show_raw;
     struct client *next;     // next client in session or next in free list
 };
 
+struct show_raw {
+    int active; // whether this request has been sent to the server
+    int position;
+    char *syntax;
+    char *esn;
+    void (*error_handler)(void *data, const char *addinfo);
+    void (*record_handler)(void *data, const char *buf, size_t sz);
+    void *data;
+};
+
 static const char *client_states[] = {
     "Client_Connecting",
     "Client_Connected",
@@ -117,9 +126,12 @@ void client_set_state(struct client *cl, enum client_state st)
     cl->state = st;
 }
 
+static void client_show_raw_error(struct client *cl, const char *addinfo);
+
 // Close connection and set state to error
 void client_fatal(struct client *cl)
 {
+    client_show_raw_error(cl, "client connection failure");
     yaz_log(YLOG_WARN, "Fatal error from %s", client_get_url(cl));
     connection_destroy(cl->connection);
     cl->state = Client_Error;
@@ -150,6 +162,100 @@ void client_set_requestid(struct client *cl, int id)
     cl->requestid = id;
 }
 
+int client_show_raw(struct client *cl, int position,
+                    const char *syntax, const char *esn,
+                    void *data,
+                    void (*error_handler)(void *data, const char *addinfo),
+                    void (*record_handler)(void *data, const char *buf,
+                                           size_t sz))
+{
+    if (cl->show_raw)
+        return -1;
+    cl->show_raw = xmalloc(sizeof(*cl->show_raw));
+    cl->show_raw->position = position;
+    cl->show_raw->active = 0;
+    cl->show_raw->data = data;
+    cl->show_raw->error_handler = error_handler;
+    cl->show_raw->record_handler = record_handler;
+    if (syntax)
+        cl->show_raw->syntax = xstrdup(syntax);
+    else
+        cl->show_raw->syntax = 0;
+    if (esn)
+        cl->show_raw->esn = xstrdup(esn);
+    else
+        cl->show_raw->esn = 0;
+    client_continue(cl);
+    return 0;
+}
+
+static void client_show_raw_error(struct client *cl, const char *addinfo)
+{
+    if (cl->show_raw)
+    {
+        cl->show_raw->error_handler(cl->show_raw->data, addinfo);
+        xfree(cl->show_raw);
+        cl->show_raw = 0;
+    }
+}
+
+static void client_show_raw_cancel(struct client *cl)
+{
+    if (cl->show_raw)
+    {
+        cl->show_raw->error_handler(cl->show_raw->data, "cancel");
+        xfree(cl->show_raw);
+        cl->show_raw = 0;
+    }
+}
+
+void client_send_raw_present(struct client *cl)
+{
+    Z_APDU *a = zget_APDU(global_parameters.odr_out, Z_APDU_presentRequest);
+    int toget = 1;
+    int start = cl->show_raw->position;
+
+    assert(cl->show_raw);
+
+    yaz_log(YLOG_LOG, "Trying to present %d record(s) from %d",
+            toget, start);
+
+    a->u.presentRequest->resultSetStartPoint = &start;
+    a->u.presentRequest->numberOfRecordsRequested = &toget;
+
+    if (cl->show_raw->syntax)  // syntax is optional
+        a->u.presentRequest->preferredRecordSyntax =
+            yaz_string_to_oid_odr(yaz_oid_std(),
+                                  CLASS_RECSYN, cl->show_raw->syntax,
+                                  global_parameters.odr_out);
+    if (cl->show_raw->esn)  // element set is optional
+    {
+        Z_ElementSetNames *elementSetNames =
+            odr_malloc(global_parameters.odr_out, sizeof(*elementSetNames));
+        Z_RecordComposition *compo = 
+            odr_malloc(global_parameters.odr_out, sizeof(*compo));
+        a->u.presentRequest->recordComposition = compo;
+
+        compo->which = Z_RecordComp_simple;
+        compo->u.simple = elementSetNames;
+
+        elementSetNames->which = Z_ElementSetNames_generic;
+        elementSetNames->u.generic = 
+            odr_strdup(global_parameters.odr_out, cl->show_raw->esn);
+    }
+    if (send_apdu(cl, a) >= 0)
+    {
+        cl->show_raw->active = 1;
+       cl->state = Client_Presenting;
+    }
+    else
+    {
+        client_show_raw_error(cl, "send_apdu failed");
+        cl->state = Client_Error;
+    }
+    odr_reset(global_parameters.odr_out);
+}
+
 void client_send_present(struct client *cl)
 {
     struct session_database *sdb = client_get_database(cl);
@@ -164,25 +270,18 @@ void client_send_present(struct client *cl)
     if (toget > cl->hits - cl->records)
        toget = cl->hits - cl->records;
 
-    yaz_log(YLOG_DEBUG, "Trying to present %d records\n", toget);
+    yaz_log(YLOG_DEBUG, "Trying to present %d record(s) from %d",
+            toget, start);
 
     a->u.presentRequest->resultSetStartPoint = &start;
     a->u.presentRequest->numberOfRecordsRequested = &toget;
 
-    a->u.presentRequest->resultSetId = "Default";
-
     if ((recsyn = session_setting_oneval(sdb, PZ_REQUESTSYNTAX)))
     {
-#if YAZ_VERSIONL >= 0x020163
         a->u.presentRequest->preferredRecordSyntax =
             yaz_string_to_oid_odr(yaz_oid_std(),
                                   CLASS_RECSYN, recsyn,
                                   global_parameters.odr_out);
-#else
-        a->u.presentRequest->preferredRecordSyntax =
-            yaz_str_to_z3950oid(global_parameters.odr_out,
-                                CLASS_RECSYN, recsyn);
-#endif
     }
 
     if (send_apdu(cl, a) >= 0)
@@ -209,6 +308,7 @@ void client_send_search(struct client *cl)
 
     yaz_log(YLOG_DEBUG, "Sending search to %s", sdb->database->url);
 
+    
     // constructing RPN query
     a->u.searchRequest->query = zquery = odr_malloc(global_parameters.odr_out,
                                                     sizeof(Z_Query));
@@ -240,16 +340,10 @@ void client_send_search(struct client *cl)
     {
         if ((recsyn = session_setting_oneval(sdb, PZ_REQUESTSYNTAX)))
         {
-#if YAZ_VERSIONL >= 0x020163
             a->u.searchRequest->preferredRecordSyntax =
                 yaz_string_to_oid_odr(yaz_oid_std(),
                                       CLASS_RECSYN, recsyn,
                                       global_parameters.odr_out);
-#else
-            a->u.searchRequest->preferredRecordSyntax =
-                yaz_str_to_z3950oid(global_parameters.odr_out,
-                                    CLASS_RECSYN, recsyn);
-#endif
         }
         a->u.searchRequest->smallSetUpperBound = &ssub;
         a->u.searchRequest->largeSetLowerBound = &lslb;
@@ -303,6 +397,49 @@ void client_init_response(struct client *cl, Z_APDU *a)
 }
 
 
+static void ingest_raw_records(struct client *cl, Z_Records *r)
+{
+    Z_NamePlusRecordList *rlist;
+    Z_NamePlusRecord *npr;
+    xmlDoc *doc;
+    xmlChar *buf_out;
+    int len_out;
+    if (r->which != Z_Records_DBOSD)
+    {
+        client_show_raw_error(cl, "non-surrogate diagnostics");
+        return;
+    }
+
+    rlist = r->u.databaseOrSurDiagnostics;
+    if (rlist->num_records != 1 || !rlist->records || !rlist->records[0])
+    {
+        client_show_raw_error(cl, "no records");
+        return;
+    }
+    npr = rlist->records[0];
+    if (npr->which != Z_NamePlusRecord_databaseRecord)
+    {
+        client_show_raw_error(cl, "surrogate diagnostic");
+        return;
+    }
+
+    doc = record_to_xml(client_get_database(cl), npr->u.databaseRecord);
+    if (!doc)
+    {
+        client_show_raw_error(cl, "unable to convert record to xml");
+        return;
+    }
+
+    xmlDocDumpMemory(doc, &buf_out, &len_out);
+
+    cl->show_raw->record_handler(cl->show_raw->data,
+                                 (const char *) buf_out, len_out);
+    
+    xmlFreeDoc(doc);
+    xfree(cl->show_raw);
+    cl->show_raw = 0;
+}
+
 static void ingest_records(struct client *cl, Z_Records *r)
 {
 #if USE_TIMING
@@ -397,6 +534,7 @@ void client_present_response(struct client *cl, Z_APDU *a)
                     cl->database->database->url);
             cl->diagnostic = *recs->u.nonSurrogateDiagnostic->condition;
             cl->state = Client_Error;
+            client_show_raw_error(cl, "non surrogate diagnostics");
         }
     }
 
@@ -404,7 +542,15 @@ void client_present_response(struct client *cl, Z_APDU *a)
     {
         yaz_log(YLOG_DEBUG, "Good Present response %s",
                 cl->database->database->url);
-        ingest_records(cl, r->records);
+
+        // we can mix show raw and normal show ..
+        if (cl->show_raw && cl->show_raw->active)
+        {
+            cl->show_raw->active = 0; // no longer active
+            ingest_raw_records(cl, r->records);
+        }
+        else
+            ingest_records(cl, r->records);
         cl->state = Client_Idle;
     }
     else if (*r->presentStatus) 
@@ -412,6 +558,7 @@ void client_present_response(struct client *cl, Z_APDU *a)
         yaz_log(YLOG_WARN, "Bad Present response %s",
                 cl->database->database->url);
         cl->state = Client_Error;
+        client_show_raw_error(cl, "bad present response");
     }
 }
 
@@ -464,16 +611,10 @@ static void init_zproxy(struct client *cl, Z_InitRequest *req)
     char *zproxy = session_setting_oneval(sdb, PZ_ZPROXY);
 
     if (*zproxy)
-#if YAZ_VERSIONL >= 0x020163
         yaz_oi_set_string_oid(&req->otherInfo,
                               global_parameters.odr_out,
                               yaz_oid_userinfo_proxy,
                               1, ztarget);
-#else
-        yaz_oi_set_string_oidval(&req->otherInfo,
-                                 global_parameters.odr_out, VAL_PROXY,
-                                 1, ztarget);
-#endif
 }
 
 
@@ -496,8 +637,6 @@ static void client_init_request(struct client *cl)
     init_authentication(cl, a->u.initRequest);
     init_zproxy(cl, a->u.initRequest);
 
-
-
     if (send_apdu(cl, a) >= 0)
        client_set_state(cl, Client_Initializing);
     else
@@ -515,8 +654,14 @@ void client_continue(struct client *cl)
     {
         struct session *se = client_get_session(cl);
         if (cl->requestid != se->requestid && cl->pquery) {
+            // we'll have to abort this because result set is to be deleted
+            client_show_raw_cancel(cl);   
             client_send_search(cl);
         }
+        else if (cl->show_raw)
+        {
+            client_send_raw_present(cl);
+        }
         else if (cl->hits > 0 && cl->records < global_parameters.toget &&
             cl->records < cl->hits) {
             client_send_present(cl);
@@ -544,6 +689,7 @@ struct client *client_create(void)
     r->requestid = -1;
     r->diagnostic = 0;
     r->state = Client_Disconnected;
+    r->show_raw = 0;
     r->next = 0;
     return r;
 }
index eb2abf1..f01e980 100644 (file)
@@ -1,4 +1,4 @@
-/* $Id: client.h,v 1.1 2007-04-23 21:05:23 adam Exp $
+/* $Id: client.h,v 1.2 2007-06-15 06:45:39 adam Exp $
    Copyright (c) 2006-2007, Index Data.
 
 This file is part of Pazpar2.
@@ -43,6 +43,13 @@ enum client_state
     Client_Stopped
 };
 
+int client_show_raw(struct client *cl, int position,
+                    const char *syntax, const char *esn,
+                    void *data,
+                    void (*error_handler)(void *data, const char *addinfo),
+                    void (*record_handler)(void *data, const char *buf,
+                                           size_t sz));
+
 const char *client_get_state_str(struct client *cl);
 enum client_state client_get_state(struct client *cl);
 void client_set_state(struct client *cl, enum client_state st);
index 3128fec..ffc9b81 100644 (file)
@@ -1,4 +1,4 @@
-/* $Id: http_command.c,v 1.52 2007-06-13 13:04:34 adam Exp $
+/* $Id: http_command.c,v 1.53 2007-06-15 06:45:39 adam Exp $
    Copyright (c) 2006-2007, Index Data.
 
 This file is part of Pazpar2.
@@ -20,7 +20,7 @@ Free Software Foundation, 59 Temple Place - Suite 330, Boston, MA
  */
 
 /*
- * $Id: http_command.c,v 1.52 2007-06-13 13:04:34 adam Exp $
+ * $Id: http_command.c,v 1.53 2007-06-15 06:45:39 adam Exp $
  */
 
 #include <stdio.h>
@@ -119,6 +119,9 @@ static const char *get_msg(enum pazpar2_error_code code)
         { PAZPAR2_RECORD_MISSING, "Record missing"},
         { PAZPAR2_NO_TARGETS, "No targets"},
         { PAZPAR2_CONFIG_TARGET, "Target cannot be configured"},
+        { PAZPAR2_RECORD_FAIL, "Record command failed"},
+        { PAZPAR2_NOT_IMPLEMENTED, "Not implemented"},
+        { PAZPAR2_LAST_ERROR, "Last error"},
         { 0, 0 }
     };
     int i = 0;
@@ -484,6 +487,24 @@ static void write_subrecord(struct record *r, WRBUF w,
     wrbuf_puts(w, "</location>\n");
 }
 
+static void show_raw_record_error(void *data, const char *addinfo)
+{
+    struct http_channel *c = (struct http_channel *) data;
+    struct http_response *rs = c->response;
+
+    error(rs, PAZPAR2_NOT_IMPLEMENTED, addinfo);
+}
+
+static void show_raw_record_ok(void *data, const char *buf, size_t sz)
+{
+    struct http_channel *c = (struct http_channel *) data;
+    struct http_response *rs = c->response;
+
+    wrbuf_write(c->wrbuf, buf, sz);
+    rs->payload = nmem_strdup(c->nmem, wrbuf_cstr(c->wrbuf));
+    http_send_response(c);
+}
+
 static void cmd_record(struct http_channel *c)
 {
     struct http_response *rs = c->response;
@@ -492,7 +513,9 @@ static void cmd_record(struct http_channel *c)
     struct record_cluster *rec;
     struct record *r;
     struct conf_service *service = global_parameters.server->service;
-    char *idstr = http_argbyname(rq, "id");
+    const char *idstr = http_argbyname(rq, "id");
+    const char *offsetstr = http_argbyname(rq, "offset");
+    
     int id;
 
     if (!s)
@@ -509,14 +532,41 @@ static void cmd_record(struct http_channel *c)
         error(rs, PAZPAR2_RECORD_MISSING, idstr);
         return;
     }
-    wrbuf_puts(c->wrbuf, "<record>\n");
-    wrbuf_printf(c->wrbuf, "<recid>%d</recid>\n", rec->recid);
-    write_metadata(c->wrbuf, service, rec->metadata, 1);
-    for (r = rec->records; r; r = r->next)
-        write_subrecord(r, c->wrbuf, service, 1);
-    wrbuf_puts(c->wrbuf, "</record>\n");
-    rs->payload = nmem_strdup(c->nmem, wrbuf_cstr(c->wrbuf));
-    http_send_response(c);
+    if (offsetstr)
+    {
+        int offset = atoi(offsetstr);
+        const char *syntax = http_argbyname(rq, "syntax");
+        const char *esn = http_argbyname(rq, "esn");
+        int i;
+        struct record*r = rec->records;
+
+        for (i = 0; i < offset && r; r = r->next, i++)
+            ;
+        if (!r)
+        {
+            error(rs, PAZPAR2_RECORD_FAIL, "no record at offset given");
+            return;
+        }
+        if (client_show_raw(r->client, r->position, syntax, esn, 
+                            c /* data */,
+                            show_raw_record_error,
+                            show_raw_record_ok))
+        {
+            error(rs, PAZPAR2_RECORD_FAIL, "invalid parameters");
+            return;
+        }
+    }
+    else
+    {
+        wrbuf_puts(c->wrbuf, "<record>\n");
+        wrbuf_printf(c->wrbuf, "<recid>%d</recid>\n", rec->recid);
+        write_metadata(c->wrbuf, service, rec->metadata, 1);
+        for (r = rec->records; r; r = r->next)
+            write_subrecord(r, c->wrbuf, service, 1);
+        wrbuf_puts(c->wrbuf, "</record>\n");
+        rs->payload = nmem_strdup(c->nmem, wrbuf_cstr(c->wrbuf));
+        http_send_response(c);
+    }
 }
 
 static void show_records(struct http_channel *c, int active)
index d0a92a8..05527bc 100644 (file)
@@ -1,4 +1,4 @@
-/* $Id: logic.c,v 1.43 2007-06-13 21:29:04 adam Exp $
+/* $Id: logic.c,v 1.44 2007-06-15 06:45:39 adam Exp $
    Copyright (c) 2006-2007, Index Data.
 
 This file is part of Pazpar2.
@@ -45,9 +45,7 @@ Free Software Foundation, 59 Temple Place - Suite 330, Boston, MA
 #include <yaz/nmem.h>
 #include <yaz/query-charset.h>
 #include <yaz/querytowrbuf.h>
-#if YAZ_VERSIONL >= 0x020163
 #include <yaz/oid_db.h>
-#endif
 
 #if HAVE_CONFIG_H
 #include "cconfig.h"
@@ -152,9 +150,8 @@ static void add_facet(struct session *s, const char *type, const char *value)
     termlist_insert(s->termlists[i].termlist, value);
 }
 
-xmlDoc *normalize_record(struct session_database *sdb, Z_External *rec)
+xmlDoc *record_to_xml(struct session_database *sdb, Z_External *rec)
 {
-    struct database_retrievalmap *m;
     struct database *db = sdb->database;
     xmlDoc *rdoc = 0;
     const Odr_oid *oid = rec->direct_reference;
@@ -233,36 +230,45 @@ xmlDoc *normalize_record(struct session_database *sdb, Z_External *rec)
         xmlDocDump(stderr, rdoc);
 #endif
     }
+    return rdoc;
+}
 
-    for (m = sdb->map; m; m = m->next){
-        xmlDoc *new = 0;
-
-        {
-            xmlNodePtr root = 0;
-            new = xsltApplyStylesheet(m->stylesheet, rdoc, 0);
-            root= xmlDocGetRootElement(new);
-        if (!new || !root || !(root->children))
-        {
-            yaz_log(YLOG_WARN, "XSLT transformation failed from %s",
-                    sdb->database->url);
-            xmlFreeDoc(new);
+xmlDoc *normalize_record(struct session_database *sdb, Z_External *rec)
+{
+    struct database_retrievalmap *m;
+    xmlDoc *rdoc = record_to_xml(sdb, rec);
+    if (rdoc)
+    {
+        for (m = sdb->map; m; m = m->next){
+            xmlDoc *new = 0;
+            
+            {
+                xmlNodePtr root = 0;
+                new = xsltApplyStylesheet(m->stylesheet, rdoc, 0);
+                root= xmlDocGetRootElement(new);
+                if (!new || !root || !(root->children))
+                {
+                    yaz_log(YLOG_WARN, "XSLT transformation failed from %s",
+                            sdb->database->url);
+                    xmlFreeDoc(new);
+                    xmlFreeDoc(rdoc);
+                    return 0;
+                }
+            }
+            
             xmlFreeDoc(rdoc);
-            return 0;
-        }
+            rdoc = new;
         }
-   
-        xmlFreeDoc(rdoc);
-        rdoc = new;
-    }
-    if (global_parameters.dump_records)
-    {
-        fprintf(stderr, "Record from %s\n----------------\n", 
-                sdb->database->url);
+        if (global_parameters.dump_records)
+        {
+            fprintf(stderr, "Record from %s\n----------------\n", 
+                    sdb->database->url);
 #if LIBXML_VERSION >= 20600
-        xmlDocFormatDump(stderr, rdoc, 1);
+            xmlDocFormatDump(stderr, rdoc, 1);
 #else
-        xmlDocDump(stderr, rdoc);
+            xmlDocDump(stderr, rdoc);
 #endif
+        }
     }
     return rdoc;
 }
index 0e69b2c..0776874 100644 (file)
@@ -1,4 +1,4 @@
-/* $Id: pazpar2.h,v 1.40 2007-06-13 08:04:03 adam Exp $
+/* $Id: pazpar2.h,v 1.41 2007-06-15 06:45:39 adam Exp $
    Copyright (c) 2006-2007, Index Data.
 
 This file is part of Pazpar2.
@@ -55,7 +55,11 @@ enum pazpar2_error_code {
     PAZPAR2_HITCOUNTS_FAILED,
     PAZPAR2_RECORD_MISSING,
     PAZPAR2_NO_TARGETS,
-    PAZPAR2_CONFIG_TARGET
+    PAZPAR2_CONFIG_TARGET,
+    PAZPAR2_RECORD_FAIL,
+    PAZPAR2_NOT_IMPLEMENTED,
+
+    PAZPAR2_LAST_ERROR
 };
 
 // Represents a (virtual) database on a host
@@ -185,6 +189,7 @@ void pazpar2_event_loop(void);
 int host_getaddrinfo(struct host *host);
 
 xmlDoc *normalize_record(struct session_database *sdb, Z_External *rec);
+xmlDoc *record_to_xml(struct session_database *sdb, Z_External *rec);
 
 struct record *ingest_record(struct client *cl, Z_External *rec,
                              int record_no);