/*
 * The OpenSAML License, Version 1.
 * Copyright (c) 2002
 * University Corporation for Advanced Internet Development, Inc.
 * All rights reserved
 *
 *
 * Redistribution and use in source and binary forms, with or without
 * modification, are permitted provided that the following conditions are met:
 *
 * Redistributions of source code must retain the above copyright notice, this
 * list of conditions and the following disclaimer.
 *
 * Redistributions in binary form must reproduce the above copyright notice,
 * this list of conditions and the following disclaimer in the documentation
 * and/or other materials provided with the distribution, if any, must include
 * the following acknowledgment: "This product includes software developed by
 * the University Corporation for Advanced Internet Development
 * <http://www.ucaid.edu>Internet2 Project. Alternately, this acknowledegement
 * may appear in the software itself, if and wherever such third-party
 * acknowledgments normally appear.
 *
 * Neither the name of OpenSAML nor the names of its contributors, nor
 * Internet2, nor the University Corporation for Advanced Internet Development,
 * Inc., nor UCAID may be used to endorse or promote products derived from this
 * software without specific prior written permission. For written permission,
 * please contact opensaml@opensaml.org
 *
 * Products derived from this software may not be called OpenSAML, Internet2,
 * UCAID, or the University Corporation for Advanced Internet Development, nor
 * may OpenSAML appear in their name, without prior written permission of the
 * University Corporation for Advanced Internet Development.
 *
 *
 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
 * AND WITH ALL FAULTS. ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
 * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY, FITNESS FOR A
 * PARTICULAR PURPOSE, AND NON-INFRINGEMENT ARE DISCLAIMED AND THE ENTIRE RISK
 * OF SATISFACTORY QUALITY, PERFORMANCE, ACCURACY, AND EFFORT IS WITH LICENSEE.
 * IN NO EVENT SHALL THE COPYRIGHT OWNER, CONTRIBUTORS OR THE UNIVERSITY
 * CORPORATION FOR ADVANCED INTERNET DEVELOPMENT, INC. BE LIABLE FOR ANY DIRECT,
 * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
 * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
 * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
 * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
 * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 */


/* saml.h - OpenSAML header file

   Scott Cantor
   5/7/02

   $History:$
*/

#ifndef __saml_h__
#define __saml_h__

#include <xercesc/dom/DOM.hpp>
#include <xercesc/util/XMLString.hpp>
#include <xercesc/util/XMLDateTime.hpp>
#include <xercesc/sax/InputSource.hpp>
#include <xercesc/util/BinInputStream.hpp>
#include <xercesc/framework/Wrapper4InputSource.hpp>
#include <xsec/dsig/DSIGSignature.hpp>

using namespace xercesc;

#ifdef WIN32
# include <windows.h>
# ifndef SAML_EXPORTS
#  define SAML_EXPORTS __declspec(dllimport)
# endif
# pragma warning(disable : 4786 4231 4251 4503)
#else
# define __stdcall
# define __cdecl
# define SAML_EXPORTS
#endif

/* Xerces defines its own macro for wide literals, but on platforms without 16 bit wchar_t
   it defines away the L"" and assumes the ASCII literal is usable, which is never true.
   I convert L"foo" to L(foo) so that the macro handles the reference to the static constant,
   while the code stays somewhat similar.
 */
#define L(s) saml::XML::Literals::s
#define L_QNAME(p,s) saml::XML::Literals::p##_##s

/* RTTI and dynamic casting are completely broken in recent versions of GCC.
   The claim that they work subject to certain linker options is unhelpful for
   general use.
 */
#ifdef __GNUC__DISABLE
# define NO_RTTI
# define RTTI_BASE              0
# define RTTI_EXTENSION_BASE    1000
# define dynaptr(type,ptr) static_cast<type*>(ptr->_dynaptr(saml::RTTI_##type))
namespace saml {
  extern SAML_EXPORTS const unsigned short RTTI_SAMLObject;
  extern SAML_EXPORTS const unsigned short RTTI_SAMLSignedObject;
  extern SAML_EXPORTS const unsigned short RTTI_SAMLRequest;
  extern SAML_EXPORTS const unsigned short RTTI_SAMLResponse;
  extern SAML_EXPORTS const unsigned short RTTI_SAMLAssertion;
  extern SAML_EXPORTS const unsigned short RTTI_SAMLCondition;
  extern SAML_EXPORTS const unsigned short RTTI_SAMLAudienceRestrictionCondition;
  extern SAML_EXPORTS const unsigned short RTTI_SAMLQuery;
  extern SAML_EXPORTS const unsigned short RTTI_SAMLAttributeQuery;
  extern SAML_EXPORTS const unsigned short RTTI_SAMLStatement;
  extern SAML_EXPORTS const unsigned short RTTI_SAMLAttributeStatement;
  extern SAML_EXPORTS const unsigned short RTTI_SAMLAuthenticationStatement;
  extern SAML_EXPORTS const unsigned short RTTI_SAMLException;
  extern SAML_EXPORTS const unsigned short RTTI_MalformedException;
  extern SAML_EXPORTS const unsigned short RTTI_UnsupportedExtensionException;
  extern SAML_EXPORTS const unsigned short RTTI_InvalidCryptoException;
  extern SAML_EXPORTS const unsigned short RTTI_TrustException;
  extern SAML_EXPORTS const unsigned short RTTI_BindingException;
  extern SAML_EXPORTS const unsigned short RTTI_SOAPException;
  extern SAML_EXPORTS const unsigned short RTTI_ContentTypeException;
  extern SAML_EXPORTS const unsigned short RTTI_UnknownAssertionException;
  extern SAML_EXPORTS const unsigned short RTTI_ProfileException;
  extern SAML_EXPORTS const unsigned short RTTI_FatalProfileException;
  extern SAML_EXPORTS const unsigned short RTTI_RetryableProfileException;
  extern SAML_EXPORTS const unsigned short RTTI_ExpiredAssertionException;
  extern SAML_EXPORTS const unsigned short RTTI_InvalidAssertionException;
  extern SAML_EXPORTS const unsigned short RTTI_ReplayedAssertionException;
  extern SAML_EXPORTS const unsigned short RTTI_SAMLAttribute;
  extern SAML_EXPORTS const unsigned short RTTI_SAMLAuthorityBinding;
  extern SAML_EXPORTS const unsigned short RTTI_SAMLSubject;
}
# define RTTI(type) _RTTI(RTTI_##type)
#else
# define dynaptr(type,ptr) const_cast<type*>(dynamic_cast<const type*>(ptr))
# define RTTI(type)
#endif

