[ Index ] |
PHP Cross Reference of Joomla 4.2.2 documentation |
[Summary view] [Print] [Text view]
1 <?php 2 3 /** 4 * @package Joomla.Installation 5 * 6 * @copyright (C) 2009 Open Source Matters, Inc. <https://www.joomla.org> 7 * @license GNU General Public License version 2 or later; see LICENSE.txt 8 */ 9 10 namespace Joomla\CMS\Installation\Helper; 11 12 use Joomla\CMS\Factory; 13 use Joomla\CMS\Filesystem\File; 14 use Joomla\CMS\Filesystem\Path; 15 use Joomla\CMS\Language\Text; 16 use Joomla\CMS\User\UserHelper; 17 use Joomla\Database\DatabaseDriver; 18 use Joomla\Database\DatabaseInterface; 19 use Joomla\Utilities\ArrayHelper; 20 21 // phpcs:disable PSR1.Files.SideEffects 22 \defined('_JEXEC') or die; 23 // phpcs:enable PSR1.Files.SideEffects 24 25 /** 26 * Joomla Installation Database Helper Class. 27 * 28 * @since 1.6 29 */ 30 abstract class DatabaseHelper 31 { 32 /** 33 * The minimum database server version for MariaDB databases as required by the CMS. 34 * This is not necessarily equal to what the database driver requires. 35 * 36 * @var string 37 * @since 4.0.0 38 */ 39 protected static $dbMinimumMariaDb = '10.1'; 40 41 /** 42 * The minimum database server version for MySQL databases as required by the CMS. 43 * This is not necessarily equal to what the database driver requires. 44 * 45 * @var string 46 * @since 4.0.0 47 */ 48 protected static $dbMinimumMySql = '5.6'; 49 50 /** 51 * The minimum database server version for PostgreSQL databases as required by the CMS. 52 * This is not necessarily equal to what the database driver requires. 53 * 54 * @var string 55 * @since 4.0.0 56 */ 57 protected static $dbMinimumPostgreSql = '11.0'; 58 59 /** 60 * Method to get a database driver. 61 * 62 * @param string $driver The database driver to use. 63 * @param string $host The hostname to connect on. 64 * @param string $user The user name to connect with. 65 * @param string $password The password to use for connection authentication. 66 * @param string $database The database to use. 67 * @param string $prefix The table prefix to use. 68 * @param boolean $select True if the database should be selected. 69 * @param array $ssl Database TLS connection options. 70 * 71 * @return DatabaseInterface 72 * 73 * @since 1.6 74 */ 75 public static function getDbo($driver, $host, $user, $password, $database, $prefix, $select = true, array $ssl = []) 76 { 77 static $db; 78 79 if (!$db) { 80 // Build the connection options array. 81 $options = [ 82 'driver' => $driver, 83 'host' => $host, 84 'user' => $user, 85 'password' => $password, 86 'database' => $database, 87 'prefix' => $prefix, 88 'select' => $select, 89 ]; 90 91 if (!empty($ssl['dbencryption'])) { 92 $options['ssl'] = [ 93 'enable' => true, 94 'verify_server_cert' => (bool) $ssl['dbsslverifyservercert'], 95 ]; 96 97 foreach (['cipher', 'ca', 'key', 'cert'] as $value) { 98 $confVal = trim($ssl['dbssl' . $value]); 99 100 if ($confVal !== '') { 101 $options['ssl'][$value] = $confVal; 102 } 103 } 104 } 105 106 // Enable utf8mb4 connections for mysql adapters 107 if (strtolower($driver) === 'mysqli') { 108 $options['utf8mb4'] = true; 109 } 110 111 if (strtolower($driver) === 'mysql') { 112 $options['charset'] = 'utf8mb4'; 113 } 114 115 // Get a database object. 116 $db = DatabaseDriver::getInstance($options); 117 } 118 119 return $db; 120 } 121 122 /** 123 * Convert encryption options to array. 124 * 125 * @param \stdClass $options The session options 126 * 127 * @return array The encryption settings 128 * 129 * @since 4.0.0 130 */ 131 public static function getEncryptionSettings($options) 132 { 133 return [ 134 'dbencryption' => $options->db_encryption, 135 'dbsslverifyservercert' => $options->db_sslverifyservercert, 136 'dbsslkey' => $options->db_sslkey, 137 'dbsslcert' => $options->db_sslcert, 138 'dbsslca' => $options->db_sslca, 139 'dbsslcipher' => $options->db_sslcipher, 140 ]; 141 } 142 143 /** 144 * Get the minimum required database server version. 145 * 146 * @param DatabaseDriver $db Database object 147 * @param \stdClass $options The session options 148 * 149 * @return string The minimum required database server version. 150 * 151 * @since 4.0.0 152 */ 153 public static function getMinimumServerVersion($db, $options) 154 { 155 // Get minimum database version required by the database driver 156 $minDbVersionRequired = $db->getMinimum(); 157 158 // Get minimum database version required by the CMS 159 if (in_array($options->db_type, ['mysql', 'mysqli'])) { 160 if ($db->isMariaDb()) { 161 $minDbVersionCms = self::$dbMinimumMariaDb; 162 } else { 163 $minDbVersionCms = self::$dbMinimumMySql; 164 } 165 } else { 166 $minDbVersionCms = self::$dbMinimumPostgreSql; 167 } 168 169 // Use most restrictive, i.e. largest minimum database version requirement 170 if (version_compare($minDbVersionCms, $minDbVersionRequired) > 0) { 171 $minDbVersionRequired = $minDbVersionCms; 172 } 173 174 return $minDbVersionRequired; 175 } 176 177 /** 178 * Validate and clean up database connection parameters. 179 * 180 * @param \stdClass $options The session options 181 * 182 * @return string|boolean A string with the translated error message if 183 * validation error, otherwise false. 184 * 185 * @since 4.0.0 186 */ 187 public static function validateConnectionParameters($options) 188 { 189 // Ensure a database type was selected. 190 if (empty($options->db_type)) { 191 return Text::_('INSTL_DATABASE_INVALID_TYPE'); 192 } 193 194 // Ensure that a hostname and user name were input. 195 if (empty($options->db_host) || empty($options->db_user)) { 196 return Text::_('INSTL_DATABASE_INVALID_DB_DETAILS'); 197 } 198 199 // Ensure that a database name is given. 200 if (empty($options->db_name)) { 201 return Text::_('INSTL_DATABASE_EMPTY_NAME'); 202 } 203 204 // Validate length of database name. 205 if (strlen($options->db_name) > 64) { 206 return Text::_('INSTL_DATABASE_NAME_TOO_LONG'); 207 } 208 209 // Validate database table prefix. 210 if (empty($options->db_prefix) || !preg_match('#^[a-zA-Z]+[a-zA-Z0-9_]*$#', $options->db_prefix)) { 211 return Text::_('INSTL_DATABASE_PREFIX_MSG'); 212 } 213 214 // Validate length of database table prefix. 215 if (strlen($options->db_prefix) > 15) { 216 return Text::_('INSTL_DATABASE_FIX_TOO_LONG'); 217 } 218 219 // Validate database name. 220 if (in_array($options->db_type, ['pgsql', 'postgresql']) && !preg_match('#^[a-zA-Z_][0-9a-zA-Z_$]*$#', $options->db_name)) { 221 return Text::_('INSTL_DATABASE_NAME_MSG_POSTGRES'); 222 } 223 224 if (in_array($options->db_type, ['mysql', 'mysqli']) && preg_match('#[\\\\\/]#', $options->db_name)) { 225 return Text::_('INSTL_DATABASE_NAME_MSG_MYSQL'); 226 } 227 228 // Workaround for UPPERCASE table prefix for postgresql 229 if (in_array($options->db_type, ['pgsql', 'postgresql'])) { 230 if (strtolower($options->db_prefix) != $options->db_prefix) { 231 return Text::_('INSTL_DATABASE_FIX_LOWERCASE'); 232 } 233 } 234 235 // Validate and clean up database connection encryption options 236 $optionsChanged = false; 237 238 if ($options->db_encryption === 0) { 239 // Reset unused options 240 if (!empty($options->db_sslkey)) { 241 $options->db_sslkey = ''; 242 $optionsChanged = true; 243 } 244 245 if (!empty($options->db_sslcert)) { 246 $options->db_sslcert = ''; 247 $optionsChanged = true; 248 } 249 250 if ($options->db_sslverifyservercert) { 251 $options->db_sslverifyservercert = false; 252 $optionsChanged = true; 253 } 254 255 if (!empty($options->db_sslca)) { 256 $options->db_sslca = ''; 257 $optionsChanged = true; 258 } 259 260 if (!empty($options->db_sslcipher)) { 261 $options->db_sslcipher = ''; 262 $optionsChanged = true; 263 } 264 } else { 265 // Check localhost 266 if (strtolower($options->db_host) === 'localhost') { 267 return Text::_('INSTL_DATABASE_ENCRYPTION_MSG_LOCALHOST'); 268 } 269 270 // Check CA file and folder depending on database type if server certificate verification 271 if ($options->db_sslverifyservercert) { 272 if (empty($options->db_sslca)) { 273 return Text::sprintf('INSTL_DATABASE_ENCRYPTION_MSG_FILE_FIELD_EMPTY', Text::_('INSTL_DATABASE_ENCRYPTION_CA_LABEL')); 274 } 275 276 if (!File::exists(Path::clean($options->db_sslca))) { 277 return Text::sprintf('INSTL_DATABASE_ENCRYPTION_MSG_FILE_FIELD_BAD', Text::_('INSTL_DATABASE_ENCRYPTION_CA_LABEL')); 278 } 279 } else { 280 // Reset unused option 281 if (!empty($options->db_sslca)) { 282 $options->db_sslca = ''; 283 $optionsChanged = true; 284 } 285 } 286 287 // Check key and certificate if two-way encryption 288 if ($options->db_encryption === 2) { 289 if (empty($options->db_sslkey)) { 290 return Text::sprintf('INSTL_DATABASE_ENCRYPTION_MSG_FILE_FIELD_EMPTY', Text::_('INSTL_DATABASE_ENCRYPTION_KEY_LABEL')); 291 } 292 293 if (!File::exists(Path::clean($options->db_sslkey))) { 294 return Text::sprintf('INSTL_DATABASE_ENCRYPTION_MSG_FILE_FIELD_BAD', Text::_('INSTL_DATABASE_ENCRYPTION_KEY_LABEL')); 295 } 296 297 if (empty($options->db_sslcert)) { 298 return Text::sprintf('INSTL_DATABASE_ENCRYPTION_MSG_FILE_FIELD_EMPTY', Text::_('INSTL_DATABASE_ENCRYPTION_CERT_LABEL')); 299 } 300 301 if (!File::exists(Path::clean($options->db_sslcert))) { 302 return Text::sprintf('INSTL_DATABASE_ENCRYPTION_MSG_FILE_FIELD_BAD', Text::_('INSTL_DATABASE_ENCRYPTION_CERT_LABEL')); 303 } 304 } else { 305 // Reset unused options 306 if (!empty($options->db_sslkey)) { 307 $options->db_sslkey = ''; 308 $optionsChanged = true; 309 } 310 311 if (!empty($options->db_sslcert)) { 312 $options->db_sslcert = ''; 313 $optionsChanged = true; 314 } 315 } 316 } 317 318 // Save options to session data if changed 319 if ($optionsChanged) { 320 $optsArr = ArrayHelper::fromObject($options); 321 Factory::getSession()->set('setup.options', $optsArr); 322 } 323 324 return false; 325 } 326 327 /** 328 * Security check for remote db hosts 329 * 330 * @param \stdClass $options The session options 331 * 332 * @return boolean True if passed, otherwise false. 333 * 334 * @since 4.0.0 335 */ 336 public static function checkRemoteDbHost($options) 337 { 338 // Security check for remote db hosts: Check env var if disabled 339 $shouldCheckLocalhost = getenv('JOOMLA_INSTALLATION_DISABLE_LOCALHOST_CHECK') !== '1'; 340 341 // Per default allowed DB hosts: localhost / 127.0.0.1 / ::1 (optionally with port) 342 $localhost = '/^(((localhost|127\.0\.0\.1|\[\:\:1\])(\:[1-9]{1}[0-9]{0,4})?)|(\:\:1))$/'; 343 344 // Check the security file if the db_host is not localhost / 127.0.0.1 / ::1 345 if ($shouldCheckLocalhost && preg_match($localhost, $options->db_host) !== 1) { 346 $remoteDbFileTestsPassed = Factory::getSession()->get('remoteDbFileTestsPassed', false); 347 348 // When all checks have been passed we don't need to do this here again. 349 if ($remoteDbFileTestsPassed === false) { 350 $generalRemoteDatabaseMessage = Text::sprintf( 351 'INSTL_DATABASE_HOST_IS_NOT_LOCALHOST_GENERAL_MESSAGE', 352 'https://docs.joomla.org/Special:MyLanguage/J3.x:Secured_procedure_for_installing_Joomla_with_a_remote_database' 353 ); 354 355 $remoteDbFile = Factory::getSession()->get('remoteDbFile', false); 356 357 if ($remoteDbFile === false) { 358 // Add the general message 359 Factory::getApplication()->enqueueMessage($generalRemoteDatabaseMessage, 'warning'); 360 361 // This is the file you need to remove if you want to use a remote database 362 $remoteDbFile = '_Joomla' . UserHelper::genRandomPassword(21) . '.txt'; 363 Factory::getSession()->set('remoteDbFile', $remoteDbFile); 364 365 // Get the path 366 $remoteDbPath = JPATH_INSTALLATION . '/' . $remoteDbFile; 367 368 // When the path is not writable the user needs to create the file manually 369 if (!File::write($remoteDbPath, '')) { 370 // Request to create the file manually 371 Factory::getApplication()->enqueueMessage( 372 Text::sprintf( 373 'INSTL_DATABASE_HOST_IS_NOT_LOCALHOST_CREATE_FILE', 374 $remoteDbFile, 375 'installation', 376 Text::_('INSTL_INSTALL_JOOMLA') 377 ), 378 'notice' 379 ); 380 381 Factory::getSession()->set('remoteDbFileUnwritable', true); 382 383 return false; 384 } 385 386 // Save the file name to the session 387 Factory::getSession()->set('remoteDbFileWrittenByJoomla', true); 388 389 // Request to delete that file 390 Factory::getApplication()->enqueueMessage( 391 Text::sprintf( 392 'INSTL_DATABASE_HOST_IS_NOT_LOCALHOST_DELETE_FILE', 393 $remoteDbFile, 394 'installation', 395 Text::_('INSTL_INSTALL_JOOMLA') 396 ), 397 'notice' 398 ); 399 400 return false; 401 } 402 403 if ( 404 Factory::getSession()->get('remoteDbFileWrittenByJoomla', false) === true 405 && File::exists(JPATH_INSTALLATION . '/' . $remoteDbFile) 406 ) { 407 // Add the general message 408 Factory::getApplication()->enqueueMessage($generalRemoteDatabaseMessage, 'warning'); 409 410 // Request to delete the file 411 Factory::getApplication()->enqueueMessage( 412 Text::sprintf( 413 'INSTL_DATABASE_HOST_IS_NOT_LOCALHOST_DELETE_FILE', 414 $remoteDbFile, 415 'installation', 416 Text::_('INSTL_INSTALL_JOOMLA') 417 ), 418 'notice' 419 ); 420 421 return false; 422 } 423 424 if (Factory::getSession()->get('remoteDbFileUnwritable', false) === true && !File::exists(JPATH_INSTALLATION . '/' . $remoteDbFile)) { 425 // Add the general message 426 Factory::getApplication()->enqueueMessage($generalRemoteDatabaseMessage, 'warning'); 427 428 // Request to create the file manually 429 Factory::getApplication()->enqueueMessage( 430 Text::sprintf( 431 'INSTL_DATABASE_HOST_IS_NOT_LOCALHOST_CREATE_FILE', 432 $remoteDbFile, 433 'installation', 434 Text::_('INSTL_INSTALL_JOOMLA') 435 ), 436 'notice' 437 ); 438 439 return false; 440 } 441 442 // All tests for this session passed set it to the session 443 Factory::getSession()->set('remoteDbFileTestsPassed', true); 444 } 445 } 446 447 return true; 448 } 449 450 /** 451 * Check database server parameters after connection 452 * 453 * @param DatabaseDriver $db Database object 454 * @param \stdClass $options The session options 455 * 456 * @return string|boolean A string with the translated error message if 457 * some server parameter is not ok, otherwise false. 458 * 459 * @since 4.0.0 460 */ 461 public static function checkDbServerParameters($db, $options) 462 { 463 $dbVersion = $db->getVersion(); 464 465 // Get required database version 466 $minDbVersionRequired = self::getMinimumServerVersion($db, $options); 467 468 // Check minimum database version 469 if (version_compare($dbVersion, $minDbVersionRequired) < 0) { 470 if (in_array($options->db_type, ['mysql', 'mysqli']) && $db->isMariaDb()) { 471 $errorMessage = Text::sprintf( 472 'INSTL_DATABASE_INVALID_MARIADB_VERSION', 473 $minDbVersionRequired, 474 $dbVersion 475 ); 476 } else { 477 $errorMessage = Text::sprintf( 478 'INSTL_DATABASE_INVALID_' . strtoupper($options->db_type) . '_VERSION', 479 $minDbVersionRequired, 480 $dbVersion 481 ); 482 } 483 484 return $errorMessage; 485 } 486 487 // Check database connection encryption 488 if ($options->db_encryption !== 0 && empty($db->getConnectionEncryption())) { 489 if ($db->isConnectionEncryptionSupported()) { 490 $errorMessage = Text::_('INSTL_DATABASE_ENCRYPTION_MSG_CONN_NOT_ENCRYPT'); 491 } else { 492 $errorMessage = Text::_('INSTL_DATABASE_ENCRYPTION_MSG_SRV_NOT_SUPPORTS'); 493 } 494 495 return $errorMessage; 496 } 497 498 return false; 499 } 500 }
title
Description
Body
title
Description
Body
title
Description
Body
title
Body
Generated: Wed Sep 7 05:41:13 2022 | Chilli.vc Blog - For Webmaster,Blog-Writer,System Admin and Domainer |