*
* @author Roland Haeder <webmaster@shipsimu.org>
* @version 0.0.0
- * @copyright Copyright (c) 2007, 2008 Roland Haeder, 2009 - 2012 Hub Developer Team
+ * @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
*
*/
private $rawPackageData = '';
+ /**
+ * Fragmenter instance, needs to be set here again
+ */
+ private $fragmenterInstance = NULL;
+
/**
* Protected constructor
*
// Initialize handler
$this->initHandler();
+
+ // Get a fragmenter instance for later verification of serial numbers (e.g. if all are received)
+ $fragmenterInstance = FragmenterFactory::createFragmenterInstance('package');
+
+ // Set it in this handler
+ $this->fragmenterInstance = $fragmenterInstance;
}
/**
$handlerInstance = new ChunkHandler();
// Get a FIFO stacker
- $stackerInstance = ObjectFactory::createObjectByConfiguredName('chunk_handler_stacker_class');
+ $stackInstance = ObjectFactory::createObjectByConfiguredName('chunk_handler_stacker_class');
// Init all stacker
- $stackerInstance->initStacks(array(
+ $stackInstance->initStacks(array(
self::STACKER_NAME_CHUNKS_WITH_FINAL_EOP,
self::STACKER_NAME_CHUNKS_WITHOUT_FINAL,
self::STACKER_NAME_ASSEMBLED_RAW_DATA
));
// Set the stacker in this handler
- $handlerInstance->setStackerInstance($stackerInstance);
+ $handlerInstance->setStackInstance($stackInstance);
// Get a crypto instance ...
$cryptoInstance = ObjectFactory::createObjectByConfiguredName('crypto_class');
// ... and set it in this handler
$handlerInstance->setCryptoInstance($cryptoInstance);
- // Get a fragmenter instance for later verification of serial numbers (e.g. if all are received)
- $fragmenterInstance = FragmenterFactory::createFragmenterInstance('package');
-
- // Set it in this handler
- $handlerInstance->setFragmenterInstance($fragmenterInstance);
-
// Return the prepared instance
return $handlerInstance;
}
* @return void
*/
private function verifyChunkSerialNumbers () {
+ // Debug message
+ //* NOISY-DEBUG */ self::createDebugInstance(__CLASS__)->debugOutput('CHUNK-HANDLER[' . __METHOD__ . ':' . __LINE__ . ']: finalPackageChunks=' . print_r($this->finalPackageChunks, TRUE));
+
+ // Get final hash
+ $finalHash = $this->generateFinalHash(implode('', $this->finalPackageChunks['content']));
+
// Reset the serial number generator
- $this->getFragmenterInstance()->resetSerialNumber();
+ $this->fragmenterInstance->resetSerialNumber($finalHash);
// "Walk" through all (content) chunks
foreach ($this->finalPackageChunks['content'] as $serialNumber => $content) {
// Get next serial number
- $nextSerial = $this->getFragmenterInstance()->getNextHexSerialNumber();
+ $nextSerial = $this->fragmenterInstance->getNextHexSerialNumber($finalHash);
// Debug output
//* NOISY-DEBUG */ self::createDebugInstance(__CLASS__)->debugOutput('CHUNK-HANDLER[' . __METHOD__ . ':' . __LINE__ . ']: serialNumber=' . $serialNumber . ',nextSerial=' . $nextSerial);
// That went well, so start assembling all chunks
//* NOISY-DEBUG: */ self::createDebugInstance(__CLASS__)->debugOutput('CHUNK-HANDLER[' . __METHOD__ . ':' . __LINE__ . ']: Handling ' . count($this->finalPackageChunks['content']) . ' entries ...');
foreach ($this->finalPackageChunks['content'] as $serialNumber => $content) {
+ // Assert on 'hash' entry (must always be set)
+ assert(isset($this->finalPackageChunks['hashes'][$serialNumber]));
+
// Debug message
- //* NOISY-DEBUG: */ self::createDebugInstance(__CLASS__)->debugOutput('CHUNK-HANDLER[' . __METHOD__ . ':' . __LINE__ . ']: serialNumber=' . $serialNumber . ' - validating ...');
+ //* NOISY-DEBUG: */ self::createDebugInstance(__CLASS__)->debugOutput('CHUNK-HANDLER[' . __METHOD__ . ':' . __LINE__ . ']: serialNumber=' . $serialNumber . ',hashes=' . $this->finalPackageChunks['hashes'][$serialNumber] . ' - validating ...');
//* NOISY-DEBUG: */ self::createDebugInstance(__CLASS__)->debugOutput('finalPackageChunks=' . print_r($this->finalPackageChunks, TRUE) . 'chunkHashes=' . print_r($this->chunkHashes, TRUE));
// Is this chunk valid? This should be the case
assert($this->eopChunk[1] == $this->chunkHashes[count($this->chunkHashes) - 2]);
}
+ /**
+ * Generate final hash if EOP chunk is found, else an assert will happen.
+ *
+ * @param $rawPackageData Raw package data
+ * @return $finalHash Final hash if EOP chunk is found
+ */
+ private function generateFinalHash ($rawPackageData) {
+ // Make sure the raw package data is given
+ assert((is_string($rawPackageData)) && (!empty($rawPackageData)));
+
+ // Make sure the EOP chunk is set
+ assert((isset($this->eopChunk[0])) && (isset($this->eopChunk[1])));
+ assert((is_string($this->eopChunk[0])) && (!empty($this->eopChunk[0])));
+
+ // Hash the raw data
+ $finalHash = $this->getCryptoInstance()->hashString($rawPackageData, $this->eopChunk[0], FALSE);
+
+ // Return it
+ return $finalHash;
+ }
+
/**
* Verifies the finally assembled raw package data by comparing it against
* the final hash.
* @return void
*/
private function verifyRawPackageData () {
- // Hash the raw package data for final verification
- $finalHash = $this->getCryptoInstance()->hashString($this->rawPackageData, $this->eopChunk[0], FALSE);
+ // Generate final hash
+ $finalHash = $this->generateFinalHash($this->rawPackageData);
// Is it the same?
//* NOISY-DEBUG: */ self::createDebugInstance(__CLASS__)->debugOutput('CHUNK-HANDLER[' . __METHOD__ . ':' . __LINE__ . ']: eopChunk[1]=' . $this->eopChunk[1] . ',finalHash=' . $finalHash);
assert($finalHash == $this->eopChunk[0]);
}
+ /**
+ * Checks whether the final (last) chunk is valid
+ *
+ * @param $chunks An array with chunks and (hopefully) a valid final chunk
+ * @return $isValid Whether the final (last) chunk is valid
+ */
+ private function isValidFinalChunk (array $chunks) {
+ // Default is all fine
+ $isValid = TRUE;
+
+ // Split the (possible) EOP chunk
+ $chunkSplits = explode(PackageFragmenter::CHUNK_DATA_HASH_SEPARATOR, $chunks[count($chunks) - 1]);
+
+ // Make sure chunks with only 3 elements are parsed (for details see ChunkHandler)
+ //* NOISY-DEBUG: */ self::createDebugInstance(__CLASS__)->debugOutput('eopChunk=' . $chunks[count($chunks) - 1] . ',chunkSplits=' . print_r($chunkSplits, TRUE));
+ assert(count($chunkSplits) == 3);
+
+ // Validate final chunk
+ if (substr($chunkSplits[ChunkHandler::CHUNK_SPLITS_INDEX_RAW_DATA], 0, strlen(PackageFragmenter::END_OF_PACKAGE_IDENTIFIER)) != PackageFragmenter::END_OF_PACKAGE_IDENTIFIER) {
+ // Not fine
+ $isValid = FALSE;
+ } elseif (substr_count($chunkSplits[ChunkHandler::CHUNK_SPLITS_INDEX_RAW_DATA], PackageFragmenter::CHUNK_HASH_SEPARATOR) != 1) {
+ // CHUNK_HASH_SEPARATOR shall only be found once
+ $isValid = FALSE;
+ }
+
+ // Return status
+ return $isValid;
+ }
+
/**
* Adds all chunks if the last one verifies as a 'final chunk'.
*
}
// Do we have some pending chunks (no final)?
- while (!$this->getStackerInstance()->isStackEmpty(self::STACKER_NAME_CHUNKS_WITHOUT_FINAL)) {
+ while (!$this->getStackInstance()->isStackEmpty(self::STACKER_NAME_CHUNKS_WITHOUT_FINAL)) {
// Then get it first and add it before the EOP chunks
- array_unshift($chunks, $this->getStackerInstance()->popNamed(self::STACKER_NAME_CHUNKS_WITHOUT_FINAL));
+ array_unshift($chunks, $this->getStackInstance()->popNamed(self::STACKER_NAME_CHUNKS_WITHOUT_FINAL));
} // END - while
// Add all chunks to the FIFO stacker
foreach ($chunks as $chunk) {
// Add the chunk
- $this->getStackerInstance()->pushNamed(self::STACKER_NAME_CHUNKS_WITH_FINAL_EOP, $chunk);
+ $this->getStackInstance()->pushNamed(self::STACKER_NAME_CHUNKS_WITH_FINAL_EOP, $chunk);
} // END - foreach
}
// Add all chunks to the FIFO stacker
foreach ($chunks as $chunk) {
// Add the chunk
- $this->getStackerInstance()->pushNamed(self::STACKER_NAME_CHUNKS_WITHOUT_FINAL, $chunk);
+ $this->getStackInstance()->pushNamed(self::STACKER_NAME_CHUNKS_WITHOUT_FINAL, $chunk);
} // END - foreach
}
*/
public function ifUnhandledChunksWithFinalAvailable () {
// Simply check if the stacker is not empty
- $unhandledChunks = $this->getStackerInstance()->isStackEmpty(self::STACKER_NAME_CHUNKS_WITH_FINAL_EOP) === FALSE;
+ $unhandledChunks = $this->getStackInstance()->isStackEmpty(self::STACKER_NAME_CHUNKS_WITH_FINAL_EOP) === FALSE;
// Return result
return $unhandledChunks;
assert($this->ifUnhandledChunksWithFinalAvailable());
// Get an entry from the stacker
- $chunk = $this->getStackerInstance()->popNamed(self::STACKER_NAME_CHUNKS_WITH_FINAL_EOP);
+ $chunk = $this->getStackInstance()->popNamed(self::STACKER_NAME_CHUNKS_WITH_FINAL_EOP);
// Split the string with proper separator character
$chunkSplits = explode(PackageFragmenter::CHUNK_DATA_HASH_SEPARATOR, $chunk);
$this->addChunkToFinalArray($chunkSplits);
// Is the stack now empty?
- if ($this->getStackerInstance()->isStackEmpty(self::STACKER_NAME_CHUNKS_WITH_FINAL_EOP)) {
+ if ($this->getStackInstance()->isStackEmpty(self::STACKER_NAME_CHUNKS_WITH_FINAL_EOP)) {
// Then mark the final array as complete
$this->markFinalArrayAsCompleted();
} // END - if
$isRawPackageDataAvailable = ((!empty($this->rawPackageData)) && (!$this->ifUnassembledChunksAvailable()));
// Return it
- /* NOISY-DEBUG: */ self::createDebugInstance(__CLASS__)->debugOutput('CHUNK-HANDLER[' . __METHOD__ . ':' . __LINE__ . ']: isRawPackageDataAvailable=' . intval($isRawPackageDataAvailable));
+ //* NOISY-DEBUG: */ self::createDebugInstance(__CLASS__)->debugOutput('CHUNK-HANDLER[' . __METHOD__ . ':' . __LINE__ . ']: isRawPackageDataAvailable=' . intval($isRawPackageDataAvailable));
return $isRawPackageDataAvailable;
}
assert($this->ifRawPackageDataIsAvailable());
// Then feed it into the next stacker
- /* NOISY-DEBUG: */ self::createDebugInstance(__CLASS__)->debugOutput('CHUNK-HANDLER[' . __METHOD__ . ':' . __LINE__ . ']: Pushing ' . strlen($this->rawPackageData) . ' bytes to stack ' . self::STACKER_NAME_ASSEMBLED_RAW_DATA . ' ...');
- $this->getStackerInstance()->pushNamed(self::STACKER_NAME_ASSEMBLED_RAW_DATA, $this->rawPackageData);
+ //* NOISY-DEBUG: */ self::createDebugInstance(__CLASS__)->debugOutput('CHUNK-HANDLER[' . __METHOD__ . ':' . __LINE__ . ']: Pushing ' . strlen($this->rawPackageData) . ' bytes to stack ' . self::STACKER_NAME_ASSEMBLED_RAW_DATA . ' ...');
+ $this->getStackInstance()->pushNamed(self::STACKER_NAME_ASSEMBLED_RAW_DATA, $this->rawPackageData);
// ... and reset it
$this->rawPackageData = '';