1<?php
2/**
3 * @author Bernhard Posselt <dev@bernhard-posselt.com>
4 * @author Christian Kampka <christian@kampka.net>
5 * @author Jörn Friedrich Dreyer <jfd@butonic.de>
6 * @author Morris Jobke <hey@morrisjobke.de>
7 * @author Thomas Müller <thomas.mueller@tmit.eu>
8 *
9 * @copyright Copyright (c) 2018, ownCloud GmbH
10 * @license AGPL-3.0
11 *
12 * This code is free software: you can redistribute it and/or modify
13 * it under the terms of the GNU Affero General Public License, version 3,
14 * as published by the Free Software Foundation.
15 *
16 * This program is distributed in the hope that it will be useful,
17 * but WITHOUT ANY WARRANTY; without even the implied warranty of
18 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
19 * GNU Affero General Public License for more details.
20 *
21 * You should have received a copy of the GNU Affero General Public License, version 3,
22 * along with this program.  If not, see <http://www.gnu.org/licenses/>
23 *
24 */
25namespace OC\Core\Command\Maintenance;
26
27use InvalidArgumentException;
28use OC\Setup;
29use OCP\IConfig;
30use Symfony\Component\Console\Command\Command;
31use Symfony\Component\Console\Input\InputInterface;
32use Symfony\Component\Console\Input\InputOption;
33use Symfony\Component\Console\Output\OutputInterface;
34use Symfony\Component\Console\Question\Question;
35
36class Install extends Command {
37
38	/**
39	 * @var IConfig
40	 */
41	private $config;
42
43	public function __construct(IConfig $config) {
44		parent::__construct();
45		$this->config = $config;
46	}
47
48	protected function configure() {
49		$this
50			->setName('maintenance:install')
51			->setDescription('Install ownCloud.')
52			->addOption('database', null, InputOption::VALUE_REQUIRED, 'Supported database type.', 'sqlite')
53			->addOption('database-connection-string', null, InputOption::VALUE_REQUIRED, 'Oracle specific connection string. As soon as this parameter is provided other parameters like database-host and database-name are not used and do not need to be provided')
54			->addOption('database-name', null, InputOption::VALUE_REQUIRED, 'Name of the database.')
55			->addOption('database-host', null, InputOption::VALUE_REQUIRED, 'Hostname of the database.', 'localhost')
56			->addOption('database-user', null, InputOption::VALUE_REQUIRED, 'User name to connect to the database.')
57			->addOption('database-pass', null, InputOption::VALUE_OPTIONAL, 'Password of the database user.', null)
58			->addOption('database-table-prefix', null, InputOption::VALUE_OPTIONAL, 'Prefix for all tables (default: oc_).', null)
59			->addOption('admin-user', null, InputOption::VALUE_REQUIRED, 'User name of the admin account.', 'admin')
60			->addOption('admin-pass', null, InputOption::VALUE_REQUIRED, 'Password of the admin account.')
61			->addOption('data-dir', null, InputOption::VALUE_REQUIRED, 'Path to the data directory.', \OC::$SERVERROOT."/data");
62	}
63
64	protected function execute(InputInterface $input, OutputInterface $output) {
65
66		// validate the environment
67		$server = \OC::$server;
68		$setupHelper = new Setup(
69			$this->config,
70			$server->getIniWrapper(),
71			$server->getL10N('lib'),
72			new \OC_Defaults(),
73			$server->getLogger(),
74			$server->getSecureRandom()
75		);
76		$sysInfo = $setupHelper->getSystemInfo(true);
77		$errors = $sysInfo['errors'];
78		if (\count($errors) > 0) {
79			$this->printErrors($output, $errors);
80
81			// ignore the OS X setup warning
82			if (\count($errors) !== 1 ||
83				(string)($errors[0]['error']) !== 'Mac OS X is not supported and ownCloud will not work properly on this platform. Use it at your own risk! ') {
84				return 1;
85			}
86		}
87
88		// validate user input
89		$options = $this->validateInput($input, $output, \array_keys($sysInfo['databases']));
90
91		// perform installation
92		$errors = $setupHelper->install($options);
93		if (\count($errors) > 0) {
94			$this->printErrors($output, $errors);
95			return 1;
96		}
97		$output->writeln("ownCloud was successfully installed");
98		return 0;
99	}
100
101	/**
102	 * @param InputInterface $input
103	 * @param OutputInterface $output
104	 * @param string[] $supportedDatabases
105	 * @return array
106	 */
107	protected function validateInput(InputInterface $input, OutputInterface $output, $supportedDatabases) {
108		$db = \strtolower($input->getOption('database'));
109
110		if (!\in_array($db, $supportedDatabases)) {
111			throw new InvalidArgumentException("Database <$db> is not supported.");
112		}
113
114		$dbUser = $input->getOption('database-user');
115		$dbPass = $input->getOption('database-pass');
116		$dbName = $input->getOption('database-name');
117		$dbConnectionString = $input->getOption('database-connection-string');
118		if ($db === 'oci') {
119			// an empty hostname needs to be read from the raw parameters
120			$dbHost = $input->getParameterOption('--database-host', '');
121		} else {
122			$dbHost = $input->getOption('database-host');
123		}
124		$dbTablePrefix = 'oc_';
125		if ($input->hasParameterOption('--database-table-prefix')) {
126			$dbTablePrefix = (string) $input->getOption('database-table-prefix');
127			$dbTablePrefix = \trim($dbTablePrefix);
128		}
129		if ($input->hasParameterOption('--database-pass')) {
130			$dbPass = (string) $input->getOption('database-pass');
131		}
132		$adminLogin = $input->getOption('admin-user');
133		$adminPassword = $input->getOption('admin-pass');
134		$dataDir = $input->getOption('data-dir');
135
136		if ($db !== 'sqlite') {
137			if ($dbUser === null) {
138				throw new InvalidArgumentException("Database user not provided.");
139			}
140			if ($dbName === null && $dbConnectionString === null) {
141				throw new InvalidArgumentException('Database name and connection string not provided.');
142			}
143			if ($dbPass === null) {
144				/** @var $dialog \Symfony\Component\Console\Helper\QuestionHelper */
145				$dialog = $this->getHelperSet()->get('question');
146				'@phan-var \Symfony\Component\Console\Helper\QuestionHelper $dialog';
147				$q = new Question("<question>What is the password to access the database with user <$dbUser>?</question>", false);
148				$q->setHidden(true);
149				$dbPass = $dialog->ask($input, $output, $q);
150			}
151		}
152
153		if ($adminPassword === null) {
154			/** @var $dialog \Symfony\Component\Console\Helper\QuestionHelper */
155			$dialog = $this->getHelperSet()->get('question');
156			'@phan-var \Symfony\Component\Console\Helper\QuestionHelper $dialog';
157			$q = new Question("<question>What is the password you like to use for the admin account <$adminLogin>?</question>", false);
158			$q->setHidden(true);
159			$adminPassword = $dialog->ask($input, $output, $q);
160		}
161
162		$options = [
163			'dbtype' => $db,
164			'dbuser' => $dbUser,
165			'dbpass' => $dbPass,
166			'dbname' => $dbName,
167			'dbhost' => $dbHost,
168			'dbconnectionstring' => $dbConnectionString,
169			'dbtableprefix' => $dbTablePrefix,
170			'adminlogin' => $adminLogin,
171			'adminpass' => $adminPassword,
172			'directory' => $dataDir
173		];
174		return $options;
175	}
176
177	/**
178	 * @param OutputInterface $output
179	 * @param $errors
180	 */
181	protected function printErrors(OutputInterface $output, $errors) {
182		foreach ($errors as $error) {
183			if (\is_array($error)) {
184				$output->writeln('<error>' . (string)$error['error'] . '</error>');
185				$output->writeln('<info> -> ' . (string)$error['hint'] . '</info>');
186			} else {
187				$output->writeln('<error>' . (string)$error . '</error>');
188			}
189		}
190	}
191}
192