/**
 * Licensed to the Apache Software Foundation (ASF) under one
 * or more contributor license agreements. See the NOTICE file
 * distributed with this work for additional information
 * regarding copyright ownership. The ASF licenses this file
 * to you under the Apache License, Version 2.0 (the
 * "License"); you may not use this file except in compliance
 * with the License. You may obtain a copy of the License at
 *
 * http://www.apache.org/licenses/LICENSE-2.0
 *
 * 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.
 */

/*
 * XSEC
 *
 * XKMSAuthenticationImpl := Implementation of Authentication elements
 *
 * $Id: XKMSAuthenticationImpl.cpp 1833340 2018-06-11 15:40:13Z scantor $
 *
 */

#include <xsec/dsig/DSIGReference.hpp>
#include <xsec/framework/XSECDefs.hpp>
#include <xsec/framework/XSECEnv.hpp>
#include <xsec/framework/XSECError.hpp>

#ifdef XSEC_XKMS_ENABLED

#include "../../utils/XSECDOMUtils.hpp"

#include "XKMSAuthenticationImpl.hpp"
#include "XKMSNotBoundAuthenticationImpl.hpp"

#include <xsec/xkms/XKMSConstants.hpp>

#include <xercesc/dom/DOM.hpp>
#include <xercesc/util/XMLUniDefs.hpp>

XERCES_CPP_NAMESPACE_USE

// --------------------------------------------------------------------------------
//           Constructor/Destructor
// --------------------------------------------------------------------------------

XKMSAuthenticationImpl::XKMSAuthenticationImpl(const XSECEnv * env) :
mp_env(env),
mp_authenticationElement(NULL),
mp_keyBindingAuthenticationSignatureElement(NULL),
mp_keyBindingAuthenticationSignature(NULL),
mp_notBoundAuthentication(NULL)
{
}

XKMSAuthenticationImpl::XKMSAuthenticationImpl(const XSECEnv * env, DOMElement * node) :
mp_env(env),
mp_authenticationElement(node),
mp_keyBindingAuthenticationSignatureElement(NULL),
mp_keyBindingAuthenticationSignature(NULL),
mp_notBoundAuthentication(NULL)
{
}

XKMSAuthenticationImpl::~XKMSAuthenticationImpl() {

	if (mp_notBoundAuthentication != NULL)
		delete mp_notBoundAuthentication;
}

// --------------------------------------------------------------------------------
//           Load
// --------------------------------------------------------------------------------

void XKMSAuthenticationImpl::load(const XMLCh * id) {

	if (mp_authenticationElement == NULL) {
		throw XSECException(XSECException::ExpectedXKMSChildNotFound,
			"XKMSAuthenticationImpl::load - called on empty DOM");
	}

	// Store for later use
	mp_keyBindingId = id;

	DOMElement * tmpElt = findFirstElementChild(mp_authenticationElement);

	if (tmpElt != NULL && strEquals(getXKMSLocalName(tmpElt), 
									XKMSConstants::s_tagKeyBindingAuthentication)) {

		// Find the signature
		mp_keyBindingAuthenticationSignatureElement = 
			(DOMElement *) findFirstElementChild(tmpElt);

		while (mp_keyBindingAuthenticationSignatureElement != NULL && 
			!strEquals(getDSIGLocalName(mp_keyBindingAuthenticationSignatureElement), 
						XKMSConstants::s_tagSignature)) {

			mp_keyBindingAuthenticationSignatureElement= 
				findNextElementChild(mp_keyBindingAuthenticationSignatureElement);

		}

		// The provider will take care of cleaning this up later.

		if (mp_keyBindingAuthenticationSignatureElement != NULL) {

			mp_keyBindingAuthenticationSignature = m_prov.newSignatureFromDOM(
													  mp_keyBindingAuthenticationSignatureElement->getOwnerDocument(), 
													  mp_keyBindingAuthenticationSignatureElement);
			mp_keyBindingAuthenticationSignature->load();

			// Check the signature is across the correct input
			
			DSIGReferenceList * rl = 
				mp_keyBindingAuthenticationSignature->getReferenceList();

			if (rl->getSize() != 1) {
				throw XSECException(XSECException::XKMSError,
					"XKMSAuthenticationImpl::load - KeyBindingAuthentication Signature with incorrect number of references found (should be 1)");
			}

			safeBuffer sb;
			sb.sbXMLChIn(DSIGConstants::s_unicodeStrEmpty);
			sb.sbXMLChAppendCh(chPound);
			sb.sbXMLChCat(mp_keyBindingId);

			if (!strEquals(rl->item(0)->getURI(), sb.rawXMLChBuffer())) {
				throw XSECException(XSECException::XKMSError,
					"XKMSAuthenticationImpl::load - KeyBindingAuthentication Signature refers to incorrect Id (should be for KeyBinding)");
			}

			// We don't actually check the signature as we have no key material to do so!
		}

		tmpElt = findNextElementChild(tmpElt);

	}

	if (tmpElt != NULL && strEquals(getXKMSLocalName(tmpElt), 
									XKMSConstants::s_tagNotBoundAuthentication)) {

		XSECnew(mp_notBoundAuthentication, XKMSNotBoundAuthenticationImpl(mp_env, tmpElt));
		mp_notBoundAuthentication->load();

	}

}

