PluginProbe ʕ •ᴥ•ʔ
Matomo Analytics – Powerful, Privacy-First Insights for WordPress / 5.2.0
Matomo Analytics – Powerful, Privacy-First Insights for WordPress v5.2.0
5.11.1 5.11.0 5.10.2 5.10.1 trunk 1.0.2 1.0.3 1.0.4 1.0.5 1.0.6 1.1.0 1.1.1 1.1.2 1.1.3 1.2.0 1.3.0 1.3.1 1.3.2 4.0.0 4.0.1 4.0.2 4.0.3 4.0.4 4.1.0 4.1.1 4.1.2 4.1.3 4.10.0 4.11.0 4.12.0 4.13.0 4.13.2 4.13.3 4.13.4 4.13.5 4.14.0 4.14.1 4.14.2 4.15.0 4.15.1 4.15.2 4.15.3 4.2.0 4.3.0 4.3.1 4.4.1 4.4.2 4.5.0 4.6.0 5.0.1 5.0.2 5.0.3 5.0.4 5.0.5 5.0.6 5.0.7 5.0.8 5.1.0 5.1.1 5.1.2 5.1.3 5.1.4 5.1.5 5.1.6 5.1.7 5.10.0 5.2.0 5.2.1 5.2.2 5.3.0 5.3.1 5.3.2 5.3.3 5.6.0 5.6.1 5.7.0 5.7.1 5.8.0 5.8.1 5.8.2
matomo / app / core / FrontController.php
matomo / app / core Last commit date
API 1 year ago Access 1 year ago Application 1 year ago Archive 1 year ago ArchiveProcessor 1 year ago Archiver 2 years ago AssetManager 1 year ago Auth 1 year ago Category 2 years ago Changes 1 year ago CliMulti 1 year ago Columns 1 year ago Concurrency 1 year ago Config 1 year ago Container 1 year ago CronArchive 1 year ago DataAccess 1 year ago DataFiles 2 years ago DataTable 1 year ago Db 1 year ago DeviceDetector 1 year ago Email 2 years ago Exception 1 year ago Http 1 year ago Intl 1 year ago Log 2 years ago Mail 1 year ago Measurable 1 year ago Menu 1 year ago Metrics 1 year ago Notification 1 year ago Period 1 year ago Plugin 1 year ago ProfessionalServices 1 year ago Report 1 year ago ReportRenderer 1 year ago Scheduler 1 year ago Segment 1 year ago Session 1 year ago Settings 1 year ago Tracker 1 year ago Translation 1 year ago Twig 1 year ago UpdateCheck 1 year ago Updater 1 year ago Updates 1 year ago Validators 1 year ago View 1 year ago ViewDataTable 1 year ago Visualization 1 year ago Widget 1 year ago .htaccess 2 years ago Access.php 1 year ago Archive.php 1 year ago ArchiveProcessor.php 1 year ago AssetManager.php 1 year ago Auth.php 2 years ago AuthResult.php 2 years ago BaseFactory.php 2 years ago Cache.php 2 years ago CacheId.php 1 year ago CliMulti.php 1 year ago Common.php 1 year ago Config.php 1 year ago Console.php 1 year ago Context.php 2 years ago Cookie.php 1 year ago CronArchive.php 1 year ago DI.php 1 year ago DataArray.php 1 year ago DataTable.php 1 year ago Date.php 1 year ago Db.php 1 year ago DbHelper.php 1 year ago Development.php 1 year ago ErrorHandler.php 1 year ago EventDispatcher.php 1 year ago ExceptionHandler.php 1 year ago FileIntegrity.php 1 year ago Filechecks.php 1 year ago Filesystem.php 1 year ago FrontController.php 1 year ago Http.php 1 year ago IP.php 1 year ago Log.php 2 years ago LogDeleter.php 1 year ago Mail.php 1 year ago Metrics.php 1 year ago NoAccessException.php 2 years ago Nonce.php 1 year ago Notification.php 1 year ago NumberFormatter.php 1 year ago Option.php 1 year ago Period.php 1 year ago Piwik.php 1 year ago Plugin.php 1 year ago Process.php 1 year ago Profiler.php 1 year ago ProxyHeaders.php 2 years ago ProxyHttp.php 1 year ago QuickForm2.php 1 year ago RankingQuery.php 1 year ago ReportRenderer.php 1 year ago Request.php 1 year ago Segment.php 1 year ago Sequence.php 2 years ago Session.php 1 year ago SettingsPiwik.php 1 year ago SettingsServer.php 1 year ago Singleton.php 2 years ago Site.php 1 year ago SiteContentDetector.php 1 year ago SupportedBrowser.php 2 years ago TCPDF.php 1 year ago Theme.php 1 year ago Timer.php 2 years ago Tracker.php 1 year ago Twig.php 1 year ago Unzip.php 1 year ago UpdateCheck.php 1 year ago Updater.php 1 year ago UpdaterErrorException.php 2 years ago Updates.php 1 year ago Url.php 1 year ago UrlHelper.php 1 year ago Version.php 1 year ago View.php 1 year ago bootstrap.php 1 year ago dispatch.php 2 years ago testMinimumPhpVersion.php 2 years ago
FrontController.php
663 lines
1 <?php
2
3 /**
4 * Matomo - free/libre analytics platform
5 *
6 * @link https://matomo.org
7 * @license https://www.gnu.org/licenses/gpl-3.0.html GPL v3 or later
8 */
9 namespace Piwik;
10
11 use Exception;
12 use Piwik\API\Request;
13 use Piwik\Config\GeneralConfig;
14 use Piwik\Container\StaticContainer;
15 use Piwik\DataTable\Manager;
16 use Piwik\Exception\AuthenticationFailedException;
17 use Piwik\Exception\DatabaseSchemaIsNewerThanCodebaseException;
18 use Piwik\Exception\PluginDeactivatedException;
19 use Piwik\Exception\PluginRequiresInternetException;
20 use Piwik\Exception\StylesheetLessCompileException;
21 use Piwik\Http\ControllerResolver;
22 use Piwik\Http\Router;
23 use Piwik\Plugins\CoreAdminHome\CustomLogo;
24 use Piwik\Session\SessionAuth;
25 use Piwik\Session\SessionInitializer;
26 use Piwik\SupportedBrowser;
27 use Piwik\Log\LoggerInterface;
28 /**
29 * This singleton dispatches requests to the appropriate plugin Controller.
30 *
31 * Piwik uses this class for all requests that go through **index.php**. Plugins can
32 * use it to call controller actions of other plugins.
33 *
34 * ### Examples
35 *
36 * **Forwarding controller requests**
37 *
38 * public function myConfiguredRealtimeMap()
39 * {
40 * $_GET['changeVisitAlpha'] = false;
41 * $_GET['removeOldVisits'] = false;
42 * $_GET['showFooterMessage'] = false;
43 * return FrontController::getInstance()->dispatch('UserCountryMap', 'realtimeMap');
44 * }
45 *
46 * **Using other plugin controller actions**
47 *
48 * public function myPopupWithRealtimeMap()
49 * {
50 * $_GET['changeVisitAlpha'] = false;
51 * $_GET['removeOldVisits'] = false;
52 * $_GET['showFooterMessage'] = false;
53 * $realtimeMap = FrontController::getInstance()->dispatch('UserCountryMap', 'realtimeMap');
54 *
55 * $view = new View('@MyPlugin/myPopupWithRealtimeMap.twig');
56 * $view->realtimeMap = $realtimeMap;
57 * return $realtimeMap->render();
58 * }
59 *
60 * For a detailed explanation, see the documentation [here](https://developer.piwik.org/guides/how-piwik-works).
61 *
62 * @method static \Piwik\FrontController getInstance()
63 */
64 class FrontController extends \Piwik\Singleton
65 {
66 public const DEFAULT_MODULE = 'CoreHome';
67 public const DEFAULT_LOGIN = 'anonymous';
68 public const DEFAULT_TOKEN_AUTH = 'anonymous';
69 // public for tests
70 public static $requestId = null;
71 /**
72 * Set to false and the Front Controller will not dispatch the request
73 *
74 * @var bool
75 */
76 public static $enableDispatch = \true;
77 /**
78 * @var bool
79 */
80 private $initialized = \false;
81 /**
82 * @param $lastError
83 * @return string
84 * @throws AuthenticationFailedException
85 * @throws Exception
86 */
87 private static function generateSafeModeOutputFromError($lastError)
88 {
89 \Piwik\Common::sendResponseCode(500);
90 $controller = \Piwik\FrontController::getInstance();
91 try {
92 $controller->init();
93 $message = $controller->dispatch('CorePluginsAdmin', 'safemode', array($lastError));
94 } catch (Exception $e) {
95 // may fail in safe mode (eg. global.ini.php not found)
96 $message = sprintf("Matomo encountered an error: %s (which lead to: %s)", $lastError['message'], $e->getMessage());
97 }
98 return $message;
99 }
100 /**
101 * @param Exception $e
102 * @return string
103 */
104 public static function generateSafeModeOutputFromException($e)
105 {
106 StaticContainer::get(LoggerInterface::class)->error('Uncaught exception: {exception}', ['exception' => $e, 'ignoreInScreenWriter' => \true]);
107 $error = array('message' => $e->getMessage(), 'file' => $e->getFile(), 'line' => $e->getLine());
108 if (isset(self::$requestId)) {
109 $error['request_id'] = self::$requestId;
110 }
111 $error['backtrace'] = ' on ' . $error['file'] . '(' . $error['line'] . ")\n";
112 $error['backtrace'] .= $e->getTraceAsString();
113 $exception = $e;
114 while ($exception = $exception->getPrevious()) {
115 $error['backtrace'] .= "\ncaused by: " . $exception->getMessage();
116 $error['backtrace'] .= ' on ' . $exception->getFile() . '(' . $exception->getLine() . ")\n";
117 $error['backtrace'] .= $exception->getTraceAsString();
118 }
119 return self::generateSafeModeOutputFromError($error);
120 }
121 /**
122 * Executes the requested plugin controller method.
123 *
124 * @throws Exception|\Piwik\Exception\PluginDeactivatedException in case the plugin doesn't exist, the action doesn't exist,
125 * there is not enough permission, etc.
126 *
127 * @param string $module The name of the plugin whose controller to execute, eg, `'UserCountryMap'`.
128 * @param string $action The controller method name, eg, `'realtimeMap'`.
129 * @param array $parameters Array of parameters to pass to the controller method.
130 * @return void|mixed The returned value of the call. This is the output of the controller method.
131 * @api
132 */
133 public function dispatch($module = null, $action = null, $parameters = null)
134 {
135 if (self::$enableDispatch === \false) {
136 return;
137 }
138 $filter = new Router();
139 $redirection = $filter->filterUrl(\Piwik\Url::getCurrentUrl());
140 if ($redirection !== null) {
141 \Piwik\Url::redirectToUrl($redirection);
142 return;
143 }
144 try {
145 $result = $this->doDispatch($module, $action, $parameters);
146 return $result;
147 } catch (\Piwik\NoAccessException $exception) {
148 \Piwik\Log::debug($exception);
149 /**
150 * Triggered when a user with insufficient access permissions tries to view some resource.
151 *
152 * This event can be used to customize the error that occurs when a user is denied access
153 * (for example, displaying an error message, redirecting to a page other than login, etc.).
154 *
155 * @param \Piwik\NoAccessException $exception The exception that was caught.
156 */
157 \Piwik\Piwik::postEvent('User.isNotAuthorized', array($exception), $pending = \true);
158 } catch (\Matomo\Dependencies\Twig\Error\RuntimeError $e) {
159 if ($e->getPrevious() && !$e->getPrevious() instanceof \Matomo\Dependencies\Twig\Error\RuntimeError) {
160 // a regular exception unrelated to twig was triggered while rendering an a view, for example as part of a triggered event
161 // we want to ensure to show the regular error message response instead of the safemode as it's likely wrong user input
162 throw $e;
163 } else {
164 echo $this->generateSafeModeOutputFromException($e);
165 exit;
166 }
167 } catch (StylesheetLessCompileException $e) {
168 echo $this->generateSafeModeOutputFromException($e);
169 exit;
170 } catch (\Error $e) {
171 echo $this->generateSafeModeOutputFromException($e);
172 exit;
173 }
174 }
175 /**
176 * Executes the requested plugin controller method and returns the data, capturing anything the
177 * method `echo`s.
178 *
179 * _Note: If the plugin controller returns something, the return value is returned instead
180 * of whatever is in the output buffer._
181 *
182 * @param string $module The name of the plugin whose controller to execute, eg, `'UserCountryMap'`.
183 * @param string $actionName The controller action name, eg, `'realtimeMap'`.
184 * @param array $parameters Array of parameters to pass to the controller action method.
185 * @return string The `echo`'d data or the return value of the controller action.
186 */
187 public function fetchDispatch($module = null, $actionName = null, $parameters = null)
188 {
189 ob_start();
190 $output = $this->dispatch($module, $actionName, $parameters);
191 // if nothing returned we try to load something that was printed on the screen
192 if (empty($output)) {
193 $output = ob_get_contents();
194 } else {
195 // if something was returned, flush output buffer as it is meant to be written to the screen
196 ob_flush();
197 }
198 ob_end_clean();
199 return $output;
200 }
201 /**
202 * Called at the end of the page generation
203 */
204 public function __destruct()
205 {
206 try {
207 if (class_exists('Piwik\\Profiler') && !\Piwik\SettingsServer::isTrackerApiRequest()) {
208 // in tracker mode Piwik\Tracker\Db\Pdo\Mysql does currently not implement profiling
209 \Piwik\Profiler::displayDbProfileReport();
210 \Piwik\Profiler::printQueryCount();
211 }
212 } catch (Exception $e) {
213 \Piwik\Log::debug($e);
214 }
215 }
216 // Should we show exceptions messages directly rather than display an html error page?
217 public static function shouldRethrowException()
218 {
219 // If we are in no dispatch mode, eg. a script reusing Piwik libs,
220 // then we should return the exception directly, rather than trigger the event "bad config file"
221 // which load the HTML page of the installer with the error.
222 return defined('PIWIK_ENABLE_DISPATCH') && !PIWIK_ENABLE_DISPATCH || \Piwik\Common::isPhpCliMode() || \Piwik\SettingsServer::isArchivePhpTriggered();
223 }
224 public static function setUpSafeMode()
225 {
226 register_shutdown_function(array('\\Piwik\\FrontController', 'triggerSafeModeWhenError'));
227 }
228 public static function triggerSafeModeWhenError()
229 {
230 Manager::getInstance()->deleteAll();
231 $lastError = error_get_last();
232 if (!empty($lastError) && isset(self::$requestId)) {
233 $lastError['request_id'] = self::$requestId;
234 }
235 if (!empty($lastError) && $lastError['type'] == \E_ERROR) {
236 $lastError['backtrace'] = ' on ' . $lastError['file'] . '(' . $lastError['line'] . ")\n" . \Piwik\ErrorHandler::getFatalErrorPartialBacktrace();
237 StaticContainer::get(LoggerInterface::class)->error('Fatal error encountered: {exception}', ['exception' => $lastError, 'ignoreInScreenWriter' => \true]);
238 $message = self::generateSafeModeOutputFromError($lastError);
239 echo $message;
240 }
241 }
242 /**
243 * Must be called before dispatch()
244 * - checks that directories are writable,
245 * - loads the configuration file,
246 * - loads the plugin,
247 * - inits the DB connection,
248 * - etc.
249 *
250 * @throws Exception
251 * @return void
252 */
253 public function init()
254 {
255 if ($this->initialized) {
256 return;
257 }
258 self::setRequestIdHeader();
259 $this->initialized = \true;
260 $tmpPath = StaticContainer::get('path.tmp');
261 $directoriesToCheck = array($tmpPath, $tmpPath . '/assets/', $tmpPath . '/cache/', $tmpPath . '/logs/', $tmpPath . '/tcpdf/', StaticContainer::get('path.tmp.templates'));
262 \Piwik\Filechecks::dieIfDirectoriesNotWritable($directoriesToCheck);
263 $this->handleMaintenanceMode();
264 $this->handleProfiler();
265 $this->handleSSLRedirection();
266 \Piwik\Plugin\Manager::getInstance()->loadPluginTranslations();
267 \Piwik\Plugin\Manager::getInstance()->loadActivatedPlugins();
268 // try to connect to the database
269 try {
270 \Piwik\Db::createDatabaseObject();
271 \Piwik\Db::fetchAll("SELECT DATABASE()");
272 } catch (Exception $exception) {
273 if (self::shouldRethrowException()) {
274 throw $exception;
275 }
276 \Piwik\Log::debug($exception);
277 /**
278 * Triggered when Piwik cannot connect to the database.
279 *
280 * This event can be used to start the installation process or to display a custom error
281 * message.
282 *
283 * @param Exception $exception The exception thrown from creating and testing the database
284 * connection.
285 */
286 \Piwik\Piwik::postEvent('Db.cannotConnectToDb', array($exception), $pending = \true);
287 throw $exception;
288 }
289 // try to get an option (to check if data can be queried)
290 try {
291 \Piwik\Option::get('TestingIfDatabaseConnectionWorked');
292 } catch (Exception $exception) {
293 if (self::shouldRethrowException()) {
294 throw $exception;
295 }
296 \Piwik\Log::debug($exception);
297 /**
298 * Triggered when Piwik cannot access database data.
299 *
300 * This event can be used to start the installation process or to display a custom error
301 * message.
302 *
303 * @param Exception $exception The exception thrown from trying to get an option value.
304 */
305 \Piwik\Piwik::postEvent('Config.badConfigurationFile', array($exception), $pending = \true);
306 throw $exception;
307 }
308 // Init the Access object, so that eg. core/Updates/* can enforce Super User and use some APIs
309 \Piwik\Access::getInstance();
310 /**
311 * Triggered just after the platform is initialized and plugins are loaded.
312 *
313 * This event can be used to do early initialization.
314 *
315 * _Note: At this point the user is not authenticated yet._
316 */
317 \Piwik\Piwik::postEvent('Request.dispatchCoreAndPluginUpdatesScreen');
318 $this->throwIfPiwikVersionIsOlderThanDBSchema();
319 $module = \Piwik\Piwik::getModule();
320 $action = \Piwik\Piwik::getAction();
321 if (empty($module) || empty($action) || $module !== 'Installation' || !in_array($action, array('getInstallationCss', 'getInstallationJs'))) {
322 \Piwik\Plugin\Manager::getInstance()->installLoadedPlugins();
323 }
324 // ensure the current Piwik URL is known for later use
325 if (method_exists('Piwik\\SettingsPiwik', 'getPiwikUrl')) {
326 \Piwik\SettingsPiwik::getPiwikUrl();
327 }
328 $loggedIn = \false;
329 //move this up unsupported Browser do not create session
330 if ($this->isSupportedBrowserCheckNeeded()) {
331 SupportedBrowser::checkIfBrowserSupported();
332 }
333 // don't use sessionauth in cli mode
334 // try authenticating w/ session first...
335 $sessionAuth = $this->makeSessionAuthenticator();
336 if ($sessionAuth) {
337 $loggedIn = \Piwik\Access::getInstance()->reloadAccess($sessionAuth);
338 }
339 // ... if session auth fails try normal auth (which will login the anonymous user)
340 if (!$loggedIn) {
341 $authAdapter = $this->makeAuthenticator();
342 $success = \Piwik\Access::getInstance()->reloadAccess($authAdapter);
343 if ($success && \Piwik\Piwik::isUserIsAnonymous() && $authAdapter->getLogin() === 'anonymous' && \Piwik\Piwik::isUserHasSomeViewAccess() && \Piwik\Session::isSessionStarted() && \Piwik\Session::isWritable()) {
344 // usually the session would be started when someone logs in using login controller. But in this
345 // case we need to init session here for anoynymous users
346 $init = StaticContainer::get(SessionInitializer::class);
347 $init->initSession($authAdapter);
348 }
349 } else {
350 $this->makeAuthenticator($sessionAuth);
351 // Piwik\Auth must be set to the correct Login plugin
352 }
353 // Force the auth to use the token_auth if specified, so that embed dashboard
354 // and all other non widgetized controller methods works fine
355 if (\Piwik\Common::getRequestVar('token_auth', '', 'string') !== '' && Request::shouldReloadAuthUsingTokenAuth(null)) {
356 Request::reloadAuthUsingTokenAuth();
357 Request::checkTokenAuthIsNotLimited($module, $action);
358 }
359 \Piwik\SettingsServer::raiseMemoryLimitIfNecessary();
360 \Piwik\Plugin\Manager::getInstance()->postLoadPlugins();
361 /**
362 * Triggered after the platform is initialized and after the user has been authenticated, but
363 * before the platform has handled the request.
364 *
365 * Piwik uses this event to check for updates to Piwik.
366 */
367 \Piwik\Piwik::postEvent('Platform.initialized');
368 }
369 protected function prepareDispatch($module, $action, $parameters)
370 {
371 if (is_null($module)) {
372 $module = \Piwik\Common::getRequestVar('module', self::DEFAULT_MODULE, 'string');
373 }
374 if (is_null($action)) {
375 $action = \Piwik\Common::getRequestVar('action', \false);
376 if ($action !== \false) {
377 // If a value was provided, check it has the correct type.
378 $action = \Piwik\Common::getRequestVar('action', null, 'string');
379 }
380 }
381 if (\Piwik\Session::isSessionStarted()) {
382 $this->closeSessionEarlyForFasterUI();
383 }
384 if (is_null($parameters)) {
385 $parameters = array();
386 }
387 if (!ctype_alnum($module)) {
388 throw new Exception("Invalid module name '{$module}'");
389 }
390 list($module, $action) = Request::getRenamedModuleAndAction($module, $action);
391 if (!\Piwik\SettingsPiwik::isInternetEnabled() && \Piwik\Plugin\Manager::getInstance()->doesPluginRequireInternetConnection($module)) {
392 throw new PluginRequiresInternetException($module);
393 }
394 if (!\Piwik\Plugin\Manager::getInstance()->isPluginActivated($module)) {
395 throw new PluginDeactivatedException($module);
396 }
397 return array($module, $action, $parameters);
398 }
399 protected function handleMaintenanceMode()
400 {
401 if (GeneralConfig::getConfigValue('maintenance_mode') != 1 || \Piwik\Common::isPhpCliMode()) {
402 return;
403 }
404 // as request matomo behind load balancer should not return 503. https://github.com/matomo-org/matomo/issues/18054
405 if (GeneralConfig::getConfigValue('multi_server_environment') != 1) {
406 \Piwik\Common::sendResponseCode(503);
407 }
408 $logoUrl = 'plugins/Morpheus/images/logo.svg';
409 $faviconUrl = 'plugins/CoreHome/images/favicon.png';
410 try {
411 $logo = new CustomLogo();
412 if ($logo->hasSVGLogo()) {
413 $logoUrl = $logo->getSVGLogoUrl();
414 } else {
415 $logoUrl = $logo->getHeaderLogoUrl();
416 }
417 $faviconUrl = $logo->getPathUserFavicon();
418 } catch (Exception $ex) {
419 }
420 $recordStatistics = \Piwik\Config::getInstance()->Tracker['record_statistics'];
421 $trackMessage = '';
422 if ($recordStatistics) {
423 $trackMessage = 'Your analytics data will continue to be tracked as normal.';
424 } else {
425 $trackMessage = 'While the maintenance mode is active, data tracking is disabled.';
426 }
427 $page = file_get_contents(PIWIK_INCLUDE_PATH . '/plugins/Morpheus/templates/maintenance.tpl');
428 $page = str_replace('%logoUrl%', $logoUrl, $page);
429 $page = str_replace('%faviconUrl%', $faviconUrl, $page);
430 $page = str_replace('%piwikTitle%', \Piwik\Piwik::getRandomTitle(), $page);
431 $page = str_replace('%trackMessage%', $trackMessage, $page);
432 echo $page;
433 exit;
434 }
435 protected function handleSSLRedirection()
436 {
437 // Specifically disable for the opt out iframe
438 if (\Piwik\Piwik::getModule() == 'CoreAdminHome' && (\Piwik\Piwik::getAction() == 'optOut' || \Piwik\Piwik::getAction() == 'optOutJS')) {
439 return;
440 }
441 // Disable Https for VisitorGenerator
442 if (\Piwik\Piwik::getModule() == 'VisitorGenerator') {
443 return;
444 }
445 if (\Piwik\Common::isPhpCliMode()) {
446 return;
447 }
448 // proceed only when force_ssl = 1
449 if (!\Piwik\SettingsPiwik::isHttpsForced()) {
450 return;
451 }
452 \Piwik\Url::redirectToHttps();
453 }
454 private function closeSessionEarlyForFasterUI()
455 {
456 $isDashboardReferrer = !empty($_SERVER['HTTP_REFERER']) && strpos($_SERVER['HTTP_REFERER'], 'module=CoreHome&action=index') !== \false;
457 $isAllWebsitesReferrer = !empty($_SERVER['HTTP_REFERER']) && strpos($_SERVER['HTTP_REFERER'], 'module=MultiSites&action=index') !== \false;
458 if ($isDashboardReferrer && !empty($_POST['token_auth']) && \Piwik\Common::getRequestVar('widget', 0, 'int') === 1) {
459 \Piwik\Session::close();
460 }
461 if (($isDashboardReferrer || $isAllWebsitesReferrer) && \Piwik\Common::getRequestVar('viewDataTable', '', 'string') === 'sparkline') {
462 \Piwik\Session::close();
463 }
464 }
465 private function handleProfiler()
466 {
467 $profilerEnabled = \Piwik\Config::getInstance()->Debug['enable_php_profiler'] == 1;
468 if (!$profilerEnabled) {
469 return;
470 }
471 if (!empty($_GET['xhprof'])) {
472 $mainRun = $_GET['xhprof'] == 1;
473 // core:archive command sets xhprof=2
474 \Piwik\Profiler::setupProfilerXHProf($mainRun);
475 }
476 }
477 /**
478 * @param $module
479 * @param $action
480 * @param $parameters
481 * @return mixed
482 */
483 private function doDispatch($module, $action, $parameters)
484 {
485 list($module, $action, $parameters) = $this->prepareDispatch($module, $action, $parameters);
486 /**
487 * Triggered directly before controller actions are dispatched.
488 *
489 * This event can be used to modify the parameters passed to one or more controller actions
490 * and can be used to change the controller action being dispatched to.
491 *
492 * @param string &$module The name of the plugin being dispatched to.
493 * @param string &$action The name of the controller method being dispatched to.
494 * @param array &$parameters The arguments passed to the controller action.
495 */
496 \Piwik\Piwik::postEvent('Request.dispatch', array(&$module, &$action, &$parameters));
497 /** @var ControllerResolver $controllerResolver */
498 $controllerResolver = StaticContainer::get('Piwik\\Http\\ControllerResolver');
499 $controller = $controllerResolver->getController($module, $action, $parameters);
500 /**
501 * Triggered directly before controller actions are dispatched.
502 *
503 * This event exists for convenience and is triggered directly after the {@hook Request.dispatch}
504 * event is triggered.
505 *
506 * It can be used to do the same things as the {@hook Request.dispatch} event, but for one controller
507 * action only. Using this event will result in a little less code than {@hook Request.dispatch}.
508 *
509 * @param array &$parameters The arguments passed to the controller action.
510 */
511 \Piwik\Piwik::postEvent(sprintf('Controller.%s.%s', $module, $action), array(&$parameters));
512 $result = call_user_func_array($controller, $parameters);
513 /**
514 * Triggered after a controller action is successfully called.
515 *
516 * This event exists for convenience and is triggered immediately before the {@hook Request.dispatch.end}
517 * event is triggered.
518 *
519 * It can be used to do the same things as the {@hook Request.dispatch.end} event, but for one
520 * controller action only. Using this event will result in a little less code than
521 * {@hook Request.dispatch.end}.
522 *
523 * @param mixed &$result The result of the controller action.
524 * @param array $parameters The arguments passed to the controller action.
525 */
526 \Piwik\Piwik::postEvent(sprintf('Controller.%s.%s.end', $module, $action), array(&$result, $parameters));
527 /**
528 * Triggered after a controller action is successfully called.
529 *
530 * This event can be used to modify controller action output (if any) before the output is returned.
531 *
532 * @param mixed &$result The controller action result.
533 * @param array $parameters The arguments passed to the controller action.
534 */
535 \Piwik\Piwik::postEvent('Request.dispatch.end', array(&$result, $module, $action, $parameters));
536 return $result;
537 }
538 /**
539 * This method ensures that Piwik Platform cannot be running when using a NEWER database.
540 */
541 private function throwIfPiwikVersionIsOlderThanDBSchema()
542 {
543 // When developing this situation happens often when switching branches
544 if (\Piwik\Development::isEnabled()) {
545 return;
546 }
547 if (!StaticContainer::get('EnableDbVersionCheck')) {
548 return;
549 }
550 $updater = new \Piwik\Updater();
551 $dbSchemaVersion = $updater->getCurrentComponentVersion('core');
552 $current = \Piwik\Version::VERSION;
553 if (-1 === version_compare($current, $dbSchemaVersion)) {
554 $messages = array(
555 \Piwik\Piwik::translate('General_ExceptionDatabaseVersionNewerThanCodebase', array($current, $dbSchemaVersion)),
556 \Piwik\Piwik::translate('General_ExceptionDatabaseVersionNewerThanCodebaseWait'),
557 // we cannot fill in the Super User emails as we are failing before Authentication was ready
558 \Piwik\Piwik::translate('General_ExceptionContactSupportGeneric', array('', '')),
559 );
560 throw new DatabaseSchemaIsNewerThanCodebaseException(implode(" ", $messages));
561 }
562 }
563 private function makeSessionAuthenticator()
564 {
565 if (\Piwik\Common::isPhpClimode() && !defined('PIWIK_TEST_MODE')) {
566 // don't use the session auth during CLI requests
567 return null;
568 }
569 if (\Piwik\Common::getRequestVar('token_auth', '', 'string') !== '' && !\Piwik\Common::getRequestVar('force_api_session', 0)) {
570 return null;
571 }
572 $module = \Piwik\Common::getRequestVar('module', self::DEFAULT_MODULE, 'string');
573 $action = \Piwik\Common::getRequestVar('action', \false);
574 // the session must be started before using the session authenticator,
575 // so we do it here, if this is not an API request.
576 if (\Piwik\SettingsPiwik::isMatomoInstalled() && ($module !== 'API' || $action && $action !== 'index') && !($module === 'CoreAdminHome' && $action === 'optOutJS')) {
577 /**
578 * @ignore
579 */
580 \Piwik\Piwik::postEvent('Session.beforeSessionStart');
581 \Piwik\Session::start();
582 return StaticContainer::get(SessionAuth::class);
583 }
584 return null;
585 }
586 private function makeAuthenticator(SessionAuth $auth = null)
587 {
588 /**
589 * Triggered before the user is authenticated, when the global authentication object
590 * should be created.
591 *
592 * Plugins that provide their own authentication implementation should use this event
593 * to set the global authentication object (which must derive from {@link Piwik\Auth}).
594 *
595 * **Example**
596 *
597 * Piwik::addAction('Request.initAuthenticationObject', function() {
598 * StaticContainer::getContainer()->set('Piwik\Auth', new MyAuthImplementation());
599 * });
600 */
601 \Piwik\Piwik::postEvent('Request.initAuthenticationObject');
602 try {
603 $authAdapter = StaticContainer::get('Piwik\\Auth');
604 } catch (Exception $e) {
605 $message = "Authentication object cannot be found in the container. Maybe the Login plugin is not activated?\n <br />You can activate the plugin by adding:<br />\n <code>Plugins[] = Login</code><br />\n under the <code>[Plugins]</code> section in your config/config.ini.php";
606 $ex = new AuthenticationFailedException($message);
607 $ex->setIsHtmlMessage();
608 throw $ex;
609 }
610 if ($auth) {
611 $authAdapter->setLogin($auth->getLogin());
612 $authAdapter->setTokenAuth($auth->getTokenAuth());
613 } else {
614 $authAdapter->setLogin(self::DEFAULT_LOGIN);
615 $authAdapter->setTokenAuth(self::DEFAULT_TOKEN_AUTH);
616 }
617 return $authAdapter;
618 }
619 public static function getUniqueRequestId()
620 {
621 if (self::$requestId === null) {
622 self::$requestId = substr(\Piwik\Common::generateUniqId(), 0, 5);
623 }
624 return self::$requestId;
625 }
626 private static function setRequestIdHeader()
627 {
628 $requestId = self::getUniqueRequestId();
629 \Piwik\Common::sendHeader("X-Matomo-Request-Id: {$requestId}");
630 }
631 private function isSupportedBrowserCheckNeeded()
632 {
633 if (defined('PIWIK_ENABLE_DISPATCH') && !PIWIK_ENABLE_DISPATCH) {
634 return \false;
635 }
636 $userAgent = isset($_SERVER['HTTP_USER_AGENT']) ? $_SERVER['HTTP_USER_AGENT'] : '';
637 if ($userAgent === '') {
638 return \false;
639 }
640 $isTestMode = defined('PIWIK_TEST_MODE') && PIWIK_TEST_MODE;
641 if (!$isTestMode && \Piwik\Common::isPhpCliMode() === \true) {
642 return \false;
643 }
644 if (\Piwik\Piwik::getModule() === 'API' && (empty(\Piwik\Piwik::getAction()) || \Piwik\Piwik::getAction() === 'index' || \Piwik\Piwik::getAction() === 'glossary')) {
645 return \false;
646 }
647 if (\Piwik\Piwik::getModule() === 'Widgetize') {
648 return \true;
649 }
650 $generalConfig = \Piwik\Config::getInstance()->General;
651 if ($generalConfig['enable_framed_pages'] == '1' || $generalConfig['enable_framed_settings'] == '1') {
652 return \true;
653 }
654 if (\Piwik\Common::getRequestVar('token_auth', '', 'string') !== '') {
655 return \true;
656 }
657 if (\Piwik\Piwik::isUserIsAnonymous()) {
658 return \true;
659 }
660 return \false;
661 }
662 }
663