GPL v2.
[metaproxy-moved-to-github.git] / src / filter_query_rewrite.cpp
1 /* $Id: filter_query_rewrite.cpp,v 1.11 2007-05-09 21:23:09 adam Exp $
2    Copyright (c) 2005-2007, Index Data.
3
4 This file is part of Metaproxy.
5
6 Metaproxy is free software; you can redistribute it and/or modify it under
7 the terms of the GNU General Public License as published by the Free
8 Software Foundation; either version 2, or (at your option) any later
9 version.
10
11 Metaproxy is distributed in the hope that it will be useful, but WITHOUT ANY
12 WARRANTY; without even the implied warranty of MERCHANTABILITY or
13 FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
14 for more details.
15
16 You should have received a copy of the GNU General Public License
17 along with Metaproxy; see the file LICENSE.  If not, write to the
18 Free Software Foundation, 59 Temple Place - Suite 330, Boston, MA
19 02111-1307, USA.
20  */
21
22 #include "config.hpp"
23 #include "filter.hpp"
24 #include "package.hpp"
25
26 #include "util.hpp"
27 #include "xmlutil.hpp"
28 #include "filter_query_rewrite.hpp"
29
30 #include <yaz/zgdu.h>
31 #include <yaz/xmlquery.h>
32 #include <yaz/diagbib1.h>
33
34 #include <libxslt/xsltutils.h>
35 #include <libxslt/transform.h>
36
37 namespace mp = metaproxy_1;
38 namespace yf = mp::filter;
39
40 namespace metaproxy_1 {
41     namespace filter {
42         class QueryRewrite::Rep {
43         public:
44             Rep();
45             ~Rep();
46             void process(mp::Package &package) const;
47             void configure(const xmlNode * ptr);
48         private:
49             xsltStylesheetPtr m_stylesheet;
50         };
51     }
52 }
53
54 yf::QueryRewrite::Rep::Rep()
55 {
56     m_stylesheet = 0;
57 }
58
59 yf::QueryRewrite::Rep::~Rep()
60 {
61     if (m_stylesheet)
62         xsltFreeStylesheet(m_stylesheet);
63 }
64
65 yf::QueryRewrite::QueryRewrite() : m_p(new Rep)
66 {
67 }
68
69 yf::QueryRewrite::~QueryRewrite()
70 {  // must have a destructor because of boost::scoped_ptr
71 }
72
73 void yf::QueryRewrite::process(mp::Package &package) const
74 {
75     m_p->process(package);
76 }
77
78 void mp::filter::QueryRewrite::configure(const xmlNode *ptr)
79 {
80     m_p->configure(ptr);
81 }
82
83 void yf::QueryRewrite::Rep::process(mp::Package &package) const
84 {
85     Z_GDU *gdu = package.request().get();
86     
87     if (gdu && gdu->which == Z_GDU_Z3950)
88     {
89         Z_APDU *apdu_req = gdu->u.z3950;
90         if (apdu_req->which == Z_APDU_searchRequest)
91         {
92             int error_code = 0;
93             const char *addinfo = 0;
94             mp::odr odr;
95             Z_SearchRequest *req = apdu_req->u.searchRequest;
96             
97             xmlDocPtr doc_input = 0;
98             yaz_query2xml(req->query, &doc_input);
99             
100             if (!doc_input)
101             {
102                 error_code = YAZ_BIB1_MALFORMED_QUERY;
103                 addinfo = "converion from Query to XML failed";
104             }
105             else
106             {
107                 if (m_stylesheet)
108                 {
109                     xmlDocPtr doc_res = xsltApplyStylesheet(m_stylesheet,
110                                                             doc_input, 0);
111                     if (!doc_res)
112                     {
113                         error_code = YAZ_BIB1_MALFORMED_QUERY;
114                         addinfo = "XSLT transform failed for query";
115                     }
116                     else
117                     {
118                         const xmlNode *root_element = xmlDocGetRootElement(doc_res);
119                         yaz_xml2query(root_element, &req->query, odr,
120                                       &error_code, &addinfo);
121                         xmlFreeDoc(doc_res);
122                     }
123                 }
124                 xmlFreeDoc(doc_input);
125             }
126             package.request() = gdu;
127             if (error_code)
128             {
129                 Z_APDU *f_apdu = 
130                     odr.create_searchResponse(apdu_req, error_code, addinfo);
131                 package.response() = f_apdu;
132                 return;
133             }
134         } 
135     }
136     package.move();
137 }
138
139 void mp::filter::QueryRewrite::Rep::configure(const xmlNode *ptr)
140 {
141     for (ptr = ptr->children; ptr; ptr = ptr->next)
142     {
143         if (ptr->type != XML_ELEMENT_NODE)
144             continue;
145
146         if (mp::xml::check_element_mp(ptr, "xslt"))
147         {
148             if (m_stylesheet)
149             {
150                 throw mp::filter::FilterException
151                     ("Only one xslt element allowed in query_rewrite filter");
152             }
153
154             std::string fname;// = mp::xml::get_text(ptr);
155
156             for (struct _xmlAttr *attr = ptr->properties; 
157                  attr; attr = attr->next)
158             {
159                 mp::xml::check_attribute(attr, "", "stylesheet");
160                 fname = mp::xml::get_text(attr);            
161             }
162
163             if (0 == fname.size())
164                 throw mp::filter::FilterException
165                     ("Attribute <xslt stylesheet=\"" 
166                      + fname
167                      + "\"> needs XSLT stylesheet path content"
168                      + " in query_rewrite filter");
169             
170             m_stylesheet = xsltParseStylesheetFile(BAD_CAST fname.c_str());
171             if (!m_stylesheet)
172             {
173                 throw mp::filter::FilterException
174                     ("Failed to read XSLT stylesheet '" 
175                      + fname
176                      + "' in query_rewrite filter");
177             }
178         }
179         else
180         {
181             throw mp::filter::FilterException
182                 ("Bad element " 
183                  + std::string((const char *) ptr->name)
184                  + " in query_rewrite filter");
185         }
186     }
187 }
188
189 static mp::filter::Base* filter_creator()
190 {
191     return new mp::filter::QueryRewrite;
192 }
193
194 extern "C" {
195     struct metaproxy_1_filter_struct metaproxy_1_filter_query_rewrite = {
196         0,
197         "query_rewrite",
198         filter_creator
199     };
200 }
201
202 /*
203  * Local variables:
204  * c-basic-offset: 4
205  * indent-tabs-mode: nil
206  * c-file-style: "stroustrup"
207  * End:
208  * vim: shiftwidth=4 tabstop=8 expandtab
209  */