/**
 *  ---------
 * |.##> <##.|  SmartCard-HSM Support Scripts
 * |#       #|
 * |#       #|  Copyright (c) 2011-2012 CardContact Software & System Consulting
 * |'##> <##'|  Andreas Schwier, 32429 Minden, Germany (www.cardcontact.de)
 *  ---------
 *
 * Consult your license package for usage terms and conditions.
 *
 * @fileoverview Top-level UI Interface
 */

var CommonUI			= require('scsh/srv-cc1/CommonUI').CommonUI;
var SigningWizard		= require('pki-as-a-service/ui/SigningWizard').SigningWizard;
var RegistrationWizard		= require('pki-as-a-service/ui/RegistrationWizard').RegistrationWizard;
var SmartCardHSM		= require('scsh/sc-hsm/SmartCardHSM').SmartCardHSM;
var ServiceRequestBrowser	= require('pki-as-a-service/ui/ServiceRequestBrowser').ServiceRequestBrowser;
var ServiceRequestController	= require('pki-as-a-service/ui/ServiceRequestController').ServiceRequestController;
var CertificateView		= require('pki-as-a-service/ui/CertificateView').CertificateView;
var SubjectController		= require('pki-as-a-service/ui/SubjectController').SubjectController;
var ServiceRequestOverviewController	= require('pki-as-a-service/ui/ServiceRequestOverviewController').ServiceRequestOverviewController;
var SubjectOverviewController	= require('pki-as-a-service/ui/SubjectOverviewController').SubjectOverviewController;
var TokenListViewController = require('pki-as-a-service/ui/TokenListViewController').TokenListViewController;
var HolderListViewController = require('pki-as-a-service/ui/HolderListViewController').HolderListViewController;
var RoleController		= require('pki-as-a-service/ui/RoleController').RoleController;
var MenuModel			= require('pki-as-a-service/ui/MenuModel').MenuModel;
var PureMenuGenerator		= require('pki-as-a-service/ui/PureMenuGenerator').PureMenuGenerator;
var RoleDAO			= require('scsh/pki-db/db/RoleDAO').RoleDAO;
var I18N			= require('scsh/srv-cc1/I18N').I18N;


I18N.addResources([
	{ lang: "EN", keys: {
		"msg.subject": "Subject",
		"msg.person": "Person",
		"msg.system": "System",
		"msg.holder": "Holder",
		"msg.issuer": "Issuer",
		"msg.token": "Token",
		"msg.keydomain": "Key Domain",
		"msg.profile": "Profile",
		"msg.name": "Name",
		"msg.email": "email",
		"msg.status": "Status",
		"msg.request": "Request",
		"msg.daysvalid": "Days valid",
		"msg.tokenpath": "ID",
		"msg.imprint": "Imprint",
		"msg.chr": "Certificate Holder Reference",
		"msg.keys": "Keys",

		"action.close": "Close",
		"action.create": "Create",
		"action.save": "Save",
		"action.save.tooltip": "Save changes made in the form without proceeding in the process",
		"action.submit": "Submit",
		"action.submit.tooltip": "Save changes made and proceed in the process",
		"action.update": "Update",
		"action.change": "Change",
		"action.cancel": "Cancel",
		"action.forward": "Forward",
		"action.approve": "Approve",
		"action.reject": "Reject",
		"action.apply": "Apply",
		"action.accept": "Accept",
		"action.remove": "Remove",
		"action.terminate": "Terminate",
		"action.revoke": "Revoke",
		"action.delete": "Delete",
		"action.suspend": "Suspend",
		"action.reinstate": "Reinstate",
		"action.link": "Link",
		"action.unlink": "Unlink",

		"error.invalid_data": "Invalid input",
		"error.not_a_number": "Input is not a number",
		"error.too_small": "Number is too small",
		"error.too_large": "Number is too large",
		"error.input_too_long": "Input is too long",
		"error.input_too_short": "Input is too short",
		"error.field_empty": "Empty input",
	}},
	{ lang: "DE", keys: {
		"msg.subject": "Subject",
		"msg.person": "Person",
		"msg.system": "System",
		"msg.holder": "Inhaber",
		"msg.issuer": "Herausgeber",
		"msg.token": "Token",
		"msg.keydomain": "Schlüsseldomäne",
		"msg.profile": "Profil",
		"msg.name": "Name",
		"msg.email": "email",
		"msg.status": "Status",
		"msg.request": "Request",
		"msg.daysvalid": "Tage gültig",
		"msg.tokenpath": "ID",
		"msg.imprint": "Impressum",
		"msg.chr": "Certificate Holder Reference",
		"msg.keys": "Schlüssel",

		"action.close": "Schließen",
		"action.create": "Anlegen",
		"action.save": "Speichern",
		"action.save.tooltip": "Änderungen speichern ohne den Vorgang fortzusetzen",
		"action.submit": "Einreichen",
		"action.submit.tooltip": "Änderungen speichern und Vorgang fortsetzen",
		"action.update": "Aktualisieren",
		"action.change": "Ändern",
		"action.cancel": "Abbrechen",
		"action.forward": "Weiterleiten",
		"action.approve": "Genehmigen",
		"action.reject": "Ablehnen",
		"action.apply": "Anwenden",
		"action.accept": "Annehmen",
		"action.remove": "Entfernen",
		"action.terminate": "Beenden",
		"action.revoke": "Zurückziehen",
		"action.delete": "Löschen",
		"action.suspend": "Suspendieren",
		"action.reinstate": "Wiederherstellen",
		"action.link": "Verbinden",
		"action.unlink": "Trennen",
	}}
]);



