+ /* DEBUG: */ self::createDebugInstance(__CLASS__)->debugOutput('NETWORK-PACKAGE: All stacker have been re-initialized.');
+ }
+
+ /**
+ * Removes the first failed outoging package from the stack to continue
+ * with next one (it will never work until the issue is fixed by you).
+ *
+ * @return void
+ * @throws UnexpectedPackageStatusException If the package status is not 'failed'
+ * @todo This may be enchanced for outgoing packages?
+ */
+ public function removeFirstFailedPackage () {
+ // Get the package again
+ $packageData = $this->getStackerInstance()->getNamed(self::STACKER_NAME_DECLARED);
+
+ // Is the package status 'failed'?
+ if ($packageData[self::PACKAGE_DATA_STATUS] != self::PACKAGE_STATUS_FAILED) {
+ // Not failed!
+ throw new UnexpectedPackageStatusException(array($this, $packageData, self::PACKAGE_STATUS_FAILED), BaseListener::EXCEPTION_UNEXPECTED_PACKAGE_STATUS);
+ } // END - if
+
+ // Remove this entry
+ $this->getStackerInstance()->popNamed(self::STACKER_NAME_DECLARED);
+ }
+
+ /**
+ * "Decode" the package content into the same array when it was sent.
+ *
+ * @param $rawPackageContent The raw package content to be "decoded"
+ * @return $decodedData An array with 'sender', 'recipient', 'content' and 'status' elements
+ */
+ public function decodeRawContent ($rawPackageContent) {
+ // Use the separator '#' to "decode" it
+ $decodedArray = explode(self::PACKAGE_DATA_SEPARATOR, $rawPackageContent);
+
+ // Assert on count (should be always 3)
+ assert(count($decodedArray) == self::DECODED_DATA_ARRAY_SIZE);
+
+ // Generate the signature of comparing it
+ /*
+ * @todo Unsupported feature of "signed" messages commented out
+ if (!$this->isPackageSignatureValid($decodedArray)) {
+ // Is not valid, so throw an exception here
+ exit(__METHOD__ . ':INVALID SIG! UNDER CONSTRUCTION!' . chr(10));
+ } // END - if
+ */
+
+ /*
+ * Create 'decodedData' array with all assoziative array elements,
+ * except signature.
+ */
+ $decodedData = array(
+ self::PACKAGE_DATA_SENDER => $decodedArray[self::INDEX_PACKAGE_SENDER],
+ self::PACKAGE_DATA_RECIPIENT => $decodedArray[self::INDEX_PACKAGE_RECIPIENT],
+ self::PACKAGE_DATA_CONTENT => $decodedArray[self::INDEX_PACKAGE_CONTENT],
+ self::PACKAGE_DATA_STATUS => self::PACKAGE_STATUS_DECODED
+ );
+
+ // And return it
+ return $decodedData;
+ }
+
+ /**
+ * Handles decoded data for this node by "decoding" the 'content' part of
+ * it. Again this method uses explode() for the "decoding" process.
+ *
+ * @param $decodedData An array with decoded raw package data
+ * @return void
+ * @throws InvalidDataChecksumException If the checksum doesn't match
+ */
+ public function handleRawData (array $decodedData) {
+ /*
+ * "Decode" the package's content by a simple explode() call, for
+ * details of the array elements, see comments for constant
+ * PACKAGE_MASK.
+ */
+ $decodedContent = explode(self::PACKAGE_MASK_SEPARATOR, $decodedData[self::PACKAGE_DATA_CONTENT]);
+
+ // Assert on array count for a very basic validation
+ assert(count($decodedContent) == self::PACKAGE_CONTENT_ARRAY_SIZE);
+
+ /*
+ * Convert the indexed array into an associative array. This is much
+ * better to remember than plain numbers, isn't it?
+ */
+ $decodedContent = array(
+ // Compressor's extension used to compress the data
+ self::PACKAGE_CONTENT_EXTENSION => $decodedContent[self::INDEX_COMPRESSOR_EXTENSION],
+ // Package data (aka "message") in BASE64-decoded form but still compressed
+ self::PACKAGE_CONTENT_MESSAGE => base64_decode($decodedContent[self::INDEX_PACKAGE_DATA]),
+ // Tags as an indexed array for "tagging" the message
+ self::PACKAGE_CONTENT_TAGS => explode(self::PACKAGE_TAGS_SEPARATOR, $decodedContent[self::INDEX_TAGS]),
+ // Checksum of the _decoded_ data
+ self::PACKAGE_CONTENT_CHECKSUM => $decodedContent[self::INDEX_CHECKSUM]
+ );
+
+ // Is the checksum valid?
+ if (!$this->isChecksumValid($decodedContent, $decodedData)) {
+ // Is not the same, so throw an exception here
+ throw new InvalidDataChecksumException(array($this, $decodedContent, $decodedData), BaseListener::EXCEPTION_INVALID_DATA_CHECKSUM);
+ } // END - if
+
+ /*
+ * The checksum is the same, then it can be decompressed safely. The
+ * original message is at this point fully decoded.
+ */
+ $decodedContent[self::PACKAGE_CONTENT_MESSAGE] = $this->getCompressorInstance()->decompressStream($decodedContent[self::PACKAGE_CONTENT_MESSAGE]);
+
+ // And push it on the next stack
+ $this->getStackerInstance()->pushNamed(self::STACKER_NAME_NEW_MESSAGE, $decodedContent);
+ }
+
+ /**
+ * Checks whether a new message has arrived
+ *
+ * @return $hasArrived Whether a new message has arrived for processing
+ */
+ public function isNewMessageArrived () {
+ // Determine if the stack is not empty
+ $hasArrived = (!$this->getStackerInstance()->isStackEmpty(self::STACKER_NAME_NEW_MESSAGE));
+
+ // Return it
+ return $hasArrived;
+ }
+
+ /**
+ * Handles newly arrived messages
+ *
+ * @return void
+ * @todo Implement verification of all sent tags here?
+ */
+ public function handleNewlyArrivedMessage () {
+ // Get it from the stacker, it is the full array with the decoded message
+ $decodedContent = $this->getStackerInstance()->popNamed(self::STACKER_NAME_NEW_MESSAGE);
+
+ // Now get a filter chain back from factory with given tags array
+ $chainInstance = PackageFilterChainFactory::createChainByTagsArray($decodedContent[self::PACKAGE_CONTENT_TAGS]);
+
+ /*
+ * Process the message through all filters, note that all other
+ * elements from $decodedContent are no longer needed.
+ */
+ $chainInstance->processMessage($decodedContent[self::PACKAGE_CONTENT_MESSAGE], $this);
+ }
+
+ /**
+ * Checks whether a processed message is pending for "interpretation"
+ *
+ * @return $isPending Whether a processed message is pending
+ */
+ public function isProcessedMessagePending () {
+ // Check it
+ $isPending = (!$this->getStackerInstance()->isStackEmpty(self::STACKER_NAME_PROCESSED_MESSAGE));
+
+ // Return it
+ return $isPending;
+ }
+
+ /**
+ * Handle processed messages by "interpreting" the 'message_type' element
+ *
+ * @return void
+ */
+ public function handleProcessedMessage () {
+ // Get it from the stacker, it is the full array with the processed message
+ $messageArray = $this->getStackerInstance()->popNamed(self::STACKER_NAME_PROCESSED_MESSAGE);
+
+ // Create a handler instance from given message type
+ $handlerInstance = MessageTypeHandlerFactory::createMessageTypeHandlerInstance($messageArray[self::MESSAGE_ARRAY_TYPE]);
+
+ // Handle message data
+ $handlerInstance->handleMessageData($messageArray[self::MESSAGE_ARRAY_DATA], $this);