[cpp-sp] branch dev/scott-wip updated: Port mostly complete, pending Apache module conversion.

Scott Cantor cantor.2 at osu.edu
Fri Dec 13 01:21:12 UTC 2024


This is an automated email from the git hooks/post-receive script.

scantor pushed a commit to branch dev/scott-wip
in repository cpp-sp.

View the commit online:
http://git.shibboleth.net/view/?p=cpp-sp.git;a=commit;h=752f0134c25192cb0e67d99df892a82d7cb92dc4

The following commit(s) were added to refs/heads/dev/scott-wip by this push:
     new 752f0134 Port mostly complete, pending Apache module conversion.
752f0134 is described below

commit 752f0134c25192cb0e67d99df892a82d7cb92dc4
Author: Scott Cantor <cantor.2 at osu.edu>
AuthorDate: Thu Dec 12 20:20:32 2024 -0500

    Port mostly complete, pending Apache module conversion.
---
 shibsp/AbstractSPRequest.cpp                   |  38 +-
 shibsp/AbstractSPRequest.h                     |  24 +-
 shibsp/RequestMapper.h                         |   4 +-
 shibsp/ServiceProvider.cpp                     | 196 ++++---
 shibsp/ServiceProvider.h                       |  24 +-
 shibsp/handler/AbstractHandler.h               |  52 +-
 shibsp/handler/impl/AbstractHandler.cpp        |  55 +-
 shibsp/handler/impl/RemotedHandler.cpp         |  30 +-
 shibsp/handler/impl/SAMLDSSessionInitiator.cpp |  35 +-
 shibsp/handler/impl/SessionHandler.cpp         |  20 +-
 shibsp/handler/impl/SessionInitiator.cpp       |  10 +-
 shibsp/handler/impl/StatusHandler.cpp          |   6 +-
 shibsp/impl/XMLAccessControl.cpp               |  24 +-
 shibsp/impl/XMLRequestMapper.cpp               | 689 ++++++++++++-------------
 shibsp/util/BoostPropertySet.cpp               |  14 +
 shibsp/util/BoostPropertySet.h                 |   1 +
 shibsp/util/PropertySet.h                      |   8 +
 shibsp/util/ReloadableXMLFile.h                |   6 +-
 18 files changed, 593 insertions(+), 643 deletions(-)

diff --git a/shibsp/AbstractSPRequest.cpp b/shibsp/AbstractSPRequest.cpp
index baaba8d9..68f65ce9 100644
--- a/shibsp/AbstractSPRequest.cpp
+++ b/shibsp/AbstractSPRequest.cpp
@@ -1,21 +1,15 @@
 /**
- * Licensed to the University Corporation for Advanced Internet
- * Development, Inc. (UCAID) under one or more contributor license
- * agreements. See the NOTICE file distributed with this work for
- * additional information regarding copyright ownership.
+ * 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
  *
- * UCAID licenses this file to you 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
  *
- * 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.
+ * 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.
  */
 
 /**
@@ -35,7 +29,6 @@
 #include <boost/lexical_cast.hpp>
 
 using namespace shibsp;
-using namespace xmltooling;
 using namespace std;
 
 SPRequest::SPRequest()
@@ -60,7 +53,7 @@ AbstractSPRequest::~AbstractSPRequest()
     if (m_session)
         m_session->unlock();
     if (m_mapper)
-        m_mapper->unlock();
+        m_mapper->unlock_shared();
     if (m_sp)
         m_sp->unlock();
 }
@@ -75,7 +68,7 @@ RequestMapper::Settings AbstractSPRequest::getRequestSettings() const
     if (!m_mapper) {
         // Map request to application and content settings.
         m_mapper = m_sp->getRequestMapper();
-        m_mapper->lock();
+        m_mapper->lock_shared();
         m_settings = m_mapper->getSettings(*this);
 
 /*
@@ -93,7 +86,7 @@ const Application& AbstractSPRequest::getApplication() const
 {
     if (!m_app) {
         // Now find the application from the URL settings
-        m_app = m_sp->getApplication(getRequestSettings().first->getString("applicationId").second);
+        m_app = m_sp->getApplication(getRequestSettings().first->getString("applicationId"));
         if (!m_app)
             throw ConfigurationException("Unable to map non-default applicationId to an ApplicationOverride, check configuration.");
     }
@@ -162,8 +155,8 @@ const char* AbstractSPRequest::getRequestURL() const
 
 string AbstractSPRequest::getRemoteAddr() const
 {
-    pair<bool,const char*> addr = getRequestSettings().first->getString("REMOTE_ADDR");
-    return addr.first ? getHeader(addr.second) : "";
+    const char* addr = getRequestSettings().first->getString("REMOTE_ADDR");
+    return addr ? getHeader(addr) : "";
 }
 
 const char* AbstractSPRequest::getParameter(const char* name) const
@@ -233,8 +226,7 @@ const char* AbstractSPRequest::getHandlerURL(const char* resource) const
     }
     else if (*handler!='/' && strncmp(handler,"http:",5) && strncmp(handler,"https:",6)) {
         throw ConfigurationException(
-            "Invalid handlerURL property ($1) in <Sessions> element for Application ($2)",
-            params(2, handler ? handler : "null", m_app->getId())
+            string("Invalid handlerURL property in <Sessions> element for Application ") + m_app->getId()
             );
     }
 
diff --git a/shibsp/AbstractSPRequest.h b/shibsp/AbstractSPRequest.h
index 48a2e282..79c7b0a0 100644
--- a/shibsp/AbstractSPRequest.h
+++ b/shibsp/AbstractSPRequest.h
@@ -1,21 +1,15 @@
 /**
- * Licensed to the University Corporation for Advanced Internet
- * Development, Inc. (UCAID) under one or more contributor license
- * agreements. See the NOTICE file distributed with this work for
- * additional information regarding copyright ownership.
+ * 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
  *
- * UCAID licenses this file to you 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
  *
- * 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.
+ * 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.
  */
 
 /**
diff --git a/shibsp/RequestMapper.h b/shibsp/RequestMapper.h
index 24ffc1dd..d8c945d8 100644
--- a/shibsp/RequestMapper.h
+++ b/shibsp/RequestMapper.h
@@ -34,7 +34,7 @@ namespace shibsp {
 
     class SHIBSP_API AccessControl;
     class SHIBSP_API HTTPRequest;
-    class SHIBSP_API PropertySet;
+    class SHIBSP_API PropertySet2;
 
     /**
      * Interface to a request mapping plugin
@@ -51,7 +51,7 @@ namespace shibsp {
         virtual ~RequestMapper();
 
         /** Combination of configuration settings and effective access control. */