function CAGUI(service, session) {
	CommonUI.call(this, service, session);
	this.inboundServiceRequestBrowser = new ServiceRequestBrowser(this, ServiceRequestBrowser.ASSIGNEDTOME);
	this.myServiceRequestBrowser = new ServiceRequestBrowser(this, ServiceRequestBrowser.MYREQUESTS);
	this.serviceRequestController = new ServiceRequestController(this);
	this.certificateView = new CertificateView(this);
	this.subjectController = new SubjectController(this);
	this.auditorSubjectOverview = new SubjectOverviewController(this, true);
	this.auditorServiceRequestOverview = new ServiceRequestOverviewController(this, true);
	this.serviceRequestOverview = new ServiceRequestOverviewController(this);
	this.roleController = new RoleController(this);
	this.tokenListViewController = new TokenListViewController(this);
	this.holderListViewController = new HolderListViewController(this);

	this.crypto = new Crypto();
	this.sessionkey = new Key();
	this.sessionkey.setComponent(Key.AES, this.crypto.generateRandom(16));
	this.sessionts = Date.now();
}



CAGUI.prototype = Object.create(CommonUI.prototype);
CAGUI.constructor = CAGUI;

exports.CAGUI = CAGUI;



CAGUI.prototype.i18n = function(key) {
	return I18N.t(this.session.user.lang, key);
}



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

	var page =
		<div>
			<h1>PKI as a Service</h1>
			<div class="pure-g">
				<div id="outboundrequests" class="pure-u-1 pure-u-lg-1-2"/>
				<div id="inboundrequests" class="pure-u-1 pure-u-lg-1-2"/>
			</div>
		</div>

	var srview = this.myServiceRequestBrowser.renderServiceRequestList(url[0]);
	if (srview) {
		var div = page..div.(@id == "outboundrequests");
		div.appendChild(srview);
	}

	var srview = this.inboundServiceRequestBrowser.renderServiceRequestList(url[0]);
	if (srview) {
		srview.@class += " right-box";
		var div = page..div.(@id == "inboundrequests");
		div.appendChild(srview);
	}

	var t = this.generateTemplate(url);
 	var c = t..div.(@id == "content");
 	c.div = page;
	this.sendPage(req, res, url, t);
}



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

	var page = <p>You are not authorized to access this service or function.</p>

	var t = this.generateTemplate(url);
 	var c = t..div.(@id == "content");
 	c.div = page;
	this.sendPage(req, res, url, t);
}



CAGUI.prototype.handleCard = function(session, pathInfo) {
	if (this.wizard) {
		this.wizard.handleCard(session, pathInfo);
	}
}



/**
 * Check if the current token is authorized.
 * Sets a Registration Wizard if not authorized.
 *
 * @return true if token is authorized, false otherwise
 */
