/** * Builds nodes for all dependent packages. */ protected function buildDependentPackageNodes() { if (!PackageUninstallationDispatcher::hasDependencies($this->installation->queue->packageID)) { return; } $packageList = PackageUninstallationDispatcher::getOrderedPackageDependencies($this->installation->queue->packageID); $queue = $this->installation->queue; foreach ($packageList as $package) { $queue = PackageInstallationQueueEditor::create(array('processNo' => $queue->processNo, 'parentQueueID' => $queue->queueID, 'userID' => WCF::getUser()->userID, 'package' => $package->package, 'packageName' => $package->getName(), 'packageID' => $package->packageID, 'action' => 'uninstall')); // spawn nodes $uninstallation = new PackageUninstallationDispatcher($queue); $uninstallation->nodeBuilder->setParentNode($this->node); $uninstallation->nodeBuilder->buildNodes(); $this->parentNode = $uninstallation->nodeBuilder->getCurrentNode(); $this->node = $this->getToken(); } }
/** * Prepares a new package installation queue. * * @return array<integer> */ public function prepareQueue() { $processNo = PackageInstallationQueue::getNewProcessNo(); $queue = PackageInstallationQueueEditor::create(array( 'processNo' => $processNo, 'userID' => WCF::getUser()->userID, 'package' => $this->package->package, 'packageName' => $this->package->packageName, 'packageID' => $this->package->packageID, 'action' => $this->parameters['action'], 'installationType' => 'other' )); return array( 'queueID' => $queue->queueID ); }
/** * Prepares the uninstallation process. */ protected function stepPrepare() { $package = new Package($this->packageID); if (!$package->packageID) { throw new IllegalLinkException(); } // get new process no $processNo = PackageInstallationQueue::getNewProcessNo(); // create queue $queue = PackageInstallationQueueEditor::create(array('processNo' => $processNo, 'userID' => WCF::getUser()->userID, 'packageName' => $package->getName(), 'packageID' => $package->packageID, 'action' => 'uninstall', 'cancelable' => 0)); // initialize uninstallation $this->installation = new PackageUninstallationDispatcher($queue); $this->installation->nodeBuilder->purgeNodes(); $this->installation->nodeBuilder->buildNodes(); WCF::getTPL()->assign(array('queue' => $queue)); $queueID = $this->installation->nodeBuilder->getQueueByNode($queue->processNo, $this->installation->nodeBuilder->getNextNode()); $this->data = array('template' => WCF::getTPL()->fetch($this->templateName), 'step' => 'uninstall', 'node' => $this->installation->nodeBuilder->getNextNode(), 'currentAction' => WCF::getLanguage()->get('wcf.package.installation.step.uninstalling'), 'progress' => 0, 'queueID' => $queueID); }
/** * Updates queue information. */ public function updatePackage() { if (empty($this->queue->packageName)) { $queueEditor = new PackageInstallationQueueEditor($this->queue); $queueEditor->update(array( 'packageName' => $this->getArchive()->getLocalizedPackageInfo('packageName') )); // reload queue $this->queue = new PackageInstallationQueue($this->queue->queueID); } }
/** * Builds nodes for required packages, whereas each has it own node. * * @return string */ protected function buildRequirementNodes() { $queue = $this->installation->queue; // handle requirements $requiredPackages = $this->installation->getArchive()->getOpenRequirements(); foreach ($requiredPackages as $packageName => $package) { if (!isset($package['file'])) { if (isset(self::$pendingPackages[$packageName]) && (!isset($package['minversion']) || Package::compareVersion(self::$pendingPackages[$packageName], $package['minversion']) >= 0)) { // the package will already be installed and no // minversion is given or the package which will be // installed satisfies the minversion, thus we can // ignore this requirement continue; } // requirements will be checked once package is about to be installed $this->requirements[$packageName] = array('minVersion' => isset($package['minversion']) ? $package['minversion'] : '', 'packageID' => $package['packageID']); continue; } if ($this->node == '' && !empty($this->parentNode)) { $this->node = $this->parentNode; } // extract package $index = $this->installation->getArchive()->getTar()->getIndexByFilename($package['file']); if ($index === false) { // workaround for WCFSetup if (!PACKAGE_ID && $packageName == 'com.woltlab.wcf') { continue; } throw new SystemException("Unable to find required package '" . $package['file'] . "' within archive of package '" . $this->installation->queue->package . "'."); } $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 delivered package has correct identifier if ($archive->getPackageInfo('name') != $packageName) { throw new SystemException("Invalid package file delivered for '" . $packageName . "' requirement of package '" . $this->installation->getArchive()->getPackageInfo('name') . "' (delivered package: '" . $archive->getPackageInfo('name') . "')."); } // check if delivered version satisfies minversion if (isset($package['minversion']) && Package::compareVersion($package['minversion'], $archive->getPackageInfo('version')) > 0) { throw new SystemException("Package '" . $this->installation->getArchive()->getPackageInfo('name') . "' requires package '" . $packageName . "' at least in version " . $package['minversion'] . ", but only delivers version " . $archive->getPackageInfo('version') . "."); } // get package id $sql = "SELECT\tpackageID\n\t\t\t\tFROM\twcf" . WCF_N . "_package\n\t\t\t\tWHERE\tpackage = ?"; $statement = WCF::getDB()->prepareStatement($sql); $statement->execute(array($archive->getPackageInfo('name'))); $row = $statement->fetchArray(); $packageID = $row === false ? null : $row['packageID']; // check if package will already be installed if (isset(self::$pendingPackages[$packageName])) { if (Package::compareVersion(self::$pendingPackages[$packageName], $archive->getPackageInfo('version')) >= 0) { // the version to be installed satisfies the required version continue; } else { // the new delivered required version of the package has a // higher version number, thus update/replace the existing // package installation queue // todo } } // create new queue $queue = PackageInstallationQueueEditor::create(array('parentQueueID' => $queue->queueID, 'processNo' => $queue->processNo, 'userID' => WCF::getUser()->userID, 'package' => $archive->getPackageInfo('name'), 'packageID' => $packageID, 'packageName' => $archive->getLocalizedPackageInfo('packageName'), 'archive' => $fileName, 'action' => $packageID ? 'update' : 'install')); self::$pendingPackages[$archive->getPackageInfo('name')] = $archive->getPackageInfo('version'); // spawn nodes $installation = new PackageInstallationDispatcher($queue); $installation->nodeBuilder->setParentNode($this->node); $installation->nodeBuilder->buildNodes(); $this->node = $installation->nodeBuilder->getCurrentNode(); } }
/** * Uninstalls the specified package. * $package may either be the packageID or the package identifier. * * @param mixed $package */ private function uninstall($package) { if (Package::isValidPackageName($package)) { $packageID = PackageCache::getInstance()->getPackageID($package); } else { $packageID = $package; } // UninstallPackageAction::prepare() $package = new Package($packageID); if (!$package->packageID || !$package->canUninstall()) { $this->error('invalidUninstallation'); } // get new process no $processNo = PackageInstallationQueue::getNewProcessNo(); // create queue $queue = PackageInstallationQueueEditor::create(array('processNo' => $processNo, 'userID' => CLIWCF::getUser()->userID, 'packageName' => $package->getName(), 'packageID' => $package->packageID, 'action' => 'uninstall')); // initialize uninstallation $installation = new PackageUninstallationDispatcher($queue); $installation->nodeBuilder->purgeNodes(); $installation->nodeBuilder->buildNodes(); CLIWCF::getTPL()->assign(array('queue' => $queue)); $queueID = $installation->nodeBuilder->getQueueByNode($queue->processNo, $installation->nodeBuilder->getNextNode()); $step = 'uninstall'; $node = $installation->nodeBuilder->getNextNode(); $currentAction = CLIWCF::getLanguage()->get('wcf.package.installation.step.uninstalling'); $progress = 0; // initialize progressbar $progressbar = new ProgressBar(new ConsoleProgressBar(array('width' => CLIWCF::getTerminal()->getWidth(), 'elements' => array(ConsoleProgressBar::ELEMENT_PERCENT, ConsoleProgressBar::ELEMENT_BAR, ConsoleProgressBar::ELEMENT_TEXT), 'textWidth' => min(floor(CLIWCF::getTerminal()->getWidth() / 2), 50)))); // InstallPackageAction::readParameters() $finished = false; while (!$finished) { $queue = new PackageInstallationQueue($queueID); $installation = new PackageUninstallationDispatcher($queue); switch ($step) { case 'uninstall': $_node = $installation->uninstall($node); if ($_node == '') { // remove node data $installation->nodeBuilder->purgeNodes(); // UninstallPackageAction::finalize() CacheHandler::getInstance()->flushAll(); // /UninstallPackageAction::finalize() // show success $currentAction = CLIWCF::getLanguage()->get('wcf.acp.package.uninstallation.step.success'); $progress = 100; $step = 'success'; $finished = true; continue; } // continue with next node $queueID = $installation->nodeBuilder->getQueueByNode($installation->queue->processNo, $installation->nodeBuilder->getNextNode($node)); $step = 'uninstall'; $progress = $installation->nodeBuilder->calculateProgress($node); $node = $_node; } $progressbar->update($progress, $currentAction); } $progressbar->getAdapter()->finish(); }
/** * Creates a new package installation queue. * * @param string $queueType * @return array */ protected function createQueue($queueType) { if (isset($this->parameters['authData'])) { PackageUpdateServer::storeAuthData($this->parameters['authData']['packageUpdateServerID'], $this->parameters['authData']['username'], $this->parameters['authData']['password'], $this->parameters['authData']['saveCredentials']); } $scheduler = new PackageInstallationScheduler($this->parameters['packages']); try { $scheduler->buildPackageInstallationStack($queueType == 'install'); } catch (PackageUpdateUnauthorizedException $e) { return array('template' => $e->getRenderedTemplate()); } // validate exclusions if ($queueType == 'update') { $excludedPackages = $scheduler->getExcludedPackages(); if (!empty($excludedPackages)) { return array('excludedPackages' => true, 'template' => WCF::getTPL()->fetch('packageUpdateExcludedPackages', 'wcf', array('excludedPackages' => $excludedPackages))); } } $stack = $scheduler->getPackageInstallationStack(); $queueID = null; if (!empty($stack)) { $parentQueueID = 0; $processNo = PackageInstallationQueue::getNewProcessNo(); foreach ($stack as $package) { $queue = PackageInstallationQueueEditor::create(array('parentQueueID' => $parentQueueID, 'processNo' => $processNo, 'userID' => WCF::getUser()->userID, 'package' => $package['package'], 'packageName' => $package['packageName'], 'packageID' => $package['packageID'] ?: null, 'archive' => $package['archive'], 'action' => $package['action'])); $parentQueueID = $queue->queueID; if ($queueID === null) { $queueID = $queue->queueID; } } } return array('queueID' => $queueID); }
/** * @see wcf\form\IForm::save() */ public function save() { parent::save(); // get new process no $processNo = PackageInstallationQueue::getNewProcessNo(); // obey foreign key $packageID = ($this->packageID) ? $this->packageID : null; // insert queue $this->queue = PackageInstallationQueueEditor::create(array( 'processNo' => $processNo, 'userID' => WCF::getUser()->userID, 'package' => $this->archive->getPackageInfo('name'), 'packageName' => $this->archive->getLocalizedPackageInfo('packageName'), 'packageID' => $packageID, 'archive' => (!empty($this->uploadPackage['tmp_name']) ? $this->uploadPackage['name'] : $this->downloadPackage), 'action' => ($this->package != null ? 'update' : 'install'), 'confirmInstallation' => 1 )); $this->saved(); // open queue PackageInstallationDispatcher::openQueue(0, $processNo); }
/** * @see wcf\form\IForm::save() */ public function save() { parent::save(); // get new process no $processNo = PackageInstallationQueue::getNewProcessNo(); // obey foreign key $packageID = $this->packageID ? $this->packageID : null; // insert queue $this->queue = PackageInstallationQueueEditor::create(array('processNo' => $processNo, 'userID' => WCF::getUser()->userID, 'package' => $this->archive->getPackageInfo('name'), 'packageName' => $this->archive->getLocalizedPackageInfo('packageName'), 'packageID' => $packageID, 'archive' => !empty($this->uploadPackage['tmp_name']) ? $this->uploadPackage['name'] : $this->downloadPackage, 'action' => $this->package != null ? 'update' : 'install', 'confirmInstallation' => 1, 'cancelable' => $this->package != null ? 0 : 1)); $this->saved(); // open queue $url = LinkHandler::getInstance()->getLink('Package', array(), 'action=openQueue&processNo=' . $processNo); HeaderUtil::redirect($url); exit; }
/** * Builds nodes for required packages, whereas each has it own node. * * @return string */ protected function buildRequirementNodes() { $queue = $this->installation->queue; // handle requirements $requiredPackages = $this->installation->getArchive()->getOpenRequirements(); foreach ($requiredPackages as $packageName => $package) { if (!isset($package['file'])) { // requirements will be checked once package is about to be installed $this->requirements[$packageName] = array('minVersion' => isset($package['minversion']) ? $package['minversion'] : '', 'packageID' => $package['packageID']); continue; } if ($this->node == '' && !empty($this->parentNode)) { $this->node = $this->parentNode; } // extract package $index = $this->installation->getArchive()->getTar()->getIndexByFilename($package['file']); if ($index === false) { // workaround for WCFSetup if (!PACKAGE_ID && $packageName == 'com.woltlab.wcf') { continue; } 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(); // create new queue $queue = PackageInstallationQueueEditor::create(array('parentQueueID' => $queue->queueID, 'processNo' => $queue->processNo, 'userID' => WCF::getUser()->userID, 'package' => $archive->getPackageInfo('name'), 'packageName' => $archive->getLocalizedPackageInfo('packageName'), 'archive' => $fileName, 'action' => $queue->action)); // spawn nodes $installation = new PackageInstallationDispatcher($queue); $installation->nodeBuilder->setParentNode($this->node); $installation->nodeBuilder->buildNodes(); $this->node = $installation->nodeBuilder->getCurrentNode(); } }
/** * Registers with wcf setup delivered packages in the package installation queue. */ protected function installPackages() { // init database connection $this->initDB(); // get admin account $admin = new User(1); // get delivered packages $wcfPackageFile = ''; $otherPackages = array(); $tar = new Tar(SETUP_FILE); foreach ($tar->getContentList() as $file) { if ($file['type'] != 'folder' && mb_strpos($file['filename'], 'install/packages/') === 0) { $packageFile = basename($file['filename']); // ignore any files which aren't an archive if (preg_match('~\\.(tar\\.gz|tgz|tar)$~', $packageFile)) { $packageName = preg_replace('!\\.(tar\\.gz|tgz|tar)$!', '', $packageFile); if ($packageName == 'com.woltlab.wcf') { $wcfPackageFile = $packageFile; } else { $isStrato = !empty($_SERVER['DOCUMENT_ROOT']) && strpos($_SERVER['DOCUMENT_ROOT'], 'strato') !== false; if (!$isStrato && preg_match('!\\.(tar\\.gz|tgz)$!', $packageFile)) { // try to unzip zipped package files if (FileUtil::uncompressFile(TMP_DIR . 'install/packages/' . $packageFile, TMP_DIR . 'install/packages/' . $packageName . '.tar')) { @unlink(TMP_DIR . 'install/packages/' . $packageFile); $packageFile = $packageName . '.tar'; } } $otherPackages[$packageName] = $packageFile; } } } } $tar->close(); // register packages in queue // get new process id $sql = "SELECT\tMAX(processNo) AS processNo\n\t\t\tFROM\twcf" . WCF_N . "_package_installation_queue"; $statement = self::getDB()->prepareStatement($sql); $statement->execute(); $result = $statement->fetchArray(); $processNo = intval($result['processNo']) + 1; // search existing wcf package $sql = "SELECT\tCOUNT(*) AS count\n\t\t\tFROM\twcf" . WCF_N . "_package\n\t\t\tWHERE\tpackage = 'com.woltlab.wcf'"; $statement = self::getDB()->prepareStatement($sql); $statement->execute(); $row = $statement->fetchArray(); if (!$row['count']) { if (empty($wcfPackageFile)) { throw new SystemException('the essential package com.woltlab.wcf is missing.'); } // register essential wcf package $queue = PackageInstallationQueueEditor::create(array('processNo' => $processNo, 'userID' => $admin->userID, 'package' => 'com.woltlab.wcf', 'packageName' => 'WoltLab Community Framework', 'archive' => TMP_DIR . 'install/packages/' . $wcfPackageFile, 'isApplication' => 1)); } // register all other delivered packages asort($otherPackages); foreach ($otherPackages as $packageName => $packageFile) { // extract packageName from archive's package.xml $archive = new PackageArchive(TMP_DIR . 'install/packages/' . $packageFile); try { $archive->openArchive(); } catch (\Exception $e) { // we've encountered a broken archive, revert everything and then fail $sql = "SELECT\tqueueID, parentQueueID\n\t\t\t\t\tFROM\twcf" . WCF_N . "_package_installation_queue"; $statement = WCF::getDB()->prepareStatement($sql); $statement->execute(); $queues = array(); while ($row = $statement->fetchArray()) { $queues[$row['queueID']] = $row['parentQueueID']; } $queueIDs = array(); $queueID = $queue->queueID; while ($queueID) { $queueIDs[] = $queueID; $queueID = isset($queues[$queueID]) ? $queues[$queueID] : 0; } // remove previously created queues if (!empty($queueIDs)) { $sql = "DELETE FROM\twcf" . WCF_N . "_package_installation_queue\n\t\t\t\t\t\tWHERE\t\tqueueID = ?"; $statement = WCF::getDB()->prepareStatement($sql); WCF::getDB()->beginTransaction(); foreach ($queueIDs as $queueID) { $statement->execute(array($queueID)); } WCF::getDB()->commitTransaction(); } // remove package files @unlink(TMP_DIR . 'install/packages/' . $wcfPackageFile); foreach ($otherPackages as $packageFile) { @unlink(TMP_DIR . 'install/packages/' . $packageFile); } // throw exception again throw new SystemException('', 0, '', $e); } $queue = PackageInstallationQueueEditor::create(array('parentQueueID' => $queue->queueID, 'processNo' => $processNo, 'userID' => $admin->userID, 'package' => $packageName, 'packageName' => $archive->getLocalizedPackageInfo('packageName'), 'archive' => TMP_DIR . 'install/packages/' . $packageFile, 'isApplication' => 1)); } // login as admin $factory = new ACPSessionFactory(); $factory->load(); SessionHandler::getInstance()->changeUser($admin); SessionHandler::getInstance()->register('masterPassword', 1); SessionHandler::getInstance()->register('__wcfSetup_developerMode', self::$developerMode); SessionHandler::getInstance()->update(); $installPhpDeleted = @unlink('./install.php'); @unlink('./test.php'); $wcfSetupTarDeleted = @unlink('./WCFSetup.tar.gz'); // print page WCF::getTPL()->assign(array('installPhpDeleted' => $installPhpDeleted, 'wcfSetupTarDeleted' => $wcfSetupTarDeleted)); WCF::getTPL()->display('stepInstallPackages'); // delete tmp files $directory = TMP_DIR . '/'; DirectoryUtil::getInstance($directory)->removePattern(new Regex('\\.tar(\\.gz)?$'), true); }
/** * @see \wcf\form\IForm::save() */ public function save() { parent::save(); // get new process no $processNo = PackageInstallationQueue::getNewProcessNo(); // obey foreign key $packageID = $this->package ? $this->package->packageID : null; $archive = $this->downloadPackage; if ($this->stylePackageImportLocation) { $archive = $this->stylePackageImportLocation; } else { if (!empty($this->uploadPackage['tmp_name'])) { $archive = $this->uploadPackage['name']; } } // insert queue $isApplication = PackageValidationManager::getInstance()->getPackageValidationArchive()->getArchive()->getPackageInfo('isApplication'); $this->queue = PackageInstallationQueueEditor::create(array('processNo' => $processNo, 'userID' => WCF::getUser()->userID, 'package' => PackageValidationManager::getInstance()->getPackageValidationArchive()->getArchive()->getPackageInfo('name'), 'packageName' => PackageValidationManager::getInstance()->getPackageValidationArchive()->getArchive()->getLocalizedPackageInfo('packageName'), 'packageID' => $packageID, 'archive' => $archive, 'action' => $this->package != null ? 'update' : 'install', 'isApplication' => !$isApplication ? '0' : '1')); $this->saved(); // open queue PackageInstallationDispatcher::openQueue(0, $processNo); }
/** * Cancels a certain installation. */ public function cancelInstallation() { @unlink($this->queue->archive); $this->queue->delete(); return array('url' => LinkHandler::getInstance()->getLink('PackageList')); }