/**
  * Builds nodes for optional packages, whereas each package exists within
  * one node with the same parent node, seperated by sequence no (which does
  * not really matter at this point).
  */
 protected function buildOptionalNodes()
 {
     $packages = array();
     $optionalPackages = $this->installation->getArchive()->getOptionals();
     foreach ($optionalPackages as $package) {
         // check if already installed
         if (Package::isAlreadyInstalled($package['name'])) {
             continue;
         }
         // extract package
         $index = $this->installation->getArchive()->getTar()->getIndexByFilename($package['file']);
         if ($index === false) {
             throw new SystemException("Unable to find required package '" . $package['file'] . "' within archive.");
         }
         $fileName = FileUtil::getTemporaryFilename('package_', preg_replace('!^.*(?=\\.(?:tar\\.gz|tgz|tar)$)!i', '', basename($package['file'])));
         $this->installation->getArchive()->getTar()->extract($index, $fileName);
         // get archive data
         $archive = new PackageArchive($fileName);
         $archive->openArchive();
         // check if all requirements are met
         $isInstallable = true;
         foreach ($archive->getOpenRequirements() as $packageName => $package) {
             if (!isset($package['file'])) {
                 // requirement is neither installed nor shipped, check if it is about to be installed
                 if (!isset(self::$pendingPackages[$packageName])) {
                     $isInstallable = false;
                     break;
                 }
             }
         }
         // check for exclusions
         $excludedPackages = $archive->getConflictedExcludedPackages();
         if (!empty($excludedPackages)) {
             $isInstallable = false;
         }
         $excludingPackages = $archive->getConflictedExcludingPackages();
         if (!empty($excludingPackages)) {
             $isInstallable = false;
         }
         $packages[] = array('archive' => $fileName, 'isInstallable' => $isInstallable, 'package' => $archive->getPackageInfo('name'), 'packageName' => $archive->getLocalizedPackageInfo('packageName'), 'packageDescription' => $archive->getLocalizedPackageInfo('packageDescription'), 'selected' => 0);
         self::$pendingPackages[$archive->getPackageInfo('name')] = $archive->getPackageInfo('version');
     }
     if (!empty($packages)) {
         $this->parentNode = $this->node;
         $this->node = $this->getToken();
         $this->sequenceNo = 0;
         $sql = "INSERT INTO\twcf" . WCF_N . "_package_installation_node\n\t\t\t\t\t\t(queueID, processNo, sequenceNo, node, parentNode, nodeType, nodeData)\n\t\t\t\tVALUES\t\t(?, ?, ?, ?, ?, ?, ?)";
         $statement = WCF::getDB()->prepareStatement($sql);
         $statement->execute(array($this->installation->queue->queueID, $this->installation->queue->processNo, $this->sequenceNo, $this->node, $this->parentNode, 'optionalPackages', serialize($packages)));
     }
 }
 /**
  * Validates this package and optionally it's delivered requirements. The set validation
  * mode will toggle between different checks. 
  * 
  * @param	integer		$validationMode
  * @return	boolean
  */
 public function validate($validationMode, $requiredVersion = '')
 {
     if ($validationMode !== PackageValidationManager::VALIDATION_EXCLUSION) {
         try {
             // try to read archive
             $this->archive->openArchive();
             // check if package is installable or suitable for an update
             $this->validateInstructions($requiredVersion, $validationMode);
         } catch (PackageValidationException $e) {
             $this->exception = $e;
             return false;
         }
     }
     $package = $this->archive->getPackageInfo('name');
     if ($validationMode === PackageValidationManager::VALIDATION_RECURSIVE) {
         try {
             PackageValidationManager::getInstance()->addVirtualPackage($package, $this->archive->getPackageInfo('version'));
             // cache excluded packages
             self::$excludedPackages[$package] = array();
             $excludedPackages = $this->archive->getExcludedPackages();
             for ($i = 0, $count = count($excludedPackages); $i < $count; $i++) {
                 if (!isset(self::$excludedPackages[$package][$excludedPackages[$i]['name']])) {
                     self::$excludedPackages[$package][$excludedPackages[$i]['name']] = array();
                 }
                 self::$excludedPackages[$package][$excludedPackages[$i]['name']][] = $excludedPackages[$i]['version'];
             }
             // traverse open requirements
             foreach ($this->archive->getOpenRequirements() as $requirement) {
                 $virtualPackageVersion = PackageValidationManager::getInstance()->getVirtualPackage($requirement['name']);
                 if ($virtualPackageVersion === null || Package::compareVersion($virtualPackageVersion, $requirement['minversion'], '<')) {
                     if (empty($requirement['file'])) {
                         // check if package is known
                         $sql = "SELECT\t*\n\t\t\t\t\t\t\t\tFROM\twcf" . WCF_N . "_package\n\t\t\t\t\t\t\t\tWHERE\tpackage = ?";
                         $statement = WCF::getDB()->prepareStatement($sql);
                         $statement->execute(array($requirement['name']));
                         $package = $statement->fetchObject('wcf\\data\\package\\Package');
                         throw new PackageValidationException(PackageValidationException::MISSING_REQUIREMENT, array('package' => $package, 'packageName' => $requirement['name'], 'packageVersion' => $requirement['minversion']));
                     }
                     $archive = $this->archive->extractTar($requirement['file']);
                     $index = count($this->children);
                     $this->children[$index] = new PackageValidationArchive($archive, $this, $this->depth + 1);
                     if (!$this->children[$index]->validate(PackageValidationManager::VALIDATION_RECURSIVE, $requirement['minversion'])) {
                         return false;
                     }
                     PackageValidationManager::getInstance()->addVirtualPackage($this->children[$index]->getArchive()->getPackageInfo('name'), $this->children[$index]->getArchive()->getPackageInfo('version'));
                 }
             }
         } catch (PackageValidationException $e) {
             $this->exception = $e;
             return false;
         }
     } else {
         if ($validationMode === PackageValidationManager::VALIDATION_EXCLUSION) {
             try {
                 $this->validateExclusion($package);
                 for ($i = 0, $count = count($this->children); $i < $count; $i++) {
                     if (!$this->children[$i]->validate(PackageValidationManager::VALIDATION_EXCLUSION)) {
                         return false;
                     }
                 }
             } catch (PackageValidationException $e) {
                 $this->exception = $e;
                 return false;
             }
         }
     }
     return true;
 }