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