/**
 *  ---------
 * |.##> <##.|  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 Configure services
 */

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

var DAOFactoryDatabase		= require('scsh/pki-db/DAOFactoryDatabase').DAOFactoryDatabase;
var CAService			= require('pki-as-a-service/service/CAService').CAService;
var CRLPublisher		= require('pki-as-a-service/service/CRLPublisher').CRLPublisher;
var PluginRegistry		= require('pki-as-a-service/service/PluginRegistry').PluginRegistry;
var HSMService			= require('pki-as-a-service/service/HSMService').HSMService;
var PingService			= require('pki-as-a-service/service/PingService').PingService;

var AuthorizationManager	= require('pki-as-a-service/service/AuthorizationManager').AuthorizationManager;

var File = require("scsh/file/File").File;
var SmartCardHSM		= require('scsh/sc-hsm/SmartCardHSM').SmartCardHSM;

var I18N			= require('scsh/srv-cc1/I18N').I18N;
/* Enable to help finding text that needs translation
I18N.FallbackToDefaultLang = false;
*/

if (GPSystem.mapFilename("build.version")) {
	load("build.version");
} else {
	VERSION = "snapshot";
}
GPSystem.log(GPSystem.INFO, "paas.SharedScope", "Version is " + VERSION);


var Config = {};
if (GPSystem.mapFilename("etc/configuration.js")) {
	load("etc/configuration.js");
} else {
	GPSystem.log(GPSystem.INFO, "paas.SharedScope", "Not sourcing etc/configuration.js");
}



// --- Default Global settings ---

if (!Config.database) {
	Config.database = {
		type: "H2",
		url: "jdbc:h2:~/h2/paas",
		user: "sa",
		password: ""
	}
}

if (!Config.global) {
	Config.global = {
		// allowResetDB allows recreating all database tables from the GUI using the ?op=resetdb URL
		// This is for testing only and must never be enabled on a production system
		allowResetDB: false,

		// For new user the role subscriber shall be automatically granted.
		// This is for the sandbox system only and must never be enabled on a production system
		grantSubscriberRole: false,

		// Disable the ability for users to self-register a token.
		disableSelfRegistration: false,

		// Reinitialize the first registered HSM in the HSM service during the resetdb process
		// This is for testing only and must never be enabled on a production system
		reinitializeHSMOnResetDB: false,

		// allowChangeConfig allows changing configuration settings using the ?op=changeconfig URL
		// This is for testing only and must never be enabled on a production system
		allowChangeConfig: false,

		// allowDevHSM globally allows the use of SmartCard-HSM issues under the Development Scheme Root CA (UTSRCACC1).
		allowDevHSM: false
	}
}

if (!Config.default) {
	Config.default = {};

	Config.default.blacklist =	[
					//	"/UTSRCACC1/UTCC02/UTCC0200061",
					//	/\/(\w+)\/(\w+)\/(\w+)/					// Block all
					//	/\/UTSRCACC1\/(\w+)\/(\w+)/				// Block all from development root ca
					];
}


// If no services are defined in configuration, then we create a number of example
// service. This setup is also used in the regression test suite-
if (!Config.services) {
	GPSystem.log(GPSystem.INFO, "paas.SharedScope", "Using the default service definitions");

	Config.global.allowResetDB = true;
	Config.global.reinitializeHSMOnResetDB = true,
	Config.global.allowChangeConfig = true;
	Config.global.allowDevHSM = true;
	Config.global.disableSelfRegistration = false;

	Config.services = [
		{	type: "PAAS",
			name: "PKI-as-a-Service",
			description: "The PKI-as-a-Service Portal",
			url: "paas",
			port: 8080,
			smtpConfig: {
				smtpserver: "",
				emailfrom: "do-not-reply@cardcontact.de",
				emailAdmin: "andreas.schwier@cardcontact.de"
			},
			testMode: false	// if TRUE then no activation code will be send but rather a static activation code will be used
							// TRUE also allows SmartCard-HSMs from the development CA
		}
	];



	Config.keystore = {
		withHSMService: {
			localHSMs: [
//				{ readerName: "REINER SCT cyberJack RFID komfort (5968333780) 00 00", protectedPINEntry: true },
//				{ readerName: "Identive SCT3522CC token [CCID Interface] (55521515601800) 00 00", protectedPINEntry: false, pin: "648219" }
			]
		}
	}
}



if (Config.global.serverURL) {
	ApplicationServer.instance.setServerURL(Config.global.serverURL);
}

if (Config.global.imprintURL) {
	ApplicationServer.instance.setImprintURL(Config.global.imprintURL);
}

function handleRequest(req, res) {
	ApplicationServer.instance.handleRequest(req, res);
}

function performCardUpdate(session, pathInfo, queryInfo) {
	ApplicationServer.instance.performCardUpdate(session, pathInfo, queryInfo);
}



function ServiceConfigurationManager(cfg) {
	this.config = cfg;
	this.contextMarker = 0;

	this.daof = new DAOFactoryDatabase(this.config.database.type, this.config.database.url, this.config.database.user, this.config.database.password);
	this.daof.createTables();
}



ServiceConfigurationManager.prototype.loadTrustAnchors = function() {
	var dir = GPSystem.mapFilename("etc/tas");

	if (!dir) {
		return;
	}

	var f = new File(dir);
	var fns = f.list();

	var cvcs = [];

	for each (var fn in fns) {
		if (fn.endsWith(".cvcert")) {
			var f = new File(dir + "/" + fn);
			print(f);
			var bin = f.readAllAsBinary();
			cvcs.push(bin);
		}
	}
	SmartCardHSM.setTrustedSRCACertificates(cvcs);
}



