Odr_oct updates - YAZ 4 and 5.
[metaproxy-moved-to-github.git] / src / filter_http_rewrite.cpp
1 /* This file is part of Metaproxy.
2    Copyright (C) 2005-2013 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 #include <metaproxy/util.hpp>
23 #include "filter_http_rewrite.hpp"
24 #include "html_parser.hpp"
25
26 #include <yaz/zgdu.h>
27 #include <yaz/log.h>
28
29 #include <stack>
30 #include <boost/regex.hpp>
31 #include <boost/lexical_cast.hpp>
32 #include <boost/algorithm/string.hpp>
33
34 #include <map>
35
36 namespace mp = metaproxy_1;
37 namespace yf = mp::filter;
38
39 namespace metaproxy_1 {
40     namespace filter {
41         class HttpRewrite::Replace {
42         public:
43             bool start_anchor;
44             boost::regex re;
45             boost::smatch what;
46             std::string recipe;
47             std::map<int, std::string> group_index;
48             std::string sub_vars(
49                 const std::map<std::string, std::string> & vars) const;
50             void parse_groups(std::string pattern);
51         };
52
53         class HttpRewrite::Rule {
54         public:
55             std::list<Replace> replace_list;
56             bool test_patterns(
57                 std::map<std::string, std::string> &vars,
58                 std::string &txt, bool anchor);
59         };
60         class HttpRewrite::Within {
61         public:
62             boost::regex header;
63             boost::regex attr;
64             boost::regex tag;
65             std::string type;
66             bool reqline;
67             RulePtr rule;
68             bool exec(std::map<std::string, std::string> &vars,
69                       std::string &txt, bool anchor) const;
70         };
71
72         class HttpRewrite::Content {
73         public:
74             std::string type;
75             boost::regex content_re;
76             std::list<Within> within_list;
77             void configure(const xmlNode *ptr,
78                            std::map<std::string, RulePtr > &rules);
79             void quoted_literal(std::string &content,
80                                 std::map<std::string, std::string> &vars) const;
81             void parse(int verbose, std::string &content,
82                        std::map<std::string, std::string> & vars) const;
83         };
84         class HttpRewrite::Phase {
85         public:
86             Phase();
87             int m_verbose;
88             std::list<Content> content_list;
89             void rewrite_reqline(mp::odr & o, Z_HTTP_Request *hreq,
90                 std::map<std::string, std::string> & vars) const;
91             void rewrite_headers(mp::odr & o, Z_HTTP_Header *headers,
92                 std::map<std::string, std::string> & vars) const;
93             void rewrite_body(mp::odr & o,
94                               const char *content_type,
95                               char **content_buf, int *content_len,
96                               std::map<std::string, std::string> & vars) const;
97         };
98         class HttpRewrite::Event : public HTMLParserEvent {
99             void openTagStart(const char *tag, int tag_len);
100             void anyTagEnd(const char *tag, int tag_len, int close_it);
101             void attribute(const char *tag, int tag_len,
102                            const char *attr, int attr_len,
103                            const char *value, int val_len,
104                            const char *sep);
105             void closeTag(const char *tag, int tag_len);
106             void text(const char *value, int len);
107             const Content *m_content;
108             WRBUF m_w;
109             std::stack<std::list<Within>::const_iterator> s_within;
110             std::map<std::string, std::string> &m_vars;
111         public:
112             Event(const Content *p, std::map<std::string, std::string> &vars);
113             ~Event();
114             const char *result();
115         };
116     }
117 }
118
119 yf::HttpRewrite::HttpRewrite() :
120     req_phase(new Phase), res_phase(new Phase)
121 {
122 }
123
124 yf::HttpRewrite::~HttpRewrite()
125 {
126 }
127
128 void yf::HttpRewrite::process(mp::Package & package) const
129 {
130     yaz_log(YLOG_LOG, "HttpRewrite begins....");
131     Z_GDU *gdu = package.request().get();
132     //map of request/response vars
133     std::map<std::string, std::string> vars;
134     //we have an http req
135     if (gdu && gdu->which == Z_GDU_HTTP_Request)
136     {
137         Z_HTTP_Request *hreq = gdu->u.HTTP_Request;
138         mp::odr o;
139         req_phase->rewrite_reqline(o, hreq, vars);
140         yaz_log(YLOG_LOG, ">> Request headers");
141         req_phase->rewrite_headers(o, hreq->headers, vars);
142         req_phase->rewrite_body(o,
143                                 z_HTTP_header_lookup(hreq->headers,
144                                                      "Content-Type"),
145                                 &hreq->content_buf, &hreq->content_len,
146                                 vars);
147         package.request() = gdu;
148     }
149     package.move();
150     gdu = package.response().get();
151     if (gdu && gdu->which == Z_GDU_HTTP_Response)
152     {
153         Z_HTTP_Response *hres = gdu->u.HTTP_Response;
154         yaz_log(YLOG_LOG, "Response code %d", hres->code);
155         mp::odr o;
156         yaz_log(YLOG_LOG, "<< Respose headers");
157         res_phase->rewrite_headers(o, hres->headers, vars);
158         res_phase->rewrite_body(o,
159                                 z_HTTP_header_lookup(hres->headers,
160                                                      "Content-Type"),
161                                 &hres->content_buf, &hres->content_len,
162                                 vars);
163         package.response() = gdu;
164     }
165 }
166
167 void yf::HttpRewrite::Phase::rewrite_reqline (mp::odr & o,
168         Z_HTTP_Request *hreq,
169         std::map<std::string, std::string> & vars) const
170 {
171     //rewrite the request line
172     std::string path;
173     if (strstr(hreq->path, "http://") == hreq->path)
174     {
175         yaz_log(YLOG_LOG, "Path in the method line is absolute, "
176             "possibly a proxy request");
177         path += hreq->path;
178     }
179     else
180     {
181         //TODO what about proto
182         const char *host = z_HTTP_header_lookup(hreq->headers, "Host");
183         if (!host)
184             return;
185
186         path += "http://";
187         path += host;
188         path += hreq->path;
189     }
190
191     std::list<Content>::const_iterator cit = content_list.begin();
192     for (; cit != content_list.end(); cit++)
193         if (cit->type == "headers")
194             break;
195
196     if (cit == content_list.end())
197         return;
198
199     std::list<Within>::const_iterator it = cit->within_list.begin();
200     for (; it != cit->within_list.end(); it++)
201         if (it->reqline)
202         {
203             yaz_log(YLOG_LOG, "Proxy request URL is %s", path.c_str());
204             if (it->exec(vars, path, true))
205             {
206                 yaz_log(YLOG_LOG, "Rewritten request URL is %s", path.c_str());
207                 hreq->path = odr_strdup(o, path.c_str());
208             }
209         }
210 }
211
212 void yf::HttpRewrite::Phase::rewrite_headers(mp::odr & o,
213         Z_HTTP_Header *headers,
214         std::map<std::string, std::string> & vars) const
215 {
216     std::list<Content>::const_iterator cit = content_list.begin();
217     for (; cit != content_list.end(); cit++)
218         if (cit->type == "headers")
219             break;
220
221     if (cit == content_list.end())
222         return;
223
224     for (Z_HTTP_Header *header = headers; header; header = header->next)
225     {
226         std::list<Within>::const_iterator it = cit->within_list.begin();
227         for (; it != cit->within_list.end(); it++)
228         {
229             if (!it->header.empty() &&
230                 regex_match(header->name, it->header))
231             {
232                 // Match and replace only the header value
233                 std::string hval(header->value);
234                 if (it->exec(vars, hval, true))
235                 {
236                     header->value = odr_strdup(o, hval.c_str());
237                 }
238             }
239         }
240     }
241 }
242
243 void yf::HttpRewrite::Phase::rewrite_body(
244     mp::odr &o,
245     const char *content_type,
246     char **content_buf,
247     int *content_len,
248     std::map<std::string, std::string> & vars) const
249 {
250     if (*content_len == 0)
251         return;
252     if (!content_type) {
253         yaz_log(YLOG_LOG, "rewrite_body: null content_type, can not rewrite");
254         return;
255     }
256     std::list<Content>::const_iterator cit = content_list.begin();
257     for (; cit != content_list.end(); cit++)
258     {
259         yaz_log(YLOG_LOG, "rewrite_body: content_type=%s type=%s",
260                 content_type, cit->type.c_str());
261         if (cit->type != "headers"
262             && regex_match(content_type, cit->content_re))
263             break;
264     }
265     if (cit == content_list.end()) {
266         yaz_log(YLOG_LOG,"rewrite_body: No content rule matched %s, not rewriting",
267                 content_type );  
268         return;
269     }
270
271     int i;
272     for (i = 0; i < *content_len; i++)
273         if ((*content_buf)[i] == 0) {
274             yaz_log(YLOG_LOG,"rewrite_body: Looks like binary stuff, not rewriting");
275             return;  // binary content. skip
276         }
277
278     std::string content(*content_buf, *content_len);
279     cit->parse(m_verbose, content, vars);
280     *content_buf = odr_strdup(o, content.c_str());
281     *content_len = strlen(*content_buf);
282 }
283
284 yf::HttpRewrite::Event::Event(const Content *p,
285                               std::map<std::string, std::string> & vars
286     ) : m_content(p), m_vars(vars)
287 {
288     m_w = wrbuf_alloc();
289 }
290
291 yf::HttpRewrite::Event::~Event()
292 {
293     wrbuf_destroy(m_w);
294 }
295
296 const char *yf::HttpRewrite::Event::result()
297 {
298     return wrbuf_cstr(m_w);
299 }
300
301 void yf::HttpRewrite::Event::openTagStart(const char *tag, int tag_len)
302 {
303     wrbuf_putc(m_w, '<');
304     wrbuf_write(m_w, tag, tag_len);
305
306     std::string t(tag, tag_len);
307     std::list<Within>::const_iterator it = m_content->within_list.begin();
308     for (; it != m_content->within_list.end(); it++)
309     {
310         if (!it->tag.empty() && regex_match(t, it->tag))
311         {
312             if (!it->attr.empty() && regex_match("#text", it->attr))
313             {
314                 s_within.push(it);
315                 return;
316             }
317         }
318     }
319 }
320
321 void yf::HttpRewrite::Event::anyTagEnd(const char *tag, int tag_len,
322                                        int close_it)
323 {
324     if (close_it)
325     {
326         if (!s_within.empty())
327         {
328             std::list<Within>::const_iterator it = s_within.top();
329             std::string t(tag, tag_len);
330             if (regex_match(t, it->tag))
331                 s_within.pop();
332         }
333     }
334     if (close_it)
335         wrbuf_putc(m_w, '/');
336     wrbuf_putc(m_w, '>');
337 }
338
339 void yf::HttpRewrite::Event::attribute(const char *tag, int tag_len,
340                                        const char *attr, int attr_len,
341                                        const char *value, int val_len,
342                                        const char *sep)
343 {
344     std::list<Within>::const_iterator it = m_content->within_list.begin();
345     bool subst = false;
346
347     for (; it != m_content->within_list.end(); it++)
348     {
349         std::string t(tag, tag_len);
350         if (it->tag.empty() || regex_match(t, it->tag))
351         {
352             std::string a(attr, attr_len);
353             if (!it->attr.empty() && regex_match(a, it->attr))
354                 subst = true;
355         }
356         if (subst)
357             break;
358     }
359
360     wrbuf_putc(m_w, ' ');
361     wrbuf_write(m_w, attr, attr_len);
362     if (value)
363     {
364         wrbuf_puts(m_w, "=");
365         wrbuf_puts(m_w, sep);
366
367         std::string output;
368         if (subst)
369         {
370             std::string s(value, val_len);
371             it->exec(m_vars, s, true);
372             wrbuf_puts(m_w, s.c_str());
373         }
374         else
375             wrbuf_write(m_w, value, val_len);
376         wrbuf_puts(m_w, sep);
377     }
378 }
379
380 void yf::HttpRewrite::Event::closeTag(const char *tag, int tag_len)
381 {
382     if (!s_within.empty())
383     {
384         std::list<Within>::const_iterator it = s_within.top();
385         std::string t(tag, tag_len);
386         if (regex_match(t, it->tag))
387             s_within.pop();
388     }
389     wrbuf_puts(m_w, "</");
390     wrbuf_write(m_w, tag, tag_len);
391 }
392
393 void yf::HttpRewrite::Event::text(const char *value, int len)
394 {
395     std::list<Within>::const_iterator it = m_content->within_list.end();
396     if (!s_within.empty())
397         it = s_within.top();
398     if (it != m_content->within_list.end())
399     {
400         std::string s(value, len);
401         it->exec(m_vars, s, false);
402         wrbuf_puts(m_w, s.c_str());
403     }
404     else
405         wrbuf_write(m_w, value, len);
406 }
407
408 static bool embed_quoted_literal(
409     std::string &content,
410     std::map<std::string, std::string> &vars,
411     mp::filter::HttpRewrite::RulePtr ruleptr,
412     bool html_context)
413 {
414     bool replace = false;
415     std::string res;
416     const char *cp = content.c_str();
417     const char *cp0 = cp;
418     while (*cp)
419     {
420         if (html_context && !strncmp(cp, "&quot;", 6))
421         {
422             cp += 6;
423             res.append(cp0, cp - cp0);
424             cp0 = cp;
425             while (*cp)
426             {
427                 if (!strncmp(cp, "&quot;", 6))
428                     break;
429                 if (*cp == '\n')
430                     break;
431                 cp++;
432             }
433             if (!*cp)
434                 break;
435             std::string s(cp0, cp - cp0);
436             if (ruleptr->test_patterns(vars, s, true))
437                 replace = true;
438             cp0 = cp;
439             res.append(s);
440         }
441         else if (*cp == '"' || *cp == '\'')
442         {
443             int m = *cp;
444             cp++;
445             res.append(cp0, cp - cp0);
446             cp0 = cp;
447             while (*cp)
448             {
449                 if (cp[-1] != '\\' && *cp == m)
450                     break;
451                 if (*cp == '\n')
452                     break;
453                 cp++;
454             }
455             if (!*cp)
456                 break;
457             std::string s(cp0, cp - cp0);
458             if (ruleptr->test_patterns(vars, s, true))
459                 replace = true;
460             cp0 = cp;
461             res.append(s);
462         }
463         else if (*cp == '/' && cp[1] == '/')
464         {
465             while (cp[1] && cp[1] != '\n')
466                 cp++;
467         }
468         cp++;
469     }
470     res.append(cp0, cp - cp0);
471     content = res;
472     return replace;
473 }
474
475 bool yf::HttpRewrite::Within::exec(
476     std::map<std::string, std::string> & vars,
477     std::string & txt, bool anchor) const
478 {
479     if (type == "quoted-literal")
480     {
481         return embed_quoted_literal(txt, vars, rule, true);
482     }
483     else
484     {
485         return rule->test_patterns(vars, txt, anchor);
486     }
487 }
488
489 bool yf::HttpRewrite::Rule::test_patterns(
490     std::map<std::string, std::string> & vars,
491     std::string & txt, bool anchor)
492 {
493     bool replaces = false;
494     bool first = anchor;
495     std::string out;
496     std::string::const_iterator start, end;
497     start = txt.begin();
498     end = txt.end();
499     while (1)
500     {
501         std::list<Replace>::iterator bit = replace_list.end();
502         {
503             std::string::const_iterator best_pos = txt.end();
504             std::list<Replace>::iterator it = replace_list.begin();
505             for (; it != replace_list.end(); it++)
506             {
507                 if (it->start_anchor && !first)
508                     continue;
509                 if (regex_search(start, end, it->what, it->re))
510                 {
511                     if (it->what[0].first < best_pos)
512                     {
513                         best_pos = it->what[0].first;
514                         bit = it;
515                     }
516                 }
517             }
518             if (bit == replace_list.end())
519                 break;
520         }
521         first = false;
522         replaces = true;
523         size_t i;
524         for (i = 1; i < bit->what.size(); ++i)
525         {
526             //check if the group is named
527             std::map<int, std::string>::const_iterator git
528                 = bit->group_index.find(i);
529             if (git != bit->group_index.end())
530             {   //it is
531                 vars[git->second] = bit->what[i];
532             }
533
534         }
535         //prepare replacement string
536         std::string rvalue = bit->sub_vars(vars);
537         yaz_log(YLOG_LOG, "! Rewritten '%s' to '%s'",
538                 bit->what.str(0).c_str(), rvalue.c_str());
539         out.append(start, bit->what[0].first);
540         out.append(rvalue);
541         start = bit->what[0].second; //move search forward
542     }
543     out.append(start, end);
544     txt = out;
545     return replaces;
546 }
547
548 void yf::HttpRewrite::Replace::parse_groups(std::string pattern)
549 {
550     int gnum = 0;
551     bool esc = false;
552     const std::string &str = pattern;
553     std::string res;
554     start_anchor = str[0] == '^';
555     yaz_log(YLOG_LOG, "Parsing groups from '%s'", str.c_str());
556     for (size_t i = 0; i < str.size(); ++i)
557     {
558         res += str[i];
559         if (!esc && str[i] == '\\')
560         {
561             esc = true;
562             continue;
563         }
564         if (!esc && str[i] == '(') //group starts
565         {
566             gnum++;
567             if (i+1 < str.size() && str[i+1] == '?') //group with attrs
568             {
569                 i++;
570                 if (i+1 < str.size() && str[i+1] == ':') //non-capturing
571                 {
572                     if (gnum > 0) gnum--;
573                     res += str[i];
574                     i++;
575                     res += str[i];
576                     continue;
577                 }
578                 if (i+1 < str.size() && str[i+1] == 'P') //optional, python
579                     i++;
580                 if (i+1 < str.size() && str[i+1] == '<') //named
581                 {
582                     i++;
583                     std::string gname;
584                     bool term = false;
585                     while (++i < str.size())
586                     {
587                         if (str[i] == '>') { term = true; break; }
588                         if (!isalnum(str[i]))
589                             throw mp::filter::FilterException
590                                 ("Only alphanumeric chars allowed, found "
591                                  " in '"
592                                  + str
593                                  + "' at "
594                                  + boost::lexical_cast<std::string>(i));
595                         gname += str[i];
596                     }
597                     if (!term)
598                         throw mp::filter::FilterException
599                             ("Unterminated group name '" + gname
600                              + " in '" + str +"'");
601                     group_index[gnum] = gname;
602                     yaz_log(YLOG_LOG, "Found named group '%s' at $%d",
603                             gname.c_str(), gnum);
604                 }
605             }
606         }
607         esc = false;
608     }
609     re = res;
610 }
611
612 std::string yf::HttpRewrite::Replace::sub_vars(
613     const std::map<std::string, std::string> & vars) const
614 {
615     std::string out;
616     bool esc = false;
617     const std::string & in = recipe;
618     for (size_t i = 0; i < in.size(); ++i)
619     {
620         if (!esc && in[i] == '\\')
621         {
622             esc = true;
623             continue;
624         }
625         if (!esc && in[i] == '$') //var
626         {
627             if (i+1 < in.size() && in[i+1] == '{') //ref prefix
628             {
629                 ++i;
630                 std::string name;
631                 bool term = false;
632                 while (++i < in.size())
633                 {
634                     if (in[i] == '}') { term = true; break; }
635                     name += in[i];
636                 }
637                 if (!term) throw mp::filter::FilterException
638                     ("Unterminated var ref in '"+in+"' at "
639                      + boost::lexical_cast<std::string>(i));
640                 std::map<std::string, std::string>::const_iterator it
641                     = vars.find(name);
642                 if (it != vars.end())
643                 {
644                     out += it->second;
645                 }
646             }
647             else
648             {
649                 throw mp::filter::FilterException
650                     ("Malformed or trimmed var ref in '"
651                      +in+"' at "+boost::lexical_cast<std::string>(i));
652             }
653             continue;
654         }
655         //passthru
656         out += in[i];
657         esc = false;
658     }
659     return out;
660 }
661
662 yf::HttpRewrite::Phase::Phase() : m_verbose(0)
663 {
664 }
665
666 void yf::HttpRewrite::Content::parse(
667     int verbose,
668     std::string &content,
669     std::map<std::string, std::string> &vars) const
670 {
671     if (type == "html")
672     {
673         HTMLParser parser;
674         Event ev(this, vars);
675
676         parser.set_verbose(verbose);
677
678         parser.parse(ev, content.c_str());
679         content = ev.result();
680     }
681     if (type == "quoted-literal")
682     {
683         quoted_literal(content, vars);
684     }
685 }
686
687 void yf::HttpRewrite::Content::quoted_literal(
688     std::string &content,
689     std::map<std::string, std::string> &vars) const
690 {
691     std::list<Within>::const_iterator it = within_list.begin();
692     if (it != within_list.end())
693         embed_quoted_literal(content, vars, it->rule, false);
694 }
695
696 void yf::HttpRewrite::Content::configure(
697     const xmlNode *ptr, std::map<std::string, RulePtr > &rules)
698 {
699     for (; ptr; ptr = ptr->next)
700     {
701         if (ptr->type != XML_ELEMENT_NODE)
702             continue;
703         if (!strcmp((const char *) ptr->name, "within"))
704         {
705             static const char *names[7] =
706                 { "header", "attr", "tag", "rule", "reqline", "type", 0 };
707             std::string values[6];
708             mp::xml::parse_attr(ptr, names, values);
709             Within w;
710             if (values[0].length() > 0)
711                 w.header.assign(values[0], boost::regex_constants::icase);
712             if (values[1].length() > 0)
713                 w.attr.assign(values[1], boost::regex_constants::icase);
714             if (values[2].length() > 0)
715                 w.tag.assign(values[2], boost::regex_constants::icase);
716
717             std::vector<std::string> rulenames;
718             boost::split(rulenames, values[3], boost::is_any_of(","));
719             if (rulenames.size() == 0)
720             {
721                 throw mp::filter::FilterException
722                     ("Empty rule in '" + values[3] +
723                      "' in http_rewrite filter");
724             }
725             else if (rulenames.size() == 1)
726             {
727                 std::map<std::string,RulePtr>::const_iterator it =
728                     rules.find(rulenames[0]);
729                 if (it == rules.end())
730                     throw mp::filter::FilterException
731                         ("Reference to non-existing rule '" + rulenames[0] +
732                          "' in http_rewrite filter");
733                 w.rule = it->second;
734
735             }
736             else
737             {
738                 RulePtr rule(new Rule);
739                 size_t i;
740                 for (i = 0; i < rulenames.size(); i++)
741                 {
742                     std::map<std::string,RulePtr>::const_iterator it =
743                         rules.find(rulenames[i]);
744                     if (it == rules.end())
745                         throw mp::filter::FilterException
746                             ("Reference to non-existing rule '" + rulenames[i] +
747                              "' in http_rewrite filter");
748                     RulePtr subRule = it->second;
749                     std::list<Replace>::iterator rit =
750                         subRule->replace_list.begin();
751                     for (; rit != subRule->replace_list.end(); rit++)
752                         rule->replace_list.push_back(*rit);
753                 }
754                 w.rule = rule;
755             }
756             w.reqline = values[4] == "1";
757             w.type = values[5];
758             if (w.type.empty() || w.type == "quoted-literal")
759                 ;
760             else
761                 throw mp::filter::FilterException
762                     ("within type must be quoted-literal or none in "
763                      " in http_rewrite filter");
764             within_list.push_back(w);
765         }
766     }
767 }
768
769 void yf::HttpRewrite::configure_phase(const xmlNode *ptr, Phase &phase)
770 {
771     static const char *names[2] = { "verbose", 0 };
772     std::string values[1];
773     values[0] = "0";
774     mp::xml::parse_attr(ptr, names, values);
775
776     phase.m_verbose = atoi(values[0].c_str());
777
778     std::map<std::string, RulePtr > rules;
779     for (ptr = ptr->children; ptr; ptr = ptr->next)
780     {
781         if (ptr->type != XML_ELEMENT_NODE)
782             continue;
783         else if (!strcmp((const char *) ptr->name, "rule"))
784         {
785             static const char *names[2] = { "name", 0 };
786             std::string values[1];
787             values[0] = "default";
788             mp::xml::parse_attr(ptr, names, values);
789
790             RulePtr rule(new Rule);
791             for (xmlNode *p = ptr->children; p; p = p->next)
792             {
793                 if (p->type != XML_ELEMENT_NODE)
794                     continue;
795                 if (!strcmp((const char *) p->name, "rewrite"))
796                 {
797                     Replace replace;
798                     std::string from;
799                     const struct _xmlAttr *attr;
800                     for (attr = p->properties; attr; attr = attr->next)
801                     {
802                         if (!strcmp((const char *) attr->name,  "from"))
803                             from = mp::xml::get_text(attr->children);
804                         else if (!strcmp((const char *) attr->name,  "to"))
805                             replace.recipe = mp::xml::get_text(attr->children);
806                         else
807                             throw mp::filter::FilterException
808                                 ("Bad attribute "
809                                  + std::string((const char *) attr->name)
810                                  + " in rewrite section of http_rewrite");
811                     }
812                     yaz_log(YLOG_LOG, "Found rewrite rule from '%s' to '%s'",
813                             from.c_str(), replace.recipe.c_str());
814                     if (!from.empty())
815                     {
816                         replace.parse_groups(from);
817                         rule->replace_list.push_back(replace);
818                     }
819                 }
820                 else
821                     throw mp::filter::FilterException
822                         ("Bad element "
823                          + std::string((const char *) p->name)
824                          + " in http_rewrite filter");
825             }
826             rules[values[0]] = rule;
827         }
828         else if (!strcmp((const char *) ptr->name, "content"))
829         {
830             static const char *names[3] =
831                 { "type", "mime", 0 };
832             std::string values[2];
833             mp::xml::parse_attr(ptr, names, values);
834             if (values[0].empty())
835             {
836                     throw mp::filter::FilterException
837                         ("Missing attribute, type for for element "
838                          + std::string((const char *) ptr->name)
839                          + " in http_rewrite filter");
840             }
841             Content c;
842
843             c.type = values[0];
844             if (!values[1].empty())
845                 c.content_re.assign(values[1], boost::regex::icase);
846             c.configure(ptr->children, rules);
847             phase.content_list.push_back(c);
848         }
849         else
850         {
851             throw mp::filter::FilterException
852                 ("Bad element "
853                  + std::string((const char *) ptr->name)
854                  + " in http_rewrite filter");
855         }
856     }
857 }
858
859 void yf::HttpRewrite::configure(const xmlNode * ptr, bool test_only,
860         const char *path)
861 {
862     for (ptr = ptr->children; ptr; ptr = ptr->next)
863     {
864         if (ptr->type != XML_ELEMENT_NODE)
865             continue;
866         else if (!strcmp((const char *) ptr->name, "request"))
867         {
868             configure_phase(ptr, *req_phase);
869         }
870         else if (!strcmp((const char *) ptr->name, "response"))
871         {
872             configure_phase(ptr, *res_phase);
873         }
874         else
875         {
876             throw mp::filter::FilterException
877                 ("Bad element "
878                  + std::string((const char *) ptr->name)
879                  + " in http_rewrite1 filter");
880         }
881     }
882 }
883
884 static mp::filter::Base* filter_creator()
885 {
886     return new mp::filter::HttpRewrite;
887 }
888
889 extern "C" {
890     struct metaproxy_1_filter_struct metaproxy_1_filter_http_rewrite = {
891         0,
892         "http_rewrite",
893         filter_creator
894     };
895 }
896
897
898 /*
899  * Local variables:
900  * c-basic-offset: 4
901  * c-file-style: "Stroustrup"
902  * indent-tabs-mode: nil
903  * End:
904  * vim: shiftwidth=4 tabstop=8 expandtab
905  */
906