summaryrefslogtreecommitdiffstats
path: root/core/Command
diff options
context:
space:
mode:
Diffstat (limited to 'core/Command')
-rw-r--r--core/Command/App/CheckCode.php181
-rw-r--r--core/Command/App/Disable.php71
-rw-r--r--core/Command/App/Enable.php83
-rw-r--r--core/Command/App/GetPath.php62
-rw-r--r--core/Command/App/ListApps.php119
-rw-r--r--core/Command/Background/Ajax.php33
-rw-r--r--core/Command/Background/Base.php77
-rw-r--r--core/Command/Background/Cron.php33
-rw-r--r--core/Command/Background/WebCron.php33
-rw-r--r--core/Command/Base.php160
-rw-r--r--core/Command/Check.php61
-rw-r--r--core/Command/Config/App/DeleteConfig.php81
-rw-r--r--core/Command/Config/App/GetConfig.php93
-rw-r--r--core/Command/Config/App/SetConfig.php89
-rw-r--r--core/Command/Config/Import.php195
-rw-r--r--core/Command/Config/ListConfigs.php129
-rw-r--r--core/Command/Config/System/DeleteConfig.php117
-rw-r--r--core/Command/Config/System/GetConfig.php100
-rw-r--r--core/Command/Config/System/SetConfig.php198
-rw-r--r--core/Command/Db/ConvertType.php311
-rw-r--r--core/Command/Db/GenerateChangeScript.php58
-rw-r--r--core/Command/Encryption/ChangeKeyStorageRoot.php270
-rw-r--r--core/Command/Encryption/DecryptAll.php160
-rw-r--r--core/Command/Encryption/Disable.php56
-rw-r--r--core/Command/Encryption/Enable.php78
-rw-r--r--core/Command/Encryption/EncryptAll.php134
-rw-r--r--core/Command/Encryption/ListModules.php80
-rw-r--r--core/Command/Encryption/SetDefaultModule.php68
-rw-r--r--core/Command/Encryption/ShowKeyStorageRoot.php58
-rw-r--r--core/Command/Encryption/Status.php56
-rw-r--r--core/Command/Integrity/CheckApp.php69
-rw-r--r--core/Command/Integrity/CheckCore.php62
-rw-r--r--core/Command/Integrity/SignApp.php107
-rw-r--r--core/Command/Integrity/SignCore.php100
-rw-r--r--core/Command/L10n/CreateJs.php137
-rw-r--r--core/Command/Log/Manage.php171
-rw-r--r--core/Command/Log/OwnCloud.php124
-rw-r--r--core/Command/Maintenance/Install.php178
-rw-r--r--core/Command/Maintenance/Mimetype/UpdateDB.php97
-rw-r--r--core/Command/Maintenance/Mimetype/UpdateJS.php129
-rw-r--r--core/Command/Maintenance/Mode.php75
-rw-r--r--core/Command/Maintenance/Repair.php91
-rw-r--r--core/Command/Maintenance/SingleUser.php78
-rw-r--r--core/Command/Security/ImportCertificate.php67
-rw-r--r--core/Command/Security/ListCertificates.php96
-rw-r--r--core/Command/Security/RemoveCertificate.php59
-rw-r--r--core/Command/Status.php49
-rw-r--r--core/Command/Upgrade.php295
-rw-r--r--core/Command/User/Add.php154
-rw-r--r--core/Command/User/Delete.php70
-rw-r--r--core/Command/User/LastSeen.php74
-rw-r--r--core/Command/User/Report.php86
-rw-r--r--core/Command/User/ResetPassword.php121
53 files changed, 5733 insertions, 0 deletions
diff --git a/core/Command/App/CheckCode.php b/core/Command/App/CheckCode.php
new file mode 100644
index 00000000000..78f4390e70a
--- /dev/null
+++ b/core/Command/App/CheckCode.php
@@ -0,0 +1,181 @@
+<?php
+/**
+ * @author Joas Schilling <nickvergessen@owncloud.com>
+ * @author Morris Jobke <hey@morrisjobke.de>
+ * @author Robin McCorkell <robin@mccorkell.me.uk>
+ * @author Thomas Müller <thomas.mueller@tmit.eu>
+ *
+ * @copyright Copyright (c) 2016, ownCloud, Inc.
+ * @license AGPL-3.0
+ *
+ * This code is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU Affero General Public License, version 3,
+ * as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU Affero General Public License for more details.
+ *
+ * You should have received a copy of the GNU Affero General Public License, version 3,
+ * along with this program. If not, see <http://www.gnu.org/licenses/>
+ *
+ */
+
+namespace OC\Core\Command\App;
+
+use OC\App\CodeChecker\CodeChecker;
+use OC\App\CodeChecker\EmptyCheck;
+use OC\App\CodeChecker\InfoChecker;
+use OC\App\InfoParser;
+use Symfony\Component\Console\Command\Command;
+use Symfony\Component\Console\Input\InputArgument;
+use Symfony\Component\Console\Input\InputInterface;
+use Symfony\Component\Console\Input\InputOption;
+use Symfony\Component\Console\Output\OutputInterface;
+
+class CheckCode extends Command {
+
+ /** @var InfoParser */
+ private $infoParser;
+
+ protected $checkers = [
+ 'private' => '\OC\App\CodeChecker\PrivateCheck',
+ 'deprecation' => '\OC\App\CodeChecker\DeprecationCheck',
+ 'strong-comparison' => '\OC\App\CodeChecker\StrongComparisonCheck',
+ ];
+
+ public function __construct(InfoParser $infoParser) {
+ parent::__construct();
+ $this->infoParser = $infoParser;
+ }
+
+ protected function configure() {
+ $this
+ ->setName('app:check-code')
+ ->setDescription('check code to be compliant')
+ ->addArgument(
+ 'app-id',
+ InputArgument::REQUIRED,
+ 'check the specified app'
+ )
+ ->addOption(
+ 'checker',
+ 'c',
+ InputOption::VALUE_REQUIRED | InputOption::VALUE_IS_ARRAY,
+ 'enable the specified checker(s)',
+ [ 'private', 'deprecation', 'strong-comparison' ]
+ )
+ ->addOption(
+ '--skip-validate-info',
+ null,
+ InputOption::VALUE_NONE,
+ 'skips the info.xml/version check'
+ );
+ }
+
+ protected function execute(InputInterface $input, OutputInterface $output) {
+ $appId = $input->getArgument('app-id');
+
+ $checkList = new EmptyCheck();
+ foreach ($input->getOption('checker') as $checker) {
+ if (!isset($this->checkers[$checker])) {
+ throw new \InvalidArgumentException('Invalid checker: '.$checker);
+ }
+ $checkerClass = $this->checkers[$checker];
+ $checkList = new $checkerClass($checkList);
+ }
+
+ $codeChecker = new CodeChecker($checkList);
+
+ $codeChecker->listen('CodeChecker', 'analyseFileBegin', function($params) use ($output) {
+ if(OutputInterface::VERBOSITY_VERBOSE <= $output->getVerbosity()) {
+ $output->writeln("<info>Analysing {$params}</info>");
+ }
+ });
+ $codeChecker->listen('CodeChecker', 'analyseFileFinished', function($filename, $errors) use ($output) {
+ $count = count($errors);
+
+ // show filename if the verbosity is low, but there are errors in a file
+ if($count > 0 && OutputInterface::VERBOSITY_VERBOSE > $output->getVerbosity()) {
+ $output->writeln("<info>Analysing {$filename}</info>");
+ }
+
+ // show error count if there are errors present or the verbosity is high
+ if($count > 0 || OutputInterface::VERBOSITY_VERBOSE <= $output->getVerbosity()) {
+ $output->writeln(" {$count} errors");
+ }
+ usort($errors, function($a, $b) {
+ return $a['line'] >$b['line'];
+ });
+
+ foreach($errors as $p) {
+ $line = sprintf("%' 4d", $p['line']);
+ $output->writeln(" <error>line $line: {$p['disallowedToken']} - {$p['reason']}</error>");
+ }
+ });
+ $errors = $codeChecker->analyse($appId);
+
+ if(!$input->getOption('skip-validate-info')) {
+ $infoChecker = new InfoChecker($this->infoParser);
+
+ $infoChecker->listen('InfoChecker', 'mandatoryFieldMissing', function($key) use ($output) {
+ $output->writeln("<error>Mandatory field missing: $key</error>");
+ });
+
+ $infoChecker->listen('InfoChecker', 'deprecatedFieldFound', function($key, $value) use ($output) {
+ if($value === [] || is_null($value) || $value === '') {
+ $output->writeln("<info>Deprecated field available: $key</info>");
+ } else {
+ $output->writeln("<info>Deprecated field available: $key => $value</info>");
+ }
+ });
+
+ $infoChecker->listen('InfoChecker', 'missingRequirement', function($minMax) use ($output) {
+ $output->writeln("<comment>ownCloud $minMax version requirement missing (will be an error in ownCloud 11 and later)</comment>");
+ });
+
+ $infoChecker->listen('InfoChecker', 'duplicateRequirement', function($minMax) use ($output) {
+ $output->writeln("<error>Duplicate $minMax ownCloud version requirement found</error>");
+ });
+
+ $infoChecker->listen('InfoChecker', 'differentVersions', function($versionFile, $infoXML) use ($output) {
+ $output->writeln("<error>Different versions provided (appinfo/version: $versionFile - appinfo/info.xml: $infoXML)</error>");
+ });
+
+ $infoChecker->listen('InfoChecker', 'sameVersions', function($path) use ($output) {
+ $output->writeln("<info>Version file isn't needed anymore and can be safely removed ($path)</info>");
+ });
+
+ $infoChecker->listen('InfoChecker', 'migrateVersion', function($version) use ($output) {
+ $output->writeln("<info>Migrate the app version to appinfo/info.xml (add <version>$version</version> to appinfo/info.xml and remove appinfo/version)</info>");
+ });
+
+ if(OutputInterface::VERBOSITY_VERBOSE <= $output->getVerbosity()) {
+ $infoChecker->listen('InfoChecker', 'mandatoryFieldFound', function($key, $value) use ($output) {
+ $output->writeln("<info>Mandatory field available: $key => $value</info>");
+ });
+
+ $infoChecker->listen('InfoChecker', 'optionalFieldFound', function($key, $value) use ($output) {
+ $output->writeln("<info>Optional field available: $key => $value</info>");
+ });
+
+ $infoChecker->listen('InfoChecker', 'unusedFieldFound', function($key, $value) use ($output) {
+ $output->writeln("<info>Unused field available: $key => $value</info>");
+ });
+ }
+
+ $infoErrors = $infoChecker->analyse($appId);
+
+ $errors = array_merge($errors, $infoErrors);
+ }
+
+ if (empty($errors)) {
+ $output->writeln('<info>App is compliant - awesome job!</info>');
+ return 0;
+ } else {
+ $output->writeln('<error>App is not compliant</error>');
+ return 101;
+ }
+ }
+}
diff --git a/core/Command/App/Disable.php b/core/Command/App/Disable.php
new file mode 100644
index 00000000000..743a78cb88d
--- /dev/null
+++ b/core/Command/App/Disable.php
@@ -0,0 +1,71 @@
+<?php
+/**
+ * @author Joas Schilling <nickvergessen@owncloud.com>
+ * @author Morris Jobke <hey@morrisjobke.de>
+ * @author Robin Appelman <icewind@owncloud.com>
+ * @author Vincent Petry <pvince81@owncloud.com>
+ *
+ * @copyright Copyright (c) 2016, ownCloud, Inc.
+ * @license AGPL-3.0
+ *
+ * This code is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU Affero General Public License, version 3,
+ * as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU Affero General Public License for more details.
+ *
+ * You should have received a copy of the GNU Affero General Public License, version 3,
+ * along with this program. If not, see <http://www.gnu.org/licenses/>
+ *
+ */
+
+namespace OC\Core\Command\App;
+
+use OCP\App\IAppManager;
+use Symfony\Component\Console\Command\Command;
+use Symfony\Component\Console\Input\InputArgument;
+use Symfony\Component\Console\Input\InputInterface;
+use Symfony\Component\Console\Output\OutputInterface;
+
+class Disable extends Command {
+
+ /** @var IAppManager */
+ protected $manager;
+
+ /**
+ * @param IAppManager $manager
+ */
+ public function __construct(IAppManager $manager) {
+ parent::__construct();
+ $this->manager = $manager;
+ }
+
+ protected function configure() {
+ $this
+ ->setName('app:disable')
+ ->setDescription('disable an app')
+ ->addArgument(
+ 'app-id',
+ InputArgument::REQUIRED,
+ 'disable the specified app'
+ );
+ }
+
+ protected function execute(InputInterface $input, OutputInterface $output) {
+ $appId = $input->getArgument('app-id');
+ if ($this->manager->isInstalled($appId)) {
+ try {
+ $this->manager->disableApp($appId);
+ $output->writeln($appId . ' disabled');
+ } catch(\Exception $e) {
+ $output->writeln($e->getMessage());
+ return 2;
+ }
+ } else {
+ $output->writeln('No such app enabled: ' . $appId);
+ }
+ }
+}
diff --git a/core/Command/App/Enable.php b/core/Command/App/Enable.php
new file mode 100644
index 00000000000..0f6ce51fe8f
--- /dev/null
+++ b/core/Command/App/Enable.php
@@ -0,0 +1,83 @@
+<?php
+/**
+ * @author Joas Schilling <nickvergessen@owncloud.com>
+ * @author Morris Jobke <hey@morrisjobke.de>
+ * @author Robin Appelman <icewind@owncloud.com>
+ * @author Vincent Petry <pvince81@owncloud.com>
+ *
+ * @copyright Copyright (c) 2016, ownCloud, Inc.
+ * @license AGPL-3.0
+ *
+ * This code is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU Affero General Public License, version 3,
+ * as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU Affero General Public License for more details.
+ *
+ * You should have received a copy of the GNU Affero General Public License, version 3,
+ * along with this program. If not, see <http://www.gnu.org/licenses/>
+ *
+ */
+
+namespace OC\Core\Command\App;
+
+use OCP\App\IAppManager;
+use Symfony\Component\Console\Command\Command;
+use Symfony\Component\Console\Input\InputArgument;
+use Symfony\Component\Console\Input\InputInterface;
+use Symfony\Component\Console\Input\InputOption;
+use Symfony\Component\Console\Output\OutputInterface;
+
+class Enable extends Command {
+
+ /** @var IAppManager */
+ protected $manager;
+
+ /**
+ * @param IAppManager $manager
+ */
+ public function __construct(IAppManager $manager) {
+ parent::__construct();
+ $this->manager = $manager;
+ }
+
+ protected function configure() {
+ $this
+ ->setName('app:enable')
+ ->setDescription('enable an app')
+ ->addArgument(
+ 'app-id',
+ InputArgument::REQUIRED,
+ 'enable the specified app'
+ )
+ ->addOption(
+ 'groups',
+ 'g',
+ InputOption::VALUE_REQUIRED | InputOption::VALUE_IS_ARRAY,
+ 'enable the app only for a list of groups'
+ )
+ ;
+ }
+
+ protected function execute(InputInterface $input, OutputInterface $output) {
+ $appId = $input->getArgument('app-id');
+
+ if (!\OC_App::getAppPath($appId)) {
+ $output->writeln($appId . ' not found');
+ return 1;
+ }
+
+ $groups = $input->getOption('groups');
+ if (empty($groups)) {
+ \OC_App::enable($appId);
+ $output->writeln($appId . ' enabled');
+ } else {
+ \OC_App::enable($appId, $groups);
+ $output->writeln($appId . ' enabled for groups: ' . implode(', ', $groups));
+ }
+ return 0;
+ }
+}
diff --git a/core/Command/App/GetPath.php b/core/Command/App/GetPath.php
new file mode 100644
index 00000000000..33a3f64c53d
--- /dev/null
+++ b/core/Command/App/GetPath.php
@@ -0,0 +1,62 @@
+<?php
+/**
+ * @author Victor Dubiniuk <dubiniuk@owncloud.com>
+ *
+ * @copyright Copyright (c) 2016, ownCloud, Inc.
+ * @license AGPL-3.0
+ *
+ * This code is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU Affero General Public License, version 3,
+ * as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU Affero General Public License for more details.
+ *
+ * You should have received a copy of the GNU Affero General Public License, version 3,
+ * along with this program. If not, see <http://www.gnu.org/licenses/>
+ *
+ */
+
+namespace OC\Core\Command\App;
+
+use OC\Core\Command\Base;
+use Symfony\Component\Console\Input\InputArgument;
+use Symfony\Component\Console\Input\InputInterface;
+use Symfony\Component\Console\Output\OutputInterface;
+
+class GetPath extends Base {
+ protected function configure() {
+ parent::configure();
+
+ $this
+ ->setName('app:getpath')
+ ->setDescription('Get an absolute path to the app directory')
+ ->addArgument(
+ 'app',
+ InputArgument::REQUIRED,
+ 'Name of the app'
+ )
+ ;
+ }
+
+ /**
+ * Executes the current command.
+ *
+ * @param InputInterface $input An InputInterface instance
+ * @param OutputInterface $output An OutputInterface instance
+ * @return null|int null or 0 if everything went fine, or an error code
+ */
+ protected function execute(InputInterface $input, OutputInterface $output) {
+ $appName = $input->getArgument('app');
+ $path = \OC_App::getAppPath($appName);
+ if ($path !== false) {
+ $output->writeln($path);
+ return 0;
+ }
+
+ // App not found, exit with non-zero
+ return 1;
+ }
+}
diff --git a/core/Command/App/ListApps.php b/core/Command/App/ListApps.php
new file mode 100644
index 00000000000..d7546b3c0c7
--- /dev/null
+++ b/core/Command/App/ListApps.php
@@ -0,0 +1,119 @@
+<?php
+/**
+ * @author Joas Schilling <nickvergessen@owncloud.com>
+ * @author Morris Jobke <hey@morrisjobke.de>
+ * @author Robin Appelman <icewind@owncloud.com>
+ * @author Victor Dubiniuk <dubiniuk@owncloud.com>
+ *
+ * @copyright Copyright (c) 2016, ownCloud, Inc.
+ * @license AGPL-3.0
+ *
+ * This code is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU Affero General Public License, version 3,
+ * as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU Affero General Public License for more details.
+ *
+ * You should have received a copy of the GNU Affero General Public License, version 3,
+ * along with this program. If not, see <http://www.gnu.org/licenses/>
+ *
+ */
+
+namespace OC\Core\Command\App;
+
+use OC\Core\Command\Base;
+use OCP\App\IAppManager;
+use Symfony\Component\Console\Input\InputInterface;
+use Symfony\Component\Console\Input\InputOption;
+use Symfony\Component\Console\Output\OutputInterface;
+
+class ListApps extends Base {
+
+ /** @var IAppManager */
+ protected $manager;
+
+ /**
+ * @param IAppManager $manager
+ */
+ public function __construct(IAppManager $manager) {
+ parent::__construct();
+ $this->manager = $manager;
+ }
+
+ protected function configure() {
+ parent::configure();
+
+ $this
+ ->setName('app:list')
+ ->setDescription('List all available apps')
+ ->addOption(
+ 'shipped',
+ null,
+ InputOption::VALUE_REQUIRED,
+ 'true - limit to shipped apps only, false - limit to non-shipped apps only'
+ )
+ ;
+ }
+
+ protected function execute(InputInterface $input, OutputInterface $output) {
+ if ($input->getOption('shipped') === 'true' || $input->getOption('shipped') === 'false'){
+ $shippedFilter = $input->getOption('shipped') === 'true';
+ } else {
+ $shippedFilter = null;
+ }
+
+ $apps = \OC_App::getAllApps();
+ $enabledApps = $disabledApps = [];
+ $versions = \OC_App::getAppVersions();
+
+ //sort enabled apps above disabled apps
+ foreach ($apps as $app) {
+ if ($shippedFilter !== null && \OC_App::isShipped($app) !== $shippedFilter){
+ continue;
+ }
+ if ($this->manager->isInstalled($app)) {
+ $enabledApps[] = $app;
+ } else {
+ $disabledApps[] = $app;
+ }
+ }
+
+ $apps = ['enabled' => [], 'disabled' => []];
+
+ sort($enabledApps);
+ foreach ($enabledApps as $app) {
+ $apps['enabled'][$app] = (isset($versions[$app])) ? $versions[$app] : true;
+ }
+
+ sort($disabledApps);
+ foreach ($disabledApps as $app) {
+ $apps['disabled'][$app] = null;
+ }
+
+ $this->writeAppList($input, $output, $apps);
+ }
+
+ /**
+ * @param InputInterface $input
+ * @param OutputInterface $output
+ * @param array $items
+ */
+ protected function writeAppList(InputInterface $input, OutputInterface $output, $items) {
+ switch ($input->getOption('output')) {
+ case self::OUTPUT_FORMAT_PLAIN:
+ $output->writeln('Enabled:');
+ parent::writeArrayInOutputFormat($input, $output, $items['enabled']);
+
+ $output->writeln('Disabled:');
+ parent::writeArrayInOutputFormat($input, $output, $items['disabled']);
+ break;
+
+ default:
+ parent::writeArrayInOutputFormat($input, $output, $items);
+ break;
+ }
+ }
+}
diff --git a/core/Command/Background/Ajax.php b/core/Command/Background/Ajax.php
new file mode 100644
index 00000000000..e9cd1405ebd
--- /dev/null
+++ b/core/Command/Background/Ajax.php
@@ -0,0 +1,33 @@
+<?php
+/**
+* The MIT License (MIT)
+*
+* Copyright (c) 2015 Christian Kampka <christian@kampka.net>
+*
+* Permission is hereby granted, free of charge, to any person obtaining a copy
+* of this software and associated documentation files (the "Software"), to deal
+* in the Software without restriction, including without limitation the rights
+* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+* copies of the Software, and to permit persons to whom the Software is
+* furnished to do so, subject to the following conditions:
+*
+* The above copyright notice and this permission notice shall be included in
+* all copies or substantial portions of the Software.
+*
+* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+* THE SOFTWARE.
+*/
+
+namespace OC\Core\Command\Background;
+
+class Ajax extends Base {
+
+ protected function getMode() {
+ return 'ajax';
+ }
+}
diff --git a/core/Command/Background/Base.php b/core/Command/Background/Base.php
new file mode 100644
index 00000000000..48fee818d0a
--- /dev/null
+++ b/core/Command/Background/Base.php
@@ -0,0 +1,77 @@
+<?php
+/**
+* The MIT License (MIT)
+*
+* Copyright (c) 2015 Christian Kampka <christian@kampka.net>
+*
+* Permission is hereby granted, free of charge, to any person obtaining a copy
+* of this software and associated documentation files (the "Software"), to deal
+* in the Software without restriction, including without limitation the rights
+* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+* copies of the Software, and to permit persons to whom the Software is
+* furnished to do so, subject to the following conditions:
+*
+* The above copyright notice and this permission notice shall be included in
+* all copies or substantial portions of the Software.
+*
+* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+* THE SOFTWARE.
+*/
+
+namespace OC\Core\Command\Background;
+
+use \OCP\IConfig;
+
+use Symfony\Component\Console\Command\Command;
+use Symfony\Component\Console\Input\InputInterface;
+use Symfony\Component\Console\Output\OutputInterface;
+
+/**
+* An abstract base class for configuring the background job mode
+* from the command line interface.
+* Subclasses will override the getMode() function to specify the mode to configure.
+*/
+abstract class Base extends Command {
+
+
+ abstract protected function getMode();
+
+ /**
+ * @var \OCP\IConfig
+ */
+ protected $config;
+
+ /**
+ * @param \OCP\IConfig $config
+ */
+ public function __construct(IConfig $config) {
+ $this->config = $config;
+ parent::__construct();
+ }
+
+ protected function configure() {
+ $mode = $this->getMode();
+ $this
+ ->setName("background:$mode")
+ ->setDescription("Use $mode to run background jobs");
+ }
+
+ /**
+ * Executing this command will set the background job mode for owncloud.
+ * The mode to set is specified by the concrete sub class by implementing the
+ * getMode() function.
+ *
+ * @param InputInterface $input
+ * @param OutputInterface $output
+ */
+ protected function execute(InputInterface $input, OutputInterface $output) {
+ $mode = $this->getMode();
+ $this->config->setAppValue( 'core', 'backgroundjobs_mode', $mode );
+ $output->writeln("Set mode for background jobs to '$mode'");
+ }
+}
diff --git a/core/Command/Background/Cron.php b/core/Command/Background/Cron.php
new file mode 100644
index 00000000000..434e88893b2
--- /dev/null
+++ b/core/Command/Background/Cron.php
@@ -0,0 +1,33 @@
+<?php
+/**
+* The MIT License (MIT)
+*
+* Copyright (c) 2015 Christian Kampka <christian@kampka.net>
+*
+* Permission is hereby granted, free of charge, to any person obtaining a copy
+* of this software and associated documentation files (the "Software"), to deal
+* in the Software without restriction, including without limitation the rights
+* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+* copies of the Software, and to permit persons to whom the Software is
+* furnished to do so, subject to the following conditions:
+*
+* The above copyright notice and this permission notice shall be included in
+* all copies or substantial portions of the Software.
+*
+* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+* THE SOFTWARE.
+*/
+
+namespace OC\Core\Command\Background;
+
+class Cron extends Base {
+
+ protected function getMode() {
+ return 'cron';
+ }
+}
diff --git a/core/Command/Background/WebCron.php b/core/Command/Background/WebCron.php
new file mode 100644
index 00000000000..23dbe98e635
--- /dev/null
+++ b/core/Command/Background/WebCron.php
@@ -0,0 +1,33 @@
+<?php
+/**
+* The MIT License (MIT)
+*
+* Copyright (c) 2015 Christian Kampka <christian@kampka.net>
+*
+* Permission is hereby granted, free of charge, to any person obtaining a copy
+* of this software and associated documentation files (the "Software"), to deal
+* in the Software without restriction, including without limitation the rights
+* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+* copies of the Software, and to permit persons to whom the Software is
+* furnished to do so, subject to the following conditions:
+*
+* The above copyright notice and this permission notice shall be included in
+* all copies or substantial portions of the Software.
+*
+* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+* THE SOFTWARE.
+*/
+
+namespace OC\Core\Command\Background;
+
+class WebCron extends Base {
+
+ protected function getMode() {
+ return 'webcron';
+ }
+}
diff --git a/core/Command/Base.php b/core/Command/Base.php
new file mode 100644
index 00000000000..7538effd74a
--- /dev/null
+++ b/core/Command/Base.php
@@ -0,0 +1,160 @@
+<?php
+/**
+ * @author Joas Schilling <nickvergessen@owncloud.com>
+ * @author Thomas Müller <thomas.mueller@tmit.eu>
+ *
+ * @copyright Copyright (c) 2016, ownCloud, Inc.
+ * @license AGPL-3.0
+ *
+ * This code is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU Affero General Public License, version 3,
+ * as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU Affero General Public License for more details.
+ *
+ * You should have received a copy of the GNU Affero General Public License, version 3,
+ * along with this program. If not, see <http://www.gnu.org/licenses/>
+ *
+ */
+
+namespace OC\Core\Command;
+
+use Symfony\Component\Console\Command\Command;
+use Symfony\Component\Console\Input\InputInterface;
+use Symfony\Component\Console\Input\InputOption;
+use Symfony\Component\Console\Output\OutputInterface;
+
+class Base extends Command {
+ const OUTPUT_FORMAT_PLAIN = 'plain';
+ const OUTPUT_FORMAT_JSON = 'json';
+ const OUTPUT_FORMAT_JSON_PRETTY = 'json_pretty';
+
+ protected $defaultOutputFormat = self::OUTPUT_FORMAT_PLAIN;
+
+ /** @var boolean */
+ private $php_pcntl_signal = false;
+
+ /** @var boolean */
+ private $interrupted = false;
+
+ protected function configure() {
+ $this
+ ->addOption(
+ 'output',
+ null,
+ InputOption::VALUE_OPTIONAL,
+ 'Output format (plain, json or json_pretty, default is plain)',
+ $this->defaultOutputFormat
+ )
+ ;
+ }
+
+ /**
+ * @param InputInterface $input
+ * @param OutputInterface $output
+ * @param array $items
+ * @param string $prefix
+ */
+ protected function writeArrayInOutputFormat(InputInterface $input, OutputInterface $output, $items, $prefix = ' - ') {
+ switch ($input->getOption('output')) {
+ case self::OUTPUT_FORMAT_JSON:
+ $output->writeln(json_encode($items));
+ break;
+ case self::OUTPUT_FORMAT_JSON_PRETTY:
+ $output->writeln(json_encode($items, JSON_PRETTY_PRINT));
+ break;
+ default:
+ foreach ($items as $key => $item) {
+ if (is_array($item)) {
+ $output->writeln($prefix . $key . ':');
+ $this->writeArrayInOutputFormat($input, $output, $item, ' ' . $prefix);
+ continue;
+ }
+ if (!is_int($key)) {
+ $value = $this->valueToString($item);
+ if (!is_null($value)) {
+ $output->writeln($prefix . $key . ': ' . $value);
+ } else {
+ $output->writeln($prefix . $key);
+ }
+ } else {
+ $output->writeln($prefix . $this->valueToString($item));
+ }
+ }
+ break;
+ }
+ }
+
+ /**
+ * @param InputInterface $input
+ * @param OutputInterface $output
+ * @param mixed $item
+ */
+ protected function writeMixedInOutputFormat(InputInterface $input, OutputInterface $output, $item) {
+ if (is_array($item)) {
+ $this->writeArrayInOutputFormat($input, $output, $item, '');
+ return;
+ }
+
+ switch ($input->getOption('output')) {
+ case self::OUTPUT_FORMAT_JSON:
+ $output->writeln(json_encode($item));
+ break;
+ case self::OUTPUT_FORMAT_JSON_PRETTY:
+ $output->writeln(json_encode($item, JSON_PRETTY_PRINT));
+ break;
+ default:
+ $output->writeln($this->valueToString($item, false));
+ break;
+ }
+ }
+
+ protected function valueToString($value, $returnNull = true) {
+ if ($value === false) {
+ return 'false';
+ } else if ($value === true) {
+ return 'true';
+ } else if ($value === null) {
+ return ($returnNull) ? null : 'null';
+ } else {
+ return $value;
+ }
+ }
+
+ /**
+ * @return bool
+ */
+ protected function hasBeenInterrupted() {
+ // return always false if pcntl_signal functions are not accessible
+ if ($this->php_pcntl_signal) {
+ pcntl_signal_dispatch();
+ return $this->interrupted;
+ } else {
+ return false;
+ }
+ }
+
+ /**
+ * Changes the status of the command to "interrupted" if ctrl-c has been pressed
+ *
+ * Gives a chance to the command to properly terminate what it's doing
+ */
+ protected function cancelOperation() {
+ $this->interrupted = true;
+ }
+
+ public function run(InputInterface $input, OutputInterface $output) {
+ // check if the php pcntl_signal functions are accessible
+ $this->php_pcntl_signal = function_exists('pcntl_signal');
+ if ($this->php_pcntl_signal) {
+ // Collect interrupts and notify the running command
+ pcntl_signal(SIGTERM, [$this, 'cancelOperation']);
+ pcntl_signal(SIGINT, [$this, 'cancelOperation']);
+ }
+
+ return parent::run($input, $output);
+ }
+}
diff --git a/core/Command/Check.php b/core/Command/Check.php
new file mode 100644
index 00000000000..c2e92f7a8da
--- /dev/null
+++ b/core/Command/Check.php
@@ -0,0 +1,61 @@
+<?php
+/**
+ * @author Joas Schilling <nickvergessen@owncloud.com>
+ * @author Morris Jobke <hey@morrisjobke.de>
+ * @author Thomas Müller <thomas.mueller@tmit.eu>
+ *
+ * @copyright Copyright (c) 2016, ownCloud, Inc.
+ * @license AGPL-3.0
+ *
+ * This code is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU Affero General Public License, version 3,
+ * as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU Affero General Public License for more details.
+ *
+ * You should have received a copy of the GNU Affero General Public License, version 3,
+ * along with this program. If not, see <http://www.gnu.org/licenses/>
+ *
+ */
+namespace OC\Core\Command;
+
+use OCP\IConfig;
+use Symfony\Component\Console\Input\InputInterface;
+use Symfony\Component\Console\Output\OutputInterface;
+
+class Check extends Base {
+ /**
+ * @var IConfig
+ */
+ private $config;
+
+ public function __construct(IConfig $config) {
+ parent::__construct();
+ $this->config = $config;
+ }
+
+ protected function configure() {
+ parent::configure();
+
+ $this
+ ->setName('check')
+ ->setDescription('check dependencies of the server environment')
+ ;
+ }
+
+ protected function execute(InputInterface $input, OutputInterface $output) {
+ $errors = \OC_Util::checkServer($this->config);
+ if (!empty($errors)) {
+ $errors = array_map(function($item) {
+ return (string) $item['error'];
+ }, $errors);
+
+ $this->writeArrayInOutputFormat($input, $output, $errors);
+ return 1;
+ }
+ return 0;
+ }
+}
diff --git a/core/Command/Config/App/DeleteConfig.php b/core/Command/Config/App/DeleteConfig.php
new file mode 100644
index 00000000000..cccd92ea3d6
--- /dev/null
+++ b/core/Command/Config/App/DeleteConfig.php
@@ -0,0 +1,81 @@
+<?php
+/**
+ * @author Joas Schilling <nickvergessen@owncloud.com>
+ *
+ * @copyright Copyright (c) 2016, ownCloud, Inc.
+ * @license AGPL-3.0
+ *
+ * This code is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU Affero General Public License, version 3,
+ * as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU Affero General Public License for more details.
+ *
+ * You should have received a copy of the GNU Affero General Public License, version 3,
+ * along with this program. If not, see <http://www.gnu.org/licenses/>
+ *
+ */
+
+namespace OC\Core\Command\Config\App;
+
+use OC\Core\Command\Base;
+use OCP\IConfig;
+use Symfony\Component\Console\Input\InputArgument;
+use Symfony\Component\Console\Input\InputInterface;
+use Symfony\Component\Console\Input\InputOption;
+use Symfony\Component\Console\Output\OutputInterface;
+
+class DeleteConfig extends Base {
+ /** * @var IConfig */
+ protected $config;
+
+ /**
+ * @param IConfig $config
+ */
+ public function __construct(IConfig $config) {
+ parent::__construct();
+ $this->config = $config;
+ }
+
+ protected function configure() {
+ parent::configure();
+
+ $this
+ ->setName('config:app:delete')
+ ->setDescription('Delete an app config value')
+ ->addArgument(
+ 'app',
+ InputArgument::REQUIRED,
+ 'Name of the app'
+ )
+ ->addArgument(
+ 'name',
+ InputArgument::REQUIRED,
+ 'Name of the config to delete'
+ )
+ ->addOption(
+ 'error-if-not-exists',
+ null,
+ InputOption::VALUE_NONE,
+ 'Checks whether the config exists before deleting it'
+ )
+ ;
+ }
+
+ protected function execute(InputInterface $input, OutputInterface $output) {
+ $appName = $input->getArgument('app');
+ $configName = $input->getArgument('name');
+
+ if ($input->hasParameterOption('--error-if-not-exists') && !in_array($configName, $this->config->getAppKeys($appName))) {
+ $output->writeln('<error>Config ' . $configName . ' of app ' . $appName . ' could not be deleted because it did not exist</error>');
+ return 1;
+ }
+
+ $this->config->deleteAppValue($appName, $configName);
+ $output->writeln('<info>Config value ' . $configName . ' of app ' . $appName . ' deleted</info>');
+ return 0;
+ }
+}
diff --git a/core/Command/Config/App/GetConfig.php b/core/Command/Config/App/GetConfig.php
new file mode 100644
index 00000000000..abe71e57d8c
--- /dev/null
+++ b/core/Command/Config/App/GetConfig.php
@@ -0,0 +1,93 @@
+<?php
+/**
+ * @author Joas Schilling <nickvergessen@owncloud.com>
+ *
+ * @copyright Copyright (c) 2016, ownCloud, Inc.
+ * @license AGPL-3.0
+ *
+ * This code is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU Affero General Public License, version 3,
+ * as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU Affero General Public License for more details.
+ *
+ * You should have received a copy of the GNU Affero General Public License, version 3,
+ * along with this program. If not, see <http://www.gnu.org/licenses/>
+ *
+ */
+
+namespace OC\Core\Command\Config\App;
+
+use OC\Core\Command\Base;
+use OCP\IConfig;
+use Symfony\Component\Console\Input\InputArgument;
+use Symfony\Component\Console\Input\InputInterface;
+use Symfony\Component\Console\Input\InputOption;
+use Symfony\Component\Console\Output\OutputInterface;
+
+class GetConfig extends Base {
+ /** * @var IConfig */
+ protected $config;
+
+ /**
+ * @param IConfig $config
+ */
+ public function __construct(IConfig $config) {
+ parent::__construct();
+ $this->config = $config;
+ }
+
+ protected function configure() {
+ parent::configure();
+
+ $this
+ ->setName('config:app:get')
+ ->setDescription('Get an app config value')
+ ->addArgument(
+ 'app',
+ InputArgument::REQUIRED,
+ 'Name of the app'
+ )
+ ->addArgument(
+ 'name',
+ InputArgument::REQUIRED,
+ 'Name of the config to get'
+ )
+ ->addOption(
+ 'default-value',
+ null,
+ InputOption::VALUE_OPTIONAL,
+ 'If no default value is set and the config does not exist, the command will exit with 1'
+ )
+ ;
+ }
+
+ /**
+ * Executes the current command.
+ *
+ * @param InputInterface $input An InputInterface instance
+ * @param OutputInterface $output An OutputInterface instance
+ * @return null|int null or 0 if everything went fine, or an error code
+ */
+ protected function execute(InputInterface $input, OutputInterface $output) {
+ $appName = $input->getArgument('app');
+ $configName = $input->getArgument('name');
+ $defaultValue = $input->getOption('default-value');
+
+ if (!in_array($configName, $this->config->getAppKeys($appName)) && !$input->hasParameterOption('--default-value')) {
+ return 1;
+ }
+
+ if (!in_array($configName, $this->config->getAppKeys($appName))) {
+ $configValue = $defaultValue;
+ } else {
+ $configValue = $this->config->getAppValue($appName, $configName);
+ }
+
+ $this->writeMixedInOutputFormat($input, $output, $configValue);
+ return 0;
+ }
+}
diff --git a/core/Command/Config/App/SetConfig.php b/core/Command/Config/App/SetConfig.php
new file mode 100644
index 00000000000..097fde6ba95
--- /dev/null
+++ b/core/Command/Config/App/SetConfig.php
@@ -0,0 +1,89 @@
+<?php
+/**
+ * @author Joas Schilling <nickvergessen@owncloud.com>
+ *
+ * @copyright Copyright (c) 2016, ownCloud, Inc.
+ * @license AGPL-3.0
+ *
+ * This code is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU Affero General Public License, version 3,
+ * as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU Affero General Public License for more details.
+ *
+ * You should have received a copy of the GNU Affero General Public License, version 3,
+ * along with this program. If not, see <http://www.gnu.org/licenses/>
+ *
+ */
+
+namespace OC\Core\Command\Config\App;
+
+use OC\Core\Command\Base;
+use OCP\IConfig;
+use Symfony\Component\Console\Input\InputArgument;
+use Symfony\Component\Console\Input\InputInterface;
+use Symfony\Component\Console\Input\InputOption;
+use Symfony\Component\Console\Output\OutputInterface;
+
+class SetConfig extends Base {
+ /** * @var IConfig */
+ protected $config;
+
+ /**
+ * @param IConfig $config
+ */
+ public function __construct(IConfig $config) {
+ parent::__construct();
+ $this->config = $config;
+ }
+
+ protected function configure() {
+ parent::configure();
+
+ $this
+ ->setName('config:app:set')
+ ->setDescription('Set an app config value')
+ ->addArgument(
+ 'app',
+ InputArgument::REQUIRED,
+ 'Name of the app'
+ )
+ ->addArgument(
+ 'name',
+ InputArgument::REQUIRED,
+ 'Name of the config to set'
+ )
+ ->addOption(
+ 'value',
+ null,
+ InputOption::VALUE_REQUIRED,
+ 'The new value of the config'
+ )
+ ->addOption(
+ 'update-only',
+ null,
+ InputOption::VALUE_NONE,
+ 'Only updates the value, if it is not set before, it is not being added'
+ )
+ ;
+ }
+
+ protected function execute(InputInterface $input, OutputInterface $output) {
+ $appName = $input->getArgument('app');
+ $configName = $input->getArgument('name');
+
+ if (!in_array($configName, $this->config->getAppKeys($appName)) && $input->hasParameterOption('--update-only')) {
+ $output->writeln('<comment>Config value ' . $configName . ' for app ' . $appName . ' not updated, as it has not been set before.</comment>');
+ return 1;
+ }
+
+ $configValue = $input->getOption('value');
+ $this->config->setAppValue($appName, $configName, $configValue);
+
+ $output->writeln('<info>Config value ' . $configName . ' for app ' . $appName . ' set to ' . $configValue . '</info>');
+ return 0;
+ }
+}
diff --git a/core/Command/Config/Import.php b/core/Command/Config/Import.php
new file mode 100644
index 00000000000..7f1e09d2c95
--- /dev/null
+++ b/core/Command/Config/Import.php
@@ -0,0 +1,195 @@
+<?php
+/**
+ * @author Joas Schilling <nickvergessen@owncloud.com>
+ *
+ * @copyright Copyright (c) 2016, ownCloud, Inc.
+ * @license AGPL-3.0
+ *
+ * This code is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU Affero General Public License, version 3,
+ * as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU Affero General Public License for more details.
+ *
+ * You should have received a copy of the GNU Affero General Public License, version 3,
+ * along with this program. If not, see <http://www.gnu.org/licenses/>
+ *
+ */
+
+namespace OC\Core\Command\Config;
+
+use OCP\IConfig;
+use Symfony\Component\Console\Command\Command;
+use Symfony\Component\Console\Input\InputArgument;
+use Symfony\Component\Console\Input\InputInterface;
+use Symfony\Component\Console\Output\OutputInterface;
+
+class Import extends Command {
+ protected $validRootKeys = ['system', 'apps'];
+
+ /** @var IConfig */
+ protected $config;
+
+ /**
+ * @param IConfig $config
+ */
+ public function __construct(IConfig $config) {
+ parent::__construct();
+ $this->config = $config;
+ }
+
+ protected function configure() {
+ $this
+ ->setName('config:import')
+ ->setDescription('Import a list of configs')
+ ->addArgument(
+ 'file',
+ InputArgument::OPTIONAL,
+ 'File with the json array to import'
+ )
+ ;
+ }
+
+ protected function execute(InputInterface $input, OutputInterface $output) {
+ $importFile = $input->getArgument('file');
+ if ($importFile !== null) {
+ $content = $this->getArrayFromFile($importFile);
+ } else {
+ $content = $this->getArrayFromStdin();
+ }
+
+ try {
+ $configs = $this->validateFileContent($content);
+ } catch (\UnexpectedValueException $e) {
+ $output->writeln('<error>' . $e->getMessage(). '</error>');
+ return;
+ }
+
+ if (!empty($configs['system'])) {
+ $this->config->setSystemValues($configs['system']);
+ }
+
+ if (!empty($configs['apps'])) {
+ foreach ($configs['apps'] as $app => $appConfigs) {
+ foreach ($appConfigs as $key => $value) {
+ if ($value === null) {
+ $this->config->deleteAppValue($app, $key);
+ } else {
+ $this->config->setAppValue($app, $key, $value);
+ }
+ }
+ }
+ }
+
+ $output->writeln('<info>Config successfully imported from: ' . $importFile . '</info>');
+ }
+
+ /**
+ * Get the content from stdin ("config:import < file.json")
+ *
+ * @return string
+ */
+ protected function getArrayFromStdin() {
+ // Read from stdin. stream_set_blocking is used to prevent blocking
+ // when nothing is passed via stdin.
+ stream_set_blocking(STDIN, 0);
+ $content = file_get_contents('php://stdin');
+ stream_set_blocking(STDIN, 1);
+ return $content;
+ }
+
+ /**
+ * Get the content of the specified file ("config:import file.json")
+ *
+ * @param string $importFile
+ * @return string
+ */
+ protected function getArrayFromFile($importFile) {
+ $content = file_get_contents($importFile);
+ return $content;
+ }
+
+ /**
+ * @param string $content
+ * @return array
+ * @throws \UnexpectedValueException when the array is invalid
+ */
+ protected function validateFileContent($content) {
+ $decodedContent = json_decode($content, true);
+ if (!is_array($decodedContent) || empty($decodedContent)) {
+ throw new \UnexpectedValueException('The file must contain a valid json array');
+ }
+
+ $this->validateArray($decodedContent);
+
+ return $decodedContent;
+ }
+
+ /**
+ * Validates that the array only contains `system` and `apps`
+ *
+ * @param array $array
+ */
+ protected function validateArray($array) {
+ $arrayKeys = array_keys($array);
+ $additionalKeys = array_diff($arrayKeys, $this->validRootKeys);
+ $commonKeys = array_intersect($arrayKeys, $this->validRootKeys);
+ if (!empty($additionalKeys)) {
+ throw new \UnexpectedValueException('Found invalid entries in root: ' . implode(', ', $additionalKeys));
+ }
+ if (empty($commonKeys)) {
+ throw new \UnexpectedValueException('At least one key of the following is expected: ' . implode(', ', $this->validRootKeys));
+ }
+
+ if (isset($array['system'])) {
+ if (is_array($array['system'])) {
+ foreach ($array['system'] as $name => $value) {
+ $this->checkTypeRecursively($value, $name);
+ }
+ } else {
+ throw new \UnexpectedValueException('The system config array is not an array');
+ }
+ }
+
+ if (isset($array['apps'])) {
+ if (is_array($array['apps'])) {
+ $this->validateAppsArray($array['apps']);
+ } else {
+ throw new \UnexpectedValueException('The apps config array is not an array');
+ }
+ }
+ }
+
+ /**
+ * @param mixed $configValue
+ * @param string $configName
+ */
+ protected function checkTypeRecursively($configValue, $configName) {
+ if (!is_array($configValue) && !is_bool($configValue) && !is_int($configValue) && !is_string($configValue) && !is_null($configValue)) {
+ throw new \UnexpectedValueException('Invalid system config value for "' . $configName . '". Only arrays, bools, integers, strings and null (delete) are allowed.');
+ }
+ if (is_array($configValue)) {
+ foreach ($configValue as $key => $value) {
+ $this->checkTypeRecursively($value, $configName);
+ }
+ }
+ }
+
+ /**
+ * Validates that app configs are only integers and strings
+ *
+ * @param array $array
+ */
+ protected function validateAppsArray($array) {
+ foreach ($array as $app => $configs) {
+ foreach ($configs as $name => $value) {
+ if (!is_int($value) && !is_string($value) && !is_null($value)) {
+ throw new \UnexpectedValueException('Invalid app config value for "' . $app . '":"' . $name . '". Only integers, strings and null (delete) are allowed.');
+ }
+ }
+ }
+ }
+}
diff --git a/core/Command/Config/ListConfigs.php b/core/Command/Config/ListConfigs.php
new file mode 100644
index 00000000000..afebe4c4c07
--- /dev/null
+++ b/core/Command/Config/ListConfigs.php
@@ -0,0 +1,129 @@
+<?php
+/**
+ * @author Joas Schilling <nickvergessen@owncloud.com>
+ *
+ * @copyright Copyright (c) 2016, ownCloud, Inc.
+ * @license AGPL-3.0
+ *
+ * This code is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU Affero General Public License, version 3,
+ * as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU Affero General Public License for more details.
+ *
+ * You should have received a copy of the GNU Affero General Public License, version 3,
+ * along with this program. If not, see <http://www.gnu.org/licenses/>
+ *
+ */
+
+namespace OC\Core\Command\Config;
+
+use OC\Core\Command\Base;
+use OC\SystemConfig;
+use OCP\IAppConfig;
+use Symfony\Component\Console\Input\InputArgument;
+use Symfony\Component\Console\Input\InputInterface;
+use Symfony\Component\Console\Input\InputOption;
+use Symfony\Component\Console\Output\OutputInterface;
+
+class ListConfigs extends Base {
+ protected $defaultOutputFormat = self::OUTPUT_FORMAT_JSON_PRETTY;
+
+ /** * @var SystemConfig */
+ protected $systemConfig;
+
+ /** @var IAppConfig */
+ protected $appConfig;
+
+ /**
+ * @param SystemConfig $systemConfig
+ * @param IAppConfig $appConfig
+ */
+ public function __construct(SystemConfig $systemConfig, IAppConfig $appConfig) {
+ parent::__construct();
+ $this->systemConfig = $systemConfig;
+ $this->appConfig = $appConfig;
+ }
+
+ protected function configure() {
+ parent::configure();
+
+ $this
+ ->setName('config:list')
+ ->setDescription('List all configs')
+ ->addArgument(
+ 'app',
+ InputArgument::OPTIONAL,
+ 'Name of the app ("system" to get the config.php values, "all" for all apps and system)',
+ 'all'
+ )
+ ->addOption(
+ 'private',
+ null,
+ InputOption::VALUE_NONE,
+ 'Use this option when you want to include sensitive configs like passwords, salts, ...'
+ )
+ ;
+ }
+
+ protected function execute(InputInterface $input, OutputInterface $output) {
+ $app = $input->getArgument('app');
+ $noSensitiveValues = !$input->getOption('private');
+
+ switch ($app) {
+ case 'system':
+ $configs = [
+ 'system' => $this->getSystemConfigs($noSensitiveValues),
+ ];
+ break;
+
+ case 'all':
+ $apps = $this->appConfig->getApps();
+ $configs = [
+ 'system' => $this->getSystemConfigs($noSensitiveValues),
+ 'apps' => [],
+ ];
+ foreach ($apps as $appName) {
+ $configs['apps'][$appName] = $this->appConfig->getValues($appName, false);
+ }
+ break;
+
+ default:
+ $configs = [
+ 'apps' => [
+ $app => $this->appConfig->getValues($app, false),
+ ],
+ ];
+ }
+
+ $this->writeArrayInOutputFormat($input, $output, $configs);
+ }
+
+ /**
+ * Get the system configs
+ *
+ * @param bool $noSensitiveValues
+ * @return array
+ */
+ protected function getSystemConfigs($noSensitiveValues) {
+ $keys = $this->systemConfig->getKeys();
+
+ $configs = [];
+ foreach ($keys as $key) {
+ if ($noSensitiveValues) {
+ $value = $this->systemConfig->getFilteredValue($key, serialize(null));
+ } else {
+ $value = $this->systemConfig->getValue($key, serialize(null));
+ }
+
+ if ($value !== 'N;') {
+ $configs[$key] = $value;
+ }
+ }
+
+ return $configs;
+ }
+}
diff --git a/core/Command/Config/System/DeleteConfig.php b/core/Command/Config/System/DeleteConfig.php
new file mode 100644
index 00000000000..374f5ac69b7
--- /dev/null
+++ b/core/Command/Config/System/DeleteConfig.php
@@ -0,0 +1,117 @@
+<?php
+/**
+ * @author Joas Schilling <nickvergessen@owncloud.com>
+ *
+ * @copyright Copyright (c) 2016, ownCloud, Inc.
+ * @license AGPL-3.0
+ *
+ * This code is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU Affero General Public License, version 3,
+ * as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU Affero General Public License for more details.
+ *
+ * You should have received a copy of the GNU Affero General Public License, version 3,
+ * along with this program. If not, see <http://www.gnu.org/licenses/>
+ *
+ */
+
+namespace OC\Core\Command\Config\System;
+
+use OC\Core\Command\Base;
+use OC\SystemConfig;
+use Symfony\Component\Console\Input\InputArgument;
+use Symfony\Component\Console\Input\InputInterface;
+use Symfony\Component\Console\Input\InputOption;
+use Symfony\Component\Console\Output\OutputInterface;
+
+class DeleteConfig extends Base {
+ /** * @var SystemConfig */
+ protected $systemConfig;
+
+ /**
+ * @param SystemConfig $systemConfig
+ */
+ public function __construct(SystemConfig $systemConfig) {
+ parent::__construct();
+ $this->systemConfig = $systemConfig;
+ }
+
+ protected function configure() {
+ parent::configure();
+
+ $this
+ ->setName('config:system:delete')
+ ->setDescription('Delete a system config value')
+ ->addArgument(
+ 'name',
+ InputArgument::REQUIRED | InputArgument::IS_ARRAY,
+ 'Name of the config to delete, specify multiple for array parameter'
+ )
+ ->addOption(
+ 'error-if-not-exists',
+ null,
+ InputOption::VALUE_NONE,
+ 'Checks whether the config exists before deleting it'
+ )
+ ;
+ }
+
+ protected function execute(InputInterface $input, OutputInterface $output) {
+ $configNames = $input->getArgument('name');
+ $configName = $configNames[0];
+
+ if (sizeof($configNames) > 1) {
+ if ($input->hasParameterOption('--error-if-not-exists') && !in_array($configName, $this->systemConfig->getKeys())) {
+ $output->writeln('<error>System config ' . implode(' => ', $configNames) . ' could not be deleted because it did not exist</error>');
+ return 1;
+ }
+
+ $value = $this->systemConfig->getValue($configName);
+
+ try {
+ $value = $this->removeSubValue(array_slice($configNames, 1), $value, $input->hasParameterOption('--error-if-not-exists'));
+ }
+ catch (\UnexpectedValueException $e) {
+ $output->writeln('<error>System config ' . implode(' => ', $configNames) . ' could not be deleted because it did not exist</error>');
+ return 1;
+ }
+
+ $this->systemConfig->setValue($configName, $value);
+ $output->writeln('<info>System config value ' . implode(' => ', $configNames) . ' deleted</info>');
+ return 0;
+ } else {
+ if ($input->hasParameterOption('--error-if-not-exists') && !in_array($configName, $this->systemConfig->getKeys())) {
+ $output->writeln('<error>System config ' . $configName . ' could not be deleted because it did not exist</error>');
+ return 1;
+ }
+
+ $this->systemConfig->deleteValue($configName);
+ $output->writeln('<info>System config value ' . $configName . ' deleted</info>');
+ return 0;
+ }
+ }
+
+ protected function removeSubValue($keys, $currentValue, $throwError) {
+ $nextKey = array_shift($keys);
+
+ if (is_array($currentValue)) {
+ if (isset($currentValue[$nextKey])) {
+ if (empty($keys)) {
+ unset($currentValue[$nextKey]);
+ } else {
+ $currentValue[$nextKey] = $this->removeSubValue($keys, $currentValue[$nextKey], $throwError);
+ }
+ } else if ($throwError) {
+ throw new \UnexpectedValueException('Config parameter does not exist');
+ }
+ } else if ($throwError) {
+ throw new \UnexpectedValueException('Config parameter does not exist');
+ }
+
+ return $currentValue;
+ }
+}
diff --git a/core/Command/Config/System/GetConfig.php b/core/Command/Config/System/GetConfig.php
new file mode 100644
index 00000000000..b76474112a0
--- /dev/null
+++ b/core/Command/Config/System/GetConfig.php
@@ -0,0 +1,100 @@
+<?php
+/**
+ * @author Joas Schilling <nickvergessen@owncloud.com>
+ *
+ * @copyright Copyright (c) 2016, ownCloud, Inc.
+ * @license AGPL-3.0
+ *
+ * This code is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU Affero General Public License, version 3,
+ * as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU Affero General Public License for more details.
+ *
+ * You should have received a copy of the GNU Affero General Public License, version 3,
+ * along with this program. If not, see <http://www.gnu.org/licenses/>
+ *
+ */
+
+namespace OC\Core\Command\Config\System;
+
+use OC\Core\Command\Base;
+use OC\SystemConfig;
+use Symfony\Component\Console\Input\InputArgument;
+use Symfony\Component\Console\Input\InputInterface;
+use Symfony\Component\Console\Input\InputOption;
+use Symfony\Component\Console\Output\OutputInterface;
+
+class GetConfig extends Base {
+ /** * @var SystemConfig */
+ protected $systemConfig;
+
+ /**
+ * @param SystemConfig $systemConfig
+ */
+ public function __construct(SystemConfig $systemConfig) {
+ parent::__construct();
+ $this->systemConfig = $systemConfig;
+ }
+
+ protected function configure() {
+ parent::configure();
+
+ $this
+ ->setName('config:system:get')
+ ->setDescription('Get a system config value')
+ ->addArgument(
+ 'name',
+ InputArgument::REQUIRED | InputArgument::IS_ARRAY,
+ 'Name of the config to get, specify multiple for array parameter'
+ )
+ ->addOption(
+ 'default-value',
+ null,
+ InputOption::VALUE_OPTIONAL,
+ 'If no default value is set and the config does not exist, the command will exit with 1'
+ )
+ ;
+ }
+
+ /**
+ * Executes the current command.
+ *
+ * @param InputInterface $input An InputInterface instance
+ * @param OutputInterface $output An OutputInterface instance
+ * @return null|int null or 0 if everything went fine, or an error code
+ */
+ protected function execute(InputInterface $input, OutputInterface $output) {
+ $configNames = $input->getArgument('name');
+ $configName = array_shift($configNames);
+ $defaultValue = $input->getOption('default-value');
+
+ if (!in_array($configName, $this->systemConfig->getKeys()) && !$input->hasParameterOption('--default-value')) {
+ return 1;
+ }
+
+ if (!in_array($configName, $this->systemConfig->getKeys())) {
+ $configValue = $defaultValue;
+ } else {
+ $configValue = $this->systemConfig->getValue($configName);
+ if (!empty($configNames)) {
+ foreach ($configNames as $configName) {
+ if (isset($configValue[$configName])) {
+ $configValue = $configValue[$configName];
+ } else if (!$input->hasParameterOption('--default-value')) {
+ return 1;
+ } else {
+ $configValue = $defaultValue;
+ break;
+ }
+ }
+ }
+ }
+
+ $this->writeMixedInOutputFormat($input, $output, $configValue);
+ return 0;
+ }
+}
diff --git a/core/Command/Config/System/SetConfig.php b/core/Command/Config/System/SetConfig.php
new file mode 100644
index 00000000000..c7f206b05d1
--- /dev/null
+++ b/core/Command/Config/System/SetConfig.php
@@ -0,0 +1,198 @@
+<?php
+/**
+ * @author Joas Schilling <nickvergessen@owncloud.com>
+ * @author Robin McCorkell <robin@mccorkell.me.uk>
+ *
+ * @copyright Copyright (c) 2016, ownCloud, Inc.
+ * @license AGPL-3.0
+ *
+ * This code is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU Affero General Public License, version 3,
+ * as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU Affero General Public License for more details.
+ *
+ * You should have received a copy of the GNU Affero General Public License, version 3,
+ * along with this program. If not, see <http://www.gnu.org/licenses/>
+ *
+ */
+
+namespace OC\Core\Command\Config\System;
+
+use OC\Core\Command\Base;
+use OC\SystemConfig;
+use Symfony\Component\Console\Input\InputArgument;
+use Symfony\Component\Console\Input\InputInterface;
+use Symfony\Component\Console\Input\InputOption;
+use Symfony\Component\Console\Output\OutputInterface;
+
+class SetConfig extends Base {
+ /** * @var SystemConfig */
+ protected $systemConfig;
+
+ /**
+ * @param SystemConfig $systemConfig
+ */
+ public function __construct(SystemConfig $systemConfig) {
+ parent::__construct();
+ $this->systemConfig = $systemConfig;
+ }
+
+ protected function configure() {
+ parent::configure();
+
+ $this
+ ->setName('config:system:set')
+ ->setDescription('Set a system config value')
+ ->addArgument(
+ 'name',
+ InputArgument::REQUIRED | InputArgument::IS_ARRAY,
+ 'Name of the config parameter, specify multiple for array parameter'
+ )
+ ->addOption(
+ 'type',
+ null,
+ InputOption::VALUE_REQUIRED,
+ 'Value type [string, integer, double, boolean]',
+ 'string'
+ )
+ ->addOption(
+ 'value',
+ null,
+ InputOption::VALUE_REQUIRED,
+ 'The new value of the config'
+ )
+ ->addOption(
+ 'update-only',
+ null,
+ InputOption::VALUE_NONE,
+ 'Only updates the value, if it is not set before, it is not being added'
+ )
+ ;
+ }
+
+ protected function execute(InputInterface $input, OutputInterface $output) {
+ $configNames = $input->getArgument('name');
+ $configName = $configNames[0];
+ $configValue = $this->castValue($input->getOption('value'), $input->getOption('type'));
+ $updateOnly = $input->getOption('update-only');
+
+ if (sizeof($configNames) > 1) {
+ $existingValue = $this->systemConfig->getValue($configName);
+
+ $newValue = $this->mergeArrayValue(
+ array_slice($configNames, 1), $existingValue, $configValue['value'], $updateOnly
+ );
+
+ $this->systemConfig->setValue($configName, $newValue);
+ } else {
+ if ($updateOnly && !in_array($configName, $this->systemConfig->getKeys(), true)) {
+ throw new \UnexpectedValueException('Config parameter does not exist');
+ }
+
+ $this->systemConfig->setValue($configName, $configValue['value']);
+ }
+
+ $output->writeln('<info>System config value ' . implode(' => ', $configNames) . ' set to ' . $configValue['readable-value'] . '</info>');
+ return 0;
+ }
+
+ /**
+ * @param string $value
+ * @param string $type
+ * @return mixed
+ * @throws \InvalidArgumentException
+ */
+ protected function castValue($value, $type) {
+ switch ($type) {
+ case 'integer':
+ case 'int':
+ if (!is_numeric($value)) {
+ throw new \InvalidArgumentException('Non-numeric value specified');
+ }
+ return [
+ 'value' => (int) $value,
+ 'readable-value' => 'integer ' . (int) $value,
+ ];
+
+ case 'double':
+ case 'float':
+ if (!is_numeric($value)) {
+ throw new \InvalidArgumentException('Non-numeric value specified');
+ }
+ return [
+ 'value' => (double) $value,
+ 'readable-value' => 'double ' . (double) $value,
+ ];
+
+ case 'boolean':
+ case 'bool':
+ $value = strtolower($value);
+ switch ($value) {
+ case 'true':
+ return [
+ 'value' => true,
+ 'readable-value' => 'boolean ' . $value,
+ ];
+
+ case 'false':
+ return [
+ 'value' => false,
+ 'readable-value' => 'boolean ' . $value,
+ ];
+
+ default:
+ throw new \InvalidArgumentException('Unable to parse value as boolean');
+ }
+
+ case 'null':
+ return [
+ 'value' => null,
+ 'readable-value' => 'null',
+ ];
+
+ case 'string':
+ $value = (string) $value;
+ return [
+ 'value' => $value,
+ 'readable-value' => ($value === '') ? 'empty string' : 'string ' . $value,
+ ];
+
+ default:
+ throw new \InvalidArgumentException('Invalid type');
+ }
+ }
+
+ /**
+ * @param array $configNames
+ * @param mixed $existingValues
+ * @param mixed $value
+ * @param bool $updateOnly
+ * @return array merged value
+ * @throws \UnexpectedValueException
+ */
+ protected function mergeArrayValue(array $configNames, $existingValues, $value, $updateOnly) {
+ $configName = array_shift($configNames);
+ if (!is_array($existingValues)) {
+ $existingValues = [];
+ }
+ if (!empty($configNames)) {
+ if (isset($existingValues[$configName])) {
+ $existingValue = $existingValues[$configName];
+ } else {
+ $existingValue = [];
+ }
+ $existingValues[$configName] = $this->mergeArrayValue($configNames, $existingValue, $value, $updateOnly);
+ } else {
+ if (!isset($existingValues[$configName]) && $updateOnly) {
+ throw new \UnexpectedValueException('Config parameter does not exist');
+ }
+ $existingValues[$configName] = $value;
+ }
+ return $existingValues;
+ }
+
+}
diff --git a/core/Command/Db/ConvertType.php b/core/Command/Db/ConvertType.php
new file mode 100644
index 00000000000..864499dcce0
--- /dev/null
+++ b/core/Command/Db/ConvertType.php
@@ -0,0 +1,311 @@
+<?php
+/**
+ * @author Andreas Fischer <bantu@owncloud.com>
+ * @author Bart Visscher <bartv@thisnet.nl>
+ * @author Joas Schilling <nickvergessen@owncloud.com>
+ * @author Morris Jobke <hey@morrisjobke.de>
+ * @author tbelau666 <thomas.belau@gmx.de>
+ * @author Thomas Müller <thomas.mueller@tmit.eu>
+ * @author unclejamal3000 <andreas.pramhaas@posteo.de>
+ *
+ * @copyright Copyright (c) 2016, ownCloud, Inc.
+ * @license AGPL-3.0
+ *
+ * This code is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU Affero General Public License, version 3,
+ * as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU Affero General Public License for more details.
+ *
+ * You should have received a copy of the GNU Affero General Public License, version 3,
+ * along with this program. If not, see <http://www.gnu.org/licenses/>
+ *
+ */
+
+namespace OC\Core\Command\Db;
+
+use \OCP\IConfig;
+use OC\DB\Connection;
+use OC\DB\ConnectionFactory;
+use Symfony\Component\Console\Command\Command;
+use Symfony\Component\Console\Input\InputArgument;
+use Symfony\Component\Console\Input\InputInterface;
+use Symfony\Component\Console\Input\InputOption;
+use Symfony\Component\Console\Output\OutputInterface;
+
+class ConvertType extends Command {
+ /**
+ * @var \OCP\IConfig
+ */
+ protected $config;
+
+ /**
+ * @var \OC\DB\ConnectionFactory
+ */
+ protected $connectionFactory;
+
+ /**
+ * @param \OCP\IConfig $config
+ * @param \OC\DB\ConnectionFactory $connectionFactory
+ */
+ public function __construct(IConfig $config, ConnectionFactory $connectionFactory) {
+ $this->config = $config;
+ $this->connectionFactory = $connectionFactory;
+ parent::__construct();
+ }
+
+ protected function configure() {
+ $this
+ ->setName('db:convert-type')
+ ->setDescription('Convert the ownCloud database to the newly configured one')
+ ->addArgument(
+ 'type',
+ InputArgument::REQUIRED,
+ 'the type of the database to convert to'
+ )
+ ->addArgument(
+ 'username',
+ InputArgument::REQUIRED,
+ 'the username of the database to convert to'
+ )
+ ->addArgument(
+ 'hostname',
+ InputArgument::REQUIRED,
+ 'the hostname of the database to convert to'
+ )
+ ->addArgument(
+ 'database',
+ InputArgument::REQUIRED,
+ 'the name of the database to convert to'
+ )
+ ->addOption(
+ 'port',
+ null,
+ InputOption::VALUE_REQUIRED,
+ 'the port of the database to convert to'
+ )
+ ->addOption(
+ 'password',
+ null,
+ InputOption::VALUE_REQUIRED,
+ 'the password of the database to convert to. Will be asked when not specified. Can also be passed via stdin.'
+ )
+ ->addOption(
+ 'clear-schema',
+ null,
+ InputOption::VALUE_NONE,
+ 'remove all tables from the destination database'
+ )
+ ->addOption(
+ 'all-apps',
+ null,
+ InputOption::VALUE_NONE,
+ 'whether to create schema for all apps instead of only installed apps'
+ )
+ ;
+ }
+
+ protected function validateInput(InputInterface $input, OutputInterface $output) {
+ $type = $this->connectionFactory->normalizeType($input->getArgument('type'));
+ if ($type === 'sqlite3') {
+ throw new \InvalidArgumentException(
+ 'Converting to SQLite (sqlite3) is currently not supported.'
+ );
+ }
+ if ($type === $this->config->getSystemValue('dbtype', '')) {
+ throw new \InvalidArgumentException(sprintf(
+ 'Can not convert from %1$s to %1$s.',
+ $type
+ ));
+ }
+ if ($type === 'oci' && $input->getOption('clear-schema')) {
+ // Doctrine unconditionally tries (at least in version 2.3)
+ // to drop sequence triggers when dropping a table, even though
+ // such triggers may not exist. This results in errors like
+ // "ORA-04080: trigger 'OC_STORAGES_AI_PK' does not exist".
+ throw new \InvalidArgumentException(
+ 'The --clear-schema option is not supported when converting to Oracle (oci).'
+ );
+ }
+ }
+
+ protected function readPassword(InputInterface $input, OutputInterface $output) {
+ // Explicitly specified password
+ if ($input->getOption('password')) {
+ return;
+ }
+
+ // Read from stdin. stream_set_blocking is used to prevent blocking
+ // when nothing is passed via stdin.
+ stream_set_blocking(STDIN, 0);
+ $password = file_get_contents('php://stdin');
+ stream_set_blocking(STDIN, 1);
+ if (trim($password) !== '') {
+ $input->setOption('password', $password);
+ return;
+ }
+
+ // Read password by interacting
+ if ($input->isInteractive()) {
+ /** @var $dialog \Symfony\Component\Console\Helper\DialogHelper */
+ $dialog = $this->getHelperSet()->get('dialog');
+ $password = $dialog->askHiddenResponse(
+ $output,
+ '<question>What is the database password?</question>',
+ false
+ );
+ $input->setOption('password', $password);
+ return;
+ }
+ }
+
+ protected function execute(InputInterface $input, OutputInterface $output) {
+ $this->validateInput($input, $output);
+ $this->readPassword($input, $output);
+
+ $fromDB = \OC::$server->getDatabaseConnection();
+ $toDB = $this->getToDBConnection($input, $output);
+
+ if ($input->getOption('clear-schema')) {
+ $this->clearSchema($toDB, $input, $output);
+ }
+
+ $this->createSchema($toDB, $input, $output);
+
+ $toTables = $this->getTables($toDB);
+ $fromTables = $this->getTables($fromDB);
+
+ // warn/fail if there are more tables in 'from' database
+ $extraFromTables = array_diff($fromTables, $toTables);
+ if (!empty($extraFromTables)) {
+ $output->writeln('<comment>The following tables will not be converted:</comment>');
+ $output->writeln($extraFromTables);
+ if (!$input->getOption('all-apps')) {
+ $output->writeln('<comment>Please note that tables belonging to available but currently not installed apps</comment>');
+ $output->writeln('<comment>can be included by specifying the --all-apps option.</comment>');
+ }
+ /** @var $dialog \Symfony\Component\Console\Helper\DialogHelper */
+ $dialog = $this->getHelperSet()->get('dialog');
+ if (!$dialog->askConfirmation(
+ $output,
+ '<question>Continue with the conversion (y/n)? [n] </question>',
+ false
+ )) {
+ return;
+ }
+ }
+ $intersectingTables = array_intersect($toTables, $fromTables);
+ $this->convertDB($fromDB, $toDB, $intersectingTables, $input, $output);
+ }
+
+ protected function createSchema(Connection $toDB, InputInterface $input, OutputInterface $output) {
+ $output->writeln('<info>Creating schema in new database</info>');
+ $schemaManager = new \OC\DB\MDB2SchemaManager($toDB);
+ $schemaManager->createDbFromStructure(\OC::$SERVERROOT.'/db_structure.xml');
+ $apps = $input->getOption('all-apps') ? \OC_App::getAllApps() : \OC_App::getEnabledApps();
+ foreach($apps as $app) {
+ if (file_exists(\OC_App::getAppPath($app).'/appinfo/database.xml')) {
+ $schemaManager->createDbFromStructure(\OC_App::getAppPath($app).'/appinfo/database.xml');
+ }
+ }
+ }
+
+ protected function getToDBConnection(InputInterface $input, OutputInterface $output) {
+ $type = $input->getArgument('type');
+ $connectionParams = array(
+ 'host' => $input->getArgument('hostname'),
+ 'user' => $input->getArgument('username'),
+ 'password' => $input->getOption('password'),
+ 'dbname' => $input->getArgument('database'),
+ 'tablePrefix' => $this->config->getSystemValue('dbtableprefix', 'oc_'),
+ );
+ if ($input->getOption('port')) {
+ $connectionParams['port'] = $input->getOption('port');
+ }
+ return $this->connectionFactory->getConnection($type, $connectionParams);
+ }
+
+ protected function clearSchema(Connection $db, InputInterface $input, OutputInterface $output) {
+ $toTables = $this->getTables($db);
+ if (!empty($toTables)) {
+ $output->writeln('<info>Clearing schema in new database</info>');
+ }
+ foreach($toTables as $table) {
+ $db->getSchemaManager()->dropTable($table);
+ }
+ }
+
+ protected function getTables(Connection $db) {
+ $filterExpression = '/^' . preg_quote($this->config->getSystemValue('dbtableprefix', 'oc_')) . '/';
+ $db->getConfiguration()->
+ setFilterSchemaAssetsExpression($filterExpression);
+ return $db->getSchemaManager()->listTableNames();
+ }
+
+ protected function copyTable(Connection $fromDB, Connection $toDB, $table, InputInterface $input, OutputInterface $output) {
+ /** @var $progress \Symfony\Component\Console\Helper\ProgressHelper */
+ $progress = $this->getHelperSet()->get('progress');
+ $query = 'SELECT COUNT(*) FROM '.$table;
+ $count = $fromDB->fetchColumn($query);
+ $query = 'SELECT * FROM '.$table;
+ $statement = $fromDB->executeQuery($query);
+ $progress->start($output, $count);
+ $progress->setRedrawFrequency($count > 100 ? 5 : 1);
+ while($row = $statement->fetch()) {
+ $progress->advance();
+ if ($input->getArgument('type') === 'oci') {
+ $data = $row;
+ } else {
+ $data = array();
+ foreach ($row as $columnName => $value) {
+ $data[$toDB->quoteIdentifier($columnName)] = $value;
+ }
+ }
+ $toDB->insert($table, $data);
+ }
+ $progress->finish();
+ }
+
+ protected function convertDB(Connection $fromDB, Connection $toDB, array $tables, InputInterface $input, OutputInterface $output) {
+ $this->config->setSystemValue('maintenance', true);
+ try {
+ // copy table rows
+ foreach($tables as $table) {
+ $output->writeln($table);
+ $this->copyTable($fromDB, $toDB, $table, $input, $output);
+ }
+ if ($input->getArgument('type') === 'pgsql') {
+ $tools = new \OC\DB\PgSqlTools($this->config);
+ $tools->resynchronizeDatabaseSequences($toDB);
+ }
+ // save new database config
+ $this->saveDBInfo($input);
+ } catch(\Exception $e) {
+ $this->config->setSystemValue('maintenance', false);
+ throw $e;
+ }
+ $this->config->setSystemValue('maintenance', false);
+ }
+
+ protected function saveDBInfo(InputInterface $input) {
+ $type = $input->getArgument('type');
+ $username = $input->getArgument('username');
+ $dbHost = $input->getArgument('hostname');
+ $dbName = $input->getArgument('database');
+ $password = $input->getOption('password');
+ if ($input->getOption('port')) {
+ $dbHost .= ':'.$input->getOption('port');
+ }
+
+ $this->config->setSystemValues([
+ 'dbtype' => $type,
+ 'dbname' => $dbName,
+ 'dbhost' => $dbHost,
+ 'dbuser' => $username,
+ 'dbpassword' => $password,
+ ]);
+ }
+}
diff --git a/core/Command/Db/GenerateChangeScript.php b/core/Command/Db/GenerateChangeScript.php
new file mode 100644
index 00000000000..85436b02d65
--- /dev/null
+++ b/core/Command/Db/GenerateChangeScript.php
@@ -0,0 +1,58 @@
+<?php
+/**
+ * @author Morris Jobke <hey@morrisjobke.de>
+ * @author Thomas Müller <thomas.mueller@tmit.eu>
+ *
+ * @copyright Copyright (c) 2016, ownCloud, Inc.
+ * @license AGPL-3.0
+ *
+ * This code is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU Affero General Public License, version 3,
+ * as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU Affero General Public License for more details.
+ *
+ * You should have received a copy of the GNU Affero General Public License, version 3,
+ * along with this program. If not, see <http://www.gnu.org/licenses/>
+ *
+ */
+
+namespace OC\Core\Command\Db;
+
+use Symfony\Component\Console\Command\Command;
+use Symfony\Component\Console\Input\InputArgument;
+use Symfony\Component\Console\Input\InputInterface;
+use Symfony\Component\Console\Output\OutputInterface;
+
+class GenerateChangeScript extends Command {
+ protected function configure() {
+ $this
+ ->setName('db:generate-change-script')
+ ->setDescription('generates the change script from the current connected db to db_structure.xml')
+ ->addArgument(
+ 'schema-xml',
+ InputArgument::OPTIONAL,
+ 'the schema xml to be used as target schema',
+ \OC::$SERVERROOT . '/db_structure.xml'
+ )
+ ;
+ }
+
+ protected function execute(InputInterface $input, OutputInterface $output) {
+
+ $file = $input->getArgument('schema-xml');
+
+ $schemaManager = new \OC\DB\MDB2SchemaManager(\OC::$server->getDatabaseConnection());
+
+ try {
+ $result = $schemaManager->updateDbFromStructure($file, true);
+ $output->writeln($result);
+ } catch (\Exception $e) {
+ $output->writeln('Failed to update database structure ('.$e.')');
+ }
+
+ }
+}
diff --git a/core/Command/Encryption/ChangeKeyStorageRoot.php b/core/Command/Encryption/ChangeKeyStorageRoot.php
new file mode 100644
index 00000000000..801a08b42a8
--- /dev/null
+++ b/core/Command/Encryption/ChangeKeyStorageRoot.php
@@ -0,0 +1,270 @@
+<?php
+/**
+ * @author Björn Schießle <schiessle@owncloud.com>
+ *
+ * @copyright Copyright (c) 2016, ownCloud, Inc.
+ * @license AGPL-3.0
+ *
+ * This code is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU Affero General Public License, version 3,
+ * as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU Affero General Public License for more details.
+ *
+ * You should have received a copy of the GNU Affero General Public License, version 3,
+ * along with this program. If not, see <http://www.gnu.org/licenses/>
+ *
+ */
+
+
+namespace OC\Core\Command\Encryption;
+
+use OC\Encryption\Keys\Storage;
+use OC\Encryption\Util;
+use OC\Files\Filesystem;
+use OC\Files\View;
+use OCP\IConfig;
+use OCP\IUserManager;
+use Symfony\Component\Console\Command\Command;
+use Symfony\Component\Console\Helper\ProgressBar;
+use Symfony\Component\Console\Helper\QuestionHelper;
+use Symfony\Component\Console\Input\InputArgument;
+use Symfony\Component\Console\Input\InputInterface;
+use Symfony\Component\Console\Output\OutputInterface;
+use Symfony\Component\Console\Question\ConfirmationQuestion;
+
+class ChangeKeyStorageRoot extends Command {
+
+ /** @var View */
+ protected $rootView;
+
+ /** @var IUserManager */
+ protected $userManager;
+
+ /** @var IConfig */
+ protected $config;
+
+ /** @var Util */
+ protected $util;
+
+ /** @var QuestionHelper */
+ protected $questionHelper;
+
+ /**
+ * @param View $view
+ * @param IUserManager $userManager
+ * @param IConfig $config
+ * @param Util $util
+ * @param QuestionHelper $questionHelper
+ */
+ public function __construct(View $view, IUserManager $userManager, IConfig $config, Util $util, QuestionHelper $questionHelper) {
+ parent::__construct();
+ $this->rootView = $view;
+ $this->userManager = $userManager;
+ $this->config = $config;
+ $this->util = $util;
+ $this->questionHelper = $questionHelper;
+ }
+
+ protected function configure() {
+ parent::configure();
+ $this
+ ->setName('encryption:change-key-storage-root')
+ ->setDescription('Change key storage root')
+ ->addArgument(
+ 'newRoot',
+ InputArgument::OPTIONAL,
+ 'new root of the key storage relative to the data folder'
+ );
+ }
+
+ protected function execute(InputInterface $input, OutputInterface $output) {
+ $oldRoot = $this->util->getKeyStorageRoot();
+ $newRoot = $input->getArgument('newRoot');
+
+ if ($newRoot === null) {
+ $question = new ConfirmationQuestion('No storage root given, do you want to reset the key storage root to the default location? (y/n) ', false);
+ if (!$this->questionHelper->ask($input, $output, $question)) {
+ return;
+ }
+ $newRoot = '';
+ }
+
+ $oldRootDescription = $oldRoot !== '' ? $oldRoot : 'default storage location';
+ $newRootDescription = $newRoot !== '' ? $newRoot : 'default storage location';
+ $output->writeln("Change key storage root from <info>$oldRootDescription</info> to <info>$newRootDescription</info>");
+ $success = $this->moveAllKeys($oldRoot, $newRoot, $output);
+ if ($success) {
+ $this->util->setKeyStorageRoot($newRoot);
+ $output->writeln('');
+ $output->writeln("Key storage root successfully changed to <info>$newRootDescription</info>");
+ }
+ }
+
+ /**
+ * move keys to new key storage root
+ *
+ * @param string $oldRoot
+ * @param string $newRoot
+ * @param OutputInterface $output
+ * @return bool
+ * @throws \Exception
+ */
+ protected function moveAllKeys($oldRoot, $newRoot, OutputInterface $output) {
+
+ $output->writeln("Start to move keys:");
+
+ if ($this->rootView->is_dir(($oldRoot)) === false) {
+ $output->writeln("No old keys found: Nothing needs to be moved");
+ return false;
+ }
+
+ $this->prepareNewRoot($newRoot);
+ $this->moveSystemKeys($oldRoot, $newRoot);
+ $this->moveUserKeys($oldRoot, $newRoot, $output);
+
+ return true;
+ }
+
+ /**
+ * prepare new key storage
+ *
+ * @param string $newRoot
+ * @throws \Exception
+ */
+ protected function prepareNewRoot($newRoot) {
+ if ($this->rootView->is_dir($newRoot) === false) {
+ throw new \Exception("New root folder doesn't exist. Please create the folder or check the permissions and try again.");
+ }
+
+ $result = $this->rootView->file_put_contents(
+ $newRoot . '/' . Storage::KEY_STORAGE_MARKER,
+ 'ownCloud will detect this folder as key storage root only if this file exists'
+ );
+
+ if ($result === false) {
+ throw new \Exception("Can't write to new root folder. Please check the permissions and try again");
+ }
+
+ }
+
+
+ /**
+ * move system key folder
+ *
+ * @param string $oldRoot
+ * @param string $newRoot
+ */
+ protected function moveSystemKeys($oldRoot, $newRoot) {
+ if (
+ $this->rootView->is_dir($oldRoot . '/files_encryption') &&
+ $this->targetExists($newRoot . '/files_encryption') === false
+ ) {
+ $this->rootView->rename($oldRoot . '/files_encryption', $newRoot . '/files_encryption');
+ }
+ }
+
+
+ /**
+ * setup file system for the given user
+ *
+ * @param string $uid
+ */
+ protected function setupUserFS($uid) {
+ \OC_Util::tearDownFS();
+ \OC_Util::setupFS($uid);
+ }
+
+
+ /**
+ * iterate over each user and move the keys to the new storage
+ *
+ * @param string $oldRoot
+ * @param string $newRoot
+ * @param OutputInterface $output
+ */
+ protected function moveUserKeys($oldRoot, $newRoot, OutputInterface $output) {
+
+ $progress = new ProgressBar($output);
+ $progress->start();
+
+
+ foreach($this->userManager->getBackends() as $backend) {
+ $limit = 500;
+ $offset = 0;
+ do {
+ $users = $backend->getUsers('', $limit, $offset);
+ foreach ($users as $user) {
+ $progress->advance();
+ $this->setupUserFS($user);
+ $this->moveUserEncryptionFolder($user, $oldRoot, $newRoot);
+ }
+ $offset += $limit;
+ } while(count($users) >= $limit);
+ }
+ $progress->finish();
+ }
+
+ /**
+ * move user encryption folder to new root folder
+ *
+ * @param string $user
+ * @param string $oldRoot
+ * @param string $newRoot
+ * @throws \Exception
+ */
+ protected function moveUserEncryptionFolder($user, $oldRoot, $newRoot) {
+
+ if ($this->userManager->userExists($user)) {
+
+ $source = $oldRoot . '/' . $user . '/files_encryption';
+ $target = $newRoot . '/' . $user . '/files_encryption';
+ if (
+ $this->rootView->is_dir($source) &&
+ $this->targetExists($target) === false
+ ) {
+ $this->prepareParentFolder($newRoot . '/' . $user);
+ $this->rootView->rename($source, $target);
+ }
+ }
+ }
+
+ /**
+ * Make preparations to filesystem for saving a key file
+ *
+ * @param string $path relative to data/
+ */
+ protected function prepareParentFolder($path) {
+ $path = Filesystem::normalizePath($path);
+ // If the file resides within a subdirectory, create it
+ if ($this->rootView->file_exists($path) === false) {
+ $sub_dirs = explode('/', ltrim($path, '/'));
+ $dir = '';
+ foreach ($sub_dirs as $sub_dir) {
+ $dir .= '/' . $sub_dir;
+ if ($this->rootView->file_exists($dir) === false) {
+ $this->rootView->mkdir($dir);
+ }
+ }
+ }
+ }
+
+ /**
+ * check if target already exists
+ *
+ * @param $path
+ * @return bool
+ * @throws \Exception
+ */
+ protected function targetExists($path) {
+ if ($this->rootView->file_exists($path)) {
+ throw new \Exception("new folder '$path' already exists");
+ }
+
+ return false;
+ }
+
+}
diff --git a/core/Command/Encryption/DecryptAll.php b/core/Command/Encryption/DecryptAll.php
new file mode 100644
index 00000000000..0a126db5b17
--- /dev/null
+++ b/core/Command/Encryption/DecryptAll.php
@@ -0,0 +1,160 @@
+<?php
+/**
+ * @author Björn Schießle <schiessle@owncloud.com>
+ * @author Joas Schilling <nickvergessen@owncloud.com>
+ *
+ * @copyright Copyright (c) 2016, ownCloud, Inc.
+ * @license AGPL-3.0
+ *
+ * This code is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU Affero General Public License, version 3,
+ * as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU Affero General Public License for more details.
+ *
+ * You should have received a copy of the GNU Affero General Public License, version 3,
+ * along with this program. If not, see <http://www.gnu.org/licenses/>
+ *
+ */
+
+namespace OC\Core\Command\Encryption;
+
+use OCP\App\IAppManager;
+use OCP\Encryption\IManager;
+use OCP\IConfig;
+use Symfony\Component\Console\Command\Command;
+use Symfony\Component\Console\Helper\QuestionHelper;
+use Symfony\Component\Console\Input\InputArgument;
+use Symfony\Component\Console\Input\InputInterface;
+use Symfony\Component\Console\Output\OutputInterface;
+use Symfony\Component\Console\Question\ConfirmationQuestion;
+
+class DecryptAll extends Command {
+
+ /** @var IManager */
+ protected $encryptionManager;
+
+ /** @var IAppManager */
+ protected $appManager;
+
+ /** @var IConfig */
+ protected $config;
+
+ /** @var QuestionHelper */
+ protected $questionHelper;
+
+ /** @var bool */
+ protected $wasTrashbinEnabled;
+
+ /** @var bool */
+ protected $wasSingleUserModeEnabled;
+
+ /** @var \OC\Encryption\DecryptAll */
+ protected $decryptAll;
+
+ /**
+ * @param IManager $encryptionManager
+ * @param IAppManager $appManager
+ * @param IConfig $config
+ * @param \OC\Encryption\DecryptAll $decryptAll
+ * @param QuestionHelper $questionHelper
+ */
+ public function __construct(
+ IManager $encryptionManager,
+ IAppManager $appManager,
+ IConfig $config,
+ \OC\Encryption\DecryptAll $decryptAll,
+ QuestionHelper $questionHelper
+ ) {
+ parent::__construct();
+
+ $this->appManager = $appManager;
+ $this->encryptionManager = $encryptionManager;
+ $this->config = $config;
+ $this->decryptAll = $decryptAll;
+ $this->questionHelper = $questionHelper;
+ }
+
+ /**
+ * Set single user mode and disable the trashbin app
+ */
+ protected function forceSingleUserAndTrashbin() {
+ $this->wasTrashbinEnabled = $this->appManager->isEnabledForUser('files_trashbin');
+ $this->wasSingleUserModeEnabled = $this->config->getSystemValue('singleuser', false);
+ $this->config->setSystemValue('singleuser', true);
+ $this->appManager->disableApp('files_trashbin');
+ }
+
+ /**
+ * Reset the single user mode and re-enable the trashbin app
+ */
+ protected function resetSingleUserAndTrashbin() {
+ $this->config->setSystemValue('singleuser', $this->wasSingleUserModeEnabled);
+ if ($this->wasTrashbinEnabled) {
+ $this->appManager->enableApp('files_trashbin');
+ }
+ }
+
+ protected function configure() {
+ parent::configure();
+
+ $this->setName('encryption:decrypt-all');
+ $this->setDescription('Disable server-side encryption and decrypt all files');
+ $this->setHelp(
+ 'This will disable server-side encryption and decrypt all files for '
+ . 'all users if it is supported by your encryption module. '
+ . 'Please make sure that no user access his files during this process!'
+ );
+ $this->addArgument(
+ 'user',
+ InputArgument::OPTIONAL,
+ 'user for which you want to decrypt all files (optional)'
+ );
+ }
+
+ protected function execute(InputInterface $input, OutputInterface $output) {
+
+ try {
+ if ($this->encryptionManager->isEnabled() === true) {
+ $output->write('Disable server side encryption... ');
+ $this->config->setAppValue('core', 'encryption_enabled', 'no');
+ $output->writeln('done.');
+ } else {
+ $output->writeln('Server side encryption not enabled. Nothing to do.');
+ return;
+ }
+
+ $output->writeln("\n");
+ $output->writeln('You are about to start to decrypt all files stored in your ownCloud.');
+ $output->writeln('It will depend on the encryption module and your setup if this is possible.');
+ $output->writeln('Depending on the number and size of your files this can take some time');
+ $output->writeln('Please make sure that no user access his files during this process!');
+ $output->writeln('');
+ $question = new ConfirmationQuestion('Do you really want to continue? (y/n) ', false);
+ if ($this->questionHelper->ask($input, $output, $question)) {
+ $this->forceSingleUserAndTrashbin();
+ $user = $input->getArgument('user');
+ $result = $this->decryptAll->decryptAll($input, $output, $user);
+ if ($result === false) {
+ $output->writeln(' aborted.');
+ $this->config->setAppValue('core', 'encryption_enabled', 'yes');
+ }
+ $this->resetSingleUserAndTrashbin();
+ } else {
+ $output->write('Enable server side encryption... ');
+ $this->config->setAppValue('core', 'encryption_enabled', 'yes');
+ $output->writeln('done.');
+ $output->writeln('aborted');
+ }
+ } catch (\Exception $e) {
+ // enable server side encryption again if something went wrong
+ $this->config->setAppValue('core', 'encryption_enabled', 'yes');
+ $this->resetSingleUserAndTrashbin();
+ throw $e;
+ }
+
+ }
+}
diff --git a/core/Command/Encryption/Disable.php b/core/Command/Encryption/Disable.php
new file mode 100644
index 00000000000..0e08a314473
--- /dev/null
+++ b/core/Command/Encryption/Disable.php
@@ -0,0 +1,56 @@
+<?php
+/**
+ * @author Joas Schilling <nickvergessen@owncloud.com>
+ *
+ * @copyright Copyright (c) 2016, ownCloud, Inc.
+ * @license AGPL-3.0
+ *
+ * This code is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU Affero General Public License, version 3,
+ * as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU Affero General Public License for more details.
+ *
+ * You should have received a copy of the GNU Affero General Public License, version 3,
+ * along with this program. If not, see <http://www.gnu.org/licenses/>
+ *
+ */
+
+namespace OC\Core\Command\Encryption;
+
+use OCP\IConfig;
+use Symfony\Component\Console\Command\Command;
+use Symfony\Component\Console\Input\InputInterface;
+use Symfony\Component\Console\Output\OutputInterface;
+
+class Disable extends Command {
+ /** @var IConfig */
+ protected $config;
+
+ /**
+ * @param IConfig $config
+ */
+ public function __construct(IConfig $config) {
+ parent::__construct();
+ $this->config = $config;
+ }
+
+ protected function configure() {
+ $this
+ ->setName('encryption:disable')
+ ->setDescription('Disable encryption')
+ ;
+ }
+
+ protected function execute(InputInterface $input, OutputInterface $output) {
+ if ($this->config->getAppValue('core', 'encryption_enabled', 'no') !== 'yes') {
+ $output->writeln('Encryption is already disabled');
+ } else {
+ $this->config->setAppValue('core', 'encryption_enabled', 'no');
+ $output->writeln('<info>Encryption disabled</info>');
+ }
+ }
+}
diff --git a/core/Command/Encryption/Enable.php b/core/Command/Encryption/Enable.php
new file mode 100644
index 00000000000..273320e6155
--- /dev/null
+++ b/core/Command/Encryption/Enable.php
@@ -0,0 +1,78 @@
+<?php
+/**
+ * @author Joas Schilling <nickvergessen@owncloud.com>
+ *
+ * @copyright Copyright (c) 2016, ownCloud, Inc.
+ * @license AGPL-3.0
+ *
+ * This code is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU Affero General Public License, version 3,
+ * as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU Affero General Public License for more details.
+ *
+ * You should have received a copy of the GNU Affero General Public License, version 3,
+ * along with this program. If not, see <http://www.gnu.org/licenses/>
+ *
+ */
+
+namespace OC\Core\Command\Encryption;
+
+use OCP\Encryption\IManager;
+use OCP\IConfig;
+use Symfony\Component\Console\Command\Command;
+use Symfony\Component\Console\Input\InputInterface;
+use Symfony\Component\Console\Output\OutputInterface;
+
+class Enable extends Command {
+ /** @var IConfig */
+ protected $config;
+
+ /** @var IManager */
+ protected $encryptionManager;
+
+ /**
+ * @param IConfig $config
+ * @param IManager $encryptionManager
+ */
+ public function __construct(IConfig $config, IManager $encryptionManager) {
+ parent::__construct();
+
+ $this->encryptionManager = $encryptionManager;
+ $this->config = $config;
+ }
+
+ protected function configure() {
+ $this
+ ->setName('encryption:enable')
+ ->setDescription('Enable encryption')
+ ;
+ }
+
+ protected function execute(InputInterface $input, OutputInterface $output) {
+ if ($this->config->getAppValue('core', 'encryption_enabled', 'no') === 'yes') {
+ $output->writeln('Encryption is already enabled');
+ } else {
+ $this->config->setAppValue('core', 'encryption_enabled', 'yes');
+ $output->writeln('<info>Encryption enabled</info>');
+ }
+ $output->writeln('');
+
+ $modules = $this->encryptionManager->getEncryptionModules();
+ if (empty($modules)) {
+ $output->writeln('<error>No encryption module is loaded</error>');
+ } else {
+ $defaultModule = $this->config->getAppValue('core', 'default_encryption_module', null);
+ if ($defaultModule === null) {
+ $output->writeln('<error>No default module is set</error>');
+ } else if (!isset($modules[$defaultModule])) {
+ $output->writeln('<error>The current default module does not exist: ' . $defaultModule . '</error>');
+ } else {
+ $output->writeln('Default module: ' . $defaultModule);
+ }
+ }
+ }
+}
diff --git a/core/Command/Encryption/EncryptAll.php b/core/Command/Encryption/EncryptAll.php
new file mode 100644
index 00000000000..02f74a9dea4
--- /dev/null
+++ b/core/Command/Encryption/EncryptAll.php
@@ -0,0 +1,134 @@
+<?php
+/**
+ * @author Björn Schießle <schiessle@owncloud.com>
+ * @author Joas Schilling <nickvergessen@owncloud.com>
+ *
+ * @copyright Copyright (c) 2016, ownCloud, Inc.
+ * @license AGPL-3.0
+ *
+ * This code is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU Affero General Public License, version 3,
+ * as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU Affero General Public License for more details.
+ *
+ * You should have received a copy of the GNU Affero General Public License, version 3,
+ * along with this program. If not, see <http://www.gnu.org/licenses/>
+ *
+ */
+
+namespace OC\Core\Command\Encryption;
+
+use OCP\App\IAppManager;
+use OCP\Encryption\IManager;
+use OCP\IConfig;
+use Symfony\Component\Console\Command\Command;
+use Symfony\Component\Console\Helper\QuestionHelper;
+use Symfony\Component\Console\Input\InputInterface;
+use Symfony\Component\Console\Output\OutputInterface;
+use Symfony\Component\Console\Question\ConfirmationQuestion;
+
+class EncryptAll extends Command {
+
+ /** @var IManager */
+ protected $encryptionManager;
+
+ /** @var IAppManager */
+ protected $appManager;
+
+ /** @var IConfig */
+ protected $config;
+
+ /** @var QuestionHelper */
+ protected $questionHelper;
+
+ /** @var bool */
+ protected $wasTrashbinEnabled;
+
+ /** @var bool */
+ protected $wasSingleUserModeEnabled;
+
+ /**
+ * @param IManager $encryptionManager
+ * @param IAppManager $appManager
+ * @param IConfig $config
+ * @param QuestionHelper $questionHelper
+ */
+ public function __construct(
+ IManager $encryptionManager,
+ IAppManager $appManager,
+ IConfig $config,
+ QuestionHelper $questionHelper
+ ) {
+ parent::__construct();
+ $this->appManager = $appManager;
+ $this->encryptionManager = $encryptionManager;
+ $this->config = $config;
+ $this->questionHelper = $questionHelper;
+ }
+
+ /**
+ * Set single user mode and disable the trashbin app
+ */
+ protected function forceSingleUserAndTrashbin() {
+ $this->wasTrashbinEnabled = $this->appManager->isEnabledForUser('files_trashbin');
+ $this->wasSingleUserModeEnabled = $this->config->getSystemValue('singleuser', false);
+ $this->config->setSystemValue('singleuser', true);
+ $this->appManager->disableApp('files_trashbin');
+ }
+
+ /**
+ * Reset the single user mode and re-enable the trashbin app
+ */
+ protected function resetSingleUserAndTrashbin() {
+ $this->config->setSystemValue('singleuser', $this->wasSingleUserModeEnabled);
+ if ($this->wasTrashbinEnabled) {
+ $this->appManager->enableApp('files_trashbin');
+ }
+ }
+
+ protected function configure() {
+ parent::configure();
+
+ $this->setName('encryption:encrypt-all');
+ $this->setDescription('Encrypt all files for all users');
+ $this->setHelp(
+ 'This will encrypt all files for all users. '
+ . 'Please make sure that no user access his files during this process!'
+ );
+ }
+
+ protected function execute(InputInterface $input, OutputInterface $output) {
+
+ if ($this->encryptionManager->isEnabled() === false) {
+ throw new \Exception('Server side encryption is not enabled');
+ }
+
+ $output->writeln("\n");
+ $output->writeln('You are about to start to encrypt all files stored in your ownCloud.');
+ $output->writeln('It will depend on the encryption module you use which files get encrypted.');
+ $output->writeln('Depending on the number and size of your files this can take some time');
+ $output->writeln('Please make sure that no user access his files during this process!');
+ $output->writeln('');
+ $question = new ConfirmationQuestion('Do you really want to continue? (y/n) ', false);
+ if ($this->questionHelper->ask($input, $output, $question)) {
+ $this->forceSingleUserAndTrashbin();
+
+ try {
+ $defaultModule = $this->encryptionManager->getEncryptionModule();
+ $defaultModule->encryptAll($input, $output);
+ } catch (\Exception $ex) {
+ $this->resetSingleUserAndTrashbin();
+ throw $ex;
+ }
+
+ $this->resetSingleUserAndTrashbin();
+ } else {
+ $output->writeln('aborted');
+ }
+ }
+
+}
diff --git a/core/Command/Encryption/ListModules.php b/core/Command/Encryption/ListModules.php
new file mode 100644
index 00000000000..9c061b6e764
--- /dev/null
+++ b/core/Command/Encryption/ListModules.php
@@ -0,0 +1,80 @@
+<?php
+/**
+ * @author Joas Schilling <nickvergessen@owncloud.com>
+ *
+ * @copyright Copyright (c) 2016, ownCloud, Inc.
+ * @license AGPL-3.0
+ *
+ * This code is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU Affero General Public License, version 3,
+ * as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU Affero General Public License for more details.
+ *
+ * You should have received a copy of the GNU Affero General Public License, version 3,
+ * along with this program. If not, see <http://www.gnu.org/licenses/>
+ *
+ */
+
+namespace OC\Core\Command\Encryption;
+
+use OC\Core\Command\Base;
+use OCP\Encryption\IManager;
+use Symfony\Component\Console\Input\InputInterface;
+use Symfony\Component\Console\Output\OutputInterface;
+
+class ListModules extends Base {
+ /** @var IManager */
+ protected $encryptionManager;
+
+ /**
+ * @param IManager $encryptionManager
+ */
+ public function __construct(IManager $encryptionManager) {
+ parent::__construct();
+ $this->encryptionManager = $encryptionManager;
+ }
+
+ protected function configure() {
+ parent::configure();
+
+ $this
+ ->setName('encryption:list-modules')
+ ->setDescription('List all available encryption modules')
+ ;
+ }
+
+ protected function execute(InputInterface $input, OutputInterface $output) {
+ $encryptionModules = $this->encryptionManager->getEncryptionModules();
+ $defaultEncryptionModuleId = $this->encryptionManager->getDefaultEncryptionModuleId();
+
+ $encModules = array();
+ foreach ($encryptionModules as $module) {
+ $encModules[$module['id']]['displayName'] = $module['displayName'];
+ $encModules[$module['id']]['default'] = $module['id'] === $defaultEncryptionModuleId;
+ }
+ $this->writeModuleList($input, $output, $encModules);
+ }
+
+ /**
+ * @param InputInterface $input
+ * @param OutputInterface $output
+ * @param array $items
+ */
+ protected function writeModuleList(InputInterface $input, OutputInterface $output, $items) {
+ if ($input->getOption('output') === self::OUTPUT_FORMAT_PLAIN) {
+ array_walk($items, function(&$item) {
+ if (!$item['default']) {
+ $item = $item['displayName'];
+ } else {
+ $item = $item['displayName'] . ' [default*]';
+ }
+ });
+ }
+
+ $this->writeArrayInOutputFormat($input, $output, $items);
+ }
+}
diff --git a/core/Command/Encryption/SetDefaultModule.php b/core/Command/Encryption/SetDefaultModule.php
new file mode 100644
index 00000000000..e9978536201
--- /dev/null
+++ b/core/Command/Encryption/SetDefaultModule.php
@@ -0,0 +1,68 @@
+<?php
+/**
+ * @author Joas Schilling <nickvergessen@owncloud.com>
+ *
+ * @copyright Copyright (c) 2016, ownCloud, Inc.
+ * @license AGPL-3.0
+ *
+ * This code is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU Affero General Public License, version 3,
+ * as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU Affero General Public License for more details.
+ *
+ * You should have received a copy of the GNU Affero General Public License, version 3,
+ * along with this program. If not, see <http://www.gnu.org/licenses/>
+ *
+ */
+
+namespace OC\Core\Command\Encryption;
+
+
+use OCP\Encryption\IManager;
+use Symfony\Component\Console\Command\Command;
+use Symfony\Component\Console\Input\InputArgument;
+use Symfony\Component\Console\Input\InputInterface;
+use Symfony\Component\Console\Output\OutputInterface;
+
+class SetDefaultModule extends Command {
+ /** @var IManager */
+ protected $encryptionManager;
+
+ /**
+ * @param IManager $encryptionManager
+ */
+ public function __construct(IManager $encryptionManager) {
+ parent::__construct();
+ $this->encryptionManager = $encryptionManager;
+ }
+
+ protected function configure() {
+ parent::configure();
+
+ $this
+ ->setName('encryption:set-default-module')
+ ->setDescription('Set the encryption default module')
+ ->addArgument(
+ 'module',
+ InputArgument::REQUIRED,
+ 'ID of the encryption module that should be used'
+ )
+ ;
+ }
+
+ protected function execute(InputInterface $input, OutputInterface $output) {
+ $moduleId = $input->getArgument('module');
+
+ if ($moduleId === $this->encryptionManager->getDefaultEncryptionModuleId()) {
+ $output->writeln('"' . $moduleId . '"" is already the default module');
+ } else if ($this->encryptionManager->setDefaultEncryptionModule($moduleId)) {
+ $output->writeln('<info>Set default module to "' . $moduleId . '"</info>');
+ } else {
+ $output->writeln('<error>The specified module "' . $moduleId . '" does not exist</error>');
+ }
+ }
+}
diff --git a/core/Command/Encryption/ShowKeyStorageRoot.php b/core/Command/Encryption/ShowKeyStorageRoot.php
new file mode 100644
index 00000000000..402352c4bcf
--- /dev/null
+++ b/core/Command/Encryption/ShowKeyStorageRoot.php
@@ -0,0 +1,58 @@
+<?php
+/**
+ * @author Björn Schießle <schiessle@owncloud.com>
+ *
+ * @copyright Copyright (c) 2016, ownCloud, Inc.
+ * @license AGPL-3.0
+ *
+ * This code is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU Affero General Public License, version 3,
+ * as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU Affero General Public License for more details.
+ *
+ * You should have received a copy of the GNU Affero General Public License, version 3,
+ * along with this program. If not, see <http://www.gnu.org/licenses/>
+ *
+ */
+
+
+namespace OC\Core\Command\Encryption;
+
+use OC\Encryption\Util;
+use Symfony\Component\Console\Command\Command;
+use Symfony\Component\Console\Input\InputInterface;
+use Symfony\Component\Console\Output\OutputInterface;
+
+class ShowKeyStorageRoot extends Command{
+
+ /** @var Util */
+ protected $util;
+
+ /**
+ * @param Util $util
+ */
+ public function __construct(Util $util) {
+ parent::__construct();
+ $this->util = $util;
+ }
+
+ protected function configure() {
+ parent::configure();
+ $this
+ ->setName('encryption:show-key-storage-root')
+ ->setDescription('Show current key storage root');
+ }
+
+ protected function execute(InputInterface $input, OutputInterface $output) {
+ $currentRoot = $this->util->getKeyStorageRoot();
+
+ $rootDescription = $currentRoot !== '' ? $currentRoot : 'default storage location (data/)';
+
+ $output->writeln("Current key storage root: <info>$rootDescription</info>");
+ }
+
+}
diff --git a/core/Command/Encryption/Status.php b/core/Command/Encryption/Status.php
new file mode 100644
index 00000000000..b97ea8833fa
--- /dev/null
+++ b/core/Command/Encryption/Status.php
@@ -0,0 +1,56 @@
+<?php
+/**
+ * @author Joas Schilling <nickvergessen@owncloud.com>
+ *
+ * @copyright Copyright (c) 2016, ownCloud, Inc.
+ * @license AGPL-3.0
+ *
+ * This code is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU Affero General Public License, version 3,
+ * as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU Affero General Public License for more details.
+ *
+ * You should have received a copy of the GNU Affero General Public License, version 3,
+ * along with this program. If not, see <http://www.gnu.org/licenses/>
+ *
+ */
+
+namespace OC\Core\Command\Encryption;
+
+use OC\Core\Command\Base;
+use OCP\Encryption\IManager;
+use Symfony\Component\Console\Input\InputInterface;
+use Symfony\Component\Console\Output\OutputInterface;
+
+class Status extends Base {
+ /** @var IManager */
+ protected $encryptionManager;
+
+ /**
+ * @param IManager $encryptionManager
+ */
+ public function __construct(IManager $encryptionManager) {
+ parent::__construct();
+ $this->encryptionManager = $encryptionManager;
+ }
+
+ protected function configure() {
+ parent::configure();
+
+ $this
+ ->setName('encryption:status')
+ ->setDescription('Lists the current status of encryption')
+ ;
+ }
+
+ protected function execute(InputInterface $input, OutputInterface $output) {
+ $this->writeArrayInOutputFormat($input, $output, [
+ 'enabled' => $this->encryptionManager->isEnabled(),
+ 'defaultModule' => $this->encryptionManager->getDefaultEncryptionModuleId(),
+ ]);
+ }
+}
diff --git a/core/Command/Integrity/CheckApp.php b/core/Command/Integrity/CheckApp.php
new file mode 100644
index 00000000000..643af5285b4
--- /dev/null
+++ b/core/Command/Integrity/CheckApp.php
@@ -0,0 +1,69 @@
+<?php
+/**
+ * @author Victor Dubiniuk <dubiniuk@owncloud.com>
+ *
+ * @copyright Copyright (c) 2016, ownCloud, Inc.
+ * @license AGPL-3.0
+ *
+ * This code is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU Affero General Public License, version 3,
+ * as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU Affero General Public License for more details.
+ *
+ * You should have received a copy of the GNU Affero General Public License, version 3,
+ * along with this program. If not, see <http://www.gnu.org/licenses/>
+ *
+ */
+namespace OC\Core\Command\Integrity;
+
+use OC\IntegrityCheck\Checker;
+use OC\Core\Command\Base;
+use Symfony\Component\Console\Input\InputInterface;
+use Symfony\Component\Console\Input\InputArgument;
+use Symfony\Component\Console\Input\InputOption;
+use Symfony\Component\Console\Output\OutputInterface;
+
+/**
+ * Class CheckApp
+ *
+ * @package OC\Core\Command\Integrity
+ */
+class CheckApp extends Base {
+
+ /**
+ * @var Checker
+ */
+ private $checker;
+
+ public function __construct(Checker $checker) {
+ parent::__construct();
+ $this->checker = $checker;
+ }
+
+ /**
+ * {@inheritdoc }
+ */
+ protected function configure() {
+ parent::configure();
+ $this
+ ->setName('integrity:check-app')
+ ->setDescription('Check integrity of an app using a signature.')
+ ->addArgument('appid', null, InputArgument::REQUIRED, 'Application to check')
+ ->addOption('path', null, InputOption::VALUE_OPTIONAL, 'Path to application. If none is given it will be guessed.');
+ }
+
+ /**
+ * {@inheritdoc }
+ */
+ protected function execute(InputInterface $input, OutputInterface $output) {
+ $appid = $input->getArgument('appid');
+ $path = strval($input->getOption('path'));
+ $result = $this->checker->verifyAppSignature($appid, $path);
+ $this->writeArrayInOutputFormat($input, $output, $result);
+ }
+
+}
diff --git a/core/Command/Integrity/CheckCore.php b/core/Command/Integrity/CheckCore.php
new file mode 100644
index 00000000000..460a78e4da7
--- /dev/null
+++ b/core/Command/Integrity/CheckCore.php
@@ -0,0 +1,62 @@
+<?php
+/**
+ * @author Victor Dubiniuk <dubiniuk@owncloud.com>
+ *
+ * @copyright Copyright (c) 2016, ownCloud, Inc.
+ * @license AGPL-3.0
+ *
+ * This code is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU Affero General Public License, version 3,
+ * as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU Affero General Public License for more details.
+ *
+ * You should have received a copy of the GNU Affero General Public License, version 3,
+ * along with this program. If not, see <http://www.gnu.org/licenses/>
+ *
+ */
+
+namespace OC\Core\Command\Integrity;
+
+use OC\IntegrityCheck\Checker;
+use OC\Core\Command\Base;
+use Symfony\Component\Console\Input\InputInterface;
+use Symfony\Component\Console\Output\OutputInterface;
+
+/**
+ * Class CheckCore
+ *
+ * @package OC\Core\Command\Integrity
+ */
+class CheckCore extends Base {
+ /**
+ * @var Checker
+ */
+ private $checker;
+
+ public function __construct(Checker $checker) {
+ parent::__construct();
+ $this->checker = $checker;
+ }
+
+ /**
+ * {@inheritdoc }
+ */
+ protected function configure() {
+ parent::configure();
+ $this
+ ->setName('integrity:check-core')
+ ->setDescription('Check integrity of core code using a signature.');
+ }
+
+ /**
+ * {@inheritdoc }
+ */
+ protected function execute(InputInterface $input, OutputInterface $output) {
+ $result = $this->checker->verifyCoreSignature();
+ $this->writeArrayInOutputFormat($input, $output, $result);
+ }
+}
diff --git a/core/Command/Integrity/SignApp.php b/core/Command/Integrity/SignApp.php
new file mode 100644
index 00000000000..53df9619c6d
--- /dev/null
+++ b/core/Command/Integrity/SignApp.php
@@ -0,0 +1,107 @@
+<?php
+/**
+ * @author Lukas Reschke <lukas@owncloud.com>
+ *
+ * @copyright Copyright (c) 2016, ownCloud, Inc.
+ * @license AGPL-3.0
+ *
+ * This code is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU Affero General Public License, version 3,
+ * as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU Affero General Public License for more details.
+ *
+ * You should have received a copy of the GNU Affero General Public License, version 3,
+ * along with this program. If not, see <http://www.gnu.org/licenses/>
+ *
+ */
+
+namespace OC\Core\Command\Integrity;
+
+use OC\IntegrityCheck\Checker;
+use OC\IntegrityCheck\Helpers\FileAccessHelper;
+use OCP\IURLGenerator;
+use phpseclib\Crypt\RSA;
+use phpseclib\File\X509;
+use Symfony\Component\Console\Command\Command;
+use Symfony\Component\Console\Input\InputInterface;
+use Symfony\Component\Console\Input\InputOption;
+use Symfony\Component\Console\Output\OutputInterface;
+
+/**
+ * Class SignApp
+ *
+ * @package OC\Core\Command\Integrity
+ */
+class SignApp extends Command {
+ /** @var Checker */
+ private $checker;
+ /** @var FileAccessHelper */
+ private $fileAccessHelper;
+ /** @var IURLGenerator */
+ private $urlGenerator;
+
+ /**
+ * @param Checker $checker
+ * @param FileAccessHelper $fileAccessHelper
+ * @param IURLGenerator $urlGenerator
+ */
+ public function __construct(Checker $checker,
+ FileAccessHelper $fileAccessHelper,
+ IURLGenerator $urlGenerator) {
+ parent::__construct(null);
+ $this->checker = $checker;
+ $this->fileAccessHelper = $fileAccessHelper;
+ $this->urlGenerator = $urlGenerator;
+ }
+
+ protected function configure() {
+ $this
+ ->setName('integrity:sign-app')
+ ->setDescription('Signs an app using a private key.')
+ ->addOption('path', null, InputOption::VALUE_REQUIRED, 'Application to sign')
+ ->addOption('privateKey', null, InputOption::VALUE_REQUIRED, 'Path to private key to use for signing')
+ ->addOption('certificate', null, InputOption::VALUE_REQUIRED, 'Path to certificate to use for signing');
+ }
+
+ /**
+ * {@inheritdoc }
+ */
+ protected function execute(InputInterface $input, OutputInterface $output) {
+ $path = $input->getOption('path');
+ $privateKeyPath = $input->getOption('privateKey');
+ $keyBundlePath = $input->getOption('certificate');
+ if(is_null($path) || is_null($privateKeyPath) || is_null($keyBundlePath)) {
+ $documentationUrl = $this->urlGenerator->linkToDocs('developer-code-integrity');
+ $output->writeln('This command requires the --path, --privateKey and --certificate.');
+ $output->writeln('Example: ./occ integrity:sign-app --path="/Users/lukasreschke/Programming/myapp/" --privateKey="/Users/lukasreschke/private/myapp.key" --certificate="/Users/lukasreschke/public/mycert.crt"');
+ $output->writeln('For more information please consult the documentation: '. $documentationUrl);
+ return null;
+ }
+
+ $privateKey = $this->fileAccessHelper->file_get_contents($privateKeyPath);
+ $keyBundle = $this->fileAccessHelper->file_get_contents($keyBundlePath);
+
+ if($privateKey === false) {
+ $output->writeln(sprintf('Private key "%s" does not exists.', $privateKeyPath));
+ return null;
+ }
+
+ if($keyBundle === false) {
+ $output->writeln(sprintf('Certificate "%s" does not exists.', $keyBundlePath));
+ return null;
+ }
+
+ $rsa = new RSA();
+ $rsa->loadKey($privateKey);
+ $x509 = new X509();
+ $x509->loadX509($keyBundle);
+ $x509->setPrivateKey($rsa);
+ $this->checker->writeAppSignature($path, $x509, $rsa);
+
+ $output->writeln('Successfully signed "'.$path.'"');
+ }
+}
diff --git a/core/Command/Integrity/SignCore.php b/core/Command/Integrity/SignCore.php
new file mode 100644
index 00000000000..e5c2de73e00
--- /dev/null
+++ b/core/Command/Integrity/SignCore.php
@@ -0,0 +1,100 @@
+<?php
+/**
+ * @author Lukas Reschke <lukas@owncloud.com>
+ *
+ * @copyright Copyright (c) 2016, ownCloud, Inc.
+ * @license AGPL-3.0
+ *
+ * This code is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU Affero General Public License, version 3,
+ * as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU Affero General Public License for more details.
+ *
+ * You should have received a copy of the GNU Affero General Public License, version 3,
+ * along with this program. If not, see <http://www.gnu.org/licenses/>
+ *
+ */
+
+namespace OC\Core\Command\Integrity;
+
+use OC\IntegrityCheck\Checker;
+use OC\IntegrityCheck\Helpers\EnvironmentHelper;
+use OC\IntegrityCheck\Helpers\FileAccessHelper;
+use phpseclib\Crypt\RSA;
+use phpseclib\File\X509;
+use Symfony\Component\Console\Command\Command;
+use OCP\IConfig;
+use Symfony\Component\Console\Input\InputInterface;
+use Symfony\Component\Console\Input\InputOption;
+use Symfony\Component\Console\Output\OutputInterface;
+
+/**
+ * Class SignCore
+ *
+ * @package OC\Core\Command\Integrity
+ */
+class SignCore extends Command {
+ /** @var Checker */
+ private $checker;
+ /** @var FileAccessHelper */
+ private $fileAccessHelper;
+
+ /**
+ * @param Checker $checker
+ * @param FileAccessHelper $fileAccessHelper
+ */
+ public function __construct(Checker $checker,
+ FileAccessHelper $fileAccessHelper) {
+ parent::__construct(null);
+ $this->checker = $checker;
+ $this->fileAccessHelper = $fileAccessHelper;
+ }
+
+ protected function configure() {
+ $this
+ ->setName('integrity:sign-core')
+ ->setDescription('Sign core using a private key.')
+ ->addOption('privateKey', null, InputOption::VALUE_REQUIRED, 'Path to private key to use for signing')
+ ->addOption('certificate', null, InputOption::VALUE_REQUIRED, 'Path to certificate to use for signing')
+ ->addOption('path', null, InputOption::VALUE_REQUIRED, 'Path of core to sign');
+ }
+
+ /**
+ * {@inheritdoc }
+ */
+ protected function execute(InputInterface $input, OutputInterface $output) {
+ $privateKeyPath = $input->getOption('privateKey');
+ $keyBundlePath = $input->getOption('certificate');
+ $path = $input->getOption('path');
+ if(is_null($privateKeyPath) || is_null($keyBundlePath) || is_null($path)) {
+ $output->writeln('--privateKey, --certificate and --path are required.');
+ return null;
+ }
+
+ $privateKey = $this->fileAccessHelper->file_get_contents($privateKeyPath);
+ $keyBundle = $this->fileAccessHelper->file_get_contents($keyBundlePath);
+
+ if($privateKey === false) {
+ $output->writeln(sprintf('Private key "%s" does not exists.', $privateKeyPath));
+ return null;
+ }
+
+ if($keyBundle === false) {
+ $output->writeln(sprintf('Certificate "%s" does not exists.', $keyBundlePath));
+ return null;
+ }
+
+ $rsa = new RSA();
+ $rsa->loadKey($privateKey);
+ $x509 = new X509();
+ $x509->loadX509($keyBundle);
+ $x509->setPrivateKey($rsa);
+ $this->checker->writeCoreSignature($x509, $rsa, $path);
+
+ $output->writeln('Successfully signed "core"');
+ }
+}
diff --git a/core/Command/L10n/CreateJs.php b/core/Command/L10n/CreateJs.php
new file mode 100644
index 00000000000..c2cfc5d0934
--- /dev/null
+++ b/core/Command/L10n/CreateJs.php
@@ -0,0 +1,137 @@
+<?php
+/**
+ * @author Morris Jobke <hey@morrisjobke.de>
+ * @author Thomas Müller <thomas.mueller@tmit.eu>
+ *
+ * @copyright Copyright (c) 2016, ownCloud, Inc.
+ * @license AGPL-3.0
+ *
+ * This code is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU Affero General Public License, version 3,
+ * as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU Affero General Public License for more details.
+ *
+ * You should have received a copy of the GNU Affero General Public License, version 3,
+ * along with this program. If not, see <http://www.gnu.org/licenses/>
+ *
+ */
+
+namespace OC\Core\Command\L10n;
+
+use DirectoryIterator;
+
+use Symfony\Component\Console\Command\Command;
+use Symfony\Component\Console\Input\InputInterface;
+use Symfony\Component\Console\Input\InputOption;
+use Symfony\Component\Console\Output\OutputInterface;
+use UnexpectedValueException;
+
+class CreateJs extends Command {
+
+ protected function configure() {
+ $this
+ ->setName('l10n:createjs')
+ ->setDescription('Create javascript translation files for a given app')
+ ->addArgument(
+ 'app',
+ InputOption::VALUE_REQUIRED,
+ 'name of the app'
+ )
+ ->addArgument(
+ 'lang',
+ InputOption::VALUE_OPTIONAL,
+ 'name of the language'
+ );
+ }
+
+ protected function execute(InputInterface $input, OutputInterface $output) {
+ $app = $input->getArgument('app');
+ $lang = $input->getArgument('lang');
+
+ $path = \OC_App::getAppPath($app);
+ if ($path === false) {
+ $output->writeln("The app <$app> is unknown.");
+ return;
+ }
+ $languages = $lang;
+ if (empty($lang)) {
+ $languages= $this->getAllLanguages($path);
+ }
+
+ foreach($languages as $lang) {
+ $this->writeFiles($app, $path, $lang, $output);
+ }
+ }
+
+ private function getAllLanguages($path) {
+ $result = array();
+ foreach (new DirectoryIterator("$path/l10n") as $fileInfo) {
+ if($fileInfo->isDot()) {
+ continue;
+ }
+ if($fileInfo->isDir()) {
+ continue;
+ }
+ if($fileInfo->getExtension() !== 'php') {
+ continue;
+ }
+ $result[]= substr($fileInfo->getBasename(), 0, -4);
+ }
+
+ return $result;
+ }
+
+ private function writeFiles($app, $path, $lang, OutputInterface $output) {
+ list($translations, $plurals) = $this->loadTranslations($path, $lang);
+ $this->writeJsFile($app, $path, $lang, $output, $translations, $plurals);
+ $this->writeJsonFile($path, $lang, $output, $translations, $plurals);
+ }
+
+ private function writeJsFile($app, $path, $lang, OutputInterface $output, $translations, $plurals) {
+ $jsFile = "$path/l10n/$lang.js";
+ if (file_exists($jsFile)) {
+ $output->writeln("File already exists: $jsFile");
+ return;
+ }
+ $content = "OC.L10N.register(\n \"$app\",\n {\n ";
+ $jsTrans = array();
+ foreach ($translations as $id => $val) {
+ if (is_array($val)) {
+ $val = '[ ' . join(',', $val) . ']';
+ }
+ $jsTrans[] = "\"$id\" : \"$val\"";
+ }
+ $content .= join(",\n ", $jsTrans);
+ $content .= "\n},\n\"$plurals\");\n";
+
+ file_put_contents($jsFile, $content);
+ $output->writeln("Javascript translation file generated: $jsFile");
+ }
+
+ private function writeJsonFile($path, $lang, OutputInterface $output, $translations, $plurals) {
+ $jsFile = "$path/l10n/$lang.json";
+ if (file_exists($jsFile)) {
+ $output->writeln("File already exists: $jsFile");
+ return;
+ }
+ $content = array('translations' => $translations, 'pluralForm' => $plurals);
+ file_put_contents($jsFile, json_encode($content));
+ $output->writeln("Json translation file generated: $jsFile");
+ }
+
+ private function loadTranslations($path, $lang) {
+ $phpFile = "$path/l10n/$lang.php";
+ $TRANSLATIONS = array();
+ $PLURAL_FORMS = '';
+ if (!file_exists($phpFile)) {
+ throw new UnexpectedValueException("PHP translation file <$phpFile> does not exist.");
+ }
+ require $phpFile;
+
+ return array($TRANSLATIONS, $PLURAL_FORMS);
+ }
+}
diff --git a/core/Command/Log/Manage.php b/core/Command/Log/Manage.php
new file mode 100644
index 00000000000..1d65d7ed0d8
--- /dev/null
+++ b/core/Command/Log/Manage.php
@@ -0,0 +1,171 @@
+<?php
+/**
+ * @author Robin McCorkell <robin@mccorkell.me.uk>
+ *
+ * @copyright Copyright (c) 2016, ownCloud, Inc.
+ * @license AGPL-3.0
+ *
+ * This code is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU Affero General Public License, version 3,
+ * as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU Affero General Public License for more details.
+ *
+ * You should have received a copy of the GNU Affero General Public License, version 3,
+ * along with this program. If not, see <http://www.gnu.org/licenses/>
+ *
+ */
+
+namespace OC\Core\Command\Log;
+
+use \OCP\IConfig;
+
+use Symfony\Component\Console\Command\Command;
+use Symfony\Component\Console\Input\InputInterface;
+use Symfony\Component\Console\Input\InputArgument;
+use Symfony\Component\Console\Input\InputOption;
+use Symfony\Component\Console\Output\OutputInterface;
+
+class Manage extends Command {
+
+ const DEFAULT_BACKEND = 'owncloud';
+ const DEFAULT_LOG_LEVEL = 2;
+ const DEFAULT_TIMEZONE = 'UTC';
+
+ /** @var IConfig */
+ protected $config;
+
+ public function __construct(IConfig $config) {
+ $this->config = $config;
+ parent::__construct();
+ }
+
+ protected function configure() {
+ $this
+ ->setName('log:manage')
+ ->setDescription('manage logging configuration')
+ ->addOption(
+ 'backend',
+ null,
+ InputOption::VALUE_REQUIRED,
+ 'set the logging backend [owncloud, syslog, errorlog]'
+ )
+ ->addOption(
+ 'level',
+ null,
+ InputOption::VALUE_REQUIRED,
+ 'set the log level [debug, info, warning, error]'
+ )
+ ->addOption(
+ 'timezone',
+ null,
+ InputOption::VALUE_REQUIRED,
+ 'set the logging timezone'
+ )
+ ;
+ }
+
+ protected function execute(InputInterface $input, OutputInterface $output) {
+ // collate config setting to the end, to avoid partial configuration
+ $toBeSet = [];
+
+ if ($backend = $input->getOption('backend')) {
+ $this->validateBackend($backend);
+ $toBeSet['log_type'] = $backend;
+ }
+
+ if ($level = $input->getOption('level')) {
+ if (is_numeric($level)) {
+ $levelNum = $level;
+ // sanity check
+ $this->convertLevelNumber($levelNum);
+ } else {
+ $levelNum = $this->convertLevelString($level);
+ }
+ $toBeSet['loglevel'] = $levelNum;
+ }
+
+ if ($timezone = $input->getOption('timezone')) {
+ $this->validateTimezone($timezone);
+ $toBeSet['logtimezone'] = $timezone;
+ }
+
+ // set config
+ foreach ($toBeSet as $option => $value) {
+ $this->config->setSystemValue($option, $value);
+ }
+
+ // display configuration
+ $backend = $this->config->getSystemValue('log_type', self::DEFAULT_BACKEND);
+ $output->writeln('Enabled logging backend: '.$backend);
+
+ $levelNum = $this->config->getSystemValue('loglevel', self::DEFAULT_LOG_LEVEL);
+ $level = $this->convertLevelNumber($levelNum);
+ $output->writeln('Log level: '.$level.' ('.$levelNum.')');
+
+ $timezone = $this->config->getSystemValue('logtimezone', self::DEFAULT_TIMEZONE);
+ $output->writeln('Log timezone: '.$timezone);
+ }
+
+ /**
+ * @param string $backend
+ * @throws \InvalidArgumentException
+ */
+ protected function validateBackend($backend) {
+ if (!class_exists('OC_Log_'.$backend)) {
+ throw new \InvalidArgumentException('Invalid backend');
+ }
+ }
+
+ /**
+ * @param string $timezone
+ * @throws \Exception
+ */
+ protected function validateTimezone($timezone) {
+ new \DateTimeZone($timezone);
+ }
+
+ /**
+ * @param string $level
+ * @return int
+ * @throws \InvalidArgumentException
+ */
+ protected function convertLevelString($level) {
+ $level = strtolower($level);
+ switch ($level) {
+ case 'debug':
+ return 0;
+ case 'info':
+ return 1;
+ case 'warning':
+ case 'warn':
+ return 2;
+ case 'error':
+ case 'err':
+ return 3;
+ }
+ throw new \InvalidArgumentException('Invalid log level string');
+ }
+
+ /**
+ * @param int $levelNum
+ * @return string
+ * @throws \InvalidArgumentException
+ */
+ protected function convertLevelNumber($levelNum) {
+ switch ($levelNum) {
+ case 0:
+ return 'Debug';
+ case 1:
+ return 'Info';
+ case 2:
+ return 'Warning';
+ case 3:
+ return 'Error';
+ }
+ throw new \InvalidArgumentException('Invalid log level number');
+ }
+}
diff --git a/core/Command/Log/OwnCloud.php b/core/Command/Log/OwnCloud.php
new file mode 100644
index 00000000000..7213f6726a2
--- /dev/null
+++ b/core/Command/Log/OwnCloud.php
@@ -0,0 +1,124 @@
+<?php
+/**
+ * @author Robin McCorkell <robin@mccorkell.me.uk>
+ *
+ * @copyright Copyright (c) 2016, ownCloud, Inc.
+ * @license AGPL-3.0
+ *
+ * This code is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU Affero General Public License, version 3,
+ * as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU Affero General Public License for more details.
+ *
+ * You should have received a copy of the GNU Affero General Public License, version 3,
+ * along with this program. If not, see <http://www.gnu.org/licenses/>
+ *
+ */
+
+namespace OC\Core\Command\Log;
+
+use \OCP\IConfig;
+
+use Symfony\Component\Console\Command\Command;
+use Symfony\Component\Console\Input\InputInterface;
+use Symfony\Component\Console\Input\InputArgument;
+use Symfony\Component\Console\Input\InputOption;
+use Symfony\Component\Console\Output\OutputInterface;
+
+class OwnCloud extends Command {
+
+ /** @var IConfig */
+ protected $config;
+
+ public function __construct(IConfig $config) {
+ $this->config = $config;
+ parent::__construct();
+ }
+
+ protected function configure() {
+ $this
+ ->setName('log:owncloud')
+ ->setDescription('manipulate ownCloud logging backend')
+ ->addOption(
+ 'enable',
+ null,
+ InputOption::VALUE_NONE,
+ 'enable this logging backend'
+ )
+ ->addOption(
+ 'file',
+ null,
+ InputOption::VALUE_REQUIRED,
+ 'set the log file path'
+ )
+ ->addOption(
+ 'rotate-size',
+ null,
+ InputOption::VALUE_REQUIRED,
+ 'set the file size for log rotation, 0 = disabled'
+ )
+ ;
+ }
+
+ protected function execute(InputInterface $input, OutputInterface $output) {
+ $toBeSet = [];
+
+ if ($input->getOption('enable')) {
+ $toBeSet['log_type'] = 'owncloud';
+ }
+
+ if ($file = $input->getOption('file')) {
+ $toBeSet['logfile'] = $file;
+ }
+
+ if (($rotateSize = $input->getOption('rotate-size')) !== null) {
+ $rotateSize = \OCP\Util::computerFileSize($rotateSize);
+ $this->validateRotateSize($rotateSize);
+ $toBeSet['log_rotate_size'] = $rotateSize;
+ }
+
+ // set config
+ foreach ($toBeSet as $option => $value) {
+ $this->config->setSystemValue($option, $value);
+ }
+
+ // display config
+ if ($this->config->getSystemValue('log_type', 'owncloud') === 'owncloud') {
+ $enabledText = 'enabled';
+ } else {
+ $enabledText = 'disabled';
+ }
+ $output->writeln('Log backend ownCloud: '.$enabledText);
+
+ $dataDir = $this->config->getSystemValue('datadirectory', \OC::$SERVERROOT.'/data');
+ $defaultLogFile = rtrim($dataDir, '/').'/owncloud.log';
+ $output->writeln('Log file: '.$this->config->getSystemValue('logfile', $defaultLogFile));
+
+ $rotateSize = $this->config->getSystemValue('log_rotate_size', 0);
+ if ($rotateSize) {
+ $rotateString = \OCP\Util::humanFileSize($rotateSize);
+ } else {
+ $rotateString = 'disabled';
+ }
+ $output->writeln('Rotate at: '.$rotateString);
+ }
+
+ /**
+ * @param mixed $rotateSize
+ * @throws \InvalidArgumentException
+ */
+ protected function validateRotateSize(&$rotateSize) {
+ if ($rotateSize === false) {
+ throw new \InvalidArgumentException('Error parsing log rotation file size');
+ }
+ $rotateSize = (int) $rotateSize;
+ if ($rotateSize < 0) {
+ throw new \InvalidArgumentException('Log rotation file size must be non-negative');
+ }
+ }
+
+}
diff --git a/core/Command/Maintenance/Install.php b/core/Command/Maintenance/Install.php
new file mode 100644
index 00000000000..b1b63b9b3bd
--- /dev/null
+++ b/core/Command/Maintenance/Install.php
@@ -0,0 +1,178 @@
+<?php
+/**
+ * @author Bernhard Posselt <dev@bernhard-posselt.com>
+ * @author Christian Kampka <christian@kampka.net>
+ * @author Morris Jobke <hey@morrisjobke.de>
+ * @author Thomas Müller <thomas.mueller@tmit.eu>
+ *
+ * @copyright Copyright (c) 2016, ownCloud, Inc.
+ * @license AGPL-3.0
+ *
+ * This code is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU Affero General Public License, version 3,
+ * as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU Affero General Public License for more details.
+ *
+ * You should have received a copy of the GNU Affero General Public License, version 3,
+ * along with this program. If not, see <http://www.gnu.org/licenses/>
+ *
+ */
+namespace OC\Core\Command\Maintenance;
+
+use InvalidArgumentException;
+use OC\Setup;
+use OCP\IConfig;
+use Symfony\Component\Console\Command\Command;
+use Symfony\Component\Console\Input\InputInterface;
+use Symfony\Component\Console\Input\InputOption;
+use Symfony\Component\Console\Output\OutputInterface;
+
+class Install extends Command {
+
+ /**
+ * @var IConfig
+ */
+ private $config;
+
+ public function __construct(IConfig $config) {
+ parent::__construct();
+ $this->config = $config;
+ }
+
+ protected function configure() {
+ $this
+ ->setName('maintenance:install')
+ ->setDescription('install ownCloud')
+ ->addOption('database', null, InputOption::VALUE_REQUIRED, 'Supported database type', 'sqlite')
+ ->addOption('database-name', null, InputOption::VALUE_REQUIRED, 'Name of the database')
+ ->addOption('database-host', null, InputOption::VALUE_REQUIRED, 'Hostname of the database', 'localhost')
+ ->addOption('database-user', null, InputOption::VALUE_REQUIRED, 'User name to connect to the database')
+ ->addOption('database-pass', null, InputOption::VALUE_OPTIONAL, 'Password of the database user', null)
+ ->addOption('database-table-prefix', null, InputOption::VALUE_OPTIONAL, 'Prefix for all tables (default: oc_)', null)
+ ->addOption('admin-user', null, InputOption::VALUE_REQUIRED, 'User name of the admin account', 'admin')
+ ->addOption('admin-pass', null, InputOption::VALUE_REQUIRED, 'Password of the admin account')
+ ->addOption('data-dir', null, InputOption::VALUE_REQUIRED, 'Path to data directory', \OC::$SERVERROOT."/data");
+ }
+
+ protected function execute(InputInterface $input, OutputInterface $output) {
+
+ // validate the environment
+ $server = \OC::$server;
+ $setupHelper = new Setup($this->config, $server->getIniWrapper(),
+ $server->getL10N('lib'), new \OC_Defaults(), $server->getLogger(),
+ $server->getSecureRandom());
+ $sysInfo = $setupHelper->getSystemInfo(true);
+ $errors = $sysInfo['errors'];
+ if (count($errors) > 0) {
+ $this->printErrors($output, $errors);
+
+ // ignore the OS X setup warning
+ if(count($errors) !== 1 ||
+ (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! ') {
+ return 1;
+ }
+ }
+
+ // validate user input
+ $options = $this->validateInput($input, $output, array_keys($sysInfo['databases']));
+
+ // perform installation
+ $errors = $setupHelper->install($options);
+ if (count($errors) > 0) {
+ $this->printErrors($output, $errors);
+ return 1;
+ }
+ $output->writeln("ownCloud was successfully installed");
+ return 0;
+ }
+
+ /**
+ * @param InputInterface $input
+ * @param OutputInterface $output
+ * @param string[] $supportedDatabases
+ * @return array
+ */
+ protected function validateInput(InputInterface $input, OutputInterface $output, $supportedDatabases) {
+ $db = strtolower($input->getOption('database'));
+
+ if (!in_array($db, $supportedDatabases)) {
+ throw new InvalidArgumentException("Database <$db> is not supported.");
+ }
+
+ $dbUser = $input->getOption('database-user');
+ $dbPass = $input->getOption('database-pass');
+ $dbName = $input->getOption('database-name');
+ $dbHost = $input->getOption('database-host');
+ $dbTablePrefix = 'oc_';
+ if ($input->hasParameterOption('--database-table-prefix')) {
+ $dbTablePrefix = (string) $input->getOption('database-table-prefix');
+ $dbTablePrefix = trim($dbTablePrefix);
+ }
+ if ($input->hasParameterOption('--database-pass')) {
+ $dbPass = (string) $input->getOption('database-pass');
+ }
+ $adminLogin = $input->getOption('admin-user');
+ $adminPassword = $input->getOption('admin-pass');
+ $dataDir = $input->getOption('data-dir');
+
+ if ($db !== 'sqlite') {
+ if (is_null($dbUser)) {
+ throw new InvalidArgumentException("Database user not provided.");
+ }
+ if (is_null($dbName)) {
+ throw new InvalidArgumentException("Database name not provided.");
+ }
+ if (is_null($dbPass)) {
+ /** @var $dialog \Symfony\Component\Console\Helper\DialogHelper */
+ $dialog = $this->getHelperSet()->get('dialog');
+ $dbPass = $dialog->askHiddenResponse(
+ $output,
+ "<question>What is the password to access the database with user <$dbUser>?</question>",
+ false
+ );
+ }
+ }
+
+ if (is_null($adminPassword)) {
+ /** @var $dialog \Symfony\Component\Console\Helper\DialogHelper */
+ $dialog = $this->getHelperSet()->get('dialog');
+ $adminPassword = $dialog->askHiddenResponse(
+ $output,
+ "<question>What is the password you like to use for the admin account <$adminLogin>?</question>",
+ false
+ );
+ }
+
+ $options = [
+ 'dbtype' => $db,
+ 'dbuser' => $dbUser,
+ 'dbpass' => $dbPass,
+ 'dbname' => $dbName,
+ 'dbhost' => $dbHost,
+ 'dbtableprefix' => $dbTablePrefix,
+ 'adminlogin' => $adminLogin,
+ 'adminpass' => $adminPassword,
+ 'directory' => $dataDir
+ ];
+ return $options;
+ }
+
+ /**
+ * @param OutputInterface $output
+ * @param $errors
+ */
+ protected function printErrors(OutputInterface $output, $errors) {
+ foreach ($errors as $error) {
+ if (is_array($error)) {
+ $output->writeln('<error>' . (string)$error['error'] . '</error>');
+ $output->writeln('<info> -> ' . (string)$error['hint'] . '</info>');
+ } else {
+ $output->writeln('<error>' . (string)$error . '</error>');
+ }
+ }
+ }
+}
diff --git a/core/Command/Maintenance/Mimetype/UpdateDB.php b/core/Command/Maintenance/Mimetype/UpdateDB.php
new file mode 100644
index 00000000000..9532f9e1cd9
--- /dev/null
+++ b/core/Command/Maintenance/Mimetype/UpdateDB.php
@@ -0,0 +1,97 @@
+<?php
+/**
+ * @author Robin McCorkell <robin@mccorkell.me.uk>
+ *
+ * @copyright Copyright (c) 2016, ownCloud, Inc.
+ * @license AGPL-3.0
+ *
+ * This code is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU Affero General Public License, version 3,
+ * as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU Affero General Public License for more details.
+ *
+ * You should have received a copy of the GNU Affero General Public License, version 3,
+ * along with this program. If not, see <http://www.gnu.org/licenses/>
+ *
+ */
+
+namespace OC\Core\Command\Maintenance\Mimetype;
+
+use Symfony\Component\Console\Command\Command;
+use Symfony\Component\Console\Input\InputInterface;
+use Symfony\Component\Console\Output\OutputInterface;
+use Symfony\Component\Console\Input\InputOption;
+
+use OCP\Files\IMimeTypeDetector;
+use OCP\Files\IMimeTypeLoader;
+
+class UpdateDB extends Command {
+
+ const DEFAULT_MIMETYPE = 'application/octet-stream';
+
+ /** @var IMimeTypeDetector */
+ protected $mimetypeDetector;
+
+ /** @var IMimeTypeLoader */
+ protected $mimetypeLoader;
+
+ public function __construct(
+ IMimeTypeDetector $mimetypeDetector,
+ IMimeTypeLoader $mimetypeLoader
+ ) {
+ parent::__construct();
+ $this->mimetypeDetector = $mimetypeDetector;
+ $this->mimetypeLoader = $mimetypeLoader;
+ }
+
+ protected function configure() {
+ $this
+ ->setName('maintenance:mimetype:update-db')
+ ->setDescription('Update database mimetypes and update filecache')
+ ->addOption(
+ 'repair-filecache',
+ null,
+ InputOption::VALUE_NONE,
+ 'Repair filecache for all mimetypes, not just new ones'
+ )
+ ;
+ }
+
+ protected function execute(InputInterface $input, OutputInterface $output) {
+ $mappings = $this->mimetypeDetector->getAllMappings();
+
+ $totalFilecacheUpdates = 0;
+ $totalNewMimetypes = 0;
+
+ foreach ($mappings as $ext => $mimetypes) {
+ if ($ext[0] === '_') {
+ // comment
+ continue;
+ }
+ $mimetype = $mimetypes[0];
+ $existing = $this->mimetypeLoader->exists($mimetype);
+ // this will add the mimetype if it didn't exist
+ $mimetypeId = $this->mimetypeLoader->getId($mimetype);
+
+ if (!$existing) {
+ $output->writeln('Added mimetype "'.$mimetype.'" to database');
+ $totalNewMimetypes++;
+ }
+
+ if (!$existing || $input->getOption('repair-filecache')) {
+ $touchedFilecacheRows = $this->mimetypeLoader->updateFilecache($ext, $mimetypeId);
+ if ($touchedFilecacheRows > 0) {
+ $output->writeln('Updated '.$touchedFilecacheRows.' filecache rows for mimetype "'.$mimetype.'"');
+ }
+ $totalFilecacheUpdates += $touchedFilecacheRows;
+ }
+ }
+
+ $output->writeln('Added '.$totalNewMimetypes.' new mimetypes');
+ $output->writeln('Updated '.$totalFilecacheUpdates.' filecache rows');
+ }
+}
diff --git a/core/Command/Maintenance/Mimetype/UpdateJS.php b/core/Command/Maintenance/Mimetype/UpdateJS.php
new file mode 100644
index 00000000000..a87f50e32de
--- /dev/null
+++ b/core/Command/Maintenance/Mimetype/UpdateJS.php
@@ -0,0 +1,129 @@
+<?php
+/**
+ * @author Joas Schilling <nickvergessen@owncloud.com>
+ * @author Robin McCorkell <robin@mccorkell.me.uk>
+ * @author Roeland Jago Douma <rullzer@owncloud.com>
+ *
+ * @copyright Copyright (c) 2016, ownCloud, Inc.
+ * @license AGPL-3.0
+ *
+ * This code is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU Affero General Public License, version 3,
+ * as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU Affero General Public License for more details.
+ *
+ * You should have received a copy of the GNU Affero General Public License, version 3,
+ * along with this program. If not, see <http://www.gnu.org/licenses/>
+ *
+ */
+
+namespace OC\Core\Command\Maintenance\Mimetype;
+
+use Symfony\Component\Console\Command\Command;
+use Symfony\Component\Console\Input\InputInterface;
+use Symfony\Component\Console\Output\OutputInterface;
+
+use OCP\Files\IMimeTypeDetector;
+
+class UpdateJS extends Command {
+
+ /** @var IMimeTypeDetector */
+ protected $mimetypeDetector;
+
+ public function __construct(
+ IMimeTypeDetector $mimetypeDetector
+ ) {
+ parent::__construct();
+ $this->mimetypeDetector = $mimetypeDetector;
+ }
+
+ protected function configure() {
+ $this
+ ->setName('maintenance:mimetype:update-js')
+ ->setDescription('Update mimetypelist.js');
+ }
+
+ protected function execute(InputInterface $input, OutputInterface $output) {
+ // Fetch all the aliases
+ $aliases = $this->mimetypeDetector->getAllAliases();
+
+ // Remove comments
+ $keys = array_filter(array_keys($aliases), function($k) {
+ return $k[0] === '_';
+ });
+ foreach($keys as $key) {
+ unset($aliases[$key]);
+ }
+
+ // Fetch all files
+ $dir = new \DirectoryIterator(\OC::$SERVERROOT.'/core/img/filetypes');
+
+ $files = [];
+ foreach($dir as $fileInfo) {
+ if ($fileInfo->isFile()) {
+ $file = preg_replace('/.[^.]*$/', '', $fileInfo->getFilename());
+ $files[] = $file;
+ }
+ }
+
+ //Remove duplicates
+ $files = array_values(array_unique($files));
+ sort($files);
+
+ // Fetch all themes!
+ $themes = [];
+ $dirs = new \DirectoryIterator(\OC::$SERVERROOT.'/themes/');
+ foreach($dirs as $dir) {
+ //Valid theme dir
+ if ($dir->isFile() || $dir->isDot()) {
+ continue;
+ }
+
+ $theme = $dir->getFilename();
+ $themeDir = $dir->getPath() . '/' . $theme . '/core/img/filetypes/';
+ // Check if this theme has its own filetype icons
+ if (!file_exists($themeDir)) {
+ continue;
+ }
+
+ $themes[$theme] = [];
+ // Fetch all the theme icons!
+ $themeIt = new \DirectoryIterator($themeDir);
+ foreach ($themeIt as $fileInfo) {
+ if ($fileInfo->isFile()) {
+ $file = preg_replace('/.[^.]*$/', '', $fileInfo->getFilename());
+ $themes[$theme][] = $file;
+ }
+ }
+
+ //Remove Duplicates
+ $themes[$theme] = array_values(array_unique($themes[$theme]));
+ sort($themes[$theme]);
+ }
+
+ //Generate the JS
+ $js = '/**
+* This file is automatically generated
+* DO NOT EDIT MANUALLY!
+*
+* You can update the list of MimeType Aliases in config/mimetypealiases.json
+* The list of files is fetched from core/img/filetypes
+* To regenerate this file run ./occ maintenance:mimetypesjs
+*/
+OC.MimeTypeList={
+ aliases: ' . json_encode($aliases, JSON_PRETTY_PRINT | JSON_UNESCAPED_SLASHES) . ',
+ files: ' . json_encode($files, JSON_PRETTY_PRINT) . ',
+ themes: ' . json_encode($themes, JSON_PRETTY_PRINT) . '
+};
+';
+
+ //Output the JS
+ file_put_contents(\OC::$SERVERROOT.'/core/js/mimetypelist.js', $js);
+
+ $output->writeln('<info>mimetypelist.js is updated');
+ }
+}
diff --git a/core/Command/Maintenance/Mode.php b/core/Command/Maintenance/Mode.php
new file mode 100644
index 00000000000..28f4fb2f7f1
--- /dev/null
+++ b/core/Command/Maintenance/Mode.php
@@ -0,0 +1,75 @@
+<?php
+/**
+ * @author Morris Jobke <hey@morrisjobke.de>
+ * @author scolebrook <scolebrook@mac.com>
+ *
+ * @copyright Copyright (c) 2016, ownCloud, Inc.
+ * @license AGPL-3.0
+ *
+ * This code is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU Affero General Public License, version 3,
+ * as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU Affero General Public License for more details.
+ *
+ * You should have received a copy of the GNU Affero General Public License, version 3,
+ * along with this program. If not, see <http://www.gnu.org/licenses/>
+ *
+ */
+
+namespace OC\Core\Command\Maintenance;
+
+use \OCP\IConfig;
+
+use Symfony\Component\Console\Command\Command;
+use Symfony\Component\Console\Input\InputInterface;
+use Symfony\Component\Console\Input\InputOption;
+use Symfony\Component\Console\Output\OutputInterface;
+
+class Mode extends Command {
+
+ /** @var IConfig */
+ protected $config;
+
+ public function __construct(IConfig $config) {
+ $this->config = $config;
+ parent::__construct();
+ }
+
+ protected function configure() {
+ $this
+ ->setName('maintenance:mode')
+ ->setDescription('set maintenance mode')
+ ->addOption(
+ 'on',
+ null,
+ InputOption::VALUE_NONE,
+ 'enable maintenance mode'
+ )
+ ->addOption(
+ 'off',
+ null,
+ InputOption::VALUE_NONE,
+ 'disable maintenance mode'
+ );
+ }
+
+ protected function execute(InputInterface $input, OutputInterface $output) {
+ if ($input->getOption('on')) {
+ $this->config->setSystemValue('maintenance', true);
+ $output->writeln('Maintenance mode enabled');
+ } elseif ($input->getOption('off')) {
+ $this->config->setSystemValue('maintenance', false);
+ $output->writeln('Maintenance mode disabled');
+ } else {
+ if ($this->config->getSystemValue('maintenance', false)) {
+ $output->writeln('Maintenance mode is currently enabled');
+ } else {
+ $output->writeln('Maintenance mode is currently disabled');
+ }
+ }
+ }
+}
diff --git a/core/Command/Maintenance/Repair.php b/core/Command/Maintenance/Repair.php
new file mode 100644
index 00000000000..95e2b872227
--- /dev/null
+++ b/core/Command/Maintenance/Repair.php
@@ -0,0 +1,91 @@
+<?php
+/**
+ * @author Joas Schilling <nickvergessen@owncloud.com>
+ * @author Morris Jobke <hey@morrisjobke.de>
+ * @author Robin Appelman <icewind@owncloud.com>
+ * @author Vincent Petry <pvince81@owncloud.com>
+ *
+ * @copyright Copyright (c) 2016, ownCloud, Inc.
+ * @license AGPL-3.0
+ *
+ * This code is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU Affero General Public License, version 3,
+ * as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU Affero General Public License for more details.
+ *
+ * You should have received a copy of the GNU Affero General Public License, version 3,
+ * along with this program. If not, see <http://www.gnu.org/licenses/>
+ *
+ */
+
+namespace OC\Core\Command\Maintenance;
+
+use Symfony\Component\Console\Command\Command;
+use Symfony\Component\Console\Input\InputInterface;
+use Symfony\Component\Console\Input\InputOption;
+use Symfony\Component\Console\Output\OutputInterface;
+
+class Repair extends Command {
+ /**
+ * @var \OC\Repair $repair
+ */
+ protected $repair;
+ /** @var \OCP\IConfig */
+ protected $config;
+
+ /**
+ * @param \OC\Repair $repair
+ * @param \OCP\IConfig $config
+ */
+ public function __construct(\OC\Repair $repair, \OCP\IConfig $config) {
+ $this->repair = $repair;
+ $this->config = $config;
+ parent::__construct();
+ }
+
+ protected function configure() {
+ $this
+ ->setName('maintenance:repair')
+ ->setDescription('repair this installation')
+ ->addOption(
+ 'include-expensive',
+ null,
+ InputOption::VALUE_NONE,
+ 'Use this option when you want to include resource and load expensive tasks'
+ )
+ ;
+ }
+
+ protected function execute(InputInterface $input, OutputInterface $output) {
+ $includeExpensive = $input->getOption('include-expensive');
+ if ($includeExpensive) {
+ foreach ($this->repair->getExpensiveRepairSteps() as $step) {
+ $this->repair->addStep($step);
+ }
+ }
+
+ $maintenanceMode = $this->config->getSystemValue('maintenance', false);
+ $this->config->setSystemValue('maintenance', true);
+
+ $this->repair->listen('\OC\Repair', 'step', function ($description) use ($output) {
+ $output->writeln(' - ' . $description);
+ });
+ $this->repair->listen('\OC\Repair', 'info', function ($description) use ($output) {
+ $output->writeln(' - ' . $description);
+ });
+ $this->repair->listen('\OC\Repair', 'warning', function ($description) use ($output) {
+ $output->writeln(' - WARNING: ' . $description);
+ });
+ $this->repair->listen('\OC\Repair', 'error', function ($description) use ($output) {
+ $output->writeln(' - ERROR: ' . $description);
+ });
+
+ $this->repair->run();
+
+ $this->config->setSystemValue('maintenance', $maintenanceMode);
+ }
+}
diff --git a/core/Command/Maintenance/SingleUser.php b/core/Command/Maintenance/SingleUser.php
new file mode 100644
index 00000000000..2e6f1f136e7
--- /dev/null
+++ b/core/Command/Maintenance/SingleUser.php
@@ -0,0 +1,78 @@
+<?php
+/**
+ * @author Morris Jobke <hey@morrisjobke.de>
+ * @author Robin Appelman <icewind@owncloud.com>
+ *
+ * @copyright Copyright (c) 2016, ownCloud, Inc.
+ * @license AGPL-3.0
+ *
+ * This code is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU Affero General Public License, version 3,
+ * as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU Affero General Public License for more details.
+ *
+ * You should have received a copy of the GNU Affero General Public License, version 3,
+ * along with this program. If not, see <http://www.gnu.org/licenses/>
+ *
+ */
+
+namespace OC\Core\Command\Maintenance;
+
+use Symfony\Component\Console\Command\Command;
+use Symfony\Component\Console\Input\InputInterface;
+use Symfony\Component\Console\Input\InputOption;
+use Symfony\Component\Console\Output\OutputInterface;
+
+use OCP\IConfig;
+
+class SingleUser extends Command {
+
+ /** @var IConfig */
+ protected $config;
+
+ /**
+ * @param IConfig $config
+ */
+ public function __construct(IConfig $config) {
+ $this->config = $config;
+ parent::__construct();
+ }
+
+ protected function configure() {
+ $this
+ ->setName('maintenance:singleuser')
+ ->setDescription('set single user mode')
+ ->addOption(
+ 'on',
+ null,
+ InputOption::VALUE_NONE,
+ 'enable single user mode'
+ )
+ ->addOption(
+ 'off',
+ null,
+ InputOption::VALUE_NONE,
+ 'disable single user mode'
+ );
+ }
+
+ protected function execute(InputInterface $input, OutputInterface $output) {
+ if ($input->getOption('on')) {
+ $this->config->setSystemValue('singleuser', true);
+ $output->writeln('Single user mode enabled');
+ } elseif ($input->getOption('off')) {
+ $this->config->setSystemValue('singleuser', false);
+ $output->writeln('Single user mode disabled');
+ } else {
+ if ($this->config->getSystemValue('singleuser', false)) {
+ $output->writeln('Single user mode is currently enabled');
+ } else {
+ $output->writeln('Single user mode is currently disabled');
+ }
+ }
+ }
+}
diff --git a/core/Command/Security/ImportCertificate.php b/core/Command/Security/ImportCertificate.php
new file mode 100644
index 00000000000..6aae7ad1d9f
--- /dev/null
+++ b/core/Command/Security/ImportCertificate.php
@@ -0,0 +1,67 @@
+<?php
+/**
+ * @author Robin Appelman <icewind@owncloud.com>
+ *
+ * @copyright Copyright (c) 2016, ownCloud, Inc.
+ * @license AGPL-3.0
+ *
+ * This code is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU Affero General Public License, version 3,
+ * as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU Affero General Public License for more details.
+ *
+ * You should have received a copy of the GNU Affero General Public License, version 3,
+ * along with this program. If not, see <http://www.gnu.org/licenses/>
+ *
+ */
+
+namespace OC\Core\Command\Security;
+
+use OC\Core\Command\Base;
+use OCP\ICertificateManager;
+use Symfony\Component\Console\Command\Command;
+use Symfony\Component\Console\Helper\Table;
+use Symfony\Component\Console\Input\InputArgument;
+use Symfony\Component\Console\Input\InputInterface;
+use Symfony\Component\Console\Input\InputOption;
+use Symfony\Component\Console\Output\OutputInterface;
+
+class ImportCertificate extends Base {
+
+ /** @var ICertificateManager */
+ protected $certificateManager;
+
+ public function __construct(ICertificateManager $certificateManager) {
+ $this->certificateManager = $certificateManager;
+ parent::__construct();
+ }
+
+ protected function configure() {
+ $this
+ ->setName('security:certificates:import')
+ ->setDescription('import trusted certificate')
+ ->addArgument(
+ 'path',
+ InputArgument::REQUIRED,
+ 'path to the certificate to import'
+ );
+ }
+
+ protected function execute(InputInterface $input, OutputInterface $output) {
+ $path = $input->getArgument('path');
+
+ if (!file_exists($path)) {
+ $output->writeln('<error>certificate not found</error>');
+ return;
+ }
+
+ $certData = file_get_contents($path);
+ $name = basename($path);
+
+ $this->certificateManager->addCertificate($certData, $name);
+ }
+}
diff --git a/core/Command/Security/ListCertificates.php b/core/Command/Security/ListCertificates.php
new file mode 100644
index 00000000000..91deb2d340a
--- /dev/null
+++ b/core/Command/Security/ListCertificates.php
@@ -0,0 +1,96 @@
+<?php
+/**
+ * @author Robin Appelman <icewind@owncloud.com>
+ *
+ * @copyright Copyright (c) 2016, ownCloud, Inc.
+ * @license AGPL-3.0
+ *
+ * This code is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU Affero General Public License, version 3,
+ * as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU Affero General Public License for more details.
+ *
+ * You should have received a copy of the GNU Affero General Public License, version 3,
+ * along with this program. If not, see <http://www.gnu.org/licenses/>
+ *
+ */
+
+namespace OC\Core\Command\Security;
+
+use OC\Core\Command\Base;
+use OCP\ICertificate;
+use OCP\ICertificateManager;
+use OCP\IL10N;
+use Symfony\Component\Console\Command\Command;
+use Symfony\Component\Console\Helper\Table;
+use Symfony\Component\Console\Input\InputInterface;
+use Symfony\Component\Console\Input\InputOption;
+use Symfony\Component\Console\Output\OutputInterface;
+
+class ListCertificates extends Base {
+
+ /** @var ICertificateManager */
+ protected $certificateManager;
+ /** @var IL10N */
+ protected $l;
+
+ public function __construct(ICertificateManager $certificateManager, IL10N $l) {
+ $this->certificateManager = $certificateManager;
+ $this->l = $l;
+ parent::__construct();
+ }
+
+ protected function configure() {
+ $this
+ ->setName('security:certificates')
+ ->setDescription('list trusted certificates');
+ parent::configure();
+ }
+
+ protected function execute(InputInterface $input, OutputInterface $output) {
+ $outputType = $input->getOption('output');
+ if ($outputType === self::OUTPUT_FORMAT_JSON || $outputType === self::OUTPUT_FORMAT_JSON_PRETTY) {
+ $certificates = array_map(function (ICertificate $certificate) {
+ return [
+ 'name' => $certificate->getName(),
+ 'common_name' => $certificate->getCommonName(),
+ 'organization' => $certificate->getOrganization(),
+ 'expire' => $certificate->getExpireDate()->format(\DateTime::ATOM),
+ 'issuer' => $certificate->getIssuerName(),
+ 'issuer_organization' => $certificate->getIssuerOrganization(),
+ 'issue_date' => $certificate->getIssueDate()->format(\DateTime::ATOM)
+ ];
+ }, $this->certificateManager->listCertificates());
+ if ($outputType === self::OUTPUT_FORMAT_JSON) {
+ $output->writeln(json_encode(array_values($certificates)));
+ } else {
+ $output->writeln(json_encode(array_values($certificates), JSON_PRETTY_PRINT));
+ }
+ } else {
+ $table = new Table($output);
+ $table->setHeaders([
+ 'File Name',
+ 'Common Name',
+ 'Organization',
+ 'Valid Until',
+ 'Issued By'
+ ]);
+
+ $rows = array_map(function (ICertificate $certificate) {
+ return [
+ $certificate->getName(),
+ $certificate->getCommonName(),
+ $certificate->getOrganization(),
+ $this->l->l('date', $certificate->getExpireDate()),
+ $certificate->getIssuerName()
+ ];
+ }, $this->certificateManager->listCertificates());
+ $table->setRows($rows);
+ $table->render();
+ }
+ }
+}
diff --git a/core/Command/Security/RemoveCertificate.php b/core/Command/Security/RemoveCertificate.php
new file mode 100644
index 00000000000..68e409aee1c
--- /dev/null
+++ b/core/Command/Security/RemoveCertificate.php
@@ -0,0 +1,59 @@
+<?php
+/**
+ * @author Robin Appelman <icewind@owncloud.com>
+ *
+ * @copyright Copyright (c) 2016, ownCloud, Inc.
+ * @license AGPL-3.0
+ *
+ * This code is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU Affero General Public License, version 3,
+ * as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU Affero General Public License for more details.
+ *
+ * You should have received a copy of the GNU Affero General Public License, version 3,
+ * along with this program. If not, see <http://www.gnu.org/licenses/>
+ *
+ */
+
+namespace OC\Core\Command\Security;
+
+use OC\Core\Command\Base;
+use OCP\ICertificateManager;
+use Symfony\Component\Console\Command\Command;
+use Symfony\Component\Console\Helper\Table;
+use Symfony\Component\Console\Input\InputArgument;
+use Symfony\Component\Console\Input\InputInterface;
+use Symfony\Component\Console\Input\InputOption;
+use Symfony\Component\Console\Output\OutputInterface;
+
+class RemoveCertificate extends Base {
+
+ /** @var ICertificateManager */
+ protected $certificateManager;
+
+ public function __construct(ICertificateManager $certificateManager) {
+ $this->certificateManager = $certificateManager;
+ parent::__construct();
+ }
+
+ protected function configure() {
+ $this
+ ->setName('security:certificates:remove')
+ ->setDescription('remove trusted certificate')
+ ->addArgument(
+ 'name',
+ InputArgument::REQUIRED,
+ 'the file name of the certificate to remove'
+ );
+ }
+
+ protected function execute(InputInterface $input, OutputInterface $output) {
+ $name = $input->getArgument('name');
+
+ $this->certificateManager->removeCertificate($name);
+ }
+}
diff --git a/core/Command/Status.php b/core/Command/Status.php
new file mode 100644
index 00000000000..6bc9c28d4d1
--- /dev/null
+++ b/core/Command/Status.php
@@ -0,0 +1,49 @@
+<?php
+/**
+ * @author Bart Visscher <bartv@thisnet.nl>
+ * @author Joas Schilling <nickvergessen@owncloud.com>
+ * @author Morris Jobke <hey@morrisjobke.de>
+ *
+ * @copyright Copyright (c) 2016, ownCloud, Inc.
+ * @license AGPL-3.0
+ *
+ * This code is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU Affero General Public License, version 3,
+ * as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU Affero General Public License for more details.
+ *
+ * You should have received a copy of the GNU Affero General Public License, version 3,
+ * along with this program. If not, see <http://www.gnu.org/licenses/>
+ *
+ */
+
+namespace OC\Core\Command;
+
+use Symfony\Component\Console\Input\InputInterface;
+use Symfony\Component\Console\Output\OutputInterface;
+
+class Status extends Base {
+ protected function configure() {
+ parent::configure();
+
+ $this
+ ->setName('status')
+ ->setDescription('show some status information')
+ ;
+ }
+
+ protected function execute(InputInterface $input, OutputInterface $output) {
+ $values = array(
+ 'installed' => (bool) \OC::$server->getConfig()->getSystemValue('installed', false),
+ 'version' => implode('.', \OCP\Util::getVersion()),
+ 'versionstring' => \OC_Util::getVersionString(),
+ 'edition' => \OC_Util::getEditionString(),
+ );
+
+ $this->writeArrayInOutputFormat($input, $output, $values);
+ }
+}
diff --git a/core/Command/Upgrade.php b/core/Command/Upgrade.php
new file mode 100644
index 00000000000..cbb1f26f938
--- /dev/null
+++ b/core/Command/Upgrade.php
@@ -0,0 +1,295 @@
+<?php
+/**
+ * @author Andreas Fischer <bantu@owncloud.com>
+ * @author Björn Schießle <schiessle@owncloud.com>
+ * @author Joas Schilling <nickvergessen@owncloud.com>
+ * @author Lukas Reschke <lukas@owncloud.com>
+ * @author Morris Jobke <hey@morrisjobke.de>
+ * @author Owen Winkler <a_github@midnightcircus.com>
+ * @author Steffen Lindner <mail@steffen-lindner.de>
+ * @author Thomas Müller <thomas.mueller@tmit.eu>
+ * @author Vincent Petry <pvince81@owncloud.com>
+ *
+ * @copyright Copyright (c) 2016, ownCloud, Inc.
+ * @license AGPL-3.0
+ *
+ * This code is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU Affero General Public License, version 3,
+ * as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU Affero General Public License for more details.
+ *
+ * You should have received a copy of the GNU Affero General Public License, version 3,
+ * along with this program. If not, see <http://www.gnu.org/licenses/>
+ *
+ */
+
+namespace OC\Core\Command;
+
+use OC\Console\TimestampFormatter;
+use OC\Updater;
+use OCP\IConfig;
+use OCP\ILogger;
+use Symfony\Component\Console\Command\Command;
+use Symfony\Component\Console\Helper\ProgressBar;
+use Symfony\Component\Console\Input\InputInterface;
+use Symfony\Component\Console\Output\OutputInterface;
+use Symfony\Component\Console\Input\InputOption;
+use Symfony\Component\EventDispatcher\GenericEvent;
+
+class Upgrade extends Command {
+
+ const ERROR_SUCCESS = 0;
+ const ERROR_NOT_INSTALLED = 1;
+ const ERROR_MAINTENANCE_MODE = 2;
+ const ERROR_UP_TO_DATE = 3;
+ const ERROR_INVALID_ARGUMENTS = 4;
+ const ERROR_FAILURE = 5;
+
+ /** @var IConfig */
+ private $config;
+
+ /** @var ILogger */
+ private $logger;
+
+ /**
+ * @param IConfig $config
+ * @param ILogger $logger
+ */
+ public function __construct(IConfig $config, ILogger $logger) {
+ parent::__construct();
+ $this->config = $config;
+ $this->logger = $logger;
+ }
+
+ protected function configure() {
+ $this
+ ->setName('upgrade')
+ ->setDescription('run upgrade routines after installation of a new release. The release has to be installed before.')
+ ->addOption(
+ '--skip-migration-test',
+ null,
+ InputOption::VALUE_NONE,
+ 'skips the database schema migration simulation and update directly'
+ )
+ ->addOption(
+ '--dry-run',
+ null,
+ InputOption::VALUE_NONE,
+ 'only runs the database schema migration simulation, do not actually update'
+ )
+ ->addOption(
+ '--no-app-disable',
+ null,
+ InputOption::VALUE_NONE,
+ 'skips the disable of third party apps'
+ );
+ }
+
+ /**
+ * Execute the upgrade command
+ *
+ * @param InputInterface $input input interface
+ * @param OutputInterface $output output interface
+ */
+ protected function execute(InputInterface $input, OutputInterface $output) {
+
+ $simulateStepEnabled = true;
+ $updateStepEnabled = true;
+ $skip3rdPartyAppsDisable = false;
+
+ if ($input->getOption('skip-migration-test')) {
+ $simulateStepEnabled = false;
+ }
+ if ($input->getOption('dry-run')) {
+ $updateStepEnabled = false;
+ }
+ if ($input->getOption('no-app-disable')) {
+ $skip3rdPartyAppsDisable = true;
+ }
+
+ if (!$simulateStepEnabled && !$updateStepEnabled) {
+ $output->writeln(
+ '<error>Only one of "--skip-migration-test" or "--dry-run" ' .
+ 'can be specified at a time.</error>'
+ );
+ return self::ERROR_INVALID_ARGUMENTS;
+ }
+
+ if(\OC::checkUpgrade(false)) {
+ if (OutputInterface::VERBOSITY_NORMAL < $output->getVerbosity()) {
+ // Prepend each line with a little timestamp
+ $timestampFormatter = new TimestampFormatter($this->config, $output->getFormatter());
+ $output->setFormatter($timestampFormatter);
+ }
+
+ $self = $this;
+ $updater = new Updater(
+ \OC::$server->getHTTPHelper(),
+ $this->config,
+ \OC::$server->getIntegrityCodeChecker(),
+ $this->logger
+ );
+
+ $updater->setSimulateStepEnabled($simulateStepEnabled);
+ $updater->setUpdateStepEnabled($updateStepEnabled);
+ $updater->setSkip3rdPartyAppsDisable($skip3rdPartyAppsDisable);
+ $dispatcher = \OC::$server->getEventDispatcher();
+ $progress = new ProgressBar($output);
+ $progress->setFormat(" %message%\n %current%/%max% [%bar%] %percent:3s%%");
+ $listener = function($event) use ($progress, $output) {
+ if ($event instanceof GenericEvent) {
+ $message = $event->getSubject();
+ if (OutputInterface::VERBOSITY_NORMAL < $output->getVerbosity()) {
+ $output->writeln(' Checking table ' . $message);
+ } else {
+ if (strlen($message) > 60) {
+ $message = substr($message, 0, 57) . '...';
+ }
+ $progress->setMessage($message);
+ if ($event[0] === 1) {
+ $output->writeln('');
+ $progress->start($event[1]);
+ }
+ $progress->setProgress($event[0]);
+ if ($event[0] === $event[1]) {
+ $progress->setMessage('Done');
+ $progress->finish();
+ $output->writeln('');
+ }
+ }
+ }
+ };
+ $dispatcher->addListener('\OC\DB\Migrator::executeSql', $listener);
+ $dispatcher->addListener('\OC\DB\Migrator::checkTable', $listener);
+
+ $updater->listen('\OC\Updater', 'maintenanceEnabled', function () use($output) {
+ $output->writeln('<info>Turned on maintenance mode</info>');
+ });
+ $updater->listen('\OC\Updater', 'maintenanceDisabled', function () use($output) {
+ $output->writeln('<info>Turned off maintenance mode</info>');
+ });
+ $updater->listen('\OC\Updater', 'maintenanceActive', function () use($output) {
+ $output->writeln('<info>Maintenance mode is kept active</info>');
+ });
+ $updater->listen('\OC\Updater', 'updateEnd',
+ function ($success) use($output, $updateStepEnabled, $self) {
+ $mode = $updateStepEnabled ? 'Update' : 'Update simulation';
+ if ($success) {
+ $message = "<info>$mode successful</info>";
+ } else {
+ $message = "<error>$mode failed</error>";
+ }
+ $output->writeln($message);
+ });
+ $updater->listen('\OC\Updater', 'dbUpgradeBefore', function () use($output) {
+ $output->writeln('<info>Updating database schema</info>');
+ });
+ $updater->listen('\OC\Updater', 'dbUpgrade', function () use($output) {
+ $output->writeln('<info>Updated database</info>');
+ });
+ $updater->listen('\OC\Updater', 'dbSimulateUpgradeBefore', function () use($output) {
+ $output->writeln('<info>Checking whether the database schema can be updated (this can take a long time depending on the database size)</info>');
+ });
+ $updater->listen('\OC\Updater', 'dbSimulateUpgrade', function () use($output) {
+ $output->writeln('<info>Checked database schema update</info>');
+ });
+ $updater->listen('\OC\Updater', 'incompatibleAppDisabled', function ($app) use($output) {
+ $output->writeln('<info>Disabled incompatible app: ' . $app . '</info>');
+ });
+ $updater->listen('\OC\Updater', 'thirdPartyAppDisabled', function ($app) use ($output) {
+ $output->writeln('<info>Disabled 3rd-party app: ' . $app . '</info>');
+ });
+ $updater->listen('\OC\Updater', 'upgradeAppStoreApp', function ($app) use($output) {
+ $output->writeln('<info>Update 3rd-party app: ' . $app . '</info>');
+ });
+ $updater->listen('\OC\Updater', 'repairWarning', function ($app) use($output) {
+ $output->writeln('<error>Repair warning: ' . $app . '</error>');
+ });
+ $updater->listen('\OC\Updater', 'repairError', function ($app) use($output) {
+ $output->writeln('<error>Repair error: ' . $app . '</error>');
+ });
+ $updater->listen('\OC\Updater', 'appUpgradeCheckBefore', function () use ($output) {
+ $output->writeln('<info>Checking updates of apps</info>');
+ });
+ $updater->listen('\OC\Updater', 'appSimulateUpdate', function ($app) use ($output) {
+ $output->writeln("<info>Checking whether the database schema for <$app> can be updated (this can take a long time depending on the database size)</info>");
+ });
+ $updater->listen('\OC\Updater', 'appUpgradeCheck', function () use ($output) {
+ $output->writeln('<info>Checked database schema update for apps</info>');
+ });
+ $updater->listen('\OC\Updater', 'appUpgradeStarted', function ($app, $version) use ($output) {
+ $output->writeln("<info>Updating <$app> ...</info>");
+ });
+ $updater->listen('\OC\Updater', 'appUpgrade', function ($app, $version) use ($output) {
+ $output->writeln("<info>Updated <$app> to $version</info>");
+ });
+ $updater->listen('\OC\Updater', 'failure', function ($message) use($output, $self) {
+ $output->writeln("<error>$message</error>");
+ });
+ $updater->listen('\OC\Updater', 'setDebugLogLevel', function ($logLevel, $logLevelName) use($output) {
+ $output->writeln("<info>Set log level to debug</info>");
+ });
+ $updater->listen('\OC\Updater', 'resetLogLevel', function ($logLevel, $logLevelName) use($output) {
+ $output->writeln("<info>Reset log level</info>");
+ });
+ $updater->listen('\OC\Updater', 'startCheckCodeIntegrity', function () use($output) {
+ $output->writeln("<info>Starting code integrity check...</info>");
+ });
+ $updater->listen('\OC\Updater', 'finishedCheckCodeIntegrity', function () use($output) {
+ $output->writeln("<info>Finished code integrity check</info>");
+ });
+
+ if(OutputInterface::VERBOSITY_NORMAL < $output->getVerbosity()) {
+ $updater->listen('\OC\Updater', 'repairInfo', function ($message) use($output) {
+ $output->writeln('<info>Repair info: ' . $message . '</info>');
+ });
+ $updater->listen('\OC\Updater', 'repairStep', function ($message) use($output) {
+ $output->writeln('<info>Repair step: ' . $message . '</info>');
+ });
+ }
+
+ $success = $updater->upgrade();
+
+ $this->postUpgradeCheck($input, $output);
+
+ if(!$success) {
+ return self::ERROR_FAILURE;
+ }
+
+ return self::ERROR_SUCCESS;
+ } else if($this->config->getSystemValue('maintenance', false)) {
+ //Possible scenario: ownCloud core is updated but an app failed
+ $output->writeln('<warning>ownCloud is in maintenance mode</warning>');
+ $output->write('<comment>Maybe an upgrade is already in process. Please check the '
+ . 'logfile (data/owncloud.log). If you want to re-run the '
+ . 'upgrade procedure, remove the "maintenance mode" from '
+ . 'config.php and call this script again.</comment>'
+ , true);
+ return self::ERROR_MAINTENANCE_MODE;
+ } else {
+ $output->writeln('<info>ownCloud is already latest version</info>');
+ return self::ERROR_UP_TO_DATE;
+ }
+ }
+
+ /**
+ * Perform a post upgrade check (specific to the command line tool)
+ *
+ * @param InputInterface $input input interface
+ * @param OutputInterface $output output interface
+ */
+ protected function postUpgradeCheck(InputInterface $input, OutputInterface $output) {
+ $trustedDomains = $this->config->getSystemValue('trusted_domains', array());
+ if (empty($trustedDomains)) {
+ $output->write(
+ '<warning>The setting "trusted_domains" could not be ' .
+ 'set automatically by the upgrade script, ' .
+ 'please set it manually</warning>'
+ );
+ }
+ }
+}
diff --git a/core/Command/User/Add.php b/core/Command/User/Add.php
new file mode 100644
index 00000000000..6c7e3a47231
--- /dev/null
+++ b/core/Command/User/Add.php
@@ -0,0 +1,154 @@
+<?php
+/**
+ * @author Joas Schilling <nickvergessen@owncloud.com>
+ * @author Laurens Post <lkpost@scept.re>
+ *
+ * @copyright Copyright (c) 2016, ownCloud, Inc.
+ * @license AGPL-3.0
+ *
+ * This code is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU Affero General Public License, version 3,
+ * as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU Affero General Public License for more details.
+ *
+ * You should have received a copy of the GNU Affero General Public License, version 3,
+ * along with this program. If not, see <http://www.gnu.org/licenses/>
+ *
+ */
+
+namespace OC\Core\Command\User;
+
+use OC\Files\Filesystem;
+use OCP\IGroupManager;
+use OCP\IUser;
+use OCP\IUserManager;
+use Symfony\Component\Console\Command\Command;
+use Symfony\Component\Console\Input\InputInterface;
+use Symfony\Component\Console\Input\InputOption;
+use Symfony\Component\Console\Output\OutputInterface;
+use Symfony\Component\Console\Input\InputArgument;
+use Symfony\Component\Console\Question\Question;
+
+class Add extends Command {
+ /** @var \OCP\IUserManager */
+ protected $userManager;
+
+ /** @var \OCP\IGroupManager */
+ protected $groupManager;
+
+ /**
+ * @param IUserManager $userManager
+ * @param IGroupManager $groupManager
+ */
+ public function __construct(IUserManager $userManager, IGroupManager $groupManager) {
+ parent::__construct();
+ $this->userManager = $userManager;
+ $this->groupManager = $groupManager;
+ }
+
+ protected function configure() {
+ $this
+ ->setName('user:add')
+ ->setDescription('adds a user')
+ ->addArgument(
+ 'uid',
+ InputArgument::REQUIRED,
+ 'User ID used to login (must only contain a-z, A-Z, 0-9, -, _ and @)'
+ )
+ ->addOption(
+ 'password-from-env',
+ null,
+ InputOption::VALUE_NONE,
+ 'read password from environment variable OC_PASS'
+ )
+ ->addOption(
+ 'display-name',
+ null,
+ InputOption::VALUE_OPTIONAL,
+ 'User name used in the web UI (can contain any characters)'
+ )
+ ->addOption(
+ 'group',
+ 'g',
+ InputOption::VALUE_OPTIONAL | InputOption::VALUE_IS_ARRAY,
+ 'groups the user should be added to (The group will be created if it does not exist)'
+ );
+ }
+
+ protected function execute(InputInterface $input, OutputInterface $output) {
+ $uid = $input->getArgument('uid');
+ if ($this->userManager->userExists($uid)) {
+ $output->writeln('<error>The user "' . $uid . '" already exists.</error>');
+ return 1;
+ }
+
+ if ($input->getOption('password-from-env')) {
+ $password = getenv('OC_PASS');
+ if (!$password) {
+ $output->writeln('<error>--password-from-env given, but OC_PASS is empty!</error>');
+ return 1;
+ }
+ } elseif ($input->isInteractive()) {
+ /** @var $dialog \Symfony\Component\Console\Helper\DialogHelper */
+ $dialog = $this->getHelperSet()->get('dialog');
+ $password = $dialog->askHiddenResponse(
+ $output,
+ '<question>Enter password: </question>',
+ false
+ );
+ $confirm = $dialog->askHiddenResponse(
+ $output,
+ '<question>Confirm password: </question>',
+ false
+ );
+
+ if ($password !== $confirm) {
+ $output->writeln("<error>Passwords did not match!</error>");
+ return 1;
+ }
+ } else {
+ $output->writeln("<error>Interactive input or --password-from-env is needed for entering a password!</error>");
+ return 1;
+ }
+
+ $user = $this->userManager->createUser(
+ $input->getArgument('uid'),
+ $password
+ );
+
+ if ($user instanceof IUser) {
+ $output->writeln('<info>The user "' . $user->getUID() . '" was created successfully</info>');
+ } else {
+ $output->writeln('<error>An error occurred while creating the user</error>');
+ return 1;
+ }
+
+ if ($input->getOption('display-name')) {
+ $user->setDisplayName($input->getOption('display-name'));
+ $output->writeln('Display name set to "' . $user->getDisplayName() . '"');
+ }
+
+ $groups = $input->getOption('group');
+
+ if (!empty($groups)) {
+ // Make sure we init the Filesystem for the user, in case we need to
+ // init some group shares.
+ Filesystem::init($user->getUID(), '');
+ }
+
+ foreach ($groups as $groupName) {
+ $group = $this->groupManager->get($groupName);
+ if (!$group) {
+ $this->groupManager->createGroup($groupName);
+ $group = $this->groupManager->get($groupName);
+ $output->writeln('Created group "' . $group->getGID() . '"');
+ }
+ $group->addUser($user);
+ $output->writeln('User "' . $user->getUID() . '" added to group "' . $group->getGID() . '"');
+ }
+ }
+}
diff --git a/core/Command/User/Delete.php b/core/Command/User/Delete.php
new file mode 100644
index 00000000000..b9a0a0e3950
--- /dev/null
+++ b/core/Command/User/Delete.php
@@ -0,0 +1,70 @@
+<?php
+/**
+ * @author Arthur Schiwon <blizzz@owncloud.com>
+ * @author Jens-Christian Fischer <jens-christian.fischer@switch.ch>
+ * @author Joas Schilling <nickvergessen@owncloud.com>
+ * @author Morris Jobke <hey@morrisjobke.de>
+ *
+ * @copyright Copyright (c) 2016, ownCloud, Inc.
+ * @license AGPL-3.0
+ *
+ * This code is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU Affero General Public License, version 3,
+ * as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU Affero General Public License for more details.
+ *
+ * You should have received a copy of the GNU Affero General Public License, version 3,
+ * along with this program. If not, see <http://www.gnu.org/licenses/>
+ *
+ */
+
+namespace OC\Core\Command\User;
+
+use OCP\IUserManager;
+use Symfony\Component\Console\Command\Command;
+use Symfony\Component\Console\Input\InputInterface;
+use Symfony\Component\Console\Output\OutputInterface;
+use Symfony\Component\Console\Input\InputArgument;
+
+class Delete extends Command {
+ /** @var IUserManager */
+ protected $userManager;
+
+ /**
+ * @param IUserManager $userManager
+ */
+ public function __construct(IUserManager $userManager) {
+ $this->userManager = $userManager;
+ parent::__construct();
+ }
+
+ protected function configure() {
+ $this
+ ->setName('user:delete')
+ ->setDescription('deletes the specified user')
+ ->addArgument(
+ 'uid',
+ InputArgument::REQUIRED,
+ 'the username'
+ );
+ }
+
+ protected function execute(InputInterface $input, OutputInterface $output) {
+ $user = $this->userManager->get($input->getArgument('uid'));
+ if (is_null($user)) {
+ $output->writeln('<error>User does not exist</error>');
+ return;
+ }
+
+ if ($user->delete()) {
+ $output->writeln('<info>The specified user was deleted</info>');
+ return;
+ }
+
+ $output->writeln('<error>The specified user could not be deleted. Please check the logs.</error>');
+ }
+}
diff --git a/core/Command/User/LastSeen.php b/core/Command/User/LastSeen.php
new file mode 100644
index 00000000000..6bb45a87875
--- /dev/null
+++ b/core/Command/User/LastSeen.php
@@ -0,0 +1,74 @@
+<?php
+/**
+ * @author Arthur Schiwon <blizzz@owncloud.com>
+ * @author Joas Schilling <nickvergessen@owncloud.com>
+ * @author Morris Jobke <hey@morrisjobke.de>
+ * @author Pierre Ozoux <pierre@ozoux.net>
+ *
+ * @copyright Copyright (c) 2016, ownCloud, Inc.
+ * @license AGPL-3.0
+ *
+ * This code is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU Affero General Public License, version 3,
+ * as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU Affero General Public License for more details.
+ *
+ * You should have received a copy of the GNU Affero General Public License, version 3,
+ * along with this program. If not, see <http://www.gnu.org/licenses/>
+ *
+ */
+
+namespace OC\Core\Command\User;
+
+use OCP\IUserManager;
+use Symfony\Component\Console\Command\Command;
+use Symfony\Component\Console\Input\InputInterface;
+use Symfony\Component\Console\Output\OutputInterface;
+use Symfony\Component\Console\Input\InputArgument;
+
+class LastSeen extends Command {
+ /** @var IUserManager */
+ protected $userManager;
+
+ /**
+ * @param IUserManager $userManager
+ */
+ public function __construct(IUserManager $userManager) {
+ $this->userManager = $userManager;
+ parent::__construct();
+ }
+
+ protected function configure() {
+ $this
+ ->setName('user:lastseen')
+ ->setDescription('shows when the user was logged in last time')
+ ->addArgument(
+ 'uid',
+ InputArgument::REQUIRED,
+ 'the username'
+ );
+ }
+
+ protected function execute(InputInterface $input, OutputInterface $output) {
+ $user = $this->userManager->get($input->getArgument('uid'));
+ if(is_null($user)) {
+ $output->writeln('<error>User does not exist</error>');
+ return;
+ }
+
+ $lastLogin = $user->getLastLogin();
+ if($lastLogin === 0) {
+ $output->writeln('User ' . $user->getUID() .
+ ' has never logged in, yet.');
+ } else {
+ $date = new \DateTime();
+ $date->setTimestamp($lastLogin);
+ $output->writeln($user->getUID() .
+ '`s last login: ' . $date->format('d.m.Y H:i'));
+ }
+ }
+}
diff --git a/core/Command/User/Report.php b/core/Command/User/Report.php
new file mode 100644
index 00000000000..df9f7e41620
--- /dev/null
+++ b/core/Command/User/Report.php
@@ -0,0 +1,86 @@
+<?php
+/**
+ * @author Arthur Schiwon <blizzz@owncloud.com>
+ * @author Joas Schilling <nickvergessen@owncloud.com>
+ * @author Morris Jobke <hey@morrisjobke.de>
+ * @author Thomas Müller <thomas.mueller@tmit.eu>
+ *
+ * @copyright Copyright (c) 2016, ownCloud, Inc.
+ * @license AGPL-3.0
+ *
+ * This code is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU Affero General Public License, version 3,
+ * as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU Affero General Public License for more details.
+ *
+ * You should have received a copy of the GNU Affero General Public License, version 3,
+ * along with this program. If not, see <http://www.gnu.org/licenses/>
+ *
+ */
+
+namespace OC\Core\Command\User;
+
+use OCP\IUserManager;
+use Symfony\Component\Console\Command\Command;
+use Symfony\Component\Console\Input\InputInterface;
+use Symfony\Component\Console\Output\OutputInterface;
+
+class Report extends Command {
+ /** @var IUserManager */
+ protected $userManager;
+
+ /**
+ * @param IUserManager $userManager
+ */
+ public function __construct(IUserManager $userManager) {
+ $this->userManager = $userManager;
+ parent::__construct();
+ }
+
+ protected function configure() {
+ $this
+ ->setName('user:report')
+ ->setDescription('shows how many users have access');
+ }
+
+ protected function execute(InputInterface $input, OutputInterface $output) {
+ /** @var \Symfony\Component\Console\Helper\TableHelper $table */
+ $table = $this->getHelperSet()->get('table');
+ $table->setHeaders(array('User Report', ''));
+ $userCountArray = $this->countUsers();
+ if(!empty($userCountArray)) {
+ $total = 0;
+ $rows = array();
+ foreach($userCountArray as $classname => $users) {
+ $total += $users;
+ $rows[] = array($classname, $users);
+ }
+
+ $rows[] = array(' ');
+ $rows[] = array('total users', $total);
+ } else {
+ $rows[] = array('No backend enabled that supports user counting', '');
+ }
+
+ $userDirectoryCount = $this->countUserDirectories();
+ $rows[] = array(' ');
+ $rows[] = array('user directories', $userDirectoryCount);
+
+ $table->setRows($rows);
+ $table->render($output);
+ }
+
+ private function countUsers() {
+ return $this->userManager->countUsers();
+ }
+
+ private function countUserDirectories() {
+ $dataview = new \OC\Files\View('/');
+ $userDirectories = $dataview->getDirectoryContent('/', 'httpd/unix-directory');
+ return count($userDirectories);
+ }
+}
diff --git a/core/Command/User/ResetPassword.php b/core/Command/User/ResetPassword.php
new file mode 100644
index 00000000000..f3f2d5b0630
--- /dev/null
+++ b/core/Command/User/ResetPassword.php
@@ -0,0 +1,121 @@
+<?php
+/**
+ * @author Andreas Fischer <bantu@owncloud.com>
+ * @author Christopher Schäpers <kondou@ts.unde.re>
+ * @author Clark Tomlinson <fallen013@gmail.com>
+ * @author Joas Schilling <nickvergessen@owncloud.com>
+ * @author Laurens Post <lkpost@scept.re>
+ * @author Morris Jobke <hey@morrisjobke.de>
+ *
+ * @copyright Copyright (c) 2016, ownCloud, Inc.
+ * @license AGPL-3.0
+ *
+ * This code is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU Affero General Public License, version 3,
+ * as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU Affero General Public License for more details.
+ *
+ * You should have received a copy of the GNU Affero General Public License, version 3,
+ * along with this program. If not, see <http://www.gnu.org/licenses/>
+ *
+ */
+
+namespace OC\Core\Command\User;
+
+use OCP\IUserManager;
+use Symfony\Component\Console\Command\Command;
+use Symfony\Component\Console\Input\InputInterface;
+use Symfony\Component\Console\Input\InputArgument;
+use Symfony\Component\Console\Input\InputOption;
+use Symfony\Component\Console\Output\OutputInterface;
+
+class ResetPassword extends Command {
+
+ /** @var IUserManager */
+ protected $userManager;
+
+ public function __construct(IUserManager $userManager) {
+ $this->userManager = $userManager;
+ parent::__construct();
+ }
+
+ protected function configure() {
+ $this
+ ->setName('user:resetpassword')
+ ->setDescription('Resets the password of the named user')
+ ->addArgument(
+ 'user',
+ InputArgument::REQUIRED,
+ 'Username to reset password'
+ )
+ ->addOption(
+ 'password-from-env',
+ null,
+ InputOption::VALUE_NONE,
+ 'read password from environment variable OC_PASS'
+ )
+ ;
+ }
+
+ protected function execute(InputInterface $input, OutputInterface $output) {
+ $username = $input->getArgument('user');
+
+ /** @var $user \OCP\IUser */
+ $user = $this->userManager->get($username);
+ if (is_null($user)) {
+ $output->writeln('<error>User does not exist</error>');
+ return 1;
+ }
+
+ if ($input->getOption('password-from-env')) {
+ $password = getenv('OC_PASS');
+ if (!$password) {
+ $output->writeln('<error>--password-from-env given, but OC_PASS is empty!</error>');
+ return 1;
+ }
+ } elseif ($input->isInteractive()) {
+ /** @var $dialog \Symfony\Component\Console\Helper\DialogHelper */
+ $dialog = $this->getHelperSet()->get('dialog');
+
+ if (\OCP\App::isEnabled('encryption')) {
+ $output->writeln(
+ '<error>Warning: Resetting the password when using encryption will result in data loss!</error>'
+ );
+ if (!$dialog->askConfirmation($output, '<question>Do you want to continue?</question>', true)) {
+ return 1;
+ }
+ }
+
+ $password = $dialog->askHiddenResponse(
+ $output,
+ '<question>Enter a new password: </question>',
+ false
+ );
+ $confirm = $dialog->askHiddenResponse(
+ $output,
+ '<question>Confirm the new password: </question>',
+ false
+ );
+
+ if ($password !== $confirm) {
+ $output->writeln("<error>Passwords did not match!</error>");
+ return 1;
+ }
+ } else {
+ $output->writeln("<error>Interactive input or --password-from-env is needed for entering a new password!</error>");
+ return 1;
+ }
+
+ $success = $user->setPassword($password);
+ if ($success) {
+ $output->writeln("<info>Successfully reset password for " . $username . "</info>");
+ } else {
+ $output->writeln("<error>Error while resetting password!</error>");
+ return 1;
+ }
+ }
+}