From 1c3a46c869307f45e3c456254b086503600f8602 Mon Sep 17 00:00:00 2001
From: =?utf8?q?Roland=20H=C3=A4der?= <roland@mxchange.org>
Date: Sat, 14 Jun 2008 19:20:03 +0000
Subject: [PATCH] Cookie-based login initially done

---
 .gitattributes                                |  4 +
 application/ship-simu/config.php              |  6 ++
 .../main/login/class_ShipSimuUserLogin.php    | 18 +++-
 .../helper/class_ShipSimuLoginHelper.php      | 73 +++++++++++++-
 inc/classes/interfaces/helper/.htaccess       |  1 +
 .../interfaces/helper/class_HelpableLogin.php | 36 +++++++
 .../interfaces/login/class_LoginableUser.php  |  3 +-
 .../response/class_Responseable.php           | 23 ++++-
 .../class_LoginAfterRegistrationAction.php    |  4 +-
 .../main/helper/web/class_WebFormHelper.php   |  3 +-
 inc/classes/main/login/.htaccess              |  1 +
 inc/classes/main/login/class_CookieLogin.php  | 99 +++++++++++++++++++
 .../main/response/class_HttpResponse.php      | 82 ++++++++++++++-
 inc/config.php                                | 15 +++
 inc/config/class_FrameworkConfiguration.php   | 24 ++++-
 templates/de/code/emergency_exit.ctp          |  9 ++
 16 files changed, 387 insertions(+), 14 deletions(-)
 create mode 100644 inc/classes/interfaces/helper/.htaccess
 create mode 100644 inc/classes/interfaces/helper/class_HelpableLogin.php
 create mode 100644 inc/classes/main/login/.htaccess
 create mode 100644 inc/classes/main/login/class_CookieLogin.php

diff --git a/.gitattributes b/.gitattributes
index e2ffaca..81369b8 100644
--- a/.gitattributes
+++ b/.gitattributes
@@ -259,6 +259,8 @@ inc/classes/interfaces/extended/.htaccess -text
 inc/classes/interfaces/extended/class_LimitableObject.php -text
 inc/classes/interfaces/filter/.htaccess -text
 inc/classes/interfaces/filter/class_Filterable.php -text
+inc/classes/interfaces/helper/.htaccess -text
+inc/classes/interfaces/helper/class_HelpableLogin.php -text
 inc/classes/interfaces/io/.htaccess -text
 inc/classes/interfaces/io/class_Streamable.php -text
 inc/classes/interfaces/io/file/.htaccess -text
@@ -376,6 +378,8 @@ inc/classes/main/io/class_FrameworkFileInputPointer.php -text
 inc/classes/main/io/class_FrameworkFileOutputPointer.php -text
 inc/classes/main/language/.htaccess -text
 inc/classes/main/language/class_LanguageSystem.php -text
+inc/classes/main/login/.htaccess -text
+inc/classes/main/login/class_CookieLogin.php -text
 inc/classes/main/output/.htaccess -text
 inc/classes/main/output/class_ConsoleOutput.php -text
 inc/classes/main/output/class_WebOutput.php -text
diff --git a/application/ship-simu/config.php b/application/ship-simu/config.php
index 3d4b877..a827e38 100644
--- a/application/ship-simu/config.php
+++ b/application/ship-simu/config.php
@@ -82,5 +82,11 @@ $cfg->setConfigEntry('user_status_register', "UNCONFIRMED");
 // CFG: LOGIN-HELPER
 $cfg->setConfigEntry('login_helper', "ShipSimuLoginHelper");
 
+// CFG: LOGIN-METHOD
+$cfg->setConfigEntry('login_method', "cookie");
+
+// CFG: APP-LOGIN-URL
+$cfg->setConfigEntry('app_login_url', "index.php?app=ship-simu&page=login_area");
+
 // [EOF]
 ?>
