. * --------------------------------------------------------------------- */ namespace Glpi\Console\Database; if (!defined('GLPI_ROOT')) { die("Sorry. You can't access this file directly"); } use Config; use DBConnection; use Glpi\Console\AbstractCommand; use Glpi\Console\Command\ForceNoPluginsOptionCommandInterface; use Symfony\Component\Console\Exception\InvalidArgumentException; use Symfony\Component\Console\Helper\Table; use Symfony\Component\Console\Input\InputInterface; use Symfony\Component\Console\Input\InputOption; use Symfony\Component\Console\Output\OutputInterface; use Symfony\Component\Console\Question\ConfirmationQuestion; use Symfony\Component\Console\Question\Question; abstract class AbstractConfigureCommand extends AbstractCommand implements ForceNoPluginsOptionCommandInterface { /** * Error code returned if DB configuration is aborted by user. * * @var integer */ const ABORTED_BY_USER = -1; /** * Error code returned if DB configuration succeed. * * @var integer */ const SUCCESS = 0; /** * Error code returned if DB connection initialization fails. * * @var integer */ const ERROR_DB_CONNECTION_FAILED = 1; /** * Error code returned if DB engine is unsupported. * * @var integer */ const ERROR_DB_ENGINE_UNSUPPORTED = 2; /** * Error code returned when trying to configure and having a DB config already set. * * @var integer */ const ERROR_DB_CONFIG_ALREADY_SET = 3; /** * Error code returned when failing to save database configuration file. * * @var integer */ const ERROR_DB_CONFIG_FILE_NOT_SAVED = 4; protected function configure() { parent::configure(); $this->setName('glpi:database:install'); $this->setAliases(['db:install']); $this->setDescription('Install database schema'); $this->addOption( 'db-host', 'H', InputOption::VALUE_OPTIONAL, __('Database host'), 'localhost' ); $this->addOption( 'db-name', 'd', InputOption::VALUE_REQUIRED, __('Database name') ); $this->addOption( 'db-password', 'p', InputOption::VALUE_OPTIONAL, __('Database password (will be prompted for value if option passed without value)'), '' // Empty string by default (enable detection of null if passed without value) ); $this->addOption( 'db-port', 'P', InputOption::VALUE_OPTIONAL, __('Database port') ); $this->addOption( 'db-user', 'u', InputOption::VALUE_REQUIRED, __('Database user') ); $this->addOption( 'reconfigure', 'r', InputOption::VALUE_NONE, __('Reconfigure database, override configuration file if it already exists') ); } protected function interact(InputInterface $input, OutputInterface $output) { $questions = [ 'db-name' => new Question(__('Database name:'), ''), // Required 'db-user' => new Question(__('Database user:'), ''), // Required 'db-password' => new Question(__('Database password:'), ''), // Prompt if null (passed without value) ]; $questions['db-password']->setHidden(true); // Make password input hidden foreach ($questions as $name => $question) { if (null === $input->getOption($name)) { /** @var \Symfony\Component\Console\Helper\QuestionHelper $question_helper */ $question_helper = $this->getHelper('question'); $value = $question_helper->ask($input, $output, $question); $input->setOption($name, $value); } } } protected function initDbConnection() { return; // Prevent DB connection } /** * Save database configuration file. * * @param InputInterface $input * @param OutputInterface $output * @throws InvalidArgumentException * @return string */ protected function configureDatabase(InputInterface $input, OutputInterface $output) { $db_pass = $input->getOption('db-password'); $db_host = $input->getOption('db-host'); $db_name = $input->getOption('db-name'); $db_port = $input->getOption('db-port'); $db_user = $input->getOption('db-user'); $db_hostport = $db_host . (!empty($db_port) ? ':' . $db_port : ''); $reconfigure = $input->getOption('reconfigure'); if (file_exists(GLPI_CONFIG_DIR . '/config_db.php') && !$reconfigure) { // Prevent overriding of existing DB $output->writeln( '' . __('Database configuration already exists. Use --reconfigure option to override existing configuration.') . '' ); return self::ERROR_DB_CONFIG_ALREADY_SET; } $this->validateConfigInput($input); $run = $this->askForDbConfigConfirmation( $input, $output, $db_hostport, $db_name, $db_user ); if (!$run) { $output->writeln( '' . __('Configuration aborted.') . '', OutputInterface::VERBOSITY_VERBOSE ); return self::ABORTED_BY_USER; } $mysqli = new \mysqli(); if (intval($db_port) > 0) { // Network port @$mysqli->connect($db_host, $db_user, $db_pass, null, $db_port); } else { // Unix Domain Socket @$mysqli->connect($db_host, $db_user, $db_pass, null, 0, $db_port); } if (0 !== $mysqli->connect_errno) { $message = sprintf( __('Database connection failed with message "(%s) %s".'), $mysqli->connect_errno, $mysqli->connect_error ); $output->writeln('' . $message . '', OutputInterface::VERBOSITY_QUIET); return self::ERROR_DB_CONNECTION_FAILED; } ob_start(); $db_version_data = $mysqli->query('SELECT version()')->fetch_array(); $checkdb = Config::displayCheckDbEngine(false, $db_version_data[0]); $message = ob_get_clean(); if ($checkdb > 0) { $output->writeln('' . $message . '', OutputInterface::VERBOSITY_QUIET); return self::ERROR_DB_ENGINE_UNSUPPORTED; } $db_name = $mysqli->real_escape_string($db_name); $output->writeln( '' . __('Saving configuration file...') . '', OutputInterface::VERBOSITY_VERBOSE ); if (!DBConnection::createMainConfig($db_hostport, $db_user, $db_pass, $db_name)) { $message = sprintf( __('Cannot write configuration file "%s".'), GLPI_CONFIG_DIR . DIRECTORY_SEPARATOR . 'config_db.php' ); $output->writeln( '' . $message . '', OutputInterface::VERBOSITY_QUIET ); return self::ERROR_DB_CONFIG_FILE_NOT_SAVED; } return self::SUCCESS; } public function getNoPluginsOptionValue() { return true; } /** * Check if DB is already configured. * * @return boolean */ protected function isDbAlreadyConfigured() { return file_exists(GLPI_CONFIG_DIR . '/config_db.php'); } /** * Validate configuration variables from input. * * @param InputInterface $input * * @throws InvalidArgumentException */ protected function validateConfigInput(InputInterface $input) { $db_name = $input->getOption('db-name'); $db_user = $input->getOption('db-user'); $db_pass = $input->getOption('db-password'); if (empty($db_name)) { throw new InvalidArgumentException( __('Database name defined by --db-name option cannot be empty.') ); } if (empty($db_user)) { throw new InvalidArgumentException( __('Database user defined by --db-user option cannot be empty.') ); } if (null === $db_pass) { // Will be null if option used without value and without interaction throw new InvalidArgumentException( __('--db-password option value cannot be null.') ); } } /** * Ask user to confirm DB configuration. * * @param InputInterface $input * @param OutputInterface $output * * @return boolean */ protected function askForDbConfigConfirmation( InputInterface $input, OutputInterface $output, $db_hostport, $db_name, $db_user) { $informations = new Table($output); $informations->addRow([__('Database host'), $db_hostport]); $informations->addRow([__('Database name'), $db_name]); $informations->addRow([__('Database user'), $db_user]); $informations->render(); if ($input->getOption('no-interaction')) { // Consider that config is validated if user require no interaction return true; } /** @var \Symfony\Component\Console\Helper\QuestionHelper $question_helper */ $question_helper = $this->getHelper('question'); return $question_helper->ask( $input, $output, new ConfirmationQuestion(__('Do you want to continue ?') . ' [Yes/no]', true) ); } }