metaproxy  1.21.0
filter_query_rewrite.cpp
Go to the documentation of this file.
1 /* This file is part of Metaproxy.
2  Copyright (C) Index Data
3 
4 Metaproxy is free software; you can redistribute it and/or modify it under
5 the terms of the GNU General Public License as published by the Free
6 Software Foundation; either version 2, or (at your option) any later
7 version.
8 
9 Metaproxy is distributed in the hope that it will be useful, but WITHOUT ANY
10 WARRANTY; without even the implied warranty of MERCHANTABILITY or
11 FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
12 for more details.
13 
14 You should have received a copy of the GNU General Public License
15 along with this program; if not, write to the Free Software
16 Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
17 */
18 
19 #include "config.hpp"
20 #include <metaproxy/filter.hpp>
21 #include <metaproxy/package.hpp>
22 
23 #include <metaproxy/util.hpp>
24 #include "filter_query_rewrite.hpp"
25 
26 #include <yaz/log.h>
27 #include <yaz/zgdu.h>
28 #include <yaz/xmlquery.h>
29 #include <yaz/diagbib1.h>
30 #include <yaz/query-charset.h>
31 #include <yaz/tpath.h>
32 
33 #include <libxslt/xsltutils.h>
34 #include <libxslt/transform.h>
35 
36 namespace mp = metaproxy_1;
37 namespace yf = mp::filter;
38 
39 namespace metaproxy_1 {
40  namespace filter {
42  public:
43  Rep();
44  ~Rep();
45  void process(mp::Package &package) const;
46  void configure(const xmlNode * ptr, bool test_only,
47  const char *path);
48  private:
49  xsltStylesheetPtr m_stylesheet;
50  std::string charset_from;
51  std::string charset_to;
52  };
53  }
54 }
55 
56 yf::QueryRewrite::Rep::Rep() : m_stylesheet(0), charset_from("UTF-8")
57 {
58 }
59 
60 yf::QueryRewrite::Rep::~Rep()
61 {
62  if (m_stylesheet)
63  xsltFreeStylesheet(m_stylesheet);
64 }
65 
66 yf::QueryRewrite::QueryRewrite() : m_p(new Rep)
67 {
68 }
69 
70 yf::QueryRewrite::~QueryRewrite()
71 { // must have a destructor because of boost::scoped_ptr
72 }
73 
74 void mp::filter::QueryRewrite::configure(const xmlNode *ptr, bool test_only,
75  const char *path)
76 {
77  m_p->configure(ptr, test_only, path);
78 }
79 
80 void yf::QueryRewrite::process(mp::Package &package) const
81 {
82  m_p->process(package);
83 }
84 
85 void yf::QueryRewrite::Rep::process(mp::Package &package) const
86 {
87  Z_GDU *gdu = package.request().get();
88 
89  if (gdu && gdu->which == Z_GDU_Z3950)
90  {
91  Z_APDU *apdu_req = gdu->u.z3950;
92  if (apdu_req->which == Z_APDU_searchRequest)
93  {
94  int error_code = 0;
95  const char *addinfo = 0;
96  mp::odr odr;
97  Z_SearchRequest *req = apdu_req->u.searchRequest;
98 
99  if (m_stylesheet)
100  {
101  xmlDocPtr doc_input = 0;
102  yaz_query2xml(req->query, &doc_input);
103 
104  if (doc_input)
105  {
106  xmlDocPtr doc_res = xsltApplyStylesheet(m_stylesheet,
107  doc_input, 0);
108  if (!doc_res)
109  {
110  error_code = YAZ_BIB1_MALFORMED_QUERY;
111  addinfo = "XSLT transform failed for query";
112  }
113  else
114  {
115  const xmlNode *root_element = xmlDocGetRootElement(doc_res);
116  yaz_xml2query(root_element, &req->query, odr,
117  &error_code, &addinfo);
118  xmlFreeDoc(doc_res);
119  }
120  xmlFreeDoc(doc_input);
121  }
122  }
123  if (!error_code && charset_to.length() && charset_from.length() &&
124  (req->query->which == Z_Query_type_1
125  || req->query->which == Z_Query_type_101))
126  {
127  yaz_iconv_t cd = yaz_iconv_open(charset_to.c_str(),
128  charset_from.c_str());
129  if (cd)
130  {
131  int r = yaz_query_charset_convert_rpnquery_check(
132  req->query->u.type_1, odr, cd);
133  yaz_iconv_close(cd);
134  if (r)
135  { /* query could not be char converted */
136  error_code = YAZ_BIB1_MALFORMED_QUERY;
137  addinfo = "could not convert query to target charset";
138  }
139  }
140  }
141  if (error_code)
142  {
143  Z_APDU *f_apdu =
144  odr.create_searchResponse(apdu_req, error_code, addinfo);
145  package.response() = f_apdu;
146  return;
147  }
148  package.request() = gdu;
149  }
150  }
151  package.move();
152 }
153 
154 void mp::filter::QueryRewrite::Rep::configure(const xmlNode *ptr,
155  bool test_only, const char *path)
156 {
157  for (ptr = ptr->children; ptr; ptr = ptr->next)
158  {
159  if (ptr->type != XML_ELEMENT_NODE)
160  continue;
161 
162  if (mp::xml::is_element_mp(ptr, "xslt"))
163  {
164  if (m_stylesheet)
165  {
166  throw mp::filter::FilterException
167  ("Only one xslt element allowed in query_rewrite filter");
168  }
169 
170  std::string fname;
171 
172  for (struct _xmlAttr *attr = ptr->properties;
173  attr; attr = attr->next)
174  {
175  mp::xml::check_attribute(attr, "", "stylesheet");
176  fname = mp::xml::get_text(attr);
177  }
178 
179  if (0 == fname.size())
180  throw mp::filter::FilterException
181  ("Attribute <xslt stylesheet=\""
182  + fname
183  + "\"> needs XSLT stylesheet path content"
184  + " in query_rewrite filter");
185 
186  char fullpath[1024];
187  char *cp = yaz_filepath_resolve(fname.c_str(), path, 0, fullpath);
188  if (!cp)
189  {
190  throw mp::filter::FilterException("Cannot read XSLT " + fname);
191  }
192 
193  m_stylesheet = xsltParseStylesheetFile(BAD_CAST cp);
194  if (!m_stylesheet)
195  {
196  throw mp::filter::FilterException
197  ("Failed to read XSLT stylesheet '"
198  + fname
199  + "' in query_rewrite filter");
200  }
201  }
202  else if (mp::xml::is_element_mp(ptr, "charset"))
203  {
204  for (struct _xmlAttr *attr = ptr->properties;
205  attr; attr = attr->next)
206  {
207  if (!strcmp((const char *) attr->name, "from"))
208  {
209  charset_from = mp::xml::get_text(attr);
210  }
211  else if (!strcmp((const char *) attr->name, "to"))
212  {
213  charset_to = mp::xml::get_text(attr);
214  }
215  else
216  throw mp::filter::FilterException
217  ("Invalid attribute inside charset inside "
218  "query_rewrite filter");
219  }
220  }
221  else
222  {
223  throw mp::filter::FilterException
224  ("Bad element "
225  + std::string((const char *) ptr->name)
226  + " in query_rewrite filter");
227  }
228  }
229 }
230 
231 static mp::filter::Base* filter_creator()
232 {
233  return new mp::filter::QueryRewrite;
234 }
235 
236 extern "C" {
237  struct metaproxy_1_filter_struct metaproxy_1_filter_query_rewrite = {
238  0,
239  "query_rewrite",
241  };
242 }
243 
244 /*
245  * Local variables:
246  * c-basic-offset: 4
247  * c-file-style: "Stroustrup"
248  * indent-tabs-mode: nil
249  * End:
250  * vim: shiftwidth=4 tabstop=8 expandtab
251  */
252 
void configure(const xmlNode *ptr, bool test_only, const char *path)
void process(mp::Package &package) const
static mp::filter::Base * filter_creator()
struct metaproxy_1_filter_struct metaproxy_1_filter_query_rewrite