diff --git a/application/ship-simu/main/login/class_ShipSimuUserLogin.php b/application/ship-simu/main/login/class_ShipSimuUserLogin.php
index a043de8..59ddcdc 100644
--- a/application/ship-simu/main/login/class_ShipSimuUserLogin.php
+++ b/application/ship-simu/main/login/class_ShipSimuUserLogin.php
@@ -61,6 +61,7 @@ class ShipSimuUserLogin extends BaseFrameworkSystem implements LoginableUser {
 	 * in a boolean attribute which is then readable by a matching getter.
 	 *
 	 * @param	$requestInstance	An instance of a Requestable class
+	 * @param	$responseInstance	An instance of a Responseable class
 	 * @return	void
 	 * @throws	UserLoginMethodException	If wether username nor email login
 	 *										was detected
@@ -69,7 +70,7 @@ class ShipSimuUserLogin extends BaseFrameworkSystem implements LoginableUser {
 	 * @throws	UserPasswordMismatchException	If the supplied password did not
 	 *										match with the stored password
 	 */
-	public function doLogin (Requestable $requestInstance) {
+	public function doLogin (Requestable $requestInstance, Responseable $responseInstance) {
 		// By default no method is selected
 		$method = null;
 		$data = "";
@@ -118,7 +119,20 @@ class ShipSimuUserLogin extends BaseFrameworkSystem implements LoginableUser {
 		$helperInstance = ObjectFactory::createObjectByConfiguredName('login_helper', array($requestInstance));
 
 		// 2) Execute the login. This will now login...
-		$helperInstance->executeLogin();
+		$helperInstance->executeLogin($responseInstance);
+	}
+
+	/**
+	 * Determines wether the login was fine. This is done by checking if the 'login' instance is in registry
+	 *
+	 * @return	$loginDone	Wether the login was fine or not
+	 */
+	public function ifLoginWasSuccessfull () {
+		// Is the registry key there?
+		$loginDone = (Registry::getRegistry()->getInstance('login') instanceof Registerable);
+
+		// Return the result
+		return $loginDone;
 	}
 }
 
diff --git a/application/ship-simu/main/login/helper/class_ShipSimuLoginHelper.php b/application/ship-simu/main/login/helper/class_ShipSimuLoginHelper.php
index 13bed29..9dd7f96 100644
--- a/application/ship-simu/main/login/helper/class_ShipSimuLoginHelper.php
+++ b/application/ship-simu/main/login/helper/class_ShipSimuLoginHelper.php
@@ -15,7 +15,6 @@
  * @copyright	Copyright(c) 2007, 2008 Roland Haeder, this is free software
  * @license		GNU GPL 3.0 or any newer version
  * @link		http://www.ship-simu.org
- * @todo		Find an interface name for login helper
  *
  * This program is free software: you can redistribute it and/or modify
  * it under the terms of the GNU General Public License as published by
@@ -30,7 +29,17 @@
  * You should have received a copy of the GNU General Public License
  * along with this program. If not, see <http://www.gnu.org/licenses/>.
  */
-class ShipSimuLoginHelper extends BaseLoginHelper {
+class ShipSimuLoginHelper extends BaseLoginHelper implements HelpableLogin {
+	/**
+	 * The login method we shall choose
+	 */
+	private $loginMethod = "";
+
+	/**
+	 * Instance for a request class
+	 */
+	private $requestInstance = null;
+
 	// Exception constants
 	const EXCEPTION_INVALID_USER_INSTANCE = 0xf00;
 
@@ -88,11 +97,67 @@ class ShipSimuLoginHelper extends BaseLoginHelper {
 			// Set default login method from config
 			$helperInstance->setDefaultLoginMethod();
 		}
-		
+
+		// Set request instance
+		$helperInstance->setRequestInstance($requestInstance);
+
 		// Return the prepared instance
 		return $helperInstance;
 	}
+
+	/**
+	 * Setter for default login method from config
+	 *
+	 * @return	void
+	 */
+	protected function setDefaultLoginMethod () {
+		$this->loginMethod = $this->getConfigInstance()->readConfig('login_method');
+	}
+
+	/**
+	 * Setter for request instance
+	 *
+	 * @param	$requestInstance	A Requestable class instance
+	 * @return	void
+	 */
+	public final function setRequestInstance (Requestable $requestInstance) {
+		$this->requestInstance = $requestInstance;
+	}
+
+	/**
+	 * Getter for request instance
+	 *
+	 * @param	
+	 * @return	$requestInstance	A Requestable class instance
+	 */
+	public final function getRequestInstance () {
+		return $this->requestInstance;
+	}
+
+	/**
+	 * Execute the login request by given response instance. This instance can
+	 * be used for sending cookies or at least the session id out.
+	 *
+	 * @param	$responseInstance	An instance of a Responseable class
+	 * @return	void
+	 */
+	public function executeLogin (Responseable $responseInstance) {
+		// First create the requested login method name
+		$loginMethodClass = ucfirst(strtolower($this->loginMethod)) . "Login";
+
+		// Then try to get an instance from it
+		$loginInstance = ObjectFactory::createObjectByName($loginMethodClass, array($responseInstance));
+
+		// Set user cookie
+		$loginInstance->setUserAuth($this->requestInstance->getRequestElement('username'));
+
+		// Set password cookie
+		$loginInstance->setPasswordAuth($this->requestInstance->getRequestElement('pass_hash'));
+
+		// Remember this login instance for later usage
+		Registry::getRegistry()->addInstance('login', $loginInstance);
+	}
 }
 
-// [EOF]
+//
 ?>
diff --git a/inc/classes/interfaces/helper/.htaccess b/inc/classes/interfaces/helper/.htaccess
new file mode 100644
index 0000000..3a42882
--- /dev/null
+++ b/inc/classes/interfaces/helper/.htaccess
@@ -0,0 +1 @@
+Deny from all
diff --git a/inc/classes/interfaces/helper/class_HelpableLogin.php b/inc/classes/interfaces/helper/class_HelpableLogin.php
new file mode 100644
index 0000000..cf5f84f
--- /dev/null
+++ b/inc/classes/interfaces/helper/class_HelpableLogin.php
@@ -0,0 +1,36 @@
+<?php
+/**
+ * A helper interface for logins
+ *
+ * @author		Roland Haeder <webmaster@ship-simu.org>
+ * @version		0.0.0
+ * @copyright	Copyright(c) 2007, 2008 Roland Haeder, this is free software
+ * @license		GNU GPL 3.0 or any newer version
+ * @link		http://www.ship-simu.org
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program 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 this program. If not, see <http://www.gnu.org/licenses/>.
+ */
+interface HelpableLogin extends FrameworkInterface {
+	/**
+	 * Execute the login request by given response instance. This instance can
+	 * be used for sending cookies or at least the session id out.
+	 *
+	 * @param	$responseInstance	An instance of a Responseable class
+	 * @return	void
+	 */
+	function executeLogin (Responseable $responseInstance);
+}
+
+//
+?>
diff --git a/inc/classes/interfaces/login/class_LoginableUser.php b/inc/classes/interfaces/login/class_LoginableUser.php
index 962cba8..107b133 100644
--- a/inc/classes/interfaces/login/class_LoginableUser.php
+++ b/inc/classes/interfaces/login/class_LoginableUser.php
@@ -28,9 +28,10 @@ interface LoginableUser extends FrameworkInterface {
 	 * in a boolean attribute which is then readable by a matching getter.
 	 *
 	 * @param	$requestInstance	An instance of a Requestable class
+	 * @param	$responseInstance	An instance of a Responseable class
 	 * @return	void
 	 */
-	function doLogin (Requestable $requestInstance);
+	function doLogin (Requestable $requestInstance, Responseable $responseInstance);
 }
 
 //
diff --git a/inc/classes/interfaces/response/class_Responseable.php b/inc/classes/interfaces/response/class_Responseable.php
index c7d2103..5fd6b50 100644
--- a/inc/classes/interfaces/response/class_Responseable.php
+++ b/inc/classes/interfaces/response/class_Responseable.php
@@ -56,7 +56,7 @@ interface Responseable extends FrameworkInterface {
 	 * @throws	ResponseHeadersAlreadySentException		Thrown if headers are
 	 *													already sent
 	 */
-	function flushBuffer($force=false);
+	function flushBuffer ($force = false);
 
 	/**
 	 * Adds a fatal message id to the response. The added messages can then be
@@ -66,6 +66,27 @@ interface Responseable extends FrameworkInterface {
 	 * @return	void
 	 */
 	function addFatalMessage ($messageId);
+
+	/**
+	 * Adds a cookie to the response
+	 *
+	 * @param	$cookieName		Cookie's name
+	 * @param	$cookieValue	Value to store in the cookie
+	 * @param	$encrypted		Do some extra encryption on the value
+	 * @return	void
+	 * @throws	ResponseHeadersAlreadySentException		If headers are already sent
+	 */
+	function addCookie ($cookieName, $cookieValue, $encrypted = false);
+
+	/**
+	 * Redirect to a configured URL. The URL can be absolute or relative. In
+	 * case of relative URL it will be extended automatically.
+	 *
+	 * @param	$configEntry	The configuration entry which holds our URL
+	 * @return	void
+	 * @throws	ResponseHeadersAlreadySentException		If headers are already sent
+	 */
+	function redirectToConfiguredUrl ($configEntry);
 }
 
 //
diff --git a/inc/classes/main/actions/post_registration/class_LoginAfterRegistrationAction.php b/inc/classes/main/actions/post_registration/class_LoginAfterRegistrationAction.php
index aae33eb..63c4450 100644
--- a/inc/classes/main/actions/post_registration/class_LoginAfterRegistrationAction.php
+++ b/inc/classes/main/actions/post_registration/class_LoginAfterRegistrationAction.php
@@ -63,13 +63,13 @@ class LoginAfterRegistrationAction extends BaseAction implements Commandable {
 		$loginInstance = ObjectFactory::createObjectByConfiguredName('login_user');
 
 		// Login the user by the request instance
-		$loginInstance->doLogin($requestInstance);
+		$loginInstance->doLogin($requestInstance, $responseInstance);
 
 		// Was the login fine? Then redirect here
 		if ($loginInstance->ifLoginWasSuccessfull()) {
 			// Try to redirect here
 			try {
-				$responseInstance->redirectConfiguredUrl('app_login');
+				$responseInstance->redirectToConfiguredUrl('app_login_url');
 			} catch (FrameworkException $e) {
 				// Something went wrong here!
 				$responseInstance->addFatalMessage($e->getMessage());
diff --git a/inc/classes/main/helper/web/class_WebFormHelper.php b/inc/classes/main/helper/web/class_WebFormHelper.php
index e71f151..718ed77 100644
--- a/inc/classes/main/helper/web/class_WebFormHelper.php
+++ b/inc/classes/main/helper/web/class_WebFormHelper.php
@@ -119,8 +119,9 @@ class WebFormHelper extends BaseHelper {
 		// Check wether we shall open or close the form
 		if ($this->formOpened === false) {
 			// Add HTML code
-			$formContent = sprintf("<form name=\"%s\" class=\"forms\" action=\"%s\" method=\"%s\" target=\"%s\"",
+			$formContent = sprintf("<form name=\"%s\" class=\"forms\" action=\"%s/%s\" method=\"%s\" target=\"%s\"",
 				$formName,
+				$this->getConfigInstance()->readConfig('base_url'),
 				$this->getConfigInstance()->readConfig('form_action'),
 				$this->getConfigInstance()->readConfig('form_method'),
 				$this->getConfigInstance()->readConfig('form_target')
diff --git a/inc/classes/main/login/.htaccess b/inc/classes/main/login/.htaccess
new file mode 100644
index 0000000..3a42882
--- /dev/null
+++ b/inc/classes/main/login/.htaccess
@@ -0,0 +1 @@
+Deny from all
diff --git a/inc/classes/main/login/class_CookieLogin.php b/inc/classes/main/login/class_CookieLogin.php
new file mode 100644
index 0000000..cbc9203
--- /dev/null
+++ b/inc/classes/main/login/class_CookieLogin.php
@@ -0,0 +1,99 @@
+<?php
+/**
+ * A cookie-bases login class
+ *
+ * @author		Roland Haeder <webmaster@ship-simu.org>
+ * @version		0.0.0
+ * @copyright	Copyright(c) 2007, 2008 Roland Haeder, this is free software
+ * @license		GNU GPL 3.0 or any newer version
+ * @link		http://www.ship-simu.org
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program 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 this program.  If not, see <http://www.gnu.org/licenses/>.
+ */
+class CookieLogin extends BaseFrameworkSystem implements Registerable {
+	/**
+	 * Response instance
+	 */
+	private $responseInstance = null;
+
+	/**
+	 * Protected constructor
+	 *
+	 * @return	void
+	 */
+	protected function __construct () {
+		// Call parent constructor
+		parent::__construct(__CLASS__);
+
+		// Set part description
+		$this->setObjectDescription("Cookie-based login");
+
+		// Create unique ID number
+		$this->generateUniqueId();
+
+		// Clean up a little
+		$this->removeNumberFormaters();
+		$this->removeSystemArray();
+	}
+
+	/**
+	 * Creates an instance of this class by the given response instance
+	 *
+	 * @param	$responseInstance	An instance of a Responseable class
+	 * @return	$loginInstance		An instance of this login class
+	 */
+	public final static function createCookieLogin (Responseable $responseInstance) {
+		// Get a new instance
+		$loginInstance = new CookieLogin();
+
+		// Set the response instance
+		$loginInstance->setResponseInstance($responseInstance);
+
+		// Return the prepared instance
+		return $loginInstance;
+	}
+
+	/**
+	 * Setter for login instance
+	 *
+	 * @param	$responseInstance	An instance of a Responseable class
+	 * @return	void
+	 */
+	protected final function setResponseInstance (Responseable $responseInstance) {
+		$this->responseInstance = $responseInstance;
+	}
+
+	/**
+	 * "Setter" for username auth data
+	 *
+	 * @param	$userName	The username from request we shall set
+	 * @return	void
+	 */
+	public function setUserAuth ($userName) {
+		$this->responseInstance->addCookie('username', $userName);
+	}
+
+	/**
+	 * "Setter" for password hash auth data
+	 *
+	 * @param	$passHash	The hashed password from request we shall set
+	 * @return	void
+	 */
+	public function setPasswordAuth ($passHash) {
+		$this->responseInstance->addCookie('u_hash', $passHash, true);
+	}
+}
+
+// [EOF]
+?>
diff --git a/inc/classes/main/response/class_HttpResponse.php b/inc/classes/main/response/class_HttpResponse.php
index 074373d..bc76933 100644
--- a/inc/classes/main/response/class_HttpResponse.php
+++ b/inc/classes/main/response/class_HttpResponse.php
@@ -136,7 +136,7 @@ class HttpResponse extends BaseFrameworkSystem implements Responseable {
 	 * @param	$output		Output we shall sent in the HTTP response
 	 * @return	void
 	 */
-	public function setReponseBody ($output) {
+	public function setResponseBody ($output) {
 		$this->responseBody = $output;
 	}
 
@@ -187,7 +187,7 @@ class HttpResponse extends BaseFrameworkSystem implements Responseable {
 		}
 
 		// Clear response header and body
-		$this->setReponseBody("");
+		$this->setResponseBody("");
 		$this->resetResponseHeaders();
 	}
 
@@ -221,6 +221,84 @@ class HttpResponse extends BaseFrameworkSystem implements Responseable {
 		// Adds the resolved message id to the fatal message list
 		$this->fatalMessages[] = $this->getApplicationInstance()->getLanguageInstance()->getMessage($messageId);
 	}
+
+	/**
+	 * Adds a cookie to the response
+	 *
+	 * @param	$cookieName		Cookie's name
+	 * @param	$cookieValue	Value to store in the cookie
+	 * @param	$encrypted		Do some extra encryption on the value
+	 * @return	void
+	 * @throws	ResponseHeadersAlreadySentException		If headers are already sent
+	 */
+	public function addCookie ($cookieName, $cookieValue, $encrypted = false) {
+		// Are headers already sent?
+		if (headers_sent()) {
+			// Throw an exception here
+			throw new ResponseHeadersAlreadySentException($this, self::EXCEPTION_HEADERS_ALREADY_SENT);
+		} // END - if
+
+		// Shall we encrypt the cookie?
+		if ($encrypted === true) {
+			// @TODO Encryption of cookie data not yet supported
+		} // END - if
+
+		// Set the cookie
+		setcookie(
+			$cookieName,
+			$cookieValue,
+			$this->getConfigInstance()->readConfig('cookie_expire'),
+			$this->getConfigInstance()->readConfig('cookie_path'),
+			$this->getConfigInstance()->readConfig('cookie_domain'),
+			$this->getConfigInstance()->readConfig('cookie_ssl')
+		);
+	}
+
+	/**
+	 * Redirect to a configured URL. The URL can be absolute or relative. In
+	 * case of relative URL it will be extended automatically.
+	 *
+	 * @param	$configEntry	The configuration entry which holds our URL
+	 * @return	void
+	 * @throws	ResponseHeadersAlreadySentException		If headers are already sent
+	 */
+	public function redirectToConfiguredUrl ($configEntry) {
+		// Is the header not yet sent?
+		if (headers_sent()) {
+			// Throw an exception here
+			throw new ResponseHeadersAlreadySentException($this, self::EXCEPTION_HEADERS_ALREADY_SENT);
+		} // END - if
+
+		// Get the url from config
+		$url = $this->getConfigInstance()->readConfig($configEntry);
+
+		// Do we have a "http" in front of the URL?
+		if (substr(strtolower($url), 0, 4) != "http") {
+			// Is there a / in front of the relative URL?
+			if (substr($url, 0, 1) == "/") $url = substr($url, 1);
+
+			// No, then extend it with our base URL
+			$url = $this->getConfigInstance()->readConfig('base_url') . "/" . $url;
+		} // END - if
+
+		// Clean response headers
+		$this->resetResponseHeaders();
+
+		// Add redirect header
+		$this->addHeader("Location", $url);
+
+		// Set correct response status
+		$this->setResponseStatus("301 Moved Permanently");
+
+		// Clear the body
+		$this->setResponseBody("");
+
+		// Flush the result
+		$this->flushBuffer();
+
+		// All done here...
+		exit();
+	}
 }
 
 // [EOF]
diff --git a/inc/config.php b/inc/config.php
index 0b450d8..817609c 100644
--- a/inc/config.php
+++ b/inc/config.php
@@ -35,6 +35,9 @@ $cfg = FrameworkConfiguration::createFrameworkConfiguration();
 // CFG: SERVER-PATH
 $cfg->definePath(dirname(dirname(__FILE__)) . '/'); // DON'T MISS THE TRAILING SLASH!!!
 
+// CFG: BASE-URL
+$cfg->setConfigEntry('base_url', $cfg->detectBaseUrl());
+
 // CFG: DATABASE-TYPE
 $cfg->defineDatabaseType('local');
 
@@ -218,5 +221,17 @@ $cfg->setConfigEntry('post_registration_action', "LoginAfterRegistrationAction")
 // CFG: USER-CLASS
 $cfg->setConfigEntry('user_class', "User");
 
+// CFG: COOKIE-EXPIRE
+$cfg->setConfigEntry('cookie_expire', 60*60*24*2); // Two hours!
+
+// CFG: COOKIE-PATH
+$cfg->setConfigEntry('cookie_path', dirname($_SERVER['SCRIPT_NAME']) . "/");
+
+// CFG: COOKIE-DOMAIN
+$cfg->setConfigEntry('cookie_domain', $cfg->readConfig('base_url')); // Is mostly the same...
+
+// CFG: COOKIE-SSL
+$cfg->setConfigEntry('cookie_ssl', (isset($_SERVER['HTTPS'])));
+
 // [EOF]
 ?>
diff --git a/inc/config/class_FrameworkConfiguration.php b/inc/config/class_FrameworkConfiguration.php
index 6bfc3a2..5e9b87e 100644
--- a/inc/config/class_FrameworkConfiguration.php
+++ b/inc/config/class_FrameworkConfiguration.php
@@ -169,7 +169,7 @@ class FrameworkConfiguration implements Registerable {
 	/**
 	 * Define the local file path
 	 *
-	 * @param		$path	The database type. See path inc/database/.
+	 * @param		$path	Local file path for include files.
 	 * @return	void
 	 */
 	public function definePath ($path) {
@@ -261,6 +261,28 @@ class FrameworkConfiguration implements Registerable {
 	public function __toString () {
 		return get_class($this);
 	}
+
+	/**
+	 * Dectect and return the base URL for all URLs and forms
+	 *
+	 * @return	$baseUrl	Detected base URL
+	 */
+	public function detectBaseUrl() {
+		// Initialize the URL
+		$baseUrl = "http";
+
+		// Do we have HTTPS?
+		if (isset($_SERVER['HTTPS'])) {
+			// Add the >s< for HTTPS
+			$baseUrl .= "s";
+		} // END - if
+
+		// Construct the full URL now and secure it against CSRF attacks
+		$baseUrl = $baseUrl . "://" . htmlentities(strip_tags($_SERVER['SERVER_NAME']), ENT_QUOTES) . dirname($_SERVER['SCRIPT_NAME']);
+
+		// Return the URL
+		return $baseUrl;
+	}
 } // END - class
 
 // [EOF]
diff --git a/templates/de/code/emergency_exit.ctp b/templates/de/code/emergency_exit.ctp
index d5c6f90..29725af 100644
--- a/templates/de/code/emergency_exit.ctp
+++ b/templates/de/code/emergency_exit.ctp
@@ -17,4 +17,13 @@
 		</div>
 	</div>
 
+	<div id="stats_box">
+		<div id="stats_header">
+			Statistics
+		</div>
+		<div id="stats_objects">
+			Total objects: $content[total_objects]
+		</div>
+	</div>
+
 {?footer_msg:footer_msg="Please contact the support and supply the full above message, if you think you are not qualified to fix this problem."?}
-- 
2.39.5