* @license GNU General Public License version 2 or later; see LICENSE.txt */ namespace Joomla\CMS\Installation\Model; use Joomla\CMS\Factory; use Joomla\CMS\Form\Form; use Joomla\CMS\Installation\Helper\DatabaseHelper; use Joomla\CMS\Language\LanguageHelper; use Joomla\CMS\Language\Text; use Joomla\Utilities\ArrayHelper; // phpcs:disable PSR1.Files.SideEffects \defined('_JEXEC') or die; // phpcs:enable PSR1.Files.SideEffects /** * Setup model for the Joomla Core Installer. * * @since 3.1 */ class SetupModel extends BaseInstallationModel { /** * Get the current setup options from the session. * * @return array An array of options from the session. * * @since 3.1 */ public function getOptions() { if (!empty(Factory::getSession()->get('setup.options', array()))) { return Factory::getSession()->get('setup.options', array()); } } /** * Store the current setup options in the session. * * @param array $options The installation options. * * @return array An array of options from the session. * * @since 3.1 */ public function storeOptions($options) { // Get the current setup options from the session. $old = (array) $this->getOptions(); // Ensure that we have language if (!isset($options['language']) || empty($options['language'])) { $options['language'] = Factory::getLanguage()->getTag(); } // Store passwords as a separate key that is not used in the forms foreach (array('admin_password', 'db_pass') as $passwordField) { if (isset($options[$passwordField])) { $plainTextKey = $passwordField . '_plain'; $options[$plainTextKey] = $options[$passwordField]; unset($options[$passwordField]); } } // Get the session $session = Factory::getSession(); $options['helpurl'] = $session->get('setup.helpurl', null); // Merge the new setup options into the current ones and store in the session. $options = array_merge($old, (array) $options); $session->set('setup.options', $options); return $options; } /** * Method to get the form. * * @param string|null $view The view being processed. * * @return Form|boolean JForm object on success, false on failure. * * @since 3.1 */ public function getForm($view = null) { if (!$view) { $view = Factory::getApplication()->input->getWord('view', 'setup'); } // Get the form. Form::addFormPath(JPATH_COMPONENT . '/forms'); try { $form = Form::getInstance('jform', $view, array('control' => 'jform')); } catch (\Exception $e) { Factory::getApplication()->enqueueMessage($e->getMessage(), 'error'); return false; } // Check the session for previously entered form data. $data = (array) $this->getOptions(); // Bind the form data if present. if (!empty($data)) { $form->bind($data); } return $form; } /** * Method to check the form data. * * @param string $page The view being checked. * * @return array|boolean Array with the validated form data or boolean false on a validation failure. * * @since 3.1 */ public function checkForm($page = 'setup') { // Get the posted values from the request and validate them. $data = Factory::getApplication()->input->post->get('jform', array(), 'array'); $return = $this->validate($data, $page); // Attempt to save the data before validation. $form = $this->getForm(); $data = $form->filter($data); $this->storeOptions($data); // Check for validation errors. if ($return === false) { return false; } // Store the options in the session. return $this->storeOptions($return); } /** * Generate a panel of language choices for the user to select their language. * * @return array * * @since 3.1 */ public function getLanguages() { // Detect the native language. $native = LanguageHelper::detectLanguage(); if (empty($native)) { $native = 'en-GB'; } // Get a forced language if it exists. $forced = Factory::getApplication()->getLocalise(); if (!empty($forced['language'])) { $native = $forced['language']; } // Get the list of available languages. $list = LanguageHelper::createLanguageList($native); if (!$list || $list instanceof \Exception) { $list = array(); } return $list; } /** * Method to validate the form data. * * @param array $data The form data. * @param string|null $view The view. * * @return array|boolean Array of filtered data if valid, false otherwise. * * @since 3.1 */ public function validate($data, $view = null) { // Get the form. $form = $this->getForm($view); // Check for an error. if ($form === false) { return false; } // Filter and validate the form data. $data = $form->filter($data); $return = $form->validate($data); // Check for an error. if ($return instanceof \Exception) { Factory::getApplication()->enqueueMessage($return->getMessage(), 'warning'); return false; } // Check the validation results. if ($return === false) { // Get the validation messages from the form. $messages = array_reverse($form->getErrors()); foreach ($messages as $message) { if ($message instanceof \Exception) { Factory::getApplication()->enqueueMessage($message->getMessage(), 'warning'); } else { Factory::getApplication()->enqueueMessage($message, 'warning'); } } return false; } return $data; } /** * Method to validate the db connection properties. * * @return boolean * * @since 4.0.0 * @throws \Exception */ public function validateDbConnection() { $options = $this->getOptions(); // Get the options as an object for easier handling. $options = ArrayHelper::toObject($options); // Load the backend language files so that the DB error messages work. $lang = Factory::getLanguage(); $currentLang = $lang->getTag(); // Load the selected language if (LanguageHelper::exists($currentLang, JPATH_ADMINISTRATOR)) { $lang->load('joomla', JPATH_ADMINISTRATOR, $currentLang, true); } else { // Pre-load en-GB in case the chosen language files do not exist. $lang->load('joomla', JPATH_ADMINISTRATOR, 'en-GB', true); } // Validate and clean up connection parameters $paramsCheck = DatabaseHelper::validateConnectionParameters($options); if ($paramsCheck) { // Validation error: Enqueue the error message Factory::getApplication()->enqueueMessage($paramsCheck, 'error'); return false; } // Security check for remote db hosts if (!DatabaseHelper::checkRemoteDbHost($options)) { // Messages have been enqueued in the called function. return false; } // Get a database object. try { $db = DatabaseHelper::getDbo( $options->db_type, $options->db_host, $options->db_user, $options->db_pass_plain, $options->db_name, $options->db_prefix, false, DatabaseHelper::getEncryptionSettings($options) ); $db->connect(); } catch (\RuntimeException $e) { if ( $options->db_type === 'mysql' && strpos($e->getMessage(), '[1049] Unknown database') === 42 || $options->db_type === 'pgsql' && strpos($e->getMessage(), 'database "' . $options->db_name . '" does not exist') ) { // Database doesn't exist: Skip the below checks, they will be done later at database creation return true; } Factory::getApplication()->enqueueMessage(Text::sprintf('INSTL_DATABASE_COULD_NOT_CONNECT', $e->getMessage()), 'error'); return false; } // Check database server parameters $dbServerCheck = DatabaseHelper::checkDbServerParameters($db, $options); if ($dbServerCheck) { // Some server parameter is not ok: Enqueue the error message Factory::getApplication()->enqueueMessage($dbServerCheck, 'error'); return false; } return true; } }