-        typedef std::pair<const PropertySet*,AccessControl*> Settings;
+        typedef std::pair<const PropertySet2*,AccessControl*> Settings;
 
         /**
          * Map request to settings.
diff --git a/shibsp/ServiceProvider.cpp b/shibsp/ServiceProvider.cpp
index 97539277..b95d8fde 100644
--- a/shibsp/ServiceProvider.cpp
+++ b/shibsp/ServiceProvider.cpp
@@ -1,21 +1,15 @@
 /**
- * Licensed to the University Corporation for Advanced Internet
- * Development, Inc. (UCAID) under one or more contributor license
- * agreements. See the NOTICE file distributed with this work for
- * additional information regarding copyright ownership.
+ * 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
  *
- * UCAID licenses this file to you 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
  *
- * 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.
+ * 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.
  */
 
 /**
@@ -46,6 +40,7 @@
 #include <boost/algorithm/string.hpp>
 #include <boost/lexical_cast.hpp>
 
+// This is there until we figure out the TemplateEngine remediation/removal.
 #include <xmltooling/XMLToolingConfig.h>
 
 using namespace shibsp;
@@ -62,8 +57,8 @@ namespace shibsp {
         // The properties we need can be set in the RequestMap, or the Errors element.
         bool mderror = false;
         bool accesserror = (strcmp(page, "access")==0);
-        pair<bool,const char*> redirectErrors = pair<bool,const char*>(false,nullptr);
-        pair<bool,const char*> pathname = pair<bool,const char*>(false,nullptr);
+        const char* redirectErrors = nullptr;
+        const char* pathname = nullptr;
 
         // Strictly for error handling, detect a nullptr application and point at the default.
         if (!app)
@@ -83,7 +78,7 @@ namespace shibsp {
             RequestMapper::Settings settings = request.getRequestSettings();
             if (mderror)
                 pathname = settings.first->getString("metadataError");
-            if (!pathname.first) {
+            if (!pathname) {
                 string pagename(page);
                 pagename += "Error";
                 pathname = settings.first->getString(pagename.c_str());
@@ -97,10 +92,10 @@ namespace shibsp {
 
         // Check for redirection on errors instead of template.
         if (mayRedirect) {
-            if (!redirectErrors.first && props)
-                redirectErrors = props->getString("redirectErrors");
-            if (redirectErrors.first) {
-                string loc(redirectErrors.second);
+            if (!redirectErrors && props)
+                redirectErrors = props->getString("redirectErrors").second;
+            if (redirectErrors) {
+                string loc(redirectErrors);
                 request.absolutize(loc);
                 loc = loc + '?' + tp.toQueryString();
                 return request.sendRedirect(loc.c_str());
@@ -112,23 +107,23 @@ namespace shibsp {
         request.setResponseHeader("Cache-Control","private,no-store,no-cache,max-age=0");
 
         // Nothing in the request map, so check for a property named "page" in the Errors property set.
-        if (!pathname.first && props) {
+        if (!pathname && props) {
             if (mderror)
-                pathname=props->getString("metadata");
-            if (!pathname.first)
-                pathname=props->getString(page);
+                pathname=props->getString("metadata").second;
+            if (!pathname)
+                pathname=props->getString(page).second;
         }
 
         // If there's still no template to use, just use pageError.html unless it's an access issue.
         string fname;
-        if (!pathname.first) {
+        if (!pathname) {
             if (!accesserror) {
                 fname = string(mderror ? "metadata" : page) + "Error.html";
-                pathname.second = fname.c_str();
+                pathname = fname.c_str();
             }
         }
         else {
-            fname = pathname.second;
+            fname = pathname;
         }
 
         // If we have a template to use, use it.
@@ -148,7 +143,7 @@ namespace shibsp {
             return request.sendResponse(msg, HTTPResponse::XMLTOOLING_HTTP_STATUS_FORBIDDEN);
         }
 
-        log.error("sendError could not process error template (%s)", pathname.second);
+        log.error("sendError could not process error template (%s)", pathname);
         istringstream msg("Internal Server Error. Please contact the site administrator.");
         return request.sendError(msg);
     }
@@ -173,24 +168,21 @@ namespace shibsp {
 
     void SHIBSP_DLLLOCAL exportAttributes(SPRequest& request, const Session* session, RequestMapper::Settings settings) {
 
-        pair<bool,const char*> enc = settings.first->getString("encoding");
-        if (enc.first && strcmp(enc.second, "URL"))
-            throw ConfigurationException("Unsupported value for 'encoding' content setting ($1).", params(1,enc.second));
+        const char* enc = settings.first->getString("encoding");
+        if (enc && strcmp(enc, "URL"))
+            throw ConfigurationException(string("Unsupported value for 'encoding' content setting: ") + enc);
 
         const URLEncoder& encoder = AgentConfig::getConfig().getURLEncoder();
 
         // Default delimiter is semicolon but is now configurable.
-        pair<bool,const char*> delim = settings.first->getString("attributeValueDelimiter");
-        if (enc.first || !delim.first) {
-            delim.second = ";";
-        }
-        size_t delim_len = strlen(delim.second);
+        const char* delim = settings.first->getString("attributeValueDelimiter", ";");
+        size_t delim_len = strlen(delim);
 
-        pair<bool,bool> exportDups = settings.first->getBool("exportDuplicateValues");
+        bool exportDups = settings.first->getBool("exportDuplicateValues", true);
         const multimap<string,const Attribute*>& attributes = session->getIndexedAttributes();
 
         // Default export strategy will include duplicates.
-        if (!exportDups.first || exportDups.second) {
+        if (exportDups) {
             for (multimap<string,const Attribute*>::const_iterator a = attributes.begin(); a != attributes.end(); ++a) {
                 if (a->second->isInternal())
                     continue;
@@ -198,16 +190,16 @@ namespace shibsp {
                 const vector<string>& vals = a->second->getSerializedValues();
                 for (vector<string>::const_iterator v = vals.begin(); v != vals.end(); ++v) {
                     if (!header.empty())
-                        header += delim.second;
-                    if (enc.first) {
+                        header += delim;
+                    if (enc) {
                         // If URL-encoding, any semicolons will get escaped anyway.
                         header += encoder.encode(v->c_str());
                     }
                     else {
-                        string::size_type pos = v->find(delim.second, string::size_type(0));
+                        string::size_type pos = v->find(delim, string::size_type(0));
                         if (pos != string::npos) {
                             string value(*v);
-                            for (; pos != string::npos; pos = value.find(delim.second, pos)) {
+                            for (; pos != string::npos; pos = value.find(delim, pos)) {
                                 value.insert(pos, "\\");
                                 pos += delim_len + 1;
                             }
@@ -236,16 +228,16 @@ namespace shibsp {
                 string header;
                 for (set<string>::const_iterator v = deduped->second.begin(); v != deduped->second.end(); ++v) {
                     if (!header.empty())
-                        header += delim.second;
-                    if (enc.first) {
+                        header += delim;
+                    if (enc) {
                         // If URL-encoding, any semicolons will get escaped anyway.
                         header += encoder.encode(v->c_str());
                     }
                     else {
-                        string::size_type pos = v->find(delim.second, string::size_type(0));
+                        string::size_type pos = v->find(delim, string::size_type(0));
                         if (pos != string::npos) {
                             string value(*v);
-                            for (; pos != string::npos; pos = value.find(delim.second, pos)) {
+                            for (; pos != string::npos; pos = value.find(delim, pos)) {
                                 value.insert(pos, "\\");
                                 pos += delim_len + 1;
                             }
@@ -269,7 +261,7 @@ namespace shibsp {
             for (; matches.first != matches.second; ++matches.first) {
                 const vector<string>& vals = matches.first->second->getSerializedValues();
                 if (!vals.empty()) {
-                    if (enc.first)
+                    if (enc)
                         request.setRemoteUser(encoder.encode(vals.front().c_str()).c_str());
                     else
                         request.setRemoteUser(vals.front().c_str());
@@ -308,8 +300,8 @@ pair<bool,long> ServiceProvider::doAuthentication(SPRequest& request, bool handl
 
         // If not SSL, check to see if we should block or redirect it.
         if (!request.isSecure()) {
-            pair<bool,const char*> redirectToSSL = settings.first->getString("redirectToSSL");
-            if (redirectToSSL.first) {
+            const char* redirectToSSL = settings.first->getString("redirectToSSL");
+            if (redirectToSSL) {
 #ifdef HAVE_STRCASECMP
                 if (!strcasecmp("GET",request.getMethod()) || !strcasecmp("HEAD",request.getMethod())) {
 #else
@@ -317,8 +309,8 @@ pair<bool,long> ServiceProvider::doAuthentication(SPRequest& request, bool handl
 #endif
                     // Compute the new target URL
                     string redirectURL = string("https://") + request.getHostname();
-                    if (strcmp(redirectToSSL.second,"443")) {
-                        redirectURL = redirectURL + ':' + redirectToSSL.second;
+                    if (strcmp(redirectToSSL,"443")) {
+                        redirectURL = redirectURL + ':' + redirectToSSL;
                     }
                     redirectURL += request.getRequestURI();
                     return make_pair(true, request.sendRedirect(redirectURL.c_str()));
@@ -345,16 +337,16 @@ pair<bool,long> ServiceProvider::doAuthentication(SPRequest& request, bool handl
         }
 
         // These settings dictate how to proceed.
-        pair<bool,const char*> authType = settings.first->getString("authType");
-        pair<bool,bool> requireSession = settings.first->getBool("requireSession");
-        pair<bool,const char*> requireSessionWith = settings.first->getString("requireSessionWith");
-        pair<bool,const char*> requireLogoutWith = settings.first->getString("requireLogoutWith");
+        const char* authType = settings.first->getString("authType");
+        bool requireSession = settings.first->getBool("requireSession", false);
+        const char* requireSessionWith = settings.first->getString("requireSessionWith");
+        const char* requireLogoutWith = settings.first->getString("requireLogoutWith");
 
         // If no session is required AND the AuthType (an Apache-derived concept) isn't recognized,
         // then we ignore this request and consider it unprotected. Apache might lie to us if
         // ShibBasicHijack is on, but that's up to it.
-        if ((!requireSession.first || !requireSession.second) && !requireSessionWith.first &&
-                (!authType.first || m_authTypes.find(boost::to_lower_copy(string(authType.second))) == m_authTypes.end()))
+        if (!requireSession && !requireSessionWith &&
+            (!authType || m_authTypes.find(boost::to_lower_copy(string(authType))) == m_authTypes.end()))
             return make_pair(true, request.returnDecline());
 
         // Fix for secadv 20050901
@@ -373,7 +365,7 @@ pair<bool,long> ServiceProvider::doAuthentication(SPRequest& request, bool handl
         Locker slocker(session, false); // pop existing lock on exit
         if (session) {
             // Check for logout interception.
-            if (requireLogoutWith.first) {
+            if (requireLogoutWith) {
                 // Check for a completion parameter on the query string.
                 const char* qstr = request.getQueryString();
                 if (!qstr || !strstr(qstr, "shiblogoutdone=1")) {
@@ -384,7 +376,7 @@ pair<bool,long> ServiceProvider::doAuthentication(SPRequest& request, bool handl
                     else
                         selfurl += '?';
                     selfurl += "shiblogoutdone=1";
-                    string loc = requireLogoutWith.second;
+                    string loc(requireLogoutWith);
                     request.absolutize(loc);
                     if (loc.find('?') != string::npos)
                         loc += '&';
@@ -398,20 +390,18 @@ pair<bool,long> ServiceProvider::doAuthentication(SPRequest& request, bool handl
         }
         else {
             // No session.  Maybe that's acceptable?
-            if ((!requireSession.first || !requireSession.second) && !requireSessionWith.first) {
+            if (!requireSession && !requireSessionWith) {
                 app->setHeader(request, "Shib-Handler", handlerURL);
                 return make_pair(true, request.returnOK());
             }
 
             // No session, but we require one. Initiate a new session using the indicated method.
             const SessionInitiator* initiator=nullptr;
-            if (requireSessionWith.first) {
+            if (requireSessionWith) {
                 SPConfig::getConfig().deprecation().warn("requireSessionWith");
-                initiator=app->getSessionInitiatorById(requireSessionWith.second);
+                initiator=app->getSessionInitiatorById(requireSessionWith);
                 if (!initiator) {
-                    throw ConfigurationException(
-                        "No session initiator found with id ($1), check requireSessionWith setting.", params(1, requireSessionWith.second)
-                        );
+                    throw ConfigurationException(string("No session initiator found with id: ") + requireSessionWith);
                 }
             }
             else {
@@ -422,20 +412,13 @@ pair<bool,long> ServiceProvider::doAuthentication(SPRequest& request, bool handl
 
             // Dispatch to SessionInitiator. This MUST handle the request, or we want to fail here.
             // Used to fall through into doExport, but this is a cleaner exit path.
-            try {
-                pair<bool, long> ret = initiator->run(request, false);
-                if (ret.first)
-                    return ret;
-                throw ConfigurationException("Session initiator did not handle request for a new session, check configuration.");
-            }
-            catch (XMLToolingException& ex) {
-                if (!ex.getProperty("eventType") && initiator->getEventType())
-                    ex.addProperty("eventType", initiator->getEventType());
-                throw;
-            }
+            pair<bool, long> ret = initiator->run(request, false);
+            if (ret.first)
+                return ret;
+            throw ConfigurationException("Session initiator did not handle request for a new session, check configuration.");
         }
 
-        request.setAuthType(authType.second);
+        request.setAuthType(authType);
 
         // We're done.  Everything is okay.  Nothing to report.  Nothing to do..
         // Let the caller decide how to proceed.
@@ -464,15 +447,15 @@ pair<bool,long> ServiceProvider::doAuthorization(SPRequest& request) const
         app = &(request.getApplication());
 
         // Three settings dictate how to proceed.
-        pair<bool,const char*> authType = settings.first->getString("authType");
-        pair<bool,bool> requireSession = settings.first->getBool("requireSession");
-        pair<bool,const char*> requireSessionWith = settings.first->getString("requireSessionWith");
+        const char* authType = settings.first->getString("authType");
+        bool requireSession = settings.first->getBool("requireSession", false);
+        const char* requireSessionWith = settings.first->getString("requireSessionWith");
 
         // If no session is required AND the AuthType (an Apache-derived concept) isn't recognized,
         // then we ignore this request and consider it unprotected. Apache might lie to us if
         // ShibBasicHijack is on, but that's up to it.
-        if ((!requireSession.first || !requireSession.second) && !requireSessionWith.first &&
-                (!authType.first || m_authTypes.find(boost::to_lower_copy(string(authType.second))) == m_authTypes.end()))
+        if (!requireSession && !requireSessionWith &&
+                (!authType || m_authTypes.find(boost::to_lower_copy(string(authType))) == m_authTypes.end()))
             return make_pair(true, request.returnDecline());
 
         // Do we have an access control plugin?
@@ -560,8 +543,8 @@ pair<bool,long> ServiceProvider::doExport(SPRequest& request, bool requireSessio
         // Check for export of "standard" variables.
         // A 3.0 release would switch this default to false and rely solely on the
         // Assertion extractor plugin and ship out of the box with the same defaults.
-        pair<bool,bool> stdvars = settings.first->getBool("exportStdVars");
-        if (!stdvars.first || stdvars.second) {
+        bool stdvars = settings.first->getBool("exportStdVars", true);
+        if (stdvars) {
             const char* hval = session->getEntityID();
             if (hval)
                 app->setHeader(request, "Shib-Identity-Provider", hval);
@@ -588,25 +571,27 @@ pair<bool,long> ServiceProvider::doExport(SPRequest& request, bool requireSessio
         }
 
         // Check for export of algorithmically-derived portion of cookie names.
-        stdvars = settings.first->getBool("exportCookie");
-        if (stdvars.first && stdvars.second) {
+        bool exportCookie = settings.first->getBool("exportCookie", false);
+        if (exportCookie) {
             pair<string,const char*> cookieprops = app->getCookieNameProps(nullptr);
             app->setHeader(request, "Shib-Cookie-Name", cookieprops.first.c_str());
         }
 
         // Maybe export the assertion keys.
-        pair<bool,bool> exp = settings.first->getBool("exportAssertion");
-        if (exp.first && exp.second) {
-            pair<bool,const char*> exportLocation = sessionProps ? sessionProps->getString("exportLocation") : pair<bool,const char*>(false,nullptr);
+        bool exportAssertion = settings.first->getBool("exportAssertion", false);
+        if (exportAssertion) {
+            pair<bool,const char*> exportLocation = sessionProps ? sessionProps->getString("exportLocation") : make_pair(false,nullptr);
             if (!exportLocation.first)
                 log.warn("can't export assertions without an exportLocation Sessions property");
             else {
                 string exportName = "Shib-Assertion-00";
                 string baseURL;
-                if (!strncmp(exportLocation.second, "http", 4))
+                if (!strncmp(exportLocation.second, "http", 4)) {
                     baseURL = exportLocation.second;
-                else
+                }
+                else {
                     baseURL = string(request.getHandlerURL(targetURL.c_str())) + exportLocation.second;
+                }
                 baseURL = baseURL + "?key=" + session->getID() + "&ID=";
                 const vector<const char*>& tokens = session->getAssertionIDs();
                 vector<const char*>::size_type count = 0;
@@ -647,8 +632,8 @@ pair<bool,long> ServiceProvider::doHandler(SPRequest& request) const
 
         // If not SSL, check to see if we should block or redirect it.
         if (!request.isSecure()) {
-            pair<bool,const char*> redirectToSSL = settings.first->getString("redirectToSSL");
-            if (redirectToSSL.first) {
+            const char* redirectToSSL = settings.first->getString("redirectToSSL");
+            if (redirectToSSL) {
 #ifdef HAVE_STRCASECMP
                 if (!strcasecmp("GET",request.getMethod()) || !strcasecmp("HEAD",request.getMethod())) {
 #else
@@ -656,8 +641,8 @@ pair<bool,long> ServiceProvider::doHandler(SPRequest& request) const
 #endif
                     // Compute the new target URL
                     string redirectURL = string("https://") + request.getHostname();
-                    if (strcmp(redirectToSSL.second,"443")) {
-                        redirectURL = redirectURL + ':' + redirectToSSL.second;
+                    if (strcmp(redirectToSSL,"443")) {
+                        redirectURL = redirectURL + ':' + redirectToSSL;
                     }
                     redirectURL += request.getRequestURI();
                     return make_pair(true, request.sendRedirect(redirectURL.c_str()));
@@ -695,18 +680,11 @@ pair<bool,long> ServiceProvider::doHandler(SPRequest& request) const
         if (!handler)
             throw ConfigurationException("Shibboleth handler invoked at an unconfigured location.");
 
-        try {
-            pair<bool, long> hret = handler->run(request);
-            // Did the handler run successfully?
-            if (hret.first)
-                return hret;
-            throw ConfigurationException("Configured Shibboleth handler failed to process the request.");
-        }
-        catch (XMLToolingException& ex) {
-            if (!ex.getProperty("eventType") && handler->getEventType())
-                ex.addProperty("eventType", handler->getEventType());
-            throw;
-        }
+        pair<bool, long> hret = handler->run(request);
+        // Did the handler run successfully?
+        if (hret.first)
+            return hret;
+        throw ConfigurationException("Configured Shibboleth handler failed to process the request.");
     }
     catch (const exception& e) {
         request.log(SPRequest::SPError, e.what());
diff --git a/shibsp/ServiceProvider.h b/shibsp/ServiceProvider.h
index d52b7e08..41d68d57 100644
--- a/shibsp/ServiceProvider.h
+++ b/shibsp/ServiceProvider.h
@@ -1,21 +1,15 @@
 /**
- * Licensed to the University Corporation for Advanced Internet
- * Development, Inc. (UCAID) under one or more contributor license
- * agreements. See the NOTICE file distributed with this work for
- * additional information regarding copyright ownership.
+ * 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
  *
- * UCAID licenses this file to you 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
  *
- * 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.
+ * 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.
  */
 
 /**
diff --git a/shibsp/handler/AbstractHandler.h b/shibsp/handler/AbstractHandler.h
index 60a29057..3b0bba11 100644
--- a/shibsp/handler/AbstractHandler.h
+++ b/shibsp/handler/AbstractHandler.h
@@ -1,21 +1,15 @@
 /**
- * Licensed to the University Corporation for Advanced Internet
- * Development, Inc. (UCAID) under one or more contributor license
- * agreements. See the NOTICE file distributed with this work for
- * additional information regarding copyright ownership.
+ * 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
  *
- * UCAID licenses this file to you 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
  *
- * 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.
+ * 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.
  */
 
 /**
@@ -193,19 +187,21 @@ namespace shibsp {
         /**
          * Returns a boolean-valued property.
          * 
-         * @param name      property name
-         * @param request   reference to incoming request
-         * @param type      bitmask of property sources to use
+         * @param name          property name
+         * @param request       reference to incoming request
+         * @param type          bitmask of property sources to use
          * @return a pair consisting of a nullptr indicator and the property value iff the indicator is true
          */