// STL headers
#include <map>
#include <set>
#include <stack>
#include <string>
#include <vector>
#include <iostream>
#include <exception>

namespace saml
{
    // A dynamic widestring that works across platforms
    typedef std::basic_string<XMLCh> xstring;

    // Wraps a UTF-8 transcoder (both directions).
    SAML_EXPORTS char* toUTF8(const XMLCh* src);
    SAML_EXPORTS XMLCh* fromUTF8(const char* src);

    // Java-style Iterator interfaces
    template<class T> class SAML_EXPORTS Iterator
    {
    public:
        Iterator();
        Iterator(const std::vector<T>& v);
        std::vector<T> clone() const;
        bool hasNext() const;
        const T& next() const;
        size_t size() const;
        void reset() const;

    protected:
        const std::vector<T>& m_vector;
        mutable typename std::vector<T>::const_iterator m_iter;

    private:
        static std::vector<T> m_empty_vector;
    };

    template<class T> class SAML_EXPORTS ArrayIterator : public Iterator<T>
    {
    public:
        ArrayIterator(T v[]);

    protected:
        std::vector<T> m_real_vector;
    };

    template<class T> std::vector<T> Iterator<T>::m_empty_vector;

    #define EMPTY(T) saml::Iterator<T>()
    template<class T> Iterator<T>::Iterator() : m_vector(m_empty_vector)
    {
        m_iter=m_vector.begin();
    }

    template<class T> Iterator<T>::Iterator(const std::vector<T>& v) : m_vector(v)
    {
        m_iter=m_vector.begin();
    }

    template<class T> bool Iterator<T>::hasNext() const
    {
        return m_iter!=m_vector.end();
    }

    template<class T> const T& Iterator<T>::next() const
    {
        return *(m_iter++);
    }

    template<class T> size_t Iterator<T>::size() const
    {
        return m_vector.size();
    }

    template<class T> void Iterator<T>::reset() const
    {
        m_iter=m_vector.begin();
    }

    template<class T> ArrayIterator<T>::ArrayIterator(T v[]) : Iterator<T>(m_real_vector)
    {
        for (size_t i=0; v && i<(sizeof(v)/sizeof(T)); i++)
            m_real_vector.push_back(v[i]);
        m_iter=m_vector.begin();
    }

#ifdef SAML_INSTANTIATE
    class QName;
    class SAMLAttribute;
    class SAMLAuthorityBinding;
    class SAMLStatement;
    class SAMLCondition;
    class SAMLAssertion;
    template class SAML_EXPORTS saml::Iterator<xstring>;
    template class SAML_EXPORTS saml::ArrayIterator<xstring>;
    template class SAML_EXPORTS saml::Iterator<std::string>;
    template class SAML_EXPORTS saml::ArrayIterator<std::string>;
    template class SAML_EXPORTS saml::Iterator<const XMLCh*>;
    template class SAML_EXPORTS saml::ArrayIterator<const XMLCh*>;
    template class SAML_EXPORTS saml::Iterator<saml::QName>;
    template class SAML_EXPORTS saml::ArrayIterator<saml::QName>;
    template class SAML_EXPORTS saml::Iterator<XSECCryptoX509*>;
    template class SAML_EXPORTS saml::ArrayIterator<XSECCryptoX509*>;
    template class SAML_EXPORTS saml::Iterator<SAMLAttribute*>;
    template class SAML_EXPORTS saml::ArrayIterator<SAMLAttribute*>;
    template class SAML_EXPORTS saml::Iterator<SAMLAuthorityBinding*>;
    template class SAML_EXPORTS saml::ArrayIterator<SAMLAuthorityBinding*>;
    template class SAML_EXPORTS saml::Iterator<SAMLStatement*>;
    template class SAML_EXPORTS saml::ArrayIterator<SAMLStatement*>;
    template class SAML_EXPORTS saml::Iterator<SAMLCondition*>;
    template class SAML_EXPORTS saml::ArrayIterator<SAMLCondition*>;
    template class SAML_EXPORTS saml::Iterator<SAMLAssertion*>;
    template class SAML_EXPORTS saml::ArrayIterator<SAMLAssertion*>;

#ifdef NO_RTTI
    const unsigned short RTTI_SAMLObject=                           RTTI_BASE;
    const unsigned short RTTI_SAMLSignedObject=                     RTTI_BASE+1;
    const unsigned short RTTI_SAMLRequest=                          RTTI_BASE+2;
    const unsigned short RTTI_SAMLResponse=                         RTTI_BASE+3;
    const unsigned short RTTI_SAMLAssertion=                        RTTI_BASE+4;
    const unsigned short RTTI_SAMLCondition=                        RTTI_BASE+5;
    const unsigned short RTTI_SAMLAudienceRestrictionCondition=     RTTI_BASE+6;
    const unsigned short RTTI_SAMLQuery=                            RTTI_BASE+100;
    const unsigned short RTTI_SAMLAttributeQuery=                   RTTI_BASE+101;
    const unsigned short RTTI_SAMLStatement=                        RTTI_BASE+200;
    const unsigned short RTTI_SAMLAttributeStatement=               RTTI_BASE+201;
    const unsigned short RTTI_SAMLAuthenticationStatement=          RTTI_BASE+202;
    const unsigned short RTTI_SAMLException=                        RTTI_BASE+300;
    const unsigned short RTTI_MalformedException=                   RTTI_BASE+301;
    const unsigned short RTTI_UnsupportedExtensionException=        RTTI_BASE+302;
    const unsigned short RTTI_InvalidCryptoException=               RTTI_BASE+303;
    const unsigned short RTTI_TrustException=                       RTTI_BASE+304;
    const unsigned short RTTI_BindingException=                     RTTI_BASE+305;
    const unsigned short RTTI_SOAPException=                        RTTI_BASE+306;
    const unsigned short RTTI_ProfileException=                     RTTI_BASE+307;
    const unsigned short RTTI_FatalProfileException=                RTTI_BASE+308;
    const unsigned short RTTI_RetryableProfileException=            RTTI_BASE+309;
    const unsigned short RTTI_ExpiredAssertionException=            RTTI_BASE+310;
    const unsigned short RTTI_InvalidAssertionException=            RTTI_BASE+311;
    const unsigned short RTTI_ReplayedAssertionException=           RTTI_BASE+312;
    const unsigned short RTTI_ContentTypeException=                 RTTI_BASE+313;
    const unsigned short RTTI_UnknownAssertionException=            RTTI_BASE+314;
    const unsigned short RTTI_SAMLAttribute=                        RTTI_BASE+500;
    const unsigned short RTTI_SAMLAuthorityBinding=                 RTTI_BASE+501;
    const unsigned short RTTI_SAMLSubject=                          RTTI_BASE+502;
#endif
#endif