CAGUI.prototype.isRegistrationActive = function (req, res, url) {
	GPSystem.log(GPSystem.DEBUG, module.id, "Session Path: " + this.session.cardState.chain.path);

	if (typeof(this.session.user.id) == "undefined") {
		if (!this.wizard) {
			var sr = this.service.getRegisterMyTokenServiceRequest(this.session.cardState.chain.path, this.session.user.lang);
			if (!sr) {	// No registration available because token is assigned to non-person subject
				this.setErrorMessage("Configuration", "Self-registration is disabled or token is already assigned to a non-person subject");
				return false;
			}
			this.wizard = new RegistrationWizard(this, sr);
		}
		return true;
	}

	return false;
}



CAGUI.prototype.isAuditor = function() {
	GPSystem.log(GPSystem.DEBUG, module.id, "isAuditor( user=" + this.session.user.id +" )");
	GPSystem.log(GPSystem.DEBUG, module.id, "roles= " + this.session.user.roles);
	if (this.session.user.roles.indexOf(RoleDAO.ID_Auditor) >= 0) {
		return true;
	}

	return false;
}



CAGUI.prototype.resetWizard = function (req, res, url) {
	delete this.wizard;
}



CAGUI.prototype.getBasicMenuModel = function(url) {
	default xml namespace = "http://www.w3.org/1999/xhtml";

	var model = new MenuModel();

	var baseurl = this.homeURL(url);

	var home = new MenuModel(<a href={baseurl}>Home</a>);

	if (url.length == 1) {
		var processes = this.service.pluginRegistry.enumerateRunnableProcesses(this.session.user);

		for (var i = 0; i < processes.length; i++) {
			home.addItem(
				<a href= {baseurl + "/sr/new?process=" + processes[i].process }>
					{ processes[i].label }
				</a>);
		}
	}

	var views = new MenuModel(<p class="no-margin">Views</p>);
	views.addItem(<a href={baseurl + "/subject"}>Subjects</a>);
	views.addItem(<a href={baseurl + "/sroverview"}>Service Requests</a>);
	if (this.isAuditor()) {
		views.addItem(<a href={baseurl + "/auditor/sroverview"}>Audit Service Requests</a>);
		views.addItem(<a href={baseurl + "/auditor/subject"}>Audit Subjects</a>);
	}
	if (this.session.user.roles.length > 0) {
		views.addItem(<a href={baseurl + "/token"}>Tokens</a>);
	}

	var bookmarks = ApplicationServer.instance.getUIList();
	for (var i = 0; i < bookmarks.length; i++) {
		var bm = bookmarks[i];

		if (this.session.user.roles.indexOf(bm.roleRequired) < 0) {
			// skip bookmark if the user wasn't assigned to the required role
			continue;
		}
		if (bm.name != this.service.name) {
			views.addItem(<a href={bm.url}>{bm.name}</a>);
		}
	}

	model.addItem(home);
	model.addItem(views);

	return model;
}



/**
 * Generate a HTML template
 *
 * @param {String[]} url the array of URL elements
 * @returns the HTML page template
 * @type XML
 */
