diff --git a/appinfo/app.php b/appinfo/app.php deleted file mode 100755 index e2d077c..0000000 --- a/appinfo/app.php +++ /dev/null @@ -1,37 +0,0 @@ - -* @author Maxence Lange -* @copyright 2019, Maxence Lange -* @license GNU AGPL version 3 or any later version -* -* This program is free software: you can redistribute it and/or modify -* it under the terms of the GNU Affero General Public License as -* published by the Free Software Foundation, either version 3 of the -* License, or (at your option) any later version. -* -* 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 -* along with this program. If not, see . -* -*/ - - - -namespace OCA\Backup\AppInfo; - - -require_once __DIR__ . '/autoload.php'; - diff --git a/appinfo/info.xml b/appinfo/info.xml index da59b0a..a35c499 100755 --- a/appinfo/info.xml +++ b/appinfo/info.xml @@ -3,11 +3,10 @@ xsi:noNamespaceSchemaLocation="https://apps.nextcloud.com/schema/apps/info.xsd"> backup Backup - + agpl Maxence Lange - Frank Karlitschek - 0.1.0 + 0.1.2 Backup tools https://github.com/nextcloud/backup @@ -15,17 +14,20 @@ https://github.com/nextcloud/backup.git https://raw.githubusercontent.com/nextcloud/backup/master/img/screenshot.png - + - - OCA\Backup\Cron\Backup - + + + - OCA\Backup\Command\Create - OCA\Backup\Command\Listing - OCA\Backup\Command\Details - OCA\Backup\Command\Restore + OCA\Backup\Command\RemoteAdd + OCA\Backup\Command\RemoteList + + + + + diff --git a/appinfo/routes.php b/appinfo/routes.php index 38ecdcc..df93367 100755 --- a/appinfo/routes.php +++ b/appinfo/routes.php @@ -1,29 +1,8 @@ - * - * @author Frank Karlitschek - * - * @license GNU AGPL version 3 or any later version - * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU Affero General Public License as - * published by the Free Software Foundation, either version 3 of the - * License, or (at your option) any later version. - * - * 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 - * along with this program. If not, see . - * - */ return [ - 'ocs' => [ - ['name' => 'API#createBackup', 'url' => '/api/v1/create', 'verb' => 'POST'], - ['name' => 'API#restoreBackup', 'url' => '/api/v1/restore', 'verb' => 'POST'], - ], + 'routes' => [ + ['name' => 'Remote#appService', 'url' => '/', 'verb' => 'GET'], + ['name' => 'Remote#test', 'url' => '/test', 'verb' => 'GET'] + ] ]; diff --git a/composer.json b/composer.json index 081c8d1..1a3ee36 100644 --- a/composer.json +++ b/composer.json @@ -15,9 +15,8 @@ } }, "require": { - "daita/my-small-php-tools": "dev-master", + "artificial-owl/my-small-php-tools": "~23", "ifsnop/mysqldump-php": "2.0.0", - "symfony/console": "^4.2", "pimple/pimple": "~3.0" } } diff --git a/composer.json~ b/composer.json~ deleted file mode 100644 index b9719e6..0000000 --- a/composer.json~ +++ /dev/null @@ -1,23 +0,0 @@ -{ - "name": "nextcloud/backup", - "description": "Backup app", - "minimum-stability": "stable", - "license": "AGPL-3.0-or-later", - "authors": [ - { - "name": "Maxence Lange", - "email": "maxence@artificial-owl.com" - } - ], - "config": { - "platform": { - "php": "7.2.0" - } - }, - "require": { - "daita/my-small-php-tools": "dev-master", - "ifsnop/mysqldump-php": "2.0.0", - "symfony/console": "^4.2", - "pimple/pimple": "~3.0", - } -} diff --git a/composer.lock b/composer.lock index 8827c2c..966ad7c 100644 --- a/composer.lock +++ b/composer.lock @@ -1,32 +1,32 @@ { "_readme": [ "This file locks the dependencies of your project to a known state", - "Read more about it at https://getcomposer.org/doc/01-basic-usage.md#composer-lock-the-lock-file", + "Read more about it at https://getcomposer.org/doc/01-basic-usage.md#installing-dependencies", "This file is @generated automatically" ], - "content-hash": "b6c5df6ef9c263971da6fe6265a51a5f", + "content-hash": "f5e410c5db65574e29844da89236fc3e", "packages": [ { - "name": "daita/my-small-php-tools", - "version": "dev-master", + "name": "artificial-owl/my-small-php-tools", + "version": "v23.0.5", "source": { "type": "git", - "url": "https://github.com/daita/my-small-php-tools.git", - "reference": "e2ac095a0148fec7aaafe5dc9edc20d65c2d1f14" + "url": "https://github.com/ArtificialOwl/my-small-php-tools.git", + "reference": "0574c5936e45299541453f47344ce90866abc0f2" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/daita/my-small-php-tools/zipball/e2ac095a0148fec7aaafe5dc9edc20d65c2d1f14", - "reference": "e2ac095a0148fec7aaafe5dc9edc20d65c2d1f14", + "url": "https://api.github.com/repos/ArtificialOwl/my-small-php-tools/zipball/0574c5936e45299541453f47344ce90866abc0f2", + "reference": "0574c5936e45299541453f47344ce90866abc0f2", "shasum": "" }, "require": { - "php": "^7.0" + "php": ">=7.0" }, "type": "library", "autoload": { "psr-4": { - "daita\\MySmallPhpTools\\": "lib/" + "ArtificialOwl\\MySmallPhpTools\\": "lib/" } }, "notification-url": "https://packagist.org/downloads/", @@ -40,7 +40,11 @@ } ], "description": "My small PHP Tools", - "time": "2019-03-29T18:48:14+00:00" + "support": { + "issues": "https://github.com/ArtificialOwl/my-small-php-tools/issues", + "source": "https://github.com/ArtificialOwl/my-small-php-tools/tree/v23.0.5" + }, + "time": "2021-09-08T12:00:47+00:00" }, { "name": "ifsnop/mysqldump-php", @@ -92,6 +96,10 @@ "pdo", "sqlite" ], + "support": { + "issues": "https://github.com/ifsnop/mysqldump-php/issues", + "source": "https://github.com/ifsnop/mysqldump-php/tree/devel" + }, "time": "2015-09-10T19:58:38+00:00" }, { @@ -142,31 +150,30 @@ "container", "dependency injection" ], + "support": { + "issues": "https://github.com/silexphp/Pimple/issues", + "source": "https://github.com/silexphp/Pimple/tree/master" + }, "time": "2018-01-21T07:42:36+00:00" }, { "name": "psr/container", - "version": "1.0.0", + "version": "1.1.1", "source": { "type": "git", "url": "https://github.com/php-fig/container.git", - "reference": "b7ce3b176482dbbc1245ebf52b181af44c2cf55f" + "reference": "8622567409010282b7aeebe4bb841fe98b58dcaf" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/php-fig/container/zipball/b7ce3b176482dbbc1245ebf52b181af44c2cf55f", - "reference": "b7ce3b176482dbbc1245ebf52b181af44c2cf55f", + "url": "https://api.github.com/repos/php-fig/container/zipball/8622567409010282b7aeebe4bb841fe98b58dcaf", + "reference": "8622567409010282b7aeebe4bb841fe98b58dcaf", "shasum": "" }, "require": { - "php": ">=5.3.0" + "php": ">=7.2.0" }, "type": "library", - "extra": { - "branch-alias": { - "dev-master": "1.0.x-dev" - } - }, "autoload": { "psr-4": { "Psr\\Container\\": "src/" @@ -179,7 +186,7 @@ "authors": [ { "name": "PHP-FIG", - "homepage": "http://www.php-fig.org/" + "homepage": "https://www.php-fig.org/" } ], "description": "Common Container Interface (PHP FIG PSR-11)", @@ -191,219 +198,23 @@ "container-interop", "psr" ], - "time": "2017-02-14T16:28:37+00:00" - }, - { - "name": "symfony/console", - "version": "v4.2.5", - "source": { - "type": "git", - "url": "https://github.com/symfony/console.git", - "reference": "24206aff3efe6962593297e57ef697ebb220e384" + "support": { + "issues": "https://github.com/php-fig/container/issues", + "source": "https://github.com/php-fig/container/tree/1.1.1" }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/symfony/console/zipball/24206aff3efe6962593297e57ef697ebb220e384", - "reference": "24206aff3efe6962593297e57ef697ebb220e384", - "shasum": "" - }, - "require": { - "php": "^7.1.3", - "symfony/contracts": "^1.0", - "symfony/polyfill-mbstring": "~1.0" - }, - "conflict": { - "symfony/dependency-injection": "<3.4", - "symfony/process": "<3.3" - }, - "provide": { - "psr/log-implementation": "1.0" - }, - "require-dev": { - "psr/log": "~1.0", - "symfony/config": "~3.4|~4.0", - "symfony/dependency-injection": "~3.4|~4.0", - "symfony/event-dispatcher": "~3.4|~4.0", - "symfony/lock": "~3.4|~4.0", - "symfony/process": "~3.4|~4.0" - }, - "suggest": { - "psr/log": "For using the console logger", - "symfony/event-dispatcher": "", - "symfony/lock": "", - "symfony/process": "" - }, - "type": "library", - "extra": { - "branch-alias": { - "dev-master": "4.2-dev" - } - }, - "autoload": { - "psr-4": { - "Symfony\\Component\\Console\\": "" - }, - "exclude-from-classmap": [ - "/Tests/" - ] - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "MIT" - ], - "authors": [ - { - "name": "Fabien Potencier", - "email": "fabien@symfony.com" - }, - { - "name": "Symfony Community", - "homepage": "https://symfony.com/contributors" - } - ], - "description": "Symfony Console Component", - "homepage": "https://symfony.com", - "time": "2019-04-01T07:32:59+00:00" - }, - { - "name": "symfony/contracts", - "version": "v1.0.2", - "source": { - "type": "git", - "url": "https://github.com/symfony/contracts.git", - "reference": "1aa7ab2429c3d594dd70689604b5cf7421254cdf" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/symfony/contracts/zipball/1aa7ab2429c3d594dd70689604b5cf7421254cdf", - "reference": "1aa7ab2429c3d594dd70689604b5cf7421254cdf", - "shasum": "" - }, - "require": { - "php": "^7.1.3" - }, - "require-dev": { - "psr/cache": "^1.0", - "psr/container": "^1.0" - }, - "suggest": { - "psr/cache": "When using the Cache contracts", - "psr/container": "When using the Service contracts", - "symfony/cache-contracts-implementation": "", - "symfony/service-contracts-implementation": "", - "symfony/translation-contracts-implementation": "" - }, - "type": "library", - "extra": { - "branch-alias": { - "dev-master": "1.0-dev" - } - }, - "autoload": { - "psr-4": { - "Symfony\\Contracts\\": "" - }, - "exclude-from-classmap": [ - "**/Tests/" - ] - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "MIT" - ], - "authors": [ - { - "name": "Nicolas Grekas", - "email": "p@tchwork.com" - }, - { - "name": "Symfony Community", - "homepage": "https://symfony.com/contributors" - } - ], - "description": "A set of abstractions extracted out of the Symfony components", - "homepage": "https://symfony.com", - "keywords": [ - "abstractions", - "contracts", - "decoupling", - "interfaces", - "interoperability", - "standards" - ], - "time": "2018-12-05T08:06:11+00:00" - }, - { - "name": "symfony/polyfill-mbstring", - "version": "v1.11.0", - "source": { - "type": "git", - "url": "https://github.com/symfony/polyfill-mbstring.git", - "reference": "fe5e94c604826c35a32fa832f35bd036b6799609" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/symfony/polyfill-mbstring/zipball/fe5e94c604826c35a32fa832f35bd036b6799609", - "reference": "fe5e94c604826c35a32fa832f35bd036b6799609", - "shasum": "" - }, - "require": { - "php": ">=5.3.3" - }, - "suggest": { - "ext-mbstring": "For best performance" - }, - "type": "library", - "extra": { - "branch-alias": { - "dev-master": "1.11-dev" - } - }, - "autoload": { - "psr-4": { - "Symfony\\Polyfill\\Mbstring\\": "" - }, - "files": [ - "bootstrap.php" - ] - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "MIT" - ], - "authors": [ - { - "name": "Nicolas Grekas", - "email": "p@tchwork.com" - }, - { - "name": "Symfony Community", - "homepage": "https://symfony.com/contributors" - } - ], - "description": "Symfony polyfill for the Mbstring extension", - "homepage": "https://symfony.com", - "keywords": [ - "compatibility", - "mbstring", - "polyfill", - "portable", - "shim" - ], - "time": "2019-02-06T07:57:58+00:00" + "time": "2021-03-05T17:36:06+00:00" } ], "packages-dev": [], "aliases": [], "minimum-stability": "stable", - "stability-flags": { - "daita/my-small-php-tools": 20 - }, + "stability-flags": [], "prefer-stable": false, "prefer-lowest": false, "platform": [], "platform-dev": [], "platform-overrides": { "php": "7.2.0" - } + }, + "plugin-api-version": "2.0.0" } diff --git a/lib/AppInfo/Application.php b/lib/AppInfo/Application.php index 113059b..53d134b 100755 --- a/lib/AppInfo/Application.php +++ b/lib/AppInfo/Application.php @@ -1,4 +1,5 @@ * @author Maxence Lange * @copyright 2019, Maxence Lange * @license GNU AGPL version 3 or any later version @@ -32,7 +32,22 @@ declare(strict_types=1); namespace OCA\Backup\AppInfo; +use OCA\Backup\Handlers\WebfingerHandler; +use OCA\Backup\Listeners\FileChanged; +use OCA\Backup\Listeners\FileCreated; +use OCA\Backup\Listeners\FileDeleted; +use OCA\Backup\Listeners\FileRenamed; use OCP\AppFramework\App; +use OCP\AppFramework\Bootstrap\IBootContext; +use OCP\AppFramework\Bootstrap\IBootstrap; +use OCP\AppFramework\Bootstrap\IRegistrationContext; +use OCP\Files\Events\Node\NodeCreatedEvent; +use OCP\Files\Events\Node\NodeDeletedEvent; +use OCP\Files\Events\Node\NodeRenamedEvent; +use OCP\Files\Events\Node\NodeWrittenEvent; + + +require_once __DIR__ . '/../../vendor/autoload.php'; /** @@ -40,10 +55,13 @@ use OCP\AppFramework\App; * * @package OCA\Backup\AppInfo */ -class Application extends App { +class Application extends App implements IBootstrap { const APP_ID = 'backup'; + const APP_NAME = 'Backup'; + const APP_SUBJECT = 'http://nextcloud.com/'; + const APP_REL = 'https://apps.nextcloud.com/apps/backup'; /** @@ -55,5 +73,25 @@ class Application extends App { parent::__construct(self::APP_ID, $params); } + + /** + * @param IRegistrationContext $context + */ + public function register(IRegistrationContext $context): void { + $context->registerEventListener(NodeCreatedEvent::class, FileCreated::class); + $context->registerEventListener(NodeWrittenEvent::class, FileChanged::class); + $context->registerEventListener(NodeRenamedEvent::class, FileRenamed::class); + $context->registerEventListener(NodeDeletedEvent::class, FileDeleted::class); + + $context->registerWellKnownHandler(WebfingerHandler::class); + } + + + /** + * @param IBootContext $context + */ + public function boot(IBootContext $context): void { + } + } diff --git a/lib/Backup/Create.php b/lib/Backup/Create.php deleted file mode 100755 index 129fd7b..0000000 --- a/lib/Backup/Create.php +++ /dev/null @@ -1,146 +0,0 @@ - - * - * @author Frank Karlitschek - * - * @license GNU AGPL version 3 or any later version - * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU Affero General Public License as - * published by the Free Software Foundation, either version 3 of the - * License, or (at your option) any later version. - * - * 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 - * along with this program. If not, see . - * - */ - -namespace OCA\Backup\Backup; - -class Create { - - /** @var path */ - protected $path; - - /** @var password */ - protected $password; - - /** - * @param string $path - */ - public function __construct($path) { - $this->path = $path; - } - - /** - * @param string $password - */ - public function password($password) { - $this->password = $password; - } - - /** - * @param string $dbtype - * @param string $dbname - * @param string $dbuser - * @param string $dbpassword - * @param string $dbhost - * @param string $path - */ - private function createDump($dbtype,$dbname,$dbuser,$dbpassword,$dbhost,$path) { - switch ($dbtype) { - case 'sqlite3': - // nothing to do because DB file is already in the data folder - break; - case 'mysql': - $check = shell_exec('which mysqldump'); - if(!empty($check)) { - $cmd = 'mysqldump --password='.$dbpassword.' --user='.$dbuser.' --host='.$dbhost.' '.$dbname.' >'.$path.'/mysql.dmp'; - shell_exec($cmd); - } else { - \OC::$server->getLogger()->error('rsync is not installed',['app' => 'backup',]); - } - break; - case 'pqsql': - // todo - break; - case 'oci': - // todo - break; - } - } - - /** - * @param string $path - */ - private function copydata($path) { - $check = shell_exec('which rsync'); - if(!empty($check)) { - $output = shell_exec('rsync -r '.\OCP\Config::getSystemValue('datadirectory').' '.$path); - } else { - \OC::$server->getLogger()->error('rsync is not installed',['app' => 'backup',]); - } - } - - /** - * @param string $path - */ - private function copyconfig($path) { - $check = shell_exec('which rsync'); - if(!empty($check)) { - $output = shell_exec('rsync -r '.\OC::$configDir.' '.$path.'/config'); - } else { - \OC::$server->getLogger()->error('rsync is not installed',['app' => 'backup',]); - } - } - - /** - * @param string $path - */ - private function writemeta($path) { - $meta = array('date' => date('c'), 'instanceid' => \OCP\Config::getSystemValue('instanceid'), 'dbtype' => \OCP\Config::getSystemValue('dbtype')); - file_put_contents($path.'/backup.json', json_encode($meta)); - } - - /** - */ - public function create() { - \OC::$server->getLogger()->warning('Create backup! Path:'.$this->path.' Password:'.$this->password,['app' => 'backup',]); - - if(!is_dir($this->path) || !is_writable($this->path)) { - \OC::$server->getLogger()->error('Can\'t access directory '.$this->path,['app' => 'backup',]); - exit; - } - - $maintainance = \OCP\Config::getSystemValue('maintenance'); - \OCP\Config::setSystemValue('maintenance',true); - - // create DB dump - $this->createDump( - \OCP\Config::getSystemValue('dbtype'), - \OCP\Config::getSystemValue('dbname'), - \OCP\Config::getSystemValue('dbuser'), - \OCP\Config::getSystemValue('dbpassword'), - \OCP\Config::getSystemValue('dbhost'), - $this->path - ); - - // copy config folder - $this->copyconfig($this->path); - - // syncing data directory - $this->copydata($this->path); - - // write meta file - $this->writemeta($this->path); - - \OCP\Config::setSystemValue('maintenance',$maintainance); - - } -} diff --git a/lib/Backup/Restore.php b/lib/Backup/Restore.php deleted file mode 100755 index 42ae76c..0000000 --- a/lib/Backup/Restore.php +++ /dev/null @@ -1,82 +0,0 @@ - - * - * @author Frank Karlitschek - * - * @license GNU AGPL version 3 or any later version - * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU Affero General Public License as - * published by the Free Software Foundation, either version 3 of the - * License, or (at your option) any later version. - * - * 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 - * along with this program. If not, see . - * - */ - -namespace OCA\Backup\Backup; - -class Restore { - - /** @var path */ - protected $path; - - /** @var password */ - protected $password; - - /** - * @param string $path - */ - public function __construct($path) { - $this->path = $path; - } - - /** - * @param string $password - */ - public function password($password) { - $this->password = $password; - } - - /** - * @param string $dbtype - * @param string $dbname - * @param string $dbuser - * @param string $dbpassword - * @param string $dbhost - * @param string $path - */ - private function restoreDump($dbtype,$dbname,$dbuser,$dbpassword,$dbhost,$path) { - } - - /** - * @param string $path - */ - private function copydata($path) { - } - - /** - * @param string $path - */ - private function copyconfig($path) { - } - - /** - * @param string $path - */ - private function readmeta($path) { - } - - /** - */ - public function restore() { - \OC::$server->getLogger()->warning('Restore backup! Path:'.$this->path.' Password:'.$this->password,['app' => 'backup',]); - } -} diff --git a/lib/Command/Create.php b/lib/Command/Create.php index 11422be..0279fca 100755 --- a/lib/Command/Create.php +++ b/lib/Command/Create.php @@ -1,4 +1,5 @@ * @author Maxence Lange * @copyright 2019, Maxence Lange * @license GNU AGPL version 3 or any later version diff --git a/lib/Command/RemoteAdd.php b/lib/Command/RemoteAdd.php new file mode 100755 index 0000000..63529b2 --- /dev/null +++ b/lib/Command/RemoteAdd.php @@ -0,0 +1,286 @@ + + * @copyright 2021, Maxence Lange + * @license GNU AGPL version 3 or any later version + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU Affero General Public License as + * published by the Free Software Foundation, either version 3 of the + * License, or (at your option) any later version. + * + * 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 + * along with this program. If not, see . + * + */ + + +namespace OCA\Backup\Command; + + +use ArtificialOwl\MySmallPhpTools\Exceptions\RequestNetworkException; +use ArtificialOwl\MySmallPhpTools\Exceptions\SignatoryException; +use ArtificialOwl\MySmallPhpTools\Exceptions\SignatureException; +use ArtificialOwl\MySmallPhpTools\Exceptions\WellKnownLinkNotFoundException; +use ArtificialOwl\MySmallPhpTools\Model\SimpleDataStore; +use ArtificialOwl\MySmallPhpTools\Traits\Nextcloud\nc23\TNC23WellKnown; +use OC\Core\Command\Base; +use OCA\Backup\AppInfo\Application; +use OCA\Backup\Db\RemoteRequest; +use OCA\Backup\Exceptions\RemoteInstanceDuplicateException; +use OCA\Backup\Exceptions\RemoteInstanceNotFoundException; +use OCA\Backup\Exceptions\RemoteInstanceUidException; +use OCA\Backup\Model\RemoteInstance; +use OCA\Backup\Service\RemoteStreamService; +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 RemoteAdd + * + * @package OCA\Backup\Command + */ +class RemoteAdd extends Base { + use TNC23WellKnown; + + + /** @var RemoteRequest */ + private $remoteRequest; + + /** @var RemoteStreamService */ + private $remoteStreamService; + + + /** + * RemoteAdd constructor. + * + * @param RemoteRequest $remoteRequest + * @param RemoteStreamService $remoteStreamService + */ + public function __construct( + RemoteRequest $remoteRequest, + RemoteStreamService $remoteStreamService + ) { + parent::__construct(); + + $this->remoteRequest = $remoteRequest; + $this->remoteStreamService = $remoteStreamService; + } + + + /** + * + */ + protected function configure() { + $this->setName('backup:remote:add') + ->setDescription('Add remote instances to store your backups') + ->addArgument('address', InputArgument::REQUIRED, 'address of the remote instance of Nextcloud'); + } + + + /** + * @param InputInterface $input + * @param OutputInterface $output + * + * @return int + * @throws RequestNetworkException + * @throws SignatureException + * @throws WellKnownLinkNotFoundException + * @throws SignatoryException + * @throws RemoteInstanceUidException + * @throws RemoteInstanceDuplicateException + */ + protected function execute(InputInterface $input, OutputInterface $output): int { + $address = $input->getArgument('address'); + + $resource = $this->getCurrentResourceFromAddress($output, $address); + + $knownInstance = null; + try { + $knownInstance = $this->remoteRequest->getFromHref($resource->g('id')); + } catch (RemoteInstanceNotFoundException $e) { + } + + try { + /** @var RemoteInstance $remoteSignatory */ + $remoteSignatory = $this->remoteStreamService->retrieveSignatory($resource->g('id'), true); + } catch (SignatureException $e) { + throw new SignatureException($address . ' cannot auth its identity: ' . $e->getMessage()); + } + + try { + $duplicateInstance = $this->remoteRequest->getFromInstance($address); + if ($duplicateInstance->getId() !== $remoteSignatory->getId()) { + throw new RemoteInstanceDuplicateException( + 'There is already a known instance with same ADDRESS but different HREF. Please remove it first!' + ); + } + } catch (RemoteInstanceNotFoundException $e) { + } + + $remoteSignatory->setInstance($address); +echo json_encode($knownInstance) . "\n"; + if (!is_null($knownInstance)) { + if ($remoteSignatory->getInstance() !== $knownInstance->getInstance()) { + throw new RemoteInstanceDuplicateException( + 'There is already a known instance with same HREF but different ADDRESS (' + . $knownInstance->getInstance() . '). Please remove it first!' + ); + } + + if ($remoteSignatory->getUid(true) !== $knownInstance->getUid()) { + $output->writeln(''); + $output->writeln('This instance is already known under an other identity!'); + $output->writeln( + 'Please CONFIRM with the admin of the remote instance before updating this known instance.' + ); + $helper = $this->getHelper('question'); + $question = new ConfirmationQuestion( + 'Are you sure you want to continue with the process ? (y/N)', + false, + '/^(y|Y)/i' + ); + + if (!$helper->ask($input, $output, $question)) { + return 0; + } + } + } + + $this->configureRemoteInstance($input, $output, $remoteSignatory, $knownInstance); + $this->saveRemoteInstance($input, $output, $remoteSignatory); + + return 0; + } + + + /** + * @param OutputInterface $output + * @param string $address + * + * @return SimpleDataStore + * @throws RequestNetworkException + * @throws WellKnownLinkNotFoundException + */ + private function getCurrentResourceFromAddress( + OutputInterface $output, + string $address + ): SimpleDataStore { + try { + $webfinger = $this->getWebfinger($address, Application::APP_SUBJECT); + } catch (RequestNetworkException $e) { + throw new RequestNetworkException( + $address + . ' is not reachable or is not a instance of Nextcloud or do not have the Backup App installed' + ); + } + try { + $backupLink = $this->extractLink(Application::APP_REL, $webfinger); + } catch (WellKnownLinkNotFoundException $e) { + throw new WellKnownLinkNotFoundException( + $address + . ' is not a instance of Nextcloud or do not have the Backup App installed and configured' + ); + } + + $output->writeln( + 'Remote instance ' . $address . ' is using ' . $backupLink->getProperty('name') + . ' v' . $backupLink->getProperty('version') . '' + ); + + $resource = $this->getResourceFromLink($backupLink); + $output->writeln('Authentication key: ' . $resource->g('uid') . ''); + + return $resource; + } + + + /** + * @param InputInterface $input + * @param OutputInterface $output + * @param RemoteInstance $remoteInstance + */ + private function configureRemoteInstance( + InputInterface $input, + OutputInterface $output, + RemoteInstance $remoteInstance, + ?RemoteInstance $knownInstance + ): void { + $outgoing = !(is_null($knownInstance)) && $knownInstance->isOutgoing(); + $incoming = !(is_null($knownInstance)) && $knownInstance->isIncoming(); + + $output->writeln(''); + $helper = $this->getHelper('question'); + $question = new ConfirmationQuestion( + 'Do you want to use ' . $remoteInstance->getInstance() + . ' as a remote instance to store your backup files ? ' + . ($outgoing ? '(Y/n)' : '(y/N)'), + $outgoing, + '/^(y|Y)/i' + ); + + $remoteInstance->setOutgoing($helper->ask($input, $output, $question)); + + $question = new ConfirmationQuestion( + 'Do you want to allow ' . $remoteInstance->getInstance() + . ' to store its backup files on your own instance ? ' + . ($incoming ? '(Y/n)' : '(y/N)'), + $incoming, + '/^(y|Y)/i' + ); + + $remoteInstance->setIncoming($helper->ask($input, $output, $question)); + } + + + /** + * @throws RemoteInstanceUidException + */ + private function saveRemoteInstance( + InputInterface $input, + OutputInterface $output, + RemoteInstance $remoteInstance + ): void { + $output->writeln(''); + $output->writeln( + 'Using remote instance to store local backups: ' . ($remoteInstance->isOutgoing( + ) ? 'yes' : 'no') + ); + $output->writeln( + 'Locally storing backups from remote instance: ' . ($remoteInstance->isIncoming( + ) ? 'yes' : 'no') + ); + + $helper = $this->getHelper('question'); + $question = new ConfirmationQuestion( + 'Please confirm those settings (y/N) ', + false, + '/^(y|Y)/i' + ); + + if (!$helper->ask($input, $output, $question)) { + return; + } + + $this->remoteRequest->insertOrUpdate($remoteInstance); + } + +} + diff --git a/lib/Command/RemoteList.php b/lib/Command/RemoteList.php new file mode 100755 index 0000000..18e7e17 --- /dev/null +++ b/lib/Command/RemoteList.php @@ -0,0 +1,76 @@ + + * @copyright 2021, Maxence Lange + * @license GNU AGPL version 3 or any later version + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU Affero General Public License as + * published by the Free Software Foundation, either version 3 of the + * License, or (at your option) any later version. + * + * 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 + * along with this program. If not, see . + * + */ + + +namespace OCA\Backup\Command; + + +use OC\Core\Command\Base; +use Symfony\Component\Console\Input\InputInterface; +use Symfony\Component\Console\Output\OutputInterface; + + +/** + * Class RemoteList + * + * @package OCA\Backup\Command + */ +class RemoteList extends Base { + + + public function __construct() { + + parent::__construct(); + } + + + /** + * + */ + protected function configure() { + $this->setName('backup:remote:list') + ->setDescription('Listing configured remote instances'); + } + + + /** + * @param InputInterface $input + * @param OutputInterface $output + * + * @return int + */ + protected function execute(InputInterface $input, OutputInterface $output): int { + + + return 0; + } + +} + diff --git a/lib/Controller/APIController.php b/lib/Controller/APIController.php deleted file mode 100755 index c82da38..0000000 --- a/lib/Controller/APIController.php +++ /dev/null @@ -1,67 +0,0 @@ - - * - * @author Frank Karlitschek - * - * @license GNU AGPL version 3 or any later version - * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU Affero General Public License as - * published by the Free Software Foundation, either version 3 of the - * License, or (at your option) any later version. - * - * 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 - * along with this program. If not, see . - * - */ - -namespace OCA\Backup\Controller; - -use OCP\AppFramework\Http\DataResponse; -use OCP\AppFramework\OCSController; -use OCP\IRequest; - -class APIController extends OCSController { - - /** - * @param string $appName - * @param IRequest $request - */ - public function __construct($appName, IRequest $request) { - parent::__construct($appName, $request); - } - - /** - * @param string $path - * @param string $password - * @return DataResponse - */ - public function createBackup($path, $password) { - $backup = new \OCA\Backup\Backup\Create($path); - $backup -> password($password); - $backup -> create(); - - return new DataResponse(); - } - - /** - * @param string $path - * @param string $password - * @return DataResponse - */ - public function restoreBackup($path, $password) { - $backup = new \OCA\Backup\Backup\Restore($path); - $backup -> password($password); - $backup -> restore(); - - return new DataResponse(); - } - - -} diff --git a/lib/Controller/RemoteController.php b/lib/Controller/RemoteController.php new file mode 100755 index 0000000..d204ea8 --- /dev/null +++ b/lib/Controller/RemoteController.php @@ -0,0 +1,99 @@ + + * @copyright 2019, Maxence Lange + * @license GNU AGPL version 3 or any later version + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU Affero General Public License as + * published by the Free Software Foundation, either version 3 of the + * License, or (at your option) any later version. + * + * 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 + * along with this program. If not, see . + * + */ + + +namespace OCA\Backup\Controller; + + +use ArtificialOwl\MySmallPhpTools\Exceptions\JsonNotRequestedException; +use ArtificialOwl\MySmallPhpTools\Exceptions\SignatoryException; +use ArtificialOwl\MySmallPhpTools\Traits\Nextcloud\nc22\TNC22Controller; +use OC\AppFramework\Middleware\Security\Exceptions\NotLoggedInException; +use OCA\Backup\Service\RemoteStreamService; +use OCP\AppFramework\Controller; +use OCP\AppFramework\Http\DataResponse; +use OCP\IRequest; + + +/** + * Class RemoteController + * + * @package OCA\Backup\Controller + */ +class RemoteController extends Controller { + + + use TNC22Controller; + + + /** @var RemoteStreamService */ + private $remoteStreamService; + + + /** + * RemoteController constructor. + * + * @param string $appName + * @param IRequest $request + * @param RemoteStreamService $remoteStreamService + */ + public function __construct( + string $appName, + IRequest $request, + RemoteStreamService $remoteStreamService + ) { + parent::__construct($appName, $request); + + $this->remoteStreamService = $remoteStreamService; + } + + + /** + * @PublicPage + * @NoCSRFRequired + * + * @return DataResponse + * @throws NotLoggedInException + * @throws SignatoryException + */ + public function appService(): DataResponse { + try { + $this->publicPageJsonLimited(); + } catch (JsonNotRequestedException $e) { + return new DataResponse(); + } + + $signatory = $this->remoteStreamService->getAppSignatory(false, $this->request->getParam('auth', '')); + + return new DataResponse($signatory); + } + +} + diff --git a/lib/Db/BackupsRequest.php b/lib/Db/BackupsRequest.php index b62d8dd..e5d8563 100644 --- a/lib/Db/BackupsRequest.php +++ b/lib/Db/BackupsRequest.php @@ -72,7 +72,7 @@ class BackupsRequest extends BackupsRequestBuilder { */ public function update(Backup $backup) { $qb = $this->getBackupsUpdateSql(); - $this->limitToId($qb, $backup->getId()); + $qb->limitToId($backup->getId()); $qb->execute(); } diff --git a/lib/Db/BackupsRequestBuilder.php b/lib/Db/BackupsRequestBuilder.php index 4896d9d..3879dbb 100644 --- a/lib/Db/BackupsRequestBuilder.php +++ b/lib/Db/BackupsRequestBuilder.php @@ -31,8 +31,10 @@ declare(strict_types=1); namespace OCA\Backup\Db; -use daita\MySmallPhpTools\Traits\TArrayTools; -use OCA\Backup\Model\Backup; +use ArtificialOwl\MySmallPhpTools\Exceptions\RowNotFoundException; +use ArtificialOwl\MySmallPhpTools\Traits\TArrayTools; +use OCA\Backup\Exceptions\RestoringPointNotFoundException; +use OCA\Backup\Model\RestoringPoint; use OCP\DB\QueryBuilder\IQueryBuilder; /** @@ -49,11 +51,11 @@ class BackupsRequestBuilder extends CoreRequestBuilder { /** * Base of the Sql Insert request * - * @return IQueryBuilder + * @return CoreQueryBuilder */ - protected function getBackupsInsertSql(): IQueryBuilder { - $qb = $this->dbConnection->getQueryBuilder(); - $qb->insert(self::SQL_TABLES['BACKUPS']); + protected function getBackupsInsertSql(): CoreQueryBuilder { + $qb = $this->getQueryBuilder(); + $qb->select(self::TABLE_RESTORING_POINT); return $qb; } @@ -64,9 +66,9 @@ class BackupsRequestBuilder extends CoreRequestBuilder { * * @return IQueryBuilder */ - protected function getBackupsUpdateSql(): IQueryBuilder { - $qb = $this->dbConnection->getQueryBuilder(); - $qb->update(self::SQL_TABLES['BACKUPS']); + protected function getBackupsUpdateSql(): CoreQueryBuilder { + $qb = $this->getQueryBuilder(); + $qb->update(self::TABLE_RESTORING_POINT); return $qb; } @@ -75,19 +77,15 @@ class BackupsRequestBuilder extends CoreRequestBuilder { /** * Base of the Sql Select request for Shares * - * @return IQueryBuilder + * @return CoreQueryBuilder */ - protected function getBackupsSelectSql(): IQueryBuilder { - $qb = $this->dbConnection->getQueryBuilder(); - - /** @noinspection PhpMethodParametersCountMismatchInspection */ - $qb->select( - 'sa.id', 'sa.user_id', 'sa.preferred_username', 'sa.name', 'sa.summary', - 'sa.public_key', 'sa.avatar_version', 'sa.private_key', 'sa.creation' - ) - ->from(self::SQL_TABLES['BACKUPS'], 'sa'); - - $this->defaultSelectAlias = 'sa'; + protected function getBackupsSelectSql(): CoreQueryBuilder { + $qb = $this->getQueryBuilder(); + $qb->generateSelect( + self::TABLE_RESTORING_POINT, + self::$tables[self::TABLE_RESTORING_POINT], + 'b' + ); return $qb; } @@ -99,23 +97,38 @@ class BackupsRequestBuilder extends CoreRequestBuilder { * @return IQueryBuilder */ protected function getBackupsDeleteSql(): IQueryBuilder { - $qb = $this->dbConnection->getQueryBuilder(); - $qb->delete(self::SQL_TABLES['BACKUPS']); + $qb = $this->getQueryBuilder(); + $qb->delete(self::TABLE_RESTORING_POINT); return $qb; } /** - * @param array $data + * @param CoreQueryBuilder $qb * - * @return Backup + * @return RestoringPoint + * @throws RestoringPointNotFoundException */ - protected function parseBackupsSelectSql($data): Backup { - $backup = new Backup(); - $backup->importFromDatabase($data); + public function getItemFromRequest(CoreQueryBuilder $qb): RestoringPoint { + /** @var RestoringPoint $restoringPoint */ + try { + $restoringPoint = $qb->asItem(RestoringPoint::class); + } catch (RowNotFoundException $e) { + throw new RestoringPointNotFoundException(); + } - return $backup; + return $restoringPoint; + } + + /** + * @param CoreQueryBuilder $qb + * + * @return RestoringPoint[] + */ + public function getItemsFromRequest(CoreQueryBuilder $qb): array { + /** @var RestoringPoint[] $result */ + return $qb->asItems(RestoringPoint::class); } } diff --git a/lib/Db/CoreQueryBuilder.php b/lib/Db/CoreQueryBuilder.php new file mode 100644 index 0000000..7eaa552 --- /dev/null +++ b/lib/Db/CoreQueryBuilder.php @@ -0,0 +1,70 @@ + + * @copyright 2021 + * @license GNU AGPL version 3 or any later version + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU Affero General Public License as + * published by the Free Software Foundation, either version 3 of the + * License, or (at your option) any later version. + * + * 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 + * along with this program. If not, see . + * + */ + + +namespace OCA\Backup\Db; + +use ArtificialOwl\MySmallPhpTools\Db\Nextcloud\nc22\NC22ExtendedQueryBuilder; + + +/** + * Class CoreQueryBuilder + * + * @package OCA\Backup\Db + */ +class CoreQueryBuilder extends NC22ExtendedQueryBuilder { + + + /** + * CoreQueryBuilder constructor. + */ + public function __construct() { + parent::__construct(); + } + + + /** + * Limit the request to the Id + * + * @param int $id + */ + public function limitToId(int $id): void { + $this->limitInt('id', $id); + } + + + /** + * @param string $host + */ + public function limitToInstance(string $host): void { + $this->limit('instance', $host, '', false); + } + +} diff --git a/lib/Db/CoreRequestBuilder.php b/lib/Db/CoreRequestBuilder.php index 7c18412..8c75d15 100644 --- a/lib/Db/CoreRequestBuilder.php +++ b/lib/Db/CoreRequestBuilder.php @@ -1,4 +1,5 @@ - * @copyright 2019, Maxence Lange + * @copyright 2021 * @license GNU AGPL version 3 or any later version * * This program is free software: you can redistribute it and/or modify @@ -30,11 +31,10 @@ declare(strict_types=1); namespace OCA\Backup\Db; - -use daita\MySmallPhpTools\Db\RequestBuilder; -use OCA\Backup\Service\MiscService; -use OCP\DB\QueryBuilder\IQueryBuilder; -use OCP\IDBConnection; +use Exception; +use OC\DB\Connection; +use OC\DB\SchemaWrapper; +use OCA\Backup\Service\ConfigService; /** @@ -42,68 +42,117 @@ use OCP\IDBConnection; * * @package OCA\Backup\Db */ -class CoreRequestBuilder extends RequestBuilder { +class CoreRequestBuilder { - const SQL_TABLES = [ - 'BACKUPS' => 'backups' + public const TABLE_RESTORING_POINT = 'backup_point'; + public const TABLE_REMOTE = 'backup_remote'; + + + /** @var array */ + public static $tables = [ + self::TABLE_REMOTE => [ + 'id', + 'uid', + 'instance', + 'href', + 'exchange', + 'item' + ], + self::TABLE_RESTORING_POINT => [ + 'id', + 'user_id', + 'preferred_username', + 'name', + 'summary', + 'sa.public_key', + 'avatar_version', + 'private_key', + 'creation' + ] ]; - /** @var IDBConnection */ - protected $dbConnection; - - /** @var MiscService */ - protected $miscService; - - - /** @var string */ - protected $defaultSelectAlias; + /** @var ConfigService */ + protected $configService; /** - * CoreRequestBuilder constructor. + * CoreQueryBuilder constructor. * - * @param IDBConnection $connection - * @param MiscService $miscService + * @param ConfigService $configService */ - public function __construct(IDBConnection $connection, MiscService $miscService) { - $this->dbConnection = $connection; - $this->miscService = $miscService; + public function __construct(ConfigService $configService) { + $this->configService = $configService; } /** - * Limit the request to the Id - * - * @param IQueryBuilder $qb - * @param int $id + * @return CoreQueryBuilder */ - protected function limitToId(IQueryBuilder &$qb, int $id) { - $this->limitToDBFieldInt($qb, 'id', $id); + public function getQueryBuilder(): CoreQueryBuilder { + return new CoreQueryBuilder(); } /** - * Limit the request to the status - * - * @param IQueryBuilder $qb - * @param int $status + * @param bool $shares */ - protected function limitToStatus(IQueryBuilder &$qb, int $status) { - $this->limitToDBFieldInt($qb, 'status', $status); + public function cleanDatabase(bool $shares = false): void { + foreach (array_keys(self::$tables) as $table) { + $qb = $this->getQueryBuilder(); + try { + $qb->delete($table); + $qb->execute(); + } catch (Exception $e) { + } + } + } + + + public function uninstall(): void { + $this->uninstallAppTables(); + $this->uninstallFromMigrations(); + $this->uninstallFromJobs(); + $this->configService->unsetAppConfig(); + } + + /** + * this just empty all tables from the app. + */ + public function uninstallAppTables() { + $dbConn = \OC::$server->get(Connection::class); + $schema = new SchemaWrapper($dbConn); + + foreach (array_keys(self::$tables) as $table) { + if ($schema->hasTable($table)) { + $schema->dropTable($table); + } + } + + $schema->performDropTableCalls(); } /** - * Limit the request to the instance * - * @param IQueryBuilder $qb - * @param bool $local */ - protected function limitToLocal(IQueryBuilder &$qb, bool $local) { - $this->limitToDBField($qb, 'local', ($local) ? '1' : '0'); + public function uninstallFromMigrations() { + $qb = $this->getQueryBuilder(); + $qb->delete('migrations'); + $qb->limit('app', 'backup'); + $qb->unlike('version', '001%'); + + $qb->execute(); } + /** + * + */ + public function uninstallFromJobs() { + $qb = $this->getQueryBuilder(); +// $qb->delete('jobs'); +// $qb->where($this->exprLimitToDBField($qb, 'class', 'OCA\Backup\', true, true)); +// $qb->execute(); + } } - diff --git a/lib/Db/RemoteRequest.php b/lib/Db/RemoteRequest.php new file mode 100644 index 0000000..59d0d6e --- /dev/null +++ b/lib/Db/RemoteRequest.php @@ -0,0 +1,142 @@ + + * @copyright 2019, Maxence Lange + * @license GNU AGPL version 3 or any later version + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU Affero General Public License as + * published by the Free Software Foundation, either version 3 of the + * License, or (at your option) any later version. + * + * 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 + * along with this program. If not, see . + * + */ + + +namespace OCA\Backup\Db; + + +use OCA\Backup\Exceptions\RemoteInstanceNotFoundException; +use OCA\Backup\Exceptions\RemoteInstanceUidException; +use OCA\Backup\Model\RemoteInstance; + + +/** + * Class RemoteRequest + * + * @package OCA\Backup\Db + */ +class RemoteRequest extends RemoteRequestBuilder { + + + /** + * create a new Person in the database. + * + * @param RemoteInstance $remote + * + * @return int + * @throws RemoteInstanceUidException + */ + public function save(RemoteInstance $remote): int { + $remote->mustBeIdentityAuthed(); + $qb = $this->getRemoteInsertSql(); + $qb->setValue('uid', $qb->createNamedParameter($remote->getUid(true))) + ->setValue('instance', $qb->createNamedParameter($remote->getInstance())) + ->setValue('href', $qb->createNamedParameter($remote->getId())) + ->setValue('exchange', $qb->createNamedParameter($remote->getExchange())) + ->setValue('item', $qb->createNamedParameter(json_encode($remote->getOrigData()))); + + return $qb->execute(); + } + + + /** + * @param RemoteInstance $remote + * + * @throws RemoteInstanceUidException + */ + public function update(RemoteInstance $remote) { + $remote->mustBeIdentityAuthed(); + $qb = $this->getRemoteUpdateSql(); + $qb->set('uid', $qb->createNamedParameter($remote->getUid(true))) + ->set('href', $qb->createNamedParameter($remote->getId())) + ->set('exchange', $qb->createNamedParameter($remote->getExchange())) + ->set('item', $qb->createNamedParameter(json_encode($remote->getOrigData()))); + + $qb->limitToInstance($remote->getInstance()); + + $qb->execute(); + } + + + /** + * @param int $dbId + * + * @return RemoteInstance + * @throws RemoteInstanceNotFoundException + */ + public function getFromDbId(int $dbId): RemoteInstance { + $qb = $this->getRemoteSelectSql(); + $qb->limitToId($dbId); + + return $this->getItemFromRequest($qb); + } + + + /** + * @throws RemoteInstanceNotFoundException + */ + public function getFromInstance(string $instance): RemoteInstance { + $qb = $this->getRemoteSelectSql(); + $qb->limit('instance', $instance, '', false); + + return $this->getItemFromRequest($qb); + } + + /** + * @param string $href + * + * @return RemoteInstance + * @throws RemoteInstanceNotFoundException + */ + public function getFromHref(string $href): RemoteInstance { + $qb = $this->getRemoteSelectSql(); + $qb->limit('href', $href, '', false); + + return $this->getItemFromRequest($qb); + } + + + /** + * @param RemoteInstance $remoteInstance + * + * @throws RemoteInstanceUidException + */ + public function insertOrUpdate(RemoteInstance $remoteInstance): void { + try { + $this->getFromHref($remoteInstance->getId()); + $this->update($remoteInstance); + } catch (RemoteInstanceNotFoundException $e) { + $this->save($remoteInstance); + } + } + + +} + diff --git a/lib/Db/RemoteRequestBuilder.php b/lib/Db/RemoteRequestBuilder.php new file mode 100644 index 0000000..589372f --- /dev/null +++ b/lib/Db/RemoteRequestBuilder.php @@ -0,0 +1,129 @@ + + * @copyright 2019, Maxence Lange + * @license GNU AGPL version 3 or any later version + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU Affero General Public License as + * published by the Free Software Foundation, either version 3 of the + * License, or (at your option) any later version. + * + * 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 + * along with this program. If not, see . + * + */ + + +namespace OCA\Backup\Db; + + +use ArtificialOwl\MySmallPhpTools\Exceptions\RowNotFoundException; +use ArtificialOwl\MySmallPhpTools\Traits\TArrayTools; +use OCA\Backup\Exceptions\RemoteInstanceNotFoundException; +use OCA\Backup\Model\RemoteInstance; +use OCP\DB\QueryBuilder\IQueryBuilder; + + +/** + * Class RemoteRequestBuilder + * + * @package OCA\Backup\Db + */ +class RemoteRequestBuilder extends CoreRequestBuilder { + + + use TArrayTools; + + + /** + * @return CoreQueryBuilder + */ + protected function getRemoteInsertSql(): CoreQueryBuilder { + $qb = $this->getQueryBuilder(); + $qb->insert(self::TABLE_REMOTE); + + return $qb; + } + + + /** + * @return CoreQueryBuilder + */ + protected function getRemoteUpdateSql(): CoreQueryBuilder { + $qb = $this->getQueryBuilder(); + $qb->update(self::TABLE_REMOTE); + + return $qb; + } + + + /** + * @return CoreQueryBuilder + */ + protected function getRemoteSelectSql(): CoreQueryBuilder { + $qb = $this->getQueryBuilder(); + $qb->generateSelect( + self::TABLE_REMOTE, + self::$tables[self::TABLE_REMOTE], + 'remote' + ); + + return $qb; + } + + + /** + * @return IQueryBuilder + */ + protected function getRemoteDeleteSql(): IQueryBuilder { + $qb = $this->getQueryBuilder(); + $qb->delete(self::TABLE_REMOTE); + + return $qb; + } + + + /** + * @param CoreQueryBuilder $qb + * + * @return RemoteInstance + * @throws RemoteInstanceNotFoundException + */ + public function getItemFromRequest(CoreQueryBuilder $qb): RemoteInstance { + /** @var RemoteInstance $remote */ + try { + $remote = $qb->asItem(RemoteInstance::class); + } catch (RowNotFoundException $e) { + throw new RemoteInstanceNotFoundException(); + } + + return $remote; + } + + /** + * @param CoreQueryBuilder $qb + * + * @return RemoteInstance[] + */ + public function getItemsFromRequest(CoreQueryBuilder $qb): array { + /** @var RemoteInstance[] $result */ + return $qb->asItems(RemoteInstance::class); + } + +} + diff --git a/lib/Exceptions/RemoteInstanceDuplicateException.php b/lib/Exceptions/RemoteInstanceDuplicateException.php new file mode 100644 index 0000000..bee7221 --- /dev/null +++ b/lib/Exceptions/RemoteInstanceDuplicateException.php @@ -0,0 +1,46 @@ + + * @copyright 2021, Maxence Lange + * @license GNU AGPL version 3 or any later version + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU Affero General Public License as + * published by the Free Software Foundation, either version 3 of the + * License, or (at your option) any later version. + * + * 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 + * along with this program. If not, see . + * + */ + + +namespace OCA\Backup\Exceptions; + + +use Exception; + + +/** + * Class RemoteInstanceDuplicateException + * + * @package OCA\Backup\Exceptions + */ +class RemoteInstanceDuplicateException extends Exception { + +} + diff --git a/lib/Exceptions/RemoteInstanceNotFoundException.php b/lib/Exceptions/RemoteInstanceNotFoundException.php new file mode 100644 index 0000000..e1d7ee7 --- /dev/null +++ b/lib/Exceptions/RemoteInstanceNotFoundException.php @@ -0,0 +1,46 @@ + + * @copyright 2021, Maxence Lange + * @license GNU AGPL version 3 or any later version + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU Affero General Public License as + * published by the Free Software Foundation, either version 3 of the + * License, or (at your option) any later version. + * + * 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 + * along with this program. If not, see . + * + */ + + +namespace OCA\Backup\Exceptions; + + +use Exception; + + +/** + * Class RemoteInstanceNotFoundException + * + * @package OCA\Backup\Exceptions + */ +class RemoteInstanceNotFoundException extends Exception { + +} + diff --git a/lib/Exceptions/RemoteInstanceUidException.php b/lib/Exceptions/RemoteInstanceUidException.php new file mode 100644 index 0000000..ca72f6e --- /dev/null +++ b/lib/Exceptions/RemoteInstanceUidException.php @@ -0,0 +1,46 @@ + + * @copyright 2021, Maxence Lange + * @license GNU AGPL version 3 or any later version + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU Affero General Public License as + * published by the Free Software Foundation, either version 3 of the + * License, or (at your option) any later version. + * + * 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 + * along with this program. If not, see . + * + */ + + +namespace OCA\Backup\Exceptions; + + +use Exception; + + +/** + * Class RemoteInstanceUidException + * + * @package OCA\Backup\Exceptions + */ +class RemoteInstanceUidException extends Exception { + +} + diff --git a/appinfo/autoload.php b/lib/Exceptions/RestoringPointNotFoundException.php similarity index 84% rename from appinfo/autoload.php rename to lib/Exceptions/RestoringPointNotFoundException.php index edd0cc8..3d345a1 100644 --- a/appinfo/autoload.php +++ b/lib/Exceptions/RestoringPointNotFoundException.php @@ -1,4 +1,5 @@ + * @copyright 2019, Maxence Lange + * @license GNU AGPL version 3 or any later version + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU Affero General Public License as + * published by the Free Software Foundation, either version 3 of the + * License, or (at your option) any later version. + * + * 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 + * along with this program. If not, see . + * + */ + + +namespace OCA\Backup\Handlers; + + +use ArtificialOwl\MySmallPhpTools\Exceptions\SignatoryException; +use ArtificialOwl\MySmallPhpTools\Traits\TArrayTools; +use OC\URLGenerator; +use OCA\Backup\AppInfo\Application; +use OCA\Backup\Service\ConfigService; +use OCA\Backup\Service\RemoteStreamService; +use OCP\Http\WellKnown\IHandler; +use OCP\Http\WellKnown\IRequestContext; +use OCP\Http\WellKnown\IResponse; +use OCP\Http\WellKnown\JrdResponse; +use OCP\IURLGenerator; + + +/** + * Class WebfingerHandler + * + * @package OCA\Circles\Handlers + */ +class WebfingerHandler implements IHandler { + + + use TArrayTools; + + + /** @var URLGenerator */ + private $urlGenerator; + + /** @var RemoteStreamService */ + private $remoteStreamService; + + /** @var ConfigService */ + private $configService; + + + /** + * WebfingerHandler constructor. + * + * @param IURLGenerator $urlGenerator + * @param RemoteStreamService $remoteStreamService + * @param ConfigService $configService + */ + public function __construct( + IURLGenerator $urlGenerator, + RemoteStreamService $remoteStreamService, + ConfigService $configService + ) { + $this->urlGenerator = $urlGenerator; + $this->remoteStreamService = $remoteStreamService; + $this->configService = $configService; + } + + + /** + * @param string $service + * @param IRequestContext $context + * @param IResponse|null $response + * + * @return IResponse|null + */ + public function handle(string $service, IRequestContext $context, ?IResponse $response): ?IResponse { + if ($service !== 'webfinger') { + return $response; + } + + $request = $context->getHttpRequest(); + $subject = $request->getParam('resource', ''); + if ($subject !== Application::APP_SUBJECT) { + return $response; + } + + $token = $request->getParam('check', ''); + + if (!($response instanceof JrdResponse)) { + $response = new JrdResponse($subject); + } + + try { + $this->remoteStreamService->getAppSignatory(); + $href = $this->urlGenerator->linkToRouteAbsolute('backup.Remote.appService'); + $info = [ + 'app' => Application::APP_ID, + 'name' => Application::APP_NAME, + 'version' => $this->configService->getAppValue('installed_version') + ]; + } catch (SignatoryException $e) { + return $response; + } + + return $response->addLink(Application::APP_REL, 'application/json', $href, [], $info); + } + +} + diff --git a/lib/Listeners/FileChanged.php b/lib/Listeners/FileChanged.php new file mode 100644 index 0000000..8871609 --- /dev/null +++ b/lib/Listeners/FileChanged.php @@ -0,0 +1,64 @@ + + * @copyright 2021, Maxence Lange + * @license GNU AGPL version 3 or any later version + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU Affero General Public License as + * published by the Free Software Foundation, either version 3 of the + * License, or (at your option) any later version. + * + * 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 + * along with this program. If not, see . + * + */ + + +namespace OCA\Backup\Listeners; + +use OCP\EventDispatcher\Event; +use OCP\EventDispatcher\IEventListener; +use OCP\Files\Events\Node\NodeWrittenEvent; +use OCP\Files\InvalidPathException; +use OCP\Files\NotFoundException; + + +/** + * Class FileChanged + * + * @package OCA\Backup\Listeners + */ +class FileChanged implements IEventListener { + + + /** + * @param Event $event + */ + public function handle(Event $event): void { + if (!($event instanceof NodeWrittenEvent)) { + return; + } + + try { + $node = $event->getNode(); + } catch (InvalidPathException | NotFoundException $e) { + } + } + +} + diff --git a/lib/Listeners/FileCreated.php b/lib/Listeners/FileCreated.php new file mode 100644 index 0000000..28ff256 --- /dev/null +++ b/lib/Listeners/FileCreated.php @@ -0,0 +1,69 @@ + + * @copyright 2021, Maxence Lange + * @license GNU AGPL version 3 or any later version + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU Affero General Public License as + * published by the Free Software Foundation, either version 3 of the + * License, or (at your option) any later version. + * + * 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 + * along with this program. If not, see . + * + */ + + +namespace OCA\Backup\Listeners; + + +use OCP\EventDispatcher\Event; +use OCP\EventDispatcher\IEventListener; +use OCP\Files\Events\Node\NodeCreatedEvent; + + +/** + * Class FileCreated + * + * @package OCA\Backup\Listeners + */ +class FileCreated implements IEventListener { + + + /** + * @param Event $event + */ + public function handle(Event $event): void { + if (!($event instanceof NodeCreatedEvent)) { + return; + } + +// $node = $event->getNode(); +// $user = $this->userSession->getUser(); +// if ($user === null) { +// return; +// } +// +// try { +// } catch (InvalidPathException | NotFoundException $e) { +// $this->exception($e); +// } + } + +} + diff --git a/lib/Listeners/FileDeleted.php b/lib/Listeners/FileDeleted.php new file mode 100644 index 0000000..9fc4f5b --- /dev/null +++ b/lib/Listeners/FileDeleted.php @@ -0,0 +1,64 @@ + + * @copyright 2021, Maxence Lange + * @license GNU AGPL version 3 or any later version + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU Affero General Public License as + * published by the Free Software Foundation, either version 3 of the + * License, or (at your option) any later version. + * + * 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 + * along with this program. If not, see . + * + */ + + +namespace OCA\Backup\Listeners; + +use OCP\EventDispatcher\Event; +use OCP\EventDispatcher\IEventListener; +use OCP\Files\Events\Node\NodeDeletedEvent; +use OCP\Files\InvalidPathException; +use OCP\Files\NotFoundException; + + +/** + * Class FileDeleted + * + * @package OCA\Backup\Listeners + */ +class FileDeleted implements IEventListener { + + + /** + * @param Event $event + */ + public function handle(Event $event): void { + if (!($event instanceof NodeDeletedEvent)) { + return; + } + + try { + $node = $event->getNode(); + } catch (InvalidPathException | NotFoundException $e) { + } + } + +} + diff --git a/lib/Listeners/FileRenamed.php b/lib/Listeners/FileRenamed.php new file mode 100644 index 0000000..4db6c38 --- /dev/null +++ b/lib/Listeners/FileRenamed.php @@ -0,0 +1,64 @@ + + * @copyright 2021, Maxence Lange + * @license GNU AGPL version 3 or any later version + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU Affero General Public License as + * published by the Free Software Foundation, either version 3 of the + * License, or (at your option) any later version. + * + * 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 + * along with this program. If not, see . + * + */ + + +namespace OCA\Backup\Listeners; + +use OCP\EventDispatcher\Event; +use OCP\EventDispatcher\IEventListener; +use OCP\Files\Events\Node\NodeRenamedEvent; +use OCP\Files\InvalidPathException; +use OCP\Files\NotFoundException; + + +/** + * Class FileRenamed + * + * @package OCA\Backup\Listeners + */ +class FileRenamed implements IEventListener { + + + /** + * @param Event $event + */ + public function handle(Event $event): void { + if (!($event instanceof NodeRenamedEvent)) { + return; + } + + try { + $node = $event->getTarget(); + } catch (InvalidPathException | NotFoundException $e) { + } + } + +} + diff --git a/lib/Migration/Version0023Date20210907122531.php b/lib/Migration/Version0023Date20210907122531.php new file mode 100644 index 0000000..39cf7eb --- /dev/null +++ b/lib/Migration/Version0023Date20210907122531.php @@ -0,0 +1,125 @@ + + * @copyright 2021 + * @license GNU AGPL version 3 or any later version + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU Affero General Public License as + * published by the Free Software Foundation, either version 3 of the + * License, or (at your option) any later version. + * + * 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 + * along with this program. If not, see . + * + */ + + +namespace OCA\Backup\Migration; + +use Closure; +use Doctrine\DBAL\Schema\SchemaException; +use OCP\DB\ISchemaWrapper; +use OCP\IDBConnection; +use OCP\Migration\IOutput; +use OCP\Migration\SimpleMigrationStep; + +/** + * Class Version0023Date20210907122531 + * + * @package OCA\Backup\Migration + */ +class Version0023Date20210907122531 extends SimpleMigrationStep { + + + /** + * @param IDBConnection $connection + */ + public function __construct(IDBConnection $connection) { + } + + + /** + * @param IOutput $output + * @param Closure $schemaClosure The `\Closure` returns a `ISchemaWrapper` + * @param array $options + * + * @return null|ISchemaWrapper + * @throws SchemaException + */ + public function changeSchema(IOutput $output, Closure $schemaClosure, array $options): ?ISchemaWrapper { + /** @var ISchemaWrapper $schema */ + $schema = $schemaClosure(); + + /** + * BACKUP_REMOTE + */ + if (!$schema->hasTable('backup_remote')) { + $table = $schema->createTable('backup_remote'); + $table->addColumn( + 'id', 'integer', [ + 'autoincrement' => true, + 'notnull' => true, + 'length' => 4, + 'unsigned' => true, + ] + ); + $table->addColumn( + 'uid', 'string', [ + 'notnull' => false, + 'length' => 20, + ] + ); + $table->addColumn( + 'instance', 'string', [ + 'notnull' => false, + 'length' => 127, + ] + ); + $table->addColumn( + 'exchange', 'integer', [ + 'notnull' => true, + 'length' => 1, + 'unsigned' => true, + ] + ); + $table->addColumn( + 'href', 'string', [ + 'notnull' => false, + 'length' => 254, + ] + ); + $table->addColumn( + 'item', 'text', [ + 'notnull' => false, + ] + ); + $table->addColumn( + 'creation', 'datetime', [ + 'notnull' => false, + ] + ); + + $table->setPrimaryKey(['id']); + $table->addUniqueIndex(['instance']); + $table->addIndex(['uid']); + $table->addIndex(['href']); + } + + return $schema; + } +} diff --git a/lib/Model/RemoteInstance.php b/lib/Model/RemoteInstance.php new file mode 100644 index 0000000..37b9744 --- /dev/null +++ b/lib/Model/RemoteInstance.php @@ -0,0 +1,349 @@ + + * @copyright 2021, Maxence Lange + * @license GNU AGPL version 3 or any later version + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU Affero General Public License as + * published by the Free Software Foundation, either version 3 of the + * License, or (at your option) any later version. + * + * 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 + * along with this program. If not, see . + * + */ + + +namespace OCA\Backup\Model; + + +use ArtificialOwl\MySmallPhpTools\Db\Nextcloud\nc22\INC22QueryRow; +use ArtificialOwl\MySmallPhpTools\Model\Nextcloud\nc23\NC23Signatory; +use ArtificialOwl\MySmallPhpTools\Traits\TArrayTools; +use JsonSerializable; +use OCA\Backup\Exceptions\RemoteInstanceNotFoundException; +use OCA\Backup\Exceptions\RemoteInstanceUidException; + + +/** + * Class AppService + * + * @package OCA\Circles\Model + */ +class RemoteInstance extends NC23Signatory implements INC22QueryRow, JsonSerializable { + + + use TArrayTools; + + + const EXCHANGE_IN = 1; + const EXCHANGE_OUT = 2; + + + const TEST = 'test'; + + + /** @var int */ + private $dbId = 0; + + /** @var string */ + private $root = ''; + + /** @var int */ + private $exchange = 0; + + /** @var string */ + private $test = ''; + + /** @var string */ + private $uid = ''; + + /** @var string */ + private $authSigned = ''; + + /** @var bool */ + private $identityAuthed = false; + + + /** + * @param int $dbId + * + * @return self + */ + public function setDbId(int $dbId): self { + $this->dbId = $dbId; + + return $this; + } + + /** + * @return int + */ + public function getDbId(): int { + return $this->dbId; + } + + + /** + * @return string + */ + public function getRoot(): string { + return $this->root; + } + + /** + * @param string $root + * + * @return $this + */ + public function setRoot(string $root): self { + $this->root = $root; + + return $this; + } + + + /** + * @param int $exchange + * + * @return $this + */ + public function setExchange(int $exchange): self { + $this->exchange = $exchange; + + return $this; + } + + /** + * @return int + */ + public function getExchange(): int { + return $this->exchange; + } + + /** + * @param bool $incoming + * + * @return $this + */ + public function setIncoming(bool $incoming = false): self { + $this->exchange |= self::EXCHANGE_IN; + if (!$incoming) { + $this->exchange -= self::EXCHANGE_IN; + } + + return $this; + } + + /** + * @return bool + */ + public function isIncoming(): bool { + return (($this->getExchange() & self::EXCHANGE_IN) !== 0); + } + + + /** + * @param bool $outgoing + * + * @return $this + */ + public function setOutgoing(bool $outgoing = false): self { + $this->exchange |= self::EXCHANGE_OUT; + if (!$outgoing) { + $this->exchange -= self::EXCHANGE_OUT; + } + + return $this; + } + + /** + * @return bool + */ + public function isOutgoing(): bool { + return (($this->getExchange() & self::EXCHANGE_OUT) !== 0); + } + + + /** + * @param string $test + * + * @return RemoteInstance + */ + public function setTest(string $test): self { + $this->test = $test; + + return $this; + } + + /** + * @return string + */ + public function getTest(): string { + return $this->test; + } + + + /** + * @return $this + */ + public function setUidFromKey(): self { + $this->setUid(hash('sha512', $this->getPublicKey())); + + return $this; + } + + /** + * @param string $uid + * + * @return RemoteInstance + */ + public function setUid(string $uid): self { + $this->uid = $uid; + + return $this; + } + + /** + * @param bool $shorten + * + * @return string + */ + public function getUid(bool $shorten = false): string { + if ($shorten) { + return substr($this->uid, 0, 18); + } + + return $this->uid; + } + + + /** + * @param string $authSigned + * + * @return RemoteInstance + */ + public function setAuthSigned(string $authSigned): self { + $this->authSigned = $authSigned; + + return $this; + } + + /** + * @return string + */ + public function getAuthSigned(): string { + return $this->authSigned; + } + + + /** + * @param bool $identityAuthed + * + * @return RemoteInstance + */ + public function setIdentityAuthed(bool $identityAuthed): self { + $this->identityAuthed = $identityAuthed; + + return $this; + } + + /** + * @return bool + */ + public function isIdentityAuthed(): bool { + return $this->identityAuthed; + } + + /** + * @throws RemoteInstanceUidException + */ + public function mustBeIdentityAuthed(): void { + if (!$this->isIdentityAuthed()) { + throw new RemoteInstanceUidException('identity not authed'); + } + } + + + /** + * @param array $data + * + * @return NC23Signatory + */ + public function import(array $data): NC23Signatory { + parent::import($data); + + $this->setTest($this->get('test', $data)) + ->setRoot($this->get('root', $data)) + ->setUid($this->get('uid', $data)); + + $algo = ''; + $authSigned = trim($this->get('auth-signed', $data), ':'); + if (strpos($authSigned, ':') > 0) { + list($algo, $authSigned) = explode(':', $authSigned); + } + + $this->setAuthSigned($authSigned) + ->setAlgorithm($algo); + + return $this; + } + + + /** + * @return array + */ + public function jsonSerialize(): array { + $data = [ + 'uid' => $this->getUid(true), + 'root' => $this->getRoot(), + 'test' => $this->getTest() + ]; + + if ($this->getAuthSigned() !== '') { + $data['auth-signed'] = $this->getAlgorithm() . ':' . $this->getAuthSigned(); + } + + return array_filter(array_merge($data, parent::jsonSerialize())); + } + + + /** + * @param array $data + * + * @return self + * @throws RemoteInstanceNotFoundException + */ + public function importFromDatabase(array $data): INC22QueryRow { + if ($this->getInt('id', $data) === 0) { + throw new RemoteInstanceNotFoundException(); + } + + $this->setDbId($this->getInt('id', $data)); + $this->import($this->getArray('item', $data)); + $this->setExchange($this->getInt('exchange', $data)); + $this->setOrigData($this->getArray('item', $data)); + $this->setInstance($this->get('instance', $data)); + $this->setId($this->get('href', $data)); + + return $this; + } + + +} + diff --git a/lib/Model/RestoringPoint.php b/lib/Model/RestoringPoint.php new file mode 100644 index 0000000..0f7724f --- /dev/null +++ b/lib/Model/RestoringPoint.php @@ -0,0 +1,68 @@ + + * @copyright 2019, Maxence Lange + * @license GNU AGPL version 3 or any later version + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU Affero General Public License as + * published by the Free Software Foundation, either version 3 of the + * License, or (at your option) any later version. + * + * 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 + * along with this program. If not, see . + * + */ + + +namespace OCA\Backup\Model; + + +use ArtificialOwl\MySmallPhpTools\Db\Nextcloud\nc23\INC23QueryRow; +use ArtificialOwl\MySmallPhpTools\IDeserializable; +use JsonSerializable; + + +/** + * Class RestoringPoint + * + * @package OCA\Backup\Model + */ +class RestoringPoint implements IDeserializable, INC23QueryRow, JsonSerializable { + + + /** + * @return array + */ + public function jsonSerialize(): array { + return + [ + ]; + } + + + public function importFromDatabase(array $data): INC23QueryRow { + return $this; + } + + + public function import(array $data): IDeserializable { + return $this; + } + +} + diff --git a/lib/Service/ConfigService.php b/lib/Service/ConfigService.php index 5a0b5d1..788d040 100644 --- a/lib/Service/ConfigService.php +++ b/lib/Service/ConfigService.php @@ -1,5 +1,5 @@ '0' + ]; /** @var IConfig */ private $config; - /** @var MiscService */ - private $miscService; - /** * ConfigService constructor. * * @param IConfig $config - * @param MiscService $miscService */ - public function __construct(IConfig $config, MiscService $miscService) { + public function __construct(IConfig $config) { $this->config = $config; - $this->miscService = $miscService; } @@ -74,31 +73,37 @@ class ConfigService { * * @return string */ - public function getAppValue($key) { - $defaultValue = null; - if (array_key_exists($key, $this->defaults)) { - $defaultValue = $this->defaults[$key]; + public function getAppValue(string $key): string { + if (($value = $this->config->getAppValue(Application::APP_ID, $key, '')) !== '') { + return $value; } - return $this->config->getAppValue(Application::APP_ID, $key, $defaultValue); + if (($value = $this->config->getSystemValue(Application::APP_ID . '.' . $key, '')) !== '') { + return $value; + } + + return $this->get($key, $this->defaults); } /** - * Get a value by key - * * @param string $key * * @return int */ public function getAppValueInt(string $key): int { - $defaultValue = null; - if (array_key_exists($key, $this->defaults)) { - $defaultValue = $this->defaults[$key]; - } - - return (int)$this->config->getAppValue(Application::APP_ID, $key, $defaultValue); + return (int)$this->getAppValue($key); } + /** + * @param string $key + * + * @return bool + */ + public function getAppValueBool(string $key): bool { + return ($this->getAppValueInt($key) === 1); + } + + /** * Set a value by key * @@ -107,19 +112,41 @@ class ConfigService { * * @return void */ - public function setAppValue($key, $value) { + public function setAppValue(string $key, string $value): void { $this->config->setAppValue(Application::APP_ID, $key, $value); } + /** + * + */ + public function unsetAppConfig(): void { + $this->config->deleteAppValues(Application::APP_ID); + } + + /** * @param $key * - * @return mixed + * @return string */ - public function getSystemValue($key) { + public function getSystemValue($key): string { return $this->config->getSystemValue($key, ''); } + + /** + * @param NC23Request $request + */ + public function configureRequest(NC23Request $request): void { + $request->setVerifyPeer($this->getAppValue(self::SELF_SIGNED_CERT) !== '1'); + $request->setProtocols(['https', 'http']); + $request->setHttpErrorsAllowed(true); + $request->setLocalAddressAllowed(true); + $request->setFollowLocation(true); + $request->setTimeout(5); + } + + } diff --git a/lib/Service/RemoteStreamService.php b/lib/Service/RemoteStreamService.php new file mode 100644 index 0000000..d0ceeef --- /dev/null +++ b/lib/Service/RemoteStreamService.php @@ -0,0 +1,218 @@ + + * @copyright 2019, Maxence Lange + * @license GNU AGPL version 3 or any later version + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU Affero General Public License as + * published by the Free Software Foundation, either version 3 of the + * License, or (at your option) any later version. + * + * 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 + * along with this program. If not, see . + * + */ + + +namespace OCA\Backup\Service; + + +use ArtificialOwl\MySmallPhpTools\ActivityPub\Nextcloud\nc23\NC23Signature; +use ArtificialOwl\MySmallPhpTools\Exceptions\RequestNetworkException; +use ArtificialOwl\MySmallPhpTools\Exceptions\SignatoryException; +use ArtificialOwl\MySmallPhpTools\Exceptions\SignatureException; +use ArtificialOwl\MySmallPhpTools\Exceptions\WellKnownLinkNotFoundException; +use ArtificialOwl\MySmallPhpTools\Model\Nextcloud\nc23\NC23Request; +use ArtificialOwl\MySmallPhpTools\Model\Nextcloud\nc23\NC23Signatory; +use ArtificialOwl\MySmallPhpTools\Traits\Nextcloud\nc23\TNC23Deserialize; +use ArtificialOwl\MySmallPhpTools\Traits\Nextcloud\nc23\TNC23LocalSignatory; +use ArtificialOwl\MySmallPhpTools\Traits\Nextcloud\nc23\TNC23WellKnown; +use ArtificialOwl\MySmallPhpTools\Traits\TStringTools; +use OCA\Backup\AppInfo\Application; +use OCA\Backup\Db\RemoteRequest; +use OCA\Backup\Exceptions\RemoteInstanceNotFoundException; +use OCA\Backup\Model\RemoteInstance; +use OCP\IURLGenerator; + + +/** + * Class RemoteStreamService + * + * @package OCA\Backup\Service + */ +class RemoteStreamService extends NC23Signature { + + + use TNC23Deserialize; + use TNC23LocalSignatory; + use TStringTools; + use TNC23WellKnown; + + + /** @var IURLGenerator */ + private $urlGenerator; + + /** @var RemoteRequest */ + private $remoteRequest; + + /** @var ConfigService */ + private $configService; + + + /** + * RemoteStreamService constructor. + * + * @param IURLGenerator $urlGenerator + * @param ConfigService $configService + */ + public function __construct( + IURLGenerator $urlGenerator, + RemoteRequest $remoteRequest, + ConfigService $configService + ) { + $this->urlGenerator = $urlGenerator; + $this->remoteRequest = $remoteRequest; + $this->configService = $configService; + + $this->setup('app', 'backup'); + } + + + /** + * Returns the Signatory model for the Backup app. + * Can be signed with a confirmKey. + * + * @param bool $generate + * @param string $confirmKey + * + * @return RemoteInstance + * @throws SignatoryException + */ + public function getAppSignatory(bool $generate = true, string $confirmKey = ''): RemoteInstance { + $app = new RemoteInstance($this->urlGenerator->linkToRouteAbsolute('backup.Remote.appService')); + $this->fillSimpleSignatory($app, $generate); + $app->setUidFromKey(); + + if ($confirmKey !== '') { + $app->setAuthSigned($this->signString($confirmKey, $app)); + } + + $app->setRoot($this->urlGenerator->linkToRouteAbsolute('backup.Remote.test')); + $app->setTest($this->urlGenerator->linkToRouteAbsolute('backup.Remote.test')); + + $app->setOrigData($this->serialize($app)); + + return $app; + } + + + /** + * Reset the Signatory (and the Identity) for the Circles app. + */ + public function resetAppSignatory(): void { + try { + $app = $this->getAppSignatory(); + + $this->removeSimpleSignatory($app); + } catch (SignatoryException $e) { + } + } + + + /** + * Add a remote instance, based on the address + * + * @param string $instance + * + * @return RemoteInstance + * @throws RequestNetworkException + * @throws SignatoryException + * @throws WellKnownLinkNotFoundException + */ + public function retrieveRemoteInstance(string $instance): RemoteInstance { + $resource = $this->getResourceData($instance, Application::APP_SUBJECT, Application::APP_REL); + + /** @var RemoteInstance $remoteInstance */ + $remoteInstance = $this->retrieveSignatory($resource->g('id'), true); + $remoteInstance->setInstance($instance); + + return $remoteInstance; + } + + + /** + * retrieve Signatory. + * + * @param string $keyId + * @param bool $refresh + * + * @return NC23Signatory + * @throws SignatoryException + * @throws SignatureException + */ + public function retrieveSignatory(string $keyId, bool $refresh = true): NC23Signatory { + if (!$refresh) { + try { + return $this->remoteRequest->getFromHref(NC23Signatory::removeFragment($keyId)); + } catch (RemoteInstanceNotFoundException $e) { + throw new SignatoryException(); + } + } + + $remoteInstance = new RemoteInstance($keyId); + $confirm = $this->uuid(); + + $request = new NC23Request(); + $this->configService->configureRequest($request); + + $this->downloadSignatory($remoteInstance, $keyId, ['auth' => $confirm], $request); + $remoteInstance->setUidFromKey(); + + $this->confirmAuth($remoteInstance, $confirm); + + return $remoteInstance; + } + + + /** + * Confirm the Auth of a RemoteInstance, based on the result from a request + * + * @param RemoteInstance $remote + * @param string $auth + * + * @throws SignatureException + */ + private function confirmAuth(RemoteInstance $remote, string $auth): void { + [$algo, $signed] = explode(':', $this->get('auth-signed', $remote->getOrigData())); + try { + if ($signed === null) { + throw new SignatureException('invalid auth-signed'); + } + $this->verifyString($auth, base64_decode($signed), $remote->getPublicKey(), $algo); + $remote->setIdentityAuthed(true); + } catch (SignatureException $e) { + $this->e( + $e, + ['auth' => $auth, 'signed' => $signed, 'signatory' => $remote, 'msg' => 'auth not confirmed'] + ); + throw new SignatureException('auth not confirmed'); + } + } + +} + diff --git a/lib/SqlDump/SqlDumpMySQL.php b/lib/SqlDump/SqlDumpMySQL.php index c70f507..a916ae0 100644 --- a/lib/SqlDump/SqlDumpMySQL.php +++ b/lib/SqlDump/SqlDumpMySQL.php @@ -1,4 +1,5 @@