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
path: root/core
diff options
context:
space:
mode:
Diffstat (limited to 'core')
-rw-r--r--core/AssetManager/UIAssetMerger/StylesheetUIAssetMerger.php7
-rw-r--r--core/DataTable/Filter/ColumnDelete.php62
-rw-r--r--core/Exception/StylesheetLessCompileException.php13
-rw-r--r--core/FileIntegrity.php288
-rw-r--r--core/Filechecks.php110
-rw-r--r--core/FrontController.php30
-rw-r--r--core/Http.php4
-rw-r--r--core/SettingsPiwik.php4
-rw-r--r--core/Updates/3.0.1-b1.php24
-rw-r--r--core/Version.php2
10 files changed, 409 insertions, 135 deletions
diff --git a/core/AssetManager/UIAssetMerger/StylesheetUIAssetMerger.php b/core/AssetManager/UIAssetMerger/StylesheetUIAssetMerger.php
index de32191b6b..3d5cad72b6 100644
--- a/core/AssetManager/UIAssetMerger/StylesheetUIAssetMerger.php
+++ b/core/AssetManager/UIAssetMerger/StylesheetUIAssetMerger.php
@@ -13,6 +13,7 @@ use lessc;
use Piwik\AssetManager\UIAsset;
use Piwik\AssetManager\UIAssetMerger;
use Piwik\Common;
+use Piwik\Exception\StylesheetLessCompileException;
use Piwik\Piwik;
class StylesheetUIAssetMerger extends UIAssetMerger
@@ -41,7 +42,11 @@ class StylesheetUIAssetMerger extends UIAssetMerger
$concatenatedAssets = $this->getConcatenatedAssets();
$this->lessCompiler->setFormatter('classic');
- $compiled = $this->lessCompiler->compile($concatenatedAssets);
+ try {
+ $compiled = $this->lessCompiler->compile($concatenatedAssets);
+ } catch(\Exception $e) {
+ throw new StylesheetLessCompileException($e->getMessage());
+ }
foreach ($this->cssAssetsToReplace as $asset) {
// to fix #10173
diff --git a/core/DataTable/Filter/ColumnDelete.php b/core/DataTable/Filter/ColumnDelete.php
index 60e53fd7dd..37f0b1bf97 100644
--- a/core/DataTable/Filter/ColumnDelete.php
+++ b/core/DataTable/Filter/ColumnDelete.php
@@ -101,25 +101,8 @@ class ColumnDelete extends BaseFilter
// remove columns specified in $this->columnsToRemove
if (!empty($this->columnsToRemove)) {
- foreach ($table as $index => $row) {
- foreach ($this->columnsToRemove as $column) {
- if (!array_key_exists($column, $row)) {
- continue;
- }
-
- if ($this->deleteIfZeroOnly) {
- $value = $row[$column];
- if ($value === false || !empty($value)) {
- continue;
- }
- }
-
- unset($table[$index][$column]);
- }
- }
-
+ $this->removeColumnsFromTable($table);
$recurse = true;
-
}
// remove columns not specified in $columnsToKeep
@@ -128,6 +111,7 @@ class ColumnDelete extends BaseFilter
$columnsToDelete = array();
foreach ($row as $name => $value) {
$keep = false;
+
// @see self::APPEND_TO_COLUMN_NAME_TO_KEEP
foreach ($this->columnsToKeep as $nameKeep => $true) {
if (strpos($name, $nameKeep . self::APPEND_TO_COLUMN_NAME_TO_KEEP) === 0) {
@@ -161,4 +145,46 @@ class ColumnDelete extends BaseFilter
return $table;
}
+
+ /**
+ * @param $table
+ * @return array
+ */
+ protected function removeColumnsFromTable(&$table)
+ {
+ if(!$this->isArrayAccess($table)) {
+ return;
+ }
+ foreach ($table as $index => &$row) {
+ if(!$this->isArrayAccess($row)) {
+ continue;
+ }
+ foreach ($this->columnsToRemove as $column) {
+
+ if (!array_key_exists($column, $row)) {
+ continue;
+ }
+
+ if ($this->deleteIfZeroOnly) {
+ $value = $row[$column];
+ if ($value === false || !empty($value)) {
+ continue;
+ }
+ }
+
+ unset($table[$index][$column]);
+ }
+
+ $this->removeColumnsFromTable($row);
+ }
+ }
+
+ /**
+ * @param $table
+ * @return bool
+ */
+ protected function isArrayAccess(&$table)
+ {
+ return is_array($table) || $table instanceof \ArrayAccess;
+ }
}
diff --git a/core/Exception/StylesheetLessCompileException.php b/core/Exception/StylesheetLessCompileException.php
new file mode 100644
index 0000000000..3e3124eb77
--- /dev/null
+++ b/core/Exception/StylesheetLessCompileException.php
@@ -0,0 +1,13 @@
+<?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\Exception;
+
+class StylesheetLessCompileException extends Exception
+{
+}
diff --git a/core/FileIntegrity.php b/core/FileIntegrity.php
new file mode 100644
index 0000000000..b457f1d05f
--- /dev/null
+++ b/core/FileIntegrity.php
@@ -0,0 +1,288 @@
+<?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;
+
+use Piwik\Plugins\CustomPiwikJs\Exception\AccessDeniedException;
+use Piwik\Plugins\CustomPiwikJs\TrackerUpdater;
+
+class FileIntegrity
+{
+
+ /**
+ * Get file integrity information
+ *
+ * @return array(bool $success, array $messages)
+ */
+ public static function getFileIntegrityInformation()
+ {
+ $messages = array();
+
+ $manifest = PIWIK_INCLUDE_PATH . '/config/manifest.inc.php';
+
+ if (file_exists($manifest)) {
+ require_once $manifest;
+ }
+
+ if (!class_exists('Piwik\\Manifest')) {
+ $messages[] = Piwik::translate('General_WarningFileIntegrityNoManifest')
+ . '<br/>'
+ . Piwik::translate('General_WarningFileIntegrityNoManifestDeployingFromGit');
+
+ return array(
+ $success = false,
+ $messages
+ );
+ }
+
+ $messages = self::getMessagesFilesFoundButNotExpected($messages);
+
+ $messages = self::getMessagesFilesMismatch($messages);
+
+ return array(
+ $success = empty($messages),
+ $messages
+ );
+ }
+
+ protected static function getFilesNotInManifestButExpectedAnyway()
+ {
+ return array(
+ '*/.htaccess',
+ '*/web.config',
+ 'bootstrap.php',
+ 'favicon.ico',
+ 'robots.txt',
+ 'config/config.ini.php',
+ 'config/common.ini.php',
+ 'config/*.config.ini.php',
+ 'config/manifest.inc.php',
+ 'misc/*.dat',
+ 'misc/*.dat.gz',
+ 'misc/user/*png',
+ 'misc/package/WebAppGallery/*.xml',
+ 'misc/package/WebAppGallery/install.sql',
+ 'vendor/autoload.php',
+ 'vendor/composer/autoload_real.php',
+ 'tmp/*',
+ );
+ }
+
+
+ /**
+ * @param $messages
+ * @return array
+ */
+ protected static function getMessagesFilesFoundButNotExpected($messages)
+ {
+ $filesFoundButNotExpected = self::getFilesFoundButNotExpected();
+ if (count($filesFoundButNotExpected) > 0) {
+
+ $messageFilesToDelete = '';
+ foreach ($filesFoundButNotExpected as $fileFoundNotExpected) {
+ $messageFilesToDelete .= Piwik::translate('General_ExceptionFileToDelete', $fileFoundNotExpected) . '<br/>';
+ }
+ $messages[] = Piwik::translate('General_ExceptionUnexpectedFile')
+ . '<br/>'
+ . '--> ' . Piwik::translate('General_ExceptionUnexpectedFilePleaseDelete') . ' <--'
+ . '<br/><br/>'
+ . $messageFilesToDelete
+ . '<br/>';
+ return $messages;
+
+ }
+ return $messages;
+ }
+
+ /**
+ * Look for files which are in the filesystem, but should not be
+ *
+ * @return array
+ */
+ protected static function getFilesFoundButNotExpected()
+ {
+ $files = \Piwik\Manifest::$files;
+ $pluginsInManifest = self::getPluginsFoundInManifest();
+
+ $filesFoundButNotExpected = array();
+
+ $filesToInvestigate = array_merge(
+ // all normal files
+ Filesystem::globr('.', '*'),
+ // all hidden files
+ Filesystem::globr('.', '.*')
+ );
+ foreach ($filesToInvestigate as $file) {
+ if (is_dir($file)) {
+ continue;
+ }
+ $file = substr($file, 2); // remove starting characters ./ to match format in manifest.inc.php
+
+ if (self::isFileFromPluginNotInManifest($file, $pluginsInManifest)) {
+ continue;
+ }
+ if (self::isFileNotInManifestButExpectedAnyway($file)) {
+ continue;
+ }
+
+ if (!isset($files[$file])) {
+ $filesFoundButNotExpected[] = $file;
+ }
+ }
+
+ return $filesFoundButNotExpected;
+ }
+
+
+ protected static function getPluginsFoundInManifest()
+ {
+ $files = \Piwik\Manifest::$files;
+
+ $pluginsInManifest = array();
+ foreach($files as $file => $manifestIntegrityInfo) {
+ if(strpos($file, 'plugins/') === 0) {
+ $pluginName = self::getPluginNameFromFilepath($file);
+ $pluginsInManifest[] = $pluginName;
+ }
+ }
+ return $pluginsInManifest;
+ }
+
+ /**
+ * If a plugin folder is not tracked in the manifest then we don't try to report any files in this folder
+ * Could be a third party plugin or any plugin from the Marketplace
+ *
+ * @param $file
+ * @param $pluginsInManifest
+ * @return bool
+ */
+ protected static function isFileFromPluginNotInManifest($file, $pluginsInManifest)
+ {
+ if (strpos($file, 'plugins/') !== 0) {
+ return false;
+ }
+
+ if (substr_count($file, '/') < 2) {
+ // must be a file plugins/abc.xyz and not a plugin directory
+ return false;
+ }
+
+ $pluginName = self::getPluginNameFromFilepath($file);
+ if(in_array($pluginName, $pluginsInManifest)) {
+ return false;
+ }
+
+ return true;
+ }
+
+ protected static function isFileNotInManifestButExpectedAnyway($file)
+ {
+ $expected = self::getFilesNotInManifestButExpectedAnyway();
+ foreach ($expected as $expectedPattern) {
+ if (fnmatch($expectedPattern, $file)) {
+ return true;
+ }
+ }
+ return false;
+ }
+
+ protected static function getMessagesFilesMismatch($messages)
+ {
+ $messagesMismatch = array();
+ $hasMd5file = function_exists('md5_file');
+ $files = \Piwik\Manifest::$files;
+ $hasMd5 = function_exists('md5');
+ foreach ($files as $path => $props) {
+ $file = PIWIK_INCLUDE_PATH . '/' . $path;
+
+ if (!file_exists($file) || !is_readable($file)) {
+ $messagesMismatch[] = Piwik::translate('General_ExceptionMissingFile', $file);
+ } elseif (filesize($file) != $props[0]) {
+
+ if (self::isModifiedPathValid($path)) {
+ continue;
+ }
+
+ if (!$hasMd5 || in_array(substr($path, -4), array('.gif', '.ico', '.jpg', '.png', '.swf'))) {
+ // files that contain binary data (e.g., images) must match the file size
+ $messagesMismatch[] = Piwik::translate('General_ExceptionFilesizeMismatch', array($file, $props[0], filesize($file)));
+ } else {
+ // convert end-of-line characters and re-test text files
+ $content = @file_get_contents($file);
+ $content = str_replace("\r\n", "\n", $content);
+ if ((strlen($content) != $props[0])
+ || (@md5($content) !== $props[1])
+ ) {
+ $messagesMismatch[] = Piwik::translate('General_ExceptionFilesizeMismatch', array($file, $props[0], filesize($file)));
+ }
+ }
+ } elseif ($hasMd5file && (@md5_file($file) !== $props[1])) {
+ if (self::isModifiedPathValid($path)) {
+ continue;
+ }
+
+ $messagesMismatch[] = Piwik::translate('General_ExceptionFileIntegrity', $file);
+ }
+ }
+
+ if (!$hasMd5file) {
+ $messages[] = Piwik::translate('General_WarningFileIntegrityNoMd5file');
+ }
+
+ if (!empty($messagesMismatch)) {
+ $messages[] = Piwik::translate('General_FileIntegrityWarningReupload');
+ $messages[] = Piwik::translate('General_FileIntegrityWarningReuploadBis') . '<br/>';
+ $messages = array_merge($messages, $messagesMismatch);
+ }
+
+ return $messages;
+ }
+
+ protected static function isModifiedPathValid($path)
+ {
+ if ($path === 'piwik.js') {
+ // we could have used a postEvent hook to enrich "\Piwik\Manifest::$files;" which would also benefit plugins
+ // that want to check for file integrity but we do not want to risk to break anything right now. It is not
+ // as trivial because piwik.js might be already updated, or updated on the next request. We cannot define
+ // 2 or 3 different filesizes and md5 hashes for one file so we check it here.
+
+ if (Plugin\Manager::getInstance()->isPluginActivated('CustomPiwikJs')) {
+ $trackerUpdater = new TrackerUpdater();
+
+ if ($trackerUpdater->getCurrentTrackerFileContent() === $trackerUpdater->getUpdatedTrackerFileContent()) {
+ // file was already updated, eg manually or via custom piwik.js, this is a valid piwik.js file as
+ // it was enriched by tracker plugins
+ return true;
+ }
+
+ try {
+ // the piwik.js tracker file was not updated yet, but may be updated just after the update by
+ // one of the events CustomPiwikJs is listening to or by a scheduled task.
+ // In this case, we check whether such an update will succeed later and if it will, the file is
+ // valid as well as it will be updated on the next request
+ $trackerUpdater->checkWillSucceed();
+ return true;
+ } catch (AccessDeniedException $e) {
+ return false;
+ }
+
+ }
+ }
+
+ return false;
+ }
+
+ protected static function getPluginNameFromFilepath($file)
+ {
+ $pathRelativeToPlugins = substr($file, strlen('plugins/'));
+ $pluginName = substr($pathRelativeToPlugins, 0, strpos($pathRelativeToPlugins, '/'));
+ return $pluginName;
+ }
+
+} \ No newline at end of file
diff --git a/core/Filechecks.php b/core/Filechecks.php
index 33e65c9055..5f98fd228d 100644
--- a/core/Filechecks.php
+++ b/core/Filechecks.php
@@ -9,8 +9,6 @@
namespace Piwik;
use Piwik\Exception\MissingFilePermissionException;
-use Piwik\Plugins\CustomPiwikJs\Exception\AccessDeniedException;
-use Piwik\Plugins\CustomPiwikJs\TrackerUpdater;
class Filechecks
{
@@ -104,112 +102,6 @@ class Filechecks
throw $ex;
}
- private static function isModifiedPathValid($path)
- {
- if ($path === 'piwik.js') {
- // we could have used a postEvent hook to enrich "\Piwik\Manifest::$files;" which would also benefit plugins
- // that want to check for file integrity but we do not want to risk to break anything right now. It is not
- // as trivial because piwik.js might be already updated, or updated on the next request. We cannot define
- // 2 or 3 different filesizes and md5 hashes for one file so we check it here.
-
- if (Plugin\Manager::getInstance()->isPluginActivated('CustomPiwikJs')) {
- $trackerUpdater = new TrackerUpdater();
-
- if ($trackerUpdater->getCurrentTrackerFileContent() === $trackerUpdater->getUpdatedTrackerFileContent()) {
- // file was already updated, eg manually or via custom piwik.js, this is a valid piwik.js file as
- // it was enriched by tracker plugins
- return true;
- }
-
- try {
- // the piwik.js tracker file was not updated yet, but may be updated just after the update by
- // one of the events CustomPiwikJs is listening to or by a scheduled task.
- // In this case, we check whether such an update will succeed later and if it will, the file is
- // valid as well as it will be updated on the next request
- $trackerUpdater->checkWillSucceed();
- return true;
- } catch (AccessDeniedException $e) {
- return false;
- }
-
- }
- }
-
- return false;
- }
-
- /**
- * Get file integrity information (in PIWIK_INCLUDE_PATH).
- *
- * @return array(bool, string, ...) Return code (true/false), followed by zero or more error messages
- */
- public static function getFileIntegrityInformation()
- {
- $messages = array();
- $messages[] = true;
-
- $manifest = PIWIK_INCLUDE_PATH . '/config/manifest.inc.php';
-
- if (file_exists($manifest)) {
- require_once $manifest;
- }
-
- if (!class_exists('Piwik\\Manifest')) {
- $messages[] = Piwik::translate('General_WarningFileIntegrityNoManifest')
- . ' '
- . Piwik::translate('General_WarningFileIntegrityNoManifestDeployingFromGit');
-
- return $messages;
- }
-
- $files = \Piwik\Manifest::$files;
-
- $hasMd5file = function_exists('md5_file');
- $hasMd5 = function_exists('md5');
- foreach ($files as $path => $props) {
- $file = PIWIK_INCLUDE_PATH . '/' . $path;
-
- if (!file_exists($file) || !is_readable($file)) {
- $messages[] = Piwik::translate('General_ExceptionMissingFile', $file);
- } elseif (filesize($file) != $props[0]) {
-
- if (self::isModifiedPathValid($path)) {
- continue;
- }
-
- if (!$hasMd5 || in_array(substr($path, -4), array('.gif', '.ico', '.jpg', '.png', '.swf'))) {
- // files that contain binary data (e.g., images) must match the file size
- $messages[] = Piwik::translate('General_ExceptionFilesizeMismatch', array($file, $props[0], filesize($file)));
- } else {
- // convert end-of-line characters and re-test text files
- $content = @file_get_contents($file);
- $content = str_replace("\r\n", "\n", $content);
- if ((strlen($content) != $props[0])
- || (@md5($content) !== $props[1])
- ) {
- $messages[] = Piwik::translate('General_ExceptionFilesizeMismatch', array($file, $props[0], filesize($file)));
- }
- }
- } elseif ($hasMd5file && (@md5_file($file) !== $props[1])) {
- if (self::isModifiedPathValid($path)) {
- continue;
- }
-
- $messages[] = Piwik::translate('General_ExceptionFileIntegrity', $file);
- }
- }
-
- if (count($messages) > 1) {
- $messages[0] = false;
- }
-
- if (!$hasMd5file) {
- $messages[] = Piwik::translate('General_WarningFileIntegrityNoMd5file');
- }
-
- return $messages;
- }
-
/**
* Returns the help message when the auto update can't run because of missing permissions
*
@@ -326,4 +218,6 @@ class Filechecks
return "$user:$group";
}
+
+
}
diff --git a/core/FrontController.php b/core/FrontController.php
index 569f541dfa..c08ad5a6c2 100644
--- a/core/FrontController.php
+++ b/core/FrontController.php
@@ -15,6 +15,7 @@ use Piwik\Container\StaticContainer;
use Piwik\Exception\AuthenticationFailedException;
use Piwik\Exception\DatabaseSchemaIsNewerThanCodebaseException;
use Piwik\Exception\PluginDeactivatedException;
+use Piwik\Exception\StylesheetLessCompileException;
use Piwik\Http\ControllerResolver;
use Piwik\Http\Router;
use Piwik\Plugins\CoreAdminHome\CustomLogo;
@@ -73,11 +74,11 @@ class FrontController extends Singleton
/**
* @param $lastError
- * @return mixed|void
+ * @return string
* @throws AuthenticationFailedException
* @throws Exception
*/
- private static function generateSafeModeOutput($lastError)
+ private static function generateSafeModeOutputFromError($lastError)
{
Common::sendResponseCode(500);
@@ -94,6 +95,20 @@ class FrontController extends Singleton
}
/**
+ * @param Exception $e
+ * @return string
+ */
+ private static function generateSafeModeOutputFromException($e)
+ {
+ $error = array(
+ 'message' => $e->getMessage(),
+ 'file' => $e->getFile(),
+ 'line' => $e->getLine()
+ );
+ return self::generateSafeModeOutputFromError($error);
+ }
+
+ /**
* Executes the requested plugin controller method.
*
* @throws Exception|\Piwik\Exception\PluginDeactivatedException in case the plugin doesn't exist, the action doesn't exist,
@@ -133,6 +148,15 @@ class FrontController extends Singleton
* @param \Piwik\NoAccessException $exception The exception that was caught.
*/
Piwik::postEvent('User.isNotAuthorized', array($exception), $pending = true);
+ } catch (\Twig_Error_Runtime $e) {
+ echo $this->generateSafeModeOutputFromException($e);
+ exit;
+ } catch(StylesheetLessCompileException $e) {
+ echo $this->generateSafeModeOutputFromException($e);
+ exit;
+ } catch(\Error $e) {
+ echo $this->generateSafeModeOutputFromException($e);
+ exit;
}
}
@@ -202,7 +226,7 @@ class FrontController extends Singleton
{
$lastError = error_get_last();
if (!empty($lastError) && $lastError['type'] == E_ERROR) {
- $message = self::generateSafeModeOutput($lastError);
+ $message = self::generateSafeModeOutputFromError($lastError);
echo $message;
}
}
diff --git a/core/Http.php b/core/Http.php
index 79ee0d2af3..48e1fa14fa 100644
--- a/core/Http.php
+++ b/core/Http.php
@@ -792,9 +792,7 @@ class Http
*/
public static function configCurlCertificate(&$ch)
{
- if (file_exists(PIWIK_INCLUDE_PATH . '/core/DataFiles/cacert.pem')) {
- @curl_setopt($ch, CURLOPT_CAINFO, PIWIK_INCLUDE_PATH . '/core/DataFiles/cacert.pem');
- }
+ @curl_setopt($ch, CURLOPT_CAINFO, PIWIK_INCLUDE_PATH . '/core/DataFiles/cacert.pem');
}
public static function getUserAgent()
diff --git a/core/SettingsPiwik.php b/core/SettingsPiwik.php
index 52c4f3e16f..446712703a 100644
--- a/core/SettingsPiwik.php
+++ b/core/SettingsPiwik.php
@@ -20,7 +20,9 @@ class SettingsPiwik
const OPTION_PIWIK_URL = 'piwikUrl';
/**
- * Get salt from [General] section
+ * Get salt from [General] section. Should ONLY be used as a seed to create hashes
+ *
+ * NOTE: Keep this salt secret! Never output anywhere or share it etc.
*
* @return string
*/
diff --git a/core/Updates/3.0.1-b1.php b/core/Updates/3.0.1-b1.php
new file mode 100644
index 0000000000..6222e27b2c
--- /dev/null
+++ b/core/Updates/3.0.1-b1.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\Updates;
+
+use Piwik\Plugins\Installation\ServerFilesGenerator;
+use Piwik\Updater;
+use Piwik\Updates as PiwikUpdates;
+
+class Updates_3_0_1_b1 extends PiwikUpdates
+{
+ public function doUpdate(Updater $updater)
+ {
+ // Allow IIS to serve .woff files (https://github.com/piwik/piwik/pull/11091).
+ // Re-generate .htaccess without 'Options -Indexes' because it does not always work on some servers
+ ServerFilesGenerator::createFilesForSecurity();
+ }
+}
diff --git a/core/Version.php b/core/Version.php
index 964c773ece..4c3cef867f 100644
--- a/core/Version.php
+++ b/core/Version.php
@@ -20,7 +20,7 @@ final class Version
* The current Piwik version.
* @var string
*/
- const VERSION = '3.0.0';
+ const VERSION = '3.0.1-b1';
public function isStableVersion($version)
{