// --------------------------------------------------------------------------------
//           Create
// --------------------------------------------------------------------------------

DOMElement * XKMSAuthenticationImpl::createBlankAuthentication(const XMLCh * id) {

	// Get some setup values
	safeBuffer str;
	DOMDocument *doc = mp_env->getParentDocument();
	const XMLCh * prefix = mp_env->getXKMSNSPrefix();

	makeQName(str, prefix, XKMSConstants::s_tagAuthentication);

	mp_authenticationElement = doc->createElementNS(XKMSConstants::s_unicodeStrURIXKMS, 
												str.rawXMLChBuffer());

	mp_env->doPrettyPrint(mp_authenticationElement);

	// This isn't used immediately - it is simply held for when we create a signature
	mp_keyBindingId = id;

	return mp_authenticationElement;

}


// --------------------------------------------------------------------------------
//           Interfacet - Get
// --------------------------------------------------------------------------------

DSIGSignature * XKMSAuthenticationImpl::getKeyBindingAuthenticationSignature(void) const {

	return mp_keyBindingAuthenticationSignature;

}

XKMSNotBoundAuthentication * XKMSAuthenticationImpl::getNotBoundAuthentication(void) const {

	return mp_notBoundAuthentication;

}

// --------------------------------------------------------------------------------
//           Interface - Set
// --------------------------------------------------------------------------------

DSIGSignature * XKMSAuthenticationImpl::addKeyBindingAuthenticationSignature(
                const XMLCh* c14nAlgorithm,
		const XMLCh* signatureAlgorithm,	
		const XMLCh* hashAlgorithm) {

	if (mp_keyBindingId == NULL) {
		throw XSECException(XSECException::XKMSError,
			"XKMSAuthenticationImpl::addKeyBindingAuthenticationSignature - called prior to key infos being added");
	}

	DSIGSignature * ret = m_prov.newSignature();
	DOMElement * elt = ret->createBlankSignature(mp_env->getParentDocument(), c14nAlgorithm, signatureAlgorithm);

	/* Create the enveloping reference */
	safeBuffer sb;
	sb.sbXMLChIn(DSIGConstants::s_unicodeStrEmpty);
	sb.sbXMLChAppendCh(chPound);
	sb.sbXMLChCat(mp_keyBindingId);

	DSIGReference *ref = ret->createReference(sb.rawXMLChBuffer(), hashAlgorithm);
	ref->appendCanonicalizationTransform(DSIGConstants::s_unicodeStrURIEXC_C14N_COM);

	/* Embed the signature in the document inside a KeyBindingAuthentication element */
	safeBuffer str;
	DOMDocument *doc = mp_env->getParentDocument();
	const XMLCh * prefix = mp_env->getXKMSNSPrefix();

	makeQName(str, prefix, XKMSConstants::s_tagKeyBindingAuthentication);

	DOMElement * t = doc->createElementNS(XKMSConstants::s_unicodeStrURIXKMS, 
												str.rawXMLChBuffer());

	mp_env->doPrettyPrint(t);
	t->appendChild(elt);
	mp_env->doPrettyPrint(t);

	mp_authenticationElement->appendChild(t);
	mp_env->doPrettyPrint(mp_authenticationElement);

	return ret;
}

void XKMSAuthenticationImpl::setNotBoundAuthentication(
		const XMLCh * uri, 
		const XMLCh * value) {

	XSECnew(mp_notBoundAuthentication, XKMSNotBoundAuthenticationImpl(mp_env));
	mp_authenticationElement->appendChild(mp_notBoundAuthentication->createBlankNotBoundAuthentication(uri, value));
	mp_env->doPrettyPrint(mp_authenticationElement);

}

#endif /* XSEC_XKMS_ENABLED */