    // Abstract handling of diagnostic context for logging
    class SAML_EXPORTS NDC
    {
    public:
        NDC();
        NDC(const char* context);
        NDC(std::string& context);
        ~NDC();
    };

    // Abstracts handling of XML QNames, including namespace prefix traversal
    class SAML_EXPORTS QName
    {
    public:
        QName(const XMLCh* nsURI=NULL, const XMLCh* localName=NULL);
        ~QName();

        const XMLCh* getNamespaceURI() const;
        const XMLCh* getLocalName() const;

        static QName* getQNameAttribute(DOMElement* e, const XMLCh* nsURI, const XMLCh* localName);
        static QName* getQNameTextNode(DOMText* t);
        static const XMLCh* getNamespaceForQName(const XMLCh* qname, DOMElement* e);
        static const XMLCh* getNamespaceForPrefix(const XMLCh* prefix, DOMElement* e);

    private:
        xstring m_namespace;
        xstring m_localName;
    };

    // Needed to allow QNames in sorted STL containers
    SAML_EXPORTS bool operator<(const saml::QName& op1, const saml::QName& op2);
    
    // Wraps pseudorandom identifier generation for SAML objects
    class SAML_EXPORTS SAMLIdentifier
    {
    public:
        SAMLIdentifier();
        ~SAMLIdentifier();
        operator const XMLCh*() {return m_buffer;}

    private:
        SAMLIdentifier(const SAMLIdentifier&) {}
        SAMLIdentifier& operator=(const SAMLIdentifier&) {return *this;}
        XMLCh* m_buffer;
    };

    // Wraps basic XML handling and wide constants (ugly, but fast)
    class SAML_EXPORTS XML
    {
    public:
        // URI constants
        static const XMLCh OPENSAML_NS[];
        static const XMLCh SAML_NS[];
        static const XMLCh SAML_SCHEMA_ID[];
        static const XMLCh SAML11_SCHEMA_ID[];
        static const XMLCh SAMLP_NS[];
        static const XMLCh SAMLP_SCHEMA_ID[];
        static const XMLCh SAMLP11_SCHEMA_ID[];
        static const XMLCh SOAP11ENV_NS[];
        static const XMLCh SOAP11ENV_SCHEMA_ID[];
        static const XMLCh XMLSIG_NS[];
        static const XMLCh XMLSIG_SCHEMA_ID[];
        static const XMLCh XPATH2_NS[];
        static const XMLCh XPATH2_SCHEMA_ID[];
        static const XMLCh XMLNS_NS[];
        static const XMLCh XML_NS[];
        static const XMLCh XML_SCHEMA_ID[];
        static const XMLCh XSD_NS[];
        static const XMLCh XSI_NS[];

        struct SAML_EXPORTS Literals
        {
            // SAML vocabulary
            static const XMLCh Advice[];
            static const XMLCh Assertion[];
            static const XMLCh AssertionArtifact[];
            static const XMLCh AssertionID[];
            static const XMLCh AssertionIDRef[];
            static const XMLCh saml_AssertionIDRef[];
            static const XMLCh Attribute[];
            static const XMLCh AttributeDesignator[];
            static const XMLCh AttributeName[];
            static const XMLCh AttributeNamespace[];
            static const XMLCh AttributeQuery[];
            static const XMLCh AttributeQueryType[];
            static const XMLCh AttributeStatement[];
            static const XMLCh AttributeStatementType[];
            static const XMLCh AttributeValue[];
            static const XMLCh Audience[];
            static const XMLCh AudienceRestrictionCondition[];
            static const XMLCh AudienceRestrictionConditionType[];
            static const XMLCh AuthenticationInstant[];
            static const XMLCh AuthenticationMethod[];
            static const XMLCh AuthenticationStatement[];
            static const XMLCh AuthenticationStatementType[];
            static const XMLCh AuthorityBinding[];
            static const XMLCh AuthorityKind[];
            static const XMLCh Binding[];
            static const XMLCh Condition[];
            static const XMLCh Conditions[];
            static const XMLCh ConfirmationMethod[];
            static const XMLCh DNSAddress[];
            static const XMLCh Format[];
            static const XMLCh InResponseTo[];
            static const XMLCh IPAddress[];
            static const XMLCh Issuer[];
            static const XMLCh IssueInstant[];
            static const XMLCh Location[];
            static const XMLCh MajorVersion[];
            static const XMLCh MinorVersion[];
            static const XMLCh NameIdentifier[];
            static const XMLCh NameQualifier[];
            static const XMLCh NotBefore[];
            static const XMLCh NotOnOrAfter[];
            static const XMLCh Query[];
            static const XMLCh Recipient[];
            static const XMLCh Request[];
            static const XMLCh Requester[];
            static const XMLCh RequestID[];
            static const XMLCh Resource[];
            static const XMLCh Responder[];
            static const XMLCh RespondWith[];
            static const XMLCh Response[];
            static const XMLCh ResponseID[];
            static const XMLCh Statement[];
            static const XMLCh Status[];
            static const XMLCh StatusMessage[];
            static const XMLCh StatusCode[];
            static const XMLCh StatusDetail[];
            static const XMLCh Subject[];
            static const XMLCh saml_Subject[];
            static const XMLCh SubjectConfirmation[];
            static const XMLCh SubjectConfirmationData[];
            static const XMLCh SubjectLocality[];
            static const XMLCh SubjectQuery[];
            static const XMLCh SubjectStatement[];
            static const XMLCh Success[];
            static const XMLCh Value[];
            static const XMLCh VersionMismatch[];

            // SOAP vocabulary
            static const XMLCh Body[];
            static const XMLCh Envelope[];
            static const XMLCh Fault[];
            static const XMLCh faultcode[];
            static const XMLCh faultstring[];
            static const XMLCh Header[];
            static const XMLCh mustUnderstand[];

            // Signature vocabulary
            static const XMLCh KeyInfo[];
            static const XMLCh Signature[];
            static const XMLCh X509Data[];
            static const XMLCh X509Certificate[];

            // XML vocabulary
            static const XMLCh type[];
            static const XMLCh xml[];
            static const XMLCh xmlns[];
            static const XMLCh xmlns_xsd[];
            static const XMLCh xmlns_xsi[];
            static const XMLCh xmlns_saml[];
            static const XMLCh xmlns_samlp[];
            static const XMLCh xmlns_soap[];
            
