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:
authorMatthieu Aubry <matt@piwik.org>2015-03-16 07:53:20 +0300
committerMatthieu Aubry <matt@piwik.org>2015-03-16 07:53:20 +0300
commit57d3e10376bdde32e4c03c647144ee56e4f57075 (patch)
tree5937e95068de5e40f48b43f9f611f08754e3fe7b /plugins
parentccddd801de4cb76b5a95375e54f1bfcf0fe4f6c1 (diff)
parent15d69e7a7ef02508c7a89d15794f9159c95e9928 (diff)
Merge pull request #7429 from piwik/https-update
When failing to update over HTTPS, let the user update over HTTP
Diffstat (limited to 'plugins')
-rw-r--r--plugins/CoreUpdater/ArchiveDownloadException.php22
-rw-r--r--plugins/CoreUpdater/Controller.php215
-rw-r--r--plugins/CoreUpdater/Test/Fixtures/DbUpdaterTestFixture.php23
-rw-r--r--plugins/CoreUpdater/Test/Fixtures/FailUpdateHttpsFixture.php24
-rw-r--r--plugins/CoreUpdater/Test/Integration/UpdateCommunicationTest.php (renamed from plugins/CoreUpdater/tests/Integration/UpdateCommunicationTest.php)2
-rw-r--r--plugins/CoreUpdater/Test/Mock/UpdaterMock.php54
-rw-r--r--plugins/CoreUpdater/Test/Unit/ModelTest.php (renamed from plugins/CoreUpdater/tests/Unit/ModelTest.php)3
-rw-r--r--plugins/CoreUpdater/Updater.php272
-rw-r--r--plugins/CoreUpdater/UpdaterException.php37
-rw-r--r--plugins/CoreUpdater/config/config.php6
-rw-r--r--plugins/CoreUpdater/lang/en.json4
-rw-r--r--plugins/CoreUpdater/templates/newVersionAvailable.twig2
-rw-r--r--plugins/CoreUpdater/templates/oneClickResults.twig30
13 files changed, 499 insertions, 195 deletions
diff --git a/plugins/CoreUpdater/ArchiveDownloadException.php b/plugins/CoreUpdater/ArchiveDownloadException.php
new file mode 100644
index 0000000000..0b68f30c18
--- /dev/null
+++ b/plugins/CoreUpdater/ArchiveDownloadException.php
@@ -0,0 +1,22 @@
+<?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;
+
+/**
+ * Error while downloading the archive.
+ */
+class ArchiveDownloadException extends UpdaterException
+{
+ public function __construct(Exception $exception)
+ {
+ parent::__construct($exception, array());
+ }
+}
diff --git a/plugins/CoreUpdater/Controller.php b/plugins/CoreUpdater/Controller.php
index c8f3e927b5..49cb1080a2 100644
--- a/plugins/CoreUpdater/Controller.php
+++ b/plugins/CoreUpdater/Controller.php
@@ -9,10 +9,8 @@
namespace Piwik\Plugins\CoreUpdater;
use Exception;
-use Piwik\ArchiveProcessor\Rules;
use Piwik\Common;
use Piwik\Config;
-use Piwik\Container\StaticContainer;
use Piwik\DbHelper;
use Piwik\Filechecks;
use Piwik\Filesystem;
@@ -24,51 +22,34 @@ use Piwik\Plugin;
use Piwik\Plugins\CorePluginsAdmin\Marketplace;
use Piwik\Plugins\LanguagesManager\LanguagesManager;
use Piwik\SettingsServer;
-use Piwik\Unzip;
-use Piwik\UpdateCheck;
-use Piwik\Updater;
+use Piwik\Updater as DbUpdater;
use Piwik\Version;
use Piwik\View\OneClickDone;
use Piwik\View;
-/**
- *
- */
class Controller extends \Piwik\Plugin\Controller
{
- 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';
-
private $coreError = false;
private $warningMessages = array();
private $errorMessages = array();
private $deactivatedPlugins = array();
- private $pathPiwikZip = false;
- private $newVersion;
- protected static function getLatestZipUrl($newVersion)
- {
- if (@Config::getInstance()->Debug['allow_upgrades_to_beta']) {
- $url = sprintf(self::LATEST_BETA_VERSION_URL, $newVersion);
- } else {
- $url = self::LATEST_VERSION_URL;
- }
+ /**
+ * @var Updater
+ */
+ private $updater;
- if (self::isUpdatingOverHttps()) {
- $url = 'https' . $url;
- } else {
- $url = 'http' . $url;
- }
-
- return $url;
+ public function __construct(Updater $updater)
+ {
+ $this->updater = $updater;
}
public function newVersionAvailable()
{
Piwik::checkUserHasSuperUserAccess();
+ $this->checkNewVersionIsAvailableOrDie();
- $newVersion = $this->checkNewVersionIsAvailableOrDie();
+ $newVersion = $this->updater->getLatestVersion();
$view = new View('@CoreUpdater/newVersionAvailable');
$this->addCustomLogoInfo($view);
@@ -88,7 +69,7 @@ class Controller extends \Piwik\Plugin\Controller
$view->marketplacePlugins = $marketplacePlugins;
$view->incompatiblePlugins = $incompatiblePlugins;
- $view->piwik_latest_version_url = self::getLatestZipUrl($newVersion);
+ $view->piwik_latest_version_url = $this->updater->getArchiveUrl($newVersion);
$view->can_auto_update = Filechecks::canAutoUpdate();
$view->makeWritableCommands = Filechecks::getAutoUpdateMakeWritableMessage();
@@ -98,56 +79,33 @@ class Controller extends \Piwik\Plugin\Controller
public function oneClickUpdate()
{
Piwik::checkUserHasSuperUserAccess();
- $this->newVersion = $this->checkNewVersionIsAvailableOrDie();
-
- SettingsServer::setMaxExecutionTime(0);
- $url = self::getLatestZipUrl($this->newVersion);
- $steps = array(
- array('oneClick_Download', Piwik::translate('CoreUpdater_DownloadingUpdateFromX', $url)),
- array('oneClick_Unpack', Piwik::translate('CoreUpdater_UnpackingTheUpdate')),
- array('oneClick_Verify', Piwik::translate('CoreUpdater_VerifyingUnpackedFiles')),
- );
- $incompatiblePlugins = $this->getIncompatiblePlugins($this->newVersion);
- if (!empty($incompatiblePlugins)) {
- $namesToDisable = array();
- foreach ($incompatiblePlugins as $incompatiblePlugin) {
- $namesToDisable[] = $incompatiblePlugin->getPluginName();
- }
- $steps[] = array('oneClick_DisableIncompatiblePlugins', Piwik::translate('CoreUpdater_DisablingIncompatiblePlugins', implode(', ', $namesToDisable)));
- }
+ $view = new OneClickDone(Piwik::getCurrentUserTokenAuth());
- $steps[] = array('oneClick_Copy', Piwik::translate('CoreUpdater_InstallingTheLatestVersion'));
- $steps[] = array('oneClick_Finished', Piwik::translate('CoreUpdater_PiwikUpdatedSuccessfully'));
+ $useHttps = Common::getRequestVar('https', 1, 'int');
- $errorMessage = false;
- $messages = array();
- foreach ($steps as $step) {
- try {
- $method = $step[0];
- $message = $step[1];
- $this->$method();
- $messages[] = $message;
- } catch (Exception $e) {
- $errorMessage = $e->getMessage();
- break;
- }
+ try {
+ $messages = $this->updater->updatePiwik($useHttps);
+ } catch (ArchiveDownloadException $e) {
+ $view->httpsFail = $useHttps;
+ $view->error = $e->getMessage();
+ $messages = $e->getUpdateLogMessages();
+ } catch (UpdaterException $e) {
+ $view->error = $e->getMessage();
+ $messages = $e->getUpdateLogMessages();
}
- $view = new OneClickDone(Piwik::getCurrentUserTokenAuth());
- $view->coreError = $errorMessage;
$view->feedbackMessages = $messages;
-
$this->addCustomLogoInfo($view);
-
return $view->render();
}
public function oneClickResults()
{
$view = new View('@CoreUpdater/oneClickResults');
- $view->coreError = Common::getRequestVar('error', '', 'string', $_POST);
+ $view->error = Common::getRequestVar('error', '', 'string', $_POST);
$view->feedbackMessages = safe_unserialize(Common::unsanitizeInputValue(Common::getRequestVar('messages', '', 'string', $_POST)));
+ $view->httpsFail = (bool) Common::getRequestVar('httpsFail', 0, 'int', $_POST);
$this->addCustomLogoInfo($view);
return $view->render();
}
@@ -166,130 +124,9 @@ class Controller extends \Piwik\Plugin\Controller
private function checkNewVersionIsAvailableOrDie()
{
- $newVersion = UpdateCheck::isNewestVersionAvailable();
- if (!$newVersion) {
+ if (!$this->updater->isNewVersionAvailable()) {
throw new Exception(Piwik::translate('CoreUpdater_ExceptionAlreadyLatestVersion', Version::VERSION));
}
- return $newVersion;
- }
-
- private function oneClick_Download()
- {
- $path = StaticContainer::get('path.tmp') . self::PATH_TO_EXTRACT_LATEST_VERSION;
- $this->pathPiwikZip = $path . 'latest.zip';
-
- Filechecks::dieIfDirectoriesNotWritable(array($path));
-
- // we catch exceptions in the caller (i.e., oneClickUpdate)
- $url = self::getLatestZipUrl($this->newVersion) . '?cb=' . $this->newVersion;
-
- Http::fetchRemoteFile($url, $this->pathPiwikZip, 0, 120);
- }
-
- private function oneClick_Unpack()
- {
- $pathExtracted = StaticContainer::get('path.tmp') . self::PATH_TO_EXTRACT_LATEST_VERSION;
-
- $this->pathRootExtractedPiwik = $pathExtracted . 'piwik';
-
- if (file_exists($this->pathRootExtractedPiwik)) {
- Filesystem::unlinkRecursive($this->pathRootExtractedPiwik, true);
- }
-
- $archive = Unzip::factory('PclZip', $this->pathPiwikZip);
-
- if (0 == ($archive_files = $archive->extract($pathExtracted))) {
- throw new Exception(Piwik::translate('CoreUpdater_ExceptionArchiveIncompatible', $archive->errorInfo()));
- }
-
- if (0 == count($archive_files)) {
- throw new Exception(Piwik::translate('CoreUpdater_ExceptionArchiveEmpty'));
- }
- unlink($this->pathPiwikZip);
- }
-
- private function oneClick_Verify()
- {
- $someExpectedFiles = array(
- '/config/global.ini.php',
- '/index.php',
- '/core/Piwik.php',
- '/piwik.php',
- '/plugins/API/API.php'
- );
- foreach ($someExpectedFiles as $file) {
- if (!is_file($this->pathRootExtractedPiwik . $file)) {
- throw new Exception(Piwik::translate('CoreUpdater_ExceptionArchiveIncomplete', $file));
- }
- }
- }
-
- private function oneClick_DisableIncompatiblePlugins()
- {
- $plugins = $this->getIncompatiblePlugins($this->newVersion);
-
- foreach ($plugins as $plugin) {
- PluginManager::getInstance()->deactivatePlugin($plugin->getPluginName());
- }
- }
-
- private function oneClick_Copy()
- {
- /*
- * Make sure the execute bit is set for this shell script
- */
- if (!Rules::isBrowserTriggerEnabled()) {
- @chmod($this->pathRootExtractedPiwik . '/misc/cron/archive.sh', 0755);
- }
-
- $model = new Model();
-
- /*
- * Copy all files to PIWIK_INCLUDE_PATH.
- * These files are accessed through the dispatcher.
- */
- Filesystem::copyRecursive($this->pathRootExtractedPiwik, PIWIK_INCLUDE_PATH);
- $model->removeGoneFiles($this->pathRootExtractedPiwik, 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($this->pathRootExtractedPiwik . $file, PIWIK_DOCUMENT_ROOT . $file);
- }
-
- /*
- * Copy the non-PHP files (e.g., images, css, javascript)
- */
- Filesystem::copyRecursive($this->pathRootExtractedPiwik, PIWIK_DOCUMENT_ROOT, true);
- $model->removeGoneFiles($this->pathRootExtractedPiwik, PIWIK_DOCUMENT_ROOT);
- }
-
- /*
- * Config files may be user (account) specific
- */
- if (PIWIK_INCLUDE_PATH !== PIWIK_USER_PATH) {
- Filesystem::copyRecursive($this->pathRootExtractedPiwik . '/config', PIWIK_USER_PATH . '/config');
- }
-
- Filesystem::unlinkRecursive($this->pathRootExtractedPiwik, true);
-
- Filesystem::clearPhpCaches();
- }
-
- private function oneClick_Finished()
- {
}
public function index()
@@ -308,7 +145,7 @@ class Controller extends \Piwik\Plugin\Controller
public function runUpdaterAndExit($doDryRun = null)
{
- $updater = new Updater();
+ $updater = new DbUpdater();
$componentsWithUpdateFile = CoreUpdater::getComponentUpdates($updater);
if (empty($componentsWithUpdateFile)) {
throw new NoUpdatesFoundException("Everything is already up to date.");
diff --git a/plugins/CoreUpdater/Test/Fixtures/DbUpdaterTestFixture.php b/plugins/CoreUpdater/Test/Fixtures/DbUpdaterTestFixture.php
new file mode 100644
index 0000000000..89a29126f2
--- /dev/null
+++ b/plugins/CoreUpdater/Test/Fixtures/DbUpdaterTestFixture.php
@@ -0,0 +1,23 @@
+<?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\Test\Fixtures;
+
+use Piwik\Tests\Fixtures\SqlDump;
+
+class DbUpdaterTestFixture extends SqlDump
+{
+ public function performSetUp($setupEnvironmentOnly = false)
+ {
+ $this->dumpUrl = PIWIK_INCLUDE_PATH . "/tests/UI/resources/piwik1.0.sql.gz";
+ $this->dropDatabaseInSetUp = true;
+ $this->resetPersistedFixture = true;
+
+ parent::performSetUp($setupEnvironmentOnly);
+ }
+}
diff --git a/plugins/CoreUpdater/Test/Fixtures/FailUpdateHttpsFixture.php b/plugins/CoreUpdater/Test/Fixtures/FailUpdateHttpsFixture.php
new file mode 100644
index 0000000000..5060c99320
--- /dev/null
+++ b/plugins/CoreUpdater/Test/Fixtures/FailUpdateHttpsFixture.php
@@ -0,0 +1,24 @@
+<?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\Test\Fixtures;
+
+use Piwik\Tests\Framework\Fixture;
+
+/**
+ * Fixture that makes the update over HTTPS fail to be able to test that users can still update over HTTP.
+ */
+class FailUpdateHttpsFixture extends Fixture
+{
+ public function provideContainerConfig()
+ {
+ return array(
+ 'Piwik\Plugins\CoreUpdater\Updater' => \DI\object('Piwik\Plugins\CoreUpdater\Test\Mock\UpdaterMock'),
+ );
+ }
+}
diff --git a/plugins/CoreUpdater/tests/Integration/UpdateCommunicationTest.php b/plugins/CoreUpdater/Test/Integration/UpdateCommunicationTest.php
index 91b6e2c2a1..6ec5fdaf83 100644
--- a/plugins/CoreUpdater/tests/Integration/UpdateCommunicationTest.php
+++ b/plugins/CoreUpdater/Test/Integration/UpdateCommunicationTest.php
@@ -6,7 +6,7 @@
* @license http://www.gnu.org/licenses/gpl-3.0.html GPL v3 or later
*/
-namespace Piwik\Plugins\CoreUpdater\tests;
+namespace Piwik\Plugins\CoreUpdater\Test;
use Piwik\Config;
use Piwik\Option;
diff --git a/plugins/CoreUpdater/Test/Mock/UpdaterMock.php b/plugins/CoreUpdater/Test/Mock/UpdaterMock.php
new file mode 100644
index 0000000000..7a73129c5b
--- /dev/null
+++ b/plugins/CoreUpdater/Test/Mock/UpdaterMock.php
@@ -0,0 +1,54 @@
+<?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\Test\Mock;
+
+use Piwik\Plugins\CoreUpdater\ArchiveDownloadException;
+use Piwik\Plugins\CoreUpdater\Updater;
+use Piwik\Translation\Translator;
+
+class UpdaterMock extends Updater
+{
+ /**
+ * @var Translator
+ */
+ private $translator;
+
+ public function __construct(Translator $translator)
+ {
+ $this->translator = $translator;
+ }
+
+ public function getLatestVersion()
+ {
+ return '4.0.0';
+ }
+
+ public function isNewVersionAvailable()
+ {
+ return true;
+ }
+
+ public function updatePiwik($https = true)
+ {
+ // Simulate that the update over HTTPS fails
+ if ($https) {
+ // The actual error message depends on the OS, the HTTP method etc.
+ // This is what I get on my machine, but it doesn't really matter
+ throw new ArchiveDownloadException(new \Exception('curl_exec: SSL certificate problem: Invalid certificate chain. Hostname requested was: piwik.org'), array());
+ }
+
+ // Simulate that the update over HTTP succeeds
+ return array(
+ $this->translator->translate('CoreUpdater_DownloadingUpdateFromX', ''),
+ $this->translator->translate('CoreUpdater_UnpackingTheUpdate'),
+ $this->translator->translate('CoreUpdater_VerifyingUnpackedFiles'),
+ $this->translator->translate('CoreUpdater_InstallingTheLatestVersion'),
+ );
+ }
+}
diff --git a/plugins/CoreUpdater/tests/Unit/ModelTest.php b/plugins/CoreUpdater/Test/Unit/ModelTest.php
index 3098595b77..521aa8fc35 100644
--- a/plugins/CoreUpdater/tests/Unit/ModelTest.php
+++ b/plugins/CoreUpdater/Test/Unit/ModelTest.php
@@ -6,7 +6,8 @@
* @license http://www.gnu.org/licenses/gpl-3.0.html GPL v3 or later
*/
-namespace Piwik\Plugins\CoreUpdater\tests;
+namespace Piwik\Plugins\CoreUpdater\Test\Unit;
+
use Piwik\Plugins\CoreUpdater\Model;
/**
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);
+ }
+}
diff --git a/plugins/CoreUpdater/UpdaterException.php b/plugins/CoreUpdater/UpdaterException.php
new file mode 100644
index 0000000000..bd4599b111
--- /dev/null
+++ b/plugins/CoreUpdater/UpdaterException.php
@@ -0,0 +1,37 @@
+<?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;
+
+/**
+ * Exception during the updating of Piwik to a new version.
+ */
+class UpdaterException extends Exception
+{
+ /**
+ * @var string[]
+ */
+ private $updateLogMessages;
+
+ public function __construct(Exception $exception, array $updateLogMessages)
+ {
+ parent::__construct($exception->getMessage(), 0, $exception);
+
+ $this->updateLogMessages = $updateLogMessages;
+ }
+
+ /**
+ * @return string[]
+ */
+ public function getUpdateLogMessages()
+ {
+ return $this->updateLogMessages;
+ }
+}
diff --git a/plugins/CoreUpdater/config/config.php b/plugins/CoreUpdater/config/config.php
new file mode 100644
index 0000000000..419e43cc7b
--- /dev/null
+++ b/plugins/CoreUpdater/config/config.php
@@ -0,0 +1,6 @@
+<?php
+
+return array(
+ 'Piwik\Plugins\CoreUpdater\Updater' => DI\object()
+ ->constructorParameter('tmpPath', DI\link('path.tmp')),
+);
diff --git a/plugins/CoreUpdater/lang/en.json b/plugins/CoreUpdater/lang/en.json
index 67bc20f27b..2c3ffb9be2 100644
--- a/plugins/CoreUpdater/lang/en.json
+++ b/plugins/CoreUpdater/lang/en.json
@@ -47,6 +47,10 @@
"UpdateAutomatically": "Update Automatically",
"UpdateHasBeenCancelledExplanation": "Piwik One Click Update has been cancelled. If you can't fix the above error message, it is recommended that you manually update Piwik. %1$s Please check out the %2$sUpdate documentation%3$s to get started!",
"UpdateTitle": "Update",
+ "UpdateUsingHttpsFailed": "Downloading the latest Piwik version over secure HTTPS connection did not succeed, because of the following error:",
+ "UpdateUsingHttpsFailedHelp": "Please note that downloading the latest Piwik version (over secure HTTPS connection) can fail for various reasons, for example in case of network error, slow network speed, or wrong system configuration.",
+ "UpdateUsingHttpsFailedHelpWhatToDo": "You may continue the update via the non-secure standard HTTP connection by clicking on the button '%s'.",
+ "UpdateUsingHttpMessage": "Update Piwik automatically (over the non secure HTTP connection)",
"UpgradeComplete": "Upgrade complete!",
"UpgradePiwik": "Upgrade Piwik",
"VerifyingUnpackedFiles": "Verifying the unpacked files",
diff --git a/plugins/CoreUpdater/templates/newVersionAvailable.twig b/plugins/CoreUpdater/templates/newVersionAvailable.twig
index cf87a949e6..8691d97595 100644
--- a/plugins/CoreUpdater/templates/newVersionAvailable.twig
+++ b/plugins/CoreUpdater/templates/newVersionAvailable.twig
@@ -29,7 +29,7 @@
<form id="oneclickupdate" action="index.php">
<input type="hidden" name="module" value="CoreUpdater"/>
<input type="hidden" name="action" value="oneClickUpdate"/>
- <input type="submit" class="submit" value="{{ 'CoreUpdater_UpdateAutomatically'|translate }}"/>
+ <input id="updateAutomatically" type="submit" class="submit" value="{{ 'CoreUpdater_UpdateAutomatically'|translate }}"/>
{% endif %}
<a style="margin-left:50px;" class="submit button"
href="{{ piwik_latest_version_url }}?cb={{ piwik_new_version }}">{{ 'CoreUpdater_DownloadX'|translate(piwik_new_version) }}</a><br/>
diff --git a/plugins/CoreUpdater/templates/oneClickResults.twig b/plugins/CoreUpdater/templates/oneClickResults.twig
index d08378d6c3..8ded5fd604 100644
--- a/plugins/CoreUpdater/templates/oneClickResults.twig
+++ b/plugins/CoreUpdater/templates/oneClickResults.twig
@@ -3,13 +3,37 @@
{% block content %}
<br/>
{% for message in feedbackMessages %}
-<p>&#10003; {{ message }}</p>
+ <p>&#10003; {{ message }}</p>
{% endfor %}
-{% if coreError %}
+{% if httpsFail %}
<br/>
<br/>
- <div class="error"><img src="plugins/Morpheus/images/error_medium.png"/> {{ coreError }}</div>
+ <div class="warning">
+ <img src="plugins/Morpheus/images/warning_medium.png"/>
+ {{ 'CoreUpdater_UpdateUsingHttpsFailed'|translate }}<br/>
+ "{{ error }}"
+ </div>
+ <p>{{ 'CoreUpdater_UpdateUsingHttpsFailedHelp'|translate }}</p>
+ <p>{{ 'CoreUpdater_UpdateUsingHttpsFailedHelpWhatToDo'|translate('CoreUpdater_UpdateAutomatically'|translate) }}</p>
+ <div class="warning">
+ {{ 'CoreUpdater_UpdateUsingHttpMessage'|translate }}
+ <form id="oneclickupdate" action="index.php">
+ <input type="hidden" name="module" value="CoreUpdater"/>
+ <input type="hidden" name="action" value="oneClickUpdate"/>
+ <input type="hidden" name="https" value="0"/>
+ <input id="updateUsingHttp" type="submit" class="submit" value="{{ 'CoreUpdater_UpdateAutomatically'|translate }}"/>
+ </form>
+ </div>
+ <br/>
+ <br/>
+{% elseif error %}
+ <br/>
+ <br/>
+ <div class="error">
+ <img src="plugins/Morpheus/images/error_medium.png"/>
+ {{ error }}
+ </div>
<br/>
<br/>
<div class="warning">