HomeController.php 19 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517
  1. <?php
  2. /* vim: set expandtab sw=4 ts=4 sts=4: */
  3. /**
  4. * Holds the PhpMyAdmin\Controllers\HomeController
  5. *
  6. * @package PhpMyAdmin\Controllers
  7. */
  8. declare(strict_types=1);
  9. namespace PhpMyAdmin\Controllers;
  10. use PhpMyAdmin\Charsets;
  11. use PhpMyAdmin\Charsets\Charset;
  12. use PhpMyAdmin\Charsets\Collation;
  13. use PhpMyAdmin\CheckUserPrivileges;
  14. use PhpMyAdmin\Config;
  15. use PhpMyAdmin\DatabaseInterface;
  16. use PhpMyAdmin\Display\GitRevision;
  17. use PhpMyAdmin\LanguageManager;
  18. use PhpMyAdmin\Message;
  19. use PhpMyAdmin\RecentFavoriteTable;
  20. use PhpMyAdmin\Relation;
  21. use PhpMyAdmin\Response;
  22. use PhpMyAdmin\Server\Select;
  23. use PhpMyAdmin\Template;
  24. use PhpMyAdmin\ThemeManager;
  25. use PhpMyAdmin\Url;
  26. use PhpMyAdmin\UserPreferences;
  27. use PhpMyAdmin\Util;
  28. /**
  29. * Class HomeController
  30. * @package PhpMyAdmin\Controllers
  31. */
  32. class HomeController extends AbstractController
  33. {
  34. /**
  35. * @var Config
  36. */
  37. private $config;
  38. /**
  39. * @var ThemeManager
  40. */
  41. private $themeManager;
  42. /**
  43. * HomeController constructor.
  44. *
  45. * @param Response $response Response instance
  46. * @param DatabaseInterface $dbi DatabaseInterface instance
  47. * @param Template $template Template object
  48. * @param Config $config Config instance
  49. * @param ThemeManager $themeManager ThemeManager instance
  50. */
  51. public function __construct($response, $dbi, Template $template, $config, ThemeManager $themeManager)
  52. {
  53. parent::__construct($response, $dbi, $template);
  54. $this->config = $config;
  55. $this->themeManager = $themeManager;
  56. }
  57. /**
  58. * @return string HTML
  59. */
  60. public function index(): string
  61. {
  62. global $cfg, $server, $collation_connection, $message;
  63. $languageManager = LanguageManager::getInstance();
  64. if (! empty($message)) {
  65. $displayMessage = Util::getMessage($message);
  66. unset($message);
  67. }
  68. if (isset($_SESSION['partial_logout'])) {
  69. $partialLogout = Message::success(__(
  70. 'You were logged out from one server, to logout completely '
  71. . 'from phpMyAdmin, you need to logout from all servers.'
  72. ))->getDisplay();
  73. unset($_SESSION['partial_logout']);
  74. }
  75. $syncFavoriteTables = RecentFavoriteTable::getInstance('favorite')
  76. ->getHtmlSyncFavoriteTables();
  77. $hasServer = $server > 0 || count($cfg['Servers']) > 1;
  78. if ($hasServer) {
  79. $hasServerSelection = $cfg['ServerDefault'] == 0
  80. || (! $cfg['NavigationDisplayServers']
  81. && (count($cfg['Servers']) > 1
  82. || ($server == 0 && count($cfg['Servers']) === 1)));
  83. if ($hasServerSelection) {
  84. $serverSelection = Select::render(true, true);
  85. }
  86. if ($server > 0) {
  87. $checkUserPrivileges = new CheckUserPrivileges($this->dbi);
  88. $checkUserPrivileges->getPrivileges();
  89. if (($cfg['Server']['auth_type'] != 'config') && $cfg['ShowChgPassword']) {
  90. $changePassword = $this->template->render('list/item', [
  91. 'content' => Util::getImage('s_passwd') . ' ' . __(
  92. 'Change password'
  93. ),
  94. 'id' => 'li_change_password',
  95. 'class' => 'no_bullets',
  96. 'url' => [
  97. 'href' => 'user_password.php' . Url::getCommon(),
  98. 'target' => null,
  99. 'id' => 'change_password_anchor',
  100. 'class' => 'ajax',
  101. ],
  102. 'mysql_help_page' => null,
  103. ]);
  104. }
  105. $charsets = Charsets::getCharsets($this->dbi, $cfg['Server']['DisableIS']);
  106. $collations = Charsets::getCollations($this->dbi, $cfg['Server']['DisableIS']);
  107. $charsetsList = [];
  108. /** @var Charset $charset */
  109. foreach ($charsets as $charset) {
  110. $collationsList = [];
  111. /** @var Collation $collation */
  112. foreach ($collations[$charset->getName()] as $collation) {
  113. $collationsList[] = [
  114. 'name' => $collation->getName(),
  115. 'description' => $collation->getDescription(),
  116. 'is_selected' => $collation_connection === $collation->getName(),
  117. ];
  118. }
  119. $charsetsList[] = [
  120. 'name' => $charset->getName(),
  121. 'description' => $charset->getDescription(),
  122. 'collations' => $collationsList,
  123. ];
  124. }
  125. $userPreferences = $this->template->render('list/item', [
  126. 'content' => Util::getImage('b_tblops') . ' ' . __(
  127. 'More settings'
  128. ),
  129. 'id' => 'li_user_preferences',
  130. 'class' => 'no_bullets',
  131. 'url' => [
  132. 'href' => 'prefs_manage.php' . Url::getCommon(),
  133. 'target' => null,
  134. 'id' => null,
  135. 'class' => null,
  136. ],
  137. 'mysql_help_page' => null,
  138. ]);
  139. }
  140. }
  141. $languageSelector = '';
  142. if (empty($cfg['Lang']) && $languageManager->hasChoice()) {
  143. $languageSelector = $languageManager->getSelectorDisplay($this->template);
  144. }
  145. $themeSelection = '';
  146. if ($cfg['ThemeManager']) {
  147. $themeSelection = $this->themeManager->getHtmlSelectBox();
  148. }
  149. $databaseServer = [];
  150. if ($server > 0 && $cfg['ShowServerInfo']) {
  151. $hostInfo = '';
  152. if (! empty($cfg['Server']['verbose'])) {
  153. $hostInfo .= $cfg['Server']['verbose'];
  154. if ($cfg['ShowServerInfo']) {
  155. $hostInfo .= ' (';
  156. }
  157. }
  158. if ($cfg['ShowServerInfo'] || empty($cfg['Server']['verbose'])) {
  159. $hostInfo .= $this->dbi->getHostInfo();
  160. }
  161. if (! empty($cfg['Server']['verbose']) && $cfg['ShowServerInfo']) {
  162. $hostInfo .= ')';
  163. }
  164. $serverCharset = Charsets::getServerCharset($this->dbi, $cfg['Server']['DisableIS']);
  165. $databaseServer = [
  166. 'host' => $hostInfo,
  167. 'type' => Util::getServerType(),
  168. 'connection' => Util::getServerSSL(),
  169. 'version' => $this->dbi->getVersionString() . ' - ' . $this->dbi->getVersionComment(),
  170. 'protocol' => $this->dbi->getProtoInfo(),
  171. 'user' => $this->dbi->fetchValue('SELECT USER();'),
  172. 'charset' => $serverCharset->getDescription() . ' (' . $serverCharset->getName() . ')',
  173. ];
  174. }
  175. $webServer = [];
  176. if ($cfg['ShowServerInfo']) {
  177. $webServer['software'] = $_SERVER['SERVER_SOFTWARE'];
  178. if ($server > 0) {
  179. $clientVersion = $this->dbi->getClientInfo();
  180. if (preg_match('#\d+\.\d+\.\d+#', $clientVersion)) {
  181. $clientVersion = 'libmysql - ' . $clientVersion;
  182. }
  183. $webServer['database'] = $clientVersion;
  184. $webServer['php_extensions'] = Util::listPHPExtensions();
  185. $webServer['php_version'] = PHP_VERSION;
  186. }
  187. }
  188. if ($cfg['ShowPhpInfo']) {
  189. $phpInfo = $this->template->render('list/item', [
  190. 'content' => __('Show PHP information'),
  191. 'id' => 'li_phpinfo',
  192. 'class' => null,
  193. 'url' => [
  194. 'href' => 'phpinfo.php' . Url::getCommon(),
  195. 'target' => '_blank',
  196. 'id' => null,
  197. 'class' => null,
  198. ],
  199. 'mysql_help_page' => null,
  200. ]);
  201. }
  202. $relation = new Relation($this->dbi);
  203. if ($server > 0) {
  204. $cfgRelation = $relation->getRelationsParam();
  205. if (! $cfgRelation['allworks']
  206. && $cfg['PmaNoRelation_DisableWarning'] == false
  207. ) {
  208. $messageText = __(
  209. 'The phpMyAdmin configuration storage is not completely '
  210. . 'configured, some extended features have been deactivated. '
  211. . '%sFind out why%s. '
  212. );
  213. if ($cfg['ZeroConf'] == true) {
  214. $messageText .= '<br>' .
  215. __(
  216. 'Or alternately go to \'Operations\' tab of any database '
  217. . 'to set it up there.'
  218. );
  219. }
  220. $messageInstance = Message::notice($messageText);
  221. $messageInstance->addParamHtml('<a href="./chk_rel.php" data-post="' . Url::getCommon() . '">');
  222. $messageInstance->addParamHtml('</a>');
  223. /* Show error if user has configured something, notice elsewhere */
  224. if (! empty($cfg['Servers'][$server]['pmadb'])) {
  225. $messageInstance->isError(true);
  226. }
  227. $configStorageMessage = $messageInstance->getDisplay();
  228. }
  229. }
  230. $this->checkRequirements();
  231. return $this->template->render('home/index', [
  232. 'message' => $displayMessage ?? '',
  233. 'partial_logout' => $partialLogout ?? '',
  234. 'is_git_revision' => $this->config->isGitRevision(),
  235. 'server' => $server,
  236. 'sync_favorite_tables' => $syncFavoriteTables,
  237. 'has_server' => $hasServer,
  238. 'is_demo' => $cfg['DBG']['demo'],
  239. 'has_server_selection' => $hasServerSelection ?? false,
  240. 'server_selection' => $serverSelection ?? '',
  241. 'change_password' => $changePassword ?? '',
  242. 'charsets' => $charsetsList ?? [],
  243. 'language_selector' => $languageSelector,
  244. 'theme_selection' => $themeSelection,
  245. 'user_preferences' => $userPreferences ?? '',
  246. 'database_server' => $databaseServer,
  247. 'web_server' => $webServer,
  248. 'php_info' => $phpInfo ?? '',
  249. 'is_version_checked' => $cfg['VersionCheck'],
  250. 'phpmyadmin_version' => PMA_VERSION,
  251. 'config_storage_message' => $configStorageMessage ?? '',
  252. ]);
  253. }
  254. /**
  255. * @param array $params Request parameters
  256. * @return void
  257. */
  258. public function setTheme(array $params): void
  259. {
  260. $this->themeManager->setActiveTheme($params['set_theme']);
  261. $this->themeManager->setThemeCookie();
  262. $userPreferences = new UserPreferences();
  263. $preferences = $userPreferences->load();
  264. $preferences['config_data']['ThemeDefault'] = $params['set_theme'];
  265. $userPreferences->save($preferences['config_data']);
  266. }
  267. /**
  268. * @param array $params Request parameters
  269. * @return void
  270. */
  271. public function setCollationConnection(array $params): void
  272. {
  273. $this->config->setUserValue(
  274. null,
  275. 'DefaultConnectionCollation',
  276. $params['collation_connection'],
  277. 'utf8mb4_unicode_ci'
  278. );
  279. }
  280. /**
  281. * @return array JSON
  282. */
  283. public function reloadRecentTablesList(): array
  284. {
  285. return [
  286. 'list' => RecentFavoriteTable::getInstance('recent')->getHtmlList(),
  287. ];
  288. }
  289. /**
  290. * @return string HTML
  291. */
  292. public function gitRevision(): string
  293. {
  294. return (new GitRevision(
  295. $this->response,
  296. $this->config,
  297. $this->template
  298. ))->display();
  299. }
  300. /**
  301. * @return void
  302. */
  303. private function checkRequirements(): void
  304. {
  305. global $cfg, $server, $lang;
  306. /**
  307. * mbstring is used for handling multibytes inside parser, so it is good
  308. * to tell user something might be broken without it, see bug #1063149.
  309. */
  310. if (! extension_loaded('mbstring')) {
  311. trigger_error(
  312. __(
  313. 'The mbstring PHP extension was not found and you seem to be using'
  314. . ' a multibyte charset. Without the mbstring extension phpMyAdmin'
  315. . ' is unable to split strings correctly and it may result in'
  316. . ' unexpected results.'
  317. ),
  318. E_USER_WARNING
  319. );
  320. }
  321. /**
  322. * Missing functionality
  323. */
  324. if (! extension_loaded('curl') && ! ini_get('allow_url_fopen')) {
  325. trigger_error(
  326. __(
  327. 'The curl extension was not found and allow_url_fopen is '
  328. . 'disabled. Due to this some features such as error reporting '
  329. . 'or version check are disabled.'
  330. )
  331. );
  332. }
  333. if ($cfg['LoginCookieValidityDisableWarning'] == false) {
  334. /**
  335. * Check whether session.gc_maxlifetime limits session validity.
  336. */
  337. $gc_time = (int) ini_get('session.gc_maxlifetime');
  338. if ($gc_time < $cfg['LoginCookieValidity']) {
  339. trigger_error(
  340. __(
  341. 'Your PHP parameter [a@https://secure.php.net/manual/en/session.' .
  342. 'configuration.php#ini.session.gc-maxlifetime@_blank]session.' .
  343. 'gc_maxlifetime[/a] is lower than cookie validity configured ' .
  344. 'in phpMyAdmin, because of this, your login might expire sooner ' .
  345. 'than configured in phpMyAdmin.'
  346. ),
  347. E_USER_WARNING
  348. );
  349. }
  350. }
  351. /**
  352. * Check whether LoginCookieValidity is limited by LoginCookieStore.
  353. */
  354. if ($cfg['LoginCookieStore'] != 0
  355. && $cfg['LoginCookieStore'] < $cfg['LoginCookieValidity']
  356. ) {
  357. trigger_error(
  358. __(
  359. 'Login cookie store is lower than cookie validity configured in ' .
  360. 'phpMyAdmin, because of this, your login will expire sooner than ' .
  361. 'configured in phpMyAdmin.'
  362. ),
  363. E_USER_WARNING
  364. );
  365. }
  366. /**
  367. * Warning if using the default MySQL controluser account
  368. */
  369. if ($server != 0
  370. && isset($cfg['Server']['controluser']) && $cfg['Server']['controluser'] == 'pma'
  371. && isset($cfg['Server']['controlpass']) && $cfg['Server']['controlpass'] == 'pmapass'
  372. ) {
  373. trigger_error(
  374. __(
  375. 'Your server is running with default values for the ' .
  376. 'controluser and password (controlpass) and is open to ' .
  377. 'intrusion; you really should fix this security weakness' .
  378. ' by changing the password for controluser \'pma\'.'
  379. ),
  380. E_USER_WARNING
  381. );
  382. }
  383. /**
  384. * Check if user does not have defined blowfish secret and it is being used.
  385. */
  386. if (! empty($_SESSION['encryption_key'])) {
  387. if (empty($cfg['blowfish_secret'])) {
  388. trigger_error(
  389. __(
  390. 'The configuration file now needs a secret passphrase (blowfish_secret).'
  391. ),
  392. E_USER_WARNING
  393. );
  394. } elseif (strlen($cfg['blowfish_secret']) < 32) {
  395. trigger_error(
  396. __(
  397. 'The secret passphrase in configuration (blowfish_secret) is too short.'
  398. ),
  399. E_USER_WARNING
  400. );
  401. }
  402. }
  403. /**
  404. * Check for existence of config directory which should not exist in
  405. * production environment.
  406. */
  407. if (@file_exists(ROOT_PATH . 'config')) {
  408. trigger_error(
  409. __(
  410. 'Directory [code]config[/code], which is used by the setup script, ' .
  411. 'still exists in your phpMyAdmin directory. It is strongly ' .
  412. 'recommended to remove it once phpMyAdmin has been configured. ' .
  413. 'Otherwise the security of your server may be compromised by ' .
  414. 'unauthorized people downloading your configuration.'
  415. ),
  416. E_USER_WARNING
  417. );
  418. }
  419. /**
  420. * Warning about Suhosin only if its simulation mode is not enabled
  421. */
  422. if ($cfg['SuhosinDisableWarning'] == false
  423. && ini_get('suhosin.request.max_value_length')
  424. && ini_get('suhosin.simulation') == '0'
  425. ) {
  426. trigger_error(
  427. sprintf(
  428. __(
  429. 'Server running with Suhosin. Please refer ' .
  430. 'to %sdocumentation%s for possible issues.'
  431. ),
  432. '[doc@faq1-38]',
  433. '[/doc]'
  434. ),
  435. E_USER_WARNING
  436. );
  437. }
  438. /* Missing template cache */
  439. if ($this->config->getTempDir('twig') === null) {
  440. trigger_error(
  441. sprintf(
  442. __(
  443. 'The $cfg[\'TempDir\'] (%s) is not accessible. ' .
  444. 'phpMyAdmin is not able to cache templates and will ' .
  445. 'be slow because of this.'
  446. ),
  447. $this->config->get('TempDir')
  448. ),
  449. E_USER_WARNING
  450. );
  451. }
  452. /**
  453. * Warning about incomplete translations.
  454. *
  455. * The data file is created while creating release by ./scripts/remove-incomplete-mo
  456. */
  457. if (@file_exists(ROOT_PATH . 'libraries/language_stats.inc.php')) {
  458. include ROOT_PATH . 'libraries/language_stats.inc.php';
  459. /*
  460. * This message is intentionally not translated, because we're
  461. * handling incomplete translations here and focus on english
  462. * speaking users.
  463. */
  464. if (isset($GLOBALS['language_stats'][$lang])
  465. && $GLOBALS['language_stats'][$lang] < $cfg['TranslationWarningThreshold']
  466. ) {
  467. trigger_error(
  468. 'You are using an incomplete translation, please help to make it '
  469. . 'better by [a@https://www.phpmyadmin.net/translate/'
  470. . '@_blank]contributing[/a].',
  471. E_USER_NOTICE
  472. );
  473. }
  474. }
  475. }
  476. }