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