            // OpenSAML vocabulary
            static const XMLCh ExceptionClass[];
        };

        static void registerSchema(const XMLCh* xmlns, const XMLCh* systemId, DOMEntityResolver* resolver=NULL);

        class SAML_EXPORTS ParserPool : public DOMEntityResolver, DOMErrorHandler
        {
        public:
            ParserPool();
            ~ParserPool();

            DOMBuilder* get();
            void put(DOMBuilder* p);

            void registerSchema(const XMLCh* xmlns, const XMLCh* systemId, DOMEntityResolver* resolver=NULL);

            DOMInputSource* resolveEntity(const XMLCh* const publicId, const XMLCh* const systemId, const XMLCh* const baseURI);
            bool handleError(const DOMError& e);

        private:
            typedef std::map<xstring,xstring> SchemaLocationMap;
            typedef std::map<xstring,DOMEntityResolver*> ResolverMap;
        
            SchemaLocationMap m_SchemaLocMap;
            ResolverMap m_ResolverMap;
            xstring m_schemaLocations;
            std::stack<DOMBuilder*> m_pool;
            void* m_lock;
        };

        class SAML_EXPORTS Parser
        {
        public:
            Parser();
            ~Parser();
            DOMDocument* parse(DOMInputSource& domsrc)
            {
                DOMDocument* doc=m_parser->parse(domsrc);
                m_parser->setFeature(XMLUni::fgXercesUserAdoptsDOMDocument,true);
                return doc;
            }

        private:
            Parser(const Parser&) {}
            Parser& operator=(const Parser&) { return *this; }
            DOMBuilder* m_parser;
        };

        class SAML_EXPORTS StreamInputSource : public InputSource
        {
        public:
            StreamInputSource(std::istream& is, const char* systemId=NULL) : InputSource(systemId), m_is(is) {}
            virtual BinInputStream* makeStream() const { return new StreamBinInputStream(m_is); }

        private:
            std::istream& m_is;

            class SAML_EXPORTS StreamBinInputStream : public BinInputStream
            {
            public:
                StreamBinInputStream(std::istream& is) : m_is(is), m_pos(0) {}
                virtual unsigned int curPos() const { return m_pos; }
                virtual unsigned int readBytes(XMLByte* const toFill, const unsigned int maxToRead);
            private:
                std::istream& m_is;
                unsigned int m_pos;
            };
        };
    };

    // Captures global configuration data that must be provided by library client
    class SAML_EXPORTS SAMLConfig
    {
    public:
        SAMLConfig() : compatibility_mode(false), strict_dom_checking(true), clock_skew_secs(300) {}

        // enables runtime and clients to access global configuration object
        static SAMLConfig& getConfig();

        // global per-process setup and shutdown of runtime
        virtual bool init()=0;
        virtual void term()=0;

        struct SAMLBindingConfig
        {
            SAMLBindingConfig() : timeout(-1), conn_timeout(-1) {}
            long timeout;
            long conn_timeout;
            std::string ssl_certfile;
            std::string ssl_certtype;
            std::string ssl_keyfile;
            std::string ssl_keytype;
            std::string ssl_keypass;
            std::string ssl_calist;
            std::string ssl_cadir;
        };

    /* start of externally supplied configuration */
        bool compatibility_mode;
        std::string schema_dir;
        std::string log_config;
        std::string inclusive_namespace_prefixes;
        bool strict_dom_checking;
        time_t clock_skew_secs;
        SAMLBindingConfig binding_defaults;
    /* end of externally supplied configuration */

        // global mutex available to library applications
        virtual void saml_lock() const=0;
        virtual void saml_unlock() const=0;

        // cross-platform extension library loader
        // extensions must provide:
        //      extern "C" int saml_extension_init(void* context);
        //      extern "C" void saml_extension_term();
        virtual void saml_register_extension(const char* path, void* context=NULL) const=0;
    };


    // Most of the rest of the classes implement SAML constructs. The root
    // object provides a framework for serialization/deserialization and memory
    // management that is tied to the XML parser's behavior and assumptions.
    // Extension classes should always inherit from SAMLObject and use the
    // built-in classes as examples of how to code new types.

    class SAML_EXPORTS SAMLObject
    {
    public:
        virtual DOMNode* toDOM(DOMDocument* doc=NULL, bool xmlns=true) const;
        XMLByte* toBase64(unsigned int* outputLength) const;
        virtual ~SAMLObject();
        virtual SAMLObject* clone() const=0;

        DOMDocument* setDocument(DOMDocument* doc) {DOMDocument* ret=m_document; m_document=doc; return ret;}

    protected:
        SAMLObject();
        SAMLObject(std::istream& in);
        SAMLObject(const SAMLObject&) : m_root(NULL), m_document(NULL), m_bOwnStrings(true), m_log(NULL) {}
        SAMLObject& operator=(const SAMLObject&) { return *this; }

        virtual void fromDOM(DOMElement* e);
        DOMNode* plantRoot();
#ifdef NO_RTTI
        void _RTTI(unsigned short type) { m_types.push_back(type); }
#endif

        friend SAML_EXPORTS std::ostream& operator<<(std::ostream& ostr, const SAMLObject& obj);

        mutable DOMNode* m_root;              // no memory management on DOM itself
        mutable DOMDocument* m_document;      // DOM built manually is freed with "document->release()"
        bool m_bOwnStrings;                   // Does this SAML object own its component fields?
        void* m_log;                          // Logging object, does not expose the implementation
#ifdef NO_RTTI
        std::vector<unsigned short> m_types;  // Homebrew RTTI metadata
    public:
        SAMLObject* _dynaptr(unsigned short type) const;
#endif
    };

    // Serialization of objects and wide strings (eventually will use EXC-C14N)
    SAML_EXPORTS std::ostream& operator<<(std::ostream& ostr, const DOMNode& node);
    SAML_EXPORTS std::ostream& operator<<(std::ostream& ostr, const SAMLObject& obj);
    SAML_EXPORTS std::ostream& xmlout(std::ostream& target, const XMLCh* s);

    // Wraps all internal library errors and reflects SAML status structure
    class SAML_EXPORTS SAMLException;
    extern "C" { typedef SAMLException* SAMLExceptionFactory(DOMElement*); }

    class SAML_EXPORTS SAMLException : public std::exception, public SAMLObject
    {
    public:
        static saml::QName SUCCESS;
        static saml::QName REQUESTER;
        static saml::QName RESPONDER;
        static saml::QName VERSIONMISMATCH;

