/*
 * 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.
 */


/* SAMLObject.cpp - base class for all SAML constructs

   Scott Cantor
   2/21/02

   $History:$
*/

#include <xercesc/util/Base64.hpp>
#include <xercesc/util/XMLUTF8Transcoder.hpp>
#include <xercesc/framework/MemBufFormatTarget.hpp>
//#include <xsec/utils/XSECNameSpaceExpander.hpp>
//#include <xsec/canon/XSECC14n20010315.hpp>

#include "internal.h"
using namespace saml;

#include <sstream>
using namespace std;

#ifdef NO_RTTI
SAMLObject* SAMLObject::_dynaptr(unsigned short type) const
{
    for (vector<unsigned short>::const_iterator i=m_types.begin(); i!=m_types.end(); i++)
    {
        if (*i==type)
            return const_cast<SAMLObject*>(this);
    }
    return NULL;
}
#endif

SAMLObject::SAMLObject() : m_root(NULL), m_document(NULL), m_bOwnStrings(true), m_log(NULL)
{
    RTTI(SAMLObject);
    m_log=&log4cpp::Category::getInstance(string(SAML_LOGCAT) + '.' + typeid(this).name());
}

SAMLObject::SAMLObject(istream& in) : m_root(NULL), m_document(NULL), m_bOwnStrings(true), m_log(NULL)
{
    RTTI(SAMLObject);
    m_log=&log4cpp::Category::getInstance(string(SAML_LOGCAT) + '.' + typeid(this).name());
    try
    {
        XML::Parser p;
        XML::StreamInputSource src(in);
        Wrapper4InputSource dsrc(&src,false);
        m_document=p.parse(dsrc);
    }
    catch (XMLException& e)
    {
        auto_ptr<char> buf(XMLString::transcode(e.getMessage()));
        SAML_log.error("caught an XML exception while parsing an istream&:\n"
                     "\tFile: %s\tLine: %u\n\tMessage: %s",
                     e.getSrcFile(),e.getSrcLine(),buf.get());
        throw MalformedException(string("SAMLObject() caught XML exception: ") + buf.get());
    }
}

SAMLObject::~SAMLObject()
{
    if (m_document)
        m_document->release();
}

DOMNode* SAMLObject::toDOM(DOMDocument* doc, bool xmlns) const
{
    // Already built?
    if (m_root)
        return m_root;

    // If no document provided, build a new one for our use.
    if (!doc)
    {
        DOMImplementation* impl=DOMImplementationRegistry::getDOMImplementation(NULL);
        m_document=impl->createDocument();
    }

    // We're a 'null' object, so if we're given a document, do nothing with it.
    return NULL;
}

void SAMLObject::fromDOM(DOMElement* e)
{
    if (!e)
        throw MalformedException(SAMLException::RESPONDER,"SAMLObject::fromDOM() given an empty DOM");
}

DOMNode* SAMLObject::plantRoot()
{
    if (m_root)
    {
        DOMNode* domroot=m_root;
        while (domroot->getParentNode() && domroot->getParentNode()->getNodeType()!=DOMNode::DOCUMENT_NODE)
            domroot=domroot->getParentNode();

        DOMElement* e=m_root->getOwnerDocument()->getDocumentElement();
        if (e && e!=domroot)
            m_root->getOwnerDocument()->replaceChild(domroot,e);
        else if (!e)
            m_root->getOwnerDocument()->appendChild(domroot);
    }
    return m_root;
}

XMLByte* SAMLObject::toBase64(unsigned int* outputLength) const
{
    ostringstream os;
    os << *this;
    string osstr=os.str();
    return Base64::encode(const_cast<XMLByte*>((XMLByte*)osstr.c_str()),osstr.length(),outputLength);
}

namespace {
    static XMLCh UTF8[]={ chLatin_U, chLatin_T, chLatin_F, chDigit_8, chNull };
}

ostream& saml::xmlout(ostream& target, const XMLCh* s)
{
    if (!s)
        return target;
    auto_ptr<char> p(toUTF8(s));
    return target << p.get();
}

ostream& saml::operator<<(ostream& ostr, const DOMNode& node)
{
    static const XMLCh impltype[] = { chLatin_L, chLatin_S, chNull };
    DOMImplementation* impl=DOMImplementationRegistry::getDOMImplementation(impltype);
    DOMWriter* serializer=(static_cast<DOMImplementationLS*>(impl))->createDOMWriter();
    serializer->setEncoding(UTF8);
    try
    {
        MemBufFormatTarget target;
        if (!serializer->writeNode(&target,node))
            throw SAMLException("operator <<: unable to serialize XML instance");
        unsigned int len=target.getLen();
        const XMLByte* byteptr=target.getRawBuffer();
        while (len--)
            ostr << (char)*(byteptr++);
    }
    catch (...)
    {
        serializer->release();
        throw;
    }
    serializer->release();
    return ostr;
}

ostream& saml::operator<<(ostream& ostr, const SAMLObject& obj)
{
    DOMNode* n=obj.toDOM();
    if (n)
        ostr << *n;
    else
        ostr << "{null SAML object}" << endl;
    return ostr;
}

char* saml::toUTF8(const XMLCh* src)
{
    unsigned int eaten;
    unsigned int srclen=XMLString::stringLen(src);
    XMLUTF8Transcoder t(UTF8, srclen*4 + 1);
    char* buf=new char[srclen*4 + 1];
    memset(buf,0,srclen*4 + 1);
    t.transcodeTo(
        src,srclen,
        reinterpret_cast<XMLByte*>(buf),srclen*4,
        eaten,XMLTranscoder::UnRep_RepChar);
    return buf;
}

XMLCh* saml::fromUTF8(const char* src)
{
    unsigned int eaten;
    unsigned int srclen=strlen(src);
    XMLUTF8Transcoder t(UTF8, srclen + 1);
    XMLCh* buf=new XMLCh[srclen + 1];
    auto_ptr<unsigned char> sizes(new unsigned char[srclen]);
    memset(buf,0,(srclen+1)*sizeof(XMLCh));
    t.transcodeFrom(
        reinterpret_cast<const XMLByte*>(src),srclen,
        buf,srclen,
        eaten,sizes.get());
    return buf;
}
