/**
 *  ---------
 * |.##> <##.|  Open Smart Card Development Platform (www.openscdp.org)
 * |#       #|
 * |#       #|  Copyright (c) 1999-2010 CardContact Software & System Consulting
 * |'##> <##'|  Andreas Schwier, 32429 Minden, Germany (www.cardcontact.de)
 *  ---------
 *
 *  This file is part of OpenSCDP.
 *
 *  OpenSCDP is free software; you can redistribute it and/or modify
 *  it under the terms of the GNU General Public License version 2 as
 *  published by the Free Software Foundation.
 *
 *  OpenSCDP is distributed in the hope that it will be useful,
 *  but WITHOUT ANY WARRANTY; without even the implied warranty of
 *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 *  GNU General Public License for more details.
 *
 *  You should have received a copy of the GNU General Public License
 *  along with OpenSCDP; if not, write to the Free Software
 *  Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
 *
 * @fileoverview Wizard for public key authentication
 */

var CVC			= require('scsh/eac/CVC').CVC;
var SmartCardHSM	= require('scsh/sc-hsm/SmartCardHSM').SmartCardHSM;
var ManagePKA		= require('scsh/sc-hsm/ManagePKA').ManagePKA;
var HSMKeyStore		= require('scsh/sc-hsm/HSMKeyStore').HSMKeyStore;

var CardAction		= require('scsh/srv-cc1/CardAction').CardAction;



/**
 * Wizard to perform a public key authentication with a SmartCard-HSM
 *
 */
function PKAWizard(ui, hsmstate, redirectURL) {
	CardAction.call(this, ui);
	this.hsmstate = hsmstate;
	this.redirectURL = redirectURL
}

PKAWizard.prototype = new CardAction();
PKAWizard.constructor = PKAWizard;

exports.PKAWizard = PKAWizard;



PKAWizard.prototype.getPage = function(req, res, url) {
	GPSystem.log(GPSystem.DEBUG, module.id, "PKAWizard state: " + this.state);
	switch(this.state) {
		case "new":
			var page = this.getClientActivationPage(req, res, url);
			this.state = "enumerateKeys";
			break;
		case "enumerateKeys":
			// no client found
			var page = this.getAuthenticationTokenPage(req, res, url);
			this.state = "new";
			break;
		case "enterData":
			if (req.method == "POST") {
				if (req.parameters.key) {
					this.key = req.parameters.key;
					this.state = "startPKA";
				} else {
					this.state = "new";
				}
			} else {
				var page = this.getKeySelectionPage(req, res, url);
			}
			break;
		case "reconnect":
			this.hsmstate = this.ui.service.hsmService.getHSMState(this.hsmstate.path);
			// continue with pka
		case "startPKA":
			var page = this.getClientActivationPageWithPIN(req, res, url);
			this.state = "performPKA";
			break;
		case "performPKA":
			var page = this.getErrorPage(req, res, url);
			this.state = "startPKA";
			break;
		case "targetOffline":
			var page = this.getTokenOfflinePage(req, res, url);
			this.state = "reconnect";
			break;
		case CardAction.SUCCESS:
			this.ui.redirectTo(req, res, req.url, this.redirectURL);
			return;
		default:
			var page = CardAction.prototype.getPage.call(this, req, res, this.redirectURL);
	}
	return page;
}



/**
 * Serve the error page if no local client was found
 *
 * @param {HttpRequest} req the request object
 * @param {HttpResponse} req the response object
 */
PKAWizard.prototype.getAuthenticationTokenPage = function(req, res) {
	default xml namespace = "http://www.w3.org/1999/xhtml";

	var page =
		<div>
			<h1>Public Key Authentication Wizard</h1>
			<p>This wizard performs public key authentication on token {this.hsmstate.path}.</p>
			<p>Please connect the authentication token with your local client and press <a href="">refresh</a> to proceed.</p>
			<p>
			If you have problems connecting your token see the <a href="https://www.openscdp.org/ocf/faq.html">FAQ</a> for more details
			   and troubleshooting hints.</p>
		</div>

	return page;
}