        static SAMLException* getInstance(DOMElement* e);
        static SAMLException* getInstance(std::istream& in);
        static void regFactory(const char* exceptionClass, SAMLExceptionFactory* factory);
        static void unregFactory(const char* exceptionClass);

        SAMLException(const char* msg, DOMElement* detail=NULL)
            : m_typename("SAMLException"), m_detail(detail) {RTTI(SAMLException); if (msg) m_msg=msg;}
        SAMLException(const std::string& msg, DOMElement* detail=NULL)
            : m_typename("SAMLException"), m_msg(msg), m_detail(detail) {RTTI(SAMLException);}
        SAMLException(const Iterator<saml::QName>& codes, const char* msg, DOMElement* detail=NULL);
        SAMLException(const Iterator<saml::QName>& codes, const std::string& msg, DOMElement* detail=NULL);
        SAMLException(const saml::QName& code, const char* msg, DOMElement* detail=NULL);
        SAMLException(const saml::QName& code, const std::string& msg, DOMElement* detail=NULL);
        virtual ~SAMLException() throw ();

        virtual const char* what() const throw () {return (m_msg.c_str());}
        Iterator<saml::QName> getCodes() const {return m_codes;}
        DOMElement* getDetail() const {return m_detail;}

        virtual DOMNode* toDOM(DOMDocument* doc=NULL, bool xmlns=true) const;
        virtual SAMLObject* clone() const;

    protected:
        SAMLException(DOMElement* e);
        SAMLException(std::istream& in);
        virtual void fromDOM(DOMElement* e);
        const char* m_typename;

    private:
        std::vector<saml::QName> m_codes;
        std::string m_msg;
        mutable DOMElement* m_detail;

        typedef std::map<std::string,SAMLExceptionFactory*> SAMLExceptionFactoryMap;
        static SAMLExceptionFactoryMap m_map;
    };