CAGUI.prototype.generateTemplate = function(url, bookmarks) {
	default xml namespace = "http://www.w3.org/1999/xhtml";

	var prefix = "";
	for (var i = 1; i < url.length; i++) {
		prefix += "../";
	}

	var pagetemplate =
		<html xmlns="http://www.w3.org/1999/xhtml" lang="en" xml:lang="en" >
			<head>
				<title>{this.service.type + " " + this.service.name}</title>
				<link rel="stylesheet" type="text/css" href={prefix + "../css/paas_style.css"}/>
				<link rel="stylesheet" type="text/css" href={prefix + "../css/pure-min.css"}/>
				<link rel="stylesheet" type="text/css" href={prefix + "../css/grids-responsive-min.css"}/>
			</head>
			<body>
				<div class="banner" align="left">
					<a href="http://www.cardcontact.de"><img src={prefix + "../images/CardContact_logo_b28.jpg"}  border="0"/></a>
				</div>
				<div id = "menu" class="paas-navigator pure-g ">
					<div class="pure-u-1-2">
						<div id="navigator" class="pure-menu pure-menu-horizontal"/>
					</div>
					<div class="pure-u-1-2">
						<div id="user" class="paas-navigator-user pure-menu pure-menu-horizontal"/>
					</div>
				</div>
				<div id="main">
					<div id="error"/>
					<div id="content"/>
					<div class="footer pure-g">
						<div class="pure-u-1 pure-u-md-1-2">
							<p id="imprint" class="imprint"></p>
						</div>
						<div class="pure-u-1 pure-u-md-1-2">
							<p class="copyright">Version { VERSION } {String.fromCharCode(0xA9) + " Copyright 2003 - 2023 "}<a href="http://www.cardcontact.de">CardContact</a> Systems GmbH, Minden, Germany</p>
						</div>
					</div>
				</div>
			</body>
		</html>

	if (this.session.user.id) {
		var menuModel = this.getBasicMenuModel(url);
		if (bookmarks) {
			menuModel.addItem(bookmarks);
		}
		var navigator = pagetemplate..div.(@id == "navigator");
		var gen = new PureMenuGenerator();
		var menu = gen.generateHorizontalMenu(menuModel);
		navigator.appendChild(menu);

		var userMenuModel = new MenuModel();
		var user = new MenuModel(<p class="paas-navigator-user-name no-margin">{this.session.user.name}</p>);
		user.status = MenuModel.STATE_PRE_RENDERED;
		userMenuModel.addItem(user);
		userMenuModel.addItem(<a href={prefix + url[0] + "/logout"}>Logout</a>);
		var gen = new PureMenuGenerator();
		var content = gen.generateHorizontalMenu(userMenuModel);

		var div = pagetemplate.body..div.(@id == "user");
		div.appendChild(content);
	} else {
//		var menuDiv = pagetemplate..div.(@id == "menu");
//		menuDiv.div = <div class = "paas-navigator-empty"/>;

		var userMenuModel = new MenuModel();
		userMenuModel.addItem(<a href={prefix + url[0] + "/logout"}>Logout</a>);
		var gen = new PureMenuGenerator();
		var content = gen.generateHorizontalMenu(userMenuModel);

		var div = pagetemplate.body..div.(@id == "user");
		div.appendChild(content);
	}

	var p = pagetemplate.body..p.(@id == "imprint");
	if (typeof(this.service.imprintURL) != "undefined") {
		p.appendChild(
			<a href={this.service.imprintURL}>{this.i18n("msg.imprint")}</a>
		);
	}

	if (this.resultCode != null) {
		var page =
			<div>
				<p id="result" class= { this.resultClass }>{ this.resultMessage } (<b id="resultCode">{ this.resultCode }</b>)</p>
			</div>

		var div = pagetemplate.body..div.(@id == "error");
		div.appendChild(page);
	}

	return pagetemplate;
}



/**
 * Send page after embedding in the HTML template
 *
 * @param {HttpRequest} req the request object
 * @param {HttpResponse} req the response object
 * @param {String[]} url the array of URL elements
 * @param {XML} page the page contents
 */
CAGUI.prototype.sendPage = function(req, res, url, page) {
	default xml namespace = "http://www.w3.org/1999/xhtml";

	if (typeof(page) == "undefined") {
		url = req.url;
		page = url;
	}

	this.clearResultMessage();

	res.setContentType("application/xhtml+xml");
	res.print('<?xml version="1.0" encoding="UTF-8" standalone="no"?>');
	res.print('<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd">\n');
	res.print(page.toXMLString());
}



/**
 * Serves a refresh page that redirects back to the given URL
 *
 * @param {HttpRequest} req the request object
 * @param {HttpResponse} req the response object
 * @param {String} refreshUrl the redirect URL
 * @param {String} statusMessage the status message or undefined
 */
CAGUI.prototype.serveRefreshPage = function(req, res, url, statusMessage) {
	default xml namespace = "http://www.w3.org/1999/xhtml";

	var prefix = "";
	for (var i = 1; i < url.length; i++) {
		prefix += "../";
	}

	var page = this.generateTemplate(url);

	if ((typeof(statusMessage) == "undefined") || (statusMessage == null)) {
		statusMessage = "Operation completed";
	}

 	page.head.meta = <meta http-equiv="Refresh" content={"1; url=" + prefix + url[0]}/>;

	var c = page..div.(@id == "content");

	c.p = <h3>{statusMessage}</h3>

	res.setContentType("application/xhtml+xml");
	res.print('<?xml version="1.0" encoding="UTF-8" standalone="no"?>');
	res.print('<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd">\n');
	res.print(page.toXMLString());
}