ServiceConfigurationManager.prototype.configureBefore = function() {
	GPSystem.log(GPSystem.INFO, "paas.config", "configureBefore");

	if (this.config.keystore) {
		var cfg = this.config.keystore.withHSMService;
		if (cfg) {
			GPSystem.log(GPSystem.INFO, "paas.config", "Set CryptoProviderFactory");
			this.hsmService = new HSMService(this.daof);
			this.hsmService.authenticationRequired = true;
			this.hsmService.roleRequired = 2;	// See RoleDAO.ID_HSMAdmin

			if (cfg.localHSMs) {
				for (var i = 0; i < cfg.localHSMs.length; i++) {
					c = cfg.localHSMs[i];
					var pin;
					if (c.pin) {
						pin = new ByteString(c.pin, ASCII);
					}
					this.hsmService.addLocalHSM(c.readerName, c.protectedPINEntry, undefined, pin);
				}
			}
		}
	}

	if (this.config.global.allowDevHSM) {
		GPSystem.log(GPSystem.INFO, "paas.config", "UTSRCACC1 enabled");
	} else {
		GPSystem.log(GPSystem.DEBUG, "paas.config", "UTSRCACC1 disabled");
		SmartCardHSM.disableUTSRCA();
	}
	if (this.config.global.enforceEN) {
		I18N.EnforceDefaultLang = true;
	}
}



ServiceConfigurationManager.prototype.configureAfter = function() {
	if (this.config.keystore) {
		var cfg = this.config.keystore.withHSMService;
		if (cfg) {
			ApplicationServer.instance.registerServiceForURL("hsm", { name: "SC-HSM", description: "SmartCard-HSM Status Page", uiFactory: this.hsmService, cardHandler: this.hsmService});
		}
		var crlPublisher = new CRLPublisher(this.daof);
		ApplicationServer.instance.registerServiceForURL("crl", { name: "CRL Publisher", description: "CRL Publisher", restHandler: crlPublisher});
	}
}



ServiceConfigurationManager.prototype.createPAAS = function(cfg) {
	if (!cfg.blacklist) {
		cfg.blacklist= Config.default.blacklist;
	}

	var paas = new CAService(this.hsmService, cfg.name, this.daof);
	if (cfg.testMode) {
		paas.testMode = cfg.testMode;
	}
	if (this.config.global.imprintURL) {
		paas.imprintURL = this.config.global.imprintURL;
	}

	paas.allowResetDB = this.config.global.allowResetDB;
	paas.reinitializeHSMOnResetDB = this.config.global.reinitializeHSMOnResetDB;
	paas.updateDB = this.config.global.updateDB;
	paas.allowChangeConfig = this.config.global.allowChangeConfig;
	paas.grantSubscriberRole = this.config.global.grantSubscriberRole;
	paas.disableBuildinX509 = this.config.global.disableBuildinX509;
	if (typeof(this.config.global.selfRegistrationMode) != "undefined") {
		paas.selfRegistrationMode = this.config.global.selfRegistrationMode;
	} else {
		paas.selfRegistrationMode = this.config.global.disableSelfRegistration ? 0 : 2;
	}

	paas.addBlackList(cfg.blacklist);
	paas.setSMTPConfig(cfg.smtpConfig.smtpserver, cfg.smtpConfig.emailfrom, cfg.smtpConfig.emailAdmin);

	var pr = new PluginRegistry(paas);
	paas.pluginRegistry = pr;

	pr.add("pki-as-a-service/processes/CoreServiceRequests", cfg);
	pr.add("pki-as-a-service/subjects/CoreSubjects", cfg);
	pr.loadPlugins(cfg);

	var authorizationManager = new AuthorizationManager(paas);
	paas.authorizationManager = authorizationManager;
	ApplicationServer.instance.setAuthorizationManager(authorizationManager, false);
	ApplicationServer.instance.enablePaaSStyle();

	var cus = paas.getCardUpdateHandler();

	// Enable token authentication if firmware update is enabled
	if (cfg.updateService && cfg.updateService.tokenKey) {
		var key = new Key();
		key.setComponent(Key.AES, new ByteString(cfg.updateService.tokenKey, HEX));
		cus.setAuthenticationKey(key);
	}
	ApplicationServer.instance.registerServiceForURL(cfg.url, { name: cfg.name, description: cfg.description, uiFactory: paas, cardHandler: cus });

	var ping = new PingService(paas, 8088);
	ApplicationServer.instance.registerServiceForURL("ping", { name: "Ping Service", description: "Ping Service", restHandler: ping}, 8088);
}



ServiceConfigurationManager.prototype.createServices = function() {

	for (var i = 0; i < this.config.services.length; i++) {
		var cfg = this.config.services[i];

		GPSystem.log(GPSystem.INFO, "paas.config", "Creating " + cfg.type + " service '" + cfg.description + "'");
		switch(cfg.type) {
			case "PAAS":
				this.createPAAS(cfg);
				break;
			default:
				throw new GPError("paas.config", GPError.INVALID_DATA, 0, "Invalid service type " + cfg.type);
		}
	}
}



var serviceConfigurationManager = new ServiceConfigurationManager(Config);

serviceConfigurationManager.loadTrustAnchors();
serviceConfigurationManager.configureBefore();
serviceConfigurationManager.createServices();
serviceConfigurationManager.configureAfter();
