593afe3a965484a2202bfdc005f657ed27e29870
[shibboleth/sp.git] / shibsp / impl / XMLRequestMapper.cpp
1 /*
2  *  Copyright 2001-2010 Internet2
3  *
4  * Licensed under the Apache License, Version 2.0 (the "License");
5  * you may not use this file except in compliance with the License.
6  * You may obtain a copy of the License at
7  *
8  *     http://www.apache.org/licenses/LICENSE-2.0
9  *
10  * Unless required by applicable law or agreed to in writing, software
11  * distributed under the License is distributed on an "AS IS" BASIS,
12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13  * See the License for the specific language governing permissions and
14  * limitations under the License.
15  */
16
17 /** XMLRequestMapper.cpp
18  *
19  * XML-based RequestMapper implementation.
20  */
21
22 #include "internal.h"
23 #include "exceptions.h"
24 #include "AccessControl.h"
25 #include "RequestMapper.h"
26 #include "SPRequest.h"
27 #include "util/DOMPropertySet.h"
28 #include "util/SPConstants.h"
29
30 #include <algorithm>
31 #include <xmltooling/util/NDC.h>
32 #include <xmltooling/util/ReloadableXMLFile.h>
33 #include <xmltooling/util/Threads.h>
34 #include <xmltooling/util/XMLHelper.h>
35 #include <xercesc/util/XMLUniDefs.hpp>
36 #include <xercesc/util/regx/RegularExpression.hpp>
37
38 using shibspconstants::SHIB2SPCONFIG_NS;
39 using namespace shibsp;
40 using namespace xmltooling;
41 using namespace std;
42
43 namespace shibsp {
44
45     // Blocks access when an ACL plugin fails to load.
46     class AccessControlDummy : public AccessControl
47     {
48     public:
49         Lockable* lock() {
50             return this;
51         }
52
53         void unlock() {}
54
55         aclresult_t authorized(const SPRequest& request, const Session* session) const {
56             return shib_acl_false;
57         }
58     };
59
60     class Override : public DOMPropertySet, public DOMNodeFilter
61     {
62     public:
63         Override() : m_acl(nullptr) {}
64         Override(const DOMElement* e, Category& log, const Override* base=nullptr);
65         ~Override();
66
67         // Provides filter to exclude special config elements.
68 #ifdef SHIBSP_XERCESC_SHORT_ACCEPTNODE
69         short
70 #else
71         FilterAction
72 #endif
73         acceptNode(const DOMNode* node) const {
74             return FILTER_REJECT;
75         }
76
77         const Override* locate(const HTTPRequest& request) const;
78         AccessControl* getAC() const { return (m_acl ? m_acl : (getParent() ? dynamic_cast<const Override*>(getParent())->getAC() : nullptr)); }
79
80     protected:
81         void loadACL(const DOMElement* e, Category& log);
82
83         map<string,Override*> m_map;
84         vector< pair<RegularExpression*,Override*> > m_regexps;
85         vector< pair< pair<string,RegularExpression*>,Override*> > m_queries;
86
87     private:
88         AccessControl* m_acl;
89     };
90
91     class XMLRequestMapperImpl : public Override
92     {
93     public:
94         XMLRequestMapperImpl(const DOMElement* e, Category& log);
95
96         ~XMLRequestMapperImpl() {
97             if (m_document)
98                 m_document->release();
99         }
100
101         void setDocument(DOMDocument* doc) {
102             m_document = doc;
103         }
104
105         const Override* findOverride(const char* vhost, const HTTPRequest& request) const;
106
107     private:
108         map<string,Override*> m_extras;
109         DOMDocument* m_document;
110     };
111
112 #if defined (_MSC_VER)
113     #pragma warning( push )
114     #pragma warning( disable : 4250 )
115 #endif
116
117     class XMLRequestMapper : public RequestMapper, public ReloadableXMLFile
118     {
119     public:
120         XMLRequestMapper(const DOMElement* e) : ReloadableXMLFile(e,Category::getInstance(SHIBSP_LOGCAT".RequestMapper")), m_impl(nullptr) {
121             background_load();
122         }
123
124         ~XMLRequestMapper() {
125             shutdown();
126             delete m_impl;
127         }
128
129         Settings getSettings(const HTTPRequest& request) const;
130
131     protected:
132         pair<bool,DOMElement*> background_load();
133
134     private:
135         XMLRequestMapperImpl* m_impl;
136     };
137
138 #if defined (_MSC_VER)
139     #pragma warning( pop )
140 #endif
141
142     RequestMapper* SHIBSP_DLLLOCAL XMLRequestMapperFactory(const DOMElement* const & e)
143     {
144         return new XMLRequestMapper(e);
145     }
146
147     static const XMLCh _AccessControl[] =           UNICODE_LITERAL_13(A,c,c,e,s,s,C,o,n,t,r,o,l);
148     static const XMLCh AccessControlProvider[] =    UNICODE_LITERAL_21(A,c,c,e,s,s,C,o,n,t,r,o,l,P,r,o,v,i,d,e,r);
149     static const XMLCh Host[] =                     UNICODE_LITERAL_4(H,o,s,t);
150     static const XMLCh HostRegex[] =                UNICODE_LITERAL_9(H,o,s,t,R,e,g,e,x);
151     static const XMLCh htaccess[] =                 UNICODE_LITERAL_8(h,t,a,c,c,e,s,s);
152     static const XMLCh ignoreCase[] =               UNICODE_LITERAL_10(i,g,n,o,r,e,C,a,s,e);
153     static const XMLCh ignoreOption[] =             UNICODE_LITERAL_1(i);
154     static const XMLCh Path[] =                     UNICODE_LITERAL_4(P,a,t,h);
155     static const XMLCh PathRegex[] =                UNICODE_LITERAL_9(P,a,t,h,R,e,g,e,x);
156     static const XMLCh Query[] =                    UNICODE_LITERAL_5(Q,u,e,r,y);
157     static const XMLCh name[] =                     UNICODE_LITERAL_4(n,a,m,e);
158     static const XMLCh regex[] =                    UNICODE_LITERAL_5(r,e,g,e,x);
159     static const XMLCh _type[] =                    UNICODE_LITERAL_4(t,y,p,e);
160 }
161
162 void SHIBSP_API shibsp::registerRequestMappers()
163 {
164     SPConfig& conf=SPConfig::getConfig();
165     conf.RequestMapperManager.registerFactory(XML_REQUEST_MAPPER, XMLRequestMapperFactory);
166     conf.RequestMapperManager.registerFactory(NATIVE_REQUEST_MAPPER, XMLRequestMapperFactory);
167 }
168
169 RequestMapper::RequestMapper()
170 {
171 }
172
173 RequestMapper::~RequestMapper()
174 {
175 }
176
177 void Override::loadACL(const DOMElement* e, Category& log)
178 {
179     try {
180         const DOMElement* acl=XMLHelper::getFirstChildElement(e,htaccess);
181         if (acl) {
182             log.info("building Apache htaccess AccessControl provider...");
183             m_acl=SPConfig::getConfig().AccessControlManager.newPlugin(HT_ACCESS_CONTROL,acl);
184         }
185         else {
186             acl=XMLHelper::getFirstChildElement(e,_AccessControl);
187             if (acl) {
188                 log.info("building XML-based AccessControl provider...");
189                 m_acl=SPConfig::getConfig().AccessControlManager.newPlugin(XML_ACCESS_CONTROL,acl);
190             }
191             else {
192                 acl=XMLHelper::getFirstChildElement(e,AccessControlProvider);
193                 if (acl) {
194                     string t(XMLHelper::getAttrString(acl, nullptr, _type));
195                     if (!t.empty()) {
196                         log.info("building AccessControl provider of type %s...", t.c_str());
197                         m_acl = SPConfig::getConfig().AccessControlManager.newPlugin(t.c_str(), acl);
198                     }
199                     else {
200                         throw ConfigurationException("<AccessControlProvider> missing type attribute.");
201                     }
202                 }
203             }
204         }
205     }
206     catch (exception& ex) {
207         log.crit("exception building AccessControl provider: %s", ex.what());
208         m_acl = new AccessControlDummy();
209     }
210 }
211
212 Override::Override(const DOMElement* e, Category& log, const Override* base) : m_acl(nullptr)
213 {
214     try {
215         // Load the property set.
216         load(e,nullptr,this);
217         setParent(base);
218
219         // Load any AccessControl provider.
220         loadACL(e,log);
221
222         // Handle nested Paths.
223         DOMElement* path = XMLHelper::getFirstChildElement(e,Path);
224         for (int i=1; path; ++i, path=XMLHelper::getNextSiblingElement(path,Path)) {
225             const XMLCh* n=path->getAttributeNS(nullptr,name);
226
227             // Skip any leading slashes.
228             while (n && *n==chForwardSlash)
229                 n++;
230
231             // Check for empty name.
232             if (!n || !*n) {
233                 log.warn("skipping Path element (%d) with empty name attribute", i);
234                 continue;
235             }
236
237             // Check for an embedded slash.
238             int slash=XMLString::indexOf(n,chForwardSlash);
239             if (slash>0) {
240                 // Copy the first path segment.
241                 XMLCh* namebuf=new XMLCh[slash + 1];
242                 for (int pos=0; pos < slash; pos++)
243                     namebuf[pos]=n[pos];
244                 namebuf[slash]=chNull;
245
246                 // Move past the slash in the original pathname.
247                 n=n+slash+1;
248
249                 // Skip any leading slashes again.
250                 while (*n==chForwardSlash)
251                     n++;
252
253                 if (*n) {
254                     // Create a placeholder Path element for the first path segment and replant under it.
255                     DOMElement* newpath=path->getOwnerDocument()->createElementNS(shibspconstants::SHIB2SPCONFIG_NS,Path);
256                     newpath->setAttributeNS(nullptr,name,namebuf);
257                     path->setAttributeNS(nullptr,name,n);
258                     path->getParentNode()->replaceChild(newpath,path);
259                     newpath->appendChild(path);
260
261                     // Repoint our locals at the new parent.
262                     path=newpath;
263                     n=path->getAttributeNS(nullptr,name);
264                 }
265                 else {
266                     // All we had was a pathname with trailing slash(es), so just reset it without them.
267                     path->setAttributeNS(nullptr,name,namebuf);
268                     n=path->getAttributeNS(nullptr,name);
269                 }
270                 delete[] namebuf;
271             }
272
273             Override* o=new Override(path,log,this);
274             pair<bool,const char*> name=o->getString("name");
275             char* dup=strdup(name.second);
276             for (char* pch=dup; *pch; pch++)
277                 *pch=tolower(*pch);
278             if (m_map.count(dup)) {
279                 log.warn("skipping duplicate Path element (%s)",dup);
280                 free(dup);
281                 delete o;
282                 continue;
283             }
284             m_map[dup]=o;
285             log.debug("added Path mapping (%s)", dup);
286             free(dup);
287         }
288
289         if (!XMLString::equals(e->getLocalName(), PathRegex)) {
290             // Handle nested PathRegexs.
291             path = XMLHelper::getFirstChildElement(e,PathRegex);
292             for (int i=1; path; ++i, path=XMLHelper::getNextSiblingElement(path,PathRegex)) {
293                 const XMLCh* n=path->getAttributeNS(nullptr,regex);
294                 if (!n || !*n) {
295                     log.warn("skipping PathRegex element (%d) with empty regex attribute",i);
296                     continue;
297                 }
298
299                 auto_ptr<Override> o(new Override(path,log,this));
300
301                 const XMLCh* flag=path->getAttributeNS(nullptr,ignoreCase);
302                 try {
303                     auto_ptr<RegularExpression> re(
304                         new RegularExpression(n, (flag && (*flag==chLatin_f || *flag==chDigit_0)) ? &chNull : ignoreOption)
305                         );
306                     m_regexps.push_back(make_pair(re.release(), o.release()));
307                 }
308                 catch (XMLException& ex) {
309                     auto_ptr_char tmp(ex.getMessage());
310                     log.error("caught exception while parsing PathRegex regular expression (%d): %s", i, tmp.get());
311                     throw ConfigurationException("Invalid regular expression in PathRegex element.");
312                 }
313
314                 if (log.isDebugEnabled())
315                     log.debug("added <PathRegex> mapping (%s)", m_regexps.back().second->getString("regex").second);
316             }
317         }
318
319         // Handle nested Querys.
320         path = XMLHelper::getFirstChildElement(e,Query);
321         for (int i=1; path; ++i, path=XMLHelper::getNextSiblingElement(path,Query)) {
322             const XMLCh* n=path->getAttributeNS(nullptr,name);
323             if (!n || !*n) {
324                 log.warn("skipping Query element (%d) with empty name attribute",i);
325                 continue;
326             }
327             auto_ptr_char ntemp(n);
328             const XMLCh* v=path->getAttributeNS(nullptr,regex);
329
330             auto_ptr<Override> o(new Override(path,log,this));
331             try {
332                 RegularExpression* re = nullptr;
333                 if (v && *v)
334                     re = new RegularExpression(v);
335                 m_queries.push_back(make_pair(make_pair(string(ntemp.get()),re), o.release()));
336             }
337             catch (XMLException& ex) {
338                 auto_ptr_char tmp(ex.getMessage());
339                 log.error("caught exception while parsing Query regular expression (%d): %s", i, tmp.get());
340                 throw ConfigurationException("Invalid regular expression in Query element.");
341             }
342
343             log.debug("added <Query> mapping (%s)", ntemp.get());
344         }
345     }
346     catch (exception&) {
347         delete m_acl;
348         for_each(m_map.begin(),m_map.end(),xmltooling::cleanup_pair<string,Override>());
349         for (vector< pair<RegularExpression*,Override*> >::iterator i = m_regexps.begin(); i != m_regexps.end(); ++i) {
350             delete i->first;
351             delete i->second;
352         }
353         for (vector< pair< pair<string,RegularExpression*>,Override*> >::iterator j = m_queries.begin(); j != m_queries.end(); ++j) {
354             delete j->first.second;
355             delete j->second;
356         }
357         throw;
358     }
359 }
360
361 Override::~Override()
362 {
363     delete m_acl;
364     for_each(m_map.begin(),m_map.end(),xmltooling::cleanup_pair<string,Override>());
365     for (vector< pair<RegularExpression*,Override*> >::iterator i = m_regexps.begin(); i != m_regexps.end(); ++i) {
366         delete i->first;
367         delete i->second;
368     }
369     for (vector< pair< pair<string,RegularExpression*>,Override*> >::iterator j = m_queries.begin(); j != m_queries.end(); ++j) {
370         delete j->first.second;
371         delete j->second;
372     }
373 }
374
375 const Override* Override::locate(const HTTPRequest& request) const
376 {
377     // This function is confusing because it's *not* recursive.
378     // The whole path is tokenized and mapped in a loop, so the
379     // path parameter starts with the entire request path and
380     // we can skip the leading slash as irrelevant.
381     const char* path = request.getRequestURI();
382     if (*path == '/')
383         path++;
384
385     // Now we copy the path, chop the query string, and lower case it.
386     char* dup=strdup(path);
387     char* sep=strchr(dup,'?');
388     if (sep)
389         *sep=0;
390     for (char* pch=dup; *pch; pch++)
391         *pch=tolower(*pch);
392
393     // Default is for the current object to provide settings.
394     const Override* o=this;
395
396     // Tokenize the path by segment and try and map each segment.
397 #ifdef HAVE_STRTOK_R
398     char* pos=nullptr;
399     const char* token=strtok_r(dup,"/",&pos);
400 #else
401     const char* token=strtok(dup,"/");
402 #endif
403     while (token) {
404         map<string,Override*>::const_iterator i=o->m_map.find(token);
405         if (i==o->m_map.end())
406             break;  // Once there's no match, we've consumed as much of the path as possible here.
407         // We found a match, so reset the settings pointer.
408         o=i->second;
409
410         // We descended a step down the path, so we need to advance the original
411         // parameter for the regex step later.
412         path += strlen(token);
413         if (*path == '/')
414             path++;
415
416         // Get the next segment, if any.
417 #ifdef HAVE_STRTOK_R
418         token=strtok_r(nullptr,"/",&pos);
419 #else
420         token=strtok(nullptr,"/");
421 #endif
422     }
423
424     free(dup);
425
426     // If there's anything left, we try for a regex match on the rest of the path minus the query string.
427     if (*path) {
428         string path2(path);
429         path2 = path2.substr(0,path2.find('?'));
430
431         for (vector< pair<RegularExpression*,Override*> >::const_iterator re = o->m_regexps.begin(); re != o->m_regexps.end(); ++re) {
432             if (re->first->matches(path2.c_str())) {
433                 o = re->second;
434                 break;
435             }
436         }
437     }
438
439     // Finally, check for query string matches. This is another "unrolled" recursive descent in a loop.
440     bool descended;
441     do {
442         descended = false;
443         for (vector< pair< pair<string,RegularExpression*>,Override*> >::const_iterator q = o->m_queries.begin(); !descended && q != o->m_queries.end(); ++q) {
444             vector<const char*> vals;
445             if (request.getParameters(q->first.first.c_str(), vals)) {
446                 if (q->first.second) {
447                     // We have to match one of the values.
448                     for (vector<const char*>::const_iterator v = vals.begin(); v != vals.end(); ++v) {
449                         if (q->first.second->matches(*v)) {
450                             o = q->second;
451                             descended = true;
452                             break;
453                         }
454                     }
455                 }
456                 else {
457                     // The simple presence of the parameter is sufficient to match.
458                     o = q->second;
459                     descended = true;
460                 }
461             }
462         }
463     } while (descended);
464
465     return o;
466 }
467
468 XMLRequestMapperImpl::XMLRequestMapperImpl(const DOMElement* e, Category& log) : m_document(nullptr)
469 {
470 #ifdef _DEBUG
471     xmltooling::NDC ndc("XMLRequestMapperImpl");
472 #endif
473     static const XMLCh _default[] =    UNICODE_LITERAL_7(d,e,f,a,u,l,t);
474     static const XMLCh _id[] =         UNICODE_LITERAL_2(i,d);
475     static const XMLCh _RequestMap[] = UNICODE_LITERAL_10(R,e,q,u,e,s,t,M,a,p);
476
477     if (!XMLHelper::isNodeNamed(e, SHIB2SPCONFIG_NS, _RequestMap))
478         throw ConfigurationException("XML RequestMapper requires conf:RequestMap at root of configuration.");
479
480     // Load the property set.
481     load(e,nullptr,this);
482
483     // Inject "default" app ID if not explicit.
484     if (!getString("applicationId").first)
485         setProperty("applicationId", "default");
486
487     // Load any AccessControl provider.
488     loadACL(e,log);
489
490     // Loop over the HostRegex elements.
491     const DOMElement* host = XMLHelper::getFirstChildElement(e,HostRegex);
492     for (int i=1; host; ++i, host=XMLHelper::getNextSiblingElement(host,HostRegex)) {
493         const XMLCh* n=host->getAttributeNS(nullptr,regex);
494         if (!n || !*n) {
495             log.warn("Skipping HostRegex element (%d) with empty regex attribute",i);
496             continue;
497         }
498
499         auto_ptr<Override> o(new Override(host,log,this));
500
501         const XMLCh* flag=host->getAttributeNS(nullptr,ignoreCase);
502         try {
503             auto_ptr<RegularExpression> re(
504                 new RegularExpression(n, (flag && (*flag==chLatin_f || *flag==chDigit_0)) ? &chNull : ignoreOption)
505                 );
506             m_regexps.push_back(make_pair(re.release(), o.release()));
507         }
508         catch (XMLException& ex) {
509             auto_ptr_char tmp(ex.getMessage());
510             log.error("caught exception while parsing HostRegex regular expression (%d): %s", i, tmp.get());
511         }
512
513         log.debug("Added <HostRegex> mapping for %s", m_regexps.back().second->getString("regex").second);
514     }
515
516     // Loop over the Host elements.
517     host = XMLHelper::getFirstChildElement(e,Host);
518     for (int i=1; host; ++i, host=XMLHelper::getNextSiblingElement(host,Host)) {
519         const XMLCh* n=host->getAttributeNS(nullptr,name);
520         if (!n || !*n) {
521             log.warn("Skipping Host element (%d) with empty name attribute",i);
522             continue;
523         }
524
525         Override* o=new Override(host,log,this);
526         pair<bool,const char*> name=o->getString("name");
527         pair<bool,const char*> scheme=o->getString("scheme");
528         pair<bool,const char*> port=o->getString("port");
529
530         char* dup=strdup(name.second);
531         for (char* pch=dup; *pch; pch++)
532             *pch=tolower(*pch);
533         auto_ptr<char> dupwrap(dup);
534
535         if (!scheme.first && port.first) {
536             // No scheme, but a port, so assume http.
537             scheme = pair<bool,const char*>(true,"http");
538         }
539         else if (scheme.first && !port.first) {
540             // Scheme, no port, so default it.
541             // XXX Use getservbyname instead?
542             port.first = true;
543             if (!strcmp(scheme.second,"http"))
544                 port.second = "80";
545             else if (!strcmp(scheme.second,"https"))
546                 port.second = "443";
547             else if (!strcmp(scheme.second,"ftp"))
548                 port.second = "21";
549             else if (!strcmp(scheme.second,"ldap"))
550                 port.second = "389";
551             else if (!strcmp(scheme.second,"ldaps"))
552                 port.second = "636";
553         }
554
555         if (scheme.first) {
556             string url(scheme.second);
557             url=url + "://" + dup;
558
559             // Is this the default port?
560             if ((!strcmp(scheme.second,"http") && !strcmp(port.second,"80")) ||
561                 (!strcmp(scheme.second,"https") && !strcmp(port.second,"443")) ||
562                 (!strcmp(scheme.second,"ftp") && !strcmp(port.second,"21")) ||
563                 (!strcmp(scheme.second,"ldap") && !strcmp(port.second,"389")) ||
564                 (!strcmp(scheme.second,"ldaps") && !strcmp(port.second,"636"))) {
565                 // First store a port-less version.
566                 if (m_map.count(url) || m_extras.count(url)) {
567                     log.warn("Skipping duplicate Host element (%s)",url.c_str());
568                     delete o;
569                     continue;
570                 }
571                 m_map[url]=o;
572                 log.debug("Added <Host> mapping for %s",url.c_str());
573
574                 // Now append the port. We use the extras vector, to avoid double freeing the object later.
575                 url=url + ':' + port.second;
576                 m_extras[url]=o;
577                 log.debug("Added <Host> mapping for %s",url.c_str());
578             }
579             else {
580                 url=url + ':' + port.second;
581                 if (m_map.count(url) || m_extras.count(url)) {
582                     log.warn("Skipping duplicate Host element (%s)",url.c_str());
583                     delete o;
584                     continue;
585                 }
586                 m_map[url]=o;
587                 log.debug("Added <Host> mapping for %s",url.c_str());
588             }
589         }
590         else {
591             // No scheme or port, so we enter dual hosts on http:80 and https:443
592             string url("http://");
593             url = url + dup;
594             if (m_map.count(url) || m_extras.count(url)) {
595                 log.warn("Skipping duplicate Host element (%s)",url.c_str());
596                 delete o;
597                 continue;
598             }
599             m_map[url]=o;
600             log.debug("Added <Host> mapping for %s",url.c_str());
601
602             url = url + ":80";
603             if (m_map.count(url) || m_extras.count(url)) {
604                 log.warn("Skipping duplicate Host element (%s)",url.c_str());
605                 continue;
606             }
607             m_extras[url]=o;
608             log.debug("Added <Host> mapping for %s",url.c_str());
609
610             url = "https://";
611             url = url + dup;
612             if (m_map.count(url) || m_extras.count(url)) {
613                 log.warn("Skipping duplicate Host element (%s)",url.c_str());
614                 continue;
615             }
616             m_extras[url]=o;
617             log.debug("Added <Host> mapping for %s",url.c_str());
618
619             url = url + ":443";
620             if (m_map.count(url) || m_extras.count(url)) {
621                 log.warn("Skipping duplicate Host element (%s)",url.c_str());
622                 continue;
623             }
624             m_extras[url]=o;
625             log.debug("Added <Host> mapping for %s",url.c_str());
626         }
627     }
628 }
629
630 const Override* XMLRequestMapperImpl::findOverride(const char* vhost, const HTTPRequest& request) const
631 {
632     const Override* o=nullptr;
633     map<string,Override*>::const_iterator i=m_map.find(vhost);
634     if (i!=m_map.end())
635         o=i->second;
636     else {
637         i=m_extras.find(vhost);
638         if (i!=m_extras.end())
639             o=i->second;
640         else {
641             for (vector< pair<RegularExpression*,Override*> >::const_iterator re = m_regexps.begin(); !o && re != m_regexps.end(); ++re) {
642                 if (re->first->matches(vhost))
643                     o=re->second;
644             }
645         }
646     }
647
648     return o ? o->locate(request) : this;
649 }
650
651 pair<bool,DOMElement*> XMLRequestMapper::background_load()
652 {
653     // Load from source using base class.
654     pair<bool,DOMElement*> raw = ReloadableXMLFile::load();
655
656     // If we own it, wrap it.
657     XercesJanitor<DOMDocument> docjanitor(raw.first ? raw.second->getOwnerDocument() : nullptr);
658
659     XMLRequestMapperImpl* impl = new XMLRequestMapperImpl(raw.second, m_log);
660
661     // If we held the document, transfer it to the impl. If we didn't, it's a no-op.
662     impl->setDocument(docjanitor.release());
663
664     // Perform the swap inside a lock.
665     if (m_lock)
666         m_lock->wrlock();
667     SharedLock locker(m_lock, false);
668     delete m_impl;
669     m_impl = impl;
670
671     return make_pair(false,(DOMElement*)nullptr);
672 }
673
674 RequestMapper::Settings XMLRequestMapper::getSettings(const HTTPRequest& request) const
675 {
676     try {
677         ostringstream vhost;
678         vhost << request.getScheme() << "://" << request.getHostname() << ':' << request.getPort();
679         const Override* o=m_impl->findOverride(vhost.str().c_str(), request);
680         return Settings(o,o->getAC());
681     }
682     catch (XMLException& ex) {
683         auto_ptr_char tmp(ex.getMessage());
684         m_log.error("caught exception while locating content settings: %s", tmp.get());
685         throw ConfigurationException("XML-based RequestMapper failed to retrieve content settings.");
686     }
687 }