/**
 * @param {HttpRequest} req the request object
 * @param {HttpResponse} req the response object
 * @param {String[]} url array of URL path elements
 */
CAGUI.prototype.handleWizard = function(req, res, url) {
	default xml namespace = "http://www.w3.org/1999/xhtml";
	GPSystem.log(GPSystem.DEBUG, module.id, "handleWizard(" + url + ")");

	if (!this.wizard) {
		var content = <div>
				<p>No wizard active or cookies disabled in browser.
				   You see this message, because the application redirected your browser
				   to the wizard URL, but the server was unable to related a running
				   wizard to your browser session. The most likely reason is that
				   you disabled cookies in the browser or entered the wizard URL without
				   first starting a wizard.</p>
			</div>

		var page = this.generateTemplate(req.url);
		var c = page..div.(@id == "content");
		c.div = content;

		this.sendPage(req, res, url, page);
		return;
	}

	if (typeof(this.wizard.handleRequest) == "function") {
		this.wizard.handleRequest(req, res);
	} else {
		content = this.wizard.getPage(req, res, url);

		if (content) {
			var page = this.generateTemplate(req.url);
			var c = page..div.(@id == "content");
			c.div = content;

			this.sendPage(req, res, url, page);
		} else {
			if (req.queryString || req.method == "POST") {			// Form processing redirect
				this.redirectTo(req, res, url, "/wizard");
			} else {
				if (this.resultCode) {
					this.redirectTo(req, res, url, "");
				} else {
					this.serveRefreshPage(req, res, url, this.wizard.getReturnCode());
				}
			}
		}
	}
}



/**
 * Handle operations in the status page
 *
 * @param {HttpRequest} req the request object
 * @param {HttpResponse} req the response object
 * @param {String[]} url array of URL path elements
 */
CAGUI.prototype.handleOperation = function(req, res, url) {
	try	{
		// Handle operations
		var operation = CommonUI.parseQueryString(req.queryString);
		GPSystem.log(GPSystem.INFO, module.id, "handleOperation(" + operation.op + ")");

		switch(operation.op) {
			case "resetdb":
				if (this.service.allowResetDB && this.service.daof) {
					this.service.daof.dropTables();
					this.service.daof.createTables();
					if (this.service.reinitializeHSMOnResetDB) {
						var list = this.service.hsmService.getHSMList();
						if (list.length > 0) {
							var id = list[0];
							this.service.hsmService.reinitializeHSM(id);
						}
					}

					this.handleLogout(req, res, url);
				} else {
					this.setErrorMessage("not_authorized", "Database reset not allowed");
					this.redirectTo(req, res, url, "");
				}
				break;
			case "updatedb":
				if (this.service.updateDB && this.service.daof) {
					this.service.daof.updateTables(this.service.updateDB);
				}
				this.redirectTo(req, res, url, "");
				break;
			case "logout":
				this.handleLogout(req, res, url);
				break;
			case "changepagesizeinboundrequest":
				var val = parseInt(operation.pagesize);
				if (val > 0) {
					this.inboundServiceRequestBrowser.setPageSize(val);
				} else {
					this.setErrorMessage("invalid_argument", "Page size must be greater than 0");
				}
				this.redirectTo(req, res, url, "");
				break;
			case "changepagesizemyrequest":
				var val = parseInt(operation.pagesize);
				if (val > 0) {
					this.myServiceRequestBrowser.setPageSize(val);
				} else {
					this.setErrorMessage("invalid_argument", "Page size must be greater than 0");
				}
				this.redirectTo(req, res, url, "");
				break;
			case "changeoffsetinboundrequest":
				var val = parseInt(operation.offset);
				this.inboundServiceRequestBrowser.setOffset(val);
				this.redirectTo(req, res, url, "");
				break;
			case "changeoffsetmyrequest":
				var val = parseInt(operation.offset);
				this.myServiceRequestBrowser.setOffset(val);
				this.redirectTo(req, res, url, "");
				break;
			default:
				this.setErrorMessage("invalid_operation", "The operation " + operation.op + " is unknown");
				this.serveMainPage(req, res, url);
		}

	}
	catch(e) {
		GPSystem.log(GPSystem.ERROR, module.id, e.message + "\n" + e.stack);
		if (!this.handleSmartCardHSMError(e, true)) {
			this.setErrorMessage("internal_error", "Internal error '" + e.message + "' at " + e.fileName + "#" + e.lineNumber + ". See log for details");
		}
		this.serveMainPage(req, res, url);
	}
}



