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