From 1871b8b380a5b154d0513286e147a0d1f9f5f8a4 Mon Sep 17 00:00:00 2001 From: Alexander Ebert Date: Tue, 8 Apr 2014 16:43:26 +0200 Subject: [PATCH] Added prototype of a proper package archive validation Ignore this, these classes are just lying around doing nothing. --- .../system/package/PackageArchive.class.php | 21 +-- .../PackageValidationArchive.class.php | 152 ++++++++++++++++++ .../PackageValidationException.class.php | 103 ++++++++++++ .../PackageValidationManager.class.php | 84 ++++++++++ 4 files changed, 351 insertions(+), 9 deletions(-) create mode 100644 wcfsetup/install/files/lib/system/package/validation/PackageValidationArchive.class.php create mode 100644 wcfsetup/install/files/lib/system/package/validation/PackageValidationException.class.php create mode 100644 wcfsetup/install/files/lib/system/package/validation/PackageValidationManager.class.php diff --git a/wcfsetup/install/files/lib/system/package/PackageArchive.class.php b/wcfsetup/install/files/lib/system/package/PackageArchive.class.php index 898c7c5d5d..4342867d6c 100644 --- a/wcfsetup/install/files/lib/system/package/PackageArchive.class.php +++ b/wcfsetup/install/files/lib/system/package/PackageArchive.class.php @@ -2,7 +2,7 @@ namespace wcf\system\package; use wcf\data\package\Package; use wcf\system\database\util\PreparedStatementConditionBuilder; -use wcf\system\exception\SystemException; +use wcf\system\package\validation\PackageValidationException; use wcf\system\io\Tar; use wcf\system\WCF; use wcf\util\DateUtil; @@ -134,7 +134,7 @@ class PackageArchive { public function openArchive() { // check whether archive exists and is a TAR archive if (!file_exists($this->archive)) { - throw new SystemException("unable to find package file '".$this->archive."'"); + throw new PackageValidationException(PackageValidationException::FILE_NOT_FOUND, array('archive' => $this->archive)); } // open archive and read package information @@ -149,7 +149,7 @@ class PackageArchive { // search package.xml in package archive // throw error message if not found if ($this->tar->getIndexByFilename(self::INFO_FILE) === false) { - throw new SystemException("package information file '".(self::INFO_FILE)."' not found in '".$this->archive."'"); + throw new PackageValidationException(PackageValidationException::MISSING_PACKAGE_XML, array('archive' => $this->archive)); } // extract package.xml, parse XML @@ -170,7 +170,7 @@ class PackageArchive { $packageName = $package->getAttribute('name'); if (!Package::isValidPackageName($packageName)) { // package name is not a valid package identifier - throw new SystemException("'".$packageName."' is not a valid package name."); + throw new PackageValidationException(PackageValidationException::INVALID_PACKAGE_NAME, array('packageName' => $packageName)); } $this->packageInfo['name'] = $packageName; @@ -209,7 +209,7 @@ class PackageArchive { case 'version': if (!Package::isValidVersion($element->nodeValue)) { - throw new SystemException("package version '".$element->nodeValue."' is invalid"); + throw new PackageValidationException(PackageValidationException::INVALID_PACKAGE_VERSION, array('packageVersion' => $element->nodeValue)); } $this->packageInfo['version'] = $element->nodeValue; @@ -235,7 +235,7 @@ class PackageArchive { $elements = $xpath->query('child::ns:requiredpackages/ns:requiredpackage', $package); foreach ($elements as $element) { if (!Package::isValidPackageName($element->nodeValue)) { - throw new SystemException("'".$element->nodeValue."' is not a valid package name."); + throw new PackageValidationException(PackageValidationException::INVALID_PACKAGE_NAME, array('packageName' => $element->nodeValue)); } // read attributes @@ -252,7 +252,7 @@ class PackageArchive { $elements = $xpath->query('child::ns:optionalpackages/ns:optionalpackage', $package); foreach ($elements as $element) { if (!Package::isValidPackageName($element->nodeValue)) { - throw new SystemException("'".$element->nodeValue."' is not a valid package name."); + throw new PackageValidationException(PackageValidationException::INVALID_PACKAGE_NAME, array('packageName' => $element->nodeValue)); } // read attributes @@ -269,7 +269,7 @@ class PackageArchive { $elements = $xpath->query('child::ns:excludedpackages/ns:excludedpackage', $package); foreach ($elements as $element) { if (!Package::isValidPackageName($element->nodeValue)) { - throw new SystemException("'".$element->nodeValue."' is not a valid package name."); + throw new PackageValidationException(PackageValidationException::INVALID_PACKAGE_NAME, array('packageName' => $element->nodeValue)); } // read attributes @@ -738,7 +738,10 @@ class PackageArchive { // search the requested tar archive in our package archive. // throw error message if not found. if (($fileIndex = $this->tar->getIndexByFilename($filename)) === false) { - throw new SystemException("tar archive '".$filename."' not found in '".$this->archive."'."); + throw new PackageValidationException(PackageValidationException::FILE_NOT_FOUND, array( + 'archive' => $this->archive, + 'targetArchive' => $filename + )); } // requested tar archive was found diff --git a/wcfsetup/install/files/lib/system/package/validation/PackageValidationArchive.class.php b/wcfsetup/install/files/lib/system/package/validation/PackageValidationArchive.class.php new file mode 100644 index 0000000000..763a64ec25 --- /dev/null +++ b/wcfsetup/install/files/lib/system/package/validation/PackageValidationArchive.class.php @@ -0,0 +1,152 @@ + + * @package com.woltlab.wcf + * @subpackage system.package.validation + * @category Community Framework + */ +class PackageValidationArchive implements \RecursiveIterator { + /** + * package archive object + * @var \wcf\system\package\PackageArchive + */ + protected $archive = null; + + /** + * list of direct requirements delivered by this package + * @var array<\wcf\system\package\validation\PackageValidationArchive> + */ + protected $children = array(); + + /** + * exception occured during validation + * @var \Exception + */ + protected $exception = null; + + /** + * children pointer + * @var integer + */ + private $position = 0; + + /** + * Creates a new package validation archive instance. + * + * @param string $archive + */ + public function __construct($archive) { + $this->archive = new PackageArchive($archive); + } + + /** + * Validates this package and it's delivered requirements. + * + * @return boolean + */ + public function validate() { + // + // step 1) try to read archive + // + try { + $this->archive->openArchive(); + } + catch (\Exception $e) { + $this->exception = $e; + } + + // + // step 2) traverse requirements + // + die("
".print_r($this->archive->getOpenRequirements(), true));
+		
+		//
+		// step 3) check requirements against virtual package table
+		//
+		
+		/* TODO: do something */
+		
+		//
+		// step 4) check exclusions
+		//
+		
+		/* TODO: do something */
+		
+		return true;
+		
+	}
+	
+	/**
+	 * Returns the exception message.
+	 * 
+	 * @return	string
+	 */
+	public function getExceptionMessage() {
+		if ($this->exception === null) {
+			return '';
+		}
+		
+		if ($this->exception instanceof PackageValidationException) {
+			return WCF::getLanguage()->getDynamicVariable('wcf.package.validation.errorCode.' . $this->exception->getCode(), $this->exception->getDetails());
+		}
+		
+		return $this->exception->getMessage();
+	}
+	
+	/**
+	 * @see	\Iterator::rewind()
+	 */
+	public function rewind() {
+		$this->position = 0;
+	}
+	
+	/**
+	 * @see	\Iterator::valid()
+	 */
+	public function valid() {
+		return isset($this->children[$this->position]);
+	}
+	
+	/**
+	 * @see	\Iterator::next()
+	 */
+	public function next() {
+		$this->position++;
+	}
+	
+	/**
+	 * @see	\Iterator::current()
+	 */
+	public function current() {
+		return $this->children[$this->position];
+	}
+	
+	/**
+	 * @see	\Iterator::key()
+	 */
+	public function key() {
+		return $this->position;
+	}
+	
+	/**
+	 * @see	\RecursiveIterator::getChildren()
+	 */
+	public function getChildren() {
+		return $this->children[$this->position];
+	}
+	
+	/**
+	 * @see	\RecursiveIterator::hasChildren()
+	 */
+	public function hasChildren() {
+		return count($this->children) > 0;
+	}
+}
diff --git a/wcfsetup/install/files/lib/system/package/validation/PackageValidationException.class.php b/wcfsetup/install/files/lib/system/package/validation/PackageValidationException.class.php
new file mode 100644
index 0000000000..554e0c1cc8
--- /dev/null
+++ b/wcfsetup/install/files/lib/system/package/validation/PackageValidationException.class.php
@@ -0,0 +1,103 @@
+
+ * @package	com.woltlab.wcf
+ * @subpackage	system.package.validation
+ * @category	Community Framework
+ */
+class PackageValidationException extends SystemException {
+	/**
+	 * list of additional details for each subtype
+	 * @var	array
+	 */
+	protected $details = array();
+	
+	/**
+	 * missing archive, expects the detail 'archive' and optionally 'targetArchive' (extracting archive from the archive)
+	 * @var	integer
+	 */
+	const FILE_NOT_FOUND = 1;
+	
+	/**
+	 * missing package.xml, expects the detail 'archive'
+	 * @var	integer
+	 */
+	const MISSING_PACKAGE_XML = 2;
+	
+	/**
+	 * package name violates WCF's schema, expects the detail 'packageName'
+	 * @var	integer
+	 */
+	const INVALID_PACKAGE_NAME = 3;
+	
+	/**
+	 * package version violates WCF's schema, expects the detail 'packageVersion'
+	 * @var	integer
+	 */
+	const INVALID_PACKAGE_VERSION = 4;
+	
+	/**
+	 * Creates a new PackageArchiveValidationException.
+	 * 
+	 * @param	integer		$code
+	 * @param	array	$details
+	 */
+	public function __construct($code, array $details = array()) {
+		parent::__construct($this->getLegacyMessage(), $code);
+		
+		$this->details = $details;
+	}
+	
+	/**
+	 * Returns exception details.
+	 * 
+	 * @return	array
+	 */
+	public function getDetails() {
+		return $this->details;
+	}
+	
+	/**
+	 * Returns legacy error messages to mimic WCF 2.0.x PackageArchive's exceptions.
+	 * 
+	 * @return	string
+	 */
+	protected function getLegacyMessage() {
+		switch ($this->getCode()) {
+			case self::FILE_NOT_FOUND:
+				if (isset($this->details['targetArchive'])) {
+					return "tar archive '".$this->details['targetArchive']."' not found in '".$this->details['archive']."'.";
+				}
+				
+				return "unable to find package file '".$this->details['archive']."'";
+			break;
+			
+			case self::MISSING_PACKAGE_XML:
+				return "package information file '".PackageArchive::INFO_FILE."' not found in '".$this->details['archive']."'";
+			break;
+			
+			case self::INVALID_PACKAGE_NAME:
+				return "'".$this->details['packageName']."' is not a valid package name.";
+			break;
+			
+			case self::INVALID_PACKAGE_VERSION:
+				return "package version '".$this->details['packageVersion']."' is invalid";
+			break;
+		}
+	}
+	
+	/**
+	 * @see	\wcf\system\exception\LoggedException::logError()
+	 */
+	protected function logError() {
+		// do not log errors
+	}
+}
diff --git a/wcfsetup/install/files/lib/system/package/validation/PackageValidationManager.class.php b/wcfsetup/install/files/lib/system/package/validation/PackageValidationManager.class.php
new file mode 100644
index 0000000000..c9c10459a1
--- /dev/null
+++ b/wcfsetup/install/files/lib/system/package/validation/PackageValidationManager.class.php
@@ -0,0 +1,84 @@
+
+ * @package	com.woltlab.wcf
+ * @subpackage	system.package.validation
+ * @category	Community Framework
+ */
+class PackageValidationManager extends SingletonFactory {
+	/**
+	 * package validation archive object
+	 * @var	\wcf\system\package\validation\PackageValidationArchive
+	 */
+	protected $packageValidationArchive = null;
+	
+	/**
+	 * virtual package list containing package => packageVersion
+	 * @var	array
+	 */
+	protected $virtualPackageList = array();
+	
+	/**
+	 * Validates given archive for existance and ability to be installed/updated
+	 * 
+	 * @param	string		$archive
+	 * @return	boolean
+	 */
+	public function validate($archive) {
+		$this->virtualPackageList = array();
+		$this->packageValidationArchive = new PackageValidationArchive($archive);
+		
+		return $this->packageValidationArchive->validate();
+	}
+	
+	/**
+	 * Returns package validation archive object.
+	 * 
+	 * @return	\wcf\system\package\validation\PackageValidationArchive
+	 */
+	public function getPackageValidationArchive() {
+		return $this->packageValidationArchive;
+	}
+	
+	/**
+	 * Adds a virtual package with the corresponding version, if the package is already known,
+	 * the higher version number will be stored.
+	 * 
+	 * @param	string		$package
+	 * @param	string		$packageVersion
+	 * @return	boolean
+	 */
+	public function addVirtualPackage($package, $packageVersion) {
+		if (isset($this->virtualPackageList[$package])) {
+			if (Package::compareVersion($packageVersion, $this->virtualPackageList[$package], '<')) {
+				return false;
+			}
+		}
+		
+		$this->virtualPackageList[$package] = $packageVersion;
+		
+		return true;
+	}
+	
+	/**
+	 * Returns the version number of a virtual package or null if it doesn't exist.
+	 * 
+	 * @param	string		$package
+	 * @return	string
+	 */
+	public function geVirtualPackageVersion($package) {
+		if (isset($this->virtualPackageList[$package])) {
+			return $this->virtualPackageList[$package];
+		}
+		
+		return null;
+	}
+}
-- 
2.20.1