CAGUI.prototype.generateSessionToken = function() {
	var input = ByteString.valueOf(Date.now());
	var mac = this.crypto.sign(this.sessionkey, Crypto.AES_CMAC, input);
	return input.concat(mac);
}



CAGUI.prototype.verifySessionToken = function(sessionToken) {
	var input = sessionToken.left(sessionToken.length - 16);
	var mac = this.crypto.sign(this.sessionkey, Crypto.AES_CMAC, input);
	if (!sessionToken.right(16).equals(mac)) {
		GPSystem.log(GPSystem.ERROR, module.id, "sessionToken " + sessionToken.toString(HEX) + " fails MAC checking");
		return false;
	}

	var ts = input.toUnsigned();
	if (this.sessionts >= ts) {
		GPSystem.log(GPSystem.ERROR, module.id, "sessionToken " + sessionToken.toString(HEX) + " is expired");
		return false;
	}

	this.sessionts = ts;
	return true;
}



CAGUI.prototype.handleRequest = function(req, res) {
	GPSystem.log(GPSystem.INFO, module.id, req.toString());

	var url = req.pathInfo.substr(1).split("/");

	if (url.length == 2 && url[1] == "logout") {
		this.handleLogout(req, res, url);
		GPSystem.log(GPSystem.INFO, module.id, "Logout" );
		return;
	}

	if (this.isRegistrationActive(req, res, url)) {
		this.handleWizard(req, res, url);
		GPSystem.log(GPSystem.INFO, module.id, "Registration active" );
		return;
	}

	if (!this.isAuthorized()) {
		this.serveNotAuthorizedPage(req, res, url);
		GPSystem.log(GPSystem.ERROR, module.id, "Not authorized" );
		return;
	}

	if (url.length > 1) {
		// Handle details view
		var detailsType = url[1];

		if (detailsType != "wizard") {
			delete this.wizard;
		}

		switch(detailsType) {
		case "wizard":
			this.handleWizard(req, res, url);
			break;
		case "logout":
			this.handleLogout(req, res, url);
			break;
		case "sr":
			this.serviceRequestController.handleRequest(req, res);
			break;
		case "sr-rest":
			this.serviceRequestController.handleRestRequest(req, res);
			break;
		case "auditor":
			if (!this.isAuditor()) {
				GPSystem.log(GPSystem.ERROR, module.id, "Not authorized to access audit functions" );
				this.serveNotAuthorizedPage(req, res, url);
				break;
			}
			if (url[2] == "subject") {
				this.auditorSubjectOverview.handleRequest(req, res);
				break;
			} else if (url[2] == "sroverview") {
				this.auditorServiceRequestOverview.handleRequest(req, res);
				break;
			} else {
				res.setStatus(HttpResponse.SC_NOT_FOUND);
			}
			break;
		case "sroverview":
			this.serviceRequestOverview.handleRequest(req, res);
			break;
		case "subject":
			this.subjectController.handleRequest(req, res);
			break;
		case "subject-rest":
			this.subjectController.handleRestRequest(req, res);
			break;
		case "holder":
			this.holderListViewController.handleRequest(req, res);
			break;
		case "token":
			this.tokenListViewController.handleRequest(req, res);
			break;
		case "cert":
			this.certificateView.handleRequest(req, res);
			break;
		case "cert-rest":
			this.certificateView.handleRestRequest(req, res);
			break;
		case "role":
			this.roleController.handleRequest(req, res);
			break;
		default:
			res.setStatus(HttpResponse.SC_NOT_FOUND);
		}
	} else {
		// Handle operations
		if (req.queryString) {
			this.handleOperation(req, res, url);
		} else {
			this.signState = 0;
			this.serveMainPage(req, res, url);
		}
	}

	GPSystem.log(GPSystem.DEBUG, module.id, "Completed with " + res.toString());
}
