From 64c177abc79f904bc0e0cf78f83a939dfa5ef9e4 Mon Sep 17 00:00:00 2001 From: Roland Haeder Date: Sat, 12 Sep 2015 21:11:08 +0200 Subject: [PATCH] Continued: - moved some files/methods ("generic" enough to be here) from 'hub'. - continued with file-based socket listener to allow listening to sockets ... MIME-Version: 1.0 Content-Type: text/plain; charset=utf8 Content-Transfer-Encoding: 8bit Signed-off-by: Roland Häder --- inc/config.php | 3 + .../classes/class_BaseFrameworkSystem.php | 104 +++++++-- .../raw_data/class_BaseDataHandler.php | 197 ++++++++++++++++ .../handler/raw_data/network/.htaccess | 1 + .../classes/handler/raw_data/network/class_ | 68 ++++++ .../network/class_BaseRawDataHandler.php | 183 +++++++++++++++ .../classes/listener/class_BaseListener.php | 213 ++++++++++++++++++ .../socket/class_SocketFileListener.php | 130 ++++++++++- .../console/class_DebugConsoleOutput.php | 2 +- .../socket/class_SocketBindingException.php | 47 ++++ .../index/stack/class_IndexableStack.ph | 9 + 11 files changed, 933 insertions(+), 24 deletions(-) create mode 100644 inc/main/classes/handler/raw_data/class_BaseDataHandler.php create mode 100644 inc/main/classes/handler/raw_data/network/.htaccess create mode 100644 inc/main/classes/handler/raw_data/network/class_ create mode 100644 inc/main/classes/handler/raw_data/network/class_BaseRawDataHandler.php create mode 100644 inc/main/exceptions/socket/class_SocketBindingException.php create mode 100644 inc/main/interfaces/index/stack/class_IndexableStack.ph diff --git a/inc/config.php b/inc/config.php index bc6f8e1a..7d263be3 100644 --- a/inc/config.php +++ b/inc/config.php @@ -458,5 +458,8 @@ $cfg->setConfigEntry('menu_template_extension', '.xml'); // CFG: FEATURE-FUSE-CLASS $cfg->setConfigEntry('feature_fuse_class', 'FuseFeature'); +// CFG: TEMP-FILE-PATH +$cfg->setConfigEntry('temp_file_path', sys_get_temp_dir()); + // [EOF] ?> diff --git a/inc/main/classes/class_BaseFrameworkSystem.php b/inc/main/classes/class_BaseFrameworkSystem.php index 7bee5eca..74d92235 100644 --- a/inc/main/classes/class_BaseFrameworkSystem.php +++ b/inc/main/classes/class_BaseFrameworkSystem.php @@ -495,44 +495,52 @@ class BaseFrameworkSystem extends stdClass implements FrameworkInterface { if (is_null($args)) { // No arguments $argsString = 'NULL'; - } elseif (empty($args)) { - // Empty arguments - $argsString = '(empty)'; } elseif (is_array($args)) { + // Start braces + $argsString = '('; + // Some arguments are there foreach ($args as $arg) { - // Add the value itself if not array. This prevents 'array to string conversion' message - if (is_array($arg)) { - $argsString .= 'Array'; - } else { - $argsString .= $arg; - } - // Add data about the argument - $argsString .= ' (' . gettype($arg); + $argsString .= gettype($arg) . ':'; - if (is_string($arg)) { + if (is_null($arg)) { + // Found a NULL argument + $argsString .= 'NULL'; + } elseif (is_string($arg)) { // Add length for strings - $argsString .= ', ' . strlen($arg); + $argsString .= strlen($arg); + } elseif ((is_int($arg)) || (is_float($arg))) { + // ... integer/float + $argsString .= $arg; } elseif (is_array($arg)) { // .. or size if array - $argsString .= ', ' . count($arg); + $argsString .= count($arg); + } elseif (is_object($arg)) { + // Get reflection + $reflection = new ReflectionClass($arg); + + // Is an other object, maybe no __toString() available + $argsString .= $reflection->getName(); } elseif ($arg === TRUE) { // ... is boolean 'TRUE' - $argsString .= ', TRUE'; + $argsString .= 'TRUE'; } elseif ($arg === FALSE) { // ... is boolean 'FALSE' - $argsString .= ', FALSE'; + $argsString .= 'FALSE'; } - // Closing bracket - $argsString .= '), '; + // Comma for next one + $argsString .= ', '; } // END - foreach // Remove last comma if (substr($argsString, -2, 1) == ',') { $argsString = substr($argsString, 0, -2); } // END - if + + // Close braces + $argsString .= ')'; } else { // Invalid arguments! $argsString = '!INVALID:' . gettype($args) . '!'; @@ -1853,7 +1861,7 @@ class BaseFrameworkSystem extends stdClass implements FrameworkInterface { // Sorry, there is no other way getting this nice backtrace if (!empty($message)) { // Output message - printf('Message: %s
' . chr(10), $message); + printf('Message: %s
' . PHP_EOL, $message); } // END - if print('
');
@@ -2026,7 +2034,7 @@ class BaseFrameworkSystem extends stdClass implements FrameworkInterface {
 		// Add line number to the code
 		foreach (explode(chr(10), $phpCode) as $lineNo => $code) {
 			// Add line numbers
-			$markedCode .= sprintf('%s: %s' . chr(10),
+			$markedCode .= sprintf('%s: %s' . PHP_EOL,
 				($lineNo + 1),
 				htmlentities($code, ENT_QUOTES)
 			);
@@ -3331,7 +3339,7 @@ class BaseFrameworkSystem extends stdClass implements FrameworkInterface {
 	 * @param	$filePathName	Name of the file/path to be checked
 	 * @return	$isReachable	Whether it is within open_basedir()
 	 */
-	public static function isReachableFilePath ($filePathName) {
+	protected static function isReachableFilePath ($filePathName) {
 		// Is not reachable by default
 		$isReachable = FALSE;
 
@@ -3374,6 +3382,60 @@ class BaseFrameworkSystem extends stdClass implements FrameworkInterface {
 		// Return status
 		return $isReadable;
 	}
+
+	/**
+	 * Creates a full-qualified file name (FQFN) for given file name by adding
+	 * a configured temporary file path to it.
+	 *
+	 * @param	$fileName	Name for temporary file
+	 * @return	$fqfn	Full-qualified file name
+	 * @throw	PathWriteProtectedException If the path in 'temp_file_path' is write-protected
+	 * @throws	FileIoException If the file cannot be written
+	 */
+	 protected static function createTempPathForFile ($fileName) {
+		// Get config entry
+		$basePath = FrameworkConfiguration::getSelfInstance()->getConfigEntry('temp_file_path');
+
+		// Is the path writeable?
+		if (!is_writable($basePath)) {
+			// Path is write-protected
+			throw new PathWriteProtectedException($fileName, self::EXCEPTION_PATH_CANNOT_BE_WRITTEN);
+		} // END - if
+
+		// Add it
+		$fqfn = $basePath . '/' . $fileName;
+
+		// Is it reachable?
+		if (!self::isReachableFilePath($fqfn)) {
+			// Not reachable
+			throw new FileIoException($fqfn, self::EXCEPTION_FILE_NOT_REACHABLE);
+		} // END - if
+
+		// Return it
+		return $fqfn;
+	 }
+
+	/**
+	 * "Getter" for a printable state name
+	 *
+	 * @return	$stateName	Name of the node's state in a printable format
+	 */
+	public final function getPrintableState () {
+		// Default is 'null'
+		$stateName = 'null';
+
+		// Get the state instance
+		$stateInstance = $this->getStateInstance();
+
+		// Is it an instance of Stateable?
+		if ($stateInstance instanceof Stateable) {
+			// Then use that state name
+			$stateName = $stateInstance->getStateName();
+		} // END - if
+
+		// Return result
+		return $stateName;
+	}
 }
 
 // [EOF]
diff --git a/inc/main/classes/handler/raw_data/class_BaseDataHandler.php b/inc/main/classes/handler/raw_data/class_BaseDataHandler.php
new file mode 100644
index 00000000..b80f1ef7
--- /dev/null
+++ b/inc/main/classes/handler/raw_data/class_BaseDataHandler.php
@@ -0,0 +1,197 @@
+
+ * @version		0.0.0
+ * @copyright	Copyright (c) 2007, 2008 Roland Haeder, 2009 - 2015 Core Developer Team
+ * @license		GNU GPL 3.0 or any newer version
+ * @link		http://www.shipsimu.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 .
+ */
+abstract class BaseDataHandler extends BaseHandler {
+	/**
+	 * Last exception instance from database layer or NULL (default)
+	 */
+	private $lastException = NULL;
+
+	/**
+	 * Array with search criteria elements
+	 */
+	protected $searchData = array();
+
+	/**
+	 * Array with all data XML nodes (which hold the actual data) and their values
+	 */
+	protected $messageDataElements = array();
+
+	/**
+	 * Array for translating message data elements (other node's data mostly)
+	 * into configuration elements.
+	 */
+	protected $messageToConfig = array();
+
+	/**
+	 * Array for copying configuration entries
+	 */
+	protected $configCopy = array();
+
+	/**
+	 * Protected constructor
+	 *
+	 * @param	$className	Name of the class
+	 * @return	void
+	 */
+	protected function __construct ($className) {
+		// Call parent constructor
+		parent::__construct($className);
+
+		// Get a DHT instance
+		$dhtInstance = DhtObjectFactory::createDhtInstance('node');
+
+		// Set it here
+		$this->setDhtInstance($dhtInstance);
+	}
+
+	/**
+	 * Getter for search data array
+	 *
+	 * @return	$searchData		Search data array
+	 */
+	public final function getSearchData () {
+		return $this->searchData;
+	}
+
+	/**
+	 * Getter for last exception
+	 *
+	 * @return	$lastException	Last thrown exception
+	 */
+	public final function getLastException () {
+		return $this->lastException;
+	}
+
+	/**
+	 * Setter for last exception
+	 *
+	 * @param	$lastException	Last thrown exception
+	 * @return	void
+	 */
+	public final function setLastException (FrameworkException $exceptionInstance = NULL) {
+		$this->lastException = $exceptionInstance;
+	}
+
+	/**
+	 * Prepares a message as answer for given message data for delivery.
+	 *
+	 * @param	$messageData		An array with all message data
+	 * @param	$packageInstance	An instance of a Deliverable instance
+	 * @return	void
+	 */
+	protected function prepareAnswerMessage (array $messageData, Deliverable $packageInstance) {
+		// Debug message
+		/* NOISY-DEBUG: */ self::createDebugInstance(__CLASS__)->debugOutput('MESSAGE-HANDLER[' . __METHOD__ . ':' . __LINE__ . ']: Going to send an answer message for ' . $this->getHandlerName() . ' ...');
+
+		// Get a helper instance based on this handler's name
+		$helperInstance = ObjectFactory::createObjectByConfiguredName('node_answer_' . $this->getHandlerName() . '_helper_class', array($messageData));
+
+		// Get node instance
+		$nodeInstance = NodeObjectFactory::createNodeInstance();
+
+		// Load descriptor XML
+		$helperInstance->loadDescriptorXml($nodeInstance);
+
+		/*
+		 * Set missing (temporary) configuration data, mostly it needs to be
+		 * copied from message data array.
+		 */
+		$this->initMessageConfigurationData($messageData);
+
+		// Compile any configuration variables
+		$helperInstance->getTemplateInstance()->compileConfigInVariables();
+
+		// Deliver the package
+		$helperInstance->sendPackage($nodeInstance);
+
+		/*
+		 * Remove temporary configuration
+		 */
+		$this->removeMessageConfigurationData($messageData);
+
+		// Debug message
+		/* NOISY-DEBUG: */ self::createDebugInstance(__CLASS__)->debugOutput('MESSAGE-HANDLER[' . __METHOD__ . ':' . __LINE__ . ']: Answer message has been prepared.');
+	}
+
+	/**
+	 * Prepares the next message
+	 *
+	 * @param	$messageData		An array with all message data
+	 * @param	$packageInstance	An instance of a Deliverable instance
+	 * @return	void
+	 */
+	protected function prepareNextMessage (array $messageData, Deliverable $packageInstance) {
+		// Debug message
+		/* NOISY-DEBUG: */ self::createDebugInstance(__CLASS__)->debugOutput('MESSAGE-HANDLER[' . __METHOD__ . ':' . __LINE__ . ']: Going to send next message ...');
+
+		// Get a helper instance based on this handler's name
+		$helperInstance = ObjectFactory::createObjectByConfiguredName('node_next_' . $this->getHandlerName() . '_helper_class', array($messageData));
+
+		// Get node instance
+		$nodeInstance = NodeObjectFactory::createNodeInstance();
+
+		// Load descriptor XML
+		$helperInstance->loadDescriptorXml($nodeInstance);
+
+		/*
+		 * Set missing (temporary) configuration data, mostly it needs to be
+		 * copied from message data array.
+		 */
+		$this->initMessageConfigurationData($messageData);
+
+		// Compile any configuration variables
+		$helperInstance->getTemplateInstance()->compileConfigInVariables();
+
+		// Deliver the package
+		$helperInstance->sendPackage($nodeInstance);
+
+		/*
+		 * Remove temporary configuration
+		 */
+		$this->removeMessageConfigurationData($messageData);
+
+		// Debug message
+		/* NOISY-DEBUG: */ self::createDebugInstance(__CLASS__)->debugOutput('MESSAGE-HANDLER[' . __METHOD__ . ':' . __LINE__ . ']: Next message has been prepared.');
+	}
+
+	/**
+	 * Initializes configuration data from given message data array
+	 *
+	 * @param	$messageData	An array with all message data
+	 * @return	void
+	 */
+	abstract protected function initMessageConfigurationData (array $messageData);
+
+	/**
+	 * Removes configuration data with given message data array from global
+	 * configuration
+	 *
+	 * @param	$messageData	An array with all message data
+	 * @return	void
+	 */
+	abstract protected function removeMessageConfigurationData (array $messageData);
+}
+
+// [EOF]
+?>
diff --git a/inc/main/classes/handler/raw_data/network/.htaccess b/inc/main/classes/handler/raw_data/network/.htaccess
new file mode 100644
index 00000000..3a428827
--- /dev/null
+++ b/inc/main/classes/handler/raw_data/network/.htaccess
@@ -0,0 +1 @@
+Deny from all
diff --git a/inc/main/classes/handler/raw_data/network/class_ b/inc/main/classes/handler/raw_data/network/class_
new file mode 100644
index 00000000..b452a2ba
--- /dev/null
+++ b/inc/main/classes/handler/raw_data/network/class_
@@ -0,0 +1,68 @@
+
+ * @version		0.0.0
+ * @copyright	Copyright (c) 2007, 2008 Roland Haeder, 2009 - 2015 Hub Developer Team
+ * @license		GNU GPL 3.0 or any newer version
+ * @link		http://www.shipsimu.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 .
+ */
+class ???RawDataHandler extends BaseRawDataHandler implements Networkable {
+	/**
+	 * Last socket error (default: Success)
+	 */
+	private $lastSocketError = 0;
+
+	/**
+	 * Protected constructor
+	 *
+	 * @return	void
+	 */
+	protected function __construct () {
+		// Call parent constructor
+		parent::__construct(__CLASS__);
+
+		// Set handler name
+		$this->setHandlerName('|||');
+	}
+
+	/**
+	 * Creates an instance of this class
+	 *
+	 * @return	$handlerInstance	An instance of a Networkable class
+	 */
+	public static final function create???RawDataHandler () {
+		// Get new instance
+		$handlerInstance = new ???RawDataHandler();
+
+		// Return the prepared instance
+		return $handlerInstance;
+	}
+
+	/**
+	 * Processes raw data from given resource. This is mostly useful for TCP
+	 * package handling and is implemented in the ???Listener class
+	 *
+	 * @param	$resource	A valid socket resource array
+	 * @return	void
+	 */
+	public function processRawDataFromResource (array $socketArray) {
+	}
+}
+
+// [EOF]
+?>
diff --git a/inc/main/classes/handler/raw_data/network/class_BaseRawDataHandler.php b/inc/main/classes/handler/raw_data/network/class_BaseRawDataHandler.php
new file mode 100644
index 00000000..4a6c3c63
--- /dev/null
+++ b/inc/main/classes/handler/raw_data/network/class_BaseRawDataHandler.php
@@ -0,0 +1,183 @@
+
+ * @version		0.0.0
+ * @copyright	Copyright (c) 2007, 2008 Roland Haeder, 2009 - 2015 Hub Developer Team
+ * @license		GNU GPL 3.0 or any newer version
+ * @link		http://www.shipsimu.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 .
+ */
+class BaseRawDataHandler extends BaseHandler {
+	// Error codes:
+	// - Socket raw data stream errors
+	const SOCKET_ERROR_UNKNOWN                    = 'unknown_error';              // Unknown error (should not happen)
+	const SOCKET_ERROR_TRANSPORT_ENDPOINT         = 'transport_endpoint';         // Transport endpoint has closed
+	const SOCKET_ERROR_INVALID_BASE64_MODULO      = 'base64_modulo';              // Length is not modulo 4
+	const SOCKET_ERROR_INVALID_BASE64_MESSAGE     = 'base64_message';             // Raw data is not Base64-encoded
+	const SOCKET_ERROR_UNHANDLED                  = 'unhandled_package';          // Unhandled raw data (not bad)
+	const SOCKET_ERROR_CONNECTION_REFUSED         = 'connection_refused';         // The name says it: connection refused
+	const SOCKET_ERROR_CONNECTION_TIMED_OUT       = 'connection_timed_out';       // The name says it: connection attempt has timed-out
+	const SOCKET_ERROR_OPERATION_IN_PROGRESS      = 'operation_in_progress';      // 'Operation now in progress'
+	const SOCKET_ERROR_OPERATION_ALREADY_PROGRESS = 'operation_already_progress'; // 'Operation already in progress'
+	const SOCKET_ERROR_RESOURCE_UNAVAILABLE       = 'resource_unavailable';       // 'Resource temporary unavailable'
+	const SOCKET_ERROR_NO_ROUTE_TO_HOST           = 'no_route_to_host';           // The name says it: no route to host
+	const SOCKET_ERROR_CONNECTION_RESET_BY_PEER   = 'connection_reset_by_peer';   // Connection reset by peer
+	const SOCKET_ERROR_BROKEN_PIPE                = 'broken_pipe';                // Broken pipe
+	const SOCKET_ERROR_PERMISSION_DENIED          = 'permission_denied';          // Permission denied
+	const SOCKET_CONNECTED                        = 'connected';                  // Nothing errorous happens, socket is connected
+
+	// - Package errors
+	const PACKAGE_ERROR_INVALID_DATA       = 'invalid_data';    // Invalid data in package found
+	const PACKAGE_ERROR_INCOMPLETE_DATA    = 'incomplete_data'; // Incomplete data sent (e.g. field is missing)
+	const PACKAGE_ERROR_INVALID_CONTENT    = 'invalid_content'; // Content is invalid (e.g. not well-formed)
+	const PACKAGE_ERROR_RECIPIENT_MISMATCH = 'recipient_error'; // Recipient is not us
+	const PACKAGE_LEVEL_CHECK_OKAY         = 'checked_package'; // Package is fine
+
+	// Package data
+	const PACKAGE_RAW_DATA   = 'raw_data';
+	const PACKAGE_ERROR_CODE = 'error_code';
+
+	// Start/end marker
+	const STREAM_START_MARKER = '[[S]]';
+	const STREAM_END_MARKER   = '[[E]]';
+
+	/**
+	 * Stacker for raw data
+	 */
+	const STACKER_NAME_RAW_DATA = 'raw_data';
+
+	/**
+	 * Error code from socket
+	 */
+	private $errorCode = -1;
+
+	/**
+	 * Protected constructor
+	 *
+	 * @param	$className	Name of the class
+	 * @return	void
+	 */
+	protected function __construct ($className) {
+		// Call parent constructor
+		parent::__construct($className);
+
+		// Set error code to 'unknown'
+		$this->setErrorCode(self::SOCKET_ERROR_UNKNOWN);
+
+		// Init stacker instance for processed raw data
+		$stackInstance = ObjectFactory::createObjectByConfiguredName('node_raw_data_stacker_class');
+
+		// Remember this in this package handler
+		$this->setStackInstance($stackInstance);
+
+		// Init stacker
+		$this->initStack();
+	}
+
+	/**
+	 * Initializes the stacker for raw data
+	 *
+	 * @return	void
+	 */
+	protected function initStack () {
+		$this->getStackInstance()->initStack(self::STACKER_NAME_RAW_DATA);
+	}
+
+	/**
+	 * Adds given raw data to the raw data stacker
+	 *
+	 * @param	$rawData	raw data from the socket resource
+	 * @return	void
+	 */
+	protected function addRawDataToStacker ($rawData) {
+		/*
+		 * Add the deocoded data and error code to the stacker so other classes
+		 * (e.g. NetworkPackage) can "pop" it from the stacker.
+		 */
+		$this->getStackInstance()->pushNamed(self::STACKER_NAME_RAW_DATA, array(
+			self::PACKAGE_RAW_DATA   => $rawData,
+			self::PACKAGE_ERROR_CODE => $this->getErrorCode()
+		));
+	}
+
+	/**
+	 * Checks whether raw data is pending for further processing.
+	 *
+	 * @return	$isPending	Whether raw data is pending
+	 */
+	public function isRawDataPending () {
+		// Does the stacker have some entries (not empty)?
+		$isPending = (!$this->getStackInstance()->isStackEmpty(self::STACKER_NAME_RAW_DATA));
+
+		// Return it
+		return $isPending;
+	}
+
+	/**
+	 * "Getter" for next raw data from the stacker
+	 *
+	 * @return	$rawData	Raw data from the stacker
+	 */
+	public function getNextRawData () {
+		// "Pop" the raw data from the stacker
+		$rawData = $this->getStackInstance()->popNamed(self::STACKER_NAME_RAW_DATA);
+
+		// And return it
+		return $rawData;
+	}
+
+	/**
+	 * Checks whether the 'recipient' field matches our own an universal node
+	 * locator.
+	 *
+	 * @param	$packageData	Raw package data
+	 * @return	$matches		Whether it matches
+	 * @todo	This method will be moved to a better place
+	 */
+	protected function ifRecipientMatchesOwnUniversalNodeLocator (array $packageData) {
+		// Construct own address first
+		$ownAddress = NodeObjectFactory::createNodeInstance()->determineUniversalNodeLocator();
+
+		// Does it match?
+		$matches = ($ownAddress === $packageData[NetworkPackage::PACKAGE_DATA_RECIPIENT]);
+
+		// Return result
+		return $matches;
+	}
+
+	/**
+	 * Setter for error code
+	 *
+	 * @param	$errorCode	The error code we shall set
+	 * @return	void
+	 */
+	public final function setErrorCode ($errorCode) {
+		$this->errorCode = $errorCode;
+	}
+
+	/**
+	 * Getter for error code
+	 *
+	 * @return	$errorCode	The error code
+	 */
+	public final function getErrorCode () {
+		return $this->errorCode;
+	}
+}
+
+// [EOF]
+?>
diff --git a/inc/main/classes/listener/class_BaseListener.php b/inc/main/classes/listener/class_BaseListener.php
index 5a572135..bd807bd7 100644
--- a/inc/main/classes/listener/class_BaseListener.php
+++ b/inc/main/classes/listener/class_BaseListener.php
@@ -286,6 +286,219 @@ class BaseListener extends BaseFrameworkSystem implements Visitable {
 	public function monitorIncomingRawData () {
 		throw new UnsupportedOperationException(array($this, __FUNCTION__), self::EXCEPTION_UNSPPORTED_OPERATION);
 	}
+
+	/**
+	 * Constructs a callable method name from given socket error code. If the
+	 * method is not found, a generic one is used.
+	 *
+	 * @param	$errorCode		Error code from socket_last_error()
+	 * @return	$handlerName	Call-back method name for the error handler
+	 * @throws	UnsupportedSocketErrorHandlerException If the error handler is not implemented
+	 */
+	protected function getSocketErrorHandlerFromCode ($errorCode) {
+		// Create a name from translated error code
+		$handlerName = 'socketError' . self::convertToClassName($this->translateSocketErrorCodeToName($errorCode)) . 'Handler';
+
+		// Is the call-back method there?
+		if (!method_exists($this, $handlerName)) {
+			// Please implement this
+			throw new UnsupportedSocketErrorHandlerException(array($this, $handlerName, $errorCode), BaseConnectionHelper::EXCEPTION_UNSUPPORTED_ERROR_HANDLER);
+		} // END - if
+
+		// Return it
+		return $handlerName;
+	}
+
+	/**
+	 * Handles socket error for given socket resource and peer data. This method
+	 * validates $socketResource if it is a valid resource (see is_resource())
+	 * but assumes valid data in array $recipientData, except that
+	 * count($recipientData) is always 2.
+	 *
+	 * @param	$method				Value of __METHOD__ from calling method
+	 * @param	$line				Value of __LINE__ from calling method
+	 * @param	$socketResource		A valid socket resource
+	 * @param	$socketData			A valid socket data array (0 = IP/file name, 1 = port)
+	 * @return	void
+	 * @throws	InvalidSocketException	If $socketResource is no socket resource
+	 * @throws	NoSocketErrorDetectedException	If socket_last_error() gives zero back
+	 */
+	protected final function handleSocketError ($method, $line, $socketResource, array $socketData) {
+		// This method handles only socket resources
+		if (!is_resource($socketResource)) {
+			// No resource, abort here
+			throw new InvalidSocketException(array($this, $socketResource), BaseListener::EXCEPTION_INVALID_SOCKET);
+		} // END - if
+
+		// Check socket array, 1st element is mostly IP address (or file name), 2nd is port number
+		//* DEBUG-DIE: */ die(__METHOD__ . ':socketData=' . print_r($socketData, TRUE));
+		assert(isset($socketData[0]));
+		assert(isset($socketData[1]));
+
+		// Get error code for first validation (0 is not an error)
+		$errorCode = socket_last_error($socketResource);
+
+		// If the error code is zero, someone called this method without an error
+		if ($errorCode == 0) {
+			// No error detected (or previously cleared outside this method)
+			throw new NoSocketErrorDetectedException(array($this, $socketResource), BaseListener::EXCEPTION_NO_SOCKET_ERROR);
+		} // END - if
+
+		// Get handler (method) name
+		$handlerName = $this->getSocketErrorHandlerFromCode($errorCode);
+
+		// Call-back the error handler method
+		call_user_func_array(array($this, $handlerName), array($socketResource, $socketData));
+
+		// Finally clear the error because it has been handled
+		socket_clear_error($socketResource);
+	}
+
+	/**
+	 * Translates socket error codes into our own internal names which can be
+	 * used for call-backs.
+	 *
+	 * @param	$errorCode	The error code from socket_last_error() to be translated
+	 * @return	$errorName	The translated name (all lower-case, with underlines)
+	 */
+	public function translateSocketErrorCodeToName ($errorCode) {
+		// Nothing bad happened by default
+		$errorName = BaseRawDataHandler::SOCKET_CONNECTED;
+
+		// Is the code a number, then we have to change it
+		switch ($errorCode) {
+			case 0: // Silently ignored, the socket is connected
+				break;
+
+			case 11:  // "Resource temporary unavailable"
+				$errorName = BaseRawDataHandler::SOCKET_ERROR_RESOURCE_UNAVAILABLE;
+				break;
+
+			case 13:  // "Permission denied"
+				$errorName = BaseRawDataHandler::SOCKET_ERROR_PERMISSION_DENIED;
+				break;
+
+			case 32:  // "Broken pipe"
+				$errorName = BaseRawDataHandler::SOCKET_ERROR_BROKEN_PIPE;
+				break;
+
+			case 104: // "Connection reset by peer"
+				$errorName = BaseRawDataHandler::SOCKET_ERROR_CONNECTION_RESET_BY_PEER;
+				break;
+
+			case 107: // "Transport end-point not connected"
+			case 134: // On some (?) systems for 'transport end-point not connected'
+				// @TODO On some systems it is 134, on some 107?
+				$errorName = BaseRawDataHandler::SOCKET_ERROR_TRANSPORT_ENDPOINT;
+				break;
+
+			case 110: // "Connection timed out"
+				$errorName = BaseRawDataHandler::SOCKET_ERROR_CONNECTION_TIMED_OUT;
+				break;
+
+			case 111: // "Connection refused"
+				$errorName = BaseRawDataHandler::SOCKET_ERROR_CONNECTION_REFUSED;
+				break;
+
+			case 113: // "No route to host"
+				$errorName = BaseRawDataHandler::SOCKET_ERROR_NO_ROUTE_TO_HOST;
+				break;
+
+			case 114: // "Operation already in progress"
+				$errorName = BaseRawDataHandler::SOCKET_ERROR_OPERATION_ALREADY_PROGRESS;
+				break;
+
+			case 115: // "Operation now in progress"
+				$errorName = BaseRawDataHandler::SOCKET_ERROR_OPERATION_IN_PROGRESS;
+				break;
+
+			default: // Everything else <> 0
+				// Unhandled error code detected, so first debug it because we may want to handle it like the others
+				self::createDebugInstance(__CLASS__)->debugOutput('BASE-HUB[' . __METHOD__ . ':' . __LINE__ . '] UNKNOWN ERROR CODE = ' . $errorCode . ', MESSAGE = ' . socket_strerror($errorCode));
+
+				// Change it only in this class
+				$errorName = BaseRawDataHandler::SOCKET_ERROR_UNKNOWN;
+				break;
+		}
+
+		// Return translated name
+		return $errorName;
+	}
+
+	/**
+	 * Shuts down a given socket resource. This method does only ease calling
+	 * the right visitor.
+	 *
+	 * @param	$socketResource		A valid socket resource
+	 * @return	void
+	 */
+	public function shutdownSocket ($socketResource) {
+		// Debug message
+		self::createDebugInstance(__CLASS__)->debugOutput('HUB-SYSTEM: Shutting down socket resource ' . $socketResource . ' with state ' . $this->getPrintableState() . ' ...');
+
+		// Set socket resource
+		$this->setSocketResource($socketResource);
+
+		// Get a visitor instance
+		$visitorInstance = ObjectFactory::createObjectByConfiguredName('shutdown_socket_visitor_class');
+
+		// Debug output
+		self::createDebugInstance(__CLASS__)->debugOutput('HUB-SYSTEM:' . $this->__toString() . ': visitorInstance=' . $visitorInstance->__toString());
+
+		// Call the visitor
+		$this->accept($visitorInstance);
+	}
+
+	/**
+	 * Half-shuts down a given socket resource. This method does only ease calling
+	 * an other visitor than shutdownSocket() does.
+	 *
+	 * @param	$socketResource		A valid socket resource
+	 * @return	void
+	 */
+	public function halfShutdownSocket ($socketResource) {
+		// Debug message
+		self::createDebugInstance(__CLASS__)->debugOutput('HUB-SYSTEM: Half-shutting down socket resource ' . $socketResource . ' with state ' . $this->getPrintableState() . ' ...');
+
+		// Set socket resource
+		$this->setSocketResource($socketResource);
+
+		// Get a visitor instance
+		$visitorInstance = ObjectFactory::createObjectByConfiguredName('half_shutdown_socket_visitor_class');
+
+		// Debug output
+		self::createDebugInstance(__CLASS__)->debugOutput('HUB-SYSTEM:' . $this->__toString() . ': visitorInstance=' . $visitorInstance->__toString());
+
+		// Call the visitor
+		$this->accept($visitorInstance);
+	}
+
+	// ************************************************************************
+	//                 Socket error handler call-back methods
+	// ************************************************************************
+
+	/**
+	 * Handles socket error 'permission denied', but does not clear it for
+	 * later debugging purposes.
+	 *
+	 * @param	$socketResource		A valid socket resource
+	 * @param	$socketData			A valid socket data array (0 = IP/file name, 1 = port)
+	 * @return	void
+	 * @throws	SocketBindingException	The socket could not be bind to
+	 */
+	protected function socketErrorPermissionDeniedHandler ($socketResource, array $socketData) {
+		// Get socket error code for verification
+		$socketError = socket_last_error($socketResource);
+
+		// Get error message
+		$errorMessage = socket_strerror($socketError);
+
+		// Shutdown this socket
+		$this->shutdownSocket($socketResource);
+
+		// Throw it again
+		throw new SocketBindingException(array($this, $socketData, $socketResource, $socketError, $errorMessage), BaseListener::EXCEPTION_INVALID_SOCKET);
+	}
 }
 
 // [EOF]
diff --git a/inc/main/classes/listener/socket/class_SocketFileListener.php b/inc/main/classes/listener/socket/class_SocketFileListener.php
index 4dea353c..35469d8c 100644
--- a/inc/main/classes/listener/socket/class_SocketFileListener.php
+++ b/inc/main/classes/listener/socket/class_SocketFileListener.php
@@ -52,10 +52,136 @@ class SocketFileListener extends BaseListener implements Listenable {
 	 * Initializes the listener by setting up the required socket server
 	 *
 	 * @return	void
-	 * @todo	0% done
 	 */
 	public function initListener() {
-		$this->partialStub('Need to implement this method.');
+		// Init socket
+		$mainSocket = socket_create(AF_UNIX, SOCK_STREAM, 0);
+
+		// Is the socket resource valid?
+		if (!is_resource($mainSocket)) {
+			// Something bad happened
+			throw new InvalidSocketException(array($this, $mainSocket), BaseListener::EXCEPTION_INVALID_SOCKET);
+		} // END - if
+
+		// Get socket error code for verification
+		$socketError = socket_last_error($mainSocket);
+
+		// Check if there was an error else
+		if ($socketError > 0) {
+			// Handle this socket error with a faked recipientData array
+			$this->handleSocketError(__METHOD__, __LINE__, $mainSocket, array('null', '0'));
+		} // END - if
+
+		// Create file name
+		$socketFile = self::createTempPathForFile('php_hub_ipc_socket');
+
+		// Debug message
+		self::createDebugInstance(__CLASS__)->debugOutput('SOCKET-FILE-LISTENER[' . __METHOD__ . ':' . __LINE__ . ']: socketFile=' . $socketFile . ' ...');
+
+		// File name must not be empty
+		assert(!empty($socketFile));
+
+		// Is the file there?
+		if ((self::isReachableFilePath($socketFile)) && (file_exists($socketFile))) {
+			// Old socket found
+			self::createDebugInstance(__CLASS__)->debugOutput('SOCKET-FILE-LISTENER[' . __METHOD__ . ':' . __LINE__ . ']: WARNING: Old socket at ' . $socketFile . ' found. Will not start.');
+
+			// Shutdown this socket
+			$this->shutdownSocket($mainSocket);
+
+			// Quit here
+			exit;
+		} // END - if
+
+		// Debug message
+		self::createDebugInstance(__CLASS__)->debugOutput('SOCKET-FILE-LISTENER[' . __METHOD__ . ':' . __LINE__ . ']: Binding to ' . $socketFile . ' ...');
+
+		// Try to bind to it
+		if (!socket_bind($mainSocket, $socketFile)) {
+			// Handle error here
+			$this->handleSocketError(__METHOD__, __LINE__, $mainSocket, array($socketFile, '0'));
+			/*
+			// Get socket error code for verification
+			$socketError = socket_last_error($mainSocket);
+
+			// Get error message
+			$errorMessage = socket_strerror($socketError);
+
+			// Shutdown this socket
+			$this->shutdownSocket($mainSocket);
+
+			// And throw again
+			throw new InvalidSocketException(array($this, $mainSocket, $socketError, $errorMessage), BaseListener::EXCEPTION_INVALID_SOCKET);
+			*/
+		} // END - if
+
+		// Start listen for connections
+		self::createDebugInstance(__CLASS__)->debugOutput('SOCKET-FILE-LISTENER[' . __METHOD__ . ':' . __LINE__ . ']: Listening for connections.');
+		if (!socket_listen($mainSocket)) {
+			// Handle this socket error with a faked recipientData array
+			$this->handleSocketError(__METHOD__, __LINE__, $mainSocket, array($socketFile, '0'));
+			/*
+			// Get socket error code for verification
+			$socketError = socket_last_error($mainSocket);
+
+			// Get error message
+			$errorMessage = socket_strerror($socketError);
+
+			// Shutdown this socket
+			$this->shutdownSocket($mainSocket);
+
+			// And throw again
+			throw new InvalidSocketException(array($this, $mainSocket, $socketError, $errorMessage), BaseListener::EXCEPTION_INVALID_SOCKET);
+			*/
+		} // END - if
+
+		// Now, we want non-blocking mode
+		self::createDebugInstance(__CLASS__)->debugOutput('SOCKET-FILE-LISTENER[' . __METHOD__ . ':' . __LINE__ . ']: Setting non-blocking mode.');
+		if (!socket_set_nonblock($mainSocket)) {
+			// Handle this socket error with a faked recipientData array
+			$this->handleSocketError(__METHOD__, __LINE__, $mainSocket, array($socketFile, '0'));
+			/*
+			// Get socket error code for verification
+			$socketError = socket_last_error($mainSocket);
+
+			// Get error message
+			$errorMessage = socket_strerror($socketError);
+
+			// Shutdown this socket
+			$this->shutdownSocket($mainSocket);
+
+			// And throw again
+			throw new InvalidSocketException(array($this, $mainSocket, $socketError, $errorMessage), BaseListener::EXCEPTION_INVALID_SOCKET);
+			*/
+		} // END - if
+
+		// Set the main socket
+		$this->registerServerSocketResource($mainSocket);
+
+		// Initialize the peer pool instance
+		$poolInstance = ObjectFactory::createObjectByConfiguredName('application_pool_class', array($this));
+
+		// Add main socket
+		$poolInstance->addPeer($mainSocket, BaseConnectionHelper::CONNECTION_TYPE_SERVER);
+
+		// And add it to this listener
+		$this->setPoolInstance($poolInstance);
+
+		// Initialize iterator for listening on packages
+		$iteratorInstance = ObjectFactory::createObjectByConfiguredName('socket_listen_iterator_class', array($poolInstance->getPoolEntriesInstance()));
+
+		// Rewind it and remember it in this class
+		$iteratorInstance->rewind();
+		$this->setIteratorInstance($iteratorInstance);
+
+		// Initialize the raw data handler
+		$handlerInstance = ObjectFactory::createObjectByConfiguredName('socket_raw_data_handler_class');
+
+		// Set it in this class
+		$this->setHandlerInstance($handlerInstance);
+
+		// Output message
+		self::createDebugInstance(__CLASS__)->debugOutput('SOCKET-FILE-LISTENER[' . __METHOD__ . ':' . __LINE__ . ']: TCP listener now ready on IP ' . $this->getListenAddress() . ', port ' . $this->getListenPort() . ' for service.');
 	}
 
 	/**
diff --git a/inc/main/classes/output/debug/console/class_DebugConsoleOutput.php b/inc/main/classes/output/debug/console/class_DebugConsoleOutput.php
index 3598cc18..d1de0cab 100644
--- a/inc/main/classes/output/debug/console/class_DebugConsoleOutput.php
+++ b/inc/main/classes/output/debug/console/class_DebugConsoleOutput.php
@@ -69,7 +69,7 @@ class DebugConsoleOutput extends BaseDebugOutput implements Debugger, OutputStre
 		} // END - if
 
 		// And print it out...
-		printf('%s%s', $output, chr(10));
+		printf('%s%s', str_replace('->', '->', $output), PHP_EOL);
 	}
 
 	/**
diff --git a/inc/main/exceptions/socket/class_SocketBindingException.php b/inc/main/exceptions/socket/class_SocketBindingException.php
new file mode 100644
index 00000000..cc52b99a
--- /dev/null
+++ b/inc/main/exceptions/socket/class_SocketBindingException.php
@@ -0,0 +1,47 @@
+
+ * @version		0.0.0
+ * @copyright	Copyright (c) 2007, 2008 Roland Haeder, 2009 - 2015 Core Developer Team
+ * @license		GNU GPL 3.0 or any newer version
+ * @link		http://www.shipsimu.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 .
+ */
+class SocketBindingException extends AbstractSocketException {
+	/**
+	 * A Constructor for this exception
+	 *
+	 * @param	$messageArray	Error message array
+	 * @param	$code			Error code
+	 * @return	void
+	 */
+	public function __construct (array $messageData, $code) {
+		// Construct the message
+		$message = sprintf('[%s:] Could not bind to socket %s: Type=%s, errno=%s, errstr=%s',
+			$messageData[0]->__toString(),
+			$messageData[1][0],
+			gettype($messageData[2]),
+			$messageData[3],
+			$messageData[4]
+		);
+
+		// Call parent exception constructor
+		parent::__construct($message, $code);
+	}
+}
+
+// [EOF]
+?>
diff --git a/inc/main/interfaces/index/stack/class_IndexableStack.ph b/inc/main/interfaces/index/stack/class_IndexableStack.ph
new file mode 100644
index 00000000..ff3d15fb
--- /dev/null
+++ b/inc/main/interfaces/index/stack/class_IndexableStack.ph
@@ -0,0 +1,9 @@
+
+	/**
+	 * Adds given hash to an index file
+	 *
+	 * @param	$stackName	Name of stack to add hash
+	 * @param	$data		Hash and gap position to be added to the index
+	 * @return	void
+	 */
+	function addHashToIndex ($stackName, array $data);
-- 
2.39.2