Clean Solaris build.
authorcantor <cantor@cb58f699-b61c-0410-a6fe-9272a202ed29>
Thu, 15 Nov 2007 22:02:26 +0000 (22:02 +0000)
committercantor <cantor@cb58f699-b61c-0410-a6fe-9272a202ed29>
Thu, 15 Nov 2007 22:02:26 +0000 (22:02 +0000)
git-svn-id: https://svn.middleware.georgetown.edu/cpp-sp/trunk@2620 cb58f699-b61c-0410-a6fe-9272a202ed29

26 files changed:
adfs/adfs.cpp
apache/mod_apache.cpp
shibsp/attribute/ScopedAttributeDecoder.cpp
shibsp/attribute/filtering/impl/AndMatchFunctor.cpp
shibsp/attribute/filtering/impl/NotMatchFunctor.cpp
shibsp/attribute/filtering/impl/OrMatchFunctor.cpp
shibsp/attribute/filtering/impl/XMLAttributeFilter.cpp
shibsp/attribute/resolver/impl/XMLAttributeExtractor.cpp
shibsp/handler/impl/AssertionLookup.cpp
shibsp/handler/impl/MetadataGenerator.cpp
shibsp/handler/impl/SAML2ArtifactResolution.cpp
shibsp/handler/impl/SAML2Logout.cpp
shibsp/handler/impl/SAML2LogoutInitiator.cpp
shibsp/handler/impl/SAML2NameIDMgmt.cpp
shibsp/handler/impl/SAML2SessionInitiator.cpp
shibsp/handler/impl/SAMLDSSessionInitiator.cpp
shibsp/handler/impl/Shib1SessionInitiator.cpp
shibsp/handler/impl/StatusHandler.cpp
shibsp/handler/impl/WAYFSessionInitiator.cpp
shibsp/impl/RemotedSessionCache.cpp
shibsp/impl/StorageServiceSessionCache.cpp
shibsp/impl/XMLRequestMapper.cpp
shibsp/impl/XMLServiceProvider.cpp
shibsp/remoting/impl/SocketListener.cpp
shibsp/util/DOMPropertySet.cpp
shibsp/util/TemplateParameters.cpp

