Fix Display\ResultsTest test on Windows
[phpmyadmin.git] / libraries / common.inc.php
blob2c4c10fbf407c115dc191ffb8c6e390a4d67bebf
1 <?php
2 /* vim: set expandtab sw=4 ts=4 sts=4: */
3 /**
4 * Misc stuff and REQUIRED by ALL the scripts.
5 * MUST be included by every script
7 * Among other things, it contains the advanced authentication work.
9 * Order of sections for common.inc.php:
11 * the authentication libraries must be before the connection to db
13 * ... so the required order is:
15 * LABEL_variables_init
16 * - initialize some variables always needed
17 * LABEL_parsing_config_file
18 * - parsing of the configuration file
19 * LABEL_loading_language_file
20 * - loading language file
21 * LABEL_setup_servers
22 * - check and setup configured servers
23 * LABEL_theme_setup
24 * - setting up themes
26 * - load of MySQL extension (if necessary)
27 * - loading of an authentication library
28 * - db connection
29 * - authentication work
31 * @package PhpMyAdmin
33 declare(strict_types=1);
35 use PhpMyAdmin\Config;
36 use PhpMyAdmin\Core;
37 use PhpMyAdmin\DatabaseInterface;
38 use PhpMyAdmin\Di\Migration;
39 use PhpMyAdmin\ErrorHandler;
40 use PhpMyAdmin\LanguageManager;
41 use PhpMyAdmin\Logging;
42 use PhpMyAdmin\Message;
43 use PhpMyAdmin\Response;
44 use PhpMyAdmin\Session;
45 use PhpMyAdmin\ThemeManager;
46 use PhpMyAdmin\Tracker;
47 use PhpMyAdmin\Util;
48 use Symfony\Component\Config\FileLocator;
49 use Symfony\Component\DependencyInjection\ContainerBuilder;
50 use Symfony\Component\DependencyInjection\Loader\YamlFileLoader;
52 /**
53 * block attempts to directly run this script
55 if (getcwd() == dirname(__FILE__)) {
56 die('Attack stopped');
59 /**
60 * Minimum PHP version; can't call Core::fatalError() which uses a
61 * PHP 5 function, so cannot easily localize this message.
63 if (version_compare(PHP_VERSION, '7.1.3', 'lt')) {
64 die(
65 'PHP 7.1.3+ is required. <br> Currently installed version is: '
66 . phpversion()
70 /**
71 * for verification in all procedural scripts under libraries
73 define('PHPMYADMIN', true);
75 /**
76 * Load vendor configuration.
78 require_once ROOT_PATH . 'libraries/vendor_config.php';
80 /**
81 * Activate autoloader
83 if (! @is_readable(AUTOLOAD_FILE)) {
84 die(
85 'File <tt>' . AUTOLOAD_FILE . '</tt> missing or not readable. <br>'
86 . 'Most likely you did not run Composer to '
87 . '<a href="https://docs.phpmyadmin.net/en/latest/setup.html#installing-from-git">install library files</a>.'
90 require_once AUTOLOAD_FILE;
92 $containerBuilder = new ContainerBuilder();
93 $loader = new YamlFileLoader($containerBuilder, new FileLocator(__DIR__));
94 $loader->load('../services.yml');
95 $loader->load('../services_controllers.yml');
96 /** @var Migration $diMigration */
97 $diMigration = $containerBuilder->get('di_migration');
99 /**
100 * Load gettext functions.
102 PhpMyAdmin\MoTranslator\Loader::loadFunctions();
104 /** @var ErrorHandler $GLOBALS['error_handler'] */
105 $GLOBALS['error_handler'] = $containerBuilder->get('error_handler');
108 * Warning about missing PHP extensions.
110 Core::checkExtensions();
113 * Configure required PHP settings.
115 Core::configure();
117 /******************************************************************************/
118 /* start procedural code label_start_procedural */
120 Core::cleanupPathInfo();
122 /******************************************************************************/
123 /* parsing configuration file LABEL_parsing_config_file */
126 * @global Config $GLOBALS['PMA_Config']
127 * force reading of config file, because we removed sensitive values
128 * in the previous iteration
130 $GLOBALS['PMA_Config'] = $containerBuilder->get('config');
131 //$containerBuilder->set('config', $GLOBALS['PMA_Config']);
134 * include session handling after the globals, to prevent overwriting
136 if (! defined('PMA_NO_SESSION')) {
137 Session::setUp($GLOBALS['PMA_Config'], $GLOBALS['error_handler']);
141 * init some variables LABEL_variables_init
145 * holds parameters to be passed to next page
146 * @global array $GLOBALS['url_params']
148 $diMigration->setGlobal('url_params', []);
151 * holds page that should be displayed
152 * @global string $GLOBALS['goto']
154 $diMigration->setGlobal('goto', '');
155 // Security fix: disallow accessing serious server files via "?goto="
156 if (isset($_REQUEST['goto']) && Core::checkPageValidity($_REQUEST['goto'])) {
157 $diMigration->setGlobal('goto', $_REQUEST['goto']);
158 $diMigration->setGlobal('url_params', ['goto' => $_REQUEST['goto']]);
159 } else {
160 $GLOBALS['PMA_Config']->removeCookie('goto');
161 unset($_REQUEST['goto'], $_GET['goto'], $_POST['goto']);
165 * returning page
166 * @global string $GLOBALS['back']
168 if (isset($_REQUEST['back']) && Core::checkPageValidity($_REQUEST['back'])) {
169 $diMigration->setGlobal('back', $_REQUEST['back']);
170 } else {
171 $GLOBALS['PMA_Config']->removeCookie('back');
172 unset($_REQUEST['back'], $_GET['back'], $_POST['back']);
176 * Check whether user supplied token is valid, if not remove any possibly
177 * dangerous stuff from request.
179 * remember that some objects in the session with session_start and __wakeup()
180 * could access this variables before we reach this point
181 * f.e. PhpMyAdmin\Config: fontsize
183 * Check for token mismatch only if the Request method is POST
184 * GET Requests would never have token and therefore checking
185 * mis-match does not make sense
187 * @todo variables should be handled by their respective owners (objects)
188 * f.e. lang, server in PhpMyAdmin\Config
191 $token_mismatch = true;
192 $token_provided = false;
194 if ($_SERVER['REQUEST_METHOD'] == 'POST') {
195 if (Core::isValid($_POST['token'])) {
196 $token_provided = true;
197 $token_mismatch = ! @hash_equals($_SESSION[' PMA_token '], $_POST['token']);
200 if ($token_mismatch) {
201 /* Warn in case the mismatch is result of failed setting of session cookie */
202 if (isset($_POST['set_session']) && $_POST['set_session'] != session_id()) {
203 trigger_error(
205 'Failed to set session cookie. Maybe you are using '
206 . 'HTTP instead of HTTPS to access phpMyAdmin.'
208 E_USER_ERROR
212 * We don't allow any POST operation parameters if the token is mismatched
213 * or is not provided
215 $whitelist = ['ajax_request'];
216 PhpMyAdmin\Sanitize::removeRequestVars($whitelist);
222 * current selected database
223 * @global string $GLOBALS['db']
225 Core::setGlobalDbOrTable('db');
228 * current selected table
229 * @global string $GLOBALS['table']
231 Core::setGlobalDbOrTable('table');
234 * Store currently selected recent table.
235 * Affect $GLOBALS['db'] and $GLOBALS['table']
237 if (isset($_REQUEST['selected_recent_table']) && Core::isValid($_REQUEST['selected_recent_table'])) {
238 $recent_table = json_decode($_REQUEST['selected_recent_table'], true);
240 $diMigration->setGlobal(
241 'db',
242 (array_key_exists('db', $recent_table) && is_string($recent_table['db'])) ? $recent_table['db'] : ''
244 $diMigration->setGlobal(
245 'url_params',
246 ['db' => $containerBuilder->getParameter('db')] + $containerBuilder->getParameter('url_params')
249 $diMigration->setGlobal(
250 'table',
251 (array_key_exists('table', $recent_table) && is_string($recent_table['table'])) ? $recent_table['table'] : ''
253 $diMigration->setGlobal(
254 'url_params',
255 ['table' => $containerBuilder->getParameter('table')] + $containerBuilder->getParameter('url_params')
260 * SQL query to be executed
261 * @global string $GLOBALS['sql_query']
263 $diMigration->setGlobal('sql_query', '');
264 if (Core::isValid($_POST['sql_query'])) {
265 $diMigration->setGlobal('sql_query', $_POST['sql_query']);
268 //$_REQUEST['set_theme'] // checked later in this file LABEL_theme_setup
269 //$_REQUEST['server']; // checked later in this file
270 //$_REQUEST['lang']; // checked by LABEL_loading_language_file
272 /******************************************************************************/
273 /* loading language file LABEL_loading_language_file */
276 * lang detection is done here
278 $language = LanguageManager::getInstance()->selectLanguage();
279 $language->activate();
282 * check for errors occurred while loading configuration
283 * this check is done here after loading language files to present errors in locale
285 $GLOBALS['PMA_Config']->checkPermissions();
286 $GLOBALS['PMA_Config']->checkErrors();
288 /* Check server configuration */
289 Core::checkConfiguration();
291 /* Check request for possible attacks */
292 Core::checkRequest();
294 /******************************************************************************/
295 /* setup servers LABEL_setup_servers */
297 $GLOBALS['PMA_Config']->checkServers();
300 * current server
301 * @global integer $GLOBALS['server']
303 $diMigration->setGlobal('server', $GLOBALS['PMA_Config']->selectServer());
304 $diMigration->setGlobal('url_params', ['server' => $containerBuilder->getParameter('server')] + $containerBuilder->getParameter('url_params'));
307 * BC - enable backward compatibility
308 * exports all configuration settings into $GLOBALS ($GLOBALS['cfg'])
310 $GLOBALS['PMA_Config']->enableBc();
312 /******************************************************************************/
313 /* setup themes LABEL_theme_setup */
315 ThemeManager::initializeTheme();
317 $GLOBALS['dbi'] = null;
319 if (! defined('PMA_MINIMUM_COMMON')) {
321 * save some settings in cookies
322 * @todo should be done in PhpMyAdmin\Config
324 $GLOBALS['PMA_Config']->setCookie('pma_lang', $GLOBALS['lang']);
326 ThemeManager::getInstance()->setThemeCookie();
328 $containerBuilder->set(DatabaseInterface::class, DatabaseInterface::load());
329 $containerBuilder->setAlias('dbi', DatabaseInterface::class);
331 if (! empty($cfg['Server'])) {
332 // get LoginCookieValidity from preferences cache
333 // no generic solution for loading preferences from cache as some settings
334 // need to be kept for processing in
335 // PhpMyAdmin\Config::loadUserPreferences()
336 $cache_key = 'server_' . $GLOBALS['server'];
337 if (isset($_SESSION['cache'][$cache_key]['userprefs']['LoginCookieValidity'])
339 $value
340 = $_SESSION['cache'][$cache_key]['userprefs']['LoginCookieValidity'];
341 $GLOBALS['PMA_Config']->set('LoginCookieValidity', $value);
342 $GLOBALS['cfg']['LoginCookieValidity'] = $value;
343 unset($value);
345 unset($cache_key);
347 // Gets the authentication library that fits the $cfg['Server'] settings
348 // and run authentication
351 * the required auth type plugin
353 $auth_class = 'PhpMyAdmin\\Plugins\\Auth\\Authentication' . ucfirst(strtolower($cfg['Server']['auth_type']));
354 if (! @class_exists($auth_class)) {
355 Core::fatalError(
356 __('Invalid authentication method set in configuration:')
357 . ' ' . $cfg['Server']['auth_type']
360 if (isset($_POST['pma_password']) && strlen($_POST['pma_password']) > 256) {
361 $_POST['pma_password'] = substr($_POST['pma_password'], 0, 256);
363 $auth_plugin = new $auth_class();
365 $auth_plugin->authenticate();
367 // Try to connect MySQL with the control user profile (will be used to
368 // get the privileges list for the current user but the true user link
369 // must be open after this one so it would be default one for all the
370 // scripts)
371 $controllink = false;
372 if ($cfg['Server']['controluser'] != '') {
373 $controllink = $GLOBALS['dbi']->connect(
374 DatabaseInterface::CONNECT_CONTROL
378 // Connects to the server (validates user's login)
379 /** @var DatabaseInterface $userlink */
380 $userlink = $GLOBALS['dbi']->connect(DatabaseInterface::CONNECT_USER);
382 if ($userlink === false) {
383 $auth_plugin->showFailure('mysql-denied');
386 if (! $controllink) {
388 * Open separate connection for control queries, this is needed
389 * to avoid problems with table locking used in main connection
390 * and phpMyAdmin issuing queries to configuration storage, which
391 * is not locked by that time.
393 $controllink = $GLOBALS['dbi']->connect(
394 DatabaseInterface::CONNECT_USER,
395 null,
396 DatabaseInterface::CONNECT_CONTROL
400 $auth_plugin->rememberCredentials();
402 $auth_plugin->checkTwoFactor();
404 /* Log success */
405 Logging::logUser($cfg['Server']['user']);
407 if ($GLOBALS['dbi']->getVersion() < $cfg['MysqlMinVersion']['internal']) {
408 Core::fatalError(
409 __('You should upgrade to %s %s or later.'),
411 'MySQL',
412 $cfg['MysqlMinVersion']['human'],
417 // Sets the default delimiter (if specified).
418 if (! empty($_REQUEST['sql_delimiter'])) {
419 PhpMyAdmin\SqlParser\Lexer::$DEFAULT_DELIMITER = $_REQUEST['sql_delimiter'];
422 // TODO: Set SQL modes too.
423 } else { // end server connecting
424 $response = Response::getInstance();
425 $response->getHeader()->disableMenuAndConsole();
426 $response->getFooter()->setMinimal();
430 * check if profiling was requested and remember it
431 * (note: when $cfg['ServerDefault'] = 0, constant is not defined)
433 if (isset($_REQUEST['profiling'])
434 && Util::profilingSupported()
436 $_SESSION['profiling'] = true;
437 } elseif (isset($_REQUEST['profiling_form'])) {
438 // the checkbox was unchecked
439 unset($_SESSION['profiling']);
443 * Inclusion of profiling scripts is needed on various
444 * pages like sql, tbl_sql, db_sql, tbl_select
446 $response = Response::getInstance();
447 if (isset($_SESSION['profiling'])) {
448 $scripts = $response->getHeader()->getScripts();
449 $scripts->addFile('chart.js');
450 $scripts->addFile('vendor/jqplot/jquery.jqplot.js');
451 $scripts->addFile('vendor/jqplot/plugins/jqplot.pieRenderer.js');
452 $scripts->addFile('vendor/jqplot/plugins/jqplot.highlighter.js');
453 $scripts->addFile('vendor/jquery/jquery.tablesorter.js');
457 * There is no point in even attempting to process
458 * an ajax request if there is a token mismatch
460 if ($response->isAjax() && $_SERVER['REQUEST_METHOD'] == 'POST' && $token_mismatch) {
461 $response->setRequestStatus(false);
462 $response->addJSON(
463 'message',
464 Message::error(__('Error: Token mismatch'))
466 exit;
469 $containerBuilder->set('response', Response::getInstance());
472 // load user preferences
473 $GLOBALS['PMA_Config']->loadUserPreferences();
475 $containerBuilder->set('theme_manager', ThemeManager::getInstance());
477 /* Tell tracker that it can actually work */
478 Tracker::enable();
480 if (! defined('PMA_MINIMUM_COMMON')
481 && ! empty($GLOBALS['server'])
482 && isset($GLOBALS['cfg']['ZeroConf'])
483 && $GLOBALS['cfg']['ZeroConf'] == true
485 $GLOBALS['dbi']->postConnectControl();