Welcome to mirror list, hosted at ThFree Co, Russian Federation.

github.com/matomo-org/matomo.git - Unnamed repository; edit this file 'description' to name the repository.
summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
Diffstat (limited to 'plugins/CoreUpdater/Updater.php')
-rw-r--r--plugins/CoreUpdater/Updater.php272
1 files changed, 272 insertions, 0 deletions
diff --git a/plugins/CoreUpdater/Updater.php b/plugins/CoreUpdater/Updater.php
new file mode 100644
index 0000000000..1adcbc6cc0
--- /dev/null
+++ b/plugins/CoreUpdater/Updater.php
@@ -0,0 +1,272 @@
+<?php
+/**
+ * Piwik - free/libre analytics platform
+ *
+ * @link http://piwik.org
+ * @license http://www.gnu.org/licenses/gpl-3.0.html GPL v3 or later
+ */
+
+namespace Piwik\Plugins\CoreUpdater;
+
+use Exception;
+use Piwik\ArchiveProcessor\Rules;
+use Piwik\Config;
+use Piwik\Filechecks;
+use Piwik\Filesystem;
+use Piwik\Http;
+use Piwik\Option;
+use Piwik\Plugin\Manager as PluginManager;
+use Piwik\SettingsServer;
+use Piwik\Translation\Translator;
+use Piwik\Unzip;
+use Piwik\Version;
+
+class Updater
+{
+ const OPTION_LATEST_VERSION = 'UpdateCheck_LatestVersion';
+ const PATH_TO_EXTRACT_LATEST_VERSION = '/latest/';
+ const LATEST_VERSION_URL = '://builds.piwik.org/piwik.zip';
+ const LATEST_BETA_VERSION_URL = '://builds.piwik.org/piwik-%s.zip';
+ const DOWNLOAD_TIMEOUT = 120;
+
+ /**
+ * @var Translator
+ */
+ private $translator;
+
+ /**
+ * @var string
+ */
+ private $tmpPath;
+
+ public function __construct(Translator $translator, $tmpPath)
+ {
+ $this->translator = $translator;
+ $this->tmpPath = $tmpPath;
+ }
+
+ /**
+ * Returns the latest available version number. Does not perform a check whether a later version is available.
+ *
+ * @return false|string
+ */
+ public function getLatestVersion()
+ {
+ return Option::get(self::OPTION_LATEST_VERSION);
+ }
+
+ /**
+ * @return bool
+ */
+ public function isNewVersionAvailable()
+ {
+ $latestVersion = self::getLatestVersion();
+ return $latestVersion && version_compare(Version::VERSION, $latestVersion) === -1;
+ }
+
+ /**
+ * @return bool
+ */
+ public function isUpdatingOverHttps()
+ {
+ $openSslEnabled = extension_loaded('openssl');
+ $usingMethodSupportingHttps = (Http::getTransportMethod() !== 'socket');
+
+ return $openSslEnabled && $usingMethodSupportingHttps;
+ }
+
+ /**
+ * Update Piwik codebase by downloading and installing the latest version.
+ *
+ * @param bool $https Whether to use HTTPS if supported of not. If false, will use HTTP.
+ * @return string[] Return an array of messages for the user.
+ * @throws ArchiveDownloadException
+ * @throws UpdaterException
+ * @throws Exception
+ */
+ public function updatePiwik($https = true)
+ {
+ if (!$this->isNewVersionAvailable()) {
+ throw new Exception($this->translator->translate('CoreUpdater_ExceptionAlreadyLatestVersion', Version::VERSION));
+ }
+
+ SettingsServer::setMaxExecutionTime(0);
+
+ $newVersion = $this->getLatestVersion();
+ $url = $this->getArchiveUrl($newVersion, $https);
+ $messages = array();
+
+ try {
+ $archiveFile = $this->downloadArchive($newVersion, $url);
+ $messages[] = $this->translator->translate('CoreUpdater_DownloadingUpdateFromX', $url);
+
+ $extractedArchiveDirectory = $this->decompressArchive($archiveFile);
+ $messages[] = $this->translator->translate('CoreUpdater_UnpackingTheUpdate');
+
+ $this->verifyDecompressedArchive($extractedArchiveDirectory);
+ $messages[] = $this->translator->translate('CoreUpdater_VerifyingUnpackedFiles');
+
+ $disabledPluginNames = $this->disableIncompatiblePlugins($newVersion);
+ if (!empty($disabledPluginNames)) {
+ $messages[] = $this->translator->translate('CoreUpdater_DisablingIncompatiblePlugins', implode(', ', $disabledPluginNames));
+ }
+
+ $this->installNewFiles($extractedArchiveDirectory);
+ $messages[] = $this->translator->translate('CoreUpdater_InstallingTheLatestVersion');
+ } catch (Exception $e) {
+ throw new UpdaterException($e, $messages);
+ }
+
+ return $messages;
+ }
+
+ private function downloadArchive($version, $url)
+ {
+ $path = $this->tmpPath . self::PATH_TO_EXTRACT_LATEST_VERSION;
+ $archiveFile = $path . 'latest.zip';
+
+ Filechecks::dieIfDirectoriesNotWritable(array($path));
+
+ $url .= '?cb=' . $version;
+
+ try {
+ Http::fetchRemoteFile($url, $archiveFile, 0, self::DOWNLOAD_TIMEOUT);
+ } catch (Exception $e) {
+ // We throw a specific exception allowing to offer HTTP download if HTTPS failed
+ throw new ArchiveDownloadException($e);
+ }
+
+ return $archiveFile;
+ }
+
+ private function decompressArchive($archiveFile)
+ {
+ $extractionPath = $this->tmpPath . self::PATH_TO_EXTRACT_LATEST_VERSION;
+
+ $extractedArchiveDirectory = $extractionPath . 'piwik';
+
+ // Remove previous decompressed archive
+ if (file_exists($extractedArchiveDirectory)) {
+ Filesystem::unlinkRecursive($extractedArchiveDirectory, true);
+ }
+
+ $archive = Unzip::factory('PclZip', $archiveFile);
+ $archiveFiles = $archive->extract($extractionPath);
+
+ if (0 == $archiveFiles) {
+ throw new Exception($this->translator->translate('CoreUpdater_ExceptionArchiveIncompatible', $archive->errorInfo()));
+ }
+
+ if (0 == count($archiveFiles)) {
+ throw new Exception($this->translator->translate('CoreUpdater_ExceptionArchiveEmpty'));
+ }
+
+ unlink($archiveFile);
+
+ return $extractedArchiveDirectory;
+ }
+
+ private function verifyDecompressedArchive($extractedArchiveDirectory)
+ {
+ $someExpectedFiles = array(
+ '/config/global.ini.php',
+ '/index.php',
+ '/core/Piwik.php',
+ '/piwik.php',
+ '/plugins/API/API.php'
+ );
+ foreach ($someExpectedFiles as $file) {
+ if (!is_file($extractedArchiveDirectory . $file)) {
+ throw new Exception($this->translator->translate('CoreUpdater_ExceptionArchiveIncomplete', $file));
+ }
+ }
+ }
+
+ private function disableIncompatiblePlugins($version)
+ {
+ $incompatiblePlugins = $this->getIncompatiblePlugins($version);
+ $disabledPluginNames = array();
+
+ foreach ($incompatiblePlugins as $plugin) {
+ $name = $plugin->getPluginName();
+ PluginManager::getInstance()->deactivatePlugin($name);
+ $disabledPluginNames[] = $name;
+ }
+
+ return $disabledPluginNames;
+ }
+
+ private function installNewFiles($extractedArchiveDirectory)
+ {
+ // Make sure the execute bit is set for this shell script
+ if (!Rules::isBrowserTriggerEnabled()) {
+ @chmod($extractedArchiveDirectory . '/misc/cron/archive.sh', 0755);
+ }
+
+ $model = new Model();
+
+ /*
+ * Copy all files to PIWIK_INCLUDE_PATH.
+ * These files are accessed through the dispatcher.
+ */
+ Filesystem::copyRecursive($extractedArchiveDirectory, PIWIK_INCLUDE_PATH);
+ $model->removeGoneFiles($extractedArchiveDirectory, PIWIK_INCLUDE_PATH);
+
+ /*
+ * These files are visible in the web root and are generally
+ * served directly by the web server. May be shared.
+ */
+ if (PIWIK_INCLUDE_PATH !== PIWIK_DOCUMENT_ROOT) {
+ // Copy PHP files that expect to be in the document root
+ $specialCases = array(
+ '/index.php',
+ '/piwik.php',
+ '/js/index.php',
+ );
+
+ foreach ($specialCases as $file) {
+ Filesystem::copy($extractedArchiveDirectory . $file, PIWIK_DOCUMENT_ROOT . $file);
+ }
+
+ // Copy the non-PHP files (e.g., images, css, javascript)
+ Filesystem::copyRecursive($extractedArchiveDirectory, PIWIK_DOCUMENT_ROOT, true);
+ $model->removeGoneFiles($extractedArchiveDirectory, PIWIK_DOCUMENT_ROOT);
+ }
+
+ // Config files may be user (account) specific
+ if (PIWIK_INCLUDE_PATH !== PIWIK_USER_PATH) {
+ Filesystem::copyRecursive($extractedArchiveDirectory . '/config', PIWIK_USER_PATH . '/config');
+ }
+
+ Filesystem::unlinkRecursive($extractedArchiveDirectory, true);
+
+ Filesystem::clearPhpCaches();
+ }
+
+ /**
+ * @param string $version
+ * @param bool $https Whether to use HTTPS if supported of not. If false, will use HTTP.
+ * @return string
+ */
+ public function getArchiveUrl($version, $https = true)
+ {
+ if (@Config::getInstance()->Debug['allow_upgrades_to_beta']) {
+ $url = sprintf(self::LATEST_BETA_VERSION_URL, $version);
+ } else {
+ $url = self::LATEST_VERSION_URL;
+ }
+
+ if ($this->isUpdatingOverHttps() && $https) {
+ $url = 'https' . $url;
+ } else {
+ $url = 'http' . $url;
+ }
+
+ return $url;
+ }
+
+ private function getIncompatiblePlugins($piwikVersion)
+ {
+ return PluginManager::getInstance()->getIncompatiblePlugins($piwikVersion);
+ }
+}