Old config class ported, all config files now loading with new parser.
[shibboleth/sp.git] / shib-target / XMLRequestMapper.cpp
index 2d81093..621feab 100644 (file)
@@ -1,50 +1,17 @@
-/* 
- * The Shibboleth License, Version 1. 
- * Copyright (c) 2002 
- * University Corporation for Advanced Internet Development, Inc. 
- * All rights reserved
+/*
+ *  Copyright 2001-2005 Internet2
  * 
- * 
- * Redistribution and use in source and binary forms, with or without 
- * modification, are permitted provided that the following conditions are met:
- * 
- * Redistributions of source code must retain the above copyright notice, this 
- * list of conditions and the following disclaimer.
- * 
- * Redistributions in binary form must reproduce the above copyright notice, 
- * this list of conditions and the following disclaimer in the documentation 
- * and/or other materials provided with the distribution, if any, must include 
- * the following acknowledgment: "This product includes software developed by 
- * the University Corporation for Advanced Internet Development 
- * <http://www.ucaid.edu>Internet2 Project. Alternately, this acknowledegement 
- * may appear in the software itself, if and wherever such third-party 
- * acknowledgments normally appear.
- * 
- * Neither the name of Shibboleth nor the names of its contributors, nor 
- * Internet2, nor the University Corporation for Advanced Internet Development, 
- * Inc., nor UCAID may be used to endorse or promote products derived from this 
- * software without specific prior written permission. For written permission, 
- * please contact shibboleth@shibboleth.org
- * 
- * Products derived from this software may not be called Shibboleth, Internet2, 
- * UCAID, or the University Corporation for Advanced Internet Development, nor 
- * may Shibboleth appear in their name, without prior written permission of the 
- * University Corporation for Advanced Internet Development.
- * 
- * 
- * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" 
- * AND WITH ALL FAULTS. ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT 
- * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY, FITNESS FOR A 
- * PARTICULAR PURPOSE, AND NON-INFRINGEMENT ARE DISCLAIMED AND THE ENTIRE RISK 
- * OF SATISFACTORY QUALITY, PERFORMANCE, ACCURACY, AND EFFORT IS WITH LICENSEE. 
- * IN NO EVENT SHALL THE COPYRIGHT OWNER, CONTRIBUTORS OR THE UNIVERSITY 
- * CORPORATION FOR ADVANCED INTERNET DEVELOPMENT, INC. BE LIABLE FOR ANY DIRECT, 
- * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES 
- * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; 
- * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND 
- * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 
- * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS 
- * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
  */
 
 /* XMLRequestMapper.cpp - an XML-based map of URLs to application names and settings
 
 #include "internal.h"
 
-#include <log4cpp/Category.hh>
+#include <algorithm>
+#include <shibsp/DOMPropertySet.h>
+#include <xmltooling/util/ReloadableXMLFile.h>
+#include <xmltooling/util/XMLHelper.h>
 
-using namespace std;
-using namespace log4cpp;
-using namespace saml;
-using namespace shibboleth;
+using namespace shibsp;
 using namespace shibtarget;
+using namespace xmltooling;
+using namespace log4cpp;
+using namespace std;
 
 namespace shibtarget {
 
-    class Override : public XMLPropertySet, public DOMNodeFilter
+    // Blocks access when an ACL plugin fails to load. 
+    class AccessControlDummy : public IAccessControl
+    {
+    public:
+        Lockable* lock() {
+            return this;
+        }
+        
+        void unlock() {}
+    
+        bool authorized(ShibTarget* st, ISessionCacheEntry* entry) const {
+            return false;
+        }
+    };
+
+    class Override : public DOMPropertySet, public DOMNodeFilter
     {
     public:
         Override() : m_base(NULL), m_acl(NULL) {}
         Override(const DOMElement* e, Category& log, const Override* base=NULL);
         ~Override();
-        IAccessControl* m_acl;
 
-        // IPropertySet
+        // PropertySet
         pair<bool,bool> getBool(const char* name, const char* ns=NULL) const;
         pair<bool,const char*> getString(const char* name, const char* ns=NULL) const;
         pair<bool,const XMLCh*> getXMLString(const char* name, const char* ns=NULL) const;
         pair<bool,unsigned int> getUnsignedInt(const char* name, const char* ns=NULL) const;
         pair<bool,int> getInt(const char* name, const char* ns=NULL) const;
-        const IPropertySet* getPropertySet(const char* name, const char* ns="urn:mace:shibboleth:target:config:1.0") const;
+        const PropertySet* getPropertySet(const char* name, const char* ns="urn:mace:shibboleth:target:config:1.0") const;
         
         // Provides filter to exclude special config elements.
         short acceptNode(const DOMNode* node) const;
 
         const Override* locate(const char* path) const;
+        IAccessControl* getAC() const { return (m_acl ? m_acl : (m_base ? m_base->getAC() : NULL)); }
         
     protected:
         void loadACL(const DOMElement* e, Category& log);
@@ -95,62 +80,85 @@ namespace shibtarget {
     
     private:
         const Override* m_base;
+        IAccessControl* m_acl;
     };
 
-    class XMLRequestMapperImpl : public ReloadableXMLFileImpl, public Override
+    class XMLRequestMapperImpl : public Override
     {
     public:
-        XMLRequestMapperImpl(const char* pathname) : ReloadableXMLFileImpl(pathname) { init(); }
-        XMLRequestMapperImpl(const DOMElement* e) : ReloadableXMLFileImpl(e) { init(); }
-        void init();
-        ~XMLRequestMapperImpl() {}
+        XMLRequestMapperImpl(const DOMElement* e, Category& log);
+
+        ~XMLRequestMapperImpl() {
+            if (m_document)
+                m_document->release();
+        }
+
+        void setDocument(DOMDocument* doc) {
+            m_document = doc;
+        }
     
         const Override* findOverride(const char* vhost, const char* path) const;
-        Category* log;
 
     private:    
         map<string,Override*> m_extras;
+        DOMDocument* m_document;
     };
 
-    // An implementation of the URL->application mapping API using an XML file
+#if defined (_MSC_VER)
+    #pragma warning( push )
+    #pragma warning( disable : 4250 )
+#endif
+
     class XMLRequestMapper : public IRequestMapper, public ReloadableXMLFile
     {
     public:
-        XMLRequestMapper(const DOMElement* e) : ReloadableXMLFile(e) {}
-        ~XMLRequestMapper() {}
+        XMLRequestMapper(const DOMElement* e)
+                : ReloadableXMLFile(e), m_impl(NULL), m_log(Category::getInstance(SHIBT_LOGCAT".RequestMapper")) {
+            load();
+        }
 
-        virtual Settings getSettingsFromURL(const char* url) const;
-        virtual Settings getSettingsFromParsedURL(
-            const char* scheme, const char* hostname, unsigned int port, const char* path=NULL
-            ) const;
+        ~XMLRequestMapper() {
+            delete m_impl;
+        }
+
+        virtual Settings getSettings(ShibTarget* st) const;
 
     protected:
-        virtual ReloadableXMLFileImpl* newImplementation(const char* pathname, bool first=true) const;
-        virtual ReloadableXMLFileImpl* newImplementation(const DOMElement* e, bool first=true) const;
+        pair<bool,DOMElement*> load();
+
+    private:
+        XMLRequestMapperImpl* m_impl;
+        Category& m_log;
     };
+
+#if defined (_MSC_VER)
+    #pragma warning( pop )
+#endif
+
+    static const XMLCh AccessControl[] =            UNICODE_LITERAL_13(A,c,c,e,s,s,C,o,n,t,r,o,l);
+    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);
+    static const XMLCh htaccess[] =                 UNICODE_LITERAL_8(h,t,a,c,c,e,s,s);
+    static const XMLCh Host[] =                     UNICODE_LITERAL_4(H,o,s,t);
+    static const XMLCh Path[] =                     UNICODE_LITERAL_4(P,a,t,h);
+    static const XMLCh name[] =                     UNICODE_LITERAL_4(n,a,m,e);
+    static const XMLCh type[] =                     UNICODE_LITERAL_4(t,y,p,e);
 }
 
-IPlugIn* XMLRequestMapFactory(const DOMElement* e)
+saml::IPlugIn* XMLRequestMapFactory(const DOMElement* e)
 {
-    XMLRequestMapper* m=new XMLRequestMapper(e);
-    try {
-        m->getImplementation();
-    }
-    catch (...) {
-        delete m;
-        throw;
-    }
-    return m;
+    return new XMLRequestMapper(e);
 }
 
 short Override::acceptNode(const DOMNode* node) const
 {
-    if (XMLString::compareString(node->getNamespaceURI(),ShibTargetConfig::SHIBTARGET_NS))
+    if (!XMLString::equals(node->getNamespaceURI(),shibspconstants::SHIB1SPCONFIG_NS))
         return FILTER_ACCEPT;
     const XMLCh* name=node->getLocalName();
-    if (XMLString::compareString(name,SHIBT_L(AccessControlProvider)) ||
-        XMLString::compareString(name,SHIBT_L(Host)) ||
-        XMLString::compareString(name,SHIBT_L(Path)))
+    if (XMLString::equals(name,Host) ||
+        XMLString::equals(name,Path) ||
+        XMLString::equals(name,AccessControl) ||
+        XMLString::equals(name,htaccess) ||
+        XMLString::equals(name,AccessControlProvider))
         return FILTER_REJECT;
 
     return FILTER_ACCEPT;
@@ -158,30 +166,42 @@ short Override::acceptNode(const DOMNode* node) const
 
 void Override::loadACL(const DOMElement* e, Category& log)
 {
-    IPlugIn* plugin=NULL;
-    const DOMElement* acl=saml::XML::getFirstChildElement(e,ShibTargetConfig::SHIBTARGET_NS,SHIBT_L(htaccess));
-    if (acl) {
-        log.info("building htaccess provider...");
-        plugin=SAMLConfig::getConfig().m_plugMgr.newPlugin(shibtarget::XML::htaccessType,acl);
-    }
-    else {
-        acl=saml::XML::getFirstChildElement(e,ShibTargetConfig::SHIBTARGET_NS,SHIBT_L(AccessControlProvider));
+    try {
+        saml::IPlugIn* plugin=NULL;
+        const DOMElement* acl=XMLHelper::getFirstChildElement(e,htaccess);
         if (acl) {
-            auto_ptr_char type(acl->getAttributeNS(NULL,SHIBT_L(type)));
-            log.info("building Access Control provider of type %s...",type.get());
-            plugin=SAMLConfig::getConfig().m_plugMgr.newPlugin(type.get(),acl);
+            log.info("building Apache htaccess provider...");
+            plugin=saml::SAMLConfig::getConfig().getPlugMgr().newPlugin(HTACCESS_ACCESSCONTROL,acl);
         }
-    }
-    if (plugin) {
-        IAccessControl* acl=dynamic_cast<IAccessControl*>(plugin);
-        if (acl)
-            m_acl=acl;
         else {
-            delete plugin;
-            log.fatal("plugin was not an Access Control provider");
-            throw UnsupportedExtensionException("plugin was not an Access Control provider");
+            acl=XMLHelper::getFirstChildElement(e,AccessControl);
+            if (acl) {
+                log.info("building XML-based Access Control provider...");
+                plugin=saml::SAMLConfig::getConfig().getPlugMgr().newPlugin(XML_ACCESSCONTROL,acl);
+            }
+            else {
+                acl=XMLHelper::getFirstChildElement(e,AccessControlProvider);
+                if (acl) {
+                    xmltooling::auto_ptr_char type(acl->getAttributeNS(NULL,type));
+                    log.info("building Access Control provider of type %s...",type.get());
+                    plugin=saml::SAMLConfig::getConfig().getPlugMgr().newPlugin(type.get(),acl);
+                }
+            }
+        }
+        if (plugin) {
+            IAccessControl* acl=dynamic_cast<IAccessControl*>(plugin);
+            if (acl)
+                m_acl=acl;
+            else {
+                delete plugin;
+                throw UnknownExtensionException("plugin was not an Access Control provider");
+            }
         }
     }
+    catch (exception& ex) {
+        log.crit("exception building AccessControl provider: %s", ex.what());
+        m_acl = new AccessControlDummy();
+    }
 }
 
 Override::Override(const DOMElement* e, Category& log, const Override* base) : m_base(base), m_acl(NULL)
@@ -194,18 +214,56 @@ Override::Override(const DOMElement* e, Category& log, const Override* base) : m
         loadACL(e,log);
     
         // Handle nested Paths.
-        DOMNodeList* nlist=e->getElementsByTagNameNS(ShibTargetConfig::SHIBTARGET_NS,SHIBT_L(Path));
-        for (int i=0; nlist && i<nlist->getLength(); i++) {
-            DOMElement* path=static_cast<DOMElement*>(nlist->item(i));
-            const XMLCh* n=path->getAttributeNS(NULL,SHIBT_L(name));
+        DOMElement* path = XMLHelper::getFirstChildElement(e,Path);
+        for (int i=1; path; ++i, path=XMLHelper::getNextSiblingElement(path,Path)) {
+            const XMLCh* n=path->getAttributeNS(NULL,name);
+            
+            // Skip any leading slashes.
+            while (n && *n==chForwardSlash)
+                n++;
+            
+            // Check for empty name.
             if (!n || !*n) {
-                log.warn("skipping Path element (%d) with empty name attribute",i);
+                log.warn("skipping Path element (%d) with empty name attribute", i);
                 continue;
             }
-            else if (*n==chForwardSlash && !n[1]) {
-                log.warn("skipping Path element (%d) with a lone slash in the name attribute",i);
-                continue;
+
+            // Check for an embedded slash.
+            int slash=XMLString::indexOf(n,chForwardSlash);
+            if (slash>0) {
+                // Copy the first path segment.
+                XMLCh* namebuf=new XMLCh[slash + 1];
+                for (int pos=0; pos < slash; pos++)
+                    namebuf[pos]=n[pos];
+                namebuf[slash]=chNull;
+                
+                // Move past the slash in the original pathname.
+                n=n+slash+1;
+                
+                // Skip any leading slashes again.
+                while (*n==chForwardSlash)
+                    n++;
+                
+                if (*n) {
+                    // Create a placeholder Path element for the first path segment and replant under it.
+                    DOMElement* newpath=path->getOwnerDocument()->createElementNS(shibspconstants::SHIB1SPCONFIG_NS,Path);
+                    newpath->setAttributeNS(NULL,name,namebuf);
+                    path->setAttributeNS(NULL,name,n);
+                    path->getParentNode()->replaceChild(newpath,path);
+                    newpath->appendChild(path);
+                    
+                    // Repoint our locals at the new parent.
+                    path=newpath;
+                    n=path->getAttributeNS(NULL,name);
+                }
+                else {
+                    // All we had was a pathname with trailing slash(es), so just reset it without them.
+                    path->setAttributeNS(NULL,name,namebuf);
+                    n=path->getAttributeNS(NULL,name);
+                }
+                delete[] namebuf;
             }
+            
             Override* o=new Override(path,log,this);
             pair<bool,const char*> name=o->getString("name");
             char* dup=strdup(name.second);
@@ -221,8 +279,9 @@ Override::Override(const DOMElement* e, Category& log, const Override* base) : m
             free(dup);
         }
     }
-    catch (...) {
-        this->~Override();
+    catch (exception&) {
+        delete m_acl;
+        for_each(m_map.begin(),m_map.end(),xmltooling::cleanup_pair<string,Override>());
         throw;
     }
 }
@@ -230,13 +289,12 @@ Override::Override(const DOMElement* e, Category& log, const Override* base) : m
 Override::~Override()
 {
     delete m_acl;
-    for (map<string,Override*>::iterator i=m_map.begin(); i!=m_map.end(); i++)
-        delete i->second;
+    for_each(m_map.begin(),m_map.end(),xmltooling::cleanup_pair<string,Override>());
 }
 
 pair<bool,bool> Override::getBool(const char* name, const char* ns) const
 {
-    pair<bool,bool> ret=XMLPropertySet::getBool(name,ns);
+    pair<bool,bool> ret=DOMPropertySet::getBool(name,ns);
     if (ret.first)
         return ret;
     return m_base ? m_base->getBool(name,ns) : ret;
@@ -244,7 +302,7 @@ pair<bool,bool> Override::getBool(const char* name, const char* ns) const
 
 pair<bool,const char*> Override::getString(const char* name, const char* ns) const
 {
-    pair<bool,const char*> ret=XMLPropertySet::getString(name,ns);
+    pair<bool,const char*> ret=DOMPropertySet::getString(name,ns);
     if (ret.first)
         return ret;
     return m_base ? m_base->getString(name,ns) : ret;
@@ -252,7 +310,7 @@ pair<bool,const char*> Override::getString(const char* name, const char* ns) con
 
 pair<bool,const XMLCh*> Override::getXMLString(const char* name, const char* ns) const
 {
-    pair<bool,const XMLCh*> ret=XMLPropertySet::getXMLString(name,ns);
+    pair<bool,const XMLCh*> ret=DOMPropertySet::getXMLString(name,ns);
     if (ret.first)
         return ret;
     return m_base ? m_base->getXMLString(name,ns) : ret;
@@ -260,7 +318,7 @@ pair<bool,const XMLCh*> Override::getXMLString(const char* name, const char* ns)
 
 pair<bool,unsigned int> Override::getUnsignedInt(const char* name, const char* ns) const
 {
-    pair<bool,unsigned int> ret=XMLPropertySet::getUnsignedInt(name,ns);
+    pair<bool,unsigned int> ret=DOMPropertySet::getUnsignedInt(name,ns);
     if (ret.first)
         return ret;
     return m_base ? m_base->getUnsignedInt(name,ns) : ret;
@@ -268,15 +326,15 @@ pair<bool,unsigned int> Override::getUnsignedInt(const char* name, const char* n
 
 pair<bool,int> Override::getInt(const char* name, const char* ns) const
 {
-    pair<bool,int> ret=XMLPropertySet::getInt(name,ns);
+    pair<bool,int> ret=DOMPropertySet::getInt(name,ns);
     if (ret.first)
         return ret;
     return m_base ? m_base->getInt(name,ns) : ret;
 }
 
-const IPropertySet* Override::getPropertySet(const char* name, const char* ns) const
+const PropertySet* Override::getPropertySet(const char* name, const char* ns) const
 {
-    const IPropertySet* ret=XMLPropertySet::getPropertySet(name,ns);
+    const PropertySet* ret=DOMPropertySet::getPropertySet(name,ns);
     if (ret || !m_base)
         return ret;
     return m_base->getPropertySet(name,ns);
@@ -316,148 +374,130 @@ const Override* Override::locate(const char* path) const
     return o;
 }
 
-void XMLRequestMapperImpl::init()
+XMLRequestMapperImpl::XMLRequestMapperImpl(const DOMElement* e, Category& log) : m_document(NULL)
 {
-    NDC ndc("init");
-    log=&Category::getInstance("shibtarget.XMLRequestMapper");
+#ifdef _DEBUG
+    xmltooling::NDC ndc("XMLRequestMapperImpl");
+#endif
 
-    try {
-        if (!saml::XML::isElementNamed(ReloadableXMLFileImpl::m_root,ShibTargetConfig::SHIBTARGET_NS,SHIBT_L(RequestMap))) {
-            log->error("Construction requires a valid request mapping file: (conf:RequestMap as root element)");
-            throw MalformedException("Construction requires a valid request mapping file: (conf:RequestMap as root element)");
+    // Load the property set.
+    load(e,log,this);
+    
+    // Load any AccessControl provider.
+    loadACL(e,log);
+
+    // Loop over the Host elements.
+    const DOMElement* host = XMLHelper::getFirstChildElement(e,Host);
+    for (int i=1; host; ++i, host=XMLHelper::getNextSiblingElement(host,Host)) {
+        const XMLCh* n=host->getAttributeNS(NULL,name);
+        if (!n || !*n) {
+            log.warn("Skipping Host element (%d) with empty name attribute",i);
+            continue;
         }
-
-        // Load the property set.
-        load(ReloadableXMLFileImpl::m_root,*log,this);
         
-        // Load any AccessControl provider.
-        loadACL(ReloadableXMLFileImpl::m_root,*log);
-    
-        // Loop over the Host elements.
-        DOMNodeList* nlist = ReloadableXMLFileImpl::m_root->getElementsByTagNameNS(ShibTargetConfig::SHIBTARGET_NS,SHIBT_L(Host));
-        for (int i=0; nlist && i<nlist->getLength(); i++) {
-            DOMElement* host=static_cast<DOMElement*>(nlist->item(i));
-            const XMLCh* n=host->getAttributeNS(NULL,SHIBT_L(name));
-            if (!n || !*n) {
-                log->warn("Skipping Host element (%d) with empty name attribute",i);
-                continue;
-            }
-            
-            Override* o=new Override(host,*log,this);
-            pair<bool,const char*> name=o->getString("name");
-            pair<bool,const char*> scheme=o->getString("scheme");
-            pair<bool,const char*> port=o->getString("port");
-            
-            char* dup=strdup(name.second);
-            for (char* pch=dup; *pch; pch++)
-                *pch=tolower(*pch);
-            auto_ptr<char> dupwrap(dup);
-
-            if (!scheme.first && port.first) {
-                // No scheme, but a port, so assume http.
-                scheme = pair<bool,const char*>(true,"http");
-            }
-            else if (scheme.first && !port.first) {
-                // Scheme, no port, so default it.
-                // XXX Use getservbyname instead?
-                port.first = true;
-                if (!strcmp(scheme.second,"http"))
-                    port.second = "80";
-                else if (!strcmp(scheme.second,"https"))
-                    port.second = "443";
-                else if (!strcmp(scheme.second,"ftp"))
-                    port.second = "21";
-                else if (!strcmp(scheme.second,"ldap"))
-                    port.second = "389";
-                else if (!strcmp(scheme.second,"ldaps"))
-                    port.second = "636";
-            }
+        Override* o=new Override(host,log,this);
+        pair<bool,const char*> name=o->getString("name");
+        pair<bool,const char*> scheme=o->getString("scheme");
+        pair<bool,const char*> port=o->getString("port");
+        
+        char* dup=strdup(name.second);
+        for (char* pch=dup; *pch; pch++)
+            *pch=tolower(*pch);
+        auto_ptr<char> dupwrap(dup);
+
+        if (!scheme.first && port.first) {
+            // No scheme, but a port, so assume http.
+            scheme = pair<bool,const char*>(true,"http");
+        }
+        else if (scheme.first && !port.first) {
+            // Scheme, no port, so default it.
+            // XXX Use getservbyname instead?
+            port.first = true;
+            if (!strcmp(scheme.second,"http"))
+                port.second = "80";
+            else if (!strcmp(scheme.second,"https"))
+                port.second = "443";
+            else if (!strcmp(scheme.second,"ftp"))
+                port.second = "21";
+            else if (!strcmp(scheme.second,"ldap"))
+                port.second = "389";
+            else if (!strcmp(scheme.second,"ldaps"))
+                port.second = "636";
+        }
 
-            if (scheme.first) {
-                string url(scheme.second);
-                url=url + "://" + dup;
-                
-                // Is this the default port?
-                if ((!strcmp(scheme.second,"http") && !strcmp(port.second,"80")) ||
-                    (!strcmp(scheme.second,"https") && !strcmp(port.second,"443")) ||
-                    (!strcmp(scheme.second,"ftp") && !strcmp(port.second,"21")) ||
-                    (!strcmp(scheme.second,"ldap") && !strcmp(port.second,"389")) ||
-                    (!strcmp(scheme.second,"ldaps") && !strcmp(port.second,"636"))) {
-                    // First store a port-less version.
-                    if (m_map.count(url) || m_extras.count(url)) {
-                        log->warn("Skipping duplicate Host element (%s)",url.c_str());
-                        delete o;
-                        continue;
-                    }
-                    m_map[url]=o;
-                    log->debug("Added <Host> mapping for %s",url.c_str());
-                    
-                    // Now append the port. We use the extras vector, to avoid double freeing the object later.
-                    url=url + ':' + port.second;
-                    m_extras[url]=o;
-                    log->debug("Added <Host> mapping for %s",url.c_str());
-                }
-                else {
-                    url=url + ':' + port.second;
-                    if (m_map.count(url) || m_extras.count(url)) {
-                        log->warn("Skipping duplicate Host element (%s)",url.c_str());
-                        delete o;
-                        continue;
-                    }
-                    m_map[url]=o;
-                    log->debug("Added <Host> mapping for %s",url.c_str());
-                }
-            }
-            else {
-                // No scheme or port, so we enter dual hosts on http:80 and https:443
-                string url("http://");
-                url = url + dup;
+        if (scheme.first) {
+            string url(scheme.second);
+            url=url + "://" + dup;
+            
+            // Is this the default port?
+            if ((!strcmp(scheme.second,"http") && !strcmp(port.second,"80")) ||
+                (!strcmp(scheme.second,"https") && !strcmp(port.second,"443")) ||
+                (!strcmp(scheme.second,"ftp") && !strcmp(port.second,"21")) ||
+                (!strcmp(scheme.second,"ldap") && !strcmp(port.second,"389")) ||
+                (!strcmp(scheme.second,"ldaps") && !strcmp(port.second,"636"))) {
+                // First store a port-less version.
                 if (m_map.count(url) || m_extras.count(url)) {
-                    log->warn("Skipping duplicate Host element (%s)",url.c_str());
+                    log.warn("Skipping duplicate Host element (%s)",url.c_str());
                     delete o;
                     continue;
                 }
                 m_map[url]=o;
-                log->debug("Added <Host> mapping for %s",url.c_str());
+                log.debug("Added <Host> mapping for %s",url.c_str());
                 
-                url = url + ":80";
-                if (m_map.count(url) || m_extras.count(url)) {
-                    log->warn("Skipping duplicate Host element (%s)",url.c_str());
-                    continue;
-                }
+                // Now append the port. We use the extras vector, to avoid double freeing the object later.
+                url=url + ':' + port.second;
                 m_extras[url]=o;
-                log->debug("Added <Host> mapping for %s",url.c_str());
-                
-                url = "https://";
-                url = url + dup;
-                if (m_map.count(url) || m_extras.count(url)) {
-                    log->warn("Skipping duplicate Host element (%s)",url.c_str());
-                    continue;
-                }
-                m_extras[url]=o;
-                log->debug("Added <Host> mapping for %s",url.c_str());
-                
-                url = url + ":443";
+                log.debug("Added <Host> mapping for %s",url.c_str());
+            }
+            else {
+                url=url + ':' + port.second;
                 if (m_map.count(url) || m_extras.count(url)) {
-                    log->warn("Skipping duplicate Host element (%s)",url.c_str());
+                    log.warn("Skipping duplicate Host element (%s)",url.c_str());
+                    delete o;
                     continue;
                 }
-                m_extras[url]=o;
-                log->debug("Added <Host> mapping for %s",url.c_str());
+                m_map[url]=o;
+                log.debug("Added <Host> mapping for %s",url.c_str());
             }
         }
+        else {
+            // No scheme or port, so we enter dual hosts on http:80 and https:443
+            string url("http://");
+            url = url + dup;
+            if (m_map.count(url) || m_extras.count(url)) {
+                log.warn("Skipping duplicate Host element (%s)",url.c_str());
+                delete o;
+                continue;
+            }
+            m_map[url]=o;
+            log.debug("Added <Host> mapping for %s",url.c_str());
+            
+            url = url + ":80";
+            if (m_map.count(url) || m_extras.count(url)) {
+                log.warn("Skipping duplicate Host element (%s)",url.c_str());
+                continue;
+            }
+            m_extras[url]=o;
+            log.debug("Added <Host> mapping for %s",url.c_str());
+            
+            url = "https://";
+            url = url + dup;
+            if (m_map.count(url) || m_extras.count(url)) {
+                log.warn("Skipping duplicate Host element (%s)",url.c_str());
+                continue;
+            }
+            m_extras[url]=o;
+            log.debug("Added <Host> mapping for %s",url.c_str());
+            
+            url = url + ":443";
+            if (m_map.count(url) || m_extras.count(url)) {
+                log.warn("Skipping duplicate Host element (%s)",url.c_str());
+                continue;
+            }
+            m_extras[url]=o;
+            log.debug("Added <Host> mapping for %s",url.c_str());
+        }
     }
-    catch (SAMLException& e) {
-        log->errorStream() << "Error while parsing request mapping configuration: " << e.what() << CategoryStream::ENDLINE;
-        throw;
-    }
-#ifndef _DEBUG
-    catch (...)
-    {
-        log->error("Unexpected error while parsing request mapping configuration");
-        throw;
-    }
-#endif
 }
 
 const Override* XMLRequestMapperImpl::findOverride(const char* vhost, const char* path) const
@@ -475,75 +515,39 @@ const Override* XMLRequestMapperImpl::findOverride(const char* vhost, const char
     return o ? o->locate(path) : this;
 }
 
-const char* split_url(const char* url, string& vhost)
-{
-    const char* path=NULL;
-    const char* slash=strchr(url,'/');
-    if (slash)
-    {
-        slash=strchr(slash,'/');
-        if (slash)
-        {
-            path=strchr(slash,'/');
-            if (path)
-                vhost.append(url,path-url);
-            else
-                vhost=url;
-        }
-    }
-    return path;
-}
-
-ReloadableXMLFileImpl* XMLRequestMapper::newImplementation(const char* pathname, bool first) const
-{
-    return new XMLRequestMapperImpl(pathname);
-}
-
-ReloadableXMLFileImpl* XMLRequestMapper::newImplementation(const DOMElement* e, bool first) const
-{
-    return new XMLRequestMapperImpl(e);
-}
-
-IRequestMapper::Settings XMLRequestMapper::getSettingsFromURL(const char* url) const
+pair<bool,DOMElement*> XMLRequestMapper::load()
 {
-    string vhost;
-    const char* path=split_url(url,vhost);
+    // Load from source using base class.
+    pair<bool,DOMElement*> raw = ReloadableXMLFile::load();
+    
+    // If we own it, wrap it.
+    XercesJanitor<DOMDocument> docjanitor(raw.first ? raw.second->getOwnerDocument() : NULL);
 
-    XMLRequestMapperImpl* impl=static_cast<XMLRequestMapperImpl*>(getImplementation());
-    const Override* o=impl->findOverride(vhost.c_str(), path);
+    XMLRequestMapperImpl* impl = new XMLRequestMapperImpl(raw.second,m_log);
+    
+    // If we held the document, transfer it to the impl. If we didn't, it's a no-op.
+    impl->setDocument(docjanitor.release());
 
-    if (impl->log->isDebugEnabled()) {
-        saml::NDC ndc("getApplicationFromURL");
-        pair<bool,const char*> ret=o->getString("applicationId");
-        impl->log->debug("mapped %s to %s", url, ret.second);
-    }
+    delete m_impl;
+    m_impl = impl;
 
-    return Settings(o,o->m_acl);
+    return make_pair(false,(DOMElement*)NULL);
 }
 
-IRequestMapper::Settings XMLRequestMapper::getSettingsFromParsedURL(
-    const char* scheme, const char* hostname, unsigned int port, const char* path
-    ) const
+IRequestMapper::Settings XMLRequestMapper::getSettings(ShibTarget* st) const
 {
-    char buf[21];
-    string vhost(scheme);
-    vhost=vhost + "://" + hostname + ':';
-#ifdef WIN32
-    _snprintf(buf,20,"%u",port);
-#else
-    snprintf(buf,20,"%u",port);
-#endif
-    vhost+=buf;
+    ostringstream vhost;
+    vhost << st->getProtocol() << "://" << st->getHostname() << ':' << st->getPort();
 
-    XMLRequestMapperImpl* impl=static_cast<XMLRequestMapperImpl*>(getImplementation());
-    const Override* o=impl->findOverride(vhost.c_str(), path);
+    const Override* o=m_impl->findOverride(vhost.str().c_str(), st->getRequestURI());
 
-    if (impl->log->isDebugEnabled())
-    {
-        saml::NDC ndc("getApplicationFromParsedURL");
+    if (m_log.isDebugEnabled()) {
+#ifdef _DEBUG
+        xmltooling::NDC ndc("getSettings");
+#endif
         pair<bool,const char*> ret=o->getString("applicationId");
-        impl->log->debug("mapped %s%s to %s", vhost.c_str(), path ? path : "", ret.second);
+        m_log.debug("mapped %s%s to %s", vhost.str().c_str(), st->getRequestURI() ? st->getRequestURI() : "", ret.second);
     }
 
-    return Settings(o,o->m_acl);
+    return Settings(o,o->getAC());
 }