index 833cc63..c06a203 100644 (file)
@@ -312,7 +312,7 @@ pair<bool,long> ADFSSessionInitiator::run(SPRequest& request, const char* entity
 {
     // We have to know the IdP to function.
     if (!entityID || !*entityID)
-        return make_pair(false,0);
+        return make_pair(false,0L);
 
     string target;
     const Handler* ACS=NULL;
@@ -339,11 +339,11 @@ pair<bool,long> ADFSSessionInitiator::run(SPRequest& request, const char* entity
     const vector<const Handler*>& handlers = app.getAssertionConsumerServicesByBinding(m_binding.get());
 
     // Index comes from request, or default set in the handler, or we just pick the first endpoint.
-    pair<bool,unsigned int> index = make_pair(false,0);
+    pair<bool,unsigned int> index(false,0);
     if (isHandler) {
         option = request.getParameter("acsIndex");
         if (option)
-            index = make_pair(true, atoi(option));
+            index = pair<bool,unsigned int>(true, atoi(option));
     }
     if (!index.first)
         index = getUnsignedInt("defaultACSIndex");
@@ -443,14 +443,14 @@ pair<bool,long> ADFSSessionInitiator::doRequest(
     }
     else if (!entity.second) {
         m_log.error("unable to locate ADFS-aware identity provider role for provider (%s)", entityID);
-        return make_pair(false,0);
+        return make_pair(false,0L);
     }
     const EndpointType* ep = EndpointManager<SingleSignOnService>(
         dynamic_cast<const IDPSSODescriptor*>(entity.second)->getSingleSignOnServices()
         ).getByBinding(m_binding.get());
     if (!ep) {
         m_log.error("unable to locate compatible SSO service for provider (%s)", entityID);
-        return make_pair(false,0);
+        return make_pair(false,0L);
     }
 
     preserveRelayState(app, httpResponse, relayState);
@@ -476,7 +476,7 @@ pair<bool,long> ADFSSessionInitiator::doRequest(
 
     return make_pair(true, httpResponse.sendRedirect(req.c_str()));
 #else
-    return make_pair(false,0);
+    return make_pair(false,0L);
 #endif
 }
 
@@ -655,17 +655,17 @@ pair<bool,long> ADFSLogoutInitiator::run(SPRequest& request, bool isHandler) con
     try {
         session = request.getSession(false, true, false);  // don't cache it and ignore all checks
         if (!session)
-            return make_pair(false,0);
+            return make_pair(false,0L);
 
         // We only handle ADFS sessions.
         if (!XMLString::equals(session->getProtocol(), WSFED_NS) || !session->getEntityID()) {
             session->unlock();
-            return make_pair(false,0);
+            return make_pair(false,0L);
         }
     }
     catch (exception& ex) {
         m_log.error("error accessing current session: %s", ex.what());
-        return make_pair(false,0);
+        return make_pair(false,0L);
     }
 
     string entityID(session->getEntityID());
@@ -754,7 +754,7 @@ pair<bool,long> ADFSLogoutInitiator::doRequest(
         m_log.error("error issuing ADFS logout request: %s", ex.what());
     }
 
-    return make_pair(false,0);
+    return make_pair(false,0L);
 #else
     throw ConfigurationException("Cannot perform logout using lite version of shibsp library.");
 #endif
index 46250dc..cbd7c2e 100644 (file)
@@ -788,7 +788,7 @@ pair<bool,unsigned int> ApacheRequestMapper::getUnsignedInt(const char* name, co
         else if (sta->m_dc->tSettings) {
             const char* prop = ap_table_get(sta->m_dc->tSettings, name);
             if (prop)
-                return make_pair(true, strtol(prop, NULL, 10));
+                return pair<bool,unsigned int>(true, atoi(prop));
         }
     }
     return s ? s->getUnsignedInt(name,ns) : pair<bool,unsigned int>(false,0);
index c194896..cdfacad 100644 (file)
-/*\r
- *  Copyright 2001-2007 Internet2\r
- * \r
- * Licensed under the Apache License, Version 2.0 (the "License");\r
- * you may not use this file except in compliance with the License.\r
- * You may obtain a copy of the License at\r
- *\r
- *     http://www.apache.org/licenses/LICENSE-2.0\r
- *\r
- * Unless required by applicable law or agreed to in writing, software\r
- * distributed under the License is distributed on an "AS IS" BASIS,\r
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\r
- * See the License for the specific language governing permissions and\r
- * limitations under the License.\r
- */\r
-\r
-/**\r
- * ScopedAttributeDecoder.cpp\r
- * \r
- * Decodes SAML into ScopedAttributes\r
- */\r
-\r
-#include "internal.h"\r
-#include "attribute/AttributeDecoder.h"\r
-#include "attribute/ScopedAttribute.h"\r
-\r
-#include <saml/saml1/core/Assertions.h>\r
-#include <saml/saml2/core/Assertions.h>\r
-\r
-using namespace shibsp;\r
-using namespace opensaml::saml1;\r
-using namespace opensaml::saml2;\r
-using namespace xmltooling;\r
-using namespace std;\r
-\r
-namespace shibsp {\r
-    static const XMLCh Scope[] =            UNICODE_LITERAL_5(S,c,o,p,e);\r
-    static const XMLCh scopeDelimeter[] =   UNICODE_LITERAL_14(s,c,o,p,e,D,e,l,i,m,e,t,e,r);\r
-\r
-    class SHIBSP_DLLLOCAL ScopedAttributeDecoder : virtual public AttributeDecoder\r
-    {\r
-    public:\r
-        ScopedAttributeDecoder(const DOMElement* e) : AttributeDecoder(e), m_delimeter('@') {\r
-            if (e) {\r
-                if (e->hasAttributeNS(NULL,scopeDelimeter)) {\r
-                    auto_ptr_char d(e->getAttributeNS(NULL,scopeDelimeter));\r
-                    m_delimeter = *(d.get());\r
-                }\r
-            }\r
-        }\r
-        ~ScopedAttributeDecoder() {}\r
-\r
-        shibsp::Attribute* decode(\r
-            const vector<string>& ids, const XMLObject* xmlObject, const char* assertingParty=NULL, const char* relyingParty=NULL\r
-            ) const;\r
-\r
-    private:\r
-        char m_delimeter;\r
-    };\r
-\r
-    AttributeDecoder* SHIBSP_DLLLOCAL ScopedAttributeDecoderFactory(const DOMElement* const & e)\r
-    {\r
-        return new ScopedAttributeDecoder(e);\r
-    }\r
-};\r
-\r
-shibsp::Attribute* ScopedAttributeDecoder::decode(\r
-    const vector<string>& ids, const XMLObject* xmlObject, const char* assertingParty, const char* relyingParty\r
-    ) const\r
-{\r
-    char* val;\r
-    char* scope;\r
-    const XMLCh* xmlscope;\r
-    QName scopeqname(NULL,Scope);\r
-    auto_ptr<ScopedAttribute> scoped(new ScopedAttribute(ids, m_delimeter));\r
-    scoped->setCaseSensitive(m_caseSensitive);\r
-    vector< pair<string,string> >& dest = scoped->getValues();\r
-    vector<XMLObject*>::const_iterator v,stop;\r
-\r
-    Category& log = Category::getInstance(SHIBSP_LOGCAT".AttributeDecoder");\r
-    \r
-    if (xmlObject && XMLString::equals(opensaml::saml1::Attribute::LOCAL_NAME,xmlObject->getElementQName().getLocalPart())) {\r
-        const opensaml::saml2::Attribute* saml2attr = dynamic_cast<const opensaml::saml2::Attribute*>(xmlObject);\r
-        if (saml2attr) {\r
-            const vector<XMLObject*>& values = saml2attr->getAttributeValues();\r
-            v = values.begin();\r
-            stop = values.end();\r
-            if (log.isDebugEnabled()) {\r
-                auto_ptr_char n(saml2attr->getName());\r
-                log.debug(\r
-                    "decoding ScopedAttribute (%s) from SAML 2 Attribute (%s) with %lu value(s)",\r
-                    ids.front().c_str(), n.get() ? n.get() : "unnamed", values.size()\r
-                    );\r
-            }\r
-        }\r
-        else {\r
-            const opensaml::saml1::Attribute* saml1attr = dynamic_cast<const opensaml::saml1::Attribute*>(xmlObject);\r
-            if (saml1attr) {\r
-                const vector<XMLObject*>& values = saml1attr->getAttributeValues();\r
-                v = values.begin();\r
-                stop = values.end();\r
-                if (log.isDebugEnabled()) {\r
-                    auto_ptr_char n(saml1attr->getAttributeName());\r
-                    log.debug(\r
-                        "decoding ScopedAttribute (%s) from SAML 1 Attribute (%s) with %lu value(s)",\r
-                        ids.front().c_str(), n.get() ? n.get() : "unnamed", values.size()\r
-                        );\r
-                }\r
-            }\r
-            else {\r
-                log.warn("XMLObject type not recognized by ScopedAttributeDecoder, no values returned");\r
-                return NULL;\r
-            }\r
-        }\r
-\r
-        for (; v!=stop; ++v) {\r
-            if (!(*v)->hasChildren()) {\r
-                val = toUTF8((*v)->getTextContent());\r
-                if (val && *val) {\r
-                    const AttributeExtensibleXMLObject* aexo=dynamic_cast<const AttributeExtensibleXMLObject*>(*v);\r
-                    xmlscope = aexo->getAttribute(scopeqname);\r
-                    if (xmlscope && *xmlscope) {\r
-                        scope = toUTF8(xmlscope);\r
-                        dest.push_back(make_pair(val,scope));\r
-                        delete[] scope;\r
-                    }\r
-                    else {\r
-                        scope = strchr(val, m_delimeter);\r
-                        if (scope) {\r
-                            *scope++ = 0;\r
-                            if (*scope)\r
-                                dest.push_back(make_pair(val,scope));\r
-                            else\r
-                                log.warn("ignoring unscoped AttributeValue");\r
-                        }\r
-                        else {\r
-                            log.warn("ignoring unscoped AttributeValue");\r
-                        }\r
-                    }\r
-                }\r
-                else {\r
-                    log.warn("skipping empty AttributeValue");\r
-                }\r
-                delete[] val;\r
-            }\r
-            else {\r
-                log.warn("skipping complex AttributeValue");\r
-            }\r
-        }\r
-\r
-        return dest.empty() ? NULL : scoped.release();\r
-    }\r
-\r
-    const NameID* saml2name = dynamic_cast<const NameID*>(xmlObject);\r
-    if (saml2name) {\r
-        if (log.isDebugEnabled()) {\r
-            auto_ptr_char f(saml2name->getFormat());\r
-            log.debug("decoding ScopedAttribute (%s) from SAML 2 NameID with Format (%s)", ids.front().c_str(), f.get() ? f.get() : "unspecified");\r
-        }\r
-        val = toUTF8(saml2name->getName());\r
-    }\r
-    else {\r
-        const NameIdentifier* saml1name = dynamic_cast<const NameIdentifier*>(xmlObject);\r
-        if (saml1name) {\r
-            if (log.isDebugEnabled()) {\r
-                auto_ptr_char f(saml1name->getFormat());\r
-                log.debug(\r
-                    "decoding ScopedAttribute (%s) from SAML 1 NameIdentifier with Format (%s)",\r
-                    ids.front().c_str(), f.get() ? f.get() : "unspecified"\r
-                    );\r
-            }\r
-            val = toUTF8(saml1name->getName());\r
-        }\r
-        else {\r
-            log.warn("XMLObject type not recognized by ScopedAttributeDecoder, no values returned");\r
-            return NULL;\r
-        }\r
-    }\r
-\r
-    if (val && *val && *val!=m_delimeter) {\r
-        scope = strchr(val, m_delimeter);\r
-        if (scope) {\r
-            *scope++ = 0;\r
-            if (*scope)\r
-                dest.push_back(make_pair(val,scope));\r
-            else\r
-                log.warn("ignoring NameID with no scope");\r
-        }\r
-        else {\r
-            log.warn("ignoring NameID with no scope delimiter (%c)", m_delimeter);\r
-        }\r
-    }\r
-    else {\r
-        log.warn("ignoring empty NameID");\r
-    }\r
-    delete[] val;\r
-    return dest.empty() ? NULL : scoped.release();\r
-}\r
+/*
+ *  Copyright 2001-2007 Internet2
+ * 
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+/**
+ * ScopedAttributeDecoder.cpp
+ * 
+ * Decodes SAML into ScopedAttributes
+ */
+
+#include "internal.h"
+#include "attribute/AttributeDecoder.h"
+#include "attribute/ScopedAttribute.h"
+
+#include <saml/saml1/core/Assertions.h>
+#include <saml/saml2/core/Assertions.h>
+
+using namespace shibsp;
+using namespace opensaml::saml1;
+using namespace opensaml::saml2;
+using namespace xmltooling;
+using namespace std;
+
+namespace shibsp {
+    static const XMLCh Scope[] =            UNICODE_LITERAL_5(S,c,o,p,e);
+    static const XMLCh scopeDelimeter[] =   UNICODE_LITERAL_14(s,c,o,p,e,D,e,l,i,m,e,t,e,r);
+
+    class SHIBSP_DLLLOCAL ScopedAttributeDecoder : virtual public AttributeDecoder
+    {
+    public:
+        ScopedAttributeDecoder(const DOMElement* e) : AttributeDecoder(e), m_delimeter('@') {
+            if (e) {
+                if (e->hasAttributeNS(NULL,scopeDelimeter)) {
+                    auto_ptr_char d(e->getAttributeNS(NULL,scopeDelimeter));
+                    m_delimeter = *(d.get());
+                }
+            }
+        }
+        ~ScopedAttributeDecoder() {}
+
+        shibsp::Attribute* decode(
+            const vector<string>& ids, const XMLObject* xmlObject, const char* assertingParty=NULL, const char* relyingParty=NULL
+            ) const;
+
+    private:
+        char m_delimeter;
+    };
+
+    AttributeDecoder* SHIBSP_DLLLOCAL ScopedAttributeDecoderFactory(const DOMElement* const & e)
+    {
+        return new ScopedAttributeDecoder(e);
+    }
+};
+
+shibsp::Attribute* ScopedAttributeDecoder::decode(
+    const vector<string>& ids, const XMLObject* xmlObject, const char* assertingParty, const char* relyingParty
+    ) const
+{
+    char* val;
+    char* scope;
+    const XMLCh* xmlscope;
+    QName scopeqname(NULL,Scope);
+    auto_ptr<ScopedAttribute> scoped(new ScopedAttribute(ids, m_delimeter));
+    scoped->setCaseSensitive(m_caseSensitive);
+    vector< pair<string,string> >& dest = scoped->getValues();
+    vector<XMLObject*>::const_iterator v,stop;
+
+    Category& log = Category::getInstance(SHIBSP_LOGCAT".AttributeDecoder");
+    
+    if (xmlObject && XMLString::equals(opensaml::saml1::Attribute::LOCAL_NAME,xmlObject->getElementQName().getLocalPart())) {
+        const opensaml::saml2::Attribute* saml2attr = dynamic_cast<const opensaml::saml2::Attribute*>(xmlObject);
+        if (saml2attr) {
+            const vector<XMLObject*>& values = saml2attr->getAttributeValues();
+            v = values.begin();
+            stop = values.end();
+            if (log.isDebugEnabled()) {
+                auto_ptr_char n(saml2attr->getName());
+                log.debug(
+                    "decoding ScopedAttribute (%s) from SAML 2 Attribute (%s) with %lu value(s)",
+                    ids.front().c_str(), n.get() ? n.get() : "unnamed", values.size()
+                    );
+            }
+        }
+        else {
+            const opensaml::saml1::Attribute* saml1attr = dynamic_cast<const opensaml::saml1::Attribute*>(xmlObject);
+            if (saml1attr) {
+                const vector<XMLObject*>& values = saml1attr->getAttributeValues();
+                v = values.begin();
+                stop = values.end();
+                if (log.isDebugEnabled()) {
+                    auto_ptr_char n(saml1attr->getAttributeName());
+                    log.debug(
+                        "decoding ScopedAttribute (%s) from SAML 1 Attribute (%s) with %lu value(s)",
+                        ids.front().c_str(), n.get() ? n.get() : "unnamed", values.size()
+                        );
+                }
+            }
+            else {
+                log.warn("XMLObject type not recognized by ScopedAttributeDecoder, no values returned");
+                return NULL;
+            }
+        }
+
+        for (; v!=stop; ++v) {
+            if (!(*v)->hasChildren()) {
+                val = toUTF8((*v)->getTextContent());
+                if (val && *val) {
+                    const AttributeExtensibleXMLObject* aexo=dynamic_cast<const AttributeExtensibleXMLObject*>(*v);
+                    xmlscope = aexo->getAttribute(scopeqname);
+                    if (xmlscope && *xmlscope) {
+                        scope = toUTF8(xmlscope);
+                        dest.push_back(pair<string,string>(val,scope));
+                        delete[] scope;
+                    }
+                    else {
+                        scope = strchr(val, m_delimeter);
+                        if (scope) {
+                            *scope++ = 0;
+                            if (*scope)
+                                dest.push_back(pair<string,string>(val,scope));
+                            else
+                                log.warn("ignoring unscoped AttributeValue");
+                        }
+                        else {
+                            log.warn("ignoring unscoped AttributeValue");
+                        }
+                    }
+                }
+                else {
+                    log.warn("skipping empty AttributeValue");
+                }
+                delete[] val;
+            }
+            else {
+                log.warn("skipping complex AttributeValue");
+            }
+        }
+
+        return dest.empty() ? NULL : scoped.release();
+    }
+
+    const NameID* saml2name = dynamic_cast<const NameID*>(xmlObject);
+    if (saml2name) {
+        if (log.isDebugEnabled()) {
+            auto_ptr_char f(saml2name->getFormat());
+            log.debug("decoding ScopedAttribute (%s) from SAML 2 NameID with Format (%s)", ids.front().c_str(), f.get() ? f.get() : "unspecified");
+        }
+        val = toUTF8(saml2name->getName());
+    }
+    else {
+        const NameIdentifier* saml1name = dynamic_cast<const NameIdentifier*>(xmlObject);
+        if (saml1name) {
+            if (log.isDebugEnabled()) {
+                auto_ptr_char f(saml1name->getFormat());
+                log.debug(
+                    "decoding ScopedAttribute (%s) from SAML 1 NameIdentifier with Format (%s)",
+                    ids.front().c_str(), f.get() ? f.get() : "unspecified"
+                    );
+            }
+            val = toUTF8(saml1name->getName());
+        }
+        else {
+            log.warn("XMLObject type not recognized by ScopedAttributeDecoder, no values returned");
+            return NULL;
+        }
+    }
+
+    if (val && *val && *val!=m_delimeter) {
+        scope = strchr(val, m_delimeter);
+        if (scope) {
+            *scope++ = 0;
+            if (*scope)
+                dest.push_back(pair<string,string>(val,scope));
+            else
+                log.warn("ignoring NameID with no scope");
+        }
+        else {
+            log.warn("ignoring NameID with no scope delimiter (%c)", m_delimeter);
+        }
+    }
+    else {
+        log.warn("ignoring empty NameID");
+    }
+    delete[] val;
+    return dest.empty() ? NULL : scoped.release();
+}
index 4951005..857050d 100644 (file)
@@ -114,6 +114,6 @@ MatchFunctor* AndMatchFunctor::buildFunctor(const DOMElement* e, const FilterPol
         throw ConfigurationException("Child Rule found with no xsi:type.");
 
     MatchFunctor* func = SPConfig::getConfig().MatchFunctorManager.newPlugin(*type.get(), make_pair(functorMap,e));
-    functorMap->getMatchFunctors().insert(make_pair(id, func));
+    functorMap->getMatchFunctors().insert(multimap<string,MatchFunctor*>::value_type(id, func));
     return func;
 }
index 17d9bcd..0f79a84 100644 (file)
@@ -104,6 +104,6 @@ MatchFunctor* NotMatchFunctor::buildFunctor(const DOMElement* e, const FilterPol
         throw ConfigurationException("Child Rule found with no xsi:type.");
 
     MatchFunctor* func = SPConfig::getConfig().MatchFunctorManager.newPlugin(*type.get(), make_pair(functorMap,e));
-    functorMap->getMatchFunctors().insert(make_pair(id, func));
+    functorMap->getMatchFunctors().insert(multimap<string,MatchFunctor*>::value_type(id, func));
     return func;
 }
index e58d7c7..cd29548 100644 (file)
@@ -110,6 +110,6 @@ MatchFunctor* OrMatchFunctor::buildFunctor(const DOMElement* e, const FilterPoli
         throw ConfigurationException("Child Rule found with no xsi:type.");
 
     MatchFunctor* func = SPConfig::getConfig().MatchFunctorManager.newPlugin(*type.get(), make_pair(functorMap,e));
-    functorMap->getMatchFunctors().insert(make_pair(id, func));
+    functorMap->getMatchFunctors().insert(multimap<string,MatchFunctor*>::value_type(id, func));
     return func;
 }
index 0e50fbb..d1656e5 100644 (file)
@@ -172,14 +172,14 @@ XMLFilterImpl::XMLFilterImpl(const DOMElement* e, Category& log) : m_log(log), m
                     if (e && XMLHelper::isNodeNamed(e, shibspconstants::SHIB2ATTRIBUTEFILTER_NS, AttributeRule)) {
                         pair<string,const MatchFunctor*> rule = buildAttributeRule(e, valFunctors, false);
                         if (rule.second)
-                            m_policies.back().m_rules.insert(rule);
+                            m_policies.back().m_rules.insert(Policy::rules_t::value_type(rule.first, rule.second));
                     }
                     else if (e && XMLHelper::isNodeNamed(e, shibspconstants::SHIB2ATTRIBUTEFILTER_NS, AttributeRuleReference)) {
                         auto_ptr_char ref(e->getAttributeNS(NULL, _ref));
                         if (ref.get() && *ref.get()) {
                             map< string,pair<string,const MatchFunctor*> >::const_iterator ar = m_attrRules.find(ref.get());
                             if (ar != m_attrRules.end())
-                                m_policies.back().m_rules.insert(ar->second);
+                                m_policies.back().m_rules.insert(Policy::rules_t::value_type(ar->second.first, ar->second.second));
                             else
                                 m_log.warn("skipping invalid AttributeRuleReference (%s)", ref.get());
                         }
@@ -219,7 +219,7 @@ MatchFunctor* XMLFilterImpl::buildFunctor(
     if (type.get()) {
         try {
             MatchFunctor* func = SPConfig::getConfig().MatchFunctorManager.newPlugin(*type.get(), make_pair(&functorMap,e));
-            functorMap.getMatchFunctors().insert(make_pair(id, func));
+            functorMap.getMatchFunctors().insert(multimap<string,MatchFunctor*>::value_type(id, func));
             return func;
         }
         catch (exception& ex) {
@@ -241,12 +241,12 @@ pair<string,const MatchFunctor*> XMLFilterImpl::buildAttributeRule(const DOMElem
 
     if (standalone && !*id) {
         m_log.warn("skipping stand-alone AttributeRule with no id");
-        return make_pair(string(),(MatchFunctor*)NULL);
+        return make_pair(string(),(const MatchFunctor*)NULL);
     }
     else if (*id && m_attrRules.count(id)) {
         if (standalone) {
             m_log.warn("skipping duplicate stand-alone AttributeRule with id (%s)", id);
-            return make_pair(string(),(MatchFunctor*)NULL);
+            return make_pair(string(),(const MatchFunctor*)NULL);
         }
         else
             id = "";
@@ -271,13 +271,13 @@ pair<string,const MatchFunctor*> XMLFilterImpl::buildAttributeRule(const DOMElem
 
     if (func) {
         if (*id)
-            return m_attrRules[id] = make_pair(attrID.get(), func);
+            return m_attrRules[id] = pair<string,const MatchFunctor*>(attrID.get(), func);
         else
-            return make_pair(attrID.get(), func);
+            return pair<string,const MatchFunctor*>(attrID.get(), func);
     }
 
     m_log.warn("skipping AttributeRule (%s), PermitValueRule invalid or missing", id);
-    return make_pair(string(),(MatchFunctor*)NULL);
+    return make_pair(string(),(const MatchFunctor*)NULL);
 }
 
 void XMLFilterImpl::filterAttributes(const FilteringContext& context, vector<Attribute*>& attributes) const
index 2b5621d..eb4c6ec 100644 (file)
@@ -205,11 +205,11 @@ XMLExtractorImpl::XMLExtractorImpl(const DOMElement* e, Category& log) : m_log(l
 
         // Fetch/create the map entry and see if it's a duplicate rule.
 #ifdef HAVE_GOOD_STL
-        pair< AttributeDecoder*,vector<string> >& decl = m_attrMap[make_pair(name,format)];
+        pair< AttributeDecoder*,vector<string> >& decl = m_attrMap[pair<xstring,xstring>(name,format)];
 #else
         auto_ptr_char n(name);
         auto_ptr_char f(format);
-        pair< AttributeDecoder*,vector<string> >& decl = m_attrMap[make_pair(n.get(),f.get())];
+        pair< AttributeDecoder*,vector<string> >& decl = m_attrMap[pair<string,string>(n.get(),f.get())];
 #endif
         if (decl.first) {
             m_log.warn("skipping duplicate Attribute mapping (same name and nameFormat)");
@@ -272,10 +272,10 @@ void XMLExtractorImpl::extractAttributes(
     if (!format || !*format)
         format = NameIdentifier::UNSPECIFIED;
 #ifdef HAVE_GOOD_STL
-    if ((rule=m_attrMap.find(make_pair(format,xstring()))) != m_attrMap.end()) {
+    if ((rule=m_attrMap.find(pair<xstring,xstring>(format,xstring()))) != m_attrMap.end()) {
 #else
     auto_ptr_char temp(format);
-    if ((rule=m_attrMap.find(make_pair(temp.get(),string()))) != m_attrMap.end()) {
+    if ((rule=m_attrMap.find(pair<string,string>(temp.get(),string()))) != m_attrMap.end()) {
 #endif
         Attribute* a = rule->second.first->decode(rule->second.second, &nameid, assertingParty, application.getString("entityID").second);
         if (a)
@@ -297,10 +297,10 @@ void XMLExtractorImpl::extractAttributes(
     if (!format || !*format)
         format = NameID::UNSPECIFIED;
 #ifdef HAVE_GOOD_STL
-    if ((rule=m_attrMap.find(make_pair(format,xstring()))) != m_attrMap.end()) {
+    if ((rule=m_attrMap.find(pair<xstring,xstring>(format,xstring()))) != m_attrMap.end()) {
 #else
     auto_ptr_char temp(format);
-    if ((rule=m_attrMap.find(make_pair(temp.get(),string()))) != m_attrMap.end()) {
+    if ((rule=m_attrMap.find(pair<string,string>(temp.get(),string()))) != m_attrMap.end()) {
 #endif
         Attribute* a = rule->second.first->decode(rule->second.second, &nameid, assertingParty, application.getString("entityID").second);
         if (a)
@@ -325,11 +325,11 @@ void XMLExtractorImpl::extractAttributes(
     if (!format || XMLString::equals(format, shibspconstants::SHIB1_ATTRIBUTE_NAMESPACE_URI))
         format = &chNull;
 #ifdef HAVE_GOOD_STL
-    if ((rule=m_attrMap.find(make_pair(name,format))) != m_attrMap.end()) {
+    if ((rule=m_attrMap.find(pair<xstring,xstring>(name,format))) != m_attrMap.end()) {
 #else
     auto_ptr_char temp1(name);
     auto_ptr_char temp2(format);
-    if ((rule=m_attrMap.find(make_pair(temp1.get(),temp2.get()))) != m_attrMap.end()) {
+    if ((rule=m_attrMap.find(pair<string,string>(temp1.get(),temp2.get()))) != m_attrMap.end()) {
 #endif
         Attribute* a = rule->second.first->decode(rule->second.second, &attr, assertingParty, application.getString("entityID").second);
         if (a)
@@ -356,11 +356,11 @@ void XMLExtractorImpl::extractAttributes(
     else if (XMLString::equals(format, saml2::Attribute::URI_REFERENCE))
         format = &chNull;
 #ifdef HAVE_GOOD_STL
-    if ((rule=m_attrMap.find(make_pair(name,format))) != m_attrMap.end()) {
+    if ((rule=m_attrMap.find(pair<xstring,xstring>(name,format))) != m_attrMap.end()) {
 #else
     auto_ptr_char temp1(name);
     auto_ptr_char temp2(format);
-    if ((rule=m_attrMap.find(make_pair(temp1.get(),temp2.get()))) != m_attrMap.end()) {
+    if ((rule=m_attrMap.find(pair<string,string>(temp1.get(),temp2.get()))) != m_attrMap.end()) {
 #endif
         Attribute* a = rule->second.first->decode(rule->second.second, &attr, assertingParty, application.getString("entityID").second);
         if (a)
index 24915a7..c43712e 100644 (file)
@@ -196,6 +196,6 @@ pair<bool,long> AssertionLookup::processMessage(const Application& application,
     httpResponse.setContentType("application/samlassertion+xml");
     return make_pair(true, httpResponse.sendResponse(s));
 #else
-    return make_pair(false,0);
+    return make_pair(false,0L);
 #endif
 }
index d694926..ca5c926 100644 (file)
@@ -310,6 +310,6 @@ pair<bool,long> MetadataGenerator::processMessage(const Application& application
     httpResponse.setContentType("application/samlmetadata+xml");
     return make_pair(true, httpResponse.sendResponse(s));
 #else
-    return make_pair(false,0);
+    return make_pair(false,0L);
 #endif
 }
index 07fa66e..df95366 100644 (file)
@@ -315,7 +315,7 @@ pair<bool,long> SAML2ArtifactResolution::processMessage(const Application& appli
         return samlError(application, *req, httpResponse, StatusCode::RESPONDER, NULL, ex.what());
     }
 #else
-    return make_pair(false,0);
+    return make_pair(false,0L);
 #endif
 }
 
index 23b5421..05a80a7 100644 (file)
@@ -146,7 +146,9 @@ SAML2Logout::SAML2Logout(const DOMElement* e, const char* appId)
         SAMLConfig& conf = SAMLConfig::getConfig();
 
         // Handle incoming binding.
-        m_decoder = conf.MessageDecoderManager.newPlugin(getString("Binding").second,make_pair(e,shibspconstants::SHIB2SPCONFIG_NS));
+        m_decoder = conf.MessageDecoderManager.newPlugin(
+            getString("Binding").second, pair<const DOMElement*,const XMLCh*>(e,shibspconstants::SHIB2SPCONFIG_NS)
+            );
         m_decoder->setArtifactResolver(SPConfig::getConfig().getArtifactResolver());
 
         if (m_decoder->isUserAgentPresent()) {
@@ -172,7 +174,9 @@ SAML2Logout::SAML2Logout(const DOMElement* e, const char* appId)
                 m_bindings.push_back(start);
                 try {
                     auto_ptr_char b(start);
-                    MessageEncoder * encoder = conf.MessageEncoderManager.newPlugin(b.get(),make_pair(e,shibspconstants::SHIB2SPCONFIG_NS));
+                    MessageEncoder * encoder = conf.MessageEncoderManager.newPlugin(
+                        b.get(), pair<const DOMElement*,const XMLCh*>(e,shibspconstants::SHIB2SPCONFIG_NS)
+                        );
                     m_encoders[start] = encoder;
                     m_log.debug("supporting outgoing front-channel binding (%s)", b.get());
                 }
@@ -187,7 +191,7 @@ SAML2Logout::SAML2Logout(const DOMElement* e, const char* appId)
         }
         else {
             MessageEncoder* encoder = conf.MessageEncoderManager.newPlugin(
-                getString("Binding").second,make_pair(e,shibspconstants::SHIB2SPCONFIG_NS)
+                getString("Binding").second, pair<const DOMElement*,const XMLCh*>(e,shibspconstants::SHIB2SPCONFIG_NS)
                 );
             m_encoders.insert(pair<const XMLCh*,MessageEncoder*>(NULL, encoder));
         }
@@ -530,7 +534,7 @@ pair<bool,long> SAML2Logout::doRequest(
     if (policy.getIssuerMetadata())
         annotateException(&ex, policy.getIssuerMetadata()); // throws it
     ex.raise();
-    return make_pair(false,0);  // never happen, satisfies compiler
+    return make_pair(false,0L);  // never happen, satisfies compiler
 #else
     throw ConfigurationException("Cannot process logout message using lite version of shibsp library.");
 #endif
index 06edde9..50f06b9 100644 (file)
@@ -184,17 +184,17 @@ pair<bool,long> SAML2LogoutInitiator::run(SPRequest& request, bool isHandler) co
     try {
         session = request.getSession(false, true, false);  // don't cache it and ignore all checks
         if (!session)
-            return make_pair(false,0);
+            return make_pair(false,0L);
 
         // We only handle SAML 2.0 sessions.
         if (!XMLString::equals(session->getProtocol(), m_protocol.get())) {
             session->unlock();
-            return make_pair(false,0);
+            return make_pair(false,0L);
         }
     }
     catch (exception& ex) {
         m_log.error("error accessing current session: %s", ex.what());
-        return make_pair(false,0);
+        return make_pair(false,0L);
     }
 
     if (SPConfig::getConfig().isEnabled(SPConfig::OutOfProcess)) {
@@ -284,7 +284,7 @@ pair<bool,long> SAML2LogoutInitiator::doRequest(
     }
 
 #ifndef SHIBSP_LITE
-    pair<bool,long> ret = make_pair(false,0);
+    pair<bool,long> ret = make_pair(false,0L);
     try {
         // With a session in hand, we can create a LogoutRequest message, if we can find a compatible endpoint.
         MetadataProvider* m = application.getMetadataProvider();
index 0f90abb..a63bc6c 100644 (file)
@@ -142,7 +142,9 @@ SAML2NameIDMgmt::SAML2NameIDMgmt(const DOMElement* e, const char* appId)
         SAMLConfig& conf = SAMLConfig::getConfig();
 
         // Handle incoming binding.
-        m_decoder = conf.MessageDecoderManager.newPlugin(getString("Binding").second,make_pair(e,shibspconstants::SHIB2SPCONFIG_NS));
+        m_decoder = conf.MessageDecoderManager.newPlugin(
+            getString("Binding").second, pair<const DOMElement*,const XMLCh*>(e,shibspconstants::SHIB2SPCONFIG_NS)
+            );
         m_decoder->setArtifactResolver(SPConfig::getConfig().getArtifactResolver());
 
         if (m_decoder->isUserAgentPresent()) {
@@ -168,7 +170,9 @@ SAML2NameIDMgmt::SAML2NameIDMgmt(const DOMElement* e, const char* appId)
                 m_bindings.push_back(start);
                 try {
                     auto_ptr_char b(start);
-                    MessageEncoder * encoder = conf.MessageEncoderManager.newPlugin(b.get(),make_pair(e,shibspconstants::SHIB2SPCONFIG_NS));
+                    MessageEncoder * encoder = conf.MessageEncoderManager.newPlugin(
+                        b.get(), pair<const DOMElement*,const XMLCh*>(e,shibspconstants::SHIB2SPCONFIG_NS)
+                        );
                     m_encoders[start] = encoder;
                     m_log.debug("supporting outgoing front-channel binding (%s)", b.get());
                 }
@@ -183,7 +187,7 @@ SAML2NameIDMgmt::SAML2NameIDMgmt(const DOMElement* e, const char* appId)
         }
         else {
             MessageEncoder* encoder = conf.MessageEncoderManager.newPlugin(
-                getString("Binding").second,make_pair(e,shibspconstants::SHIB2SPCONFIG_NS)
+                getString("Binding").second, pair<const DOMElement*,const XMLCh*>(e,shibspconstants::SHIB2SPCONFIG_NS)
                 );
             m_encoders.insert(pair<const XMLCh*,MessageEncoder*>(NULL, encoder));
         }
@@ -464,7 +468,7 @@ pair<bool,long> SAML2NameIDMgmt::doRequest(
     if (policy.getIssuerMetadata())
         annotateException(&ex, policy.getIssuerMetadata()); // throws it
     ex.raise();
-    return make_pair(false,0);  // never happen, satisfies compiler
+    return make_pair(false,0L);  // never happen, satisfies compiler
 #else
     throw ConfigurationException("Cannot process NameID mgmt message using lite version of shibsp library.");
 #endif
index 6d74a34..a022c91 100644 (file)
@@ -216,7 +216,7 @@ pair<bool,long> SAML2SessionInitiator::run(SPRequest& request, const char* entit
 
     // We have to know the IdP to function unless this is ECP.
     if (!ECP && (!entityID || !*entityID))
-        return make_pair(false,0);
+        return make_pair(false,0L);
 
     string target;
     const Handler* ACS=NULL;
@@ -471,7 +471,7 @@ pair<bool,long> SAML2SessionInitiator::doRequest(
         encoder = m_ecp;
         if (!encoder) {
             m_log.error("MessageEncoder for PAOS binding not available");
-            return make_pair(false,0);
+            return make_pair(false,0L);
         }
     }
     else {
@@ -486,7 +486,7 @@ pair<bool,long> SAML2SessionInitiator::doRequest(
         }
         else if (!entity.second) {
             m_log.error("unable to locate SAML 2.0 identity provider role for provider (%s)", entityID);
-            return make_pair(false,0);
+            return make_pair(false,0L);
         }
 
         // Loop over the supportable outgoing bindings.
@@ -502,7 +502,7 @@ pair<bool,long> SAML2SessionInitiator::doRequest(
         }
         if (!ep || !encoder) {
             m_log.error("unable to locate compatible SSO service for provider (%s)", entityID);
-            return make_pair(false,0);
+            return make_pair(false,0L);
         }
     }
 
@@ -587,6 +587,6 @@ pair<bool,long> SAML2SessionInitiator::doRequest(
     req.release();  // freed by encoder
     return make_pair(true,ret);
 #else
-    return make_pair(false,0);
+    return make_pair(false,0L);
 #endif
 }
index ddbcd6b..1aec1e2 100644 (file)
@@ -80,7 +80,7 @@ pair<bool,long> SAMLDSSessionInitiator::run(SPRequest& request, const char* enti
     // The IdP CANNOT be specified for us to run. Otherwise, we'd be redirecting to a DS
     // anytime the IdP's metadata was wrong.
     if (entityID && *entityID)
-        return make_pair(false,0);
+        return make_pair(false,0L);
 
     string target;
     const char* option;
index f370e77..6d2152f 100644 (file)
@@ -107,7 +107,7 @@ pair<bool,long> Shib1SessionInitiator::run(SPRequest& request, const char* entit
 {
     // We have to know the IdP to function.
     if (!entityID || !*entityID)
-        return make_pair(false,0);
+        return make_pair(false,0L);
 
     string target;
     const Handler* ACS=NULL;
@@ -232,14 +232,14 @@ pair<bool,long> Shib1SessionInitiator::doRequest(
     }
     else if (!entity.second) {
         m_log.error("unable to locate Shibboleth-aware identity provider role for provider (%s)", entityID);
-        return make_pair(false,0);
+        return make_pair(false,0L);
     }
     const EndpointType* ep=EndpointManager<SingleSignOnService>(
         dynamic_cast<const IDPSSODescriptor*>(entity.second)->getSingleSignOnServices()
         ).getByBinding(shibspconstants::SHIB1_AUTHNREQUEST_PROFILE_URI);
     if (!ep) {
         m_log.error("unable to locate compatible SSO service for provider (%s)", entityID);
-        return make_pair(false,0);
+        return make_pair(false,0L);
     }
 
     preserveRelayState(app, httpResponse, relayState);
@@ -258,6 +258,6 @@ pair<bool,long> Shib1SessionInitiator::doRequest(
 
     return make_pair(true, httpResponse.sendRedirect(req.c_str()));
 #else
-    return make_pair(false,0);
+    return make_pair(false,0L);
 #endif
 }
index 8862f58..f090bb0 100644 (file)
@@ -446,6 +446,6 @@ pair<bool,long> StatusHandler::processMessage(
     httpResponse.setContentType("text/xml");
     return make_pair(true, httpResponse.sendResponse(s));
 #else
-    return make_pair(false,0);
+    return make_pair(false,0L);
 #endif
 }
index 49f1242..fe627d1 100644 (file)
@@ -77,7 +77,7 @@ pair<bool,long> WAYFSessionInitiator::run(SPRequest& request, const char* entity
     // The IdP CANNOT be specified for us to run. Otherwise, we'd be redirecting to a WAYF
     // anytime the IdP's metadata was wrong.
     if (entityID && *entityID)
-        return make_pair(false,0);
+        return make_pair(false,0L);
 
     string target;
     const char* option;
index f9162b7..d203319 100644 (file)
-/*\r
- *  Copyright 2001-2007 Internet2\r
- * \r
- * Licensed under the Apache License, Version 2.0 (the "License");\r
- * you may not use this file except in compliance with the License.\r
- * You may obtain a copy of the License at\r
- *\r
- *     http://www.apache.org/licenses/LICENSE-2.0\r
- *\r
- * Unless required by applicable law or agreed to in writing, software\r
- * distributed under the License is distributed on an "AS IS" BASIS,\r
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\r
- * See the License for the specific language governing permissions and\r
- * limitations under the License.\r
- */\r
-\r
-/**\r
- * RemotedSessionCache.cpp\r
- * \r
- * SessionCache implementation that delegates to a remoted version.\r
- */\r
-\r
-#include "internal.h"\r
-#include "Application.h"\r
-#include "exceptions.h"\r
-#include "ServiceProvider.h"\r
-#include "SessionCache.h"\r
-#include "attribute/Attribute.h"\r
-#include "remoting/ListenerService.h"\r
-#include "util/SPConstants.h"\r
-\r
-#include <ctime>\r
-#include <sstream>\r
-#include <xmltooling/XMLToolingConfig.h>\r
-#include <xmltooling/util/DateTime.h>\r
-#include <xmltooling/util/NDC.h>\r
-#include <xmltooling/util/XMLHelper.h>\r
-\r
-using namespace shibsp;\r
-using namespace xmltooling;\r
-using namespace std;\r
-\r
-namespace shibsp {\r
-\r
-    class RemotedCache;\r
-    class RemotedSession : public virtual Session\r
-    {\r
-    public:\r
-        RemotedSession(RemotedCache* cache, DDF& obj) : m_version(obj["version"].integer()), m_obj(obj),\r
-                m_expires(0), m_lastAccess(time(NULL)), m_cache(cache), m_lock(NULL) {\r
-            auto_ptr_XMLCh exp(m_obj["expires"].string());\r
-            if (exp.get()) {\r
-                DateTime iso(exp.get());\r
-                iso.parseDateTime();\r
-                m_expires = iso.getEpoch();\r
-            }\r
-\r
-            m_lock = Mutex::create();\r
-        }\r
-        \r
-        ~RemotedSession() {\r
-            delete m_lock;\r
-            m_obj.destroy();\r
-            for_each(m_attributes.begin(), m_attributes.end(), xmltooling::cleanup<Attribute>());\r
-        }\r
-        \r
-        Lockable* lock() {\r
-            m_lock->lock();\r
-            return this;\r
-        }\r
-        void unlock() {\r
-            m_lock->unlock();\r
-        }\r
-\r
-        const char* getID() const {\r
-            return m_obj.name();\r
-        }\r
-        const char* getApplicationID() const {\r
-            return m_obj["application_id"].string();\r
-        }\r
-        const char* getClientAddress() const {\r
-            return m_obj["client_addr"].string();\r
-        }\r
-        const char* getEntityID() const {\r
-            return m_obj["entity_id"].string();\r
-        }\r
-        const char* getProtocol() const {\r
-            return m_obj["protocol"].string();\r
-        }\r
-        const char* getAuthnInstant() const {\r
-            return m_obj["authn_instant"].string();\r
-        }\r
-        const char* getSessionIndex() const {\r
-            return m_obj["session_index"].string();\r
-        }\r
-        const char* getAuthnContextClassRef() const {\r
-            return m_obj["authncontext_class"].string();\r
-        }\r
-        const char* getAuthnContextDeclRef() const {\r
-            return m_obj["authncontext_decl"].string();\r
-        }\r
-        const vector<Attribute*>& getAttributes() const {\r
-            if (m_attributes.empty())\r
-                unmarshallAttributes();\r
-            return m_attributes;\r
-        }\r
-        const multimap<string,const Attribute*>& getIndexedAttributes() const {\r
-            if (m_attributeIndex.empty()) {\r
-                if (m_attributes.empty())\r
-                    unmarshallAttributes();\r
-                for (vector<Attribute*>::const_iterator a = m_attributes.begin(); a != m_attributes.end(); ++a) {\r
-                    const vector<string>& aliases = (*a)->getAliases();\r
-                    for (vector<string>::const_iterator alias = aliases.begin(); alias != aliases.end(); ++alias)\r
-                        m_attributeIndex.insert(make_pair(*alias, *a));\r
-                }\r
-            }\r
-            return m_attributeIndex;\r
-        }\r
-        const vector<const char*>& getAssertionIDs() const {\r
-            if (m_ids.empty()) {\r
-                DDF ids = m_obj["assertions"];\r
-                DDF id = ids.first();\r
-                while (id.isstring()) {\r
-                    m_ids.push_back(id.string());\r
-                    id = ids.next();\r
-                }\r
-            }\r
-            return m_ids;\r
-        }\r
-        \r
-        time_t expires() const { return m_expires; }\r
-        time_t lastAccess() const { return m_lastAccess; }\r
-        void validate(const Application& application, const char* client_addr, time_t* timeout);\r
-\r
-    private:\r
-        void unmarshallAttributes() const;\r
-\r
-        int m_version;\r
-        mutable DDF m_obj;\r
-        mutable vector<Attribute*> m_attributes;\r
-        mutable multimap<string,const Attribute*> m_attributeIndex;\r
-        mutable vector<const char*> m_ids;\r
-        time_t m_expires,m_lastAccess;\r
-        RemotedCache* m_cache;\r
-        Mutex* m_lock;\r
-    };\r
-    \r
-    class RemotedCache : public SessionCache\r
-    {\r
-    public:\r
-        RemotedCache(const DOMElement* e);\r
-        ~RemotedCache();\r
-    \r
-        Session* find(const char* key, const Application& application, const char* client_addr=NULL, time_t* timeout=NULL);\r
-        void remove(const char* key, const Application& application);\r
-        \r
-        void cleanup();\r
-    \r
-        Category& m_log;\r
-    private:\r
-        const DOMElement* m_root;         // Only valid during initialization\r
-        RWLock* m_lock;\r
-        map<string,RemotedSession*> m_hashtable;\r
-    \r
-        void dormant(const char* key);\r
-        static void* cleanup_fn(void*);\r
-        bool shutdown;\r
-        CondWait* shutdown_wait;\r
-        Thread* cleanup_thread;\r
-    };\r
-\r
-    SessionCache* SHIBSP_DLLLOCAL RemotedCacheFactory(const DOMElement* const & e)\r
-    {\r
-        return new RemotedCache(e);\r
-    }\r
-}\r
-\r
-void RemotedSession::unmarshallAttributes() const\r
-{\r
-    Attribute* attribute;\r
-    DDF attrs = m_obj["attributes"];\r
-    DDF attr = attrs.first();\r
-    while (!attr.isnull()) {\r
-        try {\r
-            attribute = Attribute::unmarshall(attr);\r
-            m_attributes.push_back(attribute);\r
-            if (m_cache->m_log.isDebugEnabled())\r
-                m_cache->m_log.debug("unmarshalled attribute (ID: %s) with %d value%s",\r
-                    attribute->getId(), attr.first().integer(), attr.first().integer()!=1 ? "s" : "");\r
-        }\r
-        catch (AttributeException& ex) {\r
-            const char* id = attr.first().name();\r
-            m_cache->m_log.error("error unmarshalling attribute (ID: %s): %s", id ? id : "none", ex.what());\r
-        }\r
-        attr = attrs.next();\r
-    }\r
-}\r
-\r
-void RemotedSession::validate(const Application& application, const char* client_addr, time_t* timeout)\r
-{\r
-    // Basic expiration?\r
-    time_t now = time(NULL);\r
-    if (now > m_expires) {\r
-        m_cache->m_log.info("session expired (ID: %s)", getID());\r
-        throw opensaml::RetryableProfileException("Your session has expired, and you must re-authenticate.");\r
-    }\r
-\r
-    // Address check?\r
-    if (client_addr) {\r
-        if (m_cache->m_log.isDebugEnabled())\r
-            m_cache->m_log.debug("comparing client address %s against %s", client_addr, getClientAddress());\r
-        if (strcmp(getClientAddress(),client_addr)) {\r
-            m_cache->m_log.warn("client address mismatch");\r
-            throw opensaml::RetryableProfileException(\r
-                "Your IP address ($1) does not match the address recorded at the time the session was established.",\r
-                params(1,client_addr)\r
-                );\r
-        }\r
-    }\r
-\r
-    if (!timeout)\r
-        return;\r
-    \r
-    DDF in("touch::"REMOTED_SESSION_CACHE"::SessionCache"), out;\r
-    DDFJanitor jin(in);\r
-    in.structure();\r
-    in.addmember("key").string(getID());\r
-    in.addmember("version").integer(m_obj["version"].integer());\r
-    if (*timeout) {\r
-        // On 64-bit Windows, time_t doesn't fit in a long, so I'm using ISO timestamps.  \r
-#ifndef HAVE_GMTIME_R\r
-        struct tm* ptime=gmtime(timeout);\r
-#else\r
-        struct tm res;\r
-        struct tm* ptime=gmtime_r(timeout,&res);\r
-#endif\r
-        char timebuf[32];\r
-        strftime(timebuf,32,"%Y-%m-%dT%H:%M:%SZ",ptime);\r
-        in.addmember("timeout").string(timebuf);\r
-    }\r
-\r
-    try {\r
-        out=application.getServiceProvider().getListenerService()->send(in);\r
-    }\r
-    catch (...) {\r
-        out.destroy();\r
-        throw;\r
-    }\r
-\r
-    if (out.isstruct()) {\r
-        // We got an updated record back.\r
-        m_ids.clear();\r
-        for_each(m_attributes.begin(), m_attributes.end(), xmltooling::cleanup<Attribute>());\r
-        m_attributes.clear();\r
-        m_attributeIndex.clear();\r
-        m_obj.destroy();\r
-        m_obj = out;\r
-    }\r
-\r
-    m_lastAccess = now;\r
-}\r
-\r
-RemotedCache::RemotedCache(const DOMElement* e)\r
-    : SessionCache(e, 900), m_log(Category::getInstance(SHIBSP_LOGCAT".SessionCache")), m_root(e), m_lock(NULL), shutdown(false)\r
-{\r
-    if (!SPConfig::getConfig().getServiceProvider()->getListenerService())\r
-        throw ConfigurationException("RemotedCacheService requires a ListenerService, but none available.");\r
-        \r
-    m_lock = RWLock::create();\r
-    shutdown_wait = CondWait::create();\r
-    cleanup_thread = Thread::create(&cleanup_fn, (void*)this);\r
-}\r
-\r
-RemotedCache::~RemotedCache()\r
-{\r
-    // Shut down the cleanup thread and let it know...\r
-    shutdown = true;\r
-    shutdown_wait->signal();\r
-    cleanup_thread->join(NULL);\r
-\r
-    for_each(m_hashtable.begin(),m_hashtable.end(),xmltooling::cleanup_pair<string,RemotedSession>());\r
-    delete m_lock;\r
-    delete shutdown_wait;\r
-}\r
-\r
-Session* RemotedCache::find(const char* key, const Application& application, const char* client_addr, time_t* timeout)\r
-{\r
-#ifdef _DEBUG\r
-    xmltooling::NDC ndc("find");\r
-#endif\r
-\r
-    RemotedSession* session=NULL;\r
-    m_log.debug("searching local cache for session (%s)", key);\r
-    m_lock->rdlock();\r
-    map<string,RemotedSession*>::const_iterator i=m_hashtable.find(key);\r
-    if (i==m_hashtable.end()) {\r
-        m_lock->unlock();\r
-        m_log.debug("session not found locally, searching remote cache");\r
-\r
-        DDF in("find::"REMOTED_SESSION_CACHE"::SessionCache"), out;\r
-        DDFJanitor jin(in);\r
-        in.structure();\r
-        in.addmember("key").string(key);\r
-        in.addmember("application_id").string(application.getId());\r
-        if (timeout && *timeout) {\r
-            // On 64-bit Windows, time_t doesn't fit in a long, so I'm using ISO timestamps.  \r
-#ifndef HAVE_GMTIME_R\r
-            struct tm* ptime=gmtime(timeout);\r
-#else\r
-            struct tm res;\r
-            struct tm* ptime=gmtime_r(timeout,&res);\r
-#endif\r
-            char timebuf[32];\r
-            strftime(timebuf,32,"%Y-%m-%dT%H:%M:%SZ",ptime);\r
-            in.addmember("timeout").string(timebuf);\r
-        }\r
-        \r
-        try {\r
-            out=application.getServiceProvider().getListenerService()->send(in);\r
-            if (!out.isstruct()) {\r
-                out.destroy();\r
-                m_log.debug("session not found in remote cache");\r
-                return NULL;\r
-            }\r
-            \r
-            // Wrap the results in a local entry and save it.\r
-            session = new RemotedSession(this, out);\r
-            // The remote end has handled timeout issues, we handle address and expiration checks.\r
-            timeout = NULL;\r
-        }\r
-        catch (...) {\r
-            out.destroy();\r
-            throw;\r
-        }\r
-\r
-        // Lock for writing and repeat the search to avoid duplication.\r
-        m_lock->wrlock();\r
-        SharedLock shared(m_lock, false);\r
-        if (m_hashtable.count(key)) {\r
-            // We're using an existing session entry.\r
-            delete session;\r
-            session = m_hashtable[key];\r
-            session->lock();\r
-        }\r
-        else {\r
-            m_hashtable[key]=session;\r
-            session->lock();\r
-        }\r
-    }\r
-    else {\r
-        // Save off and lock the session.\r
-        session = i->second;\r
-        session->lock();\r
-        m_lock->unlock();\r
-        \r
-        m_log.debug("session found locally, validating it for use");\r
-    }\r
-\r
-    if (!XMLString::equals(session->getApplicationID(), application.getId())) {\r
-        m_log.error("an application (%s) tried to access another application's session", application.getId());\r
-        session->unlock();\r
-        return NULL;\r
-    }\r
-\r
-    // Verify currency and update the timestamp if indicated by caller.\r
-    try {\r
-        session->validate(application, client_addr, timeout);\r
-    }\r
-    catch (...) {\r
-        session->unlock();\r
-        remove(key, application);\r
-        throw;\r
-    }\r
-    \r
-    return session;\r
-}\r
-\r
-void RemotedCache::remove(const char* key, const Application& application)\r
-{\r
-    // Take care of local copy.\r
-    dormant(key);\r
-    \r
-    // Now remote...\r
-    DDF in("remove::"REMOTED_SESSION_CACHE"::SessionCache");\r
-    DDFJanitor jin(in);\r
-    in.structure();\r
-    in.addmember("key").string(key);\r
-    in.addmember("application_id").string(application.getId());\r
-    \r
-    DDF out = application.getServiceProvider().getListenerService()->send(in);\r
-    out.destroy();\r
-}\r
-\r
-void RemotedCache::dormant(const char* key)\r
-{\r
-#ifdef _DEBUG\r
-    xmltooling::NDC ndc("dormant");\r
-#endif\r
-\r
-    m_log.debug("deleting local copy of session (%s)", key);\r
-\r
-    // lock the cache for writing, which means we know nobody is sitting in find()\r
-    m_lock->wrlock();\r
-\r
-    // grab the entry from the table\r
-    map<string,RemotedSession*>::const_iterator i=m_hashtable.find(key);\r
-    if (i==m_hashtable.end()) {\r
-        m_lock->unlock();\r
-        return;\r
-    }\r
-\r
-    // ok, remove the entry and lock it\r
-    RemotedSession* entry=i->second;\r
-    m_hashtable.erase(key);\r
-    entry->lock();\r
-    \r
-    // unlock the cache\r
-    m_lock->unlock();\r
-\r
-    // we can release the cache entry lock because we know we're not in the cache anymore\r
-    entry->unlock();\r
-\r
-    delete entry;\r
-}\r
-\r
-void RemotedCache::cleanup()\r
-{\r
-#ifdef _DEBUG\r
-    xmltooling::NDC ndc("cleanup");\r
-#endif\r
-\r
-    Mutex* mutex = Mutex::create();\r
-  \r
-    // Load our configuration details...\r
-    static const XMLCh cleanupInterval[] = UNICODE_LITERAL_15(c,l,e,a,n,u,p,I,n,t,e,r,v,a,l);\r
-    const XMLCh* tag=m_root ? m_root->getAttributeNS(NULL,cleanupInterval) : NULL;\r
-    int rerun_timer = 900;\r
-    if (tag && *tag)\r
-        rerun_timer = XMLString::parseInt(tag);\r
-    if (rerun_timer <= 0)\r
-        rerun_timer = 900;\r
-\r
-    mutex->lock();\r
-\r
-    m_log.info("cleanup thread started...run every %d secs; timeout after %d secs", rerun_timer, m_cacheTimeout);\r
-\r
-    while (!shutdown) {\r
-        shutdown_wait->timedwait(mutex,rerun_timer);\r
-        if (shutdown)\r
-            break;\r
-\r
-        // Ok, let's run through the cleanup process and clean out\r
-        // really old sessions.  This is a two-pass process.  The\r
-        // first pass is done holding a read-lock while we iterate over\r
-        // the cache.  The second pass doesn't need a lock because\r
-        // the 'deletes' will lock the cache.\r
-    \r
-        // Pass 1: iterate over the map and find all entries that have not been\r
-        // used in X hours\r
-        vector<string> stale_keys;\r
-        time_t stale = time(NULL) - m_cacheTimeout;\r
-    \r
-        m_log.debug("cleanup thread running");\r
-\r
-        m_lock->rdlock();\r
-        for (map<string,RemotedSession*>::const_iterator i=m_hashtable.begin(); i!=m_hashtable.end(); ++i) {\r
-            // If the last access was BEFORE the stale timeout...\r
-            i->second->lock();\r
-            time_t last=i->second->lastAccess();\r
-            i->second->unlock();\r
-            if (last < stale)\r
-                stale_keys.push_back(i->first);\r
-        }\r
-        m_lock->unlock();\r
-    \r
-        if (!stale_keys.empty()) {\r
-            m_log.info("purging %d old sessions", stale_keys.size());\r
-    \r
-            // Pass 2: walk through the list of stale entries and remove them from the cache\r
-            for (vector<string>::const_iterator j = stale_keys.begin(); j != stale_keys.end(); ++j)\r
-                dormant(j->c_str());\r
-        }\r
-\r
-        m_log.debug("cleanup thread completed");\r
-    }\r
-\r
-    m_log.info("cleanup thread exiting");\r
-\r
-    mutex->unlock();\r
-    delete mutex;\r
-    Thread::exit(NULL);\r
-}\r
-\r
-void* RemotedCache::cleanup_fn(void* cache_p)\r
-{\r
-    RemotedCache* cache = reinterpret_cast<RemotedCache*>(cache_p);\r
-\r
-#ifndef WIN32\r
-    // First, let's block all signals \r
-    Thread::mask_all_signals();\r
-#endif\r
-\r
-    // Now run the cleanup process.\r
-    cache->cleanup();\r
-    return NULL;\r
-}\r
+/*
+ *  Copyright 2001-2007 Internet2
+ * 
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+/**
+ * RemotedSessionCache.cpp
+ * 
+ * SessionCache implementation that delegates to a remoted version.
+ */
+
+#include "internal.h"
+#include "Application.h"
+#include "exceptions.h"
+#include "ServiceProvider.h"
+#include "SessionCache.h"
+#include "attribute/Attribute.h"
+#include "remoting/ListenerService.h"
+#include "util/SPConstants.h"
+
+#include <ctime>
+#include <sstream>
+#include <xmltooling/XMLToolingConfig.h>
+#include <xmltooling/util/DateTime.h>
+#include <xmltooling/util/NDC.h>
+#include <xmltooling/util/XMLHelper.h>
+
+using namespace shibsp;
+using namespace xmltooling;
+using namespace std;
+
+namespace shibsp {
+
+    class RemotedCache;
+    class RemotedSession : public virtual Session
+    {
+    public:
+        RemotedSession(RemotedCache* cache, DDF& obj) : m_version(obj["version"].integer()), m_obj(obj),
+                m_expires(0), m_lastAccess(time(NULL)), m_cache(cache), m_lock(NULL) {
+            auto_ptr_XMLCh exp(m_obj["expires"].string());
+            if (exp.get()) {
+                DateTime iso(exp.get());
+                iso.parseDateTime();
+                m_expires = iso.getEpoch();
+            }
+
+            m_lock = Mutex::create();
+        }
+        
+        ~RemotedSession() {
+            delete m_lock;
+            m_obj.destroy();
+            for_each(m_attributes.begin(), m_attributes.end(), xmltooling::cleanup<Attribute>());
+        }
+        
+        Lockable* lock() {
+            m_lock->lock();
+            return this;
+        }
+        void unlock() {
+            m_lock->unlock();
+        }
+
+        const char* getID() const {
+            return m_obj.name();
+        }
+        const char* getApplicationID() const {
+            return m_obj["application_id"].string();
+        }
+        const char* getClientAddress() const {
+            return m_obj["client_addr"].string();
+        }
+        const char* getEntityID() const {
+            return m_obj["entity_id"].string();
+        }
+        const char* getProtocol() const {
+            return m_obj["protocol"].string();
+        }
+        const char* getAuthnInstant() const {
+            return m_obj["authn_instant"].string();
+        }
+        const char* getSessionIndex() const {
+            return m_obj["session_index"].string();
+        }
+        const char* getAuthnContextClassRef() const {
+            return m_obj["authncontext_class"].string();
+        }
+        const char* getAuthnContextDeclRef() const {
+            return m_obj["authncontext_decl"].string();
+        }
+        const vector<Attribute*>& getAttributes() const {
+            if (m_attributes.empty())
+                unmarshallAttributes();
+            return m_attributes;
+        }
+        const multimap<string,const Attribute*>& getIndexedAttributes() const {
+            if (m_attributeIndex.empty()) {
+                if (m_attributes.empty())
+                    unmarshallAttributes();
+                for (vector<Attribute*>::const_iterator a = m_attributes.begin(); a != m_attributes.end(); ++a) {
+                    const vector<string>& aliases = (*a)->getAliases();
+                    for (vector<string>::const_iterator alias = aliases.begin(); alias != aliases.end(); ++alias)
+                        m_attributeIndex.insert(multimap<string,const Attribute*>::value_type(*alias, *a));
+                }
+            }
+            return m_attributeIndex;
+        }
+        const vector<const char*>& getAssertionIDs() const {
+            if (m_ids.empty()) {
+                DDF ids = m_obj["assertions"];
+                DDF id = ids.first();
+                while (id.isstring()) {
+                    m_ids.push_back(id.string());
+                    id = ids.next();
+                }
+            }
+            return m_ids;
+        }
+        
+        time_t expires() const { return m_expires; }
+        time_t lastAccess() const { return m_lastAccess; }
+        void validate(const Application& application, const char* client_addr, time_t* timeout);
+
+    private:
+        void unmarshallAttributes() const;
+
+        int m_version;
+        mutable DDF m_obj;
+        mutable vector<Attribute*> m_attributes;
+        mutable multimap<string,const Attribute*> m_attributeIndex;
+        mutable vector<const char*> m_ids;
+        time_t m_expires,m_lastAccess;
+        RemotedCache* m_cache;
+        Mutex* m_lock;
+    };
+    
+    class RemotedCache : public SessionCache
+    {
+    public:
+        RemotedCache(const DOMElement* e);
+        ~RemotedCache();
+    
+        Session* find(const char* key, const Application& application, const char* client_addr=NULL, time_t* timeout=NULL);
+        void remove(const char* key, const Application& application);
+        
+        void cleanup();
+    
+        Category& m_log;
+    private:
+        const DOMElement* m_root;         // Only valid during initialization
+        RWLock* m_lock;
+        map<string,RemotedSession*> m_hashtable;
+    
+        void dormant(const char* key);
+        static void* cleanup_fn(void*);
+        bool shutdown;
+        CondWait* shutdown_wait;
+        Thread* cleanup_thread;
+    };
+
+    SessionCache* SHIBSP_DLLLOCAL RemotedCacheFactory(const DOMElement* const & e)
+    {
+        return new RemotedCache(e);
+    }
+}
+
+void RemotedSession::unmarshallAttributes() const
+{
+    Attribute* attribute;
+    DDF attrs = m_obj["attributes"];
+    DDF attr = attrs.first();
+    while (!attr.isnull()) {
+        try {
+            attribute = Attribute::unmarshall(attr);
+            m_attributes.push_back(attribute);
+            if (m_cache->m_log.isDebugEnabled())
+                m_cache->m_log.debug("unmarshalled attribute (ID: %s) with %d value%s",
+                    attribute->getId(), attr.first().integer(), attr.first().integer()!=1 ? "s" : "");
+        }
+        catch (AttributeException& ex) {
+            const char* id = attr.first().name();
+            m_cache->m_log.error("error unmarshalling attribute (ID: %s): %s", id ? id : "none", ex.what());
+        }
+        attr = attrs.next();
+    }
+}
+
+void RemotedSession::validate(const Application& application, const char* client_addr, time_t* timeout)
+{
+    // Basic expiration?
+    time_t now = time(NULL);
+    if (now > m_expires) {
+        m_cache->m_log.info("session expired (ID: %s)", getID());
+        throw opensaml::RetryableProfileException("Your session has expired, and you must re-authenticate.");
+    }
+
+    // Address check?
+    if (client_addr) {
+        if (m_cache->m_log.isDebugEnabled())
+            m_cache->m_log.debug("comparing client address %s against %s", client_addr, getClientAddress());
+        if (strcmp(getClientAddress(),client_addr)) {
+            m_cache->m_log.warn("client address mismatch");
+            throw opensaml::RetryableProfileException(
+                "Your IP address ($1) does not match the address recorded at the time the session was established.",
+                params(1,client_addr)
+                );
+        }
+    }
+
+    if (!timeout)
+        return;
+    
+    DDF in("touch::"REMOTED_SESSION_CACHE"::SessionCache"), out;
+    DDFJanitor jin(in);
+    in.structure();
+    in.addmember("key").string(getID());
+    in.addmember("version").integer(m_obj["version"].integer());
+    if (*timeout) {
+        // On 64-bit Windows, time_t doesn't fit in a long, so I'm using ISO timestamps.  
+#ifndef HAVE_GMTIME_R
+        struct tm* ptime=gmtime(timeout);
+#else
+        struct tm res;
+        struct tm* ptime=gmtime_r(timeout,&res);
+#endif
+        char timebuf[32];
+        strftime(timebuf,32,"%Y-%m-%dT%H:%M:%SZ",ptime);
+        in.addmember("timeout").string(timebuf);
+    }
+
+    try {
+        out=application.getServiceProvider().getListenerService()->send(in);
+    }
+    catch (...) {
+        out.destroy();
+        throw;
+    }
+
+    if (out.isstruct()) {
+        // We got an updated record back.
+        m_ids.clear();
+        for_each(m_attributes.begin(), m_attributes.end(), xmltooling::cleanup<Attribute>());
+        m_attributes.clear();
+        m_attributeIndex.clear();
+        m_obj.destroy();
+        m_obj = out;
+    }
+
+    m_lastAccess = now;
+}
+
+RemotedCache::RemotedCache(const DOMElement* e)
+    : SessionCache(e, 900), m_log(Category::getInstance(SHIBSP_LOGCAT".SessionCache")), m_root(e), m_lock(NULL), shutdown(false)
+{
+    if (!SPConfig::getConfig().getServiceProvider()->getListenerService())
+        throw ConfigurationException("RemotedCacheService requires a ListenerService, but none available.");
+        
+    m_lock = RWLock::create();
+    shutdown_wait = CondWait::create();
+    cleanup_thread = Thread::create(&cleanup_fn, (void*)this);
+}
+
+RemotedCache::~RemotedCache()
+{
+    // Shut down the cleanup thread and let it know...
+    shutdown = true;
+    shutdown_wait->signal();
+    cleanup_thread->join(NULL);
+
+    for_each(m_hashtable.begin(),m_hashtable.end(),xmltooling::cleanup_pair<string,RemotedSession>());
+    delete m_lock;
+    delete shutdown_wait;
+}
+
+Session* RemotedCache::find(const char* key, const Application& application, const char* client_addr, time_t* timeout)
+{
+#ifdef _DEBUG
+    xmltooling::NDC ndc("find");
+#endif
+
+    RemotedSession* session=NULL;
+    m_log.debug("searching local cache for session (%s)", key);
+    m_lock->rdlock();
+    map<string,RemotedSession*>::const_iterator i=m_hashtable.find(key);
+    if (i==m_hashtable.end()) {
+        m_lock->unlock();
+        m_log.debug("session not found locally, searching remote cache");
+
+        DDF in("find::"REMOTED_SESSION_CACHE"::SessionCache"), out;
+        DDFJanitor jin(in);
+        in.structure();
+        in.addmember("key").string(key);
+        in.addmember("application_id").string(application.getId());
+        if (timeout && *timeout) {
+            // On 64-bit Windows, time_t doesn't fit in a long, so I'm using ISO timestamps.  
+#ifndef HAVE_GMTIME_R
+            struct tm* ptime=gmtime(timeout);
+#else
+            struct tm res;
+            struct tm* ptime=gmtime_r(timeout,&res);
+#endif
+            char timebuf[32];
+            strftime(timebuf,32,"%Y-%m-%dT%H:%M:%SZ",ptime);
+            in.addmember("timeout").string(timebuf);
+        }
+        
+        try {
+            out=application.getServiceProvider().getListenerService()->send(in);
+            if (!out.isstruct()) {
+                out.destroy();
+                m_log.debug("session not found in remote cache");
+                return NULL;
+            }
+            
+            // Wrap the results in a local entry and save it.
+            session = new RemotedSession(this, out);
+            // The remote end has handled timeout issues, we handle address and expiration checks.
+            timeout = NULL;
+        }
+        catch (...) {
+            out.destroy();
+            throw;
+        }
+
+        // Lock for writing and repeat the search to avoid duplication.
+        m_lock->wrlock();
+        SharedLock shared(m_lock, false);
+        if (m_hashtable.count(key)) {
+            // We're using an existing session entry.
+            delete session;
+            session = m_hashtable[key];
+            session->lock();
+        }
+        else {
+            m_hashtable[key]=session;
+            session->lock();
+        }
+    }
+    else {
+        // Save off and lock the session.
+        session = i->second;
+        session->lock();
+        m_lock->unlock();
+        
+        m_log.debug("session found locally, validating it for use");
+    }
+
+    if (!XMLString::equals(session->getApplicationID(), application.getId())) {
+        m_log.error("an application (%s) tried to access another application's session", application.getId());
+        session->unlock();
+        return NULL;
+    }
+
+    // Verify currency and update the timestamp if indicated by caller.
+    try {
+        session->validate(application, client_addr, timeout);
+    }
+    catch (...) {
+        session->unlock();
+        remove(key, application);
+        throw;
+    }
+    
+    return session;
+}
+
+void RemotedCache::remove(const char* key, const Application& application)
+{
+    // Take care of local copy.
+    dormant(key);
+    
+    // Now remote...
+    DDF in("remove::"REMOTED_SESSION_CACHE"::SessionCache");
+    DDFJanitor jin(in);
+    in.structure();
+    in.addmember("key").string(key);
+    in.addmember("application_id").string(application.getId());
+    
+    DDF out = application.getServiceProvider().getListenerService()->send(in);
+    out.destroy();
+}
+
+void RemotedCache::dormant(const char* key)
+{
+#ifdef _DEBUG
+    xmltooling::NDC ndc("dormant");
+#endif
+
+    m_log.debug("deleting local copy of session (%s)", key);
+
+    // lock the cache for writing, which means we know nobody is sitting in find()
+    m_lock->wrlock();
+
+    // grab the entry from the table
+    map<string,RemotedSession*>::const_iterator i=m_hashtable.find(key);
+    if (i==m_hashtable.end()) {
+        m_lock->unlock();
+        return;
+    }
+
+    // ok, remove the entry and lock it
+    RemotedSession* entry=i->second;
+    m_hashtable.erase(key);
+    entry->lock();
+    
+    // unlock the cache
+    m_lock->unlock();
+
+    // we can release the cache entry lock because we know we're not in the cache anymore
+    entry->unlock();
+
+    delete entry;
+}
+
+void RemotedCache::cleanup()
+{
+#ifdef _DEBUG
+    xmltooling::NDC ndc("cleanup");
+#endif
+
+    Mutex* mutex = Mutex::create();
+  
+    // Load our configuration details...
+    static const XMLCh cleanupInterval[] = UNICODE_LITERAL_15(c,l,e,a,n,u,p,I,n,t,e,r,v,a,l);
+    const XMLCh* tag=m_root ? m_root->getAttributeNS(NULL,cleanupInterval) : NULL;
+    int rerun_timer = 900;
+    if (tag && *tag)
+        rerun_timer = XMLString::parseInt(tag);
+    if (rerun_timer <= 0)
+        rerun_timer = 900;
+
+    mutex->lock();
+
+    m_log.info("cleanup thread started...run every %d secs; timeout after %d secs", rerun_timer, m_cacheTimeout);
+
+    while (!shutdown) {
+        shutdown_wait->timedwait(mutex,rerun_timer);
+        if (shutdown)
+            break;
+
+        // Ok, let's run through the cleanup process and clean out
+        // really old sessions.  This is a two-pass process.  The
+        // first pass is done holding a read-lock while we iterate over
+        // the cache.  The second pass doesn't need a lock because
+        // the 'deletes' will lock the cache.
+    
+        // Pass 1: iterate over the map and find all entries that have not been
+        // used in X hours
+        vector<string> stale_keys;
+        time_t stale = time(NULL) - m_cacheTimeout;
+    
+        m_log.debug("cleanup thread running");
+
+        m_lock->rdlock();
+        for (map<string,RemotedSession*>::const_iterator i=m_hashtable.begin(); i!=m_hashtable.end(); ++i) {
+            // If the last access was BEFORE the stale timeout...
+            i->second->lock();
+            time_t last=i->second->lastAccess();
+            i->second->unlock();
+            if (last < stale)
+                stale_keys.push_back(i->first);
+        }
+        m_lock->unlock();
+    
+        if (!stale_keys.empty()) {
+            m_log.info("purging %d old sessions", stale_keys.size());
+    
+            // Pass 2: walk through the list of stale entries and remove them from the cache
+            for (vector<string>::const_iterator j = stale_keys.begin(); j != stale_keys.end(); ++j)
+                dormant(j->c_str());
+        }
+
+        m_log.debug("cleanup thread completed");
+    }
+
+    m_log.info("cleanup thread exiting");
+
+    mutex->unlock();
+    delete mutex;
+    Thread::exit(NULL);
+}
+
+void* RemotedCache::cleanup_fn(void* cache_p)
+{
+    RemotedCache* cache = reinterpret_cast<RemotedCache*>(cache_p);
+
+#ifndef WIN32
+    // First, let's block all signals 
+    Thread::mask_all_signals();
+#endif
+
+    // Now run the cleanup process.
+    cache->cleanup();
+    return NULL;
+}
index 5aa9a7e..4d20c95 100644 (file)
@@ -118,7 +118,7 @@ namespace shibsp {
                 for (vector<Attribute*>::const_iterator a = m_attributes.begin(); a != m_attributes.end(); ++a) {
                     const vector<string>& aliases = (*a)->getAliases();
                     for (vector<string>::const_iterator alias = aliases.begin(); alias != aliases.end(); ++alias)
-                        m_attributeIndex.insert(make_pair(*alias, *a));
+                        m_attributeIndex.insert(multimap<string,const Attribute*>::value_type(*alias, *a));
                 }
             }
             return m_attributeIndex;
@@ -227,7 +227,7 @@ void StoredSession::unmarshallAttributes() const
         try {
             attribute = Attribute::unmarshall(attr);
             m_attributes.push_back(attribute);
-            m_attributeIndex.insert(make_pair(attribute->getId(), attribute));
+            m_attributeIndex.insert(multimap<string,const Attribute*>::value_type(attribute->getId(), attribute));
             if (m_cache->m_log.isDebugEnabled())
                 m_cache->m_log.debug("unmarshalled attribute (ID: %s) with %d value%s",
                     attribute->getId(), attr.first().integer(), attr.first().integer()!=1 ? "s" : "");
index 1f2174b..1d214ee 100644 (file)
-/*\r
- *  Copyright 2001-2007 Internet2\r
- * \r
- * Licensed under the Apache License, Version 2.0 (the "License");\r
- * you may not use this file except in compliance with the License.\r
- * You may obtain a copy of the License at\r
- *\r
- *     http://www.apache.org/licenses/LICENSE-2.0\r
- *\r
- * Unless required by applicable law or agreed to in writing, software\r
- * distributed under the License is distributed on an "AS IS" BASIS,\r
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\r
- * See the License for the specific language governing permissions and\r
- * limitations under the License.\r
- */\r
-\r
-/** XMLRequestMapper.cpp\r
- * \r
- * XML-based RequestMapper implementation\r
- */\r
-\r
-#include "internal.h"\r
-#include "exceptions.h"\r
-#include "AccessControl.h"\r
-#include "RequestMapper.h"\r
-#include "SPRequest.h"\r
-#include "util/DOMPropertySet.h"\r
-#include "util/SPConstants.h"\r
-\r
-#include <xmltooling/util/NDC.h>\r
-#include <xmltooling/util/ReloadableXMLFile.h>\r
-#include <xmltooling/util/XMLHelper.h>\r
-#include <xercesc/util/XMLUniDefs.hpp>\r
-#include <xercesc/util/regx/RegularExpression.hpp>\r
-\r
-using namespace shibsp;\r
-using namespace xmltooling;\r
-using namespace std;\r
-\r
-namespace shibsp {\r
-\r
-    // Blocks access when an ACL plugin fails to load. \r
-    class AccessControlDummy : public AccessControl\r
-    {\r
-    public:\r
-        Lockable* lock() {\r
-            return this;\r
-        }\r
-        \r
-        void unlock() {}\r
-    \r
-        aclresult_t authorized(const SPRequest& request, const Session* session) const {\r
-            return shib_acl_false;\r
-        }\r
-    };\r
-\r
-    class Override : public DOMPropertySet, public DOMNodeFilter\r
-    {\r
-    public:\r
-        Override() : m_acl(NULL) {}\r
-        Override(const DOMElement* e, Category& log, const Override* base=NULL);\r
-        ~Override();\r
-\r
-        // Provides filter to exclude special config elements.\r
-        short acceptNode(const DOMNode* node) const {\r
-            return FILTER_REJECT;\r
-        }\r
-\r
-        const Override* locate(const HTTPRequest& request) const;\r
-        AccessControl* getAC() const { return (m_acl ? m_acl : (getParent() ? dynamic_cast<const Override*>(getParent())->getAC() : NULL)); }\r
-        \r
-    protected:\r
-        void loadACL(const DOMElement* e, Category& log);\r
-        \r
-        map<string,Override*> m_map;\r
-        vector< pair<RegularExpression*,Override*> > m_regexps;\r
-        vector< pair< pair<string,RegularExpression*>,Override*> > m_queries;\r
-    \r
-    private:\r
-        AccessControl* m_acl;\r
-    };\r
-\r
-    class XMLRequestMapperImpl : public Override\r
-    {\r
-    public:\r
-        XMLRequestMapperImpl(const DOMElement* e, Category& log);\r
-\r
-        ~XMLRequestMapperImpl() {\r
-            if (m_document)\r
-                m_document->release();\r
-        }\r
-\r
-        void setDocument(DOMDocument* doc) {\r
-            m_document = doc;\r
-        }\r
-    \r
-        const Override* findOverride(const char* vhost, const HTTPRequest& request) const;\r
-\r
-    private:    \r
-        map<string,Override*> m_extras;\r
-        DOMDocument* m_document;\r
-    };\r
-\r
-#if defined (_MSC_VER)\r
-    #pragma warning( push )\r
-    #pragma warning( disable : 4250 )\r
-#endif\r
-\r
-    class XMLRequestMapper : public RequestMapper, public ReloadableXMLFile\r
-    {\r
-    public:\r
-        XMLRequestMapper(const DOMElement* e) : ReloadableXMLFile(e,Category::getInstance(SHIBSP_LOGCAT".RequestMapper")), m_impl(NULL) {\r
-            load();\r
-        }\r
-\r
-        ~XMLRequestMapper() {\r
-            delete m_impl;\r
-        }\r
-\r
-        Settings getSettings(const HTTPRequest& request) const;\r
-\r
-    protected:\r
-        pair<bool,DOMElement*> load();\r
-\r
-    private:\r
-        XMLRequestMapperImpl* m_impl;\r
-    };\r
-\r
-#if defined (_MSC_VER)\r
-    #pragma warning( pop )\r
-#endif\r
-\r
-    RequestMapper* SHIBSP_DLLLOCAL XMLRequestMapperFactory(const DOMElement* const & e)\r
-    {\r
-        return new XMLRequestMapper(e);\r
-    }\r
-\r
-    static const XMLCh _AccessControl[] =           UNICODE_LITERAL_13(A,c,c,e,s,s,C,o,n,t,r,o,l);\r
-    static const XMLCh AccessControlProvider[] =    UNICODE_LITERAL_21(A,c,c,e,s,s,C,o,n,t,r,o,l,P,r,o,v,i,d,e,r);\r
-    static const XMLCh Host[] =                     UNICODE_LITERAL_4(H,o,s,t);\r
-    static const XMLCh HostRegex[] =                UNICODE_LITERAL_9(H,o,s,t,R,e,g,e,x);\r
-    static const XMLCh htaccess[] =                 UNICODE_LITERAL_8(h,t,a,c,c,e,s,s);\r
-    static const XMLCh ignoreCase[] =               UNICODE_LITERAL_10(i,g,n,o,r,e,C,a,s,e);\r
-    static const XMLCh ignoreOption[] =             UNICODE_LITERAL_1(i);\r
-    static const XMLCh Path[] =                     UNICODE_LITERAL_4(P,a,t,h);\r
-    static const XMLCh PathRegex[] =                UNICODE_LITERAL_9(P,a,t,h,R,e,g,e,x);\r
-    static const XMLCh Query[] =                    UNICODE_LITERAL_5(Q,u,e,r,y);\r
-    static const XMLCh name[] =                     UNICODE_LITERAL_4(n,a,m,e);\r
-    static const XMLCh regex[] =                    UNICODE_LITERAL_5(r,e,g,e,x);\r
-    static const XMLCh _type[] =                    UNICODE_LITERAL_4(t,y,p,e);\r
-}\r
-\r
-void SHIBSP_API shibsp::registerRequestMappers()\r
-{\r
-    SPConfig& conf=SPConfig::getConfig();\r
-    conf.RequestMapperManager.registerFactory(XML_REQUEST_MAPPER, XMLRequestMapperFactory);\r
-    conf.RequestMapperManager.registerFactory(NATIVE_REQUEST_MAPPER, XMLRequestMapperFactory);\r
-}\r
-\r
-void Override::loadACL(const DOMElement* e, Category& log)\r
-{\r
-    try {\r
-        const DOMElement* acl=XMLHelper::getFirstChildElement(e,htaccess);\r
-        if (acl) {\r
-            log.info("building Apache htaccess AccessControl provider...");\r
-            m_acl=SPConfig::getConfig().AccessControlManager.newPlugin(HT_ACCESS_CONTROL,acl);\r
-        }\r
-        else {\r
-            acl=XMLHelper::getFirstChildElement(e,_AccessControl);\r
-            if (acl) {\r
-                log.info("building XML-based AccessControl provider...");\r
-                m_acl=SPConfig::getConfig().AccessControlManager.newPlugin(XML_ACCESS_CONTROL,acl);\r
-            }\r
-            else {\r
-                acl=XMLHelper::getFirstChildElement(e,AccessControlProvider);\r
-                if (acl) {\r
-                    auto_ptr_char type(acl->getAttributeNS(NULL,_type));\r
-                    log.info("building AccessControl provider of type %s...",type.get());\r
-                    m_acl=SPConfig::getConfig().AccessControlManager.newPlugin(type.get(),acl);\r
-                }\r
-            }\r
-        }\r
-    }\r
-    catch (exception& ex) {\r
-        log.crit("exception building AccessControl provider: %s", ex.what());\r
-        m_acl = new AccessControlDummy();\r
-    }\r
-}\r
-\r
-Override::Override(const DOMElement* e, Category& log, const Override* base) : m_acl(NULL)\r
-{\r
-    try {\r
-        // Load the property set.\r
-        load(e,log,this);\r
-        setParent(base);\r
-        \r
-        // Load any AccessControl provider.\r
-        loadACL(e,log);\r
-    \r
-        // Handle nested Paths.\r
-        DOMElement* path = XMLHelper::getFirstChildElement(e,Path);\r
-        for (int i=1; path; ++i, path=XMLHelper::getNextSiblingElement(path,Path)) {\r
-            const XMLCh* n=path->getAttributeNS(NULL,name);\r
-            \r
-            // Skip any leading slashes.\r
-            while (n && *n==chForwardSlash)\r
-                n++;\r
-            \r
-            // Check for empty name.\r
-            if (!n || !*n) {\r
-                log.warn("skipping Path element (%d) with empty name attribute", i);\r
-                continue;\r
-            }\r
-\r
-            // Check for an embedded slash.\r
-            int slash=XMLString::indexOf(n,chForwardSlash);\r
-            if (slash>0) {\r
-                // Copy the first path segment.\r
-                XMLCh* namebuf=new XMLCh[slash + 1];\r
-                for (int pos=0; pos < slash; pos++)\r
-                    namebuf[pos]=n[pos];\r
-                namebuf[slash]=chNull;\r
-                \r
-                // Move past the slash in the original pathname.\r
-                n=n+slash+1;\r
-                \r
-                // Skip any leading slashes again.\r
-                while (*n==chForwardSlash)\r
-                    n++;\r
-                \r
-                if (*n) {\r
-                    // Create a placeholder Path element for the first path segment and replant under it.\r
-                    DOMElement* newpath=path->getOwnerDocument()->createElementNS(shibspconstants::SHIB2SPCONFIG_NS,Path);\r
-                    newpath->setAttributeNS(NULL,name,namebuf);\r
-                    path->setAttributeNS(NULL,name,n);\r
-                    path->getParentNode()->replaceChild(newpath,path);\r
-                    newpath->appendChild(path);\r
-                    \r
-                    // Repoint our locals at the new parent.\r
-                    path=newpath;\r
-                    n=path->getAttributeNS(NULL,name);\r
-                }\r
-                else {\r
-                    // All we had was a pathname with trailing slash(es), so just reset it without them.\r
-                    path->setAttributeNS(NULL,name,namebuf);\r
-                    n=path->getAttributeNS(NULL,name);\r
-                }\r
-                delete[] namebuf;\r
-            }\r
-            \r
-            Override* o=new Override(path,log,this);\r
-            pair<bool,const char*> name=o->getString("name");\r
-            char* dup=strdup(name.second);\r
-            for (char* pch=dup; *pch; pch++)\r
-                *pch=tolower(*pch);\r
-            if (m_map.count(dup)) {\r
-                log.warn("skipping duplicate Path element (%s)",dup);\r
-                free(dup);\r
-                delete o;\r
-                continue;\r
-            }\r
-            m_map[dup]=o;\r
-            log.debug("added Path mapping (%s)", dup);\r
-            free(dup);\r
-        }\r
-\r
-        if (!XMLString::equals(e->getLocalName(), PathRegex)) {\r
-            // Handle nested PathRegexs.\r
-            path = XMLHelper::getFirstChildElement(e,PathRegex);\r
-            for (int i=1; path; ++i, path=XMLHelper::getNextSiblingElement(path,PathRegex)) {\r
-                const XMLCh* n=path->getAttributeNS(NULL,regex);\r
-                if (!n || !*n) {\r
-                    log.warn("skipping PathRegex element (%d) with empty regex attribute",i);\r
-                    continue;\r
-                }\r
-\r
-                auto_ptr<Override> o(new Override(path,log,this));\r
-\r
-                const XMLCh* flag=path->getAttributeNS(NULL,ignoreCase);\r
-                try {\r
-                    auto_ptr<RegularExpression> re(\r
-                        new RegularExpression(n, (flag && (*flag==chLatin_f || *flag==chDigit_0)) ? &chNull : ignoreOption)\r
-                        );\r
-                    m_regexps.push_back(make_pair(re.release(), o.release()));\r
-                }\r
-                catch (XMLException& ex) {\r
-                    auto_ptr_char tmp(ex.getMessage());\r
-                    log.error("caught exception while parsing PathRegex regular expression (%d): %s", i, tmp.get());\r
-                    throw ConfigurationException("Invalid regular expression in PathRegex element.");\r
-                }\r
-\r
-                if (log.isDebugEnabled())\r
-                    log.debug("added <PathRegex> mapping (%s)", m_regexps.back().second->getString("regex").second);\r
-            }\r
-        }\r
-\r
-        // Handle nested Querys.\r
-        path = XMLHelper::getFirstChildElement(e,Query);\r
-        for (int i=1; path; ++i, path=XMLHelper::getNextSiblingElement(path,Query)) {\r
-            const XMLCh* n=path->getAttributeNS(NULL,name);\r
-            if (!n || !*n) {\r
-                log.warn("skipping Query element (%d) with empty name attribute",i);\r
-                continue;\r
-            }\r
-            auto_ptr_char ntemp(n);\r
-            const XMLCh* v=path->getAttributeNS(NULL,regex);\r
-\r
-            auto_ptr<Override> o(new Override(path,log,this));\r
-            try {\r
-                RegularExpression* re = NULL;\r
-                if (v && *v)\r
-                    re = new RegularExpression(v);\r
-                m_queries.push_back(make_pair(make_pair(ntemp.get(),re), o.release()));\r
-            }\r
-            catch (XMLException& ex) {\r
-                auto_ptr_char tmp(ex.getMessage());\r
-                log.error("caught exception while parsing Query regular expression (%d): %s", i, tmp.get());\r
-                throw ConfigurationException("Invalid regular expression in Query element.");\r
-            }\r
-            \r
-            log.debug("added <Query> mapping (%s)", ntemp.get());\r
-        }\r
-    }\r
-    catch (exception&) {\r
-        delete m_acl;\r
-        for_each(m_map.begin(),m_map.end(),xmltooling::cleanup_pair<string,Override>());\r
-        for (vector< pair<RegularExpression*,Override*> >::iterator i = m_regexps.begin(); i != m_regexps.end(); ++i) {\r
-            delete i->first;\r
-            delete i->second;\r
-        }\r
-        for (vector< pair< pair<string,RegularExpression*>,Override*> >::iterator j = m_queries.begin(); j != m_queries.end(); ++j) {\r
-            delete j->first.second;\r
-            delete j->second;\r
-        }\r
-        throw;\r
-    }\r
-}\r
-\r
-Override::~Override()\r
-{\r
-    delete m_acl;\r
-    for_each(m_map.begin(),m_map.end(),xmltooling::cleanup_pair<string,Override>());\r
-    for (vector< pair<RegularExpression*,Override*> >::iterator i = m_regexps.begin(); i != m_regexps.end(); ++i) {\r
-        delete i->first;\r
-        delete i->second;\r
-    }\r
-    for (vector< pair< pair<string,RegularExpression*>,Override*> >::iterator j = m_queries.begin(); j != m_queries.end(); ++j) {\r
-        delete j->first.second;\r
-        delete j->second;\r
-    }\r
-}\r
-\r
-const Override* Override::locate(const HTTPRequest& request) const\r
-{\r
-    // This function is confusing because it's *not* recursive.\r
-    // The whole path is tokenized and mapped in a loop, so the\r
-    // path parameter starts with the entire request path and\r
-    // we can skip the leading slash as irrelevant.\r
-    const char* path = request.getRequestURI();\r
-    if (*path == '/')\r
-        path++;\r
-\r
-    // Now we copy the path, chop the query string, and lower case it.\r
-    char* dup=strdup(path);\r
-    char* sep=strchr(dup,'?');\r
-    if (sep)\r
-        *sep=0;\r
-    for (char* pch=dup; *pch; pch++)\r
-        *pch=tolower(*pch);\r
-\r
-    // Default is for the current object to provide settings.\r
-    const Override* o=this;\r
-\r
-    // Tokenize the path by segment and try and map each segment.\r
-#ifdef HAVE_STRTOK_R\r
-    char* pos=NULL;\r
-    const char* token=strtok_r(dup,"/",&pos);\r
-#else\r
-    const char* token=strtok(dup,"/");\r
-#endif\r
-    while (token) {\r
-        map<string,Override*>::const_iterator i=o->m_map.find(token);\r
-        if (i==o->m_map.end())\r
-            break;  // Once there's no match, we've consumed as much of the path as possible here.\r
-        // We found a match, so reset the settings pointer.\r
-        o=i->second;\r
-        \r
-        // We descended a step down the path, so we need to advance the original\r
-        // parameter for the regex step later.\r
-        path += strlen(token);\r
-        if (*path == '/')\r
-            path++;\r
-\r
-        // Get the next segment, if any.\r
-#ifdef HAVE_STRTOK_R\r
-        token=strtok_r(NULL,"/",&pos);\r
-#else\r
-        token=strtok(NULL,"/");\r
-#endif\r
-    }\r
-\r
-    free(dup);\r
-\r
-    // If there's anything left, we try for a regex match on the rest of the path minus the query string.\r
-    if (*path) {\r
-        string path2(path);\r
-        path2 = path2.substr(0,path2.find('?'));\r
-\r
-        for (vector< pair<RegularExpression*,Override*> >::const_iterator re = o->m_regexps.begin(); re != o->m_regexps.end(); ++re) {\r
-            if (re->first->matches(path2.c_str())) {\r
-                o = re->second;\r
-                break;\r
-            }\r
-        }\r
-    }\r
-\r
-    // Finally, check for query string matches. This is another "unrolled" recursive descent in a loop.\r
-    bool descended;\r
-    do {\r
-        descended = false;\r
-        for (vector< pair< pair<string,RegularExpression*>,Override*> >::const_iterator q = o->m_queries.begin(); !descended && q != o->m_queries.end(); ++q) {\r
-            vector<const char*> vals;\r
-            if (request.getParameters(q->first.first.c_str(), vals)) {\r
-                if (q->first.second) {\r
-                    // We have to match one of the values.\r
-                    for (vector<const char*>::const_iterator v = vals.begin(); v != vals.end(); ++v) {\r
-                        if (q->first.second->matches(*v)) {\r
-                            o = q->second;\r
-                            descended = true;\r
-                            break;\r
-                        }\r
-                    }\r
-                }\r
-                else {\r
-                    // The simple presence of the parameter is sufficient to match.\r
-                    o = q->second;\r
-                    descended = true;\r
-                }\r
-            }\r
-        }\r
-    } while (descended);\r
-\r
-    return o;\r
-}\r
-\r
-XMLRequestMapperImpl::XMLRequestMapperImpl(const DOMElement* e, Category& log) : m_document(NULL)\r
-{\r
-#ifdef _DEBUG\r
-    xmltooling::NDC ndc("XMLRequestMapperImpl");\r
-#endif\r
-\r
-    // Load the property set.\r
-    load(e,log,this);\r
-    \r
-    // Load any AccessControl provider.\r
-    loadACL(e,log);\r
-\r
-    // Loop over the HostRegex elements.\r
-    const DOMElement* host = XMLHelper::getFirstChildElement(e,HostRegex);\r
-    for (int i=1; host; ++i, host=XMLHelper::getNextSiblingElement(host,HostRegex)) {\r
-        const XMLCh* n=host->getAttributeNS(NULL,regex);\r
-        if (!n || !*n) {\r
-            log.warn("Skipping HostRegex element (%d) with empty regex attribute",i);\r
-            continue;\r
-        }\r
-\r
-        auto_ptr<Override> o(new Override(host,log,this));\r
-\r
-        const XMLCh* flag=host->getAttributeNS(NULL,ignoreCase);\r
-        try {\r
-            auto_ptr<RegularExpression> re(\r
-                new RegularExpression(n, (flag && (*flag==chLatin_f || *flag==chDigit_0)) ? &chNull : ignoreOption)\r
-                );\r
-            m_regexps.push_back(make_pair(re.release(), o.release()));\r
-        }\r
-        catch (XMLException& ex) {\r
-            auto_ptr_char tmp(ex.getMessage());\r
-            log.error("caught exception while parsing HostRegex regular expression (%d): %s", i, tmp.get());\r
-        }\r
-\r
-        log.debug("Added <HostRegex> mapping for %s", m_regexps.back().second->getString("regex").second);\r
-    }\r
-\r
-    // Loop over the Host elements.\r
-    host = XMLHelper::getFirstChildElement(e,Host);\r
-    for (int i=1; host; ++i, host=XMLHelper::getNextSiblingElement(host,Host)) {\r
-        const XMLCh* n=host->getAttributeNS(NULL,name);\r
-        if (!n || !*n) {\r
-            log.warn("Skipping Host element (%d) with empty name attribute",i);\r
-            continue;\r
-        }\r
-        \r
-        Override* o=new Override(host,log,this);\r
-        pair<bool,const char*> name=o->getString("name");\r
-        pair<bool,const char*> scheme=o->getString("scheme");\r
-        pair<bool,const char*> port=o->getString("port");\r
-        \r
-        char* dup=strdup(name.second);\r
-        for (char* pch=dup; *pch; pch++)\r
-            *pch=tolower(*pch);\r
-        auto_ptr<char> dupwrap(dup);\r
-\r
-        if (!scheme.first && port.first) {\r
-            // No scheme, but a port, so assume http.\r
-            scheme = pair<bool,const char*>(true,"http");\r
-        }\r
-        else if (scheme.first && !port.first) {\r
-            // Scheme, no port, so default it.\r
-            // XXX Use getservbyname instead?\r
-            port.first = true;\r
-            if (!strcmp(scheme.second,"http"))\r
-                port.second = "80";\r
-            else if (!strcmp(scheme.second,"https"))\r
-                port.second = "443";\r
-            else if (!strcmp(scheme.second,"ftp"))\r
-                port.second = "21";\r
-            else if (!strcmp(scheme.second,"ldap"))\r
-                port.second = "389";\r
-            else if (!strcmp(scheme.second,"ldaps"))\r
-                port.second = "636";\r
-        }\r
-\r
-        if (scheme.first) {\r
-            string url(scheme.second);\r
-            url=url + "://" + dup;\r
-            \r
-            // Is this the default port?\r
-            if ((!strcmp(scheme.second,"http") && !strcmp(port.second,"80")) ||\r
-                (!strcmp(scheme.second,"https") && !strcmp(port.second,"443")) ||\r
-                (!strcmp(scheme.second,"ftp") && !strcmp(port.second,"21")) ||\r
-                (!strcmp(scheme.second,"ldap") && !strcmp(port.second,"389")) ||\r
-                (!strcmp(scheme.second,"ldaps") && !strcmp(port.second,"636"))) {\r
-                // First store a port-less version.\r
-                if (m_map.count(url) || m_extras.count(url)) {\r
-                    log.warn("Skipping duplicate Host element (%s)",url.c_str());\r
-                    delete o;\r
-                    continue;\r
-                }\r
-                m_map[url]=o;\r
-                log.debug("Added <Host> mapping for %s",url.c_str());\r
-                \r
-                // Now append the port. We use the extras vector, to avoid double freeing the object later.\r
-                url=url + ':' + port.second;\r
-                m_extras[url]=o;\r
-                log.debug("Added <Host> mapping for %s",url.c_str());\r
-            }\r
-            else {\r
-                url=url + ':' + port.second;\r
-                if (m_map.count(url) || m_extras.count(url)) {\r
-                    log.warn("Skipping duplicate Host element (%s)",url.c_str());\r
-                    delete o;\r
-                    continue;\r
-                }\r
-                m_map[url]=o;\r
-                log.debug("Added <Host> mapping for %s",url.c_str());\r
-            }\r
-        }\r
-        else {\r
-            // No scheme or port, so we enter dual hosts on http:80 and https:443\r
-            string url("http://");\r
-            url = url + dup;\r
-            if (m_map.count(url) || m_extras.count(url)) {\r
-                log.warn("Skipping duplicate Host element (%s)",url.c_str());\r
-                delete o;\r
-                continue;\r
-            }\r
-            m_map[url]=o;\r
-            log.debug("Added <Host> mapping for %s",url.c_str());\r
-            \r
-            url = url + ":80";\r
-            if (m_map.count(url) || m_extras.count(url)) {\r
-                log.warn("Skipping duplicate Host element (%s)",url.c_str());\r
-                continue;\r
-            }\r
-            m_extras[url]=o;\r
-            log.debug("Added <Host> mapping for %s",url.c_str());\r
-            \r
-            url = "https://";\r
-            url = url + dup;\r
-            if (m_map.count(url) || m_extras.count(url)) {\r
-                log.warn("Skipping duplicate Host element (%s)",url.c_str());\r
-                continue;\r
-            }\r
-            m_extras[url]=o;\r
-            log.debug("Added <Host> mapping for %s",url.c_str());\r
-            \r
-            url = url + ":443";\r
-            if (m_map.count(url) || m_extras.count(url)) {\r
-                log.warn("Skipping duplicate Host element (%s)",url.c_str());\r
-                continue;\r
-            }\r
-            m_extras[url]=o;\r
-            log.debug("Added <Host> mapping for %s",url.c_str());\r
-        }\r
-    }\r
-}\r
-\r
-const Override* XMLRequestMapperImpl::findOverride(const char* vhost, const HTTPRequest& request) const\r
-{\r
-    const Override* o=NULL;\r
-    map<string,Override*>::const_iterator i=m_map.find(vhost);\r
-    if (i!=m_map.end())\r
-        o=i->second;\r
-    else {\r
-        i=m_extras.find(vhost);\r
-        if (i!=m_extras.end())\r
-            o=i->second;\r
-        else {\r
-            for (vector< pair<RegularExpression*,Override*> >::const_iterator re = m_regexps.begin(); !o && re != m_regexps.end(); ++re) {\r
-                if (re->first->matches(vhost))\r
-                    o=re->second;\r
-            }\r
-        }\r
-    }\r
-    \r
-    return o ? o->locate(request) : this;\r
-}\r
-\r
-pair<bool,DOMElement*> XMLRequestMapper::load()\r
-{\r
-    // Load from source using base class.\r
-    pair<bool,DOMElement*> raw = ReloadableXMLFile::load();\r
-    \r
-    // If we own it, wrap it.\r
-    XercesJanitor<DOMDocument> docjanitor(raw.first ? raw.second->getOwnerDocument() : NULL);\r
-\r
-    XMLRequestMapperImpl* impl = new XMLRequestMapperImpl(raw.second,m_log);\r
-    \r
-    // If we held the document, transfer it to the impl. If we didn't, it's a no-op.\r
-    impl->setDocument(docjanitor.release());\r
-\r
-    delete m_impl;\r
-    m_impl = impl;\r
-\r
-    return make_pair(false,(DOMElement*)NULL);\r
-}\r
-\r
-RequestMapper::Settings XMLRequestMapper::getSettings(const HTTPRequest& request) const\r
-{\r
-    ostringstream vhost;\r
-    vhost << request.getScheme() << "://" << request.getHostname() << ':' << request.getPort();\r
-\r
-    const Override* o=m_impl->findOverride(vhost.str().c_str(), request);\r
-\r
-    if (m_log.isDebugEnabled()) {\r
-#ifdef _DEBUG\r
-        xmltooling::NDC ndc("getSettings");\r
-#endif\r
-        pair<bool,const char*> ret=o->getString("applicationId");\r
-        m_log.debug("mapped %s%s to %s", vhost.str().c_str(), request.getRequestURI() ? request.getRequestURI() : "", ret.second);\r
-    }\r
-\r
-    return Settings(o,o->getAC());\r
-}\r
+/*
+ *  Copyright 2001-2007 Internet2
+ * 
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+/** XMLRequestMapper.cpp
+ * 
+ * XML-based RequestMapper implementation
+ */
+
+#include "internal.h"
+#include "exceptions.h"
+#include "AccessControl.h"
+#include "RequestMapper.h"
+#include "SPRequest.h"
+#include "util/DOMPropertySet.h"
+#include "util/SPConstants.h"
+
+#include <xmltooling/util/NDC.h>
+#include <xmltooling/util/ReloadableXMLFile.h>
+#include <xmltooling/util/XMLHelper.h>
+#include <xercesc/util/XMLUniDefs.hpp>
+#include <xercesc/util/regx/RegularExpression.hpp>
+
+using namespace shibsp;
+using namespace xmltooling;
+using namespace std;
+
+namespace shibsp {
+
+    // Blocks access when an ACL plugin fails to load. 
+    class AccessControlDummy : public AccessControl
+    {
+    public:
+        Lockable* lock() {
+            return this;
+        }
+        
+        void unlock() {}
+    
+        aclresult_t authorized(const SPRequest& request, const Session* session) const {
+            return shib_acl_false;
+        }
+    };
+
+    class Override : public DOMPropertySet, public DOMNodeFilter
+    {
+    public:
+        Override() : m_acl(NULL) {}
+        Override(const DOMElement* e, Category& log, const Override* base=NULL);
+        ~Override();
+
+        // Provides filter to exclude special config elements.
+        short acceptNode(const DOMNode* node) const {
+            return FILTER_REJECT;
+        }
+
+        const Override* locate(const HTTPRequest& request) const;
+        AccessControl* getAC() const { return (m_acl ? m_acl : (getParent() ? dynamic_cast<const Override*>(getParent())->getAC() : NULL)); }
+        
+    protected:
+        void loadACL(const DOMElement* e, Category& log);
+        
+        map<string,Override*> m_map;
+        vector< pair<RegularExpression*,Override*> > m_regexps;
+        vector< pair< pair<string,RegularExpression*>,Override*> > m_queries;
+    
+    private:
+        AccessControl* m_acl;
+    };
+
+    class XMLRequestMapperImpl : public Override
+    {
+    public:
+        XMLRequestMapperImpl(const DOMElement* e, Category& log);
+
+        ~XMLRequestMapperImpl() {
+            if (m_document)
+                m_document->release();
+        }
+
+        void setDocument(DOMDocument* doc) {
+            m_document = doc;
+        }
+    
+        const Override* findOverride(const char* vhost, const HTTPRequest& request) const;
+
+    private:    
+        map<string,Override*> m_extras;
+        DOMDocument* m_document;
+    };
+
+#if defined (_MSC_VER)
+    #pragma warning( push )
+    #pragma warning( disable : 4250 )
+#endif
+
+    class XMLRequestMapper : public RequestMapper, public ReloadableXMLFile
+    {
+    public:
+        XMLRequestMapper(const DOMElement* e) : ReloadableXMLFile(e,Category::getInstance(SHIBSP_LOGCAT".RequestMapper")), m_impl(NULL) {
+            load();
+        }
+
+        ~XMLRequestMapper() {
+            delete m_impl;
+        }
+
+        Settings getSettings(const HTTPRequest& request) const;
+
+    protected:
+        pair<bool,DOMElement*> load();
+
+    private:
+        XMLRequestMapperImpl* m_impl;
+    };
+
+#if defined (_MSC_VER)
+    #pragma warning( pop )
+#endif
+
+    RequestMapper* SHIBSP_DLLLOCAL XMLRequestMapperFactory(const DOMElement* const & e)
+    {
+        return new XMLRequestMapper(e);
+    }
+
+    static const XMLCh _AccessControl[] =           UNICODE_LITERAL_13(A,c,c,e,s,s,C,o,n,t,r,o,l);
+    static const XMLCh AccessControlProvider[] =    UNICODE_LITERAL_21(A,c,c,e,s,s,C,o,n,t,r,o,l,P,r,o,v,i,d,e,r);
+    static const XMLCh Host[] =                     UNICODE_LITERAL_4(H,o,s,t);
+    static const XMLCh HostRegex[] =                UNICODE_LITERAL_9(H,o,s,t,R,e,g,e,x);
+    static const XMLCh htaccess[] =                 UNICODE_LITERAL_8(h,t,a,c,c,e,s,s);
+    static const XMLCh ignoreCase[] =               UNICODE_LITERAL_10(i,g,n,o,r,e,C,a,s,e);
+    static const XMLCh ignoreOption[] =             UNICODE_LITERAL_1(i);
+    static const XMLCh Path[] =                     UNICODE_LITERAL_4(P,a,t,h);
+    static const XMLCh PathRegex[] =                UNICODE_LITERAL_9(P,a,t,h,R,e,g,e,x);
+    static const XMLCh Query[] =                    UNICODE_LITERAL_5(Q,u,e,r,y);
+    static const XMLCh name[] =                     UNICODE_LITERAL_4(n,a,m,e);
+    static const XMLCh regex[] =                    UNICODE_LITERAL_5(r,e,g,e,x);
+    static const XMLCh _type[] =                    UNICODE_LITERAL_4(t,y,p,e);
+}
+
+void SHIBSP_API shibsp::registerRequestMappers()
+{
+    SPConfig& conf=SPConfig::getConfig();
+    conf.RequestMapperManager.registerFactory(XML_REQUEST_MAPPER, XMLRequestMapperFactory);
+    conf.RequestMapperManager.registerFactory(NATIVE_REQUEST_MAPPER, XMLRequestMapperFactory);
+}
+
+void Override::loadACL(const DOMElement* e, Category& log)
+{
+    try {
+        const DOMElement* acl=XMLHelper::getFirstChildElement(e,htaccess);
+        if (acl) {
+            log.info("building Apache htaccess AccessControl provider...");
+            m_acl=SPConfig::getConfig().AccessControlManager.newPlugin(HT_ACCESS_CONTROL,acl);
+        }
+        else {
+            acl=XMLHelper::getFirstChildElement(e,_AccessControl);
+            if (acl) {
+                log.info("building XML-based AccessControl provider...");
+                m_acl=SPConfig::getConfig().AccessControlManager.newPlugin(XML_ACCESS_CONTROL,acl);
+            }
+            else {
+                acl=XMLHelper::getFirstChildElement(e,AccessControlProvider);
+                if (acl) {
+                    auto_ptr_char type(acl->getAttributeNS(NULL,_type));
+                    log.info("building AccessControl provider of type %s...",type.get());
+                    m_acl=SPConfig::getConfig().AccessControlManager.newPlugin(type.get(),acl);
+                }
+            }
+        }
+    }
+    catch (exception& ex) {
+        log.crit("exception building AccessControl provider: %s", ex.what());
+        m_acl = new AccessControlDummy();
+    }
+}
+
+Override::Override(const DOMElement* e, Category& log, const Override* base) : m_acl(NULL)
+{
+    try {
+        // Load the property set.
+        load(e,log,this);
+        setParent(base);
+        
+        // Load any AccessControl provider.
+        loadACL(e,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(NULL,name);
+            
+            // Skip any leading slashes.
+            while (n && *n==chForwardSlash)
+                n++;
+            
+            // Check for empty name.
+            if (!n || !*n) {
+                log.warn("skipping Path element (%d) with empty name attribute", i);
+                continue;
+            }
+
+            // Check for an embedded slash.
+            int slash=XMLString::indexOf(n,chForwardSlash);
+            if (slash>0) {
+                // Copy the first path segment.
+                XMLCh* namebuf=new XMLCh[slash + 1];
+                for (int pos=0; pos < slash; pos++)
+                    namebuf[pos]=n[pos];
+                namebuf[slash]=chNull;
+                
+                // Move past the slash in the original pathname.
+                n=n+slash+1;
+                
+                // Skip any leading slashes again.
+                while (*n==chForwardSlash)
+                    n++;
+                
+                if (*n) {
+                    // Create a placeholder Path element for the first path segment and replant under it.
+                    DOMElement* newpath=path->getOwnerDocument()->createElementNS(shibspconstants::SHIB2SPCONFIG_NS,Path);
+                    newpath->setAttributeNS(NULL,name,namebuf);
+                    path->setAttributeNS(NULL,name,n);
+                    path->getParentNode()->replaceChild(newpath,path);
+                    newpath->appendChild(path);
+                    
+                    // Repoint our locals at the new parent.
+                    path=newpath;
+                    n=path->getAttributeNS(NULL,name);
+                }
+                else {
+                    // All we had was a pathname with trailing slash(es), so just reset it without them.
+                    path->setAttributeNS(NULL,name,namebuf);
+                    n=path->getAttributeNS(NULL,name);
+                }
+                delete[] namebuf;
+            }
+            
+            Override* o=new Override(path,log,this);
+            pair<bool,const char*> name=o->getString("name");
+            char* dup=strdup(name.second);
+            for (char* pch=dup; *pch; pch++)
+                *pch=tolower(*pch);
+            if (m_map.count(dup)) {
+                log.warn("skipping duplicate Path element (%s)",dup);
+                free(dup);
+                delete o;
+                continue;
+            }
+            m_map[dup]=o;
+            log.debug("added Path mapping (%s)", dup);
+            free(dup);
+        }
+
+        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(NULL,regex);
+                if (!n || !*n) {
+                    log.warn("skipping PathRegex element (%d) with empty regex attribute",i);
+                    continue;
+                }
+
+                auto_ptr<Override> o(new Override(path,log,this));
+
+                const XMLCh* flag=path->getAttributeNS(NULL,ignoreCase);
+                try {
+                    auto_ptr<RegularExpression> re(
+                        new RegularExpression(n, (flag && (*flag==chLatin_f || *flag==chDigit_0)) ? &chNull : ignoreOption)
+                        );
+                    m_regexps.push_back(make_pair(re.release(), o.release()));
+                }
+                catch (XMLException& ex) {
+                    auto_ptr_char tmp(ex.getMessage());
+                    log.error("caught exception while parsing PathRegex regular expression (%d): %s", i, tmp.get());
+                    throw ConfigurationException("Invalid regular expression in PathRegex element.");
+                }
+
+                if (log.isDebugEnabled())
+                    log.debug("added <PathRegex> mapping (%s)", m_regexps.back().second->getString("regex").second);
+            }
+        }
+
+        // Handle nested Querys.
+        path = XMLHelper::getFirstChildElement(e,Query);
+        for (int i=1; path; ++i, path=XMLHelper::getNextSiblingElement(path,Query)) {
+            const XMLCh* n=path->getAttributeNS(NULL,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(NULL,regex);
+
+            auto_ptr<Override> o(new Override(path,log,this));
+            try {
+                RegularExpression* re = NULL;
+                if (v && *v)
+                    re = new RegularExpression(v);
+                m_queries.push_back(make_pair(make_pair(string(ntemp.get()),re), o.release()));
+            }
+            catch (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.");
+            }
+            
+            log.debug("added <Query> mapping (%s)", ntemp.get());
+        }
+    }
+    catch (exception&) {
+        delete m_acl;
+        for_each(m_map.begin(),m_map.end(),xmltooling::cleanup_pair<string,Override>());
+        for (vector< pair<RegularExpression*,Override*> >::iterator i = m_regexps.begin(); i != m_regexps.end(); ++i) {
+            delete i->first;
+            delete i->second;
+        }
+        for (vector< pair< pair<string,RegularExpression*>,Override*> >::iterator j = m_queries.begin(); j != m_queries.end(); ++j) {
+            delete j->first.second;
+            delete j->second;
+        }
+        throw;
+    }
+}
+
+Override::~Override()
+{
+    delete m_acl;
+    for_each(m_map.begin(),m_map.end(),xmltooling::cleanup_pair<string,Override>());
+    for (vector< pair<RegularExpression*,Override*> >::iterator i = m_regexps.begin(); i != m_regexps.end(); ++i) {
+        delete i->first;
+        delete i->second;
+    }
+    for (vector< pair< pair<string,RegularExpression*>,Override*> >::iterator j = m_queries.begin(); j != m_queries.end(); ++j) {
+        delete j->first.second;
+        delete j->second;
+    }
+}
+
+const Override* Override::locate(const HTTPRequest& request) const
+{
+    // This function is confusing because it's *not* recursive.
+    // The whole path is tokenized and mapped in a loop, so the
+    // path parameter starts with the entire request path and
+    // we can skip the leading slash as irrelevant.
+    const char* path = request.getRequestURI();
+    if (*path == '/')
+        path++;
+
+    // Now we copy the path, chop the query string, and lower case it.
+    char* dup=strdup(path);
+    char* sep=strchr(dup,'?');
+    if (sep)
+        *sep=0;
+    for (char* pch=dup; *pch; pch++)
+        *pch=tolower(*pch);
+
+    // Default is for the current object to provide settings.
+    const Override* o=this;
+
+    // Tokenize the path by segment and try and map each segment.
+#ifdef HAVE_STRTOK_R
+    char* pos=NULL;
+    const char* token=strtok_r(dup,"/",&pos);
+#else
+    const char* token=strtok(dup,"/");
+#endif
+    while (token) {
+        map<string,Override*>::const_iterator i=o->m_map.find(token);
+        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.
+        o=i->second;
+        
+        // We descended a step down the path, so we need to advance the original
+        // parameter for the regex step later.
+        path += strlen(token);
+        if (*path == '/')
+            path++;
+
+        // Get the next segment, if any.
+#ifdef HAVE_STRTOK_R
+        token=strtok_r(NULL,"/",&pos);
+#else
+        token=strtok(NULL,"/");
+#endif
+    }
+
+    free(dup);
+
+    // If there's anything left, we try for a regex match on the rest of the path minus the query string.
+    if (*path) {
+        string path2(path);
+        path2 = path2.substr(0,path2.find('?'));
+
+        for (vector< pair<RegularExpression*,Override*> >::const_iterator re = o->m_regexps.begin(); re != o->m_regexps.end(); ++re) {
+            if (re->first->matches(path2.c_str())) {
+                o = re->second;
+                break;
+            }
+        }
+    }
+
+    // Finally, check for query string matches. This is another "unrolled" recursive descent in a loop.
+    bool descended;
+    do {
+        descended = false;
+        for (vector< pair< pair<string,RegularExpression*>,Override*> >::const_iterator q = o->m_queries.begin(); !descended && q != o->m_queries.end(); ++q) {
+            vector<const char*> vals;
+            if (request.getParameters(q->first.first.c_str(), vals)) {
+                if (q->first.second) {
+                    // We have to match one of the values.
+                    for (vector<const char*>::const_iterator v = vals.begin(); v != vals.end(); ++v) {
+                        if (q->first.second->matches(*v)) {
+                            o = q->second;
+                            descended = true;
+                            break;
+                        }
+                    }
+                }
+                else {
+                    // The simple presence of the parameter is sufficient to match.
+                    o = q->second;
+                    descended = true;
+                }
+            }
+        }
+    } while (descended);
+
+    return o;
+}
+
+XMLRequestMapperImpl::XMLRequestMapperImpl(const DOMElement* e, Category& log) : m_document(NULL)
+{
+#ifdef _DEBUG
+    xmltooling::NDC ndc("XMLRequestMapperImpl");
+#endif
+
+    // Load the property set.
+    load(e,log,this);
+    
+    // Load any AccessControl provider.
+    loadACL(e,log);
+
+    // 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(NULL,regex);
+        if (!n || !*n) {
+            log.warn("Skipping HostRegex element (%d) with empty regex attribute",i);
+            continue;
+        }
+
+        auto_ptr<Override> o(new Override(host,log,this));
+
+        const XMLCh* flag=host->getAttributeNS(NULL,ignoreCase);
+        try {
+            auto_ptr<RegularExpression> re(
+                new RegularExpression(n, (flag && (*flag==chLatin_f || *flag==chDigit_0)) ? &chNull : ignoreOption)
+                );
+            m_regexps.push_back(make_pair(re.release(), o.release()));
+        }
+        catch (XMLException& ex) {
+            auto_ptr_char tmp(ex.getMessage());
+            log.error("caught exception while parsing HostRegex regular expression (%d): %s", i, tmp.get());
+        }
+
+        log.debug("Added <HostRegex> mapping for %s", m_regexps.back().second->getString("regex").second);
+    }
+
+    // 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(NULL,name);
+        if (!n || !*n) {
+            log.warn("Skipping Host element (%d) with empty name attribute",i);
+            continue;
+        }
+        
+        Override* o=new Override(host,log,this);
+        pair<bool,const char*> name=o->getString("name");
+        pair<bool,const char*> scheme=o->getString("scheme");
+        pair<bool,const char*> port=o->getString("port");
+        
+        char* dup=strdup(name.second);
+        for (char* pch=dup; *pch; pch++)
+            *pch=tolower(*pch);
+        auto_ptr<char> dupwrap(dup);
+
+        if (!scheme.first && port.first) {
+            // No scheme, but a port, so assume http.
+            scheme = pair<bool,const char*>(true,"http");
+        }
+        else if (scheme.first && !port.first) {
+            // Scheme, no port, so default it.
+            // XXX Use getservbyname instead?
+            port.first = true;
+            if (!strcmp(scheme.second,"http"))
+                port.second = "80";
+            else if (!strcmp(scheme.second,"https"))
+                port.second = "443";
+            else if (!strcmp(scheme.second,"ftp"))
+                port.second = "21";
+            else if (!strcmp(scheme.second,"ldap"))
+                port.second = "389";
+            else if (!strcmp(scheme.second,"ldaps"))
+                port.second = "636";
+        }
+
+        if (scheme.first) {
+            string url(scheme.second);
+            url=url + "://" + dup;
+            
+            // Is this the default port?
+            if ((!strcmp(scheme.second,"http") && !strcmp(port.second,"80")) ||
+                (!strcmp(scheme.second,"https") && !strcmp(port.second,"443")) ||
+                (!strcmp(scheme.second,"ftp") && !strcmp(port.second,"21")) ||
+                (!strcmp(scheme.second,"ldap") && !strcmp(port.second,"389")) ||
+                (!strcmp(scheme.second,"ldaps") && !strcmp(port.second,"636"))) {
+                // First store a port-less version.
+                if (m_map.count(url) || m_extras.count(url)) {
+                    log.warn("Skipping duplicate Host element (%s)",url.c_str());
+                    delete o;
+                    continue;
+                }
+                m_map[url]=o;
+                log.debug("Added <Host> mapping for %s",url.c_str());
+                
+                // Now append the port. We use the extras vector, to avoid double freeing the object later.
+                url=url + ':' + port.second;
+                m_extras[url]=o;
+                log.debug("Added <Host> mapping for %s",url.c_str());
+            }
+            else {
+                url=url + ':' + port.second;
+                if (m_map.count(url) || m_extras.count(url)) {
+                    log.warn("Skipping duplicate Host element (%s)",url.c_str());
+                    delete o;
+                    continue;
+                }
+                m_map[url]=o;
+                log.debug("Added <Host> mapping for %s",url.c_str());
+            }
+        }
+        else {
+            // No scheme or port, so we enter dual hosts on http:80 and https:443
+            string url("http://");
+            url = url + dup;
+            if (m_map.count(url) || m_extras.count(url)) {
+                log.warn("Skipping duplicate Host element (%s)",url.c_str());
+                delete o;
+                continue;
+            }
+            m_map[url]=o;
+            log.debug("Added <Host> mapping for %s",url.c_str());
+            
+            url = url + ":80";
+            if (m_map.count(url) || m_extras.count(url)) {
+                log.warn("Skipping duplicate Host element (%s)",url.c_str());
+                continue;
+            }
+            m_extras[url]=o;
+            log.debug("Added <Host> mapping for %s",url.c_str());
+            
+            url = "https://";
+            url = url + dup;
+            if (m_map.count(url) || m_extras.count(url)) {
+                log.warn("Skipping duplicate Host element (%s)",url.c_str());
+                continue;
+            }
+            m_extras[url]=o;
+            log.debug("Added <Host> mapping for %s",url.c_str());
+            
+            url = url + ":443";
+            if (m_map.count(url) || m_extras.count(url)) {
+                log.warn("Skipping duplicate Host element (%s)",url.c_str());
+                continue;
+            }
+            m_extras[url]=o;
+            log.debug("Added <Host> mapping for %s",url.c_str());
+        }
+    }
+}
+
+const Override* XMLRequestMapperImpl::findOverride(const char* vhost, const HTTPRequest& request) const
+{
+    const Override* o=NULL;
+    map<string,Override*>::const_iterator i=m_map.find(vhost);
+    if (i!=m_map.end())
+        o=i->second;
+    else {
+        i=m_extras.find(vhost);
+        if (i!=m_extras.end())
+            o=i->second;
+        else {
+            for (vector< pair<RegularExpression*,Override*> >::const_iterator re = m_regexps.begin(); !o && re != m_regexps.end(); ++re) {
+                if (re->first->matches(vhost))
+                    o=re->second;
+            }
+        }
+    }
+    
+    return o ? o->locate(request) : this;
+}
+
+pair<bool,DOMElement*> XMLRequestMapper::load()
+{
+    // Load from source using base class.
+    pair<bool,DOMElement*> raw = ReloadableXMLFile::load();
+    
+    // If we own it, wrap it.
+    XercesJanitor<DOMDocument> docjanitor(raw.first ? raw.second->getOwnerDocument() : NULL);
+
+    XMLRequestMapperImpl* impl = 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());
+
+    delete m_impl;
+    m_impl = impl;
+
+    return make_pair(false,(DOMElement*)NULL);
+}
+
+RequestMapper::Settings XMLRequestMapper::getSettings(const HTTPRequest& request) const
+{
+    ostringstream vhost;
+    vhost << request.getScheme() << "://" << request.getHostname() << ':' << request.getPort();
+
+    const Override* o=m_impl->findOverride(vhost.str().c_str(), request);
+
+    if (m_log.isDebugEnabled()) {
+#ifdef _DEBUG
+        xmltooling::NDC ndc("getSettings");
+#endif
+        pair<bool,const char*> ret=o->getString("applicationId");
+        m_log.debug("mapped %s%s to %s", vhost.str().c_str(), request.getRequestURI() ? request.getRequestURI() : "", ret.second);
+    }
+
+    return Settings(o,o->getAC());
+}
index 91869c0..8002859 100644 (file)
@@ -559,7 +559,9 @@ XMLApplication::XMLApplication(
                     static const XMLCh _acl[] = UNICODE_LITERAL_9(e,x,p,o,r,t,A,C,L);
                     exportElement->setAttributeNS(NULL,_acl,exportACL.second);
                 }
-                handler = conf.HandlerManager.newPlugin(samlconstants::SAML20_BINDING_URI, make_pair(exportElement, getId()));
+                handler = conf.HandlerManager.newPlugin(
+                    samlconstants::SAML20_BINDING_URI, pair<const DOMElement*,const char*>(exportElement, getId())
+                    );
                 m_handlers.push_back(handler);
 
                 // Insert into location map. If it contains the handlerURL, we skip past that part.
@@ -1415,7 +1417,7 @@ XMLConfigImpl::XMLConfigImpl(const DOMElement* e, bool first, const XMLConfig* o
                         auto_ptr_char value(rule->getFirstChild()->getNodeValue());
                         if (provider.get() && *provider.get() && option.get() && *option.get() && value.get() && *value.get()) {
                             m_transportOptionMap[id.get()].push_back(
-                                make_pair(provider.get(), make_pair(option.get(), value.get()))
+                                make_pair(string(provider.get()), make_pair(string(option.get()), string(value.get())))
                                 );
                         }
                     }
index d302b5d..32bfadf 100644 (file)
 # include <netinet/in.h>
 #endif
 
+#ifdef HAVE_UNISTD_H
+# include <unistd.h>
+#endif
+
 using namespace shibsp;
 using namespace xmltooling;
 using namespace std;
index 05f2ee0..d7dc70f 100644 (file)
@@ -153,7 +153,7 @@ pair<bool,const char*> DOMPropertySet::getString(const char* name, const char* n
         i=m_map.find(name);
 
     if (i!=m_map.end())
-        return make_pair(true,i->second.first);
+        return pair<bool,const char*>(true,i->second.first);
     else if (m_parent)
         return m_parent->getString(name,ns);
     return pair<bool,const char*>(false,NULL);
index eee522d..93a5a42 100644 (file)
@@ -39,7 +39,7 @@ void TemplateParameters::setPropertySet(const PropertySet* props)
     time_t now = time(NULL);
 #ifdef HAVE_CTIME_R
     char timebuf[32];
-    m_map["now"] = ctime_r(&now,timebuf);
+    m_map["now"] = ctime_r(&now,timebuf,sizeof(timebuf));
 #else
     m_map["now"] = ctime(&now);
 #endif