-        std::pair<bool,bool> getBool(const char* name, const HTTPRequest& request, unsigned int type=HANDLER_PROPERTY_ALL) const;
+        std::pair<bool,bool> getBool(
+            const char* name, const HTTPRequest& request, unsigned int type=HANDLER_PROPERTY_ALL
+            ) const;
 
         /**
          * Returns a string-valued property.
          * 
-         * @param name      property name
-         * @param request   reference to incoming request
-         * @param type      bitmask of property sources to use
+         * @param name          property name
+         * @param request       reference to incoming request
+         * @param type          bitmask of property sources to use
          * @return a pair consisting of a nullptr indicator and the property value iff the indicator is true
          */
         std::pair<bool,const char*> getString(const char* name, const HTTPRequest& request, unsigned int type=HANDLER_PROPERTY_ALL) const;
@@ -213,9 +209,9 @@ namespace shibsp {
         /**
          * Returns an unsigned integer-valued property.
          * 
-         * @param name      property name
-         * @param request   reference to incoming request
-         * @param type      bitmask of property sources to use
+         * @param name          property name
+         * @param request       reference to incoming request
+         * @param type          bitmask of property sources to use
          * @return a pair consisting of a nullptr indicator and the property value iff the indicator is true
          */
         std::pair<bool,unsigned int> getUnsignedInt(const char* name, const HTTPRequest& request, unsigned int type=HANDLER_PROPERTY_ALL) const;
@@ -223,9 +219,9 @@ namespace shibsp {
         /**
          * Returns an integer-valued property.
          * 
-         * @param name      property name
-         * @param request   reference to incoming request
-         * @param type      bitmask of property sources to use
+         * @param name          property name
+         * @param request       reference to incoming request
+         * @param type          bitmask of property sources to use
          * @return a pair consisting of a nullptr indicator and the property value iff the indicator is true
          */
         std::pair<bool,int> getInt(const char* name, const HTTPRequest& request, unsigned int type=HANDLER_PROPERTY_ALL) const;
diff --git a/shibsp/handler/impl/AbstractHandler.cpp b/shibsp/handler/impl/AbstractHandler.cpp
index d617661e..47c904f1 100644
--- a/shibsp/handler/impl/AbstractHandler.cpp
+++ b/shibsp/handler/impl/AbstractHandler.cpp
@@ -1,25 +1,19 @@
 /**
- * Licensed to the University Corporation for Advanced Internet
- * Development, Inc. (UCAID) under one or more contributor license
- * agreements. See the NOTICE file distributed with this work for
- * additional information regarding copyright ownership.
+ * 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
  *
- * UCAID licenses this file to you 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
  *
- * 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.
+ * 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.
  */
 
 /**
- * AbstractHandler.cpp
+ * handler/impl/AbstractHandler.cpp
  *
  * Base class for handlers based on a DOMPropertySet.
  */
@@ -38,6 +32,7 @@
 #include "util/SPConstants.h"
 #include "util/PathResolver.h"
 #include "util/TemplateParameters.h"
+#include "util/URLEncoder.h"
 
 #include <vector>
 #include <fstream>
@@ -49,7 +44,6 @@
 #include <xmltooling/util/URLEncoder.h>
 
 #include <xmltooling/XMLToolingConfig.h>
-#include <xmltooling/util/URLEncoder.h>
 
 using namespace shibsp;
 using namespace xmltooling;
@@ -804,9 +798,10 @@ pair<bool,bool> AbstractHandler::getBool(const char* name, const HTTPRequest& re
     
     const SPRequest* sprequest = dynamic_cast<const SPRequest*>(&request);
     if (sprequest && (type & HANDLER_PROPERTY_MAP)) {
-        pair<bool,bool> ret = sprequest->getRequestSettings().first->getBool(name);
-        if (ret.first)
-            return ret;
+        if (sprequest->getRequestSettings().first->hasProperty(name)) {
+            // The default won't matter since we've already verified the property "exists".
+            return make_pair(true, sprequest->getRequestSettings().first->getBool(name, false));
+        }
     }
 
     if (type & HANDLER_PROPERTY_FIXED) {
@@ -826,9 +821,9 @@ pair<bool,const char*> AbstractHandler::getString(const char* name, const HTTPRe
     
     const SPRequest* sprequest = dynamic_cast<const SPRequest*>(&request);
     if (sprequest && (type & HANDLER_PROPERTY_MAP)) {
-        pair<bool,const char*> ret = sprequest->getRequestSettings().first->getString(name);
-        if (ret.first)
-            return ret;
+        const char* ret = sprequest->getRequestSettings().first->getString(name);
+        if (ret)
+            return make_pair(true, ret);
     }
 
     if (type & HANDLER_PROPERTY_FIXED) {
@@ -854,9 +849,10 @@ pair<bool,unsigned int> AbstractHandler::getUnsignedInt(const char* name, const
     
     const SPRequest* sprequest = dynamic_cast<const SPRequest*>(&request);
     if (sprequest && (type & HANDLER_PROPERTY_MAP)) {
-        pair<bool,unsigned int> ret = sprequest->getRequestSettings().first->getUnsignedInt(name);
-        if (ret.first)
-            return ret;
+        if (sprequest->getRequestSettings().first->hasProperty(name)) {
+            // The default won't matter since we've already verified the property "exists".
+            return make_pair(true, sprequest->getRequestSettings().first->getUnsignedInt(name, 0));
+        }
     }
 
     if (type & HANDLER_PROPERTY_FIXED) {
@@ -876,9 +872,10 @@ pair<bool,int> AbstractHandler::getInt(const char* name, const HTTPRequest& requ
     
     const SPRequest* sprequest = dynamic_cast<const SPRequest*>(&request);
     if (sprequest && (type & HANDLER_PROPERTY_MAP)) {
-        pair<bool,int> ret = sprequest->getRequestSettings().first->getInt(name);
-        if (ret.first)
-            return ret;
+        if (sprequest->getRequestSettings().first->hasProperty(name)) {
+            // The default won't matter since we've already verified the property "exists".
+            return make_pair(true, sprequest->getRequestSettings().first->getInt(name, 0));
+        }
     }
 
     if (type & HANDLER_PROPERTY_FIXED) {
diff --git a/shibsp/handler/impl/RemotedHandler.cpp b/shibsp/handler/impl/RemotedHandler.cpp
index 20290c06..90c732aa 100644
--- a/shibsp/handler/impl/RemotedHandler.cpp
+++ b/shibsp/handler/impl/RemotedHandler.cpp
@@ -1,21 +1,15 @@
 /**
- * Licensed to the University Corporation for Advanced Internet
- * Development, Inc. (UCAID) under one or more contributor license
- * agreements. See the NOTICE file distributed with this work for
- * additional information regarding copyright ownership.
+ * 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
  *
- * UCAID licenses this file to you 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
  *
- * 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.
+ * 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.
  */
 
 /**
@@ -311,9 +305,9 @@ void RemotedHandler::addRemotedHeader(const char* header)
 DDF RemotedHandler::send(const SPRequest& request, DDF& in) const
 {
     // Capture and forward entityIDSelf content setting, if set.
-    pair<bool, const char*> entityID = request.getRequestSettings().first->getString("entityIDSelf");
-    if (entityID.first) {
-        string s(entityID.second);
+    const char* entityID = request.getRequestSettings().first->getString("entityIDSelf");
+    if (entityID) {
+        string s(entityID);
         string::size_type pos = s.find("$hostname");
         if (pos != string::npos)
             s.replace(pos, 9, request.getHostname());
diff --git a/shibsp/handler/impl/SAMLDSSessionInitiator.cpp b/shibsp/handler/impl/SAMLDSSessionInitiator.cpp
index 7bbfa744..132263df 100644
--- a/shibsp/handler/impl/SAMLDSSessionInitiator.cpp
+++ b/shibsp/handler/impl/SAMLDSSessionInitiator.cpp
@@ -1,21 +1,15 @@
 /**
- * Licensed to the University Corporation for Advanced Internet
- * Development, Inc. (UCAID) under one or more contributor license
- * agreements. See the NOTICE file distributed with this work for
- * additional information regarding copyright ownership.
+ * 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
  *
- * UCAID licenses this file to you 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
  *
- * 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.
+ * 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.
  */
 
 /**
@@ -144,7 +138,10 @@ pair<bool,long> SAMLDSSessionInitiator::run(SPRequest& request, string& entityID
 
         pair<bool,bool> passopt = getBool("isPassive", request, HANDLER_PROPERTY_MAP|HANDLER_PROPERTY_FIXED);
         isPassive = passopt.first && passopt.second;
-        discoveryURL = request.getRequestSettings().first->getString("discoveryURL");
+        discoveryURL.second = request.getRequestSettings().first->getString("discoveryURL");
+        if (discoveryURL.second) {
+            discoveryURL.first = true;
+        }
     }
 
     if (!discoveryURL.first)
@@ -214,9 +211,9 @@ pair<bool,long> SAMLDSSessionInitiator::run(SPRequest& request, string& entityID
             returnURL = returnURL + "&target=" + urlenc->encode(target.c_str());
          // Preserve designated request settings on the URL.
          for (vector<string>::const_iterator opt = m_preservedOptions.begin(); opt != m_preservedOptions.end(); ++ opt) {
-             prop = request.getRequestSettings().first->getString(opt->c_str());
-             if (prop.first)
-                 returnURL = returnURL + '&' + (*opt) + '=' + urlenc->encode(prop.second);
+             const char* optval = request.getRequestSettings().first->getString(opt->c_str());
+             if (optval)
+                 returnURL = returnURL + '&' + (*opt) + '=' + urlenc->encode(optval);
          }
     }
 
diff --git a/shibsp/handler/impl/SessionHandler.cpp b/shibsp/handler/impl/SessionHandler.cpp
index 8b11c5dd..558353d1 100644
--- a/shibsp/handler/impl/SessionHandler.cpp
+++ b/shibsp/handler/impl/SessionHandler.cpp
@@ -174,8 +174,8 @@ pair<bool,long> SessionHandler::doJSON(SPRequest& request) const
         json_safe(s, session->getProtocol());
     }
 
-    pair<bool,bool> stdvars = request.getRequestSettings().first->getBool("exportStdVars");
-    if (!stdvars.first || stdvars.second) {
+    bool stdvars = request.getRequestSettings().first->getBool("exportStdVars", true);
+    if (stdvars) {
         if (session->getEntityID()) {
             s << ", \"identity_provider\": ";
             json_safe(s, session->getEntityID());
@@ -287,8 +287,8 @@ pair<bool,long> SessionHandler::doHTML(SPRequest& request) const
     s << "<strong>Client Address:</strong> " << (session->getClientAddress() ? session->getClientAddress() : "(none)") << endl;
     s << "<strong>SSO Protocol:</strong> " << (session->getProtocol() ? session->getProtocol() : "(none)") << endl;
 
-    pair<bool,bool> stdvars = request.getRequestSettings().first->getBool("exportStdVars");
-    if (!stdvars.first || stdvars.second) {
+    bool stdvars = request.getRequestSettings().first->getBool("exportStdVars", true);
+    if (stdvars) {
         s << "<strong>Identity Provider:</strong> " << (session->getEntityID() ? session->getEntityID() : "(none)") << endl;
         s << "<strong>Authentication Time:</strong> " << (session->getAuthnInstant() ? session->getAuthnInstant() : "(none)") << endl;
         s << "<strong>Authentication Context Class:</strong> " << (session->getAuthnContextClassRef() ? session->getAuthnContextClassRef() : "(none)") << endl;
@@ -315,19 +315,17 @@ pair<bool,long> SessionHandler::doHTML(SPRequest& request) const
 
         if (m_values) {
             // Default delimiter is semicolon but is now configurable.
-            pair<bool,const char*> delim = request.getRequestSettings().first->getString("attributeValueDelimiter");
-            if (!delim.first)
-                delim.second = ";";
-            size_t delim_len = strlen(delim.second);
+            const char* delim = request.getRequestSettings().first->getString("attributeValueDelimiter", ";");
+            size_t delim_len = strlen(delim);
 
             const vector<string>& vals = a->second->getSerializedValues();
             for (vector<string>::const_iterator v = vals.begin(); v!=vals.end(); ++v) {
                 if (v != vals.begin() || a->first == key)
-                    s << delim.second;
-                string::size_type pos = v->find(delim.second, string::size_type(0));
+                    s << delim;
+                string::size_type pos = v->find(delim, string::size_type(0));
                 if (pos != string::npos) {
                     string value(*v);
-                    for (; pos != string::npos; pos = value.find(delim.second, pos)) {
+                    for (; pos != string::npos; pos = value.find(delim, pos)) {
                         value.insert(pos, "\\");
                         pos += delim_len + 1;
                     }
diff --git a/shibsp/handler/impl/SessionInitiator.cpp b/shibsp/handler/impl/SessionInitiator.cpp
index bef97f0c..47abb0e1 100644
--- a/shibsp/handler/impl/SessionInitiator.cpp
+++ b/shibsp/handler/impl/SessionInitiator.cpp
@@ -86,7 +86,11 @@ bool SessionInitiator::checkCompatibility(SPRequest& request, bool isHandler) co
     }
     else {
         // It doesn't really make sense to use isPassive with automated sessions, but...
-        pair<bool,bool> flagprop = request.getRequestSettings().first->getBool("isPassive");
+        pair<bool,bool> flagprop;
+        if (request.getRequestSettings().first->hasProperty("isPassive")) {
+            flagprop.second = request.getRequestSettings().first->getBool("isPassive", false);
+            flagprop.first = true;
+        }
         if (!flagprop.first)
             flagprop = getBool("isPassive");
         isPassive = (flagprop.first && flagprop.second);
@@ -116,8 +120,8 @@ pair<bool,long> SessionInitiator::run(SPRequest& request, bool isHandler) const
             entityID=request.getParameter("providerId");
     }
     if (!entityID || !*entityID) {
-        param = request.getRequestSettings().first->getString("entityID");
-        if (param.first)
+        param.second = request.getRequestSettings().first->getString("entityID");
+        if (param.second)
             entityID = param.second;
     }
     if (!entityID || !*entityID)
diff --git a/shibsp/handler/impl/StatusHandler.cpp b/shibsp/handler/impl/StatusHandler.cpp
index 261bcd48..d0ae6bf1 100644
--- a/shibsp/handler/impl/StatusHandler.cpp
+++ b/shibsp/handler/impl/StatusHandler.cpp
@@ -256,9 +256,9 @@ pair<bool,long> StatusHandler::run(SPRequest& request, bool isHandler) const
             const char* setting = request.getParameter("setting");
                 systemInfo(msg) << "<RequestSettings";
                 if (setting) {
-                    pair<bool, const char*> prop = settings.first->getString(setting);
-                    if (prop.first)
-                        msg << ' ' << setting << "='" << prop.second << "'";
+                    const char* prop = settings.first->getString(setting);
+                    if (prop)
+                        msg << ' ' << setting << "='" << prop << "'";
                 }
                 msg << '>' << target << "</RequestSettings>";
             msg << "<Status><OK/></Status>";
diff --git a/shibsp/impl/XMLAccessControl.cpp b/shibsp/impl/XMLAccessControl.cpp
index 96af278b..8a08c2ef 100644
--- a/shibsp/impl/XMLAccessControl.cpp
+++ b/shibsp/impl/XMLAccessControl.cpp
@@ -1,21 +1,15 @@
 /**
- * Licensed to the University Corporation for Advanced Internet
- * Development, Inc. (UCAID) under one or more contributor license
- * agreements. See the NOTICE file distributed with this work for
- * additional information regarding copyright ownership.
+ * 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
  *
- * UCAID licenses this file to you 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
  *
- * 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.
+ * 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.
  */
 
 /**
diff --git a/shibsp/impl/XMLRequestMapper.cpp b/shibsp/impl/XMLRequestMapper.cpp
index 8c7d2a6d..74e3a1a3 100644
--- a/shibsp/impl/XMLRequestMapper.cpp
+++ b/shibsp/impl/XMLRequestMapper.cpp
@@ -1,21 +1,15 @@
 /**
- * Licensed to the University Corporation for Advanced Internet
- * Development, Inc. (UCAID) under one or more contributor license
- * agreements. See the NOTICE file distributed with this work for
- * additional information regarding copyright ownership.
+ * 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
  *
- * UCAID licenses this file to you 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
  *
- * 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.
+ * 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
@@ -41,8 +35,8 @@
 #include <memory>
 #include <regex>
 #include <tuple>
+#include <utility>
 #include <boost/property_tree/ptree.hpp>
-#include <boost/shared_ptr.hpp>
 #include <boost/lexical_cast.hpp>
 #include <boost/tokenizer.hpp>
 #include <boost/algorithm/string.hpp>
@@ -66,7 +60,7 @@ namespace {
     {
     public:
         Override(bool unicodeAware=false) : m_unicodeAware(unicodeAware) {}
-        Override(bool unicodeAware, const ptree& pt, Category& log, const Override* base=nullptr);
+        Override(bool unicodeAware, ptree& pt, Category& log, const Override* base=nullptr);
         ~Override() {}
 
         const Override* locate(const HTTPRequest& request) const;
@@ -75,12 +69,14 @@ namespace {
         }
 
     protected:
-        void loadACL(const DOMElement* e, Category& log);
+        void loadACL(const ptree& pt, Category& log);
 
         bool m_unicodeAware;
-        map< string,unique_ptr<Override> > m_map;
-        vector< pair< std::regex,unique_ptr<Override> > > m_regexps;
-        vector< tuple< string,std::regex,unique_ptr<Override> > > m_queries;
+        // This uses shared_ptr to support multiple mappings for a given Override for Host.
+        // For Path, it's just overhead.
+        map< string,shared_ptr<Override> > m_map;
+        vector< pair< regex,unique_ptr<Override> > > m_regexps;
+        vector< tuple< string,boost::optional<regex>,unique_ptr<Override> > > m_queries;
 
     private:
         unique_ptr<AccessControl> m_acl;
@@ -89,34 +85,31 @@ namespace {
     class XMLRequestMapperImpl : public Override
     {
     public:
-        XMLRequestMapperImpl(const ptree& pt, Category& log);
+        XMLRequestMapperImpl(ptree& pt, Category& log);
         ~XMLRequestMapperImpl() {}
 
         const Override* findOverride(const char* vhost, const HTTPRequest& request) const;
-    };
 
-    static const char ACCESS_CONTROL_PROP_PATH[] = "AccessControl";
-    static const char AccessControlProvider[] = "AccessControlProvider";
-    static const char Host[] = "Host";
-    static const char HostRegex[] = "HostRegex";
-    static const char htaccess[] = "htaccess";
-    static const char Path[] = "Path";
-    static const char PathRegex[] = "PathRegex";
-    static const char Query[] = "Query";
-    static const char name[] = "name";
-    static const char regex[] = "regex";
-    static const char _type[] = "type";
+        void setTree(ptree* pt) {
+            m_tree.reset(pt);
+        }
+
+    private:
+        unique_ptr<ptree> m_tree;
+    };
 
 #if defined (_MSC_VER)
     #pragma warning( push )
     #pragma warning( disable : 4250 )
 #endif
 
+    static const char REQUEST_MAP_PROP_PATH[] = "RequestMap";
+
     class XMLRequestMapper : public RequestMapper, public ReloadableXMLFile
     {
     public:
         XMLRequestMapper(const ptree& pt)
-            : ReloadableXMLFile(ACCESS_CONTROL_PROP_PATH, pt, Category::getInstance(SHIBSP_LOGCAT ".RequestMapper")) {
+            : ReloadableXMLFile(REQUEST_MAP_PROP_PATH, pt, Category::getInstance(SHIBSP_LOGCAT ".RequestMapper")) {
             load(); // guarantees an exception or the map is loaded
         }
 
@@ -156,28 +149,37 @@ RequestMapper::~RequestMapper()
 {
 }
 
-void Override::loadACL(const DOMElement* e, Category& log)
+void Override::loadACL(const ptree& pt, Category& log)
 {
-    bool deprecationSupport = e ? XMLString::equals(e->getNamespaceURI(), shibspconstants::SHIB2SPCONFIG_NS) : false;
+    // This method looks for a supported child element to use as the basis
+    // of constructing an AccessControl plugin.
+
+    static const char ACCESS_CONTROL_PROP_PATH[] = "AccessControl";
+    static const char ACCESS_CONTROL_PROVIDER_PROP_PATH[] = "AccessControlProvider";
+    static const char HTACCESS_PROP_PATH[] = "htaccess";
+    static const char TYPE_PROP_PATH[] = "<xmlattr>.type";
+
     try {
-        const DOMElement* acl = XMLHelper::getFirstChildElement(e,htaccess);
+        boost::optional<const ptree&> acl = pt.get_child_optional(HTACCESS_PROP_PATH);
         if (acl) {
             log.info("building Apache htaccess AccessControl provider...");
-            m_acl.reset(SPConfig::getConfig().AccessControlManager.newPlugin(HT_ACCESS_CONTROL,acl, deprecationSupport));
+            m_acl.reset(AgentConfig::getConfig().AccessControlManager.newPlugin(HT_ACCESS_CONTROL, acl.get(), false));
         }
         else {
-            acl = XMLHelper::getFirstChildElement(e,_AccessControl);
+            acl = pt.get_child_optional(ACCESS_CONTROL_PROP_PATH);
             if (acl) {
                 log.info("building XML-based AccessControl provider...");
-                m_acl.reset(SPConfig::getConfig().AccessControlManager.newPlugin(XML_ACCESS_CONTROL,acl, deprecationSupport));
+                // TODO: this is tenative, but it seems like we need to pass in the parent tree to allow it to
+                // walk down to the "expected" element, but TBD.
+                m_acl.reset(AgentConfig::getConfig().AccessControlManager.newPlugin(XML_ACCESS_CONTROL, pt, false));
             }
             else {
-                acl = XMLHelper::getFirstChildElement(e,AccessControlProvider);
+                acl = pt.get_child_optional(ACCESS_CONTROL_PROVIDER_PROP_PATH);
                 if (acl) {
-                    string t(XMLHelper::getAttrString(acl, nullptr, _type));
+                    string t(pt.get(TYPE_PROP_PATH, ""));
                     if (!t.empty()) {
                         log.info("building AccessControl provider of type %s...", t.c_str());
-                        m_acl.reset(SPConfig::getConfig().AccessControlManager.newPlugin(t.c_str(), acl, deprecationSupport));
+                        m_acl.reset(AgentConfig::getConfig().AccessControlManager.newPlugin(t.c_str(), acl.get(), false));
                     }
                     else {
                         throw ConfigurationException("<AccessControlProvider> missing type attribute.");
@@ -186,159 +188,177 @@ void Override::loadACL(const DOMElement* e, Category& log)
             }
         }
     }
-    catch (const std::exception& ex) {
+    catch (const exception& ex) {
         log.crit("exception building AccessControl provider: %s", ex.what());
         m_acl.reset(new AccessControlDummy());
     }
 }
 
-Override::Override(bool unicodeAware, const DOMElement* e, Category& log, const Override* base)
+Override::Override(bool unicodeAware, ptree& pt, Category& log, const Override* base)
     : m_unicodeAware(unicodeAware)
 {
-    // Load the property set.
-    xmltooling::QName unsetter(nullptr, "unset");
-    load(e, nullptr, this, nullptr, &unsetter);
+    // Load the <xmlattr> tree as a property set.
+    const boost::optional<ptree&> xmlattrs = pt.get_child_optional("<xmlattr>");
+    if (xmlattrs) {
+        load(xmlattrs.get(), "unset");
+    }
     setParent(base);
 
     // Load any AccessControl provider.
-    loadACL(e, log);
+    loadACL(pt, log);
 
-    // Handle nested Paths.
-    DOMElement* path = XMLHelper::getFirstChildElement(e, Path);
-    for (int i = 1; path; ++i, path = XMLHelper::getNextSiblingElement(path, Path)) {
-        const XMLCh* n = path->getAttributeNS(nullptr,name);
+    static const char PATH_PROP_PATH[] = "Path";
+    static const char PATH_REGEX_PROP_PATH[] = "PathRegex";
+    static const char QUERY_PROP_PATH[] = "Query";
+    static const char NAME_PROP_PATH[] = "<xmlattr>.name";
+    static const char REGEX_PROP_PATH[] = "<xmlattr>.regex";
 
-        // Skip any leading slashes.
-        while (n && *n == chForwardSlash)
-            n++;
+    // Process the various child types.
 
-        // Check for empty name.
-        if (!n || !*n) {
-            log.warn("skipping Path element (%d) with empty name attribute", i);
-            continue;
-        }
+    for (auto& child : pt) {
+        if (child.first == PATH_PROP_PATH) {
+            const string nameprop(getString("name", ""));
+            const char* n = nameprop.c_str();
 
-        // Check for an embedded slash.
-        int slash = XMLString::indexOf(n, chForwardSlash);
-        if (slash > 0) {
-            // Copy the first path segment.
-            xstring namebuf;
-            for (int pos = 0; pos < slash; ++pos)
-                namebuf += n[pos];
-
-            // 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(path->getNamespaceURI(), Path);
-                newpath->setAttributeNS(nullptr, name, namebuf.c_str());
-                path->setAttributeNS(nullptr, name, n);
-                path->getParentNode()->replaceChild(newpath, path);
-                newpath->appendChild(path);
-
-                // Repoint our locals at the new parent.
-                path = newpath;
-                n = path->getAttributeNS(nullptr, name);
+            // Skip any leading slashes.
+            while (n && *n == '/')
+                n++;
+
+            // Check for empty name.
+            if (!n || !*n) {
+                log.warn("skipping Path element with empty name attribute");
+                continue;
             }
-            else {
-                // All we had was a pathname with trailing slash(es), so just reset it without them.
-                path->setAttributeNS(nullptr, name, namebuf.c_str());
-                n = path->getAttributeNS(nullptr, name);
+
+            // Check for an embedded slash.
+            const char* slash = strchr(n, '/');
+            if (slash) {
+                // Copy the first path segment.
+                string namebuf;
+                for (const char* pos = n; pos < slash; ++pos) {
+                    namebuf += *pos;
+                }
+
+                // Move past the slash in the original pathname.
+                n = slash + 1;
+
+                // Skip any leading slashes again.
+                while (*n == '/')
+                    ++n;
+
+                if (*n) {
+                    // TODO: Seriously doubt any of this will work, but fixing it will
+                    // require substantial redesign.
+
+                    // namebuf has the segment to process at "this" level
+                    // The "new" injected Path Oevrride containing it would have no other
+                    // attributes since the settings in the slash-containing Path would
+                    // apply only to the final "leaf" of the Path's directory tree.
+
+                    // The currently iterated pair's second member is the original Path
+                    // tree with the multi-part pathname and all the settings under <xmlattr>.
+                    // We would have to make the iterated pair's second member be a tree
+                    // containing the namebuf path segment under <xmlattr>.name and containing
+                    // the original tree with a modified <xmlattr>.name set to *n under a child named Path.
+
+                    // Copy the old child tree into a local variable and adjust its name.
+                    ptree old_child(child.second);
+                    old_child.put(NAME_PROP_PATH, n);
+
+                    // Create a new tree with just the namebuf prefix and the new child under it.
+                    ptree new_child;
+                    new_child.put(NAME_PROP_PATH, namebuf);
+                    new_child.add_child(PATH_PROP_PATH, old_child);
+
+                    // Replace the original child iterated with the "new" child.
+                    child.second = new_child;
+                    
+                    // Repoint our locals at the new parent.
+                    n = namebuf.c_str();    // seems like this shouldn't be needed
+                }
+                else {
+                    // All we had was a pathname with trailing slash(es), so just reset it without them.
+                    child.second.put(NAME_PROP_PATH, namebuf);
+                    n = namebuf.c_str();    // seems like this shouldn't be needed
+                }
             }
-        }
 
-        char* dup = nullptr;
-        try {
-            boost::shared_ptr<Override> o(new Override(m_unicodeAware, path, log, this));
-            if (m_unicodeAware) {
-                //dup = toUTF8(o->getXMLString("name").second, true /* use malloc */);
-                dup = strdup(o->getString("name").second);
+            shared_ptr<Override> o(new Override(m_unicodeAware, child.second, log, this));
+            string mutable_path = o->getString("name", "");
+            if (mutable_path.empty()) {
+                throw new ConfigurationException("Path element did not contain a name attribute.");
             }
-            else {
-                dup = strdup(o->getString("name").second);
-                for (char* pch = dup; *pch; ++pch)
-                    *pch = tolower(*pch);
+
+            // The thinking here is that the Unicode flag tells it to treat the
+            // Path name as UTF-8, and thus can't be safely case-folded.
+            if (!m_unicodeAware) {
+                boost::algorithm::to_lower(mutable_path);
             }
-            if (m_map.count(dup)) {
-                log.warn("skipping duplicate Path element (%s)", dup);
+
+            if (m_map.count(mutable_path)) {
+                log.warn("skipping duplicate Path element (%s)", mutable_path.c_str());
             }
             else {
-                m_map[dup] = o;
-                log.debug("added Path mapping (%s)", dup);
+                m_map[mutable_path] = o;
+                log.debug("added Path mapping (%s)", mutable_path.c_str());
             }
-            free(dup);
-        }
-        catch (const std::exception&) {
-            free(dup);
-            throw;
         }
-    }
-
-    if (!XMLString::equals(e->getLocalName(), PathRegex)) {
-        // Handle nested PathRegexs.
-        path = XMLHelper::getFirstChildElement(e, PathRegex);
-        for (int i = 1; path; ++i, path = XMLHelper::getNextSiblingElement(path, PathRegex)) {
-            const XMLCh* n = path->getAttributeNS(nullptr, regex);
-            if (!n || !*n) {
-                log.warn("skipping PathRegex element (%d) with empty regex attribute",i);
+        else if (child.first == PATH_REGEX_PROP_PATH) {
+            const string regexpprop(child.second.get(REGEX_PROP_PATH, ""));
+            if (regexpprop.empty()) {
+                log.warn("skipping PathRegex element with empty regex attribute");
                 continue;
             }
 
-            boost::shared_ptr<Override> o(new Override(m_unicodeAware, path, log, this));
-
-            bool caseSensitive;
-            if (path && path->hasAttributeNS(nullptr, ignoreCase)) {
-                // In this one case, we've left ignoreCase reversed (true means case sensitive, false means insensitive).
-                // This was to protect people who followed the security advisory for SSPCPP-691 and reversed their setting.
-                SPConfig::getConfig().deprecation().error("ignoreCase attribute in PathRegex element will be interpreted backwards. Replace with caseSensitive");
-                caseSensitive = XMLHelper::getAttrBool(path, true, ignoreCase);
-            } else {
-                // If the old ignoreCase setting isn't set, then we just process normally.
-                caseSensitive = XMLHelper::getCaseSensitive(path, false);
-            }
+            unique_ptr<Override> o(new Override(m_unicodeAware, child.second, log, this));
+
             try {
-                boost::shared_ptr<RegularExpression> re(new RegularExpression(n, caseSensitive ? &chNull : caseInsensitiveOption));
-                m_regexps.push_back(make_pair(re, o));
+                // TODO: more flag options, particular for dialect.
+                regex::flag_type flags = regex_constants::optimize;
+                if (getBool("caseSensitive", false)) {
+                    flags |= regex_constants::icase;
+                }
+                regex exp(regexpprop, flags);
+                m_regexps.push_back(make_pair(exp, std::move(o)));
+                log.debug("added <PathRegex> mapping (%s)", regexpprop.c_str());
             }
-            catch (const XMLException& ex) {
-                auto_ptr_char tmp(ex.getMessage());
-                log.error("caught exception while parsing PathRegex regular expression (%d): %s", i, tmp.get());
+            catch (const regex_error& e) {
+                log.error("error parsing PathRegex regular expression: %s", e.what());
                 throw ConfigurationException("Invalid regular expression in PathRegex element.");
             }
-
-            if (log.isDebugEnabled())
-                log.debug("added <PathRegex> mapping (%s)", o->getString("regex").second);
         }
-    }
+        else if (child.first == QUERY_PROP_PATH) {
+            string nameprop(getString("name", ""));
+            if (nameprop.empty()) {
+                log.warn("skipping Query element with empty name attribute");
+                continue;
+            }
 
-    // Handle nested Querys.
-    path = XMLHelper::getFirstChildElement(e, Query);
-    for (int i = 1; path; ++i, path = XMLHelper::getNextSiblingElement(path, Query)) {
-        const XMLCh* n = path->getAttributeNS(nullptr, name);
-        if (!n || !*n) {
-            log.warn("skipping Query element (%d) with empty name attribute",i);
-            continue;
-        }
-        auto_ptr_char ntemp(n);
-        const XMLCh* v = path->getAttributeNS(nullptr, regex);
+            unique_ptr<Override> o(new Override(m_unicodeAware, child.second, log, this));
 
-        try {
-            boost::shared_ptr<Override> o(new Override(m_unicodeAware, path, log, this));
-            boost::shared_ptr<RegularExpression> re((v && *v) ? new RegularExpression(v) : nullptr);
-            m_queries.push_back(boost::make_tuple(string(ntemp.get()), re, o));
-        }
-        catch (const XMLException& ex) {
-            auto_ptr_char tmp(ex.getMessage());
-            log.error("caught exception while parsing Query regular expression (%d): %s", i, tmp.get());
-            throw ConfigurationException("Invalid regular expression in Query element.");
-        }
+            string regexpprop(getString("regex", ""));
+
+            if (regexpprop.empty()) {
+                m_queries.push_back(make_tuple(nameprop, boost::optional<regex>(), std::move(o)));
+            }
+            else {
+                try {
+                    // TODO: more flag options, particular for dialect.
+                    regex::flag_type flags = regex_constants::optimize;
+                    if (getBool("caseSensitive", false)) {
+                        flags |= regex_constants::icase;
+                    }
+                    regex exp(regexpprop, flags);
 
-        log.debug("added <Query> mapping (%s)", ntemp.get());
+                    m_queries.push_back(make_tuple(nameprop, boost::optional<regex>(exp), std::move(o)));
+                    log.debug("added <Query> mapping (%s)", nameprop.c_str());
+                }
+                catch (const regex_error& e) {
+                    log.error("caught exception while parsing Query regular expression: %s", e.what());
+                    throw ConfigurationException("Invalid regular expression in Query element.");
+                }
+            }
+        }
     }
 }
 
@@ -389,15 +409,15 @@ const Override* Override::locate(const HTTPRequest& request) const
     path = dup.c_str();
 
     // Tokenize the path by segment and try and map each segment.
-    tokenizer< char_separator<char> > tokens(dup, char_separator<char>("/"));
-    for (tokenizer< char_separator<char> >::iterator token = tokens.begin(); token != tokens.end(); ++token) {
+    boost::tokenizer< boost::char_separator<char> > tokens(dup, boost::char_separator<char>("/"));
+    for (const string& token : tokens) {
 
-        string tokendup(*token);
+        string tokendup(token);
         if (!m_unicodeAware) {
-            to_lower(tokendup);
+            boost::algorithm::to_lower(tokendup);
         }
 
-        map< string,boost::shared_ptr<Override> >::const_iterator i = o->m_map.find(tokendup);
+        const auto& i = o->m_map.find(tokendup);
         if (i == o->m_map.end())
             break;  // Once there's no match, we've consumed as much of the path as possible here.
         // We found a match, so reset the settings pointer.
@@ -412,48 +432,38 @@ const Override* Override::locate(const HTTPRequest& request) const
 
     // If there's anything left, we try for a regex match on the rest of the path minus the query string.
     if (*path) {
-        for (vector< pair< boost::shared_ptr<RegularExpression>,boost::shared_ptr<Override> > >::const_iterator re = o->m_regexps.begin(); re != o->m_regexps.end(); ++re) {
-            try {
-                if (re->first->matches(path)) {
-                    o = re->second.get();
-                    break;
-                }
-            } catch (const XMLException& ex) {
-                auto_ptr_char tmp(ex.getMessage());
-                throw ConfigurationException("Caught exception while matching PathRegex : $1", params(1, tmp.get()));
+        for (const auto& re : m_regexps) {
+            if (regex_match(path, re.first)) {
+                o = re.second.get();
+                break;
             }
         }
     }
 
     // Finally, check for query string matches. This is another "unrolled" recursive descent in a loop.
-    // To avoid consuming any POST data, we use a dedicated CGIParser.
+    // To avoid consuming any POST data, we use a dedicated CGIParser that only consumes the query string.
     if (!o->m_queries.empty()) {
         bool descended;
         CGIParser cgi(request, true);
         do {
             descended = false;
-            for (vector< boost::tuple< string,boost::shared_ptr<RegularExpression>,boost::shared_ptr<Override> > >::const_iterator q = o->m_queries.begin(); !descended && q != o->m_queries.end(); ++q) {
-                pair<CGIParser::walker,CGIParser::walker> vals = cgi.getParameters(q->get<0>().c_str());
+            for (auto q = o->m_queries.begin(); !descended && q != o->m_queries.end(); ++q) {
+                pair<CGIParser::walker,CGIParser::walker> vals = cgi.getParameters(get<0>(*q).c_str());
                 if (vals.first != vals.second) {
-                    if (q->get<1>()) {
+                    if (get<1>(*q)) {
                         // We have to match one of the values.
                         while (vals.first != vals.second) {
-                            try{
-                                if (q->get<1>()->matches(vals.first->second)) {
-                                    o = q->get<2>().get();
-                                    descended = true;
-                                    break;
-                                }
-                            } catch (XMLException& ex) {
-                                auto_ptr_char tmp(ex.getMessage());
-                                throw ConfigurationException("Caught exception while matching Query regular expression : $1", params(1, tmp.get()));
+                            if (regex_match(vals.first->second, get<1>(*q).get())) {
+                                o = get<2>(*q).get();
+                                descended = true;
+                                break;
                             }
                             ++vals.first;
                         }
                     }
                     else {
                         // The simple presence of the parameter is sufficient to match.
-                        o = q->get<2>().get();
+                        o = get<2>(*q).get();
                         descended = true;
                     }
                 }
@@ -464,163 +474,150 @@ const Override* Override::locate(const HTTPRequest& request) const
     return o;
 }
 
-XMLRequestMapperImpl::XMLRequestMapperImpl(const DOMElement* e, Category& log) : m_document(nullptr)
+XMLRequestMapperImpl::XMLRequestMapperImpl(ptree& pt, Category& log)
 {
-    static const XMLCh _RequestMap[] =  UNICODE_LITERAL_10(R,e,q,u,e,s,t,M,a,p);
-
-    if (e && !XMLHelper::isNodeNamed(e, shibspconstants::SHIB2SPCONFIG_NS, _RequestMap)
-          && !XMLHelper::isNodeNamed(e, shibspconstants::SHIB3SPCONFIG_NS, _RequestMap)) {
-        throw ConfigurationException("XML RequestMapper requires conf:RequestMap at root of configuration.");
-    }
-
-    if (XMLString::equals(e->getNamespaceURI(), shibspconstants::SHIB2SPCONFIG_NS)) {
-        SPConfig::getConfig().deprecation().warn("legacy V2 configuration");
-    }
-
     // Load the property set.
-    xmltooling::QName unsetter(nullptr, "unset");
-    load(e, nullptr, this, nullptr, &unsetter);
+    load(pt, "unset");
 
+    // This probably will go away at some point but for now just leaving it.
     // Inject "default" app ID if not explicit.
-    if (!getString("applicationId").first)
-        setProperty("applicationId", "default");
+    if (!getString("applicationId")) {
+        pt.put("applicationId", "default");
+    }
 
     // Load any AccessControl provider.
-    loadACL(e, log);
+    loadACL(pt, log);
 
-    pair<bool,bool> unicodeAware = getBool("unicodeAware");
-    m_unicodeAware = (unicodeAware.first && unicodeAware.second);
+    m_unicodeAware = getBool("unicodeAware", false);
 
-    // Loop over the HostRegex elements.
-    const DOMElement* host = XMLHelper::getFirstChildElement(e, HostRegex);
-    for (int i = 1; host; ++i, host = XMLHelper::getNextSiblingElement(host, HostRegex)) {
-        const XMLCh* n = host->getAttributeNS(nullptr,regex);
-        if (!n || !*n) {
-            log.warn("Skipping HostRegex element (%d) with empty regex attribute", i);
-            continue;
-        }
+    static const char HOST_PROP_PATH[] = "Host";
+    static const char HOST_REGEX_PROP_PATH[] = "HostRegex";
 
-        boost::shared_ptr<Override> o(new Override(m_unicodeAware, host, log, this));
+    // Loop over the HostRegex elements.
+    for (auto& child : pt) {
+        if (child.first == HOST_REGEX_PROP_PATH) {
+            string regexprop(getString("regex", ""));
+            if (regexprop.empty()) {
+                log.warn("Skipping HostRegex element with empty regex attribute");
+                continue;
+            }
 
-        const bool caseSensitive = XMLHelper::getCaseSensitive(host, false);
-        try {
-            boost::shared_ptr<RegularExpression> re(
-                new RegularExpression(n, caseSensitive ? &chNull : caseInsensitiveOption)
-                );
-            m_regexps.push_back(make_pair(re, o));
-        }
-        catch (const XMLException& ex) {
-            auto_ptr_char tmp(ex.getMessage());
-            log.error("caught exception while parsing HostRegex regular expression (%d): %s", i, tmp.get());
-        }
+            unique_ptr<Override> o(new Override(m_unicodeAware, child.second, log, this));
 
-        log.debug("Added <HostRegex> mapping for %s", m_regexps.back().second->getString("regex").second);
-    }
+            try {
+                regex::flag_type flags = regex_constants::optimize;
+                if (getBool("caseSensitive", false)) {
+                    flags |= regex_constants::icase;
+                }
+                regex exp(regexprop, flags);
+                m_regexps.push_back(make_pair(exp, std::move(o)));
+            }
+            catch (const regex_error& e) {
+                log.error("caught exception while parsing HostRegex regular expression: %s", e.what());
+            }
 
-    // Loop over the Host elements.
-    host = XMLHelper::getFirstChildElement(e, Host);
-    for (int i = 1; host; ++i, host = XMLHelper::getNextSiblingElement(host, Host)) {
-        const XMLCh* n=host->getAttributeNS(nullptr,name);
-        if (!n || !*n) {
-            log.warn("Skipping Host element (%d) with empty name attribute", i);
-            continue;
+            log.debug("Added <HostRegex> mapping for %s", regexprop.c_str());
         }
+        else if (child.first == HOST_PROP_PATH) {
+            string name(getString("name", ""));
+            if (name.empty()) {
+                log.warn("Skipping Host element with empty name attribute");
+                continue;
+            }
 
-        boost::shared_ptr<Override> o(new Override(m_unicodeAware, 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");
+            shared_ptr<Override> o(new Override(m_unicodeAware, child.second, log, this));
+            const char* scheme = o->getString("scheme");
+            const char* port = o->getString("port");
 
-        string dup(name.first ? name.second : "");
-        to_lower(dup);
+            boost::algorithm::to_lower(name);
 
-        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 && port) {
+                // No scheme, but a port, so assume http.
+                scheme = "http";
+            }
+            else if (scheme && !port) {
+                // Scheme, no port, so default it.
+                // XXX Use getservbyname instead?
+                if (!strcmp(scheme,"http"))
+                    port = "80";
+                else if (!strcmp(scheme,"https"))
+                    port = "443";
+                else if (!strcmp(scheme,"ftp"))
+                    port = "21";
+                else if (!strcmp(scheme,"ldap"))
+                    port = "389";
+                else if (!strcmp(scheme,"ldaps"))
+                    port = "636";
+            }
+
+            if (scheme) {
+                string url(scheme);
+                url = url + "://" + name;
+
+                // Is this the default port?
+                if ((!strcmp(scheme,"http") && !strcmp(port,"80")) ||
+                    (!strcmp(scheme,"https") && !strcmp(port,"443")) ||
+                    (!strcmp(scheme,"ftp") && !strcmp(port,"21")) ||
+                    (!strcmp(scheme,"ldap") && !strcmp(port,"389")) ||
+                    (!strcmp(scheme,"ldaps") && !strcmp(port,"636"))) {
+                    // First store a port-less version.
+                    if (m_map.count(url)) {
+                        log.warn("Skipping duplicate Host element (%s)", url.c_str());
+                        continue;
+                    }
+                    m_map[url] = o;
+                    log.debug("Added <Host> mapping for %s", url.c_str());
 
-        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.
+                    // Now append the port. The shared_ptr should refcount the Override to avoid double deletes.
+                    url=url + ':' + port;
+                    m_map[url] = o;
+                    log.debug("Added <Host> mapping for %s", url.c_str());
+                }
+                else {
+                    url=url + ':' + port;
+                    if (m_map.count(url)) {
+                        log.warn("Skipping duplicate Host element (%s)", url.c_str());
+                        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 += name;
                 if (m_map.count(url)) {
-                    log.warn("Skipping duplicate Host element (%s)",url.c_str());
+                    log.warn("Skipping duplicate Host element (%s)", url.c_str());
                     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());
 
-                // Now append the port. The shared_ptr should refcount the Override to avoid double deletes.
-                url=url + ':' + port.second;
-                m_map[url] = o;
-                log.debug("Added <Host> mapping for %s",url.c_str());
-            }
-            else {
-                url=url + ':' + port.second;
+                url += ":80";
                 if (m_map.count(url)) {
-                    log.warn("Skipping duplicate Host element (%s)",url.c_str());
+                    log.warn("Skipping duplicate Host element (%s)", url.c_str());
                     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 += dup;
-            if (m_map.count(url)) {
-                log.warn("Skipping duplicate Host element (%s)",url.c_str());
-                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 += ":80";
-            if (m_map.count(url)) {
-                log.warn("Skipping duplicate Host element (%s)",url.c_str());
-                continue;
-            }
-            m_map[url] = o;
-            log.debug("Added <Host> mapping for %s",url.c_str());
-
-            url = "https://" + dup;
-            if (m_map.count(url)) {
-                log.warn("Skipping duplicate Host element (%s)",url.c_str());
-                continue;
-            }
-            m_map[url] = o;
-            log.debug("Added <Host> mapping for %s",url.c_str());
+                url = "https://" + name;
+                if (m_map.count(url)) {
+                    log.warn("Skipping duplicate Host element (%s)", url.c_str());
+                    continue;
+                }
+                m_map[url] = o;
+                log.debug("Added <Host> mapping for %s", url.c_str());
 
-            url += ":443";
-            if (m_map.count(url)) {
-                log.warn("Skipping duplicate Host element (%s)",url.c_str());
-                continue;
+                url += ":443";
+                if (m_map.count(url)) {
+                    log.warn("Skipping duplicate Host element (%s)", url.c_str());
+                    continue;
+                }
+                m_map[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());
         }
     }
 }
@@ -628,60 +625,52 @@ XMLRequestMapperImpl::XMLRequestMapperImpl(const DOMElement* e, Category& log) :
 const Override* XMLRequestMapperImpl::findOverride(const char* vhost, const HTTPRequest& request) const
 {
     const Override* o = nullptr;
-    map< string,boost::shared_ptr<Override> >::const_iterator i = m_map.find(vhost);
+    const auto& i = m_map.find(vhost);
     if (i != m_map.end())
         o = i->second.get();
     else {
-        for (vector< pair< boost::shared_ptr<RegularExpression>,boost::shared_ptr<Override> > >::const_iterator re = m_regexps.begin(); !o && re != m_regexps.end(); ++re) {
-            try{
-                if (re->first->matches(vhost))
-                    o=re->second.get();
-            } catch (XMLException& ex) {
-                auto_ptr_char tmp(ex.getMessage());
-                throw ConfigurationException("Caught exception while matching HostRegex : $1", params(1, tmp.get()));
+        for (const auto& re : m_regexps) {
+            if (regex_match(vhost, re.first)) {
+                o = re.second.get();
             }
-
         }
     }
 
     return o ? o->locate(request) : this;
 }
 
-pair<bool,DOMElement*> XMLRequestMapper::background_load()
+pair<bool,ptree*> XMLRequestMapper::load() noexcept
 {
     // Load from source using base class.
-    pair<bool,DOMElement*> raw = ReloadableXMLFile::load();
+    pair<bool,ptree*> raw = ReloadableXMLFile::load();
+    if (!raw.second) {
+        return raw;
+    }
 
     // If we own it, wrap it.
-    XercesJanitor<DOMDocument> docjanitor(raw.first ? raw.second->getOwnerDocument() : nullptr);
+    unique_ptr<ptree> treejanitor(raw.first ? raw.second : nullptr);
 
-    //scoped_ptr<XMLRequestMapperImpl> impl(new XMLRequestMapperImpl(raw.second, m_log));
-    scoped_ptr<XMLRequestMapperImpl> impl(nullptr);
+    unique_ptr<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());
+    impl->setTree(treejanitor.release());
 
     // Perform the swap inside a lock.
-    if (m_lock)
-        m_lock->wrlock();
-    SharedLock locker(m_lock, false);
+#ifdef HAVE_CXX14
+    unique_lock<ReloadableXMLFile> locker(*this);
+#endif
     m_impl.swap(impl);
 
-    return make_pair(false,(DOMElement*)nullptr);
+    return make_pair(false,raw.second);
 }
 
 RequestMapper::Settings XMLRequestMapper::getSettings(const HTTPRequest& request) const
 {
-    try {
-        string normalizedhost(request.getHostname());
-        to_lower(normalizedhost);
-        string vhost = string(request.getScheme()) + "://" + normalizedhost + ':' + lexical_cast<string>(request.getPort());
-        const Override* o = m_impl->findOverride(vhost.c_str(), request);
-        return Settings(o, o->getAC());
-    }
-    catch (const XMLException& ex) {
-        auto_ptr_char tmp(ex.getMessage());
-        m_log.error("caught exception while locating content settings: %s", tmp.get());
-        throw ConfigurationException("XML-based RequestMapper failed to retrieve content settings.");
-    }
+    string normalizedhost(request.getHostname());
+    boost::algorithm::to_lower(normalizedhost);
+    string vhost = string(request.getScheme()) + "://" + normalizedhost + ':' + boost::lexical_cast<string>(request.getPort());
+    
+    const Override* o = m_impl->findOverride(vhost.c_str(), request);
+
+    return Settings(o, o->getAC());
 }
diff --git a/shibsp/util/BoostPropertySet.cpp b/shibsp/util/BoostPropertySet.cpp
index 7c290dff..48cf1bd6 100644
--- a/shibsp/util/BoostPropertySet.cpp
+++ b/shibsp/util/BoostPropertySet.cpp
@@ -77,6 +77,20 @@ void BoostPropertySet::load(const property_tree::ptree& pt, const char* unsetter
     }
 }
 
+bool BoostPropertySet::hasProperty(const char* name) const
+{
+    if (m_pt) {
+        bool ret = m_pt->get_child_optional(name).has_value();
+        if (ret) {
+            return ret;
+        }
+    }
+
+    if (m_parent && m_unset.find(name) == m_unset.end()) {
+        return m_parent->hasProperty(name);
+    }
+}
+
 bool BoostPropertySet::getBool(const char* name, bool defaultValue) const
 {
     if (m_pt) {
diff --git a/shibsp/util/BoostPropertySet.h b/shibsp/util/BoostPropertySet.h
index d14cecf6..c82fb31a 100644
--- a/shibsp/util/BoostPropertySet.h
+++ b/shibsp/util/BoostPropertySet.h
@@ -49,6 +49,7 @@ namespace shibsp {
         BoostPropertySet();
         virtual ~BoostPropertySet();
 
+        bool hasProperty(const char* name) const;
         bool getBool(const char* name, bool defaultValue) const;
         const char* getString(const char* name, const char* defaultValue=nullptr) const;
         unsigned int getUnsignedInt(const char* name, unsigned int defaultValue) const;
diff --git a/shibsp/util/PropertySet.h b/shibsp/util/PropertySet.h
index fd74c2ce..a405bb8b 100644
--- a/shibsp/util/PropertySet.h
+++ b/shibsp/util/PropertySet.h
@@ -116,6 +116,14 @@ namespace shibsp {
     public:
         virtual ~PropertySet2();
 
+        /**
+         * Gets whether a matching property exists.
+         * 
+         * @param name  property name
+         * @return true iff the named property exists
+         */
+        virtual bool hasProperty(const char* name) const=0;
+
         /**
          * Returns a boolean-valued property.
          * 
diff --git a/shibsp/util/ReloadableXMLFile.h b/shibsp/util/ReloadableXMLFile.h
index 7e9a1a45..9a41cceb 100644
--- a/shibsp/util/ReloadableXMLFile.h
+++ b/shibsp/util/ReloadableXMLFile.h
@@ -154,13 +154,13 @@ namespace shibsp {
          */
         void updateModificationTime(time_t t);
 
+        /** Logging object. */
+        Category& m_log;
+
     private:
         /** Root of configuration or of the pointer to the configuration. */
         const boost::property_tree::ptree& m_root;
 
-        /** Logging object. */
-        Category& m_log;
-
         /** Resource path. */
         std::string m_source;
 

-- 
To stop receiving notification emails like this one, please contact
the administrator of this repository.


More information about the commits mailing list