    // Simplify derivation of exception types
    #define DECLARE_SAML_EXCEPTION(name,base) \
    class SAML_EXPORTS name : public base \
    { \
    public: \
        name(const char* msg) : base(msg) {RTTI(name); m_typename=#name;} \
        name(const std::string& msg) : base(msg) {RTTI(name); m_typename=#name;} \
        name(const Iterator<saml::QName>& codes, const char* msg) : base(codes,msg) {RTTI(name); m_typename=#name;} \
        name(const Iterator<saml::QName>& codes, const std::string& msg) : base(codes, msg) {RTTI(name); m_typename=#name;} \
        name(const saml::QName& code, const char* msg) : base(code,msg) {RTTI(name); m_typename=#name;} \
        name(const saml::QName& code, const std::string& msg) : base(code, msg) {RTTI(name); m_typename=#name;} \
        name(DOMElement* e) : base(e) {RTTI(name); m_typename=#name;} \
        name(std::istream& in) : base(in) {RTTI(name); m_typename=#name;} \
        virtual ~name() throw() {} \
    }
    
    #define SAML_EXCEPTION_FACTORY(name) \
        extern "C" SAMLException* name##Factory(DOMElement* e) \
        { \
            return new name(e); \
        }
    
    #define REGISTER_EXCEPTION_FACTORY(pkg,name) SAMLException::regFactory(#pkg"."#name,name##Factory)

    DECLARE_SAML_EXCEPTION(MalformedException,SAMLException);
    DECLARE_SAML_EXCEPTION(UnsupportedExtensionException,SAMLException);
    DECLARE_SAML_EXCEPTION(InvalidCryptoException,SAMLException);
    DECLARE_SAML_EXCEPTION(TrustException,InvalidCryptoException);
    DECLARE_SAML_EXCEPTION(BindingException,SAMLException);
    DECLARE_SAML_EXCEPTION(SOAPException,BindingException);
    DECLARE_SAML_EXCEPTION(ContentTypeException,BindingException);
    DECLARE_SAML_EXCEPTION(UnknownAssertionException,BindingException);

    class SAML_EXPORTS SAMLSignedObject : public SAMLObject
    {
    public:
        virtual ~SAMLSignedObject();

        virtual void sign(signatureMethod alg, XSECCryptoKey* k,
                          const Iterator<XSECCryptoX509*>& certs=Iterator<XSECCryptoX509*>());
        virtual void verify(XSECCryptoKey* k=NULL) const;
        virtual void verify(XSECCryptoX509& cert) const;
        virtual unsigned int getX509CertificateCount() const;
        virtual const XMLCh* getX509Certificate(unsigned int index) const;
        virtual signatureMethod getSignatureAlgorithm() const;
        bool isSigned() const;

    protected:
        SAMLSignedObject(std::istream& in);
        SAMLSignedObject() : m_signature(NULL), m_sigElement(NULL) { RTTI(SAMLSignedObject); }
        
        virtual const XMLCh* getId() const=0;
        virtual void insertSignature()=0;
        DOMElement* getSignatureElement() { return m_sigElement; }

        DSIGSignature* m_signature;
        DOMElement* m_sigElement;
    };

    class SAML_EXPORTS SAMLSubject : public SAMLObject
    {
    public:
        SAMLSubject(const XMLCh* name, const XMLCh* nameQualifier, const XMLCh* format=NULL,
                    const Iterator<const XMLCh*>& confirmationMethods=EMPTY(const XMLCh*),
                    const XMLCh* confirmationData=NULL, DOMElement* keyInfo=NULL);
        SAMLSubject(DOMElement* e);
        SAMLSubject(std::istream& in);
        virtual ~SAMLSubject();
        virtual SAMLObject* clone() const;
        virtual DOMNode* toDOM(DOMDocument* doc=NULL, bool xmlns=true) const;

        const XMLCh* getName() const {return m_name;}
        const XMLCh* getNameQualifier() const {return m_nameQualifier;}
        const XMLCh* getFormat() const {return m_format;}
        Iterator<const XMLCh*> getConfirmationMethods() const {return m_confirmationMethods;}
        const XMLCh* getConfirmationData() const {return m_confirmationData;}
        DOMElement* getKeyInfo() const {return m_keyInfo;}
        
        static const XMLCh ARTIFACT01[];
        static const XMLCh CONF_BEARER[];
        static const XMLCh HOLDER_KEY[];
        static const XMLCh SENDER_VOUCHES[];

    protected:
        virtual void fromDOM(DOMElement* e);

        const XMLCh* m_name;
        const XMLCh* m_nameQualifier;
        const XMLCh* m_format;
        std::vector<const XMLCh*> m_confirmationMethods;
        const XMLCh* m_confirmationData;
        DOMElement* m_keyInfo;

    private:
        SAMLSubject(const SAMLSubject&) {}
        SAMLSubject& operator=(const SAMLSubject&) {return *this;}
    };

    class SAML_EXPORTS SAMLAttribute;
    extern "C" { typedef SAMLAttribute* SAMLAttributeFactory(DOMElement* e); }

    class SAML_EXPORTS SAMLAttribute : public SAMLObject
    {
    public:
        SAMLAttribute(const XMLCh* name, const XMLCh* ns, const saml::QName* type=NULL, long lifetime=0,
                      const Iterator<const XMLCh*>& values=Iterator<const XMLCh*>());
        SAMLAttribute(DOMElement* e);
        SAMLAttribute(std::istream& in);
        virtual void addValues(DOMElement* e);
        virtual ~SAMLAttribute();
        virtual SAMLObject* clone() const;

        virtual const XMLCh* getName() const {return m_name;}
        virtual const XMLCh* getNamespace() const {return m_namespace;}
        virtual const QName* getType() const {return m_type;}
        virtual long getLifetime() const {return m_lifetime;}

        virtual Iterator<xstring> getValues() const {return m_values;}
        virtual Iterator<std::string> getSingleByteValues() const;
        virtual DOMNode* toDOM(DOMDocument* doc=NULL, bool xmlns=true) const;

        static void regFactory(const XMLCh* attrName, const XMLCh* attrNamespace,
                               SAMLAttributeFactory* factory);
        static void unregFactory(const XMLCh* attrName, const XMLCh* attrNamespace);
        static SAMLAttribute* getInstance(DOMElement* e);
        static SAMLAttribute* getInstance(std::istream& in);

    protected:
        virtual void fromDOM(DOMElement* e);
        virtual bool accept(DOMElement* e) const {return true;}
        virtual bool addValue(DOMElement* e);

        const XMLCh* m_name;
        const XMLCh* m_namespace;
        QName* m_type;
        long m_lifetime;
        mutable std::vector<xstring> m_values;
        mutable std::vector<std::string> m_sbValues;

    private:
        SAMLAttribute(const SAMLAttribute&) {}
        SAMLAttribute& operator=(const SAMLAttribute&) {return *this;}
        typedef std::map<xstring,SAMLAttributeFactory*> SAMLAttributeFactoryMap;
        static SAMLAttributeFactoryMap m_map;
    };

    class SAML_EXPORTS SAMLAuthorityBinding : public SAMLObject
    {
    public:
        SAMLAuthorityBinding(const saml::QName& kind, const XMLCh* binding, const XMLCh* location);
        SAMLAuthorityBinding(DOMElement* e);
        SAMLAuthorityBinding(std::istream& in);
        virtual ~SAMLAuthorityBinding();
        virtual SAMLObject* clone() const;

        const saml::QName* getAuthorityKind() const {return m_kind;}
        const XMLCh* getBinding() const {return m_binding;}
        const XMLCh* getLocation() const {return m_location;}

        virtual DOMNode* toDOM(DOMDocument* doc=NULL, bool xmlns=true) const;

    protected:
        virtual void fromDOM(DOMElement* e);

        saml::QName* m_kind;
        const XMLCh* m_binding;
        const XMLCh* m_location;

    private:
        SAMLAuthorityBinding(const SAMLAuthorityBinding&) {}
        SAMLAuthorityBinding& operator=(const SAMLAuthorityBinding&) {return *this;}
    };

    class SAML_EXPORTS SAMLQuery;
    extern "C" { typedef SAMLQuery* SAMLQueryFactory(DOMElement*); }

    class SAML_EXPORTS SAMLQuery : public SAMLObject
    {
    public:
        virtual ~SAMLQuery() {}

        static void regFactory(saml::QName& type, SAMLQueryFactory* factory) {m_map.insert(SAMLQueryFactoryMap::value_type(type,factory));}
        static void unregFactory(saml::QName& type) {m_map.erase(type);}
        static SAMLQuery* getInstance(DOMElement* e);
        static SAMLQuery* getInstance(std::istream& in);

    protected:
        SAMLQuery() { RTTI(SAMLQuery); }
        SAMLQuery(std::istream& in) : SAMLObject(in) { RTTI(SAMLQuery); }

    private:
        SAMLQuery(const SAMLQuery&) {}
        SAMLQuery& operator=(const SAMLQuery&) {return *this;}

        typedef std::map<saml::QName,SAMLQueryFactory*> SAMLQueryFactoryMap;
        static SAMLQueryFactoryMap m_map;
    };

    class SAML_EXPORTS SAMLAttributeQuery : public SAMLQuery
    {
    public:
        SAMLAttributeQuery(SAMLSubject* subject, const XMLCh* resource=NULL,
                           const Iterator<SAMLAttribute*>& designators=Iterator<SAMLAttribute*>());
        SAMLAttributeQuery(DOMElement* e);
        SAMLAttributeQuery(std::istream& in);
        virtual ~SAMLAttributeQuery();
        virtual SAMLObject* clone() const;

        virtual DOMNode* toDOM(DOMDocument* doc=NULL, bool xmlns=true) const;

        const SAMLSubject* getSubject() const {return m_subject;}
        const XMLCh* getResource() const {return m_resource;}
        Iterator<SAMLAttribute*> getDesignators() const {return m_designators;}

    protected:
        virtual void fromDOM(DOMElement* e);

        SAMLSubject* m_subject;
        const XMLCh* m_resource;
        std::vector<SAMLAttribute*> m_designators;

    private:
        SAMLAttributeQuery(const SAMLAttributeQuery&) {}
        SAMLAttributeQuery& operator=(const SAMLAttributeQuery&) {return *this;}
    };

    class SAML_EXPORTS SAMLRequest : public SAMLSignedObject
    {
    public:
        SAMLRequest(const Iterator<saml::QName>& respondWiths=Iterator<saml::QName>(), SAMLQuery* query=NULL,
                    const Iterator<const XMLCh*>& assertionIdRefs=Iterator<const XMLCh*>(),
                    const Iterator<const XMLCh*>& artifacts=Iterator<const XMLCh*>());
        SAMLRequest(DOMElement* e);
        SAMLRequest(std::istream& in);
        virtual ~SAMLRequest();
        virtual SAMLObject* clone() const;

        virtual DOMNode* toDOM(DOMDocument* doc=NULL, bool xmlns=true) const;

        const XMLCh* getId() const {return m_requestId;}
        const XMLDateTime* getIssueInstant() const {return m_issueInstant;}
        Iterator<saml::QName> getRespondWiths() const {return m_respondWiths;}
        const SAMLQuery* getQuery() const {return m_query;}
        Iterator<const XMLCh*> getAssertionIdRefs() const {return m_assertionIdRefs;}
        Iterator<const XMLCh*> getArtifacts() const {return m_artifacts;}

    protected:
        virtual void fromDOM(DOMElement* e);
        virtual void insertSignature();

        const XMLCh* m_requestId;
        XMLDateTime* m_issueInstant;
        std::vector<saml::QName> m_respondWiths;
        SAMLQuery* m_query;
        std::vector<const XMLCh*> m_assertionIdRefs;
        std::vector<const XMLCh*> m_artifacts;

    private:
        SAMLRequest(const SAMLRequest&) {}
        SAMLRequest& operator=(const SAMLRequest&) {return *this;}
    };

    class SAML_EXPORTS SAMLStatement;
    extern "C" { typedef SAMLStatement* SAMLStatementFactory(DOMElement*); }

    class SAML_EXPORTS SAMLStatement : public SAMLObject
    {
    public:
        virtual ~SAMLStatement() {}

        static void regFactory(saml::QName& type, SAMLStatementFactory* factory) {m_map.insert(SAMLStatementFactoryMap::value_type(type,factory));}
        static void unregFactory(saml::QName& type) {m_map.erase(type);}
        static SAMLStatement* getInstance(DOMElement* e);
        static SAMLStatement* getInstance(std::istream& in);

    protected:
        SAMLStatement() { RTTI(SAMLStatement); }
        SAMLStatement(std::istream& in) : SAMLObject(in) { RTTI(SAMLStatement); }

    private:
        SAMLStatement(const SAMLStatement&) {}
        SAMLStatement& operator=(const SAMLStatement&) {return *this;}

        typedef std::map<saml::QName,SAMLStatementFactory*> SAMLStatementFactoryMap;
        static SAMLStatementFactoryMap m_map;
    };

    class SAML_EXPORTS SAMLAttributeStatement : public SAMLStatement
    {
    public:
        SAMLAttributeStatement(SAMLSubject* subject, const Iterator<SAMLAttribute*>& designators=Iterator<SAMLAttribute*>());
        SAMLAttributeStatement(DOMElement* e);
        SAMLAttributeStatement(std::istream& in);
        virtual ~SAMLAttributeStatement();
        virtual SAMLObject* clone() const;

        virtual DOMNode* toDOM(DOMDocument* doc=NULL, bool xmlns=true) const;

        const SAMLSubject* getSubject() const {return m_subject;}
        Iterator<SAMLAttribute*> getAttributes() const {return m_attributes;}

    protected:
        virtual void fromDOM(DOMElement* e);
        SAMLSubject* m_subject;
        std::vector<SAMLAttribute*> m_attributes;

    private:
        SAMLAttributeStatement(const SAMLAttributeStatement&) {}
        SAMLAttributeStatement& operator=(const SAMLAttributeStatement&) {return *this;}
    };

    class SAML_EXPORTS SAMLAuthenticationStatement : public SAMLStatement
    {
    public:
        SAMLAuthenticationStatement(SAMLSubject* subject,
                                    const XMLCh* authMethod, const XMLDateTime& authInstant,
                                    const XMLCh* subjectIP=NULL, const XMLCh* subjectDNS=NULL,
                                    const Iterator<SAMLAuthorityBinding*>& bindings=Iterator<SAMLAuthorityBinding*>());
        SAMLAuthenticationStatement(DOMElement* e);
        SAMLAuthenticationStatement(std::istream& in);
        virtual ~SAMLAuthenticationStatement();
        virtual SAMLObject* clone() const;

        virtual DOMNode* toDOM(DOMDocument* doc=NULL, bool xmlns=true) const;

        const SAMLSubject* getSubject() const {return m_subject;}
        const XMLCh* getSubjectIP() const {return m_subjectIP;}
        const XMLCh* getSubjectDNS() const {return m_subjectDNS;}
        const XMLCh* getAuthMethod() const {return m_authMethod;}
        const XMLDateTime* getAuthInstant() const {return m_authInstant;}
        Iterator<SAMLAuthorityBinding*> getBindings() const {return m_bindings;}

    protected:
        virtual void fromDOM(DOMElement* e);

        SAMLSubject* m_subject;
        const XMLCh* m_subjectIP;
        const XMLCh* m_subjectDNS;
        const XMLCh* m_authMethod;
        XMLDateTime* m_authInstant;
        std::vector<SAMLAuthorityBinding*> m_bindings;

    private:
        SAMLAuthenticationStatement(const SAMLAuthenticationStatement&) {}
        SAMLAuthenticationStatement& operator=(const SAMLAuthenticationStatement&) {return *this;}
    };

    class SAML_EXPORTS SAMLCondition;
    extern "C" { typedef SAMLCondition* SAMLConditionFactory(DOMElement*); }

    class SAML_EXPORTS SAMLCondition : public SAMLObject
    {
    public:
        virtual ~SAMLCondition() {}

        static void regFactory(saml::QName& type, SAMLConditionFactory* factory) {m_map.insert(SAMLConditionFactoryMap::value_type(type,factory));}
        static void unregFactory(saml::QName& type) {m_map.erase(type);}
        static SAMLCondition* getInstance(DOMElement* e);
        static SAMLCondition* getInstance(std::istream& in);

    protected:
        SAMLCondition() { RTTI(SAMLCondition); }
        SAMLCondition(std::istream& in) : SAMLObject(in) { RTTI(SAMLCondition); }

    private:
        SAMLCondition(const SAMLCondition&) {}
        SAMLCondition& operator=(const SAMLCondition&) {return *this;}

        typedef std::map<saml::QName,SAMLConditionFactory*> SAMLConditionFactoryMap;
        static SAMLConditionFactoryMap m_map;
    };

    class SAML_EXPORTS SAMLAudienceRestrictionCondition : public SAMLCondition
    {
    public:
        SAMLAudienceRestrictionCondition(const Iterator<const XMLCh*>& audiences);
        SAMLAudienceRestrictionCondition(DOMElement* e);
        SAMLAudienceRestrictionCondition(std::istream& in);
        virtual ~SAMLAudienceRestrictionCondition();
        virtual SAMLObject* clone() const;

        saml::Iterator<const XMLCh*> getAudiences() {return m_audiences;}

        virtual DOMNode* toDOM(DOMDocument* doc=NULL, bool xmlns=true) const;

        bool eval(const Iterator<const XMLCh*>& audiences) const;
        bool eval(const Iterator<xstring>& audiences) const;

    protected:
        virtual void fromDOM(DOMElement* e);

        std::vector<const XMLCh*> m_audiences;

    private:
        SAMLAudienceRestrictionCondition(const SAMLAudienceRestrictionCondition&) {}
        SAMLAudienceRestrictionCondition& operator=(const SAMLAudienceRestrictionCondition&) {return *this;}
    };

    class SAML_EXPORTS SAMLAssertion : public SAMLSignedObject
    {
    public:
        SAMLAssertion(const XMLCh* issuer, const XMLDateTime* notBefore=NULL, const XMLDateTime* notOnOrAfter=NULL,
                      const Iterator<SAMLCondition*>& conditions=Iterator<SAMLCondition*>(),
                      const Iterator<SAMLStatement*>& statements=Iterator<SAMLStatement*>());
        SAMLAssertion(DOMElement* e);
        SAMLAssertion(std::istream& in);
        virtual ~SAMLAssertion();
        virtual SAMLObject* clone() const;

        virtual DOMNode* toDOM(DOMDocument* doc=NULL, bool xmlns=true) const;

        const XMLCh* getId() const {return m_assertionId;}
        const XMLCh* getIssuer() const {return m_issuer;}
        const XMLDateTime* getNotBefore() const {return m_notBefore;}
        const XMLDateTime* getNotOnOrAfter() const {return m_notOnOrAfter;}
        Iterator<SAMLCondition*> getConditions() const {return m_conditions;}
        Iterator<SAMLStatement*> getStatements() const {return m_statements;}

    protected:
        virtual void fromDOM(DOMElement* e);
        virtual void insertSignature();

        const XMLCh* m_assertionId;
        const XMLCh* m_issuer;
        XMLDateTime* m_issueInstant;
        XMLDateTime* m_notBefore;
        XMLDateTime* m_notOnOrAfter;
        std::vector<SAMLCondition*> m_conditions;
        std::vector<SAMLStatement*> m_statements;

    private:
        SAMLAssertion(const SAMLAssertion&) {}
        SAMLAssertion& operator=(const SAMLAssertion&) {return *this;}
    };

    class SAML_EXPORTS SAMLResponse : public SAMLSignedObject
    {
    public:
        SAMLResponse(const XMLCh* inResponseTo=NULL, const XMLCh* recipient=NULL,
                     const Iterator<SAMLAssertion*>& assertions=Iterator<SAMLAssertion*>(), SAMLException* e=NULL);
        SAMLResponse(DOMElement* e);
        SAMLResponse(std::istream& in);
        virtual ~SAMLResponse();
        virtual SAMLObject* clone() const;

        virtual DOMNode* toDOM(DOMDocument* doc=NULL, bool xmlns=true) const;

        const XMLCh* getId() const {return m_responseId;}
        const XMLCh* getInResponseTo() const {return m_inResponseTo;}
        const XMLDateTime* getIssueInstant() const {return m_issueInstant;}
        const XMLCh* getRecipient() const {return m_recipient;}
        Iterator<SAMLAssertion*> getAssertions() const {return m_assertions;}

    protected:
        virtual void fromDOM(DOMElement* e);
        virtual void insertSignature();

        const XMLCh* m_responseId;
        const XMLCh* m_inResponseTo;
        XMLDateTime* m_issueInstant;
        const XMLCh* m_recipient;
        std::vector<SAMLAssertion*> m_assertions;
        SAMLException* m_exception;

    private:
        SAMLResponse(const SAMLResponse&) {}
        SAMLResponse& operator=(const SAMLResponse&) {return *this;}
    };

    // The final classes support SAML binding and profile implementations that
    // use the SAML objects to do useful things.

    DECLARE_SAML_EXCEPTION(ProfileException,SAMLException);
    DECLARE_SAML_EXCEPTION(FatalProfileException,ProfileException);
    DECLARE_SAML_EXCEPTION(RetryableProfileException,ProfileException);
    DECLARE_SAML_EXCEPTION(ExpiredAssertionException,FatalProfileException);
    DECLARE_SAML_EXCEPTION(InvalidAssertionException,FatalProfileException);
    DECLARE_SAML_EXCEPTION(ReplayedAssertionException,RetryableProfileException);

    class SAML_EXPORTS SAMLPOSTProfile
    {
    public:
        static SAMLResponse* prepare(
            const XMLCh* recipient,
            const XMLCh* issuer,
            const Iterator<const XMLCh*>& audiences,
            const XMLCh* name,
            const XMLCh* nameQualifier,
            const XMLCh* format,
            const XMLCh* subjectIP,
            const XMLCh* authMethod,
            const XMLDateTime& authInstant,
            const Iterator<SAMLAuthorityBinding*>& bindings
            );
        static SAMLResponse* accept(const XMLByte* buf, const XMLCh* receiver, int ttlSeconds, bool process=true);
        static void process(SAMLResponse& r, const XMLCh* receiver, int ttlSeconds);
        static const SAMLAssertion* getSSOAssertion(const SAMLResponse& r, const Iterator<const XMLCh*>& audiences);
        static const SAMLAuthenticationStatement* getSSOStatement(const SAMLAssertion& a);
        static bool checkReplayCache(const SAMLAssertion& a);

    private:
        static std::set<xstring> m_replayCache;
        static std::multimap<xstring,xstring> m_replayExpMap;
    };

    class SAML_EXPORTS SAMLBinding
    {
    public:
        virtual ~SAMLBinding() {}

        virtual SAMLResponse* send(
            const SAMLAuthorityBinding& bindingInfo, SAMLRequest& req,
            const SAMLConfig::SAMLBindingConfig& conf=SAMLConfig::getConfig().binding_defaults
            )=0;
        virtual SAMLRequest* recieve(void* reqContext, const XMLCh** requester)=0;
        virtual void respond(void* respContext, SAMLResponse* response, SAMLException* e)=0;

        static const XMLCh SAML_SOAP_HTTPS[];
    };

    class SAML_EXPORTS SAMLSOAPBinding : public SAMLBinding
    {
    public:
        SAMLSOAPBinding();
        virtual ~SAMLSOAPBinding();
    
        virtual SAMLResponse* send(
            const SAMLAuthorityBinding& bindingInfo, SAMLRequest& req,
            const SAMLConfig::SAMLBindingConfig& conf=SAMLConfig::getConfig().binding_defaults
            );
        virtual SAMLRequest* recieve(void* reqContext, const XMLCh** requester);
        virtual void respond(void* respContext, SAMLResponse* response, SAMLException* e);
    };
}

#endif