PKAWizard.prototype.getClientActivationPage = function(req, res, url) {
	default xml namespace = "http://www.w3.org/1999/xhtml";

	var page =
		<div>
			<h1>Public Key Authentication Wizard</h1>
			<p>Enumerate keys... { this.getActivationLink(req, res, -1) }</p>
			<p>Please wait for the green tick to appear. The page will be reloaded automatically if JavaScript is enabled.</p>
			<p>If JavaScript is disabled, then press <a href="">continue</a> after the green tick is shown.</p>
		</div>

	return page;
}



PKAWizard.prototype.getKeySelectionPage = function(req, res, url) {
	default xml namespace = "http://www.w3.org/1999/xhtml";

	var page =
		<div>
			<div id="error"/>
			<h1>Public Key Authentication Wizard</h1>

			<p>
			<form class="pure-form" action="" method="post" enctype="multipart/form-data">
				<p>Token: {this.hsmstate.path}</p>
				<label>Authentication Token: {this.path}</label>
				<input name="action" type="hidden" value="enumerate"/>
				<button class="pure-button" type="submit">Enumerate Keys</button>
			</form>
			</p>

			<form class="pure-form" action="" method="post" enctype="multipart/form-data">
				<p>Select the key for authentication:</p>
				<select name="key"/>
				<button class="pure-button" type="submit">Authenticate</button>
			</form>
		</div>

	var l = page.form.select;
	for each (var alias in this.keys) {
		l[0].appendChild(<option>{alias}</option>);
	}

	return page;
}



PKAWizard.prototype.getClientActivationPageWithPIN = function(req, res, url) {
	default xml namespace = "http://www.w3.org/1999/xhtml";

	var page =
		<div>
			<h1>Public Key Authentication Wizard</h1>
			<p>Start authentication... { this.getActivationLink(req, res, 1) }</p>
			<p>This operation requires PIN verification. Please watch the PIN dialog on your screen or on your card readers PIN pad.</p>
			<p>Please wait for the green tick to appear. The page will be reloaded automatically if JavaScript is enabled.</p>
			<p>If JavaScript is disabled, then press <a href="">continue</a> after the green tick is shown.</p>
		</div>

	return page;
}



PKAWizard.prototype.getTokenOfflinePage = function(req, res, url) {
	default xml namespace = "http://www.w3.org/1999/xhtml";

	var page =
		<div>
			<h1>Public Key Authentication Wizard</h1>
			<p>The Authentication failed because the token {this.hsmstate.path} is not online anymore. To proceed reconnect the token and press <a href="">refresh</a>.</p>
		</div>
	return page;
}



PKAWizard.prototype.handleCardAction = function(card, reqInfo) {
	var sc = new SmartCardHSM(card);

	var devAutCert = sc.readBinary(SmartCardHSM.C_DevAut);
	var crypto = new Crypto();
	var chain = SmartCardHSM.validateCertificateChain(crypto, devAutCert);
	if (chain == null) {
		this.lastMessage = "Could not authenticate SmartCard-HSM";
		return;
	}

	sc.openSecureChannel(crypto, chain.publicKey);

	switch(this.state) {
		case "enumerateKeys":
			this.path = chain.path;
			this.keys = sc.enumerateKeys();
			if (this.keys.length > 0) {
				this.state = "enterData";
			} else {
				this.reportError("No keys found on card");
			}
			break;
		case "performPKA":
			var ks = new HSMKeyStore(sc);

			var keys = ks.enumerateKeys()

			var key = ks.getKey(this.key);
			var cert = ks.getCertificate(this.key);
			var cvc = new CVC(cert);

			var pka = this.hsmstate.pka;
			if (!pka) {
				this.reportError("Target device no longer online");
				this.state = "targetOffline";
				return;
			}

			var status = pka.checkKeyStatus(cvc.getCHR());

			if (status == ManagePKA.KEY_NOT_FOUND) {
				this.reportError("Key not registered as public key for authentication");
				return;
			}

			if (status == ManagePKA.KEY_ALREADY_AUTHENTICATED) {
				this.reportError("Key already authenticated");
				return;
			}

			pka.performAuthentication(sc, key);

			this.result = "Public key authentication successful";
			this.state = "success";
			break;
		default:
			GPSystem.log(GPSystem.ERROR, module.id, "Unexpected handleCardAction in state " + this.state);
	}
}
